diff -urN a/net/ipv4/devinet.c b/net/ipv4/devinet.c --- a/net/ipv4/devinet.c 2012-07-12 06:32:21.000000000 +0300 +++ b/net/ipv4/devinet.c 2012-10-19 22:45:55.284491421 +0300 @@ -965,6 +965,7 @@ __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) { __be32 addr = 0; + unsigned char plen = 0; struct in_device *in_dev; struct net *net = dev_net(dev); @@ -976,12 +977,16 @@ for_primary_ifa(in_dev) { if (ifa->ifa_scope > scope) continue; - if (!dst || inet_ifa_match(dst, ifa)) { + if (!addr) { addr = ifa->ifa_local; - break; + if (!dst) + goto out_unlock; } - if (!addr) + if (inet_ifa_match(dst, ifa) && + ifa->ifa_prefixlen > plen) { addr = ifa->ifa_local; + plen = ifa->ifa_prefixlen; + } } endfor_ifa(in_dev); if (addr) @@ -998,10 +1003,18 @@ continue; for_primary_ifa(in_dev) { - if (ifa->ifa_scope != RT_SCOPE_LINK && - ifa->ifa_scope <= scope) { + if (ifa->ifa_scope == RT_SCOPE_LINK || + ifa->ifa_scope > scope) + continue; + if (!addr) { addr = ifa->ifa_local; - goto out_unlock; + if (!dst) + goto out_unlock; + } + if (inet_ifa_match(dst, ifa) && + ifa->ifa_prefixlen > plen) { + addr = ifa->ifa_local; + plen = ifa->ifa_prefixlen; } } endfor_ifa(in_dev); } diff -urN a/net/ipv4/icmp.c b/net/ipv4/icmp.c --- a/net/ipv4/icmp.c 2012-07-12 06:32:21.000000000 +0300 +++ b/net/ipv4/icmp.c 2012-10-18 12:53:36.362650652 +0300 @@ -571,7 +571,7 @@ dev = dev_get_by_index_rcu(net, rt->rt_iif); if (dev) - saddr = inet_select_addr(dev, 0, RT_SCOPE_LINK); + saddr = inet_select_addr(dev, iph->saddr, RT_SCOPE_LINK); else saddr = 0; rcu_read_unlock();