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