Merge branch 'bridge-global-mcast'
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>
This commit is contained in:
commit
e9c130ad66
@ -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)
|
||||
|
@ -16,57 +16,59 @@
|
||||
|
||||
#include "br_private.h"
|
||||
|
||||
static bool br_rports_have_mc_router(struct net_bridge_mcast *brmctx)
|
||||
static bool
|
||||
br_ip4_rports_get_timer(struct net_bridge_mcast_port *pmctx,
|
||||
unsigned long *timer)
|
||||
{
|
||||
#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
|
||||
*timer = br_timer_value(&pmctx->ip4_mc_router_timer);
|
||||
return !hlist_unhashed(&pmctx->ip4_rlist);
|
||||
}
|
||||
|
||||
static bool
|
||||
br_ip4_rports_get_timer(struct net_bridge_port *port, unsigned long *timer)
|
||||
{
|
||||
*timer = br_timer_value(&port->multicast_ctx.ip4_mc_router_timer);
|
||||
return !hlist_unhashed(&port->multicast_ctx.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;
|
||||
@ -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;
|
||||
}
|
||||
@ -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)
|
||||
@ -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;
|
||||
|
@ -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:
|
||||
@ -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));
|
||||
@ -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)
|
||||
@ -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;
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
@ -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);
|
||||
|
||||
@ -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)
|
||||
@ -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)
|
||||
@ -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);
|
||||
@ -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;
|
||||
}
|
||||
@ -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();
|
||||
@ -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;
|
||||
|
||||
@ -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) {
|
||||
@ -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) {
|
||||
@ -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;
|
||||
}
|
||||
|
@ -1286,7 +1286,8 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
|
||||
if (data[IFLA_BR_MCAST_ROUTER]) {
|
||||
u8 multicast_router = nla_get_u8(data[IFLA_BR_MCAST_ROUTER]);
|
||||
|
||||
err = br_multicast_set_router(br, multicast_router);
|
||||
err = br_multicast_set_router(&br->multicast_ctx,
|
||||
multicast_router);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
@ -1309,7 +1310,8 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
|
||||
if (data[IFLA_BR_MCAST_QUERIER]) {
|
||||
u8 mcast_querier = nla_get_u8(data[IFLA_BR_MCAST_QUERIER]);
|
||||
|
||||
err = br_multicast_set_querier(br, mcast_querier);
|
||||
err = br_multicast_set_querier(&br->multicast_ctx,
|
||||
mcast_querier);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
@ -1380,7 +1382,8 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
|
||||
__u8 igmp_version;
|
||||
|
||||
igmp_version = nla_get_u8(data[IFLA_BR_MCAST_IGMP_VERSION]);
|
||||
err = br_multicast_set_igmp_version(br, igmp_version);
|
||||
err = br_multicast_set_igmp_version(&br->multicast_ctx,
|
||||
igmp_version);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
@ -1390,7 +1393,8 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
|
||||
__u8 mld_version;
|
||||
|
||||
mld_version = nla_get_u8(data[IFLA_BR_MCAST_MLD_VERSION]);
|
||||
err = br_multicast_set_mld_version(br, mld_version);
|
||||
err = br_multicast_set_mld_version(&br->multicast_ctx,
|
||||
mld_version);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
@ -1573,7 +1577,7 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
|
||||
nla_put_u8(skb, IFLA_BR_MCAST_QUERY_USE_IFADDR,
|
||||
br_opt_get(br, BROPT_MULTICAST_QUERY_USE_IFADDR)) ||
|
||||
nla_put_u8(skb, IFLA_BR_MCAST_QUERIER,
|
||||
br_opt_get(br, BROPT_MULTICAST_QUERIER)) ||
|
||||
br->multicast_ctx.multicast_querier) ||
|
||||
nla_put_u8(skb, IFLA_BR_MCAST_STATS_ENABLED,
|
||||
br_opt_get(br, BROPT_MULTICAST_STATS_ENABLED)) ||
|
||||
nla_put_u32(skb, IFLA_BR_MCAST_HASH_ELASTICITY, RHT_ELASTICITY) ||
|
||||
|
@ -118,6 +118,7 @@ struct net_bridge_mcast {
|
||||
u32 multicast_last_member_count;
|
||||
u32 multicast_startup_query_count;
|
||||
|
||||
u8 multicast_querier;
|
||||
u8 multicast_igmp_version;
|
||||
u8 multicast_router;
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
@ -431,7 +432,6 @@ enum net_bridge_opts {
|
||||
BROPT_NF_CALL_ARPTABLES,
|
||||
BROPT_GROUP_ADDR_SET,
|
||||
BROPT_MULTICAST_ENABLED,
|
||||
BROPT_MULTICAST_QUERIER,
|
||||
BROPT_MULTICAST_QUERY_USE_IFADDR,
|
||||
BROPT_MULTICAST_STATS_ENABLED,
|
||||
BROPT_HAS_IPV6_ADDR,
|
||||
@ -877,15 +877,17 @@ void br_multicast_dev_del(struct net_bridge *br);
|
||||
void br_multicast_flood(struct net_bridge_mdb_entry *mdst, struct sk_buff *skb,
|
||||
struct net_bridge_mcast *brmctx,
|
||||
bool local_rcv, bool local_orig);
|
||||
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);
|
||||
int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val);
|
||||
int br_multicast_toggle(struct net_bridge *br, unsigned long val,
|
||||
struct netlink_ext_ack *extack);
|
||||
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);
|
||||
int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val);
|
||||
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);
|
||||
#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);
|
||||
#endif
|
||||
struct net_bridge_mdb_entry *
|
||||
br_mdb_ip_get(struct net_bridge *br, struct br_ip *dst);
|
||||
@ -943,6 +945,8 @@ bool br_multicast_toggle_global_vlan(struct net_bridge_vlan *vlan, bool on);
|
||||
int br_mdb_replay(struct net_device *br_dev, struct net_device *dev,
|
||||
const void *ctx, bool adding, struct notifier_block *nb,
|
||||
struct netlink_ext_ack *extack);
|
||||
int br_rports_fill_info(struct sk_buff *skb,
|
||||
const struct net_bridge_mcast *brmctx);
|
||||
|
||||
static inline bool br_group_is_l2(const struct br_ip *group)
|
||||
{
|
||||
@ -1026,7 +1030,7 @@ __br_multicast_querier_exists(struct net_bridge_mcast *brmctx,
|
||||
{
|
||||
bool own_querier_enabled;
|
||||
|
||||
if (br_opt_get(brmctx->br, BROPT_MULTICAST_QUERIER)) {
|
||||
if (brmctx->multicast_querier) {
|
||||
if (is_ipv6 && !br_opt_get(brmctx->br, BROPT_HAS_IPV6_ADDR))
|
||||
own_querier_enabled = false;
|
||||
else
|
||||
@ -1165,6 +1169,61 @@ br_multicast_port_ctx_state_stopped(const struct net_bridge_mcast_port *pmctx)
|
||||
(br_multicast_port_ctx_is_vlan(pmctx) &&
|
||||
pmctx->vlan->state == BR_STATE_BLOCKING);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
br_rports_have_mc_router(const 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 inline bool
|
||||
br_multicast_ctx_options_equal(const struct net_bridge_mcast *brmctx1,
|
||||
const struct net_bridge_mcast *brmctx2)
|
||||
{
|
||||
return brmctx1->multicast_igmp_version ==
|
||||
brmctx2->multicast_igmp_version &&
|
||||
brmctx1->multicast_last_member_count ==
|
||||
brmctx2->multicast_last_member_count &&
|
||||
brmctx1->multicast_startup_query_count ==
|
||||
brmctx2->multicast_startup_query_count &&
|
||||
brmctx1->multicast_last_member_interval ==
|
||||
brmctx2->multicast_last_member_interval &&
|
||||
brmctx1->multicast_membership_interval ==
|
||||
brmctx2->multicast_membership_interval &&
|
||||
brmctx1->multicast_querier_interval ==
|
||||
brmctx2->multicast_querier_interval &&
|
||||
brmctx1->multicast_query_interval ==
|
||||
brmctx2->multicast_query_interval &&
|
||||
brmctx1->multicast_query_response_interval ==
|
||||
brmctx2->multicast_query_response_interval &&
|
||||
brmctx1->multicast_startup_query_interval ==
|
||||
brmctx2->multicast_startup_query_interval &&
|
||||
brmctx1->multicast_querier == brmctx2->multicast_querier &&
|
||||
brmctx1->multicast_router == brmctx2->multicast_router &&
|
||||
!br_rports_have_mc_router(brmctx1) &&
|
||||
!br_rports_have_mc_router(brmctx2) &&
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
brmctx1->multicast_mld_version ==
|
||||
brmctx2->multicast_mld_version &&
|
||||
#endif
|
||||
true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
br_multicast_ctx_matches_vlan_snooping(const struct net_bridge_mcast *brmctx)
|
||||
{
|
||||
bool vlan_snooping_enabled;
|
||||
|
||||
vlan_snooping_enabled = !!br_opt_get(brmctx->br,
|
||||
BROPT_MCAST_VLAN_SNOOPING_ENABLED);
|
||||
|
||||
return !!(vlan_snooping_enabled == br_multicast_ctx_is_vlan(brmctx));
|
||||
}
|
||||
#else
|
||||
static inline int br_multicast_rcv(struct net_bridge_mcast **brmctx,
|
||||
struct net_bridge_mcast_port **pmctx,
|
||||
@ -1330,6 +1389,13 @@ static inline int br_mdb_replay(struct net_device *br_dev,
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
br_multicast_ctx_options_equal(const struct net_bridge_mcast *brmctx1,
|
||||
const struct net_bridge_mcast *brmctx2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* br_vlan.c */
|
||||
|
@ -390,7 +390,7 @@ static ssize_t multicast_router_show(struct device *d,
|
||||
static int set_multicast_router(struct net_bridge *br, unsigned long val,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
return br_multicast_set_router(br, val);
|
||||
return br_multicast_set_router(&br->multicast_ctx, val);
|
||||
}
|
||||
|
||||
static ssize_t multicast_router_store(struct device *d,
|
||||
@ -447,13 +447,13 @@ static ssize_t multicast_querier_show(struct device *d,
|
||||
char *buf)
|
||||
{
|
||||
struct net_bridge *br = to_bridge(d);
|
||||
return sprintf(buf, "%d\n", br_opt_get(br, BROPT_MULTICAST_QUERIER));
|
||||
return sprintf(buf, "%d\n", br->multicast_ctx.multicast_querier);
|
||||
}
|
||||
|
||||
static int set_multicast_querier(struct net_bridge *br, unsigned long val,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
return br_multicast_set_querier(br, val);
|
||||
return br_multicast_set_querier(&br->multicast_ctx, val);
|
||||
}
|
||||
|
||||
static ssize_t multicast_querier_store(struct device *d,
|
||||
@ -520,7 +520,7 @@ static ssize_t multicast_igmp_version_show(struct device *d,
|
||||
static int set_multicast_igmp_version(struct net_bridge *br, unsigned long val,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
return br_multicast_set_igmp_version(br, val);
|
||||
return br_multicast_set_igmp_version(&br->multicast_ctx, val);
|
||||
}
|
||||
|
||||
static ssize_t multicast_igmp_version_store(struct device *d,
|
||||
@ -757,7 +757,7 @@ static ssize_t multicast_mld_version_show(struct device *d,
|
||||
static int set_multicast_mld_version(struct net_bridge *br, unsigned long val,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
return br_multicast_set_mld_version(br, val);
|
||||
return br_multicast_set_mld_version(&br->multicast_ctx, val);
|
||||
}
|
||||
|
||||
static ssize_t multicast_mld_version_store(struct device *d,
|
||||
|
@ -264,12 +264,16 @@ bool br_vlan_global_opts_can_enter_range(const struct net_bridge_vlan *v_curr,
|
||||
{
|
||||
return v_curr->vid - r_end->vid == 1 &&
|
||||
((v_curr->priv_flags ^ r_end->priv_flags) &
|
||||
BR_VLFLAG_GLOBAL_MCAST_ENABLED) == 0;
|
||||
BR_VLFLAG_GLOBAL_MCAST_ENABLED) == 0 &&
|
||||
br_multicast_ctx_options_equal(&v_curr->br_mcast_ctx,
|
||||
&r_end->br_mcast_ctx);
|
||||
}
|
||||
|
||||
bool br_vlan_global_opts_fill(struct sk_buff *skb, u16 vid, u16 vid_range,
|
||||
const struct net_bridge_vlan *v_opts)
|
||||
{
|
||||
struct nlattr *nest2 __maybe_unused;
|
||||
u64 clockval __maybe_unused;
|
||||
struct nlattr *nest;
|
||||
|
||||
nest = nla_nest_start(skb, BRIDGE_VLANDB_GLOBAL_OPTIONS);
|
||||
@ -285,8 +289,66 @@ bool br_vlan_global_opts_fill(struct sk_buff *skb, u16 vid, u16 vid_range,
|
||||
|
||||
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
|
||||
if (nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING,
|
||||
!!(v_opts->priv_flags & BR_VLFLAG_GLOBAL_MCAST_ENABLED)))
|
||||
!!(v_opts->priv_flags & BR_VLFLAG_GLOBAL_MCAST_ENABLED)) ||
|
||||
nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION,
|
||||
v_opts->br_mcast_ctx.multicast_igmp_version) ||
|
||||
nla_put_u32(skb, BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT,
|
||||
v_opts->br_mcast_ctx.multicast_last_member_count) ||
|
||||
nla_put_u32(skb, BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT,
|
||||
v_opts->br_mcast_ctx.multicast_startup_query_count) ||
|
||||
nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_QUERIER,
|
||||
v_opts->br_mcast_ctx.multicast_querier) ||
|
||||
nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_ROUTER,
|
||||
v_opts->br_mcast_ctx.multicast_router))
|
||||
goto out_err;
|
||||
|
||||
clockval = jiffies_to_clock_t(v_opts->br_mcast_ctx.multicast_last_member_interval);
|
||||
if (nla_put_u64_64bit(skb, BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL,
|
||||
clockval, BRIDGE_VLANDB_GOPTS_PAD))
|
||||
goto out_err;
|
||||
clockval = jiffies_to_clock_t(v_opts->br_mcast_ctx.multicast_membership_interval);
|
||||
if (nla_put_u64_64bit(skb, BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL,
|
||||
clockval, BRIDGE_VLANDB_GOPTS_PAD))
|
||||
goto out_err;
|
||||
clockval = jiffies_to_clock_t(v_opts->br_mcast_ctx.multicast_querier_interval);
|
||||
if (nla_put_u64_64bit(skb, BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL,
|
||||
clockval, BRIDGE_VLANDB_GOPTS_PAD))
|
||||
goto out_err;
|
||||
clockval = jiffies_to_clock_t(v_opts->br_mcast_ctx.multicast_query_interval);
|
||||
if (nla_put_u64_64bit(skb, BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL,
|
||||
clockval, BRIDGE_VLANDB_GOPTS_PAD))
|
||||
goto out_err;
|
||||
clockval = jiffies_to_clock_t(v_opts->br_mcast_ctx.multicast_query_response_interval);
|
||||
if (nla_put_u64_64bit(skb, BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL,
|
||||
clockval, BRIDGE_VLANDB_GOPTS_PAD))
|
||||
goto out_err;
|
||||
clockval = jiffies_to_clock_t(v_opts->br_mcast_ctx.multicast_startup_query_interval);
|
||||
if (nla_put_u64_64bit(skb, BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL,
|
||||
clockval, BRIDGE_VLANDB_GOPTS_PAD))
|
||||
goto out_err;
|
||||
|
||||
if (br_rports_have_mc_router(&v_opts->br_mcast_ctx)) {
|
||||
nest2 = nla_nest_start(skb,
|
||||
BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS);
|
||||
if (!nest2)
|
||||
goto out_err;
|
||||
|
||||
rcu_read_lock();
|
||||
if (br_rports_fill_info(skb, &v_opts->br_mcast_ctx)) {
|
||||
rcu_read_unlock();
|
||||
nla_nest_cancel(skb, nest2);
|
||||
goto out_err;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
nla_nest_end(skb, nest2);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
if (nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION,
|
||||
v_opts->br_mcast_ctx.multicast_mld_version))
|
||||
goto out_err;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
nla_nest_end(skb, nest);
|
||||
@ -305,6 +367,18 @@ static size_t rtnl_vlan_global_opts_nlmsg_size(void)
|
||||
+ nla_total_size(sizeof(u16)) /* BRIDGE_VLANDB_GOPTS_ID */
|
||||
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
|
||||
+ nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING */
|
||||
+ nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION */
|
||||
+ nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION */
|
||||
+ nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT */
|
||||
+ nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT */
|
||||
+ nla_total_size(sizeof(u64)) /* BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL */
|
||||
+ nla_total_size(sizeof(u64)) /* BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL */
|
||||
+ nla_total_size(sizeof(u64)) /* BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL */
|
||||
+ nla_total_size(sizeof(u64)) /* BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL */
|
||||
+ nla_total_size(sizeof(u64)) /* BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL */
|
||||
+ nla_total_size(sizeof(u64)) /* BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL */
|
||||
+ nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_QUERIER */
|
||||
+ nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_ROUTER */
|
||||
#endif
|
||||
+ nla_total_size(sizeof(u16)); /* BRIDGE_VLANDB_GOPTS_RANGE */
|
||||
}
|
||||
@ -359,6 +433,8 @@ static int br_vlan_process_global_one_opts(const struct net_bridge *br,
|
||||
bool *changed,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
int err __maybe_unused;
|
||||
|
||||
*changed = false;
|
||||
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
|
||||
if (tb[BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING]) {
|
||||
@ -368,6 +444,100 @@ static int br_vlan_process_global_one_opts(const struct net_bridge *br,
|
||||
if (br_multicast_toggle_global_vlan(v, !!mc_snooping))
|
||||
*changed = true;
|
||||
}
|
||||
if (tb[BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION]) {
|
||||
u8 ver;
|
||||
|
||||
ver = nla_get_u8(tb[BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION]);
|
||||
err = br_multicast_set_igmp_version(&v->br_mcast_ctx, ver);
|
||||
if (err)
|
||||
return err;
|
||||
*changed = true;
|
||||
}
|
||||
if (tb[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT]) {
|
||||
u32 cnt;
|
||||
|
||||
cnt = nla_get_u32(tb[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT]);
|
||||
v->br_mcast_ctx.multicast_last_member_count = cnt;
|
||||
*changed = true;
|
||||
}
|
||||
if (tb[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT]) {
|
||||
u32 cnt;
|
||||
|
||||
cnt = nla_get_u32(tb[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT]);
|
||||
v->br_mcast_ctx.multicast_startup_query_count = cnt;
|
||||
*changed = true;
|
||||
}
|
||||
if (tb[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL]) {
|
||||
u64 val;
|
||||
|
||||
val = nla_get_u64(tb[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL]);
|
||||
v->br_mcast_ctx.multicast_last_member_interval = clock_t_to_jiffies(val);
|
||||
*changed = true;
|
||||
}
|
||||
if (tb[BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL]) {
|
||||
u64 val;
|
||||
|
||||
val = nla_get_u64(tb[BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL]);
|
||||
v->br_mcast_ctx.multicast_membership_interval = clock_t_to_jiffies(val);
|
||||
*changed = true;
|
||||
}
|
||||
if (tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL]) {
|
||||
u64 val;
|
||||
|
||||
val = nla_get_u64(tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL]);
|
||||
v->br_mcast_ctx.multicast_querier_interval = clock_t_to_jiffies(val);
|
||||
*changed = true;
|
||||
}
|
||||
if (tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL]) {
|
||||
u64 val;
|
||||
|
||||
val = nla_get_u64(tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL]);
|
||||
v->br_mcast_ctx.multicast_query_interval = clock_t_to_jiffies(val);
|
||||
*changed = true;
|
||||
}
|
||||
if (tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL]) {
|
||||
u64 val;
|
||||
|
||||
val = nla_get_u64(tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL]);
|
||||
v->br_mcast_ctx.multicast_query_response_interval = clock_t_to_jiffies(val);
|
||||
*changed = true;
|
||||
}
|
||||
if (tb[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL]) {
|
||||
u64 val;
|
||||
|
||||
val = nla_get_u64(tb[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL]);
|
||||
v->br_mcast_ctx.multicast_startup_query_interval = clock_t_to_jiffies(val);
|
||||
*changed = true;
|
||||
}
|
||||
if (tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER]) {
|
||||
u8 val;
|
||||
|
||||
val = nla_get_u8(tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER]);
|
||||
err = br_multicast_set_querier(&v->br_mcast_ctx, val);
|
||||
if (err)
|
||||
return err;
|
||||
*changed = true;
|
||||
}
|
||||
if (tb[BRIDGE_VLANDB_GOPTS_MCAST_ROUTER]) {
|
||||
u8 val;
|
||||
|
||||
val = nla_get_u8(tb[BRIDGE_VLANDB_GOPTS_MCAST_ROUTER]);
|
||||
err = br_multicast_set_router(&v->br_mcast_ctx, val);
|
||||
if (err)
|
||||
return err;
|
||||
*changed = true;
|
||||
}
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
if (tb[BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION]) {
|
||||
u8 ver;
|
||||
|
||||
ver = nla_get_u8(tb[BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION]);
|
||||
err = br_multicast_set_mld_version(&v->br_mcast_ctx, ver);
|
||||
if (err)
|
||||
return err;
|
||||
*changed = true;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
@ -377,6 +547,18 @@ static const struct nla_policy br_vlan_db_gpol[BRIDGE_VLANDB_GOPTS_MAX + 1] = {
|
||||
[BRIDGE_VLANDB_GOPTS_ID] = { .type = NLA_U16 },
|
||||
[BRIDGE_VLANDB_GOPTS_RANGE] = { .type = NLA_U16 },
|
||||
[BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING] = { .type = NLA_U8 },
|
||||
[BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION] = { .type = NLA_U8 },
|
||||
[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL] = { .type = NLA_U64 },
|
||||
[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER] = { .type = NLA_U8 },
|
||||
[BRIDGE_VLANDB_GOPTS_MCAST_ROUTER] = { .type = NLA_U8 },
|
||||
[BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION] = { .type = NLA_U8 },
|
||||
[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT] = { .type = NLA_U32 },
|
||||
[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT] = { .type = NLA_U32 },
|
||||
[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL] = { .type = NLA_U64 },
|
||||
[BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL] = { .type = NLA_U64 },
|
||||
[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL] = { .type = NLA_U64 },
|
||||
[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL] = { .type = NLA_U64 },
|
||||
[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL] = { .type = NLA_U64 },
|
||||
};
|
||||
|
||||
int br_vlan_rtm_process_global_options(struct net_device *dev,
|
||||
|
Loading…
Reference in New Issue
Block a user