diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 585cab98b4..1acf8fddf8 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -408,6 +408,16 @@ enum index {
 	ACTION_SAMPLE_INDEX_VALUE,
 	ACTION_SHARED,
 	SHARED_ACTION_ID2PTR,
+	ACTION_COPY_ITEM,
+	ACTION_COPY_ITEM_DST_TYPE,
+	ACTION_COPY_ITEM_DST_TYPE_VALUE,
+	ACTION_COPY_ITEM_DST_INDEX,
+	ACTION_COPY_ITEM_DST_OFFSET,
+	ACTION_COPY_ITEM_SRC_TYPE,
+	ACTION_COPY_ITEM_SRC_TYPE_VALUE,
+	ACTION_COPY_ITEM_SRC_INDEX,
+	ACTION_COPY_ITEM_SRC_OFFSET,
+	ACTION_COPY_ITEM_WIDTH,
 };
 
 /** Maximum size for pattern in struct rte_flow_item_raw. */
@@ -561,6 +571,18 @@ struct rte_flow_action_count sample_count[RAW_SAMPLE_CONFS_MAX_NUM];
 struct rte_flow_action_port_id sample_port_id[RAW_SAMPLE_CONFS_MAX_NUM];
 struct rte_flow_action_raw_encap sample_encap[RAW_SAMPLE_CONFS_MAX_NUM];
 
+static const char *const copy_item_table[] = {
+	"none", "mac_dst", "mac_src",
+	"vlan_type", "vlan_id", "mac_type",
+	"ipv4_dscp", "ipv4_ttl", "ipv4_src", "ipv4_dst",
+	"ipv6_hoplimit", "ipv6_src", "ipv6_dst",
+	"tcp_port_src", "tcp_port_dst",
+	"tcp_seq_num", "tcp_ack_num", "tcp_flags",
+	"udp_port_src", "udp_port_dst",
+	"vxlan_vni", "geneve_vni", "gtp_teid",
+	"tag", "mark", "meta", NULL
+};
+
 /** Maximum number of subsequent tokens and arguments on the stack. */
 #define CTX_STACK_SIZE 16
 
@@ -1306,6 +1328,7 @@ static const enum index next_action[] = {
 	ACTION_AGE,
 	ACTION_SAMPLE,
 	ACTION_SHARED,
+	ACTION_COPY_ITEM,
 	ZERO,
 };
 
@@ -1638,6 +1661,10 @@ static int
 parse_vc_action_sample_index(struct context *ctx, const struct token *token,
 				const char *str, unsigned int len, void *buf,
 				unsigned int size);
+static int
+parse_vc_copy_item(struct context *ctx, const struct token *token,
+				const char *str, unsigned int len, void *buf,
+				unsigned int size);
 static int parse_destroy(struct context *, const struct token *,
 			 const char *, unsigned int,
 			 void *, unsigned int);
@@ -1722,6 +1749,8 @@ static int comp_set_raw_index(struct context *, const struct token *,
 			      unsigned int, char *, unsigned int);
 static int comp_set_sample_index(struct context *, const struct token *,
 			      unsigned int, char *, unsigned int);
+static int comp_set_copy_item(struct context *, const struct token *,
+			      unsigned int, char *, unsigned int);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -4037,6 +4066,85 @@ static const struct token token_list[] = {
 		.call = parse_vc_action_raw_decap_index,
 		.comp = comp_set_raw_index,
 	},
+	[ACTION_COPY_ITEM] = {
+		.name = "copy_item",
+		.help = "copy data from dst to src item",
+		.priv = PRIV_ACTION(COPY_ITEM,
+			sizeof(struct rte_flow_action_copy_item)),
+		.next = NEXT(NEXT_ENTRY(ACTION_COPY_ITEM_DST_TYPE)),
+		.call = parse_vc,
+	},
+	[ACTION_COPY_ITEM_DST_TYPE] = {
+		.name = "dst_type",
+		.help = "destination item type",
+		.next = NEXT(NEXT_ENTRY(ACTION_COPY_ITEM_DST_INDEX),
+			NEXT_ENTRY(ACTION_COPY_ITEM_DST_TYPE_VALUE)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_COPY_ITEM_DST_TYPE_VALUE] = {
+		.name = "{type}",
+		.help = "destination item type value",
+		.call = parse_vc_copy_item,
+		.comp = comp_set_copy_item,
+	},
+	[ACTION_COPY_ITEM_DST_INDEX] = {
+		.name = "dst_index",
+		.help = "destination item index",
+		.next = NEXT(NEXT_ENTRY(ACTION_COPY_ITEM_DST_OFFSET),
+			NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_copy_item,
+					dst.index)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_COPY_ITEM_DST_OFFSET] = {
+		.name = "dst_offset",
+		.help = "destination item offset",
+		.next = NEXT(NEXT_ENTRY(ACTION_COPY_ITEM_SRC_TYPE),
+			NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_copy_item,
+					dst.offset)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_COPY_ITEM_SRC_TYPE] = {
+		.name = "src_type",
+		.help = "source item type",
+		.next = NEXT(NEXT_ENTRY(ACTION_COPY_ITEM_SRC_INDEX),
+			NEXT_ENTRY(ACTION_COPY_ITEM_SRC_TYPE_VALUE)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_COPY_ITEM_SRC_TYPE_VALUE] = {
+		.name = "{type}",
+		.help = "source item type value",
+		.call = parse_vc_copy_item,
+		.comp = comp_set_copy_item,
+	},
+	[ACTION_COPY_ITEM_SRC_INDEX] = {
+		.name = "src_index",
+		.help = "source item type index",
+		.next = NEXT(NEXT_ENTRY(ACTION_COPY_ITEM_SRC_OFFSET),
+			NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_copy_item,
+					src.index)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_COPY_ITEM_SRC_OFFSET] = {
+		.name = "src_offset",
+		.help = "source item type offset",
+		.next = NEXT(NEXT_ENTRY(ACTION_COPY_ITEM_WIDTH),
+			NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_copy_item,
+					src.offset)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_COPY_ITEM_WIDTH] = {
+		.name = "width",
+		.help = "number of bits to copy",
+		.next = NEXT(NEXT_ENTRY(ACTION_NEXT),
+			NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_copy_item,
+					width)),
+		.call = parse_vc_conf,
+	},
 	/* Top level command. */
 	[SET] = {
 		.name = "set",
@@ -5960,6 +6068,36 @@ parse_vc_action_sample_index(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/** Parse tokens for copy_item command. */
+static int
+parse_vc_copy_item(struct context *ctx, const struct token *token,
+			 const char *str, unsigned int len, void *buf,
+			 unsigned int size)
+{
+	struct rte_flow_action_copy_item *action_copy_item;
+	unsigned int i;
+
+	(void)token;
+	(void)buf;
+	(void)size;
+	if (ctx->curr != ACTION_COPY_ITEM_DST_TYPE_VALUE &&
+		ctx->curr != ACTION_COPY_ITEM_SRC_TYPE_VALUE)
+		return -1;
+	for (i = 0; copy_item_table[i]; ++i)
+		if (!strcmp_partial(copy_item_table[i], str, len))
+			break;
+	if (!copy_item_table[i])
+		return -1;
+	if (!ctx->object)
+		return len;
+	action_copy_item = ctx->object;
+	if (ctx->curr == ACTION_COPY_ITEM_DST_TYPE_VALUE)
+		action_copy_item->dst.item = i;
+	else
+		action_copy_item->src.item = i;
+	return len;
+}
+
 /** Parse tokens for destroy command. */
 static int
 parse_destroy(struct context *ctx, const struct token *token,
@@ -7029,6 +7167,24 @@ comp_set_sample_index(struct context *ctx, const struct token *token,
 	return nb;
 }
 
+/** Complete item type for copy_item command. */
+static int
+comp_set_copy_item(struct context *ctx, const struct token *token,
+		   unsigned int ent, char *buf, unsigned int size)
+{
+	uint16_t idx = 0;
+
+	RTE_SET_USED(ctx);
+	RTE_SET_USED(token);
+	for (idx = 0; copy_item_table[idx]; ++idx)
+		;
+	if (!buf)
+		return idx + 1;
+	if (ent < idx)
+		return strlcpy(buf, copy_item_table[ent], size);
+	return -1;
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;
 
