Blame view

net/ipv6/ip6_fib.c 62.4 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
  /*
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
3
   *	Linux INET6 implementation
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
5
6
   *	Forwarding Information Database
   *
   *	Authors:
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
7
   *	Pedro Roque		<roque@di.fc.ul.pt>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
   *
8db46f1d4   Wang Yufen   ipv6: fix checkpa...
9
10
11
12
13
   *	Changes:
   *	Yuji SEKIYA @USAGI:	Support default route on router node;
   *				remove ip6_null_entry from the top of
   *				routing table.
   *	Ville Nuorvala:		Fixed routing subtrees.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
   */
f32138319   Joe Perches   net: ipv6: Standa...
15
16
  
  #define pr_fmt(fmt) "IPv6: " fmt
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
20
21
22
23
  #include <linux/errno.h>
  #include <linux/types.h>
  #include <linux/net.h>
  #include <linux/route.h>
  #include <linux/netdevice.h>
  #include <linux/in6.h>
  #include <linux/init.h>
c71099acc   Thomas Graf   [IPV6]: Multiple ...
24
  #include <linux/list.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
25
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26

cc5f0eb21   David Ahern   net: Move free of...
27
  #include <net/ip.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
30
  #include <net/ipv6.h>
  #include <net/ndisc.h>
  #include <net/addrconf.h>
19e42e451   Roopa Prabhu   ipv6: support for...
31
  #include <net/lwtunnel.h>
df77fe4d9   Ido Schimmel   ipv6: fib: Add in...
32
  #include <net/fib_notifier.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
  
  #include <net/ip6_fib.h>
  #include <net/ip6_route.h>
437de07ce   Wang Yufen   ipv6: fix checkpa...
36
  static struct kmem_cache *fib6_node_kmem __read_mostly;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37

94b2cfe02   Hannes Frederic Sowa   ipv6: minor fib6 ...
38
39
  struct fib6_cleaner {
  	struct fib6_walker w;
ec7d43c29   Benjamin Thery   [NETNS][IPV6] ip6...
40
  	struct net *net;
8d1c802b2   David Ahern   net/ipv6: Flip FI...
41
  	int (*func)(struct fib6_info *, void *arg);
327571cb1   Hannes Frederic Sowa   ipv6: don't walk ...
42
  	int sernum;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
  	void *arg;
7c6bb7d2f   David Ahern   net/ipv6: Add kno...
44
  	bool skip_notify;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
47
  #ifdef CONFIG_IPV6_SUBTREES
  #define FWS_INIT FWS_S
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
  #else
  #define FWS_INIT FWS_L
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
  #endif
8d1c802b2   David Ahern   net/ipv6: Flip FI...
51
  static struct fib6_info *fib6_find_prefix(struct net *net,
66f5d6ce5   Wei Wang   ipv6: replace rwl...
52
53
54
55
56
  					 struct fib6_table *table,
  					 struct fib6_node *fn);
  static struct fib6_node *fib6_repair_tree(struct net *net,
  					  struct fib6_table *table,
  					  struct fib6_node *fn);
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
57
  static int fib6_walk(struct net *net, struct fib6_walker *w);
94b2cfe02   Hannes Frederic Sowa   ipv6: minor fib6 ...
58
  static int fib6_walk_continue(struct fib6_walker *w);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59
60
61
62
63
64
65
  
  /*
   *	A routing update causes an increase of the serial number on the
   *	affected subtree. This allows for cached routes to be asynchronously
   *	tested when modifications are made to the destination cache as a
   *	result of redirects, path MTU changes, etc.
   */
86cb30ec0   Kees Cook   treewide: setup_t...
66
  static void fib6_gc_timer_cb(struct timer_list *t);
5b7c931df   Daniel Lezcano   [NETNS][IPV6] ip6...
67

9a03cd8f3   Michal Kubeček   ipv6: per netns f...
68
69
  #define FOR_WALKERS(net, w) \
  	list_for_each_entry(w, &(net)->ipv6.fib6_walkers, lh)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70

9a03cd8f3   Michal Kubeček   ipv6: per netns f...
71
  static void fib6_walker_link(struct net *net, struct fib6_walker *w)
90d41122f   Adrian Bunk   [IPV6] ip6_fib.c:...
72
  {
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
73
74
75
  	write_lock_bh(&net->ipv6.fib6_walker_lock);
  	list_add(&w->lh, &net->ipv6.fib6_walkers);
  	write_unlock_bh(&net->ipv6.fib6_walker_lock);
90d41122f   Adrian Bunk   [IPV6] ip6_fib.c:...
76
  }
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
77
  static void fib6_walker_unlink(struct net *net, struct fib6_walker *w)
90d41122f   Adrian Bunk   [IPV6] ip6_fib.c:...
78
  {
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
79
  	write_lock_bh(&net->ipv6.fib6_walker_lock);
bbef49dac   Alexey Dobriyan   ipv6: use standar...
80
  	list_del(&w->lh);
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
81
  	write_unlock_bh(&net->ipv6.fib6_walker_lock);
90d41122f   Adrian Bunk   [IPV6] ip6_fib.c:...
82
  }
94b2cfe02   Hannes Frederic Sowa   ipv6: minor fib6 ...
83

812918c46   Hannes Frederic Sowa   ipv6: make fib6 s...
84
  static int fib6_new_sernum(struct net *net)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
  {
42b187064   Hannes Frederic Sowa   ipv6: make rt_ser...
86
87
88
  	int new, old;
  
  	do {
812918c46   Hannes Frederic Sowa   ipv6: make fib6 s...
89
  		old = atomic_read(&net->ipv6.fib6_sernum);
42b187064   Hannes Frederic Sowa   ipv6: make rt_ser...
90
  		new = old < INT_MAX ? old + 1 : 1;
812918c46   Hannes Frederic Sowa   ipv6: make fib6 s...
91
92
  	} while (atomic_cmpxchg(&net->ipv6.fib6_sernum,
  				old, new) != old);
42b187064   Hannes Frederic Sowa   ipv6: make rt_ser...
93
  	return new;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
  }
327571cb1   Hannes Frederic Sowa   ipv6: don't walk ...
95
96
97
  enum {
  	FIB6_NO_SERNUM_CHANGE = 0,
  };
93c2fb253   David Ahern   net/ipv6: Rename ...
98
  void fib6_update_sernum(struct net *net, struct fib6_info *f6i)
180ca444b   Wei Wang   ipv6: introduce a...
99
  {
180ca444b   Wei Wang   ipv6: introduce a...
100
  	struct fib6_node *fn;
93c2fb253   David Ahern   net/ipv6: Rename ...
101
102
  	fn = rcu_dereference_protected(f6i->fib6_node,
  			lockdep_is_held(&f6i->fib6_table->tb6_lock));
180ca444b   Wei Wang   ipv6: introduce a...
103
104
  	if (fn)
  		fn->fn_sernum = fib6_new_sernum(net);
180ca444b   Wei Wang   ipv6: introduce a...
105
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
107
108
  /*
   *	Auxiliary address test functions for the radix tree.
   *
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
109
   *	These assume a 32bit processor (although it will work on
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
111
112
113
114
115
   *	64bit processors)
   */
  
  /*
   *	test bit
   */
02cdce53f   YOSHIFUJI Hideaki / 吉藤英明   ipv6 fib: Use "Sw...
116
117
118
119
120
  #if defined(__LITTLE_ENDIAN)
  # define BITOP_BE32_SWIZZLE	(0x1F & ~7)
  #else
  # define BITOP_BE32_SWIZZLE	0
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121

94b2cfe02   Hannes Frederic Sowa   ipv6: minor fib6 ...
122
  static __be32 addr_bit_set(const void *token, int fn_bit)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
  {
b71d1d426   Eric Dumazet   inet: constify ip...
124
  	const __be32 *addr = token;
02cdce53f   YOSHIFUJI Hideaki / 吉藤英明   ipv6 fib: Use "Sw...
125
126
  	/*
  	 * Here,
8db46f1d4   Wang Yufen   ipv6: fix checkpa...
127
  	 *	1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f)
02cdce53f   YOSHIFUJI Hideaki / 吉藤英明   ipv6 fib: Use "Sw...
128
129
130
131
  	 * is optimized version of
  	 *	htonl(1 << ((~fn_bit)&0x1F))
  	 * See include/asm-generic/bitops/le.h.
  	 */
0eae88f31   Eric Dumazet   net: Fix various ...
132
133
  	return (__force __be32)(1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f)) &
  	       addr[fn_bit >> 5];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134
  }
1cf844c74   David Ahern   ipv6: Make fib6_n...
135
  struct fib6_info *fib6_info_alloc(gfp_t gfp_flags, bool with_fib6_nh)
a64efe142   David Ahern   net/ipv6: introdu...
136
  {
8d1c802b2   David Ahern   net/ipv6: Flip FI...
137
  	struct fib6_info *f6i;
1cf844c74   David Ahern   ipv6: Make fib6_n...
138
  	size_t sz = sizeof(*f6i);
a64efe142   David Ahern   net/ipv6: introdu...
139

1cf844c74   David Ahern   ipv6: Make fib6_n...
140
141
142
143
  	if (with_fib6_nh)
  		sz += sizeof(struct fib6_nh);
  
  	f6i = kzalloc(sz, gfp_flags);
a64efe142   David Ahern   net/ipv6: introdu...
144
145
  	if (!f6i)
  		return NULL;
f88d8ea67   David Ahern   ipv6: Plumb suppo...
146
  	/* fib6_siblings is a union with nh_list, so this initializes both */
93c2fb253   David Ahern   net/ipv6: Rename ...
147
  	INIT_LIST_HEAD(&f6i->fib6_siblings);
f05713e09   Eric Dumazet   ipv6: convert fib...
148
  	refcount_set(&f6i->fib6_ref, 1);
a64efe142   David Ahern   net/ipv6: introdu...
149
150
151
  
  	return f6i;
  }
9b0a8da8c   Eric Dumazet   net/ipv6: respect...
152
  void fib6_info_destroy_rcu(struct rcu_head *head)
a64efe142   David Ahern   net/ipv6: introdu...
153
  {
9b0a8da8c   Eric Dumazet   net/ipv6: respect...
154
  	struct fib6_info *f6i = container_of(head, struct fib6_info, rcu);
a64efe142   David Ahern   net/ipv6: introdu...
155

93c2fb253   David Ahern   net/ipv6: Rename ...
156
  	WARN_ON(f6i->fib6_node);
a64efe142   David Ahern   net/ipv6: introdu...
157

f88d8ea67   David Ahern   ipv6: Plumb suppo...
158
159
160
161
  	if (f6i->nh)
  		nexthop_put(f6i->nh);
  	else
  		fib6_nh_release(f6i->fib6_nh);
cc5f0eb21   David Ahern   net: Move free of...
162
  	ip_fib_metrics_put(f6i->fib6_metrics);
a64efe142   David Ahern   net/ipv6: introdu...
163
164
  	kfree(f6i);
  }
9b0a8da8c   Eric Dumazet   net/ipv6: respect...
165
  EXPORT_SYMBOL_GPL(fib6_info_destroy_rcu);
a64efe142   David Ahern   net/ipv6: introdu...
166

81eb8447d   Wei Wang   ipv6: take care o...
167
  static struct fib6_node *node_alloc(struct net *net)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
169
  {
  	struct fib6_node *fn;
c37622296   Robert P. J. Day   [PATCH] Transform...
170
  	fn = kmem_cache_zalloc(fib6_node_kmem, GFP_ATOMIC);
81eb8447d   Wei Wang   ipv6: take care o...
171
172
  	if (fn)
  		net->ipv6.rt6_stats->fib_nodes++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
174
175
  
  	return fn;
  }
81eb8447d   Wei Wang   ipv6: take care o...
176
  static void node_free_immediate(struct net *net, struct fib6_node *fn)
c5cff8561   Wei Wang   ipv6: add rcu gra...
177
178
  {
  	kmem_cache_free(fib6_node_kmem, fn);
81eb8447d   Wei Wang   ipv6: take care o...
179
  	net->ipv6.rt6_stats->fib_nodes--;
c5cff8561   Wei Wang   ipv6: add rcu gra...
180
181
182
  }
  
  static void node_free_rcu(struct rcu_head *head)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
  {
c5cff8561   Wei Wang   ipv6: add rcu gra...
184
  	struct fib6_node *fn = container_of(head, struct fib6_node, rcu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
186
  	kmem_cache_free(fib6_node_kmem, fn);
  }
81eb8447d   Wei Wang   ipv6: take care o...
187
  static void node_free(struct net *net, struct fib6_node *fn)
c5cff8561   Wei Wang   ipv6: add rcu gra...
188
189
  {
  	call_rcu(&fn->rcu, node_free_rcu);
81eb8447d   Wei Wang   ipv6: take care o...
190
  	net->ipv6.rt6_stats->fib_nodes--;
c5cff8561   Wei Wang   ipv6: add rcu gra...
191
  }
ba1cc08d9   Sabrina Dubroca   ipv6: fix memory ...
192
193
194
195
196
  static void fib6_free_table(struct fib6_table *table)
  {
  	inetpeer_invalidate_tree(&table->tb6_peers);
  	kfree(table);
  }
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
197
  static void fib6_link_table(struct net *net, struct fib6_table *tb)
1b43af548   Patrick McHardy   [IPV6]: Increase ...
198
199
  {
  	unsigned int h;
375216ad0   Thomas Graf   [IPv6] fib: initi...
200
201
202
203
  	/*
  	 * Initialize table lock at a single place to give lockdep a key,
  	 * tables aren't visible prior to being linked to the list.
  	 */
66f5d6ce5   Wei Wang   ipv6: replace rwl...
204
  	spin_lock_init(&tb->tb6_lock);
a33bc5c15   Neil Horman   xfrm: select sane...
205
  	h = tb->tb6_id & (FIB6_TABLE_HASHSZ - 1);
1b43af548   Patrick McHardy   [IPV6]: Increase ...
206
207
208
209
210
  
  	/*
  	 * No protection necessary, this is the only list mutatation
  	 * operation, tables never disappear once they exist.
  	 */
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
211
  	hlist_add_head_rcu(&tb->tb6_hlist, &net->ipv6.fib_table_hash[h]);
1b43af548   Patrick McHardy   [IPV6]: Increase ...
212
  }
c71099acc   Thomas Graf   [IPV6]: Multiple ...
213

1b43af548   Patrick McHardy   [IPV6]: Increase ...
214
  #ifdef CONFIG_IPV6_MULTIPLE_TABLES
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
215

8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
216
  static struct fib6_table *fib6_alloc_table(struct net *net, u32 id)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
217
218
219
220
  {
  	struct fib6_table *table;
  
  	table = kzalloc(sizeof(*table), GFP_ATOMIC);
507c9b1e0   David S. Miller   ipv6: Various cle...
221
  	if (table) {
c71099acc   Thomas Graf   [IPV6]: Multiple ...
222
  		table->tb6_id = id;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
223
  		rcu_assign_pointer(table->tb6_root.leaf,
421842ede   David Ahern   net/ipv6: Add fib...
224
  				   net->ipv6.fib6_null_entry);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
225
  		table->tb6_root.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
8e7732778   David S. Miller   inet: Add inetpee...
226
  		inet_peer_base_init(&table->tb6_peers);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
227
228
229
230
  	}
  
  	return table;
  }
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
231
  struct fib6_table *fib6_new_table(struct net *net, u32 id)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
232
233
234
235
236
  {
  	struct fib6_table *tb;
  
  	if (id == 0)
  		id = RT6_TABLE_MAIN;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
237
  	tb = fib6_get_table(net, id);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
238
239
  	if (tb)
  		return tb;
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
240
  	tb = fib6_alloc_table(net, id);
507c9b1e0   David S. Miller   ipv6: Various cle...
241
  	if (tb)
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
242
  		fib6_link_table(net, tb);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
243
244
245
  
  	return tb;
  }
b3b4663c9   David Ahern   net: vrf: Create ...
246
  EXPORT_SYMBOL_GPL(fib6_new_table);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
247

58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
248
  struct fib6_table *fib6_get_table(struct net *net, u32 id)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
249
250
  {
  	struct fib6_table *tb;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
251
  	struct hlist_head *head;
c71099acc   Thomas Graf   [IPV6]: Multiple ...
252
253
254
255
  	unsigned int h;
  
  	if (id == 0)
  		id = RT6_TABLE_MAIN;
a33bc5c15   Neil Horman   xfrm: select sane...
256
  	h = id & (FIB6_TABLE_HASHSZ - 1);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
257
  	rcu_read_lock();
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
258
  	head = &net->ipv6.fib_table_hash[h];
b67bfe0d4   Sasha Levin   hlist: drop the n...
259
  	hlist_for_each_entry_rcu(tb, head, tb6_hlist) {
c71099acc   Thomas Graf   [IPV6]: Multiple ...
260
261
262
263
264
265
266
267
268
  		if (tb->tb6_id == id) {
  			rcu_read_unlock();
  			return tb;
  		}
  	}
  	rcu_read_unlock();
  
  	return NULL;
  }
c48506877   David Ahern   net: Export fib6_...
269
  EXPORT_SYMBOL_GPL(fib6_get_table);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
270

2c8c1e729   Alexey Dobriyan   net: spread __net...
271
  static void __net_init fib6_tables_init(struct net *net)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
272
  {
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
273
274
  	fib6_link_table(net, net->ipv6.fib6_main_tbl);
  	fib6_link_table(net, net->ipv6.fib6_local_tbl);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
275
  }
c71099acc   Thomas Graf   [IPV6]: Multiple ...
276
  #else
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
277
  struct fib6_table *fib6_new_table(struct net *net, u32 id)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
278
  {
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
279
  	return fib6_get_table(net, id);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
280
  }
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
281
  struct fib6_table *fib6_get_table(struct net *net, u32 id)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
282
  {
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
283
  	  return net->ipv6.fib6_main_tbl;
c71099acc   Thomas Graf   [IPV6]: Multiple ...
284
  }
4c9483b2f   David S. Miller   ipv6: Convert to ...
285
  struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,
b75cc8f90   David Ahern   net/ipv6: Pass sk...
286
  				   const struct sk_buff *skb,
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
287
  				   int flags, pol_lookup_t lookup)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
288
  {
ab997ad40   lucien   ipv6: fix the inc...
289
  	struct rt6_info *rt;
55cced4f8   Brian Vazquez   ipv6: fib6: avoid...
290
291
  	rt = pol_lookup_func(lookup,
  			net, net->ipv6.fib6_main_tbl, fl6, skb, flags);
07f615574   Serhey Popovych   ipv6: Do not leak...
292
  	if (rt->dst.error == -EAGAIN) {
d64a1f574   Wei Wang   ipv6: honor RT6_L...
293
  		ip6_rt_put_flags(rt, flags);
ab997ad40   lucien   ipv6: fix the inc...
294
  		rt = net->ipv6.ip6_null_entry;
7b09c2d05   Eric Dumazet   ipv6: fix a typo ...
295
  		if (!(flags & RT6_LOOKUP_F_DST_NOREF))
d64a1f574   Wei Wang   ipv6: honor RT6_L...
296
  			dst_hold(&rt->dst);
ab997ad40   lucien   ipv6: fix the inc...
297
298
299
  	}
  
  	return &rt->dst;
c71099acc   Thomas Graf   [IPV6]: Multiple ...
300
  }
138118ec9   David Ahern   net/ipv6: Add fib...
301
  /* called with rcu lock held; no reference taken on fib6_info */
effda4dd9   David Ahern   ipv6: Pass fib6_r...
302
303
  int fib6_lookup(struct net *net, int oif, struct flowi6 *fl6,
  		struct fib6_result *res, int flags)
138118ec9   David Ahern   net/ipv6: Add fib...
304
  {
effda4dd9   David Ahern   ipv6: Pass fib6_r...
305
306
  	return fib6_table_lookup(net, net->ipv6.fib6_main_tbl, oif, fl6,
  				 res, flags);
138118ec9   David Ahern   net/ipv6: Add fib...
307
  }
2c8c1e729   Alexey Dobriyan   net: spread __net...
308
  static void __net_init fib6_tables_init(struct net *net)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
309
  {
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
310
  	fib6_link_table(net, net->ipv6.fib6_main_tbl);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
311
312
313
  }
  
  #endif
e1ee0a5ba   Ido Schimmel   ipv6: fib: Dump t...
314
315
316
317
318
319
320
321
  unsigned int fib6_tables_seq_read(struct net *net)
  {
  	unsigned int h, fib_seq = 0;
  
  	rcu_read_lock();
  	for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
  		struct hlist_head *head = &net->ipv6.fib_table_hash[h];
  		struct fib6_table *tb;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
322
  		hlist_for_each_entry_rcu(tb, head, tb6_hlist)
e1ee0a5ba   Ido Schimmel   ipv6: fib: Dump t...
323
  			fib_seq += tb->fib_seq;
e1ee0a5ba   Ido Schimmel   ipv6: fib: Dump t...
324
325
326
327
328
  	}
  	rcu_read_unlock();
  
  	return fib_seq;
  }
7c550daff   Jiri Pirko   net: fib_notifier...
329
  static int call_fib6_entry_notifier(struct notifier_block *nb,
e1ee0a5ba   Ido Schimmel   ipv6: fib: Dump t...
330
  				    enum fib_event_type event_type,
b7a595577   Jiri Pirko   net: fib_notifier...
331
332
  				    struct fib6_info *rt,
  				    struct netlink_ext_ack *extack)
e1ee0a5ba   Ido Schimmel   ipv6: fib: Dump t...
333
334
  {
  	struct fib6_entry_notifier_info info = {
b7a595577   Jiri Pirko   net: fib_notifier...
335
  		.info.extack = extack,
e1ee0a5ba   Ido Schimmel   ipv6: fib: Dump t...
336
337
  		.rt = rt,
  	};
7c550daff   Jiri Pirko   net: fib_notifier...
338
  	return call_fib6_notifier(nb, event_type, &info.info);
e1ee0a5ba   Ido Schimmel   ipv6: fib: Dump t...
339
  }
9c6ecd3cf   Ido Schimmel   ipv6: Only Replay...
340
341
342
343
344
345
346
347
348
349
350
351
352
353
  static int call_fib6_multipath_entry_notifier(struct notifier_block *nb,
  					      enum fib_event_type event_type,
  					      struct fib6_info *rt,
  					      unsigned int nsiblings,
  					      struct netlink_ext_ack *extack)
  {
  	struct fib6_entry_notifier_info info = {
  		.info.extack = extack,
  		.rt = rt,
  		.nsiblings = nsiblings,
  	};
  
  	return call_fib6_notifier(nb, event_type, &info.info);
  }
19a3b7eea   David Ahern   ipv6: export func...
354
355
356
357
  int call_fib6_entry_notifiers(struct net *net,
  			      enum fib_event_type event_type,
  			      struct fib6_info *rt,
  			      struct netlink_ext_ack *extack)
df77fe4d9   Ido Schimmel   ipv6: fib: Add in...
358
359
  {
  	struct fib6_entry_notifier_info info = {
6c31e5a91   David Ahern   net: Add extack t...
360
  		.info.extack = extack,
df77fe4d9   Ido Schimmel   ipv6: fib: Add in...
361
362
  		.rt = rt,
  	};
93c2fb253   David Ahern   net/ipv6: Rename ...
363
  	rt->fib6_table->fib_seq++;
df77fe4d9   Ido Schimmel   ipv6: fib: Add in...
364
365
  	return call_fib6_notifiers(net, event_type, &info.info);
  }
d4b96c7b5   Ido Schimmel   ipv6: Extend noti...
366
367
368
369
370
371
372
373
374
375
  int call_fib6_multipath_entry_notifiers(struct net *net,
  					enum fib_event_type event_type,
  					struct fib6_info *rt,
  					unsigned int nsiblings,
  					struct netlink_ext_ack *extack)
  {
  	struct fib6_entry_notifier_info info = {
  		.info.extack = extack,
  		.rt = rt,
  		.nsiblings = nsiblings,
d4b96c7b5   Ido Schimmel   ipv6: Extend noti...
376
377
378
379
380
  	};
  
  	rt->fib6_table->fib_seq++;
  	return call_fib6_notifiers(net, event_type, &info.info);
  }
d2f0c9b11   Ido Schimmel   ipv6: Handle rout...
381
382
383
384
385
386
387
388
  int call_fib6_entry_notifiers_replace(struct net *net, struct fib6_info *rt)
  {
  	struct fib6_entry_notifier_info info = {
  		.rt = rt,
  		.nsiblings = rt->fib6_nsiblings,
  	};
  
  	rt->fib6_table->fib_seq++;
caafb2509   Ido Schimmel   ipv6: Remove old ...
389
  	return call_fib6_notifiers(net, FIB_EVENT_ENTRY_REPLACE, &info.info);
d2f0c9b11   Ido Schimmel   ipv6: Handle rout...
390
  }
e1ee0a5ba   Ido Schimmel   ipv6: fib: Dump t...
391
392
393
  struct fib6_dump_arg {
  	struct net *net;
  	struct notifier_block *nb;
b7a595577   Jiri Pirko   net: fib_notifier...
394
  	struct netlink_ext_ack *extack;
e1ee0a5ba   Ido Schimmel   ipv6: fib: Dump t...
395
  };
55c894f76   Jiri Pirko   net: fib_notifier...
396
  static int fib6_rt_dump(struct fib6_info *rt, struct fib6_dump_arg *arg)
e1ee0a5ba   Ido Schimmel   ipv6: fib: Dump t...
397
  {
caafb2509   Ido Schimmel   ipv6: Remove old ...
398
  	enum fib_event_type fib_event = FIB_EVENT_ENTRY_REPLACE;
9c6ecd3cf   Ido Schimmel   ipv6: Only Replay...
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
  	int err;
  
  	if (!rt || rt == arg->net->ipv6.fib6_null_entry)
  		return 0;
  
  	if (rt->fib6_nsiblings)
  		err = call_fib6_multipath_entry_notifier(arg->nb, fib_event,
  							 rt,
  							 rt->fib6_nsiblings,
  							 arg->extack);
  	else
  		err = call_fib6_entry_notifier(arg->nb, fib_event, rt,
  					       arg->extack);
  
  	return err;
  }
e1ee0a5ba   Ido Schimmel   ipv6: fib: Dump t...
415
416
  static int fib6_node_dump(struct fib6_walker *w)
  {
caafb2509   Ido Schimmel   ipv6: Remove old ...
417
  	int err;
9c6ecd3cf   Ido Schimmel   ipv6: Only Replay...
418

caafb2509   Ido Schimmel   ipv6: Remove old ...
419
  	err = fib6_rt_dump(w->leaf, w->args);
e1ee0a5ba   Ido Schimmel   ipv6: fib: Dump t...
420
  	w->leaf = NULL;
55c894f76   Jiri Pirko   net: fib_notifier...
421
  	return err;
e1ee0a5ba   Ido Schimmel   ipv6: fib: Dump t...
422
  }
55c894f76   Jiri Pirko   net: fib_notifier...
423
424
  static int fib6_table_dump(struct net *net, struct fib6_table *tb,
  			   struct fib6_walker *w)
e1ee0a5ba   Ido Schimmel   ipv6: fib: Dump t...
425
  {
55c894f76   Jiri Pirko   net: fib_notifier...
426
  	int err;
e1ee0a5ba   Ido Schimmel   ipv6: fib: Dump t...
427
  	w->root = &tb->tb6_root;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
428
  	spin_lock_bh(&tb->tb6_lock);
55c894f76   Jiri Pirko   net: fib_notifier...
429
  	err = fib6_walk(net, w);
66f5d6ce5   Wei Wang   ipv6: replace rwl...
430
  	spin_unlock_bh(&tb->tb6_lock);
55c894f76   Jiri Pirko   net: fib_notifier...
431
  	return err;
e1ee0a5ba   Ido Schimmel   ipv6: fib: Dump t...
432
433
434
  }
  
  /* Called with rcu_read_lock() */
b7a595577   Jiri Pirko   net: fib_notifier...
435
436
  int fib6_tables_dump(struct net *net, struct notifier_block *nb,
  		     struct netlink_ext_ack *extack)
e1ee0a5ba   Ido Schimmel   ipv6: fib: Dump t...
437
438
439
440
  {
  	struct fib6_dump_arg arg;
  	struct fib6_walker *w;
  	unsigned int h;
55c894f76   Jiri Pirko   net: fib_notifier...
441
  	int err = 0;
e1ee0a5ba   Ido Schimmel   ipv6: fib: Dump t...
442
443
444
445
446
447
448
449
  
  	w = kzalloc(sizeof(*w), GFP_ATOMIC);
  	if (!w)
  		return -ENOMEM;
  
  	w->func = fib6_node_dump;
  	arg.net = net;
  	arg.nb = nb;
b7a595577   Jiri Pirko   net: fib_notifier...
450
  	arg.extack = extack;
e1ee0a5ba   Ido Schimmel   ipv6: fib: Dump t...
451
452
453
454
455
  	w->args = &arg;
  
  	for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
  		struct hlist_head *head = &net->ipv6.fib_table_hash[h];
  		struct fib6_table *tb;
55c894f76   Jiri Pirko   net: fib_notifier...
456
457
458
459
460
  		hlist_for_each_entry_rcu(tb, head, tb6_hlist) {
  			err = fib6_table_dump(net, tb, w);
  			if (err < 0)
  				goto out;
  		}
e1ee0a5ba   Ido Schimmel   ipv6: fib: Dump t...
461
  	}
55c894f76   Jiri Pirko   net: fib_notifier...
462
  out:
e1ee0a5ba   Ido Schimmel   ipv6: fib: Dump t...
463
  	kfree(w);
55c894f76   Jiri Pirko   net: fib_notifier...
464
  	return err;
e1ee0a5ba   Ido Schimmel   ipv6: fib: Dump t...
465
  }
94b2cfe02   Hannes Frederic Sowa   ipv6: minor fib6 ...
466
  static int fib6_dump_node(struct fib6_walker *w)
1b43af548   Patrick McHardy   [IPV6]: Increase ...
467
468
  {
  	int res;
8d1c802b2   David Ahern   net/ipv6: Flip FI...
469
  	struct fib6_info *rt;
1b43af548   Patrick McHardy   [IPV6]: Increase ...
470

66f5d6ce5   Wei Wang   ipv6: replace rwl...
471
  	for_each_fib6_walker_rt(w) {
1e47b4837   Stefano Brivio   ipv6: Dump route ...
472
  		res = rt6_dump_route(rt, w->args, w->skip_in_node);
bf9a8a061   Stefano Brivio   ipv6/route: Chang...
473
  		if (res >= 0) {
1b43af548   Patrick McHardy   [IPV6]: Increase ...
474
475
  			/* Frame is full, suspend walking */
  			w->leaf = rt;
1e47b4837   Stefano Brivio   ipv6: Dump route ...
476
477
478
479
480
  
  			/* We'll restart from this node, so if some routes were
  			 * already dumped, skip them next time.
  			 */
  			w->skip_in_node += res;
1b43af548   Patrick McHardy   [IPV6]: Increase ...
481
482
  			return 1;
  		}
1e47b4837   Stefano Brivio   ipv6: Dump route ...
483
  		w->skip_in_node = 0;
beb1afac5   David Ahern   net: ipv6: Add su...
484
485
486
487
488
489
  
  		/* Multipath routes are dumped in one route with the
  		 * RTA_MULTIPATH attribute. Jump 'rt' to point to the
  		 * last sibling of this route (no need to dump the
  		 * sibling routes again)
  		 */
93c2fb253   David Ahern   net/ipv6: Rename ...
490
491
  		if (rt->fib6_nsiblings)
  			rt = list_last_entry(&rt->fib6_siblings,
8d1c802b2   David Ahern   net/ipv6: Flip FI...
492
  					     struct fib6_info,
93c2fb253   David Ahern   net/ipv6: Rename ...
493
  					     fib6_siblings);
1b43af548   Patrick McHardy   [IPV6]: Increase ...
494
495
496
497
498
499
500
  	}
  	w->leaf = NULL;
  	return 0;
  }
  
  static void fib6_dump_end(struct netlink_callback *cb)
  {
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
501
  	struct net *net = sock_net(cb->skb->sk);
94b2cfe02   Hannes Frederic Sowa   ipv6: minor fib6 ...
502
  	struct fib6_walker *w = (void *)cb->args[2];
1b43af548   Patrick McHardy   [IPV6]: Increase ...
503
504
  
  	if (w) {
7891cc818   Herbert Xu   ipv6: Fix fib6_du...
505
506
  		if (cb->args[4]) {
  			cb->args[4] = 0;
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
507
  			fib6_walker_unlink(net, w);
7891cc818   Herbert Xu   ipv6: Fix fib6_du...
508
  		}
1b43af548   Patrick McHardy   [IPV6]: Increase ...
509
510
511
  		cb->args[2] = 0;
  		kfree(w);
  	}
437de07ce   Wang Yufen   ipv6: fix checkpa...
512
  	cb->done = (void *)cb->args[3];
1b43af548   Patrick McHardy   [IPV6]: Increase ...
513
514
515
516
517
518
519
520
521
522
523
524
  	cb->args[1] = 3;
  }
  
  static int fib6_dump_done(struct netlink_callback *cb)
  {
  	fib6_dump_end(cb);
  	return cb->done ? cb->done(cb) : 0;
  }
  
  static int fib6_dump_table(struct fib6_table *table, struct sk_buff *skb,
  			   struct netlink_callback *cb)
  {
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
525
  	struct net *net = sock_net(skb->sk);
94b2cfe02   Hannes Frederic Sowa   ipv6: minor fib6 ...
526
  	struct fib6_walker *w;
1b43af548   Patrick McHardy   [IPV6]: Increase ...
527
528
529
530
531
532
  	int res;
  
  	w = (void *)cb->args[2];
  	w->root = &table->tb6_root;
  
  	if (cb->args[4] == 0) {
2bec5a369   Patrick McHardy   ipv6: fib: fix cr...
533
534
  		w->count = 0;
  		w->skip = 0;
1e47b4837   Stefano Brivio   ipv6: Dump route ...
535
  		w->skip_in_node = 0;
2bec5a369   Patrick McHardy   ipv6: fib: fix cr...
536

66f5d6ce5   Wei Wang   ipv6: replace rwl...
537
  		spin_lock_bh(&table->tb6_lock);
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
538
  		res = fib6_walk(net, w);
66f5d6ce5   Wei Wang   ipv6: replace rwl...
539
  		spin_unlock_bh(&table->tb6_lock);
2bec5a369   Patrick McHardy   ipv6: fib: fix cr...
540
  		if (res > 0) {
1b43af548   Patrick McHardy   [IPV6]: Increase ...
541
  			cb->args[4] = 1;
2bec5a369   Patrick McHardy   ipv6: fib: fix cr...
542
543
  			cb->args[5] = w->root->fn_sernum;
  		}
1b43af548   Patrick McHardy   [IPV6]: Increase ...
544
  	} else {
2bec5a369   Patrick McHardy   ipv6: fib: fix cr...
545
546
547
548
549
550
  		if (cb->args[5] != w->root->fn_sernum) {
  			/* Begin at the root if the tree changed */
  			cb->args[5] = w->root->fn_sernum;
  			w->state = FWS_INIT;
  			w->node = w->root;
  			w->skip = w->count;
1e47b4837   Stefano Brivio   ipv6: Dump route ...
551
  			w->skip_in_node = 0;
2bec5a369   Patrick McHardy   ipv6: fib: fix cr...
552
553
  		} else
  			w->skip = 0;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
554
  		spin_lock_bh(&table->tb6_lock);
1b43af548   Patrick McHardy   [IPV6]: Increase ...
555
  		res = fib6_walk_continue(w);
66f5d6ce5   Wei Wang   ipv6: replace rwl...
556
  		spin_unlock_bh(&table->tb6_lock);
7891cc818   Herbert Xu   ipv6: Fix fib6_du...
557
  		if (res <= 0) {
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
558
  			fib6_walker_unlink(net, w);
7891cc818   Herbert Xu   ipv6: Fix fib6_du...
559
  			cb->args[4] = 0;
1b43af548   Patrick McHardy   [IPV6]: Increase ...
560
  		}
1b43af548   Patrick McHardy   [IPV6]: Increase ...
561
  	}
7891cc818   Herbert Xu   ipv6: Fix fib6_du...
562

1b43af548   Patrick McHardy   [IPV6]: Increase ...
563
564
  	return res;
  }
c127ea2c4   Thomas Graf   [IPv6]: Use rtnl ...
565
  static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
1b43af548   Patrick McHardy   [IPV6]: Increase ...
566
  {
564c91f7e   Stefano Brivio   fib_frontend, ip6...
567
568
  	struct rt6_rtnl_dump_arg arg = { .filter.dump_exceptions = true,
  					 .filter.dump_routes = true };
e8ba330ac   David Ahern   rtnetlink: Update...
569
  	const struct nlmsghdr *nlh = cb->nlh;
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
570
  	struct net *net = sock_net(skb->sk);
1b43af548   Patrick McHardy   [IPV6]: Increase ...
571
572
  	unsigned int h, s_h;
  	unsigned int e = 0, s_e;
94b2cfe02   Hannes Frederic Sowa   ipv6: minor fib6 ...
573
  	struct fib6_walker *w;
1b43af548   Patrick McHardy   [IPV6]: Increase ...
574
  	struct fib6_table *tb;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
575
  	struct hlist_head *head;
1b43af548   Patrick McHardy   [IPV6]: Increase ...
576
  	int res = 0;
e8ba330ac   David Ahern   rtnetlink: Update...
577
  	if (cb->strict_check) {
4724676d5   David Ahern   net: Add struct f...
578
  		int err;
e8ba330ac   David Ahern   rtnetlink: Update...
579

effe67926   David Ahern   net: Enable kerne...
580
  		err = ip_valid_fib_dump_req(net, nlh, &arg.filter, cb);
e8ba330ac   David Ahern   rtnetlink: Update...
581
582
  		if (err < 0)
  			return err;
13e38901d   David Ahern   net/ipv6: Plumb s...
583
584
  	} else if (nlmsg_len(nlh) >= sizeof(struct rtmsg)) {
  		struct rtmsg *rtm = nlmsg_data(nlh);
e8ba330ac   David Ahern   rtnetlink: Update...
585

ef11209d4   Stefano Brivio   Revert "net/ipv6:...
586
587
  		if (rtm->rtm_flags & RTM_F_PREFIX)
  			arg.filter.flags = RTM_F_PREFIX;
13e38901d   David Ahern   net/ipv6: Plumb s...
588
  	}
1b43af548   Patrick McHardy   [IPV6]: Increase ...
589
590
  
  	w = (void *)cb->args[2];
507c9b1e0   David S. Miller   ipv6: Various cle...
591
  	if (!w) {
1b43af548   Patrick McHardy   [IPV6]: Increase ...
592
593
594
595
596
597
598
599
600
601
602
  		/* New dump:
  		 *
  		 * 1. hook callback destructor.
  		 */
  		cb->args[3] = (long)cb->done;
  		cb->done = fib6_dump_done;
  
  		/*
  		 * 2. allocate and initialize walker.
  		 */
  		w = kzalloc(sizeof(*w), GFP_ATOMIC);
507c9b1e0   David S. Miller   ipv6: Various cle...
603
  		if (!w)
1b43af548   Patrick McHardy   [IPV6]: Increase ...
604
605
606
607
608
609
610
  			return -ENOMEM;
  		w->func = fib6_dump_node;
  		cb->args[2] = (long)w;
  	}
  
  	arg.skb = skb;
  	arg.cb = cb;
191cd5825   Brian Haley   netns: Add networ...
611
  	arg.net = net;
1b43af548   Patrick McHardy   [IPV6]: Increase ...
612
  	w->args = &arg;
13e38901d   David Ahern   net/ipv6: Plumb s...
613
614
615
  	if (arg.filter.table_id) {
  		tb = fib6_get_table(net, arg.filter.table_id);
  		if (!tb) {
41b4bd986   Sabrina Dubroca   net: don't return...
616
  			if (rtnl_msg_family(cb->nlh) != PF_INET6)
e22d0bfa0   Alexey Kodanev   ipv6: properly ch...
617
  				goto out;
ae677bbb4   David Ahern   net: Don't return...
618

13e38901d   David Ahern   net/ipv6: Plumb s...
619
620
621
  			NL_SET_ERR_MSG_MOD(cb->extack, "FIB table does not exist");
  			return -ENOENT;
  		}
73155879b   David Ahern   ipv6: Fix dump of...
622
623
624
625
626
  		if (!cb->args[0]) {
  			res = fib6_dump_table(tb, skb, cb);
  			if (!res)
  				cb->args[0] = 1;
  		}
13e38901d   David Ahern   net/ipv6: Plumb s...
627
628
629
630
631
  		goto out;
  	}
  
  	s_h = cb->args[0];
  	s_e = cb->args[1];
e67f88dd1   Eric Dumazet   net: dont hold rt...
632
  	rcu_read_lock();
a33bc5c15   Neil Horman   xfrm: select sane...
633
  	for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_e = 0) {
1b43af548   Patrick McHardy   [IPV6]: Increase ...
634
  		e = 0;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
635
  		head = &net->ipv6.fib_table_hash[h];
b67bfe0d4   Sasha Levin   hlist: drop the n...
636
  		hlist_for_each_entry_rcu(tb, head, tb6_hlist) {
1b43af548   Patrick McHardy   [IPV6]: Increase ...
637
638
639
640
  			if (e < s_e)
  				goto next;
  			res = fib6_dump_table(tb, skb, cb);
  			if (res != 0)
13e38901d   David Ahern   net/ipv6: Plumb s...
641
  				goto out_unlock;
1b43af548   Patrick McHardy   [IPV6]: Increase ...
642
643
644
645
  next:
  			e++;
  		}
  	}
13e38901d   David Ahern   net/ipv6: Plumb s...
646
  out_unlock:
e67f88dd1   Eric Dumazet   net: dont hold rt...
647
  	rcu_read_unlock();
1b43af548   Patrick McHardy   [IPV6]: Increase ...
648
649
  	cb->args[1] = e;
  	cb->args[0] = h;
13e38901d   David Ahern   net/ipv6: Plumb s...
650
  out:
1b43af548   Patrick McHardy   [IPV6]: Increase ...
651
652
653
654
655
  	res = res < 0 ? res : skb->len;
  	if (res <= 0)
  		fib6_dump_end(cb);
  	return res;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
656

8d1c802b2   David Ahern   net/ipv6: Flip FI...
657
  void fib6_metric_set(struct fib6_info *f6i, int metric, u32 val)
d4ead6b34   David Ahern   net/ipv6: move me...
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
  {
  	if (!f6i)
  		return;
  
  	if (f6i->fib6_metrics == &dst_default_metrics) {
  		struct dst_metrics *p = kzalloc(sizeof(*p), GFP_ATOMIC);
  
  		if (!p)
  			return;
  
  		refcount_set(&p->refcnt, 1);
  		f6i->fib6_metrics = p;
  	}
  
  	f6i->fib6_metrics->metrics[metric - 1] = val;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
674
675
676
677
678
679
680
  /*
   *	Routing Table
   *
   *	return the appropriate node for a routing tree "add" operation
   *	by either creating and inserting or by returning an existing
   *	node.
   */
81eb8447d   Wei Wang   ipv6: take care o...
681
682
  static struct fib6_node *fib6_add_1(struct net *net,
  				    struct fib6_table *table,
66f5d6ce5   Wei Wang   ipv6: replace rwl...
683
684
685
686
687
  				    struct fib6_node *root,
  				    struct in6_addr *addr, int plen,
  				    int offset, int allow_create,
  				    int replace_required,
  				    struct netlink_ext_ack *extack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
688
689
690
691
692
  {
  	struct fib6_node *fn, *in, *ln;
  	struct fib6_node *pn = NULL;
  	struct rt6key *key;
  	int	bit;
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
693
  	__be32	dir = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
694
695
696
697
698
699
700
701
702
  
  	RT6_TRACE("fib6_add_1
  ");
  
  	/* insert node in tree */
  
  	fn = root;
  
  	do {
8d1c802b2   David Ahern   net/ipv6: Flip FI...
703
  		struct fib6_info *leaf = rcu_dereference_protected(fn->leaf,
66f5d6ce5   Wei Wang   ipv6: replace rwl...
704
705
  					    lockdep_is_held(&table->tb6_lock));
  		key = (struct rt6key *)((u8 *)leaf + offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
706
707
708
709
710
  
  		/*
  		 *	Prefix match
  		 */
  		if (plen < fn->fn_bit ||
4a287eba2   Matti Vaittinen   IPv6 routing, NLM...
711
  		    !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit)) {
14df015bb   Matti Vaittinen   IPV6 Fix a crash ...
712
713
  			if (!allow_create) {
  				if (replace_required) {
d5d531cb5   David Ahern   net: ipv6: Add ex...
714
715
  					NL_SET_ERR_MSG(extack,
  						       "Can not replace route - no match found");
f32138319   Joe Perches   net: ipv6: Standa...
716
717
  					pr_warn("Can't replace route, no match found
  ");
14df015bb   Matti Vaittinen   IPV6 Fix a crash ...
718
719
  					return ERR_PTR(-ENOENT);
  				}
f32138319   Joe Perches   net: ipv6: Standa...
720
721
  				pr_warn("NLM_F_CREATE should be set when creating new route
  ");
14df015bb   Matti Vaittinen   IPV6 Fix a crash ...
722
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
723
  			goto insert_above;
4a287eba2   Matti Vaittinen   IPv6 routing, NLM...
724
  		}
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
725

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
726
727
728
  		/*
  		 *	Exact match ?
  		 */
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
729

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
730
731
  		if (plen == fn->fn_bit) {
  			/* clean up an intermediate node */
507c9b1e0   David S. Miller   ipv6: Various cle...
732
  			if (!(fn->fn_flags & RTN_RTINFO)) {
66f5d6ce5   Wei Wang   ipv6: replace rwl...
733
  				RCU_INIT_POINTER(fn->leaf, NULL);
93531c674   David Ahern   net/ipv6: separat...
734
  				fib6_info_release(leaf);
4512c43ea   Wei Wang   ipv6: remove null...
735
736
737
  			/* remove null_entry in the root node */
  			} else if (fn->fn_flags & RTN_TL_ROOT &&
  				   rcu_access_pointer(fn->leaf) ==
421842ede   David Ahern   net/ipv6: Add fib...
738
  				   net->ipv6.fib6_null_entry) {
4512c43ea   Wei Wang   ipv6: remove null...
739
  				RCU_INIT_POINTER(fn->leaf, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
740
  			}
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
741

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
742
743
744
745
746
747
  			return fn;
  		}
  
  		/*
  		 *	We have more bits to go
  		 */
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
748

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
749
  		/* Try to walk down on tree. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
750
751
  		dir = addr_bit_set(addr, fn->fn_bit);
  		pn = fn;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
752
753
754
755
756
  		fn = dir ?
  		     rcu_dereference_protected(fn->right,
  					lockdep_is_held(&table->tb6_lock)) :
  		     rcu_dereference_protected(fn->left,
  					lockdep_is_held(&table->tb6_lock));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
757
  	} while (fn);
14df015bb   Matti Vaittinen   IPV6 Fix a crash ...
758
  	if (!allow_create) {
4a287eba2   Matti Vaittinen   IPv6 routing, NLM...
759
760
761
762
763
764
765
766
767
  		/* We should not create new node because
  		 * NLM_F_REPLACE was specified without NLM_F_CREATE
  		 * I assume it is safe to require NLM_F_CREATE when
  		 * REPLACE flag is used! Later we may want to remove the
  		 * check for replace_required, because according
  		 * to netlink specification, NLM_F_CREATE
  		 * MUST be specified if new route is created.
  		 * That would keep IPv6 consistent with IPv4
  		 */
14df015bb   Matti Vaittinen   IPV6 Fix a crash ...
768
  		if (replace_required) {
d5d531cb5   David Ahern   net: ipv6: Add ex...
769
770
  			NL_SET_ERR_MSG(extack,
  				       "Can not replace route - no match found");
f32138319   Joe Perches   net: ipv6: Standa...
771
772
  			pr_warn("Can't replace route, no match found
  ");
14df015bb   Matti Vaittinen   IPV6 Fix a crash ...
773
774
  			return ERR_PTR(-ENOENT);
  		}
f32138319   Joe Perches   net: ipv6: Standa...
775
776
  		pr_warn("NLM_F_CREATE should be set when creating new route
  ");
4a287eba2   Matti Vaittinen   IPv6 routing, NLM...
777
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
778
779
780
781
  	/*
  	 *	We walked to the bottom of tree.
  	 *	Create new leaf node without children.
  	 */
81eb8447d   Wei Wang   ipv6: take care o...
782
  	ln = node_alloc(net);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
783

507c9b1e0   David S. Miller   ipv6: Various cle...
784
  	if (!ln)
188c517a0   Lin Ming   ipv6: return errn...
785
  		return ERR_PTR(-ENOMEM);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
786
  	ln->fn_bit = plen;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
787
  	RCU_INIT_POINTER(ln->parent, pn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
788
789
  
  	if (dir)
66f5d6ce5   Wei Wang   ipv6: replace rwl...
790
  		rcu_assign_pointer(pn->right, ln);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
791
  	else
66f5d6ce5   Wei Wang   ipv6: replace rwl...
792
  		rcu_assign_pointer(pn->left, ln);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
793
794
795
796
797
798
  
  	return ln;
  
  
  insert_above:
  	/*
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
799
  	 * split since we don't have a common prefix anymore or
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
800
801
802
803
804
  	 * we have a less significant route.
  	 * we've to insert an intermediate node on the list
  	 * this new node will point to the one we need to create
  	 * and the current
  	 */
66f5d6ce5   Wei Wang   ipv6: replace rwl...
805
806
  	pn = rcu_dereference_protected(fn->parent,
  				       lockdep_is_held(&table->tb6_lock));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
807
808
  
  	/* find 1st bit in difference between the 2 addrs.
971f359dd   YOSHIFUJI Hideaki   [IPV6]: Put addr_...
809
  	   See comment in __ipv6_addr_diff: bit may be an invalid value,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
810
811
  	   but if it is >= plen, the value is ignored in any case.
  	 */
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
812

9225b2305   fan.du   net: ipv6 elimina...
813
  	bit = __ipv6_addr_diff(addr, &key->addr, sizeof(*addr));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
814

1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
815
816
  	/*
  	 *		(intermediate)[in]
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
817
818
819
820
  	 *	          /	   \
  	 *	(new leaf node)[ln] (old node)[fn]
  	 */
  	if (plen > bit) {
81eb8447d   Wei Wang   ipv6: take care o...
821
822
  		in = node_alloc(net);
  		ln = node_alloc(net);
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
823

507c9b1e0   David S. Miller   ipv6: Various cle...
824
  		if (!in || !ln) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
825
  			if (in)
81eb8447d   Wei Wang   ipv6: take care o...
826
  				node_free_immediate(net, in);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
827
  			if (ln)
81eb8447d   Wei Wang   ipv6: take care o...
828
  				node_free_immediate(net, ln);
188c517a0   Lin Ming   ipv6: return errn...
829
  			return ERR_PTR(-ENOMEM);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
830
  		}
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
831
832
  		/*
  		 * new intermediate node.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
833
834
835
836
837
838
839
  		 * RTN_RTINFO will
  		 * be off since that an address that chooses one of
  		 * the branches would not match less specific routes
  		 * in the other branch
  		 */
  
  		in->fn_bit = bit;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
840
  		RCU_INIT_POINTER(in->parent, pn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
841
  		in->leaf = fn->leaf;
5ea715289   Eric Dumazet   ipv6: broadly use...
842
843
  		fib6_info_hold(rcu_dereference_protected(in->leaf,
  				lockdep_is_held(&table->tb6_lock)));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
844

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
845
846
  		/* update parent pointer */
  		if (dir)
66f5d6ce5   Wei Wang   ipv6: replace rwl...
847
  			rcu_assign_pointer(pn->right, in);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
848
  		else
66f5d6ce5   Wei Wang   ipv6: replace rwl...
849
  			rcu_assign_pointer(pn->left, in);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
850
851
  
  		ln->fn_bit = plen;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
852
853
  		RCU_INIT_POINTER(ln->parent, in);
  		rcu_assign_pointer(fn->parent, in);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
854

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
855
  		if (addr_bit_set(addr, bit)) {
66f5d6ce5   Wei Wang   ipv6: replace rwl...
856
857
  			rcu_assign_pointer(in->right, ln);
  			rcu_assign_pointer(in->left, fn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
858
  		} else {
66f5d6ce5   Wei Wang   ipv6: replace rwl...
859
860
  			rcu_assign_pointer(in->left, ln);
  			rcu_assign_pointer(in->right, fn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
861
862
  		}
  	} else { /* plen <= bit */
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
863
  		/*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
864
865
866
867
  		 *		(new leaf node)[ln]
  		 *	          /	   \
  		 *	     (old node)[fn] NULL
  		 */
81eb8447d   Wei Wang   ipv6: take care o...
868
  		ln = node_alloc(net);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
869

507c9b1e0   David S. Miller   ipv6: Various cle...
870
  		if (!ln)
188c517a0   Lin Ming   ipv6: return errn...
871
  			return ERR_PTR(-ENOMEM);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
872
873
  
  		ln->fn_bit = plen;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
874
  		RCU_INIT_POINTER(ln->parent, pn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
875
876
  
  		if (addr_bit_set(&key->addr, plen))
66f5d6ce5   Wei Wang   ipv6: replace rwl...
877
  			RCU_INIT_POINTER(ln->right, fn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
878
  		else
66f5d6ce5   Wei Wang   ipv6: replace rwl...
879
880
881
  			RCU_INIT_POINTER(ln->left, fn);
  
  		rcu_assign_pointer(fn->parent, ln);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
882

66f5d6ce5   Wei Wang   ipv6: replace rwl...
883
884
885
886
  		if (dir)
  			rcu_assign_pointer(pn->right, ln);
  		else
  			rcu_assign_pointer(pn->left, ln);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
887
888
889
  	}
  	return ln;
  }
7d88d8b55   David Ahern   ipv6: Refactor fi...
890
891
892
  static void __fib6_drop_pcpu_from(struct fib6_nh *fib6_nh,
  				  const struct fib6_info *match,
  				  const struct fib6_table *table)
e715b6d3a   Florian Westphal   net: fib6: conver...
893
  {
5bcaa41b9   David Ahern   net/ipv6: Move re...
894
  	int cpu;
e715b6d3a   Florian Westphal   net: fib6: conver...
895

f40b6ae2b   David Ahern   ipv6: Move pcpu c...
896
897
  	if (!fib6_nh->rt6i_pcpu)
  		return;
5bcaa41b9   David Ahern   net/ipv6: Move re...
898
899
900
901
902
903
  	/* release the reference to this fib entry from
  	 * all of its cached pcpu routes
  	 */
  	for_each_possible_cpu(cpu) {
  		struct rt6_info **ppcpu_rt;
  		struct rt6_info *pcpu_rt;
e715b6d3a   Florian Westphal   net: fib6: conver...
904

f40b6ae2b   David Ahern   ipv6: Move pcpu c...
905
  		ppcpu_rt = per_cpu_ptr(fib6_nh->rt6i_pcpu, cpu);
5bcaa41b9   David Ahern   net/ipv6: Move re...
906
  		pcpu_rt = *ppcpu_rt;
7d88d8b55   David Ahern   ipv6: Refactor fi...
907
908
909
910
911
912
913
  
  		/* only dropping the 'from' reference if the cached route
  		 * is using 'match'. The cached pcpu_rt->from only changes
  		 * from a fib6_info to NULL (ip6_dst_destroy); it can never
  		 * change from one fib6_info reference to another
  		 */
  		if (pcpu_rt && rcu_access_pointer(pcpu_rt->from) == match) {
a68886a69   David Ahern   net/ipv6: Make fr...
914
  			struct fib6_info *from;
e715b6d3a   Florian Westphal   net: fib6: conver...
915

0e2338749   Eric Dumazet   ipv6: fix races i...
916
  			from = xchg((__force struct fib6_info **)&pcpu_rt->from, NULL);
a68886a69   David Ahern   net/ipv6: Make fr...
917
  			fib6_info_release(from);
5bcaa41b9   David Ahern   net/ipv6: Move re...
918
  		}
e5fd387ad   Michal Kubeček   ipv6: do not over...
919
  	}
e5fd387ad   Michal Kubeček   ipv6: do not over...
920
  }
2ab75bfb1   David Ahern   ipv6: Handle all ...
921
922
923
924
925
926
927
928
929
930
931
932
  struct fib6_nh_pcpu_arg {
  	struct fib6_info	*from;
  	const struct fib6_table *table;
  };
  
  static int fib6_nh_drop_pcpu_from(struct fib6_nh *nh, void *_arg)
  {
  	struct fib6_nh_pcpu_arg *arg = _arg;
  
  	__fib6_drop_pcpu_from(nh, arg->from, arg->table);
  	return 0;
  }
7d88d8b55   David Ahern   ipv6: Refactor fi...
933
934
935
  static void fib6_drop_pcpu_from(struct fib6_info *f6i,
  				const struct fib6_table *table)
  {
7d88d8b55   David Ahern   ipv6: Refactor fi...
936
937
938
939
940
  	/* Make sure rt6_make_pcpu_route() wont add other percpu routes
  	 * while we are cleaning them here.
  	 */
  	f6i->fib6_destroying = 1;
  	mb(); /* paired with the cmpxchg() in rt6_make_pcpu_route() */
2ab75bfb1   David Ahern   ipv6: Handle all ...
941
942
943
944
945
946
947
948
949
950
951
952
953
954
  	if (f6i->nh) {
  		struct fib6_nh_pcpu_arg arg = {
  			.from = f6i,
  			.table = table
  		};
  
  		nexthop_for_each_fib6_nh(f6i->nh, fib6_nh_drop_pcpu_from,
  					 &arg);
  	} else {
  		struct fib6_nh *fib6_nh;
  
  		fib6_nh = f6i->fib6_nh;
  		__fib6_drop_pcpu_from(fib6_nh, f6i, table);
  	}
7d88d8b55   David Ahern   ipv6: Refactor fi...
955
  }
8d1c802b2   David Ahern   net/ipv6: Flip FI...
956
  static void fib6_purge_rt(struct fib6_info *rt, struct fib6_node *fn,
6e9e16e61   Hannes Frederic Sowa   ipv6: replacing a...
957
958
  			  struct net *net)
  {
93c2fb253   David Ahern   net/ipv6: Rename ...
959
  	struct fib6_table *table = rt->fib6_table;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
960

69363e37d   Sean Tranchetti   net: ipv6: fib: f...
961
962
  	/* Flush all cached dst in exception table */
  	rt6_flush_exceptions(rt);
f40b6ae2b   David Ahern   ipv6: Move pcpu c...
963
  	fib6_drop_pcpu_from(rt, table);
61fb0d016   Eric Dumazet   ipv6: prevent pos...
964

f88d8ea67   David Ahern   ipv6: Plumb suppo...
965
966
  	if (rt->nh && !list_empty(&rt->nh_list))
  		list_del_init(&rt->nh_list);
f05713e09   Eric Dumazet   ipv6: convert fib...
967
  	if (refcount_read(&rt->fib6_ref) != 1) {
6e9e16e61   Hannes Frederic Sowa   ipv6: replacing a...
968
969
970
971
972
973
974
  		/* This route is used as dummy address holder in some split
  		 * nodes. It is not leaked, but it still holds other resources,
  		 * which must be released in time. So, scan ascendant nodes
  		 * and replace dummy references to this route with references
  		 * to still alive ones.
  		 */
  		while (fn) {
8d1c802b2   David Ahern   net/ipv6: Flip FI...
975
  			struct fib6_info *leaf = rcu_dereference_protected(fn->leaf,
66f5d6ce5   Wei Wang   ipv6: replace rwl...
976
  					    lockdep_is_held(&table->tb6_lock));
8d1c802b2   David Ahern   net/ipv6: Flip FI...
977
  			struct fib6_info *new_leaf;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
978
979
  			if (!(fn->fn_flags & RTN_RTINFO) && leaf == rt) {
  				new_leaf = fib6_find_prefix(net, table, fn);
5ea715289   Eric Dumazet   ipv6: broadly use...
980
  				fib6_info_hold(new_leaf);
93531c674   David Ahern   net/ipv6: separat...
981

66f5d6ce5   Wei Wang   ipv6: replace rwl...
982
  				rcu_assign_pointer(fn->leaf, new_leaf);
93531c674   David Ahern   net/ipv6: separat...
983
  				fib6_info_release(rt);
6e9e16e61   Hannes Frederic Sowa   ipv6: replacing a...
984
  			}
66f5d6ce5   Wei Wang   ipv6: replace rwl...
985
986
  			fn = rcu_dereference_protected(fn->parent,
  				    lockdep_is_held(&table->tb6_lock));
6e9e16e61   Hannes Frederic Sowa   ipv6: replacing a...
987
  		}
6e9e16e61   Hannes Frederic Sowa   ipv6: replacing a...
988
989
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
990
991
992
  /*
   *	Insert routing information in a node.
   */
8d1c802b2   David Ahern   net/ipv6: Flip FI...
993
  static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
d4ead6b34   David Ahern   net/ipv6: move me...
994
  			    struct nl_info *info,
6c31e5a91   David Ahern   net: Add extack t...
995
  			    struct netlink_ext_ack *extack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
996
  {
8d1c802b2   David Ahern   net/ipv6: Flip FI...
997
  	struct fib6_info *leaf = rcu_dereference_protected(fn->leaf,
93c2fb253   David Ahern   net/ipv6: Rename ...
998
  				    lockdep_is_held(&rt->fib6_table->tb6_lock));
33bd5ac54   David Ahern   net/ipv6: Revert ...
999
  	struct fib6_info *iter = NULL;
8d1c802b2   David Ahern   net/ipv6: Flip FI...
1000
  	struct fib6_info __rcu **ins;
33bd5ac54   David Ahern   net/ipv6: Revert ...
1001
  	struct fib6_info __rcu **fallback_ins = NULL;
507c9b1e0   David S. Miller   ipv6: Various cle...
1002
1003
1004
1005
  	int replace = (info->nlh &&
  		       (info->nlh->nlmsg_flags & NLM_F_REPLACE));
  	int add = (!info->nlh ||
  		   (info->nlh->nlmsg_flags & NLM_F_CREATE));
4a287eba2   Matti Vaittinen   IPv6 routing, NLM...
1006
  	int found = 0;
33bd5ac54   David Ahern   net/ipv6: Revert ...
1007
  	bool rt_can_ecmp = rt6_qualify_for_ecmp(rt);
c10c4279c   Ido Schimmel   ipv6: Notify newl...
1008
  	bool notify_sibling_rt = false;
73483c128   Guillaume Nault   ipv6: report NLM_...
1009
  	u16 nlflags = NLM_F_EXCL;
e5fd387ad   Michal Kubeček   ipv6: do not over...
1010
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1011

33bd5ac54   David Ahern   net/ipv6: Revert ...
1012
  	if (info->nlh && (info->nlh->nlmsg_flags & NLM_F_APPEND))
1f5e29ce7   David Ahern   net: ipv6: add NL...
1013
  		nlflags |= NLM_F_APPEND;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1014
  	ins = &fn->leaf;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1015
  	for (iter = leaf; iter;
8fb11a9a8   David Ahern   net/ipv6: rename ...
1016
  	     iter = rcu_dereference_protected(iter->fib6_next,
93c2fb253   David Ahern   net/ipv6: Rename ...
1017
  				lockdep_is_held(&rt->fib6_table->tb6_lock))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1018
1019
1020
  		/*
  		 *	Search for duplicates
  		 */
93c2fb253   David Ahern   net/ipv6: Rename ...
1021
  		if (iter->fib6_metric == rt->fib6_metric) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1022
1023
1024
  			/*
  			 *	Same priority level
  			 */
507c9b1e0   David S. Miller   ipv6: Various cle...
1025
1026
  			if (info->nlh &&
  			    (info->nlh->nlmsg_flags & NLM_F_EXCL))
4a287eba2   Matti Vaittinen   IPv6 routing, NLM...
1027
  				return -EEXIST;
73483c128   Guillaume Nault   ipv6: report NLM_...
1028
1029
  
  			nlflags &= ~NLM_F_EXCL;
4a287eba2   Matti Vaittinen   IPv6 routing, NLM...
1030
  			if (replace) {
33bd5ac54   David Ahern   net/ipv6: Revert ...
1031
1032
1033
1034
  				if (rt_can_ecmp == rt6_qualify_for_ecmp(iter)) {
  					found++;
  					break;
  				}
e404b8c7c   Benjamin Poirier   ipv6: Fix route r...
1035
  				fallback_ins = fallback_ins ?: ins;
33bd5ac54   David Ahern   net/ipv6: Revert ...
1036
  				goto next_iter;
4a287eba2   Matti Vaittinen   IPv6 routing, NLM...
1037
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1038

f06b7549b   David Ahern   net: ipv6: Compar...
1039
  			if (rt6_duplicate_nexthop(iter, rt)) {
93c2fb253   David Ahern   net/ipv6: Rename ...
1040
1041
1042
  				if (rt->fib6_nsiblings)
  					rt->fib6_nsiblings = 0;
  				if (!(iter->fib6_flags & RTF_EXPIRES))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1043
  					return -EEXIST;
93c2fb253   David Ahern   net/ipv6: Rename ...
1044
  				if (!(rt->fib6_flags & RTF_EXPIRES))
14895687d   David Ahern   net/ipv6: move ex...
1045
  					fib6_clean_expires(iter);
1716a9610   Gao feng   ipv6: fix problem...
1046
  				else
14895687d   David Ahern   net/ipv6: move ex...
1047
  					fib6_set_expires(iter, rt->expires);
15a81b418   David Ahern   net/ipv6: Only up...
1048
1049
1050
1051
  
  				if (rt->fib6_pmtu)
  					fib6_metric_set(iter, RTAX_MTU,
  							rt->fib6_pmtu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1052
1053
  				return -EEXIST;
  			}
33bd5ac54   David Ahern   net/ipv6: Revert ...
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
  			/* If we have the same destination and the same metric,
  			 * but not the same gateway, then the route we try to
  			 * add is sibling to this route, increment our counter
  			 * of siblings, and later we will add our route to the
  			 * list.
  			 * Only static routes (which don't have flag
  			 * RTF_EXPIRES) are used for ECMPv6.
  			 *
  			 * To avoid long list, we only had siblings if the
  			 * route have a gateway.
  			 */
  			if (rt_can_ecmp &&
  			    rt6_qualify_for_ecmp(iter))
  				rt->fib6_nsiblings++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1068
  		}
93c2fb253   David Ahern   net/ipv6: Rename ...
1069
  		if (iter->fib6_metric > rt->fib6_metric)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1070
  			break;
33bd5ac54   David Ahern   net/ipv6: Revert ...
1071
  next_iter:
8fb11a9a8   David Ahern   net/ipv6: rename ...
1072
  		ins = &iter->fib6_next;
275964724   Michal Kubeček   ipv6: fix ECMP ro...
1073
  	}
33bd5ac54   David Ahern   net/ipv6: Revert ...
1074
  	if (fallback_ins && !found) {
e404b8c7c   Benjamin Poirier   ipv6: Fix route r...
1075
1076
1077
  		/* No matching route with same ecmp-able-ness found, replace
  		 * first matching route
  		 */
33bd5ac54   David Ahern   net/ipv6: Revert ...
1078
1079
1080
1081
1082
  		ins = fallback_ins;
  		iter = rcu_dereference_protected(*ins,
  				    lockdep_is_held(&rt->fib6_table->tb6_lock));
  		found++;
  	}
f11e6659c   David S. Miller   [IPV6]: Fix routi...
1083
1084
1085
  	/* Reset round-robin state, if necessary */
  	if (ins == &fn->leaf)
  		fn->rr_ptr = NULL;
51ebd3181   Nicolas Dichtel   ipv6: add support...
1086
  	/* Link this route to others same route. */
33bd5ac54   David Ahern   net/ipv6: Revert ...
1087
1088
  	if (rt->fib6_nsiblings) {
  		unsigned int fib6_nsiblings;
8d1c802b2   David Ahern   net/ipv6: Flip FI...
1089
  		struct fib6_info *sibling, *temp_sibling;
51ebd3181   Nicolas Dichtel   ipv6: add support...
1090

33bd5ac54   David Ahern   net/ipv6: Revert ...
1091
1092
  		/* Find the first route that have the same metric */
  		sibling = leaf;
c10c4279c   Ido Schimmel   ipv6: Notify newl...
1093
  		notify_sibling_rt = true;
33bd5ac54   David Ahern   net/ipv6: Revert ...
1094
1095
1096
1097
1098
1099
1100
1101
1102
  		while (sibling) {
  			if (sibling->fib6_metric == rt->fib6_metric &&
  			    rt6_qualify_for_ecmp(sibling)) {
  				list_add_tail(&rt->fib6_siblings,
  					      &sibling->fib6_siblings);
  				break;
  			}
  			sibling = rcu_dereference_protected(sibling->fib6_next,
  				    lockdep_is_held(&rt->fib6_table->tb6_lock));
c10c4279c   Ido Schimmel   ipv6: Notify newl...
1103
  			notify_sibling_rt = false;
51ebd3181   Nicolas Dichtel   ipv6: add support...
1104
1105
1106
1107
1108
  		}
  		/* For each sibling in the list, increment the counter of
  		 * siblings. BUG() if counters does not match, list of siblings
  		 * is broken!
  		 */
33bd5ac54   David Ahern   net/ipv6: Revert ...
1109
  		fib6_nsiblings = 0;
51ebd3181   Nicolas Dichtel   ipv6: add support...
1110
  		list_for_each_entry_safe(sibling, temp_sibling,
33bd5ac54   David Ahern   net/ipv6: Revert ...
1111
  					 &rt->fib6_siblings, fib6_siblings) {
93c2fb253   David Ahern   net/ipv6: Rename ...
1112
  			sibling->fib6_nsiblings++;
33bd5ac54   David Ahern   net/ipv6: Revert ...
1113
1114
  			BUG_ON(sibling->fib6_nsiblings != rt->fib6_nsiblings);
  			fib6_nsiblings++;
51ebd3181   Nicolas Dichtel   ipv6: add support...
1115
  		}
33bd5ac54   David Ahern   net/ipv6: Revert ...
1116
1117
  		BUG_ON(fib6_nsiblings != rt->fib6_nsiblings);
  		rt6_multipath_rebalance(temp_sibling);
51ebd3181   Nicolas Dichtel   ipv6: add support...
1118
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1119
1120
1121
  	/*
  	 *	insert node
  	 */
4a287eba2   Matti Vaittinen   IPv6 routing, NLM...
1122
1123
  	if (!replace) {
  		if (!add)
f32138319   Joe Perches   net: ipv6: Standa...
1124
1125
  			pr_warn("NLM_F_CREATE should be set when creating new route
  ");
4a287eba2   Matti Vaittinen   IPv6 routing, NLM...
1126
1127
  
  add:
73483c128   Guillaume Nault   ipv6: report NLM_...
1128
  		nlflags |= NLM_F_CREATE;
e715b6d3a   Florian Westphal   net: fib6: conver...
1129

caafb2509   Ido Schimmel   ipv6: Remove old ...
1130
1131
1132
1133
1134
1135
  		/* The route should only be notified if it is the first
  		 * route in the node or if it is added as a sibling
  		 * route to the first route in the node.
  		 */
  		if (!info->skip_notify_kernel &&
  		    (notify_sibling_rt || ins == &fn->leaf)) {
c10c4279c   Ido Schimmel   ipv6: Notify newl...
1136
1137
1138
1139
1140
  			enum fib_event_type fib_event;
  
  			if (notify_sibling_rt)
  				fib_event = FIB_EVENT_ENTRY_APPEND;
  			else
caafb2509   Ido Schimmel   ipv6: Remove old ...
1141
  				fib_event = FIB_EVENT_ENTRY_REPLACE;
d5382fef7   Ido Schimmel   ipv6: Stop sendin...
1142
  			err = call_fib6_entry_notifiers(info->nl_net,
caafb2509   Ido Schimmel   ipv6: Remove old ...
1143
1144
  							fib_event, rt,
  							extack);
54851aa90   Ido Schimmel   ipv6: Unlink sibl...
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
  			if (err) {
  				struct fib6_info *sibling, *next_sibling;
  
  				/* If the route has siblings, then it first
  				 * needs to be unlinked from them.
  				 */
  				if (!rt->fib6_nsiblings)
  					return err;
  
  				list_for_each_entry_safe(sibling, next_sibling,
  							 &rt->fib6_siblings,
  							 fib6_siblings)
  					sibling->fib6_nsiblings--;
  				rt->fib6_nsiblings = 0;
  				list_del_init(&rt->fib6_siblings);
  				rt6_multipath_rebalance(next_sibling);
d5382fef7   Ido Schimmel   ipv6: Stop sendin...
1161
  				return err;
54851aa90   Ido Schimmel   ipv6: Unlink sibl...
1162
  			}
d5382fef7   Ido Schimmel   ipv6: Stop sendin...
1163
  		}
2233000cb   David Ahern   net/ipv6: Move ca...
1164

8fb11a9a8   David Ahern   net/ipv6: rename ...
1165
  		rcu_assign_pointer(rt->fib6_next, iter);
5ea715289   Eric Dumazet   ipv6: broadly use...
1166
  		fib6_info_hold(rt);
93c2fb253   David Ahern   net/ipv6: Rename ...
1167
  		rcu_assign_pointer(rt->fib6_node, fn);
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1168
  		rcu_assign_pointer(*ins, rt);
3b1137fe7   David Ahern   net: ipv6: Change...
1169
1170
  		if (!info->skip_notify)
  			inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
4a287eba2   Matti Vaittinen   IPv6 routing, NLM...
1171
  		info->nl_net->ipv6.rt6_stats->fib_rt_entries++;
507c9b1e0   David S. Miller   ipv6: Various cle...
1172
  		if (!(fn->fn_flags & RTN_RTINFO)) {
4a287eba2   Matti Vaittinen   IPv6 routing, NLM...
1173
1174
1175
  			info->nl_net->ipv6.rt6_stats->fib_route_nodes++;
  			fn->fn_flags |= RTN_RTINFO;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1176

4a287eba2   Matti Vaittinen   IPv6 routing, NLM...
1177
  	} else {
33bd5ac54   David Ahern   net/ipv6: Revert ...
1178
  		int nsiblings;
275964724   Michal Kubeček   ipv6: fix ECMP ro...
1179

4a287eba2   Matti Vaittinen   IPv6 routing, NLM...
1180
1181
1182
  		if (!found) {
  			if (add)
  				goto add;
f32138319   Joe Perches   net: ipv6: Standa...
1183
1184
  			pr_warn("NLM_F_REPLACE set, but no existing node found!
  ");
4a287eba2   Matti Vaittinen   IPv6 routing, NLM...
1185
1186
  			return -ENOENT;
  		}
e715b6d3a   Florian Westphal   net: fib6: conver...
1187

caafb2509   Ido Schimmel   ipv6: Remove old ...
1188
  		if (!info->skip_notify_kernel && ins == &fn->leaf) {
d5382fef7   Ido Schimmel   ipv6: Stop sendin...
1189
1190
1191
1192
1193
1194
  			err = call_fib6_entry_notifiers(info->nl_net,
  							FIB_EVENT_ENTRY_REPLACE,
  							rt, extack);
  			if (err)
  				return err;
  		}
2233000cb   David Ahern   net/ipv6: Move ca...
1195

5ea715289   Eric Dumazet   ipv6: broadly use...
1196
  		fib6_info_hold(rt);
93c2fb253   David Ahern   net/ipv6: Rename ...
1197
  		rcu_assign_pointer(rt->fib6_node, fn);
33bd5ac54   David Ahern   net/ipv6: Revert ...
1198
  		rt->fib6_next = iter->fib6_next;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1199
  		rcu_assign_pointer(*ins, rt);
3b1137fe7   David Ahern   net: ipv6: Change...
1200
1201
  		if (!info->skip_notify)
  			inet6_rt_notify(RTM_NEWROUTE, rt, info, NLM_F_REPLACE);
507c9b1e0   David S. Miller   ipv6: Various cle...
1202
  		if (!(fn->fn_flags & RTN_RTINFO)) {
4a287eba2   Matti Vaittinen   IPv6 routing, NLM...
1203
1204
1205
  			info->nl_net->ipv6.rt6_stats->fib_route_nodes++;
  			fn->fn_flags |= RTN_RTINFO;
  		}
33bd5ac54   David Ahern   net/ipv6: Revert ...
1206
1207
1208
1209
1210
1211
  		nsiblings = iter->fib6_nsiblings;
  		iter->fib6_node = NULL;
  		fib6_purge_rt(iter, fn, info->nl_net);
  		if (rcu_access_pointer(fn->rr_ptr) == iter)
  			fn->rr_ptr = NULL;
  		fib6_info_release(iter);
275964724   Michal Kubeček   ipv6: fix ECMP ro...
1212

33bd5ac54   David Ahern   net/ipv6: Revert ...
1213
  		if (nsiblings) {
275964724   Michal Kubeček   ipv6: fix ECMP ro...
1214
  			/* Replacing an ECMP route, remove all siblings */
33bd5ac54   David Ahern   net/ipv6: Revert ...
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
  			ins = &rt->fib6_next;
  			iter = rcu_dereference_protected(*ins,
  				    lockdep_is_held(&rt->fib6_table->tb6_lock));
  			while (iter) {
  				if (iter->fib6_metric > rt->fib6_metric)
  					break;
  				if (rt6_qualify_for_ecmp(iter)) {
  					*ins = iter->fib6_next;
  					iter->fib6_node = NULL;
  					fib6_purge_rt(iter, fn, info->nl_net);
  					if (rcu_access_pointer(fn->rr_ptr) == iter)
  						fn->rr_ptr = NULL;
  					fib6_info_release(iter);
  					nsiblings--;
  					info->nl_net->ipv6.rt6_stats->fib_rt_entries--;
  				} else {
  					ins = &iter->fib6_next;
  				}
  				iter = rcu_dereference_protected(*ins,
  					lockdep_is_held(&rt->fib6_table->tb6_lock));
275964724   Michal Kubeček   ipv6: fix ECMP ro...
1235
  			}
33bd5ac54   David Ahern   net/ipv6: Revert ...
1236
  			WARN_ON(nsiblings != 0);
275964724   Michal Kubeček   ipv6: fix ECMP ro...
1237
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1238
1239
1240
1241
  	}
  
  	return 0;
  }
8d1c802b2   David Ahern   net/ipv6: Flip FI...
1242
  static void fib6_start_gc(struct net *net, struct fib6_info *rt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1243
  {
417f28bb3   Stephen Hemminger   netns: dont alloc...
1244
  	if (!timer_pending(&net->ipv6.ip6_fib_timer) &&
93c2fb253   David Ahern   net/ipv6: Rename ...
1245
  	    (rt->fib6_flags & RTF_EXPIRES))
417f28bb3   Stephen Hemminger   netns: dont alloc...
1246
  		mod_timer(&net->ipv6.ip6_fib_timer,
847499ce7   Stephen Hemminger   ipv6: use timer p...
1247
  			  jiffies + net->ipv6.sysctl.ip6_rt_gc_interval);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1248
  }
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
1249
  void fib6_force_start_gc(struct net *net)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1250
  {
417f28bb3   Stephen Hemminger   netns: dont alloc...
1251
1252
  	if (!timer_pending(&net->ipv6.ip6_fib_timer))
  		mod_timer(&net->ipv6.ip6_fib_timer,
847499ce7   Stephen Hemminger   ipv6: use timer p...
1253
  			  jiffies + net->ipv6.sysctl.ip6_rt_gc_interval);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1254
  }
8d1c802b2   David Ahern   net/ipv6: Flip FI...
1255
  static void __fib6_update_sernum_upto_root(struct fib6_info *rt,
4a8e56ee2   Ido Schimmel   ipv6: Export sern...
1256
  					   int sernum)
bbd63f06d   Wei Wang   ipv6: update fn_s...
1257
  {
93c2fb253   David Ahern   net/ipv6: Rename ...
1258
1259
  	struct fib6_node *fn = rcu_dereference_protected(rt->fib6_node,
  				lockdep_is_held(&rt->fib6_table->tb6_lock));
bbd63f06d   Wei Wang   ipv6: update fn_s...
1260
1261
1262
1263
1264
  
  	/* paired with smp_rmb() in rt6_get_cookie_safe() */
  	smp_wmb();
  	while (fn) {
  		fn->fn_sernum = sernum;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1265
  		fn = rcu_dereference_protected(fn->parent,
93c2fb253   David Ahern   net/ipv6: Rename ...
1266
  				lockdep_is_held(&rt->fib6_table->tb6_lock));
bbd63f06d   Wei Wang   ipv6: update fn_s...
1267
1268
  	}
  }
8d1c802b2   David Ahern   net/ipv6: Flip FI...
1269
  void fib6_update_sernum_upto_root(struct net *net, struct fib6_info *rt)
4a8e56ee2   Ido Schimmel   ipv6: Export sern...
1270
1271
1272
  {
  	__fib6_update_sernum_upto_root(rt, fib6_new_sernum(net));
  }
cdaa16a4f   David Ahern   ipv6: Add hook to...
1273
1274
1275
1276
1277
1278
1279
  /* allow ipv4 to update sernum via ipv6_stub */
  void fib6_update_sernum_stub(struct net *net, struct fib6_info *f6i)
  {
  	spin_lock_bh(&f6i->fib6_table->tb6_lock);
  	fib6_update_sernum_upto_root(net, f6i);
  	spin_unlock_bh(&f6i->fib6_table->tb6_lock);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1280
1281
1282
1283
  /*
   *	Add routing information to the routing tree.
   *	<destination addr>/<source addr>
   *	with source addr info in sub-trees
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1284
   *	Need to own table->tb6_lock
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1285
   */
8d1c802b2   David Ahern   net/ipv6: Flip FI...
1286
  int fib6_add(struct fib6_node *root, struct fib6_info *rt,
d4ead6b34   David Ahern   net/ipv6: move me...
1287
  	     struct nl_info *info, struct netlink_ext_ack *extack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1288
  {
93c2fb253   David Ahern   net/ipv6: Rename ...
1289
  	struct fib6_table *table = rt->fib6_table;
66729e18d   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
1290
  	struct fib6_node *fn, *pn = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1291
  	int err = -ENOMEM;
4a287eba2   Matti Vaittinen   IPv6 routing, NLM...
1292
1293
  	int allow_create = 1;
  	int replace_required = 0;
812918c46   Hannes Frederic Sowa   ipv6: make fib6 s...
1294
  	int sernum = fib6_new_sernum(info->nl_net);
507c9b1e0   David S. Miller   ipv6: Various cle...
1295
1296
1297
  
  	if (info->nlh) {
  		if (!(info->nlh->nlmsg_flags & NLM_F_CREATE))
4a287eba2   Matti Vaittinen   IPv6 routing, NLM...
1298
  			allow_create = 0;
507c9b1e0   David S. Miller   ipv6: Various cle...
1299
  		if (info->nlh->nlmsg_flags & NLM_F_REPLACE)
4a287eba2   Matti Vaittinen   IPv6 routing, NLM...
1300
1301
1302
  			replace_required = 1;
  	}
  	if (!allow_create && !replace_required)
f32138319   Joe Perches   net: ipv6: Standa...
1303
1304
  		pr_warn("RTM_NEWROUTE with no NLM_F_CREATE or NLM_F_REPLACE
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1305

81eb8447d   Wei Wang   ipv6: take care o...
1306
  	fn = fib6_add_1(info->nl_net, table, root,
93c2fb253   David Ahern   net/ipv6: Rename ...
1307
1308
  			&rt->fib6_dst.addr, rt->fib6_dst.plen,
  			offsetof(struct fib6_info, fib6_dst), allow_create,
bbd63f06d   Wei Wang   ipv6: update fn_s...
1309
  			replace_required, extack);
4a287eba2   Matti Vaittinen   IPv6 routing, NLM...
1310
1311
  	if (IS_ERR(fn)) {
  		err = PTR_ERR(fn);
ae7b4e1f2   Daniel Borkmann   net: fib: fib6_ad...
1312
  		fn = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1313
  		goto out;
188c517a0   Lin Ming   ipv6: return errn...
1314
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1315

66729e18d   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
1316
  	pn = fn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1317
  #ifdef CONFIG_IPV6_SUBTREES
93c2fb253   David Ahern   net/ipv6: Rename ...
1318
  	if (rt->fib6_src.plen) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1319
  		struct fib6_node *sn;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1320
  		if (!rcu_access_pointer(fn->subtree)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
  			struct fib6_node *sfn;
  
  			/*
  			 * Create subtree.
  			 *
  			 *		fn[main tree]
  			 *		|
  			 *		sfn[subtree root]
  			 *		   \
  			 *		    sn[new leaf node]
  			 */
  
  			/* Create subtree root node */
81eb8447d   Wei Wang   ipv6: take care o...
1334
  			sfn = node_alloc(info->nl_net);
507c9b1e0   David S. Miller   ipv6: Various cle...
1335
  			if (!sfn)
348a40027   Wei Wang   ipv6: repair fib6...
1336
  				goto failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1337

5ea715289   Eric Dumazet   ipv6: broadly use...
1338
  			fib6_info_hold(info->nl_net->ipv6.fib6_null_entry);
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1339
  			rcu_assign_pointer(sfn->leaf,
421842ede   David Ahern   net/ipv6: Add fib...
1340
  					   info->nl_net->ipv6.fib6_null_entry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1341
  			sfn->fn_flags = RTN_ROOT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1342
1343
  
  			/* Now add the first leaf node to new subtree */
81eb8447d   Wei Wang   ipv6: take care o...
1344
  			sn = fib6_add_1(info->nl_net, table, sfn,
93c2fb253   David Ahern   net/ipv6: Rename ...
1345
1346
  					&rt->fib6_src.addr, rt->fib6_src.plen,
  					offsetof(struct fib6_info, fib6_src),
bbd63f06d   Wei Wang   ipv6: update fn_s...
1347
  					allow_create, replace_required, extack);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1348

f950c0ecc   Wei Yongjun   ipv6: fix return ...
1349
  			if (IS_ERR(sn)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1350
  				/* If it is failed, discard just allocated
348a40027   Wei Wang   ipv6: repair fib6...
1351
  				   root, and then (in failure) stale node
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1352
1353
  				   in main tree.
  				 */
81eb8447d   Wei Wang   ipv6: take care o...
1354
  				node_free_immediate(info->nl_net, sfn);
188c517a0   Lin Ming   ipv6: return errn...
1355
  				err = PTR_ERR(sn);
348a40027   Wei Wang   ipv6: repair fib6...
1356
  				goto failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1357
1358
1359
  			}
  
  			/* Now link new subtree to main tree */
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1360
1361
  			rcu_assign_pointer(sfn->parent, fn);
  			rcu_assign_pointer(fn->subtree, sfn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1362
  		} else {
81eb8447d   Wei Wang   ipv6: take care o...
1363
  			sn = fib6_add_1(info->nl_net, table, FIB6_SUBTREE(fn),
93c2fb253   David Ahern   net/ipv6: Rename ...
1364
1365
  					&rt->fib6_src.addr, rt->fib6_src.plen,
  					offsetof(struct fib6_info, fib6_src),
bbd63f06d   Wei Wang   ipv6: update fn_s...
1366
  					allow_create, replace_required, extack);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1367

4a287eba2   Matti Vaittinen   IPv6 routing, NLM...
1368
1369
  			if (IS_ERR(sn)) {
  				err = PTR_ERR(sn);
348a40027   Wei Wang   ipv6: repair fib6...
1370
  				goto failure;
188c517a0   Lin Ming   ipv6: return errn...
1371
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1372
  		}
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1373
  		if (!rcu_access_pointer(fn->leaf)) {
591ff9ea5   Wei Wang   ipv6: don't let t...
1374
1375
1376
  			if (fn->fn_flags & RTN_TL_ROOT) {
  				/* put back null_entry for root node */
  				rcu_assign_pointer(fn->leaf,
421842ede   David Ahern   net/ipv6: Add fib...
1377
  					    info->nl_net->ipv6.fib6_null_entry);
591ff9ea5   Wei Wang   ipv6: don't let t...
1378
  			} else {
5ea715289   Eric Dumazet   ipv6: broadly use...
1379
  				fib6_info_hold(rt);
591ff9ea5   Wei Wang   ipv6: don't let t...
1380
1381
  				rcu_assign_pointer(fn->leaf, rt);
  			}
66729e18d   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
1382
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1383
1384
1385
  		fn = sn;
  	}
  #endif
d4ead6b34   David Ahern   net/ipv6: move me...
1386
  	err = fib6_add_rt2node(fn, rt, info, extack);
bbd63f06d   Wei Wang   ipv6: update fn_s...
1387
  	if (!err) {
f88d8ea67   David Ahern   ipv6: Plumb suppo...
1388
1389
  		if (rt->nh)
  			list_add(&rt->nh_list, &rt->nh->f6i_list);
4a8e56ee2   Ido Schimmel   ipv6: Export sern...
1390
  		__fib6_update_sernum_upto_root(rt, sernum);
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
1391
  		fib6_start_gc(info->nl_net, rt);
bbd63f06d   Wei Wang   ipv6: update fn_s...
1392
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1393
1394
  
  out:
66729e18d   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
1395
1396
1397
1398
1399
1400
  	if (err) {
  #ifdef CONFIG_IPV6_SUBTREES
  		/*
  		 * If fib6_add_1 has cleared the old leaf pointer in the
  		 * super-tree leaf node we have to find a new one for it.
  		 */
7bbfe00e0   Wei Wang   ipv6: fix general...
1401
  		if (pn != fn) {
8d1c802b2   David Ahern   net/ipv6: Flip FI...
1402
  			struct fib6_info *pn_leaf =
7bbfe00e0   Wei Wang   ipv6: fix general...
1403
1404
1405
1406
1407
  				rcu_dereference_protected(pn->leaf,
  				    lockdep_is_held(&table->tb6_lock));
  			if (pn_leaf == rt) {
  				pn_leaf = NULL;
  				RCU_INIT_POINTER(pn->leaf, NULL);
93531c674   David Ahern   net/ipv6: separat...
1408
  				fib6_info_release(rt);
66729e18d   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
1409
  			}
7bbfe00e0   Wei Wang   ipv6: fix general...
1410
1411
1412
1413
1414
1415
1416
  			if (!pn_leaf && !(pn->fn_flags & RTN_RTINFO)) {
  				pn_leaf = fib6_find_prefix(info->nl_net, table,
  							   pn);
  #if RT6_DEBUG >= 2
  				if (!pn_leaf) {
  					WARN_ON(!pn_leaf);
  					pn_leaf =
421842ede   David Ahern   net/ipv6: Add fib...
1417
  					    info->nl_net->ipv6.fib6_null_entry;
7bbfe00e0   Wei Wang   ipv6: fix general...
1418
  				}
66729e18d   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
1419
  #endif
93531c674   David Ahern   net/ipv6: separat...
1420
  				fib6_info_hold(pn_leaf);
7bbfe00e0   Wei Wang   ipv6: fix general...
1421
1422
  				rcu_assign_pointer(pn->leaf, pn_leaf);
  			}
66729e18d   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
1423
1424
  		}
  #endif
348a40027   Wei Wang   ipv6: repair fib6...
1425
  		goto failure;
b9b33e7c2   Paolo Abeni   ipv6: keep track ...
1426
1427
  	} else if (fib6_requires_src(rt)) {
  		fib6_routes_require_src_inc(info->nl_net);
66729e18d   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
1428
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1429
  	return err;
348a40027   Wei Wang   ipv6: repair fib6...
1430
  failure:
4512c43ea   Wei Wang   ipv6: remove null...
1431
1432
1433
1434
1435
1436
  	/* fn->leaf could be NULL and fib6_repair_tree() needs to be called if:
  	 * 1. fn is an intermediate node and we failed to add the new
  	 * route to it in both subtree creation failure and fib6_add_rt2node()
  	 * failure case.
  	 * 2. fn is the root node in the table and we fail to add the first
  	 * default route to it.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1437
  	 */
4512c43ea   Wei Wang   ipv6: remove null...
1438
1439
1440
1441
  	if (fn &&
  	    (!(fn->fn_flags & (RTN_RTINFO|RTN_ROOT)) ||
  	     (fn->fn_flags & RTN_TL_ROOT &&
  	      !rcu_access_pointer(fn->leaf))))
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1442
  		fib6_repair_tree(info->nl_net, table, fn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1443
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1444
1445
1446
1447
1448
1449
1450
1451
  }
  
  /*
   *	Routing tree lookup
   *
   */
  
  struct lookup_args {
8d1c802b2   David Ahern   net/ipv6: Flip FI...
1452
  	int			offset;		/* key offset on fib6_info */
b71d1d426   Eric Dumazet   inet: constify ip...
1453
  	const struct in6_addr	*addr;		/* search key			*/
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1454
  };
6454743bc   David Ahern   net/ipv6: Rename ...
1455
1456
  static struct fib6_node *fib6_node_lookup_1(struct fib6_node *root,
  					    struct lookup_args *args)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1457
1458
  {
  	struct fib6_node *fn;
e69a4adc6   Al Viro   [IPV6]: Misc endi...
1459
  	__be32 dir;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1460

825e288ef   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
1461
1462
  	if (unlikely(args->offset == 0))
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
  	/*
  	 *	Descend on a tree
  	 */
  
  	fn = root;
  
  	for (;;) {
  		struct fib6_node *next;
  
  		dir = addr_bit_set(args->addr, fn->fn_bit);
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1473
1474
  		next = dir ? rcu_dereference(fn->right) :
  			     rcu_dereference(fn->left);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1475
1476
1477
1478
1479
  
  		if (next) {
  			fn = next;
  			continue;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1480
1481
  		break;
  	}
507c9b1e0   David S. Miller   ipv6: Various cle...
1482
  	while (fn) {
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1483
1484
1485
  		struct fib6_node *subtree = FIB6_SUBTREE(fn);
  
  		if (subtree || fn->fn_flags & RTN_RTINFO) {
8d1c802b2   David Ahern   net/ipv6: Flip FI...
1486
  			struct fib6_info *leaf = rcu_dereference(fn->leaf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1487
  			struct rt6key *key;
8d1040e80   Wei Wang   ipv6: check fn->l...
1488
1489
1490
1491
  			if (!leaf)
  				goto backtrack;
  
  			key = (struct rt6key *) ((u8 *)leaf + args->offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1492

3fc5e0440   YOSHIFUJI Hideaki   [IPV6] ROUTE: Fix...
1493
1494
  			if (ipv6_prefix_equal(&key->addr, args->addr, key->plen)) {
  #ifdef CONFIG_IPV6_SUBTREES
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1495
  				if (subtree) {
3e3be2758   Hannes Frederic Sowa   ipv6: don't stop ...
1496
  					struct fib6_node *sfn;
6454743bc   David Ahern   net/ipv6: Rename ...
1497
1498
  					sfn = fib6_node_lookup_1(subtree,
  								 args + 1);
3e3be2758   Hannes Frederic Sowa   ipv6: don't stop ...
1499
1500
1501
1502
  					if (!sfn)
  						goto backtrack;
  					fn = sfn;
  				}
3fc5e0440   YOSHIFUJI Hideaki   [IPV6] ROUTE: Fix...
1503
  #endif
3e3be2758   Hannes Frederic Sowa   ipv6: don't stop ...
1504
  				if (fn->fn_flags & RTN_RTINFO)
3fc5e0440   YOSHIFUJI Hideaki   [IPV6] ROUTE: Fix...
1505
1506
  					return fn;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1507
  		}
3e3be2758   Hannes Frederic Sowa   ipv6: don't stop ...
1508
  backtrack:
3fc5e0440   YOSHIFUJI Hideaki   [IPV6] ROUTE: Fix...
1509
1510
  		if (fn->fn_flags & RTN_ROOT)
  			break;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1511
  		fn = rcu_dereference(fn->parent);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1512
1513
1514
1515
  	}
  
  	return NULL;
  }
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1516
1517
  /* called with rcu_read_lock() held
   */
6454743bc   David Ahern   net/ipv6: Rename ...
1518
1519
1520
  struct fib6_node *fib6_node_lookup(struct fib6_node *root,
  				   const struct in6_addr *daddr,
  				   const struct in6_addr *saddr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1521
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1522
  	struct fib6_node *fn;
825e288ef   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
1523
1524
  	struct lookup_args args[] = {
  		{
93c2fb253   David Ahern   net/ipv6: Rename ...
1525
  			.offset = offsetof(struct fib6_info, fib6_dst),
825e288ef   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
1526
1527
  			.addr = daddr,
  		},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1528
  #ifdef CONFIG_IPV6_SUBTREES
825e288ef   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
1529
  		{
93c2fb253   David Ahern   net/ipv6: Rename ...
1530
  			.offset = offsetof(struct fib6_info, fib6_src),
825e288ef   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
1531
1532
  			.addr = saddr,
  		},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1533
  #endif
825e288ef   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
1534
1535
1536
1537
  		{
  			.offset = 0,	/* sentinel */
  		}
  	};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1538

6454743bc   David Ahern   net/ipv6: Rename ...
1539
  	fn = fib6_node_lookup_1(root, daddr ? args : args + 1);
507c9b1e0   David S. Miller   ipv6: Various cle...
1540
  	if (!fn || fn->fn_flags & RTN_TL_ROOT)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1541
1542
1543
1544
1545
1546
1547
1548
  		fn = root;
  
  	return fn;
  }
  
  /*
   *	Get node with specified destination prefix (and source prefix,
   *	if subtrees are used)
38fbeeeec   Wei Wang   ipv6: prepare fib...
1549
1550
1551
1552
1553
1554
   *	exact_match == true means we try to find fn with exact match of
   *	the passed in prefix addr
   *	exact_match == false means we try to find fn with longest prefix
   *	match of the passed in prefix addr. This is useful for finding fn
   *	for cached route as it will be stored in the exception table under
   *	the node with longest prefix length.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1555
   */
437de07ce   Wang Yufen   ipv6: fix checkpa...
1556
1557
  static struct fib6_node *fib6_locate_1(struct fib6_node *root,
  				       const struct in6_addr *addr,
38fbeeeec   Wei Wang   ipv6: prepare fib...
1558
1559
  				       int plen, int offset,
  				       bool exact_match)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1560
  {
38fbeeeec   Wei Wang   ipv6: prepare fib...
1561
  	struct fib6_node *fn, *prev = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1562
1563
  
  	for (fn = root; fn ; ) {
8d1c802b2   David Ahern   net/ipv6: Flip FI...
1564
  		struct fib6_info *leaf = rcu_dereference(fn->leaf);
8d1040e80   Wei Wang   ipv6: check fn->l...
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
  		struct rt6key *key;
  
  		/* This node is being deleted */
  		if (!leaf) {
  			if (plen <= fn->fn_bit)
  				goto out;
  			else
  				goto next;
  		}
  
  		key = (struct rt6key *)((u8 *)leaf + offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1576
1577
1578
1579
1580
1581
  
  		/*
  		 *	Prefix match
  		 */
  		if (plen < fn->fn_bit ||
  		    !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit))
38fbeeeec   Wei Wang   ipv6: prepare fib...
1582
  			goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1583
1584
1585
  
  		if (plen == fn->fn_bit)
  			return fn;
40cb35d5d   Stefano Brivio   ip6_fib: Don't di...
1586
1587
  		if (fn->fn_flags & RTN_RTINFO)
  			prev = fn;
38fbeeeec   Wei Wang   ipv6: prepare fib...
1588

8d1040e80   Wei Wang   ipv6: check fn->l...
1589
  next:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1590
1591
1592
1593
  		/*
  		 *	We have more bits to go
  		 */
  		if (addr_bit_set(addr, fn->fn_bit))
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1594
  			fn = rcu_dereference(fn->right);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1595
  		else
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1596
  			fn = rcu_dereference(fn->left);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1597
  	}
38fbeeeec   Wei Wang   ipv6: prepare fib...
1598
1599
1600
1601
1602
  out:
  	if (exact_match)
  		return NULL;
  	else
  		return prev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1603
  }
437de07ce   Wang Yufen   ipv6: fix checkpa...
1604
1605
  struct fib6_node *fib6_locate(struct fib6_node *root,
  			      const struct in6_addr *daddr, int dst_len,
38fbeeeec   Wei Wang   ipv6: prepare fib...
1606
1607
  			      const struct in6_addr *saddr, int src_len,
  			      bool exact_match)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1608
1609
1610
1611
  {
  	struct fib6_node *fn;
  
  	fn = fib6_locate_1(root, daddr, dst_len,
93c2fb253   David Ahern   net/ipv6: Rename ...
1612
  			   offsetof(struct fib6_info, fib6_dst),
38fbeeeec   Wei Wang   ipv6: prepare fib...
1613
  			   exact_match);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1614
1615
1616
  
  #ifdef CONFIG_IPV6_SUBTREES
  	if (src_len) {
547b792ca   Ilpo Järvinen   net: convert BUG_...
1617
  		WARN_ON(saddr == NULL);
0e80193bd   Wei Wang   ipv6: check fn be...
1618
1619
1620
1621
1622
  		if (fn) {
  			struct fib6_node *subtree = FIB6_SUBTREE(fn);
  
  			if (subtree) {
  				fn = fib6_locate_1(subtree, saddr, src_len,
93c2fb253   David Ahern   net/ipv6: Rename ...
1623
  					   offsetof(struct fib6_info, fib6_src),
38fbeeeec   Wei Wang   ipv6: prepare fib...
1624
  					   exact_match);
0e80193bd   Wei Wang   ipv6: check fn be...
1625
1626
  			}
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1627
1628
  	}
  #endif
507c9b1e0   David S. Miller   ipv6: Various cle...
1629
  	if (fn && fn->fn_flags & RTN_RTINFO)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
  		return fn;
  
  	return NULL;
  }
  
  
  /*
   *	Deletion
   *
   */
8d1c802b2   David Ahern   net/ipv6: Flip FI...
1640
  static struct fib6_info *fib6_find_prefix(struct net *net,
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1641
1642
  					 struct fib6_table *table,
  					 struct fib6_node *fn)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1643
  {
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1644
  	struct fib6_node *child_left, *child_right;
507c9b1e0   David S. Miller   ipv6: Various cle...
1645
  	if (fn->fn_flags & RTN_ROOT)
421842ede   David Ahern   net/ipv6: Add fib...
1646
  		return net->ipv6.fib6_null_entry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1647

507c9b1e0   David S. Miller   ipv6: Various cle...
1648
  	while (fn) {
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
  		child_left = rcu_dereference_protected(fn->left,
  				    lockdep_is_held(&table->tb6_lock));
  		child_right = rcu_dereference_protected(fn->right,
  				    lockdep_is_held(&table->tb6_lock));
  		if (child_left)
  			return rcu_dereference_protected(child_left->leaf,
  					lockdep_is_held(&table->tb6_lock));
  		if (child_right)
  			return rcu_dereference_protected(child_right->leaf,
  					lockdep_is_held(&table->tb6_lock));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1659

7fc33165a   YOSHIFUJI Hideaki   [IPV6] ROUTE: Put...
1660
  		fn = FIB6_SUBTREE(fn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1661
1662
1663
1664
1665
1666
1667
  	}
  	return NULL;
  }
  
  /*
   *	Called to trim the tree of intermediate nodes when possible. "fn"
   *	is the node we want to try and remove.
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1668
   *	Need to own table->tb6_lock
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1669
   */
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
1670
  static struct fib6_node *fib6_repair_tree(struct net *net,
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1671
1672
  					  struct fib6_table *table,
  					  struct fib6_node *fn)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1673
1674
1675
  {
  	int children;
  	int nstate;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1676
  	struct fib6_node *child;
94b2cfe02   Hannes Frederic Sowa   ipv6: minor fib6 ...
1677
  	struct fib6_walker *w;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1678
  	int iter = 0;
4512c43ea   Wei Wang   ipv6: remove null...
1679
1680
  	/* Set fn->leaf to null_entry for root node. */
  	if (fn->fn_flags & RTN_TL_ROOT) {
421842ede   David Ahern   net/ipv6: Add fib...
1681
  		rcu_assign_pointer(fn->leaf, net->ipv6.fib6_null_entry);
4512c43ea   Wei Wang   ipv6: remove null...
1682
1683
  		return fn;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1684
  	for (;;) {
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
  		struct fib6_node *fn_r = rcu_dereference_protected(fn->right,
  					    lockdep_is_held(&table->tb6_lock));
  		struct fib6_node *fn_l = rcu_dereference_protected(fn->left,
  					    lockdep_is_held(&table->tb6_lock));
  		struct fib6_node *pn = rcu_dereference_protected(fn->parent,
  					    lockdep_is_held(&table->tb6_lock));
  		struct fib6_node *pn_r = rcu_dereference_protected(pn->right,
  					    lockdep_is_held(&table->tb6_lock));
  		struct fib6_node *pn_l = rcu_dereference_protected(pn->left,
  					    lockdep_is_held(&table->tb6_lock));
8d1c802b2   David Ahern   net/ipv6: Flip FI...
1695
  		struct fib6_info *fn_leaf = rcu_dereference_protected(fn->leaf,
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1696
  					    lockdep_is_held(&table->tb6_lock));
8d1c802b2   David Ahern   net/ipv6: Flip FI...
1697
  		struct fib6_info *pn_leaf = rcu_dereference_protected(pn->leaf,
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1698
  					    lockdep_is_held(&table->tb6_lock));
8d1c802b2   David Ahern   net/ipv6: Flip FI...
1699
  		struct fib6_info *new_fn_leaf;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1700

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1701
1702
1703
  		RT6_TRACE("fixing tree: plen=%d iter=%d
  ", fn->fn_bit, iter);
  		iter++;
547b792ca   Ilpo Järvinen   net: convert BUG_...
1704
1705
  		WARN_ON(fn->fn_flags & RTN_RTINFO);
  		WARN_ON(fn->fn_flags & RTN_TL_ROOT);
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1706
  		WARN_ON(fn_leaf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1707
1708
1709
  
  		children = 0;
  		child = NULL;
dee847793   Joe Perches   ipv6: fib6: Avoid...
1710
1711
1712
1713
1714
1715
1716
1717
  		if (fn_r) {
  			child = fn_r;
  			children |= 1;
  		}
  		if (fn_l) {
  			child = fn_l;
  			children |= 2;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1718

7fc33165a   YOSHIFUJI Hideaki   [IPV6] ROUTE: Put...
1719
  		if (children == 3 || FIB6_SUBTREE(fn)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1720
1721
  #ifdef CONFIG_IPV6_SUBTREES
  		    /* Subtree root (i.e. fn) may have one child */
507c9b1e0   David S. Miller   ipv6: Various cle...
1722
  		    || (children && fn->fn_flags & RTN_ROOT)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1723
1724
  #endif
  		    ) {
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1725
  			new_fn_leaf = fib6_find_prefix(net, table, fn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1726
  #if RT6_DEBUG >= 2
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1727
1728
  			if (!new_fn_leaf) {
  				WARN_ON(!new_fn_leaf);
421842ede   David Ahern   net/ipv6: Add fib...
1729
  				new_fn_leaf = net->ipv6.fib6_null_entry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1730
1731
  			}
  #endif
93531c674   David Ahern   net/ipv6: separat...
1732
  			fib6_info_hold(new_fn_leaf);
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1733
1734
  			rcu_assign_pointer(fn->leaf, new_fn_leaf);
  			return pn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1735
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1736
  #ifdef CONFIG_IPV6_SUBTREES
7fc33165a   YOSHIFUJI Hideaki   [IPV6] ROUTE: Put...
1737
  		if (FIB6_SUBTREE(pn) == fn) {
547b792ca   Ilpo Järvinen   net: convert BUG_...
1738
  			WARN_ON(!(fn->fn_flags & RTN_ROOT));
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1739
  			RCU_INIT_POINTER(pn->subtree, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1740
1741
  			nstate = FWS_L;
  		} else {
547b792ca   Ilpo Järvinen   net: convert BUG_...
1742
  			WARN_ON(fn->fn_flags & RTN_ROOT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1743
  #endif
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1744
1745
1746
1747
  			if (pn_r == fn)
  				rcu_assign_pointer(pn->right, child);
  			else if (pn_l == fn)
  				rcu_assign_pointer(pn->left, child);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1748
  #if RT6_DEBUG >= 2
547b792ca   Ilpo Järvinen   net: convert BUG_...
1749
1750
  			else
  				WARN_ON(1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1751
1752
  #endif
  			if (child)
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1753
  				rcu_assign_pointer(child->parent, pn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1754
1755
1756
1757
  			nstate = FWS_R;
  #ifdef CONFIG_IPV6_SUBTREES
  		}
  #endif
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
1758
1759
  		read_lock(&net->ipv6.fib6_walker_lock);
  		FOR_WALKERS(net, w) {
507c9b1e0   David S. Miller   ipv6: Various cle...
1760
  			if (!child) {
2b760fcf5   Wei Wang   ipv6: hook up exc...
1761
  				if (w->node == fn) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1762
1763
1764
1765
1766
1767
  					RT6_TRACE("W %p adjusted by delnode 1, s=%d/%d
  ", w, w->state, nstate);
  					w->node = pn;
  					w->state = nstate;
  				}
  			} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1768
1769
1770
1771
1772
  				if (w->node == fn) {
  					w->node = child;
  					if (children&2) {
  						RT6_TRACE("W %p adjusted by delnode 2, s=%d
  ", w, w->state);
8db46f1d4   Wang Yufen   ipv6: fix checkpa...
1773
  						w->state = w->state >= FWS_R ? FWS_U : FWS_INIT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1774
1775
1776
  					} else {
  						RT6_TRACE("W %p adjusted by delnode 2, s=%d
  ", w, w->state);
8db46f1d4   Wang Yufen   ipv6: fix checkpa...
1777
  						w->state = w->state >= FWS_C ? FWS_U : FWS_INIT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1778
1779
1780
1781
  					}
  				}
  			}
  		}
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
1782
  		read_unlock(&net->ipv6.fib6_walker_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1783

81eb8447d   Wei Wang   ipv6: take care o...
1784
  		node_free(net, fn);
507c9b1e0   David S. Miller   ipv6: Various cle...
1785
  		if (pn->fn_flags & RTN_RTINFO || FIB6_SUBTREE(pn))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1786
  			return pn;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1787
  		RCU_INIT_POINTER(pn->leaf, NULL);
93531c674   David Ahern   net/ipv6: separat...
1788
  		fib6_info_release(pn_leaf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1789
1790
1791
  		fn = pn;
  	}
  }
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1792
  static void fib6_del_route(struct fib6_table *table, struct fib6_node *fn,
8d1c802b2   David Ahern   net/ipv6: Flip FI...
1793
  			   struct fib6_info __rcu **rtp, struct nl_info *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1794
  {
d2f0c9b11   Ido Schimmel   ipv6: Handle rout...
1795
  	struct fib6_info *leaf, *replace_rt = NULL;
94b2cfe02   Hannes Frederic Sowa   ipv6: minor fib6 ...
1796
  	struct fib6_walker *w;
8d1c802b2   David Ahern   net/ipv6: Flip FI...
1797
  	struct fib6_info *rt = rcu_dereference_protected(*rtp,
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1798
  				    lockdep_is_held(&table->tb6_lock));
c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
1799
  	struct net *net = info->nl_net;
d2f0c9b11   Ido Schimmel   ipv6: Handle rout...
1800
  	bool notify_del = false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1801
1802
1803
  
  	RT6_TRACE("fib6_del_route
  ");
d2f0c9b11   Ido Schimmel   ipv6: Handle rout...
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
  	/* If the deleted route is the first in the node and it is not part of
  	 * a multipath route, then we need to replace it with the next route
  	 * in the node, if exists.
  	 */
  	leaf = rcu_dereference_protected(fn->leaf,
  					 lockdep_is_held(&table->tb6_lock));
  	if (leaf == rt && !rt->fib6_nsiblings) {
  		if (rcu_access_pointer(rt->fib6_next))
  			replace_rt = rcu_dereference_protected(rt->fib6_next,
  					    lockdep_is_held(&table->tb6_lock));
  		else
  			notify_del = true;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1817
  	/* Unlink it */
8fb11a9a8   David Ahern   net/ipv6: rename ...
1818
  	*rtp = rt->fib6_next;
93c2fb253   David Ahern   net/ipv6: Rename ...
1819
  	rt->fib6_node = NULL;
c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
1820
1821
  	net->ipv6.rt6_stats->fib_rt_entries--;
  	net->ipv6.rt6_stats->fib_discarded_routes++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1822

f11e6659c   David S. Miller   [IPV6]: Fix routi...
1823
  	/* Reset round-robin state, if necessary */
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1824
  	if (rcu_access_pointer(fn->rr_ptr) == rt)
f11e6659c   David S. Miller   [IPV6]: Fix routi...
1825
  		fn->rr_ptr = NULL;
51ebd3181   Nicolas Dichtel   ipv6: add support...
1826
  	/* Remove this entry from other siblings */
93c2fb253   David Ahern   net/ipv6: Rename ...
1827
  	if (rt->fib6_nsiblings) {
8d1c802b2   David Ahern   net/ipv6: Flip FI...
1828
  		struct fib6_info *sibling, *next_sibling;
51ebd3181   Nicolas Dichtel   ipv6: add support...
1829

d2f0c9b11   Ido Schimmel   ipv6: Handle rout...
1830
1831
1832
1833
1834
1835
1836
1837
  		/* The route is deleted from a multipath route. If this
  		 * multipath route is the first route in the node, then we need
  		 * to emit a delete notification. Otherwise, we need to skip
  		 * the notification.
  		 */
  		if (rt->fib6_metric == leaf->fib6_metric &&
  		    rt6_qualify_for_ecmp(leaf))
  			notify_del = true;
51ebd3181   Nicolas Dichtel   ipv6: add support...
1838
  		list_for_each_entry_safe(sibling, next_sibling,
93c2fb253   David Ahern   net/ipv6: Rename ...
1839
1840
1841
1842
  					 &rt->fib6_siblings, fib6_siblings)
  			sibling->fib6_nsiblings--;
  		rt->fib6_nsiblings = 0;
  		list_del_init(&rt->fib6_siblings);
d7dedee18   Ido Schimmel   ipv6: Calculate h...
1843
  		rt6_multipath_rebalance(next_sibling);
51ebd3181   Nicolas Dichtel   ipv6: add support...
1844
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1845
  	/* Adjust walkers */
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
1846
1847
  	read_lock(&net->ipv6.fib6_walker_lock);
  	FOR_WALKERS(net, w) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1848
1849
1850
  		if (w->state == FWS_C && w->leaf == rt) {
  			RT6_TRACE("walker %p adjusted by delroute
  ", w);
8fb11a9a8   David Ahern   net/ipv6: rename ...
1851
  			w->leaf = rcu_dereference_protected(rt->fib6_next,
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1852
  					    lockdep_is_held(&table->tb6_lock));
507c9b1e0   David S. Miller   ipv6: Various cle...
1853
  			if (!w->leaf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1854
1855
1856
  				w->state = FWS_U;
  		}
  	}
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
1857
  	read_unlock(&net->ipv6.fib6_walker_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1858

4512c43ea   Wei Wang   ipv6: remove null...
1859
1860
1861
1862
  	/* If it was last route, call fib6_repair_tree() to:
  	 * 1. For root node, put back null_entry as how the table was created.
  	 * 2. For other nodes, expunge its radix tree node.
  	 */
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1863
  	if (!rcu_access_pointer(fn->leaf)) {
4512c43ea   Wei Wang   ipv6: remove null...
1864
1865
1866
1867
  		if (!(fn->fn_flags & RTN_TL_ROOT)) {
  			fn->fn_flags &= ~RTN_RTINFO;
  			net->ipv6.rt6_stats->fib_route_nodes--;
  		}
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1868
  		fn = fib6_repair_tree(net, table, fn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1869
  	}
6e9e16e61   Hannes Frederic Sowa   ipv6: replacing a...
1870
  	fib6_purge_rt(rt, fn, net);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1871

d2f0c9b11   Ido Schimmel   ipv6: Handle rout...
1872
1873
  	if (!info->skip_notify_kernel) {
  		if (notify_del)
caafb2509   Ido Schimmel   ipv6: Remove old ...
1874
  			call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_DEL,
d2f0c9b11   Ido Schimmel   ipv6: Handle rout...
1875
1876
1877
  						  rt, NULL);
  		else if (replace_rt)
  			call_fib6_entry_notifiers_replace(net, replace_rt);
d2f0c9b11   Ido Schimmel   ipv6: Handle rout...
1878
  	}
16a16cd35   David Ahern   net: ipv6: Change...
1879
1880
  	if (!info->skip_notify)
  		inet6_rt_notify(RTM_DELROUTE, rt, info, 0);
d5382fef7   Ido Schimmel   ipv6: Stop sendin...
1881

93531c674   David Ahern   net/ipv6: separat...
1882
  	fib6_info_release(rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1883
  }
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1884
  /* Need to own table->tb6_lock */
8d1c802b2   David Ahern   net/ipv6: Flip FI...
1885
  int fib6_del(struct fib6_info *rt, struct nl_info *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1886
  {
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
1887
  	struct net *net = info->nl_net;
8d1c802b2   David Ahern   net/ipv6: Flip FI...
1888
1889
  	struct fib6_info __rcu **rtp;
  	struct fib6_info __rcu **rtp_next;
843d926b0   Eric Dumazet   ipv6: avoid lockd...
1890
1891
1892
1893
1894
  	struct fib6_table *table;
  	struct fib6_node *fn;
  
  	if (rt == net->ipv6.fib6_null_entry)
  		return -ENOENT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1895

843d926b0   Eric Dumazet   ipv6: avoid lockd...
1896
1897
1898
1899
  	table = rt->fib6_table;
  	fn = rcu_dereference_protected(rt->fib6_node,
  				       lockdep_is_held(&table->tb6_lock));
  	if (!fn)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1900
  		return -ENOENT;
547b792ca   Ilpo Järvinen   net: convert BUG_...
1901
  	WARN_ON(!(fn->fn_flags & RTN_RTINFO));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1902

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1903
1904
1905
  	/*
  	 *	Walk the leaf entries looking for ourself
  	 */
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1906
  	for (rtp = &fn->leaf; *rtp; rtp = rtp_next) {
8d1c802b2   David Ahern   net/ipv6: Flip FI...
1907
  		struct fib6_info *cur = rcu_dereference_protected(*rtp,
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1908
1909
  					lockdep_is_held(&table->tb6_lock));
  		if (rt == cur) {
b9b33e7c2   Paolo Abeni   ipv6: keep track ...
1910
1911
  			if (fib6_requires_src(cur))
  				fib6_routes_require_src_dec(info->nl_net);
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1912
  			fib6_del_route(table, fn, rtp, info);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1913
1914
  			return 0;
  		}
8fb11a9a8   David Ahern   net/ipv6: rename ...
1915
  		rtp_next = &cur->fib6_next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
  	}
  	return -ENOENT;
  }
  
  /*
   *	Tree traversal function.
   *
   *	Certainly, it is not interrupt safe.
   *	However, it is internally reenterable wrt itself and fib6_add/fib6_del.
   *	It means, that we can modify tree during walking
   *	and use this function for garbage collection, clone pruning,
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
1927
   *	cleaning tree when a device goes down etc. etc.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
   *
   *	It guarantees that every node will be traversed,
   *	and that it will be traversed only once.
   *
   *	Callback function w->func may return:
   *	0 -> continue walking.
   *	positive value -> walking is suspended (used by tree dumps,
   *	and probably by gc, if it will be split to several slices)
   *	negative value -> terminate walking.
   *
   *	The function itself returns:
   *	0   -> walk is complete.
   *	>0  -> walk is incomplete (i.e. suspended)
   *	<0  -> walk is terminated by an error.
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1942
1943
   *
   *	This function is called with tb6_lock held.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1944
   */
94b2cfe02   Hannes Frederic Sowa   ipv6: minor fib6 ...
1945
  static int fib6_walk_continue(struct fib6_walker *w)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1946
  {
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1947
  	struct fib6_node *fn, *pn, *left, *right;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1948

2b760fcf5   Wei Wang   ipv6: hook up exc...
1949
1950
  	/* w->root should always be table->tb6_root */
  	WARN_ON_ONCE(!(w->root->fn_flags & RTN_TL_ROOT));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1951
1952
  	for (;;) {
  		fn = w->node;
507c9b1e0   David S. Miller   ipv6: Various cle...
1953
  		if (!fn)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1954
  			return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1955
1956
1957
  		switch (w->state) {
  #ifdef CONFIG_IPV6_SUBTREES
  		case FWS_S:
7fc33165a   YOSHIFUJI Hideaki   [IPV6] ROUTE: Put...
1958
1959
  			if (FIB6_SUBTREE(fn)) {
  				w->node = FIB6_SUBTREE(fn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1960
1961
1962
  				continue;
  			}
  			w->state = FWS_L;
a8eceea84   Joe Perches   inet: Use fallthr...
1963
  			fallthrough;
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
1964
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1965
  		case FWS_L:
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1966
1967
1968
  			left = rcu_dereference_protected(fn->left, 1);
  			if (left) {
  				w->node = left;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1969
1970
1971
1972
  				w->state = FWS_INIT;
  				continue;
  			}
  			w->state = FWS_R;
a8eceea84   Joe Perches   inet: Use fallthr...
1973
  			fallthrough;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1974
  		case FWS_R:
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1975
1976
1977
  			right = rcu_dereference_protected(fn->right, 1);
  			if (right) {
  				w->node = right;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1978
1979
1980
1981
  				w->state = FWS_INIT;
  				continue;
  			}
  			w->state = FWS_C;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
1982
  			w->leaf = rcu_dereference_protected(fn->leaf, 1);
a8eceea84   Joe Perches   inet: Use fallthr...
1983
  			fallthrough;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1984
  		case FWS_C:
507c9b1e0   David S. Miller   ipv6: Various cle...
1985
  			if (w->leaf && fn->fn_flags & RTN_RTINFO) {
2bec5a369   Patrick McHardy   ipv6: fib: fix cr...
1986
  				int err;
fa809e2fd   Eric Dumazet   ipv6: fib: fix fi...
1987
1988
  				if (w->skip) {
  					w->skip--;
1c2658545   Kumar Sundararajan   ipv6: fib: fix fi...
1989
  					goto skip;
2bec5a369   Patrick McHardy   ipv6: fib: fix cr...
1990
1991
1992
  				}
  
  				err = w->func(w);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1993
1994
  				if (err)
  					return err;
2bec5a369   Patrick McHardy   ipv6: fib: fix cr...
1995
1996
  
  				w->count++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1997
1998
  				continue;
  			}
1c2658545   Kumar Sundararajan   ipv6: fib: fix fi...
1999
  skip:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2000
  			w->state = FWS_U;
a8eceea84   Joe Perches   inet: Use fallthr...
2001
  			fallthrough;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2002
2003
2004
  		case FWS_U:
  			if (fn == w->root)
  				return 0;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
2005
2006
2007
  			pn = rcu_dereference_protected(fn->parent, 1);
  			left = rcu_dereference_protected(pn->left, 1);
  			right = rcu_dereference_protected(pn->right, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2008
2009
  			w->node = pn;
  #ifdef CONFIG_IPV6_SUBTREES
7fc33165a   YOSHIFUJI Hideaki   [IPV6] ROUTE: Put...
2010
  			if (FIB6_SUBTREE(pn) == fn) {
547b792ca   Ilpo Järvinen   net: convert BUG_...
2011
  				WARN_ON(!(fn->fn_flags & RTN_ROOT));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2012
2013
2014
2015
  				w->state = FWS_L;
  				continue;
  			}
  #endif
66f5d6ce5   Wei Wang   ipv6: replace rwl...
2016
  			if (left == fn) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2017
2018
2019
  				w->state = FWS_R;
  				continue;
  			}
66f5d6ce5   Wei Wang   ipv6: replace rwl...
2020
  			if (right == fn) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2021
  				w->state = FWS_C;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
2022
  				w->leaf = rcu_dereference_protected(w->node->leaf, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2023
2024
2025
  				continue;
  			}
  #if RT6_DEBUG >= 2
547b792ca   Ilpo Järvinen   net: convert BUG_...
2026
  			WARN_ON(1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2027
2028
2029
2030
  #endif
  		}
  	}
  }
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
2031
  static int fib6_walk(struct net *net, struct fib6_walker *w)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2032
2033
2034
2035
2036
  {
  	int res;
  
  	w->state = FWS_INIT;
  	w->node = w->root;
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
2037
  	fib6_walker_link(net, w);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2038
2039
  	res = fib6_walk_continue(w);
  	if (res <= 0)
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
2040
  		fib6_walker_unlink(net, w);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2041
2042
  	return res;
  }
94b2cfe02   Hannes Frederic Sowa   ipv6: minor fib6 ...
2043
  static int fib6_clean_node(struct fib6_walker *w)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2044
2045
  {
  	int res;
8d1c802b2   David Ahern   net/ipv6: Flip FI...
2046
  	struct fib6_info *rt;
94b2cfe02   Hannes Frederic Sowa   ipv6: minor fib6 ...
2047
  	struct fib6_cleaner *c = container_of(w, struct fib6_cleaner, w);
ec7d43c29   Benjamin Thery   [NETNS][IPV6] ip6...
2048
2049
  	struct nl_info info = {
  		.nl_net = c->net,
7c6bb7d2f   David Ahern   net/ipv6: Add kno...
2050
  		.skip_notify = c->skip_notify,
ec7d43c29   Benjamin Thery   [NETNS][IPV6] ip6...
2051
  	};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2052

327571cb1   Hannes Frederic Sowa   ipv6: don't walk ...
2053
2054
2055
2056
2057
2058
2059
2060
2061
  	if (c->sernum != FIB6_NO_SERNUM_CHANGE &&
  	    w->node->fn_sernum != c->sernum)
  		w->node->fn_sernum = c->sernum;
  
  	if (!c->func) {
  		WARN_ON_ONCE(c->sernum == FIB6_NO_SERNUM_CHANGE);
  		w->leaf = NULL;
  		return 0;
  	}
66f5d6ce5   Wei Wang   ipv6: replace rwl...
2062
  	for_each_fib6_walker_rt(w) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2063
  		res = c->func(rt, c->arg);
b5cb5a755   Ido Schimmel   ipv6: Teach tree ...
2064
  		if (res == -1) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2065
  			w->leaf = rt;
528c4ceb4   Denis V. Lunev   [IPV6]: Always pa...
2066
  			res = fib6_del(rt, &info);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2067
2068
  			if (res) {
  #if RT6_DEBUG >= 2
91df42bed   Joe Perches   net: ipv4 and ipv...
2069
2070
  				pr_debug("%s: del failed: rt=%p@%p err=%d
  ",
4e587ea71   Wei Wang   ipv6: fix sparse ...
2071
  					 __func__, rt,
93c2fb253   David Ahern   net/ipv6: Rename ...
2072
  					 rcu_access_pointer(rt->fib6_node),
4e587ea71   Wei Wang   ipv6: fix sparse ...
2073
  					 res);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2074
2075
2076
2077
  #endif
  				continue;
  			}
  			return 0;
b5cb5a755   Ido Schimmel   ipv6: Teach tree ...
2078
  		} else if (res == -2) {
93c2fb253   David Ahern   net/ipv6: Rename ...
2079
  			if (WARN_ON(!rt->fib6_nsiblings))
b5cb5a755   Ido Schimmel   ipv6: Teach tree ...
2080
  				continue;
93c2fb253   David Ahern   net/ipv6: Rename ...
2081
2082
  			rt = list_last_entry(&rt->fib6_siblings,
  					     struct fib6_info, fib6_siblings);
b5cb5a755   Ido Schimmel   ipv6: Teach tree ...
2083
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2084
  		}
547b792ca   Ilpo Järvinen   net: convert BUG_...
2085
  		WARN_ON(res != 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2086
2087
2088
2089
2090
2091
2092
  	}
  	w->leaf = rt;
  	return 0;
  }
  
  /*
   *	Convenient frontend to tree walker.
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
2093
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2094
   *	func is called on each route.
b5cb5a755   Ido Schimmel   ipv6: Teach tree ...
2095
2096
   *		It may return -2 -> skip multipath route.
   *			      -1 -> delete this route.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2097
   *		              0  -> continue walking
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2098
   */
ec7d43c29   Benjamin Thery   [NETNS][IPV6] ip6...
2099
  static void fib6_clean_tree(struct net *net, struct fib6_node *root,
8d1c802b2   David Ahern   net/ipv6: Flip FI...
2100
  			    int (*func)(struct fib6_info *, void *arg),
7c6bb7d2f   David Ahern   net/ipv6: Add kno...
2101
  			    int sernum, void *arg, bool skip_notify)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2102
  {
94b2cfe02   Hannes Frederic Sowa   ipv6: minor fib6 ...
2103
  	struct fib6_cleaner c;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2104
2105
2106
  
  	c.w.root = root;
  	c.w.func = fib6_clean_node;
2bec5a369   Patrick McHardy   ipv6: fib: fix cr...
2107
2108
  	c.w.count = 0;
  	c.w.skip = 0;
1e47b4837   Stefano Brivio   ipv6: Dump route ...
2109
  	c.w.skip_in_node = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2110
  	c.func = func;
327571cb1   Hannes Frederic Sowa   ipv6: don't walk ...
2111
  	c.sernum = sernum;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2112
  	c.arg = arg;
ec7d43c29   Benjamin Thery   [NETNS][IPV6] ip6...
2113
  	c.net = net;
7c6bb7d2f   David Ahern   net/ipv6: Add kno...
2114
  	c.skip_notify = skip_notify;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2115

9a03cd8f3   Michal Kubeček   ipv6: per netns f...
2116
  	fib6_walk(net, &c.w);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2117
  }
327571cb1   Hannes Frederic Sowa   ipv6: don't walk ...
2118
  static void __fib6_clean_all(struct net *net,
8d1c802b2   David Ahern   net/ipv6: Flip FI...
2119
  			     int (*func)(struct fib6_info *, void *),
7c6bb7d2f   David Ahern   net/ipv6: Add kno...
2120
  			     int sernum, void *arg, bool skip_notify)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
2121
  {
c71099acc   Thomas Graf   [IPV6]: Multiple ...
2122
  	struct fib6_table *table;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
2123
  	struct hlist_head *head;
1b43af548   Patrick McHardy   [IPV6]: Increase ...
2124
  	unsigned int h;
c71099acc   Thomas Graf   [IPV6]: Multiple ...
2125

1b43af548   Patrick McHardy   [IPV6]: Increase ...
2126
  	rcu_read_lock();
a33bc5c15   Neil Horman   xfrm: select sane...
2127
  	for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
f3db48517   Daniel Lezcano   [NETNS][IPV6] ip6...
2128
  		head = &net->ipv6.fib_table_hash[h];
b67bfe0d4   Sasha Levin   hlist: drop the n...
2129
  		hlist_for_each_entry_rcu(table, head, tb6_hlist) {
66f5d6ce5   Wei Wang   ipv6: replace rwl...
2130
  			spin_lock_bh(&table->tb6_lock);
ec7d43c29   Benjamin Thery   [NETNS][IPV6] ip6...
2131
  			fib6_clean_tree(net, &table->tb6_root,
7c6bb7d2f   David Ahern   net/ipv6: Add kno...
2132
  					func, sernum, arg, skip_notify);
66f5d6ce5   Wei Wang   ipv6: replace rwl...
2133
  			spin_unlock_bh(&table->tb6_lock);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
2134
2135
  		}
  	}
1b43af548   Patrick McHardy   [IPV6]: Increase ...
2136
  	rcu_read_unlock();
c71099acc   Thomas Graf   [IPV6]: Multiple ...
2137
  }
8d1c802b2   David Ahern   net/ipv6: Flip FI...
2138
  void fib6_clean_all(struct net *net, int (*func)(struct fib6_info *, void *),
327571cb1   Hannes Frederic Sowa   ipv6: don't walk ...
2139
2140
  		    void *arg)
  {
7c6bb7d2f   David Ahern   net/ipv6: Add kno...
2141
2142
2143
2144
2145
2146
2147
2148
  	__fib6_clean_all(net, func, FIB6_NO_SERNUM_CHANGE, arg, false);
  }
  
  void fib6_clean_all_skip_notify(struct net *net,
  				int (*func)(struct fib6_info *, void *),
  				void *arg)
  {
  	__fib6_clean_all(net, func, FIB6_NO_SERNUM_CHANGE, arg, true);
327571cb1   Hannes Frederic Sowa   ipv6: don't walk ...
2149
  }
705f1c869   Hannes Frederic Sowa   ipv6: remove rt6i...
2150
2151
  static void fib6_flush_trees(struct net *net)
  {
812918c46   Hannes Frederic Sowa   ipv6: make fib6 s...
2152
  	int new_sernum = fib6_new_sernum(net);
705f1c869   Hannes Frederic Sowa   ipv6: remove rt6i...
2153

7c6bb7d2f   David Ahern   net/ipv6: Add kno...
2154
  	__fib6_clean_all(net, NULL, new_sernum, NULL, false);
705f1c869   Hannes Frederic Sowa   ipv6: remove rt6i...
2155
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2156
2157
2158
  /*
   *	Garbage collection
   */
8d1c802b2   David Ahern   net/ipv6: Flip FI...
2159
  static int fib6_age(struct fib6_info *rt, void *arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2160
  {
3570df914   Michal Kubeček   ipv6: replace glo...
2161
  	struct fib6_gc_args *gc_args = arg;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2162
2163
2164
2165
2166
  	unsigned long now = jiffies;
  
  	/*
  	 *	check addrconf expiration here.
  	 *	Routes are expired even if they are in use.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2167
  	 */
93c2fb253   David Ahern   net/ipv6: Rename ...
2168
  	if (rt->fib6_flags & RTF_EXPIRES && rt->expires) {
14895687d   David Ahern   net/ipv6: move ex...
2169
  		if (time_after(now, rt->expires)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2170
2171
  			RT6_TRACE("expiring %p
  ", rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2172
2173
  			return -1;
  		}
3570df914   Michal Kubeček   ipv6: replace glo...
2174
  		gc_args->more++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2175
  	}
c757faa8b   Wei Wang   ipv6: prepare fib...
2176
2177
2178
2179
2180
  	/*	Also age clones in the exception table.
  	 *	Note, that clones are aged out
  	 *	only if they are not in use now.
  	 */
  	rt6_age_exceptions(rt, gc_args, now);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2181
2182
  	return 0;
  }
2ac3ac8f8   Michal Kubeček   ipv6: prevent fib...
2183
  void fib6_run_gc(unsigned long expires, struct net *net, bool force)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2184
  {
3570df914   Michal Kubeček   ipv6: replace glo...
2185
  	struct fib6_gc_args gc_args;
49a18d86f   Michal Kubeček   ipv6: update ip6_...
2186
  	unsigned long now;
2ac3ac8f8   Michal Kubeček   ipv6: prevent fib...
2187
  	if (force) {
3dc94f93b   Michal Kubeček   ipv6: per netns F...
2188
2189
  		spin_lock_bh(&net->ipv6.fib6_gc_lock);
  	} else if (!spin_trylock_bh(&net->ipv6.fib6_gc_lock)) {
2ac3ac8f8   Michal Kubeček   ipv6: prevent fib...
2190
2191
  		mod_timer(&net->ipv6.ip6_fib_timer, jiffies + HZ);
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2192
  	}
2ac3ac8f8   Michal Kubeček   ipv6: prevent fib...
2193
2194
  	gc_args.timeout = expires ? (int)expires :
  			  net->ipv6.sysctl.ip6_rt_gc_interval;
db916649b   Wei Wang   ipv6: get rid of ...
2195
  	gc_args.more = 0;
f3db48517   Daniel Lezcano   [NETNS][IPV6] ip6...
2196

3570df914   Michal Kubeček   ipv6: replace glo...
2197
  	fib6_clean_all(net, fib6_age, &gc_args);
49a18d86f   Michal Kubeček   ipv6: update ip6_...
2198
2199
  	now = jiffies;
  	net->ipv6.ip6_rt_last_gc = now;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2200
2201
  
  	if (gc_args.more)
c8a452224   Stephen Hemminger   ipv6: use round_j...
2202
  		mod_timer(&net->ipv6.ip6_fib_timer,
49a18d86f   Michal Kubeček   ipv6: update ip6_...
2203
  			  round_jiffies(now
c8a452224   Stephen Hemminger   ipv6: use round_j...
2204
  					+ net->ipv6.sysctl.ip6_rt_gc_interval));
417f28bb3   Stephen Hemminger   netns: dont alloc...
2205
2206
  	else
  		del_timer(&net->ipv6.ip6_fib_timer);
3dc94f93b   Michal Kubeček   ipv6: per netns F...
2207
  	spin_unlock_bh(&net->ipv6.fib6_gc_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2208
  }
86cb30ec0   Kees Cook   treewide: setup_t...
2209
  static void fib6_gc_timer_cb(struct timer_list *t)
5b7c931df   Daniel Lezcano   [NETNS][IPV6] ip6...
2210
  {
86cb30ec0   Kees Cook   treewide: setup_t...
2211
2212
2213
  	struct net *arg = from_timer(arg, t, ipv6.ip6_fib_timer);
  
  	fib6_run_gc(0, arg, true);
5b7c931df   Daniel Lezcano   [NETNS][IPV6] ip6...
2214
  }
2c8c1e729   Alexey Dobriyan   net: spread __net...
2215
  static int __net_init fib6_net_init(struct net *net)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2216
  {
10da66f75   Eric Dumazet   fib: avoid false ...
2217
  	size_t size = sizeof(struct hlist_head) * FIB6_TABLE_HASHSZ;
16ab6d7d4   Ido Schimmel   ipv6: fib: Add FI...
2218
2219
2220
2221
2222
  	int err;
  
  	err = fib6_notifier_init(net);
  	if (err)
  		return err;
10da66f75   Eric Dumazet   fib: avoid false ...
2223

3dc94f93b   Michal Kubeček   ipv6: per netns F...
2224
  	spin_lock_init(&net->ipv6.fib6_gc_lock);
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
2225
2226
  	rwlock_init(&net->ipv6.fib6_walker_lock);
  	INIT_LIST_HEAD(&net->ipv6.fib6_walkers);
86cb30ec0   Kees Cook   treewide: setup_t...
2227
  	timer_setup(&net->ipv6.ip6_fib_timer, fib6_gc_timer_cb, 0);
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
2228

c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
2229
2230
2231
  	net->ipv6.rt6_stats = kzalloc(sizeof(*net->ipv6.rt6_stats), GFP_KERNEL);
  	if (!net->ipv6.rt6_stats)
  		goto out_timer;
10da66f75   Eric Dumazet   fib: avoid false ...
2232
2233
2234
2235
  	/* Avoid false sharing : Use at least a full cache line */
  	size = max_t(size_t, size, L1_CACHE_BYTES);
  
  	net->ipv6.fib_table_hash = kzalloc(size, GFP_KERNEL);
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
2236
  	if (!net->ipv6.fib_table_hash)
c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
2237
  		goto out_rt6_stats;
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
2238

58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
2239
2240
2241
  	net->ipv6.fib6_main_tbl = kzalloc(sizeof(*net->ipv6.fib6_main_tbl),
  					  GFP_KERNEL);
  	if (!net->ipv6.fib6_main_tbl)
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
2242
  		goto out_fib_table_hash;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
2243
  	net->ipv6.fib6_main_tbl->tb6_id = RT6_TABLE_MAIN;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
2244
  	rcu_assign_pointer(net->ipv6.fib6_main_tbl->tb6_root.leaf,
421842ede   David Ahern   net/ipv6: Add fib...
2245
  			   net->ipv6.fib6_null_entry);
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
2246
2247
  	net->ipv6.fib6_main_tbl->tb6_root.fn_flags =
  		RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
8e7732778   David S. Miller   inet: Add inetpee...
2248
  	inet_peer_base_init(&net->ipv6.fib6_main_tbl->tb6_peers);
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
2249
2250
  
  #ifdef CONFIG_IPV6_MULTIPLE_TABLES
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
2251
2252
2253
  	net->ipv6.fib6_local_tbl = kzalloc(sizeof(*net->ipv6.fib6_local_tbl),
  					   GFP_KERNEL);
  	if (!net->ipv6.fib6_local_tbl)
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
2254
  		goto out_fib6_main_tbl;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
2255
  	net->ipv6.fib6_local_tbl->tb6_id = RT6_TABLE_LOCAL;
66f5d6ce5   Wei Wang   ipv6: replace rwl...
2256
  	rcu_assign_pointer(net->ipv6.fib6_local_tbl->tb6_root.leaf,
421842ede   David Ahern   net/ipv6: Add fib...
2257
  			   net->ipv6.fib6_null_entry);
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
2258
2259
  	net->ipv6.fib6_local_tbl->tb6_root.fn_flags =
  		RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
8e7732778   David S. Miller   inet: Add inetpee...
2260
  	inet_peer_base_init(&net->ipv6.fib6_local_tbl->tb6_peers);
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
2261
  #endif
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
2262
  	fib6_tables_init(net);
f845ab6b7   Daniel Lezcano   [IPV6] route6/fib...
2263

417f28bb3   Stephen Hemminger   netns: dont alloc...
2264
  	return 0;
d63bddbe9   Daniel Lezcano   [IPV6]: Make fib6...
2265

e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
2266
  #ifdef CONFIG_IPV6_MULTIPLE_TABLES
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
2267
  out_fib6_main_tbl:
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
2268
  	kfree(net->ipv6.fib6_main_tbl);
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
2269
  #endif
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
2270
  out_fib_table_hash:
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
2271
  	kfree(net->ipv6.fib_table_hash);
c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
2272
2273
  out_rt6_stats:
  	kfree(net->ipv6.rt6_stats);
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
2274
  out_timer:
16ab6d7d4   Ido Schimmel   ipv6: fib: Add FI...
2275
  	fib6_notifier_exit(net);
417f28bb3   Stephen Hemminger   netns: dont alloc...
2276
  	return -ENOMEM;
8db46f1d4   Wang Yufen   ipv6: fix checkpa...
2277
  }
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
2278
2279
2280
  
  static void fib6_net_exit(struct net *net)
  {
ba1cc08d9   Sabrina Dubroca   ipv6: fix memory ...
2281
  	unsigned int i;
417f28bb3   Stephen Hemminger   netns: dont alloc...
2282
  	del_timer_sync(&net->ipv6.ip6_fib_timer);
32a805baf   Eric Dumazet   ipv6: fix typo in...
2283
  	for (i = 0; i < FIB6_TABLE_HASHSZ; i++) {
ba1cc08d9   Sabrina Dubroca   ipv6: fix memory ...
2284
2285
2286
2287
2288
2289
2290
2291
2292
  		struct hlist_head *head = &net->ipv6.fib_table_hash[i];
  		struct hlist_node *tmp;
  		struct fib6_table *tb;
  
  		hlist_for_each_entry_safe(tb, tmp, head, tb6_hlist) {
  			hlist_del(&tb->tb6_hlist);
  			fib6_free_table(tb);
  		}
  	}
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
2293
  	kfree(net->ipv6.fib_table_hash);
c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
2294
  	kfree(net->ipv6.rt6_stats);
16ab6d7d4   Ido Schimmel   ipv6: fib: Add FI...
2295
  	fib6_notifier_exit(net);
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
  }
  
  static struct pernet_operations fib6_net_ops = {
  	.init = fib6_net_init,
  	.exit = fib6_net_exit,
  };
  
  int __init fib6_init(void)
  {
  	int ret = -ENOMEM;
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
2306

58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
2307
2308
2309
2310
2311
2312
2313
2314
2315
  	fib6_node_kmem = kmem_cache_create("fib6_nodes",
  					   sizeof(struct fib6_node),
  					   0, SLAB_HWCACHE_ALIGN,
  					   NULL);
  	if (!fib6_node_kmem)
  		goto out;
  
  	ret = register_pernet_subsys(&fib6_net_ops);
  	if (ret)
c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
2316
  		goto out_kmem_cache_create;
e8803b6c3   David S. Miller   Revert "ipv6: Pre...
2317

16feebcf2   Florian Westphal   rtnetlink: remove...
2318
2319
  	ret = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETROUTE, NULL,
  				   inet6_dump_fib, 0);
e8803b6c3   David S. Miller   Revert "ipv6: Pre...
2320
2321
  	if (ret)
  		goto out_unregister_subsys;
705f1c869   Hannes Frederic Sowa   ipv6: remove rt6i...
2322
2323
  
  	__fib6_flush_trees = fib6_flush_trees;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
2324
2325
  out:
  	return ret;
e8803b6c3   David S. Miller   Revert "ipv6: Pre...
2326
2327
  out_unregister_subsys:
  	unregister_pernet_subsys(&fib6_net_ops);
d63bddbe9   Daniel Lezcano   [IPV6]: Make fib6...
2328
2329
2330
  out_kmem_cache_create:
  	kmem_cache_destroy(fib6_node_kmem);
  	goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2331
2332
2333
2334
  }
  
  void fib6_gc_cleanup(void)
  {
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
2335
  	unregister_pernet_subsys(&fib6_net_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2336
2337
  	kmem_cache_destroy(fib6_node_kmem);
  }
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2338
2339
  
  #ifdef CONFIG_PROC_FS
138d0be35   Yonghong Song   net: bpf: Add net...
2340
  static int ipv6_route_native_seq_show(struct seq_file *seq, void *v)
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2341
  {
8d1c802b2   David Ahern   net/ipv6: Flip FI...
2342
  	struct fib6_info *rt = v;
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2343
  	struct ipv6_route_iter *iter = seq->private;
f88d8ea67   David Ahern   ipv6: Plumb suppo...
2344
  	struct fib6_nh *fib6_nh = rt->fib6_nh;
2b2450ca4   David Ahern   ipv6: Move gatewa...
2345
  	unsigned int flags = rt->fib6_flags;
5e670d844   David Ahern   net/ipv6: Move ne...
2346
  	const struct net_device *dev;
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2347

f88d8ea67   David Ahern   ipv6: Plumb suppo...
2348
2349
  	if (rt->nh)
  		fib6_nh = nexthop_fib6_nh(rt->nh);
93c2fb253   David Ahern   net/ipv6: Rename ...
2350
  	seq_printf(seq, "%pi6 %02x ", &rt->fib6_dst.addr, rt->fib6_dst.plen);
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2351
2352
  
  #ifdef CONFIG_IPV6_SUBTREES
93c2fb253   David Ahern   net/ipv6: Rename ...
2353
  	seq_printf(seq, "%pi6 %02x ", &rt->fib6_src.addr, rt->fib6_src.plen);
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2354
2355
2356
  #else
  	seq_puts(seq, "00000000000000000000000000000000 00 ");
  #endif
f88d8ea67   David Ahern   ipv6: Plumb suppo...
2357
  	if (fib6_nh->fib_nh_gw_family) {
2b2450ca4   David Ahern   ipv6: Move gatewa...
2358
  		flags |= RTF_GATEWAY;
f88d8ea67   David Ahern   ipv6: Plumb suppo...
2359
  		seq_printf(seq, "%pi6", &fib6_nh->fib_nh_gw6);
2b2450ca4   David Ahern   ipv6: Move gatewa...
2360
  	} else {
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2361
  		seq_puts(seq, "00000000000000000000000000000000");
2b2450ca4   David Ahern   ipv6: Move gatewa...
2362
  	}
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2363

f88d8ea67   David Ahern   ipv6: Plumb suppo...
2364
  	dev = fib6_nh->fib_nh_dev;
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2365
2366
  	seq_printf(seq, " %08x %08x %08x %08x %8s
  ",
f05713e09   Eric Dumazet   ipv6: convert fib...
2367
  		   rt->fib6_metric, refcount_read(&rt->fib6_ref), 0,
2b2450ca4   David Ahern   ipv6: Move gatewa...
2368
  		   flags, dev ? dev->name : "");
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2369
2370
2371
  	iter->w.leaf = NULL;
  	return 0;
  }
94b2cfe02   Hannes Frederic Sowa   ipv6: minor fib6 ...
2372
  static int ipv6_route_yield(struct fib6_walker *w)
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2373
2374
2375
2376
2377
2378
2379
  {
  	struct ipv6_route_iter *iter = w->args;
  
  	if (!iter->skip)
  		return 1;
  
  	do {
66f5d6ce5   Wei Wang   ipv6: replace rwl...
2380
  		iter->w.leaf = rcu_dereference_protected(
8fb11a9a8   David Ahern   net/ipv6: rename ...
2381
  				iter->w.leaf->fib6_next,
66f5d6ce5   Wei Wang   ipv6: replace rwl...
2382
  				lockdep_is_held(&iter->tbl->tb6_lock));
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2383
2384
2385
2386
2387
2388
2389
  		iter->skip--;
  		if (!iter->skip && iter->w.leaf)
  			return 1;
  	} while (iter->w.leaf);
  
  	return 0;
  }
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
2390
2391
  static void ipv6_route_seq_setup_walk(struct ipv6_route_iter *iter,
  				      struct net *net)
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2392
2393
2394
2395
2396
2397
2398
  {
  	memset(&iter->w, 0, sizeof(iter->w));
  	iter->w.func = ipv6_route_yield;
  	iter->w.root = &iter->tbl->tb6_root;
  	iter->w.state = FWS_INIT;
  	iter->w.node = iter->w.root;
  	iter->w.args = iter;
0a67d3efa   Hannes Frederic Sowa   ipv6: compare ser...
2399
  	iter->sernum = iter->w.root->fn_sernum;
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2400
  	INIT_LIST_HEAD(&iter->w.lh);
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
2401
  	fib6_walker_link(net, &iter->w);
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
  }
  
  static struct fib6_table *ipv6_route_seq_next_table(struct fib6_table *tbl,
  						    struct net *net)
  {
  	unsigned int h;
  	struct hlist_node *node;
  
  	if (tbl) {
  		h = (tbl->tb6_id & (FIB6_TABLE_HASHSZ - 1)) + 1;
  		node = rcu_dereference_bh(hlist_next_rcu(&tbl->tb6_hlist));
  	} else {
  		h = 0;
  		node = NULL;
  	}
  
  	while (!node && h < FIB6_TABLE_HASHSZ) {
  		node = rcu_dereference_bh(
  			hlist_first_rcu(&net->ipv6.fib_table_hash[h++]));
  	}
  	return hlist_entry_safe(node, struct fib6_table, tb6_hlist);
  }
0a67d3efa   Hannes Frederic Sowa   ipv6: compare ser...
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
  static void ipv6_route_check_sernum(struct ipv6_route_iter *iter)
  {
  	if (iter->sernum != iter->w.root->fn_sernum) {
  		iter->sernum = iter->w.root->fn_sernum;
  		iter->w.state = FWS_INIT;
  		iter->w.node = iter->w.root;
  		WARN_ON(iter->w.skip);
  		iter->w.skip = iter->w.count;
  	}
  }
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2434
2435
2436
  static void *ipv6_route_seq_next(struct seq_file *seq, void *v, loff_t *pos)
  {
  	int r;
8d1c802b2   David Ahern   net/ipv6: Flip FI...
2437
  	struct fib6_info *n;
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2438
2439
  	struct net *net = seq_file_net(seq);
  	struct ipv6_route_iter *iter = seq->private;
4fc427e05   Vasily Averin   ipv6_route_seq_ne...
2440
  	++(*pos);
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2441
2442
  	if (!v)
  		goto iter_table;
8fb11a9a8   David Ahern   net/ipv6: rename ...
2443
  	n = rcu_dereference_bh(((struct fib6_info *)v)->fib6_next);
4fc427e05   Vasily Averin   ipv6_route_seq_ne...
2444
  	if (n)
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2445
  		return n;
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2446
2447
  
  iter_table:
0a67d3efa   Hannes Frederic Sowa   ipv6: compare ser...
2448
  	ipv6_route_check_sernum(iter);
66f5d6ce5   Wei Wang   ipv6: replace rwl...
2449
  	spin_lock_bh(&iter->tbl->tb6_lock);
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2450
  	r = fib6_walk_continue(&iter->w);
66f5d6ce5   Wei Wang   ipv6: replace rwl...
2451
  	spin_unlock_bh(&iter->tbl->tb6_lock);
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2452
  	if (r > 0) {
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2453
2454
  		return iter->w.leaf;
  	} else if (r < 0) {
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
2455
  		fib6_walker_unlink(net, &iter->w);
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2456
2457
  		return NULL;
  	}
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
2458
  	fib6_walker_unlink(net, &iter->w);
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2459
2460
2461
2462
  
  	iter->tbl = ipv6_route_seq_next_table(iter->tbl, net);
  	if (!iter->tbl)
  		return NULL;
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
2463
  	ipv6_route_seq_setup_walk(iter, net);
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
  	goto iter_table;
  }
  
  static void *ipv6_route_seq_start(struct seq_file *seq, loff_t *pos)
  	__acquires(RCU_BH)
  {
  	struct net *net = seq_file_net(seq);
  	struct ipv6_route_iter *iter = seq->private;
  
  	rcu_read_lock_bh();
  	iter->tbl = ipv6_route_seq_next_table(NULL, net);
  	iter->skip = *pos;
  
  	if (iter->tbl) {
6617dfd44   Yonghong Song   net: fix pos incr...
2478
  		loff_t p = 0;
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
2479
  		ipv6_route_seq_setup_walk(iter, net);
6617dfd44   Yonghong Song   net: fix pos incr...
2480
  		return ipv6_route_seq_next(seq, NULL, &p);
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2481
2482
2483
2484
2485
2486
2487
  	} else {
  		return NULL;
  	}
  }
  
  static bool ipv6_route_iter_active(struct ipv6_route_iter *iter)
  {
94b2cfe02   Hannes Frederic Sowa   ipv6: minor fib6 ...
2488
  	struct fib6_walker *w = &iter->w;
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2489
2490
  	return w->node && !(w->state == FWS_U && w->node == w->root);
  }
138d0be35   Yonghong Song   net: bpf: Add net...
2491
  static void ipv6_route_native_seq_stop(struct seq_file *seq, void *v)
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2492
2493
  	__releases(RCU_BH)
  {
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
2494
  	struct net *net = seq_file_net(seq);
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2495
2496
2497
  	struct ipv6_route_iter *iter = seq->private;
  
  	if (ipv6_route_iter_active(iter))
9a03cd8f3   Michal Kubeček   ipv6: per netns f...
2498
  		fib6_walker_unlink(net, &iter->w);
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2499
2500
2501
  
  	rcu_read_unlock_bh();
  }
138d0be35   Yonghong Song   net: bpf: Add net...
2502
  #if IS_BUILTIN(CONFIG_IPV6) && defined(CONFIG_BPF_SYSCALL)
138d0be35   Yonghong Song   net: bpf: Add net...
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
  static int ipv6_route_prog_seq_show(struct bpf_prog *prog,
  				    struct bpf_iter_meta *meta,
  				    void *v)
  {
  	struct bpf_iter__ipv6_route ctx;
  
  	ctx.meta = meta;
  	ctx.rt = v;
  	return bpf_iter_run_prog(prog, &ctx);
  }
  
  static int ipv6_route_seq_show(struct seq_file *seq, void *v)
  {
  	struct ipv6_route_iter *iter = seq->private;
  	struct bpf_iter_meta meta;
  	struct bpf_prog *prog;
  	int ret;
  
  	meta.seq = seq;
  	prog = bpf_iter_get_info(&meta, false);
  	if (!prog)
  		return ipv6_route_native_seq_show(seq, v);
  
  	ret = ipv6_route_prog_seq_show(prog, &meta, v);
  	iter->w.leaf = NULL;
  
  	return ret;
  }
  
  static void ipv6_route_seq_stop(struct seq_file *seq, void *v)
  {
  	struct bpf_iter_meta meta;
  	struct bpf_prog *prog;
  
  	if (!v) {
  		meta.seq = seq;
  		prog = bpf_iter_get_info(&meta, true);
  		if (prog)
  			(void)ipv6_route_prog_seq_show(prog, &meta, v);
  	}
  
  	ipv6_route_native_seq_stop(seq, v);
  }
  #else
  static int ipv6_route_seq_show(struct seq_file *seq, void *v)
  {
  	return ipv6_route_native_seq_show(seq, v);
  }
  
  static void ipv6_route_seq_stop(struct seq_file *seq, void *v)
  {
  	ipv6_route_native_seq_stop(seq, v);
  }
  #endif
c35063722   Christoph Hellwig   proc: introduce p...
2557
  const struct seq_operations ipv6_route_seq_ops = {
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2558
2559
2560
2561
2562
  	.start	= ipv6_route_seq_start,
  	.next	= ipv6_route_seq_next,
  	.stop	= ipv6_route_seq_stop,
  	.show	= ipv6_route_seq_show
  };
8d2ca1d7b   Hannes Frederic Sowa   ipv6: avoid high ...
2563
  #endif /* CONFIG_PROC_FS */