Skip to content

Commit

Permalink
xfrm: Fix crash with ipv6 IPsec tunnel and NAT.
Browse files Browse the repository at this point in the history
The ipv6 xfrm output path is not aware that packets can be
rerouted by NAT to not use IPsec. We crash in this case
because we expect to have a xfrm state at the dst_entry.
This crash happens if the ipv6 layer does IPsec and NAT
or if we have an interfamily IPsec tunnel with ipv4 NAT.

We fix this by checking for a NAT rerouted packet in each
address family and dst_output() to the new destination
in this case.

Reported-by: Martin Pelikan <martin.pelikan@gmail.com>
Tested-by: Martin Pelikan <martin.pelikan@gmail.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
  • Loading branch information
Steffen Klassert committed Apr 7, 2014
1 parent d80e773 commit 5596732
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 23 deletions.
32 changes: 18 additions & 14 deletions net/ipv4/xfrm4_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,38 +62,42 @@ int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
if (err)
return err;

memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED;

skb->protocol = htons(ETH_P_IP);
IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE;

return x->outer_mode->output2(x, skb);
}
EXPORT_SYMBOL(xfrm4_prepare_output);

int xfrm4_output_finish(struct sk_buff *skb)
{
memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
skb->protocol = htons(ETH_P_IP);

#ifdef CONFIG_NETFILTER
IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED;
#endif

return xfrm_output(skb);
}

static int __xfrm4_output(struct sk_buff *skb)
{
struct xfrm_state *x = skb_dst(skb)->xfrm;

#ifdef CONFIG_NETFILTER
if (!skb_dst(skb)->xfrm) {
if (!x) {
IPCB(skb)->flags |= IPSKB_REROUTED;
return dst_output(skb);
}

IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED;
#endif

skb->protocol = htons(ETH_P_IP);
return xfrm_output(skb);
return x->outer_mode->afinfo->output_finish(skb);
}

int xfrm4_output(struct sk_buff *skb)
{
struct dst_entry *dst = skb_dst(skb);
struct xfrm_state *x = dst->xfrm;

return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb,
NULL, dst->dev,
x->outer_mode->afinfo->output_finish,
NULL, skb_dst(skb)->dev, __xfrm4_output,
!(IPCB(skb)->flags & IPSKB_REROUTED));
}

Expand Down
22 changes: 13 additions & 9 deletions net/ipv6/xfrm6_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,6 @@ int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
if (err)
return err;

memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
#ifdef CONFIG_NETFILTER
IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED;
#endif

skb->protocol = htons(ETH_P_IPV6);
skb->local_df = 1;

return x->outer_mode->output2(x, skb);
Expand All @@ -128,11 +122,13 @@ EXPORT_SYMBOL(xfrm6_prepare_output);

int xfrm6_output_finish(struct sk_buff *skb)
{
memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
skb->protocol = htons(ETH_P_IPV6);

#ifdef CONFIG_NETFILTER
IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED;
#endif

skb->protocol = htons(ETH_P_IPV6);
return xfrm_output(skb);
}

Expand All @@ -142,6 +138,13 @@ static int __xfrm6_output(struct sk_buff *skb)
struct xfrm_state *x = dst->xfrm;
int mtu;

#ifdef CONFIG_NETFILTER
if (!x) {
IP6CB(skb)->flags |= IP6SKB_REROUTED;
return dst_output(skb);
}
#endif

if (skb->protocol == htons(ETH_P_IPV6))
mtu = ip6_skb_dst_mtu(skb);
else
Expand All @@ -165,6 +168,7 @@ static int __xfrm6_output(struct sk_buff *skb)

int xfrm6_output(struct sk_buff *skb)
{
return NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, skb, NULL,
skb_dst(skb)->dev, __xfrm6_output);
return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, skb,
NULL, skb_dst(skb)->dev, __xfrm6_output,
!(IP6CB(skb)->flags & IP6SKB_REROUTED));
}

0 comments on commit 5596732

Please sign in to comment.