nicira-ext: Support matching IPv6 Neighbor Discovery messages.
[sliver-openvswitch.git] / lib / classifier.c
index 53280e5..fcc965c 100644 (file)
@@ -319,6 +319,63 @@ cls_rule_set_icmp_code(struct cls_rule *rule, uint8_t icmp_code)
     rule->flow.icmp_code = htons(icmp_code);
 }
 
+void
+cls_rule_set_arp_sha(struct cls_rule *rule, const uint8_t sha[ETH_ADDR_LEN])
+{
+    rule->wc.wildcards &= ~FWW_ARP_SHA;
+    memcpy(rule->flow.arp_sha, sha, ETH_ADDR_LEN);
+}
+
+void
+cls_rule_set_arp_tha(struct cls_rule *rule, const uint8_t tha[ETH_ADDR_LEN])
+{
+    rule->wc.wildcards &= ~FWW_ARP_THA;
+    memcpy(rule->flow.arp_tha, tha, ETH_ADDR_LEN);
+}
+
+void
+cls_rule_set_ipv6_src(struct cls_rule *rule, const struct in6_addr *src)
+{
+    cls_rule_set_ipv6_src_masked(rule, src, &in6addr_exact);
+}
+
+bool
+cls_rule_set_ipv6_src_masked(struct cls_rule *rule, const struct in6_addr *src,
+                             const struct in6_addr *mask)
+{
+    if (flow_wildcards_set_ipv6_src_mask(&rule->wc, mask)) {
+        rule->flow.ipv6_src = ipv6_addr_bitand(src, mask);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+void
+cls_rule_set_ipv6_dst(struct cls_rule *rule, const struct in6_addr *dst)
+{
+    cls_rule_set_ipv6_dst_masked(rule, dst, &in6addr_exact);
+}
+
+bool
+cls_rule_set_ipv6_dst_masked(struct cls_rule *rule, const struct in6_addr *dst,
+                             const struct in6_addr *mask)
+{
+    if (flow_wildcards_set_ipv6_dst_mask(&rule->wc, mask)) {
+        rule->flow.ipv6_dst = ipv6_addr_bitand(dst, mask);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+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
@@ -347,6 +404,27 @@ format_ip_netmask(struct ds *s, const char *name, ovs_be32 ip,
     }
 }
 
+static void
+format_ipv6_netmask(struct ds *s, const char *name,
+                    const struct in6_addr *addr,
+                    const struct in6_addr *netmask)
+{
+    if (!ipv6_mask_is_any(netmask)) {
+        ds_put_format(s, "%s=", name);
+        print_ipv6_addr(s, addr);
+        if (!ipv6_mask_is_exact(netmask)) {
+            if (ipv6_is_cidr(netmask)) {
+                int cidr_bits = ipv6_count_cidr_bits(netmask);
+                ds_put_format(s, "/%d", cidr_bits);
+            } else {
+                ds_put_char(s, '/');
+                print_ipv6_addr(s, netmask);
+            }
+        }
+        ds_put_char(s, ',');
+    }
+}
+
 void
 cls_rule_format(const struct cls_rule *rule, struct ds *s)
 {
@@ -381,6 +459,22 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
             } else {
                 ds_put_cstr(s, "ip,");
             }
+        } else if (f->dl_type == htons(ETH_TYPE_IPV6)) {
+            if (!(w & FWW_NW_PROTO)) {
+                skip_proto = true;
+                if (f->nw_proto == IPPROTO_ICMPV6) {
+                    ds_put_cstr(s, "icmp6,");
+                } else if (f->nw_proto == IPPROTO_TCP) {
+                    ds_put_cstr(s, "tcp6,");
+                } else if (f->nw_proto == IPPROTO_UDP) {
+                    ds_put_cstr(s, "udp6,");
+                } else {
+                    ds_put_cstr(s, "ipv6,");
+                    skip_proto = false;
+                }
+            } else {
+                ds_put_cstr(s, "ipv6,");
+            }
         } else if (f->dl_type == htons(ETH_TYPE_ARP)) {
             ds_put_cstr(s, "arp,");
         } else {
@@ -458,8 +552,13 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
     if (!skip_type && !(w & FWW_DL_TYPE)) {
         ds_put_format(s, "dl_type=0x%04"PRIx16",", ntohs(f->dl_type));
     }
-    format_ip_netmask(s, "nw_src", f->nw_src, wc->nw_src_mask);
-    format_ip_netmask(s, "nw_dst", f->nw_dst, wc->nw_dst_mask);
+    if (f->dl_type == htons(ETH_TYPE_IPV6)) {
+        format_ipv6_netmask(s, "ipv6_src", &f->ipv6_src, &wc->ipv6_src_mask);
+        format_ipv6_netmask(s, "ipv6_dst", &f->ipv6_dst, &wc->ipv6_dst_mask);
+    } else {
+        format_ip_netmask(s, "nw_src", f->nw_src, wc->nw_src_mask);
+        format_ip_netmask(s, "nw_dst", f->nw_dst, wc->nw_dst_mask);
+    }
     if (!skip_proto && !(w & FWW_NW_PROTO)) {
         if (f->dl_type == htons(ETH_TYPE_ARP)) {
             ds_put_format(s, "opcode=%"PRIu8",", f->nw_proto);
@@ -467,6 +566,16 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
             ds_put_format(s, "nw_proto=%"PRIu8",", f->nw_proto);
         }
     }
+    if (f->dl_type == htons(ETH_TYPE_ARP)) {
+        if (!(w & FWW_ARP_SHA)) {
+            ds_put_format(s, "arp_sha="ETH_ADDR_FMT",",
+                    ETH_ADDR_ARGS(f->arp_sha));
+        }
+        if (!(w & FWW_ARP_THA)) {
+            ds_put_format(s, "arp_tha="ETH_ADDR_FMT",",
+                    ETH_ADDR_ARGS(f->arp_tha));
+        }
+    }
     if (!(w & FWW_NW_TOS)) {
         ds_put_format(s, "nw_tos=%"PRIu8",", f->nw_tos);
     }
@@ -477,7 +586,27 @@ 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 {
+    } else if (f->nw_proto == IPPROTO_ICMPV6) {
+        if (!(w & FWW_TP_SRC)) {
+            ds_put_format(s, "icmp_type=%"PRIu16",", ntohs(f->tp_src));
+        }
+        if (!(w & FWW_TP_DST)) {
+            ds_put_format(s, "icmp_code=%"PRIu16",", ntohs(f->tp_dst));
+        }
+        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));
         }
@@ -940,6 +1069,30 @@ next_rule_in_list(struct cls_rule *rule)
     return next->priority < rule->priority ? next : NULL;
 }
 
+static bool
+ipv6_equal_except(const struct in6_addr *a, const struct in6_addr *b,
+                  const struct in6_addr *mask)
+{
+    int i;
+
+#ifdef s6_addr32
+    for (i=0; i<4; i++) {
+        if ((a->s6_addr32[i] ^ b->s6_addr32[i]) & mask->s6_addr32[i]) {
+            return false;
+        }
+    }
+#else
+    for (i=0; i<16; i++) {
+        if ((a->s6_addr[i] ^ b->s6_addr[i]) & mask->s6_addr[i]) {
+            return false;
+        }
+    }
+#endif
+
+    return true;
+}
+
+
 static bool
 flow_equal_except(const struct flow *a, const struct flow *b,
                   const struct flow_wildcards *wildcards)
@@ -947,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 == 40 + 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]) {
@@ -974,7 +1127,15 @@ flow_equal_except(const struct flow *a, const struct flow *b,
             && (wc & FWW_ETH_MCAST
                 || !((a->dl_dst[0] ^ b->dl_dst[0]) & 0x01))
             && (wc & FWW_NW_PROTO || a->nw_proto == b->nw_proto)
-            && (wc & FWW_NW_TOS || a->nw_tos == b->nw_tos));
+            && (wc & FWW_NW_TOS || a->nw_tos == b->nw_tos)
+            && (wc & FWW_ARP_SHA || eth_addr_equals(a->arp_sha, b->arp_sha))
+            && (wc & FWW_ARP_THA || eth_addr_equals(a->arp_tha, b->arp_tha))
+            && 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)
+            && (wc & FWW_ND_TARGET 
+                || ipv6_addr_equals(&a->nd_target, &b->nd_target)));
 }
 
 static void
@@ -983,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 == 40 + 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];
@@ -1020,4 +1181,17 @@ zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
     if (wc & FWW_NW_TOS) {
         flow->nw_tos = 0;
     }
+    if (wc & FWW_ARP_SHA) {
+        memset(flow->arp_sha, 0, sizeof flow->arp_sha);
+    }
+    if (wc & FWW_ARP_THA) {
+        memset(flow->arp_tha, 0, sizeof flow->arp_tha);
+    }
+    flow->ipv6_src = ipv6_addr_bitand(&flow->ipv6_src,
+            &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);
+    }
 }