Commit ee68cea2 authored by Patrick McHardy's avatar Patrick McHardy Committed by David S. Miller

[NETFILTER]: Fix xfrm lookup after SNAT

To find out if a packet needs to be handled by IPsec after SNAT, packets
are currently rerouted in POST_ROUTING and a new xfrm lookup is done. This
breaks SNAT of non-unicast packets to non-local addresses because the
packet is routed as incoming packet and no neighbour entry is bound to the
dst_entry. In general, it seems to be a bad idea to replace the dst_entry
after the packet was already sent to the output routine because its state
might not match what's expected.

This patch changes the xfrm lookup in POST_ROUTING to re-use the original
dst_entry without routing the packet again. This means no policy routing
can be used for transport mode transforms (which keep the original route)
when packets are SNATed to match the policy, but it looks like the best
we can do for now.
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 10ee39fe
...@@ -79,7 +79,7 @@ enum nf_ip_hook_priorities { ...@@ -79,7 +79,7 @@ enum nf_ip_hook_priorities {
#ifdef __KERNEL__ #ifdef __KERNEL__
extern int ip_route_me_harder(struct sk_buff **pskb); extern int ip_route_me_harder(struct sk_buff **pskb);
extern int ip_xfrm_me_harder(struct sk_buff **pskb);
#endif /*__KERNEL__*/ #endif /*__KERNEL__*/
#endif /*__LINUX_IP_NETFILTER_H*/ #endif /*__LINUX_IP_NETFILTER_H*/
...@@ -78,6 +78,47 @@ int ip_route_me_harder(struct sk_buff **pskb) ...@@ -78,6 +78,47 @@ int ip_route_me_harder(struct sk_buff **pskb)
} }
EXPORT_SYMBOL(ip_route_me_harder); EXPORT_SYMBOL(ip_route_me_harder);
#ifdef CONFIG_XFRM
int ip_xfrm_me_harder(struct sk_buff **pskb)
{
struct flowi fl;
unsigned int hh_len;
struct dst_entry *dst;
if (IPCB(*pskb)->flags & IPSKB_XFRM_TRANSFORMED)
return 0;
if (xfrm_decode_session(*pskb, &fl, AF_INET) < 0)
return -1;
dst = (*pskb)->dst;
if (dst->xfrm)
dst = ((struct xfrm_dst *)dst)->route;
dst_hold(dst);
if (xfrm_lookup(&dst, &fl, (*pskb)->sk, 0) < 0)
return -1;
dst_release((*pskb)->dst);
(*pskb)->dst = dst;
/* Change in oif may mean change in hh_len. */
hh_len = (*pskb)->dst->dev->hard_header_len;
if (skb_headroom(*pskb) < hh_len) {
struct sk_buff *nskb;
nskb = skb_realloc_headroom(*pskb, hh_len);
if (!nskb)
return -1;
if ((*pskb)->sk)
skb_set_owner_w(nskb, (*pskb)->sk);
kfree_skb(*pskb);
*pskb = nskb;
}
return 0;
}
EXPORT_SYMBOL(ip_xfrm_me_harder);
#endif
void (*ip_nat_decode_session)(struct sk_buff *, struct flowi *); void (*ip_nat_decode_session)(struct sk_buff *, struct flowi *);
EXPORT_SYMBOL(ip_nat_decode_session); EXPORT_SYMBOL(ip_nat_decode_session);
......
...@@ -235,19 +235,19 @@ ip_nat_out(unsigned int hooknum, ...@@ -235,19 +235,19 @@ ip_nat_out(unsigned int hooknum,
return NF_ACCEPT; return NF_ACCEPT;
ret = ip_nat_fn(hooknum, pskb, in, out, okfn); ret = ip_nat_fn(hooknum, pskb, in, out, okfn);
#ifdef CONFIG_XFRM
if (ret != NF_DROP && ret != NF_STOLEN if (ret != NF_DROP && ret != NF_STOLEN
&& (ct = ip_conntrack_get(*pskb, &ctinfo)) != NULL) { && (ct = ip_conntrack_get(*pskb, &ctinfo)) != NULL) {
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
if (ct->tuplehash[dir].tuple.src.ip != if (ct->tuplehash[dir].tuple.src.ip !=
ct->tuplehash[!dir].tuple.dst.ip ct->tuplehash[!dir].tuple.dst.ip
#ifdef CONFIG_XFRM
|| ct->tuplehash[dir].tuple.src.u.all != || ct->tuplehash[dir].tuple.src.u.all !=
ct->tuplehash[!dir].tuple.dst.u.all ct->tuplehash[!dir].tuple.dst.u.all
#endif
) )
return ip_route_me_harder(pskb) == 0 ? ret : NF_DROP; return ip_xfrm_me_harder(pskb) == 0 ? ret : NF_DROP;
} }
#endif
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment