Commit a1aee20d authored by Petr Machata's avatar Petr Machata Committed by David S. Miller

net: bridge: Add netlink knobs for number / maximum MDB entries

The previous patch added accounting for number of MDB entries per port and
per port-VLAN, and the logic to verify that these values stay within
configured bounds. However it didn't provide means to actually configure
those bounds or read the occupancy. This patch does that.

Two new netlink attributes are added for the MDB occupancy:
IFLA_BRPORT_MCAST_N_GROUPS for the per-port occupancy and
BRIDGE_VLANDB_ENTRY_MCAST_N_GROUPS for the per-port-VLAN occupancy.
And another two for the maximum number of MDB entries:
IFLA_BRPORT_MCAST_MAX_GROUPS for the per-port maximum, and
BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS for the per-port-VLAN one.

Note that the two new IFLA_BRPORT_ attributes prompt bumping of
RTNL_SLAVE_MAX_TYPE to size the slave attribute tables large enough.

The new attributes are used like this:

 # ip link add name br up type bridge vlan_filtering 1 mcast_snooping 1 \
                                      mcast_vlan_snooping 1 mcast_querier 1
 # ip link set dev v1 master br
 # bridge vlan add dev v1 vid 2

 # bridge vlan set dev v1 vid 1 mcast_max_groups 1
 # bridge mdb add dev br port v1 grp 230.1.2.3 temp vid 1
 # bridge mdb add dev br port v1 grp 230.1.2.4 temp vid 1
 Error: bridge: Port-VLAN is already in 1 groups, and mcast_max_groups=1.

 # bridge link set dev v1 mcast_max_groups 1
 # bridge mdb add dev br port v1 grp 230.1.2.3 temp vid 2
 Error: bridge: Port is already in 1 groups, and mcast_max_groups=1.

 # bridge -d link show
 5: v1@v2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master br [...]
     [...] mcast_n_groups 1 mcast_max_groups 1

 # bridge -d vlan show
 port              vlan-id
 br                1 PVID Egress Untagged
                     state forwarding mcast_router 1
 v1                1 PVID Egress Untagged
                     [...] mcast_n_groups 1 mcast_max_groups 1
                   2
                     [...] mcast_n_groups 0 mcast_max_groups 0
Signed-off-by: default avatarPetr Machata <petrm@nvidia.com>
Acked-by: default avatarNikolay Aleksandrov <razor@blackwall.org>
Reviewed-by: default avatarIdo Schimmel <idosch@nvidia.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b57e8d87
...@@ -523,6 +523,8 @@ enum { ...@@ -523,6 +523,8 @@ enum {
BRIDGE_VLANDB_ENTRY_TUNNEL_INFO, BRIDGE_VLANDB_ENTRY_TUNNEL_INFO,
BRIDGE_VLANDB_ENTRY_STATS, BRIDGE_VLANDB_ENTRY_STATS,
BRIDGE_VLANDB_ENTRY_MCAST_ROUTER, BRIDGE_VLANDB_ENTRY_MCAST_ROUTER,
BRIDGE_VLANDB_ENTRY_MCAST_N_GROUPS,
BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS,
__BRIDGE_VLANDB_ENTRY_MAX, __BRIDGE_VLANDB_ENTRY_MAX,
}; };
#define BRIDGE_VLANDB_ENTRY_MAX (__BRIDGE_VLANDB_ENTRY_MAX - 1) #define BRIDGE_VLANDB_ENTRY_MAX (__BRIDGE_VLANDB_ENTRY_MAX - 1)
......
...@@ -567,6 +567,8 @@ enum { ...@@ -567,6 +567,8 @@ enum {
IFLA_BRPORT_MCAST_EHT_HOSTS_CNT, IFLA_BRPORT_MCAST_EHT_HOSTS_CNT,
IFLA_BRPORT_LOCKED, IFLA_BRPORT_LOCKED,
IFLA_BRPORT_MAB, IFLA_BRPORT_MAB,
IFLA_BRPORT_MCAST_N_GROUPS,
IFLA_BRPORT_MCAST_MAX_GROUPS,
__IFLA_BRPORT_MAX __IFLA_BRPORT_MAX
}; };
#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
......
...@@ -772,6 +772,21 @@ static void br_multicast_port_ngroups_dec(struct net_bridge_port *port, u16 vid) ...@@ -772,6 +772,21 @@ static void br_multicast_port_ngroups_dec(struct net_bridge_port *port, u16 vid)
br_multicast_port_ngroups_dec_one(&port->multicast_ctx); br_multicast_port_ngroups_dec_one(&port->multicast_ctx);
} }
u32 br_multicast_ngroups_get(const struct net_bridge_mcast_port *pmctx)
{
return READ_ONCE(pmctx->mdb_n_entries);
}
void br_multicast_ngroups_set_max(struct net_bridge_mcast_port *pmctx, u32 max)
{
WRITE_ONCE(pmctx->mdb_max_entries, max);
}
u32 br_multicast_ngroups_get_max(const struct net_bridge_mcast_port *pmctx)
{
return READ_ONCE(pmctx->mdb_max_entries);
}
static void br_multicast_destroy_port_group(struct net_bridge_mcast_gc *gc) static void br_multicast_destroy_port_group(struct net_bridge_mcast_gc *gc)
{ {
struct net_bridge_port_group *pg; struct net_bridge_port_group *pg;
......
...@@ -202,6 +202,8 @@ static inline size_t br_port_info_size(void) ...@@ -202,6 +202,8 @@ static inline size_t br_port_info_size(void)
+ nla_total_size_64bit(sizeof(u64)) /* IFLA_BRPORT_HOLD_TIMER */ + nla_total_size_64bit(sizeof(u64)) /* IFLA_BRPORT_HOLD_TIMER */
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+ nla_total_size(sizeof(u8)) /* IFLA_BRPORT_MULTICAST_ROUTER */ + nla_total_size(sizeof(u8)) /* IFLA_BRPORT_MULTICAST_ROUTER */
+ nla_total_size(sizeof(u32)) /* IFLA_BRPORT_MCAST_N_GROUPS */
+ nla_total_size(sizeof(u32)) /* IFLA_BRPORT_MCAST_MAX_GROUPS */
#endif #endif
+ nla_total_size(sizeof(u16)) /* IFLA_BRPORT_GROUP_FWD_MASK */ + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_GROUP_FWD_MASK */
+ nla_total_size(sizeof(u8)) /* IFLA_BRPORT_MRP_RING_OPEN */ + nla_total_size(sizeof(u8)) /* IFLA_BRPORT_MRP_RING_OPEN */
...@@ -298,7 +300,11 @@ static int br_port_fill_attrs(struct sk_buff *skb, ...@@ -298,7 +300,11 @@ static int br_port_fill_attrs(struct sk_buff *skb,
nla_put_u32(skb, IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT, nla_put_u32(skb, IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT,
p->multicast_eht_hosts_limit) || p->multicast_eht_hosts_limit) ||
nla_put_u32(skb, IFLA_BRPORT_MCAST_EHT_HOSTS_CNT, nla_put_u32(skb, IFLA_BRPORT_MCAST_EHT_HOSTS_CNT,
p->multicast_eht_hosts_cnt)) p->multicast_eht_hosts_cnt) ||
nla_put_u32(skb, IFLA_BRPORT_MCAST_N_GROUPS,
br_multicast_ngroups_get(&p->multicast_ctx)) ||
nla_put_u32(skb, IFLA_BRPORT_MCAST_MAX_GROUPS,
br_multicast_ngroups_get_max(&p->multicast_ctx)))
return -EMSGSIZE; return -EMSGSIZE;
#endif #endif
...@@ -883,6 +889,8 @@ static const struct nla_policy br_port_policy[IFLA_BRPORT_MAX + 1] = { ...@@ -883,6 +889,8 @@ static const struct nla_policy br_port_policy[IFLA_BRPORT_MAX + 1] = {
[IFLA_BRPORT_MAB] = { .type = NLA_U8 }, [IFLA_BRPORT_MAB] = { .type = NLA_U8 },
[IFLA_BRPORT_BACKUP_PORT] = { .type = NLA_U32 }, [IFLA_BRPORT_BACKUP_PORT] = { .type = NLA_U32 },
[IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT] = { .type = NLA_U32 }, [IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT] = { .type = NLA_U32 },
[IFLA_BRPORT_MCAST_N_GROUPS] = { .type = NLA_REJECT },
[IFLA_BRPORT_MCAST_MAX_GROUPS] = { .type = NLA_U32 },
}; };
/* Change the state of the port and notify spanning tree */ /* Change the state of the port and notify spanning tree */
...@@ -1017,6 +1025,13 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[], ...@@ -1017,6 +1025,13 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[],
if (err) if (err)
return err; return err;
} }
if (tb[IFLA_BRPORT_MCAST_MAX_GROUPS]) {
u32 max_groups;
max_groups = nla_get_u32(tb[IFLA_BRPORT_MCAST_MAX_GROUPS]);
br_multicast_ngroups_set_max(&p->multicast_ctx, max_groups);
}
#endif #endif
if (tb[IFLA_BRPORT_GROUP_FWD_MASK]) { if (tb[IFLA_BRPORT_GROUP_FWD_MASK]) {
......
...@@ -978,6 +978,9 @@ void br_multicast_uninit_stats(struct net_bridge *br); ...@@ -978,6 +978,9 @@ void br_multicast_uninit_stats(struct net_bridge *br);
void br_multicast_get_stats(const struct net_bridge *br, void br_multicast_get_stats(const struct net_bridge *br,
const struct net_bridge_port *p, const struct net_bridge_port *p,
struct br_mcast_stats *dest); struct br_mcast_stats *dest);
u32 br_multicast_ngroups_get(const struct net_bridge_mcast_port *pmctx);
void br_multicast_ngroups_set_max(struct net_bridge_mcast_port *pmctx, u32 max);
u32 br_multicast_ngroups_get_max(const struct net_bridge_mcast_port *pmctx);
void br_mdb_init(void); void br_mdb_init(void);
void br_mdb_uninit(void); void br_mdb_uninit(void);
void br_multicast_host_join(const struct net_bridge_mcast *brmctx, void br_multicast_host_join(const struct net_bridge_mcast *brmctx,
...@@ -1761,7 +1764,8 @@ static inline u16 br_vlan_flags(const struct net_bridge_vlan *v, u16 pvid) ...@@ -1761,7 +1764,8 @@ static inline u16 br_vlan_flags(const struct net_bridge_vlan *v, u16 pvid)
#ifdef CONFIG_BRIDGE_VLAN_FILTERING #ifdef CONFIG_BRIDGE_VLAN_FILTERING
bool br_vlan_opts_eq_range(const struct net_bridge_vlan *v_curr, bool br_vlan_opts_eq_range(const struct net_bridge_vlan *v_curr,
const struct net_bridge_vlan *range_end); const struct net_bridge_vlan *range_end);
bool br_vlan_opts_fill(struct sk_buff *skb, const struct net_bridge_vlan *v); bool br_vlan_opts_fill(struct sk_buff *skb, const struct net_bridge_vlan *v,
const struct net_bridge_port *p);
size_t br_vlan_opts_nl_size(void); size_t br_vlan_opts_nl_size(void);
int br_vlan_process_options(const struct net_bridge *br, int br_vlan_process_options(const struct net_bridge *br,
const struct net_bridge_port *p, const struct net_bridge_port *p,
......
...@@ -1816,6 +1816,7 @@ static bool br_vlan_stats_fill(struct sk_buff *skb, ...@@ -1816,6 +1816,7 @@ static bool br_vlan_stats_fill(struct sk_buff *skb,
/* v_opts is used to dump the options which must be equal in the whole range */ /* v_opts is used to dump the options which must be equal in the whole range */
static bool br_vlan_fill_vids(struct sk_buff *skb, u16 vid, u16 vid_range, static bool br_vlan_fill_vids(struct sk_buff *skb, u16 vid, u16 vid_range,
const struct net_bridge_vlan *v_opts, const struct net_bridge_vlan *v_opts,
const struct net_bridge_port *p,
u16 flags, u16 flags,
bool dump_stats) bool dump_stats)
{ {
...@@ -1842,7 +1843,7 @@ static bool br_vlan_fill_vids(struct sk_buff *skb, u16 vid, u16 vid_range, ...@@ -1842,7 +1843,7 @@ static bool br_vlan_fill_vids(struct sk_buff *skb, u16 vid, u16 vid_range,
goto out_err; goto out_err;
if (v_opts) { if (v_opts) {
if (!br_vlan_opts_fill(skb, v_opts)) if (!br_vlan_opts_fill(skb, v_opts, p))
goto out_err; goto out_err;
if (dump_stats && !br_vlan_stats_fill(skb, v_opts)) if (dump_stats && !br_vlan_stats_fill(skb, v_opts))
...@@ -1925,7 +1926,7 @@ void br_vlan_notify(const struct net_bridge *br, ...@@ -1925,7 +1926,7 @@ void br_vlan_notify(const struct net_bridge *br,
goto out_kfree; goto out_kfree;
} }
if (!br_vlan_fill_vids(skb, vid, vid_range, v, flags, false)) if (!br_vlan_fill_vids(skb, vid, vid_range, v, p, flags, false))
goto out_err; goto out_err;
nlmsg_end(skb, nlh); nlmsg_end(skb, nlh);
...@@ -2030,7 +2031,7 @@ static int br_vlan_dump_dev(const struct net_device *dev, ...@@ -2030,7 +2031,7 @@ static int br_vlan_dump_dev(const struct net_device *dev,
if (!br_vlan_fill_vids(skb, range_start->vid, if (!br_vlan_fill_vids(skb, range_start->vid,
range_end->vid, range_start, range_end->vid, range_start,
vlan_flags, dump_stats)) { p, vlan_flags, dump_stats)) {
err = -EMSGSIZE; err = -EMSGSIZE;
break; break;
} }
...@@ -2056,7 +2057,7 @@ static int br_vlan_dump_dev(const struct net_device *dev, ...@@ -2056,7 +2057,7 @@ static int br_vlan_dump_dev(const struct net_device *dev,
else if (!dump_global && else if (!dump_global &&
!br_vlan_fill_vids(skb, range_start->vid, !br_vlan_fill_vids(skb, range_start->vid,
range_end->vid, range_start, range_end->vid, range_start,
br_vlan_flags(range_start, pvid), p, br_vlan_flags(range_start, pvid),
dump_stats)) dump_stats))
err = -EMSGSIZE; err = -EMSGSIZE;
} }
...@@ -2131,6 +2132,8 @@ static const struct nla_policy br_vlan_db_policy[BRIDGE_VLANDB_ENTRY_MAX + 1] = ...@@ -2131,6 +2132,8 @@ static const struct nla_policy br_vlan_db_policy[BRIDGE_VLANDB_ENTRY_MAX + 1] =
[BRIDGE_VLANDB_ENTRY_STATE] = { .type = NLA_U8 }, [BRIDGE_VLANDB_ENTRY_STATE] = { .type = NLA_U8 },
[BRIDGE_VLANDB_ENTRY_TUNNEL_INFO] = { .type = NLA_NESTED }, [BRIDGE_VLANDB_ENTRY_TUNNEL_INFO] = { .type = NLA_NESTED },
[BRIDGE_VLANDB_ENTRY_MCAST_ROUTER] = { .type = NLA_U8 }, [BRIDGE_VLANDB_ENTRY_MCAST_ROUTER] = { .type = NLA_U8 },
[BRIDGE_VLANDB_ENTRY_MCAST_N_GROUPS] = { .type = NLA_REJECT },
[BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS] = { .type = NLA_U32 },
}; };
static int br_vlan_rtm_process_one(struct net_device *dev, static int br_vlan_rtm_process_one(struct net_device *dev,
......
...@@ -48,7 +48,8 @@ bool br_vlan_opts_eq_range(const struct net_bridge_vlan *v_curr, ...@@ -48,7 +48,8 @@ bool br_vlan_opts_eq_range(const struct net_bridge_vlan *v_curr,
curr_mc_rtr == range_mc_rtr; curr_mc_rtr == range_mc_rtr;
} }
bool br_vlan_opts_fill(struct sk_buff *skb, const struct net_bridge_vlan *v) bool br_vlan_opts_fill(struct sk_buff *skb, const struct net_bridge_vlan *v,
const struct net_bridge_port *p)
{ {
if (nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_STATE, br_vlan_get_state(v)) || if (nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_STATE, br_vlan_get_state(v)) ||
!__vlan_tun_put(skb, v)) !__vlan_tun_put(skb, v))
...@@ -58,6 +59,12 @@ bool br_vlan_opts_fill(struct sk_buff *skb, const struct net_bridge_vlan *v) ...@@ -58,6 +59,12 @@ bool br_vlan_opts_fill(struct sk_buff *skb, const struct net_bridge_vlan *v)
if (nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_MCAST_ROUTER, if (nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_MCAST_ROUTER,
br_vlan_multicast_router(v))) br_vlan_multicast_router(v)))
return false; return false;
if (p && !br_multicast_port_ctx_vlan_disabled(&v->port_mcast_ctx) &&
(nla_put_u32(skb, BRIDGE_VLANDB_ENTRY_MCAST_N_GROUPS,
br_multicast_ngroups_get(&v->port_mcast_ctx)) ||
nla_put_u32(skb, BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS,
br_multicast_ngroups_get_max(&v->port_mcast_ctx))))
return false;
#endif #endif
return true; return true;
...@@ -70,6 +77,8 @@ size_t br_vlan_opts_nl_size(void) ...@@ -70,6 +77,8 @@ size_t br_vlan_opts_nl_size(void)
+ nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_TINFO_ID */ + nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_TINFO_ID */
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+ nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_ENTRY_MCAST_ROUTER */ + nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_ENTRY_MCAST_ROUTER */
+ nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_ENTRY_MCAST_N_GROUPS */
+ nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS */
#endif #endif
+ 0; + 0;
} }
...@@ -212,6 +221,22 @@ static int br_vlan_process_one_opts(const struct net_bridge *br, ...@@ -212,6 +221,22 @@ static int br_vlan_process_one_opts(const struct net_bridge *br,
return err; return err;
*changed = true; *changed = true;
} }
if (tb[BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS]) {
u32 val;
if (!p) {
NL_SET_ERR_MSG_MOD(extack, "Can't set mcast_max_groups for non-port vlans");
return -EINVAL;
}
if (br_multicast_port_ctx_vlan_disabled(&v->port_mcast_ctx)) {
NL_SET_ERR_MSG_MOD(extack, "Multicast snooping disabled on this VLAN");
return -EINVAL;
}
val = nla_get_u32(tb[BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS]);
br_multicast_ngroups_set_max(&v->port_mcast_ctx, val);
*changed = true;
}
#endif #endif
return 0; return 0;
......
...@@ -58,7 +58,7 @@ ...@@ -58,7 +58,7 @@
#include "dev.h" #include "dev.h"
#define RTNL_MAX_TYPE 50 #define RTNL_MAX_TYPE 50
#define RTNL_SLAVE_MAX_TYPE 40 #define RTNL_SLAVE_MAX_TYPE 42
struct rtnl_link { struct rtnl_link {
rtnl_doit_func doit; rtnl_doit_func doit;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment