Blame view

net/ipv6/ip6_fib.c 32.5 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
8
9
10
11
12
13
14
15
16
17
18
19
20
   *
   *	$Id: ip6_fib.c,v 1.25 2001/10/31 21:55:55 davem Exp $
   *
   *	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...
21
   * 	Ville Nuorvala:		Fixed routing subtrees.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
25
26
27
28
29
  #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 ...
30
  #include <linux/list.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  
  #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...
50
  static struct kmem_cache * fib6_node_kmem __read_mostly;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
  
  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...
66
  	struct net *net;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
68
69
  	int (*func)(struct rt6_info *, void *arg);
  	void *arg;
  };
90d41122f   Adrian Bunk   [IPV6] ip6_fib.c:...
70
  static DEFINE_RWLOCK(fib6_walker_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
72
73
  
  #ifdef CONFIG_IPV6_SUBTREES
  #define FWS_INIT FWS_S
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
75
  #else
  #define FWS_INIT FWS_L
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
  #endif
ec7d43c29   Benjamin Thery   [NETNS][IPV6] ip6...
77
78
  static void fib6_prune_clones(struct net *net, struct fib6_node *fn,
  			      struct rt6_info *rt);
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
79
80
  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:...
81
82
  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
83
84
85
86
87
88
89
90
91
  
  /*
   *	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...
92
  static void fib6_gc_timer_cb(unsigned long arg);
90d41122f   Adrian Bunk   [IPV6] ip6_fib.c:...
93
  static struct fib6_walker_t fib6_walker_list = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
  	.prev	= &fib6_walker_list,
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
95
  	.next	= &fib6_walker_list,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
97
98
  };
  
  #define FOR_WALKERS(w) for ((w)=fib6_walker_list.next; (w) != &fib6_walker_list; (w)=(w)->next)
90d41122f   Adrian Bunk   [IPV6] ip6_fib.c:...
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
  static inline void fib6_walker_link(struct fib6_walker_t *w)
  {
  	write_lock_bh(&fib6_walker_lock);
  	w->next = fib6_walker_list.next;
  	w->prev = &fib6_walker_list;
  	w->next->prev = w;
  	w->prev->next = w;
  	write_unlock_bh(&fib6_walker_lock);
  }
  
  static inline void fib6_walker_unlink(struct fib6_walker_t *w)
  {
  	write_lock_bh(&fib6_walker_lock);
  	w->next->prev = w->prev;
  	w->prev->next = w->next;
  	w->prev = w->next = w;
  	write_unlock_bh(&fib6_walker_lock);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
118
119
120
121
122
123
124
125
126
127
  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...
128
   *	These assume a 32bit processor (although it will work on
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
130
131
132
133
134
   *	64bit processors)
   */
  
  /*
   *	test bit
   */
e69a4adc6   Al Viro   [IPV6]: Misc endi...
135
  static __inline__ __be32 addr_bit_set(void *token, int fn_bit)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
  {
e69a4adc6   Al Viro   [IPV6]: Misc endi...
137
  	__be32 *addr = token;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
139
140
  
  	return htonl(1 << ((~fn_bit)&0x1F)) & addr[fn_bit>>5];
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
142
143
  static __inline__ struct fib6_node * node_alloc(void)
  {
  	struct fib6_node *fn;
c37622296   Robert P. J. Day   [PATCH] Transform...
144
  	fn = kmem_cache_zalloc(fib6_node_kmem, GFP_ATOMIC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
146
147
148
149
150
151
152
153
154
155
156
157
158
  
  	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))
  		dst_free(&rt->u.dst);
  }
c71099acc   Thomas Graf   [IPV6]: Multiple ...
159
  #ifdef CONFIG_IPV6_MULTIPLE_TABLES
1b43af548   Patrick McHardy   [IPV6]: Increase ...
160
161
162
163
  #define FIB_TABLE_HASHSZ 256
  #else
  #define FIB_TABLE_HASHSZ 1
  #endif
1b43af548   Patrick McHardy   [IPV6]: Increase ...
164

58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
165
  static void fib6_link_table(struct net *net, struct fib6_table *tb)
1b43af548   Patrick McHardy   [IPV6]: Increase ...
166
167
  {
  	unsigned int h;
375216ad0   Thomas Graf   [IPv6] fib: initi...
168
169
170
171
172
  	/*
  	 * 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);
1b43af548   Patrick McHardy   [IPV6]: Increase ...
173
174
175
176
177
178
  	h = tb->tb6_id & (FIB_TABLE_HASHSZ - 1);
  
  	/*
  	 * No protection necessary, this is the only list mutatation
  	 * operation, tables never disappear once they exist.
  	 */
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
179
  	hlist_add_head_rcu(&tb->tb6_hlist, &net->ipv6.fib_table_hash[h]);
1b43af548   Patrick McHardy   [IPV6]: Increase ...
180
  }
c71099acc   Thomas Graf   [IPV6]: Multiple ...
181

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

8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
184
  static struct fib6_table *fib6_alloc_table(struct net *net, u32 id)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
185
186
187
188
189
190
  {
  	struct fib6_table *table;
  
  	table = kzalloc(sizeof(*table), GFP_ATOMIC);
  	if (table != NULL) {
  		table->tb6_id = id;
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
191
  		table->tb6_root.leaf = net->ipv6.ip6_null_entry;
c71099acc   Thomas Graf   [IPV6]: Multiple ...
192
193
194
195
196
  		table->tb6_root.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
  	}
  
  	return table;
  }
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
197
  struct fib6_table *fib6_new_table(struct net *net, u32 id)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
198
199
200
201
202
  {
  	struct fib6_table *tb;
  
  	if (id == 0)
  		id = RT6_TABLE_MAIN;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
203
  	tb = fib6_get_table(net, id);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
204
205
  	if (tb)
  		return tb;
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
206
  	tb = fib6_alloc_table(net, id);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
207
  	if (tb != NULL)
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
208
  		fib6_link_table(net, tb);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
209
210
211
  
  	return tb;
  }
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
212
  struct fib6_table *fib6_get_table(struct net *net, u32 id)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
213
214
  {
  	struct fib6_table *tb;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
215
  	struct hlist_head *head;
c71099acc   Thomas Graf   [IPV6]: Multiple ...
216
217
218
219
220
221
222
  	struct hlist_node *node;
  	unsigned int h;
  
  	if (id == 0)
  		id = RT6_TABLE_MAIN;
  	h = id & (FIB_TABLE_HASHSZ - 1);
  	rcu_read_lock();
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
223
224
  	head = &net->ipv6.fib_table_hash[h];
  	hlist_for_each_entry_rcu(tb, node, head, tb6_hlist) {
c71099acc   Thomas Graf   [IPV6]: Multiple ...
225
226
227
228
229
230
231
232
233
  		if (tb->tb6_id == id) {
  			rcu_read_unlock();
  			return tb;
  		}
  	}
  	rcu_read_unlock();
  
  	return NULL;
  }
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
234
  static void fib6_tables_init(struct net *net)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
235
  {
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
236
237
  	fib6_link_table(net, net->ipv6.fib6_main_tbl);
  	fib6_link_table(net, net->ipv6.fib6_local_tbl);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
238
  }
c71099acc   Thomas Graf   [IPV6]: Multiple ...
239
  #else
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
240
  struct fib6_table *fib6_new_table(struct net *net, u32 id)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
241
  {
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
242
  	return fib6_get_table(net, id);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
243
  }
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
244
  struct fib6_table *fib6_get_table(struct net *net, u32 id)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
245
  {
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
246
  	  return net->ipv6.fib6_main_tbl;
c71099acc   Thomas Graf   [IPV6]: Multiple ...
247
  }
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
248
249
  struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi *fl,
  				   int flags, pol_lookup_t lookup)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
250
  {
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
251
  	return (struct dst_entry *) lookup(net, net->ipv6.fib6_main_tbl, fl, flags);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
252
  }
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
253
  static void fib6_tables_init(struct net *net)
c71099acc   Thomas Graf   [IPV6]: Multiple ...
254
  {
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
255
  	fib6_link_table(net, net->ipv6.fib6_main_tbl);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
256
257
258
  }
  
  #endif
1b43af548   Patrick McHardy   [IPV6]: Increase ...
259
260
261
262
  static int fib6_dump_node(struct fib6_walker_t *w)
  {
  	int res;
  	struct rt6_info *rt;
7cc482634   Eric Dumazet   [IPV6]: Convert i...
263
  	for (rt = w->leaf; rt; rt = rt->u.dst.rt6_next) {
1b43af548   Patrick McHardy   [IPV6]: Increase ...
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
  		res = rt6_dump_route(rt, w->args);
  		if (res < 0) {
  			/* Frame is full, suspend walking */
  			w->leaf = rt;
  			return 1;
  		}
  		BUG_TRAP(res!=0);
  	}
  	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) {
  		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) {
  		read_lock_bh(&table->tb6_lock);
  		res = fib6_walk(w);
  		read_unlock_bh(&table->tb6_lock);
  		if (res > 0)
  			cb->args[4] = 1;
  	} else {
  		read_lock_bh(&table->tb6_lock);
  		res = fib6_walk_continue(w);
  		read_unlock_bh(&table->tb6_lock);
  		if (res != 0) {
  			if (res < 0)
  				fib6_walker_unlink(w);
  			goto end;
  		}
  		fib6_walker_unlink(w);
  		cb->args[4] = 0;
  	}
  end:
  	return res;
  }
c127ea2c4   Thomas Graf   [IPv6]: Use rtnl ...
324
  static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
1b43af548   Patrick McHardy   [IPV6]: Increase ...
325
  {
3b1e0a655   YOSHIFUJI Hideaki   [NET] NETNS: Omit...
326
  	struct net *net = sock_net(skb->sk);
1b43af548   Patrick McHardy   [IPV6]: Increase ...
327
328
329
330
331
332
  	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...
333
  	struct hlist_head *head;
1b43af548   Patrick McHardy   [IPV6]: Increase ...
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
  	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;
  	w->args = &arg;
  
  	for (h = s_h; h < FIB_TABLE_HASHSZ; h++, s_e = 0) {
  		e = 0;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
364
365
  		head = &net->ipv6.fib_table_hash[h];
  		hlist_for_each_entry(tb, node, head, tb6_hlist) {
1b43af548   Patrick McHardy   [IPV6]: Increase ...
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
  			if (e < s_e)
  				goto next;
  			res = fib6_dump_table(tb, skb, cb);
  			if (res != 0)
  				goto out;
  next:
  			e++;
  		}
  	}
  out:
  	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
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
  
  /*
   *	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...
401
  	__be32	dir = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
  	__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...
420

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421
422
423
  		/*
  		 *	Exact match ?
  		 */
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
424

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
425
426
427
428
429
430
  		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...
431

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434
435
436
437
438
439
  			return fn;
  		}
  
  		/*
  		 *	We have more bits to go
  		 */
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
440

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
  		/* 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...
458

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
460
461
462
463
464
465
466
467
468
469
470
471
  	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...
472
  	 * split since we don't have a common prefix anymore or
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
474
475
476
477
478
479
480
481
  	 * 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_...
482
  	   See comment in __ipv6_addr_diff: bit may be an invalid value,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483
484
  	   but if it is >= plen, the value is ignored in any case.
  	 */
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
485

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

1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
488
489
  	/*
  	 *		(intermediate)[in]
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
490
491
492
493
494
495
  	 *	          /	   \
  	 *	(new leaf node)[ln] (old node)[fn]
  	 */
  	if (plen > bit) {
  		in = node_alloc();
  		ln = node_alloc();
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
496

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
497
498
499
500
501
502
503
  		if (in == NULL || ln == NULL) {
  			if (in)
  				node_free(in);
  			if (ln)
  				node_free(ln);
  			return NULL;
  		}
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
504
505
  		/*
  		 * new intermediate node.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
  		 * 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...
541
  		/*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
  		 *		(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...
557

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
  		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...
578
  			    struct nl_info *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
579
580
581
582
583
  {
  	struct rt6_info *iter = NULL;
  	struct rt6_info **ins;
  
  	ins = &fn->leaf;
7cc482634   Eric Dumazet   [IPV6]: Convert i...
584
  	for (iter = fn->leaf; iter; iter=iter->u.dst.rt6_next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
  		/*
  		 *	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;
7cc482634   Eric Dumazet   [IPV6]: Convert i...
611
  		ins = &iter->u.dst.rt6_next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
612
  	}
f11e6659c   David S. Miller   [IPV6]: Fix routi...
613
614
615
  	/* Reset round-robin state, if necessary */
  	if (ins == &fn->leaf)
  		fn->rr_ptr = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
616
617
618
  	/*
  	 *	insert node
  	 */
7cc482634   Eric Dumazet   [IPV6]: Convert i...
619
  	rt->u.dst.rt6_next = iter;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
620
621
622
  	*ins = rt;
  	rt->rt6i_node = fn;
  	atomic_inc(&rt->rt6i_ref);
86872cb57   Thomas Graf   [IPv6] route: FIB...
623
  	inet6_rt_notify(RTM_NEWROUTE, rt, info);
c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
624
  	info->nl_net->ipv6.rt6_stats->fib_rt_entries++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
625
626
  
  	if ((fn->fn_flags & RTN_RTINFO) == 0) {
c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
627
  		info->nl_net->ipv6.rt6_stats->fib_route_nodes++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
628
629
630
631
632
  		fn->fn_flags |= RTN_RTINFO;
  	}
  
  	return 0;
  }
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
633
  static __inline__ void fib6_start_gc(struct net *net, struct rt6_info *rt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
634
  {
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
635
  	if (net->ipv6.ip6_fib_timer->expires == 0 &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
636
  	    (rt->rt6i_flags & (RTF_EXPIRES|RTF_CACHE)))
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
637
638
  		mod_timer(net->ipv6.ip6_fib_timer, jiffies +
  			  net->ipv6.sysctl.ip6_rt_gc_interval);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
639
  }
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
640
  void fib6_force_start_gc(struct net *net)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
641
  {
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
642
643
644
  	if (net->ipv6.ip6_fib_timer->expires == 0)
  		mod_timer(net->ipv6.ip6_fib_timer, jiffies +
  			  net->ipv6.sysctl.ip6_rt_gc_interval);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
645
646
647
648
649
650
651
  }
  
  /*
   *	Add routing information to the routing tree.
   *	<destination addr>/<source addr>
   *	with source addr info in sub-trees
   */
86872cb57   Thomas Graf   [IPv6] route: FIB...
652
  int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
653
  {
66729e18d   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
654
  	struct fib6_node *fn, *pn = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
655
656
657
658
659
660
661
  	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...
662
  	pn = fn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
  #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...
684
685
  			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
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
  			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
707
708
709
710
711
712
713
714
  		} 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...
715
716
717
718
  		if (fn->leaf == NULL) {
  			fn->leaf = rt;
  			atomic_inc(&rt->rt6i_ref);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
719
720
721
  		fn = sn;
  	}
  #endif
86872cb57   Thomas Graf   [IPv6] route: FIB...
722
  	err = fib6_add_rt2node(fn, rt, info);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
723
724
  
  	if (err == 0) {
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
725
  		fib6_start_gc(info->nl_net, rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
726
  		if (!(rt->rt6i_flags&RTF_CACHE))
ec7d43c29   Benjamin Thery   [NETNS][IPV6] ip6...
727
  			fib6_prune_clones(info->nl_net, pn, rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
728
729
730
  	}
  
  out:
66729e18d   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
731
732
733
734
735
736
  	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...
737
738
739
740
  		if (pn != fn && pn->leaf == rt) {
  			pn->leaf = NULL;
  			atomic_dec(&rt->rt6i_ref);
  		}
66729e18d   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
741
  		if (pn != fn && !pn->leaf && !(pn->fn_flags & RTN_RTINFO)) {
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
742
  			pn->leaf = fib6_find_prefix(info->nl_net, pn);
66729e18d   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
743
744
745
  #if RT6_DEBUG >= 2
  			if (!pn->leaf) {
  				BUG_TRAP(pn->leaf != NULL);
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
746
  				pn->leaf = info->nl_net->ipv6.ip6_null_entry;
66729e18d   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
747
748
749
750
751
  			}
  #endif
  			atomic_inc(&pn->leaf->rt6i_ref);
  		}
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
752
  		dst_free(&rt->u.dst);
66729e18d   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
753
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
754
755
756
757
758
759
760
761
  	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...
762
  		fib6_repair_tree(info->nl_net, fn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
  	dst_free(&rt->u.dst);
  	return err;
  #endif
  }
  
  /*
   *	Routing tree lookup
   *
   */
  
  struct lookup_args {
  	int		offset;		/* key offset on rt6_info	*/
  	struct in6_addr	*addr;		/* search key			*/
  };
  
  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...
782
  	__be32 dir;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
783

825e288ef   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
784
785
  	if (unlikely(args->offset == 0))
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
  	/*
  	 *	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...
806
  	while(fn) {
7fc33165a   YOSHIFUJI Hideaki   [IPV6] ROUTE: Put...
807
  		if (FIB6_SUBTREE(fn) || fn->fn_flags & RTN_RTINFO) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
808
809
810
811
  			struct rt6key *key;
  
  			key = (struct rt6key *) ((u8 *) fn->leaf +
  						 args->offset);
3fc5e0440   YOSHIFUJI Hideaki   [IPV6] ROUTE: Fix...
812
813
814
815
816
817
818
819
  			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
820
  		}
3fc5e0440   YOSHIFUJI Hideaki   [IPV6] ROUTE: Fix...
821
822
  		if (fn->fn_flags & RTN_ROOT)
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
823
824
825
826
827
828
829
830
831
  		fn = fn->parent;
  	}
  
  	return NULL;
  }
  
  struct fib6_node * fib6_lookup(struct fib6_node *root, struct in6_addr *daddr,
  			       struct in6_addr *saddr)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
832
  	struct fib6_node *fn;
825e288ef   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
833
834
835
836
837
  	struct lookup_args args[] = {
  		{
  			.offset = offsetof(struct rt6_info, rt6i_dst),
  			.addr = daddr,
  		},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
838
  #ifdef CONFIG_IPV6_SUBTREES
825e288ef   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
839
840
841
842
  		{
  			.offset = offsetof(struct rt6_info, rt6i_src),
  			.addr = saddr,
  		},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
843
  #endif
825e288ef   YOSHIFUJI Hideaki   [IPV6] ROUTE: Mak...
844
845
846
847
  		{
  			.offset = 0,	/* sentinel */
  		}
  	};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
848

fefc2a6c2   YOSHIFUJI Hideaki   [IPV6] ROUTE: All...
849
  	fn = fib6_lookup_1(root, daddr ? args : args + 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
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
  
  	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,
  					struct in6_addr *addr,
  					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,
  			       struct in6_addr *daddr, int dst_len,
  			       struct in6_addr *saddr, int src_len)
  {
  	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) {
  		BUG_TRAP(saddr!=NULL);
3fc5e0440   YOSHIFUJI Hideaki   [IPV6] ROUTE: Fix...
905
906
  		if (fn && fn->subtree)
  			fn = fib6_locate_1(fn->subtree, saddr, src_len,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
  					   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...
922
  static struct rt6_info *fib6_find_prefix(struct net *net, struct fib6_node *fn)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
923
924
  {
  	if (fn->fn_flags&RTN_ROOT)
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
925
  		return net->ipv6.ip6_null_entry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
926
927
928
929
930
931
932
  
  	while(fn) {
  		if(fn->left)
  			return fn->left->leaf;
  
  		if(fn->right)
  			return fn->right->leaf;
7fc33165a   YOSHIFUJI Hideaki   [IPV6] ROUTE: Put...
933
  		fn = FIB6_SUBTREE(fn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
934
935
936
937
938
939
940
941
  	}
  	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...
942
943
  static struct fib6_node *fib6_repair_tree(struct net *net,
  					   struct fib6_node *fn)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
  {
  	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++;
  
  		BUG_TRAP(!(fn->fn_flags&RTN_RTINFO));
  		BUG_TRAP(!(fn->fn_flags&RTN_TL_ROOT));
  		BUG_TRAP(fn->leaf==NULL);
  
  		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...
964
  		if (children == 3 || FIB6_SUBTREE(fn)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
965
966
967
968
969
  #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...
970
  			fn->leaf = fib6_find_prefix(net, fn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
971
972
973
  #if RT6_DEBUG >= 2
  			if (fn->leaf==NULL) {
  				BUG_TRAP(fn->leaf);
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
974
  				fn->leaf = net->ipv6.ip6_null_entry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
975
976
977
978
979
980
981
982
  			}
  #endif
  			atomic_inc(&fn->leaf->rt6i_ref);
  			return fn->parent;
  		}
  
  		pn = fn->parent;
  #ifdef CONFIG_IPV6_SUBTREES
7fc33165a   YOSHIFUJI Hideaki   [IPV6] ROUTE: Put...
983
  		if (FIB6_SUBTREE(pn) == fn) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
984
  			BUG_TRAP(fn->fn_flags&RTN_ROOT);
7fc33165a   YOSHIFUJI Hideaki   [IPV6] ROUTE: Put...
985
  			FIB6_SUBTREE(pn) = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
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
  			nstate = FWS_L;
  		} else {
  			BUG_TRAP(!(fn->fn_flags&RTN_ROOT));
  #endif
  			if (pn->right == fn) pn->right = child;
  			else if (pn->left == fn) pn->left = child;
  #if RT6_DEBUG >= 2
  			else BUG_TRAP(0);
  #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...
1038
  		if (pn->fn_flags&RTN_RTINFO || FIB6_SUBTREE(pn))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1039
1040
1041
1042
1043
1044
1045
1046
1047
  			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...
1048
  			   struct nl_info *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1049
1050
1051
  {
  	struct fib6_walker_t *w;
  	struct rt6_info *rt = *rtp;
c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
1052
  	struct net *net = info->nl_net;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1053
1054
1055
1056
1057
  
  	RT6_TRACE("fib6_del_route
  ");
  
  	/* Unlink it */
7cc482634   Eric Dumazet   [IPV6]: Convert i...
1058
  	*rtp = rt->u.dst.rt6_next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1059
  	rt->rt6i_node = NULL;
c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
1060
1061
  	net->ipv6.rt6_stats->fib_rt_entries--;
  	net->ipv6.rt6_stats->fib_discarded_routes++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1062

f11e6659c   David S. Miller   [IPV6]: Fix routi...
1063
1064
1065
  	/* Reset round-robin state, if necessary */
  	if (fn->rr_ptr == rt)
  		fn->rr_ptr = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1066
1067
1068
1069
1070
1071
  	/* 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);
7cc482634   Eric Dumazet   [IPV6]: Convert i...
1072
  			w->leaf = rt->u.dst.rt6_next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1073
1074
1075
1076
1077
  			if (w->leaf == NULL)
  				w->state = FWS_U;
  		}
  	}
  	read_unlock(&fib6_walker_lock);
7cc482634   Eric Dumazet   [IPV6]: Convert i...
1078
  	rt->u.dst.rt6_next = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1079

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1080
1081
1082
  	/* 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...
1083
  		net->ipv6.rt6_stats->fib_route_nodes--;
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
1084
  		fn = fib6_repair_tree(net, fn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
  	}
  
  	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...
1096
  				fn->leaf = fib6_find_prefix(net, fn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1097
1098
1099
1100
1101
1102
  				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...
1103
  		BUG_ON(atomic_read(&rt->rt6i_ref) != 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1104
  	}
86872cb57   Thomas Graf   [IPv6] route: FIB...
1105
  	inet6_rt_notify(RTM_DELROUTE, rt, info);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1106
1107
  	rt6_release(rt);
  }
86872cb57   Thomas Graf   [IPv6] route: FIB...
1108
  int fib6_del(struct rt6_info *rt, struct nl_info *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1109
  {
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
1110
  	struct net *net = info->nl_net;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1111
1112
1113
1114
1115
1116
1117
1118
1119
  	struct fib6_node *fn = rt->rt6i_node;
  	struct rt6_info **rtp;
  
  #if RT6_DEBUG >= 2
  	if (rt->u.dst.obsolete>0) {
  		BUG_TRAP(fn==NULL);
  		return -ENOENT;
  	}
  #endif
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
1120
  	if (fn == NULL || rt == net->ipv6.ip6_null_entry)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1121
1122
1123
  		return -ENOENT;
  
  	BUG_TRAP(fn->fn_flags&RTN_RTINFO);
150730d5a   YOSHIFUJI Hideaki   [IPV6] ROUTE: Pur...
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
  	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...
1134
  		fib6_prune_clones(info->nl_net, pn, rt);
150730d5a   YOSHIFUJI Hideaki   [IPV6] ROUTE: Pur...
1135
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1136
1137
1138
1139
  
  	/*
  	 *	Walk the leaf entries looking for ourself
  	 */
7cc482634   Eric Dumazet   [IPV6]: Convert i...
1140
  	for (rtp = &fn->leaf; *rtp; rtp = &(*rtp)->u.dst.rt6_next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1141
  		if (*rtp == rt) {
86872cb57   Thomas Graf   [IPv6] route: FIB...
1142
  			fib6_del_route(fn, rtp, info);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
  			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...
1156
   *	cleaning tree when a device goes down etc. etc.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
   *
   *	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:...
1172
  static int fib6_walk_continue(struct fib6_walker_t *w)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
  {
  	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...
1189
1190
  			if (FIB6_SUBTREE(fn)) {
  				w->node = FIB6_SUBTREE(fn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1191
1192
1193
  				continue;
  			}
  			w->state = FWS_L;
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
1194
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
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) {
  				int err = w->func(w);
  				if (err)
  					return err;
  				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...
1224
  			if (FIB6_SUBTREE(pn) == fn) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
  				BUG_TRAP(fn->fn_flags&RTN_ROOT);
  				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
  			BUG_TRAP(0);
  #endif
  		}
  	}
  }
90d41122f   Adrian Bunk   [IPV6] ip6_fib.c:...
1245
  static int fib6_walk(struct fib6_walker_t *w)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
  {
  	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...
1263
  	struct fib6_cleaner_t *c = container_of(w, struct fib6_cleaner_t, w);
ec7d43c29   Benjamin Thery   [NETNS][IPV6] ip6...
1264
1265
1266
  	struct nl_info info = {
  		.nl_net = c->net,
  	};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1267

7cc482634   Eric Dumazet   [IPV6]: Convert i...
1268
  	for (rt = w->leaf; rt; rt = rt->u.dst.rt6_next) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1269
1270
1271
  		res = c->func(rt, c->arg);
  		if (res < 0) {
  			w->leaf = rt;
528c4ceb4   Denis V. Lunev   [IPV6]: Always pa...
1272
  			res = fib6_del(rt, &info);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
  			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;
  		}
  		BUG_TRAP(res==0);
  	}
  	w->leaf = rt;
  	return 0;
  }
  
  /*
   *	Convenient frontend to tree walker.
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
1290
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1291
1292
1293
1294
1295
1296
1297
   *	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...
1298
  static void fib6_clean_tree(struct net *net, struct fib6_node *root,
8ce11e6a9   Adrian Bunk   [NET]: Make code ...
1299
1300
  			    int (*func)(struct rt6_info *, void *arg),
  			    int prune, void *arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1301
1302
1303
1304
1305
1306
1307
1308
  {
  	struct fib6_cleaner_t c;
  
  	c.w.root = root;
  	c.w.func = fib6_clean_node;
  	c.w.prune = prune;
  	c.func = func;
  	c.arg = arg;
ec7d43c29   Benjamin Thery   [NETNS][IPV6] ip6...
1309
  	c.net = net;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1310
1311
1312
  
  	fib6_walk(&c.w);
  }
f3db48517   Daniel Lezcano   [NETNS][IPV6] ip6...
1313
  void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg),
c71099acc   Thomas Graf   [IPV6]: Multiple ...
1314
1315
  		    int prune, void *arg)
  {
c71099acc   Thomas Graf   [IPV6]: Multiple ...
1316
  	struct fib6_table *table;
1b43af548   Patrick McHardy   [IPV6]: Increase ...
1317
  	struct hlist_node *node;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1318
  	struct hlist_head *head;
1b43af548   Patrick McHardy   [IPV6]: Increase ...
1319
  	unsigned int h;
c71099acc   Thomas Graf   [IPV6]: Multiple ...
1320

1b43af548   Patrick McHardy   [IPV6]: Increase ...
1321
1322
  	rcu_read_lock();
  	for (h = 0; h < FIB_TABLE_HASHSZ; h++) {
f3db48517   Daniel Lezcano   [NETNS][IPV6] ip6...
1323
  		head = &net->ipv6.fib_table_hash[h];
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1324
  		hlist_for_each_entry_rcu(table, node, head, tb6_hlist) {
c71099acc   Thomas Graf   [IPV6]: Multiple ...
1325
  			write_lock_bh(&table->tb6_lock);
ec7d43c29   Benjamin Thery   [NETNS][IPV6] ip6...
1326
1327
  			fib6_clean_tree(net, &table->tb6_root,
  					func, prune, arg);
c71099acc   Thomas Graf   [IPV6]: Multiple ...
1328
1329
1330
  			write_unlock_bh(&table->tb6_lock);
  		}
  	}
1b43af548   Patrick McHardy   [IPV6]: Increase ...
1331
  	rcu_read_unlock();
c71099acc   Thomas Graf   [IPV6]: Multiple ...
1332
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
  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...
1343
1344
  static void fib6_prune_clones(struct net *net, struct fib6_node *fn,
  			      struct rt6_info *rt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1345
  {
ec7d43c29   Benjamin Thery   [NETNS][IPV6] ip6...
1346
  	fib6_clean_tree(net, fn, fib6_prune_clone, 1, rt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
  }
  
  /*
   *	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
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
  			return -1;
  		}
  		gc_args.more++;
  	} else if (rt->rt6i_flags & RTF_CACHE) {
  		if (atomic_read(&rt->u.dst.__refcnt) == 0 &&
  		    time_after_eq(now, rt->u.dst.lastuse + gc_args.timeout)) {
  			RT6_TRACE("aging clone %p
  ", rt);
  			return -1;
  		} else if ((rt->rt6i_flags & RTF_GATEWAY) &&
  			   (!(rt->rt6i_nexthop->flags & NTF_ROUTER))) {
  			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...
1398
  void fib6_run_gc(unsigned long expires, struct net *net)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1399
  {
5b7c931df   Daniel Lezcano   [NETNS][IPV6] ip6...
1400
  	if (expires != ~0UL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1401
  		spin_lock_bh(&fib6_gc_lock);
5b7c931df   Daniel Lezcano   [NETNS][IPV6] ip6...
1402
1403
  		gc_args.timeout = expires ? (int)expires :
  			net->ipv6.sysctl.ip6_rt_gc_interval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1404
1405
1406
  	} else {
  		local_bh_disable();
  		if (!spin_trylock(&fib6_gc_lock)) {
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
1407
  			mod_timer(net->ipv6.ip6_fib_timer, jiffies + HZ);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1408
1409
1410
  			local_bh_enable();
  			return;
  		}
5b7c931df   Daniel Lezcano   [NETNS][IPV6] ip6...
1411
  		gc_args.timeout = net->ipv6.sysctl.ip6_rt_gc_interval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1412
1413
  	}
  	gc_args.more = 0;
3b00944c5   YOSHIFUJI Hideaki   [IPV6]: Make ndis...
1414
  	icmp6_dst_gc(&gc_args.more);
f3db48517   Daniel Lezcano   [NETNS][IPV6] ip6...
1415

5b7c931df   Daniel Lezcano   [NETNS][IPV6] ip6...
1416
  	fib6_clean_all(net, fib6_age, 0, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1417
1418
  
  	if (gc_args.more)
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
1419
  		mod_timer(net->ipv6.ip6_fib_timer, jiffies +
5b7c931df   Daniel Lezcano   [NETNS][IPV6] ip6...
1420
  			  net->ipv6.sysctl.ip6_rt_gc_interval);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1421
  	else {
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
1422
1423
  		del_timer(net->ipv6.ip6_fib_timer);
  		net->ipv6.ip6_fib_timer->expires = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1424
1425
1426
  	}
  	spin_unlock_bh(&fib6_gc_lock);
  }
5b7c931df   Daniel Lezcano   [NETNS][IPV6] ip6...
1427
1428
1429
1430
  static void fib6_gc_timer_cb(unsigned long arg)
  {
  	fib6_run_gc(0, (struct net *)arg);
  }
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1431
  static int fib6_net_init(struct net *net)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1432
  {
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1433
  	int ret;
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
1434
  	struct timer_list *timer;
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
1435

58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1436
  	ret = -ENOMEM;
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
1437
1438
1439
1440
1441
1442
  	timer = kzalloc(sizeof(*timer), GFP_KERNEL);
  	if (!timer)
  		goto out;
  
  	setup_timer(timer, fib6_gc_timer_cb, (unsigned long)net);
  	net->ipv6.ip6_fib_timer = timer;
c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
1443
1444
1445
  	net->ipv6.rt6_stats = kzalloc(sizeof(*net->ipv6.rt6_stats), GFP_KERNEL);
  	if (!net->ipv6.rt6_stats)
  		goto out_timer;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1446
1447
1448
1449
  	net->ipv6.fib_table_hash =
  		kzalloc(sizeof(*net->ipv6.fib_table_hash)*FIB_TABLE_HASHSZ,
  			GFP_KERNEL);
  	if (!net->ipv6.fib_table_hash)
c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
1450
  		goto out_rt6_stats;
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
1451

58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1452
1453
1454
  	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...
1455
  		goto out_fib_table_hash;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1456
  	net->ipv6.fib6_main_tbl->tb6_id = RT6_TABLE_MAIN;
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
1457
  	net->ipv6.fib6_main_tbl->tb6_root.leaf = net->ipv6.ip6_null_entry;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1458
1459
  	net->ipv6.fib6_main_tbl->tb6_root.fn_flags =
  		RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
1460
1461
  
  #ifdef CONFIG_IPV6_MULTIPLE_TABLES
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1462
1463
1464
  	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...
1465
  		goto out_fib6_main_tbl;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1466
  	net->ipv6.fib6_local_tbl->tb6_id = RT6_TABLE_LOCAL;
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
1467
  	net->ipv6.fib6_local_tbl->tb6_root.leaf = net->ipv6.ip6_null_entry;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1468
1469
  	net->ipv6.fib6_local_tbl->tb6_root.fn_flags =
  		RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
1470
  #endif
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1471
  	fib6_tables_init(net);
f845ab6b7   Daniel Lezcano   [IPV6] route6/fib...
1472

58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1473
  	ret = 0;
d63bddbe9   Daniel Lezcano   [IPV6]: Make fib6...
1474
1475
  out:
  	return ret;
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
1476
  #ifdef CONFIG_IPV6_MULTIPLE_TABLES
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
1477
  out_fib6_main_tbl:
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1478
  	kfree(net->ipv6.fib6_main_tbl);
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
1479
  #endif
e0b85590b   Daniel Lezcano   [NETNS][IPV6] ip6...
1480
  out_fib_table_hash:
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1481
  	kfree(net->ipv6.fib_table_hash);
c572872f8   Benjamin Thery   [NETNS][IPV6] rt6...
1482
1483
  out_rt6_stats:
  	kfree(net->ipv6.rt6_stats);
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
1484
1485
  out_timer:
  	kfree(timer);
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1486
1487
1488
1489
1490
  	goto out;
   }
  
  static void fib6_net_exit(struct net *net)
  {
8ed677896   Daniel Lezcano   [NETNS][IPV6] rt6...
1491
  	rt6_ifdown(net, NULL);
2aed2827d   Pavel Emelyanov   [NETNS]: The ip6_...
1492
  	del_timer_sync(net->ipv6.ip6_fib_timer);
63152fc0d   Daniel Lezcano   [NETNS][IPV6] ip6...
1493
  	kfree(net->ipv6.ip6_fib_timer);
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1494
1495
1496
1497
1498
  #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...
1499
  	kfree(net->ipv6.rt6_stats);
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
  }
  
  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...
1510

58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1511
1512
1513
1514
1515
1516
1517
1518
1519
  	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...
1520
  		goto out_kmem_cache_create;
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1521
1522
1523
1524
1525
1526
1527
1528
1529
  
  	ret = __rtnl_register(PF_INET6, RTM_GETROUTE, NULL, inet6_dump_fib);
  	if (ret)
  		goto out_unregister_subsys;
  out:
  	return ret;
  
  out_unregister_subsys:
  	unregister_pernet_subsys(&fib6_net_ops);
d63bddbe9   Daniel Lezcano   [IPV6]: Make fib6...
1530
1531
1532
  out_kmem_cache_create:
  	kmem_cache_destroy(fib6_node_kmem);
  	goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1533
1534
1535
1536
  }
  
  void fib6_gc_cleanup(void)
  {
58f09b78b   Daniel Lezcano   [NETNS][IPV6] ip6...
1537
  	unregister_pernet_subsys(&fib6_net_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1538
1539
  	kmem_cache_destroy(fib6_node_kmem);
  }