diff --git a/lib/librte_flow_classify/rte_flow_classify.c b/lib/librte_flow_classify/rte_flow_classify.c
index e6f4486..c2200df 100644
--- a/lib/librte_flow_classify/rte_flow_classify.c
+++ b/lib/librte_flow_classify/rte_flow_classify.c
@@ -39,16 +39,20 @@
 
 int librte_flow_classify_logtype;
 
-static struct rte_eth_ntuple_filter ntuple_filter;
 static uint32_t unique_id = 1;
 
+enum rte_flow_classify_table_type table_type
+	= RTE_FLOW_CLASSIFY_TABLE_TYPE_NONE;
 
 struct rte_flow_classify_table_entry {
 	/* meta-data for classify rule */
 	uint32_t rule_id;
+
+	/* Flow action */
+	struct classify_action action;
 };
 
-struct rte_table {
+struct rte_cls_table {
 	/* Input parameters */
 	struct rte_table_ops ops;
 	uint32_t entry_size;
@@ -64,11 +68,16 @@ struct rte_flow_classifier {
 	/* Input parameters */
 	char name[RTE_FLOW_CLASSIFIER_MAX_NAME_SZ];
 	int socket_id;
-	enum rte_flow_classify_table_type type;
 
-	/* Internal tables */
-	struct rte_table tables[RTE_FLOW_CLASSIFY_TABLE_MAX];
+	/* Internal */
+	/* ntuple_fliter */
+	struct rte_eth_ntuple_filter ntuple_filter;
+
+	/* clasifier tables */
+	struct rte_cls_table tables[RTE_FLOW_CLASSIFY_TABLE_MAX];
+	uint32_t table_mask;
 	uint32_t num_tables;
+
 	uint16_t nb_pkts;
 	struct rte_flow_classify_table_entry
 		*entries[RTE_PORT_IN_BURST_SIZE_MAX];
@@ -97,18 +106,19 @@ struct classify_rules {
 
 struct rte_flow_classify_rule {
 	uint32_t id; /* unique ID of classify rule */
-	struct rte_flow_action action; /* action when match found */
+	enum rte_flow_classify_table_type tbl_type; /* rule table */
 	struct classify_rules rules; /* union of rules */
 	union {
 		struct acl_keys key;
 	} u;
 	int key_found;   /* rule key found in table */
-	void *entry;     /* pointer to buffer to hold rule meta data */
+	struct rte_flow_classify_table_entry entry;  /* rule meta data */
 	void *entry_ptr; /* handle to the table entry for rule meta data */
 };
 
-static int
-flow_classify_parse_flow(
+int
+rte_flow_classify_validate(
+		   struct rte_flow_classifier *cls,
 		   const struct rte_flow_attr *attr,
 		   const struct rte_flow_item pattern[],
 		   const struct rte_flow_action actions[],
@@ -120,7 +130,38 @@ flow_classify_parse_flow(
 	uint32_t i = 0;
 	int ret;
 
-	memset(&ntuple_filter, 0, sizeof(ntuple_filter));
+	if (error == NULL)
+		return -EINVAL;
+
+	if (cls == NULL) {
+		RTE_FLOW_CLASSIFY_LOG(ERR,
+			"%s: rte_flow_classifier parameter is NULL\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (!attr) {
+		rte_flow_error_set(error, EINVAL,
+				   RTE_FLOW_ERROR_TYPE_ATTR,
+				   NULL, "NULL attribute.");
+		return -EINVAL;
+	}
+
+	if (!pattern) {
+		rte_flow_error_set(error,
+			EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_NUM,
+			NULL, "NULL pattern.");
+		return -EINVAL;
+	}
+
+	if (!actions) {
+		rte_flow_error_set(error, EINVAL,
+				   RTE_FLOW_ERROR_TYPE_ACTION_NUM,
+				   NULL, "NULL action.");
+		return -EINVAL;
+	}
+
+	memset(&cls->ntuple_filter, 0, sizeof(cls->ntuple_filter));
 
 	/* Get the non-void item number of pattern */
 	while ((pattern + i)->type != RTE_FLOW_ITEM_TYPE_END) {
@@ -150,7 +191,7 @@ flow_classify_parse_flow(
 		return -EINVAL;
 	}
 
-	ret = parse_filter(attr, items, actions, &ntuple_filter, error);
+	ret = parse_filter(attr, items, actions, &cls->ntuple_filter, error);
 	free(items);
 	return ret;
 }
@@ -275,17 +316,14 @@ rte_flow_classifier_create(struct rte_flow_classifier_params *params)
 	/* Save input parameters */
 	snprintf(cls->name, RTE_FLOW_CLASSIFIER_MAX_NAME_SZ, "%s",
 			params->name);
-	cls->socket_id = params->socket_id;
-	cls->type = params->type;
 
-	/* Initialize flow classifier internal data structure */
-	cls->num_tables = 0;
+	cls->socket_id = params->socket_id;
 
 	return cls;
 }
 
 static void
-rte_flow_classify_table_free(struct rte_table *table)
+rte_flow_classify_table_free(struct rte_cls_table *table)
 {
 	if (table->ops.f_free != NULL)
 		table->ops.f_free(table->h_table);
@@ -306,7 +344,7 @@ rte_flow_classifier_free(struct rte_flow_classifier *cls)
 
 	/* Free tables */
 	for (i = 0; i < cls->num_tables; i++) {
-		struct rte_table *table = &cls->tables[i];
+		struct rte_cls_table *table = &cls->tables[i];
 
 		rte_flow_classify_table_free(table);
 	}
@@ -319,8 +357,7 @@ rte_flow_classifier_free(struct rte_flow_classifier *cls)
 
 static int
 rte_table_check_params(struct rte_flow_classifier *cls,
-		struct rte_flow_classify_table_params *params,
-		uint32_t *table_id)
+		struct rte_flow_classify_table_params *params)
 {
 	if (cls == NULL) {
 		RTE_FLOW_CLASSIFY_LOG(ERR,
@@ -333,11 +370,6 @@ rte_table_check_params(struct rte_flow_classifier *cls,
 			__func__);
 		return -EINVAL;
 	}
-	if (table_id == NULL) {
-		RTE_FLOW_CLASSIFY_LOG(ERR, "%s: table_id parameter is NULL\n",
-			__func__);
-		return -EINVAL;
-	}
 
 	/* ops */
 	if (params->ops == NULL) {
@@ -371,22 +403,18 @@ rte_table_check_params(struct rte_flow_classifier *cls,
 
 int
 rte_flow_classify_table_create(struct rte_flow_classifier *cls,
-	struct rte_flow_classify_table_params *params,
-	uint32_t *table_id)
+	struct rte_flow_classify_table_params *params)
 {
-	struct rte_table *table;
+	struct rte_cls_table *table;
 	void *h_table;
-	uint32_t entry_size, id;
+	uint32_t entry_size;
 	int ret;
 
 	/* Check input arguments */
-	ret = rte_table_check_params(cls, params, table_id);
+	ret = rte_table_check_params(cls, params);
 	if (ret != 0)
 		return ret;
 
-	id = cls->num_tables;
-	table = &cls->tables[id];
-
 	/* calculate table entry size */
 	entry_size = sizeof(struct rte_flow_classify_table_entry);
 
@@ -400,8 +428,9 @@ rte_flow_classify_table_create(struct rte_flow_classifier *cls,
 	}
 
 	/* Commit current table to the classifier */
+	table = &cls->tables[cls->num_tables];
+	table->type = params->type;
 	cls->num_tables++;
-	*table_id = id;
 
 	/* Save input parameters */
 	memcpy(&table->ops, params->ops, sizeof(struct rte_table_ops));
@@ -414,7 +443,7 @@ rte_flow_classify_table_create(struct rte_flow_classifier *cls,
 }
 
 static struct rte_flow_classify_rule *
-allocate_acl_ipv4_5tuple_rule(void)
+allocate_acl_ipv4_5tuple_rule(struct rte_flow_classifier *cls)
 {
 	struct rte_flow_classify_rule *rule;
 	int log_level;
@@ -427,45 +456,44 @@ allocate_acl_ipv4_5tuple_rule(void)
 	rule->id = unique_id++;
 	rule->rules.type = RTE_FLOW_CLASSIFY_RULE_TYPE_IPV4_5TUPLE;
 
-	memcpy(&rule->action, classify_get_flow_action(),
-	       sizeof(struct rte_flow_action));
-
 	/* key add values */
-	rule->u.key.key_add.priority = ntuple_filter.priority;
+	rule->u.key.key_add.priority = cls->ntuple_filter.priority;
 	rule->u.key.key_add.field_value[PROTO_FIELD_IPV4].mask_range.u8 =
-			ntuple_filter.proto_mask;
+			cls->ntuple_filter.proto_mask;
 	rule->u.key.key_add.field_value[PROTO_FIELD_IPV4].value.u8 =
-			ntuple_filter.proto;
-	rule->rules.u.ipv4_5tuple.proto = ntuple_filter.proto;
-	rule->rules.u.ipv4_5tuple.proto_mask = ntuple_filter.proto_mask;
+			cls->ntuple_filter.proto;
+	rule->rules.u.ipv4_5tuple.proto = cls->ntuple_filter.proto;
+	rule->rules.u.ipv4_5tuple.proto_mask = cls->ntuple_filter.proto_mask;
 
 	rule->u.key.key_add.field_value[SRC_FIELD_IPV4].mask_range.u32 =
-			ntuple_filter.src_ip_mask;
+			cls->ntuple_filter.src_ip_mask;
 	rule->u.key.key_add.field_value[SRC_FIELD_IPV4].value.u32 =
-			ntuple_filter.src_ip;
-	rule->rules.u.ipv4_5tuple.src_ip_mask = ntuple_filter.src_ip_mask;
-	rule->rules.u.ipv4_5tuple.src_ip = ntuple_filter.src_ip;
+			cls->ntuple_filter.src_ip;
+	rule->rules.u.ipv4_5tuple.src_ip_mask = cls->ntuple_filter.src_ip_mask;
+	rule->rules.u.ipv4_5tuple.src_ip = cls->ntuple_filter.src_ip;
 
 	rule->u.key.key_add.field_value[DST_FIELD_IPV4].mask_range.u32 =
-			ntuple_filter.dst_ip_mask;
+			cls->ntuple_filter.dst_ip_mask;
 	rule->u.key.key_add.field_value[DST_FIELD_IPV4].value.u32 =
-			ntuple_filter.dst_ip;
-	rule->rules.u.ipv4_5tuple.dst_ip_mask = ntuple_filter.dst_ip_mask;
-	rule->rules.u.ipv4_5tuple.dst_ip = ntuple_filter.dst_ip;
+			cls->ntuple_filter.dst_ip;
+	rule->rules.u.ipv4_5tuple.dst_ip_mask = cls->ntuple_filter.dst_ip_mask;
+	rule->rules.u.ipv4_5tuple.dst_ip = cls->ntuple_filter.dst_ip;
 
 	rule->u.key.key_add.field_value[SRCP_FIELD_IPV4].mask_range.u16 =
-			ntuple_filter.src_port_mask;
+			cls->ntuple_filter.src_port_mask;
 	rule->u.key.key_add.field_value[SRCP_FIELD_IPV4].value.u16 =
-			ntuple_filter.src_port;
-	rule->rules.u.ipv4_5tuple.src_port_mask = ntuple_filter.src_port_mask;
-	rule->rules.u.ipv4_5tuple.src_port = ntuple_filter.src_port;
+			cls->ntuple_filter.src_port;
+	rule->rules.u.ipv4_5tuple.src_port_mask =
+			cls->ntuple_filter.src_port_mask;
+	rule->rules.u.ipv4_5tuple.src_port = cls->ntuple_filter.src_port;
 
 	rule->u.key.key_add.field_value[DSTP_FIELD_IPV4].mask_range.u16 =
-			ntuple_filter.dst_port_mask;
+			cls->ntuple_filter.dst_port_mask;
 	rule->u.key.key_add.field_value[DSTP_FIELD_IPV4].value.u16 =
-			ntuple_filter.dst_port;
-	rule->rules.u.ipv4_5tuple.dst_port_mask = ntuple_filter.dst_port_mask;
-	rule->rules.u.ipv4_5tuple.dst_port = ntuple_filter.dst_port;
+			cls->ntuple_filter.dst_port;
+	rule->rules.u.ipv4_5tuple.dst_port_mask =
+			cls->ntuple_filter.dst_port_mask;
+	rule->rules.u.ipv4_5tuple.dst_port = cls->ntuple_filter.dst_port;
 
 	log_level = rte_log_get_level(librte_flow_classify_logtype);
 
@@ -485,34 +513,21 @@ allocate_acl_ipv4_5tuple_rule(void)
 
 struct rte_flow_classify_rule *
 rte_flow_classify_table_entry_add(struct rte_flow_classifier *cls,
-		uint32_t table_id,
-		int *key_found,
 		const struct rte_flow_attr *attr,
 		const struct rte_flow_item pattern[],
 		const struct rte_flow_action actions[],
+		int *key_found,
 		struct rte_flow_error *error)
 {
 	struct rte_flow_classify_rule *rule;
 	struct rte_flow_classify_table_entry *table_entry;
+	struct classify_action *action;
+	uint32_t i;
 	int ret;
 
 	if (!error)
 		return NULL;
 
-	if (!cls) {
-		rte_flow_error_set(error, EINVAL,
-				RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
-				NULL, "NULL classifier.");
-		return NULL;
-	}
-
-	if (table_id >= cls->num_tables) {
-		rte_flow_error_set(error, EINVAL,
-				RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
-				NULL, "invalid table_id.");
-		return NULL;
-	}
-
 	if (key_found == NULL) {
 		rte_flow_error_set(error, EINVAL,
 				RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
@@ -520,91 +535,95 @@ rte_flow_classify_table_entry_add(struct rte_flow_classifier *cls,
 		return NULL;
 	}
 
-	if (!pattern) {
-		rte_flow_error_set(error, EINVAL,
-				RTE_FLOW_ERROR_TYPE_ITEM_NUM,
-				NULL, "NULL pattern.");
-		return NULL;
-	}
-
-	if (!actions) {
-		rte_flow_error_set(error, EINVAL,
-				RTE_FLOW_ERROR_TYPE_ACTION_NUM,
-				NULL, "NULL action.");
-		return NULL;
-	}
-
-	if (!attr) {
-		rte_flow_error_set(error, EINVAL,
-				RTE_FLOW_ERROR_TYPE_ATTR,
-				NULL, "NULL attribute.");
-		return NULL;
-	}
-
 	/* parse attr, pattern and actions */
-	ret = flow_classify_parse_flow(attr, pattern, actions, error);
+	ret = rte_flow_classify_validate(cls, attr, pattern, actions, error);
 	if (ret < 0)
 		return NULL;
 
-	switch (cls->type) {
-	case RTE_FLOW_CLASSIFY_TABLE_TYPE_ACL:
-		rule = allocate_acl_ipv4_5tuple_rule();
+	switch (table_type) {
+	case RTE_FLOW_CLASSIFY_TABLE_ACL_IP4_5TUPLE:
+		rule = allocate_acl_ipv4_5tuple_rule(cls);
 		if (!rule)
 			return NULL;
+		rule->tbl_type = table_type;
+		cls->table_mask |= table_type;
 		break;
 	default:
 		return NULL;
 	}
 
-	rule->entry = malloc(sizeof(struct rte_flow_classify_table_entry));
-	if (!rule->entry) {
-		free(rule);
-		return NULL;
-	}
-
-	table_entry = rule->entry;
+	action = classify_get_flow_action();
+	table_entry = &rule->entry;
 	table_entry->rule_id = rule->id;
+	table_entry->action.action_mask = action->action_mask;
 
-	if (cls->tables[table_id].ops.f_add != NULL) {
-		ret = cls->tables[table_id].ops.f_add(
-			cls->tables[table_id].h_table,
-			&rule->u.key.key_add,
-			rule->entry,
-			&rule->key_found,
-			&rule->entry_ptr);
-		if (ret) {
-			free(rule->entry);
-			free(rule);
-			return NULL;
+	/* Copy actions */
+	if (action->action_mask & (1LLU << RTE_FLOW_ACTION_TYPE_COUNT)) {
+		memcpy(&table_entry->action.act.counter, &action->act.counter,
+				sizeof(table_entry->action.act.counter));
+	}
+	if (action->action_mask & (1LLU << RTE_FLOW_ACTION_TYPE_MARK)) {
+		memcpy(&table_entry->action.act.mark, &action->act.mark,
+				sizeof(table_entry->action.act.mark));
+	}
+
+	for (i = 0; i < cls->num_tables; i++) {
+		struct rte_cls_table *table = &cls->tables[i];
+
+		if (table->type == table_type) {
+			if (table->ops.f_add != NULL) {
+				ret = table->ops.f_add(
+					table->h_table,
+					&rule->u.key.key_add,
+					&rule->entry,
+					&rule->key_found,
+					&rule->entry_ptr);
+				if (ret) {
+					free(rule);
+					return NULL;
+				}
+
+			*key_found = rule->key_found;
+			}
+
+			return rule;
 		}
-		*key_found = rule->key_found;
 	}
-	return rule;
+	return NULL;
 }
 
 int
 rte_flow_classify_table_entry_delete(struct rte_flow_classifier *cls,
-		uint32_t table_id,
 		struct rte_flow_classify_rule *rule)
 {
+	uint32_t i;
 	int ret = -EINVAL;
 
-	if (!cls || !rule || table_id >= cls->num_tables)
+	if (!cls || !rule)
 		return ret;
+	enum rte_flow_classify_table_type tbl_type = rule->tbl_type;
+
+	for (i = 0; i < cls->num_tables; i++) {
+		struct rte_cls_table *table = &cls->tables[i];
 
-	if (cls->tables[table_id].ops.f_delete != NULL)
-		ret = cls->tables[table_id].ops.f_delete(
-			cls->tables[table_id].h_table,
-			&rule->u.key.key_del,
-			&rule->key_found,
-			&rule->entry);
+		if (table->type == tbl_type) {
+			if (table->ops.f_delete != NULL) {
+				ret = table->ops.f_delete(table->h_table,
+						&rule->u.key.key_del,
+						&rule->key_found,
+						&rule->entry);
 
+				return ret;
+			}
+		}
+	}
+	free(rule);
 	return ret;
 }
 
 static int
 flow_classifier_lookup(struct rte_flow_classifier *cls,
-		uint32_t table_id,
+		struct rte_cls_table *table,
 		struct rte_mbuf **pkts,
 		const uint16_t nb_pkts)
 {
@@ -613,8 +632,7 @@ flow_classifier_lookup(struct rte_flow_classifier *cls,
 	uint64_t lookup_hit_mask;
 
 	pkts_mask = RTE_LEN2MASK(nb_pkts, uint64_t);
-	ret = cls->tables[table_id].ops.f_lookup(
-		cls->tables[table_id].h_table,
+	ret = table->ops.f_lookup(table->h_table,
 		pkts, pkts_mask, &lookup_hit_mask,
 		(void **)cls->entries);
 
@@ -632,12 +650,12 @@ action_apply(struct rte_flow_classifier *cls,
 		struct rte_flow_classify_stats *stats)
 {
 	struct rte_flow_classify_ipv4_5tuple_stats *ntuple_stats;
+	struct rte_flow_classify_table_entry *entry = &rule->entry;
 	uint64_t count = 0;
-	int i;
-	int ret = -EINVAL;
+	uint32_t action_mask = entry->action.action_mask;
+	int i, ret = -EINVAL;
 
-	switch (rule->action.type) {
-	case RTE_FLOW_ACTION_TYPE_COUNT:
+	if (action_mask & (1LLU << RTE_FLOW_ACTION_TYPE_COUNT)) {
 		for (i = 0; i < cls->nb_pkts; i++) {
 			if (rule->id == cls->entries[i]->rule_id)
 				count++;
@@ -650,32 +668,37 @@ action_apply(struct rte_flow_classifier *cls,
 			ntuple_stats->counter1 = count;
 			ntuple_stats->ipv4_5tuple = rule->rules.u.ipv4_5tuple;
 		}
-		break;
-	default:
-		ret = -ENOTSUP;
-		break;
 	}
-
 	return ret;
 }
 
 int
 rte_flow_classifier_query(struct rte_flow_classifier *cls,
-		uint32_t table_id,
 		struct rte_mbuf **pkts,
 		const uint16_t nb_pkts,
 		struct rte_flow_classify_rule *rule,
 		struct rte_flow_classify_stats *stats)
 {
+	enum rte_flow_classify_table_type tbl_type;
+	uint32_t i;
 	int ret = -EINVAL;
 
-	if (!cls || !rule || !stats || !pkts  || nb_pkts == 0 ||
-		table_id >= cls->num_tables)
+	if (!cls || !rule || !stats || !pkts  || nb_pkts == 0)
 		return ret;
 
-	ret = flow_classifier_lookup(cls, table_id, pkts, nb_pkts);
-	if (!ret)
-		ret = action_apply(cls, rule, stats);
+	tbl_type = rule->tbl_type;
+	for (i = 0; i < cls->num_tables; i++) {
+		struct rte_cls_table *table = &cls->tables[i];
+
+			if (table->type == tbl_type) {
+				ret = flow_classifier_lookup(cls, table,
+						pkts, nb_pkts);
+				if (!ret) {
+					ret = action_apply(cls, rule, stats);
+					return ret;
+				}
+			}
+	}
 	return ret;
 }
 
diff --git a/lib/librte_flow_classify/rte_flow_classify.h b/lib/librte_flow_classify/rte_flow_classify.h
index 1211873..b9b669f 100644
--- a/lib/librte_flow_classify/rte_flow_classify.h
+++ b/lib/librte_flow_classify/rte_flow_classify.h
@@ -86,6 +86,10 @@ extern int librte_flow_classify_logtype;
 rte_log(RTE_LOG_ ## level, librte_flow_classify_logtype, "%s(): " fmt, \
 	__func__, ## args)
 
+#ifndef RTE_FLOW_CLASSIFY_TABLE_MAX
+#define RTE_FLOW_CLASSIFY_TABLE_MAX		32
+#endif
+
 /** Opaque data type for flow classifier */
 struct rte_flow_classifier;
 
@@ -102,17 +106,16 @@ enum rte_flow_classify_rule_type {
 
 /** Flow classify table type */
 enum rte_flow_classify_table_type {
-	/** no type */
-	RTE_FLOW_CLASSIFY_TABLE_TYPE_NONE,
-	/** ACL type */
-	RTE_FLOW_CLASSIFY_TABLE_TYPE_ACL,
-};
+	/** No type */
+	RTE_FLOW_CLASSIFY_TABLE_TYPE_NONE = 1 << 0,
+	/** ACL IP4 5TUPLE */
+	RTE_FLOW_CLASSIFY_TABLE_ACL_IP4_5TUPLE = 1 << 1,
+	/** ACL VLAN IP4 5TUPLE */
+	RTE_FLOW_CLASSIFY_TABLE_ACL_VLAN_IP4_5TUPLE = 1 << 2,
+	/** ACL QinQ IP4 5TUPLE */
+	RTE_FLOW_CLASSIFY_TABLE_ACL_QINQ_IP4_5TUPLE = 1 << 3,
 
-/**
- * Maximum number of tables allowed for any Flow Classifier instance.
- * The value of this parameter cannot be changed.
- */
-#define RTE_FLOW_CLASSIFY_TABLE_MAX  64
+};
 
 /** Parameters for flow classifier creation */
 struct rte_flow_classifier_params {
@@ -122,9 +125,6 @@ struct rte_flow_classifier_params {
 	/** CPU socket ID where memory for the flow classifier and its */
 	/** elements (tables) should be allocated */
 	int socket_id;
-
-	/** Table type */
-	enum rte_flow_classify_table_type type;
 };
 
 /** Parameters for table creation */
@@ -134,6 +134,9 @@ struct rte_flow_classify_table_params {
 
 	/** Opaque param to be passed to the table create operation */
 	void *arg_create;
+
+	/** Classifier table type */
+	enum rte_flow_classify_table_type type;
 };
 
 /** IPv4 5-tuple data */
@@ -197,32 +200,50 @@ rte_flow_classifier_free(struct rte_flow_classifier *cls);
  *   Handle to flow classifier instance
  * @param params
  *   Parameters for flow_classify table creation
- * @param table_id
- *   Table ID. Valid only within the scope of table IDs of the current
- *   classifier. Only returned after a successful invocation.
  * @return
  *   0 on success, error code otherwise
  */
 int
 rte_flow_classify_table_create(struct rte_flow_classifier *cls,
-		struct rte_flow_classify_table_params *params,
-		uint32_t *table_id);
+		struct rte_flow_classify_table_params *params);
+
+/**
+ * Flow classify validate
+ *
+ * @param cls
+ *   Handle to flow classifier instance
+ * @param[in] attr
+ *   Flow rule attributes
+ * @param[in] pattern
+ *   Pattern specification (list terminated by the END pattern item).
+ * @param[in] actions
+ *   Associated actions (list terminated by the END pattern item).
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL. Structure
+ *   initialised in case of error only.
+ * @return
+ *   0 on success, error code otherwise
+ */
+int
+rte_flow_classify_validate(struct rte_flow_classifier *cls,
+		const struct rte_flow_attr *attr,
+		const struct rte_flow_item pattern[],
+		const struct rte_flow_action actions[],
+		struct rte_flow_error *error);
 
 /**
  * Add a flow classify rule to the flow_classifer table.
  *
  * @param[in] cls
  *   Flow classifier handle
- * @param[in] table_id
- *   id of table
- * @param[out] key_found
- *  returns 1 if key present already, 0 otherwise.
  * @param[in] attr
  *   Flow rule attributes
  * @param[in] pattern
  *   Pattern specification (list terminated by the END pattern item).
  * @param[in] actions
  *   Associated actions (list terminated by the END pattern item).
+ * @param[out] key_found
+ *  returns 1 if rule present already, 0 otherwise.
  * @param[out] error
  *   Perform verbose error reporting if not NULL. Structure
  *   initialised in case of error only.
@@ -231,11 +252,10 @@ rte_flow_classify_table_create(struct rte_flow_classifier *cls,
  */
 struct rte_flow_classify_rule *
 rte_flow_classify_table_entry_add(struct rte_flow_classifier *cls,
-		uint32_t table_id,
-		int *key_found,
 		const struct rte_flow_attr *attr,
 		const struct rte_flow_item pattern[],
 		const struct rte_flow_action actions[],
+		int *key_found,
 		struct rte_flow_error *error);
 
 /**
@@ -243,8 +263,6 @@ rte_flow_classify_table_entry_add(struct rte_flow_classifier *cls,
  *
  * @param[in] cls
  *   Flow classifier handle
- * @param[in] table_id
- *   id of table
  * @param[in] rule
  *   Flow classify rule
  * @return
@@ -252,7 +270,6 @@ rte_flow_classify_table_entry_add(struct rte_flow_classifier *cls,
  */
 int
 rte_flow_classify_table_entry_delete(struct rte_flow_classifier *cls,
-		uint32_t table_id,
 		struct rte_flow_classify_rule *rule);
 
 /**
@@ -260,8 +277,6 @@ rte_flow_classify_table_entry_delete(struct rte_flow_classifier *cls,
  *
  * @param[in] cls
  *   Flow classifier handle
- * @param[in] table_id
- *   id of table
  * @param[in] pkts
  *   Pointer to packets to process
  * @param[in] nb_pkts
@@ -276,7 +291,6 @@ rte_flow_classify_table_entry_delete(struct rte_flow_classifier *cls,
  */
 int
 rte_flow_classifier_query(struct rte_flow_classifier *cls,
-		uint32_t table_id,
 		struct rte_mbuf **pkts,
 		const uint16_t nb_pkts,
 		struct rte_flow_classify_rule *rule,
diff --git a/lib/librte_flow_classify/rte_flow_classify_parse.c b/lib/librte_flow_classify/rte_flow_classify_parse.c
index dbfa111..9fb3e51 100644
--- a/lib/librte_flow_classify/rte_flow_classify_parse.c
+++ b/lib/librte_flow_classify/rte_flow_classify_parse.c
@@ -40,7 +40,7 @@ struct classify_valid_pattern {
 	parse_filter_t parse_filter;
 };
 
-static struct rte_flow_action action;
+static struct classify_action action;
 
 /* Pattern for IPv4 5-tuple UDP filter */
 static enum rte_flow_item_type pattern_ntuple_1[] = {
@@ -80,7 +80,7 @@ static struct classify_valid_pattern classify_supported_patterns[] = {
 	{ pattern_ntuple_3, classify_parse_ntuple_filter },
 };
 
-struct rte_flow_action *
+struct classify_action *
 classify_get_flow_action(void)
 {
 	return &action;
@@ -244,28 +244,10 @@ classify_parse_ntuple_filter(const struct rte_flow_attr *attr,
 	const struct rte_flow_item_udp *udp_mask;
 	const struct rte_flow_item_sctp *sctp_spec;
 	const struct rte_flow_item_sctp *sctp_mask;
+	const struct rte_flow_action_count *count;
+	const struct rte_flow_action_mark *mark_spec;
 	uint32_t index;
 
-	if (!pattern) {
-		rte_flow_error_set(error,
-			EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_NUM,
-			NULL, "NULL pattern.");
-		return -EINVAL;
-	}
-
-	if (!actions) {
-		rte_flow_error_set(error, EINVAL,
-				   RTE_FLOW_ERROR_TYPE_ACTION_NUM,
-				   NULL, "NULL action.");
-		return -EINVAL;
-	}
-	if (!attr) {
-		rte_flow_error_set(error, EINVAL,
-				   RTE_FLOW_ERROR_TYPE_ATTR,
-				   NULL, "NULL attribute.");
-		return -EINVAL;
-	}
-
 	/* parse pattern */
 	index = 0;
 
@@ -483,34 +465,7 @@ classify_parse_ntuple_filter(const struct rte_flow_attr *attr,
 		return -EINVAL;
 	}
 
-	/* parse action */
-	index = 0;
-
-	/**
-	 * n-tuple only supports count,
-	 * check if the first not void action is COUNT.
-	 */
-	memset(&action, 0, sizeof(action));
-	NEXT_ITEM_OF_ACTION(act, actions, index);
-	if (act->type != RTE_FLOW_ACTION_TYPE_COUNT) {
-		memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
-		rte_flow_error_set(error, EINVAL,
-			RTE_FLOW_ERROR_TYPE_ACTION,
-			item, "Not supported action.");
-		return -EINVAL;
-	}
-	action.type = RTE_FLOW_ACTION_TYPE_COUNT;
-
-	/* check if the next not void item is END */
-	index++;
-	NEXT_ITEM_OF_ACTION(act, actions, index);
-	if (act->type != RTE_FLOW_ACTION_TYPE_END) {
-		memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
-		rte_flow_error_set(error, EINVAL,
-			RTE_FLOW_ERROR_TYPE_ACTION,
-			act, "Not supported action.");
-		return -EINVAL;
-	}
+	table_type = RTE_FLOW_CLASSIFY_TABLE_ACL_IP4_5TUPLE;
 
 	/* parse attr */
 	/* must be input direction */
@@ -542,5 +497,68 @@ classify_parse_ntuple_filter(const struct rte_flow_attr *attr,
 	if (attr->priority >  FLOW_RULE_MIN_PRIORITY)
 		filter->priority = FLOW_RULE_MAX_PRIORITY;
 
+	/* parse action */
+	index = 0;
+
+	/**
+	 * n-tuple only supports count and Mark,
+	 * check if the first not void action is COUNT or MARK.
+	 */
+	memset(&action, 0, sizeof(action));
+	NEXT_ITEM_OF_ACTION(act, actions, index);
+	switch (act->type) {
+	case RTE_FLOW_ACTION_TYPE_COUNT:
+		action.action_mask |= 1LLU << RTE_FLOW_ACTION_TYPE_COUNT;
+		count = (const struct rte_flow_action_count *)act->conf;
+		memcpy(&action.act.counter, count, sizeof(action.act.counter));
+		break;
+	case RTE_FLOW_ACTION_TYPE_MARK:
+		action.action_mask |= 1LLU << RTE_FLOW_ACTION_TYPE_MARK;
+		mark_spec = (const struct rte_flow_action_mark *)act->conf;
+		memcpy(&action.act.mark, mark_spec, sizeof(action.act.mark));
+		break;
+	default:
+		memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
+		rte_flow_error_set(error, EINVAL,
+		   RTE_FLOW_ERROR_TYPE_ACTION, act,
+		   "Invalid action.");
+		return -EINVAL;
+	}
+
+	/* check if the next not void item is MARK or COUNT or END */
+	index++;
+	NEXT_ITEM_OF_ACTION(act, actions, index);
+	switch (act->type) {
+	case RTE_FLOW_ACTION_TYPE_COUNT:
+		action.action_mask |= 1LLU << RTE_FLOW_ACTION_TYPE_COUNT;
+		count = (const struct rte_flow_action_count *)act->conf;
+		memcpy(&action.act.counter, count, sizeof(action.act.counter));
+		break;
+	case RTE_FLOW_ACTION_TYPE_MARK:
+		action.action_mask |= 1LLU << RTE_FLOW_ACTION_TYPE_MARK;
+		mark_spec = (const struct rte_flow_action_mark *)act->conf;
+		memcpy(&action.act.mark, mark_spec, sizeof(action.act.mark));
+		break;
+	case RTE_FLOW_ACTION_TYPE_END:
+		return 0;
+	default:
+		memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
+		rte_flow_error_set(error, EINVAL,
+		   RTE_FLOW_ERROR_TYPE_ACTION, act,
+		   "Invalid action.");
+		return -EINVAL;
+	}
+
+	/* check if the next not void item is END */
+	index++;
+	NEXT_ITEM_OF_ACTION(act, actions, index);
+	if (act->type != RTE_FLOW_ACTION_TYPE_END) {
+		memset(filter, 0, sizeof(struct rte_eth_ntuple_filter));
+		rte_flow_error_set(error, EINVAL,
+		   RTE_FLOW_ERROR_TYPE_ACTION, act,
+		   "Invalid action.");
+		return -EINVAL;
+	}
+
 	return 0;
 }
diff --git a/lib/librte_flow_classify/rte_flow_classify_parse.h b/lib/librte_flow_classify/rte_flow_classify_parse.h
index 1d4708a..9c1de72 100644
--- a/lib/librte_flow_classify/rte_flow_classify_parse.h
+++ b/lib/librte_flow_classify/rte_flow_classify_parse.h
@@ -43,6 +43,20 @@
 extern "C" {
 #endif
 
+extern enum rte_flow_classify_table_type table_type;
+
+struct classify_action {
+	/* Flow action mask */
+	uint64_t action_mask;
+
+	struct action {
+		/** Integer value to return with packets */
+		struct rte_flow_action_mark mark;
+		/** Flow rule counter */
+		struct rte_flow_query_count counter;
+	} act;
+};
+
 typedef int (*parse_filter_t)(const struct rte_flow_attr *attr,
 			      const struct rte_flow_item pattern[],
 			      const struct rte_flow_action actions[],
@@ -64,7 +78,7 @@ parse_filter_t
 classify_find_parse_filter_func(struct rte_flow_item *pattern);
 
 /* get action data */
-struct rte_flow_action *
+struct classify_action *
 classify_get_flow_action(void);
 
 #ifdef __cplusplus
diff --git a/lib/librte_flow_classify/rte_flow_classify_version.map b/lib/librte_flow_classify/rte_flow_classify_version.map
index f7695cb..49bc25c 100644
--- a/lib/librte_flow_classify/rte_flow_classify_version.map
+++ b/lib/librte_flow_classify/rte_flow_classify_version.map
@@ -7,6 +7,7 @@ EXPERIMENTAL {
 	rte_flow_classify_table_create;
 	rte_flow_classify_table_entry_add;
 	rte_flow_classify_table_entry_delete;
+	rte_flow_classify_validate;
 
 	local: *;
 };
