changeset 12721:4bd66ee3b274

6944465 SCTP should be more robust when the peer does not conform to the standard
author Anil udupa <anil.udupa@sun.com>
date Mon, 28 Jun 2010 16:53:55 -0700
parents 3db6e0082404
children 0cc41f673f9d
files usr/src/uts/common/inet/sctp/sctp_impl.h usr/src/uts/common/inet/sctp/sctp_input.c usr/src/uts/common/inet/sctp/sctp_shutdown.c
diffstat 3 files changed, 89 insertions(+), 40 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/common/inet/sctp/sctp_impl.h	Mon Jun 28 16:04:11 2010 -0700
+++ b/usr/src/uts/common/inet/sctp/sctp_impl.h	Mon Jun 28 16:53:55 2010 -0700
@@ -1080,6 +1080,7 @@
 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 */
 
--- a/usr/src/uts/common/inet/sctp/sctp_input.c	Mon Jun 28 16:04:11 2010 -0700
+++ b/usr/src/uts/common/inet/sctp/sctp_input.c	Mon Jun 28 16:53:55 2010 -0700
@@ -3615,6 +3615,7 @@
 	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 @@
 			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 @@
 					    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 @@
 	/* 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;
--- a/usr/src/uts/common/inet/sctp/sctp_shutdown.c	Mon Jun 28 16:04:11 2010 -0700
+++ b/usr/src/uts/common/inet/sctp/sctp_shutdown.c	Mon Jun 28 16:53:55 2010 -0700
@@ -147,10 +147,8 @@
     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 @@
 		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 @@
 	(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);
+}