git: d18c845f99cb - main - sctp: improve handling of SHUTDOWN and SHUTDOWN ACK chunks

From: Michael Tuexen <tuexen_at_FreeBSD.org>
Date: Wed, 23 Aug 2023 09:16:07 UTC
The branch main has been updated by tuexen:

URL: https://cgit.FreeBSD.org/src/commit/?id=d18c845f99cbd2d3c0e70b3b9b09d80c655b6fb6

commit d18c845f99cbd2d3c0e70b3b9b09d80c655b6fb6
Author:     Michael Tuexen <tuexen@FreeBSD.org>
AuthorDate: 2023-08-23 06:36:15 +0000
Commit:     Michael Tuexen <tuexen@FreeBSD.org>
CommitDate: 2023-08-23 06:36:15 +0000

    sctp: improve handling of SHUTDOWN and SHUTDOWN ACK chunks
    
    When handling a SHUTDOWN or SHUTDOWN ACK chunk detect if the peer
    is violating the protocol by not having made sure all user messages
    are reveived by the peer. If this situation is detected, abort the
    association.
    
    MFC after:      1 week
---
 sys/netinet/sctp_input.c | 54 +++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 42 insertions(+), 12 deletions(-)

diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c
index f3227c913e1e..e6dc41e08909 100644
--- a/sys/netinet/sctp_input.c
+++ b/sys/netinet/sctp_input.c
@@ -829,6 +829,38 @@ sctp_start_net_timers(struct sctp_tcb *stcb)
 	}
 }
 
+static void
+sctp_check_data_from_peer(struct sctp_tcb *stcb, int *abort_flag)
+{
+	char msg[SCTP_DIAG_INFO_LEN];
+	struct sctp_association *asoc;
+	struct mbuf *op_err;
+	unsigned int i;
+
+	*abort_flag = 0;
+	asoc = &stcb->asoc;
+	if (SCTP_TSN_GT(asoc->highest_tsn_inside_map, asoc->cumulative_tsn) ||
+	    SCTP_TSN_GT(asoc->highest_tsn_inside_nr_map, asoc->cumulative_tsn)) {
+		SCTP_SNPRINTF(msg, sizeof(msg), "Missing TSN");
+		*abort_flag = 1;
+	}
+	if (!*abort_flag) {
+		for (i = 0; i < asoc->streamincnt; i++) {
+			if (!TAILQ_EMPTY(&asoc->strmin[i].inqueue) ||
+			    !TAILQ_EMPTY(&asoc->strmin[i].uno_inqueue)) {
+				SCTP_SNPRINTF(msg, sizeof(msg), "Missing user data");
+				*abort_flag = 1;
+				break;
+			}
+		}
+	}
+	if (*abort_flag) {
+		op_err = sctp_generate_cause(SCTP_CAUSE_PROTOCOL_VIOLATION, msg);
+		stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_INPUT + SCTP_LOC_9;
+		sctp_abort_an_association(stcb->sctp_ep, stcb, op_err, false, SCTP_SO_NOT_LOCKED);
+	}
+}
+
 static void
 sctp_handle_shutdown(struct sctp_shutdown_chunk *cp,
     struct sctp_tcb *stcb, struct sctp_nets *net, int *abort_flag)
@@ -852,12 +884,10 @@ sctp_handle_shutdown(struct sctp_shutdown_chunk *cp,
 	if (*abort_flag) {
 		return;
 	}
-	/*
-	 * FIXME MT: Handle the case where there are still incomplete
-	 * received user messages or known missing user messages from the
-	 * peer. One way to handle this is to abort the associations in this
-	 * case.
-	 */
+	sctp_check_data_from_peer(stcb, abort_flag);
+	if (*abort_flag) {
+		return;
+	}
 	if (stcb->sctp_socket) {
 		if ((SCTP_GET_STATE(stcb) != SCTP_STATE_SHUTDOWN_RECEIVED) &&
 		    (SCTP_GET_STATE(stcb) != SCTP_STATE_SHUTDOWN_ACK_SENT) &&
@@ -914,6 +944,8 @@ sctp_handle_shutdown_ack(struct sctp_shutdown_ack_chunk *cp SCTP_UNUSED,
     struct sctp_tcb *stcb,
     struct sctp_nets *net)
 {
+	int abort_flag;
+
 	SCTPDBG(SCTP_DEBUG_INPUT2,
 	    "sctp_handle_shutdown_ack: handling SHUTDOWN ACK\n");
 	if (stcb == NULL) {
@@ -934,12 +966,10 @@ sctp_handle_shutdown_ack(struct sctp_shutdown_ack_chunk *cp SCTP_UNUSED,
 		SCTP_TCB_UNLOCK(stcb);
 		return;
 	}
-	/*
-	 * FIXME MT: Handle the case where there are still incomplete
-	 * received user messages or known missing user messages from the
-	 * peer. One way to handle this is to abort the associations in this
-	 * case.
-	 */
+	sctp_check_data_from_peer(stcb, &abort_flag);
+	if (abort_flag) {
+		return;
+	}
 #ifdef INVARIANTS
 	if (!TAILQ_EMPTY(&stcb->asoc.send_queue) ||
 	    !TAILQ_EMPTY(&stcb->asoc.sent_queue) ||