diff mbox series

[net-next,v6,7/8] net/funeth: add kTLS TX control part

Message ID 20220110015636.245666-8-dmichail@fungible.com (mailing list archive)
State Deferred
Delegated to: Netdev Maintainers
Headers show
Series new Fungible Ethernet driver | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers success CCed 4 of 4 maintainers
netdev/build_clang success Errors and warnings before: 0 this patch: 0
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/checkpatch warning CHECK: Alignment should match open parenthesis WARNING: ENOTSUPP is not a SUSV4 error code, prefer EOPNOTSUPP WARNING: From:/Signed-off-by: email address mismatch: 'From: Dimitris Michailidis <d.michailidis@fungible.com>' != 'Signed-off-by: Dimitris Michailidis <dmichail@fungible.com>' WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Dimitris Michailidis Jan. 10, 2022, 1:56 a.m. UTC
This provides the control pieces for kTLS Tx offload, implementinng the
offload operations.

Signed-off-by: Dimitris Michailidis <dmichail@fungible.com>
---
 .../ethernet/fungible/funeth/funeth_ktls.c    | 181 ++++++++++++++++++
 .../ethernet/fungible/funeth/funeth_ktls.h    |  33 ++++
 2 files changed, 214 insertions(+)
 create mode 100644 drivers/net/ethernet/fungible/funeth/funeth_ktls.c
 create mode 100644 drivers/net/ethernet/fungible/funeth/funeth_ktls.h

Comments

Jakub Kicinski Jan. 12, 2022, 10:32 p.m. UTC | #1
On Sun,  9 Jan 2022 17:56:35 -0800 Dimitris Michailidis wrote:
> +static inline int fun_ktls_init(struct net_device *netdev)
> +{
> +	return -ENOTSUPP;

ENOTSUPP is best avoided - EOPNOTSUPP is the proper error code which
can be returned to user space if needed. But you should make this
function void since the return value is ignored, anyway.
Jakub Kicinski Jan. 12, 2022, 10:35 p.m. UTC | #2
On Sun,  9 Jan 2022 17:56:35 -0800 Dimitris Michailidis wrote:
> This provides the control pieces for kTLS Tx offload, implementinng the
> offload operations.
> 
> Signed-off-by: Dimitris Michailidis <dmichail@fungible.com>
> ---
>  .../ethernet/fungible/funeth/funeth_ktls.c    | 181 ++++++++++++++++++
>  .../ethernet/fungible/funeth/funeth_ktls.h    |  33 ++++
>  2 files changed, 214 insertions(+)
>  create mode 100644 drivers/net/ethernet/fungible/funeth/funeth_ktls.c
>  create mode 100644 drivers/net/ethernet/fungible/funeth/funeth_ktls.h
> 
> diff --git a/drivers/net/ethernet/fungible/funeth/funeth_ktls.c b/drivers/net/ethernet/fungible/funeth/funeth_ktls.c
> new file mode 100644
> index 000000000000..bdcf3365bb16
> --- /dev/null
> +++ b/drivers/net/ethernet/fungible/funeth/funeth_ktls.c
> @@ -0,0 +1,181 @@
> +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
> +
> +#include "funeth.h"
> +#include "funeth_ktls.h"
> +
> +static int fun_admin_ktls_create(struct funeth_priv *fp, unsigned int id)
> +{
> +	struct fun_admin_ktls_create_req req = {
> +		.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_KTLS,
> +						     sizeof(req)),
> +		.subop = FUN_ADMIN_SUBOP_CREATE,
> +		.id = cpu_to_be32(id),
> +	};
> +
> +	return fun_submit_admin_sync_cmd(fp->fdev, &req.common, NULL, 0, 0);
> +}
> +
> +static int fun_ktls_add(struct net_device *netdev, struct sock *sk,
> +			enum tls_offload_ctx_dir direction,
> +			struct tls_crypto_info *crypto_info,
> +			u32 start_offload_tcp_sn)
> +{
> +	struct funeth_priv *fp = netdev_priv(netdev);
> +	struct fun_admin_ktls_modify_req req = {
> +		.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_KTLS,
> +						     sizeof(req)),
> +		.subop = FUN_ADMIN_SUBOP_MODIFY,
> +		.id = cpu_to_be32(fp->ktls_id),
> +		.tcp_seq = cpu_to_be32(start_offload_tcp_sn),
> +	};
> +	struct fun_admin_ktls_modify_rsp rsp;
> +	struct fun_ktls_tx_ctx *tx_ctx;
> +	int rc;
> +
> +	if (direction != TLS_OFFLOAD_CTX_DIR_TX)
> +		return -EOPNOTSUPP;
> +
> +	if (crypto_info->version == TLS_1_2_VERSION)
> +		req.version = FUN_KTLS_TLSV2;
> +	else if (crypto_info->version == TLS_1_3_VERSION)
> +		req.version = FUN_KTLS_TLSV3;


> +	else
> +		return -EOPNOTSUPP;
> +
> +	switch (crypto_info->cipher_type) {
> +	case TLS_CIPHER_AES_GCM_128: {
> +		struct tls12_crypto_info_aes_gcm_128 *c = (void *)crypto_info;
> +
> +		req.cipher = FUN_KTLS_CIPHER_AES_GCM_128;
> +		memcpy(req.key, c->key, sizeof(c->key));
> +		memcpy(req.iv, c->iv, sizeof(c->iv));
> +		memcpy(req.salt, c->salt, sizeof(c->salt));
> +		memcpy(req.record_seq, c->rec_seq, sizeof(c->rec_seq));
> +		break;
> +	}
> +
> +	case TLS_CIPHER_AES_GCM_256: {
> +		struct tls12_crypto_info_aes_gcm_256 *c = (void *)crypto_info;
> +
> +		req.cipher = FUN_KTLS_CIPHER_AES_GCM_256;
> +		memcpy(req.key, c->key, sizeof(c->key));
> +		memcpy(req.iv, c->iv, sizeof(c->iv));
> +		memcpy(req.salt, c->salt, sizeof(c->salt));
> +		memcpy(req.record_seq, c->rec_seq, sizeof(c->rec_seq));
> +		break;
> +	}
> +
> +	case TLS_CIPHER_CHACHA20_POLY1305: {
> +		struct tls12_crypto_info_chacha20_poly1305 *c;
> +
> +		c = (void *)crypto_info;
> +		req.cipher = FUN_KTLS_CIPHER_CHACHA20_POLY1305;
> +		memcpy(req.key, c->key, sizeof(c->key));
> +		memcpy(req.iv, c->iv, sizeof(c->iv));
> +		memcpy(req.salt, c->salt, sizeof(c->salt));
> +		memcpy(req.record_seq, c->rec_seq, sizeof(c->rec_seq));
> +		break;
> +	}
> +
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	rc = fun_submit_admin_sync_cmd(fp->fdev, &req.common, &rsp,
> +				       sizeof(rsp), 0);
> +	memzero_explicit(&req, sizeof(req));
> +	if (rc)
> +		return rc;
> +
> +	tx_ctx = tls_driver_ctx(sk, direction);
> +	tx_ctx->tlsid = rsp.tlsid;
> +	tx_ctx->next_seq = start_offload_tcp_sn;
> +	atomic64_inc(&fp->tx_tls_add);
> +	return 0;
> +}
> +
> +static void fun_ktls_del(struct net_device *netdev,
> +			 struct tls_context *tls_ctx,
> +			 enum tls_offload_ctx_dir direction)
> +{
> +	struct funeth_priv *fp = netdev_priv(netdev);
> +	struct fun_admin_ktls_modify_req req;
> +	struct fun_ktls_tx_ctx *tx_ctx;
> +
> +	if (direction != TLS_OFFLOAD_CTX_DIR_TX)
> +		return;
> +
> +	tx_ctx = __tls_driver_ctx(tls_ctx, direction);
> +
> +	req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_KTLS,
> +			offsetof(struct fun_admin_ktls_modify_req, tcp_seq));
> +	req.subop = FUN_ADMIN_SUBOP_MODIFY;
> +	req.flags = cpu_to_be16(FUN_KTLS_MODIFY_REMOVE);
> +	req.id = cpu_to_be32(fp->ktls_id);
> +	req.tlsid = tx_ctx->tlsid;
> +
> +	fun_submit_admin_sync_cmd(fp->fdev, &req.common, NULL, 0, 0);
> +	atomic64_inc(&fp->tx_tls_del);
> +}
> +
> +static int fun_ktls_resync(struct net_device *netdev, struct sock *sk, u32 seq,
> +			   u8 *rcd_sn, enum tls_offload_ctx_dir direction)
> +{
> +	struct funeth_priv *fp = netdev_priv(netdev);
> +	struct fun_admin_ktls_modify_req req;
> +	struct fun_ktls_tx_ctx *tx_ctx;
> +	int rc;
> +
> +	if (direction != TLS_OFFLOAD_CTX_DIR_TX)
> +		return -EOPNOTSUPP;
> +
> +	tx_ctx = tls_driver_ctx(sk, direction);
> +
> +	req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_KTLS,
> +			offsetof(struct fun_admin_ktls_modify_req, key));
> +	req.subop = FUN_ADMIN_SUBOP_MODIFY;
> +	req.flags = 0;
> +	req.id = cpu_to_be32(fp->ktls_id);
> +	req.tlsid = tx_ctx->tlsid;
> +	req.tcp_seq = cpu_to_be32(seq);
> +	req.version = 0;
> +	req.cipher = 0;
> +	memcpy(req.record_seq, rcd_sn, sizeof(req.record_seq));
> +
> +	atomic64_inc(&fp->tx_tls_resync);
> +	rc = fun_submit_admin_sync_cmd(fp->fdev, &req.common, NULL, 0, 0);
> +	if (!rc)
> +		tx_ctx->next_seq = seq;
> +	return rc;
> +}
> +
> +static const struct tlsdev_ops fun_ktls_ops = {
> +	.tls_dev_add = fun_ktls_add,
> +	.tls_dev_del = fun_ktls_del,
> +	.tls_dev_resync = fun_ktls_resync,
> +};
> +
> +int fun_ktls_init(struct net_device *netdev)
> +{
> +	struct funeth_priv *fp = netdev_priv(netdev);
> +	int rc;
> +
> +	rc = fun_admin_ktls_create(fp, netdev->dev_port);
> +	if (rc)
> +		return rc;
> +
> +	fp->ktls_id = netdev->dev_port;
> +	netdev->tlsdev_ops = &fun_ktls_ops;
> +	netdev->hw_features |= NETIF_F_HW_TLS_TX;
> +	netdev->features |= NETIF_F_HW_TLS_TX;
> +	return 0;
> +}
> +
> +void fun_ktls_cleanup(struct funeth_priv *fp)
> +{
> +	if (fp->ktls_id == FUN_HCI_ID_INVALID)
> +		return;
> +
> +	fun_res_destroy(fp->fdev, FUN_ADMIN_OP_KTLS, 0, fp->ktls_id);
> +	fp->ktls_id = FUN_HCI_ID_INVALID;
> +}
> diff --git a/drivers/net/ethernet/fungible/funeth/funeth_ktls.h b/drivers/net/ethernet/fungible/funeth/funeth_ktls.h
> new file mode 100644
> index 000000000000..1b433ac8cd7b
> --- /dev/null
> +++ b/drivers/net/ethernet/fungible/funeth/funeth_ktls.h
> @@ -0,0 +1,33 @@
> +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
> +
> +#ifndef _FUN_KTLS_H
> +#define _FUN_KTLS_H
> +
> +struct net_device;
> +struct funeth_priv;
> +
> +#ifdef CONFIG_TLS_DEVICE
> +#include <net/tls.h>
> +
> +struct fun_ktls_tx_ctx {
> +	__be64 tlsid;
> +	u32 next_seq;
> +};
> +
> +int fun_ktls_init(struct net_device *netdev);
> +void fun_ktls_cleanup(struct funeth_priv *fp);
> +
> +#else
> +#include <linux/errno.h>
> +
> +static inline int fun_ktls_init(struct net_device *netdev)
> +{
> +	return -ENOTSUPP;
> +}
> +
> +static inline void fun_ktls_cleanup(struct funeth_priv *fp)
> +{
> +}
> +#endif
> +
> +#endif /* _FUN_KTLS_H */
Jakub Kicinski Jan. 12, 2022, 10:40 p.m. UTC | #3
On Wed, 12 Jan 2022 14:35:32 -0800 Jakub Kicinski wrote:
> > +	if (crypto_info->version == TLS_1_2_VERSION)
> > +		req.version = FUN_KTLS_TLSV2;
> > +	else if (crypto_info->version == TLS_1_3_VERSION)
> > +		req.version = FUN_KTLS_TLSV3;  

I don't think offload of TLS 1.3 is supported by the kernel.

> > +	else
> > +		return -EOPNOTSUPP;
> > +
> > +	switch (crypto_info->cipher_type) {
> > +	case TLS_CIPHER_AES_GCM_128: {
> > +		struct tls12_crypto_info_aes_gcm_128 *c = (void *)crypto_info;
> > +
> > +		req.cipher = FUN_KTLS_CIPHER_AES_GCM_128;
> > +		memcpy(req.key, c->key, sizeof(c->key));
> > +		memcpy(req.iv, c->iv, sizeof(c->iv));
> > +		memcpy(req.salt, c->salt, sizeof(c->salt));
> > +		memcpy(req.record_seq, c->rec_seq, sizeof(c->rec_seq));
> > +		break;
> > +	}

Neither are all the algos below. Please remove dead code.

> > +	case TLS_CIPHER_AES_GCM_256: {
> > +		struct tls12_crypto_info_aes_gcm_256 *c = (void *)crypto_info;
> > +
> > +		req.cipher = FUN_KTLS_CIPHER_AES_GCM_256;
> > +		memcpy(req.key, c->key, sizeof(c->key));
> > +		memcpy(req.iv, c->iv, sizeof(c->iv));
> > +		memcpy(req.salt, c->salt, sizeof(c->salt));
> > +		memcpy(req.record_seq, c->rec_seq, sizeof(c->rec_seq));
> > +		break;
> > +	}
> > +
> > +	case TLS_CIPHER_CHACHA20_POLY1305: {
> > +		struct tls12_crypto_info_chacha20_poly1305 *c;
> > +
> > +		c = (void *)crypto_info;
> > +		req.cipher = FUN_KTLS_CIPHER_CHACHA20_POLY1305;
> > +		memcpy(req.key, c->key, sizeof(c->key));
> > +		memcpy(req.iv, c->iv, sizeof(c->iv));
> > +		memcpy(req.salt, c->salt, sizeof(c->salt));
> > +		memcpy(req.record_seq, c->rec_seq, sizeof(c->rec_seq));
> > +		break;
> > +	}
Dimitris Michailidis Feb. 17, 2022, 3:10 a.m. UTC | #4
On Wed, Jan 12, 2022 at 2:40 PM Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Wed, 12 Jan 2022 14:35:32 -0800 Jakub Kicinski wrote:
> > > +   if (crypto_info->version == TLS_1_2_VERSION)
> > > +           req.version = FUN_KTLS_TLSV2;
> > > +   else if (crypto_info->version == TLS_1_3_VERSION)
> > > +           req.version = FUN_KTLS_TLSV3;
>
> I don't think offload of TLS 1.3 is supported by the kernel.
>
> > > +   else
> > > +           return -EOPNOTSUPP;
> > > +
> > > +   switch (crypto_info->cipher_type) {
> > > +   case TLS_CIPHER_AES_GCM_128: {
> > > +           struct tls12_crypto_info_aes_gcm_128 *c = (void *)crypto_info;
> > > +
> > > +           req.cipher = FUN_KTLS_CIPHER_AES_GCM_128;
> > > +           memcpy(req.key, c->key, sizeof(c->key));
> > > +           memcpy(req.iv, c->iv, sizeof(c->iv));
> > > +           memcpy(req.salt, c->salt, sizeof(c->salt));
> > > +           memcpy(req.record_seq, c->rec_seq, sizeof(c->rec_seq));
> > > +           break;
> > > +   }
>
> Neither are all the algos below. Please remove dead code.

I've removed the TLS 1.3 pieces and the non-offloaded 1.2 algos.

>
> > > +   case TLS_CIPHER_AES_GCM_256: {
> > > +           struct tls12_crypto_info_aes_gcm_256 *c = (void *)crypto_info;
> > > +
> > > +           req.cipher = FUN_KTLS_CIPHER_AES_GCM_256;
> > > +           memcpy(req.key, c->key, sizeof(c->key));
> > > +           memcpy(req.iv, c->iv, sizeof(c->iv));
> > > +           memcpy(req.salt, c->salt, sizeof(c->salt));
> > > +           memcpy(req.record_seq, c->rec_seq, sizeof(c->rec_seq));
> > > +           break;
> > > +   }
> > > +
> > > +   case TLS_CIPHER_CHACHA20_POLY1305: {
> > > +           struct tls12_crypto_info_chacha20_poly1305 *c;
> > > +
> > > +           c = (void *)crypto_info;
> > > +           req.cipher = FUN_KTLS_CIPHER_CHACHA20_POLY1305;
> > > +           memcpy(req.key, c->key, sizeof(c->key));
> > > +           memcpy(req.iv, c->iv, sizeof(c->iv));
> > > +           memcpy(req.salt, c->salt, sizeof(c->salt));
> > > +           memcpy(req.record_seq, c->rec_seq, sizeof(c->rec_seq));
> > > +           break;
> > > +   }
diff mbox series

Patch

diff --git a/drivers/net/ethernet/fungible/funeth/funeth_ktls.c b/drivers/net/ethernet/fungible/funeth/funeth_ktls.c
new file mode 100644
index 000000000000..bdcf3365bb16
--- /dev/null
+++ b/drivers/net/ethernet/fungible/funeth/funeth_ktls.c
@@ -0,0 +1,181 @@ 
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+
+#include "funeth.h"
+#include "funeth_ktls.h"
+
+static int fun_admin_ktls_create(struct funeth_priv *fp, unsigned int id)
+{
+	struct fun_admin_ktls_create_req req = {
+		.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_KTLS,
+						     sizeof(req)),
+		.subop = FUN_ADMIN_SUBOP_CREATE,
+		.id = cpu_to_be32(id),
+	};
+
+	return fun_submit_admin_sync_cmd(fp->fdev, &req.common, NULL, 0, 0);
+}
+
+static int fun_ktls_add(struct net_device *netdev, struct sock *sk,
+			enum tls_offload_ctx_dir direction,
+			struct tls_crypto_info *crypto_info,
+			u32 start_offload_tcp_sn)
+{
+	struct funeth_priv *fp = netdev_priv(netdev);
+	struct fun_admin_ktls_modify_req req = {
+		.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_KTLS,
+						     sizeof(req)),
+		.subop = FUN_ADMIN_SUBOP_MODIFY,
+		.id = cpu_to_be32(fp->ktls_id),
+		.tcp_seq = cpu_to_be32(start_offload_tcp_sn),
+	};
+	struct fun_admin_ktls_modify_rsp rsp;
+	struct fun_ktls_tx_ctx *tx_ctx;
+	int rc;
+
+	if (direction != TLS_OFFLOAD_CTX_DIR_TX)
+		return -EOPNOTSUPP;
+
+	if (crypto_info->version == TLS_1_2_VERSION)
+		req.version = FUN_KTLS_TLSV2;
+	else if (crypto_info->version == TLS_1_3_VERSION)
+		req.version = FUN_KTLS_TLSV3;
+	else
+		return -EOPNOTSUPP;
+
+	switch (crypto_info->cipher_type) {
+	case TLS_CIPHER_AES_GCM_128: {
+		struct tls12_crypto_info_aes_gcm_128 *c = (void *)crypto_info;
+
+		req.cipher = FUN_KTLS_CIPHER_AES_GCM_128;
+		memcpy(req.key, c->key, sizeof(c->key));
+		memcpy(req.iv, c->iv, sizeof(c->iv));
+		memcpy(req.salt, c->salt, sizeof(c->salt));
+		memcpy(req.record_seq, c->rec_seq, sizeof(c->rec_seq));
+		break;
+	}
+
+	case TLS_CIPHER_AES_GCM_256: {
+		struct tls12_crypto_info_aes_gcm_256 *c = (void *)crypto_info;
+
+		req.cipher = FUN_KTLS_CIPHER_AES_GCM_256;
+		memcpy(req.key, c->key, sizeof(c->key));
+		memcpy(req.iv, c->iv, sizeof(c->iv));
+		memcpy(req.salt, c->salt, sizeof(c->salt));
+		memcpy(req.record_seq, c->rec_seq, sizeof(c->rec_seq));
+		break;
+	}
+
+	case TLS_CIPHER_CHACHA20_POLY1305: {
+		struct tls12_crypto_info_chacha20_poly1305 *c;
+
+		c = (void *)crypto_info;
+		req.cipher = FUN_KTLS_CIPHER_CHACHA20_POLY1305;
+		memcpy(req.key, c->key, sizeof(c->key));
+		memcpy(req.iv, c->iv, sizeof(c->iv));
+		memcpy(req.salt, c->salt, sizeof(c->salt));
+		memcpy(req.record_seq, c->rec_seq, sizeof(c->rec_seq));
+		break;
+	}
+
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	rc = fun_submit_admin_sync_cmd(fp->fdev, &req.common, &rsp,
+				       sizeof(rsp), 0);
+	memzero_explicit(&req, sizeof(req));
+	if (rc)
+		return rc;
+
+	tx_ctx = tls_driver_ctx(sk, direction);
+	tx_ctx->tlsid = rsp.tlsid;
+	tx_ctx->next_seq = start_offload_tcp_sn;
+	atomic64_inc(&fp->tx_tls_add);
+	return 0;
+}
+
+static void fun_ktls_del(struct net_device *netdev,
+			 struct tls_context *tls_ctx,
+			 enum tls_offload_ctx_dir direction)
+{
+	struct funeth_priv *fp = netdev_priv(netdev);
+	struct fun_admin_ktls_modify_req req;
+	struct fun_ktls_tx_ctx *tx_ctx;
+
+	if (direction != TLS_OFFLOAD_CTX_DIR_TX)
+		return;
+
+	tx_ctx = __tls_driver_ctx(tls_ctx, direction);
+
+	req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_KTLS,
+			offsetof(struct fun_admin_ktls_modify_req, tcp_seq));
+	req.subop = FUN_ADMIN_SUBOP_MODIFY;
+	req.flags = cpu_to_be16(FUN_KTLS_MODIFY_REMOVE);
+	req.id = cpu_to_be32(fp->ktls_id);
+	req.tlsid = tx_ctx->tlsid;
+
+	fun_submit_admin_sync_cmd(fp->fdev, &req.common, NULL, 0, 0);
+	atomic64_inc(&fp->tx_tls_del);
+}
+
+static int fun_ktls_resync(struct net_device *netdev, struct sock *sk, u32 seq,
+			   u8 *rcd_sn, enum tls_offload_ctx_dir direction)
+{
+	struct funeth_priv *fp = netdev_priv(netdev);
+	struct fun_admin_ktls_modify_req req;
+	struct fun_ktls_tx_ctx *tx_ctx;
+	int rc;
+
+	if (direction != TLS_OFFLOAD_CTX_DIR_TX)
+		return -EOPNOTSUPP;
+
+	tx_ctx = tls_driver_ctx(sk, direction);
+
+	req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_KTLS,
+			offsetof(struct fun_admin_ktls_modify_req, key));
+	req.subop = FUN_ADMIN_SUBOP_MODIFY;
+	req.flags = 0;
+	req.id = cpu_to_be32(fp->ktls_id);
+	req.tlsid = tx_ctx->tlsid;
+	req.tcp_seq = cpu_to_be32(seq);
+	req.version = 0;
+	req.cipher = 0;
+	memcpy(req.record_seq, rcd_sn, sizeof(req.record_seq));
+
+	atomic64_inc(&fp->tx_tls_resync);
+	rc = fun_submit_admin_sync_cmd(fp->fdev, &req.common, NULL, 0, 0);
+	if (!rc)
+		tx_ctx->next_seq = seq;
+	return rc;
+}
+
+static const struct tlsdev_ops fun_ktls_ops = {
+	.tls_dev_add = fun_ktls_add,
+	.tls_dev_del = fun_ktls_del,
+	.tls_dev_resync = fun_ktls_resync,
+};
+
+int fun_ktls_init(struct net_device *netdev)
+{
+	struct funeth_priv *fp = netdev_priv(netdev);
+	int rc;
+
+	rc = fun_admin_ktls_create(fp, netdev->dev_port);
+	if (rc)
+		return rc;
+
+	fp->ktls_id = netdev->dev_port;
+	netdev->tlsdev_ops = &fun_ktls_ops;
+	netdev->hw_features |= NETIF_F_HW_TLS_TX;
+	netdev->features |= NETIF_F_HW_TLS_TX;
+	return 0;
+}
+
+void fun_ktls_cleanup(struct funeth_priv *fp)
+{
+	if (fp->ktls_id == FUN_HCI_ID_INVALID)
+		return;
+
+	fun_res_destroy(fp->fdev, FUN_ADMIN_OP_KTLS, 0, fp->ktls_id);
+	fp->ktls_id = FUN_HCI_ID_INVALID;
+}
diff --git a/drivers/net/ethernet/fungible/funeth/funeth_ktls.h b/drivers/net/ethernet/fungible/funeth/funeth_ktls.h
new file mode 100644
index 000000000000..1b433ac8cd7b
--- /dev/null
+++ b/drivers/net/ethernet/fungible/funeth/funeth_ktls.h
@@ -0,0 +1,33 @@ 
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+
+#ifndef _FUN_KTLS_H
+#define _FUN_KTLS_H
+
+struct net_device;
+struct funeth_priv;
+
+#ifdef CONFIG_TLS_DEVICE
+#include <net/tls.h>
+
+struct fun_ktls_tx_ctx {
+	__be64 tlsid;
+	u32 next_seq;
+};
+
+int fun_ktls_init(struct net_device *netdev);
+void fun_ktls_cleanup(struct funeth_priv *fp);
+
+#else
+#include <linux/errno.h>
+
+static inline int fun_ktls_init(struct net_device *netdev)
+{
+	return -ENOTSUPP;
+}
+
+static inline void fun_ktls_cleanup(struct funeth_priv *fp)
+{
+}
+#endif
+
+#endif /* _FUN_KTLS_H */