diff options
Diffstat (limited to 'net/ipv4/route.c')
-rw-r--r-- | net/ipv4/route.c | 105 |
1 files changed, 70 insertions, 35 deletions
diff --git a/net/ipv4/route.c b/net/ipv4/route.c index b2ba5581d2ae..cb562fdd9b9a 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c | |||
@@ -90,6 +90,7 @@ | |||
90 | #include <linux/jhash.h> | 90 | #include <linux/jhash.h> |
91 | #include <linux/rcupdate.h> | 91 | #include <linux/rcupdate.h> |
92 | #include <linux/times.h> | 92 | #include <linux/times.h> |
93 | #include <linux/slab.h> | ||
93 | #include <net/dst.h> | 94 | #include <net/dst.h> |
94 | #include <net/net_namespace.h> | 95 | #include <net/net_namespace.h> |
95 | #include <net/protocol.h> | 96 | #include <net/protocol.h> |
@@ -146,7 +147,6 @@ static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst); | |||
146 | static void ipv4_link_failure(struct sk_buff *skb); | 147 | static void ipv4_link_failure(struct sk_buff *skb); |
147 | static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu); | 148 | static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu); |
148 | static int rt_garbage_collect(struct dst_ops *ops); | 149 | static int rt_garbage_collect(struct dst_ops *ops); |
149 | static void rt_emergency_hash_rebuild(struct net *net); | ||
150 | 150 | ||
151 | 151 | ||
152 | static struct dst_ops ipv4_dst_ops = { | 152 | static struct dst_ops ipv4_dst_ops = { |
@@ -780,11 +780,30 @@ static void rt_do_flush(int process_context) | |||
780 | #define FRACT_BITS 3 | 780 | #define FRACT_BITS 3 |
781 | #define ONE (1UL << FRACT_BITS) | 781 | #define ONE (1UL << FRACT_BITS) |
782 | 782 | ||
783 | /* | ||
784 | * Given a hash chain and an item in this hash chain, | ||
785 | * find if a previous entry has the same hash_inputs | ||
786 | * (but differs on tos, mark or oif) | ||
787 | * Returns 0 if an alias is found. | ||
788 | * Returns ONE if rth has no alias before itself. | ||
789 | */ | ||
790 | static int has_noalias(const struct rtable *head, const struct rtable *rth) | ||
791 | { | ||
792 | const struct rtable *aux = head; | ||
793 | |||
794 | while (aux != rth) { | ||
795 | if (compare_hash_inputs(&aux->fl, &rth->fl)) | ||
796 | return 0; | ||
797 | aux = aux->u.dst.rt_next; | ||
798 | } | ||
799 | return ONE; | ||
800 | } | ||
801 | |||
783 | static void rt_check_expire(void) | 802 | static void rt_check_expire(void) |
784 | { | 803 | { |
785 | static unsigned int rover; | 804 | static unsigned int rover; |
786 | unsigned int i = rover, goal; | 805 | unsigned int i = rover, goal; |
787 | struct rtable *rth, *aux, **rthp; | 806 | struct rtable *rth, **rthp; |
788 | unsigned long samples = 0; | 807 | unsigned long samples = 0; |
789 | unsigned long sum = 0, sum2 = 0; | 808 | unsigned long sum = 0, sum2 = 0; |
790 | unsigned long delta; | 809 | unsigned long delta; |
@@ -835,15 +854,7 @@ nofree: | |||
835 | * attributes don't unfairly skew | 854 | * attributes don't unfairly skew |
836 | * the length computation | 855 | * the length computation |
837 | */ | 856 | */ |
838 | for (aux = rt_hash_table[i].chain;;) { | 857 | length += has_noalias(rt_hash_table[i].chain, rth); |
839 | if (aux == rth) { | ||
840 | length += ONE; | ||
841 | break; | ||
842 | } | ||
843 | if (compare_hash_inputs(&aux->fl, &rth->fl)) | ||
844 | break; | ||
845 | aux = aux->u.dst.rt_next; | ||
846 | } | ||
847 | continue; | 858 | continue; |
848 | } | 859 | } |
849 | } else if (!rt_may_expire(rth, tmo, ip_rt_gc_timeout)) | 860 | } else if (!rt_may_expire(rth, tmo, ip_rt_gc_timeout)) |
@@ -922,10 +933,8 @@ static void rt_secret_rebuild_oneshot(struct net *net) | |||
922 | { | 933 | { |
923 | del_timer_sync(&net->ipv4.rt_secret_timer); | 934 | del_timer_sync(&net->ipv4.rt_secret_timer); |
924 | rt_cache_invalidate(net); | 935 | rt_cache_invalidate(net); |
925 | if (ip_rt_secret_interval) { | 936 | if (ip_rt_secret_interval) |
926 | net->ipv4.rt_secret_timer.expires += ip_rt_secret_interval; | 937 | mod_timer(&net->ipv4.rt_secret_timer, jiffies + ip_rt_secret_interval); |
927 | add_timer(&net->ipv4.rt_secret_timer); | ||
928 | } | ||
929 | } | 938 | } |
930 | 939 | ||
931 | static void rt_emergency_hash_rebuild(struct net *net) | 940 | static void rt_emergency_hash_rebuild(struct net *net) |
@@ -1073,8 +1082,23 @@ work_done: | |||
1073 | out: return 0; | 1082 | out: return 0; |
1074 | } | 1083 | } |
1075 | 1084 | ||
1085 | /* | ||
1086 | * Returns number of entries in a hash chain that have different hash_inputs | ||
1087 | */ | ||
1088 | static int slow_chain_length(const struct rtable *head) | ||
1089 | { | ||
1090 | int length = 0; | ||
1091 | const struct rtable *rth = head; | ||
1092 | |||
1093 | while (rth) { | ||
1094 | length += has_noalias(head, rth); | ||
1095 | rth = rth->u.dst.rt_next; | ||
1096 | } | ||
1097 | return length >> FRACT_BITS; | ||
1098 | } | ||
1099 | |||
1076 | static int rt_intern_hash(unsigned hash, struct rtable *rt, | 1100 | static int rt_intern_hash(unsigned hash, struct rtable *rt, |
1077 | struct rtable **rp, struct sk_buff *skb) | 1101 | struct rtable **rp, struct sk_buff *skb, int ifindex) |
1078 | { | 1102 | { |
1079 | struct rtable *rth, **rthp; | 1103 | struct rtable *rth, **rthp; |
1080 | unsigned long now; | 1104 | unsigned long now; |
@@ -1185,14 +1209,20 @@ restart: | |||
1185 | rt_free(cand); | 1209 | rt_free(cand); |
1186 | } | 1210 | } |
1187 | } else { | 1211 | } else { |
1188 | if (chain_length > rt_chain_length_max) { | 1212 | if (chain_length > rt_chain_length_max && |
1213 | slow_chain_length(rt_hash_table[hash].chain) > rt_chain_length_max) { | ||
1189 | struct net *net = dev_net(rt->u.dst.dev); | 1214 | struct net *net = dev_net(rt->u.dst.dev); |
1190 | int num = ++net->ipv4.current_rt_cache_rebuild_count; | 1215 | int num = ++net->ipv4.current_rt_cache_rebuild_count; |
1191 | if (!rt_caching(dev_net(rt->u.dst.dev))) { | 1216 | if (!rt_caching(net)) { |
1192 | printk(KERN_WARNING "%s: %d rebuilds is over limit, route caching disabled\n", | 1217 | printk(KERN_WARNING "%s: %d rebuilds is over limit, route caching disabled\n", |
1193 | rt->u.dst.dev->name, num); | 1218 | rt->u.dst.dev->name, num); |
1194 | } | 1219 | } |
1195 | rt_emergency_hash_rebuild(dev_net(rt->u.dst.dev)); | 1220 | rt_emergency_hash_rebuild(net); |
1221 | spin_unlock_bh(rt_hash_lock_addr(hash)); | ||
1222 | |||
1223 | hash = rt_hash(rt->fl.fl4_dst, rt->fl.fl4_src, | ||
1224 | ifindex, rt_genid(net)); | ||
1225 | goto restart; | ||
1196 | } | 1226 | } |
1197 | } | 1227 | } |
1198 | 1228 | ||
@@ -1417,7 +1447,7 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw, | |||
1417 | dev_hold(rt->u.dst.dev); | 1447 | dev_hold(rt->u.dst.dev); |
1418 | if (rt->idev) | 1448 | if (rt->idev) |
1419 | in_dev_hold(rt->idev); | 1449 | in_dev_hold(rt->idev); |
1420 | rt->u.dst.obsolete = 0; | 1450 | rt->u.dst.obsolete = -1; |
1421 | rt->u.dst.lastuse = jiffies; | 1451 | rt->u.dst.lastuse = jiffies; |
1422 | rt->u.dst.path = &rt->u.dst; | 1452 | rt->u.dst.path = &rt->u.dst; |
1423 | rt->u.dst.neighbour = NULL; | 1453 | rt->u.dst.neighbour = NULL; |
@@ -1453,7 +1483,7 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw, | |||
1453 | &netevent); | 1483 | &netevent); |
1454 | 1484 | ||
1455 | rt_del(hash, rth); | 1485 | rt_del(hash, rth); |
1456 | if (!rt_intern_hash(hash, rt, &rt, NULL)) | 1486 | if (!rt_intern_hash(hash, rt, &rt, NULL, rt->fl.oif)) |
1457 | ip_rt_put(rt); | 1487 | ip_rt_put(rt); |
1458 | goto do_next; | 1488 | goto do_next; |
1459 | } | 1489 | } |
@@ -1482,11 +1512,12 @@ static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst) | |||
1482 | struct dst_entry *ret = dst; | 1512 | struct dst_entry *ret = dst; |
1483 | 1513 | ||
1484 | if (rt) { | 1514 | if (rt) { |
1485 | if (dst->obsolete) { | 1515 | if (dst->obsolete > 0) { |
1486 | ip_rt_put(rt); | 1516 | ip_rt_put(rt); |
1487 | ret = NULL; | 1517 | ret = NULL; |
1488 | } else if ((rt->rt_flags & RTCF_REDIRECTED) || | 1518 | } else if ((rt->rt_flags & RTCF_REDIRECTED) || |
1489 | rt->u.dst.expires) { | 1519 | (rt->u.dst.expires && |
1520 | time_after_eq(jiffies, rt->u.dst.expires))) { | ||
1490 | unsigned hash = rt_hash(rt->fl.fl4_dst, rt->fl.fl4_src, | 1521 | unsigned hash = rt_hash(rt->fl.fl4_dst, rt->fl.fl4_src, |
1491 | rt->fl.oif, | 1522 | rt->fl.oif, |
1492 | rt_genid(dev_net(dst->dev))); | 1523 | rt_genid(dev_net(dst->dev))); |
@@ -1702,7 +1733,9 @@ static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu) | |||
1702 | 1733 | ||
1703 | static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie) | 1734 | static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie) |
1704 | { | 1735 | { |
1705 | return NULL; | 1736 | if (rt_is_expired((struct rtable *)dst)) |
1737 | return NULL; | ||
1738 | return dst; | ||
1706 | } | 1739 | } |
1707 | 1740 | ||
1708 | static void ipv4_dst_destroy(struct dst_entry *dst) | 1741 | static void ipv4_dst_destroy(struct dst_entry *dst) |
@@ -1864,7 +1897,8 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, | |||
1864 | if (!rth) | 1897 | if (!rth) |
1865 | goto e_nobufs; | 1898 | goto e_nobufs; |
1866 | 1899 | ||
1867 | rth->u.dst.output= ip_rt_bug; | 1900 | rth->u.dst.output = ip_rt_bug; |
1901 | rth->u.dst.obsolete = -1; | ||
1868 | 1902 | ||
1869 | atomic_set(&rth->u.dst.__refcnt, 1); | 1903 | atomic_set(&rth->u.dst.__refcnt, 1); |
1870 | rth->u.dst.flags= DST_HOST; | 1904 | rth->u.dst.flags= DST_HOST; |
@@ -1903,7 +1937,7 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, | |||
1903 | 1937 | ||
1904 | in_dev_put(in_dev); | 1938 | in_dev_put(in_dev); |
1905 | hash = rt_hash(daddr, saddr, dev->ifindex, rt_genid(dev_net(dev))); | 1939 | hash = rt_hash(daddr, saddr, dev->ifindex, rt_genid(dev_net(dev))); |
1906 | return rt_intern_hash(hash, rth, NULL, skb); | 1940 | return rt_intern_hash(hash, rth, NULL, skb, dev->ifindex); |
1907 | 1941 | ||
1908 | e_nobufs: | 1942 | e_nobufs: |
1909 | in_dev_put(in_dev); | 1943 | in_dev_put(in_dev); |
@@ -2030,6 +2064,7 @@ static int __mkroute_input(struct sk_buff *skb, | |||
2030 | rth->fl.oif = 0; | 2064 | rth->fl.oif = 0; |
2031 | rth->rt_spec_dst= spec_dst; | 2065 | rth->rt_spec_dst= spec_dst; |
2032 | 2066 | ||
2067 | rth->u.dst.obsolete = -1; | ||
2033 | rth->u.dst.input = ip_forward; | 2068 | rth->u.dst.input = ip_forward; |
2034 | rth->u.dst.output = ip_output; | 2069 | rth->u.dst.output = ip_output; |
2035 | rth->rt_genid = rt_genid(dev_net(rth->u.dst.dev)); | 2070 | rth->rt_genid = rt_genid(dev_net(rth->u.dst.dev)); |
@@ -2069,7 +2104,7 @@ static int ip_mkroute_input(struct sk_buff *skb, | |||
2069 | /* put it into the cache */ | 2104 | /* put it into the cache */ |
2070 | hash = rt_hash(daddr, saddr, fl->iif, | 2105 | hash = rt_hash(daddr, saddr, fl->iif, |
2071 | rt_genid(dev_net(rth->u.dst.dev))); | 2106 | rt_genid(dev_net(rth->u.dst.dev))); |
2072 | return rt_intern_hash(hash, rth, NULL, skb); | 2107 | return rt_intern_hash(hash, rth, NULL, skb, fl->iif); |
2073 | } | 2108 | } |
2074 | 2109 | ||
2075 | /* | 2110 | /* |
@@ -2194,6 +2229,7 @@ local_input: | |||
2194 | goto e_nobufs; | 2229 | goto e_nobufs; |
2195 | 2230 | ||
2196 | rth->u.dst.output= ip_rt_bug; | 2231 | rth->u.dst.output= ip_rt_bug; |
2232 | rth->u.dst.obsolete = -1; | ||
2197 | rth->rt_genid = rt_genid(net); | 2233 | rth->rt_genid = rt_genid(net); |
2198 | 2234 | ||
2199 | atomic_set(&rth->u.dst.__refcnt, 1); | 2235 | atomic_set(&rth->u.dst.__refcnt, 1); |
@@ -2225,7 +2261,7 @@ local_input: | |||
2225 | } | 2261 | } |
2226 | rth->rt_type = res.type; | 2262 | rth->rt_type = res.type; |
2227 | hash = rt_hash(daddr, saddr, fl.iif, rt_genid(net)); | 2263 | hash = rt_hash(daddr, saddr, fl.iif, rt_genid(net)); |
2228 | err = rt_intern_hash(hash, rth, NULL, skb); | 2264 | err = rt_intern_hash(hash, rth, NULL, skb, fl.iif); |
2229 | goto done; | 2265 | goto done; |
2230 | 2266 | ||
2231 | no_route: | 2267 | no_route: |
@@ -2420,6 +2456,7 @@ static int __mkroute_output(struct rtable **result, | |||
2420 | rth->rt_spec_dst= fl->fl4_src; | 2456 | rth->rt_spec_dst= fl->fl4_src; |
2421 | 2457 | ||
2422 | rth->u.dst.output=ip_output; | 2458 | rth->u.dst.output=ip_output; |
2459 | rth->u.dst.obsolete = -1; | ||
2423 | rth->rt_genid = rt_genid(dev_net(dev_out)); | 2460 | rth->rt_genid = rt_genid(dev_net(dev_out)); |
2424 | 2461 | ||
2425 | RT_CACHE_STAT_INC(out_slow_tot); | 2462 | RT_CACHE_STAT_INC(out_slow_tot); |
@@ -2471,7 +2508,7 @@ static int ip_mkroute_output(struct rtable **rp, | |||
2471 | if (err == 0) { | 2508 | if (err == 0) { |
2472 | hash = rt_hash(oldflp->fl4_dst, oldflp->fl4_src, oldflp->oif, | 2509 | hash = rt_hash(oldflp->fl4_dst, oldflp->fl4_src, oldflp->oif, |
2473 | rt_genid(dev_net(dev_out))); | 2510 | rt_genid(dev_net(dev_out))); |
2474 | err = rt_intern_hash(hash, rth, rp, NULL); | 2511 | err = rt_intern_hash(hash, rth, rp, NULL, oldflp->oif); |
2475 | } | 2512 | } |
2476 | 2513 | ||
2477 | return err; | 2514 | return err; |
@@ -3077,22 +3114,20 @@ static void rt_secret_reschedule(int old) | |||
3077 | rtnl_lock(); | 3114 | rtnl_lock(); |
3078 | for_each_net(net) { | 3115 | for_each_net(net) { |
3079 | int deleted = del_timer_sync(&net->ipv4.rt_secret_timer); | 3116 | int deleted = del_timer_sync(&net->ipv4.rt_secret_timer); |
3117 | long time; | ||
3080 | 3118 | ||
3081 | if (!new) | 3119 | if (!new) |
3082 | continue; | 3120 | continue; |
3083 | 3121 | ||
3084 | if (deleted) { | 3122 | if (deleted) { |
3085 | long time = net->ipv4.rt_secret_timer.expires - jiffies; | 3123 | time = net->ipv4.rt_secret_timer.expires - jiffies; |
3086 | 3124 | ||
3087 | if (time <= 0 || (time += diff) <= 0) | 3125 | if (time <= 0 || (time += diff) <= 0) |
3088 | time = 0; | 3126 | time = 0; |
3089 | |||
3090 | net->ipv4.rt_secret_timer.expires = time; | ||
3091 | } else | 3127 | } else |
3092 | net->ipv4.rt_secret_timer.expires = new; | 3128 | time = new; |
3093 | 3129 | ||
3094 | net->ipv4.rt_secret_timer.expires += jiffies; | 3130 | mod_timer(&net->ipv4.rt_secret_timer, jiffies + time); |
3095 | add_timer(&net->ipv4.rt_secret_timer); | ||
3096 | } | 3131 | } |
3097 | rtnl_unlock(); | 3132 | rtnl_unlock(); |
3098 | } | 3133 | } |