diff --git a/doc/guides/nics/mlx5.rst b/doc/guides/nics/mlx5.rst
index fceb5bd58b..85820d7931 100644
--- a/doc/guides/nics/mlx5.rst
+++ b/doc/guides/nics/mlx5.rst
@@ -582,6 +582,10 @@ Limitations
   - Modification of GENEVE Network ID's is not supported when configured
     ``FLEX_PARSER_PROFILE_ENABLE`` supports Geneve TLV options.
     See :ref:`mlx5_firmware_config` for more flex parser information.
+  - Modification of GENEVE TLV option fields is supported only for HW steering.
+    Only DWs configured in :ref:`parser creation <geneve_parser_api>` can be modified,
+    'type' and 'class' fields can be modified when ``match_on_class_mode=2``.
+  - Modification of GENEVE TLV option data supports one DW per action.
   - Encapsulation levels are not supported, can modify outermost header fields only.
   - Offsets cannot skip past the boundary of a field.
   - If the field type is ``RTE_FLOW_FIELD_MAC_TYPE``
diff --git a/doc/guides/rel_notes/release_24_03.rst b/doc/guides/rel_notes/release_24_03.rst
index 8a99d6bfa4..f8d87c8a3c 100644
--- a/doc/guides/rel_notes/release_24_03.rst
+++ b/doc/guides/rel_notes/release_24_03.rst
@@ -60,6 +60,9 @@ New Features
   * Added HW steering support for ``RTE_FLOW_ITEM_TYPE_GENEVE`` flow item.
   * Added HW steering support for ``RTE_FLOW_ITEM_TYPE_GENEVE_OPT`` flow item.
   * Added HW steering support for modify field ``RTE_FLOW_FIELD_GENEVE_VNI`` flow action.
+  * Added HW steering support for modify field ``RTE_FLOW_FIELD_GENEVE_OPT_TYPE`` flow action.
+  * Added HW steering support for modify field ``RTE_FLOW_FIELD_GENEVE_OPT_CLASS`` flow action.
+  * Added HW steering support for modify field ``RTE_FLOW_FIELD_GENEVE_OPT_DATA`` flow action.
 
 
 Removed Items
diff --git a/drivers/net/mlx5/mlx5_flow.h b/drivers/net/mlx5/mlx5_flow.h
index 808f364c6c..65fe5be2fd 100644
--- a/drivers/net/mlx5/mlx5_flow.h
+++ b/drivers/net/mlx5/mlx5_flow.h
@@ -1805,6 +1805,25 @@ mlx5_get_geneve_hl_data(const void *dr_ctx, uint8_t type, uint16_t class,
 			struct mlx5_hl_data ** const hl_dws,
 			bool *ok_bit_on_class);
 
+/**
+ * Get modify field ID for single DW inside configured GENEVE TLV option.
+ *
+ * @param[in] dr_ctx
+ *   Pointer to HW steering DR context.
+ * @param[in] type
+ *   GENEVE TLV option type.
+ * @param[in] class
+ *   GENEVE TLV option class.
+ * @param[in] dw_offset
+ *   Offset of DW inside the option.
+ *
+ * @return
+ *   Modify field ID on success, negative errno otherwise and rte_errno is set.
+ */
+int
+mlx5_get_geneve_option_modify_field_id(const void *dr_ctx, uint8_t type,
+				       uint16_t class, uint8_t dw_offset);
+
 void *
 mlx5_geneve_tlv_parser_create(uint16_t port_id,
 			      const struct rte_pmd_mlx5_geneve_tlv tlv_list[],
@@ -1813,6 +1832,8 @@ int mlx5_geneve_tlv_parser_destroy(void *handle);
 int mlx5_flow_geneve_tlv_option_validate(struct mlx5_priv *priv,
 					 const struct rte_flow_item *geneve_opt,
 					 struct rte_flow_error *error);
+int mlx5_geneve_opt_modi_field_get(struct mlx5_priv *priv,
+				   const struct rte_flow_action_modify_data *data);
 
 struct mlx5_geneve_tlv_options_mng;
 int mlx5_geneve_tlv_option_register(struct mlx5_priv *priv,
diff --git a/drivers/net/mlx5/mlx5_flow_dv.c b/drivers/net/mlx5/mlx5_flow_dv.c
index bb3d7ddc3c..2a7ee4e91f 100644
--- a/drivers/net/mlx5/mlx5_flow_dv.c
+++ b/drivers/net/mlx5/mlx5_flow_dv.c
@@ -1446,6 +1446,21 @@ mlx5_mpls_modi_field_get(const struct rte_flow_action_modify_data *data)
 	return MLX5_MODI_IN_MPLS_LABEL_0 + data->tag_index;
 }
 
+static __rte_always_inline int
+flow_geneve_opt_modi_field_get(struct mlx5_priv *priv,
+			       const struct rte_flow_action_modify_data *data)
+{
+#ifdef HAVE_MLX5_HWS_SUPPORT
+	return mlx5_geneve_opt_modi_field_get(priv, data);
+#else
+	(void)priv;
+	(void)data;
+	DRV_LOG(ERR, "GENEVE option modification is not supported.");
+	rte_errno = ENOTSUP;
+	return -rte_errno;
+#endif
+}
+
 static void
 mlx5_modify_flex_item(const struct rte_eth_dev *dev,
 		      const struct mlx5_flex_item *flex,
@@ -1579,9 +1594,11 @@ mlx5_flow_field_id_to_modify_info
 		 const struct rte_flow_attr *attr, struct rte_flow_error *error)
 {
 	struct mlx5_priv *priv = dev->data->dev_private;
+	enum mlx5_modification_field modi_id;
 	uint32_t idx = 0;
 	uint32_t off_be = 0;
 	uint32_t length = 0;
+
 	switch ((int)data->field) {
 	case RTE_FLOW_FIELD_START:
 		/* not supported yet */
@@ -1892,6 +1909,48 @@ mlx5_flow_field_id_to_modify_info
 		else
 			info[idx].offset = off_be;
 		break;
+	case RTE_FLOW_FIELD_GENEVE_OPT_TYPE:
+		MLX5_ASSERT(data->offset + width <= 8);
+		modi_id = flow_geneve_opt_modi_field_get(priv, data);
+		if (modi_id < 0)
+			return;
+		/* Type is on bits 16-8 of GENEVE option header (DW0). */
+		off_be = 32 - (16 + data->offset + width);
+		info[idx] = (struct field_modify_info){4, 0, modi_id};
+		if (mask)
+			mask[idx] = flow_modify_info_mask_32(width, off_be);
+		else
+			info[idx].offset = off_be;
+		break;
+	case RTE_FLOW_FIELD_GENEVE_OPT_CLASS:
+		MLX5_ASSERT(data->offset + width <= 16);
+		modi_id = flow_geneve_opt_modi_field_get(priv, data);
+		if (modi_id < 0)
+			return;
+		/* Class is on bits 31-16 of GENEVE option header (DW0). */
+		off_be = 32 - (data->offset + width);
+		info[idx] = (struct field_modify_info){4, 0, modi_id};
+		if (mask)
+			mask[idx] = flow_modify_info_mask_32(width, off_be);
+		else
+			info[idx].offset = off_be;
+		break;
+	case RTE_FLOW_FIELD_GENEVE_OPT_DATA:
+		if ((data->offset % 32) + width > 32) {
+			DRV_LOG(ERR, "Geneve TLV option data is per DW.");
+			return;
+		}
+		modi_id = flow_geneve_opt_modi_field_get(priv, data);
+		if (modi_id < 0)
+			return;
+		/* Use offset inside DW. */
+		off_be = 32 - ((data->offset % 32) + width);
+		info[idx] = (struct field_modify_info){4, 0, modi_id};
+		if (mask)
+			mask[idx] = flow_modify_info_mask_32(width, off_be);
+		else
+			info[idx].offset = off_be;
+		break;
 	case RTE_FLOW_FIELD_GTP_TEID:
 		MLX5_ASSERT(data->offset + width <= 32);
 		off_be = 32 - (data->offset + width);
@@ -1905,8 +1964,8 @@ mlx5_flow_field_id_to_modify_info
 	case RTE_FLOW_FIELD_MPLS:
 		MLX5_ASSERT(data->offset + width <= 32);
 		off_be = 32 - (data->offset + width);
-		info[idx] = (struct field_modify_info){4, 0,
-					mlx5_mpls_modi_field_get(data)};
+		modi_id = mlx5_mpls_modi_field_get(data);
+		info[idx] = (struct field_modify_info){4, 0, modi_id};
 		if (mask)
 			mask[idx] = flow_modify_info_mask_32(width, off_be);
 		else
@@ -5388,6 +5447,21 @@ flow_dv_validate_action_modify_field(struct rte_eth_dev *dev,
 				RTE_FLOW_ERROR_TYPE_ACTION, action,
 				"modifications of the GENEVE Network"
 				" Identifier is not supported");
+	if (dst_data->field == RTE_FLOW_FIELD_GENEVE_OPT_TYPE ||
+	    src_data->field == RTE_FLOW_FIELD_GENEVE_OPT_TYPE)
+		return rte_flow_error_set(error, ENOTSUP,
+				RTE_FLOW_ERROR_TYPE_ACTION, action,
+				"modifications of the GENEVE option type is not supported");
+	if (dst_data->field == RTE_FLOW_FIELD_GENEVE_OPT_CLASS ||
+	    src_data->field == RTE_FLOW_FIELD_GENEVE_OPT_CLASS)
+		return rte_flow_error_set(error, ENOTSUP,
+				RTE_FLOW_ERROR_TYPE_ACTION, action,
+				"modifications of the GENEVE option class is not supported");
+	if (dst_data->field == RTE_FLOW_FIELD_GENEVE_OPT_DATA ||
+	    src_data->field == RTE_FLOW_FIELD_GENEVE_OPT_DATA)
+		return rte_flow_error_set(error, ENOTSUP,
+				RTE_FLOW_ERROR_TYPE_ACTION, action,
+				"modifications of the GENEVE option data is not supported");
 	if (dst_data->field == RTE_FLOW_FIELD_MPLS ||
 	    src_data->field == RTE_FLOW_FIELD_MPLS)
 		return rte_flow_error_set(error, ENOTSUP,
diff --git a/drivers/net/mlx5/mlx5_flow_geneve.c b/drivers/net/mlx5/mlx5_flow_geneve.c
index f3ee414d02..d5847a60e9 100644
--- a/drivers/net/mlx5/mlx5_flow_geneve.c
+++ b/drivers/net/mlx5/mlx5_flow_geneve.c
@@ -254,6 +254,123 @@ mlx5_geneve_tlv_options_unregister(struct mlx5_priv *priv,
 	mng->nb_options = 0;
 }
 
+/**
+ * Get single DW resource from given option.
+ *
+ * @param option
+ *   Pointer to single GENEVE TLV option.
+ * @param offset
+ *   Offset of DW related to option start.
+ *
+ * @return
+ *   DW resource on success, NULL otherwise and rte_errno is set.
+ */
+static struct mlx5_geneve_tlv_resource *
+mlx5_geneve_tlv_option_get_resource_by_offset(struct mlx5_geneve_tlv_option *option,
+					      uint8_t offset)
+{
+	uint8_t i;
+
+	for (i = 0; option->resources[i].obj != NULL; ++i) {
+		if (option->resources[i].offset < offset)
+			continue;
+		if (option->resources[i].offset == offset)
+			return &option->resources[i];
+		break;
+	}
+	DRV_LOG(ERR, "The DW in offset %u wasn't configured.", offset);
+	rte_errno = EINVAL;
+	return NULL;
+}
+
+int
+mlx5_get_geneve_option_modify_field_id(const void *dr_ctx, uint8_t type,
+				       uint16_t class, uint8_t dw_offset)
+{
+	uint16_t port_id;
+
+	MLX5_ETH_FOREACH_DEV(port_id, NULL) {
+		struct mlx5_priv *priv;
+		struct mlx5_geneve_tlv_option *option;
+		struct mlx5_geneve_tlv_resource *resource;
+
+		priv = rte_eth_devices[port_id].data->dev_private;
+		if (priv->dr_ctx != dr_ctx)
+			continue;
+		/* Find specific option inside list. */
+		option = mlx5_geneve_tlv_option_get(priv, type, class);
+		if (option == NULL)
+			return -rte_errno;
+		/* Find specific FW object inside option resources. */
+		resource = mlx5_geneve_tlv_option_get_resource_by_offset(option,
+									 dw_offset);
+		if (resource == NULL)
+			return -rte_errno;
+		return resource->modify_field;
+	}
+	DRV_LOG(ERR, "DR CTX %p doesn't belong to any DPDK port.", dr_ctx);
+	rte_errno = EINVAL;
+	return -rte_errno;
+}
+
+/**
+ * Get modify field ID for single DW inside configured GENEVE TLV option.
+ *
+ * @param[in] priv
+ *   Pointer to port's private data.
+ * @param[in] data
+ *   Pointer to modify field data structure.
+ *
+ * @return
+ *   Modify field ID on success, negative errno otherwise and rte_errno is set.
+ */
+int
+mlx5_geneve_opt_modi_field_get(struct mlx5_priv *priv,
+			       const struct rte_flow_action_modify_data *data)
+{
+	uint16_t class = data->class_id;
+	uint8_t type = data->type;
+	struct mlx5_geneve_tlv_option *option;
+	struct mlx5_geneve_tlv_resource *resource;
+	uint8_t offset;
+
+	option = mlx5_geneve_tlv_option_get(priv, type, class);
+	if (option == NULL)
+		return -rte_errno;
+	switch (data->field) {
+	case RTE_FLOW_FIELD_GENEVE_OPT_TYPE:
+	case RTE_FLOW_FIELD_GENEVE_OPT_CLASS:
+		if (!option->match_data[0].dw_mask) {
+			DRV_LOG(ERR, "DW0 isn't configured");
+			rte_errno = EINVAL;
+			return -rte_errno;
+		}
+		resource = &option->resources[0];
+		MLX5_ASSERT(resource->offset == 0);
+		break;
+	case RTE_FLOW_FIELD_GENEVE_OPT_DATA:
+		/*
+		 * Convert offset twice:
+		 *  - First conversion from bit offset to DW offset.
+		 *  - Second conversion is to be related to data start instead
+		 *    of option start.
+		 */
+		offset = (data->offset >> 5) + 1;
+		resource = mlx5_geneve_tlv_option_get_resource_by_offset(option,
+									 offset);
+		break;
+	default:
+		DRV_LOG(ERR,
+			"Field ID %u doesn't describe GENEVE option header.",
+			data->field);
+		rte_errno = EINVAL;
+		return -rte_errno;
+	}
+	if (resource == NULL)
+		return -rte_errno;
+	return resource->modify_field;
+}
+
 /**
  * Create single GENEVE TLV option sample.
  *
diff --git a/drivers/net/mlx5/mlx5_flow_hw.c b/drivers/net/mlx5/mlx5_flow_hw.c
index 22ac4e0a7c..692bbe063e 100644
--- a/drivers/net/mlx5/mlx5_flow_hw.c
+++ b/drivers/net/mlx5/mlx5_flow_hw.c
@@ -1254,10 +1254,12 @@ flow_hw_modify_field_compile(struct rte_eth_dev *dev,
 			else
 				value = rte_cpu_to_be_32(value);
 			item.spec = &value;
-		} else if (conf->dst.field == RTE_FLOW_FIELD_GTP_PSC_QFI) {
+		} else if (conf->dst.field == RTE_FLOW_FIELD_GTP_PSC_QFI ||
+			   conf->dst.field == RTE_FLOW_FIELD_GENEVE_OPT_TYPE) {
 			/*
-			 * QFI is passed as an uint8_t integer, but it is accessed through
-			 * a 2nd least significant byte of a 32-bit field in modify header command.
+			 * Both QFI and Geneve option type are passed as an uint8_t integer,
+			 * but it is accessed through a 2nd least significant byte of a 32-bit
+			 * field in modify header command.
 			 */
 			value = *(const uint8_t *)item.spec;
 			value = rte_cpu_to_be_32(value << 8);
@@ -2825,12 +2827,14 @@ flow_hw_modify_field_construct(struct mlx5_hw_q_job *job,
 			*value_p = rte_cpu_to_be_32(*value_p << 16);
 		else
 			*value_p = rte_cpu_to_be_32(*value_p);
-	} else if (mhdr_action->dst.field == RTE_FLOW_FIELD_GTP_PSC_QFI) {
+	} else if (mhdr_action->dst.field == RTE_FLOW_FIELD_GTP_PSC_QFI ||
+		   mhdr_action->dst.field == RTE_FLOW_FIELD_GENEVE_OPT_TYPE) {
 		uint32_t tmp;
 
 		/*
-		 * QFI is passed as an uint8_t integer, but it is accessed through
-		 * a 2nd least significant byte of a 32-bit field in modify header command.
+		 * Both QFI and Geneve option type are passed as an uint8_t integer,
+		 * but it is accessed through a 2nd least significant byte of a 32-bit
+		 * field in modify header command.
 		 */
 		tmp = values[0];
 		value_p = (unaligned_uint32_t *)values;
@@ -4944,6 +4948,14 @@ flow_hw_modify_field_is_used(const struct rte_flow_action_modify_field *action,
 	return action->src.field == field || action->dst.field == field;
 }
 
+static bool
+flow_hw_modify_field_is_geneve_opt(enum rte_flow_field_id field)
+{
+	return field == RTE_FLOW_FIELD_GENEVE_OPT_TYPE ||
+	       field == RTE_FLOW_FIELD_GENEVE_OPT_CLASS ||
+	       field == RTE_FLOW_FIELD_GENEVE_OPT_DATA;
+}
+
 static int
 flow_hw_validate_action_modify_field(struct rte_eth_dev *dev,
 				     const struct rte_flow_action *action,
@@ -4977,15 +4989,17 @@ flow_hw_validate_action_modify_field(struct rte_eth_dev *dev,
 	ret = flow_validate_modify_field_level(&action_conf->dst, error);
 	if (ret)
 		return ret;
-	if (action_conf->dst.tag_index &&
-	    !flow_modify_field_support_tag_array(action_conf->dst.field))
-		return rte_flow_error_set(error, EINVAL,
-				RTE_FLOW_ERROR_TYPE_ACTION, action,
-				"destination tag index is not supported");
-	if (action_conf->dst.class_id)
-		return rte_flow_error_set(error, EINVAL,
-				RTE_FLOW_ERROR_TYPE_ACTION, action,
-				"destination class id is not supported");
+	if (!flow_hw_modify_field_is_geneve_opt(action_conf->dst.field)) {
+		if (action_conf->dst.tag_index &&
+		    !flow_modify_field_support_tag_array(action_conf->dst.field))
+			return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ACTION, action,
+					"destination tag index is not supported");
+		if (action_conf->dst.class_id)
+			return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ACTION, action,
+					"destination class id is not supported");
+	}
 	if (mask_conf->dst.level != UINT8_MAX)
 		return rte_flow_error_set(error, EINVAL,
 			RTE_FLOW_ERROR_TYPE_ACTION, action,
@@ -5000,15 +5014,17 @@ flow_hw_validate_action_modify_field(struct rte_eth_dev *dev,
 				"destination field mask and template are not equal");
 	if (action_conf->src.field != RTE_FLOW_FIELD_POINTER &&
 	    action_conf->src.field != RTE_FLOW_FIELD_VALUE) {
-		if (action_conf->src.tag_index &&
-		    !flow_modify_field_support_tag_array(action_conf->src.field))
-			return rte_flow_error_set(error, EINVAL,
-				RTE_FLOW_ERROR_TYPE_ACTION, action,
-				"source tag index is not supported");
-		if (action_conf->src.class_id)
-			return rte_flow_error_set(error, EINVAL,
-				RTE_FLOW_ERROR_TYPE_ACTION, action,
-				"source class id is not supported");
+		if (!flow_hw_modify_field_is_geneve_opt(action_conf->src.field)) {
+			if (action_conf->src.tag_index &&
+			    !flow_modify_field_support_tag_array(action_conf->src.field))
+				return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ACTION, action,
+					"source tag index is not supported");
+			if (action_conf->src.class_id)
+				return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ACTION, action,
+					"source class id is not supported");
+		}
 		if (mask_conf->src.level != UINT8_MAX)
 			return rte_flow_error_set(error, EINVAL,
 				RTE_FLOW_ERROR_TYPE_ACTION, action,
@@ -5059,6 +5075,13 @@ flow_hw_validate_action_modify_field(struct rte_eth_dev *dev,
 		return rte_flow_error_set(error, EINVAL,
 				RTE_FLOW_ERROR_TYPE_ACTION, action,
 				"modifying Geneve VNI is not supported when GENEVE opt is supported");
+	if (priv->tlv_options == NULL &&
+	    (flow_hw_modify_field_is_used(action_conf, RTE_FLOW_FIELD_GENEVE_OPT_TYPE) ||
+	     flow_hw_modify_field_is_used(action_conf, RTE_FLOW_FIELD_GENEVE_OPT_CLASS) ||
+	     flow_hw_modify_field_is_used(action_conf, RTE_FLOW_FIELD_GENEVE_OPT_DATA)))
+		return rte_flow_error_set(error, EINVAL,
+				RTE_FLOW_ERROR_TYPE_ACTION, action,
+				"modifying Geneve TLV option is supported only after parser configuration");
 	/* Due to HW bug, tunnel MPLS header is read only. */
 	if (action_conf->dst.field == RTE_FLOW_FIELD_MPLS)
 		return rte_flow_error_set(error, EINVAL,
