Fix the handling of parital and too short chunks.

Ensure that the current behaviour is consistent: stop processing
of the chunk, but finish the processing of the previous chunks.

This behaviour might be changed in a later commit to ABORT the
assoication due to a protocol violation, but changing this
is a separate issue.

MFC after:	3 days
This commit is contained in:
Michael Tuexen 2017-10-15 19:33:30 +00:00
parent 03cea61b3a
commit 80a2d1406f
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=324638

View File

@ -4518,7 +4518,6 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
* until we get into jumbo grams and such..
*/
uint8_t chunk_buf[SCTP_CHUNK_BUFFER_SIZE];
struct sctp_tcb *locked_tcb = stcb;
int got_auth = 0;
uint32_t auth_offset = 0, auth_len = 0;
int auth_skipped = 0;
@ -4530,31 +4529,28 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
SCTPDBG(SCTP_DEBUG_INPUT1, "sctp_process_control: iphlen=%u, offset=%u, length=%u stcb:%p\n",
iphlen, *offset, length, (void *)stcb);
if (stcb) {
SCTP_TCB_LOCK_ASSERT(stcb);
}
/* validate chunk header length... */
if (ntohs(ch->chunk_length) < sizeof(*ch)) {
SCTPDBG(SCTP_DEBUG_INPUT1, "Invalid header length %d\n",
ntohs(ch->chunk_length));
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
}
return (NULL);
return (stcb);
}
/*
* validate the verification tag
*/
vtag_in = ntohl(sh->v_tag);
if (locked_tcb) {
SCTP_TCB_LOCK_ASSERT(locked_tcb);
}
if (ch->chunk_type == SCTP_INITIATION) {
SCTPDBG(SCTP_DEBUG_INPUT1, "Its an INIT of len:%d vtag:%x\n",
ntohs(ch->chunk_length), vtag_in);
if (vtag_in != 0) {
/* protocol error- silently discard... */
SCTP_STAT_INCR(sctps_badvtag);
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
if (stcb != NULL) {
SCTP_TCB_UNLOCK(stcb);
}
return (NULL);
}
@ -4577,9 +4573,6 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
if (*offset >= length) {
/* no more data left in the mbuf chain */
*offset = length;
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
}
return (NULL);
}
ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, *offset,
@ -4588,10 +4581,7 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
if (ch == NULL) {
/* Help */
*offset = length;
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
}
return (NULL);
return (stcb);
}
if (ch->chunk_type == SCTP_COOKIE_ECHO) {
goto process_control_chunks;
@ -4628,10 +4618,7 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
* sctp_findassociation_ep_asconf().
*/
SCTP_INP_DECR_REF(inp);
} else {
locked_tcb = stcb;
}
/* now go back and verify any auth chunk to be sure */
if (auth_skipped && (stcb != NULL)) {
struct sctp_auth_chunk *auth;
@ -4645,10 +4632,7 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
auth_offset)) {
/* auth HMAC failed so dump it */
*offset = length;
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
}
return (NULL);
return (stcb);
} else {
/* remaining chunks are HMAC checked */
stcb->asoc.authenticated = 1;
@ -4664,9 +4648,6 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
mflowtype, mflowid, inp->fibnum,
vrf_id, port);
*offset = length;
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
}
return (NULL);
}
asoc = &stcb->asoc;
@ -4684,8 +4665,8 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
} else {
/* drop this packet... */
SCTP_STAT_INCR(sctps_badvtag);
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
if (stcb != NULL) {
SCTP_TCB_UNLOCK(stcb);
}
return (NULL);
}
@ -4698,8 +4679,8 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
* but it won't complete until the shutdown
* is completed
*/
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
if (stcb != NULL) {
SCTP_TCB_UNLOCK(stcb);
}
snprintf(msg, sizeof(msg), "OOTB, %s:%d at %s", __FILE__, __LINE__, __func__);
op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code),
@ -4718,8 +4699,8 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
"invalid vtag: %xh, expect %xh\n",
vtag_in, asoc->my_vtag);
SCTP_STAT_INCR(sctps_badvtag);
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
if (stcb != NULL) {
SCTP_TCB_UNLOCK(stcb);
}
*offset = length;
return (NULL);
@ -4755,10 +4736,7 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
if (chk_length < sizeof(*ch) ||
(*offset + (int)chk_length) > length) {
*offset = length;
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
}
return (NULL);
return (stcb);
}
SCTP_STAT_INCR_COUNTER64(sctps_incontrolchunks);
/*
@ -4773,8 +4751,8 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
sizeof(struct sctp_init_ack_chunk), chunk_buf);
if (ch == NULL) {
*offset = length;
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
if (stcb != NULL) {
SCTP_TCB_UNLOCK(stcb);
}
return (NULL);
}
@ -4796,8 +4774,8 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
chunk_buf);
if (ch == NULL) {
*offset = length;
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
if (stcb != NULL) {
SCTP_TCB_UNLOCK(stcb);
}
return (NULL);
}
@ -4808,8 +4786,8 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
if (ch == NULL) {
SCTP_PRINTF("sctp_process_control: Can't get the all data....\n");
*offset = length;
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
if (stcb != NULL) {
SCTP_TCB_UNLOCK(stcb);
}
return (NULL);
}
@ -4849,8 +4827,8 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
(length - *offset > (int)SCTP_SIZE32(chk_length))) {
/* RFC 4960 requires that no ABORT is sent */
*offset = length;
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
if (stcb != NULL) {
SCTP_TCB_UNLOCK(stcb);
}
return (NULL);
}
@ -4870,8 +4848,8 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
mflowtype, mflowid,
vrf_id, port);
*offset = length;
if ((!abort_no_unlock) && (locked_tcb)) {
SCTP_TCB_UNLOCK(locked_tcb);
if ((!abort_no_unlock) && (stcb != NULL)) {
SCTP_TCB_UNLOCK(stcb);
}
return (NULL);
break;
@ -4881,15 +4859,14 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_INIT-ACK\n");
if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
/* We are not interested anymore */
if ((stcb) && (stcb->asoc.total_output_queue_size)) {
if ((stcb != NULL) && (stcb->asoc.total_output_queue_size)) {
;
} else {
if ((locked_tcb != NULL) && (locked_tcb != stcb)) {
/* Very unlikely */
SCTP_TCB_UNLOCK(locked_tcb);
if (stcb != NULL) {
SCTP_TCB_UNLOCK(stcb);
}
*offset = length;
if (stcb) {
if (stcb != NULL) {
#if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
so = SCTP_INP_SO(inp);
atomic_add_int(&stcb->asoc.refcnt, 1);
@ -4911,10 +4888,7 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
if ((num_chunks > 1) ||
(length - *offset > (int)SCTP_SIZE32(chk_length))) {
*offset = length;
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
}
return (NULL);
return (stcb);
}
if ((netp) && (*netp)) {
ret = sctp_handle_init_ack(m, iphlen, *offset,
@ -4938,10 +4912,7 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
if ((stcb != NULL) && (ret == 0)) {
sctp_chunk_output(stcb->sctp_ep, stcb, SCTP_OUTPUT_FROM_CONTROL_PROC, SCTP_SO_NOT_LOCKED);
}
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
}
return (NULL);
return (stcb);
break;
case SCTP_SELECTIVE_ACK:
{
@ -5133,10 +5104,7 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
if ((stcb == NULL) || (chk_length != sizeof(struct sctp_heartbeat_chunk))) {
/* Its not ours */
*offset = length;
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
}
return (NULL);
return (stcb);
}
/* He's alive so give him credit */
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_THRESHOLD_LOGGING) {
@ -5166,10 +5134,7 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
(void *)stcb);
if ((stcb == NULL) || (chk_length != sizeof(struct sctp_shutdown_chunk))) {
*offset = length;
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
}
return (NULL);
return (stcb);
}
if (netp && *netp) {
int abort_flag = 0;
@ -5206,7 +5171,7 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
/* We are not interested anymore */
abend:
if (stcb) {
if (stcb != NULL) {
SCTP_TCB_UNLOCK(stcb);
}
*offset = length;
@ -5263,6 +5228,9 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
}
}
if (netp) {
struct sctp_tcb *locked_stcb;
locked_stcb = stcb;
ret_buf =
sctp_handle_cookie_echo(m, iphlen,
*offset,
@ -5273,11 +5241,17 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
auth_skipped,
auth_offset,
auth_len,
&locked_tcb,
&locked_stcb,
mflowtype,
mflowid,
vrf_id,
port);
if ((locked_stcb != NULL) && (locked_stcb != stcb)) {
SCTP_TCB_UNLOCK(locked_stcb);
}
if (stcb != NULL) {
SCTP_TCB_LOCK_ASSERT(stcb);
}
} else {
ret_buf = NULL;
}
@ -5285,8 +5259,8 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
SCTP_ASOC_CREATE_UNLOCK(linp);
}
if (ret_buf == NULL) {
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
if (stcb != NULL) {
SCTP_TCB_UNLOCK(stcb);
}
SCTPDBG(SCTP_DEBUG_INPUT3,
"GAK, null buffer\n");
@ -5313,10 +5287,7 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
case SCTP_COOKIE_ACK:
SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_COOKIE-ACK, stcb %p\n", (void *)stcb);
if ((stcb == NULL) || chk_length != sizeof(struct sctp_cookie_ack_chunk)) {
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
}
return (NULL);
return (stcb);
}
if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
/* We are not interested anymore */
@ -5358,11 +5329,8 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
/* He's alive so give him credit */
if ((stcb == NULL) || (chk_length != sizeof(struct sctp_ecne_chunk))) {
/* Its not ours */
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
}
*offset = length;
return (NULL);
return (stcb);
}
if (stcb) {
if (stcb->asoc.ecn_supported == 0) {
@ -5385,12 +5353,8 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_ECN-CWR\n");
/* He's alive so give him credit */
if ((stcb == NULL) || (chk_length != sizeof(struct sctp_cwr_chunk))) {
/* Its not ours */
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
}
*offset = length;
return (NULL);
return (stcb);
}
if (stcb) {
if (stcb->asoc.ecn_supported == 0) {
@ -5413,10 +5377,7 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
if ((num_chunks > 1) ||
(length - *offset > (int)SCTP_SIZE32(chk_length))) {
*offset = length;
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
}
return (NULL);
return (stcb);
}
if ((stcb) && netp && *netp) {
sctp_handle_shutdown_complete((struct sctp_shutdown_complete_chunk *)ch,
@ -5449,11 +5410,8 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_ASCONF-ACK\n");
if (chk_length < sizeof(struct sctp_asconf_ack_chunk)) {
/* Its not ours */
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
}
*offset = length;
return (NULL);
return (stcb);
}
if ((stcb) && netp && *netp) {
if (stcb->asoc.asconf_supported == 0) {
@ -5479,11 +5437,8 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_FWD-TSN\n");
if (chk_length < sizeof(struct sctp_forward_tsn_chunk)) {
/* Its not ours */
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
}
*offset = length;
return (NULL);
return (stcb);
}
/* He's alive so give him credit */
if (stcb) {
@ -5546,11 +5501,8 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
SCTPDBG(SCTP_DEBUG_INPUT3, "SCTP_STREAM_RESET\n");
if (((stcb == NULL) || (ch == NULL) || (chk_length < sizeof(struct sctp_stream_reset_tsn_req)))) {
/* Its not ours */
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
}
*offset = length;
return (NULL);
return (stcb);
}
if (stcb->asoc.reconfig_supported == 0) {
goto unknown_chunk;
@ -5566,11 +5518,8 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
/* re-get it all please */
if (chk_length < sizeof(struct sctp_pktdrop_chunk)) {
/* Its not ours */
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
}
*offset = length;
return (NULL);
return (stcb);
}
if (ch && (stcb) && netp && (*netp)) {
if (stcb->asoc.pktdrop_supported == 0) {
@ -5579,7 +5528,6 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
sctp_handle_packet_dropped((struct sctp_pktdrop_chunk *)ch,
stcb, *netp,
min(chk_length, (sizeof(chunk_buf) - 4)));
}
break;
case SCTP_AUTHENTICATION:
@ -5601,11 +5549,8 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
(chk_length > (sizeof(struct sctp_auth_chunk) +
SCTP_AUTH_DIGEST_LEN_MAX))) {
/* Its not ours */
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
}
*offset = length;
return (NULL);
return (stcb);
}
if (got_auth == 1) {
/* skip this chunk... it's already auth'd */
@ -5670,11 +5615,8 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, *offset,
sizeof(struct sctp_chunkhdr), chunk_buf);
if (ch == NULL) {
if (locked_tcb) {
SCTP_TCB_UNLOCK(locked_tcb);
}
*offset = length;
return (NULL);
return (stcb);
}
} /* while */