The RFC4541 section 2.1.1 item 1 allows the snooping switch
to provide an administrative control to allow Report messages
to be flooded to ports not connected to multicast routers.

Signed-off-by: Flavio Leitner <f...@redhat.com>
---
 lib/mcast-snooping.c         | 91 ++++++++++++++++++++++++++++++++++++++++++++
 lib/mcast-snooping.h         | 14 ++++++-
 ofproto/ofproto-dpif-xlate.c | 34 +++++++++++++++++
 ofproto/ofproto-dpif.c       | 13 ++++---
 ofproto/ofproto-provider.h   |  9 +++--
 ofproto/ofproto.c            |  8 ++--
 ofproto/ofproto.h            |  8 +++-
 utilities/ovs-vsctl.8.in     |  6 ++-
 vswitchd/bridge.c            |  7 +++-
 vswitchd/vswitch.xml         |  9 ++++-
 10 files changed, 180 insertions(+), 19 deletions(-)

diff --git a/lib/mcast-snooping.c b/lib/mcast-snooping.c
index 887519c..798fca1 100644
--- a/lib/mcast-snooping.c
+++ b/lib/mcast-snooping.c
@@ -158,6 +158,7 @@ mcast_snooping_create(void)
     list_init(&ms->group_lru);
     list_init(&ms->mrouter_lru);
     list_init(&ms->fport_list);
+    list_init(&ms->rport_list);
     ms->secret = random_uint32();
     ms->idle_time = MCAST_ENTRY_DEFAULT_IDLE_TIME;
     ms->max_entries = MCAST_DEFAULT_MAX_ENTRIES;
@@ -421,6 +422,13 @@ mcast_snooping_leave_group(struct mcast_snooping *ms, 
ovs_be32 ip4,
 {
     struct mcast_group *grp;
 
+    /* Ports flagged to forward Reports usually have more
+     * than one host behind it, so don't leave the group
+     * on the first message and just let it expire */
+    if (mcast_snooping_rport_lookup(ms, vlan, port)) {
+        return false;
+    }
+
     grp = mcast_snooping_lookup(ms, ip4, vlan);
     if (grp && mcast_group_delete_bundle(ms, grp, port)) {
         ms->need_revalidate = true;
@@ -589,6 +597,85 @@ mcast_snooping_set_port_flood(struct mcast_snooping *ms, 
uint16_t vlan,
     }
 }
 
+/* Flood Reports ports. */
+
+static struct mcast_port_bundle *
+mcast_rport_from_list_node(struct list *list)
+{
+    return CONTAINER_OF(list, struct mcast_port_bundle, node);
+}
+
+/* If the list is not empty, stores the rport in '*r' and returns true.
+ * Otherwise, if the list is empty, stores NULL in '*r' and return false. */
+static bool
+rport_get(const struct mcast_snooping *ms, struct mcast_port_bundle **r)
+    OVS_REQ_RDLOCK(ms->rwlock)
+{
+    if (!list_is_empty(&ms->rport_list)) {
+        *r = mcast_rport_from_list_node(ms->rport_list.next);
+        return true;
+    } else {
+        *r = NULL;
+        return false;
+    }
+}
+
+struct mcast_port_bundle *
+mcast_snooping_rport_lookup(struct mcast_snooping *ms, uint16_t vlan,
+                            void *port)
+    OVS_REQ_RDLOCK(ms->rwlock)
+{
+    struct mcast_port_bundle *rport;
+    uint16_t vlan_trunk = UINT16_MAX;
+
+    LIST_FOR_EACH (rport, node, &ms->rport_list) {
+        if (rport->vlan == vlan_trunk && rport->port == port) {
+            return rport;
+        }
+
+        if (rport->vlan == vlan && rport->port == port) {
+            return rport;
+        }
+    }
+    return NULL;
+}
+
+static void
+mcast_snooping_add_rport(struct mcast_snooping *ms, uint16_t vlan, void *port)
+    OVS_REQ_WRLOCK(ms->rwlock)
+{
+    struct mcast_port_bundle *rport;
+
+    rport = xmalloc(sizeof *rport);
+    rport->vlan = vlan;
+    rport->port = port;
+    list_insert(&ms->rport_list, &rport->node);
+}
+
+static void
+mcast_snooping_flush_rport(struct mcast_port_bundle *rport)
+{
+    list_remove(&rport->node);
+    free(rport);
+}
+
+void
+mcast_snooping_set_port_flood_reports(struct mcast_snooping *ms, uint16_t vlan,
+                                      void *port, bool flood)
+    OVS_REQ_WRLOCK(ms->rwlock)
+{
+    struct mcast_port_bundle *rport;
+
+    rport = mcast_snooping_rport_lookup(ms, vlan, port);
+    if (flood && !rport) {
+        mcast_snooping_add_rport(ms, vlan, port);
+        ms->need_revalidate = true;
+    } else if (!flood && rport) {
+        mcast_snooping_flush_rport(rport);
+        ms->need_revalidate = true;
+    }
+}
+
 /* Run and flush. */
 
 static void
@@ -643,6 +730,10 @@ mcast_snooping_flush__(struct mcast_snooping *ms)
     while (fport_get(ms, &port)) {
         mcast_snooping_flush_fport(port);
     }
+
+    while (rport_get(ms, &port)) {
+        mcast_snooping_flush_rport(port);
+    }
 }
 
 void
diff --git a/lib/mcast-snooping.h b/lib/mcast-snooping.h
index 861b973..9b91952 100644
--- a/lib/mcast-snooping.h
+++ b/lib/mcast-snooping.h
@@ -87,7 +87,7 @@ struct mcast_mrouter_bundle {
     void *port OVS_GUARDED;
 };
 
-/* The bundle to send multicast traffic.
+/* The bundle to send multicast traffic or Reports.
  * Guarded by owning 'mcast_snooping''s rwlock */
 struct mcast_port_bundle {
     /* Node in parent struct mcast_snooping. */
@@ -117,6 +117,10 @@ struct mcast_snooping {
      * packets in no special order. */
     struct list fport_list OVS_GUARDED;
 
+    /* Contains struct mcast_port_bundle to send Reports in
+     * no special order. */
+    struct list rport_list OVS_GUARDED;
+
     /* Secret for randomizing hash table. */
     uint32_t secret;
 
@@ -163,6 +167,10 @@ mcast_snooping_set_flood_unreg(struct mcast_snooping *ms, 
bool enable)
 void mcast_snooping_set_port_flood(struct mcast_snooping *ms, uint16_t vlan,
                                    void *port, bool flood)
     OVS_REQ_WRLOCK(ms->rwlock);
+void mcast_snooping_set_port_flood_reports(struct mcast_snooping *ms,
+                                           uint16_t vlan, void *port,
+                                           bool flood)
+    OVS_REQ_WRLOCK(ms->rwlock);
 
 /* Lookup. */
 struct mcast_group *
@@ -184,6 +192,10 @@ struct mcast_port_bundle *
 mcast_snooping_fport_lookup(struct mcast_snooping *ms, uint16_t vlan,
                             void *port)
     OVS_REQ_RDLOCK(ms->rwlock);
+struct mcast_port_bundle *
+mcast_snooping_rport_lookup(struct mcast_snooping *ms, uint16_t vlan,
+                            void *port)
+    OVS_REQ_RDLOCK(ms->rwlock);
 bool mcast_snooping_is_query(ovs_be16 igmp_type);
 bool mcast_snooping_is_membership(ovs_be16 igmp_type);
 
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 7f6aaf9..663eb85 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -2059,6 +2059,31 @@ xlate_normal_mcast_send_fports(struct xlate_ctx *ctx,
     }
 }
 
+/* send the Reports to configured ports */
+static void
+xlate_normal_mcast_send_rports(struct xlate_ctx *ctx,
+                               struct mcast_snooping *ms,
+                               struct xbundle *in_xbundle, uint16_t vlan)
+    OVS_REQ_RDLOCK(ms->rwlock)
+{
+    struct xlate_cfg *xcfg;
+    struct mcast_port_bundle *rport;
+    struct xbundle *mcast_xbundle;
+
+    xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);
+    LIST_FOR_EACH(rport, node, &ms->rport_list) {
+        mcast_xbundle = xbundle_lookup(xcfg, rport->port);
+        if (mcast_xbundle && mcast_xbundle != in_xbundle) {
+            xlate_report(ctx, "forwarding Report to mcast flagged port");
+            output_normal(ctx, mcast_xbundle, vlan);
+        } else if (!mcast_xbundle) {
+            xlate_report(ctx, "mcast port is unknown, dropping the Report");
+        } else {
+            xlate_report(ctx, "mcast port is input port, dropping the Report");
+        }
+    }
+}
+
 static void
 xlate_normal_flood(struct xlate_ctx *ctx, struct xbundle *in_xbundle,
                    uint16_t vlan)
@@ -2173,6 +2198,15 @@ xlate_normal(struct xlate_ctx *ctx)
             if (mcast_snooping_is_membership(flow->tp_src)) {
                 ovs_rwlock_rdlock(&ms->rwlock);
                 xlate_normal_mcast_send_mrouters(ctx, ms, in_xbundle, vlan);
+                /* RFC4541: section 2.1.1, item 1: A snooping switch should
+                 * forward IGMP Membership Reports only to those ports where
+                 * multicast routers are attached.  Alternatively stated: a
+                 * snooping switch should not forward IGMP Membership Reports
+                 * to ports on which only hosts are attached.
+                 * An administrative control may be provided to override this
+                 * restriction, allowing the report messages to be flooded to
+                 * other ports. */
+                xlate_normal_mcast_send_rports(ctx, ms, in_xbundle, vlan);
                 ovs_rwlock_unlock(&ms->rwlock);
             } else {
                 xlate_report(ctx, "multicast traffic, flooding");
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 5b3e64c..79d50f1 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -3100,17 +3100,20 @@ set_mcast_snooping(struct ofproto *ofproto_,
     return 0;
 }
 
-/* Configures multicast snooping port's flood setting on 'ofproto'. */
+/* Configures multicast snooping port's flood settings on 'ofproto'. */
 static int
-set_mcast_snooping_port(struct ofproto *ofproto_, void *aux, bool flood)
+set_mcast_snooping_port(struct ofproto *ofproto_, void *aux,
+                        const struct ofproto_mcast_snooping_port_settings *s)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct ofbundle *bundle = bundle_lookup(ofproto, aux);
 
-    if (ofproto->ms) {
+    if (ofproto->ms && s) {
         ovs_rwlock_wrlock(&ofproto->ms->rwlock);
-        mcast_snooping_set_port_flood(ofproto->ms, bundle->vlan, bundle,
-                                      flood);
+        mcast_snooping_set_port_flood(ofproto->ms, bundle->vlan,
+                                      bundle, s->flood);
+        mcast_snooping_set_port_flood_reports(ofproto->ms, bundle->vlan,
+                                              bundle, s->flood_reports);
         ovs_rwlock_unlock(&ofproto->ms->rwlock);
     }
     return 0;
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 4dc8adb..fa7319e 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -1588,14 +1588,15 @@ struct ofproto_class {
 
     /* Configures multicast snooping port's flood setting on 'ofproto'.
      *
-     * All multicast traffic is sent to struct port 'aux' in 'ofproto'
-     * if 'flood' is true. Otherwise, struct port 'aux' is an ordinary
-     * switch port.
+     * If 's' is nonnull, this function updates multicast snooping
+     * configuration to 's' in 'ofproto'.
+     *
+     * If 's' is NULL, this function doesn't change anything.
      *
      * An implementation that does not support multicast snooping may set
      * it to NULL or return EOPNOTSUPP. */
     int (*set_mcast_snooping_port)(struct ofproto *ofproto_, void *aux,
-                                   bool flood);
+                          const struct ofproto_mcast_snooping_port_settings 
*s);
 
 /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
  *
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 1485fb4..ac89312 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -721,15 +721,15 @@ ofproto_set_mcast_snooping(struct ofproto *ofproto,
             : EOPNOTSUPP);
 }
 
-/* Configures multicast snooping flood setting on 'ofp_port' of 'ofproto'.
+/* Configures multicast snooping flood settings on 'ofp_port' of 'ofproto'.
  *
  * Returns 0 if successful, otherwise a positive errno value.*/
 int
-ofproto_port_set_mcast_snooping(struct ofproto *ofproto, void *aux, bool flood)
+ofproto_port_set_mcast_snooping(struct ofproto *ofproto, void *aux,
+                           const struct ofproto_mcast_snooping_port_settings 
*s)
 {
     return (ofproto->ofproto_class->set_mcast_snooping_port
-            ? ofproto->ofproto_class->set_mcast_snooping_port(ofproto, aux,
-                                                              flood)
+            ? ofproto->ofproto_class->set_mcast_snooping_port(ofproto, aux, s)
             : EOPNOTSUPP);
 }
 
diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
index 261bc3b..85fe029 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -182,6 +182,12 @@ struct ofproto_mcast_snooping_settings {
     unsigned int max_entries;   /* Size of the multicast snooping table. */
 };
 
+struct ofproto_mcast_snooping_port_settings {
+    bool flood;                 /* If true, flood the port with all multicast
+                                   traffic excluding Reports. */
+    bool flood_reports;         /* If true, flood the port with Reports */
+};
+
 /* How the switch should act if the controller cannot be contacted. */
 enum ofproto_fail_mode {
     OFPROTO_FAIL_SECURE,        /* Preserve flow table. */
@@ -303,7 +309,7 @@ void ofproto_set_mac_table_config(struct ofproto *, 
unsigned idle_time,
 int ofproto_set_mcast_snooping(struct ofproto *ofproto,
                               const struct ofproto_mcast_snooping_settings *s);
 int ofproto_port_set_mcast_snooping(struct ofproto *ofproto, void *aux,
-                                    bool flood);
+                          const struct ofproto_mcast_snooping_port_settings 
*s);
 void ofproto_set_threads(int n_handlers, int n_revalidators);
 void ofproto_set_n_dpdk_rxqs(int n_rxqs);
 void ofproto_set_cpu_mask(const char *cmask);
diff --git a/utilities/ovs-vsctl.8.in b/utilities/ovs-vsctl.8.in
index bb030e4..c9123d0 100644
--- a/utilities/ovs-vsctl.8.in
+++ b/utilities/ovs-vsctl.8.in
@@ -995,10 +995,14 @@ unregistered packets on bridge \fBbr0\fR.
 .IP
 .B "ovs\-vsctl set Bridge br0 
other_config:mcast-snooping-disable-flood-unregistered=true"
 .PP
-Enable flooding of multicast packets on a specific port.
+Enable flooding of multicast packets (except Reports) on a specific port.
 .IP
 .B "ovs\-vsctl set Port eth1 other_config:mcast-snooping-flood=true"
 .PP
+Enable flooding of Reports on a specific port.
+.IP
+.B "ovs\-vsctl set Port eth1 other_config:mcast-snooping-flood-reports=true"
+.PP
 Deconfigure multicasting snooping from above:
 .IP
 .B "ovs\-vsctl set Bridge br0 mcast_snooping_enable=false"
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index fea273a..73c0487 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -1896,9 +1896,12 @@ bridge_configure_mcast_snooping(struct bridge *br)
         }
 
         HMAP_FOR_EACH (port, hmap_node, &br->ports) {
-            bool flood = smap_get_bool(&port->cfg->other_config,
+            struct ofproto_mcast_snooping_port_settings port_s;
+            port_s.flood = smap_get_bool(&port->cfg->other_config,
                                        "mcast-snooping-flood", false);
-            if (ofproto_port_set_mcast_snooping(br->ofproto, port, flood)) {
+            port_s.flood_reports = smap_get_bool(&port->cfg->other_config,
+                                       "mcast-snooping-flood-reports", false);
+            if (ofproto_port_set_mcast_snooping(br->ofproto, port, &port_s)) {
                 VLOG_ERR("port %s: could not configure mcast snooping",
                          port->name);
             }
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index 37a33a6..d0bd446 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -1419,7 +1419,14 @@
       <column name="other_config" key="mcast-snooping-flood"
               type='{"type": "boolean"}'>
         <p>
-          If set to <code>true</code>, multicast packets are unconditionally
+          If set to <code>true</code>, multicast packets (except Reports) are
+          unconditionally forwarded to the specific port.
+        </p>
+      </column>
+      <column name="other_config" key="mcast-snooping-flood-reports"
+              type='{"type": "boolean"}'>
+        <p>
+          If set to <code>true</code>, multicast Reports are unconditionally
           forwarded to the specific port.
         </p>
       </column>
-- 
1.9.3

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

Reply via email to