diff options
author | Nikolay Aleksandrov <nikolay@nvidia.com> | 2021-08-16 17:57:05 +0300 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2021-08-17 10:37:29 +0100 |
commit | 05d6f38ec0a5108596941f8b25d92c45aa08fc40 (patch) | |
tree | 329481a47e73da443394b9aee3c19d431f3f95e1 /net/bridge | |
parent | b92dace38f8f8ee9895fa211b79ad1b5ba3a2020 (diff) |
net: bridge: vlan: account for router port lists when notifying
When sending a global vlan notification we should account for the number
of router ports when allocating the skb, otherwise we might end up
losing notifications.
Fixes: dc002875c22b ("net: bridge: vlan: use br_rports_fill_info() to export mcast router ports")
Signed-off-by: Nikolay Aleksandrov <nikolay@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/bridge')
-rw-r--r-- | net/bridge/br_mdb.c | 30 | ||||
-rw-r--r-- | net/bridge/br_private.h | 1 | ||||
-rw-r--r-- | net/bridge/br_vlan_options.c | 17 |
3 files changed, 40 insertions, 8 deletions
diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index 389ff3c1e9d9..0281453f7766 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -37,6 +37,36 @@ br_ip6_rports_get_timer(struct net_bridge_mcast_port *pmctx, #endif } +static size_t __br_rports_one_size(void) +{ + return nla_total_size(sizeof(u32)) + /* MDBA_ROUTER_PORT */ + nla_total_size(sizeof(u32)) + /* MDBA_ROUTER_PATTR_TIMER */ + nla_total_size(sizeof(u8)) + /* MDBA_ROUTER_PATTR_TYPE */ + nla_total_size(sizeof(u32)) + /* MDBA_ROUTER_PATTR_INET_TIMER */ + nla_total_size(sizeof(u32)) + /* MDBA_ROUTER_PATTR_INET6_TIMER */ + nla_total_size(sizeof(u32)); /* MDBA_ROUTER_PATTR_VID */ +} + +size_t br_rports_size(const struct net_bridge_mcast *brmctx) +{ + struct net_bridge_mcast_port *pmctx; + size_t size = nla_total_size(0); /* MDBA_ROUTER */ + + rcu_read_lock(); + hlist_for_each_entry_rcu(pmctx, &brmctx->ip4_mc_router_list, + ip4_rlist) + size += __br_rports_one_size(); + +#if IS_ENABLED(CONFIG_IPV6) + hlist_for_each_entry_rcu(pmctx, &brmctx->ip6_mc_router_list, + ip6_rlist) + size += __br_rports_one_size(); +#endif + rcu_read_unlock(); + + return size; +} + int br_rports_fill_info(struct sk_buff *skb, const struct net_bridge_mcast *brmctx) { diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 9b1bf98a2c5a..df0fa246c80c 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -952,6 +952,7 @@ int br_multicast_dump_querier_state(struct sk_buff *skb, const struct net_bridge_mcast *brmctx, int nest_attr); size_t br_multicast_querier_state_size(void); +size_t br_rports_size(const struct net_bridge_mcast *brmctx); static inline bool br_group_is_l2(const struct br_ip *group) { diff --git a/net/bridge/br_vlan_options.c b/net/bridge/br_vlan_options.c index 49dec53a4a74..a3b8a086284b 100644 --- a/net/bridge/br_vlan_options.c +++ b/net/bridge/br_vlan_options.c @@ -362,7 +362,7 @@ out_err: return false; } -static size_t rtnl_vlan_global_opts_nlmsg_size(void) +static size_t rtnl_vlan_global_opts_nlmsg_size(const struct net_bridge_vlan *v) { return NLMSG_ALIGN(sizeof(struct br_vlan_msg)) + nla_total_size(0) /* BRIDGE_VLANDB_GLOBAL_OPTIONS */ @@ -382,6 +382,8 @@ static size_t rtnl_vlan_global_opts_nlmsg_size(void) + nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_QUERIER */ + nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_ROUTER */ + br_multicast_querier_state_size() /* BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_STATE */ + + nla_total_size(0) /* BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS */ + + br_rports_size(&v->br_mcast_ctx) /* BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS */ #endif + nla_total_size(sizeof(u16)); /* BRIDGE_VLANDB_GOPTS_RANGE */ } @@ -398,7 +400,12 @@ static void br_vlan_global_opts_notify(const struct net_bridge *br, /* right now notifications are done only with rtnl held */ ASSERT_RTNL(); - skb = nlmsg_new(rtnl_vlan_global_opts_nlmsg_size(), GFP_KERNEL); + /* need to find the vlan due to flags/options */ + v = br_vlan_find(br_vlan_group(br), vid); + if (!v) + return; + + skb = nlmsg_new(rtnl_vlan_global_opts_nlmsg_size(v), GFP_KERNEL); if (!skb) goto out_err; @@ -411,11 +418,6 @@ static void br_vlan_global_opts_notify(const struct net_bridge *br, bvm->family = AF_BRIDGE; bvm->ifindex = br->dev->ifindex; - /* need to find the vlan due to flags/options */ - v = br_vlan_find(br_vlan_group(br), vid); - if (!v) - goto out_kfree; - if (!br_vlan_global_opts_fill(skb, vid, vid_range, v)) goto out_err; @@ -425,7 +427,6 @@ static void br_vlan_global_opts_notify(const struct net_bridge *br, out_err: rtnl_set_sk_err(dev_net(br->dev), RTNLGRP_BRVLAN, err); -out_kfree: kfree_skb(skb); } |