diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index 4d9e038ce8..a318544fc6 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -13621,6 +13621,358 @@ 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_conntrack =
+	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_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 +17472,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..d82b08c609 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 c219ef25f7..02b7d4719a 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -1484,6 +1484,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));
@@ -1565,11 +1570,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);
 	}
@@ -1622,6 +1640,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)
 {
