aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2011-03-24 20:42:21 -0400
committerDavid S. Miller <davem@davemloft.net>2011-03-24 20:42:21 -0400
commit436c3b66ec9824a633724ae42de1c416af4f2063 (patch)
tree8da6452386b6e900c4226c9b67694d1ea21e847e
parentf7594d42944c0dfca90318f50978a4bdf8504086 (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.h12
-rw-r--r--include/net/netns/ipv4.h1
-rw-r--r--net/ipv4/fib_frontend.c11
-rw-r--r--net/ipv4/fib_semantics.c32
-rw-r--r--net/ipv4/route.c6
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) 145extern __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
151struct fib_table { 159struct 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
698struct fib_info *fib_create_info(struct fib_config *cfg) 708struct 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
863link_it: 871link_it:
@@ -1128,24 +1136,6 @@ out:
1128 return; 1136 return;
1129} 1137}
1130 1138
1131void 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