[PATCH net 1/2] sctp: factor out INIT verification failure handling
From: Xin Long <lucien.xin@gmail.com>
Date: 2026-06-16 00:34:22
Also in:
linux-sctp
Subsystem:
networking [general], sctp protocol, the rest · Maintainers:
"David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Marcelo Ricardo Leitner, Xin Long, Linus Torvalds
Extract the duplicated INIT/INIT-ACK error handling logic into a new helper, sctp_abort_on_init_err(). Several state functions open-code the same pattern after sctp_verify_init() fails: construct an ABORT with error causes if available, send it when allocation succeeds, or fall back to T-bit ABORT handling when no error chunk is present. INIT-ACK handling also includes additional teardown logic for malformed packets. Move this logic into sctp_abort_on_init_err() to reduce duplication and centralize INIT/INIT-ACK failure handling. No functional change intended. The helper will be used in a subsequent patch. Signed-off-by: Xin Long <lucien.xin@gmail.com> --- net/sctp/sm_statefuns.c | 171 +++++++++++++++++----------------------- 1 file changed, 72 insertions(+), 99 deletions(-)
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 9b23c11cbb9e..544f308ee527 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c@@ -68,6 +68,14 @@ static void sctp_send_stale_cookie_err(struct net *net, const struct sctp_chunk *chunk, struct sctp_cmd_seq *commands, struct sctp_chunk *err_chunk); +static enum sctp_disposition sctp_abort_on_init_err( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const struct sctp_chunk *chunk, + void *arg, + struct sctp_cmd_seq *commands, + struct sctp_chunk *err_chunk); static enum sctp_disposition sctp_sf_do_5_2_6_stale( struct net *net, const struct sctp_endpoint *ep,
@@ -325,7 +333,6 @@ enum sctp_disposition sctp_sf_do_5_1B_init(struct net *net, struct sctp_chunk *chunk = arg, *repl, *err_chunk; struct sctp_unrecognized_param *unk_param; struct sctp_association *new_asoc; - struct sctp_packet *packet; int len; /* 6.10 Bundling
@@ -375,32 +382,9 @@ enum sctp_disposition sctp_sf_do_5_1B_init(struct net *net, err_chunk = NULL; if (!sctp_verify_init(net, ep, asoc, chunk->chunk_hdr->type, (struct sctp_init_chunk *)chunk->chunk_hdr, chunk, - &err_chunk)) { - /* This chunk contains fatal error. It is to be discarded. - * Send an ABORT, with causes if there is any. - */ - if (err_chunk) { - packet = sctp_abort_pkt_new(net, ep, asoc, arg, - (__u8 *)(err_chunk->chunk_hdr) + - sizeof(struct sctp_chunkhdr), - ntohs(err_chunk->chunk_hdr->length) - - sizeof(struct sctp_chunkhdr)); - - sctp_chunk_free(err_chunk); - - if (packet) { - sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, - SCTP_PACKET(packet)); - SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS); - return SCTP_DISPOSITION_CONSUME; - } else { - return SCTP_DISPOSITION_NOMEM; - } - } else { - return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg, - commands); - } - } + &err_chunk)) + return sctp_abort_on_init_err(net, ep, asoc, chunk, arg, + commands, err_chunk); /* Grab the INIT header. */ chunk->subh.init_hdr = (struct sctp_inithdr *)chunk->skb->data;
@@ -525,7 +509,6 @@ enum sctp_disposition sctp_sf_do_5_1C_ack(struct net *net, struct sctp_init_chunk *initchunk; struct sctp_chunk *chunk = arg; struct sctp_chunk *err_chunk; - struct sctp_packet *packet; if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
@@ -548,50 +531,9 @@ enum sctp_disposition sctp_sf_do_5_1C_ack(struct net *net, err_chunk = NULL; if (!sctp_verify_init(net, ep, asoc, chunk->chunk_hdr->type, (struct sctp_init_chunk *)chunk->chunk_hdr, chunk, - &err_chunk)) { - - enum sctp_error error = SCTP_ERROR_NO_RESOURCE; - - /* This chunk contains fatal error. It is to be discarded. - * Send an ABORT, with causes. If there are no causes, - * then there wasn't enough memory. Just terminate - * the association. - */ - if (err_chunk) { - packet = sctp_abort_pkt_new(net, ep, asoc, arg, - (__u8 *)(err_chunk->chunk_hdr) + - sizeof(struct sctp_chunkhdr), - ntohs(err_chunk->chunk_hdr->length) - - sizeof(struct sctp_chunkhdr)); - - sctp_chunk_free(err_chunk); - - if (packet) { - sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, - SCTP_PACKET(packet)); - SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS); - error = SCTP_ERROR_INV_PARAM; - } - } - - /* SCTP-AUTH, Section 6.3: - * It should be noted that if the receiver wants to tear - * down an association in an authenticated way only, the - * handling of malformed packets should not result in - * tearing down the association. - * - * This means that if we only want to abort associations - * in an authenticated way (i.e AUTH+ABORT), then we - * can't destroy this association just because the packet - * was malformed. - */ - if (sctp_auth_recv_cid(SCTP_CID_ABORT, asoc)) - return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); - - SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); - return sctp_stop_t1_and_abort(net, commands, error, ECONNREFUSED, - asoc, chunk->transport); - } + &err_chunk)) + return sctp_abort_on_init_err(net, ep, asoc, chunk, arg, + commands, err_chunk); /* Tag the variable length parameters. Note that we never * convert the parameters in an INIT chunk.
@@ -1522,7 +1464,6 @@ static enum sctp_disposition sctp_sf_do_unexpected_init( struct sctp_unrecognized_param *unk_param; struct sctp_association *new_asoc; enum sctp_disposition retval; - struct sctp_packet *packet; int len; /* 6.10 Bundling
@@ -1566,31 +1507,9 @@ static enum sctp_disposition sctp_sf_do_unexpected_init( err_chunk = NULL; if (!sctp_verify_init(net, ep, asoc, chunk->chunk_hdr->type, (struct sctp_init_chunk *)chunk->chunk_hdr, chunk, - &err_chunk)) { - /* This chunk contains fatal error. It is to be discarded. - * Send an ABORT, with causes if there is any. - */ - if (err_chunk) { - packet = sctp_abort_pkt_new(net, ep, asoc, arg, - (__u8 *)(err_chunk->chunk_hdr) + - sizeof(struct sctp_chunkhdr), - ntohs(err_chunk->chunk_hdr->length) - - sizeof(struct sctp_chunkhdr)); - - if (packet) { - sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, - SCTP_PACKET(packet)); - SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS); - retval = SCTP_DISPOSITION_CONSUME; - } else { - retval = SCTP_DISPOSITION_NOMEM; - } - goto cleanup; - } else { - return sctp_sf_tabort_8_4_8(net, ep, asoc, type, arg, - commands); - } - } + &err_chunk)) + return sctp_abort_on_init_err(net, ep, asoc, chunk, arg, + commands, err_chunk); /* * Other parameters for the endpoint SHOULD be copied from the
@@ -1691,7 +1610,6 @@ static enum sctp_disposition sctp_sf_do_unexpected_init( nomem_retval: if (new_asoc) sctp_association_free(new_asoc); -cleanup: if (err_chunk) sctp_chunk_free(err_chunk); return retval;
@@ -6485,6 +6403,61 @@ static void sctp_send_stale_cookie_err(struct net *net, } } +static enum sctp_disposition sctp_abort_on_init_err( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const struct sctp_chunk *chunk, + void *arg, + struct sctp_cmd_seq *commands, + struct sctp_chunk *err_chunk) +{ + enum sctp_error error = SCTP_ERROR_NO_RESOURCE; + struct sctp_packet *packet; + struct sctp_chunkhdr *ch; + + if (!err_chunk) + return sctp_sf_tabort_8_4_8(net, ep, asoc, SCTP_ST_CHUNK(0), + arg, commands); + + ch = err_chunk->chunk_hdr; + packet = sctp_abort_pkt_new(net, ep, asoc, arg, + (__u8 *)ch + sizeof(*ch), + ntohs(ch->length) - sizeof(*ch)); + + sctp_chunk_free(err_chunk); + + if (packet) { + sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, + SCTP_PACKET(packet)); + SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS); + error = SCTP_ERROR_INV_PARAM; + } + + if (chunk->chunk_hdr->type != SCTP_CID_INIT_ACK) { + if (!packet) + return SCTP_DISPOSITION_NOMEM; + return SCTP_DISPOSITION_CONSUME; + } + /* SCTP-AUTH, Section 6.3: + * It should be noted that if the receiver wants to tear + * down an association in an authenticated way only, the + * handling of malformed packets should not result in + * tearing down the association. + * + * This means that if we only want to abort associations + * in an authenticated way (i.e AUTH+ABORT), then we + * can't destroy this association just because the packet + * was malformed. + */ + if (sctp_auth_recv_cid(SCTP_CID_ABORT, asoc)) + return sctp_sf_pdiscard(net, ep, asoc, SCTP_ST_CHUNK(0), arg, + commands); + + SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); + return sctp_stop_t1_and_abort(net, commands, error, ECONNREFUSED, + asoc, chunk->transport); +} /* Process a data chunk */ static int sctp_eat_data(const struct sctp_association *asoc,
--
2.47.1