The current CLC proposal message construction uses a mix of `ini->smc_type_v1/v2` and `pclc_base->hdr.typev1/v2` to decide whether to include optional extensions (IPv6 prefix extension for v1, and v2 extension). This leads to a critical inconsistency: when `smc_clc_prfx_set()` fails - for example, in IPv6-only environments with only link-local addresses, or when the local IP address and the outgoing interface’s network address are not in the same subnet. As a result, the proposal message is assembled using the stale `ini->smc_type_v1` value—causing the IPv6 prefix extension to be included even though the header indicates v1 is not supported. The peer then receives a malformed CLC proposal where the header type does not match the payload, and immediately resets the connection. Fix this by consistently using `pclc_base->hdr.typev1` and `pclc_base->hdr.typev2`—the authoritative fields that reflect the actual capabilities advertised in the CLC header—when deciding whether to include optional extensions, as required by the SMC-R v2 specification ("V1 IP Subnet Extension and V2 Extension only present if applicable"). Fixes: 8c3dca341aea ("net/smc: build and send V2 CLC proposal") Signed-off-by: D. Wythe --- net/smc/smc_clc.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/net/smc/smc_clc.c b/net/smc/smc_clc.c index 157aace169d4..d9ff5f433720 100644 --- a/net/smc/smc_clc.c +++ b/net/smc/smc_clc.c @@ -922,7 +922,7 @@ int smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini) htons(smc_ism_get_chid(ini->ism_dev[0])); } } - if (ini->smc_type_v2 == SMC_TYPE_N) { + if (pclc_base->hdr.typev2 == SMC_TYPE_N) { pclc_smcd->v2_ext_offset = 0; } else { struct smc_clc_eid_entry *ueident; @@ -931,7 +931,7 @@ int smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini) v2_ext->hdr.flag.release = SMC_RELEASE; v2_ext_offset = sizeof(*pclc_smcd) - offsetofend(struct smc_clc_msg_smcd, v2_ext_offset); - if (ini->smc_type_v1 != SMC_TYPE_N) + if (pclc_base->hdr.typev1 != SMC_TYPE_N) v2_ext_offset += sizeof(*pclc_prfx) + pclc_prfx->ipv6_prefixes_cnt * sizeof(ipv6_prfx[0]); @@ -949,7 +949,7 @@ int smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini) } read_unlock(&smc_clc_eid_table.lock); } - if (smcd_indicated(ini->smc_type_v2)) { + if (smcd_indicated(pclc_base->hdr.typev2)) { struct smcd_gid smcd_gid; u8 *eid = NULL; int entry = 0; @@ -987,7 +987,7 @@ int smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini) } v2_ext->hdr.ism_gid_cnt = entry; } - if (smcr_indicated(ini->smc_type_v2)) { + if (smcr_indicated(pclc_base->hdr.typev2)) { memcpy(v2_ext->roce, ini->smcrv2.ib_gid_v2, SMC_GID_SIZE); v2_ext->max_conns = net->smc.sysctl_max_conns_per_lgr; v2_ext->max_links = net->smc.sysctl_max_links_per_lgr; @@ -1003,7 +1003,7 @@ int smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini) vec[i++].iov_len = sizeof(*pclc_base); vec[i].iov_base = pclc_smcd; vec[i++].iov_len = sizeof(*pclc_smcd); - if (ini->smc_type_v1 != SMC_TYPE_N) { + if (pclc_base->hdr.typev1 != SMC_TYPE_N) { vec[i].iov_base = pclc_prfx; vec[i++].iov_len = sizeof(*pclc_prfx); if (pclc_prfx->ipv6_prefixes_cnt > 0) { @@ -1012,11 +1012,11 @@ int smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini) sizeof(ipv6_prfx[0]); } } - if (ini->smc_type_v2 != SMC_TYPE_N) { + if (pclc_base->hdr.typev2 != SMC_TYPE_N) { vec[i].iov_base = v2_ext; vec[i++].iov_len = sizeof(*v2_ext) + (v2_ext->hdr.eid_cnt * SMC_MAX_EID_LEN); - if (smcd_indicated(ini->smc_type_v2)) { + if (smcd_indicated(pclc_base->hdr.typev2)) { vec[i].iov_base = smcd_v2_ext; vec[i++].iov_len = sizeof(*smcd_v2_ext); if (ini->ism_offered_cnt) { -- 2.45.0