sctp: Fix data segmentation with small frag_size
authorVlad Yasevich <vladislav.yasevich@hp.com>
Fri, 4 Sep 2009 22:20:56 +0000 (18:20 -0400)
committerVlad Yasevich <vladislav.yasevich@hp.com>
Fri, 4 Sep 2009 22:20:56 +0000 (18:20 -0400)
Since an application may specify the maximum SCTP fragment size
that all data should be fragmented to, we need to fix how
we do segmentation.   Right now, if a user specifies a small
fragment size, the segment size can go negative in the presence
of AUTH or COOKIE_ECHO bundling.

What we need to do is track the largest possbile DATA chunk that
can fit into the mtu.  Then if the fragment size specified is
bigger then this maximum length, we'll shrink it down.  Otherwise,
we just use the smaller segment size without changing it further.

Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
net/sctp/chunk.c

index 1748ef9..9292294 100644 (file)
@@ -158,6 +158,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
 {
        int max, whole, i, offset, over, err;
        int len, first_len;
+       int max_data;
        struct sctp_chunk *chunk;
        struct sctp_datamsg *msg;
        struct list_head *pos, *temp;
@@ -179,8 +180,14 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
                                  __func__, msg, msg->expires_at, jiffies);
        }
 
-       max = asoc->frag_point;
+       /* This is the biggest possible DATA chunk that can fit into
+        * the packet
+        */
+       max_data = asoc->pathmtu -
+               sctp_sk(asoc->base.sk)->pf->af->net_header_len -
+               sizeof(struct sctphdr) - sizeof(struct sctp_data_chunk);
 
+       max = asoc->frag_point;
        /* If the the peer requested that we authenticate DATA chunks
         * we need to accound for bundling of the AUTH chunks along with
         * DATA.
@@ -189,23 +196,30 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
                struct sctp_hmac *hmac_desc = sctp_auth_asoc_get_hmac(asoc);
 
                if (hmac_desc)
-                       max -= WORD_ROUND(sizeof(sctp_auth_chunk_t) +
+                       max_data -= WORD_ROUND(sizeof(sctp_auth_chunk_t) +
                                            hmac_desc->hmac_len);
        }
 
+       /* Now, check if we need to reduce our max */
+       if (max > max_data)
+               max = max_data;
+
        whole = 0;
        first_len = max;
 
        /* Encourage Cookie-ECHO bundling. */
        if (asoc->state < SCTP_STATE_COOKIE_ECHOED) {
-               whole = msg_len / (max - SCTP_ARBITRARY_COOKIE_ECHO_LEN);
+               max_data -= SCTP_ARBITRARY_COOKIE_ECHO_LEN;
 
-               /* Account for the DATA to be bundled with the COOKIE-ECHO. */
-               if (whole) {
-                       first_len = max - SCTP_ARBITRARY_COOKIE_ECHO_LEN;
-                       msg_len -= first_len;
-                       whole = 1;
-               }
+               /* This is the biggesr first_len we can have */
+               if (first_len > max_data)
+                       first_len = max_data;
+       }
+
+       /* Account for a different sized first fragment */
+       if (msg_len >= first_len) {
+               msg_len -= first_len;
+               whole = 1;
        }
 
        /* How many full sized?  How many bytes leftover? */