Commit 42c11ccf authored by Nikolay Aleksandrov's avatar Nikolay Aleksandrov Committed by Jakub Kicinski
Browse files

net: bridge: mcast: add support for group query retransmit



We need to be able to retransmit group-specific and group-and-source
specific queries. The new timer takes care of those.

v3: add IPv6 support

Signed-off-by: default avatarNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 438ef2d0
Loading
Loading
Loading
Loading
+63 −10
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ static void br_ip4_multicast_leave_group(struct net_bridge *br,
					 __be32 group,
					 __u16 vid,
					 const unsigned char *src);
static void br_multicast_port_group_rexmit(struct timer_list *t);

static void __del_port_router(struct net_bridge_port *p);
#if IS_ENABLED(CONFIG_IPV6)
@@ -184,6 +185,7 @@ void br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
	rcu_assign_pointer(*pp, pg->next);
	hlist_del_init(&pg->mglist);
	del_timer(&pg->timer);
	del_timer(&pg->rexmit_timer);
	hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node)
		br_multicast_del_group_src(ent);
	br_mdb_notify(br->dev, pg->port, &pg->addr, RTM_DELMDB, pg->flags);
@@ -237,7 +239,8 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br,
						    struct net_bridge_port_group *pg,
						    __be32 ip_dst, __be32 group,
						    bool with_srcs, bool over_lmqt,
						    u8 sflag, u8 *igmp_type)
						    u8 sflag, u8 *igmp_type,
						    bool *need_rexmit)
{
	struct net_bridge_port *p = pg ? pg->port : NULL;
	struct net_bridge_group_src *ent;
@@ -352,6 +355,8 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br,
			    ent->src_query_rexmit_cnt > 0) {
				ihv3->srcs[lmqt_srcs++] = ent->addr.u.ip4;
				ent->src_query_rexmit_cnt--;
				if (need_rexmit && ent->src_query_rexmit_cnt)
					*need_rexmit = true;
			}
		}
		if (WARN_ON(lmqt_srcs != ntohs(ihv3->nsrcs))) {
@@ -380,7 +385,8 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br,
						    const struct in6_addr *ip6_dst,
						    const struct in6_addr *group,
						    bool with_srcs, bool over_llqt,
						    u8 sflag, u8 *igmp_type)
						    u8 sflag, u8 *igmp_type,
						    bool *need_rexmit)
{
	struct net_bridge_port *p = pg ? pg->port : NULL;
	struct net_bridge_group_src *ent;
@@ -510,6 +516,8 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br,
			    ent->src_query_rexmit_cnt > 0) {
				mld2q->mld2q_srcs[llqt_srcs++] = ent->addr.u.ip6;
				ent->src_query_rexmit_cnt--;
				if (need_rexmit && ent->src_query_rexmit_cnt)
					*need_rexmit = true;
			}
		}
		if (WARN_ON(llqt_srcs != ntohs(mld2q->mld2q_nsrcs))) {
@@ -540,7 +548,8 @@ static struct sk_buff *br_multicast_alloc_query(struct net_bridge *br,
						struct br_ip *ip_dst,
						struct br_ip *group,
						bool with_srcs, bool over_lmqt,
						u8 sflag, u8 *igmp_type)
						u8 sflag, u8 *igmp_type,
						bool *need_rexmit)
{
	__be32 ip4_dst;

@@ -550,7 +559,8 @@ static struct sk_buff *br_multicast_alloc_query(struct net_bridge *br,
		return br_ip4_multicast_alloc_query(br, pg,
						    ip4_dst, group->u.ip4,
						    with_srcs, over_lmqt,
						    sflag, igmp_type);
						    sflag, igmp_type,
						    need_rexmit);
#if IS_ENABLED(CONFIG_IPV6)
	case htons(ETH_P_IPV6): {
		struct in6_addr ip6_dst;
@@ -564,7 +574,8 @@ static struct sk_buff *br_multicast_alloc_query(struct net_bridge *br,
		return br_ip6_multicast_alloc_query(br, pg,
						    &ip6_dst, &group->u.ip6,
						    with_srcs, over_lmqt,
						    sflag, igmp_type);
						    sflag, igmp_type,
						    need_rexmit);
	}
#endif
	}
@@ -708,8 +719,9 @@ struct net_bridge_port_group *br_multicast_new_port_group(
	p->filter_mode = filter_mode;
	INIT_HLIST_HEAD(&p->src_list);
	rcu_assign_pointer(p->next, next);
	hlist_add_head(&p->mglist, &port->mglist);
	timer_setup(&p->timer, br_multicast_port_group_expired, 0);
	timer_setup(&p->rexmit_timer, br_multicast_port_group_rexmit, 0);
	hlist_add_head(&p->mglist, &port->mglist);

	if (src)
		memcpy(p->eth_addr, src, ETH_ALEN);
@@ -943,7 +955,8 @@ static void __br_multicast_send_query(struct net_bridge *br,
				      struct br_ip *ip_dst,
				      struct br_ip *group,
				      bool with_srcs,
				      u8 sflag)
				      u8 sflag,
				      bool *need_rexmit)
{
	bool over_lmqt = !!sflag;
	struct sk_buff *skb;
@@ -951,7 +964,8 @@ static void __br_multicast_send_query(struct net_bridge *br,

again_under_lmqt:
	skb = br_multicast_alloc_query(br, pg, ip_dst, group, with_srcs,
				       over_lmqt, sflag, &igmp_type);
				       over_lmqt, sflag, &igmp_type,
				       need_rexmit);
	if (!skb)
		return;

@@ -1004,7 +1018,8 @@ static void br_multicast_send_query(struct net_bridge *br,
	if (!other_query || timer_pending(&other_query->timer))
		return;

	__br_multicast_send_query(br, port, NULL, NULL, &br_group, false, 0);
	__br_multicast_send_query(br, port, NULL, NULL, &br_group, false, 0,
				  NULL);

	time = jiffies;
	time += own_query->startup_sent < br->multicast_startup_query_count ?
@@ -1049,6 +1064,44 @@ static void br_ip6_multicast_port_query_expired(struct timer_list *t)
}
#endif

static void br_multicast_port_group_rexmit(struct timer_list *t)
{
	struct net_bridge_port_group *pg = from_timer(pg, t, rexmit_timer);
	struct bridge_mcast_other_query *other_query = NULL;
	struct net_bridge *br = pg->port->br;
	bool need_rexmit = false;

	spin_lock(&br->multicast_lock);
	if (!netif_running(br->dev) || hlist_unhashed(&pg->mglist) ||
	    !br_opt_get(br, BROPT_MULTICAST_ENABLED) ||
	    !br_opt_get(br, BROPT_MULTICAST_QUERIER))
		goto out;

	if (pg->addr.proto == htons(ETH_P_IP))
		other_query = &br->ip4_other_query;
#if IS_ENABLED(CONFIG_IPV6)
	else
		other_query = &br->ip6_other_query;
#endif

	if (!other_query || timer_pending(&other_query->timer))
		goto out;

	if (pg->grp_query_rexmit_cnt) {
		pg->grp_query_rexmit_cnt--;
		__br_multicast_send_query(br, pg->port, pg, &pg->addr,
					  &pg->addr, false, 1, NULL);
	}
	__br_multicast_send_query(br, pg->port, pg, &pg->addr,
				  &pg->addr, true, 0, &need_rexmit);

	if (pg->grp_query_rexmit_cnt || need_rexmit)
		mod_timer(&pg->rexmit_timer, jiffies +
					     br->multicast_last_member_interval);
out:
	spin_unlock(&br->multicast_lock);
}

static void br_mc_disabled_update(struct net_device *dev, bool value)
{
	struct switchdev_attr attr = {
@@ -1658,7 +1711,7 @@ br_multicast_leave_group(struct net_bridge *br,

	if (br_opt_get(br, BROPT_MULTICAST_QUERIER)) {
		__br_multicast_send_query(br, port, NULL, NULL, &mp->addr,
					  false, 0);
					  false, 0, NULL);

		time = jiffies + br->multicast_last_member_count *
				 br->multicast_last_member_interval;
+8 −0
Original line number Diff line number Diff line
@@ -240,10 +240,12 @@ struct net_bridge_port_group {
	unsigned char			eth_addr[ETH_ALEN] __aligned(2);
	unsigned char			flags;
	unsigned char			filter_mode;
	unsigned char			grp_query_rexmit_cnt;

	struct hlist_head		src_list;
	unsigned int			src_ents;
	struct timer_list		timer;
	struct timer_list		rexmit_timer;
	struct hlist_node		mglist;

	struct rcu_head			rcu;
@@ -868,6 +870,12 @@ static inline int br_multicast_igmp_type(const struct sk_buff *skb)
{
	return BR_INPUT_SKB_CB(skb)->igmp;
}

static inline unsigned long br_multicast_lmqt(const struct net_bridge *br)
{
	return br->multicast_last_member_interval *
	       br->multicast_last_member_count;
}
#else
static inline int br_multicast_rcv(struct net_bridge *br,
				   struct net_bridge_port *port,