18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	Linux INET6 implementation
48c2ecf20Sopenharmony_ci *	Forwarding Information Database
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *	Authors:
78c2ecf20Sopenharmony_ci *	Pedro Roque		<roque@di.fc.ul.pt>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci *	Changes:
108c2ecf20Sopenharmony_ci *	Yuji SEKIYA @USAGI:	Support default route on router node;
118c2ecf20Sopenharmony_ci *				remove ip6_null_entry from the top of
128c2ecf20Sopenharmony_ci *				routing table.
138c2ecf20Sopenharmony_ci *	Ville Nuorvala:		Fixed routing subtrees.
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "IPv6: " fmt
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/errno.h>
198c2ecf20Sopenharmony_ci#include <linux/types.h>
208c2ecf20Sopenharmony_ci#include <linux/net.h>
218c2ecf20Sopenharmony_ci#include <linux/route.h>
228c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
238c2ecf20Sopenharmony_ci#include <linux/in6.h>
248c2ecf20Sopenharmony_ci#include <linux/init.h>
258c2ecf20Sopenharmony_ci#include <linux/list.h>
268c2ecf20Sopenharmony_ci#include <linux/slab.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include <net/ip.h>
298c2ecf20Sopenharmony_ci#include <net/ipv6.h>
308c2ecf20Sopenharmony_ci#include <net/ndisc.h>
318c2ecf20Sopenharmony_ci#include <net/addrconf.h>
328c2ecf20Sopenharmony_ci#include <net/lwtunnel.h>
338c2ecf20Sopenharmony_ci#include <net/fib_notifier.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include <net/ip6_fib.h>
368c2ecf20Sopenharmony_ci#include <net/ip6_route.h>
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic struct kmem_cache *fib6_node_kmem __read_mostly;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistruct fib6_cleaner {
418c2ecf20Sopenharmony_ci	struct fib6_walker w;
428c2ecf20Sopenharmony_ci	struct net *net;
438c2ecf20Sopenharmony_ci	int (*func)(struct fib6_info *, void *arg);
448c2ecf20Sopenharmony_ci	int sernum;
458c2ecf20Sopenharmony_ci	void *arg;
468c2ecf20Sopenharmony_ci	bool skip_notify;
478c2ecf20Sopenharmony_ci};
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
508c2ecf20Sopenharmony_ci#define FWS_INIT FWS_S
518c2ecf20Sopenharmony_ci#else
528c2ecf20Sopenharmony_ci#define FWS_INIT FWS_L
538c2ecf20Sopenharmony_ci#endif
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic struct fib6_info *fib6_find_prefix(struct net *net,
568c2ecf20Sopenharmony_ci					 struct fib6_table *table,
578c2ecf20Sopenharmony_ci					 struct fib6_node *fn);
588c2ecf20Sopenharmony_cistatic struct fib6_node *fib6_repair_tree(struct net *net,
598c2ecf20Sopenharmony_ci					  struct fib6_table *table,
608c2ecf20Sopenharmony_ci					  struct fib6_node *fn);
618c2ecf20Sopenharmony_cistatic int fib6_walk(struct net *net, struct fib6_walker *w);
628c2ecf20Sopenharmony_cistatic int fib6_walk_continue(struct fib6_walker *w);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/*
658c2ecf20Sopenharmony_ci *	A routing update causes an increase of the serial number on the
668c2ecf20Sopenharmony_ci *	affected subtree. This allows for cached routes to be asynchronously
678c2ecf20Sopenharmony_ci *	tested when modifications are made to the destination cache as a
688c2ecf20Sopenharmony_ci *	result of redirects, path MTU changes, etc.
698c2ecf20Sopenharmony_ci */
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic void fib6_gc_timer_cb(struct timer_list *t);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci#define FOR_WALKERS(net, w) \
748c2ecf20Sopenharmony_ci	list_for_each_entry(w, &(net)->ipv6.fib6_walkers, lh)
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic void fib6_walker_link(struct net *net, struct fib6_walker *w)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	write_lock_bh(&net->ipv6.fib6_walker_lock);
798c2ecf20Sopenharmony_ci	list_add(&w->lh, &net->ipv6.fib6_walkers);
808c2ecf20Sopenharmony_ci	write_unlock_bh(&net->ipv6.fib6_walker_lock);
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic void fib6_walker_unlink(struct net *net, struct fib6_walker *w)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	write_lock_bh(&net->ipv6.fib6_walker_lock);
868c2ecf20Sopenharmony_ci	list_del(&w->lh);
878c2ecf20Sopenharmony_ci	write_unlock_bh(&net->ipv6.fib6_walker_lock);
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic int fib6_new_sernum(struct net *net)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	int new, old;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	do {
958c2ecf20Sopenharmony_ci		old = atomic_read(&net->ipv6.fib6_sernum);
968c2ecf20Sopenharmony_ci		new = old < INT_MAX ? old + 1 : 1;
978c2ecf20Sopenharmony_ci	} while (atomic_cmpxchg(&net->ipv6.fib6_sernum,
988c2ecf20Sopenharmony_ci				old, new) != old);
998c2ecf20Sopenharmony_ci	return new;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cienum {
1038c2ecf20Sopenharmony_ci	FIB6_NO_SERNUM_CHANGE = 0,
1048c2ecf20Sopenharmony_ci};
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_civoid fib6_update_sernum(struct net *net, struct fib6_info *f6i)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct fib6_node *fn;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	fn = rcu_dereference_protected(f6i->fib6_node,
1118c2ecf20Sopenharmony_ci			lockdep_is_held(&f6i->fib6_table->tb6_lock));
1128c2ecf20Sopenharmony_ci	if (fn)
1138c2ecf20Sopenharmony_ci		WRITE_ONCE(fn->fn_sernum, fib6_new_sernum(net));
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/*
1178c2ecf20Sopenharmony_ci *	Auxiliary address test functions for the radix tree.
1188c2ecf20Sopenharmony_ci *
1198c2ecf20Sopenharmony_ci *	These assume a 32bit processor (although it will work on
1208c2ecf20Sopenharmony_ci *	64bit processors)
1218c2ecf20Sopenharmony_ci */
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/*
1248c2ecf20Sopenharmony_ci *	test bit
1258c2ecf20Sopenharmony_ci */
1268c2ecf20Sopenharmony_ci#if defined(__LITTLE_ENDIAN)
1278c2ecf20Sopenharmony_ci# define BITOP_BE32_SWIZZLE	(0x1F & ~7)
1288c2ecf20Sopenharmony_ci#else
1298c2ecf20Sopenharmony_ci# define BITOP_BE32_SWIZZLE	0
1308c2ecf20Sopenharmony_ci#endif
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic __be32 addr_bit_set(const void *token, int fn_bit)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	const __be32 *addr = token;
1358c2ecf20Sopenharmony_ci	/*
1368c2ecf20Sopenharmony_ci	 * Here,
1378c2ecf20Sopenharmony_ci	 *	1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f)
1388c2ecf20Sopenharmony_ci	 * is optimized version of
1398c2ecf20Sopenharmony_ci	 *	htonl(1 << ((~fn_bit)&0x1F))
1408c2ecf20Sopenharmony_ci	 * See include/asm-generic/bitops/le.h.
1418c2ecf20Sopenharmony_ci	 */
1428c2ecf20Sopenharmony_ci	return (__force __be32)(1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f)) &
1438c2ecf20Sopenharmony_ci	       addr[fn_bit >> 5];
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistruct fib6_info *fib6_info_alloc(gfp_t gfp_flags, bool with_fib6_nh)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	struct fib6_info *f6i;
1498c2ecf20Sopenharmony_ci	size_t sz = sizeof(*f6i);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	if (with_fib6_nh)
1528c2ecf20Sopenharmony_ci		sz += sizeof(struct fib6_nh);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	f6i = kzalloc(sz, gfp_flags);
1558c2ecf20Sopenharmony_ci	if (!f6i)
1568c2ecf20Sopenharmony_ci		return NULL;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	/* fib6_siblings is a union with nh_list, so this initializes both */
1598c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&f6i->fib6_siblings);
1608c2ecf20Sopenharmony_ci	refcount_set(&f6i->fib6_ref, 1);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return f6i;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_civoid fib6_info_destroy_rcu(struct rcu_head *head)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	struct fib6_info *f6i = container_of(head, struct fib6_info, rcu);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	WARN_ON(f6i->fib6_node);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (f6i->nh)
1728c2ecf20Sopenharmony_ci		nexthop_put(f6i->nh);
1738c2ecf20Sopenharmony_ci	else
1748c2ecf20Sopenharmony_ci		fib6_nh_release(f6i->fib6_nh);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	ip_fib_metrics_put(f6i->fib6_metrics);
1778c2ecf20Sopenharmony_ci	kfree(f6i);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fib6_info_destroy_rcu);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic struct fib6_node *node_alloc(struct net *net)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	struct fib6_node *fn;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	fn = kmem_cache_zalloc(fib6_node_kmem, GFP_ATOMIC);
1868c2ecf20Sopenharmony_ci	if (fn)
1878c2ecf20Sopenharmony_ci		net->ipv6.rt6_stats->fib_nodes++;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	return fn;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic void node_free_immediate(struct net *net, struct fib6_node *fn)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	kmem_cache_free(fib6_node_kmem, fn);
1958c2ecf20Sopenharmony_ci	net->ipv6.rt6_stats->fib_nodes--;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic void node_free_rcu(struct rcu_head *head)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	struct fib6_node *fn = container_of(head, struct fib6_node, rcu);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	kmem_cache_free(fib6_node_kmem, fn);
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic void node_free(struct net *net, struct fib6_node *fn)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	call_rcu(&fn->rcu, node_free_rcu);
2088c2ecf20Sopenharmony_ci	net->ipv6.rt6_stats->fib_nodes--;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic void fib6_free_table(struct fib6_table *table)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	inetpeer_invalidate_tree(&table->tb6_peers);
2148c2ecf20Sopenharmony_ci	kfree(table);
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic void fib6_link_table(struct net *net, struct fib6_table *tb)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	unsigned int h;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	/*
2228c2ecf20Sopenharmony_ci	 * Initialize table lock at a single place to give lockdep a key,
2238c2ecf20Sopenharmony_ci	 * tables aren't visible prior to being linked to the list.
2248c2ecf20Sopenharmony_ci	 */
2258c2ecf20Sopenharmony_ci	spin_lock_init(&tb->tb6_lock);
2268c2ecf20Sopenharmony_ci	h = tb->tb6_id & (FIB6_TABLE_HASHSZ - 1);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	/*
2298c2ecf20Sopenharmony_ci	 * No protection necessary, this is the only list mutatation
2308c2ecf20Sopenharmony_ci	 * operation, tables never disappear once they exist.
2318c2ecf20Sopenharmony_ci	 */
2328c2ecf20Sopenharmony_ci	hlist_add_head_rcu(&tb->tb6_hlist, &net->ipv6.fib_table_hash[h]);
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic struct fib6_table *fib6_alloc_table(struct net *net, u32 id)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	struct fib6_table *table;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	table = kzalloc(sizeof(*table), GFP_ATOMIC);
2428c2ecf20Sopenharmony_ci	if (table) {
2438c2ecf20Sopenharmony_ci		table->tb6_id = id;
2448c2ecf20Sopenharmony_ci		rcu_assign_pointer(table->tb6_root.leaf,
2458c2ecf20Sopenharmony_ci				   net->ipv6.fib6_null_entry);
2468c2ecf20Sopenharmony_ci		table->tb6_root.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
2478c2ecf20Sopenharmony_ci		inet_peer_base_init(&table->tb6_peers);
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	return table;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistruct fib6_table *fib6_new_table(struct net *net, u32 id)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	struct fib6_table *tb;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (id == 0)
2588c2ecf20Sopenharmony_ci		id = RT6_TABLE_MAIN;
2598c2ecf20Sopenharmony_ci	tb = fib6_get_table(net, id);
2608c2ecf20Sopenharmony_ci	if (tb)
2618c2ecf20Sopenharmony_ci		return tb;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	tb = fib6_alloc_table(net, id);
2648c2ecf20Sopenharmony_ci	if (tb)
2658c2ecf20Sopenharmony_ci		fib6_link_table(net, tb);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	return tb;
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fib6_new_table);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistruct fib6_table *fib6_get_table(struct net *net, u32 id)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct fib6_table *tb;
2748c2ecf20Sopenharmony_ci	struct hlist_head *head;
2758c2ecf20Sopenharmony_ci	unsigned int h;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (id == 0)
2788c2ecf20Sopenharmony_ci		id = RT6_TABLE_MAIN;
2798c2ecf20Sopenharmony_ci	h = id & (FIB6_TABLE_HASHSZ - 1);
2808c2ecf20Sopenharmony_ci	rcu_read_lock();
2818c2ecf20Sopenharmony_ci	head = &net->ipv6.fib_table_hash[h];
2828c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(tb, head, tb6_hlist) {
2838c2ecf20Sopenharmony_ci		if (tb->tb6_id == id) {
2848c2ecf20Sopenharmony_ci			rcu_read_unlock();
2858c2ecf20Sopenharmony_ci			return tb;
2868c2ecf20Sopenharmony_ci		}
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci	rcu_read_unlock();
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	return NULL;
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fib6_get_table);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic void __net_init fib6_tables_init(struct net *net)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	fib6_link_table(net, net->ipv6.fib6_main_tbl);
2978c2ecf20Sopenharmony_ci	fib6_link_table(net, net->ipv6.fib6_local_tbl);
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci#else
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistruct fib6_table *fib6_new_table(struct net *net, u32 id)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	return fib6_get_table(net, id);
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistruct fib6_table *fib6_get_table(struct net *net, u32 id)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	  return net->ipv6.fib6_main_tbl;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistruct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,
3128c2ecf20Sopenharmony_ci				   const struct sk_buff *skb,
3138c2ecf20Sopenharmony_ci				   int flags, pol_lookup_t lookup)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	struct rt6_info *rt;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	rt = pol_lookup_func(lookup,
3188c2ecf20Sopenharmony_ci			net, net->ipv6.fib6_main_tbl, fl6, skb, flags);
3198c2ecf20Sopenharmony_ci	if (rt->dst.error == -EAGAIN) {
3208c2ecf20Sopenharmony_ci		ip6_rt_put_flags(rt, flags);
3218c2ecf20Sopenharmony_ci		rt = net->ipv6.ip6_null_entry;
3228c2ecf20Sopenharmony_ci		if (!(flags & RT6_LOOKUP_F_DST_NOREF))
3238c2ecf20Sopenharmony_ci			dst_hold(&rt->dst);
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	return &rt->dst;
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci/* called with rcu lock held; no reference taken on fib6_info */
3308c2ecf20Sopenharmony_ciint fib6_lookup(struct net *net, int oif, struct flowi6 *fl6,
3318c2ecf20Sopenharmony_ci		struct fib6_result *res, int flags)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	return fib6_table_lookup(net, net->ipv6.fib6_main_tbl, oif, fl6,
3348c2ecf20Sopenharmony_ci				 res, flags);
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic void __net_init fib6_tables_init(struct net *net)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	fib6_link_table(net, net->ipv6.fib6_main_tbl);
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci#endif
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ciunsigned int fib6_tables_seq_read(struct net *net)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	unsigned int h, fib_seq = 0;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	rcu_read_lock();
3498c2ecf20Sopenharmony_ci	for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
3508c2ecf20Sopenharmony_ci		struct hlist_head *head = &net->ipv6.fib_table_hash[h];
3518c2ecf20Sopenharmony_ci		struct fib6_table *tb;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci		hlist_for_each_entry_rcu(tb, head, tb6_hlist)
3548c2ecf20Sopenharmony_ci			fib_seq += tb->fib_seq;
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci	rcu_read_unlock();
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	return fib_seq;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic int call_fib6_entry_notifier(struct notifier_block *nb,
3628c2ecf20Sopenharmony_ci				    enum fib_event_type event_type,
3638c2ecf20Sopenharmony_ci				    struct fib6_info *rt,
3648c2ecf20Sopenharmony_ci				    struct netlink_ext_ack *extack)
3658c2ecf20Sopenharmony_ci{
3668c2ecf20Sopenharmony_ci	struct fib6_entry_notifier_info info = {
3678c2ecf20Sopenharmony_ci		.info.extack = extack,
3688c2ecf20Sopenharmony_ci		.rt = rt,
3698c2ecf20Sopenharmony_ci	};
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	return call_fib6_notifier(nb, event_type, &info.info);
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cistatic int call_fib6_multipath_entry_notifier(struct notifier_block *nb,
3758c2ecf20Sopenharmony_ci					      enum fib_event_type event_type,
3768c2ecf20Sopenharmony_ci					      struct fib6_info *rt,
3778c2ecf20Sopenharmony_ci					      unsigned int nsiblings,
3788c2ecf20Sopenharmony_ci					      struct netlink_ext_ack *extack)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	struct fib6_entry_notifier_info info = {
3818c2ecf20Sopenharmony_ci		.info.extack = extack,
3828c2ecf20Sopenharmony_ci		.rt = rt,
3838c2ecf20Sopenharmony_ci		.nsiblings = nsiblings,
3848c2ecf20Sopenharmony_ci	};
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	return call_fib6_notifier(nb, event_type, &info.info);
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ciint call_fib6_entry_notifiers(struct net *net,
3908c2ecf20Sopenharmony_ci			      enum fib_event_type event_type,
3918c2ecf20Sopenharmony_ci			      struct fib6_info *rt,
3928c2ecf20Sopenharmony_ci			      struct netlink_ext_ack *extack)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct fib6_entry_notifier_info info = {
3958c2ecf20Sopenharmony_ci		.info.extack = extack,
3968c2ecf20Sopenharmony_ci		.rt = rt,
3978c2ecf20Sopenharmony_ci	};
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	rt->fib6_table->fib_seq++;
4008c2ecf20Sopenharmony_ci	return call_fib6_notifiers(net, event_type, &info.info);
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ciint call_fib6_multipath_entry_notifiers(struct net *net,
4048c2ecf20Sopenharmony_ci					enum fib_event_type event_type,
4058c2ecf20Sopenharmony_ci					struct fib6_info *rt,
4068c2ecf20Sopenharmony_ci					unsigned int nsiblings,
4078c2ecf20Sopenharmony_ci					struct netlink_ext_ack *extack)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	struct fib6_entry_notifier_info info = {
4108c2ecf20Sopenharmony_ci		.info.extack = extack,
4118c2ecf20Sopenharmony_ci		.rt = rt,
4128c2ecf20Sopenharmony_ci		.nsiblings = nsiblings,
4138c2ecf20Sopenharmony_ci	};
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	rt->fib6_table->fib_seq++;
4168c2ecf20Sopenharmony_ci	return call_fib6_notifiers(net, event_type, &info.info);
4178c2ecf20Sopenharmony_ci}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ciint call_fib6_entry_notifiers_replace(struct net *net, struct fib6_info *rt)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	struct fib6_entry_notifier_info info = {
4228c2ecf20Sopenharmony_ci		.rt = rt,
4238c2ecf20Sopenharmony_ci		.nsiblings = rt->fib6_nsiblings,
4248c2ecf20Sopenharmony_ci	};
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	rt->fib6_table->fib_seq++;
4278c2ecf20Sopenharmony_ci	return call_fib6_notifiers(net, FIB_EVENT_ENTRY_REPLACE, &info.info);
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistruct fib6_dump_arg {
4318c2ecf20Sopenharmony_ci	struct net *net;
4328c2ecf20Sopenharmony_ci	struct notifier_block *nb;
4338c2ecf20Sopenharmony_ci	struct netlink_ext_ack *extack;
4348c2ecf20Sopenharmony_ci};
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_cistatic int fib6_rt_dump(struct fib6_info *rt, struct fib6_dump_arg *arg)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	enum fib_event_type fib_event = FIB_EVENT_ENTRY_REPLACE;
4398c2ecf20Sopenharmony_ci	int err;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	if (!rt || rt == arg->net->ipv6.fib6_null_entry)
4428c2ecf20Sopenharmony_ci		return 0;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	if (rt->fib6_nsiblings)
4458c2ecf20Sopenharmony_ci		err = call_fib6_multipath_entry_notifier(arg->nb, fib_event,
4468c2ecf20Sopenharmony_ci							 rt,
4478c2ecf20Sopenharmony_ci							 rt->fib6_nsiblings,
4488c2ecf20Sopenharmony_ci							 arg->extack);
4498c2ecf20Sopenharmony_ci	else
4508c2ecf20Sopenharmony_ci		err = call_fib6_entry_notifier(arg->nb, fib_event, rt,
4518c2ecf20Sopenharmony_ci					       arg->extack);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	return err;
4548c2ecf20Sopenharmony_ci}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cistatic int fib6_node_dump(struct fib6_walker *w)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	int err;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	err = fib6_rt_dump(w->leaf, w->args);
4618c2ecf20Sopenharmony_ci	w->leaf = NULL;
4628c2ecf20Sopenharmony_ci	return err;
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic int fib6_table_dump(struct net *net, struct fib6_table *tb,
4668c2ecf20Sopenharmony_ci			   struct fib6_walker *w)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	int err;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	w->root = &tb->tb6_root;
4718c2ecf20Sopenharmony_ci	spin_lock_bh(&tb->tb6_lock);
4728c2ecf20Sopenharmony_ci	err = fib6_walk(net, w);
4738c2ecf20Sopenharmony_ci	spin_unlock_bh(&tb->tb6_lock);
4748c2ecf20Sopenharmony_ci	return err;
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci/* Called with rcu_read_lock() */
4788c2ecf20Sopenharmony_ciint fib6_tables_dump(struct net *net, struct notifier_block *nb,
4798c2ecf20Sopenharmony_ci		     struct netlink_ext_ack *extack)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	struct fib6_dump_arg arg;
4828c2ecf20Sopenharmony_ci	struct fib6_walker *w;
4838c2ecf20Sopenharmony_ci	unsigned int h;
4848c2ecf20Sopenharmony_ci	int err = 0;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	w = kzalloc(sizeof(*w), GFP_ATOMIC);
4878c2ecf20Sopenharmony_ci	if (!w)
4888c2ecf20Sopenharmony_ci		return -ENOMEM;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	w->func = fib6_node_dump;
4918c2ecf20Sopenharmony_ci	arg.net = net;
4928c2ecf20Sopenharmony_ci	arg.nb = nb;
4938c2ecf20Sopenharmony_ci	arg.extack = extack;
4948c2ecf20Sopenharmony_ci	w->args = &arg;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
4978c2ecf20Sopenharmony_ci		struct hlist_head *head = &net->ipv6.fib_table_hash[h];
4988c2ecf20Sopenharmony_ci		struct fib6_table *tb;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci		hlist_for_each_entry_rcu(tb, head, tb6_hlist) {
5018c2ecf20Sopenharmony_ci			err = fib6_table_dump(net, tb, w);
5028c2ecf20Sopenharmony_ci			if (err)
5038c2ecf20Sopenharmony_ci				goto out;
5048c2ecf20Sopenharmony_ci		}
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ciout:
5088c2ecf20Sopenharmony_ci	kfree(w);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	/* The tree traversal function should never return a positive value. */
5118c2ecf20Sopenharmony_ci	return err > 0 ? -EINVAL : err;
5128c2ecf20Sopenharmony_ci}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_cistatic int fib6_dump_node(struct fib6_walker *w)
5158c2ecf20Sopenharmony_ci{
5168c2ecf20Sopenharmony_ci	int res;
5178c2ecf20Sopenharmony_ci	struct fib6_info *rt;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	for_each_fib6_walker_rt(w) {
5208c2ecf20Sopenharmony_ci		res = rt6_dump_route(rt, w->args, w->skip_in_node);
5218c2ecf20Sopenharmony_ci		if (res >= 0) {
5228c2ecf20Sopenharmony_ci			/* Frame is full, suspend walking */
5238c2ecf20Sopenharmony_ci			w->leaf = rt;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci			/* We'll restart from this node, so if some routes were
5268c2ecf20Sopenharmony_ci			 * already dumped, skip them next time.
5278c2ecf20Sopenharmony_ci			 */
5288c2ecf20Sopenharmony_ci			w->skip_in_node += res;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci			return 1;
5318c2ecf20Sopenharmony_ci		}
5328c2ecf20Sopenharmony_ci		w->skip_in_node = 0;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci		/* Multipath routes are dumped in one route with the
5358c2ecf20Sopenharmony_ci		 * RTA_MULTIPATH attribute. Jump 'rt' to point to the
5368c2ecf20Sopenharmony_ci		 * last sibling of this route (no need to dump the
5378c2ecf20Sopenharmony_ci		 * sibling routes again)
5388c2ecf20Sopenharmony_ci		 */
5398c2ecf20Sopenharmony_ci		if (rt->fib6_nsiblings)
5408c2ecf20Sopenharmony_ci			rt = list_last_entry(&rt->fib6_siblings,
5418c2ecf20Sopenharmony_ci					     struct fib6_info,
5428c2ecf20Sopenharmony_ci					     fib6_siblings);
5438c2ecf20Sopenharmony_ci	}
5448c2ecf20Sopenharmony_ci	w->leaf = NULL;
5458c2ecf20Sopenharmony_ci	return 0;
5468c2ecf20Sopenharmony_ci}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_cistatic void fib6_dump_end(struct netlink_callback *cb)
5498c2ecf20Sopenharmony_ci{
5508c2ecf20Sopenharmony_ci	struct net *net = sock_net(cb->skb->sk);
5518c2ecf20Sopenharmony_ci	struct fib6_walker *w = (void *)cb->args[2];
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	if (w) {
5548c2ecf20Sopenharmony_ci		if (cb->args[4]) {
5558c2ecf20Sopenharmony_ci			cb->args[4] = 0;
5568c2ecf20Sopenharmony_ci			fib6_walker_unlink(net, w);
5578c2ecf20Sopenharmony_ci		}
5588c2ecf20Sopenharmony_ci		cb->args[2] = 0;
5598c2ecf20Sopenharmony_ci		kfree(w);
5608c2ecf20Sopenharmony_ci	}
5618c2ecf20Sopenharmony_ci	cb->done = (void *)cb->args[3];
5628c2ecf20Sopenharmony_ci	cb->args[1] = 3;
5638c2ecf20Sopenharmony_ci}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_cistatic int fib6_dump_done(struct netlink_callback *cb)
5668c2ecf20Sopenharmony_ci{
5678c2ecf20Sopenharmony_ci	fib6_dump_end(cb);
5688c2ecf20Sopenharmony_ci	return cb->done ? cb->done(cb) : 0;
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_cistatic int fib6_dump_table(struct fib6_table *table, struct sk_buff *skb,
5728c2ecf20Sopenharmony_ci			   struct netlink_callback *cb)
5738c2ecf20Sopenharmony_ci{
5748c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
5758c2ecf20Sopenharmony_ci	struct fib6_walker *w;
5768c2ecf20Sopenharmony_ci	int res;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	w = (void *)cb->args[2];
5798c2ecf20Sopenharmony_ci	w->root = &table->tb6_root;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	if (cb->args[4] == 0) {
5828c2ecf20Sopenharmony_ci		w->count = 0;
5838c2ecf20Sopenharmony_ci		w->skip = 0;
5848c2ecf20Sopenharmony_ci		w->skip_in_node = 0;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci		spin_lock_bh(&table->tb6_lock);
5878c2ecf20Sopenharmony_ci		res = fib6_walk(net, w);
5888c2ecf20Sopenharmony_ci		spin_unlock_bh(&table->tb6_lock);
5898c2ecf20Sopenharmony_ci		if (res > 0) {
5908c2ecf20Sopenharmony_ci			cb->args[4] = 1;
5918c2ecf20Sopenharmony_ci			cb->args[5] = READ_ONCE(w->root->fn_sernum);
5928c2ecf20Sopenharmony_ci		}
5938c2ecf20Sopenharmony_ci	} else {
5948c2ecf20Sopenharmony_ci		int sernum = READ_ONCE(w->root->fn_sernum);
5958c2ecf20Sopenharmony_ci		if (cb->args[5] != sernum) {
5968c2ecf20Sopenharmony_ci			/* Begin at the root if the tree changed */
5978c2ecf20Sopenharmony_ci			cb->args[5] = sernum;
5988c2ecf20Sopenharmony_ci			w->state = FWS_INIT;
5998c2ecf20Sopenharmony_ci			w->node = w->root;
6008c2ecf20Sopenharmony_ci			w->skip = w->count;
6018c2ecf20Sopenharmony_ci			w->skip_in_node = 0;
6028c2ecf20Sopenharmony_ci		} else
6038c2ecf20Sopenharmony_ci			w->skip = 0;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci		spin_lock_bh(&table->tb6_lock);
6068c2ecf20Sopenharmony_ci		res = fib6_walk_continue(w);
6078c2ecf20Sopenharmony_ci		spin_unlock_bh(&table->tb6_lock);
6088c2ecf20Sopenharmony_ci		if (res <= 0) {
6098c2ecf20Sopenharmony_ci			fib6_walker_unlink(net, w);
6108c2ecf20Sopenharmony_ci			cb->args[4] = 0;
6118c2ecf20Sopenharmony_ci		}
6128c2ecf20Sopenharmony_ci	}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	return res;
6158c2ecf20Sopenharmony_ci}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_cistatic int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
6188c2ecf20Sopenharmony_ci{
6198c2ecf20Sopenharmony_ci	struct rt6_rtnl_dump_arg arg = { .filter.dump_exceptions = true,
6208c2ecf20Sopenharmony_ci					 .filter.dump_routes = true };
6218c2ecf20Sopenharmony_ci	const struct nlmsghdr *nlh = cb->nlh;
6228c2ecf20Sopenharmony_ci	struct net *net = sock_net(skb->sk);
6238c2ecf20Sopenharmony_ci	unsigned int h, s_h;
6248c2ecf20Sopenharmony_ci	unsigned int e = 0, s_e;
6258c2ecf20Sopenharmony_ci	struct fib6_walker *w;
6268c2ecf20Sopenharmony_ci	struct fib6_table *tb;
6278c2ecf20Sopenharmony_ci	struct hlist_head *head;
6288c2ecf20Sopenharmony_ci	int res = 0;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	if (cb->strict_check) {
6318c2ecf20Sopenharmony_ci		int err;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci		err = ip_valid_fib_dump_req(net, nlh, &arg.filter, cb);
6348c2ecf20Sopenharmony_ci		if (err < 0)
6358c2ecf20Sopenharmony_ci			return err;
6368c2ecf20Sopenharmony_ci	} else if (nlmsg_len(nlh) >= sizeof(struct rtmsg)) {
6378c2ecf20Sopenharmony_ci		struct rtmsg *rtm = nlmsg_data(nlh);
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci		if (rtm->rtm_flags & RTM_F_PREFIX)
6408c2ecf20Sopenharmony_ci			arg.filter.flags = RTM_F_PREFIX;
6418c2ecf20Sopenharmony_ci	}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	w = (void *)cb->args[2];
6448c2ecf20Sopenharmony_ci	if (!w) {
6458c2ecf20Sopenharmony_ci		/* New dump:
6468c2ecf20Sopenharmony_ci		 *
6478c2ecf20Sopenharmony_ci		 * 1. allocate and initialize walker.
6488c2ecf20Sopenharmony_ci		 */
6498c2ecf20Sopenharmony_ci		w = kzalloc(sizeof(*w), GFP_ATOMIC);
6508c2ecf20Sopenharmony_ci		if (!w)
6518c2ecf20Sopenharmony_ci			return -ENOMEM;
6528c2ecf20Sopenharmony_ci		w->func = fib6_dump_node;
6538c2ecf20Sopenharmony_ci		cb->args[2] = (long)w;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci		/* 2. hook callback destructor.
6568c2ecf20Sopenharmony_ci		 */
6578c2ecf20Sopenharmony_ci		cb->args[3] = (long)cb->done;
6588c2ecf20Sopenharmony_ci		cb->done = fib6_dump_done;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	}
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	arg.skb = skb;
6638c2ecf20Sopenharmony_ci	arg.cb = cb;
6648c2ecf20Sopenharmony_ci	arg.net = net;
6658c2ecf20Sopenharmony_ci	w->args = &arg;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	if (arg.filter.table_id) {
6688c2ecf20Sopenharmony_ci		tb = fib6_get_table(net, arg.filter.table_id);
6698c2ecf20Sopenharmony_ci		if (!tb) {
6708c2ecf20Sopenharmony_ci			if (rtnl_msg_family(cb->nlh) != PF_INET6)
6718c2ecf20Sopenharmony_ci				goto out;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(cb->extack, "FIB table does not exist");
6748c2ecf20Sopenharmony_ci			return -ENOENT;
6758c2ecf20Sopenharmony_ci		}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci		if (!cb->args[0]) {
6788c2ecf20Sopenharmony_ci			res = fib6_dump_table(tb, skb, cb);
6798c2ecf20Sopenharmony_ci			if (!res)
6808c2ecf20Sopenharmony_ci				cb->args[0] = 1;
6818c2ecf20Sopenharmony_ci		}
6828c2ecf20Sopenharmony_ci		goto out;
6838c2ecf20Sopenharmony_ci	}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	s_h = cb->args[0];
6868c2ecf20Sopenharmony_ci	s_e = cb->args[1];
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	rcu_read_lock();
6898c2ecf20Sopenharmony_ci	for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_e = 0) {
6908c2ecf20Sopenharmony_ci		e = 0;
6918c2ecf20Sopenharmony_ci		head = &net->ipv6.fib_table_hash[h];
6928c2ecf20Sopenharmony_ci		hlist_for_each_entry_rcu(tb, head, tb6_hlist) {
6938c2ecf20Sopenharmony_ci			if (e < s_e)
6948c2ecf20Sopenharmony_ci				goto next;
6958c2ecf20Sopenharmony_ci			res = fib6_dump_table(tb, skb, cb);
6968c2ecf20Sopenharmony_ci			if (res != 0)
6978c2ecf20Sopenharmony_ci				goto out_unlock;
6988c2ecf20Sopenharmony_cinext:
6998c2ecf20Sopenharmony_ci			e++;
7008c2ecf20Sopenharmony_ci		}
7018c2ecf20Sopenharmony_ci	}
7028c2ecf20Sopenharmony_ciout_unlock:
7038c2ecf20Sopenharmony_ci	rcu_read_unlock();
7048c2ecf20Sopenharmony_ci	cb->args[1] = e;
7058c2ecf20Sopenharmony_ci	cb->args[0] = h;
7068c2ecf20Sopenharmony_ciout:
7078c2ecf20Sopenharmony_ci	res = res < 0 ? res : skb->len;
7088c2ecf20Sopenharmony_ci	if (res <= 0)
7098c2ecf20Sopenharmony_ci		fib6_dump_end(cb);
7108c2ecf20Sopenharmony_ci	return res;
7118c2ecf20Sopenharmony_ci}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_civoid fib6_metric_set(struct fib6_info *f6i, int metric, u32 val)
7148c2ecf20Sopenharmony_ci{
7158c2ecf20Sopenharmony_ci	if (!f6i)
7168c2ecf20Sopenharmony_ci		return;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	if (f6i->fib6_metrics == &dst_default_metrics) {
7198c2ecf20Sopenharmony_ci		struct dst_metrics *p = kzalloc(sizeof(*p), GFP_ATOMIC);
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci		if (!p)
7228c2ecf20Sopenharmony_ci			return;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci		refcount_set(&p->refcnt, 1);
7258c2ecf20Sopenharmony_ci		f6i->fib6_metrics = p;
7268c2ecf20Sopenharmony_ci	}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	f6i->fib6_metrics->metrics[metric - 1] = val;
7298c2ecf20Sopenharmony_ci}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci/*
7328c2ecf20Sopenharmony_ci *	Routing Table
7338c2ecf20Sopenharmony_ci *
7348c2ecf20Sopenharmony_ci *	return the appropriate node for a routing tree "add" operation
7358c2ecf20Sopenharmony_ci *	by either creating and inserting or by returning an existing
7368c2ecf20Sopenharmony_ci *	node.
7378c2ecf20Sopenharmony_ci */
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_cistatic struct fib6_node *fib6_add_1(struct net *net,
7408c2ecf20Sopenharmony_ci				    struct fib6_table *table,
7418c2ecf20Sopenharmony_ci				    struct fib6_node *root,
7428c2ecf20Sopenharmony_ci				    struct in6_addr *addr, int plen,
7438c2ecf20Sopenharmony_ci				    int offset, int allow_create,
7448c2ecf20Sopenharmony_ci				    int replace_required,
7458c2ecf20Sopenharmony_ci				    struct netlink_ext_ack *extack)
7468c2ecf20Sopenharmony_ci{
7478c2ecf20Sopenharmony_ci	struct fib6_node *fn, *in, *ln;
7488c2ecf20Sopenharmony_ci	struct fib6_node *pn = NULL;
7498c2ecf20Sopenharmony_ci	struct rt6key *key;
7508c2ecf20Sopenharmony_ci	int	bit;
7518c2ecf20Sopenharmony_ci	__be32	dir = 0;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	RT6_TRACE("fib6_add_1\n");
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	/* insert node in tree */
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	fn = root;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	do {
7608c2ecf20Sopenharmony_ci		struct fib6_info *leaf = rcu_dereference_protected(fn->leaf,
7618c2ecf20Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
7628c2ecf20Sopenharmony_ci		key = (struct rt6key *)((u8 *)leaf + offset);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci		/*
7658c2ecf20Sopenharmony_ci		 *	Prefix match
7668c2ecf20Sopenharmony_ci		 */
7678c2ecf20Sopenharmony_ci		if (plen < fn->fn_bit ||
7688c2ecf20Sopenharmony_ci		    !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit)) {
7698c2ecf20Sopenharmony_ci			if (!allow_create) {
7708c2ecf20Sopenharmony_ci				if (replace_required) {
7718c2ecf20Sopenharmony_ci					NL_SET_ERR_MSG(extack,
7728c2ecf20Sopenharmony_ci						       "Can not replace route - no match found");
7738c2ecf20Sopenharmony_ci					pr_warn("Can't replace route, no match found\n");
7748c2ecf20Sopenharmony_ci					return ERR_PTR(-ENOENT);
7758c2ecf20Sopenharmony_ci				}
7768c2ecf20Sopenharmony_ci				pr_warn("NLM_F_CREATE should be set when creating new route\n");
7778c2ecf20Sopenharmony_ci			}
7788c2ecf20Sopenharmony_ci			goto insert_above;
7798c2ecf20Sopenharmony_ci		}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci		/*
7828c2ecf20Sopenharmony_ci		 *	Exact match ?
7838c2ecf20Sopenharmony_ci		 */
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci		if (plen == fn->fn_bit) {
7868c2ecf20Sopenharmony_ci			/* clean up an intermediate node */
7878c2ecf20Sopenharmony_ci			if (!(fn->fn_flags & RTN_RTINFO)) {
7888c2ecf20Sopenharmony_ci				RCU_INIT_POINTER(fn->leaf, NULL);
7898c2ecf20Sopenharmony_ci				fib6_info_release(leaf);
7908c2ecf20Sopenharmony_ci			/* remove null_entry in the root node */
7918c2ecf20Sopenharmony_ci			} else if (fn->fn_flags & RTN_TL_ROOT &&
7928c2ecf20Sopenharmony_ci				   rcu_access_pointer(fn->leaf) ==
7938c2ecf20Sopenharmony_ci				   net->ipv6.fib6_null_entry) {
7948c2ecf20Sopenharmony_ci				RCU_INIT_POINTER(fn->leaf, NULL);
7958c2ecf20Sopenharmony_ci			}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci			return fn;
7988c2ecf20Sopenharmony_ci		}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci		/*
8018c2ecf20Sopenharmony_ci		 *	We have more bits to go
8028c2ecf20Sopenharmony_ci		 */
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci		/* Try to walk down on tree. */
8058c2ecf20Sopenharmony_ci		dir = addr_bit_set(addr, fn->fn_bit);
8068c2ecf20Sopenharmony_ci		pn = fn;
8078c2ecf20Sopenharmony_ci		fn = dir ?
8088c2ecf20Sopenharmony_ci		     rcu_dereference_protected(fn->right,
8098c2ecf20Sopenharmony_ci					lockdep_is_held(&table->tb6_lock)) :
8108c2ecf20Sopenharmony_ci		     rcu_dereference_protected(fn->left,
8118c2ecf20Sopenharmony_ci					lockdep_is_held(&table->tb6_lock));
8128c2ecf20Sopenharmony_ci	} while (fn);
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	if (!allow_create) {
8158c2ecf20Sopenharmony_ci		/* We should not create new node because
8168c2ecf20Sopenharmony_ci		 * NLM_F_REPLACE was specified without NLM_F_CREATE
8178c2ecf20Sopenharmony_ci		 * I assume it is safe to require NLM_F_CREATE when
8188c2ecf20Sopenharmony_ci		 * REPLACE flag is used! Later we may want to remove the
8198c2ecf20Sopenharmony_ci		 * check for replace_required, because according
8208c2ecf20Sopenharmony_ci		 * to netlink specification, NLM_F_CREATE
8218c2ecf20Sopenharmony_ci		 * MUST be specified if new route is created.
8228c2ecf20Sopenharmony_ci		 * That would keep IPv6 consistent with IPv4
8238c2ecf20Sopenharmony_ci		 */
8248c2ecf20Sopenharmony_ci		if (replace_required) {
8258c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG(extack,
8268c2ecf20Sopenharmony_ci				       "Can not replace route - no match found");
8278c2ecf20Sopenharmony_ci			pr_warn("Can't replace route, no match found\n");
8288c2ecf20Sopenharmony_ci			return ERR_PTR(-ENOENT);
8298c2ecf20Sopenharmony_ci		}
8308c2ecf20Sopenharmony_ci		pr_warn("NLM_F_CREATE should be set when creating new route\n");
8318c2ecf20Sopenharmony_ci	}
8328c2ecf20Sopenharmony_ci	/*
8338c2ecf20Sopenharmony_ci	 *	We walked to the bottom of tree.
8348c2ecf20Sopenharmony_ci	 *	Create new leaf node without children.
8358c2ecf20Sopenharmony_ci	 */
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	ln = node_alloc(net);
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	if (!ln)
8408c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
8418c2ecf20Sopenharmony_ci	ln->fn_bit = plen;
8428c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(ln->parent, pn);
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	if (dir)
8458c2ecf20Sopenharmony_ci		rcu_assign_pointer(pn->right, ln);
8468c2ecf20Sopenharmony_ci	else
8478c2ecf20Sopenharmony_ci		rcu_assign_pointer(pn->left, ln);
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	return ln;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ciinsert_above:
8538c2ecf20Sopenharmony_ci	/*
8548c2ecf20Sopenharmony_ci	 * split since we don't have a common prefix anymore or
8558c2ecf20Sopenharmony_ci	 * we have a less significant route.
8568c2ecf20Sopenharmony_ci	 * we've to insert an intermediate node on the list
8578c2ecf20Sopenharmony_ci	 * this new node will point to the one we need to create
8588c2ecf20Sopenharmony_ci	 * and the current
8598c2ecf20Sopenharmony_ci	 */
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	pn = rcu_dereference_protected(fn->parent,
8628c2ecf20Sopenharmony_ci				       lockdep_is_held(&table->tb6_lock));
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	/* find 1st bit in difference between the 2 addrs.
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	   See comment in __ipv6_addr_diff: bit may be an invalid value,
8678c2ecf20Sopenharmony_ci	   but if it is >= plen, the value is ignored in any case.
8688c2ecf20Sopenharmony_ci	 */
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	bit = __ipv6_addr_diff(addr, &key->addr, sizeof(*addr));
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	/*
8738c2ecf20Sopenharmony_ci	 *		(intermediate)[in]
8748c2ecf20Sopenharmony_ci	 *	          /	   \
8758c2ecf20Sopenharmony_ci	 *	(new leaf node)[ln] (old node)[fn]
8768c2ecf20Sopenharmony_ci	 */
8778c2ecf20Sopenharmony_ci	if (plen > bit) {
8788c2ecf20Sopenharmony_ci		in = node_alloc(net);
8798c2ecf20Sopenharmony_ci		ln = node_alloc(net);
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci		if (!in || !ln) {
8828c2ecf20Sopenharmony_ci			if (in)
8838c2ecf20Sopenharmony_ci				node_free_immediate(net, in);
8848c2ecf20Sopenharmony_ci			if (ln)
8858c2ecf20Sopenharmony_ci				node_free_immediate(net, ln);
8868c2ecf20Sopenharmony_ci			return ERR_PTR(-ENOMEM);
8878c2ecf20Sopenharmony_ci		}
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci		/*
8908c2ecf20Sopenharmony_ci		 * new intermediate node.
8918c2ecf20Sopenharmony_ci		 * RTN_RTINFO will
8928c2ecf20Sopenharmony_ci		 * be off since that an address that chooses one of
8938c2ecf20Sopenharmony_ci		 * the branches would not match less specific routes
8948c2ecf20Sopenharmony_ci		 * in the other branch
8958c2ecf20Sopenharmony_ci		 */
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci		in->fn_bit = bit;
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci		RCU_INIT_POINTER(in->parent, pn);
9008c2ecf20Sopenharmony_ci		in->leaf = fn->leaf;
9018c2ecf20Sopenharmony_ci		fib6_info_hold(rcu_dereference_protected(in->leaf,
9028c2ecf20Sopenharmony_ci				lockdep_is_held(&table->tb6_lock)));
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci		/* update parent pointer */
9058c2ecf20Sopenharmony_ci		if (dir)
9068c2ecf20Sopenharmony_ci			rcu_assign_pointer(pn->right, in);
9078c2ecf20Sopenharmony_ci		else
9088c2ecf20Sopenharmony_ci			rcu_assign_pointer(pn->left, in);
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci		ln->fn_bit = plen;
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci		RCU_INIT_POINTER(ln->parent, in);
9138c2ecf20Sopenharmony_ci		rcu_assign_pointer(fn->parent, in);
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci		if (addr_bit_set(addr, bit)) {
9168c2ecf20Sopenharmony_ci			rcu_assign_pointer(in->right, ln);
9178c2ecf20Sopenharmony_ci			rcu_assign_pointer(in->left, fn);
9188c2ecf20Sopenharmony_ci		} else {
9198c2ecf20Sopenharmony_ci			rcu_assign_pointer(in->left, ln);
9208c2ecf20Sopenharmony_ci			rcu_assign_pointer(in->right, fn);
9218c2ecf20Sopenharmony_ci		}
9228c2ecf20Sopenharmony_ci	} else { /* plen <= bit */
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci		/*
9258c2ecf20Sopenharmony_ci		 *		(new leaf node)[ln]
9268c2ecf20Sopenharmony_ci		 *	          /	   \
9278c2ecf20Sopenharmony_ci		 *	     (old node)[fn] NULL
9288c2ecf20Sopenharmony_ci		 */
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci		ln = node_alloc(net);
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci		if (!ln)
9338c2ecf20Sopenharmony_ci			return ERR_PTR(-ENOMEM);
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci		ln->fn_bit = plen;
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci		RCU_INIT_POINTER(ln->parent, pn);
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci		if (addr_bit_set(&key->addr, plen))
9408c2ecf20Sopenharmony_ci			RCU_INIT_POINTER(ln->right, fn);
9418c2ecf20Sopenharmony_ci		else
9428c2ecf20Sopenharmony_ci			RCU_INIT_POINTER(ln->left, fn);
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci		rcu_assign_pointer(fn->parent, ln);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci		if (dir)
9478c2ecf20Sopenharmony_ci			rcu_assign_pointer(pn->right, ln);
9488c2ecf20Sopenharmony_ci		else
9498c2ecf20Sopenharmony_ci			rcu_assign_pointer(pn->left, ln);
9508c2ecf20Sopenharmony_ci	}
9518c2ecf20Sopenharmony_ci	return ln;
9528c2ecf20Sopenharmony_ci}
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_cistatic void __fib6_drop_pcpu_from(struct fib6_nh *fib6_nh,
9558c2ecf20Sopenharmony_ci				  const struct fib6_info *match,
9568c2ecf20Sopenharmony_ci				  const struct fib6_table *table)
9578c2ecf20Sopenharmony_ci{
9588c2ecf20Sopenharmony_ci	int cpu;
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	if (!fib6_nh->rt6i_pcpu)
9618c2ecf20Sopenharmony_ci		return;
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	rcu_read_lock();
9648c2ecf20Sopenharmony_ci	/* release the reference to this fib entry from
9658c2ecf20Sopenharmony_ci	 * all of its cached pcpu routes
9668c2ecf20Sopenharmony_ci	 */
9678c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
9688c2ecf20Sopenharmony_ci		struct rt6_info **ppcpu_rt;
9698c2ecf20Sopenharmony_ci		struct rt6_info *pcpu_rt;
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci		ppcpu_rt = per_cpu_ptr(fib6_nh->rt6i_pcpu, cpu);
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci		/* Paired with xchg() in rt6_get_pcpu_route() */
9748c2ecf20Sopenharmony_ci		pcpu_rt = READ_ONCE(*ppcpu_rt);
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci		/* only dropping the 'from' reference if the cached route
9778c2ecf20Sopenharmony_ci		 * is using 'match'. The cached pcpu_rt->from only changes
9788c2ecf20Sopenharmony_ci		 * from a fib6_info to NULL (ip6_dst_destroy); it can never
9798c2ecf20Sopenharmony_ci		 * change from one fib6_info reference to another
9808c2ecf20Sopenharmony_ci		 */
9818c2ecf20Sopenharmony_ci		if (pcpu_rt && rcu_access_pointer(pcpu_rt->from) == match) {
9828c2ecf20Sopenharmony_ci			struct fib6_info *from;
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci			from = xchg((__force struct fib6_info **)&pcpu_rt->from, NULL);
9858c2ecf20Sopenharmony_ci			fib6_info_release(from);
9868c2ecf20Sopenharmony_ci		}
9878c2ecf20Sopenharmony_ci	}
9888c2ecf20Sopenharmony_ci	rcu_read_unlock();
9898c2ecf20Sopenharmony_ci}
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_cistruct fib6_nh_pcpu_arg {
9928c2ecf20Sopenharmony_ci	struct fib6_info	*from;
9938c2ecf20Sopenharmony_ci	const struct fib6_table *table;
9948c2ecf20Sopenharmony_ci};
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_cistatic int fib6_nh_drop_pcpu_from(struct fib6_nh *nh, void *_arg)
9978c2ecf20Sopenharmony_ci{
9988c2ecf20Sopenharmony_ci	struct fib6_nh_pcpu_arg *arg = _arg;
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	__fib6_drop_pcpu_from(nh, arg->from, arg->table);
10018c2ecf20Sopenharmony_ci	return 0;
10028c2ecf20Sopenharmony_ci}
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_cistatic void fib6_drop_pcpu_from(struct fib6_info *f6i,
10058c2ecf20Sopenharmony_ci				const struct fib6_table *table)
10068c2ecf20Sopenharmony_ci{
10078c2ecf20Sopenharmony_ci	/* Make sure rt6_make_pcpu_route() wont add other percpu routes
10088c2ecf20Sopenharmony_ci	 * while we are cleaning them here.
10098c2ecf20Sopenharmony_ci	 */
10108c2ecf20Sopenharmony_ci	f6i->fib6_destroying = 1;
10118c2ecf20Sopenharmony_ci	mb(); /* paired with the cmpxchg() in rt6_make_pcpu_route() */
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	if (f6i->nh) {
10148c2ecf20Sopenharmony_ci		struct fib6_nh_pcpu_arg arg = {
10158c2ecf20Sopenharmony_ci			.from = f6i,
10168c2ecf20Sopenharmony_ci			.table = table
10178c2ecf20Sopenharmony_ci		};
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci		nexthop_for_each_fib6_nh(f6i->nh, fib6_nh_drop_pcpu_from,
10208c2ecf20Sopenharmony_ci					 &arg);
10218c2ecf20Sopenharmony_ci	} else {
10228c2ecf20Sopenharmony_ci		struct fib6_nh *fib6_nh;
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci		fib6_nh = f6i->fib6_nh;
10258c2ecf20Sopenharmony_ci		__fib6_drop_pcpu_from(fib6_nh, f6i, table);
10268c2ecf20Sopenharmony_ci	}
10278c2ecf20Sopenharmony_ci}
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_cistatic void fib6_purge_rt(struct fib6_info *rt, struct fib6_node *fn,
10308c2ecf20Sopenharmony_ci			  struct net *net)
10318c2ecf20Sopenharmony_ci{
10328c2ecf20Sopenharmony_ci	struct fib6_table *table = rt->fib6_table;
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	/* Flush all cached dst in exception table */
10358c2ecf20Sopenharmony_ci	rt6_flush_exceptions(rt);
10368c2ecf20Sopenharmony_ci	fib6_drop_pcpu_from(rt, table);
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	if (rt->nh && !list_empty(&rt->nh_list))
10398c2ecf20Sopenharmony_ci		list_del_init(&rt->nh_list);
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	if (refcount_read(&rt->fib6_ref) != 1) {
10428c2ecf20Sopenharmony_ci		/* This route is used as dummy address holder in some split
10438c2ecf20Sopenharmony_ci		 * nodes. It is not leaked, but it still holds other resources,
10448c2ecf20Sopenharmony_ci		 * which must be released in time. So, scan ascendant nodes
10458c2ecf20Sopenharmony_ci		 * and replace dummy references to this route with references
10468c2ecf20Sopenharmony_ci		 * to still alive ones.
10478c2ecf20Sopenharmony_ci		 */
10488c2ecf20Sopenharmony_ci		while (fn) {
10498c2ecf20Sopenharmony_ci			struct fib6_info *leaf = rcu_dereference_protected(fn->leaf,
10508c2ecf20Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
10518c2ecf20Sopenharmony_ci			struct fib6_info *new_leaf;
10528c2ecf20Sopenharmony_ci			if (!(fn->fn_flags & RTN_RTINFO) && leaf == rt) {
10538c2ecf20Sopenharmony_ci				new_leaf = fib6_find_prefix(net, table, fn);
10548c2ecf20Sopenharmony_ci				fib6_info_hold(new_leaf);
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci				rcu_assign_pointer(fn->leaf, new_leaf);
10578c2ecf20Sopenharmony_ci				fib6_info_release(rt);
10588c2ecf20Sopenharmony_ci			}
10598c2ecf20Sopenharmony_ci			fn = rcu_dereference_protected(fn->parent,
10608c2ecf20Sopenharmony_ci				    lockdep_is_held(&table->tb6_lock));
10618c2ecf20Sopenharmony_ci		}
10628c2ecf20Sopenharmony_ci	}
10638c2ecf20Sopenharmony_ci}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci/*
10668c2ecf20Sopenharmony_ci *	Insert routing information in a node.
10678c2ecf20Sopenharmony_ci */
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_cistatic int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
10708c2ecf20Sopenharmony_ci			    struct nl_info *info,
10718c2ecf20Sopenharmony_ci			    struct netlink_ext_ack *extack)
10728c2ecf20Sopenharmony_ci{
10738c2ecf20Sopenharmony_ci	struct fib6_info *leaf = rcu_dereference_protected(fn->leaf,
10748c2ecf20Sopenharmony_ci				    lockdep_is_held(&rt->fib6_table->tb6_lock));
10758c2ecf20Sopenharmony_ci	struct fib6_info *iter = NULL;
10768c2ecf20Sopenharmony_ci	struct fib6_info __rcu **ins;
10778c2ecf20Sopenharmony_ci	struct fib6_info __rcu **fallback_ins = NULL;
10788c2ecf20Sopenharmony_ci	int replace = (info->nlh &&
10798c2ecf20Sopenharmony_ci		       (info->nlh->nlmsg_flags & NLM_F_REPLACE));
10808c2ecf20Sopenharmony_ci	int add = (!info->nlh ||
10818c2ecf20Sopenharmony_ci		   (info->nlh->nlmsg_flags & NLM_F_CREATE));
10828c2ecf20Sopenharmony_ci	int found = 0;
10838c2ecf20Sopenharmony_ci	bool rt_can_ecmp = rt6_qualify_for_ecmp(rt);
10848c2ecf20Sopenharmony_ci	bool notify_sibling_rt = false;
10858c2ecf20Sopenharmony_ci	u16 nlflags = NLM_F_EXCL;
10868c2ecf20Sopenharmony_ci	int err;
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	if (info->nlh && (info->nlh->nlmsg_flags & NLM_F_APPEND))
10898c2ecf20Sopenharmony_ci		nlflags |= NLM_F_APPEND;
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	ins = &fn->leaf;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	for (iter = leaf; iter;
10948c2ecf20Sopenharmony_ci	     iter = rcu_dereference_protected(iter->fib6_next,
10958c2ecf20Sopenharmony_ci				lockdep_is_held(&rt->fib6_table->tb6_lock))) {
10968c2ecf20Sopenharmony_ci		/*
10978c2ecf20Sopenharmony_ci		 *	Search for duplicates
10988c2ecf20Sopenharmony_ci		 */
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci		if (iter->fib6_metric == rt->fib6_metric) {
11018c2ecf20Sopenharmony_ci			/*
11028c2ecf20Sopenharmony_ci			 *	Same priority level
11038c2ecf20Sopenharmony_ci			 */
11048c2ecf20Sopenharmony_ci			if (info->nlh &&
11058c2ecf20Sopenharmony_ci			    (info->nlh->nlmsg_flags & NLM_F_EXCL))
11068c2ecf20Sopenharmony_ci				return -EEXIST;
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci			nlflags &= ~NLM_F_EXCL;
11098c2ecf20Sopenharmony_ci			if (replace) {
11108c2ecf20Sopenharmony_ci				if (rt_can_ecmp == rt6_qualify_for_ecmp(iter)) {
11118c2ecf20Sopenharmony_ci					found++;
11128c2ecf20Sopenharmony_ci					break;
11138c2ecf20Sopenharmony_ci				}
11148c2ecf20Sopenharmony_ci				fallback_ins = fallback_ins ?: ins;
11158c2ecf20Sopenharmony_ci				goto next_iter;
11168c2ecf20Sopenharmony_ci			}
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci			if (rt6_duplicate_nexthop(iter, rt)) {
11198c2ecf20Sopenharmony_ci				if (rt->fib6_nsiblings)
11208c2ecf20Sopenharmony_ci					rt->fib6_nsiblings = 0;
11218c2ecf20Sopenharmony_ci				if (!(iter->fib6_flags & RTF_EXPIRES))
11228c2ecf20Sopenharmony_ci					return -EEXIST;
11238c2ecf20Sopenharmony_ci				if (!(rt->fib6_flags & RTF_EXPIRES))
11248c2ecf20Sopenharmony_ci					fib6_clean_expires(iter);
11258c2ecf20Sopenharmony_ci				else
11268c2ecf20Sopenharmony_ci					fib6_set_expires(iter, rt->expires);
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci				if (rt->fib6_pmtu)
11298c2ecf20Sopenharmony_ci					fib6_metric_set(iter, RTAX_MTU,
11308c2ecf20Sopenharmony_ci							rt->fib6_pmtu);
11318c2ecf20Sopenharmony_ci				return -EEXIST;
11328c2ecf20Sopenharmony_ci			}
11338c2ecf20Sopenharmony_ci			/* If we have the same destination and the same metric,
11348c2ecf20Sopenharmony_ci			 * but not the same gateway, then the route we try to
11358c2ecf20Sopenharmony_ci			 * add is sibling to this route, increment our counter
11368c2ecf20Sopenharmony_ci			 * of siblings, and later we will add our route to the
11378c2ecf20Sopenharmony_ci			 * list.
11388c2ecf20Sopenharmony_ci			 * Only static routes (which don't have flag
11398c2ecf20Sopenharmony_ci			 * RTF_EXPIRES) are used for ECMPv6.
11408c2ecf20Sopenharmony_ci			 *
11418c2ecf20Sopenharmony_ci			 * To avoid long list, we only had siblings if the
11428c2ecf20Sopenharmony_ci			 * route have a gateway.
11438c2ecf20Sopenharmony_ci			 */
11448c2ecf20Sopenharmony_ci			if (rt_can_ecmp &&
11458c2ecf20Sopenharmony_ci			    rt6_qualify_for_ecmp(iter))
11468c2ecf20Sopenharmony_ci				rt->fib6_nsiblings++;
11478c2ecf20Sopenharmony_ci		}
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci		if (iter->fib6_metric > rt->fib6_metric)
11508c2ecf20Sopenharmony_ci			break;
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_cinext_iter:
11538c2ecf20Sopenharmony_ci		ins = &iter->fib6_next;
11548c2ecf20Sopenharmony_ci	}
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	if (fallback_ins && !found) {
11578c2ecf20Sopenharmony_ci		/* No matching route with same ecmp-able-ness found, replace
11588c2ecf20Sopenharmony_ci		 * first matching route
11598c2ecf20Sopenharmony_ci		 */
11608c2ecf20Sopenharmony_ci		ins = fallback_ins;
11618c2ecf20Sopenharmony_ci		iter = rcu_dereference_protected(*ins,
11628c2ecf20Sopenharmony_ci				    lockdep_is_held(&rt->fib6_table->tb6_lock));
11638c2ecf20Sopenharmony_ci		found++;
11648c2ecf20Sopenharmony_ci	}
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci	/* Reset round-robin state, if necessary */
11678c2ecf20Sopenharmony_ci	if (ins == &fn->leaf)
11688c2ecf20Sopenharmony_ci		fn->rr_ptr = NULL;
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	/* Link this route to others same route. */
11718c2ecf20Sopenharmony_ci	if (rt->fib6_nsiblings) {
11728c2ecf20Sopenharmony_ci		unsigned int fib6_nsiblings;
11738c2ecf20Sopenharmony_ci		struct fib6_info *sibling, *temp_sibling;
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci		/* Find the first route that have the same metric */
11768c2ecf20Sopenharmony_ci		sibling = leaf;
11778c2ecf20Sopenharmony_ci		notify_sibling_rt = true;
11788c2ecf20Sopenharmony_ci		while (sibling) {
11798c2ecf20Sopenharmony_ci			if (sibling->fib6_metric == rt->fib6_metric &&
11808c2ecf20Sopenharmony_ci			    rt6_qualify_for_ecmp(sibling)) {
11818c2ecf20Sopenharmony_ci				list_add_tail(&rt->fib6_siblings,
11828c2ecf20Sopenharmony_ci					      &sibling->fib6_siblings);
11838c2ecf20Sopenharmony_ci				break;
11848c2ecf20Sopenharmony_ci			}
11858c2ecf20Sopenharmony_ci			sibling = rcu_dereference_protected(sibling->fib6_next,
11868c2ecf20Sopenharmony_ci				    lockdep_is_held(&rt->fib6_table->tb6_lock));
11878c2ecf20Sopenharmony_ci			notify_sibling_rt = false;
11888c2ecf20Sopenharmony_ci		}
11898c2ecf20Sopenharmony_ci		/* For each sibling in the list, increment the counter of
11908c2ecf20Sopenharmony_ci		 * siblings. BUG() if counters does not match, list of siblings
11918c2ecf20Sopenharmony_ci		 * is broken!
11928c2ecf20Sopenharmony_ci		 */
11938c2ecf20Sopenharmony_ci		fib6_nsiblings = 0;
11948c2ecf20Sopenharmony_ci		list_for_each_entry_safe(sibling, temp_sibling,
11958c2ecf20Sopenharmony_ci					 &rt->fib6_siblings, fib6_siblings) {
11968c2ecf20Sopenharmony_ci			sibling->fib6_nsiblings++;
11978c2ecf20Sopenharmony_ci			BUG_ON(sibling->fib6_nsiblings != rt->fib6_nsiblings);
11988c2ecf20Sopenharmony_ci			fib6_nsiblings++;
11998c2ecf20Sopenharmony_ci		}
12008c2ecf20Sopenharmony_ci		BUG_ON(fib6_nsiblings != rt->fib6_nsiblings);
12018c2ecf20Sopenharmony_ci		rt6_multipath_rebalance(temp_sibling);
12028c2ecf20Sopenharmony_ci	}
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	/*
12058c2ecf20Sopenharmony_ci	 *	insert node
12068c2ecf20Sopenharmony_ci	 */
12078c2ecf20Sopenharmony_ci	if (!replace) {
12088c2ecf20Sopenharmony_ci		if (!add)
12098c2ecf20Sopenharmony_ci			pr_warn("NLM_F_CREATE should be set when creating new route\n");
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ciadd:
12128c2ecf20Sopenharmony_ci		nlflags |= NLM_F_CREATE;
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci		/* The route should only be notified if it is the first
12158c2ecf20Sopenharmony_ci		 * route in the node or if it is added as a sibling
12168c2ecf20Sopenharmony_ci		 * route to the first route in the node.
12178c2ecf20Sopenharmony_ci		 */
12188c2ecf20Sopenharmony_ci		if (!info->skip_notify_kernel &&
12198c2ecf20Sopenharmony_ci		    (notify_sibling_rt || ins == &fn->leaf)) {
12208c2ecf20Sopenharmony_ci			enum fib_event_type fib_event;
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci			if (notify_sibling_rt)
12238c2ecf20Sopenharmony_ci				fib_event = FIB_EVENT_ENTRY_APPEND;
12248c2ecf20Sopenharmony_ci			else
12258c2ecf20Sopenharmony_ci				fib_event = FIB_EVENT_ENTRY_REPLACE;
12268c2ecf20Sopenharmony_ci			err = call_fib6_entry_notifiers(info->nl_net,
12278c2ecf20Sopenharmony_ci							fib_event, rt,
12288c2ecf20Sopenharmony_ci							extack);
12298c2ecf20Sopenharmony_ci			if (err) {
12308c2ecf20Sopenharmony_ci				struct fib6_info *sibling, *next_sibling;
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci				/* If the route has siblings, then it first
12338c2ecf20Sopenharmony_ci				 * needs to be unlinked from them.
12348c2ecf20Sopenharmony_ci				 */
12358c2ecf20Sopenharmony_ci				if (!rt->fib6_nsiblings)
12368c2ecf20Sopenharmony_ci					return err;
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci				list_for_each_entry_safe(sibling, next_sibling,
12398c2ecf20Sopenharmony_ci							 &rt->fib6_siblings,
12408c2ecf20Sopenharmony_ci							 fib6_siblings)
12418c2ecf20Sopenharmony_ci					sibling->fib6_nsiblings--;
12428c2ecf20Sopenharmony_ci				rt->fib6_nsiblings = 0;
12438c2ecf20Sopenharmony_ci				list_del_init(&rt->fib6_siblings);
12448c2ecf20Sopenharmony_ci				rt6_multipath_rebalance(next_sibling);
12458c2ecf20Sopenharmony_ci				return err;
12468c2ecf20Sopenharmony_ci			}
12478c2ecf20Sopenharmony_ci		}
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci		rcu_assign_pointer(rt->fib6_next, iter);
12508c2ecf20Sopenharmony_ci		fib6_info_hold(rt);
12518c2ecf20Sopenharmony_ci		rcu_assign_pointer(rt->fib6_node, fn);
12528c2ecf20Sopenharmony_ci		rcu_assign_pointer(*ins, rt);
12538c2ecf20Sopenharmony_ci		if (!info->skip_notify)
12548c2ecf20Sopenharmony_ci			inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
12558c2ecf20Sopenharmony_ci		info->nl_net->ipv6.rt6_stats->fib_rt_entries++;
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci		if (!(fn->fn_flags & RTN_RTINFO)) {
12588c2ecf20Sopenharmony_ci			info->nl_net->ipv6.rt6_stats->fib_route_nodes++;
12598c2ecf20Sopenharmony_ci			fn->fn_flags |= RTN_RTINFO;
12608c2ecf20Sopenharmony_ci		}
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	} else {
12638c2ecf20Sopenharmony_ci		int nsiblings;
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci		if (!found) {
12668c2ecf20Sopenharmony_ci			if (add)
12678c2ecf20Sopenharmony_ci				goto add;
12688c2ecf20Sopenharmony_ci			pr_warn("NLM_F_REPLACE set, but no existing node found!\n");
12698c2ecf20Sopenharmony_ci			return -ENOENT;
12708c2ecf20Sopenharmony_ci		}
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci		if (!info->skip_notify_kernel && ins == &fn->leaf) {
12738c2ecf20Sopenharmony_ci			err = call_fib6_entry_notifiers(info->nl_net,
12748c2ecf20Sopenharmony_ci							FIB_EVENT_ENTRY_REPLACE,
12758c2ecf20Sopenharmony_ci							rt, extack);
12768c2ecf20Sopenharmony_ci			if (err)
12778c2ecf20Sopenharmony_ci				return err;
12788c2ecf20Sopenharmony_ci		}
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci		fib6_info_hold(rt);
12818c2ecf20Sopenharmony_ci		rcu_assign_pointer(rt->fib6_node, fn);
12828c2ecf20Sopenharmony_ci		rt->fib6_next = iter->fib6_next;
12838c2ecf20Sopenharmony_ci		rcu_assign_pointer(*ins, rt);
12848c2ecf20Sopenharmony_ci		if (!info->skip_notify)
12858c2ecf20Sopenharmony_ci			inet6_rt_notify(RTM_NEWROUTE, rt, info, NLM_F_REPLACE);
12868c2ecf20Sopenharmony_ci		if (!(fn->fn_flags & RTN_RTINFO)) {
12878c2ecf20Sopenharmony_ci			info->nl_net->ipv6.rt6_stats->fib_route_nodes++;
12888c2ecf20Sopenharmony_ci			fn->fn_flags |= RTN_RTINFO;
12898c2ecf20Sopenharmony_ci		}
12908c2ecf20Sopenharmony_ci		nsiblings = iter->fib6_nsiblings;
12918c2ecf20Sopenharmony_ci		iter->fib6_node = NULL;
12928c2ecf20Sopenharmony_ci		fib6_purge_rt(iter, fn, info->nl_net);
12938c2ecf20Sopenharmony_ci		if (rcu_access_pointer(fn->rr_ptr) == iter)
12948c2ecf20Sopenharmony_ci			fn->rr_ptr = NULL;
12958c2ecf20Sopenharmony_ci		fib6_info_release(iter);
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci		if (nsiblings) {
12988c2ecf20Sopenharmony_ci			/* Replacing an ECMP route, remove all siblings */
12998c2ecf20Sopenharmony_ci			ins = &rt->fib6_next;
13008c2ecf20Sopenharmony_ci			iter = rcu_dereference_protected(*ins,
13018c2ecf20Sopenharmony_ci				    lockdep_is_held(&rt->fib6_table->tb6_lock));
13028c2ecf20Sopenharmony_ci			while (iter) {
13038c2ecf20Sopenharmony_ci				if (iter->fib6_metric > rt->fib6_metric)
13048c2ecf20Sopenharmony_ci					break;
13058c2ecf20Sopenharmony_ci				if (rt6_qualify_for_ecmp(iter)) {
13068c2ecf20Sopenharmony_ci					*ins = iter->fib6_next;
13078c2ecf20Sopenharmony_ci					iter->fib6_node = NULL;
13088c2ecf20Sopenharmony_ci					fib6_purge_rt(iter, fn, info->nl_net);
13098c2ecf20Sopenharmony_ci					if (rcu_access_pointer(fn->rr_ptr) == iter)
13108c2ecf20Sopenharmony_ci						fn->rr_ptr = NULL;
13118c2ecf20Sopenharmony_ci					fib6_info_release(iter);
13128c2ecf20Sopenharmony_ci					nsiblings--;
13138c2ecf20Sopenharmony_ci					info->nl_net->ipv6.rt6_stats->fib_rt_entries--;
13148c2ecf20Sopenharmony_ci				} else {
13158c2ecf20Sopenharmony_ci					ins = &iter->fib6_next;
13168c2ecf20Sopenharmony_ci				}
13178c2ecf20Sopenharmony_ci				iter = rcu_dereference_protected(*ins,
13188c2ecf20Sopenharmony_ci					lockdep_is_held(&rt->fib6_table->tb6_lock));
13198c2ecf20Sopenharmony_ci			}
13208c2ecf20Sopenharmony_ci			WARN_ON(nsiblings != 0);
13218c2ecf20Sopenharmony_ci		}
13228c2ecf20Sopenharmony_ci	}
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci	return 0;
13258c2ecf20Sopenharmony_ci}
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_cistatic void fib6_start_gc(struct net *net, struct fib6_info *rt)
13288c2ecf20Sopenharmony_ci{
13298c2ecf20Sopenharmony_ci	if (!timer_pending(&net->ipv6.ip6_fib_timer) &&
13308c2ecf20Sopenharmony_ci	    (rt->fib6_flags & RTF_EXPIRES))
13318c2ecf20Sopenharmony_ci		mod_timer(&net->ipv6.ip6_fib_timer,
13328c2ecf20Sopenharmony_ci			  jiffies + net->ipv6.sysctl.ip6_rt_gc_interval);
13338c2ecf20Sopenharmony_ci}
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_civoid fib6_force_start_gc(struct net *net)
13368c2ecf20Sopenharmony_ci{
13378c2ecf20Sopenharmony_ci	if (!timer_pending(&net->ipv6.ip6_fib_timer))
13388c2ecf20Sopenharmony_ci		mod_timer(&net->ipv6.ip6_fib_timer,
13398c2ecf20Sopenharmony_ci			  jiffies + net->ipv6.sysctl.ip6_rt_gc_interval);
13408c2ecf20Sopenharmony_ci}
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_cistatic void __fib6_update_sernum_upto_root(struct fib6_info *rt,
13438c2ecf20Sopenharmony_ci					   int sernum)
13448c2ecf20Sopenharmony_ci{
13458c2ecf20Sopenharmony_ci	struct fib6_node *fn = rcu_dereference_protected(rt->fib6_node,
13468c2ecf20Sopenharmony_ci				lockdep_is_held(&rt->fib6_table->tb6_lock));
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci	/* paired with smp_rmb() in rt6_get_cookie_safe() */
13498c2ecf20Sopenharmony_ci	smp_wmb();
13508c2ecf20Sopenharmony_ci	while (fn) {
13518c2ecf20Sopenharmony_ci		WRITE_ONCE(fn->fn_sernum, sernum);
13528c2ecf20Sopenharmony_ci		fn = rcu_dereference_protected(fn->parent,
13538c2ecf20Sopenharmony_ci				lockdep_is_held(&rt->fib6_table->tb6_lock));
13548c2ecf20Sopenharmony_ci	}
13558c2ecf20Sopenharmony_ci}
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_civoid fib6_update_sernum_upto_root(struct net *net, struct fib6_info *rt)
13588c2ecf20Sopenharmony_ci{
13598c2ecf20Sopenharmony_ci	__fib6_update_sernum_upto_root(rt, fib6_new_sernum(net));
13608c2ecf20Sopenharmony_ci}
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci/* allow ipv4 to update sernum via ipv6_stub */
13638c2ecf20Sopenharmony_civoid fib6_update_sernum_stub(struct net *net, struct fib6_info *f6i)
13648c2ecf20Sopenharmony_ci{
13658c2ecf20Sopenharmony_ci	spin_lock_bh(&f6i->fib6_table->tb6_lock);
13668c2ecf20Sopenharmony_ci	fib6_update_sernum_upto_root(net, f6i);
13678c2ecf20Sopenharmony_ci	spin_unlock_bh(&f6i->fib6_table->tb6_lock);
13688c2ecf20Sopenharmony_ci}
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci/*
13718c2ecf20Sopenharmony_ci *	Add routing information to the routing tree.
13728c2ecf20Sopenharmony_ci *	<destination addr>/<source addr>
13738c2ecf20Sopenharmony_ci *	with source addr info in sub-trees
13748c2ecf20Sopenharmony_ci *	Need to own table->tb6_lock
13758c2ecf20Sopenharmony_ci */
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ciint fib6_add(struct fib6_node *root, struct fib6_info *rt,
13788c2ecf20Sopenharmony_ci	     struct nl_info *info, struct netlink_ext_ack *extack)
13798c2ecf20Sopenharmony_ci{
13808c2ecf20Sopenharmony_ci	struct fib6_table *table = rt->fib6_table;
13818c2ecf20Sopenharmony_ci	struct fib6_node *fn, *pn = NULL;
13828c2ecf20Sopenharmony_ci	int err = -ENOMEM;
13838c2ecf20Sopenharmony_ci	int allow_create = 1;
13848c2ecf20Sopenharmony_ci	int replace_required = 0;
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci	if (info->nlh) {
13878c2ecf20Sopenharmony_ci		if (!(info->nlh->nlmsg_flags & NLM_F_CREATE))
13888c2ecf20Sopenharmony_ci			allow_create = 0;
13898c2ecf20Sopenharmony_ci		if (info->nlh->nlmsg_flags & NLM_F_REPLACE)
13908c2ecf20Sopenharmony_ci			replace_required = 1;
13918c2ecf20Sopenharmony_ci	}
13928c2ecf20Sopenharmony_ci	if (!allow_create && !replace_required)
13938c2ecf20Sopenharmony_ci		pr_warn("RTM_NEWROUTE with no NLM_F_CREATE or NLM_F_REPLACE\n");
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci	fn = fib6_add_1(info->nl_net, table, root,
13968c2ecf20Sopenharmony_ci			&rt->fib6_dst.addr, rt->fib6_dst.plen,
13978c2ecf20Sopenharmony_ci			offsetof(struct fib6_info, fib6_dst), allow_create,
13988c2ecf20Sopenharmony_ci			replace_required, extack);
13998c2ecf20Sopenharmony_ci	if (IS_ERR(fn)) {
14008c2ecf20Sopenharmony_ci		err = PTR_ERR(fn);
14018c2ecf20Sopenharmony_ci		fn = NULL;
14028c2ecf20Sopenharmony_ci		goto out;
14038c2ecf20Sopenharmony_ci	}
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	pn = fn;
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
14088c2ecf20Sopenharmony_ci	if (rt->fib6_src.plen) {
14098c2ecf20Sopenharmony_ci		struct fib6_node *sn;
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci		if (!rcu_access_pointer(fn->subtree)) {
14128c2ecf20Sopenharmony_ci			struct fib6_node *sfn;
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci			/*
14158c2ecf20Sopenharmony_ci			 * Create subtree.
14168c2ecf20Sopenharmony_ci			 *
14178c2ecf20Sopenharmony_ci			 *		fn[main tree]
14188c2ecf20Sopenharmony_ci			 *		|
14198c2ecf20Sopenharmony_ci			 *		sfn[subtree root]
14208c2ecf20Sopenharmony_ci			 *		   \
14218c2ecf20Sopenharmony_ci			 *		    sn[new leaf node]
14228c2ecf20Sopenharmony_ci			 */
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci			/* Create subtree root node */
14258c2ecf20Sopenharmony_ci			sfn = node_alloc(info->nl_net);
14268c2ecf20Sopenharmony_ci			if (!sfn)
14278c2ecf20Sopenharmony_ci				goto failure;
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci			fib6_info_hold(info->nl_net->ipv6.fib6_null_entry);
14308c2ecf20Sopenharmony_ci			rcu_assign_pointer(sfn->leaf,
14318c2ecf20Sopenharmony_ci					   info->nl_net->ipv6.fib6_null_entry);
14328c2ecf20Sopenharmony_ci			sfn->fn_flags = RTN_ROOT;
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci			/* Now add the first leaf node to new subtree */
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci			sn = fib6_add_1(info->nl_net, table, sfn,
14378c2ecf20Sopenharmony_ci					&rt->fib6_src.addr, rt->fib6_src.plen,
14388c2ecf20Sopenharmony_ci					offsetof(struct fib6_info, fib6_src),
14398c2ecf20Sopenharmony_ci					allow_create, replace_required, extack);
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci			if (IS_ERR(sn)) {
14428c2ecf20Sopenharmony_ci				/* If it is failed, discard just allocated
14438c2ecf20Sopenharmony_ci				   root, and then (in failure) stale node
14448c2ecf20Sopenharmony_ci				   in main tree.
14458c2ecf20Sopenharmony_ci				 */
14468c2ecf20Sopenharmony_ci				node_free_immediate(info->nl_net, sfn);
14478c2ecf20Sopenharmony_ci				err = PTR_ERR(sn);
14488c2ecf20Sopenharmony_ci				goto failure;
14498c2ecf20Sopenharmony_ci			}
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci			/* Now link new subtree to main tree */
14528c2ecf20Sopenharmony_ci			rcu_assign_pointer(sfn->parent, fn);
14538c2ecf20Sopenharmony_ci			rcu_assign_pointer(fn->subtree, sfn);
14548c2ecf20Sopenharmony_ci		} else {
14558c2ecf20Sopenharmony_ci			sn = fib6_add_1(info->nl_net, table, FIB6_SUBTREE(fn),
14568c2ecf20Sopenharmony_ci					&rt->fib6_src.addr, rt->fib6_src.plen,
14578c2ecf20Sopenharmony_ci					offsetof(struct fib6_info, fib6_src),
14588c2ecf20Sopenharmony_ci					allow_create, replace_required, extack);
14598c2ecf20Sopenharmony_ci
14608c2ecf20Sopenharmony_ci			if (IS_ERR(sn)) {
14618c2ecf20Sopenharmony_ci				err = PTR_ERR(sn);
14628c2ecf20Sopenharmony_ci				goto failure;
14638c2ecf20Sopenharmony_ci			}
14648c2ecf20Sopenharmony_ci		}
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci		if (!rcu_access_pointer(fn->leaf)) {
14678c2ecf20Sopenharmony_ci			if (fn->fn_flags & RTN_TL_ROOT) {
14688c2ecf20Sopenharmony_ci				/* put back null_entry for root node */
14698c2ecf20Sopenharmony_ci				rcu_assign_pointer(fn->leaf,
14708c2ecf20Sopenharmony_ci					    info->nl_net->ipv6.fib6_null_entry);
14718c2ecf20Sopenharmony_ci			} else {
14728c2ecf20Sopenharmony_ci				fib6_info_hold(rt);
14738c2ecf20Sopenharmony_ci				rcu_assign_pointer(fn->leaf, rt);
14748c2ecf20Sopenharmony_ci			}
14758c2ecf20Sopenharmony_ci		}
14768c2ecf20Sopenharmony_ci		fn = sn;
14778c2ecf20Sopenharmony_ci	}
14788c2ecf20Sopenharmony_ci#endif
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci	err = fib6_add_rt2node(fn, rt, info, extack);
14818c2ecf20Sopenharmony_ci	if (!err) {
14828c2ecf20Sopenharmony_ci		if (rt->nh)
14838c2ecf20Sopenharmony_ci			list_add(&rt->nh_list, &rt->nh->f6i_list);
14848c2ecf20Sopenharmony_ci		__fib6_update_sernum_upto_root(rt, fib6_new_sernum(info->nl_net));
14858c2ecf20Sopenharmony_ci		fib6_start_gc(info->nl_net, rt);
14868c2ecf20Sopenharmony_ci	}
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ciout:
14898c2ecf20Sopenharmony_ci	if (err) {
14908c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
14918c2ecf20Sopenharmony_ci		/*
14928c2ecf20Sopenharmony_ci		 * If fib6_add_1 has cleared the old leaf pointer in the
14938c2ecf20Sopenharmony_ci		 * super-tree leaf node we have to find a new one for it.
14948c2ecf20Sopenharmony_ci		 */
14958c2ecf20Sopenharmony_ci		if (pn != fn) {
14968c2ecf20Sopenharmony_ci			struct fib6_info *pn_leaf =
14978c2ecf20Sopenharmony_ci				rcu_dereference_protected(pn->leaf,
14988c2ecf20Sopenharmony_ci				    lockdep_is_held(&table->tb6_lock));
14998c2ecf20Sopenharmony_ci			if (pn_leaf == rt) {
15008c2ecf20Sopenharmony_ci				pn_leaf = NULL;
15018c2ecf20Sopenharmony_ci				RCU_INIT_POINTER(pn->leaf, NULL);
15028c2ecf20Sopenharmony_ci				fib6_info_release(rt);
15038c2ecf20Sopenharmony_ci			}
15048c2ecf20Sopenharmony_ci			if (!pn_leaf && !(pn->fn_flags & RTN_RTINFO)) {
15058c2ecf20Sopenharmony_ci				pn_leaf = fib6_find_prefix(info->nl_net, table,
15068c2ecf20Sopenharmony_ci							   pn);
15078c2ecf20Sopenharmony_ci				if (!pn_leaf)
15088c2ecf20Sopenharmony_ci					pn_leaf =
15098c2ecf20Sopenharmony_ci					    info->nl_net->ipv6.fib6_null_entry;
15108c2ecf20Sopenharmony_ci				fib6_info_hold(pn_leaf);
15118c2ecf20Sopenharmony_ci				rcu_assign_pointer(pn->leaf, pn_leaf);
15128c2ecf20Sopenharmony_ci			}
15138c2ecf20Sopenharmony_ci		}
15148c2ecf20Sopenharmony_ci#endif
15158c2ecf20Sopenharmony_ci		goto failure;
15168c2ecf20Sopenharmony_ci	} else if (fib6_requires_src(rt)) {
15178c2ecf20Sopenharmony_ci		fib6_routes_require_src_inc(info->nl_net);
15188c2ecf20Sopenharmony_ci	}
15198c2ecf20Sopenharmony_ci	return err;
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_cifailure:
15228c2ecf20Sopenharmony_ci	/* fn->leaf could be NULL and fib6_repair_tree() needs to be called if:
15238c2ecf20Sopenharmony_ci	 * 1. fn is an intermediate node and we failed to add the new
15248c2ecf20Sopenharmony_ci	 * route to it in both subtree creation failure and fib6_add_rt2node()
15258c2ecf20Sopenharmony_ci	 * failure case.
15268c2ecf20Sopenharmony_ci	 * 2. fn is the root node in the table and we fail to add the first
15278c2ecf20Sopenharmony_ci	 * default route to it.
15288c2ecf20Sopenharmony_ci	 */
15298c2ecf20Sopenharmony_ci	if (fn &&
15308c2ecf20Sopenharmony_ci	    (!(fn->fn_flags & (RTN_RTINFO|RTN_ROOT)) ||
15318c2ecf20Sopenharmony_ci	     (fn->fn_flags & RTN_TL_ROOT &&
15328c2ecf20Sopenharmony_ci	      !rcu_access_pointer(fn->leaf))))
15338c2ecf20Sopenharmony_ci		fib6_repair_tree(info->nl_net, table, fn);
15348c2ecf20Sopenharmony_ci	return err;
15358c2ecf20Sopenharmony_ci}
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci/*
15388c2ecf20Sopenharmony_ci *	Routing tree lookup
15398c2ecf20Sopenharmony_ci *
15408c2ecf20Sopenharmony_ci */
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_cistruct lookup_args {
15438c2ecf20Sopenharmony_ci	int			offset;		/* key offset on fib6_info */
15448c2ecf20Sopenharmony_ci	const struct in6_addr	*addr;		/* search key			*/
15458c2ecf20Sopenharmony_ci};
15468c2ecf20Sopenharmony_ci
15478c2ecf20Sopenharmony_cistatic struct fib6_node *fib6_node_lookup_1(struct fib6_node *root,
15488c2ecf20Sopenharmony_ci					    struct lookup_args *args)
15498c2ecf20Sopenharmony_ci{
15508c2ecf20Sopenharmony_ci	struct fib6_node *fn;
15518c2ecf20Sopenharmony_ci	__be32 dir;
15528c2ecf20Sopenharmony_ci
15538c2ecf20Sopenharmony_ci	if (unlikely(args->offset == 0))
15548c2ecf20Sopenharmony_ci		return NULL;
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_ci	/*
15578c2ecf20Sopenharmony_ci	 *	Descend on a tree
15588c2ecf20Sopenharmony_ci	 */
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	fn = root;
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	for (;;) {
15638c2ecf20Sopenharmony_ci		struct fib6_node *next;
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci		dir = addr_bit_set(args->addr, fn->fn_bit);
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci		next = dir ? rcu_dereference(fn->right) :
15688c2ecf20Sopenharmony_ci			     rcu_dereference(fn->left);
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci		if (next) {
15718c2ecf20Sopenharmony_ci			fn = next;
15728c2ecf20Sopenharmony_ci			continue;
15738c2ecf20Sopenharmony_ci		}
15748c2ecf20Sopenharmony_ci		break;
15758c2ecf20Sopenharmony_ci	}
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ci	while (fn) {
15788c2ecf20Sopenharmony_ci		struct fib6_node *subtree = FIB6_SUBTREE(fn);
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci		if (subtree || fn->fn_flags & RTN_RTINFO) {
15818c2ecf20Sopenharmony_ci			struct fib6_info *leaf = rcu_dereference(fn->leaf);
15828c2ecf20Sopenharmony_ci			struct rt6key *key;
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci			if (!leaf)
15858c2ecf20Sopenharmony_ci				goto backtrack;
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ci			key = (struct rt6key *) ((u8 *)leaf + args->offset);
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_ci			if (ipv6_prefix_equal(&key->addr, args->addr, key->plen)) {
15908c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
15918c2ecf20Sopenharmony_ci				if (subtree) {
15928c2ecf20Sopenharmony_ci					struct fib6_node *sfn;
15938c2ecf20Sopenharmony_ci					sfn = fib6_node_lookup_1(subtree,
15948c2ecf20Sopenharmony_ci								 args + 1);
15958c2ecf20Sopenharmony_ci					if (!sfn)
15968c2ecf20Sopenharmony_ci						goto backtrack;
15978c2ecf20Sopenharmony_ci					fn = sfn;
15988c2ecf20Sopenharmony_ci				}
15998c2ecf20Sopenharmony_ci#endif
16008c2ecf20Sopenharmony_ci				if (fn->fn_flags & RTN_RTINFO)
16018c2ecf20Sopenharmony_ci					return fn;
16028c2ecf20Sopenharmony_ci			}
16038c2ecf20Sopenharmony_ci		}
16048c2ecf20Sopenharmony_cibacktrack:
16058c2ecf20Sopenharmony_ci		if (fn->fn_flags & RTN_ROOT)
16068c2ecf20Sopenharmony_ci			break;
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_ci		fn = rcu_dereference(fn->parent);
16098c2ecf20Sopenharmony_ci	}
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci	return NULL;
16128c2ecf20Sopenharmony_ci}
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci/* called with rcu_read_lock() held
16158c2ecf20Sopenharmony_ci */
16168c2ecf20Sopenharmony_cistruct fib6_node *fib6_node_lookup(struct fib6_node *root,
16178c2ecf20Sopenharmony_ci				   const struct in6_addr *daddr,
16188c2ecf20Sopenharmony_ci				   const struct in6_addr *saddr)
16198c2ecf20Sopenharmony_ci{
16208c2ecf20Sopenharmony_ci	struct fib6_node *fn;
16218c2ecf20Sopenharmony_ci	struct lookup_args args[] = {
16228c2ecf20Sopenharmony_ci		{
16238c2ecf20Sopenharmony_ci			.offset = offsetof(struct fib6_info, fib6_dst),
16248c2ecf20Sopenharmony_ci			.addr = daddr,
16258c2ecf20Sopenharmony_ci		},
16268c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
16278c2ecf20Sopenharmony_ci		{
16288c2ecf20Sopenharmony_ci			.offset = offsetof(struct fib6_info, fib6_src),
16298c2ecf20Sopenharmony_ci			.addr = saddr,
16308c2ecf20Sopenharmony_ci		},
16318c2ecf20Sopenharmony_ci#endif
16328c2ecf20Sopenharmony_ci		{
16338c2ecf20Sopenharmony_ci			.offset = 0,	/* sentinel */
16348c2ecf20Sopenharmony_ci		}
16358c2ecf20Sopenharmony_ci	};
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	fn = fib6_node_lookup_1(root, daddr ? args : args + 1);
16388c2ecf20Sopenharmony_ci	if (!fn || fn->fn_flags & RTN_TL_ROOT)
16398c2ecf20Sopenharmony_ci		fn = root;
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci	return fn;
16428c2ecf20Sopenharmony_ci}
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci/*
16458c2ecf20Sopenharmony_ci *	Get node with specified destination prefix (and source prefix,
16468c2ecf20Sopenharmony_ci *	if subtrees are used)
16478c2ecf20Sopenharmony_ci *	exact_match == true means we try to find fn with exact match of
16488c2ecf20Sopenharmony_ci *	the passed in prefix addr
16498c2ecf20Sopenharmony_ci *	exact_match == false means we try to find fn with longest prefix
16508c2ecf20Sopenharmony_ci *	match of the passed in prefix addr. This is useful for finding fn
16518c2ecf20Sopenharmony_ci *	for cached route as it will be stored in the exception table under
16528c2ecf20Sopenharmony_ci *	the node with longest prefix length.
16538c2ecf20Sopenharmony_ci */
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_ci
16568c2ecf20Sopenharmony_cistatic struct fib6_node *fib6_locate_1(struct fib6_node *root,
16578c2ecf20Sopenharmony_ci				       const struct in6_addr *addr,
16588c2ecf20Sopenharmony_ci				       int plen, int offset,
16598c2ecf20Sopenharmony_ci				       bool exact_match)
16608c2ecf20Sopenharmony_ci{
16618c2ecf20Sopenharmony_ci	struct fib6_node *fn, *prev = NULL;
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci	for (fn = root; fn ; ) {
16648c2ecf20Sopenharmony_ci		struct fib6_info *leaf = rcu_dereference(fn->leaf);
16658c2ecf20Sopenharmony_ci		struct rt6key *key;
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_ci		/* This node is being deleted */
16688c2ecf20Sopenharmony_ci		if (!leaf) {
16698c2ecf20Sopenharmony_ci			if (plen <= fn->fn_bit)
16708c2ecf20Sopenharmony_ci				goto out;
16718c2ecf20Sopenharmony_ci			else
16728c2ecf20Sopenharmony_ci				goto next;
16738c2ecf20Sopenharmony_ci		}
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci		key = (struct rt6key *)((u8 *)leaf + offset);
16768c2ecf20Sopenharmony_ci
16778c2ecf20Sopenharmony_ci		/*
16788c2ecf20Sopenharmony_ci		 *	Prefix match
16798c2ecf20Sopenharmony_ci		 */
16808c2ecf20Sopenharmony_ci		if (plen < fn->fn_bit ||
16818c2ecf20Sopenharmony_ci		    !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit))
16828c2ecf20Sopenharmony_ci			goto out;
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci		if (plen == fn->fn_bit)
16858c2ecf20Sopenharmony_ci			return fn;
16868c2ecf20Sopenharmony_ci
16878c2ecf20Sopenharmony_ci		if (fn->fn_flags & RTN_RTINFO)
16888c2ecf20Sopenharmony_ci			prev = fn;
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_cinext:
16918c2ecf20Sopenharmony_ci		/*
16928c2ecf20Sopenharmony_ci		 *	We have more bits to go
16938c2ecf20Sopenharmony_ci		 */
16948c2ecf20Sopenharmony_ci		if (addr_bit_set(addr, fn->fn_bit))
16958c2ecf20Sopenharmony_ci			fn = rcu_dereference(fn->right);
16968c2ecf20Sopenharmony_ci		else
16978c2ecf20Sopenharmony_ci			fn = rcu_dereference(fn->left);
16988c2ecf20Sopenharmony_ci	}
16998c2ecf20Sopenharmony_ciout:
17008c2ecf20Sopenharmony_ci	if (exact_match)
17018c2ecf20Sopenharmony_ci		return NULL;
17028c2ecf20Sopenharmony_ci	else
17038c2ecf20Sopenharmony_ci		return prev;
17048c2ecf20Sopenharmony_ci}
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_cistruct fib6_node *fib6_locate(struct fib6_node *root,
17078c2ecf20Sopenharmony_ci			      const struct in6_addr *daddr, int dst_len,
17088c2ecf20Sopenharmony_ci			      const struct in6_addr *saddr, int src_len,
17098c2ecf20Sopenharmony_ci			      bool exact_match)
17108c2ecf20Sopenharmony_ci{
17118c2ecf20Sopenharmony_ci	struct fib6_node *fn;
17128c2ecf20Sopenharmony_ci
17138c2ecf20Sopenharmony_ci	fn = fib6_locate_1(root, daddr, dst_len,
17148c2ecf20Sopenharmony_ci			   offsetof(struct fib6_info, fib6_dst),
17158c2ecf20Sopenharmony_ci			   exact_match);
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
17188c2ecf20Sopenharmony_ci	if (src_len) {
17198c2ecf20Sopenharmony_ci		WARN_ON(saddr == NULL);
17208c2ecf20Sopenharmony_ci		if (fn) {
17218c2ecf20Sopenharmony_ci			struct fib6_node *subtree = FIB6_SUBTREE(fn);
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_ci			if (subtree) {
17248c2ecf20Sopenharmony_ci				fn = fib6_locate_1(subtree, saddr, src_len,
17258c2ecf20Sopenharmony_ci					   offsetof(struct fib6_info, fib6_src),
17268c2ecf20Sopenharmony_ci					   exact_match);
17278c2ecf20Sopenharmony_ci			}
17288c2ecf20Sopenharmony_ci		}
17298c2ecf20Sopenharmony_ci	}
17308c2ecf20Sopenharmony_ci#endif
17318c2ecf20Sopenharmony_ci
17328c2ecf20Sopenharmony_ci	if (fn && fn->fn_flags & RTN_RTINFO)
17338c2ecf20Sopenharmony_ci		return fn;
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci	return NULL;
17368c2ecf20Sopenharmony_ci}
17378c2ecf20Sopenharmony_ci
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_ci/*
17408c2ecf20Sopenharmony_ci *	Deletion
17418c2ecf20Sopenharmony_ci *
17428c2ecf20Sopenharmony_ci */
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_cistatic struct fib6_info *fib6_find_prefix(struct net *net,
17458c2ecf20Sopenharmony_ci					 struct fib6_table *table,
17468c2ecf20Sopenharmony_ci					 struct fib6_node *fn)
17478c2ecf20Sopenharmony_ci{
17488c2ecf20Sopenharmony_ci	struct fib6_node *child_left, *child_right;
17498c2ecf20Sopenharmony_ci
17508c2ecf20Sopenharmony_ci	if (fn->fn_flags & RTN_ROOT)
17518c2ecf20Sopenharmony_ci		return net->ipv6.fib6_null_entry;
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci	while (fn) {
17548c2ecf20Sopenharmony_ci		child_left = rcu_dereference_protected(fn->left,
17558c2ecf20Sopenharmony_ci				    lockdep_is_held(&table->tb6_lock));
17568c2ecf20Sopenharmony_ci		child_right = rcu_dereference_protected(fn->right,
17578c2ecf20Sopenharmony_ci				    lockdep_is_held(&table->tb6_lock));
17588c2ecf20Sopenharmony_ci		if (child_left)
17598c2ecf20Sopenharmony_ci			return rcu_dereference_protected(child_left->leaf,
17608c2ecf20Sopenharmony_ci					lockdep_is_held(&table->tb6_lock));
17618c2ecf20Sopenharmony_ci		if (child_right)
17628c2ecf20Sopenharmony_ci			return rcu_dereference_protected(child_right->leaf,
17638c2ecf20Sopenharmony_ci					lockdep_is_held(&table->tb6_lock));
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_ci		fn = FIB6_SUBTREE(fn);
17668c2ecf20Sopenharmony_ci	}
17678c2ecf20Sopenharmony_ci	return NULL;
17688c2ecf20Sopenharmony_ci}
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci/*
17718c2ecf20Sopenharmony_ci *	Called to trim the tree of intermediate nodes when possible. "fn"
17728c2ecf20Sopenharmony_ci *	is the node we want to try and remove.
17738c2ecf20Sopenharmony_ci *	Need to own table->tb6_lock
17748c2ecf20Sopenharmony_ci */
17758c2ecf20Sopenharmony_ci
17768c2ecf20Sopenharmony_cistatic struct fib6_node *fib6_repair_tree(struct net *net,
17778c2ecf20Sopenharmony_ci					  struct fib6_table *table,
17788c2ecf20Sopenharmony_ci					  struct fib6_node *fn)
17798c2ecf20Sopenharmony_ci{
17808c2ecf20Sopenharmony_ci	int children;
17818c2ecf20Sopenharmony_ci	int nstate;
17828c2ecf20Sopenharmony_ci	struct fib6_node *child;
17838c2ecf20Sopenharmony_ci	struct fib6_walker *w;
17848c2ecf20Sopenharmony_ci	int iter = 0;
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_ci	/* Set fn->leaf to null_entry for root node. */
17878c2ecf20Sopenharmony_ci	if (fn->fn_flags & RTN_TL_ROOT) {
17888c2ecf20Sopenharmony_ci		rcu_assign_pointer(fn->leaf, net->ipv6.fib6_null_entry);
17898c2ecf20Sopenharmony_ci		return fn;
17908c2ecf20Sopenharmony_ci	}
17918c2ecf20Sopenharmony_ci
17928c2ecf20Sopenharmony_ci	for (;;) {
17938c2ecf20Sopenharmony_ci		struct fib6_node *fn_r = rcu_dereference_protected(fn->right,
17948c2ecf20Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
17958c2ecf20Sopenharmony_ci		struct fib6_node *fn_l = rcu_dereference_protected(fn->left,
17968c2ecf20Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
17978c2ecf20Sopenharmony_ci		struct fib6_node *pn = rcu_dereference_protected(fn->parent,
17988c2ecf20Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
17998c2ecf20Sopenharmony_ci		struct fib6_node *pn_r = rcu_dereference_protected(pn->right,
18008c2ecf20Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
18018c2ecf20Sopenharmony_ci		struct fib6_node *pn_l = rcu_dereference_protected(pn->left,
18028c2ecf20Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
18038c2ecf20Sopenharmony_ci		struct fib6_info *fn_leaf = rcu_dereference_protected(fn->leaf,
18048c2ecf20Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
18058c2ecf20Sopenharmony_ci		struct fib6_info *pn_leaf = rcu_dereference_protected(pn->leaf,
18068c2ecf20Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
18078c2ecf20Sopenharmony_ci		struct fib6_info *new_fn_leaf;
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci		RT6_TRACE("fixing tree: plen=%d iter=%d\n", fn->fn_bit, iter);
18108c2ecf20Sopenharmony_ci		iter++;
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_ci		WARN_ON(fn->fn_flags & RTN_RTINFO);
18138c2ecf20Sopenharmony_ci		WARN_ON(fn->fn_flags & RTN_TL_ROOT);
18148c2ecf20Sopenharmony_ci		WARN_ON(fn_leaf);
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci		children = 0;
18178c2ecf20Sopenharmony_ci		child = NULL;
18188c2ecf20Sopenharmony_ci		if (fn_r) {
18198c2ecf20Sopenharmony_ci			child = fn_r;
18208c2ecf20Sopenharmony_ci			children |= 1;
18218c2ecf20Sopenharmony_ci		}
18228c2ecf20Sopenharmony_ci		if (fn_l) {
18238c2ecf20Sopenharmony_ci			child = fn_l;
18248c2ecf20Sopenharmony_ci			children |= 2;
18258c2ecf20Sopenharmony_ci		}
18268c2ecf20Sopenharmony_ci
18278c2ecf20Sopenharmony_ci		if (children == 3 || FIB6_SUBTREE(fn)
18288c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
18298c2ecf20Sopenharmony_ci		    /* Subtree root (i.e. fn) may have one child */
18308c2ecf20Sopenharmony_ci		    || (children && fn->fn_flags & RTN_ROOT)
18318c2ecf20Sopenharmony_ci#endif
18328c2ecf20Sopenharmony_ci		    ) {
18338c2ecf20Sopenharmony_ci			new_fn_leaf = fib6_find_prefix(net, table, fn);
18348c2ecf20Sopenharmony_ci#if RT6_DEBUG >= 2
18358c2ecf20Sopenharmony_ci			if (!new_fn_leaf) {
18368c2ecf20Sopenharmony_ci				WARN_ON(!new_fn_leaf);
18378c2ecf20Sopenharmony_ci				new_fn_leaf = net->ipv6.fib6_null_entry;
18388c2ecf20Sopenharmony_ci			}
18398c2ecf20Sopenharmony_ci#endif
18408c2ecf20Sopenharmony_ci			fib6_info_hold(new_fn_leaf);
18418c2ecf20Sopenharmony_ci			rcu_assign_pointer(fn->leaf, new_fn_leaf);
18428c2ecf20Sopenharmony_ci			return pn;
18438c2ecf20Sopenharmony_ci		}
18448c2ecf20Sopenharmony_ci
18458c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
18468c2ecf20Sopenharmony_ci		if (FIB6_SUBTREE(pn) == fn) {
18478c2ecf20Sopenharmony_ci			WARN_ON(!(fn->fn_flags & RTN_ROOT));
18488c2ecf20Sopenharmony_ci			RCU_INIT_POINTER(pn->subtree, NULL);
18498c2ecf20Sopenharmony_ci			nstate = FWS_L;
18508c2ecf20Sopenharmony_ci		} else {
18518c2ecf20Sopenharmony_ci			WARN_ON(fn->fn_flags & RTN_ROOT);
18528c2ecf20Sopenharmony_ci#endif
18538c2ecf20Sopenharmony_ci			if (pn_r == fn)
18548c2ecf20Sopenharmony_ci				rcu_assign_pointer(pn->right, child);
18558c2ecf20Sopenharmony_ci			else if (pn_l == fn)
18568c2ecf20Sopenharmony_ci				rcu_assign_pointer(pn->left, child);
18578c2ecf20Sopenharmony_ci#if RT6_DEBUG >= 2
18588c2ecf20Sopenharmony_ci			else
18598c2ecf20Sopenharmony_ci				WARN_ON(1);
18608c2ecf20Sopenharmony_ci#endif
18618c2ecf20Sopenharmony_ci			if (child)
18628c2ecf20Sopenharmony_ci				rcu_assign_pointer(child->parent, pn);
18638c2ecf20Sopenharmony_ci			nstate = FWS_R;
18648c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
18658c2ecf20Sopenharmony_ci		}
18668c2ecf20Sopenharmony_ci#endif
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci		read_lock(&net->ipv6.fib6_walker_lock);
18698c2ecf20Sopenharmony_ci		FOR_WALKERS(net, w) {
18708c2ecf20Sopenharmony_ci			if (!child) {
18718c2ecf20Sopenharmony_ci				if (w->node == fn) {
18728c2ecf20Sopenharmony_ci					RT6_TRACE("W %p adjusted by delnode 1, s=%d/%d\n", w, w->state, nstate);
18738c2ecf20Sopenharmony_ci					w->node = pn;
18748c2ecf20Sopenharmony_ci					w->state = nstate;
18758c2ecf20Sopenharmony_ci				}
18768c2ecf20Sopenharmony_ci			} else {
18778c2ecf20Sopenharmony_ci				if (w->node == fn) {
18788c2ecf20Sopenharmony_ci					w->node = child;
18798c2ecf20Sopenharmony_ci					if (children&2) {
18808c2ecf20Sopenharmony_ci						RT6_TRACE("W %p adjusted by delnode 2, s=%d\n", w, w->state);
18818c2ecf20Sopenharmony_ci						w->state = w->state >= FWS_R ? FWS_U : FWS_INIT;
18828c2ecf20Sopenharmony_ci					} else {
18838c2ecf20Sopenharmony_ci						RT6_TRACE("W %p adjusted by delnode 2, s=%d\n", w, w->state);
18848c2ecf20Sopenharmony_ci						w->state = w->state >= FWS_C ? FWS_U : FWS_INIT;
18858c2ecf20Sopenharmony_ci					}
18868c2ecf20Sopenharmony_ci				}
18878c2ecf20Sopenharmony_ci			}
18888c2ecf20Sopenharmony_ci		}
18898c2ecf20Sopenharmony_ci		read_unlock(&net->ipv6.fib6_walker_lock);
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci		node_free(net, fn);
18928c2ecf20Sopenharmony_ci		if (pn->fn_flags & RTN_RTINFO || FIB6_SUBTREE(pn))
18938c2ecf20Sopenharmony_ci			return pn;
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_ci		RCU_INIT_POINTER(pn->leaf, NULL);
18968c2ecf20Sopenharmony_ci		fib6_info_release(pn_leaf);
18978c2ecf20Sopenharmony_ci		fn = pn;
18988c2ecf20Sopenharmony_ci	}
18998c2ecf20Sopenharmony_ci}
19008c2ecf20Sopenharmony_ci
19018c2ecf20Sopenharmony_cistatic void fib6_del_route(struct fib6_table *table, struct fib6_node *fn,
19028c2ecf20Sopenharmony_ci			   struct fib6_info __rcu **rtp, struct nl_info *info)
19038c2ecf20Sopenharmony_ci{
19048c2ecf20Sopenharmony_ci	struct fib6_info *leaf, *replace_rt = NULL;
19058c2ecf20Sopenharmony_ci	struct fib6_walker *w;
19068c2ecf20Sopenharmony_ci	struct fib6_info *rt = rcu_dereference_protected(*rtp,
19078c2ecf20Sopenharmony_ci				    lockdep_is_held(&table->tb6_lock));
19088c2ecf20Sopenharmony_ci	struct net *net = info->nl_net;
19098c2ecf20Sopenharmony_ci	bool notify_del = false;
19108c2ecf20Sopenharmony_ci
19118c2ecf20Sopenharmony_ci	RT6_TRACE("fib6_del_route\n");
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_ci	/* If the deleted route is the first in the node and it is not part of
19148c2ecf20Sopenharmony_ci	 * a multipath route, then we need to replace it with the next route
19158c2ecf20Sopenharmony_ci	 * in the node, if exists.
19168c2ecf20Sopenharmony_ci	 */
19178c2ecf20Sopenharmony_ci	leaf = rcu_dereference_protected(fn->leaf,
19188c2ecf20Sopenharmony_ci					 lockdep_is_held(&table->tb6_lock));
19198c2ecf20Sopenharmony_ci	if (leaf == rt && !rt->fib6_nsiblings) {
19208c2ecf20Sopenharmony_ci		if (rcu_access_pointer(rt->fib6_next))
19218c2ecf20Sopenharmony_ci			replace_rt = rcu_dereference_protected(rt->fib6_next,
19228c2ecf20Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
19238c2ecf20Sopenharmony_ci		else
19248c2ecf20Sopenharmony_ci			notify_del = true;
19258c2ecf20Sopenharmony_ci	}
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_ci	/* Unlink it */
19288c2ecf20Sopenharmony_ci	*rtp = rt->fib6_next;
19298c2ecf20Sopenharmony_ci	rt->fib6_node = NULL;
19308c2ecf20Sopenharmony_ci	net->ipv6.rt6_stats->fib_rt_entries--;
19318c2ecf20Sopenharmony_ci	net->ipv6.rt6_stats->fib_discarded_routes++;
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci	/* Reset round-robin state, if necessary */
19348c2ecf20Sopenharmony_ci	if (rcu_access_pointer(fn->rr_ptr) == rt)
19358c2ecf20Sopenharmony_ci		fn->rr_ptr = NULL;
19368c2ecf20Sopenharmony_ci
19378c2ecf20Sopenharmony_ci	/* Remove this entry from other siblings */
19388c2ecf20Sopenharmony_ci	if (rt->fib6_nsiblings) {
19398c2ecf20Sopenharmony_ci		struct fib6_info *sibling, *next_sibling;
19408c2ecf20Sopenharmony_ci
19418c2ecf20Sopenharmony_ci		/* The route is deleted from a multipath route. If this
19428c2ecf20Sopenharmony_ci		 * multipath route is the first route in the node, then we need
19438c2ecf20Sopenharmony_ci		 * to emit a delete notification. Otherwise, we need to skip
19448c2ecf20Sopenharmony_ci		 * the notification.
19458c2ecf20Sopenharmony_ci		 */
19468c2ecf20Sopenharmony_ci		if (rt->fib6_metric == leaf->fib6_metric &&
19478c2ecf20Sopenharmony_ci		    rt6_qualify_for_ecmp(leaf))
19488c2ecf20Sopenharmony_ci			notify_del = true;
19498c2ecf20Sopenharmony_ci		list_for_each_entry_safe(sibling, next_sibling,
19508c2ecf20Sopenharmony_ci					 &rt->fib6_siblings, fib6_siblings)
19518c2ecf20Sopenharmony_ci			sibling->fib6_nsiblings--;
19528c2ecf20Sopenharmony_ci		rt->fib6_nsiblings = 0;
19538c2ecf20Sopenharmony_ci		list_del_init(&rt->fib6_siblings);
19548c2ecf20Sopenharmony_ci		rt6_multipath_rebalance(next_sibling);
19558c2ecf20Sopenharmony_ci	}
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ci	/* Adjust walkers */
19588c2ecf20Sopenharmony_ci	read_lock(&net->ipv6.fib6_walker_lock);
19598c2ecf20Sopenharmony_ci	FOR_WALKERS(net, w) {
19608c2ecf20Sopenharmony_ci		if (w->state == FWS_C && w->leaf == rt) {
19618c2ecf20Sopenharmony_ci			RT6_TRACE("walker %p adjusted by delroute\n", w);
19628c2ecf20Sopenharmony_ci			w->leaf = rcu_dereference_protected(rt->fib6_next,
19638c2ecf20Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
19648c2ecf20Sopenharmony_ci			if (!w->leaf)
19658c2ecf20Sopenharmony_ci				w->state = FWS_U;
19668c2ecf20Sopenharmony_ci		}
19678c2ecf20Sopenharmony_ci	}
19688c2ecf20Sopenharmony_ci	read_unlock(&net->ipv6.fib6_walker_lock);
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci	/* If it was last route, call fib6_repair_tree() to:
19718c2ecf20Sopenharmony_ci	 * 1. For root node, put back null_entry as how the table was created.
19728c2ecf20Sopenharmony_ci	 * 2. For other nodes, expunge its radix tree node.
19738c2ecf20Sopenharmony_ci	 */
19748c2ecf20Sopenharmony_ci	if (!rcu_access_pointer(fn->leaf)) {
19758c2ecf20Sopenharmony_ci		if (!(fn->fn_flags & RTN_TL_ROOT)) {
19768c2ecf20Sopenharmony_ci			fn->fn_flags &= ~RTN_RTINFO;
19778c2ecf20Sopenharmony_ci			net->ipv6.rt6_stats->fib_route_nodes--;
19788c2ecf20Sopenharmony_ci		}
19798c2ecf20Sopenharmony_ci		fn = fib6_repair_tree(net, table, fn);
19808c2ecf20Sopenharmony_ci	}
19818c2ecf20Sopenharmony_ci
19828c2ecf20Sopenharmony_ci	fib6_purge_rt(rt, fn, net);
19838c2ecf20Sopenharmony_ci
19848c2ecf20Sopenharmony_ci	if (!info->skip_notify_kernel) {
19858c2ecf20Sopenharmony_ci		if (notify_del)
19868c2ecf20Sopenharmony_ci			call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_DEL,
19878c2ecf20Sopenharmony_ci						  rt, NULL);
19888c2ecf20Sopenharmony_ci		else if (replace_rt)
19898c2ecf20Sopenharmony_ci			call_fib6_entry_notifiers_replace(net, replace_rt);
19908c2ecf20Sopenharmony_ci	}
19918c2ecf20Sopenharmony_ci	if (!info->skip_notify)
19928c2ecf20Sopenharmony_ci		inet6_rt_notify(RTM_DELROUTE, rt, info, 0);
19938c2ecf20Sopenharmony_ci
19948c2ecf20Sopenharmony_ci	fib6_info_release(rt);
19958c2ecf20Sopenharmony_ci}
19968c2ecf20Sopenharmony_ci
19978c2ecf20Sopenharmony_ci/* Need to own table->tb6_lock */
19988c2ecf20Sopenharmony_ciint fib6_del(struct fib6_info *rt, struct nl_info *info)
19998c2ecf20Sopenharmony_ci{
20008c2ecf20Sopenharmony_ci	struct net *net = info->nl_net;
20018c2ecf20Sopenharmony_ci	struct fib6_info __rcu **rtp;
20028c2ecf20Sopenharmony_ci	struct fib6_info __rcu **rtp_next;
20038c2ecf20Sopenharmony_ci	struct fib6_table *table;
20048c2ecf20Sopenharmony_ci	struct fib6_node *fn;
20058c2ecf20Sopenharmony_ci
20068c2ecf20Sopenharmony_ci	if (rt == net->ipv6.fib6_null_entry)
20078c2ecf20Sopenharmony_ci		return -ENOENT;
20088c2ecf20Sopenharmony_ci
20098c2ecf20Sopenharmony_ci	table = rt->fib6_table;
20108c2ecf20Sopenharmony_ci	fn = rcu_dereference_protected(rt->fib6_node,
20118c2ecf20Sopenharmony_ci				       lockdep_is_held(&table->tb6_lock));
20128c2ecf20Sopenharmony_ci	if (!fn)
20138c2ecf20Sopenharmony_ci		return -ENOENT;
20148c2ecf20Sopenharmony_ci
20158c2ecf20Sopenharmony_ci	WARN_ON(!(fn->fn_flags & RTN_RTINFO));
20168c2ecf20Sopenharmony_ci
20178c2ecf20Sopenharmony_ci	/*
20188c2ecf20Sopenharmony_ci	 *	Walk the leaf entries looking for ourself
20198c2ecf20Sopenharmony_ci	 */
20208c2ecf20Sopenharmony_ci
20218c2ecf20Sopenharmony_ci	for (rtp = &fn->leaf; *rtp; rtp = rtp_next) {
20228c2ecf20Sopenharmony_ci		struct fib6_info *cur = rcu_dereference_protected(*rtp,
20238c2ecf20Sopenharmony_ci					lockdep_is_held(&table->tb6_lock));
20248c2ecf20Sopenharmony_ci		if (rt == cur) {
20258c2ecf20Sopenharmony_ci			if (fib6_requires_src(cur))
20268c2ecf20Sopenharmony_ci				fib6_routes_require_src_dec(info->nl_net);
20278c2ecf20Sopenharmony_ci			fib6_del_route(table, fn, rtp, info);
20288c2ecf20Sopenharmony_ci			return 0;
20298c2ecf20Sopenharmony_ci		}
20308c2ecf20Sopenharmony_ci		rtp_next = &cur->fib6_next;
20318c2ecf20Sopenharmony_ci	}
20328c2ecf20Sopenharmony_ci	return -ENOENT;
20338c2ecf20Sopenharmony_ci}
20348c2ecf20Sopenharmony_ci
20358c2ecf20Sopenharmony_ci/*
20368c2ecf20Sopenharmony_ci *	Tree traversal function.
20378c2ecf20Sopenharmony_ci *
20388c2ecf20Sopenharmony_ci *	Certainly, it is not interrupt safe.
20398c2ecf20Sopenharmony_ci *	However, it is internally reenterable wrt itself and fib6_add/fib6_del.
20408c2ecf20Sopenharmony_ci *	It means, that we can modify tree during walking
20418c2ecf20Sopenharmony_ci *	and use this function for garbage collection, clone pruning,
20428c2ecf20Sopenharmony_ci *	cleaning tree when a device goes down etc. etc.
20438c2ecf20Sopenharmony_ci *
20448c2ecf20Sopenharmony_ci *	It guarantees that every node will be traversed,
20458c2ecf20Sopenharmony_ci *	and that it will be traversed only once.
20468c2ecf20Sopenharmony_ci *
20478c2ecf20Sopenharmony_ci *	Callback function w->func may return:
20488c2ecf20Sopenharmony_ci *	0 -> continue walking.
20498c2ecf20Sopenharmony_ci *	positive value -> walking is suspended (used by tree dumps,
20508c2ecf20Sopenharmony_ci *	and probably by gc, if it will be split to several slices)
20518c2ecf20Sopenharmony_ci *	negative value -> terminate walking.
20528c2ecf20Sopenharmony_ci *
20538c2ecf20Sopenharmony_ci *	The function itself returns:
20548c2ecf20Sopenharmony_ci *	0   -> walk is complete.
20558c2ecf20Sopenharmony_ci *	>0  -> walk is incomplete (i.e. suspended)
20568c2ecf20Sopenharmony_ci *	<0  -> walk is terminated by an error.
20578c2ecf20Sopenharmony_ci *
20588c2ecf20Sopenharmony_ci *	This function is called with tb6_lock held.
20598c2ecf20Sopenharmony_ci */
20608c2ecf20Sopenharmony_ci
20618c2ecf20Sopenharmony_cistatic int fib6_walk_continue(struct fib6_walker *w)
20628c2ecf20Sopenharmony_ci{
20638c2ecf20Sopenharmony_ci	struct fib6_node *fn, *pn, *left, *right;
20648c2ecf20Sopenharmony_ci
20658c2ecf20Sopenharmony_ci	/* w->root should always be table->tb6_root */
20668c2ecf20Sopenharmony_ci	WARN_ON_ONCE(!(w->root->fn_flags & RTN_TL_ROOT));
20678c2ecf20Sopenharmony_ci
20688c2ecf20Sopenharmony_ci	for (;;) {
20698c2ecf20Sopenharmony_ci		fn = w->node;
20708c2ecf20Sopenharmony_ci		if (!fn)
20718c2ecf20Sopenharmony_ci			return 0;
20728c2ecf20Sopenharmony_ci
20738c2ecf20Sopenharmony_ci		switch (w->state) {
20748c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
20758c2ecf20Sopenharmony_ci		case FWS_S:
20768c2ecf20Sopenharmony_ci			if (FIB6_SUBTREE(fn)) {
20778c2ecf20Sopenharmony_ci				w->node = FIB6_SUBTREE(fn);
20788c2ecf20Sopenharmony_ci				continue;
20798c2ecf20Sopenharmony_ci			}
20808c2ecf20Sopenharmony_ci			w->state = FWS_L;
20818c2ecf20Sopenharmony_ci			fallthrough;
20828c2ecf20Sopenharmony_ci#endif
20838c2ecf20Sopenharmony_ci		case FWS_L:
20848c2ecf20Sopenharmony_ci			left = rcu_dereference_protected(fn->left, 1);
20858c2ecf20Sopenharmony_ci			if (left) {
20868c2ecf20Sopenharmony_ci				w->node = left;
20878c2ecf20Sopenharmony_ci				w->state = FWS_INIT;
20888c2ecf20Sopenharmony_ci				continue;
20898c2ecf20Sopenharmony_ci			}
20908c2ecf20Sopenharmony_ci			w->state = FWS_R;
20918c2ecf20Sopenharmony_ci			fallthrough;
20928c2ecf20Sopenharmony_ci		case FWS_R:
20938c2ecf20Sopenharmony_ci			right = rcu_dereference_protected(fn->right, 1);
20948c2ecf20Sopenharmony_ci			if (right) {
20958c2ecf20Sopenharmony_ci				w->node = right;
20968c2ecf20Sopenharmony_ci				w->state = FWS_INIT;
20978c2ecf20Sopenharmony_ci				continue;
20988c2ecf20Sopenharmony_ci			}
20998c2ecf20Sopenharmony_ci			w->state = FWS_C;
21008c2ecf20Sopenharmony_ci			w->leaf = rcu_dereference_protected(fn->leaf, 1);
21018c2ecf20Sopenharmony_ci			fallthrough;
21028c2ecf20Sopenharmony_ci		case FWS_C:
21038c2ecf20Sopenharmony_ci			if (w->leaf && fn->fn_flags & RTN_RTINFO) {
21048c2ecf20Sopenharmony_ci				int err;
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_ci				if (w->skip) {
21078c2ecf20Sopenharmony_ci					w->skip--;
21088c2ecf20Sopenharmony_ci					goto skip;
21098c2ecf20Sopenharmony_ci				}
21108c2ecf20Sopenharmony_ci
21118c2ecf20Sopenharmony_ci				err = w->func(w);
21128c2ecf20Sopenharmony_ci				if (err)
21138c2ecf20Sopenharmony_ci					return err;
21148c2ecf20Sopenharmony_ci
21158c2ecf20Sopenharmony_ci				w->count++;
21168c2ecf20Sopenharmony_ci				continue;
21178c2ecf20Sopenharmony_ci			}
21188c2ecf20Sopenharmony_ciskip:
21198c2ecf20Sopenharmony_ci			w->state = FWS_U;
21208c2ecf20Sopenharmony_ci			fallthrough;
21218c2ecf20Sopenharmony_ci		case FWS_U:
21228c2ecf20Sopenharmony_ci			if (fn == w->root)
21238c2ecf20Sopenharmony_ci				return 0;
21248c2ecf20Sopenharmony_ci			pn = rcu_dereference_protected(fn->parent, 1);
21258c2ecf20Sopenharmony_ci			left = rcu_dereference_protected(pn->left, 1);
21268c2ecf20Sopenharmony_ci			right = rcu_dereference_protected(pn->right, 1);
21278c2ecf20Sopenharmony_ci			w->node = pn;
21288c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
21298c2ecf20Sopenharmony_ci			if (FIB6_SUBTREE(pn) == fn) {
21308c2ecf20Sopenharmony_ci				WARN_ON(!(fn->fn_flags & RTN_ROOT));
21318c2ecf20Sopenharmony_ci				w->state = FWS_L;
21328c2ecf20Sopenharmony_ci				continue;
21338c2ecf20Sopenharmony_ci			}
21348c2ecf20Sopenharmony_ci#endif
21358c2ecf20Sopenharmony_ci			if (left == fn) {
21368c2ecf20Sopenharmony_ci				w->state = FWS_R;
21378c2ecf20Sopenharmony_ci				continue;
21388c2ecf20Sopenharmony_ci			}
21398c2ecf20Sopenharmony_ci			if (right == fn) {
21408c2ecf20Sopenharmony_ci				w->state = FWS_C;
21418c2ecf20Sopenharmony_ci				w->leaf = rcu_dereference_protected(w->node->leaf, 1);
21428c2ecf20Sopenharmony_ci				continue;
21438c2ecf20Sopenharmony_ci			}
21448c2ecf20Sopenharmony_ci#if RT6_DEBUG >= 2
21458c2ecf20Sopenharmony_ci			WARN_ON(1);
21468c2ecf20Sopenharmony_ci#endif
21478c2ecf20Sopenharmony_ci		}
21488c2ecf20Sopenharmony_ci	}
21498c2ecf20Sopenharmony_ci}
21508c2ecf20Sopenharmony_ci
21518c2ecf20Sopenharmony_cistatic int fib6_walk(struct net *net, struct fib6_walker *w)
21528c2ecf20Sopenharmony_ci{
21538c2ecf20Sopenharmony_ci	int res;
21548c2ecf20Sopenharmony_ci
21558c2ecf20Sopenharmony_ci	w->state = FWS_INIT;
21568c2ecf20Sopenharmony_ci	w->node = w->root;
21578c2ecf20Sopenharmony_ci
21588c2ecf20Sopenharmony_ci	fib6_walker_link(net, w);
21598c2ecf20Sopenharmony_ci	res = fib6_walk_continue(w);
21608c2ecf20Sopenharmony_ci	if (res <= 0)
21618c2ecf20Sopenharmony_ci		fib6_walker_unlink(net, w);
21628c2ecf20Sopenharmony_ci	return res;
21638c2ecf20Sopenharmony_ci}
21648c2ecf20Sopenharmony_ci
21658c2ecf20Sopenharmony_cistatic int fib6_clean_node(struct fib6_walker *w)
21668c2ecf20Sopenharmony_ci{
21678c2ecf20Sopenharmony_ci	int res;
21688c2ecf20Sopenharmony_ci	struct fib6_info *rt;
21698c2ecf20Sopenharmony_ci	struct fib6_cleaner *c = container_of(w, struct fib6_cleaner, w);
21708c2ecf20Sopenharmony_ci	struct nl_info info = {
21718c2ecf20Sopenharmony_ci		.nl_net = c->net,
21728c2ecf20Sopenharmony_ci		.skip_notify = c->skip_notify,
21738c2ecf20Sopenharmony_ci	};
21748c2ecf20Sopenharmony_ci
21758c2ecf20Sopenharmony_ci	if (c->sernum != FIB6_NO_SERNUM_CHANGE &&
21768c2ecf20Sopenharmony_ci	    READ_ONCE(w->node->fn_sernum) != c->sernum)
21778c2ecf20Sopenharmony_ci		WRITE_ONCE(w->node->fn_sernum, c->sernum);
21788c2ecf20Sopenharmony_ci
21798c2ecf20Sopenharmony_ci	if (!c->func) {
21808c2ecf20Sopenharmony_ci		WARN_ON_ONCE(c->sernum == FIB6_NO_SERNUM_CHANGE);
21818c2ecf20Sopenharmony_ci		w->leaf = NULL;
21828c2ecf20Sopenharmony_ci		return 0;
21838c2ecf20Sopenharmony_ci	}
21848c2ecf20Sopenharmony_ci
21858c2ecf20Sopenharmony_ci	for_each_fib6_walker_rt(w) {
21868c2ecf20Sopenharmony_ci		res = c->func(rt, c->arg);
21878c2ecf20Sopenharmony_ci		if (res == -1) {
21888c2ecf20Sopenharmony_ci			w->leaf = rt;
21898c2ecf20Sopenharmony_ci			res = fib6_del(rt, &info);
21908c2ecf20Sopenharmony_ci			if (res) {
21918c2ecf20Sopenharmony_ci#if RT6_DEBUG >= 2
21928c2ecf20Sopenharmony_ci				pr_debug("%s: del failed: rt=%p@%p err=%d\n",
21938c2ecf20Sopenharmony_ci					 __func__, rt,
21948c2ecf20Sopenharmony_ci					 rcu_access_pointer(rt->fib6_node),
21958c2ecf20Sopenharmony_ci					 res);
21968c2ecf20Sopenharmony_ci#endif
21978c2ecf20Sopenharmony_ci				continue;
21988c2ecf20Sopenharmony_ci			}
21998c2ecf20Sopenharmony_ci			return 0;
22008c2ecf20Sopenharmony_ci		} else if (res == -2) {
22018c2ecf20Sopenharmony_ci			if (WARN_ON(!rt->fib6_nsiblings))
22028c2ecf20Sopenharmony_ci				continue;
22038c2ecf20Sopenharmony_ci			rt = list_last_entry(&rt->fib6_siblings,
22048c2ecf20Sopenharmony_ci					     struct fib6_info, fib6_siblings);
22058c2ecf20Sopenharmony_ci			continue;
22068c2ecf20Sopenharmony_ci		}
22078c2ecf20Sopenharmony_ci		WARN_ON(res != 0);
22088c2ecf20Sopenharmony_ci	}
22098c2ecf20Sopenharmony_ci	w->leaf = rt;
22108c2ecf20Sopenharmony_ci	return 0;
22118c2ecf20Sopenharmony_ci}
22128c2ecf20Sopenharmony_ci
22138c2ecf20Sopenharmony_ci/*
22148c2ecf20Sopenharmony_ci *	Convenient frontend to tree walker.
22158c2ecf20Sopenharmony_ci *
22168c2ecf20Sopenharmony_ci *	func is called on each route.
22178c2ecf20Sopenharmony_ci *		It may return -2 -> skip multipath route.
22188c2ecf20Sopenharmony_ci *			      -1 -> delete this route.
22198c2ecf20Sopenharmony_ci *		              0  -> continue walking
22208c2ecf20Sopenharmony_ci */
22218c2ecf20Sopenharmony_ci
22228c2ecf20Sopenharmony_cistatic void fib6_clean_tree(struct net *net, struct fib6_node *root,
22238c2ecf20Sopenharmony_ci			    int (*func)(struct fib6_info *, void *arg),
22248c2ecf20Sopenharmony_ci			    int sernum, void *arg, bool skip_notify)
22258c2ecf20Sopenharmony_ci{
22268c2ecf20Sopenharmony_ci	struct fib6_cleaner c;
22278c2ecf20Sopenharmony_ci
22288c2ecf20Sopenharmony_ci	c.w.root = root;
22298c2ecf20Sopenharmony_ci	c.w.func = fib6_clean_node;
22308c2ecf20Sopenharmony_ci	c.w.count = 0;
22318c2ecf20Sopenharmony_ci	c.w.skip = 0;
22328c2ecf20Sopenharmony_ci	c.w.skip_in_node = 0;
22338c2ecf20Sopenharmony_ci	c.func = func;
22348c2ecf20Sopenharmony_ci	c.sernum = sernum;
22358c2ecf20Sopenharmony_ci	c.arg = arg;
22368c2ecf20Sopenharmony_ci	c.net = net;
22378c2ecf20Sopenharmony_ci	c.skip_notify = skip_notify;
22388c2ecf20Sopenharmony_ci
22398c2ecf20Sopenharmony_ci	fib6_walk(net, &c.w);
22408c2ecf20Sopenharmony_ci}
22418c2ecf20Sopenharmony_ci
22428c2ecf20Sopenharmony_cistatic void __fib6_clean_all(struct net *net,
22438c2ecf20Sopenharmony_ci			     int (*func)(struct fib6_info *, void *),
22448c2ecf20Sopenharmony_ci			     int sernum, void *arg, bool skip_notify)
22458c2ecf20Sopenharmony_ci{
22468c2ecf20Sopenharmony_ci	struct fib6_table *table;
22478c2ecf20Sopenharmony_ci	struct hlist_head *head;
22488c2ecf20Sopenharmony_ci	unsigned int h;
22498c2ecf20Sopenharmony_ci
22508c2ecf20Sopenharmony_ci	rcu_read_lock();
22518c2ecf20Sopenharmony_ci	for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
22528c2ecf20Sopenharmony_ci		head = &net->ipv6.fib_table_hash[h];
22538c2ecf20Sopenharmony_ci		hlist_for_each_entry_rcu(table, head, tb6_hlist) {
22548c2ecf20Sopenharmony_ci			spin_lock_bh(&table->tb6_lock);
22558c2ecf20Sopenharmony_ci			fib6_clean_tree(net, &table->tb6_root,
22568c2ecf20Sopenharmony_ci					func, sernum, arg, skip_notify);
22578c2ecf20Sopenharmony_ci			spin_unlock_bh(&table->tb6_lock);
22588c2ecf20Sopenharmony_ci		}
22598c2ecf20Sopenharmony_ci	}
22608c2ecf20Sopenharmony_ci	rcu_read_unlock();
22618c2ecf20Sopenharmony_ci}
22628c2ecf20Sopenharmony_ci
22638c2ecf20Sopenharmony_civoid fib6_clean_all(struct net *net, int (*func)(struct fib6_info *, void *),
22648c2ecf20Sopenharmony_ci		    void *arg)
22658c2ecf20Sopenharmony_ci{
22668c2ecf20Sopenharmony_ci	__fib6_clean_all(net, func, FIB6_NO_SERNUM_CHANGE, arg, false);
22678c2ecf20Sopenharmony_ci}
22688c2ecf20Sopenharmony_ci
22698c2ecf20Sopenharmony_civoid fib6_clean_all_skip_notify(struct net *net,
22708c2ecf20Sopenharmony_ci				int (*func)(struct fib6_info *, void *),
22718c2ecf20Sopenharmony_ci				void *arg)
22728c2ecf20Sopenharmony_ci{
22738c2ecf20Sopenharmony_ci	__fib6_clean_all(net, func, FIB6_NO_SERNUM_CHANGE, arg, true);
22748c2ecf20Sopenharmony_ci}
22758c2ecf20Sopenharmony_ci
22768c2ecf20Sopenharmony_cistatic void fib6_flush_trees(struct net *net)
22778c2ecf20Sopenharmony_ci{
22788c2ecf20Sopenharmony_ci	int new_sernum = fib6_new_sernum(net);
22798c2ecf20Sopenharmony_ci
22808c2ecf20Sopenharmony_ci	__fib6_clean_all(net, NULL, new_sernum, NULL, false);
22818c2ecf20Sopenharmony_ci}
22828c2ecf20Sopenharmony_ci
22838c2ecf20Sopenharmony_ci/*
22848c2ecf20Sopenharmony_ci *	Garbage collection
22858c2ecf20Sopenharmony_ci */
22868c2ecf20Sopenharmony_ci
22878c2ecf20Sopenharmony_cistatic int fib6_age(struct fib6_info *rt, void *arg)
22888c2ecf20Sopenharmony_ci{
22898c2ecf20Sopenharmony_ci	struct fib6_gc_args *gc_args = arg;
22908c2ecf20Sopenharmony_ci	unsigned long now = jiffies;
22918c2ecf20Sopenharmony_ci
22928c2ecf20Sopenharmony_ci	/*
22938c2ecf20Sopenharmony_ci	 *	check addrconf expiration here.
22948c2ecf20Sopenharmony_ci	 *	Routes are expired even if they are in use.
22958c2ecf20Sopenharmony_ci	 */
22968c2ecf20Sopenharmony_ci
22978c2ecf20Sopenharmony_ci	if (rt->fib6_flags & RTF_EXPIRES && rt->expires) {
22988c2ecf20Sopenharmony_ci		if (time_after(now, rt->expires)) {
22998c2ecf20Sopenharmony_ci			RT6_TRACE("expiring %p\n", rt);
23008c2ecf20Sopenharmony_ci			return -1;
23018c2ecf20Sopenharmony_ci		}
23028c2ecf20Sopenharmony_ci		gc_args->more++;
23038c2ecf20Sopenharmony_ci	}
23048c2ecf20Sopenharmony_ci
23058c2ecf20Sopenharmony_ci	/*	Also age clones in the exception table.
23068c2ecf20Sopenharmony_ci	 *	Note, that clones are aged out
23078c2ecf20Sopenharmony_ci	 *	only if they are not in use now.
23088c2ecf20Sopenharmony_ci	 */
23098c2ecf20Sopenharmony_ci	rt6_age_exceptions(rt, gc_args, now);
23108c2ecf20Sopenharmony_ci
23118c2ecf20Sopenharmony_ci	return 0;
23128c2ecf20Sopenharmony_ci}
23138c2ecf20Sopenharmony_ci
23148c2ecf20Sopenharmony_civoid fib6_run_gc(unsigned long expires, struct net *net, bool force)
23158c2ecf20Sopenharmony_ci{
23168c2ecf20Sopenharmony_ci	struct fib6_gc_args gc_args;
23178c2ecf20Sopenharmony_ci	unsigned long now;
23188c2ecf20Sopenharmony_ci
23198c2ecf20Sopenharmony_ci	if (force) {
23208c2ecf20Sopenharmony_ci		spin_lock_bh(&net->ipv6.fib6_gc_lock);
23218c2ecf20Sopenharmony_ci	} else if (!spin_trylock_bh(&net->ipv6.fib6_gc_lock)) {
23228c2ecf20Sopenharmony_ci		mod_timer(&net->ipv6.ip6_fib_timer, jiffies + HZ);
23238c2ecf20Sopenharmony_ci		return;
23248c2ecf20Sopenharmony_ci	}
23258c2ecf20Sopenharmony_ci	gc_args.timeout = expires ? (int)expires :
23268c2ecf20Sopenharmony_ci			  net->ipv6.sysctl.ip6_rt_gc_interval;
23278c2ecf20Sopenharmony_ci	gc_args.more = 0;
23288c2ecf20Sopenharmony_ci
23298c2ecf20Sopenharmony_ci	fib6_clean_all(net, fib6_age, &gc_args);
23308c2ecf20Sopenharmony_ci	now = jiffies;
23318c2ecf20Sopenharmony_ci	net->ipv6.ip6_rt_last_gc = now;
23328c2ecf20Sopenharmony_ci
23338c2ecf20Sopenharmony_ci	if (gc_args.more)
23348c2ecf20Sopenharmony_ci		mod_timer(&net->ipv6.ip6_fib_timer,
23358c2ecf20Sopenharmony_ci			  round_jiffies(now
23368c2ecf20Sopenharmony_ci					+ net->ipv6.sysctl.ip6_rt_gc_interval));
23378c2ecf20Sopenharmony_ci	else
23388c2ecf20Sopenharmony_ci		del_timer(&net->ipv6.ip6_fib_timer);
23398c2ecf20Sopenharmony_ci	spin_unlock_bh(&net->ipv6.fib6_gc_lock);
23408c2ecf20Sopenharmony_ci}
23418c2ecf20Sopenharmony_ci
23428c2ecf20Sopenharmony_cistatic void fib6_gc_timer_cb(struct timer_list *t)
23438c2ecf20Sopenharmony_ci{
23448c2ecf20Sopenharmony_ci	struct net *arg = from_timer(arg, t, ipv6.ip6_fib_timer);
23458c2ecf20Sopenharmony_ci
23468c2ecf20Sopenharmony_ci	fib6_run_gc(0, arg, true);
23478c2ecf20Sopenharmony_ci}
23488c2ecf20Sopenharmony_ci
23498c2ecf20Sopenharmony_cistatic int __net_init fib6_net_init(struct net *net)
23508c2ecf20Sopenharmony_ci{
23518c2ecf20Sopenharmony_ci	size_t size = sizeof(struct hlist_head) * FIB6_TABLE_HASHSZ;
23528c2ecf20Sopenharmony_ci	int err;
23538c2ecf20Sopenharmony_ci
23548c2ecf20Sopenharmony_ci	err = fib6_notifier_init(net);
23558c2ecf20Sopenharmony_ci	if (err)
23568c2ecf20Sopenharmony_ci		return err;
23578c2ecf20Sopenharmony_ci
23588c2ecf20Sopenharmony_ci	spin_lock_init(&net->ipv6.fib6_gc_lock);
23598c2ecf20Sopenharmony_ci	rwlock_init(&net->ipv6.fib6_walker_lock);
23608c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&net->ipv6.fib6_walkers);
23618c2ecf20Sopenharmony_ci	timer_setup(&net->ipv6.ip6_fib_timer, fib6_gc_timer_cb, 0);
23628c2ecf20Sopenharmony_ci
23638c2ecf20Sopenharmony_ci	net->ipv6.rt6_stats = kzalloc(sizeof(*net->ipv6.rt6_stats), GFP_KERNEL);
23648c2ecf20Sopenharmony_ci	if (!net->ipv6.rt6_stats)
23658c2ecf20Sopenharmony_ci		goto out_timer;
23668c2ecf20Sopenharmony_ci
23678c2ecf20Sopenharmony_ci	/* Avoid false sharing : Use at least a full cache line */
23688c2ecf20Sopenharmony_ci	size = max_t(size_t, size, L1_CACHE_BYTES);
23698c2ecf20Sopenharmony_ci
23708c2ecf20Sopenharmony_ci	net->ipv6.fib_table_hash = kzalloc(size, GFP_KERNEL);
23718c2ecf20Sopenharmony_ci	if (!net->ipv6.fib_table_hash)
23728c2ecf20Sopenharmony_ci		goto out_rt6_stats;
23738c2ecf20Sopenharmony_ci
23748c2ecf20Sopenharmony_ci	net->ipv6.fib6_main_tbl = kzalloc(sizeof(*net->ipv6.fib6_main_tbl),
23758c2ecf20Sopenharmony_ci					  GFP_KERNEL);
23768c2ecf20Sopenharmony_ci	if (!net->ipv6.fib6_main_tbl)
23778c2ecf20Sopenharmony_ci		goto out_fib_table_hash;
23788c2ecf20Sopenharmony_ci
23798c2ecf20Sopenharmony_ci	net->ipv6.fib6_main_tbl->tb6_id = RT6_TABLE_MAIN;
23808c2ecf20Sopenharmony_ci	rcu_assign_pointer(net->ipv6.fib6_main_tbl->tb6_root.leaf,
23818c2ecf20Sopenharmony_ci			   net->ipv6.fib6_null_entry);
23828c2ecf20Sopenharmony_ci	net->ipv6.fib6_main_tbl->tb6_root.fn_flags =
23838c2ecf20Sopenharmony_ci		RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
23848c2ecf20Sopenharmony_ci	inet_peer_base_init(&net->ipv6.fib6_main_tbl->tb6_peers);
23858c2ecf20Sopenharmony_ci
23868c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES
23878c2ecf20Sopenharmony_ci	net->ipv6.fib6_local_tbl = kzalloc(sizeof(*net->ipv6.fib6_local_tbl),
23888c2ecf20Sopenharmony_ci					   GFP_KERNEL);
23898c2ecf20Sopenharmony_ci	if (!net->ipv6.fib6_local_tbl)
23908c2ecf20Sopenharmony_ci		goto out_fib6_main_tbl;
23918c2ecf20Sopenharmony_ci	net->ipv6.fib6_local_tbl->tb6_id = RT6_TABLE_LOCAL;
23928c2ecf20Sopenharmony_ci	rcu_assign_pointer(net->ipv6.fib6_local_tbl->tb6_root.leaf,
23938c2ecf20Sopenharmony_ci			   net->ipv6.fib6_null_entry);
23948c2ecf20Sopenharmony_ci	net->ipv6.fib6_local_tbl->tb6_root.fn_flags =
23958c2ecf20Sopenharmony_ci		RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
23968c2ecf20Sopenharmony_ci	inet_peer_base_init(&net->ipv6.fib6_local_tbl->tb6_peers);
23978c2ecf20Sopenharmony_ci#endif
23988c2ecf20Sopenharmony_ci	fib6_tables_init(net);
23998c2ecf20Sopenharmony_ci
24008c2ecf20Sopenharmony_ci	return 0;
24018c2ecf20Sopenharmony_ci
24028c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES
24038c2ecf20Sopenharmony_ciout_fib6_main_tbl:
24048c2ecf20Sopenharmony_ci	kfree(net->ipv6.fib6_main_tbl);
24058c2ecf20Sopenharmony_ci#endif
24068c2ecf20Sopenharmony_ciout_fib_table_hash:
24078c2ecf20Sopenharmony_ci	kfree(net->ipv6.fib_table_hash);
24088c2ecf20Sopenharmony_ciout_rt6_stats:
24098c2ecf20Sopenharmony_ci	kfree(net->ipv6.rt6_stats);
24108c2ecf20Sopenharmony_ciout_timer:
24118c2ecf20Sopenharmony_ci	fib6_notifier_exit(net);
24128c2ecf20Sopenharmony_ci	return -ENOMEM;
24138c2ecf20Sopenharmony_ci}
24148c2ecf20Sopenharmony_ci
24158c2ecf20Sopenharmony_cistatic void fib6_net_exit(struct net *net)
24168c2ecf20Sopenharmony_ci{
24178c2ecf20Sopenharmony_ci	unsigned int i;
24188c2ecf20Sopenharmony_ci
24198c2ecf20Sopenharmony_ci	del_timer_sync(&net->ipv6.ip6_fib_timer);
24208c2ecf20Sopenharmony_ci
24218c2ecf20Sopenharmony_ci	for (i = 0; i < FIB6_TABLE_HASHSZ; i++) {
24228c2ecf20Sopenharmony_ci		struct hlist_head *head = &net->ipv6.fib_table_hash[i];
24238c2ecf20Sopenharmony_ci		struct hlist_node *tmp;
24248c2ecf20Sopenharmony_ci		struct fib6_table *tb;
24258c2ecf20Sopenharmony_ci
24268c2ecf20Sopenharmony_ci		hlist_for_each_entry_safe(tb, tmp, head, tb6_hlist) {
24278c2ecf20Sopenharmony_ci			hlist_del(&tb->tb6_hlist);
24288c2ecf20Sopenharmony_ci			fib6_free_table(tb);
24298c2ecf20Sopenharmony_ci		}
24308c2ecf20Sopenharmony_ci	}
24318c2ecf20Sopenharmony_ci
24328c2ecf20Sopenharmony_ci	kfree(net->ipv6.fib_table_hash);
24338c2ecf20Sopenharmony_ci	kfree(net->ipv6.rt6_stats);
24348c2ecf20Sopenharmony_ci	fib6_notifier_exit(net);
24358c2ecf20Sopenharmony_ci}
24368c2ecf20Sopenharmony_ci
24378c2ecf20Sopenharmony_cistatic struct pernet_operations fib6_net_ops = {
24388c2ecf20Sopenharmony_ci	.init = fib6_net_init,
24398c2ecf20Sopenharmony_ci	.exit = fib6_net_exit,
24408c2ecf20Sopenharmony_ci};
24418c2ecf20Sopenharmony_ci
24428c2ecf20Sopenharmony_ciint __init fib6_init(void)
24438c2ecf20Sopenharmony_ci{
24448c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
24458c2ecf20Sopenharmony_ci
24468c2ecf20Sopenharmony_ci	fib6_node_kmem = kmem_cache_create("fib6_nodes",
24478c2ecf20Sopenharmony_ci					   sizeof(struct fib6_node),
24488c2ecf20Sopenharmony_ci					   0, SLAB_HWCACHE_ALIGN,
24498c2ecf20Sopenharmony_ci					   NULL);
24508c2ecf20Sopenharmony_ci	if (!fib6_node_kmem)
24518c2ecf20Sopenharmony_ci		goto out;
24528c2ecf20Sopenharmony_ci
24538c2ecf20Sopenharmony_ci	ret = register_pernet_subsys(&fib6_net_ops);
24548c2ecf20Sopenharmony_ci	if (ret)
24558c2ecf20Sopenharmony_ci		goto out_kmem_cache_create;
24568c2ecf20Sopenharmony_ci
24578c2ecf20Sopenharmony_ci	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE, NULL,
24588c2ecf20Sopenharmony_ci				   inet6_dump_fib, 0);
24598c2ecf20Sopenharmony_ci	if (ret)
24608c2ecf20Sopenharmony_ci		goto out_unregister_subsys;
24618c2ecf20Sopenharmony_ci
24628c2ecf20Sopenharmony_ci	__fib6_flush_trees = fib6_flush_trees;
24638c2ecf20Sopenharmony_ciout:
24648c2ecf20Sopenharmony_ci	return ret;
24658c2ecf20Sopenharmony_ci
24668c2ecf20Sopenharmony_ciout_unregister_subsys:
24678c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&fib6_net_ops);
24688c2ecf20Sopenharmony_ciout_kmem_cache_create:
24698c2ecf20Sopenharmony_ci	kmem_cache_destroy(fib6_node_kmem);
24708c2ecf20Sopenharmony_ci	goto out;
24718c2ecf20Sopenharmony_ci}
24728c2ecf20Sopenharmony_ci
24738c2ecf20Sopenharmony_civoid fib6_gc_cleanup(void)
24748c2ecf20Sopenharmony_ci{
24758c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&fib6_net_ops);
24768c2ecf20Sopenharmony_ci	kmem_cache_destroy(fib6_node_kmem);
24778c2ecf20Sopenharmony_ci}
24788c2ecf20Sopenharmony_ci
24798c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS
24808c2ecf20Sopenharmony_cistatic int ipv6_route_native_seq_show(struct seq_file *seq, void *v)
24818c2ecf20Sopenharmony_ci{
24828c2ecf20Sopenharmony_ci	struct fib6_info *rt = v;
24838c2ecf20Sopenharmony_ci	struct ipv6_route_iter *iter = seq->private;
24848c2ecf20Sopenharmony_ci	struct fib6_nh *fib6_nh = rt->fib6_nh;
24858c2ecf20Sopenharmony_ci	unsigned int flags = rt->fib6_flags;
24868c2ecf20Sopenharmony_ci	const struct net_device *dev;
24878c2ecf20Sopenharmony_ci
24888c2ecf20Sopenharmony_ci	if (rt->nh)
24898c2ecf20Sopenharmony_ci		fib6_nh = nexthop_fib6_nh_bh(rt->nh);
24908c2ecf20Sopenharmony_ci
24918c2ecf20Sopenharmony_ci	seq_printf(seq, "%pi6 %02x ", &rt->fib6_dst.addr, rt->fib6_dst.plen);
24928c2ecf20Sopenharmony_ci
24938c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
24948c2ecf20Sopenharmony_ci	seq_printf(seq, "%pi6 %02x ", &rt->fib6_src.addr, rt->fib6_src.plen);
24958c2ecf20Sopenharmony_ci#else
24968c2ecf20Sopenharmony_ci	seq_puts(seq, "00000000000000000000000000000000 00 ");
24978c2ecf20Sopenharmony_ci#endif
24988c2ecf20Sopenharmony_ci	if (fib6_nh->fib_nh_gw_family) {
24998c2ecf20Sopenharmony_ci		flags |= RTF_GATEWAY;
25008c2ecf20Sopenharmony_ci		seq_printf(seq, "%pi6", &fib6_nh->fib_nh_gw6);
25018c2ecf20Sopenharmony_ci	} else {
25028c2ecf20Sopenharmony_ci		seq_puts(seq, "00000000000000000000000000000000");
25038c2ecf20Sopenharmony_ci	}
25048c2ecf20Sopenharmony_ci
25058c2ecf20Sopenharmony_ci	dev = fib6_nh->fib_nh_dev;
25068c2ecf20Sopenharmony_ci	seq_printf(seq, " %08x %08x %08x %08x %8s\n",
25078c2ecf20Sopenharmony_ci		   rt->fib6_metric, refcount_read(&rt->fib6_ref), 0,
25088c2ecf20Sopenharmony_ci		   flags, dev ? dev->name : "");
25098c2ecf20Sopenharmony_ci	iter->w.leaf = NULL;
25108c2ecf20Sopenharmony_ci	return 0;
25118c2ecf20Sopenharmony_ci}
25128c2ecf20Sopenharmony_ci
25138c2ecf20Sopenharmony_cistatic int ipv6_route_yield(struct fib6_walker *w)
25148c2ecf20Sopenharmony_ci{
25158c2ecf20Sopenharmony_ci	struct ipv6_route_iter *iter = w->args;
25168c2ecf20Sopenharmony_ci
25178c2ecf20Sopenharmony_ci	if (!iter->skip)
25188c2ecf20Sopenharmony_ci		return 1;
25198c2ecf20Sopenharmony_ci
25208c2ecf20Sopenharmony_ci	do {
25218c2ecf20Sopenharmony_ci		iter->w.leaf = rcu_dereference_protected(
25228c2ecf20Sopenharmony_ci				iter->w.leaf->fib6_next,
25238c2ecf20Sopenharmony_ci				lockdep_is_held(&iter->tbl->tb6_lock));
25248c2ecf20Sopenharmony_ci		iter->skip--;
25258c2ecf20Sopenharmony_ci		if (!iter->skip && iter->w.leaf)
25268c2ecf20Sopenharmony_ci			return 1;
25278c2ecf20Sopenharmony_ci	} while (iter->w.leaf);
25288c2ecf20Sopenharmony_ci
25298c2ecf20Sopenharmony_ci	return 0;
25308c2ecf20Sopenharmony_ci}
25318c2ecf20Sopenharmony_ci
25328c2ecf20Sopenharmony_cistatic void ipv6_route_seq_setup_walk(struct ipv6_route_iter *iter,
25338c2ecf20Sopenharmony_ci				      struct net *net)
25348c2ecf20Sopenharmony_ci{
25358c2ecf20Sopenharmony_ci	memset(&iter->w, 0, sizeof(iter->w));
25368c2ecf20Sopenharmony_ci	iter->w.func = ipv6_route_yield;
25378c2ecf20Sopenharmony_ci	iter->w.root = &iter->tbl->tb6_root;
25388c2ecf20Sopenharmony_ci	iter->w.state = FWS_INIT;
25398c2ecf20Sopenharmony_ci	iter->w.node = iter->w.root;
25408c2ecf20Sopenharmony_ci	iter->w.args = iter;
25418c2ecf20Sopenharmony_ci	iter->sernum = READ_ONCE(iter->w.root->fn_sernum);
25428c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&iter->w.lh);
25438c2ecf20Sopenharmony_ci	fib6_walker_link(net, &iter->w);
25448c2ecf20Sopenharmony_ci}
25458c2ecf20Sopenharmony_ci
25468c2ecf20Sopenharmony_cistatic struct fib6_table *ipv6_route_seq_next_table(struct fib6_table *tbl,
25478c2ecf20Sopenharmony_ci						    struct net *net)
25488c2ecf20Sopenharmony_ci{
25498c2ecf20Sopenharmony_ci	unsigned int h;
25508c2ecf20Sopenharmony_ci	struct hlist_node *node;
25518c2ecf20Sopenharmony_ci
25528c2ecf20Sopenharmony_ci	if (tbl) {
25538c2ecf20Sopenharmony_ci		h = (tbl->tb6_id & (FIB6_TABLE_HASHSZ - 1)) + 1;
25548c2ecf20Sopenharmony_ci		node = rcu_dereference_bh(hlist_next_rcu(&tbl->tb6_hlist));
25558c2ecf20Sopenharmony_ci	} else {
25568c2ecf20Sopenharmony_ci		h = 0;
25578c2ecf20Sopenharmony_ci		node = NULL;
25588c2ecf20Sopenharmony_ci	}
25598c2ecf20Sopenharmony_ci
25608c2ecf20Sopenharmony_ci	while (!node && h < FIB6_TABLE_HASHSZ) {
25618c2ecf20Sopenharmony_ci		node = rcu_dereference_bh(
25628c2ecf20Sopenharmony_ci			hlist_first_rcu(&net->ipv6.fib_table_hash[h++]));
25638c2ecf20Sopenharmony_ci	}
25648c2ecf20Sopenharmony_ci	return hlist_entry_safe(node, struct fib6_table, tb6_hlist);
25658c2ecf20Sopenharmony_ci}
25668c2ecf20Sopenharmony_ci
25678c2ecf20Sopenharmony_cistatic void ipv6_route_check_sernum(struct ipv6_route_iter *iter)
25688c2ecf20Sopenharmony_ci{
25698c2ecf20Sopenharmony_ci	int sernum = READ_ONCE(iter->w.root->fn_sernum);
25708c2ecf20Sopenharmony_ci
25718c2ecf20Sopenharmony_ci	if (iter->sernum != sernum) {
25728c2ecf20Sopenharmony_ci		iter->sernum = sernum;
25738c2ecf20Sopenharmony_ci		iter->w.state = FWS_INIT;
25748c2ecf20Sopenharmony_ci		iter->w.node = iter->w.root;
25758c2ecf20Sopenharmony_ci		WARN_ON(iter->w.skip);
25768c2ecf20Sopenharmony_ci		iter->w.skip = iter->w.count;
25778c2ecf20Sopenharmony_ci	}
25788c2ecf20Sopenharmony_ci}
25798c2ecf20Sopenharmony_ci
25808c2ecf20Sopenharmony_cistatic void *ipv6_route_seq_next(struct seq_file *seq, void *v, loff_t *pos)
25818c2ecf20Sopenharmony_ci{
25828c2ecf20Sopenharmony_ci	int r;
25838c2ecf20Sopenharmony_ci	struct fib6_info *n;
25848c2ecf20Sopenharmony_ci	struct net *net = seq_file_net(seq);
25858c2ecf20Sopenharmony_ci	struct ipv6_route_iter *iter = seq->private;
25868c2ecf20Sopenharmony_ci
25878c2ecf20Sopenharmony_ci	++(*pos);
25888c2ecf20Sopenharmony_ci	if (!v)
25898c2ecf20Sopenharmony_ci		goto iter_table;
25908c2ecf20Sopenharmony_ci
25918c2ecf20Sopenharmony_ci	n = rcu_dereference_bh(((struct fib6_info *)v)->fib6_next);
25928c2ecf20Sopenharmony_ci	if (n)
25938c2ecf20Sopenharmony_ci		return n;
25948c2ecf20Sopenharmony_ci
25958c2ecf20Sopenharmony_ciiter_table:
25968c2ecf20Sopenharmony_ci	ipv6_route_check_sernum(iter);
25978c2ecf20Sopenharmony_ci	spin_lock_bh(&iter->tbl->tb6_lock);
25988c2ecf20Sopenharmony_ci	r = fib6_walk_continue(&iter->w);
25998c2ecf20Sopenharmony_ci	spin_unlock_bh(&iter->tbl->tb6_lock);
26008c2ecf20Sopenharmony_ci	if (r > 0) {
26018c2ecf20Sopenharmony_ci		return iter->w.leaf;
26028c2ecf20Sopenharmony_ci	} else if (r < 0) {
26038c2ecf20Sopenharmony_ci		fib6_walker_unlink(net, &iter->w);
26048c2ecf20Sopenharmony_ci		return NULL;
26058c2ecf20Sopenharmony_ci	}
26068c2ecf20Sopenharmony_ci	fib6_walker_unlink(net, &iter->w);
26078c2ecf20Sopenharmony_ci
26088c2ecf20Sopenharmony_ci	iter->tbl = ipv6_route_seq_next_table(iter->tbl, net);
26098c2ecf20Sopenharmony_ci	if (!iter->tbl)
26108c2ecf20Sopenharmony_ci		return NULL;
26118c2ecf20Sopenharmony_ci
26128c2ecf20Sopenharmony_ci	ipv6_route_seq_setup_walk(iter, net);
26138c2ecf20Sopenharmony_ci	goto iter_table;
26148c2ecf20Sopenharmony_ci}
26158c2ecf20Sopenharmony_ci
26168c2ecf20Sopenharmony_cistatic void *ipv6_route_seq_start(struct seq_file *seq, loff_t *pos)
26178c2ecf20Sopenharmony_ci	__acquires(RCU_BH)
26188c2ecf20Sopenharmony_ci{
26198c2ecf20Sopenharmony_ci	struct net *net = seq_file_net(seq);
26208c2ecf20Sopenharmony_ci	struct ipv6_route_iter *iter = seq->private;
26218c2ecf20Sopenharmony_ci
26228c2ecf20Sopenharmony_ci	rcu_read_lock_bh();
26238c2ecf20Sopenharmony_ci	iter->tbl = ipv6_route_seq_next_table(NULL, net);
26248c2ecf20Sopenharmony_ci	iter->skip = *pos;
26258c2ecf20Sopenharmony_ci
26268c2ecf20Sopenharmony_ci	if (iter->tbl) {
26278c2ecf20Sopenharmony_ci		loff_t p = 0;
26288c2ecf20Sopenharmony_ci
26298c2ecf20Sopenharmony_ci		ipv6_route_seq_setup_walk(iter, net);
26308c2ecf20Sopenharmony_ci		return ipv6_route_seq_next(seq, NULL, &p);
26318c2ecf20Sopenharmony_ci	} else {
26328c2ecf20Sopenharmony_ci		return NULL;
26338c2ecf20Sopenharmony_ci	}
26348c2ecf20Sopenharmony_ci}
26358c2ecf20Sopenharmony_ci
26368c2ecf20Sopenharmony_cistatic bool ipv6_route_iter_active(struct ipv6_route_iter *iter)
26378c2ecf20Sopenharmony_ci{
26388c2ecf20Sopenharmony_ci	struct fib6_walker *w = &iter->w;
26398c2ecf20Sopenharmony_ci	return w->node && !(w->state == FWS_U && w->node == w->root);
26408c2ecf20Sopenharmony_ci}
26418c2ecf20Sopenharmony_ci
26428c2ecf20Sopenharmony_cistatic void ipv6_route_native_seq_stop(struct seq_file *seq, void *v)
26438c2ecf20Sopenharmony_ci	__releases(RCU_BH)
26448c2ecf20Sopenharmony_ci{
26458c2ecf20Sopenharmony_ci	struct net *net = seq_file_net(seq);
26468c2ecf20Sopenharmony_ci	struct ipv6_route_iter *iter = seq->private;
26478c2ecf20Sopenharmony_ci
26488c2ecf20Sopenharmony_ci	if (ipv6_route_iter_active(iter))
26498c2ecf20Sopenharmony_ci		fib6_walker_unlink(net, &iter->w);
26508c2ecf20Sopenharmony_ci
26518c2ecf20Sopenharmony_ci	rcu_read_unlock_bh();
26528c2ecf20Sopenharmony_ci}
26538c2ecf20Sopenharmony_ci
26548c2ecf20Sopenharmony_ci#if IS_BUILTIN(CONFIG_IPV6) && defined(CONFIG_BPF_SYSCALL)
26558c2ecf20Sopenharmony_cistatic int ipv6_route_prog_seq_show(struct bpf_prog *prog,
26568c2ecf20Sopenharmony_ci				    struct bpf_iter_meta *meta,
26578c2ecf20Sopenharmony_ci				    void *v)
26588c2ecf20Sopenharmony_ci{
26598c2ecf20Sopenharmony_ci	struct bpf_iter__ipv6_route ctx;
26608c2ecf20Sopenharmony_ci
26618c2ecf20Sopenharmony_ci	ctx.meta = meta;
26628c2ecf20Sopenharmony_ci	ctx.rt = v;
26638c2ecf20Sopenharmony_ci	return bpf_iter_run_prog(prog, &ctx);
26648c2ecf20Sopenharmony_ci}
26658c2ecf20Sopenharmony_ci
26668c2ecf20Sopenharmony_cistatic int ipv6_route_seq_show(struct seq_file *seq, void *v)
26678c2ecf20Sopenharmony_ci{
26688c2ecf20Sopenharmony_ci	struct ipv6_route_iter *iter = seq->private;
26698c2ecf20Sopenharmony_ci	struct bpf_iter_meta meta;
26708c2ecf20Sopenharmony_ci	struct bpf_prog *prog;
26718c2ecf20Sopenharmony_ci	int ret;
26728c2ecf20Sopenharmony_ci
26738c2ecf20Sopenharmony_ci	meta.seq = seq;
26748c2ecf20Sopenharmony_ci	prog = bpf_iter_get_info(&meta, false);
26758c2ecf20Sopenharmony_ci	if (!prog)
26768c2ecf20Sopenharmony_ci		return ipv6_route_native_seq_show(seq, v);
26778c2ecf20Sopenharmony_ci
26788c2ecf20Sopenharmony_ci	ret = ipv6_route_prog_seq_show(prog, &meta, v);
26798c2ecf20Sopenharmony_ci	iter->w.leaf = NULL;
26808c2ecf20Sopenharmony_ci
26818c2ecf20Sopenharmony_ci	return ret;
26828c2ecf20Sopenharmony_ci}
26838c2ecf20Sopenharmony_ci
26848c2ecf20Sopenharmony_cistatic void ipv6_route_seq_stop(struct seq_file *seq, void *v)
26858c2ecf20Sopenharmony_ci{
26868c2ecf20Sopenharmony_ci	struct bpf_iter_meta meta;
26878c2ecf20Sopenharmony_ci	struct bpf_prog *prog;
26888c2ecf20Sopenharmony_ci
26898c2ecf20Sopenharmony_ci	if (!v) {
26908c2ecf20Sopenharmony_ci		meta.seq = seq;
26918c2ecf20Sopenharmony_ci		prog = bpf_iter_get_info(&meta, true);
26928c2ecf20Sopenharmony_ci		if (prog)
26938c2ecf20Sopenharmony_ci			(void)ipv6_route_prog_seq_show(prog, &meta, v);
26948c2ecf20Sopenharmony_ci	}
26958c2ecf20Sopenharmony_ci
26968c2ecf20Sopenharmony_ci	ipv6_route_native_seq_stop(seq, v);
26978c2ecf20Sopenharmony_ci}
26988c2ecf20Sopenharmony_ci#else
26998c2ecf20Sopenharmony_cistatic int ipv6_route_seq_show(struct seq_file *seq, void *v)
27008c2ecf20Sopenharmony_ci{
27018c2ecf20Sopenharmony_ci	return ipv6_route_native_seq_show(seq, v);
27028c2ecf20Sopenharmony_ci}
27038c2ecf20Sopenharmony_ci
27048c2ecf20Sopenharmony_cistatic void ipv6_route_seq_stop(struct seq_file *seq, void *v)
27058c2ecf20Sopenharmony_ci{
27068c2ecf20Sopenharmony_ci	ipv6_route_native_seq_stop(seq, v);
27078c2ecf20Sopenharmony_ci}
27088c2ecf20Sopenharmony_ci#endif
27098c2ecf20Sopenharmony_ci
27108c2ecf20Sopenharmony_ciconst struct seq_operations ipv6_route_seq_ops = {
27118c2ecf20Sopenharmony_ci	.start	= ipv6_route_seq_start,
27128c2ecf20Sopenharmony_ci	.next	= ipv6_route_seq_next,
27138c2ecf20Sopenharmony_ci	.stop	= ipv6_route_seq_stop,
27148c2ecf20Sopenharmony_ci	.show	= ipv6_route_seq_show
27158c2ecf20Sopenharmony_ci};
27168c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_FS */
2717