162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci#include <linux/kernel.h>
362306a36Sopenharmony_ci#include <linux/netdevice.h>
462306a36Sopenharmony_ci#include <linux/rtnetlink.h>
562306a36Sopenharmony_ci#include <linux/slab.h>
662306a36Sopenharmony_ci#include <net/switchdev.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "br_private.h"
962306a36Sopenharmony_ci#include "br_private_tunnel.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistatic void nbp_vlan_set_vlan_dev_state(struct net_bridge_port *p, u16 vid);
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic inline int br_vlan_cmp(struct rhashtable_compare_arg *arg,
1462306a36Sopenharmony_ci			      const void *ptr)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	const struct net_bridge_vlan *vle = ptr;
1762306a36Sopenharmony_ci	u16 vid = *(u16 *)arg->key;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci	return vle->vid != vid;
2062306a36Sopenharmony_ci}
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic const struct rhashtable_params br_vlan_rht_params = {
2362306a36Sopenharmony_ci	.head_offset = offsetof(struct net_bridge_vlan, vnode),
2462306a36Sopenharmony_ci	.key_offset = offsetof(struct net_bridge_vlan, vid),
2562306a36Sopenharmony_ci	.key_len = sizeof(u16),
2662306a36Sopenharmony_ci	.nelem_hint = 3,
2762306a36Sopenharmony_ci	.max_size = VLAN_N_VID,
2862306a36Sopenharmony_ci	.obj_cmpfn = br_vlan_cmp,
2962306a36Sopenharmony_ci	.automatic_shrinking = true,
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic struct net_bridge_vlan *br_vlan_lookup(struct rhashtable *tbl, u16 vid)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	return rhashtable_lookup_fast(tbl, &vid, br_vlan_rht_params);
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic void __vlan_add_pvid(struct net_bridge_vlan_group *vg,
3862306a36Sopenharmony_ci			    const struct net_bridge_vlan *v)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	if (vg->pvid == v->vid)
4162306a36Sopenharmony_ci		return;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	smp_wmb();
4462306a36Sopenharmony_ci	br_vlan_set_pvid_state(vg, v->state);
4562306a36Sopenharmony_ci	vg->pvid = v->vid;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic void __vlan_delete_pvid(struct net_bridge_vlan_group *vg, u16 vid)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	if (vg->pvid != vid)
5162306a36Sopenharmony_ci		return;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	smp_wmb();
5462306a36Sopenharmony_ci	vg->pvid = 0;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/* Update the BRIDGE_VLAN_INFO_PVID and BRIDGE_VLAN_INFO_UNTAGGED flags of @v.
5862306a36Sopenharmony_ci * If @commit is false, return just whether the BRIDGE_VLAN_INFO_PVID and
5962306a36Sopenharmony_ci * BRIDGE_VLAN_INFO_UNTAGGED bits of @flags would produce any change onto @v.
6062306a36Sopenharmony_ci */
6162306a36Sopenharmony_cistatic bool __vlan_flags_update(struct net_bridge_vlan *v, u16 flags,
6262306a36Sopenharmony_ci				bool commit)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
6562306a36Sopenharmony_ci	bool change;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	if (br_vlan_is_master(v))
6862306a36Sopenharmony_ci		vg = br_vlan_group(v->br);
6962306a36Sopenharmony_ci	else
7062306a36Sopenharmony_ci		vg = nbp_vlan_group(v->port);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	/* check if anything would be changed on commit */
7362306a36Sopenharmony_ci	change = !!(flags & BRIDGE_VLAN_INFO_PVID) == !!(vg->pvid != v->vid) ||
7462306a36Sopenharmony_ci		 ((flags ^ v->flags) & BRIDGE_VLAN_INFO_UNTAGGED);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (!commit)
7762306a36Sopenharmony_ci		goto out;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (flags & BRIDGE_VLAN_INFO_PVID)
8062306a36Sopenharmony_ci		__vlan_add_pvid(vg, v);
8162306a36Sopenharmony_ci	else
8262306a36Sopenharmony_ci		__vlan_delete_pvid(vg, v->vid);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
8562306a36Sopenharmony_ci		v->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
8662306a36Sopenharmony_ci	else
8762306a36Sopenharmony_ci		v->flags &= ~BRIDGE_VLAN_INFO_UNTAGGED;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ciout:
9062306a36Sopenharmony_ci	return change;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic bool __vlan_flags_would_change(struct net_bridge_vlan *v, u16 flags)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	return __vlan_flags_update(v, flags, false);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic void __vlan_flags_commit(struct net_bridge_vlan *v, u16 flags)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	__vlan_flags_update(v, flags, true);
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic int __vlan_vid_add(struct net_device *dev, struct net_bridge *br,
10462306a36Sopenharmony_ci			  struct net_bridge_vlan *v, u16 flags,
10562306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	int err;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	/* Try switchdev op first. In case it is not supported, fallback to
11062306a36Sopenharmony_ci	 * 8021q add.
11162306a36Sopenharmony_ci	 */
11262306a36Sopenharmony_ci	err = br_switchdev_port_vlan_add(dev, v->vid, flags, false, extack);
11362306a36Sopenharmony_ci	if (err == -EOPNOTSUPP)
11462306a36Sopenharmony_ci		return vlan_vid_add(dev, br->vlan_proto, v->vid);
11562306a36Sopenharmony_ci	v->priv_flags |= BR_VLFLAG_ADDED_BY_SWITCHDEV;
11662306a36Sopenharmony_ci	return err;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic void __vlan_add_list(struct net_bridge_vlan *v)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
12262306a36Sopenharmony_ci	struct list_head *headp, *hpos;
12362306a36Sopenharmony_ci	struct net_bridge_vlan *vent;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (br_vlan_is_master(v))
12662306a36Sopenharmony_ci		vg = br_vlan_group(v->br);
12762306a36Sopenharmony_ci	else
12862306a36Sopenharmony_ci		vg = nbp_vlan_group(v->port);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	headp = &vg->vlan_list;
13162306a36Sopenharmony_ci	list_for_each_prev(hpos, headp) {
13262306a36Sopenharmony_ci		vent = list_entry(hpos, struct net_bridge_vlan, vlist);
13362306a36Sopenharmony_ci		if (v->vid >= vent->vid)
13462306a36Sopenharmony_ci			break;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci	list_add_rcu(&v->vlist, hpos);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic void __vlan_del_list(struct net_bridge_vlan *v)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	list_del_rcu(&v->vlist);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic int __vlan_vid_del(struct net_device *dev, struct net_bridge *br,
14562306a36Sopenharmony_ci			  const struct net_bridge_vlan *v)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	int err;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/* Try switchdev op first. In case it is not supported, fallback to
15062306a36Sopenharmony_ci	 * 8021q del.
15162306a36Sopenharmony_ci	 */
15262306a36Sopenharmony_ci	err = br_switchdev_port_vlan_del(dev, v->vid);
15362306a36Sopenharmony_ci	if (!(v->priv_flags & BR_VLFLAG_ADDED_BY_SWITCHDEV))
15462306a36Sopenharmony_ci		vlan_vid_del(dev, br->vlan_proto, v->vid);
15562306a36Sopenharmony_ci	return err == -EOPNOTSUPP ? 0 : err;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/* Returns a master vlan, if it didn't exist it gets created. In all cases
15962306a36Sopenharmony_ci * a reference is taken to the master vlan before returning.
16062306a36Sopenharmony_ci */
16162306a36Sopenharmony_cistatic struct net_bridge_vlan *
16262306a36Sopenharmony_cibr_vlan_get_master(struct net_bridge *br, u16 vid,
16362306a36Sopenharmony_ci		   struct netlink_ext_ack *extack)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
16662306a36Sopenharmony_ci	struct net_bridge_vlan *masterv;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	vg = br_vlan_group(br);
16962306a36Sopenharmony_ci	masterv = br_vlan_find(vg, vid);
17062306a36Sopenharmony_ci	if (!masterv) {
17162306a36Sopenharmony_ci		bool changed;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci		/* missing global ctx, create it now */
17462306a36Sopenharmony_ci		if (br_vlan_add(br, vid, 0, &changed, extack))
17562306a36Sopenharmony_ci			return NULL;
17662306a36Sopenharmony_ci		masterv = br_vlan_find(vg, vid);
17762306a36Sopenharmony_ci		if (WARN_ON(!masterv))
17862306a36Sopenharmony_ci			return NULL;
17962306a36Sopenharmony_ci		refcount_set(&masterv->refcnt, 1);
18062306a36Sopenharmony_ci		return masterv;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci	refcount_inc(&masterv->refcnt);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	return masterv;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic void br_master_vlan_rcu_free(struct rcu_head *rcu)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	struct net_bridge_vlan *v;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	v = container_of(rcu, struct net_bridge_vlan, rcu);
19262306a36Sopenharmony_ci	WARN_ON(!br_vlan_is_master(v));
19362306a36Sopenharmony_ci	free_percpu(v->stats);
19462306a36Sopenharmony_ci	v->stats = NULL;
19562306a36Sopenharmony_ci	kfree(v);
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic void br_vlan_put_master(struct net_bridge_vlan *masterv)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (!br_vlan_is_master(masterv))
20362306a36Sopenharmony_ci		return;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	vg = br_vlan_group(masterv->br);
20662306a36Sopenharmony_ci	if (refcount_dec_and_test(&masterv->refcnt)) {
20762306a36Sopenharmony_ci		rhashtable_remove_fast(&vg->vlan_hash,
20862306a36Sopenharmony_ci				       &masterv->vnode, br_vlan_rht_params);
20962306a36Sopenharmony_ci		__vlan_del_list(masterv);
21062306a36Sopenharmony_ci		br_multicast_toggle_one_vlan(masterv, false);
21162306a36Sopenharmony_ci		br_multicast_ctx_deinit(&masterv->br_mcast_ctx);
21262306a36Sopenharmony_ci		call_rcu(&masterv->rcu, br_master_vlan_rcu_free);
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic void nbp_vlan_rcu_free(struct rcu_head *rcu)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	struct net_bridge_vlan *v;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	v = container_of(rcu, struct net_bridge_vlan, rcu);
22162306a36Sopenharmony_ci	WARN_ON(br_vlan_is_master(v));
22262306a36Sopenharmony_ci	/* if we had per-port stats configured then free them here */
22362306a36Sopenharmony_ci	if (v->priv_flags & BR_VLFLAG_PER_PORT_STATS)
22462306a36Sopenharmony_ci		free_percpu(v->stats);
22562306a36Sopenharmony_ci	v->stats = NULL;
22662306a36Sopenharmony_ci	kfree(v);
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic void br_vlan_init_state(struct net_bridge_vlan *v)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	struct net_bridge *br;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	if (br_vlan_is_master(v))
23462306a36Sopenharmony_ci		br = v->br;
23562306a36Sopenharmony_ci	else
23662306a36Sopenharmony_ci		br = v->port->br;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	if (br_opt_get(br, BROPT_MST_ENABLED)) {
23962306a36Sopenharmony_ci		br_mst_vlan_init_state(v);
24062306a36Sopenharmony_ci		return;
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	v->state = BR_STATE_FORWARDING;
24462306a36Sopenharmony_ci	v->msti = 0;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci/* This is the shared VLAN add function which works for both ports and bridge
24862306a36Sopenharmony_ci * devices. There are four possible calls to this function in terms of the
24962306a36Sopenharmony_ci * vlan entry type:
25062306a36Sopenharmony_ci * 1. vlan is being added on a port (no master flags, global entry exists)
25162306a36Sopenharmony_ci * 2. vlan is being added on a bridge (both master and brentry flags)
25262306a36Sopenharmony_ci * 3. vlan is being added on a port, but a global entry didn't exist which
25362306a36Sopenharmony_ci *    is being created right now (master flag set, brentry flag unset), the
25462306a36Sopenharmony_ci *    global entry is used for global per-vlan features, but not for filtering
25562306a36Sopenharmony_ci * 4. same as 3 but with both master and brentry flags set so the entry
25662306a36Sopenharmony_ci *    will be used for filtering in both the port and the bridge
25762306a36Sopenharmony_ci */
25862306a36Sopenharmony_cistatic int __vlan_add(struct net_bridge_vlan *v, u16 flags,
25962306a36Sopenharmony_ci		      struct netlink_ext_ack *extack)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	struct net_bridge_vlan *masterv = NULL;
26262306a36Sopenharmony_ci	struct net_bridge_port *p = NULL;
26362306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
26462306a36Sopenharmony_ci	struct net_device *dev;
26562306a36Sopenharmony_ci	struct net_bridge *br;
26662306a36Sopenharmony_ci	int err;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if (br_vlan_is_master(v)) {
26962306a36Sopenharmony_ci		br = v->br;
27062306a36Sopenharmony_ci		dev = br->dev;
27162306a36Sopenharmony_ci		vg = br_vlan_group(br);
27262306a36Sopenharmony_ci	} else {
27362306a36Sopenharmony_ci		p = v->port;
27462306a36Sopenharmony_ci		br = p->br;
27562306a36Sopenharmony_ci		dev = p->dev;
27662306a36Sopenharmony_ci		vg = nbp_vlan_group(p);
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (p) {
28062306a36Sopenharmony_ci		/* Add VLAN to the device filter if it is supported.
28162306a36Sopenharmony_ci		 * This ensures tagged traffic enters the bridge when
28262306a36Sopenharmony_ci		 * promiscuous mode is disabled by br_manage_promisc().
28362306a36Sopenharmony_ci		 */
28462306a36Sopenharmony_ci		err = __vlan_vid_add(dev, br, v, flags, extack);
28562306a36Sopenharmony_ci		if (err)
28662306a36Sopenharmony_ci			goto out;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci		/* need to work on the master vlan too */
28962306a36Sopenharmony_ci		if (flags & BRIDGE_VLAN_INFO_MASTER) {
29062306a36Sopenharmony_ci			bool changed;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci			err = br_vlan_add(br, v->vid,
29362306a36Sopenharmony_ci					  flags | BRIDGE_VLAN_INFO_BRENTRY,
29462306a36Sopenharmony_ci					  &changed, extack);
29562306a36Sopenharmony_ci			if (err)
29662306a36Sopenharmony_ci				goto out_filt;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci			if (changed)
29962306a36Sopenharmony_ci				br_vlan_notify(br, NULL, v->vid, 0,
30062306a36Sopenharmony_ci					       RTM_NEWVLAN);
30162306a36Sopenharmony_ci		}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci		masterv = br_vlan_get_master(br, v->vid, extack);
30462306a36Sopenharmony_ci		if (!masterv) {
30562306a36Sopenharmony_ci			err = -ENOMEM;
30662306a36Sopenharmony_ci			goto out_filt;
30762306a36Sopenharmony_ci		}
30862306a36Sopenharmony_ci		v->brvlan = masterv;
30962306a36Sopenharmony_ci		if (br_opt_get(br, BROPT_VLAN_STATS_PER_PORT)) {
31062306a36Sopenharmony_ci			v->stats =
31162306a36Sopenharmony_ci			     netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
31262306a36Sopenharmony_ci			if (!v->stats) {
31362306a36Sopenharmony_ci				err = -ENOMEM;
31462306a36Sopenharmony_ci				goto out_filt;
31562306a36Sopenharmony_ci			}
31662306a36Sopenharmony_ci			v->priv_flags |= BR_VLFLAG_PER_PORT_STATS;
31762306a36Sopenharmony_ci		} else {
31862306a36Sopenharmony_ci			v->stats = masterv->stats;
31962306a36Sopenharmony_ci		}
32062306a36Sopenharmony_ci		br_multicast_port_ctx_init(p, v, &v->port_mcast_ctx);
32162306a36Sopenharmony_ci	} else {
32262306a36Sopenharmony_ci		if (br_vlan_should_use(v)) {
32362306a36Sopenharmony_ci			err = br_switchdev_port_vlan_add(dev, v->vid, flags,
32462306a36Sopenharmony_ci							 false, extack);
32562306a36Sopenharmony_ci			if (err && err != -EOPNOTSUPP)
32662306a36Sopenharmony_ci				goto out;
32762306a36Sopenharmony_ci		}
32862306a36Sopenharmony_ci		br_multicast_ctx_init(br, v, &v->br_mcast_ctx);
32962306a36Sopenharmony_ci		v->priv_flags |= BR_VLFLAG_GLOBAL_MCAST_ENABLED;
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	/* Add the dev mac and count the vlan only if it's usable */
33362306a36Sopenharmony_ci	if (br_vlan_should_use(v)) {
33462306a36Sopenharmony_ci		err = br_fdb_add_local(br, p, dev->dev_addr, v->vid);
33562306a36Sopenharmony_ci		if (err) {
33662306a36Sopenharmony_ci			br_err(br, "failed insert local address into bridge forwarding table\n");
33762306a36Sopenharmony_ci			goto out_filt;
33862306a36Sopenharmony_ci		}
33962306a36Sopenharmony_ci		vg->num_vlans++;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/* set the state before publishing */
34362306a36Sopenharmony_ci	br_vlan_init_state(v);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	err = rhashtable_lookup_insert_fast(&vg->vlan_hash, &v->vnode,
34662306a36Sopenharmony_ci					    br_vlan_rht_params);
34762306a36Sopenharmony_ci	if (err)
34862306a36Sopenharmony_ci		goto out_fdb_insert;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	__vlan_add_list(v);
35162306a36Sopenharmony_ci	__vlan_flags_commit(v, flags);
35262306a36Sopenharmony_ci	br_multicast_toggle_one_vlan(v, true);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	if (p)
35562306a36Sopenharmony_ci		nbp_vlan_set_vlan_dev_state(p, v->vid);
35662306a36Sopenharmony_ciout:
35762306a36Sopenharmony_ci	return err;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ciout_fdb_insert:
36062306a36Sopenharmony_ci	if (br_vlan_should_use(v)) {
36162306a36Sopenharmony_ci		br_fdb_find_delete_local(br, p, dev->dev_addr, v->vid);
36262306a36Sopenharmony_ci		vg->num_vlans--;
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ciout_filt:
36662306a36Sopenharmony_ci	if (p) {
36762306a36Sopenharmony_ci		__vlan_vid_del(dev, br, v);
36862306a36Sopenharmony_ci		if (masterv) {
36962306a36Sopenharmony_ci			if (v->stats && masterv->stats != v->stats)
37062306a36Sopenharmony_ci				free_percpu(v->stats);
37162306a36Sopenharmony_ci			v->stats = NULL;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci			br_vlan_put_master(masterv);
37462306a36Sopenharmony_ci			v->brvlan = NULL;
37562306a36Sopenharmony_ci		}
37662306a36Sopenharmony_ci	} else {
37762306a36Sopenharmony_ci		br_switchdev_port_vlan_del(dev, v->vid);
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	goto out;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic int __vlan_del(struct net_bridge_vlan *v)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	struct net_bridge_vlan *masterv = v;
38662306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
38762306a36Sopenharmony_ci	struct net_bridge_port *p = NULL;
38862306a36Sopenharmony_ci	int err = 0;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	if (br_vlan_is_master(v)) {
39162306a36Sopenharmony_ci		vg = br_vlan_group(v->br);
39262306a36Sopenharmony_ci	} else {
39362306a36Sopenharmony_ci		p = v->port;
39462306a36Sopenharmony_ci		vg = nbp_vlan_group(v->port);
39562306a36Sopenharmony_ci		masterv = v->brvlan;
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	__vlan_delete_pvid(vg, v->vid);
39962306a36Sopenharmony_ci	if (p) {
40062306a36Sopenharmony_ci		err = __vlan_vid_del(p->dev, p->br, v);
40162306a36Sopenharmony_ci		if (err)
40262306a36Sopenharmony_ci			goto out;
40362306a36Sopenharmony_ci	} else {
40462306a36Sopenharmony_ci		err = br_switchdev_port_vlan_del(v->br->dev, v->vid);
40562306a36Sopenharmony_ci		if (err && err != -EOPNOTSUPP)
40662306a36Sopenharmony_ci			goto out;
40762306a36Sopenharmony_ci		err = 0;
40862306a36Sopenharmony_ci	}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	if (br_vlan_should_use(v)) {
41162306a36Sopenharmony_ci		v->flags &= ~BRIDGE_VLAN_INFO_BRENTRY;
41262306a36Sopenharmony_ci		vg->num_vlans--;
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	if (masterv != v) {
41662306a36Sopenharmony_ci		vlan_tunnel_info_del(vg, v);
41762306a36Sopenharmony_ci		rhashtable_remove_fast(&vg->vlan_hash, &v->vnode,
41862306a36Sopenharmony_ci				       br_vlan_rht_params);
41962306a36Sopenharmony_ci		__vlan_del_list(v);
42062306a36Sopenharmony_ci		nbp_vlan_set_vlan_dev_state(p, v->vid);
42162306a36Sopenharmony_ci		br_multicast_toggle_one_vlan(v, false);
42262306a36Sopenharmony_ci		br_multicast_port_ctx_deinit(&v->port_mcast_ctx);
42362306a36Sopenharmony_ci		call_rcu(&v->rcu, nbp_vlan_rcu_free);
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	br_vlan_put_master(masterv);
42762306a36Sopenharmony_ciout:
42862306a36Sopenharmony_ci	return err;
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic void __vlan_group_free(struct net_bridge_vlan_group *vg)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	WARN_ON(!list_empty(&vg->vlan_list));
43462306a36Sopenharmony_ci	rhashtable_destroy(&vg->vlan_hash);
43562306a36Sopenharmony_ci	vlan_tunnel_deinit(vg);
43662306a36Sopenharmony_ci	kfree(vg);
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic void __vlan_flush(const struct net_bridge *br,
44062306a36Sopenharmony_ci			 const struct net_bridge_port *p,
44162306a36Sopenharmony_ci			 struct net_bridge_vlan_group *vg)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	struct net_bridge_vlan *vlan, *tmp;
44462306a36Sopenharmony_ci	u16 v_start = 0, v_end = 0;
44562306a36Sopenharmony_ci	int err;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	__vlan_delete_pvid(vg, vg->pvid);
44862306a36Sopenharmony_ci	list_for_each_entry_safe(vlan, tmp, &vg->vlan_list, vlist) {
44962306a36Sopenharmony_ci		/* take care of disjoint ranges */
45062306a36Sopenharmony_ci		if (!v_start) {
45162306a36Sopenharmony_ci			v_start = vlan->vid;
45262306a36Sopenharmony_ci		} else if (vlan->vid - v_end != 1) {
45362306a36Sopenharmony_ci			/* found range end, notify and start next one */
45462306a36Sopenharmony_ci			br_vlan_notify(br, p, v_start, v_end, RTM_DELVLAN);
45562306a36Sopenharmony_ci			v_start = vlan->vid;
45662306a36Sopenharmony_ci		}
45762306a36Sopenharmony_ci		v_end = vlan->vid;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci		err = __vlan_del(vlan);
46062306a36Sopenharmony_ci		if (err) {
46162306a36Sopenharmony_ci			br_err(br,
46262306a36Sopenharmony_ci			       "port %u(%s) failed to delete vlan %d: %pe\n",
46362306a36Sopenharmony_ci			       (unsigned int) p->port_no, p->dev->name,
46462306a36Sopenharmony_ci			       vlan->vid, ERR_PTR(err));
46562306a36Sopenharmony_ci		}
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	/* notify about the last/whole vlan range */
46962306a36Sopenharmony_ci	if (v_start)
47062306a36Sopenharmony_ci		br_vlan_notify(br, p, v_start, v_end, RTM_DELVLAN);
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cistruct sk_buff *br_handle_vlan(struct net_bridge *br,
47462306a36Sopenharmony_ci			       const struct net_bridge_port *p,
47562306a36Sopenharmony_ci			       struct net_bridge_vlan_group *vg,
47662306a36Sopenharmony_ci			       struct sk_buff *skb)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	struct pcpu_sw_netstats *stats;
47962306a36Sopenharmony_ci	struct net_bridge_vlan *v;
48062306a36Sopenharmony_ci	u16 vid;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	/* If this packet was not filtered at input, let it pass */
48362306a36Sopenharmony_ci	if (!BR_INPUT_SKB_CB(skb)->vlan_filtered)
48462306a36Sopenharmony_ci		goto out;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	/* At this point, we know that the frame was filtered and contains
48762306a36Sopenharmony_ci	 * a valid vlan id.  If the vlan id has untagged flag set,
48862306a36Sopenharmony_ci	 * send untagged; otherwise, send tagged.
48962306a36Sopenharmony_ci	 */
49062306a36Sopenharmony_ci	br_vlan_get_tag(skb, &vid);
49162306a36Sopenharmony_ci	v = br_vlan_find(vg, vid);
49262306a36Sopenharmony_ci	/* Vlan entry must be configured at this point.  The
49362306a36Sopenharmony_ci	 * only exception is the bridge is set in promisc mode and the
49462306a36Sopenharmony_ci	 * packet is destined for the bridge device.  In this case
49562306a36Sopenharmony_ci	 * pass the packet as is.
49662306a36Sopenharmony_ci	 */
49762306a36Sopenharmony_ci	if (!v || !br_vlan_should_use(v)) {
49862306a36Sopenharmony_ci		if ((br->dev->flags & IFF_PROMISC) && skb->dev == br->dev) {
49962306a36Sopenharmony_ci			goto out;
50062306a36Sopenharmony_ci		} else {
50162306a36Sopenharmony_ci			kfree_skb(skb);
50262306a36Sopenharmony_ci			return NULL;
50362306a36Sopenharmony_ci		}
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci	if (br_opt_get(br, BROPT_VLAN_STATS_ENABLED)) {
50662306a36Sopenharmony_ci		stats = this_cpu_ptr(v->stats);
50762306a36Sopenharmony_ci		u64_stats_update_begin(&stats->syncp);
50862306a36Sopenharmony_ci		u64_stats_add(&stats->tx_bytes, skb->len);
50962306a36Sopenharmony_ci		u64_stats_inc(&stats->tx_packets);
51062306a36Sopenharmony_ci		u64_stats_update_end(&stats->syncp);
51162306a36Sopenharmony_ci	}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	/* If the skb will be sent using forwarding offload, the assumption is
51462306a36Sopenharmony_ci	 * that the switchdev will inject the packet into hardware together
51562306a36Sopenharmony_ci	 * with the bridge VLAN, so that it can be forwarded according to that
51662306a36Sopenharmony_ci	 * VLAN. The switchdev should deal with popping the VLAN header in
51762306a36Sopenharmony_ci	 * hardware on each egress port as appropriate. So only strip the VLAN
51862306a36Sopenharmony_ci	 * header if forwarding offload is not being used.
51962306a36Sopenharmony_ci	 */
52062306a36Sopenharmony_ci	if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED &&
52162306a36Sopenharmony_ci	    !br_switchdev_frame_uses_tx_fwd_offload(skb))
52262306a36Sopenharmony_ci		__vlan_hwaccel_clear_tag(skb);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	if (p && (p->flags & BR_VLAN_TUNNEL) &&
52562306a36Sopenharmony_ci	    br_handle_egress_vlan_tunnel(skb, v)) {
52662306a36Sopenharmony_ci		kfree_skb(skb);
52762306a36Sopenharmony_ci		return NULL;
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ciout:
53062306a36Sopenharmony_ci	return skb;
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci/* Called under RCU */
53462306a36Sopenharmony_cistatic bool __allowed_ingress(const struct net_bridge *br,
53562306a36Sopenharmony_ci			      struct net_bridge_vlan_group *vg,
53662306a36Sopenharmony_ci			      struct sk_buff *skb, u16 *vid,
53762306a36Sopenharmony_ci			      u8 *state,
53862306a36Sopenharmony_ci			      struct net_bridge_vlan **vlan)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct pcpu_sw_netstats *stats;
54162306a36Sopenharmony_ci	struct net_bridge_vlan *v;
54262306a36Sopenharmony_ci	bool tagged;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	BR_INPUT_SKB_CB(skb)->vlan_filtered = true;
54562306a36Sopenharmony_ci	/* If vlan tx offload is disabled on bridge device and frame was
54662306a36Sopenharmony_ci	 * sent from vlan device on the bridge device, it does not have
54762306a36Sopenharmony_ci	 * HW accelerated vlan tag.
54862306a36Sopenharmony_ci	 */
54962306a36Sopenharmony_ci	if (unlikely(!skb_vlan_tag_present(skb) &&
55062306a36Sopenharmony_ci		     skb->protocol == br->vlan_proto)) {
55162306a36Sopenharmony_ci		skb = skb_vlan_untag(skb);
55262306a36Sopenharmony_ci		if (unlikely(!skb))
55362306a36Sopenharmony_ci			return false;
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	if (!br_vlan_get_tag(skb, vid)) {
55762306a36Sopenharmony_ci		/* Tagged frame */
55862306a36Sopenharmony_ci		if (skb->vlan_proto != br->vlan_proto) {
55962306a36Sopenharmony_ci			/* Protocol-mismatch, empty out vlan_tci for new tag */
56062306a36Sopenharmony_ci			skb_push(skb, ETH_HLEN);
56162306a36Sopenharmony_ci			skb = vlan_insert_tag_set_proto(skb, skb->vlan_proto,
56262306a36Sopenharmony_ci							skb_vlan_tag_get(skb));
56362306a36Sopenharmony_ci			if (unlikely(!skb))
56462306a36Sopenharmony_ci				return false;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci			skb_pull(skb, ETH_HLEN);
56762306a36Sopenharmony_ci			skb_reset_mac_len(skb);
56862306a36Sopenharmony_ci			*vid = 0;
56962306a36Sopenharmony_ci			tagged = false;
57062306a36Sopenharmony_ci		} else {
57162306a36Sopenharmony_ci			tagged = true;
57262306a36Sopenharmony_ci		}
57362306a36Sopenharmony_ci	} else {
57462306a36Sopenharmony_ci		/* Untagged frame */
57562306a36Sopenharmony_ci		tagged = false;
57662306a36Sopenharmony_ci	}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	if (!*vid) {
57962306a36Sopenharmony_ci		u16 pvid = br_get_pvid(vg);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		/* Frame had a tag with VID 0 or did not have a tag.
58262306a36Sopenharmony_ci		 * See if pvid is set on this port.  That tells us which
58362306a36Sopenharmony_ci		 * vlan untagged or priority-tagged traffic belongs to.
58462306a36Sopenharmony_ci		 */
58562306a36Sopenharmony_ci		if (!pvid)
58662306a36Sopenharmony_ci			goto drop;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci		/* PVID is set on this port.  Any untagged or priority-tagged
58962306a36Sopenharmony_ci		 * ingress frame is considered to belong to this vlan.
59062306a36Sopenharmony_ci		 */
59162306a36Sopenharmony_ci		*vid = pvid;
59262306a36Sopenharmony_ci		if (likely(!tagged))
59362306a36Sopenharmony_ci			/* Untagged Frame. */
59462306a36Sopenharmony_ci			__vlan_hwaccel_put_tag(skb, br->vlan_proto, pvid);
59562306a36Sopenharmony_ci		else
59662306a36Sopenharmony_ci			/* Priority-tagged Frame.
59762306a36Sopenharmony_ci			 * At this point, we know that skb->vlan_tci VID
59862306a36Sopenharmony_ci			 * field was 0.
59962306a36Sopenharmony_ci			 * We update only VID field and preserve PCP field.
60062306a36Sopenharmony_ci			 */
60162306a36Sopenharmony_ci			skb->vlan_tci |= pvid;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci		/* if snooping and stats are disabled we can avoid the lookup */
60462306a36Sopenharmony_ci		if (!br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED) &&
60562306a36Sopenharmony_ci		    !br_opt_get(br, BROPT_VLAN_STATS_ENABLED)) {
60662306a36Sopenharmony_ci			if (*state == BR_STATE_FORWARDING) {
60762306a36Sopenharmony_ci				*state = br_vlan_get_pvid_state(vg);
60862306a36Sopenharmony_ci				if (!br_vlan_state_allowed(*state, true))
60962306a36Sopenharmony_ci					goto drop;
61062306a36Sopenharmony_ci			}
61162306a36Sopenharmony_ci			return true;
61262306a36Sopenharmony_ci		}
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci	v = br_vlan_find(vg, *vid);
61562306a36Sopenharmony_ci	if (!v || !br_vlan_should_use(v))
61662306a36Sopenharmony_ci		goto drop;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	if (*state == BR_STATE_FORWARDING) {
61962306a36Sopenharmony_ci		*state = br_vlan_get_state(v);
62062306a36Sopenharmony_ci		if (!br_vlan_state_allowed(*state, true))
62162306a36Sopenharmony_ci			goto drop;
62262306a36Sopenharmony_ci	}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	if (br_opt_get(br, BROPT_VLAN_STATS_ENABLED)) {
62562306a36Sopenharmony_ci		stats = this_cpu_ptr(v->stats);
62662306a36Sopenharmony_ci		u64_stats_update_begin(&stats->syncp);
62762306a36Sopenharmony_ci		u64_stats_add(&stats->rx_bytes, skb->len);
62862306a36Sopenharmony_ci		u64_stats_inc(&stats->rx_packets);
62962306a36Sopenharmony_ci		u64_stats_update_end(&stats->syncp);
63062306a36Sopenharmony_ci	}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	*vlan = v;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	return true;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_cidrop:
63762306a36Sopenharmony_ci	kfree_skb(skb);
63862306a36Sopenharmony_ci	return false;
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_cibool br_allowed_ingress(const struct net_bridge *br,
64262306a36Sopenharmony_ci			struct net_bridge_vlan_group *vg, struct sk_buff *skb,
64362306a36Sopenharmony_ci			u16 *vid, u8 *state,
64462306a36Sopenharmony_ci			struct net_bridge_vlan **vlan)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	/* If VLAN filtering is disabled on the bridge, all packets are
64762306a36Sopenharmony_ci	 * permitted.
64862306a36Sopenharmony_ci	 */
64962306a36Sopenharmony_ci	*vlan = NULL;
65062306a36Sopenharmony_ci	if (!br_opt_get(br, BROPT_VLAN_ENABLED)) {
65162306a36Sopenharmony_ci		BR_INPUT_SKB_CB(skb)->vlan_filtered = false;
65262306a36Sopenharmony_ci		return true;
65362306a36Sopenharmony_ci	}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	return __allowed_ingress(br, vg, skb, vid, state, vlan);
65662306a36Sopenharmony_ci}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci/* Called under RCU. */
65962306a36Sopenharmony_cibool br_allowed_egress(struct net_bridge_vlan_group *vg,
66062306a36Sopenharmony_ci		       const struct sk_buff *skb)
66162306a36Sopenharmony_ci{
66262306a36Sopenharmony_ci	const struct net_bridge_vlan *v;
66362306a36Sopenharmony_ci	u16 vid;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	/* If this packet was not filtered at input, let it pass */
66662306a36Sopenharmony_ci	if (!BR_INPUT_SKB_CB(skb)->vlan_filtered)
66762306a36Sopenharmony_ci		return true;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	br_vlan_get_tag(skb, &vid);
67062306a36Sopenharmony_ci	v = br_vlan_find(vg, vid);
67162306a36Sopenharmony_ci	if (v && br_vlan_should_use(v) &&
67262306a36Sopenharmony_ci	    br_vlan_state_allowed(br_vlan_get_state(v), false))
67362306a36Sopenharmony_ci		return true;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	return false;
67662306a36Sopenharmony_ci}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci/* Called under RCU */
67962306a36Sopenharmony_cibool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid)
68062306a36Sopenharmony_ci{
68162306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
68262306a36Sopenharmony_ci	struct net_bridge *br = p->br;
68362306a36Sopenharmony_ci	struct net_bridge_vlan *v;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	/* If filtering was disabled at input, let it pass. */
68662306a36Sopenharmony_ci	if (!br_opt_get(br, BROPT_VLAN_ENABLED))
68762306a36Sopenharmony_ci		return true;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	vg = nbp_vlan_group_rcu(p);
69062306a36Sopenharmony_ci	if (!vg || !vg->num_vlans)
69162306a36Sopenharmony_ci		return false;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	if (!br_vlan_get_tag(skb, vid) && skb->vlan_proto != br->vlan_proto)
69462306a36Sopenharmony_ci		*vid = 0;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	if (!*vid) {
69762306a36Sopenharmony_ci		*vid = br_get_pvid(vg);
69862306a36Sopenharmony_ci		if (!*vid ||
69962306a36Sopenharmony_ci		    !br_vlan_state_allowed(br_vlan_get_pvid_state(vg), true))
70062306a36Sopenharmony_ci			return false;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci		return true;
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	v = br_vlan_find(vg, *vid);
70662306a36Sopenharmony_ci	if (v && br_vlan_state_allowed(br_vlan_get_state(v), true))
70762306a36Sopenharmony_ci		return true;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	return false;
71062306a36Sopenharmony_ci}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cistatic int br_vlan_add_existing(struct net_bridge *br,
71362306a36Sopenharmony_ci				struct net_bridge_vlan_group *vg,
71462306a36Sopenharmony_ci				struct net_bridge_vlan *vlan,
71562306a36Sopenharmony_ci				u16 flags, bool *changed,
71662306a36Sopenharmony_ci				struct netlink_ext_ack *extack)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	bool would_change = __vlan_flags_would_change(vlan, flags);
71962306a36Sopenharmony_ci	bool becomes_brentry = false;
72062306a36Sopenharmony_ci	int err;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	if (!br_vlan_is_brentry(vlan)) {
72362306a36Sopenharmony_ci		/* Trying to change flags of non-existent bridge vlan */
72462306a36Sopenharmony_ci		if (!(flags & BRIDGE_VLAN_INFO_BRENTRY))
72562306a36Sopenharmony_ci			return -EINVAL;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci		becomes_brentry = true;
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	/* Master VLANs that aren't brentries weren't notified before,
73162306a36Sopenharmony_ci	 * time to notify them now.
73262306a36Sopenharmony_ci	 */
73362306a36Sopenharmony_ci	if (becomes_brentry || would_change) {
73462306a36Sopenharmony_ci		err = br_switchdev_port_vlan_add(br->dev, vlan->vid, flags,
73562306a36Sopenharmony_ci						 would_change, extack);
73662306a36Sopenharmony_ci		if (err && err != -EOPNOTSUPP)
73762306a36Sopenharmony_ci			return err;
73862306a36Sopenharmony_ci	}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	if (becomes_brentry) {
74162306a36Sopenharmony_ci		/* It was only kept for port vlans, now make it real */
74262306a36Sopenharmony_ci		err = br_fdb_add_local(br, NULL, br->dev->dev_addr, vlan->vid);
74362306a36Sopenharmony_ci		if (err) {
74462306a36Sopenharmony_ci			br_err(br, "failed to insert local address into bridge forwarding table\n");
74562306a36Sopenharmony_ci			goto err_fdb_insert;
74662306a36Sopenharmony_ci		}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci		refcount_inc(&vlan->refcnt);
74962306a36Sopenharmony_ci		vlan->flags |= BRIDGE_VLAN_INFO_BRENTRY;
75062306a36Sopenharmony_ci		vg->num_vlans++;
75162306a36Sopenharmony_ci		*changed = true;
75262306a36Sopenharmony_ci		br_multicast_toggle_one_vlan(vlan, true);
75362306a36Sopenharmony_ci	}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	__vlan_flags_commit(vlan, flags);
75662306a36Sopenharmony_ci	if (would_change)
75762306a36Sopenharmony_ci		*changed = true;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	return 0;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_cierr_fdb_insert:
76262306a36Sopenharmony_ci	br_switchdev_port_vlan_del(br->dev, vlan->vid);
76362306a36Sopenharmony_ci	return err;
76462306a36Sopenharmony_ci}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci/* Must be protected by RTNL.
76762306a36Sopenharmony_ci * Must be called with vid in range from 1 to 4094 inclusive.
76862306a36Sopenharmony_ci * changed must be true only if the vlan was created or updated
76962306a36Sopenharmony_ci */
77062306a36Sopenharmony_ciint br_vlan_add(struct net_bridge *br, u16 vid, u16 flags, bool *changed,
77162306a36Sopenharmony_ci		struct netlink_ext_ack *extack)
77262306a36Sopenharmony_ci{
77362306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
77462306a36Sopenharmony_ci	struct net_bridge_vlan *vlan;
77562306a36Sopenharmony_ci	int ret;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	ASSERT_RTNL();
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	*changed = false;
78062306a36Sopenharmony_ci	vg = br_vlan_group(br);
78162306a36Sopenharmony_ci	vlan = br_vlan_find(vg, vid);
78262306a36Sopenharmony_ci	if (vlan)
78362306a36Sopenharmony_ci		return br_vlan_add_existing(br, vg, vlan, flags, changed,
78462306a36Sopenharmony_ci					    extack);
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
78762306a36Sopenharmony_ci	if (!vlan)
78862306a36Sopenharmony_ci		return -ENOMEM;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	vlan->stats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
79162306a36Sopenharmony_ci	if (!vlan->stats) {
79262306a36Sopenharmony_ci		kfree(vlan);
79362306a36Sopenharmony_ci		return -ENOMEM;
79462306a36Sopenharmony_ci	}
79562306a36Sopenharmony_ci	vlan->vid = vid;
79662306a36Sopenharmony_ci	vlan->flags = flags | BRIDGE_VLAN_INFO_MASTER;
79762306a36Sopenharmony_ci	vlan->flags &= ~BRIDGE_VLAN_INFO_PVID;
79862306a36Sopenharmony_ci	vlan->br = br;
79962306a36Sopenharmony_ci	if (flags & BRIDGE_VLAN_INFO_BRENTRY)
80062306a36Sopenharmony_ci		refcount_set(&vlan->refcnt, 1);
80162306a36Sopenharmony_ci	ret = __vlan_add(vlan, flags, extack);
80262306a36Sopenharmony_ci	if (ret) {
80362306a36Sopenharmony_ci		free_percpu(vlan->stats);
80462306a36Sopenharmony_ci		kfree(vlan);
80562306a36Sopenharmony_ci	} else {
80662306a36Sopenharmony_ci		*changed = true;
80762306a36Sopenharmony_ci	}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	return ret;
81062306a36Sopenharmony_ci}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci/* Must be protected by RTNL.
81362306a36Sopenharmony_ci * Must be called with vid in range from 1 to 4094 inclusive.
81462306a36Sopenharmony_ci */
81562306a36Sopenharmony_ciint br_vlan_delete(struct net_bridge *br, u16 vid)
81662306a36Sopenharmony_ci{
81762306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
81862306a36Sopenharmony_ci	struct net_bridge_vlan *v;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	ASSERT_RTNL();
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	vg = br_vlan_group(br);
82362306a36Sopenharmony_ci	v = br_vlan_find(vg, vid);
82462306a36Sopenharmony_ci	if (!v || !br_vlan_is_brentry(v))
82562306a36Sopenharmony_ci		return -ENOENT;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	br_fdb_find_delete_local(br, NULL, br->dev->dev_addr, vid);
82862306a36Sopenharmony_ci	br_fdb_delete_by_port(br, NULL, vid, 0);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	vlan_tunnel_info_del(vg, v);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	return __vlan_del(v);
83362306a36Sopenharmony_ci}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_civoid br_vlan_flush(struct net_bridge *br)
83662306a36Sopenharmony_ci{
83762306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	ASSERT_RTNL();
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	vg = br_vlan_group(br);
84262306a36Sopenharmony_ci	__vlan_flush(br, NULL, vg);
84362306a36Sopenharmony_ci	RCU_INIT_POINTER(br->vlgrp, NULL);
84462306a36Sopenharmony_ci	synchronize_rcu();
84562306a36Sopenharmony_ci	__vlan_group_free(vg);
84662306a36Sopenharmony_ci}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_cistruct net_bridge_vlan *br_vlan_find(struct net_bridge_vlan_group *vg, u16 vid)
84962306a36Sopenharmony_ci{
85062306a36Sopenharmony_ci	if (!vg)
85162306a36Sopenharmony_ci		return NULL;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	return br_vlan_lookup(&vg->vlan_hash, vid);
85462306a36Sopenharmony_ci}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci/* Must be protected by RTNL. */
85762306a36Sopenharmony_cistatic void recalculate_group_addr(struct net_bridge *br)
85862306a36Sopenharmony_ci{
85962306a36Sopenharmony_ci	if (br_opt_get(br, BROPT_GROUP_ADDR_SET))
86062306a36Sopenharmony_ci		return;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	spin_lock_bh(&br->lock);
86362306a36Sopenharmony_ci	if (!br_opt_get(br, BROPT_VLAN_ENABLED) ||
86462306a36Sopenharmony_ci	    br->vlan_proto == htons(ETH_P_8021Q)) {
86562306a36Sopenharmony_ci		/* Bridge Group Address */
86662306a36Sopenharmony_ci		br->group_addr[5] = 0x00;
86762306a36Sopenharmony_ci	} else { /* vlan_enabled && ETH_P_8021AD */
86862306a36Sopenharmony_ci		/* Provider Bridge Group Address */
86962306a36Sopenharmony_ci		br->group_addr[5] = 0x08;
87062306a36Sopenharmony_ci	}
87162306a36Sopenharmony_ci	spin_unlock_bh(&br->lock);
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci/* Must be protected by RTNL. */
87562306a36Sopenharmony_civoid br_recalculate_fwd_mask(struct net_bridge *br)
87662306a36Sopenharmony_ci{
87762306a36Sopenharmony_ci	if (!br_opt_get(br, BROPT_VLAN_ENABLED) ||
87862306a36Sopenharmony_ci	    br->vlan_proto == htons(ETH_P_8021Q))
87962306a36Sopenharmony_ci		br->group_fwd_mask_required = BR_GROUPFWD_DEFAULT;
88062306a36Sopenharmony_ci	else /* vlan_enabled && ETH_P_8021AD */
88162306a36Sopenharmony_ci		br->group_fwd_mask_required = BR_GROUPFWD_8021AD &
88262306a36Sopenharmony_ci					      ~(1u << br->group_addr[5]);
88362306a36Sopenharmony_ci}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ciint br_vlan_filter_toggle(struct net_bridge *br, unsigned long val,
88662306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
88762306a36Sopenharmony_ci{
88862306a36Sopenharmony_ci	struct switchdev_attr attr = {
88962306a36Sopenharmony_ci		.orig_dev = br->dev,
89062306a36Sopenharmony_ci		.id = SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING,
89162306a36Sopenharmony_ci		.flags = SWITCHDEV_F_SKIP_EOPNOTSUPP,
89262306a36Sopenharmony_ci		.u.vlan_filtering = val,
89362306a36Sopenharmony_ci	};
89462306a36Sopenharmony_ci	int err;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	if (br_opt_get(br, BROPT_VLAN_ENABLED) == !!val)
89762306a36Sopenharmony_ci		return 0;
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	br_opt_toggle(br, BROPT_VLAN_ENABLED, !!val);
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	err = switchdev_port_attr_set(br->dev, &attr, extack);
90262306a36Sopenharmony_ci	if (err && err != -EOPNOTSUPP) {
90362306a36Sopenharmony_ci		br_opt_toggle(br, BROPT_VLAN_ENABLED, !val);
90462306a36Sopenharmony_ci		return err;
90562306a36Sopenharmony_ci	}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	br_manage_promisc(br);
90862306a36Sopenharmony_ci	recalculate_group_addr(br);
90962306a36Sopenharmony_ci	br_recalculate_fwd_mask(br);
91062306a36Sopenharmony_ci	if (!val && br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) {
91162306a36Sopenharmony_ci		br_info(br, "vlan filtering disabled, automatically disabling multicast vlan snooping\n");
91262306a36Sopenharmony_ci		br_multicast_toggle_vlan_snooping(br, false, NULL);
91362306a36Sopenharmony_ci	}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	return 0;
91662306a36Sopenharmony_ci}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_cibool br_vlan_enabled(const struct net_device *dev)
91962306a36Sopenharmony_ci{
92062306a36Sopenharmony_ci	struct net_bridge *br = netdev_priv(dev);
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	return br_opt_get(br, BROPT_VLAN_ENABLED);
92362306a36Sopenharmony_ci}
92462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_vlan_enabled);
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ciint br_vlan_get_proto(const struct net_device *dev, u16 *p_proto)
92762306a36Sopenharmony_ci{
92862306a36Sopenharmony_ci	struct net_bridge *br = netdev_priv(dev);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	*p_proto = ntohs(br->vlan_proto);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	return 0;
93362306a36Sopenharmony_ci}
93462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_vlan_get_proto);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ciint __br_vlan_set_proto(struct net_bridge *br, __be16 proto,
93762306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
93862306a36Sopenharmony_ci{
93962306a36Sopenharmony_ci	struct switchdev_attr attr = {
94062306a36Sopenharmony_ci		.orig_dev = br->dev,
94162306a36Sopenharmony_ci		.id = SWITCHDEV_ATTR_ID_BRIDGE_VLAN_PROTOCOL,
94262306a36Sopenharmony_ci		.flags = SWITCHDEV_F_SKIP_EOPNOTSUPP,
94362306a36Sopenharmony_ci		.u.vlan_protocol = ntohs(proto),
94462306a36Sopenharmony_ci	};
94562306a36Sopenharmony_ci	int err = 0;
94662306a36Sopenharmony_ci	struct net_bridge_port *p;
94762306a36Sopenharmony_ci	struct net_bridge_vlan *vlan;
94862306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
94962306a36Sopenharmony_ci	__be16 oldproto = br->vlan_proto;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	if (br->vlan_proto == proto)
95262306a36Sopenharmony_ci		return 0;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	err = switchdev_port_attr_set(br->dev, &attr, extack);
95562306a36Sopenharmony_ci	if (err && err != -EOPNOTSUPP)
95662306a36Sopenharmony_ci		return err;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	/* Add VLANs for the new proto to the device filter. */
95962306a36Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list) {
96062306a36Sopenharmony_ci		vg = nbp_vlan_group(p);
96162306a36Sopenharmony_ci		list_for_each_entry(vlan, &vg->vlan_list, vlist) {
96262306a36Sopenharmony_ci			if (vlan->priv_flags & BR_VLFLAG_ADDED_BY_SWITCHDEV)
96362306a36Sopenharmony_ci				continue;
96462306a36Sopenharmony_ci			err = vlan_vid_add(p->dev, proto, vlan->vid);
96562306a36Sopenharmony_ci			if (err)
96662306a36Sopenharmony_ci				goto err_filt;
96762306a36Sopenharmony_ci		}
96862306a36Sopenharmony_ci	}
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	br->vlan_proto = proto;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	recalculate_group_addr(br);
97362306a36Sopenharmony_ci	br_recalculate_fwd_mask(br);
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	/* Delete VLANs for the old proto from the device filter. */
97662306a36Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list) {
97762306a36Sopenharmony_ci		vg = nbp_vlan_group(p);
97862306a36Sopenharmony_ci		list_for_each_entry(vlan, &vg->vlan_list, vlist) {
97962306a36Sopenharmony_ci			if (vlan->priv_flags & BR_VLFLAG_ADDED_BY_SWITCHDEV)
98062306a36Sopenharmony_ci				continue;
98162306a36Sopenharmony_ci			vlan_vid_del(p->dev, oldproto, vlan->vid);
98262306a36Sopenharmony_ci		}
98362306a36Sopenharmony_ci	}
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	return 0;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_cierr_filt:
98862306a36Sopenharmony_ci	attr.u.vlan_protocol = ntohs(oldproto);
98962306a36Sopenharmony_ci	switchdev_port_attr_set(br->dev, &attr, NULL);
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	list_for_each_entry_continue_reverse(vlan, &vg->vlan_list, vlist) {
99262306a36Sopenharmony_ci		if (vlan->priv_flags & BR_VLFLAG_ADDED_BY_SWITCHDEV)
99362306a36Sopenharmony_ci			continue;
99462306a36Sopenharmony_ci		vlan_vid_del(p->dev, proto, vlan->vid);
99562306a36Sopenharmony_ci	}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	list_for_each_entry_continue_reverse(p, &br->port_list, list) {
99862306a36Sopenharmony_ci		vg = nbp_vlan_group(p);
99962306a36Sopenharmony_ci		list_for_each_entry(vlan, &vg->vlan_list, vlist) {
100062306a36Sopenharmony_ci			if (vlan->priv_flags & BR_VLFLAG_ADDED_BY_SWITCHDEV)
100162306a36Sopenharmony_ci				continue;
100262306a36Sopenharmony_ci			vlan_vid_del(p->dev, proto, vlan->vid);
100362306a36Sopenharmony_ci		}
100462306a36Sopenharmony_ci	}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	return err;
100762306a36Sopenharmony_ci}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ciint br_vlan_set_proto(struct net_bridge *br, unsigned long val,
101062306a36Sopenharmony_ci		      struct netlink_ext_ack *extack)
101162306a36Sopenharmony_ci{
101262306a36Sopenharmony_ci	if (!eth_type_vlan(htons(val)))
101362306a36Sopenharmony_ci		return -EPROTONOSUPPORT;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	return __br_vlan_set_proto(br, htons(val), extack);
101662306a36Sopenharmony_ci}
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ciint br_vlan_set_stats(struct net_bridge *br, unsigned long val)
101962306a36Sopenharmony_ci{
102062306a36Sopenharmony_ci	switch (val) {
102162306a36Sopenharmony_ci	case 0:
102262306a36Sopenharmony_ci	case 1:
102362306a36Sopenharmony_ci		br_opt_toggle(br, BROPT_VLAN_STATS_ENABLED, !!val);
102462306a36Sopenharmony_ci		break;
102562306a36Sopenharmony_ci	default:
102662306a36Sopenharmony_ci		return -EINVAL;
102762306a36Sopenharmony_ci	}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	return 0;
103062306a36Sopenharmony_ci}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ciint br_vlan_set_stats_per_port(struct net_bridge *br, unsigned long val)
103362306a36Sopenharmony_ci{
103462306a36Sopenharmony_ci	struct net_bridge_port *p;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	/* allow to change the option if there are no port vlans configured */
103762306a36Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list) {
103862306a36Sopenharmony_ci		struct net_bridge_vlan_group *vg = nbp_vlan_group(p);
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci		if (vg->num_vlans)
104162306a36Sopenharmony_ci			return -EBUSY;
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	switch (val) {
104562306a36Sopenharmony_ci	case 0:
104662306a36Sopenharmony_ci	case 1:
104762306a36Sopenharmony_ci		br_opt_toggle(br, BROPT_VLAN_STATS_PER_PORT, !!val);
104862306a36Sopenharmony_ci		break;
104962306a36Sopenharmony_ci	default:
105062306a36Sopenharmony_ci		return -EINVAL;
105162306a36Sopenharmony_ci	}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	return 0;
105462306a36Sopenharmony_ci}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_cistatic bool vlan_default_pvid(struct net_bridge_vlan_group *vg, u16 vid)
105762306a36Sopenharmony_ci{
105862306a36Sopenharmony_ci	struct net_bridge_vlan *v;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	if (vid != vg->pvid)
106162306a36Sopenharmony_ci		return false;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	v = br_vlan_lookup(&vg->vlan_hash, vid);
106462306a36Sopenharmony_ci	if (v && br_vlan_should_use(v) &&
106562306a36Sopenharmony_ci	    (v->flags & BRIDGE_VLAN_INFO_UNTAGGED))
106662306a36Sopenharmony_ci		return true;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	return false;
106962306a36Sopenharmony_ci}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_cistatic void br_vlan_disable_default_pvid(struct net_bridge *br)
107262306a36Sopenharmony_ci{
107362306a36Sopenharmony_ci	struct net_bridge_port *p;
107462306a36Sopenharmony_ci	u16 pvid = br->default_pvid;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	/* Disable default_pvid on all ports where it is still
107762306a36Sopenharmony_ci	 * configured.
107862306a36Sopenharmony_ci	 */
107962306a36Sopenharmony_ci	if (vlan_default_pvid(br_vlan_group(br), pvid)) {
108062306a36Sopenharmony_ci		if (!br_vlan_delete(br, pvid))
108162306a36Sopenharmony_ci			br_vlan_notify(br, NULL, pvid, 0, RTM_DELVLAN);
108262306a36Sopenharmony_ci	}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list) {
108562306a36Sopenharmony_ci		if (vlan_default_pvid(nbp_vlan_group(p), pvid) &&
108662306a36Sopenharmony_ci		    !nbp_vlan_delete(p, pvid))
108762306a36Sopenharmony_ci			br_vlan_notify(br, p, pvid, 0, RTM_DELVLAN);
108862306a36Sopenharmony_ci	}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	br->default_pvid = 0;
109162306a36Sopenharmony_ci}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ciint __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid,
109462306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
109562306a36Sopenharmony_ci{
109662306a36Sopenharmony_ci	const struct net_bridge_vlan *pvent;
109762306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
109862306a36Sopenharmony_ci	struct net_bridge_port *p;
109962306a36Sopenharmony_ci	unsigned long *changed;
110062306a36Sopenharmony_ci	bool vlchange;
110162306a36Sopenharmony_ci	u16 old_pvid;
110262306a36Sopenharmony_ci	int err = 0;
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	if (!pvid) {
110562306a36Sopenharmony_ci		br_vlan_disable_default_pvid(br);
110662306a36Sopenharmony_ci		return 0;
110762306a36Sopenharmony_ci	}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	changed = bitmap_zalloc(BR_MAX_PORTS, GFP_KERNEL);
111062306a36Sopenharmony_ci	if (!changed)
111162306a36Sopenharmony_ci		return -ENOMEM;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	old_pvid = br->default_pvid;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	/* Update default_pvid config only if we do not conflict with
111662306a36Sopenharmony_ci	 * user configuration.
111762306a36Sopenharmony_ci	 */
111862306a36Sopenharmony_ci	vg = br_vlan_group(br);
111962306a36Sopenharmony_ci	pvent = br_vlan_find(vg, pvid);
112062306a36Sopenharmony_ci	if ((!old_pvid || vlan_default_pvid(vg, old_pvid)) &&
112162306a36Sopenharmony_ci	    (!pvent || !br_vlan_should_use(pvent))) {
112262306a36Sopenharmony_ci		err = br_vlan_add(br, pvid,
112362306a36Sopenharmony_ci				  BRIDGE_VLAN_INFO_PVID |
112462306a36Sopenharmony_ci				  BRIDGE_VLAN_INFO_UNTAGGED |
112562306a36Sopenharmony_ci				  BRIDGE_VLAN_INFO_BRENTRY,
112662306a36Sopenharmony_ci				  &vlchange, extack);
112762306a36Sopenharmony_ci		if (err)
112862306a36Sopenharmony_ci			goto out;
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci		if (br_vlan_delete(br, old_pvid))
113162306a36Sopenharmony_ci			br_vlan_notify(br, NULL, old_pvid, 0, RTM_DELVLAN);
113262306a36Sopenharmony_ci		br_vlan_notify(br, NULL, pvid, 0, RTM_NEWVLAN);
113362306a36Sopenharmony_ci		__set_bit(0, changed);
113462306a36Sopenharmony_ci	}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list) {
113762306a36Sopenharmony_ci		/* Update default_pvid config only if we do not conflict with
113862306a36Sopenharmony_ci		 * user configuration.
113962306a36Sopenharmony_ci		 */
114062306a36Sopenharmony_ci		vg = nbp_vlan_group(p);
114162306a36Sopenharmony_ci		if ((old_pvid &&
114262306a36Sopenharmony_ci		     !vlan_default_pvid(vg, old_pvid)) ||
114362306a36Sopenharmony_ci		    br_vlan_find(vg, pvid))
114462306a36Sopenharmony_ci			continue;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci		err = nbp_vlan_add(p, pvid,
114762306a36Sopenharmony_ci				   BRIDGE_VLAN_INFO_PVID |
114862306a36Sopenharmony_ci				   BRIDGE_VLAN_INFO_UNTAGGED,
114962306a36Sopenharmony_ci				   &vlchange, extack);
115062306a36Sopenharmony_ci		if (err)
115162306a36Sopenharmony_ci			goto err_port;
115262306a36Sopenharmony_ci		if (nbp_vlan_delete(p, old_pvid))
115362306a36Sopenharmony_ci			br_vlan_notify(br, p, old_pvid, 0, RTM_DELVLAN);
115462306a36Sopenharmony_ci		br_vlan_notify(p->br, p, pvid, 0, RTM_NEWVLAN);
115562306a36Sopenharmony_ci		__set_bit(p->port_no, changed);
115662306a36Sopenharmony_ci	}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	br->default_pvid = pvid;
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ciout:
116162306a36Sopenharmony_ci	bitmap_free(changed);
116262306a36Sopenharmony_ci	return err;
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_cierr_port:
116562306a36Sopenharmony_ci	list_for_each_entry_continue_reverse(p, &br->port_list, list) {
116662306a36Sopenharmony_ci		if (!test_bit(p->port_no, changed))
116762306a36Sopenharmony_ci			continue;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci		if (old_pvid) {
117062306a36Sopenharmony_ci			nbp_vlan_add(p, old_pvid,
117162306a36Sopenharmony_ci				     BRIDGE_VLAN_INFO_PVID |
117262306a36Sopenharmony_ci				     BRIDGE_VLAN_INFO_UNTAGGED,
117362306a36Sopenharmony_ci				     &vlchange, NULL);
117462306a36Sopenharmony_ci			br_vlan_notify(p->br, p, old_pvid, 0, RTM_NEWVLAN);
117562306a36Sopenharmony_ci		}
117662306a36Sopenharmony_ci		nbp_vlan_delete(p, pvid);
117762306a36Sopenharmony_ci		br_vlan_notify(br, p, pvid, 0, RTM_DELVLAN);
117862306a36Sopenharmony_ci	}
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	if (test_bit(0, changed)) {
118162306a36Sopenharmony_ci		if (old_pvid) {
118262306a36Sopenharmony_ci			br_vlan_add(br, old_pvid,
118362306a36Sopenharmony_ci				    BRIDGE_VLAN_INFO_PVID |
118462306a36Sopenharmony_ci				    BRIDGE_VLAN_INFO_UNTAGGED |
118562306a36Sopenharmony_ci				    BRIDGE_VLAN_INFO_BRENTRY,
118662306a36Sopenharmony_ci				    &vlchange, NULL);
118762306a36Sopenharmony_ci			br_vlan_notify(br, NULL, old_pvid, 0, RTM_NEWVLAN);
118862306a36Sopenharmony_ci		}
118962306a36Sopenharmony_ci		br_vlan_delete(br, pvid);
119062306a36Sopenharmony_ci		br_vlan_notify(br, NULL, pvid, 0, RTM_DELVLAN);
119162306a36Sopenharmony_ci	}
119262306a36Sopenharmony_ci	goto out;
119362306a36Sopenharmony_ci}
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ciint br_vlan_set_default_pvid(struct net_bridge *br, unsigned long val,
119662306a36Sopenharmony_ci			     struct netlink_ext_ack *extack)
119762306a36Sopenharmony_ci{
119862306a36Sopenharmony_ci	u16 pvid = val;
119962306a36Sopenharmony_ci	int err = 0;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	if (val >= VLAN_VID_MASK)
120262306a36Sopenharmony_ci		return -EINVAL;
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	if (pvid == br->default_pvid)
120562306a36Sopenharmony_ci		goto out;
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	/* Only allow default pvid change when filtering is disabled */
120862306a36Sopenharmony_ci	if (br_opt_get(br, BROPT_VLAN_ENABLED)) {
120962306a36Sopenharmony_ci		pr_info_once("Please disable vlan filtering to change default_pvid\n");
121062306a36Sopenharmony_ci		err = -EPERM;
121162306a36Sopenharmony_ci		goto out;
121262306a36Sopenharmony_ci	}
121362306a36Sopenharmony_ci	err = __br_vlan_set_default_pvid(br, pvid, extack);
121462306a36Sopenharmony_ciout:
121562306a36Sopenharmony_ci	return err;
121662306a36Sopenharmony_ci}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ciint br_vlan_init(struct net_bridge *br)
121962306a36Sopenharmony_ci{
122062306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
122162306a36Sopenharmony_ci	int ret = -ENOMEM;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	vg = kzalloc(sizeof(*vg), GFP_KERNEL);
122462306a36Sopenharmony_ci	if (!vg)
122562306a36Sopenharmony_ci		goto out;
122662306a36Sopenharmony_ci	ret = rhashtable_init(&vg->vlan_hash, &br_vlan_rht_params);
122762306a36Sopenharmony_ci	if (ret)
122862306a36Sopenharmony_ci		goto err_rhtbl;
122962306a36Sopenharmony_ci	ret = vlan_tunnel_init(vg);
123062306a36Sopenharmony_ci	if (ret)
123162306a36Sopenharmony_ci		goto err_tunnel_init;
123262306a36Sopenharmony_ci	INIT_LIST_HEAD(&vg->vlan_list);
123362306a36Sopenharmony_ci	br->vlan_proto = htons(ETH_P_8021Q);
123462306a36Sopenharmony_ci	br->default_pvid = 1;
123562306a36Sopenharmony_ci	rcu_assign_pointer(br->vlgrp, vg);
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ciout:
123862306a36Sopenharmony_ci	return ret;
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_cierr_tunnel_init:
124162306a36Sopenharmony_ci	rhashtable_destroy(&vg->vlan_hash);
124262306a36Sopenharmony_cierr_rhtbl:
124362306a36Sopenharmony_ci	kfree(vg);
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	goto out;
124662306a36Sopenharmony_ci}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ciint nbp_vlan_init(struct net_bridge_port *p, struct netlink_ext_ack *extack)
124962306a36Sopenharmony_ci{
125062306a36Sopenharmony_ci	struct switchdev_attr attr = {
125162306a36Sopenharmony_ci		.orig_dev = p->br->dev,
125262306a36Sopenharmony_ci		.id = SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING,
125362306a36Sopenharmony_ci		.flags = SWITCHDEV_F_SKIP_EOPNOTSUPP,
125462306a36Sopenharmony_ci		.u.vlan_filtering = br_opt_get(p->br, BROPT_VLAN_ENABLED),
125562306a36Sopenharmony_ci	};
125662306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
125762306a36Sopenharmony_ci	int ret = -ENOMEM;
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	vg = kzalloc(sizeof(struct net_bridge_vlan_group), GFP_KERNEL);
126062306a36Sopenharmony_ci	if (!vg)
126162306a36Sopenharmony_ci		goto out;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	ret = switchdev_port_attr_set(p->dev, &attr, extack);
126462306a36Sopenharmony_ci	if (ret && ret != -EOPNOTSUPP)
126562306a36Sopenharmony_ci		goto err_vlan_enabled;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	ret = rhashtable_init(&vg->vlan_hash, &br_vlan_rht_params);
126862306a36Sopenharmony_ci	if (ret)
126962306a36Sopenharmony_ci		goto err_rhtbl;
127062306a36Sopenharmony_ci	ret = vlan_tunnel_init(vg);
127162306a36Sopenharmony_ci	if (ret)
127262306a36Sopenharmony_ci		goto err_tunnel_init;
127362306a36Sopenharmony_ci	INIT_LIST_HEAD(&vg->vlan_list);
127462306a36Sopenharmony_ci	rcu_assign_pointer(p->vlgrp, vg);
127562306a36Sopenharmony_ci	if (p->br->default_pvid) {
127662306a36Sopenharmony_ci		bool changed;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci		ret = nbp_vlan_add(p, p->br->default_pvid,
127962306a36Sopenharmony_ci				   BRIDGE_VLAN_INFO_PVID |
128062306a36Sopenharmony_ci				   BRIDGE_VLAN_INFO_UNTAGGED,
128162306a36Sopenharmony_ci				   &changed, extack);
128262306a36Sopenharmony_ci		if (ret)
128362306a36Sopenharmony_ci			goto err_vlan_add;
128462306a36Sopenharmony_ci		br_vlan_notify(p->br, p, p->br->default_pvid, 0, RTM_NEWVLAN);
128562306a36Sopenharmony_ci	}
128662306a36Sopenharmony_ciout:
128762306a36Sopenharmony_ci	return ret;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_cierr_vlan_add:
129062306a36Sopenharmony_ci	RCU_INIT_POINTER(p->vlgrp, NULL);
129162306a36Sopenharmony_ci	synchronize_rcu();
129262306a36Sopenharmony_ci	vlan_tunnel_deinit(vg);
129362306a36Sopenharmony_cierr_tunnel_init:
129462306a36Sopenharmony_ci	rhashtable_destroy(&vg->vlan_hash);
129562306a36Sopenharmony_cierr_rhtbl:
129662306a36Sopenharmony_cierr_vlan_enabled:
129762306a36Sopenharmony_ci	kfree(vg);
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	goto out;
130062306a36Sopenharmony_ci}
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci/* Must be protected by RTNL.
130362306a36Sopenharmony_ci * Must be called with vid in range from 1 to 4094 inclusive.
130462306a36Sopenharmony_ci * changed must be true only if the vlan was created or updated
130562306a36Sopenharmony_ci */
130662306a36Sopenharmony_ciint nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags,
130762306a36Sopenharmony_ci		 bool *changed, struct netlink_ext_ack *extack)
130862306a36Sopenharmony_ci{
130962306a36Sopenharmony_ci	struct net_bridge_vlan *vlan;
131062306a36Sopenharmony_ci	int ret;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	ASSERT_RTNL();
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	*changed = false;
131562306a36Sopenharmony_ci	vlan = br_vlan_find(nbp_vlan_group(port), vid);
131662306a36Sopenharmony_ci	if (vlan) {
131762306a36Sopenharmony_ci		bool would_change = __vlan_flags_would_change(vlan, flags);
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci		if (would_change) {
132062306a36Sopenharmony_ci			/* Pass the flags to the hardware bridge */
132162306a36Sopenharmony_ci			ret = br_switchdev_port_vlan_add(port->dev, vid, flags,
132262306a36Sopenharmony_ci							 true, extack);
132362306a36Sopenharmony_ci			if (ret && ret != -EOPNOTSUPP)
132462306a36Sopenharmony_ci				return ret;
132562306a36Sopenharmony_ci		}
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci		__vlan_flags_commit(vlan, flags);
132862306a36Sopenharmony_ci		*changed = would_change;
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci		return 0;
133162306a36Sopenharmony_ci	}
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
133462306a36Sopenharmony_ci	if (!vlan)
133562306a36Sopenharmony_ci		return -ENOMEM;
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	vlan->vid = vid;
133862306a36Sopenharmony_ci	vlan->port = port;
133962306a36Sopenharmony_ci	ret = __vlan_add(vlan, flags, extack);
134062306a36Sopenharmony_ci	if (ret)
134162306a36Sopenharmony_ci		kfree(vlan);
134262306a36Sopenharmony_ci	else
134362306a36Sopenharmony_ci		*changed = true;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	return ret;
134662306a36Sopenharmony_ci}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci/* Must be protected by RTNL.
134962306a36Sopenharmony_ci * Must be called with vid in range from 1 to 4094 inclusive.
135062306a36Sopenharmony_ci */
135162306a36Sopenharmony_ciint nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
135262306a36Sopenharmony_ci{
135362306a36Sopenharmony_ci	struct net_bridge_vlan *v;
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	ASSERT_RTNL();
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	v = br_vlan_find(nbp_vlan_group(port), vid);
135862306a36Sopenharmony_ci	if (!v)
135962306a36Sopenharmony_ci		return -ENOENT;
136062306a36Sopenharmony_ci	br_fdb_find_delete_local(port->br, port, port->dev->dev_addr, vid);
136162306a36Sopenharmony_ci	br_fdb_delete_by_port(port->br, port, vid, 0);
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	return __vlan_del(v);
136462306a36Sopenharmony_ci}
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_civoid nbp_vlan_flush(struct net_bridge_port *port)
136762306a36Sopenharmony_ci{
136862306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	ASSERT_RTNL();
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	vg = nbp_vlan_group(port);
137362306a36Sopenharmony_ci	__vlan_flush(port->br, port, vg);
137462306a36Sopenharmony_ci	RCU_INIT_POINTER(port->vlgrp, NULL);
137562306a36Sopenharmony_ci	synchronize_rcu();
137662306a36Sopenharmony_ci	__vlan_group_free(vg);
137762306a36Sopenharmony_ci}
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_civoid br_vlan_get_stats(const struct net_bridge_vlan *v,
138062306a36Sopenharmony_ci		       struct pcpu_sw_netstats *stats)
138162306a36Sopenharmony_ci{
138262306a36Sopenharmony_ci	int i;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	memset(stats, 0, sizeof(*stats));
138562306a36Sopenharmony_ci	for_each_possible_cpu(i) {
138662306a36Sopenharmony_ci		u64 rxpackets, rxbytes, txpackets, txbytes;
138762306a36Sopenharmony_ci		struct pcpu_sw_netstats *cpu_stats;
138862306a36Sopenharmony_ci		unsigned int start;
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci		cpu_stats = per_cpu_ptr(v->stats, i);
139162306a36Sopenharmony_ci		do {
139262306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&cpu_stats->syncp);
139362306a36Sopenharmony_ci			rxpackets = u64_stats_read(&cpu_stats->rx_packets);
139462306a36Sopenharmony_ci			rxbytes = u64_stats_read(&cpu_stats->rx_bytes);
139562306a36Sopenharmony_ci			txbytes = u64_stats_read(&cpu_stats->tx_bytes);
139662306a36Sopenharmony_ci			txpackets = u64_stats_read(&cpu_stats->tx_packets);
139762306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&cpu_stats->syncp, start));
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci		u64_stats_add(&stats->rx_packets, rxpackets);
140062306a36Sopenharmony_ci		u64_stats_add(&stats->rx_bytes, rxbytes);
140162306a36Sopenharmony_ci		u64_stats_add(&stats->tx_bytes, txbytes);
140262306a36Sopenharmony_ci		u64_stats_add(&stats->tx_packets, txpackets);
140362306a36Sopenharmony_ci	}
140462306a36Sopenharmony_ci}
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ciint br_vlan_get_pvid(const struct net_device *dev, u16 *p_pvid)
140762306a36Sopenharmony_ci{
140862306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
140962306a36Sopenharmony_ci	struct net_bridge_port *p;
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	ASSERT_RTNL();
141262306a36Sopenharmony_ci	p = br_port_get_check_rtnl(dev);
141362306a36Sopenharmony_ci	if (p)
141462306a36Sopenharmony_ci		vg = nbp_vlan_group(p);
141562306a36Sopenharmony_ci	else if (netif_is_bridge_master(dev))
141662306a36Sopenharmony_ci		vg = br_vlan_group(netdev_priv(dev));
141762306a36Sopenharmony_ci	else
141862306a36Sopenharmony_ci		return -EINVAL;
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	*p_pvid = br_get_pvid(vg);
142162306a36Sopenharmony_ci	return 0;
142262306a36Sopenharmony_ci}
142362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_vlan_get_pvid);
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ciint br_vlan_get_pvid_rcu(const struct net_device *dev, u16 *p_pvid)
142662306a36Sopenharmony_ci{
142762306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
142862306a36Sopenharmony_ci	struct net_bridge_port *p;
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	p = br_port_get_check_rcu(dev);
143162306a36Sopenharmony_ci	if (p)
143262306a36Sopenharmony_ci		vg = nbp_vlan_group_rcu(p);
143362306a36Sopenharmony_ci	else if (netif_is_bridge_master(dev))
143462306a36Sopenharmony_ci		vg = br_vlan_group_rcu(netdev_priv(dev));
143562306a36Sopenharmony_ci	else
143662306a36Sopenharmony_ci		return -EINVAL;
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	*p_pvid = br_get_pvid(vg);
143962306a36Sopenharmony_ci	return 0;
144062306a36Sopenharmony_ci}
144162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_vlan_get_pvid_rcu);
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_civoid br_vlan_fill_forward_path_pvid(struct net_bridge *br,
144462306a36Sopenharmony_ci				    struct net_device_path_ctx *ctx,
144562306a36Sopenharmony_ci				    struct net_device_path *path)
144662306a36Sopenharmony_ci{
144762306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
144862306a36Sopenharmony_ci	int idx = ctx->num_vlans - 1;
144962306a36Sopenharmony_ci	u16 vid;
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	path->bridge.vlan_mode = DEV_PATH_BR_VLAN_KEEP;
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	if (!br_opt_get(br, BROPT_VLAN_ENABLED))
145462306a36Sopenharmony_ci		return;
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	vg = br_vlan_group(br);
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	if (idx >= 0 &&
145962306a36Sopenharmony_ci	    ctx->vlan[idx].proto == br->vlan_proto) {
146062306a36Sopenharmony_ci		vid = ctx->vlan[idx].id;
146162306a36Sopenharmony_ci	} else {
146262306a36Sopenharmony_ci		path->bridge.vlan_mode = DEV_PATH_BR_VLAN_TAG;
146362306a36Sopenharmony_ci		vid = br_get_pvid(vg);
146462306a36Sopenharmony_ci	}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	path->bridge.vlan_id = vid;
146762306a36Sopenharmony_ci	path->bridge.vlan_proto = br->vlan_proto;
146862306a36Sopenharmony_ci}
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ciint br_vlan_fill_forward_path_mode(struct net_bridge *br,
147162306a36Sopenharmony_ci				   struct net_bridge_port *dst,
147262306a36Sopenharmony_ci				   struct net_device_path *path)
147362306a36Sopenharmony_ci{
147462306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
147562306a36Sopenharmony_ci	struct net_bridge_vlan *v;
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	if (!br_opt_get(br, BROPT_VLAN_ENABLED))
147862306a36Sopenharmony_ci		return 0;
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	vg = nbp_vlan_group_rcu(dst);
148162306a36Sopenharmony_ci	v = br_vlan_find(vg, path->bridge.vlan_id);
148262306a36Sopenharmony_ci	if (!v || !br_vlan_should_use(v))
148362306a36Sopenharmony_ci		return -EINVAL;
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	if (!(v->flags & BRIDGE_VLAN_INFO_UNTAGGED))
148662306a36Sopenharmony_ci		return 0;
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	if (path->bridge.vlan_mode == DEV_PATH_BR_VLAN_TAG)
148962306a36Sopenharmony_ci		path->bridge.vlan_mode = DEV_PATH_BR_VLAN_KEEP;
149062306a36Sopenharmony_ci	else if (v->priv_flags & BR_VLFLAG_ADDED_BY_SWITCHDEV)
149162306a36Sopenharmony_ci		path->bridge.vlan_mode = DEV_PATH_BR_VLAN_UNTAG_HW;
149262306a36Sopenharmony_ci	else
149362306a36Sopenharmony_ci		path->bridge.vlan_mode = DEV_PATH_BR_VLAN_UNTAG;
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	return 0;
149662306a36Sopenharmony_ci}
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ciint br_vlan_get_info(const struct net_device *dev, u16 vid,
149962306a36Sopenharmony_ci		     struct bridge_vlan_info *p_vinfo)
150062306a36Sopenharmony_ci{
150162306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
150262306a36Sopenharmony_ci	struct net_bridge_vlan *v;
150362306a36Sopenharmony_ci	struct net_bridge_port *p;
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	ASSERT_RTNL();
150662306a36Sopenharmony_ci	p = br_port_get_check_rtnl(dev);
150762306a36Sopenharmony_ci	if (p)
150862306a36Sopenharmony_ci		vg = nbp_vlan_group(p);
150962306a36Sopenharmony_ci	else if (netif_is_bridge_master(dev))
151062306a36Sopenharmony_ci		vg = br_vlan_group(netdev_priv(dev));
151162306a36Sopenharmony_ci	else
151262306a36Sopenharmony_ci		return -EINVAL;
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	v = br_vlan_find(vg, vid);
151562306a36Sopenharmony_ci	if (!v)
151662306a36Sopenharmony_ci		return -ENOENT;
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	p_vinfo->vid = vid;
151962306a36Sopenharmony_ci	p_vinfo->flags = v->flags;
152062306a36Sopenharmony_ci	if (vid == br_get_pvid(vg))
152162306a36Sopenharmony_ci		p_vinfo->flags |= BRIDGE_VLAN_INFO_PVID;
152262306a36Sopenharmony_ci	return 0;
152362306a36Sopenharmony_ci}
152462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_vlan_get_info);
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ciint br_vlan_get_info_rcu(const struct net_device *dev, u16 vid,
152762306a36Sopenharmony_ci			 struct bridge_vlan_info *p_vinfo)
152862306a36Sopenharmony_ci{
152962306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
153062306a36Sopenharmony_ci	struct net_bridge_vlan *v;
153162306a36Sopenharmony_ci	struct net_bridge_port *p;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	p = br_port_get_check_rcu(dev);
153462306a36Sopenharmony_ci	if (p)
153562306a36Sopenharmony_ci		vg = nbp_vlan_group_rcu(p);
153662306a36Sopenharmony_ci	else if (netif_is_bridge_master(dev))
153762306a36Sopenharmony_ci		vg = br_vlan_group_rcu(netdev_priv(dev));
153862306a36Sopenharmony_ci	else
153962306a36Sopenharmony_ci		return -EINVAL;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	v = br_vlan_find(vg, vid);
154262306a36Sopenharmony_ci	if (!v)
154362306a36Sopenharmony_ci		return -ENOENT;
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	p_vinfo->vid = vid;
154662306a36Sopenharmony_ci	p_vinfo->flags = v->flags;
154762306a36Sopenharmony_ci	if (vid == br_get_pvid(vg))
154862306a36Sopenharmony_ci		p_vinfo->flags |= BRIDGE_VLAN_INFO_PVID;
154962306a36Sopenharmony_ci	return 0;
155062306a36Sopenharmony_ci}
155162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(br_vlan_get_info_rcu);
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_cistatic int br_vlan_is_bind_vlan_dev(const struct net_device *dev)
155462306a36Sopenharmony_ci{
155562306a36Sopenharmony_ci	return is_vlan_dev(dev) &&
155662306a36Sopenharmony_ci		!!(vlan_dev_priv(dev)->flags & VLAN_FLAG_BRIDGE_BINDING);
155762306a36Sopenharmony_ci}
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_cistatic int br_vlan_is_bind_vlan_dev_fn(struct net_device *dev,
156062306a36Sopenharmony_ci			       __always_unused struct netdev_nested_priv *priv)
156162306a36Sopenharmony_ci{
156262306a36Sopenharmony_ci	return br_vlan_is_bind_vlan_dev(dev);
156362306a36Sopenharmony_ci}
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_cistatic bool br_vlan_has_upper_bind_vlan_dev(struct net_device *dev)
156662306a36Sopenharmony_ci{
156762306a36Sopenharmony_ci	int found;
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci	rcu_read_lock();
157062306a36Sopenharmony_ci	found = netdev_walk_all_upper_dev_rcu(dev, br_vlan_is_bind_vlan_dev_fn,
157162306a36Sopenharmony_ci					      NULL);
157262306a36Sopenharmony_ci	rcu_read_unlock();
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci	return !!found;
157562306a36Sopenharmony_ci}
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_cistruct br_vlan_bind_walk_data {
157862306a36Sopenharmony_ci	u16 vid;
157962306a36Sopenharmony_ci	struct net_device *result;
158062306a36Sopenharmony_ci};
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_cistatic int br_vlan_match_bind_vlan_dev_fn(struct net_device *dev,
158362306a36Sopenharmony_ci					  struct netdev_nested_priv *priv)
158462306a36Sopenharmony_ci{
158562306a36Sopenharmony_ci	struct br_vlan_bind_walk_data *data = priv->data;
158662306a36Sopenharmony_ci	int found = 0;
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	if (br_vlan_is_bind_vlan_dev(dev) &&
158962306a36Sopenharmony_ci	    vlan_dev_priv(dev)->vlan_id == data->vid) {
159062306a36Sopenharmony_ci		data->result = dev;
159162306a36Sopenharmony_ci		found = 1;
159262306a36Sopenharmony_ci	}
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	return found;
159562306a36Sopenharmony_ci}
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_cistatic struct net_device *
159862306a36Sopenharmony_cibr_vlan_get_upper_bind_vlan_dev(struct net_device *dev, u16 vid)
159962306a36Sopenharmony_ci{
160062306a36Sopenharmony_ci	struct br_vlan_bind_walk_data data = {
160162306a36Sopenharmony_ci		.vid = vid,
160262306a36Sopenharmony_ci	};
160362306a36Sopenharmony_ci	struct netdev_nested_priv priv = {
160462306a36Sopenharmony_ci		.data = (void *)&data,
160562306a36Sopenharmony_ci	};
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	rcu_read_lock();
160862306a36Sopenharmony_ci	netdev_walk_all_upper_dev_rcu(dev, br_vlan_match_bind_vlan_dev_fn,
160962306a36Sopenharmony_ci				      &priv);
161062306a36Sopenharmony_ci	rcu_read_unlock();
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	return data.result;
161362306a36Sopenharmony_ci}
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_cistatic bool br_vlan_is_dev_up(const struct net_device *dev)
161662306a36Sopenharmony_ci{
161762306a36Sopenharmony_ci	return  !!(dev->flags & IFF_UP) && netif_oper_up(dev);
161862306a36Sopenharmony_ci}
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_cistatic void br_vlan_set_vlan_dev_state(const struct net_bridge *br,
162162306a36Sopenharmony_ci				       struct net_device *vlan_dev)
162262306a36Sopenharmony_ci{
162362306a36Sopenharmony_ci	u16 vid = vlan_dev_priv(vlan_dev)->vlan_id;
162462306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
162562306a36Sopenharmony_ci	struct net_bridge_port *p;
162662306a36Sopenharmony_ci	bool has_carrier = false;
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	if (!netif_carrier_ok(br->dev)) {
162962306a36Sopenharmony_ci		netif_carrier_off(vlan_dev);
163062306a36Sopenharmony_ci		return;
163162306a36Sopenharmony_ci	}
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list) {
163462306a36Sopenharmony_ci		vg = nbp_vlan_group(p);
163562306a36Sopenharmony_ci		if (br_vlan_find(vg, vid) && br_vlan_is_dev_up(p->dev)) {
163662306a36Sopenharmony_ci			has_carrier = true;
163762306a36Sopenharmony_ci			break;
163862306a36Sopenharmony_ci		}
163962306a36Sopenharmony_ci	}
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	if (has_carrier)
164262306a36Sopenharmony_ci		netif_carrier_on(vlan_dev);
164362306a36Sopenharmony_ci	else
164462306a36Sopenharmony_ci		netif_carrier_off(vlan_dev);
164562306a36Sopenharmony_ci}
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_cistatic void br_vlan_set_all_vlan_dev_state(struct net_bridge_port *p)
164862306a36Sopenharmony_ci{
164962306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg = nbp_vlan_group(p);
165062306a36Sopenharmony_ci	struct net_bridge_vlan *vlan;
165162306a36Sopenharmony_ci	struct net_device *vlan_dev;
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	list_for_each_entry(vlan, &vg->vlan_list, vlist) {
165462306a36Sopenharmony_ci		vlan_dev = br_vlan_get_upper_bind_vlan_dev(p->br->dev,
165562306a36Sopenharmony_ci							   vlan->vid);
165662306a36Sopenharmony_ci		if (vlan_dev) {
165762306a36Sopenharmony_ci			if (br_vlan_is_dev_up(p->dev)) {
165862306a36Sopenharmony_ci				if (netif_carrier_ok(p->br->dev))
165962306a36Sopenharmony_ci					netif_carrier_on(vlan_dev);
166062306a36Sopenharmony_ci			} else {
166162306a36Sopenharmony_ci				br_vlan_set_vlan_dev_state(p->br, vlan_dev);
166262306a36Sopenharmony_ci			}
166362306a36Sopenharmony_ci		}
166462306a36Sopenharmony_ci	}
166562306a36Sopenharmony_ci}
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_cistatic void br_vlan_upper_change(struct net_device *dev,
166862306a36Sopenharmony_ci				 struct net_device *upper_dev,
166962306a36Sopenharmony_ci				 bool linking)
167062306a36Sopenharmony_ci{
167162306a36Sopenharmony_ci	struct net_bridge *br = netdev_priv(dev);
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	if (!br_vlan_is_bind_vlan_dev(upper_dev))
167462306a36Sopenharmony_ci		return;
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci	if (linking) {
167762306a36Sopenharmony_ci		br_vlan_set_vlan_dev_state(br, upper_dev);
167862306a36Sopenharmony_ci		br_opt_toggle(br, BROPT_VLAN_BRIDGE_BINDING, true);
167962306a36Sopenharmony_ci	} else {
168062306a36Sopenharmony_ci		br_opt_toggle(br, BROPT_VLAN_BRIDGE_BINDING,
168162306a36Sopenharmony_ci			      br_vlan_has_upper_bind_vlan_dev(dev));
168262306a36Sopenharmony_ci	}
168362306a36Sopenharmony_ci}
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_cistruct br_vlan_link_state_walk_data {
168662306a36Sopenharmony_ci	struct net_bridge *br;
168762306a36Sopenharmony_ci};
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_cistatic int br_vlan_link_state_change_fn(struct net_device *vlan_dev,
169062306a36Sopenharmony_ci					struct netdev_nested_priv *priv)
169162306a36Sopenharmony_ci{
169262306a36Sopenharmony_ci	struct br_vlan_link_state_walk_data *data = priv->data;
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci	if (br_vlan_is_bind_vlan_dev(vlan_dev))
169562306a36Sopenharmony_ci		br_vlan_set_vlan_dev_state(data->br, vlan_dev);
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	return 0;
169862306a36Sopenharmony_ci}
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_cistatic void br_vlan_link_state_change(struct net_device *dev,
170162306a36Sopenharmony_ci				      struct net_bridge *br)
170262306a36Sopenharmony_ci{
170362306a36Sopenharmony_ci	struct br_vlan_link_state_walk_data data = {
170462306a36Sopenharmony_ci		.br = br
170562306a36Sopenharmony_ci	};
170662306a36Sopenharmony_ci	struct netdev_nested_priv priv = {
170762306a36Sopenharmony_ci		.data = (void *)&data,
170862306a36Sopenharmony_ci	};
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci	rcu_read_lock();
171162306a36Sopenharmony_ci	netdev_walk_all_upper_dev_rcu(dev, br_vlan_link_state_change_fn,
171262306a36Sopenharmony_ci				      &priv);
171362306a36Sopenharmony_ci	rcu_read_unlock();
171462306a36Sopenharmony_ci}
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci/* Must be protected by RTNL. */
171762306a36Sopenharmony_cistatic void nbp_vlan_set_vlan_dev_state(struct net_bridge_port *p, u16 vid)
171862306a36Sopenharmony_ci{
171962306a36Sopenharmony_ci	struct net_device *vlan_dev;
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	if (!br_opt_get(p->br, BROPT_VLAN_BRIDGE_BINDING))
172262306a36Sopenharmony_ci		return;
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	vlan_dev = br_vlan_get_upper_bind_vlan_dev(p->br->dev, vid);
172562306a36Sopenharmony_ci	if (vlan_dev)
172662306a36Sopenharmony_ci		br_vlan_set_vlan_dev_state(p->br, vlan_dev);
172762306a36Sopenharmony_ci}
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci/* Must be protected by RTNL. */
173062306a36Sopenharmony_ciint br_vlan_bridge_event(struct net_device *dev, unsigned long event, void *ptr)
173162306a36Sopenharmony_ci{
173262306a36Sopenharmony_ci	struct netdev_notifier_changeupper_info *info;
173362306a36Sopenharmony_ci	struct net_bridge *br = netdev_priv(dev);
173462306a36Sopenharmony_ci	int vlcmd = 0, ret = 0;
173562306a36Sopenharmony_ci	bool changed = false;
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	switch (event) {
173862306a36Sopenharmony_ci	case NETDEV_REGISTER:
173962306a36Sopenharmony_ci		ret = br_vlan_add(br, br->default_pvid,
174062306a36Sopenharmony_ci				  BRIDGE_VLAN_INFO_PVID |
174162306a36Sopenharmony_ci				  BRIDGE_VLAN_INFO_UNTAGGED |
174262306a36Sopenharmony_ci				  BRIDGE_VLAN_INFO_BRENTRY, &changed, NULL);
174362306a36Sopenharmony_ci		vlcmd = RTM_NEWVLAN;
174462306a36Sopenharmony_ci		break;
174562306a36Sopenharmony_ci	case NETDEV_UNREGISTER:
174662306a36Sopenharmony_ci		changed = !br_vlan_delete(br, br->default_pvid);
174762306a36Sopenharmony_ci		vlcmd = RTM_DELVLAN;
174862306a36Sopenharmony_ci		break;
174962306a36Sopenharmony_ci	case NETDEV_CHANGEUPPER:
175062306a36Sopenharmony_ci		info = ptr;
175162306a36Sopenharmony_ci		br_vlan_upper_change(dev, info->upper_dev, info->linking);
175262306a36Sopenharmony_ci		break;
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	case NETDEV_CHANGE:
175562306a36Sopenharmony_ci	case NETDEV_UP:
175662306a36Sopenharmony_ci		if (!br_opt_get(br, BROPT_VLAN_BRIDGE_BINDING))
175762306a36Sopenharmony_ci			break;
175862306a36Sopenharmony_ci		br_vlan_link_state_change(dev, br);
175962306a36Sopenharmony_ci		break;
176062306a36Sopenharmony_ci	}
176162306a36Sopenharmony_ci	if (changed)
176262306a36Sopenharmony_ci		br_vlan_notify(br, NULL, br->default_pvid, 0, vlcmd);
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	return ret;
176562306a36Sopenharmony_ci}
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci/* Must be protected by RTNL. */
176862306a36Sopenharmony_civoid br_vlan_port_event(struct net_bridge_port *p, unsigned long event)
176962306a36Sopenharmony_ci{
177062306a36Sopenharmony_ci	if (!br_opt_get(p->br, BROPT_VLAN_BRIDGE_BINDING))
177162306a36Sopenharmony_ci		return;
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	switch (event) {
177462306a36Sopenharmony_ci	case NETDEV_CHANGE:
177562306a36Sopenharmony_ci	case NETDEV_DOWN:
177662306a36Sopenharmony_ci	case NETDEV_UP:
177762306a36Sopenharmony_ci		br_vlan_set_all_vlan_dev_state(p);
177862306a36Sopenharmony_ci		break;
177962306a36Sopenharmony_ci	}
178062306a36Sopenharmony_ci}
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_cistatic bool br_vlan_stats_fill(struct sk_buff *skb,
178362306a36Sopenharmony_ci			       const struct net_bridge_vlan *v)
178462306a36Sopenharmony_ci{
178562306a36Sopenharmony_ci	struct pcpu_sw_netstats stats;
178662306a36Sopenharmony_ci	struct nlattr *nest;
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci	nest = nla_nest_start(skb, BRIDGE_VLANDB_ENTRY_STATS);
178962306a36Sopenharmony_ci	if (!nest)
179062306a36Sopenharmony_ci		return false;
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci	br_vlan_get_stats(v, &stats);
179362306a36Sopenharmony_ci	if (nla_put_u64_64bit(skb, BRIDGE_VLANDB_STATS_RX_BYTES,
179462306a36Sopenharmony_ci			      u64_stats_read(&stats.rx_bytes),
179562306a36Sopenharmony_ci			      BRIDGE_VLANDB_STATS_PAD) ||
179662306a36Sopenharmony_ci	    nla_put_u64_64bit(skb, BRIDGE_VLANDB_STATS_RX_PACKETS,
179762306a36Sopenharmony_ci			      u64_stats_read(&stats.rx_packets),
179862306a36Sopenharmony_ci			      BRIDGE_VLANDB_STATS_PAD) ||
179962306a36Sopenharmony_ci	    nla_put_u64_64bit(skb, BRIDGE_VLANDB_STATS_TX_BYTES,
180062306a36Sopenharmony_ci			      u64_stats_read(&stats.tx_bytes),
180162306a36Sopenharmony_ci			      BRIDGE_VLANDB_STATS_PAD) ||
180262306a36Sopenharmony_ci	    nla_put_u64_64bit(skb, BRIDGE_VLANDB_STATS_TX_PACKETS,
180362306a36Sopenharmony_ci			      u64_stats_read(&stats.tx_packets),
180462306a36Sopenharmony_ci			      BRIDGE_VLANDB_STATS_PAD))
180562306a36Sopenharmony_ci		goto out_err;
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci	nla_nest_end(skb, nest);
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	return true;
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ciout_err:
181262306a36Sopenharmony_ci	nla_nest_cancel(skb, nest);
181362306a36Sopenharmony_ci	return false;
181462306a36Sopenharmony_ci}
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci/* v_opts is used to dump the options which must be equal in the whole range */
181762306a36Sopenharmony_cistatic bool br_vlan_fill_vids(struct sk_buff *skb, u16 vid, u16 vid_range,
181862306a36Sopenharmony_ci			      const struct net_bridge_vlan *v_opts,
181962306a36Sopenharmony_ci			      const struct net_bridge_port *p,
182062306a36Sopenharmony_ci			      u16 flags,
182162306a36Sopenharmony_ci			      bool dump_stats)
182262306a36Sopenharmony_ci{
182362306a36Sopenharmony_ci	struct bridge_vlan_info info;
182462306a36Sopenharmony_ci	struct nlattr *nest;
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci	nest = nla_nest_start(skb, BRIDGE_VLANDB_ENTRY);
182762306a36Sopenharmony_ci	if (!nest)
182862306a36Sopenharmony_ci		return false;
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	memset(&info, 0, sizeof(info));
183162306a36Sopenharmony_ci	info.vid = vid;
183262306a36Sopenharmony_ci	if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
183362306a36Sopenharmony_ci		info.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
183462306a36Sopenharmony_ci	if (flags & BRIDGE_VLAN_INFO_PVID)
183562306a36Sopenharmony_ci		info.flags |= BRIDGE_VLAN_INFO_PVID;
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	if (nla_put(skb, BRIDGE_VLANDB_ENTRY_INFO, sizeof(info), &info))
183862306a36Sopenharmony_ci		goto out_err;
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	if (vid_range && vid < vid_range &&
184162306a36Sopenharmony_ci	    !(flags & BRIDGE_VLAN_INFO_PVID) &&
184262306a36Sopenharmony_ci	    nla_put_u16(skb, BRIDGE_VLANDB_ENTRY_RANGE, vid_range))
184362306a36Sopenharmony_ci		goto out_err;
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_ci	if (v_opts) {
184662306a36Sopenharmony_ci		if (!br_vlan_opts_fill(skb, v_opts, p))
184762306a36Sopenharmony_ci			goto out_err;
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci		if (dump_stats && !br_vlan_stats_fill(skb, v_opts))
185062306a36Sopenharmony_ci			goto out_err;
185162306a36Sopenharmony_ci	}
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_ci	nla_nest_end(skb, nest);
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	return true;
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ciout_err:
185862306a36Sopenharmony_ci	nla_nest_cancel(skb, nest);
185962306a36Sopenharmony_ci	return false;
186062306a36Sopenharmony_ci}
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_cistatic size_t rtnl_vlan_nlmsg_size(void)
186362306a36Sopenharmony_ci{
186462306a36Sopenharmony_ci	return NLMSG_ALIGN(sizeof(struct br_vlan_msg))
186562306a36Sopenharmony_ci		+ nla_total_size(0) /* BRIDGE_VLANDB_ENTRY */
186662306a36Sopenharmony_ci		+ nla_total_size(sizeof(u16)) /* BRIDGE_VLANDB_ENTRY_RANGE */
186762306a36Sopenharmony_ci		+ nla_total_size(sizeof(struct bridge_vlan_info)) /* BRIDGE_VLANDB_ENTRY_INFO */
186862306a36Sopenharmony_ci		+ br_vlan_opts_nl_size(); /* bridge vlan options */
186962306a36Sopenharmony_ci}
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_civoid br_vlan_notify(const struct net_bridge *br,
187262306a36Sopenharmony_ci		    const struct net_bridge_port *p,
187362306a36Sopenharmony_ci		    u16 vid, u16 vid_range,
187462306a36Sopenharmony_ci		    int cmd)
187562306a36Sopenharmony_ci{
187662306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
187762306a36Sopenharmony_ci	struct net_bridge_vlan *v = NULL;
187862306a36Sopenharmony_ci	struct br_vlan_msg *bvm;
187962306a36Sopenharmony_ci	struct nlmsghdr *nlh;
188062306a36Sopenharmony_ci	struct sk_buff *skb;
188162306a36Sopenharmony_ci	int err = -ENOBUFS;
188262306a36Sopenharmony_ci	struct net *net;
188362306a36Sopenharmony_ci	u16 flags = 0;
188462306a36Sopenharmony_ci	int ifindex;
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	/* right now notifications are done only with rtnl held */
188762306a36Sopenharmony_ci	ASSERT_RTNL();
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci	if (p) {
189062306a36Sopenharmony_ci		ifindex = p->dev->ifindex;
189162306a36Sopenharmony_ci		vg = nbp_vlan_group(p);
189262306a36Sopenharmony_ci		net = dev_net(p->dev);
189362306a36Sopenharmony_ci	} else {
189462306a36Sopenharmony_ci		ifindex = br->dev->ifindex;
189562306a36Sopenharmony_ci		vg = br_vlan_group(br);
189662306a36Sopenharmony_ci		net = dev_net(br->dev);
189762306a36Sopenharmony_ci	}
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci	skb = nlmsg_new(rtnl_vlan_nlmsg_size(), GFP_KERNEL);
190062306a36Sopenharmony_ci	if (!skb)
190162306a36Sopenharmony_ci		goto out_err;
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci	err = -EMSGSIZE;
190462306a36Sopenharmony_ci	nlh = nlmsg_put(skb, 0, 0, cmd, sizeof(*bvm), 0);
190562306a36Sopenharmony_ci	if (!nlh)
190662306a36Sopenharmony_ci		goto out_err;
190762306a36Sopenharmony_ci	bvm = nlmsg_data(nlh);
190862306a36Sopenharmony_ci	memset(bvm, 0, sizeof(*bvm));
190962306a36Sopenharmony_ci	bvm->family = AF_BRIDGE;
191062306a36Sopenharmony_ci	bvm->ifindex = ifindex;
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	switch (cmd) {
191362306a36Sopenharmony_ci	case RTM_NEWVLAN:
191462306a36Sopenharmony_ci		/* need to find the vlan due to flags/options */
191562306a36Sopenharmony_ci		v = br_vlan_find(vg, vid);
191662306a36Sopenharmony_ci		if (!v || !br_vlan_should_use(v))
191762306a36Sopenharmony_ci			goto out_kfree;
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci		flags = v->flags;
192062306a36Sopenharmony_ci		if (br_get_pvid(vg) == v->vid)
192162306a36Sopenharmony_ci			flags |= BRIDGE_VLAN_INFO_PVID;
192262306a36Sopenharmony_ci		break;
192362306a36Sopenharmony_ci	case RTM_DELVLAN:
192462306a36Sopenharmony_ci		break;
192562306a36Sopenharmony_ci	default:
192662306a36Sopenharmony_ci		goto out_kfree;
192762306a36Sopenharmony_ci	}
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_ci	if (!br_vlan_fill_vids(skb, vid, vid_range, v, p, flags, false))
193062306a36Sopenharmony_ci		goto out_err;
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
193362306a36Sopenharmony_ci	rtnl_notify(skb, net, 0, RTNLGRP_BRVLAN, NULL, GFP_KERNEL);
193462306a36Sopenharmony_ci	return;
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ciout_err:
193762306a36Sopenharmony_ci	rtnl_set_sk_err(net, RTNLGRP_BRVLAN, err);
193862306a36Sopenharmony_ciout_kfree:
193962306a36Sopenharmony_ci	kfree_skb(skb);
194062306a36Sopenharmony_ci}
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_ci/* check if v_curr can enter a range ending in range_end */
194362306a36Sopenharmony_cibool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
194462306a36Sopenharmony_ci			     const struct net_bridge_vlan *range_end)
194562306a36Sopenharmony_ci{
194662306a36Sopenharmony_ci	return v_curr->vid - range_end->vid == 1 &&
194762306a36Sopenharmony_ci	       range_end->flags == v_curr->flags &&
194862306a36Sopenharmony_ci	       br_vlan_opts_eq_range(v_curr, range_end);
194962306a36Sopenharmony_ci}
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_cistatic int br_vlan_dump_dev(const struct net_device *dev,
195262306a36Sopenharmony_ci			    struct sk_buff *skb,
195362306a36Sopenharmony_ci			    struct netlink_callback *cb,
195462306a36Sopenharmony_ci			    u32 dump_flags)
195562306a36Sopenharmony_ci{
195662306a36Sopenharmony_ci	struct net_bridge_vlan *v, *range_start = NULL, *range_end = NULL;
195762306a36Sopenharmony_ci	bool dump_global = !!(dump_flags & BRIDGE_VLANDB_DUMPF_GLOBAL);
195862306a36Sopenharmony_ci	bool dump_stats = !!(dump_flags & BRIDGE_VLANDB_DUMPF_STATS);
195962306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
196062306a36Sopenharmony_ci	int idx = 0, s_idx = cb->args[1];
196162306a36Sopenharmony_ci	struct nlmsghdr *nlh = NULL;
196262306a36Sopenharmony_ci	struct net_bridge_port *p;
196362306a36Sopenharmony_ci	struct br_vlan_msg *bvm;
196462306a36Sopenharmony_ci	struct net_bridge *br;
196562306a36Sopenharmony_ci	int err = 0;
196662306a36Sopenharmony_ci	u16 pvid;
196762306a36Sopenharmony_ci
196862306a36Sopenharmony_ci	if (!netif_is_bridge_master(dev) && !netif_is_bridge_port(dev))
196962306a36Sopenharmony_ci		return -EINVAL;
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	if (netif_is_bridge_master(dev)) {
197262306a36Sopenharmony_ci		br = netdev_priv(dev);
197362306a36Sopenharmony_ci		vg = br_vlan_group_rcu(br);
197462306a36Sopenharmony_ci		p = NULL;
197562306a36Sopenharmony_ci	} else {
197662306a36Sopenharmony_ci		/* global options are dumped only for bridge devices */
197762306a36Sopenharmony_ci		if (dump_global)
197862306a36Sopenharmony_ci			return 0;
197962306a36Sopenharmony_ci
198062306a36Sopenharmony_ci		p = br_port_get_rcu(dev);
198162306a36Sopenharmony_ci		if (WARN_ON(!p))
198262306a36Sopenharmony_ci			return -EINVAL;
198362306a36Sopenharmony_ci		vg = nbp_vlan_group_rcu(p);
198462306a36Sopenharmony_ci		br = p->br;
198562306a36Sopenharmony_ci	}
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci	if (!vg)
198862306a36Sopenharmony_ci		return 0;
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_ci	nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
199162306a36Sopenharmony_ci			RTM_NEWVLAN, sizeof(*bvm), NLM_F_MULTI);
199262306a36Sopenharmony_ci	if (!nlh)
199362306a36Sopenharmony_ci		return -EMSGSIZE;
199462306a36Sopenharmony_ci	bvm = nlmsg_data(nlh);
199562306a36Sopenharmony_ci	memset(bvm, 0, sizeof(*bvm));
199662306a36Sopenharmony_ci	bvm->family = PF_BRIDGE;
199762306a36Sopenharmony_ci	bvm->ifindex = dev->ifindex;
199862306a36Sopenharmony_ci	pvid = br_get_pvid(vg);
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_ci	/* idx must stay at range's beginning until it is filled in */
200162306a36Sopenharmony_ci	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
200262306a36Sopenharmony_ci		if (!dump_global && !br_vlan_should_use(v))
200362306a36Sopenharmony_ci			continue;
200462306a36Sopenharmony_ci		if (idx < s_idx) {
200562306a36Sopenharmony_ci			idx++;
200662306a36Sopenharmony_ci			continue;
200762306a36Sopenharmony_ci		}
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ci		if (!range_start) {
201062306a36Sopenharmony_ci			range_start = v;
201162306a36Sopenharmony_ci			range_end = v;
201262306a36Sopenharmony_ci			continue;
201362306a36Sopenharmony_ci		}
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_ci		if (dump_global) {
201662306a36Sopenharmony_ci			if (br_vlan_global_opts_can_enter_range(v, range_end))
201762306a36Sopenharmony_ci				goto update_end;
201862306a36Sopenharmony_ci			if (!br_vlan_global_opts_fill(skb, range_start->vid,
201962306a36Sopenharmony_ci						      range_end->vid,
202062306a36Sopenharmony_ci						      range_start)) {
202162306a36Sopenharmony_ci				err = -EMSGSIZE;
202262306a36Sopenharmony_ci				break;
202362306a36Sopenharmony_ci			}
202462306a36Sopenharmony_ci			/* advance number of filled vlans */
202562306a36Sopenharmony_ci			idx += range_end->vid - range_start->vid + 1;
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci			range_start = v;
202862306a36Sopenharmony_ci		} else if (dump_stats || v->vid == pvid ||
202962306a36Sopenharmony_ci			   !br_vlan_can_enter_range(v, range_end)) {
203062306a36Sopenharmony_ci			u16 vlan_flags = br_vlan_flags(range_start, pvid);
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci			if (!br_vlan_fill_vids(skb, range_start->vid,
203362306a36Sopenharmony_ci					       range_end->vid, range_start,
203462306a36Sopenharmony_ci					       p, vlan_flags, dump_stats)) {
203562306a36Sopenharmony_ci				err = -EMSGSIZE;
203662306a36Sopenharmony_ci				break;
203762306a36Sopenharmony_ci			}
203862306a36Sopenharmony_ci			/* advance number of filled vlans */
203962306a36Sopenharmony_ci			idx += range_end->vid - range_start->vid + 1;
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci			range_start = v;
204262306a36Sopenharmony_ci		}
204362306a36Sopenharmony_ciupdate_end:
204462306a36Sopenharmony_ci		range_end = v;
204562306a36Sopenharmony_ci	}
204662306a36Sopenharmony_ci
204762306a36Sopenharmony_ci	/* err will be 0 and range_start will be set in 3 cases here:
204862306a36Sopenharmony_ci	 * - first vlan (range_start == range_end)
204962306a36Sopenharmony_ci	 * - last vlan (range_start == range_end, not in range)
205062306a36Sopenharmony_ci	 * - last vlan range (range_start != range_end, in range)
205162306a36Sopenharmony_ci	 */
205262306a36Sopenharmony_ci	if (!err && range_start) {
205362306a36Sopenharmony_ci		if (dump_global &&
205462306a36Sopenharmony_ci		    !br_vlan_global_opts_fill(skb, range_start->vid,
205562306a36Sopenharmony_ci					      range_end->vid, range_start))
205662306a36Sopenharmony_ci			err = -EMSGSIZE;
205762306a36Sopenharmony_ci		else if (!dump_global &&
205862306a36Sopenharmony_ci			 !br_vlan_fill_vids(skb, range_start->vid,
205962306a36Sopenharmony_ci					    range_end->vid, range_start,
206062306a36Sopenharmony_ci					    p, br_vlan_flags(range_start, pvid),
206162306a36Sopenharmony_ci					    dump_stats))
206262306a36Sopenharmony_ci			err = -EMSGSIZE;
206362306a36Sopenharmony_ci	}
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci	cb->args[1] = err ? idx : 0;
206662306a36Sopenharmony_ci
206762306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	return err;
207062306a36Sopenharmony_ci}
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_cistatic const struct nla_policy br_vlan_db_dump_pol[BRIDGE_VLANDB_DUMP_MAX + 1] = {
207362306a36Sopenharmony_ci	[BRIDGE_VLANDB_DUMP_FLAGS] = { .type = NLA_U32 },
207462306a36Sopenharmony_ci};
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_cistatic int br_vlan_rtm_dump(struct sk_buff *skb, struct netlink_callback *cb)
207762306a36Sopenharmony_ci{
207862306a36Sopenharmony_ci	struct nlattr *dtb[BRIDGE_VLANDB_DUMP_MAX + 1];
207962306a36Sopenharmony_ci	int idx = 0, err = 0, s_idx = cb->args[0];
208062306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
208162306a36Sopenharmony_ci	struct br_vlan_msg *bvm;
208262306a36Sopenharmony_ci	struct net_device *dev;
208362306a36Sopenharmony_ci	u32 dump_flags = 0;
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci	err = nlmsg_parse(cb->nlh, sizeof(*bvm), dtb, BRIDGE_VLANDB_DUMP_MAX,
208662306a36Sopenharmony_ci			  br_vlan_db_dump_pol, cb->extack);
208762306a36Sopenharmony_ci	if (err < 0)
208862306a36Sopenharmony_ci		return err;
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci	bvm = nlmsg_data(cb->nlh);
209162306a36Sopenharmony_ci	if (dtb[BRIDGE_VLANDB_DUMP_FLAGS])
209262306a36Sopenharmony_ci		dump_flags = nla_get_u32(dtb[BRIDGE_VLANDB_DUMP_FLAGS]);
209362306a36Sopenharmony_ci
209462306a36Sopenharmony_ci	rcu_read_lock();
209562306a36Sopenharmony_ci	if (bvm->ifindex) {
209662306a36Sopenharmony_ci		dev = dev_get_by_index_rcu(net, bvm->ifindex);
209762306a36Sopenharmony_ci		if (!dev) {
209862306a36Sopenharmony_ci			err = -ENODEV;
209962306a36Sopenharmony_ci			goto out_err;
210062306a36Sopenharmony_ci		}
210162306a36Sopenharmony_ci		err = br_vlan_dump_dev(dev, skb, cb, dump_flags);
210262306a36Sopenharmony_ci		/* if the dump completed without an error we return 0 here */
210362306a36Sopenharmony_ci		if (err != -EMSGSIZE)
210462306a36Sopenharmony_ci			goto out_err;
210562306a36Sopenharmony_ci	} else {
210662306a36Sopenharmony_ci		for_each_netdev_rcu(net, dev) {
210762306a36Sopenharmony_ci			if (idx < s_idx)
210862306a36Sopenharmony_ci				goto skip;
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci			err = br_vlan_dump_dev(dev, skb, cb, dump_flags);
211162306a36Sopenharmony_ci			if (err == -EMSGSIZE)
211262306a36Sopenharmony_ci				break;
211362306a36Sopenharmony_ciskip:
211462306a36Sopenharmony_ci			idx++;
211562306a36Sopenharmony_ci		}
211662306a36Sopenharmony_ci	}
211762306a36Sopenharmony_ci	cb->args[0] = idx;
211862306a36Sopenharmony_ci	rcu_read_unlock();
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_ci	return skb->len;
212162306a36Sopenharmony_ci
212262306a36Sopenharmony_ciout_err:
212362306a36Sopenharmony_ci	rcu_read_unlock();
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	return err;
212662306a36Sopenharmony_ci}
212762306a36Sopenharmony_ci
212862306a36Sopenharmony_cistatic const struct nla_policy br_vlan_db_policy[BRIDGE_VLANDB_ENTRY_MAX + 1] = {
212962306a36Sopenharmony_ci	[BRIDGE_VLANDB_ENTRY_INFO]	=
213062306a36Sopenharmony_ci		NLA_POLICY_EXACT_LEN(sizeof(struct bridge_vlan_info)),
213162306a36Sopenharmony_ci	[BRIDGE_VLANDB_ENTRY_RANGE]	= { .type = NLA_U16 },
213262306a36Sopenharmony_ci	[BRIDGE_VLANDB_ENTRY_STATE]	= { .type = NLA_U8 },
213362306a36Sopenharmony_ci	[BRIDGE_VLANDB_ENTRY_TUNNEL_INFO] = { .type = NLA_NESTED },
213462306a36Sopenharmony_ci	[BRIDGE_VLANDB_ENTRY_MCAST_ROUTER]	= { .type = NLA_U8 },
213562306a36Sopenharmony_ci	[BRIDGE_VLANDB_ENTRY_MCAST_N_GROUPS]	= { .type = NLA_REJECT },
213662306a36Sopenharmony_ci	[BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS]	= { .type = NLA_U32 },
213762306a36Sopenharmony_ci	[BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS]	= NLA_POLICY_MAX(NLA_U8, 1),
213862306a36Sopenharmony_ci};
213962306a36Sopenharmony_ci
214062306a36Sopenharmony_cistatic int br_vlan_rtm_process_one(struct net_device *dev,
214162306a36Sopenharmony_ci				   const struct nlattr *attr,
214262306a36Sopenharmony_ci				   int cmd, struct netlink_ext_ack *extack)
214362306a36Sopenharmony_ci{
214462306a36Sopenharmony_ci	struct bridge_vlan_info *vinfo, vrange_end, *vinfo_last = NULL;
214562306a36Sopenharmony_ci	struct nlattr *tb[BRIDGE_VLANDB_ENTRY_MAX + 1];
214662306a36Sopenharmony_ci	bool changed = false, skip_processing = false;
214762306a36Sopenharmony_ci	struct net_bridge_vlan_group *vg;
214862306a36Sopenharmony_ci	struct net_bridge_port *p = NULL;
214962306a36Sopenharmony_ci	int err = 0, cmdmap = 0;
215062306a36Sopenharmony_ci	struct net_bridge *br;
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci	if (netif_is_bridge_master(dev)) {
215362306a36Sopenharmony_ci		br = netdev_priv(dev);
215462306a36Sopenharmony_ci		vg = br_vlan_group(br);
215562306a36Sopenharmony_ci	} else {
215662306a36Sopenharmony_ci		p = br_port_get_rtnl(dev);
215762306a36Sopenharmony_ci		if (WARN_ON(!p))
215862306a36Sopenharmony_ci			return -ENODEV;
215962306a36Sopenharmony_ci		br = p->br;
216062306a36Sopenharmony_ci		vg = nbp_vlan_group(p);
216162306a36Sopenharmony_ci	}
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_ci	if (WARN_ON(!vg))
216462306a36Sopenharmony_ci		return -ENODEV;
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_ci	err = nla_parse_nested(tb, BRIDGE_VLANDB_ENTRY_MAX, attr,
216762306a36Sopenharmony_ci			       br_vlan_db_policy, extack);
216862306a36Sopenharmony_ci	if (err)
216962306a36Sopenharmony_ci		return err;
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci	if (!tb[BRIDGE_VLANDB_ENTRY_INFO]) {
217262306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Missing vlan entry info");
217362306a36Sopenharmony_ci		return -EINVAL;
217462306a36Sopenharmony_ci	}
217562306a36Sopenharmony_ci	memset(&vrange_end, 0, sizeof(vrange_end));
217662306a36Sopenharmony_ci
217762306a36Sopenharmony_ci	vinfo = nla_data(tb[BRIDGE_VLANDB_ENTRY_INFO]);
217862306a36Sopenharmony_ci	if (vinfo->flags & (BRIDGE_VLAN_INFO_RANGE_BEGIN |
217962306a36Sopenharmony_ci			    BRIDGE_VLAN_INFO_RANGE_END)) {
218062306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Old-style vlan ranges are not allowed when using RTM vlan calls");
218162306a36Sopenharmony_ci		return -EINVAL;
218262306a36Sopenharmony_ci	}
218362306a36Sopenharmony_ci	if (!br_vlan_valid_id(vinfo->vid, extack))
218462306a36Sopenharmony_ci		return -EINVAL;
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci	if (tb[BRIDGE_VLANDB_ENTRY_RANGE]) {
218762306a36Sopenharmony_ci		vrange_end.vid = nla_get_u16(tb[BRIDGE_VLANDB_ENTRY_RANGE]);
218862306a36Sopenharmony_ci		/* validate user-provided flags without RANGE_BEGIN */
218962306a36Sopenharmony_ci		vrange_end.flags = BRIDGE_VLAN_INFO_RANGE_END | vinfo->flags;
219062306a36Sopenharmony_ci		vinfo->flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN;
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_ci		/* vinfo_last is the range start, vinfo the range end */
219362306a36Sopenharmony_ci		vinfo_last = vinfo;
219462306a36Sopenharmony_ci		vinfo = &vrange_end;
219562306a36Sopenharmony_ci
219662306a36Sopenharmony_ci		if (!br_vlan_valid_id(vinfo->vid, extack) ||
219762306a36Sopenharmony_ci		    !br_vlan_valid_range(vinfo, vinfo_last, extack))
219862306a36Sopenharmony_ci			return -EINVAL;
219962306a36Sopenharmony_ci	}
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci	switch (cmd) {
220262306a36Sopenharmony_ci	case RTM_NEWVLAN:
220362306a36Sopenharmony_ci		cmdmap = RTM_SETLINK;
220462306a36Sopenharmony_ci		skip_processing = !!(vinfo->flags & BRIDGE_VLAN_INFO_ONLY_OPTS);
220562306a36Sopenharmony_ci		break;
220662306a36Sopenharmony_ci	case RTM_DELVLAN:
220762306a36Sopenharmony_ci		cmdmap = RTM_DELLINK;
220862306a36Sopenharmony_ci		break;
220962306a36Sopenharmony_ci	}
221062306a36Sopenharmony_ci
221162306a36Sopenharmony_ci	if (!skip_processing) {
221262306a36Sopenharmony_ci		struct bridge_vlan_info *tmp_last = vinfo_last;
221362306a36Sopenharmony_ci
221462306a36Sopenharmony_ci		/* br_process_vlan_info may overwrite vinfo_last */
221562306a36Sopenharmony_ci		err = br_process_vlan_info(br, p, cmdmap, vinfo, &tmp_last,
221662306a36Sopenharmony_ci					   &changed, extack);
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_ci		/* notify first if anything changed */
221962306a36Sopenharmony_ci		if (changed)
222062306a36Sopenharmony_ci			br_ifinfo_notify(cmdmap, br, p);
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci		if (err)
222362306a36Sopenharmony_ci			return err;
222462306a36Sopenharmony_ci	}
222562306a36Sopenharmony_ci
222662306a36Sopenharmony_ci	/* deal with options */
222762306a36Sopenharmony_ci	if (cmd == RTM_NEWVLAN) {
222862306a36Sopenharmony_ci		struct net_bridge_vlan *range_start, *range_end;
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_ci		if (vinfo_last) {
223162306a36Sopenharmony_ci			range_start = br_vlan_find(vg, vinfo_last->vid);
223262306a36Sopenharmony_ci			range_end = br_vlan_find(vg, vinfo->vid);
223362306a36Sopenharmony_ci		} else {
223462306a36Sopenharmony_ci			range_start = br_vlan_find(vg, vinfo->vid);
223562306a36Sopenharmony_ci			range_end = range_start;
223662306a36Sopenharmony_ci		}
223762306a36Sopenharmony_ci
223862306a36Sopenharmony_ci		err = br_vlan_process_options(br, p, range_start, range_end,
223962306a36Sopenharmony_ci					      tb, extack);
224062306a36Sopenharmony_ci	}
224162306a36Sopenharmony_ci
224262306a36Sopenharmony_ci	return err;
224362306a36Sopenharmony_ci}
224462306a36Sopenharmony_ci
224562306a36Sopenharmony_cistatic int br_vlan_rtm_process(struct sk_buff *skb, struct nlmsghdr *nlh,
224662306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
224762306a36Sopenharmony_ci{
224862306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
224962306a36Sopenharmony_ci	struct br_vlan_msg *bvm;
225062306a36Sopenharmony_ci	struct net_device *dev;
225162306a36Sopenharmony_ci	struct nlattr *attr;
225262306a36Sopenharmony_ci	int err, vlans = 0;
225362306a36Sopenharmony_ci	int rem;
225462306a36Sopenharmony_ci
225562306a36Sopenharmony_ci	/* this should validate the header and check for remaining bytes */
225662306a36Sopenharmony_ci	err = nlmsg_parse(nlh, sizeof(*bvm), NULL, BRIDGE_VLANDB_MAX, NULL,
225762306a36Sopenharmony_ci			  extack);
225862306a36Sopenharmony_ci	if (err < 0)
225962306a36Sopenharmony_ci		return err;
226062306a36Sopenharmony_ci
226162306a36Sopenharmony_ci	bvm = nlmsg_data(nlh);
226262306a36Sopenharmony_ci	dev = __dev_get_by_index(net, bvm->ifindex);
226362306a36Sopenharmony_ci	if (!dev)
226462306a36Sopenharmony_ci		return -ENODEV;
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	if (!netif_is_bridge_master(dev) && !netif_is_bridge_port(dev)) {
226762306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "The device is not a valid bridge or bridge port");
226862306a36Sopenharmony_ci		return -EINVAL;
226962306a36Sopenharmony_ci	}
227062306a36Sopenharmony_ci
227162306a36Sopenharmony_ci	nlmsg_for_each_attr(attr, nlh, sizeof(*bvm), rem) {
227262306a36Sopenharmony_ci		switch (nla_type(attr)) {
227362306a36Sopenharmony_ci		case BRIDGE_VLANDB_ENTRY:
227462306a36Sopenharmony_ci			err = br_vlan_rtm_process_one(dev, attr,
227562306a36Sopenharmony_ci						      nlh->nlmsg_type,
227662306a36Sopenharmony_ci						      extack);
227762306a36Sopenharmony_ci			break;
227862306a36Sopenharmony_ci		case BRIDGE_VLANDB_GLOBAL_OPTIONS:
227962306a36Sopenharmony_ci			err = br_vlan_rtm_process_global_options(dev, attr,
228062306a36Sopenharmony_ci								 nlh->nlmsg_type,
228162306a36Sopenharmony_ci								 extack);
228262306a36Sopenharmony_ci			break;
228362306a36Sopenharmony_ci		default:
228462306a36Sopenharmony_ci			continue;
228562306a36Sopenharmony_ci		}
228662306a36Sopenharmony_ci
228762306a36Sopenharmony_ci		vlans++;
228862306a36Sopenharmony_ci		if (err)
228962306a36Sopenharmony_ci			break;
229062306a36Sopenharmony_ci	}
229162306a36Sopenharmony_ci	if (!vlans) {
229262306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "No vlans found to process");
229362306a36Sopenharmony_ci		err = -EINVAL;
229462306a36Sopenharmony_ci	}
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci	return err;
229762306a36Sopenharmony_ci}
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_civoid br_vlan_rtnl_init(void)
230062306a36Sopenharmony_ci{
230162306a36Sopenharmony_ci	rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_GETVLAN, NULL,
230262306a36Sopenharmony_ci			     br_vlan_rtm_dump, 0);
230362306a36Sopenharmony_ci	rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_NEWVLAN,
230462306a36Sopenharmony_ci			     br_vlan_rtm_process, NULL, 0);
230562306a36Sopenharmony_ci	rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_DELVLAN,
230662306a36Sopenharmony_ci			     br_vlan_rtm_process, NULL, 0);
230762306a36Sopenharmony_ci}
230862306a36Sopenharmony_ci
230962306a36Sopenharmony_civoid br_vlan_rtnl_uninit(void)
231062306a36Sopenharmony_ci{
231162306a36Sopenharmony_ci	rtnl_unregister(PF_BRIDGE, RTM_GETVLAN);
231262306a36Sopenharmony_ci	rtnl_unregister(PF_BRIDGE, RTM_NEWVLAN);
231362306a36Sopenharmony_ci	rtnl_unregister(PF_BRIDGE, RTM_DELVLAN);
231462306a36Sopenharmony_ci}
2315