diff --git a/drivers/net/mlx5/mlx5_flow.h b/drivers/net/mlx5/mlx5_flow.h
index 4ad9910..d49cb53 100644
--- a/drivers/net/mlx5/mlx5_flow.h
+++ b/drivers/net/mlx5/mlx5_flow.h
@@ -48,6 +48,25 @@ enum {
 	MLX5_INDIRECT_ACTION_TYPE_CT,
 };
 
+/* Now, the maximal ports will be supported is 256, action number is 4M. */
+#define MLX5_INDIRECT_ACT_CT_MAX_PORT 0x100
+
+#define MLX5_INDIRECT_ACT_CT_OWNER_SHIFT 22
+#define MLX5_INDIRECT_ACT_CT_OWNER_MASK (MLX5_INDIRECT_ACT_CT_MAX_PORT - 1)
+
+/* 30-31: type, 22-29: owner port, 0-21: index. */
+#define MLX5_INDIRECT_ACT_CT_GEN_IDX(owner, index) \
+	((MLX5_INDIRECT_ACTION_TYPE_CT << MLX5_INDIRECT_ACTION_TYPE_OFFSET) | \
+	 (((owner) & MLX5_INDIRECT_ACT_CT_OWNER_MASK) << \
+	  MLX5_INDIRECT_ACT_CT_OWNER_SHIFT) | (index))
+
+#define MLX5_INDIRECT_ACT_CT_GET_OWNER(index) \
+	(((index) >> MLX5_INDIRECT_ACT_CT_OWNER_SHIFT) & \
+	 MLX5_INDIRECT_ACT_CT_OWNER_MASK)
+
+#define MLX5_INDIRECT_ACT_CT_GET_IDX(index) \
+	((index) & ((1 << MLX5_INDIRECT_ACT_CT_OWNER_SHIFT) - 1))
+
 /* Matches on selected register. */
 struct mlx5_rte_flow_item_tag {
 	enum modify_reg id;
@@ -1302,7 +1321,7 @@ mlx5_aso_meter_by_idx(struct mlx5_priv *priv, uint32_t idx)
 }
 
 /*
- * Get ASO CT action by index.
+ * Get ASO CT action by device and index.
  *
  * @param[in] dev
  *   Pointer to the Ethernet device structure.
@@ -1313,7 +1332,7 @@ mlx5_aso_meter_by_idx(struct mlx5_priv *priv, uint32_t idx)
  *   The specified ASO CT action pointer.
  */
 static inline struct mlx5_aso_ct_action *
-flow_aso_ct_get_by_idx(struct rte_eth_dev *dev, uint32_t idx)
+flow_aso_ct_get_by_dev_idx(struct rte_eth_dev *dev, uint32_t idx)
 {
 	struct mlx5_priv *priv = dev->data->dev_private;
 	struct mlx5_aso_ct_pools_mng *mng = priv->sh->ct_mng;
@@ -1326,6 +1345,40 @@ flow_aso_ct_get_by_idx(struct rte_eth_dev *dev, uint32_t idx)
 	return &pool->actions[idx % MLX5_ASO_CT_ACTIONS_PER_POOL];
 }
 
+/*
+ * Get ASO CT action by owner & index.
+ *
+ * @param[in] dev
+ *   Pointer to the Ethernet device structure.
+ * @param[in] idx
+ *   Index to the ASO CT action and owner port combination.
+ *
+ * @return
+ *   The specified ASO CT action pointer.
+ */
+static inline struct mlx5_aso_ct_action *
+flow_aso_ct_get_by_idx(struct rte_eth_dev *dev, uint32_t own_idx)
+{
+	struct mlx5_priv *priv = dev->data->dev_private;
+	struct mlx5_aso_ct_action *ct;
+	uint16_t owner = (uint16_t)MLX5_INDIRECT_ACT_CT_GET_OWNER(own_idx);
+	uint32_t idx = MLX5_INDIRECT_ACT_CT_GET_IDX(own_idx);
+
+	if (owner == PORT_ID(priv)) {
+		ct = flow_aso_ct_get_by_dev_idx(dev, idx);
+	} else {
+		struct rte_eth_dev *owndev = &rte_eth_devices[owner];
+
+		MLX5_ASSERT(owner < RTE_MAX_ETHPORTS);
+		if (dev->data->dev_started != 1)
+			return NULL;
+		ct = flow_aso_ct_get_by_dev_idx(owndev, idx);
+		if (ct->peer != PORT_ID(priv))
+			return NULL;
+	}
+	return ct;
+}
+
 int mlx5_flow_group_to_table(struct rte_eth_dev *dev,
 			     const struct mlx5_flow_tunnel *tunnel,
 			     uint32_t group, uint32_t *table,
diff --git a/drivers/net/mlx5/mlx5_flow_dv.c b/drivers/net/mlx5/mlx5_flow_dv.c
index 3e85d5e..1cb6f73 100644
--- a/drivers/net/mlx5/mlx5_flow_dv.c
+++ b/drivers/net/mlx5/mlx5_flow_dv.c
@@ -11289,7 +11289,7 @@ flow_dv_translate_create_aso_age(struct rte_eth_dev *dev,
 }
 
 /*
- * Release an ASO CT action.
+ * Release an ASO CT action by its own device.
  *
  * @param[in] dev
  *   Pointer to the Ethernet device structure.
@@ -11300,11 +11300,11 @@ flow_dv_translate_create_aso_age(struct rte_eth_dev *dev,
  *   0 when CT action was removed, otherwise the number of references.
  */
 static inline int
-flow_dv_aso_ct_release(struct rte_eth_dev *dev, uint32_t idx)
+flow_dv_aso_ct_dev_release(struct rte_eth_dev *dev, uint32_t idx)
 {
 	struct mlx5_priv *priv = dev->data->dev_private;
 	struct mlx5_aso_ct_pools_mng *mng = priv->sh->ct_mng;
-	struct mlx5_aso_ct_action *ct = flow_aso_ct_get_by_idx(dev, idx);
+	struct mlx5_aso_ct_action *ct = flow_aso_ct_get_by_dev_idx(dev, idx);
 	uint32_t ret = __atomic_sub_fetch(&ct->refcnt, 1, __ATOMIC_RELAXED);
 
 	if (!ret) {
@@ -11329,7 +11329,21 @@ flow_dv_aso_ct_release(struct rte_eth_dev *dev, uint32_t idx)
 		LIST_INSERT_HEAD(&mng->free_cts, ct, next);
 		rte_spinlock_unlock(&mng->ct_sl);
 	}
-	return ret;
+	return (int)ret;
+}
+
+static inline int
+flow_dv_aso_ct_release(struct rte_eth_dev *dev, uint32_t own_idx)
+{
+	uint16_t owner = (uint16_t)MLX5_INDIRECT_ACT_CT_GET_OWNER(own_idx);
+	uint32_t idx = MLX5_INDIRECT_ACT_CT_GET_IDX(own_idx);
+	struct rte_eth_dev *owndev = &rte_eth_devices[owner];
+	RTE_SET_USED(dev);
+
+	MLX5_ASSERT(owner < RTE_MAX_ETHPORTS);
+	if (dev->data->dev_started != 1)
+		return -1;
+	return flow_dv_aso_ct_dev_release(owndev, idx);
 }
 
 /*
@@ -11479,7 +11493,7 @@ flow_dv_aso_ct_alloc(struct rte_eth_dev *dev, struct rte_flow_error *error)
 		RTE_SET_USED(reg_c);
 #endif
 		if (!ct->dr_action_orig) {
-			flow_dv_aso_ct_release(dev, ct_idx);
+			flow_dv_aso_ct_dev_release(dev, ct_idx);
 			rte_flow_error_set(error, rte_errno,
 					   RTE_FLOW_ERROR_TYPE_ACTION, NULL,
 					   "failed to create ASO CT action");
@@ -11495,7 +11509,7 @@ flow_dv_aso_ct_alloc(struct rte_eth_dev *dev, struct rte_flow_error *error)
 			 reg_c - REG_C_0);
 #endif
 		if (!ct->dr_action_rply) {
-			flow_dv_aso_ct_release(dev, ct_idx);
+			flow_dv_aso_ct_dev_release(dev, ct_idx);
 			rte_flow_error_set(error, rte_errno,
 					   RTE_FLOW_ERROR_TYPE_ACTION, NULL,
 					   "failed to create ASO CT action");
@@ -11537,12 +11551,13 @@ flow_dv_translate_create_conntrack(struct rte_eth_dev *dev,
 		return rte_flow_error_set(error, rte_errno,
 					  RTE_FLOW_ERROR_TYPE_ACTION, NULL,
 					  "Failed to allocate CT object");
-	ct = flow_aso_ct_get_by_idx(dev, idx);
+	ct = flow_aso_ct_get_by_dev_idx(dev, idx);
 	if (mlx5_aso_ct_update_by_wqe(sh, ct, pro))
 		return rte_flow_error_set(error, EBUSY,
 					  RTE_FLOW_ERROR_TYPE_ACTION, NULL,
 					  "Failed to update CT");
 	ct->is_original = !!pro->is_original_dir;
+	ct->peer = pro->peer_port;
 	return idx;
 }
 
@@ -11699,7 +11714,7 @@ flow_dv_translate(struct rte_eth_dev *dev,
 		const struct rte_flow_action *found_action = NULL;
 		uint32_t jump_group = 0;
 		struct mlx5_flow_counter *cnt;
-		uint32_t ct_idx;
+		uint32_t owner_idx;
 		struct mlx5_aso_ct_action *ct;
 
 		if (!mlx5_flow_os_action_supported(action_type))
@@ -12175,8 +12190,12 @@ flow_dv_translate(struct rte_eth_dev *dev,
 			action_flags |= MLX5_FLOW_ACTION_MODIFY_FIELD;
 			break;
 		case RTE_FLOW_ACTION_TYPE_CONNTRACK:
-			ct_idx = (uint32_t)(uintptr_t)action->conf;
-			ct = flow_aso_ct_get_by_idx(dev, ct_idx);
+			owner_idx = (uint32_t)(uintptr_t)action->conf;
+			ct = flow_aso_ct_get_by_idx(dev, owner_idx);
+			if (!ct) {
+				rte_errno = EINVAL;
+				return -rte_errno;
+			}
 			if (mlx5_aso_ct_available(priv->sh, ct))
 				return -rte_errno;
 			if (ct->is_original)
@@ -12186,7 +12205,7 @@ flow_dv_translate(struct rte_eth_dev *dev,
 				dev_flow->dv.actions[actions_n] =
 							ct->dr_action_rply;
 			flow->indirect_type = MLX5_INDIRECT_ACTION_TYPE_CT;
-			flow->ct = ct_idx;
+			flow->ct = owner_idx;
 			__atomic_fetch_add(&ct->refcnt, 1, __ATOMIC_RELAXED);
 			actions_n++;
 			action_flags |= MLX5_FLOW_ACTION_CT;
@@ -13786,8 +13805,7 @@ flow_dv_action_create(struct rte_eth_dev *dev,
 	case RTE_FLOW_ACTION_TYPE_CONNTRACK:
 		ret = flow_dv_translate_create_conntrack(dev, action->conf,
 							 err);
-		idx = (MLX5_INDIRECT_ACTION_TYPE_CT <<
-		       MLX5_INDIRECT_ACTION_TYPE_OFFSET) | ret;
+		idx = MLX5_INDIRECT_ACT_CT_GEN_IDX(PORT_ID(priv), ret);
 		break;
 	default:
 		rte_flow_error_set(err, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION,
@@ -13839,7 +13857,9 @@ flow_dv_action_destroy(struct rte_eth_dev *dev,
 		return 0;
 	case MLX5_INDIRECT_ACTION_TYPE_CT:
 		ret = flow_dv_aso_ct_release(dev, idx);
-		if (ret)
+		if (ret < 0)
+			return ret;
+		if (ret > 0)
 			DRV_LOG(DEBUG, "Connection tracking object %u still "
 				"has references %d.", idx, ret);
 		return 0;
@@ -13943,8 +13963,16 @@ __flow_dv_action_ct_update(struct rte_eth_dev *dev, uint32_t idx,
 	struct mlx5_aso_ct_action *ct;
 	const struct rte_flow_action_conntrack *new_prf;
 	int ret = 0;
+	uint16_t owner = (uint16_t)MLX5_INDIRECT_ACT_CT_GET_OWNER(idx);
+	uint32_t dev_idx;
 
-	ct = flow_aso_ct_get_by_idx(dev, idx);
+	if (PORT_ID(priv) != owner)
+		return rte_flow_error_set(error, EACCES,
+					  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+					  NULL,
+					  "CT object owned by another port");
+	dev_idx = MLX5_INDIRECT_ACT_CT_GET_IDX(idx);
+	ct = flow_aso_ct_get_by_dev_idx(dev, dev_idx);
 	if (!ct->refcnt)
 		return rte_flow_error_set(error, ENOMEM,
 					  RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
@@ -14022,6 +14050,8 @@ flow_dv_action_query(struct rte_eth_dev *dev,
 	uint32_t idx = act_idx & ((1u << MLX5_INDIRECT_ACTION_TYPE_OFFSET) - 1);
 	struct mlx5_priv *priv = dev->data->dev_private;
 	struct mlx5_aso_ct_action *ct;
+	uint16_t owner;
+	uint32_t dev_idx;
 
 	switch (type) {
 	case MLX5_INDIRECT_ACTION_TYPE_AGE:
@@ -14036,7 +14066,14 @@ flow_dv_action_query(struct rte_eth_dev *dev,
 			     (&age_param->sec_since_last_hit, __ATOMIC_RELAXED);
 		return 0;
 	case MLX5_INDIRECT_ACTION_TYPE_CT:
-		ct = flow_aso_ct_get_by_idx(dev, idx);
+		owner = (uint16_t)MLX5_INDIRECT_ACT_CT_GET_OWNER(idx);
+		if (owner != PORT_ID(priv))
+			return rte_flow_error_set(error, EACCES,
+					RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+					NULL,
+					"CT object owned by another port");
+		dev_idx = MLX5_INDIRECT_ACT_CT_GET_IDX(idx);
+		ct = flow_aso_ct_get_by_dev_idx(dev, dev_idx);
 		if (!ct->refcnt)
 			return rte_flow_error_set(error, ENOMEM,
 					RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
