summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrrs <rrs@FreeBSD.org>2006-11-11 22:44:12 +0000
committerrrs <rrs@FreeBSD.org>2006-11-11 22:44:12 +0000
commitfb5651e047ba819adc96c9f9e76b4559123edf34 (patch)
tree7a67d3389c46e84245fca92a06dcb77f66dbafcb
parent26d0f2fab6f6582478cd2a21695844600a1bd490 (diff)
downloadFreeBSD-src-fb5651e047ba819adc96c9f9e76b4559123edf34.zip
FreeBSD-src-fb5651e047ba819adc96c9f9e76b4559123edf34.tar.gz
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
-rw-r--r--sys/netinet/sctp_constants.h1
-rw-r--r--sys/netinet/sctp_input.c50
-rw-r--r--sys/netinet/sctp_output.c13
-rw-r--r--sys/netinet/sctputil.c16
-rw-r--r--sys/netinet/sctputil.h2
5 files changed, 62 insertions, 20 deletions
diff --git a/sys/netinet/sctp_constants.h b/sys/netinet/sctp_constants.h
index 3e927bb..d11d380 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 86ed9f8..69d441c 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->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;
- 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;
+ 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 78b9a3e..e71e5ac 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 f98a81e..0f8fa0b 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 ef0e9a4..06f6218 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 *);
OpenPOWER on IntegriCloud