Skip to content

Commit e7cc082

Browse files
sbrivio-rhdavem330
authored andcommitted
udp: Support for error handlers of tunnels with arbitrary destination port
ICMP error handling is currently not possible for UDP tunnels not employing a receiving socket with local destination port matching the remote one, because we have no way to look them up. Add an err_handler tunnel encapsulation operation that can be exported by tunnels in order to pass the error to the protocol implementing the encapsulation. We can't easily use a lookup function as we did for VXLAN and GENEVE, as protocol error handlers, which would be in turn called by implementations of this new operation, handle the errors themselves, together with the tunnel lookup. Without a socket, we can't be sure which encapsulation error handler is the appropriate one: encapsulation handlers (the ones for FoU and GUE introduced in the next patch, e.g.) will need to check the new error codes returned by protocol handlers to figure out if errors match the given encapsulation, and, in turn, report this error back, so that we can try all of them in __udp{4,6}_lib_err_encap_no_sk() until we have a match. v2: - Name all arguments in err_handler prototypes (David Miller) Signed-off-by: Stefano Brivio <sbrivio@redhat.com> Reviewed-by: Sabrina Dubroca <sd@queasysnail.net> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 32bbd87 commit e7cc082

File tree

4 files changed

+113
-35
lines changed

4 files changed

+113
-35
lines changed

include/net/ip6_tunnel.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ struct ip6_tnl_encap_ops {
6969
size_t (*encap_hlen)(struct ip_tunnel_encap *e);
7070
int (*build_header)(struct sk_buff *skb, struct ip_tunnel_encap *e,
7171
u8 *protocol, struct flowi6 *fl6);
72+
int (*err_handler)(struct sk_buff *skb, struct inet6_skb_parm *opt,
73+
u8 type, u8 code, int offset, __be32 info);
7274
};
7375

7476
#ifdef CONFIG_INET

include/net/ip_tunnels.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ struct ip_tunnel_encap_ops {
311311
size_t (*encap_hlen)(struct ip_tunnel_encap *e);
312312
int (*build_header)(struct sk_buff *skb, struct ip_tunnel_encap *e,
313313
u8 *protocol, struct flowi4 *fl4);
314+
int (*err_handler)(struct sk_buff *skb, u32 info);
314315
};
315316

316317
#define MAX_IPTUN_ENCAP_OPS 8

net/ipv4/udp.c

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
#include <net/net_namespace.h>
106106
#include <net/icmp.h>
107107
#include <net/inet_hashtables.h>
108+
#include <net/ip_tunnels.h>
108109
#include <net/route.h>
109110
#include <net/checksum.h>
110111
#include <net/xfrm.h>
@@ -590,35 +591,52 @@ void udp_encap_enable(void)
590591
}
591592
EXPORT_SYMBOL(udp_encap_enable);
592593

594+
/* Handler for tunnels with arbitrary destination ports: no socket lookup, go
595+
* through error handlers in encapsulations looking for a match.
596+
*/
597+
static int __udp4_lib_err_encap_no_sk(struct sk_buff *skb, u32 info)
598+
{
599+
int i;
600+
601+
for (i = 0; i < MAX_IPTUN_ENCAP_OPS; i++) {
602+
int (*handler)(struct sk_buff *skb, u32 info);
603+
604+
if (!iptun_encaps[i])
605+
continue;
606+
handler = rcu_dereference(iptun_encaps[i]->err_handler);
607+
if (handler && !handler(skb, info))
608+
return 0;
609+
}
610+
611+
return -ENOENT;
612+
}
613+
593614
/* Try to match ICMP errors to UDP tunnels by looking up a socket without
594615
* reversing source and destination port: this will match tunnels that force the
595616
* same destination port on both endpoints (e.g. VXLAN, GENEVE). Note that
596617
* lwtunnels might actually break this assumption by being configured with
597618
* different destination ports on endpoints, in this case we won't be able to
598619
* trace ICMP messages back to them.
599620
*
621+
* If this doesn't match any socket, probe tunnels with arbitrary destination
622+
* ports (e.g. FoU, GUE): there, the receiving socket is useless, as the port
623+
* we've sent packets to won't necessarily match the local destination port.
624+
*
600625
* Then ask the tunnel implementation to match the error against a valid
601626
* association.
602627
*
603-
* Return the socket if we have a match.
628+
* Return an error if we can't find a match, the socket if we need further
629+
* processing, zero otherwise.
604630
*/
605631
static struct sock *__udp4_lib_err_encap(struct net *net,
606632
const struct iphdr *iph,
607633
struct udphdr *uh,
608634
struct udp_table *udptable,
609-
struct sk_buff *skb)
635+
struct sk_buff *skb, u32 info)
610636
{
611-
int (*lookup)(struct sock *sk, struct sk_buff *skb);
612637
int network_offset, transport_offset;
613-
struct udp_sock *up;
614638
struct sock *sk;
615639

616-
sk = __udp4_lib_lookup(net, iph->daddr, uh->source,
617-
iph->saddr, uh->dest, skb->dev->ifindex, 0,
618-
udptable, NULL);
619-
if (!sk)
620-
return NULL;
621-
622640
network_offset = skb_network_offset(skb);
623641
transport_offset = skb_transport_offset(skb);
624642

@@ -628,10 +646,20 @@ static struct sock *__udp4_lib_err_encap(struct net *net,
628646
/* Transport header needs to point to the UDP header */
629647
skb_set_transport_header(skb, iph->ihl << 2);
630648

631-
up = udp_sk(sk);
632-
lookup = READ_ONCE(up->encap_err_lookup);
633-
if (!lookup || lookup(sk, skb))
634-
sk = NULL;
649+
sk = __udp4_lib_lookup(net, iph->daddr, uh->source,
650+
iph->saddr, uh->dest, skb->dev->ifindex, 0,
651+
udptable, NULL);
652+
if (sk) {
653+
int (*lookup)(struct sock *sk, struct sk_buff *skb);
654+
struct udp_sock *up = udp_sk(sk);
655+
656+
lookup = READ_ONCE(up->encap_err_lookup);
657+
if (!lookup || lookup(sk, skb))
658+
sk = NULL;
659+
}
660+
661+
if (!sk)
662+
sk = ERR_PTR(__udp4_lib_err_encap_no_sk(skb, info));
635663

636664
skb_set_transport_header(skb, transport_offset);
637665
skb_set_network_header(skb, network_offset);
@@ -668,13 +696,19 @@ int __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
668696
inet_sdif(skb), udptable, NULL);
669697
if (!sk) {
670698
/* No socket for error: try tunnels before discarding */
671-
if (static_branch_unlikely(&udp_encap_needed_key))
672-
sk = __udp4_lib_err_encap(net, iph, uh, udptable, skb);
699+
sk = ERR_PTR(-ENOENT);
700+
if (static_branch_unlikely(&udp_encap_needed_key)) {
701+
sk = __udp4_lib_err_encap(net, iph, uh, udptable, skb,
702+
info);
703+
if (!sk)
704+
return 0;
705+
}
673706

674-
if (!sk) {
707+
if (IS_ERR(sk)) {
675708
__ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
676-
return -ENOENT;
709+
return PTR_ERR(sk);
677710
}
711+
678712
tunnel = true;
679713
}
680714

net/ipv6/udp.c

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#include <net/raw.h>
4646
#include <net/tcp_states.h>
4747
#include <net/ip6_checksum.h>
48+
#include <net/ip6_tunnel.h>
4849
#include <net/xfrm.h>
4950
#include <net/inet_hashtables.h>
5051
#include <net/inet6_hashtables.h>
@@ -469,35 +470,57 @@ void udpv6_encap_enable(void)
469470
}
470471
EXPORT_SYMBOL(udpv6_encap_enable);
471472

473+
/* Handler for tunnels with arbitrary destination ports: no socket lookup, go
474+
* through error handlers in encapsulations looking for a match.
475+
*/
476+
static int __udp6_lib_err_encap_no_sk(struct sk_buff *skb,
477+
struct inet6_skb_parm *opt,
478+
u8 type, u8 code, int offset, u32 info)
479+
{
480+
int i;
481+
482+
for (i = 0; i < MAX_IPTUN_ENCAP_OPS; i++) {
483+
int (*handler)(struct sk_buff *skb, struct inet6_skb_parm *opt,
484+
u8 type, u8 code, int offset, u32 info);
485+
486+
if (!ip6tun_encaps[i])
487+
continue;
488+
handler = rcu_dereference(ip6tun_encaps[i]->err_handler);
489+
if (handler && !handler(skb, opt, type, code, offset, info))
490+
return 0;
491+
}
492+
493+
return -ENOENT;
494+
}
495+
472496
/* Try to match ICMP errors to UDP tunnels by looking up a socket without
473497
* reversing source and destination port: this will match tunnels that force the
474498
* same destination port on both endpoints (e.g. VXLAN, GENEVE). Note that
475499
* lwtunnels might actually break this assumption by being configured with
476500
* different destination ports on endpoints, in this case we won't be able to
477501
* trace ICMP messages back to them.
478502
*
503+
* If this doesn't match any socket, probe tunnels with arbitrary destination
504+
* ports (e.g. FoU, GUE): there, the receiving socket is useless, as the port
505+
* we've sent packets to won't necessarily match the local destination port.
506+
*
479507
* Then ask the tunnel implementation to match the error against a valid
480508
* association.
481509
*
482-
* Return the socket if we have a match.
510+
* Return an error if we can't find a match, the socket if we need further
511+
* processing, zero otherwise.
483512
*/
484513
static struct sock *__udp6_lib_err_encap(struct net *net,
485514
const struct ipv6hdr *hdr, int offset,
486515
struct udphdr *uh,
487516
struct udp_table *udptable,
488-
struct sk_buff *skb)
517+
struct sk_buff *skb,
518+
struct inet6_skb_parm *opt,
519+
u8 type, u8 code, __be32 info)
489520
{
490-
int (*lookup)(struct sock *sk, struct sk_buff *skb);
491521
int network_offset, transport_offset;
492-
struct udp_sock *up;
493522
struct sock *sk;
494523

495-
sk = __udp6_lib_lookup(net, &hdr->daddr, uh->source,
496-
&hdr->saddr, uh->dest,
497-
inet6_iif(skb), 0, udptable, skb);
498-
if (!sk)
499-
return NULL;
500-
501524
network_offset = skb_network_offset(skb);
502525
transport_offset = skb_transport_offset(skb);
503526

@@ -507,13 +530,26 @@ static struct sock *__udp6_lib_err_encap(struct net *net,
507530
/* Transport header needs to point to the UDP header */
508531
skb_set_transport_header(skb, offset);
509532

510-
up = udp_sk(sk);
511-
lookup = READ_ONCE(up->encap_err_lookup);
512-
if (!lookup || lookup(sk, skb))
513-
sk = NULL;
533+
sk = __udp6_lib_lookup(net, &hdr->daddr, uh->source,
534+
&hdr->saddr, uh->dest,
535+
inet6_iif(skb), 0, udptable, skb);
536+
if (sk) {
537+
int (*lookup)(struct sock *sk, struct sk_buff *skb);
538+
struct udp_sock *up = udp_sk(sk);
539+
540+
lookup = READ_ONCE(up->encap_err_lookup);
541+
if (!lookup || lookup(sk, skb))
542+
sk = NULL;
543+
}
544+
545+
if (!sk) {
546+
sk = ERR_PTR(__udp6_lib_err_encap_no_sk(skb, opt, type, code,
547+
offset, info));
548+
}
514549

515550
skb_set_transport_header(skb, transport_offset);
516551
skb_set_network_header(skb, network_offset);
552+
517553
return sk;
518554
}
519555

@@ -536,16 +572,21 @@ int __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
536572
inet6_iif(skb), inet6_sdif(skb), udptable, skb);
537573
if (!sk) {
538574
/* No socket for error: try tunnels before discarding */
575+
sk = ERR_PTR(-ENOENT);
539576
if (static_branch_unlikely(&udpv6_encap_needed_key)) {
540577
sk = __udp6_lib_err_encap(net, hdr, offset, uh,
541-
udptable, skb);
578+
udptable, skb,
579+
opt, type, code, info);
580+
if (!sk)
581+
return 0;
542582
}
543583

544-
if (!sk) {
584+
if (IS_ERR(sk)) {
545585
__ICMP6_INC_STATS(net, __in6_dev_get(skb->dev),
546586
ICMP6_MIB_INERRORS);
547-
return -ENOENT;
587+
return PTR_ERR(sk);
548588
}
589+
549590
tunnel = true;
550591
}
551592

0 commit comments

Comments
 (0)