sctp: Fix SCTP_MAXSEG socket option to comply to spec.
authorVlad Yasevich <vladislav.yasevich@hp.com>
Fri, 4 Sep 2009 22:21:00 +0000 (18:21 -0400)
committerVlad Yasevich <vladislav.yasevich@hp.com>
Fri, 4 Sep 2009 22:21:00 +0000 (18:21 -0400)
We had a bug that we never stored the user-defined value for
MAXSEG when setting the value on an association.  Thus future
PMTU events ended up re-writing the frag point and increasing
it past user limit.  Additionally, when setting the option on
the socket/endpoint, we effect all current associations, which
is against spec.

Now, we store the user 'maxseg' value along with the computed
'frag_point'.  We inherit 'maxseg' from the socket at association
creation and use it as an upper limit for 'frag_point' when its
set.

Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
include/net/sctp/sctp.h
include/net/sctp/structs.h
net/sctp/associola.c
net/sctp/socket.c

index d16a304..8a6d529 100644 (file)
@@ -486,15 +486,16 @@ static inline __s32 sctp_jitter(__u32 rto)
 }
 
 /* Break down data chunks at this point.  */
-static inline int sctp_frag_point(const struct sctp_sock *sp, int pmtu)
+static inline int sctp_frag_point(const struct sctp_association *asoc, int pmtu)
 {
+       struct sctp_sock *sp = sctp_sk(asoc->base.sk);
        int frag = pmtu;
 
        frag -= sp->pf->af->net_header_len;
        frag -= sizeof(struct sctphdr) + sizeof(struct sctp_data_chunk);
 
-       if (sp->user_frag)
-               frag = min_t(int, frag, sp->user_frag);
+       if (asoc->user_frag)
+               frag = min_t(int, frag, asoc->user_frag);
 
        frag = min_t(int, frag, SCTP_MAX_CHUNK_LEN);
 
index df4c632..b106128 100644 (file)
@@ -1763,6 +1763,7 @@ struct sctp_association {
 
        /* The message size at which SCTP fragmentation will occur. */
        __u32 frag_point;
+       __u32 user_frag;
 
        /* Counter used to count INIT errors. */
        int init_err_counter;
index 39c3821..1f05b94 100644 (file)
@@ -112,6 +112,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
        asoc->cookie_life.tv_usec = (sp->assocparams.sasoc_cookie_life % 1000)
                                        * 1000;
        asoc->frag_point = 0;
+       asoc->user_frag = sp->user_frag;
 
        /* Set the association max_retrans and RTO values from the
         * socket values.
@@ -674,7 +675,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
                          "%d\n", asoc, asoc->pathmtu);
        peer->pmtu_pending = 0;
 
-       asoc->frag_point = sctp_frag_point(sp, asoc->pathmtu);
+       asoc->frag_point = sctp_frag_point(asoc, asoc->pathmtu);
 
        /* The asoc->peer.port might not be meaningful yet, but
         * initialize the packet structure anyway.
@@ -1330,9 +1331,8 @@ void sctp_assoc_sync_pmtu(struct sctp_association *asoc)
        }
 
        if (pmtu) {
-               struct sctp_sock *sp = sctp_sk(asoc->base.sk);
                asoc->pathmtu = pmtu;
-               asoc->frag_point = sctp_frag_point(sp, pmtu);
+               asoc->frag_point = sctp_frag_point(asoc, pmtu);
        }
 
        SCTP_DEBUG_PRINTK("%s: asoc:%p, pmtu:%d, frag_point:%d\n",
index 95a5623..89af37a 100644 (file)
@@ -2243,7 +2243,7 @@ static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params,
                        sctp_assoc_sync_pmtu(asoc);
                } else if (asoc) {
                        asoc->pathmtu = params->spp_pathmtu;
-                       sctp_frag_point(sp, params->spp_pathmtu);
+                       sctp_frag_point(asoc, params->spp_pathmtu);
                } else {
                        sp->pathmtu = params->spp_pathmtu;
                }
@@ -2880,15 +2880,10 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, int optl
                        val -= sizeof(struct sctphdr) +
                                        sizeof(struct sctp_data_chunk);
                }
-
-               asoc->frag_point = val;
+               asoc->user_frag = val;
+               asoc->frag_point = sctp_frag_point(asoc, asoc->pathmtu);
        } else {
                sp->user_frag = val;
-
-               /* Update the frag_point of the existing associations. */
-               list_for_each_entry(asoc, &(sp->ep->asocs), asocs) {
-                       asoc->frag_point = sctp_frag_point(sp, asoc->pathmtu);
-               }
        }
 
        return 0;