2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-30 13:58:14 +00:00

nicira-ext: Support matching IPv6 Neighbor Discovery messages.

IPv6 uses Neighbor Discovery messages in a similar manner to how IPv4
uses ARP.  This commit adds support for matching deeper into the
payloads of Neighbor Solicitation (NS) and Neighbor Advertisement (NA)
messages.  Currently, the matching fields include:

    - NS and NA Target (nd_target)
    - NS Source Link Layer Address (nd_sll)
    - NA Target Link Layer Address (nd_tll)

When defining IPv6 Neighbor Discovery rules, the Nicira Extensible Match
(NXM) extension to OVS must be used.

Signed-off-by: Justin Pettit <jpettit@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
This commit is contained in:
Justin Pettit
2011-02-01 22:54:11 -08:00
parent d31f1109f1
commit 685a51a5b8
18 changed files with 468 additions and 33 deletions

3
DESIGN
View File

@@ -15,7 +15,8 @@ IPv6
Open vSwitch supports stateless handling of IPv6 packets. Flows can be Open vSwitch supports stateless handling of IPv6 packets. Flows can be
written to support matching TCP, UDP, and ICMPv6 headers within an IPv6 written to support matching TCP, UDP, and ICMPv6 headers within an IPv6
packet. packet. Deeper matching of some Neighbor Discovery messages is also
supported.
IPv6 was not designed to interact well with middle-boxes. This, IPv6 was not designed to interact well with middle-boxes. This,
combined with Open vSwitch's stateless nature, have affected the combined with Open vSwitch's stateless nature, have affected the

View File

@@ -32,6 +32,7 @@
#include <net/inet_ecn.h> #include <net/inet_ecn.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#include <net/ndisc.h>
static struct kmem_cache *flow_cache; static struct kmem_cache *flow_cache;
static unsigned int hash_seed __read_mostly; static unsigned int hash_seed __read_mostly;
@@ -314,6 +315,75 @@ static __be16 parse_ethertype(struct sk_buff *skb)
return llc->ethertype; return llc->ethertype;
} }
static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key,
int nh_len)
{
struct ipv6hdr *nh = ipv6_hdr(skb);
int icmp_len = ntohs(nh->payload_len) + sizeof(*nh) - nh_len;
struct icmp6hdr *icmp = icmp6_hdr(skb);
/* 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. */
key->tp_src = htons(icmp->icmp6_type);
key->tp_dst = htons(icmp->icmp6_code);
if (!icmp->icmp6_code
&& ((icmp->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION)
|| (icmp->icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT))) {
struct nd_msg *nd;
int offset;
/* In order to process neighbor discovery options, we need the
* entire packet. */
if (icmp_len < sizeof(*nd))
goto invalid;
if (!pskb_may_pull(skb, skb_transport_offset(skb) + icmp_len))
return -ENOMEM;
nd = (struct nd_msg *)skb_transport_header(skb);
memcpy(key->nd_target, &nd->target, sizeof(key->nd_target));
icmp_len -= sizeof(*nd);
offset = 0;
while (icmp_len >= 8) {
struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd->opt + offset);
int opt_len = nd_opt->nd_opt_len * 8;
if (!opt_len || (opt_len > icmp_len))
goto invalid;
/* 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_LL_ADDR
&& opt_len == 8) {
if (!is_zero_ether_addr(key->arp_sha))
goto invalid;
memcpy(key->arp_sha,
&nd->opt[offset+sizeof(*nd_opt)], ETH_ALEN);
} else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LL_ADDR
&& opt_len == 8) {
if (!is_zero_ether_addr(key->arp_tha))
goto invalid;
memcpy(key->arp_tha,
&nd->opt[offset+sizeof(*nd_opt)], ETH_ALEN);
}
icmp_len -= opt_len;
offset += opt_len;
}
}
return 0;
invalid:
memset(key->nd_target, 0, sizeof(key->nd_target));
memset(key->arp_sha, 0, sizeof(key->arp_sha));
memset(key->arp_tha, 0, sizeof(key->arp_tha));
return 0;
}
/** /**
* flow_extract - extracts a flow key from an Ethernet frame. * flow_extract - extracts a flow key from an Ethernet frame.
* @skb: sk_buff that contains the frame, with skb->data pointing to the * @skb: sk_buff that contains the frame, with skb->data pointing to the
@@ -482,12 +552,9 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
} }
} else if (key->nw_proto == NEXTHDR_ICMP) { } else if (key->nw_proto == NEXTHDR_ICMP) {
if (icmp6hdr_ok(skb)) { if (icmp6hdr_ok(skb)) {
struct icmp6hdr *icmp = icmp6_hdr(skb); int error = parse_icmpv6(skb, key, nh_len);
/* The ICMPv6 type and code fields use the 16-bit if (error < 0)
* transport port fields, so we need to store them return error;
* in 16-bit network byte order. */
key->tp_src = htons(icmp->icmp6_type);
key->tp_dst = htons(icmp->icmp6_code);
} }
} }
} }
@@ -517,7 +584,7 @@ int flow_cmp(const struct tbl_node *node, void *key2_)
* elements and | for alternatives: * elements and | for alternatives:
* *
* [tun_id] in_port ethernet [8021q] [ethertype \ * [tun_id] in_port ethernet [8021q] [ethertype \
* [IPv4 [TCP|UDP|ICMP] | IPv6 [TCP|UDP|ICMPv6] | ARP]] * [IPv4 [TCP|UDP|ICMP] | IPv6 [TCP|UDP|ICMPv6 [ND]] | ARP]]
*/ */
int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr) int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
{ {
@@ -543,6 +610,7 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
[ODP_KEY_ATTR_ICMP] = sizeof(struct odp_key_icmp), [ODP_KEY_ATTR_ICMP] = sizeof(struct odp_key_icmp),
[ODP_KEY_ATTR_ICMPV6] = sizeof(struct odp_key_icmpv6), [ODP_KEY_ATTR_ICMPV6] = sizeof(struct odp_key_icmpv6),
[ODP_KEY_ATTR_ARP] = sizeof(struct odp_key_arp), [ODP_KEY_ATTR_ARP] = sizeof(struct odp_key_arp),
[ODP_KEY_ATTR_ND] = sizeof(struct odp_key_nd),
}; };
const struct odp_key_ethernet *eth_key; const struct odp_key_ethernet *eth_key;
@@ -554,6 +622,7 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
const struct odp_key_icmp *icmp_key; const struct odp_key_icmp *icmp_key;
const struct odp_key_icmpv6 *icmpv6_key; const struct odp_key_icmpv6 *icmpv6_key;
const struct odp_key_arp *arp_key; const struct odp_key_arp *arp_key;
const struct odp_key_nd *nd_key;
int type = nla_type(nla); int type = nla_type(nla);
@@ -669,6 +738,17 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
memcpy(swkey->arp_tha, arp_key->arp_tha, ETH_ALEN); memcpy(swkey->arp_tha, arp_key->arp_tha, ETH_ALEN);
break; break;
case TRANSITION(ODP_KEY_ATTR_ICMPV6, ODP_KEY_ATTR_ND):
if (swkey->tp_src != htons(NDISC_NEIGHBOUR_SOLICITATION)
&& swkey->tp_src != htons(NDISC_NEIGHBOUR_ADVERTISEMENT))
return -EINVAL;
nd_key = nla_data(nla);
memcpy(swkey->nd_target, nd_key->nd_target,
sizeof(swkey->nd_target));
memcpy(swkey->arp_sha, nd_key->nd_sll, ETH_ALEN);
memcpy(swkey->arp_tha, nd_key->nd_tll, ETH_ALEN);
break;
default: default:
return -EINVAL; return -EINVAL;
} }
@@ -710,11 +790,17 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
return -EINVAL; return -EINVAL;
return 0; return 0;
case ODP_KEY_ATTR_ICMPV6:
if (swkey->tp_src == htons(NDISC_NEIGHBOUR_SOLICITATION) ||
swkey->tp_src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT))
return -EINVAL;
return 0;
case ODP_KEY_ATTR_TCP: case ODP_KEY_ATTR_TCP:
case ODP_KEY_ATTR_UDP: case ODP_KEY_ATTR_UDP:
case ODP_KEY_ATTR_ICMP: case ODP_KEY_ATTR_ICMP:
case ODP_KEY_ATTR_ICMPV6:
case ODP_KEY_ATTR_ARP: case ODP_KEY_ATTR_ARP:
case ODP_KEY_ATTR_ND:
return 0; return 0;
} }
@@ -831,6 +917,20 @@ int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
icmpv6_key = nla_data(nla); icmpv6_key = nla_data(nla);
icmpv6_key->icmpv6_type = ntohs(swkey->tp_src); icmpv6_key->icmpv6_type = ntohs(swkey->tp_src);
icmpv6_key->icmpv6_code = ntohs(swkey->tp_dst); icmpv6_key->icmpv6_code = ntohs(swkey->tp_dst);
if (icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_SOLICITATION
|| icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_ADVERTISEMENT) {
struct odp_key_nd *nd_key;
nla = nla_reserve(skb, ODP_KEY_ATTR_ND, sizeof(*nd_key));
if (!nla)
goto nla_put_failure;
nd_key = nla_data(nla);
memcpy(nd_key->nd_target, swkey->nd_target,
sizeof(nd_key->nd_target));
memcpy(nd_key->nd_sll, swkey->arp_sha, ETH_ALEN);
memcpy(nd_key->nd_tll, swkey->arp_tha, ETH_ALEN);
}
} }
} }

View File

@@ -41,6 +41,7 @@ struct sw_flow_key {
__be32 ipv6_dst[4]; /* IPv6 source address. */ __be32 ipv6_dst[4]; /* IPv6 source address. */
}; };
}; };
__be32 nd_target[4]; /* IPv6 ND target address. */
u16 in_port; /* Input switch port. */ u16 in_port; /* Input switch port. */
__be16 dl_tci; /* 0 if no VLAN, VLAN_TAG_PRESENT set otherwise. */ __be16 dl_tci; /* 0 if no VLAN, VLAN_TAG_PRESENT set otherwise. */
__be16 dl_type; /* Ethernet frame type. */ __be16 dl_type; /* Ethernet frame type. */
@@ -50,8 +51,8 @@ struct sw_flow_key {
u8 dl_dst[ETH_ALEN]; /* Ethernet destination address. */ u8 dl_dst[ETH_ALEN]; /* Ethernet destination address. */
u8 nw_proto; /* IP protocol or lower 8 bits of ARP opcode. */ u8 nw_proto; /* IP protocol or lower 8 bits of ARP opcode. */
u8 nw_tos; /* IP ToS (DSCP field, 6 bits). */ u8 nw_tos; /* IP ToS (DSCP field, 6 bits). */
u8 arp_sha[ETH_ALEN]; /* ARP source hardware address. */ u8 arp_sha[ETH_ALEN]; /* ARP/ND source hardware address. */
u8 arp_tha[ETH_ALEN]; /* ARP target hardware address. */ u8 arp_tha[ETH_ALEN]; /* ARP/ND target hardware address. */
}; };
struct sw_flow { struct sw_flow {

View File

@@ -390,6 +390,8 @@ OFP_ASSERT(sizeof(struct nx_action_pop_queue) == 16);
* - NXM_NX_ARP_THA * - NXM_NX_ARP_THA
* - NXM_NX_ICMPV6_TYPE * - NXM_NX_ICMPV6_TYPE
* - NXM_NX_ICMPV6_CODE * - NXM_NX_ICMPV6_CODE
* - NXM_NX_ND_SLL
* - NXM_NX_ND_TLL
* - NXM_NX_REG(idx) for idx in the switch's accepted range. * - NXM_NX_REG(idx) for idx in the switch's accepted range.
* *
* The following nxm_header values are potentially acceptable as 'dst': * The following nxm_header values are potentially acceptable as 'dst':
@@ -1078,6 +1080,44 @@ enum nx_mp_algorithm {
#define NXM_NX_ICMPV6_TYPE NXM_HEADER (0x0001, 21, 1) #define NXM_NX_ICMPV6_TYPE NXM_HEADER (0x0001, 21, 1)
#define NXM_NX_ICMPV6_CODE NXM_HEADER (0x0001, 22, 1) #define NXM_NX_ICMPV6_CODE NXM_HEADER (0x0001, 22, 1)
/* The target address in an IPv6 Neighbor Discovery message.
*
* Prereqs:
* NXM_OF_ETH_TYPE must match 0x86dd exactly.
* NXM_OF_IP_PROTO must match 58 exactly.
* NXM_OF_ICMPV6_TYPE must be either 135 or 136.
*
* Format: 128-bit IPv6 address.
*
* Masking: Not maskable. */
#define NXM_NX_ND_TARGET NXM_HEADER (0x0001, 23, 16)
/* The source link-layer address option in an IPv6 Neighbor Discovery
* message.
*
* Prereqs:
* NXM_OF_ETH_TYPE must match 0x86dd exactly.
* NXM_OF_IP_PROTO must match 58 exactly.
* NXM_OF_ICMPV6_TYPE must be exactly 135.
*
* Format: 48-bit Ethernet MAC address.
*
* Masking: Not maskable. */
#define NXM_NX_ND_SLL NXM_HEADER (0x0001, 24, 6)
/* The target link-layer address option in an IPv6 Neighbor Discovery
* message.
*
* Prereqs:
* NXM_OF_ETH_TYPE must match 0x86dd exactly.
* NXM_OF_IP_PROTO must match 58 exactly.
* NXM_OF_ICMPV6_TYPE must be exactly 136.
*
* Format: 48-bit Ethernet MAC address.
*
* Masking: Not maskable. */
#define NXM_NX_ND_TLL NXM_HEADER (0x0001, 25, 6)
/* ## --------------------- ## */ /* ## --------------------- ## */
/* ## Requests and replies. ## */ /* ## Requests and replies. ## */

View File

@@ -317,6 +317,7 @@ enum odp_key_type {
ODP_KEY_ATTR_ICMP, /* struct odp_key_icmp */ ODP_KEY_ATTR_ICMP, /* struct odp_key_icmp */
ODP_KEY_ATTR_ICMPV6, /* struct odp_key_icmpv6 */ ODP_KEY_ATTR_ICMPV6, /* struct odp_key_icmpv6 */
ODP_KEY_ATTR_ARP, /* struct odp_key_arp */ ODP_KEY_ATTR_ARP, /* struct odp_key_arp */
ODP_KEY_ATTR_ND, /* struct odp_key_nd */
__ODP_KEY_ATTR_MAX __ODP_KEY_ATTR_MAX
}; };
@@ -374,6 +375,12 @@ struct odp_key_arp {
uint8_t arp_tha[6]; uint8_t arp_tha[6];
}; };
struct odp_key_nd {
uint32_t nd_target[4];
uint8_t nd_sll[6];
uint8_t nd_tll[6];
};
/** /**
* enum odp_flow_attr - attributes for %ODP_FLOW_* commands. * enum odp_flow_attr - attributes for %ODP_FLOW_* commands.
* @ODP_FLOW_ATTR_KEY: Nested %ODP_KEY_ATTR_* attributes specifying the flow * @ODP_FLOW_ATTR_KEY: Nested %ODP_KEY_ATTR_* attributes specifying the flow

View File

@@ -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 /* Returns true if 'a' and 'b' have the same priority, wildcard the same
* fields, and have the same values for fixed fields, otherwise false. */ * fields, and have the same values for fixed fields, otherwise false. */
bool bool
@@ -586,7 +593,20 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
if (!(w & FWW_TP_DST)) { if (!(w & FWW_TP_DST)) {
ds_put_format(s, "icmp_code=%"PRIu16",", ntohs(f->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)) { if (!(w & FWW_TP_SRC)) {
ds_put_format(s, "tp_src=%"PRIu16",", ntohs(f->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; const flow_wildcards_t wc = wildcards->wildcards;
int i; 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++) { for (i = 0; i < FLOW_N_REGS; i++) {
if ((a->regs[i] ^ b->regs[i]) & wildcards->reg_masks[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, && ipv6_equal_except(&a->ipv6_src, &b->ipv6_src,
&wildcards->ipv6_src_mask) &wildcards->ipv6_src_mask)
&& ipv6_equal_except(&a->ipv6_dst, &b->ipv6_dst, && 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 static void
@@ -1122,7 +1144,7 @@ zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
const flow_wildcards_t wc = wildcards->wildcards; const flow_wildcards_t wc = wildcards->wildcards;
int i; 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++) { for (i = 0; i < FLOW_N_REGS; i++) {
flow->regs[i] &= wildcards->reg_masks[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); &wildcards->ipv6_src_mask);
flow->ipv6_dst = ipv6_addr_bitand(&flow->ipv6_dst, flow->ipv6_dst = ipv6_addr_bitand(&flow->ipv6_dst,
&wildcards->ipv6_dst_mask); &wildcards->ipv6_dst_mask);
if (wc & FWW_ND_TARGET) {
memset(&flow->nd_target, 0, sizeof flow->nd_target);
}
} }

View File

@@ -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 *); 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 *, bool cls_rule_set_ipv6_dst_masked(struct cls_rule *, const struct in6_addr *,
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 *); bool cls_rule_equal(const struct cls_rule *, const struct cls_rule *);

View File

@@ -230,6 +230,94 @@ parse_ipv6(struct ofpbuf *packet, struct flow *flow)
return nh_len; 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 'flow' members from 'packet', 'tun_id', and 'in_port.
* Initializes 'packet' header pointers as follows: * 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; packet->l7 = b.data;
} }
} else if (flow->nw_proto == IPPROTO_ICMPV6) { } else if (flow->nw_proto == IPPROTO_ICMPV6) {
const struct icmp6_hdr *icmp = pull_icmpv6(&b); int icmp_len = ntohs(nh->ip6_plen) + sizeof *nh - nh_len;
if (icmp) { if (parse_icmpv6(&b, flow, icmp_len)) {
flow->icmp_type = htons(icmp->icmp6_type);
flow->icmp_code = htons(icmp->icmp6_code);
packet->l7 = b.data; packet->l7 = b.data;
} }
} }

View File

@@ -54,19 +54,20 @@ struct flow {
uint8_t dl_dst[6]; /* Ethernet destination address. */ uint8_t dl_dst[6]; /* Ethernet destination address. */
uint8_t nw_proto; /* IP protocol or low 8 bits of ARP opcode. */ 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 nw_tos; /* IP ToS (DSCP field, 6 bits). */
uint8_t arp_sha[6]; /* ARP source hardware address. */ uint8_t arp_sha[6]; /* ARP/ND source hardware address. */
uint8_t arp_tha[6]; /* ARP target hardware address. */ uint8_t arp_tha[6]; /* ARP/ND target hardware address. */
struct in6_addr ipv6_src; /* IPv6 source address. */ struct in6_addr ipv6_src; /* IPv6 source address. */
struct in6_addr ipv6_dst; /* IPv6 destination 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. */ uint32_t reserved; /* Reserved for 64-bit packing. */
}; };
/* Assert that there are FLOW_SIG_SIZE bytes of significant data in "struct /* Assert that there are FLOW_SIG_SIZE bytes of significant data in "struct
* flow", followed by FLOW_PAD_SIZE bytes of padding. */ * 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 #define FLOW_PAD_SIZE 4
BUILD_ASSERT_DECL(offsetof(struct flow, ipv6_dst) == FLOW_SIG_SIZE - 16); BUILD_ASSERT_DECL(offsetof(struct flow, nd_target) == FLOW_SIG_SIZE - 16);
BUILD_ASSERT_DECL(sizeof(((struct flow *)0)->ipv6_dst) == 16); BUILD_ASSERT_DECL(sizeof(((struct flow *)0)->nd_target) == 16);
BUILD_ASSERT_DECL(sizeof(struct flow) == FLOW_SIG_SIZE + FLOW_PAD_SIZE); 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, 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 */ /* multicast bit only */
#define FWW_ARP_SHA ((OVS_FORCE flow_wildcards_t) (1 << 9)) #define FWW_ARP_SHA ((OVS_FORCE flow_wildcards_t) (1 << 9))
#define FWW_ARP_THA ((OVS_FORCE flow_wildcards_t) (1 << 10)) #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". /* Information on wildcards for a flow, as a supplement to "struct flow".
* *

View File

@@ -18,6 +18,8 @@
#include "nx-match.h" #include "nx-match.h"
#include <netinet/icmp6.h>
#include "classifier.h" #include "classifier.h"
#include "dynamic-string.h" #include "dynamic-string.h"
#include "ofp-util.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); flow->tp_dst = htons(*(uint8_t *) value);
return 0; 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. */ /* ARP header. */
case NFI_NXM_OF_ARP_OP: case NFI_NXM_OF_ARP_OP:
if (ntohs(get_unaligned_be16(value)) > 255) { 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)) { if (!(wc & FWW_TP_DST)) {
nxm_put_8(b, NXM_NX_ICMPV6_CODE, ntohs(flow->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; break;
} }
} }
@@ -1303,9 +1339,11 @@ nxm_read_field(const struct nxm_field *src, const struct flow *flow)
#endif #endif
case NFI_NXM_NX_ARP_SHA: case NFI_NXM_NX_ARP_SHA:
case NFI_NXM_NX_ND_SLL:
return eth_addr_to_uint64(flow->arp_sha); return eth_addr_to_uint64(flow->arp_sha);
case NFI_NXM_NX_ARP_THA: case NFI_NXM_NX_ARP_THA:
case NFI_NXM_NX_ND_TLL:
return eth_addr_to_uint64(flow->arp_tha); return eth_addr_to_uint64(flow->arp_tha);
case NFI_NXM_NX_TUN_ID_W: 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_SRC_W:
case NFI_NXM_NX_IPV6_DST: case NFI_NXM_NX_IPV6_DST:
case NFI_NXM_NX_IPV6_DST_W: case NFI_NXM_NX_IPV6_DST_W:
case NFI_NXM_NX_ND_TARGET:
case N_NXM_FIELDS: case N_NXM_FIELDS:
NOT_REACHED(); 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_IPV6_DST_W:
case NFI_NXM_NX_ICMPV6_TYPE: case NFI_NXM_NX_ICMPV6_TYPE:
case NFI_NXM_NX_ICMPV6_CODE: 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: case N_NXM_FIELDS:
NOT_REACHED(); NOT_REACHED();
} }

View File

@@ -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_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_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_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) DEFINE_FIELD_M(NX_REG0, 0, NXM_DL_NONE, 0, true)
#if FLOW_N_REGS >= 2 #if FLOW_N_REGS >= 2

View File

@@ -95,15 +95,17 @@ nxm_decode_n_bits(ovs_be16 ofs_nbits)
* NXM_OF_IP_PROTO 4 2 -- 6 * NXM_OF_IP_PROTO 4 2 -- 6
* NXM_OF_IPV6_SRC_W 4 16 16 36 * NXM_OF_IPV6_SRC_W 4 16 16 36
* NXM_OF_IPV6_DST_W 4 16 16 36 * NXM_OF_IPV6_DST_W 4 16 16 36
* NXM_OF_TCP_SRC 4 2 -- 6 * NXM_OF_ICMP_TYPE 4 1 -- 5
* NXM_OF_TCP_DST 4 2 -- 6 * 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(0) 4 4 4 12
* NXM_NX_REG_W(1) 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(2) 4 4 4 12
* NXM_NX_REG_W(3) 4 4 4 12 * NXM_NX_REG_W(3) 4 4 4 12
* NXM_NX_TUN_ID_W 4 8 8 20 * NXM_NX_TUN_ID_W 4 8 8 20
* ------------------------------------------- * -------------------------------------------
* total 209 * total 237
* *
* So this value is conservative. * So this value is conservative.
*/ */

View File

@@ -19,6 +19,7 @@
#include "odp-util.h" #include "odp-util.h"
#include <errno.h> #include <errno.h>
#include <inttypes.h> #include <inttypes.h>
#include <netinet/icmp6.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "byte-order.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_ICMP: return sizeof(struct odp_key_icmp);
case ODP_KEY_ATTR_ICMPV6: return sizeof(struct odp_key_icmpv6); 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_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_UNSPEC:
case __ODP_KEY_ATTR_MAX: 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_icmp *icmp_key;
const struct odp_key_icmpv6 *icmpv6_key; const struct odp_key_icmpv6 *icmpv6_key;
const struct odp_key_arp *arp_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))) { 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: ", 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)); ETH_ADDR_ARGS(arp_key->arp_tha));
break; 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: default:
format_generic_odp_key(a, ds); format_generic_odp_key(a, ds);
break; break;
@@ -466,6 +488,18 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow)
sizeof *icmpv6_key); sizeof *icmpv6_key);
icmpv6_key->icmpv6_type = ntohs(flow->tp_src); icmpv6_key->icmpv6_type = ntohs(flow->tp_src);
icmpv6_key->icmpv6_code = ntohs(flow->tp_dst); 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_icmp *icmp_key;
const struct odp_key_icmpv6 *icmpv6_key; const struct odp_key_icmpv6 *icmpv6_key;
const struct odp_key_arp *arp_key; const struct odp_key_arp *arp_key;
const struct odp_key_nd *nd_key;
uint16_t type = nl_attr_type(nla); uint16_t type = nl_attr_type(nla);
int len = odp_flow_key_attr_len(type); 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); memcpy(flow->arp_tha, arp_key->arp_tha, ETH_ADDR_LEN);
break; 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: default:
if (type == ODP_KEY_ATTR_UNSPEC if (type == ODP_KEY_ATTR_UNSPEC
|| prev_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; 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_TCP:
case ODP_KEY_ATTR_UDP: case ODP_KEY_ATTR_UDP:
case ODP_KEY_ATTR_ICMP: case ODP_KEY_ATTR_ICMP:
case ODP_KEY_ATTR_ICMPV6:
case ODP_KEY_ATTR_ARP: case ODP_KEY_ATTR_ARP:
case ODP_KEY_ATTR_ND:
return 0; return 0;
case __ODP_KEY_ATTR_MAX: case __ODP_KEY_ATTR_MAX:

View File

@@ -64,13 +64,13 @@ void format_odp_actions(struct ds *, const struct nlattr *odp_actions,
size_t actions_len); size_t actions_len);
/* By my calculations currently the longest valid nlattr-formatted flow key is /* 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 * 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 * to ensure proper 32-bit alignment for Netlink attributes. (An array of
* "struct nlattr" might not, in theory, be sufficiently aligned because it * "struct nlattr" might not, in theory, be sufficiently aligned because it
* only contains 16-bit types.) */ * 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) #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 *); void odp_flow_key_format(const struct nlattr *, size_t, struct ds *);

View File

@@ -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_SHA, "arp_sha", FWW_ARP_SHA) \
FIELD(F_ARP_THA, "arp_tha", FWW_ARP_THA) \ FIELD(F_ARP_THA, "arp_tha", FWW_ARP_THA) \
FIELD(F_IPV6_SRC, "ipv6_src", 0) \ 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 { enum field_index {
#define FIELD(ENUM, NAME, WILDCARD) ENUM, #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); cls_rule_set_ipv6_dst_masked(rule, &ipv6, &ipv6_mask);
break; 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: case N_FIELDS:
NOT_REACHED(); NOT_REACHED();
} }

View File

@@ -126,7 +126,7 @@ ofputil_cls_rule_from_match(const struct ofp_match *match,
wc->wildcards = ofpfw & WC_INVARIANTS; wc->wildcards = ofpfw & WC_INVARIANTS;
/* Wildcard fields that aren't defined by ofp_match or tun_id. */ /* 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) { if (ofpfw & OFPFW_NW_TOS) {
wc->wildcards |= FWW_NW_TOS; wc->wildcards |= FWW_NW_TOS;

View File

@@ -71,6 +71,9 @@ in_port=3 icmp6,ipv6_src=2001:db8:3c4d:1::1,icmp_type=134 actions=drop
udp dl_vlan_pcp=7 idle_timeout=5 actions=strip_vlan output:0 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 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 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_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
cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller
actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note
tun_id=0x1234,cookie=0x5678,actions=flood tun_id=0x1234,cookie=0x5678,actions=flood
@@ -92,6 +95,9 @@ NXT_FLOW_MOD: ADD icmp6,in_port=3,ipv6_src=2001:db8:3c4d:1::1,icmp_type=134 acti
NXT_FLOW_MOD: ADD udp,dl_vlan_pcp=7 idle:5 actions=strip_vlan,output:0 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 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 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_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 priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535 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 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 NXT_FLOW_MOD: ADD tun_id=0x1234 cookie:0x5678 actions=FLOOD
@@ -116,6 +122,9 @@ in_port=3 icmp6,ipv6_src=2001:db8:3c4d:1::1,icmp_type=134 actions=drop
udp dl_vlan_pcp=7 idle_timeout=5 actions=strip_vlan output:0 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 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 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_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
cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller
actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note
tun_id=0x1234,cookie=0x5678,actions=flood tun_id=0x1234,cookie=0x5678,actions=flood
@@ -137,6 +146,9 @@ NXT_FLOW_MOD: ADD NXM_OF_IN_PORT(0003), NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(2
NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_VLAN_TCI_W(f000/f000), NXM_OF_IP_PROTO(11) idle:5 actions=strip_vlan,output:0 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_VLAN_TCI_W(f000/f000), NXM_OF_IP_PROTO(11) idle:5 actions=strip_vlan,output:0
NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(c0a80003), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST(0050) actions=set_queue:37,output:1 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(c0a80003), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST(0050) actions=set_queue:37,output:1
NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(c0a80003), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST(0035) actions=pop_queue,output:1 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(c0a80003), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST(0035) actions=pop_queue,output:1
NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(87), NXM_NX_ND_TARGET(fec000001234f0458fff1111fe4e0571) actions=drop
NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(87), NXM_NX_ND_SLL(000ae4256bb0) actions=drop
NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(88), NXM_NX_ND_TARGET(fec000001234f0458fff1111fe4e0571), NXM_NX_ND_TLL(000ae4256bb1) actions=drop
NXT_FLOW_MOD: ADD <any> cookie:0x123456789abcdef hard:10 pri:60000 actions=CONTROLLER:65535 NXT_FLOW_MOD: ADD <any> cookie:0x123456789abcdef hard:10 pri:60000 actions=CONTROLLER:65535
NXT_FLOW_MOD: ADD <any> 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 <any> 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 NXM_NX_TUN_ID(0000000000001234) cookie:0x5678 actions=FLOOD NXT_FLOW_MOD: ADD NXM_NX_TUN_ID(0000000000001234) cookie:0x5678 actions=FLOOD
@@ -267,6 +279,18 @@ NXM_OF_ETH_TYPE(0800) NXM_NX_IPV6_DST(20010db83c4d00010002000300040005)
NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000) NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
NXM_OF_ETH_TYPE(0800) NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000) NXM_OF_ETH_TYPE(0800) NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
# ND source hardware address
NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_NX_ND_SLL(0002e30f80a4)
NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(88) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_NX_ND_SLL(0002e30f80a4)
NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3b) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_NX_ND_SLL(0002e30f80a4)
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_NX_ND_SLL(0002e30f80a4)
# ND destination hardware address
NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(88) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_NX_ND_TLL(0002e30f80a4)
NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_NX_ND_TLL(0002e30f80a4)
NXM_OF_ETH_TYPE(86dd) NXM_OF_IP_PROTO(3b) NXM_NX_ICMPV6_TYPE(87) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_NX_ND_TLL(0002e30f80a4)
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(3a) NXM_NX_ICMPV6_TYPE(88) NXM_NX_ND_TARGET(20010db83c4d00010002000300040005) NXM_NX_ND_TLL(0002e30f80a4)
# Tunnel ID. # Tunnel ID.
NXM_NX_TUN_ID(00000000abcdef01) NXM_NX_TUN_ID(00000000abcdef01)
NXM_NX_TUN_ID_W(84200000abcdef01/84200000FFFFFFFF) NXM_NX_TUN_ID_W(84200000abcdef01/84200000FFFFFFFF)
@@ -407,6 +431,18 @@ nx_pull_match() returned error 44010104
NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000) NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
nx_pull_match() returned error 44010104 nx_pull_match() returned error 44010104
# ND source hardware address
NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(87), NXM_NX_ND_TARGET(20010db83c4d00010002000300040005), NXM_NX_ND_SLL(0002e30f80a4)
nx_pull_match() returned error 44010104
nx_pull_match() returned error 44010104
nx_pull_match() returned error 44010104
# ND destination hardware address
NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(88), NXM_NX_ND_TARGET(20010db83c4d00010002000300040005), NXM_NX_ND_TLL(0002e30f80a4)
nx_pull_match() returned error 44010104
nx_pull_match() returned error 44010104
nx_pull_match() returned error 44010104
# Tunnel ID. # Tunnel ID.
NXM_NX_TUN_ID(00000000abcdef01) NXM_NX_TUN_ID(00000000abcdef01)
NXM_NX_TUN_ID_W(84200000abcdef01/84200000ffffffff) NXM_NX_TUN_ID_W(84200000abcdef01/84200000ffffffff)

View File

@@ -388,6 +388,24 @@ groups of 16-bits of zeros. The optional \fInetmask\fR allows
restricting a match to an IPv6 address prefix. A netmask is specified restricting a match to an IPv6 address prefix. A netmask is specified
as a CIDR block (e.g. \fB2001:db8:3c4d:1::/64\fR). as a CIDR block (e.g. \fB2001:db8:3c4d:1::/64\fR).
. .
.IP \fBnd_target=\fIipv6\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
\fBipv6_src\fR and \fBipv6_dst\fR fields.
.
.IP \fBnd_sll=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR
When \fBdl_type\fR, \fBnw_proto\fR, and \fBicmp_type\fR specify IPv6
Neighbor Solicitation (ICMPv6 type 135), matches the source link\-layer
address option. An address is specified as 6 pairs of hexadecimal
digits delimited by colons.
.
.IP \fBnd_tll=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR
When \fBdl_type\fR, \fBnw_proto\fR, and \fBicmp_type\fR specify IPv6
Neighbor Advertisement (ICMPv6 type 136), matches the target link\-layer
address option. An address is specified as 6 pairs of hexadecimal
digits delimited by colons.
.
.IP \fBtun_id=\fItunnel-id\fR[\fB/\fImask\fR] .IP \fBtun_id=\fItunnel-id\fR[\fB/\fImask\fR]
Matches tunnel identifier \fItunnel-id\fR. Only packets that arrive Matches tunnel identifier \fItunnel-id\fR. Only packets that arrive
over a tunnel that carries a key (e.g. GRE with the RFC 2890 key over a tunnel that carries a key (e.g. GRE with the RFC 2890 key