aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnsis Atteka <aatteka@nicira.com>2012-04-25 15:48:40 -0700
committerAnsis Atteka <aatteka@nicira.com>2012-04-26 15:22:48 -0700
commit47284b1fc6fe84a9b5b43b49bef868e4eb230cd1 (patch)
tree2d37c22ae94ddcaa1eafa9574bf3302f3e25ea6c
parenta5f607bc8994e5ed870a62062df54e1b5753c6a2 (diff)
nicira-ext: Support masking of nd_target field
This commit adds support to specify a mask in CIDR format for the nd_target field. Signed-off-by: Ansis Atteka <aatteka@nicira.com>
-rw-r--r--NEWS2
-rw-r--r--include/openflow/nicira-ext.h6
-rw-r--r--lib/classifier.c26
-rw-r--r--lib/classifier.h2
-rw-r--r--lib/flow.c37
-rw-r--r--lib/flow.h17
-rw-r--r--lib/learn.c2
-rw-r--r--lib/meta-flow.c18
-rw-r--r--lib/nx-match.c8
-rw-r--r--lib/nx-match.h8
-rw-r--r--lib/ofp-util.c8
-rw-r--r--tests/ovs-ofctl.at4
-rw-r--r--utilities/ovs-ofctl.8.in2
13 files changed, 87 insertions, 53 deletions
diff --git a/NEWS b/NEWS
index e717a4a4..723c256c 100644
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,8 @@ post-v1.6.0
Internetwork Control (0xc0).
- Added the granular link health statistics, 'cfm_health', to an
interface.
+ - OpenFlow:
+ - Added support to mask nd_target for ICMPv6 neighbor discovery flows.
- ovs-test:
- Added support for spawning ovs-test server from the client.
- Now ovs-test is able to automatically create test bridges and ports.
diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
index f8b863c9..ccd62739 100644
--- a/include/openflow/nicira-ext.h
+++ b/include/openflow/nicira-ext.h
@@ -1634,8 +1634,10 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
*
* Format: 128-bit IPv6 address.
*
- * Masking: Not maskable. */
-#define NXM_NX_ND_TARGET NXM_HEADER (0x0001, 23, 16)
+ * Masking: Only CIDR masks are allowed, that is, masks that consist of N
+ * high-order bits set to 1 and the other 128-N bits set to 0. */
+#define NXM_NX_ND_TARGET NXM_HEADER (0x0001, 23, 16)
+#define NXM_NX_ND_TARGET_W NXM_HEADER_W (0x0001, 23, 16)
/* The source link-layer address option in an IPv6 Neighbor Discovery
* message.
diff --git a/lib/classifier.c b/lib/classifier.c
index 122a6c6e..30cc31d3 100644
--- a/lib/classifier.c
+++ b/lib/classifier.c
@@ -415,8 +415,17 @@ cls_rule_set_ipv6_label(struct cls_rule *rule, ovs_be32 ipv6_label)
void
cls_rule_set_nd_target(struct cls_rule *rule, const struct in6_addr *target)
{
- rule->wc.wildcards &= ~FWW_ND_TARGET;
rule->flow.nd_target = *target;
+ rule->wc.nd_target_mask = in6addr_exact;
+}
+
+void
+cls_rule_set_nd_target_masked(struct cls_rule *rule,
+ const struct in6_addr *target,
+ const struct in6_addr *mask)
+{
+ rule->flow.nd_target = ipv6_addr_bitand(target, mask);
+ rule->wc.nd_target_mask = *mask;
}
/* Returns true if 'a' and 'b' have the same priority, wildcard the same
@@ -491,7 +500,7 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
if (rule->priority != OFP_DEFAULT_PRIORITY) {
ds_put_format(s, "priority=%d,", rule->priority);
@@ -669,11 +678,8 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
} else if (f->nw_proto == IPPROTO_ICMPV6) {
format_be16_masked(s, "icmp_type", f->tp_src, wc->tp_src_mask);
format_be16_masked(s, "icmp_code", f->tp_dst, wc->tp_dst_mask);
- if (!(w & FWW_ND_TARGET)) {
- ds_put_cstr(s, "nd_target=");
- print_ipv6_addr(s, &f->nd_target);
- ds_put_char(s, ',');
- }
+ format_ipv6_netmask(s, "nd_target", &f->nd_target,
+ &wc->nd_target_mask);
if (!(w & FWW_ARP_SHA)) {
ds_put_format(s, "nd_sll="ETH_ADDR_FMT",",
ETH_ADDR_ARGS(f->arp_sha));
@@ -1173,7 +1179,7 @@ flow_equal_except(const struct flow *a, const struct flow *b,
const flow_wildcards_t wc = wildcards->wildcards;
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
for (i = 0; i < FLOW_N_REGS; i++) {
if ((a->regs[i] ^ b->regs[i]) & wildcards->reg_masks[i]) {
@@ -1211,6 +1217,6 @@ flow_equal_except(const struct flow *a, const struct flow *b,
&wildcards->ipv6_src_mask)
&& ipv6_equal_except(&a->ipv6_dst, &b->ipv6_dst,
&wildcards->ipv6_dst_mask)
- && (wc & FWW_ND_TARGET
- || ipv6_addr_equals(&a->nd_target, &b->nd_target)));
+ && ipv6_equal_except(&a->nd_target, &b->nd_target,
+ &wildcards->nd_target_mask));
}
diff --git a/lib/classifier.h b/lib/classifier.h
index 84cb6028..48eb5962 100644
--- a/lib/classifier.h
+++ b/lib/classifier.h
@@ -135,6 +135,8 @@ void cls_rule_set_ipv6_dst_masked(struct cls_rule *, const struct in6_addr *,
const struct in6_addr *);
void cls_rule_set_ipv6_label(struct cls_rule *, ovs_be32);
void cls_rule_set_nd_target(struct cls_rule *, const struct in6_addr *);
+void cls_rule_set_nd_target_masked(struct cls_rule *, const struct in6_addr *,
+ const struct in6_addr *);
bool cls_rule_equal(const struct cls_rule *, const struct cls_rule *);
uint32_t cls_rule_hash(const struct cls_rule *, uint32_t basis);
diff --git a/lib/flow.c b/lib/flow.c
index 4d472312..ef1dd6d2 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -444,7 +444,7 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
const flow_wildcards_t wc = wildcards->wildcards;
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
for (i = 0; i < FLOW_N_REGS; i++) {
flow->regs[i] &= wildcards->reg_masks[i];
@@ -497,9 +497,8 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
&wildcards->ipv6_src_mask);
flow->ipv6_dst = ipv6_addr_bitand(&flow->ipv6_dst,
&wildcards->ipv6_dst_mask);
- if (wc & FWW_ND_TARGET) {
- memset(&flow->nd_target, 0, sizeof flow->nd_target);
- }
+ flow->nd_target = ipv6_addr_bitand(&flow->nd_target,
+ &wildcards->nd_target_mask);
flow->skb_priority = 0;
}
@@ -507,7 +506,7 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
void
flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
fmd->tun_id = flow->tun_id;
fmd->tun_id_mask = htonll(UINT64_MAX);
@@ -596,7 +595,7 @@ flow_print(FILE *stream, const struct flow *flow)
void
flow_wildcards_init_catchall(struct flow_wildcards *wc)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
wc->wildcards = FWW_ALL;
wc->tun_id_mask = htonll(0);
@@ -604,6 +603,7 @@ flow_wildcards_init_catchall(struct flow_wildcards *wc)
wc->nw_dst_mask = htonl(0);
wc->ipv6_src_mask = in6addr_any;
wc->ipv6_dst_mask = in6addr_any;
+ wc->nd_target_mask = in6addr_any;
memset(wc->reg_masks, 0, sizeof wc->reg_masks);
wc->vlan_tci_mask = htons(0);
wc->nw_frag_mask = 0;
@@ -617,7 +617,7 @@ flow_wildcards_init_catchall(struct flow_wildcards *wc)
void
flow_wildcards_init_exact(struct flow_wildcards *wc)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
wc->wildcards = 0;
wc->tun_id_mask = htonll(UINT64_MAX);
@@ -625,6 +625,7 @@ flow_wildcards_init_exact(struct flow_wildcards *wc)
wc->nw_dst_mask = htonl(UINT32_MAX);
wc->ipv6_src_mask = in6addr_exact;
wc->ipv6_dst_mask = in6addr_exact;
+ wc->nd_target_mask = in6addr_exact;
memset(wc->reg_masks, 0xff, sizeof wc->reg_masks);
wc->vlan_tci_mask = htons(UINT16_MAX);
wc->nw_frag_mask = UINT8_MAX;
@@ -640,7 +641,7 @@ flow_wildcards_is_exact(const struct flow_wildcards *wc)
{
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
if (wc->wildcards
|| wc->tun_id_mask != htonll(UINT64_MAX)
@@ -651,6 +652,7 @@ flow_wildcards_is_exact(const struct flow_wildcards *wc)
|| wc->vlan_tci_mask != htons(UINT16_MAX)
|| !ipv6_mask_is_exact(&wc->ipv6_src_mask)
|| !ipv6_mask_is_exact(&wc->ipv6_dst_mask)
+ || !ipv6_mask_is_exact(&wc->nd_target_mask)
|| wc->nw_frag_mask != UINT8_MAX) {
return false;
}
@@ -671,7 +673,7 @@ flow_wildcards_is_catchall(const struct flow_wildcards *wc)
{
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
if (wc->wildcards != FWW_ALL
|| wc->tun_id_mask != htonll(0)
@@ -682,6 +684,7 @@ flow_wildcards_is_catchall(const struct flow_wildcards *wc)
|| wc->vlan_tci_mask != htons(0)
|| !ipv6_mask_is_any(&wc->ipv6_src_mask)
|| !ipv6_mask_is_any(&wc->ipv6_dst_mask)
+ || !ipv6_mask_is_any(&wc->nd_target_mask)
|| wc->nw_frag_mask != 0) {
return false;
}
@@ -705,7 +708,7 @@ flow_wildcards_combine(struct flow_wildcards *dst,
{
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
dst->wildcards = src1->wildcards | src2->wildcards;
dst->tun_id_mask = src1->tun_id_mask & src2->tun_id_mask;
@@ -715,6 +718,8 @@ flow_wildcards_combine(struct flow_wildcards *dst,
&src2->ipv6_src_mask);
dst->ipv6_dst_mask = ipv6_addr_bitand(&src1->ipv6_dst_mask,
&src2->ipv6_dst_mask);
+ dst->nd_target_mask = ipv6_addr_bitand(&src1->nd_target_mask,
+ &src2->nd_target_mask);
for (i = 0; i < FLOW_N_REGS; i++) {
dst->reg_masks[i] = src1->reg_masks[i] & src2->reg_masks[i];
}
@@ -730,7 +735,7 @@ flow_wildcards_hash(const struct flow_wildcards *wc, uint32_t basis)
/* If you change struct flow_wildcards and thereby trigger this
* assertion, please check that the new struct flow_wildcards has no holes
* in it before you update the assertion. */
- BUILD_ASSERT_DECL(sizeof *wc == 64 + FLOW_N_REGS * 4);
+ BUILD_ASSERT_DECL(sizeof *wc == 80 + FLOW_N_REGS * 4);
return hash_bytes(wc, sizeof *wc, basis);
}
@@ -742,7 +747,7 @@ flow_wildcards_equal(const struct flow_wildcards *a,
{
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
if (a->wildcards != b->wildcards
|| a->tun_id_mask != b->tun_id_mask
@@ -751,6 +756,7 @@ flow_wildcards_equal(const struct flow_wildcards *a,
|| a->vlan_tci_mask != b->vlan_tci_mask
|| !ipv6_addr_equals(&a->ipv6_src_mask, &b->ipv6_src_mask)
|| !ipv6_addr_equals(&a->ipv6_dst_mask, &b->ipv6_dst_mask)
+ || !ipv6_addr_equals(&a->nd_target_mask, &b->nd_target_mask)
|| a->tp_src_mask != b->tp_src_mask
|| a->tp_dst_mask != b->tp_dst_mask) {
return false;
@@ -774,7 +780,7 @@ flow_wildcards_has_extra(const struct flow_wildcards *a,
int i;
struct in6_addr ipv6_masked;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
for (i = 0; i < FLOW_N_REGS; i++) {
if ((a->reg_masks[i] & b->reg_masks[i]) != b->reg_masks[i]) {
@@ -792,6 +798,11 @@ flow_wildcards_has_extra(const struct flow_wildcards *a,
return true;
}
+ ipv6_masked = ipv6_addr_bitand(&a->nd_target_mask, &b->nd_target_mask);
+ if (!ipv6_addr_equals(&ipv6_masked, &b->nd_target_mask)) {
+ return true;
+ }
+
return (a->wildcards & ~b->wildcards
|| (a->tun_id_mask & b->tun_id_mask) != b->tun_id_mask
|| (a->nw_src_mask & b->nw_src_mask) != b->nw_src_mask
diff --git a/lib/flow.h b/lib/flow.h
index 5b389bca..41e63865 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -35,7 +35,7 @@ struct ofpbuf;
/* This sequence number should be incremented whenever anything involving flows
* or the wildcarding of flows changes. This will cause build assertion
* failures in places which likely need to be updated. */
-#define FLOW_WC_SEQ 9
+#define FLOW_WC_SEQ 10
#define FLOW_N_REGS 8
BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
@@ -100,7 +100,7 @@ BUILD_ASSERT_DECL(sizeof(((struct flow *)0)->nw_frag) == 1);
BUILD_ASSERT_DECL(sizeof(struct flow) == FLOW_SIG_SIZE + FLOW_PAD_SIZE);
/* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
-BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 142 && FLOW_WC_SEQ == 9);
+BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 142 && FLOW_WC_SEQ == 10);
void flow_extract(struct ofpbuf *, uint32_t priority, ovs_be64 tun_id,
uint16_t in_port, struct flow *);
@@ -159,13 +159,12 @@ typedef unsigned int OVS_BITWISE flow_wildcards_t;
#define FWW_NW_ECN ((OVS_FORCE flow_wildcards_t) (1 << 7))
#define FWW_ARP_SHA ((OVS_FORCE flow_wildcards_t) (1 << 8))
#define FWW_ARP_THA ((OVS_FORCE flow_wildcards_t) (1 << 9))
-#define FWW_ND_TARGET ((OVS_FORCE flow_wildcards_t) (1 << 10))
-#define FWW_IPV6_LABEL ((OVS_FORCE flow_wildcards_t) (1 << 11))
-#define FWW_NW_TTL ((OVS_FORCE flow_wildcards_t) (1 << 12))
-#define FWW_ALL ((OVS_FORCE flow_wildcards_t) (((1 << 13)) - 1))
+#define FWW_IPV6_LABEL ((OVS_FORCE flow_wildcards_t) (1 << 10))
+#define FWW_NW_TTL ((OVS_FORCE flow_wildcards_t) (1 << 11))
+#define FWW_ALL ((OVS_FORCE flow_wildcards_t) (((1 << 12)) - 1))
/* Remember to update FLOW_WC_SEQ when adding or removing FWW_*. */
-BUILD_ASSERT_DECL(FWW_ALL == ((1 << 13) - 1) && FLOW_WC_SEQ == 9);
+BUILD_ASSERT_DECL(FWW_ALL == ((1 << 12) - 1) && FLOW_WC_SEQ == 10);
/* Information on wildcards for a flow, as a supplement to "struct flow".
*
@@ -179,6 +178,8 @@ struct flow_wildcards {
ovs_be32 nw_dst_mask; /* 1-bit in each significant nw_dst bit. */
struct in6_addr ipv6_src_mask; /* 1-bit in each signficant ipv6_src bit. */
struct in6_addr ipv6_dst_mask; /* 1-bit in each signficant ipv6_dst bit. */
+ struct in6_addr nd_target_mask; /* 1-bit in each significant
+ nd_target bit. */
ovs_be16 vlan_tci_mask; /* 1-bit in each significant vlan_tci bit. */
ovs_be16 tp_src_mask; /* 1-bit in each significant tp_src bit. */
ovs_be16 tp_dst_mask; /* 1-bit in each significant tp_dst bit. */
@@ -187,7 +188,7 @@ struct flow_wildcards {
};
/* Remember to update FLOW_WC_SEQ when updating struct flow_wildcards. */
-BUILD_ASSERT_DECL(sizeof(struct flow_wildcards) == 96 && FLOW_WC_SEQ == 9);
+BUILD_ASSERT_DECL(sizeof(struct flow_wildcards) == 112 && FLOW_WC_SEQ == 10);
void flow_wildcards_init_catchall(struct flow_wildcards *);
void flow_wildcards_init_exact(struct flow_wildcards *);
diff --git a/lib/learn.c b/lib/learn.c
index 480ad909..7f30f6ee 100644
--- a/lib/learn.c
+++ b/lib/learn.c
@@ -184,7 +184,7 @@ learn_check(const struct nx_action_learn *learn, const struct flow *flow)
* prerequisites. No prerequisite depends on the value of
* a field that is wider than 64 bits. So just skip
* setting it entirely. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
}
}
}
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index 3db528fb..69226cbc 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -407,7 +407,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
{
MFF_ND_TARGET, "nd_target", NULL,
MF_FIELD_SIZES(ipv6),
- MFM_NONE, FWW_ND_TARGET,
+ MFM_CIDR, 0,
MFS_IPV6,
MFP_ND,
false,
@@ -553,7 +553,6 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
case MFF_ARP_OP:
case MFF_ARP_SHA:
case MFF_ARP_THA:
- case MFF_ND_TARGET:
case MFF_ND_SLL:
case MFF_ND_TLL:
assert(mf->fww_bit != 0);
@@ -612,6 +611,9 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
case MFF_IPV6_DST:
return ipv6_mask_is_any(&wc->ipv6_dst_mask);
+ case MFF_ND_TARGET:
+ return ipv6_mask_is_any(&wc->nd_target_mask);
+
case MFF_IP_FRAG:
return !(wc->nw_frag_mask & FLOW_NW_FRAG_MASK);
@@ -659,7 +661,6 @@ mf_get_mask(const struct mf_field *mf, const struct flow_wildcards *wc,
case MFF_ARP_OP:
case MFF_ARP_SHA:
case MFF_ARP_THA:
- case MFF_ND_TARGET:
case MFF_ND_SLL:
case MFF_ND_TLL:
assert(mf->fww_bit != 0);
@@ -729,6 +730,10 @@ mf_get_mask(const struct mf_field *mf, const struct flow_wildcards *wc,
mask->ipv6 = wc->ipv6_dst_mask;
break;
+ case MFF_ND_TARGET:
+ mask->ipv6 = wc->nd_target_mask;
+ break;
+
case MFF_IP_FRAG:
mask->u8 = wc->nw_frag_mask & FLOW_NW_FRAG_MASK;
break;
@@ -1624,7 +1629,7 @@ mf_set_wild(const struct mf_field *mf, struct cls_rule *rule)
break;
case MFF_ND_TARGET:
- rule->wc.wildcards |= FWW_ND_TARGET;
+ memset(&rule->wc.nd_target_mask, 0, sizeof rule->wc.nd_target_mask);
memset(&rule->flow.nd_target, 0, sizeof rule->flow.nd_target);
break;
@@ -1676,7 +1681,6 @@ mf_set(const struct mf_field *mf,
case MFF_ICMPV4_CODE:
case MFF_ICMPV6_TYPE:
case MFF_ICMPV6_CODE:
- case MFF_ND_TARGET:
case MFF_ND_SLL:
case MFF_ND_TLL:
NOT_REACHED();
@@ -1742,6 +1746,10 @@ mf_set(const struct mf_field *mf,
cls_rule_set_ipv6_dst_masked(rule, &value->ipv6, &mask->ipv6);
break;
+ case MFF_ND_TARGET:
+ cls_rule_set_nd_target_masked(rule, &value->ipv6, &mask->ipv6);
+ break;
+
case MFF_IP_FRAG:
cls_rule_set_nw_frag_masked(rule, value->u8, mask->u8);
break;
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 0e61d352..91dd7fc5 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -471,7 +471,7 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr,
int match_len;
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
/* Metadata. */
if (!(wc & FWW_IN_PORT)) {
@@ -514,10 +514,8 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr,
if (flow->nw_proto == IPPROTO_ICMPV6
&& (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT) ||
flow->tp_src == htons(ND_NEIGHBOR_ADVERT))) {
- if (!(wc & FWW_ND_TARGET)) {
- nxm_put_ipv6(b, NXM_NX_ND_TARGET, &flow->nd_target,
- &in6addr_exact);
- }
+ nxm_put_ipv6(b, NXM_NX_ND_TARGET, &flow->nd_target,
+ &cr->wc.nd_target_mask);
if (!(wc & FWW_ARP_SHA)
&& flow->tp_src == htons(ND_NEIGHBOR_SOLICIT)) {
nxm_put_eth(b, NXM_NX_ND_SLL, flow->arp_sha);
diff --git a/lib/nx-match.h b/lib/nx-match.h
index 592d46fb..296a63a0 100644
--- a/lib/nx-match.h
+++ b/lib/nx-match.h
@@ -90,7 +90,7 @@ void nxm_decode(struct mf_subfield *, ovs_be32 header, ovs_be16 ofs_nbits);
void nxm_decode_discrete(struct mf_subfield *, ovs_be32 header,
ovs_be16 ofs, ovs_be16 n_bits);
-BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
+BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
/* Upper bound on the length of an nx_match. The longest nx_match (an
* IPV6 neighbor discovery message using 5 registers) would be:
*
@@ -111,7 +111,7 @@ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
* NXM_OF_IPV6_LABEL 4 4 -- 8
* NXM_OF_ICMP_TYPE 4 1 -- 5
* NXM_OF_ICMP_CODE 4 1 -- 5
- * NXM_NX_ND_TARGET 4 16 -- 20
+ * NXM_NX_ND_TARGET 4 16 16 36
* NXM_NX_ND_SLL 4 6 -- 10
* NXM_NX_REG_W(0) 4 4 4 12
* NXM_NX_REG_W(1) 4 4 4 12
@@ -123,11 +123,11 @@ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
* NXM_NX_REG_W(7) 4 4 4 12
* NXM_NX_TUN_ID_W 4 8 8 20
* -------------------------------------------
- * total 311
+ * total 327
*
* So this value is conservative.
*/
-#define NXM_MAX_LEN 384
+#define NXM_MAX_LEN 400
/* This is my guess at the length of a "typical" nx_match, for use in
* predicting space requirements. */
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 90475f7a..ae9b30d3 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -101,7 +101,7 @@ static const flow_wildcards_t WC_INVARIANTS = 0
void
ofputil_wildcard_from_openflow(uint32_t ofpfw, struct flow_wildcards *wc)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
/* Initialize most of rule->wc. */
flow_wildcards_init_catchall(wc);
@@ -109,7 +109,7 @@ ofputil_wildcard_from_openflow(uint32_t ofpfw, struct flow_wildcards *wc)
/* Wildcard fields that aren't defined by ofp_match or tun_id. */
wc->wildcards |= (FWW_ARP_SHA | FWW_ARP_THA | FWW_NW_ECN | FWW_NW_TTL
- | FWW_ND_TARGET | FWW_IPV6_LABEL);
+ | FWW_IPV6_LABEL);
if (ofpfw & OFPFW_NW_TOS) {
/* OpenFlow 1.0 defines a TOS wildcard, but it's much later in
@@ -1166,7 +1166,7 @@ ofputil_usable_protocols(const struct cls_rule *rule)
{
const struct flow_wildcards *wc = &rule->wc;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 9);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
/* Only NXM supports separately wildcards the Ethernet multicast bit. */
if (!(wc->wildcards & FWW_DL_DST) != !(wc->wildcards & FWW_ETH_MCAST)) {
@@ -3810,7 +3810,7 @@ ofputil_normalize_rule(struct cls_rule *rule)
wc.wildcards |= FWW_IPV6_LABEL;
}
if (!(may_match & MAY_ND_TARGET)) {
- wc.wildcards |= FWW_ND_TARGET;
+ wc.nd_target_mask = in6addr_any;
}
/* Log any changes. */
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index a52382ea..37498a74 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -109,8 +109,10 @@ udp dl_vlan_pcp=7 idle_timeout=5 actions=strip_vlan output:0
tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1
udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
icmp6,icmp_type=135,nd_target=FEC0::1234:F045:8FFF:1111:FE4E:0571 actions=drop
+icmp6,icmp_type=135,nd_target=FEC0::1234:F045:8FFF:1111:FE4F:0571/112 actions=drop
icmp6,icmp_type=135,nd_sll=00:0A:E4:25:6B:B0 actions=drop
icmp6,icmp_type=136,nd_target=FEC0::1234:F045:8FFF:1111:FE4E:0571,nd_tll=00:0A:E4:25:6B:B1 actions=drop
+icmp6,icmp_type=136,nd_target=FEC0::1234:F045:8FFF:1111:FE00:0000/96,nd_tll=00:0A:E4:25:6B:B1 actions=drop
cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller
actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note
tun_id=0x1234,cookie=0x5678,actions=flood
@@ -139,8 +141,10 @@ NXT_FLOW_MOD: ADD udp,dl_vlan_pcp=7 idle:5 actions=strip_vlan,output:0
NXT_FLOW_MOD: ADD tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1
NXT_FLOW_MOD: ADD udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
NXT_FLOW_MOD: ADD icmp6,icmp_type=135,nd_target=fec0:0:1234:f045:8fff:1111:fe4e:571 actions=drop
+NXT_FLOW_MOD: ADD icmp6,icmp_type=135,nd_target=fec0:0:1234:f045:8fff:1111:fe4f:0/112 actions=drop
NXT_FLOW_MOD: ADD icmp6,icmp_type=135,nd_sll=00:0a:e4:25:6b:b0 actions=drop
NXT_FLOW_MOD: ADD icmp6,icmp_type=136,nd_target=fec0:0:1234:f045:8fff:1111:fe4e:571,nd_tll=00:0a:e4:25:6b:b1 actions=drop
+NXT_FLOW_MOD: ADD icmp6,icmp_type=136,nd_target=fec0:0:1234:f045:8fff:1111::/96,nd_tll=00:0a:e4:25:6b:b1 actions=drop
NXT_FLOW_MOD: ADD priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535
NXT_FLOW_MOD: ADD actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00
NXT_FLOW_MOD: ADD tun_id=0x1234 cookie:0x5678 actions=FLOOD
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index d1d82c50..18769290 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -654,7 +654,7 @@ as a CIDR block (e.g. \fB2001:db8:3c4d:1::/64\fR).
When \fBdl_type\fR is 0x86dd (possibly via shorthand, e.g., \fBipv6\fR
or \fBtcp6\fR), matches IPv6 flow label \fIlabel\fR.
.
-.IP \fBnd_target=\fIipv6\fR
+.IP \fBnd_target=\fIipv6\fR[\fB/\fInetmask\fR]
When \fBdl_type\fR, \fBnw_proto\fR, and \fBicmp_type\fR specify
IPv6 Neighbor Discovery (ICMPv6 type 135 or 136), matches the target address
\fIipv6\fR. \fIipv6\fR is in the same format described earlier for the