diff mbox series

[v5,2/2] app/testpmd: add CLI for conntrack

Message ID 1618854691-370765-3-git-send-email-bingz@nvidia.com (mailing list archive)
State Accepted
Delegated to: Ferruh Yigit
Headers show
Series ethdev: introduce conntrack flow action and item | expand

Checks

Context Check Description
ci/Intel-compilation fail apply issues
ci/checkpatch success coding style OK

Commit Message

Bing Zhao April 19, 2021, 5:51 p.m. UTC
The command line for testing connection tracking is added. To create
a conntrack object, 3 parts are needed.
  set conntrack com peer ...
  set conntrack orig scale ...
  set conntrack rply scale ...
This will create a full conntrack action structure for the indirect
action. After the indirect action handle of "conntrack" created, it
could be used in the flow creation. Before updating, the same
structure is also needed together with the update command
"conntrack_update" to update the "dir" or "ctx".

After the flow with conntrack action created, the packet should jump
to the next flow for the result checking with conntrack item. The
state is defined with bits and a valid combination could be
supported.

Signed-off-by: Bing Zhao <bingz@nvidia.com>
Acked-by: Ori Kam <orika@nvidia.com>
---
 app/test-pmd/cmdline.c                      | 355 ++++++++++++++++++++
 app/test-pmd/cmdline_flow.c                 |  92 +++++
 app/test-pmd/config.c                       |  65 +++-
 app/test-pmd/testpmd.h                      |   2 +
 doc/guides/rel_notes/release_21_05.rst      |   3 +
 doc/guides/testpmd_app_ug/testpmd_funcs.rst |  35 ++
 6 files changed, 551 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index 4d9e038ce8..d282c7cad6 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -13621,6 +13621,359 @@  cmdline_parse_inst_t cmd_set_mplsoudp_decap_with_vlan = {
 	},
 };
 
+/** Set connection tracking object common details */
+struct cmd_set_conntrack_common_result {
+	cmdline_fixed_string_t set;
+	cmdline_fixed_string_t conntrack;
+	cmdline_fixed_string_t common;
+	cmdline_fixed_string_t peer;
+	cmdline_fixed_string_t is_orig;
+	cmdline_fixed_string_t enable;
+	cmdline_fixed_string_t live;
+	cmdline_fixed_string_t sack;
+	cmdline_fixed_string_t cack;
+	cmdline_fixed_string_t last_dir;
+	cmdline_fixed_string_t liberal;
+	cmdline_fixed_string_t state;
+	cmdline_fixed_string_t max_ack_win;
+	cmdline_fixed_string_t retrans;
+	cmdline_fixed_string_t last_win;
+	cmdline_fixed_string_t last_seq;
+	cmdline_fixed_string_t last_ack;
+	cmdline_fixed_string_t last_end;
+	cmdline_fixed_string_t last_index;
+	uint8_t stat;
+	uint8_t factor;
+	uint16_t peer_port;
+	uint32_t is_original;
+	uint32_t en;
+	uint32_t is_live;
+	uint32_t s_ack;
+	uint32_t c_ack;
+	uint32_t ld;
+	uint32_t lb;
+	uint8_t re_num;
+	uint8_t li;
+	uint16_t lw;
+	uint32_t ls;
+	uint32_t la;
+	uint32_t le;
+};
+
+cmdline_parse_token_string_t cmd_set_conntrack_set =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_common_result,
+				 set, "set");
+cmdline_parse_token_string_t cmd_set_conntrack_conntrack =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_common_result,
+				 conntrack, "conntrack");
+cmdline_parse_token_string_t cmd_set_conntrack_common_com =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_common_result,
+				 common, "com");
+cmdline_parse_token_string_t cmd_set_conntrack_common_peer =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_common_result,
+				 peer, "peer");
+cmdline_parse_token_num_t cmd_set_conntrack_common_peer_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_common_result,
+			      peer_port, RTE_UINT16);
+cmdline_parse_token_string_t cmd_set_conntrack_common_is_orig =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_common_result,
+				 is_orig, "is_orig");
+cmdline_parse_token_num_t cmd_set_conntrack_common_is_orig_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_common_result,
+			      is_original, RTE_UINT32);
+cmdline_parse_token_string_t cmd_set_conntrack_common_enable =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_common_result,
+				 enable, "enable");
+cmdline_parse_token_num_t cmd_set_conntrack_common_enable_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_common_result,
+			      en, RTE_UINT32);
+cmdline_parse_token_string_t cmd_set_conntrack_common_live =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_common_result,
+				 live, "live");
+cmdline_parse_token_num_t cmd_set_conntrack_common_live_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_common_result,
+			      is_live, RTE_UINT32);
+cmdline_parse_token_string_t cmd_set_conntrack_common_sack =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_common_result,
+				 sack, "sack");
+cmdline_parse_token_num_t cmd_set_conntrack_common_sack_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_common_result,
+			      s_ack, RTE_UINT32);
+cmdline_parse_token_string_t cmd_set_conntrack_common_cack =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_common_result,
+				 cack, "cack");
+cmdline_parse_token_num_t cmd_set_conntrack_common_cack_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_common_result,
+			      c_ack, RTE_UINT32);
+cmdline_parse_token_string_t cmd_set_conntrack_common_last_dir =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_common_result,
+				 last_dir, "last_dir");
+cmdline_parse_token_num_t cmd_set_conntrack_common_last_dir_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_common_result,
+			      ld, RTE_UINT32);
+cmdline_parse_token_string_t cmd_set_conntrack_common_liberal =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_common_result,
+				 liberal, "liberal");
+cmdline_parse_token_num_t cmd_set_conntrack_common_liberal_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_common_result,
+			      lb, RTE_UINT32);
+cmdline_parse_token_string_t cmd_set_conntrack_common_state =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_common_result,
+				 state, "state");
+cmdline_parse_token_num_t cmd_set_conntrack_common_state_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_common_result,
+			      stat, RTE_UINT8);
+cmdline_parse_token_string_t cmd_set_conntrack_common_max_ackwin =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_common_result,
+				 max_ack_win, "max_ack_win");
+cmdline_parse_token_num_t cmd_set_conntrack_common_max_ackwin_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_common_result,
+			      factor, RTE_UINT8);
+cmdline_parse_token_string_t cmd_set_conntrack_common_retrans =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_common_result,
+				 retrans, "r_lim");
+cmdline_parse_token_num_t cmd_set_conntrack_common_retrans_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_common_result,
+			      re_num, RTE_UINT8);
+cmdline_parse_token_string_t cmd_set_conntrack_common_last_win =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_common_result,
+				 last_win, "last_win");
+cmdline_parse_token_num_t cmd_set_conntrack_common_last_win_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_common_result,
+			      lw, RTE_UINT16);
+cmdline_parse_token_string_t cmd_set_conntrack_common_last_seq =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_common_result,
+				 last_seq, "last_seq");
+cmdline_parse_token_num_t cmd_set_conntrack_common_last_seq_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_common_result,
+			      ls, RTE_UINT32);
+cmdline_parse_token_string_t cmd_set_conntrack_common_last_ack =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_common_result,
+				 last_ack, "last_ack");
+cmdline_parse_token_num_t cmd_set_conntrack_common_last_ack_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_common_result,
+			      la, RTE_UINT32);
+cmdline_parse_token_string_t cmd_set_conntrack_common_last_end =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_common_result,
+				 last_end, "last_end");
+cmdline_parse_token_num_t cmd_set_conntrack_common_last_end_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_common_result,
+			      le, RTE_UINT32);
+cmdline_parse_token_string_t cmd_set_conntrack_common_last_index =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_common_result,
+				 last_index, "last_index");
+cmdline_parse_token_num_t cmd_set_conntrack_common_last_index_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_common_result,
+			      li, RTE_UINT8);
+
+static void cmd_set_conntrack_common_parsed(void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	__rte_unused void *data)
+{
+	struct cmd_set_conntrack_common_result *res = parsed_result;
+
+	/* No need to swap to big endian. */
+	conntrack_context.peer_port = res->peer_port;
+	conntrack_context.is_original_dir = res->is_original;
+	conntrack_context.enable = res->en;
+	conntrack_context.live_connection = res->is_live;
+	conntrack_context.selective_ack = res->s_ack;
+	conntrack_context.challenge_ack_passed = res->c_ack;
+	conntrack_context.last_direction = res->ld;
+	conntrack_context.liberal_mode = res->lb;
+	conntrack_context.state = (enum rte_flow_conntrack_state)res->stat;
+	conntrack_context.max_ack_window = res->factor;
+	conntrack_context.retransmission_limit = res->re_num;
+	conntrack_context.last_window = res->lw;
+	conntrack_context.last_index =
+		(enum rte_flow_conntrack_tcp_last_index)res->li;
+	conntrack_context.last_seq = res->ls;
+	conntrack_context.last_ack = res->la;
+	conntrack_context.last_end = res->le;
+}
+
+cmdline_parse_inst_t cmd_set_conntrack_common = {
+	.f = cmd_set_conntrack_common_parsed,
+	.data = NULL,
+	.help_str = "set conntrack com peer <port_id> is_orig <dir> enable <en>"
+		" live <ack_seen> sack <en> cack <passed> last_dir <dir>"
+		" liberal <en> state <s> max_ack_win <factor> r_lim <num>"
+		" last_win <win> last_seq <seq> last_ack <ack> last_end <end>"
+		" last_index <flag>",
+	.tokens = {
+		(void *)&cmd_set_conntrack_set,
+		(void *)&cmd_set_conntrack_conntrack,
+		(void *)&cmd_set_conntrack_common_com,
+		(void *)&cmd_set_conntrack_common_peer,
+		(void *)&cmd_set_conntrack_common_peer_value,
+		(void *)&cmd_set_conntrack_common_is_orig,
+		(void *)&cmd_set_conntrack_common_is_orig_value,
+		(void *)&cmd_set_conntrack_common_enable,
+		(void *)&cmd_set_conntrack_common_enable_value,
+		(void *)&cmd_set_conntrack_common_live,
+		(void *)&cmd_set_conntrack_common_live_value,
+		(void *)&cmd_set_conntrack_common_sack,
+		(void *)&cmd_set_conntrack_common_sack_value,
+		(void *)&cmd_set_conntrack_common_cack,
+		(void *)&cmd_set_conntrack_common_cack_value,
+		(void *)&cmd_set_conntrack_common_last_dir,
+		(void *)&cmd_set_conntrack_common_last_dir_value,
+		(void *)&cmd_set_conntrack_common_liberal,
+		(void *)&cmd_set_conntrack_common_liberal_value,
+		(void *)&cmd_set_conntrack_common_state,
+		(void *)&cmd_set_conntrack_common_state_value,
+		(void *)&cmd_set_conntrack_common_max_ackwin,
+		(void *)&cmd_set_conntrack_common_max_ackwin_value,
+		(void *)&cmd_set_conntrack_common_retrans,
+		(void *)&cmd_set_conntrack_common_retrans_value,
+		(void *)&cmd_set_conntrack_common_last_win,
+		(void *)&cmd_set_conntrack_common_last_win_value,
+		(void *)&cmd_set_conntrack_common_last_seq,
+		(void *)&cmd_set_conntrack_common_last_seq_value,
+		(void *)&cmd_set_conntrack_common_last_ack,
+		(void *)&cmd_set_conntrack_common_last_ack_value,
+		(void *)&cmd_set_conntrack_common_last_end,
+		(void *)&cmd_set_conntrack_common_last_end_value,
+		(void *)&cmd_set_conntrack_common_last_index,
+		(void *)&cmd_set_conntrack_common_last_index_value,
+		NULL,
+	},
+};
+
+/** Set connection tracking object both directions' details */
+struct cmd_set_conntrack_dir_result {
+	cmdline_fixed_string_t set;
+	cmdline_fixed_string_t conntrack;
+	cmdline_fixed_string_t dir;
+	cmdline_fixed_string_t scale;
+	cmdline_fixed_string_t fin;
+	cmdline_fixed_string_t ack_seen;
+	cmdline_fixed_string_t unack;
+	cmdline_fixed_string_t sent_end;
+	cmdline_fixed_string_t reply_end;
+	cmdline_fixed_string_t max_win;
+	cmdline_fixed_string_t max_ack;
+	uint32_t factor;
+	uint32_t f;
+	uint32_t as;
+	uint32_t un;
+	uint32_t se;
+	uint32_t re;
+	uint32_t mw;
+	uint32_t ma;
+};
+
+cmdline_parse_token_string_t cmd_set_conntrack_dir_set =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_dir_result,
+				 set, "set");
+cmdline_parse_token_string_t cmd_set_conntrack_dir_conntrack =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_dir_result,
+				 conntrack, "conntrack");
+cmdline_parse_token_string_t cmd_set_conntrack_dir_dir =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_dir_result,
+				 dir, "orig#rply");
+cmdline_parse_token_string_t cmd_set_conntrack_dir_scale =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_dir_result,
+				 scale, "scale");
+cmdline_parse_token_num_t cmd_set_conntrack_dir_scale_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_dir_result,
+			      factor, RTE_UINT32);
+cmdline_parse_token_string_t cmd_set_conntrack_dir_fin =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_dir_result,
+				 fin, "fin");
+cmdline_parse_token_num_t cmd_set_conntrack_dir_fin_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_dir_result,
+			      f, RTE_UINT32);
+cmdline_parse_token_string_t cmd_set_conntrack_dir_ack =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_dir_result,
+				 ack_seen, "acked");
+cmdline_parse_token_num_t cmd_set_conntrack_dir_ack_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_dir_result,
+			      as, RTE_UINT32);
+cmdline_parse_token_string_t cmd_set_conntrack_dir_unack_data =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_dir_result,
+				 unack, "unack_data");
+cmdline_parse_token_num_t cmd_set_conntrack_dir_unack_data_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_dir_result,
+			      un, RTE_UINT32);
+cmdline_parse_token_string_t cmd_set_conntrack_dir_sent_end =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_dir_result,
+				 sent_end, "sent_end");
+cmdline_parse_token_num_t cmd_set_conntrack_dir_sent_end_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_dir_result,
+			      se, RTE_UINT32);
+cmdline_parse_token_string_t cmd_set_conntrack_dir_reply_end =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_dir_result,
+				 reply_end, "reply_end");
+cmdline_parse_token_num_t cmd_set_conntrack_dir_reply_end_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_dir_result,
+			      re, RTE_UINT32);
+cmdline_parse_token_string_t cmd_set_conntrack_dir_max_win =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_dir_result,
+				 max_win, "max_win");
+cmdline_parse_token_num_t cmd_set_conntrack_dir_max_win_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_dir_result,
+			      mw, RTE_UINT32);
+cmdline_parse_token_string_t cmd_set_conntrack_dir_max_ack =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_conntrack_dir_result,
+				 max_ack, "max_ack");
+cmdline_parse_token_num_t cmd_set_conntrack_dir_max_ack_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_conntrack_dir_result,
+			      ma, RTE_UINT32);
+
+static void cmd_set_conntrack_dir_parsed(void *parsed_result,
+	__rte_unused struct cmdline *cl,
+	__rte_unused void *data)
+{
+	struct cmd_set_conntrack_dir_result *res = parsed_result;
+	struct rte_flow_tcp_dir_param *dir = NULL;
+
+	if (strcmp(res->dir, "orig") == 0)
+		dir = &conntrack_context.original_dir;
+	else if (strcmp(res->dir, "rply") == 0)
+		dir = &conntrack_context.reply_dir;
+	else
+		return;
+	dir->scale = res->factor;
+	dir->close_initiated = res->f;
+	dir->last_ack_seen = res->as;
+	dir->data_unacked = res->un;
+	dir->sent_end = res->se;
+	dir->reply_end = res->re;
+	dir->max_ack = res->ma;
+	dir->max_win = res->mw;
+}
+
+cmdline_parse_inst_t cmd_set_conntrack_dir = {
+	.f = cmd_set_conntrack_dir_parsed,
+	.data = NULL,
+	.help_str = "set conntrack orig|rply scale <factor> fin <sent>"
+		    " acked <seen> unack_data <unack> sent_end <sent>"
+		    " reply_end <reply> max_win <win> max_ack <ack>",
+	.tokens = {
+		(void *)&cmd_set_conntrack_set,
+		(void *)&cmd_set_conntrack_conntrack,
+		(void *)&cmd_set_conntrack_dir_dir,
+		(void *)&cmd_set_conntrack_dir_scale,
+		(void *)&cmd_set_conntrack_dir_scale_value,
+		(void *)&cmd_set_conntrack_dir_fin,
+		(void *)&cmd_set_conntrack_dir_fin_value,
+		(void *)&cmd_set_conntrack_dir_ack,
+		(void *)&cmd_set_conntrack_dir_ack_value,
+		(void *)&cmd_set_conntrack_dir_unack_data,
+		(void *)&cmd_set_conntrack_dir_unack_data_value,
+		(void *)&cmd_set_conntrack_dir_sent_end,
+		(void *)&cmd_set_conntrack_dir_sent_end_value,
+		(void *)&cmd_set_conntrack_dir_reply_end,
+		(void *)&cmd_set_conntrack_dir_reply_end_value,
+		(void *)&cmd_set_conntrack_dir_max_win,
+		(void *)&cmd_set_conntrack_dir_max_win_value,
+		(void *)&cmd_set_conntrack_dir_max_ack,
+		(void *)&cmd_set_conntrack_dir_max_ack_value,
+		NULL,
+	},
+};
+
 /* Strict link priority scheduling mode setting */
 static void
 cmd_strict_link_prio_parsed(
@@ -17120,6 +17473,8 @@  cmdline_parse_ctx_t main_ctx[] = {
 	(cmdline_parse_inst_t *)&cmd_set_mplsoudp_encap_with_vlan,
 	(cmdline_parse_inst_t *)&cmd_set_mplsoudp_decap,
 	(cmdline_parse_inst_t *)&cmd_set_mplsoudp_decap_with_vlan,
+	(cmdline_parse_inst_t *)&cmd_set_conntrack_common,
+	(cmdline_parse_inst_t *)&cmd_set_conntrack_dir,
 	(cmdline_parse_inst_t *)&cmd_ddp_add,
 	(cmdline_parse_inst_t *)&cmd_ddp_del,
 	(cmdline_parse_inst_t *)&cmd_ddp_get_list,
diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index c5381c638b..e2b09cf16d 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -293,6 +293,7 @@  enum index {
 	ITEM_GENEVE_OPT_TYPE,
 	ITEM_GENEVE_OPT_LENGTH,
 	ITEM_GENEVE_OPT_DATA,
+	ITEM_CONNTRACK,
 
 	/* Validate/create actions. */
 	ACTIONS,
@@ -431,6 +432,10 @@  enum index {
 	ACTION_MODIFY_FIELD_SRC_OFFSET,
 	ACTION_MODIFY_FIELD_SRC_VALUE,
 	ACTION_MODIFY_FIELD_WIDTH,
+	ACTION_CONNTRACK,
+	ACTION_CONNTRACK_UPDATE,
+	ACTION_CONNTRACK_UPDATE_DIR,
+	ACTION_CONNTRACK_UPDATE_CTX,
 };
 
 /** Maximum size for pattern in struct rte_flow_item_raw. */
@@ -569,6 +574,8 @@  struct mplsoudp_encap_conf mplsoudp_encap_conf;
 
 struct mplsoudp_decap_conf mplsoudp_decap_conf;
 
+struct rte_flow_action_conntrack conntrack_context;
+
 #define ACTION_SAMPLE_ACTIONS_NUM 10
 #define RAW_SAMPLE_CONFS_MAX_NUM 8
 /** Storage for struct rte_flow_action_sample including external data. */
@@ -968,6 +975,7 @@  static const enum index next_item[] = {
 	ITEM_PFCP,
 	ITEM_ECPRI,
 	ITEM_GENEVE_OPT,
+	ITEM_CONNTRACK,
 	END_SET,
 	ZERO,
 };
@@ -1382,6 +1390,8 @@  static const enum index next_action[] = {
 	ACTION_SAMPLE,
 	ACTION_INDIRECT,
 	ACTION_MODIFY_FIELD,
+	ACTION_CONNTRACK,
+	ACTION_CONNTRACK_UPDATE,
 	ZERO,
 };
 
@@ -1650,6 +1660,13 @@  static const enum index action_modify_field_src[] = {
 	ZERO,
 };
 
+static const enum index action_update_conntrack[] = {
+	ACTION_CONNTRACK_UPDATE_DIR,
+	ACTION_CONNTRACK_UPDATE_CTX,
+	ACTION_NEXT,
+	ZERO,
+};
+
 static int parse_set_raw_encap_decap(struct context *, const struct token *,
 				     const char *, unsigned int,
 				     void *, unsigned int);
@@ -1740,6 +1757,10 @@  static int
 parse_vc_modify_field_id(struct context *ctx, const struct token *token,
 				const char *str, unsigned int len, void *buf,
 				unsigned int size);
+static int
+parse_vc_action_conntrack_update(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);
@@ -3400,6 +3421,13 @@  static const struct token token_list[] = {
 				(sizeof(struct rte_flow_item_geneve_opt),
 				ITEM_GENEVE_OPT_DATA_SIZE)),
 	},
+	[ITEM_CONNTRACK] = {
+		.name = "conntrack",
+		.help = "conntrack state",
+		.next = NEXT(NEXT_ENTRY(ITEM_NEXT), NEXT_ENTRY(UNSIGNED),
+			     item_param),
+		.args = ARGS(ARGS_ENTRY(struct rte_flow_item_conntrack, flags)),
+	},
 	/* Validate/create actions. */
 	[ACTIONS] = {
 		.name = "actions",
@@ -4498,6 +4526,34 @@  static const struct token token_list[] = {
 		.call = parse_vc_action_sample_index,
 		.comp = comp_set_sample_index,
 	},
+	[ACTION_CONNTRACK] = {
+		.name = "conntrack",
+		.help = "create a conntrack object",
+		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+		.priv = PRIV_ACTION(CONNTRACK,
+				    sizeof(struct rte_flow_action_conntrack)),
+		.call = parse_vc,
+	},
+	[ACTION_CONNTRACK_UPDATE] = {
+		.name = "conntrack_update",
+		.help = "update a conntrack object",
+		.next = NEXT(action_update_conntrack),
+		.priv = PRIV_ACTION(CONNTRACK,
+				    sizeof(struct rte_flow_modify_conntrack)),
+		.call = parse_vc,
+	},
+	[ACTION_CONNTRACK_UPDATE_DIR] = {
+		.name = "dir",
+		.help = "update a conntrack object direction",
+		.next = NEXT(action_update_conntrack),
+		.call = parse_vc_action_conntrack_update,
+	},
+	[ACTION_CONNTRACK_UPDATE_CTX] = {
+		.name = "ctx",
+		.help = "update a conntrack object context",
+		.next = NEXT(action_update_conntrack),
+		.call = parse_vc_action_conntrack_update,
+	},
 	/* Indirect action destroy arguments. */
 	[INDIRECT_ACTION_DESTROY_ID] = {
 		.name = "action_id",
@@ -6304,6 +6360,42 @@  parse_vc_modify_field_id(struct context *ctx, const struct token *token,
 	return len;
 }
 
+/** Parse the conntrack update, not a rte_flow_action. */
+static int
+parse_vc_action_conntrack_update(struct context *ctx, const struct token *token,
+			 const char *str, unsigned int len, void *buf,
+			 unsigned int size)
+{
+	struct buffer *out = buf;
+	struct rte_flow_modify_conntrack *ct_modify = NULL;
+
+	(void)size;
+	if (ctx->curr != ACTION_CONNTRACK_UPDATE_CTX &&
+	    ctx->curr != ACTION_CONNTRACK_UPDATE_DIR)
+		return -1;
+	/* Token name must match. */
+	if (parse_default(ctx, token, str, len, NULL, 0) < 0)
+		return -1;
+	ct_modify = (struct rte_flow_modify_conntrack *)out->args.vc.data;
+	/* Nothing else to do if there is no buffer. */
+	if (!out)
+		return len;
+	if (ctx->curr == ACTION_CONNTRACK_UPDATE_DIR) {
+		ct_modify->new_ct.is_original_dir =
+				conntrack_context.is_original_dir;
+		ct_modify->direction = 1;
+	} else {
+		uint32_t old_dir;
+
+		old_dir = ct_modify->new_ct.is_original_dir;
+		memcpy(&ct_modify->new_ct, &conntrack_context,
+		       sizeof(conntrack_context));
+		ct_modify->new_ct.is_original_dir = old_dir;
+		ct_modify->state = 1;
+	}
+	return len;
+}
+
 /** Parse tokens for destroy command. */
 static int
 parse_destroy(struct context *ctx, const struct token *token,
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 868ff3469b..787d45afbd 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -1483,6 +1483,11 @@  port_action_handle_create(portid_t port_id, uint32_t id,
 
 		pia->age_type = ACTION_AGE_CONTEXT_TYPE_INDIRECT_ACTION;
 		age->context = &pia->age_type;
+	} else if (action->type == RTE_FLOW_ACTION_TYPE_CONNTRACK) {
+		struct rte_flow_action_conntrack *ct =
+		(struct rte_flow_action_conntrack *)(uintptr_t)(action->conf);
+
+		memcpy(ct, &conntrack_context, sizeof(*ct));
 	}
 	/* Poisoning to make sure PMDs update it in case of error. */
 	memset(&error, 0x22, sizeof(error));
@@ -1564,11 +1569,24 @@  port_action_handle_update(portid_t port_id, uint32_t id,
 {
 	struct rte_flow_error error;
 	struct rte_flow_action_handle *action_handle;
+	struct port_indirect_action *pia;
+	const void *update;
 
 	action_handle = port_action_handle_get_by_id(port_id, id);
 	if (!action_handle)
 		return -EINVAL;
-	if (rte_flow_action_handle_update(port_id, action_handle, action,
+	pia = action_get_by_id(port_id, id);
+	if (!pia)
+		return -EINVAL;
+	switch (pia->type) {
+	case RTE_FLOW_ACTION_TYPE_CONNTRACK:
+		update = action->conf;
+		break;
+	default:
+		update = action;
+		break;
+	}
+	if (rte_flow_action_handle_update(port_id, action_handle, update,
 					  &error)) {
 		return port_flow_complain(&error);
 	}
@@ -1621,6 +1639,51 @@  port_action_handle_query(portid_t port_id, uint32_t id)
 		}
 		data = NULL;
 		break;
+	case RTE_FLOW_ACTION_TYPE_CONNTRACK:
+		if (!ret) {
+			struct rte_flow_action_conntrack *ct = data;
+
+			printf("Conntrack Context:\n"
+			       "  Peer: %u, Flow dir: %s, Enable: %u\n"
+			       "  Live: %u, SACK: %u, CACK: %u\n"
+			       "  Packet dir: %s, Liberal: %u, State: %u\n"
+			       "  Factor: %u, Retrans: %u, TCP flags: %u\n"
+			       "  Last Seq: %u, Last ACK: %u\n"
+			       "  Last Win: %u, Last End: %u\n",
+			       ct->peer_port,
+			       ct->is_original_dir ? "Original" : "Reply",
+			       ct->enable, ct->live_connection,
+			       ct->selective_ack, ct->challenge_ack_passed,
+			       ct->last_direction ? "Original" : "Reply",
+			       ct->liberal_mode, ct->state,
+			       ct->max_ack_window, ct->retransmission_limit,
+			       ct->last_index, ct->last_seq, ct->last_ack,
+			       ct->last_window, ct->last_end);
+			printf("  Original Dir:\n"
+			       "    scale: %u, fin: %u, ack seen: %u\n"
+			       " unacked data: %u\n    Sent end: %u,"
+			       "    Reply end: %u, Max win: %u, Max ACK: %u\n",
+			       ct->original_dir.scale,
+			       ct->original_dir.close_initiated,
+			       ct->original_dir.last_ack_seen,
+			       ct->original_dir.data_unacked,
+			       ct->original_dir.sent_end,
+			       ct->original_dir.reply_end,
+			       ct->original_dir.max_win,
+			       ct->original_dir.max_ack);
+			printf("  Reply Dir:\n"
+			       "    scale: %u, fin: %u, ack seen: %u\n"
+			       " unacked data: %u\n    Sent end: %u,"
+			       "    Reply end: %u, Max win: %u, Max ACK: %u\n",
+			       ct->reply_dir.scale,
+			       ct->reply_dir.close_initiated,
+			       ct->reply_dir.last_ack_seen,
+			       ct->reply_dir.data_unacked,
+			       ct->reply_dir.sent_end, ct->reply_dir.reply_end,
+			       ct->reply_dir.max_win, ct->reply_dir.max_ack);
+		}
+		data = NULL;
+		break;
 	default:
 		printf("Indirect action %u (type: %d) on port %u doesn't"
 		       " support query\n", id, pia->type, port_id);
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index c314b30f2e..9530ec5fe0 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -630,6 +630,8 @@  extern struct mplsoudp_decap_conf mplsoudp_decap_conf;
 
 extern enum rte_eth_rx_mq_mode rx_mq_mode;
 
+extern struct rte_flow_action_conntrack conntrack_context;
+
 static inline unsigned int
 lcore_num(void)
 {
diff --git a/doc/guides/rel_notes/release_21_05.rst b/doc/guides/rel_notes/release_21_05.rst
index a5e2a8e503..d06ddb2074 100644
--- a/doc/guides/rel_notes/release_21_05.rst
+++ b/doc/guides/rel_notes/release_21_05.rst
@@ -215,6 +215,9 @@  New Features
     ``show port (port_id) rxq (queue_id) desc used count``
   * Added command to dump internal representation information of single flow.
     ``flow dump (port_id) rule (rule_id)``
+  * Added commands to construct conntrack context and relevant indirect
+    action handle creation, update for conntrack action as well as conntrack
+    item matching.
 
 * **Updated ipsec-secgw sample application.**
 
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index 715e209fd2..efa32bb6ad 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -3789,6 +3789,8 @@  This section lists supported pattern items and their attributes, if any.
   - ``s_field {unsigned}``: S field.
   - ``seid {unsigned}``: session endpoint identifier.
 
+- ``conntrack``: match conntrack state.
+
 Actions list
 ^^^^^^^^^^^^
 
@@ -4927,6 +4929,39 @@  NVGRE encapsulation header and sent to port id 0.
  testpmd> flow create 0 ingress transfer pattern eth / end actions
         sample ratio 1 index 0  / port_id id 2 / end
 
+Sample conntrack rules
+~~~~~~~~~~~~~~~~~~~~~~
+
+Conntrack rules can be set by the following commands
+
+Need to construct the connection context with provided information.
+In the first table, create a flow rule by using conntrack action and jump to
+the next table. In the next table, create a rule to check the state.
+
+::
+
+ testpmd> set conntrack com peer 1 is_orig 1 enable 1 live 1 sack 1 cack 0
+        last_dir 0 liberal 0 state 1 max_ack_win 7 r_lim 5 last_win 510
+        last_seq 2632987379 last_ack 2532480967 last_end 2632987379
+        last_index 0x8
+ testpmd> set conntrack orig scale 7 fin 0 acked 1 unack_data 0
+        sent_end 2632987379 reply_end 2633016339 max_win 28960
+        max_ack 2632987379
+ testpmd> set conntrack rply scale 7 fin 0 acked 1 unack_data 0
+        sent_end 2532480967 reply_end 2532546247 max_win 65280
+        max_ack 2532480967
+ testpmd> flow indirect_action 0 create ingress action conntrack / end
+ testpmd> flow create 0 group 3 ingress pattern eth / ipv4 / tcp / end actions indirect 0 / jump group 5 / end
+ testpmd> flow create 0 group 5 ingress pattern eth / ipv4 / tcp / conntrack is 1 / end actions queue index 5 / end
+
+Construct the conntrack again with only "is_orig" set to 0 (other fields are
+ignored), then use "update" interface to update the direction. Create flow
+rules like above for the peer port.
+
+::
+
+ testpmd> flow indirect_action 0 update 0 action conntrack_update dir / end
+
 BPF Functions
 --------------