Merge tag 'xfs-4.21-merge-3' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux
[linux] / net / sctp / socket.c
index b8cebd5..f93c3cf 100644 (file)
@@ -2230,7 +2230,7 @@ static int sctp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
        if (sp->recvrcvinfo)
                sctp_ulpevent_read_rcvinfo(event, msg);
        /* Check if we allow SCTP_SNDRCVINFO. */
-       if (sp->subscribe.sctp_data_io_event)
+       if (sctp_ulpevent_type_enabled(sp->subscribe, SCTP_DATA_IO_EVENT))
                sctp_ulpevent_read_sndrcvinfo(event, msg);
 
        err = copied;
@@ -2304,22 +2304,33 @@ static int sctp_setsockopt_disable_fragments(struct sock *sk,
 static int sctp_setsockopt_events(struct sock *sk, char __user *optval,
                                  unsigned int optlen)
 {
+       struct sctp_event_subscribe subscribe;
+       __u8 *sn_type = (__u8 *)&subscribe;
+       struct sctp_sock *sp = sctp_sk(sk);
        struct sctp_association *asoc;
-       struct sctp_ulpevent *event;
+       int i;
 
        if (optlen > sizeof(struct sctp_event_subscribe))
                return -EINVAL;
-       if (copy_from_user(&sctp_sk(sk)->subscribe, optval, optlen))
+
+       if (copy_from_user(&subscribe, optval, optlen))
                return -EFAULT;
 
+       for (i = 0; i < optlen; i++)
+               sctp_ulpevent_type_set(&sp->subscribe, SCTP_SN_TYPE_BASE + i,
+                                      sn_type[i]);
+
+       list_for_each_entry(asoc, &sp->ep->asocs, asocs)
+               asoc->subscribe = sctp_sk(sk)->subscribe;
+
        /* At the time when a user app subscribes to SCTP_SENDER_DRY_EVENT,
         * if there is no data to be sent or retransmit, the stack will
         * immediately send up this notification.
         */
-       if (sctp_ulpevent_type_enabled(SCTP_SENDER_DRY_EVENT,
-                                      &sctp_sk(sk)->subscribe)) {
-               asoc = sctp_id2assoc(sk, 0);
+       if (sctp_ulpevent_type_enabled(sp->subscribe, SCTP_SENDER_DRY_EVENT)) {
+               struct sctp_ulpevent *event;
 
+               asoc = sctp_id2assoc(sk, 0);
                if (asoc && sctp_outq_is_empty(&asoc->outqueue)) {
                        event = sctp_ulpevent_make_sender_dry_event(asoc,
                                        GFP_USER | __GFP_NOWARN);
@@ -4260,6 +4271,57 @@ static int sctp_setsockopt_reuse_port(struct sock *sk, char __user *optval,
        return 0;
 }
 
+static int sctp_setsockopt_event(struct sock *sk, char __user *optval,
+                                unsigned int optlen)
+{
+       struct sctp_association *asoc;
+       struct sctp_ulpevent *event;
+       struct sctp_event param;
+       int retval = 0;
+
+       if (optlen < sizeof(param)) {
+               retval = -EINVAL;
+               goto out;
+       }
+
+       optlen = sizeof(param);
+       if (copy_from_user(&param, optval, optlen)) {
+               retval = -EFAULT;
+               goto out;
+       }
+
+       if (param.se_type < SCTP_SN_TYPE_BASE ||
+           param.se_type > SCTP_SN_TYPE_MAX) {
+               retval = -EINVAL;
+               goto out;
+       }
+
+       asoc = sctp_id2assoc(sk, param.se_assoc_id);
+       if (!asoc) {
+               sctp_ulpevent_type_set(&sctp_sk(sk)->subscribe,
+                                      param.se_type, param.se_on);
+               goto out;
+       }
+
+       sctp_ulpevent_type_set(&asoc->subscribe, param.se_type, param.se_on);
+
+       if (param.se_type == SCTP_SENDER_DRY_EVENT && param.se_on) {
+               if (sctp_outq_is_empty(&asoc->outqueue)) {
+                       event = sctp_ulpevent_make_sender_dry_event(asoc,
+                                       GFP_USER | __GFP_NOWARN);
+                       if (!event) {
+                               retval = -ENOMEM;
+                               goto out;
+                       }
+
+                       asoc->stream.si->enqueue_event(&asoc->ulpq, event);
+               }
+       }
+
+out:
+       return retval;
+}
+
 /* API 6.2 setsockopt(), getsockopt()
  *
  * Applications use setsockopt() and getsockopt() to set or retrieve
@@ -4457,6 +4519,9 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname,
        case SCTP_REUSE_PORT:
                retval = sctp_setsockopt_reuse_port(sk, optval, optlen);
                break;
+       case SCTP_EVENT:
+               retval = sctp_setsockopt_event(sk, optval, optlen);
+               break;
        default:
                retval = -ENOPROTOOPT;
                break;
@@ -4705,7 +4770,7 @@ static int sctp_init_sock(struct sock *sk)
        /* Initialize default event subscriptions. By default, all the
         * options are off.
         */
-       memset(&sp->subscribe, 0, sizeof(struct sctp_event_subscribe));
+       sp->subscribe = 0;
 
        /* Default Peer Address Parameters.  These defaults can
         * be modified via SCTP_PEER_ADDR_PARAMS
@@ -5250,14 +5315,24 @@ static int sctp_getsockopt_disable_fragments(struct sock *sk, int len,
 static int sctp_getsockopt_events(struct sock *sk, int len, char __user *optval,
                                  int __user *optlen)
 {
+       struct sctp_event_subscribe subscribe;
+       __u8 *sn_type = (__u8 *)&subscribe;
+       int i;
+
        if (len == 0)
                return -EINVAL;
        if (len > sizeof(struct sctp_event_subscribe))
                len = sizeof(struct sctp_event_subscribe);
        if (put_user(len, optlen))
                return -EFAULT;
-       if (copy_to_user(optval, &sctp_sk(sk)->subscribe, len))
+
+       for (i = 0; i < len; i++)
+               sn_type[i] = sctp_ulpevent_type_enabled(sctp_sk(sk)->subscribe,
+                                                       SCTP_SN_TYPE_BASE + i);
+
+       if (copy_to_user(optval, &subscribe, len))
                return -EFAULT;
+
        return 0;
 }
 
@@ -7392,6 +7467,37 @@ static int sctp_getsockopt_reuse_port(struct sock *sk, int len,
        return 0;
 }
 
+static int sctp_getsockopt_event(struct sock *sk, int len, char __user *optval,
+                                int __user *optlen)
+{
+       struct sctp_association *asoc;
+       struct sctp_event param;
+       __u16 subscribe;
+
+       if (len < sizeof(param))
+               return -EINVAL;
+
+       len = sizeof(param);
+       if (copy_from_user(&param, optval, len))
+               return -EFAULT;
+
+       if (param.se_type < SCTP_SN_TYPE_BASE ||
+           param.se_type > SCTP_SN_TYPE_MAX)
+               return -EINVAL;
+
+       asoc = sctp_id2assoc(sk, param.se_assoc_id);
+       subscribe = asoc ? asoc->subscribe : sctp_sk(sk)->subscribe;
+       param.se_on = sctp_ulpevent_type_enabled(subscribe, param.se_type);
+
+       if (put_user(len, optlen))
+               return -EFAULT;
+
+       if (copy_to_user(optval, &param, len))
+               return -EFAULT;
+
+       return 0;
+}
+
 static int sctp_getsockopt(struct sock *sk, int level, int optname,
                           char __user *optval, int __user *optlen)
 {
@@ -7590,6 +7696,9 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname,
        case SCTP_REUSE_PORT:
                retval = sctp_getsockopt_reuse_port(sk, len, optval, optlen);
                break;
+       case SCTP_EVENT:
+               retval = sctp_getsockopt_event(sk, len, optval, optlen);
+               break;
        default:
                retval = -ENOPROTOOPT;
                break;
@@ -7627,8 +7736,10 @@ static struct sctp_bind_bucket *sctp_bucket_create(
 
 static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
 {
-       bool reuse = (sk->sk_reuse || sctp_sk(sk)->reuse);
+       struct sctp_sock *sp = sctp_sk(sk);
+       bool reuse = (sk->sk_reuse || sp->reuse);
        struct sctp_bind_hashbucket *head; /* hash list */
+       kuid_t uid = sock_i_uid(sk);
        struct sctp_bind_bucket *pp;
        unsigned short snum;
        int ret;
@@ -7704,7 +7815,10 @@ pp_found:
 
                pr_debug("%s: found a possible match\n", __func__);
 
-               if (pp->fastreuse && reuse && sk->sk_state != SCTP_SS_LISTENING)
+               if ((pp->fastreuse && reuse &&
+                    sk->sk_state != SCTP_SS_LISTENING) ||
+                   (pp->fastreuseport && sk->sk_reuseport &&
+                    uid_eq(pp->fastuid, uid)))
                        goto success;
 
                /* Run through the list of sockets bound to the port
@@ -7718,16 +7832,18 @@ pp_found:
                 * in an endpoint.
                 */
                sk_for_each_bound(sk2, &pp->owner) {
-                       struct sctp_endpoint *ep2;
-                       ep2 = sctp_sk(sk2)->ep;
+                       struct sctp_sock *sp2 = sctp_sk(sk2);
+                       struct sctp_endpoint *ep2 = sp2->ep;
 
                        if (sk == sk2 ||
-                           (reuse && (sk2->sk_reuse || sctp_sk(sk2)->reuse) &&
-                            sk2->sk_state != SCTP_SS_LISTENING))
+                           (reuse && (sk2->sk_reuse || sp2->reuse) &&
+                            sk2->sk_state != SCTP_SS_LISTENING) ||
+                           (sk->sk_reuseport && sk2->sk_reuseport &&
+                            uid_eq(uid, sock_i_uid(sk2))))
                                continue;
 
-                       if (sctp_bind_addr_conflict(&ep2->base.bind_addr, addr,
-                                                sctp_sk(sk2), sctp_sk(sk))) {
+                       if (sctp_bind_addr_conflict(&ep2->base.bind_addr,
+                                                   addr, sp2, sp)) {
                                ret = (long)sk2;
                                goto fail_unlock;
                        }
@@ -7750,19 +7866,32 @@ pp_not_found:
                        pp->fastreuse = 1;
                else
                        pp->fastreuse = 0;
-       } else if (pp->fastreuse &&
-                  (!reuse || sk->sk_state == SCTP_SS_LISTENING))
-               pp->fastreuse = 0;
+
+               if (sk->sk_reuseport) {
+                       pp->fastreuseport = 1;
+                       pp->fastuid = uid;
+               } else {
+                       pp->fastreuseport = 0;
+               }
+       } else {
+               if (pp->fastreuse &&
+                   (!reuse || sk->sk_state == SCTP_SS_LISTENING))
+                       pp->fastreuse = 0;
+
+               if (pp->fastreuseport &&
+                   (!sk->sk_reuseport || !uid_eq(pp->fastuid, uid)))
+                       pp->fastreuseport = 0;
+       }
 
        /* We are set, so fill up all the data in the hash table
         * entry, tie the socket list information with the rest of the
         * sockets FIXME: Blurry, NPI (ipg).
         */
 success:
-       if (!sctp_sk(sk)->bind_hash) {
+       if (!sp->bind_hash) {
                inet_sk(sk)->inet_num = snum;
                sk_add_bind_node(sk, &pp->owner);
-               sctp_sk(sk)->bind_hash = pp;
+               sp->bind_hash = pp;
        }
        ret = 0;
 
@@ -7835,8 +7964,7 @@ static int sctp_listen_start(struct sock *sk, int backlog)
        }
 
        sk->sk_max_ack_backlog = backlog;
-       sctp_hash_endpoint(ep);
-       return 0;
+       return sctp_hash_endpoint(ep);
 }
 
 /*