diff mbox

[iptables-nftables,-,RFC,03/15] nft: Add nft expressions translation engine as a library

Message ID 1374247064-3361-4-git-send-email-tomasz.bursztyka@linux.intel.com
State Superseded
Headers show

Commit Message

Tomasz Bursztyka July 19, 2013, 3:17 p.m. UTC
libnfttrans is a generic translation engine from nft expressions to
registered "complex instructions". It works on a simple tree based
pattern matching algorithm.

Idea is to be able to register any kind of expressions suit (or pattern)
linked to a parsing function representing the "complex instruction".
Then, being able to go through the whole expression list of a rule and
retrieving the original complex instructions suit.

Once applied on xtables (iptables over nftables), this will allow to
retrieve the exact iptables_command_state structure for instance.
However, such engine is generic enough to be reused in any other tool,
like future arptables and ebtables compatible tool over nftables.

Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
---
 Makefile.am                   |   3 +
 configure.ac                  |   1 +
 include/nft-translator.h      |  85 ++++++
 libnfttrans/Makefile.am       |  28 ++
 libnfttrans/libnfttrans.pc.in |  11 +
 libnfttrans/nft-translator.c  | 618 ++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 746 insertions(+)
 create mode 100644 include/nft-translator.h
 create mode 100644 libnfttrans/Makefile.am
 create mode 100644 libnfttrans/libnfttrans.pc.in
 create mode 100644 libnfttrans/nft-translator.c
diff mbox

Patch

diff --git a/Makefile.am b/Makefile.am
index 4eb63eb..8123a87 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,6 +4,9 @@  ACLOCAL_AMFLAGS  = -I m4
 AUTOMAKE_OPTIONS = foreign subdir-objects
 
 SUBDIRS          = libiptc libxtables
+if ENABLE_NFTABLES
+SUBDIRS		+= libnfttrans
+endif
 if ENABLE_DEVEL
 SUBDIRS         += include
 endif
diff --git a/configure.ac b/configure.ac
index e228078..6f144bf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -176,5 +176,6 @@  AC_CONFIG_FILES([Makefile extensions/GNUmakefile include/Makefile
 	libiptc/Makefile libiptc/libiptc.pc
 	libiptc/libip4tc.pc libiptc/libip6tc.pc
 	libxtables/Makefile utils/Makefile
+	libnfttrans/Makefile libnfttrans/libnfttrans.pc
 	include/xtables.h include/iptables/internal.h])
 AC_OUTPUT
diff --git a/include/nft-translator.h b/include/nft-translator.h
new file mode 100644
index 0000000..aa2eb7f
--- /dev/null
+++ b/include/nft-translator.h
@@ -0,0 +1,85 @@ 
+/*
+ * (C) 2013 by Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _NFT_TRANSLATOR_H
+#define _NFT_TRANSLATOR_H
+
+#include <stdint.h>
+#include <libnftables/rule.h>
+#include <libnftables/expr.h>
+
+enum nft_instruction {
+	NFT_INSTRUCTION_BITWISE   = 0,
+	NFT_INSTRUCTION_BYTEORDER = 1,
+	NFT_INSTRUCTION_CMP       = 2,
+	NFT_INSTRUCTION_COUNTER   = 3,
+	NFT_INSTRUCTION_CT        = 4,
+	NFT_INSTRUCTION_EXTHDR    = 5,
+	NFT_INSTRUCTION_IMMEDIATE = 6,
+	NFT_INSTRUCTION_LIMIT     = 7,
+	NFT_INSTRUCTION_LOG       = 8,
+	NFT_INSTRUCTION_LOOKUP    = 9,
+	NFT_INSTRUCTION_MATCH     = 10,
+	NFT_INSTRUCTION_META      = 11,
+	NFT_INSTRUCTION_NAT       = 12,
+	NFT_INSTRUCTION_PAYLOAD   = 13,
+	NFT_INSTRUCTION_REJECT    = 14,
+	NFT_INSTRUCTION_TARGET    = 15,
+	NFT_INSTRUCTION_MAX       = 16,
+};
+
+struct nft_trans_instruction_tree;
+struct nft_trans_rule_context;
+struct nft_trans_instruction_context;
+
+typedef int (*nft_trans_parse_callback_f)(const char *ident,
+					  void *data,
+					  void *user_data);
+
+typedef int
+(*nft_trans_parse_instruction_f)(struct nft_trans_rule_context *rule_ctx,
+				 struct nft_trans_instruction_context *first,
+				 struct nft_trans_instruction_context *last,
+				 nft_trans_parse_callback_f user_cb,
+				 void *user_data);
+
+struct nft_trans_instruction {
+	enum nft_instruction *instructions;
+	nft_trans_parse_instruction_f function;
+};
+
+struct nft_trans_instruction_tree *nft_trans_instruction_tree_new(void);
+
+void
+nft_trans_instruction_tree_destroy(struct nft_trans_instruction_tree *tree);
+
+int nft_trans_add_instruction(struct nft_trans_instruction_tree *tree,
+			      struct nft_trans_instruction *ipt_i);
+
+int
+nft_trans_rulecontext_inhibate_instruction(struct nft_trans_rule_context *rule_ctx,
+					   nft_trans_parse_instruction_f function);
+
+int
+nft_trans_rule_translate_to_instructions(struct nft_trans_instruction_tree *tree,
+					 struct nft_rule *rule,
+					 nft_trans_parse_callback_f user_cb,
+					 void *user_data);
+
+struct nft_trans_instruction_context *
+nft_trans_instruction_context_get_next(struct nft_trans_instruction_context *i_ctx);
+
+struct nft_rule_expr *
+nft_trans_instruction_context_get_expr(struct nft_trans_instruction_context *i_ctx);
+
+struct nft_rule_expr *
+nft_trans_instruction_context_get_register(struct nft_trans_instruction_context *i_ctx,
+					   int reg);
+
+#endif /* _NFT_TRANSLATOR_H */
diff --git a/libnfttrans/Makefile.am b/libnfttrans/Makefile.am
new file mode 100644
index 0000000..5befb63
--- /dev/null
+++ b/libnfttrans/Makefile.am
@@ -0,0 +1,28 @@ 
+# -*- Makefile -*-
+if ENABLE_NFTABLES
+if HAVE_LIBMNL
+if HAVE_LIBNFTABLES
+
+AM_CFLAGS   = ${regular_CFLAGS}
+AM_CPPFLAGS = ${regular_CPPFLAGS}  -I${top_builddir}/include \
+		-I${top_srcdir}/include  -I./ ${kinclude_CPPFLAGS}
+
+lib_LTLIBRARIES       = libnfttrans.la
+libnfttrans_la_SOURCES = nft-translator.c
+libnfttrans_la_LDFLAGS =
+libnfttrans_la_LIBADD  =
+if ENABLE_STATIC
+# With --enable-static, shipped extensions are linked into the main executable,
+# so we need all the LIBADDs here too
+libnfttrans_la_LIBADD += -lm
+endif
+if ENABLE_SHARED
+libnfttrans_la_CFLAGS  = ${AM_CFLAGS}
+libnfttrans_la_LIBADD += -ldl
+else
+libnfttrans_la_CFLAGS  = ${AM_CFLAGS} -DNO_SHARED_LIBS=1
+endif
+
+endif # HAVE_LIBNFTABLES
+endif # HAVE_LIBMNL
+endif # ENABLE_NFTABLES
diff --git a/libnfttrans/libnfttrans.pc.in b/libnfttrans/libnfttrans.pc.in
new file mode 100644
index 0000000..f3363de
--- /dev/null
+++ b/libnfttrans/libnfttrans.pc.in
@@ -0,0 +1,11 @@ 
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name:		libnfttrans
+Description:	Small engine to translate nft expressions list into more complex registered subset
+Version:	@PACKAGE_VERSION@
+Libs:		-L${libdir} -lnfttrans
+Cflags:		-I${includedir}
diff --git a/libnfttrans/nft-translator.c b/libnfttrans/nft-translator.c
new file mode 100644
index 0000000..50db967
--- /dev/null
+++ b/libnfttrans/nft-translator.c
@@ -0,0 +1,618 @@ 
+/*
+ * (C) 2013 by Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include <nft-translator.h>
+
+static const char *nft_instruction_name[NFT_INSTRUCTION_MAX] = {
+	"bitwise",
+	"byteorder",
+	"cmp",
+	"counter",
+	"ct",
+	"exthdr",
+	"immediate",
+	"limit",
+	"log",
+	"lookup",
+	"match",
+	"meta",
+	"nat",
+	"payload",
+	"reject",
+	"target",
+};
+
+typedef void (*free_function_f)(void *);
+
+struct s_list {
+	void *data;
+	struct s_list *next;
+};
+
+struct nft_trans_instruction_node {
+	struct s_list *functions;
+	struct nft_trans_instruction_node *nodes[NFT_INSTRUCTION_MAX];
+};
+
+struct nft_trans_instruction_tree {
+	struct s_list *nodes;
+	struct nft_trans_instruction_node *root;
+};
+
+struct nft_trans_register_context {
+	struct nft_rule_expr *reg[NFT_REG_MAX];
+};
+
+struct nft_trans_instruction_context {
+	struct nft_trans_instruction_context *next;
+
+	struct nft_rule_expr *current_expr;
+	enum nft_instruction instruction;
+	struct nft_trans_register_context *registers;
+};
+
+struct nft_trans_rule_context {
+	struct nft_trans_instruction_context *instr_contexts;
+
+	/* Some complex instructions cannot be seen multiple times */
+	struct s_list *inhibited;
+};
+
+struct nft_trans_found_instruction {
+	const struct s_list *functions;
+	struct nft_trans_instruction_context *position;
+};
+
+static enum nft_instruction str2nft_intruction(const char *name)
+{
+	enum nft_instruction i;
+
+	for (i = 0; i < NFT_INSTRUCTION_MAX; i++) {
+		 if (strncmp(nft_instruction_name[i], name,
+					strlen(nft_instruction_name[i])) == 0)
+			 return i;
+	}
+
+	return NFT_INSTRUCTION_MAX;
+}
+
+static struct s_list *s_list_prepend(struct s_list *list, void *data)
+{
+	struct s_list *n_list;
+
+	n_list = calloc(1, sizeof(struct s_list));
+	if (n_list == NULL)
+		return list;
+
+	n_list->data = data;
+	n_list->next = list;
+
+	return n_list;
+}
+
+static void _s_list_free(struct s_list *list, int data_too,
+			 free_function_f _free)
+{
+	struct s_list *previous = NULL;
+
+	for (; list != NULL; list = list->next) {
+		if (previous != NULL) {
+			if (previous->data != NULL && data_too != 0) {
+				if (_free != NULL)
+					_free(previous->data);
+				else
+					free(previous->data);
+			}
+
+			free(previous);
+		}
+
+		previous = list;
+	}
+
+	if (previous != NULL) {
+		if (previous->data != NULL && data_too != 0) {
+			if (_free != NULL)
+				_free(previous->data);
+			else
+				free(previous->data);
+		}
+
+		free(previous);
+	}
+}
+
+static inline void s_list_free(struct s_list *list)
+{
+	_s_list_free(list, 0, NULL);
+}
+
+static inline void s_list_free_all(struct s_list *list)
+{
+	_s_list_free(list, 1, NULL);
+}
+
+static inline void s_list_free_full(struct s_list *list, free_function_f _free)
+{
+	_s_list_free(list, 1, _free);
+}
+
+struct nft_trans_instruction_tree *nft_trans_instruction_tree_new(void)
+{
+	struct nft_trans_instruction_tree *tree;
+
+	tree = calloc(1, sizeof(struct nft_trans_instruction_tree));
+	if (tree != NULL) {
+		tree->root = calloc(1, sizeof(struct nft_trans_instruction_node));
+		if (tree->root == NULL)
+			goto error;
+
+		tree->nodes = s_list_prepend(tree->nodes, tree->root);
+		if (tree->nodes == NULL)
+			goto error;
+	}
+
+	return tree;
+
+error:
+	free(tree);
+	return NULL;
+}
+
+static void _free_nft_trans_instruction_node(void *data)
+{
+	struct nft_trans_instruction_node *node = data;
+
+	if (node == NULL)
+		return;
+
+	s_list_free(node->functions);
+	free(node);
+}
+
+void
+nft_trans_instruction_tree_destroy(struct nft_trans_instruction_tree *tree)
+{
+	if (tree == NULL)
+		return;
+
+	s_list_free_full(tree->nodes, _free_nft_trans_instruction_node);
+	free(tree);
+}
+
+int nft_trans_add_instruction(struct nft_trans_instruction_tree *tree,
+			      struct nft_trans_instruction *ipt_i)
+{
+	struct nft_trans_instruction_node *node;
+	enum nft_instruction *instr;
+
+	if (tree == NULL)
+		return -EINVAL;
+
+	node = tree->root;
+	for (instr = ipt_i->instructions;
+				*instr < NFT_INSTRUCTION_MAX; instr++) {
+		if (node->nodes[*instr] == NULL) {
+			node->nodes[*instr] = calloc(1,
+					sizeof(struct nft_trans_instruction_node));
+			if (node->nodes[*instr] == NULL)
+				return -ENOMEM;
+		}
+
+		node = node->nodes[*instr];
+		tree->nodes = s_list_prepend(tree->nodes, node);
+	}
+
+	node->functions = s_list_prepend(node->functions, ipt_i->function);
+
+	return 0;
+}
+
+static void
+free_nft_trans_instruction_context(struct nft_trans_instruction_context *i_ctx)
+{
+	if (i_ctx == NULL)
+		return;
+
+	free(i_ctx->registers);
+	free(i_ctx);
+}
+
+static void
+destroy_nft_trans_rule_context(struct nft_trans_rule_context *rule_ctx)
+{
+	if (rule_ctx == NULL)
+		return;
+
+	if (rule_ctx->instr_contexts != NULL) {
+		struct nft_trans_instruction_context *i_ctx, *prev = NULL;
+
+		for (i_ctx = rule_ctx->instr_contexts;
+					i_ctx != NULL; i_ctx = i_ctx->next) {
+			free_nft_trans_instruction_context(prev);
+			prev = i_ctx;
+		}
+
+		free_nft_trans_instruction_context(prev);
+	}
+
+	if (rule_ctx->inhibited != NULL)
+		s_list_free(rule_ctx->inhibited);
+
+	free(rule_ctx);
+}
+
+static void
+update_register_from_bitwise(struct nft_rule_expr *expr,
+			     struct nft_trans_register_context *registers)
+{
+	if (nft_rule_expr_is_set(expr, NFT_EXPR_BITWISE_DREG))
+		registers->reg[nft_rule_expr_get_u32(expr,
+						NFT_EXPR_BITWISE_DREG)] = expr;
+}
+
+static void
+update_register_from_byteorder(struct nft_rule_expr *expr,
+			       struct nft_trans_register_context *registers)
+{
+	if (nft_rule_expr_is_set(expr, NFT_EXPR_BYTEORDER_DREG))
+		registers->reg[nft_rule_expr_get_u32(expr,
+					NFT_EXPR_BYTEORDER_DREG)] = expr;
+}
+
+static void
+update_register_from_ct(struct nft_rule_expr *expr,
+			struct nft_trans_register_context *registers)
+{
+	if (nft_rule_expr_is_set(expr, NFT_EXPR_CT_DREG))
+		registers->reg[nft_rule_expr_get_u32(expr,
+						NFT_EXPR_CT_DREG)] = expr;
+}
+
+static void
+update_register_from_exthdr(struct nft_rule_expr *expr,
+			    struct nft_trans_register_context *registers)
+{
+	if (nft_rule_expr_is_set(expr, NFT_EXPR_EXTHDR_DREG))
+		registers->reg[nft_rule_expr_get_u32(expr,
+						NFT_EXPR_EXTHDR_DREG)] = expr;
+}
+
+static void
+update_register_from_immediate(struct nft_rule_expr *expr,
+			       struct nft_trans_register_context *registers)
+{
+	if (nft_rule_expr_is_set(expr, NFT_EXPR_IMM_DREG))
+		registers->reg[nft_rule_expr_get_u32(expr,
+						NFT_EXPR_IMM_DREG)] = expr;
+}
+
+static void
+update_register_from_lookup(struct nft_rule_expr *expr,
+			    struct nft_trans_register_context *registers)
+{
+	if (nft_rule_expr_is_set(expr, NFT_EXPR_LOOKUP_DREG))
+		registers->reg[nft_rule_expr_get_u32(expr,
+						NFT_EXPR_LOOKUP_DREG)] = expr;
+}
+
+static void
+update_register_from_meta(struct nft_rule_expr *expr,
+			  struct nft_trans_register_context *registers)
+{
+	if (nft_rule_expr_is_set(expr, NFT_EXPR_META_DREG))
+		registers->reg[nft_rule_expr_get_u32(expr,
+						NFT_EXPR_META_DREG)] = expr;
+}
+
+static void
+update_register_from_payload(struct nft_rule_expr *expr,
+			     struct nft_trans_register_context *registers)
+{
+	if (nft_rule_expr_is_set(expr, NFT_EXPR_PAYLOAD_DREG))
+		registers->reg[nft_rule_expr_get_u32(expr,
+						NFT_EXPR_PAYLOAD_DREG)] = expr;
+}
+
+static struct nft_trans_register_context *
+update_registers(enum nft_instruction instruction, struct nft_rule_expr *expr,
+		 struct nft_trans_register_context *registers)
+{
+	struct nft_trans_register_context *new_registers;
+
+	new_registers = calloc(1, sizeof(struct nft_trans_register_context));
+	if (new_registers == NULL)
+		return NULL;
+
+	memcpy(new_registers, registers, sizeof(struct nft_trans_register_context));
+
+	switch (instruction) {
+	case NFT_INSTRUCTION_BITWISE:
+		update_register_from_bitwise(expr, new_registers);
+		break;
+	case NFT_INSTRUCTION_BYTEORDER:
+		update_register_from_byteorder(expr, new_registers);
+		break;
+	case NFT_INSTRUCTION_CMP:
+	case NFT_INSTRUCTION_COUNTER:
+		break;
+	case NFT_INSTRUCTION_CT:
+		update_register_from_ct(expr, new_registers);
+		break;
+	case NFT_INSTRUCTION_EXTHDR:
+		update_register_from_exthdr(expr, new_registers);
+		break;
+	case NFT_INSTRUCTION_IMMEDIATE:
+		update_register_from_immediate(expr, new_registers);
+		break;
+	case NFT_INSTRUCTION_LIMIT:
+	case NFT_INSTRUCTION_LOG:
+		break;
+	case NFT_INSTRUCTION_LOOKUP:
+		update_register_from_lookup(expr, new_registers);
+		break;
+	case NFT_INSTRUCTION_MATCH:
+		break;
+	case NFT_INSTRUCTION_META:
+		update_register_from_meta(expr, new_registers);
+		break;
+	case NFT_INSTRUCTION_NAT:
+		break;
+	case NFT_INSTRUCTION_PAYLOAD:
+		update_register_from_payload(expr, new_registers);
+		break;
+	case NFT_INSTRUCTION_REJECT:
+	case NFT_INSTRUCTION_TARGET:
+		break;
+	case NFT_INSTRUCTION_MAX:
+		return NULL;
+	};
+
+	return new_registers;
+}
+
+static struct nft_trans_rule_context *
+generate_nft_trans_rule_context(struct nft_rule *rule)
+{
+	struct nft_trans_instruction_context *cur_ctx = NULL;
+	struct nft_trans_register_context *cur_regs = NULL;
+	struct nft_trans_rule_context *rule_ctx;
+	struct nft_rule_expr_iter *iter;
+	struct nft_rule_expr *expr;
+
+	rule_ctx = calloc(1, sizeof(struct nft_trans_rule_context));
+	if (rule_ctx == NULL)
+		return NULL;
+
+	iter = nft_rule_expr_iter_create(rule);
+	if (iter == NULL)
+		goto error;
+
+	cur_regs = calloc(1, sizeof(struct nft_trans_register_context));
+	if (cur_regs == NULL)
+		goto error;
+
+	expr = nft_rule_expr_iter_next(iter);
+	while (expr != NULL) {
+		struct nft_trans_instruction_context *ctx;
+		enum nft_instruction instr;
+
+		ctx = calloc(1, sizeof(struct nft_trans_instruction_context));
+		if (ctx == NULL)
+			goto error;
+
+		instr = str2nft_intruction(nft_rule_expr_get_str(expr,
+						NFT_RULE_EXPR_ATTR_NAME));
+		if (instr == NFT_INSTRUCTION_MAX)
+			goto error;
+
+		ctx->current_expr = expr;
+		ctx->instruction = instr;
+		ctx->registers = cur_regs;
+
+		if (cur_ctx == NULL)
+			rule_ctx->instr_contexts = ctx;
+		else
+			cur_ctx->next = ctx;
+
+		cur_ctx = ctx;
+
+		cur_regs = update_registers(instr, expr, cur_regs);
+		if (cur_regs == NULL)
+			goto error;
+
+		expr = nft_rule_expr_iter_next(iter);
+	}
+
+	if (cur_regs != NULL)
+		free(cur_regs);
+
+	nft_rule_expr_iter_destroy(iter);
+
+	return rule_ctx;
+
+error:
+	destroy_nft_trans_rule_context(rule_ctx);
+
+	if (cur_regs != NULL)
+		free(cur_regs);
+
+	if (iter != NULL)
+		nft_rule_expr_iter_destroy(iter);
+
+	return NULL;
+}
+
+int
+nft_trans_rulecontext_inhibate_instruction(struct nft_trans_rule_context *rule_ctx,
+					   nft_trans_parse_instruction_f function)
+{
+	if (rule_ctx == NULL)
+		return -EINVAL;
+
+	rule_ctx->inhibited = s_list_prepend(rule_ctx->inhibited, function);
+
+	return 0;
+}
+
+static struct s_list *
+retrieve_nft_trans_instructions(struct nft_trans_instruction_tree *tree,
+				struct nft_trans_instruction_context *instructions)
+{
+	struct s_list *nft_trans_instructions = NULL;
+	struct nft_trans_instruction_context *ctx;
+	struct nft_trans_found_instruction *ipt_i;
+	struct nft_trans_instruction_node *node;
+
+	ctx = instructions;
+	node = tree->root;
+
+	while (ctx != NULL) {
+		if (node->nodes[ctx->instruction] != NULL) {
+			node = node->nodes[ctx->instruction];
+
+			if (node->functions != NULL) {
+				ipt_i = calloc(1,
+					sizeof(struct nft_trans_found_instruction));
+
+				ipt_i->functions = node->functions;
+				ipt_i->position = ctx;
+
+				/* It prepends since "longest path first"
+				 * is applied */
+				nft_trans_instructions = s_list_prepend(
+						nft_trans_instructions, ipt_i);
+			}
+		} else
+			break;
+
+		ctx = ctx->next;
+	};
+
+	return nft_trans_instructions;
+}
+
+static bool is_instruction_inhibited(struct s_list *inhibited, void *data)
+{
+	for (; inhibited != NULL; inhibited = inhibited->next) {
+		if (inhibited->data == data)
+			return true;
+	}
+
+	return false;
+}
+
+static struct nft_trans_instruction_context *
+execute_relevant_instruction(struct s_list *instructions,
+			     struct nft_trans_rule_context *rule_ctx,
+			     struct nft_trans_instruction_context *position,
+			     nft_trans_parse_callback_f user_cb,
+			     void *user_data)
+{
+	for (; instructions != NULL; instructions = instructions->next) {
+		struct nft_trans_found_instruction *i_f = instructions->data;
+		const struct s_list *fl;
+
+		for (fl = i_f->functions; fl != NULL; fl = fl->next) {
+			nft_trans_parse_instruction_f function = fl->data;
+
+			if (is_instruction_inhibited(rule_ctx->inhibited,
+								function))
+				continue;
+
+			if (function(rule_ctx, position, i_f->position,
+						user_cb, user_data) == 0)
+				return i_f->position;
+		}
+	}
+
+	return NULL;
+}
+
+int
+nft_trans_rule_translate_to_instructions(struct nft_trans_instruction_tree *tree,
+					 struct nft_rule *rule,
+					 nft_trans_parse_callback_f user_cb,
+					 void *user_data)
+{
+	struct nft_trans_instruction_context *position;
+	struct s_list *nft_trans_instructions;
+	struct nft_trans_rule_context *rule_ctx;
+
+	if (tree == NULL)
+		return -1;
+
+	rule_ctx = generate_nft_trans_rule_context(rule);
+	if (rule_ctx == NULL)
+		return -1;
+
+	position = rule_ctx->instr_contexts;
+	while (position != NULL) {
+		struct nft_trans_instruction_context *pos;
+
+		nft_trans_instructions = retrieve_nft_trans_instructions(tree,
+								position);
+		if (nft_trans_instructions == NULL)
+			goto error;
+
+		pos = execute_relevant_instruction(nft_trans_instructions,
+				rule_ctx, position, user_cb, user_data);
+		if (pos == NULL)
+			goto error;
+
+		s_list_free_all(nft_trans_instructions);
+		position = pos->next;
+	}
+
+	destroy_nft_trans_rule_context(rule_ctx);
+
+	return 0;
+
+error:
+	s_list_free_all(nft_trans_instructions);
+	destroy_nft_trans_rule_context(rule_ctx);
+
+	return -1;
+}
+
+struct nft_trans_instruction_context *
+nft_trans_instruction_context_get_next(struct nft_trans_instruction_context *i_ctx)
+{
+	if (i_ctx == NULL)
+		return NULL;
+
+	return i_ctx->next;
+}
+
+struct nft_rule_expr *
+nft_trans_instruction_context_get_expr(struct nft_trans_instruction_context *i_ctx)
+{
+	if (i_ctx == NULL)
+		return NULL;
+
+	return i_ctx->current_expr;
+}
+
+struct nft_rule_expr *
+nft_trans_instruction_context_get_register(struct nft_trans_instruction_context *i_ctx,
+					int register_index)
+{
+	if (i_ctx == NULL || i_ctx->registers == NULL ||
+					register_index >= NFT_REG_MAX)
+		return NULL;
+
+	return i_ctx->registers->reg[register_index];
+}
+