diff --git a/examples/ip_pipeline/cli.c b/examples/ip_pipeline/cli.c
index 65600b7..5011cad 100644
--- a/examples/ip_pipeline/cli.c
+++ b/examples/ip_pipeline/cli.c
@@ -4523,7 +4523,7 @@ cmd_pipeline_table_rule_add_default(char **tokens,
 
 
 static const char cmd_pipeline_table_rule_add_bulk_help[] =
-"pipeline <pipeline_name> table <table_id> rule add bulk <file_name> <n_rules>\n"
+"pipeline <pipeline_name> table <table_id> rule add bulk <file_name>\n"
 "\n"
 "  File <file_name>:\n"
 "  - line format: match <match> action <action>\n";
@@ -4531,8 +4531,7 @@ static const char cmd_pipeline_table_rule_add_bulk_help[] =
 static int
 cli_rule_file_process(const char *file_name,
 	size_t line_len_max,
-	struct table_rule_match *m,
-	struct table_rule_action *a,
+	struct table_rule_list **rule_list,
 	uint32_t *n_rules,
 	uint32_t *line_number,
 	char *out,
@@ -4544,14 +4543,12 @@ cmd_pipeline_table_rule_add_bulk(char **tokens,
 	char *out,
 	size_t out_size)
 {
-	struct table_rule_match *match;
-	struct table_rule_action *action;
-	void **data;
+	struct table_rule_list *list = NULL;
 	char *pipeline_name, *file_name;
-	uint32_t table_id, n_rules, n_rules_parsed, line_number;
+	uint32_t table_id, n_rules, n_rules_added, n_rules_not_added, line_number;
 	int status;
 
-	if (n_tokens != 9) {
+	if (n_tokens != 8) {
 		snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
 		return;
 	}
@@ -4585,68 +4582,33 @@ cmd_pipeline_table_rule_add_bulk(char **tokens,
 
 	file_name = tokens[7];
 
-	if ((parser_read_uint32(&n_rules, tokens[8]) != 0) ||
-		(n_rules == 0)) {
-		snprintf(out, out_size, MSG_ARG_INVALID, "n_rules");
-		return;
-	}
-
-	/* Memory allocation. */
-	match = calloc(n_rules, sizeof(struct table_rule_match));
-	action = calloc(n_rules, sizeof(struct table_rule_action));
-	data = calloc(n_rules, sizeof(void *));
-	if ((match == NULL) || (action == NULL) || (data == NULL)) {
-		snprintf(out, out_size, MSG_OUT_OF_MEMORY);
-		free(data);
-		free(action);
-		free(match);
-		return;
-	}
-
-	/* Load rule file */
-	n_rules_parsed = n_rules;
+	/* Load rules from file. */
 	status = cli_rule_file_process(file_name,
 		1024,
-		match,
-		action,
-		&n_rules_parsed,
+		&list,
+		&n_rules,
 		&line_number,
 		out,
 		out_size);
 	if (status) {
 		snprintf(out, out_size, MSG_FILE_ERR, file_name, line_number);
-		free(data);
-		free(action);
-		free(match);
-		return;
-	}
-	if (n_rules_parsed != n_rules) {
-		snprintf(out, out_size, MSG_FILE_NOT_ENOUGH, file_name);
-		free(data);
-		free(action);
-		free(match);
 		return;
 	}
 
 	/* Rule bulk add */
 	status = pipeline_table_rule_add_bulk(pipeline_name,
 		table_id,
-		match,
-		action,
-		data,
-		&n_rules);
+		list,
+		&n_rules_added,
+		&n_rules_not_added);
 	if (status) {
 		snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]);
-		free(data);
-		free(action);
-		free(match);
 		return;
 	}
 
-	/* Memory free */
-	free(data);
-	free(action);
-	free(match);
+	snprintf(out, out_size, "Added %u rules out of %u.\n",
+		n_rules_added,
+		n_rules);
 }
 
 
@@ -5950,44 +5912,56 @@ cli_script_process(const char *file_name,
 static int
 cli_rule_file_process(const char *file_name,
 	size_t line_len_max,
-	struct table_rule_match *m,
-	struct table_rule_action *a,
+	struct table_rule_list **rule_list,
 	uint32_t *n_rules,
 	uint32_t *line_number,
 	char *out,
 	size_t out_size)
 {
-	FILE *f = NULL;
+	struct table_rule_list *list = NULL;
 	char *line = NULL;
-	uint32_t rule_id, line_id;
+	FILE *f = NULL;
+	uint32_t rule_id = 0, line_id = 0;
 	int status = 0;
 
 	/* Check input arguments */
 	if ((file_name == NULL) ||
 		(strlen(file_name) == 0) ||
-		(line_len_max == 0)) {
-		*line_number = 0;
-		return -EINVAL;
+		(line_len_max == 0) ||
+		(rule_list == NULL) ||
+		(n_rules == NULL) ||
+		(line_number == NULL) ||
+		(out == NULL)) {
+		status = -EINVAL;
+		goto cli_rule_file_process_free;
 	}
 
 	/* Memory allocation */
+	list = malloc(sizeof(struct table_rule_list));
+	if (list == NULL) {
+		status = -ENOMEM;
+		goto cli_rule_file_process_free;
+	}
+
+	TAILQ_INIT(list);
+
 	line = malloc(line_len_max + 1);
 	if (line == NULL) {
-		*line_number = 0;
-		return -ENOMEM;
+		status = -ENOMEM;
+		goto cli_rule_file_process_free;
 	}
 
 	/* Open file */
 	f = fopen(file_name, "r");
 	if (f == NULL) {
-		*line_number = 0;
-		free(line);
-		return -EIO;
+		status = -EIO;
+		goto cli_rule_file_process_free;
 	}
 
 	/* Read file */
-	for (line_id = 1, rule_id = 0; rule_id < *n_rules; line_id++) {
+	for (line_id = 1, rule_id = 0; ; line_id++) {
 		char *tokens[CMD_MAX_TOKENS];
+		struct table_rule *rule = NULL;
 		uint32_t n_tokens, n_tokens_parsed, t0;
 
 		/* Read next line from file. */
@@ -6003,7 +5977,7 @@ cli_rule_file_process(const char *file_name,
 		status = parse_tokenize_string(line, tokens, &n_tokens);
 		if (status) {
 			status = -EINVAL;
-			break;
+			goto cli_rule_file_process_free;
 		}
 
 		/* Empty line. */
@@ -6011,15 +5985,24 @@ cli_rule_file_process(const char *file_name,
 			continue;
 		t0 = 0;
 
+		/* Rule alloc and insert. */
+		rule = calloc(1, sizeof(struct table_rule));
+		if (rule == NULL) {
+			status = -ENOMEM;
+			goto cli_rule_file_process_free;
+		}
+
+		TAILQ_INSERT_TAIL(list, rule, node);
+
 		/* Rule match. */
 		n_tokens_parsed = parse_match(tokens + t0,
 			n_tokens - t0,
 			out,
 			out_size,
-			&m[rule_id]);
+			&rule->match);
 		if (n_tokens_parsed == 0) {
 			status = -EINVAL;
-			break;
+			goto cli_rule_file_process_free;
 		}
 		t0 += n_tokens_parsed;
 
@@ -6028,17 +6011,17 @@ cli_rule_file_process(const char *file_name,
 			n_tokens - t0,
 			out,
 			out_size,
-			&a[rule_id]);
+			&rule->action);
 		if (n_tokens_parsed == 0) {
 			status = -EINVAL;
-			break;
+			goto cli_rule_file_process_free;
 		}
 		t0 += n_tokens_parsed;
 
 		/* Line completed. */
 		if (t0 < n_tokens) {
 			status = -EINVAL;
-			break;
+			goto cli_rule_file_process_free;
 		}
 
 		/* Increment rule count */
@@ -6051,7 +6034,31 @@ cli_rule_file_process(const char *file_name,
 	/* Memory free */
 	free(line);
 
+	*rule_list = list;
+	*n_rules = rule_id;
+	*line_number = line_id;
+	return 0;
+
+cli_rule_file_process_free:
+	*rule_list = NULL;
 	*n_rules = rule_id;
 	*line_number = line_id;
+
+	for ( ; ; ) {
+		struct table_rule *rule;
+
+		rule = TAILQ_FIRST(list);
+		if (rule == NULL)
+			break;
+
+		TAILQ_REMOVE(list, rule, node);
+		free(rule);
+	}
+
+	if (f)
+		fclose(f);
+	free(line);
+	free(list);
+
 	return status;
 }
diff --git a/examples/ip_pipeline/pipeline.h b/examples/ip_pipeline/pipeline.h
index 58067ed..8a364a9 100644
--- a/examples/ip_pipeline/pipeline.h
+++ b/examples/ip_pipeline/pipeline.h
@@ -334,10 +334,9 @@ pipeline_table_rule_add(const char *pipeline_name,
 int
 pipeline_table_rule_add_bulk(const char *pipeline_name,
 	uint32_t table_id,
-	struct table_rule_match *match,
-	struct table_rule_action *action,
-	void **data,
-	uint32_t *n_rules);
+	struct table_rule_list *list,
+	uint32_t *n_rules_added,
+	uint32_t *n_rules_not_added);
 
 int
 pipeline_table_rule_add_default(const char *pipeline_name,
diff --git a/examples/ip_pipeline/thread.c b/examples/ip_pipeline/thread.c
index 5019de9..2c0570f 100644
--- a/examples/ip_pipeline/thread.c
+++ b/examples/ip_pipeline/thread.c
@@ -609,10 +609,7 @@ struct pipeline_msg_req_table_rule_add_default {
 };
 
 struct pipeline_msg_req_table_rule_add_bulk {
-	struct table_rule_match *match;
-	struct table_rule_action *action;
-	void **data;
-	uint32_t n_rules;
+	struct table_rule_list *list;
 	int bulk;
 };
 
@@ -1206,6 +1203,128 @@ action_convert(struct rte_table_action *a,
 	struct table_rule_action *action,
 	struct rte_pipeline_table_entry *data);
 
+struct table_ll {
+	struct rte_pipeline *p;
+	int table_id;
+	struct rte_table_action *a;
+	int bulk_supported;
+};
+
+static int
+table_rule_add_bulk_ll(struct table_ll *table,
+	struct table_rule_list *list,
+	uint32_t *n_rules)
+{
+	union table_rule_match_low_level *match_ll = NULL;
+	uint8_t *action_ll = NULL;
+	void **match_ll_ptr = NULL;
+	struct rte_pipeline_table_entry **action_ll_ptr = NULL;
+	struct rte_pipeline_table_entry **entries_ptr = NULL;
+	int *found = NULL;
+	struct table_rule *rule;
+	uint32_t n, i;
+	int status = 0;
+
+	n = 0;
+	TAILQ_FOREACH(rule, list, node)
+		n++;
+
+	/* Memory allocation */
+	match_ll = calloc(n, sizeof(union table_rule_match_low_level));
+	action_ll = calloc(n, TABLE_RULE_ACTION_SIZE_MAX);
+
+	match_ll_ptr = calloc(n, sizeof(void *));
+	action_ll_ptr = calloc(n, sizeof(struct rte_pipeline_table_entry *));
+
+	entries_ptr = calloc(n, sizeof(struct rte_pipeline_table_entry *));
+	found = calloc(n, sizeof(int));
+
+	if (match_ll == NULL ||
+		action_ll == NULL ||
+		match_ll_ptr == NULL ||
+		action_ll_ptr == NULL ||
+		entries_ptr == NULL ||
+		found == NULL) {
+			status = -ENOMEM;
+			goto table_rule_add_bulk_ll_free;
+	}
+
+	/* Init */
+	for (i = 0; i < n; i++) {
+		match_ll_ptr[i] = (void *)&match_ll[i];
+		action_ll_ptr[i] = (struct rte_pipeline_table_entry *)
+			&action_ll[i * TABLE_RULE_ACTION_SIZE_MAX];
+	}
+
+	/* Rule (match, action) conversion */
+	i = 0;
+	TAILQ_FOREACH(rule, list, node) {
+		status = match_convert(&rule->match, match_ll_ptr[i], 1);
+		if (status)
+			goto table_rule_add_bulk_ll_free;
+
+		status = action_convert(table->a, &rule->action, action_ll_ptr[i]);
+		if (status)
+			goto table_rule_add_bulk_ll_free;
+
+		i++;
+	}
+
+	/* Add rule (match, action) to table */
+	if (table->bulk_supported) {
+		status = rte_pipeline_table_entry_add_bulk(table->p,
+			table->table_id,
+			match_ll_ptr,
+			action_ll_ptr,
+			n,
+			found,
+			entries_ptr);
+		if (status)
+			goto table_rule_add_bulk_ll_free;
+	} else
+		for (i = 0; i < n; i++) {
+			status = rte_pipeline_table_entry_add(table->p,
+				table->table_id,
+				match_ll_ptr[i],
+				action_ll_ptr[i],
+				&found[i],
+				&entries_ptr[i]);
+			if (status) {
+				if (i == 0)
+					goto table_rule_add_bulk_ll_free;
+
+				/* No roll-back. */
+				status = 0;
+				n = i;
+				break;
+			}
+		}
+
+	/* Write back to the rule list. */
+	i = 0;
+	TAILQ_FOREACH(rule, list, node) {
+		if (i >= n)
+			break;
+
+		rule->data = entries_ptr[i];
+
+		i++;
+	}
+
+	*n_rules = n;
+
+	/* Free */
+table_rule_add_bulk_ll_free:
+	free(found);
+	free(entries_ptr);
+	free(action_ll_ptr);
+	free(match_ll_ptr);
+	free(action_ll);
+	free(match_ll);
+
+	return status;
+}
+
 int
 pipeline_table_rule_add(const char *pipeline_name,
 	uint32_t table_id,
@@ -1408,156 +1527,119 @@ pipeline_table_rule_add_default(const char *pipeline_name,
 	return status;
 }
 
+static uint32_t
+table_rule_list_free(struct table_rule_list *list)
+{
+	uint32_t n = 0;
+
+	if (!list)
+		return 0;
+
+	for ( ; ; ) {
+		struct table_rule *rule;
+
+		rule = TAILQ_FIRST(list);
+		if (rule == NULL)
+			break;
+
+		TAILQ_REMOVE(list, rule, node);
+		free(rule);
+		n++;
+	}
+
+	free(list);
+	return n;
+}
+
 int
 pipeline_table_rule_add_bulk(const char *pipeline_name,
 	uint32_t table_id,
-	struct table_rule_match *match,
-	struct table_rule_action *action,
-	void **data,
-	uint32_t *n_rules)
+	struct table_rule_list *list,
+	uint32_t *n_rules_added,
+	uint32_t *n_rules_not_added)
 {
 	struct pipeline *p;
+	struct table *table;
 	struct pipeline_msg_req *req;
 	struct pipeline_msg_rsp *rsp;
-	uint32_t i;
-	int status;
+	struct table_rule *rule;
+	int status = 0;
 
 	/* Check input params */
 	if ((pipeline_name == NULL) ||
-		(match == NULL) ||
-		(action == NULL) ||
-		(data == NULL) ||
-		(n_rules == NULL) ||
-		(*n_rules == 0))
-		return -1;
+		(list == NULL) ||
+		TAILQ_EMPTY(list) ||
+		(n_rules_added == NULL) ||
+		(n_rules_not_added == NULL)) {
+		table_rule_list_free(list);
+		return -EINVAL;
+	}
 
 	p = pipeline_find(pipeline_name);
 	if ((p == NULL) ||
-		(table_id >= p->n_tables))
-		return -1;
-
-	for (i = 0; i < *n_rules; i++)
-		if (match_check(match, p, table_id) ||
-			action_check(action, p, table_id))
-			return -1;
-
-	if (!pipeline_is_running(p)) {
-		struct rte_table_action *a = p->table[table_id].a;
-		union table_rule_match_low_level *match_ll;
-		uint8_t *action_ll;
-		void **match_ll_ptr;
-		struct rte_pipeline_table_entry **action_ll_ptr;
-		struct rte_pipeline_table_entry **entries_ptr =
-			(struct rte_pipeline_table_entry **)data;
-		uint32_t bulk =
-			(p->table[table_id].params.match_type == TABLE_ACL) ? 1 : 0;
-		int *found;
-
-		/* Memory allocation */
-		match_ll = calloc(*n_rules, sizeof(union table_rule_match_low_level));
-		action_ll = calloc(*n_rules, TABLE_RULE_ACTION_SIZE_MAX);
-		match_ll_ptr = calloc(*n_rules, sizeof(void *));
-		action_ll_ptr =
-			calloc(*n_rules, sizeof(struct rte_pipeline_table_entry *));
-		found = calloc(*n_rules, sizeof(int));
-
-		if (match_ll == NULL ||
-			action_ll == NULL ||
-			match_ll_ptr == NULL ||
-			action_ll_ptr == NULL ||
-			found == NULL)
-			goto fail;
-
-		for (i = 0; i < *n_rules; i++) {
-			match_ll_ptr[i] = (void *)&match_ll[i];
-			action_ll_ptr[i] =
-				(struct rte_pipeline_table_entry *)&action_ll[i * TABLE_RULE_ACTION_SIZE_MAX];
-		}
+		(table_id >= p->n_tables)) {
+		table_rule_list_free(list);
+		return -EINVAL;
+	}
 
-		/* Rule match conversion */
-		for (i = 0; i < *n_rules; i++) {
-			status = match_convert(&match[i], match_ll_ptr[i], 1);
-			if (status)
-				goto fail;
-		}
+	table = &p->table[table_id];
 
-		/* Rule action conversion */
-		for (i = 0; i < *n_rules; i++) {
-			status = action_convert(a, &action[i], action_ll_ptr[i]);
-			if (status)
-				goto fail;
+	TAILQ_FOREACH(rule, list, node)
+		if (match_check(&rule->match, p, table_id) ||
+			action_check(&rule->action, p, table_id)) {
+			table_rule_list_free(list);
+			return -EINVAL;
 		}
 
-		/* Add rule (match, action) to table */
-		if (bulk) {
-			status = rte_pipeline_table_entry_add_bulk(p->p,
-				table_id,
-				match_ll_ptr,
-				action_ll_ptr,
-				*n_rules,
-				found,
-				entries_ptr);
-			if (status)
-				*n_rules = 0;
-		} else {
-			for (i = 0; i < *n_rules; i++) {
-				status = rte_pipeline_table_entry_add(p->p,
-					table_id,
-					match_ll_ptr[i],
-					action_ll_ptr[i],
-					&found[i],
-					&entries_ptr[i]);
-				if (status) {
-					*n_rules = i;
-					break;
-				}
-			}
+	if (!pipeline_is_running(p)) {
+		struct table_ll table_ll = {
+			.p = p->p,
+			.table_id = table_id,
+			.a = table->a,
+			.bulk_supported = table->params.match_type == TABLE_ACL,
+		};
+
+		status = table_rule_add_bulk_ll(&table_ll, list, n_rules_added);
+		if (status) {
+			table_rule_list_free(list);
+			return status;
 		}
 
-		/* Free */
-		free(found);
-		free(action_ll_ptr);
-		free(match_ll_ptr);
-		free(action_ll);
-		free(match_ll);
-
-		return status;
-
-fail:
-		free(found);
-		free(action_ll_ptr);
-		free(match_ll_ptr);
-		free(action_ll);
-		free(match_ll);
-
-		*n_rules = 0;
-		return -1;
+		table_rule_add_bulk(table, list, *n_rules_added);
+		*n_rules_not_added = table_rule_list_free(list);
+		return 0;
 	}
 
 	/* Allocate request */
 	req = pipeline_msg_alloc();
-	if (req == NULL)
-		return -1;
+	if (req == NULL) {
+		table_rule_list_free(list);
+		return -ENOMEM;
+	}
 
 	/* Write request */
 	req->type = PIPELINE_REQ_TABLE_RULE_ADD_BULK;
 	req->id = table_id;
-	req->table_rule_add_bulk.match = match;
-	req->table_rule_add_bulk.action = action;
-	req->table_rule_add_bulk.data = data;
-	req->table_rule_add_bulk.n_rules = *n_rules;
-	req->table_rule_add_bulk.bulk =
-		(p->table[table_id].params.match_type == TABLE_ACL) ? 1 : 0;
+	req->table_rule_add_bulk.list = list;
+	req->table_rule_add_bulk.bulk = table->params.match_type == TABLE_ACL;
 
 	/* Send request and wait for response */
 	rsp = pipeline_msg_send_recv(p, req);
-	if (rsp == NULL)
-		return -1;
+	if (rsp == NULL) {
+		table_rule_list_free(list);
+		return -ENOMEM;
+	}
 
 	/* Read response */
 	status = rsp->status;
-	if (status == 0)
-		*n_rules = rsp->table_rule_add_bulk.n_rules;
+	if (status == 0) {
+		*n_rules_added = rsp->table_rule_add_bulk.n_rules;
+
+		table_rule_add_bulk(table, list, *n_rules_added);
+		*n_rules_not_added = table_rule_list_free(list);
+	} else
+		table_rule_list_free(list);
+
 
 	/* Free response */
 	pipeline_msg_free(rsp);
@@ -2626,107 +2708,32 @@ static struct pipeline_msg_rsp *
 pipeline_msg_handle_table_rule_add_bulk(struct pipeline_data *p,
 	struct pipeline_msg_req *req)
 {
-
 	struct pipeline_msg_rsp *rsp = (struct pipeline_msg_rsp *) req;
 
 	uint32_t table_id = req->id;
-	struct table_rule_match *match = req->table_rule_add_bulk.match;
-	struct table_rule_action *action = req->table_rule_add_bulk.action;
-	struct rte_pipeline_table_entry **data =
-		(struct rte_pipeline_table_entry **)req->table_rule_add_bulk.data;
-	uint32_t n_rules = req->table_rule_add_bulk.n_rules;
+	struct table_rule_list *list = req->table_rule_add_bulk.list;
 	uint32_t bulk = req->table_rule_add_bulk.bulk;
 
-	struct rte_table_action *a = p->table_data[table_id].a;
-	union table_rule_match_low_level *match_ll;
-	uint8_t *action_ll;
-	void **match_ll_ptr;
-	struct rte_pipeline_table_entry **action_ll_ptr;
-	int *found, status;
-	uint32_t i;
-
-	/* Memory allocation */
-	match_ll = calloc(n_rules, sizeof(union table_rule_match_low_level));
-	action_ll = calloc(n_rules, TABLE_RULE_ACTION_SIZE_MAX);
-	match_ll_ptr = calloc(n_rules, sizeof(void *));
-	action_ll_ptr =
-		calloc(n_rules, sizeof(struct rte_pipeline_table_entry *));
-	found = calloc(n_rules, sizeof(int));
-
-	if ((match_ll == NULL) ||
-		(action_ll == NULL) ||
-		(match_ll_ptr == NULL) ||
-		(action_ll_ptr == NULL) ||
-		(found == NULL))
-		goto fail;
-
-	for (i = 0; i < n_rules; i++) {
-		match_ll_ptr[i] = (void *)&match_ll[i];
-		action_ll_ptr[i] =
-			(struct rte_pipeline_table_entry *)&action_ll[i * TABLE_RULE_ACTION_SIZE_MAX];
-	}
+	uint32_t n_rules_added;
+	int status;
 
-	/* Rule match conversion */
-	for (i = 0; i < n_rules; i++) {
-		status = match_convert(&match[i], match_ll_ptr[i], 1);
-		if (status)
-			goto fail;
-	}
+	struct table_ll table_ll = {
+		.p = p->p,
+		.table_id = table_id,
+		.a = p->table_data[table_id].a,
+		.bulk_supported = bulk,
+	};
 
-	/* Rule action conversion */
-	for (i = 0; i < n_rules; i++) {
-		status = action_convert(a, &action[i], action_ll_ptr[i]);
-		if (status)
-			goto fail;
+	status = table_rule_add_bulk_ll(&table_ll, list, &n_rules_added);
+	if (status) {
+		rsp->status = -1;
+		rsp->table_rule_add_bulk.n_rules = 0;
+		return rsp;
 	}
 
-	/* Add rule (match, action) to table */
-	if (bulk) {
-		status = rte_pipeline_table_entry_add_bulk(p->p,
-			table_id,
-			match_ll_ptr,
-			action_ll_ptr,
-			n_rules,
-			found,
-			data);
-		if (status)
-			n_rules = 0;
-	} else
-		for (i = 0; i < n_rules; i++) {
-			status = rte_pipeline_table_entry_add(p->p,
-				table_id,
-				match_ll_ptr[i],
-				action_ll_ptr[i],
-				&found[i],
-				&data[i]);
-			if (status) {
-				n_rules = i;
-				break;
-			}
-		}
-
 	/* Write response */
 	rsp->status = 0;
-	rsp->table_rule_add_bulk.n_rules = n_rules;
-
-	/* Free */
-	free(found);
-	free(action_ll_ptr);
-	free(match_ll_ptr);
-	free(action_ll);
-	free(match_ll);
-
-	return rsp;
-
-fail:
-	free(found);
-	free(action_ll_ptr);
-	free(match_ll_ptr);
-	free(action_ll);
-	free(match_ll);
-
-	rsp->status = -1;
-	rsp->table_rule_add_bulk.n_rules = 0;
+	rsp->table_rule_add_bulk.n_rules = n_rules_added;
 	return rsp;
 }
 
