blob: dc6e0b8f260d2cdf4403dd42ba14abcd4c6c3f2f [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09002 * Linux INET6 implementation
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 * Forwarding Information Database
4 *
5 * Authors:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09006 * Pedro Roque <roque@di.fc.ul.pt>
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version
11 * 2 of the License, or (at your option) any later version.
12 */
13
14/*
15 * Changes:
16 * Yuji SEKIYA @USAGI: Support default route on router node;
17 * remove ip6_null_entry from the top of
18 * routing table.
YOSHIFUJI Hideakic0bece92006-08-23 17:23:25 -070019 * Ville Nuorvala: Fixed routing subtrees.
Linus Torvalds1da177e2005-04-16 15:20:36 -070020 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include <linux/errno.h>
22#include <linux/types.h>
23#include <linux/net.h>
24#include <linux/route.h>
25#include <linux/netdevice.h>
26#include <linux/in6.h>
27#include <linux/init.h>
Thomas Grafc71099a2006-08-04 23:20:06 -070028#include <linux/list.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090029#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030
31#ifdef CONFIG_PROC_FS
32#include <linux/proc_fs.h>
33#endif
34
35#include <net/ipv6.h>
36#include <net/ndisc.h>
37#include <net/addrconf.h>
38
39#include <net/ip6_fib.h>
40#include <net/ip6_route.h>
41
42#define RT6_DEBUG 2
43
44#if RT6_DEBUG >= 3
45#define RT6_TRACE(x...) printk(KERN_DEBUG x)
46#else
47#define RT6_TRACE(x...) do { ; } while (0)
48#endif
49
Christoph Lametere18b8902006-12-06 20:33:20 -080050static struct kmem_cache * fib6_node_kmem __read_mostly;
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
52enum fib_walk_state_t
53{
54#ifdef CONFIG_IPV6_SUBTREES
55 FWS_S,
56#endif
57 FWS_L,
58 FWS_R,
59 FWS_C,
60 FWS_U
61};
62
63struct fib6_cleaner_t
64{
65 struct fib6_walker_t w;
Benjamin Theryec7d43c2008-03-03 23:31:57 -080066 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 int (*func)(struct rt6_info *, void *arg);
68 void *arg;
69};
70
Adrian Bunk90d41122006-08-14 23:49:16 -070071static DEFINE_RWLOCK(fib6_walker_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070072
73#ifdef CONFIG_IPV6_SUBTREES
74#define FWS_INIT FWS_S
Linus Torvalds1da177e2005-04-16 15:20:36 -070075#else
76#define FWS_INIT FWS_L
Linus Torvalds1da177e2005-04-16 15:20:36 -070077#endif
78
Benjamin Theryec7d43c2008-03-03 23:31:57 -080079static void fib6_prune_clones(struct net *net, struct fib6_node *fn,
80 struct rt6_info *rt);
Daniel Lezcano8ed67782008-03-04 13:48:30 -080081static struct rt6_info *fib6_find_prefix(struct net *net, struct fib6_node *fn);
82static struct fib6_node *fib6_repair_tree(struct net *net, struct fib6_node *fn);
Adrian Bunk90d41122006-08-14 23:49:16 -070083static int fib6_walk(struct fib6_walker_t *w);
84static int fib6_walk_continue(struct fib6_walker_t *w);
Linus Torvalds1da177e2005-04-16 15:20:36 -070085
86/*
87 * A routing update causes an increase of the serial number on the
88 * affected subtree. This allows for cached routes to be asynchronously
89 * tested when modifications are made to the destination cache as a
90 * result of redirects, path MTU changes, etc.
91 */
92
93static __u32 rt_sernum;
94
Daniel Lezcano5b7c9312008-03-03 23:28:58 -080095static void fib6_gc_timer_cb(unsigned long arg);
96
Alexey Dobriyanbbef49d2010-02-18 08:13:30 +000097static LIST_HEAD(fib6_walkers);
98#define FOR_WALKERS(w) list_for_each_entry(w, &fib6_walkers, lh)
Linus Torvalds1da177e2005-04-16 15:20:36 -070099
Adrian Bunk90d41122006-08-14 23:49:16 -0700100static inline void fib6_walker_link(struct fib6_walker_t *w)
101{
102 write_lock_bh(&fib6_walker_lock);
Alexey Dobriyanbbef49d2010-02-18 08:13:30 +0000103 list_add(&w->lh, &fib6_walkers);
Adrian Bunk90d41122006-08-14 23:49:16 -0700104 write_unlock_bh(&fib6_walker_lock);
105}
106
107static inline void fib6_walker_unlink(struct fib6_walker_t *w)
108{
109 write_lock_bh(&fib6_walker_lock);
Alexey Dobriyanbbef49d2010-02-18 08:13:30 +0000110 list_del(&w->lh);
Adrian Bunk90d41122006-08-14 23:49:16 -0700111 write_unlock_bh(&fib6_walker_lock);
112}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113static __inline__ u32 fib6_new_sernum(void)
114{
115 u32 n = ++rt_sernum;
116 if ((__s32)n <= 0)
117 rt_sernum = n = 1;
118 return n;
119}
120
121/*
122 * Auxiliary address test functions for the radix tree.
123 *
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900124 * These assume a 32bit processor (although it will work on
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 * 64bit processors)
126 */
127
128/*
129 * test bit
130 */
YOSHIFUJI Hideaki / 吉藤英明02cdce52010-03-27 01:24:16 +0000131#if defined(__LITTLE_ENDIAN)
132# define BITOP_BE32_SWIZZLE (0x1F & ~7)
133#else
134# define BITOP_BE32_SWIZZLE 0
135#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136
Al Viroe69a4adc2006-11-14 20:56:00 -0800137static __inline__ __be32 addr_bit_set(void *token, int fn_bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138{
Al Viroe69a4adc2006-11-14 20:56:00 -0800139 __be32 *addr = token;
YOSHIFUJI Hideaki / 吉藤英明02cdce52010-03-27 01:24:16 +0000140 /*
141 * Here,
142 * 1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f)
143 * is optimized version of
144 * htonl(1 << ((~fn_bit)&0x1F))
145 * See include/asm-generic/bitops/le.h.
146 */
147 return (1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f)) & addr[fn_bit >> 5];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148}
149
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150static __inline__ struct fib6_node * node_alloc(void)
151{
152 struct fib6_node *fn;
153
Robert P. J. Dayc3762222007-02-10 01:45:03 -0800154 fn = kmem_cache_zalloc(fib6_node_kmem, GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155
156 return fn;
157}
158
159static __inline__ void node_free(struct fib6_node * fn)
160{
161 kmem_cache_free(fib6_node_kmem, fn);
162}
163
164static __inline__ void rt6_release(struct rt6_info *rt)
165{
166 if (atomic_dec_and_test(&rt->rt6i_ref))
167 dst_free(&rt->u.dst);
168}
169
Daniel Lezcano58f09b72008-03-03 23:25:27 -0800170static void fib6_link_table(struct net *net, struct fib6_table *tb)
Patrick McHardy1b43af52006-08-10 23:11:17 -0700171{
172 unsigned int h;
173
Thomas Graf375216a2006-10-21 20:20:54 -0700174 /*
175 * Initialize table lock at a single place to give lockdep a key,
176 * tables aren't visible prior to being linked to the list.
177 */
178 rwlock_init(&tb->tb6_lock);
179
Neil Hormana33bc5c2009-07-30 18:52:15 -0700180 h = tb->tb6_id & (FIB6_TABLE_HASHSZ - 1);
Patrick McHardy1b43af52006-08-10 23:11:17 -0700181
182 /*
183 * No protection necessary, this is the only list mutatation
184 * operation, tables never disappear once they exist.
185 */
Daniel Lezcano58f09b72008-03-03 23:25:27 -0800186 hlist_add_head_rcu(&tb->tb6_hlist, &net->ipv6.fib_table_hash[h]);
Patrick McHardy1b43af52006-08-10 23:11:17 -0700187}
188
189#ifdef CONFIG_IPV6_MULTIPLE_TABLES
Daniel Lezcanoe0b855902008-03-03 23:24:31 -0800190
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800191static struct fib6_table *fib6_alloc_table(struct net *net, u32 id)
Thomas Grafc71099a2006-08-04 23:20:06 -0700192{
193 struct fib6_table *table;
194
195 table = kzalloc(sizeof(*table), GFP_ATOMIC);
196 if (table != NULL) {
197 table->tb6_id = id;
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800198 table->tb6_root.leaf = net->ipv6.ip6_null_entry;
Thomas Grafc71099a2006-08-04 23:20:06 -0700199 table->tb6_root.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
200 }
201
202 return table;
203}
204
Daniel Lezcano58f09b72008-03-03 23:25:27 -0800205struct fib6_table *fib6_new_table(struct net *net, u32 id)
Thomas Grafc71099a2006-08-04 23:20:06 -0700206{
207 struct fib6_table *tb;
208
209 if (id == 0)
210 id = RT6_TABLE_MAIN;
Daniel Lezcano58f09b72008-03-03 23:25:27 -0800211 tb = fib6_get_table(net, id);
Thomas Grafc71099a2006-08-04 23:20:06 -0700212 if (tb)
213 return tb;
214
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800215 tb = fib6_alloc_table(net, id);
Thomas Grafc71099a2006-08-04 23:20:06 -0700216 if (tb != NULL)
Daniel Lezcano58f09b72008-03-03 23:25:27 -0800217 fib6_link_table(net, tb);
Thomas Grafc71099a2006-08-04 23:20:06 -0700218
219 return tb;
220}
221
Daniel Lezcano58f09b72008-03-03 23:25:27 -0800222struct fib6_table *fib6_get_table(struct net *net, u32 id)
Thomas Grafc71099a2006-08-04 23:20:06 -0700223{
224 struct fib6_table *tb;
Daniel Lezcano58f09b72008-03-03 23:25:27 -0800225 struct hlist_head *head;
Thomas Grafc71099a2006-08-04 23:20:06 -0700226 struct hlist_node *node;
227 unsigned int h;
228
229 if (id == 0)
230 id = RT6_TABLE_MAIN;
Neil Hormana33bc5c2009-07-30 18:52:15 -0700231 h = id & (FIB6_TABLE_HASHSZ - 1);
Thomas Grafc71099a2006-08-04 23:20:06 -0700232 rcu_read_lock();
Daniel Lezcano58f09b72008-03-03 23:25:27 -0800233 head = &net->ipv6.fib_table_hash[h];
234 hlist_for_each_entry_rcu(tb, node, head, tb6_hlist) {
Thomas Grafc71099a2006-08-04 23:20:06 -0700235 if (tb->tb6_id == id) {
236 rcu_read_unlock();
237 return tb;
238 }
239 }
240 rcu_read_unlock();
241
242 return NULL;
243}
244
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +0000245static void __net_init fib6_tables_init(struct net *net)
Thomas Grafc71099a2006-08-04 23:20:06 -0700246{
Daniel Lezcano58f09b72008-03-03 23:25:27 -0800247 fib6_link_table(net, net->ipv6.fib6_main_tbl);
248 fib6_link_table(net, net->ipv6.fib6_local_tbl);
Thomas Grafc71099a2006-08-04 23:20:06 -0700249}
Thomas Grafc71099a2006-08-04 23:20:06 -0700250#else
251
Daniel Lezcano58f09b72008-03-03 23:25:27 -0800252struct fib6_table *fib6_new_table(struct net *net, u32 id)
Thomas Grafc71099a2006-08-04 23:20:06 -0700253{
Daniel Lezcano58f09b72008-03-03 23:25:27 -0800254 return fib6_get_table(net, id);
Thomas Grafc71099a2006-08-04 23:20:06 -0700255}
256
Daniel Lezcano58f09b72008-03-03 23:25:27 -0800257struct fib6_table *fib6_get_table(struct net *net, u32 id)
Thomas Grafc71099a2006-08-04 23:20:06 -0700258{
Daniel Lezcano58f09b72008-03-03 23:25:27 -0800259 return net->ipv6.fib6_main_tbl;
Thomas Grafc71099a2006-08-04 23:20:06 -0700260}
261
Daniel Lezcano58f09b72008-03-03 23:25:27 -0800262struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi *fl,
263 int flags, pol_lookup_t lookup)
Thomas Grafc71099a2006-08-04 23:20:06 -0700264{
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800265 return (struct dst_entry *) lookup(net, net->ipv6.fib6_main_tbl, fl, flags);
Thomas Grafc71099a2006-08-04 23:20:06 -0700266}
267
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +0000268static void __net_init fib6_tables_init(struct net *net)
Thomas Grafc71099a2006-08-04 23:20:06 -0700269{
Daniel Lezcano58f09b72008-03-03 23:25:27 -0800270 fib6_link_table(net, net->ipv6.fib6_main_tbl);
Thomas Grafc71099a2006-08-04 23:20:06 -0700271}
272
273#endif
274
Patrick McHardy1b43af52006-08-10 23:11:17 -0700275static int fib6_dump_node(struct fib6_walker_t *w)
276{
277 int res;
278 struct rt6_info *rt;
279
Eric Dumazet7cc48262007-02-09 16:22:57 -0800280 for (rt = w->leaf; rt; rt = rt->u.dst.rt6_next) {
Patrick McHardy1b43af52006-08-10 23:11:17 -0700281 res = rt6_dump_route(rt, w->args);
282 if (res < 0) {
283 /* Frame is full, suspend walking */
284 w->leaf = rt;
285 return 1;
286 }
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700287 WARN_ON(res == 0);
Patrick McHardy1b43af52006-08-10 23:11:17 -0700288 }
289 w->leaf = NULL;
290 return 0;
291}
292
293static void fib6_dump_end(struct netlink_callback *cb)
294{
295 struct fib6_walker_t *w = (void*)cb->args[2];
296
297 if (w) {
Herbert Xu7891cc82009-01-13 22:17:51 -0800298 if (cb->args[4]) {
299 cb->args[4] = 0;
300 fib6_walker_unlink(w);
301 }
Patrick McHardy1b43af52006-08-10 23:11:17 -0700302 cb->args[2] = 0;
303 kfree(w);
304 }
305 cb->done = (void*)cb->args[3];
306 cb->args[1] = 3;
307}
308
309static int fib6_dump_done(struct netlink_callback *cb)
310{
311 fib6_dump_end(cb);
312 return cb->done ? cb->done(cb) : 0;
313}
314
315static int fib6_dump_table(struct fib6_table *table, struct sk_buff *skb,
316 struct netlink_callback *cb)
317{
318 struct fib6_walker_t *w;
319 int res;
320
321 w = (void *)cb->args[2];
322 w->root = &table->tb6_root;
323
324 if (cb->args[4] == 0) {
Patrick McHardy2bec5a32010-02-08 05:19:03 +0000325 w->count = 0;
326 w->skip = 0;
327
Patrick McHardy1b43af52006-08-10 23:11:17 -0700328 read_lock_bh(&table->tb6_lock);
329 res = fib6_walk(w);
330 read_unlock_bh(&table->tb6_lock);
Patrick McHardy2bec5a32010-02-08 05:19:03 +0000331 if (res > 0) {
Patrick McHardy1b43af52006-08-10 23:11:17 -0700332 cb->args[4] = 1;
Patrick McHardy2bec5a32010-02-08 05:19:03 +0000333 cb->args[5] = w->root->fn_sernum;
334 }
Patrick McHardy1b43af52006-08-10 23:11:17 -0700335 } else {
Patrick McHardy2bec5a32010-02-08 05:19:03 +0000336 if (cb->args[5] != w->root->fn_sernum) {
337 /* Begin at the root if the tree changed */
338 cb->args[5] = w->root->fn_sernum;
339 w->state = FWS_INIT;
340 w->node = w->root;
341 w->skip = w->count;
342 } else
343 w->skip = 0;
344
Patrick McHardy1b43af52006-08-10 23:11:17 -0700345 read_lock_bh(&table->tb6_lock);
346 res = fib6_walk_continue(w);
347 read_unlock_bh(&table->tb6_lock);
Herbert Xu7891cc82009-01-13 22:17:51 -0800348 if (res <= 0) {
349 fib6_walker_unlink(w);
350 cb->args[4] = 0;
Patrick McHardy1b43af52006-08-10 23:11:17 -0700351 }
Patrick McHardy1b43af52006-08-10 23:11:17 -0700352 }
Herbert Xu7891cc82009-01-13 22:17:51 -0800353
Patrick McHardy1b43af52006-08-10 23:11:17 -0700354 return res;
355}
356
Thomas Grafc127ea22007-03-22 11:58:32 -0700357static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
Patrick McHardy1b43af52006-08-10 23:11:17 -0700358{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +0900359 struct net *net = sock_net(skb->sk);
Patrick McHardy1b43af52006-08-10 23:11:17 -0700360 unsigned int h, s_h;
361 unsigned int e = 0, s_e;
362 struct rt6_rtnl_dump_arg arg;
363 struct fib6_walker_t *w;
364 struct fib6_table *tb;
365 struct hlist_node *node;
Daniel Lezcano58f09b72008-03-03 23:25:27 -0800366 struct hlist_head *head;
Patrick McHardy1b43af52006-08-10 23:11:17 -0700367 int res = 0;
368
369 s_h = cb->args[0];
370 s_e = cb->args[1];
371
372 w = (void *)cb->args[2];
373 if (w == NULL) {
374 /* New dump:
375 *
376 * 1. hook callback destructor.
377 */
378 cb->args[3] = (long)cb->done;
379 cb->done = fib6_dump_done;
380
381 /*
382 * 2. allocate and initialize walker.
383 */
384 w = kzalloc(sizeof(*w), GFP_ATOMIC);
385 if (w == NULL)
386 return -ENOMEM;
387 w->func = fib6_dump_node;
388 cb->args[2] = (long)w;
389 }
390
391 arg.skb = skb;
392 arg.cb = cb;
Brian Haley191cd582008-08-14 15:33:21 -0700393 arg.net = net;
Patrick McHardy1b43af52006-08-10 23:11:17 -0700394 w->args = &arg;
395
Neil Hormana33bc5c2009-07-30 18:52:15 -0700396 for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_e = 0) {
Patrick McHardy1b43af52006-08-10 23:11:17 -0700397 e = 0;
Daniel Lezcano58f09b72008-03-03 23:25:27 -0800398 head = &net->ipv6.fib_table_hash[h];
399 hlist_for_each_entry(tb, node, head, tb6_hlist) {
Patrick McHardy1b43af52006-08-10 23:11:17 -0700400 if (e < s_e)
401 goto next;
402 res = fib6_dump_table(tb, skb, cb);
403 if (res != 0)
404 goto out;
405next:
406 e++;
407 }
408 }
409out:
410 cb->args[1] = e;
411 cb->args[0] = h;
412
413 res = res < 0 ? res : skb->len;
414 if (res <= 0)
415 fib6_dump_end(cb);
416 return res;
417}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418
419/*
420 * Routing Table
421 *
422 * return the appropriate node for a routing tree "add" operation
423 * by either creating and inserting or by returning an existing
424 * node.
425 */
426
427static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr,
428 int addrlen, int plen,
429 int offset)
430{
431 struct fib6_node *fn, *in, *ln;
432 struct fib6_node *pn = NULL;
433 struct rt6key *key;
434 int bit;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900435 __be32 dir = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 __u32 sernum = fib6_new_sernum();
437
438 RT6_TRACE("fib6_add_1\n");
439
440 /* insert node in tree */
441
442 fn = root;
443
444 do {
445 key = (struct rt6key *)((u8 *)fn->leaf + offset);
446
447 /*
448 * Prefix match
449 */
450 if (plen < fn->fn_bit ||
451 !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit))
452 goto insert_above;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900453
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 /*
455 * Exact match ?
456 */
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900457
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 if (plen == fn->fn_bit) {
459 /* clean up an intermediate node */
460 if ((fn->fn_flags & RTN_RTINFO) == 0) {
461 rt6_release(fn->leaf);
462 fn->leaf = NULL;
463 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900464
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 fn->fn_sernum = sernum;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900466
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 return fn;
468 }
469
470 /*
471 * We have more bits to go
472 */
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900473
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 /* Try to walk down on tree. */
475 fn->fn_sernum = sernum;
476 dir = addr_bit_set(addr, fn->fn_bit);
477 pn = fn;
478 fn = dir ? fn->right: fn->left;
479 } while (fn);
480
481 /*
482 * We walked to the bottom of tree.
483 * Create new leaf node without children.
484 */
485
486 ln = node_alloc();
487
488 if (ln == NULL)
489 return NULL;
490 ln->fn_bit = plen;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900491
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 ln->parent = pn;
493 ln->fn_sernum = sernum;
494
495 if (dir)
496 pn->right = ln;
497 else
498 pn->left = ln;
499
500 return ln;
501
502
503insert_above:
504 /*
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900505 * split since we don't have a common prefix anymore or
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 * we have a less significant route.
507 * we've to insert an intermediate node on the list
508 * this new node will point to the one we need to create
509 * and the current
510 */
511
512 pn = fn->parent;
513
514 /* find 1st bit in difference between the 2 addrs.
515
YOSHIFUJI Hideaki971f3592005-11-08 09:37:56 -0800516 See comment in __ipv6_addr_diff: bit may be an invalid value,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 but if it is >= plen, the value is ignored in any case.
518 */
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900519
YOSHIFUJI Hideaki971f3592005-11-08 09:37:56 -0800520 bit = __ipv6_addr_diff(addr, &key->addr, addrlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900522 /*
523 * (intermediate)[in]
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 * / \
525 * (new leaf node)[ln] (old node)[fn]
526 */
527 if (plen > bit) {
528 in = node_alloc();
529 ln = node_alloc();
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900530
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 if (in == NULL || ln == NULL) {
532 if (in)
533 node_free(in);
534 if (ln)
535 node_free(ln);
536 return NULL;
537 }
538
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900539 /*
540 * new intermediate node.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 * RTN_RTINFO will
542 * be off since that an address that chooses one of
543 * the branches would not match less specific routes
544 * in the other branch
545 */
546
547 in->fn_bit = bit;
548
549 in->parent = pn;
550 in->leaf = fn->leaf;
551 atomic_inc(&in->leaf->rt6i_ref);
552
553 in->fn_sernum = sernum;
554
555 /* update parent pointer */
556 if (dir)
557 pn->right = in;
558 else
559 pn->left = in;
560
561 ln->fn_bit = plen;
562
563 ln->parent = in;
564 fn->parent = in;
565
566 ln->fn_sernum = sernum;
567
568 if (addr_bit_set(addr, bit)) {
569 in->right = ln;
570 in->left = fn;
571 } else {
572 in->left = ln;
573 in->right = fn;
574 }
575 } else { /* plen <= bit */
576
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900577 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 * (new leaf node)[ln]
579 * / \
580 * (old node)[fn] NULL
581 */
582
583 ln = node_alloc();
584
585 if (ln == NULL)
586 return NULL;
587
588 ln->fn_bit = plen;
589
590 ln->parent = pn;
591
592 ln->fn_sernum = sernum;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900593
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 if (dir)
595 pn->right = ln;
596 else
597 pn->left = ln;
598
599 if (addr_bit_set(&key->addr, plen))
600 ln->right = fn;
601 else
602 ln->left = fn;
603
604 fn->parent = ln;
605 }
606 return ln;
607}
608
609/*
610 * Insert routing information in a node.
611 */
612
613static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
Thomas Graf86872cb2006-08-22 00:01:08 -0700614 struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615{
616 struct rt6_info *iter = NULL;
617 struct rt6_info **ins;
618
619 ins = &fn->leaf;
620
Eric Dumazet7cc48262007-02-09 16:22:57 -0800621 for (iter = fn->leaf; iter; iter=iter->u.dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 /*
623 * Search for duplicates
624 */
625
626 if (iter->rt6i_metric == rt->rt6i_metric) {
627 /*
628 * Same priority level
629 */
630
631 if (iter->rt6i_dev == rt->rt6i_dev &&
632 iter->rt6i_idev == rt->rt6i_idev &&
633 ipv6_addr_equal(&iter->rt6i_gateway,
634 &rt->rt6i_gateway)) {
635 if (!(iter->rt6i_flags&RTF_EXPIRES))
636 return -EEXIST;
637 iter->rt6i_expires = rt->rt6i_expires;
638 if (!(rt->rt6i_flags&RTF_EXPIRES)) {
639 iter->rt6i_flags &= ~RTF_EXPIRES;
640 iter->rt6i_expires = 0;
641 }
642 return -EEXIST;
643 }
644 }
645
646 if (iter->rt6i_metric > rt->rt6i_metric)
647 break;
648
Eric Dumazet7cc48262007-02-09 16:22:57 -0800649 ins = &iter->u.dst.rt6_next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 }
651
David S. Millerf11e6652007-03-24 20:36:25 -0700652 /* Reset round-robin state, if necessary */
653 if (ins == &fn->leaf)
654 fn->rr_ptr = NULL;
655
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 /*
657 * insert node
658 */
659
Eric Dumazet7cc48262007-02-09 16:22:57 -0800660 rt->u.dst.rt6_next = iter;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 *ins = rt;
662 rt->rt6i_node = fn;
663 atomic_inc(&rt->rt6i_ref);
Thomas Graf86872cb2006-08-22 00:01:08 -0700664 inet6_rt_notify(RTM_NEWROUTE, rt, info);
Benjamin Theryc5728722008-03-03 23:34:17 -0800665 info->nl_net->ipv6.rt6_stats->fib_rt_entries++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666
667 if ((fn->fn_flags & RTN_RTINFO) == 0) {
Benjamin Theryc5728722008-03-03 23:34:17 -0800668 info->nl_net->ipv6.rt6_stats->fib_route_nodes++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 fn->fn_flags |= RTN_RTINFO;
670 }
671
672 return 0;
673}
674
Daniel Lezcano63152fc2008-03-03 23:31:11 -0800675static __inline__ void fib6_start_gc(struct net *net, struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676{
Stephen Hemminger417f28b2008-07-22 14:33:45 -0700677 if (!timer_pending(&net->ipv6.ip6_fib_timer) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 (rt->rt6i_flags & (RTF_EXPIRES|RTF_CACHE)))
Stephen Hemminger417f28b2008-07-22 14:33:45 -0700679 mod_timer(&net->ipv6.ip6_fib_timer,
Stephen Hemminger847499c2008-07-21 13:21:35 -0700680 jiffies + net->ipv6.sysctl.ip6_rt_gc_interval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681}
682
Daniel Lezcano63152fc2008-03-03 23:31:11 -0800683void fib6_force_start_gc(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684{
Stephen Hemminger417f28b2008-07-22 14:33:45 -0700685 if (!timer_pending(&net->ipv6.ip6_fib_timer))
686 mod_timer(&net->ipv6.ip6_fib_timer,
Stephen Hemminger847499c2008-07-21 13:21:35 -0700687 jiffies + net->ipv6.sysctl.ip6_rt_gc_interval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688}
689
690/*
691 * Add routing information to the routing tree.
692 * <destination addr>/<source addr>
693 * with source addr info in sub-trees
694 */
695
Thomas Graf86872cb2006-08-22 00:01:08 -0700696int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697{
YOSHIFUJI Hideaki66729e12006-08-23 17:20:34 -0700698 struct fib6_node *fn, *pn = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 int err = -ENOMEM;
700
701 fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr),
702 rt->rt6i_dst.plen, offsetof(struct rt6_info, rt6i_dst));
703
704 if (fn == NULL)
705 goto out;
706
YOSHIFUJI Hideaki66729e12006-08-23 17:20:34 -0700707 pn = fn;
708
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709#ifdef CONFIG_IPV6_SUBTREES
710 if (rt->rt6i_src.plen) {
711 struct fib6_node *sn;
712
713 if (fn->subtree == NULL) {
714 struct fib6_node *sfn;
715
716 /*
717 * Create subtree.
718 *
719 * fn[main tree]
720 * |
721 * sfn[subtree root]
722 * \
723 * sn[new leaf node]
724 */
725
726 /* Create subtree root node */
727 sfn = node_alloc();
728 if (sfn == NULL)
729 goto st_failure;
730
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800731 sfn->leaf = info->nl_net->ipv6.ip6_null_entry;
732 atomic_inc(&info->nl_net->ipv6.ip6_null_entry->rt6i_ref);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 sfn->fn_flags = RTN_ROOT;
734 sfn->fn_sernum = fib6_new_sernum();
735
736 /* Now add the first leaf node to new subtree */
737
738 sn = fib6_add_1(sfn, &rt->rt6i_src.addr,
739 sizeof(struct in6_addr), rt->rt6i_src.plen,
740 offsetof(struct rt6_info, rt6i_src));
741
742 if (sn == NULL) {
743 /* If it is failed, discard just allocated
744 root, and then (in st_failure) stale node
745 in main tree.
746 */
747 node_free(sfn);
748 goto st_failure;
749 }
750
751 /* Now link new subtree to main tree */
752 sfn->parent = fn;
753 fn->subtree = sfn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 } else {
755 sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr,
756 sizeof(struct in6_addr), rt->rt6i_src.plen,
757 offsetof(struct rt6_info, rt6i_src));
758
759 if (sn == NULL)
760 goto st_failure;
761 }
762
YOSHIFUJI Hideaki66729e12006-08-23 17:20:34 -0700763 if (fn->leaf == NULL) {
764 fn->leaf = rt;
765 atomic_inc(&rt->rt6i_ref);
766 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 fn = sn;
768 }
769#endif
770
Thomas Graf86872cb2006-08-22 00:01:08 -0700771 err = fib6_add_rt2node(fn, rt, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772
773 if (err == 0) {
Daniel Lezcano63152fc2008-03-03 23:31:11 -0800774 fib6_start_gc(info->nl_net, rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 if (!(rt->rt6i_flags&RTF_CACHE))
Benjamin Theryec7d43c2008-03-03 23:31:57 -0800776 fib6_prune_clones(info->nl_net, pn, rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 }
778
779out:
YOSHIFUJI Hideaki66729e12006-08-23 17:20:34 -0700780 if (err) {
781#ifdef CONFIG_IPV6_SUBTREES
782 /*
783 * If fib6_add_1 has cleared the old leaf pointer in the
784 * super-tree leaf node we have to find a new one for it.
785 */
David S. Miller3c051232008-04-18 01:46:19 -0700786 if (pn != fn && pn->leaf == rt) {
787 pn->leaf = NULL;
788 atomic_dec(&rt->rt6i_ref);
789 }
YOSHIFUJI Hideaki66729e12006-08-23 17:20:34 -0700790 if (pn != fn && !pn->leaf && !(pn->fn_flags & RTN_RTINFO)) {
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800791 pn->leaf = fib6_find_prefix(info->nl_net, pn);
YOSHIFUJI Hideaki66729e12006-08-23 17:20:34 -0700792#if RT6_DEBUG >= 2
793 if (!pn->leaf) {
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700794 WARN_ON(pn->leaf == NULL);
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800795 pn->leaf = info->nl_net->ipv6.ip6_null_entry;
YOSHIFUJI Hideaki66729e12006-08-23 17:20:34 -0700796 }
797#endif
798 atomic_inc(&pn->leaf->rt6i_ref);
799 }
800#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 dst_free(&rt->u.dst);
YOSHIFUJI Hideaki66729e12006-08-23 17:20:34 -0700802 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 return err;
804
805#ifdef CONFIG_IPV6_SUBTREES
806 /* Subtree creation failed, probably main tree node
807 is orphan. If it is, shoot it.
808 */
809st_failure:
810 if (fn && !(fn->fn_flags & (RTN_RTINFO|RTN_ROOT)))
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800811 fib6_repair_tree(info->nl_net, fn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 dst_free(&rt->u.dst);
813 return err;
814#endif
815}
816
817/*
818 * Routing tree lookup
819 *
820 */
821
822struct lookup_args {
823 int offset; /* key offset on rt6_info */
824 struct in6_addr *addr; /* search key */
825};
826
827static struct fib6_node * fib6_lookup_1(struct fib6_node *root,
828 struct lookup_args *args)
829{
830 struct fib6_node *fn;
Al Viroe69a4adc2006-11-14 20:56:00 -0800831 __be32 dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832
YOSHIFUJI Hideaki825e2882006-08-23 17:21:29 -0700833 if (unlikely(args->offset == 0))
834 return NULL;
835
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 /*
837 * Descend on a tree
838 */
839
840 fn = root;
841
842 for (;;) {
843 struct fib6_node *next;
844
845 dir = addr_bit_set(args->addr, fn->fn_bit);
846
847 next = dir ? fn->right : fn->left;
848
849 if (next) {
850 fn = next;
851 continue;
852 }
853
854 break;
855 }
856
YOSHIFUJI Hideaki3fc5e042006-08-23 17:21:12 -0700857 while(fn) {
YOSHIFUJI Hideaki7fc33162006-08-23 17:22:24 -0700858 if (FIB6_SUBTREE(fn) || fn->fn_flags & RTN_RTINFO) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 struct rt6key *key;
860
861 key = (struct rt6key *) ((u8 *) fn->leaf +
862 args->offset);
863
YOSHIFUJI Hideaki3fc5e042006-08-23 17:21:12 -0700864 if (ipv6_prefix_equal(&key->addr, args->addr, key->plen)) {
865#ifdef CONFIG_IPV6_SUBTREES
866 if (fn->subtree)
867 fn = fib6_lookup_1(fn->subtree, args + 1);
868#endif
869 if (!fn || fn->fn_flags & RTN_RTINFO)
870 return fn;
871 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 }
873
YOSHIFUJI Hideaki3fc5e042006-08-23 17:21:12 -0700874 if (fn->fn_flags & RTN_ROOT)
875 break;
876
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 fn = fn->parent;
878 }
879
880 return NULL;
881}
882
883struct fib6_node * fib6_lookup(struct fib6_node *root, struct in6_addr *daddr,
884 struct in6_addr *saddr)
885{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886 struct fib6_node *fn;
YOSHIFUJI Hideaki825e2882006-08-23 17:21:29 -0700887 struct lookup_args args[] = {
888 {
889 .offset = offsetof(struct rt6_info, rt6i_dst),
890 .addr = daddr,
891 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892#ifdef CONFIG_IPV6_SUBTREES
YOSHIFUJI Hideaki825e2882006-08-23 17:21:29 -0700893 {
894 .offset = offsetof(struct rt6_info, rt6i_src),
895 .addr = saddr,
896 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897#endif
YOSHIFUJI Hideaki825e2882006-08-23 17:21:29 -0700898 {
899 .offset = 0, /* sentinel */
900 }
901 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902
YOSHIFUJI Hideakifefc2a62006-08-23 17:21:50 -0700903 fn = fib6_lookup_1(root, daddr ? args : args + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904
905 if (fn == NULL || fn->fn_flags & RTN_TL_ROOT)
906 fn = root;
907
908 return fn;
909}
910
911/*
912 * Get node with specified destination prefix (and source prefix,
913 * if subtrees are used)
914 */
915
916
917static struct fib6_node * fib6_locate_1(struct fib6_node *root,
918 struct in6_addr *addr,
919 int plen, int offset)
920{
921 struct fib6_node *fn;
922
923 for (fn = root; fn ; ) {
924 struct rt6key *key = (struct rt6key *)((u8 *)fn->leaf + offset);
925
926 /*
927 * Prefix match
928 */
929 if (plen < fn->fn_bit ||
930 !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit))
931 return NULL;
932
933 if (plen == fn->fn_bit)
934 return fn;
935
936 /*
937 * We have more bits to go
938 */
939 if (addr_bit_set(addr, fn->fn_bit))
940 fn = fn->right;
941 else
942 fn = fn->left;
943 }
944 return NULL;
945}
946
947struct fib6_node * fib6_locate(struct fib6_node *root,
948 struct in6_addr *daddr, int dst_len,
949 struct in6_addr *saddr, int src_len)
950{
951 struct fib6_node *fn;
952
953 fn = fib6_locate_1(root, daddr, dst_len,
954 offsetof(struct rt6_info, rt6i_dst));
955
956#ifdef CONFIG_IPV6_SUBTREES
957 if (src_len) {
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700958 WARN_ON(saddr == NULL);
YOSHIFUJI Hideaki3fc5e042006-08-23 17:21:12 -0700959 if (fn && fn->subtree)
960 fn = fib6_locate_1(fn->subtree, saddr, src_len,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 offsetof(struct rt6_info, rt6i_src));
962 }
963#endif
964
965 if (fn && fn->fn_flags&RTN_RTINFO)
966 return fn;
967
968 return NULL;
969}
970
971
972/*
973 * Deletion
974 *
975 */
976
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800977static struct rt6_info *fib6_find_prefix(struct net *net, struct fib6_node *fn)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978{
979 if (fn->fn_flags&RTN_ROOT)
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800980 return net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981
982 while(fn) {
983 if(fn->left)
984 return fn->left->leaf;
985
986 if(fn->right)
987 return fn->right->leaf;
988
YOSHIFUJI Hideaki7fc33162006-08-23 17:22:24 -0700989 fn = FIB6_SUBTREE(fn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 }
991 return NULL;
992}
993
994/*
995 * Called to trim the tree of intermediate nodes when possible. "fn"
996 * is the node we want to try and remove.
997 */
998
Daniel Lezcano8ed67782008-03-04 13:48:30 -0800999static struct fib6_node *fib6_repair_tree(struct net *net,
1000 struct fib6_node *fn)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001{
1002 int children;
1003 int nstate;
1004 struct fib6_node *child, *pn;
1005 struct fib6_walker_t *w;
1006 int iter = 0;
1007
1008 for (;;) {
1009 RT6_TRACE("fixing tree: plen=%d iter=%d\n", fn->fn_bit, iter);
1010 iter++;
1011
Ilpo Järvinen547b7922008-07-25 21:43:18 -07001012 WARN_ON(fn->fn_flags & RTN_RTINFO);
1013 WARN_ON(fn->fn_flags & RTN_TL_ROOT);
1014 WARN_ON(fn->leaf != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015
1016 children = 0;
1017 child = NULL;
1018 if (fn->right) child = fn->right, children |= 1;
1019 if (fn->left) child = fn->left, children |= 2;
1020
YOSHIFUJI Hideaki7fc33162006-08-23 17:22:24 -07001021 if (children == 3 || FIB6_SUBTREE(fn)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022#ifdef CONFIG_IPV6_SUBTREES
1023 /* Subtree root (i.e. fn) may have one child */
1024 || (children && fn->fn_flags&RTN_ROOT)
1025#endif
1026 ) {
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001027 fn->leaf = fib6_find_prefix(net, fn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028#if RT6_DEBUG >= 2
1029 if (fn->leaf==NULL) {
Ilpo Järvinen547b7922008-07-25 21:43:18 -07001030 WARN_ON(!fn->leaf);
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001031 fn->leaf = net->ipv6.ip6_null_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 }
1033#endif
1034 atomic_inc(&fn->leaf->rt6i_ref);
1035 return fn->parent;
1036 }
1037
1038 pn = fn->parent;
1039#ifdef CONFIG_IPV6_SUBTREES
YOSHIFUJI Hideaki7fc33162006-08-23 17:22:24 -07001040 if (FIB6_SUBTREE(pn) == fn) {
Ilpo Järvinen547b7922008-07-25 21:43:18 -07001041 WARN_ON(!(fn->fn_flags & RTN_ROOT));
YOSHIFUJI Hideaki7fc33162006-08-23 17:22:24 -07001042 FIB6_SUBTREE(pn) = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 nstate = FWS_L;
1044 } else {
Ilpo Järvinen547b7922008-07-25 21:43:18 -07001045 WARN_ON(fn->fn_flags & RTN_ROOT);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046#endif
1047 if (pn->right == fn) pn->right = child;
1048 else if (pn->left == fn) pn->left = child;
1049#if RT6_DEBUG >= 2
Ilpo Järvinen547b7922008-07-25 21:43:18 -07001050 else
1051 WARN_ON(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052#endif
1053 if (child)
1054 child->parent = pn;
1055 nstate = FWS_R;
1056#ifdef CONFIG_IPV6_SUBTREES
1057 }
1058#endif
1059
1060 read_lock(&fib6_walker_lock);
1061 FOR_WALKERS(w) {
1062 if (child == NULL) {
1063 if (w->root == fn) {
1064 w->root = w->node = NULL;
1065 RT6_TRACE("W %p adjusted by delroot 1\n", w);
1066 } else if (w->node == fn) {
1067 RT6_TRACE("W %p adjusted by delnode 1, s=%d/%d\n", w, w->state, nstate);
1068 w->node = pn;
1069 w->state = nstate;
1070 }
1071 } else {
1072 if (w->root == fn) {
1073 w->root = child;
1074 RT6_TRACE("W %p adjusted by delroot 2\n", w);
1075 }
1076 if (w->node == fn) {
1077 w->node = child;
1078 if (children&2) {
1079 RT6_TRACE("W %p adjusted by delnode 2, s=%d\n", w, w->state);
1080 w->state = w->state>=FWS_R ? FWS_U : FWS_INIT;
1081 } else {
1082 RT6_TRACE("W %p adjusted by delnode 2, s=%d\n", w, w->state);
1083 w->state = w->state>=FWS_C ? FWS_U : FWS_INIT;
1084 }
1085 }
1086 }
1087 }
1088 read_unlock(&fib6_walker_lock);
1089
1090 node_free(fn);
YOSHIFUJI Hideaki7fc33162006-08-23 17:22:24 -07001091 if (pn->fn_flags&RTN_RTINFO || FIB6_SUBTREE(pn))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 return pn;
1093
1094 rt6_release(pn->leaf);
1095 pn->leaf = NULL;
1096 fn = pn;
1097 }
1098}
1099
1100static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp,
Thomas Graf86872cb2006-08-22 00:01:08 -07001101 struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102{
1103 struct fib6_walker_t *w;
1104 struct rt6_info *rt = *rtp;
Benjamin Theryc5728722008-03-03 23:34:17 -08001105 struct net *net = info->nl_net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106
1107 RT6_TRACE("fib6_del_route\n");
1108
1109 /* Unlink it */
Eric Dumazet7cc48262007-02-09 16:22:57 -08001110 *rtp = rt->u.dst.rt6_next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 rt->rt6i_node = NULL;
Benjamin Theryc5728722008-03-03 23:34:17 -08001112 net->ipv6.rt6_stats->fib_rt_entries--;
1113 net->ipv6.rt6_stats->fib_discarded_routes++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114
David S. Millerf11e6652007-03-24 20:36:25 -07001115 /* Reset round-robin state, if necessary */
1116 if (fn->rr_ptr == rt)
1117 fn->rr_ptr = NULL;
1118
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 /* Adjust walkers */
1120 read_lock(&fib6_walker_lock);
1121 FOR_WALKERS(w) {
1122 if (w->state == FWS_C && w->leaf == rt) {
1123 RT6_TRACE("walker %p adjusted by delroute\n", w);
Eric Dumazet7cc48262007-02-09 16:22:57 -08001124 w->leaf = rt->u.dst.rt6_next;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 if (w->leaf == NULL)
1126 w->state = FWS_U;
1127 }
1128 }
1129 read_unlock(&fib6_walker_lock);
1130
Eric Dumazet7cc48262007-02-09 16:22:57 -08001131 rt->u.dst.rt6_next = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 /* If it was last route, expunge its radix tree node */
1134 if (fn->leaf == NULL) {
1135 fn->fn_flags &= ~RTN_RTINFO;
Benjamin Theryc5728722008-03-03 23:34:17 -08001136 net->ipv6.rt6_stats->fib_route_nodes--;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001137 fn = fib6_repair_tree(net, fn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 }
1139
1140 if (atomic_read(&rt->rt6i_ref) != 1) {
1141 /* This route is used as dummy address holder in some split
1142 * nodes. It is not leaked, but it still holds other resources,
1143 * which must be released in time. So, scan ascendant nodes
1144 * and replace dummy references to this route with references
1145 * to still alive ones.
1146 */
1147 while (fn) {
1148 if (!(fn->fn_flags&RTN_RTINFO) && fn->leaf == rt) {
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001149 fn->leaf = fib6_find_prefix(net, fn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 atomic_inc(&fn->leaf->rt6i_ref);
1151 rt6_release(rt);
1152 }
1153 fn = fn->parent;
1154 }
1155 /* No more references are possible at this point. */
Pavel Emelyanov2df96af2008-02-18 20:50:42 -08001156 BUG_ON(atomic_read(&rt->rt6i_ref) != 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157 }
1158
Thomas Graf86872cb2006-08-22 00:01:08 -07001159 inet6_rt_notify(RTM_DELROUTE, rt, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 rt6_release(rt);
1161}
1162
Thomas Graf86872cb2006-08-22 00:01:08 -07001163int fib6_del(struct rt6_info *rt, struct nl_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001165 struct net *net = info->nl_net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 struct fib6_node *fn = rt->rt6i_node;
1167 struct rt6_info **rtp;
1168
1169#if RT6_DEBUG >= 2
1170 if (rt->u.dst.obsolete>0) {
Ilpo Järvinen547b7922008-07-25 21:43:18 -07001171 WARN_ON(fn != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 return -ENOENT;
1173 }
1174#endif
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001175 if (fn == NULL || rt == net->ipv6.ip6_null_entry)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176 return -ENOENT;
1177
Ilpo Järvinen547b7922008-07-25 21:43:18 -07001178 WARN_ON(!(fn->fn_flags & RTN_RTINFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179
YOSHIFUJI Hideaki150730d2006-08-23 17:22:55 -07001180 if (!(rt->rt6i_flags&RTF_CACHE)) {
1181 struct fib6_node *pn = fn;
1182#ifdef CONFIG_IPV6_SUBTREES
1183 /* clones of this route might be in another subtree */
1184 if (rt->rt6i_src.plen) {
1185 while (!(pn->fn_flags&RTN_ROOT))
1186 pn = pn->parent;
1187 pn = pn->parent;
1188 }
1189#endif
Benjamin Theryec7d43c2008-03-03 23:31:57 -08001190 fib6_prune_clones(info->nl_net, pn, rt);
YOSHIFUJI Hideaki150730d2006-08-23 17:22:55 -07001191 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192
1193 /*
1194 * Walk the leaf entries looking for ourself
1195 */
1196
Eric Dumazet7cc48262007-02-09 16:22:57 -08001197 for (rtp = &fn->leaf; *rtp; rtp = &(*rtp)->u.dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 if (*rtp == rt) {
Thomas Graf86872cb2006-08-22 00:01:08 -07001199 fib6_del_route(fn, rtp, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200 return 0;
1201 }
1202 }
1203 return -ENOENT;
1204}
1205
1206/*
1207 * Tree traversal function.
1208 *
1209 * Certainly, it is not interrupt safe.
1210 * However, it is internally reenterable wrt itself and fib6_add/fib6_del.
1211 * It means, that we can modify tree during walking
1212 * and use this function for garbage collection, clone pruning,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001213 * cleaning tree when a device goes down etc. etc.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 *
1215 * It guarantees that every node will be traversed,
1216 * and that it will be traversed only once.
1217 *
1218 * Callback function w->func may return:
1219 * 0 -> continue walking.
1220 * positive value -> walking is suspended (used by tree dumps,
1221 * and probably by gc, if it will be split to several slices)
1222 * negative value -> terminate walking.
1223 *
1224 * The function itself returns:
1225 * 0 -> walk is complete.
1226 * >0 -> walk is incomplete (i.e. suspended)
1227 * <0 -> walk is terminated by an error.
1228 */
1229
Adrian Bunk90d41122006-08-14 23:49:16 -07001230static int fib6_walk_continue(struct fib6_walker_t *w)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231{
1232 struct fib6_node *fn, *pn;
1233
1234 for (;;) {
1235 fn = w->node;
1236 if (fn == NULL)
1237 return 0;
1238
1239 if (w->prune && fn != w->root &&
1240 fn->fn_flags&RTN_RTINFO && w->state < FWS_C) {
1241 w->state = FWS_C;
1242 w->leaf = fn->leaf;
1243 }
1244 switch (w->state) {
1245#ifdef CONFIG_IPV6_SUBTREES
1246 case FWS_S:
YOSHIFUJI Hideaki7fc33162006-08-23 17:22:24 -07001247 if (FIB6_SUBTREE(fn)) {
1248 w->node = FIB6_SUBTREE(fn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 continue;
1250 }
1251 w->state = FWS_L;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001252#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253 case FWS_L:
1254 if (fn->left) {
1255 w->node = fn->left;
1256 w->state = FWS_INIT;
1257 continue;
1258 }
1259 w->state = FWS_R;
1260 case FWS_R:
1261 if (fn->right) {
1262 w->node = fn->right;
1263 w->state = FWS_INIT;
1264 continue;
1265 }
1266 w->state = FWS_C;
1267 w->leaf = fn->leaf;
1268 case FWS_C:
1269 if (w->leaf && fn->fn_flags&RTN_RTINFO) {
Patrick McHardy2bec5a32010-02-08 05:19:03 +00001270 int err;
1271
1272 if (w->count < w->skip) {
1273 w->count++;
1274 continue;
1275 }
1276
1277 err = w->func(w);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 if (err)
1279 return err;
Patrick McHardy2bec5a32010-02-08 05:19:03 +00001280
1281 w->count++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 continue;
1283 }
1284 w->state = FWS_U;
1285 case FWS_U:
1286 if (fn == w->root)
1287 return 0;
1288 pn = fn->parent;
1289 w->node = pn;
1290#ifdef CONFIG_IPV6_SUBTREES
YOSHIFUJI Hideaki7fc33162006-08-23 17:22:24 -07001291 if (FIB6_SUBTREE(pn) == fn) {
Ilpo Järvinen547b7922008-07-25 21:43:18 -07001292 WARN_ON(!(fn->fn_flags & RTN_ROOT));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293 w->state = FWS_L;
1294 continue;
1295 }
1296#endif
1297 if (pn->left == fn) {
1298 w->state = FWS_R;
1299 continue;
1300 }
1301 if (pn->right == fn) {
1302 w->state = FWS_C;
1303 w->leaf = w->node->leaf;
1304 continue;
1305 }
1306#if RT6_DEBUG >= 2
Ilpo Järvinen547b7922008-07-25 21:43:18 -07001307 WARN_ON(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308#endif
1309 }
1310 }
1311}
1312
Adrian Bunk90d41122006-08-14 23:49:16 -07001313static int fib6_walk(struct fib6_walker_t *w)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314{
1315 int res;
1316
1317 w->state = FWS_INIT;
1318 w->node = w->root;
1319
1320 fib6_walker_link(w);
1321 res = fib6_walk_continue(w);
1322 if (res <= 0)
1323 fib6_walker_unlink(w);
1324 return res;
1325}
1326
1327static int fib6_clean_node(struct fib6_walker_t *w)
1328{
1329 int res;
1330 struct rt6_info *rt;
Benjamin Thery0a8891a2007-10-08 20:39:36 -07001331 struct fib6_cleaner_t *c = container_of(w, struct fib6_cleaner_t, w);
Benjamin Theryec7d43c2008-03-03 23:31:57 -08001332 struct nl_info info = {
1333 .nl_net = c->net,
1334 };
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335
Eric Dumazet7cc48262007-02-09 16:22:57 -08001336 for (rt = w->leaf; rt; rt = rt->u.dst.rt6_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337 res = c->func(rt, c->arg);
1338 if (res < 0) {
1339 w->leaf = rt;
Denis V. Lunev528c4ce2007-12-13 09:45:12 -08001340 res = fib6_del(rt, &info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341 if (res) {
1342#if RT6_DEBUG >= 2
1343 printk(KERN_DEBUG "fib6_clean_node: del failed: rt=%p@%p err=%d\n", rt, rt->rt6i_node, res);
1344#endif
1345 continue;
1346 }
1347 return 0;
1348 }
Ilpo Järvinen547b7922008-07-25 21:43:18 -07001349 WARN_ON(res != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 }
1351 w->leaf = rt;
1352 return 0;
1353}
1354
1355/*
1356 * Convenient frontend to tree walker.
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001357 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 * func is called on each route.
1359 * It may return -1 -> delete this route.
1360 * 0 -> continue walking
1361 *
1362 * prune==1 -> only immediate children of node (certainly,
1363 * ignoring pure split nodes) will be scanned.
1364 */
1365
Benjamin Theryec7d43c2008-03-03 23:31:57 -08001366static void fib6_clean_tree(struct net *net, struct fib6_node *root,
Adrian Bunk8ce11e62006-08-07 21:50:48 -07001367 int (*func)(struct rt6_info *, void *arg),
1368 int prune, void *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369{
1370 struct fib6_cleaner_t c;
1371
1372 c.w.root = root;
1373 c.w.func = fib6_clean_node;
1374 c.w.prune = prune;
Patrick McHardy2bec5a32010-02-08 05:19:03 +00001375 c.w.count = 0;
1376 c.w.skip = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377 c.func = func;
1378 c.arg = arg;
Benjamin Theryec7d43c2008-03-03 23:31:57 -08001379 c.net = net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380
1381 fib6_walk(&c.w);
1382}
1383
Daniel Lezcanof3db4852008-03-03 23:27:06 -08001384void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg),
Thomas Grafc71099a2006-08-04 23:20:06 -07001385 int prune, void *arg)
1386{
Thomas Grafc71099a2006-08-04 23:20:06 -07001387 struct fib6_table *table;
Patrick McHardy1b43af52006-08-10 23:11:17 -07001388 struct hlist_node *node;
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001389 struct hlist_head *head;
Patrick McHardy1b43af52006-08-10 23:11:17 -07001390 unsigned int h;
Thomas Grafc71099a2006-08-04 23:20:06 -07001391
Patrick McHardy1b43af52006-08-10 23:11:17 -07001392 rcu_read_lock();
Neil Hormana33bc5c2009-07-30 18:52:15 -07001393 for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
Daniel Lezcanof3db4852008-03-03 23:27:06 -08001394 head = &net->ipv6.fib_table_hash[h];
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001395 hlist_for_each_entry_rcu(table, node, head, tb6_hlist) {
Thomas Grafc71099a2006-08-04 23:20:06 -07001396 write_lock_bh(&table->tb6_lock);
Benjamin Theryec7d43c2008-03-03 23:31:57 -08001397 fib6_clean_tree(net, &table->tb6_root,
1398 func, prune, arg);
Thomas Grafc71099a2006-08-04 23:20:06 -07001399 write_unlock_bh(&table->tb6_lock);
1400 }
1401 }
Patrick McHardy1b43af52006-08-10 23:11:17 -07001402 rcu_read_unlock();
Thomas Grafc71099a2006-08-04 23:20:06 -07001403}
1404
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405static int fib6_prune_clone(struct rt6_info *rt, void *arg)
1406{
1407 if (rt->rt6i_flags & RTF_CACHE) {
1408 RT6_TRACE("pruning clone %p\n", rt);
1409 return -1;
1410 }
1411
1412 return 0;
1413}
1414
Benjamin Theryec7d43c2008-03-03 23:31:57 -08001415static void fib6_prune_clones(struct net *net, struct fib6_node *fn,
1416 struct rt6_info *rt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417{
Benjamin Theryec7d43c2008-03-03 23:31:57 -08001418 fib6_clean_tree(net, fn, fib6_prune_clone, 1, rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419}
1420
1421/*
1422 * Garbage collection
1423 */
1424
1425static struct fib6_gc_args
1426{
1427 int timeout;
1428 int more;
1429} gc_args;
1430
1431static int fib6_age(struct rt6_info *rt, void *arg)
1432{
1433 unsigned long now = jiffies;
1434
1435 /*
1436 * check addrconf expiration here.
1437 * Routes are expired even if they are in use.
1438 *
1439 * Also age clones. Note, that clones are aged out
1440 * only if they are not in use now.
1441 */
1442
1443 if (rt->rt6i_flags&RTF_EXPIRES && rt->rt6i_expires) {
1444 if (time_after(now, rt->rt6i_expires)) {
1445 RT6_TRACE("expiring %p\n", rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 return -1;
1447 }
1448 gc_args.more++;
1449 } else if (rt->rt6i_flags & RTF_CACHE) {
1450 if (atomic_read(&rt->u.dst.__refcnt) == 0 &&
1451 time_after_eq(now, rt->u.dst.lastuse + gc_args.timeout)) {
1452 RT6_TRACE("aging clone %p\n", rt);
1453 return -1;
1454 } else if ((rt->rt6i_flags & RTF_GATEWAY) &&
1455 (!(rt->rt6i_nexthop->flags & NTF_ROUTER))) {
1456 RT6_TRACE("purging route %p via non-router but gateway\n",
1457 rt);
1458 return -1;
1459 }
1460 gc_args.more++;
1461 }
1462
1463 return 0;
1464}
1465
1466static DEFINE_SPINLOCK(fib6_gc_lock);
1467
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001468void fib6_run_gc(unsigned long expires, struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469{
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001470 if (expires != ~0UL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471 spin_lock_bh(&fib6_gc_lock);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001472 gc_args.timeout = expires ? (int)expires :
1473 net->ipv6.sysctl.ip6_rt_gc_interval;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474 } else {
Stephen Hemmingera76d7342008-07-22 14:34:35 -07001475 if (!spin_trylock_bh(&fib6_gc_lock)) {
Stephen Hemminger417f28b2008-07-22 14:33:45 -07001476 mod_timer(&net->ipv6.ip6_fib_timer, jiffies + HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001477 return;
1478 }
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001479 gc_args.timeout = net->ipv6.sysctl.ip6_rt_gc_interval;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481
Stephen Hemminger3d0f24a2008-07-22 14:35:50 -07001482 gc_args.more = icmp6_dst_gc();
Daniel Lezcanof3db4852008-03-03 23:27:06 -08001483
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001484 fib6_clean_all(net, fib6_age, 0, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485
1486 if (gc_args.more)
Stephen Hemmingerc8a45222008-07-22 14:34:09 -07001487 mod_timer(&net->ipv6.ip6_fib_timer,
1488 round_jiffies(jiffies
1489 + net->ipv6.sysctl.ip6_rt_gc_interval));
Stephen Hemminger417f28b2008-07-22 14:33:45 -07001490 else
1491 del_timer(&net->ipv6.ip6_fib_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492 spin_unlock_bh(&fib6_gc_lock);
1493}
1494
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001495static void fib6_gc_timer_cb(unsigned long arg)
1496{
1497 fib6_run_gc(0, (struct net *)arg);
1498}
1499
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001500static int __net_init fib6_net_init(struct net *net)
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001501{
Stephen Hemminger417f28b2008-07-22 14:33:45 -07001502 setup_timer(&net->ipv6.ip6_fib_timer, fib6_gc_timer_cb, (unsigned long)net);
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001503
Benjamin Theryc5728722008-03-03 23:34:17 -08001504 net->ipv6.rt6_stats = kzalloc(sizeof(*net->ipv6.rt6_stats), GFP_KERNEL);
1505 if (!net->ipv6.rt6_stats)
1506 goto out_timer;
1507
Neil Hormana33bc5c2009-07-30 18:52:15 -07001508 net->ipv6.fib_table_hash = kcalloc(FIB6_TABLE_HASHSZ,
Stephen Hemminger75307c02008-07-22 14:35:07 -07001509 sizeof(*net->ipv6.fib_table_hash),
1510 GFP_KERNEL);
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001511 if (!net->ipv6.fib_table_hash)
Benjamin Theryc5728722008-03-03 23:34:17 -08001512 goto out_rt6_stats;
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001513
1514 net->ipv6.fib6_main_tbl = kzalloc(sizeof(*net->ipv6.fib6_main_tbl),
1515 GFP_KERNEL);
1516 if (!net->ipv6.fib6_main_tbl)
1517 goto out_fib_table_hash;
1518
1519 net->ipv6.fib6_main_tbl->tb6_id = RT6_TABLE_MAIN;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001520 net->ipv6.fib6_main_tbl->tb6_root.leaf = net->ipv6.ip6_null_entry;
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001521 net->ipv6.fib6_main_tbl->tb6_root.fn_flags =
1522 RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
1523
1524#ifdef CONFIG_IPV6_MULTIPLE_TABLES
1525 net->ipv6.fib6_local_tbl = kzalloc(sizeof(*net->ipv6.fib6_local_tbl),
1526 GFP_KERNEL);
1527 if (!net->ipv6.fib6_local_tbl)
1528 goto out_fib6_main_tbl;
1529 net->ipv6.fib6_local_tbl->tb6_id = RT6_TABLE_LOCAL;
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001530 net->ipv6.fib6_local_tbl->tb6_root.leaf = net->ipv6.ip6_null_entry;
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001531 net->ipv6.fib6_local_tbl->tb6_root.fn_flags =
1532 RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
1533#endif
1534 fib6_tables_init(net);
1535
Stephen Hemminger417f28b2008-07-22 14:33:45 -07001536 return 0;
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001537
1538#ifdef CONFIG_IPV6_MULTIPLE_TABLES
1539out_fib6_main_tbl:
1540 kfree(net->ipv6.fib6_main_tbl);
1541#endif
1542out_fib_table_hash:
1543 kfree(net->ipv6.fib_table_hash);
Benjamin Theryc5728722008-03-03 23:34:17 -08001544out_rt6_stats:
1545 kfree(net->ipv6.rt6_stats);
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001546out_timer:
Stephen Hemminger417f28b2008-07-22 14:33:45 -07001547 return -ENOMEM;
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001548 }
1549
1550static void fib6_net_exit(struct net *net)
1551{
Daniel Lezcano8ed67782008-03-04 13:48:30 -08001552 rt6_ifdown(net, NULL);
Stephen Hemminger417f28b2008-07-22 14:33:45 -07001553 del_timer_sync(&net->ipv6.ip6_fib_timer);
1554
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001555#ifdef CONFIG_IPV6_MULTIPLE_TABLES
1556 kfree(net->ipv6.fib6_local_tbl);
1557#endif
1558 kfree(net->ipv6.fib6_main_tbl);
1559 kfree(net->ipv6.fib_table_hash);
Benjamin Theryc5728722008-03-03 23:34:17 -08001560 kfree(net->ipv6.rt6_stats);
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001561}
1562
1563static struct pernet_operations fib6_net_ops = {
1564 .init = fib6_net_init,
1565 .exit = fib6_net_exit,
1566};
1567
Daniel Lezcanod63bddb2007-12-07 00:40:34 -08001568int __init fib6_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569{
Daniel Lezcanoe0b855902008-03-03 23:24:31 -08001570 int ret = -ENOMEM;
Daniel Lezcano63152fc2008-03-03 23:31:11 -08001571
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572 fib6_node_kmem = kmem_cache_create("fib6_nodes",
1573 sizeof(struct fib6_node),
Daniel Lezcanof845ab62007-12-07 00:45:16 -08001574 0, SLAB_HWCACHE_ALIGN,
Paul Mundt20c2df82007-07-20 10:11:58 +09001575 NULL);
Daniel Lezcanof845ab62007-12-07 00:45:16 -08001576 if (!fib6_node_kmem)
Daniel Lezcanoe0b855902008-03-03 23:24:31 -08001577 goto out;
1578
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001579 ret = register_pernet_subsys(&fib6_net_ops);
1580 if (ret)
Benjamin Theryc5728722008-03-03 23:34:17 -08001581 goto out_kmem_cache_create;
Daniel Lezcanoe0b855902008-03-03 23:24:31 -08001582
Daniel Lezcanod63bddb2007-12-07 00:40:34 -08001583 ret = __rtnl_register(PF_INET6, RTM_GETROUTE, NULL, inet6_dump_fib);
1584 if (ret)
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001585 goto out_unregister_subsys;
Daniel Lezcanod63bddb2007-12-07 00:40:34 -08001586out:
1587 return ret;
1588
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001589out_unregister_subsys:
1590 unregister_pernet_subsys(&fib6_net_ops);
Daniel Lezcanod63bddb2007-12-07 00:40:34 -08001591out_kmem_cache_create:
1592 kmem_cache_destroy(fib6_node_kmem);
1593 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001594}
1595
1596void fib6_gc_cleanup(void)
1597{
Daniel Lezcano58f09b72008-03-03 23:25:27 -08001598 unregister_pernet_subsys(&fib6_net_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599 kmem_cache_destroy(fib6_node_kmem);
1600}