]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blobdiff - net/ipv4/udp.c
net: Convert protocol error handlers from void to int
[mirror_ubuntu-jammy-kernel.git] / net / ipv4 / udp.c
index ca3ed931f2a91479940d1c2dbe55870aef2df96f..a505ee5eb92c35b2c0ad3f0a8b22fa1c32de547b 100644 (file)
@@ -81,7 +81,7 @@
 
 #include <linux/uaccess.h>
 #include <asm/ioctls.h>
-#include <linux/bootmem.h>
+#include <linux/memblock.h>
 #include <linux/highmem.h>
 #include <linux/swap.h>
 #include <linux/types.h>
 #include "udp_impl.h"
 #include <net/sock_reuseport.h>
 #include <net/addrconf.h>
+#include <net/udp_tunnel.h>
 
 struct udp_table udp_table __read_mostly;
 EXPORT_SYMBOL(udp_table);
@@ -371,6 +372,7 @@ static int compute_score(struct sock *sk, struct net *net,
 {
        int score;
        struct inet_sock *inet;
+       bool dev_match;
 
        if (!net_eq(sock_net(sk), net) ||
            udp_sk(sk)->udp_port_hash != hnum ||
@@ -398,15 +400,11 @@ static int compute_score(struct sock *sk, struct net *net,
                score += 4;
        }
 
-       if (sk->sk_bound_dev_if || exact_dif) {
-               bool dev_match = (sk->sk_bound_dev_if == dif ||
-                                 sk->sk_bound_dev_if == sdif);
-
-               if (!dev_match)
-                       return -1;
-               if (sk->sk_bound_dev_if)
-                       score += 4;
-       }
+       dev_match = udp_sk_bound_dev_eq(net, sk->sk_bound_dev_if,
+                                       dif, sdif);
+       if (!dev_match)
+               return -1;
+       score += 4;
 
        if (sk->sk_incoming_cpu == raw_smp_processor_id())
                score++;
@@ -585,6 +583,62 @@ static inline bool __udp_is_mcast_sock(struct net *net, struct sock *sk,
        return true;
 }
 
+DEFINE_STATIC_KEY_FALSE(udp_encap_needed_key);
+void udp_encap_enable(void)
+{
+       static_branch_enable(&udp_encap_needed_key);
+}
+EXPORT_SYMBOL(udp_encap_enable);
+
+/* Try to match ICMP errors to UDP tunnels by looking up a socket without
+ * reversing source and destination port: this will match tunnels that force the
+ * same destination port on both endpoints (e.g. VXLAN, GENEVE). Note that
+ * lwtunnels might actually break this assumption by being configured with
+ * different destination ports on endpoints, in this case we won't be able to
+ * trace ICMP messages back to them.
+ *
+ * Then ask the tunnel implementation to match the error against a valid
+ * association.
+ *
+ * Return the socket if we have a match.
+ */
+static struct sock *__udp4_lib_err_encap(struct net *net,
+                                        const struct iphdr *iph,
+                                        struct udphdr *uh,
+                                        struct udp_table *udptable,
+                                        struct sk_buff *skb)
+{
+       int (*lookup)(struct sock *sk, struct sk_buff *skb);
+       int network_offset, transport_offset;
+       struct udp_sock *up;
+       struct sock *sk;
+
+       sk = __udp4_lib_lookup(net, iph->daddr, uh->source,
+                              iph->saddr, uh->dest, skb->dev->ifindex, 0,
+                              udptable, NULL);
+       if (!sk)
+               return NULL;
+
+       network_offset = skb_network_offset(skb);
+       transport_offset = skb_transport_offset(skb);
+
+       /* Network header needs to point to the outer IPv4 header inside ICMP */
+       skb_reset_network_header(skb);
+
+       /* Transport header needs to point to the UDP header */
+       skb_set_transport_header(skb, iph->ihl << 2);
+
+       up = udp_sk(sk);
+       lookup = READ_ONCE(up->encap_err_lookup);
+       if (!lookup || lookup(sk, skb))
+               sk = NULL;
+
+       skb_set_transport_header(skb, transport_offset);
+       skb_set_network_header(skb, network_offset);
+
+       return sk;
+}
+
 /*
  * This routine is called by the ICMP module when it gets some
  * sort of error condition.  If err < 0 then the socket should
@@ -596,13 +650,14 @@ static inline bool __udp_is_mcast_sock(struct net *net, struct sock *sk,
  * to find the appropriate port.
  */
 
-void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
+int __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
 {
        struct inet_sock *inet;
        const struct iphdr *iph = (const struct iphdr *)skb->data;
        struct udphdr *uh = (struct udphdr *)(skb->data+(iph->ihl<<2));
        const int type = icmp_hdr(skb)->type;
        const int code = icmp_hdr(skb)->code;
+       bool tunnel = false;
        struct sock *sk;
        int harderr;
        int err;
@@ -612,8 +667,15 @@ void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
                               iph->saddr, uh->source, skb->dev->ifindex,
                               inet_sdif(skb), udptable, NULL);
        if (!sk) {
-               __ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
-               return; /* No socket for error */
+               /* No socket for error: try tunnels before discarding */
+               if (static_branch_unlikely(&udp_encap_needed_key))
+                       sk = __udp4_lib_err_encap(net, iph, uh, udptable, skb);
+
+               if (!sk) {
+                       __ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
+                       return -ENOENT;
+               }
+               tunnel = true;
        }
 
        err = 0;
@@ -656,6 +718,10 @@ void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
         *      RFC1122: OK.  Passes ICMP errors back to application, as per
         *      4.1.3.3.
         */
+       if (tunnel) {
+               /* ...not for tunnels though: we don't have a sending socket */
+               goto out;
+       }
        if (!inet->recverr) {
                if (!harderr || sk->sk_state != TCP_ESTABLISHED)
                        goto out;
@@ -665,12 +731,12 @@ void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
        sk->sk_err = err;
        sk->sk_error_report(sk);
 out:
-       return;
+       return 0;
 }
 
-void udp_err(struct sk_buff *skb, u32 info)
+int udp_err(struct sk_buff *skb, u32 info)
 {
-       __udp4_lib_err(skb, info, &udp_table);
+       return __udp4_lib_err(skb, info, &udp_table);
 }
 
 /*
@@ -1713,6 +1779,10 @@ try_again:
                memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
                *addr_len = sizeof(*sin);
        }
+
+       if (udp_sk(sk)->gro_enabled)
+               udp_cmsg_recv(msg, sk, skb);
+
        if (inet->cmsg_flags)
                ip_cmsg_recv_offset(msg, sk, skb, sizeof(struct udphdr), off);
 
@@ -1889,13 +1959,6 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
        return 0;
 }
 
-DEFINE_STATIC_KEY_FALSE(udp_encap_needed_key);
-void udp_encap_enable(void)
-{
-       static_branch_enable(&udp_encap_needed_key);
-}
-EXPORT_SYMBOL(udp_encap_enable);
-
 /* returns:
  *  -1: error
  *   0: success
@@ -1904,7 +1967,7 @@ EXPORT_SYMBOL(udp_encap_enable);
  * Note that in the success and error cases, the skb is assumed to
  * have either been requeued or freed.
  */
-static int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+static int udp_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb)
 {
        struct udp_sock *up = udp_sk(sk);
        int is_udplite = IS_UDPLITE(sk);
@@ -2007,6 +2070,27 @@ drop:
        return -1;
 }
 
+static int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+       struct sk_buff *next, *segs;
+       int ret;
+
+       if (likely(!udp_unexpected_gso(sk, skb)))
+               return udp_queue_rcv_one_skb(sk, skb);
+
+       BUILD_BUG_ON(sizeof(struct udp_skb_cb) > SKB_SGO_CB_OFFSET);
+       __skb_push(skb, -skb_mac_offset(skb));
+       segs = udp_rcv_segment(sk, skb, true);
+       for (skb = segs; skb; skb = next) {
+               next = skb->next;
+               __skb_pull(skb, skb_transport_offset(skb));
+               ret = udp_queue_rcv_one_skb(sk, skb);
+               if (ret > 0)
+                       ip_protocol_deliver_rcu(dev_net(skb->dev), skb, -ret);
+       }
+       return 0;
+}
+
 /* For TCP sockets, sk_rx_dst is protected by socket lock
  * For UDP, we use xchg() to guard against concurrent changes.
  */
@@ -2398,11 +2482,15 @@ void udp_destroy_sock(struct sock *sk)
        bool slow = lock_sock_fast(sk);
        udp_flush_pending_frames(sk);
        unlock_sock_fast(sk, slow);
-       if (static_branch_unlikely(&udp_encap_needed_key) && up->encap_type) {
-               void (*encap_destroy)(struct sock *sk);
-               encap_destroy = READ_ONCE(up->encap_destroy);
-               if (encap_destroy)
-                       encap_destroy(sk);
+       if (static_branch_unlikely(&udp_encap_needed_key)) {
+               if (up->encap_type) {
+                       void (*encap_destroy)(struct sock *sk);
+                       encap_destroy = READ_ONCE(up->encap_destroy);
+                       if (encap_destroy)
+                               encap_destroy(sk);
+               }
+               if (up->encap_enabled)
+                       static_branch_disable(&udp_encap_needed_key);
        }
 }
 
@@ -2447,7 +2535,9 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
                        /* FALLTHROUGH */
                case UDP_ENCAP_L2TPINUDP:
                        up->encap_type = val;
-                       udp_encap_enable();
+                       lock_sock(sk);
+                       udp_tunnel_encap_enable(sk->sk_socket);
+                       release_sock(sk);
                        break;
                default:
                        err = -ENOPROTOOPT;
@@ -2469,6 +2559,14 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
                up->gso_size = val;
                break;
 
+       case UDP_GRO:
+               lock_sock(sk);
+               if (valbool)
+                       udp_tunnel_encap_enable(sk->sk_socket);
+               up->gro_enabled = valbool;
+               release_sock(sk);
+               break;
+
        /*
         *      UDP-Lite's partial checksum coverage (RFC 3828).
         */