18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	Bridge per vlan tunnel port dst_metadata netlink control interface
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *	Authors:
68c2ecf20Sopenharmony_ci *	Roopa Prabhu		<roopa@cumulusnetworks.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
128c2ecf20Sopenharmony_ci#include <net/rtnetlink.h>
138c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
148c2ecf20Sopenharmony_ci#include <net/sock.h>
158c2ecf20Sopenharmony_ci#include <uapi/linux/if_bridge.h>
168c2ecf20Sopenharmony_ci#include <net/dst_metadata.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "br_private.h"
198c2ecf20Sopenharmony_ci#include "br_private_tunnel.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic size_t __get_vlan_tinfo_size(void)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	return nla_total_size(0) + /* nest IFLA_BRIDGE_VLAN_TUNNEL_INFO */
248c2ecf20Sopenharmony_ci		  nla_total_size(sizeof(u32)) + /* IFLA_BRIDGE_VLAN_TUNNEL_ID */
258c2ecf20Sopenharmony_ci		  nla_total_size(sizeof(u16)) + /* IFLA_BRIDGE_VLAN_TUNNEL_VID */
268c2ecf20Sopenharmony_ci		  nla_total_size(sizeof(u16)); /* IFLA_BRIDGE_VLAN_TUNNEL_FLAGS */
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cibool vlan_tunid_inrange(const struct net_bridge_vlan *v_curr,
308c2ecf20Sopenharmony_ci			const struct net_bridge_vlan *v_last)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	__be32 tunid_curr = tunnel_id_to_key32(v_curr->tinfo.tunnel_id);
338c2ecf20Sopenharmony_ci	__be32 tunid_last = tunnel_id_to_key32(v_last->tinfo.tunnel_id);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	return (be32_to_cpu(tunid_curr) - be32_to_cpu(tunid_last)) == 1;
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic int __get_num_vlan_tunnel_infos(struct net_bridge_vlan_group *vg)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct net_bridge_vlan *v, *vtbegin = NULL, *vtend = NULL;
418c2ecf20Sopenharmony_ci	int num_tinfos = 0;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	/* Count number of vlan infos */
448c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
458c2ecf20Sopenharmony_ci		/* only a context, bridge vlan not activated */
468c2ecf20Sopenharmony_ci		if (!br_vlan_should_use(v) || !v->tinfo.tunnel_id)
478c2ecf20Sopenharmony_ci			continue;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci		if (!vtbegin) {
508c2ecf20Sopenharmony_ci			goto initvars;
518c2ecf20Sopenharmony_ci		} else if ((v->vid - vtend->vid) == 1 &&
528c2ecf20Sopenharmony_ci			   vlan_tunid_inrange(v, vtend)) {
538c2ecf20Sopenharmony_ci			vtend = v;
548c2ecf20Sopenharmony_ci			continue;
558c2ecf20Sopenharmony_ci		} else {
568c2ecf20Sopenharmony_ci			if ((vtend->vid - vtbegin->vid) > 0)
578c2ecf20Sopenharmony_ci				num_tinfos += 2;
588c2ecf20Sopenharmony_ci			else
598c2ecf20Sopenharmony_ci				num_tinfos += 1;
608c2ecf20Sopenharmony_ci		}
618c2ecf20Sopenharmony_ciinitvars:
628c2ecf20Sopenharmony_ci		vtbegin = v;
638c2ecf20Sopenharmony_ci		vtend = v;
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	if (vtbegin && vtend) {
678c2ecf20Sopenharmony_ci		if ((vtend->vid - vtbegin->vid) > 0)
688c2ecf20Sopenharmony_ci			num_tinfos += 2;
698c2ecf20Sopenharmony_ci		else
708c2ecf20Sopenharmony_ci			num_tinfos += 1;
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	return num_tinfos;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ciint br_get_vlan_tunnel_info_size(struct net_bridge_vlan_group *vg)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	int num_tinfos;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (!vg)
818c2ecf20Sopenharmony_ci		return 0;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	rcu_read_lock();
848c2ecf20Sopenharmony_ci	num_tinfos = __get_num_vlan_tunnel_infos(vg);
858c2ecf20Sopenharmony_ci	rcu_read_unlock();
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	return num_tinfos * __get_vlan_tinfo_size();
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic int br_fill_vlan_tinfo(struct sk_buff *skb, u16 vid,
918c2ecf20Sopenharmony_ci			      __be64 tunnel_id, u16 flags)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	__be32 tid = tunnel_id_to_key32(tunnel_id);
948c2ecf20Sopenharmony_ci	struct nlattr *tmap;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	tmap = nla_nest_start_noflag(skb, IFLA_BRIDGE_VLAN_TUNNEL_INFO);
978c2ecf20Sopenharmony_ci	if (!tmap)
988c2ecf20Sopenharmony_ci		return -EMSGSIZE;
998c2ecf20Sopenharmony_ci	if (nla_put_u32(skb, IFLA_BRIDGE_VLAN_TUNNEL_ID,
1008c2ecf20Sopenharmony_ci			be32_to_cpu(tid)))
1018c2ecf20Sopenharmony_ci		goto nla_put_failure;
1028c2ecf20Sopenharmony_ci	if (nla_put_u16(skb, IFLA_BRIDGE_VLAN_TUNNEL_VID,
1038c2ecf20Sopenharmony_ci			vid))
1048c2ecf20Sopenharmony_ci		goto nla_put_failure;
1058c2ecf20Sopenharmony_ci	if (nla_put_u16(skb, IFLA_BRIDGE_VLAN_TUNNEL_FLAGS,
1068c2ecf20Sopenharmony_ci			flags))
1078c2ecf20Sopenharmony_ci		goto nla_put_failure;
1088c2ecf20Sopenharmony_ci	nla_nest_end(skb, tmap);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	return 0;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cinla_put_failure:
1138c2ecf20Sopenharmony_ci	nla_nest_cancel(skb, tmap);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return -EMSGSIZE;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic int br_fill_vlan_tinfo_range(struct sk_buff *skb,
1198c2ecf20Sopenharmony_ci				    struct net_bridge_vlan *vtbegin,
1208c2ecf20Sopenharmony_ci				    struct net_bridge_vlan *vtend)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	int err;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (vtend && (vtend->vid - vtbegin->vid) > 0) {
1258c2ecf20Sopenharmony_ci		/* add range to skb */
1268c2ecf20Sopenharmony_ci		err = br_fill_vlan_tinfo(skb, vtbegin->vid,
1278c2ecf20Sopenharmony_ci					 vtbegin->tinfo.tunnel_id,
1288c2ecf20Sopenharmony_ci					 BRIDGE_VLAN_INFO_RANGE_BEGIN);
1298c2ecf20Sopenharmony_ci		if (err)
1308c2ecf20Sopenharmony_ci			return err;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci		err = br_fill_vlan_tinfo(skb, vtend->vid,
1338c2ecf20Sopenharmony_ci					 vtend->tinfo.tunnel_id,
1348c2ecf20Sopenharmony_ci					 BRIDGE_VLAN_INFO_RANGE_END);
1358c2ecf20Sopenharmony_ci		if (err)
1368c2ecf20Sopenharmony_ci			return err;
1378c2ecf20Sopenharmony_ci	} else {
1388c2ecf20Sopenharmony_ci		err = br_fill_vlan_tinfo(skb, vtbegin->vid,
1398c2ecf20Sopenharmony_ci					 vtbegin->tinfo.tunnel_id,
1408c2ecf20Sopenharmony_ci					 0);
1418c2ecf20Sopenharmony_ci		if (err)
1428c2ecf20Sopenharmony_ci			return err;
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	return 0;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ciint br_fill_vlan_tunnel_info(struct sk_buff *skb,
1498c2ecf20Sopenharmony_ci			     struct net_bridge_vlan_group *vg)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct net_bridge_vlan *vtbegin = NULL;
1528c2ecf20Sopenharmony_ci	struct net_bridge_vlan *vtend = NULL;
1538c2ecf20Sopenharmony_ci	struct net_bridge_vlan *v;
1548c2ecf20Sopenharmony_ci	int err;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	/* Count number of vlan infos */
1578c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
1588c2ecf20Sopenharmony_ci		/* only a context, bridge vlan not activated */
1598c2ecf20Sopenharmony_ci		if (!br_vlan_should_use(v))
1608c2ecf20Sopenharmony_ci			continue;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci		if (!v->tinfo.tunnel_dst)
1638c2ecf20Sopenharmony_ci			continue;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		if (!vtbegin) {
1668c2ecf20Sopenharmony_ci			goto initvars;
1678c2ecf20Sopenharmony_ci		} else if ((v->vid - vtend->vid) == 1 &&
1688c2ecf20Sopenharmony_ci			    vlan_tunid_inrange(v, vtend)) {
1698c2ecf20Sopenharmony_ci			vtend = v;
1708c2ecf20Sopenharmony_ci			continue;
1718c2ecf20Sopenharmony_ci		} else {
1728c2ecf20Sopenharmony_ci			err = br_fill_vlan_tinfo_range(skb, vtbegin, vtend);
1738c2ecf20Sopenharmony_ci			if (err)
1748c2ecf20Sopenharmony_ci				return err;
1758c2ecf20Sopenharmony_ci		}
1768c2ecf20Sopenharmony_ciinitvars:
1778c2ecf20Sopenharmony_ci		vtbegin = v;
1788c2ecf20Sopenharmony_ci		vtend = v;
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (vtbegin) {
1828c2ecf20Sopenharmony_ci		err = br_fill_vlan_tinfo_range(skb, vtbegin, vtend);
1838c2ecf20Sopenharmony_ci		if (err)
1848c2ecf20Sopenharmony_ci			return err;
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	return 0;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic const struct nla_policy vlan_tunnel_policy[IFLA_BRIDGE_VLAN_TUNNEL_MAX + 1] = {
1918c2ecf20Sopenharmony_ci	[IFLA_BRIDGE_VLAN_TUNNEL_ID] = { .type = NLA_U32 },
1928c2ecf20Sopenharmony_ci	[IFLA_BRIDGE_VLAN_TUNNEL_VID] = { .type = NLA_U16 },
1938c2ecf20Sopenharmony_ci	[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS] = { .type = NLA_U16 },
1948c2ecf20Sopenharmony_ci};
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ciint br_vlan_tunnel_info(const struct net_bridge_port *p, int cmd,
1978c2ecf20Sopenharmony_ci			u16 vid, u32 tun_id, bool *changed)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	int err = 0;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (!p)
2028c2ecf20Sopenharmony_ci		return -EINVAL;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	switch (cmd) {
2058c2ecf20Sopenharmony_ci	case RTM_SETLINK:
2068c2ecf20Sopenharmony_ci		err = nbp_vlan_tunnel_info_add(p, vid, tun_id);
2078c2ecf20Sopenharmony_ci		if (!err)
2088c2ecf20Sopenharmony_ci			*changed = true;
2098c2ecf20Sopenharmony_ci		break;
2108c2ecf20Sopenharmony_ci	case RTM_DELLINK:
2118c2ecf20Sopenharmony_ci		if (!nbp_vlan_tunnel_info_delete(p, vid))
2128c2ecf20Sopenharmony_ci			*changed = true;
2138c2ecf20Sopenharmony_ci		break;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	return err;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ciint br_parse_vlan_tunnel_info(struct nlattr *attr,
2208c2ecf20Sopenharmony_ci			      struct vtunnel_info *tinfo)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct nlattr *tb[IFLA_BRIDGE_VLAN_TUNNEL_MAX + 1];
2238c2ecf20Sopenharmony_ci	u32 tun_id;
2248c2ecf20Sopenharmony_ci	u16 vid, flags = 0;
2258c2ecf20Sopenharmony_ci	int err;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	memset(tinfo, 0, sizeof(*tinfo));
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	err = nla_parse_nested_deprecated(tb, IFLA_BRIDGE_VLAN_TUNNEL_MAX,
2308c2ecf20Sopenharmony_ci					  attr, vlan_tunnel_policy, NULL);
2318c2ecf20Sopenharmony_ci	if (err < 0)
2328c2ecf20Sopenharmony_ci		return err;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (!tb[IFLA_BRIDGE_VLAN_TUNNEL_ID] ||
2358c2ecf20Sopenharmony_ci	    !tb[IFLA_BRIDGE_VLAN_TUNNEL_VID])
2368c2ecf20Sopenharmony_ci		return -EINVAL;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	tun_id = nla_get_u32(tb[IFLA_BRIDGE_VLAN_TUNNEL_ID]);
2398c2ecf20Sopenharmony_ci	vid = nla_get_u16(tb[IFLA_BRIDGE_VLAN_TUNNEL_VID]);
2408c2ecf20Sopenharmony_ci	if (vid >= VLAN_VID_MASK)
2418c2ecf20Sopenharmony_ci		return -ERANGE;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	if (tb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS])
2448c2ecf20Sopenharmony_ci		flags = nla_get_u16(tb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	tinfo->tunid = tun_id;
2478c2ecf20Sopenharmony_ci	tinfo->vid = vid;
2488c2ecf20Sopenharmony_ci	tinfo->flags = flags;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	return 0;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci/* send a notification if v_curr can't enter the range and start a new one */
2548c2ecf20Sopenharmony_cistatic void __vlan_tunnel_handle_range(const struct net_bridge_port *p,
2558c2ecf20Sopenharmony_ci				       struct net_bridge_vlan **v_start,
2568c2ecf20Sopenharmony_ci				       struct net_bridge_vlan **v_end,
2578c2ecf20Sopenharmony_ci				       int v_curr, bool curr_change)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	struct net_bridge_vlan_group *vg;
2608c2ecf20Sopenharmony_ci	struct net_bridge_vlan *v;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	vg = nbp_vlan_group(p);
2638c2ecf20Sopenharmony_ci	if (!vg)
2648c2ecf20Sopenharmony_ci		return;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	v = br_vlan_find(vg, v_curr);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (!*v_start)
2698c2ecf20Sopenharmony_ci		goto out_init;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	if (v && curr_change && br_vlan_can_enter_range(v, *v_end)) {
2728c2ecf20Sopenharmony_ci		*v_end = v;
2738c2ecf20Sopenharmony_ci		return;
2748c2ecf20Sopenharmony_ci	}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	br_vlan_notify(p->br, p, (*v_start)->vid, (*v_end)->vid, RTM_NEWVLAN);
2778c2ecf20Sopenharmony_ciout_init:
2788c2ecf20Sopenharmony_ci	/* we start a range only if there are any changes to notify about */
2798c2ecf20Sopenharmony_ci	*v_start = curr_change ? v : NULL;
2808c2ecf20Sopenharmony_ci	*v_end = *v_start;
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ciint br_process_vlan_tunnel_info(const struct net_bridge *br,
2848c2ecf20Sopenharmony_ci				const struct net_bridge_port *p, int cmd,
2858c2ecf20Sopenharmony_ci				struct vtunnel_info *tinfo_curr,
2868c2ecf20Sopenharmony_ci				struct vtunnel_info *tinfo_last,
2878c2ecf20Sopenharmony_ci				bool *changed)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	int err;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if (tinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
2928c2ecf20Sopenharmony_ci		if (tinfo_last->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN)
2938c2ecf20Sopenharmony_ci			return -EINVAL;
2948c2ecf20Sopenharmony_ci		memcpy(tinfo_last, tinfo_curr, sizeof(struct vtunnel_info));
2958c2ecf20Sopenharmony_ci	} else if (tinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_END) {
2968c2ecf20Sopenharmony_ci		struct net_bridge_vlan *v_start = NULL, *v_end = NULL;
2978c2ecf20Sopenharmony_ci		int t, v;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci		if (!(tinfo_last->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN))
3008c2ecf20Sopenharmony_ci			return -EINVAL;
3018c2ecf20Sopenharmony_ci		if ((tinfo_curr->vid - tinfo_last->vid) !=
3028c2ecf20Sopenharmony_ci		    (tinfo_curr->tunid - tinfo_last->tunid))
3038c2ecf20Sopenharmony_ci			return -EINVAL;
3048c2ecf20Sopenharmony_ci		t = tinfo_last->tunid;
3058c2ecf20Sopenharmony_ci		for (v = tinfo_last->vid; v <= tinfo_curr->vid; v++) {
3068c2ecf20Sopenharmony_ci			bool curr_change = false;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci			err = br_vlan_tunnel_info(p, cmd, v, t, &curr_change);
3098c2ecf20Sopenharmony_ci			if (err)
3108c2ecf20Sopenharmony_ci				break;
3118c2ecf20Sopenharmony_ci			t++;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci			if (curr_change)
3148c2ecf20Sopenharmony_ci				*changed = curr_change;
3158c2ecf20Sopenharmony_ci			 __vlan_tunnel_handle_range(p, &v_start, &v_end, v,
3168c2ecf20Sopenharmony_ci						    curr_change);
3178c2ecf20Sopenharmony_ci		}
3188c2ecf20Sopenharmony_ci		if (v_start && v_end)
3198c2ecf20Sopenharmony_ci			br_vlan_notify(br, p, v_start->vid, v_end->vid,
3208c2ecf20Sopenharmony_ci				       RTM_NEWVLAN);
3218c2ecf20Sopenharmony_ci		if (err)
3228c2ecf20Sopenharmony_ci			return err;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci		memset(tinfo_last, 0, sizeof(struct vtunnel_info));
3258c2ecf20Sopenharmony_ci		memset(tinfo_curr, 0, sizeof(struct vtunnel_info));
3268c2ecf20Sopenharmony_ci	} else {
3278c2ecf20Sopenharmony_ci		if (tinfo_last->flags)
3288c2ecf20Sopenharmony_ci			return -EINVAL;
3298c2ecf20Sopenharmony_ci		err = br_vlan_tunnel_info(p, cmd, tinfo_curr->vid,
3308c2ecf20Sopenharmony_ci					  tinfo_curr->tunid, changed);
3318c2ecf20Sopenharmony_ci		if (err)
3328c2ecf20Sopenharmony_ci			return err;
3338c2ecf20Sopenharmony_ci		br_vlan_notify(br, p, tinfo_curr->vid, 0, RTM_NEWVLAN);
3348c2ecf20Sopenharmony_ci		memset(tinfo_last, 0, sizeof(struct vtunnel_info));
3358c2ecf20Sopenharmony_ci		memset(tinfo_curr, 0, sizeof(struct vtunnel_info));
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	return 0;
3398c2ecf20Sopenharmony_ci}
340