OSDN Git Service

udp: fix rx queue len reported by diag and proc interface
[uclinux-h8/linux.git] / net / ipv4 / udp.c
index 675433e..9bb27df 100644 (file)
@@ -544,9 +544,7 @@ EXPORT_SYMBOL_GPL(udp4_lib_lookup_skb);
 /* Must be called under rcu_read_lock().
  * Does increment socket refcount.
  */
-#if IS_ENABLED(CONFIG_NETFILTER_XT_MATCH_SOCKET) || \
-    IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TPROXY) || \
-    IS_ENABLED(CONFIG_NF_SOCKET_IPV4)
+#if IS_ENABLED(CONFIG_NF_TPROXY_IPV4) || IS_ENABLED(CONFIG_NF_SOCKET_IPV4)
 struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport,
                             __be32 daddr, __be16 dport, int dif)
 {
@@ -757,7 +755,8 @@ void udp_set_csum(bool nocheck, struct sk_buff *skb,
 }
 EXPORT_SYMBOL(udp_set_csum);
 
-static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4)
+static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4,
+                       struct inet_cork *cork)
 {
        struct sock *sk = skb->sk;
        struct inet_sock *inet = inet_sk(sk);
@@ -777,6 +776,27 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4)
        uh->len = htons(len);
        uh->check = 0;
 
+       if (cork->gso_size) {
+               const int hlen = skb_network_header_len(skb) +
+                                sizeof(struct udphdr);
+
+               if (hlen + cork->gso_size > cork->fragsize)
+                       return -EINVAL;
+               if (skb->len > cork->gso_size * UDP_MAX_SEGMENTS)
+                       return -EINVAL;
+               if (sk->sk_no_check_tx)
+                       return -EINVAL;
+               if (skb->ip_summed != CHECKSUM_PARTIAL || is_udplite ||
+                   dst_xfrm(skb_dst(skb)))
+                       return -EIO;
+
+               skb_shinfo(skb)->gso_size = cork->gso_size;
+               skb_shinfo(skb)->gso_type = SKB_GSO_UDP_L4;
+               skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(len - sizeof(uh),
+                                                        cork->gso_size);
+               goto csum_partial;
+       }
+
        if (is_udplite)                                  /*     UDP-Lite      */
                csum = udplite_csum(skb);
 
@@ -786,6 +806,7 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4)
                goto send;
 
        } else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */
+csum_partial:
 
                udp4_hwcsum(skb, fl4->saddr, fl4->daddr);
                goto send;
@@ -828,7 +849,7 @@ int udp_push_pending_frames(struct sock *sk)
        if (!skb)
                goto out;
 
-       err = udp_send_skb(skb, fl4);
+       err = udp_send_skb(skb, fl4, &inet->cork.base);
 
 out:
        up->len = 0;
@@ -837,10 +858,48 @@ out:
 }
 EXPORT_SYMBOL(udp_push_pending_frames);
 
+static int __udp_cmsg_send(struct cmsghdr *cmsg, u16 *gso_size)
+{
+       switch (cmsg->cmsg_type) {
+       case UDP_SEGMENT:
+               if (cmsg->cmsg_len != CMSG_LEN(sizeof(__u16)))
+                       return -EINVAL;
+               *gso_size = *(__u16 *)CMSG_DATA(cmsg);
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+int udp_cmsg_send(struct sock *sk, struct msghdr *msg, u16 *gso_size)
+{
+       struct cmsghdr *cmsg;
+       bool need_ip = false;
+       int err;
+
+       for_each_cmsghdr(cmsg, msg) {
+               if (!CMSG_OK(msg, cmsg))
+                       return -EINVAL;
+
+               if (cmsg->cmsg_level != SOL_UDP) {
+                       need_ip = true;
+                       continue;
+               }
+
+               err = __udp_cmsg_send(cmsg, gso_size);
+               if (err)
+                       return err;
+       }
+
+       return need_ip;
+}
+EXPORT_SYMBOL_GPL(udp_cmsg_send);
+
 int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
 {
        struct inet_sock *inet = inet_sk(sk);
        struct udp_sock *up = udp_sk(sk);
+       DECLARE_SOCKADDR(struct sockaddr_in *, usin, msg->msg_name);
        struct flowi4 fl4_stack;
        struct flowi4 *fl4;
        int ulen = len;
@@ -895,8 +954,7 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
        /*
         *      Get and verify the address.
         */
-       if (msg->msg_name) {
-               DECLARE_SOCKADDR(struct sockaddr_in *, usin, msg->msg_name);
+       if (usin) {
                if (msg->msg_namelen < sizeof(*usin))
                        return -EINVAL;
                if (usin->sin_family != AF_INET) {
@@ -922,10 +980,14 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
        ipc.sockc.tsflags = sk->sk_tsflags;
        ipc.addr = inet->inet_saddr;
        ipc.oif = sk->sk_bound_dev_if;
+       ipc.gso_size = up->gso_size;
 
        if (msg->msg_controllen) {
-               err = ip_cmsg_send(sk, msg, &ipc, sk->sk_family == AF_INET6);
-               if (unlikely(err)) {
+               err = udp_cmsg_send(sk, msg, &ipc.gso_size);
+               if (err > 0)
+                       err = ip_cmsg_send(sk, msg, &ipc,
+                                          sk->sk_family == AF_INET6);
+               if (unlikely(err < 0)) {
                        kfree(ipc.opt);
                        return err;
                }
@@ -946,6 +1008,22 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
                rcu_read_unlock();
        }
 
+       if (cgroup_bpf_enabled && !connected) {
+               err = BPF_CGROUP_RUN_PROG_UDP4_SENDMSG_LOCK(sk,
+                                           (struct sockaddr *)usin, &ipc.addr);
+               if (err)
+                       goto out_free;
+               if (usin) {
+                       if (usin->sin_port == 0) {
+                               /* BPF program set invalid port. Reject it. */
+                               err = -EINVAL;
+                               goto out_free;
+                       }
+                       daddr = usin->sin_addr.s_addr;
+                       dport = usin->sin_port;
+               }
+       }
+
        saddr = ipc.addr;
        ipc.addr = faddr = daddr;
 
@@ -1032,12 +1110,14 @@ back_from_confirm:
 
        /* Lockless fast path for the non-corking case. */
        if (!corkreq) {
+               struct inet_cork cork;
+
                skb = ip_make_skb(sk, fl4, getfrag, msg, ulen,
                                  sizeof(struct udphdr), &ipc, &rt,
-                                 msg->msg_flags);
+                                 &cork, msg->msg_flags);
                err = PTR_ERR(skb);
                if (!IS_ERR_OR_NULL(skb))
-                       err = udp_send_skb(skb, fl4);
+                       err = udp_send_skb(skb, fl4, &cork);
                goto out;
        }
 
@@ -1813,10 +1893,10 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
        return 0;
 }
 
-static struct static_key udp_encap_needed __read_mostly;
+static DEFINE_STATIC_KEY_FALSE(udp_encap_needed_key);
 void udp_encap_enable(void)
 {
-       static_key_enable(&udp_encap_needed);
+       static_branch_enable(&udp_encap_needed_key);
 }
 EXPORT_SYMBOL(udp_encap_enable);
 
@@ -1840,7 +1920,7 @@ static int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
                goto drop;
        nf_reset(skb);
 
-       if (static_key_false(&udp_encap_needed) && up->encap_type) {
+       if (static_branch_unlikely(&udp_encap_needed_key) && up->encap_type) {
                int (*encap_rcv)(struct sock *sk, struct sk_buff *skb);
 
                /*
@@ -2303,7 +2383,7 @@ 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_key_false(&udp_encap_needed) && up->encap_type) {
+       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)
@@ -2368,6 +2448,12 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
                up->no_check6_rx = valbool;
                break;
 
+       case UDP_SEGMENT:
+               if (val < 0 || val > USHRT_MAX)
+                       return -EINVAL;
+               up->gso_size = val;
+               break;
+
        /*
         *      UDP-Lite's partial checksum coverage (RFC 3828).
         */
@@ -2458,6 +2544,10 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname,
                val = up->no_check6_rx;
                break;
 
+       case UDP_SEGMENT:
+               val = up->gso_size;
+               break;
+
        /* The following two cannot be changed on UDP sockets, the return is
         * always 0 (which corresponds to the full checksum coverage of UDP). */
        case UDPLITE_SEND_CSCOV:
@@ -2682,7 +2772,7 @@ static void udp4_format_sock(struct sock *sp, struct seq_file *f,
                " %02X %08X:%08X %02X:%08lX %08X %5u %8d %lu %d %pK %d",
                bucket, src, srcp, dest, destp, sp->sk_state,
                sk_wmem_alloc_get(sp),
-               sk_rmem_alloc_get(sp),
+               udp_rqueue_get(sp),
                0, 0L, 0,
                from_kuid_munged(seq_user_ns(f), sock_i_uid(sp)),
                0, sock_i_ino(sp),