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