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;
/* 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 *andtheabandonedlistareinascendingorder.
*/ 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, *stopconsideringit'outstanding'.Ourpeer *willmostlikelyneverseeitsinceitwill *notberetransmitted
*/ 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,onlythechunksthatarenotyetackedshould *beaddedtotheretransmitqueue.
*/ 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)AnytimeaDATAchunkismarkedfor *retransmission(viaeitherT3-rtxtimerexpiration *(Section6.3.3)orviafastretransmit *(Section7.2.4)),addthedatasizeofthose *chunkstotherwnd.
*/
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);
/* If a chunk that is being used for RTT measurement *hastoberetransmitted,wecannotusethischunk *anymoreforRTTmeasurements.Resetrto_pendingso *thatanewRTTmeasurementisstartedwhenanew *datachunkissent.
*/ if (chunk->rtt_in_progress) {
chunk->rtt_in_progress = 0;
transport->rto_pending = 0;
}
/* Move the chunk to the retransmit queue. The chunks *ontheretransmitqueuearealwayskeptinorder.
*/
list_del_init(lchunk);
sctp_insert_list(&q->retransmit, lchunk);
}
}
/* Mark all the eligible packets on a transport for retransmission and force *onepacketout.
*/ 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 *thecurrentretranpath.
*/ 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, *thesenderSHOULDtrytoadvancethe"Advanced.Peer.Ack.Point"by *followingtheproceduresoutlinedinC1-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 *triggeredduringsackprocessingandthequeue *willbeflushedattheend.
*/ if (reason != SCTP_RTXR_FAST_RTX)
sctp_outq_flush(q, /* rtx_timeout */ 1, GFP_ATOMIC);
}
/* *TransmitDATAchunksontheretransmitqueue.Uponreturnfrom *__sctp_outq_flush_rtx()thepacket'pkt'maycontainchunkswhich *needtobetransmittedbythecaller. *Weassumethatpkt->transporthasalreadybeenset. * *Thereturnvalueisanormalkernelerrorreturnvalue.
*/ 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, *andretransmissionsduetoopeningofwhindow. * *RFC29606.3.3HandleT3-rtxExpiration * *E3)Determinehowmanyoftheearliest(i.e.,lowestTSN) *outstandingDATAchunksfortheaddressforwhichthe *T3-rtxhasexpiredwillfitintoasinglepacket,subject *totheMTUconstraintforthepathcorrespondingtothe *destinationtransportaddresstowhichtheretransmission *isbeingsent(thismaybedifferentfromtheaddressfor *whichthetimerexpires[seeSection6.4]).Callthisvalue *K.BundleandretransmitthoseKDATAchunksinasingle *packettothedestinationendpoint. * *[Justtobepainfullyclear,ifweareretransmitting *becauseatimeoutjusthappened,weshouldsendonlyONE *packetofretransmitteddata.] * *ForfastretransmissionswealsosendonlyONEpacket.However, *ifwearejustflushingthequeueduetoopenwindow,we'll *trytosendasmuchaspossible.
*/
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 *simpleapproachisjusttomovesuchTSNsoutofthe *wayandintoa'transmitted'queueandskiptothe *nextchunk.
*/ 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 *retransmissiondidnothappen,sodoit *again.We'llignoretheerrorheresince *controlchunksarealreadyfreedsothere *isnothingwecando.
*/
sctp_packet_transmit(pkt, gfp); goto redo;
}
/* Send this packet. */
error = sctp_packet_transmit(pkt, gfp);
/* If we are retransmitting, we should only *sendasinglepacket. *Otherwise,tryappendingthischunkagain.
*/ 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 *atthereceiver.
*/
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 *thetransmittedlist.
*/
list_move_tail(&chunk->transmitted_list,
&transport->transmitted);
/* Mark the chunk as ineligible for fast retransmit *afteritisretransmitted.
*/ 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 *retransmitandifthereareanychunksleftintheretransmit *queuethatcouldnotfitinthePMTUsizedpacket,theyneed *tobemarkedasineligibleforasubsequentfastretransmit.
*/ 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 *thedestinationaddressofthechunk *matchesthedestinationaddressofthe *currenttransport.Ifnotamatch,then *trytolookupthetransportwithagiven *destinationaddress.Wedothisbecause *afterprocessingASCONFs,wemayhavenew *transportscreated.
*/ 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 *usethecurrentactivepath.
*/ 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, *sendittochunk->transport,evenifit's *inactive. * *3.3.6HeartbeatAcknowledgement: *... *AHEARTBEATACKisalwayssenttothesourceIP *addressoftheIPdatagramcontainingthe *HEARTBEATchunktowhichthisackisresponding. *... * *ASCONF_ACKsalsomustbesenttothesource.
*/
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 *Burstlimittothenewtransport.
*/
sctp_transport_burst_limited(ctx->transport);
}
}
/* Pick the right transport to use. Should always be true for *thefirstchunkaswedon'thaveatransportbythen.
*/
sctp_outq_select_transport(ctx, chunk);
switch (chunk->chunk_hdr->type) { /* 6.10 Bundling *... *AnendpointMUSTNOTbundleINIT,INITACKorSHUTDOWN *COMPLETEwithanyotherchunks.[Sendthemimmediately.]
*/ 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. *theyaregeneratedinresponsetosomethingwe *received.Ifwearesendingthese,thenwecan *sendonly1packetcontainingthesechunks.
*/ 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 *senderMUSTassurethatatleastoneT3-rtx *timerisrunning.
*/ 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)Whenthetimecomesforthesendertotransmit, *beforesendingnewDATAchunks,thesenderMUST *firsttransmitanyoutstandingDATAchunkswhich *aremarkedforretransmission(limitedbythe *currentcwnd).
*/ if (!list_empty(&ctx->q->retransmit) &&
!sctp_outq_flush_rtx(ctx, rtx_timeout)) return;
/* Apply Max.Burst limitation to the current transport in *caseitwillbeusedfornewdata.Wearegoingto *restitbeforewereturn,butwewanttoapplythelimit *tothecurrentlyqueueddata.
*/ 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 *thechunkbackontheoutputqueue.
*/
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, *ThesenderMAYsettheI-bitintheDATA *chunkheader.
*/ 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 *chunkassent,sched-wise.
*/
sctp_sched_dequeue_done(ctx->q, chunk);
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;
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 *andfreethosechunksthatwecan.
*/
sctp_check_transmitted(q, &q->retransmit, NULL, NULL, sack, &highest_new_tsn);
/* Run through the transmitted queue. *Creditbytesreceivedandfreethosechunkswhichwecan. * *ThisisaMASSIVEcandidateforoptimization.
*/
list_for_each_entry(transport, transport_list, transports) {
sctp_check_transmitted(q, &transport->transmitted,
transport, &chunk->source, sack,
&highest_new_tsn); /* *SFR-CACCalgorithm: *C)Letcount_of_newacksbethenumberof *destinationsforwhichcacc_saw_newackisset.
*/ 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;
/* Go through a transport's transmitted list or the association's retransmit *listandmovechunksthatareackedbytheCumulativeTSNAcktoq->sacked. *Theretransmitlistwillnothaveanassociatedtransport. * *Iaddedcoherentdebuginformationoutput.--xguo * *Insteadofprinting'sacked'or'kept'foreachTSNonthe *transmitted_queue,weprintarange:SACKED:TSN1-TSN2,TSN3,TSN4-TSN5. *KEPTTSN6-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 *consideringitas'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 *retransmittimerhasalreadyreclaimed *theoutstandingbytesforthischunk,soonly *countbytesassociatedwithatransport.
*/ if (transport && !tchunk->tsn_gap_acked) { /* If this chunk is being used for RTT *measurement,calculatetheRTTandupdate *theRTOusingthisvalue. * *6.3.1C5)Karn'salgorithm:RTTmeasurements *MUSTNOTbemadeusingpacketsthatwere *retransmitted(andthusforwhichitis *ambiguouswhetherthereplywasforthe *firstinstanceofthepacketoralater *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 (transport) { if (bytes_acked) { struct sctp_association *asoc = transport->asoc;
/* We may have counted DATA that was migrated *tothistransportduetoDEL-IPoperation. *Subtractthosebytes,sincethewerenever *sendonthistransportandshouldn'tbe *creditedtothistransport.
*/
bytes_acked -= migrate_bytes;
/* 8.2. When an outstanding TSN is acknowledged, *theendpointshallcleartheerrorcounterof *thedestinationtransportaddresstowhichthe *DATAchunkwaslastsent. *Theassociation'soverallerrorcounteris *alsocleared.
*/
transport->error_count = 0;
transport->asoc->overall_error_count = 0;
forward_progress = true;
/* SFR-CACC may require us to skip marking *thischunkasmissing.
*/ if (!transport || !sctp_cacc_skip(primary,
chunk->transport,
count_of_newacks, tsn)) {
chunk->tsn_missing_report++;
/* 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;
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;
}
return0;
pass: return1;
}
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 *receivedSACK. * *If(Advanced.Peer.Ack.Point<SackCumAck),thenupdate *Advanced.Peer.Ack.PointtobeequaltoSackCumAck.
*/ if (TSN_lt(asoc->adv_peer_ack_point, ctsn))
asoc->adv_peer_ack_point = ctsn;
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.