[v4,2/2] app/testpmd: add support for generic copy rte flow action

Message ID 20210113170751.13044-3-akozyrev@nvidia.com (mailing list archive)
State Superseded, archived
Delegated to: Ferruh Yigit
Headers
Series generic copy rte flow action support |

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/Intel-compilation success Compilation OK
ci/iol-broadcom-Functional success Functional Testing PASS
ci/iol-broadcom-Performance success Performance Testing PASS
ci/iol-intel-Functional success Functional Testing PASS
ci/iol-intel-Performance success Performance Testing PASS
ci/intel-Testing success Testing PASS
ci/iol-abi-testing success Testing PASS
ci/iol-testing success Testing PASS

Commit Message

Alexander Kozyrev Jan. 13, 2021, 5:07 p.m. UTC
  Add support for the RTE_FLOW_ACTION_COPY_FIELD to the testpmd.
Implement CLI to create the copy_field action and supply all the
needed parameters to copy an arbitrary packet field (as well as
mark, tag or metadata) into another field.

Example of the flow is the following:
flow create 0 egress group 1 pattern eth / ipv4 / udp / end
     actions copy_field dst_type tag dst_level 0 dst_offset 8
     src_type gtp_teid src_level 0 src_offset 0 width 32 / end

This flow copies 32 bits from the first Tag in the Tags array
into the outermost GTP TEID packet header field. 8 bits of the
Tag are skipped as indicated by the dst_offset action parameter.

dst_type and src_type are the only mandatory parameters to
specify. Levels and offset are 0 by default if they are not
overridden by a user. The width parameter gets the smallest width
from the source and destination sizes if it is not specified.

Signed-off-by: Alexander Kozyrev <akozyrev@nvidia.com>
---
 app/test-pmd/cmdline_flow.c | 166 ++++++++++++++++++++++++++++++++++++
 1 file changed, 166 insertions(+)
  

Comments

Ori Kam Jan. 14, 2021, 3:18 p.m. UTC | #1
Hi

> -----Original Message-----
> From: Alexander Kozyrev <akozyrev@nvidia.com>
> Sent: Wednesday, January 13, 2021 7:08 PM
> Subject: [PATCH v4 2/2] app/testpmd: add support for generic copy rte flow
> action
> 
> Add support for the RTE_FLOW_ACTION_COPY_FIELD to the testpmd.
> Implement CLI to create the copy_field action and supply all the
> needed parameters to copy an arbitrary packet field (as well as
> mark, tag or metadata) into another field.
> 
> Example of the flow is the following:
> flow create 0 egress group 1 pattern eth / ipv4 / udp / end
>      actions copy_field dst_type tag dst_level 0 dst_offset 8
>      src_type gtp_teid src_level 0 src_offset 0 width 32 / end
> 
> This flow copies 32 bits from the first Tag in the Tags array
> into the outermost GTP TEID packet header field. 8 bits of the
> Tag are skipped as indicated by the dst_offset action parameter.
> 
> dst_type and src_type are the only mandatory parameters to
> specify. Levels and offset are 0 by default if they are not
> overridden by a user. The width parameter gets the smallest width
> from the source and destination sizes if it is not specified.
> 
> Signed-off-by: Alexander Kozyrev <akozyrev@nvidia.com>
> ---
>  app/test-pmd/cmdline_flow.c | 166
> ++++++++++++++++++++++++++++++++++++
>  1 file changed, 166 insertions(+)
> 
> diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
> index 585cab98b4..115d4772f9 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_FIELD,
> +	ACTION_COPY_FIELD_DST_TYPE,
> +	ACTION_COPY_FIELD_DST_TYPE_VALUE,
> +	ACTION_COPY_FIELD_DST_LEVEL,
> +	ACTION_COPY_FIELD_DST_OFFSET,
> +	ACTION_COPY_FIELD_SRC_TYPE,
> +	ACTION_COPY_FIELD_SRC_TYPE_VALUE,
> +	ACTION_COPY_FIELD_SRC_LEVEL,
> +	ACTION_COPY_FIELD_SRC_OFFSET,
> +	ACTION_COPY_FIELD_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_field_table[] = {
> +	"start", "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_FIELD,
>  	ZERO,
>  };
> 
> @@ -1556,6 +1579,20 @@ static const enum index next_action_sample[] = {
>  	ZERO,
>  };
> 
> +static const enum index action_copy_field_dst[] = {
> +	ACTION_COPY_FIELD_DST_LEVEL,
> +	ACTION_COPY_FIELD_DST_OFFSET,
> +	ACTION_COPY_FIELD_SRC_TYPE,
> +	ZERO,
> +};
> +
> +static const enum index action_copy_field_src[] = {
> +	ACTION_COPY_FIELD_SRC_LEVEL,
> +	ACTION_COPY_FIELD_SRC_OFFSET,
> +	ACTION_COPY_FIELD_WIDTH,
> +	ZERO,
> +};
> +
I think if we remove the order of actions then it is possible to use the same struct
both for the src and the dst. What do you think? (see my comment below)

>  static int parse_set_raw_encap_decap(struct context *, const struct token *,
>  				     const char *, unsigned int,
>  				     void *, unsigned int);
> @@ -1638,6 +1675,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_field(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 +1763,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_field(struct context *, const struct token *,
> +			      unsigned int, char *, unsigned int);
> 
>  /** Token definitions. */
>  static const struct token token_list[] = {
> @@ -4037,6 +4080,81 @@ static const struct token token_list[] = {
>  		.call = parse_vc_action_raw_decap_index,
>  		.comp = comp_set_raw_index,
>  	},
> +	[ACTION_COPY_FIELD] = {
> +		.name = "copy_field",
> +		.help = "copy data from destination field to source field",
> +		.priv = PRIV_ACTION(COPY_FIELD,
> +			sizeof(struct rte_flow_action_copy_field)),
> +		.next = NEXT(NEXT_ENTRY(ACTION_COPY_FIELD_DST_TYPE)),
> +		.call = parse_vc,

You are forcing that the first parameter will be the source, what do you think about
removing this limitation?

> +	},
> +	[ACTION_COPY_FIELD_DST_TYPE] = {
> +		.name = "dst_type",
> +		.help = "destination field type",
> +		.next = NEXT(action_copy_field_dst,
> +
> 	NEXT_ENTRY(ACTION_COPY_FIELD_DST_TYPE_VALUE)),
> +		.call = parse_vc_conf,
> +	},
> +	[ACTION_COPY_FIELD_DST_TYPE_VALUE] = {
> +		.name = "{type}",
> +		.help = "destination field type value",
> +		.call = parse_vc_copy_field,
> +		.comp = comp_set_copy_field,
> +	},
> +	[ACTION_COPY_FIELD_DST_LEVEL] = {
> +		.name = "dst_level",
> +		.help = "destination field level",
> +		.next = NEXT(action_copy_field_dst, NEXT_ENTRY(UNSIGNED)),
> +		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_copy_field,
> +					dst.level)),
> +		.call = parse_vc_conf,
> +	},
> +	[ACTION_COPY_FIELD_DST_OFFSET] = {
> +		.name = "dst_offset",
> +		.help = "destination field bit offset",
> +		.next = NEXT(action_copy_field_dst, NEXT_ENTRY(UNSIGNED)),
> +		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_copy_field,
> +					dst.offset)),
> +		.call = parse_vc_conf,
> +	},
> +	[ACTION_COPY_FIELD_SRC_TYPE] = {
> +		.name = "src_type",
> +		.help = "source field type",
> +		.next = NEXT(action_copy_field_src,
> +
> 	NEXT_ENTRY(ACTION_COPY_FIELD_SRC_TYPE_VALUE)),
> +		.call = parse_vc_conf,
> +	},
> +	[ACTION_COPY_FIELD_SRC_TYPE_VALUE] = {
> +		.name = "{type}",
> +		.help = "source field type value",
> +		.call = parse_vc_copy_field,
> +		.comp = comp_set_copy_field,
> +	},
> +	[ACTION_COPY_FIELD_SRC_LEVEL] = {
> +		.name = "src_level",
> +		.help = "source field level",
> +		.next = NEXT(action_copy_field_src, NEXT_ENTRY(UNSIGNED)),
> +		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_copy_field,
> +					src.level)),
> +		.call = parse_vc_conf,
> +	},
> +	[ACTION_COPY_FIELD_SRC_OFFSET] = {
> +		.name = "src_offset",
> +		.help = "source field bit offset",
> +		.next = NEXT(action_copy_field_src, NEXT_ENTRY(UNSIGNED)),
> +		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_copy_field,
> +					src.offset)),
> +		.call = parse_vc_conf,
> +	},
> +	[ACTION_COPY_FIELD_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_field,
> +					width)),
> +		.call = parse_vc_conf,
> +	},
>  	/* Top level command. */
>  	[SET] = {
>  		.name = "set",
> @@ -5960,6 +6078,36 @@ parse_vc_action_sample_index(struct context *ctx,
> const struct token *token,
>  	return len;
>  }
> 
> +/** Parse tokens for copy_field command. */
> +static int
> +parse_vc_copy_field(struct context *ctx, const struct token *token,
> +			 const char *str, unsigned int len, void *buf,
> +			 unsigned int size)
> +{
> +	struct rte_flow_action_copy_field *action_copy_field;
> +	unsigned int i;
> +
> +	(void)token;
> +	(void)buf;
> +	(void)size;
> +	if (ctx->curr != ACTION_COPY_FIELD_DST_TYPE_VALUE &&
> +		ctx->curr != ACTION_COPY_FIELD_SRC_TYPE_VALUE)
> +		return -1;
> +	for (i = 0; copy_field_table[i]; ++i)
> +		if (!strcmp_partial(copy_field_table[i], str, len))
> +			break;
> +	if (!copy_field_table[i])
> +		return -1;
> +	if (!ctx->object)
> +		return len;
> +	action_copy_field = ctx->object;
> +	if (ctx->curr == ACTION_COPY_FIELD_DST_TYPE_VALUE)
> +		action_copy_field->dst.field = (enum rte_flow_field_id)i;
> +	else
> +		action_copy_field->src.field = (enum rte_flow_field_id)i;
> +	return len;
> +}
> +
>  /** Parse tokens for destroy command. */
>  static int
>  parse_destroy(struct context *ctx, const struct token *token,
> @@ -7029,6 +7177,24 @@ comp_set_sample_index(struct context *ctx, const
> struct token *token,
>  	return nb;
>  }
> 
> +/** Complete field type for copy_field command. */
> +static int
> +comp_set_copy_field(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_field_table[idx]; ++idx)
> +		;
> +	if (!buf)
> +		return idx + 1;
> +	if (ent < idx)
> +		return strlcpy(buf, copy_field_table[ent], size);
> +	return -1;
> +}
> +
>  /** Internal context. */
>  static struct context cmd_flow_context;
> 
> --
> 2.24.1


Acked-by: Ori Kam <orika@nvidia.com>
Best,
Ori
  
Alexander Kozyrev Jan. 15, 2021, 3:37 p.m. UTC | #2
> From: Ori Kam <orika@nvidia.com> on Thursday, January 14, 2021 10:19
> Subject: RE: [PATCH v4 2/2] app/testpmd: add support for generic copy rte
> flow action
> 
> Hi
> 
> > -----Original Message-----
> > From: Alexander Kozyrev <akozyrev@nvidia.com>
> > Sent: Wednesday, January 13, 2021 7:08 PM
> > Subject: [PATCH v4 2/2] app/testpmd: add support for generic copy rte
> flow
> > action
> >
> > Add support for the RTE_FLOW_ACTION_COPY_FIELD to the testpmd.
> > Implement CLI to create the copy_field action and supply all the
> > needed parameters to copy an arbitrary packet field (as well as
> > mark, tag or metadata) into another field.
> >
> > Example of the flow is the following:
> > flow create 0 egress group 1 pattern eth / ipv4 / udp / end
> >      actions copy_field dst_type tag dst_level 0 dst_offset 8
> >      src_type gtp_teid src_level 0 src_offset 0 width 32 / end
> >
> > This flow copies 32 bits from the first Tag in the Tags array
> > into the outermost GTP TEID packet header field. 8 bits of the
> > Tag are skipped as indicated by the dst_offset action parameter.
> >
> > dst_type and src_type are the only mandatory parameters to
> > specify. Levels and offset are 0 by default if they are not
> > overridden by a user. The width parameter gets the smallest width
> > from the source and destination sizes if it is not specified.
> >
> > Signed-off-by: Alexander Kozyrev <akozyrev@nvidia.com>
> > ---
> >  app/test-pmd/cmdline_flow.c | 166
> > ++++++++++++++++++++++++++++++++++++
> >  1 file changed, 166 insertions(+)
> >
> > diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
> > index 585cab98b4..115d4772f9 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_FIELD,
> > +	ACTION_COPY_FIELD_DST_TYPE,
> > +	ACTION_COPY_FIELD_DST_TYPE_VALUE,
> > +	ACTION_COPY_FIELD_DST_LEVEL,
> > +	ACTION_COPY_FIELD_DST_OFFSET,
> > +	ACTION_COPY_FIELD_SRC_TYPE,
> > +	ACTION_COPY_FIELD_SRC_TYPE_VALUE,
> > +	ACTION_COPY_FIELD_SRC_LEVEL,
> > +	ACTION_COPY_FIELD_SRC_OFFSET,
> > +	ACTION_COPY_FIELD_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_field_table[] = {
> > +	"start", "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_FIELD,
> >  	ZERO,
> >  };
> >
> > @@ -1556,6 +1579,20 @@ static const enum index next_action_sample[] =
> {
> >  	ZERO,
> >  };
> >
> > +static const enum index action_copy_field_dst[] = {
> > +	ACTION_COPY_FIELD_DST_LEVEL,
> > +	ACTION_COPY_FIELD_DST_OFFSET,
> > +	ACTION_COPY_FIELD_SRC_TYPE,
> > +	ZERO,
> > +};
> > +
> > +static const enum index action_copy_field_src[] = {
> > +	ACTION_COPY_FIELD_SRC_LEVEL,
> > +	ACTION_COPY_FIELD_SRC_OFFSET,
> > +	ACTION_COPY_FIELD_WIDTH,
> > +	ZERO,
> > +};
> > +
> I think if we remove the order of actions then it is possible to use the same
> struct
> both for the src and the dst. What do you think? (see my comment below)
This is done intentionally so user can get away without specifying all the mandatory
parameters. That saves us a trouble to validate if they are present or not later.

> >  static int parse_set_raw_encap_decap(struct context *, const struct token
> *,
> >  				     const char *, unsigned int,
> >  				     void *, unsigned int);
> > @@ -1638,6 +1675,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_field(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 +1763,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_field(struct context *, const struct token *,
> > +			      unsigned int, char *, unsigned int);
> >
> >  /** Token definitions. */
> >  static const struct token token_list[] = {
> > @@ -4037,6 +4080,81 @@ static const struct token token_list[] = {
> >  		.call = parse_vc_action_raw_decap_index,
> >  		.comp = comp_set_raw_index,
> >  	},
> > +	[ACTION_COPY_FIELD] = {
> > +		.name = "copy_field",
> > +		.help = "copy data from destination field to source field",
> > +		.priv = PRIV_ACTION(COPY_FIELD,
> > +			sizeof(struct rte_flow_action_copy_field)),
> > +		.next =
> NEXT(NEXT_ENTRY(ACTION_COPY_FIELD_DST_TYPE)),
> > +		.call = parse_vc,
> 
> You are forcing that the first parameter will be the source, what do you think
> about
> removing this limitation?
I'm forcing the first parameter to be the destination, then the source, then the width.
This way I can be sure that all of them are specified when the flow is created.

> 
> > +	},
> > +	[ACTION_COPY_FIELD_DST_TYPE] = {
> > +		.name = "dst_type",
> > +		.help = "destination field type",
> > +		.next = NEXT(action_copy_field_dst,
> > +
> > 	NEXT_ENTRY(ACTION_COPY_FIELD_DST_TYPE_VALUE)),
> > +		.call = parse_vc_conf,
> > +	},
> > +	[ACTION_COPY_FIELD_DST_TYPE_VALUE] = {
> > +		.name = "{type}",
> > +		.help = "destination field type value",
> > +		.call = parse_vc_copy_field,
> > +		.comp = comp_set_copy_field,
> > +	},
> > +	[ACTION_COPY_FIELD_DST_LEVEL] = {
> > +		.name = "dst_level",
> > +		.help = "destination field level",
> > +		.next = NEXT(action_copy_field_dst,
> NEXT_ENTRY(UNSIGNED)),
> > +		.args = ARGS(ARGS_ENTRY(struct
> rte_flow_action_copy_field,
> > +					dst.level)),
> > +		.call = parse_vc_conf,
> > +	},
> > +	[ACTION_COPY_FIELD_DST_OFFSET] = {
> > +		.name = "dst_offset",
> > +		.help = "destination field bit offset",
> > +		.next = NEXT(action_copy_field_dst,
> NEXT_ENTRY(UNSIGNED)),
> > +		.args = ARGS(ARGS_ENTRY(struct
> rte_flow_action_copy_field,
> > +					dst.offset)),
> > +		.call = parse_vc_conf,
> > +	},
> > +	[ACTION_COPY_FIELD_SRC_TYPE] = {
> > +		.name = "src_type",
> > +		.help = "source field type",
> > +		.next = NEXT(action_copy_field_src,
> > +
> > 	NEXT_ENTRY(ACTION_COPY_FIELD_SRC_TYPE_VALUE)),
> > +		.call = parse_vc_conf,
> > +	},
> > +	[ACTION_COPY_FIELD_SRC_TYPE_VALUE] = {
> > +		.name = "{type}",
> > +		.help = "source field type value",
> > +		.call = parse_vc_copy_field,
> > +		.comp = comp_set_copy_field,
> > +	},
> > +	[ACTION_COPY_FIELD_SRC_LEVEL] = {
> > +		.name = "src_level",
> > +		.help = "source field level",
> > +		.next = NEXT(action_copy_field_src,
> NEXT_ENTRY(UNSIGNED)),
> > +		.args = ARGS(ARGS_ENTRY(struct
> rte_flow_action_copy_field,
> > +					src.level)),
> > +		.call = parse_vc_conf,
> > +	},
> > +	[ACTION_COPY_FIELD_SRC_OFFSET] = {
> > +		.name = "src_offset",
> > +		.help = "source field bit offset",
> > +		.next = NEXT(action_copy_field_src,
> NEXT_ENTRY(UNSIGNED)),
> > +		.args = ARGS(ARGS_ENTRY(struct
> rte_flow_action_copy_field,
> > +					src.offset)),
> > +		.call = parse_vc_conf,
> > +	},
> > +	[ACTION_COPY_FIELD_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_field,
> > +					width)),
> > +		.call = parse_vc_conf,
> > +	},
> >  	/* Top level command. */
> >  	[SET] = {
> >  		.name = "set",
> > @@ -5960,6 +6078,36 @@ parse_vc_action_sample_index(struct context
> *ctx,
> > const struct token *token,
> >  	return len;
> >  }
> >
> > +/** Parse tokens for copy_field command. */
> > +static int
> > +parse_vc_copy_field(struct context *ctx, const struct token *token,
> > +			 const char *str, unsigned int len, void *buf,
> > +			 unsigned int size)
> > +{
> > +	struct rte_flow_action_copy_field *action_copy_field;
> > +	unsigned int i;
> > +
> > +	(void)token;
> > +	(void)buf;
> > +	(void)size;
> > +	if (ctx->curr != ACTION_COPY_FIELD_DST_TYPE_VALUE &&
> > +		ctx->curr != ACTION_COPY_FIELD_SRC_TYPE_VALUE)
> > +		return -1;
> > +	for (i = 0; copy_field_table[i]; ++i)
> > +		if (!strcmp_partial(copy_field_table[i], str, len))
> > +			break;
> > +	if (!copy_field_table[i])
> > +		return -1;
> > +	if (!ctx->object)
> > +		return len;
> > +	action_copy_field = ctx->object;
> > +	if (ctx->curr == ACTION_COPY_FIELD_DST_TYPE_VALUE)
> > +		action_copy_field->dst.field = (enum rte_flow_field_id)i;
> > +	else
> > +		action_copy_field->src.field = (enum rte_flow_field_id)i;
> > +	return len;
> > +}
> > +
> >  /** Parse tokens for destroy command. */
> >  static int
> >  parse_destroy(struct context *ctx, const struct token *token,
> > @@ -7029,6 +7177,24 @@ comp_set_sample_index(struct context *ctx,
> const
> > struct token *token,
> >  	return nb;
> >  }
> >
> > +/** Complete field type for copy_field command. */
> > +static int
> > +comp_set_copy_field(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_field_table[idx]; ++idx)
> > +		;
> > +	if (!buf)
> > +		return idx + 1;
> > +	if (ent < idx)
> > +		return strlcpy(buf, copy_field_table[ent], size);
> > +	return -1;
> > +}
> > +
> >  /** Internal context. */
> >  static struct context cmd_flow_context;
> >
> > --
> > 2.24.1
> 
> 
> Acked-by: Ori Kam <orika@nvidia.com>
> Best,
> Ori
  

Patch

diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 585cab98b4..115d4772f9 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_FIELD,
+	ACTION_COPY_FIELD_DST_TYPE,
+	ACTION_COPY_FIELD_DST_TYPE_VALUE,
+	ACTION_COPY_FIELD_DST_LEVEL,
+	ACTION_COPY_FIELD_DST_OFFSET,
+	ACTION_COPY_FIELD_SRC_TYPE,
+	ACTION_COPY_FIELD_SRC_TYPE_VALUE,
+	ACTION_COPY_FIELD_SRC_LEVEL,
+	ACTION_COPY_FIELD_SRC_OFFSET,
+	ACTION_COPY_FIELD_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_field_table[] = {
+	"start", "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_FIELD,
 	ZERO,
 };
 
@@ -1556,6 +1579,20 @@  static const enum index next_action_sample[] = {
 	ZERO,
 };
 
+static const enum index action_copy_field_dst[] = {
+	ACTION_COPY_FIELD_DST_LEVEL,
+	ACTION_COPY_FIELD_DST_OFFSET,
+	ACTION_COPY_FIELD_SRC_TYPE,
+	ZERO,
+};
+
+static const enum index action_copy_field_src[] = {
+	ACTION_COPY_FIELD_SRC_LEVEL,
+	ACTION_COPY_FIELD_SRC_OFFSET,
+	ACTION_COPY_FIELD_WIDTH,
+	ZERO,
+};
+
 static int parse_set_raw_encap_decap(struct context *, const struct token *,
 				     const char *, unsigned int,
 				     void *, unsigned int);
@@ -1638,6 +1675,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_field(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 +1763,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_field(struct context *, const struct token *,
+			      unsigned int, char *, unsigned int);
 
 /** Token definitions. */
 static const struct token token_list[] = {
@@ -4037,6 +4080,81 @@  static const struct token token_list[] = {
 		.call = parse_vc_action_raw_decap_index,
 		.comp = comp_set_raw_index,
 	},
+	[ACTION_COPY_FIELD] = {
+		.name = "copy_field",
+		.help = "copy data from destination field to source field",
+		.priv = PRIV_ACTION(COPY_FIELD,
+			sizeof(struct rte_flow_action_copy_field)),
+		.next = NEXT(NEXT_ENTRY(ACTION_COPY_FIELD_DST_TYPE)),
+		.call = parse_vc,
+	},
+	[ACTION_COPY_FIELD_DST_TYPE] = {
+		.name = "dst_type",
+		.help = "destination field type",
+		.next = NEXT(action_copy_field_dst,
+			NEXT_ENTRY(ACTION_COPY_FIELD_DST_TYPE_VALUE)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_COPY_FIELD_DST_TYPE_VALUE] = {
+		.name = "{type}",
+		.help = "destination field type value",
+		.call = parse_vc_copy_field,
+		.comp = comp_set_copy_field,
+	},
+	[ACTION_COPY_FIELD_DST_LEVEL] = {
+		.name = "dst_level",
+		.help = "destination field level",
+		.next = NEXT(action_copy_field_dst, NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_copy_field,
+					dst.level)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_COPY_FIELD_DST_OFFSET] = {
+		.name = "dst_offset",
+		.help = "destination field bit offset",
+		.next = NEXT(action_copy_field_dst, NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_copy_field,
+					dst.offset)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_COPY_FIELD_SRC_TYPE] = {
+		.name = "src_type",
+		.help = "source field type",
+		.next = NEXT(action_copy_field_src,
+			NEXT_ENTRY(ACTION_COPY_FIELD_SRC_TYPE_VALUE)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_COPY_FIELD_SRC_TYPE_VALUE] = {
+		.name = "{type}",
+		.help = "source field type value",
+		.call = parse_vc_copy_field,
+		.comp = comp_set_copy_field,
+	},
+	[ACTION_COPY_FIELD_SRC_LEVEL] = {
+		.name = "src_level",
+		.help = "source field level",
+		.next = NEXT(action_copy_field_src, NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_copy_field,
+					src.level)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_COPY_FIELD_SRC_OFFSET] = {
+		.name = "src_offset",
+		.help = "source field bit offset",
+		.next = NEXT(action_copy_field_src, NEXT_ENTRY(UNSIGNED)),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_action_copy_field,
+					src.offset)),
+		.call = parse_vc_conf,
+	},
+	[ACTION_COPY_FIELD_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_field,
+					width)),
+		.call = parse_vc_conf,
+	},
 	/* Top level command. */
 	[SET] = {
 		.name = "set",
@@ -5960,6 +6078,36 @@  parse_vc_action_sample_index(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/** Parse tokens for copy_field command. */
+static int
+parse_vc_copy_field(struct context *ctx, const struct token *token,
+			 const char *str, unsigned int len, void *buf,
+			 unsigned int size)
+{
+	struct rte_flow_action_copy_field *action_copy_field;
+	unsigned int i;
+
+	(void)token;
+	(void)buf;
+	(void)size;
+	if (ctx->curr != ACTION_COPY_FIELD_DST_TYPE_VALUE &&
+		ctx->curr != ACTION_COPY_FIELD_SRC_TYPE_VALUE)
+		return -1;
+	for (i = 0; copy_field_table[i]; ++i)
+		if (!strcmp_partial(copy_field_table[i], str, len))
+			break;
+	if (!copy_field_table[i])
+		return -1;
+	if (!ctx->object)
+		return len;
+	action_copy_field = ctx->object;
+	if (ctx->curr == ACTION_COPY_FIELD_DST_TYPE_VALUE)
+		action_copy_field->dst.field = (enum rte_flow_field_id)i;
+	else
+		action_copy_field->src.field = (enum rte_flow_field_id)i;
+	return len;
+}
+
 /** Parse tokens for destroy command. */
 static int
 parse_destroy(struct context *ctx, const struct token *token,
@@ -7029,6 +7177,24 @@  comp_set_sample_index(struct context *ctx, const struct token *token,
 	return nb;
 }
 
+/** Complete field type for copy_field command. */
+static int
+comp_set_copy_field(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_field_table[idx]; ++idx)
+		;
+	if (!buf)
+		return idx + 1;
+	if (ent < idx)
+		return strlcpy(buf, copy_field_table[ent], size);
+	return -1;
+}
+
 /** Internal context. */
 static struct context cmd_flow_context;