So it can be used from out of openvswitch code. Did couple of cosmetic changes on the way, namely variable naming and adding support for 8021AD proto.
Note on backwards compatability: Unlike the upstream version, the backport of skb_vlan_push() does not support translating a hardware accelerated 8021AD tag to software. This is not a problem though as it preserves existing behaviour. Upstream: 93515d53 ("net: move vlan pop/push functions into common code") Signed-off-by: Thomas Graf <tg...@noironetworks.com> --- acinclude.m4 | 2 + datapath/actions.c | 83 +++--------------------- datapath/linux/compat/include/linux/skbuff.h | 10 +++ datapath/linux/compat/skbuff-openvswitch.c | 97 ++++++++++++++++++++++++++++ 4 files changed, 119 insertions(+), 73 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index 9766bed..2579754 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -346,6 +346,8 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [ [OVS_DEFINE([HAVE_SKB_ZEROCOPY])]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [l4_rxhash]) OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_ensure_writable]) + OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_vlan_pop]) + OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_vlan_push]) OVS_GREP_IFELSE([$KSRC/include/linux/types.h], [bool], [OVS_DEFINE([HAVE_BOOL_TYPE])]) diff --git a/datapath/actions.c b/datapath/actions.c index 7f61915..0ac6684 100644 --- a/datapath/actions.c +++ b/datapath/actions.c @@ -212,90 +212,29 @@ static int set_mpls(struct sk_buff *skb, struct sw_flow_key *key, return 0; } -/* remove VLAN header from packet and update csum accordingly. */ -static int __pop_vlan_tci(struct sk_buff *skb, __be16 *current_tci) -{ - struct vlan_hdr *vhdr; - int err; - - err = skb_ensure_writable(skb, VLAN_ETH_HLEN); - if (unlikely(err)) - return err; - - if (skb->ip_summed == CHECKSUM_COMPLETE) - skb->csum = csum_sub(skb->csum, csum_partial(skb->data - + (2 * ETH_ALEN), VLAN_HLEN, 0)); - - vhdr = (struct vlan_hdr *)(skb->data + ETH_HLEN); - *current_tci = vhdr->h_vlan_TCI; - - memmove(skb->data + VLAN_HLEN, skb->data, 2 * ETH_ALEN); - __skb_pull(skb, VLAN_HLEN); - - vlan_set_encap_proto(skb, vhdr); - skb->mac_header += VLAN_HLEN; - /* Update mac_len for subsequent MPLS actions */ - skb->mac_len -= VLAN_HLEN; - - return 0; -} - static int pop_vlan(struct sk_buff *skb, struct sw_flow_key *key) { - __be16 tci; int err; - if (likely(vlan_tx_tag_present(skb))) { - vlan_set_tci(skb, 0); - } else { - if (unlikely(skb->protocol != htons(ETH_P_8021Q) || - skb->len < VLAN_ETH_HLEN)) - return 0; - - err = __pop_vlan_tci(skb, &tci); - if (err) - return err; - } - /* move next vlan tag to hw accel tag */ - if (likely(skb->protocol != htons(ETH_P_8021Q) || - skb->len < VLAN_ETH_HLEN)) { + err = skb_vlan_pop(skb); + if (vlan_tx_tag_present(skb)) + invalidate_flow_key(key); + else key->eth.tci = 0; - return 0; - } - - invalidate_flow_key(key); - err = __pop_vlan_tci(skb, &tci); - if (unlikely(err)) - return err; - __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(tci)); - return 0; + return err; } static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key, const struct ovs_action_push_vlan *vlan) { - if (unlikely(vlan_tx_tag_present(skb))) { - u16 current_tag; - - /* push down current VLAN tag */ - current_tag = vlan_tx_tag_get(skb); - - if (!vlan_insert_tag_set_proto(skb, skb->vlan_proto, current_tag)) - return -ENOMEM; - /* Update mac_len for subsequent MPLS actions */ - skb->mac_len += VLAN_HLEN; - - if (skb->ip_summed == CHECKSUM_COMPLETE) - skb->csum = csum_add(skb->csum, csum_partial(skb->data - + (2 * ETH_ALEN), VLAN_HLEN, 0)); - + if (vlan_tx_tag_present(skb)) invalidate_flow_key(key); - } else { + else key->eth.tci = vlan->vlan_tci; - } - __vlan_hwaccel_put_tag(skb, vlan->vlan_tpid, ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT); - return 0; + + return skb_vlan_push(skb, vlan->vlan_tpid, + ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT); } static int set_eth_addr(struct sk_buff *skb, struct sw_flow_key *key, @@ -865,8 +804,6 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, case OVS_ACTION_ATTR_PUSH_VLAN: err = push_vlan(skb, key, nla_data(a)); - if (unlikely(err)) /* skb already freed. */ - return err; break; case OVS_ACTION_ATTR_POP_VLAN: diff --git a/datapath/linux/compat/include/linux/skbuff.h b/datapath/linux/compat/include/linux/skbuff.h index fae39a5..9cf9159 100644 --- a/datapath/linux/compat/include/linux/skbuff.h +++ b/datapath/linux/compat/include/linux/skbuff.h @@ -329,4 +329,14 @@ static inline void __skb_fill_page_desc(struct sk_buff *skb, int i, int skb_ensure_writable(struct sk_buff *skb, int write_len); #endif +#ifndef HAVE_SKB_VLAN_POP +#define skb_vlan_pop rpl_skb_vlan_pop +int skb_vlan_pop(struct sk_buff *skb); +#endif + +#ifndef HAVE_SKB_VLAN_PUSH +#define skb_vlan_push rpl_skb_vlan_push +int skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci); +#endif + #endif diff --git a/datapath/linux/compat/skbuff-openvswitch.c b/datapath/linux/compat/skbuff-openvswitch.c index 66cc614..b2eab79 100644 --- a/datapath/linux/compat/skbuff-openvswitch.c +++ b/datapath/linux/compat/skbuff-openvswitch.c @@ -1,6 +1,7 @@ #include <linux/module.h> #include <linux/netdevice.h> #include <linux/skbuff.h> +#include <linux/if_vlan.h> #if !defined(HAVE_SKB_WARN_LRO) && defined(NETIF_F_LRO) @@ -137,3 +138,99 @@ int skb_ensure_writable(struct sk_buff *skb, int write_len) return pskb_expand_head(skb, 0, 0, GFP_ATOMIC); } #endif + +#ifndef HAVE_SKB_VLAN_POP +/* remove VLAN header from packet and update csum accordingly. */ +static int __skb_vlan_pop(struct sk_buff *skb, u16 *vlan_tci) +{ + struct vlan_hdr *vhdr; + unsigned int offset = skb->data - skb_mac_header(skb); + int err; + + __skb_push(skb, offset); + err = skb_ensure_writable(skb, VLAN_ETH_HLEN); + if (unlikely(err)) + goto pull; + + skb_postpull_rcsum(skb, skb->data + (2 * ETH_ALEN), VLAN_HLEN); + + vhdr = (struct vlan_hdr *)(skb->data + ETH_HLEN); + *vlan_tci = ntohs(vhdr->h_vlan_TCI); + + memmove(skb->data + VLAN_HLEN, skb->data, 2 * ETH_ALEN); + __skb_pull(skb, VLAN_HLEN); + + vlan_set_encap_proto(skb, vhdr); + skb->mac_header += VLAN_HLEN; + + if (skb_network_offset(skb) < ETH_HLEN) + skb_set_network_header(skb, ETH_HLEN); + + skb_reset_mac_len(skb); +pull: + __skb_pull(skb, offset); + + return err; +} + +int skb_vlan_pop(struct sk_buff *skb) +{ + u16 vlan_tci; + __be16 vlan_proto; + int err; + + if (likely(vlan_tx_tag_present(skb))) { + skb->vlan_tci = 0; + } else { + if (unlikely((skb->protocol != htons(ETH_P_8021Q) && + skb->protocol != htons(ETH_P_8021AD)) || + skb->len < VLAN_ETH_HLEN)) + return 0; + + err = __skb_vlan_pop(skb, &vlan_tci); + if (err) + return err; + } + /* move next vlan tag to hw accel tag */ + if (likely((skb->protocol != htons(ETH_P_8021Q) && + skb->protocol != htons(ETH_P_8021AD)) || + skb->len < VLAN_ETH_HLEN)) + return 0; + + vlan_proto = htons(ETH_P_8021Q); + err = __skb_vlan_pop(skb, &vlan_tci); + if (unlikely(err)) + return err; + + __vlan_hwaccel_put_tag(skb, vlan_proto, vlan_tci); + return 0; +} +#endif + +#ifndef HAVE_SKB_VLAN_PUSH +int skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci) +{ + if (vlan_tx_tag_present(skb)) { + unsigned int offset = skb->data - skb_mac_header(skb); + int err; + + /* __vlan_insert_tag expect skb->data pointing to mac header. + * So change skb->data before calling it and change back to + * original position later + */ + __skb_push(skb, offset); + err = __vlan_insert_tag(skb, skb->vlan_proto, + vlan_tx_tag_get(skb)); + if (err) + return err; + skb->mac_len += VLAN_HLEN; + __skb_pull(skb, offset); + + if (skb->ip_summed == CHECKSUM_COMPLETE) + skb->csum = csum_add(skb->csum, csum_partial(skb->data + + (2 * ETH_ALEN), VLAN_HLEN, 0)); + } + __vlan_hwaccel_put_tag(skb, vlan_proto, vlan_tci); + return 0; +} +#endif -- 1.9.3 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev