@@ -71,6 +71,7 @@ OXM_CLASSES = {"NXM_OF_": (0, 0x0000, 'extension'),
"OXM_OF_": (0, 0x8000, 'standard'),
"OXM_OF_PKT_REG": (0, 0x8001, 'standard'),
"ONFOXM_ET_": (0x4f4e4600, 0xffff, 'standard'),
+ "ERICOXM_OF_": (0, 0x1000, 'extension'),
# This is the experimenter OXM class for Nicira, which is the
# one that OVS would be using instead of NXM_OF_ and NXM_NX_
@@ -375,6 +375,7 @@ enum ovs_key_attr {
#ifndef __KERNEL__
/* Only used within userspace data path. */
OVS_KEY_ATTR_PACKET_TYPE, /* be32 packet type */
+ OVS_KEY_ATTR_ND_EXTENSIONS, /* struct ovs_key_nd_extensions */
#endif
__OVS_KEY_ATTR_MAX
@@ -489,6 +490,13 @@ struct ovs_key_nd {
__u8 nd_tll[ETH_ALEN];
};
+#ifndef __KERNEL__
+struct ovs_key_nd_extensions {
+ __be32 nd_reserved;
+ __u8 nd_options_type;
+};
+#endif
+
#define OVS_CT_LABELS_LEN_32 4
#define OVS_CT_LABELS_LEN (OVS_CT_LABELS_LEN_32 * sizeof(__u32))
struct ovs_key_ct_labels {
@@ -219,6 +219,9 @@ void match_set_nd_target(struct match *, const struct in6_addr *);
void match_set_nd_target_masked(struct match *, const struct in6_addr *,
const struct in6_addr *);
+void match_set_nd_reserved(struct match *, ovs_be32);
+void match_set_nd_options_type(struct match *, uint8_t);
+
bool match_equal(const struct match *, const struct match *);
uint32_t match_hash(const struct match *, uint32_t basis);
@@ -1796,6 +1796,34 @@ enum OVS_PACKED_ENUM mf_field_id {
*/
MFF_ND_TLL,
+ /* "nd_reserved".
+ *
+ * The reserved field in IPv6 Neighbor Discovery message.
+ *
+ * Type: be32.
+ * Maskable: no.
+ * Formatting: decimal.
+ * Prerequisites: ND.
+ * Access: read/write.
+ * NXM: none.
+ * OXM: ERICOXM_OF_ICMPV6_ND_RESERVED(1) since v2.11.
+ */
+ MFF_ND_RESERVED,
+
+ /* "nd_options_type".
+ *
+ * The type of the option in IPv6 Neighbor Discovery message.
+ *
+ * Type: u8.
+ * Maskable: no.
+ * Formatting: decimal.
+ * Prerequisites: ND.
+ * Access: read/write.
+ * NXM: none.
+ * OXM: ERICOXM_OF_ICMPV6_ND_OPTIONS_TYPE(2) since v2.11.
+ */
+ MFF_ND_OPTIONS_TYPE,
+
/* ## ---- ## */
/* ## NSH ## */
/* ## ---- ## */
@@ -401,7 +401,7 @@ parse_ethertype(const void **datap, size_t *sizep)
static inline bool
parse_icmpv6(const void **datap, size_t *sizep, const struct icmp6_hdr *icmp,
const struct in6_addr **nd_target,
- struct eth_addr arp_buf[2])
+ struct eth_addr arp_buf[2], uint8_t *opt_type)
{
if (icmp->icmp6_code != 0 ||
(icmp->icmp6_type != ND_NEIGHBOR_SOLICIT &&
@@ -411,6 +411,9 @@ parse_icmpv6(const void **datap, size_t *sizep, const struct icmp6_hdr *icmp,
arp_buf[0] = eth_addr_zero;
arp_buf[1] = eth_addr_zero;
+ opt_type[0] = 0;
+ opt_type[1] = 0;
+
*nd_target = data_try_pull(datap, sizep, sizeof **nd_target);
if (OVS_UNLIKELY(!*nd_target)) {
return true;
@@ -432,12 +435,14 @@ parse_icmpv6(const void **datap, size_t *sizep, const struct icmp6_hdr *icmp,
if (lla_opt->type == ND_OPT_SOURCE_LINKADDR && opt_len == 8) {
if (OVS_LIKELY(eth_addr_is_zero(arp_buf[0]))) {
arp_buf[0] = lla_opt->mac;
+ opt_type[0] = lla_opt->type;
} else {
goto invalid;
}
} else if (lla_opt->type == ND_OPT_TARGET_LINKADDR && opt_len == 8) {
if (OVS_LIKELY(eth_addr_is_zero(arp_buf[1]))) {
arp_buf[1] = lla_opt->mac;
+ opt_type[1] = lla_opt->type;
} else {
goto invalid;
}
@@ -987,18 +992,45 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
if (OVS_LIKELY(size >= sizeof(struct icmp6_hdr))) {
const struct in6_addr *nd_target;
struct eth_addr arp_buf[2];
- const struct icmp6_hdr *icmp = data_pull(&data, &size,
- sizeof *icmp);
- if (parse_icmpv6(&data, &size, icmp, &nd_target, arp_buf)) {
+ /* This will populate whether we received Option 1
+ * or Option 2. */
+ uint8_t opt_type[2];
+ /* This holds the ND Reserved field. */
+ uint32_t *rso_flags;
+ const struct icmp6_hdr *icmp = data_pull(&data,
+ &size,ICMP6_HEADER_LEN);
+ rso_flags = (uint32_t *) data_pull(&data,
+ &size, sizeof(uint32_t));
+ if (parse_icmpv6(&data, &size, icmp,
+ &nd_target, arp_buf, opt_type)) {
if (nd_target) {
miniflow_push_words(mf, nd_target, nd_target,
sizeof *nd_target / sizeof(uint64_t));
}
miniflow_push_macs(mf, arp_sha, arp_buf);
- miniflow_pad_to_64(mf, arp_tha);
+ /* Populate options field and set the padding
+ * accordingly. */
+ if (opt_type[0] != 0) {
+ miniflow_push_be16(mf, tcp_flags, htons(opt_type[0]));
+ /* Pad to align with 64 bits.
+ * This will zero out the pad3 field. */
+ miniflow_pad_to_64(mf, tcp_flags);
+ } else if (opt_type[1] != 0) {
+ miniflow_push_be16(mf, tcp_flags, htons(opt_type[1]));
+ /* Pad to align with 64 bits.
+ * This will zero out the pad3 field. */
+ miniflow_pad_to_64(mf, tcp_flags);
+ } else {
+ /* Pad to align with 64 bits.
+ * This will zero out the tcp_flags & pad3 field. */
+ miniflow_pad_to_64(mf, arp_tha);
+ }
miniflow_push_be16(mf, tp_src, htons(icmp->icmp6_type));
miniflow_push_be16(mf, tp_dst, htons(icmp->icmp6_code));
miniflow_pad_to_64(mf, tp_dst);
+ /* Fill ND reserved field. */
+ miniflow_push_be32(mf, igmp_group_ip4, htonl(*rso_flags));
+ miniflow_pad_to_64(mf, igmp_group_ip4);
} else {
/* ICMPv6 but not ND. */
miniflow_push_be16(mf, tp_src, htons(icmp->icmp6_type));
@@ -1927,6 +1959,8 @@ flow_wc_map(const struct flow *flow, struct flowmap *map)
FLOWMAP_SET(map, nd_target);
FLOWMAP_SET(map, arp_sha);
FLOWMAP_SET(map, arp_tha);
+ FLOWMAP_SET(map, tcp_flags);
+ FLOWMAP_SET(map, igmp_group_ip4);
} else {
FLOWMAP_SET(map, ct_nw_proto);
FLOWMAP_SET(map, ct_ipv6_src);
@@ -2968,6 +3002,8 @@ flow_compose_l4(struct dp_packet *p, const struct flow *flow,
struct icmp6_hdr *icmp = dp_packet_put_zeros(p, sizeof *icmp);
icmp->icmp6_type = ntohs(flow->tp_src);
icmp->icmp6_code = ntohs(flow->tp_dst);
+ uint32_t *reserved = &icmp->icmp6_dataun.icmp6_un_data32[0];
+ *reserved = ntohl(flow->igmp_group_ip4);
if (icmp->icmp6_code == 0 &&
(icmp->icmp6_type == ND_NEIGHBOR_SOLICIT ||
@@ -1080,6 +1080,19 @@ match_set_nd_target_masked(struct match *match,
match->wc.masks.nd_target = *mask;
}
+void
+match_set_nd_reserved (struct match *match, ovs_be32 value)
+{
+ match->flow.igmp_group_ip4 = value;
+ match->wc.masks.igmp_group_ip4 = OVS_BE32_MAX;
+}
+
+void
+match_set_nd_options_type(struct match *match, uint8_t option)
+{
+ match_set_tcp_flags(match, htons(option));
+}
+
/* Returns true if 'a' and 'b' wildcard the same fields and have the same
* values for fixed fields, otherwise false. */
bool
@@ -1688,6 +1701,14 @@ match_format(const struct match *match,
&wc->masks.nd_target);
format_eth_masked(s, "nd_sll", f->arp_sha, wc->masks.arp_sha);
format_eth_masked(s, "nd_tll", f->arp_tha, wc->masks.arp_tha);
+ if (wc->masks.igmp_group_ip4) {
+ format_be32_masked(s,"nd_reserved", f->igmp_group_ip4,
+ wc->masks.igmp_group_ip4);
+ }
+ if (wc->masks.tcp_flags) {
+ format_be16_masked(s,"nd_options_type", f->tcp_flags,
+ wc->masks.tcp_flags);
+ }
} else {
format_be16_masked(s, "tp_src", f->tp_src, wc->masks.tp_src);
format_be16_masked(s, "tp_dst", f->tp_dst, wc->masks.tp_dst);
@@ -344,6 +344,11 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
case MFF_ND_TARGET:
return ipv6_mask_is_any(&wc->masks.nd_target);
+ case MFF_ND_RESERVED:
+ return !wc->masks.igmp_group_ip4;
+ case MFF_ND_OPTIONS_TYPE:
+ return !wc->masks.tcp_flags;
+
case MFF_IP_FRAG:
return !(wc->masks.nw_frag & FLOW_NW_FRAG_MASK);
@@ -571,6 +576,8 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
case MFF_ND_TARGET:
case MFF_ND_SLL:
case MFF_ND_TLL:
+ case MFF_ND_RESERVED:
+ case MFF_ND_OPTIONS_TYPE:
return true;
case MFF_IN_PORT_OXM:
@@ -909,9 +916,14 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
break;
case MFF_TCP_FLAGS:
+ case MFF_ND_OPTIONS_TYPE:
value->be16 = flow->tcp_flags;
break;
+ case MFF_ND_RESERVED:
+ value->be32 = flow->igmp_group_ip4;
+ break;
+
case MFF_ICMPV4_TYPE:
case MFF_ICMPV6_TYPE:
value->u8 = ntohs(flow->tp_src);
@@ -1259,6 +1271,14 @@ mf_set_value(const struct mf_field *mf,
match_set_nd_target(match, &value->ipv6);
break;
+ case MFF_ND_RESERVED:
+ match_set_nd_reserved(match, value->be32);
+ break;
+
+ case MFF_ND_OPTIONS_TYPE:
+ match_set_nd_options_type(match, value->u8);
+ break;
+
case MFF_NSH_FLAGS:
MATCH_SET_FIELD_UINT8(match, nsh.flags, value->u8);
break;
@@ -1668,6 +1688,14 @@ mf_set_flow_value(const struct mf_field *mf,
flow->nd_target = value->ipv6;
break;
+ case MFF_ND_RESERVED:
+ flow->igmp_group_ip4 = value->be32;
+ break;
+
+ case MFF_ND_OPTIONS_TYPE:
+ flow->tcp_flags = htons(value->u8);
+ break;
+
case MFF_NSH_FLAGS:
flow->nsh.flags = value->u8;
break;
@@ -1823,6 +1851,8 @@ mf_is_pipeline_field(const struct mf_field *mf)
case MFF_ND_TARGET:
case MFF_ND_SLL:
case MFF_ND_TLL:
+ case MFF_ND_RESERVED:
+ case MFF_ND_OPTIONS_TYPE:
case MFF_NSH_FLAGS:
case MFF_NSH_TTL:
case MFF_NSH_MDTYPE:
@@ -2150,6 +2180,11 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)
match->wc.masks.arp_tha = eth_addr_zero;
break;
+ case MFF_ND_RESERVED:
+ match->wc.masks.igmp_group_ip4 = htonl(0);
+ match->flow.igmp_group_ip4 = htonl(0);
+ break;
+
case MFF_TCP_SRC:
case MFF_UDP_SRC:
case MFF_SCTP_SRC:
@@ -2169,6 +2204,7 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)
break;
case MFF_TCP_FLAGS:
+ case MFF_ND_OPTIONS_TYPE:
match->wc.masks.tcp_flags = htons(0);
match->flow.tcp_flags = htons(0);
break;
@@ -2285,6 +2321,8 @@ mf_set(const struct mf_field *mf,
case MFF_ICMPV4_CODE:
case MFF_ICMPV6_TYPE:
case MFF_ICMPV6_CODE:
+ case MFF_ND_RESERVED:
+ case MFF_ND_OPTIONS_TYPE:
return OFPUTIL_P_NONE;
case MFF_DP_HASH:
@@ -4586,6 +4586,18 @@ r r c c c.
title="ICMPv6 Neighbor Discovery Source Ethernet Address"/>
<field id="MFF_ND_TLL"
title="ICMPv6 Neighbor Discovery Target Ethernet Address"/>
+ <field id="MFF_ND_RESERVED"
+ title="ICMPv6 Neighbor Discovery Reserved Field"/>
+ <p>
+ This is used to set the R,S,O bits in Neighbor Advertisement Messages
+ </p>
+ <field id="MFF_ND_OPTIONS_TYPE"
+ title="ICMPv6 Neighbor Discovery Options Type Field"/>
+ <p>
+ A value of 1 indicates that the option is Source Link Layer.
+ A value of 2 indicates that the options is Target Link Layer.
+ See RFC 4861 for further details.
+ </p>
</group>
<h1>References</h1>
@@ -990,8 +990,16 @@ nxm_put_ip(struct nxm_put_ctx *ctx,
ntohs(flow->tp_dst));
}
if (is_nd(flow, NULL)) {
+ if (match->wc.masks.igmp_group_ip4) {
+ nxm_put_32(ctx, MFF_ND_RESERVED, oxm,
+ flow->igmp_group_ip4);
+ }
nxm_put_ipv6(ctx, MFF_ND_TARGET, oxm,
&flow->nd_target, &match->wc.masks.nd_target);
+ if (match->wc.masks.tcp_flags) {
+ nxm_put_8(ctx, MFF_ND_OPTIONS_TYPE, oxm,
+ ntohs(flow->tcp_flags));
+ }
if (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT)) {
nxm_put_eth_masked(ctx, MFF_ND_SLL, oxm,
flow->arp_sha, match->wc.masks.arp_sha);
@@ -228,6 +228,23 @@ set_arp(struct dp_packet *packet, const struct ovs_key_arp *key,
}
static void
+odp_set_nd_ext(struct dp_packet *packet, const struct ovs_key_nd_extensions
+ *key, const struct ovs_key_nd_extensions *mask)
+{
+ const struct ovs_nd_msg *ns = dp_packet_l4(packet);
+ ovs_16aligned_be32 reserved = ns->rso_flags;
+ uint8_t opt_type = ns->options[0].type;
+
+ if (mask->nd_reserved) {
+ put_16aligned_be32(&reserved, key->nd_reserved);
+ }
+ if (mask->nd_options_type) {
+ opt_type = key->nd_options_type;
+ }
+ packet_set_nd_ext(packet, reserved, opt_type);
+}
+
+static void
odp_set_nd(struct dp_packet *packet, const struct ovs_key_nd *key,
const struct ovs_key_nd *mask)
{
@@ -438,6 +455,16 @@ odp_execute_set_action(struct dp_packet *packet, const struct nlattr *a)
}
break;
+ case OVS_KEY_ATTR_ND_EXTENSIONS:
+ if (OVS_LIKELY(dp_packet_get_nd_payload(packet))) {
+ const struct ovs_key_nd_extensions *nd_ext_key
+ = nl_attr_get_unspec(a, sizeof(struct ovs_key_nd_extensions));
+ ovs_16aligned_be32 rso_flags;
+ put_16aligned_be32(&rso_flags, nd_ext_key->nd_reserved);
+ packet_set_nd_ext(packet, rso_flags, nd_ext_key->nd_options_type);
+ }
+ break;
+
case OVS_KEY_ATTR_DP_HASH:
md->dp_hash = nl_attr_get_u32(a);
break;
@@ -540,6 +567,11 @@ odp_execute_masked_set_action(struct dp_packet *packet,
get_mask(a, struct ovs_key_nd));
break;
+ case OVS_KEY_ATTR_ND_EXTENSIONS:
+ odp_set_nd_ext(packet, nl_attr_get(a),
+ get_mask(a, struct ovs_key_nd_extensions));
+ break;
+
case OVS_KEY_ATTR_DP_HASH:
md->dp_hash = nl_attr_get_u32(a)
| (md->dp_hash & ~*get_mask(a, uint32_t));
@@ -173,6 +173,7 @@ ovs_key_attr_to_string(enum ovs_key_attr attr, char *namebuf, size_t bufsize)
case OVS_KEY_ATTR_ICMPV6: return "icmpv6";
case OVS_KEY_ATTR_ARP: return "arp";
case OVS_KEY_ATTR_ND: return "nd";
+ case OVS_KEY_ATTR_ND_EXTENSIONS: return "nd_ext";
case OVS_KEY_ATTR_MPLS: return "mpls";
case OVS_KEY_ATTR_DP_HASH: return "dp_hash";
case OVS_KEY_ATTR_RECIRC_ID: return "recirc_id";
@@ -2500,6 +2501,7 @@ const struct attr_len_tbl ovs_flow_key_attr_lens[OVS_KEY_ATTR_MAX + 1] = {
[OVS_KEY_ATTR_ICMPV6] = { .len = sizeof(struct ovs_key_icmpv6) },
[OVS_KEY_ATTR_ARP] = { .len = sizeof(struct ovs_key_arp) },
[OVS_KEY_ATTR_ND] = { .len = sizeof(struct ovs_key_nd) },
+ [OVS_KEY_ATTR_ND_EXTENSIONS] = { .len = sizeof(struct ovs_key_nd_extensions) },
[OVS_KEY_ATTR_CT_STATE] = { .len = 4 },
[OVS_KEY_ATTR_CT_ZONE] = { .len = 2 },
[OVS_KEY_ATTR_CT_MARK] = { .len = 4 },
@@ -2950,6 +2952,7 @@ odp_mask_is_constant__(enum ovs_key_attr attr, const void *mask, size_t size,
case OVS_KEY_ATTR_ICMP:
case OVS_KEY_ATTR_ICMPV6:
case OVS_KEY_ATTR_ND:
+ case OVS_KEY_ATTR_ND_EXTENSIONS:
case OVS_KEY_ATTR_SKB_MARK:
case OVS_KEY_ATTR_TUNNEL:
case OVS_KEY_ATTR_SCTP:
@@ -4044,6 +4047,21 @@ format_odp_key_attr__(const struct nlattr *a, const struct nlattr *ma,
ds_chomp(ds, ',');
break;
}
+ case OVS_KEY_ATTR_ND_EXTENSIONS: {
+ const struct ovs_key_nd_extensions *mask = ma ? nl_attr_get(ma) : NULL;
+ const struct ovs_key_nd_extensions *key = nl_attr_get(a);
+
+ bool first = true;
+ format_be32_masked(ds, &first, "nd_reserved", key->nd_reserved,
+ OVS_BE32_MAX);
+ ds_put_char(ds, ',');
+
+ format_u8u(ds, "nd_options_type", key->nd_options_type,
+ MASK(mask, nd_options_type), verbose);
+
+ ds_chomp(ds, ',');
+ break;
+ }
case OVS_KEY_ATTR_NSH: {
format_odp_nsh_attr(a, ma, ds);
break;
@@ -5550,6 +5568,11 @@ parse_odp_key_mask_attr(struct parse_odp_context *context, const char *s,
SCAN_FIELD("tll=", eth, nd_tll);
} SCAN_END(OVS_KEY_ATTR_ND);
+ SCAN_BEGIN("nd_ext(", struct ovs_key_nd_extensions) {
+ SCAN_FIELD("reserved=", be32, nd_reserved);
+ SCAN_FIELD("nd_options_type=", u8, nd_options_type);
+ } SCAN_END(OVS_KEY_ATTR_ND_EXTENSIONS);
+
struct packet_type {
ovs_be16 ns;
ovs_be16 id;
@@ -5927,14 +5950,25 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
* xlate_wc_finish() for details. */
&& (!export_mask || (data->tp_src == htons(0xff)
&& data->tp_dst == htons(0xff)))) {
-
struct ovs_key_nd *nd_key;
-
+ struct ovs_key_nd_extensions *nd_ext_key;
nd_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ND,
sizeof *nd_key);
nd_key->nd_target = data->nd_target;
nd_key->nd_sll = data->arp_sha;
nd_key->nd_tll = data->arp_tha;
+
+ /* Add ND Extensions Attr only if reserved field
+ * or options type is set. */
+ if (data->igmp_group_ip4 != 0 ||
+ data->tcp_flags != 0) {
+ nd_ext_key =
+ nl_msg_put_unspec_uninit(buf,
+ OVS_KEY_ATTR_ND_EXTENSIONS,
+ sizeof *nd_ext_key);
+ nd_ext_key->nd_reserved = data->igmp_group_ip4;
+ nd_ext_key->nd_options_type = ntohs(data->tcp_flags);
+ }
}
}
}
@@ -6118,6 +6152,7 @@ odp_key_to_dp_packet(const struct nlattr *key, size_t key_len,
case OVS_KEY_ATTR_ICMPV6:
case OVS_KEY_ATTR_ARP:
case OVS_KEY_ATTR_ND:
+ case OVS_KEY_ATTR_ND_EXTENSIONS:
case OVS_KEY_ATTR_SCTP:
case OVS_KEY_ATTR_TCP_FLAGS:
case OVS_KEY_ATTR_MPLS:
@@ -6557,6 +6592,35 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
}
}
}
+ if (present_attrs &
+ (UINT64_C(1) << OVS_KEY_ATTR_ND_EXTENSIONS)) {
+ const struct ovs_key_nd_extensions *nd_ext_key;
+ if (!is_mask) {
+ expected_attrs |=
+ UINT64_C(1) << OVS_KEY_ATTR_ND_EXTENSIONS;
+ }
+
+ nd_ext_key =
+ nl_attr_get(attrs[OVS_KEY_ATTR_ND_EXTENSIONS]);
+ flow->igmp_group_ip4 = nd_ext_key->nd_reserved;
+ flow->tcp_flags = htons(nd_ext_key->nd_options_type);
+
+ if (is_mask) {
+ /* Even though 'tp_src' and 'tp_dst' are 16 bits wide,
+ * ICMP type and code are 8 bits wide. Therefore, an
+ * exact match looks like htons(0xff), not
+ * htons(0xffff). See xlate_wc_finish() for details.
+ * */
+ if (!is_all_zeros(nd_ext_key, sizeof *nd_ext_key) &&
+ (flow->tp_src != htons(0xff) ||
+ flow->tp_dst != htons(0xff))) {
+ return ODP_FIT_ERROR;
+ } else {
+ expected_attrs |=
+ UINT64_C(1) << OVS_KEY_ATTR_ND_EXTENSIONS;
+ }
+ }
+ }
}
}
} else if (src_flow->nw_proto == IPPROTO_IGMP
@@ -7453,6 +7517,24 @@ put_nd_key(const struct ovs_key_nd *nd, struct flow *flow)
flow->arp_tha = nd->nd_tll;
}
+static void
+get_nd_extensions_key(const struct flow *flow,
+ struct ovs_key_nd_extensions *nd_ext)
+{
+ /* ND Extensions key has padding, clear it. */
+ memset(nd_ext, 0, sizeof *nd_ext);
+ nd_ext->nd_reserved = flow->igmp_group_ip4;
+ nd_ext->nd_options_type = ntohs(flow->tcp_flags);
+}
+
+static void
+put_nd_extensions_key(const struct ovs_key_nd_extensions *nd_ext,
+ struct flow *flow)
+{
+ flow->igmp_group_ip4 = nd_ext->nd_reserved;
+ flow->tcp_flags = htons(nd_ext->nd_options_type);
+}
+
static enum slow_path_reason
commit_set_nd_action(const struct flow *flow, struct flow *base_flow,
struct ofpbuf *odp_actions,
@@ -7475,10 +7557,33 @@ commit_set_nd_action(const struct flow *flow, struct flow *base_flow,
}
static enum slow_path_reason
+commit_set_nd_extensions_action(const struct flow *flow,
+ struct flow *base_flow,
+ struct ofpbuf *odp_actions,
+ struct flow_wildcards *wc, bool use_masked)
+{
+ struct ovs_key_nd_extensions key, mask, base;
+
+ get_nd_extensions_key(flow, &key);
+ get_nd_extensions_key(base_flow, &base);
+ get_nd_extensions_key(&wc->masks, &mask);
+
+ if (commit(OVS_KEY_ATTR_ND_EXTENSIONS, use_masked, &key,
+ &base, &mask, sizeof key, odp_actions)) {
+ put_nd_extensions_key(&base, base_flow);
+ put_nd_extensions_key(&mask, &wc->masks);
+ return SLOW_ACTION;
+ }
+ return 0;
+}
+
+static enum slow_path_reason
commit_set_nw_action(const struct flow *flow, struct flow *base,
struct ofpbuf *odp_actions, struct flow_wildcards *wc,
bool use_masked)
{
+ uint32_t reason;
+
/* Check if 'flow' really has an L3 header. */
if (!flow->nw_proto) {
return 0;
@@ -7491,7 +7596,16 @@ commit_set_nw_action(const struct flow *flow, struct flow *base,
case ETH_TYPE_IPV6:
commit_set_ipv6_action(flow, base, odp_actions, wc, use_masked);
- return commit_set_nd_action(flow, base, odp_actions, wc, use_masked);
+ if (base->nw_proto == IPPROTO_ICMPV6) {
+ /* Commit extended attrs first to make sure
+ correct options are added.*/
+ reason = commit_set_nd_extensions_action(flow, base,
+ odp_actions, wc, use_masked);
+ reason |= commit_set_nd_action(flow, base, odp_actions,
+ wc, use_masked);
+ return reason;
+ }
+ break;
case ETH_TYPE_ARP:
return commit_set_arp_action(flow, base, odp_actions, wc);
@@ -1282,6 +1282,39 @@ packet_set_icmp(struct dp_packet *packet, uint8_t type, uint8_t code)
}
void
+packet_set_nd_ext(struct dp_packet *packet, const ovs_16aligned_be32 rso_flags,
+ const uint8_t opt_type)
+{
+ struct ovs_nd_msg *ns;
+ struct ovs_nd_lla_opt *opt;
+ int bytes_remain = dp_packet_l4_size(packet);
+ struct ovs_16aligned_ip6_hdr * nh = dp_packet_l3(packet);
+ uint32_t pseudo_hdr_csum = 0;
+
+ if (OVS_UNLIKELY(bytes_remain < sizeof(*ns))) {
+ return;
+ }
+
+ if (nh) {
+ pseudo_hdr_csum = packet_csum_pseudoheader6(nh);
+ }
+
+ ns = dp_packet_l4(packet);
+ opt = &ns->options[0];
+
+ /* set RSO flags and option type */
+ ns->rso_flags = rso_flags;
+ opt->type = opt_type;
+
+ /* recalculate checksum */
+ ovs_be16 *csum_value = &(ns->icmph.icmp6_cksum);
+ *csum_value = 0;
+ *csum_value = csum_finish(csum_continue(pseudo_hdr_csum,
+ &(ns->icmph), bytes_remain));
+
+}
+
+void
packet_set_nd(struct dp_packet *packet, const struct in6_addr *target,
const struct eth_addr sll, const struct eth_addr tll)
{
@@ -1543,6 +1543,9 @@ void packet_set_sctp_port(struct dp_packet *, ovs_be16 src, ovs_be16 dst);
void packet_set_icmp(struct dp_packet *, uint8_t type, uint8_t code);
void packet_set_nd(struct dp_packet *, const struct in6_addr *target,
const struct eth_addr sll, const struct eth_addr tll);
+void packet_set_nd_ext(struct dp_packet *packet,
+ const ovs_16aligned_be32 rso_flags,
+ const uint8_t opt_type);
void packet_format_tcp_flags(struct ds *, uint16_t);
const char *packet_tcp_flag_to_string(uint32_t flag);
@@ -1055,6 +1055,7 @@ sflow_read_set_action(const struct nlattr *attr,
case OVS_KEY_ATTR_ICMPV6:
case OVS_KEY_ATTR_ARP:
case OVS_KEY_ATTR_ND:
+ case OVS_KEY_ATTR_ND_EXTENSIONS:
case OVS_KEY_ATTR_CT_STATE:
case OVS_KEY_ATTR_CT_ZONE:
case OVS_KEY_ATTR_CT_MARK:
@@ -2445,7 +2445,7 @@ head_table () {
actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
supported on Set-Field: tun_id tun_src tun_dst tun_ipv6_src tun_ipv6_dst tun_flags tun_gbp_id tun_gbp_flags tun_erspan_idx tun_erspan_ver tun_erspan_dir tun_erspan_hwid tun_metadata0 dnl
tun_metadata1 tun_metadata2 tun_metadata3 tun_metadata4 tun_metadata5 tun_metadata6 tun_metadata7 tun_metadata8 tun_metadata9 tun_metadata10 tun_metadata11 tun_metadata12 tun_metadata13 tun_metadata14 tun_metadata15 tun_metadata16 tun_metadata17 tun_metadata18 tun_metadata19 tun_metadata20 tun_metadata21 tun_metadata22 tun_metadata23 tun_metadata24 tun_metadata25 tun_metadata26 tun_metadata27 tun_metadata28 tun_metadata29 tun_metadata30 tun_metadata31 tun_metadata32 tun_metadata33 tun_metadata34 tun_metadata35 tun_metadata36 tun_metadata37 tun_metadata38 tun_metadata39 tun_metadata40 tun_metadata41 tun_metadata42 tun_metadata43 tun_metadata44 tun_metadata45 tun_metadata46 tun_metadata47 tun_metadata48 tun_metadata49 tun_metadata50 tun_metadata51 tun_metadata52 tun_metadata53 tun_metadata54 tun_metadata55 tun_metadata56 tun_metadata57 tun_metadata58 tun_metadata59 tun_metadata60 tun_metadata61 tun_metadata62 tun_metadata63 dnl
-metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 reg9 reg10 reg11 reg12 reg13 reg14 reg15 xreg0 xreg1 xreg2 xreg3 xreg4 xreg5 xreg6 xreg7 xxreg0 xxreg1 xxreg2 xxreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc mpls_ttl ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst icmp_type icmp_code icmpv6_type icmpv6_code nd_target nd_sll nd_tll nsh_flags nsh_spi nsh_si nsh_c1 nsh_c2 nsh_c3 nsh_c4 nsh_ttl
+metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 reg9 reg10 reg11 reg12 reg13 reg14 reg15 xreg0 xreg1 xreg2 xreg3 xreg4 xreg5 xreg6 xreg7 xxreg0 xxreg1 xxreg2 xxreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc mpls_ttl ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst icmp_type icmp_code icmpv6_type icmpv6_code nd_target nd_sll nd_tll nd_reserved nd_options_type nsh_flags nsh_spi nsh_si nsh_c1 nsh_c2 nsh_c3 nsh_c4 nsh_ttl
matching:
dp_hash: arbitrary mask
recirc_id: exact match or wildcard
@@ -2611,6 +2611,8 @@ metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4
nd_target: arbitrary mask
nd_sll: arbitrary mask
nd_tll: arbitrary mask
+ nd_reserved: exact match or wildcard
+ nd_options_type: exact match or wildcard
nsh_flags: arbitrary mask
nsh_mdtype: exact match or wildcard
nsh_np: exact match or wildcard