From ceb176fdb72bb7ce90debc66e1eeb1d25823d30a Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Fri, 18 Nov 2011 11:48:01 -0800 Subject: [PATCH] datapath: Fix pop_vlan(). Following patch fixes bug in pop_vlan code by updating ethernet header len. Signed-off-by: Pravin B Shelar Acked-by: Jesse Gross --- datapath/actions.c | 13 ++++--- datapath/linux/compat/include/linux/if_vlan.h | 34 +++++++++++++++++++ datapath/linux/compat/include/linux/skbuff.h | 6 ++++ 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/datapath/actions.c b/datapath/actions.c index b430090c..70fe153a 100644 --- a/datapath/actions.c +++ b/datapath/actions.c @@ -50,8 +50,7 @@ static int make_writable(struct sk_buff *skb, int write_len) /* remove VLAN header from packet and update csum accrodingly. */ static int __pop_vlan_tci(struct sk_buff *skb, __be16 *current_tci) { - struct ethhdr *eh; - struct vlan_ethhdr *veth; + struct vlan_hdr *vhdr; int err; err = make_writable(skb, VLAN_ETH_HLEN); @@ -62,15 +61,15 @@ static int __pop_vlan_tci(struct sk_buff *skb, __be16 *current_tci) skb->csum = csum_sub(skb->csum, csum_partial(skb->data + ETH_HLEN, VLAN_HLEN, 0)); - veth = (struct vlan_ethhdr *) skb->data; - *current_tci = veth->h_vlan_TCI; + 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); - eh = (struct ethhdr *)__skb_pull(skb, VLAN_HLEN); - - skb->protocol = eh->h_proto; + vlan_set_encap_proto(skb, vhdr); skb->mac_header += VLAN_HLEN; + skb_reset_mac_len(skb); return 0; } diff --git a/datapath/linux/compat/include/linux/if_vlan.h b/datapath/linux/compat/include/linux/if_vlan.h index f4184074..326abb27 100644 --- a/datapath/linux/compat/include/linux/if_vlan.h +++ b/datapath/linux/compat/include/linux/if_vlan.h @@ -54,4 +54,38 @@ static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, u16 vlan_tci) #define VLAN_TAG_PRESENT VLAN_CFI_MASK #endif +/* This function is not exported from kernel. OVS Upstreaming patch will + * fix that. */ +static inline void vlan_set_encap_proto(struct sk_buff *skb, struct vlan_hdr *vhdr) +{ + __be16 proto; + unsigned char *rawp; + + /* + * Was a VLAN packet, grab the encapsulated protocol, which the layer + * three protocols care about. + */ + + proto = vhdr->h_vlan_encapsulated_proto; + if (ntohs(proto) >= 1536) { + skb->protocol = proto; + return; + } + + rawp = skb->data; + if (*(unsigned short *) rawp == 0xFFFF) + /* + * This is a magic hack to spot IPX packets. Older Novell + * breaks the protocol design and runs IPX over 802.3 without + * an 802.2 LLC layer. We look for FFFF which isn't a used + * 802.2 SSAP/DSAP. This won't work for fault tolerant netware + * but does for the rest. + */ + skb->protocol = htons(ETH_P_802_3); + else + /* + * Real 802.2 LLC + */ + skb->protocol = htons(ETH_P_802_2); +} #endif /* linux/if_vlan.h wrapper */ diff --git a/datapath/linux/compat/include/linux/skbuff.h b/datapath/linux/compat/include/linux/skbuff.h index 456d7446..311bfdbc 100644 --- a/datapath/linux/compat/include/linux/skbuff.h +++ b/datapath/linux/compat/include/linux/skbuff.h @@ -239,4 +239,10 @@ static inline struct page *skb_frag_page(const skb_frag_t *frag) } #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) +static inline void skb_reset_mac_len(struct sk_buff *skb) +{ + skb->mac_len = skb->network_header - skb->mac_header; +} +#endif #endif -- 2.30.2