[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <1465205686-96724-1-git-send-email-yi.y.yang@intel.com>
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