diff --git a/drivers/net/sfc/meson.build b/drivers/net/sfc/meson.build
index c9d4264674..5adde68517 100644
--- a/drivers/net/sfc/meson.build
+++ b/drivers/net/sfc/meson.build
@@ -92,6 +92,7 @@ sources = files(
         'sfc_tbl_meta_cache.c',
         'sfc_mae.c',
         'sfc_mae_counter.c',
+        'sfc_mae_ct.c',
         'sfc_flow.c',
         'sfc_flow_rss.c',
         'sfc_flow_tunnel.c',
diff --git a/drivers/net/sfc/sfc_mae_ct.c b/drivers/net/sfc/sfc_mae_ct.c
new file mode 100644
index 0000000000..fd6819c8a5
--- /dev/null
+++ b/drivers/net/sfc/sfc_mae_ct.c
@@ -0,0 +1,201 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2023 Advanced Micro Devices, Inc.
+ */
+
+#include "sfc.h"
+#include "sfc_mae_ct.h"
+
+/* SF-123102-TC-1A § 10.6.3: Conntrack_Table key */
+static void
+sfc_mae_ct_key_to_mcdi_key(const sfc_mae_conntrack_key_t *key,
+			   const efx_table_field_descriptor_t *fields,
+			   unsigned int n_fields, uint32_t *mcdi_key,
+			   unsigned int key_size)
+{
+	unsigned int i;
+
+	for (i = 0; i < n_fields; i++) {
+		const efx_table_field_descriptor_t *desc = &fields[i];
+
+		if (desc->mask_type == EFX_TABLE_FIELD_MASK_NEVER)
+			continue;
+
+		switch (desc->field_id) {
+		case EFX_TABLE_FIELD_ID_IP_PROTO:
+			sfc_tbls_field_set_u8(mcdi_key, key_size, desc->lbn,
+					      desc->width, key->ip_proto);
+			break;
+		case EFX_TABLE_FIELD_ID_ETHER_TYPE:
+			sfc_tbls_field_set_u16(mcdi_key, key_size, desc->lbn,
+					       desc->width, key->ether_type_le);
+			break;
+		case EFX_TABLE_FIELD_ID_SRC_PORT:
+			sfc_tbls_field_set_u16(mcdi_key, key_size, desc->lbn,
+					       desc->width, key->src_port_le);
+			break;
+		case EFX_TABLE_FIELD_ID_DST_PORT:
+			sfc_tbls_field_set_u16(mcdi_key, key_size, desc->lbn,
+					       desc->width, key->dst_port_le);
+			break;
+		case EFX_TABLE_FIELD_ID_SRC_IP:
+			sfc_tbls_field_set_ip(mcdi_key, key_size, desc->lbn,
+					      desc->width,
+					      (const uint32_t *)key->src_addr_le);
+			break;
+		case EFX_TABLE_FIELD_ID_DST_IP:
+			sfc_tbls_field_set_ip(mcdi_key, key_size, desc->lbn,
+					      desc->width,
+					      (const uint32_t *)key->dst_addr_le);
+			break;
+
+		default:
+			break;
+		}
+	}
+}
+
+/* SF-123102-TC-1A § 10.6.4: Conntrack_Table response */
+static void
+sfc_mae_ct_response_to_mcdi_response(const sfc_mae_conntrack_response_t *response,
+				     const efx_table_field_descriptor_t *fields,
+				     unsigned int n_fields, uint32_t *mcdi_resp,
+				     unsigned int resp_size)
+{
+	unsigned int i;
+
+	for (i = 0; i < n_fields; i++) {
+		const efx_table_field_descriptor_t *desc = &fields[i];
+
+		if (desc->mask_type == EFX_TABLE_FIELD_MASK_NEVER)
+			continue;
+
+		/* Fields of responses are always reported with the EXACT type. */
+		SFC_ASSERT(desc->mask_type == EFX_TABLE_FIELD_MASK_EXACT);
+
+		switch (desc->field_id) {
+		case EFX_TABLE_FIELD_ID_CT_MARK:
+			sfc_tbls_field_set_u32(mcdi_resp, resp_size, desc->lbn,
+					       desc->width, response->ct_mark);
+			break;
+		case EFX_TABLE_FIELD_ID_COUNTER_ID:
+			sfc_tbls_field_set_u32(mcdi_resp, resp_size, desc->lbn,
+					       desc->width, response->counter_id);
+			break;
+		case EFX_TABLE_FIELD_ID_NAT_DIR:
+			sfc_tbls_field_set_u8(mcdi_resp, resp_size, desc->lbn,
+					      desc->width, response->nat.dir_is_dst);
+			break;
+		case EFX_TABLE_FIELD_ID_NAT_IP:
+			sfc_tbls_field_set_u32(mcdi_resp, resp_size, desc->lbn,
+					       desc->width, response->nat.ip_le);
+			break;
+		case EFX_TABLE_FIELD_ID_NAT_PORT:
+			sfc_tbls_field_set_u16(mcdi_resp, resp_size, desc->lbn,
+					       desc->width, response->nat.port_le);
+			break;
+
+		default:
+			break;
+		}
+	}
+}
+
+int
+sfc_mae_conntrack_insert(struct sfc_adapter *sa,
+			 const sfc_mae_conntrack_key_t *key,
+			 const sfc_mae_conntrack_response_t *response)
+{
+	const struct sfc_tbls *tables = &sa->hw_tables;
+	uint8_t data[EFX_TABLE_ENTRY_LENGTH_MAX] = {0};
+	const struct sfc_tbl_meta *meta = NULL;
+	unsigned int response_size;
+	uint32_t *response_data;
+	unsigned int data_size;
+	unsigned int key_size;
+	uint32_t *start_data;
+	uint16_t resp_width;
+	uint16_t key_width;
+	uint32_t *key_data;
+	uint32_t *end_data;
+	int rc = 0;
+
+	if (tables->status != SFC_TBLS_STATUS_SUPPORTED)
+		return -ENOTSUP;
+
+	if (!sfc_mae_conntrack_is_supported(sa))
+		return -ENOTSUP;
+
+	meta = sfc_mae_conntrack_meta_lookup(sa);
+	if (meta == NULL)
+		return -ENOENT;
+
+	key_width = meta->descriptor.key_width;
+	resp_width = meta->descriptor.resp_width;
+
+	start_data = (uint32_t *)data;
+	key_data = start_data;
+	response_data = sfc_tbls_next_req_fields(key_data, key_width);
+	end_data = sfc_tbls_next_req_fields(response_data, resp_width);
+
+	key_size = RTE_PTR_DIFF(response_data, key_data);
+	response_size = RTE_PTR_DIFF(end_data, response_data);
+	data_size = RTE_PTR_DIFF(end_data, start_data);
+	SFC_ASSERT(data_size <= sizeof(data));
+
+	sfc_mae_ct_key_to_mcdi_key(key, meta->keys,
+				   meta->descriptor.n_key_fields, key_data,
+				   key_size);
+	sfc_mae_ct_response_to_mcdi_response(response, meta->responses,
+					     meta->descriptor.n_resp_fields,
+					     response_data, response_size);
+
+	rc = sfc_tbls_bcam_entry_insert(sa->nic, EFX_TABLE_ID_CONNTRACK,
+					key_width, resp_width, data,
+					data_size);
+
+	return rc;
+}
+
+int
+sfc_mae_conntrack_delete(struct sfc_adapter *sa,
+			 const sfc_mae_conntrack_key_t *key)
+{
+	const struct sfc_tbls *tables = &sa->hw_tables;
+	uint8_t data[EFX_TABLE_ENTRY_LENGTH_MAX] = {0};
+	const struct sfc_tbl_meta *meta = NULL;
+	unsigned int data_size;
+	uint32_t *start_data;
+	uint16_t key_width;
+	uint32_t *key_data;
+	uint32_t *end_data;
+	int rc = 0;
+
+	if (tables->status != SFC_TBLS_STATUS_SUPPORTED)
+		return -ENOTSUP;
+
+	if (!sfc_mae_conntrack_is_supported(sa))
+		return -ENOTSUP;
+
+	meta = sfc_mae_conntrack_meta_lookup(sa);
+	if (meta == NULL)
+		return -ENOENT;
+
+	key_width = meta->descriptor.key_width;
+
+	start_data = (uint32_t *)data;
+	key_data = start_data;
+	end_data = sfc_tbls_next_req_fields(key_data, key_width);
+
+	data_size = RTE_PTR_DIFF(end_data, start_data);
+	SFC_ASSERT(data_size <= sizeof(data));
+
+	sfc_mae_ct_key_to_mcdi_key(key, meta->keys,
+				   meta->descriptor.n_key_fields,
+				   key_data, data_size);
+
+	rc = sfc_tbls_bcam_entry_delete(sa->nic, EFX_TABLE_ID_CONNTRACK,
+					key_width, data, data_size);
+
+	return rc;
+}
diff --git a/drivers/net/sfc/sfc_mae_ct.h b/drivers/net/sfc/sfc_mae_ct.h
new file mode 100644
index 0000000000..b5a3181cd3
--- /dev/null
+++ b/drivers/net/sfc/sfc_mae_ct.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2023 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _SFC_MAE_CONNTRACK_H
+#define _SFC_MAE_CONNTRACK_H
+
+#include <stdbool.h>
+
+#include <rte_ip.h>
+
+#include "efx.h"
+
+#include "sfc_tbls.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct sfc_mae_conntrack_key_s {
+	uint8_t		ip_proto;
+	uint16_t	ether_type_le;
+
+	uint16_t	src_port_le;
+	uint16_t	dst_port_le;
+
+	uint8_t		src_addr_le[RTE_SIZEOF_FIELD(struct rte_ipv6_hdr, src_addr)];
+	uint8_t		dst_addr_le[RTE_SIZEOF_FIELD(struct rte_ipv6_hdr, dst_addr)];
+} sfc_mae_conntrack_key_t;
+
+typedef struct sfc_mae_conntrack_nat_s {
+	uint32_t	ip_le;
+	uint16_t	port_le;
+	bool		dir_is_dst;
+} sfc_mae_conntrack_nat_t;
+
+typedef struct sfc_mae_conntrack_response_s {
+	uint32_t		ct_mark;
+	sfc_mae_conntrack_nat_t	nat;
+	uint32_t		counter_id;
+} sfc_mae_conntrack_response_t;
+
+struct sfc_adapter;
+
+static inline bool
+sfc_mae_conntrack_is_supported(struct sfc_adapter *sa)
+{
+	return sfc_tbls_id_is_supported(sa, EFX_TABLE_ID_CONNTRACK);
+}
+
+static inline const struct sfc_tbl_meta *
+sfc_mae_conntrack_meta_lookup(struct sfc_adapter *sa)
+{
+	return sfc_tbl_meta_lookup(sa, EFX_TABLE_ID_CONNTRACK);
+}
+
+int sfc_mae_conntrack_insert(struct sfc_adapter *sa,
+			     const sfc_mae_conntrack_key_t *key,
+			     const sfc_mae_conntrack_response_t *response);
+
+int sfc_mae_conntrack_delete(struct sfc_adapter *sa,
+			     const sfc_mae_conntrack_key_t *key);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _SFC_MAE_CONNTRACK_H */
