Support IGMPv3 messages with multiple records. Make sure all IGMPv3
messages go through slow path, since they may carry multiple multicast
addresses, unlike IGMPv2.

Tests done:

* multiple addresses in IGMPv3 report are inserted in mdb;
* address is removed from IGMPv3 if record is INCLUDE_MODE;
* reports sent on a burst with same flow all go to userspace;
* IGMPv3 reports go to mrouters, i.e., ports that have issued a query.

Signed-off-by: Thadeu Lima de Souza Cascardo <casca...@redhat.com>
---
 lib/mcast-snooping.c         | 52 ++++++++++++++++++++++++++++++++++++++++++++
 lib/mcast-snooping.h         |  5 +++++
 lib/packets.h                | 28 ++++++++++++++++++++++++
 ofproto/ofproto-dpif-xlate.c | 27 +++++++++++++++++++----
 4 files changed, 108 insertions(+), 4 deletions(-)

diff --git a/lib/mcast-snooping.c b/lib/mcast-snooping.c
index c3ffd6b..70fada4 100644
--- a/lib/mcast-snooping.c
+++ b/lib/mcast-snooping.c
@@ -69,6 +69,7 @@ mcast_snooping_is_membership(ovs_be16 igmp_type)
     switch (ntohs(igmp_type)) {
     case IGMP_HOST_MEMBERSHIP_REPORT:
     case IGMPV2_HOST_MEMBERSHIP_REPORT:
+    case IGMPV3_HOST_MEMBERSHIP_REPORT:
     case IGMP_HOST_LEAVE_MESSAGE:
         return true;
     }
@@ -416,6 +417,57 @@ mcast_snooping_add_group(struct mcast_snooping *ms, 
ovs_be32 ip4,
     return learned;
 }
 
+int
+mcast_snooping_add_report(struct mcast_snooping *ms,
+                          const struct dp_packet *p,
+                          uint16_t vlan, void *port)
+{
+    ovs_be32 ip4;
+    size_t offset;
+    const struct igmpv3 *igmpv3;
+    const struct igmpv3_record *record;
+    int count = 0;
+    int ngrp;
+
+    offset = p->l4_ofs;
+    igmpv3 = dp_packet_at(p, offset, IGMPV3_HDR_LEN);
+    if (!igmpv3) {
+        return 0;
+    }
+    ngrp = ntohs(igmpv3->ngrp);
+    offset += IGMPV3_HDR_LEN;
+    while (ngrp--) {
+        bool ret;
+        record = dp_packet_at(p, offset, sizeof(struct igmpv3_record));
+        if (!record) {
+            break;
+        }
+        /* Only consider known record types. */
+        if (record->type < IGMPV3_MODE_IS_INCLUDE
+            || record->type > IGMPV3_BLOCK_OLD_SOURCES) {
+            continue;
+        }
+        ip4 = 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, ip4, vlan, port);
+        } else {
+            ret = mcast_snooping_add_group(ms, ip4, vlan, port);
+        }
+        if (ret) {
+            count++;
+        }
+        offset += sizeof(*record)
+                  + ntohs(record->nsrcs) * sizeof(ovs_be32) + record->aux_len;
+    }
+    return count;
+}
+
 bool
 mcast_snooping_leave_group(struct mcast_snooping *ms, ovs_be32 ip4,
                            uint16_t vlan, void *port)
diff --git a/lib/mcast-snooping.h b/lib/mcast-snooping.h
index 979b2aa..f4bc8fb 100644
--- a/lib/mcast-snooping.h
+++ b/lib/mcast-snooping.h
@@ -20,6 +20,7 @@
 #define MCAST_SNOOPING_H 1
 
 #include <time.h>
+#include "dp-packet.h"
 #include "hmap.h"
 #include "list.h"
 #include "ovs-atomic.h"
@@ -181,6 +182,10 @@ mcast_snooping_lookup(const struct mcast_snooping *ms, 
ovs_be32 dip,
 bool mcast_snooping_add_group(struct mcast_snooping *ms, ovs_be32 ip4,
                               uint16_t vlan, void *port)
     OVS_REQ_WRLOCK(ms->rwlock);
+int mcast_snooping_add_report(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, ovs_be32 ip4,
                                 uint16_t vlan, void *port)
     OVS_REQ_WRLOCK(ms->rwlock);
diff --git a/lib/packets.h b/lib/packets.h
index b146a50..8c9a679 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -533,12 +533,40 @@ struct igmp_header {
 };
 BUILD_ASSERT_DECL(IGMP_HEADER_LEN == sizeof(struct igmp_header));
 
+#define IGMPV3_HEADER_LEN 8
+OVS_PACKED(
+struct igmpv3_header {
+    uint8_t type;
+    uint8_t rsvr1;
+    ovs_be16 csum;
+    ovs_be16 rsvr2;
+    ovs_be16 ngrp;
+});
+BUILD_ASSERT_DECL(IGMPV3_HEADER_LEN == sizeof(struct igmpv3_header));
+
+#define IGMPV3_RECORD_LEN
+OVS_PACKED(
+struct igmpv3_record {
+    uint8_t type;
+    uint8_t aux_len;
+    ovs_be16 nsrcs;
+    ovs_be32 maddr;
+});
+BUILD_ASSERT_DECL(IGMPV3_RECORD_LEN == sizeof(struct igmpv3_record));
+
 #define IGMP_HOST_MEMBERSHIP_QUERY    0x11 /* From RFC1112 */
 #define IGMP_HOST_MEMBERSHIP_REPORT   0x12 /* Ditto */
 #define IGMPV2_HOST_MEMBERSHIP_REPORT 0x16 /* V2 version of 0x12 */
 #define IGMP_HOST_LEAVE_MESSAGE       0x17
 #define IGMPV3_HOST_MEMBERSHIP_REPORT 0x22 /* V3 version of 0x12 */
 
+#define IGMPV3_MODE_IS_INCLUDE 1
+#define IGMPV3_MODE_IS_EXCLUDE 2
+#define IGMPV3_CHANGE_TO_INCLUDE_MODE 3
+#define IGMPV3_CHANGE_TO_EXCLUDE_MODE 4
+#define IGMPV3_ALLOW_NEW_SOURCES 5
+#define IGMPV3_BLOCK_OLD_SOURCES 6
+
 #define SCTP_HEADER_LEN 12
 struct sctp_header {
     ovs_be16 sctp_src;
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 71b8bef..73d1b03 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -1994,10 +1994,12 @@ 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)
+                              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 IGMP_HOST_MEMBERSHIP_REPORT:
@@ -2024,6 +2026,14 @@ update_mcast_snooping_table__(const struct xbridge 
*xbridge,
                         in_xbundle->name, vlan);
         }
         break;
+    case IGMPV3_HOST_MEMBERSHIP_REPORT:
+        if ((count = mcast_snooping_add_report(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;
     }
 }
 
@@ -2032,7 +2042,8 @@ update_mcast_snooping_table__(const struct xbridge 
*xbridge,
 static void
 update_mcast_snooping_table(const struct xbridge *xbridge,
                             const struct flow *flow, int vlan,
-                            struct xbundle *in_xbundle)
+                            struct xbundle *in_xbundle,
+                            const struct dp_packet *packet)
 {
     struct mcast_snooping *ms = xbridge->ms;
     struct xlate_cfg *xcfg;
@@ -2057,7 +2068,7 @@ 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);
+                                      vlan, in_xbundle, packet);
     }
     ovs_rwlock_unlock(&ms->rwlock);
 }
@@ -2270,8 +2281,16 @@ xlate_normal(struct xlate_ctx *ctx)
                 if (mcast_snooping_is_membership(flow->tp_src) ||
                     mcast_snooping_is_query(flow->tp_src)) {
                     update_mcast_snooping_table(ctx->xbridge, flow, vlan,
-                                                in_xbundle);
+                                                in_xbundle, ctx->xin->packet);
+                    /*
+                     * Since there may be multiple addresses in the
+                     * packet, flow keys can't be used. Setting slow
+                     * prevents the flow from being installed in the DP.
+                     */
+                    if (ntohs(flow->tp_src) == IGMPV3_HOST_MEMBERSHIP_REPORT) {
+                        ctx->xout->slow |= SLOW_ACTION;
                     }
+                }
             }
 
             if (mcast_snooping_is_membership(flow->tp_src)) {
-- 
2.4.1

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

Reply via email to