Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipv6: probe routes asynchronous in rt6_probe

Routes need to be probed asynchronous otherwise the call stack gets
exhausted when the kernel attemps to deliver another skb inline, like
e.g. xt_TEE does, and we probe at the same time.

We update neigh->updated still at once, otherwise we would send to
many probes.

Cc: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Hannes Frederic Sowa and committed by
David S. Miller
c2f17e82 3a70417c

+32 -8
+32 -8
net/ipv6/route.c
··· 476 476 } 477 477 478 478 #ifdef CONFIG_IPV6_ROUTER_PREF 479 + struct __rt6_probe_work { 480 + struct work_struct work; 481 + struct in6_addr target; 482 + struct net_device *dev; 483 + }; 484 + 485 + static void rt6_probe_deferred(struct work_struct *w) 486 + { 487 + struct in6_addr mcaddr; 488 + struct __rt6_probe_work *work = 489 + container_of(w, struct __rt6_probe_work, work); 490 + 491 + addrconf_addr_solict_mult(&work->target, &mcaddr); 492 + ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL); 493 + dev_put(work->dev); 494 + kfree(w); 495 + } 496 + 479 497 static void rt6_probe(struct rt6_info *rt) 480 498 { 481 499 struct neighbour *neigh; ··· 517 499 518 500 if (!neigh || 519 501 time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) { 520 - struct in6_addr mcaddr; 521 - struct in6_addr *target; 502 + struct __rt6_probe_work *work; 522 503 523 - if (neigh) { 504 + work = kmalloc(sizeof(*work), GFP_ATOMIC); 505 + 506 + if (neigh && work) 524 507 neigh->updated = jiffies; 525 - write_unlock(&neigh->lock); 526 - } 527 508 528 - target = (struct in6_addr *)&rt->rt6i_gateway; 529 - addrconf_addr_solict_mult(target, &mcaddr); 530 - ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL); 509 + if (neigh) 510 + write_unlock(&neigh->lock); 511 + 512 + if (work) { 513 + INIT_WORK(&work->work, rt6_probe_deferred); 514 + work->target = rt->rt6i_gateway; 515 + dev_hold(rt->dst.dev); 516 + work->dev = rt->dst.dev; 517 + schedule_work(&work->work); 518 + } 531 519 } else { 532 520 out: 533 521 write_unlock(&neigh->lock);