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