From 69a4a76b9413b88f5b5eec5705250b4312811478 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Thu, 27 May 2010 13:03:45 -0700 Subject: [PATCH] datapath: Fix VLAN insertion behavior on Linux 2.6.27 and 2.6.28. The behavior of __vlan_put_tag() has changed over time: - In 2.6.26 and earlier, it adjusted both MAC and network header pointers. (The latter didn't make any sense.) - In 2.6.27 and 2.6.28, it did not adjust any header pointers at all. - In 2.6.29 and later, it adjusts the MAC header pointer only. The behavior in 2.6.26 and earlier, and in 2.2.29 and later, works OK for Open vSwitch. The 2.6.27 and 2.6.28 behavior *almost* works OK, with a few subtle problems. If an action that sets a VLAN tag is followed by an action that strips a VLAN tag, the "strip" action silently fails. This is because vlan_pull_tag() in datapath/actions.c sees the encapsulated protocol, not the 802.1Q protocol, because the MAC header was not adjusted and does not point to the 802.1Q header. If multiple set-VLAN actions occur in a single flow, the second and later actions will fail for the same reason. This commit fixes the problem by ensuring that __vlan_put_tag() always behaves as in 2.6.29 and later. Reported-by: Reid Price Bug #2867. --- datapath/linux-2.6/Modules.mk | 1 + .../compat-2.6/include/linux/if_vlan.h | 46 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 datapath/linux-2.6/compat-2.6/include/linux/if_vlan.h diff --git a/datapath/linux-2.6/Modules.mk b/datapath/linux-2.6/Modules.mk index f5f13e2c..baa6f53c 100644 --- a/datapath/linux-2.6/Modules.mk +++ b/datapath/linux-2.6/Modules.mk @@ -16,6 +16,7 @@ openvswitch_headers += \ linux-2.6/compat-2.6/include/linux/if.h \ linux-2.6/compat-2.6/include/linux/if_arp.h \ linux-2.6/compat-2.6/include/linux/if_ether.h \ + linux-2.6/compat-2.6/include/linux/if_vlan.h \ linux-2.6/compat-2.6/include/linux/in.h \ linux-2.6/compat-2.6/include/linux/inetdevice.h \ linux-2.6/compat-2.6/include/linux/ip.h \ diff --git a/datapath/linux-2.6/compat-2.6/include/linux/if_vlan.h b/datapath/linux-2.6/compat-2.6/include/linux/if_vlan.h new file mode 100644 index 00000000..3bede930 --- /dev/null +++ b/datapath/linux-2.6/compat-2.6/include/linux/if_vlan.h @@ -0,0 +1,46 @@ +#ifndef __LINUX_IF_VLAN_WRAPPER_H +#define __LINUX_IF_VLAN_WRAPPER_H 1 + +#include_next + +/* + * The behavior of __vlan_put_tag() has changed over time: + * + * - In 2.6.26 and earlier, it adjusted both MAC and network header + * pointers. (The latter didn't make any sense.) + * + * - In 2.6.27 and 2.6.28, it did not adjust any header pointers at all. + * + * - In 2.6.29 and later, it adjusts the MAC header pointer only. + * + * This is the version from 2.6.33. We unconditionally substitute this version + * to avoid the need to guess whether the version in the kernel tree is + * acceptable. + */ +#define __vlan_put_tag rpl_vlan_put_tag +static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, u16 vlan_tci) +{ + struct vlan_ethhdr *veth; + + if (skb_cow_head(skb, VLAN_HLEN) < 0) { + kfree_skb(skb); + return NULL; + } + veth = (struct vlan_ethhdr *)skb_push(skb, VLAN_HLEN); + + /* Move the mac addresses to the beginning of the new header. */ + memmove(skb->data, skb->data + VLAN_HLEN, 2 * VLAN_ETH_ALEN); + skb->mac_header -= VLAN_HLEN; + + /* first, the ethernet type */ + veth->h_vlan_proto = htons(ETH_P_8021Q); + + /* now, the TCI */ + veth->h_vlan_TCI = htons(vlan_tci); + + skb->protocol = htons(ETH_P_8021Q); + + return skb; +} + +#endif /* linux/if_vlan.h wrapper */ -- 2.30.2