Skip to content

Commit 8fde939

Browse files
Julian AnastasovFlorian Westphal
authored andcommitted
ipvs: do not keep dest_dst if dev is going down
There is race between the netdev notifier ip_vs_dst_event() and the code that caches dst with dev that is going down. As the FIB can be notified for the closed device after our handler finishes, it is possible valid route to be returned and cached resuling in a leaked dev reference until the dest is not removed. To prevent new dest_dst to be attached to dest just after the handler dropped the old one, add a netif_running() check to make sure the notifier handler is not currently running for device that is closing. Fixes: 7a4f076 ("IPVS: init and cleanup restructuring") Signed-off-by: Julian Anastasov <ja@ssi.bg> Signed-off-by: Florian Westphal <fw@strlen.de>
1 parent 05cfe98 commit 8fde939

1 file changed

Lines changed: 36 additions & 10 deletions

File tree

net/netfilter/ipvs/ip_vs_xmit.c

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,12 @@ static inline bool decrement_ttl(struct netns_ipvs *ipvs,
294294
return true;
295295
}
296296

297+
/* rt has device that is down */
298+
static bool rt_dev_is_down(const struct net_device *dev)
299+
{
300+
return dev && !netif_running(dev);
301+
}
302+
297303
/* Get route to destination or remote server */
298304
static int
299305
__ip_vs_get_out_rt(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb,
@@ -309,9 +315,11 @@ __ip_vs_get_out_rt(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb,
309315

310316
if (dest) {
311317
dest_dst = __ip_vs_dst_check(dest);
312-
if (likely(dest_dst))
318+
if (likely(dest_dst)) {
313319
rt = dst_rtable(dest_dst->dst_cache);
314-
else {
320+
if (ret_saddr)
321+
*ret_saddr = dest_dst->dst_saddr.ip;
322+
} else {
315323
dest_dst = ip_vs_dest_dst_alloc();
316324
spin_lock_bh(&dest->dst_lock);
317325
if (!dest_dst) {
@@ -327,14 +335,22 @@ __ip_vs_get_out_rt(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb,
327335
ip_vs_dest_dst_free(dest_dst);
328336
goto err_unreach;
329337
}
330-
__ip_vs_dst_set(dest, dest_dst, &rt->dst, 0);
338+
/* It is forbidden to attach dest->dest_dst if
339+
* device is going down.
340+
*/
341+
if (!rt_dev_is_down(dst_dev_rcu(&rt->dst)))
342+
__ip_vs_dst_set(dest, dest_dst, &rt->dst, 0);
343+
else
344+
noref = 0;
331345
spin_unlock_bh(&dest->dst_lock);
332346
IP_VS_DBG(10, "new dst %pI4, src %pI4, refcnt=%d\n",
333347
&dest->addr.ip, &dest_dst->dst_saddr.ip,
334348
rcuref_read(&rt->dst.__rcuref));
349+
if (ret_saddr)
350+
*ret_saddr = dest_dst->dst_saddr.ip;
351+
if (!noref)
352+
ip_vs_dest_dst_free(dest_dst);
335353
}
336-
if (ret_saddr)
337-
*ret_saddr = dest_dst->dst_saddr.ip;
338354
} else {
339355
noref = 0;
340356

@@ -471,9 +487,11 @@ __ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb,
471487

472488
if (dest) {
473489
dest_dst = __ip_vs_dst_check(dest);
474-
if (likely(dest_dst))
490+
if (likely(dest_dst)) {
475491
rt = dst_rt6_info(dest_dst->dst_cache);
476-
else {
492+
if (ret_saddr)
493+
*ret_saddr = dest_dst->dst_saddr.in6;
494+
} else {
477495
u32 cookie;
478496

479497
dest_dst = ip_vs_dest_dst_alloc();
@@ -494,14 +512,22 @@ __ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb,
494512
}
495513
rt = dst_rt6_info(dst);
496514
cookie = rt6_get_cookie(rt);
497-
__ip_vs_dst_set(dest, dest_dst, &rt->dst, cookie);
515+
/* It is forbidden to attach dest->dest_dst if
516+
* device is going down.
517+
*/
518+
if (!rt_dev_is_down(dst_dev_rcu(&rt->dst)))
519+
__ip_vs_dst_set(dest, dest_dst, &rt->dst, cookie);
520+
else
521+
noref = 0;
498522
spin_unlock_bh(&dest->dst_lock);
499523
IP_VS_DBG(10, "new dst %pI6, src %pI6, refcnt=%d\n",
500524
&dest->addr.in6, &dest_dst->dst_saddr.in6,
501525
rcuref_read(&rt->dst.__rcuref));
526+
if (ret_saddr)
527+
*ret_saddr = dest_dst->dst_saddr.in6;
528+
if (!noref)
529+
ip_vs_dest_dst_free(dest_dst);
502530
}
503-
if (ret_saddr)
504-
*ret_saddr = dest_dst->dst_saddr.in6;
505531
} else {
506532
noref = 0;
507533
dst = __ip_vs_route_output_v6(net, daddr, ret_saddr, do_xfrm,

0 commit comments

Comments
 (0)