diff options
Diffstat (limited to 'net/ipv4/route.c')
-rw-r--r-- | net/ipv4/route.c | 34 |
1 files changed, 23 insertions, 11 deletions
diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 6ee5354c9aa1..a6d7c584f53b 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c | |||
@@ -282,6 +282,8 @@ static struct rtable *rt_cache_get_first(struct seq_file *seq) | |||
282 | struct rtable *r = NULL; | 282 | struct rtable *r = NULL; |
283 | 283 | ||
284 | for (st->bucket = rt_hash_mask; st->bucket >= 0; --st->bucket) { | 284 | for (st->bucket = rt_hash_mask; st->bucket >= 0; --st->bucket) { |
285 | if (!rt_hash_table[st->bucket].chain) | ||
286 | continue; | ||
285 | rcu_read_lock_bh(); | 287 | rcu_read_lock_bh(); |
286 | r = rcu_dereference(rt_hash_table[st->bucket].chain); | 288 | r = rcu_dereference(rt_hash_table[st->bucket].chain); |
287 | while (r) { | 289 | while (r) { |
@@ -299,11 +301,14 @@ static struct rtable *__rt_cache_get_next(struct seq_file *seq, | |||
299 | struct rtable *r) | 301 | struct rtable *r) |
300 | { | 302 | { |
301 | struct rt_cache_iter_state *st = seq->private; | 303 | struct rt_cache_iter_state *st = seq->private; |
304 | |||
302 | r = r->u.dst.rt_next; | 305 | r = r->u.dst.rt_next; |
303 | while (!r) { | 306 | while (!r) { |
304 | rcu_read_unlock_bh(); | 307 | rcu_read_unlock_bh(); |
305 | if (--st->bucket < 0) | 308 | do { |
306 | break; | 309 | if (--st->bucket < 0) |
310 | return NULL; | ||
311 | } while (!rt_hash_table[st->bucket].chain); | ||
307 | rcu_read_lock_bh(); | 312 | rcu_read_lock_bh(); |
308 | r = rt_hash_table[st->bucket].chain; | 313 | r = rt_hash_table[st->bucket].chain; |
309 | } | 314 | } |
@@ -2356,11 +2361,6 @@ static int ip_route_output_slow(struct net *net, struct rtable **rp, | |||
2356 | ipv4_is_zeronet(oldflp->fl4_src)) | 2361 | ipv4_is_zeronet(oldflp->fl4_src)) |
2357 | goto out; | 2362 | goto out; |
2358 | 2363 | ||
2359 | /* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */ | ||
2360 | dev_out = ip_dev_find(net, oldflp->fl4_src); | ||
2361 | if (dev_out == NULL) | ||
2362 | goto out; | ||
2363 | |||
2364 | /* I removed check for oif == dev_out->oif here. | 2364 | /* I removed check for oif == dev_out->oif here. |
2365 | It was wrong for two reasons: | 2365 | It was wrong for two reasons: |
2366 | 1. ip_dev_find(net, saddr) can return wrong iface, if saddr | 2366 | 1. ip_dev_find(net, saddr) can return wrong iface, if saddr |
@@ -2372,6 +2372,11 @@ static int ip_route_output_slow(struct net *net, struct rtable **rp, | |||
2372 | if (oldflp->oif == 0 | 2372 | if (oldflp->oif == 0 |
2373 | && (ipv4_is_multicast(oldflp->fl4_dst) || | 2373 | && (ipv4_is_multicast(oldflp->fl4_dst) || |
2374 | oldflp->fl4_dst == htonl(0xFFFFFFFF))) { | 2374 | oldflp->fl4_dst == htonl(0xFFFFFFFF))) { |
2375 | /* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */ | ||
2376 | dev_out = ip_dev_find(net, oldflp->fl4_src); | ||
2377 | if (dev_out == NULL) | ||
2378 | goto out; | ||
2379 | |||
2375 | /* Special hack: user can direct multicasts | 2380 | /* Special hack: user can direct multicasts |
2376 | and limited broadcast via necessary interface | 2381 | and limited broadcast via necessary interface |
2377 | without fiddling with IP_MULTICAST_IF or IP_PKTINFO. | 2382 | without fiddling with IP_MULTICAST_IF or IP_PKTINFO. |
@@ -2390,9 +2395,15 @@ static int ip_route_output_slow(struct net *net, struct rtable **rp, | |||
2390 | fl.oif = dev_out->ifindex; | 2395 | fl.oif = dev_out->ifindex; |
2391 | goto make_route; | 2396 | goto make_route; |
2392 | } | 2397 | } |
2393 | if (dev_out) | 2398 | |
2399 | if (!(oldflp->flags & FLOWI_FLAG_ANYSRC)) { | ||
2400 | /* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */ | ||
2401 | dev_out = ip_dev_find(net, oldflp->fl4_src); | ||
2402 | if (dev_out == NULL) | ||
2403 | goto out; | ||
2394 | dev_put(dev_out); | 2404 | dev_put(dev_out); |
2395 | dev_out = NULL; | 2405 | dev_out = NULL; |
2406 | } | ||
2396 | } | 2407 | } |
2397 | 2408 | ||
2398 | 2409 | ||
@@ -2840,7 +2851,9 @@ int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb) | |||
2840 | if (s_h < 0) | 2851 | if (s_h < 0) |
2841 | s_h = 0; | 2852 | s_h = 0; |
2842 | s_idx = idx = cb->args[1]; | 2853 | s_idx = idx = cb->args[1]; |
2843 | for (h = s_h; h <= rt_hash_mask; h++) { | 2854 | for (h = s_h; h <= rt_hash_mask; h++, s_idx = 0) { |
2855 | if (!rt_hash_table[h].chain) | ||
2856 | continue; | ||
2844 | rcu_read_lock_bh(); | 2857 | rcu_read_lock_bh(); |
2845 | for (rt = rcu_dereference(rt_hash_table[h].chain), idx = 0; rt; | 2858 | for (rt = rcu_dereference(rt_hash_table[h].chain), idx = 0; rt; |
2846 | rt = rcu_dereference(rt->u.dst.rt_next), idx++) { | 2859 | rt = rcu_dereference(rt->u.dst.rt_next), idx++) { |
@@ -2859,7 +2872,6 @@ int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb) | |||
2859 | dst_release(xchg(&skb->dst, NULL)); | 2872 | dst_release(xchg(&skb->dst, NULL)); |
2860 | } | 2873 | } |
2861 | rcu_read_unlock_bh(); | 2874 | rcu_read_unlock_bh(); |
2862 | s_idx = 0; | ||
2863 | } | 2875 | } |
2864 | 2876 | ||
2865 | done: | 2877 | done: |