diff options
Diffstat (limited to 'net/ipv4/udp.c')
-rw-r--r-- | net/ipv4/udp.c | 79 |
1 files changed, 70 insertions, 9 deletions
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 3488650b90ac..ce759b61f6cd 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c | |||
@@ -583,6 +583,62 @@ static inline bool __udp_is_mcast_sock(struct net *net, struct sock *sk, | |||
583 | return true; | 583 | return true; |
584 | } | 584 | } |
585 | 585 | ||
586 | DEFINE_STATIC_KEY_FALSE(udp_encap_needed_key); | ||
587 | void udp_encap_enable(void) | ||
588 | { | ||
589 | static_branch_enable(&udp_encap_needed_key); | ||
590 | } | ||
591 | EXPORT_SYMBOL(udp_encap_enable); | ||
592 | |||
593 | /* Try to match ICMP errors to UDP tunnels by looking up a socket without | ||
594 | * reversing source and destination port: this will match tunnels that force the | ||
595 | * same destination port on both endpoints (e.g. VXLAN, GENEVE). Note that | ||
596 | * lwtunnels might actually break this assumption by being configured with | ||
597 | * different destination ports on endpoints, in this case we won't be able to | ||
598 | * trace ICMP messages back to them. | ||
599 | * | ||
600 | * Then ask the tunnel implementation to match the error against a valid | ||
601 | * association. | ||
602 | * | ||
603 | * Return the socket if we have a match. | ||
604 | */ | ||
605 | static struct sock *__udp4_lib_err_encap(struct net *net, | ||
606 | const struct iphdr *iph, | ||
607 | struct udphdr *uh, | ||
608 | struct udp_table *udptable, | ||
609 | struct sk_buff *skb) | ||
610 | { | ||
611 | int (*lookup)(struct sock *sk, struct sk_buff *skb); | ||
612 | int network_offset, transport_offset; | ||
613 | struct udp_sock *up; | ||
614 | struct sock *sk; | ||
615 | |||
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 | |||
622 | network_offset = skb_network_offset(skb); | ||
623 | transport_offset = skb_transport_offset(skb); | ||
624 | |||
625 | /* Network header needs to point to the outer IPv4 header inside ICMP */ | ||
626 | skb_reset_network_header(skb); | ||
627 | |||
628 | /* Transport header needs to point to the UDP header */ | ||
629 | skb_set_transport_header(skb, iph->ihl << 2); | ||
630 | |||
631 | up = udp_sk(sk); | ||
632 | lookup = READ_ONCE(up->encap_err_lookup); | ||
633 | if (!lookup || lookup(sk, skb)) | ||
634 | sk = NULL; | ||
635 | |||
636 | skb_set_transport_header(skb, transport_offset); | ||
637 | skb_set_network_header(skb, network_offset); | ||
638 | |||
639 | return sk; | ||
640 | } | ||
641 | |||
586 | /* | 642 | /* |
587 | * This routine is called by the ICMP module when it gets some | 643 | * This routine is called by the ICMP module when it gets some |
588 | * sort of error condition. If err < 0 then the socket should | 644 | * sort of error condition. If err < 0 then the socket should |
@@ -601,6 +657,7 @@ void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable) | |||
601 | struct udphdr *uh = (struct udphdr *)(skb->data+(iph->ihl<<2)); | 657 | struct udphdr *uh = (struct udphdr *)(skb->data+(iph->ihl<<2)); |
602 | const int type = icmp_hdr(skb)->type; | 658 | const int type = icmp_hdr(skb)->type; |
603 | const int code = icmp_hdr(skb)->code; | 659 | const int code = icmp_hdr(skb)->code; |
660 | bool tunnel = false; | ||
604 | struct sock *sk; | 661 | struct sock *sk; |
605 | int harderr; | 662 | int harderr; |
606 | int err; | 663 | int err; |
@@ -610,8 +667,15 @@ void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable) | |||
610 | iph->saddr, uh->source, skb->dev->ifindex, | 667 | iph->saddr, uh->source, skb->dev->ifindex, |
611 | inet_sdif(skb), udptable, NULL); | 668 | inet_sdif(skb), udptable, NULL); |
612 | if (!sk) { | 669 | if (!sk) { |
613 | __ICMP_INC_STATS(net, ICMP_MIB_INERRORS); | 670 | /* No socket for error: try tunnels before discarding */ |
614 | return; /* No socket for error */ | 671 | if (static_branch_unlikely(&udp_encap_needed_key)) |
672 | sk = __udp4_lib_err_encap(net, iph, uh, udptable, skb); | ||
673 | |||
674 | if (!sk) { | ||
675 | __ICMP_INC_STATS(net, ICMP_MIB_INERRORS); | ||
676 | return; | ||
677 | } | ||
678 | tunnel = true; | ||
615 | } | 679 | } |
616 | 680 | ||
617 | err = 0; | 681 | err = 0; |
@@ -654,6 +718,10 @@ void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable) | |||
654 | * RFC1122: OK. Passes ICMP errors back to application, as per | 718 | * RFC1122: OK. Passes ICMP errors back to application, as per |
655 | * 4.1.3.3. | 719 | * 4.1.3.3. |
656 | */ | 720 | */ |
721 | if (tunnel) { | ||
722 | /* ...not for tunnels though: we don't have a sending socket */ | ||
723 | goto out; | ||
724 | } | ||
657 | if (!inet->recverr) { | 725 | if (!inet->recverr) { |
658 | if (!harderr || sk->sk_state != TCP_ESTABLISHED) | 726 | if (!harderr || sk->sk_state != TCP_ESTABLISHED) |
659 | goto out; | 727 | goto out; |
@@ -1891,13 +1959,6 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) | |||
1891 | return 0; | 1959 | return 0; |
1892 | } | 1960 | } |
1893 | 1961 | ||
1894 | DEFINE_STATIC_KEY_FALSE(udp_encap_needed_key); | ||
1895 | void udp_encap_enable(void) | ||
1896 | { | ||
1897 | static_branch_enable(&udp_encap_needed_key); | ||
1898 | } | ||
1899 | EXPORT_SYMBOL(udp_encap_enable); | ||
1900 | |||
1901 | /* returns: | 1962 | /* returns: |
1902 | * -1: error | 1963 | * -1: error |
1903 | * 0: success | 1964 | * 0: success |