diff options
Diffstat (limited to 'net/ipv6/ip6_fib.c')
-rw-r--r-- | net/ipv6/ip6_fib.c | 171 |
1 files changed, 151 insertions, 20 deletions
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 1f2316187ca4..bececbe9dd2c 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c | |||
@@ -158,7 +158,26 @@ static struct fib6_table fib6_main_tbl = { | |||
158 | }; | 158 | }; |
159 | 159 | ||
160 | #ifdef CONFIG_IPV6_MULTIPLE_TABLES | 160 | #ifdef CONFIG_IPV6_MULTIPLE_TABLES |
161 | #define FIB_TABLE_HASHSZ 256 | ||
162 | #else | ||
163 | #define FIB_TABLE_HASHSZ 1 | ||
164 | #endif | ||
165 | static struct hlist_head fib_table_hash[FIB_TABLE_HASHSZ]; | ||
166 | |||
167 | static void fib6_link_table(struct fib6_table *tb) | ||
168 | { | ||
169 | unsigned int h; | ||
170 | |||
171 | h = tb->tb6_id & (FIB_TABLE_HASHSZ - 1); | ||
161 | 172 | ||
173 | /* | ||
174 | * No protection necessary, this is the only list mutatation | ||
175 | * operation, tables never disappear once they exist. | ||
176 | */ | ||
177 | hlist_add_head_rcu(&tb->tb6_hlist, &fib_table_hash[h]); | ||
178 | } | ||
179 | |||
180 | #ifdef CONFIG_IPV6_MULTIPLE_TABLES | ||
162 | static struct fib6_table fib6_local_tbl = { | 181 | static struct fib6_table fib6_local_tbl = { |
163 | .tb6_id = RT6_TABLE_LOCAL, | 182 | .tb6_id = RT6_TABLE_LOCAL, |
164 | .tb6_lock = RW_LOCK_UNLOCKED, | 183 | .tb6_lock = RW_LOCK_UNLOCKED, |
@@ -168,9 +187,6 @@ static struct fib6_table fib6_local_tbl = { | |||
168 | }, | 187 | }, |
169 | }; | 188 | }; |
170 | 189 | ||
171 | #define FIB_TABLE_HASHSZ 256 | ||
172 | static struct hlist_head fib_table_hash[FIB_TABLE_HASHSZ]; | ||
173 | |||
174 | static struct fib6_table *fib6_alloc_table(u32 id) | 190 | static struct fib6_table *fib6_alloc_table(u32 id) |
175 | { | 191 | { |
176 | struct fib6_table *table; | 192 | struct fib6_table *table; |
@@ -186,19 +202,6 @@ static struct fib6_table *fib6_alloc_table(u32 id) | |||
186 | return table; | 202 | return table; |
187 | } | 203 | } |
188 | 204 | ||
189 | static void fib6_link_table(struct fib6_table *tb) | ||
190 | { | ||
191 | unsigned int h; | ||
192 | |||
193 | h = tb->tb6_id & (FIB_TABLE_HASHSZ - 1); | ||
194 | |||
195 | /* | ||
196 | * No protection necessary, this is the only list mutatation | ||
197 | * operation, tables never disappear once they exist. | ||
198 | */ | ||
199 | hlist_add_head_rcu(&tb->tb6_hlist, &fib_table_hash[h]); | ||
200 | } | ||
201 | |||
202 | struct fib6_table *fib6_new_table(u32 id) | 205 | struct fib6_table *fib6_new_table(u32 id) |
203 | { | 206 | { |
204 | struct fib6_table *tb; | 207 | struct fib6_table *tb; |
@@ -263,10 +266,135 @@ struct dst_entry *fib6_rule_lookup(struct flowi *fl, int flags, | |||
263 | 266 | ||
264 | static void __init fib6_tables_init(void) | 267 | static void __init fib6_tables_init(void) |
265 | { | 268 | { |
269 | fib6_link_table(&fib6_main_tbl); | ||
266 | } | 270 | } |
267 | 271 | ||
268 | #endif | 272 | #endif |
269 | 273 | ||
274 | static int fib6_dump_node(struct fib6_walker_t *w) | ||
275 | { | ||
276 | int res; | ||
277 | struct rt6_info *rt; | ||
278 | |||
279 | for (rt = w->leaf; rt; rt = rt->u.next) { | ||
280 | res = rt6_dump_route(rt, w->args); | ||
281 | if (res < 0) { | ||
282 | /* Frame is full, suspend walking */ | ||
283 | w->leaf = rt; | ||
284 | return 1; | ||
285 | } | ||
286 | BUG_TRAP(res!=0); | ||
287 | } | ||
288 | w->leaf = NULL; | ||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | static void fib6_dump_end(struct netlink_callback *cb) | ||
293 | { | ||
294 | struct fib6_walker_t *w = (void*)cb->args[2]; | ||
295 | |||
296 | if (w) { | ||
297 | cb->args[2] = 0; | ||
298 | kfree(w); | ||
299 | } | ||
300 | cb->done = (void*)cb->args[3]; | ||
301 | cb->args[1] = 3; | ||
302 | } | ||
303 | |||
304 | static int fib6_dump_done(struct netlink_callback *cb) | ||
305 | { | ||
306 | fib6_dump_end(cb); | ||
307 | return cb->done ? cb->done(cb) : 0; | ||
308 | } | ||
309 | |||
310 | static int fib6_dump_table(struct fib6_table *table, struct sk_buff *skb, | ||
311 | struct netlink_callback *cb) | ||
312 | { | ||
313 | struct fib6_walker_t *w; | ||
314 | int res; | ||
315 | |||
316 | w = (void *)cb->args[2]; | ||
317 | w->root = &table->tb6_root; | ||
318 | |||
319 | if (cb->args[4] == 0) { | ||
320 | read_lock_bh(&table->tb6_lock); | ||
321 | res = fib6_walk(w); | ||
322 | read_unlock_bh(&table->tb6_lock); | ||
323 | if (res > 0) | ||
324 | cb->args[4] = 1; | ||
325 | } else { | ||
326 | read_lock_bh(&table->tb6_lock); | ||
327 | res = fib6_walk_continue(w); | ||
328 | read_unlock_bh(&table->tb6_lock); | ||
329 | if (res != 0) { | ||
330 | if (res < 0) | ||
331 | fib6_walker_unlink(w); | ||
332 | goto end; | ||
333 | } | ||
334 | fib6_walker_unlink(w); | ||
335 | cb->args[4] = 0; | ||
336 | } | ||
337 | end: | ||
338 | return res; | ||
339 | } | ||
340 | |||
341 | int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) | ||
342 | { | ||
343 | unsigned int h, s_h; | ||
344 | unsigned int e = 0, s_e; | ||
345 | struct rt6_rtnl_dump_arg arg; | ||
346 | struct fib6_walker_t *w; | ||
347 | struct fib6_table *tb; | ||
348 | struct hlist_node *node; | ||
349 | int res = 0; | ||
350 | |||
351 | s_h = cb->args[0]; | ||
352 | s_e = cb->args[1]; | ||
353 | |||
354 | w = (void *)cb->args[2]; | ||
355 | if (w == NULL) { | ||
356 | /* New dump: | ||
357 | * | ||
358 | * 1. hook callback destructor. | ||
359 | */ | ||
360 | cb->args[3] = (long)cb->done; | ||
361 | cb->done = fib6_dump_done; | ||
362 | |||
363 | /* | ||
364 | * 2. allocate and initialize walker. | ||
365 | */ | ||
366 | w = kzalloc(sizeof(*w), GFP_ATOMIC); | ||
367 | if (w == NULL) | ||
368 | return -ENOMEM; | ||
369 | w->func = fib6_dump_node; | ||
370 | cb->args[2] = (long)w; | ||
371 | } | ||
372 | |||
373 | arg.skb = skb; | ||
374 | arg.cb = cb; | ||
375 | w->args = &arg; | ||
376 | |||
377 | for (h = s_h; h < FIB_TABLE_HASHSZ; h++, s_e = 0) { | ||
378 | e = 0; | ||
379 | hlist_for_each_entry(tb, node, &fib_table_hash[h], tb6_hlist) { | ||
380 | if (e < s_e) | ||
381 | goto next; | ||
382 | res = fib6_dump_table(tb, skb, cb); | ||
383 | if (res != 0) | ||
384 | goto out; | ||
385 | next: | ||
386 | e++; | ||
387 | } | ||
388 | } | ||
389 | out: | ||
390 | cb->args[1] = e; | ||
391 | cb->args[0] = h; | ||
392 | |||
393 | res = res < 0 ? res : skb->len; | ||
394 | if (res <= 0) | ||
395 | fib6_dump_end(cb); | ||
396 | return res; | ||
397 | } | ||
270 | 398 | ||
271 | /* | 399 | /* |
272 | * Routing Table | 400 | * Routing Table |
@@ -1187,17 +1315,20 @@ static void fib6_clean_tree(struct fib6_node *root, | |||
1187 | void fib6_clean_all(int (*func)(struct rt6_info *, void *arg), | 1315 | void fib6_clean_all(int (*func)(struct rt6_info *, void *arg), |
1188 | int prune, void *arg) | 1316 | int prune, void *arg) |
1189 | { | 1317 | { |
1190 | int i; | ||
1191 | struct fib6_table *table; | 1318 | struct fib6_table *table; |
1319 | struct hlist_node *node; | ||
1320 | unsigned int h; | ||
1192 | 1321 | ||
1193 | for (i = FIB6_TABLE_MIN; i <= FIB6_TABLE_MAX; i++) { | 1322 | rcu_read_lock(); |
1194 | table = fib6_get_table(i); | 1323 | for (h = 0; h < FIB_TABLE_HASHSZ; h++) { |
1195 | if (table != NULL) { | 1324 | hlist_for_each_entry_rcu(table, node, &fib_table_hash[h], |
1325 | tb6_hlist) { | ||
1196 | write_lock_bh(&table->tb6_lock); | 1326 | write_lock_bh(&table->tb6_lock); |
1197 | fib6_clean_tree(&table->tb6_root, func, prune, arg); | 1327 | fib6_clean_tree(&table->tb6_root, func, prune, arg); |
1198 | write_unlock_bh(&table->tb6_lock); | 1328 | write_unlock_bh(&table->tb6_lock); |
1199 | } | 1329 | } |
1200 | } | 1330 | } |
1331 | rcu_read_unlock(); | ||
1201 | } | 1332 | } |
1202 | 1333 | ||
1203 | static int fib6_prune_clone(struct rt6_info *rt, void *arg) | 1334 | static int fib6_prune_clone(struct rt6_info *rt, void *arg) |