From patchwork Fri Jul 19 15:17:32 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Tomasz Bursztyka X-Patchwork-Id: 260284 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id B6F9C2C008C for ; Sat, 20 Jul 2013 01:18:03 +1000 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755250Ab3GSPSB (ORCPT ); Fri, 19 Jul 2013 11:18:01 -0400 Received: from mga14.intel.com ([143.182.124.37]:29498 "EHLO mga14.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752320Ab3GSPSA (ORCPT ); Fri, 19 Jul 2013 11:18:00 -0400 Received: from azsmga002.ch.intel.com ([10.2.17.35]) by azsmga102.ch.intel.com with ESMTP; 19 Jul 2013 08:18:00 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.89,702,1367996400"; d="scan'208";a="270608731" Received: from unknown (HELO rd-180.ger.corp.intel.com) ([10.252.122.114]) by AZSMGA002.ch.intel.com with ESMTP; 19 Jul 2013 08:17:51 -0700 From: Tomasz Bursztyka To: netfilter-devel@vger.kernel.org Cc: Tomasz Bursztyka Subject: [iptables-nftables - RFC PATCH 03/15] nft: Add nft expressions translation engine as a library Date: Fri, 19 Jul 2013 18:17:32 +0300 Message-Id: <1374247064-3361-4-git-send-email-tomasz.bursztyka@linux.intel.com> X-Mailer: git-send-email 1.8.3.2 In-Reply-To: <1374247064-3361-1-git-send-email-tomasz.bursztyka@linux.intel.com> References: <1374247064-3361-1-git-send-email-tomasz.bursztyka@linux.intel.com> MIME-Version: 1.0 Sender: netfilter-devel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netfilter-devel@vger.kernel.org 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 --- 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 --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 + * + * 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 +#include +#include + +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 + * + * 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 +#include +#include + +#include + +#include + +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]; +} +