Skip to content

Commit f4b7002

Browse files
Nikolay Aleksandrovdavem330
authored andcommitted
net: bridge: add vlan mcast snooping knob
Add a global knob that controls if vlan multicast snooping is enabled. The proper contexts (vlan or bridge-wide) will be chosen based on the knob when processing packets and changing bridge device state. Note that vlans have their individual mcast snooping enabled by default, but this knob is needed to turn on bridge vlan snooping. It is disabled by default. To enable the knob vlan filtering must also be enabled, it doesn't make sense to have vlan mcast snooping without vlan filtering since that would lead to inconsistencies. Disabling vlan filtering will also automatically disable vlan mcast snooping. Signed-off-by: Nikolay Aleksandrov <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 7b54aaa commit f4b7002

File tree

7 files changed

+175
-48
lines changed

7 files changed

+175
-48
lines changed

include/uapi/linux/if_bridge.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,12 +720,14 @@ struct br_mcast_stats {
720720

721721
/* bridge boolean options
722722
* BR_BOOLOPT_NO_LL_LEARN - disable learning from link-local packets
723+
* BR_BOOLOPT_MCAST_VLAN_SNOOPING - control vlan multicast snooping
723724
*
724725
* IMPORTANT: if adding a new option do not forget to handle
725726
* it in br_boolopt_toggle/get and bridge sysfs
726727
*/
727728
enum br_boolopt_id {
728729
BR_BOOLOPT_NO_LL_LEARN,
730+
BR_BOOLOPT_MCAST_VLAN_SNOOPING,
729731
BR_BOOLOPT_MAX
730732
};
731733

net/bridge/br.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,24 +214,31 @@ static struct notifier_block br_switchdev_notifier = {
214214
int br_boolopt_toggle(struct net_bridge *br, enum br_boolopt_id opt, bool on,
215215
struct netlink_ext_ack *extack)
216216
{
217+
int err = 0;
218+
217219
switch (opt) {
218220
case BR_BOOLOPT_NO_LL_LEARN:
219221
br_opt_toggle(br, BROPT_NO_LL_LEARN, on);
220222
break;
223+
case BR_BOOLOPT_MCAST_VLAN_SNOOPING:
224+
err = br_multicast_toggle_vlan_snooping(br, on, extack);
225+
break;
221226
default:
222227
/* shouldn't be called with unsupported options */
223228
WARN_ON(1);
224229
break;
225230
}
226231

227-
return 0;
232+
return err;
228233
}
229234

230235
int br_boolopt_get(const struct net_bridge *br, enum br_boolopt_id opt)
231236
{
232237
switch (opt) {
233238
case BR_BOOLOPT_NO_LL_LEARN:
234239
return br_opt_get(br, BROPT_NO_LL_LEARN);
240+
case BR_BOOLOPT_MCAST_VLAN_SNOOPING:
241+
return br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED);
235242
default:
236243
/* shouldn't be called with unsupported options */
237244
WARN_ON(1);

net/bridge/br_device.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@ EXPORT_SYMBOL_GPL(nf_br_ops);
2727
/* net device transmit always called with BH disabled */
2828
netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
2929
{
30+
struct net_bridge_mcast_port *pmctx_null = NULL;
3031
struct net_bridge *br = netdev_priv(dev);
3132
struct net_bridge_mcast *brmctx = &br->multicast_ctx;
3233
struct net_bridge_fdb_entry *dst;
3334
struct net_bridge_mdb_entry *mdst;
3435
const struct nf_br_ops *nf_ops;
3536
u8 state = BR_STATE_FORWARDING;
37+
struct net_bridge_vlan *vlan;
3638
const unsigned char *dest;
3739
u16 vid = 0;
3840

@@ -54,7 +56,8 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
5456
skb_reset_mac_header(skb);
5557
skb_pull(skb, ETH_HLEN);
5658

57-
if (!br_allowed_ingress(br, br_vlan_group_rcu(br), skb, &vid, &state))
59+
if (!br_allowed_ingress(br, br_vlan_group_rcu(br), skb, &vid,
60+
&state, &vlan))
5861
goto out;
5962

6063
if (IS_ENABLED(CONFIG_INET) &&
@@ -83,7 +86,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
8386
br_flood(br, skb, BR_PKT_MULTICAST, false, true);
8487
goto out;
8588
}
86-
if (br_multicast_rcv(brmctx, NULL, skb, vid)) {
89+
if (br_multicast_rcv(&brmctx, &pmctx_null, vlan, skb, vid)) {
8790
kfree_skb(skb);
8891
goto out;
8992
}

net/bridge/br_input.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
7373
struct net_bridge_mdb_entry *mdst;
7474
bool local_rcv, mcast_hit = false;
7575
struct net_bridge_mcast *brmctx;
76+
struct net_bridge_vlan *vlan;
7677
struct net_bridge *br;
7778
u16 vid = 0;
7879
u8 state;
@@ -84,7 +85,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
8485
pmctx = &p->multicast_ctx;
8586
state = p->state;
8687
if (!br_allowed_ingress(p->br, nbp_vlan_group_rcu(p), skb, &vid,
87-
&state))
88+
&state, &vlan))
8889
goto out;
8990

9091
nbp_switchdev_frame_mark(p, skb);
@@ -102,7 +103,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
102103
local_rcv = true;
103104
} else {
104105
pkt_type = BR_PKT_MULTICAST;
105-
if (br_multicast_rcv(brmctx, pmctx, skb, vid))
106+
if (br_multicast_rcv(&brmctx, &pmctx, vlan, skb, vid))
106107
goto drop;
107108
}
108109
}

net/bridge/br_multicast.c

Lines changed: 112 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1797,9 +1797,9 @@ void br_multicast_enable_port(struct net_bridge_port *port)
17971797
{
17981798
struct net_bridge *br = port->br;
17991799

1800-
spin_lock(&br->multicast_lock);
1800+
spin_lock_bh(&br->multicast_lock);
18011801
__br_multicast_enable_port_ctx(&port->multicast_ctx);
1802-
spin_unlock(&br->multicast_lock);
1802+
spin_unlock_bh(&br->multicast_lock);
18031803
}
18041804

18051805
static void __br_multicast_disable_port_ctx(struct net_bridge_mcast_port *pmctx)
@@ -1827,9 +1827,9 @@ static void __br_multicast_disable_port_ctx(struct net_bridge_mcast_port *pmctx)
18271827

18281828
void br_multicast_disable_port(struct net_bridge_port *port)
18291829
{
1830-
spin_lock(&port->br->multicast_lock);
1830+
spin_lock_bh(&port->br->multicast_lock);
18311831
__br_multicast_disable_port_ctx(&port->multicast_ctx);
1832-
spin_unlock(&port->br->multicast_lock);
1832+
spin_unlock_bh(&port->br->multicast_lock);
18331833
}
18341834

18351835
static int __grp_src_delete_marked(struct net_bridge_port_group *pg)
@@ -3510,25 +3510,46 @@ static int br_multicast_ipv6_rcv(struct net_bridge_mcast *brmctx,
35103510
}
35113511
#endif
35123512

3513-
int br_multicast_rcv(struct net_bridge_mcast *brmctx,
3514-
struct net_bridge_mcast_port *pmctx,
3513+
int br_multicast_rcv(struct net_bridge_mcast **brmctx,
3514+
struct net_bridge_mcast_port **pmctx,
3515+
struct net_bridge_vlan *vlan,
35153516
struct sk_buff *skb, u16 vid)
35163517
{
35173518
int ret = 0;
35183519

35193520
BR_INPUT_SKB_CB(skb)->igmp = 0;
35203521
BR_INPUT_SKB_CB(skb)->mrouters_only = 0;
35213522

3522-
if (!br_opt_get(brmctx->br, BROPT_MULTICAST_ENABLED))
3523+
if (!br_opt_get((*brmctx)->br, BROPT_MULTICAST_ENABLED))
35233524
return 0;
35243525

3526+
if (br_opt_get((*brmctx)->br, BROPT_MCAST_VLAN_SNOOPING_ENABLED) && vlan) {
3527+
const struct net_bridge_vlan *masterv;
3528+
3529+
/* the vlan has the master flag set only when transmitting
3530+
* through the bridge device
3531+
*/
3532+
if (br_vlan_is_master(vlan)) {
3533+
masterv = vlan;
3534+
*brmctx = &vlan->br_mcast_ctx;
3535+
*pmctx = NULL;
3536+
} else {
3537+
masterv = vlan->brvlan;
3538+
*brmctx = &vlan->brvlan->br_mcast_ctx;
3539+
*pmctx = &vlan->port_mcast_ctx;
3540+
}
3541+
3542+
if (!(masterv->priv_flags & BR_VLFLAG_GLOBAL_MCAST_ENABLED))
3543+
return 0;
3544+
}
3545+
35253546
switch (skb->protocol) {
35263547
case htons(ETH_P_IP):
3527-
ret = br_multicast_ipv4_rcv(brmctx, pmctx, skb, vid);
3548+
ret = br_multicast_ipv4_rcv(*brmctx, *pmctx, skb, vid);
35283549
break;
35293550
#if IS_ENABLED(CONFIG_IPV6)
35303551
case htons(ETH_P_IPV6):
3531-
ret = br_multicast_ipv6_rcv(brmctx, pmctx, skb, vid);
3552+
ret = br_multicast_ipv6_rcv(*brmctx, *pmctx, skb, vid);
35323553
break;
35333554
#endif
35343555
}
@@ -3727,20 +3748,22 @@ static void __br_multicast_open(struct net_bridge_mcast *brmctx)
37273748

37283749
void br_multicast_open(struct net_bridge *br)
37293750
{
3730-
struct net_bridge_vlan_group *vg;
3731-
struct net_bridge_vlan *vlan;
3732-
37333751
ASSERT_RTNL();
37343752

3735-
vg = br_vlan_group(br);
3736-
if (vg) {
3737-
list_for_each_entry(vlan, &vg->vlan_list, vlist) {
3738-
struct net_bridge_mcast *brmctx;
3739-
3740-
brmctx = &vlan->br_mcast_ctx;
3741-
if (br_vlan_is_brentry(vlan) &&
3742-
!br_multicast_ctx_vlan_disabled(brmctx))
3743-
__br_multicast_open(&vlan->br_mcast_ctx);
3753+
if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) {
3754+
struct net_bridge_vlan_group *vg;
3755+
struct net_bridge_vlan *vlan;
3756+
3757+
vg = br_vlan_group(br);
3758+
if (vg) {
3759+
list_for_each_entry(vlan, &vg->vlan_list, vlist) {
3760+
struct net_bridge_mcast *brmctx;
3761+
3762+
brmctx = &vlan->br_mcast_ctx;
3763+
if (br_vlan_is_brentry(vlan) &&
3764+
!br_multicast_ctx_vlan_disabled(brmctx))
3765+
__br_multicast_open(&vlan->br_mcast_ctx);
3766+
}
37443767
}
37453768
}
37463769

@@ -3804,22 +3827,80 @@ void br_multicast_toggle_one_vlan(struct net_bridge_vlan *vlan, bool on)
38043827
}
38053828
}
38063829

3807-
void br_multicast_stop(struct net_bridge *br)
3830+
void br_multicast_toggle_vlan(struct net_bridge_vlan *vlan, bool on)
3831+
{
3832+
struct net_bridge_port *p;
3833+
3834+
if (WARN_ON_ONCE(!br_vlan_is_master(vlan)))
3835+
return;
3836+
3837+
list_for_each_entry(p, &vlan->br->port_list, list) {
3838+
struct net_bridge_vlan *vport;
3839+
3840+
vport = br_vlan_find(nbp_vlan_group(p), vlan->vid);
3841+
if (!vport)
3842+
continue;
3843+
br_multicast_toggle_one_vlan(vport, on);
3844+
}
3845+
}
3846+
3847+
int br_multicast_toggle_vlan_snooping(struct net_bridge *br, bool on,
3848+
struct netlink_ext_ack *extack)
38083849
{
38093850
struct net_bridge_vlan_group *vg;
38103851
struct net_bridge_vlan *vlan;
3852+
struct net_bridge_port *p;
38113853

3812-
ASSERT_RTNL();
3854+
if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED) == on)
3855+
return 0;
3856+
3857+
if (on && !br_opt_get(br, BROPT_VLAN_ENABLED)) {
3858+
NL_SET_ERR_MSG_MOD(extack, "Cannot enable multicast vlan snooping with vlan filtering disabled");
3859+
return -EINVAL;
3860+
}
38133861

38143862
vg = br_vlan_group(br);
3815-
if (vg) {
3816-
list_for_each_entry(vlan, &vg->vlan_list, vlist) {
3817-
struct net_bridge_mcast *brmctx;
3818-
3819-
brmctx = &vlan->br_mcast_ctx;
3820-
if (br_vlan_is_brentry(vlan) &&
3821-
!br_multicast_ctx_vlan_disabled(brmctx))
3822-
__br_multicast_stop(&vlan->br_mcast_ctx);
3863+
if (!vg)
3864+
return 0;
3865+
3866+
br_opt_toggle(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED, on);
3867+
3868+
/* disable/enable non-vlan mcast contexts based on vlan snooping */
3869+
if (on)
3870+
__br_multicast_stop(&br->multicast_ctx);
3871+
else
3872+
__br_multicast_open(&br->multicast_ctx);
3873+
list_for_each_entry(p, &br->port_list, list) {
3874+
if (on)
3875+
br_multicast_disable_port(p);
3876+
else
3877+
br_multicast_enable_port(p);
3878+
}
3879+
3880+
list_for_each_entry(vlan, &vg->vlan_list, vlist)
3881+
br_multicast_toggle_vlan(vlan, on);
3882+
3883+
return 0;
3884+
}
3885+
3886+
void br_multicast_stop(struct net_bridge *br)
3887+
{
3888+
ASSERT_RTNL();
3889+
3890+
if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) {
3891+
struct net_bridge_vlan_group *vg;
3892+
struct net_bridge_vlan *vlan;
3893+
3894+
vg = br_vlan_group(br);
3895+
if (vg) {
3896+
list_for_each_entry(vlan, &vg->vlan_list, vlist) {
3897+
struct net_bridge_mcast *brmctx;
3898+
3899+
brmctx = &vlan->br_mcast_ctx;
3900+
if (br_vlan_is_brentry(vlan) &&
3901+
!br_multicast_ctx_vlan_disabled(brmctx))
3902+
__br_multicast_stop(&vlan->br_mcast_ctx);
3903+
}
38233904
}
38243905
}
38253906

0 commit comments

Comments
 (0)