summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnil udupa <anil.udupa@sun.com>2010-06-28 16:53:55 -0700
committerAnil udupa <anil.udupa@sun.com>2010-06-28 16:53:55 -0700
commit3e1dae9f7bdcf417fc72a039b910df3051a811e5 (patch)
tree4a8fdd6f78982a073691422a2b82a3beab627cc4
parent4f14b0f29aa144cc03efdde5508ae126ae197acf (diff)
downloadillumos-joyent-3e1dae9f7bdcf417fc72a039b910df3051a811e5.tar.gz
6944465 SCTP should be more robust when the peer does not conform to the standard
-rw-r--r--usr/src/uts/common/inet/sctp/sctp_impl.h1
-rw-r--r--usr/src/uts/common/inet/sctp/sctp_input.c31
-rw-r--r--usr/src/uts/common/inet/sctp/sctp_shutdown.c97
3 files changed, 89 insertions, 40 deletions
diff --git a/usr/src/uts/common/inet/sctp/sctp_impl.h b/usr/src/uts/common/inet/sctp/sctp_impl.h
index 8dc1eb865d..b03d25c8f1 100644
--- a/usr/src/uts/common/inet/sctp/sctp_impl.h
+++ b/usr/src/uts/common/inet/sctp/sctp_impl.h
@@ -1080,6 +1080,7 @@ extern int sctp_xmit_list_clean(sctp_t *, ssize_t);
extern void sctp_zap_addrs(sctp_t *);
extern void sctp_zap_faddrs(sctp_t *, int);
extern sctp_chunk_hdr_t *sctp_first_chunk(uchar_t *, ssize_t);
+extern void sctp_send_shutdown_ack(sctp_t *, sctp_faddr_t *, boolean_t);
/* Contract private interface between SCTP and Clustering - PSARC/2005/602 */
diff --git a/usr/src/uts/common/inet/sctp/sctp_input.c b/usr/src/uts/common/inet/sctp/sctp_input.c
index d5d376e581..0dfdf43fba 100644
--- a/usr/src/uts/common/inet/sctp/sctp_input.c
+++ b/usr/src/uts/common/inet/sctp/sctp_input.c
@@ -3615,6 +3615,7 @@ sctp_input_data(sctp_t *sctp, mblk_t *mp, ip_recv_attr_t *ira)
pid_t cpid;
uchar_t *rptr;
conn_t *connp = sctp->sctp_connp;
+ boolean_t shutdown_ack_needed = B_FALSE;
ASSERT(DB_TYPE(mp) == M_DATA);
ASSERT(ira->ira_ill == NULL);
@@ -4280,6 +4281,18 @@ sctp_input_data(sctp_t *sctp, mblk_t *mp, ip_recv_attr_t *ira)
case CHUNK_SHUTDOWN:
trysend = sctp_shutdown_received(sctp, ch,
B_FALSE, B_FALSE, fp);
+ /*
+ * shutdown_ack_needed may have been set as
+ * mentioned in the case CHUNK_SACK below.
+ * If sctp_shutdown_received() above found
+ * the xmit queue empty the SHUTDOWN ACK chunk
+ * has already been sent (or scheduled to be
+ * sent on the timer) and the SCTP state
+ * changed, so reset shutdown_ack_needed.
+ */
+ if (shutdown_ack_needed && (sctp->sctp_state ==
+ SCTPS_SHUTDOWN_ACK_SENT))
+ shutdown_ack_needed = B_FALSE;
break;
case CHUNK_SACK:
trysend = sctp_got_sack(sctp, ch);
@@ -4292,6 +4305,20 @@ sctp_input_data(sctp_t *sctp, mblk_t *mp, ip_recv_attr_t *ira)
ECONNABORTED);
goto done;
}
+
+ /*
+ * All data acknowledgement after a shutdown
+ * should be done with SHUTDOWN chunk.
+ * However some peer SCTP do not conform with
+ * this and can unexpectedly send a SACK chunk.
+ * If all data are acknowledged, set
+ * shutdown_ack_needed here indicating that
+ * SHUTDOWN ACK needs to be sent later by
+ * sctp_send_shutdown_ack().
+ */
+ if ((sctp->sctp_xmit_head == NULL) &&
+ (sctp->sctp_xmit_unsent == NULL))
+ shutdown_ack_needed = B_TRUE;
break;
case CHUNK_ABORT:
sctp_process_abort(sctp, ch, ECONNRESET);
@@ -4327,6 +4354,10 @@ sctp_input_data(sctp_t *sctp, mblk_t *mp, ip_recv_attr_t *ira)
/* Finished processing all chunks in packet */
nomorechunks:
+
+ if (shutdown_ack_needed)
+ sctp_send_shutdown_ack(sctp, fp, B_FALSE);
+
/* SACK if necessary */
if (gotdata) {
boolean_t sack_sent;
diff --git a/usr/src/uts/common/inet/sctp/sctp_shutdown.c b/usr/src/uts/common/inet/sctp/sctp_shutdown.c
index c1b648a4b8..88a65e386d 100644
--- a/usr/src/uts/common/inet/sctp/sctp_shutdown.c
+++ b/usr/src/uts/common/inet/sctp/sctp_shutdown.c
@@ -147,10 +147,8 @@ sctp_shutdown_received(sctp_t *sctp, sctp_chunk_hdr_t *sch, boolean_t crwsd,
boolean_t rexmit, sctp_faddr_t *fp)
{
mblk_t *samp;
- sctp_chunk_hdr_t *sach;
uint32_t *tsn;
int trysend = 0;
- sctp_stack_t *sctps = sctp->sctp_sctps;
if (sctp->sctp_state != SCTPS_SHUTDOWN_ACK_SENT)
sctp->sctp_state = SCTPS_SHUTDOWN_RECEIVED;
@@ -184,45 +182,8 @@ sctp_shutdown_received(sctp_t *sctp, sctp_chunk_hdr_t *sch, boolean_t crwsd,
else
fp = sctp_rotate_faddr(sctp, sctp->sctp_shutdown_faddr);
}
- sctp->sctp_shutdown_faddr = fp;
-
- samp = sctp_make_mp(sctp, fp, sizeof (*sach));
- if (samp == NULL) {
- SCTP_KSTAT(sctps, sctp_send_shutdown_ack_failed);
- goto dotimer;
- }
-
- sach = (sctp_chunk_hdr_t *)samp->b_wptr;
- sach->sch_id = CHUNK_SHUTDOWN_ACK;
- sach->sch_flags = 0;
- sach->sch_len = htons(sizeof (*sach));
-
- samp->b_wptr += sizeof (*sach);
-
- /*
- * bundle a "cookie received while shutting down" error if
- * the caller asks for it.
- */
- if (crwsd) {
- mblk_t *errmp;
-
- errmp = sctp_make_err(sctp, SCTP_ERR_COOKIE_SHUT, NULL, 0);
- if (errmp != NULL) {
- linkb(samp, errmp);
- BUMP_LOCAL(sctp->sctp_obchunks);
- }
- }
-
- BUMP_LOCAL(sctp->sctp_obchunks);
-
- sctp_set_iplen(sctp, samp, fp->ixa);
- (void) conn_ip_output(samp, fp->ixa);
- BUMP_LOCAL(sctp->sctp_opkts);
-dotimer:
- sctp->sctp_state = SCTPS_SHUTDOWN_ACK_SENT;
- SCTP_FADDR_TIMER_RESTART(sctp, sctp->sctp_current,
- sctp->sctp_current->rto);
+ sctp_send_shutdown_ack(sctp, fp, crwsd);
return (trysend);
}
@@ -396,3 +357,59 @@ sctp_ootb_shutdown_ack(mblk_t *mp, uint_t ip_hdr_len, ip_recv_attr_t *ira,
(void) ip_output_simple(mp, &ixas);
ixa_cleanup(&ixas);
}
+
+/*
+ * Called from sctp_input_data() and sctp_shutdown_received().
+ * Send a SHUTDOWN ACK chunk to the peer SCTP endpoint and change SCTP state.
+ * This should be done after all data (unacked and unsend) has been
+ * acknowledged.
+ */
+void
+sctp_send_shutdown_ack(sctp_t *sctp, sctp_faddr_t *fp, boolean_t crwsd)
+{
+ mblk_t *samp;
+ sctp_chunk_hdr_t *sach;
+ sctp_stack_t *sctps = sctp->sctp_sctps;
+
+ ASSERT(sctp->sctp_xmit_unacked == NULL);
+ ASSERT(sctp->sctp_lastack_rxd == (sctp->sctp_ltsn - 1));
+ ASSERT(fp != NULL);
+
+ sctp->sctp_shutdown_faddr = fp;
+
+ samp = sctp_make_mp(sctp, fp, sizeof (*sach));
+ if (samp == NULL) {
+ SCTP_KSTAT(sctps, sctp_send_shutdown_ack_failed);
+ goto dotimer;
+ }
+
+ sach = (sctp_chunk_hdr_t *)samp->b_wptr;
+ sach->sch_id = CHUNK_SHUTDOWN_ACK;
+ sach->sch_flags = 0;
+ sach->sch_len = htons(sizeof (*sach));
+
+ samp->b_wptr += sizeof (*sach);
+ /*
+ * bundle a "cookie received while shutting down" error if
+ * the caller asks for it.
+ */
+ if (crwsd) {
+ mblk_t *errmp;
+
+ errmp = sctp_make_err(sctp, SCTP_ERR_COOKIE_SHUT, NULL, 0);
+ if (errmp != NULL) {
+ linkb(samp, errmp);
+ BUMP_LOCAL(sctp->sctp_obchunks);
+ }
+ }
+
+ BUMP_LOCAL(sctp->sctp_obchunks);
+
+ sctp_set_iplen(sctp, samp, fp->ixa);
+ (void) conn_ip_output(samp, fp->ixa);
+ BUMP_LOCAL(sctp->sctp_opkts);
+
+dotimer:
+ sctp->sctp_state = SCTPS_SHUTDOWN_ACK_SENT;
+ SCTP_FADDR_TIMER_RESTART(sctp, fp, fp->rto);
+}