]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/sctp/socket.c
[NETFILTER]: {eb,ip6,ip}t_LOG: remove remains of LOG target overloading
[linux-2.6-omap-h63xx.git] / net / sctp / socket.c
index bdd8bd428b64f3de8ae4fff0c9414fce324ddf30..f904f2bc0f2c6f63c121db7030f92996130b2762 100644 (file)
@@ -381,12 +381,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,
@@ -396,10 +396,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. */
@@ -449,7 +449,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;
@@ -470,13 +470,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)
 {
@@ -517,8 +517,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;
@@ -627,6 +627,12 @@ int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt)
                        retval = -EINVAL;
                        goto err_bindx_rem;
                }
+
+               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;
@@ -664,7 +670,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.
  *
@@ -710,7 +716,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.
                 */
@@ -898,7 +904,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;
@@ -906,7 +912,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;
@@ -935,7 +941,7 @@ SCTP_STATIC int sctp_setsockopt_bindx(struct sock* sk,
        default:
                err = -EINVAL;
                break;
-        };
+       };
 
 out:
        kfree(kaddrs);
@@ -1035,10 +1041,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 &&
@@ -1094,8 +1100,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;
@@ -1435,7 +1441,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;
@@ -1599,7 +1605,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);
                        }
                }
@@ -2033,6 +2039,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
@@ -2075,13 +2085,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;
+                       }
                }
        }
 
@@ -2098,7 +2125,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);
@@ -2129,7 +2161,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);
@@ -2157,7 +2193,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) {
@@ -2249,7 +2289,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
@@ -2298,7 +2338,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;
@@ -2307,22 +2347,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;
                }
@@ -2338,17 +2378,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;
 }
 
@@ -2374,13 +2414,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;
 }
@@ -2511,7 +2551,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);
@@ -2665,7 +2705,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;
@@ -2703,7 +2743,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)
@@ -2731,17 +2771,17 @@ 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_setadaption))
+       if (optlen != sizeof(struct sctp_setadaptation))
                return -EINVAL;
-       if (copy_from_user(&adaption, optval, optlen)) 
+       if (copy_from_user(&adaptation, optval, optlen))
                return -EFAULT;
 
-       sctp_sk(sk)->adaption_ind = adaption.ssb_adaption_ind;
+       sctp_sk(sk)->adaptation_ind = adaptation.ssb_adaptation_ind;
 
        return 0;
 }
@@ -2786,6 +2826,102 @@ static int sctp_setsockopt_context(struct sock *sk, char __user *optval,
        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(int))
+               return -EINVAL;
+       if (get_user(val, (int __user *)optval))
+               return -EFAULT;
+
+       if (val < 0)
+               return -EINVAL;
+
+       sctp_sk(sk)->max_burst = val;
+
+       return 0;
+}
+
 /* API 6.2 setsockopt(), getsockopt()
  *
  * Applications use setsockopt() and getsockopt() to set or retrieve
@@ -2865,6 +3001,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);
@@ -2894,13 +3033,18 @@ 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;
        default:
                retval = -ENOPROTOOPT;
                break;
@@ -3015,7 +3159,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;
 }
 
@@ -3060,6 +3204,7 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
        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
@@ -3087,7 +3232,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));
 
@@ -3099,8 +3244,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.
@@ -3123,13 +3268,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
@@ -3636,7 +3782,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
@@ -3680,7 +3826,7 @@ static int sctp_getsockopt_delayed_ack_time(struct sock *sk, int len,
        /* 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;
@@ -3756,7 +3902,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
  */
@@ -3833,7 +3979,7 @@ 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) - 
+       space_left = len - sizeof(struct sctp_getaddrs) -
                        offsetof(struct sctp_getaddrs,addrs);
 
        list_for_each(pos, &asoc->peer.transport_addr_list) {
@@ -3907,7 +4053,7 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len,
                                addr = list_entry(pos,
                                                  struct sctp_sockaddr_entry,
                                                  list);
-                               if ((PF_INET == sk->sk_family) && 
+                               if ((PF_INET == sk->sk_family) &&
                                    (AF_INET6 == addr->a.sa.sa_family))
                                        continue;
                                cnt++;
@@ -3941,7 +4087,7 @@ static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_add
 
        list_for_each_safe(pos, next, &sctp_local_addr_list) {
                addr = list_entry(pos, struct sctp_sockaddr_entry, list);
-               if ((PF_INET == sk->sk_family) && 
+               if ((PF_INET == sk->sk_family) &&
                    (AF_INET6 == addr->a.sa.sa_family))
                        continue;
                memcpy(&temp, &addr->a, sizeof(temp));
@@ -3970,7 +4116,7 @@ static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port,
 
        list_for_each_safe(pos, next, &sctp_local_addr_list) {
                addr = list_entry(pos, struct sctp_sockaddr_entry, list);
-               if ((PF_INET == sk->sk_family) && 
+               if ((PF_INET == sk->sk_family) &&
                    (AF_INET6 == addr->a.sa.sa_family))
                        continue;
                memcpy(&temp, &addr->a, sizeof(temp));
@@ -4051,7 +4197,7 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
                                err = cnt;
                                goto unlock;
                        }
-                       goto copy_getaddrs;             
+                       goto copy_getaddrs;
                }
        }
 
@@ -4139,7 +4285,7 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
                                err = cnt;
                                goto unlock;
                        }
-                       goto copy_getaddrs;             
+                       goto copy_getaddrs;
                }
        }
 
@@ -4196,7 +4342,7 @@ static int sctp_getsockopt_primary_addr(struct sock *sk, int len,
 
        if (!asoc->peer.primary_path)
                return -ENOTCONN;
-       
+
        memcpy(&prim.ssp_addr, &asoc->peer.primary_path->ipaddr,
                asoc->peer.primary_path->af_specific->sockaddr_len);
 
@@ -4210,21 +4356,21 @@ static int sctp_getsockopt_primary_addr(struct sock *sk, int len,
 }
 
 /*
- * 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))
+       adaptation.ssb_adaptation_ind = sctp_sk(sk)->adaptation_ind;
+       if (copy_to_user(optval, &adaptation, len))
                return -EFAULT;
 
        return 0;
@@ -4530,6 +4676,77 @@ 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;
+}
+
 SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
                                char __user *optval, int __user *optlen)
 {
@@ -4635,13 +4852,24 @@ 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;
        default:
                retval = -ENOPROTOOPT;
                break;
@@ -4864,7 +5092,7 @@ 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;
        }
@@ -4872,7 +5100,7 @@ SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog)
        /* 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
@@ -4907,7 +5135,7 @@ 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;
        }
@@ -5010,7 +5238,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;
 
@@ -5430,7 +5658,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);
@@ -5638,6 +5866,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.
  */
@@ -5692,10 +5950,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);
                }
        }
 
@@ -5706,9 +5964,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. */
@@ -5723,10 +5981,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);
                        }
                }
 
@@ -5734,8 +5992,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