IETF defined NSH(Network Service Header) for Service
Function Chaining, this is an IETF draft

https://tools.ietf.org/html/draft-ietf-sfc-nsh-05

It will be a IETF standard shortly, this patch implemented
NSH for Open vSwitch.

Signed-off-by: Johnson Li <johnson...@intel.com>
Signed-off-by: Yi Yang <yi.y.y...@intel.com>
---
 drivers/net/vxlan.c              |   7 ++
 include/net/nsh.h                | 117 +++++++++++++++++++++++
 include/uapi/linux/openvswitch.h |  32 +++++++
 net/openvswitch/actions.c        |  68 +++++++++++++
 net/openvswitch/flow.c           |  45 ++++++++-
 net/openvswitch/flow.h           |  15 +++
 net/openvswitch/flow_netlink.c   | 202 ++++++++++++++++++++++++++++++++++++++-
 net/openvswitch/vport-netdev.c   |   3 +-
 net/openvswitch/vport-vxlan.c    |  15 +++
 9 files changed, 501 insertions(+), 3 deletions(-)
 create mode 100644 include/net/nsh.h

diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index f999db2..7a10fe99 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -42,6 +42,7 @@
 #include <net/netns/generic.h>
 #include <net/vxlan.h>
 #include <net/protocol.h>
+#include <net/nsh.h>
 
 #if IS_ENABLED(CONFIG_IPV6)
 #include <net/ipv6.h>
@@ -1225,6 +1226,9 @@ static bool vxlan_parse_gpe_hdr(struct vxlanhdr *unparsed,
        case VXLAN_GPE_NP_ETHERNET:
                *protocol = htons(ETH_P_TEB);
                break;
+       case VXLAN_GPE_NP_NSH:
+               *protocol = htons(ETH_P_NSH);
+               break;
        default:
                return false;
        }
@@ -1760,6 +1764,9 @@ static int vxlan_build_gpe_hdr(struct vxlanhdr *vxh, u32 
vxflags,
        case htons(ETH_P_TEB):
                gpe->next_protocol = VXLAN_GPE_NP_ETHERNET;
                return 0;
+       case htons(ETH_P_NSH):
+               gpe->next_protocol = VXLAN_GPE_NP_NSH;
+               return 0;
        }
        return -EPFNOSUPPORT;
 }
diff --git a/include/net/nsh.h b/include/net/nsh.h
new file mode 100644
index 0000000..98a342f
--- /dev/null
+++ b/include/net/nsh.h
@@ -0,0 +1,117 @@
+#ifndef __NET_NSH_H
+#define __NET_NSH_H 1
+
+#include <asm/byteorder.h>
+
+/*
+ * Network Service Header:
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |Ver|O|C|R|R|R|R|R|R|    Length   |   MD Type   |  Next Proto   |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                Service Path ID                | Service Index |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                                                               |
+ * ~               Mandatory/Optional Context Header               ~
+ * |                                                               |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * Ver = The version field is used to ensure backward compatibility
+ *       going forward with future NSH updates.  It MUST be set to 0x0
+ *       by the sender, in this first revision of NSH.
+ *
+ * O = OAM. when set to 0x1 indicates that this packet is an operations
+ *     and management (OAM) packet.  The receiving SFF and SFs nodes
+ *     MUST examine the payload and take appropriate action.
+ *
+ * C = context. Indicates that a critical metadata TLV is present.
+ *
+ * Length : total length, in 4-byte words, of NSH including the Base
+ *          Header, the Service Path Header and the optional variable
+ *          TLVs.
+ * MD Type: indicates the format of NSH beyond the mandatory Base Header
+ *          and the Service Path Header.
+ *
+ * Next Protocol: indicates the protocol type of the original packet. A
+ *          new IANA registry will be created for protocol type.
+ *
+ * Service Path Identifier (SPI): identifies a service path.
+ *          Participating nodes MUST use this identifier for Service
+ *          Function Path selection.
+ *
+ * Service Index (SI): provides location within the SFP.
+ *
+ * [0] https://tools.ietf.org/html/draft-ietf-sfc-nsh-01
+ */
+struct nsh_base {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+       __u8    reserved_flags1:4;
+       __u8    context_flag:1;
+       __u8    oam_flag:1;
+       __u8    version:2;
+
+       __u8    length:6;
+       __u8    reserved_flags2:2;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+       __u8    version:2;
+       __u8    oam_flag:1;
+       __u8    context_flag:1;
+       __u8    reserved_flags1:4;
+
+       __u8    reserved_flags2:2;
+       __u8    length:6;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+       __u8    md_type;
+       __u8    next_proto;
+       union {
+               struct {
+                       __u8    svc_path[3];
+                       __u8    svc_idx;
+               };
+               __be32 path_hdr;
+       };
+};
+
+/**
+ * struct nsh_md1_ctx - Keeps track of NSH context data
+ * @nshc<1-4>: NSH Contexts.
+ */
+struct nsh_md1_ctx {
+       __be32 nshc1;
+       __be32 nshc2;
+       __be32 nshc3;
+       __be32 nshc4;
+};
+
+/**
+ * struct nshdr - Network Service header
+ * @base: Network Service Base Header.
+ * @ctx: Network Service Context Header.
+ */
+struct nsh_hdr {
+       struct nsh_base base;
+       __be32 ctx[0]; /* Mandatory/optional Context Header */
+};
+
+#define NSH_DST_PORT    4790   /* UDP Port for NSH on VXLAN */
+#define ETH_P_NSH        0x894F   /* Ethertype for NSH */
+
+/* NSH Base Header Next Protocol */
+#define NSH_P_IPV4        0x01
+#define NSH_P_IPV6        0x02
+#define NSH_P_ETHERNET    0x03
+
+/* MD Type Registry */
+#define NSH_M_TYPE1     0x01
+#define NSH_M_EXP1      0xFE
+#define NSH_M_EXP2      0xFF
+
+/* Used for masking nsp and nsi values in field nsp below */
+#define NSH_M_NSP   0x00FFFFFF
+#define NSH_M_NSI   0xFF000000
+
+/* sizeof(struct nsh_hdr) + sizeof(struct nsh_md1_ctx) */
+#define NSH_M_TYPE1_LEN     24
+#define NSH_LEN_MAX        256
+
+#endif
diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h
index bb0d515..35b5b22 100644
--- a/include/uapi/linux/openvswitch.h
+++ b/include/uapi/linux/openvswitch.h
@@ -263,6 +263,7 @@ enum ovs_vport_attr {
 enum {
        OVS_VXLAN_EXT_UNSPEC,
        OVS_VXLAN_EXT_GBP,      /* Flag or __u32 */
+       OVS_VXLAN_EXT_GPE,      /* Flag, Generic Protocol Extension */
        __OVS_VXLAN_EXT_MAX,
 };
 
@@ -317,6 +318,7 @@ enum ovs_key_attr {
        OVS_KEY_ATTR_ND,        /* struct ovs_key_nd */
        OVS_KEY_ATTR_SKB_MARK,  /* u32 skb mark */
        OVS_KEY_ATTR_TUNNEL,    /* Nested set of ovs_tunnel attributes */
+       OVS_KEY_ATTR_NSH,       /* Nested set of ovs_nsh attributes */
        OVS_KEY_ATTR_SCTP,      /* struct ovs_key_sctp */
        OVS_KEY_ATTR_TCP_FLAGS, /* be16 TCP flags. */
        OVS_KEY_ATTR_DP_HASH,      /* u32 hash value. Value 0 indicates the hash
@@ -359,6 +361,21 @@ enum ovs_tunnel_key_attr {
 
 #define OVS_TUNNEL_KEY_ATTR_MAX (__OVS_TUNNEL_KEY_ATTR_MAX - 1)
 
+enum ovs_nsh_key_attr {
+       OVS_NSH_KEY_ATTR_FLAGS,                 /* u8 NSH header flags */
+       OVS_NSH_KEY_ATTR_MD_TYPE,               /* u8 Metadata Type */
+       OVS_NSH_KEY_ATTR_NEXT_PROTO,            /* u8 Next Protocol */
+       OVS_NSH_KEY_ATTR_NSI,                   /* u8 Service Index */
+       OVS_NSH_KEY_ATTR_NSP,                   /* be32 Service Path ID */
+       OVS_NSH_KEY_ATTR_NSHC1,                 /* be32 NSH Context Header 1 */
+       OVS_NSH_KEY_ATTR_NSHC2,                 /* be32 NSH Context Header 2 */
+       OVS_NSH_KEY_ATTR_NSHC3,                 /* be32 NSH Context Header 3 */
+       OVS_NSH_KEY_ATTR_NSHC4,                 /* be32 NSH Context Header 4 */
+       __OVS_NSH_KEY_ATTR_MAX
+};
+
+#define OVS_NSH_KEY_ATTR_MAX (__OVS_NSH_KEY_ATTR_MAX - 1)
+
 /**
  * enum ovs_frag_type - IPv4 and IPv6 fragment type
  * @OVS_FRAG_TYPE_NONE: Packet is not a fragment.
@@ -609,6 +626,17 @@ struct ovs_action_push_vlan {
        __be16 vlan_tci;        /* 802.1Q TCI (VLAN ID and priority). */
 };
 
+/**
+ * struct ovs_action_push_nsh - %OVS_ACTION_ATTR_PUSH_NSH action argument.
+ * @len: length of NSH header. Differs since different metadata type.
+ * @header: RAW value for the NSH header.
+ */
+#define NSH_HEADER_LEN_MAX     256
+struct ovs_action_push_nsh {
+       uint16_t len;
+       uint8_t header[NSH_HEADER_LEN_MAX]; /* NSH header */
+};
+
 /* Data path hash algorithm for computing Datapath hash.
  *
  * The algorithm type only specifies the fields in a flow
@@ -730,6 +758,8 @@ enum ovs_nat_attr {
  * is no MPLS label stack, as determined by ethertype, no action is taken.
  * @OVS_ACTION_ATTR_CT: Track the connection. Populate the conntrack-related
  * entries in the flow key.
+ * @OVS_ACTION_ATTR_PUSH_NSH: Push a Network Service Header into the packets.
+ * @OVS_ACTION_ATTR_POP_NSH: Strip the Network Service Header from packets.
  *
  * Only a single header can be set with a single %OVS_ACTION_ATTR_SET.  Not all
  * fields within a header are modifiable, e.g. the IPv4 protocol and fragment
@@ -756,6 +786,8 @@ enum ovs_action_attr {
                                       * The data must be zero for the unmasked
                                       * bits. */
        OVS_ACTION_ATTR_CT,           /* Nested OVS_CT_ATTR_* . */
+       OVS_ACTION_ATTR_PUSH_NSH,     /* struct ovs_action_push_nsh. */
+       OVS_ACTION_ATTR_POP_NSH,      /* No argument. */
 
        __OVS_ACTION_ATTR_MAX,        /* Nothing past this will be accepted
                                       * from userspace. */
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
index 9a3eb7a..38e787c 100644
--- a/net/openvswitch/actions.c
+++ b/net/openvswitch/actions.c
@@ -29,6 +29,7 @@
 #include <linux/in6.h>
 #include <linux/if_arp.h>
 #include <linux/if_vlan.h>
+#include <linux/if_ether.h>
 
 #include <net/dst.h>
 #include <net/ip.h>
@@ -38,6 +39,7 @@
 #include <net/dsfield.h>
 #include <net/mpls.h>
 #include <net/sctp/checksum.h>
+#include <net/nsh.h>
 
 #include "datapath.h"
 #include "flow.h"
@@ -259,6 +261,64 @@ static int push_vlan(struct sk_buff *skb, struct 
sw_flow_key *key,
                             ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT);
 }
 
+static int pop_nsh(struct sk_buff *skb, struct sw_flow_key *key)
+{
+       struct nsh_hdr *nsh_hdr = NULL;
+       u16 length = 0;
+
+       nsh_hdr = (struct nsh_hdr *)(skb->data);
+       length  = nsh_hdr->base.length << 2;
+
+       switch (nsh_hdr->base.next_proto) {
+       case NSH_P_IPV4:
+               skb->protocol = htons(ETH_P_IP);
+               key->eth.type = htons(ETH_P_IP);
+               break;
+       case NSH_P_IPV6:
+               skb->protocol = htons(ETH_P_IPV6);
+               key->eth.type = htons(ETH_P_IPV6);
+               break;
+       case NSH_P_ETHERNET:
+               skb->protocol = htons(ETH_P_TEB);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       __skb_pull(skb, length);
+       skb_reset_mac_header(skb);
+       skb_reset_mac_len(skb);
+
+       memset(&key->nsh, 0, sizeof(key->nsh));
+
+       return 0;
+}
+
+static int push_nsh(struct sk_buff *skb, struct sw_flow_key *key,
+                   const struct ovs_action_push_nsh *nsh)
+{
+       if (nsh->len > 0 && nsh->len <= 256) {
+               struct nsh_hdr *nsh_hdr = NULL;
+
+               if (skb_cow_head(skb, nsh->len) < 0)
+                       return -ENOMEM;
+
+               skb_push(skb, nsh->len);
+               nsh_hdr = (struct nsh_hdr *)(skb->data);
+               memcpy(nsh_hdr, nsh->header, nsh->len);
+
+               if (!skb->inner_protocol)
+                       skb_set_inner_protocol(skb, skb->protocol);
+
+               skb->protocol = htons(ETH_P_NSH); /* 0x894F */
+               key->eth.type = htons(ETH_P_NSH);
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 /* 'src' is already properly masked. */
 static void ether_addr_copy_masked(u8 *dst_, const u8 *src_, const u8 *mask_)
 {
@@ -1083,6 +1143,14 @@ static int do_execute_actions(struct datapath *dp, 
struct sk_buff *skb,
                        err = pop_vlan(skb, key);
                        break;
 
+               case OVS_ACTION_ATTR_PUSH_NSH:
+                       err = push_nsh(skb, key, nla_data(a));
+                       break;
+
+               case OVS_ACTION_ATTR_POP_NSH:
+                       err = pop_nsh(skb, key);
+                       break;
+
                case OVS_ACTION_ATTR_RECIRC:
                        err = execute_recirc(dp, skb, key, a, rem);
                        if (nla_is_last(a, rem)) {
diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c
index 0ea128e..5701612 100644
--- a/net/openvswitch/flow.c
+++ b/net/openvswitch/flow.c
@@ -45,6 +45,7 @@
 #include <net/ipv6.h>
 #include <net/mpls.h>
 #include <net/ndisc.h>
+#include <net/nsh.h>
 
 #include "conntrack.h"
 #include "datapath.h"
@@ -302,6 +303,36 @@ static bool icmp6hdr_ok(struct sk_buff *skb)
                                  sizeof(struct icmp6hdr));
 }
 
+/**
+ * Basic assumption is that the MD Type equals 1.
+ */
+static int parse_nsh(struct sk_buff *skb, struct sw_flow_key *key)
+{
+       struct nsh_hdr *nsh_hdr = (struct nsh_hdr *)skb_mac_header(skb);
+       struct nsh_md1_ctx *ctx = NULL;
+       int length = 0;
+
+       length = nsh_hdr->base.length << 2;
+       if (length > NSH_LEN_MAX)
+               return -EINVAL;
+
+       if (nsh_hdr->base.md_type != NSH_M_TYPE1)
+               return -EINVAL;
+
+       ctx = (struct nsh_md1_ctx *)(nsh_hdr->ctx);
+       key->nsh.md_type = nsh_hdr->base.md_type;
+       key->nsh.next_proto = nsh_hdr->base.next_proto;
+       key->nsh.nsi = nsh_hdr->base.svc_idx;
+       key->nsh.nsp = nsh_hdr->base.path_hdr << 8;
+       key->nsh.nshc1 = ctx->nshc1;
+       key->nsh.nshc2 = ctx->nshc2;
+       key->nsh.nshc3 = ctx->nshc3;
+       key->nsh.nshc4 = ctx->nshc4;
+
+       __skb_pull(skb, length);
+       return length;
+}
+
 static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key)
 {
        struct qtag_prefix {
@@ -460,7 +491,7 @@ invalid:
  */
 static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
 {
-       int error;
+       int error, nsh_len = 0;
        struct ethhdr *eth;
 
        /* Flags are always used as part of stats */
@@ -495,6 +526,14 @@ static int key_extract(struct sk_buff *skb, struct 
sw_flow_key *key)
        skb_reset_mac_len(skb);
        __skb_push(skb, skb->data - skb_mac_header(skb));
 
+       /* Network Service Header */
+       memset(&key->nsh, 0, sizeof(key->nsh));
+       if (skb->protocol == htons(ETH_P_NSH)) {
+               nsh_len = parse_nsh(skb, key);
+               if (unlikely(nsh_len <= 0))
+                       return -EINVAL;
+       }
+
        /* Network layer. */
        if (key->eth.type == htons(ETH_P_IP)) {
                struct iphdr *nh;
@@ -685,6 +724,10 @@ static int key_extract(struct sk_buff *skb, struct 
sw_flow_key *key)
                        }
                }
        }
+
+       if (nsh_len > 0)
+               __skb_push(skb, nsh_len);
+
        return 0;
 }
 
diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h
index 03378e7..584fd78 100644
--- a/net/openvswitch/flow.h
+++ b/net/openvswitch/flow.h
@@ -54,10 +54,25 @@ struct ovs_tunnel_info {
        (offsetof(struct sw_flow_key, recirc_id) +      \
        FIELD_SIZEOF(struct sw_flow_key, recirc_id))
 
+/* Network Service Header, MD Type I only for the moment.
+ */
+struct ovs_nsh_key {
+       u8  flags;
+       u8  md_type;    /* NSH metadata type */
+       u8  next_proto; /* NSH next protocol */
+       u8  nsi;        /* NSH index */
+       u32 nsp;        /* NSH path id */
+       u32 nshc1;      /* NSH context C1-C4 */
+       u32 nshc2;
+       u32 nshc3;
+       u32 nshc4;
+} __packed __aligned(4); /* Minimize padding. */
+
 struct sw_flow_key {
        u8 tun_opts[IP_TUNNEL_OPTS_MAX];
        u8 tun_opts_len;
        struct ip_tunnel_key tun_key;   /* Encapsulating tunnel key. */
+       struct ovs_nsh_key nsh;         /* network service header */
        struct {
                u32     priority;       /* Packet QoS priority. */
                u32     skb_mark;       /* SKB mark. */
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index 0bb650f..f80ff66 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -145,6 +145,13 @@ static bool match_validate(const struct sw_flow_match 
*match,
                       | (1 << OVS_KEY_ATTR_IN_PORT)
                       | (1 << OVS_KEY_ATTR_ETHERTYPE));
 
+       /* Network Service Header */
+       if (match->key->nsh.md_type) {
+               key_expected |= 1 << OVS_KEY_ATTR_NSH;
+               if (match->mask)
+                       mask_allowed |= 1 << OVS_KEY_ATTR_NSH;
+       }
+
        /* Check key attributes. */
        if (match->key->eth.type == htons(ETH_P_ARP)
                        || match->key->eth.type == htons(ETH_P_RARP)) {
@@ -277,12 +284,25 @@ size_t ovs_tun_key_attr_size(void)
                + nla_total_size(2);   /* OVS_TUNNEL_KEY_ATTR_TP_DST */
 }
 
+size_t ovs_nsh_key_attr_size(void)
+{
+       return    nla_total_size(1)    /* OVS_NSH_KEY_ATTR_FLAGS */
+               + nla_total_size(1)    /* OVS_NSH_KEY_ATTR_MD_TYPE */
+               + nla_total_size(1)    /* OVS_NSH_KEY_ATTR_NEXT_PROTO */
+               + nla_total_size(1)    /* OVS_NSH_KEY_ATTR_NSI */
+               + nla_total_size(4)    /* OVS_NSH_KEY_ATTR_NSP */
+               + nla_total_size(4)    /* OVS_NSH_KEY_ATTR_NSHC1 */
+               + nla_total_size(4)    /* OVS_NSH_KEY_ATTR_NSHC2 */
+               + nla_total_size(4)    /* OVS_NSH_KEY_ATTR_NSHC3 */
+               + nla_total_size(4);   /* OVS_NSH_KEY_ATTR_NSHC4 */
+}
+
 size_t ovs_key_attr_size(void)
 {
        /* Whenever adding new OVS_KEY_ FIELDS, we should consider
         * updating this function.
         */
-       BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 26);
+       BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 27);
 
        return    nla_total_size(4)   /* OVS_KEY_ATTR_PRIORITY */
                + nla_total_size(0)   /* OVS_KEY_ATTR_TUNNEL */
@@ -298,6 +318,8 @@ size_t ovs_key_attr_size(void)
                + nla_total_size(12)  /* OVS_KEY_ATTR_ETHERNET */
                + nla_total_size(2)   /* OVS_KEY_ATTR_ETHERTYPE */
                + nla_total_size(4)   /* OVS_KEY_ATTR_VLAN */
+               + nla_total_size(0)   /* OVS_KEY_ATTR_NSH */
+               + ovs_nsh_key_attr_size()
                + nla_total_size(0)   /* OVS_KEY_ATTR_ENCAP */
                + nla_total_size(2)   /* OVS_KEY_ATTR_ETHERTYPE */
                + nla_total_size(40)  /* OVS_KEY_ATTR_IPV6 */
@@ -327,6 +349,18 @@ static const struct ovs_len_tbl 
ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1]
        [OVS_TUNNEL_KEY_ATTR_IPV6_DST]      = { .len = sizeof(struct in6_addr) 
},
 };
 
+static const struct ovs_len_tbl ovs_nsh_key_lens[OVS_NSH_KEY_ATTR_MAX + 1] = {
+       [OVS_NSH_KEY_ATTR_FLAGS]            = { .len = 1 },
+       [OVS_NSH_KEY_ATTR_MD_TYPE]          = { .len = 1 },
+       [OVS_NSH_KEY_ATTR_NEXT_PROTO]       = { .len = 1 },
+       [OVS_NSH_KEY_ATTR_NSI]              = { .len = 1 },
+       [OVS_NSH_KEY_ATTR_NSP]              = { .len = 4 },
+       [OVS_NSH_KEY_ATTR_NSHC1]            = { .len = 4 },
+       [OVS_NSH_KEY_ATTR_NSHC2]            = { .len = 4 },
+       [OVS_NSH_KEY_ATTR_NSHC3]            = { .len = 4 },
+       [OVS_NSH_KEY_ATTR_NSHC4]            = { .len = 4 },
+};
+
 /* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute.  */
 static const struct ovs_len_tbl ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
        [OVS_KEY_ATTR_ENCAP]     = { .len = OVS_ATTR_NESTED },
@@ -335,6 +369,8 @@ static const struct ovs_len_tbl 
ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
        [OVS_KEY_ATTR_SKB_MARK]  = { .len = sizeof(u32) },
        [OVS_KEY_ATTR_ETHERNET]  = { .len = sizeof(struct ovs_key_ethernet) },
        [OVS_KEY_ATTR_VLAN]      = { .len = sizeof(__be16) },
+       [OVS_KEY_ATTR_NSH]       = { .len = OVS_ATTR_NESTED,
+                                    .next = ovs_nsh_key_lens, },
        [OVS_KEY_ATTR_ETHERTYPE] = { .len = sizeof(__be16) },
        [OVS_KEY_ATTR_IPV4]      = { .len = sizeof(struct ovs_key_ipv4) },
        [OVS_KEY_ATTR_IPV6]      = { .len = sizeof(struct ovs_key_ipv6) },
@@ -901,6 +937,151 @@ static int metadata_from_nlattrs(struct net *net, struct 
sw_flow_match *match,
        return 0;
 }
 
+static int nsh_from_nlattr(const struct nlattr *attr,
+                          struct sw_flow_match *match, bool is_mask,
+                          bool log)
+{
+       struct nlattr *a;
+       int rem;
+
+       nla_for_each_nested(a, attr, rem) {
+               int type = nla_type(a);
+
+               if (type > OVS_NSH_KEY_ATTR_MAX) {
+                       OVS_NLERR(log, "NSH attr %d out of range max %d",
+                                 type, OVS_NSH_KEY_ATTR_MAX);
+                       return -EINVAL;
+               }
+
+               if (!check_attr_len(nla_len(a),
+                                   ovs_nsh_key_lens[type].len)) {
+                       OVS_NLERR(log, "NSH attr %d has unexpected len %d",
+                                 type, nla_len(a));
+                       return -EINVAL;
+               }
+
+               switch (type) {
+               case OVS_NSH_KEY_ATTR_FLAGS:
+                       SW_FLOW_KEY_PUT(match, nsh.flags,
+                                       nla_get_u8(a), is_mask);
+                       break;
+               case OVS_NSH_KEY_ATTR_MD_TYPE:
+                       SW_FLOW_KEY_PUT(match, nsh.md_type,
+                                       nla_get_u8(a), is_mask);
+                       break;
+               case OVS_NSH_KEY_ATTR_NEXT_PROTO:
+                       SW_FLOW_KEY_PUT(match, nsh.next_proto,
+                                       nla_get_u8(a), is_mask);
+                       break;
+               case OVS_NSH_KEY_ATTR_NSI:
+                       SW_FLOW_KEY_PUT(match, nsh.nsi,
+                                       nla_get_u8(a), is_mask);
+                       break;
+               case OVS_NSH_KEY_ATTR_NSP:
+                       SW_FLOW_KEY_PUT(match, nsh.nsp,
+                                       nla_get_be32(a), is_mask);
+                       break;
+               case OVS_NSH_KEY_ATTR_NSHC1:
+                       SW_FLOW_KEY_PUT(match, nsh.nshc1,
+                                       nla_get_be32(a), is_mask);
+                       break;
+               case OVS_NSH_KEY_ATTR_NSHC2:
+                       SW_FLOW_KEY_PUT(match, nsh.nshc2,
+                                       nla_get_be32(a), is_mask);
+                       break;
+               case OVS_NSH_KEY_ATTR_NSHC3:
+                       SW_FLOW_KEY_PUT(match, nsh.nshc3,
+                                       nla_get_be32(a), is_mask);
+                       break;
+               case OVS_NSH_KEY_ATTR_NSHC4:
+                       SW_FLOW_KEY_PUT(match, nsh.nshc4,
+                                       nla_get_be32(a), is_mask);
+                       break;
+               default:
+                       OVS_NLERR(log, "Unknown NSH attribute %d",
+                                 type);
+                       return -EINVAL;
+               }
+       }
+
+       if (rem > 0) {
+               OVS_NLERR(log, "NSH attribute has %d unknown bytes.",
+                         rem);
+               return -EINVAL;
+       }
+
+       if (!is_mask) {
+               if (!match->key->nsh.md_type) {
+                       OVS_NLERR(log, "NSH Header MD Type is zero");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int __nsh_to_nlattr(struct sk_buff *skb,
+                          const struct ovs_nsh_key *output)
+{
+       if (output->md_type) {
+               if (output->flags &&
+                   nla_put_u8(skb, OVS_NSH_KEY_ATTR_FLAGS,
+                              output->flags))
+                       return -EMSGSIZE;
+               if (nla_put_u8(skb, OVS_NSH_KEY_ATTR_MD_TYPE,
+                              output->md_type))
+                       return -EMSGSIZE;
+               if (output->next_proto &&
+                   nla_put_u8(skb, OVS_NSH_KEY_ATTR_NEXT_PROTO,
+                              output->next_proto))
+                       return -EMSGSIZE;
+               if (output->nsi &&
+                   nla_put_u8(skb, OVS_NSH_KEY_ATTR_NSI,
+                              output->nsi))
+                       return -EMSGSIZE;
+               if (output->nsp &&
+                   nla_put_be32(skb, OVS_NSH_KEY_ATTR_NSP,
+                                output->nsp))
+                       return -EMSGSIZE;
+               if (output->nshc1 &&
+                   nla_put_be32(skb, OVS_NSH_KEY_ATTR_NSHC1,
+                                output->nshc1))
+                       return -EMSGSIZE;
+               if (output->nshc2 &&
+                   nla_put_be32(skb, OVS_NSH_KEY_ATTR_NSHC2,
+                                output->nshc2))
+                       return -EMSGSIZE;
+               if (output->nshc3 &&
+                   nla_put_be32(skb, OVS_NSH_KEY_ATTR_NSHC3,
+                                output->nshc3))
+                       return -EMSGSIZE;
+               if (output->nshc4 &&
+                   nla_put_be32(skb, OVS_NSH_KEY_ATTR_NSHC4,
+                                output->nshc4))
+                       return -EMSGSIZE;
+       }
+
+       return 0;
+}
+
+static int nsh_to_nlattr(struct sk_buff *skb,
+                        const struct ovs_nsh_key *output)
+{
+       struct nlattr *nla;
+       int err;
+
+       nla = nla_nest_start(skb, OVS_KEY_ATTR_NSH);
+       if (!nla)
+               return -EMSGSIZE;
+
+       err = __nsh_to_nlattr(skb, output);
+       if (err)
+               return err;
+
+       nla_nest_end(skb, nla);
+       return 0;
+}
+
 static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match,
                                u64 attrs, const struct nlattr **a,
                                bool is_mask, bool log)
@@ -911,6 +1092,14 @@ static int ovs_key_from_nlattrs(struct net *net, struct 
sw_flow_match *match,
        if (err)
                return err;
 
+       if (attrs & (1 << OVS_KEY_ATTR_NSH)) {
+               if (nsh_from_nlattr(a[OVS_KEY_ATTR_NSH], match,
+                                   is_mask, log) < 0) {
+                       return -EINVAL;
+               }
+               attrs &= ~(1 << OVS_KEY_ATTR_NSH);
+       }
+
        if (attrs & (1 << OVS_KEY_ATTR_ETHERNET)) {
                const struct ovs_key_ethernet *eth_key;
 
@@ -1437,6 +1626,11 @@ static int __ovs_nla_put_key(const struct sw_flow_key 
*swkey,
                        goto nla_put_failure;
        }
 
+       if ((swkey->nsh.md_type)) {
+               if (nsh_to_nlattr(skb, &output->nsh))
+                       goto nla_put_failure;
+       }
+
        if (swkey->phy.in_port == DP_MAX_PORTS) {
                if (is_mask && (output->phy.in_port == 0xffff))
                        if (nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, 0xffffffff))
@@ -2229,6 +2423,8 @@ static int __ovs_nla_copy_actions(struct net *net, const 
struct nlattr *attr,
                        [OVS_ACTION_ATTR_SAMPLE] = (u32)-1,
                        [OVS_ACTION_ATTR_HASH] = sizeof(struct ovs_action_hash),
                        [OVS_ACTION_ATTR_CT] = (u32)-1,
+                       [OVS_ACTION_ATTR_PUSH_NSH] = sizeof(struct 
ovs_action_push_nsh),
+                       [OVS_ACTION_ATTR_POP_NSH] = 0,
                };
                const struct ovs_action_push_vlan *vlan;
                int type = nla_type(a);
@@ -2281,6 +2477,10 @@ static int __ovs_nla_copy_actions(struct net *net, const 
struct nlattr *attr,
                        vlan_tci = vlan->vlan_tci;
                        break;
 
+               case OVS_ACTION_ATTR_PUSH_NSH:
+               case OVS_ACTION_ATTR_POP_NSH:
+                       break;
+
                case OVS_ACTION_ATTR_RECIRC:
                        break;
 
diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c
index 4e39723..b0f216a 100644
--- a/net/openvswitch/vport-netdev.c
+++ b/net/openvswitch/vport-netdev.c
@@ -97,7 +97,8 @@ struct vport *ovs_netdev_link(struct vport *vport, const char 
*name)
        }
 
        if (vport->dev->flags & IFF_LOOPBACK ||
-           vport->dev->type != ARPHRD_ETHER ||
+           (vport->dev->type != ARPHRD_ETHER &&
+            vport->dev->type != ARPHRD_NONE) ||
            ovs_is_internal_dev(vport->dev)) {
                err = -EINVAL;
                goto error_put;
diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c
index 5eb7694..3d060c4 100644
--- a/net/openvswitch/vport-vxlan.c
+++ b/net/openvswitch/vport-vxlan.c
@@ -52,6 +52,18 @@ static int vxlan_get_options(const struct vport *vport, 
struct sk_buff *skb)
                        return -EMSGSIZE;
 
                nla_nest_end(skb, exts);
+       } else if (vxlan->flags & VXLAN_F_GPE) {
+               struct nlattr *exts;
+
+               exts = nla_nest_start(skb, OVS_TUNNEL_ATTR_EXTENSION);
+               if (!exts)
+                       return -EMSGSIZE;
+
+               if (vxlan->flags & VXLAN_F_GPE &&
+                   nla_put_flag(skb, OVS_VXLAN_EXT_GPE))
+                       return -EMSGSIZE;
+
+               nla_nest_end(skb, exts);
        }
 
        return 0;
@@ -59,6 +71,7 @@ static int vxlan_get_options(const struct vport *vport, 
struct sk_buff *skb)
 
 static const struct nla_policy exts_policy[OVS_VXLAN_EXT_MAX + 1] = {
        [OVS_VXLAN_EXT_GBP]     = { .type = NLA_FLAG, },
+       [OVS_VXLAN_EXT_GPE]     = { .type = NLA_FLAG, },
 };
 
 static int vxlan_configure_exts(struct vport *vport, struct nlattr *attr,
@@ -76,6 +89,8 @@ static int vxlan_configure_exts(struct vport *vport, struct 
nlattr *attr,
 
        if (exts[OVS_VXLAN_EXT_GBP])
                conf->flags |= VXLAN_F_GBP;
+       else if (exts[OVS_VXLAN_EXT_GPE])
+               conf->flags |= VXLAN_F_GPE;
 
        return 0;
 }
-- 
1.9.3

Reply via email to