From 0c0982b80cb953b31820c993c6b762807f502b8f Mon Sep 17 00:00:00 2001 From: Randall Stewart Date: Sat, 14 Mar 2009 13:42:13 +0000 Subject: [PATCH] Fixes several PR-SCTP releated bugs. - When sending large PR-SCTP messages over a lossy link we would incorrectly calculate the fwd-tsn - When receiving large multipart pr-sctp packets we would incorrectly send back a SACK that would renege improperly on already received packets thus causing unneeded retransmissions. --- sys/netinet/sctp.h | 2 +- sys/netinet/sctp_constants.h | 4 +- sys/netinet/sctp_indata.c | 226 ++++++++++++++++++++++------------- sys/netinet/sctp_output.c | 56 ++++++++- sys/netinet/sctp_timer.c | 4 +- sys/netinet/sctp_var.h | 4 +- sys/netinet/sctputil.c | 205 +++++++++++++++++++++++-------- sys/netinet/sctputil.h | 3 +- 8 files changed, 361 insertions(+), 143 deletions(-) diff --git a/sys/netinet/sctp.h b/sys/netinet/sctp.h index afbf5ea57cfe..83b939aef3ba 100644 --- a/sys/netinet/sctp.h +++ b/sys/netinet/sctp.h @@ -544,7 +544,7 @@ struct sctp_error_unrecognized_chunk { #define SCTP_THRESHOLD_LOGGING 0x02000000 #define SCTP_LOG_AT_SEND_2_SCTP 0x04000000 #define SCTP_LOG_AT_SEND_2_OUTQ 0x08000000 - +#define SCTP_LOG_TRY_ADVANCE 0x10000000 #undef SCTP_PACKED diff --git a/sys/netinet/sctp_constants.h b/sys/netinet/sctp_constants.h index bdd1849d17e2..8df349096341 100644 --- a/sys/netinet/sctp_constants.h +++ b/sys/netinet/sctp_constants.h @@ -229,8 +229,8 @@ __FBSDID("$FreeBSD$"); #define SCTP_THRESHOLD_CLEAR 120 #define SCTP_THRESHOLD_INCR 121 #define SCTP_FLIGHT_LOG_DWN_WP_FWD 122 - -#define SCTP_LOG_MAX_TYPES 123 +#define SCTP_FWD_TSN_CHECK 123 +#define SCTP_LOG_MAX_TYPES 124 /* * To turn on various logging, you must first enable 'options KTR' and * you might want to bump the entires 'options KTR_ENTRIES=80000'. diff --git a/sys/netinet/sctp_indata.c b/sys/netinet/sctp_indata.c index f9fd08fe2d0e..0075007d2776 100644 --- a/sys/netinet/sctp_indata.c +++ b/sys/netinet/sctp_indata.c @@ -3680,7 +3680,7 @@ sctp_strike_gap_ack_chunks(struct sctp_tcb *stcb, struct sctp_association *asoc, if (tp1->data != NULL) { (void)sctp_release_pr_sctp_chunk(stcb, tp1, (SCTP_RESPONSE_TO_USER_REQ | SCTP_NOTIFY_DATAGRAM_SENT), - &asoc->sent_queue, SCTP_SO_NOT_LOCKED); + SCTP_SO_NOT_LOCKED); } tp1 = TAILQ_NEXT(tp1, sctp_next); continue; @@ -3693,7 +3693,7 @@ sctp_strike_gap_ack_chunks(struct sctp_tcb *stcb, struct sctp_association *asoc, if (tp1->data != NULL) { (void)sctp_release_pr_sctp_chunk(stcb, tp1, (SCTP_RESPONSE_TO_USER_REQ | SCTP_NOTIFY_DATAGRAM_SENT), - &asoc->sent_queue, SCTP_SO_NOT_LOCKED); + SCTP_SO_NOT_LOCKED); } tp1 = TAILQ_NEXT(tp1, sctp_next); continue; @@ -4078,6 +4078,13 @@ sctp_try_advance_peer_ack_point(struct sctp_tcb *stcb, /* no chance to advance, out of here */ break; } + if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_TRY_ADVANCE) { + if (tp1->sent == SCTP_FORWARD_TSN_SKIP) { + sctp_misc_ints(SCTP_FWD_TSN_CHECK, + asoc->advanced_peer_ack_point, + tp1->rec.data.TSN_seq, 0, 0); + } + } if (!PR_SCTP_ENABLED(tp1->flags)) { /* * We can't fwd-tsn past any that are reliable aka @@ -4107,7 +4114,7 @@ sctp_try_advance_peer_ack_point(struct sctp_tcb *stcb, if (tp1->data) { (void)sctp_release_pr_sctp_chunk(stcb, tp1, (SCTP_RESPONSE_TO_USER_REQ | SCTP_NOTIFY_DATAGRAM_SENT), - &asoc->sent_queue, SCTP_SO_NOT_LOCKED); + SCTP_SO_NOT_LOCKED); } } else { /* @@ -4124,8 +4131,16 @@ sctp_try_advance_peer_ack_point(struct sctp_tcb *stcb, */ if (tp1->sent == SCTP_FORWARD_TSN_SKIP) { /* advance PeerAckPoint goes forward */ - asoc->advanced_peer_ack_point = tp1->rec.data.TSN_seq; - a_adv = tp1; + if (compare_with_wrap(tp1->rec.data.TSN_seq, + asoc->advanced_peer_ack_point, + MAX_TSN)) { + + asoc->advanced_peer_ack_point = tp1->rec.data.TSN_seq; + a_adv = tp1; + } else if (tp1->rec.data.TSN_seq == asoc->advanced_peer_ack_point) { + /* No update but we do save the chk */ + a_adv = tp1; + } } else { /* * If it is still in RESEND we can advance no @@ -4142,14 +4157,27 @@ sctp_try_advance_peer_ack_point(struct sctp_tcb *stcb, return (a_adv); } -static void +static int sctp_fs_audit(struct sctp_association *asoc) { struct sctp_tmit_chunk *chk; int inflight = 0, resend = 0, inbetween = 0, acked = 0, above = 0; + int entry_flight, entry_cnt, ret; + + entry_flight = asoc->total_flight; + entry_cnt = asoc->total_flight_count; + ret = 0; + + if (asoc->pr_sctp_cnt >= asoc->sent_queue_cnt) + return (0); TAILQ_FOREACH(chk, &asoc->sent_queue, sctp_next) { if (chk->sent < SCTP_DATAGRAM_RESEND) { + printf("Chk TSN:%u size:%d inflight cnt:%d\n", + chk->rec.data.TSN_seq, + chk->send_size, + chk->snd_count + ); inflight++; } else if (chk->sent == SCTP_DATAGRAM_RESEND) { resend++; @@ -4166,10 +4194,15 @@ sctp_fs_audit(struct sctp_association *asoc) #ifdef INVARIANTS panic("Flight size-express incorrect? \n"); #else - SCTP_PRINTF("Flight size-express incorrect inflight:%d inbetween:%d\n", - inflight, inbetween); + printf("asoc->total_flight:%d cnt:%d\n", + entry_flight, entry_cnt); + + SCTP_PRINTF("Flight size-express incorrect F:%d I:%d R:%d Ab:%d ACK:%d\n", + inflight, inbetween, resend, above, acked); + ret = 1; #endif } + return (ret); } @@ -4590,20 +4623,26 @@ sctp_express_handle_sack(struct sctp_tcb *stcb, uint32_t cumack, (asoc->sent_queue_retran_cnt == 0) && (win_probe_recovered == 0) && (done_once == 0)) { - /* huh, this should not happen */ - 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); - } else if (tp1->sent == SCTP_DATAGRAM_RESEND) { - asoc->sent_queue_retran_cnt++; + /* + * huh, this should not happen unless all packets are + * PR-SCTP and marked to skip of course. + */ + if (sctp_fs_audit(asoc)) { + TAILQ_FOREACH(net, &asoc->nets, sctp_next) { + if (net->flight_size) { + 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); + } else if (tp1->sent == SCTP_DATAGRAM_RESEND) { + asoc->sent_queue_retran_cnt++; + } } } done_once = 1; @@ -4728,6 +4767,13 @@ sctp_express_handle_sack(struct sctp_tcb *stcb, uint32_t cumack, */ asoc->nonce_sum_check = 0; asoc->nonce_resync_tsn = asoc->advanced_peer_ack_point; + } else if (lchk) { + /* try to FR fwd-tsn's that get lost too */ + lchk->rec.data.fwd_tsn_cnt++; + if (lchk->rec.data.fwd_tsn_cnt > 3) { + send_forward_tsn(stcb, asoc); + lchk->rec.data.fwd_tsn_cnt = 0; + } } } if (lchk) { @@ -4813,10 +4859,6 @@ sctp_handle_sack(struct mbuf *m, int offset, num_seg = ntohs(sack->num_gap_ack_blks); a_rwnd = rwnd; - if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_SACK_ARRIVALS_ENABLE) { - sctp_misc_ints(SCTP_SACK_LOG_NORMAL, cum_ack, - rwnd, stcb->asoc.last_acked_seq, stcb->asoc.peers_rwnd); - } /* CMT DAC algo */ cmt_dac_flag = ch->ch.chunk_flags & SCTP_SACK_CMT_DAC; num_dup = ntohs(sack->num_dup_tsns); @@ -5605,20 +5647,24 @@ sctp_handle_sack(struct mbuf *m, int offset, (asoc->sent_queue_retran_cnt == 0) && (win_probe_recovered == 0) && (done_once == 0)) { - /* huh, this should not happen */ - 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); - } else if (tp1->sent == SCTP_DATAGRAM_RESEND) { - asoc->sent_queue_retran_cnt++; + /* + * huh, this should not happen unless all packets are + * PR-SCTP and marked to skip of course. + */ + 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); + } else if (tp1->sent == SCTP_DATAGRAM_RESEND) { + asoc->sent_queue_retran_cnt++; + } } } done_once = 1; @@ -5643,6 +5689,11 @@ sctp_handle_sack(struct mbuf *m, int offset, * on issues that will occur when the ECN NONCE * stuff is put into SCTP for cross checking. */ + 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 (compare_with_wrap(asoc->advanced_peer_ack_point, old_adv_peer_ack_point, MAX_TSN)) { send_forward_tsn(stcb, asoc); @@ -5652,6 +5703,13 @@ sctp_handle_sack(struct mbuf *m, int offset, */ asoc->nonce_sum_check = 0; asoc->nonce_resync_tsn = asoc->advanced_peer_ack_point; + } else if (lchk) { + /* try to FR fwd-tsn's that get lost too */ + lchk->rec.data.fwd_tsn_cnt++; + if (lchk->rec.data.fwd_tsn_cnt > 3) { + send_forward_tsn(stcb, asoc); + lchk->rec.data.fwd_tsn_cnt = 0; + } } } if (lchk) { @@ -6019,7 +6077,6 @@ sctp_handle_forward_tsn(struct sctp_tcb *stcb, return; } SCTP_STAT_INCR(sctps_fwdtsn_map_over); -slide_out: memset(stcb->asoc.mapping_array, 0, stcb->asoc.mapping_array_size); cumack_set_flag = 1; asoc->mapping_array_base_tsn = new_cum_tsn + 1; @@ -6043,13 +6100,8 @@ sctp_handle_forward_tsn(struct sctp_tcb *stcb, asoc->last_echo_tsn = asoc->highest_tsn_inside_map; } else { SCTP_TCB_LOCK_ASSERT(stcb); - if ((compare_with_wrap(((uint32_t) asoc->cumulative_tsn + gap), asoc->highest_tsn_inside_map, MAX_TSN)) || - (((uint32_t) asoc->cumulative_tsn + gap) == asoc->highest_tsn_inside_map)) { - goto slide_out; - } else { - for (i = 0; i <= gap; i++) { - SCTP_SET_TSN_PRESENT(asoc->mapping_array, i); - } + for (i = 0; i <= gap; i++) { + SCTP_SET_TSN_PRESENT(asoc->mapping_array, i); } /* * Now after marking all, slide thing forward but no sack @@ -6059,7 +6111,6 @@ sctp_handle_forward_tsn(struct sctp_tcb *stcb, if (*abort_flag) return; } - /*************************************************************/ /* 2. Clear up re-assembly queue */ /*************************************************************/ @@ -6083,9 +6134,9 @@ sctp_handle_forward_tsn(struct sctp_tcb *stcb, chk = TAILQ_FIRST(&asoc->reasmqueue); while (chk) { at = TAILQ_NEXT(chk, sctp_next); - if (compare_with_wrap(asoc->cumulative_tsn, - chk->rec.data.TSN_seq, MAX_TSN) || - asoc->cumulative_tsn == chk->rec.data.TSN_seq) { + if ((compare_with_wrap(new_cum_tsn, + chk->rec.data.TSN_seq, MAX_TSN)) || + (new_cum_tsn == chk->rec.data.TSN_seq)) { /* It needs to be tossed */ TAILQ_REMOVE(&asoc->reasmqueue, chk, sctp_next); if (compare_with_wrap(chk->rec.data.TSN_seq, @@ -6614,20 +6665,24 @@ sctp_express_handle_nr_sack(struct sctp_tcb *stcb, uint32_t cumack, (asoc->sent_queue_retran_cnt == 0) && (win_probe_recovered == 0) && (done_once == 0)) { - /* huh, this should not happen */ - 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); - } else if (tp1->sent == SCTP_DATAGRAM_RESEND) { - asoc->sent_queue_retran_cnt++; + /* + * huh, this should not happen unless all packets are + * PR-SCTP and marked to skip of course. + */ + 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); + } else if (tp1->sent == SCTP_DATAGRAM_RESEND) { + asoc->sent_queue_retran_cnt++; + } } } done_once = 1; @@ -8170,20 +8225,24 @@ sctp_handle_nr_sack(struct mbuf *m, int offset, (asoc->sent_queue_retran_cnt == 0) && (win_probe_recovered == 0) && (done_once == 0)) { - /* huh, this should not happen */ - 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); - } else if (tp1->sent == SCTP_DATAGRAM_RESEND) { - asoc->sent_queue_retran_cnt++; + /* + * huh, this should not happen unless all packets are + * PR-SCTP and marked to skip of course. + */ + 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); + } else if (tp1->sent == SCTP_DATAGRAM_RESEND) { + asoc->sent_queue_retran_cnt++; + } } } done_once = 1; @@ -8221,6 +8280,13 @@ sctp_handle_nr_sack(struct mbuf *m, int offset, */ asoc->nonce_sum_check = 0; asoc->nonce_resync_tsn = asoc->advanced_peer_ack_point; + } else if (lchk) { + /* try to FR fwd-tsn's that get lost too */ + lchk->rec.data.fwd_tsn_cnt++; + if (lchk->rec.data.fwd_tsn_cnt > 3) { + send_forward_tsn(stcb, asoc); + lchk->rec.data.fwd_tsn_cnt = 0; + } } } if (lchk) { diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c index 3729616c5977..9c5603d1db52 100644 --- a/sys/netinet/sctp_output.c +++ b/sys/netinet/sctp_output.c @@ -7513,7 +7513,7 @@ sctp_prune_prsctp(struct sctp_tcb *stcb, cause = SCTP_RESPONSE_TO_USER_REQ | SCTP_NOTIFY_DATAGRAM_UNSENT; ret_spc = sctp_release_pr_sctp_chunk(stcb, chk, cause, - &asoc->sent_queue, SCTP_SO_LOCKED); + SCTP_SO_LOCKED); freed_spc += ret_spc; if (freed_spc >= dataout) { return; @@ -7538,7 +7538,7 @@ sctp_prune_prsctp(struct sctp_tcb *stcb, ret_spc = sctp_release_pr_sctp_chunk(stcb, chk, SCTP_RESPONSE_TO_USER_REQ | SCTP_NOTIFY_DATAGRAM_UNSENT, - &asoc->send_queue, SCTP_SO_LOCKED); + SCTP_SO_LOCKED); freed_spc += ret_spc; if (freed_spc >= dataout) { @@ -8405,6 +8405,7 @@ sctp_clean_up_ctl(struct sctp_tcb *stcb, struct sctp_association *asoc) (chk->rec.chunk_id.id == SCTP_NR_SELECTIVE_ACK) || /* EY */ (chk->rec.chunk_id.id == SCTP_HEARTBEAT_REQUEST) || (chk->rec.chunk_id.id == SCTP_HEARTBEAT_ACK) || + (chk->rec.chunk_id.id == SCTP_FORWARD_CUM_TSN) || (chk->rec.chunk_id.id == SCTP_SHUTDOWN) || (chk->rec.chunk_id.id == SCTP_SHUTDOWN_ACK) || (chk->rec.chunk_id.id == SCTP_OPERATION_ERROR) || @@ -8547,7 +8548,7 @@ sctp_move_to_outqueue(struct sctp_tcb *stcb, struct sctp_nets *net, * when we took all the data the sender_all_done was * not set. */ - if (sp->put_last_out == 0) { + if ((sp->put_last_out == 0) && (sp->discard_rest == 0)) { SCTP_PRINTF("Gak, put out entire msg with NO end!-1\n"); SCTP_PRINTF("sender_done:%d len:%d msg_comp:%d put_last_out:%d send_lock:%d\n", sp->sender_all_done, @@ -8568,7 +8569,6 @@ sctp_move_to_outqueue(struct sctp_tcb *stcb, struct sctp_nets *net, sp->data = NULL; } sctp_free_a_strmoq(stcb, sp); - /* we can't be locked to it */ *locked = 0; stcb->asoc.locked_on_sending = NULL; @@ -8596,6 +8596,29 @@ sctp_move_to_outqueue(struct sctp_tcb *stcb, struct sctp_nets *net, *giveup = 1; to_move = 0; goto out_of; + } else if (sp->discard_rest) { + if (send_lock_up == 0) { + SCTP_TCB_SEND_LOCK(stcb); + send_lock_up = 1; + } + /* Whack down the size */ + atomic_subtract_int(&stcb->asoc.total_output_queue_size, sp->length); + if ((stcb->sctp_socket != NULL) && \ + ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) || + (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL))) { + atomic_subtract_int(&stcb->sctp_socket->so_snd.sb_cc, sp->length); + } + if (sp->data) { + sctp_m_freem(sp->data); + sp->data = NULL; + sp->tail_mbuf = NULL; + } + sp->length = 0; + sp->some_taken = 1; + *locked = 1; + *giveup = 1; + to_move = 0; + goto out_of; } } some_taken = sp->some_taken; @@ -11533,6 +11556,7 @@ send_forward_tsn(struct sctp_tcb *stcb, { struct sctp_tmit_chunk *chk; struct sctp_forward_tsn_chunk *fwdtsn; + uint32_t advance_peer_ack_point; SCTP_TCB_LOCK_ASSERT(stcb); TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) { @@ -11610,11 +11634,23 @@ send_forward_tsn(struct sctp_tcb *stcb, /* trim to a mtu size */ cnt_of_space = asoc->smallest_mtu - ovh; } + if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_TRY_ADVANCE) { + sctp_misc_ints(SCTP_FWD_TSN_CHECK, + 0xff, 0, cnt_of_skipped, + asoc->advanced_peer_ack_point); + + } + advance_peer_ack_point = asoc->advanced_peer_ack_point; if (cnt_of_space < space_needed) { /*- * ok we must trim down the chunk by lowering the * advance peer ack point. */ + if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_TRY_ADVANCE) { + sctp_misc_ints(SCTP_FWD_TSN_CHECK, + 0xff, 0xff, cnt_of_space, + space_needed); + } cnt_of_skipped = (cnt_of_space - ((sizeof(struct sctp_forward_tsn_chunk)) / sizeof(struct sctp_strseq))); @@ -11627,12 +11663,17 @@ send_forward_tsn(struct sctp_tcb *stcb, tp1 = TAILQ_NEXT(at, sctp_next); at = tp1; } + if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_TRY_ADVANCE) { + sctp_misc_ints(SCTP_FWD_TSN_CHECK, + 0xff, cnt_of_skipped, at->rec.data.TSN_seq, + asoc->advanced_peer_ack_point); + } last = at; /*- * last now points to last one I can report, update * peer ack point */ - asoc->advanced_peer_ack_point = last->rec.data.TSN_seq; + advance_peer_ack_point = last->rec.data.TSN_seq; space_needed -= (cnt_of_skipped * sizeof(struct sctp_strseq)); } chk->send_size = space_needed; @@ -11641,7 +11682,7 @@ send_forward_tsn(struct sctp_tcb *stcb, fwdtsn->ch.chunk_length = htons(chk->send_size); fwdtsn->ch.chunk_flags = 0; fwdtsn->ch.chunk_type = SCTP_FORWARD_CUM_TSN; - fwdtsn->new_cumulative_tsn = htonl(asoc->advanced_peer_ack_point); + fwdtsn->new_cumulative_tsn = htonl(advance_peer_ack_point); chk->send_size = (sizeof(struct sctp_forward_tsn_chunk) + (cnt_of_skipped * sizeof(struct sctp_strseq))); SCTP_BUF_LEN(chk->data) = chk->send_size; @@ -11672,6 +11713,9 @@ send_forward_tsn(struct sctp_tcb *stcb, at = tp1; continue; } + if (at->rec.data.TSN_seq == advance_peer_ack_point) { + at->rec.data.fwd_tsn_cnt = 0; + } strseq->stream = ntohs(at->rec.data.stream_number); strseq->sequence = ntohs(at->rec.data.stream_seq); strseq++; diff --git a/sys/netinet/sctp_timer.c b/sys/netinet/sctp_timer.c index 1d2d5f262e62..477c0b13e45e 100644 --- a/sys/netinet/sctp_timer.c +++ b/sys/netinet/sctp_timer.c @@ -767,7 +767,7 @@ sctp_mark_all_for_resend(struct sctp_tcb *stcb, (void)sctp_release_pr_sctp_chunk(stcb, chk, (SCTP_RESPONSE_TO_USER_REQ | SCTP_NOTIFY_DATAGRAM_SENT), - &stcb->asoc.sent_queue, SCTP_SO_NOT_LOCKED); + SCTP_SO_NOT_LOCKED); } continue; } @@ -779,7 +779,7 @@ sctp_mark_all_for_resend(struct sctp_tcb *stcb, (void)sctp_release_pr_sctp_chunk(stcb, chk, (SCTP_RESPONSE_TO_USER_REQ | SCTP_NOTIFY_DATAGRAM_SENT), - &stcb->asoc.sent_queue, SCTP_SO_NOT_LOCKED); + SCTP_SO_NOT_LOCKED); } continue; } diff --git a/sys/netinet/sctp_var.h b/sys/netinet/sctp_var.h index 934af0054af0..1ccccf3d3c48 100644 --- a/sys/netinet/sctp_var.h +++ b/sys/netinet/sctp_var.h @@ -96,7 +96,8 @@ extern struct pr_usrreqs sctp_usrreqs; #define sctp_alloc_a_strmoq(_stcb, _strmoq) { \ (_strmoq) = SCTP_ZONE_GET(SCTP_BASE_INFO(ipi_zone_strmoq), struct sctp_stream_queue_pending); \ - if ((_strmoq)) { \ + if ((_strmoq)) { \ + memset(_strmoq, 0, sizeof(struct sctp_stream_queue_pending)); \ SCTP_INCR_STRMOQ_COUNT(); \ (_strmoq)->holds_key_ref = 0; \ } \ @@ -267,6 +268,7 @@ extern struct pr_usrreqs sctp_usrreqs; #else #define sctp_total_flight_decrease(stcb, tp1) do { \ + tp1->window_probe = 0; \ if (stcb->asoc.total_flight >= tp1->book_size) { \ stcb->asoc.total_flight -= tp1->book_size; \ if (stcb->asoc.total_flight_count > 0) \ diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c index 1eac98a762e8..42a47ac7964b 100644 --- a/sys/netinet/sctputil.c +++ b/sys/netinet/sctputil.c @@ -3686,10 +3686,10 @@ sctp_report_all_outbound(struct sctp_tcb *stcb, int holds_lock, int so_locked while (chk) { TAILQ_REMOVE(&asoc->sent_queue, chk, sctp_next); asoc->sent_queue_cnt--; - sctp_free_bufspace(stcb, asoc, chk, 1); - sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, - SCTP_NOTIFY_DATAGRAM_SENT, chk, so_locked); - if (chk->data) { + if (chk->data != NULL) { + sctp_free_bufspace(stcb, asoc, chk, 1); + sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, + SCTP_NOTIFY_DATAGRAM_SENT, chk, so_locked); sctp_m_freem(chk->data); chk->data = NULL; } @@ -3704,9 +3704,10 @@ sctp_report_all_outbound(struct sctp_tcb *stcb, int holds_lock, int so_locked while (chk) { TAILQ_REMOVE(&asoc->send_queue, chk, sctp_next); asoc->send_queue_cnt--; - sctp_free_bufspace(stcb, asoc, chk, 1); - sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, SCTP_NOTIFY_DATAGRAM_UNSENT, chk, so_locked); - if (chk->data) { + if (chk->data != NULL) { + sctp_free_bufspace(stcb, asoc, chk, 1); + sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, + SCTP_NOTIFY_DATAGRAM_UNSENT, chk, so_locked); sctp_m_freem(chk->data); chk->data = NULL; } @@ -4630,64 +4631,46 @@ sctp_free_bufspace(struct sctp_tcb *stcb, struct sctp_association *asoc, int sctp_release_pr_sctp_chunk(struct sctp_tcb *stcb, struct sctp_tmit_chunk *tp1, - int reason, struct sctpchunk_listhead *queue, int so_locked + int reason, int so_locked #if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING) SCTP_UNUSED #endif ) { + struct sctp_stream_out *strq; + struct sctp_tmit_chunk *chk = NULL; + struct sctp_stream_queue_pending *sp; + uint16_t stream = 0, seq = 0; + uint8_t foundeom = 0; int ret_sz = 0; int notdone; - uint8_t foundeom = 0; + int do_wakeup_routine = 0; + stream = tp1->rec.data.stream_number; + seq = tp1->rec.data.stream_seq; do { ret_sz += tp1->book_size; tp1->sent = SCTP_FORWARD_TSN_SKIP; - if (tp1->data) { + if (tp1->data != NULL) { #if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) struct socket *so; #endif + printf("Release PR-SCTP chunk tsn:%u flags:%x\n", + tp1->rec.data.TSN_seq, + (unsigned int)tp1->rec.data.rcv_flags); sctp_free_bufspace(stcb, &stcb->asoc, tp1, 1); sctp_flight_size_decrease(tp1); sctp_total_flight_decrease(stcb, tp1); + stcb->asoc.peers_rwnd += tp1->send_size; + stcb->asoc.peers_rwnd += SCTP_BASE_SYSCTL(sctp_peer_chunk_oh); sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, reason, tp1, so_locked); sctp_m_freem(tp1->data); tp1->data = NULL; -#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) - so = SCTP_INP_SO(stcb->sctp_ep); - if (!so_locked) { - atomic_add_int(&stcb->asoc.refcnt, 1); - SCTP_TCB_UNLOCK(stcb); - SCTP_SOCKET_LOCK(so, 1); - SCTP_TCB_LOCK(stcb); - atomic_subtract_int(&stcb->asoc.refcnt, 1); - if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) { - /* - * assoc was freed while we were - * unlocked - */ - SCTP_SOCKET_UNLOCK(so, 1); - return (ret_sz); - } + do_wakeup_routine = 1; + if (PR_SCTP_BUF_ENABLED(tp1->flags)) { + stcb->asoc.sent_queue_cnt_removeable--; } -#endif - sctp_sowwakeup(stcb->sctp_ep, stcb->sctp_socket); -#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) - if (!so_locked) { - SCTP_SOCKET_UNLOCK(so, 1); - } -#endif - } - if (PR_SCTP_BUF_ENABLED(tp1->flags)) { - stcb->asoc.sent_queue_cnt_removeable--; - } - if (queue == &stcb->asoc.send_queue) { - TAILQ_REMOVE(&stcb->asoc.send_queue, tp1, sctp_next); - /* on to the sent queue */ - TAILQ_INSERT_TAIL(&stcb->asoc.sent_queue, tp1, - sctp_next); - stcb->asoc.sent_queue_cnt++; } if ((tp1->rec.data.rcv_flags & SCTP_DATA_NOT_FRAG) == SCTP_DATA_NOT_FRAG) { @@ -4707,23 +4690,147 @@ sctp_release_pr_sctp_chunk(struct sctp_tcb *stcb, struct sctp_tmit_chunk *tp1, tp1 = TAILQ_NEXT(tp1, sctp_next); } } while (tp1 && notdone); - if ((foundeom == 0) && (queue == &stcb->asoc.sent_queue)) { + if (foundeom == 0) { /* * The multi-part message was scattered across the send and * sent queue. */ +next_on_sent: tp1 = TAILQ_FIRST(&stcb->asoc.send_queue); /* * recurse throught the send_queue too, starting at the * beginning. */ - if (tp1) { - ret_sz += sctp_release_pr_sctp_chunk(stcb, tp1, reason, - &stcb->asoc.send_queue, so_locked); - } else { - SCTP_PRINTF("hmm, nothing on the send queue and no EOM?\n"); + if ((tp1) && + (tp1->rec.data.stream_number == stream) && + (tp1->rec.data.stream_seq == seq) + ) { + /* + * save to chk in case we have some on stream out + * queue. If so and we have an un-transmitted one we + * don't have to fudge the TSN. + */ + chk = tp1; + ret_sz += tp1->book_size; + sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, reason, tp1, so_locked); + sctp_free_bufspace(stcb, &stcb->asoc, tp1, 1); + sctp_m_freem(tp1->data); + tp1->data = NULL; + if (tp1->rec.data.rcv_flags & SCTP_DATA_LAST_FRAG) { + foundeom = 1; + } + do_wakeup_routine = 1; + tp1->sent = SCTP_FORWARD_TSN_SKIP; + TAILQ_REMOVE(&stcb->asoc.send_queue, tp1, sctp_next); + /* + * on to the sent queue so we can wait for it to be + * passed by. + */ + TAILQ_INSERT_TAIL(&stcb->asoc.sent_queue, tp1, + sctp_next); + stcb->asoc.send_queue_cnt--; + stcb->asoc.sent_queue_cnt++; + goto next_on_sent; } } + if (foundeom == 0) { + /* + * Still no eom found. That means there is stuff left on the + * stream out queue.. yuck. + */ + strq = &stcb->asoc.strmout[stream]; + SCTP_TCB_SEND_LOCK(stcb); + sp = TAILQ_FIRST(&strq->outqueue); + while (sp->strseq <= seq) { + /* Check if its our SEQ */ + if (sp->strseq == seq) { + sp->discard_rest = 1; + /* + * We may need to put a chunk on the queue + * that holds the TSN that would have been + * sent with the LAST bit. + */ + if (chk == NULL) { + /* Yep, we have to */ + sctp_alloc_a_chunk(stcb, chk); + if (chk == NULL) { + /* + * we are hosed. All we can + * do is nothing.. which + * will cause an abort if + * the peer is paying + * attention. + */ + goto oh_well; + } + memset(chk, 0, sizeof(*chk)); + chk->rec.data.rcv_flags = SCTP_DATA_LAST_FRAG; + chk->sent = SCTP_FORWARD_TSN_SKIP; + chk->asoc = &stcb->asoc; + chk->rec.data.stream_seq = sp->strseq; + chk->rec.data.stream_number = sp->stream; + chk->rec.data.payloadtype = sp->ppid; + chk->rec.data.context = sp->context; + chk->flags = sp->act_flags; + chk->addr_over = sp->addr_over; + chk->whoTo = sp->net; + atomic_add_int(&chk->whoTo->ref_count, 1); + chk->rec.data.TSN_seq = atomic_fetchadd_int(&stcb->asoc.sending_seq, 1); + stcb->asoc.pr_sctp_cnt++; + chk->pr_sctp_on = 1; + TAILQ_INSERT_TAIL(&stcb->asoc.sent_queue, chk, sctp_next); + stcb->asoc.sent_queue_cnt++; + } else { + chk->rec.data.rcv_flags |= SCTP_DATA_LAST_FRAG; + } + oh_well: + if (sp->data) { + /* + * Pull any data to free up the SB + * and allow sender to "add more" + * whilc we will throw away :-) + */ + sctp_free_spbufspace(stcb, &stcb->asoc, + sp); + ret_sz += sp->length; + do_wakeup_routine = 1; + sp->some_taken = 1; + sctp_m_freem(sp->data); + sp->length = 0; + sp->data = NULL; + sp->tail_mbuf = NULL; + } + break; + } else { + /* Next one please */ + sp = TAILQ_NEXT(sp, next); + } + } /* End while */ + SCTP_TCB_SEND_UNLOCK(stcb); + } + if (do_wakeup_routine) { +#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) + so = SCTP_INP_SO(stcb->sctp_ep); + if (!so_locked) { + atomic_add_int(&stcb->asoc.refcnt, 1); + SCTP_TCB_UNLOCK(stcb); + SCTP_SOCKET_LOCK(so, 1); + SCTP_TCB_LOCK(stcb); + atomic_subtract_int(&stcb->asoc.refcnt, 1); + if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) { + /* assoc was freed while we were unlocked */ + SCTP_SOCKET_UNLOCK(so, 1); + return (ret_sz); + } + } +#endif + sctp_sowwakeup(stcb->sctp_ep, stcb->sctp_socket); +#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) + if (!so_locked) { + SCTP_SOCKET_UNLOCK(so, 1); + } +#endif + } return (ret_sz); } diff --git a/sys/netinet/sctputil.h b/sys/netinet/sctputil.h index 56c672954cce..c5a2ad9abf51 100644 --- a/sys/netinet/sctputil.h +++ b/sys/netinet/sctputil.h @@ -237,7 +237,7 @@ sctp_notify_partial_delivery_indication(struct sctp_tcb *stcb, int sctp_release_pr_sctp_chunk(struct sctp_tcb *, struct sctp_tmit_chunk *, - int, struct sctpchunk_listhead *, int + int, int #if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING) SCTP_UNUSED #endif @@ -287,7 +287,6 @@ do { \ #define sctp_free_spbufspace(stcb, asoc, sp) \ do { \ if (sp->data != NULL) { \ - atomic_subtract_int(&(asoc)->chunks_on_out_queue, 1); \ if ((asoc)->total_output_queue_size >= sp->length) { \ atomic_subtract_int(&(asoc)->total_output_queue_size, sp->length); \ } else { \