xref: /linux/net/bridge/br_vlan_options.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
17a53e718SNikolay Aleksandrov // SPDX-License-Identifier: GPL-2.0-only
27a53e718SNikolay Aleksandrov // Copyright (c) 2020, Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
37a53e718SNikolay Aleksandrov #include <linux/kernel.h>
47a53e718SNikolay Aleksandrov #include <linux/netdevice.h>
57a53e718SNikolay Aleksandrov #include <linux/rtnetlink.h>
67a53e718SNikolay Aleksandrov #include <linux/slab.h>
7188c67ddSNikolay Aleksandrov #include <net/ip_tunnels.h>
87a53e718SNikolay Aleksandrov 
97a53e718SNikolay Aleksandrov #include "br_private.h"
10188c67ddSNikolay Aleksandrov #include "br_private_tunnel.h"
11188c67ddSNikolay Aleksandrov 
__vlan_tun_put(struct sk_buff * skb,const struct net_bridge_vlan * v)12188c67ddSNikolay Aleksandrov static bool __vlan_tun_put(struct sk_buff *skb, const struct net_bridge_vlan *v)
13188c67ddSNikolay Aleksandrov {
14188c67ddSNikolay Aleksandrov 	__be32 tid = tunnel_id_to_key32(v->tinfo.tunnel_id);
15fa388f29SNikolay Aleksandrov 	struct nlattr *nest;
16188c67ddSNikolay Aleksandrov 
17188c67ddSNikolay Aleksandrov 	if (!v->tinfo.tunnel_dst)
18188c67ddSNikolay Aleksandrov 		return true;
19188c67ddSNikolay Aleksandrov 
20fa388f29SNikolay Aleksandrov 	nest = nla_nest_start(skb, BRIDGE_VLANDB_ENTRY_TUNNEL_INFO);
21fa388f29SNikolay Aleksandrov 	if (!nest)
22fa388f29SNikolay Aleksandrov 		return false;
23fa388f29SNikolay Aleksandrov 	if (nla_put_u32(skb, BRIDGE_VLANDB_TINFO_ID, be32_to_cpu(tid))) {
24fa388f29SNikolay Aleksandrov 		nla_nest_cancel(skb, nest);
25fa388f29SNikolay Aleksandrov 		return false;
26fa388f29SNikolay Aleksandrov 	}
27fa388f29SNikolay Aleksandrov 	nla_nest_end(skb, nest);
28fa388f29SNikolay Aleksandrov 
29fa388f29SNikolay Aleksandrov 	return true;
30188c67ddSNikolay Aleksandrov }
31188c67ddSNikolay Aleksandrov 
__vlan_tun_can_enter_range(const struct net_bridge_vlan * v_curr,const struct net_bridge_vlan * range_end)32188c67ddSNikolay Aleksandrov static bool __vlan_tun_can_enter_range(const struct net_bridge_vlan *v_curr,
33188c67ddSNikolay Aleksandrov 				       const struct net_bridge_vlan *range_end)
34188c67ddSNikolay Aleksandrov {
35188c67ddSNikolay Aleksandrov 	return (!v_curr->tinfo.tunnel_dst && !range_end->tinfo.tunnel_dst) ||
36188c67ddSNikolay Aleksandrov 	       vlan_tunid_inrange(v_curr, range_end);
37188c67ddSNikolay Aleksandrov }
387a53e718SNikolay Aleksandrov 
3999f7c5e0SNikolay Aleksandrov /* check if the options' state of v_curr allow it to enter the range */
br_vlan_opts_eq_range(const struct net_bridge_vlan * v_curr,const struct net_bridge_vlan * range_end)4099f7c5e0SNikolay Aleksandrov bool br_vlan_opts_eq_range(const struct net_bridge_vlan *v_curr,
4199f7c5e0SNikolay Aleksandrov 			   const struct net_bridge_vlan *range_end)
427a53e718SNikolay Aleksandrov {
432796d846SNikolay Aleksandrov 	u8 range_mc_rtr = br_vlan_multicast_router(range_end);
442796d846SNikolay Aleksandrov 	u8 curr_mc_rtr = br_vlan_multicast_router(v_curr);
452796d846SNikolay Aleksandrov 
46188c67ddSNikolay Aleksandrov 	return v_curr->state == range_end->state &&
472796d846SNikolay Aleksandrov 	       __vlan_tun_can_enter_range(v_curr, range_end) &&
482796d846SNikolay Aleksandrov 	       curr_mc_rtr == range_mc_rtr;
497a53e718SNikolay Aleksandrov }
507a53e718SNikolay Aleksandrov 
br_vlan_opts_fill(struct sk_buff * skb,const struct net_bridge_vlan * v,const struct net_bridge_port * p)51a1aee20dSPetr Machata bool br_vlan_opts_fill(struct sk_buff *skb, const struct net_bridge_vlan *v,
52a1aee20dSPetr Machata 		       const struct net_bridge_port *p)
537a53e718SNikolay Aleksandrov {
542796d846SNikolay Aleksandrov 	if (nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_STATE, br_vlan_get_state(v)) ||
55*83f6d600SIdo Schimmel 	    !__vlan_tun_put(skb, v) ||
56*83f6d600SIdo Schimmel 	    nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS,
57*83f6d600SIdo Schimmel 		       !!(v->priv_flags & BR_VLFLAG_NEIGH_SUPPRESS_ENABLED)))
582796d846SNikolay Aleksandrov 		return false;
592796d846SNikolay Aleksandrov 
602796d846SNikolay Aleksandrov #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
612796d846SNikolay Aleksandrov 	if (nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_MCAST_ROUTER,
622796d846SNikolay Aleksandrov 		       br_vlan_multicast_router(v)))
632796d846SNikolay Aleksandrov 		return false;
64a1aee20dSPetr Machata 	if (p && !br_multicast_port_ctx_vlan_disabled(&v->port_mcast_ctx) &&
65a1aee20dSPetr Machata 	    (nla_put_u32(skb, BRIDGE_VLANDB_ENTRY_MCAST_N_GROUPS,
66a1aee20dSPetr Machata 			 br_multicast_ngroups_get(&v->port_mcast_ctx)) ||
67a1aee20dSPetr Machata 	     nla_put_u32(skb, BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS,
68a1aee20dSPetr Machata 			 br_multicast_ngroups_get_max(&v->port_mcast_ctx))))
69a1aee20dSPetr Machata 		return false;
702796d846SNikolay Aleksandrov #endif
712796d846SNikolay Aleksandrov 
722796d846SNikolay Aleksandrov 	return true;
737a53e718SNikolay Aleksandrov }
747a53e718SNikolay Aleksandrov 
br_vlan_opts_nl_size(void)757a53e718SNikolay Aleksandrov size_t br_vlan_opts_nl_size(void)
767a53e718SNikolay Aleksandrov {
77188c67ddSNikolay Aleksandrov 	return nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_ENTRY_STATE */
78fa388f29SNikolay Aleksandrov 	       + nla_total_size(0) /* BRIDGE_VLANDB_ENTRY_TUNNEL_INFO */
792796d846SNikolay Aleksandrov 	       + nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_TINFO_ID */
802796d846SNikolay Aleksandrov #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
812796d846SNikolay Aleksandrov 	       + nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_ENTRY_MCAST_ROUTER */
82a1aee20dSPetr Machata 	       + nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_ENTRY_MCAST_N_GROUPS */
83a1aee20dSPetr Machata 	       + nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS */
842796d846SNikolay Aleksandrov #endif
85*83f6d600SIdo Schimmel 	       + nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS */
862796d846SNikolay Aleksandrov 	       + 0;
87a580c76dSNikolay Aleksandrov }
88a580c76dSNikolay Aleksandrov 
br_vlan_modify_state(struct net_bridge_vlan_group * vg,struct net_bridge_vlan * v,u8 state,bool * changed,struct netlink_ext_ack * extack)89a580c76dSNikolay Aleksandrov static int br_vlan_modify_state(struct net_bridge_vlan_group *vg,
90a580c76dSNikolay Aleksandrov 				struct net_bridge_vlan *v,
91a580c76dSNikolay Aleksandrov 				u8 state,
92a580c76dSNikolay Aleksandrov 				bool *changed,
93a580c76dSNikolay Aleksandrov 				struct netlink_ext_ack *extack)
94a580c76dSNikolay Aleksandrov {
95a580c76dSNikolay Aleksandrov 	struct net_bridge *br;
96a580c76dSNikolay Aleksandrov 
97a580c76dSNikolay Aleksandrov 	ASSERT_RTNL();
98a580c76dSNikolay Aleksandrov 
99a580c76dSNikolay Aleksandrov 	if (state > BR_STATE_BLOCKING) {
100a580c76dSNikolay Aleksandrov 		NL_SET_ERR_MSG_MOD(extack, "Invalid vlan state");
101a580c76dSNikolay Aleksandrov 		return -EINVAL;
102a580c76dSNikolay Aleksandrov 	}
103a580c76dSNikolay Aleksandrov 
104a580c76dSNikolay Aleksandrov 	if (br_vlan_is_brentry(v))
105a580c76dSNikolay Aleksandrov 		br = v->br;
106a580c76dSNikolay Aleksandrov 	else
107a580c76dSNikolay Aleksandrov 		br = v->port->br;
108a580c76dSNikolay Aleksandrov 
109a580c76dSNikolay Aleksandrov 	if (br->stp_enabled == BR_KERNEL_STP) {
110a580c76dSNikolay Aleksandrov 		NL_SET_ERR_MSG_MOD(extack, "Can't modify vlan state when using kernel STP");
111a580c76dSNikolay Aleksandrov 		return -EBUSY;
112a580c76dSNikolay Aleksandrov 	}
113a580c76dSNikolay Aleksandrov 
114ec7328b5STobias Waldekranz 	if (br_opt_get(br, BROPT_MST_ENABLED)) {
115ec7328b5STobias Waldekranz 		NL_SET_ERR_MSG_MOD(extack, "Can't modify vlan state directly when MST is enabled");
116ec7328b5STobias Waldekranz 		return -EBUSY;
117ec7328b5STobias Waldekranz 	}
118ec7328b5STobias Waldekranz 
119a580c76dSNikolay Aleksandrov 	if (v->state == state)
120a580c76dSNikolay Aleksandrov 		return 0;
121a580c76dSNikolay Aleksandrov 
122a580c76dSNikolay Aleksandrov 	if (v->vid == br_get_pvid(vg))
123a580c76dSNikolay Aleksandrov 		br_vlan_set_pvid_state(vg, state);
124a580c76dSNikolay Aleksandrov 
125a580c76dSNikolay Aleksandrov 	br_vlan_set_state(v, state);
126a580c76dSNikolay Aleksandrov 	*changed = true;
127a580c76dSNikolay Aleksandrov 
1287a53e718SNikolay Aleksandrov 	return 0;
1297a53e718SNikolay Aleksandrov }
130a5d29ae2SNikolay Aleksandrov 
131fa388f29SNikolay Aleksandrov static const struct nla_policy br_vlandb_tinfo_pol[BRIDGE_VLANDB_TINFO_MAX + 1] = {
132fa388f29SNikolay Aleksandrov 	[BRIDGE_VLANDB_TINFO_ID]	= { .type = NLA_U32 },
133c443758bSNikolay Aleksandrov 	[BRIDGE_VLANDB_TINFO_CMD]	= { .type = NLA_U32 },
134fa388f29SNikolay Aleksandrov };
135fa388f29SNikolay Aleksandrov 
br_vlan_modify_tunnel(const struct net_bridge_port * p,struct net_bridge_vlan * v,struct nlattr ** tb,bool * changed,struct netlink_ext_ack * extack)136569da082SNikolay Aleksandrov static int br_vlan_modify_tunnel(const struct net_bridge_port *p,
137569da082SNikolay Aleksandrov 				 struct net_bridge_vlan *v,
138569da082SNikolay Aleksandrov 				 struct nlattr **tb,
139569da082SNikolay Aleksandrov 				 bool *changed,
140569da082SNikolay Aleksandrov 				 struct netlink_ext_ack *extack)
141569da082SNikolay Aleksandrov {
142fa388f29SNikolay Aleksandrov 	struct nlattr *tun_tb[BRIDGE_VLANDB_TINFO_MAX + 1], *attr;
143569da082SNikolay Aleksandrov 	struct bridge_vlan_info *vinfo;
144c443758bSNikolay Aleksandrov 	u32 tun_id = 0;
145c443758bSNikolay Aleksandrov 	int cmd, err;
146569da082SNikolay Aleksandrov 
147569da082SNikolay Aleksandrov 	if (!p) {
148569da082SNikolay Aleksandrov 		NL_SET_ERR_MSG_MOD(extack, "Can't modify tunnel mapping of non-port vlans");
149569da082SNikolay Aleksandrov 		return -EINVAL;
150569da082SNikolay Aleksandrov 	}
151569da082SNikolay Aleksandrov 	if (!(p->flags & BR_VLAN_TUNNEL)) {
152569da082SNikolay Aleksandrov 		NL_SET_ERR_MSG_MOD(extack, "Port doesn't have tunnel flag set");
153569da082SNikolay Aleksandrov 		return -EINVAL;
154569da082SNikolay Aleksandrov 	}
155569da082SNikolay Aleksandrov 
156fa388f29SNikolay Aleksandrov 	attr = tb[BRIDGE_VLANDB_ENTRY_TUNNEL_INFO];
157fa388f29SNikolay Aleksandrov 	err = nla_parse_nested(tun_tb, BRIDGE_VLANDB_TINFO_MAX, attr,
158fa388f29SNikolay Aleksandrov 			       br_vlandb_tinfo_pol, extack);
159fa388f29SNikolay Aleksandrov 	if (err)
160fa388f29SNikolay Aleksandrov 		return err;
161fa388f29SNikolay Aleksandrov 
162c443758bSNikolay Aleksandrov 	if (!tun_tb[BRIDGE_VLANDB_TINFO_CMD]) {
163c443758bSNikolay Aleksandrov 		NL_SET_ERR_MSG_MOD(extack, "Missing tunnel command attribute");
164c443758bSNikolay Aleksandrov 		return -ENOENT;
165c443758bSNikolay Aleksandrov 	}
166c443758bSNikolay Aleksandrov 	cmd = nla_get_u32(tun_tb[BRIDGE_VLANDB_TINFO_CMD]);
167c443758bSNikolay Aleksandrov 	switch (cmd) {
168c443758bSNikolay Aleksandrov 	case RTM_SETLINK:
169fa388f29SNikolay Aleksandrov 		if (!tun_tb[BRIDGE_VLANDB_TINFO_ID]) {
170fa388f29SNikolay Aleksandrov 			NL_SET_ERR_MSG_MOD(extack, "Missing tunnel id attribute");
171fa388f29SNikolay Aleksandrov 			return -ENOENT;
172fa388f29SNikolay Aleksandrov 		}
173c443758bSNikolay Aleksandrov 		/* when working on vlan ranges this is the starting tunnel id */
174fa388f29SNikolay Aleksandrov 		tun_id = nla_get_u32(tun_tb[BRIDGE_VLANDB_TINFO_ID]);
175c443758bSNikolay Aleksandrov 		/* vlan info attr is guaranteed by br_vlan_rtm_process_one */
176c443758bSNikolay Aleksandrov 		vinfo = nla_data(tb[BRIDGE_VLANDB_ENTRY_INFO]);
177569da082SNikolay Aleksandrov 		/* tunnel ids are mapped to each vlan in increasing order,
178569da082SNikolay Aleksandrov 		 * the starting vlan is in BRIDGE_VLANDB_ENTRY_INFO and v is the
179569da082SNikolay Aleksandrov 		 * current vlan, so we compute: tun_id + v - vinfo->vid
180569da082SNikolay Aleksandrov 		 */
181569da082SNikolay Aleksandrov 		tun_id += v->vid - vinfo->vid;
182c443758bSNikolay Aleksandrov 		break;
183c443758bSNikolay Aleksandrov 	case RTM_DELLINK:
184c443758bSNikolay Aleksandrov 		break;
185c443758bSNikolay Aleksandrov 	default:
186c443758bSNikolay Aleksandrov 		NL_SET_ERR_MSG_MOD(extack, "Unsupported tunnel command");
187c443758bSNikolay Aleksandrov 		return -EINVAL;
188c443758bSNikolay Aleksandrov 	}
189569da082SNikolay Aleksandrov 
190c443758bSNikolay Aleksandrov 	return br_vlan_tunnel_info(p, cmd, v->vid, tun_id, changed);
191569da082SNikolay Aleksandrov }
192569da082SNikolay Aleksandrov 
br_vlan_process_one_opts(const struct net_bridge * br,const struct net_bridge_port * p,struct net_bridge_vlan_group * vg,struct net_bridge_vlan * v,struct nlattr ** tb,bool * changed,struct netlink_ext_ack * extack)193a5d29ae2SNikolay Aleksandrov static int br_vlan_process_one_opts(const struct net_bridge *br,
194a5d29ae2SNikolay Aleksandrov 				    const struct net_bridge_port *p,
195a5d29ae2SNikolay Aleksandrov 				    struct net_bridge_vlan_group *vg,
196a5d29ae2SNikolay Aleksandrov 				    struct net_bridge_vlan *v,
197a5d29ae2SNikolay Aleksandrov 				    struct nlattr **tb,
198a5d29ae2SNikolay Aleksandrov 				    bool *changed,
199a5d29ae2SNikolay Aleksandrov 				    struct netlink_ext_ack *extack)
200a5d29ae2SNikolay Aleksandrov {
201a580c76dSNikolay Aleksandrov 	int err;
202a580c76dSNikolay Aleksandrov 
203a5d29ae2SNikolay Aleksandrov 	*changed = false;
204a580c76dSNikolay Aleksandrov 	if (tb[BRIDGE_VLANDB_ENTRY_STATE]) {
205a580c76dSNikolay Aleksandrov 		u8 state = nla_get_u8(tb[BRIDGE_VLANDB_ENTRY_STATE]);
206a580c76dSNikolay Aleksandrov 
207a580c76dSNikolay Aleksandrov 		err = br_vlan_modify_state(vg, v, state, changed, extack);
208a580c76dSNikolay Aleksandrov 		if (err)
209a580c76dSNikolay Aleksandrov 			return err;
210a580c76dSNikolay Aleksandrov 	}
211fa388f29SNikolay Aleksandrov 	if (tb[BRIDGE_VLANDB_ENTRY_TUNNEL_INFO]) {
212569da082SNikolay Aleksandrov 		err = br_vlan_modify_tunnel(p, v, tb, changed, extack);
213569da082SNikolay Aleksandrov 		if (err)
214569da082SNikolay Aleksandrov 			return err;
215569da082SNikolay Aleksandrov 	}
216a580c76dSNikolay Aleksandrov 
2172796d846SNikolay Aleksandrov #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
2182796d846SNikolay Aleksandrov 	if (tb[BRIDGE_VLANDB_ENTRY_MCAST_ROUTER]) {
2192796d846SNikolay Aleksandrov 		u8 val;
2202796d846SNikolay Aleksandrov 
2212796d846SNikolay Aleksandrov 		val = nla_get_u8(tb[BRIDGE_VLANDB_ENTRY_MCAST_ROUTER]);
2222796d846SNikolay Aleksandrov 		err = br_multicast_set_vlan_router(v, val);
2232796d846SNikolay Aleksandrov 		if (err)
2242796d846SNikolay Aleksandrov 			return err;
2252796d846SNikolay Aleksandrov 		*changed = true;
2262796d846SNikolay Aleksandrov 	}
227a1aee20dSPetr Machata 	if (tb[BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS]) {
228a1aee20dSPetr Machata 		u32 val;
229a1aee20dSPetr Machata 
230a1aee20dSPetr Machata 		if (!p) {
231a1aee20dSPetr Machata 			NL_SET_ERR_MSG_MOD(extack, "Can't set mcast_max_groups for non-port vlans");
232a1aee20dSPetr Machata 			return -EINVAL;
233a1aee20dSPetr Machata 		}
234a1aee20dSPetr Machata 		if (br_multicast_port_ctx_vlan_disabled(&v->port_mcast_ctx)) {
235a1aee20dSPetr Machata 			NL_SET_ERR_MSG_MOD(extack, "Multicast snooping disabled on this VLAN");
236a1aee20dSPetr Machata 			return -EINVAL;
237a1aee20dSPetr Machata 		}
238a1aee20dSPetr Machata 
239a1aee20dSPetr Machata 		val = nla_get_u32(tb[BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS]);
240a1aee20dSPetr Machata 		br_multicast_ngroups_set_max(&v->port_mcast_ctx, val);
241a1aee20dSPetr Machata 		*changed = true;
242a1aee20dSPetr Machata 	}
2432796d846SNikolay Aleksandrov #endif
2442796d846SNikolay Aleksandrov 
245*83f6d600SIdo Schimmel 	if (tb[BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS]) {
246*83f6d600SIdo Schimmel 		bool enabled = v->priv_flags & BR_VLFLAG_NEIGH_SUPPRESS_ENABLED;
247*83f6d600SIdo Schimmel 		bool val = nla_get_u8(tb[BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS]);
248*83f6d600SIdo Schimmel 
249*83f6d600SIdo Schimmel 		if (!p) {
250*83f6d600SIdo Schimmel 			NL_SET_ERR_MSG_MOD(extack, "Can't set neigh_suppress for non-port vlans");
251*83f6d600SIdo Schimmel 			return -EINVAL;
252*83f6d600SIdo Schimmel 		}
253*83f6d600SIdo Schimmel 
254*83f6d600SIdo Schimmel 		if (val != enabled) {
255*83f6d600SIdo Schimmel 			v->priv_flags ^= BR_VLFLAG_NEIGH_SUPPRESS_ENABLED;
256*83f6d600SIdo Schimmel 			*changed = true;
257*83f6d600SIdo Schimmel 		}
258*83f6d600SIdo Schimmel 	}
259*83f6d600SIdo Schimmel 
260a5d29ae2SNikolay Aleksandrov 	return 0;
261a5d29ae2SNikolay Aleksandrov }
262a5d29ae2SNikolay Aleksandrov 
br_vlan_process_options(const struct net_bridge * br,const struct net_bridge_port * p,struct net_bridge_vlan * range_start,struct net_bridge_vlan * range_end,struct nlattr ** tb,struct netlink_ext_ack * extack)263a5d29ae2SNikolay Aleksandrov int br_vlan_process_options(const struct net_bridge *br,
264a5d29ae2SNikolay Aleksandrov 			    const struct net_bridge_port *p,
265a5d29ae2SNikolay Aleksandrov 			    struct net_bridge_vlan *range_start,
266a5d29ae2SNikolay Aleksandrov 			    struct net_bridge_vlan *range_end,
267a5d29ae2SNikolay Aleksandrov 			    struct nlattr **tb,
268a5d29ae2SNikolay Aleksandrov 			    struct netlink_ext_ack *extack)
269a5d29ae2SNikolay Aleksandrov {
270a5d29ae2SNikolay Aleksandrov 	struct net_bridge_vlan *v, *curr_start = NULL, *curr_end = NULL;
271a5d29ae2SNikolay Aleksandrov 	struct net_bridge_vlan_group *vg;
272a5d29ae2SNikolay Aleksandrov 	int vid, err = 0;
273a5d29ae2SNikolay Aleksandrov 	u16 pvid;
274a5d29ae2SNikolay Aleksandrov 
275a5d29ae2SNikolay Aleksandrov 	if (p)
276a5d29ae2SNikolay Aleksandrov 		vg = nbp_vlan_group(p);
277a5d29ae2SNikolay Aleksandrov 	else
278a5d29ae2SNikolay Aleksandrov 		vg = br_vlan_group(br);
279a5d29ae2SNikolay Aleksandrov 
280a5d29ae2SNikolay Aleksandrov 	if (!range_start || !br_vlan_should_use(range_start)) {
281a5d29ae2SNikolay Aleksandrov 		NL_SET_ERR_MSG_MOD(extack, "Vlan range start doesn't exist, can't process options");
282a5d29ae2SNikolay Aleksandrov 		return -ENOENT;
283a5d29ae2SNikolay Aleksandrov 	}
284a5d29ae2SNikolay Aleksandrov 	if (!range_end || !br_vlan_should_use(range_end)) {
285a5d29ae2SNikolay Aleksandrov 		NL_SET_ERR_MSG_MOD(extack, "Vlan range end doesn't exist, can't process options");
286a5d29ae2SNikolay Aleksandrov 		return -ENOENT;
287a5d29ae2SNikolay Aleksandrov 	}
288a5d29ae2SNikolay Aleksandrov 
289a5d29ae2SNikolay Aleksandrov 	pvid = br_get_pvid(vg);
290a5d29ae2SNikolay Aleksandrov 	for (vid = range_start->vid; vid <= range_end->vid; vid++) {
291a5d29ae2SNikolay Aleksandrov 		bool changed = false;
292a5d29ae2SNikolay Aleksandrov 
293a5d29ae2SNikolay Aleksandrov 		v = br_vlan_find(vg, vid);
294a5d29ae2SNikolay Aleksandrov 		if (!v || !br_vlan_should_use(v)) {
295a5d29ae2SNikolay Aleksandrov 			NL_SET_ERR_MSG_MOD(extack, "Vlan in range doesn't exist, can't process options");
296a5d29ae2SNikolay Aleksandrov 			err = -ENOENT;
297a5d29ae2SNikolay Aleksandrov 			break;
298a5d29ae2SNikolay Aleksandrov 		}
299a5d29ae2SNikolay Aleksandrov 
300a5d29ae2SNikolay Aleksandrov 		err = br_vlan_process_one_opts(br, p, vg, v, tb, &changed,
301a5d29ae2SNikolay Aleksandrov 					       extack);
302a5d29ae2SNikolay Aleksandrov 		if (err)
303a5d29ae2SNikolay Aleksandrov 			break;
304a5d29ae2SNikolay Aleksandrov 
305a5d29ae2SNikolay Aleksandrov 		if (changed) {
306a5d29ae2SNikolay Aleksandrov 			/* vlan options changed, check for range */
307a5d29ae2SNikolay Aleksandrov 			if (!curr_start) {
308a5d29ae2SNikolay Aleksandrov 				curr_start = v;
309a5d29ae2SNikolay Aleksandrov 				curr_end = v;
310a5d29ae2SNikolay Aleksandrov 				continue;
311a5d29ae2SNikolay Aleksandrov 			}
312a5d29ae2SNikolay Aleksandrov 
313a5d29ae2SNikolay Aleksandrov 			if (v->vid == pvid ||
314a5d29ae2SNikolay Aleksandrov 			    !br_vlan_can_enter_range(v, curr_end)) {
315a5d29ae2SNikolay Aleksandrov 				br_vlan_notify(br, p, curr_start->vid,
316a5d29ae2SNikolay Aleksandrov 					       curr_end->vid, RTM_NEWVLAN);
317a5d29ae2SNikolay Aleksandrov 				curr_start = v;
318a5d29ae2SNikolay Aleksandrov 			}
319a5d29ae2SNikolay Aleksandrov 			curr_end = v;
320a5d29ae2SNikolay Aleksandrov 		} else {
321a5d29ae2SNikolay Aleksandrov 			/* nothing changed and nothing to notify yet */
322a5d29ae2SNikolay Aleksandrov 			if (!curr_start)
323a5d29ae2SNikolay Aleksandrov 				continue;
324a5d29ae2SNikolay Aleksandrov 
325a5d29ae2SNikolay Aleksandrov 			br_vlan_notify(br, p, curr_start->vid, curr_end->vid,
326a5d29ae2SNikolay Aleksandrov 				       RTM_NEWVLAN);
327a5d29ae2SNikolay Aleksandrov 			curr_start = NULL;
328a5d29ae2SNikolay Aleksandrov 			curr_end = NULL;
329a5d29ae2SNikolay Aleksandrov 		}
330a5d29ae2SNikolay Aleksandrov 	}
331a5d29ae2SNikolay Aleksandrov 	if (curr_start)
332a5d29ae2SNikolay Aleksandrov 		br_vlan_notify(br, p, curr_start->vid, curr_end->vid,
333a5d29ae2SNikolay Aleksandrov 			       RTM_NEWVLAN);
334a5d29ae2SNikolay Aleksandrov 
335a5d29ae2SNikolay Aleksandrov 	return err;
336a5d29ae2SNikolay Aleksandrov }
33747ecd2dbSNikolay Aleksandrov 
br_vlan_global_opts_can_enter_range(const struct net_bridge_vlan * v_curr,const struct net_bridge_vlan * r_end)338743a53d9SNikolay Aleksandrov bool br_vlan_global_opts_can_enter_range(const struct net_bridge_vlan *v_curr,
339743a53d9SNikolay Aleksandrov 					 const struct net_bridge_vlan *r_end)
340743a53d9SNikolay Aleksandrov {
3419dee572cSNikolay Aleksandrov 	return v_curr->vid - r_end->vid == 1 &&
3428c678d60STobias Waldekranz 		v_curr->msti == r_end->msti &&
3439dee572cSNikolay Aleksandrov 	       ((v_curr->priv_flags ^ r_end->priv_flags) &
344df271cd6SNikolay Aleksandrov 		BR_VLFLAG_GLOBAL_MCAST_ENABLED) == 0 &&
345df271cd6SNikolay Aleksandrov 		br_multicast_ctx_options_equal(&v_curr->br_mcast_ctx,
346df271cd6SNikolay Aleksandrov 					       &r_end->br_mcast_ctx);
347743a53d9SNikolay Aleksandrov }
348743a53d9SNikolay Aleksandrov 
br_vlan_global_opts_fill(struct sk_buff * skb,u16 vid,u16 vid_range,const struct net_bridge_vlan * v_opts)349743a53d9SNikolay Aleksandrov bool br_vlan_global_opts_fill(struct sk_buff *skb, u16 vid, u16 vid_range,
350743a53d9SNikolay Aleksandrov 			      const struct net_bridge_vlan *v_opts)
351743a53d9SNikolay Aleksandrov {
352dc002875SNikolay Aleksandrov 	struct nlattr *nest2 __maybe_unused;
35377f6ababSNikolay Aleksandrov 	u64 clockval __maybe_unused;
354743a53d9SNikolay Aleksandrov 	struct nlattr *nest;
355743a53d9SNikolay Aleksandrov 
356743a53d9SNikolay Aleksandrov 	nest = nla_nest_start(skb, BRIDGE_VLANDB_GLOBAL_OPTIONS);
357743a53d9SNikolay Aleksandrov 	if (!nest)
358743a53d9SNikolay Aleksandrov 		return false;
359743a53d9SNikolay Aleksandrov 
360743a53d9SNikolay Aleksandrov 	if (nla_put_u16(skb, BRIDGE_VLANDB_GOPTS_ID, vid))
361743a53d9SNikolay Aleksandrov 		goto out_err;
362743a53d9SNikolay Aleksandrov 
363743a53d9SNikolay Aleksandrov 	if (vid_range && vid < vid_range &&
364743a53d9SNikolay Aleksandrov 	    nla_put_u16(skb, BRIDGE_VLANDB_GOPTS_RANGE, vid_range))
365743a53d9SNikolay Aleksandrov 		goto out_err;
366743a53d9SNikolay Aleksandrov 
3679dee572cSNikolay Aleksandrov #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
3689dee572cSNikolay Aleksandrov 	if (nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING,
369df271cd6SNikolay Aleksandrov 		       !!(v_opts->priv_flags & BR_VLFLAG_GLOBAL_MCAST_ENABLED)) ||
370df271cd6SNikolay Aleksandrov 	    nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION,
371931ba87dSNikolay Aleksandrov 		       v_opts->br_mcast_ctx.multicast_igmp_version) ||
372931ba87dSNikolay Aleksandrov 	    nla_put_u32(skb, BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT,
37350725f6eSNikolay Aleksandrov 			v_opts->br_mcast_ctx.multicast_last_member_count) ||
37450725f6eSNikolay Aleksandrov 	    nla_put_u32(skb, BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT,
37562938182SNikolay Aleksandrov 			v_opts->br_mcast_ctx.multicast_startup_query_count) ||
37662938182SNikolay Aleksandrov 	    nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_QUERIER,
377a97df080SNikolay Aleksandrov 		       v_opts->br_mcast_ctx.multicast_querier) ||
378ddc649d1SNikolay Aleksandrov 	    br_multicast_dump_querier_state(skb, &v_opts->br_mcast_ctx,
379ddc649d1SNikolay Aleksandrov 					    BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_STATE))
3809dee572cSNikolay Aleksandrov 		goto out_err;
381df271cd6SNikolay Aleksandrov 
38277f6ababSNikolay Aleksandrov 	clockval = jiffies_to_clock_t(v_opts->br_mcast_ctx.multicast_last_member_interval);
38377f6ababSNikolay Aleksandrov 	if (nla_put_u64_64bit(skb, BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL,
38477f6ababSNikolay Aleksandrov 			      clockval, BRIDGE_VLANDB_GOPTS_PAD))
38577f6ababSNikolay Aleksandrov 		goto out_err;
3862da0aea2SNikolay Aleksandrov 	clockval = jiffies_to_clock_t(v_opts->br_mcast_ctx.multicast_membership_interval);
3872da0aea2SNikolay Aleksandrov 	if (nla_put_u64_64bit(skb, BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL,
3882da0aea2SNikolay Aleksandrov 			      clockval, BRIDGE_VLANDB_GOPTS_PAD))
3892da0aea2SNikolay Aleksandrov 		goto out_err;
390cd9269d4SNikolay Aleksandrov 	clockval = jiffies_to_clock_t(v_opts->br_mcast_ctx.multicast_querier_interval);
391cd9269d4SNikolay Aleksandrov 	if (nla_put_u64_64bit(skb, BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL,
392cd9269d4SNikolay Aleksandrov 			      clockval, BRIDGE_VLANDB_GOPTS_PAD))
393cd9269d4SNikolay Aleksandrov 		goto out_err;
394d6c08abaSNikolay Aleksandrov 	clockval = jiffies_to_clock_t(v_opts->br_mcast_ctx.multicast_query_interval);
395d6c08abaSNikolay Aleksandrov 	if (nla_put_u64_64bit(skb, BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL,
396d6c08abaSNikolay Aleksandrov 			      clockval, BRIDGE_VLANDB_GOPTS_PAD))
397d6c08abaSNikolay Aleksandrov 		goto out_err;
39842521450SNikolay Aleksandrov 	clockval = jiffies_to_clock_t(v_opts->br_mcast_ctx.multicast_query_response_interval);
39942521450SNikolay Aleksandrov 	if (nla_put_u64_64bit(skb, BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL,
40042521450SNikolay Aleksandrov 			      clockval, BRIDGE_VLANDB_GOPTS_PAD))
40142521450SNikolay Aleksandrov 		goto out_err;
402941121eeSNikolay Aleksandrov 	clockval = jiffies_to_clock_t(v_opts->br_mcast_ctx.multicast_startup_query_interval);
403941121eeSNikolay Aleksandrov 	if (nla_put_u64_64bit(skb, BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL,
404941121eeSNikolay Aleksandrov 			      clockval, BRIDGE_VLANDB_GOPTS_PAD))
405941121eeSNikolay Aleksandrov 		goto out_err;
40677f6ababSNikolay Aleksandrov 
407dc002875SNikolay Aleksandrov 	if (br_rports_have_mc_router(&v_opts->br_mcast_ctx)) {
408dc002875SNikolay Aleksandrov 		nest2 = nla_nest_start(skb,
409dc002875SNikolay Aleksandrov 				       BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS);
410dc002875SNikolay Aleksandrov 		if (!nest2)
411dc002875SNikolay Aleksandrov 			goto out_err;
412dc002875SNikolay Aleksandrov 
413dc002875SNikolay Aleksandrov 		rcu_read_lock();
414dc002875SNikolay Aleksandrov 		if (br_rports_fill_info(skb, &v_opts->br_mcast_ctx)) {
415dc002875SNikolay Aleksandrov 			rcu_read_unlock();
416dc002875SNikolay Aleksandrov 			nla_nest_cancel(skb, nest2);
417dc002875SNikolay Aleksandrov 			goto out_err;
418dc002875SNikolay Aleksandrov 		}
419dc002875SNikolay Aleksandrov 		rcu_read_unlock();
420dc002875SNikolay Aleksandrov 
421dc002875SNikolay Aleksandrov 		nla_nest_end(skb, nest2);
422dc002875SNikolay Aleksandrov 	}
423dc002875SNikolay Aleksandrov 
424df271cd6SNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6)
425df271cd6SNikolay Aleksandrov 	if (nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION,
426df271cd6SNikolay Aleksandrov 		       v_opts->br_mcast_ctx.multicast_mld_version))
427df271cd6SNikolay Aleksandrov 		goto out_err;
428df271cd6SNikolay Aleksandrov #endif
4299dee572cSNikolay Aleksandrov #endif
4309dee572cSNikolay Aleksandrov 
4318c678d60STobias Waldekranz 	if (nla_put_u16(skb, BRIDGE_VLANDB_GOPTS_MSTI, v_opts->msti))
4328c678d60STobias Waldekranz 		goto out_err;
4338c678d60STobias Waldekranz 
434743a53d9SNikolay Aleksandrov 	nla_nest_end(skb, nest);
435743a53d9SNikolay Aleksandrov 
436743a53d9SNikolay Aleksandrov 	return true;
437743a53d9SNikolay Aleksandrov 
438743a53d9SNikolay Aleksandrov out_err:
439743a53d9SNikolay Aleksandrov 	nla_nest_cancel(skb, nest);
440743a53d9SNikolay Aleksandrov 	return false;
441743a53d9SNikolay Aleksandrov }
442743a53d9SNikolay Aleksandrov 
rtnl_vlan_global_opts_nlmsg_size(const struct net_bridge_vlan * v)44305d6f38eSNikolay Aleksandrov static size_t rtnl_vlan_global_opts_nlmsg_size(const struct net_bridge_vlan *v)
4449aba624dSNikolay Aleksandrov {
4459aba624dSNikolay Aleksandrov 	return NLMSG_ALIGN(sizeof(struct br_vlan_msg))
4469aba624dSNikolay Aleksandrov 		+ nla_total_size(0) /* BRIDGE_VLANDB_GLOBAL_OPTIONS */
4479aba624dSNikolay Aleksandrov 		+ nla_total_size(sizeof(u16)) /* BRIDGE_VLANDB_GOPTS_ID */
4489dee572cSNikolay Aleksandrov #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
4499dee572cSNikolay Aleksandrov 		+ nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING */
450df271cd6SNikolay Aleksandrov 		+ nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION */
451df271cd6SNikolay Aleksandrov 		+ nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION */
452931ba87dSNikolay Aleksandrov 		+ nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT */
45350725f6eSNikolay Aleksandrov 		+ nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT */
45477f6ababSNikolay Aleksandrov 		+ nla_total_size(sizeof(u64)) /* BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL */
4552da0aea2SNikolay Aleksandrov 		+ nla_total_size(sizeof(u64)) /* BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL */
456cd9269d4SNikolay Aleksandrov 		+ nla_total_size(sizeof(u64)) /* BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL */
457d6c08abaSNikolay Aleksandrov 		+ nla_total_size(sizeof(u64)) /* BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL */
45842521450SNikolay Aleksandrov 		+ nla_total_size(sizeof(u64)) /* BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL */
459941121eeSNikolay Aleksandrov 		+ nla_total_size(sizeof(u64)) /* BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL */
46062938182SNikolay Aleksandrov 		+ nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_QUERIER */
461ddc649d1SNikolay Aleksandrov 		+ br_multicast_querier_state_size() /* BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_STATE */
46205d6f38eSNikolay Aleksandrov 		+ nla_total_size(0) /* BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS */
46305d6f38eSNikolay Aleksandrov 		+ br_rports_size(&v->br_mcast_ctx) /* BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS */
4649dee572cSNikolay Aleksandrov #endif
4658c678d60STobias Waldekranz 		+ nla_total_size(sizeof(u16)) /* BRIDGE_VLANDB_GOPTS_MSTI */
4669aba624dSNikolay Aleksandrov 		+ nla_total_size(sizeof(u16)); /* BRIDGE_VLANDB_GOPTS_RANGE */
4679aba624dSNikolay Aleksandrov }
4689aba624dSNikolay Aleksandrov 
br_vlan_global_opts_notify(const struct net_bridge * br,u16 vid,u16 vid_range)4699aba624dSNikolay Aleksandrov static void br_vlan_global_opts_notify(const struct net_bridge *br,
4709aba624dSNikolay Aleksandrov 				       u16 vid, u16 vid_range)
4719aba624dSNikolay Aleksandrov {
4729aba624dSNikolay Aleksandrov 	struct net_bridge_vlan *v;
4739aba624dSNikolay Aleksandrov 	struct br_vlan_msg *bvm;
4749aba624dSNikolay Aleksandrov 	struct nlmsghdr *nlh;
4759aba624dSNikolay Aleksandrov 	struct sk_buff *skb;
4769aba624dSNikolay Aleksandrov 	int err = -ENOBUFS;
4779aba624dSNikolay Aleksandrov 
4789aba624dSNikolay Aleksandrov 	/* right now notifications are done only with rtnl held */
4799aba624dSNikolay Aleksandrov 	ASSERT_RTNL();
4809aba624dSNikolay Aleksandrov 
48105d6f38eSNikolay Aleksandrov 	/* need to find the vlan due to flags/options */
48205d6f38eSNikolay Aleksandrov 	v = br_vlan_find(br_vlan_group(br), vid);
48305d6f38eSNikolay Aleksandrov 	if (!v)
48405d6f38eSNikolay Aleksandrov 		return;
48505d6f38eSNikolay Aleksandrov 
48605d6f38eSNikolay Aleksandrov 	skb = nlmsg_new(rtnl_vlan_global_opts_nlmsg_size(v), GFP_KERNEL);
4879aba624dSNikolay Aleksandrov 	if (!skb)
4889aba624dSNikolay Aleksandrov 		goto out_err;
4899aba624dSNikolay Aleksandrov 
4909aba624dSNikolay Aleksandrov 	err = -EMSGSIZE;
4919aba624dSNikolay Aleksandrov 	nlh = nlmsg_put(skb, 0, 0, RTM_NEWVLAN, sizeof(*bvm), 0);
4929aba624dSNikolay Aleksandrov 	if (!nlh)
4939aba624dSNikolay Aleksandrov 		goto out_err;
4949aba624dSNikolay Aleksandrov 	bvm = nlmsg_data(nlh);
4959aba624dSNikolay Aleksandrov 	memset(bvm, 0, sizeof(*bvm));
4969aba624dSNikolay Aleksandrov 	bvm->family = AF_BRIDGE;
4979aba624dSNikolay Aleksandrov 	bvm->ifindex = br->dev->ifindex;
4989aba624dSNikolay Aleksandrov 
4999aba624dSNikolay Aleksandrov 	if (!br_vlan_global_opts_fill(skb, vid, vid_range, v))
5009aba624dSNikolay Aleksandrov 		goto out_err;
5019aba624dSNikolay Aleksandrov 
5029aba624dSNikolay Aleksandrov 	nlmsg_end(skb, nlh);
5039aba624dSNikolay Aleksandrov 	rtnl_notify(skb, dev_net(br->dev), 0, RTNLGRP_BRVLAN, NULL, GFP_KERNEL);
5049aba624dSNikolay Aleksandrov 	return;
5059aba624dSNikolay Aleksandrov 
5069aba624dSNikolay Aleksandrov out_err:
5079aba624dSNikolay Aleksandrov 	rtnl_set_sk_err(dev_net(br->dev), RTNLGRP_BRVLAN, err);
5089aba624dSNikolay Aleksandrov 	kfree_skb(skb);
5099aba624dSNikolay Aleksandrov }
5109aba624dSNikolay Aleksandrov 
br_vlan_process_global_one_opts(const struct net_bridge * br,struct net_bridge_vlan_group * vg,struct net_bridge_vlan * v,struct nlattr ** tb,bool * changed,struct netlink_ext_ack * extack)51147ecd2dbSNikolay Aleksandrov static int br_vlan_process_global_one_opts(const struct net_bridge *br,
51247ecd2dbSNikolay Aleksandrov 					   struct net_bridge_vlan_group *vg,
51347ecd2dbSNikolay Aleksandrov 					   struct net_bridge_vlan *v,
51447ecd2dbSNikolay Aleksandrov 					   struct nlattr **tb,
51547ecd2dbSNikolay Aleksandrov 					   bool *changed,
51647ecd2dbSNikolay Aleksandrov 					   struct netlink_ext_ack *extack)
51747ecd2dbSNikolay Aleksandrov {
518df271cd6SNikolay Aleksandrov 	int err __maybe_unused;
519df271cd6SNikolay Aleksandrov 
52047ecd2dbSNikolay Aleksandrov 	*changed = false;
5219dee572cSNikolay Aleksandrov #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
5229dee572cSNikolay Aleksandrov 	if (tb[BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING]) {
5239dee572cSNikolay Aleksandrov 		u8 mc_snooping;
5249dee572cSNikolay Aleksandrov 
5259dee572cSNikolay Aleksandrov 		mc_snooping = nla_get_u8(tb[BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING]);
5269dee572cSNikolay Aleksandrov 		if (br_multicast_toggle_global_vlan(v, !!mc_snooping))
5279dee572cSNikolay Aleksandrov 			*changed = true;
5289dee572cSNikolay Aleksandrov 	}
529df271cd6SNikolay Aleksandrov 	if (tb[BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION]) {
530df271cd6SNikolay Aleksandrov 		u8 ver;
531df271cd6SNikolay Aleksandrov 
532df271cd6SNikolay Aleksandrov 		ver = nla_get_u8(tb[BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION]);
533df271cd6SNikolay Aleksandrov 		err = br_multicast_set_igmp_version(&v->br_mcast_ctx, ver);
534df271cd6SNikolay Aleksandrov 		if (err)
535df271cd6SNikolay Aleksandrov 			return err;
536df271cd6SNikolay Aleksandrov 		*changed = true;
537df271cd6SNikolay Aleksandrov 	}
538931ba87dSNikolay Aleksandrov 	if (tb[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT]) {
539931ba87dSNikolay Aleksandrov 		u32 cnt;
540931ba87dSNikolay Aleksandrov 
541931ba87dSNikolay Aleksandrov 		cnt = nla_get_u32(tb[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT]);
542931ba87dSNikolay Aleksandrov 		v->br_mcast_ctx.multicast_last_member_count = cnt;
543931ba87dSNikolay Aleksandrov 		*changed = true;
544931ba87dSNikolay Aleksandrov 	}
54550725f6eSNikolay Aleksandrov 	if (tb[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT]) {
54650725f6eSNikolay Aleksandrov 		u32 cnt;
54750725f6eSNikolay Aleksandrov 
54850725f6eSNikolay Aleksandrov 		cnt = nla_get_u32(tb[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT]);
54950725f6eSNikolay Aleksandrov 		v->br_mcast_ctx.multicast_startup_query_count = cnt;
55050725f6eSNikolay Aleksandrov 		*changed = true;
55150725f6eSNikolay Aleksandrov 	}
55277f6ababSNikolay Aleksandrov 	if (tb[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL]) {
55377f6ababSNikolay Aleksandrov 		u64 val;
55477f6ababSNikolay Aleksandrov 
55577f6ababSNikolay Aleksandrov 		val = nla_get_u64(tb[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL]);
55677f6ababSNikolay Aleksandrov 		v->br_mcast_ctx.multicast_last_member_interval = clock_t_to_jiffies(val);
55777f6ababSNikolay Aleksandrov 		*changed = true;
55877f6ababSNikolay Aleksandrov 	}
5592da0aea2SNikolay Aleksandrov 	if (tb[BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL]) {
5602da0aea2SNikolay Aleksandrov 		u64 val;
5612da0aea2SNikolay Aleksandrov 
5622da0aea2SNikolay Aleksandrov 		val = nla_get_u64(tb[BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL]);
5632da0aea2SNikolay Aleksandrov 		v->br_mcast_ctx.multicast_membership_interval = clock_t_to_jiffies(val);
5642da0aea2SNikolay Aleksandrov 		*changed = true;
5652da0aea2SNikolay Aleksandrov 	}
566cd9269d4SNikolay Aleksandrov 	if (tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL]) {
567cd9269d4SNikolay Aleksandrov 		u64 val;
568cd9269d4SNikolay Aleksandrov 
569cd9269d4SNikolay Aleksandrov 		val = nla_get_u64(tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL]);
570cd9269d4SNikolay Aleksandrov 		v->br_mcast_ctx.multicast_querier_interval = clock_t_to_jiffies(val);
571cd9269d4SNikolay Aleksandrov 		*changed = true;
572cd9269d4SNikolay Aleksandrov 	}
573d6c08abaSNikolay Aleksandrov 	if (tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL]) {
574d6c08abaSNikolay Aleksandrov 		u64 val;
575d6c08abaSNikolay Aleksandrov 
576d6c08abaSNikolay Aleksandrov 		val = nla_get_u64(tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL]);
57799b40610SNikolay Aleksandrov 		br_multicast_set_query_intvl(&v->br_mcast_ctx, val);
578d6c08abaSNikolay Aleksandrov 		*changed = true;
579d6c08abaSNikolay Aleksandrov 	}
58042521450SNikolay Aleksandrov 	if (tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL]) {
58142521450SNikolay Aleksandrov 		u64 val;
58242521450SNikolay Aleksandrov 
58342521450SNikolay Aleksandrov 		val = nla_get_u64(tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL]);
58442521450SNikolay Aleksandrov 		v->br_mcast_ctx.multicast_query_response_interval = clock_t_to_jiffies(val);
58542521450SNikolay Aleksandrov 		*changed = true;
58642521450SNikolay Aleksandrov 	}
587941121eeSNikolay Aleksandrov 	if (tb[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL]) {
588941121eeSNikolay Aleksandrov 		u64 val;
589941121eeSNikolay Aleksandrov 
590941121eeSNikolay Aleksandrov 		val = nla_get_u64(tb[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL]);
591f83a112bSNikolay Aleksandrov 		br_multicast_set_startup_query_intvl(&v->br_mcast_ctx, val);
592941121eeSNikolay Aleksandrov 		*changed = true;
593941121eeSNikolay Aleksandrov 	}
59462938182SNikolay Aleksandrov 	if (tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER]) {
59562938182SNikolay Aleksandrov 		u8 val;
59662938182SNikolay Aleksandrov 
59762938182SNikolay Aleksandrov 		val = nla_get_u8(tb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER]);
59862938182SNikolay Aleksandrov 		err = br_multicast_set_querier(&v->br_mcast_ctx, val);
59962938182SNikolay Aleksandrov 		if (err)
60062938182SNikolay Aleksandrov 			return err;
60162938182SNikolay Aleksandrov 		*changed = true;
60262938182SNikolay Aleksandrov 	}
603df271cd6SNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6)
604df271cd6SNikolay Aleksandrov 	if (tb[BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION]) {
605df271cd6SNikolay Aleksandrov 		u8 ver;
606df271cd6SNikolay Aleksandrov 
607df271cd6SNikolay Aleksandrov 		ver = nla_get_u8(tb[BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION]);
608df271cd6SNikolay Aleksandrov 		err = br_multicast_set_mld_version(&v->br_mcast_ctx, ver);
609df271cd6SNikolay Aleksandrov 		if (err)
610df271cd6SNikolay Aleksandrov 			return err;
611df271cd6SNikolay Aleksandrov 		*changed = true;
612df271cd6SNikolay Aleksandrov 	}
613df271cd6SNikolay Aleksandrov #endif
6149dee572cSNikolay Aleksandrov #endif
6158c678d60STobias Waldekranz 	if (tb[BRIDGE_VLANDB_GOPTS_MSTI]) {
6168c678d60STobias Waldekranz 		u16 msti;
6178c678d60STobias Waldekranz 
6188c678d60STobias Waldekranz 		msti = nla_get_u16(tb[BRIDGE_VLANDB_GOPTS_MSTI]);
6198c678d60STobias Waldekranz 		err = br_mst_vlan_set_msti(v, msti);
6208c678d60STobias Waldekranz 		if (err)
6218c678d60STobias Waldekranz 			return err;
6228c678d60STobias Waldekranz 		*changed = true;
6238c678d60STobias Waldekranz 	}
6249dee572cSNikolay Aleksandrov 
62547ecd2dbSNikolay Aleksandrov 	return 0;
62647ecd2dbSNikolay Aleksandrov }
62747ecd2dbSNikolay Aleksandrov 
62847ecd2dbSNikolay Aleksandrov static const struct nla_policy br_vlan_db_gpol[BRIDGE_VLANDB_GOPTS_MAX + 1] = {
62947ecd2dbSNikolay Aleksandrov 	[BRIDGE_VLANDB_GOPTS_ID]	= { .type = NLA_U16 },
63047ecd2dbSNikolay Aleksandrov 	[BRIDGE_VLANDB_GOPTS_RANGE]	= { .type = NLA_U16 },
6319dee572cSNikolay Aleksandrov 	[BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING]	= { .type = NLA_U8 },
632df271cd6SNikolay Aleksandrov 	[BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION]	= { .type = NLA_U8 },
633d6c08abaSNikolay Aleksandrov 	[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL]	= { .type = NLA_U64 },
63462938182SNikolay Aleksandrov 	[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER]	= { .type = NLA_U8 },
635df271cd6SNikolay Aleksandrov 	[BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION]	= { .type = NLA_U8 },
636931ba87dSNikolay Aleksandrov 	[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT]	= { .type = NLA_U32 },
63750725f6eSNikolay Aleksandrov 	[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT]	= { .type = NLA_U32 },
63877f6ababSNikolay Aleksandrov 	[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL]	= { .type = NLA_U64 },
6392da0aea2SNikolay Aleksandrov 	[BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL]	= { .type = NLA_U64 },
640cd9269d4SNikolay Aleksandrov 	[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL]	= { .type = NLA_U64 },
641941121eeSNikolay Aleksandrov 	[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL]	= { .type = NLA_U64 },
64242521450SNikolay Aleksandrov 	[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL] = { .type = NLA_U64 },
6438c678d60STobias Waldekranz 	[BRIDGE_VLANDB_GOPTS_MSTI] = NLA_POLICY_MAX(NLA_U16, VLAN_N_VID - 1),
64447ecd2dbSNikolay Aleksandrov };
64547ecd2dbSNikolay Aleksandrov 
br_vlan_rtm_process_global_options(struct net_device * dev,const struct nlattr * attr,int cmd,struct netlink_ext_ack * extack)64647ecd2dbSNikolay Aleksandrov int br_vlan_rtm_process_global_options(struct net_device *dev,
64747ecd2dbSNikolay Aleksandrov 				       const struct nlattr *attr,
64847ecd2dbSNikolay Aleksandrov 				       int cmd,
64947ecd2dbSNikolay Aleksandrov 				       struct netlink_ext_ack *extack)
65047ecd2dbSNikolay Aleksandrov {
6519aba624dSNikolay Aleksandrov 	struct net_bridge_vlan *v, *curr_start = NULL, *curr_end = NULL;
65247ecd2dbSNikolay Aleksandrov 	struct nlattr *tb[BRIDGE_VLANDB_GOPTS_MAX + 1];
65347ecd2dbSNikolay Aleksandrov 	struct net_bridge_vlan_group *vg;
65447ecd2dbSNikolay Aleksandrov 	u16 vid, vid_range = 0;
65547ecd2dbSNikolay Aleksandrov 	struct net_bridge *br;
65647ecd2dbSNikolay Aleksandrov 	int err = 0;
65747ecd2dbSNikolay Aleksandrov 
65847ecd2dbSNikolay Aleksandrov 	if (cmd != RTM_NEWVLAN) {
65947ecd2dbSNikolay Aleksandrov 		NL_SET_ERR_MSG_MOD(extack, "Global vlan options support only set operation");
66047ecd2dbSNikolay Aleksandrov 		return -EINVAL;
66147ecd2dbSNikolay Aleksandrov 	}
66247ecd2dbSNikolay Aleksandrov 	if (!netif_is_bridge_master(dev)) {
66347ecd2dbSNikolay Aleksandrov 		NL_SET_ERR_MSG_MOD(extack, "Global vlan options can only be set on bridge device");
66447ecd2dbSNikolay Aleksandrov 		return -EINVAL;
66547ecd2dbSNikolay Aleksandrov 	}
66647ecd2dbSNikolay Aleksandrov 	br = netdev_priv(dev);
66747ecd2dbSNikolay Aleksandrov 	vg = br_vlan_group(br);
66847ecd2dbSNikolay Aleksandrov 	if (WARN_ON(!vg))
66947ecd2dbSNikolay Aleksandrov 		return -ENODEV;
67047ecd2dbSNikolay Aleksandrov 
67147ecd2dbSNikolay Aleksandrov 	err = nla_parse_nested(tb, BRIDGE_VLANDB_GOPTS_MAX, attr,
67247ecd2dbSNikolay Aleksandrov 			       br_vlan_db_gpol, extack);
67347ecd2dbSNikolay Aleksandrov 	if (err)
67447ecd2dbSNikolay Aleksandrov 		return err;
67547ecd2dbSNikolay Aleksandrov 
67647ecd2dbSNikolay Aleksandrov 	if (!tb[BRIDGE_VLANDB_GOPTS_ID]) {
67747ecd2dbSNikolay Aleksandrov 		NL_SET_ERR_MSG_MOD(extack, "Missing vlan entry id");
67847ecd2dbSNikolay Aleksandrov 		return -EINVAL;
67947ecd2dbSNikolay Aleksandrov 	}
68047ecd2dbSNikolay Aleksandrov 	vid = nla_get_u16(tb[BRIDGE_VLANDB_GOPTS_ID]);
68147ecd2dbSNikolay Aleksandrov 	if (!br_vlan_valid_id(vid, extack))
68247ecd2dbSNikolay Aleksandrov 		return -EINVAL;
68347ecd2dbSNikolay Aleksandrov 
68447ecd2dbSNikolay Aleksandrov 	if (tb[BRIDGE_VLANDB_GOPTS_RANGE]) {
68547ecd2dbSNikolay Aleksandrov 		vid_range = nla_get_u16(tb[BRIDGE_VLANDB_GOPTS_RANGE]);
68647ecd2dbSNikolay Aleksandrov 		if (!br_vlan_valid_id(vid_range, extack))
68747ecd2dbSNikolay Aleksandrov 			return -EINVAL;
68847ecd2dbSNikolay Aleksandrov 		if (vid >= vid_range) {
68947ecd2dbSNikolay Aleksandrov 			NL_SET_ERR_MSG_MOD(extack, "End vlan id is less than or equal to start vlan id");
69047ecd2dbSNikolay Aleksandrov 			return -EINVAL;
69147ecd2dbSNikolay Aleksandrov 		}
69247ecd2dbSNikolay Aleksandrov 	} else {
69347ecd2dbSNikolay Aleksandrov 		vid_range = vid;
69447ecd2dbSNikolay Aleksandrov 	}
69547ecd2dbSNikolay Aleksandrov 
69647ecd2dbSNikolay Aleksandrov 	for (; vid <= vid_range; vid++) {
69747ecd2dbSNikolay Aleksandrov 		bool changed = false;
69847ecd2dbSNikolay Aleksandrov 
69947ecd2dbSNikolay Aleksandrov 		v = br_vlan_find(vg, vid);
70047ecd2dbSNikolay Aleksandrov 		if (!v) {
70147ecd2dbSNikolay Aleksandrov 			NL_SET_ERR_MSG_MOD(extack, "Vlan in range doesn't exist, can't process global options");
70247ecd2dbSNikolay Aleksandrov 			err = -ENOENT;
70347ecd2dbSNikolay Aleksandrov 			break;
70447ecd2dbSNikolay Aleksandrov 		}
70547ecd2dbSNikolay Aleksandrov 
70647ecd2dbSNikolay Aleksandrov 		err = br_vlan_process_global_one_opts(br, vg, v, tb, &changed,
70747ecd2dbSNikolay Aleksandrov 						      extack);
70847ecd2dbSNikolay Aleksandrov 		if (err)
70947ecd2dbSNikolay Aleksandrov 			break;
7109aba624dSNikolay Aleksandrov 
7119aba624dSNikolay Aleksandrov 		if (changed) {
7129aba624dSNikolay Aleksandrov 			/* vlan options changed, check for range */
7139aba624dSNikolay Aleksandrov 			if (!curr_start) {
7149aba624dSNikolay Aleksandrov 				curr_start = v;
7159aba624dSNikolay Aleksandrov 				curr_end = v;
7169aba624dSNikolay Aleksandrov 				continue;
71747ecd2dbSNikolay Aleksandrov 			}
71847ecd2dbSNikolay Aleksandrov 
7199aba624dSNikolay Aleksandrov 			if (!br_vlan_global_opts_can_enter_range(v, curr_end)) {
7209aba624dSNikolay Aleksandrov 				br_vlan_global_opts_notify(br, curr_start->vid,
7219aba624dSNikolay Aleksandrov 							   curr_end->vid);
7229aba624dSNikolay Aleksandrov 				curr_start = v;
7239aba624dSNikolay Aleksandrov 			}
7249aba624dSNikolay Aleksandrov 			curr_end = v;
7259aba624dSNikolay Aleksandrov 		} else {
7269aba624dSNikolay Aleksandrov 			/* nothing changed and nothing to notify yet */
7279aba624dSNikolay Aleksandrov 			if (!curr_start)
7289aba624dSNikolay Aleksandrov 				continue;
7299aba624dSNikolay Aleksandrov 
7309aba624dSNikolay Aleksandrov 			br_vlan_global_opts_notify(br, curr_start->vid,
7319aba624dSNikolay Aleksandrov 						   curr_end->vid);
7329aba624dSNikolay Aleksandrov 			curr_start = NULL;
7339aba624dSNikolay Aleksandrov 			curr_end = NULL;
7349aba624dSNikolay Aleksandrov 		}
7359aba624dSNikolay Aleksandrov 	}
7369aba624dSNikolay Aleksandrov 	if (curr_start)
7379aba624dSNikolay Aleksandrov 		br_vlan_global_opts_notify(br, curr_start->vid, curr_end->vid);
7389aba624dSNikolay Aleksandrov 
73947ecd2dbSNikolay Aleksandrov 	return err;
74047ecd2dbSNikolay Aleksandrov }
741