Blame view

net/ipv6/ip6_fib.c 32.9 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
2
   *	Linux INET6 implementation
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
4
5
   *	Forwarding Information Database
   *
   *	Authors:
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
6
   *	Pedro Roque		<roque@di.fc.ul.pt>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
10
11
12
13
14
15
16
17
18
   *	This program is free software; you can redistribute it and/or
   *      modify it under the terms of the GNU General Public License
   *      as published by the Free Software Foundation; either version
   *      2 of the License, or (at your option) any later version.
   */
  
  /*
   * 	Changes:
   * 	Yuji SEKIYA @USAGI:	Support default route on router node;
   * 				remove ip6_null_entry from the top of
   * 				routing table.
c0bece9f2   YOSHIFUJI Hideaki   [IPV6] ROUTE: Add...
19
   * 	Ville Nuorvala:		Fixed routing subtrees.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
22
23
24
25
26
27
  #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 ...
28
  #include <linux/list.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
29
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
  
  #ifdef 	CONFIG_PROC_FS
  #include <linux/proc_fs.h>
  #endif
  
  #include <net/ipv6.h>
  #include <net/ndisc.h>
  #include <net/addrconf.h>
  
  #include <net/ip6_fib.h>
  #include <net/ip6_route.h>
  
  #define RT6_DEBUG 2
  
  #if RT6_DEBUG >= 3
  #define RT6_TRACE(x...) printk(KERN_DEBUG x)
  #else
  #define RT6_TRACE(x...) do { ; } while (0)
  #endif
e18b890bb   Christoph Lameter   [PATCH] slab: rem...
49
  static struct kmem_cache * fib6_node_kmem __read_mostly;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
  
  enum fib_walk_state_t
  {
  #ifdef CONFIG_IPV6_SUBTREES
  	FWS_S,
  #endif
  	FWS_L,
  	FWS_R,
  	FWS_C,
  	FWS_U
  };
  
  struct fib6_cleaner_t
  {
  	struct fib6_walker_t w;
ec7d43c29   Benjamin Thery   [NETNS][IPV6] ip6...
65
  	struct net *net;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
67
68
  	int (*func)(struct rt6_info *, void *arg);
  	void *arg;
  };
90d41122f   Adrian Bunk   [IPV6] ip6_fib.c:...
69
  static DEFINE_RWLOCK(fib6_walker_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
72
  
  #ifdef CONFIG_IPV6_SUBTREES
  #define FWS_INIT FWS_S
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
  #else
  #define FWS_INIT FWS_L
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
  #endif
ec7d43c29   Benjamin Thery   [NETNS][IPV6] ip6...
76
77
  static void fib6_prune_clones(struct net *net, struct fib6_node *fn,
  			      struct rt6_info *rt);
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
78
79
  static struct rt6_info *fib6_find_prefix(struct net *net, struct fib6_node *fn);
  static struct fib6_node *fib6_repair_tree(struct net *net, struct fib6_node *fn);
90d41122f   Adrian Bunk   [IPV6] ip6_fib.c:...
80
81
  static int fib6_walk(struct fib6_walker_t *w);
  static int fib6_walk_continue(struct fib6_walker_t *w);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
83
84
85
86
87
88
89
90
  
  /*
   *	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.
   */
  
  static __u32 rt_sernum;
5b7c931df   Daniel Lezcano   [NETNS][IPV6] ip6...
91
  static void fib6_gc_timer_cb(unsigned long arg);
bbef49dac   Alexey Dobriyan   ipv6: use standar...
92
93
  static LIST_HEAD(fib6_walkers);
  #define FOR_WALKERS(w) list_for_each_entry(w, &fib6_walkers, lh)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94

90d41122f   Adrian Bunk   [IPV6] ip6_fib.c:...
95
96
97
  static inline void fib6_walker_link(struct fib6_walker_t *w)
  {
  	write_lock_bh(&fib6_walker_lock);
bbef49dac   Alexey Dobriyan   ipv6: use standar...
98
  	list_add(&w->lh, &fib6_walkers);
90d41122f   Adrian Bunk   [IPV6] ip6_fib.c:...
99
100
101
102
103
104
  	write_unlock_bh(&fib6_walker_lock);
  }
  
  static inline void fib6_walker_unlink(struct fib6_walker_t *w)
  {
  	write_lock_bh(&fib6_walker_lock);
bbef49dac   Alexey Dobriyan   ipv6: use standar...
105
  	list_del(&w->lh);
90d41122f   Adrian Bunk   [IPV6] ip6_fib.c:...
106
107
  	write_unlock_bh(&fib6_walker_lock);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
110
111
112
113
114
115
116
117
118
  static __inline__ u32 fib6_new_sernum(void)
  {
  	u32 n = ++rt_sernum;
  	if ((__s32)n <= 0)
  		rt_sernum = n = 1;
  	return n;
  }
  
  /*
   *	Auxiliary address test functions for the radix tree.
   *
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
119
   *	These assume a 32bit processor (although it will work on
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
121
122
123
124
125
   *	64bit processors)
   */
  
  /*
   *	test bit
   */
02cdce53f   YOSHIFUJI Hideaki / 吉藤英明   ipv6 fib: Use "Sw...
126
127
128
129
130
  #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
131

b71d1d426   Eric Dumazet   inet: constify ip...
132
  static __inline__ __be32 addr_bit_set(const void *token, int fn_bit)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
  {
b71d1d426   Eric Dumazet   inet: constify ip...
134
  	const __be32 *addr = token;
02cdce53f   YOSHIFUJI Hideaki / 吉藤英明   ipv6 fib: Use "Sw...
135
136
137
138
139
140
141
  	/*
  	 * Here,
  	 * 	1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f)
  	 * is optimized version of
  	 *	htonl(1 << ((~fn_bit)&0x1F))
  	 * See include/asm-generic/bitops/le.h.
  	 */
0eae88f31   Eric Dumazet   net: Fix various ...
142
143
  	return (__force __be32)(1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f)) &
  	       addr[fn_bit >> 5];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
146
147
  static __inline__ struct fib6_node * node_alloc(void)
  {
  	struct fib6_node *fn;
c37622296   Robert P. J. Day   [PATCH] Transform...
148
  	fn = kmem_cache_zalloc(fib6_node_kmem, GFP_ATOMIC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
150
151
152
153
154
155
156
157
158
159
160
  
  	return fn;
  }
  
  static __inline__ void node_free(struct fib6_node * fn)
  {
  	kmem_cache_free(fib6_node_kmem, fn);
  }
  
  static __inline__ void rt6_release(struct rt6_info *rt)
  {
  	if (atomic_dec_and_test(&rt->rt6i_ref))
d8d1f30b9   Changli Gao   net-next: remove ...
161
  		dst_free(&rt->dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
  }
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
163
  static void fib6_link_table(struct net *net, struct fib6_table *tb)
1b43af548   Patrick McHardy   [IPV6]: Increase ...
164
165
  {
  	unsigned int h;
375216ad0   Thomas Graf   [IPv6] fib: initi...
166
167
168
169
170
  	/*
  	 * Initialize table lock at a single place to give lockdep a key,
  	 * tables aren't visible prior to being linked to the list.
  	 */
  	rwlock_init(&tb->tb6_lock);
a33bc5c15   Neil Horman   xfrm: select sane...
171
  	h = tb->tb6_id & (FIB6_TABLE_HASHSZ - 1);
1b43af548   Patrick McHardy   [IPV6]: Increase ...
172
173
174
175
176
  
  	/*
  	 * No protection necessary, this is the only list mutatation
  	 * operation, tables never disappear once they exist.
  	 */
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
177
  	hlist_add_head_rcu(&tb->tb6_hlist, &net->ipv6.fib_table_hash[h]);
1b43af548   Patrick McHardy   [IPV6]: Increase ...
178
  }
c71099acc   Thomas Graf   [IPV6]: Multiple ...
179

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

8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
182
  static struct fib6_table *fib6_alloc_table(struct net *net, u32 id)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
183
184
185
186
187
188
  {
  	struct fib6_table *table;
  
  	table = kzalloc(sizeof(*table), GFP_ATOMIC);
  	if (table != NULL) {
  		table->tb6_id = id;
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
189
  		table->tb6_root.leaf = net->ipv6.ip6_null_entry;
c71099acc   Thomas Graf   [IPV6]: Multiple ...
190
191
192
193
194
  		table->tb6_root.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
  	}
  
  	return table;
  }
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
195
  struct fib6_table *fib6_new_table(struct net *net, u32 id)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
196
197
198
199
200
  {
  	struct fib6_table *tb;
  
  	if (id == 0)
  		id = RT6_TABLE_MAIN;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
201
  	tb = fib6_get_table(net, id);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
202
203
  	if (tb)
  		return tb;
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
204
  	tb = fib6_alloc_table(net, id);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
205
  	if (tb != NULL)
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
206
  		fib6_link_table(net, tb);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
207
208
209
  
  	return tb;
  }
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
210
  struct fib6_table *fib6_get_table(struct net *net, u32 id)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
211
212
  {
  	struct fib6_table *tb;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
213
  	struct hlist_head *head;
c71099acc   Thomas Graf   [IPV6]: Multiple ...
214
215
216
217
218
  	struct hlist_node *node;
  	unsigned int h;
  
  	if (id == 0)
  		id = RT6_TABLE_MAIN;
a33bc5c15   Neil Horman   xfrm: select sane...
219
  	h = id & (FIB6_TABLE_HASHSZ - 1);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
220
  	rcu_read_lock();
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
221
222
  	head = &net->ipv6.fib_table_hash[h];
  	hlist_for_each_entry_rcu(tb, node, head, tb6_hlist) {
c71099acc   Thomas Graf   [IPV6]: Multiple ...
223
224
225
226
227
228
229
230
231
  		if (tb->tb6_id == id) {
  			rcu_read_unlock();
  			return tb;
  		}
  	}
  	rcu_read_unlock();
  
  	return NULL;
  }
2c8c1e729   Alexey Dobriyan   net: spread __net...
232
  static void __net_init fib6_tables_init(struct net *net)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
233
  {
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
234
235
  	fib6_link_table(net, net->ipv6.fib6_main_tbl);
  	fib6_link_table(net, net->ipv6.fib6_local_tbl);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
236
  }
c71099acc   Thomas Graf   [IPV6]: Multiple ...
237
  #else
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
238
  struct fib6_table *fib6_new_table(struct net *net, u32 id)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
239
  {
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
240
  	return fib6_get_table(net, id);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
241
  }
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
242
  struct fib6_table *fib6_get_table(struct net *net, u32 id)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
243
  {
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
244
  	  return net->ipv6.fib6_main_tbl;
c71099acc   Thomas Graf   [IPV6]: Multiple ...
245
  }
4c9483b2f   David S. Miller   ipv6: Convert to ...
246
  struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
247
  				   int flags, pol_lookup_t lookup)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
248
  {
4c9483b2f   David S. Miller   ipv6: Convert to ...
249
  	return (struct dst_entry *) lookup(net, net->ipv6.fib6_main_tbl, fl6, flags);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
250
  }
2c8c1e729   Alexey Dobriyan   net: spread __net...
251
  static void __net_init fib6_tables_init(struct net *net)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
252
  {
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
253
  	fib6_link_table(net, net->ipv6.fib6_main_tbl);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
254
255
256
  }
  
  #endif
1b43af548   Patrick McHardy   [IPV6]: Increase ...
257
258
259
260
  static int fib6_dump_node(struct fib6_walker_t *w)
  {
  	int res;
  	struct rt6_info *rt;
d8d1f30b9   Changli Gao   net-next: remove ...
261
  	for (rt = w->leaf; rt; rt = rt->dst.rt6_next) {
1b43af548   Patrick McHardy   [IPV6]: Increase ...
262
263
264
265
266
267
  		res = rt6_dump_route(rt, w->args);
  		if (res < 0) {
  			/* Frame is full, suspend walking */
  			w->leaf = rt;
  			return 1;
  		}
547b792ca   Ilpo Järvinen   net: convert BUG_...
268
  		WARN_ON(res == 0);
1b43af548   Patrick McHardy   [IPV6]: Increase ...
269
270
271
272
273
274
275
276
277
278
  	}
  	w->leaf = NULL;
  	return 0;
  }
  
  static void fib6_dump_end(struct netlink_callback *cb)
  {
  	struct fib6_walker_t *w = (void*)cb->args[2];
  
  	if (w) {
7891cc818   Herbert Xu   ipv6: Fix fib6_du...
279
280
281
282
  		if (cb->args[4]) {
  			cb->args[4] = 0;
  			fib6_walker_unlink(w);
  		}
1b43af548   Patrick McHardy   [IPV6]: Increase ...
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
  		cb->args[2] = 0;
  		kfree(w);
  	}
  	cb->done = (void*)cb->args[3];
  	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)
  {
  	struct fib6_walker_t *w;
  	int res;
  
  	w = (void *)cb->args[2];
  	w->root = &table->tb6_root;
  
  	if (cb->args[4] == 0) {
2bec5a369   Patrick McHardy   ipv6: fib: fix cr...
306
307
  		w->count = 0;
  		w->skip = 0;
1b43af548   Patrick McHardy   [IPV6]: Increase ...
308
309
310
  		read_lock_bh(&table->tb6_lock);
  		res = fib6_walk(w);
  		read_unlock_bh(&table->tb6_lock);
2bec5a369   Patrick McHardy   ipv6: fib: fix cr...
311
  		if (res > 0) {
1b43af548   Patrick McHardy   [IPV6]: Increase ...
312
  			cb->args[4] = 1;
2bec5a369   Patrick McHardy   ipv6: fib: fix cr...
313
314
  			cb->args[5] = w->root->fn_sernum;
  		}
1b43af548   Patrick McHardy   [IPV6]: Increase ...
315
  	} else {
2bec5a369   Patrick McHardy   ipv6: fib: fix cr...
316
317
318
319
320
321
322
323
  		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;
  		} else
  			w->skip = 0;
1b43af548   Patrick McHardy   [IPV6]: Increase ...
324
325
326
  		read_lock_bh(&table->tb6_lock);
  		res = fib6_walk_continue(w);
  		read_unlock_bh(&table->tb6_lock);
7891cc818   Herbert Xu   ipv6: Fix fib6_du...
327
328
329
  		if (res <= 0) {
  			fib6_walker_unlink(w);
  			cb->args[4] = 0;
1b43af548   Patrick McHardy   [IPV6]: Increase ...
330
  		}
1b43af548   Patrick McHardy   [IPV6]: Increase ...
331
  	}
7891cc818   Herbert Xu   ipv6: Fix fib6_du...
332

1b43af548   Patrick McHardy   [IPV6]: Increase ...
333
334
  	return res;
  }
c127ea2c4   Thomas Graf   [IPv6]: Use rtnl ...
335
  static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
1b43af548   Patrick McHardy   [IPV6]: Increase ...
336
  {
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
337
  	struct net *net = sock_net(skb->sk);
1b43af548   Patrick McHardy   [IPV6]: Increase ...
338
339
340
341
342
343
  	unsigned int h, s_h;
  	unsigned int e = 0, s_e;
  	struct rt6_rtnl_dump_arg arg;
  	struct fib6_walker_t *w;
  	struct fib6_table *tb;
  	struct hlist_node *node;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
344
  	struct hlist_head *head;
1b43af548   Patrick McHardy   [IPV6]: Increase ...
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
  	int res = 0;
  
  	s_h = cb->args[0];
  	s_e = cb->args[1];
  
  	w = (void *)cb->args[2];
  	if (w == NULL) {
  		/* 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);
  		if (w == NULL)
  			return -ENOMEM;
  		w->func = fib6_dump_node;
  		cb->args[2] = (long)w;
  	}
  
  	arg.skb = skb;
  	arg.cb = cb;
191cd5825   Brian Haley   netns: Add networ...
371
  	arg.net = net;
1b43af548   Patrick McHardy   [IPV6]: Increase ...
372
  	w->args = &arg;
e67f88dd1   Eric Dumazet   net: dont hold rt...
373
  	rcu_read_lock();
a33bc5c15   Neil Horman   xfrm: select sane...
374
  	for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_e = 0) {
1b43af548   Patrick McHardy   [IPV6]: Increase ...
375
  		e = 0;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
376
  		head = &net->ipv6.fib_table_hash[h];
e67f88dd1   Eric Dumazet   net: dont hold rt...
377
  		hlist_for_each_entry_rcu(tb, node, head, tb6_hlist) {
1b43af548   Patrick McHardy   [IPV6]: Increase ...
378
379
380
381
382
383
384
385
386
387
  			if (e < s_e)
  				goto next;
  			res = fib6_dump_table(tb, skb, cb);
  			if (res != 0)
  				goto out;
  next:
  			e++;
  		}
  	}
  out:
e67f88dd1   Eric Dumazet   net: dont hold rt...
388
  	rcu_read_unlock();
1b43af548   Patrick McHardy   [IPV6]: Increase ...
389
390
391
392
393
394
395
396
  	cb->args[1] = e;
  	cb->args[0] = h;
  
  	res = res < 0 ? res : skb->len;
  	if (res <= 0)
  		fib6_dump_end(cb);
  	return res;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
  
  /*
   *	Routing Table
   *
   *	return the appropriate node for a routing tree "add" operation
   *	by either creating and inserting or by returning an existing
   *	node.
   */
  
  static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr,
  				     int addrlen, int plen,
  				     int offset)
  {
  	struct fib6_node *fn, *in, *ln;
  	struct fib6_node *pn = NULL;
  	struct rt6key *key;
  	int	bit;
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
414
  	__be32	dir = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
  	__u32	sernum = fib6_new_sernum();
  
  	RT6_TRACE("fib6_add_1
  ");
  
  	/* insert node in tree */
  
  	fn = root;
  
  	do {
  		key = (struct rt6key *)((u8 *)fn->leaf + offset);
  
  		/*
  		 *	Prefix match
  		 */
  		if (plen < fn->fn_bit ||
  		    !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit))
  			goto insert_above;
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
433

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434
435
436
  		/*
  		 *	Exact match ?
  		 */
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
437

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
438
439
440
441
442
443
  		if (plen == fn->fn_bit) {
  			/* clean up an intermediate node */
  			if ((fn->fn_flags & RTN_RTINFO) == 0) {
  				rt6_release(fn->leaf);
  				fn->leaf = NULL;
  			}
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
444

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
445
  			fn->fn_sernum = sernum;
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
446

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
448
449
450
451
452
  			return fn;
  		}
  
  		/*
  		 *	We have more bits to go
  		 */
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
453

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
  		/* Try to walk down on tree. */
  		fn->fn_sernum = sernum;
  		dir = addr_bit_set(addr, fn->fn_bit);
  		pn = fn;
  		fn = dir ? fn->right: fn->left;
  	} while (fn);
  
  	/*
  	 *	We walked to the bottom of tree.
  	 *	Create new leaf node without children.
  	 */
  
  	ln = node_alloc();
  
  	if (ln == NULL)
  		return NULL;
  	ln->fn_bit = plen;
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
471

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
473
474
475
476
477
478
479
480
481
482
483
484
  	ln->parent = pn;
  	ln->fn_sernum = sernum;
  
  	if (dir)
  		pn->right = ln;
  	else
  		pn->left  = ln;
  
  	return ln;
  
  
  insert_above:
  	/*
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
485
  	 * split since we don't have a common prefix anymore or
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
486
487
488
489
490
491
492
493
494
  	 * 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
  	 */
  
  	pn = fn->parent;
  
  	/* find 1st bit in difference between the 2 addrs.
971f359dd   YOSHIFUJI Hideaki   [IPV6]: Put addr_...
495
  	   See comment in __ipv6_addr_diff: bit may be an invalid value,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
496
497
  	   but if it is >= plen, the value is ignored in any case.
  	 */
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
498

971f359dd   YOSHIFUJI Hideaki   [IPV6]: Put addr_...
499
  	bit = __ipv6_addr_diff(addr, &key->addr, addrlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
500

1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
501
502
  	/*
  	 *		(intermediate)[in]
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
503
504
505
506
507
508
  	 *	          /	   \
  	 *	(new leaf node)[ln] (old node)[fn]
  	 */
  	if (plen > bit) {
  		in = node_alloc();
  		ln = node_alloc();
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
509

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
510
511
512
513
514
515
516
  		if (in == NULL || ln == NULL) {
  			if (in)
  				node_free(in);
  			if (ln)
  				node_free(ln);
  			return NULL;
  		}
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
517
518
  		/*
  		 * new intermediate node.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
  		 * 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;
  
  		in->parent = pn;
  		in->leaf = fn->leaf;
  		atomic_inc(&in->leaf->rt6i_ref);
  
  		in->fn_sernum = sernum;
  
  		/* update parent pointer */
  		if (dir)
  			pn->right = in;
  		else
  			pn->left  = in;
  
  		ln->fn_bit = plen;
  
  		ln->parent = in;
  		fn->parent = in;
  
  		ln->fn_sernum = sernum;
  
  		if (addr_bit_set(addr, bit)) {
  			in->right = ln;
  			in->left  = fn;
  		} else {
  			in->left  = ln;
  			in->right = fn;
  		}
  	} else { /* plen <= bit */
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
554
  		/*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
  		 *		(new leaf node)[ln]
  		 *	          /	   \
  		 *	     (old node)[fn] NULL
  		 */
  
  		ln = node_alloc();
  
  		if (ln == NULL)
  			return NULL;
  
  		ln->fn_bit = plen;
  
  		ln->parent = pn;
  
  		ln->fn_sernum = sernum;
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
570

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
  		if (dir)
  			pn->right = ln;
  		else
  			pn->left  = ln;
  
  		if (addr_bit_set(&key->addr, plen))
  			ln->right = fn;
  		else
  			ln->left  = fn;
  
  		fn->parent = ln;
  	}
  	return ln;
  }
  
  /*
   *	Insert routing information in a node.
   */
  
  static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
86872cb57   Thomas Graf   [IPv6] route: FIB...
591
  			    struct nl_info *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
592
593
594
595
596
  {
  	struct rt6_info *iter = NULL;
  	struct rt6_info **ins;
  
  	ins = &fn->leaf;
d8d1f30b9   Changli Gao   net-next: remove ...
597
  	for (iter = fn->leaf; iter; iter=iter->dst.rt6_next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
  		/*
  		 *	Search for duplicates
  		 */
  
  		if (iter->rt6i_metric == rt->rt6i_metric) {
  			/*
  			 *	Same priority level
  			 */
  
  			if (iter->rt6i_dev == rt->rt6i_dev &&
  			    iter->rt6i_idev == rt->rt6i_idev &&
  			    ipv6_addr_equal(&iter->rt6i_gateway,
  					    &rt->rt6i_gateway)) {
  				if (!(iter->rt6i_flags&RTF_EXPIRES))
  					return -EEXIST;
  				iter->rt6i_expires = rt->rt6i_expires;
  				if (!(rt->rt6i_flags&RTF_EXPIRES)) {
  					iter->rt6i_flags &= ~RTF_EXPIRES;
  					iter->rt6i_expires = 0;
  				}
  				return -EEXIST;
  			}
  		}
  
  		if (iter->rt6i_metric > rt->rt6i_metric)
  			break;
d8d1f30b9   Changli Gao   net-next: remove ...
624
  		ins = &iter->dst.rt6_next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
625
  	}
f11e6659c   David S. Miller   [IPV6]: Fix routi...
626
627
628
  	/* Reset round-robin state, if necessary */
  	if (ins == &fn->leaf)
  		fn->rr_ptr = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
629
630
631
  	/*
  	 *	insert node
  	 */
d8d1f30b9   Changli Gao   net-next: remove ...
632
  	rt->dst.rt6_next = iter;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
633
634
635
  	*ins = rt;
  	rt->rt6i_node = fn;
  	atomic_inc(&rt->rt6i_ref);
86872cb57   Thomas Graf   [IPv6] route: FIB...
636
  	inet6_rt_notify(RTM_NEWROUTE, rt, info);
c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
637
  	info->nl_net->ipv6.rt6_stats->fib_rt_entries++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638
639
  
  	if ((fn->fn_flags & RTN_RTINFO) == 0) {
c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
640
  		info->nl_net->ipv6.rt6_stats->fib_route_nodes++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
641
642
643
644
645
  		fn->fn_flags |= RTN_RTINFO;
  	}
  
  	return 0;
  }
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
646
  static __inline__ void fib6_start_gc(struct net *net, struct rt6_info *rt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
647
  {
417f28bb3   Stephen Hemminger   netns: dont alloc...
648
  	if (!timer_pending(&net->ipv6.ip6_fib_timer) &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
649
  	    (rt->rt6i_flags & (RTF_EXPIRES|RTF_CACHE)))
417f28bb3   Stephen Hemminger   netns: dont alloc...
650
  		mod_timer(&net->ipv6.ip6_fib_timer,
847499ce7   Stephen Hemminger   ipv6: use timer p...
651
  			  jiffies + net->ipv6.sysctl.ip6_rt_gc_interval);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
652
  }
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
653
  void fib6_force_start_gc(struct net *net)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
654
  {
417f28bb3   Stephen Hemminger   netns: dont alloc...
655
656
  	if (!timer_pending(&net->ipv6.ip6_fib_timer))
  		mod_timer(&net->ipv6.ip6_fib_timer,
847499ce7   Stephen Hemminger   ipv6: use timer p...
657
  			  jiffies + net->ipv6.sysctl.ip6_rt_gc_interval);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
658
659
660
661
662
663
664
  }
  
  /*
   *	Add routing information to the routing tree.
   *	<destination addr>/<source addr>
   *	with source addr info in sub-trees
   */
86872cb57   Thomas Graf   [IPv6] route: FIB...
665
  int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
666
  {
66729e18d   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
667
  	struct fib6_node *fn, *pn = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
668
669
670
671
672
673
674
  	int err = -ENOMEM;
  
  	fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr),
  			rt->rt6i_dst.plen, offsetof(struct rt6_info, rt6i_dst));
  
  	if (fn == NULL)
  		goto out;
66729e18d   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
675
  	pn = fn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
  #ifdef CONFIG_IPV6_SUBTREES
  	if (rt->rt6i_src.plen) {
  		struct fib6_node *sn;
  
  		if (fn->subtree == NULL) {
  			struct fib6_node *sfn;
  
  			/*
  			 * Create subtree.
  			 *
  			 *		fn[main tree]
  			 *		|
  			 *		sfn[subtree root]
  			 *		   \
  			 *		    sn[new leaf node]
  			 */
  
  			/* Create subtree root node */
  			sfn = node_alloc();
  			if (sfn == NULL)
  				goto st_failure;
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
697
698
  			sfn->leaf = info->nl_net->ipv6.ip6_null_entry;
  			atomic_inc(&info->nl_net->ipv6.ip6_null_entry->rt6i_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
  			sfn->fn_flags = RTN_ROOT;
  			sfn->fn_sernum = fib6_new_sernum();
  
  			/* Now add the first leaf node to new subtree */
  
  			sn = fib6_add_1(sfn, &rt->rt6i_src.addr,
  					sizeof(struct in6_addr), rt->rt6i_src.plen,
  					offsetof(struct rt6_info, rt6i_src));
  
  			if (sn == NULL) {
  				/* If it is failed, discard just allocated
  				   root, and then (in st_failure) stale node
  				   in main tree.
  				 */
  				node_free(sfn);
  				goto st_failure;
  			}
  
  			/* Now link new subtree to main tree */
  			sfn->parent = fn;
  			fn->subtree = sfn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
720
721
722
723
724
725
726
727
  		} else {
  			sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr,
  					sizeof(struct in6_addr), rt->rt6i_src.plen,
  					offsetof(struct rt6_info, rt6i_src));
  
  			if (sn == NULL)
  				goto st_failure;
  		}
66729e18d   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
728
729
730
731
  		if (fn->leaf == NULL) {
  			fn->leaf = rt;
  			atomic_inc(&rt->rt6i_ref);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
732
733
734
  		fn = sn;
  	}
  #endif
86872cb57   Thomas Graf   [IPv6] route: FIB...
735
  	err = fib6_add_rt2node(fn, rt, info);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
736
737
  
  	if (err == 0) {
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
738
  		fib6_start_gc(info->nl_net, rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
739
  		if (!(rt->rt6i_flags&RTF_CACHE))
ec7d43c29   Benjamin Thery   [NETNS][IPV6] ip6...
740
  			fib6_prune_clones(info->nl_net, pn, rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
741
742
743
  	}
  
  out:
66729e18d   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
744
745
746
747
748
749
  	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.
  		 */
3c051235a   David S. Miller   [IPV6]: Fix dangl...
750
751
752
753
  		if (pn != fn && pn->leaf == rt) {
  			pn->leaf = NULL;
  			atomic_dec(&rt->rt6i_ref);
  		}
66729e18d   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
754
  		if (pn != fn && !pn->leaf && !(pn->fn_flags & RTN_RTINFO)) {
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
755
  			pn->leaf = fib6_find_prefix(info->nl_net, pn);
66729e18d   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
756
757
  #if RT6_DEBUG >= 2
  			if (!pn->leaf) {
547b792ca   Ilpo Järvinen   net: convert BUG_...
758
  				WARN_ON(pn->leaf == NULL);
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
759
  				pn->leaf = info->nl_net->ipv6.ip6_null_entry;
66729e18d   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
760
761
762
763
764
  			}
  #endif
  			atomic_inc(&pn->leaf->rt6i_ref);
  		}
  #endif
d8d1f30b9   Changli Gao   net-next: remove ...
765
  		dst_free(&rt->dst);
66729e18d   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
766
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
767
768
769
770
771
772
773
774
  	return err;
  
  #ifdef CONFIG_IPV6_SUBTREES
  	/* Subtree creation failed, probably main tree node
  	   is orphan. If it is, shoot it.
  	 */
  st_failure:
  	if (fn && !(fn->fn_flags & (RTN_RTINFO|RTN_ROOT)))
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
775
  		fib6_repair_tree(info->nl_net, fn);
d8d1f30b9   Changli Gao   net-next: remove ...
776
  	dst_free(&rt->dst);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
777
778
779
780
781
782
783
784
785
786
787
  	return err;
  #endif
  }
  
  /*
   *	Routing tree lookup
   *
   */
  
  struct lookup_args {
  	int		offset;		/* key offset on rt6_info	*/
b71d1d426   Eric Dumazet   inet: constify ip...
788
  	const struct in6_addr	*addr;		/* search key			*/
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
789
790
791
792
793
794
  };
  
  static struct fib6_node * fib6_lookup_1(struct fib6_node *root,
  					struct lookup_args *args)
  {
  	struct fib6_node *fn;
e69a4adc6   Al Viro   [IPV6]: Misc endi...
795
  	__be32 dir;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
796

825e288ef   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
797
798
  	if (unlikely(args->offset == 0))
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
  	/*
  	 *	Descend on a tree
  	 */
  
  	fn = root;
  
  	for (;;) {
  		struct fib6_node *next;
  
  		dir = addr_bit_set(args->addr, fn->fn_bit);
  
  		next = dir ? fn->right : fn->left;
  
  		if (next) {
  			fn = next;
  			continue;
  		}
  
  		break;
  	}
3fc5e0440   YOSHIFUJI Hideaki   [IPV6] ROUTE: Fix...
819
  	while(fn) {
7fc33165a   YOSHIFUJI Hideaki   [IPV6] ROUTE: Put...
820
  		if (FIB6_SUBTREE(fn) || fn->fn_flags & RTN_RTINFO) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
821
822
823
824
  			struct rt6key *key;
  
  			key = (struct rt6key *) ((u8 *) fn->leaf +
  						 args->offset);
3fc5e0440   YOSHIFUJI Hideaki   [IPV6] ROUTE: Fix...
825
826
827
828
829
830
831
832
  			if (ipv6_prefix_equal(&key->addr, args->addr, key->plen)) {
  #ifdef CONFIG_IPV6_SUBTREES
  				if (fn->subtree)
  					fn = fib6_lookup_1(fn->subtree, args + 1);
  #endif
  				if (!fn || fn->fn_flags & RTN_RTINFO)
  					return fn;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
833
  		}
3fc5e0440   YOSHIFUJI Hideaki   [IPV6] ROUTE: Fix...
834
835
  		if (fn->fn_flags & RTN_ROOT)
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
836
837
838
839
840
  		fn = fn->parent;
  	}
  
  	return NULL;
  }
b71d1d426   Eric Dumazet   inet: constify ip...
841
842
  struct fib6_node * fib6_lookup(struct fib6_node *root, const struct in6_addr *daddr,
  			       const struct in6_addr *saddr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
843
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
844
  	struct fib6_node *fn;
825e288ef   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
845
846
847
848
849
  	struct lookup_args args[] = {
  		{
  			.offset = offsetof(struct rt6_info, rt6i_dst),
  			.addr = daddr,
  		},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
850
  #ifdef CONFIG_IPV6_SUBTREES
825e288ef   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
851
852
853
854
  		{
  			.offset = offsetof(struct rt6_info, rt6i_src),
  			.addr = saddr,
  		},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
855
  #endif
825e288ef   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
856
857
858
859
  		{
  			.offset = 0,	/* sentinel */
  		}
  	};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
860

fefc2a6c2   YOSHIFUJI Hideaki   [IPV6] ROUTE: All...
861
  	fn = fib6_lookup_1(root, daddr ? args : args + 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
862
863
864
865
866
867
868
869
870
871
872
873
874
875
  
  	if (fn == NULL || fn->fn_flags & RTN_TL_ROOT)
  		fn = root;
  
  	return fn;
  }
  
  /*
   *	Get node with specified destination prefix (and source prefix,
   *	if subtrees are used)
   */
  
  
  static struct fib6_node * fib6_locate_1(struct fib6_node *root,
b71d1d426   Eric Dumazet   inet: constify ip...
876
  					const struct in6_addr *addr,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
  					int plen, int offset)
  {
  	struct fib6_node *fn;
  
  	for (fn = root; fn ; ) {
  		struct rt6key *key = (struct rt6key *)((u8 *)fn->leaf + offset);
  
  		/*
  		 *	Prefix match
  		 */
  		if (plen < fn->fn_bit ||
  		    !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit))
  			return NULL;
  
  		if (plen == fn->fn_bit)
  			return fn;
  
  		/*
  		 *	We have more bits to go
  		 */
  		if (addr_bit_set(addr, fn->fn_bit))
  			fn = fn->right;
  		else
  			fn = fn->left;
  	}
  	return NULL;
  }
  
  struct fib6_node * fib6_locate(struct fib6_node *root,
b71d1d426   Eric Dumazet   inet: constify ip...
906
907
  			       const struct in6_addr *daddr, int dst_len,
  			       const struct in6_addr *saddr, int src_len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
908
909
910
911
912
913
914
915
  {
  	struct fib6_node *fn;
  
  	fn = fib6_locate_1(root, daddr, dst_len,
  			   offsetof(struct rt6_info, rt6i_dst));
  
  #ifdef CONFIG_IPV6_SUBTREES
  	if (src_len) {
547b792ca   Ilpo Järvinen   net: convert BUG_...
916
  		WARN_ON(saddr == NULL);
3fc5e0440   YOSHIFUJI Hideaki   [IPV6] ROUTE: Fix...
917
918
  		if (fn && fn->subtree)
  			fn = fib6_locate_1(fn->subtree, saddr, src_len,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
  					   offsetof(struct rt6_info, rt6i_src));
  	}
  #endif
  
  	if (fn && fn->fn_flags&RTN_RTINFO)
  		return fn;
  
  	return NULL;
  }
  
  
  /*
   *	Deletion
   *
   */
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
934
  static struct rt6_info *fib6_find_prefix(struct net *net, struct fib6_node *fn)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
935
936
  {
  	if (fn->fn_flags&RTN_ROOT)
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
937
  		return net->ipv6.ip6_null_entry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
938
939
940
941
942
943
944
  
  	while(fn) {
  		if(fn->left)
  			return fn->left->leaf;
  
  		if(fn->right)
  			return fn->right->leaf;
7fc33165a   YOSHIFUJI Hideaki   [IPV6] ROUTE: Put...
945
  		fn = FIB6_SUBTREE(fn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
946
947
948
949
950
951
952
953
  	}
  	return NULL;
  }
  
  /*
   *	Called to trim the tree of intermediate nodes when possible. "fn"
   *	is the node we want to try and remove.
   */
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
954
955
  static struct fib6_node *fib6_repair_tree(struct net *net,
  					   struct fib6_node *fn)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
956
957
958
959
960
961
962
963
964
965
966
  {
  	int children;
  	int nstate;
  	struct fib6_node *child, *pn;
  	struct fib6_walker_t *w;
  	int iter = 0;
  
  	for (;;) {
  		RT6_TRACE("fixing tree: plen=%d iter=%d
  ", fn->fn_bit, iter);
  		iter++;
547b792ca   Ilpo Järvinen   net: convert BUG_...
967
968
969
  		WARN_ON(fn->fn_flags & RTN_RTINFO);
  		WARN_ON(fn->fn_flags & RTN_TL_ROOT);
  		WARN_ON(fn->leaf != NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
970
971
972
973
974
  
  		children = 0;
  		child = NULL;
  		if (fn->right) child = fn->right, children |= 1;
  		if (fn->left) child = fn->left, children |= 2;
7fc33165a   YOSHIFUJI Hideaki   [IPV6] ROUTE: Put...
975
  		if (children == 3 || FIB6_SUBTREE(fn)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
976
977
978
979
980
  #ifdef CONFIG_IPV6_SUBTREES
  		    /* Subtree root (i.e. fn) may have one child */
  		    || (children && fn->fn_flags&RTN_ROOT)
  #endif
  		    ) {
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
981
  			fn->leaf = fib6_find_prefix(net, fn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
982
983
  #if RT6_DEBUG >= 2
  			if (fn->leaf==NULL) {
547b792ca   Ilpo Järvinen   net: convert BUG_...
984
  				WARN_ON(!fn->leaf);
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
985
  				fn->leaf = net->ipv6.ip6_null_entry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
986
987
988
989
990
991
992
993
  			}
  #endif
  			atomic_inc(&fn->leaf->rt6i_ref);
  			return fn->parent;
  		}
  
  		pn = fn->parent;
  #ifdef CONFIG_IPV6_SUBTREES
7fc33165a   YOSHIFUJI Hideaki   [IPV6] ROUTE: Put...
994
  		if (FIB6_SUBTREE(pn) == fn) {
547b792ca   Ilpo Järvinen   net: convert BUG_...
995
  			WARN_ON(!(fn->fn_flags & RTN_ROOT));
7fc33165a   YOSHIFUJI Hideaki   [IPV6] ROUTE: Put...
996
  			FIB6_SUBTREE(pn) = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
997
998
  			nstate = FWS_L;
  		} else {
547b792ca   Ilpo Järvinen   net: convert BUG_...
999
  			WARN_ON(fn->fn_flags & RTN_ROOT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1000
1001
1002
1003
  #endif
  			if (pn->right == fn) pn->right = child;
  			else if (pn->left == fn) pn->left = child;
  #if RT6_DEBUG >= 2
547b792ca   Ilpo Järvinen   net: convert BUG_...
1004
1005
  			else
  				WARN_ON(1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
  #endif
  			if (child)
  				child->parent = pn;
  			nstate = FWS_R;
  #ifdef CONFIG_IPV6_SUBTREES
  		}
  #endif
  
  		read_lock(&fib6_walker_lock);
  		FOR_WALKERS(w) {
  			if (child == NULL) {
  				if (w->root == fn) {
  					w->root = w->node = NULL;
  					RT6_TRACE("W %p adjusted by delroot 1
  ", w);
  				} else if (w->node == fn) {
  					RT6_TRACE("W %p adjusted by delnode 1, s=%d/%d
  ", w, w->state, nstate);
  					w->node = pn;
  					w->state = nstate;
  				}
  			} else {
  				if (w->root == fn) {
  					w->root = child;
  					RT6_TRACE("W %p adjusted by delroot 2
  ", w);
  				}
  				if (w->node == fn) {
  					w->node = child;
  					if (children&2) {
  						RT6_TRACE("W %p adjusted by delnode 2, s=%d
  ", w, w->state);
  						w->state = w->state>=FWS_R ? FWS_U : FWS_INIT;
  					} else {
  						RT6_TRACE("W %p adjusted by delnode 2, s=%d
  ", w, w->state);
  						w->state = w->state>=FWS_C ? FWS_U : FWS_INIT;
  					}
  				}
  			}
  		}
  		read_unlock(&fib6_walker_lock);
  
  		node_free(fn);
7fc33165a   YOSHIFUJI Hideaki   [IPV6] ROUTE: Put...
1050
  		if (pn->fn_flags&RTN_RTINFO || FIB6_SUBTREE(pn))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1051
1052
1053
1054
1055
1056
1057
1058
1059
  			return pn;
  
  		rt6_release(pn->leaf);
  		pn->leaf = NULL;
  		fn = pn;
  	}
  }
  
  static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp,
86872cb57   Thomas Graf   [IPv6] route: FIB...
1060
  			   struct nl_info *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1061
1062
1063
  {
  	struct fib6_walker_t *w;
  	struct rt6_info *rt = *rtp;
c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
1064
  	struct net *net = info->nl_net;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1065
1066
1067
1068
1069
  
  	RT6_TRACE("fib6_del_route
  ");
  
  	/* Unlink it */
d8d1f30b9   Changli Gao   net-next: remove ...
1070
  	*rtp = rt->dst.rt6_next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1071
  	rt->rt6i_node = NULL;
c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
1072
1073
  	net->ipv6.rt6_stats->fib_rt_entries--;
  	net->ipv6.rt6_stats->fib_discarded_routes++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1074

f11e6659c   David S. Miller   [IPV6]: Fix routi...
1075
1076
1077
  	/* Reset round-robin state, if necessary */
  	if (fn->rr_ptr == rt)
  		fn->rr_ptr = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1078
1079
1080
1081
1082
1083
  	/* Adjust walkers */
  	read_lock(&fib6_walker_lock);
  	FOR_WALKERS(w) {
  		if (w->state == FWS_C && w->leaf == rt) {
  			RT6_TRACE("walker %p adjusted by delroute
  ", w);
d8d1f30b9   Changli Gao   net-next: remove ...
1084
  			w->leaf = rt->dst.rt6_next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1085
1086
1087
1088
1089
  			if (w->leaf == NULL)
  				w->state = FWS_U;
  		}
  	}
  	read_unlock(&fib6_walker_lock);
d8d1f30b9   Changli Gao   net-next: remove ...
1090
  	rt->dst.rt6_next = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1091

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1092
1093
1094
  	/* If it was last route, expunge its radix tree node */
  	if (fn->leaf == NULL) {
  		fn->fn_flags &= ~RTN_RTINFO;
c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
1095
  		net->ipv6.rt6_stats->fib_route_nodes--;
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
1096
  		fn = fib6_repair_tree(net, fn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
  	}
  
  	if (atomic_read(&rt->rt6i_ref) != 1) {
  		/* 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) {
  			if (!(fn->fn_flags&RTN_RTINFO) && fn->leaf == rt) {
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
1108
  				fn->leaf = fib6_find_prefix(net, fn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1109
1110
1111
1112
1113
1114
  				atomic_inc(&fn->leaf->rt6i_ref);
  				rt6_release(rt);
  			}
  			fn = fn->parent;
  		}
  		/* No more references are possible at this point. */
2df96af03   Pavel Emelyanov   [IPV6]: Use BUG_O...
1115
  		BUG_ON(atomic_read(&rt->rt6i_ref) != 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1116
  	}
86872cb57   Thomas Graf   [IPv6] route: FIB...
1117
  	inet6_rt_notify(RTM_DELROUTE, rt, info);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1118
1119
  	rt6_release(rt);
  }
86872cb57   Thomas Graf   [IPv6] route: FIB...
1120
  int fib6_del(struct rt6_info *rt, struct nl_info *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1121
  {
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
1122
  	struct net *net = info->nl_net;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1123
1124
1125
1126
  	struct fib6_node *fn = rt->rt6i_node;
  	struct rt6_info **rtp;
  
  #if RT6_DEBUG >= 2
d8d1f30b9   Changli Gao   net-next: remove ...
1127
  	if (rt->dst.obsolete>0) {
547b792ca   Ilpo Järvinen   net: convert BUG_...
1128
  		WARN_ON(fn != NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1129
1130
1131
  		return -ENOENT;
  	}
  #endif
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
1132
  	if (fn == NULL || rt == net->ipv6.ip6_null_entry)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1133
  		return -ENOENT;
547b792ca   Ilpo Järvinen   net: convert BUG_...
1134
  	WARN_ON(!(fn->fn_flags & RTN_RTINFO));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1135

150730d5a   YOSHIFUJI Hideaki   [IPV6] ROUTE: Pur...
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
  	if (!(rt->rt6i_flags&RTF_CACHE)) {
  		struct fib6_node *pn = fn;
  #ifdef CONFIG_IPV6_SUBTREES
  		/* clones of this route might be in another subtree */
  		if (rt->rt6i_src.plen) {
  			while (!(pn->fn_flags&RTN_ROOT))
  				pn = pn->parent;
  			pn = pn->parent;
  		}
  #endif
ec7d43c29   Benjamin Thery   [NETNS][IPV6] ip6...
1146
  		fib6_prune_clones(info->nl_net, pn, rt);
150730d5a   YOSHIFUJI Hideaki   [IPV6] ROUTE: Pur...
1147
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1148
1149
1150
1151
  
  	/*
  	 *	Walk the leaf entries looking for ourself
  	 */
d8d1f30b9   Changli Gao   net-next: remove ...
1152
  	for (rtp = &fn->leaf; *rtp; rtp = &(*rtp)->dst.rt6_next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1153
  		if (*rtp == rt) {
86872cb57   Thomas Graf   [IPv6] route: FIB...
1154
  			fib6_del_route(fn, rtp, info);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
  			return 0;
  		}
  	}
  	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...
1168
   *	cleaning tree when a device goes down etc. etc.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
   *
   *	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.
   */
90d41122f   Adrian Bunk   [IPV6] ip6_fib.c:...
1184
  static int fib6_walk_continue(struct fib6_walker_t *w)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
  {
  	struct fib6_node *fn, *pn;
  
  	for (;;) {
  		fn = w->node;
  		if (fn == NULL)
  			return 0;
  
  		if (w->prune && fn != w->root &&
  		    fn->fn_flags&RTN_RTINFO && w->state < FWS_C) {
  			w->state = FWS_C;
  			w->leaf = fn->leaf;
  		}
  		switch (w->state) {
  #ifdef CONFIG_IPV6_SUBTREES
  		case FWS_S:
7fc33165a   YOSHIFUJI Hideaki   [IPV6] ROUTE: Put...
1201
1202
  			if (FIB6_SUBTREE(fn)) {
  				w->node = FIB6_SUBTREE(fn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1203
1204
1205
  				continue;
  			}
  			w->state = FWS_L;
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
1206
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
  		case FWS_L:
  			if (fn->left) {
  				w->node = fn->left;
  				w->state = FWS_INIT;
  				continue;
  			}
  			w->state = FWS_R;
  		case FWS_R:
  			if (fn->right) {
  				w->node = fn->right;
  				w->state = FWS_INIT;
  				continue;
  			}
  			w->state = FWS_C;
  			w->leaf = fn->leaf;
  		case FWS_C:
  			if (w->leaf && fn->fn_flags&RTN_RTINFO) {
2bec5a369   Patrick McHardy   ipv6: fib: fix cr...
1224
1225
1226
1227
1228
1229
1230
1231
  				int err;
  
  				if (w->count < w->skip) {
  					w->count++;
  					continue;
  				}
  
  				err = w->func(w);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1232
1233
  				if (err)
  					return err;
2bec5a369   Patrick McHardy   ipv6: fib: fix cr...
1234
1235
  
  				w->count++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1236
1237
1238
1239
1240
1241
1242
1243
1244
  				continue;
  			}
  			w->state = FWS_U;
  		case FWS_U:
  			if (fn == w->root)
  				return 0;
  			pn = fn->parent;
  			w->node = pn;
  #ifdef CONFIG_IPV6_SUBTREES
7fc33165a   YOSHIFUJI Hideaki   [IPV6] ROUTE: Put...
1245
  			if (FIB6_SUBTREE(pn) == fn) {
547b792ca   Ilpo Järvinen   net: convert BUG_...
1246
  				WARN_ON(!(fn->fn_flags & RTN_ROOT));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
  				w->state = FWS_L;
  				continue;
  			}
  #endif
  			if (pn->left == fn) {
  				w->state = FWS_R;
  				continue;
  			}
  			if (pn->right == fn) {
  				w->state = FWS_C;
  				w->leaf = w->node->leaf;
  				continue;
  			}
  #if RT6_DEBUG >= 2
547b792ca   Ilpo Järvinen   net: convert BUG_...
1261
  			WARN_ON(1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1262
1263
1264
1265
  #endif
  		}
  	}
  }
90d41122f   Adrian Bunk   [IPV6] ip6_fib.c:...
1266
  static int fib6_walk(struct fib6_walker_t *w)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
  {
  	int res;
  
  	w->state = FWS_INIT;
  	w->node = w->root;
  
  	fib6_walker_link(w);
  	res = fib6_walk_continue(w);
  	if (res <= 0)
  		fib6_walker_unlink(w);
  	return res;
  }
  
  static int fib6_clean_node(struct fib6_walker_t *w)
  {
  	int res;
  	struct rt6_info *rt;
0a8891a0a   Benjamin Thery   [IPv6]: use conta...
1284
  	struct fib6_cleaner_t *c = container_of(w, struct fib6_cleaner_t, w);
ec7d43c29   Benjamin Thery   [NETNS][IPV6] ip6...
1285
1286
1287
  	struct nl_info info = {
  		.nl_net = c->net,
  	};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1288

d8d1f30b9   Changli Gao   net-next: remove ...
1289
  	for (rt = w->leaf; rt; rt = rt->dst.rt6_next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1290
1291
1292
  		res = c->func(rt, c->arg);
  		if (res < 0) {
  			w->leaf = rt;
528c4ceb4   Denis V. Lunev   [IPV6]: Always pa...
1293
  			res = fib6_del(rt, &info);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1294
1295
1296
1297
1298
1299
1300
1301
1302
  			if (res) {
  #if RT6_DEBUG >= 2
  				printk(KERN_DEBUG "fib6_clean_node: del failed: rt=%p@%p err=%d
  ", rt, rt->rt6i_node, res);
  #endif
  				continue;
  			}
  			return 0;
  		}
547b792ca   Ilpo Järvinen   net: convert BUG_...
1303
  		WARN_ON(res != 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1304
1305
1306
1307
1308
1309
1310
  	}
  	w->leaf = rt;
  	return 0;
  }
  
  /*
   *	Convenient frontend to tree walker.
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
1311
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1312
1313
1314
1315
1316
1317
1318
   *	func is called on each route.
   *		It may return -1 -> delete this route.
   *		              0  -> continue walking
   *
   *	prune==1 -> only immediate children of node (certainly,
   *	ignoring pure split nodes) will be scanned.
   */
ec7d43c29   Benjamin Thery   [NETNS][IPV6] ip6...
1319
  static void fib6_clean_tree(struct net *net, struct fib6_node *root,
8ce11e6a9   Adrian Bunk   [NET]: Make code ...
1320
1321
  			    int (*func)(struct rt6_info *, void *arg),
  			    int prune, void *arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1322
1323
1324
1325
1326
1327
  {
  	struct fib6_cleaner_t c;
  
  	c.w.root = root;
  	c.w.func = fib6_clean_node;
  	c.w.prune = prune;
2bec5a369   Patrick McHardy   ipv6: fib: fix cr...
1328
1329
  	c.w.count = 0;
  	c.w.skip = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1330
1331
  	c.func = func;
  	c.arg = arg;
ec7d43c29   Benjamin Thery   [NETNS][IPV6] ip6...
1332
  	c.net = net;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1333
1334
1335
  
  	fib6_walk(&c.w);
  }
f3db48517   Daniel Lezcano   [NETNS][IPV6] ip6...
1336
  void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg),
c71099acc   Thomas Graf   [IPV6]: Multiple ...
1337
1338
  		    int prune, void *arg)
  {
c71099acc   Thomas Graf   [IPV6]: Multiple ...
1339
  	struct fib6_table *table;
1b43af548   Patrick McHardy   [IPV6]: Increase ...
1340
  	struct hlist_node *node;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1341
  	struct hlist_head *head;
1b43af548   Patrick McHardy   [IPV6]: Increase ...
1342
  	unsigned int h;
c71099acc   Thomas Graf   [IPV6]: Multiple ...
1343

1b43af548   Patrick McHardy   [IPV6]: Increase ...
1344
  	rcu_read_lock();
a33bc5c15   Neil Horman   xfrm: select sane...
1345
  	for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
f3db48517   Daniel Lezcano   [NETNS][IPV6] ip6...
1346
  		head = &net->ipv6.fib_table_hash[h];
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1347
  		hlist_for_each_entry_rcu(table, node, head, tb6_hlist) {
c71099acc   Thomas Graf   [IPV6]: Multiple ...
1348
  			write_lock_bh(&table->tb6_lock);
ec7d43c29   Benjamin Thery   [NETNS][IPV6] ip6...
1349
1350
  			fib6_clean_tree(net, &table->tb6_root,
  					func, prune, arg);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
1351
1352
1353
  			write_unlock_bh(&table->tb6_lock);
  		}
  	}
1b43af548   Patrick McHardy   [IPV6]: Increase ...
1354
  	rcu_read_unlock();
c71099acc   Thomas Graf   [IPV6]: Multiple ...
1355
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
  static int fib6_prune_clone(struct rt6_info *rt, void *arg)
  {
  	if (rt->rt6i_flags & RTF_CACHE) {
  		RT6_TRACE("pruning clone %p
  ", rt);
  		return -1;
  	}
  
  	return 0;
  }
ec7d43c29   Benjamin Thery   [NETNS][IPV6] ip6...
1366
1367
  static void fib6_prune_clones(struct net *net, struct fib6_node *fn,
  			      struct rt6_info *rt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1368
  {
ec7d43c29   Benjamin Thery   [NETNS][IPV6] ip6...
1369
  	fib6_clean_tree(net, fn, fib6_prune_clone, 1, rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
  }
  
  /*
   *	Garbage collection
   */
  
  static struct fib6_gc_args
  {
  	int			timeout;
  	int			more;
  } gc_args;
  
  static int fib6_age(struct rt6_info *rt, void *arg)
  {
  	unsigned long now = jiffies;
  
  	/*
  	 *	check addrconf expiration here.
  	 *	Routes are expired even if they are in use.
  	 *
  	 *	Also age clones. Note, that clones are aged out
  	 *	only if they are not in use now.
  	 */
  
  	if (rt->rt6i_flags&RTF_EXPIRES && rt->rt6i_expires) {
  		if (time_after(now, rt->rt6i_expires)) {
  			RT6_TRACE("expiring %p
  ", rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1398
1399
1400
1401
  			return -1;
  		}
  		gc_args.more++;
  	} else if (rt->rt6i_flags & RTF_CACHE) {
d8d1f30b9   Changli Gao   net-next: remove ...
1402
1403
  		if (atomic_read(&rt->dst.__refcnt) == 0 &&
  		    time_after_eq(now, rt->dst.lastuse + gc_args.timeout)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1404
1405
1406
1407
  			RT6_TRACE("aging clone %p
  ", rt);
  			return -1;
  		} else if ((rt->rt6i_flags & RTF_GATEWAY) &&
f2c31e32b   Eric Dumazet   net: fix NULL der...
1408
  			   (!(dst_get_neighbour_raw(&rt->dst)->flags & NTF_ROUTER))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
  			RT6_TRACE("purging route %p via non-router but gateway
  ",
  				  rt);
  			return -1;
  		}
  		gc_args.more++;
  	}
  
  	return 0;
  }
  
  static DEFINE_SPINLOCK(fib6_gc_lock);
5b7c931df   Daniel Lezcano   [NETNS][IPV6] ip6...
1421
  void fib6_run_gc(unsigned long expires, struct net *net)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1422
  {
5b7c931df   Daniel Lezcano   [NETNS][IPV6] ip6...
1423
  	if (expires != ~0UL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1424
  		spin_lock_bh(&fib6_gc_lock);
5b7c931df   Daniel Lezcano   [NETNS][IPV6] ip6...
1425
1426
  		gc_args.timeout = expires ? (int)expires :
  			net->ipv6.sysctl.ip6_rt_gc_interval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1427
  	} else {
a76d7345a   Stephen Hemminger   ipv6: use spin_tr...
1428
  		if (!spin_trylock_bh(&fib6_gc_lock)) {
417f28bb3   Stephen Hemminger   netns: dont alloc...
1429
  			mod_timer(&net->ipv6.ip6_fib_timer, jiffies + HZ);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1430
1431
  			return;
  		}
5b7c931df   Daniel Lezcano   [NETNS][IPV6] ip6...
1432
  		gc_args.timeout = net->ipv6.sysctl.ip6_rt_gc_interval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1433
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1434

3d0f24a74   Stephen Hemminger   ipv6: icmp6_dst_g...
1435
  	gc_args.more = icmp6_dst_gc();
f3db48517   Daniel Lezcano   [NETNS][IPV6] ip6...
1436

5b7c931df   Daniel Lezcano   [NETNS][IPV6] ip6...
1437
  	fib6_clean_all(net, fib6_age, 0, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1438
1439
  
  	if (gc_args.more)
c8a452224   Stephen Hemminger   ipv6: use round_j...
1440
1441
1442
  		mod_timer(&net->ipv6.ip6_fib_timer,
  			  round_jiffies(jiffies
  					+ net->ipv6.sysctl.ip6_rt_gc_interval));
417f28bb3   Stephen Hemminger   netns: dont alloc...
1443
1444
  	else
  		del_timer(&net->ipv6.ip6_fib_timer);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1445
1446
  	spin_unlock_bh(&fib6_gc_lock);
  }
5b7c931df   Daniel Lezcano   [NETNS][IPV6] ip6...
1447
1448
1449
1450
  static void fib6_gc_timer_cb(unsigned long arg)
  {
  	fib6_run_gc(0, (struct net *)arg);
  }
2c8c1e729   Alexey Dobriyan   net: spread __net...
1451
  static int __net_init fib6_net_init(struct net *net)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1452
  {
10da66f75   Eric Dumazet   fib: avoid false ...
1453
  	size_t size = sizeof(struct hlist_head) * FIB6_TABLE_HASHSZ;
417f28bb3   Stephen Hemminger   netns: dont alloc...
1454
  	setup_timer(&net->ipv6.ip6_fib_timer, fib6_gc_timer_cb, (unsigned long)net);
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
1455

c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
1456
1457
1458
  	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 ...
1459
1460
1461
1462
  	/* 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...
1463
  	if (!net->ipv6.fib_table_hash)
c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
1464
  		goto out_rt6_stats;
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
1465

58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1466
1467
1468
  	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...
1469
  		goto out_fib_table_hash;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1470
  	net->ipv6.fib6_main_tbl->tb6_id = RT6_TABLE_MAIN;
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
1471
  	net->ipv6.fib6_main_tbl->tb6_root.leaf = net->ipv6.ip6_null_entry;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1472
1473
  	net->ipv6.fib6_main_tbl->tb6_root.fn_flags =
  		RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
1474
1475
  
  #ifdef CONFIG_IPV6_MULTIPLE_TABLES
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1476
1477
1478
  	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...
1479
  		goto out_fib6_main_tbl;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1480
  	net->ipv6.fib6_local_tbl->tb6_id = RT6_TABLE_LOCAL;
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
1481
  	net->ipv6.fib6_local_tbl->tb6_root.leaf = net->ipv6.ip6_null_entry;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1482
1483
  	net->ipv6.fib6_local_tbl->tb6_root.fn_flags =
  		RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
1484
  #endif
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1485
  	fib6_tables_init(net);
f845ab6b7   Daniel Lezcano   [IPV6] route6/fib...
1486

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

e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
1489
  #ifdef CONFIG_IPV6_MULTIPLE_TABLES
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
1490
  out_fib6_main_tbl:
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1491
  	kfree(net->ipv6.fib6_main_tbl);
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
1492
  #endif
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
1493
  out_fib_table_hash:
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1494
  	kfree(net->ipv6.fib_table_hash);
c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
1495
1496
  out_rt6_stats:
  	kfree(net->ipv6.rt6_stats);
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
1497
  out_timer:
417f28bb3   Stephen Hemminger   netns: dont alloc...
1498
  	return -ENOMEM;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1499
1500
1501
1502
   }
  
  static void fib6_net_exit(struct net *net)
  {
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
1503
  	rt6_ifdown(net, NULL);
417f28bb3   Stephen Hemminger   netns: dont alloc...
1504
  	del_timer_sync(&net->ipv6.ip6_fib_timer);
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1505
1506
1507
1508
1509
  #ifdef CONFIG_IPV6_MULTIPLE_TABLES
  	kfree(net->ipv6.fib6_local_tbl);
  #endif
  	kfree(net->ipv6.fib6_main_tbl);
  	kfree(net->ipv6.fib_table_hash);
c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
1510
  	kfree(net->ipv6.rt6_stats);
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
  }
  
  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...
1521

58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1522
1523
1524
1525
1526
1527
1528
1529
1530
  	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...
1531
  		goto out_kmem_cache_create;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1532

c7ac8679b   Greg Rose   rtnetlink: Comput...
1533
1534
  	ret = __rtnl_register(PF_INET6, RTM_GETROUTE, NULL, inet6_dump_fib,
  			      NULL);
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1535
1536
1537
1538
1539
1540
1541
  	if (ret)
  		goto out_unregister_subsys;
  out:
  	return ret;
  
  out_unregister_subsys:
  	unregister_pernet_subsys(&fib6_net_ops);
d63bddbe9   Daniel Lezcano   [IPV6]: Make fib6...
1542
1543
1544
  out_kmem_cache_create:
  	kmem_cache_destroy(fib6_node_kmem);
  	goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1545
1546
1547
1548
  }
  
  void fib6_gc_cleanup(void)
  {
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1549
  	unregister_pernet_subsys(&fib6_net_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1550
1551
  	kmem_cache_destroy(fib6_node_kmem);
  }