Commit 109865fe authored by Nikolay Aleksandrov's avatar Nikolay Aleksandrov Committed by Jakub Kicinski
Browse files

net: bridge: mcast: support for IGMPV3/MLDv2 BLOCK_OLD_SOURCES report



We already have all necessary helpers, so process IGMPV3/MLDv2
BLOCK_OLD_SOURCES as per the RFCs.

v3: add IPv6/MLDv2 support
v2: directly do flag bit operations

Signed-off-by: default avatarNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 5bf1e00b
Loading
Loading
Loading
Loading
+97 −0
Original line number Diff line number Diff line
@@ -1663,6 +1663,95 @@ static bool br_multicast_toex(struct net_bridge_port_group *pg,
	return changed;
}

/* State          Msg type      New state                Actions
 * INCLUDE (A)    BLOCK (B)     INCLUDE (A)              Send Q(G,A*B)
 */
static void __grp_src_block_incl(struct net_bridge_port_group *pg,
				 void *srcs, u32 nsrcs, size_t src_size)
{
	struct net_bridge_group_src *ent;
	u32 src_idx, to_send = 0;
	struct br_ip src_ip;

	hlist_for_each_entry(ent, &pg->src_list, node)
		ent->flags &= ~BR_SGRP_F_SEND;

	memset(&src_ip, 0, sizeof(src_ip));
	src_ip.proto = pg->addr.proto;
	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
		memcpy(&src_ip.u, srcs, src_size);
		ent = br_multicast_find_group_src(pg, &src_ip);
		if (ent) {
			ent->flags |= BR_SGRP_F_SEND;
			to_send++;
		}
		srcs += src_size;
	}

	if (to_send)
		__grp_src_query_marked_and_rexmit(pg);

	if (pg->filter_mode == MCAST_INCLUDE && hlist_empty(&pg->src_list))
		br_multicast_find_del_pg(pg->port->br, pg);
}

/* State          Msg type      New state                Actions
 * EXCLUDE (X,Y)  BLOCK (A)     EXCLUDE (X+(A-Y),Y)      (A-X-Y)=Group Timer
 *                                                       Send Q(G,A-Y)
 */
static bool __grp_src_block_excl(struct net_bridge_port_group *pg,
				 void *srcs, u32 nsrcs, size_t src_size)
{
	struct net_bridge_group_src *ent;
	u32 src_idx, to_send = 0;
	bool changed = false;
	struct br_ip src_ip;

	hlist_for_each_entry(ent, &pg->src_list, node)
		ent->flags &= ~BR_SGRP_F_SEND;

	memset(&src_ip, 0, sizeof(src_ip));
	src_ip.proto = pg->addr.proto;
	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
		memcpy(&src_ip.u, srcs, src_size);
		ent = br_multicast_find_group_src(pg, &src_ip);
		if (!ent) {
			ent = br_multicast_new_group_src(pg, &src_ip);
			if (ent) {
				mod_timer(&ent->timer, pg->timer.expires);
				changed = true;
			}
		}
		if (ent && timer_pending(&ent->timer)) {
			ent->flags |= BR_SGRP_F_SEND;
			to_send++;
		}
		srcs += src_size;
	}

	if (to_send)
		__grp_src_query_marked_and_rexmit(pg);

	return changed;
}

static bool br_multicast_block(struct net_bridge_port_group *pg,
			       void *srcs, u32 nsrcs, size_t src_size)
{
	bool changed = false;

	switch (pg->filter_mode) {
	case MCAST_INCLUDE:
		__grp_src_block_incl(pg, srcs, nsrcs, src_size);
		break;
	case MCAST_EXCLUDE:
		changed = __grp_src_block_excl(pg, srcs, nsrcs, src_size);
		break;
	}

	return changed;
}

static struct net_bridge_port_group *
br_multicast_find_port(struct net_bridge_mdb_entry *mp,
		       struct net_bridge_port *p,
@@ -1776,6 +1865,10 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
			changed = br_multicast_toex(pg, grec->grec_src, nsrcs,
						    sizeof(__be32));
			break;
		case IGMPV3_BLOCK_OLD_SOURCES:
			changed = br_multicast_block(pg, grec->grec_src, nsrcs,
						     sizeof(__be32));
			break;
		}
		if (changed)
			br_mdb_notify(br->dev, mdst, pg, RTM_NEWMDB);
@@ -1898,6 +1991,10 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
			changed = br_multicast_toex(pg, grec->grec_src, nsrcs,
						    sizeof(struct in6_addr));
			break;
		case MLD2_BLOCK_OLD_SOURCES:
			changed = br_multicast_block(pg, grec->grec_src, nsrcs,
						     sizeof(struct in6_addr));
			break;
		}
		if (changed)
			br_mdb_notify(br->dev, mdst, pg, RTM_NEWMDB);