@@ -279,6 +279,23 @@ 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(256) /* OVS_NSH_KEY_ATTR_METADATA */
+ /* OVS_NSH_KEY_ATTR_METADATA is mutually exclusive with
+ * OVS_NSH_KEY_ATTR_NSHCx and covered by it.
+ */
+ + nla_total_size(0) /* OVS_NSH_KEY_ATTR_NSHC1 */
+ + nla_total_size(0) /* OVS_NSH_KEY_ATTR_NSHC2 */
+ + nla_total_size(0) /* OVS_NSH_KEY_ATTR_NSHC3 */
+ + nla_total_size(0); /* OVS_NSH_KEY_ATTR_NSHC4 */
+}
+
size_t ovs_key_attr_size(void)
{
/* Whenever adding new OVS_KEY_ FIELDS, we should consider
@@ -300,6 +317,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 +346,19 @@ static const struct ovs_len_tbl ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1]
.next = ovs_vxlan_ext_key_lens },
};
+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 },
+ [OVS_NSH_KEY_ATTR_METADATA] = { .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 },
@@ -335,6 +367,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) },
@@ -853,6 +887,159 @@ 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"
+ " expected %d", type, nla_len(a),
+ ovs_nsh_key_lens[type].len);
+ 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;
+ /* To be implemented for MD type 2 */
+ case OVS_NSH_KEY_ATTR_METADATA:
+ 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 != 0x1) &&
+ (match->key->nsh.md_type != 0x2)) {
+ OVS_NLERR(log, "Invalid NSH Header MD Type!!!");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int __nsh_to_nlattr(struct sk_buff *skb,
+ const struct ovs_nsh_key *output)
+{
+ if (output->nsp) {
+ 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->md_type == 0x1) { /* NSH_M_TYPE1 */
+ 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;
+#if 0
+ } else if (output->md_type == 0x2) { /* NSH_M_TYPE2 */
+ /* To be implemented. */
+#endif
+ }
+ }
+ 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)
@@ -863,6 +1050,14 @@ static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match,
if (err)
return err;
+ if (attrs & (1ULL << OVS_KEY_ATTR_NSH)) {
+ if (nsh_from_nlattr(a[OVS_KEY_ATTR_NSH], match,
+ is_mask, log) < 0) {
+ return -EINVAL;
+ }
+ attrs &= ~(1ULL << OVS_KEY_ATTR_NSH);
+ }
+
if (attrs & (1ULL << OVS_KEY_ATTR_ETHERNET)) {
const struct ovs_key_ethernet *eth_key;
@@ -1391,6 +1586,11 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
goto nla_put_failure;
}
+ if (swkey->nsh.nsp) {
+ 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))
The openvswitch exchange the key with key netlink message between the kernel data path and user space flow tables. Signed-off-by: Johnson Li <johnson.li@intel.com>