@@ -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
@@ -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
new file mode 100644
@@ -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 */
new file mode 100644
@@ -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
new file mode 100644
@@ -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}
new file mode 100644
@@ -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];
+}
+
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