diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index 2d74df8..46c9291 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -40,6 +40,7 @@
 #include <rte_devargs.h>
 #include <rte_flow.h>
 #include <rte_gro.h>
+#include <rte_mbuf_dyn.h>
 
 #include <cmdline_rdline.h>
 #include <cmdline_parse.h>
@@ -70,6 +71,8 @@
 #include "cmdline_tm.h"
 #include "bpf_cmd.h"
 
+char dynf_names[64][RTE_MBUF_DYN_NAMESIZE];
+
 static struct cmdline *testpmd_cl;
 
 static void cmd_reconfig_device_queue(portid_t id, uint8_t dev, uint8_t queue);
@@ -901,6 +904,11 @@ static void cmd_help_long_parsed(void *parsed_result,
 			"port config (port_id) tx_metadata (value)\n"
 			"    Set Tx metadata value per port. Testpmd will add this value"
 			" to any Tx packet sent from this port\n\n"
+
+			"port config (port_id) dynf (name) set|clear\n"
+			"    Register a dynf and Set/clear this flag on Tx. "
+			"Testpmd will set this value to any Tx packet "
+			"sent from this port\n\n"
 		);
 	}
 
@@ -18837,6 +18845,85 @@ struct cmd_config_tx_metadata_specific_result {
 	},
 };
 
+/* *** set dynf *** */
+struct cmd_config_tx_dynf_specific_result {
+	cmdline_fixed_string_t port;
+	cmdline_fixed_string_t keyword;
+	uint16_t port_id;
+	cmdline_fixed_string_t item;
+	cmdline_fixed_string_t name;
+	cmdline_fixed_string_t value;
+};
+
+static void
+cmd_config_dynf_specific_parsed(void *parsed_result,
+				__attribute__((unused)) struct cmdline *cl,
+				__attribute__((unused)) void *data)
+{
+	struct cmd_config_tx_dynf_specific_result *res = parsed_result;
+	struct rte_mbuf_dynflag desc_flag;
+	int flag;
+	uint64_t old_port_flags;
+
+	if (port_id_is_invalid(res->port_id, ENABLED_WARN))
+		return;
+	flag = rte_mbuf_dynflag_lookup(res->name, NULL);
+	if (flag <= 0) {
+		strcpy(desc_flag.name, res->name);
+		desc_flag.flags = 0;
+		flag = rte_mbuf_dynflag_register(&desc_flag);
+		if (flag < 0) {
+			printf("Can't register flag");
+			return;
+		}
+		strcpy(dynf_names[flag], res->name);
+	}
+	old_port_flags = ports[res->port_id].dynf;
+	if (!strcmp(res->value, "set")) {
+		ports[res->port_id].dynf |= 1UL << flag;
+		if (old_port_flags == 0)
+			add_tx_dynf_callback(res->port_id);
+	} else {
+		ports[res->port_id].dynf &= ~(1UL << flag);
+		if (ports[res->port_id].dynf == 0)
+			remove_tx_dynf_callback(res->port_id);
+	}
+}
+
+cmdline_parse_token_string_t cmd_config_tx_dynf_specific_port =
+	TOKEN_STRING_INITIALIZER(struct cmd_config_tx_dynf_specific_result,
+			keyword, "port");
+cmdline_parse_token_string_t cmd_config_tx_dynf_specific_keyword =
+	TOKEN_STRING_INITIALIZER(struct cmd_config_tx_dynf_specific_result,
+			keyword, "config");
+cmdline_parse_token_num_t cmd_config_tx_dynf_specific_port_id =
+	TOKEN_NUM_INITIALIZER(struct cmd_config_tx_dynf_specific_result,
+			port_id, UINT16);
+cmdline_parse_token_string_t cmd_config_tx_dynf_specific_item =
+	TOKEN_STRING_INITIALIZER(struct cmd_config_tx_dynf_specific_result,
+			item, "dynf");
+cmdline_parse_token_string_t cmd_config_tx_dynf_specific_name =
+	TOKEN_STRING_INITIALIZER(struct cmd_config_tx_dynf_specific_result,
+			name, NULL);
+cmdline_parse_token_string_t cmd_config_tx_dynf_specific_value =
+	TOKEN_STRING_INITIALIZER(struct cmd_config_tx_dynf_specific_result,
+			value, "set#clear");
+
+cmdline_parse_inst_t cmd_config_tx_dynf_specific = {
+	.f = cmd_config_dynf_specific_parsed,
+	.data = NULL,
+	.help_str = "port config <port id> dynf <name> set|clear",
+	.tokens = {
+		(void *)&cmd_config_tx_dynf_specific_port,
+		(void *)&cmd_config_tx_dynf_specific_keyword,
+		(void *)&cmd_config_tx_dynf_specific_port_id,
+		(void *)&cmd_config_tx_dynf_specific_item,
+		(void *)&cmd_config_tx_dynf_specific_name,
+		(void *)&cmd_config_tx_dynf_specific_value,
+		NULL,
+	},
+};
+
 /* *** display tx_metadata per port configuration *** */
 struct cmd_show_tx_metadata_result {
 	cmdline_fixed_string_t cmd_show;
@@ -19520,6 +19607,7 @@ struct cmd_showport_macs_result {
 	(cmdline_parse_inst_t *)&cmd_set_raw,
 	(cmdline_parse_inst_t *)&cmd_show_set_raw,
 	(cmdline_parse_inst_t *)&cmd_show_set_raw_all,
+	(cmdline_parse_inst_t *)&cmd_config_tx_dynf_specific,
 	NULL,
 };
 
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 857a11f..49f3e43 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -104,6 +104,13 @@ struct rss_type_info {
 extern const struct rss_type_info rss_type_table[];
 
 /**
+ * Dynf name array.
+ *
+ * Array that holds the name for each dynf.
+ */
+extern char dynf_names[64][RTE_MBUF_DYN_NAMESIZE];
+
+/**
  * The data structure associated with a forwarding stream between a receive
  * port/queue and a transmit port/queue.
  */
@@ -193,6 +200,9 @@ struct rte_port {
 	/**< metadata value to insert in Tx packets. */
 	uint32_t		tx_metadata;
 	const struct rte_eth_rxtx_callback *tx_set_md_cb[RTE_MAX_QUEUES_PER_PORT+1];
+	/**< dynamic flags. */
+	uint64_t		dynf;
+	const struct rte_eth_rxtx_callback *tx_set_dynf_cb[RTE_MAX_QUEUES_PER_PORT+1];
 };
 
 /**
@@ -884,6 +894,12 @@ uint16_t tx_pkt_set_md(uint16_t port_id, __rte_unused uint16_t queue,
 void add_tx_md_callback(portid_t portid);
 void remove_tx_md_callback(portid_t portid);
 
+uint16_t tx_pkt_set_dynf(uint16_t port_id, __rte_unused uint16_t queue,
+			 struct rte_mbuf *pkts[], uint16_t nb_pkts,
+			 __rte_unused void *user_param);
+void add_tx_dynf_callback(portid_t portid);
+void remove_tx_dynf_callback(portid_t portid);
+
 /*
  * Work-around of a compilation error with ICC on invocations of the
  * rte_be_to_cpu_16() function.
diff --git a/app/test-pmd/util.c b/app/test-pmd/util.c
index b514be5..5af3d07 100644
--- a/app/test-pmd/util.c
+++ b/app/test-pmd/util.c
@@ -39,6 +39,7 @@
 	uint16_t udp_port;
 	uint32_t vx_vni;
 	const char *reason;
+	int dynf_index;
 
 	if (!nb_pkts)
 		return;
@@ -88,6 +89,12 @@
 		if (is_rx && (ol_flags & PKT_RX_DYNF_METADATA))
 			printf(" - Rx metadata: 0x%x",
 			       *RTE_FLOW_DYNF_METADATA(mb));
+		for (dynf_index = 0; dynf_index < 64; dynf_index++) {
+			if (dynf_names[dynf_index][0] != '\0')
+				printf(" - dynf %s: %d",
+				       dynf_names[dynf_index],
+				       !!(ol_flags & (1UL << dynf_index)));
+		}
 		if (mb->packet_type) {
 			rte_get_ptype_name(mb->packet_type, buf, sizeof(buf));
 			printf(" - hw ptype: %s", buf);
@@ -241,6 +248,62 @@
 		}
 }
 
+uint16_t
+tx_pkt_set_dynf(uint16_t port_id, __rte_unused uint16_t queue,
+		struct rte_mbuf *pkts[], uint16_t nb_pkts,
+		__rte_unused void *user_param)
+{
+	uint16_t i = 0;
+
+	if (ports[port_id].dynf)
+		for (i = 0; i < nb_pkts; i++)
+			pkts[i]->ol_flags |= ports[port_id].dynf;
+	return nb_pkts;
+}
+
+void
+add_tx_dynf_callback(portid_t portid)
+{
+	struct rte_eth_dev_info dev_info;
+	uint16_t queue;
+	int ret;
+
+	if (port_id_is_invalid(portid, ENABLED_WARN))
+		return;
+
+	ret = eth_dev_info_get_print_err(portid, &dev_info);
+	if (ret != 0)
+		return;
+
+	for (queue = 0; queue < dev_info.nb_tx_queues; queue++)
+		if (!ports[portid].tx_set_dynf_cb[queue])
+			ports[portid].tx_set_dynf_cb[queue] =
+				rte_eth_add_tx_callback(portid, queue,
+							tx_pkt_set_dynf, NULL);
+}
+
+void
+remove_tx_dynf_callback(portid_t portid)
+{
+	struct rte_eth_dev_info dev_info;
+	uint16_t queue;
+	int ret;
+
+	if (port_id_is_invalid(portid, ENABLED_WARN))
+		return;
+
+	ret = eth_dev_info_get_print_err(portid, &dev_info);
+	if (ret != 0)
+		return;
+
+	for (queue = 0; queue < dev_info.nb_tx_queues; queue++)
+		if (ports[portid].tx_set_dynf_cb[queue]) {
+			rte_eth_remove_tx_callback(portid, queue,
+				ports[portid].tx_set_dynf_cb[queue]);
+			ports[portid].tx_set_dynf_cb[queue] = NULL;
+		}
+}
+
 int
 eth_dev_info_get_print_err(uint16_t port_id,
 					struct rte_eth_dev_info *dev_info)
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index 10aabf5..94327e1 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -2317,6 +2317,16 @@ testpmd will add this value to any Tx packet sent from this port::
 
    testpmd> port config (port_id) tx_metadata (value)
 
+port config dynf
+~~~~~~~~~~~~~~~~
+
+Set/clear dynamic flag per port.
+testpmd will register this flag in the mbuf (same registration
+for both Tx and Rx). Then set/clear this flag for each Tx
+packet sent from this port. The set bit only works for Tx packet::
+
+   testpmd> port config (port_id) dynf (name) (set|clear)
+
 port config mtu
 ~~~~~~~~~~~~~~~
 
