Skip to content

Commit 436c3b6

Browse files
committed
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>
1 parent f7594d4 commit 436c3b6

File tree

5 files changed

+33
-29
lines changed

5 files changed

+33
-29
lines changed

include/net/ip_fib.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ struct fib_nh {
6262
int nh_oif;
6363
__be32 nh_gw;
6464
__be32 nh_saddr;
65+
int nh_saddr_genid;
6566
};
6667

6768
/*
@@ -141,12 +142,19 @@ struct fib_result_nl {
141142

142143
#endif /* CONFIG_IP_ROUTE_MULTIPATH */
143144

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)))
145152
#define FIB_RES_GW(res) (FIB_RES_NH(res).nh_gw)
146153
#define FIB_RES_DEV(res) (FIB_RES_NH(res).nh_dev)
147154
#define FIB_RES_OIF(res) (FIB_RES_NH(res).nh_oif)
148155

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))
150158

151159
struct fib_table {
152160
struct hlist_node tb_hlist;

include/net/netns/ipv4.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ struct netns_ipv4 {
5555
int current_rt_cache_rebuild_count;
5656

5757
atomic_t rt_genid;
58+
atomic_t dev_addr_genid;
5859

5960
#ifdef CONFIG_IP_MROUTE
6061
#ifndef CONFIG_IP_MROUTE_MULTIPLE_TABLES

net/ipv4/fib_frontend.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ int fib_validate_source(__be32 src, __be32 dst, u8 tos, int oif,
228228
if (res.type != RTN_LOCAL || !accept_local)
229229
goto e_inval;
230230
}
231-
*spec_dst = FIB_RES_PREFSRC(res);
231+
*spec_dst = FIB_RES_PREFSRC(net, res);
232232
fib_combine_itag(itag, &res);
233233
dev_match = false;
234234

@@ -258,7 +258,7 @@ int fib_validate_source(__be32 src, __be32 dst, u8 tos, int oif,
258258
ret = 0;
259259
if (fib_lookup(net, &fl4, &res) == 0) {
260260
if (res.type == RTN_UNICAST) {
261-
*spec_dst = FIB_RES_PREFSRC(res);
261+
*spec_dst = FIB_RES_PREFSRC(net, res);
262262
ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
263263
}
264264
}
@@ -960,19 +960,20 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event,
960960
{
961961
struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
962962
struct net_device *dev = ifa->ifa_dev->dev;
963+
struct net *net = dev_net(dev);
963964

964965
switch (event) {
965966
case NETDEV_UP:
966967
fib_add_ifaddr(ifa);
967968
#ifdef CONFIG_IP_ROUTE_MULTIPATH
968969
fib_sync_up(dev);
969970
#endif
970-
fib_update_nh_saddrs(dev);
971+
atomic_inc(&net->ipv4.dev_addr_genid);
971972
rt_cache_flush(dev_net(dev), -1);
972973
break;
973974
case NETDEV_DOWN:
974975
fib_del_ifaddr(ifa, NULL);
975-
fib_update_nh_saddrs(dev);
976+
atomic_inc(&net->ipv4.dev_addr_genid);
976977
if (ifa->ifa_dev->ifa_list == NULL) {
977978
/* Last address was deleted from this interface.
978979
* Disable IP.
@@ -990,6 +991,7 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo
990991
{
991992
struct net_device *dev = ptr;
992993
struct in_device *in_dev = __in_dev_get_rtnl(dev);
994+
struct net *net = dev_net(dev);
993995

994996
if (event == NETDEV_UNREGISTER) {
995997
fib_disable_ip(dev, 2, -1);
@@ -1007,6 +1009,7 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo
10071009
#ifdef CONFIG_IP_ROUTE_MULTIPATH
10081010
fib_sync_up(dev);
10091011
#endif
1012+
atomic_inc(&net->ipv4.dev_addr_genid);
10101013
rt_cache_flush(dev_net(dev), -1);
10111014
break;
10121015
case NETDEV_DOWN:

net/ipv4/fib_semantics.c

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,16 @@ static void fib_info_hash_move(struct hlist_head *new_info_hash,
695695
fib_info_hash_free(old_laddrhash, bytes);
696696
}
697697

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+
698708
struct fib_info *fib_create_info(struct fib_config *cfg)
699709
{
700710
int err;
@@ -855,9 +865,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
855865

856866
change_nexthops(fi) {
857867
nexthop_nh->nh_cfg_scope = cfg->fc_scope;
858-
nexthop_nh->nh_saddr = inet_select_addr(nexthop_nh->nh_dev,
859-
nexthop_nh->nh_gw,
860-
nexthop_nh->nh_cfg_scope);
868+
fib_info_update_nh_saddr(net, nexthop_nh);
861869
} endfor_nexthops(fi)
862870

863871
link_it:
@@ -1128,24 +1136,6 @@ void fib_select_default(struct fib_result *res)
11281136
return;
11291137
}
11301138

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-
11491139
#ifdef CONFIG_IP_ROUTE_MULTIPATH
11501140

11511141
/*

net/ipv4/route.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1718,7 +1718,7 @@ void ip_rt_get_source(u8 *addr, struct rtable *rt)
17181718

17191719
rcu_read_lock();
17201720
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);
17221722
else
17231723
src = inet_select_addr(rt->dst.dev, rt->rt_gateway,
17241724
RT_SCOPE_UNIVERSE);
@@ -2615,7 +2615,7 @@ static struct rtable *ip_route_output_slow(struct net *net,
26152615
fib_select_default(&res);
26162616

26172617
if (!fl4.saddr)
2618-
fl4.saddr = FIB_RES_PREFSRC(res);
2618+
fl4.saddr = FIB_RES_PREFSRC(net, res);
26192619

26202620
dev_out = FIB_RES_DEV(res);
26212621
fl4.flowi4_oif = dev_out->ifindex;
@@ -3219,6 +3219,8 @@ static __net_init int rt_genid_init(struct net *net)
32193219
{
32203220
get_random_bytes(&net->ipv4.rt_genid,
32213221
sizeof(net->ipv4.rt_genid));
3222+
get_random_bytes(&net->ipv4.dev_addr_genid,
3223+
sizeof(net->ipv4.dev_addr_genid));
32223224
return 0;
32233225
}
32243226

0 commit comments

Comments
 (0)