diff --git a/drivers/common/nfp/nfp_common_ctrl.h b/drivers/common/nfp/nfp_common_ctrl.h
index d09fd2b892..cbde987736 100644
--- a/drivers/common/nfp/nfp_common_ctrl.h
+++ b/drivers/common/nfp/nfp_common_ctrl.h
@@ -223,6 +223,7 @@ struct nfp_net_fw_ver {
 #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_CTRL_MULTI_PF         (0x1 << 5)
+#define NFP_NET_CFG_CTRL_FLOW_STEER       (0x1 << 8) /**< Flow Steering */
 #define NFP_NET_CFG_CTRL_IN_ORDER         (0x1 << 11) /**< Virtio in-order flag */
 
 #define NFP_NET_CFG_CAP_WORD1           0x00a4
diff --git a/drivers/net/nfp/meson.build b/drivers/net/nfp/meson.build
index 8407073af8..0d0a0bd8f4 100644
--- a/drivers/net/nfp/meson.build
+++ b/drivers/net/nfp/meson.build
@@ -36,6 +36,7 @@ sources = files(
         'nfp_mtr.c',
         'nfp_net_common.c',
         'nfp_net_ctrl.c',
+        'nfp_net_flow.c',
         'nfp_rxtx.c',
 )
 
diff --git a/drivers/net/nfp/nfp_ethdev.c b/drivers/net/nfp/nfp_ethdev.c
index 537b4fe792..d0a1950ff3 100644
--- a/drivers/net/nfp/nfp_ethdev.c
+++ b/drivers/net/nfp/nfp_ethdev.c
@@ -23,6 +23,7 @@
 #include "nfp_cpp_bridge.h"
 #include "nfp_ipsec.h"
 #include "nfp_logs.h"
+#include "nfp_net_flow.h"
 
 #define NFP_PF_DRIVER_NAME net_nfp_pf
 
@@ -151,6 +152,10 @@ nfp_net_start(struct rte_eth_dev *dev)
 		ctrl_extend |= NFP_NET_CFG_CTRL_IPSEC_SM_LOOKUP
 				| NFP_NET_CFG_CTRL_IPSEC_LM_LOOKUP;
 
+	/* Enable flow steer by extend ctrl word1. */
+	if ((cap_extend & NFP_NET_CFG_CTRL_FLOW_STEER) != 0)
+		ctrl_extend |= NFP_NET_CFG_CTRL_FLOW_STEER;
+
 	update = NFP_NET_CFG_UPDATE_GEN;
 	if (nfp_ext_reconfig(hw, ctrl_extend, update) != 0)
 		return -EIO;
@@ -323,6 +328,10 @@ nfp_net_uninit(struct rte_eth_dev *eth_dev)
 	struct nfp_net_hw *net_hw;
 
 	net_hw = eth_dev->data->dev_private;
+
+	if ((net_hw->super.cap_ext & NFP_NET_CFG_CTRL_FLOW_STEER) != 0)
+		nfp_net_flow_priv_uninit(net_hw->pf_dev, net_hw->idx);
+
 	rte_free(net_hw->eth_xstats_base);
 	nfp_ipsec_uninit(eth_dev);
 	if (net_hw->mac_stats_area != NULL)
@@ -762,6 +771,14 @@ nfp_net_init(struct rte_eth_dev *eth_dev)
 	/* Recording current stats counters values */
 	nfp_net_stats_reset(eth_dev);
 
+	if ((hw->cap_ext & NFP_NET_CFG_CTRL_FLOW_STEER) != 0) {
+		err = nfp_net_flow_priv_init(pf_dev, port);
+		if (err != 0) {
+			PMD_INIT_LOG(ERR, "Init net flow priv failed");
+			goto xstats_free;
+		}
+	}
+
 	return 0;
 
 xstats_free:
@@ -1195,13 +1212,11 @@ nfp_init_app_fw_nic(struct nfp_pf_dev *pf_dev,
 port_cleanup:
 	for (i = 0; i < app_fw_nic->total_phyports; i++) {
 		id = nfp_function_id_get(pf_dev, i);
+		hw = app_fw_nic->ports[id];
 
-		if (app_fw_nic->ports[id] != NULL &&
-				app_fw_nic->ports[id]->eth_dev != NULL) {
-			struct rte_eth_dev *tmp_dev;
-			tmp_dev = app_fw_nic->ports[id]->eth_dev;
-			nfp_net_uninit(tmp_dev);
-			rte_eth_dev_release_port(tmp_dev);
+		if (hw != NULL && hw->eth_dev != NULL) {
+			nfp_net_uninit(hw->eth_dev);
+			rte_eth_dev_release_port(hw->eth_dev);
 		}
 	}
 	nfp_cpp_area_release_free(pf_dev->ctrl_area);
diff --git a/drivers/net/nfp/nfp_net_common.h b/drivers/net/nfp/nfp_net_common.h
index ded491cbdc..eb668a1505 100644
--- a/drivers/net/nfp/nfp_net_common.h
+++ b/drivers/net/nfp/nfp_net_common.h
@@ -108,6 +108,14 @@ struct nfp_pf_dev {
 	struct nfp_multi_pf multi_pf;
 };
 
+#define NFP_NET_FLOW_LIMIT    1024
+
+struct nfp_net_priv {
+	uint32_t hash_seed; /**< Hash seed for hash tables in this structure. */
+	struct rte_hash *flow_table; /**< Hash table to store flow rules. */
+	uint16_t flow_count; /**< Flow count in hash table */
+};
+
 struct nfp_app_fw_nic {
 	/** Backpointer to the PF device */
 	struct nfp_pf_dev *pf_dev;
@@ -177,6 +185,9 @@ struct nfp_net_hw {
 	struct nfp_net_tlv_caps tlv_caps;
 
 	struct nfp_net_ipsec_data *ipsec_data;
+
+	/** Used for rte_flow of CoreNIC firmware */
+	struct nfp_net_priv *priv;
 };
 
 static inline uint32_t
diff --git a/drivers/net/nfp/nfp_net_flow.c b/drivers/net/nfp/nfp_net_flow.c
new file mode 100644
index 0000000000..25da9ed8ac
--- /dev/null
+++ b/drivers/net/nfp/nfp_net_flow.c
@@ -0,0 +1,166 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Corigine, Inc.
+ * All rights reserved.
+ */
+
+#include "nfp_net_flow.h"
+
+#include <rte_flow_driver.h>
+#include <rte_hash.h>
+#include <rte_jhash.h>
+#include <rte_malloc.h>
+
+#include "nfp_logs.h"
+
+__rte_unused static int
+nfp_net_flow_table_add(struct nfp_net_priv *priv,
+		struct rte_flow *nfp_flow)
+{
+	int ret;
+
+	ret = rte_hash_add_key_data(priv->flow_table, &nfp_flow->hash_key, nfp_flow);
+	if (ret != 0) {
+		PMD_DRV_LOG(ERR, "Add to flow table failed.");
+		return ret;
+	}
+
+	return 0;
+}
+
+__rte_unused static int
+nfp_net_flow_table_delete(struct nfp_net_priv *priv,
+		struct rte_flow *nfp_flow)
+{
+	int ret;
+
+	ret = rte_hash_del_key(priv->flow_table, &nfp_flow->hash_key);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "Delete from flow table failed.");
+		return ret;
+	}
+
+	return 0;
+}
+
+__rte_unused static struct rte_flow *
+nfp_net_flow_table_search(struct nfp_net_priv *priv,
+		struct rte_flow *nfp_flow)
+{
+	int index;
+	struct rte_flow *flow_find;
+
+	index = rte_hash_lookup_data(priv->flow_table, &nfp_flow->hash_key,
+			(void **)&flow_find);
+	if (index < 0) {
+		PMD_DRV_LOG(DEBUG, "Data NOT found in the flow table.");
+		return NULL;
+	}
+
+	return flow_find;
+}
+
+__rte_unused static struct rte_flow *
+nfp_net_flow_alloc(uint32_t match_len,
+		uint32_t action_len,
+		uint32_t port_id)
+{
+	char *data;
+	struct rte_flow *nfp_flow;
+	struct nfp_net_flow_payload *payload;
+
+	nfp_flow = rte_zmalloc("nfp_flow", sizeof(struct rte_flow), 0);
+	if (nfp_flow == NULL)
+		return NULL;
+
+	data = rte_zmalloc("nfp_flow_payload", match_len + action_len, 0);
+	if (data == NULL)
+		goto free_flow;
+
+	nfp_flow->port_id      = port_id;
+	payload                = &nfp_flow->payload;
+	payload->match_len     = match_len;
+	payload->action_len    = action_len;
+	payload->match_data    = data;
+	payload->action_data   = data + match_len;
+
+	return nfp_flow;
+
+free_flow:
+	rte_free(nfp_flow);
+
+	return NULL;
+}
+
+__rte_unused static void
+nfp_net_flow_free(struct rte_flow *nfp_flow)
+{
+	rte_free(nfp_flow->payload.match_data);
+	rte_free(nfp_flow);
+}
+
+int
+nfp_net_flow_priv_init(struct nfp_pf_dev *pf_dev,
+		uint16_t port)
+{
+	int ret = 0;
+	struct nfp_net_priv *priv;
+	char flow_name[RTE_HASH_NAMESIZE];
+	struct nfp_app_fw_nic *app_fw_nic;
+	const char *pci_name = strchr(pf_dev->pci_dev->name, ':') + 1;
+
+	snprintf(flow_name, sizeof(flow_name), "%s_fl_%u", pci_name, port);
+
+	struct rte_hash_parameters flow_hash_params = {
+		.name       = flow_name,
+		.entries    = NFP_NET_FLOW_LIMIT,
+		.hash_func  = rte_jhash,
+		.socket_id  = rte_socket_id(),
+		.key_len    = sizeof(uint32_t),
+		.extra_flag = RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY,
+	};
+
+	priv = rte_zmalloc("nfp_app_nic_priv", sizeof(struct nfp_net_priv), 0);
+	if (priv == NULL) {
+		PMD_INIT_LOG(ERR, "NFP app nic priv creation failed");
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	app_fw_nic = NFP_PRIV_TO_APP_FW_NIC(pf_dev->app_fw_priv);
+	app_fw_nic->ports[port]->priv = priv;
+	priv->hash_seed = (uint32_t)rte_rand();
+
+	/* Flow table */
+	flow_hash_params.hash_func_init_val = priv->hash_seed;
+	priv->flow_table = rte_hash_create(&flow_hash_params);
+	if (priv->flow_table == NULL) {
+		PMD_INIT_LOG(ERR, "flow hash table creation failed");
+		ret = -ENOMEM;
+		goto free_priv;
+	}
+
+	return 0;
+
+free_priv:
+	rte_free(priv);
+exit:
+	return ret;
+}
+
+void
+nfp_net_flow_priv_uninit(struct nfp_pf_dev *pf_dev,
+		uint16_t port)
+{
+	struct nfp_net_priv *priv;
+	struct nfp_app_fw_nic *app_fw_nic;
+
+	if (pf_dev == NULL)
+		return;
+
+	app_fw_nic = NFP_PRIV_TO_APP_FW_NIC(pf_dev->app_fw_priv);
+	priv = app_fw_nic->ports[port]->priv;
+	if (priv != NULL)
+		rte_hash_free(priv->flow_table);
+
+	rte_free(priv);
+}
diff --git a/drivers/net/nfp/nfp_net_flow.h b/drivers/net/nfp/nfp_net_flow.h
new file mode 100644
index 0000000000..5ec80ba3b6
--- /dev/null
+++ b/drivers/net/nfp/nfp_net_flow.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2023 Corigine, Inc.
+ * All rights reserved.
+ */
+
+#ifndef __NFP_NET_FLOW_H__
+#define __NFP_NET_FLOW_H__
+
+#include "nfp_net_common.h"
+
+struct nfp_net_flow_payload {
+	uint16_t cmsg_type;
+	uint8_t match_len;
+	uint8_t action_len;
+	char *match_data;
+	char *action_data;
+};
+
+struct rte_flow {
+	struct nfp_net_flow_payload payload;
+	uint32_t hash_key;
+	uint32_t port_id;
+};
+
+int nfp_net_flow_priv_init(struct nfp_pf_dev *pf_dev, uint16_t port);
+void nfp_net_flow_priv_uninit(struct nfp_pf_dev *pf_dev, uint16_t port);
+
+#endif /* __NFP_NET_FLOW_H__ */
