18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * vrf.c: device driver to encapsulate a VRF space
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2015 Cumulus Networks. All rights reserved.
68c2ecf20Sopenharmony_ci * Copyright (c) 2015 Shrijeet Mukherjee <shm@cumulusnetworks.com>
78c2ecf20Sopenharmony_ci * Copyright (c) 2015 David Ahern <dsa@cumulusnetworks.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Based on dummy, team and ipvlan drivers
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
158c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
168c2ecf20Sopenharmony_ci#include <linux/ip.h>
178c2ecf20Sopenharmony_ci#include <linux/init.h>
188c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
198c2ecf20Sopenharmony_ci#include <linux/netfilter.h>
208c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
218c2ecf20Sopenharmony_ci#include <net/rtnetlink.h>
228c2ecf20Sopenharmony_ci#include <linux/u64_stats_sync.h>
238c2ecf20Sopenharmony_ci#include <linux/hashtable.h>
248c2ecf20Sopenharmony_ci#include <linux/spinlock_types.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <linux/inetdevice.h>
278c2ecf20Sopenharmony_ci#include <net/arp.h>
288c2ecf20Sopenharmony_ci#include <net/ip.h>
298c2ecf20Sopenharmony_ci#include <net/ip_fib.h>
308c2ecf20Sopenharmony_ci#include <net/ip6_fib.h>
318c2ecf20Sopenharmony_ci#include <net/ip6_route.h>
328c2ecf20Sopenharmony_ci#include <net/route.h>
338c2ecf20Sopenharmony_ci#include <net/addrconf.h>
348c2ecf20Sopenharmony_ci#include <net/l3mdev.h>
358c2ecf20Sopenharmony_ci#include <net/fib_rules.h>
368c2ecf20Sopenharmony_ci#include <net/netns/generic.h>
378c2ecf20Sopenharmony_ci#include <net/netfilter/nf_conntrack.h>
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define DRV_NAME	"vrf"
408c2ecf20Sopenharmony_ci#define DRV_VERSION	"1.1"
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define FIB_RULE_PREF  1000       /* default preference for FIB rules */
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define HT_MAP_BITS	4
458c2ecf20Sopenharmony_ci#define HASH_INITVAL	((u32)0xcafef00d)
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistruct  vrf_map {
488c2ecf20Sopenharmony_ci	DECLARE_HASHTABLE(ht, HT_MAP_BITS);
498c2ecf20Sopenharmony_ci	spinlock_t vmap_lock;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	/* shared_tables:
528c2ecf20Sopenharmony_ci	 * count how many distinct tables do not comply with the strict mode
538c2ecf20Sopenharmony_ci	 * requirement.
548c2ecf20Sopenharmony_ci	 * shared_tables value must be 0 in order to enable the strict mode.
558c2ecf20Sopenharmony_ci	 *
568c2ecf20Sopenharmony_ci	 * example of the evolution of shared_tables:
578c2ecf20Sopenharmony_ci	 *                                                        | time
588c2ecf20Sopenharmony_ci	 * add  vrf0 --> table 100        shared_tables = 0       | t0
598c2ecf20Sopenharmony_ci	 * add  vrf1 --> table 101        shared_tables = 0       | t1
608c2ecf20Sopenharmony_ci	 * add  vrf2 --> table 100        shared_tables = 1       | t2
618c2ecf20Sopenharmony_ci	 * add  vrf3 --> table 100        shared_tables = 1       | t3
628c2ecf20Sopenharmony_ci	 * add  vrf4 --> table 101        shared_tables = 2       v t4
638c2ecf20Sopenharmony_ci	 *
648c2ecf20Sopenharmony_ci	 * shared_tables is a "step function" (or "staircase function")
658c2ecf20Sopenharmony_ci	 * and it is increased by one when the second vrf is associated to a
668c2ecf20Sopenharmony_ci	 * table.
678c2ecf20Sopenharmony_ci	 *
688c2ecf20Sopenharmony_ci	 * at t2, vrf0 and vrf2 are bound to table 100: shared_tables = 1.
698c2ecf20Sopenharmony_ci	 *
708c2ecf20Sopenharmony_ci	 * at t3, another dev (vrf3) is bound to the same table 100 but the
718c2ecf20Sopenharmony_ci	 * value of shared_tables is still 1.
728c2ecf20Sopenharmony_ci	 * This means that no matter how many new vrfs will register on the
738c2ecf20Sopenharmony_ci	 * table 100, the shared_tables will not increase (considering only
748c2ecf20Sopenharmony_ci	 * table 100).
758c2ecf20Sopenharmony_ci	 *
768c2ecf20Sopenharmony_ci	 * at t4, vrf4 is bound to table 101, and shared_tables = 2.
778c2ecf20Sopenharmony_ci	 *
788c2ecf20Sopenharmony_ci	 * Looking at the value of shared_tables we can immediately know if
798c2ecf20Sopenharmony_ci	 * the strict_mode can or cannot be enforced. Indeed, strict_mode
808c2ecf20Sopenharmony_ci	 * can be enforced iff shared_tables = 0.
818c2ecf20Sopenharmony_ci	 *
828c2ecf20Sopenharmony_ci	 * Conversely, shared_tables is decreased when a vrf is de-associated
838c2ecf20Sopenharmony_ci	 * from a table with exactly two associated vrfs.
848c2ecf20Sopenharmony_ci	 */
858c2ecf20Sopenharmony_ci	u32 shared_tables;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	bool strict_mode;
888c2ecf20Sopenharmony_ci};
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistruct vrf_map_elem {
918c2ecf20Sopenharmony_ci	struct hlist_node hnode;
928c2ecf20Sopenharmony_ci	struct list_head vrf_list;  /* VRFs registered to this table */
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	u32 table_id;
958c2ecf20Sopenharmony_ci	int users;
968c2ecf20Sopenharmony_ci	int ifindex;
978c2ecf20Sopenharmony_ci};
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic unsigned int vrf_net_id;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/* per netns vrf data */
1028c2ecf20Sopenharmony_cistruct netns_vrf {
1038c2ecf20Sopenharmony_ci	/* protected by rtnl lock */
1048c2ecf20Sopenharmony_ci	bool add_fib_rules;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	struct vrf_map vmap;
1078c2ecf20Sopenharmony_ci	struct ctl_table_header	*ctl_hdr;
1088c2ecf20Sopenharmony_ci};
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistruct net_vrf {
1118c2ecf20Sopenharmony_ci	struct rtable __rcu	*rth;
1128c2ecf20Sopenharmony_ci	struct rt6_info	__rcu	*rt6;
1138c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
1148c2ecf20Sopenharmony_ci	struct fib6_table	*fib6_table;
1158c2ecf20Sopenharmony_ci#endif
1168c2ecf20Sopenharmony_ci	u32                     tb_id;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	struct list_head	me_list;   /* entry in vrf_map_elem */
1198c2ecf20Sopenharmony_ci	int			ifindex;
1208c2ecf20Sopenharmony_ci};
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistruct pcpu_dstats {
1238c2ecf20Sopenharmony_ci	u64			tx_pkts;
1248c2ecf20Sopenharmony_ci	u64			tx_bytes;
1258c2ecf20Sopenharmony_ci	u64			tx_drps;
1268c2ecf20Sopenharmony_ci	u64			rx_pkts;
1278c2ecf20Sopenharmony_ci	u64			rx_bytes;
1288c2ecf20Sopenharmony_ci	u64			rx_drps;
1298c2ecf20Sopenharmony_ci	struct u64_stats_sync	syncp;
1308c2ecf20Sopenharmony_ci};
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic void vrf_rx_stats(struct net_device *dev, int len)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	u64_stats_update_begin(&dstats->syncp);
1378c2ecf20Sopenharmony_ci	dstats->rx_pkts++;
1388c2ecf20Sopenharmony_ci	dstats->rx_bytes += len;
1398c2ecf20Sopenharmony_ci	u64_stats_update_end(&dstats->syncp);
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic void vrf_tx_error(struct net_device *vrf_dev, struct sk_buff *skb)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	vrf_dev->stats.tx_errors++;
1458c2ecf20Sopenharmony_ci	kfree_skb(skb);
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic void vrf_get_stats64(struct net_device *dev,
1498c2ecf20Sopenharmony_ci			    struct rtnl_link_stats64 *stats)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	int i;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	for_each_possible_cpu(i) {
1548c2ecf20Sopenharmony_ci		const struct pcpu_dstats *dstats;
1558c2ecf20Sopenharmony_ci		u64 tbytes, tpkts, tdrops, rbytes, rpkts;
1568c2ecf20Sopenharmony_ci		unsigned int start;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		dstats = per_cpu_ptr(dev->dstats, i);
1598c2ecf20Sopenharmony_ci		do {
1608c2ecf20Sopenharmony_ci			start = u64_stats_fetch_begin_irq(&dstats->syncp);
1618c2ecf20Sopenharmony_ci			tbytes = dstats->tx_bytes;
1628c2ecf20Sopenharmony_ci			tpkts = dstats->tx_pkts;
1638c2ecf20Sopenharmony_ci			tdrops = dstats->tx_drps;
1648c2ecf20Sopenharmony_ci			rbytes = dstats->rx_bytes;
1658c2ecf20Sopenharmony_ci			rpkts = dstats->rx_pkts;
1668c2ecf20Sopenharmony_ci		} while (u64_stats_fetch_retry_irq(&dstats->syncp, start));
1678c2ecf20Sopenharmony_ci		stats->tx_bytes += tbytes;
1688c2ecf20Sopenharmony_ci		stats->tx_packets += tpkts;
1698c2ecf20Sopenharmony_ci		stats->tx_dropped += tdrops;
1708c2ecf20Sopenharmony_ci		stats->rx_bytes += rbytes;
1718c2ecf20Sopenharmony_ci		stats->rx_packets += rpkts;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic struct vrf_map *netns_vrf_map(struct net *net)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	struct netns_vrf *nn_vrf = net_generic(net, vrf_net_id);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	return &nn_vrf->vmap;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic struct vrf_map *netns_vrf_map_by_dev(struct net_device *dev)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	return netns_vrf_map(dev_net(dev));
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic int vrf_map_elem_get_vrf_ifindex(struct vrf_map_elem *me)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	struct list_head *me_head = &me->vrf_list;
1908c2ecf20Sopenharmony_ci	struct net_vrf *vrf;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (list_empty(me_head))
1938c2ecf20Sopenharmony_ci		return -ENODEV;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	vrf = list_first_entry(me_head, struct net_vrf, me_list);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	return vrf->ifindex;
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic struct vrf_map_elem *vrf_map_elem_alloc(gfp_t flags)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	struct vrf_map_elem *me;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	me = kmalloc(sizeof(*me), flags);
2058c2ecf20Sopenharmony_ci	if (!me)
2068c2ecf20Sopenharmony_ci		return NULL;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	return me;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic void vrf_map_elem_free(struct vrf_map_elem *me)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	kfree(me);
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic void vrf_map_elem_init(struct vrf_map_elem *me, int table_id,
2178c2ecf20Sopenharmony_ci			      int ifindex, int users)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	me->table_id = table_id;
2208c2ecf20Sopenharmony_ci	me->ifindex = ifindex;
2218c2ecf20Sopenharmony_ci	me->users = users;
2228c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&me->vrf_list);
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic struct vrf_map_elem *vrf_map_lookup_elem(struct vrf_map *vmap,
2268c2ecf20Sopenharmony_ci						u32 table_id)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	struct vrf_map_elem *me;
2298c2ecf20Sopenharmony_ci	u32 key;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	key = jhash_1word(table_id, HASH_INITVAL);
2328c2ecf20Sopenharmony_ci	hash_for_each_possible(vmap->ht, me, hnode, key) {
2338c2ecf20Sopenharmony_ci		if (me->table_id == table_id)
2348c2ecf20Sopenharmony_ci			return me;
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	return NULL;
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic void vrf_map_add_elem(struct vrf_map *vmap, struct vrf_map_elem *me)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	u32 table_id = me->table_id;
2438c2ecf20Sopenharmony_ci	u32 key;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	key = jhash_1word(table_id, HASH_INITVAL);
2468c2ecf20Sopenharmony_ci	hash_add(vmap->ht, &me->hnode, key);
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic void vrf_map_del_elem(struct vrf_map_elem *me)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	hash_del(&me->hnode);
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic void vrf_map_lock(struct vrf_map *vmap) __acquires(&vmap->vmap_lock)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	spin_lock(&vmap->vmap_lock);
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic void vrf_map_unlock(struct vrf_map *vmap) __releases(&vmap->vmap_lock)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	spin_unlock(&vmap->vmap_lock);
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci/* called with rtnl lock held */
2658c2ecf20Sopenharmony_cistatic int
2668c2ecf20Sopenharmony_civrf_map_register_dev(struct net_device *dev, struct netlink_ext_ack *extack)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	struct vrf_map *vmap = netns_vrf_map_by_dev(dev);
2698c2ecf20Sopenharmony_ci	struct net_vrf *vrf = netdev_priv(dev);
2708c2ecf20Sopenharmony_ci	struct vrf_map_elem *new_me, *me;
2718c2ecf20Sopenharmony_ci	u32 table_id = vrf->tb_id;
2728c2ecf20Sopenharmony_ci	bool free_new_me = false;
2738c2ecf20Sopenharmony_ci	int users;
2748c2ecf20Sopenharmony_ci	int res;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	/* we pre-allocate elements used in the spin-locked section (so that we
2778c2ecf20Sopenharmony_ci	 * keep the spinlock as short as possibile).
2788c2ecf20Sopenharmony_ci	 */
2798c2ecf20Sopenharmony_ci	new_me = vrf_map_elem_alloc(GFP_KERNEL);
2808c2ecf20Sopenharmony_ci	if (!new_me)
2818c2ecf20Sopenharmony_ci		return -ENOMEM;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	vrf_map_elem_init(new_me, table_id, dev->ifindex, 0);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	vrf_map_lock(vmap);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	me = vrf_map_lookup_elem(vmap, table_id);
2888c2ecf20Sopenharmony_ci	if (!me) {
2898c2ecf20Sopenharmony_ci		me = new_me;
2908c2ecf20Sopenharmony_ci		vrf_map_add_elem(vmap, me);
2918c2ecf20Sopenharmony_ci		goto link_vrf;
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	/* we already have an entry in the vrf_map, so it means there is (at
2958c2ecf20Sopenharmony_ci	 * least) a vrf registered on the specific table.
2968c2ecf20Sopenharmony_ci	 */
2978c2ecf20Sopenharmony_ci	free_new_me = true;
2988c2ecf20Sopenharmony_ci	if (vmap->strict_mode) {
2998c2ecf20Sopenharmony_ci		/* vrfs cannot share the same table */
3008c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Table is used by another VRF");
3018c2ecf20Sopenharmony_ci		res = -EBUSY;
3028c2ecf20Sopenharmony_ci		goto unlock;
3038c2ecf20Sopenharmony_ci	}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cilink_vrf:
3068c2ecf20Sopenharmony_ci	users = ++me->users;
3078c2ecf20Sopenharmony_ci	if (users == 2)
3088c2ecf20Sopenharmony_ci		++vmap->shared_tables;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	list_add(&vrf->me_list, &me->vrf_list);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	res = 0;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ciunlock:
3158c2ecf20Sopenharmony_ci	vrf_map_unlock(vmap);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	/* clean-up, if needed */
3188c2ecf20Sopenharmony_ci	if (free_new_me)
3198c2ecf20Sopenharmony_ci		vrf_map_elem_free(new_me);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	return res;
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci/* called with rtnl lock held */
3258c2ecf20Sopenharmony_cistatic void vrf_map_unregister_dev(struct net_device *dev)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	struct vrf_map *vmap = netns_vrf_map_by_dev(dev);
3288c2ecf20Sopenharmony_ci	struct net_vrf *vrf = netdev_priv(dev);
3298c2ecf20Sopenharmony_ci	u32 table_id = vrf->tb_id;
3308c2ecf20Sopenharmony_ci	struct vrf_map_elem *me;
3318c2ecf20Sopenharmony_ci	int users;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	vrf_map_lock(vmap);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	me = vrf_map_lookup_elem(vmap, table_id);
3368c2ecf20Sopenharmony_ci	if (!me)
3378c2ecf20Sopenharmony_ci		goto unlock;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	list_del(&vrf->me_list);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	users = --me->users;
3428c2ecf20Sopenharmony_ci	if (users == 1) {
3438c2ecf20Sopenharmony_ci		--vmap->shared_tables;
3448c2ecf20Sopenharmony_ci	} else if (users == 0) {
3458c2ecf20Sopenharmony_ci		vrf_map_del_elem(me);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci		/* no one will refer to this element anymore */
3488c2ecf20Sopenharmony_ci		vrf_map_elem_free(me);
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ciunlock:
3528c2ecf20Sopenharmony_ci	vrf_map_unlock(vmap);
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci/* return the vrf device index associated with the table_id */
3568c2ecf20Sopenharmony_cistatic int vrf_ifindex_lookup_by_table_id(struct net *net, u32 table_id)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	struct vrf_map *vmap = netns_vrf_map(net);
3598c2ecf20Sopenharmony_ci	struct vrf_map_elem *me;
3608c2ecf20Sopenharmony_ci	int ifindex;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	vrf_map_lock(vmap);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	if (!vmap->strict_mode) {
3658c2ecf20Sopenharmony_ci		ifindex = -EPERM;
3668c2ecf20Sopenharmony_ci		goto unlock;
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	me = vrf_map_lookup_elem(vmap, table_id);
3708c2ecf20Sopenharmony_ci	if (!me) {
3718c2ecf20Sopenharmony_ci		ifindex = -ENODEV;
3728c2ecf20Sopenharmony_ci		goto unlock;
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	ifindex = vrf_map_elem_get_vrf_ifindex(me);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ciunlock:
3788c2ecf20Sopenharmony_ci	vrf_map_unlock(vmap);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	return ifindex;
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci/* by default VRF devices do not have a qdisc and are expected
3848c2ecf20Sopenharmony_ci * to be created with only a single queue.
3858c2ecf20Sopenharmony_ci */
3868c2ecf20Sopenharmony_cistatic bool qdisc_tx_is_default(const struct net_device *dev)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	struct netdev_queue *txq;
3898c2ecf20Sopenharmony_ci	struct Qdisc *qdisc;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	if (dev->num_tx_queues > 1)
3928c2ecf20Sopenharmony_ci		return false;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	txq = netdev_get_tx_queue(dev, 0);
3958c2ecf20Sopenharmony_ci	qdisc = rcu_access_pointer(txq->qdisc);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	return !qdisc->enqueue;
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci/* Local traffic destined to local address. Reinsert the packet to rx
4018c2ecf20Sopenharmony_ci * path, similar to loopback handling.
4028c2ecf20Sopenharmony_ci */
4038c2ecf20Sopenharmony_cistatic int vrf_local_xmit(struct sk_buff *skb, struct net_device *dev,
4048c2ecf20Sopenharmony_ci			  struct dst_entry *dst)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	int len = skb->len;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	skb_orphan(skb);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	skb_dst_set(skb, dst);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	/* set pkt_type to avoid skb hitting packet taps twice -
4138c2ecf20Sopenharmony_ci	 * once on Tx and again in Rx processing
4148c2ecf20Sopenharmony_ci	 */
4158c2ecf20Sopenharmony_ci	skb->pkt_type = PACKET_LOOPBACK;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	skb->protocol = eth_type_trans(skb, dev);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	if (likely(netif_rx(skb) == NET_RX_SUCCESS))
4208c2ecf20Sopenharmony_ci		vrf_rx_stats(dev, len);
4218c2ecf20Sopenharmony_ci	else
4228c2ecf20Sopenharmony_ci		this_cpu_inc(dev->dstats->rx_drps);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic void vrf_nf_set_untracked(struct sk_buff *skb)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	if (skb_get_nfct(skb) == 0)
4308c2ecf20Sopenharmony_ci		nf_ct_set(skb, NULL, IP_CT_UNTRACKED);
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_cistatic void vrf_nf_reset_ct(struct sk_buff *skb)
4348c2ecf20Sopenharmony_ci{
4358c2ecf20Sopenharmony_ci	if (skb_get_nfct(skb) == IP_CT_UNTRACKED)
4368c2ecf20Sopenharmony_ci		nf_reset_ct(skb);
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
4408c2ecf20Sopenharmony_cistatic int vrf_ip6_local_out(struct net *net, struct sock *sk,
4418c2ecf20Sopenharmony_ci			     struct sk_buff *skb)
4428c2ecf20Sopenharmony_ci{
4438c2ecf20Sopenharmony_ci	int err;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	vrf_nf_reset_ct(skb);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	err = nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net,
4488c2ecf20Sopenharmony_ci		      sk, skb, NULL, skb_dst(skb)->dev, dst_output);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	if (likely(err == 1))
4518c2ecf20Sopenharmony_ci		err = dst_output(net, sk, skb);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	return err;
4548c2ecf20Sopenharmony_ci}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cistatic netdev_tx_t vrf_process_v6_outbound(struct sk_buff *skb,
4578c2ecf20Sopenharmony_ci					   struct net_device *dev)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	const struct ipv6hdr *iph;
4608c2ecf20Sopenharmony_ci	struct net *net = dev_net(skb->dev);
4618c2ecf20Sopenharmony_ci	struct flowi6 fl6;
4628c2ecf20Sopenharmony_ci	int ret = NET_XMIT_DROP;
4638c2ecf20Sopenharmony_ci	struct dst_entry *dst;
4648c2ecf20Sopenharmony_ci	struct dst_entry *dst_null = &net->ipv6.ip6_null_entry->dst;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	if (!pskb_may_pull(skb, ETH_HLEN + sizeof(struct ipv6hdr)))
4678c2ecf20Sopenharmony_ci		goto err;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	iph = ipv6_hdr(skb);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	memset(&fl6, 0, sizeof(fl6));
4728c2ecf20Sopenharmony_ci	/* needed to match OIF rule */
4738c2ecf20Sopenharmony_ci	fl6.flowi6_oif = dev->ifindex;
4748c2ecf20Sopenharmony_ci	fl6.flowi6_iif = LOOPBACK_IFINDEX;
4758c2ecf20Sopenharmony_ci	fl6.daddr = iph->daddr;
4768c2ecf20Sopenharmony_ci	fl6.saddr = iph->saddr;
4778c2ecf20Sopenharmony_ci	fl6.flowlabel = ip6_flowinfo(iph);
4788c2ecf20Sopenharmony_ci	fl6.flowi6_mark = skb->mark;
4798c2ecf20Sopenharmony_ci	fl6.flowi6_proto = iph->nexthdr;
4808c2ecf20Sopenharmony_ci	fl6.flowi6_flags = FLOWI_FLAG_SKIP_NH_OIF;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	dst = ip6_dst_lookup_flow(net, NULL, &fl6, NULL);
4838c2ecf20Sopenharmony_ci	if (IS_ERR(dst) || dst == dst_null)
4848c2ecf20Sopenharmony_ci		goto err;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	skb_dst_drop(skb);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	/* if dst.dev is loopback or the VRF device again this is locally
4898c2ecf20Sopenharmony_ci	 * originated traffic destined to a local address. Short circuit
4908c2ecf20Sopenharmony_ci	 * to Rx path
4918c2ecf20Sopenharmony_ci	 */
4928c2ecf20Sopenharmony_ci	if (dst->dev == dev)
4938c2ecf20Sopenharmony_ci		return vrf_local_xmit(skb, dev, dst);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	skb_dst_set(skb, dst);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	/* strip the ethernet header added for pass through VRF device */
4988c2ecf20Sopenharmony_ci	__skb_pull(skb, skb_network_offset(skb));
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
5018c2ecf20Sopenharmony_ci	ret = vrf_ip6_local_out(net, skb->sk, skb);
5028c2ecf20Sopenharmony_ci	if (unlikely(net_xmit_eval(ret)))
5038c2ecf20Sopenharmony_ci		dev->stats.tx_errors++;
5048c2ecf20Sopenharmony_ci	else
5058c2ecf20Sopenharmony_ci		ret = NET_XMIT_SUCCESS;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	return ret;
5088c2ecf20Sopenharmony_cierr:
5098c2ecf20Sopenharmony_ci	vrf_tx_error(dev, skb);
5108c2ecf20Sopenharmony_ci	return NET_XMIT_DROP;
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ci#else
5138c2ecf20Sopenharmony_cistatic netdev_tx_t vrf_process_v6_outbound(struct sk_buff *skb,
5148c2ecf20Sopenharmony_ci					   struct net_device *dev)
5158c2ecf20Sopenharmony_ci{
5168c2ecf20Sopenharmony_ci	vrf_tx_error(dev, skb);
5178c2ecf20Sopenharmony_ci	return NET_XMIT_DROP;
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci#endif
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci/* based on ip_local_out; can't use it b/c the dst is switched pointing to us */
5228c2ecf20Sopenharmony_cistatic int vrf_ip_local_out(struct net *net, struct sock *sk,
5238c2ecf20Sopenharmony_ci			    struct sk_buff *skb)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	int err;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	vrf_nf_reset_ct(skb);
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	err = nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, net, sk,
5308c2ecf20Sopenharmony_ci		      skb, NULL, skb_dst(skb)->dev, dst_output);
5318c2ecf20Sopenharmony_ci	if (likely(err == 1))
5328c2ecf20Sopenharmony_ci		err = dst_output(net, sk, skb);
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	return err;
5358c2ecf20Sopenharmony_ci}
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_cistatic netdev_tx_t vrf_process_v4_outbound(struct sk_buff *skb,
5388c2ecf20Sopenharmony_ci					   struct net_device *vrf_dev)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	struct iphdr *ip4h;
5418c2ecf20Sopenharmony_ci	int ret = NET_XMIT_DROP;
5428c2ecf20Sopenharmony_ci	struct flowi4 fl4;
5438c2ecf20Sopenharmony_ci	struct net *net = dev_net(vrf_dev);
5448c2ecf20Sopenharmony_ci	struct rtable *rt;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	if (!pskb_may_pull(skb, ETH_HLEN + sizeof(struct iphdr)))
5478c2ecf20Sopenharmony_ci		goto err;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	ip4h = ip_hdr(skb);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	memset(&fl4, 0, sizeof(fl4));
5528c2ecf20Sopenharmony_ci	/* needed to match OIF rule */
5538c2ecf20Sopenharmony_ci	fl4.flowi4_oif = vrf_dev->ifindex;
5548c2ecf20Sopenharmony_ci	fl4.flowi4_iif = LOOPBACK_IFINDEX;
5558c2ecf20Sopenharmony_ci	fl4.flowi4_tos = RT_TOS(ip4h->tos);
5568c2ecf20Sopenharmony_ci	fl4.flowi4_flags = FLOWI_FLAG_ANYSRC | FLOWI_FLAG_SKIP_NH_OIF;
5578c2ecf20Sopenharmony_ci	fl4.flowi4_proto = ip4h->protocol;
5588c2ecf20Sopenharmony_ci	fl4.daddr = ip4h->daddr;
5598c2ecf20Sopenharmony_ci	fl4.saddr = ip4h->saddr;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	rt = ip_route_output_flow(net, &fl4, NULL);
5628c2ecf20Sopenharmony_ci	if (IS_ERR(rt))
5638c2ecf20Sopenharmony_ci		goto err;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	skb_dst_drop(skb);
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	/* if dst.dev is loopback or the VRF device again this is locally
5688c2ecf20Sopenharmony_ci	 * originated traffic destined to a local address. Short circuit
5698c2ecf20Sopenharmony_ci	 * to Rx path
5708c2ecf20Sopenharmony_ci	 */
5718c2ecf20Sopenharmony_ci	if (rt->dst.dev == vrf_dev)
5728c2ecf20Sopenharmony_ci		return vrf_local_xmit(skb, vrf_dev, &rt->dst);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	skb_dst_set(skb, &rt->dst);
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	/* strip the ethernet header added for pass through VRF device */
5778c2ecf20Sopenharmony_ci	__skb_pull(skb, skb_network_offset(skb));
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	if (!ip4h->saddr) {
5808c2ecf20Sopenharmony_ci		ip4h->saddr = inet_select_addr(skb_dst(skb)->dev, 0,
5818c2ecf20Sopenharmony_ci					       RT_SCOPE_LINK);
5828c2ecf20Sopenharmony_ci	}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
5858c2ecf20Sopenharmony_ci	ret = vrf_ip_local_out(dev_net(skb_dst(skb)->dev), skb->sk, skb);
5868c2ecf20Sopenharmony_ci	if (unlikely(net_xmit_eval(ret)))
5878c2ecf20Sopenharmony_ci		vrf_dev->stats.tx_errors++;
5888c2ecf20Sopenharmony_ci	else
5898c2ecf20Sopenharmony_ci		ret = NET_XMIT_SUCCESS;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ciout:
5928c2ecf20Sopenharmony_ci	return ret;
5938c2ecf20Sopenharmony_cierr:
5948c2ecf20Sopenharmony_ci	vrf_tx_error(vrf_dev, skb);
5958c2ecf20Sopenharmony_ci	goto out;
5968c2ecf20Sopenharmony_ci}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_cistatic netdev_tx_t is_ip_tx_frame(struct sk_buff *skb, struct net_device *dev)
5998c2ecf20Sopenharmony_ci{
6008c2ecf20Sopenharmony_ci	switch (skb->protocol) {
6018c2ecf20Sopenharmony_ci	case htons(ETH_P_IP):
6028c2ecf20Sopenharmony_ci		return vrf_process_v4_outbound(skb, dev);
6038c2ecf20Sopenharmony_ci	case htons(ETH_P_IPV6):
6048c2ecf20Sopenharmony_ci		return vrf_process_v6_outbound(skb, dev);
6058c2ecf20Sopenharmony_ci	default:
6068c2ecf20Sopenharmony_ci		vrf_tx_error(dev, skb);
6078c2ecf20Sopenharmony_ci		return NET_XMIT_DROP;
6088c2ecf20Sopenharmony_ci	}
6098c2ecf20Sopenharmony_ci}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_cistatic netdev_tx_t vrf_xmit(struct sk_buff *skb, struct net_device *dev)
6128c2ecf20Sopenharmony_ci{
6138c2ecf20Sopenharmony_ci	int len = skb->len;
6148c2ecf20Sopenharmony_ci	netdev_tx_t ret = is_ip_tx_frame(skb, dev);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
6178c2ecf20Sopenharmony_ci		struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci		u64_stats_update_begin(&dstats->syncp);
6208c2ecf20Sopenharmony_ci		dstats->tx_pkts++;
6218c2ecf20Sopenharmony_ci		dstats->tx_bytes += len;
6228c2ecf20Sopenharmony_ci		u64_stats_update_end(&dstats->syncp);
6238c2ecf20Sopenharmony_ci	} else {
6248c2ecf20Sopenharmony_ci		this_cpu_inc(dev->dstats->tx_drps);
6258c2ecf20Sopenharmony_ci	}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	return ret;
6288c2ecf20Sopenharmony_ci}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_cistatic void vrf_finish_direct(struct sk_buff *skb)
6318c2ecf20Sopenharmony_ci{
6328c2ecf20Sopenharmony_ci	struct net_device *vrf_dev = skb->dev;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	if (!list_empty(&vrf_dev->ptype_all) &&
6358c2ecf20Sopenharmony_ci	    likely(skb_headroom(skb) >= ETH_HLEN)) {
6368c2ecf20Sopenharmony_ci		struct ethhdr *eth = skb_push(skb, ETH_HLEN);
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci		ether_addr_copy(eth->h_source, vrf_dev->dev_addr);
6398c2ecf20Sopenharmony_ci		eth_zero_addr(eth->h_dest);
6408c2ecf20Sopenharmony_ci		eth->h_proto = skb->protocol;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci		rcu_read_lock_bh();
6438c2ecf20Sopenharmony_ci		dev_queue_xmit_nit(skb, vrf_dev);
6448c2ecf20Sopenharmony_ci		rcu_read_unlock_bh();
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci		skb_pull(skb, ETH_HLEN);
6478c2ecf20Sopenharmony_ci	}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	vrf_nf_reset_ct(skb);
6508c2ecf20Sopenharmony_ci}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
6538c2ecf20Sopenharmony_ci/* modelled after ip6_finish_output2 */
6548c2ecf20Sopenharmony_cistatic int vrf_finish_output6(struct net *net, struct sock *sk,
6558c2ecf20Sopenharmony_ci			      struct sk_buff *skb)
6568c2ecf20Sopenharmony_ci{
6578c2ecf20Sopenharmony_ci	struct dst_entry *dst = skb_dst(skb);
6588c2ecf20Sopenharmony_ci	struct net_device *dev = dst->dev;
6598c2ecf20Sopenharmony_ci	const struct in6_addr *nexthop;
6608c2ecf20Sopenharmony_ci	struct neighbour *neigh;
6618c2ecf20Sopenharmony_ci	int ret;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	vrf_nf_reset_ct(skb);
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	skb->protocol = htons(ETH_P_IPV6);
6668c2ecf20Sopenharmony_ci	skb->dev = dev;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	rcu_read_lock_bh();
6698c2ecf20Sopenharmony_ci	nexthop = rt6_nexthop((struct rt6_info *)dst, &ipv6_hdr(skb)->daddr);
6708c2ecf20Sopenharmony_ci	neigh = __ipv6_neigh_lookup_noref(dst->dev, nexthop);
6718c2ecf20Sopenharmony_ci	if (unlikely(!neigh))
6728c2ecf20Sopenharmony_ci		neigh = __neigh_create(&nd_tbl, nexthop, dst->dev, false);
6738c2ecf20Sopenharmony_ci	if (!IS_ERR(neigh)) {
6748c2ecf20Sopenharmony_ci		sock_confirm_neigh(skb, neigh);
6758c2ecf20Sopenharmony_ci		ret = neigh_output(neigh, skb, false);
6768c2ecf20Sopenharmony_ci		rcu_read_unlock_bh();
6778c2ecf20Sopenharmony_ci		return ret;
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci	rcu_read_unlock_bh();
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	IP6_INC_STATS(dev_net(dst->dev),
6828c2ecf20Sopenharmony_ci		      ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
6838c2ecf20Sopenharmony_ci	kfree_skb(skb);
6848c2ecf20Sopenharmony_ci	return -EINVAL;
6858c2ecf20Sopenharmony_ci}
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci/* modelled after ip6_output */
6888c2ecf20Sopenharmony_cistatic int vrf_output6(struct net *net, struct sock *sk, struct sk_buff *skb)
6898c2ecf20Sopenharmony_ci{
6908c2ecf20Sopenharmony_ci	return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING,
6918c2ecf20Sopenharmony_ci			    net, sk, skb, NULL, skb_dst(skb)->dev,
6928c2ecf20Sopenharmony_ci			    vrf_finish_output6,
6938c2ecf20Sopenharmony_ci			    !(IP6CB(skb)->flags & IP6SKB_REROUTED));
6948c2ecf20Sopenharmony_ci}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci/* set dst on skb to send packet to us via dev_xmit path. Allows
6978c2ecf20Sopenharmony_ci * packet to go through device based features such as qdisc, netfilter
6988c2ecf20Sopenharmony_ci * hooks and packet sockets with skb->dev set to vrf device.
6998c2ecf20Sopenharmony_ci */
7008c2ecf20Sopenharmony_cistatic struct sk_buff *vrf_ip6_out_redirect(struct net_device *vrf_dev,
7018c2ecf20Sopenharmony_ci					    struct sk_buff *skb)
7028c2ecf20Sopenharmony_ci{
7038c2ecf20Sopenharmony_ci	struct net_vrf *vrf = netdev_priv(vrf_dev);
7048c2ecf20Sopenharmony_ci	struct dst_entry *dst = NULL;
7058c2ecf20Sopenharmony_ci	struct rt6_info *rt6;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	rcu_read_lock();
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	rt6 = rcu_dereference(vrf->rt6);
7108c2ecf20Sopenharmony_ci	if (likely(rt6)) {
7118c2ecf20Sopenharmony_ci		dst = &rt6->dst;
7128c2ecf20Sopenharmony_ci		dst_hold(dst);
7138c2ecf20Sopenharmony_ci	}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	rcu_read_unlock();
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	if (unlikely(!dst)) {
7188c2ecf20Sopenharmony_ci		vrf_tx_error(vrf_dev, skb);
7198c2ecf20Sopenharmony_ci		return NULL;
7208c2ecf20Sopenharmony_ci	}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	skb_dst_drop(skb);
7238c2ecf20Sopenharmony_ci	skb_dst_set(skb, dst);
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	return skb;
7268c2ecf20Sopenharmony_ci}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_cistatic int vrf_output6_direct_finish(struct net *net, struct sock *sk,
7298c2ecf20Sopenharmony_ci				     struct sk_buff *skb)
7308c2ecf20Sopenharmony_ci{
7318c2ecf20Sopenharmony_ci	vrf_finish_direct(skb);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	return vrf_ip6_local_out(net, sk, skb);
7348c2ecf20Sopenharmony_ci}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_cistatic int vrf_output6_direct(struct net *net, struct sock *sk,
7378c2ecf20Sopenharmony_ci			      struct sk_buff *skb)
7388c2ecf20Sopenharmony_ci{
7398c2ecf20Sopenharmony_ci	int err = 1;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	skb->protocol = htons(ETH_P_IPV6);
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	if (!(IPCB(skb)->flags & IPSKB_REROUTED))
7448c2ecf20Sopenharmony_ci		err = nf_hook(NFPROTO_IPV6, NF_INET_POST_ROUTING, net, sk, skb,
7458c2ecf20Sopenharmony_ci			      NULL, skb->dev, vrf_output6_direct_finish);
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	if (likely(err == 1))
7488c2ecf20Sopenharmony_ci		vrf_finish_direct(skb);
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	return err;
7518c2ecf20Sopenharmony_ci}
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_cistatic int vrf_ip6_out_direct_finish(struct net *net, struct sock *sk,
7548c2ecf20Sopenharmony_ci				     struct sk_buff *skb)
7558c2ecf20Sopenharmony_ci{
7568c2ecf20Sopenharmony_ci	int err;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	err = vrf_output6_direct(net, sk, skb);
7598c2ecf20Sopenharmony_ci	if (likely(err == 1))
7608c2ecf20Sopenharmony_ci		err = vrf_ip6_local_out(net, sk, skb);
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	return err;
7638c2ecf20Sopenharmony_ci}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_cistatic struct sk_buff *vrf_ip6_out_direct(struct net_device *vrf_dev,
7668c2ecf20Sopenharmony_ci					  struct sock *sk,
7678c2ecf20Sopenharmony_ci					  struct sk_buff *skb)
7688c2ecf20Sopenharmony_ci{
7698c2ecf20Sopenharmony_ci	struct net *net = dev_net(vrf_dev);
7708c2ecf20Sopenharmony_ci	int err;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	skb->dev = vrf_dev;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	err = nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, sk,
7758c2ecf20Sopenharmony_ci		      skb, NULL, vrf_dev, vrf_ip6_out_direct_finish);
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	if (likely(err == 1))
7788c2ecf20Sopenharmony_ci		err = vrf_output6_direct(net, sk, skb);
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	if (likely(err == 1))
7818c2ecf20Sopenharmony_ci		return skb;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	return NULL;
7848c2ecf20Sopenharmony_ci}
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_cistatic struct sk_buff *vrf_ip6_out(struct net_device *vrf_dev,
7878c2ecf20Sopenharmony_ci				   struct sock *sk,
7888c2ecf20Sopenharmony_ci				   struct sk_buff *skb)
7898c2ecf20Sopenharmony_ci{
7908c2ecf20Sopenharmony_ci	/* don't divert link scope packets */
7918c2ecf20Sopenharmony_ci	if (rt6_need_strict(&ipv6_hdr(skb)->daddr))
7928c2ecf20Sopenharmony_ci		return skb;
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	vrf_nf_set_untracked(skb);
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	if (qdisc_tx_is_default(vrf_dev) ||
7978c2ecf20Sopenharmony_ci	    IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED)
7988c2ecf20Sopenharmony_ci		return vrf_ip6_out_direct(vrf_dev, sk, skb);
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	return vrf_ip6_out_redirect(vrf_dev, skb);
8018c2ecf20Sopenharmony_ci}
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci/* holding rtnl */
8048c2ecf20Sopenharmony_cistatic void vrf_rt6_release(struct net_device *dev, struct net_vrf *vrf)
8058c2ecf20Sopenharmony_ci{
8068c2ecf20Sopenharmony_ci	struct rt6_info *rt6 = rtnl_dereference(vrf->rt6);
8078c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
8088c2ecf20Sopenharmony_ci	struct dst_entry *dst;
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(vrf->rt6, NULL);
8118c2ecf20Sopenharmony_ci	synchronize_rcu();
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	/* move dev in dst's to loopback so this VRF device can be deleted
8148c2ecf20Sopenharmony_ci	 * - based on dst_ifdown
8158c2ecf20Sopenharmony_ci	 */
8168c2ecf20Sopenharmony_ci	if (rt6) {
8178c2ecf20Sopenharmony_ci		dst = &rt6->dst;
8188c2ecf20Sopenharmony_ci		dev_put(dst->dev);
8198c2ecf20Sopenharmony_ci		dst->dev = net->loopback_dev;
8208c2ecf20Sopenharmony_ci		dev_hold(dst->dev);
8218c2ecf20Sopenharmony_ci		dst_release(dst);
8228c2ecf20Sopenharmony_ci	}
8238c2ecf20Sopenharmony_ci}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_cistatic int vrf_rt6_create(struct net_device *dev)
8268c2ecf20Sopenharmony_ci{
8278c2ecf20Sopenharmony_ci	int flags = DST_NOPOLICY | DST_NOXFRM;
8288c2ecf20Sopenharmony_ci	struct net_vrf *vrf = netdev_priv(dev);
8298c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
8308c2ecf20Sopenharmony_ci	struct rt6_info *rt6;
8318c2ecf20Sopenharmony_ci	int rc = -ENOMEM;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	/* IPv6 can be CONFIG enabled and then disabled runtime */
8348c2ecf20Sopenharmony_ci	if (!ipv6_mod_enabled())
8358c2ecf20Sopenharmony_ci		return 0;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	vrf->fib6_table = fib6_new_table(net, vrf->tb_id);
8388c2ecf20Sopenharmony_ci	if (!vrf->fib6_table)
8398c2ecf20Sopenharmony_ci		goto out;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	/* create a dst for routing packets out a VRF device */
8428c2ecf20Sopenharmony_ci	rt6 = ip6_dst_alloc(net, dev, flags);
8438c2ecf20Sopenharmony_ci	if (!rt6)
8448c2ecf20Sopenharmony_ci		goto out;
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	rt6->dst.output	= vrf_output6;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	rcu_assign_pointer(vrf->rt6, rt6);
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	rc = 0;
8518c2ecf20Sopenharmony_ciout:
8528c2ecf20Sopenharmony_ci	return rc;
8538c2ecf20Sopenharmony_ci}
8548c2ecf20Sopenharmony_ci#else
8558c2ecf20Sopenharmony_cistatic struct sk_buff *vrf_ip6_out(struct net_device *vrf_dev,
8568c2ecf20Sopenharmony_ci				   struct sock *sk,
8578c2ecf20Sopenharmony_ci				   struct sk_buff *skb)
8588c2ecf20Sopenharmony_ci{
8598c2ecf20Sopenharmony_ci	return skb;
8608c2ecf20Sopenharmony_ci}
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_cistatic void vrf_rt6_release(struct net_device *dev, struct net_vrf *vrf)
8638c2ecf20Sopenharmony_ci{
8648c2ecf20Sopenharmony_ci}
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_cistatic int vrf_rt6_create(struct net_device *dev)
8678c2ecf20Sopenharmony_ci{
8688c2ecf20Sopenharmony_ci	return 0;
8698c2ecf20Sopenharmony_ci}
8708c2ecf20Sopenharmony_ci#endif
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci/* modelled after ip_finish_output2 */
8738c2ecf20Sopenharmony_cistatic int vrf_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb)
8748c2ecf20Sopenharmony_ci{
8758c2ecf20Sopenharmony_ci	struct dst_entry *dst = skb_dst(skb);
8768c2ecf20Sopenharmony_ci	struct rtable *rt = (struct rtable *)dst;
8778c2ecf20Sopenharmony_ci	struct net_device *dev = dst->dev;
8788c2ecf20Sopenharmony_ci	unsigned int hh_len = LL_RESERVED_SPACE(dev);
8798c2ecf20Sopenharmony_ci	struct neighbour *neigh;
8808c2ecf20Sopenharmony_ci	bool is_v6gw = false;
8818c2ecf20Sopenharmony_ci	int ret = -EINVAL;
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	vrf_nf_reset_ct(skb);
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	/* Be paranoid, rather than too clever. */
8868c2ecf20Sopenharmony_ci	if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
8878c2ecf20Sopenharmony_ci		struct sk_buff *skb2;
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci		skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));
8908c2ecf20Sopenharmony_ci		if (!skb2) {
8918c2ecf20Sopenharmony_ci			ret = -ENOMEM;
8928c2ecf20Sopenharmony_ci			goto err;
8938c2ecf20Sopenharmony_ci		}
8948c2ecf20Sopenharmony_ci		if (skb->sk)
8958c2ecf20Sopenharmony_ci			skb_set_owner_w(skb2, skb->sk);
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci		consume_skb(skb);
8988c2ecf20Sopenharmony_ci		skb = skb2;
8998c2ecf20Sopenharmony_ci	}
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	rcu_read_lock_bh();
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	neigh = ip_neigh_for_gw(rt, skb, &is_v6gw);
9048c2ecf20Sopenharmony_ci	if (!IS_ERR(neigh)) {
9058c2ecf20Sopenharmony_ci		sock_confirm_neigh(skb, neigh);
9068c2ecf20Sopenharmony_ci		/* if crossing protocols, can not use the cached header */
9078c2ecf20Sopenharmony_ci		ret = neigh_output(neigh, skb, is_v6gw);
9088c2ecf20Sopenharmony_ci		rcu_read_unlock_bh();
9098c2ecf20Sopenharmony_ci		return ret;
9108c2ecf20Sopenharmony_ci	}
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	rcu_read_unlock_bh();
9138c2ecf20Sopenharmony_cierr:
9148c2ecf20Sopenharmony_ci	vrf_tx_error(skb->dev, skb);
9158c2ecf20Sopenharmony_ci	return ret;
9168c2ecf20Sopenharmony_ci}
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_cistatic int vrf_output(struct net *net, struct sock *sk, struct sk_buff *skb)
9198c2ecf20Sopenharmony_ci{
9208c2ecf20Sopenharmony_ci	struct net_device *dev = skb_dst(skb)->dev;
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len);
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	skb->dev = dev;
9258c2ecf20Sopenharmony_ci	skb->protocol = htons(ETH_P_IP);
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING,
9288c2ecf20Sopenharmony_ci			    net, sk, skb, NULL, dev,
9298c2ecf20Sopenharmony_ci			    vrf_finish_output,
9308c2ecf20Sopenharmony_ci			    !(IPCB(skb)->flags & IPSKB_REROUTED));
9318c2ecf20Sopenharmony_ci}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci/* set dst on skb to send packet to us via dev_xmit path. Allows
9348c2ecf20Sopenharmony_ci * packet to go through device based features such as qdisc, netfilter
9358c2ecf20Sopenharmony_ci * hooks and packet sockets with skb->dev set to vrf device.
9368c2ecf20Sopenharmony_ci */
9378c2ecf20Sopenharmony_cistatic struct sk_buff *vrf_ip_out_redirect(struct net_device *vrf_dev,
9388c2ecf20Sopenharmony_ci					   struct sk_buff *skb)
9398c2ecf20Sopenharmony_ci{
9408c2ecf20Sopenharmony_ci	struct net_vrf *vrf = netdev_priv(vrf_dev);
9418c2ecf20Sopenharmony_ci	struct dst_entry *dst = NULL;
9428c2ecf20Sopenharmony_ci	struct rtable *rth;
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	rcu_read_lock();
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	rth = rcu_dereference(vrf->rth);
9478c2ecf20Sopenharmony_ci	if (likely(rth)) {
9488c2ecf20Sopenharmony_ci		dst = &rth->dst;
9498c2ecf20Sopenharmony_ci		dst_hold(dst);
9508c2ecf20Sopenharmony_ci	}
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	rcu_read_unlock();
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	if (unlikely(!dst)) {
9558c2ecf20Sopenharmony_ci		vrf_tx_error(vrf_dev, skb);
9568c2ecf20Sopenharmony_ci		return NULL;
9578c2ecf20Sopenharmony_ci	}
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	skb_dst_drop(skb);
9608c2ecf20Sopenharmony_ci	skb_dst_set(skb, dst);
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	return skb;
9638c2ecf20Sopenharmony_ci}
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_cistatic int vrf_output_direct_finish(struct net *net, struct sock *sk,
9668c2ecf20Sopenharmony_ci				    struct sk_buff *skb)
9678c2ecf20Sopenharmony_ci{
9688c2ecf20Sopenharmony_ci	vrf_finish_direct(skb);
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	return vrf_ip_local_out(net, sk, skb);
9718c2ecf20Sopenharmony_ci}
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_cistatic int vrf_output_direct(struct net *net, struct sock *sk,
9748c2ecf20Sopenharmony_ci			     struct sk_buff *skb)
9758c2ecf20Sopenharmony_ci{
9768c2ecf20Sopenharmony_ci	int err = 1;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	skb->protocol = htons(ETH_P_IP);
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	if (!(IPCB(skb)->flags & IPSKB_REROUTED))
9818c2ecf20Sopenharmony_ci		err = nf_hook(NFPROTO_IPV4, NF_INET_POST_ROUTING, net, sk, skb,
9828c2ecf20Sopenharmony_ci			      NULL, skb->dev, vrf_output_direct_finish);
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci	if (likely(err == 1))
9858c2ecf20Sopenharmony_ci		vrf_finish_direct(skb);
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	return err;
9888c2ecf20Sopenharmony_ci}
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_cistatic int vrf_ip_out_direct_finish(struct net *net, struct sock *sk,
9918c2ecf20Sopenharmony_ci				    struct sk_buff *skb)
9928c2ecf20Sopenharmony_ci{
9938c2ecf20Sopenharmony_ci	int err;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	err = vrf_output_direct(net, sk, skb);
9968c2ecf20Sopenharmony_ci	if (likely(err == 1))
9978c2ecf20Sopenharmony_ci		err = vrf_ip_local_out(net, sk, skb);
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	return err;
10008c2ecf20Sopenharmony_ci}
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_cistatic struct sk_buff *vrf_ip_out_direct(struct net_device *vrf_dev,
10038c2ecf20Sopenharmony_ci					 struct sock *sk,
10048c2ecf20Sopenharmony_ci					 struct sk_buff *skb)
10058c2ecf20Sopenharmony_ci{
10068c2ecf20Sopenharmony_ci	struct net *net = dev_net(vrf_dev);
10078c2ecf20Sopenharmony_ci	int err;
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	skb->dev = vrf_dev;
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	err = nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, net, sk,
10128c2ecf20Sopenharmony_ci		      skb, NULL, vrf_dev, vrf_ip_out_direct_finish);
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	if (likely(err == 1))
10158c2ecf20Sopenharmony_ci		err = vrf_output_direct(net, sk, skb);
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	if (likely(err == 1))
10188c2ecf20Sopenharmony_ci		return skb;
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	return NULL;
10218c2ecf20Sopenharmony_ci}
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_cistatic struct sk_buff *vrf_ip_out(struct net_device *vrf_dev,
10248c2ecf20Sopenharmony_ci				  struct sock *sk,
10258c2ecf20Sopenharmony_ci				  struct sk_buff *skb)
10268c2ecf20Sopenharmony_ci{
10278c2ecf20Sopenharmony_ci	/* don't divert multicast or local broadcast */
10288c2ecf20Sopenharmony_ci	if (ipv4_is_multicast(ip_hdr(skb)->daddr) ||
10298c2ecf20Sopenharmony_ci	    ipv4_is_lbcast(ip_hdr(skb)->daddr))
10308c2ecf20Sopenharmony_ci		return skb;
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	vrf_nf_set_untracked(skb);
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	if (qdisc_tx_is_default(vrf_dev) ||
10358c2ecf20Sopenharmony_ci	    IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED)
10368c2ecf20Sopenharmony_ci		return vrf_ip_out_direct(vrf_dev, sk, skb);
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	return vrf_ip_out_redirect(vrf_dev, skb);
10398c2ecf20Sopenharmony_ci}
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci/* called with rcu lock held */
10428c2ecf20Sopenharmony_cistatic struct sk_buff *vrf_l3_out(struct net_device *vrf_dev,
10438c2ecf20Sopenharmony_ci				  struct sock *sk,
10448c2ecf20Sopenharmony_ci				  struct sk_buff *skb,
10458c2ecf20Sopenharmony_ci				  u16 proto)
10468c2ecf20Sopenharmony_ci{
10478c2ecf20Sopenharmony_ci	switch (proto) {
10488c2ecf20Sopenharmony_ci	case AF_INET:
10498c2ecf20Sopenharmony_ci		return vrf_ip_out(vrf_dev, sk, skb);
10508c2ecf20Sopenharmony_ci	case AF_INET6:
10518c2ecf20Sopenharmony_ci		return vrf_ip6_out(vrf_dev, sk, skb);
10528c2ecf20Sopenharmony_ci	}
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	return skb;
10558c2ecf20Sopenharmony_ci}
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci/* holding rtnl */
10588c2ecf20Sopenharmony_cistatic void vrf_rtable_release(struct net_device *dev, struct net_vrf *vrf)
10598c2ecf20Sopenharmony_ci{
10608c2ecf20Sopenharmony_ci	struct rtable *rth = rtnl_dereference(vrf->rth);
10618c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
10628c2ecf20Sopenharmony_ci	struct dst_entry *dst;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(vrf->rth, NULL);
10658c2ecf20Sopenharmony_ci	synchronize_rcu();
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	/* move dev in dst's to loopback so this VRF device can be deleted
10688c2ecf20Sopenharmony_ci	 * - based on dst_ifdown
10698c2ecf20Sopenharmony_ci	 */
10708c2ecf20Sopenharmony_ci	if (rth) {
10718c2ecf20Sopenharmony_ci		dst = &rth->dst;
10728c2ecf20Sopenharmony_ci		dev_put(dst->dev);
10738c2ecf20Sopenharmony_ci		dst->dev = net->loopback_dev;
10748c2ecf20Sopenharmony_ci		dev_hold(dst->dev);
10758c2ecf20Sopenharmony_ci		dst_release(dst);
10768c2ecf20Sopenharmony_ci	}
10778c2ecf20Sopenharmony_ci}
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_cistatic int vrf_rtable_create(struct net_device *dev)
10808c2ecf20Sopenharmony_ci{
10818c2ecf20Sopenharmony_ci	struct net_vrf *vrf = netdev_priv(dev);
10828c2ecf20Sopenharmony_ci	struct rtable *rth;
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	if (!fib_new_table(dev_net(dev), vrf->tb_id))
10858c2ecf20Sopenharmony_ci		return -ENOMEM;
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	/* create a dst for routing packets out through a VRF device */
10888c2ecf20Sopenharmony_ci	rth = rt_dst_alloc(dev, 0, RTN_UNICAST, 1, 1);
10898c2ecf20Sopenharmony_ci	if (!rth)
10908c2ecf20Sopenharmony_ci		return -ENOMEM;
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	rth->dst.output	= vrf_output;
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	rcu_assign_pointer(vrf->rth, rth);
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	return 0;
10978c2ecf20Sopenharmony_ci}
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci/**************************** device handling ********************/
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci/* cycle interface to flush neighbor cache and move routes across tables */
11028c2ecf20Sopenharmony_cistatic void cycle_netdev(struct net_device *dev,
11038c2ecf20Sopenharmony_ci			 struct netlink_ext_ack *extack)
11048c2ecf20Sopenharmony_ci{
11058c2ecf20Sopenharmony_ci	unsigned int flags = dev->flags;
11068c2ecf20Sopenharmony_ci	int ret;
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	if (!netif_running(dev))
11098c2ecf20Sopenharmony_ci		return;
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	ret = dev_change_flags(dev, flags & ~IFF_UP, extack);
11128c2ecf20Sopenharmony_ci	if (ret >= 0)
11138c2ecf20Sopenharmony_ci		ret = dev_change_flags(dev, flags, extack);
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	if (ret < 0) {
11168c2ecf20Sopenharmony_ci		netdev_err(dev,
11178c2ecf20Sopenharmony_ci			   "Failed to cycle device %s; route tables might be wrong!\n",
11188c2ecf20Sopenharmony_ci			   dev->name);
11198c2ecf20Sopenharmony_ci	}
11208c2ecf20Sopenharmony_ci}
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_cistatic int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev,
11238c2ecf20Sopenharmony_ci			    struct netlink_ext_ack *extack)
11248c2ecf20Sopenharmony_ci{
11258c2ecf20Sopenharmony_ci	int ret;
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci	/* do not allow loopback device to be enslaved to a VRF.
11288c2ecf20Sopenharmony_ci	 * The vrf device acts as the loopback for the vrf.
11298c2ecf20Sopenharmony_ci	 */
11308c2ecf20Sopenharmony_ci	if (port_dev == dev_net(dev)->loopback_dev) {
11318c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack,
11328c2ecf20Sopenharmony_ci			       "Can not enslave loopback device to a VRF");
11338c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
11348c2ecf20Sopenharmony_ci	}
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	port_dev->priv_flags |= IFF_L3MDEV_SLAVE;
11378c2ecf20Sopenharmony_ci	ret = netdev_master_upper_dev_link(port_dev, dev, NULL, NULL, extack);
11388c2ecf20Sopenharmony_ci	if (ret < 0)
11398c2ecf20Sopenharmony_ci		goto err;
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci	cycle_netdev(port_dev, extack);
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	return 0;
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_cierr:
11468c2ecf20Sopenharmony_ci	port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE;
11478c2ecf20Sopenharmony_ci	return ret;
11488c2ecf20Sopenharmony_ci}
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_cistatic int vrf_add_slave(struct net_device *dev, struct net_device *port_dev,
11518c2ecf20Sopenharmony_ci			 struct netlink_ext_ack *extack)
11528c2ecf20Sopenharmony_ci{
11538c2ecf20Sopenharmony_ci	if (netif_is_l3_master(port_dev)) {
11548c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack,
11558c2ecf20Sopenharmony_ci			       "Can not enslave an L3 master device to a VRF");
11568c2ecf20Sopenharmony_ci		return -EINVAL;
11578c2ecf20Sopenharmony_ci	}
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	if (netif_is_l3_slave(port_dev))
11608c2ecf20Sopenharmony_ci		return -EINVAL;
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	return do_vrf_add_slave(dev, port_dev, extack);
11638c2ecf20Sopenharmony_ci}
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci/* inverse of do_vrf_add_slave */
11668c2ecf20Sopenharmony_cistatic int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
11678c2ecf20Sopenharmony_ci{
11688c2ecf20Sopenharmony_ci	netdev_upper_dev_unlink(port_dev, dev);
11698c2ecf20Sopenharmony_ci	port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE;
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	cycle_netdev(port_dev, NULL);
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci	return 0;
11748c2ecf20Sopenharmony_ci}
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_cistatic int vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
11778c2ecf20Sopenharmony_ci{
11788c2ecf20Sopenharmony_ci	return do_vrf_del_slave(dev, port_dev);
11798c2ecf20Sopenharmony_ci}
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_cistatic void vrf_dev_uninit(struct net_device *dev)
11828c2ecf20Sopenharmony_ci{
11838c2ecf20Sopenharmony_ci	struct net_vrf *vrf = netdev_priv(dev);
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	vrf_rtable_release(dev, vrf);
11868c2ecf20Sopenharmony_ci	vrf_rt6_release(dev, vrf);
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci	free_percpu(dev->dstats);
11898c2ecf20Sopenharmony_ci	dev->dstats = NULL;
11908c2ecf20Sopenharmony_ci}
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_cistatic int vrf_dev_init(struct net_device *dev)
11938c2ecf20Sopenharmony_ci{
11948c2ecf20Sopenharmony_ci	struct net_vrf *vrf = netdev_priv(dev);
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	dev->dstats = netdev_alloc_pcpu_stats(struct pcpu_dstats);
11978c2ecf20Sopenharmony_ci	if (!dev->dstats)
11988c2ecf20Sopenharmony_ci		goto out_nomem;
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	/* create the default dst which points back to us */
12018c2ecf20Sopenharmony_ci	if (vrf_rtable_create(dev) != 0)
12028c2ecf20Sopenharmony_ci		goto out_stats;
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	if (vrf_rt6_create(dev) != 0)
12058c2ecf20Sopenharmony_ci		goto out_rth;
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci	dev->flags = IFF_MASTER | IFF_NOARP;
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	/* similarly, oper state is irrelevant; set to up to avoid confusion */
12108c2ecf20Sopenharmony_ci	dev->operstate = IF_OPER_UP;
12118c2ecf20Sopenharmony_ci	netdev_lockdep_set_classes(dev);
12128c2ecf20Sopenharmony_ci	return 0;
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ciout_rth:
12158c2ecf20Sopenharmony_ci	vrf_rtable_release(dev, vrf);
12168c2ecf20Sopenharmony_ciout_stats:
12178c2ecf20Sopenharmony_ci	free_percpu(dev->dstats);
12188c2ecf20Sopenharmony_ci	dev->dstats = NULL;
12198c2ecf20Sopenharmony_ciout_nomem:
12208c2ecf20Sopenharmony_ci	return -ENOMEM;
12218c2ecf20Sopenharmony_ci}
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_cistatic const struct net_device_ops vrf_netdev_ops = {
12248c2ecf20Sopenharmony_ci	.ndo_init		= vrf_dev_init,
12258c2ecf20Sopenharmony_ci	.ndo_uninit		= vrf_dev_uninit,
12268c2ecf20Sopenharmony_ci	.ndo_start_xmit		= vrf_xmit,
12278c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
12288c2ecf20Sopenharmony_ci	.ndo_get_stats64	= vrf_get_stats64,
12298c2ecf20Sopenharmony_ci	.ndo_add_slave		= vrf_add_slave,
12308c2ecf20Sopenharmony_ci	.ndo_del_slave		= vrf_del_slave,
12318c2ecf20Sopenharmony_ci};
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_cistatic u32 vrf_fib_table(const struct net_device *dev)
12348c2ecf20Sopenharmony_ci{
12358c2ecf20Sopenharmony_ci	struct net_vrf *vrf = netdev_priv(dev);
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	return vrf->tb_id;
12388c2ecf20Sopenharmony_ci}
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_cistatic int vrf_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
12418c2ecf20Sopenharmony_ci{
12428c2ecf20Sopenharmony_ci	kfree_skb(skb);
12438c2ecf20Sopenharmony_ci	return 0;
12448c2ecf20Sopenharmony_ci}
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_cistatic struct sk_buff *vrf_rcv_nfhook(u8 pf, unsigned int hook,
12478c2ecf20Sopenharmony_ci				      struct sk_buff *skb,
12488c2ecf20Sopenharmony_ci				      struct net_device *dev)
12498c2ecf20Sopenharmony_ci{
12508c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	if (nf_hook(pf, hook, net, NULL, skb, dev, NULL, vrf_rcv_finish) != 1)
12538c2ecf20Sopenharmony_ci		skb = NULL;    /* kfree_skb(skb) handled by nf code */
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci	return skb;
12568c2ecf20Sopenharmony_ci}
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
12598c2ecf20Sopenharmony_ci/* neighbor handling is done with actual device; do not want
12608c2ecf20Sopenharmony_ci * to flip skb->dev for those ndisc packets. This really fails
12618c2ecf20Sopenharmony_ci * for multiple next protocols (e.g., NEXTHDR_HOP). But it is
12628c2ecf20Sopenharmony_ci * a start.
12638c2ecf20Sopenharmony_ci */
12648c2ecf20Sopenharmony_cistatic bool ipv6_ndisc_frame(const struct sk_buff *skb)
12658c2ecf20Sopenharmony_ci{
12668c2ecf20Sopenharmony_ci	const struct ipv6hdr *iph = ipv6_hdr(skb);
12678c2ecf20Sopenharmony_ci	bool rc = false;
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	if (iph->nexthdr == NEXTHDR_ICMP) {
12708c2ecf20Sopenharmony_ci		const struct icmp6hdr *icmph;
12718c2ecf20Sopenharmony_ci		struct icmp6hdr _icmph;
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci		icmph = skb_header_pointer(skb, sizeof(*iph),
12748c2ecf20Sopenharmony_ci					   sizeof(_icmph), &_icmph);
12758c2ecf20Sopenharmony_ci		if (!icmph)
12768c2ecf20Sopenharmony_ci			goto out;
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci		switch (icmph->icmp6_type) {
12798c2ecf20Sopenharmony_ci		case NDISC_ROUTER_SOLICITATION:
12808c2ecf20Sopenharmony_ci		case NDISC_ROUTER_ADVERTISEMENT:
12818c2ecf20Sopenharmony_ci		case NDISC_NEIGHBOUR_SOLICITATION:
12828c2ecf20Sopenharmony_ci		case NDISC_NEIGHBOUR_ADVERTISEMENT:
12838c2ecf20Sopenharmony_ci		case NDISC_REDIRECT:
12848c2ecf20Sopenharmony_ci			rc = true;
12858c2ecf20Sopenharmony_ci			break;
12868c2ecf20Sopenharmony_ci		}
12878c2ecf20Sopenharmony_ci	}
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ciout:
12908c2ecf20Sopenharmony_ci	return rc;
12918c2ecf20Sopenharmony_ci}
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_cistatic struct rt6_info *vrf_ip6_route_lookup(struct net *net,
12948c2ecf20Sopenharmony_ci					     const struct net_device *dev,
12958c2ecf20Sopenharmony_ci					     struct flowi6 *fl6,
12968c2ecf20Sopenharmony_ci					     int ifindex,
12978c2ecf20Sopenharmony_ci					     const struct sk_buff *skb,
12988c2ecf20Sopenharmony_ci					     int flags)
12998c2ecf20Sopenharmony_ci{
13008c2ecf20Sopenharmony_ci	struct net_vrf *vrf = netdev_priv(dev);
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci	return ip6_pol_route(net, vrf->fib6_table, ifindex, fl6, skb, flags);
13038c2ecf20Sopenharmony_ci}
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_cistatic void vrf_ip6_input_dst(struct sk_buff *skb, struct net_device *vrf_dev,
13068c2ecf20Sopenharmony_ci			      int ifindex)
13078c2ecf20Sopenharmony_ci{
13088c2ecf20Sopenharmony_ci	const struct ipv6hdr *iph = ipv6_hdr(skb);
13098c2ecf20Sopenharmony_ci	struct flowi6 fl6 = {
13108c2ecf20Sopenharmony_ci		.flowi6_iif     = ifindex,
13118c2ecf20Sopenharmony_ci		.flowi6_mark    = skb->mark,
13128c2ecf20Sopenharmony_ci		.flowi6_proto   = iph->nexthdr,
13138c2ecf20Sopenharmony_ci		.daddr          = iph->daddr,
13148c2ecf20Sopenharmony_ci		.saddr          = iph->saddr,
13158c2ecf20Sopenharmony_ci		.flowlabel      = ip6_flowinfo(iph),
13168c2ecf20Sopenharmony_ci	};
13178c2ecf20Sopenharmony_ci	struct net *net = dev_net(vrf_dev);
13188c2ecf20Sopenharmony_ci	struct rt6_info *rt6;
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	rt6 = vrf_ip6_route_lookup(net, vrf_dev, &fl6, ifindex, skb,
13218c2ecf20Sopenharmony_ci				   RT6_LOOKUP_F_HAS_SADDR | RT6_LOOKUP_F_IFACE);
13228c2ecf20Sopenharmony_ci	if (unlikely(!rt6))
13238c2ecf20Sopenharmony_ci		return;
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	if (unlikely(&rt6->dst == &net->ipv6.ip6_null_entry->dst))
13268c2ecf20Sopenharmony_ci		return;
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci	skb_dst_set(skb, &rt6->dst);
13298c2ecf20Sopenharmony_ci}
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_cistatic struct sk_buff *vrf_ip6_rcv(struct net_device *vrf_dev,
13328c2ecf20Sopenharmony_ci				   struct sk_buff *skb)
13338c2ecf20Sopenharmony_ci{
13348c2ecf20Sopenharmony_ci	int orig_iif = skb->skb_iif;
13358c2ecf20Sopenharmony_ci	bool need_strict = rt6_need_strict(&ipv6_hdr(skb)->daddr);
13368c2ecf20Sopenharmony_ci	bool is_ndisc = ipv6_ndisc_frame(skb);
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci	/* loopback, multicast & non-ND link-local traffic; do not push through
13398c2ecf20Sopenharmony_ci	 * packet taps again. Reset pkt_type for upper layers to process skb.
13408c2ecf20Sopenharmony_ci	 * For strict packets with a source LLA, determine the dst using the
13418c2ecf20Sopenharmony_ci	 * original ifindex.
13428c2ecf20Sopenharmony_ci	 */
13438c2ecf20Sopenharmony_ci	if (skb->pkt_type == PACKET_LOOPBACK || (need_strict && !is_ndisc)) {
13448c2ecf20Sopenharmony_ci		skb->dev = vrf_dev;
13458c2ecf20Sopenharmony_ci		skb->skb_iif = vrf_dev->ifindex;
13468c2ecf20Sopenharmony_ci		IP6CB(skb)->flags |= IP6SKB_L3SLAVE;
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci		if (skb->pkt_type == PACKET_LOOPBACK)
13498c2ecf20Sopenharmony_ci			skb->pkt_type = PACKET_HOST;
13508c2ecf20Sopenharmony_ci		else if (ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)
13518c2ecf20Sopenharmony_ci			vrf_ip6_input_dst(skb, vrf_dev, orig_iif);
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci		goto out;
13548c2ecf20Sopenharmony_ci	}
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_ci	/* if packet is NDISC then keep the ingress interface */
13578c2ecf20Sopenharmony_ci	if (!is_ndisc) {
13588c2ecf20Sopenharmony_ci		vrf_rx_stats(vrf_dev, skb->len);
13598c2ecf20Sopenharmony_ci		skb->dev = vrf_dev;
13608c2ecf20Sopenharmony_ci		skb->skb_iif = vrf_dev->ifindex;
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci		if (!list_empty(&vrf_dev->ptype_all)) {
13638c2ecf20Sopenharmony_ci			skb_push(skb, skb->mac_len);
13648c2ecf20Sopenharmony_ci			dev_queue_xmit_nit(skb, vrf_dev);
13658c2ecf20Sopenharmony_ci			skb_pull(skb, skb->mac_len);
13668c2ecf20Sopenharmony_ci		}
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci		IP6CB(skb)->flags |= IP6SKB_L3SLAVE;
13698c2ecf20Sopenharmony_ci	}
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci	if (need_strict)
13728c2ecf20Sopenharmony_ci		vrf_ip6_input_dst(skb, vrf_dev, orig_iif);
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci	skb = vrf_rcv_nfhook(NFPROTO_IPV6, NF_INET_PRE_ROUTING, skb, vrf_dev);
13758c2ecf20Sopenharmony_ciout:
13768c2ecf20Sopenharmony_ci	return skb;
13778c2ecf20Sopenharmony_ci}
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci#else
13808c2ecf20Sopenharmony_cistatic struct sk_buff *vrf_ip6_rcv(struct net_device *vrf_dev,
13818c2ecf20Sopenharmony_ci				   struct sk_buff *skb)
13828c2ecf20Sopenharmony_ci{
13838c2ecf20Sopenharmony_ci	return skb;
13848c2ecf20Sopenharmony_ci}
13858c2ecf20Sopenharmony_ci#endif
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_cistatic struct sk_buff *vrf_ip_rcv(struct net_device *vrf_dev,
13888c2ecf20Sopenharmony_ci				  struct sk_buff *skb)
13898c2ecf20Sopenharmony_ci{
13908c2ecf20Sopenharmony_ci	skb->dev = vrf_dev;
13918c2ecf20Sopenharmony_ci	skb->skb_iif = vrf_dev->ifindex;
13928c2ecf20Sopenharmony_ci	IPCB(skb)->flags |= IPSKB_L3SLAVE;
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci	if (ipv4_is_multicast(ip_hdr(skb)->daddr))
13958c2ecf20Sopenharmony_ci		goto out;
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	/* loopback traffic; do not push through packet taps again.
13988c2ecf20Sopenharmony_ci	 * Reset pkt_type for upper layers to process skb
13998c2ecf20Sopenharmony_ci	 */
14008c2ecf20Sopenharmony_ci	if (skb->pkt_type == PACKET_LOOPBACK) {
14018c2ecf20Sopenharmony_ci		skb->pkt_type = PACKET_HOST;
14028c2ecf20Sopenharmony_ci		goto out;
14038c2ecf20Sopenharmony_ci	}
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	vrf_rx_stats(vrf_dev, skb->len);
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci	if (!list_empty(&vrf_dev->ptype_all)) {
14088c2ecf20Sopenharmony_ci		skb_push(skb, skb->mac_len);
14098c2ecf20Sopenharmony_ci		dev_queue_xmit_nit(skb, vrf_dev);
14108c2ecf20Sopenharmony_ci		skb_pull(skb, skb->mac_len);
14118c2ecf20Sopenharmony_ci	}
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	skb = vrf_rcv_nfhook(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, vrf_dev);
14148c2ecf20Sopenharmony_ciout:
14158c2ecf20Sopenharmony_ci	return skb;
14168c2ecf20Sopenharmony_ci}
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci/* called with rcu lock held */
14198c2ecf20Sopenharmony_cistatic struct sk_buff *vrf_l3_rcv(struct net_device *vrf_dev,
14208c2ecf20Sopenharmony_ci				  struct sk_buff *skb,
14218c2ecf20Sopenharmony_ci				  u16 proto)
14228c2ecf20Sopenharmony_ci{
14238c2ecf20Sopenharmony_ci	switch (proto) {
14248c2ecf20Sopenharmony_ci	case AF_INET:
14258c2ecf20Sopenharmony_ci		return vrf_ip_rcv(vrf_dev, skb);
14268c2ecf20Sopenharmony_ci	case AF_INET6:
14278c2ecf20Sopenharmony_ci		return vrf_ip6_rcv(vrf_dev, skb);
14288c2ecf20Sopenharmony_ci	}
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci	return skb;
14318c2ecf20Sopenharmony_ci}
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
14348c2ecf20Sopenharmony_ci/* send to link-local or multicast address via interface enslaved to
14358c2ecf20Sopenharmony_ci * VRF device. Force lookup to VRF table without changing flow struct
14368c2ecf20Sopenharmony_ci * Note: Caller to this function must hold rcu_read_lock() and no refcnt
14378c2ecf20Sopenharmony_ci * is taken on the dst by this function.
14388c2ecf20Sopenharmony_ci */
14398c2ecf20Sopenharmony_cistatic struct dst_entry *vrf_link_scope_lookup(const struct net_device *dev,
14408c2ecf20Sopenharmony_ci					      struct flowi6 *fl6)
14418c2ecf20Sopenharmony_ci{
14428c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
14438c2ecf20Sopenharmony_ci	int flags = RT6_LOOKUP_F_IFACE | RT6_LOOKUP_F_DST_NOREF;
14448c2ecf20Sopenharmony_ci	struct dst_entry *dst = NULL;
14458c2ecf20Sopenharmony_ci	struct rt6_info *rt;
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci	/* VRF device does not have a link-local address and
14488c2ecf20Sopenharmony_ci	 * sending packets to link-local or mcast addresses over
14498c2ecf20Sopenharmony_ci	 * a VRF device does not make sense
14508c2ecf20Sopenharmony_ci	 */
14518c2ecf20Sopenharmony_ci	if (fl6->flowi6_oif == dev->ifindex) {
14528c2ecf20Sopenharmony_ci		dst = &net->ipv6.ip6_null_entry->dst;
14538c2ecf20Sopenharmony_ci		return dst;
14548c2ecf20Sopenharmony_ci	}
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	if (!ipv6_addr_any(&fl6->saddr))
14578c2ecf20Sopenharmony_ci		flags |= RT6_LOOKUP_F_HAS_SADDR;
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	rt = vrf_ip6_route_lookup(net, dev, fl6, fl6->flowi6_oif, NULL, flags);
14608c2ecf20Sopenharmony_ci	if (rt)
14618c2ecf20Sopenharmony_ci		dst = &rt->dst;
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	return dst;
14648c2ecf20Sopenharmony_ci}
14658c2ecf20Sopenharmony_ci#endif
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_cistatic const struct l3mdev_ops vrf_l3mdev_ops = {
14688c2ecf20Sopenharmony_ci	.l3mdev_fib_table	= vrf_fib_table,
14698c2ecf20Sopenharmony_ci	.l3mdev_l3_rcv		= vrf_l3_rcv,
14708c2ecf20Sopenharmony_ci	.l3mdev_l3_out		= vrf_l3_out,
14718c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
14728c2ecf20Sopenharmony_ci	.l3mdev_link_scope_lookup = vrf_link_scope_lookup,
14738c2ecf20Sopenharmony_ci#endif
14748c2ecf20Sopenharmony_ci};
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_cistatic void vrf_get_drvinfo(struct net_device *dev,
14778c2ecf20Sopenharmony_ci			    struct ethtool_drvinfo *info)
14788c2ecf20Sopenharmony_ci{
14798c2ecf20Sopenharmony_ci	strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
14808c2ecf20Sopenharmony_ci	strlcpy(info->version, DRV_VERSION, sizeof(info->version));
14818c2ecf20Sopenharmony_ci}
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_cistatic const struct ethtool_ops vrf_ethtool_ops = {
14848c2ecf20Sopenharmony_ci	.get_drvinfo	= vrf_get_drvinfo,
14858c2ecf20Sopenharmony_ci};
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_cistatic inline size_t vrf_fib_rule_nl_size(void)
14888c2ecf20Sopenharmony_ci{
14898c2ecf20Sopenharmony_ci	size_t sz;
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_ci	sz  = NLMSG_ALIGN(sizeof(struct fib_rule_hdr));
14928c2ecf20Sopenharmony_ci	sz += nla_total_size(sizeof(u8));	/* FRA_L3MDEV */
14938c2ecf20Sopenharmony_ci	sz += nla_total_size(sizeof(u32));	/* FRA_PRIORITY */
14948c2ecf20Sopenharmony_ci	sz += nla_total_size(sizeof(u8));       /* FRA_PROTOCOL */
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	return sz;
14978c2ecf20Sopenharmony_ci}
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_cistatic int vrf_fib_rule(const struct net_device *dev, __u8 family, bool add_it)
15008c2ecf20Sopenharmony_ci{
15018c2ecf20Sopenharmony_ci	struct fib_rule_hdr *frh;
15028c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh;
15038c2ecf20Sopenharmony_ci	struct sk_buff *skb;
15048c2ecf20Sopenharmony_ci	int err;
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci	if ((family == AF_INET6 || family == RTNL_FAMILY_IP6MR) &&
15078c2ecf20Sopenharmony_ci	    !ipv6_mod_enabled())
15088c2ecf20Sopenharmony_ci		return 0;
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci	skb = nlmsg_new(vrf_fib_rule_nl_size(), GFP_KERNEL);
15118c2ecf20Sopenharmony_ci	if (!skb)
15128c2ecf20Sopenharmony_ci		return -ENOMEM;
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci	nlh = nlmsg_put(skb, 0, 0, 0, sizeof(*frh), 0);
15158c2ecf20Sopenharmony_ci	if (!nlh)
15168c2ecf20Sopenharmony_ci		goto nla_put_failure;
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci	/* rule only needs to appear once */
15198c2ecf20Sopenharmony_ci	nlh->nlmsg_flags |= NLM_F_EXCL;
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_ci	frh = nlmsg_data(nlh);
15228c2ecf20Sopenharmony_ci	memset(frh, 0, sizeof(*frh));
15238c2ecf20Sopenharmony_ci	frh->family = family;
15248c2ecf20Sopenharmony_ci	frh->action = FR_ACT_TO_TBL;
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci	if (nla_put_u8(skb, FRA_PROTOCOL, RTPROT_KERNEL))
15278c2ecf20Sopenharmony_ci		goto nla_put_failure;
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci	if (nla_put_u8(skb, FRA_L3MDEV, 1))
15308c2ecf20Sopenharmony_ci		goto nla_put_failure;
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci	if (nla_put_u32(skb, FRA_PRIORITY, FIB_RULE_PREF))
15338c2ecf20Sopenharmony_ci		goto nla_put_failure;
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	nlmsg_end(skb, nlh);
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci	/* fib_nl_{new,del}rule handling looks for net from skb->sk */
15388c2ecf20Sopenharmony_ci	skb->sk = dev_net(dev)->rtnl;
15398c2ecf20Sopenharmony_ci	if (add_it) {
15408c2ecf20Sopenharmony_ci		err = fib_nl_newrule(skb, nlh, NULL);
15418c2ecf20Sopenharmony_ci		if (err == -EEXIST)
15428c2ecf20Sopenharmony_ci			err = 0;
15438c2ecf20Sopenharmony_ci	} else {
15448c2ecf20Sopenharmony_ci		err = fib_nl_delrule(skb, nlh, NULL);
15458c2ecf20Sopenharmony_ci		if (err == -ENOENT)
15468c2ecf20Sopenharmony_ci			err = 0;
15478c2ecf20Sopenharmony_ci	}
15488c2ecf20Sopenharmony_ci	nlmsg_free(skb);
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_ci	return err;
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_cinla_put_failure:
15538c2ecf20Sopenharmony_ci	nlmsg_free(skb);
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci	return -EMSGSIZE;
15568c2ecf20Sopenharmony_ci}
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_cistatic int vrf_add_fib_rules(const struct net_device *dev)
15598c2ecf20Sopenharmony_ci{
15608c2ecf20Sopenharmony_ci	int err;
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	err = vrf_fib_rule(dev, AF_INET,  true);
15638c2ecf20Sopenharmony_ci	if (err < 0)
15648c2ecf20Sopenharmony_ci		goto out_err;
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_ci	err = vrf_fib_rule(dev, AF_INET6, true);
15678c2ecf20Sopenharmony_ci	if (err < 0)
15688c2ecf20Sopenharmony_ci		goto ipv6_err;
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IP_MROUTE_MULTIPLE_TABLES)
15718c2ecf20Sopenharmony_ci	err = vrf_fib_rule(dev, RTNL_FAMILY_IPMR, true);
15728c2ecf20Sopenharmony_ci	if (err < 0)
15738c2ecf20Sopenharmony_ci		goto ipmr_err;
15748c2ecf20Sopenharmony_ci#endif
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MROUTE_MULTIPLE_TABLES)
15778c2ecf20Sopenharmony_ci	err = vrf_fib_rule(dev, RTNL_FAMILY_IP6MR, true);
15788c2ecf20Sopenharmony_ci	if (err < 0)
15798c2ecf20Sopenharmony_ci		goto ip6mr_err;
15808c2ecf20Sopenharmony_ci#endif
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci	return 0;
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MROUTE_MULTIPLE_TABLES)
15858c2ecf20Sopenharmony_ciip6mr_err:
15868c2ecf20Sopenharmony_ci	vrf_fib_rule(dev, RTNL_FAMILY_IPMR,  false);
15878c2ecf20Sopenharmony_ci#endif
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IP_MROUTE_MULTIPLE_TABLES)
15908c2ecf20Sopenharmony_ciipmr_err:
15918c2ecf20Sopenharmony_ci	vrf_fib_rule(dev, AF_INET6,  false);
15928c2ecf20Sopenharmony_ci#endif
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_ciipv6_err:
15958c2ecf20Sopenharmony_ci	vrf_fib_rule(dev, AF_INET,  false);
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ciout_err:
15988c2ecf20Sopenharmony_ci	netdev_err(dev, "Failed to add FIB rules.\n");
15998c2ecf20Sopenharmony_ci	return err;
16008c2ecf20Sopenharmony_ci}
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_cistatic void vrf_setup(struct net_device *dev)
16038c2ecf20Sopenharmony_ci{
16048c2ecf20Sopenharmony_ci	ether_setup(dev);
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	/* Initialize the device structure. */
16078c2ecf20Sopenharmony_ci	dev->netdev_ops = &vrf_netdev_ops;
16088c2ecf20Sopenharmony_ci	dev->l3mdev_ops = &vrf_l3mdev_ops;
16098c2ecf20Sopenharmony_ci	dev->ethtool_ops = &vrf_ethtool_ops;
16108c2ecf20Sopenharmony_ci	dev->needs_free_netdev = true;
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_ci	/* Fill in device structure with ethernet-generic values. */
16138c2ecf20Sopenharmony_ci	eth_hw_addr_random(dev);
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci	/* don't acquire vrf device's netif_tx_lock when transmitting */
16168c2ecf20Sopenharmony_ci	dev->features |= NETIF_F_LLTX;
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci	/* don't allow vrf devices to change network namespaces. */
16198c2ecf20Sopenharmony_ci	dev->features |= NETIF_F_NETNS_LOCAL;
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci	/* does not make sense for a VLAN to be added to a vrf device */
16228c2ecf20Sopenharmony_ci	dev->features   |= NETIF_F_VLAN_CHALLENGED;
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci	/* enable offload features */
16258c2ecf20Sopenharmony_ci	dev->features   |= NETIF_F_GSO_SOFTWARE;
16268c2ecf20Sopenharmony_ci	dev->features   |= NETIF_F_RXCSUM | NETIF_F_HW_CSUM | NETIF_F_SCTP_CRC;
16278c2ecf20Sopenharmony_ci	dev->features   |= NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA;
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	dev->hw_features = dev->features;
16308c2ecf20Sopenharmony_ci	dev->hw_enc_features = dev->features;
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_ci	/* default to no qdisc; user can add if desired */
16338c2ecf20Sopenharmony_ci	dev->priv_flags |= IFF_NO_QUEUE;
16348c2ecf20Sopenharmony_ci	dev->priv_flags |= IFF_NO_RX_HANDLER;
16358c2ecf20Sopenharmony_ci	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	/* VRF devices do not care about MTU, but if the MTU is set
16388c2ecf20Sopenharmony_ci	 * too low then the ipv4 and ipv6 protocols are disabled
16398c2ecf20Sopenharmony_ci	 * which breaks networking.
16408c2ecf20Sopenharmony_ci	 */
16418c2ecf20Sopenharmony_ci	dev->min_mtu = IPV6_MIN_MTU;
16428c2ecf20Sopenharmony_ci	dev->max_mtu = IP6_MAX_MTU;
16438c2ecf20Sopenharmony_ci	dev->mtu = dev->max_mtu;
16448c2ecf20Sopenharmony_ci}
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_cistatic int vrf_validate(struct nlattr *tb[], struct nlattr *data[],
16478c2ecf20Sopenharmony_ci			struct netlink_ext_ack *extack)
16488c2ecf20Sopenharmony_ci{
16498c2ecf20Sopenharmony_ci	if (tb[IFLA_ADDRESS]) {
16508c2ecf20Sopenharmony_ci		if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) {
16518c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Invalid hardware address");
16528c2ecf20Sopenharmony_ci			return -EINVAL;
16538c2ecf20Sopenharmony_ci		}
16548c2ecf20Sopenharmony_ci		if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) {
16558c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Invalid hardware address");
16568c2ecf20Sopenharmony_ci			return -EADDRNOTAVAIL;
16578c2ecf20Sopenharmony_ci		}
16588c2ecf20Sopenharmony_ci	}
16598c2ecf20Sopenharmony_ci	return 0;
16608c2ecf20Sopenharmony_ci}
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_cistatic void vrf_dellink(struct net_device *dev, struct list_head *head)
16638c2ecf20Sopenharmony_ci{
16648c2ecf20Sopenharmony_ci	struct net_device *port_dev;
16658c2ecf20Sopenharmony_ci	struct list_head *iter;
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_ci	netdev_for_each_lower_dev(dev, port_dev, iter)
16688c2ecf20Sopenharmony_ci		vrf_del_slave(dev, port_dev);
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_ci	vrf_map_unregister_dev(dev);
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_ci	unregister_netdevice_queue(dev, head);
16738c2ecf20Sopenharmony_ci}
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_cistatic int vrf_newlink(struct net *src_net, struct net_device *dev,
16768c2ecf20Sopenharmony_ci		       struct nlattr *tb[], struct nlattr *data[],
16778c2ecf20Sopenharmony_ci		       struct netlink_ext_ack *extack)
16788c2ecf20Sopenharmony_ci{
16798c2ecf20Sopenharmony_ci	struct net_vrf *vrf = netdev_priv(dev);
16808c2ecf20Sopenharmony_ci	struct netns_vrf *nn_vrf;
16818c2ecf20Sopenharmony_ci	bool *add_fib_rules;
16828c2ecf20Sopenharmony_ci	struct net *net;
16838c2ecf20Sopenharmony_ci	int err;
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_ci	if (!data || !data[IFLA_VRF_TABLE]) {
16868c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "VRF table id is missing");
16878c2ecf20Sopenharmony_ci		return -EINVAL;
16888c2ecf20Sopenharmony_ci	}
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci	vrf->tb_id = nla_get_u32(data[IFLA_VRF_TABLE]);
16918c2ecf20Sopenharmony_ci	if (vrf->tb_id == RT_TABLE_UNSPEC) {
16928c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, data[IFLA_VRF_TABLE],
16938c2ecf20Sopenharmony_ci				    "Invalid VRF table id");
16948c2ecf20Sopenharmony_ci		return -EINVAL;
16958c2ecf20Sopenharmony_ci	}
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ci	dev->priv_flags |= IFF_L3MDEV_MASTER;
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_ci	err = register_netdevice(dev);
17008c2ecf20Sopenharmony_ci	if (err)
17018c2ecf20Sopenharmony_ci		goto out;
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_ci	/* mapping between table_id and vrf;
17048c2ecf20Sopenharmony_ci	 * note: such binding could not be done in the dev init function
17058c2ecf20Sopenharmony_ci	 * because dev->ifindex id is not available yet.
17068c2ecf20Sopenharmony_ci	 */
17078c2ecf20Sopenharmony_ci	vrf->ifindex = dev->ifindex;
17088c2ecf20Sopenharmony_ci
17098c2ecf20Sopenharmony_ci	err = vrf_map_register_dev(dev, extack);
17108c2ecf20Sopenharmony_ci	if (err) {
17118c2ecf20Sopenharmony_ci		unregister_netdevice(dev);
17128c2ecf20Sopenharmony_ci		goto out;
17138c2ecf20Sopenharmony_ci	}
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_ci	net = dev_net(dev);
17168c2ecf20Sopenharmony_ci	nn_vrf = net_generic(net, vrf_net_id);
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_ci	add_fib_rules = &nn_vrf->add_fib_rules;
17198c2ecf20Sopenharmony_ci	if (*add_fib_rules) {
17208c2ecf20Sopenharmony_ci		err = vrf_add_fib_rules(dev);
17218c2ecf20Sopenharmony_ci		if (err) {
17228c2ecf20Sopenharmony_ci			vrf_map_unregister_dev(dev);
17238c2ecf20Sopenharmony_ci			unregister_netdevice(dev);
17248c2ecf20Sopenharmony_ci			goto out;
17258c2ecf20Sopenharmony_ci		}
17268c2ecf20Sopenharmony_ci		*add_fib_rules = false;
17278c2ecf20Sopenharmony_ci	}
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ciout:
17308c2ecf20Sopenharmony_ci	return err;
17318c2ecf20Sopenharmony_ci}
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_cistatic size_t vrf_nl_getsize(const struct net_device *dev)
17348c2ecf20Sopenharmony_ci{
17358c2ecf20Sopenharmony_ci	return nla_total_size(sizeof(u32));  /* IFLA_VRF_TABLE */
17368c2ecf20Sopenharmony_ci}
17378c2ecf20Sopenharmony_ci
17388c2ecf20Sopenharmony_cistatic int vrf_fillinfo(struct sk_buff *skb,
17398c2ecf20Sopenharmony_ci			const struct net_device *dev)
17408c2ecf20Sopenharmony_ci{
17418c2ecf20Sopenharmony_ci	struct net_vrf *vrf = netdev_priv(dev);
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci	return nla_put_u32(skb, IFLA_VRF_TABLE, vrf->tb_id);
17448c2ecf20Sopenharmony_ci}
17458c2ecf20Sopenharmony_ci
17468c2ecf20Sopenharmony_cistatic size_t vrf_get_slave_size(const struct net_device *bond_dev,
17478c2ecf20Sopenharmony_ci				 const struct net_device *slave_dev)
17488c2ecf20Sopenharmony_ci{
17498c2ecf20Sopenharmony_ci	return nla_total_size(sizeof(u32));  /* IFLA_VRF_PORT_TABLE */
17508c2ecf20Sopenharmony_ci}
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_cistatic int vrf_fill_slave_info(struct sk_buff *skb,
17538c2ecf20Sopenharmony_ci			       const struct net_device *vrf_dev,
17548c2ecf20Sopenharmony_ci			       const struct net_device *slave_dev)
17558c2ecf20Sopenharmony_ci{
17568c2ecf20Sopenharmony_ci	struct net_vrf *vrf = netdev_priv(vrf_dev);
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_ci	if (nla_put_u32(skb, IFLA_VRF_PORT_TABLE, vrf->tb_id))
17598c2ecf20Sopenharmony_ci		return -EMSGSIZE;
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ci	return 0;
17628c2ecf20Sopenharmony_ci}
17638c2ecf20Sopenharmony_ci
17648c2ecf20Sopenharmony_cistatic const struct nla_policy vrf_nl_policy[IFLA_VRF_MAX + 1] = {
17658c2ecf20Sopenharmony_ci	[IFLA_VRF_TABLE] = { .type = NLA_U32 },
17668c2ecf20Sopenharmony_ci};
17678c2ecf20Sopenharmony_ci
17688c2ecf20Sopenharmony_cistatic struct rtnl_link_ops vrf_link_ops __read_mostly = {
17698c2ecf20Sopenharmony_ci	.kind		= DRV_NAME,
17708c2ecf20Sopenharmony_ci	.priv_size	= sizeof(struct net_vrf),
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci	.get_size	= vrf_nl_getsize,
17738c2ecf20Sopenharmony_ci	.policy		= vrf_nl_policy,
17748c2ecf20Sopenharmony_ci	.validate	= vrf_validate,
17758c2ecf20Sopenharmony_ci	.fill_info	= vrf_fillinfo,
17768c2ecf20Sopenharmony_ci
17778c2ecf20Sopenharmony_ci	.get_slave_size  = vrf_get_slave_size,
17788c2ecf20Sopenharmony_ci	.fill_slave_info = vrf_fill_slave_info,
17798c2ecf20Sopenharmony_ci
17808c2ecf20Sopenharmony_ci	.newlink	= vrf_newlink,
17818c2ecf20Sopenharmony_ci	.dellink	= vrf_dellink,
17828c2ecf20Sopenharmony_ci	.setup		= vrf_setup,
17838c2ecf20Sopenharmony_ci	.maxtype	= IFLA_VRF_MAX,
17848c2ecf20Sopenharmony_ci};
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_cistatic int vrf_device_event(struct notifier_block *unused,
17878c2ecf20Sopenharmony_ci			    unsigned long event, void *ptr)
17888c2ecf20Sopenharmony_ci{
17898c2ecf20Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_ci	/* only care about unregister events to drop slave references */
17928c2ecf20Sopenharmony_ci	if (event == NETDEV_UNREGISTER) {
17938c2ecf20Sopenharmony_ci		struct net_device *vrf_dev;
17948c2ecf20Sopenharmony_ci
17958c2ecf20Sopenharmony_ci		if (!netif_is_l3_slave(dev))
17968c2ecf20Sopenharmony_ci			goto out;
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_ci		vrf_dev = netdev_master_upper_dev_get(dev);
17998c2ecf20Sopenharmony_ci		vrf_del_slave(vrf_dev, dev);
18008c2ecf20Sopenharmony_ci	}
18018c2ecf20Sopenharmony_ciout:
18028c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
18038c2ecf20Sopenharmony_ci}
18048c2ecf20Sopenharmony_ci
18058c2ecf20Sopenharmony_cistatic struct notifier_block vrf_notifier_block __read_mostly = {
18068c2ecf20Sopenharmony_ci	.notifier_call = vrf_device_event,
18078c2ecf20Sopenharmony_ci};
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_cistatic int vrf_map_init(struct vrf_map *vmap)
18108c2ecf20Sopenharmony_ci{
18118c2ecf20Sopenharmony_ci	spin_lock_init(&vmap->vmap_lock);
18128c2ecf20Sopenharmony_ci	hash_init(vmap->ht);
18138c2ecf20Sopenharmony_ci
18148c2ecf20Sopenharmony_ci	vmap->strict_mode = false;
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci	return 0;
18178c2ecf20Sopenharmony_ci}
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL
18208c2ecf20Sopenharmony_cistatic bool vrf_strict_mode(struct vrf_map *vmap)
18218c2ecf20Sopenharmony_ci{
18228c2ecf20Sopenharmony_ci	bool strict_mode;
18238c2ecf20Sopenharmony_ci
18248c2ecf20Sopenharmony_ci	vrf_map_lock(vmap);
18258c2ecf20Sopenharmony_ci	strict_mode = vmap->strict_mode;
18268c2ecf20Sopenharmony_ci	vrf_map_unlock(vmap);
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_ci	return strict_mode;
18298c2ecf20Sopenharmony_ci}
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_cistatic int vrf_strict_mode_change(struct vrf_map *vmap, bool new_mode)
18328c2ecf20Sopenharmony_ci{
18338c2ecf20Sopenharmony_ci	bool *cur_mode;
18348c2ecf20Sopenharmony_ci	int res = 0;
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_ci	vrf_map_lock(vmap);
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_ci	cur_mode = &vmap->strict_mode;
18398c2ecf20Sopenharmony_ci	if (*cur_mode == new_mode)
18408c2ecf20Sopenharmony_ci		goto unlock;
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_ci	if (*cur_mode) {
18438c2ecf20Sopenharmony_ci		/* disable strict mode */
18448c2ecf20Sopenharmony_ci		*cur_mode = false;
18458c2ecf20Sopenharmony_ci	} else {
18468c2ecf20Sopenharmony_ci		if (vmap->shared_tables) {
18478c2ecf20Sopenharmony_ci			/* we cannot allow strict_mode because there are some
18488c2ecf20Sopenharmony_ci			 * vrfs that share one or more tables.
18498c2ecf20Sopenharmony_ci			 */
18508c2ecf20Sopenharmony_ci			res = -EBUSY;
18518c2ecf20Sopenharmony_ci			goto unlock;
18528c2ecf20Sopenharmony_ci		}
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_ci		/* no tables are shared among vrfs, so we can go back
18558c2ecf20Sopenharmony_ci		 * to 1:1 association between a vrf with its table.
18568c2ecf20Sopenharmony_ci		 */
18578c2ecf20Sopenharmony_ci		*cur_mode = true;
18588c2ecf20Sopenharmony_ci	}
18598c2ecf20Sopenharmony_ci
18608c2ecf20Sopenharmony_ciunlock:
18618c2ecf20Sopenharmony_ci	vrf_map_unlock(vmap);
18628c2ecf20Sopenharmony_ci
18638c2ecf20Sopenharmony_ci	return res;
18648c2ecf20Sopenharmony_ci}
18658c2ecf20Sopenharmony_ci
18668c2ecf20Sopenharmony_cistatic int vrf_shared_table_handler(struct ctl_table *table, int write,
18678c2ecf20Sopenharmony_ci				    void *buffer, size_t *lenp, loff_t *ppos)
18688c2ecf20Sopenharmony_ci{
18698c2ecf20Sopenharmony_ci	struct net *net = (struct net *)table->extra1;
18708c2ecf20Sopenharmony_ci	struct vrf_map *vmap = netns_vrf_map(net);
18718c2ecf20Sopenharmony_ci	int proc_strict_mode = 0;
18728c2ecf20Sopenharmony_ci	struct ctl_table tmp = {
18738c2ecf20Sopenharmony_ci		.procname	= table->procname,
18748c2ecf20Sopenharmony_ci		.data		= &proc_strict_mode,
18758c2ecf20Sopenharmony_ci		.maxlen		= sizeof(int),
18768c2ecf20Sopenharmony_ci		.mode		= table->mode,
18778c2ecf20Sopenharmony_ci		.extra1		= SYSCTL_ZERO,
18788c2ecf20Sopenharmony_ci		.extra2		= SYSCTL_ONE,
18798c2ecf20Sopenharmony_ci	};
18808c2ecf20Sopenharmony_ci	int ret;
18818c2ecf20Sopenharmony_ci
18828c2ecf20Sopenharmony_ci	if (!write)
18838c2ecf20Sopenharmony_ci		proc_strict_mode = vrf_strict_mode(vmap);
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_ci	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
18868c2ecf20Sopenharmony_ci
18878c2ecf20Sopenharmony_ci	if (write && ret == 0)
18888c2ecf20Sopenharmony_ci		ret = vrf_strict_mode_change(vmap, (bool)proc_strict_mode);
18898c2ecf20Sopenharmony_ci
18908c2ecf20Sopenharmony_ci	return ret;
18918c2ecf20Sopenharmony_ci}
18928c2ecf20Sopenharmony_ci
18938c2ecf20Sopenharmony_cistatic const struct ctl_table vrf_table[] = {
18948c2ecf20Sopenharmony_ci	{
18958c2ecf20Sopenharmony_ci		.procname	= "strict_mode",
18968c2ecf20Sopenharmony_ci		.data		= NULL,
18978c2ecf20Sopenharmony_ci		.maxlen		= sizeof(int),
18988c2ecf20Sopenharmony_ci		.mode		= 0644,
18998c2ecf20Sopenharmony_ci		.proc_handler	= vrf_shared_table_handler,
19008c2ecf20Sopenharmony_ci		/* set by the vrf_netns_init */
19018c2ecf20Sopenharmony_ci		.extra1		= NULL,
19028c2ecf20Sopenharmony_ci	},
19038c2ecf20Sopenharmony_ci	{ },
19048c2ecf20Sopenharmony_ci};
19058c2ecf20Sopenharmony_ci
19068c2ecf20Sopenharmony_cistatic int vrf_netns_init_sysctl(struct net *net, struct netns_vrf *nn_vrf)
19078c2ecf20Sopenharmony_ci{
19088c2ecf20Sopenharmony_ci	struct ctl_table *table;
19098c2ecf20Sopenharmony_ci
19108c2ecf20Sopenharmony_ci	table = kmemdup(vrf_table, sizeof(vrf_table), GFP_KERNEL);
19118c2ecf20Sopenharmony_ci	if (!table)
19128c2ecf20Sopenharmony_ci		return -ENOMEM;
19138c2ecf20Sopenharmony_ci
19148c2ecf20Sopenharmony_ci	/* init the extra1 parameter with the reference to current netns */
19158c2ecf20Sopenharmony_ci	table[0].extra1 = net;
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_ci	nn_vrf->ctl_hdr = register_net_sysctl(net, "net/vrf", table);
19188c2ecf20Sopenharmony_ci	if (!nn_vrf->ctl_hdr) {
19198c2ecf20Sopenharmony_ci		kfree(table);
19208c2ecf20Sopenharmony_ci		return -ENOMEM;
19218c2ecf20Sopenharmony_ci	}
19228c2ecf20Sopenharmony_ci
19238c2ecf20Sopenharmony_ci	return 0;
19248c2ecf20Sopenharmony_ci}
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_cistatic void vrf_netns_exit_sysctl(struct net *net)
19278c2ecf20Sopenharmony_ci{
19288c2ecf20Sopenharmony_ci	struct netns_vrf *nn_vrf = net_generic(net, vrf_net_id);
19298c2ecf20Sopenharmony_ci	struct ctl_table *table;
19308c2ecf20Sopenharmony_ci
19318c2ecf20Sopenharmony_ci	table = nn_vrf->ctl_hdr->ctl_table_arg;
19328c2ecf20Sopenharmony_ci	unregister_net_sysctl_table(nn_vrf->ctl_hdr);
19338c2ecf20Sopenharmony_ci	kfree(table);
19348c2ecf20Sopenharmony_ci}
19358c2ecf20Sopenharmony_ci#else
19368c2ecf20Sopenharmony_cistatic int vrf_netns_init_sysctl(struct net *net, struct netns_vrf *nn_vrf)
19378c2ecf20Sopenharmony_ci{
19388c2ecf20Sopenharmony_ci	return 0;
19398c2ecf20Sopenharmony_ci}
19408c2ecf20Sopenharmony_ci
19418c2ecf20Sopenharmony_cistatic void vrf_netns_exit_sysctl(struct net *net)
19428c2ecf20Sopenharmony_ci{
19438c2ecf20Sopenharmony_ci}
19448c2ecf20Sopenharmony_ci#endif
19458c2ecf20Sopenharmony_ci
19468c2ecf20Sopenharmony_ci/* Initialize per network namespace state */
19478c2ecf20Sopenharmony_cistatic int __net_init vrf_netns_init(struct net *net)
19488c2ecf20Sopenharmony_ci{
19498c2ecf20Sopenharmony_ci	struct netns_vrf *nn_vrf = net_generic(net, vrf_net_id);
19508c2ecf20Sopenharmony_ci
19518c2ecf20Sopenharmony_ci	nn_vrf->add_fib_rules = true;
19528c2ecf20Sopenharmony_ci	vrf_map_init(&nn_vrf->vmap);
19538c2ecf20Sopenharmony_ci
19548c2ecf20Sopenharmony_ci	return vrf_netns_init_sysctl(net, nn_vrf);
19558c2ecf20Sopenharmony_ci}
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_cistatic void __net_exit vrf_netns_exit(struct net *net)
19588c2ecf20Sopenharmony_ci{
19598c2ecf20Sopenharmony_ci	vrf_netns_exit_sysctl(net);
19608c2ecf20Sopenharmony_ci}
19618c2ecf20Sopenharmony_ci
19628c2ecf20Sopenharmony_cistatic struct pernet_operations vrf_net_ops __net_initdata = {
19638c2ecf20Sopenharmony_ci	.init = vrf_netns_init,
19648c2ecf20Sopenharmony_ci	.exit = vrf_netns_exit,
19658c2ecf20Sopenharmony_ci	.id   = &vrf_net_id,
19668c2ecf20Sopenharmony_ci	.size = sizeof(struct netns_vrf),
19678c2ecf20Sopenharmony_ci};
19688c2ecf20Sopenharmony_ci
19698c2ecf20Sopenharmony_cistatic int __init vrf_init_module(void)
19708c2ecf20Sopenharmony_ci{
19718c2ecf20Sopenharmony_ci	int rc;
19728c2ecf20Sopenharmony_ci
19738c2ecf20Sopenharmony_ci	register_netdevice_notifier(&vrf_notifier_block);
19748c2ecf20Sopenharmony_ci
19758c2ecf20Sopenharmony_ci	rc = register_pernet_subsys(&vrf_net_ops);
19768c2ecf20Sopenharmony_ci	if (rc < 0)
19778c2ecf20Sopenharmony_ci		goto error;
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci	rc = l3mdev_table_lookup_register(L3MDEV_TYPE_VRF,
19808c2ecf20Sopenharmony_ci					  vrf_ifindex_lookup_by_table_id);
19818c2ecf20Sopenharmony_ci	if (rc < 0)
19828c2ecf20Sopenharmony_ci		goto unreg_pernet;
19838c2ecf20Sopenharmony_ci
19848c2ecf20Sopenharmony_ci	rc = rtnl_link_register(&vrf_link_ops);
19858c2ecf20Sopenharmony_ci	if (rc < 0)
19868c2ecf20Sopenharmony_ci		goto table_lookup_unreg;
19878c2ecf20Sopenharmony_ci
19888c2ecf20Sopenharmony_ci	return 0;
19898c2ecf20Sopenharmony_ci
19908c2ecf20Sopenharmony_citable_lookup_unreg:
19918c2ecf20Sopenharmony_ci	l3mdev_table_lookup_unregister(L3MDEV_TYPE_VRF,
19928c2ecf20Sopenharmony_ci				       vrf_ifindex_lookup_by_table_id);
19938c2ecf20Sopenharmony_ci
19948c2ecf20Sopenharmony_ciunreg_pernet:
19958c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&vrf_net_ops);
19968c2ecf20Sopenharmony_ci
19978c2ecf20Sopenharmony_cierror:
19988c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&vrf_notifier_block);
19998c2ecf20Sopenharmony_ci	return rc;
20008c2ecf20Sopenharmony_ci}
20018c2ecf20Sopenharmony_ci
20028c2ecf20Sopenharmony_cimodule_init(vrf_init_module);
20038c2ecf20Sopenharmony_ciMODULE_AUTHOR("Shrijeet Mukherjee, David Ahern");
20048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Device driver to instantiate VRF domains");
20058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
20068c2ecf20Sopenharmony_ciMODULE_ALIAS_RTNL_LINK(DRV_NAME);
20078c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
2008