Merge branch 'sctp-re-point-retained-control-chunks-on-association-migration'

Jun Yang says:

====================
sctp: re-point retained control chunks on association migration

This series fixes ownership of retained SCTP control chunks during socket
migration. Patch 1 carries David Lee's shared-key prerequisite.

v3: https://lore.kernel.org/20260926100359.78731-1-juny24602@gmail.com
====================

Link: https://patch.msgid.link/20261009074244.3718-1-juny24602@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-10-15 17:02:27 -07:00
commit ee3deb0151
3 changed files with 51 additions and 12 deletions

View File

@ -252,6 +252,7 @@ struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc,
struct sctp_fwdtsn_skip *skiplist);
struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc,
__u16 key_id);
void sctp_control_set_owner_w(struct sctp_chunk *chunk);
struct sctp_chunk *sctp_make_strreset_req(const struct sctp_association *asoc,
__u16 stream_num, __be16 *stream_list,
bool out, bool in);

View File

@ -94,7 +94,7 @@ static void sctp_control_release_owner(struct sk_buff *skb)
}
}
static void sctp_control_set_owner_w(struct sctp_chunk *chunk)
void sctp_control_set_owner_w(struct sctp_chunk *chunk)
{
struct sctp_association *asoc = chunk->asoc;
struct sk_buff *skb = chunk->skb;
@ -107,7 +107,8 @@ static void sctp_control_set_owner_w(struct sctp_chunk *chunk)
* For now don't do anything for now.
*/
if (chunk->auth) {
chunk->shkey = asoc->shkey;
if (!chunk->shkey)
chunk->shkey = asoc->shkey;
sctp_auth_shkey_hold(chunk->shkey);
}
skb->sk = asoc ? asoc->base.sk : NULL;

View File

@ -147,25 +147,47 @@ static inline void sctp_set_owner_w(struct sctp_chunk *chunk)
static void sctp_clear_owner_w(struct sctp_chunk *chunk)
{
/* Keep the shkey alive until the new owner takes its reference. */
if (chunk->shkey)
sctp_auth_shkey_hold(chunk->shkey);
skb_orphan(chunk->skb);
}
static void sctp_set_owner_w_migrate(struct sctp_chunk *chunk)
{
struct sctp_shared_key *shkey = chunk->shkey;
if (chunk->msg)
sctp_set_owner_w(chunk);
else
sctp_control_set_owner_w(chunk);
if (shkey)
sctp_auth_shkey_release(shkey);
}
static void sctp_process_tx_chunk(struct sctp_association *asoc,
struct sctp_chunk *chunk, bool clear,
void (*cb)(struct sctp_chunk *))
{
if ((clear && asoc->base.sk == chunk->skb->sk) ||
(!clear && asoc->base.sk != chunk->skb->sk))
cb(chunk);
}
#define traverse_and_process() \
do { \
msg = chunk->msg; \
if (msg == prev_msg) \
continue; \
list_for_each_entry(c, &msg->chunks, frag_list) { \
if ((clear && asoc->base.sk == c->skb->sk) || \
(!clear && asoc->base.sk != c->skb->sk)) \
cb(c); \
} \
list_for_each_entry(c, &msg->chunks, frag_list) \
sctp_process_tx_chunk(asoc, c, clear, cb); \
prev_msg = msg; \
} while (0)
static void sctp_for_each_tx_datachunk(struct sctp_association *asoc,
bool clear,
void (*cb)(struct sctp_chunk *))
static void sctp_for_each_tx_chunk(struct sctp_association *asoc,
bool clear,
void (*cb)(struct sctp_chunk *))
{
struct sctp_datamsg *msg, *prev_msg = NULL;
@ -188,6 +210,21 @@ static void sctp_for_each_tx_datachunk(struct sctp_association *asoc,
list_for_each_entry(chunk, &q->out_chunk_list, list)
traverse_and_process();
list_for_each_entry(chunk, &q->control_chunk_list, list)
sctp_process_tx_chunk(asoc, chunk, clear, cb);
list_for_each_entry(chunk, &asoc->asconf_ack_list, transmitted_list)
sctp_process_tx_chunk(asoc, chunk, clear, cb);
list_for_each_entry(chunk, &asoc->addip_chunk_list, list)
sctp_process_tx_chunk(asoc, chunk, clear, cb);
if (asoc->strreset_chunk)
sctp_process_tx_chunk(asoc, asoc->strreset_chunk, clear, cb);
if (asoc->addip_last_asconf)
sctp_process_tx_chunk(asoc, asoc->addip_last_asconf, clear, cb);
}
static void sctp_for_each_rx_skb(struct sctp_association *asoc, struct sock *sk,
@ -9635,9 +9672,9 @@ static int sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
* paths won't try to lock it and then oldsk.
*/
lock_sock_nested(newsk, SINGLE_DEPTH_NESTING);
sctp_for_each_tx_datachunk(assoc, true, sctp_clear_owner_w);
sctp_for_each_tx_chunk(assoc, true, sctp_clear_owner_w);
sctp_assoc_migrate(assoc, newsk);
sctp_for_each_tx_datachunk(assoc, false, sctp_set_owner_w);
sctp_for_each_tx_chunk(assoc, false, sctp_set_owner_w_migrate);
/* If the association on the newsk is already closed before accept()
* is called, set RCV_SHUTDOWN flag.