summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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