diff mbox

[net-next] NSH(Network Service Header) implementation

Message ID 1465205686-96724-1-git-send-email-yi.y.yang@intel.com
State Changes Requested, archived
Delegated to: David Miller
Headers show

Commit Message

Yang, Yi June 6, 2016, 9:34 a.m. UTC
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.li@intel.com>
Signed-off-by: Yi Yang <yi.y.yang@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

Comments

Pravin Shelar June 7, 2016, 12:45 a.m. UTC | #1
On Mon, Jun 6, 2016 at 2:34 AM, Yi Yang <yi.y.yang@intel.com> wrote:
> 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.li@intel.com>
> Signed-off-by: Yi Yang <yi.y.yang@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/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 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;
> +}

Networking stack or OVS can not handle arbitrary skb-protocol. For
example what happens if OVS has push vlan action or it sends this nsh
packet to net device which can not handle nsh packet? Even networking
stack can not parse such packet for handling offloads in software.
...

> 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;
>
This is compatibility code, no need to add new features to this code.
Now we should be directly using net devices.
Yang, Yi June 12, 2016, 7:53 a.m. UTC | #2
On Mon, Jun 06, 2016 at 05:45:08PM -0700, pravin shelar wrote:
> On Mon, Jun 6, 2016 at 2:34 AM, Yi Yang <yi.y.yang@intel.com> wrote:
> > 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.li@intel.com>
> > Signed-off-by: Yi Yang <yi.y.yang@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/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 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;
> > +}
> 
> Networking stack or OVS can not handle arbitrary skb-protocol. For
> example what happens if OVS has push vlan action or it sends this nsh
> packet to net device which can not handle nsh packet? Even networking
> stack can not parse such packet for handling offloads in software.
> ...
>
ovs nsh patch set have been sent out, they can handle nsh header. This
patch just implemented NSH push and pop actions, we do need some cdoe to
do some sanity check for the case you mentioned, but I think it is out
of scope of thi patch.

> > 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;
> >
> This is compatibility code, no need to add new features to this code.
> Now we should be directly using net devices.
Will use net device after those patches are merged into net-next, It
seems current net device implementation in net-next doesn't include it,
do you mean this one
"http://article.gmane.org/gmane.network.openvswitch.devel/64799"?
Jiri Benc June 13, 2016, 10:11 a.m. UTC | #3
On Sun, 12 Jun 2016 15:53:50 +0800, Yang, Yi wrote:
> Will use net device after those patches are merged into net-next, It
> seems current net device implementation in net-next doesn't include it,

The current kernel should have everything that's needed for VXLAN-GPE
support.

> do you mean this one
> "http://article.gmane.org/gmane.network.openvswitch.devel/64799"?

This patchset is absolutely needed for ovs to support new VXLAN
features, including VXLAN-GPE.

 Jiri
Yang, Yi June 13, 2016, 11:35 a.m. UTC | #4
On Mon, Jun 13, 2016 at 12:11:14PM +0200, Jiri Benc wrote:
> On Sun, 12 Jun 2016 15:53:50 +0800, Yang, Yi wrote:
> > Will use net device after those patches are merged into net-next, It
> > seems current net device implementation in net-next doesn't include it,
> 
> The current kernel should have everything that's needed for VXLAN-GPE
> support.
> 
> > do you mean this one
> > "http://article.gmane.org/gmane.network.openvswitch.devel/64799"?
> 
> This patchset is absolutely needed for ovs to support new VXLAN
> features, including VXLAN-GPE.
> 
>  Jiri

Got it, I'll rework it after Simon and Thadeu Lima de Souza Cascardo's
patches are merged.
diff mbox

Patch

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;
 }