]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - net/core/sock.c
udp: enable MSG_PEEK at non-zero offset
[mirror_ubuntu-bionic-kernel.git] / net / core / sock.c
index b67b9aedb230f9480d7ae91d8a8a79f5693187a5..2ce76e82857ff9cab856b8dad6d3139a2fdbbcf3 100644 (file)
@@ -402,9 +402,8 @@ static void sock_disable_timestamp(struct sock *sk, unsigned long flags)
 }
 
 
-int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+int __sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
-       int err;
        unsigned long flags;
        struct sk_buff_head *list = &sk->sk_receive_queue;
 
@@ -414,10 +413,6 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
                return -ENOMEM;
        }
 
-       err = sk_filter(sk, skb);
-       if (err)
-               return err;
-
        if (!sk_rmem_schedule(sk, skb, skb->truesize)) {
                atomic_inc(&sk->sk_drops);
                return -ENOBUFS;
@@ -440,6 +435,18 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
                sk->sk_data_ready(sk);
        return 0;
 }
+EXPORT_SYMBOL(__sock_queue_rcv_skb);
+
+int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+       int err;
+
+       err = sk_filter(sk, skb);
+       if (err)
+               return err;
+
+       return __sock_queue_rcv_skb(sk, skb);
+}
 EXPORT_SYMBOL(sock_queue_rcv_skb);
 
 int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested)
@@ -832,7 +839,8 @@ set_rcvbuf:
                    !(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID)) {
                        if (sk->sk_protocol == IPPROTO_TCP &&
                            sk->sk_type == SOCK_STREAM) {
-                               if (sk->sk_state != TCP_ESTABLISHED) {
+                               if ((1 << sk->sk_state) &
+                                   (TCPF_CLOSE | TCPF_LISTEN)) {
                                        ret = -EINVAL;
                                        break;
                                }
@@ -1418,8 +1426,12 @@ struct sock *sk_alloc(struct net *net, int family, gfp_t priority,
 }
 EXPORT_SYMBOL(sk_alloc);
 
-void sk_destruct(struct sock *sk)
+/* Sockets having SOCK_RCU_FREE will call this function after one RCU
+ * grace period. This is the case for UDP sockets and TCP listeners.
+ */
+static void __sk_destruct(struct rcu_head *head)
 {
+       struct sock *sk = container_of(head, struct sock, sk_rcu);
        struct sk_filter *filter;
 
        if (sk->sk_destruct)
@@ -1448,6 +1460,14 @@ void sk_destruct(struct sock *sk)
        sk_prot_free(sk->sk_prot_creator, sk);
 }
 
+void sk_destruct(struct sock *sk)
+{
+       if (sock_flag(sk, SOCK_RCU_FREE))
+               call_rcu(&sk->sk_rcu, __sk_destruct);
+       else
+               __sk_destruct(&sk->sk_rcu);
+}
+
 static void __sk_free(struct sock *sk)
 {
        if (unlikely(sock_diag_has_destroy_listeners(sk) && sk->sk_net_refcnt))
@@ -1512,6 +1532,7 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
                newsk->sk_dst_cache     = NULL;
                newsk->sk_wmem_queued   = 0;
                newsk->sk_forward_alloc = 0;
+               atomic_set(&newsk->sk_drops, 0);
                newsk->sk_send_head     = NULL;
                newsk->sk_userlocks     = sk->sk_userlocks & ~SOCK_BINDPORT_LOCK;
 
@@ -1866,27 +1887,51 @@ struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size,
 }
 EXPORT_SYMBOL(sock_alloc_send_skb);
 
+int __sock_cmsg_send(struct sock *sk, struct msghdr *msg, struct cmsghdr *cmsg,
+                    struct sockcm_cookie *sockc)
+{
+       u32 tsflags;
+
+       switch (cmsg->cmsg_type) {
+       case SO_MARK:
+               if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
+                       return -EPERM;
+               if (cmsg->cmsg_len != CMSG_LEN(sizeof(u32)))
+                       return -EINVAL;
+               sockc->mark = *(u32 *)CMSG_DATA(cmsg);
+               break;
+       case SO_TIMESTAMPING:
+               if (cmsg->cmsg_len != CMSG_LEN(sizeof(u32)))
+                       return -EINVAL;
+
+               tsflags = *(u32 *)CMSG_DATA(cmsg);
+               if (tsflags & ~SOF_TIMESTAMPING_TX_RECORD_MASK)
+                       return -EINVAL;
+
+               sockc->tsflags &= ~SOF_TIMESTAMPING_TX_RECORD_MASK;
+               sockc->tsflags |= tsflags;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+EXPORT_SYMBOL(__sock_cmsg_send);
+
 int sock_cmsg_send(struct sock *sk, struct msghdr *msg,
                   struct sockcm_cookie *sockc)
 {
        struct cmsghdr *cmsg;
+       int ret;
 
        for_each_cmsghdr(cmsg, msg) {
                if (!CMSG_OK(msg, cmsg))
                        return -EINVAL;
                if (cmsg->cmsg_level != SOL_SOCKET)
                        continue;
-               switch (cmsg->cmsg_type) {
-               case SO_MARK:
-                       if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
-                               return -EPERM;
-                       if (cmsg->cmsg_len != CMSG_LEN(sizeof(u32)))
-                               return -EINVAL;
-                       sockc->mark = *(u32 *)CMSG_DATA(cmsg);
-                       break;
-               default:
-                       return -EINVAL;
-               }
+               ret = __sock_cmsg_send(sk, msg, cmsg, sockc);
+               if (ret)
+                       return ret;
        }
        return 0;
 }
@@ -2142,6 +2187,15 @@ void __sk_mem_reclaim(struct sock *sk, int amount)
 }
 EXPORT_SYMBOL(__sk_mem_reclaim);
 
+int sk_set_peek_off(struct sock *sk, int val)
+{
+       if (val < 0)
+               return -EINVAL;
+
+       sk->sk_peek_off = val;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sk_set_peek_off);
 
 /*
  * Set of default routines for initialising struct proto_ops when