aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/route.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4/route.c')
-rw-r--r--net/ipv4/route.c41
1 files changed, 24 insertions, 17 deletions
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 6ee5354c9aa1..942be04e7955 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
2865done: 2877done:
@@ -2896,8 +2908,6 @@ static int ipv4_sysctl_rtcache_flush(ctl_table *__ctl, int write,
2896} 2908}
2897 2909
2898static int ipv4_sysctl_rtcache_flush_strategy(ctl_table *table, 2910static int ipv4_sysctl_rtcache_flush_strategy(ctl_table *table,
2899 int __user *name,
2900 int nlen,
2901 void __user *oldval, 2911 void __user *oldval,
2902 size_t __user *oldlenp, 2912 size_t __user *oldlenp,
2903 void __user *newval, 2913 void __user *newval,
@@ -2960,16 +2970,13 @@ static int ipv4_sysctl_rt_secret_interval(ctl_table *ctl, int write,
2960} 2970}
2961 2971
2962static int ipv4_sysctl_rt_secret_interval_strategy(ctl_table *table, 2972static int ipv4_sysctl_rt_secret_interval_strategy(ctl_table *table,
2963 int __user *name,
2964 int nlen,
2965 void __user *oldval, 2973 void __user *oldval,
2966 size_t __user *oldlenp, 2974 size_t __user *oldlenp,
2967 void __user *newval, 2975 void __user *newval,
2968 size_t newlen) 2976 size_t newlen)
2969{ 2977{
2970 int old = ip_rt_secret_interval; 2978 int old = ip_rt_secret_interval;
2971 int ret = sysctl_jiffies(table, name, nlen, oldval, oldlenp, newval, 2979 int ret = sysctl_jiffies(table, oldval, oldlenp, newval, newlen);
2972 newlen);
2973 2980
2974 rt_secret_reschedule(old); 2981 rt_secret_reschedule(old);
2975 2982