mcast-snoop: Add support to control Reports forwarding
authorFlavio Leitner <fbl@redhat.com>
Thu, 11 Dec 2014 11:38:19 +0000 (09:38 -0200)
committerBen Pfaff <blp@nicira.com>
Wed, 4 Feb 2015 18:49:07 +0000 (10:49 -0800)
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 <fbl@redhat.com>
Signed-off-by: Ben Pfaff <blp@nicira.com>
lib/mcast-snooping.c
lib/mcast-snooping.h
ofproto/ofproto-dpif-xlate.c
ofproto/ofproto-dpif.c
ofproto/ofproto-provider.h
ofproto/ofproto.c
ofproto/ofproto.h
utilities/ovs-vsctl.8.in
vswitchd/bridge.c
vswitchd/vswitch.xml

index 87b6b0c..6fe3890 100644 (file)
@@ -160,6 +160,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;
@@ -423,6 +424,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_port_lookup(&ms->rport_list, 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,25 @@ mcast_snooping_set_port_flood(struct mcast_snooping *ms, void *port,
     }
 }
 \f
+/* Flood Reports ports. */
+
+void
+mcast_snooping_set_port_flood_reports(struct mcast_snooping *ms, void *port,
+                                      bool flood)
+    OVS_REQ_WRLOCK(ms->rwlock)
+{
+    struct mcast_port_bundle *pbundle;
+
+    pbundle = mcast_snooping_port_lookup(&ms->rport_list, port);
+    if (flood && !pbundle) {
+        mcast_snooping_add_port(&ms->rport_list, port);
+        ms->need_revalidate = true;
+    } else if (!flood && pbundle) {
+        mcast_snooping_flush_port(pbundle);
+        ms->need_revalidate = true;
+    }
+}
+\f
 /* Run and flush. */
 
 static void
@@ -636,13 +663,20 @@ mcast_snooping_flush__(struct mcast_snooping *ms)
 
     hmap_shrink(&ms->table);
 
+    /* flush multicast routers */
     while (mrouter_get_lru(ms, &mrouter)) {
         mcast_snooping_flush_mrouter(mrouter);
     }
 
+    /* flush flood ports */
     while (mcast_snooping_port_get(&ms->fport_list, &pbundle)) {
         mcast_snooping_flush_port(pbundle);
     }
+
+    /* flush flood report ports */
+    while (mcast_snooping_port_get(&ms->rport_list, &pbundle)) {
+        mcast_snooping_flush_port(pbundle);
+    }
 }
 
 void
index c23ed6b..979b2aa 100644 (file)
@@ -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 ovs_list fport_list OVS_GUARDED;
 
+    /* Contains struct mcast_port_bundle to forward Reports in
+     * no special order. */
+    struct ovs_list rport_list OVS_GUARDED;
+
     /* Secret for randomizing hash table. */
     uint32_t secret;
 
@@ -163,6 +167,9 @@ mcast_snooping_set_flood_unreg(struct mcast_snooping *ms, bool enable)
 void mcast_snooping_set_port_flood(struct mcast_snooping *ms, void *port,
                                    bool flood)
     OVS_REQ_WRLOCK(ms->rwlock);
+void mcast_snooping_set_port_flood_reports(struct mcast_snooping *ms,
+                                           void *port, bool flood)
+    OVS_REQ_WRLOCK(ms->rwlock);
 
 /* Lookup. */
 struct mcast_group *
index 6f98da1..4af7c57 100644 (file)
@@ -2083,6 +2083,31 @@ xlate_normal_mcast_send_fports(struct xlate_ctx *ctx,
     }
 }
 
+/* forward 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)
@@ -2197,6 +2222,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");
index 18dbfe5..f65b27f 100644 (file)
@@ -3147,16 +3147,19 @@ 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, flood);
+        mcast_snooping_set_port_flood(ofproto->ms, bundle, s->flood);
+        mcast_snooping_set_port_flood_reports(ofproto->ms, bundle,
+                                              s->flood_reports);
         ovs_rwlock_unlock(&ofproto->ms->rwlock);
     }
     return 0;
index d114390..a0d556a 100644 (file)
@@ -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.)
  *
index b3909ad..4601670 100644 (file)
@@ -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);
 }
 
index 261bc3b..2d6e257 100644 (file)
@@ -182,6 +182,11 @@ 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 multicast traffic */
+    bool flood_reports;         /* If true, flood Reports traffic */
+};
+
 /* How the switch should act if the controller cannot be contacted. */
 enum ofproto_fail_mode {
     OFPROTO_FAIL_SECURE,        /* Preserve flow table. */
@@ -303,7 +308,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);
index bb030e4..c9123d0 100644 (file)
@@ -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"
index 4d84732..297c0dd 100644 (file)
@@ -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);
             }
index 37a33a6..d0bd446 100644 (file)
       <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>