nicira-ext: Support matching IPv6 traffic.
[sliver-openvswitch.git] / lib / classifier.c
index a4532a6..658be80 100644 (file)
@@ -333,6 +333,42 @@ cls_rule_set_arp_tha(struct cls_rule *rule, const uint8_t tha[ETH_ADDR_LEN])
     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;
+    }
+}
+
 /* 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
@@ -361,6 +397,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)
 {
@@ -395,6 +452,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 {
@@ -472,8 +545,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);
@@ -501,6 +579,13 @@ 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 (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));
+        }
     } else {
         if (!(w & FWW_TP_SRC)) {
             ds_put_format(s, "tp_src=%"PRIu16",", ntohs(f->tp_src));
@@ -964,6 +1049,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)
@@ -971,7 +1080,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 == 52 + FLOW_N_REGS * 4);
+    BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 84 + FLOW_N_REGS * 4);
 
     for (i = 0; i < FLOW_N_REGS; i++) {
         if ((a->regs[i] ^ b->regs[i]) & wildcards->reg_masks[i]) {
@@ -1000,7 +1109,11 @@ flow_equal_except(const struct flow *a, const struct flow *b,
             && (wc & FWW_NW_PROTO || a->nw_proto == b->nw_proto)
             && (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)));
+            && (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));
 }
 
 static void
@@ -1009,7 +1122,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 == 52 + 4 * FLOW_N_REGS);
+    BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 84 + 4 * FLOW_N_REGS);
 
     for (i = 0; i < FLOW_N_REGS; i++) {
         flow->regs[i] &= wildcards->reg_masks[i];
@@ -1052,4 +1165,8 @@ zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
     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);
 }