Fix a bug where an SCTP association was moved back to SHUTDOWN_SENT

state when the user issued a shutdown() call.

MFC after: 	1 week
This commit is contained in:
Michael Tuexen 2015-11-19 16:46:00 +00:00
parent 1067a2ba68
commit bd5b567213
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=291078

View File

@ -957,14 +957,15 @@ sctp_shutdown(struct socket *so)
SCTP_INP_RUNLOCK(inp);
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EOPNOTSUPP);
return (EOPNOTSUPP);
}
/*
* Ok if we reach here its the TCP model and it is either a SHUT_WR
* or SHUT_RDWR. This means we put the shutdown flag against it.
*/
{
} else {
/*
* Ok, if we reach here its the TCP model and it is either a
* SHUT_WR or SHUT_RDWR. This means we put the shutdown flag
* against it.
*/
struct sctp_tcb *stcb;
struct sctp_association *asoc;
struct sctp_nets *netp;
if ((so->so_state &
(SS_ISCONNECTED | SS_ISCONNECTING | SS_ISDISCONNECTING)) == 0) {
@ -976,7 +977,7 @@ sctp_shutdown(struct socket *so)
stcb = LIST_FIRST(&inp->sctp_asoc_list);
if (stcb == NULL) {
/*
* Ok we hit the case that the shutdown call was
* Ok, we hit the case that the shutdown call was
* made after an abort or something. Nothing to do
* now.
*/
@ -985,6 +986,27 @@ sctp_shutdown(struct socket *so)
}
SCTP_TCB_LOCK(stcb);
asoc = &stcb->asoc;
if (asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) {
SCTP_TCB_UNLOCK(stcb);
SCTP_INP_RUNLOCK(inp);
return (0);
}
if ((SCTP_GET_STATE(asoc) != SCTP_STATE_COOKIE_WAIT) &&
(SCTP_GET_STATE(asoc) != SCTP_STATE_COOKIE_ECHOED) &&
(SCTP_GET_STATE(asoc) != SCTP_STATE_OPEN)) {
/*
* If we are not in or before ESTABLISHED, there is
* no protocol action required.
*/
SCTP_TCB_UNLOCK(stcb);
SCTP_INP_RUNLOCK(inp);
return (0);
}
if (stcb->asoc.alternate) {
netp = stcb->asoc.alternate;
} else {
netp = stcb->asoc.primary_destination;
}
if (TAILQ_EMPTY(&asoc->send_queue) &&
TAILQ_EMPTY(&asoc->sent_queue) &&
(asoc->stream_queue_cnt == 0)) {
@ -992,46 +1014,21 @@ sctp_shutdown(struct socket *so)
goto abort_anyway;
}
/* there is nothing queued to send, so I'm done... */
if (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_SENT) {
/* only send SHUTDOWN the first time through */
struct sctp_nets *netp;
if ((SCTP_GET_STATE(asoc) == SCTP_STATE_OPEN) ||
(SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED)) {
SCTP_STAT_DECR_GAUGE32(sctps_currestab);
}
SCTP_SET_STATE(asoc, SCTP_STATE_SHUTDOWN_SENT);
SCTP_CLEAR_SUBSTATE(asoc, SCTP_STATE_SHUTDOWN_PENDING);
sctp_stop_timers_for_shutdown(stcb);
if (stcb->asoc.alternate) {
netp = stcb->asoc.alternate;
} else {
netp = stcb->asoc.primary_destination;
}
sctp_send_shutdown(stcb, netp);
sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN,
stcb->sctp_ep, stcb, netp);
sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD,
stcb->sctp_ep, stcb, netp);
sctp_chunk_output(stcb->sctp_ep, stcb, SCTP_OUTPUT_FROM_T3, SCTP_SO_LOCKED);
if (SCTP_GET_STATE(asoc) == SCTP_STATE_OPEN) {
SCTP_STAT_DECR_GAUGE32(sctps_currestab);
}
SCTP_SET_STATE(asoc, SCTP_STATE_SHUTDOWN_SENT);
SCTP_CLEAR_SUBSTATE(asoc, SCTP_STATE_SHUTDOWN_PENDING);
sctp_stop_timers_for_shutdown(stcb);
sctp_send_shutdown(stcb, netp);
sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN,
stcb->sctp_ep, stcb, netp);
} else {
/*
* we still got (or just got) data to send, so set
* SHUTDOWN_PENDING
* We still got (or just got) data to send, so set
* SHUTDOWN_PENDING.
*/
struct sctp_nets *netp;
if (stcb->asoc.alternate) {
netp = stcb->asoc.alternate;
} else {
netp = stcb->asoc.primary_destination;
}
asoc->state |= SCTP_STATE_SHUTDOWN_PENDING;
sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, stcb->sctp_ep, stcb,
netp);
SCTP_ADD_SUBSTATE(asoc, SCTP_STATE_SHUTDOWN_PENDING);
if (asoc->locked_on_sending) {
/* Locked to send out the data */
struct sctp_stream_queue_pending *sp;
@ -1042,7 +1039,7 @@ sctp_shutdown(struct socket *so)
asoc->locked_on_sending->stream_no);
} else {
if ((sp->length == 0) && (sp->msg_is_complete == 0)) {
asoc->state |= SCTP_STATE_PARTIAL_MSG_LEFT;
SCTP_ADD_SUBSTATE(asoc, SCTP_STATE_PARTIAL_MSG_LEFT);
}
}
}
@ -1056,16 +1053,20 @@ sctp_shutdown(struct socket *so)
stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_USRREQ + SCTP_LOC_6;
sctp_abort_an_association(stcb->sctp_ep, stcb,
op_err, SCTP_SO_LOCKED);
goto skip_unlock;
} else {
sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_CLOSING, SCTP_SO_LOCKED);
SCTP_INP_RUNLOCK(inp);
return (0);
}
}
sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, stcb->sctp_ep, stcb, netp);
/*
* XXX: Why do this in the case where we have still data
* queued?
*/
sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_CLOSING, SCTP_SO_LOCKED);
SCTP_TCB_UNLOCK(stcb);
SCTP_INP_RUNLOCK(inp);
return (0);
}
skip_unlock:
SCTP_INP_RUNLOCK(inp);
return (0);
}
/*