Merge branch 'sctp-handle-wrapped-and-duplicate-reconf-responses'

Jun Yang says:

====================
sctp: handle wrapped and duplicate RECONF responses

Fix response sequence zero lookup first, then make RECONF response
handling idempotent with an outstanding-request bitmask.
====================

Link: https://patch.msgid.link/20260824081832.98717-1-juny24602@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2026-08-25 13:33:10 +02:00
commit b95dbcdd71
2 changed files with 33 additions and 15 deletions

View File

@ -2057,7 +2057,7 @@ struct sctp_association {
force_delay:1;
__u8 strreset_enable;
__u8 strreset_outstanding; /* request param count on the fly */
__u8 strreset_outstanding; /* request param bitmask on the fly */
__u32 strreset_outseq; /* Update after receiving response */
__u32 strreset_inseq; /* Update after receiving request */

View File

@ -22,6 +22,15 @@
#include <net/sctp/sm.h>
#include <net/sctp/stream_sched.h>
#define SCTP_STRRESET_MASK(type) \
BIT(ntohs(type) - ntohs(SCTP_PARAM_RESET_OUT_REQUEST))
#define SCTP_STRRESET_TEST(asoc, type) \
((asoc)->strreset_outstanding & SCTP_STRRESET_MASK(type))
#define SCTP_STRRESET_SET(asoc, type) \
((asoc)->strreset_outstanding |= SCTP_STRRESET_MASK(type))
#define SCTP_STRRESET_CLEAR(asoc, type) \
((asoc)->strreset_outstanding &= ~SCTP_STRRESET_MASK(type))
static void sctp_stream_shrink_out(struct sctp_stream *stream, __u16 outcnt)
{
struct sctp_association *asoc;
@ -372,7 +381,10 @@ int sctp_send_reset_streams(struct sctp_association *asoc,
goto out;
}
asoc->strreset_outstanding = out + in;
if (out)
SCTP_STRRESET_SET(asoc, SCTP_PARAM_RESET_OUT_REQUEST);
if (in)
SCTP_STRRESET_SET(asoc, SCTP_PARAM_RESET_IN_REQUEST);
out:
return retval;
@ -417,7 +429,7 @@ int sctp_send_reset_assoc(struct sctp_association *asoc)
return retval;
}
asoc->strreset_outstanding = 1;
SCTP_STRRESET_SET(asoc, SCTP_PARAM_RESET_TSN_REQUEST);
return 0;
}
@ -474,7 +486,10 @@ int sctp_send_add_streams(struct sctp_association *asoc,
goto out;
}
asoc->strreset_outstanding = !!out + !!in;
if (out)
SCTP_STRRESET_SET(asoc, SCTP_PARAM_RESET_ADD_OUT_STREAMS);
if (in)
SCTP_STRRESET_SET(asoc, SCTP_PARAM_RESET_ADD_IN_STREAMS);
out:
return retval;
@ -482,7 +497,7 @@ int sctp_send_add_streams(struct sctp_association *asoc,
static struct sctp_paramhdr *sctp_chunk_lookup_strreset_param(
struct sctp_association *asoc, __be32 resp_seq,
__be16 type)
__be16 type, bool match_seq)
{
struct sctp_chunk *chunk = asoc->strreset_chunk;
struct sctp_reconf_chunk *hdr;
@ -499,7 +514,7 @@ static struct sctp_paramhdr *sctp_chunk_lookup_strreset_param(
*/
struct sctp_strreset_tsnreq *req = param.v;
if ((!resp_seq || req->request_seq == resp_seq) &&
if ((!match_seq || req->request_seq == resp_seq) &&
(!type || type == req->param_hdr.type))
return param.v;
}
@ -564,13 +579,14 @@ struct sctp_chunk *sctp_process_strreset_outreq(
if (asoc->strreset_chunk) {
if (!sctp_chunk_lookup_strreset_param(
asoc, outreq->response_seq,
SCTP_PARAM_RESET_IN_REQUEST)) {
SCTP_PARAM_RESET_IN_REQUEST, true) ||
!SCTP_STRRESET_TEST(asoc, SCTP_PARAM_RESET_IN_REQUEST)) {
/* same process with outstanding isn't 0 */
result = SCTP_STRRESET_ERR_IN_PROGRESS;
goto out;
}
asoc->strreset_outstanding--;
SCTP_STRRESET_CLEAR(asoc, SCTP_PARAM_RESET_IN_REQUEST);
asoc->strreset_outseq++;
if (!asoc->strreset_outstanding) {
@ -669,7 +685,7 @@ struct sctp_chunk *sctp_process_strreset_inreq(
SCTP_SO(stream, i)->state = SCTP_STREAM_CLOSED;
asoc->strreset_chunk = chunk;
asoc->strreset_outstanding = 1;
SCTP_STRRESET_SET(asoc, SCTP_PARAM_RESET_OUT_REQUEST);
sctp_chunk_hold(asoc->strreset_chunk);
result = SCTP_STRRESET_PERFORMED;
@ -816,13 +832,14 @@ struct sctp_chunk *sctp_process_strreset_addstrm_out(
if (asoc->strreset_chunk) {
if (!sctp_chunk_lookup_strreset_param(
asoc, 0, SCTP_PARAM_RESET_ADD_IN_STREAMS)) {
asoc, 0, SCTP_PARAM_RESET_ADD_IN_STREAMS, false) ||
!SCTP_STRRESET_TEST(asoc, SCTP_PARAM_RESET_ADD_IN_STREAMS)) {
/* same process with outstanding isn't 0 */
result = SCTP_STRRESET_ERR_IN_PROGRESS;
goto out;
}
asoc->strreset_outstanding--;
SCTP_STRRESET_CLEAR(asoc, SCTP_PARAM_RESET_ADD_IN_STREAMS);
asoc->strreset_outseq++;
if (!asoc->strreset_outstanding) {
@ -899,7 +916,7 @@ struct sctp_chunk *sctp_process_strreset_addstrm_in(
goto out;
asoc->strreset_chunk = chunk;
asoc->strreset_outstanding = 1;
SCTP_STRRESET_SET(asoc, SCTP_PARAM_RESET_ADD_OUT_STREAMS);
sctp_chunk_hold(asoc->strreset_chunk);
stream->outcnt = outcnt;
@ -927,8 +944,9 @@ struct sctp_chunk *sctp_process_strreset_resp(
struct sctp_paramhdr *req;
__u32 result;
req = sctp_chunk_lookup_strreset_param(asoc, resp->response_seq, 0);
if (!req)
req = sctp_chunk_lookup_strreset_param(asoc, resp->response_seq, 0,
true);
if (!req || !SCTP_STRRESET_TEST(asoc, req->type))
return NULL;
result = ntohl(resp->result);
@ -1078,7 +1096,7 @@ struct sctp_chunk *sctp_process_strreset_resp(
nums, 0, GFP_ATOMIC);
}
asoc->strreset_outstanding--;
SCTP_STRRESET_CLEAR(asoc, req->type);
asoc->strreset_outseq++;
/* remove everything for this reconf request */