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: Nikolay Aleksandrov <nikolay@cumulusnetworks.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
8b671779b7
commit
5205e919c9
|
@ -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,
|
||||
|
|
|
@ -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 @@ skip:
|
|||
|
||||
out:
|
||||
cb->args[1] = idx;
|
||||
cb->args[2] = pidx;
|
||||
nla_nest_end(skb, nest);
|
||||
return err;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue