Skip to content

Commit

Permalink
Merge branch 'bridge-global-mcast'
Browse files Browse the repository at this point in the history
Nikolay Aleksandrov says:

====================
net: bridge: vlan: add global mcast options

This is the first follow-up set after the support for per-vlan multicast
contexts which extends global vlan options to support bridge's multicast
config per-vlan, it enables user-space to change and dump the already
existing bridge vlan multicast context options. The global option patches
(01 - 09 and 12-13) follow a similar pattern of changing current mcast
functions to take multicast context instead of a port/bridge directly.
Option equality checks have been added for dumping vlan range compression.
The last 2 patches extend the mcast router dump support so it can be
re-used when dumping vlan config.

patches 01 - 09: add support for various mcast options
patches 10 - 11: prepare for per-vlan querier control
patches 12 - 13: add support for querier control and router control
patches 14 - 15: add support for dumping per-vlan router ports

Next patch-sets:
 - per-port/vlan router option config
 - iproute2 support for all new vlan options
 - selftests
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Aug 11, 2021
2 parents 6899192 + dc00287 commit e9c130a
Show file tree
Hide file tree
Showing 7 changed files with 354 additions and 79 deletions.
14 changes: 14 additions & 0 deletions include/uapi/linux/if_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,20 @@ enum {
BRIDGE_VLANDB_GOPTS_ID,
BRIDGE_VLANDB_GOPTS_RANGE,
BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING,
BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION,
BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION,
BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT,
BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT,
BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL,
BRIDGE_VLANDB_GOPTS_PAD,
BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL,
BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL,
BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL,
BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL,
BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL,
BRIDGE_VLANDB_GOPTS_MCAST_QUERIER,
BRIDGE_VLANDB_GOPTS_MCAST_ROUTER,
BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS,
__BRIDGE_VLANDB_GOPTS_MAX
};
#define BRIDGE_VLANDB_GOPTS_MAX (__BRIDGE_VLANDB_GOPTS_MAX - 1)
Expand Down
60 changes: 32 additions & 28 deletions net/bridge/br_mdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,57 +16,59 @@

#include "br_private.h"

static bool br_rports_have_mc_router(struct net_bridge_mcast *brmctx)
{
#if IS_ENABLED(CONFIG_IPV6)
return !hlist_empty(&brmctx->ip4_mc_router_list) ||
!hlist_empty(&brmctx->ip6_mc_router_list);
#else
return !hlist_empty(&brmctx->ip4_mc_router_list);
#endif
}

static bool
br_ip4_rports_get_timer(struct net_bridge_port *port, unsigned long *timer)
br_ip4_rports_get_timer(struct net_bridge_mcast_port *pmctx,
unsigned long *timer)
{
*timer = br_timer_value(&port->multicast_ctx.ip4_mc_router_timer);
return !hlist_unhashed(&port->multicast_ctx.ip4_rlist);
*timer = br_timer_value(&pmctx->ip4_mc_router_timer);
return !hlist_unhashed(&pmctx->ip4_rlist);
}

static bool
br_ip6_rports_get_timer(struct net_bridge_port *port, unsigned long *timer)
br_ip6_rports_get_timer(struct net_bridge_mcast_port *pmctx,
unsigned long *timer)
{
#if IS_ENABLED(CONFIG_IPV6)
*timer = br_timer_value(&port->multicast_ctx.ip6_mc_router_timer);
return !hlist_unhashed(&port->multicast_ctx.ip6_rlist);
*timer = br_timer_value(&pmctx->ip6_mc_router_timer);
return !hlist_unhashed(&pmctx->ip6_rlist);
#else
*timer = 0;
return false;
#endif
}

static int br_rports_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
struct net_device *dev)
int br_rports_fill_info(struct sk_buff *skb,
const struct net_bridge_mcast *brmctx)
{
struct net_bridge *br = netdev_priv(dev);
u16 vid = brmctx->vlan ? brmctx->vlan->vid : 0;
bool have_ip4_mc_rtr, have_ip6_mc_rtr;
unsigned long ip4_timer, ip6_timer;
struct nlattr *nest, *port_nest;
struct net_bridge_port *p;

if (!br->multicast_ctx.multicast_router)
return 0;

if (!br_rports_have_mc_router(&br->multicast_ctx))
if (!brmctx->multicast_router || !br_rports_have_mc_router(brmctx))
return 0;

nest = nla_nest_start_noflag(skb, MDBA_ROUTER);
if (nest == NULL)
return -EMSGSIZE;

list_for_each_entry_rcu(p, &br->port_list, list) {
have_ip4_mc_rtr = br_ip4_rports_get_timer(p, &ip4_timer);
have_ip6_mc_rtr = br_ip6_rports_get_timer(p, &ip6_timer);
list_for_each_entry_rcu(p, &brmctx->br->port_list, list) {
struct net_bridge_mcast_port *pmctx;

if (vid) {
struct net_bridge_vlan *v;

v = br_vlan_find(nbp_vlan_group(p), vid);
if (!v)
continue;
pmctx = &v->port_mcast_ctx;
} else {
pmctx = &p->multicast_ctx;
}

have_ip4_mc_rtr = br_ip4_rports_get_timer(pmctx, &ip4_timer);
have_ip6_mc_rtr = br_ip6_rports_get_timer(pmctx, &ip6_timer);

if (!have_ip4_mc_rtr && !have_ip6_mc_rtr)
continue;
Expand All @@ -85,7 +87,8 @@ static int br_rports_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
ip4_timer)) ||
(have_ip6_mc_rtr &&
nla_put_u32(skb, MDBA_ROUTER_PATTR_INET6_TIMER,
ip6_timer))) {
ip6_timer)) ||
(vid && nla_put_u16(skb, MDBA_ROUTER_PATTR_VID, vid))) {
nla_nest_cancel(skb, port_nest);
goto fail;
}
Expand Down Expand Up @@ -390,6 +393,7 @@ static int br_mdb_dump(struct sk_buff *skb, struct netlink_callback *cb)

for_each_netdev_rcu(net, dev) {
if (dev->priv_flags & IFF_EBRIDGE) {
struct net_bridge *br = netdev_priv(dev);
struct br_port_msg *bpm;

if (idx < s_idx)
Expand All @@ -406,7 +410,7 @@ static int br_mdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
bpm->ifindex = dev->ifindex;
if (br_mdb_fill_info(skb, cb, dev) < 0)
goto out;
if (br_rports_fill_info(skb, cb, dev) < 0)
if (br_rports_fill_info(skb, &br->multicast_ctx) < 0)
goto out;

cb->args[1] = 0;
Expand Down
71 changes: 38 additions & 33 deletions net/bridge/br_multicast.c
Original file line number Diff line number Diff line change
Expand Up @@ -1628,7 +1628,8 @@ static void __br_multicast_send_query(struct net_bridge_mcast *brmctx,
struct sk_buff *skb;
u8 igmp_type;

if (!br_multicast_ctx_should_use(brmctx, pmctx))
if (!br_multicast_ctx_should_use(brmctx, pmctx) ||
!br_multicast_ctx_matches_vlan_snooping(brmctx))
return;

again_under_lmqt:
Expand Down Expand Up @@ -1668,7 +1669,7 @@ static void br_multicast_send_query(struct net_bridge_mcast *brmctx,

if (!br_multicast_ctx_should_use(brmctx, pmctx) ||
!br_opt_get(brmctx->br, BROPT_MULTICAST_ENABLED) ||
!br_opt_get(brmctx->br, BROPT_MULTICAST_QUERIER))
!brmctx->multicast_querier)
return;

memset(&br_group.dst, 0, sizeof(br_group.dst));
Expand Down Expand Up @@ -1747,14 +1748,16 @@ static void br_multicast_port_group_rexmit(struct timer_list *t)

spin_lock(&br->multicast_lock);
if (!netif_running(br->dev) || hlist_unhashed(&pg->mglist) ||
!br_opt_get(br, BROPT_MULTICAST_ENABLED) ||
!br_opt_get(br, BROPT_MULTICAST_QUERIER))
!br_opt_get(br, BROPT_MULTICAST_ENABLED))
goto out;

pmctx = br_multicast_pg_to_port_ctx(pg);
if (!pmctx)
goto out;
brmctx = br_multicast_port_ctx_get_global(pmctx);
if (!brmctx->multicast_querier)
goto out;

if (pg->key.addr.proto == htons(ETH_P_IP))
other_query = &brmctx->ip4_other_query;
#if IS_ENABLED(CONFIG_IPV6)
Expand Down Expand Up @@ -1974,8 +1977,7 @@ static void __grp_src_query_marked_and_rexmit(struct net_bridge_mcast *brmctx,
if (ent->flags & BR_SGRP_F_SEND) {
ent->flags &= ~BR_SGRP_F_SEND;
if (ent->timer.expires > lmqt) {
if (br_opt_get(brmctx->br,
BROPT_MULTICAST_QUERIER) &&
if (brmctx->multicast_querier &&
other_query &&
!timer_pending(&other_query->timer))
ent->src_query_rexmit_cnt = lmqc;
Expand All @@ -1984,7 +1986,7 @@ static void __grp_src_query_marked_and_rexmit(struct net_bridge_mcast *brmctx,
}
}

if (!br_opt_get(brmctx->br, BROPT_MULTICAST_QUERIER) ||
if (!brmctx->multicast_querier ||
!other_query || timer_pending(&other_query->timer))
return;

Expand Down Expand Up @@ -2015,7 +2017,7 @@ static void __grp_send_query_and_rexmit(struct net_bridge_mcast *brmctx,
other_query = &brmctx->ip6_other_query;
#endif

if (br_opt_get(brmctx->br, BROPT_MULTICAST_QUERIER) &&
if (brmctx->multicast_querier &&
other_query && !timer_pending(&other_query->timer)) {
lmi = now + brmctx->multicast_last_member_interval;
pg->grp_query_rexmit_cnt = brmctx->multicast_last_member_count - 1;
Expand Down Expand Up @@ -3316,7 +3318,7 @@ br_multicast_leave_group(struct net_bridge_mcast *brmctx,
if (timer_pending(&other_query->timer))
goto out;

if (br_opt_get(brmctx->br, BROPT_MULTICAST_QUERIER)) {
if (brmctx->multicast_querier) {
__br_multicast_send_query(brmctx, pmctx, NULL, NULL, &mp->addr,
false, 0, NULL);

Expand Down Expand Up @@ -3874,9 +3876,9 @@ void br_multicast_open(struct net_bridge *br)
__br_multicast_open(&vlan->br_mcast_ctx);
}
}
} else {
__br_multicast_open(&br->multicast_ctx);
}

__br_multicast_open(&br->multicast_ctx);
}

static void __br_multicast_stop(struct net_bridge_mcast *brmctx)
Expand Down Expand Up @@ -4027,9 +4029,9 @@ void br_multicast_stop(struct net_bridge *br)
__br_multicast_stop(&vlan->br_mcast_ctx);
}
}
} else {
__br_multicast_stop(&br->multicast_ctx);
}

__br_multicast_stop(&br->multicast_ctx);
}

void br_multicast_dev_del(struct net_bridge *br)
Expand All @@ -4051,17 +4053,16 @@ void br_multicast_dev_del(struct net_bridge *br)
rcu_barrier();
}

int br_multicast_set_router(struct net_bridge *br, unsigned long val)
int br_multicast_set_router(struct net_bridge_mcast *brmctx, unsigned long val)
{
struct net_bridge_mcast *brmctx = &br->multicast_ctx;
int err = -EINVAL;

spin_lock_bh(&br->multicast_lock);
spin_lock_bh(&brmctx->br->multicast_lock);

switch (val) {
case MDB_RTR_TYPE_DISABLED:
case MDB_RTR_TYPE_PERM:
br_mc_router_state_change(br, val == MDB_RTR_TYPE_PERM);
br_mc_router_state_change(brmctx->br, val == MDB_RTR_TYPE_PERM);
del_timer(&brmctx->ip4_mc_router_timer);
#if IS_ENABLED(CONFIG_IPV6)
del_timer(&brmctx->ip6_mc_router_timer);
Expand All @@ -4071,13 +4072,13 @@ int br_multicast_set_router(struct net_bridge *br, unsigned long val)
break;
case MDB_RTR_TYPE_TEMP_QUERY:
if (brmctx->multicast_router != MDB_RTR_TYPE_TEMP_QUERY)
br_mc_router_state_change(br, false);
br_mc_router_state_change(brmctx->br, false);
brmctx->multicast_router = val;
err = 0;
break;
}

spin_unlock_bh(&br->multicast_lock);
spin_unlock_bh(&brmctx->br->multicast_lock);

return err;
}
Expand Down Expand Up @@ -4174,6 +4175,9 @@ static void br_multicast_start_querier(struct net_bridge_mcast *brmctx,
{
struct net_bridge_port *port;

if (!br_multicast_ctx_matches_vlan_snooping(brmctx))
return;

__br_multicast_open_query(brmctx->br, query);

rcu_read_lock();
Expand Down Expand Up @@ -4292,18 +4296,17 @@ bool br_multicast_router(const struct net_device *dev)
}
EXPORT_SYMBOL_GPL(br_multicast_router);

int br_multicast_set_querier(struct net_bridge *br, unsigned long val)
int br_multicast_set_querier(struct net_bridge_mcast *brmctx, unsigned long val)
{
struct net_bridge_mcast *brmctx = &br->multicast_ctx;
unsigned long max_delay;

val = !!val;

spin_lock_bh(&br->multicast_lock);
if (br_opt_get(br, BROPT_MULTICAST_QUERIER) == val)
spin_lock_bh(&brmctx->br->multicast_lock);
if (brmctx->multicast_querier == val)
goto unlock;

br_opt_toggle(br, BROPT_MULTICAST_QUERIER, !!val);
WRITE_ONCE(brmctx->multicast_querier, val);
if (!val)
goto unlock;

Expand All @@ -4322,12 +4325,13 @@ int br_multicast_set_querier(struct net_bridge *br, unsigned long val)
#endif

unlock:
spin_unlock_bh(&br->multicast_lock);
spin_unlock_bh(&brmctx->br->multicast_lock);

return 0;
}

int br_multicast_set_igmp_version(struct net_bridge *br, unsigned long val)
int br_multicast_set_igmp_version(struct net_bridge_mcast *brmctx,
unsigned long val)
{
/* Currently we support only version 2 and 3 */
switch (val) {
Expand All @@ -4338,15 +4342,16 @@ int br_multicast_set_igmp_version(struct net_bridge *br, unsigned long val)
return -EINVAL;
}

spin_lock_bh(&br->multicast_lock);
br->multicast_ctx.multicast_igmp_version = val;
spin_unlock_bh(&br->multicast_lock);
spin_lock_bh(&brmctx->br->multicast_lock);
brmctx->multicast_igmp_version = val;
spin_unlock_bh(&brmctx->br->multicast_lock);

return 0;
}

#if IS_ENABLED(CONFIG_IPV6)
int br_multicast_set_mld_version(struct net_bridge *br, unsigned long val)
int br_multicast_set_mld_version(struct net_bridge_mcast *brmctx,
unsigned long val)
{
/* Currently we support version 1 and 2 */
switch (val) {
Expand All @@ -4357,9 +4362,9 @@ int br_multicast_set_mld_version(struct net_bridge *br, unsigned long val)
return -EINVAL;
}

spin_lock_bh(&br->multicast_lock);
br->multicast_ctx.multicast_mld_version = val;
spin_unlock_bh(&br->multicast_lock);
spin_lock_bh(&brmctx->br->multicast_lock);
brmctx->multicast_mld_version = val;
spin_unlock_bh(&brmctx->br->multicast_lock);

return 0;
}
Expand Down
Loading

0 comments on commit e9c130a

Please sign in to comment.