On Wed, Aug 16, 2017 at 01:35:30PM +0800, Yi Yang wrote: > v2->v3 > - Change OVS_KEY_ATTR_NSH to nested key to handle > length-fixed attributes and length-variable > attriubte more flexibly. > - Remove struct ovs_action_push_nsh completely > - Add code to handle nested attribute for SET_MASKED > - Change PUSH_NSH to use the nested OVS_KEY_ATTR_NSH > to transfer NSH header data. > - Fix comments and coding style issues by Jiri and Eric
Thanks for the updates Yi. Some feedback below. > > v1->v2 > - Change encap_nsh and decap_nsh to push_nsh and pop_nsh > - Dynamically allocate struct ovs_action_push_nsh for > length-variable metadata. > > OVS master and 2.8 branch has merged NSH userspace > patch series, this patch is to enable NSH support > in kernel data path in order that OVS can support > NSH in 2.8 release in compat mode by porting this. > > Signed-off-by: Yi Yang <yi.y.y...@intel.com> > --- > drivers/net/vxlan.c | 7 + > include/net/nsh.h | 150 +++++++++++++++++++ > include/uapi/linux/openvswitch.h | 30 ++++ > net/openvswitch/actions.c | 175 ++++++++++++++++++++++ > net/openvswitch/flow.c | 39 +++++ > net/openvswitch/flow.h | 11 ++ > net/openvswitch/flow_netlink.c | 304 > ++++++++++++++++++++++++++++++++++++++- > net/openvswitch/flow_netlink.h | 4 + > 8 files changed, 719 insertions(+), 1 deletion(-) > create mode 100644 include/net/nsh.h > [..] > diff --git a/include/net/nsh.h b/include/net/nsh.h > new file mode 100644 > index 0000000..54f44f6 > --- /dev/null > +++ b/include/net/nsh.h > @@ -0,0 +1,150 @@ [..] > +#define NSH_VER_MASK 0xc000 > +#define NSH_VER_SHIFT 14 > +#define NSH_FLAGS_MASK 0x3fc0 > +#define NSH_FLAGS_SHIFT 6 > +#define NSH_LEN_MASK 0x003f > +#define NSH_LEN_SHIFT 0 > + > +#define NSH_SPI_MASK 0xffffff00 > +#define NSH_SPI_SHIFT 8 > +#define NSH_SI_MASK 0x000000ff > +#define NSH_SI_SHIFT 0 > + > +#define NSH_DST_PORT 4790 /* UDP Port for NSH on VXLAN. */ > +#define ETH_P_NSH 0x894F /* Ethertype for NSH. */ ETH_P_NSH probably belongs in include/uapi/linux/if_ether.h with all the other ETH_P_* defines. > + > +/* NSH Base Header Next Protocol. */ > +#define NSH_P_IPV4 0x01 > +#define NSH_P_IPV6 0x02 > +#define NSH_P_ETHERNET 0x03 > +#define NSH_P_NSH 0x04 > +#define NSH_P_MPLS 0x05 > + > +/* MD Type Registry. */ > +#define NSH_M_TYPE1 0x01 > +#define NSH_M_TYPE2 0x02 > +#define NSH_M_EXP1 0xFE > +#define NSH_M_EXP2 0xFF > + > +/* NSH Metadata Length. */ > +#define NSH_M_TYPE1_MDLEN 16 > + > +/* NSH Base Header Length */ > +#define NSH_BASE_HDR_LEN 8 > + > +/* NSH MD Type 1 header Length. */ > +#define NSH_M_TYPE1_LEN 24 > + [..] > diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h > index 1875bba..6c738d0 100644 > --- a/net/openvswitch/flow.h > +++ b/net/openvswitch/flow.h > @@ -35,6 +35,7 @@ > #include <net/inet_ecn.h> > #include <net/ip_tunnels.h> > #include <net/dst_metadata.h> > +#include <net/nsh.h> > > struct sk_buff; > > @@ -66,6 +67,15 @@ struct vlan_head { > (offsetof(struct sw_flow_key, recirc_id) + \ > FIELD_SIZEOF(struct sw_flow_key, recirc_id)) > > +struct ovs_key_nsh { > + __u8 flags; > + __u8 mdtype; > + __u8 np; > + __u8 pad; > + __be32 path_hdr; > + __be32 context[NSH_MD1_CONTEXT_SIZE]; > +}; > + > struct sw_flow_key { > u8 tun_opts[IP_TUNNEL_OPTS_MAX]; > u8 tun_opts_len; > @@ -144,6 +154,7 @@ struct sw_flow_key { > }; > } ipv6; > }; > + struct ovs_key_nsh nsh; /* network service header */ Are you intentionally not reserving space in the flow key for OVS_NSH_KEY_ATTR_MD2? I know it's not supported yet, but much of the code is stubbed out for it - just making sure this wasn't an oversight. > struct { > /* Connection tracking fields not packed above. */ > struct { > diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c > index f07d10a..79059db 100644 > --- a/net/openvswitch/flow_netlink.c > +++ b/net/openvswitch/flow_netlink.c > @@ -78,9 +78,11 @@ static bool actions_may_change_flow(const struct nlattr > *actions) > case OVS_ACTION_ATTR_HASH: > case OVS_ACTION_ATTR_POP_ETH: > case OVS_ACTION_ATTR_POP_MPLS: > + case OVS_ACTION_ATTR_POP_NSH: > case OVS_ACTION_ATTR_POP_VLAN: > case OVS_ACTION_ATTR_PUSH_ETH: > case OVS_ACTION_ATTR_PUSH_MPLS: > + case OVS_ACTION_ATTR_PUSH_NSH: > case OVS_ACTION_ATTR_PUSH_VLAN: > case OVS_ACTION_ATTR_SAMPLE: > case OVS_ACTION_ATTR_SET: > @@ -322,12 +324,22 @@ 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) > +{ > + /* Whenever adding new OVS_NSH_KEY_ FIELDS, we should consider > + * updating this function. > + */ > + return nla_total_size(8) /* OVS_NSH_KEY_ATTR_BASE */ > + + nla_total_size(16) /* OVS_NSH_KEY_ATTR_MD1 */ > + + nla_total_size(248); /* OVS_NSH_KEY_ATTR_MD2 */ _MD1 and _MD2 are mutually exclusive. You only need the larger of the two. Maybe use OVS_PUSH_NSH_MAX_MD_LEN instead of 248. > +} > + > 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 != 28); > + BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 29); > > return nla_total_size(4) /* OVS_KEY_ATTR_PRIORITY */ > + nla_total_size(0) /* OVS_KEY_ATTR_TUNNEL */ > @@ -341,6 +353,8 @@ size_t ovs_key_attr_size(void) > + nla_total_size(4) /* OVS_KEY_ATTR_CT_MARK */ > + nla_total_size(16) /* OVS_KEY_ATTR_CT_LABELS */ > + nla_total_size(40) /* OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6 */ > + + nla_total_size(0) /* OVS_KEY_ATTR_NSH */ > + + ovs_nsh_key_attr_size() > + nla_total_size(12) /* OVS_KEY_ATTR_ETHERNET */ > + nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */ > + nla_total_size(4) /* OVS_KEY_ATTR_VLAN */ > @@ -373,6 +387,13 @@ 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_attr_lens[OVS_NSH_KEY_ATTR_MAX + 1] = { > + [OVS_NSH_KEY_ATTR_BASE] = { .len = 8 }, > + [OVS_NSH_KEY_ATTR_MD1] = { .len = 16 }, > + [OVS_NSH_KEY_ATTR_MD2] = { .len = OVS_ATTR_VARIABLE }, > +}; > + > /* 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 }, > @@ -405,6 +426,8 @@ static const struct ovs_len_tbl > ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { > .len = sizeof(struct ovs_key_ct_tuple_ipv4) }, > [OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6] = { > .len = sizeof(struct ovs_key_ct_tuple_ipv6) }, > + [OVS_KEY_ATTR_NSH] = { .len = OVS_ATTR_NESTED, > + .next = ovs_nsh_key_attr_lens, }, > }; > > static bool check_attr_len(unsigned int attr_len, unsigned int expected_len) > @@ -1179,6 +1202,209 @@ static int metadata_from_nlattrs(struct net *net, > struct sw_flow_match *match, > return 0; > } > > +int push_nsh_para_from_nlattr(const struct nlattr *attr, > + struct push_nsh_para *pnp) > +{ > + struct nlattr *a; > + int rem; > + u8 flags = 0; > + size_t mdlen = 0; > + > + nla_for_each_nested(a, attr, rem) { > + int type = nla_type(a); > + > + if (type > OVS_NSH_KEY_ATTR_MAX) { > + OVS_NLERR(1, "nsh attr %d is out of range max %d", > + type, OVS_NSH_KEY_ATTR_MAX); > + return -EINVAL; > + } > + > + if (!check_attr_len(nla_len(a), > + ovs_nsh_key_attr_lens[type].len)) { > + OVS_NLERR( > + 1, > + "nsh attr %d has unexpected len %d expected %d", > + type, > + nla_len(a), > + ovs_nsh_key_attr_lens[type].len > + ); > + return -EINVAL; > + } > + > + switch (type) { > + case OVS_NSH_KEY_ATTR_BASE: { > + const struct ovs_nsh_key_base *base = > + (struct ovs_nsh_key_base *)nla_data(a); > + flags = base->flags; > + pnp->next_proto = base->np; > + pnp->md_type = base->mdtype; > + pnp->path_hdr = base->path_hdr; > + break; > + } > + case OVS_NSH_KEY_ATTR_MD1: { > + const struct ovs_nsh_key_md1 *md1 = > + (struct ovs_nsh_key_md1 *)nla_data(a); > + > + mdlen = nla_len(a); > + memcpy(pnp->metadata, md1, mdlen); > + break; > + } > + case OVS_NSH_KEY_ATTR_MD2: { > + const struct u8 *md2 = nla_data(a); > + > + mdlen = nla_len(a); > + memcpy(pnp->metadata, md2, mdlen); > + break; > + } > + default: > + OVS_NLERR(1, "Unknown nsh attribute %d", > + type); > + return -EINVAL; > + } > + } > + > + if (rem > 0) { > + OVS_NLERR(1, "nsh attribute has %d unknown bytes.", rem); > + return -EINVAL; > + } > + > + /* nsh header length = NSH_BASE_HDR_LEN + mdlen */ > + pnp->ver_flags_len = htons(flags << NSH_FLAGS_SHIFT | > + (NSH_BASE_HDR_LEN + mdlen) >> 2); > + > + return 0; > +} > + > +int nsh_key_from_nlattr(const struct nlattr *attr, > + struct ovs_key_nsh *nsh) > +{ > + 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(1, "nsh attr %d is out of range max %d", > + type, OVS_NSH_KEY_ATTR_MAX); > + return -EINVAL; > + } > + > + if (!check_attr_len(nla_len(a), > + ovs_nsh_key_attr_lens[type].len)) { > + OVS_NLERR( > + 1, > + "nsh attr %d has unexpected len %d expected %d", > + type, > + nla_len(a), > + ovs_nsh_key_attr_lens[type].len > + ); > + return -EINVAL; > + } > + > + switch (type) { > + case OVS_NSH_KEY_ATTR_BASE: { > + const struct ovs_nsh_key_base *base = > + (struct ovs_nsh_key_base *)nla_data(a); > + nsh->flags = base->flags; > + nsh->mdtype = base->mdtype; > + nsh->np = base->np; > + nsh->path_hdr = base->path_hdr; > + break; > + } > + case OVS_NSH_KEY_ATTR_MD1: { > + const struct ovs_nsh_key_md1 *md1 = > + (struct ovs_nsh_key_md1 *)nla_data(a); > + memcpy(nsh->context, md1->context, sizeof(*md1)); > + break; > + } > + case OVS_NSH_KEY_ATTR_MD2: > + /* Not supported yet */ > + break; When we encounter these metadata attributes do we need to verify that nsh->mdtype is set correctly? Keep in mind we may parse ATTR_MD{1,2} before ATTR_BASE. > + default: > + OVS_NLERR(1, "Unknown nsh attribute %d", > + type); > + return -EINVAL; > + } > + } > + > + if (rem > 0) { > + OVS_NLERR(1, "nsh attribute has %d unknown bytes.", rem); > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int nsh_key_put_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); > + int i; > + > + if (type > OVS_NSH_KEY_ATTR_MAX) { > + OVS_NLERR(log, "nsh attr %d is out of range max %d", > + type, OVS_NSH_KEY_ATTR_MAX); > + return -EINVAL; > + } > + > + if (!check_attr_len(nla_len(a), > + ovs_nsh_key_attr_lens[type].len)) { > + OVS_NLERR( > + log, > + "nsh attr %d has unexpected len %d expected %d", > + type, > + nla_len(a), > + ovs_nsh_key_attr_lens[type].len > + ); > + return -EINVAL; > + } > + > + switch (type) { > + case OVS_NSH_KEY_ATTR_BASE: { > + const struct ovs_nsh_key_base *base = > + (struct ovs_nsh_key_base *)nla_data(a); > + SW_FLOW_KEY_PUT(match, nsh.flags, > + base->flags, is_mask); > + SW_FLOW_KEY_PUT(match, nsh.mdtype, > + base->mdtype, is_mask); > + SW_FLOW_KEY_PUT(match, nsh.np, > + base->np, is_mask); > + SW_FLOW_KEY_PUT(match, nsh.path_hdr, > + base->path_hdr, is_mask); > + break; > + } > + case OVS_NSH_KEY_ATTR_MD1: { > + const struct ovs_nsh_key_md1 *md1 = > + (struct ovs_nsh_key_md1 *)nla_data(a); > + for (i = 0; i < 4; i++) > + SW_FLOW_KEY_PUT(match, nsh.context[i], > + md1->context[i], is_mask); > + break; > + } > + case OVS_NSH_KEY_ATTR_MD2: > + /* Not supported yet */ > + 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; > + } > + > + 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) [..]