sctp_assoc_update() folds a temporary association into the existing one when an INIT collision is resolved. It copies asoc->c, peer.rwnd, peer.sack_needed, peer.auth_capable and peer.i, and nothing else. The remaining peer capability bits therefore keep whatever the surviving association was given when it was created, rather than what the peer advertised in the INIT that caused the collision. Forward TSN is the visible case. The INIT-ACK is built from the temporary association, so it advertises Forward-TSN-Supported; once the collision is resolved the surviving association holds peer.prsctp_capable == 0, and the first FORWARD TSN chunk the peer sends is answered with ERROR "Unrecognized chunk type". The peer does not expect this, having been told the capability was supported. ecn_capable, asconf_capable, reconf_capable and intl_capable are lost in the same way. The two address flags fail the other way round. sctp_process_param() clears ipv4_address and ipv6_address and sets them from the peer's Supported Address Types, but only on the temporary association. The surviving association keeps the permissive defaults from sctp_association_init(), so it can believe a peer supports an address family that peer never advertised. peer.auth_capable is already carried, added by commit 1be9a950c646 ("net: sctp: inherit auth_capable on INIT collisions") for the same reason. This extends that to the rest of the block. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Warren Briggs --- Changes since v2: - dropped asoc->peer.hostname_address. The field no longer exists, removed by commit bd4b28189469 ("sctp: delete the obsolete code for the host name address param"). - wrapped the commit message at 75 columns. - added the Fixes tag. - retargeted at net, subject prefix corrected. Changes since v1: - added ecn_capable, asconf_capable, reconf_capable and intl_capable, the missing fields identified in review of v1. - also added ipv4_address and ipv6_address, which are set from the peer's Supported Address Types on the temporary association and are lost at the merge in the same way. Testing. An INIT collision was resolved between two sockets on one host and the resulting association's peer capabilities read back, with a second, non-collided association created in the same run as a control, on an unpatched and a patched kernel: prsctp_capable SCTP_PR_SUPPORTED unpatched 0, patched 1 reconf_capable SCTP_RECONFIG_SUPPORTED unpatched 0, patched 1 intl_capable SCTP_INTERLEAVING_SUPPORTED unpatched 0, patched 1 asconf_capable SCTP_ASCONF_SUPPORTED unpatched 0, patched 1 ecn_capable SCTP_ECN_SUPPORTED unpatched 0, patched 1 ipv4_address sctp_diag sctpi_peer_capable unpatched 1, patched 0, with the peer advertising IPv6 only ipv6_address sctp_diag sctpi_peer_capable unpatched 1, patched 0, with the peer advertising IPv4 only Three of those were also confirmed on the wire. An unpatched kernel that has advertised Forward-TSN-Supported in its INIT-ACK answers a FORWARD TSN chunk with ERROR cause 6; a patched one accepts it. SCTP_RESET_STREAMS and sctp_bindx(SCTP_BINDX_ADD_ADDR) put a RE-CONFIG and an ASCONF on the wire on a patched kernel and produce nothing on an unpatched one. diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 4521be3..0bd0a66 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -1107,6 +1107,13 @@ int sctp_assoc_update(struct sctp_association *asoc, asoc->peer.rwnd = new->peer.rwnd; asoc->peer.sack_needed = new->peer.sack_needed; asoc->peer.auth_capable = new->peer.auth_capable; + asoc->peer.prsctp_capable = new->peer.prsctp_capable; + asoc->peer.ecn_capable = new->peer.ecn_capable; + asoc->peer.asconf_capable = new->peer.asconf_capable; + asoc->peer.reconf_capable = new->peer.reconf_capable; + asoc->peer.intl_capable = new->peer.intl_capable; + asoc->peer.ipv4_address = new->peer.ipv4_address; + asoc->peer.ipv6_address = new->peer.ipv6_address; asoc->peer.i = new->peer.i; if (!sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_INITIAL,