Commit f68b2e05 authored by Vlad Yasevich's avatar Vlad Yasevich

sctp: Fix SCTP_MAXSEG socket option to comply to spec.

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: default avatarVlad Yasevich <vladislav.yasevich@hp.com>
parent cb95ea32
...@@ -486,15 +486,16 @@ static inline __s32 sctp_jitter(__u32 rto) ...@@ -486,15 +486,16 @@ static inline __s32 sctp_jitter(__u32 rto)
} }
/* Break down data chunks at this point. */ /* 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; int frag = pmtu;
frag -= sp->pf->af->net_header_len; frag -= sp->pf->af->net_header_len;
frag -= sizeof(struct sctphdr) + sizeof(struct sctp_data_chunk); frag -= sizeof(struct sctphdr) + sizeof(struct sctp_data_chunk);
if (sp->user_frag) if (asoc->user_frag)
frag = min_t(int, frag, sp->user_frag); frag = min_t(int, frag, asoc->user_frag);
frag = min_t(int, frag, SCTP_MAX_CHUNK_LEN); frag = min_t(int, frag, SCTP_MAX_CHUNK_LEN);
......
...@@ -1763,6 +1763,7 @@ struct sctp_association { ...@@ -1763,6 +1763,7 @@ struct sctp_association {
/* The message size at which SCTP fragmentation will occur. */ /* The message size at which SCTP fragmentation will occur. */
__u32 frag_point; __u32 frag_point;
__u32 user_frag;
/* Counter used to count INIT errors. */ /* Counter used to count INIT errors. */
int init_err_counter; int init_err_counter;
......
...@@ -112,6 +112,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a ...@@ -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) asoc->cookie_life.tv_usec = (sp->assocparams.sasoc_cookie_life % 1000)
* 1000; * 1000;
asoc->frag_point = 0; asoc->frag_point = 0;
asoc->user_frag = sp->user_frag;
/* Set the association max_retrans and RTO values from the /* Set the association max_retrans and RTO values from the
* socket values. * socket values.
...@@ -674,7 +675,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, ...@@ -674,7 +675,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
"%d\n", asoc, asoc->pathmtu); "%d\n", asoc, asoc->pathmtu);
peer->pmtu_pending = 0; 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 /* The asoc->peer.port might not be meaningful yet, but
* initialize the packet structure anyway. * initialize the packet structure anyway.
...@@ -1330,9 +1331,8 @@ void sctp_assoc_sync_pmtu(struct sctp_association *asoc) ...@@ -1330,9 +1331,8 @@ void sctp_assoc_sync_pmtu(struct sctp_association *asoc)
} }
if (pmtu) { if (pmtu) {
struct sctp_sock *sp = sctp_sk(asoc->base.sk);
asoc->pathmtu = pmtu; 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", SCTP_DEBUG_PRINTK("%s: asoc:%p, pmtu:%d, frag_point:%d\n",
......
...@@ -2243,7 +2243,7 @@ static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params, ...@@ -2243,7 +2243,7 @@ static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params,
sctp_assoc_sync_pmtu(asoc); sctp_assoc_sync_pmtu(asoc);
} else if (asoc) { } else if (asoc) {
asoc->pathmtu = params->spp_pathmtu; asoc->pathmtu = params->spp_pathmtu;
sctp_frag_point(sp, params->spp_pathmtu); sctp_frag_point(asoc, params->spp_pathmtu);
} else { } else {
sp->pathmtu = params->spp_pathmtu; sp->pathmtu = params->spp_pathmtu;
} }
...@@ -2880,15 +2880,10 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, int optl ...@@ -2880,15 +2880,10 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, int optl
val -= sizeof(struct sctphdr) + val -= sizeof(struct sctphdr) +
sizeof(struct sctp_data_chunk); sizeof(struct sctp_data_chunk);
} }
asoc->user_frag = val;
asoc->frag_point = val; asoc->frag_point = sctp_frag_point(asoc, asoc->pathmtu);
} else { } else {
sp->user_frag = val; 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; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment