162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	Linux INET6 implementation
462306a36Sopenharmony_ci *	Forwarding Information Database
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *	Authors:
762306a36Sopenharmony_ci *	Pedro Roque		<roque@di.fc.ul.pt>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *	Changes:
1062306a36Sopenharmony_ci *	Yuji SEKIYA @USAGI:	Support default route on router node;
1162306a36Sopenharmony_ci *				remove ip6_null_entry from the top of
1262306a36Sopenharmony_ci *				routing table.
1362306a36Sopenharmony_ci *	Ville Nuorvala:		Fixed routing subtrees.
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define pr_fmt(fmt) "IPv6: " fmt
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/bpf.h>
1962306a36Sopenharmony_ci#include <linux/errno.h>
2062306a36Sopenharmony_ci#include <linux/types.h>
2162306a36Sopenharmony_ci#include <linux/net.h>
2262306a36Sopenharmony_ci#include <linux/route.h>
2362306a36Sopenharmony_ci#include <linux/netdevice.h>
2462306a36Sopenharmony_ci#include <linux/in6.h>
2562306a36Sopenharmony_ci#include <linux/init.h>
2662306a36Sopenharmony_ci#include <linux/list.h>
2762306a36Sopenharmony_ci#include <linux/slab.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include <net/ip.h>
3062306a36Sopenharmony_ci#include <net/ipv6.h>
3162306a36Sopenharmony_ci#include <net/ndisc.h>
3262306a36Sopenharmony_ci#include <net/addrconf.h>
3362306a36Sopenharmony_ci#include <net/lwtunnel.h>
3462306a36Sopenharmony_ci#include <net/fib_notifier.h>
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#include <net/ip_fib.h>
3762306a36Sopenharmony_ci#include <net/ip6_fib.h>
3862306a36Sopenharmony_ci#include <net/ip6_route.h>
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic struct kmem_cache *fib6_node_kmem __read_mostly;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistruct fib6_cleaner {
4362306a36Sopenharmony_ci	struct fib6_walker w;
4462306a36Sopenharmony_ci	struct net *net;
4562306a36Sopenharmony_ci	int (*func)(struct fib6_info *, void *arg);
4662306a36Sopenharmony_ci	int sernum;
4762306a36Sopenharmony_ci	void *arg;
4862306a36Sopenharmony_ci	bool skip_notify;
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
5262306a36Sopenharmony_ci#define FWS_INIT FWS_S
5362306a36Sopenharmony_ci#else
5462306a36Sopenharmony_ci#define FWS_INIT FWS_L
5562306a36Sopenharmony_ci#endif
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic struct fib6_info *fib6_find_prefix(struct net *net,
5862306a36Sopenharmony_ci					 struct fib6_table *table,
5962306a36Sopenharmony_ci					 struct fib6_node *fn);
6062306a36Sopenharmony_cistatic struct fib6_node *fib6_repair_tree(struct net *net,
6162306a36Sopenharmony_ci					  struct fib6_table *table,
6262306a36Sopenharmony_ci					  struct fib6_node *fn);
6362306a36Sopenharmony_cistatic int fib6_walk(struct net *net, struct fib6_walker *w);
6462306a36Sopenharmony_cistatic int fib6_walk_continue(struct fib6_walker *w);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/*
6762306a36Sopenharmony_ci *	A routing update causes an increase of the serial number on the
6862306a36Sopenharmony_ci *	affected subtree. This allows for cached routes to be asynchronously
6962306a36Sopenharmony_ci *	tested when modifications are made to the destination cache as a
7062306a36Sopenharmony_ci *	result of redirects, path MTU changes, etc.
7162306a36Sopenharmony_ci */
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic void fib6_gc_timer_cb(struct timer_list *t);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#define FOR_WALKERS(net, w) \
7662306a36Sopenharmony_ci	list_for_each_entry(w, &(net)->ipv6.fib6_walkers, lh)
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void fib6_walker_link(struct net *net, struct fib6_walker *w)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	write_lock_bh(&net->ipv6.fib6_walker_lock);
8162306a36Sopenharmony_ci	list_add(&w->lh, &net->ipv6.fib6_walkers);
8262306a36Sopenharmony_ci	write_unlock_bh(&net->ipv6.fib6_walker_lock);
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic void fib6_walker_unlink(struct net *net, struct fib6_walker *w)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	write_lock_bh(&net->ipv6.fib6_walker_lock);
8862306a36Sopenharmony_ci	list_del(&w->lh);
8962306a36Sopenharmony_ci	write_unlock_bh(&net->ipv6.fib6_walker_lock);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic int fib6_new_sernum(struct net *net)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	int new, old = atomic_read(&net->ipv6.fib6_sernum);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	do {
9762306a36Sopenharmony_ci		new = old < INT_MAX ? old + 1 : 1;
9862306a36Sopenharmony_ci	} while (!atomic_try_cmpxchg(&net->ipv6.fib6_sernum, &old, new));
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	return new;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cienum {
10462306a36Sopenharmony_ci	FIB6_NO_SERNUM_CHANGE = 0,
10562306a36Sopenharmony_ci};
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_civoid fib6_update_sernum(struct net *net, struct fib6_info *f6i)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct fib6_node *fn;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	fn = rcu_dereference_protected(f6i->fib6_node,
11262306a36Sopenharmony_ci			lockdep_is_held(&f6i->fib6_table->tb6_lock));
11362306a36Sopenharmony_ci	if (fn)
11462306a36Sopenharmony_ci		WRITE_ONCE(fn->fn_sernum, fib6_new_sernum(net));
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci/*
11862306a36Sopenharmony_ci *	Auxiliary address test functions for the radix tree.
11962306a36Sopenharmony_ci *
12062306a36Sopenharmony_ci *	These assume a 32bit processor (although it will work on
12162306a36Sopenharmony_ci *	64bit processors)
12262306a36Sopenharmony_ci */
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/*
12562306a36Sopenharmony_ci *	test bit
12662306a36Sopenharmony_ci */
12762306a36Sopenharmony_ci#if defined(__LITTLE_ENDIAN)
12862306a36Sopenharmony_ci# define BITOP_BE32_SWIZZLE	(0x1F & ~7)
12962306a36Sopenharmony_ci#else
13062306a36Sopenharmony_ci# define BITOP_BE32_SWIZZLE	0
13162306a36Sopenharmony_ci#endif
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic __be32 addr_bit_set(const void *token, int fn_bit)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	const __be32 *addr = token;
13662306a36Sopenharmony_ci	/*
13762306a36Sopenharmony_ci	 * Here,
13862306a36Sopenharmony_ci	 *	1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f)
13962306a36Sopenharmony_ci	 * is optimized version of
14062306a36Sopenharmony_ci	 *	htonl(1 << ((~fn_bit)&0x1F))
14162306a36Sopenharmony_ci	 * See include/asm-generic/bitops/le.h.
14262306a36Sopenharmony_ci	 */
14362306a36Sopenharmony_ci	return (__force __be32)(1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f)) &
14462306a36Sopenharmony_ci	       addr[fn_bit >> 5];
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistruct fib6_info *fib6_info_alloc(gfp_t gfp_flags, bool with_fib6_nh)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	struct fib6_info *f6i;
15062306a36Sopenharmony_ci	size_t sz = sizeof(*f6i);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (with_fib6_nh)
15362306a36Sopenharmony_ci		sz += sizeof(struct fib6_nh);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	f6i = kzalloc(sz, gfp_flags);
15662306a36Sopenharmony_ci	if (!f6i)
15762306a36Sopenharmony_ci		return NULL;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	/* fib6_siblings is a union with nh_list, so this initializes both */
16062306a36Sopenharmony_ci	INIT_LIST_HEAD(&f6i->fib6_siblings);
16162306a36Sopenharmony_ci	refcount_set(&f6i->fib6_ref, 1);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	return f6i;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_civoid fib6_info_destroy_rcu(struct rcu_head *head)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct fib6_info *f6i = container_of(head, struct fib6_info, rcu);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	WARN_ON(f6i->fib6_node);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (f6i->nh)
17362306a36Sopenharmony_ci		nexthop_put(f6i->nh);
17462306a36Sopenharmony_ci	else
17562306a36Sopenharmony_ci		fib6_nh_release(f6i->fib6_nh);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	ip_fib_metrics_put(f6i->fib6_metrics);
17862306a36Sopenharmony_ci	kfree(f6i);
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fib6_info_destroy_rcu);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic struct fib6_node *node_alloc(struct net *net)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	struct fib6_node *fn;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	fn = kmem_cache_zalloc(fib6_node_kmem, GFP_ATOMIC);
18762306a36Sopenharmony_ci	if (fn)
18862306a36Sopenharmony_ci		net->ipv6.rt6_stats->fib_nodes++;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	return fn;
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic void node_free_immediate(struct net *net, struct fib6_node *fn)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	kmem_cache_free(fib6_node_kmem, fn);
19662306a36Sopenharmony_ci	net->ipv6.rt6_stats->fib_nodes--;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic void node_free_rcu(struct rcu_head *head)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct fib6_node *fn = container_of(head, struct fib6_node, rcu);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	kmem_cache_free(fib6_node_kmem, fn);
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic void node_free(struct net *net, struct fib6_node *fn)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	call_rcu(&fn->rcu, node_free_rcu);
20962306a36Sopenharmony_ci	net->ipv6.rt6_stats->fib_nodes--;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic void fib6_free_table(struct fib6_table *table)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	inetpeer_invalidate_tree(&table->tb6_peers);
21562306a36Sopenharmony_ci	kfree(table);
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic void fib6_link_table(struct net *net, struct fib6_table *tb)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	unsigned int h;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/*
22362306a36Sopenharmony_ci	 * Initialize table lock at a single place to give lockdep a key,
22462306a36Sopenharmony_ci	 * tables aren't visible prior to being linked to the list.
22562306a36Sopenharmony_ci	 */
22662306a36Sopenharmony_ci	spin_lock_init(&tb->tb6_lock);
22762306a36Sopenharmony_ci	h = tb->tb6_id & (FIB6_TABLE_HASHSZ - 1);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	/*
23062306a36Sopenharmony_ci	 * No protection necessary, this is the only list mutatation
23162306a36Sopenharmony_ci	 * operation, tables never disappear once they exist.
23262306a36Sopenharmony_ci	 */
23362306a36Sopenharmony_ci	hlist_add_head_rcu(&tb->tb6_hlist, &net->ipv6.fib_table_hash[h]);
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic struct fib6_table *fib6_alloc_table(struct net *net, u32 id)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	struct fib6_table *table;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	table = kzalloc(sizeof(*table), GFP_ATOMIC);
24362306a36Sopenharmony_ci	if (table) {
24462306a36Sopenharmony_ci		table->tb6_id = id;
24562306a36Sopenharmony_ci		rcu_assign_pointer(table->tb6_root.leaf,
24662306a36Sopenharmony_ci				   net->ipv6.fib6_null_entry);
24762306a36Sopenharmony_ci		table->tb6_root.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
24862306a36Sopenharmony_ci		inet_peer_base_init(&table->tb6_peers);
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	return table;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistruct fib6_table *fib6_new_table(struct net *net, u32 id)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	struct fib6_table *tb;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (id == 0)
25962306a36Sopenharmony_ci		id = RT6_TABLE_MAIN;
26062306a36Sopenharmony_ci	tb = fib6_get_table(net, id);
26162306a36Sopenharmony_ci	if (tb)
26262306a36Sopenharmony_ci		return tb;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	tb = fib6_alloc_table(net, id);
26562306a36Sopenharmony_ci	if (tb)
26662306a36Sopenharmony_ci		fib6_link_table(net, tb);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return tb;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fib6_new_table);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistruct fib6_table *fib6_get_table(struct net *net, u32 id)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct fib6_table *tb;
27562306a36Sopenharmony_ci	struct hlist_head *head;
27662306a36Sopenharmony_ci	unsigned int h;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (id == 0)
27962306a36Sopenharmony_ci		id = RT6_TABLE_MAIN;
28062306a36Sopenharmony_ci	h = id & (FIB6_TABLE_HASHSZ - 1);
28162306a36Sopenharmony_ci	rcu_read_lock();
28262306a36Sopenharmony_ci	head = &net->ipv6.fib_table_hash[h];
28362306a36Sopenharmony_ci	hlist_for_each_entry_rcu(tb, head, tb6_hlist) {
28462306a36Sopenharmony_ci		if (tb->tb6_id == id) {
28562306a36Sopenharmony_ci			rcu_read_unlock();
28662306a36Sopenharmony_ci			return tb;
28762306a36Sopenharmony_ci		}
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci	rcu_read_unlock();
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	return NULL;
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fib6_get_table);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic void __net_init fib6_tables_init(struct net *net)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	fib6_link_table(net, net->ipv6.fib6_main_tbl);
29862306a36Sopenharmony_ci	fib6_link_table(net, net->ipv6.fib6_local_tbl);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci#else
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistruct fib6_table *fib6_new_table(struct net *net, u32 id)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	return fib6_get_table(net, id);
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistruct fib6_table *fib6_get_table(struct net *net, u32 id)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	  return net->ipv6.fib6_main_tbl;
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistruct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,
31362306a36Sopenharmony_ci				   const struct sk_buff *skb,
31462306a36Sopenharmony_ci				   int flags, pol_lookup_t lookup)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	struct rt6_info *rt;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	rt = pol_lookup_func(lookup,
31962306a36Sopenharmony_ci			net, net->ipv6.fib6_main_tbl, fl6, skb, flags);
32062306a36Sopenharmony_ci	if (rt->dst.error == -EAGAIN) {
32162306a36Sopenharmony_ci		ip6_rt_put_flags(rt, flags);
32262306a36Sopenharmony_ci		rt = net->ipv6.ip6_null_entry;
32362306a36Sopenharmony_ci		if (!(flags & RT6_LOOKUP_F_DST_NOREF))
32462306a36Sopenharmony_ci			dst_hold(&rt->dst);
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	return &rt->dst;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci/* called with rcu lock held; no reference taken on fib6_info */
33162306a36Sopenharmony_ciint fib6_lookup(struct net *net, int oif, struct flowi6 *fl6,
33262306a36Sopenharmony_ci		struct fib6_result *res, int flags)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	return fib6_table_lookup(net, net->ipv6.fib6_main_tbl, oif, fl6,
33562306a36Sopenharmony_ci				 res, flags);
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic void __net_init fib6_tables_init(struct net *net)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	fib6_link_table(net, net->ipv6.fib6_main_tbl);
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci#endif
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ciunsigned int fib6_tables_seq_read(struct net *net)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	unsigned int h, fib_seq = 0;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	rcu_read_lock();
35062306a36Sopenharmony_ci	for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
35162306a36Sopenharmony_ci		struct hlist_head *head = &net->ipv6.fib_table_hash[h];
35262306a36Sopenharmony_ci		struct fib6_table *tb;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci		hlist_for_each_entry_rcu(tb, head, tb6_hlist)
35562306a36Sopenharmony_ci			fib_seq += tb->fib_seq;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci	rcu_read_unlock();
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	return fib_seq;
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic int call_fib6_entry_notifier(struct notifier_block *nb,
36362306a36Sopenharmony_ci				    enum fib_event_type event_type,
36462306a36Sopenharmony_ci				    struct fib6_info *rt,
36562306a36Sopenharmony_ci				    struct netlink_ext_ack *extack)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	struct fib6_entry_notifier_info info = {
36862306a36Sopenharmony_ci		.info.extack = extack,
36962306a36Sopenharmony_ci		.rt = rt,
37062306a36Sopenharmony_ci	};
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	return call_fib6_notifier(nb, event_type, &info.info);
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cistatic int call_fib6_multipath_entry_notifier(struct notifier_block *nb,
37662306a36Sopenharmony_ci					      enum fib_event_type event_type,
37762306a36Sopenharmony_ci					      struct fib6_info *rt,
37862306a36Sopenharmony_ci					      unsigned int nsiblings,
37962306a36Sopenharmony_ci					      struct netlink_ext_ack *extack)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	struct fib6_entry_notifier_info info = {
38262306a36Sopenharmony_ci		.info.extack = extack,
38362306a36Sopenharmony_ci		.rt = rt,
38462306a36Sopenharmony_ci		.nsiblings = nsiblings,
38562306a36Sopenharmony_ci	};
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	return call_fib6_notifier(nb, event_type, &info.info);
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ciint call_fib6_entry_notifiers(struct net *net,
39162306a36Sopenharmony_ci			      enum fib_event_type event_type,
39262306a36Sopenharmony_ci			      struct fib6_info *rt,
39362306a36Sopenharmony_ci			      struct netlink_ext_ack *extack)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	struct fib6_entry_notifier_info info = {
39662306a36Sopenharmony_ci		.info.extack = extack,
39762306a36Sopenharmony_ci		.rt = rt,
39862306a36Sopenharmony_ci	};
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	rt->fib6_table->fib_seq++;
40162306a36Sopenharmony_ci	return call_fib6_notifiers(net, event_type, &info.info);
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ciint call_fib6_multipath_entry_notifiers(struct net *net,
40562306a36Sopenharmony_ci					enum fib_event_type event_type,
40662306a36Sopenharmony_ci					struct fib6_info *rt,
40762306a36Sopenharmony_ci					unsigned int nsiblings,
40862306a36Sopenharmony_ci					struct netlink_ext_ack *extack)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	struct fib6_entry_notifier_info info = {
41162306a36Sopenharmony_ci		.info.extack = extack,
41262306a36Sopenharmony_ci		.rt = rt,
41362306a36Sopenharmony_ci		.nsiblings = nsiblings,
41462306a36Sopenharmony_ci	};
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	rt->fib6_table->fib_seq++;
41762306a36Sopenharmony_ci	return call_fib6_notifiers(net, event_type, &info.info);
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ciint call_fib6_entry_notifiers_replace(struct net *net, struct fib6_info *rt)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	struct fib6_entry_notifier_info info = {
42362306a36Sopenharmony_ci		.rt = rt,
42462306a36Sopenharmony_ci		.nsiblings = rt->fib6_nsiblings,
42562306a36Sopenharmony_ci	};
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	rt->fib6_table->fib_seq++;
42862306a36Sopenharmony_ci	return call_fib6_notifiers(net, FIB_EVENT_ENTRY_REPLACE, &info.info);
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistruct fib6_dump_arg {
43262306a36Sopenharmony_ci	struct net *net;
43362306a36Sopenharmony_ci	struct notifier_block *nb;
43462306a36Sopenharmony_ci	struct netlink_ext_ack *extack;
43562306a36Sopenharmony_ci};
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic int fib6_rt_dump(struct fib6_info *rt, struct fib6_dump_arg *arg)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	enum fib_event_type fib_event = FIB_EVENT_ENTRY_REPLACE;
44062306a36Sopenharmony_ci	int err;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (!rt || rt == arg->net->ipv6.fib6_null_entry)
44362306a36Sopenharmony_ci		return 0;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	if (rt->fib6_nsiblings)
44662306a36Sopenharmony_ci		err = call_fib6_multipath_entry_notifier(arg->nb, fib_event,
44762306a36Sopenharmony_ci							 rt,
44862306a36Sopenharmony_ci							 rt->fib6_nsiblings,
44962306a36Sopenharmony_ci							 arg->extack);
45062306a36Sopenharmony_ci	else
45162306a36Sopenharmony_ci		err = call_fib6_entry_notifier(arg->nb, fib_event, rt,
45262306a36Sopenharmony_ci					       arg->extack);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	return err;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic int fib6_node_dump(struct fib6_walker *w)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	int err;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	err = fib6_rt_dump(w->leaf, w->args);
46262306a36Sopenharmony_ci	w->leaf = NULL;
46362306a36Sopenharmony_ci	return err;
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic int fib6_table_dump(struct net *net, struct fib6_table *tb,
46762306a36Sopenharmony_ci			   struct fib6_walker *w)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	int err;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	w->root = &tb->tb6_root;
47262306a36Sopenharmony_ci	spin_lock_bh(&tb->tb6_lock);
47362306a36Sopenharmony_ci	err = fib6_walk(net, w);
47462306a36Sopenharmony_ci	spin_unlock_bh(&tb->tb6_lock);
47562306a36Sopenharmony_ci	return err;
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci/* Called with rcu_read_lock() */
47962306a36Sopenharmony_ciint fib6_tables_dump(struct net *net, struct notifier_block *nb,
48062306a36Sopenharmony_ci		     struct netlink_ext_ack *extack)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	struct fib6_dump_arg arg;
48362306a36Sopenharmony_ci	struct fib6_walker *w;
48462306a36Sopenharmony_ci	unsigned int h;
48562306a36Sopenharmony_ci	int err = 0;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	w = kzalloc(sizeof(*w), GFP_ATOMIC);
48862306a36Sopenharmony_ci	if (!w)
48962306a36Sopenharmony_ci		return -ENOMEM;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	w->func = fib6_node_dump;
49262306a36Sopenharmony_ci	arg.net = net;
49362306a36Sopenharmony_ci	arg.nb = nb;
49462306a36Sopenharmony_ci	arg.extack = extack;
49562306a36Sopenharmony_ci	w->args = &arg;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
49862306a36Sopenharmony_ci		struct hlist_head *head = &net->ipv6.fib_table_hash[h];
49962306a36Sopenharmony_ci		struct fib6_table *tb;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci		hlist_for_each_entry_rcu(tb, head, tb6_hlist) {
50262306a36Sopenharmony_ci			err = fib6_table_dump(net, tb, w);
50362306a36Sopenharmony_ci			if (err)
50462306a36Sopenharmony_ci				goto out;
50562306a36Sopenharmony_ci		}
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ciout:
50962306a36Sopenharmony_ci	kfree(w);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	/* The tree traversal function should never return a positive value. */
51262306a36Sopenharmony_ci	return err > 0 ? -EINVAL : err;
51362306a36Sopenharmony_ci}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cistatic int fib6_dump_node(struct fib6_walker *w)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	int res;
51862306a36Sopenharmony_ci	struct fib6_info *rt;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	for_each_fib6_walker_rt(w) {
52162306a36Sopenharmony_ci		res = rt6_dump_route(rt, w->args, w->skip_in_node);
52262306a36Sopenharmony_ci		if (res >= 0) {
52362306a36Sopenharmony_ci			/* Frame is full, suspend walking */
52462306a36Sopenharmony_ci			w->leaf = rt;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci			/* We'll restart from this node, so if some routes were
52762306a36Sopenharmony_ci			 * already dumped, skip them next time.
52862306a36Sopenharmony_ci			 */
52962306a36Sopenharmony_ci			w->skip_in_node += res;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci			return 1;
53262306a36Sopenharmony_ci		}
53362306a36Sopenharmony_ci		w->skip_in_node = 0;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci		/* Multipath routes are dumped in one route with the
53662306a36Sopenharmony_ci		 * RTA_MULTIPATH attribute. Jump 'rt' to point to the
53762306a36Sopenharmony_ci		 * last sibling of this route (no need to dump the
53862306a36Sopenharmony_ci		 * sibling routes again)
53962306a36Sopenharmony_ci		 */
54062306a36Sopenharmony_ci		if (rt->fib6_nsiblings)
54162306a36Sopenharmony_ci			rt = list_last_entry(&rt->fib6_siblings,
54262306a36Sopenharmony_ci					     struct fib6_info,
54362306a36Sopenharmony_ci					     fib6_siblings);
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci	w->leaf = NULL;
54662306a36Sopenharmony_ci	return 0;
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic void fib6_dump_end(struct netlink_callback *cb)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	struct net *net = sock_net(cb->skb->sk);
55262306a36Sopenharmony_ci	struct fib6_walker *w = (void *)cb->args[2];
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	if (w) {
55562306a36Sopenharmony_ci		if (cb->args[4]) {
55662306a36Sopenharmony_ci			cb->args[4] = 0;
55762306a36Sopenharmony_ci			fib6_walker_unlink(net, w);
55862306a36Sopenharmony_ci		}
55962306a36Sopenharmony_ci		cb->args[2] = 0;
56062306a36Sopenharmony_ci		kfree(w);
56162306a36Sopenharmony_ci	}
56262306a36Sopenharmony_ci	cb->done = (void *)cb->args[3];
56362306a36Sopenharmony_ci	cb->args[1] = 3;
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic int fib6_dump_done(struct netlink_callback *cb)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	fib6_dump_end(cb);
56962306a36Sopenharmony_ci	return cb->done ? cb->done(cb) : 0;
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic int fib6_dump_table(struct fib6_table *table, struct sk_buff *skb,
57362306a36Sopenharmony_ci			   struct netlink_callback *cb)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
57662306a36Sopenharmony_ci	struct fib6_walker *w;
57762306a36Sopenharmony_ci	int res;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	w = (void *)cb->args[2];
58062306a36Sopenharmony_ci	w->root = &table->tb6_root;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	if (cb->args[4] == 0) {
58362306a36Sopenharmony_ci		w->count = 0;
58462306a36Sopenharmony_ci		w->skip = 0;
58562306a36Sopenharmony_ci		w->skip_in_node = 0;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci		spin_lock_bh(&table->tb6_lock);
58862306a36Sopenharmony_ci		res = fib6_walk(net, w);
58962306a36Sopenharmony_ci		spin_unlock_bh(&table->tb6_lock);
59062306a36Sopenharmony_ci		if (res > 0) {
59162306a36Sopenharmony_ci			cb->args[4] = 1;
59262306a36Sopenharmony_ci			cb->args[5] = READ_ONCE(w->root->fn_sernum);
59362306a36Sopenharmony_ci		}
59462306a36Sopenharmony_ci	} else {
59562306a36Sopenharmony_ci		int sernum = READ_ONCE(w->root->fn_sernum);
59662306a36Sopenharmony_ci		if (cb->args[5] != sernum) {
59762306a36Sopenharmony_ci			/* Begin at the root if the tree changed */
59862306a36Sopenharmony_ci			cb->args[5] = sernum;
59962306a36Sopenharmony_ci			w->state = FWS_INIT;
60062306a36Sopenharmony_ci			w->node = w->root;
60162306a36Sopenharmony_ci			w->skip = w->count;
60262306a36Sopenharmony_ci			w->skip_in_node = 0;
60362306a36Sopenharmony_ci		} else
60462306a36Sopenharmony_ci			w->skip = 0;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci		spin_lock_bh(&table->tb6_lock);
60762306a36Sopenharmony_ci		res = fib6_walk_continue(w);
60862306a36Sopenharmony_ci		spin_unlock_bh(&table->tb6_lock);
60962306a36Sopenharmony_ci		if (res <= 0) {
61062306a36Sopenharmony_ci			fib6_walker_unlink(net, w);
61162306a36Sopenharmony_ci			cb->args[4] = 0;
61262306a36Sopenharmony_ci		}
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	return res;
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_cistatic int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
61962306a36Sopenharmony_ci{
62062306a36Sopenharmony_ci	struct rt6_rtnl_dump_arg arg = { .filter.dump_exceptions = true,
62162306a36Sopenharmony_ci					 .filter.dump_routes = true };
62262306a36Sopenharmony_ci	const struct nlmsghdr *nlh = cb->nlh;
62362306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
62462306a36Sopenharmony_ci	unsigned int h, s_h;
62562306a36Sopenharmony_ci	unsigned int e = 0, s_e;
62662306a36Sopenharmony_ci	struct fib6_walker *w;
62762306a36Sopenharmony_ci	struct fib6_table *tb;
62862306a36Sopenharmony_ci	struct hlist_head *head;
62962306a36Sopenharmony_ci	int res = 0;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	if (cb->strict_check) {
63262306a36Sopenharmony_ci		int err;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci		err = ip_valid_fib_dump_req(net, nlh, &arg.filter, cb);
63562306a36Sopenharmony_ci		if (err < 0)
63662306a36Sopenharmony_ci			return err;
63762306a36Sopenharmony_ci	} else if (nlmsg_len(nlh) >= sizeof(struct rtmsg)) {
63862306a36Sopenharmony_ci		struct rtmsg *rtm = nlmsg_data(nlh);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci		if (rtm->rtm_flags & RTM_F_PREFIX)
64162306a36Sopenharmony_ci			arg.filter.flags = RTM_F_PREFIX;
64262306a36Sopenharmony_ci	}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	w = (void *)cb->args[2];
64562306a36Sopenharmony_ci	if (!w) {
64662306a36Sopenharmony_ci		/* New dump:
64762306a36Sopenharmony_ci		 *
64862306a36Sopenharmony_ci		 * 1. hook callback destructor.
64962306a36Sopenharmony_ci		 */
65062306a36Sopenharmony_ci		cb->args[3] = (long)cb->done;
65162306a36Sopenharmony_ci		cb->done = fib6_dump_done;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci		/*
65462306a36Sopenharmony_ci		 * 2. allocate and initialize walker.
65562306a36Sopenharmony_ci		 */
65662306a36Sopenharmony_ci		w = kzalloc(sizeof(*w), GFP_ATOMIC);
65762306a36Sopenharmony_ci		if (!w)
65862306a36Sopenharmony_ci			return -ENOMEM;
65962306a36Sopenharmony_ci		w->func = fib6_dump_node;
66062306a36Sopenharmony_ci		cb->args[2] = (long)w;
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	arg.skb = skb;
66462306a36Sopenharmony_ci	arg.cb = cb;
66562306a36Sopenharmony_ci	arg.net = net;
66662306a36Sopenharmony_ci	w->args = &arg;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	if (arg.filter.table_id) {
66962306a36Sopenharmony_ci		tb = fib6_get_table(net, arg.filter.table_id);
67062306a36Sopenharmony_ci		if (!tb) {
67162306a36Sopenharmony_ci			if (rtnl_msg_family(cb->nlh) != PF_INET6)
67262306a36Sopenharmony_ci				goto out;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(cb->extack, "FIB table does not exist");
67562306a36Sopenharmony_ci			return -ENOENT;
67662306a36Sopenharmony_ci		}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci		if (!cb->args[0]) {
67962306a36Sopenharmony_ci			res = fib6_dump_table(tb, skb, cb);
68062306a36Sopenharmony_ci			if (!res)
68162306a36Sopenharmony_ci				cb->args[0] = 1;
68262306a36Sopenharmony_ci		}
68362306a36Sopenharmony_ci		goto out;
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	s_h = cb->args[0];
68762306a36Sopenharmony_ci	s_e = cb->args[1];
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	rcu_read_lock();
69062306a36Sopenharmony_ci	for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_e = 0) {
69162306a36Sopenharmony_ci		e = 0;
69262306a36Sopenharmony_ci		head = &net->ipv6.fib_table_hash[h];
69362306a36Sopenharmony_ci		hlist_for_each_entry_rcu(tb, head, tb6_hlist) {
69462306a36Sopenharmony_ci			if (e < s_e)
69562306a36Sopenharmony_ci				goto next;
69662306a36Sopenharmony_ci			res = fib6_dump_table(tb, skb, cb);
69762306a36Sopenharmony_ci			if (res != 0)
69862306a36Sopenharmony_ci				goto out_unlock;
69962306a36Sopenharmony_cinext:
70062306a36Sopenharmony_ci			e++;
70162306a36Sopenharmony_ci		}
70262306a36Sopenharmony_ci	}
70362306a36Sopenharmony_ciout_unlock:
70462306a36Sopenharmony_ci	rcu_read_unlock();
70562306a36Sopenharmony_ci	cb->args[1] = e;
70662306a36Sopenharmony_ci	cb->args[0] = h;
70762306a36Sopenharmony_ciout:
70862306a36Sopenharmony_ci	res = res < 0 ? res : skb->len;
70962306a36Sopenharmony_ci	if (res <= 0)
71062306a36Sopenharmony_ci		fib6_dump_end(cb);
71162306a36Sopenharmony_ci	return res;
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_civoid fib6_metric_set(struct fib6_info *f6i, int metric, u32 val)
71562306a36Sopenharmony_ci{
71662306a36Sopenharmony_ci	if (!f6i)
71762306a36Sopenharmony_ci		return;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	if (f6i->fib6_metrics == &dst_default_metrics) {
72062306a36Sopenharmony_ci		struct dst_metrics *p = kzalloc(sizeof(*p), GFP_ATOMIC);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci		if (!p)
72362306a36Sopenharmony_ci			return;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci		refcount_set(&p->refcnt, 1);
72662306a36Sopenharmony_ci		f6i->fib6_metrics = p;
72762306a36Sopenharmony_ci	}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	f6i->fib6_metrics->metrics[metric - 1] = val;
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci/*
73362306a36Sopenharmony_ci *	Routing Table
73462306a36Sopenharmony_ci *
73562306a36Sopenharmony_ci *	return the appropriate node for a routing tree "add" operation
73662306a36Sopenharmony_ci *	by either creating and inserting or by returning an existing
73762306a36Sopenharmony_ci *	node.
73862306a36Sopenharmony_ci */
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cistatic struct fib6_node *fib6_add_1(struct net *net,
74162306a36Sopenharmony_ci				    struct fib6_table *table,
74262306a36Sopenharmony_ci				    struct fib6_node *root,
74362306a36Sopenharmony_ci				    struct in6_addr *addr, int plen,
74462306a36Sopenharmony_ci				    int offset, int allow_create,
74562306a36Sopenharmony_ci				    int replace_required,
74662306a36Sopenharmony_ci				    struct netlink_ext_ack *extack)
74762306a36Sopenharmony_ci{
74862306a36Sopenharmony_ci	struct fib6_node *fn, *in, *ln;
74962306a36Sopenharmony_ci	struct fib6_node *pn = NULL;
75062306a36Sopenharmony_ci	struct rt6key *key;
75162306a36Sopenharmony_ci	int	bit;
75262306a36Sopenharmony_ci	__be32	dir = 0;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	RT6_TRACE("fib6_add_1\n");
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	/* insert node in tree */
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	fn = root;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	do {
76162306a36Sopenharmony_ci		struct fib6_info *leaf = rcu_dereference_protected(fn->leaf,
76262306a36Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
76362306a36Sopenharmony_ci		key = (struct rt6key *)((u8 *)leaf + offset);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci		/*
76662306a36Sopenharmony_ci		 *	Prefix match
76762306a36Sopenharmony_ci		 */
76862306a36Sopenharmony_ci		if (plen < fn->fn_bit ||
76962306a36Sopenharmony_ci		    !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit)) {
77062306a36Sopenharmony_ci			if (!allow_create) {
77162306a36Sopenharmony_ci				if (replace_required) {
77262306a36Sopenharmony_ci					NL_SET_ERR_MSG(extack,
77362306a36Sopenharmony_ci						       "Can not replace route - no match found");
77462306a36Sopenharmony_ci					pr_warn("Can't replace route, no match found\n");
77562306a36Sopenharmony_ci					return ERR_PTR(-ENOENT);
77662306a36Sopenharmony_ci				}
77762306a36Sopenharmony_ci				pr_warn("NLM_F_CREATE should be set when creating new route\n");
77862306a36Sopenharmony_ci			}
77962306a36Sopenharmony_ci			goto insert_above;
78062306a36Sopenharmony_ci		}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci		/*
78362306a36Sopenharmony_ci		 *	Exact match ?
78462306a36Sopenharmony_ci		 */
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci		if (plen == fn->fn_bit) {
78762306a36Sopenharmony_ci			/* clean up an intermediate node */
78862306a36Sopenharmony_ci			if (!(fn->fn_flags & RTN_RTINFO)) {
78962306a36Sopenharmony_ci				RCU_INIT_POINTER(fn->leaf, NULL);
79062306a36Sopenharmony_ci				fib6_info_release(leaf);
79162306a36Sopenharmony_ci			/* remove null_entry in the root node */
79262306a36Sopenharmony_ci			} else if (fn->fn_flags & RTN_TL_ROOT &&
79362306a36Sopenharmony_ci				   rcu_access_pointer(fn->leaf) ==
79462306a36Sopenharmony_ci				   net->ipv6.fib6_null_entry) {
79562306a36Sopenharmony_ci				RCU_INIT_POINTER(fn->leaf, NULL);
79662306a36Sopenharmony_ci			}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci			return fn;
79962306a36Sopenharmony_ci		}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci		/*
80262306a36Sopenharmony_ci		 *	We have more bits to go
80362306a36Sopenharmony_ci		 */
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci		/* Try to walk down on tree. */
80662306a36Sopenharmony_ci		dir = addr_bit_set(addr, fn->fn_bit);
80762306a36Sopenharmony_ci		pn = fn;
80862306a36Sopenharmony_ci		fn = dir ?
80962306a36Sopenharmony_ci		     rcu_dereference_protected(fn->right,
81062306a36Sopenharmony_ci					lockdep_is_held(&table->tb6_lock)) :
81162306a36Sopenharmony_ci		     rcu_dereference_protected(fn->left,
81262306a36Sopenharmony_ci					lockdep_is_held(&table->tb6_lock));
81362306a36Sopenharmony_ci	} while (fn);
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	if (!allow_create) {
81662306a36Sopenharmony_ci		/* We should not create new node because
81762306a36Sopenharmony_ci		 * NLM_F_REPLACE was specified without NLM_F_CREATE
81862306a36Sopenharmony_ci		 * I assume it is safe to require NLM_F_CREATE when
81962306a36Sopenharmony_ci		 * REPLACE flag is used! Later we may want to remove the
82062306a36Sopenharmony_ci		 * check for replace_required, because according
82162306a36Sopenharmony_ci		 * to netlink specification, NLM_F_CREATE
82262306a36Sopenharmony_ci		 * MUST be specified if new route is created.
82362306a36Sopenharmony_ci		 * That would keep IPv6 consistent with IPv4
82462306a36Sopenharmony_ci		 */
82562306a36Sopenharmony_ci		if (replace_required) {
82662306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack,
82762306a36Sopenharmony_ci				       "Can not replace route - no match found");
82862306a36Sopenharmony_ci			pr_warn("Can't replace route, no match found\n");
82962306a36Sopenharmony_ci			return ERR_PTR(-ENOENT);
83062306a36Sopenharmony_ci		}
83162306a36Sopenharmony_ci		pr_warn("NLM_F_CREATE should be set when creating new route\n");
83262306a36Sopenharmony_ci	}
83362306a36Sopenharmony_ci	/*
83462306a36Sopenharmony_ci	 *	We walked to the bottom of tree.
83562306a36Sopenharmony_ci	 *	Create new leaf node without children.
83662306a36Sopenharmony_ci	 */
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	ln = node_alloc(net);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	if (!ln)
84162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
84262306a36Sopenharmony_ci	ln->fn_bit = plen;
84362306a36Sopenharmony_ci	RCU_INIT_POINTER(ln->parent, pn);
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	if (dir)
84662306a36Sopenharmony_ci		rcu_assign_pointer(pn->right, ln);
84762306a36Sopenharmony_ci	else
84862306a36Sopenharmony_ci		rcu_assign_pointer(pn->left, ln);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	return ln;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ciinsert_above:
85462306a36Sopenharmony_ci	/*
85562306a36Sopenharmony_ci	 * split since we don't have a common prefix anymore or
85662306a36Sopenharmony_ci	 * we have a less significant route.
85762306a36Sopenharmony_ci	 * we've to insert an intermediate node on the list
85862306a36Sopenharmony_ci	 * this new node will point to the one we need to create
85962306a36Sopenharmony_ci	 * and the current
86062306a36Sopenharmony_ci	 */
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	pn = rcu_dereference_protected(fn->parent,
86362306a36Sopenharmony_ci				       lockdep_is_held(&table->tb6_lock));
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	/* find 1st bit in difference between the 2 addrs.
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	   See comment in __ipv6_addr_diff: bit may be an invalid value,
86862306a36Sopenharmony_ci	   but if it is >= plen, the value is ignored in any case.
86962306a36Sopenharmony_ci	 */
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	bit = __ipv6_addr_diff(addr, &key->addr, sizeof(*addr));
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	/*
87462306a36Sopenharmony_ci	 *		(intermediate)[in]
87562306a36Sopenharmony_ci	 *	          /	   \
87662306a36Sopenharmony_ci	 *	(new leaf node)[ln] (old node)[fn]
87762306a36Sopenharmony_ci	 */
87862306a36Sopenharmony_ci	if (plen > bit) {
87962306a36Sopenharmony_ci		in = node_alloc(net);
88062306a36Sopenharmony_ci		ln = node_alloc(net);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci		if (!in || !ln) {
88362306a36Sopenharmony_ci			if (in)
88462306a36Sopenharmony_ci				node_free_immediate(net, in);
88562306a36Sopenharmony_ci			if (ln)
88662306a36Sopenharmony_ci				node_free_immediate(net, ln);
88762306a36Sopenharmony_ci			return ERR_PTR(-ENOMEM);
88862306a36Sopenharmony_ci		}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci		/*
89162306a36Sopenharmony_ci		 * new intermediate node.
89262306a36Sopenharmony_ci		 * RTN_RTINFO will
89362306a36Sopenharmony_ci		 * be off since that an address that chooses one of
89462306a36Sopenharmony_ci		 * the branches would not match less specific routes
89562306a36Sopenharmony_ci		 * in the other branch
89662306a36Sopenharmony_ci		 */
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci		in->fn_bit = bit;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci		RCU_INIT_POINTER(in->parent, pn);
90162306a36Sopenharmony_ci		in->leaf = fn->leaf;
90262306a36Sopenharmony_ci		fib6_info_hold(rcu_dereference_protected(in->leaf,
90362306a36Sopenharmony_ci				lockdep_is_held(&table->tb6_lock)));
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci		/* update parent pointer */
90662306a36Sopenharmony_ci		if (dir)
90762306a36Sopenharmony_ci			rcu_assign_pointer(pn->right, in);
90862306a36Sopenharmony_ci		else
90962306a36Sopenharmony_ci			rcu_assign_pointer(pn->left, in);
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci		ln->fn_bit = plen;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci		RCU_INIT_POINTER(ln->parent, in);
91462306a36Sopenharmony_ci		rcu_assign_pointer(fn->parent, in);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci		if (addr_bit_set(addr, bit)) {
91762306a36Sopenharmony_ci			rcu_assign_pointer(in->right, ln);
91862306a36Sopenharmony_ci			rcu_assign_pointer(in->left, fn);
91962306a36Sopenharmony_ci		} else {
92062306a36Sopenharmony_ci			rcu_assign_pointer(in->left, ln);
92162306a36Sopenharmony_ci			rcu_assign_pointer(in->right, fn);
92262306a36Sopenharmony_ci		}
92362306a36Sopenharmony_ci	} else { /* plen <= bit */
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci		/*
92662306a36Sopenharmony_ci		 *		(new leaf node)[ln]
92762306a36Sopenharmony_ci		 *	          /	   \
92862306a36Sopenharmony_ci		 *	     (old node)[fn] NULL
92962306a36Sopenharmony_ci		 */
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci		ln = node_alloc(net);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci		if (!ln)
93462306a36Sopenharmony_ci			return ERR_PTR(-ENOMEM);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci		ln->fn_bit = plen;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci		RCU_INIT_POINTER(ln->parent, pn);
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci		if (addr_bit_set(&key->addr, plen))
94162306a36Sopenharmony_ci			RCU_INIT_POINTER(ln->right, fn);
94262306a36Sopenharmony_ci		else
94362306a36Sopenharmony_ci			RCU_INIT_POINTER(ln->left, fn);
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci		rcu_assign_pointer(fn->parent, ln);
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci		if (dir)
94862306a36Sopenharmony_ci			rcu_assign_pointer(pn->right, ln);
94962306a36Sopenharmony_ci		else
95062306a36Sopenharmony_ci			rcu_assign_pointer(pn->left, ln);
95162306a36Sopenharmony_ci	}
95262306a36Sopenharmony_ci	return ln;
95362306a36Sopenharmony_ci}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_cistatic void __fib6_drop_pcpu_from(struct fib6_nh *fib6_nh,
95662306a36Sopenharmony_ci				  const struct fib6_info *match,
95762306a36Sopenharmony_ci				  const struct fib6_table *table)
95862306a36Sopenharmony_ci{
95962306a36Sopenharmony_ci	int cpu;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	if (!fib6_nh->rt6i_pcpu)
96262306a36Sopenharmony_ci		return;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	/* release the reference to this fib entry from
96562306a36Sopenharmony_ci	 * all of its cached pcpu routes
96662306a36Sopenharmony_ci	 */
96762306a36Sopenharmony_ci	for_each_possible_cpu(cpu) {
96862306a36Sopenharmony_ci		struct rt6_info **ppcpu_rt;
96962306a36Sopenharmony_ci		struct rt6_info *pcpu_rt;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci		ppcpu_rt = per_cpu_ptr(fib6_nh->rt6i_pcpu, cpu);
97262306a36Sopenharmony_ci		pcpu_rt = *ppcpu_rt;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci		/* only dropping the 'from' reference if the cached route
97562306a36Sopenharmony_ci		 * is using 'match'. The cached pcpu_rt->from only changes
97662306a36Sopenharmony_ci		 * from a fib6_info to NULL (ip6_dst_destroy); it can never
97762306a36Sopenharmony_ci		 * change from one fib6_info reference to another
97862306a36Sopenharmony_ci		 */
97962306a36Sopenharmony_ci		if (pcpu_rt && rcu_access_pointer(pcpu_rt->from) == match) {
98062306a36Sopenharmony_ci			struct fib6_info *from;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci			from = xchg((__force struct fib6_info **)&pcpu_rt->from, NULL);
98362306a36Sopenharmony_ci			fib6_info_release(from);
98462306a36Sopenharmony_ci		}
98562306a36Sopenharmony_ci	}
98662306a36Sopenharmony_ci}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_cistruct fib6_nh_pcpu_arg {
98962306a36Sopenharmony_ci	struct fib6_info	*from;
99062306a36Sopenharmony_ci	const struct fib6_table *table;
99162306a36Sopenharmony_ci};
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_cistatic int fib6_nh_drop_pcpu_from(struct fib6_nh *nh, void *_arg)
99462306a36Sopenharmony_ci{
99562306a36Sopenharmony_ci	struct fib6_nh_pcpu_arg *arg = _arg;
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	__fib6_drop_pcpu_from(nh, arg->from, arg->table);
99862306a36Sopenharmony_ci	return 0;
99962306a36Sopenharmony_ci}
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_cistatic void fib6_drop_pcpu_from(struct fib6_info *f6i,
100262306a36Sopenharmony_ci				const struct fib6_table *table)
100362306a36Sopenharmony_ci{
100462306a36Sopenharmony_ci	/* Make sure rt6_make_pcpu_route() wont add other percpu routes
100562306a36Sopenharmony_ci	 * while we are cleaning them here.
100662306a36Sopenharmony_ci	 */
100762306a36Sopenharmony_ci	f6i->fib6_destroying = 1;
100862306a36Sopenharmony_ci	mb(); /* paired with the cmpxchg() in rt6_make_pcpu_route() */
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	if (f6i->nh) {
101162306a36Sopenharmony_ci		struct fib6_nh_pcpu_arg arg = {
101262306a36Sopenharmony_ci			.from = f6i,
101362306a36Sopenharmony_ci			.table = table
101462306a36Sopenharmony_ci		};
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci		nexthop_for_each_fib6_nh(f6i->nh, fib6_nh_drop_pcpu_from,
101762306a36Sopenharmony_ci					 &arg);
101862306a36Sopenharmony_ci	} else {
101962306a36Sopenharmony_ci		struct fib6_nh *fib6_nh;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci		fib6_nh = f6i->fib6_nh;
102262306a36Sopenharmony_ci		__fib6_drop_pcpu_from(fib6_nh, f6i, table);
102362306a36Sopenharmony_ci	}
102462306a36Sopenharmony_ci}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_cistatic void fib6_purge_rt(struct fib6_info *rt, struct fib6_node *fn,
102762306a36Sopenharmony_ci			  struct net *net)
102862306a36Sopenharmony_ci{
102962306a36Sopenharmony_ci	struct fib6_table *table = rt->fib6_table;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	/* Flush all cached dst in exception table */
103262306a36Sopenharmony_ci	rt6_flush_exceptions(rt);
103362306a36Sopenharmony_ci	fib6_drop_pcpu_from(rt, table);
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	if (rt->nh && !list_empty(&rt->nh_list))
103662306a36Sopenharmony_ci		list_del_init(&rt->nh_list);
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	if (refcount_read(&rt->fib6_ref) != 1) {
103962306a36Sopenharmony_ci		/* This route is used as dummy address holder in some split
104062306a36Sopenharmony_ci		 * nodes. It is not leaked, but it still holds other resources,
104162306a36Sopenharmony_ci		 * which must be released in time. So, scan ascendant nodes
104262306a36Sopenharmony_ci		 * and replace dummy references to this route with references
104362306a36Sopenharmony_ci		 * to still alive ones.
104462306a36Sopenharmony_ci		 */
104562306a36Sopenharmony_ci		while (fn) {
104662306a36Sopenharmony_ci			struct fib6_info *leaf = rcu_dereference_protected(fn->leaf,
104762306a36Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
104862306a36Sopenharmony_ci			struct fib6_info *new_leaf;
104962306a36Sopenharmony_ci			if (!(fn->fn_flags & RTN_RTINFO) && leaf == rt) {
105062306a36Sopenharmony_ci				new_leaf = fib6_find_prefix(net, table, fn);
105162306a36Sopenharmony_ci				fib6_info_hold(new_leaf);
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci				rcu_assign_pointer(fn->leaf, new_leaf);
105462306a36Sopenharmony_ci				fib6_info_release(rt);
105562306a36Sopenharmony_ci			}
105662306a36Sopenharmony_ci			fn = rcu_dereference_protected(fn->parent,
105762306a36Sopenharmony_ci				    lockdep_is_held(&table->tb6_lock));
105862306a36Sopenharmony_ci		}
105962306a36Sopenharmony_ci	}
106062306a36Sopenharmony_ci}
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci/*
106362306a36Sopenharmony_ci *	Insert routing information in a node.
106462306a36Sopenharmony_ci */
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_cistatic int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
106762306a36Sopenharmony_ci			    struct nl_info *info,
106862306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
106962306a36Sopenharmony_ci{
107062306a36Sopenharmony_ci	struct fib6_info *leaf = rcu_dereference_protected(fn->leaf,
107162306a36Sopenharmony_ci				    lockdep_is_held(&rt->fib6_table->tb6_lock));
107262306a36Sopenharmony_ci	struct fib6_info *iter = NULL;
107362306a36Sopenharmony_ci	struct fib6_info __rcu **ins;
107462306a36Sopenharmony_ci	struct fib6_info __rcu **fallback_ins = NULL;
107562306a36Sopenharmony_ci	int replace = (info->nlh &&
107662306a36Sopenharmony_ci		       (info->nlh->nlmsg_flags & NLM_F_REPLACE));
107762306a36Sopenharmony_ci	int add = (!info->nlh ||
107862306a36Sopenharmony_ci		   (info->nlh->nlmsg_flags & NLM_F_CREATE));
107962306a36Sopenharmony_ci	int found = 0;
108062306a36Sopenharmony_ci	bool rt_can_ecmp = rt6_qualify_for_ecmp(rt);
108162306a36Sopenharmony_ci	bool notify_sibling_rt = false;
108262306a36Sopenharmony_ci	u16 nlflags = NLM_F_EXCL;
108362306a36Sopenharmony_ci	int err;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	if (info->nlh && (info->nlh->nlmsg_flags & NLM_F_APPEND))
108662306a36Sopenharmony_ci		nlflags |= NLM_F_APPEND;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	ins = &fn->leaf;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	for (iter = leaf; iter;
109162306a36Sopenharmony_ci	     iter = rcu_dereference_protected(iter->fib6_next,
109262306a36Sopenharmony_ci				lockdep_is_held(&rt->fib6_table->tb6_lock))) {
109362306a36Sopenharmony_ci		/*
109462306a36Sopenharmony_ci		 *	Search for duplicates
109562306a36Sopenharmony_ci		 */
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci		if (iter->fib6_metric == rt->fib6_metric) {
109862306a36Sopenharmony_ci			/*
109962306a36Sopenharmony_ci			 *	Same priority level
110062306a36Sopenharmony_ci			 */
110162306a36Sopenharmony_ci			if (info->nlh &&
110262306a36Sopenharmony_ci			    (info->nlh->nlmsg_flags & NLM_F_EXCL))
110362306a36Sopenharmony_ci				return -EEXIST;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci			nlflags &= ~NLM_F_EXCL;
110662306a36Sopenharmony_ci			if (replace) {
110762306a36Sopenharmony_ci				if (rt_can_ecmp == rt6_qualify_for_ecmp(iter)) {
110862306a36Sopenharmony_ci					found++;
110962306a36Sopenharmony_ci					break;
111062306a36Sopenharmony_ci				}
111162306a36Sopenharmony_ci				fallback_ins = fallback_ins ?: ins;
111262306a36Sopenharmony_ci				goto next_iter;
111362306a36Sopenharmony_ci			}
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci			if (rt6_duplicate_nexthop(iter, rt)) {
111662306a36Sopenharmony_ci				if (rt->fib6_nsiblings)
111762306a36Sopenharmony_ci					rt->fib6_nsiblings = 0;
111862306a36Sopenharmony_ci				if (!(iter->fib6_flags & RTF_EXPIRES))
111962306a36Sopenharmony_ci					return -EEXIST;
112062306a36Sopenharmony_ci				if (!(rt->fib6_flags & RTF_EXPIRES))
112162306a36Sopenharmony_ci					fib6_clean_expires(iter);
112262306a36Sopenharmony_ci				else
112362306a36Sopenharmony_ci					fib6_set_expires(iter, rt->expires);
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci				if (rt->fib6_pmtu)
112662306a36Sopenharmony_ci					fib6_metric_set(iter, RTAX_MTU,
112762306a36Sopenharmony_ci							rt->fib6_pmtu);
112862306a36Sopenharmony_ci				return -EEXIST;
112962306a36Sopenharmony_ci			}
113062306a36Sopenharmony_ci			/* If we have the same destination and the same metric,
113162306a36Sopenharmony_ci			 * but not the same gateway, then the route we try to
113262306a36Sopenharmony_ci			 * add is sibling to this route, increment our counter
113362306a36Sopenharmony_ci			 * of siblings, and later we will add our route to the
113462306a36Sopenharmony_ci			 * list.
113562306a36Sopenharmony_ci			 * Only static routes (which don't have flag
113662306a36Sopenharmony_ci			 * RTF_EXPIRES) are used for ECMPv6.
113762306a36Sopenharmony_ci			 *
113862306a36Sopenharmony_ci			 * To avoid long list, we only had siblings if the
113962306a36Sopenharmony_ci			 * route have a gateway.
114062306a36Sopenharmony_ci			 */
114162306a36Sopenharmony_ci			if (rt_can_ecmp &&
114262306a36Sopenharmony_ci			    rt6_qualify_for_ecmp(iter))
114362306a36Sopenharmony_ci				rt->fib6_nsiblings++;
114462306a36Sopenharmony_ci		}
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci		if (iter->fib6_metric > rt->fib6_metric)
114762306a36Sopenharmony_ci			break;
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_cinext_iter:
115062306a36Sopenharmony_ci		ins = &iter->fib6_next;
115162306a36Sopenharmony_ci	}
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	if (fallback_ins && !found) {
115462306a36Sopenharmony_ci		/* No matching route with same ecmp-able-ness found, replace
115562306a36Sopenharmony_ci		 * first matching route
115662306a36Sopenharmony_ci		 */
115762306a36Sopenharmony_ci		ins = fallback_ins;
115862306a36Sopenharmony_ci		iter = rcu_dereference_protected(*ins,
115962306a36Sopenharmony_ci				    lockdep_is_held(&rt->fib6_table->tb6_lock));
116062306a36Sopenharmony_ci		found++;
116162306a36Sopenharmony_ci	}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	/* Reset round-robin state, if necessary */
116462306a36Sopenharmony_ci	if (ins == &fn->leaf)
116562306a36Sopenharmony_ci		fn->rr_ptr = NULL;
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	/* Link this route to others same route. */
116862306a36Sopenharmony_ci	if (rt->fib6_nsiblings) {
116962306a36Sopenharmony_ci		unsigned int fib6_nsiblings;
117062306a36Sopenharmony_ci		struct fib6_info *sibling, *temp_sibling;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci		/* Find the first route that have the same metric */
117362306a36Sopenharmony_ci		sibling = leaf;
117462306a36Sopenharmony_ci		notify_sibling_rt = true;
117562306a36Sopenharmony_ci		while (sibling) {
117662306a36Sopenharmony_ci			if (sibling->fib6_metric == rt->fib6_metric &&
117762306a36Sopenharmony_ci			    rt6_qualify_for_ecmp(sibling)) {
117862306a36Sopenharmony_ci				list_add_tail(&rt->fib6_siblings,
117962306a36Sopenharmony_ci					      &sibling->fib6_siblings);
118062306a36Sopenharmony_ci				break;
118162306a36Sopenharmony_ci			}
118262306a36Sopenharmony_ci			sibling = rcu_dereference_protected(sibling->fib6_next,
118362306a36Sopenharmony_ci				    lockdep_is_held(&rt->fib6_table->tb6_lock));
118462306a36Sopenharmony_ci			notify_sibling_rt = false;
118562306a36Sopenharmony_ci		}
118662306a36Sopenharmony_ci		/* For each sibling in the list, increment the counter of
118762306a36Sopenharmony_ci		 * siblings. BUG() if counters does not match, list of siblings
118862306a36Sopenharmony_ci		 * is broken!
118962306a36Sopenharmony_ci		 */
119062306a36Sopenharmony_ci		fib6_nsiblings = 0;
119162306a36Sopenharmony_ci		list_for_each_entry_safe(sibling, temp_sibling,
119262306a36Sopenharmony_ci					 &rt->fib6_siblings, fib6_siblings) {
119362306a36Sopenharmony_ci			sibling->fib6_nsiblings++;
119462306a36Sopenharmony_ci			BUG_ON(sibling->fib6_nsiblings != rt->fib6_nsiblings);
119562306a36Sopenharmony_ci			fib6_nsiblings++;
119662306a36Sopenharmony_ci		}
119762306a36Sopenharmony_ci		BUG_ON(fib6_nsiblings != rt->fib6_nsiblings);
119862306a36Sopenharmony_ci		rt6_multipath_rebalance(temp_sibling);
119962306a36Sopenharmony_ci	}
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	/*
120262306a36Sopenharmony_ci	 *	insert node
120362306a36Sopenharmony_ci	 */
120462306a36Sopenharmony_ci	if (!replace) {
120562306a36Sopenharmony_ci		if (!add)
120662306a36Sopenharmony_ci			pr_warn("NLM_F_CREATE should be set when creating new route\n");
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ciadd:
120962306a36Sopenharmony_ci		nlflags |= NLM_F_CREATE;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci		/* The route should only be notified if it is the first
121262306a36Sopenharmony_ci		 * route in the node or if it is added as a sibling
121362306a36Sopenharmony_ci		 * route to the first route in the node.
121462306a36Sopenharmony_ci		 */
121562306a36Sopenharmony_ci		if (!info->skip_notify_kernel &&
121662306a36Sopenharmony_ci		    (notify_sibling_rt || ins == &fn->leaf)) {
121762306a36Sopenharmony_ci			enum fib_event_type fib_event;
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci			if (notify_sibling_rt)
122062306a36Sopenharmony_ci				fib_event = FIB_EVENT_ENTRY_APPEND;
122162306a36Sopenharmony_ci			else
122262306a36Sopenharmony_ci				fib_event = FIB_EVENT_ENTRY_REPLACE;
122362306a36Sopenharmony_ci			err = call_fib6_entry_notifiers(info->nl_net,
122462306a36Sopenharmony_ci							fib_event, rt,
122562306a36Sopenharmony_ci							extack);
122662306a36Sopenharmony_ci			if (err) {
122762306a36Sopenharmony_ci				struct fib6_info *sibling, *next_sibling;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci				/* If the route has siblings, then it first
123062306a36Sopenharmony_ci				 * needs to be unlinked from them.
123162306a36Sopenharmony_ci				 */
123262306a36Sopenharmony_ci				if (!rt->fib6_nsiblings)
123362306a36Sopenharmony_ci					return err;
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci				list_for_each_entry_safe(sibling, next_sibling,
123662306a36Sopenharmony_ci							 &rt->fib6_siblings,
123762306a36Sopenharmony_ci							 fib6_siblings)
123862306a36Sopenharmony_ci					sibling->fib6_nsiblings--;
123962306a36Sopenharmony_ci				rt->fib6_nsiblings = 0;
124062306a36Sopenharmony_ci				list_del_init(&rt->fib6_siblings);
124162306a36Sopenharmony_ci				rt6_multipath_rebalance(next_sibling);
124262306a36Sopenharmony_ci				return err;
124362306a36Sopenharmony_ci			}
124462306a36Sopenharmony_ci		}
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci		rcu_assign_pointer(rt->fib6_next, iter);
124762306a36Sopenharmony_ci		fib6_info_hold(rt);
124862306a36Sopenharmony_ci		rcu_assign_pointer(rt->fib6_node, fn);
124962306a36Sopenharmony_ci		rcu_assign_pointer(*ins, rt);
125062306a36Sopenharmony_ci		if (!info->skip_notify)
125162306a36Sopenharmony_ci			inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
125262306a36Sopenharmony_ci		info->nl_net->ipv6.rt6_stats->fib_rt_entries++;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci		if (!(fn->fn_flags & RTN_RTINFO)) {
125562306a36Sopenharmony_ci			info->nl_net->ipv6.rt6_stats->fib_route_nodes++;
125662306a36Sopenharmony_ci			fn->fn_flags |= RTN_RTINFO;
125762306a36Sopenharmony_ci		}
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	} else {
126062306a36Sopenharmony_ci		int nsiblings;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci		if (!found) {
126362306a36Sopenharmony_ci			if (add)
126462306a36Sopenharmony_ci				goto add;
126562306a36Sopenharmony_ci			pr_warn("NLM_F_REPLACE set, but no existing node found!\n");
126662306a36Sopenharmony_ci			return -ENOENT;
126762306a36Sopenharmony_ci		}
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci		if (!info->skip_notify_kernel && ins == &fn->leaf) {
127062306a36Sopenharmony_ci			err = call_fib6_entry_notifiers(info->nl_net,
127162306a36Sopenharmony_ci							FIB_EVENT_ENTRY_REPLACE,
127262306a36Sopenharmony_ci							rt, extack);
127362306a36Sopenharmony_ci			if (err)
127462306a36Sopenharmony_ci				return err;
127562306a36Sopenharmony_ci		}
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci		fib6_info_hold(rt);
127862306a36Sopenharmony_ci		rcu_assign_pointer(rt->fib6_node, fn);
127962306a36Sopenharmony_ci		rt->fib6_next = iter->fib6_next;
128062306a36Sopenharmony_ci		rcu_assign_pointer(*ins, rt);
128162306a36Sopenharmony_ci		if (!info->skip_notify)
128262306a36Sopenharmony_ci			inet6_rt_notify(RTM_NEWROUTE, rt, info, NLM_F_REPLACE);
128362306a36Sopenharmony_ci		if (!(fn->fn_flags & RTN_RTINFO)) {
128462306a36Sopenharmony_ci			info->nl_net->ipv6.rt6_stats->fib_route_nodes++;
128562306a36Sopenharmony_ci			fn->fn_flags |= RTN_RTINFO;
128662306a36Sopenharmony_ci		}
128762306a36Sopenharmony_ci		nsiblings = iter->fib6_nsiblings;
128862306a36Sopenharmony_ci		iter->fib6_node = NULL;
128962306a36Sopenharmony_ci		fib6_purge_rt(iter, fn, info->nl_net);
129062306a36Sopenharmony_ci		if (rcu_access_pointer(fn->rr_ptr) == iter)
129162306a36Sopenharmony_ci			fn->rr_ptr = NULL;
129262306a36Sopenharmony_ci		fib6_info_release(iter);
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci		if (nsiblings) {
129562306a36Sopenharmony_ci			/* Replacing an ECMP route, remove all siblings */
129662306a36Sopenharmony_ci			ins = &rt->fib6_next;
129762306a36Sopenharmony_ci			iter = rcu_dereference_protected(*ins,
129862306a36Sopenharmony_ci				    lockdep_is_held(&rt->fib6_table->tb6_lock));
129962306a36Sopenharmony_ci			while (iter) {
130062306a36Sopenharmony_ci				if (iter->fib6_metric > rt->fib6_metric)
130162306a36Sopenharmony_ci					break;
130262306a36Sopenharmony_ci				if (rt6_qualify_for_ecmp(iter)) {
130362306a36Sopenharmony_ci					*ins = iter->fib6_next;
130462306a36Sopenharmony_ci					iter->fib6_node = NULL;
130562306a36Sopenharmony_ci					fib6_purge_rt(iter, fn, info->nl_net);
130662306a36Sopenharmony_ci					if (rcu_access_pointer(fn->rr_ptr) == iter)
130762306a36Sopenharmony_ci						fn->rr_ptr = NULL;
130862306a36Sopenharmony_ci					fib6_info_release(iter);
130962306a36Sopenharmony_ci					nsiblings--;
131062306a36Sopenharmony_ci					info->nl_net->ipv6.rt6_stats->fib_rt_entries--;
131162306a36Sopenharmony_ci				} else {
131262306a36Sopenharmony_ci					ins = &iter->fib6_next;
131362306a36Sopenharmony_ci				}
131462306a36Sopenharmony_ci				iter = rcu_dereference_protected(*ins,
131562306a36Sopenharmony_ci					lockdep_is_held(&rt->fib6_table->tb6_lock));
131662306a36Sopenharmony_ci			}
131762306a36Sopenharmony_ci			WARN_ON(nsiblings != 0);
131862306a36Sopenharmony_ci		}
131962306a36Sopenharmony_ci	}
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	return 0;
132262306a36Sopenharmony_ci}
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_cistatic void fib6_start_gc(struct net *net, struct fib6_info *rt)
132562306a36Sopenharmony_ci{
132662306a36Sopenharmony_ci	if (!timer_pending(&net->ipv6.ip6_fib_timer) &&
132762306a36Sopenharmony_ci	    (rt->fib6_flags & RTF_EXPIRES))
132862306a36Sopenharmony_ci		mod_timer(&net->ipv6.ip6_fib_timer,
132962306a36Sopenharmony_ci			  jiffies + net->ipv6.sysctl.ip6_rt_gc_interval);
133062306a36Sopenharmony_ci}
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_civoid fib6_force_start_gc(struct net *net)
133362306a36Sopenharmony_ci{
133462306a36Sopenharmony_ci	if (!timer_pending(&net->ipv6.ip6_fib_timer))
133562306a36Sopenharmony_ci		mod_timer(&net->ipv6.ip6_fib_timer,
133662306a36Sopenharmony_ci			  jiffies + net->ipv6.sysctl.ip6_rt_gc_interval);
133762306a36Sopenharmony_ci}
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_cistatic void __fib6_update_sernum_upto_root(struct fib6_info *rt,
134062306a36Sopenharmony_ci					   int sernum)
134162306a36Sopenharmony_ci{
134262306a36Sopenharmony_ci	struct fib6_node *fn = rcu_dereference_protected(rt->fib6_node,
134362306a36Sopenharmony_ci				lockdep_is_held(&rt->fib6_table->tb6_lock));
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	/* paired with smp_rmb() in fib6_get_cookie_safe() */
134662306a36Sopenharmony_ci	smp_wmb();
134762306a36Sopenharmony_ci	while (fn) {
134862306a36Sopenharmony_ci		WRITE_ONCE(fn->fn_sernum, sernum);
134962306a36Sopenharmony_ci		fn = rcu_dereference_protected(fn->parent,
135062306a36Sopenharmony_ci				lockdep_is_held(&rt->fib6_table->tb6_lock));
135162306a36Sopenharmony_ci	}
135262306a36Sopenharmony_ci}
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_civoid fib6_update_sernum_upto_root(struct net *net, struct fib6_info *rt)
135562306a36Sopenharmony_ci{
135662306a36Sopenharmony_ci	__fib6_update_sernum_upto_root(rt, fib6_new_sernum(net));
135762306a36Sopenharmony_ci}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci/* allow ipv4 to update sernum via ipv6_stub */
136062306a36Sopenharmony_civoid fib6_update_sernum_stub(struct net *net, struct fib6_info *f6i)
136162306a36Sopenharmony_ci{
136262306a36Sopenharmony_ci	spin_lock_bh(&f6i->fib6_table->tb6_lock);
136362306a36Sopenharmony_ci	fib6_update_sernum_upto_root(net, f6i);
136462306a36Sopenharmony_ci	spin_unlock_bh(&f6i->fib6_table->tb6_lock);
136562306a36Sopenharmony_ci}
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci/*
136862306a36Sopenharmony_ci *	Add routing information to the routing tree.
136962306a36Sopenharmony_ci *	<destination addr>/<source addr>
137062306a36Sopenharmony_ci *	with source addr info in sub-trees
137162306a36Sopenharmony_ci *	Need to own table->tb6_lock
137262306a36Sopenharmony_ci */
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ciint fib6_add(struct fib6_node *root, struct fib6_info *rt,
137562306a36Sopenharmony_ci	     struct nl_info *info, struct netlink_ext_ack *extack)
137662306a36Sopenharmony_ci{
137762306a36Sopenharmony_ci	struct fib6_table *table = rt->fib6_table;
137862306a36Sopenharmony_ci	struct fib6_node *fn, *pn = NULL;
137962306a36Sopenharmony_ci	int err = -ENOMEM;
138062306a36Sopenharmony_ci	int allow_create = 1;
138162306a36Sopenharmony_ci	int replace_required = 0;
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	if (info->nlh) {
138462306a36Sopenharmony_ci		if (!(info->nlh->nlmsg_flags & NLM_F_CREATE))
138562306a36Sopenharmony_ci			allow_create = 0;
138662306a36Sopenharmony_ci		if (info->nlh->nlmsg_flags & NLM_F_REPLACE)
138762306a36Sopenharmony_ci			replace_required = 1;
138862306a36Sopenharmony_ci	}
138962306a36Sopenharmony_ci	if (!allow_create && !replace_required)
139062306a36Sopenharmony_ci		pr_warn("RTM_NEWROUTE with no NLM_F_CREATE or NLM_F_REPLACE\n");
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	fn = fib6_add_1(info->nl_net, table, root,
139362306a36Sopenharmony_ci			&rt->fib6_dst.addr, rt->fib6_dst.plen,
139462306a36Sopenharmony_ci			offsetof(struct fib6_info, fib6_dst), allow_create,
139562306a36Sopenharmony_ci			replace_required, extack);
139662306a36Sopenharmony_ci	if (IS_ERR(fn)) {
139762306a36Sopenharmony_ci		err = PTR_ERR(fn);
139862306a36Sopenharmony_ci		fn = NULL;
139962306a36Sopenharmony_ci		goto out;
140062306a36Sopenharmony_ci	}
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	pn = fn;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
140562306a36Sopenharmony_ci	if (rt->fib6_src.plen) {
140662306a36Sopenharmony_ci		struct fib6_node *sn;
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci		if (!rcu_access_pointer(fn->subtree)) {
140962306a36Sopenharmony_ci			struct fib6_node *sfn;
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci			/*
141262306a36Sopenharmony_ci			 * Create subtree.
141362306a36Sopenharmony_ci			 *
141462306a36Sopenharmony_ci			 *		fn[main tree]
141562306a36Sopenharmony_ci			 *		|
141662306a36Sopenharmony_ci			 *		sfn[subtree root]
141762306a36Sopenharmony_ci			 *		   \
141862306a36Sopenharmony_ci			 *		    sn[new leaf node]
141962306a36Sopenharmony_ci			 */
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci			/* Create subtree root node */
142262306a36Sopenharmony_ci			sfn = node_alloc(info->nl_net);
142362306a36Sopenharmony_ci			if (!sfn)
142462306a36Sopenharmony_ci				goto failure;
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci			fib6_info_hold(info->nl_net->ipv6.fib6_null_entry);
142762306a36Sopenharmony_ci			rcu_assign_pointer(sfn->leaf,
142862306a36Sopenharmony_ci					   info->nl_net->ipv6.fib6_null_entry);
142962306a36Sopenharmony_ci			sfn->fn_flags = RTN_ROOT;
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci			/* Now add the first leaf node to new subtree */
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci			sn = fib6_add_1(info->nl_net, table, sfn,
143462306a36Sopenharmony_ci					&rt->fib6_src.addr, rt->fib6_src.plen,
143562306a36Sopenharmony_ci					offsetof(struct fib6_info, fib6_src),
143662306a36Sopenharmony_ci					allow_create, replace_required, extack);
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci			if (IS_ERR(sn)) {
143962306a36Sopenharmony_ci				/* If it is failed, discard just allocated
144062306a36Sopenharmony_ci				   root, and then (in failure) stale node
144162306a36Sopenharmony_ci				   in main tree.
144262306a36Sopenharmony_ci				 */
144362306a36Sopenharmony_ci				node_free_immediate(info->nl_net, sfn);
144462306a36Sopenharmony_ci				err = PTR_ERR(sn);
144562306a36Sopenharmony_ci				goto failure;
144662306a36Sopenharmony_ci			}
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci			/* Now link new subtree to main tree */
144962306a36Sopenharmony_ci			rcu_assign_pointer(sfn->parent, fn);
145062306a36Sopenharmony_ci			rcu_assign_pointer(fn->subtree, sfn);
145162306a36Sopenharmony_ci		} else {
145262306a36Sopenharmony_ci			sn = fib6_add_1(info->nl_net, table, FIB6_SUBTREE(fn),
145362306a36Sopenharmony_ci					&rt->fib6_src.addr, rt->fib6_src.plen,
145462306a36Sopenharmony_ci					offsetof(struct fib6_info, fib6_src),
145562306a36Sopenharmony_ci					allow_create, replace_required, extack);
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci			if (IS_ERR(sn)) {
145862306a36Sopenharmony_ci				err = PTR_ERR(sn);
145962306a36Sopenharmony_ci				goto failure;
146062306a36Sopenharmony_ci			}
146162306a36Sopenharmony_ci		}
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci		if (!rcu_access_pointer(fn->leaf)) {
146462306a36Sopenharmony_ci			if (fn->fn_flags & RTN_TL_ROOT) {
146562306a36Sopenharmony_ci				/* put back null_entry for root node */
146662306a36Sopenharmony_ci				rcu_assign_pointer(fn->leaf,
146762306a36Sopenharmony_ci					    info->nl_net->ipv6.fib6_null_entry);
146862306a36Sopenharmony_ci			} else {
146962306a36Sopenharmony_ci				fib6_info_hold(rt);
147062306a36Sopenharmony_ci				rcu_assign_pointer(fn->leaf, rt);
147162306a36Sopenharmony_ci			}
147262306a36Sopenharmony_ci		}
147362306a36Sopenharmony_ci		fn = sn;
147462306a36Sopenharmony_ci	}
147562306a36Sopenharmony_ci#endif
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	err = fib6_add_rt2node(fn, rt, info, extack);
147862306a36Sopenharmony_ci	if (!err) {
147962306a36Sopenharmony_ci		if (rt->nh)
148062306a36Sopenharmony_ci			list_add(&rt->nh_list, &rt->nh->f6i_list);
148162306a36Sopenharmony_ci		__fib6_update_sernum_upto_root(rt, fib6_new_sernum(info->nl_net));
148262306a36Sopenharmony_ci		fib6_start_gc(info->nl_net, rt);
148362306a36Sopenharmony_ci	}
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ciout:
148662306a36Sopenharmony_ci	if (err) {
148762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
148862306a36Sopenharmony_ci		/*
148962306a36Sopenharmony_ci		 * If fib6_add_1 has cleared the old leaf pointer in the
149062306a36Sopenharmony_ci		 * super-tree leaf node we have to find a new one for it.
149162306a36Sopenharmony_ci		 */
149262306a36Sopenharmony_ci		if (pn != fn) {
149362306a36Sopenharmony_ci			struct fib6_info *pn_leaf =
149462306a36Sopenharmony_ci				rcu_dereference_protected(pn->leaf,
149562306a36Sopenharmony_ci				    lockdep_is_held(&table->tb6_lock));
149662306a36Sopenharmony_ci			if (pn_leaf == rt) {
149762306a36Sopenharmony_ci				pn_leaf = NULL;
149862306a36Sopenharmony_ci				RCU_INIT_POINTER(pn->leaf, NULL);
149962306a36Sopenharmony_ci				fib6_info_release(rt);
150062306a36Sopenharmony_ci			}
150162306a36Sopenharmony_ci			if (!pn_leaf && !(pn->fn_flags & RTN_RTINFO)) {
150262306a36Sopenharmony_ci				pn_leaf = fib6_find_prefix(info->nl_net, table,
150362306a36Sopenharmony_ci							   pn);
150462306a36Sopenharmony_ci				if (!pn_leaf)
150562306a36Sopenharmony_ci					pn_leaf =
150662306a36Sopenharmony_ci					    info->nl_net->ipv6.fib6_null_entry;
150762306a36Sopenharmony_ci				fib6_info_hold(pn_leaf);
150862306a36Sopenharmony_ci				rcu_assign_pointer(pn->leaf, pn_leaf);
150962306a36Sopenharmony_ci			}
151062306a36Sopenharmony_ci		}
151162306a36Sopenharmony_ci#endif
151262306a36Sopenharmony_ci		goto failure;
151362306a36Sopenharmony_ci	} else if (fib6_requires_src(rt)) {
151462306a36Sopenharmony_ci		fib6_routes_require_src_inc(info->nl_net);
151562306a36Sopenharmony_ci	}
151662306a36Sopenharmony_ci	return err;
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_cifailure:
151962306a36Sopenharmony_ci	/* fn->leaf could be NULL and fib6_repair_tree() needs to be called if:
152062306a36Sopenharmony_ci	 * 1. fn is an intermediate node and we failed to add the new
152162306a36Sopenharmony_ci	 * route to it in both subtree creation failure and fib6_add_rt2node()
152262306a36Sopenharmony_ci	 * failure case.
152362306a36Sopenharmony_ci	 * 2. fn is the root node in the table and we fail to add the first
152462306a36Sopenharmony_ci	 * default route to it.
152562306a36Sopenharmony_ci	 */
152662306a36Sopenharmony_ci	if (fn &&
152762306a36Sopenharmony_ci	    (!(fn->fn_flags & (RTN_RTINFO|RTN_ROOT)) ||
152862306a36Sopenharmony_ci	     (fn->fn_flags & RTN_TL_ROOT &&
152962306a36Sopenharmony_ci	      !rcu_access_pointer(fn->leaf))))
153062306a36Sopenharmony_ci		fib6_repair_tree(info->nl_net, table, fn);
153162306a36Sopenharmony_ci	return err;
153262306a36Sopenharmony_ci}
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci/*
153562306a36Sopenharmony_ci *	Routing tree lookup
153662306a36Sopenharmony_ci *
153762306a36Sopenharmony_ci */
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_cistruct lookup_args {
154062306a36Sopenharmony_ci	int			offset;		/* key offset on fib6_info */
154162306a36Sopenharmony_ci	const struct in6_addr	*addr;		/* search key			*/
154262306a36Sopenharmony_ci};
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_cistatic struct fib6_node *fib6_node_lookup_1(struct fib6_node *root,
154562306a36Sopenharmony_ci					    struct lookup_args *args)
154662306a36Sopenharmony_ci{
154762306a36Sopenharmony_ci	struct fib6_node *fn;
154862306a36Sopenharmony_ci	__be32 dir;
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	if (unlikely(args->offset == 0))
155162306a36Sopenharmony_ci		return NULL;
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	/*
155462306a36Sopenharmony_ci	 *	Descend on a tree
155562306a36Sopenharmony_ci	 */
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci	fn = root;
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	for (;;) {
156062306a36Sopenharmony_ci		struct fib6_node *next;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci		dir = addr_bit_set(args->addr, fn->fn_bit);
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci		next = dir ? rcu_dereference(fn->right) :
156562306a36Sopenharmony_ci			     rcu_dereference(fn->left);
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci		if (next) {
156862306a36Sopenharmony_ci			fn = next;
156962306a36Sopenharmony_ci			continue;
157062306a36Sopenharmony_ci		}
157162306a36Sopenharmony_ci		break;
157262306a36Sopenharmony_ci	}
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci	while (fn) {
157562306a36Sopenharmony_ci		struct fib6_node *subtree = FIB6_SUBTREE(fn);
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci		if (subtree || fn->fn_flags & RTN_RTINFO) {
157862306a36Sopenharmony_ci			struct fib6_info *leaf = rcu_dereference(fn->leaf);
157962306a36Sopenharmony_ci			struct rt6key *key;
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci			if (!leaf)
158262306a36Sopenharmony_ci				goto backtrack;
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci			key = (struct rt6key *) ((u8 *)leaf + args->offset);
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci			if (ipv6_prefix_equal(&key->addr, args->addr, key->plen)) {
158762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
158862306a36Sopenharmony_ci				if (subtree) {
158962306a36Sopenharmony_ci					struct fib6_node *sfn;
159062306a36Sopenharmony_ci					sfn = fib6_node_lookup_1(subtree,
159162306a36Sopenharmony_ci								 args + 1);
159262306a36Sopenharmony_ci					if (!sfn)
159362306a36Sopenharmony_ci						goto backtrack;
159462306a36Sopenharmony_ci					fn = sfn;
159562306a36Sopenharmony_ci				}
159662306a36Sopenharmony_ci#endif
159762306a36Sopenharmony_ci				if (fn->fn_flags & RTN_RTINFO)
159862306a36Sopenharmony_ci					return fn;
159962306a36Sopenharmony_ci			}
160062306a36Sopenharmony_ci		}
160162306a36Sopenharmony_cibacktrack:
160262306a36Sopenharmony_ci		if (fn->fn_flags & RTN_ROOT)
160362306a36Sopenharmony_ci			break;
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci		fn = rcu_dereference(fn->parent);
160662306a36Sopenharmony_ci	}
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	return NULL;
160962306a36Sopenharmony_ci}
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci/* called with rcu_read_lock() held
161262306a36Sopenharmony_ci */
161362306a36Sopenharmony_cistruct fib6_node *fib6_node_lookup(struct fib6_node *root,
161462306a36Sopenharmony_ci				   const struct in6_addr *daddr,
161562306a36Sopenharmony_ci				   const struct in6_addr *saddr)
161662306a36Sopenharmony_ci{
161762306a36Sopenharmony_ci	struct fib6_node *fn;
161862306a36Sopenharmony_ci	struct lookup_args args[] = {
161962306a36Sopenharmony_ci		{
162062306a36Sopenharmony_ci			.offset = offsetof(struct fib6_info, fib6_dst),
162162306a36Sopenharmony_ci			.addr = daddr,
162262306a36Sopenharmony_ci		},
162362306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
162462306a36Sopenharmony_ci		{
162562306a36Sopenharmony_ci			.offset = offsetof(struct fib6_info, fib6_src),
162662306a36Sopenharmony_ci			.addr = saddr,
162762306a36Sopenharmony_ci		},
162862306a36Sopenharmony_ci#endif
162962306a36Sopenharmony_ci		{
163062306a36Sopenharmony_ci			.offset = 0,	/* sentinel */
163162306a36Sopenharmony_ci		}
163262306a36Sopenharmony_ci	};
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	fn = fib6_node_lookup_1(root, daddr ? args : args + 1);
163562306a36Sopenharmony_ci	if (!fn || fn->fn_flags & RTN_TL_ROOT)
163662306a36Sopenharmony_ci		fn = root;
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	return fn;
163962306a36Sopenharmony_ci}
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci/*
164262306a36Sopenharmony_ci *	Get node with specified destination prefix (and source prefix,
164362306a36Sopenharmony_ci *	if subtrees are used)
164462306a36Sopenharmony_ci *	exact_match == true means we try to find fn with exact match of
164562306a36Sopenharmony_ci *	the passed in prefix addr
164662306a36Sopenharmony_ci *	exact_match == false means we try to find fn with longest prefix
164762306a36Sopenharmony_ci *	match of the passed in prefix addr. This is useful for finding fn
164862306a36Sopenharmony_ci *	for cached route as it will be stored in the exception table under
164962306a36Sopenharmony_ci *	the node with longest prefix length.
165062306a36Sopenharmony_ci */
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_cistatic struct fib6_node *fib6_locate_1(struct fib6_node *root,
165462306a36Sopenharmony_ci				       const struct in6_addr *addr,
165562306a36Sopenharmony_ci				       int plen, int offset,
165662306a36Sopenharmony_ci				       bool exact_match)
165762306a36Sopenharmony_ci{
165862306a36Sopenharmony_ci	struct fib6_node *fn, *prev = NULL;
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	for (fn = root; fn ; ) {
166162306a36Sopenharmony_ci		struct fib6_info *leaf = rcu_dereference(fn->leaf);
166262306a36Sopenharmony_ci		struct rt6key *key;
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci		/* This node is being deleted */
166562306a36Sopenharmony_ci		if (!leaf) {
166662306a36Sopenharmony_ci			if (plen <= fn->fn_bit)
166762306a36Sopenharmony_ci				goto out;
166862306a36Sopenharmony_ci			else
166962306a36Sopenharmony_ci				goto next;
167062306a36Sopenharmony_ci		}
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci		key = (struct rt6key *)((u8 *)leaf + offset);
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci		/*
167562306a36Sopenharmony_ci		 *	Prefix match
167662306a36Sopenharmony_ci		 */
167762306a36Sopenharmony_ci		if (plen < fn->fn_bit ||
167862306a36Sopenharmony_ci		    !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit))
167962306a36Sopenharmony_ci			goto out;
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci		if (plen == fn->fn_bit)
168262306a36Sopenharmony_ci			return fn;
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci		if (fn->fn_flags & RTN_RTINFO)
168562306a36Sopenharmony_ci			prev = fn;
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_cinext:
168862306a36Sopenharmony_ci		/*
168962306a36Sopenharmony_ci		 *	We have more bits to go
169062306a36Sopenharmony_ci		 */
169162306a36Sopenharmony_ci		if (addr_bit_set(addr, fn->fn_bit))
169262306a36Sopenharmony_ci			fn = rcu_dereference(fn->right);
169362306a36Sopenharmony_ci		else
169462306a36Sopenharmony_ci			fn = rcu_dereference(fn->left);
169562306a36Sopenharmony_ci	}
169662306a36Sopenharmony_ciout:
169762306a36Sopenharmony_ci	if (exact_match)
169862306a36Sopenharmony_ci		return NULL;
169962306a36Sopenharmony_ci	else
170062306a36Sopenharmony_ci		return prev;
170162306a36Sopenharmony_ci}
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_cistruct fib6_node *fib6_locate(struct fib6_node *root,
170462306a36Sopenharmony_ci			      const struct in6_addr *daddr, int dst_len,
170562306a36Sopenharmony_ci			      const struct in6_addr *saddr, int src_len,
170662306a36Sopenharmony_ci			      bool exact_match)
170762306a36Sopenharmony_ci{
170862306a36Sopenharmony_ci	struct fib6_node *fn;
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci	fn = fib6_locate_1(root, daddr, dst_len,
171162306a36Sopenharmony_ci			   offsetof(struct fib6_info, fib6_dst),
171262306a36Sopenharmony_ci			   exact_match);
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
171562306a36Sopenharmony_ci	if (src_len) {
171662306a36Sopenharmony_ci		WARN_ON(saddr == NULL);
171762306a36Sopenharmony_ci		if (fn) {
171862306a36Sopenharmony_ci			struct fib6_node *subtree = FIB6_SUBTREE(fn);
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci			if (subtree) {
172162306a36Sopenharmony_ci				fn = fib6_locate_1(subtree, saddr, src_len,
172262306a36Sopenharmony_ci					   offsetof(struct fib6_info, fib6_src),
172362306a36Sopenharmony_ci					   exact_match);
172462306a36Sopenharmony_ci			}
172562306a36Sopenharmony_ci		}
172662306a36Sopenharmony_ci	}
172762306a36Sopenharmony_ci#endif
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	if (fn && fn->fn_flags & RTN_RTINFO)
173062306a36Sopenharmony_ci		return fn;
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci	return NULL;
173362306a36Sopenharmony_ci}
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci/*
173762306a36Sopenharmony_ci *	Deletion
173862306a36Sopenharmony_ci *
173962306a36Sopenharmony_ci */
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_cistatic struct fib6_info *fib6_find_prefix(struct net *net,
174262306a36Sopenharmony_ci					 struct fib6_table *table,
174362306a36Sopenharmony_ci					 struct fib6_node *fn)
174462306a36Sopenharmony_ci{
174562306a36Sopenharmony_ci	struct fib6_node *child_left, *child_right;
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci	if (fn->fn_flags & RTN_ROOT)
174862306a36Sopenharmony_ci		return net->ipv6.fib6_null_entry;
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	while (fn) {
175162306a36Sopenharmony_ci		child_left = rcu_dereference_protected(fn->left,
175262306a36Sopenharmony_ci				    lockdep_is_held(&table->tb6_lock));
175362306a36Sopenharmony_ci		child_right = rcu_dereference_protected(fn->right,
175462306a36Sopenharmony_ci				    lockdep_is_held(&table->tb6_lock));
175562306a36Sopenharmony_ci		if (child_left)
175662306a36Sopenharmony_ci			return rcu_dereference_protected(child_left->leaf,
175762306a36Sopenharmony_ci					lockdep_is_held(&table->tb6_lock));
175862306a36Sopenharmony_ci		if (child_right)
175962306a36Sopenharmony_ci			return rcu_dereference_protected(child_right->leaf,
176062306a36Sopenharmony_ci					lockdep_is_held(&table->tb6_lock));
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci		fn = FIB6_SUBTREE(fn);
176362306a36Sopenharmony_ci	}
176462306a36Sopenharmony_ci	return NULL;
176562306a36Sopenharmony_ci}
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci/*
176862306a36Sopenharmony_ci *	Called to trim the tree of intermediate nodes when possible. "fn"
176962306a36Sopenharmony_ci *	is the node we want to try and remove.
177062306a36Sopenharmony_ci *	Need to own table->tb6_lock
177162306a36Sopenharmony_ci */
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_cistatic struct fib6_node *fib6_repair_tree(struct net *net,
177462306a36Sopenharmony_ci					  struct fib6_table *table,
177562306a36Sopenharmony_ci					  struct fib6_node *fn)
177662306a36Sopenharmony_ci{
177762306a36Sopenharmony_ci	int children;
177862306a36Sopenharmony_ci	int nstate;
177962306a36Sopenharmony_ci	struct fib6_node *child;
178062306a36Sopenharmony_ci	struct fib6_walker *w;
178162306a36Sopenharmony_ci	int iter = 0;
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci	/* Set fn->leaf to null_entry for root node. */
178462306a36Sopenharmony_ci	if (fn->fn_flags & RTN_TL_ROOT) {
178562306a36Sopenharmony_ci		rcu_assign_pointer(fn->leaf, net->ipv6.fib6_null_entry);
178662306a36Sopenharmony_ci		return fn;
178762306a36Sopenharmony_ci	}
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci	for (;;) {
179062306a36Sopenharmony_ci		struct fib6_node *fn_r = rcu_dereference_protected(fn->right,
179162306a36Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
179262306a36Sopenharmony_ci		struct fib6_node *fn_l = rcu_dereference_protected(fn->left,
179362306a36Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
179462306a36Sopenharmony_ci		struct fib6_node *pn = rcu_dereference_protected(fn->parent,
179562306a36Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
179662306a36Sopenharmony_ci		struct fib6_node *pn_r = rcu_dereference_protected(pn->right,
179762306a36Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
179862306a36Sopenharmony_ci		struct fib6_node *pn_l = rcu_dereference_protected(pn->left,
179962306a36Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
180062306a36Sopenharmony_ci		struct fib6_info *fn_leaf = rcu_dereference_protected(fn->leaf,
180162306a36Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
180262306a36Sopenharmony_ci		struct fib6_info *pn_leaf = rcu_dereference_protected(pn->leaf,
180362306a36Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
180462306a36Sopenharmony_ci		struct fib6_info *new_fn_leaf;
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci		RT6_TRACE("fixing tree: plen=%d iter=%d\n", fn->fn_bit, iter);
180762306a36Sopenharmony_ci		iter++;
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci		WARN_ON(fn->fn_flags & RTN_RTINFO);
181062306a36Sopenharmony_ci		WARN_ON(fn->fn_flags & RTN_TL_ROOT);
181162306a36Sopenharmony_ci		WARN_ON(fn_leaf);
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci		children = 0;
181462306a36Sopenharmony_ci		child = NULL;
181562306a36Sopenharmony_ci		if (fn_r) {
181662306a36Sopenharmony_ci			child = fn_r;
181762306a36Sopenharmony_ci			children |= 1;
181862306a36Sopenharmony_ci		}
181962306a36Sopenharmony_ci		if (fn_l) {
182062306a36Sopenharmony_ci			child = fn_l;
182162306a36Sopenharmony_ci			children |= 2;
182262306a36Sopenharmony_ci		}
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci		if (children == 3 || FIB6_SUBTREE(fn)
182562306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
182662306a36Sopenharmony_ci		    /* Subtree root (i.e. fn) may have one child */
182762306a36Sopenharmony_ci		    || (children && fn->fn_flags & RTN_ROOT)
182862306a36Sopenharmony_ci#endif
182962306a36Sopenharmony_ci		    ) {
183062306a36Sopenharmony_ci			new_fn_leaf = fib6_find_prefix(net, table, fn);
183162306a36Sopenharmony_ci#if RT6_DEBUG >= 2
183262306a36Sopenharmony_ci			if (!new_fn_leaf) {
183362306a36Sopenharmony_ci				WARN_ON(!new_fn_leaf);
183462306a36Sopenharmony_ci				new_fn_leaf = net->ipv6.fib6_null_entry;
183562306a36Sopenharmony_ci			}
183662306a36Sopenharmony_ci#endif
183762306a36Sopenharmony_ci			fib6_info_hold(new_fn_leaf);
183862306a36Sopenharmony_ci			rcu_assign_pointer(fn->leaf, new_fn_leaf);
183962306a36Sopenharmony_ci			return pn;
184062306a36Sopenharmony_ci		}
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
184362306a36Sopenharmony_ci		if (FIB6_SUBTREE(pn) == fn) {
184462306a36Sopenharmony_ci			WARN_ON(!(fn->fn_flags & RTN_ROOT));
184562306a36Sopenharmony_ci			RCU_INIT_POINTER(pn->subtree, NULL);
184662306a36Sopenharmony_ci			nstate = FWS_L;
184762306a36Sopenharmony_ci		} else {
184862306a36Sopenharmony_ci			WARN_ON(fn->fn_flags & RTN_ROOT);
184962306a36Sopenharmony_ci#endif
185062306a36Sopenharmony_ci			if (pn_r == fn)
185162306a36Sopenharmony_ci				rcu_assign_pointer(pn->right, child);
185262306a36Sopenharmony_ci			else if (pn_l == fn)
185362306a36Sopenharmony_ci				rcu_assign_pointer(pn->left, child);
185462306a36Sopenharmony_ci#if RT6_DEBUG >= 2
185562306a36Sopenharmony_ci			else
185662306a36Sopenharmony_ci				WARN_ON(1);
185762306a36Sopenharmony_ci#endif
185862306a36Sopenharmony_ci			if (child)
185962306a36Sopenharmony_ci				rcu_assign_pointer(child->parent, pn);
186062306a36Sopenharmony_ci			nstate = FWS_R;
186162306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
186262306a36Sopenharmony_ci		}
186362306a36Sopenharmony_ci#endif
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci		read_lock(&net->ipv6.fib6_walker_lock);
186662306a36Sopenharmony_ci		FOR_WALKERS(net, w) {
186762306a36Sopenharmony_ci			if (!child) {
186862306a36Sopenharmony_ci				if (w->node == fn) {
186962306a36Sopenharmony_ci					RT6_TRACE("W %p adjusted by delnode 1, s=%d/%d\n", w, w->state, nstate);
187062306a36Sopenharmony_ci					w->node = pn;
187162306a36Sopenharmony_ci					w->state = nstate;
187262306a36Sopenharmony_ci				}
187362306a36Sopenharmony_ci			} else {
187462306a36Sopenharmony_ci				if (w->node == fn) {
187562306a36Sopenharmony_ci					w->node = child;
187662306a36Sopenharmony_ci					if (children&2) {
187762306a36Sopenharmony_ci						RT6_TRACE("W %p adjusted by delnode 2, s=%d\n", w, w->state);
187862306a36Sopenharmony_ci						w->state = w->state >= FWS_R ? FWS_U : FWS_INIT;
187962306a36Sopenharmony_ci					} else {
188062306a36Sopenharmony_ci						RT6_TRACE("W %p adjusted by delnode 2, s=%d\n", w, w->state);
188162306a36Sopenharmony_ci						w->state = w->state >= FWS_C ? FWS_U : FWS_INIT;
188262306a36Sopenharmony_ci					}
188362306a36Sopenharmony_ci				}
188462306a36Sopenharmony_ci			}
188562306a36Sopenharmony_ci		}
188662306a36Sopenharmony_ci		read_unlock(&net->ipv6.fib6_walker_lock);
188762306a36Sopenharmony_ci
188862306a36Sopenharmony_ci		node_free(net, fn);
188962306a36Sopenharmony_ci		if (pn->fn_flags & RTN_RTINFO || FIB6_SUBTREE(pn))
189062306a36Sopenharmony_ci			return pn;
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci		RCU_INIT_POINTER(pn->leaf, NULL);
189362306a36Sopenharmony_ci		fib6_info_release(pn_leaf);
189462306a36Sopenharmony_ci		fn = pn;
189562306a36Sopenharmony_ci	}
189662306a36Sopenharmony_ci}
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_cistatic void fib6_del_route(struct fib6_table *table, struct fib6_node *fn,
189962306a36Sopenharmony_ci			   struct fib6_info __rcu **rtp, struct nl_info *info)
190062306a36Sopenharmony_ci{
190162306a36Sopenharmony_ci	struct fib6_info *leaf, *replace_rt = NULL;
190262306a36Sopenharmony_ci	struct fib6_walker *w;
190362306a36Sopenharmony_ci	struct fib6_info *rt = rcu_dereference_protected(*rtp,
190462306a36Sopenharmony_ci				    lockdep_is_held(&table->tb6_lock));
190562306a36Sopenharmony_ci	struct net *net = info->nl_net;
190662306a36Sopenharmony_ci	bool notify_del = false;
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci	RT6_TRACE("fib6_del_route\n");
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	/* If the deleted route is the first in the node and it is not part of
191162306a36Sopenharmony_ci	 * a multipath route, then we need to replace it with the next route
191262306a36Sopenharmony_ci	 * in the node, if exists.
191362306a36Sopenharmony_ci	 */
191462306a36Sopenharmony_ci	leaf = rcu_dereference_protected(fn->leaf,
191562306a36Sopenharmony_ci					 lockdep_is_held(&table->tb6_lock));
191662306a36Sopenharmony_ci	if (leaf == rt && !rt->fib6_nsiblings) {
191762306a36Sopenharmony_ci		if (rcu_access_pointer(rt->fib6_next))
191862306a36Sopenharmony_ci			replace_rt = rcu_dereference_protected(rt->fib6_next,
191962306a36Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
192062306a36Sopenharmony_ci		else
192162306a36Sopenharmony_ci			notify_del = true;
192262306a36Sopenharmony_ci	}
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_ci	/* Unlink it */
192562306a36Sopenharmony_ci	*rtp = rt->fib6_next;
192662306a36Sopenharmony_ci	rt->fib6_node = NULL;
192762306a36Sopenharmony_ci	net->ipv6.rt6_stats->fib_rt_entries--;
192862306a36Sopenharmony_ci	net->ipv6.rt6_stats->fib_discarded_routes++;
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	/* Reset round-robin state, if necessary */
193162306a36Sopenharmony_ci	if (rcu_access_pointer(fn->rr_ptr) == rt)
193262306a36Sopenharmony_ci		fn->rr_ptr = NULL;
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci	/* Remove this entry from other siblings */
193562306a36Sopenharmony_ci	if (rt->fib6_nsiblings) {
193662306a36Sopenharmony_ci		struct fib6_info *sibling, *next_sibling;
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ci		/* The route is deleted from a multipath route. If this
193962306a36Sopenharmony_ci		 * multipath route is the first route in the node, then we need
194062306a36Sopenharmony_ci		 * to emit a delete notification. Otherwise, we need to skip
194162306a36Sopenharmony_ci		 * the notification.
194262306a36Sopenharmony_ci		 */
194362306a36Sopenharmony_ci		if (rt->fib6_metric == leaf->fib6_metric &&
194462306a36Sopenharmony_ci		    rt6_qualify_for_ecmp(leaf))
194562306a36Sopenharmony_ci			notify_del = true;
194662306a36Sopenharmony_ci		list_for_each_entry_safe(sibling, next_sibling,
194762306a36Sopenharmony_ci					 &rt->fib6_siblings, fib6_siblings)
194862306a36Sopenharmony_ci			sibling->fib6_nsiblings--;
194962306a36Sopenharmony_ci		rt->fib6_nsiblings = 0;
195062306a36Sopenharmony_ci		list_del_init(&rt->fib6_siblings);
195162306a36Sopenharmony_ci		rt6_multipath_rebalance(next_sibling);
195262306a36Sopenharmony_ci	}
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci	/* Adjust walkers */
195562306a36Sopenharmony_ci	read_lock(&net->ipv6.fib6_walker_lock);
195662306a36Sopenharmony_ci	FOR_WALKERS(net, w) {
195762306a36Sopenharmony_ci		if (w->state == FWS_C && w->leaf == rt) {
195862306a36Sopenharmony_ci			RT6_TRACE("walker %p adjusted by delroute\n", w);
195962306a36Sopenharmony_ci			w->leaf = rcu_dereference_protected(rt->fib6_next,
196062306a36Sopenharmony_ci					    lockdep_is_held(&table->tb6_lock));
196162306a36Sopenharmony_ci			if (!w->leaf)
196262306a36Sopenharmony_ci				w->state = FWS_U;
196362306a36Sopenharmony_ci		}
196462306a36Sopenharmony_ci	}
196562306a36Sopenharmony_ci	read_unlock(&net->ipv6.fib6_walker_lock);
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci	/* If it was last route, call fib6_repair_tree() to:
196862306a36Sopenharmony_ci	 * 1. For root node, put back null_entry as how the table was created.
196962306a36Sopenharmony_ci	 * 2. For other nodes, expunge its radix tree node.
197062306a36Sopenharmony_ci	 */
197162306a36Sopenharmony_ci	if (!rcu_access_pointer(fn->leaf)) {
197262306a36Sopenharmony_ci		if (!(fn->fn_flags & RTN_TL_ROOT)) {
197362306a36Sopenharmony_ci			fn->fn_flags &= ~RTN_RTINFO;
197462306a36Sopenharmony_ci			net->ipv6.rt6_stats->fib_route_nodes--;
197562306a36Sopenharmony_ci		}
197662306a36Sopenharmony_ci		fn = fib6_repair_tree(net, table, fn);
197762306a36Sopenharmony_ci	}
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci	fib6_purge_rt(rt, fn, net);
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	if (!info->skip_notify_kernel) {
198262306a36Sopenharmony_ci		if (notify_del)
198362306a36Sopenharmony_ci			call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_DEL,
198462306a36Sopenharmony_ci						  rt, NULL);
198562306a36Sopenharmony_ci		else if (replace_rt)
198662306a36Sopenharmony_ci			call_fib6_entry_notifiers_replace(net, replace_rt);
198762306a36Sopenharmony_ci	}
198862306a36Sopenharmony_ci	if (!info->skip_notify)
198962306a36Sopenharmony_ci		inet6_rt_notify(RTM_DELROUTE, rt, info, 0);
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci	fib6_info_release(rt);
199262306a36Sopenharmony_ci}
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci/* Need to own table->tb6_lock */
199562306a36Sopenharmony_ciint fib6_del(struct fib6_info *rt, struct nl_info *info)
199662306a36Sopenharmony_ci{
199762306a36Sopenharmony_ci	struct net *net = info->nl_net;
199862306a36Sopenharmony_ci	struct fib6_info __rcu **rtp;
199962306a36Sopenharmony_ci	struct fib6_info __rcu **rtp_next;
200062306a36Sopenharmony_ci	struct fib6_table *table;
200162306a36Sopenharmony_ci	struct fib6_node *fn;
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_ci	if (rt == net->ipv6.fib6_null_entry)
200462306a36Sopenharmony_ci		return -ENOENT;
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_ci	table = rt->fib6_table;
200762306a36Sopenharmony_ci	fn = rcu_dereference_protected(rt->fib6_node,
200862306a36Sopenharmony_ci				       lockdep_is_held(&table->tb6_lock));
200962306a36Sopenharmony_ci	if (!fn)
201062306a36Sopenharmony_ci		return -ENOENT;
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_ci	WARN_ON(!(fn->fn_flags & RTN_RTINFO));
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci	/*
201562306a36Sopenharmony_ci	 *	Walk the leaf entries looking for ourself
201662306a36Sopenharmony_ci	 */
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci	for (rtp = &fn->leaf; *rtp; rtp = rtp_next) {
201962306a36Sopenharmony_ci		struct fib6_info *cur = rcu_dereference_protected(*rtp,
202062306a36Sopenharmony_ci					lockdep_is_held(&table->tb6_lock));
202162306a36Sopenharmony_ci		if (rt == cur) {
202262306a36Sopenharmony_ci			if (fib6_requires_src(cur))
202362306a36Sopenharmony_ci				fib6_routes_require_src_dec(info->nl_net);
202462306a36Sopenharmony_ci			fib6_del_route(table, fn, rtp, info);
202562306a36Sopenharmony_ci			return 0;
202662306a36Sopenharmony_ci		}
202762306a36Sopenharmony_ci		rtp_next = &cur->fib6_next;
202862306a36Sopenharmony_ci	}
202962306a36Sopenharmony_ci	return -ENOENT;
203062306a36Sopenharmony_ci}
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci/*
203362306a36Sopenharmony_ci *	Tree traversal function.
203462306a36Sopenharmony_ci *
203562306a36Sopenharmony_ci *	Certainly, it is not interrupt safe.
203662306a36Sopenharmony_ci *	However, it is internally reenterable wrt itself and fib6_add/fib6_del.
203762306a36Sopenharmony_ci *	It means, that we can modify tree during walking
203862306a36Sopenharmony_ci *	and use this function for garbage collection, clone pruning,
203962306a36Sopenharmony_ci *	cleaning tree when a device goes down etc. etc.
204062306a36Sopenharmony_ci *
204162306a36Sopenharmony_ci *	It guarantees that every node will be traversed,
204262306a36Sopenharmony_ci *	and that it will be traversed only once.
204362306a36Sopenharmony_ci *
204462306a36Sopenharmony_ci *	Callback function w->func may return:
204562306a36Sopenharmony_ci *	0 -> continue walking.
204662306a36Sopenharmony_ci *	positive value -> walking is suspended (used by tree dumps,
204762306a36Sopenharmony_ci *	and probably by gc, if it will be split to several slices)
204862306a36Sopenharmony_ci *	negative value -> terminate walking.
204962306a36Sopenharmony_ci *
205062306a36Sopenharmony_ci *	The function itself returns:
205162306a36Sopenharmony_ci *	0   -> walk is complete.
205262306a36Sopenharmony_ci *	>0  -> walk is incomplete (i.e. suspended)
205362306a36Sopenharmony_ci *	<0  -> walk is terminated by an error.
205462306a36Sopenharmony_ci *
205562306a36Sopenharmony_ci *	This function is called with tb6_lock held.
205662306a36Sopenharmony_ci */
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_cistatic int fib6_walk_continue(struct fib6_walker *w)
205962306a36Sopenharmony_ci{
206062306a36Sopenharmony_ci	struct fib6_node *fn, *pn, *left, *right;
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci	/* w->root should always be table->tb6_root */
206362306a36Sopenharmony_ci	WARN_ON_ONCE(!(w->root->fn_flags & RTN_TL_ROOT));
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci	for (;;) {
206662306a36Sopenharmony_ci		fn = w->node;
206762306a36Sopenharmony_ci		if (!fn)
206862306a36Sopenharmony_ci			return 0;
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci		switch (w->state) {
207162306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
207262306a36Sopenharmony_ci		case FWS_S:
207362306a36Sopenharmony_ci			if (FIB6_SUBTREE(fn)) {
207462306a36Sopenharmony_ci				w->node = FIB6_SUBTREE(fn);
207562306a36Sopenharmony_ci				continue;
207662306a36Sopenharmony_ci			}
207762306a36Sopenharmony_ci			w->state = FWS_L;
207862306a36Sopenharmony_ci			fallthrough;
207962306a36Sopenharmony_ci#endif
208062306a36Sopenharmony_ci		case FWS_L:
208162306a36Sopenharmony_ci			left = rcu_dereference_protected(fn->left, 1);
208262306a36Sopenharmony_ci			if (left) {
208362306a36Sopenharmony_ci				w->node = left;
208462306a36Sopenharmony_ci				w->state = FWS_INIT;
208562306a36Sopenharmony_ci				continue;
208662306a36Sopenharmony_ci			}
208762306a36Sopenharmony_ci			w->state = FWS_R;
208862306a36Sopenharmony_ci			fallthrough;
208962306a36Sopenharmony_ci		case FWS_R:
209062306a36Sopenharmony_ci			right = rcu_dereference_protected(fn->right, 1);
209162306a36Sopenharmony_ci			if (right) {
209262306a36Sopenharmony_ci				w->node = right;
209362306a36Sopenharmony_ci				w->state = FWS_INIT;
209462306a36Sopenharmony_ci				continue;
209562306a36Sopenharmony_ci			}
209662306a36Sopenharmony_ci			w->state = FWS_C;
209762306a36Sopenharmony_ci			w->leaf = rcu_dereference_protected(fn->leaf, 1);
209862306a36Sopenharmony_ci			fallthrough;
209962306a36Sopenharmony_ci		case FWS_C:
210062306a36Sopenharmony_ci			if (w->leaf && fn->fn_flags & RTN_RTINFO) {
210162306a36Sopenharmony_ci				int err;
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci				if (w->skip) {
210462306a36Sopenharmony_ci					w->skip--;
210562306a36Sopenharmony_ci					goto skip;
210662306a36Sopenharmony_ci				}
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_ci				err = w->func(w);
210962306a36Sopenharmony_ci				if (err)
211062306a36Sopenharmony_ci					return err;
211162306a36Sopenharmony_ci
211262306a36Sopenharmony_ci				w->count++;
211362306a36Sopenharmony_ci				continue;
211462306a36Sopenharmony_ci			}
211562306a36Sopenharmony_ciskip:
211662306a36Sopenharmony_ci			w->state = FWS_U;
211762306a36Sopenharmony_ci			fallthrough;
211862306a36Sopenharmony_ci		case FWS_U:
211962306a36Sopenharmony_ci			if (fn == w->root)
212062306a36Sopenharmony_ci				return 0;
212162306a36Sopenharmony_ci			pn = rcu_dereference_protected(fn->parent, 1);
212262306a36Sopenharmony_ci			left = rcu_dereference_protected(pn->left, 1);
212362306a36Sopenharmony_ci			right = rcu_dereference_protected(pn->right, 1);
212462306a36Sopenharmony_ci			w->node = pn;
212562306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
212662306a36Sopenharmony_ci			if (FIB6_SUBTREE(pn) == fn) {
212762306a36Sopenharmony_ci				WARN_ON(!(fn->fn_flags & RTN_ROOT));
212862306a36Sopenharmony_ci				w->state = FWS_L;
212962306a36Sopenharmony_ci				continue;
213062306a36Sopenharmony_ci			}
213162306a36Sopenharmony_ci#endif
213262306a36Sopenharmony_ci			if (left == fn) {
213362306a36Sopenharmony_ci				w->state = FWS_R;
213462306a36Sopenharmony_ci				continue;
213562306a36Sopenharmony_ci			}
213662306a36Sopenharmony_ci			if (right == fn) {
213762306a36Sopenharmony_ci				w->state = FWS_C;
213862306a36Sopenharmony_ci				w->leaf = rcu_dereference_protected(w->node->leaf, 1);
213962306a36Sopenharmony_ci				continue;
214062306a36Sopenharmony_ci			}
214162306a36Sopenharmony_ci#if RT6_DEBUG >= 2
214262306a36Sopenharmony_ci			WARN_ON(1);
214362306a36Sopenharmony_ci#endif
214462306a36Sopenharmony_ci		}
214562306a36Sopenharmony_ci	}
214662306a36Sopenharmony_ci}
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_cistatic int fib6_walk(struct net *net, struct fib6_walker *w)
214962306a36Sopenharmony_ci{
215062306a36Sopenharmony_ci	int res;
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci	w->state = FWS_INIT;
215362306a36Sopenharmony_ci	w->node = w->root;
215462306a36Sopenharmony_ci
215562306a36Sopenharmony_ci	fib6_walker_link(net, w);
215662306a36Sopenharmony_ci	res = fib6_walk_continue(w);
215762306a36Sopenharmony_ci	if (res <= 0)
215862306a36Sopenharmony_ci		fib6_walker_unlink(net, w);
215962306a36Sopenharmony_ci	return res;
216062306a36Sopenharmony_ci}
216162306a36Sopenharmony_ci
216262306a36Sopenharmony_cistatic int fib6_clean_node(struct fib6_walker *w)
216362306a36Sopenharmony_ci{
216462306a36Sopenharmony_ci	int res;
216562306a36Sopenharmony_ci	struct fib6_info *rt;
216662306a36Sopenharmony_ci	struct fib6_cleaner *c = container_of(w, struct fib6_cleaner, w);
216762306a36Sopenharmony_ci	struct nl_info info = {
216862306a36Sopenharmony_ci		.nl_net = c->net,
216962306a36Sopenharmony_ci		.skip_notify = c->skip_notify,
217062306a36Sopenharmony_ci	};
217162306a36Sopenharmony_ci
217262306a36Sopenharmony_ci	if (c->sernum != FIB6_NO_SERNUM_CHANGE &&
217362306a36Sopenharmony_ci	    READ_ONCE(w->node->fn_sernum) != c->sernum)
217462306a36Sopenharmony_ci		WRITE_ONCE(w->node->fn_sernum, c->sernum);
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci	if (!c->func) {
217762306a36Sopenharmony_ci		WARN_ON_ONCE(c->sernum == FIB6_NO_SERNUM_CHANGE);
217862306a36Sopenharmony_ci		w->leaf = NULL;
217962306a36Sopenharmony_ci		return 0;
218062306a36Sopenharmony_ci	}
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci	for_each_fib6_walker_rt(w) {
218362306a36Sopenharmony_ci		res = c->func(rt, c->arg);
218462306a36Sopenharmony_ci		if (res == -1) {
218562306a36Sopenharmony_ci			w->leaf = rt;
218662306a36Sopenharmony_ci			res = fib6_del(rt, &info);
218762306a36Sopenharmony_ci			if (res) {
218862306a36Sopenharmony_ci#if RT6_DEBUG >= 2
218962306a36Sopenharmony_ci				pr_debug("%s: del failed: rt=%p@%p err=%d\n",
219062306a36Sopenharmony_ci					 __func__, rt,
219162306a36Sopenharmony_ci					 rcu_access_pointer(rt->fib6_node),
219262306a36Sopenharmony_ci					 res);
219362306a36Sopenharmony_ci#endif
219462306a36Sopenharmony_ci				continue;
219562306a36Sopenharmony_ci			}
219662306a36Sopenharmony_ci			return 0;
219762306a36Sopenharmony_ci		} else if (res == -2) {
219862306a36Sopenharmony_ci			if (WARN_ON(!rt->fib6_nsiblings))
219962306a36Sopenharmony_ci				continue;
220062306a36Sopenharmony_ci			rt = list_last_entry(&rt->fib6_siblings,
220162306a36Sopenharmony_ci					     struct fib6_info, fib6_siblings);
220262306a36Sopenharmony_ci			continue;
220362306a36Sopenharmony_ci		}
220462306a36Sopenharmony_ci		WARN_ON(res != 0);
220562306a36Sopenharmony_ci	}
220662306a36Sopenharmony_ci	w->leaf = rt;
220762306a36Sopenharmony_ci	return 0;
220862306a36Sopenharmony_ci}
220962306a36Sopenharmony_ci
221062306a36Sopenharmony_ci/*
221162306a36Sopenharmony_ci *	Convenient frontend to tree walker.
221262306a36Sopenharmony_ci *
221362306a36Sopenharmony_ci *	func is called on each route.
221462306a36Sopenharmony_ci *		It may return -2 -> skip multipath route.
221562306a36Sopenharmony_ci *			      -1 -> delete this route.
221662306a36Sopenharmony_ci *		              0  -> continue walking
221762306a36Sopenharmony_ci */
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_cistatic void fib6_clean_tree(struct net *net, struct fib6_node *root,
222062306a36Sopenharmony_ci			    int (*func)(struct fib6_info *, void *arg),
222162306a36Sopenharmony_ci			    int sernum, void *arg, bool skip_notify)
222262306a36Sopenharmony_ci{
222362306a36Sopenharmony_ci	struct fib6_cleaner c;
222462306a36Sopenharmony_ci
222562306a36Sopenharmony_ci	c.w.root = root;
222662306a36Sopenharmony_ci	c.w.func = fib6_clean_node;
222762306a36Sopenharmony_ci	c.w.count = 0;
222862306a36Sopenharmony_ci	c.w.skip = 0;
222962306a36Sopenharmony_ci	c.w.skip_in_node = 0;
223062306a36Sopenharmony_ci	c.func = func;
223162306a36Sopenharmony_ci	c.sernum = sernum;
223262306a36Sopenharmony_ci	c.arg = arg;
223362306a36Sopenharmony_ci	c.net = net;
223462306a36Sopenharmony_ci	c.skip_notify = skip_notify;
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_ci	fib6_walk(net, &c.w);
223762306a36Sopenharmony_ci}
223862306a36Sopenharmony_ci
223962306a36Sopenharmony_cistatic void __fib6_clean_all(struct net *net,
224062306a36Sopenharmony_ci			     int (*func)(struct fib6_info *, void *),
224162306a36Sopenharmony_ci			     int sernum, void *arg, bool skip_notify)
224262306a36Sopenharmony_ci{
224362306a36Sopenharmony_ci	struct fib6_table *table;
224462306a36Sopenharmony_ci	struct hlist_head *head;
224562306a36Sopenharmony_ci	unsigned int h;
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_ci	rcu_read_lock();
224862306a36Sopenharmony_ci	for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
224962306a36Sopenharmony_ci		head = &net->ipv6.fib_table_hash[h];
225062306a36Sopenharmony_ci		hlist_for_each_entry_rcu(table, head, tb6_hlist) {
225162306a36Sopenharmony_ci			spin_lock_bh(&table->tb6_lock);
225262306a36Sopenharmony_ci			fib6_clean_tree(net, &table->tb6_root,
225362306a36Sopenharmony_ci					func, sernum, arg, skip_notify);
225462306a36Sopenharmony_ci			spin_unlock_bh(&table->tb6_lock);
225562306a36Sopenharmony_ci		}
225662306a36Sopenharmony_ci	}
225762306a36Sopenharmony_ci	rcu_read_unlock();
225862306a36Sopenharmony_ci}
225962306a36Sopenharmony_ci
226062306a36Sopenharmony_civoid fib6_clean_all(struct net *net, int (*func)(struct fib6_info *, void *),
226162306a36Sopenharmony_ci		    void *arg)
226262306a36Sopenharmony_ci{
226362306a36Sopenharmony_ci	__fib6_clean_all(net, func, FIB6_NO_SERNUM_CHANGE, arg, false);
226462306a36Sopenharmony_ci}
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_civoid fib6_clean_all_skip_notify(struct net *net,
226762306a36Sopenharmony_ci				int (*func)(struct fib6_info *, void *),
226862306a36Sopenharmony_ci				void *arg)
226962306a36Sopenharmony_ci{
227062306a36Sopenharmony_ci	__fib6_clean_all(net, func, FIB6_NO_SERNUM_CHANGE, arg, true);
227162306a36Sopenharmony_ci}
227262306a36Sopenharmony_ci
227362306a36Sopenharmony_cistatic void fib6_flush_trees(struct net *net)
227462306a36Sopenharmony_ci{
227562306a36Sopenharmony_ci	int new_sernum = fib6_new_sernum(net);
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_ci	__fib6_clean_all(net, NULL, new_sernum, NULL, false);
227862306a36Sopenharmony_ci}
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_ci/*
228162306a36Sopenharmony_ci *	Garbage collection
228262306a36Sopenharmony_ci */
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_cistatic int fib6_age(struct fib6_info *rt, void *arg)
228562306a36Sopenharmony_ci{
228662306a36Sopenharmony_ci	struct fib6_gc_args *gc_args = arg;
228762306a36Sopenharmony_ci	unsigned long now = jiffies;
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_ci	/*
229062306a36Sopenharmony_ci	 *	check addrconf expiration here.
229162306a36Sopenharmony_ci	 *	Routes are expired even if they are in use.
229262306a36Sopenharmony_ci	 */
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ci	if (rt->fib6_flags & RTF_EXPIRES && rt->expires) {
229562306a36Sopenharmony_ci		if (time_after(now, rt->expires)) {
229662306a36Sopenharmony_ci			RT6_TRACE("expiring %p\n", rt);
229762306a36Sopenharmony_ci			return -1;
229862306a36Sopenharmony_ci		}
229962306a36Sopenharmony_ci		gc_args->more++;
230062306a36Sopenharmony_ci	}
230162306a36Sopenharmony_ci
230262306a36Sopenharmony_ci	/*	Also age clones in the exception table.
230362306a36Sopenharmony_ci	 *	Note, that clones are aged out
230462306a36Sopenharmony_ci	 *	only if they are not in use now.
230562306a36Sopenharmony_ci	 */
230662306a36Sopenharmony_ci	rt6_age_exceptions(rt, gc_args, now);
230762306a36Sopenharmony_ci
230862306a36Sopenharmony_ci	return 0;
230962306a36Sopenharmony_ci}
231062306a36Sopenharmony_ci
231162306a36Sopenharmony_civoid fib6_run_gc(unsigned long expires, struct net *net, bool force)
231262306a36Sopenharmony_ci{
231362306a36Sopenharmony_ci	struct fib6_gc_args gc_args;
231462306a36Sopenharmony_ci	unsigned long now;
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_ci	if (force) {
231762306a36Sopenharmony_ci		spin_lock_bh(&net->ipv6.fib6_gc_lock);
231862306a36Sopenharmony_ci	} else if (!spin_trylock_bh(&net->ipv6.fib6_gc_lock)) {
231962306a36Sopenharmony_ci		mod_timer(&net->ipv6.ip6_fib_timer, jiffies + HZ);
232062306a36Sopenharmony_ci		return;
232162306a36Sopenharmony_ci	}
232262306a36Sopenharmony_ci	gc_args.timeout = expires ? (int)expires :
232362306a36Sopenharmony_ci			  net->ipv6.sysctl.ip6_rt_gc_interval;
232462306a36Sopenharmony_ci	gc_args.more = 0;
232562306a36Sopenharmony_ci
232662306a36Sopenharmony_ci	fib6_clean_all(net, fib6_age, &gc_args);
232762306a36Sopenharmony_ci	now = jiffies;
232862306a36Sopenharmony_ci	net->ipv6.ip6_rt_last_gc = now;
232962306a36Sopenharmony_ci
233062306a36Sopenharmony_ci	if (gc_args.more)
233162306a36Sopenharmony_ci		mod_timer(&net->ipv6.ip6_fib_timer,
233262306a36Sopenharmony_ci			  round_jiffies(now
233362306a36Sopenharmony_ci					+ net->ipv6.sysctl.ip6_rt_gc_interval));
233462306a36Sopenharmony_ci	else
233562306a36Sopenharmony_ci		del_timer(&net->ipv6.ip6_fib_timer);
233662306a36Sopenharmony_ci	spin_unlock_bh(&net->ipv6.fib6_gc_lock);
233762306a36Sopenharmony_ci}
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_cistatic void fib6_gc_timer_cb(struct timer_list *t)
234062306a36Sopenharmony_ci{
234162306a36Sopenharmony_ci	struct net *arg = from_timer(arg, t, ipv6.ip6_fib_timer);
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci	fib6_run_gc(0, arg, true);
234462306a36Sopenharmony_ci}
234562306a36Sopenharmony_ci
234662306a36Sopenharmony_cistatic int __net_init fib6_net_init(struct net *net)
234762306a36Sopenharmony_ci{
234862306a36Sopenharmony_ci	size_t size = sizeof(struct hlist_head) * FIB6_TABLE_HASHSZ;
234962306a36Sopenharmony_ci	int err;
235062306a36Sopenharmony_ci
235162306a36Sopenharmony_ci	err = fib6_notifier_init(net);
235262306a36Sopenharmony_ci	if (err)
235362306a36Sopenharmony_ci		return err;
235462306a36Sopenharmony_ci
235562306a36Sopenharmony_ci	/* Default to 3-tuple */
235662306a36Sopenharmony_ci	net->ipv6.sysctl.multipath_hash_fields =
235762306a36Sopenharmony_ci		FIB_MULTIPATH_HASH_FIELD_DEFAULT_MASK;
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci	spin_lock_init(&net->ipv6.fib6_gc_lock);
236062306a36Sopenharmony_ci	rwlock_init(&net->ipv6.fib6_walker_lock);
236162306a36Sopenharmony_ci	INIT_LIST_HEAD(&net->ipv6.fib6_walkers);
236262306a36Sopenharmony_ci	timer_setup(&net->ipv6.ip6_fib_timer, fib6_gc_timer_cb, 0);
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci	net->ipv6.rt6_stats = kzalloc(sizeof(*net->ipv6.rt6_stats), GFP_KERNEL);
236562306a36Sopenharmony_ci	if (!net->ipv6.rt6_stats)
236662306a36Sopenharmony_ci		goto out_notifier;
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_ci	/* Avoid false sharing : Use at least a full cache line */
236962306a36Sopenharmony_ci	size = max_t(size_t, size, L1_CACHE_BYTES);
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_ci	net->ipv6.fib_table_hash = kzalloc(size, GFP_KERNEL);
237262306a36Sopenharmony_ci	if (!net->ipv6.fib_table_hash)
237362306a36Sopenharmony_ci		goto out_rt6_stats;
237462306a36Sopenharmony_ci
237562306a36Sopenharmony_ci	net->ipv6.fib6_main_tbl = kzalloc(sizeof(*net->ipv6.fib6_main_tbl),
237662306a36Sopenharmony_ci					  GFP_KERNEL);
237762306a36Sopenharmony_ci	if (!net->ipv6.fib6_main_tbl)
237862306a36Sopenharmony_ci		goto out_fib_table_hash;
237962306a36Sopenharmony_ci
238062306a36Sopenharmony_ci	net->ipv6.fib6_main_tbl->tb6_id = RT6_TABLE_MAIN;
238162306a36Sopenharmony_ci	rcu_assign_pointer(net->ipv6.fib6_main_tbl->tb6_root.leaf,
238262306a36Sopenharmony_ci			   net->ipv6.fib6_null_entry);
238362306a36Sopenharmony_ci	net->ipv6.fib6_main_tbl->tb6_root.fn_flags =
238462306a36Sopenharmony_ci		RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
238562306a36Sopenharmony_ci	inet_peer_base_init(&net->ipv6.fib6_main_tbl->tb6_peers);
238662306a36Sopenharmony_ci
238762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES
238862306a36Sopenharmony_ci	net->ipv6.fib6_local_tbl = kzalloc(sizeof(*net->ipv6.fib6_local_tbl),
238962306a36Sopenharmony_ci					   GFP_KERNEL);
239062306a36Sopenharmony_ci	if (!net->ipv6.fib6_local_tbl)
239162306a36Sopenharmony_ci		goto out_fib6_main_tbl;
239262306a36Sopenharmony_ci	net->ipv6.fib6_local_tbl->tb6_id = RT6_TABLE_LOCAL;
239362306a36Sopenharmony_ci	rcu_assign_pointer(net->ipv6.fib6_local_tbl->tb6_root.leaf,
239462306a36Sopenharmony_ci			   net->ipv6.fib6_null_entry);
239562306a36Sopenharmony_ci	net->ipv6.fib6_local_tbl->tb6_root.fn_flags =
239662306a36Sopenharmony_ci		RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
239762306a36Sopenharmony_ci	inet_peer_base_init(&net->ipv6.fib6_local_tbl->tb6_peers);
239862306a36Sopenharmony_ci#endif
239962306a36Sopenharmony_ci	fib6_tables_init(net);
240062306a36Sopenharmony_ci
240162306a36Sopenharmony_ci	return 0;
240262306a36Sopenharmony_ci
240362306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MULTIPLE_TABLES
240462306a36Sopenharmony_ciout_fib6_main_tbl:
240562306a36Sopenharmony_ci	kfree(net->ipv6.fib6_main_tbl);
240662306a36Sopenharmony_ci#endif
240762306a36Sopenharmony_ciout_fib_table_hash:
240862306a36Sopenharmony_ci	kfree(net->ipv6.fib_table_hash);
240962306a36Sopenharmony_ciout_rt6_stats:
241062306a36Sopenharmony_ci	kfree(net->ipv6.rt6_stats);
241162306a36Sopenharmony_ciout_notifier:
241262306a36Sopenharmony_ci	fib6_notifier_exit(net);
241362306a36Sopenharmony_ci	return -ENOMEM;
241462306a36Sopenharmony_ci}
241562306a36Sopenharmony_ci
241662306a36Sopenharmony_cistatic void fib6_net_exit(struct net *net)
241762306a36Sopenharmony_ci{
241862306a36Sopenharmony_ci	unsigned int i;
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci	del_timer_sync(&net->ipv6.ip6_fib_timer);
242162306a36Sopenharmony_ci
242262306a36Sopenharmony_ci	for (i = 0; i < FIB6_TABLE_HASHSZ; i++) {
242362306a36Sopenharmony_ci		struct hlist_head *head = &net->ipv6.fib_table_hash[i];
242462306a36Sopenharmony_ci		struct hlist_node *tmp;
242562306a36Sopenharmony_ci		struct fib6_table *tb;
242662306a36Sopenharmony_ci
242762306a36Sopenharmony_ci		hlist_for_each_entry_safe(tb, tmp, head, tb6_hlist) {
242862306a36Sopenharmony_ci			hlist_del(&tb->tb6_hlist);
242962306a36Sopenharmony_ci			fib6_free_table(tb);
243062306a36Sopenharmony_ci		}
243162306a36Sopenharmony_ci	}
243262306a36Sopenharmony_ci
243362306a36Sopenharmony_ci	kfree(net->ipv6.fib_table_hash);
243462306a36Sopenharmony_ci	kfree(net->ipv6.rt6_stats);
243562306a36Sopenharmony_ci	fib6_notifier_exit(net);
243662306a36Sopenharmony_ci}
243762306a36Sopenharmony_ci
243862306a36Sopenharmony_cistatic struct pernet_operations fib6_net_ops = {
243962306a36Sopenharmony_ci	.init = fib6_net_init,
244062306a36Sopenharmony_ci	.exit = fib6_net_exit,
244162306a36Sopenharmony_ci};
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_ciint __init fib6_init(void)
244462306a36Sopenharmony_ci{
244562306a36Sopenharmony_ci	int ret = -ENOMEM;
244662306a36Sopenharmony_ci
244762306a36Sopenharmony_ci	fib6_node_kmem = kmem_cache_create("fib6_nodes",
244862306a36Sopenharmony_ci					   sizeof(struct fib6_node), 0,
244962306a36Sopenharmony_ci					   SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT,
245062306a36Sopenharmony_ci					   NULL);
245162306a36Sopenharmony_ci	if (!fib6_node_kmem)
245262306a36Sopenharmony_ci		goto out;
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_ci	ret = register_pernet_subsys(&fib6_net_ops);
245562306a36Sopenharmony_ci	if (ret)
245662306a36Sopenharmony_ci		goto out_kmem_cache_create;
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE, NULL,
245962306a36Sopenharmony_ci				   inet6_dump_fib, 0);
246062306a36Sopenharmony_ci	if (ret)
246162306a36Sopenharmony_ci		goto out_unregister_subsys;
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_ci	__fib6_flush_trees = fib6_flush_trees;
246462306a36Sopenharmony_ciout:
246562306a36Sopenharmony_ci	return ret;
246662306a36Sopenharmony_ci
246762306a36Sopenharmony_ciout_unregister_subsys:
246862306a36Sopenharmony_ci	unregister_pernet_subsys(&fib6_net_ops);
246962306a36Sopenharmony_ciout_kmem_cache_create:
247062306a36Sopenharmony_ci	kmem_cache_destroy(fib6_node_kmem);
247162306a36Sopenharmony_ci	goto out;
247262306a36Sopenharmony_ci}
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_civoid fib6_gc_cleanup(void)
247562306a36Sopenharmony_ci{
247662306a36Sopenharmony_ci	unregister_pernet_subsys(&fib6_net_ops);
247762306a36Sopenharmony_ci	kmem_cache_destroy(fib6_node_kmem);
247862306a36Sopenharmony_ci}
247962306a36Sopenharmony_ci
248062306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS
248162306a36Sopenharmony_cistatic int ipv6_route_native_seq_show(struct seq_file *seq, void *v)
248262306a36Sopenharmony_ci{
248362306a36Sopenharmony_ci	struct fib6_info *rt = v;
248462306a36Sopenharmony_ci	struct ipv6_route_iter *iter = seq->private;
248562306a36Sopenharmony_ci	struct fib6_nh *fib6_nh = rt->fib6_nh;
248662306a36Sopenharmony_ci	unsigned int flags = rt->fib6_flags;
248762306a36Sopenharmony_ci	const struct net_device *dev;
248862306a36Sopenharmony_ci
248962306a36Sopenharmony_ci	if (rt->nh)
249062306a36Sopenharmony_ci		fib6_nh = nexthop_fib6_nh(rt->nh);
249162306a36Sopenharmony_ci
249262306a36Sopenharmony_ci	seq_printf(seq, "%pi6 %02x ", &rt->fib6_dst.addr, rt->fib6_dst.plen);
249362306a36Sopenharmony_ci
249462306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SUBTREES
249562306a36Sopenharmony_ci	seq_printf(seq, "%pi6 %02x ", &rt->fib6_src.addr, rt->fib6_src.plen);
249662306a36Sopenharmony_ci#else
249762306a36Sopenharmony_ci	seq_puts(seq, "00000000000000000000000000000000 00 ");
249862306a36Sopenharmony_ci#endif
249962306a36Sopenharmony_ci	if (fib6_nh->fib_nh_gw_family) {
250062306a36Sopenharmony_ci		flags |= RTF_GATEWAY;
250162306a36Sopenharmony_ci		seq_printf(seq, "%pi6", &fib6_nh->fib_nh_gw6);
250262306a36Sopenharmony_ci	} else {
250362306a36Sopenharmony_ci		seq_puts(seq, "00000000000000000000000000000000");
250462306a36Sopenharmony_ci	}
250562306a36Sopenharmony_ci
250662306a36Sopenharmony_ci	dev = fib6_nh->fib_nh_dev;
250762306a36Sopenharmony_ci	seq_printf(seq, " %08x %08x %08x %08x %8s\n",
250862306a36Sopenharmony_ci		   rt->fib6_metric, refcount_read(&rt->fib6_ref), 0,
250962306a36Sopenharmony_ci		   flags, dev ? dev->name : "");
251062306a36Sopenharmony_ci	iter->w.leaf = NULL;
251162306a36Sopenharmony_ci	return 0;
251262306a36Sopenharmony_ci}
251362306a36Sopenharmony_ci
251462306a36Sopenharmony_cistatic int ipv6_route_yield(struct fib6_walker *w)
251562306a36Sopenharmony_ci{
251662306a36Sopenharmony_ci	struct ipv6_route_iter *iter = w->args;
251762306a36Sopenharmony_ci
251862306a36Sopenharmony_ci	if (!iter->skip)
251962306a36Sopenharmony_ci		return 1;
252062306a36Sopenharmony_ci
252162306a36Sopenharmony_ci	do {
252262306a36Sopenharmony_ci		iter->w.leaf = rcu_dereference_protected(
252362306a36Sopenharmony_ci				iter->w.leaf->fib6_next,
252462306a36Sopenharmony_ci				lockdep_is_held(&iter->tbl->tb6_lock));
252562306a36Sopenharmony_ci		iter->skip--;
252662306a36Sopenharmony_ci		if (!iter->skip && iter->w.leaf)
252762306a36Sopenharmony_ci			return 1;
252862306a36Sopenharmony_ci	} while (iter->w.leaf);
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_ci	return 0;
253162306a36Sopenharmony_ci}
253262306a36Sopenharmony_ci
253362306a36Sopenharmony_cistatic void ipv6_route_seq_setup_walk(struct ipv6_route_iter *iter,
253462306a36Sopenharmony_ci				      struct net *net)
253562306a36Sopenharmony_ci{
253662306a36Sopenharmony_ci	memset(&iter->w, 0, sizeof(iter->w));
253762306a36Sopenharmony_ci	iter->w.func = ipv6_route_yield;
253862306a36Sopenharmony_ci	iter->w.root = &iter->tbl->tb6_root;
253962306a36Sopenharmony_ci	iter->w.state = FWS_INIT;
254062306a36Sopenharmony_ci	iter->w.node = iter->w.root;
254162306a36Sopenharmony_ci	iter->w.args = iter;
254262306a36Sopenharmony_ci	iter->sernum = READ_ONCE(iter->w.root->fn_sernum);
254362306a36Sopenharmony_ci	INIT_LIST_HEAD(&iter->w.lh);
254462306a36Sopenharmony_ci	fib6_walker_link(net, &iter->w);
254562306a36Sopenharmony_ci}
254662306a36Sopenharmony_ci
254762306a36Sopenharmony_cistatic struct fib6_table *ipv6_route_seq_next_table(struct fib6_table *tbl,
254862306a36Sopenharmony_ci						    struct net *net)
254962306a36Sopenharmony_ci{
255062306a36Sopenharmony_ci	unsigned int h;
255162306a36Sopenharmony_ci	struct hlist_node *node;
255262306a36Sopenharmony_ci
255362306a36Sopenharmony_ci	if (tbl) {
255462306a36Sopenharmony_ci		h = (tbl->tb6_id & (FIB6_TABLE_HASHSZ - 1)) + 1;
255562306a36Sopenharmony_ci		node = rcu_dereference(hlist_next_rcu(&tbl->tb6_hlist));
255662306a36Sopenharmony_ci	} else {
255762306a36Sopenharmony_ci		h = 0;
255862306a36Sopenharmony_ci		node = NULL;
255962306a36Sopenharmony_ci	}
256062306a36Sopenharmony_ci
256162306a36Sopenharmony_ci	while (!node && h < FIB6_TABLE_HASHSZ) {
256262306a36Sopenharmony_ci		node = rcu_dereference(
256362306a36Sopenharmony_ci			hlist_first_rcu(&net->ipv6.fib_table_hash[h++]));
256462306a36Sopenharmony_ci	}
256562306a36Sopenharmony_ci	return hlist_entry_safe(node, struct fib6_table, tb6_hlist);
256662306a36Sopenharmony_ci}
256762306a36Sopenharmony_ci
256862306a36Sopenharmony_cistatic void ipv6_route_check_sernum(struct ipv6_route_iter *iter)
256962306a36Sopenharmony_ci{
257062306a36Sopenharmony_ci	int sernum = READ_ONCE(iter->w.root->fn_sernum);
257162306a36Sopenharmony_ci
257262306a36Sopenharmony_ci	if (iter->sernum != sernum) {
257362306a36Sopenharmony_ci		iter->sernum = sernum;
257462306a36Sopenharmony_ci		iter->w.state = FWS_INIT;
257562306a36Sopenharmony_ci		iter->w.node = iter->w.root;
257662306a36Sopenharmony_ci		WARN_ON(iter->w.skip);
257762306a36Sopenharmony_ci		iter->w.skip = iter->w.count;
257862306a36Sopenharmony_ci	}
257962306a36Sopenharmony_ci}
258062306a36Sopenharmony_ci
258162306a36Sopenharmony_cistatic void *ipv6_route_seq_next(struct seq_file *seq, void *v, loff_t *pos)
258262306a36Sopenharmony_ci{
258362306a36Sopenharmony_ci	int r;
258462306a36Sopenharmony_ci	struct fib6_info *n;
258562306a36Sopenharmony_ci	struct net *net = seq_file_net(seq);
258662306a36Sopenharmony_ci	struct ipv6_route_iter *iter = seq->private;
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci	++(*pos);
258962306a36Sopenharmony_ci	if (!v)
259062306a36Sopenharmony_ci		goto iter_table;
259162306a36Sopenharmony_ci
259262306a36Sopenharmony_ci	n = rcu_dereference(((struct fib6_info *)v)->fib6_next);
259362306a36Sopenharmony_ci	if (n)
259462306a36Sopenharmony_ci		return n;
259562306a36Sopenharmony_ci
259662306a36Sopenharmony_ciiter_table:
259762306a36Sopenharmony_ci	ipv6_route_check_sernum(iter);
259862306a36Sopenharmony_ci	spin_lock_bh(&iter->tbl->tb6_lock);
259962306a36Sopenharmony_ci	r = fib6_walk_continue(&iter->w);
260062306a36Sopenharmony_ci	spin_unlock_bh(&iter->tbl->tb6_lock);
260162306a36Sopenharmony_ci	if (r > 0) {
260262306a36Sopenharmony_ci		return iter->w.leaf;
260362306a36Sopenharmony_ci	} else if (r < 0) {
260462306a36Sopenharmony_ci		fib6_walker_unlink(net, &iter->w);
260562306a36Sopenharmony_ci		return NULL;
260662306a36Sopenharmony_ci	}
260762306a36Sopenharmony_ci	fib6_walker_unlink(net, &iter->w);
260862306a36Sopenharmony_ci
260962306a36Sopenharmony_ci	iter->tbl = ipv6_route_seq_next_table(iter->tbl, net);
261062306a36Sopenharmony_ci	if (!iter->tbl)
261162306a36Sopenharmony_ci		return NULL;
261262306a36Sopenharmony_ci
261362306a36Sopenharmony_ci	ipv6_route_seq_setup_walk(iter, net);
261462306a36Sopenharmony_ci	goto iter_table;
261562306a36Sopenharmony_ci}
261662306a36Sopenharmony_ci
261762306a36Sopenharmony_cistatic void *ipv6_route_seq_start(struct seq_file *seq, loff_t *pos)
261862306a36Sopenharmony_ci	__acquires(RCU)
261962306a36Sopenharmony_ci{
262062306a36Sopenharmony_ci	struct net *net = seq_file_net(seq);
262162306a36Sopenharmony_ci	struct ipv6_route_iter *iter = seq->private;
262262306a36Sopenharmony_ci
262362306a36Sopenharmony_ci	rcu_read_lock();
262462306a36Sopenharmony_ci	iter->tbl = ipv6_route_seq_next_table(NULL, net);
262562306a36Sopenharmony_ci	iter->skip = *pos;
262662306a36Sopenharmony_ci
262762306a36Sopenharmony_ci	if (iter->tbl) {
262862306a36Sopenharmony_ci		loff_t p = 0;
262962306a36Sopenharmony_ci
263062306a36Sopenharmony_ci		ipv6_route_seq_setup_walk(iter, net);
263162306a36Sopenharmony_ci		return ipv6_route_seq_next(seq, NULL, &p);
263262306a36Sopenharmony_ci	} else {
263362306a36Sopenharmony_ci		return NULL;
263462306a36Sopenharmony_ci	}
263562306a36Sopenharmony_ci}
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_cistatic bool ipv6_route_iter_active(struct ipv6_route_iter *iter)
263862306a36Sopenharmony_ci{
263962306a36Sopenharmony_ci	struct fib6_walker *w = &iter->w;
264062306a36Sopenharmony_ci	return w->node && !(w->state == FWS_U && w->node == w->root);
264162306a36Sopenharmony_ci}
264262306a36Sopenharmony_ci
264362306a36Sopenharmony_cistatic void ipv6_route_native_seq_stop(struct seq_file *seq, void *v)
264462306a36Sopenharmony_ci	__releases(RCU)
264562306a36Sopenharmony_ci{
264662306a36Sopenharmony_ci	struct net *net = seq_file_net(seq);
264762306a36Sopenharmony_ci	struct ipv6_route_iter *iter = seq->private;
264862306a36Sopenharmony_ci
264962306a36Sopenharmony_ci	if (ipv6_route_iter_active(iter))
265062306a36Sopenharmony_ci		fib6_walker_unlink(net, &iter->w);
265162306a36Sopenharmony_ci
265262306a36Sopenharmony_ci	rcu_read_unlock();
265362306a36Sopenharmony_ci}
265462306a36Sopenharmony_ci
265562306a36Sopenharmony_ci#if IS_BUILTIN(CONFIG_IPV6) && defined(CONFIG_BPF_SYSCALL)
265662306a36Sopenharmony_cistatic int ipv6_route_prog_seq_show(struct bpf_prog *prog,
265762306a36Sopenharmony_ci				    struct bpf_iter_meta *meta,
265862306a36Sopenharmony_ci				    void *v)
265962306a36Sopenharmony_ci{
266062306a36Sopenharmony_ci	struct bpf_iter__ipv6_route ctx;
266162306a36Sopenharmony_ci
266262306a36Sopenharmony_ci	ctx.meta = meta;
266362306a36Sopenharmony_ci	ctx.rt = v;
266462306a36Sopenharmony_ci	return bpf_iter_run_prog(prog, &ctx);
266562306a36Sopenharmony_ci}
266662306a36Sopenharmony_ci
266762306a36Sopenharmony_cistatic int ipv6_route_seq_show(struct seq_file *seq, void *v)
266862306a36Sopenharmony_ci{
266962306a36Sopenharmony_ci	struct ipv6_route_iter *iter = seq->private;
267062306a36Sopenharmony_ci	struct bpf_iter_meta meta;
267162306a36Sopenharmony_ci	struct bpf_prog *prog;
267262306a36Sopenharmony_ci	int ret;
267362306a36Sopenharmony_ci
267462306a36Sopenharmony_ci	meta.seq = seq;
267562306a36Sopenharmony_ci	prog = bpf_iter_get_info(&meta, false);
267662306a36Sopenharmony_ci	if (!prog)
267762306a36Sopenharmony_ci		return ipv6_route_native_seq_show(seq, v);
267862306a36Sopenharmony_ci
267962306a36Sopenharmony_ci	ret = ipv6_route_prog_seq_show(prog, &meta, v);
268062306a36Sopenharmony_ci	iter->w.leaf = NULL;
268162306a36Sopenharmony_ci
268262306a36Sopenharmony_ci	return ret;
268362306a36Sopenharmony_ci}
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_cistatic void ipv6_route_seq_stop(struct seq_file *seq, void *v)
268662306a36Sopenharmony_ci{
268762306a36Sopenharmony_ci	struct bpf_iter_meta meta;
268862306a36Sopenharmony_ci	struct bpf_prog *prog;
268962306a36Sopenharmony_ci
269062306a36Sopenharmony_ci	if (!v) {
269162306a36Sopenharmony_ci		meta.seq = seq;
269262306a36Sopenharmony_ci		prog = bpf_iter_get_info(&meta, true);
269362306a36Sopenharmony_ci		if (prog)
269462306a36Sopenharmony_ci			(void)ipv6_route_prog_seq_show(prog, &meta, v);
269562306a36Sopenharmony_ci	}
269662306a36Sopenharmony_ci
269762306a36Sopenharmony_ci	ipv6_route_native_seq_stop(seq, v);
269862306a36Sopenharmony_ci}
269962306a36Sopenharmony_ci#else
270062306a36Sopenharmony_cistatic int ipv6_route_seq_show(struct seq_file *seq, void *v)
270162306a36Sopenharmony_ci{
270262306a36Sopenharmony_ci	return ipv6_route_native_seq_show(seq, v);
270362306a36Sopenharmony_ci}
270462306a36Sopenharmony_ci
270562306a36Sopenharmony_cistatic void ipv6_route_seq_stop(struct seq_file *seq, void *v)
270662306a36Sopenharmony_ci{
270762306a36Sopenharmony_ci	ipv6_route_native_seq_stop(seq, v);
270862306a36Sopenharmony_ci}
270962306a36Sopenharmony_ci#endif
271062306a36Sopenharmony_ci
271162306a36Sopenharmony_ciconst struct seq_operations ipv6_route_seq_ops = {
271262306a36Sopenharmony_ci	.start	= ipv6_route_seq_start,
271362306a36Sopenharmony_ci	.next	= ipv6_route_seq_next,
271462306a36Sopenharmony_ci	.stop	= ipv6_route_seq_stop,
271562306a36Sopenharmony_ci	.show	= ipv6_route_seq_show
271662306a36Sopenharmony_ci};
271762306a36Sopenharmony_ci#endif /* CONFIG_PROC_FS */
2718