[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