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

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
...@@ -645,10 +645,12 @@ static struct sock *__udp4_lib_err_encap(struct net *net, ...@@ -645,10 +645,12 @@ static struct sock *__udp4_lib_err_encap(struct net *net,
const struct iphdr *iph, const struct iphdr *iph,
struct udphdr *uh, struct udphdr *uh,
struct udp_table *udptable, struct udp_table *udptable,
struct sock *sk,
struct sk_buff *skb, u32 info) struct sk_buff *skb, u32 info)
{ {
int (*lookup)(struct sock *sk, struct sk_buff *skb);
int network_offset, transport_offset; int network_offset, transport_offset;
struct sock *sk; struct udp_sock *up;
network_offset = skb_network_offset(skb); network_offset = skb_network_offset(skb);
transport_offset = skb_transport_offset(skb); transport_offset = skb_transport_offset(skb);
...@@ -659,18 +661,28 @@ static struct sock *__udp4_lib_err_encap(struct net *net, ...@@ -659,18 +661,28 @@ static struct sock *__udp4_lib_err_encap(struct net *net,
/* Transport header needs to point to the UDP header */ /* Transport header needs to point to the UDP header */
skb_set_transport_header(skb, iph->ihl << 2); 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, sk = __udp4_lib_lookup(net, iph->daddr, uh->source,
iph->saddr, uh->dest, skb->dev->ifindex, 0, iph->saddr, uh->dest, skb->dev->ifindex, 0,
udptable, NULL); udptable, NULL);
if (sk) { if (sk) {
int (*lookup)(struct sock *sk, struct sk_buff *skb); up = udp_sk(sk);
struct udp_sock *up = udp_sk(sk);
lookup = READ_ONCE(up->encap_err_lookup); lookup = READ_ONCE(up->encap_err_lookup);
if (!lookup || lookup(sk, skb)) if (!lookup || lookup(sk, skb))
sk = NULL; sk = NULL;
} }
out:
if (!sk) if (!sk)
sk = ERR_PTR(__udp4_lib_err_encap_no_sk(skb, info)); 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) ...@@ -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, sk = __udp4_lib_lookup(net, iph->daddr, uh->dest,
iph->saddr, uh->source, skb->dev->ifindex, iph->saddr, uh->source, skb->dev->ifindex,
inet_sdif(skb), udptable, NULL); inet_sdif(skb), udptable, NULL);
if (!sk || udp_sk(sk)->encap_type) { if (!sk || udp_sk(sk)->encap_type) {
/* No socket for error: try tunnels before discarding */ /* No socket for error: try tunnels before discarding */
sk = ERR_PTR(-ENOENT);
if (static_branch_unlikely(&udp_encap_needed_key)) { 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); info);
if (!sk) if (!sk)
return 0; return 0;
} } else
sk = ERR_PTR(-ENOENT);
if (IS_ERR(sk)) { if (IS_ERR(sk)) {
__ICMP_INC_STATS(net, ICMP_MIB_INERRORS); __ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
......
...@@ -502,12 +502,14 @@ static struct sock *__udp6_lib_err_encap(struct net *net, ...@@ -502,12 +502,14 @@ static struct sock *__udp6_lib_err_encap(struct net *net,
const struct ipv6hdr *hdr, int offset, const struct ipv6hdr *hdr, int offset,
struct udphdr *uh, struct udphdr *uh,
struct udp_table *udptable, struct udp_table *udptable,
struct sock *sk,
struct sk_buff *skb, struct sk_buff *skb,
struct inet6_skb_parm *opt, struct inet6_skb_parm *opt,
u8 type, u8 code, __be32 info) u8 type, u8 code, __be32 info)
{ {
int (*lookup)(struct sock *sk, struct sk_buff *skb);
int network_offset, transport_offset; int network_offset, transport_offset;
struct sock *sk; struct udp_sock *up;
network_offset = skb_network_offset(skb); network_offset = skb_network_offset(skb);
transport_offset = skb_transport_offset(skb); transport_offset = skb_transport_offset(skb);
...@@ -518,18 +520,28 @@ static struct sock *__udp6_lib_err_encap(struct net *net, ...@@ -518,18 +520,28 @@ static struct sock *__udp6_lib_err_encap(struct net *net,
/* Transport header needs to point to the UDP header */ /* Transport header needs to point to the UDP header */
skb_set_transport_header(skb, offset); 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, sk = __udp6_lib_lookup(net, &hdr->daddr, uh->source,
&hdr->saddr, uh->dest, &hdr->saddr, uh->dest,
inet6_iif(skb), 0, udptable, skb); inet6_iif(skb), 0, udptable, skb);
if (sk) { if (sk) {
int (*lookup)(struct sock *sk, struct sk_buff *skb); up = udp_sk(sk);
struct udp_sock *up = udp_sk(sk);
lookup = READ_ONCE(up->encap_err_lookup); lookup = READ_ONCE(up->encap_err_lookup);
if (!lookup || lookup(sk, skb)) if (!lookup || lookup(sk, skb))
sk = NULL; sk = NULL;
} }
out:
if (!sk) { if (!sk) {
sk = ERR_PTR(__udp6_lib_err_encap_no_sk(skb, opt, type, code, sk = ERR_PTR(__udp6_lib_err_encap_no_sk(skb, opt, type, code,
offset, info)); offset, info));
...@@ -558,16 +570,17 @@ int __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt, ...@@ -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, sk = __udp6_lib_lookup(net, daddr, uh->dest, saddr, uh->source,
inet6_iif(skb), inet6_sdif(skb), udptable, NULL); inet6_iif(skb), inet6_sdif(skb), udptable, NULL);
if (!sk || udp_sk(sk)->encap_type) { if (!sk || udp_sk(sk)->encap_type) {
/* No socket for error: try tunnels before discarding */ /* No socket for error: try tunnels before discarding */
sk = ERR_PTR(-ENOENT);
if (static_branch_unlikely(&udpv6_encap_needed_key)) { if (static_branch_unlikely(&udpv6_encap_needed_key)) {
sk = __udp6_lib_err_encap(net, hdr, offset, uh, sk = __udp6_lib_err_encap(net, hdr, offset, uh,
udptable, skb, udptable, sk, skb,
opt, type, code, info); opt, type, code, info);
if (!sk) if (!sk)
return 0; return 0;
} } else
sk = ERR_PTR(-ENOENT);
if (IS_ERR(sk)) { if (IS_ERR(sk)) {
__ICMP6_INC_STATS(net, __in6_dev_get(skb->dev), __ICMP6_INC_STATS(net, __in6_dev_get(skb->dev),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment