diff --git a/doc/guides/rel_notes/release_23_11.rst b/doc/guides/rel_notes/release_23_11.rst
index d5f8325a99..de755e54bf 100644
--- a/doc/guides/rel_notes/release_23_11.rst
+++ b/doc/guides/rel_notes/release_23_11.rst
@@ -38,6 +38,11 @@ DPDK Release 23.11
     which also added support for standard atomics
     (Ref: https://releases.llvm.org/3.6.0/tools/clang/docs/ReleaseNotes.html)
 
+  * **Added inline IPsec offload feature for NFP PMD.**
+
+  Added the inline IPsec offload feature based on the security framework for
+  NFP PMD.
+
 New Features
 ------------
 
diff --git a/drivers/net/nfp/meson.build b/drivers/net/nfp/meson.build
index e78bcb8b75..3912566134 100644
--- a/drivers/net/nfp/meson.build
+++ b/drivers/net/nfp/meson.build
@@ -33,8 +33,9 @@ sources = files(
         'nfp_ethdev_vf.c',
         'nfp_ethdev.c',
         'nfp_flow.c',
+        'nfp_ipsec.c',
         'nfp_logs.c',
         'nfp_mtr.c',
 )
 
-deps += ['hash']
+deps += ['hash', 'security']
diff --git a/drivers/net/nfp/nfp_common.c b/drivers/net/nfp/nfp_common.c
index 31dab3ae9b..5683afc40a 100644
--- a/drivers/net/nfp/nfp_common.c
+++ b/drivers/net/nfp/nfp_common.c
@@ -1200,6 +1200,7 @@ nfp_net_tx_desc_limits(struct nfp_net_hw *hw,
 int
 nfp_net_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
 {
+	uint32_t cap_extend;
 	uint16_t min_rx_desc;
 	uint16_t max_rx_desc;
 	uint16_t min_tx_desc;
@@ -1256,6 +1257,12 @@ nfp_net_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
 	if (hw->cap & NFP_NET_CFG_CTRL_GATHER)
 		dev_info->tx_offload_capa |= RTE_ETH_TX_OFFLOAD_MULTI_SEGS;
 
+	cap_extend = nn_cfg_readl(hw, NFP_NET_CFG_CAP_WORD1);
+	if ((cap_extend & NFP_NET_CFG_CTRL_IPSEC) != 0) {
+		dev_info->tx_offload_capa |= RTE_ETH_TX_OFFLOAD_SECURITY;
+		dev_info->rx_offload_capa |= RTE_ETH_RX_OFFLOAD_SECURITY;
+	}
+
 	dev_info->default_rxconf = (struct rte_eth_rxconf) {
 		.rx_thresh = {
 			.pthresh = DEFAULT_RX_PTHRESH,
diff --git a/drivers/net/nfp/nfp_common.h b/drivers/net/nfp/nfp_common.h
index 6487468d81..bc3a948231 100644
--- a/drivers/net/nfp/nfp_common.h
+++ b/drivers/net/nfp/nfp_common.h
@@ -172,6 +172,8 @@ struct nfp_net_hw {
 	uint8_t nfp_idx;
 
 	struct nfp_net_tlv_caps tlv_caps;
+
+	struct nfp_net_ipsec_data *ipsec_data;
 };
 
 struct nfp_net_adapter {
diff --git a/drivers/net/nfp/nfp_ctrl.h b/drivers/net/nfp/nfp_ctrl.h
index 0e194538ad..99890a33a1 100644
--- a/drivers/net/nfp/nfp_ctrl.h
+++ b/drivers/net/nfp/nfp_ctrl.h
@@ -238,6 +238,9 @@ struct nfp_net_fw_ver {
  */
 #define NFP_NET_CFG_CTRL_WORD1          0x0098
 #define NFP_NET_CFG_CTRL_PKT_TYPE         (0x1 << 0)
+#define NFP_NET_CFG_CTRL_IPSEC            (0x1 << 1) /**< IPsec offload */
+#define NFP_NET_CFG_CTRL_IPSEC_SM_LOOKUP  (0x1 << 3) /**< SA short match lookup */
+#define NFP_NET_CFG_CTRL_IPSEC_LM_LOOKUP  (0x1 << 4) /**< SA long match lookup */
 
 #define NFP_NET_CFG_CAP_WORD1           0x00a4
 
diff --git a/drivers/net/nfp/nfp_ethdev.c b/drivers/net/nfp/nfp_ethdev.c
index 7dc93f7c43..ebc5538291 100644
--- a/drivers/net/nfp/nfp_ethdev.c
+++ b/drivers/net/nfp/nfp_ethdev.c
@@ -18,6 +18,7 @@
 #include "nfpcore/nfp6000_pcie.h"
 
 #include "nfp_cpp_bridge.h"
+#include "nfp_ipsec.h"
 #include "nfp_logs.h"
 
 static int
@@ -140,6 +141,10 @@ nfp_net_start(struct rte_eth_dev *dev)
 	if ((cap_extend & NFP_NET_CFG_CTRL_PKT_TYPE) != 0)
 		ctrl_extend = NFP_NET_CFG_CTRL_PKT_TYPE;
 
+	if ((cap_extend & NFP_NET_CFG_CTRL_IPSEC) != 0)
+		ctrl_extend |= NFP_NET_CFG_CTRL_IPSEC_SM_LOOKUP
+				| NFP_NET_CFG_CTRL_IPSEC_LM_LOOKUP;
+
 	update = NFP_NET_CFG_UPDATE_GEN;
 	if (nfp_net_ext_reconfig(hw, ctrl_extend, update) < 0)
 		return -EIO;
@@ -278,6 +283,9 @@ nfp_net_close(struct rte_eth_dev *dev)
 
 	nfp_net_close_rx_queue(dev);
 
+	/* Clear ipsec */
+	nfp_ipsec_uninit(dev);
+
 	/* Cancel possible impending LSC work here before releasing the port*/
 	rte_eal_alarm_cancel(nfp_net_dev_interrupt_delayed_handler,
 			     (void *)dev);
@@ -555,6 +563,12 @@ nfp_net_init(struct rte_eth_dev *eth_dev)
 		return err;
 	}
 
+	err = nfp_ipsec_init(eth_dev);
+	if (err != 0) {
+		PMD_INIT_LOG(ERR, "Failed to init IPsec module");
+		return err;
+	}
+
 	nfp_net_ethdev_ops_mount(hw, eth_dev);
 
 	hw->eth_xstats_base = rte_malloc("rte_eth_xstat", sizeof(struct rte_eth_xstat) *
@@ -867,6 +881,7 @@ nfp_init_app_fw_nic(struct nfp_pf_dev *pf_dev,
 		if (app_fw_nic->ports[i] && app_fw_nic->ports[i]->eth_dev) {
 			struct rte_eth_dev *tmp_dev;
 			tmp_dev = app_fw_nic->ports[i]->eth_dev;
+			nfp_ipsec_uninit(tmp_dev);
 			rte_eth_dev_release_port(tmp_dev);
 			app_fw_nic->ports[i] = NULL;
 		}
diff --git a/drivers/net/nfp/nfp_ipsec.c b/drivers/net/nfp/nfp_ipsec.c
new file mode 100644
index 0000000000..f16ce97703
--- /dev/null
+++ b/drivers/net/nfp/nfp_ipsec.c
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Corigine Systems, Inc.
+ * All rights reserved.
+ */
+
+#include "nfp_ipsec.h"
+
+#include <rte_malloc.h>
+#include <rte_security_driver.h>
+
+#include <ethdev_driver.h>
+#include <ethdev_pci.h>
+
+#include "nfp_common.h"
+#include "nfp_ctrl.h"
+#include "nfp_logs.h"
+#include "nfp_rxtx.h"
+
+static const struct rte_security_ops nfp_security_ops;
+
+static int
+nfp_ipsec_ctx_create(struct rte_eth_dev *dev,
+		struct nfp_net_ipsec_data *data)
+{
+	struct rte_security_ctx *ctx;
+	static const struct rte_mbuf_dynfield pkt_md_dynfield = {
+		.name = "nfp_ipsec_crypto_pkt_metadata",
+		.size = sizeof(struct nfp_tx_ipsec_desc_msg),
+		.align = __alignof__(struct nfp_tx_ipsec_desc_msg),
+	};
+
+	ctx = rte_zmalloc("security_ctx",
+			sizeof(struct rte_security_ctx), 0);
+	if (ctx == NULL) {
+		PMD_INIT_LOG(ERR, "Failed to malloc security_ctx");
+		return -ENOMEM;
+	}
+
+	ctx->device = dev;
+	ctx->ops = &nfp_security_ops;
+	ctx->sess_cnt = 0;
+	dev->security_ctx = ctx;
+
+	data->pkt_dynfield_offset = rte_mbuf_dynfield_register(&pkt_md_dynfield);
+	if (data->pkt_dynfield_offset < 0) {
+		PMD_INIT_LOG(ERR, "Failed to register mbuf esn_dynfield");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+int
+nfp_ipsec_init(struct rte_eth_dev *dev)
+{
+	int ret;
+	uint32_t cap_extend;
+	struct nfp_net_hw *hw;
+	struct nfp_net_ipsec_data *data;
+
+	hw = NFP_NET_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+
+	cap_extend = nn_cfg_readl(hw, NFP_NET_CFG_CAP_WORD1);
+	if ((cap_extend & NFP_NET_CFG_CTRL_IPSEC) == 0) {
+		PMD_INIT_LOG(INFO, "Unsupported IPsec extend capability");
+		return 0;
+	}
+
+	data = rte_zmalloc("ipsec_data", sizeof(struct nfp_net_ipsec_data), 0);
+	if (data == NULL) {
+		PMD_INIT_LOG(ERR, "Failed to malloc ipsec_data");
+		return -ENOMEM;
+	}
+
+	data->pkt_dynfield_offset = -1;
+	data->sa_free_cnt = NFP_NET_IPSEC_MAX_SA_CNT;
+	hw->ipsec_data = data;
+
+	ret = nfp_ipsec_ctx_create(dev, data);
+	if (ret != 0) {
+		PMD_INIT_LOG(ERR, "Failed to create IPsec ctx");
+		goto ipsec_cleanup;
+	}
+
+	return 0;
+
+ipsec_cleanup:
+	nfp_ipsec_uninit(dev);
+
+	return ret;
+}
+
+static void
+nfp_ipsec_ctx_destroy(struct rte_eth_dev *dev)
+{
+	if (dev->security_ctx != NULL)
+		rte_free(dev->security_ctx);
+}
+
+void
+nfp_ipsec_uninit(struct rte_eth_dev *dev)
+{
+	uint16_t i;
+	uint32_t cap_extend;
+	struct nfp_net_hw *hw;
+	struct nfp_ipsec_session *priv_session;
+
+	hw = NFP_NET_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+
+	cap_extend = nn_cfg_readl(hw, NFP_NET_CFG_CAP_WORD1);
+	if ((cap_extend & NFP_NET_CFG_CTRL_IPSEC) == 0) {
+		PMD_INIT_LOG(INFO, "Unsupported IPsec extend capability");
+		return;
+	}
+
+	nfp_ipsec_ctx_destroy(dev);
+
+	if (hw->ipsec_data == NULL) {
+		PMD_INIT_LOG(INFO, "IPsec data is NULL!");
+		return;
+	}
+
+	for (i = 0; i < NFP_NET_IPSEC_MAX_SA_CNT; i++) {
+		priv_session = hw->ipsec_data->sa_entries[i];
+		if (priv_session != NULL)
+			memset(priv_session, 0, sizeof(struct nfp_ipsec_session));
+	}
+
+	rte_free(hw->ipsec_data);
+}
+
diff --git a/drivers/net/nfp/nfp_ipsec.h b/drivers/net/nfp/nfp_ipsec.h
new file mode 100644
index 0000000000..06792199c6
--- /dev/null
+++ b/drivers/net/nfp/nfp_ipsec.h
@@ -0,0 +1,174 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Corigine Systems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef __NFP_IPSEC_H__
+#define __NFP_IPSEC_H__
+
+#include <rte_security.h>
+
+#define NFP_NET_IPSEC_MAX_SA_CNT       (16 * 1024)
+
+struct ipsec_aesgcm {            /**< AES-GCM-ESP fields */
+	uint32_t salt;           /**< Initialized with SA */
+	uint32_t iv[2];          /**< Firmware use only */
+	uint32_t cntrl;
+	uint32_t zeros[4];       /**< Init to 0 with SA */
+	uint32_t len_auth[2];    /**< Firmware use only */
+	uint32_t len_cipher[2];
+	uint32_t spare[4];
+};
+
+struct sa_ctrl_word {
+	uint32_t hash   :4;      /**< From nfp_ipsec_hash_type */
+	uint32_t cimode :4;      /**< From nfp_ipsec_cipher_mode */
+	uint32_t cipher :4;      /**< From nfp_ipsec_cipher */
+	uint32_t mode   :2;      /**< From nfp_ipsec_mode */
+	uint32_t proto  :2;      /**< From nfp_ipsec_prot */
+	uint32_t spare  :1;      /**< Should be 0 */
+	uint32_t ena_arw:1;      /**< Anti-Replay Window */
+	uint32_t ext_seq:1;      /**< 64-bit Sequence Num */
+	uint32_t ext_arw:1;      /**< 64b Anti-Replay Window */
+	uint32_t spare1 :9;      /**< Must be set to 0 */
+	uint32_t encap_dsbl:1;   /**< Encap/decap disable */
+	uint32_t gen_seq:1;      /**< Firmware Generate Seq #'s */
+	uint32_t spare2 :1;      /**< Must be set to 0 */
+};
+
+union nfp_ip_addr {
+	struct in6_addr v6;
+	struct in_addr v4;
+};
+
+struct ipsec_add_sa {
+	uint32_t cipher_key[8];           /**< Cipher Key */
+	union {
+		uint32_t auth_key[16];    /**< Authentication Key */
+		struct ipsec_aesgcm aesgcm_fields;
+	};
+	struct sa_ctrl_word ctrl_word;
+	uint32_t spi;                     /**< SPI Value */
+	uint16_t pmtu_limit;              /**< PMTU Limit */
+	uint32_t spare      :1;
+	uint32_t frag_check :1;           /**< Stateful fragment checking flag */
+	uint32_t bypass_DSCP:1;           /**< Bypass DSCP Flag */
+	uint32_t df_ctrl    :2;           /**< DF Control bits */
+	uint32_t ipv6       :1;           /**< Outbound IPv6 addr format */
+	uint32_t udp_enable :1;           /**< Add/Remove UDP header for NAT */
+	uint32_t tfc_enable :1;           /**< Traffic Flw Confidentiality */
+	uint8_t spare1;
+	uint32_t soft_byte_cnt;           /**< Soft lifetime byte count */
+	uint32_t hard_byte_cnt;           /**< Hard lifetime byte count */
+	union nfp_ip_addr src_ip;         /**< Src IP addr */
+	union nfp_ip_addr dst_ip;         /**< Dst IP addr */
+	uint16_t natt_dst_port;           /**< NAT-T UDP Header dst port */
+	uint16_t natt_src_port;           /**< NAT-T UDP Header src port */
+	uint32_t soft_lifetime_limit;     /**< Soft lifetime time limit */
+	uint32_t hard_lifetime_limit;     /**< Hard lifetime time limit */
+	uint32_t sa_time_lo;              /**< SA creation time lower 32bits, Ucode fills this in */
+	uint32_t sa_time_hi;              /**< SA creation time high 32bits, Ucode fills this in */
+	uint16_t spare2;
+	uint16_t tfc_padding;             /**< Traffic Flow Confidential Pad */
+};
+
+struct ipsec_inv_sa {
+	uint32_t spare;
+};
+
+struct ipsec_discard_stats {
+	uint32_t discards_auth;                  /**< Auth failures */
+	uint32_t discards_unsupported;           /**< Unsupported crypto mode */
+	uint32_t discards_alignment;             /**< Alignment error */
+	uint32_t discards_hard_bytelimit;        /**< Hard byte Count limit */
+	uint32_t discards_seq_num_wrap;          /**< Sequ Number wrap */
+	uint32_t discards_pmtu_exceeded;         /**< PMTU Limit exceeded*/
+	uint32_t discards_arw_old_seq;           /**< Anti-Replay seq small */
+	uint32_t discards_arw_replay;            /**< Anti-Replay seq rcvd */
+	uint32_t discards_ctrl_word;             /**< Bad SA Control word */
+	uint32_t discards_ip_hdr_len;            /**< Hdr offset from too high */
+	uint32_t discards_eop_buf;               /**< No EOP buffer */
+	uint32_t ipv4_id_counter;                /**< IPv4 ID field counter */
+	uint32_t discards_isl_fail;              /**< Inbound SPD Lookup failure */
+	uint32_t discards_ext_unfound;           /**< Ext header end */
+	uint32_t discards_max_ext_hdrs;          /**< Max ext header */
+	uint32_t discards_non_ext_hdrs;          /**< Non-extension headers */
+	uint32_t discards_ext_hdr_too_big;       /**< Ext header chain */
+	uint32_t discards_hard_timelimit;        /**< Time Limit  */
+};
+
+struct ipsec_get_sa_stats {
+	uint32_t seq_lo;                         /**< Sequence Number (low 32bits) */
+	uint32_t seq_high;                       /**< Sequence Number (high 32bits)*/
+	uint32_t arw_counter_lo;                 /**< Anti-replay wndw cntr */
+	uint32_t arw_counter_high;               /**< Anti-replay wndw cntr */
+	uint32_t arw_bitmap_lo;                  /**< Anti-replay wndw bitmap */
+	uint32_t arw_bitmap_high;                /**< Anti-replay wndw bitmap */
+	uint32_t spare:1;
+	uint32_t soft_byte_exceeded :1;          /**< Soft lifetime byte cnt exceeded*/
+	uint32_t hard_byte_exceeded :1;          /**< Hard lifetime byte cnt exceeded*/
+	uint32_t soft_time_exceeded :1;          /**< Soft lifetime time limit exceeded*/
+	uint32_t hard_time_exceeded :1;          /**< Hard lifetime time limit exceeded*/
+	uint32_t spare1:27;
+	uint32_t lifetime_byte_count;
+	uint32_t pkt_count;
+	struct ipsec_discard_stats sa_discard_stats;
+};
+
+struct ipsec_get_seq {
+	uint32_t seq_nums;      /**< Sequence numbers to allocate */
+	uint32_t seq_num_low;   /**< Return start seq num 31:00 */
+	uint32_t seq_num_hi;    /**< Return start seq num 63:32 */
+};
+
+struct nfp_ipsec_msg {
+	union {
+		struct {
+			/** NFP IPsec SA cmd message codes */
+			uint16_t cmd;
+			/** NFP IPsec SA response message */
+			uint16_t rsp;
+			/** NFP IPsec SA index in driver SA table */
+			uint16_t sa_idx;
+			/** Reserved */
+			uint16_t spare;
+			union {
+				/** IPsec configure message for add SA */
+				struct ipsec_add_sa cfg_add_sa;
+				/** IPsec configure message for del SA */
+				struct ipsec_inv_sa cfg_inv_sa;
+				/** IPsec configure message for get SA stats */
+				struct ipsec_get_sa_stats cfg_get_stats;
+				/** IPsec configure message for get SA seq numbers */
+				struct ipsec_get_seq cfg_get_seq;
+			};
+		};
+		uint32_t raw[64];
+	};
+};
+
+struct nfp_ipsec_session {
+	/** Opaque user defined data */
+	void *user_data;
+	/** NFP sa_entries database parameter index */
+	uint32_t sa_index;
+	/** Point to physical ports ethernet device */
+	struct rte_eth_dev *dev;
+	/** SA related NPF configuration data */
+	struct ipsec_add_sa msg;
+	/** Security association configuration data */
+	struct rte_security_ipsec_xform ipsec;
+	/** Security session action type */
+	enum rte_security_session_action_type action;
+} __rte_cache_aligned;
+
+struct nfp_net_ipsec_data {
+	int pkt_dynfield_offset;
+	uint32_t sa_free_cnt;
+	struct nfp_ipsec_session *sa_entries[NFP_NET_IPSEC_MAX_SA_CNT];
+};
+
+int nfp_ipsec_init(struct rte_eth_dev *dev);
+void nfp_ipsec_uninit(struct rte_eth_dev *dev);
+
+#endif /* __NFP_IPSEC_H__ */
diff --git a/drivers/net/nfp/nfp_rxtx.h b/drivers/net/nfp/nfp_rxtx.h
index ddbf97e46c..4e8558074e 100644
--- a/drivers/net/nfp/nfp_rxtx.h
+++ b/drivers/net/nfp/nfp_rxtx.h
@@ -43,6 +43,18 @@ struct nfp_net_dp_buf {
 	struct rte_mbuf *mbuf;
 };
 
+struct nfp_tx_ipsec_desc_msg {
+	uint32_t sa_idx;        /**< SA index in driver table */
+	uint32_t enc;           /**< IPsec enable flag */
+	union {
+		uint64_t value;
+		struct {
+			uint32_t low;
+			uint32_t hi;
+		};
+	} esn;                  /**< Extended Sequence Number */
+};
+
 struct nfp_net_txq {
 	/** Backpointer to nfp_net structure */
 	struct nfp_net_hw *hw;
