Merge branch 'sctp-refactor-MTU-handling'
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>
This commit is contained in:
commit
4422cc0d5b
|
@ -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 };
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
@ -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__ */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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? */
|
||||
|
|
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -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;
|
||||
if (asoc) {
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
@ -896,7 +895,6 @@ skip_mkasconf:
|
|||
*/
|
||||
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));
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
|
|
@ -242,9 +242,18 @@ void sctp_transport_pmtu(struct sctp_transport *transport, struct sock *sk)
|
|||
&transport->fl, sk);
|
||||
}
|
||||
|
||||
if (transport->dst) {
|
||||
transport->pathmtu = SCTP_TRUNC4(dst_mtu(transport->dst));
|
||||
} else
|
||||
if (transport->param_flags & SPP_PMTUD_DISABLE) {
|
||||
struct sctp_association *asoc = transport->asoc;
|
||||
|
||||
if (!transport->pathmtu && asoc && asoc->pathmtu)
|
||||
transport->pathmtu = asoc->pathmtu;
|
||||
if (transport->pathmtu)
|
||||
return;
|
||||
}
|
||||
|
||||
if (transport->dst)
|
||||
transport->pathmtu = sctp_dst_mtu(transport->dst);
|
||||
else
|
||||
transport->pathmtu = SCTP_DEFAULT_MAXSEGMENT;
|
||||
}
|
||||
|
||||
|
@ -290,6 +299,7 @@ void sctp_transport_route(struct sctp_transport *transport,
|
|||
struct sctp_association *asoc = transport->asoc;
|
||||
struct sctp_af *af = transport->af_specific;
|
||||
|
||||
sctp_transport_dst_release(transport);
|
||||
af->get_dst(transport, saddr, &transport->fl, sctp_opt2sk(opt));
|
||||
|
||||
if (saddr)
|
||||
|
@ -297,21 +307,14 @@ void sctp_transport_route(struct sctp_transport *transport,
|
|||
else
|
||||
af->get_saddr(opt, transport, &transport->fl);
|
||||
|
||||
if ((transport->param_flags & SPP_PMTUD_DISABLE) && transport->pathmtu) {
|
||||
return;
|
||||
}
|
||||
if (transport->dst) {
|
||||
transport->pathmtu = SCTP_TRUNC4(dst_mtu(transport->dst));
|
||||
sctp_transport_pmtu(transport, sctp_opt2sk(opt));
|
||||
|
||||
/* Initialize sk->sk_rcv_saddr, if the transport is the
|
||||
* association's active path for getsockname().
|
||||
*/
|
||||
if (asoc && (!asoc->peer.primary_path ||
|
||||
(transport == asoc->peer.active_path)))
|
||||
opt->pf->to_sk_saddr(&transport->saddr,
|
||||
asoc->base.sk);
|
||||
} else
|
||||
transport->pathmtu = SCTP_DEFAULT_MAXSEGMENT;
|
||||
/* Initialize sk->sk_rcv_saddr, if the transport is the
|
||||
* association's active path for getsockname().
|
||||
*/
|
||||
if (transport->dst && asoc &&
|
||||
(!asoc->peer.primary_path || transport == asoc->peer.active_path))
|
||||
opt->pf->to_sk_saddr(&transport->saddr, asoc->base.sk);
|
||||
}
|
||||
|
||||
/* Hold a reference to a transport. */
|
||||
|
|
Loading…
Reference in New Issue