diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/classifier.c | 33 | ||||
-rw-r--r-- | lib/classifier.h | 1 | ||||
-rw-r--r-- | lib/flow.c | 94 | ||||
-rw-r--r-- | lib/flow.h | 14 | ||||
-rw-r--r-- | lib/nx-match.c | 42 | ||||
-rw-r--r-- | lib/nx-match.def | 3 | ||||
-rw-r--r-- | lib/nx-match.h | 8 | ||||
-rw-r--r-- | lib/odp-util.c | 55 | ||||
-rw-r--r-- | lib/odp-util.h | 4 | ||||
-rw-r--r-- | lib/ofp-parse.c | 20 | ||||
-rw-r--r-- | lib/ofp-util.c | 2 |
11 files changed, 254 insertions, 22 deletions
diff --git a/lib/classifier.c b/lib/classifier.c index 658be808..fcc965c8 100644 --- a/lib/classifier.c +++ b/lib/classifier.c @@ -369,6 +369,13 @@ cls_rule_set_ipv6_dst_masked(struct cls_rule *rule, const struct in6_addr *dst, } } +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; +} + /* Returns true if 'a' and 'b' have the same priority, wildcard the same * fields, and have the same values for fixed fields, otherwise false. */ bool @@ -586,7 +593,20 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s) if (!(w & FWW_TP_DST)) { ds_put_format(s, "icmp_code=%"PRIu16",", ntohs(f->tp_dst)); } - } else { + if (!(w & FWW_ND_TARGET)) { + ds_put_cstr(s, "nd_target="); + print_ipv6_addr(s, &f->nd_target); + ds_put_char(s, ','); + } + if (!(w & FWW_ARP_SHA)) { + ds_put_format(s, "nd_sll="ETH_ADDR_FMT",", + ETH_ADDR_ARGS(f->arp_sha)); + } + if (!(w & FWW_ARP_THA)) { + ds_put_format(s, "nd_tll="ETH_ADDR_FMT",", + ETH_ADDR_ARGS(f->arp_tha)); + } + } else { if (!(w & FWW_TP_SRC)) { ds_put_format(s, "tp_src=%"PRIu16",", ntohs(f->tp_src)); } @@ -1080,7 +1100,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_SIG_SIZE == 84 + FLOW_N_REGS * 4); + BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 100 + FLOW_N_REGS * 4); for (i = 0; i < FLOW_N_REGS; i++) { if ((a->regs[i] ^ b->regs[i]) & wildcards->reg_masks[i]) { @@ -1113,7 +1133,9 @@ flow_equal_except(const struct flow *a, const struct flow *b, && ipv6_equal_except(&a->ipv6_src, &b->ipv6_src, &wildcards->ipv6_src_mask) && ipv6_equal_except(&a->ipv6_dst, &b->ipv6_dst, - &wildcards->ipv6_dst_mask)); + &wildcards->ipv6_dst_mask) + && (wc & FWW_ND_TARGET + || ipv6_addr_equals(&a->nd_target, &b->nd_target))); } static void @@ -1122,7 +1144,7 @@ zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards) const flow_wildcards_t wc = wildcards->wildcards; int i; - BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 84 + 4 * FLOW_N_REGS); + BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 100 + 4 * FLOW_N_REGS); for (i = 0; i < FLOW_N_REGS; i++) { flow->regs[i] &= wildcards->reg_masks[i]; @@ -1169,4 +1191,7 @@ 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); + } } diff --git a/lib/classifier.h b/lib/classifier.h index c82a4844..d3121bfb 100644 --- a/lib/classifier.h +++ b/lib/classifier.h @@ -109,6 +109,7 @@ bool cls_rule_set_ipv6_src_masked(struct cls_rule *, const struct in6_addr *, void cls_rule_set_ipv6_dst(struct cls_rule *, const struct in6_addr *); bool cls_rule_set_ipv6_dst_masked(struct cls_rule *, const struct in6_addr *, const struct in6_addr *); +void cls_rule_set_nd_target(struct cls_rule *, const struct in6_addr); bool cls_rule_equal(const struct cls_rule *, const struct cls_rule *); @@ -230,6 +230,94 @@ parse_ipv6(struct ofpbuf *packet, struct flow *flow) return nh_len; } +/* Neighbor Discovery Solicitation and Advertisement messages are + * identical in structure, so we'll just use one of them. To be safe, + * we'll assert that they're still identical. */ +BUILD_ASSERT_DECL(sizeof(struct nd_neighbor_solicit) + == sizeof(struct nd_neighbor_advert)); + +static bool +parse_icmpv6(struct ofpbuf *b, struct flow *flow, int icmp_len) +{ + const struct icmp6_hdr *icmp = pull_icmpv6(b); + + if (!icmp) { + return false; + } + + /* The ICMPv6 type and code fields use the 16-bit transport port + * fields, so we need to store them in 16-bit network byte order. */ + flow->icmp_type = htons(icmp->icmp6_type); + flow->icmp_code = htons(icmp->icmp6_code); + + if (!icmp->icmp6_code + && ((icmp->icmp6_type == ND_NEIGHBOR_SOLICIT) + || (icmp->icmp6_type == ND_NEIGHBOR_ADVERT))) { + struct nd_neighbor_solicit *nd_ns; /* Identical to ND advert */ + + /* In order to process neighbor discovery options, we need the + * entire packet. */ + if ((icmp_len < sizeof *nd_ns) + || (!ofpbuf_try_pull(b, sizeof *nd_ns - sizeof *icmp))) { + return false; + } + nd_ns = (struct nd_neighbor_solicit *)icmp; + flow->nd_target = nd_ns->nd_ns_target; + + icmp_len -= sizeof(*nd_ns); + while (icmp_len >= 8) { + struct nd_opt_hdr *nd_opt; + int opt_len; + const uint8_t *data; + + /* The minimum size of an option is 8 bytes, which also is + * the size of Ethernet link-layer options. */ + nd_opt = ofpbuf_pull(b, 8); + if (!nd_opt->nd_opt_len || nd_opt->nd_opt_len * 8 > icmp_len) { + goto invalid; + } + opt_len = nd_opt->nd_opt_len * 8; + data = (const uint8_t *)(nd_opt + 1); + + /* Store the link layer address if the appropriate option is + * provided. It is considered an error if the same link + * layer option is specified twice. */ + if (nd_opt->nd_opt_type == ND_OPT_SOURCE_LINKADDR + && opt_len == 8) { + if (eth_addr_is_zero(flow->arp_sha)) { + memcpy(flow->arp_sha, data, ETH_ADDR_LEN); + } else { + goto invalid; + } + } else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LINKADDR + && opt_len == 8) { + if (eth_addr_is_zero(flow->arp_tha)) { + memcpy(flow->arp_tha, data, ETH_ADDR_LEN); + } else { + goto invalid; + } + } + + /* Pull the rest of this option. */ + if (!ofpbuf_try_pull(b, opt_len - 8)) { + goto invalid; + } + + icmp_len -= opt_len; + } + } + + return true; + +invalid: + memset(&flow->nd_target, '\0', sizeof(flow->nd_target)); + memset(flow->arp_sha, '\0', sizeof(flow->arp_sha)); + memset(flow->arp_tha, '\0', sizeof(flow->arp_tha)); + + return false; + +} + /* Initializes 'flow' members from 'packet', 'tun_id', and 'in_port. * Initializes 'packet' header pointers as follows: * @@ -344,10 +432,8 @@ flow_extract(struct ofpbuf *packet, ovs_be64 tun_id, uint16_t in_port, packet->l7 = b.data; } } else if (flow->nw_proto == IPPROTO_ICMPV6) { - const struct icmp6_hdr *icmp = pull_icmpv6(&b); - if (icmp) { - flow->icmp_type = htons(icmp->icmp6_type); - flow->icmp_code = htons(icmp->icmp6_code); + int icmp_len = ntohs(nh->ip6_plen) + sizeof *nh - nh_len; + if (parse_icmpv6(&b, flow, icmp_len)) { packet->l7 = b.data; } } @@ -54,19 +54,20 @@ struct flow { uint8_t dl_dst[6]; /* Ethernet destination address. */ uint8_t nw_proto; /* IP protocol or low 8 bits of ARP opcode. */ uint8_t nw_tos; /* IP ToS (DSCP field, 6 bits). */ - uint8_t arp_sha[6]; /* ARP source hardware address. */ - uint8_t arp_tha[6]; /* ARP target hardware address. */ + uint8_t arp_sha[6]; /* ARP/ND source hardware address. */ + uint8_t arp_tha[6]; /* ARP/ND target hardware address. */ struct in6_addr ipv6_src; /* IPv6 source address. */ struct in6_addr ipv6_dst; /* IPv6 destination address. */ + struct in6_addr nd_target; /* IPv6 neighbor discovery (ND) target. */ uint32_t reserved; /* Reserved for 64-bit packing. */ }; /* Assert that there are FLOW_SIG_SIZE bytes of significant data in "struct * flow", followed by FLOW_PAD_SIZE bytes of padding. */ -#define FLOW_SIG_SIZE (84 + FLOW_N_REGS * 4) +#define FLOW_SIG_SIZE (100 + FLOW_N_REGS * 4) #define FLOW_PAD_SIZE 4 -BUILD_ASSERT_DECL(offsetof(struct flow, ipv6_dst) == FLOW_SIG_SIZE - 16); -BUILD_ASSERT_DECL(sizeof(((struct flow *)0)->ipv6_dst) == 16); +BUILD_ASSERT_DECL(offsetof(struct flow, nd_target) == FLOW_SIG_SIZE - 16); +BUILD_ASSERT_DECL(sizeof(((struct flow *)0)->nd_target) == 16); BUILD_ASSERT_DECL(sizeof(struct flow) == FLOW_SIG_SIZE + FLOW_PAD_SIZE); int flow_extract(struct ofpbuf *, uint64_t tun_id, uint16_t in_port, @@ -122,7 +123,8 @@ typedef unsigned int OVS_BITWISE flow_wildcards_t; /* multicast bit only */ #define FWW_ARP_SHA ((OVS_FORCE flow_wildcards_t) (1 << 9)) #define FWW_ARP_THA ((OVS_FORCE flow_wildcards_t) (1 << 10)) -#define FWW_ALL ((OVS_FORCE flow_wildcards_t) (((1 << 11)) - 1)) +#define FWW_ND_TARGET ((OVS_FORCE flow_wildcards_t) (1 << 11)) +#define FWW_ALL ((OVS_FORCE flow_wildcards_t) (((1 << 12)) - 1)) /* Information on wildcards for a flow, as a supplement to "struct flow". * diff --git a/lib/nx-match.c b/lib/nx-match.c index 5fc6aa24..abc3b210 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -18,6 +18,8 @@ #include "nx-match.h" +#include <netinet/icmp6.h> + #include "classifier.h" #include "dynamic-string.h" #include "ofp-util.h" @@ -363,6 +365,30 @@ parse_nxm_entry(struct cls_rule *rule, const struct nxm_field *f, flow->tp_dst = htons(*(uint8_t *) value); return 0; + /* IPv6 Neighbor Discovery. */ + case NFI_NXM_NX_ND_TARGET: + /* We've already verified that it's an ICMPv6 message. */ + if ((flow->tp_src != htons(ND_NEIGHBOR_SOLICIT)) + && (flow->tp_src != htons(ND_NEIGHBOR_ADVERT))) { + return NXM_BAD_PREREQ; + } + memcpy(&flow->nd_target, value, sizeof flow->nd_target); + return 0; + case NFI_NXM_NX_ND_SLL: + /* We've already verified that it's an ICMPv6 message. */ + if (flow->tp_src != htons(ND_NEIGHBOR_SOLICIT)) { + return NXM_BAD_PREREQ; + } + memcpy(flow->arp_sha, value, ETH_ADDR_LEN); + return 0; + case NFI_NXM_NX_ND_TLL: + /* We've already verified that it's an ICMPv6 message. */ + if (flow->tp_src != htons(ND_NEIGHBOR_ADVERT)) { + return NXM_BAD_PREREQ; + } + memcpy(flow->arp_tha, value, ETH_ADDR_LEN); + return 0; + /* ARP header. */ case NFI_NXM_OF_ARP_OP: if (ntohs(get_unaligned_be16(value)) > 255) { @@ -815,6 +841,16 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr) if (!(wc & FWW_TP_DST)) { nxm_put_8(b, NXM_NX_ICMPV6_CODE, ntohs(flow->tp_dst)); } + if (!(wc & FWW_ND_TARGET)) { + nxm_put_ipv6(b, NXM_NX_ND_TARGET, &flow->nd_target, + &in6addr_exact); + } + if (!(wc & FWW_ARP_SHA)) { + nxm_put_eth(b, NXM_NX_ND_SLL, flow->arp_sha); + } + if (!(wc & FWW_ARP_THA)) { + nxm_put_eth(b, NXM_NX_ND_TLL, flow->arp_tha); + } break; } } @@ -1303,9 +1339,11 @@ nxm_read_field(const struct nxm_field *src, const struct flow *flow) #endif case NFI_NXM_NX_ARP_SHA: + case NFI_NXM_NX_ND_SLL: return eth_addr_to_uint64(flow->arp_sha); case NFI_NXM_NX_ARP_THA: + case NFI_NXM_NX_ND_TLL: return eth_addr_to_uint64(flow->arp_tha); case NFI_NXM_NX_TUN_ID_W: @@ -1319,6 +1357,7 @@ nxm_read_field(const struct nxm_field *src, const struct flow *flow) case NFI_NXM_NX_IPV6_SRC_W: case NFI_NXM_NX_IPV6_DST: case NFI_NXM_NX_IPV6_DST_W: + case NFI_NXM_NX_ND_TARGET: case N_NXM_FIELDS: NOT_REACHED(); } @@ -1392,6 +1431,9 @@ nxm_write_field(const struct nxm_field *dst, struct flow *flow, case NFI_NXM_NX_IPV6_DST_W: case NFI_NXM_NX_ICMPV6_TYPE: case NFI_NXM_NX_ICMPV6_CODE: + case NFI_NXM_NX_ND_TARGET: + case NFI_NXM_NX_ND_SLL: + case NFI_NXM_NX_ND_TLL: case N_NXM_FIELDS: NOT_REACHED(); } diff --git a/lib/nx-match.def b/lib/nx-match.def index 41e76d65..4a42aaa4 100644 --- a/lib/nx-match.def +++ b/lib/nx-match.def @@ -53,6 +53,9 @@ DEFINE_FIELD_M(NX_IPV6_SRC, 0, NXM_DL_IPV6, 0, false) DEFINE_FIELD_M(NX_IPV6_DST, 0, NXM_DL_IPV6, 0, false) DEFINE_FIELD (NX_ICMPV6_TYPE, FWW_TP_SRC, NXM_DL_IPV6, IPPROTO_ICMPV6, false) DEFINE_FIELD (NX_ICMPV6_CODE, FWW_TP_DST, NXM_DL_IPV6, IPPROTO_ICMPV6, false) +DEFINE_FIELD (NX_ND_TARGET, FWW_ND_TARGET,NXM_DL_IPV6, IPPROTO_ICMPV6, false) +DEFINE_FIELD (NX_ND_SLL, FWW_ARP_SHA, NXM_DL_IPV6, IPPROTO_ICMPV6, false) +DEFINE_FIELD (NX_ND_TLL, FWW_ARP_THA, NXM_DL_IPV6, IPPROTO_ICMPV6, false) DEFINE_FIELD_M(NX_REG0, 0, NXM_DL_NONE, 0, true) #if FLOW_N_REGS >= 2 diff --git a/lib/nx-match.h b/lib/nx-match.h index aefcb653..a76ad1f2 100644 --- a/lib/nx-match.h +++ b/lib/nx-match.h @@ -95,15 +95,17 @@ nxm_decode_n_bits(ovs_be16 ofs_nbits) * NXM_OF_IP_PROTO 4 2 -- 6 * NXM_OF_IPV6_SRC_W 4 16 16 36 * NXM_OF_IPV6_DST_W 4 16 16 36 - * NXM_OF_TCP_SRC 4 2 -- 6 - * NXM_OF_TCP_DST 4 2 -- 6 + * NXM_OF_ICMP_TYPE 4 1 -- 5 + * NXM_OF_ICMP_CODE 4 1 -- 5 + * NXM_NX_ND_TARGET 4 16 -- 20 + * 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 * NXM_NX_REG_W(2) 4 4 4 12 * NXM_NX_REG_W(3) 4 4 4 12 * NXM_NX_TUN_ID_W 4 8 8 20 * ------------------------------------------- - * total 209 + * total 237 * * So this value is conservative. */ diff --git a/lib/odp-util.c b/lib/odp-util.c index e7acaad8..c90ff7d2 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -19,6 +19,7 @@ #include "odp-util.h" #include <errno.h> #include <inttypes.h> +#include <netinet/icmp6.h> #include <stdlib.h> #include <string.h> #include "byte-order.h" @@ -201,6 +202,7 @@ odp_flow_key_attr_len(uint16_t type) case ODP_KEY_ATTR_ICMP: return sizeof(struct odp_key_icmp); case ODP_KEY_ATTR_ICMPV6: return sizeof(struct odp_key_icmpv6); case ODP_KEY_ATTR_ARP: return sizeof(struct odp_key_arp); + case ODP_KEY_ATTR_ND: return sizeof(struct odp_key_nd); case ODP_KEY_ATTR_UNSPEC: case __ODP_KEY_ATTR_MAX: @@ -242,6 +244,7 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds) const struct odp_key_icmp *icmp_key; const struct odp_key_icmpv6 *icmpv6_key; const struct odp_key_arp *arp_key; + const struct odp_key_nd *nd_key; if (nl_attr_get_size(a) != odp_flow_key_attr_len(nl_attr_type(a))) { ds_put_format(ds, "bad length %zu, expected %d for: ", @@ -339,6 +342,25 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds) ETH_ADDR_ARGS(arp_key->arp_tha)); break; + case ODP_KEY_ATTR_ND: { + char target[INET6_ADDRSTRLEN]; + + nd_key = nl_attr_get(a); + inet_ntop(AF_INET6, nd_key->nd_target, target, sizeof target); + + ds_put_format(ds, "nd(target=%s", target); + if (!eth_addr_is_zero(nd_key->nd_sll)) { + ds_put_format(ds, ",sll="ETH_ADDR_FMT, + ETH_ADDR_ARGS(nd_key->nd_sll)); + } + if (!eth_addr_is_zero(nd_key->nd_tll)) { + ds_put_format(ds, ",tll="ETH_ADDR_FMT, + ETH_ADDR_ARGS(nd_key->nd_tll)); + } + ds_put_char(ds, ')'); + break; + } + default: format_generic_odp_key(a, ds); break; @@ -466,6 +488,18 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow) sizeof *icmpv6_key); icmpv6_key->icmpv6_type = ntohs(flow->tp_src); icmpv6_key->icmpv6_code = ntohs(flow->tp_dst); + + if (icmpv6_key->icmpv6_type == ND_NEIGHBOR_SOLICIT + || icmpv6_key->icmpv6_type == ND_NEIGHBOR_ADVERT) { + struct odp_key_nd *nd_key; + + nd_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_ND, + sizeof *nd_key); + memcpy(nd_key->nd_target, &flow->nd_target, + sizeof nd_key->nd_target); + memcpy(nd_key->nd_sll, flow->arp_sha, ETH_ADDR_LEN); + memcpy(nd_key->nd_tll, flow->arp_tha, ETH_ADDR_LEN); + } } } } @@ -494,6 +528,7 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, const struct odp_key_icmp *icmp_key; const struct odp_key_icmpv6 *icmpv6_key; const struct odp_key_arp *arp_key; + const struct odp_key_nd *nd_key; uint16_t type = nl_attr_type(nla); int len = odp_flow_key_attr_len(type); @@ -623,6 +658,17 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, memcpy(flow->arp_tha, arp_key->arp_tha, ETH_ADDR_LEN); break; + case TRANSITION(ODP_KEY_ATTR_ICMPV6, ODP_KEY_ATTR_ND): + if (flow->tp_src != htons(ND_NEIGHBOR_SOLICIT) + && flow->tp_src != htons(ND_NEIGHBOR_ADVERT)) { + return EINVAL; + } + nd_key = nl_attr_get(nla); + memcpy(&flow->nd_target, nd_key->nd_target, sizeof flow->nd_target); + memcpy(flow->arp_sha, nd_key->nd_sll, ETH_ADDR_LEN); + memcpy(flow->arp_tha, nd_key->nd_tll, ETH_ADDR_LEN); + break; + default: if (type == ODP_KEY_ATTR_UNSPEC || prev_type == ODP_KEY_ATTR_UNSPEC) { @@ -673,11 +719,18 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, } return 0; + case ODP_KEY_ATTR_ICMPV6: + if (flow->icmp_type == htons(ND_NEIGHBOR_SOLICIT) + || flow->icmp_type == htons(ND_NEIGHBOR_ADVERT)) { + return EINVAL; + } + return 0; + case ODP_KEY_ATTR_TCP: case ODP_KEY_ATTR_UDP: case ODP_KEY_ATTR_ICMP: - case ODP_KEY_ATTR_ICMPV6: case ODP_KEY_ATTR_ARP: + case ODP_KEY_ATTR_ND: return 0; case __ODP_KEY_ATTR_MAX: diff --git a/lib/odp-util.h b/lib/odp-util.h index 8ec09f3c..074df87f 100644 --- a/lib/odp-util.h +++ b/lib/odp-util.h @@ -64,13 +64,13 @@ void format_odp_actions(struct ds *, const struct nlattr *odp_actions, size_t actions_len); /* By my calculations currently the longest valid nlattr-formatted flow key is - * 92 bytes long, so this leaves some safety margin. + * 124 bytes long, so this leaves some safety margin. * * We allocate temporary on-stack buffers for flow keys as arrays of uint32_t * to ensure proper 32-bit alignment for Netlink attributes. (An array of * "struct nlattr" might not, in theory, be sufficiently aligned because it * only contains 16-bit types.) */ -#define ODPUTIL_FLOW_KEY_BYTES 112 +#define ODPUTIL_FLOW_KEY_BYTES 144 #define ODPUTIL_FLOW_KEY_U32S DIV_ROUND_UP(ODPUTIL_FLOW_KEY_BYTES, 4) void odp_flow_key_format(const struct nlattr *, size_t, struct ds *); diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index e77453e0..3fac4749 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -540,7 +540,10 @@ parse_protocol(const char *name, const struct protocol **p_out) FIELD(F_ARP_SHA, "arp_sha", FWW_ARP_SHA) \ FIELD(F_ARP_THA, "arp_tha", FWW_ARP_THA) \ FIELD(F_IPV6_SRC, "ipv6_src", 0) \ - FIELD(F_IPV6_DST, "ipv6_dst", 0) + FIELD(F_IPV6_DST, "ipv6_dst", 0) \ + FIELD(F_ND_TARGET, "nd_target", FWW_ND_TARGET) \ + FIELD(F_ND_SLL, "nd_sll", FWW_ARP_SHA) \ + FIELD(F_ND_TLL, "nd_tll", FWW_ARP_THA) enum field_index { #define FIELD(ENUM, NAME, WILDCARD) ENUM, @@ -677,6 +680,21 @@ parse_field_value(struct cls_rule *rule, enum field_index index, cls_rule_set_ipv6_dst_masked(rule, &ipv6, &ipv6_mask); break; + case F_ND_TARGET: + str_to_ipv6(value, &ipv6, NULL); + cls_rule_set_nd_target(rule, ipv6); + break; + + case F_ND_SLL: + str_to_mac(value, mac); + cls_rule_set_arp_sha(rule, mac); + break; + + case F_ND_TLL: + str_to_mac(value, mac); + cls_rule_set_arp_tha(rule, mac); + break; + case N_FIELDS: NOT_REACHED(); } diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 4d89e0ae..1125b83f 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -126,7 +126,7 @@ ofputil_cls_rule_from_match(const struct ofp_match *match, wc->wildcards = ofpfw & WC_INVARIANTS; /* Wildcard fields that aren't defined by ofp_match or tun_id. */ - wc->wildcards |= (FWW_ARP_SHA | FWW_ARP_THA); + wc->wildcards |= (FWW_ARP_SHA | FWW_ARP_THA | FWW_ND_TARGET); if (ofpfw & OFPFW_NW_TOS) { wc->wildcards |= FWW_NW_TOS; |