diff options
Diffstat (limited to 'net/ipv6/ip6_fib.c')
-rw-r--r-- | net/ipv6/ip6_fib.c | 463 |
1 files changed, 387 insertions, 76 deletions
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 764221220afd..8fcae7a6510b 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c | |||
@@ -18,6 +18,7 @@ | |||
18 | * Yuji SEKIYA @USAGI: Support default route on router node; | 18 | * Yuji SEKIYA @USAGI: Support default route on router node; |
19 | * remove ip6_null_entry from the top of | 19 | * remove ip6_null_entry from the top of |
20 | * routing table. | 20 | * routing table. |
21 | * Ville Nuorvala: Fixed routing subtrees. | ||
21 | */ | 22 | */ |
22 | #include <linux/errno.h> | 23 | #include <linux/errno.h> |
23 | #include <linux/types.h> | 24 | #include <linux/types.h> |
@@ -26,6 +27,7 @@ | |||
26 | #include <linux/netdevice.h> | 27 | #include <linux/netdevice.h> |
27 | #include <linux/in6.h> | 28 | #include <linux/in6.h> |
28 | #include <linux/init.h> | 29 | #include <linux/init.h> |
30 | #include <linux/list.h> | ||
29 | 31 | ||
30 | #ifdef CONFIG_PROC_FS | 32 | #ifdef CONFIG_PROC_FS |
31 | #include <linux/proc_fs.h> | 33 | #include <linux/proc_fs.h> |
@@ -68,19 +70,19 @@ struct fib6_cleaner_t | |||
68 | void *arg; | 70 | void *arg; |
69 | }; | 71 | }; |
70 | 72 | ||
71 | DEFINE_RWLOCK(fib6_walker_lock); | 73 | static DEFINE_RWLOCK(fib6_walker_lock); |
72 | |||
73 | 74 | ||
74 | #ifdef CONFIG_IPV6_SUBTREES | 75 | #ifdef CONFIG_IPV6_SUBTREES |
75 | #define FWS_INIT FWS_S | 76 | #define FWS_INIT FWS_S |
76 | #define SUBTREE(fn) ((fn)->subtree) | ||
77 | #else | 77 | #else |
78 | #define FWS_INIT FWS_L | 78 | #define FWS_INIT FWS_L |
79 | #define SUBTREE(fn) NULL | ||
80 | #endif | 79 | #endif |
81 | 80 | ||
82 | static void fib6_prune_clones(struct fib6_node *fn, struct rt6_info *rt); | 81 | static void fib6_prune_clones(struct fib6_node *fn, struct rt6_info *rt); |
82 | static struct rt6_info * fib6_find_prefix(struct fib6_node *fn); | ||
83 | static struct fib6_node * fib6_repair_tree(struct fib6_node *fn); | 83 | static struct fib6_node * fib6_repair_tree(struct fib6_node *fn); |
84 | static int fib6_walk(struct fib6_walker_t *w); | ||
85 | static int fib6_walk_continue(struct fib6_walker_t *w); | ||
84 | 86 | ||
85 | /* | 87 | /* |
86 | * A routing update causes an increase of the serial number on the | 88 | * A routing update causes an increase of the serial number on the |
@@ -93,13 +95,31 @@ static __u32 rt_sernum; | |||
93 | 95 | ||
94 | static DEFINE_TIMER(ip6_fib_timer, fib6_run_gc, 0, 0); | 96 | static DEFINE_TIMER(ip6_fib_timer, fib6_run_gc, 0, 0); |
95 | 97 | ||
96 | struct fib6_walker_t fib6_walker_list = { | 98 | static struct fib6_walker_t fib6_walker_list = { |
97 | .prev = &fib6_walker_list, | 99 | .prev = &fib6_walker_list, |
98 | .next = &fib6_walker_list, | 100 | .next = &fib6_walker_list, |
99 | }; | 101 | }; |
100 | 102 | ||
101 | #define FOR_WALKERS(w) for ((w)=fib6_walker_list.next; (w) != &fib6_walker_list; (w)=(w)->next) | 103 | #define FOR_WALKERS(w) for ((w)=fib6_walker_list.next; (w) != &fib6_walker_list; (w)=(w)->next) |
102 | 104 | ||
105 | static inline void fib6_walker_link(struct fib6_walker_t *w) | ||
106 | { | ||
107 | write_lock_bh(&fib6_walker_lock); | ||
108 | w->next = fib6_walker_list.next; | ||
109 | w->prev = &fib6_walker_list; | ||
110 | w->next->prev = w; | ||
111 | w->prev->next = w; | ||
112 | write_unlock_bh(&fib6_walker_lock); | ||
113 | } | ||
114 | |||
115 | static inline void fib6_walker_unlink(struct fib6_walker_t *w) | ||
116 | { | ||
117 | write_lock_bh(&fib6_walker_lock); | ||
118 | w->next->prev = w->prev; | ||
119 | w->prev->next = w->next; | ||
120 | w->prev = w->next = w; | ||
121 | write_unlock_bh(&fib6_walker_lock); | ||
122 | } | ||
103 | static __inline__ u32 fib6_new_sernum(void) | 123 | static __inline__ u32 fib6_new_sernum(void) |
104 | { | 124 | { |
105 | u32 n = ++rt_sernum; | 125 | u32 n = ++rt_sernum; |
@@ -147,6 +167,253 @@ static __inline__ void rt6_release(struct rt6_info *rt) | |||
147 | dst_free(&rt->u.dst); | 167 | dst_free(&rt->u.dst); |
148 | } | 168 | } |
149 | 169 | ||
170 | static struct fib6_table fib6_main_tbl = { | ||
171 | .tb6_id = RT6_TABLE_MAIN, | ||
172 | .tb6_lock = RW_LOCK_UNLOCKED, | ||
173 | .tb6_root = { | ||
174 | .leaf = &ip6_null_entry, | ||
175 | .fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO, | ||
176 | }, | ||
177 | }; | ||
178 | |||
179 | #ifdef CONFIG_IPV6_MULTIPLE_TABLES | ||
180 | #define FIB_TABLE_HASHSZ 256 | ||
181 | #else | ||
182 | #define FIB_TABLE_HASHSZ 1 | ||
183 | #endif | ||
184 | static struct hlist_head fib_table_hash[FIB_TABLE_HASHSZ]; | ||
185 | |||
186 | static void fib6_link_table(struct fib6_table *tb) | ||
187 | { | ||
188 | unsigned int h; | ||
189 | |||
190 | h = tb->tb6_id & (FIB_TABLE_HASHSZ - 1); | ||
191 | |||
192 | /* | ||
193 | * No protection necessary, this is the only list mutatation | ||
194 | * operation, tables never disappear once they exist. | ||
195 | */ | ||
196 | hlist_add_head_rcu(&tb->tb6_hlist, &fib_table_hash[h]); | ||
197 | } | ||
198 | |||
199 | #ifdef CONFIG_IPV6_MULTIPLE_TABLES | ||
200 | static struct fib6_table fib6_local_tbl = { | ||
201 | .tb6_id = RT6_TABLE_LOCAL, | ||
202 | .tb6_lock = RW_LOCK_UNLOCKED, | ||
203 | .tb6_root = { | ||
204 | .leaf = &ip6_null_entry, | ||
205 | .fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO, | ||
206 | }, | ||
207 | }; | ||
208 | |||
209 | static struct fib6_table *fib6_alloc_table(u32 id) | ||
210 | { | ||
211 | struct fib6_table *table; | ||
212 | |||
213 | table = kzalloc(sizeof(*table), GFP_ATOMIC); | ||
214 | if (table != NULL) { | ||
215 | table->tb6_id = id; | ||
216 | table->tb6_lock = RW_LOCK_UNLOCKED; | ||
217 | table->tb6_root.leaf = &ip6_null_entry; | ||
218 | table->tb6_root.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO; | ||
219 | } | ||
220 | |||
221 | return table; | ||
222 | } | ||
223 | |||
224 | struct fib6_table *fib6_new_table(u32 id) | ||
225 | { | ||
226 | struct fib6_table *tb; | ||
227 | |||
228 | if (id == 0) | ||
229 | id = RT6_TABLE_MAIN; | ||
230 | tb = fib6_get_table(id); | ||
231 | if (tb) | ||
232 | return tb; | ||
233 | |||
234 | tb = fib6_alloc_table(id); | ||
235 | if (tb != NULL) | ||
236 | fib6_link_table(tb); | ||
237 | |||
238 | return tb; | ||
239 | } | ||
240 | |||
241 | struct fib6_table *fib6_get_table(u32 id) | ||
242 | { | ||
243 | struct fib6_table *tb; | ||
244 | struct hlist_node *node; | ||
245 | unsigned int h; | ||
246 | |||
247 | if (id == 0) | ||
248 | id = RT6_TABLE_MAIN; | ||
249 | h = id & (FIB_TABLE_HASHSZ - 1); | ||
250 | rcu_read_lock(); | ||
251 | hlist_for_each_entry_rcu(tb, node, &fib_table_hash[h], tb6_hlist) { | ||
252 | if (tb->tb6_id == id) { | ||
253 | rcu_read_unlock(); | ||
254 | return tb; | ||
255 | } | ||
256 | } | ||
257 | rcu_read_unlock(); | ||
258 | |||
259 | return NULL; | ||
260 | } | ||
261 | |||
262 | static void __init fib6_tables_init(void) | ||
263 | { | ||
264 | fib6_link_table(&fib6_main_tbl); | ||
265 | fib6_link_table(&fib6_local_tbl); | ||
266 | } | ||
267 | |||
268 | #else | ||
269 | |||
270 | struct fib6_table *fib6_new_table(u32 id) | ||
271 | { | ||
272 | return fib6_get_table(id); | ||
273 | } | ||
274 | |||
275 | struct fib6_table *fib6_get_table(u32 id) | ||
276 | { | ||
277 | return &fib6_main_tbl; | ||
278 | } | ||
279 | |||
280 | struct dst_entry *fib6_rule_lookup(struct flowi *fl, int flags, | ||
281 | pol_lookup_t lookup) | ||
282 | { | ||
283 | return (struct dst_entry *) lookup(&fib6_main_tbl, fl, flags); | ||
284 | } | ||
285 | |||
286 | static void __init fib6_tables_init(void) | ||
287 | { | ||
288 | fib6_link_table(&fib6_main_tbl); | ||
289 | } | ||
290 | |||
291 | #endif | ||
292 | |||
293 | static int fib6_dump_node(struct fib6_walker_t *w) | ||
294 | { | ||
295 | int res; | ||
296 | struct rt6_info *rt; | ||
297 | |||
298 | for (rt = w->leaf; rt; rt = rt->u.next) { | ||
299 | res = rt6_dump_route(rt, w->args); | ||
300 | if (res < 0) { | ||
301 | /* Frame is full, suspend walking */ | ||
302 | w->leaf = rt; | ||
303 | return 1; | ||
304 | } | ||
305 | BUG_TRAP(res!=0); | ||
306 | } | ||
307 | w->leaf = NULL; | ||
308 | return 0; | ||
309 | } | ||
310 | |||
311 | static void fib6_dump_end(struct netlink_callback *cb) | ||
312 | { | ||
313 | struct fib6_walker_t *w = (void*)cb->args[2]; | ||
314 | |||
315 | if (w) { | ||
316 | cb->args[2] = 0; | ||
317 | kfree(w); | ||
318 | } | ||
319 | cb->done = (void*)cb->args[3]; | ||
320 | cb->args[1] = 3; | ||
321 | } | ||
322 | |||
323 | static int fib6_dump_done(struct netlink_callback *cb) | ||
324 | { | ||
325 | fib6_dump_end(cb); | ||
326 | return cb->done ? cb->done(cb) : 0; | ||
327 | } | ||
328 | |||
329 | static int fib6_dump_table(struct fib6_table *table, struct sk_buff *skb, | ||
330 | struct netlink_callback *cb) | ||
331 | { | ||
332 | struct fib6_walker_t *w; | ||
333 | int res; | ||
334 | |||
335 | w = (void *)cb->args[2]; | ||
336 | w->root = &table->tb6_root; | ||
337 | |||
338 | if (cb->args[4] == 0) { | ||
339 | read_lock_bh(&table->tb6_lock); | ||
340 | res = fib6_walk(w); | ||
341 | read_unlock_bh(&table->tb6_lock); | ||
342 | if (res > 0) | ||
343 | cb->args[4] = 1; | ||
344 | } else { | ||
345 | read_lock_bh(&table->tb6_lock); | ||
346 | res = fib6_walk_continue(w); | ||
347 | read_unlock_bh(&table->tb6_lock); | ||
348 | if (res != 0) { | ||
349 | if (res < 0) | ||
350 | fib6_walker_unlink(w); | ||
351 | goto end; | ||
352 | } | ||
353 | fib6_walker_unlink(w); | ||
354 | cb->args[4] = 0; | ||
355 | } | ||
356 | end: | ||
357 | return res; | ||
358 | } | ||
359 | |||
360 | int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) | ||
361 | { | ||
362 | unsigned int h, s_h; | ||
363 | unsigned int e = 0, s_e; | ||
364 | struct rt6_rtnl_dump_arg arg; | ||
365 | struct fib6_walker_t *w; | ||
366 | struct fib6_table *tb; | ||
367 | struct hlist_node *node; | ||
368 | int res = 0; | ||
369 | |||
370 | s_h = cb->args[0]; | ||
371 | s_e = cb->args[1]; | ||
372 | |||
373 | w = (void *)cb->args[2]; | ||
374 | if (w == NULL) { | ||
375 | /* New dump: | ||
376 | * | ||
377 | * 1. hook callback destructor. | ||
378 | */ | ||
379 | cb->args[3] = (long)cb->done; | ||
380 | cb->done = fib6_dump_done; | ||
381 | |||
382 | /* | ||
383 | * 2. allocate and initialize walker. | ||
384 | */ | ||
385 | w = kzalloc(sizeof(*w), GFP_ATOMIC); | ||
386 | if (w == NULL) | ||
387 | return -ENOMEM; | ||
388 | w->func = fib6_dump_node; | ||
389 | cb->args[2] = (long)w; | ||
390 | } | ||
391 | |||
392 | arg.skb = skb; | ||
393 | arg.cb = cb; | ||
394 | w->args = &arg; | ||
395 | |||
396 | for (h = s_h; h < FIB_TABLE_HASHSZ; h++, s_e = 0) { | ||
397 | e = 0; | ||
398 | hlist_for_each_entry(tb, node, &fib_table_hash[h], tb6_hlist) { | ||
399 | if (e < s_e) | ||
400 | goto next; | ||
401 | res = fib6_dump_table(tb, skb, cb); | ||
402 | if (res != 0) | ||
403 | goto out; | ||
404 | next: | ||
405 | e++; | ||
406 | } | ||
407 | } | ||
408 | out: | ||
409 | cb->args[1] = e; | ||
410 | cb->args[0] = h; | ||
411 | |||
412 | res = res < 0 ? res : skb->len; | ||
413 | if (res <= 0) | ||
414 | fib6_dump_end(cb); | ||
415 | return res; | ||
416 | } | ||
150 | 417 | ||
151 | /* | 418 | /* |
152 | * Routing Table | 419 | * Routing Table |
@@ -343,7 +610,7 @@ insert_above: | |||
343 | */ | 610 | */ |
344 | 611 | ||
345 | static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, | 612 | static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, |
346 | struct nlmsghdr *nlh, struct netlink_skb_parms *req) | 613 | struct nl_info *info) |
347 | { | 614 | { |
348 | struct rt6_info *iter = NULL; | 615 | struct rt6_info *iter = NULL; |
349 | struct rt6_info **ins; | 616 | struct rt6_info **ins; |
@@ -398,7 +665,7 @@ out: | |||
398 | *ins = rt; | 665 | *ins = rt; |
399 | rt->rt6i_node = fn; | 666 | rt->rt6i_node = fn; |
400 | atomic_inc(&rt->rt6i_ref); | 667 | atomic_inc(&rt->rt6i_ref); |
401 | inet6_rt_notify(RTM_NEWROUTE, rt, nlh, req); | 668 | inet6_rt_notify(RTM_NEWROUTE, rt, info); |
402 | rt6_stats.fib_rt_entries++; | 669 | rt6_stats.fib_rt_entries++; |
403 | 670 | ||
404 | if ((fn->fn_flags & RTN_RTINFO) == 0) { | 671 | if ((fn->fn_flags & RTN_RTINFO) == 0) { |
@@ -428,10 +695,9 @@ void fib6_force_start_gc(void) | |||
428 | * with source addr info in sub-trees | 695 | * with source addr info in sub-trees |
429 | */ | 696 | */ |
430 | 697 | ||
431 | int fib6_add(struct fib6_node *root, struct rt6_info *rt, | 698 | int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info) |
432 | struct nlmsghdr *nlh, void *_rtattr, struct netlink_skb_parms *req) | ||
433 | { | 699 | { |
434 | struct fib6_node *fn; | 700 | struct fib6_node *fn, *pn = NULL; |
435 | int err = -ENOMEM; | 701 | int err = -ENOMEM; |
436 | 702 | ||
437 | fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr), | 703 | fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr), |
@@ -440,6 +706,8 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, | |||
440 | if (fn == NULL) | 706 | if (fn == NULL) |
441 | goto out; | 707 | goto out; |
442 | 708 | ||
709 | pn = fn; | ||
710 | |||
443 | #ifdef CONFIG_IPV6_SUBTREES | 711 | #ifdef CONFIG_IPV6_SUBTREES |
444 | if (rt->rt6i_src.plen) { | 712 | if (rt->rt6i_src.plen) { |
445 | struct fib6_node *sn; | 713 | struct fib6_node *sn; |
@@ -485,10 +753,6 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, | |||
485 | /* Now link new subtree to main tree */ | 753 | /* Now link new subtree to main tree */ |
486 | sfn->parent = fn; | 754 | sfn->parent = fn; |
487 | fn->subtree = sfn; | 755 | fn->subtree = sfn; |
488 | if (fn->leaf == NULL) { | ||
489 | fn->leaf = rt; | ||
490 | atomic_inc(&rt->rt6i_ref); | ||
491 | } | ||
492 | } else { | 756 | } else { |
493 | sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr, | 757 | sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr, |
494 | sizeof(struct in6_addr), rt->rt6i_src.plen, | 758 | sizeof(struct in6_addr), rt->rt6i_src.plen, |
@@ -498,21 +762,42 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, | |||
498 | goto st_failure; | 762 | goto st_failure; |
499 | } | 763 | } |
500 | 764 | ||
765 | if (fn->leaf == NULL) { | ||
766 | fn->leaf = rt; | ||
767 | atomic_inc(&rt->rt6i_ref); | ||
768 | } | ||
501 | fn = sn; | 769 | fn = sn; |
502 | } | 770 | } |
503 | #endif | 771 | #endif |
504 | 772 | ||
505 | err = fib6_add_rt2node(fn, rt, nlh, req); | 773 | err = fib6_add_rt2node(fn, rt, info); |
506 | 774 | ||
507 | if (err == 0) { | 775 | if (err == 0) { |
508 | fib6_start_gc(rt); | 776 | fib6_start_gc(rt); |
509 | if (!(rt->rt6i_flags&RTF_CACHE)) | 777 | if (!(rt->rt6i_flags&RTF_CACHE)) |
510 | fib6_prune_clones(fn, rt); | 778 | fib6_prune_clones(pn, rt); |
511 | } | 779 | } |
512 | 780 | ||
513 | out: | 781 | out: |
514 | if (err) | 782 | if (err) { |
783 | #ifdef CONFIG_IPV6_SUBTREES | ||
784 | /* | ||
785 | * If fib6_add_1 has cleared the old leaf pointer in the | ||
786 | * super-tree leaf node we have to find a new one for it. | ||
787 | */ | ||
788 | if (pn != fn && !pn->leaf && !(pn->fn_flags & RTN_RTINFO)) { | ||
789 | pn->leaf = fib6_find_prefix(pn); | ||
790 | #if RT6_DEBUG >= 2 | ||
791 | if (!pn->leaf) { | ||
792 | BUG_TRAP(pn->leaf != NULL); | ||
793 | pn->leaf = &ip6_null_entry; | ||
794 | } | ||
795 | #endif | ||
796 | atomic_inc(&pn->leaf->rt6i_ref); | ||
797 | } | ||
798 | #endif | ||
515 | dst_free(&rt->u.dst); | 799 | dst_free(&rt->u.dst); |
800 | } | ||
516 | return err; | 801 | return err; |
517 | 802 | ||
518 | #ifdef CONFIG_IPV6_SUBTREES | 803 | #ifdef CONFIG_IPV6_SUBTREES |
@@ -543,6 +828,9 @@ static struct fib6_node * fib6_lookup_1(struct fib6_node *root, | |||
543 | struct fib6_node *fn; | 828 | struct fib6_node *fn; |
544 | int dir; | 829 | int dir; |
545 | 830 | ||
831 | if (unlikely(args->offset == 0)) | ||
832 | return NULL; | ||
833 | |||
546 | /* | 834 | /* |
547 | * Descend on a tree | 835 | * Descend on a tree |
548 | */ | 836 | */ |
@@ -564,33 +852,26 @@ static struct fib6_node * fib6_lookup_1(struct fib6_node *root, | |||
564 | break; | 852 | break; |
565 | } | 853 | } |
566 | 854 | ||
567 | while ((fn->fn_flags & RTN_ROOT) == 0) { | 855 | while(fn) { |
568 | #ifdef CONFIG_IPV6_SUBTREES | 856 | if (FIB6_SUBTREE(fn) || fn->fn_flags & RTN_RTINFO) { |
569 | if (fn->subtree) { | ||
570 | struct fib6_node *st; | ||
571 | struct lookup_args *narg; | ||
572 | |||
573 | narg = args + 1; | ||
574 | |||
575 | if (narg->addr) { | ||
576 | st = fib6_lookup_1(fn->subtree, narg); | ||
577 | |||
578 | if (st && !(st->fn_flags & RTN_ROOT)) | ||
579 | return st; | ||
580 | } | ||
581 | } | ||
582 | #endif | ||
583 | |||
584 | if (fn->fn_flags & RTN_RTINFO) { | ||
585 | struct rt6key *key; | 857 | struct rt6key *key; |
586 | 858 | ||
587 | key = (struct rt6key *) ((u8 *) fn->leaf + | 859 | key = (struct rt6key *) ((u8 *) fn->leaf + |
588 | args->offset); | 860 | args->offset); |
589 | 861 | ||
590 | if (ipv6_prefix_equal(&key->addr, args->addr, key->plen)) | 862 | if (ipv6_prefix_equal(&key->addr, args->addr, key->plen)) { |
591 | return fn; | 863 | #ifdef CONFIG_IPV6_SUBTREES |
864 | if (fn->subtree) | ||
865 | fn = fib6_lookup_1(fn->subtree, args + 1); | ||
866 | #endif | ||
867 | if (!fn || fn->fn_flags & RTN_RTINFO) | ||
868 | return fn; | ||
869 | } | ||
592 | } | 870 | } |
593 | 871 | ||
872 | if (fn->fn_flags & RTN_ROOT) | ||
873 | break; | ||
874 | |||
594 | fn = fn->parent; | 875 | fn = fn->parent; |
595 | } | 876 | } |
596 | 877 | ||
@@ -600,18 +881,24 @@ static struct fib6_node * fib6_lookup_1(struct fib6_node *root, | |||
600 | struct fib6_node * fib6_lookup(struct fib6_node *root, struct in6_addr *daddr, | 881 | struct fib6_node * fib6_lookup(struct fib6_node *root, struct in6_addr *daddr, |
601 | struct in6_addr *saddr) | 882 | struct in6_addr *saddr) |
602 | { | 883 | { |
603 | struct lookup_args args[2]; | ||
604 | struct fib6_node *fn; | 884 | struct fib6_node *fn; |
605 | 885 | struct lookup_args args[] = { | |
606 | args[0].offset = offsetof(struct rt6_info, rt6i_dst); | 886 | { |
607 | args[0].addr = daddr; | 887 | .offset = offsetof(struct rt6_info, rt6i_dst), |
608 | 888 | .addr = daddr, | |
889 | }, | ||
609 | #ifdef CONFIG_IPV6_SUBTREES | 890 | #ifdef CONFIG_IPV6_SUBTREES |
610 | args[1].offset = offsetof(struct rt6_info, rt6i_src); | 891 | { |
611 | args[1].addr = saddr; | 892 | .offset = offsetof(struct rt6_info, rt6i_src), |
893 | .addr = saddr, | ||
894 | }, | ||
612 | #endif | 895 | #endif |
896 | { | ||
897 | .offset = 0, /* sentinel */ | ||
898 | } | ||
899 | }; | ||
613 | 900 | ||
614 | fn = fib6_lookup_1(root, args); | 901 | fn = fib6_lookup_1(root, daddr ? args : args + 1); |
615 | 902 | ||
616 | if (fn == NULL || fn->fn_flags & RTN_TL_ROOT) | 903 | if (fn == NULL || fn->fn_flags & RTN_TL_ROOT) |
617 | fn = root; | 904 | fn = root; |
@@ -667,10 +954,8 @@ struct fib6_node * fib6_locate(struct fib6_node *root, | |||
667 | #ifdef CONFIG_IPV6_SUBTREES | 954 | #ifdef CONFIG_IPV6_SUBTREES |
668 | if (src_len) { | 955 | if (src_len) { |
669 | BUG_TRAP(saddr!=NULL); | 956 | BUG_TRAP(saddr!=NULL); |
670 | if (fn == NULL) | 957 | if (fn && fn->subtree) |
671 | fn = fn->subtree; | 958 | fn = fib6_locate_1(fn->subtree, saddr, src_len, |
672 | if (fn) | ||
673 | fn = fib6_locate_1(fn, saddr, src_len, | ||
674 | offsetof(struct rt6_info, rt6i_src)); | 959 | offsetof(struct rt6_info, rt6i_src)); |
675 | } | 960 | } |
676 | #endif | 961 | #endif |
@@ -699,7 +984,7 @@ static struct rt6_info * fib6_find_prefix(struct fib6_node *fn) | |||
699 | if(fn->right) | 984 | if(fn->right) |
700 | return fn->right->leaf; | 985 | return fn->right->leaf; |
701 | 986 | ||
702 | fn = SUBTREE(fn); | 987 | fn = FIB6_SUBTREE(fn); |
703 | } | 988 | } |
704 | return NULL; | 989 | return NULL; |
705 | } | 990 | } |
@@ -730,7 +1015,7 @@ static struct fib6_node * fib6_repair_tree(struct fib6_node *fn) | |||
730 | if (fn->right) child = fn->right, children |= 1; | 1015 | if (fn->right) child = fn->right, children |= 1; |
731 | if (fn->left) child = fn->left, children |= 2; | 1016 | if (fn->left) child = fn->left, children |= 2; |
732 | 1017 | ||
733 | if (children == 3 || SUBTREE(fn) | 1018 | if (children == 3 || FIB6_SUBTREE(fn) |
734 | #ifdef CONFIG_IPV6_SUBTREES | 1019 | #ifdef CONFIG_IPV6_SUBTREES |
735 | /* Subtree root (i.e. fn) may have one child */ | 1020 | /* Subtree root (i.e. fn) may have one child */ |
736 | || (children && fn->fn_flags&RTN_ROOT) | 1021 | || (children && fn->fn_flags&RTN_ROOT) |
@@ -749,9 +1034,9 @@ static struct fib6_node * fib6_repair_tree(struct fib6_node *fn) | |||
749 | 1034 | ||
750 | pn = fn->parent; | 1035 | pn = fn->parent; |
751 | #ifdef CONFIG_IPV6_SUBTREES | 1036 | #ifdef CONFIG_IPV6_SUBTREES |
752 | if (SUBTREE(pn) == fn) { | 1037 | if (FIB6_SUBTREE(pn) == fn) { |
753 | BUG_TRAP(fn->fn_flags&RTN_ROOT); | 1038 | BUG_TRAP(fn->fn_flags&RTN_ROOT); |
754 | SUBTREE(pn) = NULL; | 1039 | FIB6_SUBTREE(pn) = NULL; |
755 | nstate = FWS_L; | 1040 | nstate = FWS_L; |
756 | } else { | 1041 | } else { |
757 | BUG_TRAP(!(fn->fn_flags&RTN_ROOT)); | 1042 | BUG_TRAP(!(fn->fn_flags&RTN_ROOT)); |
@@ -799,7 +1084,7 @@ static struct fib6_node * fib6_repair_tree(struct fib6_node *fn) | |||
799 | read_unlock(&fib6_walker_lock); | 1084 | read_unlock(&fib6_walker_lock); |
800 | 1085 | ||
801 | node_free(fn); | 1086 | node_free(fn); |
802 | if (pn->fn_flags&RTN_RTINFO || SUBTREE(pn)) | 1087 | if (pn->fn_flags&RTN_RTINFO || FIB6_SUBTREE(pn)) |
803 | return pn; | 1088 | return pn; |
804 | 1089 | ||
805 | rt6_release(pn->leaf); | 1090 | rt6_release(pn->leaf); |
@@ -809,7 +1094,7 @@ static struct fib6_node * fib6_repair_tree(struct fib6_node *fn) | |||
809 | } | 1094 | } |
810 | 1095 | ||
811 | static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp, | 1096 | static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp, |
812 | struct nlmsghdr *nlh, void *_rtattr, struct netlink_skb_parms *req) | 1097 | struct nl_info *info) |
813 | { | 1098 | { |
814 | struct fib6_walker_t *w; | 1099 | struct fib6_walker_t *w; |
815 | struct rt6_info *rt = *rtp; | 1100 | struct rt6_info *rt = *rtp; |
@@ -865,11 +1150,11 @@ static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp, | |||
865 | if (atomic_read(&rt->rt6i_ref) != 1) BUG(); | 1150 | if (atomic_read(&rt->rt6i_ref) != 1) BUG(); |
866 | } | 1151 | } |
867 | 1152 | ||
868 | inet6_rt_notify(RTM_DELROUTE, rt, nlh, req); | 1153 | inet6_rt_notify(RTM_DELROUTE, rt, info); |
869 | rt6_release(rt); | 1154 | rt6_release(rt); |
870 | } | 1155 | } |
871 | 1156 | ||
872 | int fib6_del(struct rt6_info *rt, struct nlmsghdr *nlh, void *_rtattr, struct netlink_skb_parms *req) | 1157 | int fib6_del(struct rt6_info *rt, struct nl_info *info) |
873 | { | 1158 | { |
874 | struct fib6_node *fn = rt->rt6i_node; | 1159 | struct fib6_node *fn = rt->rt6i_node; |
875 | struct rt6_info **rtp; | 1160 | struct rt6_info **rtp; |
@@ -885,8 +1170,18 @@ int fib6_del(struct rt6_info *rt, struct nlmsghdr *nlh, void *_rtattr, struct ne | |||
885 | 1170 | ||
886 | BUG_TRAP(fn->fn_flags&RTN_RTINFO); | 1171 | BUG_TRAP(fn->fn_flags&RTN_RTINFO); |
887 | 1172 | ||
888 | if (!(rt->rt6i_flags&RTF_CACHE)) | 1173 | if (!(rt->rt6i_flags&RTF_CACHE)) { |
889 | fib6_prune_clones(fn, rt); | 1174 | struct fib6_node *pn = fn; |
1175 | #ifdef CONFIG_IPV6_SUBTREES | ||
1176 | /* clones of this route might be in another subtree */ | ||
1177 | if (rt->rt6i_src.plen) { | ||
1178 | while (!(pn->fn_flags&RTN_ROOT)) | ||
1179 | pn = pn->parent; | ||
1180 | pn = pn->parent; | ||
1181 | } | ||
1182 | #endif | ||
1183 | fib6_prune_clones(pn, rt); | ||
1184 | } | ||
890 | 1185 | ||
891 | /* | 1186 | /* |
892 | * Walk the leaf entries looking for ourself | 1187 | * Walk the leaf entries looking for ourself |
@@ -894,7 +1189,7 @@ int fib6_del(struct rt6_info *rt, struct nlmsghdr *nlh, void *_rtattr, struct ne | |||
894 | 1189 | ||
895 | for (rtp = &fn->leaf; *rtp; rtp = &(*rtp)->u.next) { | 1190 | for (rtp = &fn->leaf; *rtp; rtp = &(*rtp)->u.next) { |
896 | if (*rtp == rt) { | 1191 | if (*rtp == rt) { |
897 | fib6_del_route(fn, rtp, nlh, _rtattr, req); | 1192 | fib6_del_route(fn, rtp, info); |
898 | return 0; | 1193 | return 0; |
899 | } | 1194 | } |
900 | } | 1195 | } |
@@ -925,7 +1220,7 @@ int fib6_del(struct rt6_info *rt, struct nlmsghdr *nlh, void *_rtattr, struct ne | |||
925 | * <0 -> walk is terminated by an error. | 1220 | * <0 -> walk is terminated by an error. |
926 | */ | 1221 | */ |
927 | 1222 | ||
928 | int fib6_walk_continue(struct fib6_walker_t *w) | 1223 | static int fib6_walk_continue(struct fib6_walker_t *w) |
929 | { | 1224 | { |
930 | struct fib6_node *fn, *pn; | 1225 | struct fib6_node *fn, *pn; |
931 | 1226 | ||
@@ -942,8 +1237,8 @@ int fib6_walk_continue(struct fib6_walker_t *w) | |||
942 | switch (w->state) { | 1237 | switch (w->state) { |
943 | #ifdef CONFIG_IPV6_SUBTREES | 1238 | #ifdef CONFIG_IPV6_SUBTREES |
944 | case FWS_S: | 1239 | case FWS_S: |
945 | if (SUBTREE(fn)) { | 1240 | if (FIB6_SUBTREE(fn)) { |
946 | w->node = SUBTREE(fn); | 1241 | w->node = FIB6_SUBTREE(fn); |
947 | continue; | 1242 | continue; |
948 | } | 1243 | } |
949 | w->state = FWS_L; | 1244 | w->state = FWS_L; |
@@ -977,7 +1272,7 @@ int fib6_walk_continue(struct fib6_walker_t *w) | |||
977 | pn = fn->parent; | 1272 | pn = fn->parent; |
978 | w->node = pn; | 1273 | w->node = pn; |
979 | #ifdef CONFIG_IPV6_SUBTREES | 1274 | #ifdef CONFIG_IPV6_SUBTREES |
980 | if (SUBTREE(pn) == fn) { | 1275 | if (FIB6_SUBTREE(pn) == fn) { |
981 | BUG_TRAP(fn->fn_flags&RTN_ROOT); | 1276 | BUG_TRAP(fn->fn_flags&RTN_ROOT); |
982 | w->state = FWS_L; | 1277 | w->state = FWS_L; |
983 | continue; | 1278 | continue; |
@@ -999,7 +1294,7 @@ int fib6_walk_continue(struct fib6_walker_t *w) | |||
999 | } | 1294 | } |
1000 | } | 1295 | } |
1001 | 1296 | ||
1002 | int fib6_walk(struct fib6_walker_t *w) | 1297 | static int fib6_walk(struct fib6_walker_t *w) |
1003 | { | 1298 | { |
1004 | int res; | 1299 | int res; |
1005 | 1300 | ||
@@ -1023,7 +1318,7 @@ static int fib6_clean_node(struct fib6_walker_t *w) | |||
1023 | res = c->func(rt, c->arg); | 1318 | res = c->func(rt, c->arg); |
1024 | if (res < 0) { | 1319 | if (res < 0) { |
1025 | w->leaf = rt; | 1320 | w->leaf = rt; |
1026 | res = fib6_del(rt, NULL, NULL, NULL); | 1321 | res = fib6_del(rt, NULL); |
1027 | if (res) { | 1322 | if (res) { |
1028 | #if RT6_DEBUG >= 2 | 1323 | #if RT6_DEBUG >= 2 |
1029 | printk(KERN_DEBUG "fib6_clean_node: del failed: rt=%p@%p err=%d\n", rt, rt->rt6i_node, res); | 1324 | printk(KERN_DEBUG "fib6_clean_node: del failed: rt=%p@%p err=%d\n", rt, rt->rt6i_node, res); |
@@ -1049,9 +1344,9 @@ static int fib6_clean_node(struct fib6_walker_t *w) | |||
1049 | * ignoring pure split nodes) will be scanned. | 1344 | * ignoring pure split nodes) will be scanned. |
1050 | */ | 1345 | */ |
1051 | 1346 | ||
1052 | void fib6_clean_tree(struct fib6_node *root, | 1347 | static void fib6_clean_tree(struct fib6_node *root, |
1053 | int (*func)(struct rt6_info *, void *arg), | 1348 | int (*func)(struct rt6_info *, void *arg), |
1054 | int prune, void *arg) | 1349 | int prune, void *arg) |
1055 | { | 1350 | { |
1056 | struct fib6_cleaner_t c; | 1351 | struct fib6_cleaner_t c; |
1057 | 1352 | ||
@@ -1064,6 +1359,25 @@ void fib6_clean_tree(struct fib6_node *root, | |||
1064 | fib6_walk(&c.w); | 1359 | fib6_walk(&c.w); |
1065 | } | 1360 | } |
1066 | 1361 | ||
1362 | void fib6_clean_all(int (*func)(struct rt6_info *, void *arg), | ||
1363 | int prune, void *arg) | ||
1364 | { | ||
1365 | struct fib6_table *table; | ||
1366 | struct hlist_node *node; | ||
1367 | unsigned int h; | ||
1368 | |||
1369 | rcu_read_lock(); | ||
1370 | for (h = 0; h < FIB_TABLE_HASHSZ; h++) { | ||
1371 | hlist_for_each_entry_rcu(table, node, &fib_table_hash[h], | ||
1372 | tb6_hlist) { | ||
1373 | write_lock_bh(&table->tb6_lock); | ||
1374 | fib6_clean_tree(&table->tb6_root, func, prune, arg); | ||
1375 | write_unlock_bh(&table->tb6_lock); | ||
1376 | } | ||
1377 | } | ||
1378 | rcu_read_unlock(); | ||
1379 | } | ||
1380 | |||
1067 | static int fib6_prune_clone(struct rt6_info *rt, void *arg) | 1381 | static int fib6_prune_clone(struct rt6_info *rt, void *arg) |
1068 | { | 1382 | { |
1069 | if (rt->rt6i_flags & RTF_CACHE) { | 1383 | if (rt->rt6i_flags & RTF_CACHE) { |
@@ -1142,11 +1456,8 @@ void fib6_run_gc(unsigned long dummy) | |||
1142 | } | 1456 | } |
1143 | gc_args.more = 0; | 1457 | gc_args.more = 0; |
1144 | 1458 | ||
1145 | |||
1146 | write_lock_bh(&rt6_lock); | ||
1147 | ndisc_dst_gc(&gc_args.more); | 1459 | ndisc_dst_gc(&gc_args.more); |
1148 | fib6_clean_tree(&ip6_routing_table, fib6_age, 0, NULL); | 1460 | fib6_clean_all(fib6_age, 0, NULL); |
1149 | write_unlock_bh(&rt6_lock); | ||
1150 | 1461 | ||
1151 | if (gc_args.more) | 1462 | if (gc_args.more) |
1152 | mod_timer(&ip6_fib_timer, jiffies + ip6_rt_gc_interval); | 1463 | mod_timer(&ip6_fib_timer, jiffies + ip6_rt_gc_interval); |
@@ -1161,10 +1472,10 @@ void __init fib6_init(void) | |||
1161 | { | 1472 | { |
1162 | fib6_node_kmem = kmem_cache_create("fib6_nodes", | 1473 | fib6_node_kmem = kmem_cache_create("fib6_nodes", |
1163 | sizeof(struct fib6_node), | 1474 | sizeof(struct fib6_node), |
1164 | 0, SLAB_HWCACHE_ALIGN, | 1475 | 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, |
1165 | NULL, NULL); | 1476 | NULL, NULL); |
1166 | if (!fib6_node_kmem) | 1477 | |
1167 | panic("cannot create fib6_nodes cache"); | 1478 | fib6_tables_init(); |
1168 | } | 1479 | } |
1169 | 1480 | ||
1170 | void fib6_gc_cleanup(void) | 1481 | void fib6_gc_cleanup(void) |