162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// Copyright (c) 2020, Nikolay Aleksandrov <nikolay@cumulusnetworks.com> 362306a36Sopenharmony_ci#include <linux/kernel.h> 462306a36Sopenharmony_ci#include <linux/netdevice.h> 562306a36Sopenharmony_ci#include <linux/rtnetlink.h> 662306a36Sopenharmony_ci#include <linux/slab.h> 762306a36Sopenharmony_ci#include <net/ip_tunnels.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "br_private.h" 1062306a36Sopenharmony_ci#include "br_private_tunnel.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistatic bool __vlan_tun_put(struct sk_buff *skb, const struct net_bridge_vlan *v) 1362306a36Sopenharmony_ci{ 1462306a36Sopenharmony_ci __be32 tid = tunnel_id_to_key32(v->tinfo.tunnel_id); 1562306a36Sopenharmony_ci struct nlattr *nest; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci if (!v->tinfo.tunnel_dst) 1862306a36Sopenharmony_ci return true; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci nest = nla_nest_start(skb, BRIDGE_VLANDB_ENTRY_TUNNEL_INFO); 2162306a36Sopenharmony_ci if (!nest) 2262306a36Sopenharmony_ci return false; 2362306a36Sopenharmony_ci if (nla_put_u32(skb, BRIDGE_VLANDB_TINFO_ID, be32_to_cpu(tid))) { 2462306a36Sopenharmony_ci nla_nest_cancel(skb, nest); 2562306a36Sopenharmony_ci return false; 2662306a36Sopenharmony_ci } 2762306a36Sopenharmony_ci nla_nest_end(skb, nest); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci return true; 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic bool __vlan_tun_can_enter_range(const struct net_bridge_vlan *v_curr, 3362306a36Sopenharmony_ci const struct net_bridge_vlan *range_end) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci return (!v_curr->tinfo.tunnel_dst && !range_end->tinfo.tunnel_dst) || 3662306a36Sopenharmony_ci vlan_tunid_inrange(v_curr, range_end); 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* check if the options' state of v_curr allow it to enter the range */ 4062306a36Sopenharmony_cibool br_vlan_opts_eq_range(const struct net_bridge_vlan *v_curr, 4162306a36Sopenharmony_ci const struct net_bridge_vlan *range_end) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci u8 range_mc_rtr = br_vlan_multicast_router(range_end); 4462306a36Sopenharmony_ci u8 curr_mc_rtr = br_vlan_multicast_router(v_curr); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci return v_curr->state == range_end->state && 4762306a36Sopenharmony_ci __vlan_tun_can_enter_range(v_curr, range_end) && 4862306a36Sopenharmony_ci curr_mc_rtr == range_mc_rtr; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cibool br_vlan_opts_fill(struct sk_buff *skb, const struct net_bridge_vlan *v, 5262306a36Sopenharmony_ci const struct net_bridge_port *p) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci if (nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_STATE, br_vlan_get_state(v)) || 5562306a36Sopenharmony_ci !__vlan_tun_put(skb, v) || 5662306a36Sopenharmony_ci nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS, 5762306a36Sopenharmony_ci !!(v->priv_flags & BR_VLFLAG_NEIGH_SUPPRESS_ENABLED))) 5862306a36Sopenharmony_ci return false; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#ifdef CONFIG_BRIDGE_IGMP_SNOOPING 6162306a36Sopenharmony_ci if (nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_MCAST_ROUTER, 6262306a36Sopenharmony_ci br_vlan_multicast_router(v))) 6362306a36Sopenharmony_ci return false; 6462306a36Sopenharmony_ci if (p && !br_multicast_port_ctx_vlan_disabled(&v->port_mcast_ctx) && 6562306a36Sopenharmony_ci (nla_put_u32(skb, BRIDGE_VLANDB_ENTRY_MCAST_N_GROUPS, 6662306a36Sopenharmony_ci br_multicast_ngroups_get(&v->port_mcast_ctx)) || 6762306a36Sopenharmony_ci nla_put_u32(skb, BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS, 6862306a36Sopenharmony_ci br_multicast_ngroups_get_max(&v->port_mcast_ctx)))) 6962306a36Sopenharmony_ci return false; 7062306a36Sopenharmony_ci#endif 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return true; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cisize_t br_vlan_opts_nl_size(void) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci return nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_ENTRY_STATE */ 7862306a36Sopenharmony_ci + nla_total_size(0) /* BRIDGE_VLANDB_ENTRY_TUNNEL_INFO */ 7962306a36Sopenharmony_ci + nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_TINFO_ID */ 8062306a36Sopenharmony_ci#ifdef CONFIG_BRIDGE_IGMP_SNOOPING 8162306a36Sopenharmony_ci + nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_ENTRY_MCAST_ROUTER */ 8262306a36Sopenharmony_ci + nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_ENTRY_MCAST_N_GROUPS */ 8362306a36Sopenharmony_ci + nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS */ 8462306a36Sopenharmony_ci#endif 8562306a36Sopenharmony_ci + nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS */ 8662306a36Sopenharmony_ci + 0; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int br_vlan_modify_state(struct net_bridge_vlan_group *vg, 9062306a36Sopenharmony_ci struct net_bridge_vlan *v, 9162306a36Sopenharmony_ci u8 state, 9262306a36Sopenharmony_ci bool *changed, 9362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci struct net_bridge *br; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci ASSERT_RTNL(); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (state > BR_STATE_BLOCKING) { 10062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid vlan state"); 10162306a36Sopenharmony_ci return -EINVAL; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (br_vlan_is_brentry(v)) 10562306a36Sopenharmony_ci br = v->br; 10662306a36Sopenharmony_ci else 10762306a36Sopenharmony_ci br = v->port->br; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (br->stp_enabled == BR_KERNEL_STP) { 11062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Can't modify vlan state when using kernel STP"); 11162306a36Sopenharmony_ci return -EBUSY; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (br_opt_get(br, BROPT_MST_ENABLED)) { 11562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Can't modify vlan state directly when MST is enabled"); 11662306a36Sopenharmony_ci return -EBUSY; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (v->state == state) 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (v->vid == br_get_pvid(vg)) 12362306a36Sopenharmony_ci br_vlan_set_pvid_state(vg, state); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci br_vlan_set_state(v, state); 12662306a36Sopenharmony_ci *changed = true; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return 0; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic const struct nla_policy br_vlandb_tinfo_pol[BRIDGE_VLANDB_TINFO_MAX + 1] = { 13262306a36Sopenharmony_ci [BRIDGE_VLANDB_TINFO_ID] = { .type = NLA_U32 }, 13362306a36Sopenharmony_ci [BRIDGE_VLANDB_TINFO_CMD] = { .type = NLA_U32 }, 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic int br_vlan_modify_tunnel(const struct net_bridge_port *p, 13762306a36Sopenharmony_ci struct net_bridge_vlan *v, 13862306a36Sopenharmony_ci struct nlattr **tb, 13962306a36Sopenharmony_ci bool *changed, 14062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct nlattr *tun_tb[BRIDGE_VLANDB_TINFO_MAX + 1], *attr; 14362306a36Sopenharmony_ci struct bridge_vlan_info *vinfo; 14462306a36Sopenharmony_ci u32 tun_id = 0; 14562306a36Sopenharmony_ci int cmd, err; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (!p) { 14862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Can't modify tunnel mapping of non-port vlans"); 14962306a36Sopenharmony_ci return -EINVAL; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci if (!(p->flags & BR_VLAN_TUNNEL)) { 15262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Port doesn't have tunnel flag set"); 15362306a36Sopenharmony_ci return -EINVAL; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci attr = tb[BRIDGE_VLANDB_ENTRY_TUNNEL_INFO]; 15762306a36Sopenharmony_ci err = nla_parse_nested(tun_tb, BRIDGE_VLANDB_TINFO_MAX, attr, 15862306a36Sopenharmony_ci br_vlandb_tinfo_pol, extack); 15962306a36Sopenharmony_ci if (err) 16062306a36Sopenharmony_ci return err; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (!tun_tb[BRIDGE_VLANDB_TINFO_CMD]) { 16362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Missing tunnel command attribute"); 16462306a36Sopenharmony_ci return -ENOENT; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci cmd = nla_get_u32(tun_tb[BRIDGE_VLANDB_TINFO_CMD]); 16762306a36Sopenharmony_ci switch (cmd) { 16862306a36Sopenharmony_ci case RTM_SETLINK: 16962306a36Sopenharmony_ci if (!tun_tb[BRIDGE_VLANDB_TINFO_ID]) { 17062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Missing tunnel id attribute"); 17162306a36Sopenharmony_ci return -ENOENT; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci /* when working on vlan ranges this is the starting tunnel id */ 17462306a36Sopenharmony_ci tun_id = nla_get_u32(tun_tb[BRIDGE_VLANDB_TINFO_ID]); 17562306a36Sopenharmony_ci /* vlan info attr is guaranteed by br_vlan_rtm_process_one */ 17662306a36Sopenharmony_ci vinfo = nla_data(tb[BRIDGE_VLANDB_ENTRY_INFO]); 17762306a36Sopenharmony_ci /* tunnel ids are mapped to each vlan in increasing order, 17862306a36Sopenharmony_ci * the starting vlan is in BRIDGE_VLANDB_ENTRY_INFO and v is the 17962306a36Sopenharmony_ci * current vlan, so we compute: tun_id + v - vinfo->vid 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ci tun_id += v->vid - vinfo->vid; 18262306a36Sopenharmony_ci break; 18362306a36Sopenharmony_ci case RTM_DELLINK: 18462306a36Sopenharmony_ci break; 18562306a36Sopenharmony_ci default: 18662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unsupported tunnel command"); 18762306a36Sopenharmony_ci return -EINVAL; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci return br_vlan_tunnel_info(p, cmd, v->vid, tun_id, changed); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic int br_vlan_process_one_opts(const struct net_bridge *br, 19462306a36Sopenharmony_ci const struct net_bridge_port *p, 19562306a36Sopenharmony_ci struct net_bridge_vlan_group *vg, 19662306a36Sopenharmony_ci struct net_bridge_vlan *v, 19762306a36Sopenharmony_ci struct nlattr **tb, 19862306a36Sopenharmony_ci bool *changed, 19962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci int err; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci *changed = false; 20462306a36Sopenharmony_ci if (tb[BRIDGE_VLANDB_ENTRY_STATE]) { 20562306a36Sopenharmony_ci u8 state = nla_get_u8(tb[BRIDGE_VLANDB_ENTRY_STATE]); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci err = br_vlan_modify_state(vg, v, state, changed, extack); 20862306a36Sopenharmony_ci if (err) 20962306a36Sopenharmony_ci return err; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci if (tb[BRIDGE_VLANDB_ENTRY_TUNNEL_INFO]) { 21262306a36Sopenharmony_ci err = br_vlan_modify_tunnel(p, v, tb, changed, extack); 21362306a36Sopenharmony_ci if (err) 21462306a36Sopenharmony_ci return err; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci#ifdef CONFIG_BRIDGE_IGMP_SNOOPING 21862306a36Sopenharmony_ci if (tb[BRIDGE_VLANDB_ENTRY_MCAST_ROUTER]) { 21962306a36Sopenharmony_ci u8 val; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci val = nla_get_u8(tb[BRIDGE_VLANDB_ENTRY_MCAST_ROUTER]); 22262306a36Sopenharmony_ci err = br_multicast_set_vlan_router(v, val); 22362306a36Sopenharmony_ci if (err) 22462306a36Sopenharmony_ci return err; 22562306a36Sopenharmony_ci *changed = true; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci if (tb[BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS]) { 22862306a36Sopenharmony_ci u32 val; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (!p) { 23162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Can't set mcast_max_groups for non-port vlans"); 23262306a36Sopenharmony_ci return -EINVAL; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci if (br_multicast_port_ctx_vlan_disabled(&v->port_mcast_ctx)) { 23562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Multicast snooping disabled on this VLAN"); 23662306a36Sopenharmony_ci return -EINVAL; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci val = nla_get_u32(tb[BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS]); 24062306a36Sopenharmony_ci br_multicast_ngroups_set_max(&v->port_mcast_ctx, val); 24162306a36Sopenharmony_ci *changed = true; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci#endif 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (tb[BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS]) { 24662306a36Sopenharmony_ci bool enabled = v->priv_flags & BR_VLFLAG_NEIGH_SUPPRESS_ENABLED; 24762306a36Sopenharmony_ci bool val = nla_get_u8(tb[BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS]); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (!p) { 25062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Can't set neigh_suppress for non-port vlans"); 25162306a36Sopenharmony_ci return -EINVAL; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (val != enabled) { 25562306a36Sopenharmony_ci v->priv_flags ^= BR_VLFLAG_NEIGH_SUPPRESS_ENABLED; 25662306a36Sopenharmony_ci *changed = true; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci return 0; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ciint br_vlan_process_options(const struct net_bridge *br, 26462306a36Sopenharmony_ci const struct net_bridge_port *p, 26562306a36Sopenharmony_ci struct net_bridge_vlan *range_start, 26662306a36Sopenharmony_ci struct net_bridge_vlan *range_end, 26762306a36Sopenharmony_ci struct nlattr **tb, 26862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct net_bridge_vlan *v, *curr_start = NULL, *curr_end = NULL; 27162306a36Sopenharmony_ci struct net_bridge_vlan_group *vg; 27262306a36Sopenharmony_ci int vid, err = 0; 27362306a36Sopenharmony_ci u16 pvid; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (p) 27662306a36Sopenharmony_ci vg = nbp_vlan_group(p); 27762306a36Sopenharmony_ci else 27862306a36Sopenharmony_ci vg = br_vlan_group(br); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (!range_start || !br_vlan_should_use(range_start)) { 28162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Vlan range start doesn't exist, can't process options"); 28262306a36Sopenharmony_ci return -ENOENT; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci if (!range_end || !br_vlan_should_use(range_end)) { 28562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Vlan range end doesn't exist, can't process options"); 28662306a36Sopenharmony_ci return -ENOENT; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci pvid = br_get_pvid(vg); 29062306a36Sopenharmony_ci for (vid = range_start->vid; vid <= range_end->vid; vid++) { 29162306a36Sopenharmony_ci bool changed = false; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci v = br_vlan_find(vg, vid); 29462306a36Sopenharmony_ci if (!v || !br_vlan_should_use(v)) { 29562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Vlan in range doesn't exist, can't process options"); 29662306a36Sopenharmony_ci err = -ENOENT; 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci err = br_vlan_process_one_opts(br, p, vg, v, tb, &changed, 30162306a36Sopenharmony_ci extack); 30262306a36Sopenharmony_ci if (err) 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (changed) { 30662306a36Sopenharmony_ci /* vlan options changed, check for range */ 30762306a36Sopenharmony_ci if (!curr_start) { 30862306a36Sopenharmony_ci curr_start = v; 30962306a36Sopenharmony_ci curr_end = v; 31062306a36Sopenharmony_ci continue; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (v->vid == pvid || 31462306a36Sopenharmony_ci !br_vlan_can_enter_range(v, curr_end)) { 31562306a36Sopenharmony_ci br_vlan_notify(br, p, curr_start->vid, 31662306a36Sopenharmony_ci curr_end->vid, RTM_NEWVLAN); 31762306a36Sopenharmony_ci curr_start = v; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci curr_end = v; 32062306a36Sopenharmony_ci } else { 32162306a36Sopenharmony_ci /* nothing changed and nothing to notify yet */ 32262306a36Sopenharmony_ci if (!curr_start) 32362306a36Sopenharmony_ci continue; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci br_vlan_notify(br, p, curr_start->vid, curr_end->vid, 32662306a36Sopenharmony_ci RTM_NEWVLAN); 32762306a36Sopenharmony_ci curr_start = NULL; 32862306a36Sopenharmony_ci curr_end = NULL; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci if (curr_start) 33262306a36Sopenharmony_ci br_vlan_notify(br, p, curr_start->vid, curr_end->vid, 33362306a36Sopenharmony_ci RTM_NEWVLAN); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci return err; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cibool br_vlan_global_opts_can_enter_range(const struct net_bridge_vlan *v_curr, 33962306a36Sopenharmony_ci const struct net_bridge_vlan *r_end) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci return v_curr->vid - r_end->vid == 1 && 34262306a36Sopenharmony_ci v_curr->msti == r_end->msti && 34362306a36Sopenharmony_ci ((v_curr->priv_flags ^ r_end->priv_flags) & 34462306a36Sopenharmony_ci BR_VLFLAG_GLOBAL_MCAST_ENABLED) == 0 && 34562306a36Sopenharmony_ci br_multicast_ctx_options_equal(&v_curr->br_mcast_ctx, 34662306a36Sopenharmony_ci &r_end->br_mcast_ctx); 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cibool br_vlan_global_opts_fill(struct sk_buff *skb, u16 vid, u16 vid_range, 35062306a36Sopenharmony_ci const struct net_bridge_vlan *v_opts) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct nlattr *nest2 __maybe_unused; 35362306a36Sopenharmony_ci u64 clockval __maybe_unused; 35462306a36Sopenharmony_ci struct nlattr *nest; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci nest = nla_nest_start(skb, BRIDGE_VLANDB_GLOBAL_OPTIONS); 35762306a36Sopenharmony_ci if (!nest) 35862306a36Sopenharmony_ci return false; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (nla_put_u16(skb, BRIDGE_VLANDB_GOPTS_ID, vid)) 36162306a36Sopenharmony_ci goto out_err; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (vid_range && vid < vid_range && 36462306a36Sopenharmony_ci nla_put_u16(skb, BRIDGE_VLANDB_GOPTS_RANGE, vid_range)) 36562306a36Sopenharmony_ci goto out_err; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci#ifdef CONFIG_BRIDGE_IGMP_SNOOPING 36862306a36Sopenharmony_ci if (nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING, 36962306a36Sopenharmony_ci !!(v_opts->priv_flags & BR_VLFLAG_GLOBAL_MCAST_ENABLED)) || 37062306a36Sopenharmony_ci nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION, 37162306a36Sopenharmony_ci v_opts->br_mcast_ctx.multicast_igmp_version) || 37262306a36Sopenharmony_ci nla_put_u32(skb, BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT, 37362306a36Sopenharmony_ci v_opts->br_mcast_ctx.multicast_last_member_count) || 37462306a36Sopenharmony_ci nla_put_u32(skb, BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT, 37562306a36Sopenharmony_ci v_opts->br_mcast_ctx.multicast_startup_query_count) || 37662306a36Sopenharmony_ci nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_QUERIER, 37762306a36Sopenharmony_ci v_opts->br_mcast_ctx.multicast_querier) || 37862306a36Sopenharmony_ci br_multicast_dump_querier_state(skb, &v_opts->br_mcast_ctx, 37962306a36Sopenharmony_ci BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_STATE)) 38062306a36Sopenharmony_ci goto out_err; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci clockval = jiffies_to_clock_t(v_opts->br_mcast_ctx.multicast_last_member_interval); 38362306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL, 38462306a36Sopenharmony_ci clockval, BRIDGE_VLANDB_GOPTS_PAD)) 38562306a36Sopenharmony_ci goto out_err; 38662306a36Sopenharmony_ci clockval = jiffies_to_clock_t(v_opts->br_mcast_ctx.multicast_membership_interval); 38762306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL, 38862306a36Sopenharmony_ci clockval, BRIDGE_VLANDB_GOPTS_PAD)) 38962306a36Sopenharmony_ci goto out_err; 39062306a36Sopenharmony_ci clockval = jiffies_to_clock_t(v_opts->br_mcast_ctx.multicast_querier_interval); 39162306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL, 39262306a36Sopenharmony_ci clockval, BRIDGE_VLANDB_GOPTS_PAD)) 39362306a36Sopenharmony_ci goto out_err; 39462306a36Sopenharmony_ci clockval = jiffies_to_clock_t(v_opts->br_mcast_ctx.multicast_query_interval); 39562306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL, 39662306a36Sopenharmony_ci clockval, BRIDGE_VLANDB_GOPTS_PAD)) 39762306a36Sopenharmony_ci goto out_err; 39862306a36Sopenharmony_ci clockval = jiffies_to_clock_t(v_opts->br_mcast_ctx.multicast_query_response_interval); 39962306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL, 40062306a36Sopenharmony_ci clockval, BRIDGE_VLANDB_GOPTS_PAD)) 40162306a36Sopenharmony_ci goto out_err; 40262306a36Sopenharmony_ci clockval = jiffies_to_clock_t(v_opts->br_mcast_ctx.multicast_startup_query_interval); 40362306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL, 40462306a36Sopenharmony_ci clockval, BRIDGE_VLANDB_GOPTS_PAD)) 40562306a36Sopenharmony_ci goto out_err; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (br_rports_have_mc_router(&v_opts->br_mcast_ctx)) { 40862306a36Sopenharmony_ci nest2 = nla_nest_start(skb, 40962306a36Sopenharmony_ci BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS); 41062306a36Sopenharmony_ci if (!nest2) 41162306a36Sopenharmony_ci goto out_err; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci rcu_read_lock(); 41462306a36Sopenharmony_ci if (br_rports_fill_info(skb, &v_opts->br_mcast_ctx)) { 41562306a36Sopenharmony_ci rcu_read_unlock(); 41662306a36Sopenharmony_ci nla_nest_cancel(skb, nest2); 41762306a36Sopenharmony_ci goto out_err; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci rcu_read_unlock(); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci nla_nest_end(skb, nest2); 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 42562306a36Sopenharmony_ci if (nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION, 42662306a36Sopenharmony_ci v_opts->br_mcast_ctx.multicast_mld_version)) 42762306a36Sopenharmony_ci goto out_err; 42862306a36Sopenharmony_ci#endif 42962306a36Sopenharmony_ci#endif 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (nla_put_u16(skb, BRIDGE_VLANDB_GOPTS_MSTI, v_opts->msti)) 43262306a36Sopenharmony_ci goto out_err; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci nla_nest_end(skb, nest); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci return true; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ciout_err: 43962306a36Sopenharmony_ci nla_nest_cancel(skb, nest); 44062306a36Sopenharmony_ci return false; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic size_t rtnl_vlan_global_opts_nlmsg_size(const struct net_bridge_vlan *v) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci return NLMSG_ALIGN(sizeof(struct br_vlan_msg)) 44662306a36Sopenharmony_ci + nla_total_size(0) /* BRIDGE_VLANDB_GLOBAL_OPTIONS */ 44762306a36Sopenharmony_ci + nla_total_size(sizeof(u16)) /* BRIDGE_VLANDB_GOPTS_ID */ 44862306a36Sopenharmony_ci#ifdef CONFIG_BRIDGE_IGMP_SNOOPING 44962306a36Sopenharmony_ci + nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING */ 45062306a36Sopenharmony_ci + nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION */ 45162306a36Sopenharmony_ci + nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION */ 45262306a36Sopenharmony_ci + nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT */ 45362306a36Sopenharmony_ci + nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT */ 45462306a36Sopenharmony_ci + nla_total_size(sizeof(u64)) /* BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL */ 45562306a36Sopenharmony_ci + nla_total_size(sizeof(u64)) /* BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL */ 45662306a36Sopenharmony_ci + nla_total_size(sizeof(u64)) /* BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL */ 45762306a36Sopenharmony_ci + nla_total_size(sizeof(u64)) /* BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL */ 45862306a36Sopenharmony_ci + nla_total_size(sizeof(u64)) /* BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL */ 45962306a36Sopenharmony_ci + nla_total_size(sizeof(u64)) /* BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL */ 46062306a36Sopenharmony_ci + nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_QUERIER */ 46162306a36Sopenharmony_ci + br_multicast_querier_state_size() /* BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_STATE */ 46262306a36Sopenharmony_ci + nla_total_size(0) /* BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS */ 46362306a36Sopenharmony_ci + br_rports_size(&v->br_mcast_ctx) /* BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS */ 46462306a36Sopenharmony_ci#endif 46562306a36Sopenharmony_ci + nla_total_size(sizeof(u16)) /* BRIDGE_VLANDB_GOPTS_MSTI */ 46662306a36Sopenharmony_ci + nla_total_size(sizeof(u16)); /* BRIDGE_VLANDB_GOPTS_RANGE */ 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic void br_vlan_global_opts_notify(const struct net_bridge *br, 47062306a36Sopenharmony_ci u16 vid, u16 vid_range) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci struct net_bridge_vlan *v; 47362306a36Sopenharmony_ci struct br_vlan_msg *bvm; 47462306a36Sopenharmony_ci struct nlmsghdr *nlh; 47562306a36Sopenharmony_ci struct sk_buff *skb; 47662306a36Sopenharmony_ci int err = -ENOBUFS; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* right now notifications are done only with rtnl held */ 47962306a36Sopenharmony_ci ASSERT_RTNL(); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* need to find the vlan due to flags/options */ 48262306a36Sopenharmony_ci v = br_vlan_find(br_vlan_group(br), vid); 48362306a36Sopenharmony_ci if (!v) 48462306a36Sopenharmony_ci return; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci skb = nlmsg_new(rtnl_vlan_global_opts_nlmsg_size(v), GFP_KERNEL); 48762306a36Sopenharmony_ci if (!skb) 48862306a36Sopenharmony_ci goto out_err; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci err = -EMSGSIZE; 49162306a36Sopenharmony_ci nlh = nlmsg_put(skb, 0, 0, RTM_NEWVLAN, sizeof(*bvm), 0); 49262306a36Sopenharmony_ci if (!nlh) 49362306a36Sopenharmony_ci goto out_err; 49462306a36Sopenharmony_ci bvm = nlmsg_data(nlh); 49562306a36Sopenharmony_ci memset(bvm, 0, sizeof(*bvm)); 49662306a36Sopenharmony_ci bvm->family = AF_BRIDGE; 49762306a36Sopenharmony_ci bvm->ifindex = br->dev->ifindex; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (!br_vlan_global_opts_fill(skb, vid, vid_range, v)) 50062306a36Sopenharmony_ci goto out_err; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci nlmsg_end(skb, nlh); 50362306a36Sopenharmony_ci rtnl_notify(skb, dev_net(br->dev), 0, RTNLGRP_BRVLAN, NULL, GFP_KERNEL); 50462306a36Sopenharmony_ci return; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ciout_err: 50762306a36Sopenharmony_ci rtnl_set_sk_err(dev_net(br->dev), RTNLGRP_BRVLAN, err); 50862306a36Sopenharmony_ci kfree_skb(skb); 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic int br_vlan_process_global_one_opts(const struct net_bridge *br, 51262306a36Sopenharmony_ci struct net_bridge_vlan_group *vg, 51362306a36Sopenharmony_ci struct net_bridge_vlan *v, 51462306a36Sopenharmony_ci struct nlattr **tb, 51562306a36Sopenharmony_ci bool *changed, 51662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci int err __maybe_unused; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci *changed = false; 52162306a36Sopenharmony_ci#ifdef CONFIG_BRIDGE_IGMP_SNOOPING 52262306a36Sopenharmony_ci if (tb[BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING]) { 52362306a36Sopenharmony_ci u8 mc_snooping; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci mc_snooping = nla_get_u8(tb[BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING]); 52662306a36Sopenharmony_ci if (br_multicast_toggle_global_vlan(v, !!mc_snooping)) 52762306a36Sopenharmony_ci *changed = true; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci if (tb[BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION]) { 53062306a36Sopenharmony_ci u8 ver; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci ver = nla_get_u8(tb[BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION]); 53362306a36Sopenharmony_ci err = br_multicast_set_igmp_version(&v->br_mcast_ctx, ver); 53462306a36Sopenharmony_ci if (err) 53562306a36Sopenharmony_ci return err; 53662306a36Sopenharmony_ci *changed = true; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci if (tb[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT]) { 53962306a36Sopenharmony_ci u32 cnt; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci cnt = nla_get_u32(tb[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT]); 54262306a36Sopenharmony_ci v->br_mcast_ctx.multicast_last_member_count = cnt; 54362306a36Sopenharmony_ci *changed = true; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci if (tb[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT]) { 54662306a36Sopenharmony_ci u32 cnt; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci cnt = nla_get_u32(tb[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT]); 54962306a36Sopenharmony_ci v->br_mcast_ctx.multicast_startup_query_count = cnt; 55062306a36Sopenharmony_ci *changed = true; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci if (tb[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL]) { 55362306a36Sopenharmony_ci u64 val; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci val = nla_get_u64(tb[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL]); 55662306a36Sopenharmony_ci v->br_mcast_ctx.multicast_last_member_interval = clock_t_to_jiffies(val); 55762306a36Sopenharmony_ci *changed = true; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci if (tb[BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL]) { 56062306a36Sopenharmony_ci u64 val; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci val = nla_get_u64(tb[BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL]); 56362306a36Sopenharmony_ci v->br_mcast_ctx.multicast_membership_interval = clock_t_to_jiffies(val); 56462306a36Sopenharmony_ci *changed = true; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci if (tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL]) { 56762306a36Sopenharmony_ci u64 val; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci val = nla_get_u64(tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL]); 57062306a36Sopenharmony_ci v->br_mcast_ctx.multicast_querier_interval = clock_t_to_jiffies(val); 57162306a36Sopenharmony_ci *changed = true; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci if (tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL]) { 57462306a36Sopenharmony_ci u64 val; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci val = nla_get_u64(tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL]); 57762306a36Sopenharmony_ci br_multicast_set_query_intvl(&v->br_mcast_ctx, val); 57862306a36Sopenharmony_ci *changed = true; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci if (tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL]) { 58162306a36Sopenharmony_ci u64 val; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci val = nla_get_u64(tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL]); 58462306a36Sopenharmony_ci v->br_mcast_ctx.multicast_query_response_interval = clock_t_to_jiffies(val); 58562306a36Sopenharmony_ci *changed = true; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci if (tb[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL]) { 58862306a36Sopenharmony_ci u64 val; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci val = nla_get_u64(tb[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL]); 59162306a36Sopenharmony_ci br_multicast_set_startup_query_intvl(&v->br_mcast_ctx, val); 59262306a36Sopenharmony_ci *changed = true; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci if (tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER]) { 59562306a36Sopenharmony_ci u8 val; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci val = nla_get_u8(tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER]); 59862306a36Sopenharmony_ci err = br_multicast_set_querier(&v->br_mcast_ctx, val); 59962306a36Sopenharmony_ci if (err) 60062306a36Sopenharmony_ci return err; 60162306a36Sopenharmony_ci *changed = true; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 60462306a36Sopenharmony_ci if (tb[BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION]) { 60562306a36Sopenharmony_ci u8 ver; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci ver = nla_get_u8(tb[BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION]); 60862306a36Sopenharmony_ci err = br_multicast_set_mld_version(&v->br_mcast_ctx, ver); 60962306a36Sopenharmony_ci if (err) 61062306a36Sopenharmony_ci return err; 61162306a36Sopenharmony_ci *changed = true; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci#endif 61462306a36Sopenharmony_ci#endif 61562306a36Sopenharmony_ci if (tb[BRIDGE_VLANDB_GOPTS_MSTI]) { 61662306a36Sopenharmony_ci u16 msti; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci msti = nla_get_u16(tb[BRIDGE_VLANDB_GOPTS_MSTI]); 61962306a36Sopenharmony_ci err = br_mst_vlan_set_msti(v, msti); 62062306a36Sopenharmony_ci if (err) 62162306a36Sopenharmony_ci return err; 62262306a36Sopenharmony_ci *changed = true; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci return 0; 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic const struct nla_policy br_vlan_db_gpol[BRIDGE_VLANDB_GOPTS_MAX + 1] = { 62962306a36Sopenharmony_ci [BRIDGE_VLANDB_GOPTS_ID] = { .type = NLA_U16 }, 63062306a36Sopenharmony_ci [BRIDGE_VLANDB_GOPTS_RANGE] = { .type = NLA_U16 }, 63162306a36Sopenharmony_ci [BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING] = { .type = NLA_U8 }, 63262306a36Sopenharmony_ci [BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION] = { .type = NLA_U8 }, 63362306a36Sopenharmony_ci [BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL] = { .type = NLA_U64 }, 63462306a36Sopenharmony_ci [BRIDGE_VLANDB_GOPTS_MCAST_QUERIER] = { .type = NLA_U8 }, 63562306a36Sopenharmony_ci [BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION] = { .type = NLA_U8 }, 63662306a36Sopenharmony_ci [BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT] = { .type = NLA_U32 }, 63762306a36Sopenharmony_ci [BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT] = { .type = NLA_U32 }, 63862306a36Sopenharmony_ci [BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL] = { .type = NLA_U64 }, 63962306a36Sopenharmony_ci [BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL] = { .type = NLA_U64 }, 64062306a36Sopenharmony_ci [BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL] = { .type = NLA_U64 }, 64162306a36Sopenharmony_ci [BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL] = { .type = NLA_U64 }, 64262306a36Sopenharmony_ci [BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL] = { .type = NLA_U64 }, 64362306a36Sopenharmony_ci [BRIDGE_VLANDB_GOPTS_MSTI] = NLA_POLICY_MAX(NLA_U16, VLAN_N_VID - 1), 64462306a36Sopenharmony_ci}; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ciint br_vlan_rtm_process_global_options(struct net_device *dev, 64762306a36Sopenharmony_ci const struct nlattr *attr, 64862306a36Sopenharmony_ci int cmd, 64962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci struct net_bridge_vlan *v, *curr_start = NULL, *curr_end = NULL; 65262306a36Sopenharmony_ci struct nlattr *tb[BRIDGE_VLANDB_GOPTS_MAX + 1]; 65362306a36Sopenharmony_ci struct net_bridge_vlan_group *vg; 65462306a36Sopenharmony_ci u16 vid, vid_range = 0; 65562306a36Sopenharmony_ci struct net_bridge *br; 65662306a36Sopenharmony_ci int err = 0; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci if (cmd != RTM_NEWVLAN) { 65962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Global vlan options support only set operation"); 66062306a36Sopenharmony_ci return -EINVAL; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci if (!netif_is_bridge_master(dev)) { 66362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Global vlan options can only be set on bridge device"); 66462306a36Sopenharmony_ci return -EINVAL; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci br = netdev_priv(dev); 66762306a36Sopenharmony_ci vg = br_vlan_group(br); 66862306a36Sopenharmony_ci if (WARN_ON(!vg)) 66962306a36Sopenharmony_ci return -ENODEV; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci err = nla_parse_nested(tb, BRIDGE_VLANDB_GOPTS_MAX, attr, 67262306a36Sopenharmony_ci br_vlan_db_gpol, extack); 67362306a36Sopenharmony_ci if (err) 67462306a36Sopenharmony_ci return err; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (!tb[BRIDGE_VLANDB_GOPTS_ID]) { 67762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Missing vlan entry id"); 67862306a36Sopenharmony_ci return -EINVAL; 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci vid = nla_get_u16(tb[BRIDGE_VLANDB_GOPTS_ID]); 68162306a36Sopenharmony_ci if (!br_vlan_valid_id(vid, extack)) 68262306a36Sopenharmony_ci return -EINVAL; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if (tb[BRIDGE_VLANDB_GOPTS_RANGE]) { 68562306a36Sopenharmony_ci vid_range = nla_get_u16(tb[BRIDGE_VLANDB_GOPTS_RANGE]); 68662306a36Sopenharmony_ci if (!br_vlan_valid_id(vid_range, extack)) 68762306a36Sopenharmony_ci return -EINVAL; 68862306a36Sopenharmony_ci if (vid >= vid_range) { 68962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "End vlan id is less than or equal to start vlan id"); 69062306a36Sopenharmony_ci return -EINVAL; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci } else { 69362306a36Sopenharmony_ci vid_range = vid; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci for (; vid <= vid_range; vid++) { 69762306a36Sopenharmony_ci bool changed = false; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci v = br_vlan_find(vg, vid); 70062306a36Sopenharmony_ci if (!v) { 70162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Vlan in range doesn't exist, can't process global options"); 70262306a36Sopenharmony_ci err = -ENOENT; 70362306a36Sopenharmony_ci break; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci err = br_vlan_process_global_one_opts(br, vg, v, tb, &changed, 70762306a36Sopenharmony_ci extack); 70862306a36Sopenharmony_ci if (err) 70962306a36Sopenharmony_ci break; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (changed) { 71262306a36Sopenharmony_ci /* vlan options changed, check for range */ 71362306a36Sopenharmony_ci if (!curr_start) { 71462306a36Sopenharmony_ci curr_start = v; 71562306a36Sopenharmony_ci curr_end = v; 71662306a36Sopenharmony_ci continue; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci if (!br_vlan_global_opts_can_enter_range(v, curr_end)) { 72062306a36Sopenharmony_ci br_vlan_global_opts_notify(br, curr_start->vid, 72162306a36Sopenharmony_ci curr_end->vid); 72262306a36Sopenharmony_ci curr_start = v; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci curr_end = v; 72562306a36Sopenharmony_ci } else { 72662306a36Sopenharmony_ci /* nothing changed and nothing to notify yet */ 72762306a36Sopenharmony_ci if (!curr_start) 72862306a36Sopenharmony_ci continue; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci br_vlan_global_opts_notify(br, curr_start->vid, 73162306a36Sopenharmony_ci curr_end->vid); 73262306a36Sopenharmony_ci curr_start = NULL; 73362306a36Sopenharmony_ci curr_end = NULL; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci if (curr_start) 73762306a36Sopenharmony_ci br_vlan_global_opts_notify(br, curr_start->vid, curr_end->vid); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci return err; 74062306a36Sopenharmony_ci} 741