diff options
| author | Anil udupa <anil.udupa@sun.com> | 2010-06-28 16:53:55 -0700 |
|---|---|---|
| committer | Anil udupa <anil.udupa@sun.com> | 2010-06-28 16:53:55 -0700 |
| commit | 3e1dae9f7bdcf417fc72a039b910df3051a811e5 (patch) | |
| tree | 4a8fdd6f78982a073691422a2b82a3beab627cc4 | |
| parent | 4f14b0f29aa144cc03efdde5508ae126ae197acf (diff) | |
| download | illumos-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.h | 1 | ||||
| -rw-r--r-- | usr/src/uts/common/inet/sctp/sctp_input.c | 31 | ||||
| -rw-r--r-- | usr/src/uts/common/inet/sctp/sctp_shutdown.c | 97 |
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); +} |
