diff --git a/NEWS b/NEWS
index 358c9b9..6ffac90 100644
--- a/NEWS
+++ b/NEWS
@@ -13,6 +13,8 @@ Post-v2.10.0
* Add option for simple round-robin based Rxq to PMD assignment.
It can be set with pmd-rxq-assign.
* Add support for DPDK 18.11
+ * ICMPv6 ND enhancements: support for match and set ND options type
+ and reserved fields.
- Add 'symmetric_l3' hash function.
- OVS now honors 'updelay' and 'downdelay' for bonds with LACP configured.
- ovs-vswitchd:
diff --git a/build-aux/extract-ofp-fields b/build-aux/extract-ofp-fields
index 3592594..e159a1d 100755
--- a/build-aux/extract-ofp-fields
+++ b/build-aux/extract-ofp-fields
@@ -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_
diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
index 9b087f1..d5aa09d 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -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 {
diff --git a/include/openvswitch/flow.h b/include/openvswitch/flow.h
index 5d2cf09..57b6c92 100644
--- a/include/openvswitch/flow.h
+++ b/include/openvswitch/flow.h
@@ -144,7 +144,8 @@ struct flow {
struct in6_addr nd_target; /* IPv6 neighbor discovery (ND) target. */
struct eth_addr arp_sha; /* ARP/ND source hardware address. */
struct eth_addr arp_tha; /* ARP/ND target hardware address. */
- ovs_be16 tcp_flags; /* TCP flags. With L3 to avoid matching L4. */
+ ovs_be16 tcp_flags; /* TCP flags/ICMPv6 ND options type.
+ * With L3 to avoid matching L4. */
ovs_be16 pad2; /* Pad to 64 bits. */
struct ovs_key_nsh nsh; /* Network Service Header keys */
@@ -153,7 +154,8 @@ struct flow {
ovs_be16 tp_dst; /* TCP/UDP/SCTP destination port/ICMP code. */
ovs_be16 ct_tp_src; /* CT original tuple source port/ICMP type. */
ovs_be16 ct_tp_dst; /* CT original tuple dst port/ICMP code. */
- ovs_be32 igmp_group_ip4; /* IGMP group IPv4 address.
+ ovs_be32 igmp_group_ip4; /* IGMP group IPv4 address/ICMPv6 ND reserved
+ * field.
* Keep last for BUILD_ASSERT_DECL below. */
ovs_be32 pad3; /* Pad to 64 bits. */
};
diff --git a/include/openvswitch/match.h b/include/openvswitch/match.h
index e8c80dd..05ecee7 100644
--- a/include/openvswitch/match.h
+++ b/include/openvswitch/match.h
@@ -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);
diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h
index ffd8945..65d99f4 100644
--- a/include/openvswitch/meta-flow.h
+++ b/include/openvswitch/meta-flow.h
@@ -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 ## */
/* ## ---- ## */
diff --git a/lib/flow.c b/lib/flow.c
index c60446f..a83a840 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -396,13 +396,14 @@ parse_ethertype(const void **datap, size_t *sizep)
}
/* Returns 'true' if the packet is an ND packet. In that case the '*nd_target'
- * and 'arp_buf[]' are filled in. If the packet is not an ND pacet, 'false' is
- * returned and no values are filled in on '*nd_target' or 'arp_buf[]'. */
+ * and 'arp_buf[]' are filled in. If the packet is not an ND packet, 'false'
+ * is returned and no values are filled in on '*nd_target' or 'arp_buf[]'. */
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])
+ uint32_t *rso_flags, const struct in6_addr **nd_target,
+ struct eth_addr arp_buf[2], uint8_t *opt_type)
{
+ const uint32_t *reserved;
if (icmp->icmp6_code != 0 ||
(icmp->icmp6_type != ND_NEIGHBOR_SOLICIT &&
icmp->icmp6_type != ND_NEIGHBOR_ADVERT)) {
@@ -411,6 +412,15 @@ 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;
+
+ reserved = data_try_pull(datap, sizep, sizeof(uint32_t));
+ if (OVS_UNLIKELY(!reserved)) {
+ /* Invalid ND packet. */
+ return false;
+ }
+ *rso_flags = *reserved;
+
*nd_target = data_try_pull(datap, sizep, sizeof **nd_target);
if (OVS_UNLIKELY(!*nd_target)) {
return true;
@@ -432,12 +442,20 @@ 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;
+ /* We use only first option type present in ND packet. */
+ if (*opt_type == 0) {
+ *opt_type = 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;
+ /* We use only first option type present in ND packet. */
+ if (*opt_type == 0) {
+ *opt_type = lla_opt->type;
+ }
} else {
goto invalid;
}
@@ -987,18 +1005,38 @@ 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;
+ /* This holds the ND Reserved field. */
+ uint32_t rso_flags;
+ const struct icmp6_hdr *icmp = data_pull(&data,
+ &size,ICMP6_HEADER_LEN);
+ if (parse_icmpv6(&data, &size, icmp,
+ &rso_flags, &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) {
+ miniflow_push_be16(mf, tcp_flags, htons(opt_type));
+ /* 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 +1965,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 +3008,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 ||
diff --git a/lib/match.c b/lib/match.c
index a1407a8..052daee 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -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);
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index b6d9e92..1f5c011 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -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:
diff --git a/lib/meta-flow.xml b/lib/meta-flow.xml
index 0b5c1d0..31db761 100644
--- a/lib/meta-flow.xml
+++ b/lib/meta-flow.xml
@@ -4586,6 +4586,18 @@ r r c c c.
title="ICMPv6 Neighbor Discovery Source Ethernet Address"/>
+ This is used to set the R,S,O bits in Neighbor Advertisement Messages +
++ 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. +