Commit 5205e919 authored by Nikolay Aleksandrov's avatar Nikolay Aleksandrov Committed by Jakub Kicinski
Browse files

net: bridge: mcast: add support for src list and filter mode dumping



Support per port group src list (address and timer) and filter mode
dumping. Protected by either multicast_lock or rcu.

v3: add IPv6 support
v2: require RCU or multicast_lock to traverse src groups

Signed-off-by: default avatarNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 8b671779
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -455,10 +455,31 @@ enum {
enum {
	MDBA_MDB_EATTR_UNSPEC,
	MDBA_MDB_EATTR_TIMER,
	MDBA_MDB_EATTR_SRC_LIST,
	MDBA_MDB_EATTR_GROUP_MODE,
	__MDBA_MDB_EATTR_MAX
};
#define MDBA_MDB_EATTR_MAX (__MDBA_MDB_EATTR_MAX - 1)

/* per mdb entry source */
enum {
	MDBA_MDB_SRCLIST_UNSPEC,
	MDBA_MDB_SRCLIST_ENTRY,
	__MDBA_MDB_SRCLIST_MAX
};
#define MDBA_MDB_SRCLIST_MAX (__MDBA_MDB_SRCLIST_MAX - 1)

/* per mdb entry per source attributes
 * these are embedded in MDBA_MDB_SRCLIST_ENTRY
 */
enum {
	MDBA_MDB_SRCATTR_UNSPEC,
	MDBA_MDB_SRCATTR_ADDRESS,
	MDBA_MDB_SRCATTR_TIMER,
	__MDBA_MDB_SRCATTR_MAX
};
#define MDBA_MDB_SRCATTR_MAX (__MDBA_MDB_SRCATTR_MAX - 1)

/* multicast router types */
enum {
	MDB_RTR_TYPE_DISABLED,
+83 −2
Original line number Diff line number Diff line
@@ -77,10 +77,67 @@ static void __mdb_entry_to_br_ip(struct br_mdb_entry *entry, struct br_ip *ip)
#endif
}

static int __mdb_fill_srcs(struct sk_buff *skb,
			   struct net_bridge_port_group *p)
{
	struct net_bridge_group_src *ent;
	struct nlattr *nest, *nest_ent;

	if (hlist_empty(&p->src_list))
		return 0;

	nest = nla_nest_start(skb, MDBA_MDB_EATTR_SRC_LIST);
	if (!nest)
		return -EMSGSIZE;

	hlist_for_each_entry_rcu(ent, &p->src_list, node,
				 lockdep_is_held(&p->port->br->multicast_lock)) {
		nest_ent = nla_nest_start(skb, MDBA_MDB_SRCLIST_ENTRY);
		if (!nest_ent)
			goto out_cancel_err;
		switch (ent->addr.proto) {
		case htons(ETH_P_IP):
			if (nla_put_in_addr(skb, MDBA_MDB_SRCATTR_ADDRESS,
					    ent->addr.u.ip4)) {
				nla_nest_cancel(skb, nest_ent);
				goto out_cancel_err;
			}
			break;
#if IS_ENABLED(CONFIG_IPV6)
		case htons(ETH_P_IPV6):
			if (nla_put_in6_addr(skb, MDBA_MDB_SRCATTR_ADDRESS,
					     &ent->addr.u.ip6)) {
				nla_nest_cancel(skb, nest_ent);
				goto out_cancel_err;
			}
			break;
#endif
		default:
			nla_nest_cancel(skb, nest_ent);
			continue;
		}
		if (nla_put_u32(skb, MDBA_MDB_SRCATTR_TIMER,
				br_timer_value(&ent->timer))) {
			nla_nest_cancel(skb, nest_ent);
			goto out_cancel_err;
		}
		nla_nest_end(skb, nest_ent);
	}

	nla_nest_end(skb, nest);

	return 0;

out_cancel_err:
	nla_nest_cancel(skb, nest);
	return -EMSGSIZE;
}

static int __mdb_fill_info(struct sk_buff *skb,
			   struct net_bridge_mdb_entry *mp,
			   struct net_bridge_port_group *p)
{
	bool dump_srcs_mode = false;
	struct timer_list *mtimer;
	struct nlattr *nest_ent;
	struct br_mdb_entry e;
@@ -119,6 +176,23 @@ static int __mdb_fill_info(struct sk_buff *skb,
		nla_nest_cancel(skb, nest_ent);
		return -EMSGSIZE;
	}
	switch (mp->addr.proto) {
	case htons(ETH_P_IP):
		dump_srcs_mode = !!(p && mp->br->multicast_igmp_version == 3);
		break;
#if IS_ENABLED(CONFIG_IPV6)
	case htons(ETH_P_IPV6):
		dump_srcs_mode = !!(p && mp->br->multicast_mld_version == 2);
		break;
#endif
	}
	if (dump_srcs_mode &&
	    (__mdb_fill_srcs(skb, p) ||
	     nla_put_u8(skb, MDBA_MDB_EATTR_GROUP_MODE, p->filter_mode))) {
		nla_nest_cancel(skb, nest_ent);
		return -EMSGSIZE;
	}

	nla_nest_end(skb, nest_ent);

	return 0;
@@ -127,7 +201,7 @@ static int __mdb_fill_info(struct sk_buff *skb,
static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
			    struct net_device *dev)
{
	int idx = 0, s_idx = cb->args[1], err = 0;
	int idx = 0, s_idx = cb->args[1], err = 0, pidx = 0, s_pidx = cb->args[2];
	struct net_bridge *br = netdev_priv(dev);
	struct net_bridge_mdb_entry *mp;
	struct nlattr *nest, *nest2;
@@ -152,7 +226,7 @@ static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
			break;
		}

		if (mp->host_joined) {
		if (!s_pidx && mp->host_joined) {
			err = __mdb_fill_info(skb, mp, NULL);
			if (err) {
				nla_nest_cancel(skb, nest2);
@@ -164,13 +238,19 @@ static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
		      pp = &p->next) {
			if (!p->port)
				continue;
			if (pidx < s_pidx)
				goto skip_pg;

			err = __mdb_fill_info(skb, mp, p);
			if (err) {
				nla_nest_cancel(skb, nest2);
				goto out;
			}
skip_pg:
			pidx++;
		}
		pidx = 0;
		s_pidx = 0;
		nla_nest_end(skb, nest2);
skip:
		idx++;
@@ -178,6 +258,7 @@ static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb,

out:
	cb->args[1] = idx;
	cb->args[2] = pidx;
	nla_nest_end(skb, nest);
	return err;
}