Commit 9bfce73c authored by Vadim Fedorenko's avatar Vadim Fedorenko Committed by David S. Miller
Browse files

udp: check encap socket in __udp_lib_err



Commit d26796ae ("udp: check udp sock encap_type in __udp_lib_err")
added checks for encapsulated sockets but it broke cases when there is
no implementation of encap_err_lookup for encapsulation, i.e. ESP in
UDP encapsulation. Fix it by calling encap_err_lookup only if socket
implements this method otherwise treat it as legal socket.

Fixes: d26796ae ("udp: check udp sock encap_type in __udp_lib_err")
Signed-off-by: default avatarVadim Fedorenko <vfedorenko@novek.ru>
Reviewed-by: default avatarXin Long <lucien.xin@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 58acd100
Loading
Loading
Loading
Loading
+19 −6
Original line number Diff line number Diff line
@@ -645,10 +645,12 @@ static struct sock *__udp4_lib_err_encap(struct net *net,
					 const struct iphdr *iph,
					 struct udphdr *uh,
					 struct udp_table *udptable,
					 struct sock *sk,
					 struct sk_buff *skb, u32 info)
{
	int (*lookup)(struct sock *sk, struct sk_buff *skb);
	int network_offset, transport_offset;
	struct sock *sk;
	struct udp_sock *up;

	network_offset = skb_network_offset(skb);
	transport_offset = skb_transport_offset(skb);
@@ -659,18 +661,28 @@ static struct sock *__udp4_lib_err_encap(struct net *net,
	/* Transport header needs to point to the UDP header */
	skb_set_transport_header(skb, iph->ihl << 2);

	if (sk) {
		up = udp_sk(sk);

		lookup = READ_ONCE(up->encap_err_lookup);
		if (lookup && lookup(sk, skb))
			sk = NULL;

		goto out;
	}

	sk = __udp4_lib_lookup(net, iph->daddr, uh->source,
			       iph->saddr, uh->dest, skb->dev->ifindex, 0,
			       udptable, NULL);
	if (sk) {
		int (*lookup)(struct sock *sk, struct sk_buff *skb);
		struct udp_sock *up = udp_sk(sk);
		up = udp_sk(sk);

		lookup = READ_ONCE(up->encap_err_lookup);
		if (!lookup || lookup(sk, skb))
			sk = NULL;
	}

out:
	if (!sk)
		sk = ERR_PTR(__udp4_lib_err_encap_no_sk(skb, info));

@@ -707,15 +719,16 @@ int __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
	sk = __udp4_lib_lookup(net, iph->daddr, uh->dest,
			       iph->saddr, uh->source, skb->dev->ifindex,
			       inet_sdif(skb), udptable, NULL);

	if (!sk || udp_sk(sk)->encap_type) {
		/* No socket for error: try tunnels before discarding */
		sk = ERR_PTR(-ENOENT);
		if (static_branch_unlikely(&udp_encap_needed_key)) {
			sk = __udp4_lib_err_encap(net, iph, uh, udptable, skb,
			sk = __udp4_lib_err_encap(net, iph, uh, udptable, sk, skb,
						  info);
			if (!sk)
				return 0;
		}
		} else
			sk = ERR_PTR(-ENOENT);

		if (IS_ERR(sk)) {
			__ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
+19 −6
Original line number Diff line number Diff line
@@ -502,12 +502,14 @@ static struct sock *__udp6_lib_err_encap(struct net *net,
					 const struct ipv6hdr *hdr, int offset,
					 struct udphdr *uh,
					 struct udp_table *udptable,
					 struct sock *sk,
					 struct sk_buff *skb,
					 struct inet6_skb_parm *opt,
					 u8 type, u8 code, __be32 info)
{
	int (*lookup)(struct sock *sk, struct sk_buff *skb);
	int network_offset, transport_offset;
	struct sock *sk;
	struct udp_sock *up;

	network_offset = skb_network_offset(skb);
	transport_offset = skb_transport_offset(skb);
@@ -518,18 +520,28 @@ static struct sock *__udp6_lib_err_encap(struct net *net,
	/* Transport header needs to point to the UDP header */
	skb_set_transport_header(skb, offset);

	if (sk) {
		up = udp_sk(sk);

		lookup = READ_ONCE(up->encap_err_lookup);
		if (lookup && lookup(sk, skb))
			sk = NULL;

		goto out;
	}

	sk = __udp6_lib_lookup(net, &hdr->daddr, uh->source,
			       &hdr->saddr, uh->dest,
			       inet6_iif(skb), 0, udptable, skb);
	if (sk) {
		int (*lookup)(struct sock *sk, struct sk_buff *skb);
		struct udp_sock *up = udp_sk(sk);
		up = udp_sk(sk);

		lookup = READ_ONCE(up->encap_err_lookup);
		if (!lookup || lookup(sk, skb))
			sk = NULL;
	}

out:
	if (!sk) {
		sk = ERR_PTR(__udp6_lib_err_encap_no_sk(skb, opt, type, code,
							offset, info));
@@ -558,16 +570,17 @@ int __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt,

	sk = __udp6_lib_lookup(net, daddr, uh->dest, saddr, uh->source,
			       inet6_iif(skb), inet6_sdif(skb), udptable, NULL);

	if (!sk || udp_sk(sk)->encap_type) {
		/* No socket for error: try tunnels before discarding */
		sk = ERR_PTR(-ENOENT);
		if (static_branch_unlikely(&udpv6_encap_needed_key)) {
			sk = __udp6_lib_err_encap(net, hdr, offset, uh,
						  udptable, skb,
						  udptable, sk, skb,
						  opt, type, code, info);
			if (!sk)
				return 0;
		}
		} else
			sk = ERR_PTR(-ENOENT);

		if (IS_ERR(sk)) {
			__ICMP6_INC_STATS(net, __in6_dev_get(skb->dev),