| From 664797f4a5ddb9b2842a10b4e95a0a8a677dcd59 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Tue, 20 Jul 2021 23:35:28 +0300 |
| Subject: udp: check encap socket in __udp_lib_err |
| |
| From: Vadim Fedorenko <vfedorenko@novek.ru> |
| |
| [ Upstream commit 9bfce73c8921c92a9565562e6e7d458d37b7ce80 ] |
| |
| Commit d26796ae5894 ("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: d26796ae5894 ("udp: check udp sock encap_type in __udp_lib_err") |
| Signed-off-by: Vadim Fedorenko <vfedorenko@novek.ru> |
| Reviewed-by: Xin Long <lucien.xin@gmail.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| net/ipv4/udp.c | 25 +++++++++++++++++++------ |
| net/ipv6/udp.c | 25 +++++++++++++++++++------ |
| 2 files changed, 38 insertions(+), 12 deletions(-) |
| |
| diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c |
| index ca9cf1051b1e..568dc31a0467 100644 |
| --- a/net/ipv4/udp.c |
| +++ b/net/ipv4/udp.c |
| @@ -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); |
| diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c |
| index 6774e776228c..2d3bd4a9b0d0 100644 |
| --- a/net/ipv6/udp.c |
| +++ b/net/ipv6/udp.c |
| @@ -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), |
| -- |
| 2.30.2 |
| |