// SPDX-License-Identifier: GPL-2.0-or-later /* SCTP kernel implementation * (C) Copyright IBM Corp. 2001, 2004 * Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 2001-2003 Intel Corp. * * This file is part of the SCTP kernel implementation * * These functions implement the sctp_outq class. The outqueue handles * bundling and queueing of outgoing SCTP chunks. * * Please send any bug reports or fixes you make to the * email address(es): * lksctp developers <linux-sctp@vger.kernel.org> * * Written or modified by: * La Monte H.P. Yarroll <piggy@acm.org> * Karl Knutson <karl@athena.chicago.il.us> * Perry Melange <pmelange@null.cc.uic.edu> * Xingang Guo <xingang.guo@intel.com> * Hui Huang <hui.huang@nokia.com> * Sridhar Samudrala <sri@us.ibm.com> * Jon Grimm <jgrimm@us.ibm.com>
*/
staticvoid sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp);
/* Add data to the front of the queue. */ staticinlinevoid sctp_outq_head_data(struct sctp_outq *q, struct sctp_chunk *ch)
{ struct sctp_stream_out_ext *oute;
__u16 stream;
/* Take data from the front of the queue. */ staticinlinestruct sctp_chunk *sctp_outq_dequeue_data(struct sctp_outq *q)
{ return q->sched->dequeue(q);
}
/* Add data chunk to the end of the queue. */ staticinlinevoid sctp_outq_tail_data(struct sctp_outq *q, struct sctp_chunk *ch)
{ struct sctp_stream_out_ext *oute;
__u16 stream;
/* * SFR-CACC algorithm: * D) If count_of_newacks is greater than or equal to 2 * and t was not sent to the current primary then the * sender MUST NOT increment missing report count for t.
*/ staticinlineint sctp_cacc_skip_3_1_d(struct sctp_transport *primary, struct sctp_transport *transport, int count_of_newacks)
{ if (count_of_newacks >= 2 && transport != primary) return 1; return 0;
}
/* * SFR-CACC algorithm: * F) If count_of_newacks is less than 2, let d be the * destination to which t was sent. If cacc_saw_newack * is 0 for destination d, then the sender MUST NOT * increment missing report count for t.
*/ staticinlineint sctp_cacc_skip_3_1_f(struct sctp_transport *transport, int count_of_newacks)
{ if (count_of_newacks < 2 &&
(transport && !transport->cacc.cacc_saw_newack)) return 1; return 0;
}
/* * SFR-CACC algorithm: * 3.1) If CYCLING_CHANGEOVER is 0, the sender SHOULD * execute steps C, D, F. * * C has been implemented in sctp_outq_sack
*/ staticinlineint sctp_cacc_skip_3_1(struct sctp_transport *primary, struct sctp_transport *transport, int count_of_newacks)
{ if (!primary->cacc.cycling_changeover) { if (sctp_cacc_skip_3_1_d(primary, transport, count_of_newacks)) return 1; if (sctp_cacc_skip_3_1_f(transport, count_of_newacks)) return 1; return 0;
} return 0;
}
/* * SFR-CACC algorithm: * 3.2) Else if CYCLING_CHANGEOVER is 1, and t is less * than next_tsn_at_change of the current primary, then * the sender MUST NOT increment missing report count * for t.
*/ staticinlineint sctp_cacc_skip_3_2(struct sctp_transport *primary, __u32 tsn)
{ if (primary->cacc.cycling_changeover &&
TSN_lt(tsn, primary->cacc.next_tsn_at_change)) return 1; return 0;
}
/* * SFR-CACC algorithm: * 3) If the missing report count for TSN t is to be * incremented according to [RFC2960] and * [SCTP_STEWART-2002], and CHANGEOVER_ACTIVE is set, * then the sender MUST further execute steps 3.1 and * 3.2 to determine if the missing report count for * TSN t SHOULD NOT be incremented. * * 3.3) If 3.1 and 3.2 do not dictate that the missing * report count for t should not be incremented, then * the sender SHOULD increment missing report count for * t (according to [RFC2960] and [SCTP_STEWART_2002]).
*/ staticinlineint sctp_cacc_skip(struct sctp_transport *primary, struct sctp_transport *transport, int count_of_newacks,
__u32 tsn)
{ if (primary->cacc.changeover_active &&
(sctp_cacc_skip_3_1(primary, transport, count_of_newacks) ||
sctp_cacc_skip_3_2(primary, tsn))) return 1; return 0;
}
/* Initialize an existing sctp_outq. This does the boring stuff. * You still need to define handlers if you really want to DO * something with this structure...
*/ void sctp_outq_init(struct sctp_association *asoc, struct sctp_outq *q)
{
memset(q, 0, sizeof(struct sctp_outq));
/* Free the outqueue structure and any related pending chunks. */ void sctp_outq_free(struct sctp_outq *q)
{ /* Throw away leftover chunks. */
__sctp_outq_teardown(q);
}
/* Put a new chunk in an sctp_outq. */ void sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk, gfp_t gfp)
{ struct net *net = q->asoc->base.net;
/* If it is data, queue it up, otherwise, send it * immediately.
*/ if (sctp_chunk_is_data(chunk)) {
pr_debug("%s: outqueueing: outq:%p, chunk:%p[%s])\n",
__func__, q, chunk, chunk && chunk->chunk_hdr ?
sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) : "illegal chunk");
/* Insert a chunk into the sorted list based on the TSNs. The retransmit list * and the abandoned list are in ascending order.
*/ staticvoid sctp_insert_list(struct list_head *head, struct list_head *new)
{ struct list_head *pos; struct sctp_chunk *nchunk, *lchunk;
__u32 ntsn, ltsn; int done = 0;
/* clear out_curr if all frag chunks are pruned */ if (asoc->stream.out_curr == sout &&
list_is_last(&chk->frag_list, &chk->msg->chunks))
asoc->stream.out_curr = NULL;
/* Mark all the eligible packets on a transport for retransmission. */ void sctp_retransmit_mark(struct sctp_outq *q, struct sctp_transport *transport,
__u8 reason)
{ struct list_head *lchunk, *ltemp; struct sctp_chunk *chunk;
/* Walk through the specified transmitted queue. */
list_for_each_safe(lchunk, ltemp, &transport->transmitted) {
chunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list);
/* If the chunk is abandoned, move it to abandoned list. */ if (sctp_chunk_abandoned(chunk)) {
list_del_init(lchunk);
sctp_insert_list(&q->abandoned, lchunk);
/* If this chunk has not been previousely acked, * stop considering it 'outstanding'. Our peer * will most likely never see it since it will * not be retransmitted
*/ if (!chunk->tsn_gap_acked) { if (chunk->transport)
chunk->transport->flight_size -=
sctp_data_size(chunk);
q->outstanding_bytes -= sctp_data_size(chunk);
q->asoc->peer.rwnd += sctp_data_size(chunk);
} continue;
}
/* If we are doing retransmission due to a timeout or pmtu * discovery, only the chunks that are not yet acked should * be added to the retransmit queue.
*/ if ((reason == SCTP_RTXR_FAST_RTX &&
(chunk->fast_retransmit == SCTP_NEED_FRTX)) ||
(reason != SCTP_RTXR_FAST_RTX && !chunk->tsn_gap_acked)) { /* RFC 2960 6.2.1 Processing a Received SACK * * C) Any time a DATA chunk is marked for * retransmission (via either T3-rtx timer expiration * (Section 6.3.3) or via fast retransmit * (Section 7.2.4)), add the data size of those * chunks to the rwnd.
*/
q->asoc->peer.rwnd += sctp_data_size(chunk);
q->outstanding_bytes -= sctp_data_size(chunk); if (chunk->transport)
transport->flight_size -= sctp_data_size(chunk);
/* sctpimpguide-05 Section 2.8.2 * M5) If a T3-rtx timer expires, the * 'TSN.Missing.Report' of all affected TSNs is set * to 0.
*/
chunk->tsn_missing_report = 0;
/* If a chunk that is being used for RTT measurement * has to be retransmitted, we cannot use this chunk * anymore for RTT measurements. Reset rto_pending so * that a new RTT measurement is started when a new * data chunk is sent.
*/ if (chunk->rtt_in_progress) {
chunk->rtt_in_progress = 0;
transport->rto_pending = 0;
}
/* Move the chunk to the retransmit queue. The chunks * on the retransmit queue are always kept in order.
*/
list_del_init(lchunk);
sctp_insert_list(&q->retransmit, lchunk);
}
}
/* Mark all the eligible packets on a transport for retransmission and force * one packet out.
*/ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport, enum sctp_retransmit_reason reason)
{ struct net *net = q->asoc->base.net;
switch (reason) { case SCTP_RTXR_T3_RTX:
SCTP_INC_STATS(net, SCTP_MIB_T3_RETRANSMITS);
sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_T3_RTX); /* Update the retran path if the T3-rtx timer has expired for * the current retran path.
*/ if (transport == transport->asoc->peer.retran_path)
sctp_assoc_update_retran_path(transport->asoc);
transport->asoc->rtx_data_chunks +=
transport->asoc->unack_data; if (transport->pl.state == SCTP_PL_COMPLETE &&
transport->asoc->unack_data)
sctp_transport_reset_probe_timer(transport); break; case SCTP_RTXR_FAST_RTX:
SCTP_INC_STATS(net, SCTP_MIB_FAST_RETRANSMITS);
sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_FAST_RTX);
q->fast_rtx = 1; break; case SCTP_RTXR_PMTUD:
SCTP_INC_STATS(net, SCTP_MIB_PMTUD_RETRANSMITS); break; case SCTP_RTXR_T1_RTX:
SCTP_INC_STATS(net, SCTP_MIB_T1_RETRANSMITS);
transport->asoc->init_retries++; break; default:
BUG();
}
sctp_retransmit_mark(q, transport, reason);
/* PR-SCTP A5) Any time the T3-rtx timer expires, on any destination, * the sender SHOULD try to advance the "Advanced.Peer.Ack.Point" by * following the procedures outlined in C1 - C5.
*/ if (reason == SCTP_RTXR_T3_RTX)
q->asoc->stream.si->generate_ftsn(q, q->asoc->ctsn_ack_point);
/* Flush the queues only on timeout, since fast_rtx is only * triggered during sack processing and the queue * will be flushed at the end.
*/ if (reason != SCTP_RTXR_FAST_RTX)
sctp_outq_flush(q, /* rtx_timeout */ 1, GFP_ATOMIC);
}
/* * Transmit DATA chunks on the retransmit queue. Upon return from * __sctp_outq_flush_rtx() the packet 'pkt' may contain chunks which * need to be transmitted by the caller. * We assume that pkt->transport has already been set. * * The return value is a normal kernel error return value.
*/ staticint __sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, int rtx_timeout, int *start_timer, gfp_t gfp)
{ struct sctp_transport *transport = pkt->transport; struct sctp_chunk *chunk, *chunk1; struct list_head *lqueue; enum sctp_xmit status; int error = 0; int timer = 0; int done = 0; int fast_rtx;
lqueue = &q->retransmit;
fast_rtx = q->fast_rtx;
/* This loop handles time-out retransmissions, fast retransmissions, * and retransmissions due to opening of whindow. * * RFC 2960 6.3.3 Handle T3-rtx Expiration * * E3) Determine how many of the earliest (i.e., lowest TSN) * outstanding DATA chunks for the address for which the * T3-rtx has expired will fit into a single packet, subject * to the MTU constraint for the path corresponding to the * destination transport address to which the retransmission * is being sent (this may be different from the address for * which the timer expires [see Section 6.4]). Call this value * K. Bundle and retransmit those K DATA chunks in a single * packet to the destination endpoint. * * [Just to be painfully clear, if we are retransmitting * because a timeout just happened, we should send only ONE * packet of retransmitted data.] * * For fast retransmissions we also send only ONE packet. However, * if we are just flushing the queue due to open window, we'll * try to send as much as possible.
*/
list_for_each_entry_safe(chunk, chunk1, lqueue, transmitted_list) { /* If the chunk is abandoned, move it to abandoned list. */ if (sctp_chunk_abandoned(chunk)) {
list_del_init(&chunk->transmitted_list);
sctp_insert_list(&q->abandoned,
&chunk->transmitted_list); continue;
}
/* Make sure that Gap Acked TSNs are not retransmitted. A * simple approach is just to move such TSNs out of the * way and into a 'transmitted' queue and skip to the * next chunk.
*/ if (chunk->tsn_gap_acked) {
list_move_tail(&chunk->transmitted_list,
&transport->transmitted); continue;
}
/* If we are doing fast retransmit, ignore non-fast_rtransmit * chunks
*/ if (fast_rtx && !chunk->fast_retransmit) continue;
redo: /* Attempt to append this chunk to the packet. */
status = sctp_packet_append_chunk(pkt, chunk);
switch (status) { case SCTP_XMIT_PMTU_FULL: if (!pkt->has_data && !pkt->has_cookie_echo) { /* If this packet did not contain DATA then * retransmission did not happen, so do it * again. We'll ignore the error here since * control chunks are already freed so there * is nothing we can do.
*/
sctp_packet_transmit(pkt, gfp); goto redo;
}
/* Send this packet. */
error = sctp_packet_transmit(pkt, gfp);
/* If we are retransmitting, we should only * send a single packet. * Otherwise, try appending this chunk again.
*/ if (rtx_timeout || fast_rtx)
done = 1; else goto redo;
/* Bundle next chunk in the next round. */ break;
case SCTP_XMIT_RWND_FULL: /* Send this packet. */
error = sctp_packet_transmit(pkt, gfp);
/* Stop sending DATA as there is no more room * at the receiver.
*/
done = 1; break;
case SCTP_XMIT_DELAY: /* Send this packet. */
error = sctp_packet_transmit(pkt, gfp);
/* Stop sending DATA because of nagle delay. */
done = 1; break;
default: /* The append was successful, so add this chunk to * the transmitted list.
*/
list_move_tail(&chunk->transmitted_list,
&transport->transmitted);
/* Mark the chunk as ineligible for fast retransmit * after it is retransmitted.
*/ if (chunk->fast_retransmit == SCTP_NEED_FRTX)
chunk->fast_retransmit = SCTP_DONT_FRTX;
q->asoc->stats.rtxchunks++; break;
}
/* Set the timer if there were no errors */ if (!error && !timer)
timer = 1;
if (done) break;
}
/* If we are here due to a retransmit timeout or a fast * retransmit and if there are any chunks left in the retransmit * queue that could not fit in the PMTU sized packet, they need * to be marked as ineligible for a subsequent fast retransmit.
*/ if (rtx_timeout || fast_rtx) {
list_for_each_entry(chunk1, lqueue, transmitted_list) { if (chunk1->fast_retransmit == SCTP_NEED_FRTX)
chunk1->fast_retransmit = SCTP_DONT_FRTX;
}
}
*start_timer = timer;
/* Clear fast retransmit hint */ if (fast_rtx)
q->fast_rtx = 0;
return error;
}
/* Cork the outqueue so queued chunks are really queued. */ void sctp_outq_uncork(struct sctp_outq *q, gfp_t gfp)
{ if (q->cork)
q->cork = 0;
/* Struct to hold the context during sctp outq flush */ struct sctp_flush_ctx { struct sctp_outq *q; /* Current transport being used. It's NOT the same as curr active one */ struct sctp_transport *transport; /* These transports have chunks to send. */ struct list_head transport_list; struct sctp_association *asoc; /* Packet on the current transport above */ struct sctp_packet *packet;
gfp_t gfp;
};
/* transport: current transport */ staticvoid sctp_outq_select_transport(struct sctp_flush_ctx *ctx, struct sctp_chunk *chunk)
{ struct sctp_transport *new_transport = chunk->transport;
if (!new_transport) { if (!sctp_chunk_is_data(chunk)) { /* If we have a prior transport pointer, see if * the destination address of the chunk * matches the destination address of the * current transport. If not a match, then * try to look up the transport with a given * destination address. We do this because * after processing ASCONFs, we may have new * transports created.
*/ if (ctx->transport && sctp_cmp_addr_exact(&chunk->dest,
&ctx->transport->ipaddr))
new_transport = ctx->transport; else
new_transport = sctp_assoc_lookup_paddr(ctx->asoc,
&chunk->dest);
}
/* if we still don't have a new transport, then * use the current active path.
*/ if (!new_transport)
new_transport = ctx->asoc->peer.active_path;
} else {
__u8 type;
switch (new_transport->state) { case SCTP_INACTIVE: case SCTP_UNCONFIRMED: case SCTP_PF: /* If the chunk is Heartbeat or Heartbeat Ack, * send it to chunk->transport, even if it's * inactive. * * 3.3.6 Heartbeat Acknowledgement: * ... * A HEARTBEAT ACK is always sent to the source IP * address of the IP datagram containing the * HEARTBEAT chunk to which this ack is responding. * ... * * ASCONF_ACKs also must be sent to the source.
*/
type = chunk->chunk_hdr->type; if (type != SCTP_CID_HEARTBEAT &&
type != SCTP_CID_HEARTBEAT_ACK &&
type != SCTP_CID_ASCONF_ACK)
new_transport = ctx->asoc->peer.active_path; break; default: break;
}
}
/* Are we switching transports? Take care of transport locks. */ if (new_transport != ctx->transport) {
ctx->transport = new_transport;
ctx->packet = &ctx->transport->packet;
if (list_empty(&ctx->transport->send_ready))
list_add_tail(&ctx->transport->send_ready,
&ctx->transport_list);
sctp_packet_config(ctx->packet,
ctx->asoc->peer.i.init_tag,
ctx->asoc->peer.ecn_capable); /* We've switched transports, so apply the * Burst limit to the new transport.
*/
sctp_transport_burst_limited(ctx->transport);
}
}
/* RFC 5061, 5.3 * F1) This means that until such time as the ASCONF * containing the add is acknowledged, the sender MUST * NOT use the new IP address as a source for ANY SCTP * packet except on carrying an ASCONF Chunk.
*/ if (ctx->asoc->src_out_of_asoc_ok &&
chunk->chunk_hdr->type != SCTP_CID_ASCONF) continue;
list_del_init(&chunk->list);
/* Pick the right transport to use. Should always be true for * the first chunk as we don't have a transport by then.
*/
sctp_outq_select_transport(ctx, chunk);
switch (chunk->chunk_hdr->type) { /* 6.10 Bundling * ... * An endpoint MUST NOT bundle INIT, INIT ACK or SHUTDOWN * COMPLETE with any other chunks. [Send them immediately.]
*/ case SCTP_CID_INIT: case SCTP_CID_INIT_ACK: case SCTP_CID_SHUTDOWN_COMPLETE:
error = sctp_packet_singleton(ctx->transport, chunk,
ctx->gfp); if (error < 0) {
ctx->asoc->base.sk->sk_err = -error; return;
}
ctx->asoc->stats.octrlchunks++; break;
case SCTP_CID_ABORT: if (sctp_test_T_bit(chunk))
ctx->packet->vtag = ctx->asoc->c.my_vtag;
fallthrough;
/* The following chunks are "response" chunks, i.e. * they are generated in response to something we * received. If we are sending these, then we can * send only 1 packet containing these chunks.
*/ case SCTP_CID_HEARTBEAT_ACK: case SCTP_CID_SHUTDOWN_ACK: case SCTP_CID_COOKIE_ACK: case SCTP_CID_COOKIE_ECHO: case SCTP_CID_ERROR: case SCTP_CID_ECN_CWR: case SCTP_CID_ASCONF_ACK:
one_packet = 1;
fallthrough;
case SCTP_CID_HEARTBEAT: if (chunk->pmtu_probe) {
error = sctp_packet_singleton(ctx->transport,
chunk, ctx->gfp); if (!error)
ctx->asoc->stats.octrlchunks++; break;
}
fallthrough; case SCTP_CID_SACK: case SCTP_CID_SHUTDOWN: case SCTP_CID_ECN_ECNE: case SCTP_CID_ASCONF: case SCTP_CID_FWD_TSN: case SCTP_CID_I_FWD_TSN: case SCTP_CID_RECONF:
status = sctp_packet_transmit_chunk(ctx->packet, chunk,
one_packet, ctx->gfp); if (status != SCTP_XMIT_OK) { /* put the chunk back */
list_add(&chunk->list, &ctx->q->control_chunk_list); break;
}
ctx->asoc->stats.octrlchunks++; /* PR-SCTP C5) If a FORWARD TSN is sent, the * sender MUST assure that at least one T3-rtx * timer is running.
*/ if (chunk->chunk_hdr->type == SCTP_CID_FWD_TSN ||
chunk->chunk_hdr->type == SCTP_CID_I_FWD_TSN) {
sctp_transport_reset_t3_rtx(ctx->transport);
ctx->transport->last_time_sent = jiffies;
}
if (chunk == ctx->asoc->strreset_chunk)
sctp_transport_reset_reconf_timer(ctx->transport);
break;
default: /* We built a chunk with an illegal type! */
BUG();
}
}
}
/* Returns false if new data shouldn't be sent */ staticbool sctp_outq_flush_rtx(struct sctp_flush_ctx *ctx, int rtx_timeout)
{ int error, start_timer = 0;
if (ctx->asoc->peer.retran_path->state == SCTP_UNCONFIRMED) returnfalse;
if (ctx->transport != ctx->asoc->peer.retran_path) { /* Switch transports & prepare the packet. */
ctx->transport = ctx->asoc->peer.retran_path;
ctx->packet = &ctx->transport->packet;
if (list_empty(&ctx->transport->send_ready))
list_add_tail(&ctx->transport->send_ready,
&ctx->transport_list);
/* Is it OK to send data chunks? */ switch (ctx->asoc->state) { case SCTP_STATE_COOKIE_ECHOED: /* Only allow bundling when this packet has a COOKIE-ECHO * chunk.
*/ if (!ctx->packet || !ctx->packet->has_cookie_echo) return;
fallthrough; case SCTP_STATE_ESTABLISHED: case SCTP_STATE_SHUTDOWN_PENDING: case SCTP_STATE_SHUTDOWN_RECEIVED: break;
default: /* Do nothing. */ return;
}
/* RFC 2960 6.1 Transmission of DATA Chunks * * C) When the time comes for the sender to transmit, * before sending new DATA chunks, the sender MUST * first transmit any outstanding DATA chunks which * are marked for retransmission (limited by the * current cwnd).
*/ if (!list_empty(&ctx->q->retransmit) &&
!sctp_outq_flush_rtx(ctx, rtx_timeout)) return;
/* Apply Max.Burst limitation to the current transport in * case it will be used for new data. We are going to * rest it before we return, but we want to apply the limit * to the currently queued data.
*/ if (ctx->transport)
sctp_transport_burst_limited(ctx->transport);
/* Add the chunk to the packet. */
status = sctp_packet_transmit_chunk(ctx->packet, chunk, 0,
ctx->gfp); if (status != SCTP_XMIT_OK) { /* We could not append this chunk, so put * the chunk back on the output queue.
*/
pr_debug("%s: could not transmit tsn:0x%x, status:%d\n",
__func__, ntohl(chunk->subh.data_hdr->tsn),
status);
sctp_outq_head_data(ctx->q, chunk); break;
}
/* The sender is in the SHUTDOWN-PENDING state, * The sender MAY set the I-bit in the DATA * chunk header.
*/ if (ctx->asoc->state == SCTP_STATE_SHUTDOWN_PENDING)
chunk->chunk_hdr->flags |= SCTP_DATA_SACK_IMM; if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
ctx->asoc->stats.ouodchunks++; else
ctx->asoc->stats.oodchunks++;
/* Only now it's safe to consider this * chunk as sent, sched-wise.
*/
sctp_sched_dequeue_done(ctx->q, chunk);
while ((ltransport = sctp_list_dequeue(&ctx->transport_list)) != NULL) {
t = list_entry(ltransport, struct sctp_transport, send_ready);
packet = &t->packet; if (!sctp_packet_empty(packet)) {
rcu_read_lock(); if (t->dst && __sk_dst_get(sk) != t->dst) {
dst_hold(t->dst);
sk_setup_caps(sk, t->dst);
}
rcu_read_unlock();
error = sctp_packet_transmit(packet, ctx->gfp); if (error < 0)
ctx->q->asoc->base.sk->sk_err = -error;
}
/* Clear the burst limited state, if any */
sctp_transport_burst_reset(t);
}
}
/* Try to flush an outqueue. * * Description: Send everything in q which we legally can, subject to * congestion limitations. * * Note: This function can be called from multiple contexts so appropriate * locking concerns must be made. Today we use the sock lock to protect * this function.
*/
/* 6.10 Bundling * ... * When bundling control chunks with DATA chunks, an * endpoint MUST place control chunks first in the outbound * SCTP packet. The transmitter MUST transmit DATA chunks * within a SCTP packet in increasing order of TSN. * ...
*/
sctp_outq_flush_ctrl(&ctx);
if (q->asoc->src_out_of_asoc_ok) goto sctp_flush_out;
sctp_outq_flush_data(&ctx, rtx_timeout);
sctp_flush_out:
sctp_outq_flush_transports(&ctx);
}
/* Update unack_data based on the incoming SACK chunk */ staticvoid sctp_sack_update_unack_data(struct sctp_association *assoc, struct sctp_sackhdr *sack)
{ union sctp_sack_variable *frags;
__u16 unack_data; int i;
frags = (union sctp_sack_variable *)(sack + 1); for (i = 0; i < ntohs(sack->num_gap_ack_blocks); i++) {
unack_data -= ((ntohs(frags[i].gab.end) -
ntohs(frags[i].gab.start) + 1));
}
assoc->unack_data = unack_data;
}
/* This is where we REALLY process a SACK. * * Process the SACK against the outqueue. Mostly, this just frees * things off the transmitted queue.
*/ int sctp_outq_sack(struct sctp_outq *q, struct sctp_chunk *chunk)
{ struct sctp_association *asoc = q->asoc; struct sctp_sackhdr *sack = chunk->subh.sack_hdr; struct sctp_transport *transport; struct sctp_chunk *tchunk = NULL; struct list_head *lchunk, *transport_list, *temp;
__u32 sack_ctsn, ctsn, tsn;
__u32 highest_tsn, highest_new_tsn;
__u32 sack_a_rwnd; unsignedint outstanding; struct sctp_transport *primary = asoc->peer.primary_path; int count_of_newacks = 0; int gap_ack_blocks;
u8 accum_moved = 0;
/* SCTP path tracepoint for congestion control debugging. */ if (trace_sctp_probe_path_enabled()) {
list_for_each_entry(transport, transport_list, transports)
trace_sctp_probe_path(transport, asoc);
}
sack_ctsn = ntohl(sack->cum_tsn_ack);
gap_ack_blocks = ntohs(sack->num_gap_ack_blocks);
asoc->stats.gapcnt += gap_ack_blocks; /* * SFR-CACC algorithm: * On receipt of a SACK the sender SHOULD execute the * following statements. * * 1) If the cumulative ack in the SACK passes next tsn_at_change * on the current primary, the CHANGEOVER_ACTIVE flag SHOULD be * cleared. The CYCLING_CHANGEOVER flag SHOULD also be cleared for * all destinations. * 2) If the SACK contains gap acks and the flag CHANGEOVER_ACTIVE * is set the receiver of the SACK MUST take the following actions: * * A) Initialize the cacc_saw_newack to 0 for all destination * addresses. * * Only bother if changeover_active is set. Otherwise, this is * totally suboptimal to do on every SACK.
*/ if (primary->cacc.changeover_active) {
u8 clear_cycling = 0;
if (clear_cycling || gap_ack_blocks) {
list_for_each_entry(transport, transport_list,
transports) { if (clear_cycling)
transport->cacc.cycling_changeover = 0; if (gap_ack_blocks)
transport->cacc.cacc_saw_newack = 0;
}
}
}
/* Get the highest TSN in the sack. */
highest_tsn = sack_ctsn; if (gap_ack_blocks) { union sctp_sack_variable *frags =
(union sctp_sack_variable *)(sack + 1);
if (TSN_lt(asoc->highest_sacked, highest_tsn))
asoc->highest_sacked = highest_tsn;
highest_new_tsn = sack_ctsn;
/* Run through the retransmit queue. Credit bytes received * and free those chunks that we can.
*/
sctp_check_transmitted(q, &q->retransmit, NULL, NULL, sack, &highest_new_tsn);
/* Run through the transmitted queue. * Credit bytes received and free those chunks which we can. * * This is a MASSIVE candidate for optimization.
*/
list_for_each_entry(transport, transport_list, transports) {
sctp_check_transmitted(q, &transport->transmitted,
transport, &chunk->source, sack,
&highest_new_tsn); /* * SFR-CACC algorithm: * C) Let count_of_newacks be the number of * destinations for which cacc_saw_newack is set.
*/ if (transport->cacc.cacc_saw_newack)
count_of_newacks++;
}
/* Move the Cumulative TSN Ack Point if appropriate. */ if (TSN_lt(asoc->ctsn_ack_point, sack_ctsn)) {
asoc->ctsn_ack_point = sack_ctsn;
accum_moved = 1;
}
if (gap_ack_blocks) {
if (asoc->fast_recovery && accum_moved)
highest_new_tsn = highest_tsn;
/* Update unack_data field in the assoc. */
sctp_sack_update_unack_data(asoc, sack);
ctsn = asoc->ctsn_ack_point;
/* Throw away stuff rotting on the sack queue. */
list_for_each_safe(lchunk, temp, &q->sacked) {
tchunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list);
tsn = ntohl(tchunk->subh.data_hdr->tsn); if (TSN_lte(tsn, ctsn)) {
list_del_init(&tchunk->transmitted_list); if (asoc->peer.prsctp_capable &&
SCTP_PR_PRIO_ENABLED(chunk->sinfo.sinfo_flags))
asoc->sent_cnt_removable--;
sctp_chunk_free(tchunk);
}
}
/* ii) Set rwnd equal to the newly received a_rwnd minus the * number of bytes still outstanding after processing the * Cumulative TSN Ack and the Gap Ack Blocks.
*/
/* Is the outqueue empty? * The queue is empty when we have not pending data, no in-flight data * and nothing pending retransmissions.
*/ int sctp_outq_is_empty(conststruct sctp_outq *q)
{ return q->out_qlen == 0 && q->outstanding_bytes == 0 &&
list_empty(&q->retransmit);
}
/* Go through a transport's transmitted list or the association's retransmit * list and move chunks that are acked by the Cumulative TSN Ack to q->sacked. * The retransmit list will not have an associated transport. * * I added coherent debug information output. --xguo * * Instead of printing 'sacked' or 'kept' for each TSN on the * transmitted_queue, we print a range: SACKED: TSN1-TSN2, TSN3, TSN4-TSN5. * KEPT TSN6-TSN7, etc.
*/ staticvoid sctp_check_transmitted(struct sctp_outq *q, struct list_head *transmitted_queue, struct sctp_transport *transport, union sctp_addr *saddr, struct sctp_sackhdr *sack,
__u32 *highest_new_tsn_in_sack)
{ struct list_head *lchunk; struct sctp_chunk *tchunk; struct list_head tlist;
__u32 tsn;
__u32 sack_ctsn;
__u32 rtt;
__u8 restart_timer = 0; int bytes_acked = 0; int migrate_bytes = 0; bool forward_progress = false;
sack_ctsn = ntohl(sack->cum_tsn_ack);
INIT_LIST_HEAD(&tlist);
/* The while loop will skip empty transmitted queues. */ while (NULL != (lchunk = sctp_list_dequeue(transmitted_queue))) {
tchunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list);
if (sctp_chunk_abandoned(tchunk)) { /* Move the chunk to abandoned list. */
sctp_insert_list(&q->abandoned, lchunk);
/* If this chunk has not been acked, stop * considering it as 'outstanding'.
*/ if (transmitted_queue != &q->retransmit &&
!tchunk->tsn_gap_acked) { if (tchunk->transport)
tchunk->transport->flight_size -=
sctp_data_size(tchunk);
q->outstanding_bytes -= sctp_data_size(tchunk);
} continue;
}
tsn = ntohl(tchunk->subh.data_hdr->tsn); if (sctp_acked(sack, tsn)) { /* If this queue is the retransmit queue, the * retransmit timer has already reclaimed * the outstanding bytes for this chunk, so only * count bytes associated with a transport.
*/ if (transport && !tchunk->tsn_gap_acked) { /* If this chunk is being used for RTT * measurement, calculate the RTT and update * the RTO using this value. * * 6.3.1 C5) Karn's algorithm: RTT measurements * MUST NOT be made using packets that were * retransmitted (and thus for which it is * ambiguous whether the reply was for the * first instance of the packet or a later * instance).
*/ if (!sctp_chunk_retransmitted(tchunk) &&
tchunk->rtt_in_progress) {
tchunk->rtt_in_progress = 0;
rtt = jiffies - tchunk->sent_at;
sctp_transport_update_rto(transport,
rtt);
}
if (TSN_lte(tsn, sack_ctsn)) { /* * SFR-CACC algorithm: * 2) If the SACK contains gap acks * and the flag CHANGEOVER_ACTIVE is * set the receiver of the SACK MUST * take the following action: * * B) For each TSN t being acked that * has not been acked in any SACK so * far, set cacc_saw_newack to 1 for * the destination that the TSN was * sent to.
*/ if (sack->num_gap_ack_blocks &&
q->asoc->peer.primary_path->cacc.
changeover_active)
transport->cacc.cacc_saw_newack
= 1;
}
}
/* If the chunk hasn't been marked as ACKED, * mark it and account bytes_acked if the * chunk had a valid transport (it will not * have a transport if ASCONF had deleted it * while DATA was outstanding).
*/ if (!tchunk->tsn_gap_acked) {
tchunk->tsn_gap_acked = 1; if (TSN_lt(*highest_new_tsn_in_sack, tsn))
*highest_new_tsn_in_sack = tsn;
bytes_acked += sctp_data_size(tchunk); if (!tchunk->transport)
migrate_bytes += sctp_data_size(tchunk);
forward_progress = true;
}
if (TSN_lte(tsn, sack_ctsn)) { /* RFC 2960 6.3.2 Retransmission Timer Rules * * R3) Whenever a SACK is received * that acknowledges the DATA chunk * with the earliest outstanding TSN * for that address, restart T3-rtx * timer for that address with its * current RTO.
*/
restart_timer = 1;
forward_progress = true;
list_add_tail(&tchunk->transmitted_list,
&q->sacked);
} else { /* RFC2960 7.2.4, sctpimpguide-05 2.8.2 * M2) Each time a SACK arrives reporting * 'Stray DATA chunk(s)' record the highest TSN * reported as newly acknowledged, call this * value 'HighestTSNinSack'. A newly * acknowledged DATA chunk is one not * previously acknowledged in a SACK. * * When the SCTP sender of data receives a SACK * chunk that acknowledges, for the first time, * the receipt of a DATA chunk, all the still * unacknowledged DATA chunks whose TSN is * older than that newly acknowledged DATA * chunk, are qualified as 'Stray DATA chunks'.
*/
list_add_tail(lchunk, &tlist);
}
} else { if (tchunk->tsn_gap_acked) {
pr_debug("%s: receiver reneged on data TSN:0x%x\n",
__func__, tsn);
tchunk->tsn_gap_acked = 0;
if (tchunk->transport)
bytes_acked -= sctp_data_size(tchunk);
/* RFC 2960 6.3.2 Retransmission Timer Rules * * R4) Whenever a SACK is received missing a * TSN that was previously acknowledged via a * Gap Ack Block, start T3-rtx for the * destination address to which the DATA * chunk was originally * transmitted if it is not already running.
*/
restart_timer = 1;
}
list_add_tail(lchunk, &tlist);
}
}
if (transport) { if (bytes_acked) { struct sctp_association *asoc = transport->asoc;
/* We may have counted DATA that was migrated * to this transport due to DEL-IP operation. * Subtract those bytes, since the were never * send on this transport and shouldn't be * credited to this transport.
*/
bytes_acked -= migrate_bytes;
/* 8.2. When an outstanding TSN is acknowledged, * the endpoint shall clear the error counter of * the destination transport address to which the * DATA chunk was last sent. * The association's overall error counter is * also cleared.
*/
transport->error_count = 0;
transport->asoc->overall_error_count = 0;
forward_progress = true;
/* * While in SHUTDOWN PENDING, we may have started * the T5 shutdown guard timer after reaching the * retransmission limit. Stop that timer as soon * as the receiver acknowledged any data.
*/ if (asoc->state == SCTP_STATE_SHUTDOWN_PENDING &&
timer_delete(&asoc->timers[SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD]))
sctp_association_put(asoc);
/* Mark the destination transport address as * active if it is not so marked.
*/ if ((transport->state == SCTP_INACTIVE ||
transport->state == SCTP_UNCONFIRMED) &&
sctp_cmp_addr_exact(&transport->ipaddr, saddr)) {
sctp_assoc_control_transport(
transport->asoc,
transport,
SCTP_TRANSPORT_UP,
SCTP_RECEIVED_SACK);
}
transport->flight_size -= bytes_acked; if (transport->flight_size == 0)
transport->partial_bytes_acked = 0;
q->outstanding_bytes -= bytes_acked + migrate_bytes;
} else { /* RFC 2960 6.1, sctpimpguide-06 2.15.2 * When a sender is doing zero window probing, it * should not timeout the association if it continues * to receive new packets from the receiver. The * reason is that the receiver MAY keep its window * closed for an indefinite time. * A sender is doing zero window probing when the * receiver's advertised window is zero, and there is * only one data chunk in flight to the receiver. * * Allow the association to timeout while in SHUTDOWN * PENDING or SHUTDOWN RECEIVED in case the receiver * stays in zero window mode forever.
*/ if (!q->asoc->peer.rwnd &&
!list_empty(&tlist) &&
(sack_ctsn+2 == q->asoc->next_tsn) &&
q->asoc->state < SCTP_STATE_SHUTDOWN_PENDING) {
pr_debug("%s: sack received for zero window " "probe:%u\n", __func__, sack_ctsn);
/* RFC 2960 6.3.2 Retransmission Timer Rules * * R2) Whenever all outstanding data sent to an address have * been acknowledged, turn off the T3-rtx timer of that * address.
*/ if (!transport->flight_size) { if (timer_delete(&transport->T3_rtx_timer))
sctp_transport_put(transport);
} elseif (restart_timer) { if (!mod_timer(&transport->T3_rtx_timer,
jiffies + transport->rto))
sctp_transport_hold(transport);
}
if (forward_progress) { if (transport->dst)
sctp_transport_dst_confirm(transport);
}
}
list_splice(&tlist, transmitted_queue);
}
/* Mark chunks as missing and consequently may get retransmitted. */ staticvoid sctp_mark_missing(struct sctp_outq *q, struct list_head *transmitted_queue, struct sctp_transport *transport,
__u32 highest_new_tsn_in_sack, int count_of_newacks)
{ struct sctp_chunk *chunk;
__u32 tsn; char do_fast_retransmit = 0; struct sctp_association *asoc = q->asoc; struct sctp_transport *primary = asoc->peer.primary_path;
/* RFC 2960 7.2.4, sctpimpguide-05 2.8.2 M3) Examine all * 'Unacknowledged TSN's', if the TSN number of an * 'Unacknowledged TSN' is smaller than the 'HighestTSNinSack' * value, increment the 'TSN.Missing.Report' count on that * chunk if it has NOT been fast retransmitted or marked for * fast retransmit already.
*/ if (chunk->fast_retransmit == SCTP_CAN_FRTX &&
!chunk->tsn_gap_acked &&
TSN_lt(tsn, highest_new_tsn_in_sack)) {
/* SFR-CACC may require us to skip marking * this chunk as missing.
*/ if (!transport || !sctp_cacc_skip(primary,
chunk->transport,
count_of_newacks, tsn)) {
chunk->tsn_missing_report++;
pr_debug("%s: tsn:0x%x missing counter:%d\n",
__func__, tsn, chunk->tsn_missing_report);
}
} /* * M4) If any DATA chunk is found to have a * 'TSN.Missing.Report' * value larger than or equal to 3, mark that chunk for * retransmission and start the fast retransmit procedure.
*/
/* Is the given TSN acked by this packet? */ staticint sctp_acked(struct sctp_sackhdr *sack, __u32 tsn)
{
__u32 ctsn = ntohl(sack->cum_tsn_ack); union sctp_sack_variable *frags;
__u16 tsn_offset, blocks; int i;
if (TSN_lte(tsn, ctsn)) goto pass;
/* 3.3.4 Selective Acknowledgment (SACK) (3): * * Gap Ack Blocks: * These fields contain the Gap Ack Blocks. They are repeated * for each Gap Ack Block up to the number of Gap Ack Blocks * defined in the Number of Gap Ack Blocks field. All DATA * chunks with TSNs greater than or equal to (Cumulative TSN * Ack + Gap Ack Block Start) and less than or equal to * (Cumulative TSN Ack + Gap Ack Block End) of each Gap Ack * Block are assumed to have been received correctly.
*/
frags = (union sctp_sack_variable *)(sack + 1);
blocks = ntohs(sack->num_gap_ack_blocks);
tsn_offset = tsn - ctsn; for (i = 0; i < blocks; ++i) { if (tsn_offset >= ntohs(frags[i].gab.start) &&
tsn_offset <= ntohs(frags[i].gab.end)) goto pass;
}
return 0;
pass: return 1;
}
staticinlineint sctp_get_skip_pos(struct sctp_fwdtsn_skip *skiplist, int nskips, __be16 stream)
{ int i;
for (i = 0; i < nskips; i++) { if (skiplist[i].stream == stream) return i;
} return i;
}
/* Create and add a fwdtsn chunk to the outq's control queue if needed. */ void sctp_generate_fwdtsn(struct sctp_outq *q, __u32 ctsn)
{ struct sctp_association *asoc = q->asoc; struct sctp_chunk *ftsn_chunk = NULL; struct sctp_fwdtsn_skip ftsn_skip_arr[10]; int nskips = 0; int skip_pos = 0;
__u32 tsn; struct sctp_chunk *chunk; struct list_head *lchunk, *temp;
if (!asoc->peer.prsctp_capable) return;
/* PR-SCTP C1) Let SackCumAck be the Cumulative TSN ACK carried in the * received SACK. * * If (Advanced.Peer.Ack.Point < SackCumAck), then update * Advanced.Peer.Ack.Point to be equal to SackCumAck.
*/ if (TSN_lt(asoc->adv_peer_ack_point, ctsn))
asoc->adv_peer_ack_point = ctsn;
/* PR-SCTP C2) Try to further advance the "Advanced.Peer.Ack.Point" * locally, that is, to move "Advanced.Peer.Ack.Point" up as long as * the chunk next in the out-queue space is marked as "abandoned" as * shown in the following example: * * Assuming that a SACK arrived with the Cumulative TSN ACK 102 * and the Advanced.Peer.Ack.Point is updated to this value: * * out-queue at the end of ==> out-queue after Adv.Ack.Point * normal SACK processing local advancement * ... ... * Adv.Ack.Pt-> 102 acked 102 acked * 103 abandoned 103 abandoned * 104 abandoned Adv.Ack.P-> 104 abandoned * 105 105 * 106 acked 106 acked * ... ... * * In this example, the data sender successfully advanced the * "Advanced.Peer.Ack.Point" from 102 to 104 locally.
*/
list_for_each_safe(lchunk, temp, &q->abandoned) {
chunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list);
tsn = ntohl(chunk->subh.data_hdr->tsn);
/* Remove any chunks in the abandoned queue that are acked by * the ctsn.
*/ if (TSN_lte(tsn, ctsn)) {
list_del_init(lchunk);
sctp_chunk_free(chunk);
} else { if (TSN_lte(tsn, asoc->adv_peer_ack_point+1)) {
asoc->adv_peer_ack_point = tsn; if (chunk->chunk_hdr->flags &
SCTP_DATA_UNORDERED) continue;
skip_pos = sctp_get_skip_pos(&ftsn_skip_arr[0],
nskips,
chunk->subh.data_hdr->stream);
ftsn_skip_arr[skip_pos].stream =
chunk->subh.data_hdr->stream;
ftsn_skip_arr[skip_pos].ssn =
chunk->subh.data_hdr->ssn; if (skip_pos == nskips)
nskips++; if (nskips == 10) break;
} else break;
}
}
/* PR-SCTP C3) If, after step C1 and C2, the "Advanced.Peer.Ack.Point" * is greater than the Cumulative TSN ACK carried in the received * SACK, the data sender MUST send the data receiver a FORWARD TSN * chunk containing the latest value of the * "Advanced.Peer.Ack.Point". * * C4) For each "abandoned" TSN the sender of the FORWARD TSN SHOULD * list each stream and sequence number in the forwarded TSN. This * information will enable the receiver to easily find any * stranded TSN's waiting on stream reorder queues. Each stream * SHOULD only be reported once; this means that if multiple * abandoned messages occur in the same stream then only the * highest abandoned stream sequence number is reported. If the * total size of the FORWARD TSN does NOT fit in a single MTU then * the sender of the FORWARD TSN SHOULD lower the * Advanced.Peer.Ack.Point to the last TSN that will fit in a * single MTU.
*/ if (asoc->adv_peer_ack_point > ctsn)
ftsn_chunk = sctp_make_fwdtsn(asoc, asoc->adv_peer_ack_point,
nskips, &ftsn_skip_arr[0]);
if (ftsn_chunk) {
list_add_tail(&ftsn_chunk->list, &q->control_chunk_list);
SCTP_INC_STATS(asoc->base.net, SCTP_MIB_OUTCTRLCHUNKS);
}
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.22 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.