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>
This commit is contained in:
parent
f7594d4294
commit
436c3b66ec
|
@ -62,6 +62,7 @@ struct fib_nh {
|
||||||
int nh_oif;
|
int nh_oif;
|
||||||
__be32 nh_gw;
|
__be32 nh_gw;
|
||||||
__be32 nh_saddr;
|
__be32 nh_saddr;
|
||||||
|
int nh_saddr_genid;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -141,12 +142,19 @@ struct fib_result_nl {
|
||||||
|
|
||||||
#endif /* CONFIG_IP_ROUTE_MULTIPATH */
|
#endif /* CONFIG_IP_ROUTE_MULTIPATH */
|
||||||
|
|
||||||
#define FIB_RES_SADDR(res) (FIB_RES_NH(res).nh_saddr)
|
extern __be32 fib_info_update_nh_saddr(struct net *net, struct fib_nh *nh);
|
||||||
|
|
||||||
|
#define FIB_RES_SADDR(net, res) \
|
||||||
|
((FIB_RES_NH(res).nh_saddr_genid == \
|
||||||
|
atomic_read(&(net)->ipv4.dev_addr_genid)) ? \
|
||||||
|
FIB_RES_NH(res).nh_saddr : \
|
||||||
|
fib_info_update_nh_saddr((net), &FIB_RES_NH(res)))
|
||||||
#define FIB_RES_GW(res) (FIB_RES_NH(res).nh_gw)
|
#define FIB_RES_GW(res) (FIB_RES_NH(res).nh_gw)
|
||||||
#define FIB_RES_DEV(res) (FIB_RES_NH(res).nh_dev)
|
#define FIB_RES_DEV(res) (FIB_RES_NH(res).nh_dev)
|
||||||
#define FIB_RES_OIF(res) (FIB_RES_NH(res).nh_oif)
|
#define FIB_RES_OIF(res) (FIB_RES_NH(res).nh_oif)
|
||||||
|
|
||||||
#define FIB_RES_PREFSRC(res) ((res).fi->fib_prefsrc ? : FIB_RES_SADDR(res))
|
#define FIB_RES_PREFSRC(net, res) ((res).fi->fib_prefsrc ? : \
|
||||||
|
FIB_RES_SADDR(net, res))
|
||||||
|
|
||||||
struct fib_table {
|
struct fib_table {
|
||||||
struct hlist_node tb_hlist;
|
struct hlist_node tb_hlist;
|
||||||
|
|
|
@ -55,6 +55,7 @@ struct netns_ipv4 {
|
||||||
int current_rt_cache_rebuild_count;
|
int current_rt_cache_rebuild_count;
|
||||||
|
|
||||||
atomic_t rt_genid;
|
atomic_t rt_genid;
|
||||||
|
atomic_t dev_addr_genid;
|
||||||
|
|
||||||
#ifdef CONFIG_IP_MROUTE
|
#ifdef CONFIG_IP_MROUTE
|
||||||
#ifndef CONFIG_IP_MROUTE_MULTIPLE_TABLES
|
#ifndef CONFIG_IP_MROUTE_MULTIPLE_TABLES
|
||||||
|
|
|
@ -228,7 +228,7 @@ int fib_validate_source(__be32 src, __be32 dst, u8 tos, int oif,
|
||||||
if (res.type != RTN_LOCAL || !accept_local)
|
if (res.type != RTN_LOCAL || !accept_local)
|
||||||
goto e_inval;
|
goto e_inval;
|
||||||
}
|
}
|
||||||
*spec_dst = FIB_RES_PREFSRC(res);
|
*spec_dst = FIB_RES_PREFSRC(net, res);
|
||||||
fib_combine_itag(itag, &res);
|
fib_combine_itag(itag, &res);
|
||||||
dev_match = false;
|
dev_match = false;
|
||||||
|
|
||||||
|
@ -258,7 +258,7 @@ int fib_validate_source(__be32 src, __be32 dst, u8 tos, int oif,
|
||||||
ret = 0;
|
ret = 0;
|
||||||
if (fib_lookup(net, &fl4, &res) == 0) {
|
if (fib_lookup(net, &fl4, &res) == 0) {
|
||||||
if (res.type == RTN_UNICAST) {
|
if (res.type == RTN_UNICAST) {
|
||||||
*spec_dst = FIB_RES_PREFSRC(res);
|
*spec_dst = FIB_RES_PREFSRC(net, res);
|
||||||
ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
|
ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -960,6 +960,7 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event,
|
||||||
{
|
{
|
||||||
struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
|
struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
|
||||||
struct net_device *dev = ifa->ifa_dev->dev;
|
struct net_device *dev = ifa->ifa_dev->dev;
|
||||||
|
struct net *net = dev_net(dev);
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case NETDEV_UP:
|
case NETDEV_UP:
|
||||||
|
@ -967,12 +968,12 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event,
|
||||||
#ifdef CONFIG_IP_ROUTE_MULTIPATH
|
#ifdef CONFIG_IP_ROUTE_MULTIPATH
|
||||||
fib_sync_up(dev);
|
fib_sync_up(dev);
|
||||||
#endif
|
#endif
|
||||||
fib_update_nh_saddrs(dev);
|
atomic_inc(&net->ipv4.dev_addr_genid);
|
||||||
rt_cache_flush(dev_net(dev), -1);
|
rt_cache_flush(dev_net(dev), -1);
|
||||||
break;
|
break;
|
||||||
case NETDEV_DOWN:
|
case NETDEV_DOWN:
|
||||||
fib_del_ifaddr(ifa, NULL);
|
fib_del_ifaddr(ifa, NULL);
|
||||||
fib_update_nh_saddrs(dev);
|
atomic_inc(&net->ipv4.dev_addr_genid);
|
||||||
if (ifa->ifa_dev->ifa_list == NULL) {
|
if (ifa->ifa_dev->ifa_list == NULL) {
|
||||||
/* Last address was deleted from this interface.
|
/* Last address was deleted from this interface.
|
||||||
* Disable IP.
|
* Disable IP.
|
||||||
|
@ -990,6 +991,7 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo
|
||||||
{
|
{
|
||||||
struct net_device *dev = ptr;
|
struct net_device *dev = ptr;
|
||||||
struct in_device *in_dev = __in_dev_get_rtnl(dev);
|
struct in_device *in_dev = __in_dev_get_rtnl(dev);
|
||||||
|
struct net *net = dev_net(dev);
|
||||||
|
|
||||||
if (event == NETDEV_UNREGISTER) {
|
if (event == NETDEV_UNREGISTER) {
|
||||||
fib_disable_ip(dev, 2, -1);
|
fib_disable_ip(dev, 2, -1);
|
||||||
|
@ -1007,6 +1009,7 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo
|
||||||
#ifdef CONFIG_IP_ROUTE_MULTIPATH
|
#ifdef CONFIG_IP_ROUTE_MULTIPATH
|
||||||
fib_sync_up(dev);
|
fib_sync_up(dev);
|
||||||
#endif
|
#endif
|
||||||
|
atomic_inc(&net->ipv4.dev_addr_genid);
|
||||||
rt_cache_flush(dev_net(dev), -1);
|
rt_cache_flush(dev_net(dev), -1);
|
||||||
break;
|
break;
|
||||||
case NETDEV_DOWN:
|
case NETDEV_DOWN:
|
||||||
|
|
|
@ -695,6 +695,16 @@ static void fib_info_hash_move(struct hlist_head *new_info_hash,
|
||||||
fib_info_hash_free(old_laddrhash, bytes);
|
fib_info_hash_free(old_laddrhash, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__be32 fib_info_update_nh_saddr(struct net *net, struct fib_nh *nh)
|
||||||
|
{
|
||||||
|
nh->nh_saddr = inet_select_addr(nh->nh_dev,
|
||||||
|
nh->nh_gw,
|
||||||
|
nh->nh_cfg_scope);
|
||||||
|
nh->nh_saddr_genid = atomic_read(&net->ipv4.dev_addr_genid);
|
||||||
|
|
||||||
|
return nh->nh_saddr;
|
||||||
|
}
|
||||||
|
|
||||||
struct fib_info *fib_create_info(struct fib_config *cfg)
|
struct fib_info *fib_create_info(struct fib_config *cfg)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
@ -855,9 +865,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
|
||||||
|
|
||||||
change_nexthops(fi) {
|
change_nexthops(fi) {
|
||||||
nexthop_nh->nh_cfg_scope = cfg->fc_scope;
|
nexthop_nh->nh_cfg_scope = cfg->fc_scope;
|
||||||
nexthop_nh->nh_saddr = inet_select_addr(nexthop_nh->nh_dev,
|
fib_info_update_nh_saddr(net, nexthop_nh);
|
||||||
nexthop_nh->nh_gw,
|
|
||||||
nexthop_nh->nh_cfg_scope);
|
|
||||||
} endfor_nexthops(fi)
|
} endfor_nexthops(fi)
|
||||||
|
|
||||||
link_it:
|
link_it:
|
||||||
|
@ -1128,24 +1136,6 @@ out:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fib_update_nh_saddrs(struct net_device *dev)
|
|
||||||
{
|
|
||||||
struct hlist_head *head;
|
|
||||||
struct hlist_node *node;
|
|
||||||
struct fib_nh *nh;
|
|
||||||
unsigned int hash;
|
|
||||||
|
|
||||||
hash = fib_devindex_hashfn(dev->ifindex);
|
|
||||||
head = &fib_info_devhash[hash];
|
|
||||||
hlist_for_each_entry(nh, node, head, nh_hash) {
|
|
||||||
if (nh->nh_dev != dev)
|
|
||||||
continue;
|
|
||||||
nh->nh_saddr = inet_select_addr(nh->nh_dev,
|
|
||||||
nh->nh_gw,
|
|
||||||
nh->nh_cfg_scope);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_IP_ROUTE_MULTIPATH
|
#ifdef CONFIG_IP_ROUTE_MULTIPATH
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -1718,7 +1718,7 @@ void ip_rt_get_source(u8 *addr, struct rtable *rt)
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
if (fib_lookup(dev_net(rt->dst.dev), &fl4, &res) == 0)
|
if (fib_lookup(dev_net(rt->dst.dev), &fl4, &res) == 0)
|
||||||
src = FIB_RES_PREFSRC(res);
|
src = FIB_RES_PREFSRC(dev_net(rt->dst.dev), res);
|
||||||
else
|
else
|
||||||
src = inet_select_addr(rt->dst.dev, rt->rt_gateway,
|
src = inet_select_addr(rt->dst.dev, rt->rt_gateway,
|
||||||
RT_SCOPE_UNIVERSE);
|
RT_SCOPE_UNIVERSE);
|
||||||
|
@ -2615,7 +2615,7 @@ static struct rtable *ip_route_output_slow(struct net *net,
|
||||||
fib_select_default(&res);
|
fib_select_default(&res);
|
||||||
|
|
||||||
if (!fl4.saddr)
|
if (!fl4.saddr)
|
||||||
fl4.saddr = FIB_RES_PREFSRC(res);
|
fl4.saddr = FIB_RES_PREFSRC(net, res);
|
||||||
|
|
||||||
dev_out = FIB_RES_DEV(res);
|
dev_out = FIB_RES_DEV(res);
|
||||||
fl4.flowi4_oif = dev_out->ifindex;
|
fl4.flowi4_oif = dev_out->ifindex;
|
||||||
|
@ -3219,6 +3219,8 @@ static __net_init int rt_genid_init(struct net *net)
|
||||||
{
|
{
|
||||||
get_random_bytes(&net->ipv4.rt_genid,
|
get_random_bytes(&net->ipv4.rt_genid,
|
||||||
sizeof(net->ipv4.rt_genid));
|
sizeof(net->ipv4.rt_genid));
|
||||||
|
get_random_bytes(&net->ipv4.dev_addr_genid,
|
||||||
|
sizeof(net->ipv4.dev_addr_genid));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue