Add support for MLDv1 and MLDv2. The behavior is not that different from
IGMP. Packets to all-hosts address and queries are always flooded,
reports go to routers, routers are added when a query is observed, and
all MLD packets go through slow path.

Signed-off-by: Thadeu Lima de Souza Cascardo <casca...@redhat.com>
---
 lib/flow.h                   | 25 +++++++++++++
 lib/mcast-snooping.c         | 67 ++++++++++++++++++++++++++++++++++
 lib/mcast-snooping.h         |  4 +++
 lib/packets.c                |  1 +
 lib/packets.h                | 40 +++++++++++++++++++++
 ofproto/ofproto-dpif-xlate.c | 85 +++++++++++++++++++++++++++++++++++++-------
 6 files changed, 209 insertions(+), 13 deletions(-)

diff --git a/lib/flow.h b/lib/flow.h
index 70554e4..e68dd74 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -758,6 +758,31 @@ static inline bool is_icmpv6(const struct flow *flow)
             && flow->nw_proto == IPPROTO_ICMPV6);
 }
 
+static inline bool is_igmp(const struct flow *flow)
+{
+    return (flow->dl_type == htons(ETH_TYPE_IP)
+            && flow->nw_proto == IPPROTO_IGMP);
+}
+
+static inline bool is_mld(const struct flow *flow)
+{
+    return is_icmpv6(flow)
+           && (flow->tp_src == htons(MLD_QUERY)
+               || flow->tp_src == htons(MLD_REPORT)
+               || flow->tp_src == htons(MLD_DONE)
+               || flow->tp_src == htons(MLD2_REPORT));
+}
+
+static inline bool is_mld_query(const struct flow *flow)
+{
+    return is_icmpv6(flow) && flow->tp_src == htons(MLD_QUERY);
+}
+
+static inline bool is_mld_report(const struct flow *flow)
+{
+    return is_mld(flow) && !is_mld_query(flow);
+}
+
 static inline bool is_stp(const struct flow *flow)
 {
     return (eth_addr_equals(flow->dl_dst, eth_addr_stp)
diff --git a/lib/mcast-snooping.c b/lib/mcast-snooping.c
index f2684f3..aee80b1 100644
--- a/lib/mcast-snooping.c
+++ b/lib/mcast-snooping.c
@@ -499,6 +499,73 @@ mcast_snooping_add_report(struct mcast_snooping *ms,
     return count;
 }
 
+int
+mcast_snooping_add_mld(struct mcast_snooping *ms,
+                          const struct dp_packet *p,
+                          uint16_t vlan, void *port)
+{
+    const struct in6_addr *addr;
+    size_t offset;
+    const struct mld_header *mld;
+    const struct mld2_record *record;
+    int count = 0;
+    int ngrp;
+    bool ret;
+
+    offset = (char *) dp_packet_l4(p) - (char *) dp_packet_data(p);
+    mld = dp_packet_at(p, offset, MLD_HEADER_LEN);
+    if (!mld) {
+        return 0;
+    }
+    ngrp = ntohs(mld->ngrp);
+    offset += MLD_HEADER_LEN;
+    addr = dp_packet_at(p, offset, sizeof(struct in6_addr));
+
+    switch (mld->type) {
+    case MLD_REPORT:
+        ret = mcast_snooping_add_group(ms, addr, vlan, port);
+        if (ret)
+            count++;
+        break;
+    case MLD_DONE:
+        ret = mcast_snooping_leave_group(ms, addr, vlan, port);
+        if (ret)
+            count++;
+        break;
+    case MLD2_REPORT:
+        while (ngrp--) {
+            record = dp_packet_at(p, offset, sizeof(struct mld2_record));
+            if (!record) {
+                break;
+            }
+            /* Only consider known record types. */
+            if (record->type < IGMPV3_MODE_IS_INCLUDE
+                || record->type > IGMPV3_BLOCK_OLD_SOURCES) {
+                continue;
+            }
+            addr = &record->maddr;
+            /*
+             * If record is INCLUDE MODE and there are no sources, it's 
equivalent
+             * to a LEAVE.
+             */
+            if (ntohs(record->nsrcs) == 0
+                && (record->type == IGMPV3_MODE_IS_INCLUDE
+                    || record->type == IGMPV3_CHANGE_TO_INCLUDE_MODE)) {
+                ret = mcast_snooping_leave_group(ms, addr, vlan, port);
+            } else {
+                ret = mcast_snooping_add_group(ms, addr, vlan, port);
+            }
+            if (ret) {
+                count++;
+            }
+            offset += sizeof(*record)
+                      + ntohs(record->nsrcs) * sizeof(struct in6_addr) + 
record->aux_len;
+        }
+    }
+
+    return count;
+}
+
 bool
 mcast_snooping_leave_group(struct mcast_snooping *ms,
                            const struct in6_addr *addr,
diff --git a/lib/mcast-snooping.h b/lib/mcast-snooping.h
index e3d15e4..99c314d 100644
--- a/lib/mcast-snooping.h
+++ b/lib/mcast-snooping.h
@@ -194,6 +194,10 @@ int mcast_snooping_add_report(struct mcast_snooping *ms,
                               const struct dp_packet *p,
                               uint16_t vlan, void *port)
     OVS_REQ_WRLOCK(ms->rwlock);
+int mcast_snooping_add_mld(struct mcast_snooping *ms,
+                           const struct dp_packet *p,
+                           uint16_t vlan, void *port)
+    OVS_REQ_WRLOCK(ms->rwlock);
 bool mcast_snooping_leave_group(struct mcast_snooping *ms,
                                 const struct in6_addr *addr,
                                 uint16_t vlan, void *port)
diff --git a/lib/packets.c b/lib/packets.c
index d04fffc..c7ea24c 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -34,6 +34,7 @@
 #include "unaligned.h"
 
 const struct in6_addr in6addr_exact = IN6ADDR_EXACT_INIT;
+const struct in6_addr in6addr_all_hosts = IN6ADDR_ALL_HOSTS_INIT;
 
 /* Parses 's' as a 16-digit hexadecimal number representing a datapath ID.  On
  * success stores the dpid into '*dpidp' and returns true, on failure stores 0
diff --git a/lib/packets.h b/lib/packets.h
index b5cd6ab..136376b 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -568,6 +568,9 @@ BUILD_ASSERT_DECL(IGMPV3_RECORD_LEN == sizeof(struct 
igmpv3_record));
 #define IGMP_HOST_LEAVE_MESSAGE       0x17
 #define IGMPV3_HOST_MEMBERSHIP_REPORT 0x22 /* V3 version of 0x12 */
 
+/*
+ * IGMPv3 and MLDv2 use the same codes.
+ */
 #define IGMPV3_MODE_IS_INCLUDE 1
 #define IGMPV3_MODE_IS_EXCLUDE 2
 #define IGMPV3_CHANGE_TO_INCLUDE_MODE 3
@@ -575,6 +578,35 @@ BUILD_ASSERT_DECL(IGMPV3_RECORD_LEN == sizeof(struct 
igmpv3_record));
 #define IGMPV3_ALLOW_NEW_SOURCES 5
 #define IGMPV3_BLOCK_OLD_SOURCES 6
 
+/*
+ * Use the same struct for MLD and MLD2, naming members as the defined fields 
in
+ * in the corresponding version of the protocol, though they are reserved in 
the
+ * other one.
+ */
+#define MLD_HEADER_LEN 8
+struct mld_header {
+    uint8_t type;
+    uint8_t code;
+    ovs_be16 csum;
+    ovs_be16 mrd;
+    ovs_be16 ngrp;
+};
+BUILD_ASSERT_DECL(MLD_HEADER_LEN == sizeof(struct mld_header));
+
+#define MLD2_RECORD_LEN 20
+struct mld2_record {
+    uint8_t type;
+    uint8_t aux_len;
+    ovs_be16 nsrcs;
+    struct in6_addr maddr;
+};
+BUILD_ASSERT_DECL(MLD2_RECORD_LEN == sizeof(struct mld2_record));
+
+#define MLD_QUERY 130
+#define MLD_REPORT 131
+#define MLD_DONE 132
+#define MLD2_REPORT 143
+
 #define SCTP_HEADER_LEN 12
 struct sctp_header {
     ovs_be16 sctp_src;
@@ -726,6 +758,10 @@ 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 } } }
 
+extern const struct in6_addr in6addr_all_hosts;
+#define IN6ADDR_ALL_HOSTS_INIT { { { 0xff,0x02,0x00,0x00,0x00,0x00,0x00,0x00, \
+                                     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01 } 
} }
+
 static inline bool ipv6_addr_equals(const struct in6_addr *a,
                                     const struct in6_addr *b)
 {
@@ -744,6 +780,10 @@ static inline bool ipv6_mask_is_exact(const struct 
in6_addr *mask) {
     return ipv6_addr_equals(mask, &in6addr_exact);
 }
 
+static inline bool ipv6_is_all_hosts(const struct in6_addr *addr) {
+    return ipv6_addr_equals(addr, &in6addr_all_hosts);
+}
+
 static inline bool dl_type_is_ip_any(ovs_be16 dl_type)
 {
     return dl_type == htons(ETH_TYPE_IP)
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 1490f0f..e48f872 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -1998,12 +1998,12 @@ update_learning_table(const struct xbridge *xbridge,
 /* Updates multicast snooping table 'ms' given that a packet matching 'flow'
  * was received on 'in_xbundle' in 'vlan' and is either Report or Query. */
 static void
-update_mcast_snooping_table__(const struct xbridge *xbridge,
-                              const struct flow *flow,
-                              struct mcast_snooping *ms,
-                              ovs_be32 ip4, int vlan,
-                              struct xbundle *in_xbundle,
-                              const struct dp_packet *packet)
+update_mcast_snooping_table4__(const struct xbridge *xbridge,
+                               const struct flow *flow,
+                               struct mcast_snooping *ms,
+                               ovs_be32 ip4, int vlan,
+                               struct xbundle *in_xbundle,
+                               const struct dp_packet *packet)
     OVS_REQ_WRLOCK(ms->rwlock)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 30);
@@ -2045,6 +2045,39 @@ update_mcast_snooping_table__(const struct xbridge 
*xbridge,
     }
 }
 
+static void
+update_mcast_snooping_table6__(const struct xbridge *xbridge,
+                               const struct flow *flow,
+                               struct mcast_snooping *ms, int vlan,
+                               struct xbundle *in_xbundle,
+                               const struct dp_packet *packet)
+    OVS_REQ_WRLOCK(ms->rwlock)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 30);
+    int count;
+
+    switch (ntohs(flow->tp_src)) {
+    case MLD_QUERY:
+        if (ipv6_addr_equals(&flow->ipv6_src, &in6addr_any)
+            && mcast_snooping_add_mrouter(ms, vlan, in_xbundle->ofbundle)) {
+            VLOG_DBG_RL(&rl, "bridge %s: multicast snooping query on port %s"
+                        "in VLAN %d",
+                        xbridge->name, in_xbundle->name, vlan);
+        }
+        break;
+    case MLD_REPORT:
+    case MLD_DONE:
+    case MLD2_REPORT:
+        if ((count = mcast_snooping_add_mld(ms, packet, vlan,
+                                            in_xbundle->ofbundle))) {
+            VLOG_DBG_RL(&rl, "bridge %s: multicast snooping processed %d "
+                        "addresses on port %s in VLAN %d",
+                        xbridge->name, count, in_xbundle->name, vlan);
+        }
+        break;
+    }
+}
+
 /* Updates multicast snooping table 'ms' given that a packet matching 'flow'
  * was received on 'in_xbundle' in 'vlan'. */
 static void
@@ -2075,8 +2108,13 @@ update_mcast_snooping_table(const struct xbridge 
*xbridge,
     }
 
     if (!mcast_xbundle || mcast_xbundle != in_xbundle) {
-        update_mcast_snooping_table__(xbridge, flow, ms, flow->igmp_group_ip4,
-                                      vlan, in_xbundle, packet);
+        if (flow->dl_type == htons(ETH_TYPE_IP))
+            update_mcast_snooping_table4__(xbridge, flow, ms,
+                                           flow->igmp_group_ip4, vlan,
+                                           in_xbundle, packet);
+        else
+            update_mcast_snooping_table6__(xbridge, flow, ms, vlan,
+                                           in_xbundle, packet);
     }
     ovs_rwlock_unlock(&ms->rwlock);
 }
@@ -2280,11 +2318,11 @@ xlate_normal(struct xlate_ctx *ctx)
     if (mcast_snooping_enabled(ctx->xbridge->ms)
         && !eth_addr_is_broadcast(flow->dl_dst)
         && eth_addr_is_multicast(flow->dl_dst)
-        && flow->dl_type == htons(ETH_TYPE_IP)) {
+        && is_ip_any(flow)) {
         struct mcast_snooping *ms = ctx->xbridge->ms;
-        struct mcast_group *grp;
+        struct mcast_group *grp = NULL;
 
-        if (flow->nw_proto == IPPROTO_IGMP) {
+        if (is_igmp(flow)) {
             if (mcast_snooping_is_membership(flow->tp_src) ||
                 mcast_snooping_is_query(flow->tp_src)) {
                 if (ctx->xin->may_learn) {
@@ -2317,8 +2355,26 @@ xlate_normal(struct xlate_ctx *ctx)
                 xlate_normal_flood(ctx, in_xbundle, vlan);
             }
             return;
+        } else if (is_mld(flow)) {
+            ctx->xout->slow |= SLOW_ACTION;
+            if (ctx->xin->may_learn) {
+                update_mcast_snooping_table(ctx->xbridge, flow, vlan,
+                                            in_xbundle, ctx->xin->packet);
+            }
+            if (is_mld_report(flow)) {
+                ovs_rwlock_rdlock(&ms->rwlock);
+                xlate_normal_mcast_send_mrouters(ctx, ms, in_xbundle, vlan);
+                xlate_normal_mcast_send_rports(ctx, ms, in_xbundle, vlan);
+                ovs_rwlock_unlock(&ms->rwlock);
+            } else {
+                xlate_report(ctx, "MLD query, flooding");
+                xlate_normal_flood(ctx, in_xbundle, vlan);
+            }
         } else {
-            if (ip_is_local_multicast(flow->nw_dst)) {
+            if ((flow->dl_type == htons(ETH_TYPE_IP)
+                 && ip_is_local_multicast(flow->nw_dst))
+                || (flow->dl_type == htons(ETH_TYPE_IPV6)
+                    && ipv6_is_all_hosts(&flow->ipv6_dst))) {
                 /* RFC4541: section 2.1.2, item 2: Packets with a dst IP
                  * address in the 224.0.0.x range which are not IGMP must
                  * be forwarded on all ports */
@@ -2330,7 +2386,10 @@ xlate_normal(struct xlate_ctx *ctx)
 
         /* forwarding to group base ports */
         ovs_rwlock_rdlock(&ms->rwlock);
-        grp = mcast_snooping_lookup4(ms, flow->nw_dst, vlan);
+        if (flow->dl_type == htons(ETH_TYPE_IP))
+            grp = mcast_snooping_lookup4(ms, flow->nw_dst, vlan);
+        else if (flow->dl_type == htons(ETH_TYPE_IPV6))
+            grp = mcast_snooping_lookup(ms, &flow->ipv6_dst, vlan);
         if (grp) {
             xlate_normal_mcast_send_group(ctx, ms, grp, in_xbundle, vlan);
             xlate_normal_mcast_send_fports(ctx, ms, in_xbundle, vlan);
-- 
2.4.2

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

Reply via email to