mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 14:42:24 +00:00
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 {
|
enum {
|
||||||
MDBA_MDB_EATTR_UNSPEC,
|
MDBA_MDB_EATTR_UNSPEC,
|
||||||
MDBA_MDB_EATTR_TIMER,
|
MDBA_MDB_EATTR_TIMER,
|
||||||
|
MDBA_MDB_EATTR_SRC_LIST,
|
||||||
|
MDBA_MDB_EATTR_GROUP_MODE,
|
||||||
__MDBA_MDB_EATTR_MAX
|
__MDBA_MDB_EATTR_MAX
|
||||||
};
|
};
|
||||||
#define MDBA_MDB_EATTR_MAX (__MDBA_MDB_EATTR_MAX - 1)
|
#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 */
|
/* multicast router types */
|
||||||
enum {
|
enum {
|
||||||
MDB_RTR_TYPE_DISABLED,
|
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
|
#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,
|
static int __mdb_fill_info(struct sk_buff *skb,
|
||||||
struct net_bridge_mdb_entry *mp,
|
struct net_bridge_mdb_entry *mp,
|
||||||
struct net_bridge_port_group *p)
|
struct net_bridge_port_group *p)
|
||||||
{
|
{
|
||||||
|
bool dump_srcs_mode = false;
|
||||||
struct timer_list *mtimer;
|
struct timer_list *mtimer;
|
||||||
struct nlattr *nest_ent;
|
struct nlattr *nest_ent;
|
||||||
struct br_mdb_entry e;
|
struct br_mdb_entry e;
|
||||||
@ -119,6 +176,23 @@ static int __mdb_fill_info(struct sk_buff *skb,
|
|||||||
nla_nest_cancel(skb, nest_ent);
|
nla_nest_cancel(skb, nest_ent);
|
||||||
return -EMSGSIZE;
|
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);
|
nla_nest_end(skb, nest_ent);
|
||||||
|
|
||||||
return 0;
|
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,
|
static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
|
||||||
struct net_device *dev)
|
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 *br = netdev_priv(dev);
|
||||||
struct net_bridge_mdb_entry *mp;
|
struct net_bridge_mdb_entry *mp;
|
||||||
struct nlattr *nest, *nest2;
|
struct nlattr *nest, *nest2;
|
||||||
@ -152,7 +226,7 @@ static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mp->host_joined) {
|
if (!s_pidx && mp->host_joined) {
|
||||||
err = __mdb_fill_info(skb, mp, NULL);
|
err = __mdb_fill_info(skb, mp, NULL);
|
||||||
if (err) {
|
if (err) {
|
||||||
nla_nest_cancel(skb, nest2);
|
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) {
|
pp = &p->next) {
|
||||||
if (!p->port)
|
if (!p->port)
|
||||||
continue;
|
continue;
|
||||||
|
if (pidx < s_pidx)
|
||||||
|
goto skip_pg;
|
||||||
|
|
||||||
err = __mdb_fill_info(skb, mp, p);
|
err = __mdb_fill_info(skb, mp, p);
|
||||||
if (err) {
|
if (err) {
|
||||||
nla_nest_cancel(skb, nest2);
|
nla_nest_cancel(skb, nest2);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
skip_pg:
|
||||||
|
pidx++;
|
||||||
}
|
}
|
||||||
|
pidx = 0;
|
||||||
|
s_pidx = 0;
|
||||||
nla_nest_end(skb, nest2);
|
nla_nest_end(skb, nest2);
|
||||||
skip:
|
skip:
|
||||||
idx++;
|
idx++;
|
||||||
@ -178,6 +258,7 @@ skip:
|
|||||||
|
|
||||||
out:
|
out:
|
||||||
cb->args[1] = idx;
|
cb->args[1] = idx;
|
||||||
|
cb->args[2] = pidx;
|
||||||
nla_nest_end(skb, nest);
|
nla_nest_end(skb, nest);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user