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

Commit 628e341f authored by Hannes Frederic Sowa's avatar Hannes Frederic Sowa Committed by Steffen Klassert
Browse files

xfrm: make local error reporting more robust

In xfrm4 and xfrm6 we need to take care about sockets of the other
address family. This could happen because a 6in4 or 4in6 tunnel could
get protected by ipsec.

Because we don't want to have a run-time dependency on ipv6 when only
using ipv4 xfrm we have to embed a pointer to the correct local_error
function in xfrm_state_afinet and look it up when returning an error
depending on the socket address family.

Thanks to vi0ss for the great bug report:
<https://bugzilla.kernel.org/show_bug.cgi?id=58691

>

v2:
a) fix two more unsafe interpretations of skb->sk as ipv6 socket
   (xfrm6_local_dontfrag and __xfrm6_output)
v3:
a) add an EXPORT_SYMBOL_GPL(xfrm_local_error) to fix a link error when
   building ipv6 as a module (thanks to Steffen Klassert)

Reported-by: default avatar <vi0oss@gmail.com>
Cc: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: default avatarHannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: default avatarSteffen Klassert <steffen.klassert@secunet.com>
parent d9bf5f13
Loading
Loading
Loading
Loading
+6 −0
Original line number Original line Diff line number Diff line
@@ -341,10 +341,13 @@ struct xfrm_state_afinfo {
						  struct sk_buff *skb);
						  struct sk_buff *skb);
	int			(*transport_finish)(struct sk_buff *skb,
	int			(*transport_finish)(struct sk_buff *skb,
						    int async);
						    int async);
	void			(*local_error)(struct sk_buff *skb, u32 mtu);
};
};


extern int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo);
extern int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo);
extern int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo);
extern int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo);
extern struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
extern void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);


extern void xfrm_state_delete_tunnel(struct xfrm_state *x);
extern void xfrm_state_delete_tunnel(struct xfrm_state *x);


@@ -1477,6 +1480,7 @@ extern int xfrm_input_resume(struct sk_buff *skb, int nexthdr);
extern int xfrm_output_resume(struct sk_buff *skb, int err);
extern int xfrm_output_resume(struct sk_buff *skb, int err);
extern int xfrm_output(struct sk_buff *skb);
extern int xfrm_output(struct sk_buff *skb);
extern int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb);
extern int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb);
extern void xfrm_local_error(struct sk_buff *skb, int mtu);
extern int xfrm4_extract_header(struct sk_buff *skb);
extern int xfrm4_extract_header(struct sk_buff *skb);
extern int xfrm4_extract_input(struct xfrm_state *x, struct sk_buff *skb);
extern int xfrm4_extract_input(struct xfrm_state *x, struct sk_buff *skb);
extern int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,
extern int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,
@@ -1497,6 +1501,7 @@ extern int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short fam
extern int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family);
extern int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family);
extern int xfrm4_mode_tunnel_input_register(struct xfrm_tunnel *handler);
extern int xfrm4_mode_tunnel_input_register(struct xfrm_tunnel *handler);
extern int xfrm4_mode_tunnel_input_deregister(struct xfrm_tunnel *handler);
extern int xfrm4_mode_tunnel_input_deregister(struct xfrm_tunnel *handler);
extern void xfrm4_local_error(struct sk_buff *skb, u32 mtu);
extern int xfrm6_extract_header(struct sk_buff *skb);
extern int xfrm6_extract_header(struct sk_buff *skb);
extern int xfrm6_extract_input(struct xfrm_state *x, struct sk_buff *skb);
extern int xfrm6_extract_input(struct xfrm_state *x, struct sk_buff *skb);
extern int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi);
extern int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi);
@@ -1514,6 +1519,7 @@ extern int xfrm6_output(struct sk_buff *skb);
extern int xfrm6_output_finish(struct sk_buff *skb);
extern int xfrm6_output_finish(struct sk_buff *skb);
extern int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb,
extern int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb,
				 u8 **prevhdr);
				 u8 **prevhdr);
extern void xfrm6_local_error(struct sk_buff *skb, u32 mtu);


#ifdef CONFIG_XFRM
#ifdef CONFIG_XFRM
extern int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb);
extern int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb);
+10 −2
Original line number Original line Diff line number Diff line
@@ -33,8 +33,7 @@ static int xfrm4_tunnel_check_size(struct sk_buff *skb)
	mtu = dst_mtu(dst);
	mtu = dst_mtu(dst);
	if (skb->len > mtu) {
	if (skb->len > mtu) {
		if (skb->sk)
		if (skb->sk)
			ip_local_error(skb->sk, EMSGSIZE, ip_hdr(skb)->daddr,
			xfrm_local_error(skb, mtu);
				       inet_sk(skb->sk)->inet_dport, mtu);
		else
		else
			icmp_send(skb, ICMP_DEST_UNREACH,
			icmp_send(skb, ICMP_DEST_UNREACH,
				  ICMP_FRAG_NEEDED, htonl(mtu));
				  ICMP_FRAG_NEEDED, htonl(mtu));
@@ -99,3 +98,12 @@ int xfrm4_output(struct sk_buff *skb)
			    x->outer_mode->afinfo->output_finish,
			    x->outer_mode->afinfo->output_finish,
			    !(IPCB(skb)->flags & IPSKB_REROUTED));
			    !(IPCB(skb)->flags & IPSKB_REROUTED));
}
}

void xfrm4_local_error(struct sk_buff *skb, u32 mtu)
{
	struct iphdr *hdr;

	hdr = skb->encapsulation ? inner_ip_hdr(skb) : ip_hdr(skb);
	ip_local_error(skb->sk, EMSGSIZE, hdr->daddr,
		       inet_sk(skb->sk)->inet_dport, mtu);
}
+1 −0
Original line number Original line Diff line number Diff line
@@ -83,6 +83,7 @@ static struct xfrm_state_afinfo xfrm4_state_afinfo = {
	.extract_input		= xfrm4_extract_input,
	.extract_input		= xfrm4_extract_input,
	.extract_output		= xfrm4_extract_output,
	.extract_output		= xfrm4_extract_output,
	.transport_finish	= xfrm4_transport_finish,
	.transport_finish	= xfrm4_transport_finish,
	.local_error		= xfrm4_local_error,
};
};


void __init xfrm4_state_init(void)
void __init xfrm4_state_init(void)
+6 −4
Original line number Original line Diff line number Diff line
@@ -34,8 +34,10 @@ static int xfrm6_local_dontfrag(struct sk_buff *skb)
	struct sock *sk = skb->sk;
	struct sock *sk = skb->sk;


	if (sk) {
	if (sk) {
		proto = sk->sk_protocol;
		if (sk->sk_family != AF_INET6)
			return 0;


		proto = sk->sk_protocol;
		if (proto == IPPROTO_UDP || proto == IPPROTO_RAW)
		if (proto == IPPROTO_UDP || proto == IPPROTO_RAW)
			return inet6_sk(sk)->dontfrag;
			return inet6_sk(sk)->dontfrag;
	}
	}
@@ -54,7 +56,7 @@ static void xfrm6_local_rxpmtu(struct sk_buff *skb, u32 mtu)
	ipv6_local_rxpmtu(sk, &fl6, mtu);
	ipv6_local_rxpmtu(sk, &fl6, mtu);
}
}


static void xfrm6_local_error(struct sk_buff *skb, u32 mtu)
void xfrm6_local_error(struct sk_buff *skb, u32 mtu)
{
{
	struct flowi6 fl6;
	struct flowi6 fl6;
	struct sock *sk = skb->sk;
	struct sock *sk = skb->sk;
@@ -80,7 +82,7 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb)
		if (xfrm6_local_dontfrag(skb))
		if (xfrm6_local_dontfrag(skb))
			xfrm6_local_rxpmtu(skb, mtu);
			xfrm6_local_rxpmtu(skb, mtu);
		else if (skb->sk)
		else if (skb->sk)
			xfrm6_local_error(skb, mtu);
			xfrm_local_error(skb, mtu);
		else
		else
			icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
			icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
		ret = -EMSGSIZE;
		ret = -EMSGSIZE;
@@ -142,7 +144,7 @@ static int __xfrm6_output(struct sk_buff *skb)
		xfrm6_local_rxpmtu(skb, mtu);
		xfrm6_local_rxpmtu(skb, mtu);
		return -EMSGSIZE;
		return -EMSGSIZE;
	} else if (!skb->local_df && skb->len > mtu && skb->sk) {
	} else if (!skb->local_df && skb->len > mtu && skb->sk) {
		xfrm6_local_error(skb, mtu);
		xfrm_local_error(skb, mtu);
		return -EMSGSIZE;
		return -EMSGSIZE;
	}
	}


+1 −0
Original line number Original line Diff line number Diff line
@@ -183,6 +183,7 @@ static struct xfrm_state_afinfo xfrm6_state_afinfo = {
	.extract_input		= xfrm6_extract_input,
	.extract_input		= xfrm6_extract_input,
	.extract_output		= xfrm6_extract_output,
	.extract_output		= xfrm6_extract_output,
	.transport_finish	= xfrm6_transport_finish,
	.transport_finish	= xfrm6_transport_finish,
	.local_error		= xfrm6_local_error,
};
};


int __init xfrm6_state_init(void)
int __init xfrm6_state_init(void)
Loading