[ovs-dev] [PATCH] datapath: Fix pop_vlan()

Pravin B Shelar pshelar at nicira.com
Sat Nov 12 00:23:30 UTC 2011


Followng patch fixes bug in pop_vlan code by setting network and
transport header offsets after untagging vlan.
pop_vlan is updated to make it in sync with newer kernel.

Signed-off-by: Pravin B Shelar <pshelar at nicira.com>
---
 acinclude.m4                                 |    2 +
 datapath/actions.c                           |   28 +++++++++++---------
 datapath/linux/compat/include/linux/skbuff.h |    6 ++++
 datapath/vlan.c                              |   35 ++++++++++++++++++++++++++
 datapath/vlan.h                              |    3 ++
 5 files changed, 61 insertions(+), 13 deletions(-)

diff --git a/acinclude.m4 b/acinclude.m4
index 648132a..458b9e5 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -238,6 +238,7 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
   OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_warn_if_lro],
                   [OVS_DEFINE([HAVE_SKB_WARN_LRO])])
   OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [consume_skb])
+  OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_reset_mac_len])
 
   OVS_GREP_IFELSE([$KSRC/include/linux/string.h], [kmemdup], [],
                   [OVS_GREP_IFELSE([$KSRC/include/linux/slab.h], [kmemdup])])
@@ -254,6 +255,7 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
   OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_get_be16])
   OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_find_nested])
 
+  OVS_GREP_IFELSE([$KSRC/include/linux/if_vlan.h], [vlan_set_encap_proto])
   OVS_GREP_IFELSE([$KSRC/include/linux/if_vlan.h], [ADD_ALL_VLANS_CMD],
                   [OVS_DEFINE([HAVE_VLAN_BUG_WORKAROUND])])
 
diff --git a/datapath/actions.c b/datapath/actions.c
index ac7187b..f9746ff 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -39,30 +39,32 @@ static int make_writable(struct sk_buff *skb, int write_len)
 	return pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
 }
 
+static void vlan_reorder_header(struct sk_buff *skb)
+{
+	memmove(skb->data, skb->data - VLAN_HLEN, 2 * ETH_ALEN);
+	skb->mac_header += VLAN_HLEN;
+	skb_reset_mac_len(skb);
+}
+
 /* 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);
 	if (unlikely(err))
 		return err;
 
-	if (get_ip_summed(skb) == OVS_CSUM_COMPLETE)
-		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;
+	vlan_set_encap_proto(skb, vhdr);
 
-	memmove(skb->data + VLAN_HLEN, skb->data, 2 * ETH_ALEN);
+	skb_pull_rcsum(skb, VLAN_HLEN);
+	vlan_reorder_header(skb);
 
-	eh = (struct ethhdr *)__skb_pull(skb, VLAN_HLEN);
-
-	skb->protocol = eh->h_proto;
-	skb->mac_header += VLAN_HLEN;
+	skb_set_network_header(skb, VLAN_HLEN);
+	skb_set_transport_header(skb, VLAN_HLEN);
 
 	return 0;
 }
diff --git a/datapath/linux/compat/include/linux/skbuff.h b/datapath/linux/compat/include/linux/skbuff.h
index 456d744..22ba2e6 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
 
+#ifndef HAVE_SKB_RESET_MAC_LEN
+static inline void skb_reset_mac_len(struct sk_buff *skb)
+{
+	skb->mac_len = skb->network_header - skb->mac_header;
+}
+#endif
 #endif
diff --git a/datapath/vlan.c b/datapath/vlan.c
index 9aebecd..395fa5e 100644
--- a/datapath/vlan.c
+++ b/datapath/vlan.c
@@ -46,3 +46,38 @@ struct sk_buff *__vlan_hwaccel_put_tag(struct sk_buff *skb, u16 vlan_tci)
 	return skb;
 }
 #endif /* NEED_VLAN_FIELD */
+
+#ifndef HAVE_VLAN_SET_ENCAP_PROTO
+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
diff --git a/datapath/vlan.h b/datapath/vlan.h
index 7e1084e..1374f43 100644
--- a/datapath/vlan.h
+++ b/datapath/vlan.h
@@ -87,4 +87,7 @@ static inline int vlan_deaccel_tag(struct sk_buff *skb)
 	return 0;
 }
 
+#ifndef HAVE_VLAN_SET_ENCAP_PROTO
+void vlan_set_encap_proto(struct sk_buff *skb, struct vlan_hdr *vhdr);
+#endif
 #endif /* vlan.h */
-- 
1.7.1




More information about the dev mailing list