diff options
Diffstat (limited to 'net/ipv6/ip6_fib.c')
-rw-r--r-- | net/ipv6/ip6_fib.c | 294 |
1 files changed, 188 insertions, 106 deletions
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index bab72b6f1444..b3f6e03c454c 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c | |||
@@ -48,8 +48,6 @@ | |||
48 | #define RT6_TRACE(x...) do { ; } while (0) | 48 | #define RT6_TRACE(x...) do { ; } while (0) |
49 | #endif | 49 | #endif |
50 | 50 | ||
51 | struct rt6_statistics rt6_stats; | ||
52 | |||
53 | static struct kmem_cache * fib6_node_kmem __read_mostly; | 51 | static struct kmem_cache * fib6_node_kmem __read_mostly; |
54 | 52 | ||
55 | enum fib_walk_state_t | 53 | enum fib_walk_state_t |
@@ -66,6 +64,7 @@ enum fib_walk_state_t | |||
66 | struct fib6_cleaner_t | 64 | struct fib6_cleaner_t |
67 | { | 65 | { |
68 | struct fib6_walker_t w; | 66 | struct fib6_walker_t w; |
67 | struct net *net; | ||
69 | int (*func)(struct rt6_info *, void *arg); | 68 | int (*func)(struct rt6_info *, void *arg); |
70 | void *arg; | 69 | void *arg; |
71 | }; | 70 | }; |
@@ -78,9 +77,10 @@ static DEFINE_RWLOCK(fib6_walker_lock); | |||
78 | #define FWS_INIT FWS_L | 77 | #define FWS_INIT FWS_L |
79 | #endif | 78 | #endif |
80 | 79 | ||
81 | static void fib6_prune_clones(struct fib6_node *fn, struct rt6_info *rt); | 80 | static void fib6_prune_clones(struct net *net, struct fib6_node *fn, |
82 | static struct rt6_info * fib6_find_prefix(struct fib6_node *fn); | 81 | struct rt6_info *rt); |
83 | static struct fib6_node * fib6_repair_tree(struct fib6_node *fn); | 82 | static struct rt6_info *fib6_find_prefix(struct net *net, struct fib6_node *fn); |
83 | static struct fib6_node *fib6_repair_tree(struct net *net, struct fib6_node *fn); | ||
84 | static int fib6_walk(struct fib6_walker_t *w); | 84 | static int fib6_walk(struct fib6_walker_t *w); |
85 | static int fib6_walk_continue(struct fib6_walker_t *w); | 85 | static int fib6_walk_continue(struct fib6_walker_t *w); |
86 | 86 | ||
@@ -93,7 +93,7 @@ static int fib6_walk_continue(struct fib6_walker_t *w); | |||
93 | 93 | ||
94 | static __u32 rt_sernum; | 94 | static __u32 rt_sernum; |
95 | 95 | ||
96 | static DEFINE_TIMER(ip6_fib_timer, fib6_run_gc, 0, 0); | 96 | static void fib6_gc_timer_cb(unsigned long arg); |
97 | 97 | ||
98 | static struct fib6_walker_t fib6_walker_list = { | 98 | static struct fib6_walker_t fib6_walker_list = { |
99 | .prev = &fib6_walker_list, | 99 | .prev = &fib6_walker_list, |
@@ -166,22 +166,13 @@ static __inline__ void rt6_release(struct rt6_info *rt) | |||
166 | dst_free(&rt->u.dst); | 166 | dst_free(&rt->u.dst); |
167 | } | 167 | } |
168 | 168 | ||
169 | static struct fib6_table fib6_main_tbl = { | ||
170 | .tb6_id = RT6_TABLE_MAIN, | ||
171 | .tb6_root = { | ||
172 | .leaf = &ip6_null_entry, | ||
173 | .fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO, | ||
174 | }, | ||
175 | }; | ||
176 | |||
177 | #ifdef CONFIG_IPV6_MULTIPLE_TABLES | 169 | #ifdef CONFIG_IPV6_MULTIPLE_TABLES |
178 | #define FIB_TABLE_HASHSZ 256 | 170 | #define FIB_TABLE_HASHSZ 256 |
179 | #else | 171 | #else |
180 | #define FIB_TABLE_HASHSZ 1 | 172 | #define FIB_TABLE_HASHSZ 1 |
181 | #endif | 173 | #endif |
182 | static struct hlist_head fib_table_hash[FIB_TABLE_HASHSZ]; | ||
183 | 174 | ||
184 | static void fib6_link_table(struct fib6_table *tb) | 175 | static void fib6_link_table(struct net *net, struct fib6_table *tb) |
185 | { | 176 | { |
186 | unsigned int h; | 177 | unsigned int h; |
187 | 178 | ||
@@ -197,52 +188,46 @@ static void fib6_link_table(struct fib6_table *tb) | |||
197 | * No protection necessary, this is the only list mutatation | 188 | * No protection necessary, this is the only list mutatation |
198 | * operation, tables never disappear once they exist. | 189 | * operation, tables never disappear once they exist. |
199 | */ | 190 | */ |
200 | hlist_add_head_rcu(&tb->tb6_hlist, &fib_table_hash[h]); | 191 | hlist_add_head_rcu(&tb->tb6_hlist, &net->ipv6.fib_table_hash[h]); |
201 | } | 192 | } |
202 | 193 | ||
203 | #ifdef CONFIG_IPV6_MULTIPLE_TABLES | 194 | #ifdef CONFIG_IPV6_MULTIPLE_TABLES |
204 | static struct fib6_table fib6_local_tbl = { | ||
205 | .tb6_id = RT6_TABLE_LOCAL, | ||
206 | .tb6_root = { | ||
207 | .leaf = &ip6_null_entry, | ||
208 | .fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO, | ||
209 | }, | ||
210 | }; | ||
211 | 195 | ||
212 | static struct fib6_table *fib6_alloc_table(u32 id) | 196 | static struct fib6_table *fib6_alloc_table(struct net *net, u32 id) |
213 | { | 197 | { |
214 | struct fib6_table *table; | 198 | struct fib6_table *table; |
215 | 199 | ||
216 | table = kzalloc(sizeof(*table), GFP_ATOMIC); | 200 | table = kzalloc(sizeof(*table), GFP_ATOMIC); |
217 | if (table != NULL) { | 201 | if (table != NULL) { |
218 | table->tb6_id = id; | 202 | table->tb6_id = id; |
219 | table->tb6_root.leaf = &ip6_null_entry; | 203 | table->tb6_root.leaf = net->ipv6.ip6_null_entry; |
220 | table->tb6_root.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO; | 204 | table->tb6_root.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO; |
221 | } | 205 | } |
222 | 206 | ||
223 | return table; | 207 | return table; |
224 | } | 208 | } |
225 | 209 | ||
226 | struct fib6_table *fib6_new_table(u32 id) | 210 | struct fib6_table *fib6_new_table(struct net *net, u32 id) |
227 | { | 211 | { |
228 | struct fib6_table *tb; | 212 | struct fib6_table *tb; |
229 | 213 | ||
230 | if (id == 0) | 214 | if (id == 0) |
231 | id = RT6_TABLE_MAIN; | 215 | id = RT6_TABLE_MAIN; |
232 | tb = fib6_get_table(id); | 216 | tb = fib6_get_table(net, id); |
233 | if (tb) | 217 | if (tb) |
234 | return tb; | 218 | return tb; |
235 | 219 | ||
236 | tb = fib6_alloc_table(id); | 220 | tb = fib6_alloc_table(net, id); |
237 | if (tb != NULL) | 221 | if (tb != NULL) |
238 | fib6_link_table(tb); | 222 | fib6_link_table(net, tb); |
239 | 223 | ||
240 | return tb; | 224 | return tb; |
241 | } | 225 | } |
242 | 226 | ||
243 | struct fib6_table *fib6_get_table(u32 id) | 227 | struct fib6_table *fib6_get_table(struct net *net, u32 id) |
244 | { | 228 | { |
245 | struct fib6_table *tb; | 229 | struct fib6_table *tb; |
230 | struct hlist_head *head; | ||
246 | struct hlist_node *node; | 231 | struct hlist_node *node; |
247 | unsigned int h; | 232 | unsigned int h; |
248 | 233 | ||
@@ -250,7 +235,8 @@ struct fib6_table *fib6_get_table(u32 id) | |||
250 | id = RT6_TABLE_MAIN; | 235 | id = RT6_TABLE_MAIN; |
251 | h = id & (FIB_TABLE_HASHSZ - 1); | 236 | h = id & (FIB_TABLE_HASHSZ - 1); |
252 | rcu_read_lock(); | 237 | rcu_read_lock(); |
253 | hlist_for_each_entry_rcu(tb, node, &fib_table_hash[h], tb6_hlist) { | 238 | head = &net->ipv6.fib_table_hash[h]; |
239 | hlist_for_each_entry_rcu(tb, node, head, tb6_hlist) { | ||
254 | if (tb->tb6_id == id) { | 240 | if (tb->tb6_id == id) { |
255 | rcu_read_unlock(); | 241 | rcu_read_unlock(); |
256 | return tb; | 242 | return tb; |
@@ -261,33 +247,32 @@ struct fib6_table *fib6_get_table(u32 id) | |||
261 | return NULL; | 247 | return NULL; |
262 | } | 248 | } |
263 | 249 | ||
264 | static void __init fib6_tables_init(void) | 250 | static void fib6_tables_init(struct net *net) |
265 | { | 251 | { |
266 | fib6_link_table(&fib6_main_tbl); | 252 | fib6_link_table(net, net->ipv6.fib6_main_tbl); |
267 | fib6_link_table(&fib6_local_tbl); | 253 | fib6_link_table(net, net->ipv6.fib6_local_tbl); |
268 | } | 254 | } |
269 | |||
270 | #else | 255 | #else |
271 | 256 | ||
272 | struct fib6_table *fib6_new_table(u32 id) | 257 | struct fib6_table *fib6_new_table(struct net *net, u32 id) |
273 | { | 258 | { |
274 | return fib6_get_table(id); | 259 | return fib6_get_table(net, id); |
275 | } | 260 | } |
276 | 261 | ||
277 | struct fib6_table *fib6_get_table(u32 id) | 262 | struct fib6_table *fib6_get_table(struct net *net, u32 id) |
278 | { | 263 | { |
279 | return &fib6_main_tbl; | 264 | return net->ipv6.fib6_main_tbl; |
280 | } | 265 | } |
281 | 266 | ||
282 | struct dst_entry *fib6_rule_lookup(struct flowi *fl, int flags, | 267 | struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi *fl, |
283 | pol_lookup_t lookup) | 268 | int flags, pol_lookup_t lookup) |
284 | { | 269 | { |
285 | return (struct dst_entry *) lookup(&fib6_main_tbl, fl, flags); | 270 | return (struct dst_entry *) lookup(net, net->ipv6.fib6_main_tbl, fl, flags); |
286 | } | 271 | } |
287 | 272 | ||
288 | static void __init fib6_tables_init(void) | 273 | static void fib6_tables_init(struct net *net) |
289 | { | 274 | { |
290 | fib6_link_table(&fib6_main_tbl); | 275 | fib6_link_table(net, net->ipv6.fib6_main_tbl); |
291 | } | 276 | } |
292 | 277 | ||
293 | #endif | 278 | #endif |
@@ -361,18 +346,16 @@ end: | |||
361 | 346 | ||
362 | static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) | 347 | static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) |
363 | { | 348 | { |
364 | struct net *net = skb->sk->sk_net; | 349 | struct net *net = sock_net(skb->sk); |
365 | unsigned int h, s_h; | 350 | unsigned int h, s_h; |
366 | unsigned int e = 0, s_e; | 351 | unsigned int e = 0, s_e; |
367 | struct rt6_rtnl_dump_arg arg; | 352 | struct rt6_rtnl_dump_arg arg; |
368 | struct fib6_walker_t *w; | 353 | struct fib6_walker_t *w; |
369 | struct fib6_table *tb; | 354 | struct fib6_table *tb; |
370 | struct hlist_node *node; | 355 | struct hlist_node *node; |
356 | struct hlist_head *head; | ||
371 | int res = 0; | 357 | int res = 0; |
372 | 358 | ||
373 | if (net != &init_net) | ||
374 | return 0; | ||
375 | |||
376 | s_h = cb->args[0]; | 359 | s_h = cb->args[0]; |
377 | s_e = cb->args[1]; | 360 | s_e = cb->args[1]; |
378 | 361 | ||
@@ -401,7 +384,8 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) | |||
401 | 384 | ||
402 | for (h = s_h; h < FIB_TABLE_HASHSZ; h++, s_e = 0) { | 385 | for (h = s_h; h < FIB_TABLE_HASHSZ; h++, s_e = 0) { |
403 | e = 0; | 386 | e = 0; |
404 | hlist_for_each_entry(tb, node, &fib_table_hash[h], tb6_hlist) { | 387 | head = &net->ipv6.fib_table_hash[h]; |
388 | hlist_for_each_entry(tb, node, head, tb6_hlist) { | ||
405 | if (e < s_e) | 389 | if (e < s_e) |
406 | goto next; | 390 | goto next; |
407 | res = fib6_dump_table(tb, skb, cb); | 391 | res = fib6_dump_table(tb, skb, cb); |
@@ -667,29 +651,29 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, | |||
667 | rt->rt6i_node = fn; | 651 | rt->rt6i_node = fn; |
668 | atomic_inc(&rt->rt6i_ref); | 652 | atomic_inc(&rt->rt6i_ref); |
669 | inet6_rt_notify(RTM_NEWROUTE, rt, info); | 653 | inet6_rt_notify(RTM_NEWROUTE, rt, info); |
670 | rt6_stats.fib_rt_entries++; | 654 | info->nl_net->ipv6.rt6_stats->fib_rt_entries++; |
671 | 655 | ||
672 | if ((fn->fn_flags & RTN_RTINFO) == 0) { | 656 | if ((fn->fn_flags & RTN_RTINFO) == 0) { |
673 | rt6_stats.fib_route_nodes++; | 657 | info->nl_net->ipv6.rt6_stats->fib_route_nodes++; |
674 | fn->fn_flags |= RTN_RTINFO; | 658 | fn->fn_flags |= RTN_RTINFO; |
675 | } | 659 | } |
676 | 660 | ||
677 | return 0; | 661 | return 0; |
678 | } | 662 | } |
679 | 663 | ||
680 | static __inline__ void fib6_start_gc(struct rt6_info *rt) | 664 | static __inline__ void fib6_start_gc(struct net *net, struct rt6_info *rt) |
681 | { | 665 | { |
682 | if (ip6_fib_timer.expires == 0 && | 666 | if (net->ipv6.ip6_fib_timer->expires == 0 && |
683 | (rt->rt6i_flags & (RTF_EXPIRES|RTF_CACHE))) | 667 | (rt->rt6i_flags & (RTF_EXPIRES|RTF_CACHE))) |
684 | mod_timer(&ip6_fib_timer, jiffies + | 668 | mod_timer(net->ipv6.ip6_fib_timer, jiffies + |
685 | init_net.ipv6.sysctl.ip6_rt_gc_interval); | 669 | net->ipv6.sysctl.ip6_rt_gc_interval); |
686 | } | 670 | } |
687 | 671 | ||
688 | void fib6_force_start_gc(void) | 672 | void fib6_force_start_gc(struct net *net) |
689 | { | 673 | { |
690 | if (ip6_fib_timer.expires == 0) | 674 | if (net->ipv6.ip6_fib_timer->expires == 0) |
691 | mod_timer(&ip6_fib_timer, jiffies + | 675 | mod_timer(net->ipv6.ip6_fib_timer, jiffies + |
692 | init_net.ipv6.sysctl.ip6_rt_gc_interval); | 676 | net->ipv6.sysctl.ip6_rt_gc_interval); |
693 | } | 677 | } |
694 | 678 | ||
695 | /* | 679 | /* |
@@ -733,8 +717,8 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info) | |||
733 | if (sfn == NULL) | 717 | if (sfn == NULL) |
734 | goto st_failure; | 718 | goto st_failure; |
735 | 719 | ||
736 | sfn->leaf = &ip6_null_entry; | 720 | sfn->leaf = info->nl_net->ipv6.ip6_null_entry; |
737 | atomic_inc(&ip6_null_entry.rt6i_ref); | 721 | atomic_inc(&info->nl_net->ipv6.ip6_null_entry->rt6i_ref); |
738 | sfn->fn_flags = RTN_ROOT; | 722 | sfn->fn_flags = RTN_ROOT; |
739 | sfn->fn_sernum = fib6_new_sernum(); | 723 | sfn->fn_sernum = fib6_new_sernum(); |
740 | 724 | ||
@@ -776,9 +760,9 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info) | |||
776 | err = fib6_add_rt2node(fn, rt, info); | 760 | err = fib6_add_rt2node(fn, rt, info); |
777 | 761 | ||
778 | if (err == 0) { | 762 | if (err == 0) { |
779 | fib6_start_gc(rt); | 763 | fib6_start_gc(info->nl_net, rt); |
780 | if (!(rt->rt6i_flags&RTF_CACHE)) | 764 | if (!(rt->rt6i_flags&RTF_CACHE)) |
781 | fib6_prune_clones(pn, rt); | 765 | fib6_prune_clones(info->nl_net, pn, rt); |
782 | } | 766 | } |
783 | 767 | ||
784 | out: | 768 | out: |
@@ -789,11 +773,11 @@ out: | |||
789 | * super-tree leaf node we have to find a new one for it. | 773 | * super-tree leaf node we have to find a new one for it. |
790 | */ | 774 | */ |
791 | if (pn != fn && !pn->leaf && !(pn->fn_flags & RTN_RTINFO)) { | 775 | if (pn != fn && !pn->leaf && !(pn->fn_flags & RTN_RTINFO)) { |
792 | pn->leaf = fib6_find_prefix(pn); | 776 | pn->leaf = fib6_find_prefix(info->nl_net, pn); |
793 | #if RT6_DEBUG >= 2 | 777 | #if RT6_DEBUG >= 2 |
794 | if (!pn->leaf) { | 778 | if (!pn->leaf) { |
795 | BUG_TRAP(pn->leaf != NULL); | 779 | BUG_TRAP(pn->leaf != NULL); |
796 | pn->leaf = &ip6_null_entry; | 780 | pn->leaf = info->nl_net->ipv6.ip6_null_entry; |
797 | } | 781 | } |
798 | #endif | 782 | #endif |
799 | atomic_inc(&pn->leaf->rt6i_ref); | 783 | atomic_inc(&pn->leaf->rt6i_ref); |
@@ -809,7 +793,7 @@ out: | |||
809 | */ | 793 | */ |
810 | st_failure: | 794 | st_failure: |
811 | if (fn && !(fn->fn_flags & (RTN_RTINFO|RTN_ROOT))) | 795 | if (fn && !(fn->fn_flags & (RTN_RTINFO|RTN_ROOT))) |
812 | fib6_repair_tree(fn); | 796 | fib6_repair_tree(info->nl_net, fn); |
813 | dst_free(&rt->u.dst); | 797 | dst_free(&rt->u.dst); |
814 | return err; | 798 | return err; |
815 | #endif | 799 | #endif |
@@ -975,10 +959,10 @@ struct fib6_node * fib6_locate(struct fib6_node *root, | |||
975 | * | 959 | * |
976 | */ | 960 | */ |
977 | 961 | ||
978 | static struct rt6_info * fib6_find_prefix(struct fib6_node *fn) | 962 | static struct rt6_info *fib6_find_prefix(struct net *net, struct fib6_node *fn) |
979 | { | 963 | { |
980 | if (fn->fn_flags&RTN_ROOT) | 964 | if (fn->fn_flags&RTN_ROOT) |
981 | return &ip6_null_entry; | 965 | return net->ipv6.ip6_null_entry; |
982 | 966 | ||
983 | while(fn) { | 967 | while(fn) { |
984 | if(fn->left) | 968 | if(fn->left) |
@@ -997,7 +981,8 @@ static struct rt6_info * fib6_find_prefix(struct fib6_node *fn) | |||
997 | * is the node we want to try and remove. | 981 | * is the node we want to try and remove. |
998 | */ | 982 | */ |
999 | 983 | ||
1000 | static struct fib6_node * fib6_repair_tree(struct fib6_node *fn) | 984 | static struct fib6_node *fib6_repair_tree(struct net *net, |
985 | struct fib6_node *fn) | ||
1001 | { | 986 | { |
1002 | int children; | 987 | int children; |
1003 | int nstate; | 988 | int nstate; |
@@ -1024,11 +1009,11 @@ static struct fib6_node * fib6_repair_tree(struct fib6_node *fn) | |||
1024 | || (children && fn->fn_flags&RTN_ROOT) | 1009 | || (children && fn->fn_flags&RTN_ROOT) |
1025 | #endif | 1010 | #endif |
1026 | ) { | 1011 | ) { |
1027 | fn->leaf = fib6_find_prefix(fn); | 1012 | fn->leaf = fib6_find_prefix(net, fn); |
1028 | #if RT6_DEBUG >= 2 | 1013 | #if RT6_DEBUG >= 2 |
1029 | if (fn->leaf==NULL) { | 1014 | if (fn->leaf==NULL) { |
1030 | BUG_TRAP(fn->leaf); | 1015 | BUG_TRAP(fn->leaf); |
1031 | fn->leaf = &ip6_null_entry; | 1016 | fn->leaf = net->ipv6.ip6_null_entry; |
1032 | } | 1017 | } |
1033 | #endif | 1018 | #endif |
1034 | atomic_inc(&fn->leaf->rt6i_ref); | 1019 | atomic_inc(&fn->leaf->rt6i_ref); |
@@ -1101,14 +1086,15 @@ static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp, | |||
1101 | { | 1086 | { |
1102 | struct fib6_walker_t *w; | 1087 | struct fib6_walker_t *w; |
1103 | struct rt6_info *rt = *rtp; | 1088 | struct rt6_info *rt = *rtp; |
1089 | struct net *net = info->nl_net; | ||
1104 | 1090 | ||
1105 | RT6_TRACE("fib6_del_route\n"); | 1091 | RT6_TRACE("fib6_del_route\n"); |
1106 | 1092 | ||
1107 | /* Unlink it */ | 1093 | /* Unlink it */ |
1108 | *rtp = rt->u.dst.rt6_next; | 1094 | *rtp = rt->u.dst.rt6_next; |
1109 | rt->rt6i_node = NULL; | 1095 | rt->rt6i_node = NULL; |
1110 | rt6_stats.fib_rt_entries--; | 1096 | net->ipv6.rt6_stats->fib_rt_entries--; |
1111 | rt6_stats.fib_discarded_routes++; | 1097 | net->ipv6.rt6_stats->fib_discarded_routes++; |
1112 | 1098 | ||
1113 | /* Reset round-robin state, if necessary */ | 1099 | /* Reset round-robin state, if necessary */ |
1114 | if (fn->rr_ptr == rt) | 1100 | if (fn->rr_ptr == rt) |
@@ -1131,8 +1117,8 @@ static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp, | |||
1131 | /* If it was last route, expunge its radix tree node */ | 1117 | /* If it was last route, expunge its radix tree node */ |
1132 | if (fn->leaf == NULL) { | 1118 | if (fn->leaf == NULL) { |
1133 | fn->fn_flags &= ~RTN_RTINFO; | 1119 | fn->fn_flags &= ~RTN_RTINFO; |
1134 | rt6_stats.fib_route_nodes--; | 1120 | net->ipv6.rt6_stats->fib_route_nodes--; |
1135 | fn = fib6_repair_tree(fn); | 1121 | fn = fib6_repair_tree(net, fn); |
1136 | } | 1122 | } |
1137 | 1123 | ||
1138 | if (atomic_read(&rt->rt6i_ref) != 1) { | 1124 | if (atomic_read(&rt->rt6i_ref) != 1) { |
@@ -1144,7 +1130,7 @@ static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp, | |||
1144 | */ | 1130 | */ |
1145 | while (fn) { | 1131 | while (fn) { |
1146 | if (!(fn->fn_flags&RTN_RTINFO) && fn->leaf == rt) { | 1132 | if (!(fn->fn_flags&RTN_RTINFO) && fn->leaf == rt) { |
1147 | fn->leaf = fib6_find_prefix(fn); | 1133 | fn->leaf = fib6_find_prefix(net, fn); |
1148 | atomic_inc(&fn->leaf->rt6i_ref); | 1134 | atomic_inc(&fn->leaf->rt6i_ref); |
1149 | rt6_release(rt); | 1135 | rt6_release(rt); |
1150 | } | 1136 | } |
@@ -1160,6 +1146,7 @@ static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp, | |||
1160 | 1146 | ||
1161 | int fib6_del(struct rt6_info *rt, struct nl_info *info) | 1147 | int fib6_del(struct rt6_info *rt, struct nl_info *info) |
1162 | { | 1148 | { |
1149 | struct net *net = info->nl_net; | ||
1163 | struct fib6_node *fn = rt->rt6i_node; | 1150 | struct fib6_node *fn = rt->rt6i_node; |
1164 | struct rt6_info **rtp; | 1151 | struct rt6_info **rtp; |
1165 | 1152 | ||
@@ -1169,7 +1156,7 @@ int fib6_del(struct rt6_info *rt, struct nl_info *info) | |||
1169 | return -ENOENT; | 1156 | return -ENOENT; |
1170 | } | 1157 | } |
1171 | #endif | 1158 | #endif |
1172 | if (fn == NULL || rt == &ip6_null_entry) | 1159 | if (fn == NULL || rt == net->ipv6.ip6_null_entry) |
1173 | return -ENOENT; | 1160 | return -ENOENT; |
1174 | 1161 | ||
1175 | BUG_TRAP(fn->fn_flags&RTN_RTINFO); | 1162 | BUG_TRAP(fn->fn_flags&RTN_RTINFO); |
@@ -1184,7 +1171,7 @@ int fib6_del(struct rt6_info *rt, struct nl_info *info) | |||
1184 | pn = pn->parent; | 1171 | pn = pn->parent; |
1185 | } | 1172 | } |
1186 | #endif | 1173 | #endif |
1187 | fib6_prune_clones(pn, rt); | 1174 | fib6_prune_clones(info->nl_net, pn, rt); |
1188 | } | 1175 | } |
1189 | 1176 | ||
1190 | /* | 1177 | /* |
@@ -1314,12 +1301,12 @@ static int fib6_walk(struct fib6_walker_t *w) | |||
1314 | 1301 | ||
1315 | static int fib6_clean_node(struct fib6_walker_t *w) | 1302 | static int fib6_clean_node(struct fib6_walker_t *w) |
1316 | { | 1303 | { |
1317 | struct nl_info info = { | ||
1318 | .nl_net = &init_net, | ||
1319 | }; | ||
1320 | int res; | 1304 | int res; |
1321 | struct rt6_info *rt; | 1305 | struct rt6_info *rt; |
1322 | struct fib6_cleaner_t *c = container_of(w, struct fib6_cleaner_t, w); | 1306 | struct fib6_cleaner_t *c = container_of(w, struct fib6_cleaner_t, w); |
1307 | struct nl_info info = { | ||
1308 | .nl_net = c->net, | ||
1309 | }; | ||
1323 | 1310 | ||
1324 | for (rt = w->leaf; rt; rt = rt->u.dst.rt6_next) { | 1311 | for (rt = w->leaf; rt; rt = rt->u.dst.rt6_next) { |
1325 | res = c->func(rt, c->arg); | 1312 | res = c->func(rt, c->arg); |
@@ -1351,7 +1338,7 @@ static int fib6_clean_node(struct fib6_walker_t *w) | |||
1351 | * ignoring pure split nodes) will be scanned. | 1338 | * ignoring pure split nodes) will be scanned. |
1352 | */ | 1339 | */ |
1353 | 1340 | ||
1354 | static void fib6_clean_tree(struct fib6_node *root, | 1341 | static void fib6_clean_tree(struct net *net, struct fib6_node *root, |
1355 | int (*func)(struct rt6_info *, void *arg), | 1342 | int (*func)(struct rt6_info *, void *arg), |
1356 | int prune, void *arg) | 1343 | int prune, void *arg) |
1357 | { | 1344 | { |
@@ -1362,23 +1349,26 @@ static void fib6_clean_tree(struct fib6_node *root, | |||
1362 | c.w.prune = prune; | 1349 | c.w.prune = prune; |
1363 | c.func = func; | 1350 | c.func = func; |
1364 | c.arg = arg; | 1351 | c.arg = arg; |
1352 | c.net = net; | ||
1365 | 1353 | ||
1366 | fib6_walk(&c.w); | 1354 | fib6_walk(&c.w); |
1367 | } | 1355 | } |
1368 | 1356 | ||
1369 | void fib6_clean_all(int (*func)(struct rt6_info *, void *arg), | 1357 | void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg), |
1370 | int prune, void *arg) | 1358 | int prune, void *arg) |
1371 | { | 1359 | { |
1372 | struct fib6_table *table; | 1360 | struct fib6_table *table; |
1373 | struct hlist_node *node; | 1361 | struct hlist_node *node; |
1362 | struct hlist_head *head; | ||
1374 | unsigned int h; | 1363 | unsigned int h; |
1375 | 1364 | ||
1376 | rcu_read_lock(); | 1365 | rcu_read_lock(); |
1377 | for (h = 0; h < FIB_TABLE_HASHSZ; h++) { | 1366 | for (h = 0; h < FIB_TABLE_HASHSZ; h++) { |
1378 | hlist_for_each_entry_rcu(table, node, &fib_table_hash[h], | 1367 | head = &net->ipv6.fib_table_hash[h]; |
1379 | tb6_hlist) { | 1368 | hlist_for_each_entry_rcu(table, node, head, tb6_hlist) { |
1380 | write_lock_bh(&table->tb6_lock); | 1369 | write_lock_bh(&table->tb6_lock); |
1381 | fib6_clean_tree(&table->tb6_root, func, prune, arg); | 1370 | fib6_clean_tree(net, &table->tb6_root, |
1371 | func, prune, arg); | ||
1382 | write_unlock_bh(&table->tb6_lock); | 1372 | write_unlock_bh(&table->tb6_lock); |
1383 | } | 1373 | } |
1384 | } | 1374 | } |
@@ -1395,9 +1385,10 @@ static int fib6_prune_clone(struct rt6_info *rt, void *arg) | |||
1395 | return 0; | 1385 | return 0; |
1396 | } | 1386 | } |
1397 | 1387 | ||
1398 | static void fib6_prune_clones(struct fib6_node *fn, struct rt6_info *rt) | 1388 | static void fib6_prune_clones(struct net *net, struct fib6_node *fn, |
1389 | struct rt6_info *rt) | ||
1399 | { | 1390 | { |
1400 | fib6_clean_tree(fn, fib6_prune_clone, 1, rt); | 1391 | fib6_clean_tree(net, fn, fib6_prune_clone, 1, rt); |
1401 | } | 1392 | } |
1402 | 1393 | ||
1403 | /* | 1394 | /* |
@@ -1447,54 +1438,145 @@ static int fib6_age(struct rt6_info *rt, void *arg) | |||
1447 | 1438 | ||
1448 | static DEFINE_SPINLOCK(fib6_gc_lock); | 1439 | static DEFINE_SPINLOCK(fib6_gc_lock); |
1449 | 1440 | ||
1450 | void fib6_run_gc(unsigned long dummy) | 1441 | void fib6_run_gc(unsigned long expires, struct net *net) |
1451 | { | 1442 | { |
1452 | if (dummy != ~0UL) { | 1443 | if (expires != ~0UL) { |
1453 | spin_lock_bh(&fib6_gc_lock); | 1444 | spin_lock_bh(&fib6_gc_lock); |
1454 | gc_args.timeout = dummy ? (int)dummy : | 1445 | gc_args.timeout = expires ? (int)expires : |
1455 | init_net.ipv6.sysctl.ip6_rt_gc_interval; | 1446 | net->ipv6.sysctl.ip6_rt_gc_interval; |
1456 | } else { | 1447 | } else { |
1457 | local_bh_disable(); | 1448 | local_bh_disable(); |
1458 | if (!spin_trylock(&fib6_gc_lock)) { | 1449 | if (!spin_trylock(&fib6_gc_lock)) { |
1459 | mod_timer(&ip6_fib_timer, jiffies + HZ); | 1450 | mod_timer(net->ipv6.ip6_fib_timer, jiffies + HZ); |
1460 | local_bh_enable(); | 1451 | local_bh_enable(); |
1461 | return; | 1452 | return; |
1462 | } | 1453 | } |
1463 | gc_args.timeout = init_net.ipv6.sysctl.ip6_rt_gc_interval; | 1454 | gc_args.timeout = net->ipv6.sysctl.ip6_rt_gc_interval; |
1464 | } | 1455 | } |
1465 | gc_args.more = 0; | 1456 | gc_args.more = 0; |
1466 | 1457 | ||
1467 | ndisc_dst_gc(&gc_args.more); | 1458 | icmp6_dst_gc(&gc_args.more); |
1468 | fib6_clean_all(fib6_age, 0, NULL); | 1459 | |
1460 | fib6_clean_all(net, fib6_age, 0, NULL); | ||
1469 | 1461 | ||
1470 | if (gc_args.more) | 1462 | if (gc_args.more) |
1471 | mod_timer(&ip6_fib_timer, jiffies + | 1463 | mod_timer(net->ipv6.ip6_fib_timer, jiffies + |
1472 | init_net.ipv6.sysctl.ip6_rt_gc_interval); | 1464 | net->ipv6.sysctl.ip6_rt_gc_interval); |
1473 | else { | 1465 | else { |
1474 | del_timer(&ip6_fib_timer); | 1466 | del_timer(net->ipv6.ip6_fib_timer); |
1475 | ip6_fib_timer.expires = 0; | 1467 | net->ipv6.ip6_fib_timer->expires = 0; |
1476 | } | 1468 | } |
1477 | spin_unlock_bh(&fib6_gc_lock); | 1469 | spin_unlock_bh(&fib6_gc_lock); |
1478 | } | 1470 | } |
1479 | 1471 | ||
1480 | int __init fib6_init(void) | 1472 | static void fib6_gc_timer_cb(unsigned long arg) |
1473 | { | ||
1474 | fib6_run_gc(0, (struct net *)arg); | ||
1475 | } | ||
1476 | |||
1477 | static int fib6_net_init(struct net *net) | ||
1481 | { | 1478 | { |
1482 | int ret; | 1479 | int ret; |
1480 | struct timer_list *timer; | ||
1481 | |||
1482 | ret = -ENOMEM; | ||
1483 | timer = kzalloc(sizeof(*timer), GFP_KERNEL); | ||
1484 | if (!timer) | ||
1485 | goto out; | ||
1486 | |||
1487 | setup_timer(timer, fib6_gc_timer_cb, (unsigned long)net); | ||
1488 | net->ipv6.ip6_fib_timer = timer; | ||
1489 | |||
1490 | net->ipv6.rt6_stats = kzalloc(sizeof(*net->ipv6.rt6_stats), GFP_KERNEL); | ||
1491 | if (!net->ipv6.rt6_stats) | ||
1492 | goto out_timer; | ||
1493 | |||
1494 | net->ipv6.fib_table_hash = | ||
1495 | kzalloc(sizeof(*net->ipv6.fib_table_hash)*FIB_TABLE_HASHSZ, | ||
1496 | GFP_KERNEL); | ||
1497 | if (!net->ipv6.fib_table_hash) | ||
1498 | goto out_rt6_stats; | ||
1499 | |||
1500 | net->ipv6.fib6_main_tbl = kzalloc(sizeof(*net->ipv6.fib6_main_tbl), | ||
1501 | GFP_KERNEL); | ||
1502 | if (!net->ipv6.fib6_main_tbl) | ||
1503 | goto out_fib_table_hash; | ||
1504 | |||
1505 | net->ipv6.fib6_main_tbl->tb6_id = RT6_TABLE_MAIN; | ||
1506 | net->ipv6.fib6_main_tbl->tb6_root.leaf = net->ipv6.ip6_null_entry; | ||
1507 | net->ipv6.fib6_main_tbl->tb6_root.fn_flags = | ||
1508 | RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO; | ||
1509 | |||
1510 | #ifdef CONFIG_IPV6_MULTIPLE_TABLES | ||
1511 | net->ipv6.fib6_local_tbl = kzalloc(sizeof(*net->ipv6.fib6_local_tbl), | ||
1512 | GFP_KERNEL); | ||
1513 | if (!net->ipv6.fib6_local_tbl) | ||
1514 | goto out_fib6_main_tbl; | ||
1515 | net->ipv6.fib6_local_tbl->tb6_id = RT6_TABLE_LOCAL; | ||
1516 | net->ipv6.fib6_local_tbl->tb6_root.leaf = net->ipv6.ip6_null_entry; | ||
1517 | net->ipv6.fib6_local_tbl->tb6_root.fn_flags = | ||
1518 | RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO; | ||
1519 | #endif | ||
1520 | fib6_tables_init(net); | ||
1521 | |||
1522 | ret = 0; | ||
1523 | out: | ||
1524 | return ret; | ||
1525 | |||
1526 | #ifdef CONFIG_IPV6_MULTIPLE_TABLES | ||
1527 | out_fib6_main_tbl: | ||
1528 | kfree(net->ipv6.fib6_main_tbl); | ||
1529 | #endif | ||
1530 | out_fib_table_hash: | ||
1531 | kfree(net->ipv6.fib_table_hash); | ||
1532 | out_rt6_stats: | ||
1533 | kfree(net->ipv6.rt6_stats); | ||
1534 | out_timer: | ||
1535 | kfree(timer); | ||
1536 | goto out; | ||
1537 | } | ||
1538 | |||
1539 | static void fib6_net_exit(struct net *net) | ||
1540 | { | ||
1541 | rt6_ifdown(net, NULL); | ||
1542 | del_timer(net->ipv6.ip6_fib_timer); | ||
1543 | kfree(net->ipv6.ip6_fib_timer); | ||
1544 | #ifdef CONFIG_IPV6_MULTIPLE_TABLES | ||
1545 | kfree(net->ipv6.fib6_local_tbl); | ||
1546 | #endif | ||
1547 | kfree(net->ipv6.fib6_main_tbl); | ||
1548 | kfree(net->ipv6.fib_table_hash); | ||
1549 | kfree(net->ipv6.rt6_stats); | ||
1550 | } | ||
1551 | |||
1552 | static struct pernet_operations fib6_net_ops = { | ||
1553 | .init = fib6_net_init, | ||
1554 | .exit = fib6_net_exit, | ||
1555 | }; | ||
1556 | |||
1557 | int __init fib6_init(void) | ||
1558 | { | ||
1559 | int ret = -ENOMEM; | ||
1560 | |||
1483 | fib6_node_kmem = kmem_cache_create("fib6_nodes", | 1561 | fib6_node_kmem = kmem_cache_create("fib6_nodes", |
1484 | sizeof(struct fib6_node), | 1562 | sizeof(struct fib6_node), |
1485 | 0, SLAB_HWCACHE_ALIGN, | 1563 | 0, SLAB_HWCACHE_ALIGN, |
1486 | NULL); | 1564 | NULL); |
1487 | if (!fib6_node_kmem) | 1565 | if (!fib6_node_kmem) |
1488 | return -ENOMEM; | 1566 | goto out; |
1489 | 1567 | ||
1490 | fib6_tables_init(); | 1568 | ret = register_pernet_subsys(&fib6_net_ops); |
1569 | if (ret) | ||
1570 | goto out_kmem_cache_create; | ||
1491 | 1571 | ||
1492 | ret = __rtnl_register(PF_INET6, RTM_GETROUTE, NULL, inet6_dump_fib); | 1572 | ret = __rtnl_register(PF_INET6, RTM_GETROUTE, NULL, inet6_dump_fib); |
1493 | if (ret) | 1573 | if (ret) |
1494 | goto out_kmem_cache_create; | 1574 | goto out_unregister_subsys; |
1495 | out: | 1575 | out: |
1496 | return ret; | 1576 | return ret; |
1497 | 1577 | ||
1578 | out_unregister_subsys: | ||
1579 | unregister_pernet_subsys(&fib6_net_ops); | ||
1498 | out_kmem_cache_create: | 1580 | out_kmem_cache_create: |
1499 | kmem_cache_destroy(fib6_node_kmem); | 1581 | kmem_cache_destroy(fib6_node_kmem); |
1500 | goto out; | 1582 | goto out; |
@@ -1502,6 +1584,6 @@ out_kmem_cache_create: | |||
1502 | 1584 | ||
1503 | void fib6_gc_cleanup(void) | 1585 | void fib6_gc_cleanup(void) |
1504 | { | 1586 | { |
1505 | del_timer(&ip6_fib_timer); | 1587 | unregister_pernet_subsys(&fib6_net_ops); |
1506 | kmem_cache_destroy(fib6_node_kmem); | 1588 | kmem_cache_destroy(fib6_node_kmem); |
1507 | } | 1589 | } |