icmp: Fix regression in nexthop resolution during replies.
authorDavid S. Miller <davem@davemloft.net>
Fri, 22 Jul 2011 13:22:10 +0000 (06:22 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 22 Jul 2011 13:22:10 +0000 (06:22 -0700)
icmp_route_lookup() uses the wrong flow parameters if the reverse
session route lookup isn't used.

So do not commit to the re-decoded flow until we actually make a
final decision to use a real route saved in 'rt2'.

Reported-by: Florian Westphal <fw@strlen.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv4/icmp.c

index 5395e45dcce63bc80d4bc3ef567f84f8bae9ac36..23ef31baa1afb84d3b81af7ab031c88830efe0f5 100644 (file)
@@ -380,6 +380,7 @@ static struct rtable *icmp_route_lookup(struct net *net,
                                        struct icmp_bxm *param)
 {
        struct rtable *rt, *rt2;
+       struct flowi4 fl4_dec;
        int err;
 
        memset(fl4, 0, sizeof(*fl4));
@@ -408,19 +409,19 @@ static struct rtable *icmp_route_lookup(struct net *net,
        } else
                return rt;
 
-       err = xfrm_decode_session_reverse(skb_in, flowi4_to_flowi(fl4), AF_INET);
+       err = xfrm_decode_session_reverse(skb_in, flowi4_to_flowi(&fl4_dec), AF_INET);
        if (err)
                goto relookup_failed;
 
-       if (inet_addr_type(net, fl4->saddr) == RTN_LOCAL) {
-               rt2 = __ip_route_output_key(net, fl4);
+       if (inet_addr_type(net, fl4_dec.saddr) == RTN_LOCAL) {
+               rt2 = __ip_route_output_key(net, &fl4_dec);
                if (IS_ERR(rt2))
                        err = PTR_ERR(rt2);
        } else {
                struct flowi4 fl4_2 = {};
                unsigned long orefdst;
 
-               fl4_2.daddr = fl4->saddr;
+               fl4_2.daddr = fl4_dec.saddr;
                rt2 = ip_route_output_key(net, &fl4_2);
                if (IS_ERR(rt2)) {
                        err = PTR_ERR(rt2);
@@ -428,7 +429,7 @@ static struct rtable *icmp_route_lookup(struct net *net,
                }
                /* Ugh! */
                orefdst = skb_in->_skb_refdst; /* save old refdst */
-               err = ip_route_input(skb_in, fl4->daddr, fl4->saddr,
+               err = ip_route_input(skb_in, fl4_dec.daddr, fl4_dec.saddr,
                                     RT_TOS(tos), rt2->dst.dev);
 
                dst_release(&rt2->dst);
@@ -440,10 +441,11 @@ static struct rtable *icmp_route_lookup(struct net *net,
                goto relookup_failed;
 
        rt2 = (struct rtable *) xfrm_lookup(net, &rt2->dst,
-                                           flowi4_to_flowi(fl4), NULL,
+                                           flowi4_to_flowi(&fl4_dec), NULL,
                                            XFRM_LOOKUP_ICMP);
        if (!IS_ERR(rt2)) {
                dst_release(&rt->dst);
+               memcpy(fl4, &fl4_dec, sizeof(*fl4));
                rt = rt2;
        } else if (PTR_ERR(rt2) == -EPERM) {
                if (rt)