From nobody Fri Apr 3 04:34:53 2026 Received: from mail-pj1-f51.google.com (mail-pj1-f51.google.com [209.85.216.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8DC4E34CFDD for ; Wed, 4 Mar 2026 05:36:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772602594; cv=none; b=DZW0Ng6TmHw1O4Stc442/a5TQ0+FdtGRtITbZgC9M0NIUm0XvFwwMkttOOuaoIkHWOrlr1y4EEmXyDA4BoSdr9jfhmPdKsySYku6Sgl6Otzs1GqUsiw4W9EVKhoZDOX4HmgTErTTy3c+vlTKUMboC5ZtFcQsAdXjnjHZUvf3y8Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772602594; c=relaxed/simple; bh=7qA9Q6Pz/ZThLLziRlzsP+Ns3IK5HTYVcg1/SCKOubM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=YWdO7MLIhuRtxAUr00aL3G7OVTQLLOTWP4oXMt0OPc7pdC9433zUMQ8SxmmUwTsgMc8NZN/OX/HYLn/3FbT5VnubN1W3RwErFKj6dKHBUnmhfZ/D31Brc+d1/99ax944Ux55h64PIMxQVfz5b8S8buxNT0hftWnVoZbUZc/e5YI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=cH76yQK+; arc=none smtp.client-ip=209.85.216.51 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="cH76yQK+" Received: by mail-pj1-f51.google.com with SMTP id 98e67ed59e1d1-35691a231a7so3973046a91.3 for ; Tue, 03 Mar 2026 21:36:33 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772602593; x=1773207393; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=xNCDZkyqVlGeQwFgjROGPRwx2fLC/OmgR/7kGK793I8=; b=cH76yQK+A9h6nZkjDPrZmT9qn339hzoy2A+8gT1IdpuEcAk7yttMr+cK+u23yi9O11 LEfL/xVFnSgW6GGSbrX6/RXIWZdMsD3y37IIYAFMEcivx0qhfHHXqquF3oV4i4rnLOkf VrZ3y6GMLGNgbsvQHWAzrtubog4DdjqR3JjzsATMvOoaN03YNLJ0nNW4nA1U6hvEGaBn O5HKblyXVw7pRxktaqyAmtE61E2+8fVVf75TY2zcweCbkGpCoKp8jS+LdsrNbIULL/lT muEoSXuvIG4unAjC5uawSzvyttRPlVMFnj5ou1LZ4D97MrvnFe9QDWA5xYfUoSrxYocQ 6J1g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772602593; x=1773207393; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=xNCDZkyqVlGeQwFgjROGPRwx2fLC/OmgR/7kGK793I8=; b=QuUttEmJaVRC9jauvSJpsTw5vbIYaqZTxMUpIaVUMSPOPJNJjIRtmqClROOS5DcDV9 GS27ZymfLLEwjC+FZR25AOlBnRRB09qqr+uY2+RiJuTzynpJwVi5t7G0zrb2PISoeeOr SM29lIL4Pq5TBT2yB1b5/zl8Se1pJVxjBUai06kbAkvqLo1WPNPW+JUmeD0+Z97m9Dlq vTR8r/32tAMkgBpbxHRfghIefMfzcKfaCtuYYqoz6h58wpQVW3BcnzFXKQgMH/4zrt53 nqDweUklfg/9VW3F75xdY+I+GhH9OrN8aWTurCO3sdsQDoCUP5moDpCj6FRIYlbCJ6na I9YQ== X-Forwarded-Encrypted: i=1; AJvYcCURU568S9dsAGq8Lpcltm4BKsm+so27M/xsEWh0s8qzdU24OVSLy3qdDDbm0LhMHBOSdORo0kJVmsdWY50=@vger.kernel.org X-Gm-Message-State: AOJu0YwdVewRkcDtTvA9K20xAPTncI57j7G0a3vwhLqvDoHbJ+lvEf6U en1QoLW/mv6YQp0ZlkTukMDNMgaVcWrzWuhME6NY4PDi75SIWJbUaD3a X-Gm-Gg: ATEYQzwT/8eCvCV3dkrpAEy4H8UkhvIDRRFoQ8vukolpbolbGwFQfabPP4E+kXVAM7s Q24tqIcQ15kDXyfEbXz7XdGsav7lq5/npRHuPkdeS2lpovulNvCVPB7b0Klqqznl2bK85lyipNk 3rxpE4kRKkosepTQJv4er+rJsDtZwL3HMfyyuc92cEeuY+bxJ/AQdkdQ7o9pTznh/jVr40K9qyu iNUnWq8wnKR2bFrwrYcZVbMDuJzT2F5CbXr+gCd8gvURZrwLgr6aPtQZGakv5yvLt1jBMMKeo4Y 1TOgOZbDYRpsAm/Kw0ZfDyHRaqJ6+++eT8gmBSKPYYEILaHUD8aLMIaP+S+uGKZoqN1ws24t9SC ip+fKNeYe7cdqMPJc2KUrQ+msa0S5PLo10jMIT3N4tKkIw6Q1tUBPgrgmBQ6CCtzZ7fq6KCGi9M wQL8fHZpeZQFkHK/JrH1emGCFtrMM7ELRLGt1OPT+adQ== X-Received: by 2002:a17:90b:538c:b0:356:2db3:1206 with SMTP id 98e67ed59e1d1-359a69dae4emr997051a91.13.1772602592975; Tue, 03 Mar 2026 21:36:32 -0800 (PST) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3599c090bfdsm4020057a91.8.2026.03.03.21.36.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 03 Mar 2026 21:36:32 -0800 (PST) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: chuck.lever@oracle.com, hare@kernel.org, kernel-tls-handshake@lists.linux.dev, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-nvme@lists.infradead.org, linux-nfs@vger.kernel.org Cc: kbusch@kernel.org, axboe@kernel.dk, hch@lst.de, sagi@grimberg.me, kch@nvidia.com, hare@suse.de, alistair23@gmail.com, Alistair Francis Subject: [PATCH v7 1/5] net/handshake: Store the key serial number on completion Date: Wed, 4 Mar 2026 15:34:56 +1000 Message-ID: <20260304053500.590630-2-alistair.francis@wdc.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260304053500.590630-1-alistair.francis@wdc.com> References: <20260304053500.590630-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alistair Francis Allow userspace to include a key serial number when completing a handshake with the HANDSHAKE_CMD_DONE command. We then store this serial number and will provide it back to userspace in the future. This allows userspace to save data to the keyring and then restore that data later. This will be used to support the TLS KeyUpdate operation, as now userspace can resume information about a established session. Signed-off-by: Alistair Francis Reviewed-by: Hannes Reincke Reviewed-by: Chuck Lever Reviewed-by: Sagi Grimberg --- v7: - No change v6: - Add the "ta_" and "th_" prefixs v5: - Change name to "handshake session ID" v4: - No change v3: - No change v2: - Change "key-serial" to "session-id" Documentation/netlink/specs/handshake.yaml | 4 ++++ Documentation/networking/tls-handshake.rst | 1 + drivers/nvme/host/tcp.c | 3 ++- drivers/nvme/target/tcp.c | 3 ++- include/net/handshake.h | 5 ++++- include/uapi/linux/handshake.h | 1 + net/handshake/genl.c | 5 +++-- net/handshake/tlshd.c | 15 +++++++++++++-- net/sunrpc/svcsock.c | 4 +++- net/sunrpc/xprtsock.c | 4 +++- 10 files changed, 36 insertions(+), 9 deletions(-) diff --git a/Documentation/netlink/specs/handshake.yaml b/Documentation/net= link/specs/handshake.yaml index 95c3fade7a8d..a273bc74d26f 100644 --- a/Documentation/netlink/specs/handshake.yaml +++ b/Documentation/netlink/specs/handshake.yaml @@ -87,6 +87,9 @@ attribute-sets: name: remote-auth type: u32 multi-attr: true + - + name: session-id + type: u32 =20 operations: list: @@ -123,6 +126,7 @@ operations: - status - sockfd - remote-auth + - session-id =20 mcast-groups: list: diff --git a/Documentation/networking/tls-handshake.rst b/Documentation/net= working/tls-handshake.rst index 6f5ea1646a47..c58b3c8b16c1 100644 --- a/Documentation/networking/tls-handshake.rst +++ b/Documentation/networking/tls-handshake.rst @@ -60,6 +60,7 @@ fills in a structure that contains the parameters of the = request: key_serial_t ta_my_privkey; unsigned int ta_num_peerids; key_serial_t ta_my_peerids[5]; + key_serial_t ta_handshake_session_id; }; =20 The @ta_sock field references an open and connected socket. The consumer diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c index 9ab3f61196a3..204f45f791a3 100644 --- a/drivers/nvme/host/tcp.c +++ b/drivers/nvme/host/tcp.c @@ -1676,7 +1676,8 @@ static void nvme_tcp_set_queue_io_cpu(struct nvme_tcp= _queue *queue) qid, queue->io_cpu); } =20 -static void nvme_tcp_tls_done(void *data, int status, key_serial_t pskid) +static void nvme_tcp_tls_done(void *data, int status, key_serial_t pskid, + key_serial_t handshake_session_id) { struct nvme_tcp_queue *queue =3D data; struct nvme_tcp_ctrl *ctrl =3D queue->ctrl; diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index acc71a26733f..63613e60f566 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -1808,7 +1808,8 @@ static int nvmet_tcp_tls_key_lookup(struct nvmet_tcp_= queue *queue, } =20 static void nvmet_tcp_tls_handshake_done(void *data, int status, - key_serial_t peerid) + key_serial_t peerid, + key_serial_t handshake_session_id) { struct nvmet_tcp_queue *queue =3D data; =20 diff --git a/include/net/handshake.h b/include/net/handshake.h index 8ebd4f9ed26e..d9b2411d5523 100644 --- a/include/net/handshake.h +++ b/include/net/handshake.h @@ -15,10 +15,12 @@ enum { TLS_NO_PEERID =3D 0, TLS_NO_CERT =3D 0, TLS_NO_PRIVKEY =3D 0, + TLS_NO_SESSION_ID =3D 0, }; =20 typedef void (*tls_done_func_t)(void *data, int status, - key_serial_t peerid); + key_serial_t peerid, + key_serial_t handshake_session_id); =20 struct tls_handshake_args { struct socket *ta_sock; @@ -31,6 +33,7 @@ struct tls_handshake_args { key_serial_t ta_my_privkey; unsigned int ta_num_peerids; key_serial_t ta_my_peerids[5]; + key_serial_t ta_handshake_session_id; }; =20 int tls_client_hello_anon(const struct tls_handshake_args *args, gfp_t fla= gs); diff --git a/include/uapi/linux/handshake.h b/include/uapi/linux/handshake.h index d7e40f594888..7fb3ef7f64df 100644 --- a/include/uapi/linux/handshake.h +++ b/include/uapi/linux/handshake.h @@ -56,6 +56,7 @@ enum { HANDSHAKE_A_DONE_STATUS =3D 1, HANDSHAKE_A_DONE_SOCKFD, HANDSHAKE_A_DONE_REMOTE_AUTH, + HANDSHAKE_A_DONE_SESSION_ID, =20 __HANDSHAKE_A_DONE_MAX, HANDSHAKE_A_DONE_MAX =3D (__HANDSHAKE_A_DONE_MAX - 1) diff --git a/net/handshake/genl.c b/net/handshake/genl.c index 870612609491..2542c13bb562 100644 --- a/net/handshake/genl.c +++ b/net/handshake/genl.c @@ -17,10 +17,11 @@ static const struct nla_policy handshake_accept_nl_poli= cy[HANDSHAKE_A_ACCEPT_HAN }; =20 /* HANDSHAKE_CMD_DONE - do */ -static const struct nla_policy handshake_done_nl_policy[HANDSHAKE_A_DONE_R= EMOTE_AUTH + 1] =3D { +static const struct nla_policy handshake_done_nl_policy[HANDSHAKE_A_DONE_S= ESSION_ID + 1] =3D { [HANDSHAKE_A_DONE_STATUS] =3D { .type =3D NLA_U32, }, [HANDSHAKE_A_DONE_SOCKFD] =3D { .type =3D NLA_S32, }, [HANDSHAKE_A_DONE_REMOTE_AUTH] =3D { .type =3D NLA_U32, }, + [HANDSHAKE_A_DONE_SESSION_ID] =3D { .type =3D NLA_U32, }, }; =20 /* Ops table for handshake */ @@ -36,7 +37,7 @@ static const struct genl_split_ops handshake_nl_ops[] =3D= { .cmd =3D HANDSHAKE_CMD_DONE, .doit =3D handshake_nl_done_doit, .policy =3D handshake_done_nl_policy, - .maxattr =3D HANDSHAKE_A_DONE_REMOTE_AUTH, + .maxattr =3D HANDSHAKE_A_DONE_SESSION_ID, .flags =3D GENL_CMD_CAP_DO, }, }; diff --git a/net/handshake/tlshd.c b/net/handshake/tlshd.c index 8f9532a15f43..e72f45bdc226 100644 --- a/net/handshake/tlshd.c +++ b/net/handshake/tlshd.c @@ -26,7 +26,8 @@ =20 struct tls_handshake_req { void (*th_consumer_done)(void *data, int status, - key_serial_t peerid); + key_serial_t peerid, + key_serial_t handshake_session_id); void *th_consumer_data; =20 int th_type; @@ -39,6 +40,8 @@ struct tls_handshake_req { =20 unsigned int th_num_peerids; key_serial_t th_peerid[5]; + + key_serial_t th_handshake_session_id; }; =20 static struct tls_handshake_req * @@ -55,6 +58,7 @@ tls_handshake_req_init(struct handshake_req *req, treq->th_num_peerids =3D 0; treq->th_certificate =3D TLS_NO_CERT; treq->th_privkey =3D TLS_NO_PRIVKEY; + treq->th_handshake_session_id =3D TLS_NO_SESSION_ID; return treq; } =20 @@ -83,6 +87,13 @@ static void tls_handshake_remote_peerids(struct tls_hand= shake_req *treq, if (i >=3D treq->th_num_peerids) break; } + + nla_for_each_attr(nla, head, len, rem) { + if (nla_type(nla) =3D=3D HANDSHAKE_A_DONE_SESSION_ID) { + treq->th_handshake_session_id =3D nla_get_u32(nla); + break; + } + } } =20 /** @@ -105,7 +116,7 @@ static void tls_handshake_done(struct handshake_req *re= q, set_bit(HANDSHAKE_F_REQ_SESSION, &req->hr_flags); =20 treq->th_consumer_done(treq->th_consumer_data, -status, - treq->th_peerid[0]); + treq->th_peerid[0], treq->th_handshake_session_id); } =20 #if IS_ENABLED(CONFIG_KEYS) diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index f28c6076f7e8..5b003ac66a54 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -455,13 +455,15 @@ static void svc_tcp_kill_temp_xprt(struct svc_xprt *x= prt) * @data: address of xprt to wake * @status: status of handshake * @peerid: serial number of key containing the remote peer's identity + * @handshake_session_id: serial number of the userspace session ID * * If a security policy is specified as an export option, we don't * have a specific export here to check. So we set a "TLS session * is present" flag on the xprt and let an upper layer enforce local * security policy. */ -static void svc_tcp_handshake_done(void *data, int status, key_serial_t pe= erid) +static void svc_tcp_handshake_done(void *data, int status, key_serial_t pe= erid, + key_serial_t handshake_session_id) { struct svc_xprt *xprt =3D data; struct svc_sock *svsk =3D container_of(xprt, struct svc_sock, sk_xprt); diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 2e1fe6013361..86eb51a9b224 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -2590,9 +2590,11 @@ static int xs_tcp_tls_finish_connecting(struct rpc_x= prt *lower_xprt, * @data: address of xprt to wake * @status: status of handshake * @peerid: serial number of key containing the remote's identity + * @handshake_session_id: serial number of the userspace session ID * */ -static void xs_tls_handshake_done(void *data, int status, key_serial_t pee= rid) +static void xs_tls_handshake_done(void *data, int status, key_serial_t pee= rid, + key_serial_t handshake_session_id) { struct rpc_xprt *lower_xprt =3D data; struct sock_xprt *lower_transport =3D --=20 2.53.0 From nobody Fri Apr 3 04:34:53 2026 Received: from mail-pj1-f47.google.com (mail-pj1-f47.google.com [209.85.216.47]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8CEBA30E84D for ; Wed, 4 Mar 2026 05:36:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772602602; cv=none; b=FSrzvp8mTFIlXhc5haxhpaNO5OIN5k4RUXV2w5X0G12ZGs6bmgvHKNrR4IhPKxL77sveKhvkGdf4HbPRWJBwXOYoLE4hjFkZ4wP0L+oUWJ9c5lA7WzUqkBGkwXFeIutNDGUzsnpr5CF/Y79XEy5lBbx+3bAuSCw7fOxu43avWDk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772602602; c=relaxed/simple; bh=Fh2w9GegkIjBGfvJYSj0dj5p+FWtLjoeXIvuAgbxRcI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=bSeXgEtduRGpmIqL23Zjit9eufHbBI5efW8MUE0Ns+qEZeP/82yCYlmHtU+fwc1ZLaY0ufrpQ9jbhHs7n5LyKUpQGHeURRfeuU8yYP0FI1LIjGBvTbZdnFQqbowJrmrfwhClvFxYTTvajEpeNIj1t4HLwN1M08/3pVjXip9+Yyc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Bgkmp1KD; arc=none smtp.client-ip=209.85.216.47 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Bgkmp1KD" Received: by mail-pj1-f47.google.com with SMTP id 98e67ed59e1d1-35984cd0335so1421507a91.0 for ; Tue, 03 Mar 2026 21:36:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772602599; x=1773207399; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=zXXONp/ELYPAk8Ra1lPAUCJk3t+/mDBCyOGivtl5sfY=; b=Bgkmp1KDJuaQ07bo2dOHwHZOm2eu5vU1YzXn/9rL4JC/Sx4E981uMZi6kt8Exs+Rj/ vSgYNagHqeOkH/P5UwaYgWJfgofpOHD2XI4Xy7noH/grWbYE1n6lehuA9He6JCDDJe/K R2N6yIQPUS4frlqnd3TvNkNyD6Cd9pQkECBOF9DaRGytMm4s8pSjrlh5h2TTcuFg6iY/ rWEjmbc8jlW9pzyJQDkp135gF96rVhRcU5eBbjkxOyl/fXDIjaoecrI4YL4kTMvpcadk wmKTZp5JMb0OBvGKrOOaJ0PPloxD/DT1S7E0HOwUqlAKUuYuwyrtgqhMuROneIkLqVwn 0XjQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772602599; x=1773207399; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=zXXONp/ELYPAk8Ra1lPAUCJk3t+/mDBCyOGivtl5sfY=; b=ZMUZywJqViWZS/1dsBiBUspybSEletJ4A04ddLPFbt10/ciAP4zlzdhIh3YdLRa7IP 3mEQyVfyfJkMe5itLoYTRUqpwrkHuHHaQEAhgwsMPIymkIqvpzJiT/9kjnDTyB7XSMGs 5FxeiUORI7ctHDQDxs2MAKdOn2AAQ/mmbkQg7OQqZjj0+qES9R/V4Ak9JGnUPIRJCe1w /cL88t38yKV2b7pNtedTbsgOs7P37CJk8sEb3myCihyWc+Us6ILdfzfkwT6C9J/eYkZ5 F5b7kQjHj2CQl2ZhODI41iw6QX/fRgfw3XMlF4yOoIg+Im24FYGAlNgkquAZWY3B1NDL sQbA== X-Forwarded-Encrypted: i=1; AJvYcCVpS8Gp5EFtai4ye9eM6RkV3FtuUHZHA+XzHqd9+841v2LygozdVOPyFVtP0wvwYNAogD9E895ThTTOdoM=@vger.kernel.org X-Gm-Message-State: AOJu0YwUtRnPJ0wNI9Uz29M8tga7I3gan5wfxhyXgscsCwXG2+x5ubDC N/1uLygucDre6hiBgaiCBDwSsrfklyhoAt9EFjDuDXfipyKY+VJGb9sp X-Gm-Gg: ATEYQzwICo4NEoxSJLgE2MUBdXsBtordEhIVEsQDPdiw5HfYNDkWfpt/weDxjsqhYlc NtpPoMsUreH3q2S9zXkS5heLzQAd1OyPWgkk8KWruaSU5mNHg7nxj0F5XqH8AUQZXux9qVHoWFk 4YFWgUcT9B7AgKXvPS6anFpsCnIARjg0/5u042mMgF/uF5gmVpAh593di0ZOEYfVR9NBt8d1w9n rQkfqkxcVkq0ge9OAy8+nW4pBwjw4+zUKlIV74A416oAOXvudT9DzARZT6W0REQb0VINCY/18JT LP62YoJbaS+KcgLjZ4/SG3FpwThNbIFFn00eYcehfK1Yqjy4D07tLjWWka+U2mwjiGf8rykUdGx XN4t9VMwJbbnfSILczsJwMHEHT+U6j14U01uirdY8tfZlCAunKHlTkzwSfUVuBe2puCLUXIoCzD YSzZSkLvxKWyhQL/+RieQWpgW3QYiTJHk+b/ZneXDD2w== X-Received: by 2002:a17:90a:ec87:b0:359:8a78:5696 with SMTP id 98e67ed59e1d1-359a6ce6a46mr960709a91.1.1772602599018; Tue, 03 Mar 2026 21:36:39 -0800 (PST) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3599c090bfdsm4020057a91.8.2026.03.03.21.36.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 03 Mar 2026 21:36:38 -0800 (PST) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: chuck.lever@oracle.com, hare@kernel.org, kernel-tls-handshake@lists.linux.dev, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-nvme@lists.infradead.org, linux-nfs@vger.kernel.org Cc: kbusch@kernel.org, axboe@kernel.dk, hch@lst.de, sagi@grimberg.me, kch@nvidia.com, hare@suse.de, alistair23@gmail.com, Alistair Francis Subject: [PATCH v7 2/5] net/handshake: Define handshake_req_keyupdate Date: Wed, 4 Mar 2026 15:34:57 +1000 Message-ID: <20260304053500.590630-3-alistair.francis@wdc.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260304053500.590630-1-alistair.francis@wdc.com> References: <20260304053500.590630-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alistair Francis Add a new handshake_req_keyupdate() function which is similar to the existing handshake_req_submit(). The new handshake_req_keyupdate() does not add the request to the hash table (unlike handshake_req_submit()) but instead uses the existing request from the initial handshake. During the initial handshake handshake_req_submit() will add the request to the hash table. The request will not be removed from the hash table unless the socket is closed (reference count hits zero). After the initial handshake handshake_req_keyupdate() can be used to re-use the existing request in the hash table to trigger a KeyUpdate with userspace. Signed-off-by: Alistair Francis Reviewed-by: Hannes Reinecke --- v7: - No change v6: - New patch net/handshake/handshake.h | 2 + net/handshake/request.c | 97 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/net/handshake/handshake.h b/net/handshake/handshake.h index a48163765a7a..04feacd1e21d 100644 --- a/net/handshake/handshake.h +++ b/net/handshake/handshake.h @@ -84,6 +84,8 @@ void handshake_req_hash_destroy(void); void *handshake_req_private(struct handshake_req *req); struct handshake_req *handshake_req_hash_lookup(struct sock *sk); struct handshake_req *handshake_req_next(struct handshake_net *hn, int cla= ss); +int handshake_req_keyupdate(struct socket *sock, struct handshake_req *req, + gfp_t flags); int handshake_req_submit(struct socket *sock, struct handshake_req *req, gfp_t flags); void handshake_complete(struct handshake_req *req, unsigned int status, diff --git a/net/handshake/request.c b/net/handshake/request.c index 2829adbeb149..5653c8f69dce 100644 --- a/net/handshake/request.c +++ b/net/handshake/request.c @@ -196,6 +196,103 @@ struct handshake_req *handshake_req_next(struct hands= hake_net *hn, int class) } EXPORT_SYMBOL_IF_KUNIT(handshake_req_next); =20 +/** + * handshake_req_keyupdate - Submit a KeyUpdate request + * @sock: open socket on which to perform the handshake + * @req: handshake arguments, this must already be allocated and exist + * in the hash table, which happens as part of handshake_req_submit() + * @flags: memory allocation flags + * + * Return values: + * %0: Request queued + * %-EINVAL: Invalid argument + * %-EBUSY: A handshake is already under way for this socket + * %-ESRCH: No handshake agent is available + * %-EFAULT: An initial handshake hasn't happened yet + * %-EAGAIN: Too many pending handshake requests + * %-ENOMEM: Failed to allocate memory + * %-EMSGSIZE: Failed to construct notification message + * %-EOPNOTSUPP: Handshake module not initialized + * + * A zero return value from handshake_req_submit() means that + * exactly one subsequent completion callback is guaranteed. + * + * A negative return value from handshake_req_submit() means that + * no completion callback will be done and that @req has been + * destroyed. + */ +int handshake_req_keyupdate(struct socket *sock, struct handshake_req *req, + gfp_t flags) +{ + struct handshake_net *hn; + struct net *net; + struct handshake_req *req_lookup; + int ret; + + if (!sock || !req || !sock->file) { + kfree(req); + return -EINVAL; + } + + req->hr_sk =3D sock->sk; + if (!req->hr_sk) { + kfree(req); + return -EINVAL; + } + req->hr_odestruct =3D req->hr_sk->sk_destruct; + req->hr_sk->sk_destruct =3D handshake_sk_destruct; + + ret =3D -EOPNOTSUPP; + net =3D sock_net(req->hr_sk); + hn =3D handshake_pernet(net); + if (!hn) + goto out_err; + + ret =3D -EAGAIN; + if (READ_ONCE(hn->hn_pending) >=3D hn->hn_pending_max) + goto out_err; + + spin_lock(&hn->hn_lock); + ret =3D -EOPNOTSUPP; + if (test_bit(HANDSHAKE_F_NET_DRAINING, &hn->hn_flags)) + goto out_unlock; + + ret =3D -EFAULT; + req_lookup =3D handshake_req_hash_lookup(sock->sk); + if (!req_lookup) + goto out_unlock; + + ret =3D -EBUSY; + if (req_lookup !=3D req) + goto out_unlock; + if (!__add_pending_locked(hn, req)) + goto out_unlock; + spin_unlock(&hn->hn_lock); + + test_and_clear_bit(HANDSHAKE_F_REQ_COMPLETED, &req->hr_flags); + + ret =3D handshake_genl_notify(net, req->hr_proto, flags); + if (ret) { + trace_handshake_notify_err(net, req, req->hr_sk, ret); + if (remove_pending(hn, req)) + goto out_err; + } + + /* Prevent socket release while a handshake request is pending */ + sock_hold(req->hr_sk); + + trace_handshake_submit(net, req, req->hr_sk); + return 0; + +out_unlock: + spin_unlock(&hn->hn_lock); +out_err: + trace_handshake_submit_err(net, req, req->hr_sk, ret); + handshake_req_destroy(req); + return ret; +} +EXPORT_SYMBOL(handshake_req_keyupdate); + /** * handshake_req_submit - Submit a handshake request * @sock: open socket on which to perform the handshake --=20 2.53.0 From nobody Fri Apr 3 04:34:53 2026 Received: from mail-pj1-f48.google.com (mail-pj1-f48.google.com [209.85.216.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BCD2930E84D for ; Wed, 4 Mar 2026 05:36:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772602607; cv=none; b=qB8EhzIqU27Bden4V9Flloqcl5GiFQWtjoGv5DcHBCy1oZb3U3XjRKxTtIfobWhvxx5uDxSj1ZKjqYM06TguC7Iq6KWAZuz3CsIE9eZceP8Sdh9MgvAS0Zc3TqAuXZ4j4m6JDFHrfBUofS/mwGZ2fNinj+/3G/ZBuz/uOyDBfUM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772602607; c=relaxed/simple; bh=VqhcGKAJRjtRuoJc4J7oADAGv+ArrFQaBFuFFPKfX98=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZRDwOpwHKxOXRPgrFERgZp48Erf9JWY6O1y2B0dIZpq3w5ADhtSR8xR5KrSEKTqsixE4PTQEEI3WOt1NjZ8gck714JWaTZYFVmPO3e7Ocq18hR0V+I/r20C7Qp9C6+C9nYJOpZbgnYR8zTeK6ePE/gpFFAgx8XvZPcxjHOkVfLA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=I191fUWK; arc=none smtp.client-ip=209.85.216.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="I191fUWK" Received: by mail-pj1-f48.google.com with SMTP id 98e67ed59e1d1-359863611faso1337416a91.3 for ; Tue, 03 Mar 2026 21:36:45 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772602605; x=1773207405; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=YBh3mRAyM98WdbFlOpQdr++fXfCeSb+WIiI2vsSeFJY=; b=I191fUWK6J00M8nUnvQqLHXUQ5QXvTw7VzKEixDkPElT7NEVx7X3trrPMIK5FNfmHG 6fatEvh56jytkgN2r3McgdUtH/JskVGBvqgG9SvUW8WKW/5naxUoQzjF6eXa2eTEcfSy IW7M5PztbCYSPMh1hG1Rp7TVIvEMUb5TGVNXtnu/VYpfAue3NlW5TOm3BgoXJb80Xotk vOMRpuwcLz3EQ50d7q3jtFCGS0Ko1ScHoeaQ2U27pHLtHVYt6s7S0a1BksYD9CYrMKWm eUbK1p11ZDx+fNyPfybH0SsYh5XL1dGG9jBhYhOgaJEXAthvUELuZanrTnxkUvaFM7l8 8uAA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772602605; x=1773207405; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=YBh3mRAyM98WdbFlOpQdr++fXfCeSb+WIiI2vsSeFJY=; b=CW9x8kLhMPr084ietI69aumJIr7KGDO30unTpTRbbMgl2v8n4TXAflhk9+YI4yKt6V Rl6ZdrTmwscaipIqf1vTHwiNFDEqXlbxbHi5HSGLfU/0rYr6AQfIgGa1ruRsgdaErqWI xG5qJuwAzvrFxrURqriXM4m1MFI3/AZ1lOjKvydN/g/IuH6OM+m1F0EklcuuLZ3rfbb3 OUNDiBfL2BjJh8oP3SkawwPuCa6kzOc12u6rgDG8HNu68Qj1zwJVDQiQtl50OOcZyUez qFnSyF2XbPBZyhF7aqFuVS/ooeX0MUlGd6cUeIqVgsBpNxQoAE7YTchmlT76pfK/ARYy phUg== X-Forwarded-Encrypted: i=1; AJvYcCVBrVrOKpkwrSoedUEWCz2mD9cJh2TB9J68yoQuNMN98ssPYLoZBxiFIAnfE5KvugphrrDO1nNTfHhStSg=@vger.kernel.org X-Gm-Message-State: AOJu0Ywgy9Jq5iOY/G68+mcwHH/Jp9iuaAH71iCDkzLAol13Km7L1LBI 0FvI5ZS1EeSCscU/dljqbaIkex+YERctXcW9SVD63G65kXyAudZtwA26 X-Gm-Gg: ATEYQzz1bNpEwWDhuOxeim5AYRa+os0Ja2/zldXNefq3gHkLRYIQ4ndvDKIGMpO2GH9 7pSpyo+BrcaeHOu8f1nSFgne2P4oL/eXFs2W46VaYrafdlKZXig/xLN5as9jThhLGeT6PL2ldYm +UVB2SrBXMRxEAYwpO6sQtzIZljsbP92743WPffG7dFR43v1UunZ1TRPWPrZ7WjwkGfYE2ZaCxE y4wMJCpe+o+vXlJcaob2wA7JXM/FQIY3U6ct4Vl2M2walCiN50DFRevWg/rhw3eHiJ99LXWfHXb 7cDRhi+4h9JwXQZJ4pbcfU5+roZGa0t4PiefPE2FwO+mIYl9iCLK1VQL3ogFzdw+SaIIASohsnh /9f7sm/23K+FKk/jcy/P2zsKIN7n56PXuJ+8PlAu5CWhYXauHA47WT6XVD/CwtX4d1HQlG2gGrO cLUwj7dQ1XUaqFvADOET7o+lkaWREpeH7D6vf4+TEJxb9CMdYRkmJA X-Received: by 2002:a17:90a:d44f:b0:354:a09a:1016 with SMTP id 98e67ed59e1d1-359a6a9f53dmr915696a91.30.1772602605048; Tue, 03 Mar 2026 21:36:45 -0800 (PST) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3599c090bfdsm4020057a91.8.2026.03.03.21.36.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 03 Mar 2026 21:36:44 -0800 (PST) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: chuck.lever@oracle.com, hare@kernel.org, kernel-tls-handshake@lists.linux.dev, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-nvme@lists.infradead.org, linux-nfs@vger.kernel.org Cc: kbusch@kernel.org, axboe@kernel.dk, hch@lst.de, sagi@grimberg.me, kch@nvidia.com, hare@suse.de, alistair23@gmail.com, Alistair Francis Subject: [PATCH v7 3/5] net/handshake: Support KeyUpdate message types Date: Wed, 4 Mar 2026 15:34:58 +1000 Message-ID: <20260304053500.590630-4-alistair.francis@wdc.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260304053500.590630-1-alistair.francis@wdc.com> References: <20260304053500.590630-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alistair Francis When reporting the msg-type to userspace let's also support reporting KeyUpdate events. This supports reporting a client/server event and if the other side requested a KeyUpdateRequest. Link: https://datatracker.ietf.org/doc/html/rfc8446#section-4.6.3 Signed-off-by: Alistair Francis Reviewed-by: Chuck Lever Reviewed-by: Hannes Reinecke --- v7: - No change v6: - Init th_key_update_request field to 0 v5: - Drop clientkeyupdaterequest and serverkeyupdaterequest v4: - Don't overload existing functions, instead create new ones v3: - Fixup yamllint and kernel-doc failures Documentation/netlink/specs/handshake.yaml | 16 ++++- drivers/nvme/host/tcp.c | 15 +++- drivers/nvme/target/tcp.c | 10 ++- include/net/handshake.h | 6 ++ include/uapi/linux/handshake.h | 11 +++ net/handshake/tlshd.c | 84 +++++++++++++++++++++- 6 files changed, 134 insertions(+), 8 deletions(-) diff --git a/Documentation/netlink/specs/handshake.yaml b/Documentation/net= link/specs/handshake.yaml index a273bc74d26f..2f77216c8ddf 100644 --- a/Documentation/netlink/specs/handshake.yaml +++ b/Documentation/netlink/specs/handshake.yaml @@ -21,12 +21,18 @@ definitions: type: enum name: msg-type value-start: 0 - entries: [unspec, clienthello, serverhello] + entries: [unspec, clienthello, serverhello, clientkeyupdate, + serverkeyupdate] - type: enum name: auth value-start: 0 entries: [unspec, unauth, psk, x509] + - + type: enum + name: key-update-type + value-start: 0 + entries: [unspec, send, received, received_request_update] =20 attribute-sets: - @@ -74,6 +80,13 @@ attribute-sets: - name: keyring type: u32 + - + name: key-update-request + type: u32 + enum: key-update-type + - + name: session-id + type: u32 - name: done attributes: @@ -116,6 +129,7 @@ operations: - certificate - peername - keyring + - session-id - name: done doc: Handler reports handshake completion diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c index 204f45f791a3..8b6172dd1c0f 100644 --- a/drivers/nvme/host/tcp.c +++ b/drivers/nvme/host/tcp.c @@ -19,6 +19,7 @@ #include #include #include +#include =20 #include "nvme.h" #include "fabrics.h" @@ -205,6 +206,10 @@ static struct workqueue_struct *nvme_tcp_wq; static const struct blk_mq_ops nvme_tcp_mq_ops; static const struct blk_mq_ops nvme_tcp_admin_mq_ops; static int nvme_tcp_try_send(struct nvme_tcp_queue *queue); +static int nvme_tcp_start_tls(struct nvme_ctrl *nctrl, + struct nvme_tcp_queue *queue, + key_serial_t pskid, + enum handshake_key_update_type keyupdate); =20 static inline struct nvme_tcp_ctrl *to_tcp_ctrl(struct nvme_ctrl *ctrl) { @@ -1711,7 +1716,8 @@ static void nvme_tcp_tls_done(void *data, int status,= key_serial_t pskid, =20 static int nvme_tcp_start_tls(struct nvme_ctrl *nctrl, struct nvme_tcp_queue *queue, - key_serial_t pskid) + key_serial_t pskid, + enum handshake_key_update_type keyupdate) { int qid =3D nvme_tcp_queue_id(queue); int ret; @@ -1733,7 +1739,10 @@ static int nvme_tcp_start_tls(struct nvme_ctrl *nctr= l, args.ta_timeout_ms =3D tls_handshake_timeout * 1000; queue->tls_err =3D -EOPNOTSUPP; init_completion(&queue->tls_complete); - ret =3D tls_client_hello_psk(&args, GFP_KERNEL); + if (keyupdate =3D=3D HANDSHAKE_KEY_UPDATE_TYPE_UNSPEC) + ret =3D tls_client_hello_psk(&args, GFP_KERNEL); + else + ret =3D tls_client_keyupdate_psk(&args, GFP_KERNEL, keyupdate); if (ret) { dev_err(nctrl->device, "queue %d: failed to start TLS: %d\n", qid, ret); @@ -1883,7 +1892,7 @@ static int nvme_tcp_alloc_queue(struct nvme_ctrl *nct= rl, int qid, =20 /* If PSKs are configured try to start TLS */ if (nvme_tcp_tls_configured(nctrl) && pskid) { - ret =3D nvme_tcp_start_tls(nctrl, queue, pskid); + ret =3D nvme_tcp_start_tls(nctrl, queue, pskid, HANDSHAKE_KEY_UPDATE_TYP= E_UNSPEC); if (ret) goto err_init_connect; } diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index 63613e60f566..7f1c651a52a4 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -1861,7 +1861,8 @@ static void nvmet_tcp_tls_handshake_timeout(struct wo= rk_struct *w) kref_put(&queue->kref, nvmet_tcp_release_queue); } =20 -static int nvmet_tcp_tls_handshake(struct nvmet_tcp_queue *queue) +static int nvmet_tcp_tls_handshake(struct nvmet_tcp_queue *queue, + enum handshake_key_update_type keyupdate) { int ret =3D -EOPNOTSUPP; struct tls_handshake_args args; @@ -1880,7 +1881,10 @@ static int nvmet_tcp_tls_handshake(struct nvmet_tcp_= queue *queue) args.ta_keyring =3D key_serial(queue->port->nport->keyring); args.ta_timeout_ms =3D tls_handshake_timeout * 1000; =20 - ret =3D tls_server_hello_psk(&args, GFP_KERNEL); + if (keyupdate =3D=3D HANDSHAKE_KEY_UPDATE_TYPE_UNSPEC) + ret =3D tls_server_hello_psk(&args, GFP_KERNEL); + else + ret =3D tls_server_keyupdate_psk(&args, GFP_KERNEL, keyupdate); if (ret) { kref_put(&queue->kref, nvmet_tcp_release_queue); pr_err("failed to start TLS, err=3D%d\n", ret); @@ -1962,7 +1966,7 @@ static void nvmet_tcp_alloc_queue(struct nvmet_tcp_po= rt *port, sk->sk_data_ready =3D port->data_ready; write_unlock_bh(&sk->sk_callback_lock); if (!nvmet_tcp_try_peek_pdu(queue)) { - if (!nvmet_tcp_tls_handshake(queue)) + if (!nvmet_tcp_tls_handshake(queue, HANDSHAKE_KEY_UPDATE_TYPE_UNSPEC)) return; /* TLS handshake failed, terminate the connection */ goto out_destroy_sq; diff --git a/include/net/handshake.h b/include/net/handshake.h index d9b2411d5523..54fb101202d2 100644 --- a/include/net/handshake.h +++ b/include/net/handshake.h @@ -10,6 +10,8 @@ #ifndef _NET_HANDSHAKE_H #define _NET_HANDSHAKE_H =20 +#include + enum { TLS_NO_KEYRING =3D 0, TLS_NO_PEERID =3D 0, @@ -39,8 +41,12 @@ struct tls_handshake_args { int tls_client_hello_anon(const struct tls_handshake_args *args, gfp_t fla= gs); int tls_client_hello_x509(const struct tls_handshake_args *args, gfp_t fla= gs); int tls_client_hello_psk(const struct tls_handshake_args *args, gfp_t flag= s); +int tls_client_keyupdate_psk(const struct tls_handshake_args *args, gfp_t = flags, + enum handshake_key_update_type keyupdate); int tls_server_hello_x509(const struct tls_handshake_args *args, gfp_t fla= gs); int tls_server_hello_psk(const struct tls_handshake_args *args, gfp_t flag= s); +int tls_server_keyupdate_psk(const struct tls_handshake_args *args, gfp_t = flags, + enum handshake_key_update_type keyupdate); =20 bool tls_handshake_cancel(struct sock *sk); void tls_handshake_close(struct socket *sock); diff --git a/include/uapi/linux/handshake.h b/include/uapi/linux/handshake.h index 7fb3ef7f64df..ff8b423044ff 100644 --- a/include/uapi/linux/handshake.h +++ b/include/uapi/linux/handshake.h @@ -20,6 +20,8 @@ enum handshake_msg_type { HANDSHAKE_MSG_TYPE_UNSPEC, HANDSHAKE_MSG_TYPE_CLIENTHELLO, HANDSHAKE_MSG_TYPE_SERVERHELLO, + HANDSHAKE_MSG_TYPE_CLIENTKEYUPDATE, + HANDSHAKE_MSG_TYPE_SERVERKEYUPDATE, }; =20 enum handshake_auth { @@ -29,6 +31,13 @@ enum handshake_auth { HANDSHAKE_AUTH_X509, }; =20 +enum handshake_key_update_type { + HANDSHAKE_KEY_UPDATE_TYPE_UNSPEC, + HANDSHAKE_KEY_UPDATE_TYPE_SEND, + HANDSHAKE_KEY_UPDATE_TYPE_RECEIVED, + HANDSHAKE_KEY_UPDATE_TYPE_RECEIVED_REQUEST_UPDATE, +}; + enum { HANDSHAKE_A_X509_CERT =3D 1, HANDSHAKE_A_X509_PRIVKEY, @@ -47,6 +56,8 @@ enum { HANDSHAKE_A_ACCEPT_CERTIFICATE, HANDSHAKE_A_ACCEPT_PEERNAME, HANDSHAKE_A_ACCEPT_KEYRING, + HANDSHAKE_A_ACCEPT_KEY_UPDATE_REQUEST, + HANDSHAKE_A_ACCEPT_SESSION_ID, =20 __HANDSHAKE_A_ACCEPT_MAX, HANDSHAKE_A_ACCEPT_MAX =3D (__HANDSHAKE_A_ACCEPT_MAX - 1) diff --git a/net/handshake/tlshd.c b/net/handshake/tlshd.c index e72f45bdc226..d102211e9d77 100644 --- a/net/handshake/tlshd.c +++ b/net/handshake/tlshd.c @@ -41,6 +41,7 @@ struct tls_handshake_req { unsigned int th_num_peerids; key_serial_t th_peerid[5]; =20 + unsigned int th_key_update_request; key_serial_t th_handshake_session_id; }; =20 @@ -58,7 +59,9 @@ tls_handshake_req_init(struct handshake_req *req, treq->th_num_peerids =3D 0; treq->th_certificate =3D TLS_NO_CERT; treq->th_privkey =3D TLS_NO_PRIVKEY; - treq->th_handshake_session_id =3D TLS_NO_SESSION_ID; + treq->th_key_update_request =3D 0; + treq->th_handshake_session_id =3D args->ta_handshake_session_id; + return treq; } =20 @@ -265,6 +268,16 @@ static int tls_handshake_accept(struct handshake_req *= req, break; } =20 + ret =3D nla_put_u32(msg, HANDSHAKE_A_ACCEPT_SESSION_ID, + treq->th_handshake_session_id); + if (ret < 0) + goto out_cancel; + + ret =3D nla_put_u32(msg, HANDSHAKE_A_ACCEPT_KEY_UPDATE_REQUEST, + treq->th_key_update_request); + if (ret < 0) + goto out_cancel; + genlmsg_end(msg, hdr); return genlmsg_reply(msg, info); =20 @@ -373,6 +386,44 @@ int tls_client_hello_psk(const struct tls_handshake_ar= gs *args, gfp_t flags) } EXPORT_SYMBOL(tls_client_hello_psk); =20 +/** + * tls_client_keyupdate_psk - request a PSK-based TLS handshake on a socket + * @args: socket and handshake parameters for this request + * @flags: memory allocation control flags + * @keyupdate: specifies the type of KeyUpdate operation + * + * Return values: + * %0: Handshake request enqueue; ->done will be called when complete + * %-EINVAL: Wrong number of local peer IDs + * %-ESRCH: No user agent is available + * %-ENOMEM: Memory allocation failed + */ +int tls_client_keyupdate_psk(const struct tls_handshake_args *args, gfp_t = flags, + enum handshake_key_update_type keyupdate) +{ + struct tls_handshake_req *treq; + struct handshake_req *req; + unsigned int i; + + if (!args->ta_num_peerids || + args->ta_num_peerids > ARRAY_SIZE(treq->th_peerid)) + return -EINVAL; + + req =3D handshake_req_hash_lookup(args->ta_sock->sk); + if (!req) + return -ENOMEM; + treq =3D tls_handshake_req_init(req, args); + treq->th_type =3D HANDSHAKE_MSG_TYPE_CLIENTKEYUPDATE; + treq->th_key_update_request =3D keyupdate; + treq->th_auth_mode =3D HANDSHAKE_AUTH_PSK; + treq->th_num_peerids =3D args->ta_num_peerids; + for (i =3D 0; i < args->ta_num_peerids; i++) + treq->th_peerid[i] =3D args->ta_my_peerids[i]; + + return handshake_req_keyupdate(args->ta_sock, req, flags); +} +EXPORT_SYMBOL(tls_client_keyupdate_psk); + /** * tls_server_hello_x509 - request a server TLS handshake on a socket * @args: socket and handshake parameters for this request @@ -429,6 +480,37 @@ int tls_server_hello_psk(const struct tls_handshake_ar= gs *args, gfp_t flags) } EXPORT_SYMBOL(tls_server_hello_psk); =20 +/** + * tls_server_keyupdate_psk - request a server TLS KeyUpdate on a socket + * @args: socket and handshake parameters for this request + * @flags: memory allocation control flags + * @keyupdate: specifies the type of KeyUpdate operation + * + * Return values: + * %0: Handshake request enqueue; ->done will be called when complete + * %-ESRCH: No user agent is available + * %-ENOMEM: Memory allocation failed + */ +int tls_server_keyupdate_psk(const struct tls_handshake_args *args, gfp_t = flags, + enum handshake_key_update_type keyupdate) +{ + struct tls_handshake_req *treq; + struct handshake_req *req; + + req =3D handshake_req_hash_lookup(args->ta_sock->sk); + if (!req) + return -ENOMEM; + treq =3D tls_handshake_req_init(req, args); + treq->th_type =3D HANDSHAKE_MSG_TYPE_SERVERKEYUPDATE; + treq->th_key_update_request =3D keyupdate; + treq->th_auth_mode =3D HANDSHAKE_AUTH_PSK; + treq->th_num_peerids =3D 1; + treq->th_peerid[0] =3D args->ta_my_peerids[0]; + + return handshake_req_keyupdate(args->ta_sock, req, flags); +} +EXPORT_SYMBOL(tls_server_keyupdate_psk); + /** * tls_handshake_cancel - cancel a pending handshake * @sk: socket on which there is an ongoing handshake --=20 2.53.0 From nobody Fri Apr 3 04:34:53 2026 Received: from mail-pj1-f42.google.com (mail-pj1-f42.google.com [209.85.216.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B5CA837F8A1 for ; Wed, 4 Mar 2026 05:36:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772602613; cv=none; b=LdFtBkBmfl0HzCMw4z3DHMz/9s5r4MJf2BZdRBFdIkLm0xtqqr9K6Zx3FZOTfrFoaLQFWE5A6MgM8kmbMe2pvseUdm9dIDrhIFtuvWCDxiXM1raCIFDrr0V4Dq0CUvWn2kQFgHlByH3P/Feo8prHCR8Un4NiHFbp1CX8sfRQPCg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772602613; c=relaxed/simple; bh=X6tX6MsfjpCCm5XWJnWLuYwr3D2OjljkcPDWiXIpOAY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=U1piiqmOJaFIH9HI3N8iXvde98Wi+znyNN2htXzK0VaZwNxCd0PMwbX/n6NJAZAEli0WbXechCRiUw6QK08Sp6Mv7Xb0Rdv1rgS/VqITnjqo3gv3Z/VUURIZTKmcBY4dKYpcd+iuZsSAQrthicINGKVOXDo1eIp7jYpANT0PwS8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=h3YaSQoL; arc=none smtp.client-ip=209.85.216.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="h3YaSQoL" Received: by mail-pj1-f42.google.com with SMTP id 98e67ed59e1d1-3598c008455so1777309a91.3 for ; Tue, 03 Mar 2026 21:36:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772602611; x=1773207411; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=rNcMxZGmsNob2hi70jSfSua0A2NVCjnuBqG6KRIu95c=; b=h3YaSQoLLhnhiYDWEJdepXjDHNhTxpaKciEnl6O54BufaVNAP+MfeBNGp9mJ2IK5SF AzPtDHD2R5F8hpjYseFHu/ilBGvktPrsOIp1MQub88eQN+vWnXMYpT5OAlLZGu49U3bW d008yrwHFqGvtwknzrJrbRtYdtMRNVqa5SkCNDZldxNmUp1dub8YCITTzfFOPcYjRE5V PEdxZ5cCW4D0dPfXmH3eP4pCwzA1l++3Ho7ofMTEeWNECvCY7qsifQvbrCZzDhTBqCX1 8nTRTic/f91HvMr07VCOsOcPUea7SmgBsx4aa0D/Es1fllK5YX53ctY4Wfat8otBmvAw SGWw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772602611; x=1773207411; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=rNcMxZGmsNob2hi70jSfSua0A2NVCjnuBqG6KRIu95c=; b=bYeu1f7QJ+5CDZNP8YRFl8xSECH5lch7ICCnwipijcKJOx/KAKwi2HoqFJgn9lWQHx +m21ogo9mhD1Zomo4RHgOfP1lWsRqE1/BhRff22PwqEVgennvuKX0IGJSWAD5OTF3l/B O+R0VGM3KGKJp5eguHPi75+FTm4Ci6Rd/WnzFSYbc2AGEFtOXV9KgRH8XeZHhpmJQnBZ kKWTb3tm9NT7u1rg56I2EQKZwxArhNPFnTj5dL968jHOBY/PfMOXKMnzo1cOS6uASlFF Xewfg2Af+P4D+Ki3nxTWTVC7KcVcmIgh17ZVOksAd9ZongGMqQ0a7ro0kH8IO3PhVEEQ ctXg== X-Forwarded-Encrypted: i=1; AJvYcCXSPWjs+CsVifEvo/lV1tBN5tdT4kVFUdb3XKQiysVo6ta/zACPbUL6xTbssGhe99dC6ucnNomKJPr8UMo=@vger.kernel.org X-Gm-Message-State: AOJu0YxVAlEgw2ynTNVS1pceARKfSod3EhRQxtWRLq6nsALvRf+xJlJu 1QkjePBbsl/rgDItfcm5dsZxRUHkGsDDXe2BaPFn9/Hf1AJuyXgOK0Lk X-Gm-Gg: ATEYQzxFg8yPdbo5ShTjnYoyTFd5L0ewlK/DUph1IeKRPAeXqcxMD6jAKSxDy7yVWBM xu7gjt/d84VjXi4hRxtdGE6jquhJwLVqLHlzFPgxExRmQuJn58lIwpm1Up9jUVeSiUfEb83SFMW WeEwjBVyOd0L2N5rTdYbIGjX7vDyO8QTCMhHPr0l+K1z4Qr2RK6MmB3gCQ5ODeAVrzVB9FBSrn0 AyXuiz9KWeBFwu1Hd0R+DvjjUpjg48At2Vu6M3yqeVv7+Ww5AG9181t4PnbsBi+X4Snwfb7NpeH n5FTLlYBWr0Da8SBlRdJ/f4wRFSD1k4eBbMEAtJc+N/HR7XWEy8mWueA744QDJ5nUQ+xGNgi9R5 4N9KxLAjMqpV86kW/0GoyY93TchP8c116GhdpElsB0pzOxEEcl+R2Kx7+TvIqIetNo1mPmfZbFH cXWEc//+vzrxtQ5PqZiGMCaucv+Bv3xYkcy6GcjhliCFk41LhKikGa X-Received: by 2002:a17:90b:3d83:b0:354:c629:efb2 with SMTP id 98e67ed59e1d1-359a6a563ddmr1057766a91.24.1772602611058; Tue, 03 Mar 2026 21:36:51 -0800 (PST) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3599c090bfdsm4020057a91.8.2026.03.03.21.36.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 03 Mar 2026 21:36:50 -0800 (PST) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: chuck.lever@oracle.com, hare@kernel.org, kernel-tls-handshake@lists.linux.dev, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-nvme@lists.infradead.org, linux-nfs@vger.kernel.org Cc: kbusch@kernel.org, axboe@kernel.dk, hch@lst.de, sagi@grimberg.me, kch@nvidia.com, hare@suse.de, alistair23@gmail.com, Alistair Francis Subject: [PATCH v7 4/5] nvme-tcp: Support KeyUpdate Date: Wed, 4 Mar 2026 15:34:59 +1000 Message-ID: <20260304053500.590630-5-alistair.francis@wdc.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260304053500.590630-1-alistair.francis@wdc.com> References: <20260304053500.590630-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alistair Francis If the nvme_tcp_try_send() or nvme_tcp_try_recv() functions return EKEYEXPIRED then the underlying TLS keys need to be updated. This occurs on an KeyUpdate event as described in RFC8446 https://datatracker.ietf.org/doc/html/rfc8446#section-4.6.3. If the NVMe Target (TLS server) initiates a KeyUpdate this patch will allow the NVMe layer to process the KeyUpdate request and forward the request to userspace. Userspace must then update the key to keep the connection alive. This patch allows us to handle the NVMe target sending a KeyUpdate request without aborting the connection. At this time we don't support initiating a KeyUpdate. Signed-off-by: Alistair Francis --- v7: - Use read_sock_cmsg instead of recvmsg() to handle KeyUpdate v6: - Don't use `struct nvme_tcp_hdr` to determine TLS_HANDSHAKE_KEYUPDATE, instead look at the cmsg fields. - Don't flush async_event_work v5: - Cleanup code flow - Check for MSG_CTRUNC in the msg_flags return from recvmsg and use that to determine if it's a control message v4: - Remove all support for initiating KeyUpdate - Don't call cancel_work() when updating keys v3: - Don't cancel existing handshake requests v2: - Don't change the state - Use a helper function for KeyUpdates - Continue sending in nvme_tcp_send_all() after a KeyUpdate - Remove command message using recvmsg drivers/nvme/host/tcp.c | 59 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c index 8b6172dd1c0f..ade11d2ac9ef 100644 --- a/drivers/nvme/host/tcp.c +++ b/drivers/nvme/host/tcp.c @@ -171,6 +171,7 @@ struct nvme_tcp_queue { bool tls_enabled; u32 rcv_crc; u32 snd_crc; + key_serial_t handshake_session_id; __le32 exp_ddgst; __le32 recv_ddgst; struct completion tls_complete; @@ -1361,6 +1362,59 @@ static int nvme_tcp_try_send(struct nvme_tcp_queue *= queue) return ret; } =20 +static void update_tls_keys(struct nvme_tcp_queue *queue) +{ + int qid =3D nvme_tcp_queue_id(queue); + int ret; + + dev_dbg(queue->ctrl->ctrl.device, + "updating key for queue %d\n", qid); + + ret =3D nvme_tcp_start_tls(&(queue->ctrl->ctrl), + queue, queue->ctrl->ctrl.tls_pskid, + HANDSHAKE_KEY_UPDATE_TYPE_RECEIVED); + + if (ret < 0) { + dev_err(queue->ctrl->ctrl.device, + "failed to update the keys %d\n", ret); + nvme_tcp_fail_request(queue->request); + } +} + +static int nvme_tcp_recv_cmsg(read_descriptor_t *desc, + struct sk_buff *skb, + unsigned int offset, size_t len, + u8 content_type) +{ + struct nvme_tcp_queue *queue =3D desc->arg.data; + struct socket *sock =3D queue->sock; + struct sock *sk =3D sock->sk; + + switch (content_type) { + case TLS_RECORD_TYPE_HANDSHAKE: + if (len =3D=3D 5) { + u8 header[5]; + + if (!skb_copy_bits(skb, offset, header, + sizeof(header))) { + if (header[0] =3D=3D TLS_HANDSHAKE_KEYUPDATE) { + dev_err(queue->ctrl->ctrl.device, "KeyUpdate message\n"); + release_sock(sk); + update_tls_keys(queue); + lock_sock(sk); + return 0; + } + } + } + + break; + default: + break; + } + + return -EAGAIN; +} + static int nvme_tcp_try_recv(struct nvme_tcp_queue *queue) { struct socket *sock =3D queue->sock; @@ -1372,7 +1426,8 @@ static int nvme_tcp_try_recv(struct nvme_tcp_queue *q= ueue) rd_desc.count =3D 1; lock_sock(sk); queue->nr_cqe =3D 0; - consumed =3D sock->ops->read_sock(sk, &rd_desc, nvme_tcp_recv_skb); + consumed =3D sock->ops->read_sock_cmsg(sk, &rd_desc, nvme_tcp_recv_skb, + nvme_tcp_recv_cmsg); release_sock(sk); return consumed =3D=3D -EAGAIN ? 0 : consumed; } @@ -1708,6 +1763,7 @@ static void nvme_tcp_tls_done(void *data, int status,= key_serial_t pskid, ctrl->ctrl.tls_pskid =3D key_serial(tls_key); key_put(tls_key); queue->tls_err =3D 0; + queue->handshake_session_id =3D handshake_session_id; } =20 out_complete: @@ -1737,6 +1793,7 @@ static int nvme_tcp_start_tls(struct nvme_ctrl *nctrl, keyring =3D key_serial(nctrl->opts->keyring); args.ta_keyring =3D keyring; args.ta_timeout_ms =3D tls_handshake_timeout * 1000; + args.ta_handshake_session_id =3D queue->handshake_session_id; queue->tls_err =3D -EOPNOTSUPP; init_completion(&queue->tls_complete); if (keyupdate =3D=3D HANDSHAKE_KEY_UPDATE_TYPE_UNSPEC) --=20 2.53.0 From nobody Fri Apr 3 04:34:53 2026 Received: from mail-pj1-f48.google.com (mail-pj1-f48.google.com [209.85.216.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D2384373BF5 for ; Wed, 4 Mar 2026 05:36:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772602619; cv=none; b=es0Op9qtTnD5z76qxp6UdvK6vwLCeHleLX5P8CImDGQnudaiaYV98iJ80r1Gl5Nadmt+UltnINrtYeyT8QizZU130Jm5ShekRkwpo+iARPrODu7Nv/LVwTnKhDSibbugwGWuUaawz93OfvORXqP+S0mpcxPqiG/J8rWLxeGnHa0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772602619; c=relaxed/simple; bh=VW3M4SYeAYi3H4yI1kcS4akTS2EsE5+h2XWp6OqZeYQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=OTy4Rto5IB8oXaB50Dei9XMyVSAHkmJ861uIRtIQ5q5/VWlGKLX9Kp2pSiJ6X07U6b8Whjgcd2JCYyq6xE7VyqmAzsb8CVhzuHj6bKXs1GC+uTbmrOBy4Wqb3C3JFotcjBxNN9rE6B2mNETlNgAZ3IWjZbX2loYwWewzkd22hMw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=dys2z8fA; arc=none smtp.client-ip=209.85.216.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="dys2z8fA" Received: by mail-pj1-f48.google.com with SMTP id 98e67ed59e1d1-359894e17b8so2138230a91.1 for ; Tue, 03 Mar 2026 21:36:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772602617; x=1773207417; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ZTos+cQe/JmZkdeA5lh2ZAdOl1XgvXlmqs7be9JqOns=; b=dys2z8fAvIJVmsE6OI0DxZzNEZuPfwGoAAfxC5I95aP6pAD8t3JBpuxw1OmviEECfz aQ1uMlZ7m9Nh1+hmnoKbzzt7LArJJVtECEt7iRhBzQ3rhToF73E2C/UA2XanPAcnSvkD oBoeD8eEjqoHRhoIYDFj46MEAAhGDEj2mwSfHvPinsyMjL5JGs36/mRj2N5lqmBMAy77 oSIsw/5fF+Cqp4ifbMj4eGrGc255/QvHeQuFXpAUgIxu6xkpmQqwhp2dZwWZOqw4jvwB Dqz7WtQ737e121XT8+nZP/525AhfCbFxAwBKQmLMWB3ClLKc9CHPq6Rz5tHQc7+vbubT GObg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772602617; x=1773207417; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=ZTos+cQe/JmZkdeA5lh2ZAdOl1XgvXlmqs7be9JqOns=; b=CVRmW33j11ZkWEVYRdrB5ByyNHsUKOGKhHJ19YiBgfmOxDaulkqlLJk8uj/MFxfnjv VNRCEK1UnRM7XSwMqoJLKT7z2EUDNQyQcTZaa4fr19pFwy2DxQLjB01DPv/Kv1mnuYT+ 85dyrunsMnKbuekZdhEvybj/5kfqW7ub3YUAHXQym0IjKeWP8on4NGC2do26JxHyC0aL YvG1z4lrdCN3Zynyi0ct7dNgP0h0ukPe/DE0rnK4bQz5bs1Bqxhni3zb7teTq8S53qzi F9uwm6Od2dfEz4CZCIjz00Zk7dzyE4zTzZuQOTyzrrkCR7vQH40rkp9HEDZjk5Q3m9cG 87zQ== X-Forwarded-Encrypted: i=1; AJvYcCXj6kWQC9uqyJqPpvPsI3zFXmUBfScjwmD/bFG3MKxiIf26FgtnCFA1bXqQ0BJLe3MssoT6MnOMHziQZXo=@vger.kernel.org X-Gm-Message-State: AOJu0YzhpjWbNRV4sbIJfai7hNyufqiy7WzZwMHkhXlzUiR/fIMk7kLN O4kFAVSN8dWjCY7ZOf+XggMVRpMYg/cLC5VI/Hrh3xdyOj3EGRzzESTO X-Gm-Gg: ATEYQzxlnNsXsQjEUEvyMnvnM4HC73BlCQBp/sM1fok7OxnvAU9SifE0n1l5ePcMCkD u8vDn7NMWEBKYgRy9LmURKESt05PZJhEOkdSYtHqlRi0J/mQkpMqo2/cFpIUGXc5obDlMR2OaTS zyWqVWgHF+ntuQiNPiO/sT8kbWYdKMef6ym8wDXUb4xuqdCpSBBTw0CQKlBzGlwWPC6UasQqsBs OE/jsPxJMwDsRMiMfjLVuvfVG2B+aJDCZ8zD1MS4jun5G6sdOFgXBGeB0Y81ykzNBtd2ioxB1eG 9b5l6ggkKKA33G+kD0k+40xjgveCSXvY5+omoWvxXmnGpGsKgCfgi9wUMgQIkYsUsa3TyrYPgGj PZvmwX+xWTaUeUXpYxx99S65K3D8/98cvG9pyiYkQGqa2x5CU57EEHk365DZHOMTMvRe22ctjp0 vxK/KjfvtjBqmeyOlmniHixsDuToSsvGnIL5+/4UW2Ng== X-Received: by 2002:a17:90b:3c46:b0:354:bf10:e6a5 with SMTP id 98e67ed59e1d1-359a69c2310mr979466a91.10.1772602617080; Tue, 03 Mar 2026 21:36:57 -0800 (PST) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3599c090bfdsm4020057a91.8.2026.03.03.21.36.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 03 Mar 2026 21:36:56 -0800 (PST) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: chuck.lever@oracle.com, hare@kernel.org, kernel-tls-handshake@lists.linux.dev, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-nvme@lists.infradead.org, linux-nfs@vger.kernel.org Cc: kbusch@kernel.org, axboe@kernel.dk, hch@lst.de, sagi@grimberg.me, kch@nvidia.com, hare@suse.de, alistair23@gmail.com, Alistair Francis Subject: [PATCH v7 5/5] nvmet-tcp: Support KeyUpdate Date: Wed, 4 Mar 2026 15:35:00 +1000 Message-ID: <20260304053500.590630-6-alistair.francis@wdc.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260304053500.590630-1-alistair.francis@wdc.com> References: <20260304053500.590630-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alistair Francis If the nvmet_tcp_try_recv() function return EKEYEXPIRED or if we receive a KeyUpdate handshake type then the underlying TLS keys need to be updated. If the NVMe Host (TLS client) initiates a KeyUpdate this patch will allow the NVMe layer to process the KeyUpdate request and forward the request to userspace. Userspace must then update the key to keep the connection alive. This patch allows us to handle the NVMe host sending a KeyUpdate request without aborting the connection. At this time we don't support initiating a KeyUpdate. Link: https://datatracker.ietf.org/doc/html/rfc8446#section-4.6.3 Signed-off-by: Alistair Francis Reviewed-by: Hannes Reinecke Reviewed-by: Christoph Hellwig --- v7: - No change v6: - Simplify the nvmet_tls_key_expired() check v5: - No change v4: - Restructure code to avoid #ifdefs and forward declarations - Use a helper function for checking -EKEYEXPIRED - Remove all support for initiating KeyUpdate - Use helper function for restoring callbacks v3: - Use a write lock for sk_user_data - Fix build with CONFIG_NVME_TARGET_TCP_TLS disabled - Remove unused variable v2: - Use a helper function for KeyUpdates - Ensure keep alive timer is stopped - Wait for TLS KeyUpdate to complete drivers/nvme/target/tcp.c | 200 ++++++++++++++++++++++++++------------ 1 file changed, 139 insertions(+), 61 deletions(-) diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index 7f1c651a52a4..d1937fe7a0d2 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -175,6 +175,7 @@ struct nvmet_tcp_queue { =20 /* TLS state */ key_serial_t tls_pskid; + key_serial_t handshake_session_id; struct delayed_work tls_handshake_tmo_work; =20 unsigned long poll_end; @@ -186,6 +187,8 @@ struct nvmet_tcp_queue { struct sockaddr_storage sockaddr_peer; struct work_struct release_work; =20 + struct completion tls_complete; + int idx; struct list_head queue_list; =20 @@ -214,6 +217,10 @@ static struct workqueue_struct *nvmet_tcp_wq; static const struct nvmet_fabrics_ops nvmet_tcp_ops; static void nvmet_tcp_free_cmd(struct nvmet_tcp_cmd *c); static void nvmet_tcp_free_cmd_buffers(struct nvmet_tcp_cmd *cmd); +#ifdef CONFIG_NVME_TARGET_TCP_TLS +static int nvmet_tcp_tls_handshake(struct nvmet_tcp_queue *queue, + enum handshake_key_update_type keyupdate); +#endif =20 static inline u16 nvmet_tcp_cmd_tag(struct nvmet_tcp_queue *queue, struct nvmet_tcp_cmd *cmd) @@ -848,6 +855,20 @@ static int nvmet_tcp_try_send_one(struct nvmet_tcp_que= ue *queue, return 1; } =20 +#ifdef CONFIG_NVME_TARGET_TCP_TLS +static bool nvmet_tls_key_expired(struct nvmet_tcp_queue *queue, int ret) +{ + return ret =3D=3D -EKEYEXPIRED && + queue->state !=3D NVMET_TCP_Q_DISCONNECTING && + queue->state !=3D NVMET_TCP_Q_TLS_HANDSHAKE; +} +#else +static bool nvmet_tls_key_expired(struct nvmet_tcp_queue *queue, int ret) +{ + return false; +} +#endif + static int nvmet_tcp_try_send(struct nvmet_tcp_queue *queue, int budget, int *sends) { @@ -1134,6 +1155,103 @@ static inline bool nvmet_tcp_pdu_valid(u8 type) return false; } =20 +static void nvmet_tcp_release_queue(struct kref *kref) +{ + struct nvmet_tcp_queue *queue =3D + container_of(kref, struct nvmet_tcp_queue, kref); + + WARN_ON(queue->state !=3D NVMET_TCP_Q_DISCONNECTING); + queue_work(nvmet_wq, &queue->release_work); +} + +static void nvmet_tcp_schedule_release_queue(struct nvmet_tcp_queue *queue) +{ + spin_lock_bh(&queue->state_lock); + if (queue->state =3D=3D NVMET_TCP_Q_TLS_HANDSHAKE) { + /* Socket closed during handshake */ + tls_handshake_cancel(queue->sock->sk); + } + if (queue->state !=3D NVMET_TCP_Q_DISCONNECTING) { + queue->state =3D NVMET_TCP_Q_DISCONNECTING; + kref_put(&queue->kref, nvmet_tcp_release_queue); + } + spin_unlock_bh(&queue->state_lock); +} + +static void nvmet_tcp_restore_socket_callbacks(struct nvmet_tcp_queue *que= ue) +{ + struct socket *sock =3D queue->sock; + + if (!queue->state_change) + return; + + write_lock_bh(&sock->sk->sk_callback_lock); + sock->sk->sk_data_ready =3D queue->data_ready; + sock->sk->sk_state_change =3D queue->state_change; + sock->sk->sk_write_space =3D queue->write_space; + sock->sk->sk_user_data =3D NULL; + write_unlock_bh(&sock->sk->sk_callback_lock); +} + +#ifdef CONFIG_NVME_TARGET_TCP_TLS +static void nvmet_tcp_tls_handshake_timeout(struct work_struct *w) +{ + struct nvmet_tcp_queue *queue =3D container_of(to_delayed_work(w), + struct nvmet_tcp_queue, tls_handshake_tmo_work); + + pr_warn("queue %d: TLS handshake timeout\n", queue->idx); + /* + * If tls_handshake_cancel() fails we've lost the race with + * nvmet_tcp_tls_handshake_done() */ + if (!tls_handshake_cancel(queue->sock->sk)) + return; + spin_lock_bh(&queue->state_lock); + if (WARN_ON(queue->state !=3D NVMET_TCP_Q_TLS_HANDSHAKE)) { + spin_unlock_bh(&queue->state_lock); + return; + } + queue->state =3D NVMET_TCP_Q_FAILED; + spin_unlock_bh(&queue->state_lock); + nvmet_tcp_schedule_release_queue(queue); + kref_put(&queue->kref, nvmet_tcp_release_queue); +} + +static int update_tls_keys(struct nvmet_tcp_queue *queue) +{ + int ret; + + cancel_work(&queue->io_work); + queue->state =3D NVMET_TCP_Q_TLS_HANDSHAKE; + + nvmet_tcp_restore_socket_callbacks(queue); + + INIT_DELAYED_WORK(&queue->tls_handshake_tmo_work, + nvmet_tcp_tls_handshake_timeout); + + ret =3D nvmet_tcp_tls_handshake(queue, HANDSHAKE_KEY_UPDATE_TYPE_RECEIVED= ); + + if (ret < 0) + return ret; + + ret =3D wait_for_completion_interruptible_timeout(&queue->tls_complete, + 10 * HZ); + + if (ret <=3D 0) { + tls_handshake_cancel(queue->sock->sk); + return ret; + } + + queue->state =3D NVMET_TCP_Q_LIVE; + + return 0; +} +#else +static int update_tls_keys(struct nvmet_tcp_queue *queue) +{ + return -EPFNOSUPPORT; +} +#endif + static int nvmet_tcp_tls_record_ok(struct nvmet_tcp_queue *queue, struct msghdr *msg, char *cbuf) { @@ -1159,6 +1277,9 @@ static int nvmet_tcp_tls_record_ok(struct nvmet_tcp_q= ueue *queue, ret =3D -EAGAIN; } break; + case TLS_RECORD_TYPE_HANDSHAKE: + ret =3D -EAGAIN; + break; default: /* discard this record type */ pr_err("queue %d: TLS record %d unhandled\n", @@ -1368,6 +1489,8 @@ static int nvmet_tcp_try_recv(struct nvmet_tcp_queue = *queue, for (i =3D 0; i < budget; i++) { ret =3D nvmet_tcp_try_recv_one(queue); if (unlikely(ret < 0)) { + if (nvmet_tls_key_expired(queue, ret)) + goto done; nvmet_tcp_socket_error(queue, ret); goto done; } else if (ret =3D=3D 0) { @@ -1379,29 +1502,6 @@ static int nvmet_tcp_try_recv(struct nvmet_tcp_queue= *queue, return ret; } =20 -static void nvmet_tcp_release_queue(struct kref *kref) -{ - struct nvmet_tcp_queue *queue =3D - container_of(kref, struct nvmet_tcp_queue, kref); - - WARN_ON(queue->state !=3D NVMET_TCP_Q_DISCONNECTING); - queue_work(nvmet_wq, &queue->release_work); -} - -static void nvmet_tcp_schedule_release_queue(struct nvmet_tcp_queue *queue) -{ - spin_lock_bh(&queue->state_lock); - if (queue->state =3D=3D NVMET_TCP_Q_TLS_HANDSHAKE) { - /* Socket closed during handshake */ - tls_handshake_cancel(queue->sock->sk); - } - if (queue->state !=3D NVMET_TCP_Q_DISCONNECTING) { - queue->state =3D NVMET_TCP_Q_DISCONNECTING; - kref_put(&queue->kref, nvmet_tcp_release_queue); - } - spin_unlock_bh(&queue->state_lock); -} - static inline void nvmet_tcp_arm_queue_deadline(struct nvmet_tcp_queue *qu= eue) { queue->poll_end =3D jiffies + usecs_to_jiffies(idle_poll_period_usecs); @@ -1432,8 +1532,12 @@ static void nvmet_tcp_io_work(struct work_struct *w) ret =3D nvmet_tcp_try_recv(queue, NVMET_TCP_RECV_BUDGET, &ops); if (ret > 0) pending =3D true; - else if (ret < 0) + else if (ret < 0) { + if (ret =3D=3D -EKEYEXPIRED) + break; + return; + } =20 ret =3D nvmet_tcp_try_send(queue, NVMET_TCP_SEND_BUDGET, &ops); if (ret > 0) @@ -1443,6 +1547,11 @@ static void nvmet_tcp_io_work(struct work_struct *w) =20 } while (pending && ops < NVMET_TCP_IO_WORK_BUDGET); =20 + if (ret =3D=3D -EKEYEXPIRED) { + update_tls_keys(queue); + pending =3D true; + } + /* * Requeue the worker if idle deadline period is in progress or any * ops activity was recorded during the do-while loop above. @@ -1545,21 +1654,6 @@ static void nvmet_tcp_free_cmds(struct nvmet_tcp_que= ue *queue) kvfree(cmds); } =20 -static void nvmet_tcp_restore_socket_callbacks(struct nvmet_tcp_queue *que= ue) -{ - struct socket *sock =3D queue->sock; - - if (!queue->state_change) - return; - - write_lock_bh(&sock->sk->sk_callback_lock); - sock->sk->sk_data_ready =3D queue->data_ready; - sock->sk->sk_state_change =3D queue->state_change; - sock->sk->sk_write_space =3D queue->write_space; - sock->sk->sk_user_data =3D NULL; - write_unlock_bh(&sock->sk->sk_callback_lock); -} - static void nvmet_tcp_uninit_data_in_cmds(struct nvmet_tcp_queue *queue) { struct nvmet_tcp_cmd *cmd =3D queue->cmds; @@ -1822,6 +1916,7 @@ static void nvmet_tcp_tls_handshake_done(void *data, = int status, } if (!status) { queue->tls_pskid =3D peerid; + queue->handshake_session_id =3D handshake_session_id; queue->state =3D NVMET_TCP_Q_CONNECTING; } else queue->state =3D NVMET_TCP_Q_FAILED; @@ -1837,28 +1932,7 @@ static void nvmet_tcp_tls_handshake_done(void *data,= int status, else nvmet_tcp_set_queue_sock(queue); kref_put(&queue->kref, nvmet_tcp_release_queue); -} - -static void nvmet_tcp_tls_handshake_timeout(struct work_struct *w) -{ - struct nvmet_tcp_queue *queue =3D container_of(to_delayed_work(w), - struct nvmet_tcp_queue, tls_handshake_tmo_work); - - pr_warn("queue %d: TLS handshake timeout\n", queue->idx); - /* - * If tls_handshake_cancel() fails we've lost the race with - * nvmet_tcp_tls_handshake_done() */ - if (!tls_handshake_cancel(queue->sock->sk)) - return; - spin_lock_bh(&queue->state_lock); - if (WARN_ON(queue->state !=3D NVMET_TCP_Q_TLS_HANDSHAKE)) { - spin_unlock_bh(&queue->state_lock); - return; - } - queue->state =3D NVMET_TCP_Q_FAILED; - spin_unlock_bh(&queue->state_lock); - nvmet_tcp_schedule_release_queue(queue); - kref_put(&queue->kref, nvmet_tcp_release_queue); + complete(&queue->tls_complete); } =20 static int nvmet_tcp_tls_handshake(struct nvmet_tcp_queue *queue, @@ -1880,11 +1954,15 @@ static int nvmet_tcp_tls_handshake(struct nvmet_tcp= _queue *queue, args.ta_data =3D queue; args.ta_keyring =3D key_serial(queue->port->nport->keyring); args.ta_timeout_ms =3D tls_handshake_timeout * 1000; + args.ta_handshake_session_id =3D queue->handshake_session_id; + + init_completion(&queue->tls_complete); =20 if (keyupdate =3D=3D HANDSHAKE_KEY_UPDATE_TYPE_UNSPEC) ret =3D tls_server_hello_psk(&args, GFP_KERNEL); else ret =3D tls_server_keyupdate_psk(&args, GFP_KERNEL, keyupdate); + if (ret) { kref_put(&queue->kref, nvmet_tcp_release_queue); pr_err("failed to start TLS, err=3D%d\n", ret); --=20 2.53.0