datapath: Fix few mpls issues.
authorPravin B Shelar <pshelar@nicira.com>
Sat, 8 Nov 2014 16:14:21 +0000 (08:14 -0800)
committerPravin B Shelar <pshelar@nicira.com>
Mon, 10 Nov 2014 04:03:33 +0000 (20:03 -0800)
Found during MPLS upstreaming.  Also sync-up MPLS header files
with upstream code.

Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
12 files changed:
datapath/Modules.mk
datapath/actions.c
datapath/compat.h
datapath/flow.c
datapath/flow_netlink.c
datapath/linux/Modules.mk
datapath/linux/compat/gso.c
datapath/linux/compat/gso.h
datapath/linux/compat/include/linux/mpls.h [new file with mode: 0644]
datapath/linux/compat/include/net/mpls.h [new file with mode: 0644]
datapath/linux/compat/netdevice.c
datapath/mpls.h [deleted file]

index 72cb4dc..cca4887 100644 (file)
@@ -27,7 +27,6 @@ openvswitch_headers = \
        flow.h \
        flow_netlink.h \
        flow_table.h \
-       mpls.h \
        vlan.h \
        vport.h \
        vport-internal_dev.h \
index 3868e10..b7cd58e 100644 (file)
 #include <net/ipv6.h>
 #include <net/checksum.h>
 #include <net/dsfield.h>
+#include <net/mpls.h>
 #include <net/sctp/checksum.h>
 
 #include "datapath.h"
 #include "gso.h"
-#include "mpls.h"
 #include "vlan.h"
 #include "vport.h"
 
@@ -133,25 +133,16 @@ static int make_writable(struct sk_buff *skb, int write_len)
        return pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
 }
 
-/* The end of the mac header.
- *
- * For non-MPLS skbs this will correspond to the network header.
- * For MPLS skbs it will be before the network_header as the MPLS
- * label stack lies between the end of the mac header and the network
- * header. That is, for MPLS skbs the end of the mac header
- * is the top of the MPLS label stack.
- */
-static unsigned char *mac_header_end(const struct sk_buff *skb)
-{
-       return skb_mac_header(skb) + skb->mac_len;
-}
-
 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 ethhdr *hdr;
 
+       /* Networking stack do not allow simultaneous Tunnel and MPLS GSO. */
+       if (skb_encapsulation(skb))
+               return -ENOTSUPP;
+
        if (skb_cow_head(skb, MPLS_HLEN) < 0)
                return -ENOMEM;
 
@@ -160,7 +151,7 @@ static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key,
                skb->mac_len);
        skb_reset_mac_header(skb);
 
-       new_mpls_lse = (__be32 *)mac_header_end(skb);
+       new_mpls_lse = (__be32 *)skb_mpls_header(skb);
        *new_mpls_lse = mpls->mpls_lse;
 
        if (skb->ip_summed == CHECKSUM_COMPLETE)
@@ -172,6 +163,7 @@ static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key,
        if (!ovs_skb_get_inner_protocol(skb))
                ovs_skb_set_inner_protocol(skb, skb->protocol);
        skb->protocol = mpls->mpls_ethertype;
+
        invalidate_flow_key(key);
        return 0;
 }
@@ -188,7 +180,7 @@ static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key,
 
        if (skb->ip_summed == CHECKSUM_COMPLETE)
                skb->csum = csum_sub(skb->csum,
-                                    csum_partial(mac_header_end(skb),
+                                    csum_partial(skb_mpls_header(skb),
                                                  MPLS_HLEN, 0));
 
        memmove(skb_mac_header(skb) + MPLS_HLEN, skb_mac_header(skb),
@@ -197,13 +189,14 @@ static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key,
        __skb_pull(skb, MPLS_HLEN);
        skb_reset_mac_header(skb);
 
-       /* mac_header_end() is used to locate the ethertype
+       /* skb_mpls_header() is used to locate the ethertype
         * field correctly in the presence of VLAN tags.
         */
-       hdr = (struct ethhdr *)(mac_header_end(skb) - ETH_HLEN);
+       hdr = (struct ethhdr *)(skb_mpls_header(skb) - ETH_HLEN);
        hdr->h_proto = ethertype;
        if (eth_p_mpls(skb->protocol))
                skb->protocol = ethertype;
+
        invalidate_flow_key(key);
        return 0;
 }
@@ -211,13 +204,14 @@ 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 *key,
                    const __be32 *mpls_lse)
 {
-       __be32 *stack = (__be32 *)mac_header_end(skb);
+       __be32 *stack;
        int err;
 
        err = make_writable(skb, skb->mac_len + MPLS_HLEN);
        if (unlikely(err))
                return err;
 
+       stack = (__be32 *)skb_mpls_header(skb);
        if (skb->ip_summed == CHECKSUM_COMPLETE) {
                __be32 diff[] = { ~(*stack), *mpls_lse };
                skb->csum = ~csum_partial((char *)diff, sizeof(diff),
@@ -300,7 +294,6 @@ static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key,
 
                if (!__vlan_put_tag(skb, skb->vlan_proto, current_tag))
                        return -ENOMEM;
-
                /* Update mac_len for subsequent MPLS actions */
                skb->mac_len += VLAN_HLEN;
 
@@ -629,10 +622,10 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port)
 static int output_userspace(struct datapath *dp, struct sk_buff *skb,
                            struct sw_flow_key *key, const struct nlattr *attr)
 {
+       struct ovs_tunnel_info info;
        struct dp_upcall_info upcall;
        const struct nlattr *a;
        int rem;
-       struct ovs_tunnel_info info;
 
        upcall.cmd = OVS_PACKET_CMD_ACTION;
        upcall.userdata = NULL;
index 9632a6e..5ef626f 100644 (file)
@@ -76,4 +76,14 @@ static inline struct rtable *find_route(struct net *net,
        return rt;
 #endif
 }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
+static inline bool skb_encapsulation(struct sk_buff *skb)
+{
+       return skb->encapsulation;
+}
+#else
+#define skb_encapsulation(skb) false
+#endif
+
 #endif /* compat.h */
index a3c5d2f..5c25a5d 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/if_arp.h>
 #include <linux/ip.h>
 #include <linux/ipv6.h>
+#include <linux/mpls.h>
 #include <linux/sctp.h>
 #include <linux/smp.h>
 #include <linux/tcp.h>
 #include <linux/rculist.h>
 #include <net/ip.h>
 #include <net/ipv6.h>
+#include <net/mpls.h>
 #include <net/ndisc.h>
 
 #include "datapath.h"
 #include "flow.h"
 #include "flow_netlink.h"
 
-#include "mpls.h"
 #include "vlan.h"
 
 u64 ovs_flow_used_time(unsigned long flow_jiffies)
@@ -609,7 +610,7 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
                                memcpy(&key->mpls.top_lse, &lse, MPLS_HLEN);
 
                        skb_set_network_header(skb, skb->mac_len + stack_len);
-                       if (lse & htonl(MPLS_BOS_MASK))
+                       if (lse & htonl(MPLS_LS_S_MASK))
                                break;
 
                        stack_len += MPLS_HLEN;
index 37b0bdd..0649d2c 100644 (file)
@@ -18,9 +18,6 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
-#include "flow.h"
-#include "datapath.h"
-#include "mpls.h"
 #include <linux/uaccess.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/rculist.h>
 #include <net/geneve.h>
 #include <net/ip.h>
-#include <net/ip_tunnels.h>
 #include <net/ipv6.h>
 #include <net/ndisc.h>
+#include <net/mpls.h>
 
+#include "datapath.h"
+#include "flow.h"
 #include "flow_netlink.h"
 
 static void update_range(struct sw_flow_match *match,
@@ -1665,6 +1664,9 @@ static int validate_set(const struct nlattr *a,
                break;
 
        case OVS_KEY_ATTR_TUNNEL:
+               if (eth_p_mpls(eth_type))
+                       return -EINVAL;
+
                *set_tun = true;
                err = validate_and_copy_set_tun(a, sfa, log);
                if (err)
@@ -1778,6 +1780,7 @@ static int __ovs_nla_copy_actions(const struct nlattr *attr,
                                  __be16 eth_type, __be16 vlan_tci, bool log)
 {
        const struct nlattr *a;
+       bool out_tnl_port = false;
        int rem, err;
 
        if (depth >= SAMPLE_ACTION_DEPTH)
@@ -1820,6 +1823,7 @@ static int __ovs_nla_copy_actions(const struct nlattr *attr,
                case OVS_ACTION_ATTR_OUTPUT:
                        if (nla_get_u32(a) >= DP_MAX_PORTS)
                                return -EINVAL;
+                       out_tnl_port = false;
                        break;
 
                case OVS_ACTION_ATTR_HASH: {
@@ -1854,6 +1858,12 @@ static int __ovs_nla_copy_actions(const struct nlattr *attr,
                case OVS_ACTION_ATTR_PUSH_MPLS: {
                        const struct ovs_action_push_mpls *mpls = nla_data(a);
 
+                       /* Networking stack do not allow simultaneous Tunnel
+                        * and MPLS GSO.
+                        */
+                       if (out_tnl_port)
+                               return -EINVAL;
+
                        if (!eth_p_mpls(mpls->mpls_ethertype))
                                return -EINVAL;
                        /* Prohibit push MPLS other than to a white list
@@ -1888,10 +1898,11 @@ static int __ovs_nla_copy_actions(const struct nlattr *attr,
                        break;
 
                case OVS_ACTION_ATTR_SET:
-                       err = validate_set(a, key, sfa, &skip_copy, eth_type,
-                                          log);
+                       err = validate_set(a, key, sfa,
+                                          &out_tnl_port, eth_type, log);
                        if (err)
                                return err;
+                       skip_copy = out_tnl_port;
                        break;
 
                case OVS_ACTION_ATTR_SAMPLE:
index 274b1f1..4b38fd5 100644 (file)
@@ -41,6 +41,7 @@ openvswitch_headers += \
        linux/compat/include/linux/kernel.h \
        linux/compat/include/linux/list.h \
        linux/compat/include/linux/log2.h \
+       linux/compat/include/linux/mpls.h \
        linux/compat/include/linux/net.h \
        linux/compat/include/linux/random.h \
        linux/compat/include/linux/netdevice.h \
@@ -70,6 +71,7 @@ openvswitch_headers += \
        linux/compat/include/net/ip.h \
        linux/compat/include/net/ip_tunnels.h \
        linux/compat/include/net/ipv6.h \
+       linux/compat/include/net/mpls.h \
        linux/compat/include/net/net_namespace.h \
        linux/compat/include/net/netlink.h \
        linux/compat/include/net/udp.h \
index 8344293..96b5d3d 100644 (file)
 
 #include <net/gre.h>
 #include <net/icmp.h>
+#include <net/mpls.h>
 #include <net/protocol.h>
 #include <net/route.h>
 #include <net/xfrm.h>
 
 #include "gso.h"
-#include "mpls.h"
 #include "vlan.h"
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) && \
index 20ec55c..fd10848 100644 (file)
@@ -102,11 +102,19 @@ static inline void ovs_skb_init_inner_protocol(struct sk_buff *skb) {
         */
 }
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
 static inline void ovs_skb_set_inner_protocol(struct sk_buff *skb,
                                              __be16 ethertype)
 {
        skb->inner_protocol = ethertype;
 }
+#else
+static inline void ovs_skb_set_inner_protocol(struct sk_buff *skb,
+                                             __be16 ethertype)
+{
+       skb_set_inner_protocol(skb, ethertype);
+}
+#endif
 
 static inline __be16 ovs_skb_get_inner_protocol(struct sk_buff *skb)
 {
diff --git a/datapath/linux/compat/include/linux/mpls.h b/datapath/linux/compat/include/linux/mpls.h
new file mode 100644 (file)
index 0000000..ab99ebc
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef _UAPI_MPLS_WRAPPER_H
+#define _UAPI_MPLS_WRAPPER_H
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0)
+#include_next <linux/mpls.h>
+#else
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+/* Reference: RFC 5462, RFC 3032
+ *
+ *  0                   1                   2                   3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                Label                  | TC  |S|       TTL     |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *     Label:  Label Value, 20 bits
+ *     TC:     Traffic Class field, 3 bits
+ *     S:      Bottom of Stack, 1 bit
+ *     TTL:    Time to Live, 8 bits
+ */
+
+struct mpls_label {
+       __be32 entry;
+};
+
+#define MPLS_LS_LABEL_MASK      0xFFFFF000
+#define MPLS_LS_LABEL_SHIFT     12
+#define MPLS_LS_TC_MASK         0x00000E00
+#define MPLS_LS_TC_SHIFT        9
+#define MPLS_LS_S_MASK          0x00000100
+#define MPLS_LS_S_SHIFT         8
+#define MPLS_LS_TTL_MASK        0x000000FF
+#define MPLS_LS_TTL_SHIFT       0
+#endif
+
+#endif /* _UAPI_MPLS_WRAPPER_H */
diff --git a/datapath/linux/compat/include/net/mpls.h b/datapath/linux/compat/include/net/mpls.h
new file mode 100644 (file)
index 0000000..73e48e3
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014 Nicira, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _NET_MPLS_WRAPPER_H
+#define _NET_MPLS_WRAPPER_H 1
+
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+
+#define MPLS_HLEN 4
+
+static inline bool eth_p_mpls(__be16 eth_type)
+{
+       return eth_type == htons(ETH_P_MPLS_UC) ||
+               eth_type == htons(ETH_P_MPLS_MC);
+}
+
+/*
+ * For non-MPLS skbs this will correspond to the network header.
+ * For MPLS skbs it will be before the network_header as the MPLS
+ * label stack lies between the end of the mac header and the network
+ * 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)
+{
+       return skb_mac_header(skb) + skb->mac_len;
+}
+#endif /* _NET_MPLS_WRAPPER_H */
index 72bdec5..7930823 100644 (file)
@@ -1,7 +1,7 @@
 #include <linux/netdevice.h>
 #include <linux/if_vlan.h>
+#include <net/mpls.h>
 
-#include "mpls.h"
 #include "gso.h"
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
diff --git a/datapath/mpls.h b/datapath/mpls.h
deleted file mode 100644 (file)
index 7eab104..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef MPLS_H
-#define MPLS_H 1
-
-#include <linux/if_ether.h>
-
-#define MPLS_BOS_MASK  0x00000100
-#define MPLS_HLEN 4
-
-static inline bool eth_p_mpls(__be16 eth_type)
-{
-       return eth_type == htons(ETH_P_MPLS_UC) ||
-               eth_type == htons(ETH_P_MPLS_MC);
-}
-
-#endif