lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:	Mon,  6 Jun 2016 17:34:46 +0800
From:	Yi Yang <yi.y.yang@...el.com>
To:	netdev@...r.kernel.org
Cc:	dev@...nvswitch.org, Yi Yang <yi.y.yang@...el.com>,
	Johnson Li <johnson.li@...el.com>
Subject: [PATCH net-next] NSH(Network Service Header) implementation

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@...el.com>
Signed-off-by: Yi Yang <yi.y.yang@...el.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

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ