diff options
author | David S. Miller <davem@davemloft.net> | 2011-03-24 20:42:21 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-03-24 20:42:21 -0400 |
commit | 436c3b66ec9824a633724ae42de1c416af4f2063 (patch) | |
tree | 8da6452386b6e900c4226c9b67694d1ea21e847e | |
parent | f7594d42944c0dfca90318f50978a4bdf8504086 (diff) |
ipv4: Invalidate nexthop cache nh_saddr more correctly.
Any operation that:
1) Brings up an interface
2) Adds an IP address to an interface
3) Deletes an IP address from an interface
can potentially invalidate the nh_saddr value, requiring
it to be recomputed.
Perform the recomputation lazily using a generation ID.
Reported-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/ip_fib.h | 12 | ||||
-rw-r--r-- | include/net/netns/ipv4.h | 1 | ||||
-rw-r--r-- | net/ipv4/fib_frontend.c | 11 | ||||
-rw-r--r-- | net/ipv4/fib_semantics.c | 32 | ||||
-rw-r--r-- | net/ipv4/route.c | 6 |
5 files changed, 33 insertions, 29 deletions
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index a1a858035913..cd92b923a578 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h | |||
@@ -62,6 +62,7 @@ struct fib_nh { | |||
62 | int nh_oif; | 62 | int nh_oif; |
63 | __be32 nh_gw; | 63 | __be32 nh_gw; |
64 | __be32 nh_saddr; | 64 | __be32 nh_saddr; |
65 | int nh_saddr_genid; | ||
65 | }; | 66 | }; |
66 | 67 | ||
67 | /* | 68 | /* |
@@ -141,12 +142,19 @@ struct fib_result_nl { | |||
141 | 142 | ||
142 | #endif /* CONFIG_IP_ROUTE_MULTIPATH */ | 143 | #endif /* CONFIG_IP_ROUTE_MULTIPATH */ |
143 | 144 | ||
144 | #define FIB_RES_SADDR(res) (FIB_RES_NH(res).nh_saddr) | 145 | extern __be32 fib_info_update_nh_saddr(struct net *net, struct fib_nh *nh); |
146 | |||
147 | #define FIB_RES_SADDR(net, res) \ | ||
148 | ((FIB_RES_NH(res).nh_saddr_genid == \ | ||
149 | atomic_read(&(net)->ipv4.dev_addr_genid)) ? \ | ||
150 | FIB_RES_NH(res).nh_saddr : \ | ||
151 | fib_info_update_nh_saddr((net), &FIB_RES_NH(res))) | ||
145 | #define FIB_RES_GW(res) (FIB_RES_NH(res).nh_gw) | 152 | #define FIB_RES_GW(res) (FIB_RES_NH(res).nh_gw) |
146 | #define FIB_RES_DEV(res) (FIB_RES_NH(res).nh_dev) | 153 | #define FIB_RES_DEV(res) (FIB_RES_NH(res).nh_dev) |
147 | #define FIB_RES_OIF(res) (FIB_RES_NH(res).nh_oif) | 154 | #define FIB_RES_OIF(res) (FIB_RES_NH(res).nh_oif) |
148 | 155 | ||
149 | #define FIB_RES_PREFSRC(res) ((res).fi->fib_prefsrc ? : FIB_RES_SADDR(res)) | 156 | #define FIB_RES_PREFSRC(net, res) ((res).fi->fib_prefsrc ? : \ |
157 | FIB_RES_SADDR(net, res)) | ||
150 | 158 | ||
151 | struct fib_table { | 159 | struct fib_table { |
152 | struct hlist_node tb_hlist; | 160 | struct hlist_node tb_hlist; |
diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index e2e2ef57eca2..542195d9469e 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h | |||
@@ -55,6 +55,7 @@ struct netns_ipv4 { | |||
55 | int current_rt_cache_rebuild_count; | 55 | int current_rt_cache_rebuild_count; |
56 | 56 | ||
57 | atomic_t rt_genid; | 57 | atomic_t rt_genid; |
58 | atomic_t dev_addr_genid; | ||
58 | 59 | ||
59 | #ifdef CONFIG_IP_MROUTE | 60 | #ifdef CONFIG_IP_MROUTE |
60 | #ifndef CONFIG_IP_MROUTE_MULTIPLE_TABLES | 61 | #ifndef CONFIG_IP_MROUTE_MULTIPLE_TABLES |
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 02c3ba61884a..f116ce8f1b46 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c | |||
@@ -228,7 +228,7 @@ int fib_validate_source(__be32 src, __be32 dst, u8 tos, int oif, | |||
228 | if (res.type != RTN_LOCAL || !accept_local) | 228 | if (res.type != RTN_LOCAL || !accept_local) |
229 | goto e_inval; | 229 | goto e_inval; |
230 | } | 230 | } |
231 | *spec_dst = FIB_RES_PREFSRC(res); | 231 | *spec_dst = FIB_RES_PREFSRC(net, res); |
232 | fib_combine_itag(itag, &res); | 232 | fib_combine_itag(itag, &res); |
233 | dev_match = false; | 233 | dev_match = false; |
234 | 234 | ||
@@ -258,7 +258,7 @@ int fib_validate_source(__be32 src, __be32 dst, u8 tos, int oif, | |||
258 | ret = 0; | 258 | ret = 0; |
259 | if (fib_lookup(net, &fl4, &res) == 0) { | 259 | if (fib_lookup(net, &fl4, &res) == 0) { |
260 | if (res.type == RTN_UNICAST) { | 260 | if (res.type == RTN_UNICAST) { |
261 | *spec_dst = FIB_RES_PREFSRC(res); | 261 | *spec_dst = FIB_RES_PREFSRC(net, res); |
262 | ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST; | 262 | ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST; |
263 | } | 263 | } |
264 | } | 264 | } |
@@ -960,6 +960,7 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event, | |||
960 | { | 960 | { |
961 | struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; | 961 | struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; |
962 | struct net_device *dev = ifa->ifa_dev->dev; | 962 | struct net_device *dev = ifa->ifa_dev->dev; |
963 | struct net *net = dev_net(dev); | ||
963 | 964 | ||
964 | switch (event) { | 965 | switch (event) { |
965 | case NETDEV_UP: | 966 | case NETDEV_UP: |
@@ -967,12 +968,12 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event, | |||
967 | #ifdef CONFIG_IP_ROUTE_MULTIPATH | 968 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
968 | fib_sync_up(dev); | 969 | fib_sync_up(dev); |
969 | #endif | 970 | #endif |
970 | fib_update_nh_saddrs(dev); | 971 | atomic_inc(&net->ipv4.dev_addr_genid); |
971 | rt_cache_flush(dev_net(dev), -1); | 972 | rt_cache_flush(dev_net(dev), -1); |
972 | break; | 973 | break; |
973 | case NETDEV_DOWN: | 974 | case NETDEV_DOWN: |
974 | fib_del_ifaddr(ifa, NULL); | 975 | fib_del_ifaddr(ifa, NULL); |
975 | fib_update_nh_saddrs(dev); | 976 | atomic_inc(&net->ipv4.dev_addr_genid); |
976 | if (ifa->ifa_dev->ifa_list == NULL) { | 977 | if (ifa->ifa_dev->ifa_list == NULL) { |
977 | /* Last address was deleted from this interface. | 978 | /* Last address was deleted from this interface. |
978 | * Disable IP. | 979 | * Disable IP. |
@@ -990,6 +991,7 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo | |||
990 | { | 991 | { |
991 | struct net_device *dev = ptr; | 992 | struct net_device *dev = ptr; |
992 | struct in_device *in_dev = __in_dev_get_rtnl(dev); | 993 | struct in_device *in_dev = __in_dev_get_rtnl(dev); |
994 | struct net *net = dev_net(dev); | ||
993 | 995 | ||
994 | if (event == NETDEV_UNREGISTER) { | 996 | if (event == NETDEV_UNREGISTER) { |
995 | fib_disable_ip(dev, 2, -1); | 997 | fib_disable_ip(dev, 2, -1); |
@@ -1007,6 +1009,7 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo | |||
1007 | #ifdef CONFIG_IP_ROUTE_MULTIPATH | 1009 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
1008 | fib_sync_up(dev); | 1010 | fib_sync_up(dev); |
1009 | #endif | 1011 | #endif |
1012 | atomic_inc(&net->ipv4.dev_addr_genid); | ||
1010 | rt_cache_flush(dev_net(dev), -1); | 1013 | rt_cache_flush(dev_net(dev), -1); |
1011 | break; | 1014 | break; |
1012 | case NETDEV_DOWN: | 1015 | case NETDEV_DOWN: |
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 75b9fb5d5d37..2d4bebca671a 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c | |||
@@ -695,6 +695,16 @@ static void fib_info_hash_move(struct hlist_head *new_info_hash, | |||
695 | fib_info_hash_free(old_laddrhash, bytes); | 695 | fib_info_hash_free(old_laddrhash, bytes); |
696 | } | 696 | } |
697 | 697 | ||
698 | __be32 fib_info_update_nh_saddr(struct net *net, struct fib_nh *nh) | ||
699 | { | ||
700 | nh->nh_saddr = inet_select_addr(nh->nh_dev, | ||
701 | nh->nh_gw, | ||
702 | nh->nh_cfg_scope); | ||
703 | nh->nh_saddr_genid = atomic_read(&net->ipv4.dev_addr_genid); | ||
704 | |||
705 | return nh->nh_saddr; | ||
706 | } | ||
707 | |||
698 | struct fib_info *fib_create_info(struct fib_config *cfg) | 708 | struct fib_info *fib_create_info(struct fib_config *cfg) |
699 | { | 709 | { |
700 | int err; | 710 | int err; |
@@ -855,9 +865,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg) | |||
855 | 865 | ||
856 | change_nexthops(fi) { | 866 | change_nexthops(fi) { |
857 | nexthop_nh->nh_cfg_scope = cfg->fc_scope; | 867 | nexthop_nh->nh_cfg_scope = cfg->fc_scope; |
858 | nexthop_nh->nh_saddr = inet_select_addr(nexthop_nh->nh_dev, | 868 | fib_info_update_nh_saddr(net, nexthop_nh); |
859 | nexthop_nh->nh_gw, | ||
860 | nexthop_nh->nh_cfg_scope); | ||
861 | } endfor_nexthops(fi) | 869 | } endfor_nexthops(fi) |
862 | 870 | ||
863 | link_it: | 871 | link_it: |
@@ -1128,24 +1136,6 @@ out: | |||
1128 | return; | 1136 | return; |
1129 | } | 1137 | } |
1130 | 1138 | ||
1131 | void fib_update_nh_saddrs(struct net_device *dev) | ||
1132 | { | ||
1133 | struct hlist_head *head; | ||
1134 | struct hlist_node *node; | ||
1135 | struct fib_nh *nh; | ||
1136 | unsigned int hash; | ||
1137 | |||
1138 | hash = fib_devindex_hashfn(dev->ifindex); | ||
1139 | head = &fib_info_devhash[hash]; | ||
1140 | hlist_for_each_entry(nh, node, head, nh_hash) { | ||
1141 | if (nh->nh_dev != dev) | ||
1142 | continue; | ||
1143 | nh->nh_saddr = inet_select_addr(nh->nh_dev, | ||
1144 | nh->nh_gw, | ||
1145 | nh->nh_cfg_scope); | ||
1146 | } | ||
1147 | } | ||
1148 | |||
1149 | #ifdef CONFIG_IP_ROUTE_MULTIPATH | 1139 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
1150 | 1140 | ||
1151 | /* | 1141 | /* |
diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 34921b0d3f8e..4b0c81180804 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c | |||
@@ -1718,7 +1718,7 @@ void ip_rt_get_source(u8 *addr, struct rtable *rt) | |||
1718 | 1718 | ||
1719 | rcu_read_lock(); | 1719 | rcu_read_lock(); |
1720 | if (fib_lookup(dev_net(rt->dst.dev), &fl4, &res) == 0) | 1720 | if (fib_lookup(dev_net(rt->dst.dev), &fl4, &res) == 0) |
1721 | src = FIB_RES_PREFSRC(res); | 1721 | src = FIB_RES_PREFSRC(dev_net(rt->dst.dev), res); |
1722 | else | 1722 | else |
1723 | src = inet_select_addr(rt->dst.dev, rt->rt_gateway, | 1723 | src = inet_select_addr(rt->dst.dev, rt->rt_gateway, |
1724 | RT_SCOPE_UNIVERSE); | 1724 | RT_SCOPE_UNIVERSE); |
@@ -2615,7 +2615,7 @@ static struct rtable *ip_route_output_slow(struct net *net, | |||
2615 | fib_select_default(&res); | 2615 | fib_select_default(&res); |
2616 | 2616 | ||
2617 | if (!fl4.saddr) | 2617 | if (!fl4.saddr) |
2618 | fl4.saddr = FIB_RES_PREFSRC(res); | 2618 | fl4.saddr = FIB_RES_PREFSRC(net, res); |
2619 | 2619 | ||
2620 | dev_out = FIB_RES_DEV(res); | 2620 | dev_out = FIB_RES_DEV(res); |
2621 | fl4.flowi4_oif = dev_out->ifindex; | 2621 | fl4.flowi4_oif = dev_out->ifindex; |
@@ -3219,6 +3219,8 @@ static __net_init int rt_genid_init(struct net *net) | |||
3219 | { | 3219 | { |
3220 | get_random_bytes(&net->ipv4.rt_genid, | 3220 | get_random_bytes(&net->ipv4.rt_genid, |
3221 | sizeof(net->ipv4.rt_genid)); | 3221 | sizeof(net->ipv4.rt_genid)); |
3222 | get_random_bytes(&net->ipv4.dev_addr_genid, | ||
3223 | sizeof(net->ipv4.dev_addr_genid)); | ||
3222 | return 0; | 3224 | return 0; |
3223 | } | 3225 | } |
3224 | 3226 | ||