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 --- v2: - Pass cid to sctp_abort_on_init_err(). - Delete chunk param from sctp_abort_on_init_err() and get chunk from arg param. - Jump to label 'out:' when err_chunk is NULL and cid is INIT_ACK in sctp_abort_on_init_err(), noted by Sashiko. --- net/sctp/sm_statefuns.c | 187 ++++++++++++++++++---------------------- 1 file changed, 85 insertions(+), 102 deletions(-) diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 9b23c11cbb9e..8c636f045e45 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -68,6 +68,13 @@ 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, + enum sctp_cid cid, 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 +332,7 @@ 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; + enum sctp_cid cid; int len; /* 6.10 Bundling @@ -373,34 +380,12 @@ enum sctp_disposition sctp_sf_do_5_1B_init(struct net *net, /* Verify the INIT chunk before processing it. */ err_chunk = NULL; - if (!sctp_verify_init(net, ep, asoc, chunk->chunk_hdr->type, + cid = chunk->chunk_hdr->type; + if (!sctp_verify_init(net, ep, asoc, cid, (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, cid, arg, commands, + err_chunk); /* Grab the INIT header. */ chunk->subh.init_hdr = (struct sctp_inithdr *)chunk->skb->data; @@ -525,7 +510,7 @@ 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; + enum sctp_cid cid; if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); @@ -546,52 +531,12 @@ enum sctp_disposition sctp_sf_do_5_1C_ack(struct net *net, /* Verify the INIT chunk before processing it. */ err_chunk = NULL; - if (!sctp_verify_init(net, ep, asoc, chunk->chunk_hdr->type, + cid = chunk->chunk_hdr->type; + if (!sctp_verify_init(net, ep, asoc, cid, (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, cid, arg, commands, + err_chunk); /* Tag the variable length parameters. Note that we never * convert the parameters in an INIT chunk. @@ -1522,7 +1467,7 @@ 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; + enum sctp_cid cid; int len; /* 6.10 Bundling @@ -1564,33 +1509,12 @@ static enum sctp_disposition sctp_sf_do_unexpected_init( /* Verify the INIT chunk before processing it. */ err_chunk = NULL; - if (!sctp_verify_init(net, ep, asoc, chunk->chunk_hdr->type, + cid = chunk->chunk_hdr->type; + if (!sctp_verify_init(net, ep, asoc, cid, (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, cid, arg, commands, + err_chunk); /* * Other parameters for the endpoint SHOULD be copied from the @@ -1691,7 +1615,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 +6408,66 @@ 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, + enum sctp_cid cid, void *arg, + struct sctp_cmd_seq *commands, + struct sctp_chunk *err_chunk) +{ + enum sctp_error error = SCTP_ERROR_NO_RESOURCE; + struct sctp_chunk *chunk = arg; + struct sctp_packet *packet; + struct sctp_chunkhdr *ch; + + if (!err_chunk) { + if (cid == SCTP_CID_INIT_ACK) + goto out; + 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 (cid != SCTP_CID_INIT_ACK) { + if (!packet) + return SCTP_DISPOSITION_NOMEM; + return SCTP_DISPOSITION_CONSUME; + } + +out: + /* 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