[ovs-dev] [IPv6 6/7] nicira-ext: Support matching IPv6 Neighbor Discovery messages.

Justin Pettit jpettit at nicira.com
Fri Jan 21 12:27:42 UTC 2011


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 at nicira.com>
---
 datapath/flow.c                         |   62 +++++++++++++++++++++++++---
 include/openflow/nicira-ext.h           |   38 +++++++++++++++++
 include/openvswitch/datapath-protocol.h |    3 +
 lib/classifier.c                        |   33 +++++++++++++--
 lib/classifier.h                        |    1 +
 lib/flow.c                              |   69 ++++++++++++++++++++++++++++--
 lib/flow.h                              |   14 ++++---
 lib/nx-match.c                          |   42 +++++++++++++++++++
 lib/nx-match.def                        |    3 +
 lib/nx-match.h                          |    8 ++-
 lib/odp-util.c                          |   42 +++++++++++++++++--
 lib/ofp-parse.c                         |   20 ++++++++-
 lib/ofp-util.c                          |    2 +-
 tests/ovs-ofctl.at                      |   12 +++++
 utilities/ovs-ofctl.8.in                |   18 ++++++++
 15 files changed, 337 insertions(+), 30 deletions(-)

diff --git a/datapath/flow.c b/datapath/flow.c
index c0de868..e08ee33 100644
--- a/datapath/flow.c
+++ b/datapath/flow.c
@@ -31,6 +31,7 @@
 #include <net/inet_ecn.h>
 #include <net/ip.h>
 #include <net/ipv6.h>
+#include <net/ndisc.h>
 
 static struct kmem_cache *flow_cache;
 static unsigned int hash_seed __read_mostly;
@@ -346,6 +347,56 @@ static __be16 parse_ethertype(struct sk_buff *skb)
 	return llc->ethertype;
 }
 
+static int parse_icmpv6(struct sk_buff *skb, struct odp_flow_key *key,
+		int icmp_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_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 (skb->len < skb_transport_offset(skb) + icmp_len)
+			return -EINVAL;
+		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 (nd_opt->nd_opt_type == ND_OPT_SOURCE_LL_ADDR
+					&& opt_len == 8) {
+				memcpy(&key->nd_sll, &nd->opt[offset+2], ETH_ALEN);
+			} else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LL_ADDR
+					&& opt_len == 8) {
+				memcpy(&key->nd_tll, &nd->opt[offset+2], ETH_ALEN);
+			}
+
+			if (!opt_len || (opt_len > icmp_len))
+				return -EINVAL;
+
+			icmp_len -= opt_len;
+			offset += opt_len;
+		}
+	}
+
+	return 0;
+}
+
 /**
  * flow_extract - extracts a flow key from an Ethernet frame.
  * @skb: sk_buff that contains the frame, with skb->data pointing to the
@@ -526,12 +577,11 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct odp_flow_key *key,
 			}
 		} else if (key->nw_proto == NEXTHDR_ICMP) {
 			if (icmp6hdr_ok(skb)) {
-				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);
+				int icmp_len = ntohs(nh->payload_len) + sizeof *nh - nh_len;
+				int error = parse_icmpv6(skb, key, icmp_len);
+
+				if (error < 0)
+					return error;
 			}
 		}
 	}
diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
index 87804bd..818a6e9 100644
--- a/include/openflow/nicira-ext.h
+++ b/include/openflow/nicira-ext.h
@@ -1074,6 +1074,44 @@ enum nx_mp_algorithm {
 #define NXM_NX_ICMPV6_TYPE NXM_HEADER  (0x0001, 21, 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. ## */
diff --git a/include/openvswitch/datapath-protocol.h b/include/openvswitch/datapath-protocol.h
index c3fc570..392bce8 100644
--- a/include/openvswitch/datapath-protocol.h
+++ b/include/openvswitch/datapath-protocol.h
@@ -237,6 +237,9 @@ struct odp_flow_key {
     uint8_t  arp_tha[6];        /* ARP target hardware address. */
     uint8_t  ipv6_src[16];      /* IPv6 source address. */
     uint8_t  ipv6_dst[16];      /* IPv6 destination address. */
+    uint8_t  nd_target[16];     /* IPv6 ND target address */
+    uint8_t  nd_sll[6];         /* IPv6 ND source link layer address */
+    uint8_t  nd_tll[6];         /* IPv6 ND target link layer address */
 };
 
 /* Flags for ODP_FLOW. */
diff --git a/lib/classifier.c b/lib/classifier.c
index fe54394..1ab404b 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_masked(&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 c82a484..d3121bf 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 *);
 
diff --git a/lib/flow.c b/lib/flow.c
index 76f5d41..5fc82c6 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -229,6 +229,67 @@ parse_ethertype(struct ofpbuf *b)
     return llc->snap.snap_type;
 }
 
+/* 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 int
+parse_icmpv6(struct ofpbuf *b, struct flow *flow, int icmp_len)
+{
+    const struct icmp6_hdr *icmp = pull_icmpv6(b);
+    if (!icmp) {
+        return 1;
+    }
+
+    flow->icmp_type = htons(icmp->icmp6_type);
+    flow->icmp_code = htons(icmp->icmp6_code);
+
+    if ((icmp->icmp6_type == ND_NEIGHBOR_SOLICIT)
+        || (icmp->icmp6_type == ND_NEIGHBOR_ADVERT)) {
+        struct nd_neighbor_solicit *nd_ns;  /* Identical to ND advert */
+
+        nd_ns = (struct nd_neighbor_solicit *)icmp;
+        if (!ofpbuf_try_pull(b, sizeof *nd_ns - sizeof *icmp)) {
+            return 1;
+        }
+        flow->nd_target = nd_ns->nd_ns_target;
+
+        icmp_len -= sizeof (*nd_ns);
+        while (icmp_len >= 8) {
+            struct nd_opt_hdr *nd_opt;
+            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_try_pull(b, 8);
+            if (!nd_opt || !nd_opt->nd_opt_len
+                    || nd_opt->nd_opt_len * 8 > icmp_len) {
+                return 1;
+            }
+            data = (const uint8_t *)(nd_opt + 1);
+
+            if (nd_opt->nd_opt_type == ND_OPT_SOURCE_LINKADDR
+                    && nd_opt->nd_opt_len == 1) {
+                memcpy(&flow->arp_sha, data, ETH_ADDR_LEN);
+            } else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LINKADDR
+                    && nd_opt->nd_opt_len == 1) {
+                memcpy(&flow->arp_tha, data, ETH_ADDR_LEN);
+            }
+
+            /* Pull the rest of this option. */
+            if (!ofpbuf_try_pull(b, nd_opt->nd_opt_len * 8 - 8)) {
+                return 1;
+            }
+
+            icmp_len -= nd_opt->nd_opt_len * 8;
+        }
+    }
+
+    return 0;
+}
+
 /* Initializes 'flow' members from 'packet', 'tun_id', and 'in_port.
  * Initializes 'packet' header pointers as follows:
  *
@@ -318,7 +379,7 @@ flow_extract(struct ofpbuf *packet, ovs_be64 tun_id, uint16_t in_port,
         }
     } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
         uint8_t nexthdr = 0;
-        int nh_len;
+        int nh_len = 0;
         const struct ip6_hdr *nh = pull_ipv6(&b, &nh_len, &nexthdr);
         if (nh) {
             flow->ipv6_src = nh->ip6_src;
@@ -342,10 +403,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;
                 }
             }
diff --git a/lib/flow.h b/lib/flow.h
index dcfa3dd..f51363e 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -49,19 +49,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,
@@ -117,7 +118,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 08e85f9..3b2c920 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"
@@ -353,6 +355,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 != CONSTANT_HTONS(ND_NEIGHBOR_SOLICIT)) 
+                    && (flow->tp_src != CONSTANT_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 != CONSTANT_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 != CONSTANT_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) {
@@ -806,6 +832,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;
             }
         }
@@ -1294,9 +1330,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:
@@ -1310,6 +1348,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();
     }
@@ -1383,6 +1422,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 ec88c35..ba81140 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 aefcb65..a76ad1f 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 e3c8aaa..c1dcd72 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -64,6 +64,20 @@ format_odp_flow_key(struct ds *ds, const struct odp_flow_key *key)
         ds_put_format(ds, " port%"PRIu16"->%"PRIu16,
                   ntohs(key->tp_src), ntohs(key->tp_dst));
     }
+    if (memcmp(key->nd_target, &in6addr_any, sizeof key->nd_target)) {
+        char target[INET6_ADDRSTRLEN];
+
+        inet_ntop(AF_INET6, key->nd_target, target, INET6_ADDRSTRLEN);
+        ds_put_format(ds, " ndtarget:%s", target);
+    }
+
+    if (!eth_addr_is_zero(key->nd_sll) 
+            || !eth_addr_is_zero(key->nd_tll)) {
+        ds_put_format(ds, " ha"ETH_ADDR_FMT"->"ETH_ADDR_FMT,
+                ETH_ADDR_ARGS(key->nd_sll),
+                ETH_ADDR_ARGS(key->nd_tll));
+    }
+
     if (!eth_addr_is_zero(key->arp_sha) || !eth_addr_is_zero(key->arp_tha)) {
         ds_put_format(ds, " ha"ETH_ADDR_FMT"->"ETH_ADDR_FMT,
                 ETH_ADDR_ARGS(key->arp_sha),
@@ -284,8 +298,21 @@ odp_flow_key_from_flow(struct odp_flow_key *key, const struct flow *flow)
     memcpy(key->dl_dst, flow->dl_dst, ETH_ADDR_LEN);
     key->nw_proto = flow->nw_proto;
     key->nw_tos = flow->nw_tos;
-    memcpy(key->arp_sha, flow->arp_sha, ETH_ADDR_LEN);
-    memcpy(key->arp_tha, flow->arp_tha, ETH_ADDR_LEN);
+    if (flow->dl_type == CONSTANT_HTONS(ETH_TYPE_IPV6)) {
+        memcpy(key->nd_sll, flow->arp_sha, ETH_ADDR_LEN);
+        memcpy(key->nd_tll, flow->arp_tha, ETH_ADDR_LEN);
+        memcpy(key->nd_target, &flow->nd_target, sizeof key->nd_target);
+        /* Zero-out unused fields */
+        memset(key->arp_sha, 0, sizeof(key->arp_sha));
+        memset(key->arp_tha, 0, sizeof(key->arp_tha));
+    } else {
+        memcpy(key->arp_sha, flow->arp_sha, ETH_ADDR_LEN);
+        memcpy(key->arp_tha, flow->arp_tha, ETH_ADDR_LEN);
+        /* Zero-out unused fields */
+        memset(key->nd_sll, 0, sizeof(key->nd_sll));
+        memset(key->nd_tll, 0, sizeof(key->nd_tll));
+        memset(key->nd_target, 0, sizeof(key->nd_target));
+    }
     memcpy(key->ipv6_src, &flow->ipv6_src, sizeof key->ipv6_src);
     memcpy(key->ipv6_dst, &flow->ipv6_dst, sizeof key->ipv6_dst);
 }
@@ -306,8 +333,15 @@ odp_flow_key_to_flow(const struct odp_flow_key *key, struct flow *flow)
     memcpy(flow->dl_dst, key->dl_dst, ETH_ADDR_LEN);
     flow->nw_proto = key->nw_proto;
     flow->nw_tos = key->nw_tos;
-    memcpy(flow->arp_sha, key->arp_sha, ETH_ADDR_LEN);
-    memcpy(flow->arp_tha, key->arp_tha, ETH_ADDR_LEN);
+    if (flow->dl_type == CONSTANT_HTONS(ETH_TYPE_IPV6)) {
+        memcpy(flow->arp_sha, key->nd_sll, ETH_ADDR_LEN);
+        memcpy(flow->arp_tha, key->nd_tll, ETH_ADDR_LEN);
+        memcpy(&flow->nd_target, key->nd_target, sizeof(flow->nd_target));
+    } else {
+        memcpy(flow->arp_sha, key->arp_sha, ETH_ADDR_LEN);
+        memcpy(flow->arp_tha, key->arp_tha, ETH_ADDR_LEN);
+        memset(&flow->nd_target, 0, sizeof(flow->nd_target));
+    }
     memcpy(&flow->ipv6_src, key->ipv6_src, sizeof flow->ipv6_src);
     memcpy(&flow->ipv6_dst, key->ipv6_dst, sizeof flow->ipv6_dst);
 }
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index af6ed67..5383194 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -541,7 +541,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,
@@ -678,6 +681,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 c5b5c99..5846bff 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;
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index 45c7867..1a1d540 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -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
 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_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
 actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note
 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 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_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 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
@@ -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
 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_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
 actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note
 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_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(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> 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
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index e1e5dd5..bbec3df 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -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
 as a CIDR block (e.g. \fB2001:db8:3c4d:1::/64\fR).
 .
+.IP \fBnd_target=\fIip\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
+\fIip\fR.  \fIip\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]
 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
-- 
1.7.1





More information about the dev mailing list