Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5596732f authored by Steffen Klassert's avatar Steffen Klassert
Browse files

xfrm: Fix crash with ipv6 IPsec tunnel and NAT.



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: default avatarMartin Pelikan <martin.pelikan@gmail.com>
Tested-by: default avatarMartin Pelikan <martin.pelikan@gmail.com>
Signed-off-by: default avatarSteffen Klassert <steffen.klassert@secunet.com>
parent d80e773f
Loading
Loading
Loading
Loading
+18 −14
Original line number Original line Diff line number Diff line
@@ -62,10 +62,7 @@ int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
	if (err)
	if (err)
		return err;
		return err;


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

	skb->protocol = htons(ETH_P_IP);


	return x->outer_mode->output2(x, skb);
	return x->outer_mode->output2(x, skb);
}
}
@@ -73,27 +70,34 @@ EXPORT_SYMBOL(xfrm4_prepare_output);


int xfrm4_output_finish(struct sk_buff *skb)
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
#ifdef CONFIG_NETFILTER
	if (!skb_dst(skb)->xfrm) {
	if (!x) {
		IPCB(skb)->flags |= IPSKB_REROUTED;
		IPCB(skb)->flags |= IPSKB_REROUTED;
		return dst_output(skb);
		return dst_output(skb);
	}
	}

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


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


int xfrm4_output(struct sk_buff *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,
	return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb,
			    NULL, dst->dev,
			    NULL, skb_dst(skb)->dev, __xfrm4_output,
			    x->outer_mode->afinfo->output_finish,
			    !(IPCB(skb)->flags & IPSKB_REROUTED));
			    !(IPCB(skb)->flags & IPSKB_REROUTED));
}
}


+13 −9
Original line number Original line Diff line number Diff line
@@ -114,12 +114,6 @@ int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
	if (err)
	if (err)
		return 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;
	skb->local_df = 1;


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


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

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


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


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


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

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


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,
	return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, skb,
		       skb_dst(skb)->dev, __xfrm6_output);
			    NULL, skb_dst(skb)->dev, __xfrm6_output,
			    !(IP6CB(skb)->flags & IP6SKB_REROUTED));
}
}