KASSERT(asoc->cnt_on_reasm_queue > 0 || asoc->size_on_reasm_queue == 0,
("size_on_reasm_queue is %u", asoc->size_on_reasm_queue));
KASSERT(asoc->cnt_on_all_streams > 0 || asoc->size_on_all_streams == 0,
("size_on_all_streams is %u", asoc->size_on_all_streams)); if (stcb->asoc.sb_cc == 0 &&
asoc->cnt_on_reasm_queue == 0 &&
asoc->cnt_on_all_streams == 0) { /* Full rwnd granted */
calc = max(SCTP_SB_LIMIT_RCV(stcb->sctp_socket), SCTP_MINIMAL_RWND); return (calc);
} /* get actual space */
calc = (uint32_t) sctp_sbspace(&stcb->asoc, &stcb->sctp_socket->so_rcv); /* *takeoutwhathasNOTbeenputonsocketqueueandweyethold *forputtingup.
*/
calc = sctp_sbspace_sub(calc, (uint32_t)(asoc->size_on_reasm_queue +
asoc->cnt_on_reasm_queue * MSIZE));
calc = sctp_sbspace_sub(calc, (uint32_t)(asoc->size_on_all_streams +
asoc->cnt_on_all_streams * MSIZE)); if (calc == 0) { /* out of space */ return (calc);
}
/* what is the overhead of all these rwnd's */
calc = sctp_sbspace_sub(calc, stcb->asoc.my_rwnd_control_len); /* If the window gets too small due to ctrl-stuff, reduce it *to1,evenitis0.SWSengaged
*/ if (calc < stcb->asoc.my_rwnd_control_len) {
calc = 1;
} return (calc);
}
if (sctp_is_feature_off(inp, SCTP_PCB_FLAGS_RECVDATAIOEVNT) &&
sctp_is_feature_off(inp, SCTP_PCB_FLAGS_RECVRCVINFO) &&
sctp_is_feature_off(inp, SCTP_PCB_FLAGS_RECVNXTINFO)) { /* user does not want any ancillary data */ return (NULL);
}
len = 0; if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_RECVRCVINFO)) {
len += CMSG_SPACE(sizeof(struct sctp_rcvinfo));
}
seinfo = (struct sctp_extrcvinfo *)sinfo; if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_RECVNXTINFO) &&
(seinfo->serinfo_next_flags & SCTP_NEXT_MSG_AVAIL)) {
provide_nxt = 1;
len += CMSG_SPACE(sizeof(struct sctp_nxtinfo));
} else {
provide_nxt = 0;
} if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_RECVDATAIOEVNT)) { if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_EXT_RCVINFO)) {
use_extended = 1;
len += CMSG_SPACE(sizeof(struct sctp_extrcvinfo));
} else {
use_extended = 0;
len += CMSG_SPACE(sizeof(struct sctp_sndrcvinfo));
}
} else {
use_extended = 0;
}
ret = sctp_get_mbuf_for_msg(len, 0, M_NOWAIT, 1, MT_DATA); if (ret == NULL) { /* No space */ return (ret);
}
SCTP_BUF_LEN(ret) = 0;
staticint
sctp_handle_old_unordered_data(struct sctp_tcb *stcb, struct sctp_association *asoc, struct sctp_stream_in *strm, struct sctp_queued_to_read *control,
uint32_t pd_point, int inp_read_lock_held)
{ /* Special handling for the old un-ordered data chunk. *Allthechunks/TSN'sgotomid0.So *wehavetodotheoldstylewatchingtosee *ifwehaveitall.Ifyoureturnone,noother *controlentriesontheun-orderedqueuewill *belookedat.Intheorythereshouldbenoothers *entriesinreality,unlesstheguyissendingboth *unorderedNDATAandunorderedDATA...
*/ struct sctp_tmit_chunk *chk, *lchk, *tchk;
uint32_t fsn; struct sctp_queued_to_read *nc; int cnt_added;
if (control->first_frag_seen == 0) { /* Nothing we can do, we have not seen the first piece yet */ return (1);
} /* Collapse any we can */
cnt_added = 0;
restart:
fsn = control->fsn_included + 1; /* Now what can we add? */
TAILQ_FOREACH_SAFE(chk, &control->reasm, sctp_next, lchk) { if (chk->rec.data.fsn == fsn) { /* Ok lets add it */
sctp_alloc_a_readq(stcb, nc); if (nc == NULL) { break;
}
memset(nc, 0, sizeof(struct sctp_queued_to_read));
TAILQ_REMOVE(&control->reasm, chk, sctp_next);
sctp_add_chk_to_control(control, strm, stcb, asoc, chk, inp_read_lock_held);
fsn++;
cnt_added++;
chk = NULL; if (control->end_added) { /* We are done */ if (!TAILQ_EMPTY(&control->reasm)) { /* *Okwehavetomoveanythinglefton *thecontrolqueuetoanewcontrol.
*/
sctp_build_readq_entry_from_ctl(nc, control);
tchk = TAILQ_FIRST(&control->reasm); if (tchk->rec.data.rcv_flags & SCTP_DATA_FIRST_FRAG) {
TAILQ_REMOVE(&control->reasm, tchk, sctp_next); if (asoc->size_on_reasm_queue >= tchk->send_size) {
asoc->size_on_reasm_queue -= tchk->send_size;
} else { #ifdef INVARIANTS
panic("size_on_reasm_queue = %u smaller than chunk length %u", asoc->size_on_reasm_queue, tchk->send_size); #else
asoc->size_on_reasm_queue = 0; #endif
}
sctp_ucount_decr(asoc->cnt_on_reasm_queue);
nc->first_frag_seen = 1;
nc->fsn_included = tchk->rec.data.fsn;
nc->data = tchk->data;
nc->sinfo_ppid = tchk->rec.data.ppid;
nc->sinfo_tsn = tchk->rec.data.tsn;
sctp_mark_non_revokable(asoc, tchk->rec.data.tsn);
tchk->data = NULL;
sctp_free_a_chunk(stcb, tchk, SCTP_SO_NOT_LOCKED);
sctp_setup_tail_pointer(nc);
tchk = TAILQ_FIRST(&control->reasm);
} /* Spin the rest onto the queue */ while (tchk) {
TAILQ_REMOVE(&control->reasm, tchk, sctp_next);
TAILQ_INSERT_TAIL(&nc->reasm, tchk, sctp_next);
tchk = TAILQ_FIRST(&control->reasm);
} /* Now lets add it to the queue after removing control */
TAILQ_INSERT_TAIL(&strm->uno_inqueue, nc, next_instrm);
nc->on_strm_q = SCTP_ON_UNORDERED; if (control->on_strm_q) {
TAILQ_REMOVE(&strm->uno_inqueue, control, next_instrm);
control->on_strm_q = 0;
}
} if (control->pdapi_started) {
strm->pd_api_started = 0;
control->pdapi_started = 0;
} if (control->on_strm_q) {
TAILQ_REMOVE(&strm->uno_inqueue, control, next_instrm);
control->on_strm_q = 0;
SCTP_STAT_INCR_COUNTER64(sctps_reasmusrmsgs);
} if (control->on_read_q == 0) {
sctp_add_to_readq(stcb->sctp_ep, stcb, control,
&stcb->sctp_socket->so_rcv, control->end_added,
inp_read_lock_held, SCTP_SO_NOT_LOCKED); #ifdefined(__Userspace__)
} else {
sctp_invoke_recv_callback(stcb->sctp_ep, stcb, control, inp_read_lock_held); #endif
}
sctp_wakeup_the_read_socket(stcb->sctp_ep, stcb, SCTP_SO_NOT_LOCKED); if ((nc->first_frag_seen) && !TAILQ_EMPTY(&nc->reasm)) { /* Switch to the new guy and continue */
control = nc; goto restart;
} else { if (nc->on_strm_q == 0) {
sctp_free_a_readq(stcb, nc);
}
} return (1);
} else {
sctp_free_a_readq(stcb, nc);
}
} else { /* Can't add more */ break;
}
} if (cnt_added && strm->pd_api_started) { #ifdefined(__Userspace__)
sctp_invoke_recv_callback(stcb->sctp_ep, stcb, control, inp_read_lock_held); #endif
sctp_wakeup_the_read_socket(stcb->sctp_ep, stcb, SCTP_SO_NOT_LOCKED);
} if ((control->length > pd_point) && (strm->pd_api_started == 0)) {
strm->pd_api_started = 1;
control->pdapi_started = 1;
sctp_add_to_readq(stcb->sctp_ep, stcb, control,
&stcb->sctp_socket->so_rcv, control->end_added,
inp_read_lock_held, SCTP_SO_NOT_LOCKED);
sctp_wakeup_the_read_socket(stcb->sctp_ep, stcb, SCTP_SO_NOT_LOCKED); return (0);
} else { return (1);
}
}
staticvoid
sctp_inject_old_unordered_data(struct sctp_tcb *stcb, struct sctp_association *asoc, struct sctp_queued_to_read *control, struct sctp_tmit_chunk *chk, int *abort_flag)
{ struct sctp_tmit_chunk *at; int inserted; /* *Hereweneedtoplacethechunkintothecontrolstructure *sortedinthecorrectorder.
*/ if (chk->rec.data.rcv_flags & SCTP_DATA_FIRST_FRAG) { /* Its the very first one. */
SCTPDBG(SCTP_DEBUG_XXX, "chunk is a first fsn: %u becomes fsn_included\n",
chk->rec.data.fsn);
at = TAILQ_FIRST(&control->reasm); if (at && SCTP_TSN_GT(chk->rec.data.fsn, at->rec.data.fsn)) { /* *Thefirstchunkinthereassemblyis *asmallerTSNthanthisone,eventhough *thishasafirst,itmustbefromasubsequent *msg.
*/ goto place_chunk;
} if (control->first_frag_seen) { /* *Inoldun-orderedwecanreassemblyon *onecontrolmultiplemessages.Aslong *asthenextFIRSTisgreaterthentheold *first(TSNi.e.FSNwise)
*/ struct mbuf *tdata;
uint32_t tmp;
if (SCTP_TSN_GT(chk->rec.data.fsn, control->fsn_included)) { /* Easy way the start of a new guy beyond the lowest */ goto place_chunk;
} if ((chk->rec.data.fsn == control->fsn_included) ||
(control->pdapi_started)) { /* *Okthisshouldnothappen,ifitdoes *westartedthepd-apionthehigherTSN(since *theequalspartisaTSNfailureitmustbethat). * *WearecompletelyhosedinthatcasesinceIhave *nowaytorecover.Thisreallywillonlyhappen *ifwecangetmoreTSN'shigherbeforethepd-api-point.
*/
sctp_abort_in_reasm(stcb, control, chk,
abort_flag,
SCTP_FROM_SCTP_INDATA + SCTP_LOC_4);
strm = &asoc->strmin[control->sinfo_stream]; /* *Foroldun-ordereddatachunks.
*/ if ((control->sinfo_flags >> 8) & SCTP_DATA_UNORDERED) {
unordered = 1;
} else {
unordered = 0;
} /* Must be added to the stream-in queue */ if (created_control) { if ((unordered == 0) || (asoc->idata_supported)) {
sctp_ucount_incr(asoc->cnt_on_all_streams);
} if (sctp_place_control_in_stream(strm, asoc, control)) { /* Duplicate SSN? */
sctp_abort_in_reasm(stcb, control, chk,
abort_flag,
SCTP_FROM_SCTP_INDATA + SCTP_LOC_6);
sctp_clean_up_control(stcb, control); return;
} if ((tsn == (asoc->cumulative_tsn + 1) && (asoc->idata_supported == 0))) { /* Ok we created this control and now *letsvalidatethatitslegali.e.there *isaBbitset,ifnotandwehave *uptothecum-ackthenitsinvalid.
*/ if ((chk->rec.data.rcv_flags & SCTP_DATA_FIRST_FRAG) == 0) {
sctp_abort_in_reasm(stcb, control, chk,
abort_flag,
SCTP_FROM_SCTP_INDATA + SCTP_LOC_7); return;
}
}
} if ((asoc->idata_supported == 0) && (unordered == 1)) {
sctp_inject_old_unordered_data(stcb, asoc, control, chk, abort_flag); return;
} /* *Okwemustqueuethechunkintothereasemblyportion: *oifitsthefirstitgoestothecontrolmbuf. *oifitsnotfirstbutthenextinsequenceitgoestothecontrol, *andeachsucceedingoneinorderalsogoes. *oifitsnotinorderweplaceitonthelistinitsplace.
*/ if (chk->rec.data.rcv_flags & SCTP_DATA_FIRST_FRAG) { /* Its the very first one. */
SCTPDBG(SCTP_DEBUG_XXX, "chunk is a first fsn: %u becomes fsn_included\n",
chk->rec.data.fsn); if (control->first_frag_seen) { /* *Erroronsenderspart,theyeither *sentustwodatachunkswithFIRST, *ortheysenttwoun-orderedchunksthat *werefragmentedatthesametimeinthesamestream.
*/
sctp_abort_in_reasm(stcb, control, chk,
abort_flag,
SCTP_FROM_SCTP_INDATA + SCTP_LOC_8); return;
}
control->first_frag_seen = 1;
control->sinfo_ppid = chk->rec.data.ppid;
control->sinfo_tsn = chk->rec.data.tsn;
control->fsn_included = chk->rec.data.fsn;
control->data = chk->data;
sctp_mark_non_revokable(asoc, chk->rec.data.tsn);
chk->data = NULL;
sctp_free_a_chunk(stcb, chk, SCTP_SO_NOT_LOCKED);
sctp_setup_tail_pointer(control);
asoc->size_on_all_streams += control->length;
} else { /* Place the chunk in our list */ int inserted=0; if (control->last_frag_seen == 0) { /* Still willing to raise highest FSN seen */ if (SCTP_TSN_GT(chk->rec.data.fsn, control->top_fsn)) {
SCTPDBG(SCTP_DEBUG_XXX, "We have a new top_fsn: %u\n",
chk->rec.data.fsn);
control->top_fsn = chk->rec.data.fsn;
} if (chk->rec.data.rcv_flags & SCTP_DATA_LAST_FRAG) {
SCTPDBG(SCTP_DEBUG_XXX, "The last fsn is now in place fsn: %u\n",
chk->rec.data.fsn);
control->last_frag_seen = 1; if (SCTP_TSN_GT(control->top_fsn, chk->rec.data.fsn)) {
SCTPDBG(SCTP_DEBUG_XXX, "New fsn: %u is not at top_fsn: %u -- abort\n",
chk->rec.data.fsn,
control->top_fsn);
sctp_abort_in_reasm(stcb, control, chk,
abort_flag,
SCTP_FROM_SCTP_INDATA + SCTP_LOC_9); return;
}
} if (asoc->idata_supported || control->first_frag_seen) { /* *ForIDATAwealwayschecksinceweknowthat *thefirstfragmentis0.ForoldDATAwehave *toreceivethefirstbeforeweknowthefirstFSN *(whichistheTSN).
*/ if (SCTP_TSN_GE(control->fsn_included, chk->rec.data.fsn)) { /* We have already delivered up to this so its a dup */
sctp_abort_in_reasm(stcb, control, chk,
abort_flag,
SCTP_FROM_SCTP_INDATA + SCTP_LOC_10); return;
}
}
} else { if (chk->rec.data.rcv_flags & SCTP_DATA_LAST_FRAG) { /* Second last? huh? */
SCTPDBG(SCTP_DEBUG_XXX, "Duplicate last fsn: %u (top: %u) -- abort\n",
chk->rec.data.fsn, control->top_fsn);
sctp_abort_in_reasm(stcb, control,
chk, abort_flag,
SCTP_FROM_SCTP_INDATA + SCTP_LOC_11); return;
} if (asoc->idata_supported || control->first_frag_seen) { /* *ForIDATAwealwayschecksinceweknowthat *thefirstfragmentis0.ForoldDATAwehave *toreceivethefirstbeforeweknowthefirstFSN *(whichistheTSN).
*/
if (SCTP_TSN_GE(control->fsn_included, chk->rec.data.fsn)) { /* We have already delivered up to this so its a dup */
SCTPDBG(SCTP_DEBUG_XXX, "New fsn: %u is already seen in included_fsn: %u -- abort\n",
chk->rec.data.fsn, control->fsn_included);
sctp_abort_in_reasm(stcb, control, chk,
abort_flag,
SCTP_FROM_SCTP_INDATA + SCTP_LOC_12); return;
}
} /* validate not beyond top FSN if we have seen last one */ if (SCTP_TSN_GT(chk->rec.data.fsn, control->top_fsn)) {
SCTPDBG(SCTP_DEBUG_XXX, "New fsn: %u is beyond or at top_fsn: %u -- abort\n",
chk->rec.data.fsn,
control->top_fsn);
sctp_abort_in_reasm(stcb, control, chk,
abort_flag,
SCTP_FROM_SCTP_INDATA + SCTP_LOC_13); return;
}
} /* *Ifwereachhere,weneedtoplacethe *newchunkinthereassemblyforthis *control.
*/
SCTPDBG(SCTP_DEBUG_XXX, "chunk is a not first fsn: %u needs to be inserted\n",
chk->rec.data.fsn);
TAILQ_FOREACH(at, &control->reasm, sctp_next) { if (SCTP_TSN_GT(at->rec.data.fsn, chk->rec.data.fsn)) { if (chk->rec.data.rcv_flags & SCTP_DATA_LAST_FRAG) { /* Last not at the end? huh? */
SCTPDBG(SCTP_DEBUG_XXX, "Last fragment not last in list: -- abort\n");
sctp_abort_in_reasm(stcb, control,
chk, abort_flag,
SCTP_FROM_SCTP_INDATA + SCTP_LOC_14); return;
} /* *Thisoneinqueueisbiggerthanthenewone,insert *thenewonebeforeat.
*/
SCTPDBG(SCTP_DEBUG_XXX, "Insert it before fsn: %u\n",
at->rec.data.fsn);
asoc->size_on_reasm_queue += chk->send_size;
sctp_ucount_incr(asoc->cnt_on_reasm_queue);
TAILQ_INSERT_BEFORE(at, chk, sctp_next);
inserted = 1; break;
} elseif (at->rec.data.fsn == chk->rec.data.fsn) { /* Gak, He sent me a duplicate str seq number */ /* *foobar,IguessIwilljustfreethisnewguy, *shouldweaborttoo?FIXMEMAYBE?OritCOULDbe *thattheSSN'shavewrapped.MaybeIshould *comparetoTSNsomehow...sighfornowjustblow *awaythechunk!
*/
SCTPDBG(SCTP_DEBUG_XXX, "Duplicate to fsn: %u -- abort\n",
at->rec.data.fsn);
sctp_abort_in_reasm(stcb, control,
chk, abort_flag,
SCTP_FROM_SCTP_INDATA + SCTP_LOC_15); return;
}
} if (inserted == 0) { /* Goes on the end */
SCTPDBG(SCTP_DEBUG_XXX, "Inserting at tail of list fsn: %u\n",
chk->rec.data.fsn);
asoc->size_on_reasm_queue += chk->send_size;
sctp_ucount_incr(asoc->cnt_on_reasm_queue);
TAILQ_INSERT_TAIL(&control->reasm, chk, sctp_next);
}
} /* *Okletsseeifwecansuckanyupintothecontrol *structurethatareinseqifitmakessense.
*/
do_wakeup = 0; /* *Ifthefirstfragmenthasnotbeen *seenthereisnosenseinlooking.
*/ if (control->first_frag_seen) {
next_fsn = control->fsn_included + 1;
TAILQ_FOREACH_SAFE(at, &control->reasm, sctp_next, nat) { if (at->rec.data.fsn == next_fsn) { /* We can add this one now to the control */
SCTPDBG(SCTP_DEBUG_XXX, "Adding more to control: %p at: %p fsn: %u next_fsn: %u included: %u\n",
control, at,
at->rec.data.fsn,
next_fsn, control->fsn_included);
TAILQ_REMOVE(&control->reasm, at, sctp_next);
lenadded = sctp_add_chk_to_control(control, strm, stcb, asoc, at, SCTP_READ_LOCK_NOT_HELD); if (control->on_read_q) {
do_wakeup = 1;
} else { /* *Weonlyaddtothesize-on-all-streams *ifitsnotonthereadq.Thereadq *flagwillcauseasballocsoitsaccounted *forthere.
*/
asoc->size_on_all_streams += lenadded;
}
next_fsn++; if (control->end_added && control->pdapi_started) { if (strm->pd_api_started) {
strm->pd_api_started = 0;
control->pdapi_started = 0;
} if (control->on_read_q == 0) {
sctp_add_to_readq(stcb->sctp_ep, stcb,
control,
&stcb->sctp_socket->so_rcv, control->end_added,
SCTP_READ_LOCK_NOT_HELD, SCTP_SO_NOT_LOCKED);
} break;
}
} else { break;
}
}
} if (do_wakeup) { #ifdefined(__Userspace__)
sctp_invoke_recv_callback(stcb->sctp_ep, stcb, control, SCTP_READ_LOCK_NOT_HELD); #endif /* Need to wakeup the reader */
sctp_wakeup_the_read_socket(stcb->sctp_ep, stcb, SCTP_SO_NOT_LOCKED);
}
}
staticstruct sctp_queued_to_read *
sctp_find_reasm_entry(struct sctp_stream_in *strm, uint32_t mid, int ordered, int idata_supported)
{ struct sctp_queued_to_read *control;
if (ordered) {
TAILQ_FOREACH(control, &strm->inqueue, next_instrm) { if (SCTP_MID_EQ(idata_supported, control->mid, mid)) { break;
}
}
} else { if (idata_supported) {
TAILQ_FOREACH(control, &strm->uno_inqueue, next_instrm) { if (SCTP_MID_EQ(idata_supported, control->mid, mid)) { break;
}
}
} else {
control = TAILQ_FIRST(&strm->uno_inqueue);
}
} return (control);
}
staticint
sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc, struct mbuf **m, int offset, int chk_length, struct sctp_nets *net, uint32_t *high_tsn, int *abort_flag, int *break_flag, int last_chunk, uint8_t chk_type)
{ struct sctp_tmit_chunk *chk = NULL; /* make gcc happy */ struct sctp_stream_in *strm;
uint32_t tsn, fsn, gap, mid; struct mbuf *dmbuf; int the_len; int need_reasm_check = 0;
uint16_t sid; struct mbuf *op_err; char msg[SCTP_DIAG_INFO_LEN]; struct sctp_queued_to_read *control, *ncontrol;
uint32_t ppid;
uint8_t chk_flags; struct sctp_stream_reset_list *liste; int ordered;
size_t clen; int created_control = 0;
if (chk_type == SCTP_IDATA) { struct sctp_idata_chunk *chunk, chunk_buf;
old_cumack = asoc->cumulative_tsn;
old_base = asoc->mapping_array_base_tsn;
old_highest = asoc->highest_tsn_inside_map; /* *Wecouldprobablyimprovethisasmallbitbycalculatingthe *offsetofthecurrentcum-ackasthestartingpoint.
*/
at = 0; for (slide_from = 0; slide_from < stcb->asoc.mapping_array_size; slide_from++) {
val = asoc->nr_mapping_array[slide_from] | asoc->mapping_array[slide_from]; if (val == 0xff) {
at += 8;
} else { /* there is a 0 bit */
at += sctp_map_lookup_tab[val]; break;
}
}
asoc->cumulative_tsn = asoc->mapping_array_base_tsn + (at-1);
if (SCTP_TSN_GT(asoc->cumulative_tsn, asoc->highest_tsn_inside_map) &&
SCTP_TSN_GT(asoc->cumulative_tsn, asoc->highest_tsn_inside_nr_map)) { #ifdef INVARIANTS
panic("huh, cumack 0x%x greater than high-tsn 0x%x in map",
asoc->cumulative_tsn, asoc->highest_tsn_inside_map); #else
SCTP_PRINTF("huh, cumack 0x%x greater than high-tsn 0x%x in map - should panic?\n",
asoc->cumulative_tsn, asoc->highest_tsn_inside_map);
sctp_print_mapping_array(asoc); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MAP_LOGGING_ENABLE) {
sctp_log_map(0, 6, asoc->highest_tsn_inside_map, SCTP_MAP_SLIDE_RESULT);
}
asoc->highest_tsn_inside_map = asoc->cumulative_tsn;
asoc->highest_tsn_inside_nr_map = asoc->cumulative_tsn; #endif
} if (SCTP_TSN_GT(asoc->highest_tsn_inside_nr_map, asoc->highest_tsn_inside_map)) {
highest_tsn = asoc->highest_tsn_inside_nr_map;
} else {
highest_tsn = asoc->highest_tsn_inside_map;
} if ((asoc->cumulative_tsn == highest_tsn) && (at >= 8)) { /* The complete array was completed by a single FR */ /* highest becomes the cum-ack */ int clr; #ifdef INVARIANTS unsignedint i; #endif
/* clear the array */
clr = ((at+7) >> 3); if (clr > asoc->mapping_array_size) {
clr = asoc->mapping_array_size;
}
memset(asoc->mapping_array, 0, clr);
memset(asoc->nr_mapping_array, 0, clr); #ifdef INVARIANTS for (i = 0; i < asoc->mapping_array_size; i++) { if ((asoc->mapping_array[i]) || (asoc->nr_mapping_array[i])) {
SCTP_PRINTF("Error Mapping array's not clean at clear\n");
sctp_print_mapping_array(asoc);
}
} #endif
asoc->mapping_array_base_tsn = asoc->cumulative_tsn + 1;
asoc->highest_tsn_inside_nr_map = asoc->highest_tsn_inside_map = asoc->cumulative_tsn;
} elseif (at >= 8) { /* we can slide the mapping array down */ /* slide_from holds where we hit the first NON 0xff byte */
/*- *Nowbeforeweproceedwemustfigureoutifthisisawasted *cluster...i.e.itisasmallpacketsentinandyetthedriver *underneathallocatedafullclusterforit.Ifsowemustcopyit *toasmallermbufandfreeuptheclustermbuf.Thiswillhelp *withclusterstarvation.
*/ if (SCTP_BUF_LEN(m) < (long)MLEN && SCTP_BUF_NEXT(m) == NULL) { /* we only handle mbufs that are singletons.. not chains */
m = sctp_get_mbuf_for_msg(SCTP_BUF_LEN(m), 0, M_NOWAIT, 1, MT_DATA); if (m) { /* ok lets see if we can copy the data up */
caddr_t *from, *to; /* get the pointers and copy */
to = mtod(m, caddr_t *);
from = mtod((*mm), caddr_t *);
memcpy(to, from, SCTP_BUF_LEN((*mm))); /* copy the length and free up the old */
SCTP_BUF_LEN(m) = SCTP_BUF_LEN((*mm));
sctp_m_freem(*mm); /* success, back copy */
*mm = m;
} else { /* We are in trouble in the mbuf world .. yikes */
m = *mm;
}
} /* get pointer to the first chunk header */
ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, *offset, sizeof(struct sctp_chunkhdr),
(uint8_t *)&chunk_buf); if (ch == NULL) { return (1);
} /* *processallDATAchunks...
*/
*high_tsn = asoc->cumulative_tsn;
break_flag = 0;
asoc->data_pkts_seen++; while (stop_proc == 0) { /* validate chunk length */
chk_length = ntohs(ch->chunk_length); if (length - *offset < chk_length) { /* all done, mutulated chunk */
stop_proc = 1; continue;
} if ((asoc->idata_supported == 1) &&
(ch->chunk_type == SCTP_DATA)) { struct mbuf *op_err; char msg[SCTP_DIAG_INFO_LEN];
if (break_flag) { /* *Setbecauseofoutofrwndspaceandno *droprepspaceleft.
*/
stop_proc = 1; continue;
}
} else { /* not a data chunk in the data region */ switch (ch->chunk_type) { case SCTP_INITIATION: case SCTP_INITIATION_ACK: case SCTP_SELECTIVE_ACK: case SCTP_NR_SELECTIVE_ACK: case SCTP_HEARTBEAT_REQUEST: case SCTP_HEARTBEAT_ACK: case SCTP_ABORT_ASSOCIATION: case SCTP_SHUTDOWN: case SCTP_SHUTDOWN_ACK: case SCTP_OPERATION_ERROR: case SCTP_COOKIE_ECHO: case SCTP_COOKIE_ACK: case SCTP_ECN_ECHO: case SCTP_ECN_CWR: case SCTP_SHUTDOWN_COMPLETE: case SCTP_AUTHENTICATION: case SCTP_ASCONF_ACK: case SCTP_PACKET_DROPPED: case SCTP_STREAM_RESET: case SCTP_FORWARD_CUM_TSN: case SCTP_ASCONF:
{ /* *Now,whatdowedowithKNOWNchunksthat *areNOTintherightplace? * *Fornow,Idonothingbutignorethem.We *maylaterwanttoaddsysctlstuffto *switchoutanddoeitheranABORT()or *possiblyprocessthem.
*/ struct mbuf *op_err; char msg[SCTP_DIAG_INFO_LEN];
SCTP_SNPRINTF(msg, sizeof(msg), "DATA chunk followed by chunk of type %2.2x",
ch->chunk_type);
op_err = sctp_generate_cause(SCTP_CAUSE_PROTOCOL_VIOLATION, msg);
sctp_abort_an_association(inp, stcb, op_err, false, SCTP_SO_NOT_LOCKED); return (2);
} default: /* *Unknownchunktype:usebitrulesafter *checkinglength
*/ if (chk_length < sizeof(struct sctp_chunkhdr)) { /* *Needtosendanabortsincewehada *invalidchunk.
*/ struct mbuf *op_err; char msg[SCTP_DIAG_INFO_LEN];
SCTP_SNPRINTF(msg, sizeof(msg), "Chunk of length %u", chk_length);
op_err = sctp_generate_cause(SCTP_CAUSE_PROTOCOL_VIOLATION, msg);
stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_INDATA + SCTP_LOC_24;
sctp_abort_an_association(inp, stcb, op_err, false, SCTP_SO_NOT_LOCKED); return (2);
} if (ch->chunk_type & 0x40) { /* Add a error report to the queue */ struct mbuf *op_err; struct sctp_gen_error_cause *cause;
op_err = sctp_get_mbuf_for_msg(sizeof(struct sctp_gen_error_cause), 0, M_NOWAIT, 1, MT_DATA); if (op_err != NULL) {
cause = mtod(op_err, struct sctp_gen_error_cause *);
cause->code = htons(SCTP_CAUSE_UNRECOG_CHUNK);
cause->length = htons((uint16_t)(chk_length + sizeof(struct sctp_gen_error_cause)));
SCTP_BUF_LEN(op_err) = sizeof(struct sctp_gen_error_cause);
SCTP_BUF_NEXT(op_err) = SCTP_M_COPYM(m, *offset, chk_length, M_NOWAIT); if (SCTP_BUF_NEXT(op_err) != NULL) {
sctp_queue_op_err(stcb, op_err);
} else {
sctp_m_freem(op_err);
}
}
} if ((ch->chunk_type & 0x80) == 0) { /* discard the rest of this packet */
stop_proc = 1;
} /* else skip this bad chunk and
* continue... */ break;
} /* switch of chunk type */
}
*offset += SCTP_SIZE32(chk_length); if ((*offset >= length) || stop_proc) { /* no more data left in the mbuf chain */
stop_proc = 1; continue;
}
ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, *offset, sizeof(struct sctp_chunkhdr),
(uint8_t *)&chunk_buf); if (ch == NULL) {
*offset = length;
stop_proc = 1; continue;
}
} if (break_flag) { /* *weneedtoreportrwndoverrundrops.
*/
sctp_send_packet_dropped(stcb, net, *mm, length, iphlen, 0);
} if (num_chunks) { /* *Didwegetdata,ifsoupdatethetimeforauto-closeand *givepeercreditforbeingalive.
*/
SCTP_STAT_INCR(sctps_recvpktwithdata); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_THRESHOLD_LOGGING) {
sctp_misc_ints(SCTP_THRESHOLD_CLEAR,
stcb->asoc.overall_error_count, 0,
SCTP_FROM_SCTP_INDATA,
__LINE__);
}
stcb->asoc.overall_error_count = 0;
(void)SCTP_GETTIME_TIMEVAL(&stcb->asoc.time_last_rcvd);
} /* now service all of the reassm queue if needed */ if (SCTP_GET_STATE(stcb) == SCTP_STATE_SHUTDOWN_SENT) { /* Assure that we ack right away */
stcb->asoc.send_sack = 1;
} /* Start a sack timer or QUEUE a SACK for sending */
sctp_sack_check(stcb, was_a_gap); return (0);
}
staticint
sctp_process_segment_range(struct sctp_tcb *stcb, struct sctp_tmit_chunk **p_tp1, uint32_t last_tsn,
uint16_t frag_strt, uint16_t frag_end, int nr_sacking, int *num_frs,
uint32_t *biggest_newly_acked_tsn,
uint32_t *this_sack_lowest_newack, int *rto_ok)
{ struct sctp_tmit_chunk *tp1; unsignedint theTSN; int j, wake_him = 0, circled = 0;
/* Recover the tp1 we last saw */
tp1 = *p_tp1; if (tp1 == NULL) {
tp1 = TAILQ_FIRST(&stcb->asoc.sent_queue);
} for (j = frag_strt; j <= frag_end; j++) {
theTSN = j + last_tsn; while (tp1) { if (tp1->rec.data.doing_fast_retransmit)
(*num_frs) += 1;
TAILQ_FOREACH(tp1, &asoc->sent_queue, sctp_next) { if (SCTP_TSN_GT(tp1->rec.data.tsn, cumack)) { /* *okthisguyiseitherACKorMARKED.Ifitis *ACKEDithasbeenpreviouslyackedbutnotthis *timei.e.revoked.IfitisMARKEDitwasACK'ed *again.
*/ if (SCTP_TSN_GT(tp1->rec.data.tsn, biggest_tsn_acked)) { break;
} if (tp1->sent == SCTP_DATAGRAM_ACKED) { /* it has been revoked */
tp1->sent = SCTP_DATAGRAM_SENT;
tp1->rec.data.chunk_was_revoked = 1; /* We must add this stuff back in to *assuretimersandsuchgetstarted.
*/ if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FLIGHT_LOGGING_ENABLE) {
sctp_misc_ints(SCTP_FLIGHT_LOG_UP_REVOKE,
tp1->whoTo->flight_size,
tp1->book_size,
(uint32_t)(uintptr_t)tp1->whoTo,
tp1->rec.data.tsn);
}
sctp_flight_size_increase(tp1);
sctp_total_flight_increase(stcb, tp1); /* We inflate the cwnd to compensate for our *artificialinflationoftheflight_size.
*/
tp1->whoTo->cwnd += tp1->book_size; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SACK_LOGGING_ENABLE) {
sctp_log_sack(asoc->last_acked_seq,
cumack,
tp1->rec.data.tsn, 0, 0,
SCTP_LOG_TSN_REVOKED);
}
} elseif (tp1->sent == SCTP_DATAGRAM_MARKED) { /* it has been re-acked in this SACK */
tp1->sent = SCTP_DATAGRAM_ACKED;
}
} if (tp1->sent == SCTP_DATAGRAM_UNSENT) break;
}
}
/* CMT DAC algo: finding out if SACK is a mixed SACK */ if ((asoc->sctp_cmt_on_off > 0) &&
SCTP_BASE_SYSCTL(sctp_cmt_use_dac)) {
TAILQ_FOREACH(net, &asoc->nets, sctp_next) { if (net->saw_newack)
num_dests_sacked++;
}
} if (stcb->asoc.prsctp_supported) {
(void)SCTP_GETTIME_TIMEVAL(&now);
}
TAILQ_FOREACH(tp1, &asoc->sent_queue, sctp_next) {
strike_flag = 0; if (tp1->no_fr_allowed) { /* this one had a timeout or something */ continue;
} if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE) { if (tp1->sent < SCTP_DATAGRAM_RESEND)
sctp_log_fr(biggest_tsn_newly_acked,
tp1->rec.data.tsn,
tp1->sent,
SCTP_FR_LOG_CHECK_STRIKE);
} if (SCTP_TSN_GT(tp1->rec.data.tsn, biggest_tsn_acked) ||
tp1->sent == SCTP_DATAGRAM_UNSENT) { /* done */ break;
} if (stcb->asoc.prsctp_supported) { if ((PR_SCTP_TTL_ENABLED(tp1->flags)) && tp1->sent < SCTP_DATAGRAM_ACKED) { /* Is it expired? */ #if !(defined(__FreeBSD__) && !defined(__Userspace__)) if (timercmp(&now, &tp1->rec.data.timetodrop, >)) { #else if (timevalcmp(&now, &tp1->rec.data.timetodrop, >)) { #endif /* Yes so drop it */ if (tp1->data != NULL) {
(void)sctp_release_pr_sctp_chunk(stcb, tp1, 1,
SCTP_SO_NOT_LOCKED);
} continue;
}
}
} if (SCTP_TSN_GT(tp1->rec.data.tsn, asoc->this_sack_highest_gap) &&
!(accum_moved && asoc->fast_retran_loss_recovery)) { /* we are beyond the tsn in the sack */ break;
} if (tp1->sent >= SCTP_DATAGRAM_RESEND) { /* either a RESEND, ACKED, or MARKED */ /* skip */ if (tp1->sent == SCTP_FORWARD_TSN_SKIP) { /* Continue strikin FWD-TSN chunks */
tp1->rec.data.fwd_tsn_cnt++;
} continue;
} /* *CMT:SFRalgo(coverspartofDACandHTNAaswell)
*/ if (tp1->whoTo && tp1->whoTo->saw_newack == 0) { /* *Nonewackswerereceivedfordatasenttothis *dest.Therefore,accordingtotheSFRalgofor *CMT,nodatasenttothisdestcanbemarkedfor *FRusingthisSACK.
*/ continue;
} elseif (tp1->whoTo &&
SCTP_TSN_GT(tp1->rec.data.tsn,
tp1->whoTo->this_sack_highest_newack) &&
!(accum_moved && asoc->fast_retran_loss_recovery)) { /* *CMT:Newackswerereceivedfordatasentto *thisdest.Butnonewackswereseenfordata *sentaftertp1.Therefore,accordingtotheSFR *algoforCMT,tp1cannotbemarkedforFRusing *thisSACK.ThisstepcoverspartoftheDACalgo *andtheHTNAalgoaswell.
*/ continue;
} /* *HerewechecktoseeifwewerehavealreadydoneaFR *andifsoweseeifthebiggestTSNwesawinthesackis *smallerthantherecoverypoint.Ifsowedon'tstrike *thetsn...otherwiseweCANstriketheTSN.
*/ /* *@@@JRI:CheckforCMT *if(accum_moved&&asoc->fast_retran_loss_recovery&&(sctp_cmt_on_off==0)){
*/ if (accum_moved && asoc->fast_retran_loss_recovery) { /* *StriketheTSNifinfast-recoveryandcum-ack *moved.
*/ if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE) {
sctp_log_fr(biggest_tsn_newly_acked,
tp1->rec.data.tsn,
tp1->sent,
SCTP_FR_LOG_STRIKE_CHUNK);
} if (tp1->sent < SCTP_DATAGRAM_RESEND) {
tp1->sent++;
} if ((asoc->sctp_cmt_on_off > 0) &&
SCTP_BASE_SYSCTL(sctp_cmt_use_dac)) { /* *CMTDACalgorithm:IfSACKflagissetto *0,thenlowest_newacktestwillnotpass *becauseitwouldhavebeensettothe *cumackearlier.Ifnotalreadytobe *rtx'd,Ifnotamixedsackandiftp1is *notbetweentwosackedTSNs,thenmarkby *onemore. *NOTEthatwearemarkingbyoneadditionaltimesincetheSACKDACflagindicatesthat *twopacketshavebeenreceivedafterthismissingTSN.
*/ if ((tp1->sent < SCTP_DATAGRAM_RESEND) && (num_dests_sacked == 1) &&
SCTP_TSN_GT(this_sack_lowest_newack, tp1->rec.data.tsn)) { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE) {
sctp_log_fr(16 + num_dests_sacked,
tp1->rec.data.tsn,
tp1->sent,
SCTP_FR_LOG_STRIKE_CHUNK);
}
tp1->sent++;
}
}
} elseif ((tp1->rec.data.doing_fast_retransmit) &&
(asoc->sctp_cmt_on_off == 0)) { /* *ForthosethathavedoneaFRwemusttake *specialconsiderationifwestrike.I.ethe *biggest_newly_ackedmustbehigherthanthe *sending_seqatthetimewedidtheFR.
*/ if ( #ifdef SCTP_FR_TO_ALTERNATE /* *IfFR'sgotonewnetworks,thenwemustonlydo *thisforsinglyhomedasoc's.HoweveriftheFR's *gotothesamenetwork(Armando'swork)thenits *oktoFRmultipletimes.
*/
(asoc->numnets < 2) #else
(1) #endif
) { if (SCTP_TSN_GE(biggest_tsn_newly_acked,
tp1->rec.data.fast_retran_tsn)) { /* *StriketheTSN,sincethisackis *beyondwherethingswerewhenwe *didaFR.
*/ if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE) {
sctp_log_fr(biggest_tsn_newly_acked,
tp1->rec.data.tsn,
tp1->sent,
SCTP_FR_LOG_STRIKE_CHUNK);
} if (tp1->sent < SCTP_DATAGRAM_RESEND) {
tp1->sent++;
}
strike_flag = 1; if ((asoc->sctp_cmt_on_off > 0) &&
SCTP_BASE_SYSCTL(sctp_cmt_use_dac)) { /* *CMTDACalgorithm:If *SACKflagissetto0, *thenlowest_newacktest *willnotpassbecauseit *wouldhavebeensetto *thecumackearlier.If *notalreadytobertx'd, *Ifnotamixedsackand *iftp1isnotbetweentwo *sackedTSNs,thenmarkby *onemore. *NOTEthatwearemarkingbyoneadditionaltimesincetheSACKDACflagindicatesthat *twopacketshavebeenreceivedafterthismissingTSN.
*/ if ((tp1->sent < SCTP_DATAGRAM_RESEND) &&
(num_dests_sacked == 1) &&
SCTP_TSN_GT(this_sack_lowest_newack,
tp1->rec.data.tsn)) { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE) {
sctp_log_fr(32 + num_dests_sacked,
tp1->rec.data.tsn,
tp1->sent,
SCTP_FR_LOG_STRIKE_CHUNK);
} if (tp1->sent < SCTP_DATAGRAM_RESEND) {
tp1->sent++;
}
}
}
}
} /* *JRI:TODO:removecodeforHTNAalgo.CMT's *SFRalgocoversHTNA.
*/
} elseif (SCTP_TSN_GT(tp1->rec.data.tsn,
biggest_tsn_newly_acked)) { /* *Wedon'tstrikethese:ThisistheHTNA *algorithmi.e.wedon'tstrikeIfourTSNis *largerthantheHighestTSNNewlyAcked.
*/
;
} else { /* Strike the TSN */ if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE) {
sctp_log_fr(biggest_tsn_newly_acked,
tp1->rec.data.tsn,
tp1->sent,
SCTP_FR_LOG_STRIKE_CHUNK);
} if (tp1->sent < SCTP_DATAGRAM_RESEND) {
tp1->sent++;
} if ((asoc->sctp_cmt_on_off > 0) &&
SCTP_BASE_SYSCTL(sctp_cmt_use_dac)) { /* *CMTDACalgorithm:IfSACKflagissetto *0,thenlowest_newacktestwillnotpass *becauseitwouldhavebeensettothe *cumackearlier.Ifnotalreadytobe *rtx'd,Ifnotamixedsackandiftp1is *notbetweentwosackedTSNs,thenmarkby *onemore. *NOTEthatwearemarkingbyoneadditionaltimesincetheSACKDACflagindicatesthat *twopacketshavebeenreceivedafterthismissingTSN.
*/ if ((tp1->sent < SCTP_DATAGRAM_RESEND) && (num_dests_sacked == 1) &&
SCTP_TSN_GT(this_sack_lowest_newack, tp1->rec.data.tsn)) { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE) {
sctp_log_fr(48 + num_dests_sacked,
tp1->rec.data.tsn,
tp1->sent,
SCTP_FR_LOG_STRIKE_CHUNK);
}
tp1->sent++;
}
}
} if (tp1->sent == SCTP_DATAGRAM_RESEND) { struct sctp_nets *alt;
/* fix counts and things */ if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FLIGHT_LOGGING_ENABLE) {
sctp_misc_ints(SCTP_FLIGHT_LOG_DOWN_RSND,
(tp1->whoTo ? (tp1->whoTo->flight_size) : 0),
tp1->book_size,
(uint32_t)(uintptr_t)tp1->whoTo,
tp1->rec.data.tsn);
} if (tp1->whoTo) {
tp1->whoTo->net_ack++;
sctp_flight_size_decrease(tp1); if (stcb->asoc.cc_functions.sctp_cwnd_update_tsn_acknowledged) {
(*stcb->asoc.cc_functions.sctp_cwnd_update_tsn_acknowledged)(tp1->whoTo,
tp1);
}
}
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_RWND_ENABLE) {
sctp_log_rwnd(SCTP_INCREASE_PEER_RWND,
asoc->peers_rwnd, tp1->send_size, SCTP_BASE_SYSCTL(sctp_peer_chunk_oh));
} /* add back to the rwnd */
asoc->peers_rwnd += (tp1->send_size + SCTP_BASE_SYSCTL(sctp_peer_chunk_oh));
/* remove from the total flight */
sctp_total_flight_decrease(stcb, tp1);
if ((stcb->asoc.prsctp_supported) &&
(PR_SCTP_RTX_ENABLED(tp1->flags))) { /* Has it been retransmitted tv_sec times? - we store the retran count there. */ if (tp1->snd_count > tp1->rec.data.timetodrop.tv_sec) { /* Yes, so drop it */ if (tp1->data != NULL) {
(void)sctp_release_pr_sctp_chunk(stcb, tp1, 1,
SCTP_SO_NOT_LOCKED);
} /* Make sure to flag we had a FR */ if (tp1->whoTo != NULL) {
tp1->whoTo->net_ack++;
} continue;
}
} /* SCTP_PRINTF("OK, we are now ready to FR this guy\n"); */ if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FR_LOGGING_ENABLE) {
sctp_log_fr(tp1->rec.data.tsn, tp1->snd_count, 0, SCTP_FR_MARKED);
} if (strike_flag) { /* This is a subsequent FR */
SCTP_STAT_INCR(sctps_sendmultfastretrans);
}
sctp_ucount_incr(stcb->asoc.sent_queue_retran_cnt); if (asoc->sctp_cmt_on_off > 0) { /* *CMT:UsingRTX_SSTHRESHpolicyforCMT. *IfCMTisbeingused,thenpickdestwith *largestssthreshforanyretransmission.
*/
tp1->no_fr_allowed = 1;
alt = tp1->whoTo; /*sa_ignore NO_NULL_CHK*/ if (asoc->sctp_cmt_pf > 0) { /* JRS 5/18/07 - If CMT PF is on, use the PF version of find_alt_net() */
alt = sctp_find_alternate_net(stcb, alt, 2);
} else { /* JRS 5/18/07 - If only CMT is on, use the CMT version of find_alt_net() */ /*sa_ignore NO_NULL_CHK*/
alt = sctp_find_alternate_net(stcb, alt, 1);
} if (alt == NULL) {
alt = tp1->whoTo;
} /* *CUCv2:Ifadifferentdestispickedfor *theretransmission,thennew *(rtx-)pseudo_cumackneedstobetracked *fororigdest.LetCUCv2tracknew(rtx-) *pseudo-cumackalways.
*/ if (tp1->whoTo) {
tp1->whoTo->find_pseudo_cumack = 1;
tp1->whoTo->find_rtx_pseudo_cumack = 1;
}
} else {/* CMT is OFF */ #ifdef SCTP_FR_TO_ALTERNATE /* Can we find an alternate? */
alt = sctp_find_alternate_net(stcb, tp1->whoTo, 0); #else /* *defaultbehavioristoNOTretransmit *FR'stoanalternate.ArmandoCaro's *paperdetailswhy.
*/
alt = tp1->whoTo; #endif
}
tp1->rec.data.doing_fast_retransmit = 1; /* mark the sending seq for possible subsequent FR's */ /* *SCTP_PRINTF("MarkingTSNforFRnewvalue%x\n", *(uint32_t)tpi->rec.data.tsn);
*/ if (TAILQ_EMPTY(&asoc->send_queue)) { /* *Ifthequeueofsendisemptythenits *thenextsequencenumberthatwillbe *assignedsowesubtractonefromthisto *gettheonewelastsent.
*/
tp1->rec.data.fast_retran_tsn = sending_seq;
} else { /* *Iftherearechunksonthesendqueue *(unsentdatathathasmadeitfromthe *streamqueuesbutnotoutthedoor,we *takethefirstone(whichwillhavethe *lowestTSN)andsubtractonetogetthe *onewelastsent.
*/ struct sctp_tmit_chunk *ttt;
SCTP_STAT_DECR_GAUGE32(sctps_currestab);
SCTP_SET_STATE(stcb, SCTP_STATE_SHUTDOWN_ACK_SENT);
sctp_stop_timers_for_shutdown(stcb); if (asoc->alternate) {
netp = asoc->alternate;
} else {
netp = asoc->primary_destination;
}
sctp_send_shutdown_ack(stcb, netp);
sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNACK,
stcb->sctp_ep, stcb, netp);
}
} /*********************************************/ /* Here we perform PR-SCTP procedures */ /* (section 4.2) */ /*********************************************/ /* C1. update advancedPeerAckPoint */ if (SCTP_TSN_GT(cumack, asoc->advanced_peer_ack_point)) {
asoc->advanced_peer_ack_point = cumack;
} /* PR-Sctp issues need to be addressed too */ if ((asoc->prsctp_supported) && (asoc->pr_sctp_cnt > 0)) { struct sctp_tmit_chunk *lchk;
uint32_t old_adv_peer_ack_point;
old_adv_peer_ack_point = asoc->advanced_peer_ack_point;
lchk = sctp_try_advance_peer_ack_point(stcb, asoc); /* C3. See if we need to send a Fwd-TSN */ if (SCTP_TSN_GT(asoc->advanced_peer_ack_point, cumack)) { /* *ISSUEwithECN,seeFWD-TSNprocessing.
*/ if (SCTP_TSN_GT(asoc->advanced_peer_ack_point, old_adv_peer_ack_point)) {
send_forward_tsn(stcb, asoc);
} elseif (lchk) { /* try to FR fwd-tsn's that get lost too */ if (lchk->rec.data.fwd_tsn_cnt >= 3) {
send_forward_tsn(stcb, asoc);
}
}
} for (; lchk != NULL; lchk = TAILQ_NEXT(lchk, sctp_next)) { if (lchk->whoTo != NULL) { break;
}
} if (lchk != NULL) { /* Assure a timer is up */
sctp_timer_start(SCTP_TIMER_TYPE_SEND,
stcb->sctp_ep, stcb, lchk->whoTo);
}
} if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SACK_RWND_LOGGING_ENABLE) {
sctp_misc_ints(SCTP_SACK_RWND_UPDATE,
rwnd,
stcb->asoc.peers_rwnd,
stcb->asoc.total_flight,
stcb->asoc.total_output_queue_size);
}
}
/* JRS - Use the congestion control given in the CC module */ if (ecne_seen == 0) {
TAILQ_FOREACH(net, &asoc->nets, sctp_next) { if (net->net_ack2 > 0) { /* *Karn'sruleappliestoclearingerrorcount,this *isoptional.
*/
net->error_count = 0; if ((net->dest_state & SCTP_ADDR_REACHABLE) == 0) { /* addr came good */
net->dest_state |= SCTP_ADDR_REACHABLE;
sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_UP, stcb, 0, (void *)net, SCTP_SO_NOT_LOCKED);
}
if (net == stcb->asoc.primary_destination) { if (stcb->asoc.alternate) { /* release the alternate, primary is good */
sctp_free_remote_addr(stcb->asoc.alternate);
stcb->asoc.alternate = NULL;
}
}
if (net->dest_state & SCTP_ADDR_PF) {
net->dest_state &= ~SCTP_ADDR_PF;
sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT,
stcb->sctp_ep, stcb, net,
SCTP_FROM_SCTP_INDATA + SCTP_LOC_33);
sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep, stcb, net);
asoc->cc_functions.sctp_cwnd_update_exit_pf(stcb, net); /* Done with this net */
net->net_ack = 0;
} /* restore any doubled timers */
net->RTO = (net->lastsa >> SCTP_RTT_SHIFT) + net->lastsv; if (net->RTO < stcb->asoc.minrto) {
net->RTO = stcb->asoc.minrto;
} if (net->RTO > stcb->asoc.maxrto) {
net->RTO = stcb->asoc.maxrto;
}
}
}
asoc->cc_functions.sctp_cwnd_update_after_sack(stcb, asoc, accum_moved, reneged_all, will_exit_fast_recovery);
}
/* *CMTDACalgorithm:IfSACKDACflagwas0,thennoextramarking *tobedone.Settingthis_sack_lowest_newacktothecum_ackwill *automaticallyensurethat.
*/ if ((asoc->sctp_cmt_on_off > 0) &&
SCTP_BASE_SYSCTL(sctp_cmt_use_dac) &&
(cmt_dac_flag == 0)) {
this_sack_lowest_newack = cum_ack;
} if ((num_seg > 0) || (num_nr_seg > 0)) {
sctp_strike_gap_ack_chunks(stcb, asoc, biggest_tsn_acked,
biggest_tsn_newly_acked, this_sack_lowest_newack, accum_moved);
} /* JRS - Use the congestion control given in the CC module */
asoc->cc_functions.sctp_cwnd_update_after_fr(stcb, asoc);
/* Now are we exiting loss recovery ? */ if (will_exit_fast_recovery) { /* Ok, we must exit fast recovery */
asoc->fast_retran_loss_recovery = 0;
} if ((asoc->sat_t3_loss_recovery) &&
SCTP_TSN_GE(asoc->last_acked_seq, asoc->sat_t3_recovery_tsn)) { /* end satellite t3 loss recovery */
asoc->sat_t3_loss_recovery = 0;
} /* *CMTFastrecovery
*/
TAILQ_FOREACH(net, &asoc->nets, sctp_next) { if (net->will_exit_fast_recovery) { /* Ok, we must exit fast recovery */
net->fast_retran_loss_recovery = 0;
}
}
/* Adjust and set the new rwnd value */ if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_RWND_ENABLE) {
sctp_log_rwnd_set(SCTP_SET_PEER_RWND_VIA_SACK,
asoc->peers_rwnd, asoc->total_flight, (asoc->total_flight_count * SCTP_BASE_SYSCTL(sctp_peer_chunk_oh)), a_rwnd);
}
asoc->peers_rwnd = sctp_sbspace_sub(a_rwnd,
(uint32_t) (asoc->total_flight + (asoc->total_flight_count * SCTP_BASE_SYSCTL(sctp_peer_chunk_oh)))); if (asoc->peers_rwnd < stcb->sctp_ep->sctp_ep.sctp_sws_sender) { /* SWS sender side engages */
asoc->peers_rwnd = 0;
} if (asoc->peers_rwnd > old_rwnd) {
win_probe_recovery = 1;
}
/* *Nowwemustsetupsowehaveatimerupforanyonewith *outstandingdata.
*/
done_once = 0;
again:
j = 0;
TAILQ_FOREACH(net, &asoc->nets, sctp_next) { if (win_probe_recovery && (net->window_probe)) {
win_probe_recovered = 1; /*- *Findfirstchunkthatwasusedwith *windowprobeandcleartheevent.Put *itbackintothesendqueueasifhas *notbeensent.
*/
TAILQ_FOREACH(tp1, &asoc->sent_queue, sctp_next) { if (tp1->window_probe) {
sctp_window_probe_recovery(stcb, asoc, tp1); break;
}
}
} if (net->flight_size) {
j++; if (!SCTP_OS_TIMER_PENDING(&net->rxt_timer.timer)) {
sctp_timer_start(SCTP_TIMER_TYPE_SEND,
stcb->sctp_ep, stcb, net);
} if (net->window_probe) {
net->window_probe = 0;
}
} else { if (net->window_probe) { /* In window probes we must assure a timer is still running there */ if (!SCTP_OS_TIMER_PENDING(&net->rxt_timer.timer)) {
sctp_timer_start(SCTP_TIMER_TYPE_SEND,
stcb->sctp_ep, stcb, net);
}
} elseif (SCTP_OS_TIMER_PENDING(&net->rxt_timer.timer)) {
sctp_timer_stop(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep,
stcb, net,
SCTP_FROM_SCTP_INDATA + SCTP_LOC_36);
}
}
} if ((j == 0) &&
(!TAILQ_EMPTY(&asoc->sent_queue)) &&
(asoc->sent_queue_retran_cnt == 0) &&
(win_probe_recovered == 0) &&
(done_once == 0)) { /* huh, this should not happen unless all packets *arePR-SCTPandmarkedtoskipofcourse.
*/ if (sctp_fs_audit(asoc)) {
TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
net->flight_size = 0;
}
asoc->total_flight = 0;
asoc->total_flight_count = 0;
asoc->sent_queue_retran_cnt = 0;
TAILQ_FOREACH(tp1, &asoc->sent_queue, sctp_next) { if (tp1->sent < SCTP_DATAGRAM_RESEND) {
sctp_flight_size_increase(tp1);
sctp_total_flight_increase(stcb, tp1);
} elseif (tp1->sent == SCTP_DATAGRAM_RESEND) {
sctp_ucount_incr(asoc->sent_queue_retran_cnt);
}
}
}
done_once = 1; goto again;
} /*********************************************/ /* Here we perform PR-SCTP procedures */ /* (section 4.2) */ /*********************************************/ /* C1. update advancedPeerAckPoint */ if (SCTP_TSN_GT(cum_ack, asoc->advanced_peer_ack_point)) {
asoc->advanced_peer_ack_point = cum_ack;
} /* C2. try to further move advancedPeerAckPoint ahead */ if ((asoc->prsctp_supported) && (asoc->pr_sctp_cnt > 0)) { struct sctp_tmit_chunk *lchk;
uint32_t old_adv_peer_ack_point;
old_adv_peer_ack_point = asoc->advanced_peer_ack_point;
lchk = sctp_try_advance_peer_ack_point(stcb, asoc); /* C3. See if we need to send a Fwd-TSN */ if (SCTP_TSN_GT(asoc->advanced_peer_ack_point, cum_ack)) { /* *ISSUEwithECN,seeFWD-TSNprocessing.
*/ if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_TRY_ADVANCE) {
sctp_misc_ints(SCTP_FWD_TSN_CHECK, 0xee, cum_ack, asoc->advanced_peer_ack_point,
old_adv_peer_ack_point);
} if (SCTP_TSN_GT(asoc->advanced_peer_ack_point, old_adv_peer_ack_point)) {
send_forward_tsn(stcb, asoc);
} elseif (lchk) { /* try to FR fwd-tsn's that get lost too */ if (lchk->rec.data.fwd_tsn_cnt >= 3) {
send_forward_tsn(stcb, asoc);
}
}
} for (; lchk != NULL; lchk = TAILQ_NEXT(lchk, sctp_next)) { if (lchk->whoTo != NULL) { break;
}
} if (lchk != NULL) { /* Assure a timer is up */
sctp_timer_start(SCTP_TIMER_TYPE_SEND,
stcb->sctp_ep, stcb, lchk->whoTo);
}
} if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SACK_RWND_LOGGING_ENABLE) {
sctp_misc_ints(SCTP_SACK_RWND_UPDATE,
a_rwnd,
stcb->asoc.peers_rwnd,
stcb->asoc.total_flight,
stcb->asoc.total_output_queue_size);
}
}
/* *Okwenowlookforthestream/seqonthereadqueue *whereitsnotalldelivered.Ifwefinditwetransmutethe *readentryintoaPDI_ABORTED.
*/ if (sid >= asoc->streamincnt) { /* screwed up streams, stop! */ break;
} if ((asoc->str_of_pdapi == sid) &&
(asoc->ssn_of_pdapi == mid)) { /* If this is the one we were partially delivering *nowthenwenolongerare.Notethiswillchange *withthereassemblyre-write.
*/
asoc->fragmented_delivery_inprogress = 0;
}
strm = &asoc->strmin[sid]; if (ordered) {
TAILQ_FOREACH_SAFE(control, &strm->inqueue, next_instrm, ncontrol) { if (SCTP_MID_GE(asoc->idata_supported, mid, control->mid)) {
sctp_flush_reassm_for_str_seq(stcb, asoc, strm, control, ordered, new_cum_tsn);
}
}
} else { if (asoc->idata_supported) {
TAILQ_FOREACH_SAFE(control, &strm->uno_inqueue, next_instrm, ncontrol) { if (SCTP_MID_GE(asoc->idata_supported, mid, control->mid)) {
sctp_flush_reassm_for_str_seq(stcb, asoc, strm, control, ordered, new_cum_tsn);
}
}
} else { if (!TAILQ_EMPTY(&strm->uno_inqueue)) {
sctp_flush_reassm_for_str_seq(stcb, asoc, strm, TAILQ_FIRST(&strm->uno_inqueue), ordered, new_cum_tsn);
}
}
}
TAILQ_FOREACH(control, &stcb->sctp_ep->read_queue, next) { if ((control->sinfo_stream == sid) &&
(SCTP_MID_EQ(asoc->idata_supported, control->mid, mid))) {
control->pdapi_aborted = 1;
control->end_added = 1; if (control->on_strm_q == SCTP_ON_ORDERED) {
TAILQ_REMOVE(&strm->inqueue, control, next_instrm); if (asoc->size_on_all_streams >= control->length) {
asoc->size_on_all_streams -= control->length;
} else { #ifdef INVARIANTS
panic("size_on_all_streams = %u smaller than control length %u", asoc->size_on_all_streams, control->length); #else
asoc->size_on_all_streams = 0; #endif
}
sctp_ucount_decr(asoc->cnt_on_all_streams);
} elseif (control->on_strm_q == SCTP_ON_UNORDERED) {
TAILQ_REMOVE(&strm->uno_inqueue, control, next_instrm); #ifdef INVARIANTS
} elseif (control->on_strm_q) {
panic("strm: %p ctl: %p unknown %d",
strm, control, control->on_strm_q); #endif
}
control->on_strm_q = 0;
sctp_ulp_notify(SCTP_NOTIFY_PARTIAL_DELVIERY_INDICATION,
stcb,
SCTP_PARTIAL_DELIVERY_ABORTED,
(void *)control,
SCTP_SO_NOT_LOCKED); break;
} elseif ((control->sinfo_stream == sid) &&
SCTP_MID_GT(asoc->idata_supported, control->mid, mid)) { /* We are past our victim SSN */ break;
}
} if (SCTP_MID_GT(asoc->idata_supported, mid, strm->last_mid_delivered)) { /* Update the sequence number */
strm->last_mid_delivered = mid;
} /* now kick the stream the new way */ /*sa_ignore NO_NULL_CHK*/
sctp_kick_prsctp_reorder_queue(stcb, strm);
}
SCTP_INP_READ_UNLOCK(stcb->sctp_ep);
} /* *Nowslidethingforward.
*/
sctp_slide_mapping_arrays(stcb);
}
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.288Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-27)
¤
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.