Add IGMPv3 support.
authorThadeu Lima de Souza Cascardo <cascardo@redhat.com>
Wed, 17 Jun 2015 17:12:20 +0000 (14:12 -0300)
committerBen Pfaff <blp@nicira.com>
Wed, 17 Jun 2015 18:09:33 +0000 (11:09 -0700)
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 <cascardo@redhat.com>
Acked-by: Flavio Leitner <fbl@sysclose.org>
Signed-off-by: Ben Pfaff <blp@nicira.com>
NEWS
lib/mcast-snooping.c
lib/mcast-snooping.h
lib/packets.h
ofproto/ofproto-dpif-xlate.c
vswitchd/vswitch.xml

diff --git a/NEWS b/NEWS
index 90d9a29..43461b2 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -87,7 +87,7 @@ Post-v2.3.0
      with Docker, the wrapper script will be retired.
    - Added support for DPDK Tunneling. VXLAN, GRE, and Geneve are supported
      protocols. This is generic tunneling mechanism for userspace datapath.
-   - Support for multicast snooping (IGMPv1 and IGMPv2)
+   - Support for multicast snooping (IGMPv1, IGMPv2 and IGMPv3)
    - Support for Linux kernels up to 4.0.x
    - The documentation now use the term 'destination' to mean one of syslog,
      console or file for vlog logging instead of the previously used term
index c3ffd6b..7b927aa 100644 (file)
@@ -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_header *igmpv3;
+    const struct igmpv3_record *record;
+    int count = 0;
+    int ngrp;
+
+    offset = (char *) dp_packet_l4(p) - (char *) dp_packet_data(p);
+    igmpv3 = dp_packet_at(p, offset, IGMPV3_HEADER_LEN);
+    if (!igmpv3) {
+        return 0;
+    }
+    ngrp = ntohs(igmpv3->ngrp);
+    offset += IGMPV3_HEADER_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 = get_16aligned_be32(&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)
index 979b2aa..f4bc8fb 100644 (file)
@@ -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);
index e22267e..63ad2ff 100644 (file)
@@ -540,12 +540,38 @@ struct igmp_header {
 };
 BUILD_ASSERT_DECL(IGMP_HEADER_LEN == sizeof(struct igmp_header));
 
+#define IGMPV3_HEADER_LEN 8
+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 8
+struct igmpv3_record {
+    uint8_t type;
+    uint8_t aux_len;
+    ovs_be16 nsrcs;
+    ovs_16aligned_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;
index a0d13c2..5c1e63c 100644 (file)
@@ -1997,10 +1997,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:
@@ -2027,6 +2029,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;
     }
 }
 
@@ -2035,7 +2045,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;
@@ -2060,7 +2071,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);
 }
@@ -2273,7 +2284,7 @@ xlate_normal(struct xlate_ctx *ctx)
                 mcast_snooping_is_query(flow->tp_src)) {
                 if (ctx->xin->may_learn) {
                     update_mcast_snooping_table(ctx->xbridge, flow, vlan,
-                                                in_xbundle);
+                                                in_xbundle, ctx->xin->packet);
                 }
                 /*
                  * IGMP packets need to take the slow path, in order to be
index 8a60474..c43bfd1 100644 (file)
       Protocol (IGMP) traffic between hosts and multicast routers.  The
       switch uses what IGMP snooping learns to forward multicast traffic
       only to interfaces that are connected to interested receivers.
-      Currently it supports IGMPv1 and IGMPv2 protocols.
+      Currently it supports IGMPv1, IGMPv2 and IGMPv3 protocols.
 
       <column name="mcast_snooping_enable">
         Enable multicast snooping on the bridge. For now, the default