[SCTP]: Set ports in every address returned by sctp_getladdrs()
[safe/jmp/linux-2.6] / net / sctp / socket.c
index 935bc91..d47d578 100644 (file)
@@ -1,4 +1,4 @@
-/* SCTP kernel reference Implementation
+/* SCTP kernel implementation
  * (C) Copyright IBM Corp. 2001, 2004
  * Copyright (c) 1999-2000 Cisco, Inc.
  * Copyright (c) 1999-2001 Motorola, Inc.
@@ -6,7 +6,7 @@
  * Copyright (c) 2001-2002 Nokia, Inc.
  * Copyright (c) 2001 La Monte H.P. Yarroll
  *
- * This file is part of the SCTP kernel reference Implementation
+ * This file is part of the SCTP kernel implementation
  *
  * These functions interface with the sockets layer to implement the
  * SCTP Extensions for the Sockets API.
  * functions--this file is the functions which populate the struct proto
  * for SCTP which is the BOTTOM of the sockets interface.
  *
- * The SCTP reference implementation is free software;
+ * This SCTP implementation is free software;
  * you can redistribute it and/or modify it under the terms of
  * the GNU General Public License as published by
  * the Free Software Foundation; either version 2, or (at your option)
  * any later version.
  *
- * The SCTP reference implementation is distributed in the hope that it
+ * This SCTP implementation is distributed in the hope that it
  * will be useful, but WITHOUT ANY WARRANTY; without even the implied
  *                 ************************
  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
@@ -107,25 +107,42 @@ static void sctp_sock_migrate(struct sock *, struct sock *,
                              struct sctp_association *, sctp_socket_type_t);
 static char *sctp_hmac_alg = SCTP_COOKIE_HMAC_ALG;
 
-extern kmem_cache_t *sctp_bucket_cachep;
+extern struct kmem_cache *sctp_bucket_cachep;
+extern int sysctl_sctp_mem[3];
+extern int sysctl_sctp_rmem[3];
+extern int sysctl_sctp_wmem[3];
+
+static int sctp_memory_pressure;
+static atomic_t sctp_memory_allocated;
+static atomic_t sctp_sockets_allocated;
+
+static void sctp_enter_memory_pressure(void)
+{
+       sctp_memory_pressure = 1;
+}
+
 
 /* Get the sndbuf space available at the time on the association.  */
 static inline int sctp_wspace(struct sctp_association *asoc)
 {
-       struct sock *sk = asoc->base.sk;
-       int amt = 0;
+       int amt;
 
-       if (asoc->ep->sndbuf_policy) {
-               /* make sure that no association uses more than sk_sndbuf */
-               amt = sk->sk_sndbuf - asoc->sndbuf_used;
+       if (asoc->ep->sndbuf_policy)
+               amt = asoc->sndbuf_used;
+       else
+               amt = atomic_read(&asoc->base.sk->sk_wmem_alloc);
+
+       if (amt >= asoc->base.sk->sk_sndbuf) {
+               if (asoc->base.sk->sk_userlocks & SOCK_SNDBUF_LOCK)
+                       amt = 0;
+               else {
+                       amt = sk_stream_wspace(asoc->base.sk);
+                       if (amt < 0)
+                               amt = 0;
+               }
        } else {
-               /* do socket level accounting */
-               amt = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc);
+               amt = asoc->base.sk->sk_sndbuf - amt;
        }
-
-       if (amt < 0)
-               amt = 0;
-
        return amt;
 }
 
@@ -157,6 +174,8 @@ static inline void sctp_set_owner_w(struct sctp_chunk *chunk)
                                sizeof(struct sctp_chunk);
 
        atomic_add(sizeof(struct sctp_chunk), &sk->sk_wmem_alloc);
+       sk->sk_wmem_queued += chunk->skb->truesize;
+       sk_mem_charge(sk, chunk->skb->truesize);
 }
 
 /* Verify that this is a valid address. */
@@ -229,11 +248,9 @@ static struct sctp_transport *sctp_addr_id2transport(struct sock *sk,
        struct sctp_transport *transport;
        union sctp_addr *laddr = (union sctp_addr *)addr;
 
-       laddr->v4.sin_port = ntohs(laddr->v4.sin_port);
        addr_asoc = sctp_endpoint_lookup_assoc(sctp_sk(sk)->ep,
-                                              (union sctp_addr *)addr,
+                                              laddr,
                                               &transport);
-       laddr->v4.sin_port = htons(laddr->v4.sin_port);
 
        if (!addr_asoc)
                return NULL;
@@ -335,12 +352,19 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
        if (!sp->pf->bind_verify(sp, addr))
                return -EADDRNOTAVAIL;
 
-       /* We must either be unbound, or bind to the same port.  */
-       if (bp->port && (snum != bp->port)) {
-               SCTP_DEBUG_PRINTK("sctp_do_bind:"
+       /* We must either be unbound, or bind to the same port.
+        * It's OK to allow 0 ports if we are already bound.
+        * We'll just inhert an already bound port in this case
+        */
+       if (bp->port) {
+               if (!snum)
+                       snum = bp->port;
+               else if (snum != bp->port) {
+                       SCTP_DEBUG_PRINTK("sctp_do_bind:"
                                  " New port %d does not match existing port "
                                  "%d.\n", snum, bp->port);
-               return -EINVAL;
+                       return -EINVAL;
+               }
        }
 
        if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
@@ -350,6 +374,7 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
         * 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. */
@@ -363,16 +388,10 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
        if (!bp->port)
                bp->port = inet_sk(sk)->num;
 
-       /* Add the address to the bind address list.  */
-       sctp_local_bh_disable();
-       sctp_write_lock(&ep->base.addr_lock);
-
-       /* Use GFP_ATOMIC since BHs are disabled.  */
-       addr->v4.sin_port = ntohs(addr->v4.sin_port);
-       ret = sctp_add_bind_addr(bp, addr, 1, GFP_ATOMIC);
-       addr->v4.sin_port = htons(addr->v4.sin_port);
-       sctp_write_unlock(&ep->base.addr_lock);
-       sctp_local_bh_enable();
+       /* Add the address to the bind address list.
+        * Use GFP_ATOMIC since BHs will be disabled.
+        */
+       ret = sctp_add_bind_addr(bp, addr, SCTP_ADDR_SRC, GFP_ATOMIC);
 
        /* Copy back into socket for getsockname() use. */
        if (!ret) {
@@ -385,12 +404,12 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
 
  /* ADDIP Section 4.1.1 Congestion Control of ASCONF Chunks
  *
- * R1) One and only one ASCONF Chunk MAY be in transit and unacknowledged 
+ * R1) One and only one ASCONF Chunk MAY be in transit and unacknowledged
  * at any one time.  If a sender, after sending an ASCONF chunk, decides
- * it needs to transfer another ASCONF Chunk, it MUST wait until the 
+ * it needs to transfer another ASCONF Chunk, it MUST wait until the
  * ASCONF-ACK Chunk returns from the previous ASCONF Chunk before sending a
- * subsequent ASCONF. Note this restriction binds each side, so at any 
- * time two ASCONF may be in-transit on any given association (one sent 
+ * subsequent ASCONF. Note this restriction binds each side, so at any
+ * time two ASCONF may be in-transit on any given association (one sent
  * from each endpoint).
  */
 static int sctp_send_asconf(struct sctp_association *asoc,
@@ -400,10 +419,10 @@ static int sctp_send_asconf(struct sctp_association *asoc,
 
        /* If there is an outstanding ASCONF chunk, queue it for later
         * transmission.
-        */     
+        */
        if (asoc->addip_last_asconf) {
                list_add_tail(&chunk->list, &asoc->addip_chunk_list);
-               goto out;       
+               goto out;
        }
 
        /* Hold the chunk until an ASCONF_ACK is received. */
@@ -430,7 +449,7 @@ out:
  *
  * Only sctp_setsockopt_bindx() is supposed to call this function.
  */
-int sctp_bindx_add(struct sock *sk, struct sockaddr *addrs, int addrcnt)
+static int sctp_bindx_add(struct sock *sk, struct sockaddr *addrs, int addrcnt)
 {
        int cnt;
        int retval = 0;
@@ -453,7 +472,7 @@ int sctp_bindx_add(struct sock *sk, struct sockaddr *addrs, int addrcnt)
                        goto err_bindx_add;
                }
 
-               retval = sctp_do_bind(sk, (union sctp_addr *)sa_addr, 
+               retval = sctp_do_bind(sk, (union sctp_addr *)sa_addr,
                                      af->sockaddr_len);
 
                addr_buf += af->sockaddr_len;
@@ -474,13 +493,13 @@ err_bindx_add:
  * associations that are part of the endpoint indicating that a list of local
  * addresses are added to the endpoint.
  *
- * If any of the addresses is already in the bind address list of the 
+ * If any of the addresses is already in the bind address list of the
  * association, we do not send the chunk for that association.  But it will not
  * affect other associations.
  *
  * Only sctp_setsockopt_bindx() is supposed to call this function.
  */
-static int sctp_send_asconf_add_ip(struct sock         *sk, 
+static int sctp_send_asconf_add_ip(struct sock         *sk,
                                   struct sockaddr      *addrs,
                                   int                  addrcnt)
 {
@@ -521,8 +540,8 @@ static int sctp_send_asconf_add_ip(struct sock              *sk,
                        continue;
 
                /* Check if any address in the packed array of addresses is
-                * in the bind address list of the association. If so, 
-                * do not send the asconf chunk to its peer, but continue with 
+                * in the bind address list of the association. If so,
+                * do not send the asconf chunk to its peer, but continue with
                 * other associations.
                 */
                addr_buf = addrs;
@@ -542,15 +561,12 @@ static int sctp_send_asconf_add_ip(struct sock            *sk,
                if (i < addrcnt)
                        continue;
 
-               /* Use the first address in bind addr list of association as
-                * Address Parameter of ASCONF CHUNK.
+               /* Use the first valid address in bind addr list of
+                * association as Address Parameter of ASCONF CHUNK.
                 */
-               sctp_read_lock(&asoc->base.addr_lock);
                bp = &asoc->base.bind_addr;
                p = bp->address_list.next;
                laddr = list_entry(p, struct sctp_sockaddr_entry, list);
-               sctp_read_unlock(&asoc->base.addr_lock);
-
                chunk = sctp_make_asconf_update_ip(asoc, &laddr->a, addrs,
                                                   addrcnt, SCTP_PARAM_ADD_IP);
                if (!chunk) {
@@ -565,20 +581,15 @@ static int sctp_send_asconf_add_ip(struct sock            *sk,
                /* Add the new addresses to the bind address list with
                 * use_as_src set to 0.
                 */
-               sctp_local_bh_disable();
-               sctp_write_lock(&asoc->base.addr_lock);
                addr_buf = addrs;
                for (i = 0; i < addrcnt; i++) {
                        addr = (union sctp_addr *)addr_buf;
                        af = sctp_get_af_specific(addr->v4.sin_family);
                        memcpy(&saveaddr, addr, af->sockaddr_len);
-                       saveaddr.v4.sin_port = ntohs(saveaddr.v4.sin_port);
-                       retval = sctp_add_bind_addr(bp, &saveaddr, 0,
-                                                   GFP_ATOMIC);
+                       retval = sctp_add_bind_addr(bp, &saveaddr,
+                                                   SCTP_ADDR_NEW, GFP_ATOMIC);
                        addr_buf += af->sockaddr_len;
                }
-               sctp_write_unlock(&asoc->base.addr_lock);
-               sctp_local_bh_enable();
        }
 
 out:
@@ -600,16 +611,15 @@ out:
  *
  * Only sctp_setsockopt_bindx() is supposed to call this function.
  */
-int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt)
+static int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt)
 {
        struct sctp_sock *sp = sctp_sk(sk);
        struct sctp_endpoint *ep = sp->ep;
        int cnt;
        struct sctp_bind_addr *bp = &ep->base.bind_addr;
        int retval = 0;
-       union sctp_addr saveaddr;
        void *addr_buf;
-       struct sockaddr *sa_addr;
+       union sctp_addr *sa_addr;
        struct sctp_af *af;
 
        SCTP_DEBUG_PRINTK("sctp_bindx_rem (sk: %p, addrs: %p, addrcnt: %d)\n",
@@ -627,19 +637,19 @@ int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt)
                        goto err_bindx_rem;
                }
 
-               /* The list may contain either IPv4 or IPv6 address;
-                * determine the address length to copy the address to
-                * saveaddr. 
-                */
-               sa_addr = (struct sockaddr *)addr_buf;
-               af = sctp_get_af_specific(sa_addr->sa_family);
+               sa_addr = (union sctp_addr *)addr_buf;
+               af = sctp_get_af_specific(sa_addr->sa.sa_family);
                if (!af) {
                        retval = -EINVAL;
                        goto err_bindx_rem;
                }
-               memcpy(&saveaddr, sa_addr, af->sockaddr_len); 
-               saveaddr.v4.sin_port = ntohs(saveaddr.v4.sin_port);
-               if (saveaddr.v4.sin_port != bp->port) {
+
+               if (!af->addr_valid(sa_addr, sp, NULL)) {
+                       retval = -EADDRNOTAVAIL;
+                       goto err_bindx_rem;
+               }
+
+               if (sa_addr->v4.sin_port != htons(bp->port)) {
                        retval = -EINVAL;
                        goto err_bindx_rem;
                }
@@ -651,13 +661,7 @@ int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt)
                 * socket routing and failover schemes. Refer to comments in
                 * sctp_do_bind(). -daisy
                 */
-               sctp_local_bh_disable();
-               sctp_write_lock(&ep->base.addr_lock);
-
-               retval = sctp_del_bind_addr(bp, &saveaddr);
-
-               sctp_write_unlock(&ep->base.addr_lock);
-               sctp_local_bh_enable();
+               retval = sctp_del_bind_addr(bp, sa_addr);
 
                addr_buf += af->sockaddr_len;
 err_bindx_rem:
@@ -676,7 +680,7 @@ err_bindx_rem:
  * the associations that are part of the endpoint indicating that a list of
  * local addresses are removed from the endpoint.
  *
- * If any of the addresses is already in the bind address list of the 
+ * If any of the addresses is already in the bind address list of the
  * association, we do not send the chunk for that association.  But it will not
  * affect other associations.
  *
@@ -693,7 +697,6 @@ static int sctp_send_asconf_del_ip(struct sock              *sk,
        struct sctp_bind_addr   *bp;
        struct sctp_chunk       *chunk;
        union sctp_addr         *laddr;
-       union sctp_addr         saveaddr;
        void                    *addr_buf;
        struct sctp_af          *af;
        struct list_head        *pos, *pos1;
@@ -723,7 +726,7 @@ static int sctp_send_asconf_del_ip(struct sock              *sk,
                        continue;
 
                /* Check if any address in the packed array of addresses is
-                * not present in the bind address list of the association.
+                * not present in the bind address list of the association.
                 * If so, do not send the asconf chunk to its peer, but
                 * continue with other associations.
                 */
@@ -749,14 +752,16 @@ static int sctp_send_asconf_del_ip(struct sock            *sk,
                 * make sure that we do not delete all the addresses in the
                 * association.
                 */
-               sctp_read_lock(&asoc->base.addr_lock);
                bp = &asoc->base.bind_addr;
                laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs,
                                               addrcnt, sp);
-               sctp_read_unlock(&asoc->base.addr_lock);
                if (!laddr)
                        continue;
 
+               /* We do not need RCU protection throughout this loop
+                * because this is done under a socket lock from the
+                * setsockopt call.
+                */
                chunk = sctp_make_asconf_update_ip(asoc, laddr, addrs, addrcnt,
                                                   SCTP_PARAM_DEL_IP);
                if (!chunk) {
@@ -767,25 +772,16 @@ static int sctp_send_asconf_del_ip(struct sock            *sk,
                /* Reset use_as_src flag for the addresses in the bind address
                 * list that are to be deleted.
                 */
-               sctp_local_bh_disable();
-               sctp_write_lock(&asoc->base.addr_lock);
                addr_buf = addrs;
                for (i = 0; i < addrcnt; i++) {
                        laddr = (union sctp_addr *)addr_buf;
                        af = sctp_get_af_specific(laddr->v4.sin_family);
-                       memcpy(&saveaddr, laddr, af->sockaddr_len);
-                       saveaddr.v4.sin_port = ntohs(saveaddr.v4.sin_port);
-                       list_for_each(pos1, &bp->address_list) {
-                               saddr = list_entry(pos1,
-                                                  struct sctp_sockaddr_entry,
-                                                  list);
-                               if (sctp_cmp_addr_exact(&saddr->a, &saveaddr))
-                                       saddr->use_as_src = 0;
+                       list_for_each_entry(saddr, &bp->address_list, list) {
+                               if (sctp_cmp_addr_exact(&saddr->a, laddr))
+                                       saddr->state = SCTP_ADDR_DEL;
                        }
                        addr_buf += af->sockaddr_len;
                }
-               sctp_write_unlock(&asoc->base.addr_lock);
-               sctp_local_bh_enable();
 
                /* Update the route and saddr entries for all the transports
                 * as some of the addresses in the bind address list are
@@ -913,7 +909,7 @@ SCTP_STATIC int sctp_setsockopt_bindx(struct sock* sk,
                return -EFAULT;
        }
 
-       /* Walk through the addrs buffer and count the number of addresses. */ 
+       /* Walk through the addrs buffer and count the number of addresses. */
        addr_buf = kaddrs;
        while (walk_size < addrs_size) {
                sa_addr = (struct sockaddr *)addr_buf;
@@ -921,7 +917,7 @@ SCTP_STATIC int sctp_setsockopt_bindx(struct sock* sk,
 
                /* If the address family is not supported or if this address
                 * causes the address buffer to overflow return EINVAL.
-                */ 
+                */
                if (!af || (walk_size + af->sockaddr_len) > addrs_size) {
                        kfree(kaddrs);
                        return -EINVAL;
@@ -950,7 +946,7 @@ SCTP_STATIC int sctp_setsockopt_bindx(struct sock* sk,
        default:
                err = -EINVAL;
                break;
-        };
+       }
 
 out:
        kfree(kaddrs);
@@ -979,8 +975,10 @@ static int __sctp_connect(struct sock* sk,
        int err = 0;
        int addrcnt = 0;
        int walk_size = 0;
-       struct sockaddr *sa_addr;
+       union sctp_addr *sa_addr = NULL;
        void *addr_buf;
+       unsigned short port;
+       unsigned int f_flags = 0;
 
        sp = sctp_sk(sk);
        ep = sp->ep;
@@ -999,8 +997,9 @@ static int __sctp_connect(struct sock* sk,
        /* Walk through the addrs buffer and count the number of addresses. */
        addr_buf = kaddrs;
        while (walk_size < addrs_size) {
-               sa_addr = (struct sockaddr *)addr_buf;
-               af = sctp_get_af_specific(sa_addr->sa_family);
+               sa_addr = (union sctp_addr *)addr_buf;
+               af = sctp_get_af_specific(sa_addr->sa.sa_family);
+               port = ntohs(sa_addr->v4.sin_port);
 
                /* If the address family is not supported or if this address
                 * causes the address buffer to overflow return EINVAL.
@@ -1010,13 +1009,19 @@ static int __sctp_connect(struct sock* sk,
                        goto out_free;
                }
 
-               err = sctp_verify_addr(sk, (union sctp_addr *)sa_addr,
-                                      af->sockaddr_len);
+               /* Save current address so we can work with it */
+               memcpy(&to, sa_addr, af->sockaddr_len);
+
+               err = sctp_verify_addr(sk, &to, af->sockaddr_len);
                if (err)
                        goto out_free;
 
-               memcpy(&to, sa_addr, af->sockaddr_len);
-               to.v4.sin_port = ntohs(to.v4.sin_port);
+               /* Make sure the destination port is correctly set
+                * in all addresses.
+                */
+               if (asoc && asoc->peer.port && asoc->peer.port != port)
+                       goto out_free;
+
 
                /* Check if there already is a matching association on the
                 * endpoint (other than the one created here).
@@ -1052,10 +1057,10 @@ static int __sctp_connect(struct sock* sk,
                                }
                        } else {
                                /*
-                                * If an unprivileged user inherits a 1-many 
-                                * style socket with open associations on a 
-                                * privileged port, it MAY be permitted to 
-                                * accept new associations, but it SHOULD NOT 
+                                * If an unprivileged user inherits a 1-many
+                                * style socket with open associations on a
+                                * privileged port, it MAY be permitted to
+                                * accept new associations, but it SHOULD NOT
                                 * be permitted to open new associations.
                                 */
                                if (ep->base.bind_addr.port < PROT_SOCK &&
@@ -1098,11 +1103,18 @@ static int __sctp_connect(struct sock* sk,
 
        /* Initialize sk's dport and daddr for getpeername() */
        inet_sk(sk)->dport = htons(asoc->peer.port);
-       af = sctp_get_af_specific(to.sa.sa_family);
-       af->to_sk_daddr(&to, sk);
+       af = sctp_get_af_specific(sa_addr->sa.sa_family);
+       af->to_sk_daddr(sa_addr, sk);
        sk->sk_err = 0;
 
-       timeo = sock_sndtimeo(sk, sk->sk_socket->file->f_flags & O_NONBLOCK);
+       /* in-kernel sockets don't generally have a file allocated to them
+        * if all they do is call sock_create_kern().
+        */
+       if (sk->sk_socket->file)
+               f_flags = sk->sk_socket->file->f_flags;
+
+       timeo = sock_sndtimeo(sk, f_flags & O_NONBLOCK);
+
        err = sctp_wait_for_connect(asoc, &timeo);
 
        /* Don't free association on exit. */
@@ -1111,8 +1123,8 @@ static int __sctp_connect(struct sock* sk,
 out_free:
 
        SCTP_DEBUG_PRINTK("About to exit __sctp_connect() free asoc: %p"
-                         " kaddrs: %p err: %d\n",
-                         asoc, kaddrs, err);
+                         " kaddrs: %p err: %d\n",
+                         asoc, kaddrs, err);
        if (asoc)
                sctp_association_free(asoc);
        return err;
@@ -1427,11 +1439,6 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
                if (msg_namelen > sizeof(to))
                        msg_namelen = sizeof(to);
                memcpy(&to, msg->msg_name, msg_namelen);
-               SCTP_DEBUG_PRINTK("Just memcpy'd. msg_name is "
-                                 "0x%x:%u.\n",
-                                 to.v4.sin_addr.s_addr, to.v4.sin_port);
-
-               to.v4.sin_port = ntohs(to.v4.sin_port);
                msg_name = msg->msg_name;
        }
 
@@ -1457,7 +1464,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
         * length messages when SCTP_EOF|SCTP_ABORT is not set.
         * If SCTP_ABORT is set, the message length could be non zero with
         * the msg_iov set to the user abort reason.
-        */
+        */
        if (((sinfo_flags & SCTP_EOF) && (msg_len > 0)) ||
            (!(sinfo_flags & (SCTP_EOF|SCTP_ABORT)) && (msg_len == 0))) {
                err = -EINVAL;
@@ -1524,7 +1531,6 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
                        goto out_unlock;
                }
                if (sinfo_flags & SCTP_ABORT) {
-                       struct sctp_chunk *chunk;
 
                        chunk = sctp_make_abort_user(asoc, msg, msg_len);
                        if (!chunk) {
@@ -1621,7 +1627,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
                                        = sinit->sinit_max_attempts;
                        }
                        if (sinit->sinit_max_init_timeo) {
-                               asoc->max_init_timeo = 
+                               asoc->max_init_timeo =
                                 msecs_to_jiffies(sinit->sinit_max_init_timeo);
                        }
                }
@@ -1663,6 +1669,9 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
                goto out_free;
        }
 
+       if (asoc->pmtu_pending)
+               sctp_assoc_pending_pmtu(asoc);
+
        /* If fragmentation is disabled and the message length exceeds the
         * association fragmentation point, return EMSGSIZE.  The I-D
         * does not specify what this error is, but this looks like
@@ -1902,7 +1911,8 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk,
                 * rwnd by that amount. If all the data in the skb is read,
                 * rwnd is updated when the event is freed.
                 */
-               sctp_assoc_rwnd_increase(event->asoc, copied);
+               if (!sctp_ulpevent_is_notification(event))
+                       sctp_assoc_rwnd_increase(event->asoc, copied);
                goto out;
        } else if ((event->msg_flags & MSG_NOTIFICATION) ||
                   (event->msg_flags & MSG_EOR))
@@ -2055,6 +2065,10 @@ static int sctp_setsockopt_autoclose(struct sock *sk, char __user *optval,
  *                     SPP_HB_DEMAND - Request a user initiated heartbeat
  *                     to be made immediately.
  *
+ *                     SPP_HB_TIME_IS_ZERO - Specify's that the time for
+ *                     heartbeat delayis to be set to the value of 0
+ *                     milliseconds.
+ *
  *                     SPP_PMTUD_ENABLE - This field will enable PMTU
  *                     discovery upon the specified address. Note that
  *                     if the address feild is empty then all addresses
@@ -2097,13 +2111,30 @@ static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params,
                        return error;
        }
 
-       if (params->spp_hbinterval) {
-               if (trans) {
-                       trans->hbinterval = msecs_to_jiffies(params->spp_hbinterval);
-               } else if (asoc) {
-                       asoc->hbinterval = msecs_to_jiffies(params->spp_hbinterval);
-               } else {
-                       sp->hbinterval = params->spp_hbinterval;
+       /* Note that unless the spp_flag is set to SPP_HB_ENABLE the value of
+        * this field is ignored.  Note also that a value of zero indicates
+        * the current setting should be left unchanged.
+        */
+       if (params->spp_flags & SPP_HB_ENABLE) {
+
+               /* Re-zero the interval if the SPP_HB_TIME_IS_ZERO is
+                * set.  This lets us use 0 value when this flag
+                * is set.
+                */
+               if (params->spp_flags & SPP_HB_TIME_IS_ZERO)
+                       params->spp_hbinterval = 0;
+
+               if (params->spp_hbinterval ||
+                   (params->spp_flags & SPP_HB_TIME_IS_ZERO)) {
+                       if (trans) {
+                               trans->hbinterval =
+                                   msecs_to_jiffies(params->spp_hbinterval);
+                       } else if (asoc) {
+                               asoc->hbinterval =
+                                   msecs_to_jiffies(params->spp_hbinterval);
+                       } else {
+                               sp->hbinterval = params->spp_hbinterval;
+                       }
                }
        }
 
@@ -2120,7 +2151,12 @@ static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params,
                }
        }
 
-       if (params->spp_pathmtu) {
+       /* When Path MTU discovery is disabled the value specified here will
+        * be the "fixed" path mtu (i.e. the value of the spp_flags field must
+        * include the flag SPP_PMTUD_DISABLE for this field to have any
+        * effect).
+        */
+       if ((params->spp_flags & SPP_PMTUD_DISABLE) && params->spp_pathmtu) {
                if (trans) {
                        trans->pathmtu = params->spp_pathmtu;
                        sctp_assoc_sync_pmtu(asoc);
@@ -2151,7 +2187,11 @@ static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params,
                }
        }
 
-       if (params->spp_sackdelay) {
+       /* Note that unless the spp_flag is set to SPP_SACKDELAY_ENABLE the
+        * value of this field is ignored.  Note also that a value of zero
+        * indicates the current setting should be left unchanged.
+        */
+       if ((params->spp_flags & SPP_SACKDELAY_ENABLE) && params->spp_sackdelay) {
                if (trans) {
                        trans->sackdelay =
                                msecs_to_jiffies(params->spp_sackdelay);
@@ -2179,7 +2219,11 @@ static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params,
                }
        }
 
-       if (params->spp_pathmaxrxt) {
+       /* Note that unless the spp_flag is set to SPP_PMTUD_ENABLE the value
+        * of this field is ignored.  Note also that a value of zero
+        * indicates the current setting should be left unchanged.
+        */
+       if ((params->spp_flags & SPP_PMTUD_ENABLE) && params->spp_pathmaxrxt) {
                if (trans) {
                        trans->pathmaxrxt = params->spp_pathmaxrxt;
                } else if (asoc) {
@@ -2271,7 +2315,7 @@ static int sctp_setsockopt_peer_addr_params(struct sock *sk,
        return 0;
 }
 
-/* 7.1.24. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME)
+/* 7.1.23. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME)
  *
  *   This options will get or set the delayed ack timer.  The time is set
  *   in milliseconds.  If the assoc_id is 0, then this sets or gets the
@@ -2320,7 +2364,7 @@ static int sctp_setsockopt_delayed_ack_time(struct sock *sk,
        /* Get association, if assoc_id != 0 and the socket is a one
         * to many style socket, and an association was not found, then
         * the id was invalid.
-        */
+        */
        asoc = sctp_id2assoc(sk, params.assoc_id);
        if (!asoc && params.assoc_id && sctp_style(sk, UDP))
                return -EINVAL;
@@ -2329,22 +2373,22 @@ static int sctp_setsockopt_delayed_ack_time(struct sock *sk,
                if (asoc) {
                        asoc->sackdelay =
                                msecs_to_jiffies(params.assoc_value);
-                       asoc->param_flags = 
+                       asoc->param_flags =
                                (asoc->param_flags & ~SPP_SACKDELAY) |
                                SPP_SACKDELAY_ENABLE;
                } else {
                        sp->sackdelay = params.assoc_value;
-                       sp->param_flags = 
+                       sp->param_flags =
                                (sp->param_flags & ~SPP_SACKDELAY) |
                                SPP_SACKDELAY_ENABLE;
                }
        } else {
                if (asoc) {
-                       asoc->param_flags = 
+                       asoc->param_flags =
                                (asoc->param_flags & ~SPP_SACKDELAY) |
                                SPP_SACKDELAY_DISABLE;
                } else {
-                       sp->param_flags = 
+                       sp->param_flags =
                                (sp->param_flags & ~SPP_SACKDELAY) |
                                SPP_SACKDELAY_DISABLE;
                }
@@ -2360,17 +2404,17 @@ static int sctp_setsockopt_delayed_ack_time(struct sock *sk,
                        if (params.assoc_value) {
                                trans->sackdelay =
                                        msecs_to_jiffies(params.assoc_value);
-                               trans->param_flags = 
+                               trans->param_flags =
                                        (trans->param_flags & ~SPP_SACKDELAY) |
                                        SPP_SACKDELAY_ENABLE;
                        } else {
-                               trans->param_flags = 
+                               trans->param_flags =
                                        (trans->param_flags & ~SPP_SACKDELAY) |
                                        SPP_SACKDELAY_DISABLE;
                        }
                }
        }
+
        return 0;
 }
 
@@ -2396,13 +2440,13 @@ static int sctp_setsockopt_initmsg(struct sock *sk, char __user *optval, int opt
                return -EFAULT;
 
        if (sinit.sinit_num_ostreams)
-               sp->initmsg.sinit_num_ostreams = sinit.sinit_num_ostreams;      
+               sp->initmsg.sinit_num_ostreams = sinit.sinit_num_ostreams;
        if (sinit.sinit_max_instreams)
-               sp->initmsg.sinit_max_instreams = sinit.sinit_max_instreams;    
+               sp->initmsg.sinit_max_instreams = sinit.sinit_max_instreams;
        if (sinit.sinit_max_attempts)
-               sp->initmsg.sinit_max_attempts = sinit.sinit_max_attempts;      
+               sp->initmsg.sinit_max_attempts = sinit.sinit_max_attempts;
        if (sinit.sinit_max_init_timeo)
-               sp->initmsg.sinit_max_init_timeo = sinit.sinit_max_init_timeo;  
+               sp->initmsg.sinit_max_init_timeo = sinit.sinit_max_init_timeo;
 
        return 0;
 }
@@ -2533,7 +2577,7 @@ static int sctp_setsockopt_rtoinfo(struct sock *sk, char __user *optval, int opt
 
        if (asoc) {
                if (rtoinfo.srto_initial != 0)
-                       asoc->rto_initial = 
+                       asoc->rto_initial =
                                msecs_to_jiffies(rtoinfo.srto_initial);
                if (rtoinfo.srto_max != 0)
                        asoc->rto_max = msecs_to_jiffies(rtoinfo.srto_max);
@@ -2560,7 +2604,7 @@ static int sctp_setsockopt_rtoinfo(struct sock *sk, char __user *optval, int opt
  *
  * 7.1.2 SCTP_ASSOCINFO
  *
- * This option is used to tune the the maximum retransmission attempts
+ * This option is used to tune the maximum retransmission attempts
  * of the association.
  * Returns an error if the new association retransmission value is
  * greater than the sum of the retransmission value  of the peer.
@@ -2687,7 +2731,7 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, int optl
        /* Update the frag_point of the existing associations. */
        list_for_each(pos, &(sp->ep->asocs)) {
                asoc = list_entry(pos, struct sctp_association, asocs);
-               asoc->frag_point = sctp_frag_point(sp, asoc->pathmtu); 
+               asoc->frag_point = sctp_frag_point(sp, asoc->pathmtu);
        }
 
        return 0;
@@ -2725,7 +2769,7 @@ static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char __user *optva
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, prim.sspp_assoc_id);
-       if (!asoc) 
+       if (!asoc)
                return -EINVAL;
 
        if (!asoc->peer.asconf_capable)
@@ -2753,21 +2797,315 @@ static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char __user *optva
        return err;
 }
 
-static int sctp_setsockopt_adaption_layer(struct sock *sk, char __user *optval,
+static int sctp_setsockopt_adaptation_layer(struct sock *sk, char __user *optval,
                                          int optlen)
 {
-       struct sctp_setadaption adaption;
+       struct sctp_setadaptation adaptation;
+
+       if (optlen != sizeof(struct sctp_setadaptation))
+               return -EINVAL;
+       if (copy_from_user(&adaptation, optval, optlen))
+               return -EFAULT;
+
+       sctp_sk(sk)->adaptation_ind = adaptation.ssb_adaptation_ind;
+
+       return 0;
+}
+
+/*
+ * 7.1.29.  Set or Get the default context (SCTP_CONTEXT)
+ *
+ * The context field in the sctp_sndrcvinfo structure is normally only
+ * used when a failed message is retrieved holding the value that was
+ * sent down on the actual send call.  This option allows the setting of
+ * a default context on an association basis that will be received on
+ * reading messages from the peer.  This is especially helpful in the
+ * one-2-many model for an application to keep some reference to an
+ * internal state machine that is processing messages on the
+ * association.  Note that the setting of this value only effects
+ * received messages from the peer and does not effect the value that is
+ * saved with outbound messages.
+ */
+static int sctp_setsockopt_context(struct sock *sk, char __user *optval,
+                                  int optlen)
+{
+       struct sctp_assoc_value params;
+       struct sctp_sock *sp;
+       struct sctp_association *asoc;
+
+       if (optlen != sizeof(struct sctp_assoc_value))
+               return -EINVAL;
+       if (copy_from_user(&params, optval, optlen))
+               return -EFAULT;
+
+       sp = sctp_sk(sk);
+
+       if (params.assoc_id != 0) {
+               asoc = sctp_id2assoc(sk, params.assoc_id);
+               if (!asoc)
+                       return -EINVAL;
+               asoc->default_rcv_context = params.assoc_value;
+       } else {
+               sp->default_rcv_context = params.assoc_value;
+       }
+
+       return 0;
+}
+
+/*
+ * 7.1.24.  Get or set fragmented interleave (SCTP_FRAGMENT_INTERLEAVE)
+ *
+ * This options will at a minimum specify if the implementation is doing
+ * fragmented interleave.  Fragmented interleave, for a one to many
+ * socket, is when subsequent calls to receive a message may return
+ * parts of messages from different associations.  Some implementations
+ * may allow you to turn this value on or off.  If so, when turned off,
+ * no fragment interleave will occur (which will cause a head of line
+ * blocking amongst multiple associations sharing the same one to many
+ * socket).  When this option is turned on, then each receive call may
+ * come from a different association (thus the user must receive data
+ * with the extended calls (e.g. sctp_recvmsg) to keep track of which
+ * association each receive belongs to.
+ *
+ * This option takes a boolean value.  A non-zero value indicates that
+ * fragmented interleave is on.  A value of zero indicates that
+ * fragmented interleave is off.
+ *
+ * Note that it is important that an implementation that allows this
+ * option to be turned on, have it off by default.  Otherwise an unaware
+ * application using the one to many model may become confused and act
+ * incorrectly.
+ */
+static int sctp_setsockopt_fragment_interleave(struct sock *sk,
+                                              char __user *optval,
+                                              int optlen)
+{
+       int val;
+
+       if (optlen != sizeof(int))
+               return -EINVAL;
+       if (get_user(val, (int __user *)optval))
+               return -EFAULT;
+
+       sctp_sk(sk)->frag_interleave = (val == 0) ? 0 : 1;
+
+       return 0;
+}
+
+/*
+ * 7.1.25.  Set or Get the sctp partial delivery point
+ *       (SCTP_PARTIAL_DELIVERY_POINT)
+ * This option will set or get the SCTP partial delivery point.  This
+ * point is the size of a message where the partial delivery API will be
+ * invoked to help free up rwnd space for the peer.  Setting this to a
+ * lower value will cause partial delivery's to happen more often.  The
+ * calls argument is an integer that sets or gets the partial delivery
+ * point.
+ */
+static int sctp_setsockopt_partial_delivery_point(struct sock *sk,
+                                                 char __user *optval,
+                                                 int optlen)
+{
+       u32 val;
+
+       if (optlen != sizeof(u32))
+               return -EINVAL;
+       if (get_user(val, (int __user *)optval))
+               return -EFAULT;
+
+       sctp_sk(sk)->pd_point = val;
+
+       return 0; /* is this the right error code? */
+}
+
+/*
+ * 7.1.28.  Set or Get the maximum burst (SCTP_MAX_BURST)
+ *
+ * This option will allow a user to change the maximum burst of packets
+ * that can be emitted by this association.  Note that the default value
+ * is 4, and some implementations may restrict this setting so that it
+ * can only be lowered.
+ *
+ * NOTE: This text doesn't seem right.  Do this on a socket basis with
+ * future associations inheriting the socket value.
+ */
+static int sctp_setsockopt_maxburst(struct sock *sk,
+                                   char __user *optval,
+                                   int optlen)
+{
+       int val;
 
-       if (optlen != sizeof(struct sctp_setadaption))
+       if (optlen != sizeof(int))
                return -EINVAL;
-       if (copy_from_user(&adaption, optval, optlen)) 
+       if (get_user(val, (int __user *)optval))
                return -EFAULT;
 
-       sctp_sk(sk)->adaption_ind = adaption.ssb_adaption_ind;
+       if (val < 0)
+               return -EINVAL;
+
+       sctp_sk(sk)->max_burst = val;
 
        return 0;
 }
 
+/*
+ * 7.1.18.  Add a chunk that must be authenticated (SCTP_AUTH_CHUNK)
+ *
+ * This set option adds a chunk type that the user is requesting to be
+ * received only in an authenticated way.  Changes to the list of chunks
+ * will only effect future associations on the socket.
+ */
+static int sctp_setsockopt_auth_chunk(struct sock *sk,
+                                   char __user *optval,
+                                   int optlen)
+{
+       struct sctp_authchunk val;
+
+       if (optlen != sizeof(struct sctp_authchunk))
+               return -EINVAL;
+       if (copy_from_user(&val, optval, optlen))
+               return -EFAULT;
+
+       switch (val.sauth_chunk) {
+               case SCTP_CID_INIT:
+               case SCTP_CID_INIT_ACK:
+               case SCTP_CID_SHUTDOWN_COMPLETE:
+               case SCTP_CID_AUTH:
+                       return -EINVAL;
+       }
+
+       /* add this chunk id to the endpoint */
+       return sctp_auth_ep_add_chunkid(sctp_sk(sk)->ep, val.sauth_chunk);
+}
+
+/*
+ * 7.1.19.  Get or set the list of supported HMAC Identifiers (SCTP_HMAC_IDENT)
+ *
+ * This option gets or sets the list of HMAC algorithms that the local
+ * endpoint requires the peer to use.
+ */
+static int sctp_setsockopt_hmac_ident(struct sock *sk,
+                                   char __user *optval,
+                                   int optlen)
+{
+       struct sctp_hmacalgo *hmacs;
+       int err;
+
+       if (optlen < sizeof(struct sctp_hmacalgo))
+               return -EINVAL;
+
+       hmacs = kmalloc(optlen, GFP_KERNEL);
+       if (!hmacs)
+               return -ENOMEM;
+
+       if (copy_from_user(hmacs, optval, optlen)) {
+               err = -EFAULT;
+               goto out;
+       }
+
+       if (hmacs->shmac_num_idents == 0 ||
+           hmacs->shmac_num_idents > SCTP_AUTH_NUM_HMACS) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       err = sctp_auth_ep_set_hmacs(sctp_sk(sk)->ep, hmacs);
+out:
+       kfree(hmacs);
+       return err;
+}
+
+/*
+ * 7.1.20.  Set a shared key (SCTP_AUTH_KEY)
+ *
+ * This option will set a shared secret key which is used to build an
+ * association shared key.
+ */
+static int sctp_setsockopt_auth_key(struct sock *sk,
+                                   char __user *optval,
+                                   int optlen)
+{
+       struct sctp_authkey *authkey;
+       struct sctp_association *asoc;
+       int ret;
+
+       if (optlen <= sizeof(struct sctp_authkey))
+               return -EINVAL;
+
+       authkey = kmalloc(optlen, GFP_KERNEL);
+       if (!authkey)
+               return -ENOMEM;
+
+       if (copy_from_user(authkey, optval, optlen)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       asoc = sctp_id2assoc(sk, authkey->sca_assoc_id);
+       if (!asoc && authkey->sca_assoc_id && sctp_style(sk, UDP)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ret = sctp_auth_set_key(sctp_sk(sk)->ep, asoc, authkey);
+out:
+       kfree(authkey);
+       return ret;
+}
+
+/*
+ * 7.1.21.  Get or set the active shared key (SCTP_AUTH_ACTIVE_KEY)
+ *
+ * This option will get or set the active shared key to be used to build
+ * the association shared key.
+ */
+static int sctp_setsockopt_active_key(struct sock *sk,
+                                       char __user *optval,
+                                       int optlen)
+{
+       struct sctp_authkeyid val;
+       struct sctp_association *asoc;
+
+       if (optlen != sizeof(struct sctp_authkeyid))
+               return -EINVAL;
+       if (copy_from_user(&val, optval, optlen))
+               return -EFAULT;
+
+       asoc = sctp_id2assoc(sk, val.scact_assoc_id);
+       if (!asoc && val.scact_assoc_id && sctp_style(sk, UDP))
+               return -EINVAL;
+
+       return sctp_auth_set_active_key(sctp_sk(sk)->ep, asoc,
+                                       val.scact_keynumber);
+}
+
+/*
+ * 7.1.22.  Delete a shared key (SCTP_AUTH_DELETE_KEY)
+ *
+ * This set option will delete a shared secret key from use.
+ */
+static int sctp_setsockopt_del_key(struct sock *sk,
+                                       char __user *optval,
+                                       int optlen)
+{
+       struct sctp_authkeyid val;
+       struct sctp_association *asoc;
+
+       if (optlen != sizeof(struct sctp_authkeyid))
+               return -EINVAL;
+       if (copy_from_user(&val, optval, optlen))
+               return -EFAULT;
+
+       asoc = sctp_id2assoc(sk, val.scact_assoc_id);
+       if (!asoc && val.scact_assoc_id && sctp_style(sk, UDP))
+               return -EINVAL;
+
+       return sctp_auth_del_key_id(sctp_sk(sk)->ep, asoc,
+                                   val.scact_keynumber);
+
+}
+
+
 /* API 6.2 setsockopt(), getsockopt()
  *
  * Applications use setsockopt() and getsockopt() to set or retrieve
@@ -2847,6 +3185,9 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
        case SCTP_DELAYED_ACK_TIME:
                retval = sctp_setsockopt_delayed_ack_time(sk, optval, optlen);
                break;
+       case SCTP_PARTIAL_DELIVERY_POINT:
+               retval = sctp_setsockopt_partial_delivery_point(sk, optval, optlen);
+               break;
 
        case SCTP_INITMSG:
                retval = sctp_setsockopt_initmsg(sk, optval, optlen);
@@ -2876,14 +3217,37 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
        case SCTP_MAXSEG:
                retval = sctp_setsockopt_maxseg(sk, optval, optlen);
                break;
-       case SCTP_ADAPTION_LAYER:
-               retval = sctp_setsockopt_adaption_layer(sk, optval, optlen);
+       case SCTP_ADAPTATION_LAYER:
+               retval = sctp_setsockopt_adaptation_layer(sk, optval, optlen);
+               break;
+       case SCTP_CONTEXT:
+               retval = sctp_setsockopt_context(sk, optval, optlen);
+               break;
+       case SCTP_FRAGMENT_INTERLEAVE:
+               retval = sctp_setsockopt_fragment_interleave(sk, optval, optlen);
+               break;
+       case SCTP_MAX_BURST:
+               retval = sctp_setsockopt_maxburst(sk, optval, optlen);
+               break;
+       case SCTP_AUTH_CHUNK:
+               retval = sctp_setsockopt_auth_chunk(sk, optval, optlen);
+               break;
+       case SCTP_HMAC_IDENT:
+               retval = sctp_setsockopt_hmac_ident(sk, optval, optlen);
+               break;
+       case SCTP_AUTH_KEY:
+               retval = sctp_setsockopt_auth_key(sk, optval, optlen);
+               break;
+       case SCTP_AUTH_ACTIVE_KEY:
+               retval = sctp_setsockopt_active_key(sk, optval, optlen);
+               break;
+       case SCTP_AUTH_DELETE_KEY:
+               retval = sctp_setsockopt_del_key(sk, optval, optlen);
                break;
-
        default:
                retval = -ENOPROTOOPT;
                break;
-       };
+       }
 
        sctp_release_sock(sk);
 
@@ -2994,7 +3358,7 @@ SCTP_STATIC struct sock *sctp_accept(struct sock *sk, int flags, int *err)
 
 out:
        sctp_release_sock(sk);
-       *err = error;
+       *err = error;
        return newsk;
 }
 
@@ -3038,6 +3402,9 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
        sp->default_context = 0;
        sp->default_timetolive = 0;
 
+       sp->default_rcv_context = 0;
+       sp->max_burst = sctp_max_burst;
+
        /* Initialize default setup parameters. These parameters
         * can be modified with the SCTP_INITMSG socket option or
         * overridden by the SCTP_INIT CMSG.
@@ -3064,7 +3431,7 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
        sp->assocparams.sasoc_cookie_life = sctp_valid_cookie_life;
 
        /* Initialize default event subscriptions. By default, all the
-        * options are off. 
+        * options are off.
         */
        memset(&sp->subscribe, 0, sizeof(struct sctp_event_subscribe));
 
@@ -3076,8 +3443,8 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
        sp->pathmtu     = 0; // allow default discovery
        sp->sackdelay   = sctp_sack_timeout;
        sp->param_flags = SPP_HB_ENABLE |
-                         SPP_PMTUD_ENABLE |
-                         SPP_SACKDELAY_ENABLE;
+                         SPP_PMTUD_ENABLE |
+                         SPP_SACKDELAY_ENABLE;
 
        /* If enabled no SCTP message fragmentation will be performed.
         * Configure through SCTP_DISABLE_FRAGMENTS socket option.
@@ -3100,13 +3467,14 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
        /* User specified fragmentation limit. */
        sp->user_frag         = 0;
 
-       sp->adaption_ind = 0;
+       sp->adaptation_ind = 0;
 
        sp->pf = sctp_get_pf_specific(sk->sk_family);
 
        /* Control variables for partial data delivery. */
-       sp->pd_mode           = 0;
+       atomic_set(&sp->pd_mode, 0);
        skb_queue_head_init(&sp->pd_lobby);
+       sp->frag_interleave = 0;
 
        /* Create a per socket endpoint structure.  Even if we
         * change the data structure relationships, this may still
@@ -3120,6 +3488,7 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
        sp->hmac = NULL;
 
        SCTP_DBG_OBJCNT_INC(sock);
+       atomic_inc(&sctp_sockets_allocated);
        return 0;
 }
 
@@ -3133,7 +3502,7 @@ SCTP_STATIC int sctp_destroy_sock(struct sock *sk)
        /* Release our hold on the endpoint. */
        ep = sctp_sk(sk)->ep;
        sctp_endpoint_free(ep);
-
+       atomic_dec(&sctp_sockets_allocated);
        return 0;
 }
 
@@ -3188,12 +3557,13 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len,
        sctp_assoc_t associd;
        int retval = 0;
 
-       if (len != sizeof(status)) {
+       if (len < sizeof(status)) {
                retval = -EINVAL;
                goto out;
        }
 
-       if (copy_from_user(&status, optval, sizeof(status))) {
+       len = sizeof(status);
+       if (copy_from_user(&status, optval, len)) {
                retval = -EFAULT;
                goto out;
        }
@@ -3217,8 +3587,8 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len,
        status.sstat_outstrms = asoc->c.sinit_num_ostreams;
        status.sstat_fragmentation_point = asoc->frag_point;
        status.sstat_primary.spinfo_assoc_id = sctp_assoc2id(transport->asoc);
-       memcpy(&status.sstat_primary.spinfo_address,
-              &(transport->ipaddr), sizeof(union sctp_addr));
+       memcpy(&status.sstat_primary.spinfo_address, &transport->ipaddr,
+                       transport->af_specific->sockaddr_len);
        /* Map ipv4 address into v4-mapped-on-v6 address.  */
        sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
                (union sctp_addr *)&status.sstat_primary.spinfo_address);
@@ -3265,12 +3635,13 @@ static int sctp_getsockopt_peer_addr_info(struct sock *sk, int len,
        struct sctp_transport *transport;
        int retval = 0;
 
-       if (len != sizeof(pinfo)) {
+       if (len < sizeof(pinfo)) {
                retval = -EINVAL;
                goto out;
        }
 
-       if (copy_from_user(&pinfo, optval, sizeof(pinfo))) {
+       len = sizeof(pinfo);
+       if (copy_from_user(&pinfo, optval, len)) {
                retval = -EFAULT;
                goto out;
        }
@@ -3336,8 +3707,11 @@ 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)
 {
-       if (len != sizeof(struct sctp_event_subscribe))
+       if (len < sizeof(struct sctp_event_subscribe))
                return -EINVAL;
+       len = sizeof(struct sctp_event_subscribe);
+       if (put_user(len, optlen))
+               return -EFAULT;
        if (copy_to_user(optval, &sctp_sk(sk)->subscribe, len))
                return -EFAULT;
        return 0;
@@ -3359,9 +3733,12 @@ static int sctp_getsockopt_autoclose(struct sock *sk, int len, char __user *optv
        /* Applicable to UDP-style socket only */
        if (sctp_style(sk, TCP))
                return -EOPNOTSUPP;
-       if (len != sizeof(int))
+       if (len < sizeof(int))
                return -EINVAL;
-       if (copy_to_user(optval, &sctp_sk(sk)->autoclose, len))
+       len = sizeof(int);
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &sctp_sk(sk)->autoclose, sizeof(int)))
                return -EFAULT;
        return 0;
 }
@@ -3373,6 +3750,7 @@ SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc,
        struct sock *sk = asoc->base.sk;
        struct socket *sock;
        struct inet_sock *inetsk;
+       struct sctp_af *af;
        int err = 0;
 
        /* An association cannot be branched off from an already peeled-off
@@ -3394,8 +3772,9 @@ SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc,
        /* Make peeled-off sockets more like 1-1 accepted sockets.
         * Set the daddr and initialize id to something more random
         */
+       af = sctp_get_af_specific(asoc->peer.primary_addr.sa.sa_family);
+       af->to_sk_daddr(&asoc->peer.primary_addr, sk);
        inetsk = inet_sk(sock->sk);
-       inetsk->daddr = asoc->peer.primary_addr.v4.sin_addr.s_addr;
        inetsk->id = asoc->next_tsn ^ jiffies;
 
        *sockp = sock;
@@ -3410,8 +3789,9 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval
        int retval = 0;
        struct sctp_association *asoc;
 
-       if (len != sizeof(sctp_peeloff_arg_t))
+       if (len < sizeof(sctp_peeloff_arg_t))
                return -EINVAL;
+       len = sizeof(sctp_peeloff_arg_t);
        if (copy_from_user(&peeloff, optval, len))
                return -EFAULT;
 
@@ -3439,6 +3819,8 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval
 
        /* Return the fd mapped to the new socket.  */
        peeloff.sd = retval;
+       if (put_user(len, optlen))
+               return -EFAULT;
        if (copy_to_user(optval, &peeloff, len))
                retval = -EFAULT;
 
@@ -3547,9 +3929,9 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
        struct sctp_association *asoc = NULL;
        struct sctp_sock        *sp = sctp_sk(sk);
 
-       if (len != sizeof(struct sctp_paddrparams))
+       if (len < sizeof(struct sctp_paddrparams))
                return -EINVAL;
-
+       len = sizeof(struct sctp_paddrparams);
        if (copy_from_user(&params, optval, len))
                return -EFAULT;
 
@@ -3613,7 +3995,7 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
        return 0;
 }
 
-/* 7.1.24. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME)
+/* 7.1.23. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME)
  *
  *   This options will get or set the delayed ack timer.  The time is set
  *   in milliseconds.  If the assoc_id is 0, then this sets or gets the
@@ -3648,16 +4030,18 @@ static int sctp_getsockopt_delayed_ack_time(struct sock *sk, int len,
        struct sctp_association *asoc = NULL;
        struct sctp_sock        *sp = sctp_sk(sk);
 
-       if (len != sizeof(struct sctp_assoc_value))
+       if (len < sizeof(struct sctp_assoc_value))
                return - EINVAL;
 
+       len = sizeof(struct sctp_assoc_value);
+
        if (copy_from_user(&params, optval, len))
                return -EFAULT;
 
        /* Get association, if assoc_id != 0 and the socket is a one
         * to many style socket, and an association was not found, then
         * the id was invalid.
-        */
+        */
        asoc = sctp_id2assoc(sk, params.assoc_id);
        if (!asoc && params.assoc_id && sctp_style(sk, UDP))
                return -EINVAL;
@@ -3699,8 +4083,11 @@ static int sctp_getsockopt_delayed_ack_time(struct sock *sk, int len,
  */
 static int sctp_getsockopt_initmsg(struct sock *sk, int len, char __user *optval, int __user *optlen)
 {
-       if (len != sizeof(struct sctp_initmsg))
+       if (len < sizeof(struct sctp_initmsg))
                return -EINVAL;
+       len = sizeof(struct sctp_initmsg);
+       if (put_user(len, optlen))
+               return -EFAULT;
        if (copy_to_user(optval, &sctp_sk(sk)->initmsg, len))
                return -EFAULT;
        return 0;
@@ -3715,7 +4102,7 @@ static int sctp_getsockopt_peer_addrs_num_old(struct sock *sk, int len,
        struct list_head *pos;
        int cnt = 0;
 
-       if (len != sizeof(sctp_assoc_t))
+       if (len < sizeof(sctp_assoc_t))
                return -EINVAL;
 
        if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
@@ -3733,7 +4120,7 @@ static int sctp_getsockopt_peer_addrs_num_old(struct sock *sk, int len,
        return cnt;
 }
 
-/* 
+/*
  * Old API for getting list of peer addresses. Does not work for 32-bit
  * programs running on a 64-bit kernel
  */
@@ -3751,10 +4138,12 @@ static int sctp_getsockopt_peer_addrs_old(struct sock *sk, int len,
        struct sctp_sock *sp = sctp_sk(sk);
        int addrlen;
 
-       if (len != sizeof(struct sctp_getaddrs_old))
+       if (len < sizeof(struct sctp_getaddrs_old))
                return -EINVAL;
 
-       if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs_old)))
+       len = sizeof(struct sctp_getaddrs_old);
+
+       if (copy_from_user(&getaddrs, optval, len))
                return -EFAULT;
 
        if (getaddrs.addr_num <= 0) return -EINVAL;
@@ -3770,7 +4159,6 @@ static int sctp_getsockopt_peer_addrs_old(struct sock *sk, int len,
                memcpy(&temp, &from->ipaddr, sizeof(temp));
                sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
                addrlen = sctp_get_af_specific(sk->sk_family)->sockaddr_len;
-               temp.v4.sin_port = htons(temp.v4.sin_port);
                if (copy_to_user(to, &temp, addrlen))
                        return -EFAULT;
                to += addrlen ;
@@ -3778,7 +4166,9 @@ static int sctp_getsockopt_peer_addrs_old(struct sock *sk, int len,
                if (cnt >= getaddrs.addr_num) break;
        }
        getaddrs.addr_num = cnt;
-       if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old)))
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &getaddrs, len))
                return -EFAULT;
 
        return 0;
@@ -3811,17 +4201,15 @@ static int sctp_getsockopt_peer_addrs(struct sock *sk, int len,
                return -EINVAL;
 
        to = optval + offsetof(struct sctp_getaddrs,addrs);
-       space_left = len - sizeof(struct sctp_getaddrs) - 
-                       offsetof(struct sctp_getaddrs,addrs);
+       space_left = len - offsetof(struct sctp_getaddrs,addrs);
 
        list_for_each(pos, &asoc->peer.transport_addr_list) {
                from = list_entry(pos, struct sctp_transport, transports);
                memcpy(&temp, &from->ipaddr, sizeof(temp));
                sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
                addrlen = sctp_get_af_specific(sk->sk_family)->sockaddr_len;
-               if(space_left < addrlen)
+               if (space_left < addrlen)
                        return -ENOMEM;
-               temp.v4.sin_port = htons(temp.v4.sin_port);
                if (copy_to_user(to, &temp, addrlen))
                        return -EFAULT;
                to += addrlen;
@@ -3845,13 +4233,10 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len,
        sctp_assoc_t id;
        struct sctp_bind_addr *bp;
        struct sctp_association *asoc;
-       struct list_head *pos;
        struct sctp_sockaddr_entry *addr;
-       rwlock_t *addr_lock;
-       unsigned long flags;
        int cnt = 0;
 
-       if (len != sizeof(sctp_assoc_t))
+       if (len < sizeof(sctp_assoc_t))
                return -EINVAL;
 
        if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
@@ -3865,17 +4250,13 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len,
         */
        if (0 == id) {
                bp = &sctp_sk(sk)->ep->base.bind_addr;
-               addr_lock = &sctp_sk(sk)->ep->base.addr_lock;
        } else {
                asoc = sctp_id2assoc(sk, id);
                if (!asoc)
                        return -EINVAL;
                bp = &asoc->base.bind_addr;
-               addr_lock = &asoc->base.addr_lock;
        }
 
-       sctp_read_lock(addr_lock);
-
        /* If the endpoint is bound to 0.0.0.0 or ::0, count the valid
         * addresses from the global local address list.
         */
@@ -3883,104 +4264,110 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len,
                addr = list_entry(bp->address_list.next,
                                  struct sctp_sockaddr_entry, list);
                if (sctp_is_any(&addr->a)) {
-                       sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags);
-                       list_for_each(pos, &sctp_local_addr_list) {
-                               addr = list_entry(pos,
-                                                 struct sctp_sockaddr_entry,
-                                                 list);
-                               if ((PF_INET == sk->sk_family) && 
-                                   (AF_INET6 == addr->a.sa.sa_family)) 
+                       rcu_read_lock();
+                       list_for_each_entry_rcu(addr,
+                                               &sctp_local_addr_list, list) {
+                               if (!addr->valid)
+                                       continue;
+
+                               if ((PF_INET == sk->sk_family) &&
+                                   (AF_INET6 == addr->a.sa.sa_family))
                                        continue;
+
                                cnt++;
                        }
-                       sctp_spin_unlock_irqrestore(&sctp_local_addr_lock,
-                                                   flags);
+                       rcu_read_unlock();
                } else {
                        cnt = 1;
                }
                goto done;
        }
 
-       list_for_each(pos, &bp->address_list) {
+       /* Protection on the bound address list is not needed,
+        * since in the socket option context we hold the socket lock,
+        * so there is no way that the bound address list can change.
+        */
+       list_for_each_entry(addr, &bp->address_list, list) {
                cnt ++;
        }
-
 done:
-       sctp_read_unlock(addr_lock);
        return cnt;
 }
 
 /* Helper function that copies local addresses to user and returns the number
  * of addresses copied.
  */
-static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_addrs,
-                                       void __user *to)
+static int sctp_copy_laddrs_old(struct sock *sk, __u16 port,
+                                       int max_addrs, void *to,
+                                       int *bytes_copied)
 {
-       struct list_head *pos;
        struct sctp_sockaddr_entry *addr;
-       unsigned long flags;
        union sctp_addr temp;
        int cnt = 0;
        int addrlen;
 
-       sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags);
-       list_for_each(pos, &sctp_local_addr_list) {
-               addr = list_entry(pos, struct sctp_sockaddr_entry, list);
-               if ((PF_INET == sk->sk_family) && 
+       rcu_read_lock();
+       list_for_each_entry_rcu(addr, &sctp_local_addr_list, list) {
+               if (!addr->valid)
+                       continue;
+
+               if ((PF_INET == sk->sk_family) &&
                    (AF_INET6 == addr->a.sa.sa_family))
                        continue;
                memcpy(&temp, &addr->a, sizeof(temp));
+               if (!temp.v4.sin_port)
+                       temp.v4.sin_port = htons(port);
+
                sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
                                                                &temp);
                addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
-               temp.v4.sin_port = htons(port);
-               if (copy_to_user(to, &temp, addrlen)) {
-                       sctp_spin_unlock_irqrestore(&sctp_local_addr_lock,
-                                                   flags);
-                       return -EFAULT;
-               }
+               memcpy(to, &temp, addrlen);
+
                to += addrlen;
+               *bytes_copied += addrlen;
                cnt ++;
                if (cnt >= max_addrs) break;
        }
-       sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags);
+       rcu_read_unlock();
 
        return cnt;
 }
 
-static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port,
-                                   void __user **to, size_t space_left)
+static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to,
+                           size_t space_left, int *bytes_copied)
 {
-       struct list_head *pos;
        struct sctp_sockaddr_entry *addr;
-       unsigned long flags;
        union sctp_addr temp;
        int cnt = 0;
        int addrlen;
 
-       sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags);
-       list_for_each(pos, &sctp_local_addr_list) {
-               addr = list_entry(pos, struct sctp_sockaddr_entry, list);
-               if ((PF_INET == sk->sk_family) && 
+       rcu_read_lock();
+       list_for_each_entry_rcu(addr, &sctp_local_addr_list, list) {
+               if (!addr->valid)
+                       continue;
+
+               if ((PF_INET == sk->sk_family) &&
                    (AF_INET6 == addr->a.sa.sa_family))
                        continue;
                memcpy(&temp, &addr->a, sizeof(temp));
+               if (!temp.v4.sin_port)
+                       temp.v4.sin_port = htons(port);
+
                sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
                                                                &temp);
                addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
-               if(space_left<addrlen)
-                       return -ENOMEM;
-               temp.v4.sin_port = htons(port);
-               if (copy_to_user(*to, &temp, addrlen)) {
-                       sctp_spin_unlock_irqrestore(&sctp_local_addr_lock,
-                                                   flags);
-                       return -EFAULT;
+               if (space_left < addrlen) {
+                       cnt =  -ENOMEM;
+                       break;
                }
-               *to += addrlen;
+               memcpy(to, &temp, addrlen);
+
+               to += addrlen;
                cnt ++;
                space_left -= addrlen;
+               *bytes_copied += addrlen;
        }
-       sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags);
+       rcu_read_unlock();
 
        return cnt;
 }
@@ -3993,7 +4380,6 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
 {
        struct sctp_bind_addr *bp;
        struct sctp_association *asoc;
-       struct list_head *pos;
        int cnt = 0;
        struct sctp_getaddrs_old getaddrs;
        struct sctp_sockaddr_entry *addr;
@@ -4001,13 +4387,16 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
        union sctp_addr temp;
        struct sctp_sock *sp = sctp_sk(sk);
        int addrlen;
-       rwlock_t *addr_lock;
        int err = 0;
+       void *addrs;
+       void *buf;
+       int bytes_copied = 0;
 
-       if (len != sizeof(struct sctp_getaddrs_old))
+       if (len < sizeof(struct sctp_getaddrs_old))
                return -EINVAL;
 
-       if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs_old)))
+       len = sizeof(struct sctp_getaddrs_old);
+       if (copy_from_user(&getaddrs, optval, len))
                return -EFAULT;
 
        if (getaddrs.addr_num <= 0) return -EINVAL;
@@ -4019,18 +4408,23 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
         */
        if (0 == getaddrs.assoc_id) {
                bp = &sctp_sk(sk)->ep->base.bind_addr;
-               addr_lock = &sctp_sk(sk)->ep->base.addr_lock;
        } else {
                asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
                if (!asoc)
                        return -EINVAL;
                bp = &asoc->base.bind_addr;
-               addr_lock = &asoc->base.addr_lock;
        }
 
        to = getaddrs.addrs;
 
-       sctp_read_lock(addr_lock);
+       /* Allocate space for a local instance of packed array to hold all
+        * the data.  We store addresses here first and then put write them
+        * to the user in one shot.
+        */
+       addrs = kmalloc(sizeof(union sctp_addr) * getaddrs.addr_num,
+                       GFP_KERNEL);
+       if (!addrs)
+               return -ENOMEM;
 
        /* If the endpoint is bound to 0.0.0.0 or ::0, get the valid
         * addresses from the global local address list.
@@ -4039,39 +4433,43 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
                addr = list_entry(bp->address_list.next,
                                  struct sctp_sockaddr_entry, list);
                if (sctp_is_any(&addr->a)) {
-                       cnt = sctp_copy_laddrs_to_user_old(sk, bp->port,
-                                                          getaddrs.addr_num,
-                                                          to);
-                       if (cnt < 0) {
-                               err = cnt;
-                               goto unlock;
-                       }
-                       goto copy_getaddrs;             
+                       cnt = sctp_copy_laddrs_old(sk, bp->port,
+                                                  getaddrs.addr_num,
+                                                  addrs, &bytes_copied);
+                       goto copy_getaddrs;
                }
        }
 
-       list_for_each(pos, &bp->address_list) {
-               addr = list_entry(pos, struct sctp_sockaddr_entry, list);
+       buf = addrs;
+       /* Protection on the bound address list is not needed since
+        * in the socket option context we hold a socket lock and
+        * thus the bound address list can't change.
+        */
+       list_for_each_entry(addr, &bp->address_list, list) {
                memcpy(&temp, &addr->a, sizeof(temp));
                sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
                addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
-               temp.v4.sin_port = htons(temp.v4.sin_port);
-               if (copy_to_user(to, &temp, addrlen)) {
-                       err = -EFAULT;
-                       goto unlock;
-               }
-               to += addrlen;
+               memcpy(buf, &temp, addrlen);
+               buf += addrlen;
+               bytes_copied += addrlen;
                cnt ++;
                if (cnt >= getaddrs.addr_num) break;
        }
 
 copy_getaddrs:
+       /* copy the entire address list into the user provided space */
+       if (copy_to_user(to, addrs, bytes_copied)) {
+               err = -EFAULT;
+               goto error;
+       }
+
+       /* copy the leading structure back to user */
        getaddrs.addr_num = cnt;
-       if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old)))
+       if (copy_to_user(optval, &getaddrs, len))
                err = -EFAULT;
 
-unlock:
-       sctp_read_unlock(addr_lock);
+error:
+       kfree(addrs);
        return err;
 }
 
@@ -4080,7 +4478,6 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
 {
        struct sctp_bind_addr *bp;
        struct sctp_association *asoc;
-       struct list_head *pos;
        int cnt = 0;
        struct sctp_getaddrs getaddrs;
        struct sctp_sockaddr_entry *addr;
@@ -4088,12 +4485,13 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
        union sctp_addr temp;
        struct sctp_sock *sp = sctp_sk(sk);
        int addrlen;
-       rwlock_t *addr_lock;
        int err = 0;
        size_t space_left;
-       int bytes_copied;
+       int bytes_copied = 0;
+       void *addrs;
+       void *buf;
 
-       if (len <= sizeof(struct sctp_getaddrs))
+       if (len < sizeof(struct sctp_getaddrs))
                return -EINVAL;
 
        if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
@@ -4107,20 +4505,19 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
         */
        if (0 == getaddrs.assoc_id) {
                bp = &sctp_sk(sk)->ep->base.bind_addr;
-               addr_lock = &sctp_sk(sk)->ep->base.addr_lock;
        } else {
                asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
                if (!asoc)
                        return -EINVAL;
                bp = &asoc->base.bind_addr;
-               addr_lock = &asoc->base.addr_lock;
        }
 
        to = optval + offsetof(struct sctp_getaddrs,addrs);
-       space_left = len - sizeof(struct sctp_getaddrs) -
-                        offsetof(struct sctp_getaddrs,addrs);
+       space_left = len - offsetof(struct sctp_getaddrs,addrs);
 
-       sctp_read_lock(addr_lock);
+       addrs = kmalloc(space_left, GFP_KERNEL);
+       if (!addrs)
+               return -ENOMEM;
 
        /* If the endpoint is bound to 0.0.0.0 or ::0, get the valid
         * addresses from the global local address list.
@@ -4129,42 +4526,49 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
                addr = list_entry(bp->address_list.next,
                                  struct sctp_sockaddr_entry, list);
                if (sctp_is_any(&addr->a)) {
-                       cnt = sctp_copy_laddrs_to_user(sk, bp->port,
-                                                      &to, space_left);
+                       cnt = sctp_copy_laddrs(sk, bp->port, addrs,
+                                               space_left, &bytes_copied);
                        if (cnt < 0) {
                                err = cnt;
-                               goto unlock;
+                               goto out;
                        }
-                       goto copy_getaddrs;             
+                       goto copy_getaddrs;
                }
        }
 
-       list_for_each(pos, &bp->address_list) {
-               addr = list_entry(pos, struct sctp_sockaddr_entry, list);
+       buf = addrs;
+       /* Protection on the bound address list is not needed since
+        * in the socket option context we hold a socket lock and
+        * thus the bound address list can't change.
+        */
+       list_for_each_entry(addr, &bp->address_list, list) {
                memcpy(&temp, &addr->a, sizeof(temp));
                sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
                addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
-               if(space_left < addrlen)
-                       return -ENOMEM; /*fixme: right error?*/
-               temp.v4.sin_port = htons(temp.v4.sin_port);
-               if (copy_to_user(to, &temp, addrlen)) {
-                       err = -EFAULT;
-                       goto unlock;
+               if (space_left < addrlen) {
+                       err =  -ENOMEM; /*fixme: right error?*/
+                       goto out;
                }
-               to += addrlen;
+               memcpy(buf, &temp, addrlen);
+               buf += addrlen;
+               bytes_copied += addrlen;
                cnt ++;
                space_left -= addrlen;
        }
 
 copy_getaddrs:
-       if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num))
-               return -EFAULT;
-       bytes_copied = ((char __user *)to) - optval;
+       if (copy_to_user(to, addrs, bytes_copied)) {
+               err = -EFAULT;
+               goto out;
+       }
+       if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num)) {
+               err = -EFAULT;
+               goto out;
+       }
        if (put_user(bytes_copied, optlen))
-               return -EFAULT;
-
-unlock:
-       sctp_read_unlock(addr_lock);
+               err = -EFAULT;
+out:
+       kfree(addrs);
        return err;
 }
 
@@ -4181,10 +4585,12 @@ static int sctp_getsockopt_primary_addr(struct sock *sk, int len,
        struct sctp_association *asoc;
        struct sctp_sock *sp = sctp_sk(sk);
 
-       if (len != sizeof(struct sctp_prim))
+       if (len < sizeof(struct sctp_prim))
                return -EINVAL;
 
-       if (copy_from_user(&prim, optval, sizeof(struct sctp_prim)))
+       len = sizeof(struct sctp_prim);
+
+       if (copy_from_user(&prim, optval, len))
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, prim.ssp_assoc_id);
@@ -4193,39 +4599,42 @@ static int sctp_getsockopt_primary_addr(struct sock *sk, int len,
 
        if (!asoc->peer.primary_path)
                return -ENOTCONN;
-       
-       asoc->peer.primary_path->ipaddr.v4.sin_port =
-               htons(asoc->peer.primary_path->ipaddr.v4.sin_port);
+
        memcpy(&prim.ssp_addr, &asoc->peer.primary_path->ipaddr,
-              sizeof(union sctp_addr));
-       asoc->peer.primary_path->ipaddr.v4.sin_port =
-               ntohs(asoc->peer.primary_path->ipaddr.v4.sin_port);
+               asoc->peer.primary_path->af_specific->sockaddr_len);
 
        sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp,
                        (union sctp_addr *)&prim.ssp_addr);
 
-       if (copy_to_user(optval, &prim, sizeof(struct sctp_prim)))
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &prim, len))
                return -EFAULT;
 
        return 0;
 }
 
 /*
- * 7.1.11  Set Adaption Layer Indicator (SCTP_ADAPTION_LAYER)
+ * 7.1.11  Set Adaptation Layer Indicator (SCTP_ADAPTATION_LAYER)
  *
- * Requests that the local endpoint set the specified Adaption Layer
+ * Requests that the local endpoint set the specified Adaptation Layer
  * Indication parameter for all future INIT and INIT-ACK exchanges.
  */
-static int sctp_getsockopt_adaption_layer(struct sock *sk, int len,
+static int sctp_getsockopt_adaptation_layer(struct sock *sk, int len,
                                  char __user *optval, int __user *optlen)
 {
-       struct sctp_setadaption adaption;
+       struct sctp_setadaptation adaptation;
 
-       if (len != sizeof(struct sctp_setadaption))
+       if (len < sizeof(struct sctp_setadaptation))
                return -EINVAL;
 
-       adaption.ssb_adaption_ind = sctp_sk(sk)->adaption_ind;
-       if (copy_to_user(optval, &adaption, len))
+       len = sizeof(struct sctp_setadaptation);
+
+       adaptation.ssb_adaptation_ind = sctp_sk(sk)->adaptation_ind;
+
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &adaptation, len))
                return -EFAULT;
 
        return 0;
@@ -4258,9 +4667,12 @@ static int sctp_getsockopt_default_send_param(struct sock *sk,
        struct sctp_association *asoc;
        struct sctp_sock *sp = sctp_sk(sk);
 
-       if (len != sizeof(struct sctp_sndrcvinfo))
+       if (len < sizeof(struct sctp_sndrcvinfo))
                return -EINVAL;
-       if (copy_from_user(&info, optval, sizeof(struct sctp_sndrcvinfo)))
+
+       len = sizeof(struct sctp_sndrcvinfo);
+
+       if (copy_from_user(&info, optval, len))
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, info.sinfo_assoc_id);
@@ -4281,7 +4693,9 @@ static int sctp_getsockopt_default_send_param(struct sock *sk,
                info.sinfo_timetolive = sp->default_timetolive;
        }
 
-       if (copy_to_user(optval, &info, sizeof(struct sctp_sndrcvinfo)))
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &info, len))
                return -EFAULT;
 
        return 0;
@@ -4332,10 +4746,12 @@ static int sctp_getsockopt_rtoinfo(struct sock *sk, int len,
        struct sctp_rtoinfo rtoinfo;
        struct sctp_association *asoc;
 
-       if (len != sizeof (struct sctp_rtoinfo))
+       if (len < sizeof (struct sctp_rtoinfo))
                return -EINVAL;
 
-       if (copy_from_user(&rtoinfo, optval, sizeof (struct sctp_rtoinfo)))
+       len = sizeof(struct sctp_rtoinfo);
+
+       if (copy_from_user(&rtoinfo, optval, len))
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, rtoinfo.srto_assoc_id);
@@ -4370,7 +4786,7 @@ static int sctp_getsockopt_rtoinfo(struct sock *sk, int len,
  *
  * 7.1.2 SCTP_ASSOCINFO
  *
- * This option is used to tune the the maximum retransmission attempts
+ * This option is used to tune the maximum retransmission attempts
  * of the association.
  * Returns an error if the new association retransmission value is
  * greater than the sum of the retransmission value  of the peer.
@@ -4387,11 +4803,12 @@ static int sctp_getsockopt_associnfo(struct sock *sk, int len,
        struct list_head *pos;
        int cnt = 0;
 
-       if (len != sizeof (struct sctp_assocparams))
+       if (len < sizeof (struct sctp_assocparams))
                return -EINVAL;
 
-       if (copy_from_user(&assocparams, optval,
-                       sizeof (struct sctp_assocparams)))
+       len = sizeof(struct sctp_assocparams);
+
+       if (copy_from_user(&assocparams, optval, len))
                return -EFAULT;
 
        asoc = sctp_id2assoc(sk, assocparams.sasoc_assoc_id);
@@ -4467,6 +4884,44 @@ static int sctp_getsockopt_mappedv4(struct sock *sk, int len,
 }
 
 /*
+ * 7.1.29.  Set or Get the default context (SCTP_CONTEXT)
+ * (chapter and verse is quoted at sctp_setsockopt_context())
+ */
+static int sctp_getsockopt_context(struct sock *sk, int len,
+                                  char __user *optval, int __user *optlen)
+{
+       struct sctp_assoc_value params;
+       struct sctp_sock *sp;
+       struct sctp_association *asoc;
+
+       if (len < sizeof(struct sctp_assoc_value))
+               return -EINVAL;
+
+       len = sizeof(struct sctp_assoc_value);
+
+       if (copy_from_user(&params, optval, len))
+               return -EFAULT;
+
+       sp = sctp_sk(sk);
+
+       if (params.assoc_id != 0) {
+               asoc = sctp_id2assoc(sk, params.assoc_id);
+               if (!asoc)
+                       return -EINVAL;
+               params.assoc_value = asoc->default_rcv_context;
+       } else {
+               params.assoc_value = sp->default_rcv_context;
+       }
+
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &params, len))
+               return -EFAULT;
+
+       return 0;
+}
+
+/*
  * 7.1.17 Set the maximum fragrmentation size (SCTP_MAXSEG)
  *
  * This socket option specifies the maximum size to put in any outgoing
@@ -4495,6 +4950,191 @@ static int sctp_getsockopt_maxseg(struct sock *sk, int len,
        return 0;
 }
 
+/*
+ * 7.1.24.  Get or set fragmented interleave (SCTP_FRAGMENT_INTERLEAVE)
+ * (chapter and verse is quoted at sctp_setsockopt_fragment_interleave())
+ */
+static int sctp_getsockopt_fragment_interleave(struct sock *sk, int len,
+                                              char __user *optval, int __user *optlen)
+{
+       int val;
+
+       if (len < sizeof(int))
+               return -EINVAL;
+
+       len = sizeof(int);
+
+       val = sctp_sk(sk)->frag_interleave;
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &val, len))
+               return -EFAULT;
+
+       return 0;
+}
+
+/*
+ * 7.1.25.  Set or Get the sctp partial delivery point
+ * (chapter and verse is quoted at sctp_setsockopt_partial_delivery_point())
+ */
+static int sctp_getsockopt_partial_delivery_point(struct sock *sk, int len,
+                                                 char __user *optval,
+                                                 int __user *optlen)
+{
+       u32 val;
+
+       if (len < sizeof(u32))
+               return -EINVAL;
+
+       len = sizeof(u32);
+
+       val = sctp_sk(sk)->pd_point;
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &val, len))
+               return -EFAULT;
+
+       return -ENOTSUPP;
+}
+
+/*
+ * 7.1.28.  Set or Get the maximum burst (SCTP_MAX_BURST)
+ * (chapter and verse is quoted at sctp_setsockopt_maxburst())
+ */
+static int sctp_getsockopt_maxburst(struct sock *sk, int len,
+                                   char __user *optval,
+                                   int __user *optlen)
+{
+       int val;
+
+       if (len < sizeof(int))
+               return -EINVAL;
+
+       len = sizeof(int);
+
+       val = sctp_sk(sk)->max_burst;
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, &val, len))
+               return -EFAULT;
+
+       return -ENOTSUPP;
+}
+
+static int sctp_getsockopt_hmac_ident(struct sock *sk, int len,
+                                   char __user *optval, int __user *optlen)
+{
+       struct sctp_hmac_algo_param *hmacs;
+       __u16 param_len;
+
+       hmacs = sctp_sk(sk)->ep->auth_hmacs_list;
+       param_len = ntohs(hmacs->param_hdr.length);
+
+       if (len < param_len)
+               return -EINVAL;
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(optval, hmacs->hmac_ids, len))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int sctp_getsockopt_active_key(struct sock *sk, int len,
+                                   char __user *optval, int __user *optlen)
+{
+       struct sctp_authkeyid val;
+       struct sctp_association *asoc;
+
+       if (len < sizeof(struct sctp_authkeyid))
+               return -EINVAL;
+       if (copy_from_user(&val, optval, sizeof(struct sctp_authkeyid)))
+               return -EFAULT;
+
+       asoc = sctp_id2assoc(sk, val.scact_assoc_id);
+       if (!asoc && val.scact_assoc_id && sctp_style(sk, UDP))
+               return -EINVAL;
+
+       if (asoc)
+               val.scact_keynumber = asoc->active_key_id;
+       else
+               val.scact_keynumber = sctp_sk(sk)->ep->active_key_id;
+
+       return 0;
+}
+
+static int sctp_getsockopt_peer_auth_chunks(struct sock *sk, int len,
+                                   char __user *optval, int __user *optlen)
+{
+       struct sctp_authchunks __user *p = (void __user *)optval;
+       struct sctp_authchunks val;
+       struct sctp_association *asoc;
+       struct sctp_chunks_param *ch;
+       char __user *to;
+
+       if (len <= sizeof(struct sctp_authchunks))
+               return -EINVAL;
+
+       if (copy_from_user(&val, p, sizeof(struct sctp_authchunks)))
+               return -EFAULT;
+
+       to = p->gauth_chunks;
+       asoc = sctp_id2assoc(sk, val.gauth_assoc_id);
+       if (!asoc)
+               return -EINVAL;
+
+       ch = asoc->peer.peer_chunks;
+
+       /* See if the user provided enough room for all the data */
+       if (len < ntohs(ch->param_hdr.length))
+               return -EINVAL;
+
+       len = ntohs(ch->param_hdr.length);
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(to, ch->chunks, len))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int sctp_getsockopt_local_auth_chunks(struct sock *sk, int len,
+                                   char __user *optval, int __user *optlen)
+{
+       struct sctp_authchunks __user *p = (void __user *)optval;
+       struct sctp_authchunks val;
+       struct sctp_association *asoc;
+       struct sctp_chunks_param *ch;
+       char __user *to;
+
+       if (len <= sizeof(struct sctp_authchunks))
+               return -EINVAL;
+
+       if (copy_from_user(&val, p, sizeof(struct sctp_authchunks)))
+               return -EFAULT;
+
+       to = p->gauth_chunks;
+       asoc = sctp_id2assoc(sk, val.gauth_assoc_id);
+       if (!asoc && val.gauth_assoc_id && sctp_style(sk, UDP))
+               return -EINVAL;
+
+       if (asoc)
+               ch = (struct sctp_chunks_param*)asoc->c.auth_chunks;
+       else
+               ch = sctp_sk(sk)->ep->auth_chunk_list;
+
+       if (len < ntohs(ch->param_hdr.length))
+               return -EINVAL;
+
+       len = ntohs(ch->param_hdr.length);
+       if (put_user(len, optlen))
+               return -EFAULT;
+       if (copy_to_user(to, ch->chunks, len))
+               return -EFAULT;
+
+       return 0;
+}
+
 SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
                                char __user *optval, int __user *optlen)
 {
@@ -4600,14 +5240,47 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
                retval = sctp_getsockopt_peer_addr_info(sk, len, optval,
                                                        optlen);
                break;
-       case SCTP_ADAPTION_LAYER:
-               retval = sctp_getsockopt_adaption_layer(sk, len, optval,
+       case SCTP_ADAPTATION_LAYER:
+               retval = sctp_getsockopt_adaptation_layer(sk, len, optval,
+                                                       optlen);
+               break;
+       case SCTP_CONTEXT:
+               retval = sctp_getsockopt_context(sk, len, optval, optlen);
+               break;
+       case SCTP_FRAGMENT_INTERLEAVE:
+               retval = sctp_getsockopt_fragment_interleave(sk, len, optval,
+                                                            optlen);
+               break;
+       case SCTP_PARTIAL_DELIVERY_POINT:
+               retval = sctp_getsockopt_partial_delivery_point(sk, len, optval,
+                                                               optlen);
+               break;
+       case SCTP_MAX_BURST:
+               retval = sctp_getsockopt_maxburst(sk, len, optval, optlen);
+               break;
+       case SCTP_AUTH_KEY:
+       case SCTP_AUTH_CHUNK:
+       case SCTP_AUTH_DELETE_KEY:
+               retval = -EOPNOTSUPP;
+               break;
+       case SCTP_HMAC_IDENT:
+               retval = sctp_getsockopt_hmac_ident(sk, len, optval, optlen);
+               break;
+       case SCTP_AUTH_ACTIVE_KEY:
+               retval = sctp_getsockopt_active_key(sk, len, optval, optlen);
+               break;
+       case SCTP_PEER_AUTH_CHUNKS:
+               retval = sctp_getsockopt_peer_auth_chunks(sk, len, optval,
+                                                       optlen);
+               break;
+       case SCTP_LOCAL_AUTH_CHUNKS:
+               retval = sctp_getsockopt_local_auth_chunks(sk, len, optval,
                                                        optlen);
                break;
        default:
                retval = -ENOPROTOOPT;
                break;
-       };
+       }
 
        sctp_release_sock(sk);
        return retval;
@@ -4642,33 +5315,24 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
 {
        struct sctp_bind_hashbucket *head; /* hash list */
        struct sctp_bind_bucket *pp; /* hash list port iterator */
+       struct hlist_node *node;
        unsigned short snum;
        int ret;
 
-       /* NOTE:  Remember to put this back to net order. */
-       addr->v4.sin_port = ntohs(addr->v4.sin_port);
-       snum = addr->v4.sin_port;
+       snum = ntohs(addr->v4.sin_port);
 
        SCTP_DEBUG_PRINTK("sctp_get_port() begins, snum=%d\n", snum);
        sctp_local_bh_disable();
 
        if (snum == 0) {
-               /* Search for an available port.
-                *
-                * 'sctp_port_rover' was the last port assigned, so
-                * we start to search from 'sctp_port_rover +
-                * 1'. What we do is first check if port 'rover' is
-                * already in the hash table; if not, we use that; if
-                * it is, we try next.
-                */
-               int low = sysctl_local_port_range[0];
-               int high = sysctl_local_port_range[1];
-               int remaining = (high - low) + 1;
-               int rover;
-               int index;
-
-               sctp_spin_lock(&sctp_port_alloc_lock);
-               rover = sctp_port_rover;
+               /* Search for an available port. */
+               int low, high, remaining, index;
+               unsigned int rover;
+
+               inet_get_local_port_range(&low, &high);
+               remaining = (high - low) + 1;
+               rover = net_random() % remaining + low;
+
                do {
                        rover++;
                        if ((rover < low) || (rover > high))
@@ -4676,15 +5340,13 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
                        index = sctp_phashfn(rover);
                        head = &sctp_port_hashtable[index];
                        sctp_spin_lock(&head->lock);
-                       for (pp = head->chain; pp; pp = pp->next)
+                       sctp_for_each_hentry(pp, node, &head->chain)
                                if (pp->port == rover)
                                        goto next;
                        break;
                next:
                        sctp_spin_unlock(&head->lock);
                } while (--remaining > 0);
-               sctp_port_rover = rover;
-               sctp_spin_unlock(&sctp_port_alloc_lock);
 
                /* Exhausted local port range during search? */
                ret = 1;
@@ -4705,7 +5367,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
                 */
                head = &sctp_port_hashtable[sctp_phashfn(snum)];
                sctp_spin_lock(&head->lock);
-               for (pp = head->chain; pp; pp = pp->next) {
+               sctp_for_each_hentry(pp, node, &head->chain) {
                        if (pp->port == snum)
                                goto pp_found;
                }
@@ -4724,7 +5386,8 @@ pp_found:
                struct hlist_node *node;
 
                SCTP_DEBUG_PRINTK("sctp_get_port() found a possible match\n");
-               if (pp->fastreuse && sk->sk_reuse)
+               if (pp->fastreuse && sk->sk_reuse &&
+                       sk->sk_state != SCTP_SS_LISTENING)
                        goto success;
 
                /* Run through the list of sockets bound to the port
@@ -4741,7 +5404,8 @@ pp_found:
                        struct sctp_endpoint *ep2;
                        ep2 = sctp_sk(sk2)->ep;
 
-                       if (reuse && sk2->sk_reuse)
+                       if (reuse && sk2->sk_reuse &&
+                           sk2->sk_state != SCTP_SS_LISTENING)
                                continue;
 
                        if (sctp_bind_addr_match(&ep2->base.bind_addr, addr,
@@ -4762,9 +5426,13 @@ pp_not_found:
         * if sk->sk_reuse is too (that is, if the caller requested
         * SO_REUSEADDR on this socket -sk-).
         */
-       if (hlist_empty(&pp->owner))
-               pp->fastreuse = sk->sk_reuse ? 1 : 0;
-       else if (pp->fastreuse && !sk->sk_reuse)
+       if (hlist_empty(&pp->owner)) {
+               if (sk->sk_reuse && sk->sk_state != SCTP_SS_LISTENING)
+                       pp->fastreuse = 1;
+               else
+                       pp->fastreuse = 0;
+       } else if (pp->fastreuse &&
+               (!sk->sk_reuse || sk->sk_state == SCTP_SS_LISTENING))
                pp->fastreuse = 0;
 
        /* We are set, so fill up all the data in the hash table
@@ -4772,8 +5440,8 @@ pp_not_found:
         * sockets FIXME: Blurry, NPI (ipg).
         */
 success:
-       inet_sk(sk)->num = snum;
        if (!sctp_sk(sk)->bind_hash) {
+               inet_sk(sk)->num = snum;
                sk_add_bind_node(sk, &pp->owner);
                sctp_sk(sk)->bind_hash = pp;
        }
@@ -4784,7 +5452,6 @@ fail_unlock:
 
 fail:
        sctp_local_bh_enable();
-       addr->v4.sin_port = htons(addr->v4.sin_port);
        return ret;
 }
 
@@ -4829,15 +5496,16 @@ SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog)
        if (!backlog) {
                if (sctp_sstate(sk, CLOSED))
                        return 0;
-               
+
                sctp_unhash_endpoint(ep);
                sk->sk_state = SCTP_SS_CLOSED;
+               return 0;
        }
 
        /* Return if we are already listening. */
        if (sctp_sstate(sk, LISTENING))
                return 0;
-               
+
        /*
         * If a bind() or sctp_bindx() is not called prior to a listen()
         * call that allows new associations to be accepted, the system
@@ -4847,12 +5515,16 @@ SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog)
         * This is not currently spelled out in the SCTP sockets
         * extensions draft, but follows the practice as seen in TCP
         * sockets.
+        *
+        * Additionally, turn off fastreuse flag since we are not listening
         */
+       sk->sk_state = SCTP_SS_LISTENING;
        if (!ep->base.bind_addr.port) {
                if (sctp_autobind(sk))
                        return -EAGAIN;
-       }
-       sk->sk_state = SCTP_SS_LISTENING;
+       } else
+               sctp_sk(sk)->bind_hash->fastreuse = 0;
+
        sctp_hash_endpoint(ep);
        return 0;
 }
@@ -4872,9 +5544,10 @@ SCTP_STATIC int sctp_stream_listen(struct sock *sk, int backlog)
        if (!backlog) {
                if (sctp_sstate(sk, CLOSED))
                        return 0;
-               
+
                sctp_unhash_endpoint(ep);
                sk->sk_state = SCTP_SS_CLOSED;
+               return 0;
        }
 
        if (sctp_sstate(sk, LISTENING))
@@ -4890,11 +5563,13 @@ SCTP_STATIC int sctp_stream_listen(struct sock *sk, int backlog)
         * extensions draft, but follows the practice as seen in TCP
         * sockets.
         */
+       sk->sk_state = SCTP_SS_LISTENING;
        if (!ep->base.bind_addr.port) {
                if (sctp_autobind(sk))
                        return -EAGAIN;
-       }
-       sk->sk_state = SCTP_SS_LISTENING;
+       } else
+               sctp_sk(sk)->bind_hash->fastreuse = 0;
+
        sk->sk_max_ack_backlog = backlog;
        sctp_hash_endpoint(ep);
        return 0;
@@ -4920,7 +5595,12 @@ int sctp_inet_listen(struct socket *sock, int backlog)
        /* Allocate HMAC for generating cookie. */
        if (sctp_hmac_alg) {
                tfm = crypto_alloc_hash(sctp_hmac_alg, 0, CRYPTO_ALG_ASYNC);
-               if (!tfm) {
+               if (IS_ERR(tfm)) {
+                       if (net_ratelimit()) {
+                               printk(KERN_INFO
+                                      "SCTP: failed to load transform for %s: %ld\n",
+                                       sctp_hmac_alg, PTR_ERR(tfm));
+                       }
                        err = -ENOSYS;
                        goto out;
                }
@@ -4935,7 +5615,8 @@ int sctp_inet_listen(struct socket *sock, int backlog)
                break;
        default:
                break;
-       };
+       }
+
        if (err)
                goto cleanup;
 
@@ -4975,7 +5656,7 @@ unsigned int sctp_poll(struct file *file, struct socket *sock, poll_table *wait)
         */
        if (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING))
                return (!list_empty(&sp->ep->asocs)) ?
-                       (POLLIN | POLLRDNORM) : 0;
+                       (POLLIN | POLLRDNORM) : 0;
 
        mask = 0;
 
@@ -5024,16 +5705,13 @@ static struct sctp_bind_bucket *sctp_bucket_create(
 {
        struct sctp_bind_bucket *pp;
 
-       pp = kmem_cache_alloc(sctp_bucket_cachep, SLAB_ATOMIC);
+       pp = kmem_cache_alloc(sctp_bucket_cachep, GFP_ATOMIC);
        SCTP_DBG_OBJCNT_INC(bind_bucket);
        if (pp) {
                pp->port = snum;
                pp->fastreuse = 0;
                INIT_HLIST_HEAD(&pp->owner);
-               if ((pp->next = head->chain) != NULL)
-                       pp->next->pprev = &pp->next;
-               head->chain = pp;
-               pp->pprev = &head->chain;
+               hlist_add_head(&pp->node, &head->chain);
        }
        return pp;
 }
@@ -5042,9 +5720,7 @@ static struct sctp_bind_bucket *sctp_bucket_create(
 static void sctp_bucket_destroy(struct sctp_bind_bucket *pp)
 {
        if (pp && hlist_empty(&pp->owner)) {
-               if (pp->next)
-                       pp->next->pprev = pp->pprev;
-               *(pp->pprev) = pp->next;
+               __hlist_del(&pp->node);
                kmem_cache_free(sctp_bucket_cachep, pp);
                SCTP_DBG_OBJCNT_DEC(bind_bucket);
        }
@@ -5083,7 +5759,7 @@ static int sctp_autobind(struct sock *sk)
 {
        union sctp_addr autoaddr;
        struct sctp_af *af;
-       unsigned short port;
+       __be16 port;
 
        /* Initialize a local sockaddr structure to INADDR_ANY. */
        af = sctp_sk(sk)->pf->af;
@@ -5198,7 +5874,7 @@ SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *msg,
 
                default:
                        return -EINVAL;
-               };
+               }
        }
        return 0;
 }
@@ -5340,7 +6016,8 @@ static void __sctp_write_space(struct sctp_association *asoc)
                         */
                        if (sock->fasync_list &&
                            !(sk->sk_shutdown & SEND_SHUTDOWN))
-                               sock_wake_async(sock, 2, POLL_OUT);
+                               sock_wake_async(sock,
+                                               SOCK_WAKE_SPACE, POLL_OUT);
                }
        }
 }
@@ -5365,6 +6042,12 @@ static void sctp_wfree(struct sk_buff *skb)
 
        atomic_sub(sizeof(struct sctp_chunk), &sk->sk_wmem_alloc);
 
+       /*
+        * This undoes what is done via sctp_set_owner_w and sk_mem_charge
+        */
+       sk->sk_wmem_queued   -= skb->truesize;
+       sk_mem_uncharge(sk, skb->truesize);
+
        sock_wfree(skb);
        __sctp_write_space(asoc);
 
@@ -5382,6 +6065,11 @@ void sctp_sock_rfree(struct sk_buff *skb)
        struct sctp_ulpevent *event = sctp_skb2event(skb);
 
        atomic_sub(event->rmem_len, &sk->sk_rmem_alloc);
+
+       /*
+        * Mimic the behavior of sock_rfree
+        */
+       sk_mem_uncharge(sk, event->rmem_len);
 }
 
 
@@ -5395,7 +6083,7 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
        DEFINE_WAIT(wait);
 
        SCTP_DEBUG_PRINTK("wait_for_sndbuf: asoc=%p, timeo=%ld, msg_len=%zu\n",
-                         asoc, (long)(*timeo_p), msg_len);
+                         asoc, (long)(*timeo_p), msg_len);
 
        /* Increment the association's refcnt.  */
        sctp_association_hold(asoc);
@@ -5587,7 +6275,7 @@ static int sctp_wait_for_accept(struct sock *sk, long timeo)
        return err;
 }
 
-void sctp_wait_for_close(struct sock *sk, long timeout)
+static void sctp_wait_for_close(struct sock *sk, long timeout)
 {
        DEFINE_WAIT(wait);
 
@@ -5603,6 +6291,36 @@ void sctp_wait_for_close(struct sock *sk, long timeout)
        finish_wait(sk->sk_sleep, &wait);
 }
 
+static void sctp_sock_rfree_frag(struct sk_buff *skb)
+{
+       struct sk_buff *frag;
+
+       if (!skb->data_len)
+               goto done;
+
+       /* Don't forget the fragments. */
+       for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next)
+               sctp_sock_rfree_frag(frag);
+
+done:
+       sctp_sock_rfree(skb);
+}
+
+static void sctp_skb_set_owner_r_frag(struct sk_buff *skb, struct sock *sk)
+{
+       struct sk_buff *frag;
+
+       if (!skb->data_len)
+               goto done;
+
+       /* Don't forget the fragments. */
+       for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next)
+               sctp_skb_set_owner_r_frag(frag, sk);
+
+done:
+       sctp_skb_set_owner_r(skb, sk);
+}
+
 /* Populate the fields of the newsk from the oldsk and migrate the assoc
  * and its messages to the newsk.
  */
@@ -5616,7 +6334,7 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
        struct sctp_endpoint *newep = newsp->ep;
        struct sk_buff *skb, *tmp;
        struct sctp_ulpevent *event;
-       int flags = 0;
+       struct sctp_bind_hashbucket *head;
 
        /* Migrate socket buffer sizes and all the socket level options to the
         * new socket.
@@ -5633,23 +6351,21 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
        newsp->hmac = NULL;
 
        /* Hook this new socket in to the bind_hash list. */
+       head = &sctp_port_hashtable[sctp_phashfn(inet_sk(oldsk)->num)];
+       sctp_local_bh_disable();
+       sctp_spin_lock(&head->lock);
        pp = sctp_sk(oldsk)->bind_hash;
        sk_add_bind_node(newsk, &pp->owner);
        sctp_sk(newsk)->bind_hash = pp;
        inet_sk(newsk)->num = inet_sk(oldsk)->num;
+       sctp_spin_unlock(&head->lock);
+       sctp_local_bh_enable();
 
        /* Copy the bind_addr list from the original endpoint to the new
         * endpoint so that we can handle restarts properly
         */
-       if (PF_INET6 == assoc->base.sk->sk_family)
-               flags = SCTP_ADDR6_ALLOWED;
-       if (assoc->peer.ipv4_address)
-               flags |= SCTP_ADDR4_PEERSUPP;
-       if (assoc->peer.ipv6_address)
-               flags |= SCTP_ADDR6_PEERSUPP;
-       sctp_bind_addr_copy(&newsp->ep->base.bind_addr,
-                            &oldsp->ep->base.bind_addr,
-                            SCTP_SCOPE_GLOBAL, GFP_KERNEL, flags);
+       sctp_bind_addr_dup(&newsp->ep->base.bind_addr,
+                               &oldsp->ep->base.bind_addr, GFP_KERNEL);
 
        /* Move any messages in the old socket's receive queue that are for the
         * peeled off association to the new socket's receive queue.
@@ -5657,10 +6373,10 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
        sctp_skb_for_each(skb, &oldsk->sk_receive_queue, tmp) {
                event = sctp_skb2event(skb);
                if (event->asoc == assoc) {
-                       sctp_sock_rfree(skb);
+                       sctp_sock_rfree_frag(skb);
                        __skb_unlink(skb, &oldsk->sk_receive_queue);
                        __skb_queue_tail(&newsk->sk_receive_queue, skb);
-                       sctp_skb_set_owner_r(skb, newsk);
+                       sctp_skb_set_owner_r_frag(skb, newsk);
                }
        }
 
@@ -5671,9 +6387,9 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
         * 3) Peeling off non-partial delivery; move pd_lobby to receive_queue.
         */
        skb_queue_head_init(&newsp->pd_lobby);
-       sctp_sk(newsk)->pd_mode = assoc->ulpq.pd_mode;
+       atomic_set(&sctp_sk(newsk)->pd_mode, assoc->ulpq.pd_mode);
 
-       if (sctp_sk(oldsk)->pd_mode) {
+       if (atomic_read(&sctp_sk(oldsk)->pd_mode)) {
                struct sk_buff_head *queue;
 
                /* Decide which queue to move pd_lobby skbs to. */
@@ -5688,10 +6404,10 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
                sctp_skb_for_each(skb, &oldsp->pd_lobby, tmp) {
                        event = sctp_skb2event(skb);
                        if (event->asoc == assoc) {
-                               sctp_sock_rfree(skb);
+                               sctp_sock_rfree_frag(skb);
                                __skb_unlink(skb, &oldsp->pd_lobby);
                                __skb_queue_tail(queue, skb);
-                               sctp_skb_set_owner_r(skb, newsk);
+                               sctp_skb_set_owner_r_frag(skb, newsk);
                        }
                }
 
@@ -5699,8 +6415,18 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
                 * delivery to finish.
                 */
                if (assoc->ulpq.pd_mode)
-                       sctp_clear_pd(oldsk);
+                       sctp_clear_pd(oldsk, NULL);
+
+       }
+
+       sctp_skb_for_each(skb, &assoc->ulpq.reasm, tmp) {
+               sctp_sock_rfree_frag(skb);
+               sctp_skb_set_owner_r_frag(skb, newsk);
+       }
 
+       sctp_skb_for_each(skb, &assoc->ulpq.lobby, tmp) {
+               sctp_sock_rfree_frag(skb);
+               sctp_skb_set_owner_r_frag(skb, newsk);
        }
 
        /* Set the type of socket to indicate that it is peeled off from the
@@ -5714,8 +6440,11 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
         * queued to the backlog.  This prevents a potential race between
         * backlog processing on the old socket and new-packet processing
         * on the new socket.
+        *
+        * The caller has just allocated newsk so we can guarantee that other
+        * paths won't try to lock it and then oldsk.
         */
-       sctp_lock_sock(newsk);
+       lock_sock_nested(newsk, SINGLE_DEPTH_NESTING);
        sctp_assoc_migrate(assoc, newsk);
 
        /* If the association on the newsk is already closed before accept()
@@ -5728,6 +6457,9 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
        sctp_release_sock(newsk);
 }
 
+
+DEFINE_PROTO_INUSE(sctp)
+
 /* This proto struct describes the ULP interface for SCTP.  */
 struct proto sctp_prot = {
        .name        =  "SCTP",
@@ -5750,9 +6482,18 @@ struct proto sctp_prot = {
        .unhash      =  sctp_unhash,
        .get_port    =  sctp_get_port,
        .obj_size    =  sizeof(struct sctp_sock),
+       .sysctl_mem  =  sysctl_sctp_mem,
+       .sysctl_rmem =  sysctl_sctp_rmem,
+       .sysctl_wmem =  sysctl_sctp_wmem,
+       .memory_pressure = &sctp_memory_pressure,
+       .enter_memory_pressure = sctp_enter_memory_pressure,
+       .memory_allocated = &sctp_memory_allocated,
+       REF_PROTO_INUSE(sctp)
 };
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+DEFINE_PROTO_INUSE(sctpv6)
+
 struct proto sctpv6_prot = {
        .name           = "SCTPv6",
        .owner          = THIS_MODULE,
@@ -5774,5 +6515,12 @@ struct proto sctpv6_prot = {
        .unhash         = sctp_unhash,
        .get_port       = sctp_get_port,
        .obj_size       = sizeof(struct sctp6_sock),
+       .sysctl_mem     = sysctl_sctp_mem,
+       .sysctl_rmem    = sysctl_sctp_rmem,
+       .sysctl_wmem    = sysctl_sctp_wmem,
+       .memory_pressure = &sctp_memory_pressure,
+       .enter_memory_pressure = sctp_enter_memory_pressure,
+       .memory_allocated = &sctp_memory_allocated,
+       REF_PROTO_INUSE(sctpv6)
 };
 #endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */