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