@@ -479,6 +479,8 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
[OVS_GREP_IFELSE([$KSRC/include/net/dst_cache.h], [dst_cache],
[OVS_DEFINE([USE_UPSTREAM_TUNNEL])])])])
+ OVS_GREP_IFELSE([$KSRC/include/net/mpls.h], [mpls_hdr],
+ [OVS_DEFINE([MPLS_HEADER_IS_L3])])
OVS_GREP_IFELSE([$KSRC/include/linux/net.h], [sock_create_kern.*net],
[OVS_DEFINE([HAVE_SOCK_CREATE_KERN_NET])])
OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [ndo_fill_metadata_dst])
@@ -186,7 +186,7 @@ static void update_ethertype(struct sk_buff *skb, struct ethhdr *hdr,
static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key,
const struct ovs_action_push_mpls *mpls)
{
- __be32 *new_mpls_lse;
+ struct mpls_shim_hdr *new_mpls_lse;
/* Networking stack do not allow simultaneous Tunnel and MPLS GSO. */
if (skb->encapsulation)
@@ -195,20 +195,26 @@ static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key,
if (skb_cow_head(skb, MPLS_HLEN) < 0)
return -ENOMEM;
+ if (!ovs_skb_get_inner_protocol(skb)) {
+ skb_set_inner_network_header(skb, skb->mac_len);
+ ovs_skb_set_inner_protocol(skb, skb->protocol);
+ }
+
skb_push(skb, MPLS_HLEN);
memmove(skb_mac_header(skb) - MPLS_HLEN, skb_mac_header(skb),
skb->mac_len);
skb_reset_mac_header(skb);
+#ifdef MPLS_HEADER_IS_L3
+ skb_set_network_header(skb, skb->mac_len);
+#endif
- new_mpls_lse = (__be32 *)skb_mpls_header(skb);
- *new_mpls_lse = mpls->mpls_lse;
+ new_mpls_lse = mpls_hdr(skb);
+ new_mpls_lse->label_stack_entry = mpls->mpls_lse;
skb_postpush_rcsum(skb, new_mpls_lse, MPLS_HLEN);
if (ovs_key_mac_proto(key) == MAC_PROTO_ETHERNET)
update_ethertype(skb, eth_hdr(skb), mpls->mpls_ethertype);
- if (!ovs_skb_get_inner_protocol(skb))
- ovs_skb_set_inner_protocol(skb, skb->protocol);
skb->protocol = mpls->mpls_ethertype;
invalidate_flow_key(key);
@@ -224,21 +230,22 @@ static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key,
if (unlikely(err))
return err;
- skb_postpull_rcsum(skb, skb_mpls_header(skb), MPLS_HLEN);
+ skb_postpull_rcsum(skb, mpls_hdr(skb), MPLS_HLEN);
memmove(skb_mac_header(skb) + MPLS_HLEN, skb_mac_header(skb),
skb->mac_len);
__skb_pull(skb, MPLS_HLEN);
skb_reset_mac_header(skb);
+ skb_set_network_header(skb, skb->mac_len);
if (ovs_key_mac_proto(key) == MAC_PROTO_ETHERNET) {
struct ethhdr *hdr;
- /* skb_mpls_header() is used to locate the ethertype
+ /* mpls_hdr() is used to locate the ethertype
* field correctly in the presence of VLAN tags.
*/
- hdr = (struct ethhdr *)(skb_mpls_header(skb) - ETH_HLEN);
+ hdr = (struct ethhdr *)((void*)mpls_hdr(skb) - ETH_HLEN);
update_ethertype(skb, hdr, ethertype);
}
if (eth_p_mpls(skb->protocol))
@@ -251,7 +258,7 @@ static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key,
static int set_mpls(struct sk_buff *skb, struct sw_flow_key *flow_key,
const __be32 *mpls_lse, const __be32 *mask)
{
- __be32 *stack;
+ struct mpls_shim_hdr *stack;
__be32 lse;
int err;
@@ -259,16 +266,16 @@ static int set_mpls(struct sk_buff *skb, struct sw_flow_key *flow_key,
if (unlikely(err))
return err;
- stack = (__be32 *)skb_mpls_header(skb);
- lse = OVS_MASKED(*stack, *mpls_lse, *mask);
+ stack = mpls_hdr(skb);
+ lse = OVS_MASKED(stack->label_stack_entry, *mpls_lse, *mask);
if (skb->ip_summed == CHECKSUM_COMPLETE) {
- __be32 diff[] = { ~(*stack), lse };
+ __be32 diff[] = { ~(stack->label_stack_entry), lse };
skb->csum = ~csum_partial((char *)diff, sizeof(diff),
~skb->csum);
}
- *stack = lse;
+ stack->label_stack_entry = lse;
flow_key->mpls.top_lse = lse;
return 0;
}
@@ -19,12 +19,33 @@
#define MPLS_HLEN 4
+struct mpls_shim_hdr {
+ __be32 label_stack_entry;
+};
+
static inline bool eth_p_mpls(__be16 eth_type)
{
return eth_type == htons(ETH_P_MPLS_UC) ||
eth_type == htons(ETH_P_MPLS_MC);
}
+/* Starting from kernel 4.9, commit 48d2ab609b6b ("net: mpls: Fixups for GSO")
+ * and commit 85de4a2101ac ("openvswitch: use mpls_hdr") introduced
+ * behavioural changes to mpls_gso kernel module. It now assumes that
+ * skb_network_header() points to the mpls header and
+ * skb_inner_network_header() points to the L3 header. However, the old
+ * mpls_gso kernel module assumes that the skb_network_header() points
+ * to the L3 header. We shall backport the following function to ensure
+ * mpls_gso works properly for kernels older than the one which contains
+ * these commits.
+ */
+#ifdef MPLS_HEADER_IS_L3
+static inline struct mpls_shim_hdr *mpls_hdr(const struct sk_buff *skb)
+{
+ return (struct mpls_shim_hdr *)skb_network_header(skb);
+}
+#else
+#define mpls_hdr rpl_mpls_hdr
/*
* For non-MPLS skbs this will correspond to the network header.
* For MPLS skbs it will be before the network_header as the MPLS
@@ -32,8 +53,10 @@ static inline bool eth_p_mpls(__be16 eth_type)
* header. That is, for MPLS skbs the end of the mac header
* is the top of the MPLS label stack.
*/
-static inline unsigned char *skb_mpls_header(struct sk_buff *skb)
+static inline struct mpls_shim_hdr *rpl_mpls_hdr(const struct sk_buff *skb)
{
- return skb_mac_header(skb) + skb->mac_len;
+ return (struct mpls_shim_hdr *) (skb_mac_header(skb) + skb->mac_len);
}
+#endif
+
#endif /* _NET_MPLS_WRAPPER_H */