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