From fb5651e047ba819adc96c9f9e76b4559123edf34 Mon Sep 17 00:00:00 2001 From: rrs Date: Sat, 11 Nov 2006 22:44:12 +0000 Subject: [PATCH] In a true restart case, the send_lock was not being aquired. This meant that when we cleanup the outbound we may have one in transit to be added with the old sequence number. This is bad since then we loose a message :( Also the report_outbound needed to have the right lock when its called which it did not.. I added the lock with of course a flag since we want to have the lock before we call it in the restart case. This also fixed the FIX ME case where, in the cookie collision case, we mark for retransmit any that were bundled with the cookie that was dropped. This also means changes to the output routine so we can assure getting the COOKIE-ACK sent BEFORE we retransmit the Data. Approved by: gnn --- sys/netinet/sctp_constants.h | 1 + sys/netinet/sctp_input.c | 52 +++++++++++++++++++++++++++--------- sys/netinet/sctp_output.c | 13 +++++++-- sys/netinet/sctputil.c | 16 +++++++---- sys/netinet/sctputil.h | 2 +- 5 files changed, 63 insertions(+), 21 deletions(-) diff --git a/sys/netinet/sctp_constants.h b/sys/netinet/sctp_constants.h index 3e927bbed347..d11d380ca452 100644 --- a/sys/netinet/sctp_constants.h +++ b/sys/netinet/sctp_constants.h @@ -307,6 +307,7 @@ __FBSDID("$FreeBSD$"); #define SCTP_OUTPUT_FROM_EARLY_FR_TMR 11 #define SCTP_OUTPUT_FROM_STRRST_REQ 12 #define SCTP_OUTPUT_FROM_USR_RCVD 13 +#define SCTP_OUTPUT_FROM_COOKIE_ACK 14 /* SCTP chunk types are moved sctp.h for application (NAT, FW) use */ /* align to 32-bit sizes */ diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c index 86ed9f83dcdc..69d441cdff7f 100644 --- a/sys/netinet/sctp_input.c +++ b/sys/netinet/sctp_input.c @@ -701,7 +701,7 @@ sctp_handle_shutdown_ack(struct sctp_shutdown_ack_chunk *cp, if (!TAILQ_EMPTY(&asoc->send_queue) || !TAILQ_EMPTY(&asoc->sent_queue) || !TAILQ_EMPTY(&asoc->out_wheel)) { - sctp_report_all_outbound(stcb); + sctp_report_all_outbound(stcb, 0); } /* stop the timer */ sctp_timer_stop(SCTP_TIMER_TYPE_SHUTDOWN, stcb->sctp_ep, stcb, net); @@ -1092,6 +1092,7 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset, int chk_length; int init_offset, initack_offset; int retval; + int spec_flag = 0; /* I know that the TCB is non-NULL from the caller */ asoc = &stcb->asoc; @@ -1271,18 +1272,31 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset, sctp_timer_start(SCTP_TIMER_TYPE_AUTOCLOSE, inp, stcb, NULL); } - /* - * FIX? Should we go out, in this case (if the seq numbers - * changed on the peer) and set any data to RETRANSMIT? - */ asoc->my_rwnd = ntohl(initack_cp->init.a_rwnd); - asoc->pre_open_streams = - ntohs(initack_cp->init.num_outbound_streams); - asoc->last_cwr_tsn = asoc->init_seq_number - 1; - asoc->asconf_seq_in = asoc->last_acked_seq = asoc->init_seq_number - 1; - asoc->str_reset_seq_in = asoc->init_seq_number; - asoc->advanced_peer_ack_point = asoc->last_acked_seq; + asoc->pre_open_streams = ntohs(initack_cp->init.num_outbound_streams); + /* Note last_cwr_tsn? where is this used? */ + asoc->last_cwr_tsn = asoc->init_seq_number - 1; + if (ntohl(init_cp->init.initiate_tag) != asoc->peer_vtag) { + /* + * Ok the peer probably discarded our data (if we + * echoed a cookie+data). So anything on the + * sent_queue should be marked for retransmit, we + * may not get something to kick us so it COULD + * still take a timeout to move these.. but it can't + * hurt to mark them. + */ + struct sctp_tmit_chunk *chk; + + TAILQ_FOREACH(chk, &stcb->asoc.sent_queue, sctp_next) { + if (chk->sent < SCTP_DATAGRAM_RESEND) { + chk->sent = SCTP_DATAGRAM_RESEND; + stcb->asoc.sent_queue_retran_cnt++; + spec_flag++; + } + } + + } /* process the INIT info (peer's info) */ retval = sctp_process_init(init_cp, stcb, net); if (retval < 0) { @@ -1316,6 +1330,16 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset, } sctp_stop_all_cookie_timers(stcb); sctp_send_cookie_ack(stcb); + if (spec_flag) { + /* + * only if we have retrans set do we do this. What + * this call does is get only the COOKIE-ACK out and + * then when we return the normal call to + * sctp_chunk_output will get the retrans out behind + * this. + */ + sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_COOKIE_ACK); + } return (stcb); } if ((ntohl(initack_cp->init.initiate_tag) != asoc->my_vtag && @@ -1336,7 +1360,8 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset, /* send up all the data */ - sctp_report_all_outbound(stcb); + SCTP_TCB_SEND_LOCK(stcb); + sctp_report_all_outbound(stcb, 1); /* process the INIT-ACK info (my info) */ asoc->my_vtag = ntohl(initack_cp->init.initiate_tag); @@ -1376,6 +1401,7 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset, memset(asoc->mapping_array, 0, asoc->mapping_array_size); /* process the INIT info (peer's info) */ + SCTP_TCB_SEND_UNLOCK(stcb); retval = sctp_process_init(init_cp, stcb, net); if (retval < 0) { return (NULL); @@ -2356,7 +2382,7 @@ sctp_handle_shutdown_complete(struct sctp_shutdown_complete_chunk *cp, if (!TAILQ_EMPTY(&asoc->send_queue) || !TAILQ_EMPTY(&asoc->sent_queue) || !TAILQ_EMPTY(&asoc->out_wheel)) { - sctp_report_all_outbound(stcb); + sctp_report_all_outbound(stcb, 0); } } /* stop the timer */ diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c index 78b9a3e42e94..e71e5acbdcaa 100644 --- a/sys/netinet/sctp_output.c +++ b/sys/netinet/sctp_output.c @@ -7164,7 +7164,6 @@ sctp_chunk_output(struct sctp_inpcb *inp, un_sent = (stcb->asoc.total_output_queue_size - stcb->asoc.total_flight); - if ((un_sent <= 0) && (TAILQ_EMPTY(&asoc->control_send_queue)) && (asoc->sent_queue_retran_cnt == 0)) { @@ -7184,7 +7183,17 @@ sctp_chunk_output(struct sctp_inpcb *inp, * Ok, it is retransmission time only, we send out only ONE * packet with a single call off to the retran code. */ - if (from_where != SCTP_OUTPUT_FROM_HB_TMR) { + if (from_where == SCTP_OUTPUT_FROM_COOKIE_ACK) { + /* + * Special hook for handling cookiess discarded by + * peer that carried data. Send cookie-ack only and + * then the next call with get the retran's. + */ + (void)sctp_med_chunk_output(inp, stcb, asoc, &num_out, &reason_code, 1, + &cwnd_full, from_where, + &now, &now_filled, frag_point); + return (0); + } else if (from_where != SCTP_OUTPUT_FROM_HB_TMR) { /* if its not from a HB then do it */ ret = sctp_chunk_retransmission(inp, stcb, asoc, &num_out, &now, &now_filled); } else { diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c index f98a81eacf7a..0f8fa0bc2e4d 100644 --- a/sys/netinet/sctputil.c +++ b/sys/netinet/sctputil.c @@ -3242,12 +3242,13 @@ sctp_ulp_notify(uint32_t notification, struct sctp_tcb *stcb, } void -sctp_report_all_outbound(struct sctp_tcb *stcb) +sctp_report_all_outbound(struct sctp_tcb *stcb, int holds_lock) { struct sctp_association *asoc; struct sctp_stream_out *outs; struct sctp_tmit_chunk *chk; struct sctp_stream_queue_pending *sp; + int i; asoc = &stcb->asoc; @@ -3257,9 +3258,12 @@ sctp_report_all_outbound(struct sctp_tcb *stcb) return; } /* now through all the gunk freeing chunks */ - - TAILQ_FOREACH(outs, &asoc->out_wheel, next_spoke) { - /* now clean up any chunks here */ + if (holds_lock == 0) + SCTP_TCB_SEND_LOCK(stcb); + for (i = 0; i < stcb->asoc.streamoutcnt; i++) { + /* For each stream */ + outs = &stcb->asoc.strmout[i]; + /* clean up any sends there */ stcb->asoc.locked_on_sending = NULL; sp = TAILQ_FIRST(&outs->outqueue); while (sp) { @@ -3338,6 +3342,8 @@ sctp_report_all_outbound(struct sctp_tcb *stcb) chk = TAILQ_FIRST(&asoc->sent_queue); } } + if (holds_lock == 0) + SCTP_TCB_SEND_UNLOCK(stcb); } void @@ -3350,7 +3356,7 @@ sctp_abort_notification(struct sctp_tcb *stcb, int error) return; } /* Tell them we lost the asoc */ - sctp_report_all_outbound(stcb); + sctp_report_all_outbound(stcb, 1); if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) || ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) && (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_CONNECTED))) { diff --git a/sys/netinet/sctputil.h b/sys/netinet/sctputil.h index ef0e9a4e35f1..06f621884d09 100644 --- a/sys/netinet/sctputil.h +++ b/sys/netinet/sctputil.h @@ -131,7 +131,7 @@ sctp_pull_off_control_to_new_inp(struct sctp_inpcb *old_inp, void sctp_stop_timers_for_shutdown(struct sctp_tcb *); -void sctp_report_all_outbound(struct sctp_tcb *); +void sctp_report_all_outbound(struct sctp_tcb *, int); int sctp_expand_mapping_array(struct sctp_association *);