18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci#include <linux/kernel.h>
38c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
48c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
58c2ecf20Sopenharmony_ci#include <linux/slab.h>
68c2ecf20Sopenharmony_ci#include <net/switchdev.h>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "br_private.h"
98c2ecf20Sopenharmony_ci#include "br_private_tunnel.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_cistatic void nbp_vlan_set_vlan_dev_state(struct net_bridge_port *p, u16 vid);
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistatic inline int br_vlan_cmp(struct rhashtable_compare_arg *arg,
148c2ecf20Sopenharmony_ci			      const void *ptr)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	const struct net_bridge_vlan *vle = ptr;
178c2ecf20Sopenharmony_ci	u16 vid = *(u16 *)arg->key;
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci	return vle->vid != vid;
208c2ecf20Sopenharmony_ci}
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic const struct rhashtable_params br_vlan_rht_params = {
238c2ecf20Sopenharmony_ci	.head_offset = offsetof(struct net_bridge_vlan, vnode),
248c2ecf20Sopenharmony_ci	.key_offset = offsetof(struct net_bridge_vlan, vid),
258c2ecf20Sopenharmony_ci	.key_len = sizeof(u16),
268c2ecf20Sopenharmony_ci	.nelem_hint = 3,
278c2ecf20Sopenharmony_ci	.max_size = VLAN_N_VID,
288c2ecf20Sopenharmony_ci	.obj_cmpfn = br_vlan_cmp,
298c2ecf20Sopenharmony_ci	.automatic_shrinking = true,
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic struct net_bridge_vlan *br_vlan_lookup(struct rhashtable *tbl, u16 vid)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	return rhashtable_lookup_fast(tbl, &vid, br_vlan_rht_params);
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic bool __vlan_add_pvid(struct net_bridge_vlan_group *vg,
388c2ecf20Sopenharmony_ci			    const struct net_bridge_vlan *v)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	if (vg->pvid == v->vid)
418c2ecf20Sopenharmony_ci		return false;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	smp_wmb();
448c2ecf20Sopenharmony_ci	br_vlan_set_pvid_state(vg, v->state);
458c2ecf20Sopenharmony_ci	vg->pvid = v->vid;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	return true;
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic bool __vlan_delete_pvid(struct net_bridge_vlan_group *vg, u16 vid)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	if (vg->pvid != vid)
538c2ecf20Sopenharmony_ci		return false;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	smp_wmb();
568c2ecf20Sopenharmony_ci	vg->pvid = 0;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	return true;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/* return true if anything changed, false otherwise */
628c2ecf20Sopenharmony_cistatic bool __vlan_add_flags(struct net_bridge_vlan *v, u16 flags)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
658c2ecf20Sopenharmony_ci	u16 old_flags = v->flags;
668c2ecf20Sopenharmony_ci	bool ret;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	if (br_vlan_is_master(v))
698c2ecf20Sopenharmony_ci		vg = br_vlan_group(v->br);
708c2ecf20Sopenharmony_ci	else
718c2ecf20Sopenharmony_ci		vg = nbp_vlan_group(v->port);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if (flags & BRIDGE_VLAN_INFO_PVID)
748c2ecf20Sopenharmony_ci		ret = __vlan_add_pvid(vg, v);
758c2ecf20Sopenharmony_ci	else
768c2ecf20Sopenharmony_ci		ret = __vlan_delete_pvid(vg, v->vid);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
798c2ecf20Sopenharmony_ci		v->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
808c2ecf20Sopenharmony_ci	else
818c2ecf20Sopenharmony_ci		v->flags &= ~BRIDGE_VLAN_INFO_UNTAGGED;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	return ret || !!(old_flags ^ v->flags);
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic int __vlan_vid_add(struct net_device *dev, struct net_bridge *br,
878c2ecf20Sopenharmony_ci			  struct net_bridge_vlan *v, u16 flags,
888c2ecf20Sopenharmony_ci			  struct netlink_ext_ack *extack)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	int err;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	/* Try switchdev op first. In case it is not supported, fallback to
938c2ecf20Sopenharmony_ci	 * 8021q add.
948c2ecf20Sopenharmony_ci	 */
958c2ecf20Sopenharmony_ci	err = br_switchdev_port_vlan_add(dev, v->vid, flags, extack);
968c2ecf20Sopenharmony_ci	if (err == -EOPNOTSUPP)
978c2ecf20Sopenharmony_ci		return vlan_vid_add(dev, br->vlan_proto, v->vid);
988c2ecf20Sopenharmony_ci	v->priv_flags |= BR_VLFLAG_ADDED_BY_SWITCHDEV;
998c2ecf20Sopenharmony_ci	return err;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic void __vlan_add_list(struct net_bridge_vlan *v)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
1058c2ecf20Sopenharmony_ci	struct list_head *headp, *hpos;
1068c2ecf20Sopenharmony_ci	struct net_bridge_vlan *vent;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (br_vlan_is_master(v))
1098c2ecf20Sopenharmony_ci		vg = br_vlan_group(v->br);
1108c2ecf20Sopenharmony_ci	else
1118c2ecf20Sopenharmony_ci		vg = nbp_vlan_group(v->port);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	headp = &vg->vlan_list;
1148c2ecf20Sopenharmony_ci	list_for_each_prev(hpos, headp) {
1158c2ecf20Sopenharmony_ci		vent = list_entry(hpos, struct net_bridge_vlan, vlist);
1168c2ecf20Sopenharmony_ci		if (v->vid < vent->vid)
1178c2ecf20Sopenharmony_ci			continue;
1188c2ecf20Sopenharmony_ci		else
1198c2ecf20Sopenharmony_ci			break;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci	list_add_rcu(&v->vlist, hpos);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic void __vlan_del_list(struct net_bridge_vlan *v)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	list_del_rcu(&v->vlist);
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic int __vlan_vid_del(struct net_device *dev, struct net_bridge *br,
1308c2ecf20Sopenharmony_ci			  const struct net_bridge_vlan *v)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	int err;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	/* Try switchdev op first. In case it is not supported, fallback to
1358c2ecf20Sopenharmony_ci	 * 8021q del.
1368c2ecf20Sopenharmony_ci	 */
1378c2ecf20Sopenharmony_ci	err = br_switchdev_port_vlan_del(dev, v->vid);
1388c2ecf20Sopenharmony_ci	if (!(v->priv_flags & BR_VLFLAG_ADDED_BY_SWITCHDEV))
1398c2ecf20Sopenharmony_ci		vlan_vid_del(dev, br->vlan_proto, v->vid);
1408c2ecf20Sopenharmony_ci	return err == -EOPNOTSUPP ? 0 : err;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci/* Returns a master vlan, if it didn't exist it gets created. In all cases
1448c2ecf20Sopenharmony_ci * a reference is taken to the master vlan before returning.
1458c2ecf20Sopenharmony_ci */
1468c2ecf20Sopenharmony_cistatic struct net_bridge_vlan *
1478c2ecf20Sopenharmony_cibr_vlan_get_master(struct net_bridge *br, u16 vid,
1488c2ecf20Sopenharmony_ci		   struct netlink_ext_ack *extack)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
1518c2ecf20Sopenharmony_ci	struct net_bridge_vlan *masterv;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	vg = br_vlan_group(br);
1548c2ecf20Sopenharmony_ci	masterv = br_vlan_find(vg, vid);
1558c2ecf20Sopenharmony_ci	if (!masterv) {
1568c2ecf20Sopenharmony_ci		bool changed;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		/* missing global ctx, create it now */
1598c2ecf20Sopenharmony_ci		if (br_vlan_add(br, vid, 0, &changed, extack))
1608c2ecf20Sopenharmony_ci			return NULL;
1618c2ecf20Sopenharmony_ci		masterv = br_vlan_find(vg, vid);
1628c2ecf20Sopenharmony_ci		if (WARN_ON(!masterv))
1638c2ecf20Sopenharmony_ci			return NULL;
1648c2ecf20Sopenharmony_ci		refcount_set(&masterv->refcnt, 1);
1658c2ecf20Sopenharmony_ci		return masterv;
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci	refcount_inc(&masterv->refcnt);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	return masterv;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic void br_master_vlan_rcu_free(struct rcu_head *rcu)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	struct net_bridge_vlan *v;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	v = container_of(rcu, struct net_bridge_vlan, rcu);
1778c2ecf20Sopenharmony_ci	WARN_ON(!br_vlan_is_master(v));
1788c2ecf20Sopenharmony_ci	free_percpu(v->stats);
1798c2ecf20Sopenharmony_ci	v->stats = NULL;
1808c2ecf20Sopenharmony_ci	kfree(v);
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic void br_vlan_put_master(struct net_bridge_vlan *masterv)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (!br_vlan_is_master(masterv))
1888c2ecf20Sopenharmony_ci		return;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	vg = br_vlan_group(masterv->br);
1918c2ecf20Sopenharmony_ci	if (refcount_dec_and_test(&masterv->refcnt)) {
1928c2ecf20Sopenharmony_ci		rhashtable_remove_fast(&vg->vlan_hash,
1938c2ecf20Sopenharmony_ci				       &masterv->vnode, br_vlan_rht_params);
1948c2ecf20Sopenharmony_ci		__vlan_del_list(masterv);
1958c2ecf20Sopenharmony_ci		call_rcu(&masterv->rcu, br_master_vlan_rcu_free);
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic void nbp_vlan_rcu_free(struct rcu_head *rcu)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct net_bridge_vlan *v;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	v = container_of(rcu, struct net_bridge_vlan, rcu);
2048c2ecf20Sopenharmony_ci	WARN_ON(br_vlan_is_master(v));
2058c2ecf20Sopenharmony_ci	/* if we had per-port stats configured then free them here */
2068c2ecf20Sopenharmony_ci	if (v->priv_flags & BR_VLFLAG_PER_PORT_STATS)
2078c2ecf20Sopenharmony_ci		free_percpu(v->stats);
2088c2ecf20Sopenharmony_ci	v->stats = NULL;
2098c2ecf20Sopenharmony_ci	kfree(v);
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci/* This is the shared VLAN add function which works for both ports and bridge
2138c2ecf20Sopenharmony_ci * devices. There are four possible calls to this function in terms of the
2148c2ecf20Sopenharmony_ci * vlan entry type:
2158c2ecf20Sopenharmony_ci * 1. vlan is being added on a port (no master flags, global entry exists)
2168c2ecf20Sopenharmony_ci * 2. vlan is being added on a bridge (both master and brentry flags)
2178c2ecf20Sopenharmony_ci * 3. vlan is being added on a port, but a global entry didn't exist which
2188c2ecf20Sopenharmony_ci *    is being created right now (master flag set, brentry flag unset), the
2198c2ecf20Sopenharmony_ci *    global entry is used for global per-vlan features, but not for filtering
2208c2ecf20Sopenharmony_ci * 4. same as 3 but with both master and brentry flags set so the entry
2218c2ecf20Sopenharmony_ci *    will be used for filtering in both the port and the bridge
2228c2ecf20Sopenharmony_ci */
2238c2ecf20Sopenharmony_cistatic int __vlan_add(struct net_bridge_vlan *v, u16 flags,
2248c2ecf20Sopenharmony_ci		      struct netlink_ext_ack *extack)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	struct net_bridge_vlan *masterv = NULL;
2278c2ecf20Sopenharmony_ci	struct net_bridge_port *p = NULL;
2288c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
2298c2ecf20Sopenharmony_ci	struct net_device *dev;
2308c2ecf20Sopenharmony_ci	struct net_bridge *br;
2318c2ecf20Sopenharmony_ci	int err;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	if (br_vlan_is_master(v)) {
2348c2ecf20Sopenharmony_ci		br = v->br;
2358c2ecf20Sopenharmony_ci		dev = br->dev;
2368c2ecf20Sopenharmony_ci		vg = br_vlan_group(br);
2378c2ecf20Sopenharmony_ci	} else {
2388c2ecf20Sopenharmony_ci		p = v->port;
2398c2ecf20Sopenharmony_ci		br = p->br;
2408c2ecf20Sopenharmony_ci		dev = p->dev;
2418c2ecf20Sopenharmony_ci		vg = nbp_vlan_group(p);
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (p) {
2458c2ecf20Sopenharmony_ci		/* Add VLAN to the device filter if it is supported.
2468c2ecf20Sopenharmony_ci		 * This ensures tagged traffic enters the bridge when
2478c2ecf20Sopenharmony_ci		 * promiscuous mode is disabled by br_manage_promisc().
2488c2ecf20Sopenharmony_ci		 */
2498c2ecf20Sopenharmony_ci		err = __vlan_vid_add(dev, br, v, flags, extack);
2508c2ecf20Sopenharmony_ci		if (err)
2518c2ecf20Sopenharmony_ci			goto out;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci		/* need to work on the master vlan too */
2548c2ecf20Sopenharmony_ci		if (flags & BRIDGE_VLAN_INFO_MASTER) {
2558c2ecf20Sopenharmony_ci			bool changed;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci			err = br_vlan_add(br, v->vid,
2588c2ecf20Sopenharmony_ci					  flags | BRIDGE_VLAN_INFO_BRENTRY,
2598c2ecf20Sopenharmony_ci					  &changed, extack);
2608c2ecf20Sopenharmony_ci			if (err)
2618c2ecf20Sopenharmony_ci				goto out_filt;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci			if (changed)
2648c2ecf20Sopenharmony_ci				br_vlan_notify(br, NULL, v->vid, 0,
2658c2ecf20Sopenharmony_ci					       RTM_NEWVLAN);
2668c2ecf20Sopenharmony_ci		}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci		masterv = br_vlan_get_master(br, v->vid, extack);
2698c2ecf20Sopenharmony_ci		if (!masterv) {
2708c2ecf20Sopenharmony_ci			err = -ENOMEM;
2718c2ecf20Sopenharmony_ci			goto out_filt;
2728c2ecf20Sopenharmony_ci		}
2738c2ecf20Sopenharmony_ci		v->brvlan = masterv;
2748c2ecf20Sopenharmony_ci		if (br_opt_get(br, BROPT_VLAN_STATS_PER_PORT)) {
2758c2ecf20Sopenharmony_ci			v->stats = netdev_alloc_pcpu_stats(struct br_vlan_stats);
2768c2ecf20Sopenharmony_ci			if (!v->stats) {
2778c2ecf20Sopenharmony_ci				err = -ENOMEM;
2788c2ecf20Sopenharmony_ci				goto out_filt;
2798c2ecf20Sopenharmony_ci			}
2808c2ecf20Sopenharmony_ci			v->priv_flags |= BR_VLFLAG_PER_PORT_STATS;
2818c2ecf20Sopenharmony_ci		} else {
2828c2ecf20Sopenharmony_ci			v->stats = masterv->stats;
2838c2ecf20Sopenharmony_ci		}
2848c2ecf20Sopenharmony_ci	} else {
2858c2ecf20Sopenharmony_ci		err = br_switchdev_port_vlan_add(dev, v->vid, flags, extack);
2868c2ecf20Sopenharmony_ci		if (err && err != -EOPNOTSUPP)
2878c2ecf20Sopenharmony_ci			goto out;
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	/* Add the dev mac and count the vlan only if it's usable */
2918c2ecf20Sopenharmony_ci	if (br_vlan_should_use(v)) {
2928c2ecf20Sopenharmony_ci		err = br_fdb_insert(br, p, dev->dev_addr, v->vid);
2938c2ecf20Sopenharmony_ci		if (err) {
2948c2ecf20Sopenharmony_ci			br_err(br, "failed insert local address into bridge forwarding table\n");
2958c2ecf20Sopenharmony_ci			goto out_filt;
2968c2ecf20Sopenharmony_ci		}
2978c2ecf20Sopenharmony_ci		vg->num_vlans++;
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	/* set the state before publishing */
3018c2ecf20Sopenharmony_ci	v->state = BR_STATE_FORWARDING;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	err = rhashtable_lookup_insert_fast(&vg->vlan_hash, &v->vnode,
3048c2ecf20Sopenharmony_ci					    br_vlan_rht_params);
3058c2ecf20Sopenharmony_ci	if (err)
3068c2ecf20Sopenharmony_ci		goto out_fdb_insert;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	__vlan_add_list(v);
3098c2ecf20Sopenharmony_ci	__vlan_add_flags(v, flags);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (p)
3128c2ecf20Sopenharmony_ci		nbp_vlan_set_vlan_dev_state(p, v->vid);
3138c2ecf20Sopenharmony_ciout:
3148c2ecf20Sopenharmony_ci	return err;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ciout_fdb_insert:
3178c2ecf20Sopenharmony_ci	if (br_vlan_should_use(v)) {
3188c2ecf20Sopenharmony_ci		br_fdb_find_delete_local(br, p, dev->dev_addr, v->vid);
3198c2ecf20Sopenharmony_ci		vg->num_vlans--;
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ciout_filt:
3238c2ecf20Sopenharmony_ci	if (p) {
3248c2ecf20Sopenharmony_ci		__vlan_vid_del(dev, br, v);
3258c2ecf20Sopenharmony_ci		if (masterv) {
3268c2ecf20Sopenharmony_ci			if (v->stats && masterv->stats != v->stats)
3278c2ecf20Sopenharmony_ci				free_percpu(v->stats);
3288c2ecf20Sopenharmony_ci			v->stats = NULL;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci			br_vlan_put_master(masterv);
3318c2ecf20Sopenharmony_ci			v->brvlan = NULL;
3328c2ecf20Sopenharmony_ci		}
3338c2ecf20Sopenharmony_ci	} else {
3348c2ecf20Sopenharmony_ci		br_switchdev_port_vlan_del(dev, v->vid);
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	goto out;
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic int __vlan_del(struct net_bridge_vlan *v)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	struct net_bridge_vlan *masterv = v;
3438c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
3448c2ecf20Sopenharmony_ci	struct net_bridge_port *p = NULL;
3458c2ecf20Sopenharmony_ci	int err = 0;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (br_vlan_is_master(v)) {
3488c2ecf20Sopenharmony_ci		vg = br_vlan_group(v->br);
3498c2ecf20Sopenharmony_ci	} else {
3508c2ecf20Sopenharmony_ci		p = v->port;
3518c2ecf20Sopenharmony_ci		vg = nbp_vlan_group(v->port);
3528c2ecf20Sopenharmony_ci		masterv = v->brvlan;
3538c2ecf20Sopenharmony_ci	}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	__vlan_delete_pvid(vg, v->vid);
3568c2ecf20Sopenharmony_ci	if (p) {
3578c2ecf20Sopenharmony_ci		err = __vlan_vid_del(p->dev, p->br, v);
3588c2ecf20Sopenharmony_ci		if (err)
3598c2ecf20Sopenharmony_ci			goto out;
3608c2ecf20Sopenharmony_ci	} else {
3618c2ecf20Sopenharmony_ci		err = br_switchdev_port_vlan_del(v->br->dev, v->vid);
3628c2ecf20Sopenharmony_ci		if (err && err != -EOPNOTSUPP)
3638c2ecf20Sopenharmony_ci			goto out;
3648c2ecf20Sopenharmony_ci		err = 0;
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	if (br_vlan_should_use(v)) {
3688c2ecf20Sopenharmony_ci		v->flags &= ~BRIDGE_VLAN_INFO_BRENTRY;
3698c2ecf20Sopenharmony_ci		vg->num_vlans--;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (masterv != v) {
3738c2ecf20Sopenharmony_ci		vlan_tunnel_info_del(vg, v);
3748c2ecf20Sopenharmony_ci		rhashtable_remove_fast(&vg->vlan_hash, &v->vnode,
3758c2ecf20Sopenharmony_ci				       br_vlan_rht_params);
3768c2ecf20Sopenharmony_ci		__vlan_del_list(v);
3778c2ecf20Sopenharmony_ci		nbp_vlan_set_vlan_dev_state(p, v->vid);
3788c2ecf20Sopenharmony_ci		call_rcu(&v->rcu, nbp_vlan_rcu_free);
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	br_vlan_put_master(masterv);
3828c2ecf20Sopenharmony_ciout:
3838c2ecf20Sopenharmony_ci	return err;
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic void __vlan_group_free(struct net_bridge_vlan_group *vg)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	WARN_ON(!list_empty(&vg->vlan_list));
3898c2ecf20Sopenharmony_ci	rhashtable_destroy(&vg->vlan_hash);
3908c2ecf20Sopenharmony_ci	vlan_tunnel_deinit(vg);
3918c2ecf20Sopenharmony_ci	kfree(vg);
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic void __vlan_flush(const struct net_bridge *br,
3958c2ecf20Sopenharmony_ci			 const struct net_bridge_port *p,
3968c2ecf20Sopenharmony_ci			 struct net_bridge_vlan_group *vg)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	struct net_bridge_vlan *vlan, *tmp;
3998c2ecf20Sopenharmony_ci	u16 v_start = 0, v_end = 0;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	__vlan_delete_pvid(vg, vg->pvid);
4028c2ecf20Sopenharmony_ci	list_for_each_entry_safe(vlan, tmp, &vg->vlan_list, vlist) {
4038c2ecf20Sopenharmony_ci		/* take care of disjoint ranges */
4048c2ecf20Sopenharmony_ci		if (!v_start) {
4058c2ecf20Sopenharmony_ci			v_start = vlan->vid;
4068c2ecf20Sopenharmony_ci		} else if (vlan->vid - v_end != 1) {
4078c2ecf20Sopenharmony_ci			/* found range end, notify and start next one */
4088c2ecf20Sopenharmony_ci			br_vlan_notify(br, p, v_start, v_end, RTM_DELVLAN);
4098c2ecf20Sopenharmony_ci			v_start = vlan->vid;
4108c2ecf20Sopenharmony_ci		}
4118c2ecf20Sopenharmony_ci		v_end = vlan->vid;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci		__vlan_del(vlan);
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	/* notify about the last/whole vlan range */
4178c2ecf20Sopenharmony_ci	if (v_start)
4188c2ecf20Sopenharmony_ci		br_vlan_notify(br, p, v_start, v_end, RTM_DELVLAN);
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistruct sk_buff *br_handle_vlan(struct net_bridge *br,
4228c2ecf20Sopenharmony_ci			       const struct net_bridge_port *p,
4238c2ecf20Sopenharmony_ci			       struct net_bridge_vlan_group *vg,
4248c2ecf20Sopenharmony_ci			       struct sk_buff *skb)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	struct br_vlan_stats *stats;
4278c2ecf20Sopenharmony_ci	struct net_bridge_vlan *v;
4288c2ecf20Sopenharmony_ci	u16 vid;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	/* If this packet was not filtered at input, let it pass */
4318c2ecf20Sopenharmony_ci	if (!BR_INPUT_SKB_CB(skb)->vlan_filtered)
4328c2ecf20Sopenharmony_ci		goto out;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	/* At this point, we know that the frame was filtered and contains
4358c2ecf20Sopenharmony_ci	 * a valid vlan id.  If the vlan id has untagged flag set,
4368c2ecf20Sopenharmony_ci	 * send untagged; otherwise, send tagged.
4378c2ecf20Sopenharmony_ci	 */
4388c2ecf20Sopenharmony_ci	br_vlan_get_tag(skb, &vid);
4398c2ecf20Sopenharmony_ci	v = br_vlan_find(vg, vid);
4408c2ecf20Sopenharmony_ci	/* Vlan entry must be configured at this point.  The
4418c2ecf20Sopenharmony_ci	 * only exception is the bridge is set in promisc mode and the
4428c2ecf20Sopenharmony_ci	 * packet is destined for the bridge device.  In this case
4438c2ecf20Sopenharmony_ci	 * pass the packet as is.
4448c2ecf20Sopenharmony_ci	 */
4458c2ecf20Sopenharmony_ci	if (!v || !br_vlan_should_use(v)) {
4468c2ecf20Sopenharmony_ci		if ((br->dev->flags & IFF_PROMISC) && skb->dev == br->dev) {
4478c2ecf20Sopenharmony_ci			goto out;
4488c2ecf20Sopenharmony_ci		} else {
4498c2ecf20Sopenharmony_ci			kfree_skb(skb);
4508c2ecf20Sopenharmony_ci			return NULL;
4518c2ecf20Sopenharmony_ci		}
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci	if (br_opt_get(br, BROPT_VLAN_STATS_ENABLED)) {
4548c2ecf20Sopenharmony_ci		stats = this_cpu_ptr(v->stats);
4558c2ecf20Sopenharmony_ci		u64_stats_update_begin(&stats->syncp);
4568c2ecf20Sopenharmony_ci		stats->tx_bytes += skb->len;
4578c2ecf20Sopenharmony_ci		stats->tx_packets++;
4588c2ecf20Sopenharmony_ci		u64_stats_update_end(&stats->syncp);
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED)
4628c2ecf20Sopenharmony_ci		__vlan_hwaccel_clear_tag(skb);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	if (p && (p->flags & BR_VLAN_TUNNEL) &&
4658c2ecf20Sopenharmony_ci	    br_handle_egress_vlan_tunnel(skb, v)) {
4668c2ecf20Sopenharmony_ci		kfree_skb(skb);
4678c2ecf20Sopenharmony_ci		return NULL;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ciout:
4708c2ecf20Sopenharmony_ci	return skb;
4718c2ecf20Sopenharmony_ci}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci/* Called under RCU */
4748c2ecf20Sopenharmony_cistatic bool __allowed_ingress(const struct net_bridge *br,
4758c2ecf20Sopenharmony_ci			      struct net_bridge_vlan_group *vg,
4768c2ecf20Sopenharmony_ci			      struct sk_buff *skb, u16 *vid,
4778c2ecf20Sopenharmony_ci			      u8 *state)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	struct br_vlan_stats *stats;
4808c2ecf20Sopenharmony_ci	struct net_bridge_vlan *v;
4818c2ecf20Sopenharmony_ci	bool tagged;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	BR_INPUT_SKB_CB(skb)->vlan_filtered = true;
4848c2ecf20Sopenharmony_ci	/* If vlan tx offload is disabled on bridge device and frame was
4858c2ecf20Sopenharmony_ci	 * sent from vlan device on the bridge device, it does not have
4868c2ecf20Sopenharmony_ci	 * HW accelerated vlan tag.
4878c2ecf20Sopenharmony_ci	 */
4888c2ecf20Sopenharmony_ci	if (unlikely(!skb_vlan_tag_present(skb) &&
4898c2ecf20Sopenharmony_ci		     skb->protocol == br->vlan_proto)) {
4908c2ecf20Sopenharmony_ci		skb = skb_vlan_untag(skb);
4918c2ecf20Sopenharmony_ci		if (unlikely(!skb))
4928c2ecf20Sopenharmony_ci			return false;
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	if (!br_vlan_get_tag(skb, vid)) {
4968c2ecf20Sopenharmony_ci		/* Tagged frame */
4978c2ecf20Sopenharmony_ci		if (skb->vlan_proto != br->vlan_proto) {
4988c2ecf20Sopenharmony_ci			/* Protocol-mismatch, empty out vlan_tci for new tag */
4998c2ecf20Sopenharmony_ci			skb_push(skb, ETH_HLEN);
5008c2ecf20Sopenharmony_ci			skb = vlan_insert_tag_set_proto(skb, skb->vlan_proto,
5018c2ecf20Sopenharmony_ci							skb_vlan_tag_get(skb));
5028c2ecf20Sopenharmony_ci			if (unlikely(!skb))
5038c2ecf20Sopenharmony_ci				return false;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci			skb_pull(skb, ETH_HLEN);
5068c2ecf20Sopenharmony_ci			skb_reset_mac_len(skb);
5078c2ecf20Sopenharmony_ci			*vid = 0;
5088c2ecf20Sopenharmony_ci			tagged = false;
5098c2ecf20Sopenharmony_ci		} else {
5108c2ecf20Sopenharmony_ci			tagged = true;
5118c2ecf20Sopenharmony_ci		}
5128c2ecf20Sopenharmony_ci	} else {
5138c2ecf20Sopenharmony_ci		/* Untagged frame */
5148c2ecf20Sopenharmony_ci		tagged = false;
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	if (!*vid) {
5188c2ecf20Sopenharmony_ci		u16 pvid = br_get_pvid(vg);
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci		/* Frame had a tag with VID 0 or did not have a tag.
5218c2ecf20Sopenharmony_ci		 * See if pvid is set on this port.  That tells us which
5228c2ecf20Sopenharmony_ci		 * vlan untagged or priority-tagged traffic belongs to.
5238c2ecf20Sopenharmony_ci		 */
5248c2ecf20Sopenharmony_ci		if (!pvid)
5258c2ecf20Sopenharmony_ci			goto drop;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci		/* PVID is set on this port.  Any untagged or priority-tagged
5288c2ecf20Sopenharmony_ci		 * ingress frame is considered to belong to this vlan.
5298c2ecf20Sopenharmony_ci		 */
5308c2ecf20Sopenharmony_ci		*vid = pvid;
5318c2ecf20Sopenharmony_ci		if (likely(!tagged))
5328c2ecf20Sopenharmony_ci			/* Untagged Frame. */
5338c2ecf20Sopenharmony_ci			__vlan_hwaccel_put_tag(skb, br->vlan_proto, pvid);
5348c2ecf20Sopenharmony_ci		else
5358c2ecf20Sopenharmony_ci			/* Priority-tagged Frame.
5368c2ecf20Sopenharmony_ci			 * At this point, we know that skb->vlan_tci VID
5378c2ecf20Sopenharmony_ci			 * field was 0.
5388c2ecf20Sopenharmony_ci			 * We update only VID field and preserve PCP field.
5398c2ecf20Sopenharmony_ci			 */
5408c2ecf20Sopenharmony_ci			skb->vlan_tci |= pvid;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci		/* if stats are disabled we can avoid the lookup */
5438c2ecf20Sopenharmony_ci		if (!br_opt_get(br, BROPT_VLAN_STATS_ENABLED)) {
5448c2ecf20Sopenharmony_ci			if (*state == BR_STATE_FORWARDING) {
5458c2ecf20Sopenharmony_ci				*state = br_vlan_get_pvid_state(vg);
5468c2ecf20Sopenharmony_ci				if (!br_vlan_state_allowed(*state, true))
5478c2ecf20Sopenharmony_ci					goto drop;
5488c2ecf20Sopenharmony_ci			}
5498c2ecf20Sopenharmony_ci			return true;
5508c2ecf20Sopenharmony_ci		}
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci	v = br_vlan_find(vg, *vid);
5538c2ecf20Sopenharmony_ci	if (!v || !br_vlan_should_use(v))
5548c2ecf20Sopenharmony_ci		goto drop;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	if (*state == BR_STATE_FORWARDING) {
5578c2ecf20Sopenharmony_ci		*state = br_vlan_get_state(v);
5588c2ecf20Sopenharmony_ci		if (!br_vlan_state_allowed(*state, true))
5598c2ecf20Sopenharmony_ci			goto drop;
5608c2ecf20Sopenharmony_ci	}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	if (br_opt_get(br, BROPT_VLAN_STATS_ENABLED)) {
5638c2ecf20Sopenharmony_ci		stats = this_cpu_ptr(v->stats);
5648c2ecf20Sopenharmony_ci		u64_stats_update_begin(&stats->syncp);
5658c2ecf20Sopenharmony_ci		stats->rx_bytes += skb->len;
5668c2ecf20Sopenharmony_ci		stats->rx_packets++;
5678c2ecf20Sopenharmony_ci		u64_stats_update_end(&stats->syncp);
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	return true;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_cidrop:
5738c2ecf20Sopenharmony_ci	kfree_skb(skb);
5748c2ecf20Sopenharmony_ci	return false;
5758c2ecf20Sopenharmony_ci}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_cibool br_allowed_ingress(const struct net_bridge *br,
5788c2ecf20Sopenharmony_ci			struct net_bridge_vlan_group *vg, struct sk_buff *skb,
5798c2ecf20Sopenharmony_ci			u16 *vid, u8 *state)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	/* If VLAN filtering is disabled on the bridge, all packets are
5828c2ecf20Sopenharmony_ci	 * permitted.
5838c2ecf20Sopenharmony_ci	 */
5848c2ecf20Sopenharmony_ci	if (!br_opt_get(br, BROPT_VLAN_ENABLED)) {
5858c2ecf20Sopenharmony_ci		BR_INPUT_SKB_CB(skb)->vlan_filtered = false;
5868c2ecf20Sopenharmony_ci		return true;
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	return __allowed_ingress(br, vg, skb, vid, state);
5908c2ecf20Sopenharmony_ci}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci/* Called under RCU. */
5938c2ecf20Sopenharmony_cibool br_allowed_egress(struct net_bridge_vlan_group *vg,
5948c2ecf20Sopenharmony_ci		       const struct sk_buff *skb)
5958c2ecf20Sopenharmony_ci{
5968c2ecf20Sopenharmony_ci	const struct net_bridge_vlan *v;
5978c2ecf20Sopenharmony_ci	u16 vid;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	/* If this packet was not filtered at input, let it pass */
6008c2ecf20Sopenharmony_ci	if (!BR_INPUT_SKB_CB(skb)->vlan_filtered)
6018c2ecf20Sopenharmony_ci		return true;
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	br_vlan_get_tag(skb, &vid);
6048c2ecf20Sopenharmony_ci	v = br_vlan_find(vg, vid);
6058c2ecf20Sopenharmony_ci	if (v && br_vlan_should_use(v) &&
6068c2ecf20Sopenharmony_ci	    br_vlan_state_allowed(br_vlan_get_state(v), false))
6078c2ecf20Sopenharmony_ci		return true;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	return false;
6108c2ecf20Sopenharmony_ci}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci/* Called under RCU */
6138c2ecf20Sopenharmony_cibool br_should_learn(struct net_bridge_port *p, struct sk_buff *skb, u16 *vid)
6148c2ecf20Sopenharmony_ci{
6158c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
6168c2ecf20Sopenharmony_ci	struct net_bridge *br = p->br;
6178c2ecf20Sopenharmony_ci	struct net_bridge_vlan *v;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	/* If filtering was disabled at input, let it pass. */
6208c2ecf20Sopenharmony_ci	if (!br_opt_get(br, BROPT_VLAN_ENABLED))
6218c2ecf20Sopenharmony_ci		return true;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	vg = nbp_vlan_group_rcu(p);
6248c2ecf20Sopenharmony_ci	if (!vg || !vg->num_vlans)
6258c2ecf20Sopenharmony_ci		return false;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	if (!br_vlan_get_tag(skb, vid) && skb->vlan_proto != br->vlan_proto)
6288c2ecf20Sopenharmony_ci		*vid = 0;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	if (!*vid) {
6318c2ecf20Sopenharmony_ci		*vid = br_get_pvid(vg);
6328c2ecf20Sopenharmony_ci		if (!*vid ||
6338c2ecf20Sopenharmony_ci		    !br_vlan_state_allowed(br_vlan_get_pvid_state(vg), true))
6348c2ecf20Sopenharmony_ci			return false;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci		return true;
6378c2ecf20Sopenharmony_ci	}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	v = br_vlan_find(vg, *vid);
6408c2ecf20Sopenharmony_ci	if (v && br_vlan_state_allowed(br_vlan_get_state(v), true))
6418c2ecf20Sopenharmony_ci		return true;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	return false;
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_cistatic int br_vlan_add_existing(struct net_bridge *br,
6478c2ecf20Sopenharmony_ci				struct net_bridge_vlan_group *vg,
6488c2ecf20Sopenharmony_ci				struct net_bridge_vlan *vlan,
6498c2ecf20Sopenharmony_ci				u16 flags, bool *changed,
6508c2ecf20Sopenharmony_ci				struct netlink_ext_ack *extack)
6518c2ecf20Sopenharmony_ci{
6528c2ecf20Sopenharmony_ci	int err;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	err = br_switchdev_port_vlan_add(br->dev, vlan->vid, flags, extack);
6558c2ecf20Sopenharmony_ci	if (err && err != -EOPNOTSUPP)
6568c2ecf20Sopenharmony_ci		return err;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	if (!br_vlan_is_brentry(vlan)) {
6598c2ecf20Sopenharmony_ci		/* Trying to change flags of non-existent bridge vlan */
6608c2ecf20Sopenharmony_ci		if (!(flags & BRIDGE_VLAN_INFO_BRENTRY)) {
6618c2ecf20Sopenharmony_ci			err = -EINVAL;
6628c2ecf20Sopenharmony_ci			goto err_flags;
6638c2ecf20Sopenharmony_ci		}
6648c2ecf20Sopenharmony_ci		/* It was only kept for port vlans, now make it real */
6658c2ecf20Sopenharmony_ci		err = br_fdb_insert(br, NULL, br->dev->dev_addr,
6668c2ecf20Sopenharmony_ci				    vlan->vid);
6678c2ecf20Sopenharmony_ci		if (err) {
6688c2ecf20Sopenharmony_ci			br_err(br, "failed to insert local address into bridge forwarding table\n");
6698c2ecf20Sopenharmony_ci			goto err_fdb_insert;
6708c2ecf20Sopenharmony_ci		}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci		refcount_inc(&vlan->refcnt);
6738c2ecf20Sopenharmony_ci		vlan->flags |= BRIDGE_VLAN_INFO_BRENTRY;
6748c2ecf20Sopenharmony_ci		vg->num_vlans++;
6758c2ecf20Sopenharmony_ci		*changed = true;
6768c2ecf20Sopenharmony_ci	}
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	if (__vlan_add_flags(vlan, flags))
6798c2ecf20Sopenharmony_ci		*changed = true;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	return 0;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_cierr_fdb_insert:
6848c2ecf20Sopenharmony_cierr_flags:
6858c2ecf20Sopenharmony_ci	br_switchdev_port_vlan_del(br->dev, vlan->vid);
6868c2ecf20Sopenharmony_ci	return err;
6878c2ecf20Sopenharmony_ci}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci/* Must be protected by RTNL.
6908c2ecf20Sopenharmony_ci * Must be called with vid in range from 1 to 4094 inclusive.
6918c2ecf20Sopenharmony_ci * changed must be true only if the vlan was created or updated
6928c2ecf20Sopenharmony_ci */
6938c2ecf20Sopenharmony_ciint br_vlan_add(struct net_bridge *br, u16 vid, u16 flags, bool *changed,
6948c2ecf20Sopenharmony_ci		struct netlink_ext_ack *extack)
6958c2ecf20Sopenharmony_ci{
6968c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
6978c2ecf20Sopenharmony_ci	struct net_bridge_vlan *vlan;
6988c2ecf20Sopenharmony_ci	int ret;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	ASSERT_RTNL();
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	*changed = false;
7038c2ecf20Sopenharmony_ci	vg = br_vlan_group(br);
7048c2ecf20Sopenharmony_ci	vlan = br_vlan_find(vg, vid);
7058c2ecf20Sopenharmony_ci	if (vlan)
7068c2ecf20Sopenharmony_ci		return br_vlan_add_existing(br, vg, vlan, flags, changed,
7078c2ecf20Sopenharmony_ci					    extack);
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
7108c2ecf20Sopenharmony_ci	if (!vlan)
7118c2ecf20Sopenharmony_ci		return -ENOMEM;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	vlan->stats = netdev_alloc_pcpu_stats(struct br_vlan_stats);
7148c2ecf20Sopenharmony_ci	if (!vlan->stats) {
7158c2ecf20Sopenharmony_ci		kfree(vlan);
7168c2ecf20Sopenharmony_ci		return -ENOMEM;
7178c2ecf20Sopenharmony_ci	}
7188c2ecf20Sopenharmony_ci	vlan->vid = vid;
7198c2ecf20Sopenharmony_ci	vlan->flags = flags | BRIDGE_VLAN_INFO_MASTER;
7208c2ecf20Sopenharmony_ci	vlan->flags &= ~BRIDGE_VLAN_INFO_PVID;
7218c2ecf20Sopenharmony_ci	vlan->br = br;
7228c2ecf20Sopenharmony_ci	if (flags & BRIDGE_VLAN_INFO_BRENTRY)
7238c2ecf20Sopenharmony_ci		refcount_set(&vlan->refcnt, 1);
7248c2ecf20Sopenharmony_ci	ret = __vlan_add(vlan, flags, extack);
7258c2ecf20Sopenharmony_ci	if (ret) {
7268c2ecf20Sopenharmony_ci		free_percpu(vlan->stats);
7278c2ecf20Sopenharmony_ci		kfree(vlan);
7288c2ecf20Sopenharmony_ci	} else {
7298c2ecf20Sopenharmony_ci		*changed = true;
7308c2ecf20Sopenharmony_ci	}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	return ret;
7338c2ecf20Sopenharmony_ci}
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci/* Must be protected by RTNL.
7368c2ecf20Sopenharmony_ci * Must be called with vid in range from 1 to 4094 inclusive.
7378c2ecf20Sopenharmony_ci */
7388c2ecf20Sopenharmony_ciint br_vlan_delete(struct net_bridge *br, u16 vid)
7398c2ecf20Sopenharmony_ci{
7408c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
7418c2ecf20Sopenharmony_ci	struct net_bridge_vlan *v;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	ASSERT_RTNL();
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	vg = br_vlan_group(br);
7468c2ecf20Sopenharmony_ci	v = br_vlan_find(vg, vid);
7478c2ecf20Sopenharmony_ci	if (!v || !br_vlan_is_brentry(v))
7488c2ecf20Sopenharmony_ci		return -ENOENT;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	br_fdb_find_delete_local(br, NULL, br->dev->dev_addr, vid);
7518c2ecf20Sopenharmony_ci	br_fdb_delete_by_port(br, NULL, vid, 0);
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	vlan_tunnel_info_del(vg, v);
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	return __vlan_del(v);
7568c2ecf20Sopenharmony_ci}
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_civoid br_vlan_flush(struct net_bridge *br)
7598c2ecf20Sopenharmony_ci{
7608c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	ASSERT_RTNL();
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	vg = br_vlan_group(br);
7658c2ecf20Sopenharmony_ci	__vlan_flush(br, NULL, vg);
7668c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(br->vlgrp, NULL);
7678c2ecf20Sopenharmony_ci	synchronize_rcu();
7688c2ecf20Sopenharmony_ci	__vlan_group_free(vg);
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_cistruct net_bridge_vlan *br_vlan_find(struct net_bridge_vlan_group *vg, u16 vid)
7728c2ecf20Sopenharmony_ci{
7738c2ecf20Sopenharmony_ci	if (!vg)
7748c2ecf20Sopenharmony_ci		return NULL;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	return br_vlan_lookup(&vg->vlan_hash, vid);
7778c2ecf20Sopenharmony_ci}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci/* Must be protected by RTNL. */
7808c2ecf20Sopenharmony_cistatic void recalculate_group_addr(struct net_bridge *br)
7818c2ecf20Sopenharmony_ci{
7828c2ecf20Sopenharmony_ci	if (br_opt_get(br, BROPT_GROUP_ADDR_SET))
7838c2ecf20Sopenharmony_ci		return;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	spin_lock_bh(&br->lock);
7868c2ecf20Sopenharmony_ci	if (!br_opt_get(br, BROPT_VLAN_ENABLED) ||
7878c2ecf20Sopenharmony_ci	    br->vlan_proto == htons(ETH_P_8021Q)) {
7888c2ecf20Sopenharmony_ci		/* Bridge Group Address */
7898c2ecf20Sopenharmony_ci		br->group_addr[5] = 0x00;
7908c2ecf20Sopenharmony_ci	} else { /* vlan_enabled && ETH_P_8021AD */
7918c2ecf20Sopenharmony_ci		/* Provider Bridge Group Address */
7928c2ecf20Sopenharmony_ci		br->group_addr[5] = 0x08;
7938c2ecf20Sopenharmony_ci	}
7948c2ecf20Sopenharmony_ci	spin_unlock_bh(&br->lock);
7958c2ecf20Sopenharmony_ci}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci/* Must be protected by RTNL. */
7988c2ecf20Sopenharmony_civoid br_recalculate_fwd_mask(struct net_bridge *br)
7998c2ecf20Sopenharmony_ci{
8008c2ecf20Sopenharmony_ci	if (!br_opt_get(br, BROPT_VLAN_ENABLED) ||
8018c2ecf20Sopenharmony_ci	    br->vlan_proto == htons(ETH_P_8021Q))
8028c2ecf20Sopenharmony_ci		br->group_fwd_mask_required = BR_GROUPFWD_DEFAULT;
8038c2ecf20Sopenharmony_ci	else /* vlan_enabled && ETH_P_8021AD */
8048c2ecf20Sopenharmony_ci		br->group_fwd_mask_required = BR_GROUPFWD_8021AD &
8058c2ecf20Sopenharmony_ci					      ~(1u << br->group_addr[5]);
8068c2ecf20Sopenharmony_ci}
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ciint __br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
8098c2ecf20Sopenharmony_ci{
8108c2ecf20Sopenharmony_ci	struct switchdev_attr attr = {
8118c2ecf20Sopenharmony_ci		.orig_dev = br->dev,
8128c2ecf20Sopenharmony_ci		.id = SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING,
8138c2ecf20Sopenharmony_ci		.flags = SWITCHDEV_F_SKIP_EOPNOTSUPP,
8148c2ecf20Sopenharmony_ci		.u.vlan_filtering = val,
8158c2ecf20Sopenharmony_ci	};
8168c2ecf20Sopenharmony_ci	int err;
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	if (br_opt_get(br, BROPT_VLAN_ENABLED) == !!val)
8198c2ecf20Sopenharmony_ci		return 0;
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	err = switchdev_port_attr_set(br->dev, &attr);
8228c2ecf20Sopenharmony_ci	if (err && err != -EOPNOTSUPP)
8238c2ecf20Sopenharmony_ci		return err;
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	br_opt_toggle(br, BROPT_VLAN_ENABLED, !!val);
8268c2ecf20Sopenharmony_ci	br_manage_promisc(br);
8278c2ecf20Sopenharmony_ci	recalculate_group_addr(br);
8288c2ecf20Sopenharmony_ci	br_recalculate_fwd_mask(br);
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	return 0;
8318c2ecf20Sopenharmony_ci}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ciint br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
8348c2ecf20Sopenharmony_ci{
8358c2ecf20Sopenharmony_ci	return __br_vlan_filter_toggle(br, val);
8368c2ecf20Sopenharmony_ci}
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_cibool br_vlan_enabled(const struct net_device *dev)
8398c2ecf20Sopenharmony_ci{
8408c2ecf20Sopenharmony_ci	struct net_bridge *br = netdev_priv(dev);
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	return br_opt_get(br, BROPT_VLAN_ENABLED);
8438c2ecf20Sopenharmony_ci}
8448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(br_vlan_enabled);
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ciint br_vlan_get_proto(const struct net_device *dev, u16 *p_proto)
8478c2ecf20Sopenharmony_ci{
8488c2ecf20Sopenharmony_ci	struct net_bridge *br = netdev_priv(dev);
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	*p_proto = ntohs(br->vlan_proto);
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	return 0;
8538c2ecf20Sopenharmony_ci}
8548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(br_vlan_get_proto);
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ciint __br_vlan_set_proto(struct net_bridge *br, __be16 proto)
8578c2ecf20Sopenharmony_ci{
8588c2ecf20Sopenharmony_ci	struct switchdev_attr attr = {
8598c2ecf20Sopenharmony_ci		.orig_dev = br->dev,
8608c2ecf20Sopenharmony_ci		.id = SWITCHDEV_ATTR_ID_BRIDGE_VLAN_PROTOCOL,
8618c2ecf20Sopenharmony_ci		.flags = SWITCHDEV_F_SKIP_EOPNOTSUPP,
8628c2ecf20Sopenharmony_ci		.u.vlan_protocol = ntohs(proto),
8638c2ecf20Sopenharmony_ci	};
8648c2ecf20Sopenharmony_ci	int err = 0;
8658c2ecf20Sopenharmony_ci	struct net_bridge_port *p;
8668c2ecf20Sopenharmony_ci	struct net_bridge_vlan *vlan;
8678c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
8688c2ecf20Sopenharmony_ci	__be16 oldproto = br->vlan_proto;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	if (br->vlan_proto == proto)
8718c2ecf20Sopenharmony_ci		return 0;
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	err = switchdev_port_attr_set(br->dev, &attr);
8748c2ecf20Sopenharmony_ci	if (err && err != -EOPNOTSUPP)
8758c2ecf20Sopenharmony_ci		return err;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	/* Add VLANs for the new proto to the device filter. */
8788c2ecf20Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list) {
8798c2ecf20Sopenharmony_ci		vg = nbp_vlan_group(p);
8808c2ecf20Sopenharmony_ci		list_for_each_entry(vlan, &vg->vlan_list, vlist) {
8818c2ecf20Sopenharmony_ci			if (vlan->priv_flags & BR_VLFLAG_ADDED_BY_SWITCHDEV)
8828c2ecf20Sopenharmony_ci				continue;
8838c2ecf20Sopenharmony_ci			err = vlan_vid_add(p->dev, proto, vlan->vid);
8848c2ecf20Sopenharmony_ci			if (err)
8858c2ecf20Sopenharmony_ci				goto err_filt;
8868c2ecf20Sopenharmony_ci		}
8878c2ecf20Sopenharmony_ci	}
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	br->vlan_proto = proto;
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	recalculate_group_addr(br);
8928c2ecf20Sopenharmony_ci	br_recalculate_fwd_mask(br);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	/* Delete VLANs for the old proto from the device filter. */
8958c2ecf20Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list) {
8968c2ecf20Sopenharmony_ci		vg = nbp_vlan_group(p);
8978c2ecf20Sopenharmony_ci		list_for_each_entry(vlan, &vg->vlan_list, vlist) {
8988c2ecf20Sopenharmony_ci			if (vlan->priv_flags & BR_VLFLAG_ADDED_BY_SWITCHDEV)
8998c2ecf20Sopenharmony_ci				continue;
9008c2ecf20Sopenharmony_ci			vlan_vid_del(p->dev, oldproto, vlan->vid);
9018c2ecf20Sopenharmony_ci		}
9028c2ecf20Sopenharmony_ci	}
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	return 0;
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_cierr_filt:
9078c2ecf20Sopenharmony_ci	attr.u.vlan_protocol = ntohs(oldproto);
9088c2ecf20Sopenharmony_ci	switchdev_port_attr_set(br->dev, &attr);
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	list_for_each_entry_continue_reverse(vlan, &vg->vlan_list, vlist) {
9118c2ecf20Sopenharmony_ci		if (vlan->priv_flags & BR_VLFLAG_ADDED_BY_SWITCHDEV)
9128c2ecf20Sopenharmony_ci			continue;
9138c2ecf20Sopenharmony_ci		vlan_vid_del(p->dev, proto, vlan->vid);
9148c2ecf20Sopenharmony_ci	}
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	list_for_each_entry_continue_reverse(p, &br->port_list, list) {
9178c2ecf20Sopenharmony_ci		vg = nbp_vlan_group(p);
9188c2ecf20Sopenharmony_ci		list_for_each_entry(vlan, &vg->vlan_list, vlist) {
9198c2ecf20Sopenharmony_ci			if (vlan->priv_flags & BR_VLFLAG_ADDED_BY_SWITCHDEV)
9208c2ecf20Sopenharmony_ci				continue;
9218c2ecf20Sopenharmony_ci			vlan_vid_del(p->dev, proto, vlan->vid);
9228c2ecf20Sopenharmony_ci		}
9238c2ecf20Sopenharmony_ci	}
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	return err;
9268c2ecf20Sopenharmony_ci}
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ciint br_vlan_set_proto(struct net_bridge *br, unsigned long val)
9298c2ecf20Sopenharmony_ci{
9308c2ecf20Sopenharmony_ci	if (val != ETH_P_8021Q && val != ETH_P_8021AD)
9318c2ecf20Sopenharmony_ci		return -EPROTONOSUPPORT;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	return __br_vlan_set_proto(br, htons(val));
9348c2ecf20Sopenharmony_ci}
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ciint br_vlan_set_stats(struct net_bridge *br, unsigned long val)
9378c2ecf20Sopenharmony_ci{
9388c2ecf20Sopenharmony_ci	switch (val) {
9398c2ecf20Sopenharmony_ci	case 0:
9408c2ecf20Sopenharmony_ci	case 1:
9418c2ecf20Sopenharmony_ci		br_opt_toggle(br, BROPT_VLAN_STATS_ENABLED, !!val);
9428c2ecf20Sopenharmony_ci		break;
9438c2ecf20Sopenharmony_ci	default:
9448c2ecf20Sopenharmony_ci		return -EINVAL;
9458c2ecf20Sopenharmony_ci	}
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	return 0;
9488c2ecf20Sopenharmony_ci}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ciint br_vlan_set_stats_per_port(struct net_bridge *br, unsigned long val)
9518c2ecf20Sopenharmony_ci{
9528c2ecf20Sopenharmony_ci	struct net_bridge_port *p;
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	/* allow to change the option if there are no port vlans configured */
9558c2ecf20Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list) {
9568c2ecf20Sopenharmony_ci		struct net_bridge_vlan_group *vg = nbp_vlan_group(p);
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci		if (vg->num_vlans)
9598c2ecf20Sopenharmony_ci			return -EBUSY;
9608c2ecf20Sopenharmony_ci	}
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	switch (val) {
9638c2ecf20Sopenharmony_ci	case 0:
9648c2ecf20Sopenharmony_ci	case 1:
9658c2ecf20Sopenharmony_ci		br_opt_toggle(br, BROPT_VLAN_STATS_PER_PORT, !!val);
9668c2ecf20Sopenharmony_ci		break;
9678c2ecf20Sopenharmony_ci	default:
9688c2ecf20Sopenharmony_ci		return -EINVAL;
9698c2ecf20Sopenharmony_ci	}
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	return 0;
9728c2ecf20Sopenharmony_ci}
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_cistatic bool vlan_default_pvid(struct net_bridge_vlan_group *vg, u16 vid)
9758c2ecf20Sopenharmony_ci{
9768c2ecf20Sopenharmony_ci	struct net_bridge_vlan *v;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	if (vid != vg->pvid)
9798c2ecf20Sopenharmony_ci		return false;
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	v = br_vlan_lookup(&vg->vlan_hash, vid);
9828c2ecf20Sopenharmony_ci	if (v && br_vlan_should_use(v) &&
9838c2ecf20Sopenharmony_ci	    (v->flags & BRIDGE_VLAN_INFO_UNTAGGED))
9848c2ecf20Sopenharmony_ci		return true;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	return false;
9878c2ecf20Sopenharmony_ci}
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_cistatic void br_vlan_disable_default_pvid(struct net_bridge *br)
9908c2ecf20Sopenharmony_ci{
9918c2ecf20Sopenharmony_ci	struct net_bridge_port *p;
9928c2ecf20Sopenharmony_ci	u16 pvid = br->default_pvid;
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	/* Disable default_pvid on all ports where it is still
9958c2ecf20Sopenharmony_ci	 * configured.
9968c2ecf20Sopenharmony_ci	 */
9978c2ecf20Sopenharmony_ci	if (vlan_default_pvid(br_vlan_group(br), pvid)) {
9988c2ecf20Sopenharmony_ci		if (!br_vlan_delete(br, pvid))
9998c2ecf20Sopenharmony_ci			br_vlan_notify(br, NULL, pvid, 0, RTM_DELVLAN);
10008c2ecf20Sopenharmony_ci	}
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list) {
10038c2ecf20Sopenharmony_ci		if (vlan_default_pvid(nbp_vlan_group(p), pvid) &&
10048c2ecf20Sopenharmony_ci		    !nbp_vlan_delete(p, pvid))
10058c2ecf20Sopenharmony_ci			br_vlan_notify(br, p, pvid, 0, RTM_DELVLAN);
10068c2ecf20Sopenharmony_ci	}
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	br->default_pvid = 0;
10098c2ecf20Sopenharmony_ci}
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ciint __br_vlan_set_default_pvid(struct net_bridge *br, u16 pvid,
10128c2ecf20Sopenharmony_ci			       struct netlink_ext_ack *extack)
10138c2ecf20Sopenharmony_ci{
10148c2ecf20Sopenharmony_ci	const struct net_bridge_vlan *pvent;
10158c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
10168c2ecf20Sopenharmony_ci	struct net_bridge_port *p;
10178c2ecf20Sopenharmony_ci	unsigned long *changed;
10188c2ecf20Sopenharmony_ci	bool vlchange;
10198c2ecf20Sopenharmony_ci	u16 old_pvid;
10208c2ecf20Sopenharmony_ci	int err = 0;
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	if (!pvid) {
10238c2ecf20Sopenharmony_ci		br_vlan_disable_default_pvid(br);
10248c2ecf20Sopenharmony_ci		return 0;
10258c2ecf20Sopenharmony_ci	}
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	changed = bitmap_zalloc(BR_MAX_PORTS, GFP_KERNEL);
10288c2ecf20Sopenharmony_ci	if (!changed)
10298c2ecf20Sopenharmony_ci		return -ENOMEM;
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	old_pvid = br->default_pvid;
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	/* Update default_pvid config only if we do not conflict with
10348c2ecf20Sopenharmony_ci	 * user configuration.
10358c2ecf20Sopenharmony_ci	 */
10368c2ecf20Sopenharmony_ci	vg = br_vlan_group(br);
10378c2ecf20Sopenharmony_ci	pvent = br_vlan_find(vg, pvid);
10388c2ecf20Sopenharmony_ci	if ((!old_pvid || vlan_default_pvid(vg, old_pvid)) &&
10398c2ecf20Sopenharmony_ci	    (!pvent || !br_vlan_should_use(pvent))) {
10408c2ecf20Sopenharmony_ci		err = br_vlan_add(br, pvid,
10418c2ecf20Sopenharmony_ci				  BRIDGE_VLAN_INFO_PVID |
10428c2ecf20Sopenharmony_ci				  BRIDGE_VLAN_INFO_UNTAGGED |
10438c2ecf20Sopenharmony_ci				  BRIDGE_VLAN_INFO_BRENTRY,
10448c2ecf20Sopenharmony_ci				  &vlchange, extack);
10458c2ecf20Sopenharmony_ci		if (err)
10468c2ecf20Sopenharmony_ci			goto out;
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci		if (br_vlan_delete(br, old_pvid))
10498c2ecf20Sopenharmony_ci			br_vlan_notify(br, NULL, old_pvid, 0, RTM_DELVLAN);
10508c2ecf20Sopenharmony_ci		br_vlan_notify(br, NULL, pvid, 0, RTM_NEWVLAN);
10518c2ecf20Sopenharmony_ci		set_bit(0, changed);
10528c2ecf20Sopenharmony_ci	}
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list) {
10558c2ecf20Sopenharmony_ci		/* Update default_pvid config only if we do not conflict with
10568c2ecf20Sopenharmony_ci		 * user configuration.
10578c2ecf20Sopenharmony_ci		 */
10588c2ecf20Sopenharmony_ci		vg = nbp_vlan_group(p);
10598c2ecf20Sopenharmony_ci		if ((old_pvid &&
10608c2ecf20Sopenharmony_ci		     !vlan_default_pvid(vg, old_pvid)) ||
10618c2ecf20Sopenharmony_ci		    br_vlan_find(vg, pvid))
10628c2ecf20Sopenharmony_ci			continue;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci		err = nbp_vlan_add(p, pvid,
10658c2ecf20Sopenharmony_ci				   BRIDGE_VLAN_INFO_PVID |
10668c2ecf20Sopenharmony_ci				   BRIDGE_VLAN_INFO_UNTAGGED,
10678c2ecf20Sopenharmony_ci				   &vlchange, extack);
10688c2ecf20Sopenharmony_ci		if (err)
10698c2ecf20Sopenharmony_ci			goto err_port;
10708c2ecf20Sopenharmony_ci		if (nbp_vlan_delete(p, old_pvid))
10718c2ecf20Sopenharmony_ci			br_vlan_notify(br, p, old_pvid, 0, RTM_DELVLAN);
10728c2ecf20Sopenharmony_ci		br_vlan_notify(p->br, p, pvid, 0, RTM_NEWVLAN);
10738c2ecf20Sopenharmony_ci		set_bit(p->port_no, changed);
10748c2ecf20Sopenharmony_ci	}
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	br->default_pvid = pvid;
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ciout:
10798c2ecf20Sopenharmony_ci	bitmap_free(changed);
10808c2ecf20Sopenharmony_ci	return err;
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_cierr_port:
10838c2ecf20Sopenharmony_ci	list_for_each_entry_continue_reverse(p, &br->port_list, list) {
10848c2ecf20Sopenharmony_ci		if (!test_bit(p->port_no, changed))
10858c2ecf20Sopenharmony_ci			continue;
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci		if (old_pvid) {
10888c2ecf20Sopenharmony_ci			nbp_vlan_add(p, old_pvid,
10898c2ecf20Sopenharmony_ci				     BRIDGE_VLAN_INFO_PVID |
10908c2ecf20Sopenharmony_ci				     BRIDGE_VLAN_INFO_UNTAGGED,
10918c2ecf20Sopenharmony_ci				     &vlchange, NULL);
10928c2ecf20Sopenharmony_ci			br_vlan_notify(p->br, p, old_pvid, 0, RTM_NEWVLAN);
10938c2ecf20Sopenharmony_ci		}
10948c2ecf20Sopenharmony_ci		nbp_vlan_delete(p, pvid);
10958c2ecf20Sopenharmony_ci		br_vlan_notify(br, p, pvid, 0, RTM_DELVLAN);
10968c2ecf20Sopenharmony_ci	}
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	if (test_bit(0, changed)) {
10998c2ecf20Sopenharmony_ci		if (old_pvid) {
11008c2ecf20Sopenharmony_ci			br_vlan_add(br, old_pvid,
11018c2ecf20Sopenharmony_ci				    BRIDGE_VLAN_INFO_PVID |
11028c2ecf20Sopenharmony_ci				    BRIDGE_VLAN_INFO_UNTAGGED |
11038c2ecf20Sopenharmony_ci				    BRIDGE_VLAN_INFO_BRENTRY,
11048c2ecf20Sopenharmony_ci				    &vlchange, NULL);
11058c2ecf20Sopenharmony_ci			br_vlan_notify(br, NULL, old_pvid, 0, RTM_NEWVLAN);
11068c2ecf20Sopenharmony_ci		}
11078c2ecf20Sopenharmony_ci		br_vlan_delete(br, pvid);
11088c2ecf20Sopenharmony_ci		br_vlan_notify(br, NULL, pvid, 0, RTM_DELVLAN);
11098c2ecf20Sopenharmony_ci	}
11108c2ecf20Sopenharmony_ci	goto out;
11118c2ecf20Sopenharmony_ci}
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ciint br_vlan_set_default_pvid(struct net_bridge *br, unsigned long val)
11148c2ecf20Sopenharmony_ci{
11158c2ecf20Sopenharmony_ci	u16 pvid = val;
11168c2ecf20Sopenharmony_ci	int err = 0;
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	if (val >= VLAN_VID_MASK)
11198c2ecf20Sopenharmony_ci		return -EINVAL;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	if (pvid == br->default_pvid)
11228c2ecf20Sopenharmony_ci		goto out;
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	/* Only allow default pvid change when filtering is disabled */
11258c2ecf20Sopenharmony_ci	if (br_opt_get(br, BROPT_VLAN_ENABLED)) {
11268c2ecf20Sopenharmony_ci		pr_info_once("Please disable vlan filtering to change default_pvid\n");
11278c2ecf20Sopenharmony_ci		err = -EPERM;
11288c2ecf20Sopenharmony_ci		goto out;
11298c2ecf20Sopenharmony_ci	}
11308c2ecf20Sopenharmony_ci	err = __br_vlan_set_default_pvid(br, pvid, NULL);
11318c2ecf20Sopenharmony_ciout:
11328c2ecf20Sopenharmony_ci	return err;
11338c2ecf20Sopenharmony_ci}
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ciint br_vlan_init(struct net_bridge *br)
11368c2ecf20Sopenharmony_ci{
11378c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
11388c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	vg = kzalloc(sizeof(*vg), GFP_KERNEL);
11418c2ecf20Sopenharmony_ci	if (!vg)
11428c2ecf20Sopenharmony_ci		goto out;
11438c2ecf20Sopenharmony_ci	ret = rhashtable_init(&vg->vlan_hash, &br_vlan_rht_params);
11448c2ecf20Sopenharmony_ci	if (ret)
11458c2ecf20Sopenharmony_ci		goto err_rhtbl;
11468c2ecf20Sopenharmony_ci	ret = vlan_tunnel_init(vg);
11478c2ecf20Sopenharmony_ci	if (ret)
11488c2ecf20Sopenharmony_ci		goto err_tunnel_init;
11498c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&vg->vlan_list);
11508c2ecf20Sopenharmony_ci	br->vlan_proto = htons(ETH_P_8021Q);
11518c2ecf20Sopenharmony_ci	br->default_pvid = 1;
11528c2ecf20Sopenharmony_ci	rcu_assign_pointer(br->vlgrp, vg);
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ciout:
11558c2ecf20Sopenharmony_ci	return ret;
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_cierr_tunnel_init:
11588c2ecf20Sopenharmony_ci	rhashtable_destroy(&vg->vlan_hash);
11598c2ecf20Sopenharmony_cierr_rhtbl:
11608c2ecf20Sopenharmony_ci	kfree(vg);
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	goto out;
11638c2ecf20Sopenharmony_ci}
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ciint nbp_vlan_init(struct net_bridge_port *p, struct netlink_ext_ack *extack)
11668c2ecf20Sopenharmony_ci{
11678c2ecf20Sopenharmony_ci	struct switchdev_attr attr = {
11688c2ecf20Sopenharmony_ci		.orig_dev = p->br->dev,
11698c2ecf20Sopenharmony_ci		.id = SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING,
11708c2ecf20Sopenharmony_ci		.flags = SWITCHDEV_F_SKIP_EOPNOTSUPP,
11718c2ecf20Sopenharmony_ci		.u.vlan_filtering = br_opt_get(p->br, BROPT_VLAN_ENABLED),
11728c2ecf20Sopenharmony_ci	};
11738c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
11748c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	vg = kzalloc(sizeof(struct net_bridge_vlan_group), GFP_KERNEL);
11778c2ecf20Sopenharmony_ci	if (!vg)
11788c2ecf20Sopenharmony_ci		goto out;
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	ret = switchdev_port_attr_set(p->dev, &attr);
11818c2ecf20Sopenharmony_ci	if (ret && ret != -EOPNOTSUPP)
11828c2ecf20Sopenharmony_ci		goto err_vlan_enabled;
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	ret = rhashtable_init(&vg->vlan_hash, &br_vlan_rht_params);
11858c2ecf20Sopenharmony_ci	if (ret)
11868c2ecf20Sopenharmony_ci		goto err_rhtbl;
11878c2ecf20Sopenharmony_ci	ret = vlan_tunnel_init(vg);
11888c2ecf20Sopenharmony_ci	if (ret)
11898c2ecf20Sopenharmony_ci		goto err_tunnel_init;
11908c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&vg->vlan_list);
11918c2ecf20Sopenharmony_ci	rcu_assign_pointer(p->vlgrp, vg);
11928c2ecf20Sopenharmony_ci	if (p->br->default_pvid) {
11938c2ecf20Sopenharmony_ci		bool changed;
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci		ret = nbp_vlan_add(p, p->br->default_pvid,
11968c2ecf20Sopenharmony_ci				   BRIDGE_VLAN_INFO_PVID |
11978c2ecf20Sopenharmony_ci				   BRIDGE_VLAN_INFO_UNTAGGED,
11988c2ecf20Sopenharmony_ci				   &changed, extack);
11998c2ecf20Sopenharmony_ci		if (ret)
12008c2ecf20Sopenharmony_ci			goto err_vlan_add;
12018c2ecf20Sopenharmony_ci		br_vlan_notify(p->br, p, p->br->default_pvid, 0, RTM_NEWVLAN);
12028c2ecf20Sopenharmony_ci	}
12038c2ecf20Sopenharmony_ciout:
12048c2ecf20Sopenharmony_ci	return ret;
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_cierr_vlan_add:
12078c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(p->vlgrp, NULL);
12088c2ecf20Sopenharmony_ci	synchronize_rcu();
12098c2ecf20Sopenharmony_ci	vlan_tunnel_deinit(vg);
12108c2ecf20Sopenharmony_cierr_tunnel_init:
12118c2ecf20Sopenharmony_ci	rhashtable_destroy(&vg->vlan_hash);
12128c2ecf20Sopenharmony_cierr_rhtbl:
12138c2ecf20Sopenharmony_cierr_vlan_enabled:
12148c2ecf20Sopenharmony_ci	kfree(vg);
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	goto out;
12178c2ecf20Sopenharmony_ci}
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci/* Must be protected by RTNL.
12208c2ecf20Sopenharmony_ci * Must be called with vid in range from 1 to 4094 inclusive.
12218c2ecf20Sopenharmony_ci * changed must be true only if the vlan was created or updated
12228c2ecf20Sopenharmony_ci */
12238c2ecf20Sopenharmony_ciint nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags,
12248c2ecf20Sopenharmony_ci		 bool *changed, struct netlink_ext_ack *extack)
12258c2ecf20Sopenharmony_ci{
12268c2ecf20Sopenharmony_ci	struct net_bridge_vlan *vlan;
12278c2ecf20Sopenharmony_ci	int ret;
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	ASSERT_RTNL();
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	*changed = false;
12328c2ecf20Sopenharmony_ci	vlan = br_vlan_find(nbp_vlan_group(port), vid);
12338c2ecf20Sopenharmony_ci	if (vlan) {
12348c2ecf20Sopenharmony_ci		/* Pass the flags to the hardware bridge */
12358c2ecf20Sopenharmony_ci		ret = br_switchdev_port_vlan_add(port->dev, vid, flags, extack);
12368c2ecf20Sopenharmony_ci		if (ret && ret != -EOPNOTSUPP)
12378c2ecf20Sopenharmony_ci			return ret;
12388c2ecf20Sopenharmony_ci		*changed = __vlan_add_flags(vlan, flags);
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci		return 0;
12418c2ecf20Sopenharmony_ci	}
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci	vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
12448c2ecf20Sopenharmony_ci	if (!vlan)
12458c2ecf20Sopenharmony_ci		return -ENOMEM;
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	vlan->vid = vid;
12488c2ecf20Sopenharmony_ci	vlan->port = port;
12498c2ecf20Sopenharmony_ci	ret = __vlan_add(vlan, flags, extack);
12508c2ecf20Sopenharmony_ci	if (ret)
12518c2ecf20Sopenharmony_ci		kfree(vlan);
12528c2ecf20Sopenharmony_ci	else
12538c2ecf20Sopenharmony_ci		*changed = true;
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci	return ret;
12568c2ecf20Sopenharmony_ci}
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci/* Must be protected by RTNL.
12598c2ecf20Sopenharmony_ci * Must be called with vid in range from 1 to 4094 inclusive.
12608c2ecf20Sopenharmony_ci */
12618c2ecf20Sopenharmony_ciint nbp_vlan_delete(struct net_bridge_port *port, u16 vid)
12628c2ecf20Sopenharmony_ci{
12638c2ecf20Sopenharmony_ci	struct net_bridge_vlan *v;
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci	ASSERT_RTNL();
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	v = br_vlan_find(nbp_vlan_group(port), vid);
12688c2ecf20Sopenharmony_ci	if (!v)
12698c2ecf20Sopenharmony_ci		return -ENOENT;
12708c2ecf20Sopenharmony_ci	br_fdb_find_delete_local(port->br, port, port->dev->dev_addr, vid);
12718c2ecf20Sopenharmony_ci	br_fdb_delete_by_port(port->br, port, vid, 0);
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	return __vlan_del(v);
12748c2ecf20Sopenharmony_ci}
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_civoid nbp_vlan_flush(struct net_bridge_port *port)
12778c2ecf20Sopenharmony_ci{
12788c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	ASSERT_RTNL();
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci	vg = nbp_vlan_group(port);
12838c2ecf20Sopenharmony_ci	__vlan_flush(port->br, port, vg);
12848c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(port->vlgrp, NULL);
12858c2ecf20Sopenharmony_ci	synchronize_rcu();
12868c2ecf20Sopenharmony_ci	__vlan_group_free(vg);
12878c2ecf20Sopenharmony_ci}
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_civoid br_vlan_get_stats(const struct net_bridge_vlan *v,
12908c2ecf20Sopenharmony_ci		       struct br_vlan_stats *stats)
12918c2ecf20Sopenharmony_ci{
12928c2ecf20Sopenharmony_ci	int i;
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_ci	memset(stats, 0, sizeof(*stats));
12958c2ecf20Sopenharmony_ci	for_each_possible_cpu(i) {
12968c2ecf20Sopenharmony_ci		u64 rxpackets, rxbytes, txpackets, txbytes;
12978c2ecf20Sopenharmony_ci		struct br_vlan_stats *cpu_stats;
12988c2ecf20Sopenharmony_ci		unsigned int start;
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci		cpu_stats = per_cpu_ptr(v->stats, i);
13018c2ecf20Sopenharmony_ci		do {
13028c2ecf20Sopenharmony_ci			start = u64_stats_fetch_begin_irq(&cpu_stats->syncp);
13038c2ecf20Sopenharmony_ci			rxpackets = cpu_stats->rx_packets;
13048c2ecf20Sopenharmony_ci			rxbytes = cpu_stats->rx_bytes;
13058c2ecf20Sopenharmony_ci			txbytes = cpu_stats->tx_bytes;
13068c2ecf20Sopenharmony_ci			txpackets = cpu_stats->tx_packets;
13078c2ecf20Sopenharmony_ci		} while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, start));
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci		stats->rx_packets += rxpackets;
13108c2ecf20Sopenharmony_ci		stats->rx_bytes += rxbytes;
13118c2ecf20Sopenharmony_ci		stats->tx_bytes += txbytes;
13128c2ecf20Sopenharmony_ci		stats->tx_packets += txpackets;
13138c2ecf20Sopenharmony_ci	}
13148c2ecf20Sopenharmony_ci}
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ciint br_vlan_get_pvid(const struct net_device *dev, u16 *p_pvid)
13178c2ecf20Sopenharmony_ci{
13188c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
13198c2ecf20Sopenharmony_ci	struct net_bridge_port *p;
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci	ASSERT_RTNL();
13228c2ecf20Sopenharmony_ci	p = br_port_get_check_rtnl(dev);
13238c2ecf20Sopenharmony_ci	if (p)
13248c2ecf20Sopenharmony_ci		vg = nbp_vlan_group(p);
13258c2ecf20Sopenharmony_ci	else if (netif_is_bridge_master(dev))
13268c2ecf20Sopenharmony_ci		vg = br_vlan_group(netdev_priv(dev));
13278c2ecf20Sopenharmony_ci	else
13288c2ecf20Sopenharmony_ci		return -EINVAL;
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci	*p_pvid = br_get_pvid(vg);
13318c2ecf20Sopenharmony_ci	return 0;
13328c2ecf20Sopenharmony_ci}
13338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(br_vlan_get_pvid);
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ciint br_vlan_get_pvid_rcu(const struct net_device *dev, u16 *p_pvid)
13368c2ecf20Sopenharmony_ci{
13378c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
13388c2ecf20Sopenharmony_ci	struct net_bridge_port *p;
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	p = br_port_get_check_rcu(dev);
13418c2ecf20Sopenharmony_ci	if (p)
13428c2ecf20Sopenharmony_ci		vg = nbp_vlan_group_rcu(p);
13438c2ecf20Sopenharmony_ci	else if (netif_is_bridge_master(dev))
13448c2ecf20Sopenharmony_ci		vg = br_vlan_group_rcu(netdev_priv(dev));
13458c2ecf20Sopenharmony_ci	else
13468c2ecf20Sopenharmony_ci		return -EINVAL;
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci	*p_pvid = br_get_pvid(vg);
13498c2ecf20Sopenharmony_ci	return 0;
13508c2ecf20Sopenharmony_ci}
13518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(br_vlan_get_pvid_rcu);
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ciint br_vlan_get_info(const struct net_device *dev, u16 vid,
13548c2ecf20Sopenharmony_ci		     struct bridge_vlan_info *p_vinfo)
13558c2ecf20Sopenharmony_ci{
13568c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
13578c2ecf20Sopenharmony_ci	struct net_bridge_vlan *v;
13588c2ecf20Sopenharmony_ci	struct net_bridge_port *p;
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci	ASSERT_RTNL();
13618c2ecf20Sopenharmony_ci	p = br_port_get_check_rtnl(dev);
13628c2ecf20Sopenharmony_ci	if (p)
13638c2ecf20Sopenharmony_ci		vg = nbp_vlan_group(p);
13648c2ecf20Sopenharmony_ci	else if (netif_is_bridge_master(dev))
13658c2ecf20Sopenharmony_ci		vg = br_vlan_group(netdev_priv(dev));
13668c2ecf20Sopenharmony_ci	else
13678c2ecf20Sopenharmony_ci		return -EINVAL;
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci	v = br_vlan_find(vg, vid);
13708c2ecf20Sopenharmony_ci	if (!v)
13718c2ecf20Sopenharmony_ci		return -ENOENT;
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci	p_vinfo->vid = vid;
13748c2ecf20Sopenharmony_ci	p_vinfo->flags = v->flags;
13758c2ecf20Sopenharmony_ci	if (vid == br_get_pvid(vg))
13768c2ecf20Sopenharmony_ci		p_vinfo->flags |= BRIDGE_VLAN_INFO_PVID;
13778c2ecf20Sopenharmony_ci	return 0;
13788c2ecf20Sopenharmony_ci}
13798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(br_vlan_get_info);
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_cistatic int br_vlan_is_bind_vlan_dev(const struct net_device *dev)
13828c2ecf20Sopenharmony_ci{
13838c2ecf20Sopenharmony_ci	return is_vlan_dev(dev) &&
13848c2ecf20Sopenharmony_ci		!!(vlan_dev_priv(dev)->flags & VLAN_FLAG_BRIDGE_BINDING);
13858c2ecf20Sopenharmony_ci}
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_cistatic int br_vlan_is_bind_vlan_dev_fn(struct net_device *dev,
13888c2ecf20Sopenharmony_ci			       __always_unused struct netdev_nested_priv *priv)
13898c2ecf20Sopenharmony_ci{
13908c2ecf20Sopenharmony_ci	return br_vlan_is_bind_vlan_dev(dev);
13918c2ecf20Sopenharmony_ci}
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_cistatic bool br_vlan_has_upper_bind_vlan_dev(struct net_device *dev)
13948c2ecf20Sopenharmony_ci{
13958c2ecf20Sopenharmony_ci	int found;
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	rcu_read_lock();
13988c2ecf20Sopenharmony_ci	found = netdev_walk_all_upper_dev_rcu(dev, br_vlan_is_bind_vlan_dev_fn,
13998c2ecf20Sopenharmony_ci					      NULL);
14008c2ecf20Sopenharmony_ci	rcu_read_unlock();
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci	return !!found;
14038c2ecf20Sopenharmony_ci}
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_cistruct br_vlan_bind_walk_data {
14068c2ecf20Sopenharmony_ci	u16 vid;
14078c2ecf20Sopenharmony_ci	struct net_device *result;
14088c2ecf20Sopenharmony_ci};
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_cistatic int br_vlan_match_bind_vlan_dev_fn(struct net_device *dev,
14118c2ecf20Sopenharmony_ci					  struct netdev_nested_priv *priv)
14128c2ecf20Sopenharmony_ci{
14138c2ecf20Sopenharmony_ci	struct br_vlan_bind_walk_data *data = priv->data;
14148c2ecf20Sopenharmony_ci	int found = 0;
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	if (br_vlan_is_bind_vlan_dev(dev) &&
14178c2ecf20Sopenharmony_ci	    vlan_dev_priv(dev)->vlan_id == data->vid) {
14188c2ecf20Sopenharmony_ci		data->result = dev;
14198c2ecf20Sopenharmony_ci		found = 1;
14208c2ecf20Sopenharmony_ci	}
14218c2ecf20Sopenharmony_ci
14228c2ecf20Sopenharmony_ci	return found;
14238c2ecf20Sopenharmony_ci}
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_cistatic struct net_device *
14268c2ecf20Sopenharmony_cibr_vlan_get_upper_bind_vlan_dev(struct net_device *dev, u16 vid)
14278c2ecf20Sopenharmony_ci{
14288c2ecf20Sopenharmony_ci	struct br_vlan_bind_walk_data data = {
14298c2ecf20Sopenharmony_ci		.vid = vid,
14308c2ecf20Sopenharmony_ci	};
14318c2ecf20Sopenharmony_ci	struct netdev_nested_priv priv = {
14328c2ecf20Sopenharmony_ci		.data = (void *)&data,
14338c2ecf20Sopenharmony_ci	};
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci	rcu_read_lock();
14368c2ecf20Sopenharmony_ci	netdev_walk_all_upper_dev_rcu(dev, br_vlan_match_bind_vlan_dev_fn,
14378c2ecf20Sopenharmony_ci				      &priv);
14388c2ecf20Sopenharmony_ci	rcu_read_unlock();
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_ci	return data.result;
14418c2ecf20Sopenharmony_ci}
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_cistatic bool br_vlan_is_dev_up(const struct net_device *dev)
14448c2ecf20Sopenharmony_ci{
14458c2ecf20Sopenharmony_ci	return  !!(dev->flags & IFF_UP) && netif_oper_up(dev);
14468c2ecf20Sopenharmony_ci}
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_cistatic void br_vlan_set_vlan_dev_state(const struct net_bridge *br,
14498c2ecf20Sopenharmony_ci				       struct net_device *vlan_dev)
14508c2ecf20Sopenharmony_ci{
14518c2ecf20Sopenharmony_ci	u16 vid = vlan_dev_priv(vlan_dev)->vlan_id;
14528c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
14538c2ecf20Sopenharmony_ci	struct net_bridge_port *p;
14548c2ecf20Sopenharmony_ci	bool has_carrier = false;
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	if (!netif_carrier_ok(br->dev)) {
14578c2ecf20Sopenharmony_ci		netif_carrier_off(vlan_dev);
14588c2ecf20Sopenharmony_ci		return;
14598c2ecf20Sopenharmony_ci	}
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list) {
14628c2ecf20Sopenharmony_ci		vg = nbp_vlan_group(p);
14638c2ecf20Sopenharmony_ci		if (br_vlan_find(vg, vid) && br_vlan_is_dev_up(p->dev)) {
14648c2ecf20Sopenharmony_ci			has_carrier = true;
14658c2ecf20Sopenharmony_ci			break;
14668c2ecf20Sopenharmony_ci		}
14678c2ecf20Sopenharmony_ci	}
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	if (has_carrier)
14708c2ecf20Sopenharmony_ci		netif_carrier_on(vlan_dev);
14718c2ecf20Sopenharmony_ci	else
14728c2ecf20Sopenharmony_ci		netif_carrier_off(vlan_dev);
14738c2ecf20Sopenharmony_ci}
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_cistatic void br_vlan_set_all_vlan_dev_state(struct net_bridge_port *p)
14768c2ecf20Sopenharmony_ci{
14778c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg = nbp_vlan_group(p);
14788c2ecf20Sopenharmony_ci	struct net_bridge_vlan *vlan;
14798c2ecf20Sopenharmony_ci	struct net_device *vlan_dev;
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci	list_for_each_entry(vlan, &vg->vlan_list, vlist) {
14828c2ecf20Sopenharmony_ci		vlan_dev = br_vlan_get_upper_bind_vlan_dev(p->br->dev,
14838c2ecf20Sopenharmony_ci							   vlan->vid);
14848c2ecf20Sopenharmony_ci		if (vlan_dev) {
14858c2ecf20Sopenharmony_ci			if (br_vlan_is_dev_up(p->dev)) {
14868c2ecf20Sopenharmony_ci				if (netif_carrier_ok(p->br->dev))
14878c2ecf20Sopenharmony_ci					netif_carrier_on(vlan_dev);
14888c2ecf20Sopenharmony_ci			} else {
14898c2ecf20Sopenharmony_ci				br_vlan_set_vlan_dev_state(p->br, vlan_dev);
14908c2ecf20Sopenharmony_ci			}
14918c2ecf20Sopenharmony_ci		}
14928c2ecf20Sopenharmony_ci	}
14938c2ecf20Sopenharmony_ci}
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_cistatic void br_vlan_upper_change(struct net_device *dev,
14968c2ecf20Sopenharmony_ci				 struct net_device *upper_dev,
14978c2ecf20Sopenharmony_ci				 bool linking)
14988c2ecf20Sopenharmony_ci{
14998c2ecf20Sopenharmony_ci	struct net_bridge *br = netdev_priv(dev);
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci	if (!br_vlan_is_bind_vlan_dev(upper_dev))
15028c2ecf20Sopenharmony_ci		return;
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_ci	if (linking) {
15058c2ecf20Sopenharmony_ci		br_vlan_set_vlan_dev_state(br, upper_dev);
15068c2ecf20Sopenharmony_ci		br_opt_toggle(br, BROPT_VLAN_BRIDGE_BINDING, true);
15078c2ecf20Sopenharmony_ci	} else {
15088c2ecf20Sopenharmony_ci		br_opt_toggle(br, BROPT_VLAN_BRIDGE_BINDING,
15098c2ecf20Sopenharmony_ci			      br_vlan_has_upper_bind_vlan_dev(dev));
15108c2ecf20Sopenharmony_ci	}
15118c2ecf20Sopenharmony_ci}
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_cistruct br_vlan_link_state_walk_data {
15148c2ecf20Sopenharmony_ci	struct net_bridge *br;
15158c2ecf20Sopenharmony_ci};
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_cistatic int br_vlan_link_state_change_fn(struct net_device *vlan_dev,
15188c2ecf20Sopenharmony_ci					struct netdev_nested_priv *priv)
15198c2ecf20Sopenharmony_ci{
15208c2ecf20Sopenharmony_ci	struct br_vlan_link_state_walk_data *data = priv->data;
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci	if (br_vlan_is_bind_vlan_dev(vlan_dev))
15238c2ecf20Sopenharmony_ci		br_vlan_set_vlan_dev_state(data->br, vlan_dev);
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci	return 0;
15268c2ecf20Sopenharmony_ci}
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_cistatic void br_vlan_link_state_change(struct net_device *dev,
15298c2ecf20Sopenharmony_ci				      struct net_bridge *br)
15308c2ecf20Sopenharmony_ci{
15318c2ecf20Sopenharmony_ci	struct br_vlan_link_state_walk_data data = {
15328c2ecf20Sopenharmony_ci		.br = br
15338c2ecf20Sopenharmony_ci	};
15348c2ecf20Sopenharmony_ci	struct netdev_nested_priv priv = {
15358c2ecf20Sopenharmony_ci		.data = (void *)&data,
15368c2ecf20Sopenharmony_ci	};
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_ci	rcu_read_lock();
15398c2ecf20Sopenharmony_ci	netdev_walk_all_upper_dev_rcu(dev, br_vlan_link_state_change_fn,
15408c2ecf20Sopenharmony_ci				      &priv);
15418c2ecf20Sopenharmony_ci	rcu_read_unlock();
15428c2ecf20Sopenharmony_ci}
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci/* Must be protected by RTNL. */
15458c2ecf20Sopenharmony_cistatic void nbp_vlan_set_vlan_dev_state(struct net_bridge_port *p, u16 vid)
15468c2ecf20Sopenharmony_ci{
15478c2ecf20Sopenharmony_ci	struct net_device *vlan_dev;
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci	if (!br_opt_get(p->br, BROPT_VLAN_BRIDGE_BINDING))
15508c2ecf20Sopenharmony_ci		return;
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	vlan_dev = br_vlan_get_upper_bind_vlan_dev(p->br->dev, vid);
15538c2ecf20Sopenharmony_ci	if (vlan_dev)
15548c2ecf20Sopenharmony_ci		br_vlan_set_vlan_dev_state(p->br, vlan_dev);
15558c2ecf20Sopenharmony_ci}
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci/* Must be protected by RTNL. */
15588c2ecf20Sopenharmony_ciint br_vlan_bridge_event(struct net_device *dev, unsigned long event, void *ptr)
15598c2ecf20Sopenharmony_ci{
15608c2ecf20Sopenharmony_ci	struct netdev_notifier_changeupper_info *info;
15618c2ecf20Sopenharmony_ci	struct net_bridge *br = netdev_priv(dev);
15628c2ecf20Sopenharmony_ci	int vlcmd = 0, ret = 0;
15638c2ecf20Sopenharmony_ci	bool changed = false;
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci	switch (event) {
15668c2ecf20Sopenharmony_ci	case NETDEV_REGISTER:
15678c2ecf20Sopenharmony_ci		ret = br_vlan_add(br, br->default_pvid,
15688c2ecf20Sopenharmony_ci				  BRIDGE_VLAN_INFO_PVID |
15698c2ecf20Sopenharmony_ci				  BRIDGE_VLAN_INFO_UNTAGGED |
15708c2ecf20Sopenharmony_ci				  BRIDGE_VLAN_INFO_BRENTRY, &changed, NULL);
15718c2ecf20Sopenharmony_ci		vlcmd = RTM_NEWVLAN;
15728c2ecf20Sopenharmony_ci		break;
15738c2ecf20Sopenharmony_ci	case NETDEV_UNREGISTER:
15748c2ecf20Sopenharmony_ci		changed = !br_vlan_delete(br, br->default_pvid);
15758c2ecf20Sopenharmony_ci		vlcmd = RTM_DELVLAN;
15768c2ecf20Sopenharmony_ci		break;
15778c2ecf20Sopenharmony_ci	case NETDEV_CHANGEUPPER:
15788c2ecf20Sopenharmony_ci		info = ptr;
15798c2ecf20Sopenharmony_ci		br_vlan_upper_change(dev, info->upper_dev, info->linking);
15808c2ecf20Sopenharmony_ci		break;
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci	case NETDEV_CHANGE:
15838c2ecf20Sopenharmony_ci	case NETDEV_UP:
15848c2ecf20Sopenharmony_ci		if (!br_opt_get(br, BROPT_VLAN_BRIDGE_BINDING))
15858c2ecf20Sopenharmony_ci			break;
15868c2ecf20Sopenharmony_ci		br_vlan_link_state_change(dev, br);
15878c2ecf20Sopenharmony_ci		break;
15888c2ecf20Sopenharmony_ci	}
15898c2ecf20Sopenharmony_ci	if (changed)
15908c2ecf20Sopenharmony_ci		br_vlan_notify(br, NULL, br->default_pvid, 0, vlcmd);
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_ci	return ret;
15938c2ecf20Sopenharmony_ci}
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci/* Must be protected by RTNL. */
15968c2ecf20Sopenharmony_civoid br_vlan_port_event(struct net_bridge_port *p, unsigned long event)
15978c2ecf20Sopenharmony_ci{
15988c2ecf20Sopenharmony_ci	if (!br_opt_get(p->br, BROPT_VLAN_BRIDGE_BINDING))
15998c2ecf20Sopenharmony_ci		return;
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_ci	switch (event) {
16028c2ecf20Sopenharmony_ci	case NETDEV_CHANGE:
16038c2ecf20Sopenharmony_ci	case NETDEV_DOWN:
16048c2ecf20Sopenharmony_ci	case NETDEV_UP:
16058c2ecf20Sopenharmony_ci		br_vlan_set_all_vlan_dev_state(p);
16068c2ecf20Sopenharmony_ci		break;
16078c2ecf20Sopenharmony_ci	}
16088c2ecf20Sopenharmony_ci}
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_cistatic bool br_vlan_stats_fill(struct sk_buff *skb,
16118c2ecf20Sopenharmony_ci			       const struct net_bridge_vlan *v)
16128c2ecf20Sopenharmony_ci{
16138c2ecf20Sopenharmony_ci	struct br_vlan_stats stats;
16148c2ecf20Sopenharmony_ci	struct nlattr *nest;
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci	nest = nla_nest_start(skb, BRIDGE_VLANDB_ENTRY_STATS);
16178c2ecf20Sopenharmony_ci	if (!nest)
16188c2ecf20Sopenharmony_ci		return false;
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci	br_vlan_get_stats(v, &stats);
16218c2ecf20Sopenharmony_ci	if (nla_put_u64_64bit(skb, BRIDGE_VLANDB_STATS_RX_BYTES, stats.rx_bytes,
16228c2ecf20Sopenharmony_ci			      BRIDGE_VLANDB_STATS_PAD) ||
16238c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, BRIDGE_VLANDB_STATS_RX_PACKETS,
16248c2ecf20Sopenharmony_ci			      stats.rx_packets, BRIDGE_VLANDB_STATS_PAD) ||
16258c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, BRIDGE_VLANDB_STATS_TX_BYTES, stats.tx_bytes,
16268c2ecf20Sopenharmony_ci			      BRIDGE_VLANDB_STATS_PAD) ||
16278c2ecf20Sopenharmony_ci	    nla_put_u64_64bit(skb, BRIDGE_VLANDB_STATS_TX_PACKETS,
16288c2ecf20Sopenharmony_ci			      stats.tx_packets, BRIDGE_VLANDB_STATS_PAD))
16298c2ecf20Sopenharmony_ci		goto out_err;
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci	nla_nest_end(skb, nest);
16328c2ecf20Sopenharmony_ci
16338c2ecf20Sopenharmony_ci	return true;
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_ciout_err:
16368c2ecf20Sopenharmony_ci	nla_nest_cancel(skb, nest);
16378c2ecf20Sopenharmony_ci	return false;
16388c2ecf20Sopenharmony_ci}
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci/* v_opts is used to dump the options which must be equal in the whole range */
16418c2ecf20Sopenharmony_cistatic bool br_vlan_fill_vids(struct sk_buff *skb, u16 vid, u16 vid_range,
16428c2ecf20Sopenharmony_ci			      const struct net_bridge_vlan *v_opts,
16438c2ecf20Sopenharmony_ci			      u16 flags,
16448c2ecf20Sopenharmony_ci			      bool dump_stats)
16458c2ecf20Sopenharmony_ci{
16468c2ecf20Sopenharmony_ci	struct bridge_vlan_info info;
16478c2ecf20Sopenharmony_ci	struct nlattr *nest;
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci	nest = nla_nest_start(skb, BRIDGE_VLANDB_ENTRY);
16508c2ecf20Sopenharmony_ci	if (!nest)
16518c2ecf20Sopenharmony_ci		return false;
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_ci	memset(&info, 0, sizeof(info));
16548c2ecf20Sopenharmony_ci	info.vid = vid;
16558c2ecf20Sopenharmony_ci	if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
16568c2ecf20Sopenharmony_ci		info.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
16578c2ecf20Sopenharmony_ci	if (flags & BRIDGE_VLAN_INFO_PVID)
16588c2ecf20Sopenharmony_ci		info.flags |= BRIDGE_VLAN_INFO_PVID;
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci	if (nla_put(skb, BRIDGE_VLANDB_ENTRY_INFO, sizeof(info), &info))
16618c2ecf20Sopenharmony_ci		goto out_err;
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci	if (vid_range && vid < vid_range &&
16648c2ecf20Sopenharmony_ci	    !(flags & BRIDGE_VLAN_INFO_PVID) &&
16658c2ecf20Sopenharmony_ci	    nla_put_u16(skb, BRIDGE_VLANDB_ENTRY_RANGE, vid_range))
16668c2ecf20Sopenharmony_ci		goto out_err;
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci	if (v_opts) {
16698c2ecf20Sopenharmony_ci		if (!br_vlan_opts_fill(skb, v_opts))
16708c2ecf20Sopenharmony_ci			goto out_err;
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_ci		if (dump_stats && !br_vlan_stats_fill(skb, v_opts))
16738c2ecf20Sopenharmony_ci			goto out_err;
16748c2ecf20Sopenharmony_ci	}
16758c2ecf20Sopenharmony_ci
16768c2ecf20Sopenharmony_ci	nla_nest_end(skb, nest);
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci	return true;
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ciout_err:
16818c2ecf20Sopenharmony_ci	nla_nest_cancel(skb, nest);
16828c2ecf20Sopenharmony_ci	return false;
16838c2ecf20Sopenharmony_ci}
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_cistatic size_t rtnl_vlan_nlmsg_size(void)
16868c2ecf20Sopenharmony_ci{
16878c2ecf20Sopenharmony_ci	return NLMSG_ALIGN(sizeof(struct br_vlan_msg))
16888c2ecf20Sopenharmony_ci		+ nla_total_size(0) /* BRIDGE_VLANDB_ENTRY */
16898c2ecf20Sopenharmony_ci		+ nla_total_size(sizeof(u16)) /* BRIDGE_VLANDB_ENTRY_RANGE */
16908c2ecf20Sopenharmony_ci		+ nla_total_size(sizeof(struct bridge_vlan_info)) /* BRIDGE_VLANDB_ENTRY_INFO */
16918c2ecf20Sopenharmony_ci		+ br_vlan_opts_nl_size(); /* bridge vlan options */
16928c2ecf20Sopenharmony_ci}
16938c2ecf20Sopenharmony_ci
16948c2ecf20Sopenharmony_civoid br_vlan_notify(const struct net_bridge *br,
16958c2ecf20Sopenharmony_ci		    const struct net_bridge_port *p,
16968c2ecf20Sopenharmony_ci		    u16 vid, u16 vid_range,
16978c2ecf20Sopenharmony_ci		    int cmd)
16988c2ecf20Sopenharmony_ci{
16998c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
17008c2ecf20Sopenharmony_ci	struct net_bridge_vlan *v = NULL;
17018c2ecf20Sopenharmony_ci	struct br_vlan_msg *bvm;
17028c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh;
17038c2ecf20Sopenharmony_ci	struct sk_buff *skb;
17048c2ecf20Sopenharmony_ci	int err = -ENOBUFS;
17058c2ecf20Sopenharmony_ci	struct net *net;
17068c2ecf20Sopenharmony_ci	u16 flags = 0;
17078c2ecf20Sopenharmony_ci	int ifindex;
17088c2ecf20Sopenharmony_ci
17098c2ecf20Sopenharmony_ci	/* right now notifications are done only with rtnl held */
17108c2ecf20Sopenharmony_ci	ASSERT_RTNL();
17118c2ecf20Sopenharmony_ci
17128c2ecf20Sopenharmony_ci	if (p) {
17138c2ecf20Sopenharmony_ci		ifindex = p->dev->ifindex;
17148c2ecf20Sopenharmony_ci		vg = nbp_vlan_group(p);
17158c2ecf20Sopenharmony_ci		net = dev_net(p->dev);
17168c2ecf20Sopenharmony_ci	} else {
17178c2ecf20Sopenharmony_ci		ifindex = br->dev->ifindex;
17188c2ecf20Sopenharmony_ci		vg = br_vlan_group(br);
17198c2ecf20Sopenharmony_ci		net = dev_net(br->dev);
17208c2ecf20Sopenharmony_ci	}
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci	skb = nlmsg_new(rtnl_vlan_nlmsg_size(), GFP_KERNEL);
17238c2ecf20Sopenharmony_ci	if (!skb)
17248c2ecf20Sopenharmony_ci		goto out_err;
17258c2ecf20Sopenharmony_ci
17268c2ecf20Sopenharmony_ci	err = -EMSGSIZE;
17278c2ecf20Sopenharmony_ci	nlh = nlmsg_put(skb, 0, 0, cmd, sizeof(*bvm), 0);
17288c2ecf20Sopenharmony_ci	if (!nlh)
17298c2ecf20Sopenharmony_ci		goto out_err;
17308c2ecf20Sopenharmony_ci	bvm = nlmsg_data(nlh);
17318c2ecf20Sopenharmony_ci	memset(bvm, 0, sizeof(*bvm));
17328c2ecf20Sopenharmony_ci	bvm->family = AF_BRIDGE;
17338c2ecf20Sopenharmony_ci	bvm->ifindex = ifindex;
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci	switch (cmd) {
17368c2ecf20Sopenharmony_ci	case RTM_NEWVLAN:
17378c2ecf20Sopenharmony_ci		/* need to find the vlan due to flags/options */
17388c2ecf20Sopenharmony_ci		v = br_vlan_find(vg, vid);
17398c2ecf20Sopenharmony_ci		if (!v || !br_vlan_should_use(v))
17408c2ecf20Sopenharmony_ci			goto out_kfree;
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_ci		flags = v->flags;
17438c2ecf20Sopenharmony_ci		if (br_get_pvid(vg) == v->vid)
17448c2ecf20Sopenharmony_ci			flags |= BRIDGE_VLAN_INFO_PVID;
17458c2ecf20Sopenharmony_ci		break;
17468c2ecf20Sopenharmony_ci	case RTM_DELVLAN:
17478c2ecf20Sopenharmony_ci		break;
17488c2ecf20Sopenharmony_ci	default:
17498c2ecf20Sopenharmony_ci		goto out_kfree;
17508c2ecf20Sopenharmony_ci	}
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	if (!br_vlan_fill_vids(skb, vid, vid_range, v, flags, false))
17538c2ecf20Sopenharmony_ci		goto out_err;
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci	nlmsg_end(skb, nlh);
17568c2ecf20Sopenharmony_ci	rtnl_notify(skb, net, 0, RTNLGRP_BRVLAN, NULL, GFP_KERNEL);
17578c2ecf20Sopenharmony_ci	return;
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_ciout_err:
17608c2ecf20Sopenharmony_ci	rtnl_set_sk_err(net, RTNLGRP_BRVLAN, err);
17618c2ecf20Sopenharmony_ciout_kfree:
17628c2ecf20Sopenharmony_ci	kfree_skb(skb);
17638c2ecf20Sopenharmony_ci}
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_ci/* check if v_curr can enter a range ending in range_end */
17668c2ecf20Sopenharmony_cibool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
17678c2ecf20Sopenharmony_ci			     const struct net_bridge_vlan *range_end)
17688c2ecf20Sopenharmony_ci{
17698c2ecf20Sopenharmony_ci	return v_curr->vid - range_end->vid == 1 &&
17708c2ecf20Sopenharmony_ci	       range_end->flags == v_curr->flags &&
17718c2ecf20Sopenharmony_ci	       br_vlan_opts_eq_range(v_curr, range_end);
17728c2ecf20Sopenharmony_ci}
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_cistatic int br_vlan_dump_dev(const struct net_device *dev,
17758c2ecf20Sopenharmony_ci			    struct sk_buff *skb,
17768c2ecf20Sopenharmony_ci			    struct netlink_callback *cb,
17778c2ecf20Sopenharmony_ci			    u32 dump_flags)
17788c2ecf20Sopenharmony_ci{
17798c2ecf20Sopenharmony_ci	struct net_bridge_vlan *v, *range_start = NULL, *range_end = NULL;
17808c2ecf20Sopenharmony_ci	bool dump_stats = !!(dump_flags & BRIDGE_VLANDB_DUMPF_STATS);
17818c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
17828c2ecf20Sopenharmony_ci	int idx = 0, s_idx = cb->args[1];
17838c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh = NULL;
17848c2ecf20Sopenharmony_ci	struct net_bridge_port *p;
17858c2ecf20Sopenharmony_ci	struct br_vlan_msg *bvm;
17868c2ecf20Sopenharmony_ci	struct net_bridge *br;
17878c2ecf20Sopenharmony_ci	int err = 0;
17888c2ecf20Sopenharmony_ci	u16 pvid;
17898c2ecf20Sopenharmony_ci
17908c2ecf20Sopenharmony_ci	if (!netif_is_bridge_master(dev) && !netif_is_bridge_port(dev))
17918c2ecf20Sopenharmony_ci		return -EINVAL;
17928c2ecf20Sopenharmony_ci
17938c2ecf20Sopenharmony_ci	if (netif_is_bridge_master(dev)) {
17948c2ecf20Sopenharmony_ci		br = netdev_priv(dev);
17958c2ecf20Sopenharmony_ci		vg = br_vlan_group_rcu(br);
17968c2ecf20Sopenharmony_ci		p = NULL;
17978c2ecf20Sopenharmony_ci	} else {
17988c2ecf20Sopenharmony_ci		p = br_port_get_rcu(dev);
17998c2ecf20Sopenharmony_ci		if (WARN_ON(!p))
18008c2ecf20Sopenharmony_ci			return -EINVAL;
18018c2ecf20Sopenharmony_ci		vg = nbp_vlan_group_rcu(p);
18028c2ecf20Sopenharmony_ci		br = p->br;
18038c2ecf20Sopenharmony_ci	}
18048c2ecf20Sopenharmony_ci
18058c2ecf20Sopenharmony_ci	if (!vg)
18068c2ecf20Sopenharmony_ci		return 0;
18078c2ecf20Sopenharmony_ci
18088c2ecf20Sopenharmony_ci	nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
18098c2ecf20Sopenharmony_ci			RTM_NEWVLAN, sizeof(*bvm), NLM_F_MULTI);
18108c2ecf20Sopenharmony_ci	if (!nlh)
18118c2ecf20Sopenharmony_ci		return -EMSGSIZE;
18128c2ecf20Sopenharmony_ci	bvm = nlmsg_data(nlh);
18138c2ecf20Sopenharmony_ci	memset(bvm, 0, sizeof(*bvm));
18148c2ecf20Sopenharmony_ci	bvm->family = PF_BRIDGE;
18158c2ecf20Sopenharmony_ci	bvm->ifindex = dev->ifindex;
18168c2ecf20Sopenharmony_ci	pvid = br_get_pvid(vg);
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_ci	/* idx must stay at range's beginning until it is filled in */
18198c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
18208c2ecf20Sopenharmony_ci		if (!br_vlan_should_use(v))
18218c2ecf20Sopenharmony_ci			continue;
18228c2ecf20Sopenharmony_ci		if (idx < s_idx) {
18238c2ecf20Sopenharmony_ci			idx++;
18248c2ecf20Sopenharmony_ci			continue;
18258c2ecf20Sopenharmony_ci		}
18268c2ecf20Sopenharmony_ci
18278c2ecf20Sopenharmony_ci		if (!range_start) {
18288c2ecf20Sopenharmony_ci			range_start = v;
18298c2ecf20Sopenharmony_ci			range_end = v;
18308c2ecf20Sopenharmony_ci			continue;
18318c2ecf20Sopenharmony_ci		}
18328c2ecf20Sopenharmony_ci
18338c2ecf20Sopenharmony_ci		if (dump_stats || v->vid == pvid ||
18348c2ecf20Sopenharmony_ci		    !br_vlan_can_enter_range(v, range_end)) {
18358c2ecf20Sopenharmony_ci			u16 vlan_flags = br_vlan_flags(range_start, pvid);
18368c2ecf20Sopenharmony_ci
18378c2ecf20Sopenharmony_ci			if (!br_vlan_fill_vids(skb, range_start->vid,
18388c2ecf20Sopenharmony_ci					       range_end->vid, range_start,
18398c2ecf20Sopenharmony_ci					       vlan_flags, dump_stats)) {
18408c2ecf20Sopenharmony_ci				err = -EMSGSIZE;
18418c2ecf20Sopenharmony_ci				break;
18428c2ecf20Sopenharmony_ci			}
18438c2ecf20Sopenharmony_ci			/* advance number of filled vlans */
18448c2ecf20Sopenharmony_ci			idx += range_end->vid - range_start->vid + 1;
18458c2ecf20Sopenharmony_ci
18468c2ecf20Sopenharmony_ci			range_start = v;
18478c2ecf20Sopenharmony_ci		}
18488c2ecf20Sopenharmony_ci		range_end = v;
18498c2ecf20Sopenharmony_ci	}
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci	/* err will be 0 and range_start will be set in 3 cases here:
18528c2ecf20Sopenharmony_ci	 * - first vlan (range_start == range_end)
18538c2ecf20Sopenharmony_ci	 * - last vlan (range_start == range_end, not in range)
18548c2ecf20Sopenharmony_ci	 * - last vlan range (range_start != range_end, in range)
18558c2ecf20Sopenharmony_ci	 */
18568c2ecf20Sopenharmony_ci	if (!err && range_start &&
18578c2ecf20Sopenharmony_ci	    !br_vlan_fill_vids(skb, range_start->vid, range_end->vid,
18588c2ecf20Sopenharmony_ci			       range_start, br_vlan_flags(range_start, pvid),
18598c2ecf20Sopenharmony_ci			       dump_stats))
18608c2ecf20Sopenharmony_ci		err = -EMSGSIZE;
18618c2ecf20Sopenharmony_ci
18628c2ecf20Sopenharmony_ci	cb->args[1] = err ? idx : 0;
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ci	nlmsg_end(skb, nlh);
18658c2ecf20Sopenharmony_ci
18668c2ecf20Sopenharmony_ci	return err;
18678c2ecf20Sopenharmony_ci}
18688c2ecf20Sopenharmony_ci
18698c2ecf20Sopenharmony_cistatic const struct nla_policy br_vlan_db_dump_pol[BRIDGE_VLANDB_DUMP_MAX + 1] = {
18708c2ecf20Sopenharmony_ci	[BRIDGE_VLANDB_DUMP_FLAGS] = { .type = NLA_U32 },
18718c2ecf20Sopenharmony_ci};
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_cistatic int br_vlan_rtm_dump(struct sk_buff *skb, struct netlink_callback *cb)
18748c2ecf20Sopenharmony_ci{
18758c2ecf20Sopenharmony_ci	struct nlattr *dtb[BRIDGE_VLANDB_DUMP_MAX + 1];
18768c2ecf20Sopenharmony_ci	int idx = 0, err = 0, s_idx = cb->args[0];
18778c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
18788c2ecf20Sopenharmony_ci	struct br_vlan_msg *bvm;
18798c2ecf20Sopenharmony_ci	struct net_device *dev;
18808c2ecf20Sopenharmony_ci	u32 dump_flags = 0;
18818c2ecf20Sopenharmony_ci
18828c2ecf20Sopenharmony_ci	err = nlmsg_parse(cb->nlh, sizeof(*bvm), dtb, BRIDGE_VLANDB_DUMP_MAX,
18838c2ecf20Sopenharmony_ci			  br_vlan_db_dump_pol, cb->extack);
18848c2ecf20Sopenharmony_ci	if (err < 0)
18858c2ecf20Sopenharmony_ci		return err;
18868c2ecf20Sopenharmony_ci
18878c2ecf20Sopenharmony_ci	bvm = nlmsg_data(cb->nlh);
18888c2ecf20Sopenharmony_ci	if (dtb[BRIDGE_VLANDB_DUMP_FLAGS])
18898c2ecf20Sopenharmony_ci		dump_flags = nla_get_u32(dtb[BRIDGE_VLANDB_DUMP_FLAGS]);
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci	rcu_read_lock();
18928c2ecf20Sopenharmony_ci	if (bvm->ifindex) {
18938c2ecf20Sopenharmony_ci		dev = dev_get_by_index_rcu(net, bvm->ifindex);
18948c2ecf20Sopenharmony_ci		if (!dev) {
18958c2ecf20Sopenharmony_ci			err = -ENODEV;
18968c2ecf20Sopenharmony_ci			goto out_err;
18978c2ecf20Sopenharmony_ci		}
18988c2ecf20Sopenharmony_ci		err = br_vlan_dump_dev(dev, skb, cb, dump_flags);
18998c2ecf20Sopenharmony_ci		/* if the dump completed without an error we return 0 here */
19008c2ecf20Sopenharmony_ci		if (err != -EMSGSIZE)
19018c2ecf20Sopenharmony_ci			goto out_err;
19028c2ecf20Sopenharmony_ci	} else {
19038c2ecf20Sopenharmony_ci		for_each_netdev_rcu(net, dev) {
19048c2ecf20Sopenharmony_ci			if (idx < s_idx)
19058c2ecf20Sopenharmony_ci				goto skip;
19068c2ecf20Sopenharmony_ci
19078c2ecf20Sopenharmony_ci			err = br_vlan_dump_dev(dev, skb, cb, dump_flags);
19088c2ecf20Sopenharmony_ci			if (err == -EMSGSIZE)
19098c2ecf20Sopenharmony_ci				break;
19108c2ecf20Sopenharmony_ciskip:
19118c2ecf20Sopenharmony_ci			idx++;
19128c2ecf20Sopenharmony_ci		}
19138c2ecf20Sopenharmony_ci	}
19148c2ecf20Sopenharmony_ci	cb->args[0] = idx;
19158c2ecf20Sopenharmony_ci	rcu_read_unlock();
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_ci	return skb->len;
19188c2ecf20Sopenharmony_ci
19198c2ecf20Sopenharmony_ciout_err:
19208c2ecf20Sopenharmony_ci	rcu_read_unlock();
19218c2ecf20Sopenharmony_ci
19228c2ecf20Sopenharmony_ci	return err;
19238c2ecf20Sopenharmony_ci}
19248c2ecf20Sopenharmony_ci
19258c2ecf20Sopenharmony_cistatic const struct nla_policy br_vlan_db_policy[BRIDGE_VLANDB_ENTRY_MAX + 1] = {
19268c2ecf20Sopenharmony_ci	[BRIDGE_VLANDB_ENTRY_INFO]	=
19278c2ecf20Sopenharmony_ci		NLA_POLICY_EXACT_LEN(sizeof(struct bridge_vlan_info)),
19288c2ecf20Sopenharmony_ci	[BRIDGE_VLANDB_ENTRY_RANGE]	= { .type = NLA_U16 },
19298c2ecf20Sopenharmony_ci	[BRIDGE_VLANDB_ENTRY_STATE]	= { .type = NLA_U8 },
19308c2ecf20Sopenharmony_ci	[BRIDGE_VLANDB_ENTRY_TUNNEL_INFO] = { .type = NLA_NESTED },
19318c2ecf20Sopenharmony_ci};
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_cistatic int br_vlan_rtm_process_one(struct net_device *dev,
19348c2ecf20Sopenharmony_ci				   const struct nlattr *attr,
19358c2ecf20Sopenharmony_ci				   int cmd, struct netlink_ext_ack *extack)
19368c2ecf20Sopenharmony_ci{
19378c2ecf20Sopenharmony_ci	struct bridge_vlan_info *vinfo, vrange_end, *vinfo_last = NULL;
19388c2ecf20Sopenharmony_ci	struct nlattr *tb[BRIDGE_VLANDB_ENTRY_MAX + 1];
19398c2ecf20Sopenharmony_ci	bool changed = false, skip_processing = false;
19408c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
19418c2ecf20Sopenharmony_ci	struct net_bridge_port *p = NULL;
19428c2ecf20Sopenharmony_ci	int err = 0, cmdmap = 0;
19438c2ecf20Sopenharmony_ci	struct net_bridge *br;
19448c2ecf20Sopenharmony_ci
19458c2ecf20Sopenharmony_ci	if (netif_is_bridge_master(dev)) {
19468c2ecf20Sopenharmony_ci		br = netdev_priv(dev);
19478c2ecf20Sopenharmony_ci		vg = br_vlan_group(br);
19488c2ecf20Sopenharmony_ci	} else {
19498c2ecf20Sopenharmony_ci		p = br_port_get_rtnl(dev);
19508c2ecf20Sopenharmony_ci		if (WARN_ON(!p))
19518c2ecf20Sopenharmony_ci			return -ENODEV;
19528c2ecf20Sopenharmony_ci		br = p->br;
19538c2ecf20Sopenharmony_ci		vg = nbp_vlan_group(p);
19548c2ecf20Sopenharmony_ci	}
19558c2ecf20Sopenharmony_ci
19568c2ecf20Sopenharmony_ci	if (WARN_ON(!vg))
19578c2ecf20Sopenharmony_ci		return -ENODEV;
19588c2ecf20Sopenharmony_ci
19598c2ecf20Sopenharmony_ci	err = nla_parse_nested(tb, BRIDGE_VLANDB_ENTRY_MAX, attr,
19608c2ecf20Sopenharmony_ci			       br_vlan_db_policy, extack);
19618c2ecf20Sopenharmony_ci	if (err)
19628c2ecf20Sopenharmony_ci		return err;
19638c2ecf20Sopenharmony_ci
19648c2ecf20Sopenharmony_ci	if (!tb[BRIDGE_VLANDB_ENTRY_INFO]) {
19658c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Missing vlan entry info");
19668c2ecf20Sopenharmony_ci		return -EINVAL;
19678c2ecf20Sopenharmony_ci	}
19688c2ecf20Sopenharmony_ci	memset(&vrange_end, 0, sizeof(vrange_end));
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci	vinfo = nla_data(tb[BRIDGE_VLANDB_ENTRY_INFO]);
19718c2ecf20Sopenharmony_ci	if (vinfo->flags & (BRIDGE_VLAN_INFO_RANGE_BEGIN |
19728c2ecf20Sopenharmony_ci			    BRIDGE_VLAN_INFO_RANGE_END)) {
19738c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Old-style vlan ranges are not allowed when using RTM vlan calls");
19748c2ecf20Sopenharmony_ci		return -EINVAL;
19758c2ecf20Sopenharmony_ci	}
19768c2ecf20Sopenharmony_ci	if (!br_vlan_valid_id(vinfo->vid, extack))
19778c2ecf20Sopenharmony_ci		return -EINVAL;
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci	if (tb[BRIDGE_VLANDB_ENTRY_RANGE]) {
19808c2ecf20Sopenharmony_ci		vrange_end.vid = nla_get_u16(tb[BRIDGE_VLANDB_ENTRY_RANGE]);
19818c2ecf20Sopenharmony_ci		/* validate user-provided flags without RANGE_BEGIN */
19828c2ecf20Sopenharmony_ci		vrange_end.flags = BRIDGE_VLAN_INFO_RANGE_END | vinfo->flags;
19838c2ecf20Sopenharmony_ci		vinfo->flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN;
19848c2ecf20Sopenharmony_ci
19858c2ecf20Sopenharmony_ci		/* vinfo_last is the range start, vinfo the range end */
19868c2ecf20Sopenharmony_ci		vinfo_last = vinfo;
19878c2ecf20Sopenharmony_ci		vinfo = &vrange_end;
19888c2ecf20Sopenharmony_ci
19898c2ecf20Sopenharmony_ci		if (!br_vlan_valid_id(vinfo->vid, extack) ||
19908c2ecf20Sopenharmony_ci		    !br_vlan_valid_range(vinfo, vinfo_last, extack))
19918c2ecf20Sopenharmony_ci			return -EINVAL;
19928c2ecf20Sopenharmony_ci	}
19938c2ecf20Sopenharmony_ci
19948c2ecf20Sopenharmony_ci	switch (cmd) {
19958c2ecf20Sopenharmony_ci	case RTM_NEWVLAN:
19968c2ecf20Sopenharmony_ci		cmdmap = RTM_SETLINK;
19978c2ecf20Sopenharmony_ci		skip_processing = !!(vinfo->flags & BRIDGE_VLAN_INFO_ONLY_OPTS);
19988c2ecf20Sopenharmony_ci		break;
19998c2ecf20Sopenharmony_ci	case RTM_DELVLAN:
20008c2ecf20Sopenharmony_ci		cmdmap = RTM_DELLINK;
20018c2ecf20Sopenharmony_ci		break;
20028c2ecf20Sopenharmony_ci	}
20038c2ecf20Sopenharmony_ci
20048c2ecf20Sopenharmony_ci	if (!skip_processing) {
20058c2ecf20Sopenharmony_ci		struct bridge_vlan_info *tmp_last = vinfo_last;
20068c2ecf20Sopenharmony_ci
20078c2ecf20Sopenharmony_ci		/* br_process_vlan_info may overwrite vinfo_last */
20088c2ecf20Sopenharmony_ci		err = br_process_vlan_info(br, p, cmdmap, vinfo, &tmp_last,
20098c2ecf20Sopenharmony_ci					   &changed, extack);
20108c2ecf20Sopenharmony_ci
20118c2ecf20Sopenharmony_ci		/* notify first if anything changed */
20128c2ecf20Sopenharmony_ci		if (changed)
20138c2ecf20Sopenharmony_ci			br_ifinfo_notify(cmdmap, br, p);
20148c2ecf20Sopenharmony_ci
20158c2ecf20Sopenharmony_ci		if (err)
20168c2ecf20Sopenharmony_ci			return err;
20178c2ecf20Sopenharmony_ci	}
20188c2ecf20Sopenharmony_ci
20198c2ecf20Sopenharmony_ci	/* deal with options */
20208c2ecf20Sopenharmony_ci	if (cmd == RTM_NEWVLAN) {
20218c2ecf20Sopenharmony_ci		struct net_bridge_vlan *range_start, *range_end;
20228c2ecf20Sopenharmony_ci
20238c2ecf20Sopenharmony_ci		if (vinfo_last) {
20248c2ecf20Sopenharmony_ci			range_start = br_vlan_find(vg, vinfo_last->vid);
20258c2ecf20Sopenharmony_ci			range_end = br_vlan_find(vg, vinfo->vid);
20268c2ecf20Sopenharmony_ci		} else {
20278c2ecf20Sopenharmony_ci			range_start = br_vlan_find(vg, vinfo->vid);
20288c2ecf20Sopenharmony_ci			range_end = range_start;
20298c2ecf20Sopenharmony_ci		}
20308c2ecf20Sopenharmony_ci
20318c2ecf20Sopenharmony_ci		err = br_vlan_process_options(br, p, range_start, range_end,
20328c2ecf20Sopenharmony_ci					      tb, extack);
20338c2ecf20Sopenharmony_ci	}
20348c2ecf20Sopenharmony_ci
20358c2ecf20Sopenharmony_ci	return err;
20368c2ecf20Sopenharmony_ci}
20378c2ecf20Sopenharmony_ci
20388c2ecf20Sopenharmony_cistatic int br_vlan_rtm_process(struct sk_buff *skb, struct nlmsghdr *nlh,
20398c2ecf20Sopenharmony_ci			       struct netlink_ext_ack *extack)
20408c2ecf20Sopenharmony_ci{
20418c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
20428c2ecf20Sopenharmony_ci	struct br_vlan_msg *bvm;
20438c2ecf20Sopenharmony_ci	struct net_device *dev;
20448c2ecf20Sopenharmony_ci	struct nlattr *attr;
20458c2ecf20Sopenharmony_ci	int err, vlans = 0;
20468c2ecf20Sopenharmony_ci	int rem;
20478c2ecf20Sopenharmony_ci
20488c2ecf20Sopenharmony_ci	/* this should validate the header and check for remaining bytes */
20498c2ecf20Sopenharmony_ci	err = nlmsg_parse(nlh, sizeof(*bvm), NULL, BRIDGE_VLANDB_MAX, NULL,
20508c2ecf20Sopenharmony_ci			  extack);
20518c2ecf20Sopenharmony_ci	if (err < 0)
20528c2ecf20Sopenharmony_ci		return err;
20538c2ecf20Sopenharmony_ci
20548c2ecf20Sopenharmony_ci	bvm = nlmsg_data(nlh);
20558c2ecf20Sopenharmony_ci	dev = __dev_get_by_index(net, bvm->ifindex);
20568c2ecf20Sopenharmony_ci	if (!dev)
20578c2ecf20Sopenharmony_ci		return -ENODEV;
20588c2ecf20Sopenharmony_ci
20598c2ecf20Sopenharmony_ci	if (!netif_is_bridge_master(dev) && !netif_is_bridge_port(dev)) {
20608c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "The device is not a valid bridge or bridge port");
20618c2ecf20Sopenharmony_ci		return -EINVAL;
20628c2ecf20Sopenharmony_ci	}
20638c2ecf20Sopenharmony_ci
20648c2ecf20Sopenharmony_ci	nlmsg_for_each_attr(attr, nlh, sizeof(*bvm), rem) {
20658c2ecf20Sopenharmony_ci		if (nla_type(attr) != BRIDGE_VLANDB_ENTRY)
20668c2ecf20Sopenharmony_ci			continue;
20678c2ecf20Sopenharmony_ci
20688c2ecf20Sopenharmony_ci		vlans++;
20698c2ecf20Sopenharmony_ci		err = br_vlan_rtm_process_one(dev, attr, nlh->nlmsg_type,
20708c2ecf20Sopenharmony_ci					      extack);
20718c2ecf20Sopenharmony_ci		if (err)
20728c2ecf20Sopenharmony_ci			break;
20738c2ecf20Sopenharmony_ci	}
20748c2ecf20Sopenharmony_ci	if (!vlans) {
20758c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "No vlans found to process");
20768c2ecf20Sopenharmony_ci		err = -EINVAL;
20778c2ecf20Sopenharmony_ci	}
20788c2ecf20Sopenharmony_ci
20798c2ecf20Sopenharmony_ci	return err;
20808c2ecf20Sopenharmony_ci}
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_civoid br_vlan_rtnl_init(void)
20838c2ecf20Sopenharmony_ci{
20848c2ecf20Sopenharmony_ci	rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_GETVLAN, NULL,
20858c2ecf20Sopenharmony_ci			     br_vlan_rtm_dump, 0);
20868c2ecf20Sopenharmony_ci	rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_NEWVLAN,
20878c2ecf20Sopenharmony_ci			     br_vlan_rtm_process, NULL, 0);
20888c2ecf20Sopenharmony_ci	rtnl_register_module(THIS_MODULE, PF_BRIDGE, RTM_DELVLAN,
20898c2ecf20Sopenharmony_ci			     br_vlan_rtm_process, NULL, 0);
20908c2ecf20Sopenharmony_ci}
20918c2ecf20Sopenharmony_ci
20928c2ecf20Sopenharmony_civoid br_vlan_rtnl_uninit(void)
20938c2ecf20Sopenharmony_ci{
20948c2ecf20Sopenharmony_ci	rtnl_unregister(PF_BRIDGE, RTM_GETVLAN);
20958c2ecf20Sopenharmony_ci	rtnl_unregister(PF_BRIDGE, RTM_NEWVLAN);
20968c2ecf20Sopenharmony_ci	rtnl_unregister(PF_BRIDGE, RTM_DELVLAN);
20978c2ecf20Sopenharmony_ci}
2098