Skip to content

Commit a580c76

Browse files
Nikolay Aleksandrovdavem330
authored andcommitted
net: bridge: vlan: add per-vlan state
The first per-vlan option added is state, it is needed for EVPN and for per-vlan STP. The state allows to control the forwarding on per-vlan basis. The vlan state is considered only if the port state is forwarding in order to avoid conflicts and be consistent. br_allowed_egress is called only when the state is forwarding, but the ingress case is a bit more complicated due to the fact that we may have the transition between port:BR_STATE_FORWARDING -> vlan:BR_STATE_LEARNING which should still allow the bridge to learn from the packet after vlan filtering and it will be dropped after that. Also to optimize the pvid state check we keep a copy in the vlan group to avoid one lookup. The state members are modified with *_ONCE() to annotate the lockless access. Signed-off-by: Nikolay Aleksandrov <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent a5d29ae commit a580c76

File tree

6 files changed

+134
-19
lines changed

6 files changed

+134
-19
lines changed

include/uapi/linux/if_bridge.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ enum {
191191
BRIDGE_VLANDB_ENTRY_UNSPEC,
192192
BRIDGE_VLANDB_ENTRY_INFO,
193193
BRIDGE_VLANDB_ENTRY_RANGE,
194+
BRIDGE_VLANDB_ENTRY_STATE,
194195
__BRIDGE_VLANDB_ENTRY_MAX,
195196
};
196197
#define BRIDGE_VLANDB_ENTRY_MAX (__BRIDGE_VLANDB_ENTRY_MAX - 1)

net/bridge/br_device.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
3232
struct net_bridge_mdb_entry *mdst;
3333
struct pcpu_sw_netstats *brstats = this_cpu_ptr(br->stats);
3434
const struct nf_br_ops *nf_ops;
35+
u8 state = BR_STATE_FORWARDING;
3536
const unsigned char *dest;
3637
struct ethhdr *eth;
3738
u16 vid = 0;
@@ -56,7 +57,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
5657
eth = eth_hdr(skb);
5758
skb_pull(skb, ETH_HLEN);
5859

59-
if (!br_allowed_ingress(br, br_vlan_group_rcu(br), skb, &vid))
60+
if (!br_allowed_ingress(br, br_vlan_group_rcu(br), skb, &vid, &state))
6061
goto out;
6162

6263
if (IS_ENABLED(CONFIG_INET) &&

net/bridge/br_input.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,14 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
7676
bool local_rcv, mcast_hit = false;
7777
struct net_bridge *br;
7878
u16 vid = 0;
79+
u8 state;
7980

8081
if (!p || p->state == BR_STATE_DISABLED)
8182
goto drop;
8283

83-
if (!br_allowed_ingress(p->br, nbp_vlan_group_rcu(p), skb, &vid))
84+
state = p->state;
85+
if (!br_allowed_ingress(p->br, nbp_vlan_group_rcu(p), skb, &vid,
86+
&state))
8487
goto out;
8588

8689
nbp_switchdev_frame_mark(p, skb);
@@ -103,7 +106,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
103106
}
104107
}
105108

106-
if (p->state == BR_STATE_LEARNING)
109+
if (state == BR_STATE_LEARNING)
107110
goto drop;
108111

109112
BR_INPUT_SKB_CB(skb)->brdev = br->dev;

net/bridge/br_private.h

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ enum {
113113
* @vid: VLAN id
114114
* @flags: bridge vlan flags
115115
* @priv_flags: private (in-kernel) bridge vlan flags
116+
* @state: STP state (e.g. blocking, learning, forwarding)
116117
* @stats: per-cpu VLAN statistics
117118
* @br: if MASTER flag set, this points to a bridge struct
118119
* @port: if MASTER flag unset, this points to a port struct
@@ -133,6 +134,7 @@ struct net_bridge_vlan {
133134
u16 vid;
134135
u16 flags;
135136
u16 priv_flags;
137+
u8 state;
136138
struct br_vlan_stats __percpu *stats;
137139
union {
138140
struct net_bridge *br;
@@ -157,6 +159,7 @@ struct net_bridge_vlan {
157159
* @vlan_list: sorted VLAN entry list
158160
* @num_vlans: number of total VLAN entries
159161
* @pvid: PVID VLAN id
162+
* @pvid_state: PVID's STP state (e.g. forwarding, learning, blocking)
160163
*
161164
* IMPORTANT: Be careful when checking if there're VLAN entries using list
162165
* primitives because the bridge can have entries in its list which
@@ -170,6 +173,7 @@ struct net_bridge_vlan_group {
170173
struct list_head vlan_list;
171174
u16 num_vlans;
172175
u16 pvid;
176+
u8 pvid_state;
173177
};
174178

175179
/* bridge fdb flags */
@@ -935,7 +939,7 @@ static inline int br_multicast_igmp_type(const struct sk_buff *skb)
935939
#ifdef CONFIG_BRIDGE_VLAN_FILTERING
936940
bool br_allowed_ingress(const struct net_bridge *br,
937941
struct net_bridge_vlan_group *vg, struct sk_buff *skb,
938-
u16 *vid);
942+
u16 *vid, u8 *state);
939943
bool br_allowed_egress(struct net_bridge_vlan_group *vg,
940944
const struct sk_buff *skb);
941945
bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid);
@@ -1037,7 +1041,7 @@ static inline u16 br_vlan_flags(const struct net_bridge_vlan *v, u16 pvid)
10371041
static inline bool br_allowed_ingress(const struct net_bridge *br,
10381042
struct net_bridge_vlan_group *vg,
10391043
struct sk_buff *skb,
1040-
u16 *vid)
1044+
u16 *vid, u8 *state)
10411045
{
10421046
return true;
10431047
}
@@ -1205,6 +1209,41 @@ int br_vlan_process_options(const struct net_bridge *br,
12051209
struct net_bridge_vlan *range_end,
12061210
struct nlattr **tb,
12071211
struct netlink_ext_ack *extack);
1212+
1213+
/* vlan state manipulation helpers using *_ONCE to annotate lock-free access */
1214+
static inline u8 br_vlan_get_state(const struct net_bridge_vlan *v)
1215+
{
1216+
return READ_ONCE(v->state);
1217+
}
1218+
1219+
static inline void br_vlan_set_state(struct net_bridge_vlan *v, u8 state)
1220+
{
1221+
WRITE_ONCE(v->state, state);
1222+
}
1223+
1224+
static inline u8 br_vlan_get_pvid_state(const struct net_bridge_vlan_group *vg)
1225+
{
1226+
return READ_ONCE(vg->pvid_state);
1227+
}
1228+
1229+
static inline void br_vlan_set_pvid_state(struct net_bridge_vlan_group *vg,
1230+
u8 state)
1231+
{
1232+
WRITE_ONCE(vg->pvid_state, state);
1233+
}
1234+
1235+
/* learn_allow is true at ingress and false at egress */
1236+
static inline bool br_vlan_state_allowed(u8 state, bool learn_allow)
1237+
{
1238+
switch (state) {
1239+
case BR_STATE_LEARNING:
1240+
return learn_allow;
1241+
case BR_STATE_FORWARDING:
1242+
return true;
1243+
default:
1244+
return false;
1245+
}
1246+
}
12081247
#endif
12091248

12101249
struct nf_br_ops {

net/bridge/br_vlan.c

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,15 @@ static struct net_bridge_vlan *br_vlan_lookup(struct rhashtable *tbl, u16 vid)
3434
return rhashtable_lookup_fast(tbl, &vid, br_vlan_rht_params);
3535
}
3636

37-
static bool __vlan_add_pvid(struct net_bridge_vlan_group *vg, u16 vid)
37+
static bool __vlan_add_pvid(struct net_bridge_vlan_group *vg,
38+
const struct net_bridge_vlan *v)
3839
{
39-
if (vg->pvid == vid)
40+
if (vg->pvid == v->vid)
4041
return false;
4142

4243
smp_wmb();
43-
vg->pvid = vid;
44+
br_vlan_set_pvid_state(vg, v->state);
45+
vg->pvid = v->vid;
4446

4547
return true;
4648
}
@@ -69,7 +71,7 @@ static bool __vlan_add_flags(struct net_bridge_vlan *v, u16 flags)
6971
vg = nbp_vlan_group(v->port);
7072

7173
if (flags & BRIDGE_VLAN_INFO_PVID)
72-
ret = __vlan_add_pvid(vg, v->vid);
74+
ret = __vlan_add_pvid(vg, v);
7375
else
7476
ret = __vlan_delete_pvid(vg, v->vid);
7577

@@ -293,6 +295,9 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags,
293295
vg->num_vlans++;
294296
}
295297

298+
/* set the state before publishing */
299+
v->state = BR_STATE_FORWARDING;
300+
296301
err = rhashtable_lookup_insert_fast(&vg->vlan_hash, &v->vnode,
297302
br_vlan_rht_params);
298303
if (err)
@@ -466,7 +471,8 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br,
466471
/* Called under RCU */
467472
static bool __allowed_ingress(const struct net_bridge *br,
468473
struct net_bridge_vlan_group *vg,
469-
struct sk_buff *skb, u16 *vid)
474+
struct sk_buff *skb, u16 *vid,
475+
u8 *state)
470476
{
471477
struct br_vlan_stats *stats;
472478
struct net_bridge_vlan *v;
@@ -532,13 +538,25 @@ static bool __allowed_ingress(const struct net_bridge *br,
532538
skb->vlan_tci |= pvid;
533539

534540
/* if stats are disabled we can avoid the lookup */
535-
if (!br_opt_get(br, BROPT_VLAN_STATS_ENABLED))
536-
return true;
541+
if (!br_opt_get(br, BROPT_VLAN_STATS_ENABLED)) {
542+
if (*state == BR_STATE_FORWARDING) {
543+
*state = br_vlan_get_pvid_state(vg);
544+
return br_vlan_state_allowed(*state, true);
545+
} else {
546+
return true;
547+
}
548+
}
537549
}
538550
v = br_vlan_find(vg, *vid);
539551
if (!v || !br_vlan_should_use(v))
540552
goto drop;
541553

554+
if (*state == BR_STATE_FORWARDING) {
555+
*state = br_vlan_get_state(v);
556+
if (!br_vlan_state_allowed(*state, true))
557+
goto drop;
558+
}
559+
542560
if (br_opt_get(br, BROPT_VLAN_STATS_ENABLED)) {
543561
stats = this_cpu_ptr(v->stats);
544562
u64_stats_update_begin(&stats->syncp);
@@ -556,7 +574,7 @@ static bool __allowed_ingress(const struct net_bridge *br,
556574

557575
bool br_allowed_ingress(const struct net_bridge *br,
558576
struct net_bridge_vlan_group *vg, struct sk_buff *skb,
559-
u16 *vid)
577+
u16 *vid, u8 *state)
560578
{
561579
/* If VLAN filtering is disabled on the bridge, all packets are
562580
* permitted.
@@ -566,7 +584,7 @@ bool br_allowed_ingress(const struct net_bridge *br,
566584
return true;
567585
}
568586

569-
return __allowed_ingress(br, vg, skb, vid);
587+
return __allowed_ingress(br, vg, skb, vid, state);
570588
}
571589

572590
/* Called under RCU. */
@@ -582,7 +600,8 @@ bool br_allowed_egress(struct net_bridge_vlan_group *vg,
582600

583601
br_vlan_get_tag(skb, &vid);
584602
v = br_vlan_find(vg, vid);
585-
if (v && br_vlan_should_use(v))
603+
if (v && br_vlan_should_use(v) &&
604+
br_vlan_state_allowed(br_vlan_get_state(v), false))
586605
return true;
587606

588607
return false;
@@ -593,6 +612,7 @@ bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid)
593612
{
594613
struct net_bridge_vlan_group *vg;
595614
struct net_bridge *br = p->br;
615+
struct net_bridge_vlan *v;
596616

597617
/* If filtering was disabled at input, let it pass. */
598618
if (!br_opt_get(br, BROPT_VLAN_ENABLED))
@@ -607,13 +627,15 @@ bool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid)
607627

608628
if (!*vid) {
609629
*vid = br_get_pvid(vg);
610-
if (!*vid)
630+
if (!*vid ||
631+
!br_vlan_state_allowed(br_vlan_get_pvid_state(vg), true))
611632
return false;
612633

613634
return true;
614635
}
615636

616-
if (br_vlan_find(vg, *vid))
637+
v = br_vlan_find(vg, *vid);
638+
if (v && br_vlan_state_allowed(br_vlan_get_state(v), true))
617639
return true;
618640

619641
return false;
@@ -1816,6 +1838,7 @@ static const struct nla_policy br_vlan_db_policy[BRIDGE_VLANDB_ENTRY_MAX + 1] =
18161838
[BRIDGE_VLANDB_ENTRY_INFO] = { .type = NLA_EXACT_LEN,
18171839
.len = sizeof(struct bridge_vlan_info) },
18181840
[BRIDGE_VLANDB_ENTRY_RANGE] = { .type = NLA_U16 },
1841+
[BRIDGE_VLANDB_ENTRY_STATE] = { .type = NLA_U8 },
18191842
};
18201843

18211844
static int br_vlan_rtm_process_one(struct net_device *dev,

net/bridge/br_vlan_options.c

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,54 @@
1111
bool br_vlan_opts_eq(const struct net_bridge_vlan *v1,
1212
const struct net_bridge_vlan *v2)
1313
{
14-
return true;
14+
return v1->state == v2->state;
1515
}
1616

1717
bool br_vlan_opts_fill(struct sk_buff *skb, const struct net_bridge_vlan *v)
1818
{
19-
return true;
19+
return !nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_STATE,
20+
br_vlan_get_state(v));
2021
}
2122

2223
size_t br_vlan_opts_nl_size(void)
2324
{
25+
return nla_total_size(sizeof(u8)); /* BRIDGE_VLANDB_ENTRY_STATE */
26+
}
27+
28+
static int br_vlan_modify_state(struct net_bridge_vlan_group *vg,
29+
struct net_bridge_vlan *v,
30+
u8 state,
31+
bool *changed,
32+
struct netlink_ext_ack *extack)
33+
{
34+
struct net_bridge *br;
35+
36+
ASSERT_RTNL();
37+
38+
if (state > BR_STATE_BLOCKING) {
39+
NL_SET_ERR_MSG_MOD(extack, "Invalid vlan state");
40+
return -EINVAL;
41+
}
42+
43+
if (br_vlan_is_brentry(v))
44+
br = v->br;
45+
else
46+
br = v->port->br;
47+
48+
if (br->stp_enabled == BR_KERNEL_STP) {
49+
NL_SET_ERR_MSG_MOD(extack, "Can't modify vlan state when using kernel STP");
50+
return -EBUSY;
51+
}
52+
53+
if (v->state == state)
54+
return 0;
55+
56+
if (v->vid == br_get_pvid(vg))
57+
br_vlan_set_pvid_state(vg, state);
58+
59+
br_vlan_set_state(v, state);
60+
*changed = true;
61+
2462
return 0;
2563
}
2664

@@ -32,7 +70,17 @@ static int br_vlan_process_one_opts(const struct net_bridge *br,
3270
bool *changed,
3371
struct netlink_ext_ack *extack)
3472
{
73+
int err;
74+
3575
*changed = false;
76+
if (tb[BRIDGE_VLANDB_ENTRY_STATE]) {
77+
u8 state = nla_get_u8(tb[BRIDGE_VLANDB_ENTRY_STATE]);
78+
79+
err = br_vlan_modify_state(vg, v, state, changed, extack);
80+
if (err)
81+
return err;
82+
}
83+
3684
return 0;
3785
}
3886

0 commit comments

Comments
 (0)