]> err.no Git - linux-2.6/blobdiff - net/sctp/socket.c
rfkill: fix led-trigger unregister order in error unwind
[linux-2.6] / net / sctp / socket.c
index 66985871401be47747946eb1e1d8e13d207fd229..dbb79adf8f3c0bab606f7141e96ae96dd29b3e4c 100644 (file)
@@ -116,7 +116,7 @@ static int sctp_memory_pressure;
 static atomic_t sctp_memory_allocated;
 static atomic_t sctp_sockets_allocated;
 
-static void sctp_enter_memory_pressure(void)
+static void sctp_enter_memory_pressure(struct sock *sk)
 {
        sctp_memory_pressure = 1;
 }
@@ -308,9 +308,16 @@ static struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt,
        if (len < sizeof (struct sockaddr))
                return NULL;
 
-       /* Does this PF support this AF? */
-       if (!opt->pf->af_supported(addr->sa.sa_family, opt))
-               return NULL;
+       /* V4 mapped address are really of AF_INET family */
+       if (addr->sa.sa_family == AF_INET6 &&
+           ipv6_addr_v4mapped(&addr->v6.sin6_addr)) {
+               if (!opt->pf->af_supported(AF_INET, opt))
+                       return NULL;
+       } else {
+               /* Does this PF support this AF? */
+               if (!opt->pf->af_supported(addr->sa.sa_family, opt))
+                       return NULL;
+       }
 
        /* If we get this far, af is valid. */
        af = sctp_get_af_specific(addr->sa.sa_family);
@@ -370,18 +377,19 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
        if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
                return -EACCES;
 
+       /* See if the address matches any of the addresses we may have
+        * already bound before checking against other endpoints.
+        */
+       if (sctp_bind_addr_match(bp, addr, sp))
+               return -EINVAL;
+
        /* Make sure we are allowed to bind here.
         * The function sctp_get_port_local() does duplicate address
         * detection.
         */
        addr->v4.sin_port = htons(snum);
        if ((ret = sctp_get_port_local(sk, addr))) {
-               if (ret == (long) sk) {
-                       /* This endpoint has a conflicting address. */
-                       return -EINVAL;
-               } else {
-                       return -EADDRINUSE;
-               }
+               return -EADDRINUSE;
        }
 
        /* Refresh ephemeral port.  */
@@ -956,7 +964,8 @@ out:
  */
 static int __sctp_connect(struct sock* sk,
                          struct sockaddr *kaddrs,
-                         int addrs_size)
+                         int addrs_size,
+                         sctp_assoc_t *assoc_id)
 {
        struct sctp_sock *sp;
        struct sctp_endpoint *ep;
@@ -1111,6 +1120,8 @@ static int __sctp_connect(struct sock* sk,
        timeo = sock_sndtimeo(sk, f_flags & O_NONBLOCK);
 
        err = sctp_wait_for_connect(asoc, &timeo);
+       if (!err && assoc_id)
+               *assoc_id = asoc->assoc_id;
 
        /* Don't free association on exit. */
        asoc = NULL;
@@ -1128,7 +1139,8 @@ out_free:
 /* Helper for tunneling sctp_connectx() requests through sctp_setsockopt()
  *
  * API 8.9
- * int sctp_connectx(int sd, struct sockaddr *addrs, int addrcnt);
+ * int sctp_connectx(int sd, struct sockaddr *addrs, int addrcnt,
+ *                     sctp_assoc_t *asoc);
  *
  * If sd is an IPv4 socket, the addresses passed must be IPv4 addresses.
  * If the sd is an IPv6 socket, the addresses passed can either be IPv4
@@ -1144,8 +1156,10 @@ out_free:
  * representation is termed a "packed array" of addresses). The caller
  * specifies the number of addresses in the array with addrcnt.
  *
- * On success, sctp_connectx() returns 0. On failure, sctp_connectx() returns
- * -1, and sets errno to the appropriate error code.
+ * On success, sctp_connectx() returns 0. It also sets the assoc_id to
+ * the association id of the new association.  On failure, sctp_connectx()
+ * returns -1, and sets errno to the appropriate error code.  The assoc_id
+ * is not touched by the kernel.
  *
  * For SCTP, the port given in each socket address must be the same, or
  * sctp_connectx() will fail, setting errno to EINVAL.
@@ -1182,11 +1196,12 @@ out_free:
  * addrs     The pointer to the addresses in user land
  * addrssize Size of the addrs buffer
  *
- * Returns 0 if ok, <0 errno code on error.
+ * Returns >=0 if ok, <0 errno code on error.
  */
-SCTP_STATIC int sctp_setsockopt_connectx(struct sock* sk,
+SCTP_STATIC int __sctp_setsockopt_connectx(struct sock* sk,
                                      struct sockaddr __user *addrs,
-                                     int addrs_size)
+                                     int addrs_size,
+                                     sctp_assoc_t *assoc_id)
 {
        int err = 0;
        struct sockaddr *kaddrs;
@@ -1209,13 +1224,46 @@ SCTP_STATIC int sctp_setsockopt_connectx(struct sock* sk,
        if (__copy_from_user(kaddrs, addrs, addrs_size)) {
                err = -EFAULT;
        } else {
-               err = __sctp_connect(sk, kaddrs, addrs_size);
+               err = __sctp_connect(sk, kaddrs, addrs_size, assoc_id);
        }
 
        kfree(kaddrs);
+
        return err;
 }
 
+/*
+ * This is an older interface.  It's kept for backward compatibility
+ * to the option that doesn't provide association id.
+ */
+SCTP_STATIC int sctp_setsockopt_connectx_old(struct sock* sk,
+                                     struct sockaddr __user *addrs,
+                                     int addrs_size)
+{
+       return __sctp_setsockopt_connectx(sk, addrs, addrs_size, NULL);
+}
+
+/*
+ * New interface for the API.  The since the API is done with a socket
+ * option, to make it simple we feed back the association id is as a return
+ * indication to the call.  Error is always negative and association id is
+ * always positive.
+ */
+SCTP_STATIC int sctp_setsockopt_connectx(struct sock* sk,
+                                     struct sockaddr __user *addrs,
+                                     int addrs_size)
+{
+       sctp_assoc_t assoc_id = 0;
+       int err = 0;
+
+       err = __sctp_setsockopt_connectx(sk, addrs, addrs_size, &assoc_id);
+
+       if (err)
+               return err;
+       else
+               return assoc_id;
+}
+
 /* API 3.1.4 close() - UDP Style Syntax
  * Applications use close() to perform graceful shutdown (as described in
  * Section 10.1 of [SCTP]) on ALL the associations currently represented
@@ -2431,7 +2479,7 @@ static int sctp_setsockopt_delayed_ack(struct sock *sk,
                                        (trans->param_flags & ~SPP_SACKDELAY) |
                                        SPP_SACKDELAY_ENABLE;
                        }
-                       if (params.sack_delay == 1) {
+                       if (params.sack_freq == 1) {
                                trans->param_flags =
                                        (trans->param_flags & ~SPP_SACKDELAY) |
                                        SPP_SACKDELAY_DISABLE;
@@ -3206,10 +3254,18 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
                                               optlen, SCTP_BINDX_REM_ADDR);
                break;
 
+       case SCTP_SOCKOPT_CONNECTX_OLD:
+               /* 'optlen' is the size of the addresses buffer. */
+               retval = sctp_setsockopt_connectx_old(sk,
+                                           (struct sockaddr __user *)optval,
+                                           optlen);
+               break;
+
        case SCTP_SOCKOPT_CONNECTX:
                /* 'optlen' is the size of the addresses buffer. */
-               retval = sctp_setsockopt_connectx(sk, (struct sockaddr __user *)optval,
-                                              optlen);
+               retval = sctp_setsockopt_connectx(sk,
+                                           (struct sockaddr __user *)optval,
+                                           optlen);
                break;
 
        case SCTP_DISABLE_FRAGMENTS:
@@ -3336,7 +3392,7 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *addr,
                /* Pass correct addr len to common routine (so it knows there
                 * is only one address being passed.
                 */
-               err = __sctp_connect(sk, addr, af->sockaddr_len);
+               err = __sctp_connect(sk, addr, af->sockaddr_len, NULL);
        }
 
        sctp_release_sock(sk);
@@ -3488,7 +3544,7 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
        sp->pathmaxrxt  = sctp_max_retrans_path;
        sp->pathmtu     = 0; // allow default discovery
        sp->sackdelay   = sctp_sack_timeout;
-       sp->sackfreq    = 3;
+       sp->sackfreq    = 2;
        sp->param_flags = SPP_HB_ENABLE |
                          SPP_PMTUD_ENABLE |
                          SPP_SACKDELAY_ENABLE;
@@ -3540,7 +3596,7 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
 }
 
 /* Cleanup any SCTP per socket resources.  */
-SCTP_STATIC int sctp_destroy_sock(struct sock *sk)
+SCTP_STATIC void sctp_destroy_sock(struct sock *sk)
 {
        struct sctp_endpoint *ep;
 
@@ -3550,7 +3606,6 @@ SCTP_STATIC int sctp_destroy_sock(struct sock *sk)
        ep = sctp_sk(sk)->ep;
        sctp_endpoint_free(ep);
        atomic_dec(&sctp_sockets_allocated);
-       return 0;
 }
 
 /* API 4.1.7 shutdown() - TCP Style Syntax
@@ -3855,7 +3910,7 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval
                goto out;
 
        /* Map the socket to an unused fd that can be returned to the user.  */
-       retval = sock_map_fd(newsock);
+       retval = sock_map_fd(newsock, 0);
        if (retval < 0) {
                sock_release(newsock);
                goto out;
@@ -4176,6 +4231,8 @@ static int sctp_getsockopt_peer_addrs_num_old(struct sock *sk, int len,
        if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
                return -EFAULT;
 
+       printk(KERN_WARNING "SCTP: Use of SCTP_GET_PEER_ADDRS_NUM_OLD "
+                           "socket option deprecated\n");
        /* For UDP-style sockets, id specifies the association to query.  */
        asoc = sctp_id2assoc(sk, id);
        if (!asoc)
@@ -4215,6 +4272,9 @@ static int sctp_getsockopt_peer_addrs_old(struct sock *sk, int len,
 
        if (getaddrs.addr_num <= 0) return -EINVAL;
 
+       printk(KERN_WARNING "SCTP: Use of SCTP_GET_PEER_ADDRS_OLD "
+                           "socket option deprecated\n");
+
        /* For UDP-style sockets, id specifies the association to query.  */
        asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
        if (!asoc)
@@ -4308,6 +4368,9 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len,
        if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
                return -EFAULT;
 
+       printk(KERN_WARNING "SCTP: Use of SCTP_GET_LOCAL_ADDRS_NUM_OLD "
+                           "socket option deprecated\n");
+
        /*
         *  For UDP-style sockets, id specifies the association to query.
         *  If the id field is set to the value '0' then the locally bound
@@ -4340,6 +4403,11 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len,
                                    (AF_INET6 == addr->a.sa.sa_family))
                                        continue;
 
+                               if ((PF_INET6 == sk->sk_family) &&
+                                   inet_v6_ipv6only(sk) &&
+                                   (AF_INET == addr->a.sa.sa_family))
+                                       continue;
+
                                cnt++;
                        }
                        rcu_read_unlock();
@@ -4380,6 +4448,10 @@ static int sctp_copy_laddrs_old(struct sock *sk, __u16 port,
                if ((PF_INET == sk->sk_family) &&
                    (AF_INET6 == addr->a.sa.sa_family))
                        continue;
+               if ((PF_INET6 == sk->sk_family) &&
+                   inet_v6_ipv6only(sk) &&
+                   (AF_INET == addr->a.sa.sa_family))
+                       continue;
                memcpy(&temp, &addr->a, sizeof(temp));
                if (!temp.v4.sin_port)
                        temp.v4.sin_port = htons(port);
@@ -4415,6 +4487,10 @@ static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to,
                if ((PF_INET == sk->sk_family) &&
                    (AF_INET6 == addr->a.sa.sa_family))
                        continue;
+               if ((PF_INET6 == sk->sk_family) &&
+                   inet_v6_ipv6only(sk) &&
+                   (AF_INET == addr->a.sa.sa_family))
+                       continue;
                memcpy(&temp, &addr->a, sizeof(temp));
                if (!temp.v4.sin_port)
                        temp.v4.sin_port = htons(port);
@@ -4465,7 +4541,13 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
        if (copy_from_user(&getaddrs, optval, len))
                return -EFAULT;
 
-       if (getaddrs.addr_num <= 0) return -EINVAL;
+       if (getaddrs.addr_num <= 0 ||
+           getaddrs.addr_num >= (INT_MAX / sizeof(union sctp_addr)))
+               return -EINVAL;
+
+       printk(KERN_WARNING "SCTP: Use of SCTP_GET_LOCAL_ADDRS_OLD "
+                           "socket option deprecated\n");
+
        /*
         *  For UDP-style sockets, id specifies the association to query.
         *  If the id field is set to the value '0' then the locally bound
@@ -5503,12 +5585,13 @@ pp_found:
                        struct sctp_endpoint *ep2;
                        ep2 = sctp_sk(sk2)->ep;
 
-                       if (reuse && sk2->sk_reuse &&
-                           sk2->sk_state != SCTP_SS_LISTENING)
+                       if (sk == sk2 ||
+                           (reuse && sk2->sk_reuse &&
+                            sk2->sk_state != SCTP_SS_LISTENING))
                                continue;
 
-                       if (sctp_bind_addr_match(&ep2->base.bind_addr, addr,
-                                                sctp_sk(sk))) {
+                       if (sctp_bind_addr_conflict(&ep2->base.bind_addr, addr,
+                                                sctp_sk(sk2), sctp_sk(sk))) {
                                ret = (long)sk2;
                                goto fail_unlock;
                        }
@@ -5621,8 +5704,13 @@ SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog)
        if (!ep->base.bind_addr.port) {
                if (sctp_autobind(sk))
                        return -EAGAIN;
-       } else
+       } else {
+               if (sctp_get_port(sk, inet_sk(sk)->num)) {
+                       sk->sk_state = SCTP_SS_CLOSED;
+                       return -EADDRINUSE;
+               }
                sctp_sk(sk)->bind_hash->fastreuse = 0;
+       }
 
        sctp_hash_endpoint(ep);
        return 0;
@@ -5692,7 +5780,7 @@ int sctp_inet_listen(struct socket *sock, int backlog)
                goto out;
 
        /* Allocate HMAC for generating cookie. */
-       if (sctp_hmac_alg) {
+       if (!sctp_sk(sk)->hmac && sctp_hmac_alg) {
                tfm = crypto_alloc_hash(sctp_hmac_alg, 0, CRYPTO_ALG_ASYNC);
                if (IS_ERR(tfm)) {
                        if (net_ratelimit()) {
@@ -5720,7 +5808,8 @@ int sctp_inet_listen(struct socket *sock, int backlog)
                goto cleanup;
 
        /* Store away the transform reference. */
-       sctp_sk(sk)->hmac = tfm;
+       if (!sctp_sk(sk)->hmac)
+               sctp_sk(sk)->hmac = tfm;
 out:
        sctp_release_sock(sk);
        return err;