changeset 4964:a9481fc76e88

6340684 SCTP does "packet amplification" 6596850 Processing malformed INIT chunk causes panic
author kcpoon
date Thu, 30 Aug 2007 08:00:29 -0700
parents ce6338ba4a73
children 4c42cf7a5c57
files usr/src/cmd/mdb/common/modules/sctp/sctp.c usr/src/uts/common/inet/sctp/sctp.c usr/src/uts/common/inet/sctp/sctp_common.c usr/src/uts/common/inet/sctp/sctp_error.c 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_timer.c
diffstat 7 files changed, 201 insertions(+), 73 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/mdb/common/modules/sctp/sctp.c	Thu Aug 30 07:43:53 2007 -0700
+++ b/usr/src/cmd/mdb/common/modules/sctp/sctp.c	Thu Aug 30 08:00:29 2007 -0700
@@ -785,7 +785,9 @@
 		    sctp.sctp_recovery_tsn, sctp.sctp_adv_pap);
 		mdb_printf("num_ostr\t%?hu\tostrcntrs\t%?p\n",
 		    sctp.sctp_num_ostr, sctp.sctp_ostrcntrs);
-		mdb_printf("pad_mp\t\t%?p\n", sctp.sctp_pad_mp);
+		mdb_printf("pad_mp\t\t%?p\terr_chunks\t%?p\n",
+		    sctp.sctp_pad_mp, sctp.sctp_err_chunks);
+		mdb_printf("err_len\t\t%?u\n", sctp.sctp_err_len);
 
 		mdb_printf("%<b>Default Send Parameters%</b>\n");
 		mdb_printf("def_stream\t%?u\tdef_flags\t%?x\n",
--- a/usr/src/uts/common/inet/sctp/sctp.c	Thu Aug 30 07:43:53 2007 -0700
+++ b/usr/src/uts/common/inet/sctp/sctp.c	Thu Aug 30 08:00:29 2007 -0700
@@ -753,6 +753,12 @@
 
 	sctp->sctp_shutdown_faddr = NULL;
 
+	if (sctp->sctp_err_chunks != NULL) {
+		freemsg(sctp->sctp_err_chunks);
+		sctp->sctp_err_chunks = NULL;
+		sctp->sctp_err_len = 0;
+	}
+
 	/* Clear all the bitfields. */
 	bzero(&sctp->sctp_bits, sizeof (sctp->sctp_bits));
 
--- a/usr/src/uts/common/inet/sctp/sctp_common.c	Thu Aug 30 07:43:53 2007 -0700
+++ b/usr/src/uts/common/inet/sctp/sctp_common.c	Thu Aug 30 08:00:29 2007 -0700
@@ -1554,7 +1554,7 @@
 sctp_secure_restart_check(mblk_t *pkt, sctp_chunk_hdr_t *ich, uint32_t ports,
     int sleep, sctp_stack_t *sctps)
 {
-	sctp_faddr_t *fp, *fpa, *fphead = NULL;
+	sctp_faddr_t *fp, *fphead = NULL;
 	sctp_parm_hdr_t *ph;
 	ssize_t remaining;
 	int isv4;
@@ -1589,6 +1589,8 @@
 	ph = (sctp_parm_hdr_t *)(init + 1);
 
 	while (ph != NULL) {
+		sctp_faddr_t *fpa = NULL;
+
 		/* params will have already been byteordered when validating */
 		if (ph->sph_type == htons(PARM_ADDR4)) {
 			if (remaining >= PARM_ADDR4_LEN) {
@@ -1597,7 +1599,7 @@
 				    (ph + 1), &addr);
 				fpa = kmem_cache_alloc(sctp_kmem_faddr_cache,
 				    sleep);
-				if (!fpa) {
+				if (fpa == NULL) {
 					goto done;
 				}
 				bzero(fpa, sizeof (*fpa));
@@ -1608,7 +1610,7 @@
 			if (remaining >= PARM_ADDR6_LEN) {
 				fpa = kmem_cache_alloc(sctp_kmem_faddr_cache,
 				    sleep);
-				if (!fpa) {
+				if (fpa == NULL) {
 					goto done;
 				}
 				bzero(fpa, sizeof (*fpa));
@@ -1616,18 +1618,14 @@
 				    sizeof (fpa->faddr));
 				fpa->next = NULL;
 			}
-		} else {
-			/* else not addr param; skip */
-			fpa = NULL;
 		}
 		/* link in the new addr, if it was an addr param */
-		if (fpa) {
-			if (!fphead) {
+		if (fpa != NULL) {
+			if (fphead == NULL) {
 				fphead = fpa;
-				fp = fphead;
 			} else {
-				fp->next = fpa;
-				fp = fpa;
+				fpa->next = fphead;
+				fphead = fpa;
 			}
 		}
 
@@ -1644,10 +1642,10 @@
 	 * in the list
 	 */
 	fp = sctp_lookup_faddr_nosctp(fphead, hdraddr);
-	if (!fp) {
+	if (fp == NULL) {
 		/* not included; add it now */
 		fp = kmem_cache_alloc(sctp_kmem_faddr_cache, sleep);
-		if (!fp) {
+		if (fp == NULL) {
 			goto done;
 		}
 		bzero(fp, sizeof (*fp));
--- a/usr/src/uts/common/inet/sctp/sctp_error.c	Thu Aug 30 07:43:53 2007 -0700
+++ b/usr/src/uts/common/inet/sctp/sctp_error.c	Thu Aug 30 08:00:29 2007 -0700
@@ -130,7 +130,7 @@
 		len = 0;
 	}
 	if ((len = sctp_link_abort(mp, SCTP_ERR_USER_ABORT, cause, len, 0,
-		tbit)) < 0) {
+	    tbit)) < 0) {
 		freemsg(mp);
 		return;
 	}
@@ -335,8 +335,8 @@
 	sctp_parm_hdr_t *eph;
 	int pad;
 
-	if ((pad = len % 4) != 0) {
-		pad = 4 - pad;
+	if ((pad = len % SCTP_ALIGN) != 0) {
+		pad = SCTP_ALIGN - pad;
 	}
 
 	elen = sizeof (*ecp) + sizeof (*eph) + len;
@@ -368,28 +368,88 @@
 	return (emp);
 }
 
+/*
+ * Called from sctp_input_data() to add one error chunk to the error
+ * chunks list.  The error chunks list will be processed at the end
+ * of sctp_input_data() by calling sctp_process_err().
+ */
 void
-sctp_send_err(sctp_t *sctp, mblk_t *emp, sctp_faddr_t *dest)
+sctp_add_err(sctp_t *sctp, uint16_t serror, void *details, size_t len,
+    sctp_faddr_t *dest)
 {
-	mblk_t	*sendmp;
-	sctp_stack_t	*sctps = sctp->sctp_sctps;
+	sctp_stack_t *sctps = sctp->sctp_sctps;
+	mblk_t *emp;
+	uint32_t emp_len;
+	uint32_t mss;
+	mblk_t *sendmp;
+	sctp_faddr_t *fp;
 
-	sendmp = sctp_make_sack(sctp, dest, NULL);
-	if (sendmp != NULL) {
-		linkb(sendmp, emp);
+	emp = sctp_make_err(sctp, serror, details, len);
+	if (emp == NULL)
+		return;
+	emp_len = MBLKL(emp);
+	if (sctp->sctp_err_chunks != NULL) {
+		fp = SCTP_CHUNK_DEST(sctp->sctp_err_chunks);
 	} else {
-		sendmp = sctp_make_mp(sctp, dest, 0);
-		if (sendmp == NULL) {
+		fp = dest;
+		SCTP_SET_CHUNK_DEST(emp, dest);
+	}
+	mss = fp->sfa_pmss;
+
+	/*
+	 * If the current output packet cannot include the new error chunk,
+	 * send out the current packet and then add the new error chunk
+	 * to the new output packet.
+	 */
+	if (sctp->sctp_err_len + emp_len > mss) {
+		if ((sendmp = sctp_make_mp(sctp, fp, 0)) == NULL) {
 			SCTP_KSTAT(sctps, sctp_send_err_failed);
-			freemsg(emp);
+			/* Just free the latest error chunk. */
+			freeb(emp);
 			return;
 		}
-		sendmp->b_cont = emp;
+		sendmp->b_cont = sctp->sctp_err_chunks;
+		sctp_set_iplen(sctp, sendmp);
+		sctp_add_sendq(sctp, sendmp);
+
+		sctp->sctp_err_chunks = emp;
+		sctp->sctp_err_len = emp_len;
+		SCTP_SET_CHUNK_DEST(emp, dest);
+	} else {
+		if (sctp->sctp_err_chunks != NULL)
+			linkb(sctp->sctp_err_chunks, emp);
+		else
+			sctp->sctp_err_chunks = emp;
+		sctp->sctp_err_len += emp_len;
 	}
+	/* Assume that we will send it out... */
 	BUMP_LOCAL(sctp->sctp_obchunks);
+}
 
+/*
+ * Called from sctp_input_data() to send out error chunks created during
+ * the processing of all the chunks in an incoming packet.
+ */
+void
+sctp_process_err(sctp_t *sctp)
+{
+	sctp_stack_t *sctps = sctp->sctp_sctps;
+	mblk_t *errmp;
+	mblk_t *sendmp;
+
+	ASSERT(sctp->sctp_err_chunks != NULL);
+	errmp = sctp->sctp_err_chunks;
+	if ((sendmp = sctp_make_mp(sctp, SCTP_CHUNK_DEST(errmp), 0)) == NULL) {
+		SCTP_KSTAT(sctps, sctp_send_err_failed);
+		freemsg(errmp);
+		goto done;
+	}
+	sendmp->b_cont = errmp;
 	sctp_set_iplen(sctp, sendmp);
 	sctp_add_sendq(sctp, sendmp);
+done:
+	sctp->sctp_err_chunks = NULL;
+	sctp->sctp_err_len = 0;
 }
 
 /*
--- a/usr/src/uts/common/inet/sctp/sctp_impl.h	Thu Aug 30 07:43:53 2007 -0700
+++ b/usr/src/uts/common/inet/sctp/sctp_impl.h	Thu Aug 30 08:00:29 2007 -0700
@@ -933,6 +933,8 @@
 	uint32_t	sctp_rxt_maxtsn;	/* Max TSN sent at time out */
 
 	int		sctp_pd_point;		/* Partial delivery point */
+	mblk_t		*sctp_err_chunks;	/* Error chunks */
+	uint32_t	sctp_err_len;		/* Total error chunks length */
 } sctp_t;
 
 #endif	/* (defined(_KERNEL) || defined(_KMEMUSER)) */
@@ -940,6 +942,8 @@
 extern void	sctp_ack_timer(sctp_t *);
 extern size_t	sctp_adaption_code_param(sctp_t *, uchar_t *);
 extern void	sctp_adaption_event(sctp_t *);
+extern void	sctp_add_err(sctp_t *, uint16_t, void *, size_t,
+		    sctp_faddr_t *);
 extern int	sctp_add_faddr(sctp_t *, in6_addr_t *, int, boolean_t);
 extern boolean_t sctp_add_ftsn_set(sctp_ftsn_set_t **, sctp_faddr_t *, mblk_t *,
 		    uint_t *, uint32_t *);
@@ -1073,6 +1077,7 @@
 extern void	sctp_partial_delivery_event(sctp_t *);
 extern int	sctp_process_cookie(sctp_t *, sctp_chunk_hdr_t *, mblk_t *,
 		    sctp_init_chunk_t **, sctp_hdr_t *, int *, in6_addr_t *);
+extern void	sctp_process_err(sctp_t *);
 extern void	sctp_process_heartbeat(sctp_t *, sctp_chunk_hdr_t *);
 extern void	sctp_process_sendq(sctp_t *);
 extern void	sctp_process_timer(sctp_t *);
@@ -1086,14 +1091,13 @@
 extern void	sctp_rexmit_timer(sctp_t *, sctp_faddr_t *);
 extern sctp_faddr_t *sctp_rotate_faddr(sctp_t *, sctp_faddr_t *);
 
-extern void	sctp_sack(sctp_t *, mblk_t *);
+extern boolean_t sctp_sack(sctp_t *, mblk_t *);
 extern int	sctp_secure_restart_check(mblk_t *, sctp_chunk_hdr_t *,
 		    uint32_t, int, sctp_stack_t *);
 extern void	sctp_send_abort(sctp_t *, uint32_t, uint16_t, char *, size_t,
 		    mblk_t *, int, boolean_t);
 extern void	sctp_send_cookie_ack(sctp_t *);
 extern void	sctp_send_cookie_echo(sctp_t *, sctp_chunk_hdr_t *, mblk_t *);
-extern void	sctp_send_err(sctp_t *, mblk_t *, sctp_faddr_t *);
 extern void	sctp_send_initack(sctp_t *, sctp_hdr_t *, sctp_chunk_hdr_t *,
 		    mblk_t *);
 extern void	sctp_send_shutdown(sctp_t *, int);
--- a/usr/src/uts/common/inet/sctp/sctp_input.c	Thu Aug 30 07:43:53 2007 -0700
+++ b/usr/src/uts/common/inet/sctp/sctp_input.c	Thu Aug 30 08:00:29 2007 -0700
@@ -383,8 +383,8 @@
 	/* If app asked for hopbyhop headers and it has changed ... */
 	if ((sctp->sctp_ipv6_recvancillary & SCTP_IPV6_RECVHOPOPTS) &&
 	    ip_cmpbuf(sctp->sctp_hopopts, sctp->sctp_hopoptslen,
-		(ipp->ipp_fields & IPPF_HOPOPTS),
-		ipp->ipp_hopopts, ipp->ipp_hopoptslen)) {
+	    (ipp->ipp_fields & IPPF_HOPOPTS),
+	    ipp->ipp_hopopts, ipp->ipp_hopoptslen)) {
 		optlen += sizeof (*cmsg) + ipp->ipp_hopoptslen -
 		    sctp->sctp_v6label_len;
 		if (hdrlen == 0)
@@ -399,8 +399,8 @@
 	/* If app asked for dst headers before routing headers ... */
 	if ((sctp->sctp_ipv6_recvancillary & SCTP_IPV6_RECVRTDSTOPTS) &&
 	    ip_cmpbuf(sctp->sctp_rtdstopts, sctp->sctp_rtdstoptslen,
-		(ipp->ipp_fields & IPPF_RTDSTOPTS),
-		ipp->ipp_rtdstopts, ipp->ipp_rtdstoptslen)) {
+	    (ipp->ipp_fields & IPPF_RTDSTOPTS),
+	    ipp->ipp_rtdstopts, ipp->ipp_rtdstoptslen)) {
 		optlen += sizeof (*cmsg) + ipp->ipp_rtdstoptslen;
 		if (hdrlen == 0)
 			hdrlen = sizeof (struct T_unitdata_ind);
@@ -430,8 +430,8 @@
 	/* If app asked for dest headers and it has changed ... */
 	if ((sctp->sctp_ipv6_recvancillary & SCTP_IPV6_RECVDSTOPTS) &&
 	    ip_cmpbuf(sctp->sctp_dstopts, sctp->sctp_dstoptslen,
-		(ipp->ipp_fields & IPPF_DSTOPTS),
-		ipp->ipp_dstopts, ipp->ipp_dstoptslen)) {
+	    (ipp->ipp_fields & IPPF_DSTOPTS),
+	    ipp->ipp_dstopts, ipp->ipp_dstoptslen)) {
 		optlen += sizeof (*cmsg) + ipp->ipp_dstoptslen;
 		if (hdrlen == 0)
 			hdrlen = sizeof (struct T_unitdata_ind);
@@ -1181,7 +1181,6 @@
 {
 	sctp_data_hdr_t *dc;
 	mblk_t *dmp, *pmp;
-	mblk_t *errmp;
 	sctp_instr_t *instr;
 	int ubit;
 	int isfrag;
@@ -1209,7 +1208,6 @@
 		sctp->sctp_force_sack = 1;				\
 	}
 
-	errmp = NULL;
 	dmp = NULL;
 
 	dc = (sctp_data_hdr_t *)ch;
@@ -1231,7 +1229,8 @@
 		for (sp = sctp->sctp_sack_info; sp; sp = sp->next) {
 			if (SEQ_GEQ(tsn, sp->begin) && SEQ_LEQ(tsn, sp->end)) {
 				dprint(4,
-				("sctp_data_chunk: dropping dup > cumtsn\n"));
+				    ("sctp_data_chunk: dropping dup > "
+				    "cumtsn\n"));
 				sctp->sctp_force_sack = 1;
 				sctp_add_dup(dc->sdh_tsn, dups);
 				return;
@@ -1265,11 +1264,9 @@
 		/* RESERVED to be ignored at the receiving end */
 		inval_parm[1] = 0;
 		/* ack and drop it */
-		errmp = sctp_make_err(sctp, SCTP_ERR_BAD_SID,
-		    (char *)inval_parm, sizeof (inval_parm));
+		sctp_add_err(sctp, SCTP_ERR_BAD_SID, inval_parm,
+		    sizeof (inval_parm), fp);
 		SCTP_ACK_IT(sctp, tsn);
-		if (errmp != NULL)
-			sctp_send_err(sctp, errmp, NULL);
 		return;
 	}
 
@@ -1364,7 +1361,7 @@
 			for (;;) {
 				idc = (sctp_data_hdr_t *)imblk->b_rptr;
 				if (SSN_GT(ntohs(idc->sdh_ssn),
-					ntohs(dc->sdh_ssn))) {
+				    ntohs(dc->sdh_ssn))) {
 					if (instr->istr_msgs == imblk) {
 						instr->istr_msgs = dmp;
 						dmp->b_next = imblk;
@@ -1615,6 +1612,8 @@
 	sctp_sack_chunk_t *sc;
 	int32_t acks_max;
 	sctp_stack_t	*sctps = sctp->sctp_sctps;
+	uint32_t	dups_len;
+	sctp_faddr_t	*fp;
 
 	if (sctp->sctp_force_sack) {
 		sctp->sctp_force_sack = 0;
@@ -1642,8 +1641,36 @@
 checks_done:
 	dprint(2, ("sctp_make_sack: acking %x\n", sctp->sctp_ftsn - 1));
 
+	if (dups != NULL)
+		dups_len = MBLKL(dups);
+	else
+		dups_len = 0;
 	slen = sizeof (*sch) + sizeof (*sc) +
 	    (sizeof (sctp_sack_frag_t) * sctp->sctp_sack_gaps);
+
+	/*
+	 * If there are error chunks, check and see if we can send the
+	 * SACK chunk and error chunks together in one packet.  If not,
+	 * send the error chunks out now.
+	 */
+	if (sctp->sctp_err_chunks != NULL) {
+		fp = SCTP_CHUNK_DEST(sctp->sctp_err_chunks);
+		if (sctp->sctp_err_len + slen + dups_len > fp->sfa_pmss) {
+			if ((smp = sctp_make_mp(sctp, fp, 0)) == NULL) {
+				SCTP_KSTAT(sctps, sctp_send_err_failed);
+				SCTP_KSTAT(sctps, sctp_send_sack_failed);
+				freemsg(sctp->sctp_err_chunks);
+				sctp->sctp_err_chunks = NULL;
+				sctp->sctp_err_len = 0;
+				return (NULL);
+			}
+			smp->b_cont = sctp->sctp_err_chunks;
+			sctp_set_iplen(sctp, smp);
+			sctp_add_sendq(sctp, smp);
+			sctp->sctp_err_chunks = NULL;
+			sctp->sctp_err_len = 0;
+		}
+	}
 	smp = sctp_make_mp(sctp, sendto, slen);
 	if (smp == NULL) {
 		SCTP_KSTAT(sctps, sctp_send_sack_failed);
@@ -1653,18 +1680,26 @@
 
 	sctp_fill_sack(sctp, smp->b_wptr, slen);
 	smp->b_wptr += slen;
-	if (dups) {
+	if (dups != NULL) {
 		sc = (sctp_sack_chunk_t *)(sch + 1);
-		sc->ssc_numdups = htons((dups->b_wptr - dups->b_rptr)
-		    / sizeof (uint32_t));
-		sch->sch_len = htons(slen + (dups->b_wptr - dups->b_rptr));
+		sc->ssc_numdups = htons(MBLKL(dups) / sizeof (uint32_t));
+		sch->sch_len = htons(slen + dups_len);
 		smp->b_cont = dups;
 	}
 
+	if (sctp->sctp_err_chunks != NULL) {
+		linkb(smp, sctp->sctp_err_chunks);
+		sctp->sctp_err_chunks = NULL;
+		sctp->sctp_err_len = 0;
+	}
 	return (smp);
 }
 
-void
+/*
+ * Check and see if we need to send a SACK chunk.  If it is needed,
+ * send it out.  Return true if a SACK chunk is sent, false otherwise.
+ */
+boolean_t
 sctp_sack(sctp_t *sctp, mblk_t *dups)
 {
 	mblk_t *smp;
@@ -1681,9 +1716,8 @@
 		/* The caller of sctp_sack() will not free the dups mblk. */
 		if (dups != NULL)
 			freeb(dups);
-		return;
+		return (B_FALSE);
 	}
-
 	sctp_set_iplen(sctp, smp);
 
 	dprint(2, ("sctp_sack: sending to %p %x:%x:%x:%x\n",
@@ -1694,6 +1728,7 @@
 
 	BUMP_MIB(&sctps->sctps_mib, sctpOutAck);
 	sctp_add_sendq(sctp, smp);
+	return (B_TRUE);
 }
 
 /*
@@ -2061,15 +2096,12 @@
 		ftsn_entry->ftsn_ssn = ntohs(ftsn_entry->ftsn_ssn);
 		if (ftsn_entry->ftsn_sid >= sctp->sctp_num_istr) {
 			uint16_t	inval_parm[2];
-			mblk_t		*errmp;
 
 			inval_parm[0] = htons(ftsn_entry->ftsn_sid);
 			/* RESERVED to be ignored at the receiving end */
 			inval_parm[1] = 0;
-			errmp = sctp_make_err(sctp, SCTP_ERR_BAD_SID,
-			    (char *)inval_parm, sizeof (inval_parm));
-			if (errmp != NULL)
-				sctp_send_err(sctp, errmp, NULL);
+			sctp_add_err(sctp, SCTP_ERR_BAD_SID, inval_parm,
+			    sizeof (inval_parm), fp);
 			ftsn_entry++;
 			remaining -= sizeof (*ftsn_entry);
 			continue;
@@ -2905,16 +2937,14 @@
 static int
 sctp_strange_chunk(sctp_t *sctp, sctp_chunk_hdr_t *ch, sctp_faddr_t *fp)
 {
-	mblk_t *errmp;
 	size_t len;
 
 	BUMP_LOCAL(sctp->sctp_ibchunks);
 	/* check top two bits for action required */
 	if (ch->sch_id & 0x40) {	/* also matches 0xc0 */
 		len = ntohs(ch->sch_len);
-		errmp = sctp_make_err(sctp, SCTP_ERR_UNREC_CHUNK, ch, len);
-		if (errmp != NULL)
-			sctp_send_err(sctp, errmp, fp);
+		sctp_add_err(sctp, SCTP_ERR_UNREC_CHUNK, ch, len, fp);
+
 		if ((ch->sch_id & 0xc0) == 0xc0) {
 			/* skip and continue */
 			return (1);
@@ -3473,6 +3503,7 @@
 	int64_t			now;
 	sctp_stack_t		*sctps = sctp->sctp_sctps;
 	ip_stack_t		*ipst = sctps->sctps_netstack->netstack_ip;
+	boolean_t		hb_already = B_FALSE;
 
 	if (DB_TYPE(mp) != M_DATA) {
 		ASSERT(DB_TYPE(mp) == M_CTL);
@@ -3669,7 +3700,16 @@
 				}
 				break;
 			case CHUNK_HEARTBEAT:
-				sctp_return_heartbeat(sctp, ch, mp);
+				if (!hb_already) {
+					/*
+					 * In any one packet, there should
+					 * only be one heartbeat chunk.  So
+					 * we should not process more than
+					 * once.
+					 */
+					sctp_return_heartbeat(sctp, ch, mp);
+					hb_already = B_TRUE;
+				}
 				break;
 			case CHUNK_HEARTBEAT_ACK:
 				sctp_process_heartbeat(sctp, ch);
@@ -3901,8 +3941,9 @@
 				sctp_send_cookie_ack(sctp);
 				sctp_stop_faddr_timers(sctp);
 				if (!SCTP_IS_DETACHED(sctp)) {
-				    sctp->sctp_ulp_connected(sctp->sctp_ulpd);
-				    sctp_set_ulp_prop(sctp);
+					sctp->sctp_ulp_connected(
+					    sctp->sctp_ulpd);
+					sctp_set_ulp_prop(sctp);
 				}
 				sctp->sctp_state = SCTPS_ESTABLISHED;
 				sctp->sctp_assoc_start_time = (uint32_t)lbolt;
@@ -3936,8 +3977,9 @@
 			switch (ch->sch_id) {
 			case CHUNK_COOKIE_ACK:
 				if (!SCTP_IS_DETACHED(sctp)) {
-				    sctp->sctp_ulp_connected(sctp->sctp_ulpd);
-				    sctp_set_ulp_prop(sctp);
+					sctp->sctp_ulp_connected(
+					    sctp->sctp_ulpd);
+					sctp_set_ulp_prop(sctp);
 				}
 				if (sctp->sctp_unacked == 0)
 					sctp_stop_faddr_timers(sctp);
@@ -3972,8 +4014,9 @@
 				sctp_send_cookie_ack(sctp);
 
 				if (!SCTP_IS_DETACHED(sctp)) {
-				    sctp->sctp_ulp_connected(sctp->sctp_ulpd);
-				    sctp_set_ulp_prop(sctp);
+					sctp->sctp_ulp_connected(
+					    sctp->sctp_ulpd);
+					sctp_set_ulp_prop(sctp);
 				}
 				if (sctp->sctp_unacked == 0)
 					sctp_stop_faddr_timers(sctp);
@@ -4024,7 +4067,10 @@
 				break;
 			}
 			case CHUNK_HEARTBEAT:
-				sctp_return_heartbeat(sctp, ch, mp);
+				if (!hb_already) {
+					sctp_return_heartbeat(sctp, ch, mp);
+					hb_already = B_TRUE;
+				}
 				break;
 			default:
 				if (sctp_strange_chunk(sctp, ch, fp) == 0) {
@@ -4064,7 +4110,10 @@
 				BUMP_LOCAL(sctp->sctp_ibchunks);
 				break;
 			case CHUNK_HEARTBEAT:
-				sctp_return_heartbeat(sctp, ch, mp);
+				if (!hb_already) {
+					sctp_return_heartbeat(sctp, ch, mp);
+					hb_already = B_TRUE;
+				}
 				break;
 			default:
 				if (sctp_strange_chunk(sctp, ch, fp) == 0) {
@@ -4096,7 +4145,10 @@
 				sctp_process_abort(sctp, ch, ECONNRESET);
 				goto done;
 			case CHUNK_HEARTBEAT:
-				sctp_return_heartbeat(sctp, ch, mp);
+				if (!hb_already) {
+					sctp_return_heartbeat(sctp, ch, mp);
+					hb_already = B_TRUE;
+				}
 				break;
 			default:
 				if (sctp_strange_chunk(sctp, ch, fp) == 0) {
@@ -4125,11 +4177,14 @@
 nomorechunks:
 	/* SACK if necessary */
 	if (gotdata) {
+		boolean_t sack_sent;
+
 		(sctp->sctp_sack_toggle)++;
-		sctp_sack(sctp, dups);
+		sack_sent = sctp_sack(sctp, dups);
 		dups = NULL;
 
-		if (!sctp->sctp_ack_timer_running) {
+		/* If a SACK is sent, no need to restart the timer. */
+		if (!sack_sent && !sctp->sctp_ack_timer_running) {
 			sctp->sctp_ack_timer_running = B_TRUE;
 			sctp_timer(sctp, sctp->sctp_ack_mp,
 			    MSEC_TO_TICK(sctps->sctps_deferred_ack_interval));
@@ -4154,6 +4209,9 @@
 		freeb(ipsec_mp);
 	freemsg(mp);
 
+	if (sctp->sctp_err_chunks != NULL)
+		sctp_process_err(sctp);
+
 	if (wake_eager) {
 		/*
 		 * sctp points to newly created control block, need to
@@ -4199,7 +4257,7 @@
 	    ((old <= new >> 1) || (old < sctp->sctp_mss))) {
 		sctp->sctp_force_sack = 1;
 		BUMP_MIB(&sctps->sctps_mib, sctpOutWinUpdate);
-		sctp_sack(sctp, NULL);
+		(void) sctp_sack(sctp, NULL);
 		old = 1;
 	} else {
 		old = 0;
--- a/usr/src/uts/common/inet/sctp/sctp_timer.c	Thu Aug 30 07:43:53 2007 -0700
+++ b/usr/src/uts/common/inet/sctp/sctp_timer.c	Thu Aug 30 08:00:29 2007 -0700
@@ -376,7 +376,7 @@
 	sctp->sctp_ack_timer_running = 0;
 	sctp->sctp_sack_toggle = sctps->sctps_deferred_acks_max;
 	BUMP_MIB(&sctps->sctps_mib, sctpOutAckDelayed);
-	sctp_sack(sctp, NULL);
+	(void) sctp_sack(sctp, NULL);
 }
 
 /*