12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2efa5356bSRoopa Prabhu /*
3efa5356bSRoopa Prabhu  *	Bridge per vlan tunnel port dst_metadata netlink control interface
4efa5356bSRoopa Prabhu  *
5efa5356bSRoopa Prabhu  *	Authors:
6efa5356bSRoopa Prabhu  *	Roopa Prabhu		<roopa@cumulusnetworks.com>
7efa5356bSRoopa Prabhu  */
8efa5356bSRoopa Prabhu 
9efa5356bSRoopa Prabhu #include <linux/kernel.h>
10efa5356bSRoopa Prabhu #include <linux/slab.h>
11efa5356bSRoopa Prabhu #include <linux/etherdevice.h>
12efa5356bSRoopa Prabhu #include <net/rtnetlink.h>
13efa5356bSRoopa Prabhu #include <net/net_namespace.h>
14efa5356bSRoopa Prabhu #include <net/sock.h>
15efa5356bSRoopa Prabhu #include <uapi/linux/if_bridge.h>
16efa5356bSRoopa Prabhu #include <net/dst_metadata.h>
17efa5356bSRoopa Prabhu 
18efa5356bSRoopa Prabhu #include "br_private.h"
19efa5356bSRoopa Prabhu #include "br_private_tunnel.h"
20efa5356bSRoopa Prabhu 
__get_vlan_tinfo_size(void)21efa5356bSRoopa Prabhu static size_t __get_vlan_tinfo_size(void)
22efa5356bSRoopa Prabhu {
23efa5356bSRoopa Prabhu 	return nla_total_size(0) + /* nest IFLA_BRIDGE_VLAN_TUNNEL_INFO */
24efa5356bSRoopa Prabhu 		  nla_total_size(sizeof(u32)) + /* IFLA_BRIDGE_VLAN_TUNNEL_ID */
25efa5356bSRoopa Prabhu 		  nla_total_size(sizeof(u16)) + /* IFLA_BRIDGE_VLAN_TUNNEL_VID */
26efa5356bSRoopa Prabhu 		  nla_total_size(sizeof(u16)); /* IFLA_BRIDGE_VLAN_TUNNEL_FLAGS */
27efa5356bSRoopa Prabhu }
28efa5356bSRoopa Prabhu 
vlan_tunid_inrange(const struct net_bridge_vlan * v_curr,const struct net_bridge_vlan * v_last)29188c67ddSNikolay Aleksandrov bool vlan_tunid_inrange(const struct net_bridge_vlan *v_curr,
30188c67ddSNikolay Aleksandrov 			const struct net_bridge_vlan *v_last)
31efa5356bSRoopa Prabhu {
328ef95947SRoopa Prabhu 	__be32 tunid_curr = tunnel_id_to_key32(v_curr->tinfo.tunnel_id);
338ef95947SRoopa Prabhu 	__be32 tunid_last = tunnel_id_to_key32(v_last->tinfo.tunnel_id);
34efa5356bSRoopa Prabhu 
358ef95947SRoopa Prabhu 	return (be32_to_cpu(tunid_curr) - be32_to_cpu(tunid_last)) == 1;
36efa5356bSRoopa Prabhu }
37efa5356bSRoopa Prabhu 
__get_num_vlan_tunnel_infos(struct net_bridge_vlan_group * vg)38efa5356bSRoopa Prabhu static int __get_num_vlan_tunnel_infos(struct net_bridge_vlan_group *vg)
39efa5356bSRoopa Prabhu {
408ef95947SRoopa Prabhu 	struct net_bridge_vlan *v, *vtbegin = NULL, *vtend = NULL;
41efa5356bSRoopa Prabhu 	int num_tinfos = 0;
42efa5356bSRoopa Prabhu 
43efa5356bSRoopa Prabhu 	/* Count number of vlan infos */
44efa5356bSRoopa Prabhu 	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
45efa5356bSRoopa Prabhu 		/* only a context, bridge vlan not activated */
46efa5356bSRoopa Prabhu 		if (!br_vlan_should_use(v) || !v->tinfo.tunnel_id)
47efa5356bSRoopa Prabhu 			continue;
48efa5356bSRoopa Prabhu 
498ef95947SRoopa Prabhu 		if (!vtbegin) {
50efa5356bSRoopa Prabhu 			goto initvars;
518ef95947SRoopa Prabhu 		} else if ((v->vid - vtend->vid) == 1 &&
528ef95947SRoopa Prabhu 			   vlan_tunid_inrange(v, vtend)) {
538ef95947SRoopa Prabhu 			vtend = v;
54efa5356bSRoopa Prabhu 			continue;
55efa5356bSRoopa Prabhu 		} else {
568ef95947SRoopa Prabhu 			if ((vtend->vid - vtbegin->vid) > 0)
57efa5356bSRoopa Prabhu 				num_tinfos += 2;
58efa5356bSRoopa Prabhu 			else
59efa5356bSRoopa Prabhu 				num_tinfos += 1;
60efa5356bSRoopa Prabhu 		}
61efa5356bSRoopa Prabhu initvars:
628ef95947SRoopa Prabhu 		vtbegin = v;
638ef95947SRoopa Prabhu 		vtend = v;
64efa5356bSRoopa Prabhu 	}
65efa5356bSRoopa Prabhu 
668ef95947SRoopa Prabhu 	if (vtbegin && vtend) {
678ef95947SRoopa Prabhu 		if ((vtend->vid - vtbegin->vid) > 0)
68efa5356bSRoopa Prabhu 			num_tinfos += 2;
69efa5356bSRoopa Prabhu 		else
70efa5356bSRoopa Prabhu 			num_tinfos += 1;
71efa5356bSRoopa Prabhu 	}
72efa5356bSRoopa Prabhu 
73efa5356bSRoopa Prabhu 	return num_tinfos;
74efa5356bSRoopa Prabhu }
75efa5356bSRoopa Prabhu 
br_get_vlan_tunnel_info_size(struct net_bridge_vlan_group * vg)76efa5356bSRoopa Prabhu int br_get_vlan_tunnel_info_size(struct net_bridge_vlan_group *vg)
77efa5356bSRoopa Prabhu {
78efa5356bSRoopa Prabhu 	int num_tinfos;
79efa5356bSRoopa Prabhu 
80efa5356bSRoopa Prabhu 	if (!vg)
81efa5356bSRoopa Prabhu 		return 0;
82efa5356bSRoopa Prabhu 
83efa5356bSRoopa Prabhu 	rcu_read_lock();
84efa5356bSRoopa Prabhu 	num_tinfos = __get_num_vlan_tunnel_infos(vg);
85efa5356bSRoopa Prabhu 	rcu_read_unlock();
86efa5356bSRoopa Prabhu 
87efa5356bSRoopa Prabhu 	return num_tinfos * __get_vlan_tinfo_size();
88efa5356bSRoopa Prabhu }
89efa5356bSRoopa Prabhu 
br_fill_vlan_tinfo(struct sk_buff * skb,u16 vid,__be64 tunnel_id,u16 flags)90efa5356bSRoopa Prabhu static int br_fill_vlan_tinfo(struct sk_buff *skb, u16 vid,
91efa5356bSRoopa Prabhu 			      __be64 tunnel_id, u16 flags)
92efa5356bSRoopa Prabhu {
93efa5356bSRoopa Prabhu 	__be32 tid = tunnel_id_to_key32(tunnel_id);
94efa5356bSRoopa Prabhu 	struct nlattr *tmap;
95efa5356bSRoopa Prabhu 
96ae0be8deSMichal Kubecek 	tmap = nla_nest_start_noflag(skb, IFLA_BRIDGE_VLAN_TUNNEL_INFO);
97efa5356bSRoopa Prabhu 	if (!tmap)
98efa5356bSRoopa Prabhu 		return -EMSGSIZE;
99efa5356bSRoopa Prabhu 	if (nla_put_u32(skb, IFLA_BRIDGE_VLAN_TUNNEL_ID,
100efa5356bSRoopa Prabhu 			be32_to_cpu(tid)))
101efa5356bSRoopa Prabhu 		goto nla_put_failure;
102efa5356bSRoopa Prabhu 	if (nla_put_u16(skb, IFLA_BRIDGE_VLAN_TUNNEL_VID,
103efa5356bSRoopa Prabhu 			vid))
104efa5356bSRoopa Prabhu 		goto nla_put_failure;
105efa5356bSRoopa Prabhu 	if (nla_put_u16(skb, IFLA_BRIDGE_VLAN_TUNNEL_FLAGS,
106efa5356bSRoopa Prabhu 			flags))
107efa5356bSRoopa Prabhu 		goto nla_put_failure;
108efa5356bSRoopa Prabhu 	nla_nest_end(skb, tmap);
109efa5356bSRoopa Prabhu 
110efa5356bSRoopa Prabhu 	return 0;
111efa5356bSRoopa Prabhu 
112efa5356bSRoopa Prabhu nla_put_failure:
113efa5356bSRoopa Prabhu 	nla_nest_cancel(skb, tmap);
114efa5356bSRoopa Prabhu 
115efa5356bSRoopa Prabhu 	return -EMSGSIZE;
116efa5356bSRoopa Prabhu }
117efa5356bSRoopa Prabhu 
br_fill_vlan_tinfo_range(struct sk_buff * skb,struct net_bridge_vlan * vtbegin,struct net_bridge_vlan * vtend)118efa5356bSRoopa Prabhu static int br_fill_vlan_tinfo_range(struct sk_buff *skb,
119efa5356bSRoopa Prabhu 				    struct net_bridge_vlan *vtbegin,
120efa5356bSRoopa Prabhu 				    struct net_bridge_vlan *vtend)
121efa5356bSRoopa Prabhu {
122efa5356bSRoopa Prabhu 	int err;
123efa5356bSRoopa Prabhu 
124a8cab863SRoopa Prabhu 	if (vtend && (vtend->vid - vtbegin->vid) > 0) {
125efa5356bSRoopa Prabhu 		/* add range to skb */
126efa5356bSRoopa Prabhu 		err = br_fill_vlan_tinfo(skb, vtbegin->vid,
127efa5356bSRoopa Prabhu 					 vtbegin->tinfo.tunnel_id,
128efa5356bSRoopa Prabhu 					 BRIDGE_VLAN_INFO_RANGE_BEGIN);
129efa5356bSRoopa Prabhu 		if (err)
130efa5356bSRoopa Prabhu 			return err;
131efa5356bSRoopa Prabhu 
132efa5356bSRoopa Prabhu 		err = br_fill_vlan_tinfo(skb, vtend->vid,
133efa5356bSRoopa Prabhu 					 vtend->tinfo.tunnel_id,
134efa5356bSRoopa Prabhu 					 BRIDGE_VLAN_INFO_RANGE_END);
135efa5356bSRoopa Prabhu 		if (err)
136efa5356bSRoopa Prabhu 			return err;
137efa5356bSRoopa Prabhu 	} else {
138efa5356bSRoopa Prabhu 		err = br_fill_vlan_tinfo(skb, vtbegin->vid,
139efa5356bSRoopa Prabhu 					 vtbegin->tinfo.tunnel_id,
140efa5356bSRoopa Prabhu 					 0);
141efa5356bSRoopa Prabhu 		if (err)
142efa5356bSRoopa Prabhu 			return err;
143efa5356bSRoopa Prabhu 	}
144efa5356bSRoopa Prabhu 
145efa5356bSRoopa Prabhu 	return 0;
146efa5356bSRoopa Prabhu }
147efa5356bSRoopa Prabhu 
br_fill_vlan_tunnel_info(struct sk_buff * skb,struct net_bridge_vlan_group * vg)148efa5356bSRoopa Prabhu int br_fill_vlan_tunnel_info(struct sk_buff *skb,
149efa5356bSRoopa Prabhu 			     struct net_bridge_vlan_group *vg)
150efa5356bSRoopa Prabhu {
151efa5356bSRoopa Prabhu 	struct net_bridge_vlan *vtbegin = NULL;
152efa5356bSRoopa Prabhu 	struct net_bridge_vlan *vtend = NULL;
153efa5356bSRoopa Prabhu 	struct net_bridge_vlan *v;
154efa5356bSRoopa Prabhu 	int err;
155efa5356bSRoopa Prabhu 
156efa5356bSRoopa Prabhu 	/* Count number of vlan infos */
157efa5356bSRoopa Prabhu 	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
158efa5356bSRoopa Prabhu 		/* only a context, bridge vlan not activated */
159efa5356bSRoopa Prabhu 		if (!br_vlan_should_use(v))
160efa5356bSRoopa Prabhu 			continue;
161efa5356bSRoopa Prabhu 
162efa5356bSRoopa Prabhu 		if (!v->tinfo.tunnel_dst)
163efa5356bSRoopa Prabhu 			continue;
164efa5356bSRoopa Prabhu 
165efa5356bSRoopa Prabhu 		if (!vtbegin) {
166efa5356bSRoopa Prabhu 			goto initvars;
167efa5356bSRoopa Prabhu 		} else if ((v->vid - vtend->vid) == 1 &&
1688ef95947SRoopa Prabhu 			    vlan_tunid_inrange(v, vtend)) {
169efa5356bSRoopa Prabhu 			vtend = v;
170efa5356bSRoopa Prabhu 			continue;
171efa5356bSRoopa Prabhu 		} else {
172efa5356bSRoopa Prabhu 			err = br_fill_vlan_tinfo_range(skb, vtbegin, vtend);
173efa5356bSRoopa Prabhu 			if (err)
174efa5356bSRoopa Prabhu 				return err;
175efa5356bSRoopa Prabhu 		}
176efa5356bSRoopa Prabhu initvars:
177efa5356bSRoopa Prabhu 		vtbegin = v;
178efa5356bSRoopa Prabhu 		vtend = v;
179efa5356bSRoopa Prabhu 	}
180efa5356bSRoopa Prabhu 
181efa5356bSRoopa Prabhu 	if (vtbegin) {
182efa5356bSRoopa Prabhu 		err = br_fill_vlan_tinfo_range(skb, vtbegin, vtend);
183efa5356bSRoopa Prabhu 		if (err)
184efa5356bSRoopa Prabhu 			return err;
185efa5356bSRoopa Prabhu 	}
186efa5356bSRoopa Prabhu 
187efa5356bSRoopa Prabhu 	return 0;
188efa5356bSRoopa Prabhu }
189efa5356bSRoopa Prabhu 
190efa5356bSRoopa Prabhu static const struct nla_policy vlan_tunnel_policy[IFLA_BRIDGE_VLAN_TUNNEL_MAX + 1] = {
191*c00041cfSPetr Machata 	[IFLA_BRIDGE_VLAN_TUNNEL_UNSPEC] = {
192*c00041cfSPetr Machata 		.strict_start_type = IFLA_BRIDGE_VLAN_TUNNEL_FLAGS + 1
193*c00041cfSPetr Machata 	},
194efa5356bSRoopa Prabhu 	[IFLA_BRIDGE_VLAN_TUNNEL_ID] = { .type = NLA_U32 },
195efa5356bSRoopa Prabhu 	[IFLA_BRIDGE_VLAN_TUNNEL_VID] = { .type = NLA_U16 },
196efa5356bSRoopa Prabhu 	[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS] = { .type = NLA_U16 },
197efa5356bSRoopa Prabhu };
198efa5356bSRoopa Prabhu 
br_vlan_tunnel_info(const struct net_bridge_port * p,int cmd,u16 vid,u32 tun_id,bool * changed)199569da082SNikolay Aleksandrov int br_vlan_tunnel_info(const struct net_bridge_port *p, int cmd,
200e19b42a1SNikolay Aleksandrov 			u16 vid, u32 tun_id, bool *changed)
201efa5356bSRoopa Prabhu {
202efa5356bSRoopa Prabhu 	int err = 0;
203efa5356bSRoopa Prabhu 
204efa5356bSRoopa Prabhu 	if (!p)
205efa5356bSRoopa Prabhu 		return -EINVAL;
206efa5356bSRoopa Prabhu 
207efa5356bSRoopa Prabhu 	switch (cmd) {
208efa5356bSRoopa Prabhu 	case RTM_SETLINK:
209efa5356bSRoopa Prabhu 		err = nbp_vlan_tunnel_info_add(p, vid, tun_id);
210e19b42a1SNikolay Aleksandrov 		if (!err)
211e19b42a1SNikolay Aleksandrov 			*changed = true;
212efa5356bSRoopa Prabhu 		break;
213efa5356bSRoopa Prabhu 	case RTM_DELLINK:
214e19b42a1SNikolay Aleksandrov 		if (!nbp_vlan_tunnel_info_delete(p, vid))
215e19b42a1SNikolay Aleksandrov 			*changed = true;
216efa5356bSRoopa Prabhu 		break;
217efa5356bSRoopa Prabhu 	}
218efa5356bSRoopa Prabhu 
219efa5356bSRoopa Prabhu 	return err;
220efa5356bSRoopa Prabhu }
221efa5356bSRoopa Prabhu 
br_parse_vlan_tunnel_info(struct nlattr * attr,struct vtunnel_info * tinfo)222efa5356bSRoopa Prabhu int br_parse_vlan_tunnel_info(struct nlattr *attr,
223efa5356bSRoopa Prabhu 			      struct vtunnel_info *tinfo)
224efa5356bSRoopa Prabhu {
225efa5356bSRoopa Prabhu 	struct nlattr *tb[IFLA_BRIDGE_VLAN_TUNNEL_MAX + 1];
226efa5356bSRoopa Prabhu 	u32 tun_id;
227efa5356bSRoopa Prabhu 	u16 vid, flags = 0;
228efa5356bSRoopa Prabhu 	int err;
229efa5356bSRoopa Prabhu 
230efa5356bSRoopa Prabhu 	memset(tinfo, 0, sizeof(*tinfo));
231efa5356bSRoopa Prabhu 
2328cb08174SJohannes Berg 	err = nla_parse_nested_deprecated(tb, IFLA_BRIDGE_VLAN_TUNNEL_MAX,
2338cb08174SJohannes Berg 					  attr, vlan_tunnel_policy, NULL);
234efa5356bSRoopa Prabhu 	if (err < 0)
235efa5356bSRoopa Prabhu 		return err;
236efa5356bSRoopa Prabhu 
237bb580ad6SNikolay Aleksandrov 	if (!tb[IFLA_BRIDGE_VLAN_TUNNEL_ID] ||
238bb580ad6SNikolay Aleksandrov 	    !tb[IFLA_BRIDGE_VLAN_TUNNEL_VID])
239bb580ad6SNikolay Aleksandrov 		return -EINVAL;
240bb580ad6SNikolay Aleksandrov 
241efa5356bSRoopa Prabhu 	tun_id = nla_get_u32(tb[IFLA_BRIDGE_VLAN_TUNNEL_ID]);
242efa5356bSRoopa Prabhu 	vid = nla_get_u16(tb[IFLA_BRIDGE_VLAN_TUNNEL_VID]);
243efa5356bSRoopa Prabhu 	if (vid >= VLAN_VID_MASK)
244efa5356bSRoopa Prabhu 		return -ERANGE;
245efa5356bSRoopa Prabhu 
246efa5356bSRoopa Prabhu 	if (tb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS])
247efa5356bSRoopa Prabhu 		flags = nla_get_u16(tb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]);
248efa5356bSRoopa Prabhu 
249efa5356bSRoopa Prabhu 	tinfo->tunid = tun_id;
250efa5356bSRoopa Prabhu 	tinfo->vid = vid;
251efa5356bSRoopa Prabhu 	tinfo->flags = flags;
252efa5356bSRoopa Prabhu 
253efa5356bSRoopa Prabhu 	return 0;
254efa5356bSRoopa Prabhu }
255efa5356bSRoopa Prabhu 
25694339443SNikolay Aleksandrov /* send a notification if v_curr can't enter the range and start a new one */
__vlan_tunnel_handle_range(const struct net_bridge_port * p,struct net_bridge_vlan ** v_start,struct net_bridge_vlan ** v_end,int v_curr,bool curr_change)25794339443SNikolay Aleksandrov static void __vlan_tunnel_handle_range(const struct net_bridge_port *p,
25894339443SNikolay Aleksandrov 				       struct net_bridge_vlan **v_start,
25994339443SNikolay Aleksandrov 				       struct net_bridge_vlan **v_end,
26094339443SNikolay Aleksandrov 				       int v_curr, bool curr_change)
26194339443SNikolay Aleksandrov {
26294339443SNikolay Aleksandrov 	struct net_bridge_vlan_group *vg;
26394339443SNikolay Aleksandrov 	struct net_bridge_vlan *v;
26494339443SNikolay Aleksandrov 
26594339443SNikolay Aleksandrov 	vg = nbp_vlan_group(p);
26694339443SNikolay Aleksandrov 	if (!vg)
26794339443SNikolay Aleksandrov 		return;
26894339443SNikolay Aleksandrov 
26994339443SNikolay Aleksandrov 	v = br_vlan_find(vg, v_curr);
27094339443SNikolay Aleksandrov 
27194339443SNikolay Aleksandrov 	if (!*v_start)
27294339443SNikolay Aleksandrov 		goto out_init;
27394339443SNikolay Aleksandrov 
27494339443SNikolay Aleksandrov 	if (v && curr_change && br_vlan_can_enter_range(v, *v_end)) {
27594339443SNikolay Aleksandrov 		*v_end = v;
27694339443SNikolay Aleksandrov 		return;
27794339443SNikolay Aleksandrov 	}
27894339443SNikolay Aleksandrov 
27994339443SNikolay Aleksandrov 	br_vlan_notify(p->br, p, (*v_start)->vid, (*v_end)->vid, RTM_NEWVLAN);
28094339443SNikolay Aleksandrov out_init:
28194339443SNikolay Aleksandrov 	/* we start a range only if there are any changes to notify about */
28294339443SNikolay Aleksandrov 	*v_start = curr_change ? v : NULL;
28394339443SNikolay Aleksandrov 	*v_end = *v_start;
28494339443SNikolay Aleksandrov }
28594339443SNikolay Aleksandrov 
br_process_vlan_tunnel_info(const struct net_bridge * br,const struct net_bridge_port * p,int cmd,struct vtunnel_info * tinfo_curr,struct vtunnel_info * tinfo_last,bool * changed)28653e96632SNikolay Aleksandrov int br_process_vlan_tunnel_info(const struct net_bridge *br,
28753e96632SNikolay Aleksandrov 				const struct net_bridge_port *p, int cmd,
288efa5356bSRoopa Prabhu 				struct vtunnel_info *tinfo_curr,
289e19b42a1SNikolay Aleksandrov 				struct vtunnel_info *tinfo_last,
290e19b42a1SNikolay Aleksandrov 				bool *changed)
291efa5356bSRoopa Prabhu {
292efa5356bSRoopa Prabhu 	int err;
293efa5356bSRoopa Prabhu 
294efa5356bSRoopa Prabhu 	if (tinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
295efa5356bSRoopa Prabhu 		if (tinfo_last->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN)
296efa5356bSRoopa Prabhu 			return -EINVAL;
297efa5356bSRoopa Prabhu 		memcpy(tinfo_last, tinfo_curr, sizeof(struct vtunnel_info));
298efa5356bSRoopa Prabhu 	} else if (tinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_END) {
29994339443SNikolay Aleksandrov 		struct net_bridge_vlan *v_start = NULL, *v_end = NULL;
300efa5356bSRoopa Prabhu 		int t, v;
301efa5356bSRoopa Prabhu 
302efa5356bSRoopa Prabhu 		if (!(tinfo_last->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN))
303efa5356bSRoopa Prabhu 			return -EINVAL;
304efa5356bSRoopa Prabhu 		if ((tinfo_curr->vid - tinfo_last->vid) !=
305efa5356bSRoopa Prabhu 		    (tinfo_curr->tunid - tinfo_last->tunid))
306efa5356bSRoopa Prabhu 			return -EINVAL;
307efa5356bSRoopa Prabhu 		t = tinfo_last->tunid;
308efa5356bSRoopa Prabhu 		for (v = tinfo_last->vid; v <= tinfo_curr->vid; v++) {
30994339443SNikolay Aleksandrov 			bool curr_change = false;
31094339443SNikolay Aleksandrov 
31194339443SNikolay Aleksandrov 			err = br_vlan_tunnel_info(p, cmd, v, t, &curr_change);
31294339443SNikolay Aleksandrov 			if (err)
31394339443SNikolay Aleksandrov 				break;
31494339443SNikolay Aleksandrov 			t++;
31594339443SNikolay Aleksandrov 
31694339443SNikolay Aleksandrov 			if (curr_change)
31794339443SNikolay Aleksandrov 				*changed = curr_change;
31894339443SNikolay Aleksandrov 			 __vlan_tunnel_handle_range(p, &v_start, &v_end, v,
31994339443SNikolay Aleksandrov 						    curr_change);
32094339443SNikolay Aleksandrov 		}
32194339443SNikolay Aleksandrov 		if (v_start && v_end)
32294339443SNikolay Aleksandrov 			br_vlan_notify(br, p, v_start->vid, v_end->vid,
32394339443SNikolay Aleksandrov 				       RTM_NEWVLAN);
324efa5356bSRoopa Prabhu 		if (err)
325efa5356bSRoopa Prabhu 			return err;
32694339443SNikolay Aleksandrov 
327efa5356bSRoopa Prabhu 		memset(tinfo_last, 0, sizeof(struct vtunnel_info));
328efa5356bSRoopa Prabhu 		memset(tinfo_curr, 0, sizeof(struct vtunnel_info));
329efa5356bSRoopa Prabhu 	} else {
330efa5356bSRoopa Prabhu 		if (tinfo_last->flags)
331efa5356bSRoopa Prabhu 			return -EINVAL;
332efa5356bSRoopa Prabhu 		err = br_vlan_tunnel_info(p, cmd, tinfo_curr->vid,
333e19b42a1SNikolay Aleksandrov 					  tinfo_curr->tunid, changed);
334efa5356bSRoopa Prabhu 		if (err)
335efa5356bSRoopa Prabhu 			return err;
33694339443SNikolay Aleksandrov 		br_vlan_notify(br, p, tinfo_curr->vid, 0, RTM_NEWVLAN);
337efa5356bSRoopa Prabhu 		memset(tinfo_last, 0, sizeof(struct vtunnel_info));
338efa5356bSRoopa Prabhu 		memset(tinfo_curr, 0, sizeof(struct vtunnel_info));
339efa5356bSRoopa Prabhu 	}
340efa5356bSRoopa Prabhu 
341efa5356bSRoopa Prabhu 	return 0;
342efa5356bSRoopa Prabhu }
343