Mercurial > illumos > illumos-gate
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); +}