WARNING: This patch *only* compiles! NOT tested yet!

Based on patch 1/2, this patch adds IPv6 support.

Any comments are welcome!

Cc: Ben Pfaff <b...@nicira.com>
Cc: Jesse Gross <je...@nicira.com>
Not-Yet-Signed-off-by: Cong Wang <amw...@redhat.com>

---
 lib/flow.c             |   18 +++++++++---
 lib/flow.h             |   12 +++++++-
 lib/igmp-snooping.c    |   70 ++++++++++++++++++++++++++++++++++++++---------
 lib/igmp-snooping.h    |   11 +++++---
 lib/packets.h          |   11 +++++++
 ofproto/ofproto-dpif.c |   64 ++++++++++++++++++++++++++++++++-----------
 6 files changed, 144 insertions(+), 42 deletions(-)

diff --git a/lib/flow.c b/lib/flow.c
index 9cca7d9..cc515aa 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -286,9 +286,11 @@ parse_icmpv6(struct ofpbuf *b, struct flow *flow)
     flow->tp_src = htons(icmp->icmp6_type);
     flow->tp_dst = htons(icmp->icmp6_code);
 
-    if (icmp->icmp6_code == 0 &&
-        (icmp->icmp6_type == ND_NEIGHBOR_SOLICIT ||
-         icmp->icmp6_type == ND_NEIGHBOR_ADVERT)) {
+    if (icmp->icmp6_code != 0)
+        return true;
+
+    if (icmp->icmp6_type == ND_NEIGHBOR_SOLICIT ||
+        icmp->icmp6_type == ND_NEIGHBOR_ADVERT) {
         const struct in6_addr *nd_target;
 
         nd_target = ofpbuf_try_pull(b, sizeof *nd_target);
@@ -330,6 +332,12 @@ parse_icmpv6(struct ofpbuf *b, struct flow *flow)
                 goto invalid;
             }
         }
+    } else if (icmp->icmp6_type == ICMPV6_MGM_REPORT ||
+              icmp->icmp6_type == ICMPV6_MGM_REDUCTION) {
+        const struct mld_hdr *mld = b->data;
+        if (b->size < sizeof(*mld))
+            goto invalid;
+        flow->igmp_group.u.ip6 = mld->mld_addr;
     }
 
     return true;
@@ -471,7 +479,7 @@ flow_extract_l3_onwards(struct ofpbuf *packet, struct flow 
*flow,
                     if (igmp) {
                         flow->tp_src = htons(igmp->igmp_type);
                         flow->tp_dst = htons(igmp->igmp_code);
-                        flow->igmp_group = htons(igmp->group);
+                        flow->igmp_group.u.ip4 = htons(igmp->group);
                     }
                 }
             }
@@ -944,7 +952,7 @@ flow_compose(struct ofpbuf *b, const struct flow *flow)
                 b->l4 = igmp = ofpbuf_put_zeros(b, sizeof *igmp);
                 igmp->igmp_type = ntohs(flow->tp_src);
                 igmp->igmp_code = ntohs(flow->tp_dst);
-                igmp->group = ntohs(flow->igmp_group);
+                igmp->group = ntohs(flow->igmp_group.u.ip4);
                 igmp->igmp_csum = csum(igmp, IGMP_HEADER_LEN);
             }
         }
diff --git a/lib/flow.h b/lib/flow.h
index 4b540f9..3276a3c 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -59,6 +59,14 @@ BUILD_ASSERT_DECL(FLOW_NW_FRAG_LATER == NX_IP_FRAG_LATER);
 
 const char *flow_tun_flag_to_string(uint32_t flags);
 
+struct mdb_ip {
+    union {
+        ovs_be32 ip4;
+        struct in6_addr ip6;
+    } u;
+    uint8_t proto;
+};
+
 struct flow_tnl {
     ovs_be64 tun_id;
     ovs_be32 ip_src;
@@ -98,7 +106,7 @@ struct flow {
     ovs_be16 encap_dl_type;     /* MPLS encapsulated Ethernet frame type */
     ovs_be16 tp_src;            /* TCP/UDP source port. */
     ovs_be16 tp_dst;            /* TCP/UDP destination port. */
-    ovs_be32 igmp_group;        /* IGMP multicast group. */
+    struct mdb_ip igmp_group;   /* IGMP multicast group. */
     uint8_t dl_src[6];          /* Ethernet source address. */
     uint8_t dl_dst[6];          /* Ethernet destination address. */
     uint8_t nw_proto;           /* IP protocol or low 8 bits of ARP opcode. */
@@ -114,7 +122,7 @@ BUILD_ASSERT_DECL(sizeof(struct flow) % 4 == 0);
 #define FLOW_U32S (sizeof(struct flow) / 4)
 
 /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
-BUILD_ASSERT_DECL(sizeof(struct flow) == sizeof(struct flow_tnl) + 168 &&
+BUILD_ASSERT_DECL(sizeof(struct flow) == sizeof(struct flow_tnl) + 184 &&
                   FLOW_WC_SEQ == 19);
 
 /* Represents the metadata fields of struct flow. */
diff --git a/lib/igmp-snooping.c b/lib/igmp-snooping.c
index 3e96b38..22185ec 100644
--- a/lib/igmp-snooping.c
+++ b/lib/igmp-snooping.c
@@ -46,10 +46,17 @@ mdb_entry_age(const struct igmp_mdb *mdb, const struct 
mdb_entry *e)
 }
 
 static uint32_t
-igmp_mdb_hash(const struct igmp_mdb *mdb, ovs_be32 grp,
+igmp_mdb_hash(const struct igmp_mdb *mdb, struct mdb_ip *grp,
                uint16_t vlan)
 {
-    return hash_3words(grp, vlan, mdb->secret);
+    if (grp->proto == htons(ETH_TYPE_IP)) {
+        return hash_3words(grp->u.ip4, vlan, mdb->secret);
+    } else {
+        uint32_t tmp[sizeof(*grp)/sizeof(uint32_t) + 1] = {0};
+        memcpy(tmp, grp, sizeof(*grp));
+        tmp[sizeof(tmp)/sizeof(uint32_t) - 1] =  vlan;
+        return hash_words(tmp, sizeof(tmp)/sizeof(uint32_t), mdb->secret);
+    }
 }
 
 static struct mdb_entry *
@@ -63,22 +70,27 @@ mdb_entry_from_lru_node(struct list *list)
  * flooded to be revalidated.) */
 static tag_type
 make_unknown_mdb_tag(const struct igmp_mdb *mdb,
-                     ovs_be32 grp, uint16_t vlan)
+                     struct mdb_ip *grp, uint16_t vlan)
 {
-    return tag_create_deterministic(igmp_mdb_hash(mdb, grp, vlan));
+   return tag_create_deterministic(igmp_mdb_hash(mdb, grp, vlan));
+}
+
+static bool mdb_entry_match(struct mdb_entry *e, struct mdb_ip *grp, uint16_t 
vlan)
+{
+    return e->vlan == vlan &&
+           ((grp->proto == htons(ETH_TYPE_IP) && grp->u.ip4 == e->group.u.ip4) 
||
+            (grp->proto == htons(ETH_TYPE_IPV6) && 
ipv6_addr_equals(&e->group.u.ip6, &grp->u.ip6)));
 }
 
 static struct mdb_entry *
-mdb_entry_lookup(const struct igmp_mdb *mdb,
-                 ovs_be32 grp, uint16_t vlan)
+mdb_entry_lookup(const struct igmp_mdb *mdb, struct mdb_ip *grp, uint16_t vlan)
 {
     struct mdb_entry *e;
 
     HMAP_FOR_EACH_WITH_HASH (e, hmap_node, igmp_mdb_hash(mdb, grp, vlan),
                              &mdb->table) {
-        if (e->vlan == vlan && e->group == grp) {
+        if (mdb_entry_match(e, grp, vlan))
             return e;
-        }
     }
     return NULL;
 }
@@ -211,7 +223,7 @@ igmp_may_snoop(const struct igmp_mdb *mdb, const ovs_be32 
dst, uint16_t vlan)
  * 'port' member.  Otherwise calling those functions is at the caller's
  * discretion. */
 struct mdb_entry *
-igmp_mdb_insert(struct igmp_mdb *mdb, ovs_be32 grp, uint16_t vlan)
+igmp_mdb_insert(struct igmp_mdb *mdb, const struct mdb_ip *grp, uint16_t vlan)
 {
     struct mdb_entry *e;
 
@@ -226,7 +238,7 @@ igmp_mdb_insert(struct igmp_mdb *mdb, ovs_be32 grp, 
uint16_t vlan)
 
         e = xmalloc(sizeof *e);
         hmap_insert(&mdb->table, &e->hmap_node, hash);
-        e->group = grp;
+        e->group = *grp;
         e->vlan = vlan;
         e->tag = 0;
     } else {
@@ -255,7 +267,7 @@ igmp_mdb_changed(struct igmp_mdb *mdb, struct mdb_entry *e)
     COVERAGE_INC(igmp_snooping_learned);
 
     e->tag = tag_create_random();
-    return old_tag ? old_tag : make_unknown_mdb_tag(mdb, e->group, e->vlan);
+    return old_tag ? old_tag : make_unknown_mdb_tag(mdb, &e->group, e->vlan);
 }
 
 /* Looks up group 'dst' for VLAN 'vlan' in 'mdb' and returns the associated
@@ -275,12 +287,42 @@ igmp_mdb_lookup(const struct igmp_mdb *mdb, ovs_be32 dst,
          * rarely that we revalidate every flow when it changes. */
         return NULL;
     } else {
-        struct mdb_entry *e = mdb_entry_lookup(mdb, dst, vlan);
+        struct mdb_ip ip;
+        struct mdb_entry *e;
+        ip.u.ip4 = dst;
+        e = mdb_entry_lookup(mdb, &ip, vlan);
+
+        ovs_assert(e == NULL || e->tag != 0);
+        if (tag) {
+            /* Tag either the learned port or the lack thereof. */
+            *tag |= e ? e->tag : make_unknown_mdb_tag(mdb, &ip, vlan);
+        }
+        return e;
+    }
+}
+
+struct mdb_entry *
+igmp6_mdb_lookup(const struct igmp_mdb *mdb, struct in6_addr *dst,
+                    uint16_t vlan, tag_type *tag)
+{
+    if (!ipv6_addr_is_multicast(dst)) {
+        /* No tag because the treatment of multicast destinations never
+         * changes. */
+        return NULL;
+    } else if (!is_learning_vlan(mdb, vlan)) {
+        /* We don't tag this property.  The set of learning VLANs changes so
+         * rarely that we revalidate every flow when it changes. */
+        return NULL;
+    } else {
+        struct mdb_ip ip;
+        struct mdb_entry *e;
+        ip.u.ip6 = *dst;
+        e = mdb_entry_lookup(mdb, &ip, vlan);
 
         ovs_assert(e == NULL || e->tag != 0);
         if (tag) {
             /* Tag either the learned port or the lack thereof. */
-            *tag |= e ? e->tag : make_unknown_mdb_tag(mdb, dst, vlan);
+            *tag |= e ? e->tag : make_unknown_mdb_tag(mdb, &ip, vlan);
         }
         return e;
     }
@@ -313,7 +355,7 @@ igmp_mdb_flush(struct igmp_mdb *mdb, struct tag_set *tags)
 }
 
 void
-igmp_mdb_delete(struct igmp_mdb *mdb, ovs_be32 group, int vlan)
+igmp_mdb_delete(struct igmp_mdb *mdb, const struct mdb_ip *group, uint16_t 
vlan)
 {
     struct mdb_entry *mdb_entry;
 
diff --git a/lib/igmp-snooping.h b/lib/igmp-snooping.h
index d1f8fbb..72f00c6 100644
--- a/lib/igmp-snooping.h
+++ b/lib/igmp-snooping.h
@@ -37,7 +37,7 @@ struct mdb_entry {
     struct hmap_node hmap_node; /* Node in a mdb hmap. */
     struct list lru_node;       /* Element in 'lrus' list. */
     time_t expires;             /* Expiration time. */
-    ovs_be32 group;             /* Known multicast group. */
+    struct mdb_ip group;        /* Known multicast group. */
     uint16_t vlan;              /* VLAN tag. */
     tag_type tag;               /* Tag for this entry. */
 
@@ -85,15 +85,18 @@ void igmp_mdb_set_max_entries(struct igmp_mdb *, size_t 
max_entries);
 bool igmp_may_snoop(const struct igmp_mdb *mdb, const ovs_be32 dst,
                     uint16_t vlan);
 
-struct mdb_entry *igmp_mdb_insert(struct igmp_mdb *mdb, ovs_be32 grp,
-                                  uint16_t vlan);
-void igmp_mdb_delete(struct igmp_mdb *mdb, ovs_be32 group, int vlan);
+struct mdb_entry *igmp_mdb_insert(struct igmp_mdb *mdb, const struct mdb_ip 
*grp, uint16_t vlan);
+void igmp_mdb_delete(struct igmp_mdb *mdb, const struct mdb_ip *group, 
uint16_t vlan);
 
 tag_type igmp_mdb_changed(struct igmp_mdb *mdb, struct mdb_entry *e);
 
 struct mdb_entry *
 igmp_mdb_lookup(const struct igmp_mdb *mdb, ovs_be32 dst,
                     uint16_t vlan, tag_type *tag);
+struct mdb_entry *
+igmp6_mdb_lookup(const struct igmp_mdb *mdb, struct in6_addr *dst,
+                    uint16_t vlan, tag_type *tag);
+
 
 /* Flushing. */
 void igmp_mdb_expire(struct igmp_mdb *mdb, struct mdb_entry *e);
diff --git a/lib/packets.h b/lib/packets.h
index 5c4995a..bab0c26 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -20,6 +20,7 @@
 #include <inttypes.h>
 #include <sys/types.h>
 #include <netinet/in.h>
+#include <netinet/icmp6.h>
 #include <stdint.h>
 #include <string.h>
 #include "compiler.h"
@@ -541,6 +542,11 @@ BUILD_ASSERT_DECL(ARP_ETH_HEADER_LEN == sizeof(struct 
arp_eth_header));
 #define IPV6_SCAN_FMT "%46[0123456789abcdefABCDEF:.]"
 #define IPV6_SCAN_LEN 46
 
+#define ICMPV6_MGM_QUERY                130
+#define ICMPV6_MGM_REPORT               131
+#define ICMPV6_MGM_REDUCTION            132
+#define ICMPV6_MLD2_REPORT              143
+
 extern const struct in6_addr in6addr_exact;
 #define IN6ADDR_EXACT_INIT { { { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, \
                                  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff } } }
@@ -555,6 +561,11 @@ static inline bool ipv6_addr_equals(const struct in6_addr 
*a,
 #endif
 }
 
+static inline bool ipv6_addr_is_multicast(const struct in6_addr *addr)
+{
+    return (addr->s6_addr32[0] & htonl(0xFF000000)) == htonl(0xFF000000);
+}
+
 static inline bool ipv6_mask_is_any(const struct in6_addr *mask) {
     return ipv6_addr_equals(mask, &in6addr_any);
 }
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index ec44e41..bfb0a3b 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -7048,9 +7048,10 @@ update_mdb_table(struct ofproto_dpif *ofproto,
         return;
     }
 
-    if (ntohs(flow->tp_src) == IGMP_HOST_MEMBERSHIP_REPORT ||
-        ntohs(flow->tp_src) == IGMPV2_HOST_MEMBERSHIP_REPORT) {
-        mdb_entry = igmp_mdb_insert(ofproto->mdb, flow->igmp_group, vlan);
+    switch (ntohs(flow->tp_src)) {
+    case IGMP_HOST_MEMBERSHIP_REPORT:
+    case IGMPV2_HOST_MEMBERSHIP_REPORT:
+        mdb_entry = igmp_mdb_insert(ofproto->mdb, &flow->igmp_group, vlan);
 
         if (mdb_entry_is_new(mdb_entry) || mdb_entry->port.p != in_bundle) {
             /* The log messages here could actually be useful in debugging,
@@ -7058,15 +7059,23 @@ update_mdb_table(struct ofproto_dpif *ofproto,
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
             VLOG_DBG_RL(&rl, "bridge %s: learned that "IP_FMT" is "
                         "on port %s in VLAN %d",
-                        ofproto->up.name, IP_ARGS(flow->igmp_group),
+                        ofproto->up.name, IP_ARGS(flow->igmp_group.u.ip4),
                         in_bundle->name, vlan);
 
             mdb_entry->port.p = in_bundle;
             tag_set_add(&ofproto->backer->revalidate_set,
                         igmp_mdb_changed(ofproto->mdb, mdb_entry));
         }
-    } else if (ntohs(flow->tp_src) == IGMP_HOST_LEAVE_MESSAGE) {
-        igmp_mdb_delete(ofproto->mdb, flow->igmp_group, vlan);
+        break;
+    case IGMP_HOST_LEAVE_MESSAGE:
+        igmp_mdb_delete(ofproto->mdb, &flow->igmp_group, vlan);
+        break;
+    case ICMPV6_MGM_REPORT:
+    case ICMPV6_MGM_QUERY:
+        break;
+    case ICMPV6_MGM_REDUCTION:
+        igmp_mdb_delete(ofproto->mdb, &flow->igmp_group, vlan);
+        break;
     }
 }
 
@@ -7230,15 +7239,19 @@ xlate_normal(struct action_xlate_ctx *ctx)
     /* Learn source MAC. */
     if (ctx->may_learn) {
         if (eth_addr_is_multicast(ctx->flow.dl_dst) &&
-           (ctx->flow.nw_proto == IPPROTO_IGMP))
+           (ctx->flow.nw_proto == IPPROTO_IGMP || ctx->flow.nw_proto == 
IPPROTO_ICMPV6))
             update_mdb_table(ctx->ofproto, &ctx->flow, vlan, in_bundle);
         else
             update_learning_table(ctx->ofproto, &ctx->flow, vlan, in_bundle);
     }
 
     if (eth_addr_is_multicast(ctx->flow.dl_dst)) {
-        mdb_entry = igmp_mdb_lookup(ctx->ofproto->mdb, ctx->flow.nw_dst, vlan,
-                                     &ctx->tags);
+        if (ctx->flow.dl_type == htons(ETH_TYPE_IP))
+            mdb_entry = igmp_mdb_lookup(ctx->ofproto->mdb, ctx->flow.nw_dst, 
vlan,
+                                       &ctx->tags);
+        else
+            mdb_entry = igmp6_mdb_lookup(ctx->ofproto->mdb, 
&ctx->flow.ipv6_dst, vlan,
+                                       &ctx->tags);
         if (mdb_entry) {
             if (mdb_entry->port.p != in_bundle) {
                 xlate_report(ctx, "forwarding to learned port");
@@ -7566,10 +7579,18 @@ ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int 
argc OVS_UNUSED,
     ds_put_cstr(&ds, " port  VLAN  MAC                Age\n");
     LIST_FOR_EACH (e, lru_node, &ofproto->mdb->lrus) {
         struct ofbundle *bundle = e->port.p;
-        ds_put_format(&ds, "%5d  %4d  "IP_FMT"  %3d\n",
-                      ofbundle_get_a_port(bundle)->odp_port,
-                      e->vlan, IP_ARGS(e->group),
-                      mdb_entry_age(ofproto->mdb, e));
+        char addr_str[INET6_ADDRSTRLEN];
+        if (e->group.proto == htons(ETH_TYPE_IP))
+            ds_put_format(&ds, "%5d  %4d  "IP_FMT"  %3d\n",
+                          ofbundle_get_a_port(bundle)->odp_port,
+                          e->vlan, IP_ARGS(e->group.u.ip4),
+                          mdb_entry_age(ofproto->mdb, e));
+        else
+            ds_put_format(&ds, "%5d  %4d  %s  %3d\n",
+                          ofbundle_get_a_port(bundle)->odp_port,
+                          e->vlan,
+                          inet_ntop(AF_INET6, &e->group.u.ip6, addr_str, 
INET6_ADDRSTRLEN),
+                          mdb_entry_age(ofproto->mdb, e));
     }
     unixctl_command_reply(conn, ds_cstr(&ds));
     ds_destroy(&ds);
@@ -7614,10 +7635,19 @@ ofproto_unixctl_mdb_show(struct unixctl_conn *conn, int 
argc OVS_UNUSED,
     ds_put_cstr(&ds, " port  VLAN  GROUP                Age\n");
     LIST_FOR_EACH (e, lru_node, &ofproto->mdb->lrus) {
         struct ofbundle *bundle = e->port.p;
-        ds_put_format(&ds, "%5d  %4d  "IP_FMT"  %3d\n",
-                      ofbundle_get_a_port(bundle)->odp_port,
-                      e->vlan, IP_ARGS(e->group),
-                      mdb_entry_age(ofproto->mdb, e));
+        char addr_str[INET6_ADDRSTRLEN];
+        if (e->group.proto == htons(ETH_TYPE_IP))
+            ds_put_format(&ds, "%5d  %4d  "IP_FMT"  %3d\n",
+                          ofbundle_get_a_port(bundle)->odp_port,
+                          e->vlan, IP_ARGS(e->group.u.ip4),
+                          mdb_entry_age(ofproto->mdb, e));
+        else
+            ds_put_format(&ds, "%5d  %4d  %s  %3d\n",
+                          ofbundle_get_a_port(bundle)->odp_port,
+                          e->vlan,
+                          inet_ntop(AF_INET6, &e->group.u.ip6, addr_str, 
INET6_ADDRSTRLEN),
+                          mdb_entry_age(ofproto->mdb, e));
+
     }
     unixctl_command_reply(conn, ds_cstr(&ds));
     ds_destroy(&ds);
-- 
1.7.7.6

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to