Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'sctp-fix-a-vtag-verification-failure-caused-by-stale-inits'

Xin Long says:

====================
sctp: fix a vtag verification failure caused by stale INITs

Similar to Scenario B in commit 8e56b063c865 ( netfilter: handle the
connecting collision properly in nf_conntrack_proto_sctp"):

Scenario B: INIT_ACK is delayed until the peer completes its own handshake

192.168.1.2 > 192.168.1.1: sctp (1) [INIT] [init tag: 3922216408]
192.168.1.1 > 192.168.1.2: sctp (1) [INIT] [init tag: 144230885]
192.168.1.2 > 192.168.1.1: sctp (1) [INIT ACK] [init tag: 3922216408]
192.168.1.1 > 192.168.1.2: sctp (1) [COOKIE ECHO]
192.168.1.2 > 192.168.1.1: sctp (1) [COOKIE ACK]
192.168.1.1 > 192.168.1.2: sctp (1) [INIT ACK] [init tag: 3914796021] *

There is another case:

Scenario F: INIT is delayed until the peer completes its own handshake

192.168.1.2 > 192.168.1.1: sctp (1) [INIT] [init tag: 3922216408]
(OVS upcall)
192.168.1.1 > 192.168.1.2: sctp (1) [INIT] [init tag: 144230885]
192.168.1.2 > 192.168.1.1: sctp (1) [INIT ACK] [init tag: 3922216408]
192.168.1.1 > 192.168.1.2: sctp (1) [COOKIE ECHO]
192.168.1.2 > 192.168.1.1: sctp (1) [COOKIE ACK]
192.168.1.2 > 192.168.1.1: sctp (1) [INIT] [init tag: 3922216408]
(delayed)
192.168.1.1 > 192.168.1.2: sctp (1) [INIT ACK] [init tag: 3914796021] *

In this case, the delayed INIT (e.g. due to OVS upcall) is recorded by
conntrack, which prevents vtag verification from dropping the unexpected
INIT-ACK in nf_conntrack_sctp_packet():

vtag = ct->proto.sctp.vtag[!dir];
if (!ct->proto.sctp.init[!dir] && vtag && vtag != ih->init_tag)
goto out_unlock;

This happens because ct->proto.sctp.init[!dir] is set by the delayed INIT,
even though it is stale.

Fix this in two parts:

- In netfilter: Do not record INITs whose init_tag matches the peer vtag,
as they carry no new handshake state in the 1st patch.

- In SCTP: Prevent endpoints from responding to such INITs with INIT-ACK,
ensuring correctness even when middleboxes lack the netfilter fix in
the 2nd patch.

A follow-up selftest for this scenario will be posted in a separate patch
by Yi Chen.
====================

Link: https://patch.msgid.link/cover.1777214801.git.lucien.xin@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+13 -3
+7 -3
net/netfilter/nf_conntrack_proto_sctp.c
··· 466 466 if (!ih) 467 467 goto out_unlock; 468 468 469 - if (ct->proto.sctp.init[dir] && ct->proto.sctp.init[!dir]) 470 - ct->proto.sctp.init[!dir] = 0; 471 - ct->proto.sctp.init[dir] = 1; 469 + /* Do not record INIT matching peer vtag (stale or retransmitted INIT). */ 470 + if (old_state == SCTP_CONNTRACK_NONE || 471 + ct->proto.sctp.vtag[!dir] != ih->init_tag) { 472 + if (ct->proto.sctp.init[dir] && ct->proto.sctp.init[!dir]) 473 + ct->proto.sctp.init[!dir] = 0; 474 + ct->proto.sctp.init[dir] = 1; 475 + } 472 476 473 477 pr_debug("Setting vtag %x for dir %d\n", ih->init_tag, !dir); 474 478 ct->proto.sctp.vtag[!dir] = ih->init_tag;
+6
net/sctp/sm_statefuns.c
··· 1556 1556 /* Tag the variable length parameters. */ 1557 1557 chunk->param_hdr.v = skb_pull(chunk->skb, sizeof(struct sctp_inithdr)); 1558 1558 1559 + if (asoc->state >= SCTP_STATE_ESTABLISHED) { 1560 + /* Discard INIT matching peer vtag after handshake completion (stale INIT). */ 1561 + if (ntohl(chunk->subh.init_hdr->init_tag) == asoc->peer.i.init_tag) 1562 + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); 1563 + } 1564 + 1559 1565 /* Verify the INIT chunk before processing it. */ 1560 1566 err_chunk = NULL; 1561 1567 if (!sctp_verify_init(net, ep, asoc, chunk->chunk_hdr->type,