Subject: Re: oops in net/ipv4/icmp.c:icmp_send() with icmp_errors_use_inbound_ifaddr (fwd)

From: Patrick McHardy <[email-masked]>
Date: Mon, 21 May 2007 19:03:24 +0200

David Miller wrote:
> The post ip_output() case is very unfortunate.  Perhaps we
> can tag the call sites, or if that doesn't work we can
> find some way to perhaps tag the dst as an input vs. output
> route in order to avoid this problem.

Tagging call-sites should make sure we don't use an address from the
outgoing device, but we would still not always use an address from
the incoming device.

Thinking again, we can simply perform a lookup on rt->fl.iif, that
should always do the right thing.

Signed-off-by: Patrick McHardy <[email-masked]>

diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index e238b17..02a899b 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -514,12 +514,15 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
 
 	saddr = iph->daddr;
 	if (!(rt->rt_flags & RTCF_LOCAL)) {
-		/* This is broken, skb_in->dev points to the outgoing device
-		 * after the packet passes through ip_output().
-		 */
-		if (skb_in->dev && sysctl_icmp_errors_use_inbound_ifaddr)
-			saddr = inet_select_addr(skb_in->dev, 0, RT_SCOPE_LINK);
-		else
+		struct net_device *dev = NULL;
+
+		if (rt->fl.iif && sysctl_icmp_errors_use_inbound_ifaddr)
+			dev = dev_get_by_index(rt->fl.iif);
+
+		if (dev) {
+			saddr = inet_select_addr(dev, 0, RT_SCOPE_LINK);
+			dev_put(dev);
+		} else
 			saddr = 0;
 	}