Skip to content

Commit

Permalink
Merge branch 'sctp-refactor-MTU-handling'
Browse files Browse the repository at this point in the history
Marcelo Ricardo Leitner says:

====================
sctp: refactor MTU handling

Currently MTU handling is spread over SCTP stack. There are multiple
places doing same/similar calculations and updating them is error prone
as one spot can easily be left out.

This patchset converges it into a more concise and consistent code. In
general, it moves MTU handling from functions with bigger objectives,
such as sctp_assoc_add_peer(), to specific functions.

It's also a preparation for the next patchset, which removes the
duplication between sctp_make_op_error_space and
sctp_make_op_error_fixed and relies on sctp_mtu_payload introduced here.

More details on each patch.
====================

Reviewed-by: Xin Long <lucien.xin@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Apr 27, 2018
2 parents 5a643c8 + 38687b5 commit 4422cc0
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 134 deletions.
5 changes: 2 additions & 3 deletions include/net/sctp/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,11 +254,10 @@ enum { SCTP_ARBITRARY_COOKIE_ECHO_LEN = 200 };
#define SCTP_TSN_MAP_SIZE 4096

/* We will not record more than this many duplicate TSNs between two
* SACKs. The minimum PMTU is 576. Remove all the headers and there
* is enough room for 131 duplicate reports. Round down to the
* SACKs. The minimum PMTU is 512. Remove all the headers and there
* is enough room for 117 duplicate reports. Round down to the
* nearest power of 2.
*/
enum { SCTP_MIN_PMTU = 576 };
enum { SCTP_MAX_DUP_TSNS = 16 };
enum { SCTP_MAX_GABS = 16 };

Expand Down
52 changes: 19 additions & 33 deletions include/net/sctp/sctp.h
Original file line number Diff line number Diff line change
Expand Up @@ -428,32 +428,6 @@ static inline int sctp_list_single_entry(struct list_head *head)
return (head->next != head) && (head->next == head->prev);
}

/* Break down data chunks at this point. */
static inline int sctp_frag_point(const struct sctp_association *asoc, int pmtu)
{
struct sctp_sock *sp = sctp_sk(asoc->base.sk);
struct sctp_af *af = sp->pf->af;
int frag = pmtu;

frag -= af->ip_options_len(asoc->base.sk);
frag -= af->net_header_len;
frag -= sizeof(struct sctphdr) + sctp_datachk_len(&asoc->stream);

if (asoc->user_frag)
frag = min_t(int, frag, asoc->user_frag);

frag = SCTP_TRUNC4(min_t(int, frag, SCTP_MAX_CHUNK_LEN -
sctp_datachk_len(&asoc->stream)));

return frag;
}

static inline void sctp_assoc_pending_pmtu(struct sctp_association *asoc)
{
sctp_assoc_sync_pmtu(asoc);
asoc->pmtu_pending = 0;
}

static inline bool sctp_chunk_pending(const struct sctp_chunk *chunk)
{
return !list_empty(&chunk->list);
Expand Down Expand Up @@ -607,17 +581,29 @@ static inline struct dst_entry *sctp_transport_dst_check(struct sctp_transport *
return t->dst;
}

static inline bool sctp_transport_pmtu_check(struct sctp_transport *t)
/* Calculate max payload size given a MTU, or the total overhead if
* given MTU is zero
*/
static inline __u32 sctp_mtu_payload(const struct sctp_sock *sp,
__u32 mtu, __u32 extra)
{
__u32 pmtu = max_t(size_t, SCTP_TRUNC4(dst_mtu(t->dst)),
SCTP_DEFAULT_MINSEGMENT);
__u32 overhead = sizeof(struct sctphdr) + extra;

if (t->pathmtu == pmtu)
return true;
if (sp)
overhead += sp->pf->af->net_header_len;
else
overhead += sizeof(struct ipv6hdr);

t->pathmtu = pmtu;
if (WARN_ON_ONCE(mtu && mtu <= overhead))
mtu = overhead;

return false;
return mtu ? mtu - overhead : overhead;
}

static inline __u32 sctp_dst_mtu(const struct dst_entry *dst)
{
return SCTP_TRUNC4(max_t(__u32, dst_mtu(dst),
SCTP_DEFAULT_MINSEGMENT));
}

#endif /* __net_sctp_h__ */
2 changes: 2 additions & 0 deletions include/net/sctp/structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -2097,6 +2097,8 @@ int sctp_assoc_update(struct sctp_association *old,

__u32 sctp_association_get_next_tsn(struct sctp_association *);

void sctp_assoc_update_frag_point(struct sctp_association *asoc);
void sctp_assoc_set_pmtu(struct sctp_association *asoc, __u32 pmtu);
void sctp_assoc_sync_pmtu(struct sctp_association *asoc);
void sctp_assoc_rwnd_increase(struct sctp_association *, unsigned int);
void sctp_assoc_rwnd_decrease(struct sctp_association *, unsigned int);
Expand Down
60 changes: 32 additions & 28 deletions net/sctp/associola.c
Original file line number Diff line number Diff line change
Expand Up @@ -652,33 +652,20 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
*/
peer->param_flags = asoc->param_flags;

sctp_transport_route(peer, NULL, sp);

/* Initialize the pmtu of the transport. */
if (peer->param_flags & SPP_PMTUD_DISABLE) {
if (asoc->pathmtu)
peer->pathmtu = asoc->pathmtu;
else
peer->pathmtu = SCTP_DEFAULT_MAXSEGMENT;
}
sctp_transport_route(peer, NULL, sp);

/* If this is the first transport addr on this association,
* initialize the association PMTU to the peer's PMTU.
* If not and the current association PMTU is higher than the new
* peer's PMTU, reset the association PMTU to the new peer's PMTU.
*/
if (asoc->pathmtu)
asoc->pathmtu = min_t(int, peer->pathmtu, asoc->pathmtu);
else
asoc->pathmtu = peer->pathmtu;

pr_debug("%s: association:%p PMTU set to %d\n", __func__, asoc,
asoc->pathmtu);
sctp_assoc_set_pmtu(asoc, asoc->pathmtu ?
min_t(int, peer->pathmtu, asoc->pathmtu) :
peer->pathmtu);

peer->pmtu_pending = 0;

asoc->frag_point = sctp_frag_point(asoc, asoc->pathmtu);

/* The asoc->peer.port might not be meaningful yet, but
* initialize the packet structure anyway.
*/
Expand Down Expand Up @@ -1381,6 +1368,31 @@ sctp_assoc_choose_alter_transport(struct sctp_association *asoc,
}
}

void sctp_assoc_update_frag_point(struct sctp_association *asoc)
{
int frag = sctp_mtu_payload(sctp_sk(asoc->base.sk), asoc->pathmtu,
sctp_datachk_len(&asoc->stream));

if (asoc->user_frag)
frag = min_t(int, frag, asoc->user_frag);

frag = min_t(int, frag, SCTP_MAX_CHUNK_LEN -
sctp_datachk_len(&asoc->stream));

asoc->frag_point = SCTP_TRUNC4(frag);
}

void sctp_assoc_set_pmtu(struct sctp_association *asoc, __u32 pmtu)
{
if (asoc->pathmtu != pmtu) {
asoc->pathmtu = pmtu;
sctp_assoc_update_frag_point(asoc);
}

pr_debug("%s: asoc:%p, pmtu:%d, frag_point:%d\n", __func__, asoc,
asoc->pathmtu, asoc->frag_point);
}

/* Update the association's pmtu and frag_point by going through all the
* transports. This routine is called when a transport's PMTU has changed.
*/
Expand All @@ -1393,24 +1405,16 @@ void sctp_assoc_sync_pmtu(struct sctp_association *asoc)
return;

/* Get the lowest pmtu of all the transports. */
list_for_each_entry(t, &asoc->peer.transport_addr_list,
transports) {
list_for_each_entry(t, &asoc->peer.transport_addr_list, transports) {
if (t->pmtu_pending && t->dst) {
sctp_transport_update_pmtu(
t, SCTP_TRUNC4(dst_mtu(t->dst)));
sctp_transport_update_pmtu(t, sctp_dst_mtu(t->dst));
t->pmtu_pending = 0;
}
if (!pmtu || (t->pathmtu < pmtu))
pmtu = t->pathmtu;
}

if (pmtu) {
asoc->pathmtu = pmtu;
asoc->frag_point = sctp_frag_point(asoc, pmtu);
}

pr_debug("%s: asoc:%p, pmtu:%d, frag_point:%d\n", __func__, asoc,
asoc->pathmtu, asoc->frag_point);
sctp_assoc_set_pmtu(asoc, pmtu);
}

/* Should we send a SACK to update our peer? */
Expand Down
12 changes: 1 addition & 11 deletions net/sctp/chunk.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,6 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
struct list_head *pos, *temp;
struct sctp_chunk *chunk;
struct sctp_datamsg *msg;
struct sctp_sock *sp;
struct sctp_af *af;
int err;

msg = sctp_datamsg_new(GFP_KERNEL);
Expand All @@ -192,12 +190,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
/* This is the biggest possible DATA chunk that can fit into
* the packet
*/
sp = sctp_sk(asoc->base.sk);
af = sp->pf->af;
max_data = asoc->pathmtu - af->net_header_len -
sizeof(struct sctphdr) - sctp_datachk_len(&asoc->stream) -
af->ip_options_len(asoc->base.sk);
max_data = SCTP_TRUNC4(max_data);
max_data = asoc->frag_point;

/* If the the peer requested that we authenticate DATA chunks
* we need to account for bundling of the AUTH chunks along with
Expand All @@ -222,9 +215,6 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
}
}

/* Check what's our max considering the above */
max_data = min_t(size_t, max_data, asoc->frag_point);

/* Set first_len and then account for possible bundles on first frag */
first_len = max_data;

Expand Down
28 changes: 10 additions & 18 deletions net/sctp/output.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ void sctp_packet_config(struct sctp_packet *packet, __u32 vtag,
{
struct sctp_transport *tp = packet->transport;
struct sctp_association *asoc = tp->asoc;
struct sctp_sock *sp = NULL;
struct sock *sk;
size_t overhead = sizeof(struct ipv6hdr) + sizeof(struct sctphdr);

pr_debug("%s: packet:%p vtag:0x%x\n", __func__, packet, vtag);
packet->vtag = vtag;
Expand All @@ -102,28 +102,20 @@ void sctp_packet_config(struct sctp_packet *packet, __u32 vtag,

/* set packet max_size with pathmtu, then calculate overhead */
packet->max_size = tp->pathmtu;

if (asoc) {
struct sctp_sock *sp = sctp_sk(asoc->base.sk);
struct sctp_af *af = sp->pf->af;

overhead = af->net_header_len +
af->ip_options_len(asoc->base.sk);
overhead += sizeof(struct sctphdr);
packet->overhead = overhead;
packet->size = overhead;
} else {
packet->overhead = overhead;
packet->size = overhead;
return;
sk = asoc->base.sk;
sp = sctp_sk(sk);
}
packet->overhead = sctp_mtu_payload(sp, 0, 0);
packet->size = packet->overhead;

if (!asoc)
return;

/* update dst or transport pathmtu if in need */
sk = asoc->base.sk;
if (!sctp_transport_dst_check(tp)) {
sctp_transport_route(tp, NULL, sctp_sk(sk));
if (asoc->param_flags & SPP_PMTUD_ENABLE)
sctp_assoc_sync_pmtu(asoc);
} else if (!sctp_transport_pmtu_check(tp)) {
sctp_transport_route(tp, NULL, sp);
if (asoc->param_flags & SPP_PMTUD_ENABLE)
sctp_assoc_sync_pmtu(asoc);
}
Expand Down
43 changes: 19 additions & 24 deletions net/sctp/socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -644,16 +644,15 @@ static int sctp_send_asconf_add_ip(struct sock *sk,

list_for_each_entry(trans,
&asoc->peer.transport_addr_list, transports) {
/* Clear the source and route cache */
sctp_transport_dst_release(trans);
trans->cwnd = min(4*asoc->pathmtu, max_t(__u32,
2*asoc->pathmtu, 4380));
trans->ssthresh = asoc->peer.i.a_rwnd;
trans->rto = asoc->rto_initial;
sctp_max_rto(asoc, trans);
trans->rtt = trans->srtt = trans->rttvar = 0;
/* Clear the source and route cache */
sctp_transport_route(trans, NULL,
sctp_sk(asoc->base.sk));
sctp_sk(asoc->base.sk));
}
}
retval = sctp_send_asconf(asoc, chunk);
Expand Down Expand Up @@ -896,7 +895,6 @@ static int sctp_send_asconf_del_ip(struct sock *sk,
*/
list_for_each_entry(transport, &asoc->peer.transport_addr_list,
transports) {
sctp_transport_dst_release(transport);
sctp_transport_route(transport, NULL,
sctp_sk(asoc->base.sk));
}
Expand Down Expand Up @@ -1895,6 +1893,7 @@ static int sctp_sendmsg_to_asoc(struct sctp_association *asoc,
struct sctp_sndrcvinfo *sinfo)
{
struct sock *sk = asoc->base.sk;
struct sctp_sock *sp = sctp_sk(sk);
struct net *net = sock_net(sk);
struct sctp_datamsg *datamsg;
bool wait_connect = false;
Expand All @@ -1913,13 +1912,16 @@ static int sctp_sendmsg_to_asoc(struct sctp_association *asoc,
goto err;
}

if (sctp_sk(sk)->disable_fragments && msg_len > asoc->frag_point) {
if (sp->disable_fragments && msg_len > asoc->frag_point) {
err = -EMSGSIZE;
goto err;
}

if (asoc->pmtu_pending)
sctp_assoc_pending_pmtu(asoc);
if (asoc->pmtu_pending) {
if (sp->param_flags & SPP_PMTUD_ENABLE)
sctp_assoc_sync_pmtu(asoc);
asoc->pmtu_pending = 0;
}

if (sctp_wspace(asoc) < msg_len)
sctp_prsctp_prune(asoc, sinfo, msg_len - sctp_wspace(asoc));
Expand All @@ -1936,7 +1938,7 @@ static int sctp_sendmsg_to_asoc(struct sctp_association *asoc,
if (err)
goto err;

if (sctp_sk(sk)->strm_interleave) {
if (sp->strm_interleave) {
timeo = sock_sndtimeo(sk, 0);
err = sctp_wait_for_connect(asoc, &timeo);
if (err)
Expand Down Expand Up @@ -2539,7 +2541,7 @@ static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params,
trans->pathmtu = params->spp_pathmtu;
sctp_assoc_sync_pmtu(asoc);
} else if (asoc) {
asoc->pathmtu = params->spp_pathmtu;
sctp_assoc_set_pmtu(asoc, params->spp_pathmtu);
} else {
sp->pathmtu = params->spp_pathmtu;
}
Expand Down Expand Up @@ -3209,7 +3211,6 @@ static int sctp_setsockopt_mappedv4(struct sock *sk, char __user *optval, unsign
static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned int optlen)
{
struct sctp_sock *sp = sctp_sk(sk);
struct sctp_af *af = sp->pf->af;
struct sctp_assoc_value params;
struct sctp_association *asoc;
int val;
Expand All @@ -3231,30 +3232,24 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned
return -EINVAL;
}

asoc = sctp_id2assoc(sk, params.assoc_id);

if (val) {
int min_len, max_len;
__u16 datasize = asoc ? sctp_datachk_len(&asoc->stream) :
sizeof(struct sctp_data_chunk);

min_len = SCTP_DEFAULT_MINSEGMENT - af->net_header_len;
min_len -= af->ip_options_len(sk);
min_len -= sizeof(struct sctphdr) +
sizeof(struct sctp_data_chunk);

max_len = SCTP_MAX_CHUNK_LEN - sizeof(struct sctp_data_chunk);
min_len = sctp_mtu_payload(sp, SCTP_DEFAULT_MINSEGMENT,
datasize);
max_len = SCTP_MAX_CHUNK_LEN - datasize;

if (val < min_len || val > max_len)
return -EINVAL;
}

asoc = sctp_id2assoc(sk, params.assoc_id);
if (asoc) {
if (val == 0) {
val = asoc->pathmtu - af->net_header_len;
val -= af->ip_options_len(sk);
val -= sizeof(struct sctphdr) +
sctp_datachk_len(&asoc->stream);
}
asoc->user_frag = val;
asoc->frag_point = sctp_frag_point(asoc, asoc->pathmtu);
sctp_assoc_update_frag_point(asoc);
} else {
if (params.assoc_id && sctp_style(sk, UDP))
return -EINVAL;
Expand Down
Loading

0 comments on commit 4422cc0

Please sign in to comment.