Improve the handling of received INITs. Send an ABORT when
not accepting the connection. Also fix a crash, which could happen when the user closed the socket. MFC after: 1 month.
This commit is contained in:
parent
e883df1d1b
commit
d68fdc4df6
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=229774
@ -88,43 +88,14 @@ sctp_handle_init(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh,
|
||||
{
|
||||
struct sctp_init *init;
|
||||
struct mbuf *op_err;
|
||||
uint32_t init_limit;
|
||||
|
||||
SCTPDBG(SCTP_DEBUG_INPUT2, "sctp_handle_init: handling INIT tcb:%p\n",
|
||||
stcb);
|
||||
if (stcb == NULL) {
|
||||
SCTP_INP_RLOCK(inp);
|
||||
if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
|
||||
goto outnow;
|
||||
}
|
||||
}
|
||||
op_err = NULL;
|
||||
init = &cp->init;
|
||||
/* First are we accepting? */
|
||||
if ((inp->sctp_socket->so_qlimit == 0) && (stcb == NULL)) {
|
||||
SCTPDBG(SCTP_DEBUG_INPUT2,
|
||||
"sctp_handle_init: Abort, so_qlimit:%d\n",
|
||||
inp->sctp_socket->so_qlimit);
|
||||
/*
|
||||
* FIX ME ?? What about TCP model and we have a
|
||||
* match/restart case? Actually no fix is needed. the lookup
|
||||
* will always find the existing assoc so stcb would not be
|
||||
* NULL. It may be questionable to do this since we COULD
|
||||
* just send back the INIT-ACK and hope that the app did
|
||||
* accept()'s by the time the COOKIE was sent. But there is
|
||||
* a price to pay for COOKIE generation and I don't want to
|
||||
* pay it on the chance that the app will actually do some
|
||||
* accepts(). The App just looses and should NOT be in this
|
||||
* state :-)
|
||||
*/
|
||||
sctp_abort_association(inp, stcb, m, iphlen, sh, op_err,
|
||||
vrf_id, port);
|
||||
if (stcb)
|
||||
*abort_no_unlock = 1;
|
||||
goto outnow;
|
||||
}
|
||||
/* validate length */
|
||||
if (ntohs(cp->ch.chunk_length) < sizeof(struct sctp_init_chunk)) {
|
||||
/* Invalid length */
|
||||
op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM);
|
||||
sctp_abort_association(inp, stcb, m, iphlen, sh, op_err,
|
||||
vrf_id, port);
|
||||
@ -133,6 +104,7 @@ sctp_handle_init(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh,
|
||||
goto outnow;
|
||||
}
|
||||
/* validate parameters */
|
||||
init = &cp->init;
|
||||
if (init->initiate_tag == 0) {
|
||||
/* protocol error... send abort */
|
||||
op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM);
|
||||
@ -169,19 +141,49 @@ sctp_handle_init(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh,
|
||||
*abort_no_unlock = 1;
|
||||
goto outnow;
|
||||
}
|
||||
init_limit = offset + ntohs(cp->ch.chunk_length);
|
||||
if (sctp_validate_init_auth_params(m, offset + sizeof(*cp),
|
||||
init_limit)) {
|
||||
offset + ntohs(cp->ch.chunk_length))) {
|
||||
/* auth parameter(s) error... send abort */
|
||||
sctp_abort_association(inp, stcb, m, iphlen, sh, NULL, vrf_id, port);
|
||||
if (stcb)
|
||||
*abort_no_unlock = 1;
|
||||
goto outnow;
|
||||
}
|
||||
/* send an INIT-ACK w/cookie */
|
||||
SCTPDBG(SCTP_DEBUG_INPUT3, "sctp_handle_init: sending INIT-ACK\n");
|
||||
sctp_send_initiate_ack(inp, stcb, m, iphlen, offset, sh, cp, vrf_id, port,
|
||||
((stcb == NULL) ? SCTP_HOLDS_LOCK : SCTP_NOT_LOCKED));
|
||||
/*
|
||||
* We are only accepting if we have a socket with positive
|
||||
* so_qlimit.
|
||||
*/
|
||||
if ((stcb == NULL) &&
|
||||
((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) ||
|
||||
(inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) ||
|
||||
(inp->sctp_socket == NULL) ||
|
||||
(inp->sctp_socket->so_qlimit == 0))) {
|
||||
/*
|
||||
* FIX ME ?? What about TCP model and we have a
|
||||
* match/restart case? Actually no fix is needed. the lookup
|
||||
* will always find the existing assoc so stcb would not be
|
||||
* NULL. It may be questionable to do this since we COULD
|
||||
* just send back the INIT-ACK and hope that the app did
|
||||
* accept()'s by the time the COOKIE was sent. But there is
|
||||
* a price to pay for COOKIE generation and I don't want to
|
||||
* pay it on the chance that the app will actually do some
|
||||
* accepts(). The App just looses and should NOT be in this
|
||||
* state :-)
|
||||
*/
|
||||
sctp_abort_association(inp, stcb, m, iphlen, sh, NULL,
|
||||
vrf_id, port);
|
||||
goto outnow;
|
||||
}
|
||||
if ((stcb != NULL) &&
|
||||
(SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_SHUTDOWN_ACK_SENT)) {
|
||||
SCTPDBG(SCTP_DEBUG_INPUT3, "sctp_handle_init: sending SHUTDOWN-ACK\n");
|
||||
sctp_send_shutdown_ack(stcb, NULL);
|
||||
sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_CONTROL_PROC, SCTP_SO_NOT_LOCKED);
|
||||
} else {
|
||||
SCTPDBG(SCTP_DEBUG_INPUT3, "sctp_handle_init: sending INIT-ACK\n");
|
||||
sctp_send_initiate_ack(inp, stcb, m, iphlen, offset, sh, cp, vrf_id, port,
|
||||
((stcb == NULL) ? SCTP_HOLDS_LOCK : SCTP_NOT_LOCKED));
|
||||
}
|
||||
outnow:
|
||||
if (stcb == NULL) {
|
||||
SCTP_INP_RUNLOCK(inp);
|
||||
@ -1025,7 +1027,7 @@ sctp_handle_shutdown_ack(struct sctp_shutdown_ack_chunk *cp SCTP_UNUSED,
|
||||
if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
|
||||
(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
|
||||
/* Set the connected flag to disconnected */
|
||||
stcb->sctp_ep->sctp_socket->so_snd.sb_cc = 0;
|
||||
stcb->sctp_socket->so_snd.sb_cc = 0;
|
||||
}
|
||||
}
|
||||
SCTP_STAT_INCR_COUNTER32(sctps_shutdown);
|
||||
@ -4499,7 +4501,6 @@ __attribute__((noinline))
|
||||
* process all control chunks...
|
||||
*/
|
||||
if (((ch->chunk_type == SCTP_SELECTIVE_ACK) ||
|
||||
/* EY */
|
||||
(ch->chunk_type == SCTP_NR_SELECTIVE_ACK) ||
|
||||
(ch->chunk_type == SCTP_HEARTBEAT_REQUEST)) &&
|
||||
(SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_COOKIE_ECHOED)) {
|
||||
@ -4613,54 +4614,30 @@ __attribute__((noinline))
|
||||
}
|
||||
switch (ch->chunk_type) {
|
||||
case SCTP_INITIATION:
|
||||
/* must be first and only chunk */
|
||||
SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_INIT\n");
|
||||
if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
|
||||
/* We are not interested anymore? */
|
||||
if ((stcb) && (stcb->asoc.total_output_queue_size)) {
|
||||
/*
|
||||
* collision case where we are
|
||||
* sending to them too
|
||||
*/
|
||||
;
|
||||
} else {
|
||||
if (locked_tcb) {
|
||||
SCTP_TCB_UNLOCK(locked_tcb);
|
||||
}
|
||||
*offset = length;
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
if ((chk_length > SCTP_LARGEST_INIT_ACCEPTED) ||
|
||||
(num_chunks > 1) ||
|
||||
/* The INIT chunk must be the only chunk. */
|
||||
if ((num_chunks > 1) ||
|
||||
(SCTP_BASE_SYSCTL(sctp_strict_init) && (length - *offset > (int)SCTP_SIZE32(chk_length)))) {
|
||||
sctp_abort_association(inp, stcb, m,
|
||||
iphlen, sh, NULL, vrf_id, port);
|
||||
*offset = length;
|
||||
if (locked_tcb) {
|
||||
SCTP_TCB_UNLOCK(locked_tcb);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
if ((stcb != NULL) &&
|
||||
(SCTP_GET_STATE(&stcb->asoc) ==
|
||||
SCTP_STATE_SHUTDOWN_ACK_SENT)) {
|
||||
sctp_send_shutdown_ack(stcb, NULL);
|
||||
*offset = length;
|
||||
sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_CONTROL_PROC, SCTP_SO_NOT_LOCKED);
|
||||
if (locked_tcb) {
|
||||
SCTP_TCB_UNLOCK(locked_tcb);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
if (netp) {
|
||||
sctp_handle_init(m, iphlen, *offset, sh,
|
||||
(struct sctp_init_chunk *)ch, inp,
|
||||
stcb, &abort_no_unlock, vrf_id, port);
|
||||
}
|
||||
if (abort_no_unlock)
|
||||
return (NULL);
|
||||
/* Honor our resource limit. */
|
||||
if (chk_length > SCTP_LARGEST_INIT_ACCEPTED) {
|
||||
struct mbuf *op_err;
|
||||
|
||||
op_err = sctp_generate_invmanparam(SCTP_CAUSE_OUT_OF_RESC);
|
||||
sctp_abort_association(inp, stcb, m,
|
||||
iphlen, sh, op_err, vrf_id, port);
|
||||
*offset = length;
|
||||
return (NULL);
|
||||
}
|
||||
sctp_handle_init(m, iphlen, *offset, sh,
|
||||
(struct sctp_init_chunk *)ch, inp,
|
||||
stcb, &abort_no_unlock, vrf_id, port);
|
||||
*offset = length;
|
||||
if (locked_tcb) {
|
||||
if ((!abort_no_unlock) && (locked_tcb)) {
|
||||
SCTP_TCB_UNLOCK(locked_tcb);
|
||||
}
|
||||
return (NULL);
|
||||
@ -4668,7 +4645,6 @@ __attribute__((noinline))
|
||||
case SCTP_PAD_CHUNK:
|
||||
break;
|
||||
case SCTP_INITIATION_ACK:
|
||||
/* must be first and only chunk */
|
||||
SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_INIT-ACK\n");
|
||||
if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
|
||||
/* We are not interested anymore */
|
||||
@ -4697,6 +4673,7 @@ __attribute__((noinline))
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
/* The INIT-CK chunk must be the only chunk. */
|
||||
if ((num_chunks > 1) ||
|
||||
(SCTP_BASE_SYSCTL(sctp_strict_init) && (length - *offset > (int)SCTP_SIZE32(chk_length)))) {
|
||||
*offset = length;
|
||||
@ -4711,16 +4688,17 @@ __attribute__((noinline))
|
||||
} else {
|
||||
ret = -1;
|
||||
}
|
||||
*offset = length;
|
||||
if (abort_no_unlock) {
|
||||
return (NULL);
|
||||
}
|
||||
/*
|
||||
* Special case, I must call the output routine to
|
||||
* get the cookie echoed
|
||||
*/
|
||||
if (abort_no_unlock)
|
||||
return (NULL);
|
||||
|
||||
if ((stcb) && ret == 0)
|
||||
if ((stcb != NULL) && (ret == 0)) {
|
||||
sctp_chunk_output(stcb->sctp_ep, stcb, SCTP_OUTPUT_FROM_CONTROL_PROC, SCTP_SO_NOT_LOCKED);
|
||||
*offset = length;
|
||||
}
|
||||
if (locked_tcb) {
|
||||
SCTP_TCB_UNLOCK(locked_tcb);
|
||||
}
|
||||
@ -4977,7 +4955,6 @@ __attribute__((noinline))
|
||||
case SCTP_OPERATION_ERROR:
|
||||
SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_OP-ERR\n");
|
||||
if ((stcb) && netp && *netp && sctp_handle_error(ch, stcb, *netp) < 0) {
|
||||
|
||||
*offset = length;
|
||||
return (NULL);
|
||||
}
|
||||
@ -5009,23 +4986,11 @@ __attribute__((noinline))
|
||||
if ((stcb == NULL) && (inp->sctp_socket->so_qlen >= inp->sctp_socket->so_qlimit)) {
|
||||
if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) &&
|
||||
(SCTP_BASE_SYSCTL(sctp_abort_if_one_2_one_hits_limit))) {
|
||||
struct mbuf *oper;
|
||||
struct sctp_paramhdr *phdr;
|
||||
struct mbuf *op_err;
|
||||
|
||||
oper = sctp_get_mbuf_for_msg(sizeof(struct sctp_paramhdr),
|
||||
0, M_DONTWAIT, 1, MT_DATA);
|
||||
if (oper) {
|
||||
SCTP_BUF_LEN(oper) =
|
||||
sizeof(struct sctp_paramhdr);
|
||||
phdr = mtod(oper,
|
||||
struct sctp_paramhdr *);
|
||||
phdr->param_type =
|
||||
htons(SCTP_CAUSE_OUT_OF_RESC);
|
||||
phdr->param_length =
|
||||
htons(sizeof(struct sctp_paramhdr));
|
||||
}
|
||||
op_err = sctp_generate_invmanparam(SCTP_CAUSE_OUT_OF_RESC);
|
||||
sctp_abort_association(inp, stcb, m,
|
||||
iphlen, sh, oper, vrf_id, port);
|
||||
iphlen, sh, op_err, vrf_id, port);
|
||||
}
|
||||
*offset = length;
|
||||
return (NULL);
|
||||
|
Loading…
Reference in New Issue
Block a user