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