summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:04:34 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:04:34 +0000
commit371c8a8bca2b6862226bc79c13d143e07c1d8fc3 (patch)
tree06c7dc8826596690422e79d5bde2f46c90492de3 /src
parentInitial commit. (diff)
downloadlibnftnl-upstream.tar.xz
libnftnl-upstream.zip
Adding upstream version 1.2.6.upstream/1.2.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/Makefile.am73
-rw-r--r--src/Makefile.in1071
-rw-r--r--src/batch.c164
-rw-r--r--src/chain.c1172
-rw-r--r--src/common.c164
-rw-r--r--src/expr.c303
-rw-r--r--src/expr/bitwise.c286
-rw-r--r--src/expr/byteorder.c224
-rw-r--r--src/expr/cmp.c206
-rw-r--r--src/expr/connlimit.c139
-rw-r--r--src/expr/counter.c137
-rw-r--r--src/expr/ct.c262
-rw-r--r--src/expr/data_reg.c219
-rw-r--r--src/expr/dup.c142
-rw-r--r--src/expr/dynset.c377
-rw-r--r--src/expr/exthdr.c271
-rw-r--r--src/expr/fib.c202
-rw-r--r--src/expr/flow_offload.c124
-rw-r--r--src/expr/fwd.c162
-rw-r--r--src/expr/hash.c230
-rw-r--r--src/expr/immediate.c233
-rw-r--r--src/expr/inner.c214
-rw-r--r--src/expr/last.c138
-rw-r--r--src/expr/limit.c206
-rw-r--r--src/expr/log.c257
-rw-r--r--src/expr/lookup.c210
-rw-r--r--src/expr/masq.c167
-rw-r--r--src/expr/match.c193
-rw-r--r--src/expr/meta.c221
-rw-r--r--src/expr/nat.c278
-rw-r--r--src/expr/numgen.c184
-rw-r--r--src/expr/objref.c209
-rw-r--r--src/expr/osf.c151
-rw-r--r--src/expr/payload.c250
-rw-r--r--src/expr/queue.c197
-rw-r--r--src/expr/quota.c151
-rw-r--r--src/expr/range.c217
-rw-r--r--src/expr/redir.c171
-rw-r--r--src/expr/reject.c138
-rw-r--r--src/expr/rt.c166
-rw-r--r--src/expr/socket.c169
-rw-r--r--src/expr/synproxy.c156
-rw-r--r--src/expr/target.c193
-rw-r--r--src/expr/tproxy.c174
-rw-r--r--src/expr/tunnel.c149
-rw-r--r--src/expr/xfrm.c200
-rw-r--r--src/expr_ops.c106
-rw-r--r--src/flowtable.c735
-rw-r--r--src/gen.c196
-rw-r--r--src/libnftnl.map389
-rw-r--r--src/obj/counter.c131
-rw-r--r--src/obj/ct_expect.c200
-rw-r--r--src/obj/ct_helper.c154
-rw-r--r--src/obj/ct_timeout.c320
-rw-r--r--src/obj/limit.c172
-rw-r--r--src/obj/quota.c148
-rw-r--r--src/obj/secmark.c120
-rw-r--r--src/obj/synproxy.c147
-rw-r--r--src/obj/tunnel.c551
-rw-r--r--src/object.c562
-rw-r--r--src/rule.c876
-rw-r--r--src/ruleset.c719
-rw-r--r--src/set.c1066
-rw-r--r--src/set_elem.c918
-rw-r--r--src/table.c523
-rw-r--r--src/trace.c427
-rw-r--r--src/udata.c169
-rw-r--r--src/utils.c332
68 files changed, 19881 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..3cd259c
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,73 @@
+include $(top_srcdir)/Make_global.am
+lib_LTLIBRARIES = libnftnl.la
+
+libnftnl_la_LIBADD = ${LIBMNL_LIBS}
+libnftnl_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libnftnl.map \
+ -version-info $(LIBVERSION)
+
+libnftnl_la_SOURCES = utils.c \
+ batch.c \
+ flowtable.c \
+ common.c \
+ gen.c \
+ table.c \
+ trace.c \
+ chain.c \
+ object.c \
+ rule.c \
+ set.c \
+ set_elem.c \
+ ruleset.c \
+ udata.c \
+ expr.c \
+ expr_ops.c \
+ expr/bitwise.c \
+ expr/byteorder.c \
+ expr/cmp.c \
+ expr/range.c \
+ expr/connlimit.c \
+ expr/counter.c \
+ expr/ct.c \
+ expr/data_reg.c \
+ expr/dup.c \
+ expr/exthdr.c \
+ expr/flow_offload.c \
+ expr/fib.c \
+ expr/fwd.c \
+ expr/last.c \
+ expr/limit.c \
+ expr/log.c \
+ expr/lookup.c \
+ expr/dynset.c \
+ expr/immediate.c \
+ expr/inner.c \
+ expr/match.c \
+ expr/meta.c \
+ expr/numgen.c \
+ expr/nat.c \
+ expr/tproxy.c \
+ expr/objref.c \
+ expr/payload.c \
+ expr/queue.c \
+ expr/quota.c \
+ expr/reject.c \
+ expr/rt.c \
+ expr/target.c \
+ expr/tunnel.c \
+ expr/masq.c \
+ expr/redir.c \
+ expr/hash.c \
+ expr/socket.c \
+ expr/synproxy.c \
+ expr/osf.c \
+ expr/xfrm.c \
+ obj/counter.c \
+ obj/ct_helper.c \
+ obj/quota.c \
+ obj/tunnel.c \
+ obj/limit.c \
+ obj/synproxy.c \
+ obj/ct_timeout.c \
+ obj/secmark.c \
+ obj/ct_expect.c \
+ libnftnl.map
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..03130f7
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,1071 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/gcc4_visibility.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libnftnl_la_DEPENDENCIES = $(am__DEPENDENCIES_1)
+am__dirstamp = $(am__leading_dot)dirstamp
+am_libnftnl_la_OBJECTS = utils.lo batch.lo flowtable.lo common.lo \
+ gen.lo table.lo trace.lo chain.lo object.lo rule.lo set.lo \
+ set_elem.lo ruleset.lo udata.lo expr.lo expr_ops.lo \
+ expr/bitwise.lo expr/byteorder.lo expr/cmp.lo expr/range.lo \
+ expr/connlimit.lo expr/counter.lo expr/ct.lo expr/data_reg.lo \
+ expr/dup.lo expr/exthdr.lo expr/flow_offload.lo expr/fib.lo \
+ expr/fwd.lo expr/last.lo expr/limit.lo expr/log.lo \
+ expr/lookup.lo expr/dynset.lo expr/immediate.lo expr/inner.lo \
+ expr/match.lo expr/meta.lo expr/numgen.lo expr/nat.lo \
+ expr/tproxy.lo expr/objref.lo expr/payload.lo expr/queue.lo \
+ expr/quota.lo expr/reject.lo expr/rt.lo expr/target.lo \
+ expr/tunnel.lo expr/masq.lo expr/redir.lo expr/hash.lo \
+ expr/socket.lo expr/synproxy.lo expr/osf.lo expr/xfrm.lo \
+ obj/counter.lo obj/ct_helper.lo obj/quota.lo obj/tunnel.lo \
+ obj/limit.lo obj/synproxy.lo obj/ct_timeout.lo obj/secmark.lo \
+ obj/ct_expect.lo
+libnftnl_la_OBJECTS = $(am_libnftnl_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libnftnl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libnftnl_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/batch.Plo ./$(DEPDIR)/chain.Plo \
+ ./$(DEPDIR)/common.Plo ./$(DEPDIR)/expr.Plo \
+ ./$(DEPDIR)/expr_ops.Plo ./$(DEPDIR)/flowtable.Plo \
+ ./$(DEPDIR)/gen.Plo ./$(DEPDIR)/object.Plo \
+ ./$(DEPDIR)/rule.Plo ./$(DEPDIR)/ruleset.Plo \
+ ./$(DEPDIR)/set.Plo ./$(DEPDIR)/set_elem.Plo \
+ ./$(DEPDIR)/table.Plo ./$(DEPDIR)/trace.Plo \
+ ./$(DEPDIR)/udata.Plo ./$(DEPDIR)/utils.Plo \
+ expr/$(DEPDIR)/bitwise.Plo expr/$(DEPDIR)/byteorder.Plo \
+ expr/$(DEPDIR)/cmp.Plo expr/$(DEPDIR)/connlimit.Plo \
+ expr/$(DEPDIR)/counter.Plo expr/$(DEPDIR)/ct.Plo \
+ expr/$(DEPDIR)/data_reg.Plo expr/$(DEPDIR)/dup.Plo \
+ expr/$(DEPDIR)/dynset.Plo expr/$(DEPDIR)/exthdr.Plo \
+ expr/$(DEPDIR)/fib.Plo expr/$(DEPDIR)/flow_offload.Plo \
+ expr/$(DEPDIR)/fwd.Plo expr/$(DEPDIR)/hash.Plo \
+ expr/$(DEPDIR)/immediate.Plo expr/$(DEPDIR)/inner.Plo \
+ expr/$(DEPDIR)/last.Plo expr/$(DEPDIR)/limit.Plo \
+ expr/$(DEPDIR)/log.Plo expr/$(DEPDIR)/lookup.Plo \
+ expr/$(DEPDIR)/masq.Plo expr/$(DEPDIR)/match.Plo \
+ expr/$(DEPDIR)/meta.Plo expr/$(DEPDIR)/nat.Plo \
+ expr/$(DEPDIR)/numgen.Plo expr/$(DEPDIR)/objref.Plo \
+ expr/$(DEPDIR)/osf.Plo expr/$(DEPDIR)/payload.Plo \
+ expr/$(DEPDIR)/queue.Plo expr/$(DEPDIR)/quota.Plo \
+ expr/$(DEPDIR)/range.Plo expr/$(DEPDIR)/redir.Plo \
+ expr/$(DEPDIR)/reject.Plo expr/$(DEPDIR)/rt.Plo \
+ expr/$(DEPDIR)/socket.Plo expr/$(DEPDIR)/synproxy.Plo \
+ expr/$(DEPDIR)/target.Plo expr/$(DEPDIR)/tproxy.Plo \
+ expr/$(DEPDIR)/tunnel.Plo expr/$(DEPDIR)/xfrm.Plo \
+ obj/$(DEPDIR)/counter.Plo obj/$(DEPDIR)/ct_expect.Plo \
+ obj/$(DEPDIR)/ct_helper.Plo obj/$(DEPDIR)/ct_timeout.Plo \
+ obj/$(DEPDIR)/limit.Plo obj/$(DEPDIR)/quota.Plo \
+ obj/$(DEPDIR)/secmark.Plo obj/$(DEPDIR)/synproxy.Plo \
+ obj/$(DEPDIR)/tunnel.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libnftnl_la_SOURCES)
+DIST_SOURCES = $(libnftnl_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/Make_global.am \
+ $(top_srcdir)/build-aux/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GCC_FVISIBILITY_HIDDEN = @GCC_FVISIBILITY_HIDDEN@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBMNL_CFLAGS = @LIBMNL_CFLAGS@
+LIBMNL_LIBS = @LIBMNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+regular_CFLAGS = @regular_CFLAGS@
+regular_CPPFLAGS = @regular_CPPFLAGS@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+# This is _NOT_ the library release version, it's an API version.
+# Extracted from Chapter 6 "Library interface versions" of the libtool docs.
+#
+# <snippet>
+# Here are a set of rules to help you update your library version information:
+#
+# 1. Start with version information of `0:0:0' for each libtool library.
+# 2. Update the version information only immediately before a public release
+# of your software. More frequent updates are unnecessary, and only guarantee
+# that the current interface number gets larger faster.
+# 3. If the library source code has changed at all since the last update,
+# then increment revision (`c:r:a' becomes `c:r+1:a').
+# 4. If any interfaces have been added, removed, or changed since the last
+# update, increment current, and set revision to 0.
+# 5. If any interfaces have been added since the last public release, then
+# increment age.
+# 6. If any interfaces have been removed since the last public release, then
+# set age to 0.
+# </snippet>
+#
+LIBVERSION = 17:0:6
+AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_srcdir}/include ${LIBMNL_CFLAGS} ${LIBMXML_CFLAGS}
+AM_CFLAGS = ${regular_CFLAGS} ${GCC_FVISIBILITY_HIDDEN}
+lib_LTLIBRARIES = libnftnl.la
+libnftnl_la_LIBADD = ${LIBMNL_LIBS}
+libnftnl_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libnftnl.map \
+ -version-info $(LIBVERSION)
+
+libnftnl_la_SOURCES = utils.c \
+ batch.c \
+ flowtable.c \
+ common.c \
+ gen.c \
+ table.c \
+ trace.c \
+ chain.c \
+ object.c \
+ rule.c \
+ set.c \
+ set_elem.c \
+ ruleset.c \
+ udata.c \
+ expr.c \
+ expr_ops.c \
+ expr/bitwise.c \
+ expr/byteorder.c \
+ expr/cmp.c \
+ expr/range.c \
+ expr/connlimit.c \
+ expr/counter.c \
+ expr/ct.c \
+ expr/data_reg.c \
+ expr/dup.c \
+ expr/exthdr.c \
+ expr/flow_offload.c \
+ expr/fib.c \
+ expr/fwd.c \
+ expr/last.c \
+ expr/limit.c \
+ expr/log.c \
+ expr/lookup.c \
+ expr/dynset.c \
+ expr/immediate.c \
+ expr/inner.c \
+ expr/match.c \
+ expr/meta.c \
+ expr/numgen.c \
+ expr/nat.c \
+ expr/tproxy.c \
+ expr/objref.c \
+ expr/payload.c \
+ expr/queue.c \
+ expr/quota.c \
+ expr/reject.c \
+ expr/rt.c \
+ expr/target.c \
+ expr/tunnel.c \
+ expr/masq.c \
+ expr/redir.c \
+ expr/hash.c \
+ expr/socket.c \
+ expr/synproxy.c \
+ expr/osf.c \
+ expr/xfrm.c \
+ obj/counter.c \
+ obj/ct_helper.c \
+ obj/quota.c \
+ obj/tunnel.c \
+ obj/limit.c \
+ obj/synproxy.c \
+ obj/ct_timeout.c \
+ obj/secmark.c \
+ obj/ct_expect.c \
+ libnftnl.map
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/Make_global.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/Make_global.am $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+expr/$(am__dirstamp):
+ @$(MKDIR_P) expr
+ @: > expr/$(am__dirstamp)
+expr/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) expr/$(DEPDIR)
+ @: > expr/$(DEPDIR)/$(am__dirstamp)
+expr/bitwise.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/byteorder.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/cmp.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/range.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/connlimit.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/counter.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/ct.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/data_reg.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/dup.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/exthdr.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/flow_offload.lo: expr/$(am__dirstamp) \
+ expr/$(DEPDIR)/$(am__dirstamp)
+expr/fib.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/fwd.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/last.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/limit.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/log.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/lookup.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/dynset.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/immediate.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/inner.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/match.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/meta.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/numgen.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/nat.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/tproxy.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/objref.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/payload.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/queue.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/quota.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/reject.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/rt.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/target.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/tunnel.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/masq.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/redir.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/hash.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/socket.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/synproxy.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/osf.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+expr/xfrm.lo: expr/$(am__dirstamp) expr/$(DEPDIR)/$(am__dirstamp)
+obj/$(am__dirstamp):
+ @$(MKDIR_P) obj
+ @: > obj/$(am__dirstamp)
+obj/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) obj/$(DEPDIR)
+ @: > obj/$(DEPDIR)/$(am__dirstamp)
+obj/counter.lo: obj/$(am__dirstamp) obj/$(DEPDIR)/$(am__dirstamp)
+obj/ct_helper.lo: obj/$(am__dirstamp) obj/$(DEPDIR)/$(am__dirstamp)
+obj/quota.lo: obj/$(am__dirstamp) obj/$(DEPDIR)/$(am__dirstamp)
+obj/tunnel.lo: obj/$(am__dirstamp) obj/$(DEPDIR)/$(am__dirstamp)
+obj/limit.lo: obj/$(am__dirstamp) obj/$(DEPDIR)/$(am__dirstamp)
+obj/synproxy.lo: obj/$(am__dirstamp) obj/$(DEPDIR)/$(am__dirstamp)
+obj/ct_timeout.lo: obj/$(am__dirstamp) obj/$(DEPDIR)/$(am__dirstamp)
+obj/secmark.lo: obj/$(am__dirstamp) obj/$(DEPDIR)/$(am__dirstamp)
+obj/ct_expect.lo: obj/$(am__dirstamp) obj/$(DEPDIR)/$(am__dirstamp)
+
+libnftnl.la: $(libnftnl_la_OBJECTS) $(libnftnl_la_DEPENDENCIES) $(EXTRA_libnftnl_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libnftnl_la_LINK) -rpath $(libdir) $(libnftnl_la_OBJECTS) $(libnftnl_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+ -rm -f expr/*.$(OBJEXT)
+ -rm -f expr/*.lo
+ -rm -f obj/*.$(OBJEXT)
+ -rm -f obj/*.lo
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/batch.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chain.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/expr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/expr_ops.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flowtable.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gen.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/object.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rule.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ruleset.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/set.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/set_elem.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/table.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trace.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/udata.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/bitwise.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/byteorder.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/cmp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/connlimit.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/counter.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/ct.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/data_reg.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/dup.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/dynset.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/exthdr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/fib.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/flow_offload.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/fwd.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/hash.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/immediate.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/inner.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/last.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/limit.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/log.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/lookup.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/masq.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/match.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/meta.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/nat.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/numgen.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/objref.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/osf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/payload.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/queue.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/quota.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/range.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/redir.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/reject.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/rt.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/socket.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/synproxy.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/target.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/tproxy.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/tunnel.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@expr/$(DEPDIR)/xfrm.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@obj/$(DEPDIR)/counter.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@obj/$(DEPDIR)/ct_expect.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@obj/$(DEPDIR)/ct_helper.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@obj/$(DEPDIR)/ct_timeout.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@obj/$(DEPDIR)/limit.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@obj/$(DEPDIR)/quota.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@obj/$(DEPDIR)/secmark.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@obj/$(DEPDIR)/synproxy.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@obj/$(DEPDIR)/tunnel.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+ -rm -rf expr/.libs expr/_libs
+ -rm -rf obj/.libs obj/_libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -rm -f expr/$(DEPDIR)/$(am__dirstamp)
+ -rm -f expr/$(am__dirstamp)
+ -rm -f obj/$(DEPDIR)/$(am__dirstamp)
+ -rm -f obj/$(am__dirstamp)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/batch.Plo
+ -rm -f ./$(DEPDIR)/chain.Plo
+ -rm -f ./$(DEPDIR)/common.Plo
+ -rm -f ./$(DEPDIR)/expr.Plo
+ -rm -f ./$(DEPDIR)/expr_ops.Plo
+ -rm -f ./$(DEPDIR)/flowtable.Plo
+ -rm -f ./$(DEPDIR)/gen.Plo
+ -rm -f ./$(DEPDIR)/object.Plo
+ -rm -f ./$(DEPDIR)/rule.Plo
+ -rm -f ./$(DEPDIR)/ruleset.Plo
+ -rm -f ./$(DEPDIR)/set.Plo
+ -rm -f ./$(DEPDIR)/set_elem.Plo
+ -rm -f ./$(DEPDIR)/table.Plo
+ -rm -f ./$(DEPDIR)/trace.Plo
+ -rm -f ./$(DEPDIR)/udata.Plo
+ -rm -f ./$(DEPDIR)/utils.Plo
+ -rm -f expr/$(DEPDIR)/bitwise.Plo
+ -rm -f expr/$(DEPDIR)/byteorder.Plo
+ -rm -f expr/$(DEPDIR)/cmp.Plo
+ -rm -f expr/$(DEPDIR)/connlimit.Plo
+ -rm -f expr/$(DEPDIR)/counter.Plo
+ -rm -f expr/$(DEPDIR)/ct.Plo
+ -rm -f expr/$(DEPDIR)/data_reg.Plo
+ -rm -f expr/$(DEPDIR)/dup.Plo
+ -rm -f expr/$(DEPDIR)/dynset.Plo
+ -rm -f expr/$(DEPDIR)/exthdr.Plo
+ -rm -f expr/$(DEPDIR)/fib.Plo
+ -rm -f expr/$(DEPDIR)/flow_offload.Plo
+ -rm -f expr/$(DEPDIR)/fwd.Plo
+ -rm -f expr/$(DEPDIR)/hash.Plo
+ -rm -f expr/$(DEPDIR)/immediate.Plo
+ -rm -f expr/$(DEPDIR)/inner.Plo
+ -rm -f expr/$(DEPDIR)/last.Plo
+ -rm -f expr/$(DEPDIR)/limit.Plo
+ -rm -f expr/$(DEPDIR)/log.Plo
+ -rm -f expr/$(DEPDIR)/lookup.Plo
+ -rm -f expr/$(DEPDIR)/masq.Plo
+ -rm -f expr/$(DEPDIR)/match.Plo
+ -rm -f expr/$(DEPDIR)/meta.Plo
+ -rm -f expr/$(DEPDIR)/nat.Plo
+ -rm -f expr/$(DEPDIR)/numgen.Plo
+ -rm -f expr/$(DEPDIR)/objref.Plo
+ -rm -f expr/$(DEPDIR)/osf.Plo
+ -rm -f expr/$(DEPDIR)/payload.Plo
+ -rm -f expr/$(DEPDIR)/queue.Plo
+ -rm -f expr/$(DEPDIR)/quota.Plo
+ -rm -f expr/$(DEPDIR)/range.Plo
+ -rm -f expr/$(DEPDIR)/redir.Plo
+ -rm -f expr/$(DEPDIR)/reject.Plo
+ -rm -f expr/$(DEPDIR)/rt.Plo
+ -rm -f expr/$(DEPDIR)/socket.Plo
+ -rm -f expr/$(DEPDIR)/synproxy.Plo
+ -rm -f expr/$(DEPDIR)/target.Plo
+ -rm -f expr/$(DEPDIR)/tproxy.Plo
+ -rm -f expr/$(DEPDIR)/tunnel.Plo
+ -rm -f expr/$(DEPDIR)/xfrm.Plo
+ -rm -f obj/$(DEPDIR)/counter.Plo
+ -rm -f obj/$(DEPDIR)/ct_expect.Plo
+ -rm -f obj/$(DEPDIR)/ct_helper.Plo
+ -rm -f obj/$(DEPDIR)/ct_timeout.Plo
+ -rm -f obj/$(DEPDIR)/limit.Plo
+ -rm -f obj/$(DEPDIR)/quota.Plo
+ -rm -f obj/$(DEPDIR)/secmark.Plo
+ -rm -f obj/$(DEPDIR)/synproxy.Plo
+ -rm -f obj/$(DEPDIR)/tunnel.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/batch.Plo
+ -rm -f ./$(DEPDIR)/chain.Plo
+ -rm -f ./$(DEPDIR)/common.Plo
+ -rm -f ./$(DEPDIR)/expr.Plo
+ -rm -f ./$(DEPDIR)/expr_ops.Plo
+ -rm -f ./$(DEPDIR)/flowtable.Plo
+ -rm -f ./$(DEPDIR)/gen.Plo
+ -rm -f ./$(DEPDIR)/object.Plo
+ -rm -f ./$(DEPDIR)/rule.Plo
+ -rm -f ./$(DEPDIR)/ruleset.Plo
+ -rm -f ./$(DEPDIR)/set.Plo
+ -rm -f ./$(DEPDIR)/set_elem.Plo
+ -rm -f ./$(DEPDIR)/table.Plo
+ -rm -f ./$(DEPDIR)/trace.Plo
+ -rm -f ./$(DEPDIR)/udata.Plo
+ -rm -f ./$(DEPDIR)/utils.Plo
+ -rm -f expr/$(DEPDIR)/bitwise.Plo
+ -rm -f expr/$(DEPDIR)/byteorder.Plo
+ -rm -f expr/$(DEPDIR)/cmp.Plo
+ -rm -f expr/$(DEPDIR)/connlimit.Plo
+ -rm -f expr/$(DEPDIR)/counter.Plo
+ -rm -f expr/$(DEPDIR)/ct.Plo
+ -rm -f expr/$(DEPDIR)/data_reg.Plo
+ -rm -f expr/$(DEPDIR)/dup.Plo
+ -rm -f expr/$(DEPDIR)/dynset.Plo
+ -rm -f expr/$(DEPDIR)/exthdr.Plo
+ -rm -f expr/$(DEPDIR)/fib.Plo
+ -rm -f expr/$(DEPDIR)/flow_offload.Plo
+ -rm -f expr/$(DEPDIR)/fwd.Plo
+ -rm -f expr/$(DEPDIR)/hash.Plo
+ -rm -f expr/$(DEPDIR)/immediate.Plo
+ -rm -f expr/$(DEPDIR)/inner.Plo
+ -rm -f expr/$(DEPDIR)/last.Plo
+ -rm -f expr/$(DEPDIR)/limit.Plo
+ -rm -f expr/$(DEPDIR)/log.Plo
+ -rm -f expr/$(DEPDIR)/lookup.Plo
+ -rm -f expr/$(DEPDIR)/masq.Plo
+ -rm -f expr/$(DEPDIR)/match.Plo
+ -rm -f expr/$(DEPDIR)/meta.Plo
+ -rm -f expr/$(DEPDIR)/nat.Plo
+ -rm -f expr/$(DEPDIR)/numgen.Plo
+ -rm -f expr/$(DEPDIR)/objref.Plo
+ -rm -f expr/$(DEPDIR)/osf.Plo
+ -rm -f expr/$(DEPDIR)/payload.Plo
+ -rm -f expr/$(DEPDIR)/queue.Plo
+ -rm -f expr/$(DEPDIR)/quota.Plo
+ -rm -f expr/$(DEPDIR)/range.Plo
+ -rm -f expr/$(DEPDIR)/redir.Plo
+ -rm -f expr/$(DEPDIR)/reject.Plo
+ -rm -f expr/$(DEPDIR)/rt.Plo
+ -rm -f expr/$(DEPDIR)/socket.Plo
+ -rm -f expr/$(DEPDIR)/synproxy.Plo
+ -rm -f expr/$(DEPDIR)/target.Plo
+ -rm -f expr/$(DEPDIR)/tproxy.Plo
+ -rm -f expr/$(DEPDIR)/tunnel.Plo
+ -rm -f expr/$(DEPDIR)/xfrm.Plo
+ -rm -f obj/$(DEPDIR)/counter.Plo
+ -rm -f obj/$(DEPDIR)/ct_expect.Plo
+ -rm -f obj/$(DEPDIR)/ct_helper.Plo
+ -rm -f obj/$(DEPDIR)/ct_timeout.Plo
+ -rm -f obj/$(DEPDIR)/limit.Plo
+ -rm -f obj/$(DEPDIR)/quota.Plo
+ -rm -f obj/$(DEPDIR)/secmark.Plo
+ -rm -f obj/$(DEPDIR)/synproxy.Plo
+ -rm -f obj/$(DEPDIR)/tunnel.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libLTLIBRARIES clean-libtool cscopelist-am \
+ ctags ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-libLTLIBRARIES install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/batch.c b/src/batch.c
new file mode 100644
index 0000000..8a9c6f9
--- /dev/null
+++ b/src/batch.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2013-2015 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 "internal.h"
+#include <errno.h>
+#include <libmnl/libmnl.h>
+#include <libnftnl/batch.h>
+
+struct nftnl_batch {
+ uint32_t num_pages;
+ struct nftnl_batch_page *current_page;
+ uint32_t page_size;
+ uint32_t page_overrun_size;
+ struct list_head page_list;
+};
+
+struct nftnl_batch_page {
+ struct list_head head;
+ struct mnl_nlmsg_batch *batch;
+};
+
+static struct nftnl_batch_page *nftnl_batch_page_alloc(struct nftnl_batch *batch)
+{
+ struct nftnl_batch_page *page;
+ char *buf;
+
+ page = malloc(sizeof(struct nftnl_batch_page));
+ if (page == NULL)
+ return NULL;
+
+ buf = malloc(batch->page_size + batch->page_overrun_size);
+ if (buf == NULL)
+ goto err1;
+
+ page->batch = mnl_nlmsg_batch_start(buf, batch->page_size);
+ if (page->batch == NULL)
+ goto err2;
+
+ return page;
+err2:
+ free(buf);
+err1:
+ free(page);
+ return NULL;
+}
+
+static void nftnl_batch_add_page(struct nftnl_batch_page *page,
+ struct nftnl_batch *batch)
+{
+ batch->current_page = page;
+ batch->num_pages++;
+ list_add_tail(&page->head, &batch->page_list);
+}
+
+EXPORT_SYMBOL(nftnl_batch_alloc);
+struct nftnl_batch *nftnl_batch_alloc(uint32_t pg_size, uint32_t pg_overrun_size)
+{
+ struct nftnl_batch *batch;
+ struct nftnl_batch_page *page;
+
+ batch = calloc(1, sizeof(struct nftnl_batch));
+ if (batch == NULL)
+ return NULL;
+
+ batch->page_size = pg_size;
+ batch->page_overrun_size = pg_overrun_size;
+ INIT_LIST_HEAD(&batch->page_list);
+
+ page = nftnl_batch_page_alloc(batch);
+ if (page == NULL)
+ goto err1;
+
+ nftnl_batch_add_page(page, batch);
+ return batch;
+err1:
+ free(batch);
+ return NULL;
+}
+
+EXPORT_SYMBOL(nftnl_batch_free);
+void nftnl_batch_free(struct nftnl_batch *batch)
+{
+ struct nftnl_batch_page *page, *next;
+
+ list_for_each_entry_safe(page, next, &batch->page_list, head) {
+ free(mnl_nlmsg_batch_head(page->batch));
+ mnl_nlmsg_batch_stop(page->batch);
+ free(page);
+ }
+
+ free(batch);
+}
+
+EXPORT_SYMBOL(nftnl_batch_update);
+int nftnl_batch_update(struct nftnl_batch *batch)
+{
+ struct nftnl_batch_page *page;
+ struct nlmsghdr *last_nlh;
+
+ if (mnl_nlmsg_batch_next(batch->current_page->batch))
+ return 0;
+
+ last_nlh = nftnl_batch_buffer(batch);
+
+ page = nftnl_batch_page_alloc(batch);
+ if (page == NULL)
+ goto err1;
+
+ nftnl_batch_add_page(page, batch);
+
+ memcpy(nftnl_batch_buffer(batch), last_nlh, last_nlh->nlmsg_len);
+ mnl_nlmsg_batch_next(batch->current_page->batch);
+
+ return 0;
+err1:
+ return -1;
+}
+
+EXPORT_SYMBOL(nftnl_batch_buffer);
+void *nftnl_batch_buffer(struct nftnl_batch *batch)
+{
+ return mnl_nlmsg_batch_current(batch->current_page->batch);
+}
+
+EXPORT_SYMBOL(nftnl_batch_buffer_len);
+uint32_t nftnl_batch_buffer_len(struct nftnl_batch *batch)
+{
+ return mnl_nlmsg_batch_size(batch->current_page->batch);
+}
+
+EXPORT_SYMBOL(nftnl_batch_iovec_len);
+int nftnl_batch_iovec_len(struct nftnl_batch *batch)
+{
+ int num_pages = batch->num_pages;
+
+ /* Skip last page if it's empty */
+ if (mnl_nlmsg_batch_is_empty(batch->current_page->batch))
+ num_pages--;
+
+ return num_pages;
+}
+
+EXPORT_SYMBOL(nftnl_batch_iovec);
+void nftnl_batch_iovec(struct nftnl_batch *batch, struct iovec *iov,
+ uint32_t iovlen)
+{
+ struct nftnl_batch_page *page;
+ int i = 0;
+
+ list_for_each_entry(page, &batch->page_list, head) {
+ if (i >= iovlen)
+ break;
+
+ iov[i].iov_base = mnl_nlmsg_batch_head(page->batch);
+ iov[i].iov_len = mnl_nlmsg_batch_size(page->batch);
+ i++;
+ }
+}
diff --git a/src/chain.c b/src/chain.c
new file mode 100644
index 0000000..dcfcd04
--- /dev/null
+++ b/src/chain.c
@@ -0,0 +1,1172 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+#include "internal.h"
+
+#include <time.h>
+#include <endian.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_arp.h>
+
+#include <libnftnl/chain.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_chain {
+ struct list_head head;
+ struct hlist_node hnode;
+
+ const char *name;
+ const char *type;
+ const char *table;
+ const char *dev;
+ const char **dev_array;
+ int dev_array_len;
+ uint32_t family;
+ uint32_t policy;
+ uint32_t hooknum;
+ int32_t prio;
+ uint32_t chain_flags;
+ uint32_t use;
+ uint64_t packets;
+ uint64_t bytes;
+ uint64_t handle;
+ uint32_t flags;
+ uint32_t chain_id;
+
+ struct {
+ void *data;
+ uint32_t len;
+ } user;
+
+ struct list_head rule_list;
+};
+
+static const char *nftnl_hooknum2str(int family, int hooknum)
+{
+ switch (family) {
+ case NFPROTO_IPV4:
+ case NFPROTO_IPV6:
+ case NFPROTO_INET:
+ case NFPROTO_BRIDGE:
+ switch (hooknum) {
+ case NF_INET_PRE_ROUTING:
+ return "prerouting";
+ case NF_INET_LOCAL_IN:
+ return "input";
+ case NF_INET_FORWARD:
+ return "forward";
+ case NF_INET_LOCAL_OUT:
+ return "output";
+ case NF_INET_POST_ROUTING:
+ return "postrouting";
+ }
+ break;
+ case NFPROTO_ARP:
+ switch (hooknum) {
+ case NF_ARP_IN:
+ return "input";
+ case NF_ARP_OUT:
+ return "output";
+ case NF_ARP_FORWARD:
+ return "forward";
+ }
+ break;
+ case NFPROTO_NETDEV:
+ switch (hooknum) {
+ case NF_NETDEV_INGRESS:
+ return "ingress";
+ }
+ break;
+ }
+ return "unknown";
+}
+
+EXPORT_SYMBOL(nftnl_chain_alloc);
+struct nftnl_chain *nftnl_chain_alloc(void)
+{
+ struct nftnl_chain *c;
+
+ c = calloc(1, sizeof(struct nftnl_chain));
+ if (c == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&c->rule_list);
+
+ return c;
+}
+
+EXPORT_SYMBOL(nftnl_chain_free);
+void nftnl_chain_free(const struct nftnl_chain *c)
+{
+ struct nftnl_rule *r, *tmp;
+ int i;
+
+ list_for_each_entry_safe(r, tmp, &c->rule_list, head)
+ nftnl_rule_free(r);
+
+ if (c->flags & (1 << NFTNL_CHAIN_NAME))
+ xfree(c->name);
+ if (c->flags & (1 << NFTNL_CHAIN_TABLE))
+ xfree(c->table);
+ if (c->flags & (1 << NFTNL_CHAIN_TYPE))
+ xfree(c->type);
+ if (c->flags & (1 << NFTNL_CHAIN_DEV))
+ xfree(c->dev);
+ if (c->flags & (1 << NFTNL_CHAIN_USERDATA))
+ xfree(c->user.data);
+ if (c->flags & (1 << NFTNL_CHAIN_DEVICES)) {
+ for (i = 0; i < c->dev_array_len; i++)
+ xfree(c->dev_array[i]);
+
+ xfree(c->dev_array);
+ }
+ xfree(c);
+}
+
+EXPORT_SYMBOL(nftnl_chain_is_set);
+bool nftnl_chain_is_set(const struct nftnl_chain *c, uint16_t attr)
+{
+ return c->flags & (1 << attr);
+}
+
+EXPORT_SYMBOL(nftnl_chain_unset);
+void nftnl_chain_unset(struct nftnl_chain *c, uint16_t attr)
+{
+ int i;
+
+ if (!(c->flags & (1 << attr)))
+ return;
+
+ switch (attr) {
+ case NFTNL_CHAIN_NAME:
+ xfree(c->name);
+ break;
+ case NFTNL_CHAIN_TABLE:
+ xfree(c->table);
+ break;
+ case NFTNL_CHAIN_USE:
+ break;
+ case NFTNL_CHAIN_TYPE:
+ xfree(c->type);
+ break;
+ case NFTNL_CHAIN_HOOKNUM:
+ case NFTNL_CHAIN_PRIO:
+ case NFTNL_CHAIN_POLICY:
+ case NFTNL_CHAIN_BYTES:
+ case NFTNL_CHAIN_PACKETS:
+ case NFTNL_CHAIN_HANDLE:
+ case NFTNL_CHAIN_FAMILY:
+ case NFTNL_CHAIN_FLAGS:
+ case NFTNL_CHAIN_ID:
+ break;
+ case NFTNL_CHAIN_DEV:
+ xfree(c->dev);
+ break;
+ case NFTNL_CHAIN_DEVICES:
+ for (i = 0; i < c->dev_array_len; i++)
+ xfree(c->dev_array[i]);
+ xfree(c->dev_array);
+ break;
+ default:
+ return;
+ }
+
+ c->flags &= ~(1 << attr);
+}
+
+static uint32_t nftnl_chain_validate[NFTNL_CHAIN_MAX + 1] = {
+ [NFTNL_CHAIN_HOOKNUM] = sizeof(uint32_t),
+ [NFTNL_CHAIN_PRIO] = sizeof(int32_t),
+ [NFTNL_CHAIN_POLICY] = sizeof(uint32_t),
+ [NFTNL_CHAIN_BYTES] = sizeof(uint64_t),
+ [NFTNL_CHAIN_PACKETS] = sizeof(uint64_t),
+ [NFTNL_CHAIN_HANDLE] = sizeof(uint64_t),
+ [NFTNL_CHAIN_FAMILY] = sizeof(uint32_t),
+ [NFTNL_CHAIN_FLAGS] = sizeof(uint32_t),
+ [NFTNL_CHAIN_ID] = sizeof(uint32_t),
+};
+
+EXPORT_SYMBOL(nftnl_chain_set_data);
+int nftnl_chain_set_data(struct nftnl_chain *c, uint16_t attr,
+ const void *data, uint32_t data_len)
+{
+ const char **dev_array;
+ int len = 0, i;
+
+ nftnl_assert_attr_exists(attr, NFTNL_CHAIN_MAX);
+ nftnl_assert_validate(data, nftnl_chain_validate, attr, data_len);
+
+ switch(attr) {
+ case NFTNL_CHAIN_NAME:
+ if (c->flags & (1 << NFTNL_CHAIN_NAME))
+ xfree(c->name);
+
+ c->name = strdup(data);
+ if (!c->name)
+ return -1;
+ break;
+ case NFTNL_CHAIN_TABLE:
+ if (c->flags & (1 << NFTNL_CHAIN_TABLE))
+ xfree(c->table);
+
+ c->table = strdup(data);
+ if (!c->table)
+ return -1;
+ break;
+ case NFTNL_CHAIN_HOOKNUM:
+ memcpy(&c->hooknum, data, sizeof(c->hooknum));
+ break;
+ case NFTNL_CHAIN_PRIO:
+ memcpy(&c->prio, data, sizeof(c->prio));
+ break;
+ case NFTNL_CHAIN_POLICY:
+ memcpy(&c->policy, data, sizeof(c->policy));
+ break;
+ case NFTNL_CHAIN_USE:
+ memcpy(&c->use, data, sizeof(c->use));
+ break;
+ case NFTNL_CHAIN_BYTES:
+ memcpy(&c->bytes, data, sizeof(c->bytes));
+ break;
+ case NFTNL_CHAIN_PACKETS:
+ memcpy(&c->packets, data, sizeof(c->packets));
+ break;
+ case NFTNL_CHAIN_HANDLE:
+ memcpy(&c->handle, data, sizeof(c->handle));
+ break;
+ case NFTNL_CHAIN_FAMILY:
+ memcpy(&c->family, data, sizeof(c->family));
+ break;
+ case NFTNL_CHAIN_TYPE:
+ if (c->flags & (1 << NFTNL_CHAIN_TYPE))
+ xfree(c->type);
+
+ c->type = strdup(data);
+ if (!c->type)
+ return -1;
+ break;
+ case NFTNL_CHAIN_DEV:
+ if (c->flags & (1 << NFTNL_CHAIN_DEV))
+ xfree(c->dev);
+
+ c->dev = strdup(data);
+ if (!c->dev)
+ return -1;
+ break;
+ case NFTNL_CHAIN_DEVICES:
+ dev_array = (const char **)data;
+ while (dev_array[len] != NULL)
+ len++;
+
+ if (c->flags & (1 << NFTNL_CHAIN_DEVICES)) {
+ for (i = 0; i < c->dev_array_len; i++)
+ xfree(c->dev_array[i]);
+ xfree(c->dev_array);
+ }
+
+ c->dev_array = calloc(len + 1, sizeof(char *));
+ if (!c->dev_array)
+ return -1;
+
+ for (i = 0; i < len; i++)
+ c->dev_array[i] = strdup(dev_array[i]);
+
+ c->dev_array_len = len;
+ break;
+ case NFTNL_CHAIN_FLAGS:
+ memcpy(&c->chain_flags, data, sizeof(c->chain_flags));
+ break;
+ case NFTNL_CHAIN_ID:
+ memcpy(&c->chain_id, data, sizeof(c->chain_id));
+ break;
+ case NFTNL_CHAIN_USERDATA:
+ if (c->flags & (1 << NFTNL_CHAIN_USERDATA))
+ xfree(c->user.data);
+
+ c->user.data = malloc(data_len);
+ if (!c->user.data)
+ return -1;
+ memcpy(c->user.data, data, data_len);
+ c->user.len = data_len;
+ break;
+ }
+ c->flags |= (1 << attr);
+ return 0;
+}
+
+void nftnl_chain_set(struct nftnl_chain *c, uint16_t attr, const void *data) __visible;
+void nftnl_chain_set(struct nftnl_chain *c, uint16_t attr, const void *data)
+{
+ nftnl_chain_set_data(c, attr, data, nftnl_chain_validate[attr]);
+}
+
+EXPORT_SYMBOL(nftnl_chain_set_u32);
+void nftnl_chain_set_u32(struct nftnl_chain *c, uint16_t attr, uint32_t data)
+{
+ nftnl_chain_set_data(c, attr, &data, sizeof(uint32_t));
+}
+
+EXPORT_SYMBOL(nftnl_chain_set_s32);
+void nftnl_chain_set_s32(struct nftnl_chain *c, uint16_t attr, int32_t data)
+{
+ nftnl_chain_set_data(c, attr, &data, sizeof(int32_t));
+}
+
+EXPORT_SYMBOL(nftnl_chain_set_u64);
+void nftnl_chain_set_u64(struct nftnl_chain *c, uint16_t attr, uint64_t data)
+{
+ nftnl_chain_set_data(c, attr, &data, sizeof(uint64_t));
+}
+
+EXPORT_SYMBOL(nftnl_chain_set_u8);
+void nftnl_chain_set_u8(struct nftnl_chain *c, uint16_t attr, uint8_t data)
+{
+ nftnl_chain_set_data(c, attr, &data, sizeof(uint8_t));
+}
+
+EXPORT_SYMBOL(nftnl_chain_set_str);
+int nftnl_chain_set_str(struct nftnl_chain *c, uint16_t attr, const char *str)
+{
+ return nftnl_chain_set_data(c, attr, str, strlen(str) + 1);
+}
+
+EXPORT_SYMBOL(nftnl_chain_set_array);
+int nftnl_chain_set_array(struct nftnl_chain *c, uint16_t attr,
+ const char **data)
+{
+ return nftnl_chain_set_data(c, attr, data, 0);
+}
+
+EXPORT_SYMBOL(nftnl_chain_get_data);
+const void *nftnl_chain_get_data(const struct nftnl_chain *c, uint16_t attr,
+ uint32_t *data_len)
+{
+ if (!(c->flags & (1 << attr)))
+ return NULL;
+
+ switch(attr) {
+ case NFTNL_CHAIN_NAME:
+ *data_len = strlen(c->name) + 1;
+ return c->name;
+ case NFTNL_CHAIN_TABLE:
+ *data_len = strlen(c->table) + 1;
+ return c->table;
+ case NFTNL_CHAIN_HOOKNUM:
+ *data_len = sizeof(uint32_t);
+ return &c->hooknum;
+ case NFTNL_CHAIN_PRIO:
+ *data_len = sizeof(int32_t);
+ return &c->prio;
+ case NFTNL_CHAIN_POLICY:
+ *data_len = sizeof(uint32_t);
+ return &c->policy;
+ case NFTNL_CHAIN_USE:
+ *data_len = sizeof(uint32_t);
+ return &c->use;
+ case NFTNL_CHAIN_BYTES:
+ *data_len = sizeof(uint64_t);
+ return &c->bytes;
+ case NFTNL_CHAIN_PACKETS:
+ *data_len = sizeof(uint64_t);
+ return &c->packets;
+ case NFTNL_CHAIN_HANDLE:
+ *data_len = sizeof(uint64_t);
+ return &c->handle;
+ case NFTNL_CHAIN_FAMILY:
+ *data_len = sizeof(uint32_t);
+ return &c->family;
+ case NFTNL_CHAIN_TYPE:
+ *data_len = sizeof(uint32_t);
+ return c->type;
+ case NFTNL_CHAIN_DEV:
+ *data_len = strlen(c->dev) + 1;
+ return c->dev;
+ case NFTNL_CHAIN_DEVICES:
+ *data_len = 0;
+ return &c->dev_array[0];
+ case NFTNL_CHAIN_FLAGS:
+ *data_len = sizeof(uint32_t);
+ return &c->chain_flags;
+ case NFTNL_CHAIN_ID:
+ *data_len = sizeof(uint32_t);
+ return &c->chain_id;
+ case NFTNL_CHAIN_USERDATA:
+ *data_len = c->user.len;
+ return c->user.data;
+ }
+ return NULL;
+}
+
+EXPORT_SYMBOL(nftnl_chain_get);
+const void *nftnl_chain_get(const struct nftnl_chain *c, uint16_t attr)
+{
+ uint32_t data_len;
+ return nftnl_chain_get_data(c, attr, &data_len);
+}
+
+EXPORT_SYMBOL(nftnl_chain_get_str);
+const char *nftnl_chain_get_str(const struct nftnl_chain *c, uint16_t attr)
+{
+ return nftnl_chain_get(c, attr);
+}
+
+EXPORT_SYMBOL(nftnl_chain_get_u32);
+uint32_t nftnl_chain_get_u32(const struct nftnl_chain *c, uint16_t attr)
+{
+ uint32_t data_len;
+ const uint32_t *val = nftnl_chain_get_data(c, attr, &data_len);
+
+ nftnl_assert(val, attr, data_len == sizeof(uint32_t));
+
+ return val ? *val : 0;
+}
+
+EXPORT_SYMBOL(nftnl_chain_get_s32);
+int32_t nftnl_chain_get_s32(const struct nftnl_chain *c, uint16_t attr)
+{
+ uint32_t data_len;
+ const int32_t *val = nftnl_chain_get_data(c, attr, &data_len);
+
+ nftnl_assert(val, attr, data_len == sizeof(int32_t));
+
+ return val ? *val : 0;
+}
+
+EXPORT_SYMBOL(nftnl_chain_get_u64);
+uint64_t nftnl_chain_get_u64(const struct nftnl_chain *c, uint16_t attr)
+{
+ uint32_t data_len;
+ const uint64_t *val = nftnl_chain_get_data(c, attr, &data_len);
+
+ nftnl_assert(val, attr, data_len == sizeof(int64_t));
+
+ return val ? *val : 0;
+}
+
+EXPORT_SYMBOL(nftnl_chain_get_u8);
+uint8_t nftnl_chain_get_u8(const struct nftnl_chain *c, uint16_t attr)
+{
+ uint32_t data_len;
+ const uint8_t *val = nftnl_chain_get_data(c, attr, &data_len);
+
+ nftnl_assert(val, attr, data_len == sizeof(int8_t));
+
+ return val ? *val : 0;
+}
+
+EXPORT_SYMBOL(nftnl_chain_get_array);
+const char *const *nftnl_chain_get_array(const struct nftnl_chain *c, uint16_t attr)
+{
+ uint32_t data_len;
+ const char * const *val = nftnl_chain_get_data(c, attr, &data_len);
+
+ nftnl_assert(val, attr, attr == NFTNL_CHAIN_DEVICES);
+
+ return val;
+}
+
+EXPORT_SYMBOL(nftnl_chain_nlmsg_build_payload);
+void nftnl_chain_nlmsg_build_payload(struct nlmsghdr *nlh, const struct nftnl_chain *c)
+{
+ struct nlattr *nest = NULL;
+ int i;
+
+ if (c->flags & (1 << NFTNL_CHAIN_TABLE))
+ mnl_attr_put_strz(nlh, NFTA_CHAIN_TABLE, c->table);
+ if (c->flags & (1 << NFTNL_CHAIN_NAME))
+ mnl_attr_put_strz(nlh, NFTA_CHAIN_NAME, c->name);
+
+ if ((c->flags & (1 << NFTNL_CHAIN_HOOKNUM)) ||
+ (c->flags & (1 << NFTNL_CHAIN_PRIO)) ||
+ (c->flags & (1 << NFTNL_CHAIN_DEV)) ||
+ (c->flags & (1 << NFTNL_CHAIN_DEVICES)))
+ nest = mnl_attr_nest_start(nlh, NFTA_CHAIN_HOOK);
+
+ if ((c->flags & (1 << NFTNL_CHAIN_HOOKNUM)))
+ mnl_attr_put_u32(nlh, NFTA_HOOK_HOOKNUM, htonl(c->hooknum));
+ if ((c->flags & (1 << NFTNL_CHAIN_PRIO)))
+ mnl_attr_put_u32(nlh, NFTA_HOOK_PRIORITY, htonl(c->prio));
+
+ if (c->flags & (1 << NFTNL_CHAIN_DEV))
+ mnl_attr_put_strz(nlh, NFTA_HOOK_DEV, c->dev);
+ else if (c->flags & (1 << NFTNL_CHAIN_DEVICES)) {
+ struct nlattr *nest_dev;
+
+ nest_dev = mnl_attr_nest_start(nlh, NFTA_HOOK_DEVS);
+ for (i = 0; i < c->dev_array_len; i++)
+ mnl_attr_put_strz(nlh, NFTA_DEVICE_NAME,
+ c->dev_array[i]);
+ mnl_attr_nest_end(nlh, nest_dev);
+ }
+
+ if ((c->flags & (1 << NFTNL_CHAIN_HOOKNUM)) ||
+ (c->flags & (1 << NFTNL_CHAIN_PRIO)) ||
+ (c->flags & (1 << NFTNL_CHAIN_DEV)) ||
+ (c->flags & (1 << NFTNL_CHAIN_DEVICES)))
+ mnl_attr_nest_end(nlh, nest);
+
+ if (c->flags & (1 << NFTNL_CHAIN_POLICY))
+ mnl_attr_put_u32(nlh, NFTA_CHAIN_POLICY, htonl(c->policy));
+ if (c->flags & (1 << NFTNL_CHAIN_USE))
+ mnl_attr_put_u32(nlh, NFTA_CHAIN_USE, htonl(c->use));
+ if ((c->flags & (1 << NFTNL_CHAIN_PACKETS)) &&
+ (c->flags & (1 << NFTNL_CHAIN_BYTES))) {
+ nest = mnl_attr_nest_start(nlh, NFTA_CHAIN_COUNTERS);
+ mnl_attr_put_u64(nlh, NFTA_COUNTER_PACKETS, be64toh(c->packets));
+ mnl_attr_put_u64(nlh, NFTA_COUNTER_BYTES, be64toh(c->bytes));
+ mnl_attr_nest_end(nlh, nest);
+ }
+ if (c->flags & (1 << NFTNL_CHAIN_HANDLE))
+ mnl_attr_put_u64(nlh, NFTA_CHAIN_HANDLE, be64toh(c->handle));
+ if (c->flags & (1 << NFTNL_CHAIN_TYPE))
+ mnl_attr_put_strz(nlh, NFTA_CHAIN_TYPE, c->type);
+ if (c->flags & (1 << NFTNL_CHAIN_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_CHAIN_FLAGS, htonl(c->chain_flags));
+ if (c->flags & (1 << NFTNL_CHAIN_ID))
+ mnl_attr_put_u32(nlh, NFTA_CHAIN_ID, htonl(c->chain_id));
+ if (c->flags & (1 << NFTNL_CHAIN_USERDATA))
+ mnl_attr_put(nlh, NFTA_CHAIN_USERDATA, c->user.len, c->user.data);
+}
+
+EXPORT_SYMBOL(nftnl_chain_rule_add);
+void nftnl_chain_rule_add(struct nftnl_rule *rule, struct nftnl_chain *c)
+{
+ list_add(&rule->head, &c->rule_list);
+}
+
+EXPORT_SYMBOL(nftnl_chain_rule_del);
+void nftnl_chain_rule_del(struct nftnl_rule *r)
+{
+ list_del(&r->head);
+}
+
+EXPORT_SYMBOL(nftnl_chain_rule_add_tail);
+void nftnl_chain_rule_add_tail(struct nftnl_rule *rule, struct nftnl_chain *c)
+{
+ list_add_tail(&rule->head, &c->rule_list);
+}
+
+EXPORT_SYMBOL(nftnl_chain_rule_insert_at);
+void nftnl_chain_rule_insert_at(struct nftnl_rule *rule, struct nftnl_rule *pos)
+{
+ list_add_tail(&rule->head, &pos->head);
+}
+
+EXPORT_SYMBOL(nftnl_chain_rule_append_at);
+void nftnl_chain_rule_append_at(struct nftnl_rule *rule, struct nftnl_rule *pos)
+{
+ list_add(&rule->head, &pos->head);
+}
+
+static int nftnl_chain_parse_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_CHAIN_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_CHAIN_NAME:
+ case NFTA_CHAIN_TABLE:
+ case NFTA_CHAIN_TYPE:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ case NFTA_CHAIN_HOOK:
+ case NFTA_CHAIN_COUNTERS:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ abi_breakage();
+ break;
+ case NFTA_CHAIN_POLICY:
+ case NFTA_CHAIN_USE:
+ case NFTA_CHAIN_FLAGS:
+ case NFTA_CHAIN_ID:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_CHAIN_HANDLE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ case NFTA_CHAIN_USERDATA:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int nftnl_chain_parse_counters_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_COUNTER_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_COUNTER_BYTES:
+ case NFTA_COUNTER_PACKETS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int nftnl_chain_parse_counters(struct nlattr *attr, struct nftnl_chain *c)
+{
+ struct nlattr *tb[NFTA_COUNTER_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_chain_parse_counters_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_COUNTER_PACKETS]) {
+ c->packets = be64toh(mnl_attr_get_u64(tb[NFTA_COUNTER_PACKETS]));
+ c->flags |= (1 << NFTNL_CHAIN_PACKETS);
+ }
+ if (tb[NFTA_COUNTER_BYTES]) {
+ c->bytes = be64toh(mnl_attr_get_u64(tb[NFTA_COUNTER_BYTES]));
+ c->flags |= (1 << NFTNL_CHAIN_BYTES);
+ }
+
+ return 0;
+}
+
+static int nftnl_chain_parse_hook_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_HOOK_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_HOOK_HOOKNUM:
+ case NFTA_HOOK_PRIORITY:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_HOOK_DEV:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int nftnl_chain_parse_devs(struct nlattr *nest, struct nftnl_chain *c)
+{
+ const char **dev_array, **tmp;
+ int len = 0, size = 8;
+ struct nlattr *attr;
+
+ dev_array = calloc(8, sizeof(char *));
+ if (!dev_array)
+ return -1;
+
+ mnl_attr_for_each_nested(attr, nest) {
+ if (mnl_attr_get_type(attr) != NFTA_DEVICE_NAME)
+ goto err;
+ dev_array[len++] = strdup(mnl_attr_get_str(attr));
+ if (len >= size) {
+ tmp = realloc(dev_array, size * 2 * sizeof(char *));
+ if (!tmp)
+ goto err;
+
+ size *= 2;
+ memset(&tmp[len], 0, (size - len) * sizeof(char *));
+ dev_array = tmp;
+ }
+ }
+
+ c->dev_array = dev_array;
+ c->dev_array_len = len;
+
+ return 0;
+err:
+ while (len--)
+ xfree(dev_array[len]);
+ xfree(dev_array);
+ return -1;
+}
+
+static int nftnl_chain_parse_hook(struct nlattr *attr, struct nftnl_chain *c)
+{
+ struct nlattr *tb[NFTA_HOOK_MAX+1] = {};
+ int ret;
+
+ if (mnl_attr_parse_nested(attr, nftnl_chain_parse_hook_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_HOOK_HOOKNUM]) {
+ c->hooknum = ntohl(mnl_attr_get_u32(tb[NFTA_HOOK_HOOKNUM]));
+ c->flags |= (1 << NFTNL_CHAIN_HOOKNUM);
+ }
+ if (tb[NFTA_HOOK_PRIORITY]) {
+ c->prio = ntohl(mnl_attr_get_u32(tb[NFTA_HOOK_PRIORITY]));
+ c->flags |= (1 << NFTNL_CHAIN_PRIO);
+ }
+ if (tb[NFTA_HOOK_DEV]) {
+ c->dev = strdup(mnl_attr_get_str(tb[NFTA_HOOK_DEV]));
+ if (!c->dev)
+ return -1;
+ c->flags |= (1 << NFTNL_CHAIN_DEV);
+ }
+ if (tb[NFTA_HOOK_DEVS]) {
+ ret = nftnl_chain_parse_devs(tb[NFTA_HOOK_DEVS], c);
+ if (ret < 0)
+ return -1;
+ c->flags |= (1 << NFTNL_CHAIN_DEVICES);
+ }
+
+ return 0;
+}
+
+EXPORT_SYMBOL(nftnl_chain_nlmsg_parse);
+int nftnl_chain_nlmsg_parse(const struct nlmsghdr *nlh, struct nftnl_chain *c)
+{
+ struct nlattr *tb[NFTA_CHAIN_MAX+1] = {};
+ struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
+ int ret = 0;
+
+ if (mnl_attr_parse(nlh, sizeof(*nfg), nftnl_chain_parse_attr_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_CHAIN_NAME]) {
+ if (c->flags & (1 << NFTNL_CHAIN_NAME))
+ xfree(c->name);
+ c->name = strdup(mnl_attr_get_str(tb[NFTA_CHAIN_NAME]));
+ if (!c->name)
+ return -1;
+ c->flags |= (1 << NFTNL_CHAIN_NAME);
+ }
+ if (tb[NFTA_CHAIN_TABLE]) {
+ if (c->flags & (1 << NFTNL_CHAIN_TABLE))
+ xfree(c->table);
+ c->table = strdup(mnl_attr_get_str(tb[NFTA_CHAIN_TABLE]));
+ if (!c->table)
+ return -1;
+ c->flags |= (1 << NFTNL_CHAIN_TABLE);
+ }
+ if (tb[NFTA_CHAIN_HOOK]) {
+ ret = nftnl_chain_parse_hook(tb[NFTA_CHAIN_HOOK], c);
+ if (ret < 0)
+ return ret;
+ }
+ if (tb[NFTA_CHAIN_POLICY]) {
+ c->policy = ntohl(mnl_attr_get_u32(tb[NFTA_CHAIN_POLICY]));
+ c->flags |= (1 << NFTNL_CHAIN_POLICY);
+ }
+ if (tb[NFTA_CHAIN_USE]) {
+ c->use = ntohl(mnl_attr_get_u32(tb[NFTA_CHAIN_USE]));
+ c->flags |= (1 << NFTNL_CHAIN_USE);
+ }
+ if (tb[NFTA_CHAIN_COUNTERS]) {
+ ret = nftnl_chain_parse_counters(tb[NFTA_CHAIN_COUNTERS], c);
+ if (ret < 0)
+ return ret;
+ }
+ if (tb[NFTA_CHAIN_HANDLE]) {
+ c->handle = be64toh(mnl_attr_get_u64(tb[NFTA_CHAIN_HANDLE]));
+ c->flags |= (1 << NFTNL_CHAIN_HANDLE);
+ }
+ if (tb[NFTA_CHAIN_TYPE]) {
+ if (c->flags & (1 << NFTNL_CHAIN_TYPE))
+ xfree(c->type);
+ c->type = strdup(mnl_attr_get_str(tb[NFTA_CHAIN_TYPE]));
+ if (!c->type)
+ return -1;
+ c->flags |= (1 << NFTNL_CHAIN_TYPE);
+ }
+ if (tb[NFTA_CHAIN_FLAGS]) {
+ c->chain_flags = ntohl(mnl_attr_get_u32(tb[NFTA_CHAIN_FLAGS]));
+ c->flags |= (1 << NFTNL_CHAIN_FLAGS);
+ }
+ if (tb[NFTA_CHAIN_ID]) {
+ c->chain_id = ntohl(mnl_attr_get_u32(tb[NFTA_CHAIN_ID]));
+ c->flags |= (1 << NFTNL_CHAIN_ID);
+ }
+ if (tb[NFTA_CHAIN_USERDATA]) {
+ nftnl_chain_set_data(c, NFTNL_CHAIN_USERDATA,
+ mnl_attr_get_payload(tb[NFTA_CHAIN_USERDATA]),
+ mnl_attr_get_payload_len(tb[NFTA_CHAIN_USERDATA]));
+ }
+
+ c->family = nfg->nfgen_family;
+ c->flags |= (1 << NFTNL_CHAIN_FAMILY);
+
+ return ret;
+}
+
+static inline int nftnl_str2hooknum(int family, const char *hook)
+{
+ int hooknum;
+
+ for (hooknum = 0; hooknum < NF_INET_NUMHOOKS; hooknum++) {
+ if (strcmp(hook, nftnl_hooknum2str(family, hooknum)) == 0)
+ return hooknum;
+ }
+ return -1;
+}
+
+static int nftnl_chain_snprintf_default(char *buf, size_t remain,
+ const struct nftnl_chain *c)
+{
+ int ret, offset = 0, i;
+
+ ret = snprintf(buf, remain, "%s %s %s use %u",
+ nftnl_family2str(c->family), c->table, c->name, c->use);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (c->flags & (1 << NFTNL_CHAIN_HOOKNUM)) {
+ ret = snprintf(buf + offset, remain, " type %s hook %s prio %d",
+ c->type, nftnl_hooknum2str(c->family, c->hooknum),
+ c->prio);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (c->flags & (1 << NFTNL_CHAIN_POLICY)) {
+ ret = snprintf(buf + offset, remain, " policy %s",
+ nftnl_verdict2str(c->policy));
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ ret = snprintf(buf + offset, remain,
+ " packets %"PRIu64" bytes %"PRIu64"",
+ c->packets, c->bytes);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (c->flags & (1 << NFTNL_CHAIN_DEV)) {
+ ret = snprintf(buf + offset, remain, " dev %s ",
+ c->dev);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (c->flags & (1 << NFTNL_CHAIN_DEVICES)) {
+ ret = snprintf(buf + offset, remain, " dev { ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ for (i = 0; i < c->dev_array_len; i++) {
+ ret = snprintf(buf + offset, remain, " %s ",
+ c->dev_array[i]);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ ret = snprintf(buf + offset, remain, " } ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (c->flags & (1 << NFTNL_CHAIN_FLAGS)) {
+ ret = snprintf(buf + offset, remain, " flags %x",
+ c->chain_flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (c->flags & (1 << NFTNL_CHAIN_ID)) {
+ ret = snprintf(buf + offset, remain, " id %x",
+ c->chain_id);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ }
+
+ return offset;
+}
+
+static int nftnl_chain_cmd_snprintf(char *buf, size_t remain,
+ const struct nftnl_chain *c, uint32_t cmd,
+ uint32_t type, uint32_t flags)
+{
+ int ret, offset = 0;
+
+ if (type != NFTNL_OUTPUT_DEFAULT)
+ return -1;
+
+ ret = nftnl_chain_snprintf_default(buf + offset, remain, c);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ return offset;
+}
+
+EXPORT_SYMBOL(nftnl_chain_snprintf);
+int nftnl_chain_snprintf(char *buf, size_t size, const struct nftnl_chain *c,
+ uint32_t type, uint32_t flags)
+{
+ if (size)
+ buf[0] = '\0';
+
+ return nftnl_chain_cmd_snprintf(buf, size, c, nftnl_flag2cmd(flags),
+ type, flags);
+}
+
+static int nftnl_chain_do_snprintf(char *buf, size_t size, const void *c,
+ uint32_t cmd, uint32_t type, uint32_t flags)
+{
+ return nftnl_chain_snprintf(buf, size, c, type, flags);
+}
+
+EXPORT_SYMBOL(nftnl_chain_fprintf);
+int nftnl_chain_fprintf(FILE *fp, const struct nftnl_chain *c, uint32_t type,
+ uint32_t flags)
+{
+ return nftnl_fprintf(fp, c, NFTNL_CMD_UNSPEC, type, flags,
+ nftnl_chain_do_snprintf);
+}
+
+EXPORT_SYMBOL(nftnl_rule_foreach);
+int nftnl_rule_foreach(struct nftnl_chain *c,
+ int (*cb)(struct nftnl_rule *r, void *data),
+ void *data)
+{
+ struct nftnl_rule *cur, *tmp;
+ int ret;
+
+ list_for_each_entry_safe(cur, tmp, &c->rule_list, head) {
+ ret = cb(cur, data);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+EXPORT_SYMBOL(nftnl_rule_lookup_byindex);
+struct nftnl_rule *
+nftnl_rule_lookup_byindex(struct nftnl_chain *c, uint32_t index)
+{
+ struct nftnl_rule *r;
+
+ list_for_each_entry(r, &c->rule_list, head) {
+ if (!index)
+ return r;
+ index--;
+ }
+ return NULL;
+}
+
+struct nftnl_rule_iter {
+ const struct nftnl_chain *c;
+ struct nftnl_rule *cur;
+};
+
+static void nftnl_rule_iter_init(const struct nftnl_chain *c,
+ struct nftnl_rule_iter *iter)
+{
+ iter->c = c;
+ if (list_empty(&c->rule_list))
+ iter->cur = NULL;
+ else
+ iter->cur = list_entry(c->rule_list.next, struct nftnl_rule,
+ head);
+}
+
+EXPORT_SYMBOL(nftnl_rule_iter_create);
+struct nftnl_rule_iter *nftnl_rule_iter_create(const struct nftnl_chain *c)
+{
+ struct nftnl_rule_iter *iter;
+
+ iter = calloc(1, sizeof(struct nftnl_rule_iter));
+ if (iter == NULL)
+ return NULL;
+
+ nftnl_rule_iter_init(c, iter);
+
+ return iter;
+}
+
+EXPORT_SYMBOL(nftnl_rule_iter_next);
+struct nftnl_rule *nftnl_rule_iter_next(struct nftnl_rule_iter *iter)
+{
+ struct nftnl_rule *rule = iter->cur;
+
+ if (rule == NULL)
+ return NULL;
+
+ /* get next rule, if any */
+ iter->cur = list_entry(iter->cur->head.next, struct nftnl_rule, head);
+ if (&iter->cur->head == iter->c->rule_list.next)
+ return NULL;
+
+ return rule;
+}
+
+EXPORT_SYMBOL(nftnl_rule_iter_destroy);
+void nftnl_rule_iter_destroy(struct nftnl_rule_iter *iter)
+{
+ xfree(iter);
+}
+
+#define CHAIN_NAME_HSIZE 512
+
+struct nftnl_chain_list {
+
+ struct list_head list;
+ struct hlist_head name_hash[CHAIN_NAME_HSIZE];
+};
+
+EXPORT_SYMBOL(nftnl_chain_list_alloc);
+struct nftnl_chain_list *nftnl_chain_list_alloc(void)
+{
+ struct nftnl_chain_list *list;
+ int i;
+
+ list = calloc(1, sizeof(struct nftnl_chain_list));
+ if (list == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&list->list);
+ for (i = 0; i < CHAIN_NAME_HSIZE; i++)
+ INIT_HLIST_HEAD(&list->name_hash[i]);
+
+ return list;
+}
+
+EXPORT_SYMBOL(nftnl_chain_list_free);
+void nftnl_chain_list_free(struct nftnl_chain_list *list)
+{
+ struct nftnl_chain *r, *tmp;
+
+ list_for_each_entry_safe(r, tmp, &list->list, head) {
+ list_del(&r->head);
+ hlist_del(&r->hnode);
+ nftnl_chain_free(r);
+ }
+ xfree(list);
+}
+
+EXPORT_SYMBOL(nftnl_chain_list_is_empty);
+int nftnl_chain_list_is_empty(const struct nftnl_chain_list *list)
+{
+ return list_empty(&list->list);
+}
+
+static uint32_t djb_hash(const char *key)
+{
+ uint32_t i, hash = 5381;
+
+ for (i = 0; i < strlen(key); i++)
+ hash = ((hash << 5) + hash) + key[i];
+
+ return hash;
+}
+
+EXPORT_SYMBOL(nftnl_chain_list_add);
+void nftnl_chain_list_add(struct nftnl_chain *r, struct nftnl_chain_list *list)
+{
+ int key = djb_hash(r->name) % CHAIN_NAME_HSIZE;
+
+ hlist_add_head(&r->hnode, &list->name_hash[key]);
+ list_add(&r->head, &list->list);
+}
+
+EXPORT_SYMBOL(nftnl_chain_list_add_tail);
+void nftnl_chain_list_add_tail(struct nftnl_chain *r, struct nftnl_chain_list *list)
+{
+ int key = djb_hash(r->name) % CHAIN_NAME_HSIZE;
+
+ hlist_add_head(&r->hnode, &list->name_hash[key]);
+ list_add_tail(&r->head, &list->list);
+}
+
+EXPORT_SYMBOL(nftnl_chain_list_del);
+void nftnl_chain_list_del(struct nftnl_chain *r)
+{
+ list_del(&r->head);
+ hlist_del(&r->hnode);
+}
+
+EXPORT_SYMBOL(nftnl_chain_list_foreach);
+int nftnl_chain_list_foreach(struct nftnl_chain_list *chain_list,
+ int (*cb)(struct nftnl_chain *r, void *data),
+ void *data)
+{
+ struct nftnl_chain *cur, *tmp;
+ int ret;
+
+ list_for_each_entry_safe(cur, tmp, &chain_list->list, head) {
+ ret = cb(cur, data);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+EXPORT_SYMBOL(nftnl_chain_list_lookup_byname);
+struct nftnl_chain *
+nftnl_chain_list_lookup_byname(struct nftnl_chain_list *chain_list,
+ const char *chain)
+{
+ int key = djb_hash(chain) % CHAIN_NAME_HSIZE;
+ struct nftnl_chain *c;
+ struct hlist_node *n;
+
+ hlist_for_each_entry(c, n, &chain_list->name_hash[key], hnode) {
+ if (!strcmp(chain, c->name))
+ return c;
+ }
+ return NULL;
+}
+
+struct nftnl_chain_list_iter {
+ const struct nftnl_chain_list *list;
+ struct nftnl_chain *cur;
+};
+
+EXPORT_SYMBOL(nftnl_chain_list_iter_create);
+struct nftnl_chain_list_iter *
+nftnl_chain_list_iter_create(const struct nftnl_chain_list *l)
+{
+ struct nftnl_chain_list_iter *iter;
+
+ iter = calloc(1, sizeof(struct nftnl_chain_list_iter));
+ if (iter == NULL)
+ return NULL;
+
+ iter->list = l;
+ if (nftnl_chain_list_is_empty(l))
+ iter->cur = NULL;
+ else
+ iter->cur = list_entry(l->list.next, struct nftnl_chain, head);
+
+ return iter;
+}
+
+EXPORT_SYMBOL(nftnl_chain_list_iter_next);
+struct nftnl_chain *nftnl_chain_list_iter_next(struct nftnl_chain_list_iter *iter)
+{
+ struct nftnl_chain *r = iter->cur;
+
+ if (r == NULL)
+ return NULL;
+
+ /* get next chain, if any */
+ iter->cur = list_entry(iter->cur->head.next, struct nftnl_chain, head);
+ if (&iter->cur->head == iter->list->list.next)
+ return NULL;
+
+ return r;
+}
+
+EXPORT_SYMBOL(nftnl_chain_list_iter_destroy);
+void nftnl_chain_list_iter_destroy(struct nftnl_chain_list_iter *iter)
+{
+ xfree(iter);
+}
diff --git a/src/common.c b/src/common.c
new file mode 100644
index 0000000..ec84fa0
--- /dev/null
+++ b/src/common.c
@@ -0,0 +1,164 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 <sys/socket.h>
+#include <time.h>
+#include <arpa/inet.h>
+#include <linux/netlink.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <libmnl/libmnl.h>
+#include <libnftnl/common.h>
+#include <libnftnl/set.h>
+
+#include <errno.h>
+#include "internal.h"
+
+static struct nlmsghdr *__nftnl_nlmsg_build_hdr(char *buf, uint16_t type,
+ uint16_t family,
+ uint16_t flags, uint32_t seq,
+ uint16_t res_id)
+{
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfh;
+
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = type;
+ nlh->nlmsg_flags = NLM_F_REQUEST | flags;
+ nlh->nlmsg_seq = seq;
+
+ nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
+ nfh->nfgen_family = family;
+ nfh->version = NFNETLINK_V0;
+ nfh->res_id = htons(res_id);
+
+ return nlh;
+}
+
+EXPORT_SYMBOL(nftnl_nlmsg_build_hdr);
+struct nlmsghdr *nftnl_nlmsg_build_hdr(char *buf, uint16_t type, uint16_t family,
+ uint16_t flags, uint32_t seq)
+{
+ return __nftnl_nlmsg_build_hdr(buf, (NFNL_SUBSYS_NFTABLES << 8) | type,
+ family, flags, seq, 0);
+}
+
+EXPORT_SYMBOL(nftnl_parse_err_alloc);
+struct nftnl_parse_err *nftnl_parse_err_alloc(void)
+{
+ struct nftnl_parse_err *err;
+
+ err = calloc(1, sizeof(struct nftnl_parse_err));
+ if (err == NULL)
+ return NULL;
+
+ err->error = NFTNL_PARSE_EOPNOTSUPP;
+
+ return err;
+}
+
+EXPORT_SYMBOL(nftnl_parse_err_free);
+void nftnl_parse_err_free(struct nftnl_parse_err *err)
+{
+ xfree(err);
+}
+
+EXPORT_SYMBOL(nftnl_parse_perror);
+int nftnl_parse_perror(const char *msg, struct nftnl_parse_err *err)
+{
+ switch (err->error) {
+ case NFTNL_PARSE_EBADINPUT:
+ return fprintf(stderr, "%s: Bad input format in line %d column %d\n",
+ msg, err->line, err->column);
+ case NFTNL_PARSE_EMISSINGNODE:
+ return fprintf(stderr, "%s: Node \"%s\" not found\n",
+ msg, err->node_name);
+ case NFTNL_PARSE_EBADTYPE:
+ return fprintf(stderr, "%s: Invalid type in node \"%s\"\n",
+ msg, err->node_name);
+ case NFTNL_PARSE_EOPNOTSUPP:
+ return fprintf(stderr, "%s: Operation not supported\n", msg);
+ default:
+ return fprintf(stderr, "%s: Undefined error\n", msg);
+ }
+}
+
+EXPORT_SYMBOL(nftnl_batch_begin);
+struct nlmsghdr *nftnl_batch_begin(char *buf, uint32_t seq)
+{
+ return __nftnl_nlmsg_build_hdr(buf, NFNL_MSG_BATCH_BEGIN, AF_UNSPEC,
+ 0, seq, NFNL_SUBSYS_NFTABLES);
+}
+
+EXPORT_SYMBOL(nftnl_batch_end);
+struct nlmsghdr *nftnl_batch_end(char *buf, uint32_t seq)
+{
+ return __nftnl_nlmsg_build_hdr(buf, NFNL_MSG_BATCH_END, AF_UNSPEC,
+ 0, seq, NFNL_SUBSYS_NFTABLES);
+}
+
+EXPORT_SYMBOL(nftnl_batch_is_supported);
+int nftnl_batch_is_supported(void)
+{
+ struct mnl_socket *nl;
+ struct mnl_nlmsg_batch *b;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ uint32_t seq = time(NULL), req_seq;
+ int ret;
+
+ nl = mnl_socket_open(NETLINK_NETFILTER);
+ if (nl == NULL)
+ return -1;
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
+ return -1;
+
+ b = mnl_nlmsg_batch_start(buf, sizeof(buf));
+
+ nftnl_batch_begin(mnl_nlmsg_batch_current(b), seq++);
+ mnl_nlmsg_batch_next(b);
+
+ req_seq = seq;
+ nftnl_nlmsg_build_hdr(mnl_nlmsg_batch_current(b), NFT_MSG_NEWSET,
+ AF_INET, NLM_F_ACK, seq++);
+ mnl_nlmsg_batch_next(b);
+
+ nftnl_batch_end(mnl_nlmsg_batch_current(b), seq++);
+ mnl_nlmsg_batch_next(b);
+
+ ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(b),
+ mnl_nlmsg_batch_size(b));
+ if (ret < 0)
+ goto err;
+
+ mnl_nlmsg_batch_stop(b);
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, req_seq, mnl_socket_get_portid(nl),
+ NULL, NULL);
+ if (ret <= 0)
+ break;
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ mnl_socket_close(nl);
+
+ /* We're sending an incomplete message to see if the kernel supports
+ * set messages in batches. EINVAL means that we sent an incomplete
+ * message with missing attributes. The kernel just ignores messages
+ * that we cannot include in the batch.
+ */
+ return (ret == -1 && errno == EINVAL) ? 1 : 0;
+err:
+ mnl_nlmsg_batch_stop(b);
+ return -1;
+}
diff --git a/src/expr.c b/src/expr.c
new file mode 100644
index 0000000..b4581f1
--- /dev/null
+++ b/src/expr.c
@@ -0,0 +1,303 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+#include "internal.h"
+
+#include <time.h>
+#include <endian.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftnl/expr.h>
+
+EXPORT_SYMBOL(nftnl_expr_alloc);
+struct nftnl_expr *nftnl_expr_alloc(const char *name)
+{
+ struct nftnl_expr *expr;
+ struct expr_ops *ops;
+
+ ops = nftnl_expr_ops_lookup(name);
+ if (ops == NULL)
+ return NULL;
+
+ expr = calloc(1, sizeof(struct nftnl_expr) + ops->alloc_len);
+ if (expr == NULL)
+ return NULL;
+
+ /* Manually set expression name attribute */
+ expr->flags |= (1 << NFTNL_EXPR_NAME);
+ expr->ops = ops;
+
+ if (ops->init)
+ ops->init(expr);
+
+ return expr;
+}
+
+EXPORT_SYMBOL(nftnl_expr_free);
+void nftnl_expr_free(const struct nftnl_expr *expr)
+{
+ if (expr->ops->free)
+ expr->ops->free(expr);
+
+ xfree(expr);
+}
+
+EXPORT_SYMBOL(nftnl_expr_is_set);
+bool nftnl_expr_is_set(const struct nftnl_expr *expr, uint16_t type)
+{
+ return expr->flags & (1 << type);
+}
+
+EXPORT_SYMBOL(nftnl_expr_set);
+int nftnl_expr_set(struct nftnl_expr *expr, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ switch(type) {
+ case NFTNL_EXPR_NAME: /* cannot be modified */
+ return 0;
+ default:
+ if (expr->ops->set(expr, type, data, data_len) < 0)
+ return -1;
+ }
+ expr->flags |= (1 << type);
+ return 0;
+}
+
+EXPORT_SYMBOL(nftnl_expr_set_u8);
+void
+nftnl_expr_set_u8(struct nftnl_expr *expr, uint16_t type, uint8_t data)
+{
+ nftnl_expr_set(expr, type, &data, sizeof(uint8_t));
+}
+
+EXPORT_SYMBOL(nftnl_expr_set_u16);
+void
+nftnl_expr_set_u16(struct nftnl_expr *expr, uint16_t type, uint16_t data)
+{
+ nftnl_expr_set(expr, type, &data, sizeof(uint16_t));
+}
+
+EXPORT_SYMBOL(nftnl_expr_set_u32);
+void
+nftnl_expr_set_u32(struct nftnl_expr *expr, uint16_t type, uint32_t data)
+{
+ nftnl_expr_set(expr, type, &data, sizeof(uint32_t));
+}
+
+EXPORT_SYMBOL(nftnl_expr_set_u64);
+void
+nftnl_expr_set_u64(struct nftnl_expr *expr, uint16_t type, uint64_t data)
+{
+ nftnl_expr_set(expr, type, &data, sizeof(uint64_t));
+}
+
+EXPORT_SYMBOL(nftnl_expr_set_str);
+int nftnl_expr_set_str(struct nftnl_expr *expr, uint16_t type, const char *str)
+{
+ return nftnl_expr_set(expr, type, str, strlen(str) + 1);
+}
+
+EXPORT_SYMBOL(nftnl_expr_get);
+const void *nftnl_expr_get(const struct nftnl_expr *expr,
+ uint16_t type, uint32_t *data_len)
+{
+ const void *ret;
+
+ if (!(expr->flags & (1 << type)))
+ return NULL;
+
+ switch(type) {
+ case NFTNL_EXPR_NAME:
+ *data_len = strlen(expr->ops->name) + 1;
+ ret = expr->ops->name;
+ break;
+ default:
+ ret = expr->ops->get(expr, type, data_len);
+ break;
+ }
+
+ return ret;
+}
+
+EXPORT_SYMBOL(nftnl_expr_get_u8);
+uint8_t nftnl_expr_get_u8(const struct nftnl_expr *expr, uint16_t type)
+{
+ const void *data;
+ uint32_t data_len;
+
+ data = nftnl_expr_get(expr, type, &data_len);
+ if (data == NULL)
+ return 0;
+
+ if (data_len != sizeof(uint8_t))
+ return 0;
+
+ return *((uint8_t *)data);
+}
+
+EXPORT_SYMBOL(nftnl_expr_get_u16);
+uint16_t nftnl_expr_get_u16(const struct nftnl_expr *expr, uint16_t type)
+{
+ const void *data;
+ uint32_t data_len;
+
+ data = nftnl_expr_get(expr, type, &data_len);
+ if (data == NULL)
+ return 0;
+
+ if (data_len != sizeof(uint16_t))
+ return 0;
+
+ return *((uint16_t *)data);
+}
+
+EXPORT_SYMBOL(nftnl_expr_get_u32);
+uint32_t nftnl_expr_get_u32(const struct nftnl_expr *expr, uint16_t type)
+{
+ const void *data;
+ uint32_t data_len;
+
+ data = nftnl_expr_get(expr, type, &data_len);
+ if (data == NULL)
+ return 0;
+
+ if (data_len != sizeof(uint32_t))
+ return 0;
+
+ return *((uint32_t *)data);
+}
+
+EXPORT_SYMBOL(nftnl_expr_get_u64);
+uint64_t nftnl_expr_get_u64(const struct nftnl_expr *expr, uint16_t type)
+{
+ const void *data;
+ uint32_t data_len;
+
+ data = nftnl_expr_get(expr, type, &data_len);
+ if (data == NULL)
+ return 0;
+
+ if (data_len != sizeof(uint64_t))
+ return 0;
+
+ return *((uint64_t *)data);
+}
+
+EXPORT_SYMBOL(nftnl_expr_get_str);
+const char *nftnl_expr_get_str(const struct nftnl_expr *expr, uint16_t type)
+{
+ uint32_t data_len;
+
+ return (const char *)nftnl_expr_get(expr, type, &data_len);
+}
+
+EXPORT_SYMBOL(nftnl_expr_build_payload);
+void nftnl_expr_build_payload(struct nlmsghdr *nlh, struct nftnl_expr *expr)
+{
+ struct nlattr *nest;
+
+ mnl_attr_put_strz(nlh, NFTA_EXPR_NAME, expr->ops->name);
+
+ if (!expr->ops->build)
+ return;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_EXPR_DATA);
+ expr->ops->build(nlh, expr);
+ mnl_attr_nest_end(nlh, nest);
+}
+
+static int nftnl_rule_parse_expr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_EXPR_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_EXPR_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ case NFTA_EXPR_DATA:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+struct nftnl_expr *nftnl_expr_parse(struct nlattr *attr)
+{
+ struct nlattr *tb[NFTA_EXPR_MAX+1] = {};
+ struct nftnl_expr *expr;
+
+ if (mnl_attr_parse_nested(attr, nftnl_rule_parse_expr_cb, tb) < 0)
+ goto err1;
+
+ expr = nftnl_expr_alloc(mnl_attr_get_str(tb[NFTA_EXPR_NAME]));
+ if (expr == NULL)
+ goto err1;
+
+ if (tb[NFTA_EXPR_DATA] &&
+ expr->ops->parse &&
+ expr->ops->parse(expr, tb[NFTA_EXPR_DATA]) < 0)
+ goto err2;
+
+ return expr;
+
+err2:
+ xfree(expr);
+err1:
+ return NULL;
+}
+
+EXPORT_SYMBOL(nftnl_expr_snprintf);
+int nftnl_expr_snprintf(char *buf, size_t remain, const struct nftnl_expr *expr,
+ uint32_t type, uint32_t flags)
+{
+ int ret;
+ unsigned int offset = 0;
+
+ if (remain)
+ buf[0] = '\0';
+
+ if (!expr->ops->output || type != NFTNL_OUTPUT_DEFAULT)
+ return 0;
+
+ ret = expr->ops->output(buf + offset, remain, flags, expr);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ return offset;
+}
+
+static int nftnl_expr_do_snprintf(char *buf, size_t size, const void *e,
+ uint32_t cmd, uint32_t type, uint32_t flags)
+{
+ return nftnl_expr_snprintf(buf, size, e, type, flags);
+}
+
+EXPORT_SYMBOL(nftnl_expr_fprintf);
+int nftnl_expr_fprintf(FILE *fp, const struct nftnl_expr *expr, uint32_t type,
+ uint32_t flags)
+{
+ return nftnl_fprintf(fp, expr, NFTNL_CMD_UNSPEC, type, flags,
+ nftnl_expr_do_snprintf);
+}
diff --git a/src/expr/bitwise.c b/src/expr/bitwise.c
new file mode 100644
index 0000000..2d27233
--- /dev/null
+++ b/src/expr/bitwise.c
@@ -0,0 +1,286 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h> /* for memcpy */
+#include <arpa/inet.h>
+#include <errno.h>
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_bitwise {
+ enum nft_registers sreg;
+ enum nft_registers dreg;
+ enum nft_bitwise_ops op;
+ unsigned int len;
+ union nftnl_data_reg mask;
+ union nftnl_data_reg xor;
+ union nftnl_data_reg data;
+};
+
+static int
+nftnl_expr_bitwise_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_bitwise *bitwise = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_BITWISE_SREG:
+ memcpy(&bitwise->sreg, data, sizeof(bitwise->sreg));
+ break;
+ case NFTNL_EXPR_BITWISE_DREG:
+ memcpy(&bitwise->dreg, data, sizeof(bitwise->dreg));
+ break;
+ case NFTNL_EXPR_BITWISE_OP:
+ memcpy(&bitwise->op, data, sizeof(bitwise->op));
+ break;
+ case NFTNL_EXPR_BITWISE_LEN:
+ memcpy(&bitwise->len, data, sizeof(bitwise->len));
+ break;
+ case NFTNL_EXPR_BITWISE_MASK:
+ memcpy(&bitwise->mask.val, data, data_len);
+ bitwise->mask.len = data_len;
+ break;
+ case NFTNL_EXPR_BITWISE_XOR:
+ memcpy(&bitwise->xor.val, data, data_len);
+ bitwise->xor.len = data_len;
+ break;
+ case NFTNL_EXPR_BITWISE_DATA:
+ memcpy(&bitwise->data.val, data, data_len);
+ bitwise->data.len = data_len;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_bitwise_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_bitwise *bitwise = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_BITWISE_SREG:
+ *data_len = sizeof(bitwise->sreg);
+ return &bitwise->sreg;
+ case NFTNL_EXPR_BITWISE_DREG:
+ *data_len = sizeof(bitwise->dreg);
+ return &bitwise->dreg;
+ case NFTNL_EXPR_BITWISE_OP:
+ *data_len = sizeof(bitwise->op);
+ return &bitwise->op;
+ case NFTNL_EXPR_BITWISE_LEN:
+ *data_len = sizeof(bitwise->len);
+ return &bitwise->len;
+ case NFTNL_EXPR_BITWISE_MASK:
+ *data_len = bitwise->mask.len;
+ return &bitwise->mask.val;
+ case NFTNL_EXPR_BITWISE_XOR:
+ *data_len = bitwise->xor.len;
+ return &bitwise->xor.val;
+ case NFTNL_EXPR_BITWISE_DATA:
+ *data_len = bitwise->data.len;
+ return &bitwise->data.val;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_bitwise_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_BITWISE_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_BITWISE_SREG:
+ case NFTA_BITWISE_DREG:
+ case NFTA_BITWISE_OP:
+ case NFTA_BITWISE_LEN:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_BITWISE_MASK:
+ case NFTA_BITWISE_XOR:
+ case NFTA_BITWISE_DATA:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_bitwise_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_bitwise *bitwise = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_BITWISE_SREG))
+ mnl_attr_put_u32(nlh, NFTA_BITWISE_SREG, htonl(bitwise->sreg));
+ if (e->flags & (1 << NFTNL_EXPR_BITWISE_DREG))
+ mnl_attr_put_u32(nlh, NFTA_BITWISE_DREG, htonl(bitwise->dreg));
+ if (e->flags & (1 << NFTNL_EXPR_BITWISE_OP))
+ mnl_attr_put_u32(nlh, NFTA_BITWISE_OP, htonl(bitwise->op));
+ if (e->flags & (1 << NFTNL_EXPR_BITWISE_LEN))
+ mnl_attr_put_u32(nlh, NFTA_BITWISE_LEN, htonl(bitwise->len));
+ if (e->flags & (1 << NFTNL_EXPR_BITWISE_MASK)) {
+ struct nlattr *nest;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_BITWISE_MASK);
+ mnl_attr_put(nlh, NFTA_DATA_VALUE, bitwise->mask.len,
+ bitwise->mask.val);
+ mnl_attr_nest_end(nlh, nest);
+ }
+ if (e->flags & (1 << NFTNL_EXPR_BITWISE_XOR)) {
+ struct nlattr *nest;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_BITWISE_XOR);
+ mnl_attr_put(nlh, NFTA_DATA_VALUE, bitwise->xor.len,
+ bitwise->xor.val);
+ mnl_attr_nest_end(nlh, nest);
+ }
+ if (e->flags & (1 << NFTNL_EXPR_BITWISE_DATA)) {
+ struct nlattr *nest;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_BITWISE_DATA);
+ mnl_attr_put(nlh, NFTA_DATA_VALUE, bitwise->data.len,
+ bitwise->data.val);
+ mnl_attr_nest_end(nlh, nest);
+ }
+}
+
+static int
+nftnl_expr_bitwise_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_bitwise *bitwise = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_BITWISE_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_bitwise_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_BITWISE_SREG]) {
+ bitwise->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_BITWISE_SREG]));
+ e->flags |= (1 << NFTNL_EXPR_BITWISE_SREG);
+ }
+ if (tb[NFTA_BITWISE_DREG]) {
+ bitwise->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_BITWISE_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_BITWISE_DREG);
+ }
+ if (tb[NFTA_BITWISE_OP]) {
+ bitwise->op = ntohl(mnl_attr_get_u32(tb[NFTA_BITWISE_OP]));
+ e->flags |= (1 << NFTNL_EXPR_BITWISE_OP);
+ }
+ if (tb[NFTA_BITWISE_LEN]) {
+ bitwise->len = ntohl(mnl_attr_get_u32(tb[NFTA_BITWISE_LEN]));
+ e->flags |= (1 << NFTNL_EXPR_BITWISE_LEN);
+ }
+ if (tb[NFTA_BITWISE_MASK]) {
+ ret = nftnl_parse_data(&bitwise->mask, tb[NFTA_BITWISE_MASK], NULL);
+ e->flags |= (1 << NFTA_BITWISE_MASK);
+ }
+ if (tb[NFTA_BITWISE_XOR]) {
+ ret = nftnl_parse_data(&bitwise->xor, tb[NFTA_BITWISE_XOR], NULL);
+ e->flags |= (1 << NFTA_BITWISE_XOR);
+ }
+ if (tb[NFTA_BITWISE_DATA]) {
+ ret = nftnl_parse_data(&bitwise->data, tb[NFTA_BITWISE_DATA], NULL);
+ e->flags |= (1 << NFTNL_EXPR_BITWISE_DATA);
+ }
+
+ return ret;
+}
+
+static int
+nftnl_expr_bitwise_snprintf_bool(char *buf, size_t remain,
+ const struct nftnl_expr_bitwise *bitwise)
+{
+ int offset = 0, ret;
+
+ ret = snprintf(buf, remain, "reg %u = ( reg %u & ",
+ bitwise->dreg, bitwise->sreg);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_data_reg_snprintf(buf + offset, remain, &bitwise->mask,
+ 0, DATA_VALUE);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = snprintf(buf + offset, remain, ") ^ ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_data_reg_snprintf(buf + offset, remain, &bitwise->xor,
+ 0, DATA_VALUE);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ return offset;
+}
+
+static int
+nftnl_expr_bitwise_snprintf_shift(char *buf, size_t remain, const char *op,
+ const struct nftnl_expr_bitwise *bitwise)
+{ int offset = 0, ret;
+
+ ret = snprintf(buf, remain, "reg %u = ( reg %u %s ",
+ bitwise->dreg, bitwise->sreg, op);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_data_reg_snprintf(buf + offset, remain, &bitwise->data,
+ 0, DATA_VALUE);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = snprintf(buf + offset, remain, ") ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ return offset;
+}
+
+static int
+nftnl_expr_bitwise_snprintf(char *buf, size_t size,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_bitwise *bitwise = nftnl_expr_data(e);
+ int err = -1;
+
+ switch (bitwise->op) {
+ case NFT_BITWISE_BOOL:
+ err = nftnl_expr_bitwise_snprintf_bool(buf, size, bitwise);
+ break;
+ case NFT_BITWISE_LSHIFT:
+ err = nftnl_expr_bitwise_snprintf_shift(buf, size, "<<", bitwise);
+ break;
+ case NFT_BITWISE_RSHIFT:
+ err = nftnl_expr_bitwise_snprintf_shift(buf, size, ">>", bitwise);
+ break;
+ }
+
+ return err;
+}
+
+struct expr_ops expr_ops_bitwise = {
+ .name = "bitwise",
+ .alloc_len = sizeof(struct nftnl_expr_bitwise),
+ .max_attr = NFTA_BITWISE_MAX,
+ .set = nftnl_expr_bitwise_set,
+ .get = nftnl_expr_bitwise_get,
+ .parse = nftnl_expr_bitwise_parse,
+ .build = nftnl_expr_bitwise_build,
+ .output = nftnl_expr_bitwise_snprintf,
+};
diff --git a/src/expr/byteorder.c b/src/expr/byteorder.c
new file mode 100644
index 0000000..89ed0a8
--- /dev/null
+++ b/src/expr/byteorder.c
@@ -0,0 +1,224 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h> /* for memcpy */
+#include <arpa/inet.h>
+#include <errno.h>
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_byteorder {
+ enum nft_registers sreg;
+ enum nft_registers dreg;
+ enum nft_byteorder_ops op;
+ unsigned int len;
+ unsigned int size;
+};
+
+static int
+nftnl_expr_byteorder_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_byteorder *byteorder = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_BYTEORDER_SREG:
+ memcpy(&byteorder->sreg, data, sizeof(byteorder->sreg));
+ break;
+ case NFTNL_EXPR_BYTEORDER_DREG:
+ memcpy(&byteorder->dreg, data, sizeof(byteorder->dreg));
+ break;
+ case NFTNL_EXPR_BYTEORDER_OP:
+ memcpy(&byteorder->op, data, sizeof(byteorder->op));
+ break;
+ case NFTNL_EXPR_BYTEORDER_LEN:
+ memcpy(&byteorder->len, data, sizeof(byteorder->len));
+ break;
+ case NFTNL_EXPR_BYTEORDER_SIZE:
+ memcpy(&byteorder->size, data, sizeof(byteorder->size));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_byteorder_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_byteorder *byteorder = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_BYTEORDER_SREG:
+ *data_len = sizeof(byteorder->sreg);
+ return &byteorder->sreg;
+ case NFTNL_EXPR_BYTEORDER_DREG:
+ *data_len = sizeof(byteorder->dreg);
+ return &byteorder->dreg;
+ case NFTNL_EXPR_BYTEORDER_OP:
+ *data_len = sizeof(byteorder->op);
+ return &byteorder->op;
+ case NFTNL_EXPR_BYTEORDER_LEN:
+ *data_len = sizeof(byteorder->len);
+ return &byteorder->len;
+ case NFTNL_EXPR_BYTEORDER_SIZE:
+ *data_len = sizeof(byteorder->size);
+ return &byteorder->size;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_byteorder_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_BYTEORDER_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_BYTEORDER_SREG:
+ case NFTA_BYTEORDER_DREG:
+ case NFTA_BYTEORDER_OP:
+ case NFTA_BYTEORDER_LEN:
+ case NFTA_BYTEORDER_SIZE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_byteorder_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_byteorder *byteorder = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_BYTEORDER_SREG)) {
+ mnl_attr_put_u32(nlh, NFTA_BYTEORDER_SREG,
+ htonl(byteorder->sreg));
+ }
+ if (e->flags & (1 << NFTNL_EXPR_BYTEORDER_DREG)) {
+ mnl_attr_put_u32(nlh, NFTA_BYTEORDER_DREG,
+ htonl(byteorder->dreg));
+ }
+ if (e->flags & (1 << NFTNL_EXPR_BYTEORDER_OP)) {
+ mnl_attr_put_u32(nlh, NFTA_BYTEORDER_OP,
+ htonl(byteorder->op));
+ }
+ if (e->flags & (1 << NFTNL_EXPR_BYTEORDER_LEN)) {
+ mnl_attr_put_u32(nlh, NFTA_BYTEORDER_LEN,
+ htonl(byteorder->len));
+ }
+ if (e->flags & (1 << NFTNL_EXPR_BYTEORDER_SIZE)) {
+ mnl_attr_put_u32(nlh, NFTA_BYTEORDER_SIZE,
+ htonl(byteorder->size));
+ }
+}
+
+static int
+nftnl_expr_byteorder_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_byteorder *byteorder = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_BYTEORDER_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_byteorder_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_BYTEORDER_SREG]) {
+ byteorder->sreg =
+ ntohl(mnl_attr_get_u32(tb[NFTA_BYTEORDER_SREG]));
+ e->flags |= (1 << NFTNL_EXPR_BYTEORDER_SREG);
+ }
+ if (tb[NFTA_BYTEORDER_DREG]) {
+ byteorder->dreg =
+ ntohl(mnl_attr_get_u32(tb[NFTA_BYTEORDER_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_BYTEORDER_DREG);
+ }
+ if (tb[NFTA_BYTEORDER_OP]) {
+ byteorder->op =
+ ntohl(mnl_attr_get_u32(tb[NFTA_BYTEORDER_OP]));
+ e->flags |= (1 << NFTNL_EXPR_BYTEORDER_OP);
+ }
+ if (tb[NFTA_BYTEORDER_LEN]) {
+ byteorder->len =
+ ntohl(mnl_attr_get_u32(tb[NFTA_BYTEORDER_LEN]));
+ e->flags |= (1 << NFTNL_EXPR_BYTEORDER_LEN);
+ }
+ if (tb[NFTA_BYTEORDER_SIZE]) {
+ byteorder->size =
+ ntohl(mnl_attr_get_u32(tb[NFTA_BYTEORDER_SIZE]));
+ e->flags |= (1 << NFTNL_EXPR_BYTEORDER_SIZE);
+ }
+
+ return ret;
+}
+
+static const char *expr_byteorder_str[] = {
+ [NFT_BYTEORDER_HTON] = "hton",
+ [NFT_BYTEORDER_NTOH] = "ntoh",
+};
+
+static const char *bo2str(uint32_t type)
+{
+ if (type > NFT_BYTEORDER_HTON)
+ return "unknown";
+
+ return expr_byteorder_str[type];
+}
+
+static inline int nftnl_str2ntoh(const char *op)
+{
+ if (strcmp(op, "ntoh") == 0)
+ return NFT_BYTEORDER_NTOH;
+ else if (strcmp(op, "hton") == 0)
+ return NFT_BYTEORDER_HTON;
+ else {
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+static int
+nftnl_expr_byteorder_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_byteorder *byteorder = nftnl_expr_data(e);
+ int offset = 0, ret;
+
+ ret = snprintf(buf, remain, "reg %u = %s(reg %u, %u, %u) ",
+ byteorder->dreg, bo2str(byteorder->op),
+ byteorder->sreg, byteorder->size, byteorder->len);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ return offset;
+}
+
+struct expr_ops expr_ops_byteorder = {
+ .name = "byteorder",
+ .alloc_len = sizeof(struct nftnl_expr_byteorder),
+ .max_attr = NFTA_BYTEORDER_MAX,
+ .set = nftnl_expr_byteorder_set,
+ .get = nftnl_expr_byteorder_get,
+ .parse = nftnl_expr_byteorder_parse,
+ .build = nftnl_expr_byteorder_build,
+ .output = nftnl_expr_byteorder_snprintf,
+};
diff --git a/src/expr/cmp.c b/src/expr/cmp.c
new file mode 100644
index 0000000..f9d15bb
--- /dev/null
+++ b/src/expr/cmp.c
@@ -0,0 +1,206 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_cmp {
+ union nftnl_data_reg data;
+ enum nft_registers sreg;
+ enum nft_cmp_ops op;
+};
+
+static int
+nftnl_expr_cmp_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_cmp *cmp = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_CMP_SREG:
+ memcpy(&cmp->sreg, data, sizeof(cmp->sreg));
+ break;
+ case NFTNL_EXPR_CMP_OP:
+ memcpy(&cmp->op, data, sizeof(cmp->op));
+ break;
+ case NFTNL_EXPR_CMP_DATA:
+ memcpy(&cmp->data.val, data, data_len);
+ cmp->data.len = data_len;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_cmp_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_cmp *cmp = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_CMP_SREG:
+ *data_len = sizeof(cmp->sreg);
+ return &cmp->sreg;
+ case NFTNL_EXPR_CMP_OP:
+ *data_len = sizeof(cmp->op);
+ return &cmp->op;
+ case NFTNL_EXPR_CMP_DATA:
+ *data_len = cmp->data.len;
+ return &cmp->data.val;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_cmp_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_CMP_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_CMP_SREG:
+ case NFTA_CMP_OP:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_CMP_DATA:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_cmp_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_cmp *cmp = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_CMP_SREG))
+ mnl_attr_put_u32(nlh, NFTA_CMP_SREG, htonl(cmp->sreg));
+ if (e->flags & (1 << NFTNL_EXPR_CMP_OP))
+ mnl_attr_put_u32(nlh, NFTA_CMP_OP, htonl(cmp->op));
+ if (e->flags & (1 << NFTNL_EXPR_CMP_DATA)) {
+ struct nlattr *nest;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_CMP_DATA);
+ mnl_attr_put(nlh, NFTA_DATA_VALUE, cmp->data.len, cmp->data.val);
+ mnl_attr_nest_end(nlh, nest);
+ }
+}
+
+static int
+nftnl_expr_cmp_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_cmp *cmp = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_CMP_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_cmp_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_CMP_SREG]) {
+ cmp->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_CMP_SREG]));
+ e->flags |= (1 << NFTA_CMP_SREG);
+ }
+ if (tb[NFTA_CMP_OP]) {
+ cmp->op = ntohl(mnl_attr_get_u32(tb[NFTA_CMP_OP]));
+ e->flags |= (1 << NFTA_CMP_OP);
+ }
+ if (tb[NFTA_CMP_DATA]) {
+ ret = nftnl_parse_data(&cmp->data, tb[NFTA_CMP_DATA], NULL);
+ e->flags |= (1 << NFTA_CMP_DATA);
+ }
+
+ return ret;
+}
+
+static const char *expr_cmp_str[] = {
+ [NFT_CMP_EQ] = "eq",
+ [NFT_CMP_NEQ] = "neq",
+ [NFT_CMP_LT] = "lt",
+ [NFT_CMP_LTE] = "lte",
+ [NFT_CMP_GT] = "gt",
+ [NFT_CMP_GTE] = "gte",
+};
+
+static const char *cmp2str(uint32_t op)
+{
+ if (op > NFT_CMP_GTE)
+ return "unknown";
+
+ return expr_cmp_str[op];
+}
+
+static inline int nftnl_str2cmp(const char *op)
+{
+ if (strcmp(op, "eq") == 0)
+ return NFT_CMP_EQ;
+ else if (strcmp(op, "neq") == 0)
+ return NFT_CMP_NEQ;
+ else if (strcmp(op, "lt") == 0)
+ return NFT_CMP_LT;
+ else if (strcmp(op, "lte") == 0)
+ return NFT_CMP_LTE;
+ else if (strcmp(op, "gt") == 0)
+ return NFT_CMP_GT;
+ else if (strcmp(op, "gte") == 0)
+ return NFT_CMP_GTE;
+ else {
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+static int
+nftnl_expr_cmp_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_cmp *cmp = nftnl_expr_data(e);
+ int offset = 0, ret;
+
+ ret = snprintf(buf, remain, "%s reg %u ",
+ cmp2str(cmp->op), cmp->sreg);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_data_reg_snprintf(buf + offset, remain, &cmp->data,
+ 0, DATA_VALUE);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ return offset;
+}
+
+struct expr_ops expr_ops_cmp = {
+ .name = "cmp",
+ .alloc_len = sizeof(struct nftnl_expr_cmp),
+ .max_attr = NFTA_CMP_MAX,
+ .set = nftnl_expr_cmp_set,
+ .get = nftnl_expr_cmp_get,
+ .parse = nftnl_expr_cmp_parse,
+ .build = nftnl_expr_cmp_build,
+ .output = nftnl_expr_cmp_snprintf,
+};
diff --git a/src/expr/connlimit.c b/src/expr/connlimit.c
new file mode 100644
index 0000000..549417b
--- /dev/null
+++ b/src/expr/connlimit.c
@@ -0,0 +1,139 @@
+/*
+ * (C) 2018 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_connlimit {
+ uint32_t count;
+ uint32_t flags;
+};
+
+static int
+nftnl_expr_connlimit_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_connlimit *connlimit = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_CONNLIMIT_COUNT:
+ memcpy(&connlimit->count, data, sizeof(connlimit->count));
+ break;
+ case NFTNL_EXPR_CONNLIMIT_FLAGS:
+ memcpy(&connlimit->flags, data, sizeof(connlimit->flags));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_connlimit_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_connlimit *connlimit = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_CONNLIMIT_COUNT:
+ *data_len = sizeof(connlimit->count);
+ return &connlimit->count;
+ case NFTNL_EXPR_CONNLIMIT_FLAGS:
+ *data_len = sizeof(connlimit->flags);
+ return &connlimit->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_connlimit_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_CONNLIMIT_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_CONNLIMIT_COUNT:
+ case NFTA_CONNLIMIT_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_connlimit_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_connlimit *connlimit = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_CONNLIMIT_COUNT))
+ mnl_attr_put_u32(nlh, NFTA_CONNLIMIT_COUNT,
+ htonl(connlimit->count));
+ if (e->flags & (1 << NFTNL_EXPR_CONNLIMIT_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_CONNLIMIT_FLAGS,
+ htonl(connlimit->flags));
+}
+
+static int
+nftnl_expr_connlimit_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_connlimit *connlimit = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_CONNLIMIT_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_connlimit_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_CONNLIMIT_COUNT]) {
+ connlimit->count =
+ ntohl(mnl_attr_get_u32(tb[NFTA_CONNLIMIT_COUNT]));
+ e->flags |= (1 << NFTNL_EXPR_CONNLIMIT_COUNT);
+ }
+ if (tb[NFTA_CONNLIMIT_FLAGS]) {
+ connlimit->flags =
+ ntohl(mnl_attr_get_u32(tb[NFTA_CONNLIMIT_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_CONNLIMIT_FLAGS);
+ }
+
+ return 0;
+}
+
+static int nftnl_expr_connlimit_snprintf(char *buf, size_t len,
+ uint32_t flags,
+ const struct nftnl_expr *e)
+{
+ struct nftnl_expr_connlimit *connlimit = nftnl_expr_data(e);
+
+ return snprintf(buf, len, "count %u flags %x ",
+ connlimit->count, connlimit->flags);
+}
+
+struct expr_ops expr_ops_connlimit = {
+ .name = "connlimit",
+ .alloc_len = sizeof(struct nftnl_expr_connlimit),
+ .max_attr = NFTA_CONNLIMIT_MAX,
+ .set = nftnl_expr_connlimit_set,
+ .get = nftnl_expr_connlimit_get,
+ .parse = nftnl_expr_connlimit_parse,
+ .build = nftnl_expr_connlimit_build,
+ .output = nftnl_expr_connlimit_snprintf,
+};
diff --git a/src/expr/counter.c b/src/expr/counter.c
new file mode 100644
index 0000000..d139a5f
--- /dev/null
+++ b/src/expr/counter.c
@@ -0,0 +1,137 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_counter {
+ uint64_t pkts;
+ uint64_t bytes;
+};
+
+static int
+nftnl_expr_counter_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_counter *ctr = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_CTR_BYTES:
+ memcpy(&ctr->bytes, data, sizeof(ctr->bytes));
+ break;
+ case NFTNL_EXPR_CTR_PACKETS:
+ memcpy(&ctr->pkts, data, sizeof(ctr->pkts));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_counter_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_counter *ctr = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_CTR_BYTES:
+ *data_len = sizeof(ctr->bytes);
+ return &ctr->bytes;
+ case NFTNL_EXPR_CTR_PACKETS:
+ *data_len = sizeof(ctr->pkts);
+ return &ctr->pkts;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_counter_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_COUNTER_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_COUNTER_BYTES:
+ case NFTA_COUNTER_PACKETS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_counter_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_counter *ctr = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_CTR_BYTES))
+ mnl_attr_put_u64(nlh, NFTA_COUNTER_BYTES, htobe64(ctr->bytes));
+ if (e->flags & (1 << NFTNL_EXPR_CTR_PACKETS))
+ mnl_attr_put_u64(nlh, NFTA_COUNTER_PACKETS, htobe64(ctr->pkts));
+}
+
+static int
+nftnl_expr_counter_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_counter *ctr = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_COUNTER_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_counter_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_COUNTER_BYTES]) {
+ ctr->bytes = be64toh(mnl_attr_get_u64(tb[NFTA_COUNTER_BYTES]));
+ e->flags |= (1 << NFTNL_EXPR_CTR_BYTES);
+ }
+ if (tb[NFTA_COUNTER_PACKETS]) {
+ ctr->pkts = be64toh(mnl_attr_get_u64(tb[NFTA_COUNTER_PACKETS]));
+ e->flags |= (1 << NFTNL_EXPR_CTR_PACKETS);
+ }
+
+ return 0;
+}
+
+static int nftnl_expr_counter_snprintf(char *buf, size_t len,
+ uint32_t flags,
+ const struct nftnl_expr *e)
+{
+ struct nftnl_expr_counter *ctr = nftnl_expr_data(e);
+
+ return snprintf(buf, len, "pkts %"PRIu64" bytes %"PRIu64" ",
+ ctr->pkts, ctr->bytes);
+}
+
+struct expr_ops expr_ops_counter = {
+ .name = "counter",
+ .alloc_len = sizeof(struct nftnl_expr_counter),
+ .max_attr = NFTA_COUNTER_MAX,
+ .set = nftnl_expr_counter_set,
+ .get = nftnl_expr_counter_get,
+ .parse = nftnl_expr_counter_parse,
+ .build = nftnl_expr_counter_build,
+ .output = nftnl_expr_counter_snprintf,
+};
diff --git a/src/expr/ct.c b/src/expr/ct.c
new file mode 100644
index 0000000..f4a2aea
--- /dev/null
+++ b/src/expr/ct.c
@@ -0,0 +1,262 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_ct {
+ enum nft_ct_keys key;
+ enum nft_registers dreg;
+ enum nft_registers sreg;
+ uint8_t dir;
+};
+
+#define IP_CT_DIR_ORIGINAL 0
+#define IP_CT_DIR_REPLY 1
+
+static int
+nftnl_expr_ct_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_ct *ct = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_CT_KEY:
+ memcpy(&ct->key, data, sizeof(ct->key));
+ break;
+ case NFTNL_EXPR_CT_DIR:
+ memcpy(&ct->dir, data, sizeof(ct->dir));
+ break;
+ case NFTNL_EXPR_CT_DREG:
+ memcpy(&ct->dreg, data, sizeof(ct->dreg));
+ break;
+ case NFTNL_EXPR_CT_SREG:
+ memcpy(&ct->sreg, data, sizeof(ct->sreg));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_ct_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_ct *ct = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_CT_KEY:
+ *data_len = sizeof(ct->key);
+ return &ct->key;
+ case NFTNL_EXPR_CT_DIR:
+ *data_len = sizeof(ct->dir);
+ return &ct->dir;
+ case NFTNL_EXPR_CT_DREG:
+ *data_len = sizeof(ct->dreg);
+ return &ct->dreg;
+ case NFTNL_EXPR_CT_SREG:
+ *data_len = sizeof(ct->sreg);
+ return &ct->sreg;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_ct_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_CT_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_CT_KEY:
+ case NFTA_CT_DREG:
+ case NFTA_CT_SREG:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_CT_DIRECTION:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_ct_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_ct *ct = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_CT_KEY))
+ mnl_attr_put_u32(nlh, NFTA_CT_KEY, htonl(ct->key));
+ if (e->flags & (1 << NFTNL_EXPR_CT_DREG))
+ mnl_attr_put_u32(nlh, NFTA_CT_DREG, htonl(ct->dreg));
+ if (e->flags & (1 << NFTNL_EXPR_CT_DIR))
+ mnl_attr_put_u8(nlh, NFTA_CT_DIRECTION, ct->dir);
+ if (e->flags & (1 << NFTNL_EXPR_CT_SREG))
+ mnl_attr_put_u32(nlh, NFTA_CT_SREG, htonl(ct->sreg));
+}
+
+static int
+nftnl_expr_ct_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_ct *ct = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_CT_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_ct_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_CT_KEY]) {
+ ct->key = ntohl(mnl_attr_get_u32(tb[NFTA_CT_KEY]));
+ e->flags |= (1 << NFTNL_EXPR_CT_KEY);
+ }
+ if (tb[NFTA_CT_DREG]) {
+ ct->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_CT_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_CT_DREG);
+ }
+ if (tb[NFTA_CT_SREG]) {
+ ct->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_CT_SREG]));
+ e->flags |= (1 << NFTNL_EXPR_CT_SREG);
+ }
+ if (tb[NFTA_CT_DIRECTION]) {
+ ct->dir = mnl_attr_get_u8(tb[NFTA_CT_DIRECTION]);
+ e->flags |= (1 << NFTNL_EXPR_CT_DIR);
+ }
+
+ return 0;
+}
+
+static const char *ctkey2str_array[NFT_CT_MAX + 1] = {
+ [NFT_CT_STATE] = "state",
+ [NFT_CT_DIRECTION] = "direction",
+ [NFT_CT_STATUS] = "status",
+ [NFT_CT_MARK] = "mark",
+ [NFT_CT_SECMARK] = "secmark",
+ [NFT_CT_EXPIRATION] = "expiration",
+ [NFT_CT_HELPER] = "helper",
+ [NFT_CT_L3PROTOCOL] = "l3protocol",
+ [NFT_CT_PROTOCOL] = "protocol",
+ [NFT_CT_SRC] = "src",
+ [NFT_CT_DST] = "dst",
+ [NFT_CT_PROTO_SRC] = "proto_src",
+ [NFT_CT_PROTO_DST] = "proto_dst",
+ [NFT_CT_LABELS] = "label",
+ [NFT_CT_PKTS] = "packets",
+ [NFT_CT_BYTES] = "bytes",
+ [NFT_CT_AVGPKT] = "avgpkt",
+ [NFT_CT_ZONE] = "zone",
+ [NFT_CT_EVENTMASK] = "event",
+ [NFT_CT_SRC_IP] = "src_ip",
+ [NFT_CT_DST_IP] = "dst_ip",
+ [NFT_CT_SRC_IP6] = "src_ip6",
+ [NFT_CT_DST_IP6] = "dst_ip6",
+ [NFT_CT_ID] = "id",
+};
+
+static const char *ctkey2str(uint32_t ctkey)
+{
+ if (ctkey >= NFT_CT_MAX)
+ return "unknown";
+
+ return ctkey2str_array[ctkey];
+}
+
+static inline int str2ctkey(const char *ctkey)
+{
+ int i;
+
+ for (i = 0; i < NFT_CT_MAX; i++) {
+ if (strcmp(ctkey2str_array[i], ctkey) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+static const char *ctdir2str(uint8_t ctdir)
+{
+ switch (ctdir) {
+ case IP_CT_DIR_ORIGINAL:
+ return "original";
+ case IP_CT_DIR_REPLY:
+ return "reply";
+ default:
+ return "unknown";
+ }
+}
+
+static inline int str2ctdir(const char *str, uint8_t *ctdir)
+{
+ if (strcmp(str, "original") == 0) {
+ *ctdir = IP_CT_DIR_ORIGINAL;
+ return 0;
+ }
+
+ if (strcmp(str, "reply") == 0) {
+ *ctdir = IP_CT_DIR_REPLY;
+ return 0;
+ }
+
+ return -1;
+}
+
+static int
+nftnl_expr_ct_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_ct *ct = nftnl_expr_data(e);
+ int ret, offset = 0;
+
+ if (e->flags & (1 << NFTNL_EXPR_CT_SREG)) {
+ ret = snprintf(buf, remain, "set %s with reg %u ",
+ ctkey2str(ct->key), ct->sreg);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_CT_DREG)) {
+ ret = snprintf(buf, remain, "load %s => reg %u ",
+ ctkey2str(ct->key), ct->dreg);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (nftnl_expr_is_set(e, NFTNL_EXPR_CT_DIR)) {
+ ret = snprintf(buf + offset, remain, ", dir %s ",
+ ctdir2str(ct->dir));
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+struct expr_ops expr_ops_ct = {
+ .name = "ct",
+ .alloc_len = sizeof(struct nftnl_expr_ct),
+ .max_attr = NFTA_CT_MAX,
+ .set = nftnl_expr_ct_set,
+ .get = nftnl_expr_ct_get,
+ .parse = nftnl_expr_ct_parse,
+ .build = nftnl_expr_ct_build,
+ .output = nftnl_expr_ct_snprintf,
+};
diff --git a/src/expr/data_reg.c b/src/expr/data_reg.c
new file mode 100644
index 0000000..2633a77
--- /dev/null
+++ b/src/expr/data_reg.c
@@ -0,0 +1,219 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <limits.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netinet/in.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+#include "internal.h"
+
+static int
+nftnl_data_reg_value_snprintf_default(char *buf, size_t remain,
+ const union nftnl_data_reg *reg,
+ uint32_t flags)
+{
+ const char *pfx = flags & DATA_F_NOPFX ? "" : "0x";
+ int offset = 0, ret, i;
+
+
+
+ for (i = 0; i < div_round_up(reg->len, sizeof(uint32_t)); i++) {
+ ret = snprintf(buf + offset, remain,
+ "%s%.8x ", pfx, reg->val[i]);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+static int
+nftnl_data_reg_verdict_snprintf_def(char *buf, size_t size,
+ const union nftnl_data_reg *reg,
+ uint32_t flags)
+{
+ int remain = size, offset = 0, ret = 0;
+
+ ret = snprintf(buf, size, "%s ", nftnl_verdict2str(reg->verdict));
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (reg->chain != NULL) {
+ ret = snprintf(buf + offset, remain, "-> %s ", reg->chain);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+int nftnl_data_reg_snprintf(char *buf, size_t size,
+ const union nftnl_data_reg *reg,
+ uint32_t flags, int reg_type)
+{
+ switch(reg_type) {
+ case DATA_VALUE:
+ return nftnl_data_reg_value_snprintf_default(buf, size,
+ reg, flags);
+ case DATA_VERDICT:
+ case DATA_CHAIN:
+ return nftnl_data_reg_verdict_snprintf_def(buf, size,
+ reg, flags);
+ default:
+ return -1;
+ }
+}
+
+static int nftnl_data_parse_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_DATA_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_DATA_VALUE:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ abi_breakage();
+ break;
+ case NFTA_DATA_VERDICT:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ abi_breakage();
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int nftnl_verdict_parse_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_VERDICT_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_VERDICT_CODE:
+ case NFTA_VERDICT_CHAIN_ID:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_VERDICT_CHAIN:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ }
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int
+nftnl_parse_verdict(union nftnl_data_reg *data, const struct nlattr *attr, int *type)
+{
+ struct nlattr *tb[NFTA_VERDICT_MAX+1];
+
+ if (mnl_attr_parse_nested(attr, nftnl_verdict_parse_cb, tb) < 0)
+ return -1;
+
+ if (!tb[NFTA_VERDICT_CODE])
+ return -1;
+
+ data->verdict = ntohl(mnl_attr_get_u32(tb[NFTA_VERDICT_CODE]));
+
+ switch(data->verdict) {
+ case NF_ACCEPT:
+ case NF_DROP:
+ case NF_QUEUE:
+ case NFT_CONTINUE:
+ case NFT_BREAK:
+ case NFT_RETURN:
+ if (type)
+ *type = DATA_VERDICT;
+ data->len = sizeof(data->verdict);
+ break;
+ case NFT_JUMP:
+ case NFT_GOTO:
+ if (!tb[NFTA_VERDICT_CHAIN])
+ return -1;
+
+ data->chain = strdup(mnl_attr_get_str(tb[NFTA_VERDICT_CHAIN]));
+ if (!data->chain)
+ return -1;
+
+ if (type)
+ *type = DATA_CHAIN;
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+__nftnl_parse_data(union nftnl_data_reg *data, const struct nlattr *attr)
+{
+ void *orig = mnl_attr_get_payload(attr);
+ uint32_t data_len = mnl_attr_get_payload_len(attr);
+
+ if (data_len == 0)
+ return -1;
+
+ if (data_len > sizeof(data->val))
+ return -1;
+
+ memcpy(data->val, orig, data_len);
+ data->len = data_len;
+
+ return 0;
+}
+
+int nftnl_parse_data(union nftnl_data_reg *data, struct nlattr *attr, int *type)
+{
+ struct nlattr *tb[NFTA_DATA_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_data_parse_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_DATA_VALUE]) {
+ if (type)
+ *type = DATA_VALUE;
+
+ ret = __nftnl_parse_data(data, tb[NFTA_DATA_VALUE]);
+ if (ret < 0)
+ return ret;
+ }
+ if (tb[NFTA_DATA_VERDICT])
+ ret = nftnl_parse_verdict(data, tb[NFTA_DATA_VERDICT], type);
+
+ return ret;
+}
+
+void nftnl_free_verdict(const union nftnl_data_reg *data)
+{
+ switch(data->verdict) {
+ case NFT_JUMP:
+ case NFT_GOTO:
+ xfree(data->chain);
+ break;
+ default:
+ break;
+ }
+}
diff --git a/src/expr/dup.c b/src/expr/dup.c
new file mode 100644
index 0000000..a239ff3
--- /dev/null
+++ b/src/expr/dup.c
@@ -0,0 +1,142 @@
+/*
+ * (C) 2015 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+#include "expr_ops.h"
+#include "data_reg.h"
+
+struct nftnl_expr_dup {
+ enum nft_registers sreg_addr;
+ enum nft_registers sreg_dev;
+};
+
+static int nftnl_expr_dup_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_dup *dup = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_DUP_SREG_ADDR:
+ memcpy(&dup->sreg_addr, data, sizeof(dup->sreg_addr));
+ break;
+ case NFTNL_EXPR_DUP_SREG_DEV:
+ memcpy(&dup->sreg_dev, data, sizeof(dup->sreg_dev));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *nftnl_expr_dup_get(const struct nftnl_expr *e,
+ uint16_t type, uint32_t *data_len)
+{
+ struct nftnl_expr_dup *dup = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_DUP_SREG_ADDR:
+ *data_len = sizeof(dup->sreg_addr);
+ return &dup->sreg_addr;
+ case NFTNL_EXPR_DUP_SREG_DEV:
+ *data_len = sizeof(dup->sreg_dev);
+ return &dup->sreg_dev;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_dup_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_DUP_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_DUP_SREG_ADDR:
+ case NFTA_DUP_SREG_DEV:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void nftnl_expr_dup_build(struct nlmsghdr *nlh,
+ const struct nftnl_expr *e)
+{
+ struct nftnl_expr_dup *dup = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_DUP_SREG_ADDR))
+ mnl_attr_put_u32(nlh, NFTA_DUP_SREG_ADDR, htonl(dup->sreg_addr));
+ if (e->flags & (1 << NFTNL_EXPR_DUP_SREG_DEV))
+ mnl_attr_put_u32(nlh, NFTA_DUP_SREG_DEV, htonl(dup->sreg_dev));
+}
+
+static int nftnl_expr_dup_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_dup *dup = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_DUP_MAX + 1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_dup_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_DUP_SREG_ADDR]) {
+ dup->sreg_addr = ntohl(mnl_attr_get_u32(tb[NFTA_DUP_SREG_ADDR]));
+ e->flags |= (1 << NFTNL_EXPR_DUP_SREG_ADDR);
+ }
+ if (tb[NFTA_DUP_SREG_DEV]) {
+ dup->sreg_dev = ntohl(mnl_attr_get_u32(tb[NFTA_DUP_SREG_DEV]));
+ e->flags |= (1 << NFTNL_EXPR_DUP_SREG_DEV);
+ }
+
+ return ret;
+}
+
+static int nftnl_expr_dup_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_dup *dup = nftnl_expr_data(e);
+ int offset = 0, ret;
+
+ if (e->flags & (1 << NFTNL_EXPR_DUP_SREG_ADDR)) {
+ ret = snprintf(buf + offset, remain, "sreg_addr %u ", dup->sreg_addr);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_DUP_SREG_DEV)) {
+ ret = snprintf(buf + offset, remain, "sreg_dev %u ", dup->sreg_dev);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+struct expr_ops expr_ops_dup = {
+ .name = "dup",
+ .alloc_len = sizeof(struct nftnl_expr_dup),
+ .max_attr = NFTA_DUP_MAX,
+ .set = nftnl_expr_dup_set,
+ .get = nftnl_expr_dup_get,
+ .parse = nftnl_expr_dup_parse,
+ .build = nftnl_expr_dup_build,
+ .output = nftnl_expr_dup_snprintf,
+};
diff --git a/src/expr/dynset.c b/src/expr/dynset.c
new file mode 100644
index 0000000..5bcf1c6
--- /dev/null
+++ b/src/expr/dynset.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2014, 2015 Patrick McHardy <kaber@trash.net>
+ *
+ * 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 "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+#include "data_reg.h"
+#include "expr_ops.h"
+
+struct nftnl_expr_dynset {
+ enum nft_registers sreg_key;
+ enum nft_registers sreg_data;
+ enum nft_dynset_ops op;
+ uint64_t timeout;
+ struct list_head expr_list;
+ char *set_name;
+ uint32_t set_id;
+ uint32_t dynset_flags;
+};
+
+static int
+nftnl_expr_dynset_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
+ struct nftnl_expr *expr, *next;
+
+ switch (type) {
+ case NFTNL_EXPR_DYNSET_SREG_KEY:
+ memcpy(&dynset->sreg_key, data, sizeof(dynset->sreg_key));
+ break;
+ case NFTNL_EXPR_DYNSET_SREG_DATA:
+ memcpy(&dynset->sreg_data, data, sizeof(dynset->sreg_data));
+ break;
+ case NFTNL_EXPR_DYNSET_OP:
+ memcpy(&dynset->op, data, sizeof(dynset->op));
+ break;
+ case NFTNL_EXPR_DYNSET_TIMEOUT:
+ memcpy(&dynset->timeout, data, sizeof(dynset->timeout));
+ break;
+ case NFTNL_EXPR_DYNSET_SET_NAME:
+ dynset->set_name = strdup((const char *)data);
+ if (!dynset->set_name)
+ return -1;
+ break;
+ case NFTNL_EXPR_DYNSET_SET_ID:
+ memcpy(&dynset->set_id, data, sizeof(dynset->set_id));
+ break;
+ case NFTNL_EXPR_DYNSET_EXPR:
+ list_for_each_entry_safe(expr, next, &dynset->expr_list, head)
+ nftnl_expr_free(expr);
+
+ expr = (void *)data;
+ list_add(&expr->head, &dynset->expr_list);
+ break;
+ case NFTNL_EXPR_DYNSET_FLAGS:
+ memcpy(&dynset->dynset_flags, data, sizeof(dynset->dynset_flags));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_dynset_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
+ struct nftnl_expr *expr;
+
+ switch (type) {
+ case NFTNL_EXPR_DYNSET_SREG_KEY:
+ *data_len = sizeof(dynset->sreg_key);
+ return &dynset->sreg_key;
+ case NFTNL_EXPR_DYNSET_SREG_DATA:
+ *data_len = sizeof(dynset->sreg_data);
+ return &dynset->sreg_data;
+ case NFTNL_EXPR_DYNSET_OP:
+ *data_len = sizeof(dynset->op);
+ return &dynset->op;
+ case NFTNL_EXPR_DYNSET_TIMEOUT:
+ *data_len = sizeof(dynset->timeout);
+ return &dynset->timeout;
+ case NFTNL_EXPR_DYNSET_SET_NAME:
+ *data_len = strlen(dynset->set_name) + 1;
+ return dynset->set_name;
+ case NFTNL_EXPR_DYNSET_SET_ID:
+ *data_len = sizeof(dynset->set_id);
+ return &dynset->set_id;
+ case NFTNL_EXPR_DYNSET_EXPR:
+ list_for_each_entry(expr, &dynset->expr_list, head)
+ break;
+ return expr;
+ case NFTNL_EXPR_DYNSET_FLAGS:
+ *data_len = sizeof(dynset->dynset_flags);
+ return &dynset->dynset_flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_dynset_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_SET_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_DYNSET_SREG_KEY:
+ case NFTA_DYNSET_SREG_DATA:
+ case NFTA_DYNSET_SET_ID:
+ case NFTA_DYNSET_OP:
+ case NFTA_DYNSET_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_DYNSET_TIMEOUT:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ case NFTA_DYNSET_SET_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ case NFTA_DYNSET_EXPR:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_dynset_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
+ struct nlattr *nest;
+ int num_exprs = 0;
+
+ if (e->flags & (1 << NFTNL_EXPR_DYNSET_SREG_KEY))
+ mnl_attr_put_u32(nlh, NFTA_DYNSET_SREG_KEY, htonl(dynset->sreg_key));
+ if (e->flags & (1 << NFTNL_EXPR_DYNSET_SREG_DATA))
+ mnl_attr_put_u32(nlh, NFTA_DYNSET_SREG_DATA, htonl(dynset->sreg_data));
+ if (e->flags & (1 << NFTNL_EXPR_DYNSET_OP))
+ mnl_attr_put_u32(nlh, NFTA_DYNSET_OP, htonl(dynset->op));
+ if (e->flags & (1 << NFTNL_EXPR_DYNSET_TIMEOUT))
+ mnl_attr_put_u64(nlh, NFTA_DYNSET_TIMEOUT, htobe64(dynset->timeout));
+ if (e->flags & (1 << NFTNL_EXPR_DYNSET_SET_NAME))
+ mnl_attr_put_strz(nlh, NFTA_DYNSET_SET_NAME, dynset->set_name);
+ if (e->flags & (1 << NFTNL_EXPR_DYNSET_SET_ID))
+ mnl_attr_put_u32(nlh, NFTA_DYNSET_SET_ID, htonl(dynset->set_id));
+ if (!list_empty(&dynset->expr_list)) {
+ struct nftnl_expr *expr;
+
+ list_for_each_entry(expr, &dynset->expr_list, head)
+ num_exprs++;
+
+ if (num_exprs == 1) {
+ nest = mnl_attr_nest_start(nlh, NFTA_DYNSET_EXPR);
+ list_for_each_entry(expr, &dynset->expr_list, head)
+ nftnl_expr_build_payload(nlh, expr);
+ mnl_attr_nest_end(nlh, nest);
+ } else if (num_exprs > 1) {
+ struct nlattr *nest1, *nest2;
+
+ nest1 = mnl_attr_nest_start(nlh, NFTA_DYNSET_EXPRESSIONS);
+ list_for_each_entry(expr, &dynset->expr_list, head) {
+ nest2 = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM);
+ nftnl_expr_build_payload(nlh, expr);
+ mnl_attr_nest_end(nlh, nest2);
+ }
+ mnl_attr_nest_end(nlh, nest1);
+ }
+ }
+ if (e->flags & (1 << NFTNL_EXPR_DYNSET_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_DYNSET_FLAGS,
+ htonl(dynset->dynset_flags));
+}
+
+EXPORT_SYMBOL(nftnl_expr_add_expr);
+void nftnl_expr_add_expr(struct nftnl_expr *e, uint32_t type,
+ struct nftnl_expr *expr)
+{
+ struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
+
+ list_add_tail(&expr->head, &dynset->expr_list);
+}
+
+EXPORT_SYMBOL(nftnl_expr_expr_foreach);
+int nftnl_expr_expr_foreach(const struct nftnl_expr *e,
+ int (*cb)(struct nftnl_expr *e, void *data),
+ void *data)
+{
+ struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
+ struct nftnl_expr *cur, *tmp;
+ int ret;
+
+ list_for_each_entry_safe(cur, tmp, &dynset->expr_list, head) {
+ ret = cb(cur, data);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int
+nftnl_expr_dynset_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_SET_MAX+1] = {};
+ struct nftnl_expr *expr, *next;
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_dynset_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_DYNSET_SREG_KEY]) {
+ dynset->sreg_key = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_SREG_KEY]));
+ e->flags |= (1 << NFTNL_EXPR_DYNSET_SREG_KEY);
+ }
+ if (tb[NFTA_DYNSET_SREG_DATA]) {
+ dynset->sreg_data = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_SREG_DATA]));
+ e->flags |= (1 << NFTNL_EXPR_DYNSET_SREG_DATA);
+ }
+ if (tb[NFTA_DYNSET_OP]) {
+ dynset->op = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_OP]));
+ e->flags |= (1 << NFTNL_EXPR_DYNSET_OP);
+ }
+ if (tb[NFTA_DYNSET_TIMEOUT]) {
+ dynset->timeout = be64toh(mnl_attr_get_u64(tb[NFTA_DYNSET_TIMEOUT]));
+ e->flags |= (1 << NFTNL_EXPR_DYNSET_TIMEOUT);
+ }
+ if (tb[NFTA_DYNSET_SET_NAME]) {
+ dynset->set_name =
+ strdup(mnl_attr_get_str(tb[NFTA_DYNSET_SET_NAME]));
+ if (!dynset->set_name)
+ return -1;
+ e->flags |= (1 << NFTNL_EXPR_DYNSET_SET_NAME);
+ }
+ if (tb[NFTA_DYNSET_SET_ID]) {
+ dynset->set_id = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_SET_ID]));
+ e->flags |= (1 << NFTNL_EXPR_DYNSET_SET_ID);
+ }
+ if (tb[NFTA_DYNSET_EXPR]) {
+ expr = nftnl_expr_parse(tb[NFTA_DYNSET_EXPR]);
+ if (expr == NULL)
+ return -1;
+
+ list_add(&expr->head, &dynset->expr_list);
+ e->flags |= (1 << NFTNL_EXPR_DYNSET_EXPR);
+ } else if (tb[NFTA_DYNSET_EXPRESSIONS]) {
+ struct nlattr *attr2;
+
+ mnl_attr_for_each_nested(attr2, tb[NFTA_DYNSET_EXPRESSIONS]) {
+ if (mnl_attr_get_type(attr2) != NFTA_LIST_ELEM)
+ goto out_dynset_expr;
+
+ expr = nftnl_expr_parse(attr2);
+ if (!expr)
+ goto out_dynset_expr;
+
+ list_add_tail(&expr->head, &dynset->expr_list);
+ }
+ e->flags |= (1 << NFTNL_EXPR_DYNSET_EXPRESSIONS);
+ }
+ if (tb[NFTA_DYNSET_FLAGS]) {
+ dynset->dynset_flags = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_DYNSET_FLAGS);
+ }
+
+ return ret;
+out_dynset_expr:
+ list_for_each_entry_safe(expr, next, &dynset->expr_list, head)
+ nftnl_expr_free(expr);
+
+ return -1;
+}
+
+static const char *op2str_array[] = {
+ [NFT_DYNSET_OP_ADD] = "add",
+ [NFT_DYNSET_OP_UPDATE] = "update",
+ [NFT_DYNSET_OP_DELETE] = "delete",
+};
+
+static const char *op2str(enum nft_dynset_ops op)
+{
+ if (op > NFT_DYNSET_OP_DELETE)
+ return "unknown";
+ return op2str_array[op];
+}
+
+static int
+nftnl_expr_dynset_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
+ struct nftnl_expr *expr;
+ int offset = 0, ret;
+
+ ret = snprintf(buf, remain, "%s reg_key %u set %s ",
+ op2str(dynset->op), dynset->sreg_key, dynset->set_name);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (e->flags & (1 << NFTNL_EXPR_DYNSET_SREG_DATA)) {
+ ret = snprintf(buf + offset, remain, "sreg_data %u ",
+ dynset->sreg_data);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (e->flags & (1 << NFTNL_EXPR_DYNSET_TIMEOUT)) {
+ ret = snprintf(buf + offset, remain, "timeout %"PRIu64"ms ",
+ dynset->timeout);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ list_for_each_entry(expr, &dynset->expr_list, head) {
+ ret = snprintf(buf + offset, remain, "expr [ %s ",
+ expr->ops->name);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_expr_snprintf(buf + offset, remain, expr,
+ NFTNL_OUTPUT_DEFAULT,
+ NFTNL_OF_EVENT_ANY);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = snprintf(buf + offset, remain, "] ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+static void nftnl_expr_dynset_init(const struct nftnl_expr *e)
+{
+ struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
+
+ INIT_LIST_HEAD(&dynset->expr_list);
+}
+
+static void nftnl_expr_dynset_free(const struct nftnl_expr *e)
+{
+ struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
+ struct nftnl_expr *expr, *next;
+
+ xfree(dynset->set_name);
+ list_for_each_entry_safe(expr, next, &dynset->expr_list, head)
+ nftnl_expr_free(expr);
+}
+
+struct expr_ops expr_ops_dynset = {
+ .name = "dynset",
+ .alloc_len = sizeof(struct nftnl_expr_dynset),
+ .max_attr = NFTA_DYNSET_MAX,
+ .init = nftnl_expr_dynset_init,
+ .free = nftnl_expr_dynset_free,
+ .set = nftnl_expr_dynset_set,
+ .get = nftnl_expr_dynset_get,
+ .parse = nftnl_expr_dynset_parse,
+ .build = nftnl_expr_dynset_build,
+ .output = nftnl_expr_dynset_snprintf,
+};
diff --git a/src/expr/exthdr.c b/src/expr/exthdr.c
new file mode 100644
index 0000000..739c7ff
--- /dev/null
+++ b/src/expr/exthdr.c
@@ -0,0 +1,271 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <limits.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <libmnl/libmnl.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+#ifndef IPPROTO_MH
+#define IPPROTO_MH 135
+#endif
+
+struct nftnl_expr_exthdr {
+ enum nft_registers dreg;
+ enum nft_registers sreg;
+ uint32_t offset;
+ uint32_t len;
+ uint8_t type;
+ uint32_t op;
+ uint32_t flags;
+};
+
+static int
+nftnl_expr_exthdr_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_EXTHDR_DREG:
+ memcpy(&exthdr->dreg, data, sizeof(exthdr->dreg));
+ break;
+ case NFTNL_EXPR_EXTHDR_TYPE:
+ memcpy(&exthdr->type, data, sizeof(exthdr->type));
+ break;
+ case NFTNL_EXPR_EXTHDR_OFFSET:
+ memcpy(&exthdr->offset, data, sizeof(exthdr->offset));
+ break;
+ case NFTNL_EXPR_EXTHDR_LEN:
+ memcpy(&exthdr->len, data, sizeof(exthdr->len));
+ break;
+ case NFTNL_EXPR_EXTHDR_OP:
+ memcpy(&exthdr->op, data, sizeof(exthdr->op));
+ break;
+ case NFTNL_EXPR_EXTHDR_FLAGS:
+ memcpy(&exthdr->flags, data, sizeof(exthdr->flags));
+ break;
+ case NFTNL_EXPR_EXTHDR_SREG:
+ memcpy(&exthdr->sreg, data, sizeof(exthdr->sreg));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_exthdr_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_EXTHDR_DREG:
+ *data_len = sizeof(exthdr->dreg);
+ return &exthdr->dreg;
+ case NFTNL_EXPR_EXTHDR_TYPE:
+ *data_len = sizeof(exthdr->type);
+ return &exthdr->type;
+ case NFTNL_EXPR_EXTHDR_OFFSET:
+ *data_len = sizeof(exthdr->offset);
+ return &exthdr->offset;
+ case NFTNL_EXPR_EXTHDR_LEN:
+ *data_len = sizeof(exthdr->len);
+ return &exthdr->len;
+ case NFTNL_EXPR_EXTHDR_OP:
+ *data_len = sizeof(exthdr->op);
+ return &exthdr->op;
+ case NFTNL_EXPR_EXTHDR_FLAGS:
+ *data_len = sizeof(exthdr->flags);
+ return &exthdr->flags;
+ case NFTNL_EXPR_EXTHDR_SREG:
+ *data_len = sizeof(exthdr->sreg);
+ return &exthdr->sreg;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_exthdr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_EXTHDR_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_EXTHDR_TYPE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+ abi_breakage();
+ break;
+ case NFTA_EXTHDR_DREG:
+ case NFTA_EXTHDR_SREG:
+ case NFTA_EXTHDR_OFFSET:
+ case NFTA_EXTHDR_LEN:
+ case NFTA_EXTHDR_OP:
+ case NFTA_EXTHDR_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_exthdr_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_EXTHDR_DREG))
+ mnl_attr_put_u32(nlh, NFTA_EXTHDR_DREG, htonl(exthdr->dreg));
+ if (e->flags & (1 << NFTNL_EXPR_EXTHDR_SREG))
+ mnl_attr_put_u32(nlh, NFTA_EXTHDR_SREG, htonl(exthdr->sreg));
+ if (e->flags & (1 << NFTNL_EXPR_EXTHDR_TYPE))
+ mnl_attr_put_u8(nlh, NFTA_EXTHDR_TYPE, exthdr->type);
+ if (e->flags & (1 << NFTNL_EXPR_EXTHDR_OFFSET))
+ mnl_attr_put_u32(nlh, NFTA_EXTHDR_OFFSET, htonl(exthdr->offset));
+ if (e->flags & (1 << NFTNL_EXPR_EXTHDR_LEN))
+ mnl_attr_put_u32(nlh, NFTA_EXTHDR_LEN, htonl(exthdr->len));
+ if (e->flags & (1 << NFTNL_EXPR_EXTHDR_OP))
+ mnl_attr_put_u32(nlh, NFTA_EXTHDR_OP, htonl(exthdr->op));
+ if (e->flags & (1 << NFTNL_EXPR_EXTHDR_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_EXTHDR_FLAGS, htonl(exthdr->flags));
+}
+
+static int
+nftnl_expr_exthdr_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_EXTHDR_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_exthdr_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_EXTHDR_DREG]) {
+ exthdr->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_EXTHDR_DREG);
+ }
+ if (tb[NFTA_EXTHDR_SREG]) {
+ exthdr->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_SREG]));
+ e->flags |= (1 << NFTNL_EXPR_EXTHDR_SREG);
+ }
+ if (tb[NFTA_EXTHDR_TYPE]) {
+ exthdr->type = mnl_attr_get_u8(tb[NFTA_EXTHDR_TYPE]);
+ e->flags |= (1 << NFTNL_EXPR_EXTHDR_TYPE);
+ }
+ if (tb[NFTA_EXTHDR_OFFSET]) {
+ exthdr->offset = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_OFFSET]));
+ e->flags |= (1 << NFTNL_EXPR_EXTHDR_OFFSET);
+ }
+ if (tb[NFTA_EXTHDR_LEN]) {
+ exthdr->len = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_LEN]));
+ e->flags |= (1 << NFTNL_EXPR_EXTHDR_LEN);
+ }
+ if (tb[NFTA_EXTHDR_OP]) {
+ exthdr->op = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_OP]));
+ e->flags |= (1 << NFTNL_EXPR_EXTHDR_OP);
+ }
+ if (tb[NFTA_EXTHDR_FLAGS]) {
+ exthdr->flags = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_EXTHDR_FLAGS);
+ }
+
+ return 0;
+}
+
+static const char *op2str(uint8_t op)
+{
+ switch (op) {
+ case NFT_EXTHDR_OP_TCPOPT:
+ return " tcpopt";
+ case NFT_EXTHDR_OP_IPV6:
+ return " ipv6";
+ case NFT_EXTHDR_OP_IPV4:
+ return " ipv4";
+ default:
+ return "";
+ }
+}
+
+static inline int str2exthdr_op(const char* str)
+{
+ if (!strcmp(str, "tcpopt"))
+ return NFT_EXTHDR_OP_TCPOPT;
+ if (!strcmp(str, "ipv4"))
+ return NFT_EXTHDR_OP_IPV4;
+
+ /* if str == "ipv6" or anything else */
+ return NFT_EXTHDR_OP_IPV6;
+}
+
+static inline int str2exthdr_type(const char *str)
+{
+ if (strcmp(str, "hopopts") == 0)
+ return IPPROTO_HOPOPTS;
+ else if (strcmp(str, "routing") == 0)
+ return IPPROTO_ROUTING;
+ else if (strcmp(str, "fragment") == 0)
+ return IPPROTO_FRAGMENT;
+ else if (strcmp(str, "dstopts") == 0)
+ return IPPROTO_DSTOPTS;
+ else if (strcmp(str, "mh") == 0)
+ return IPPROTO_MH;
+
+ return -1;
+}
+
+static int
+nftnl_expr_exthdr_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_EXTHDR_DREG))
+ return snprintf(buf, len, "load%s %ub @ %u + %u%s => reg %u ",
+ op2str(exthdr->op), exthdr->len, exthdr->type,
+ exthdr->offset,
+ exthdr->flags & NFT_EXTHDR_F_PRESENT ? " present" : "",
+ exthdr->dreg);
+ else if (e->flags & (1 << NFTNL_EXPR_EXTHDR_SREG))
+ return snprintf(buf, len, "write%s reg %u => %ub @ %u + %u ",
+ op2str(exthdr->op), exthdr->sreg, exthdr->len, exthdr->type,
+ exthdr->offset);
+ else if (exthdr->op == NFT_EXTHDR_OP_TCPOPT && exthdr->len == 0)
+ return snprintf(buf, len, "reset tcpopt %u ", exthdr->type);
+ else
+ return snprintf(buf, len, "op %u len %u type %u offset %u ",
+ exthdr->op, exthdr->len, exthdr->type, exthdr->offset);
+
+}
+
+struct expr_ops expr_ops_exthdr = {
+ .name = "exthdr",
+ .alloc_len = sizeof(struct nftnl_expr_exthdr),
+ .max_attr = NFTA_EXTHDR_MAX,
+ .set = nftnl_expr_exthdr_set,
+ .get = nftnl_expr_exthdr_get,
+ .parse = nftnl_expr_exthdr_parse,
+ .build = nftnl_expr_exthdr_build,
+ .output = nftnl_expr_exthdr_snprintf,
+};
diff --git a/src/expr/fib.c b/src/expr/fib.c
new file mode 100644
index 0000000..957f929
--- /dev/null
+++ b/src/expr/fib.c
@@ -0,0 +1,202 @@
+/*
+ * (C) 2016 Red Hat GmbH
+ * Author: Florian Westphal <fw@strlen.de>
+ *
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_fib {
+ uint32_t flags;
+ uint32_t result;
+ enum nft_registers dreg;
+};
+
+static int
+nftnl_expr_fib_set(struct nftnl_expr *e, uint16_t result,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_fib *fib = nftnl_expr_data(e);
+
+ switch (result) {
+ case NFTNL_EXPR_FIB_RESULT:
+ memcpy(&fib->result, data, sizeof(fib->result));
+ break;
+ case NFTNL_EXPR_FIB_DREG:
+ memcpy(&fib->dreg, data, sizeof(fib->dreg));
+ break;
+ case NFTNL_EXPR_FIB_FLAGS:
+ memcpy(&fib->flags, data, sizeof(fib->flags));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_fib_get(const struct nftnl_expr *e, uint16_t result,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_fib *fib = nftnl_expr_data(e);
+
+ switch (result) {
+ case NFTNL_EXPR_FIB_RESULT:
+ *data_len = sizeof(fib->result);
+ return &fib->result;
+ case NFTNL_EXPR_FIB_DREG:
+ *data_len = sizeof(fib->dreg);
+ return &fib->dreg;
+ case NFTNL_EXPR_FIB_FLAGS:
+ *data_len = sizeof(fib->flags);
+ return &fib->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_fib_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_FIB_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_FIB_RESULT:
+ case NFTA_FIB_DREG:
+ case NFTA_FIB_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_fib_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_fib *fib = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_FIB_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_FIB_FLAGS, htonl(fib->flags));
+ if (e->flags & (1 << NFTNL_EXPR_FIB_RESULT))
+ mnl_attr_put_u32(nlh, NFTA_FIB_RESULT, htonl(fib->result));
+ if (e->flags & (1 << NFTNL_EXPR_FIB_DREG))
+ mnl_attr_put_u32(nlh, NFTA_FIB_DREG, htonl(fib->dreg));
+}
+
+static int
+nftnl_expr_fib_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_fib *fib = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_FIB_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_fib_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_FIB_RESULT]) {
+ fib->result = ntohl(mnl_attr_get_u32(tb[NFTA_FIB_RESULT]));
+ e->flags |= (1 << NFTNL_EXPR_FIB_RESULT);
+ }
+ if (tb[NFTA_FIB_DREG]) {
+ fib->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_FIB_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_FIB_DREG);
+ }
+ if (tb[NFTA_FIB_FLAGS]) {
+ fib->flags = ntohl(mnl_attr_get_u32(tb[NFTA_FIB_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_FIB_FLAGS);
+ }
+ return ret;
+}
+
+static const char *fib_type[NFT_FIB_RESULT_MAX + 1] = {
+ [NFT_FIB_RESULT_OIF] = "oif",
+ [NFT_FIB_RESULT_OIFNAME] = "oifname",
+ [NFT_FIB_RESULT_ADDRTYPE] = "type",
+};
+
+static const char *fib_type_str(enum nft_fib_result r)
+{
+ if (r <= NFT_FIB_RESULT_MAX)
+ return fib_type[r];
+
+ return "unknown";
+}
+
+static int
+nftnl_expr_fib_snprintf(char *buf, size_t remain,
+ uint32_t printflags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_fib *fib = nftnl_expr_data(e);
+ uint32_t flags = fib->flags & ~NFTA_FIB_F_PRESENT;
+ uint32_t present_flag = fib->flags & NFTA_FIB_F_PRESENT;
+ int offset = 0, ret, i;
+ static const struct {
+ int bit;
+ const char *name;
+ } tab[] = {
+ { NFTA_FIB_F_SADDR, "saddr" },
+ { NFTA_FIB_F_DADDR, "daddr" },
+ { NFTA_FIB_F_MARK, "mark" },
+ { NFTA_FIB_F_IIF, "iif" },
+ { NFTA_FIB_F_OIF, "oif" },
+ };
+
+ for (i = 0; i < (sizeof(tab) / sizeof(tab[0])); i++) {
+ if (flags & tab[i].bit) {
+ ret = snprintf(buf + offset, remain, "%s ",
+ tab[i].name);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ flags &= ~tab[i].bit;
+ if (flags) {
+ ret = snprintf(buf + offset, remain, ". ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ }
+ }
+
+ if (flags) {
+ ret = snprintf(buf + offset, remain, "unknown 0x%" PRIx32,
+ flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ ret = snprintf(buf + offset, remain, "%s%s => reg %d ",
+ fib_type_str(fib->result),
+ present_flag ? " present" : "",
+ fib->dreg);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ return offset;
+}
+
+struct expr_ops expr_ops_fib = {
+ .name = "fib",
+ .alloc_len = sizeof(struct nftnl_expr_fib),
+ .max_attr = NFTA_FIB_MAX,
+ .set = nftnl_expr_fib_set,
+ .get = nftnl_expr_fib_get,
+ .parse = nftnl_expr_fib_parse,
+ .build = nftnl_expr_fib_build,
+ .output = nftnl_expr_fib_snprintf,
+};
diff --git a/src/expr/flow_offload.c b/src/expr/flow_offload.c
new file mode 100644
index 0000000..4fc0563
--- /dev/null
+++ b/src/expr/flow_offload.c
@@ -0,0 +1,124 @@
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h> /* for memcpy */
+#include <arpa/inet.h>
+#include <errno.h>
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+
+struct nftnl_expr_flow {
+ char *table_name;
+};
+
+static int nftnl_expr_flow_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_flow *flow = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_FLOW_TABLE_NAME:
+ flow->table_name = strdup((const char *)data);
+ if (!flow->table_name)
+ return -1;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *nftnl_expr_flow_get(const struct nftnl_expr *e,
+ uint16_t type, uint32_t *data_len)
+{
+ struct nftnl_expr_flow *flow = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_FLOW_TABLE_NAME:
+ *data_len = strlen(flow->table_name) + 1;
+ return flow->table_name;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_flow_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_FLOW_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_FLOW_TABLE_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void nftnl_expr_flow_build(struct nlmsghdr *nlh,
+ const struct nftnl_expr *e)
+{
+ struct nftnl_expr_flow *flow = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_FLOW_TABLE_NAME))
+ mnl_attr_put_strz(nlh, NFTA_FLOW_TABLE_NAME, flow->table_name);
+}
+
+static int nftnl_expr_flow_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_flow *flow = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_FLOW_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_flow_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_FLOW_TABLE_NAME]) {
+ flow->table_name =
+ strdup(mnl_attr_get_str(tb[NFTA_FLOW_TABLE_NAME]));
+ if (!flow->table_name)
+ return -1;
+ e->flags |= (1 << NFTNL_EXPR_FLOW_TABLE_NAME);
+ }
+
+ return ret;
+}
+
+static int nftnl_expr_flow_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_flow *l = nftnl_expr_data(e);
+ int offset = 0, ret;
+
+ ret = snprintf(buf, remain, "flowtable %s ", l->table_name);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ return offset;
+}
+
+static void nftnl_expr_flow_free(const struct nftnl_expr *e)
+{
+ struct nftnl_expr_flow *flow = nftnl_expr_data(e);
+
+ xfree(flow->table_name);
+}
+
+struct expr_ops expr_ops_flow = {
+ .name = "flow_offload",
+ .alloc_len = sizeof(struct nftnl_expr_flow),
+ .max_attr = NFTA_FLOW_MAX,
+ .free = nftnl_expr_flow_free,
+ .set = nftnl_expr_flow_set,
+ .get = nftnl_expr_flow_get,
+ .parse = nftnl_expr_flow_parse,
+ .build = nftnl_expr_flow_build,
+ .output = nftnl_expr_flow_snprintf,
+};
diff --git a/src/expr/fwd.c b/src/expr/fwd.c
new file mode 100644
index 0000000..51f6612
--- /dev/null
+++ b/src/expr/fwd.c
@@ -0,0 +1,162 @@
+/*
+ * (C) 2015 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+#include "expr_ops.h"
+#include "data_reg.h"
+
+struct nftnl_expr_fwd {
+ enum nft_registers sreg_dev;
+ enum nft_registers sreg_addr;
+ uint32_t nfproto;
+};
+
+static int nftnl_expr_fwd_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_fwd *fwd = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_FWD_SREG_DEV:
+ memcpy(&fwd->sreg_dev, data, sizeof(fwd->sreg_dev));
+ break;
+ case NFTNL_EXPR_FWD_SREG_ADDR:
+ memcpy(&fwd->sreg_addr, data, sizeof(fwd->sreg_addr));
+ break;
+ case NFTNL_EXPR_FWD_NFPROTO:
+ memcpy(&fwd->nfproto, data, sizeof(fwd->nfproto));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *nftnl_expr_fwd_get(const struct nftnl_expr *e,
+ uint16_t type, uint32_t *data_len)
+{
+ struct nftnl_expr_fwd *fwd = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_FWD_SREG_DEV:
+ *data_len = sizeof(fwd->sreg_dev);
+ return &fwd->sreg_dev;
+ case NFTNL_EXPR_FWD_SREG_ADDR:
+ *data_len = sizeof(fwd->sreg_addr);
+ return &fwd->sreg_addr;
+ case NFTNL_EXPR_FWD_NFPROTO:
+ *data_len = sizeof(fwd->nfproto);
+ return &fwd->nfproto;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_fwd_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_FWD_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_FWD_SREG_DEV:
+ case NFTA_FWD_SREG_ADDR:
+ case NFTA_FWD_NFPROTO:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void nftnl_expr_fwd_build(struct nlmsghdr *nlh,
+ const struct nftnl_expr *e)
+{
+ struct nftnl_expr_fwd *fwd = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_FWD_SREG_DEV))
+ mnl_attr_put_u32(nlh, NFTA_FWD_SREG_DEV, htonl(fwd->sreg_dev));
+ if (e->flags & (1 << NFTNL_EXPR_FWD_SREG_ADDR))
+ mnl_attr_put_u32(nlh, NFTA_FWD_SREG_ADDR, htonl(fwd->sreg_addr));
+ if (e->flags & (1 << NFTNL_EXPR_FWD_NFPROTO))
+ mnl_attr_put_u32(nlh, NFTA_FWD_NFPROTO, htonl(fwd->nfproto));
+}
+
+static int nftnl_expr_fwd_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_fwd *fwd = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_FWD_MAX + 1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_fwd_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_FWD_SREG_DEV]) {
+ fwd->sreg_dev = ntohl(mnl_attr_get_u32(tb[NFTA_FWD_SREG_DEV]));
+ e->flags |= (1 << NFTNL_EXPR_FWD_SREG_DEV);
+ }
+ if (tb[NFTA_FWD_SREG_ADDR]) {
+ fwd->sreg_addr = ntohl(mnl_attr_get_u32(tb[NFTA_FWD_SREG_ADDR]));
+ e->flags |= (1 << NFTNL_EXPR_FWD_SREG_ADDR);
+ }
+ if (tb[NFTA_FWD_NFPROTO]) {
+ fwd->nfproto = ntohl(mnl_attr_get_u32(tb[NFTA_FWD_NFPROTO]));
+ e->flags |= (1 << NFTNL_EXPR_FWD_NFPROTO);
+ }
+
+ return ret;
+}
+
+static int nftnl_expr_fwd_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_fwd *fwd = nftnl_expr_data(e);
+ int offset = 0, ret;
+
+ if (e->flags & (1 << NFTNL_EXPR_FWD_SREG_DEV)) {
+ ret = snprintf(buf + offset, remain, "sreg_dev %u ",
+ fwd->sreg_dev);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (e->flags & (1 << NFTNL_EXPR_FWD_SREG_ADDR)) {
+ ret = snprintf(buf + offset, remain, "sreg_addr %u ",
+ fwd->sreg_addr);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (e->flags & (1 << NFTNL_EXPR_FWD_NFPROTO)) {
+ ret = snprintf(buf + offset, remain, "nfproto %u ",
+ fwd->nfproto);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+struct expr_ops expr_ops_fwd = {
+ .name = "fwd",
+ .alloc_len = sizeof(struct nftnl_expr_fwd),
+ .max_attr = NFTA_FWD_MAX,
+ .set = nftnl_expr_fwd_set,
+ .get = nftnl_expr_fwd_get,
+ .parse = nftnl_expr_fwd_parse,
+ .build = nftnl_expr_fwd_build,
+ .output = nftnl_expr_fwd_snprintf,
+};
diff --git a/src/expr/hash.c b/src/expr/hash.c
new file mode 100644
index 0000000..6e2dd19
--- /dev/null
+++ b/src/expr/hash.c
@@ -0,0 +1,230 @@
+/*
+ * (C) 2016 by Laura Garcia <nevola@gmail.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 <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_hash {
+ enum nft_hash_types type;
+ enum nft_registers sreg;
+ enum nft_registers dreg;
+ unsigned int len;
+ unsigned int modulus;
+ unsigned int seed;
+ unsigned int offset;
+};
+
+static int
+nftnl_expr_hash_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_hash *hash = nftnl_expr_data(e);
+ switch (type) {
+ case NFTNL_EXPR_HASH_SREG:
+ memcpy(&hash->sreg, data, sizeof(hash->sreg));
+ break;
+ case NFTNL_EXPR_HASH_DREG:
+ memcpy(&hash->dreg, data, sizeof(hash->dreg));
+ break;
+ case NFTNL_EXPR_HASH_LEN:
+ memcpy(&hash->len, data, sizeof(hash->len));
+ break;
+ case NFTNL_EXPR_HASH_MODULUS:
+ memcpy(&hash->modulus, data, sizeof(hash->modulus));
+ break;
+ case NFTNL_EXPR_HASH_SEED:
+ memcpy(&hash->seed, data, sizeof(hash->seed));
+ break;
+ case NFTNL_EXPR_HASH_OFFSET:
+ memcpy(&hash->offset, data, sizeof(hash->offset));
+ break;
+ case NFTNL_EXPR_HASH_TYPE:
+ memcpy(&hash->type, data, sizeof(hash->type));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_hash_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_hash *hash = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_HASH_SREG:
+ *data_len = sizeof(hash->sreg);
+ return &hash->sreg;
+ case NFTNL_EXPR_HASH_DREG:
+ *data_len = sizeof(hash->dreg);
+ return &hash->dreg;
+ case NFTNL_EXPR_HASH_LEN:
+ *data_len = sizeof(hash->len);
+ return &hash->len;
+ case NFTNL_EXPR_HASH_MODULUS:
+ *data_len = sizeof(hash->modulus);
+ return &hash->modulus;
+ case NFTNL_EXPR_HASH_SEED:
+ *data_len = sizeof(hash->seed);
+ return &hash->seed;
+ case NFTNL_EXPR_HASH_OFFSET:
+ *data_len = sizeof(hash->offset);
+ return &hash->offset;
+ case NFTNL_EXPR_HASH_TYPE:
+ *data_len = sizeof(hash->type);
+ return &hash->type;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_hash_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_HASH_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_HASH_SREG:
+ case NFTA_HASH_DREG:
+ case NFTA_HASH_LEN:
+ case NFTA_HASH_MODULUS:
+ case NFTA_HASH_SEED:
+ case NFTA_HASH_OFFSET:
+ case NFTA_HASH_TYPE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_hash_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_hash *hash = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_HASH_SREG))
+ mnl_attr_put_u32(nlh, NFTA_HASH_SREG, htonl(hash->sreg));
+ if (e->flags & (1 << NFTNL_EXPR_HASH_DREG))
+ mnl_attr_put_u32(nlh, NFTA_HASH_DREG, htonl(hash->dreg));
+ if (e->flags & (1 << NFTNL_EXPR_HASH_LEN))
+ mnl_attr_put_u32(nlh, NFTA_HASH_LEN, htonl(hash->len));
+ if (e->flags & (1 << NFTNL_EXPR_HASH_MODULUS))
+ mnl_attr_put_u32(nlh, NFTA_HASH_MODULUS, htonl(hash->modulus));
+ if (e->flags & (1 << NFTNL_EXPR_HASH_SEED))
+ mnl_attr_put_u32(nlh, NFTA_HASH_SEED, htonl(hash->seed));
+ if (e->flags & (1 << NFTNL_EXPR_HASH_OFFSET))
+ mnl_attr_put_u32(nlh, NFTA_HASH_OFFSET, htonl(hash->offset));
+ if (e->flags & (1 << NFTNL_EXPR_HASH_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_HASH_TYPE, htonl(hash->type));
+}
+
+static int
+nftnl_expr_hash_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_hash *hash = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_HASH_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_hash_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_HASH_SREG]) {
+ hash->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_HASH_SREG]));
+ e->flags |= (1 << NFTNL_EXPR_HASH_SREG);
+ }
+ if (tb[NFTA_HASH_DREG]) {
+ hash->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_HASH_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_HASH_DREG);
+ }
+ if (tb[NFTA_HASH_LEN]) {
+ hash->len = ntohl(mnl_attr_get_u32(tb[NFTA_HASH_LEN]));
+ e->flags |= (1 << NFTNL_EXPR_HASH_LEN);
+ }
+ if (tb[NFTA_HASH_MODULUS]) {
+ hash->modulus = ntohl(mnl_attr_get_u32(tb[NFTA_HASH_MODULUS]));
+ e->flags |= (1 << NFTNL_EXPR_HASH_MODULUS);
+ }
+ if (tb[NFTA_HASH_SEED]) {
+ hash->seed = ntohl(mnl_attr_get_u32(tb[NFTA_HASH_SEED]));
+ e->flags |= (1 << NFTNL_EXPR_HASH_SEED);
+ }
+ if (tb[NFTA_HASH_OFFSET]) {
+ hash->offset = ntohl(mnl_attr_get_u32(tb[NFTA_HASH_OFFSET]));
+ e->flags |= (1 << NFTNL_EXPR_HASH_OFFSET);
+ }
+ if (tb[NFTA_HASH_TYPE]) {
+ hash->type = ntohl(mnl_attr_get_u32(tb[NFTA_HASH_TYPE]));
+ e->flags |= (1 << NFTNL_EXPR_HASH_TYPE);
+ }
+
+ return ret;
+}
+
+static int
+nftnl_expr_hash_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_hash *hash = nftnl_expr_data(e);
+ int offset = 0, ret;
+
+ switch (hash->type) {
+ case NFT_HASH_SYM:
+ ret =
+ snprintf(buf, remain, "reg %u = symhash() %% mod %u ",
+ hash->dreg,
+ hash->modulus);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ break;
+ case NFT_HASH_JENKINS:
+ default:
+ ret =
+ snprintf(buf, remain,
+ "reg %u = jhash(reg %u, %u, 0x%x) %% mod %u ",
+ hash->dreg, hash->sreg, hash->len, hash->seed,
+ hash->modulus);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ break;
+ }
+
+ if (hash->offset) {
+ ret = snprintf(buf + offset, remain, "offset %u ",
+ hash->offset);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+struct expr_ops expr_ops_hash = {
+ .name = "hash",
+ .alloc_len = sizeof(struct nftnl_expr_hash),
+ .max_attr = NFTA_HASH_MAX,
+ .set = nftnl_expr_hash_set,
+ .get = nftnl_expr_hash_get,
+ .parse = nftnl_expr_hash_parse,
+ .build = nftnl_expr_hash_build,
+ .output = nftnl_expr_hash_snprintf,
+};
diff --git a/src/expr/immediate.c b/src/expr/immediate.c
new file mode 100644
index 0000000..5d477a8
--- /dev/null
+++ b/src/expr/immediate.c
@@ -0,0 +1,233 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_immediate {
+ union nftnl_data_reg data;
+ enum nft_registers dreg;
+};
+
+static int
+nftnl_expr_immediate_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_immediate *imm = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_IMM_DREG:
+ memcpy(&imm->dreg, data, sizeof(imm->dreg));
+ break;
+ case NFTNL_EXPR_IMM_DATA:
+ memcpy(&imm->data.val, data, data_len);
+ imm->data.len = data_len;
+ break;
+ case NFTNL_EXPR_IMM_VERDICT:
+ memcpy(&imm->data.verdict, data, sizeof(imm->data.verdict));
+ break;
+ case NFTNL_EXPR_IMM_CHAIN:
+ if (e->flags & (1 << NFTNL_EXPR_IMM_CHAIN))
+ xfree(imm->data.chain);
+
+ imm->data.chain = strdup(data);
+ if (!imm->data.chain)
+ return -1;
+ break;
+ case NFTNL_EXPR_IMM_CHAIN_ID:
+ memcpy(&imm->data.chain_id, data, sizeof(uint32_t));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_immediate_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_immediate *imm = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_IMM_DREG:
+ *data_len = sizeof(imm->dreg);
+ return &imm->dreg;
+ case NFTNL_EXPR_IMM_DATA:
+ *data_len = imm->data.len;
+ return &imm->data.val;
+ case NFTNL_EXPR_IMM_VERDICT:
+ *data_len = sizeof(imm->data.verdict);
+ return &imm->data.verdict;
+ case NFTNL_EXPR_IMM_CHAIN:
+ *data_len = strlen(imm->data.chain)+1;
+ return imm->data.chain;
+ case NFTNL_EXPR_IMM_CHAIN_ID:
+ *data_len = sizeof(imm->data.chain_id);
+ return &imm->data.chain_id;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_immediate_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_IMMEDIATE_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_IMMEDIATE_DREG:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_IMMEDIATE_DATA:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_immediate_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_immediate *imm = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_IMM_DREG))
+ mnl_attr_put_u32(nlh, NFTA_IMMEDIATE_DREG, htonl(imm->dreg));
+
+ /* Sane configurations allows you to set ONLY one of these two below */
+ if (e->flags & (1 << NFTNL_EXPR_IMM_DATA)) {
+ struct nlattr *nest;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_IMMEDIATE_DATA);
+ mnl_attr_put(nlh, NFTA_DATA_VALUE, imm->data.len, imm->data.val);
+ mnl_attr_nest_end(nlh, nest);
+
+ } else if (e->flags & (1 << NFTNL_EXPR_IMM_VERDICT)) {
+ struct nlattr *nest1, *nest2;
+
+ nest1 = mnl_attr_nest_start(nlh, NFTA_IMMEDIATE_DATA);
+ nest2 = mnl_attr_nest_start(nlh, NFTA_DATA_VERDICT);
+ mnl_attr_put_u32(nlh, NFTA_VERDICT_CODE, htonl(imm->data.verdict));
+ if (e->flags & (1 << NFTNL_EXPR_IMM_CHAIN))
+ mnl_attr_put_strz(nlh, NFTA_VERDICT_CHAIN, imm->data.chain);
+ if (e->flags & (1 << NFTNL_EXPR_IMM_CHAIN_ID)) {
+ mnl_attr_put_u32(nlh, NFTA_VERDICT_CHAIN_ID,
+ htonl(imm->data.chain_id));
+ }
+
+ mnl_attr_nest_end(nlh, nest1);
+ mnl_attr_nest_end(nlh, nest2);
+ }
+}
+
+static int
+nftnl_expr_immediate_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_immediate *imm = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_IMMEDIATE_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_immediate_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_IMMEDIATE_DREG]) {
+ imm->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_IMMEDIATE_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_IMM_DREG);
+ }
+ if (tb[NFTA_IMMEDIATE_DATA]) {
+ int type;
+
+ ret = nftnl_parse_data(&imm->data, tb[NFTA_IMMEDIATE_DATA], &type);
+ if (ret < 0)
+ return ret;
+
+ switch(type) {
+ case DATA_VALUE:
+ /* real immediate data to be loaded to destination */
+ e->flags |= (1 << NFTNL_EXPR_IMM_DATA);
+ break;
+ case DATA_VERDICT:
+ /* NF_ACCEPT, NF_DROP, NF_QUEUE and NFTNL_RETURN case */
+ e->flags |= (1 << NFTNL_EXPR_IMM_VERDICT);
+ break;
+ case DATA_CHAIN:
+ /* NFTNL_GOTO and NFTNL_JUMP case */
+ e->flags |= (1 << NFTNL_EXPR_IMM_VERDICT) |
+ (1 << NFTNL_EXPR_IMM_CHAIN);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int
+nftnl_expr_immediate_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_immediate *imm = nftnl_expr_data(e);
+ int offset = 0, ret;
+
+ ret = snprintf(buf, remain, "reg %u ", imm->dreg);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (e->flags & (1 << NFTNL_EXPR_IMM_DATA)) {
+ ret = nftnl_data_reg_snprintf(buf + offset, remain, &imm->data,
+ flags, DATA_VALUE);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ } else if (e->flags & (1 << NFTNL_EXPR_IMM_VERDICT)) {
+ ret = nftnl_data_reg_snprintf(buf + offset, remain, &imm->data,
+ flags, DATA_VERDICT);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ } else if (e->flags & (1 << NFTNL_EXPR_IMM_CHAIN)) {
+ ret = nftnl_data_reg_snprintf(buf + offset, remain, &imm->data,
+ flags, DATA_CHAIN);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+static void nftnl_expr_immediate_free(const struct nftnl_expr *e)
+{
+ struct nftnl_expr_immediate *imm = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_IMM_VERDICT))
+ nftnl_free_verdict(&imm->data);
+}
+
+struct expr_ops expr_ops_immediate = {
+ .name = "immediate",
+ .alloc_len = sizeof(struct nftnl_expr_immediate),
+ .max_attr = NFTA_IMMEDIATE_MAX,
+ .free = nftnl_expr_immediate_free,
+ .set = nftnl_expr_immediate_set,
+ .get = nftnl_expr_immediate_get,
+ .parse = nftnl_expr_immediate_parse,
+ .build = nftnl_expr_immediate_build,
+ .output = nftnl_expr_immediate_snprintf,
+};
diff --git a/src/expr/inner.c b/src/expr/inner.c
new file mode 100644
index 0000000..7daae4f
--- /dev/null
+++ b/src/expr/inner.c
@@ -0,0 +1,214 @@
+/*
+ * (C) 2012-2022 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <limits.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <libmnl/libmnl.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_inner {
+ uint32_t type;
+ uint32_t flags;
+ uint32_t hdrsize;
+ struct nftnl_expr *expr;
+};
+
+static void nftnl_expr_inner_free(const struct nftnl_expr *e)
+{
+ struct nftnl_expr_inner *inner = nftnl_expr_data(e);
+
+ if (inner->expr)
+ nftnl_expr_free(inner->expr);
+}
+
+static int
+nftnl_expr_inner_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_inner *inner = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_INNER_TYPE:
+ memcpy(&inner->type, data, sizeof(inner->type));
+ break;
+ case NFTNL_EXPR_INNER_FLAGS:
+ memcpy(&inner->flags, data, sizeof(inner->flags));
+ break;
+ case NFTNL_EXPR_INNER_HDRSIZE:
+ memcpy(&inner->hdrsize, data, sizeof(inner->hdrsize));
+ break;
+ case NFTNL_EXPR_INNER_EXPR:
+ if (inner->expr)
+ nftnl_expr_free(inner->expr);
+
+ inner->expr = (void *)data;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_inner_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_inner *inner = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_INNER_FLAGS:
+ *data_len = sizeof(inner->flags);
+ return &inner->flags;
+ case NFTNL_EXPR_INNER_TYPE:
+ *data_len = sizeof(inner->type);
+ return &inner->type;
+ case NFTNL_EXPR_INNER_HDRSIZE:
+ *data_len = sizeof(inner->hdrsize);
+ return &inner->hdrsize;
+ case NFTNL_EXPR_INNER_EXPR:
+ return inner->expr;
+ }
+ return NULL;
+}
+
+static void
+nftnl_expr_inner_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_inner *inner = nftnl_expr_data(e);
+ struct nlattr *nest;
+
+ mnl_attr_put_u32(nlh, NFTA_INNER_NUM, htonl(0));
+ if (e->flags & (1 << NFTNL_EXPR_INNER_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_INNER_TYPE, htonl(inner->type));
+ if (e->flags & (1 << NFTNL_EXPR_INNER_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_INNER_FLAGS, htonl(inner->flags));
+ if (e->flags & (1 << NFTNL_EXPR_INNER_HDRSIZE))
+ mnl_attr_put_u32(nlh, NFTA_INNER_HDRSIZE, htonl(inner->hdrsize));
+ if (e->flags & (1 << NFTNL_EXPR_INNER_EXPR)) {
+ nest = mnl_attr_nest_start(nlh, NFTA_INNER_EXPR);
+ nftnl_expr_build_payload(nlh, inner->expr);
+ mnl_attr_nest_end(nlh, nest);
+ }
+}
+
+static int nftnl_inner_parse_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_INNER_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_INNER_NUM:
+ case NFTA_INNER_TYPE:
+ case NFTA_INNER_HDRSIZE:
+ case NFTA_INNER_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_INNER_EXPR:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+
+ return MNL_CB_OK;
+}
+
+static int
+nftnl_expr_inner_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_inner *inner = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_INNER_MAX + 1] = {};
+ struct nftnl_expr *expr;
+ int err;
+
+ err = mnl_attr_parse_nested(attr, nftnl_inner_parse_cb, tb);
+ if (err < 0)
+ return err;
+
+ if (tb[NFTA_INNER_HDRSIZE]) {
+ inner->hdrsize =
+ ntohl(mnl_attr_get_u32(tb[NFTA_INNER_HDRSIZE]));
+ e->flags |= (1 << NFTNL_EXPR_INNER_HDRSIZE);
+ }
+ if (tb[NFTA_INNER_FLAGS]) {
+ inner->flags =
+ ntohl(mnl_attr_get_u32(tb[NFTA_INNER_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_INNER_FLAGS);
+ }
+ if (tb[NFTA_INNER_TYPE]) {
+ inner->type =
+ ntohl(mnl_attr_get_u32(tb[NFTA_INNER_TYPE]));
+ e->flags |= (1 << NFTNL_EXPR_INNER_TYPE);
+ }
+ if (tb[NFTA_INNER_EXPR]) {
+ expr = nftnl_expr_parse(tb[NFTA_INNER_EXPR]);
+ if (!expr)
+ return -1;
+
+ if (inner->expr)
+ nftnl_expr_free(inner->expr);
+
+ inner->expr = expr;
+ e->flags |= (1 << NFTNL_EXPR_INNER_EXPR);
+ }
+
+ return 0;
+}
+
+static int
+nftnl_expr_inner_snprintf(char *buf, size_t remain, uint32_t flags,
+ const struct nftnl_expr *e)
+{
+ struct nftnl_expr_inner *inner = nftnl_expr_data(e);
+ uint32_t offset = 0;
+ int ret;
+
+ ret = snprintf(buf, remain, "type %u hdrsize %u flags %x [",
+ inner->type, inner->hdrsize, inner->flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = snprintf(buf + offset, remain, " %s ", inner->expr->ops->name);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_expr_snprintf(buf + offset, remain, inner->expr,
+ NFTNL_OUTPUT_DEFAULT, 0);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = snprintf(buf + offset, remain, "] ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ return offset;
+}
+
+struct expr_ops expr_ops_inner = {
+ .name = "inner",
+ .alloc_len = sizeof(struct nftnl_expr_inner),
+ .max_attr = NFTA_INNER_MAX,
+ .free = nftnl_expr_inner_free,
+ .set = nftnl_expr_inner_set,
+ .get = nftnl_expr_inner_get,
+ .parse = nftnl_expr_inner_parse,
+ .build = nftnl_expr_inner_build,
+ .output = nftnl_expr_inner_snprintf,
+};
diff --git a/src/expr/last.c b/src/expr/last.c
new file mode 100644
index 0000000..641b713
--- /dev/null
+++ b/src/expr/last.c
@@ -0,0 +1,138 @@
+/*
+ * (C) 2016 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_last {
+ uint64_t msecs;
+ uint32_t set;
+};
+
+static int nftnl_expr_last_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_last *last = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_LAST_MSECS:
+ memcpy(&last->msecs, data, sizeof(last->msecs));
+ break;
+ case NFTNL_EXPR_LAST_SET:
+ memcpy(&last->set, data, sizeof(last->set));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *nftnl_expr_last_get(const struct nftnl_expr *e,
+ uint16_t type, uint32_t *data_len)
+{
+ struct nftnl_expr_last *last = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_LAST_MSECS:
+ *data_len = sizeof(last->msecs);
+ return &last->msecs;
+ case NFTNL_EXPR_LAST_SET:
+ *data_len = sizeof(last->set);
+ return &last->set;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_last_cb(const struct nlattr *attr, void *data)
+{
+ int type = mnl_attr_get_type(attr);
+ const struct nlattr **tb = data;
+
+ if (mnl_attr_type_valid(attr, NFTA_LAST_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_LAST_MSECS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ case NFTA_LAST_SET:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_last_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_last *last = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_LAST_MSECS))
+ mnl_attr_put_u64(nlh, NFTA_LAST_MSECS, htobe64(last->msecs));
+ if (e->flags & (1 << NFTNL_EXPR_LAST_SET))
+ mnl_attr_put_u32(nlh, NFTA_LAST_SET, htonl(last->set));
+}
+
+static int
+nftnl_expr_last_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_last *last = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_LAST_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_last_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_LAST_MSECS]) {
+ last->msecs = be64toh(mnl_attr_get_u64(tb[NFTA_LAST_MSECS]));
+ e->flags |= (1 << NFTNL_EXPR_LAST_MSECS);
+ }
+ if (tb[NFTA_LAST_SET]) {
+ last->set = ntohl(mnl_attr_get_u32(tb[NFTA_LAST_SET]));
+ e->flags |= (1 << NFTNL_EXPR_LAST_SET);
+ }
+
+ return 0;
+}
+
+static int nftnl_expr_last_snprintf(char *buf, size_t len,
+ uint32_t flags,
+ const struct nftnl_expr *e)
+{
+ struct nftnl_expr_last *last = nftnl_expr_data(e);
+
+ if (!last->set)
+ return snprintf(buf, len, "never ");
+
+ return snprintf(buf, len, "%"PRIu64" ", last->msecs);
+}
+
+struct expr_ops expr_ops_last = {
+ .name = "last",
+ .alloc_len = sizeof(struct nftnl_expr_last),
+ .max_attr = NFTA_LAST_MAX,
+ .set = nftnl_expr_last_set,
+ .get = nftnl_expr_last_get,
+ .parse = nftnl_expr_last_parse,
+ .build = nftnl_expr_last_build,
+ .output = nftnl_expr_last_snprintf,
+};
diff --git a/src/expr/limit.c b/src/expr/limit.c
new file mode 100644
index 0000000..1870e0e
--- /dev/null
+++ b/src/expr/limit.c
@@ -0,0 +1,206 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_limit {
+ uint64_t rate;
+ uint64_t unit;
+ uint32_t burst;
+ enum nft_limit_type type;
+ uint32_t flags;
+};
+
+static int
+nftnl_expr_limit_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_limit *limit = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_LIMIT_RATE:
+ memcpy(&limit->rate, data, sizeof(limit->rate));
+ break;
+ case NFTNL_EXPR_LIMIT_UNIT:
+ memcpy(&limit->unit, data, sizeof(limit->unit));
+ break;
+ case NFTNL_EXPR_LIMIT_BURST:
+ memcpy(&limit->burst, data, sizeof(limit->burst));
+ break;
+ case NFTNL_EXPR_LIMIT_TYPE:
+ memcpy(&limit->type, data, sizeof(limit->type));
+ break;
+ case NFTNL_EXPR_LIMIT_FLAGS:
+ memcpy(&limit->flags, data, sizeof(limit->flags));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_limit_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_limit *limit = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_LIMIT_RATE:
+ *data_len = sizeof(uint64_t);
+ return &limit->rate;
+ case NFTNL_EXPR_LIMIT_UNIT:
+ *data_len = sizeof(uint64_t);
+ return &limit->unit;
+ case NFTNL_EXPR_LIMIT_BURST:
+ *data_len = sizeof(uint32_t);
+ return &limit->burst;
+ case NFTNL_EXPR_LIMIT_TYPE:
+ *data_len = sizeof(uint32_t);
+ return &limit->type;
+ case NFTNL_EXPR_LIMIT_FLAGS:
+ *data_len = sizeof(uint32_t);
+ return &limit->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_limit_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_LIMIT_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_LIMIT_RATE:
+ case NFTA_LIMIT_UNIT:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ case NFTA_LIMIT_BURST:
+ case NFTA_LIMIT_TYPE:
+ case NFTA_LIMIT_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_limit_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_limit *limit = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_LIMIT_RATE))
+ mnl_attr_put_u64(nlh, NFTA_LIMIT_RATE, htobe64(limit->rate));
+ if (e->flags & (1 << NFTNL_EXPR_LIMIT_UNIT))
+ mnl_attr_put_u64(nlh, NFTA_LIMIT_UNIT, htobe64(limit->unit));
+ if (e->flags & (1 << NFTNL_EXPR_LIMIT_BURST))
+ mnl_attr_put_u32(nlh, NFTA_LIMIT_BURST, htonl(limit->burst));
+ if (e->flags & (1 << NFTNL_EXPR_LIMIT_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_LIMIT_TYPE, htonl(limit->type));
+ if (e->flags & (1 << NFTNL_EXPR_LIMIT_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_LIMIT_FLAGS, htonl(limit->flags));
+}
+
+static int
+nftnl_expr_limit_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_limit *limit = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_LIMIT_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_limit_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_LIMIT_RATE]) {
+ limit->rate = be64toh(mnl_attr_get_u64(tb[NFTA_LIMIT_RATE]));
+ e->flags |= (1 << NFTNL_EXPR_LIMIT_RATE);
+ }
+ if (tb[NFTA_LIMIT_UNIT]) {
+ limit->unit = be64toh(mnl_attr_get_u64(tb[NFTA_LIMIT_UNIT]));
+ e->flags |= (1 << NFTNL_EXPR_LIMIT_UNIT);
+ }
+ if (tb[NFTA_LIMIT_BURST]) {
+ limit->burst = ntohl(mnl_attr_get_u32(tb[NFTA_LIMIT_BURST]));
+ e->flags |= (1 << NFTNL_EXPR_LIMIT_BURST);
+ }
+ if (tb[NFTA_LIMIT_TYPE]) {
+ limit->type = ntohl(mnl_attr_get_u32(tb[NFTA_LIMIT_TYPE]));
+ e->flags |= (1 << NFTNL_EXPR_LIMIT_TYPE);
+ }
+ if (tb[NFTA_LIMIT_FLAGS]) {
+ limit->flags = ntohl(mnl_attr_get_u32(tb[NFTA_LIMIT_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_LIMIT_FLAGS);
+ }
+
+ return 0;
+}
+
+static const char *get_unit(uint64_t u)
+{
+ switch (u) {
+ case 1: return "second";
+ case 60: return "minute";
+ case 60 * 60: return "hour";
+ case 60 * 60 * 24: return "day";
+ case 60 * 60 * 24 * 7: return "week";
+ }
+ return "error";
+}
+
+static const char *limit_to_type(enum nft_limit_type type)
+{
+ switch (type) {
+ default:
+ case NFT_LIMIT_PKTS:
+ return "packets";
+ case NFT_LIMIT_PKT_BYTES:
+ return "bytes";
+ }
+}
+
+static int
+nftnl_expr_limit_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_limit *limit = nftnl_expr_data(e);
+
+ return snprintf(buf, len, "rate %"PRIu64"/%s burst %u type %s flags 0x%x ",
+ limit->rate, get_unit(limit->unit), limit->burst,
+ limit_to_type(limit->type), limit->flags);
+}
+
+struct expr_ops expr_ops_limit = {
+ .name = "limit",
+ .alloc_len = sizeof(struct nftnl_expr_limit),
+ .max_attr = NFTA_LIMIT_MAX,
+ .set = nftnl_expr_limit_set,
+ .get = nftnl_expr_limit_get,
+ .parse = nftnl_expr_limit_parse,
+ .build = nftnl_expr_limit_build,
+ .output = nftnl_expr_limit_snprintf,
+};
diff --git a/src/expr/log.c b/src/expr/log.c
new file mode 100644
index 0000000..180d839
--- /dev/null
+++ b/src/expr/log.c
@@ -0,0 +1,257 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/nf_log.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_log {
+ uint32_t snaplen;
+ uint16_t group;
+ uint16_t qthreshold;
+ uint32_t level;
+ uint32_t flags;
+ const char *prefix;
+};
+
+static int nftnl_expr_log_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_log *log = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_LOG_PREFIX:
+ if (log->flags & (1 << NFTNL_EXPR_LOG_PREFIX))
+ xfree(log->prefix);
+
+ log->prefix = strdup(data);
+ if (!log->prefix)
+ return -1;
+ break;
+ case NFTNL_EXPR_LOG_GROUP:
+ memcpy(&log->group, data, sizeof(log->group));
+ break;
+ case NFTNL_EXPR_LOG_SNAPLEN:
+ memcpy(&log->snaplen, data, sizeof(log->snaplen));
+ break;
+ case NFTNL_EXPR_LOG_QTHRESHOLD:
+ memcpy(&log->qthreshold, data, sizeof(log->qthreshold));
+ break;
+ case NFTNL_EXPR_LOG_LEVEL:
+ memcpy(&log->level, data, sizeof(log->level));
+ break;
+ case NFTNL_EXPR_LOG_FLAGS:
+ memcpy(&log->flags, data, sizeof(log->flags));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_log_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_log *log = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_LOG_PREFIX:
+ *data_len = strlen(log->prefix)+1;
+ return log->prefix;
+ case NFTNL_EXPR_LOG_GROUP:
+ *data_len = sizeof(log->group);
+ return &log->group;
+ case NFTNL_EXPR_LOG_SNAPLEN:
+ *data_len = sizeof(log->snaplen);
+ return &log->snaplen;
+ case NFTNL_EXPR_LOG_QTHRESHOLD:
+ *data_len = sizeof(log->qthreshold);
+ return &log->qthreshold;
+ case NFTNL_EXPR_LOG_LEVEL:
+ *data_len = sizeof(log->level);
+ return &log->level;
+ case NFTNL_EXPR_LOG_FLAGS:
+ *data_len = sizeof(log->flags);
+ return &log->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_log_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_LOG_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_LOG_PREFIX:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ case NFTA_LOG_GROUP:
+ case NFTA_LOG_QTHRESHOLD:
+ if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
+ abi_breakage();
+ break;
+ case NFTA_LOG_SNAPLEN:
+ case NFTA_LOG_LEVEL:
+ case NFTA_LOG_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_log_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_log *log = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_LOG_PREFIX))
+ mnl_attr_put_strz(nlh, NFTA_LOG_PREFIX, log->prefix);
+ if (e->flags & (1 << NFTNL_EXPR_LOG_GROUP))
+ mnl_attr_put_u16(nlh, NFTA_LOG_GROUP, htons(log->group));
+ if (e->flags & (1 << NFTNL_EXPR_LOG_SNAPLEN))
+ mnl_attr_put_u32(nlh, NFTA_LOG_SNAPLEN, htonl(log->snaplen));
+ if (e->flags & (1 << NFTNL_EXPR_LOG_QTHRESHOLD))
+ mnl_attr_put_u16(nlh, NFTA_LOG_QTHRESHOLD, htons(log->qthreshold));
+ if (e->flags & (1 << NFTNL_EXPR_LOG_LEVEL))
+ mnl_attr_put_u32(nlh, NFTA_LOG_LEVEL, htonl(log->level));
+ if (e->flags & (1 << NFTNL_EXPR_LOG_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_LOG_FLAGS, htonl(log->flags));
+}
+
+static int
+nftnl_expr_log_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_log *log = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_LOG_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_log_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_LOG_PREFIX]) {
+ if (log->prefix)
+ xfree(log->prefix);
+
+ log->prefix = strdup(mnl_attr_get_str(tb[NFTA_LOG_PREFIX]));
+ if (!log->prefix)
+ return -1;
+ e->flags |= (1 << NFTNL_EXPR_LOG_PREFIX);
+ }
+ if (tb[NFTA_LOG_GROUP]) {
+ log->group = ntohs(mnl_attr_get_u16(tb[NFTA_LOG_GROUP]));
+ e->flags |= (1 << NFTNL_EXPR_LOG_GROUP);
+ }
+ if (tb[NFTA_LOG_SNAPLEN]) {
+ log->snaplen = ntohl(mnl_attr_get_u32(tb[NFTA_LOG_SNAPLEN]));
+ e->flags |= (1 << NFTNL_EXPR_LOG_SNAPLEN);
+ }
+ if (tb[NFTA_LOG_QTHRESHOLD]) {
+ log->qthreshold = ntohs(mnl_attr_get_u16(tb[NFTA_LOG_QTHRESHOLD]));
+ e->flags |= (1 << NFTNL_EXPR_LOG_QTHRESHOLD);
+ }
+ if (tb[NFTA_LOG_LEVEL]) {
+ log->level = ntohl(mnl_attr_get_u32(tb[NFTA_LOG_LEVEL]));
+ e->flags |= (1 << NFTNL_EXPR_LOG_LEVEL);
+ }
+ if (tb[NFTA_LOG_FLAGS]) {
+ log->flags = ntohl(mnl_attr_get_u32(tb[NFTA_LOG_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_LOG_FLAGS);
+ }
+
+ return 0;
+}
+
+static int
+nftnl_expr_log_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_log *log = nftnl_expr_data(e);
+ int ret, offset = 0;
+
+ if (e->flags & (1 << NFTNL_EXPR_LOG_PREFIX)) {
+ ret = snprintf(buf, remain, "prefix %s ", log->prefix);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_LOG_GROUP)) {
+ ret = snprintf(buf + offset, remain,
+ "group %u snaplen %u qthreshold %u ",
+ log->group, log->snaplen, log->qthreshold);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ } else {
+ if (e->flags & (1 << NFTNL_EXPR_LOG_LEVEL)) {
+ ret = snprintf(buf + offset, remain, "level %u ",
+ log->level);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (e->flags & (1 << NFTNL_EXPR_LOG_FLAGS)) {
+ if (log->flags & NF_LOG_TCPSEQ) {
+ ret = snprintf(buf + offset, remain, "tcpseq ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (log->flags & NF_LOG_TCPOPT) {
+ ret = snprintf(buf + offset, remain, "tcpopt ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (log->flags & NF_LOG_IPOPT) {
+ ret = snprintf(buf + offset, remain, "ipopt ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (log->flags & NF_LOG_UID) {
+ ret = snprintf(buf + offset, remain, "uid ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (log->flags & NF_LOG_MACDECODE) {
+ ret = snprintf(buf + offset, remain,
+ "macdecode ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ }
+ }
+
+ return offset;
+}
+
+static void nftnl_expr_log_free(const struct nftnl_expr *e)
+{
+ struct nftnl_expr_log *log = nftnl_expr_data(e);
+
+ xfree(log->prefix);
+}
+
+struct expr_ops expr_ops_log = {
+ .name = "log",
+ .alloc_len = sizeof(struct nftnl_expr_log),
+ .max_attr = NFTA_LOG_MAX,
+ .free = nftnl_expr_log_free,
+ .set = nftnl_expr_log_set,
+ .get = nftnl_expr_log_get,
+ .parse = nftnl_expr_log_parse,
+ .build = nftnl_expr_log_build,
+ .output = nftnl_expr_log_snprintf,
+};
diff --git a/src/expr/lookup.c b/src/expr/lookup.c
new file mode 100644
index 0000000..a06c338
--- /dev/null
+++ b/src/expr/lookup.c
@@ -0,0 +1,210 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h> /* for memcpy */
+#include <arpa/inet.h>
+#include <errno.h>
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+
+struct nftnl_expr_lookup {
+ enum nft_registers sreg;
+ enum nft_registers dreg;
+ char *set_name;
+ uint32_t set_id;
+ uint32_t flags;
+};
+
+static int
+nftnl_expr_lookup_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_lookup *lookup = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_LOOKUP_SREG:
+ memcpy(&lookup->sreg, data, sizeof(lookup->sreg));
+ break;
+ case NFTNL_EXPR_LOOKUP_DREG:
+ memcpy(&lookup->dreg, data, sizeof(lookup->dreg));
+ break;
+ case NFTNL_EXPR_LOOKUP_SET:
+ lookup->set_name = strdup((const char *)data);
+ if (!lookup->set_name)
+ return -1;
+ break;
+ case NFTNL_EXPR_LOOKUP_SET_ID:
+ memcpy(&lookup->set_id, data, sizeof(lookup->set_id));
+ break;
+ case NFTNL_EXPR_LOOKUP_FLAGS:
+ memcpy(&lookup->flags, data, sizeof(lookup->flags));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_lookup_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_lookup *lookup = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_LOOKUP_SREG:
+ *data_len = sizeof(lookup->sreg);
+ return &lookup->sreg;
+ case NFTNL_EXPR_LOOKUP_DREG:
+ *data_len = sizeof(lookup->dreg);
+ return &lookup->dreg;
+ case NFTNL_EXPR_LOOKUP_SET:
+ *data_len = strlen(lookup->set_name) + 1;
+ return lookup->set_name;
+ case NFTNL_EXPR_LOOKUP_SET_ID:
+ *data_len = sizeof(lookup->set_id);
+ return &lookup->set_id;
+ case NFTNL_EXPR_LOOKUP_FLAGS:
+ *data_len = sizeof(lookup->flags);
+ return &lookup->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_lookup_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_LOOKUP_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_LOOKUP_SREG:
+ case NFTA_LOOKUP_DREG:
+ case NFTA_LOOKUP_SET_ID:
+ case NFTA_LOOKUP_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_LOOKUP_SET:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_lookup_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_lookup *lookup = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_LOOKUP_SREG))
+ mnl_attr_put_u32(nlh, NFTA_LOOKUP_SREG, htonl(lookup->sreg));
+ if (e->flags & (1 << NFTNL_EXPR_LOOKUP_DREG))
+ mnl_attr_put_u32(nlh, NFTA_LOOKUP_DREG, htonl(lookup->dreg));
+ if (e->flags & (1 << NFTNL_EXPR_LOOKUP_SET))
+ mnl_attr_put_strz(nlh, NFTA_LOOKUP_SET, lookup->set_name);
+ if (e->flags & (1 << NFTNL_EXPR_LOOKUP_SET_ID))
+ mnl_attr_put_u32(nlh, NFTA_LOOKUP_SET_ID,
+ htonl(lookup->set_id));
+ if (e->flags & (1 << NFTNL_EXPR_LOOKUP_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_LOOKUP_FLAGS, htonl(lookup->flags));
+}
+
+static int
+nftnl_expr_lookup_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_lookup *lookup = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_LOOKUP_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_lookup_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_LOOKUP_SREG]) {
+ lookup->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_LOOKUP_SREG]));
+ e->flags |= (1 << NFTNL_EXPR_LOOKUP_SREG);
+ }
+ if (tb[NFTA_LOOKUP_DREG]) {
+ lookup->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_LOOKUP_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_LOOKUP_DREG);
+ }
+ if (tb[NFTA_LOOKUP_SET]) {
+ lookup->set_name =
+ strdup(mnl_attr_get_str(tb[NFTA_LOOKUP_SET]));
+ if (!lookup->set_name)
+ return -1;
+ e->flags |= (1 << NFTNL_EXPR_LOOKUP_SET);
+ }
+ if (tb[NFTA_LOOKUP_SET_ID]) {
+ lookup->set_id =
+ ntohl(mnl_attr_get_u32(tb[NFTA_LOOKUP_SET_ID]));
+ e->flags |= (1 << NFTNL_EXPR_LOOKUP_SET_ID);
+ }
+ if (tb[NFTA_LOOKUP_FLAGS]) {
+ lookup->flags = ntohl(mnl_attr_get_u32(tb[NFTA_LOOKUP_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_LOOKUP_FLAGS);
+ }
+
+ return ret;
+}
+
+static int
+nftnl_expr_lookup_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_lookup *l = nftnl_expr_data(e);
+ int offset = 0, ret;
+
+ ret = snprintf(buf, remain, "reg %u set %s ", l->sreg, l->set_name);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (e->flags & (1 << NFTNL_EXPR_LOOKUP_DREG)) {
+ ret = snprintf(buf + offset, remain, "dreg %u ", l->dreg);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_LOOKUP_FLAGS)) {
+ ret = snprintf(buf + offset, remain, "0x%x ", l->flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+static void nftnl_expr_lookup_free(const struct nftnl_expr *e)
+{
+ struct nftnl_expr_lookup *lookup = nftnl_expr_data(e);
+
+ xfree(lookup->set_name);
+}
+
+struct expr_ops expr_ops_lookup = {
+ .name = "lookup",
+ .alloc_len = sizeof(struct nftnl_expr_lookup),
+ .max_attr = NFTA_LOOKUP_MAX,
+ .free = nftnl_expr_lookup_free,
+ .set = nftnl_expr_lookup_set,
+ .get = nftnl_expr_lookup_get,
+ .parse = nftnl_expr_lookup_parse,
+ .build = nftnl_expr_lookup_build,
+ .output = nftnl_expr_lookup_snprintf,
+};
diff --git a/src/expr/masq.c b/src/expr/masq.c
new file mode 100644
index 0000000..e6e528d
--- /dev/null
+++ b/src/expr/masq.c
@@ -0,0 +1,167 @@
+/*
+ * (C) 2014 by Arturo Borrero Gonzalez <arturo@debian.org>
+ *
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_masq {
+ uint32_t flags;
+ enum nft_registers sreg_proto_min;
+ enum nft_registers sreg_proto_max;
+};
+
+static int
+nftnl_expr_masq_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_masq *masq = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_MASQ_FLAGS:
+ memcpy(&masq->flags, data, sizeof(masq->flags));
+ break;
+ case NFTNL_EXPR_MASQ_REG_PROTO_MIN:
+ memcpy(&masq->sreg_proto_min, data, sizeof(masq->sreg_proto_min));
+ break;
+ case NFTNL_EXPR_MASQ_REG_PROTO_MAX:
+ memcpy(&masq->sreg_proto_max, data, sizeof(masq->sreg_proto_max));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_masq_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_masq *masq = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_MASQ_FLAGS:
+ *data_len = sizeof(masq->flags);
+ return &masq->flags;
+ case NFTNL_EXPR_MASQ_REG_PROTO_MIN:
+ *data_len = sizeof(masq->sreg_proto_min);
+ return &masq->sreg_proto_min;
+ case NFTNL_EXPR_MASQ_REG_PROTO_MAX:
+ *data_len = sizeof(masq->sreg_proto_max);
+ return &masq->sreg_proto_max;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_masq_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_MASQ_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_MASQ_REG_PROTO_MIN:
+ case NFTA_MASQ_REG_PROTO_MAX:
+ case NFTA_MASQ_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_masq_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_masq *masq = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_MASQ_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_MASQ_FLAGS, htobe32(masq->flags));
+ if (e->flags & (1 << NFTNL_EXPR_MASQ_REG_PROTO_MIN))
+ mnl_attr_put_u32(nlh, NFTA_MASQ_REG_PROTO_MIN,
+ htobe32(masq->sreg_proto_min));
+ if (e->flags & (1 << NFTNL_EXPR_MASQ_REG_PROTO_MAX))
+ mnl_attr_put_u32(nlh, NFTA_MASQ_REG_PROTO_MAX,
+ htobe32(masq->sreg_proto_max));
+}
+
+static int
+nftnl_expr_masq_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_masq *masq = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_MASQ_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_masq_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_MASQ_FLAGS]) {
+ masq->flags = be32toh(mnl_attr_get_u32(tb[NFTA_MASQ_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_MASQ_FLAGS);
+ }
+ if (tb[NFTA_MASQ_REG_PROTO_MIN]) {
+ masq->sreg_proto_min =
+ be32toh(mnl_attr_get_u32(tb[NFTA_MASQ_REG_PROTO_MIN]));
+ e->flags |= (1 << NFTNL_EXPR_MASQ_REG_PROTO_MIN);
+ }
+ if (tb[NFTA_MASQ_REG_PROTO_MAX]) {
+ masq->sreg_proto_max =
+ be32toh(mnl_attr_get_u32(tb[NFTA_MASQ_REG_PROTO_MAX]));
+ e->flags |= (1 << NFTNL_EXPR_MASQ_REG_PROTO_MAX);
+ }
+
+ return 0;
+}
+
+static int nftnl_expr_masq_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_masq *masq = nftnl_expr_data(e);
+ int offset = 0, ret = 0;
+
+ if (e->flags & (1 << NFTNL_EXPR_MASQ_REG_PROTO_MIN)) {
+ ret = snprintf(buf + offset, remain, "proto_min reg %u ",
+ masq->sreg_proto_min);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (e->flags & (1 << NFTNL_EXPR_MASQ_REG_PROTO_MAX)) {
+ ret = snprintf(buf + offset, remain, "proto_max reg %u ",
+ masq->sreg_proto_max);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (e->flags & (1 << NFTNL_EXPR_MASQ_FLAGS)) {
+ ret = snprintf(buf + offset, remain, "flags 0x%x ", masq->flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+struct expr_ops expr_ops_masq = {
+ .name = "masq",
+ .alloc_len = sizeof(struct nftnl_expr_masq),
+ .max_attr = NFTA_MASQ_MAX,
+ .set = nftnl_expr_masq_set,
+ .get = nftnl_expr_masq_get,
+ .parse = nftnl_expr_masq_parse,
+ .build = nftnl_expr_masq_build,
+ .output = nftnl_expr_masq_snprintf,
+};
diff --git a/src/expr/match.c b/src/expr/match.c
new file mode 100644
index 0000000..f472add
--- /dev/null
+++ b/src/expr/match.c
@@ -0,0 +1,193 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h> /* for memcpy */
+#include <arpa/inet.h>
+#include <errno.h>
+#include <libmnl/libmnl.h>
+
+#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/nf_tables_compat.h>
+
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+/* From include/linux/netfilter/x_tables.h */
+#define XT_EXTENSION_MAXNAMELEN 29
+
+struct nftnl_expr_match {
+ char name[XT_EXTENSION_MAXNAMELEN];
+ uint32_t rev;
+ uint32_t data_len;
+ const void *data;
+};
+
+static int
+nftnl_expr_match_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_match *mt = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_MT_NAME:
+ snprintf(mt->name, sizeof(mt->name), "%.*s", data_len,
+ (const char *)data);
+ break;
+ case NFTNL_EXPR_MT_REV:
+ memcpy(&mt->rev, data, sizeof(mt->rev));
+ break;
+ case NFTNL_EXPR_MT_INFO:
+ if (e->flags & (1 << NFTNL_EXPR_MT_INFO))
+ xfree(mt->data);
+
+ mt->data = data;
+ mt->data_len = data_len;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_match_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_match *mt = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_MT_NAME:
+ *data_len = sizeof(mt->name);
+ return mt->name;
+ case NFTNL_EXPR_MT_REV:
+ *data_len = sizeof(mt->rev);
+ return &mt->rev;
+ case NFTNL_EXPR_MT_INFO:
+ *data_len = mt->data_len;
+ return mt->data;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_match_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_MATCH_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_MATCH_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0)
+ abi_breakage();
+ break;
+ case NFTA_MATCH_REV:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_MATCH_INFO:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_match_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_match *mt = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_MT_NAME))
+ mnl_attr_put_strz(nlh, NFTA_MATCH_NAME, mt->name);
+ if (e->flags & (1 << NFTNL_EXPR_MT_REV))
+ mnl_attr_put_u32(nlh, NFTA_MATCH_REV, htonl(mt->rev));
+ if (e->flags & (1 << NFTNL_EXPR_MT_INFO))
+ mnl_attr_put(nlh, NFTA_MATCH_INFO, mt->data_len, mt->data);
+}
+
+static int nftnl_expr_match_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_match *match = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_MATCH_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_match_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_MATCH_NAME]) {
+ snprintf(match->name, XT_EXTENSION_MAXNAMELEN, "%s",
+ mnl_attr_get_str(tb[NFTA_MATCH_NAME]));
+
+ match->name[XT_EXTENSION_MAXNAMELEN-1] = '\0';
+ e->flags |= (1 << NFTNL_EXPR_MT_NAME);
+ }
+
+ if (tb[NFTA_MATCH_REV]) {
+ match->rev = ntohl(mnl_attr_get_u32(tb[NFTA_MATCH_REV]));
+ e->flags |= (1 << NFTNL_EXPR_MT_REV);
+ }
+
+ if (tb[NFTA_MATCH_INFO]) {
+ uint32_t len = mnl_attr_get_payload_len(tb[NFTA_MATCH_INFO]);
+ void *match_data;
+
+ if (e->flags & (1 << NFTNL_EXPR_MT_INFO))
+ xfree(match->data);
+
+ match_data = calloc(1, len);
+ if (match_data == NULL)
+ return -1;
+
+ memcpy(match_data, mnl_attr_get_payload(tb[NFTA_MATCH_INFO]), len);
+
+ match->data = match_data;
+ match->data_len = len;
+
+ e->flags |= (1 << NFTNL_EXPR_MT_INFO);
+ }
+
+ return 0;
+}
+
+static int
+nftnl_expr_match_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_match *match = nftnl_expr_data(e);
+
+ return snprintf(buf, len, "name %s rev %u ", match->name, match->rev);
+}
+
+static void nftnl_expr_match_free(const struct nftnl_expr *e)
+{
+ struct nftnl_expr_match *match = nftnl_expr_data(e);
+
+ xfree(match->data);
+}
+
+struct expr_ops expr_ops_match = {
+ .name = "match",
+ .alloc_len = sizeof(struct nftnl_expr_match),
+ .max_attr = NFTA_MATCH_MAX,
+ .free = nftnl_expr_match_free,
+ .set = nftnl_expr_match_set,
+ .get = nftnl_expr_match_get,
+ .parse = nftnl_expr_match_parse,
+ .build = nftnl_expr_match_build,
+ .output = nftnl_expr_match_snprintf,
+};
diff --git a/src/expr/meta.c b/src/expr/meta.c
new file mode 100644
index 0000000..183f441
--- /dev/null
+++ b/src/expr/meta.c
@@ -0,0 +1,221 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+#ifndef NFT_META_MAX
+#define NFT_META_MAX (NFT_META_BRI_BROUTE + 1)
+#endif
+
+struct nftnl_expr_meta {
+ enum nft_meta_keys key;
+ enum nft_registers dreg;
+ enum nft_registers sreg;
+};
+
+static int
+nftnl_expr_meta_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_meta *meta = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_META_KEY:
+ memcpy(&meta->key, data, sizeof(meta->key));
+ break;
+ case NFTNL_EXPR_META_DREG:
+ memcpy(&meta->dreg, data, sizeof(meta->dreg));
+ break;
+ case NFTNL_EXPR_META_SREG:
+ memcpy(&meta->sreg, data, sizeof(meta->sreg));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_meta_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_meta *meta = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_META_KEY:
+ *data_len = sizeof(meta->key);
+ return &meta->key;
+ case NFTNL_EXPR_META_DREG:
+ *data_len = sizeof(meta->dreg);
+ return &meta->dreg;
+ case NFTNL_EXPR_META_SREG:
+ *data_len = sizeof(meta->sreg);
+ return &meta->sreg;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_meta_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_META_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_META_KEY:
+ case NFTA_META_DREG:
+ case NFTA_META_SREG:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_meta_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_meta *meta = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_META_KEY))
+ mnl_attr_put_u32(nlh, NFTA_META_KEY, htonl(meta->key));
+ if (e->flags & (1 << NFTNL_EXPR_META_DREG))
+ mnl_attr_put_u32(nlh, NFTA_META_DREG, htonl(meta->dreg));
+ if (e->flags & (1 << NFTNL_EXPR_META_SREG))
+ mnl_attr_put_u32(nlh, NFTA_META_SREG, htonl(meta->sreg));
+}
+
+static int
+nftnl_expr_meta_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_meta *meta = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_META_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_meta_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_META_KEY]) {
+ meta->key = ntohl(mnl_attr_get_u32(tb[NFTA_META_KEY]));
+ e->flags |= (1 << NFTNL_EXPR_META_KEY);
+ }
+ if (tb[NFTA_META_DREG]) {
+ meta->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_META_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_META_DREG);
+ }
+ if (tb[NFTA_META_SREG]) {
+ meta->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_META_SREG]));
+ e->flags |= (1 << NFTNL_EXPR_META_SREG);
+ }
+
+ return 0;
+}
+
+static const char *meta_key2str_array[NFT_META_MAX] = {
+ [NFT_META_LEN] = "len",
+ [NFT_META_PROTOCOL] = "protocol",
+ [NFT_META_NFPROTO] = "nfproto",
+ [NFT_META_L4PROTO] = "l4proto",
+ [NFT_META_PRIORITY] = "priority",
+ [NFT_META_MARK] = "mark",
+ [NFT_META_IIF] = "iif",
+ [NFT_META_OIF] = "oif",
+ [NFT_META_IIFNAME] = "iifname",
+ [NFT_META_OIFNAME] = "oifname",
+ [NFT_META_IIFTYPE] = "iiftype",
+ [NFT_META_OIFTYPE] = "oiftype",
+ [NFT_META_SKUID] = "skuid",
+ [NFT_META_SKGID] = "skgid",
+ [NFT_META_NFTRACE] = "nftrace",
+ [NFT_META_RTCLASSID] = "rtclassid",
+ [NFT_META_SECMARK] = "secmark",
+ [NFT_META_BRI_IIFNAME] = "bri_iifname",
+ [NFT_META_BRI_OIFNAME] = "bri_oifname",
+ [NFT_META_PKTTYPE] = "pkttype",
+ [NFT_META_CPU] = "cpu",
+ [NFT_META_IIFGROUP] = "iifgroup",
+ [NFT_META_OIFGROUP] = "oifgroup",
+ [NFT_META_CGROUP] = "cgroup",
+ [NFT_META_PRANDOM] = "prandom",
+ [NFT_META_SECPATH] = "secpath",
+ [NFT_META_IIFKIND] = "iifkind",
+ [NFT_META_OIFKIND] = "oifkind",
+ [NFT_META_BRI_IIFPVID] = "bri_iifpvid",
+ [NFT_META_BRI_IIFVPROTO] = "bri_iifvproto",
+ [NFT_META_TIME_NS] = "time",
+ [NFT_META_TIME_DAY] = "day",
+ [NFT_META_TIME_HOUR] = "hour",
+ [NFT_META_SDIF] = "sdif",
+ [NFT_META_SDIFNAME] = "sdifname",
+ [NFT_META_BRI_BROUTE] = "broute",
+};
+
+static const char *meta_key2str(uint8_t key)
+{
+ if (key < NFT_META_MAX)
+ return meta_key2str_array[key];
+
+ return "unknown";
+}
+
+static inline int str2meta_key(const char *str)
+{
+ int i;
+
+ for (i = 0; i < NFT_META_MAX; i++) {
+ if (strcmp(str, meta_key2str_array[i]) == 0)
+ return i;
+ }
+
+ errno = EINVAL;
+ return -1;
+}
+
+static int
+nftnl_expr_meta_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_meta *meta = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_META_SREG)) {
+ return snprintf(buf, len, "set %s with reg %u ",
+ meta_key2str(meta->key), meta->sreg);
+ }
+ if (e->flags & (1 << NFTNL_EXPR_META_DREG)) {
+ return snprintf(buf, len, "load %s => reg %u ",
+ meta_key2str(meta->key), meta->dreg);
+ }
+ return 0;
+}
+
+struct expr_ops expr_ops_meta = {
+ .name = "meta",
+ .alloc_len = sizeof(struct nftnl_expr_meta),
+ .max_attr = NFTA_META_MAX,
+ .set = nftnl_expr_meta_set,
+ .get = nftnl_expr_meta_get,
+ .parse = nftnl_expr_meta_parse,
+ .build = nftnl_expr_meta_build,
+ .output = nftnl_expr_meta_snprintf,
+};
diff --git a/src/expr/nat.c b/src/expr/nat.c
new file mode 100644
index 0000000..ca727be
--- /dev/null
+++ b/src/expr/nat.c
@@ -0,0 +1,278 @@
+/*
+ * (C) 2012-2014 Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2012 Intel Corporation
+ *
+ * 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.
+ *
+ * Authors:
+ * Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_nat {
+ enum nft_registers sreg_addr_min;
+ enum nft_registers sreg_addr_max;
+ enum nft_registers sreg_proto_min;
+ enum nft_registers sreg_proto_max;
+ int family;
+ enum nft_nat_types type;
+ uint32_t flags;
+};
+
+static int
+nftnl_expr_nat_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_nat *nat = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_NAT_TYPE:
+ memcpy(&nat->type, data, sizeof(nat->type));
+ break;
+ case NFTNL_EXPR_NAT_FAMILY:
+ memcpy(&nat->family, data, sizeof(nat->family));
+ break;
+ case NFTNL_EXPR_NAT_REG_ADDR_MIN:
+ memcpy(&nat->sreg_addr_min, data, sizeof(nat->sreg_addr_min));
+ break;
+ case NFTNL_EXPR_NAT_REG_ADDR_MAX:
+ memcpy(&nat->sreg_addr_max, data, sizeof(nat->sreg_addr_max));
+ break;
+ case NFTNL_EXPR_NAT_REG_PROTO_MIN:
+ memcpy(&nat->sreg_proto_min, data, sizeof(nat->sreg_proto_min));
+ break;
+ case NFTNL_EXPR_NAT_REG_PROTO_MAX:
+ memcpy(&nat->sreg_proto_max, data, sizeof(nat->sreg_proto_max));
+ break;
+ case NFTNL_EXPR_NAT_FLAGS:
+ memcpy(&nat->flags, data, sizeof(nat->flags));
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static const void *
+nftnl_expr_nat_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_nat *nat = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_NAT_TYPE:
+ *data_len = sizeof(nat->type);
+ return &nat->type;
+ case NFTNL_EXPR_NAT_FAMILY:
+ *data_len = sizeof(nat->family);
+ return &nat->family;
+ case NFTNL_EXPR_NAT_REG_ADDR_MIN:
+ *data_len = sizeof(nat->sreg_addr_min);
+ return &nat->sreg_addr_min;
+ case NFTNL_EXPR_NAT_REG_ADDR_MAX:
+ *data_len = sizeof(nat->sreg_addr_max);
+ return &nat->sreg_addr_max;
+ case NFTNL_EXPR_NAT_REG_PROTO_MIN:
+ *data_len = sizeof(nat->sreg_proto_min);
+ return &nat->sreg_proto_min;
+ case NFTNL_EXPR_NAT_REG_PROTO_MAX:
+ *data_len = sizeof(nat->sreg_proto_max);
+ return &nat->sreg_proto_max;
+ case NFTNL_EXPR_NAT_FLAGS:
+ *data_len = sizeof(nat->flags);
+ return &nat->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_nat_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_NAT_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_NAT_TYPE:
+ case NFTA_NAT_FAMILY:
+ case NFTA_NAT_REG_ADDR_MIN:
+ case NFTA_NAT_REG_ADDR_MAX:
+ case NFTA_NAT_REG_PROTO_MIN:
+ case NFTA_NAT_REG_PROTO_MAX:
+ case NFTA_NAT_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int
+nftnl_expr_nat_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_nat *nat = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_NAT_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_nat_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_NAT_TYPE]) {
+ nat->type = ntohl(mnl_attr_get_u32(tb[NFTA_NAT_TYPE]));
+ e->flags |= (1 << NFTNL_EXPR_NAT_TYPE);
+ }
+ if (tb[NFTA_NAT_FAMILY]) {
+ nat->family = ntohl(mnl_attr_get_u32(tb[NFTA_NAT_FAMILY]));
+ e->flags |= (1 << NFTNL_EXPR_NAT_FAMILY);
+ }
+ if (tb[NFTA_NAT_REG_ADDR_MIN]) {
+ nat->sreg_addr_min =
+ ntohl(mnl_attr_get_u32(tb[NFTA_NAT_REG_ADDR_MIN]));
+ e->flags |= (1 << NFTNL_EXPR_NAT_REG_ADDR_MIN);
+ }
+ if (tb[NFTA_NAT_REG_ADDR_MAX]) {
+ nat->sreg_addr_max =
+ ntohl(mnl_attr_get_u32(tb[NFTA_NAT_REG_ADDR_MAX]));
+ e->flags |= (1 << NFTNL_EXPR_NAT_REG_ADDR_MAX);
+ }
+ if (tb[NFTA_NAT_REG_PROTO_MIN]) {
+ nat->sreg_proto_min =
+ ntohl(mnl_attr_get_u32(tb[NFTA_NAT_REG_PROTO_MIN]));
+ e->flags |= (1 << NFTNL_EXPR_NAT_REG_PROTO_MIN);
+ }
+ if (tb[NFTA_NAT_REG_PROTO_MAX]) {
+ nat->sreg_proto_max =
+ ntohl(mnl_attr_get_u32(tb[NFTA_NAT_REG_PROTO_MAX]));
+ e->flags |= (1 << NFTNL_EXPR_NAT_REG_PROTO_MAX);
+ }
+ if (tb[NFTA_NAT_FLAGS]) {
+ nat->flags = ntohl(mnl_attr_get_u32(tb[NFTA_NAT_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_NAT_FLAGS);
+ }
+
+ return 0;
+}
+
+static void
+nftnl_expr_nat_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_nat *nat = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_NAT_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_NAT_TYPE, htonl(nat->type));
+ if (e->flags & (1 << NFTNL_EXPR_NAT_FAMILY))
+ mnl_attr_put_u32(nlh, NFTA_NAT_FAMILY, htonl(nat->family));
+ if (e->flags & (1 << NFTNL_EXPR_NAT_REG_ADDR_MIN))
+ mnl_attr_put_u32(nlh, NFTA_NAT_REG_ADDR_MIN,
+ htonl(nat->sreg_addr_min));
+ if (e->flags & (1 << NFTNL_EXPR_NAT_REG_ADDR_MAX))
+ mnl_attr_put_u32(nlh, NFTA_NAT_REG_ADDR_MAX,
+ htonl(nat->sreg_addr_max));
+ if (e->flags & (1 << NFTNL_EXPR_NAT_REG_PROTO_MIN))
+ mnl_attr_put_u32(nlh, NFTA_NAT_REG_PROTO_MIN,
+ htonl(nat->sreg_proto_min));
+ if (e->flags & (1 << NFTNL_EXPR_NAT_REG_PROTO_MAX))
+ mnl_attr_put_u32(nlh, NFTA_NAT_REG_PROTO_MAX,
+ htonl(nat->sreg_proto_max));
+ if (e->flags & (1 << NFTNL_EXPR_NAT_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_NAT_FLAGS, htonl(nat->flags));
+}
+
+static inline const char *nat2str(uint16_t nat)
+{
+ switch (nat) {
+ case NFT_NAT_SNAT:
+ return "snat";
+ case NFT_NAT_DNAT:
+ return "dnat";
+ default:
+ return "unknown";
+ }
+}
+
+static inline int nftnl_str2nat(const char *nat)
+{
+ if (strcmp(nat, "snat") == 0)
+ return NFT_NAT_SNAT;
+ else if (strcmp(nat, "dnat") == 0)
+ return NFT_NAT_DNAT;
+ else {
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+static int
+nftnl_expr_nat_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_nat *nat = nftnl_expr_data(e);
+ int offset = 0, ret = 0;
+
+ ret = snprintf(buf, remain, "%s ", nat2str(nat->type));
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = snprintf(buf + offset, remain, "%s ",
+ nftnl_family2str(nat->family));
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (e->flags & (1 << NFTNL_EXPR_NAT_REG_ADDR_MIN)) {
+ ret = snprintf(buf + offset, remain,
+ "addr_min reg %u ", nat->sreg_addr_min);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_NAT_REG_ADDR_MAX)) {
+ ret = snprintf(buf + offset, remain,
+ "addr_max reg %u ", nat->sreg_addr_max);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_NAT_REG_PROTO_MIN)) {
+ ret = snprintf(buf + offset, remain,
+ "proto_min reg %u ", nat->sreg_proto_min);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_NAT_REG_PROTO_MAX)) {
+ ret = snprintf(buf + offset, remain,
+ "proto_max reg %u ", nat->sreg_proto_max);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_NAT_FLAGS)) {
+ ret = snprintf(buf + offset, remain, "flags 0x%x ", nat->flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+struct expr_ops expr_ops_nat = {
+ .name = "nat",
+ .alloc_len = sizeof(struct nftnl_expr_nat),
+ .max_attr = NFTA_NAT_MAX,
+ .set = nftnl_expr_nat_set,
+ .get = nftnl_expr_nat_get,
+ .parse = nftnl_expr_nat_parse,
+ .build = nftnl_expr_nat_build,
+ .output = nftnl_expr_nat_snprintf,
+};
diff --git a/src/expr/numgen.c b/src/expr/numgen.c
new file mode 100644
index 0000000..d4020a6
--- /dev/null
+++ b/src/expr/numgen.c
@@ -0,0 +1,184 @@
+/*
+ * (C) 2016 by Laura Garcia <nevola@gmail.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 <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_ng {
+ enum nft_registers dreg;
+ unsigned int modulus;
+ enum nft_ng_types type;
+ unsigned int offset;
+};
+
+static int
+nftnl_expr_ng_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_ng *ng = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_NG_DREG:
+ memcpy(&ng->dreg, data, sizeof(ng->dreg));
+ break;
+ case NFTNL_EXPR_NG_MODULUS:
+ memcpy(&ng->modulus, data, sizeof(ng->modulus));
+ break;
+ case NFTNL_EXPR_NG_TYPE:
+ memcpy(&ng->type, data, sizeof(ng->type));
+ break;
+ case NFTNL_EXPR_NG_OFFSET:
+ memcpy(&ng->offset, data, sizeof(ng->offset));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_ng_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_ng *ng = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_NG_DREG:
+ *data_len = sizeof(ng->dreg);
+ return &ng->dreg;
+ case NFTNL_EXPR_NG_MODULUS:
+ *data_len = sizeof(ng->modulus);
+ return &ng->modulus;
+ case NFTNL_EXPR_NG_TYPE:
+ *data_len = sizeof(ng->type);
+ return &ng->type;
+ case NFTNL_EXPR_NG_OFFSET:
+ *data_len = sizeof(ng->offset);
+ return &ng->offset;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_ng_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_NG_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_NG_DREG:
+ case NFTA_NG_MODULUS:
+ case NFTA_NG_TYPE:
+ case NFTA_NG_OFFSET:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_ng_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_ng *ng = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_NG_DREG))
+ mnl_attr_put_u32(nlh, NFTA_NG_DREG, htonl(ng->dreg));
+ if (e->flags & (1 << NFTNL_EXPR_NG_MODULUS))
+ mnl_attr_put_u32(nlh, NFTA_NG_MODULUS, htonl(ng->modulus));
+ if (e->flags & (1 << NFTNL_EXPR_NG_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_NG_TYPE, htonl(ng->type));
+ if (e->flags & (1 << NFTNL_EXPR_NG_OFFSET))
+ mnl_attr_put_u32(nlh, NFTA_NG_OFFSET, htonl(ng->offset));
+}
+
+static int
+nftnl_expr_ng_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_ng *ng = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_NG_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_ng_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_NG_DREG]) {
+ ng->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_NG_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_NG_DREG);
+ }
+ if (tb[NFTA_NG_MODULUS]) {
+ ng->modulus = ntohl(mnl_attr_get_u32(tb[NFTA_NG_MODULUS]));
+ e->flags |= (1 << NFTNL_EXPR_NG_MODULUS);
+ }
+ if (tb[NFTA_NG_TYPE]) {
+ ng->type = ntohl(mnl_attr_get_u32(tb[NFTA_NG_TYPE]));
+ e->flags |= (1 << NFTNL_EXPR_NG_TYPE);
+ }
+ if (tb[NFTA_NG_OFFSET]) {
+ ng->offset = ntohl(mnl_attr_get_u32(tb[NFTA_NG_OFFSET]));
+ e->flags |= (1 << NFTNL_EXPR_NG_OFFSET);
+ }
+
+ return ret;
+}
+
+static int
+nftnl_expr_ng_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_ng *ng = nftnl_expr_data(e);
+ int offset = 0, ret;
+
+ switch (ng->type) {
+ case NFT_NG_INCREMENTAL:
+ ret = snprintf(buf, remain, "reg %u = inc mod %u ",
+ ng->dreg, ng->modulus);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ break;
+ case NFT_NG_RANDOM:
+ ret = snprintf(buf, remain, "reg %u = random mod %u ",
+ ng->dreg, ng->modulus);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ break;
+ default:
+ return 0;
+ }
+
+ if (ng->offset) {
+ ret = snprintf(buf + offset, remain, "offset %u ", ng->offset);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+struct expr_ops expr_ops_ng = {
+ .name = "numgen",
+ .alloc_len = sizeof(struct nftnl_expr_ng),
+ .max_attr = NFTA_NG_MAX,
+ .set = nftnl_expr_ng_set,
+ .get = nftnl_expr_ng_get,
+ .parse = nftnl_expr_ng_parse,
+ .build = nftnl_expr_ng_build,
+ .output = nftnl_expr_ng_snprintf,
+};
diff --git a/src/expr/objref.c b/src/expr/objref.c
new file mode 100644
index 0000000..ad0688f
--- /dev/null
+++ b/src/expr/objref.c
@@ -0,0 +1,209 @@
+/*
+ * (C) 2016 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_objref {
+ struct {
+ uint32_t type;
+ const char *name;
+ } imm;
+ struct {
+ uint32_t sreg;
+ const char *name;
+ uint32_t id;
+ } set;
+};
+
+static int nftnl_expr_objref_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_objref *objref = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_OBJREF_IMM_TYPE:
+ memcpy(&objref->imm.type, data, sizeof(objref->imm.type));
+ break;
+ case NFTNL_EXPR_OBJREF_IMM_NAME:
+ objref->imm.name = strdup(data);
+ if (!objref->imm.name)
+ return -1;
+ break;
+ case NFTNL_EXPR_OBJREF_SET_SREG:
+ memcpy(&objref->set.sreg, data, sizeof(objref->set.sreg));
+ break;
+ case NFTNL_EXPR_OBJREF_SET_NAME:
+ objref->set.name = strdup(data);
+ if (!objref->set.name)
+ return -1;
+ break;
+ case NFTNL_EXPR_OBJREF_SET_ID:
+ memcpy(&objref->set.id, data, sizeof(objref->set.id));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *nftnl_expr_objref_get(const struct nftnl_expr *e,
+ uint16_t type, uint32_t *data_len)
+{
+ struct nftnl_expr_objref *objref = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_OBJREF_IMM_TYPE:
+ *data_len = sizeof(objref->imm.type);
+ return &objref->imm.type;
+ case NFTNL_EXPR_OBJREF_IMM_NAME:
+ *data_len = strlen(objref->imm.name) + 1;
+ return objref->imm.name;
+ case NFTNL_EXPR_OBJREF_SET_SREG:
+ *data_len = sizeof(objref->set.sreg);
+ return &objref->set.sreg;
+ case NFTNL_EXPR_OBJREF_SET_NAME:
+ *data_len = strlen(objref->set.name) + 1;
+ return objref->set.name;
+ case NFTNL_EXPR_OBJREF_SET_ID:
+ *data_len = sizeof(objref->set.id);
+ return &objref->set.id;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_objref_cb(const struct nlattr *attr, void *data)
+{
+ int type = mnl_attr_get_type(attr);
+ const struct nlattr **tb = data;
+
+ if (mnl_attr_type_valid(attr, NFTA_OBJREF_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_OBJREF_IMM_TYPE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_OBJREF_IMM_NAME:
+ case NFTA_OBJREF_SET_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ case NFTA_OBJREF_SET_SREG:
+ case NFTA_OBJREF_SET_ID:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void nftnl_expr_objref_build(struct nlmsghdr *nlh,
+ const struct nftnl_expr *e)
+{
+ struct nftnl_expr_objref *objref = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_OBJREF_IMM_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_OBJREF_IMM_TYPE,
+ htonl(objref->imm.type));
+ if (e->flags & (1 << NFTNL_EXPR_OBJREF_IMM_NAME))
+ mnl_attr_put_str(nlh, NFTA_OBJREF_IMM_NAME, objref->imm.name);
+ if (e->flags & (1 << NFTNL_EXPR_OBJREF_SET_SREG))
+ mnl_attr_put_u32(nlh, NFTA_OBJREF_SET_SREG,
+ htonl(objref->set.sreg));
+ if (e->flags & (1 << NFTNL_EXPR_OBJREF_SET_NAME))
+ mnl_attr_put_str(nlh, NFTA_OBJREF_SET_NAME, objref->set.name);
+ if (e->flags & (1 << NFTNL_EXPR_OBJREF_SET_ID))
+ mnl_attr_put_u32(nlh, NFTA_OBJREF_SET_ID,
+ htonl(objref->set.id));
+}
+
+static int nftnl_expr_objref_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_objref *objref = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_OBJREF_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_objref_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_OBJREF_IMM_TYPE]) {
+ objref->imm.type =
+ ntohl(mnl_attr_get_u32(tb[NFTA_OBJREF_IMM_TYPE]));
+ e->flags |= (1 << NFTNL_EXPR_OBJREF_IMM_TYPE);
+ }
+ if (tb[NFTA_OBJREF_IMM_NAME]) {
+ objref->imm.name =
+ strdup(mnl_attr_get_str(tb[NFTA_OBJREF_IMM_NAME]));
+ e->flags |= (1 << NFTNL_EXPR_OBJREF_IMM_NAME);
+ }
+ if (tb[NFTA_OBJREF_SET_SREG]) {
+ objref->set.sreg =
+ ntohl(mnl_attr_get_u32(tb[NFTA_OBJREF_SET_SREG]));
+ e->flags |= (1 << NFTNL_EXPR_OBJREF_SET_SREG);
+ }
+ if (tb[NFTA_OBJREF_SET_NAME]) {
+ objref->set.name =
+ strdup(mnl_attr_get_str(tb[NFTA_OBJREF_SET_NAME]));
+ e->flags |= (1 << NFTNL_EXPR_OBJREF_SET_NAME);
+ }
+ if (tb[NFTA_OBJREF_SET_ID]) {
+ objref->set.id =
+ ntohl(mnl_attr_get_u32(tb[NFTA_OBJREF_SET_ID]));
+ e->flags |= (1 << NFTNL_EXPR_OBJREF_SET_ID);
+ }
+
+ return 0;
+}
+
+static int nftnl_expr_objref_snprintf(char *buf, size_t len,
+ uint32_t flags,
+ const struct nftnl_expr *e)
+{
+ struct nftnl_expr_objref *objref = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_OBJREF_SET_SREG))
+ return snprintf(buf, len, "sreg %u set %s ",
+ objref->set.sreg, objref->set.name);
+ else
+ return snprintf(buf, len, "type %u name %s ",
+ objref->imm.type, objref->imm.name);
+}
+
+static void nftnl_expr_objref_free(const struct nftnl_expr *e)
+{
+ struct nftnl_expr_objref *objref = nftnl_expr_data(e);
+
+ xfree(objref->imm.name);
+ xfree(objref->set.name);
+}
+
+struct expr_ops expr_ops_objref = {
+ .name = "objref",
+ .alloc_len = sizeof(struct nftnl_expr_objref),
+ .max_attr = NFTA_OBJREF_MAX,
+ .free = nftnl_expr_objref_free,
+ .set = nftnl_expr_objref_set,
+ .get = nftnl_expr_objref_get,
+ .parse = nftnl_expr_objref_parse,
+ .build = nftnl_expr_objref_build,
+ .output = nftnl_expr_objref_snprintf,
+};
diff --git a/src/expr/osf.c b/src/expr/osf.c
new file mode 100644
index 0000000..f15a722
--- /dev/null
+++ b/src/expr/osf.c
@@ -0,0 +1,151 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+#define OSF_GENRE_SIZE 32
+
+struct nftnl_expr_osf {
+ enum nft_registers dreg;
+ uint8_t ttl;
+ uint32_t flags;
+};
+
+static int nftnl_expr_osf_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_osf *osf = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_OSF_DREG:
+ memcpy(&osf->dreg, data, sizeof(osf->dreg));
+ break;
+ case NFTNL_EXPR_OSF_TTL:
+ memcpy(&osf->ttl, data, sizeof(osf->ttl));
+ break;
+ case NFTNL_EXPR_OSF_FLAGS:
+ memcpy(&osf->flags, data, sizeof(osf->flags));
+ break;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_osf_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_osf *osf = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_OSF_DREG:
+ *data_len = sizeof(osf->dreg);
+ return &osf->dreg;
+ case NFTNL_EXPR_OSF_TTL:
+ *data_len = sizeof(osf->ttl);
+ return &osf->ttl;
+ case NFTNL_EXPR_OSF_FLAGS:
+ *data_len = sizeof(osf->flags);
+ return &osf->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_osf_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_OSF_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTNL_EXPR_OSF_DREG:
+ case NFTNL_EXPR_OSF_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+
+ case NFTNL_EXPR_OSF_TTL:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+ abi_breakage();
+ break;
+
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_osf_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_osf *osf = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_OSF_DREG))
+ mnl_attr_put_u32(nlh, NFTNL_EXPR_OSF_DREG, htonl(osf->dreg));
+ if (e->flags & (1 << NFTNL_EXPR_OSF_TTL))
+ mnl_attr_put_u8(nlh, NFTNL_EXPR_OSF_TTL, osf->ttl);
+ if (e->flags & (1 << NFTNL_EXPR_OSF_FLAGS))
+ if (osf->flags)
+ mnl_attr_put_u32(nlh, NFTNL_EXPR_OSF_FLAGS, htonl(osf->flags));
+}
+
+static int
+nftnl_expr_osf_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_osf *osf = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_OSF_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_osf_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_OSF_DREG]) {
+ osf->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_OSF_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_OSF_DREG);
+ }
+
+ if (tb[NFTA_OSF_TTL]) {
+ osf->ttl = mnl_attr_get_u8(tb[NFTA_OSF_TTL]);
+ e->flags |= (1 << NFTNL_EXPR_OSF_TTL);
+ }
+
+ if (tb[NFTA_OSF_FLAGS]) {
+ osf->flags = ntohl(mnl_attr_get_u32(tb[NFTA_OSF_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_OSF_FLAGS);
+ }
+
+ return 0;
+}
+
+static int
+nftnl_expr_osf_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_osf *osf = nftnl_expr_data(e);
+ int ret, offset = 0;
+
+ if (e->flags & (1 << NFTNL_EXPR_OSF_DREG)) {
+ ret = snprintf(buf, len, "dreg %u ", osf->dreg);
+ SNPRINTF_BUFFER_SIZE(ret, len, offset);
+ }
+
+ return offset;
+}
+
+struct expr_ops expr_ops_osf = {
+ .name = "osf",
+ .alloc_len = sizeof(struct nftnl_expr_osf),
+ .max_attr = NFTA_OSF_MAX,
+ .set = nftnl_expr_osf_set,
+ .get = nftnl_expr_osf_get,
+ .parse = nftnl_expr_osf_parse,
+ .build = nftnl_expr_osf_build,
+ .output = nftnl_expr_osf_snprintf,
+};
diff --git a/src/expr/payload.c b/src/expr/payload.c
new file mode 100644
index 0000000..c633e33
--- /dev/null
+++ b/src/expr/payload.c
@@ -0,0 +1,250 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <limits.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <libmnl/libmnl.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_payload {
+ enum nft_registers sreg;
+ enum nft_registers dreg;
+ enum nft_payload_bases base;
+ uint32_t offset;
+ uint32_t len;
+ uint32_t csum_type;
+ uint32_t csum_offset;
+ uint32_t csum_flags;
+};
+
+static int
+nftnl_expr_payload_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_payload *payload = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_PAYLOAD_SREG:
+ memcpy(&payload->sreg, data, sizeof(payload->sreg));
+ break;
+ case NFTNL_EXPR_PAYLOAD_DREG:
+ memcpy(&payload->dreg, data, sizeof(payload->dreg));
+ break;
+ case NFTNL_EXPR_PAYLOAD_BASE:
+ memcpy(&payload->base, data, sizeof(payload->base));
+ break;
+ case NFTNL_EXPR_PAYLOAD_OFFSET:
+ memcpy(&payload->offset, data, sizeof(payload->offset));
+ break;
+ case NFTNL_EXPR_PAYLOAD_LEN:
+ memcpy(&payload->len, data, sizeof(payload->len));
+ break;
+ case NFTNL_EXPR_PAYLOAD_CSUM_TYPE:
+ memcpy(&payload->csum_type, data, sizeof(payload->csum_type));
+ break;
+ case NFTNL_EXPR_PAYLOAD_CSUM_OFFSET:
+ memcpy(&payload->csum_offset, data, sizeof(payload->csum_offset));
+ break;
+ case NFTNL_EXPR_PAYLOAD_FLAGS:
+ memcpy(&payload->csum_flags, data, sizeof(payload->csum_flags));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_payload_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_payload *payload = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_PAYLOAD_SREG:
+ *data_len = sizeof(payload->sreg);
+ return &payload->sreg;
+ case NFTNL_EXPR_PAYLOAD_DREG:
+ *data_len = sizeof(payload->dreg);
+ return &payload->dreg;
+ case NFTNL_EXPR_PAYLOAD_BASE:
+ *data_len = sizeof(payload->base);
+ return &payload->base;
+ case NFTNL_EXPR_PAYLOAD_OFFSET:
+ *data_len = sizeof(payload->offset);
+ return &payload->offset;
+ case NFTNL_EXPR_PAYLOAD_LEN:
+ *data_len = sizeof(payload->len);
+ return &payload->len;
+ case NFTNL_EXPR_PAYLOAD_CSUM_TYPE:
+ *data_len = sizeof(payload->csum_type);
+ return &payload->csum_type;
+ case NFTNL_EXPR_PAYLOAD_CSUM_OFFSET:
+ *data_len = sizeof(payload->csum_offset);
+ return &payload->csum_offset;
+ case NFTNL_EXPR_PAYLOAD_FLAGS:
+ *data_len = sizeof(payload->csum_flags);
+ return &payload->csum_flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_payload_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_PAYLOAD_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_PAYLOAD_SREG:
+ case NFTA_PAYLOAD_DREG:
+ case NFTA_PAYLOAD_BASE:
+ case NFTA_PAYLOAD_OFFSET:
+ case NFTA_PAYLOAD_LEN:
+ case NFTA_PAYLOAD_CSUM_TYPE:
+ case NFTA_PAYLOAD_CSUM_OFFSET:
+ case NFTA_PAYLOAD_CSUM_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_payload_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_payload *payload = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_SREG))
+ mnl_attr_put_u32(nlh, NFTA_PAYLOAD_SREG, htonl(payload->sreg));
+ if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_DREG))
+ mnl_attr_put_u32(nlh, NFTA_PAYLOAD_DREG, htonl(payload->dreg));
+ if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_BASE))
+ mnl_attr_put_u32(nlh, NFTA_PAYLOAD_BASE, htonl(payload->base));
+ if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_OFFSET))
+ mnl_attr_put_u32(nlh, NFTA_PAYLOAD_OFFSET, htonl(payload->offset));
+ if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_LEN))
+ mnl_attr_put_u32(nlh, NFTA_PAYLOAD_LEN, htonl(payload->len));
+ if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_CSUM_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_PAYLOAD_CSUM_TYPE,
+ htonl(payload->csum_type));
+ if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_CSUM_OFFSET))
+ mnl_attr_put_u32(nlh, NFTA_PAYLOAD_CSUM_OFFSET,
+ htonl(payload->csum_offset));
+ if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_PAYLOAD_CSUM_FLAGS,
+ htonl(payload->csum_flags));
+}
+
+static int
+nftnl_expr_payload_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_payload *payload = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_PAYLOAD_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_payload_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_PAYLOAD_SREG]) {
+ payload->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_SREG]));
+ e->flags |= (1 << NFTNL_EXPR_PAYLOAD_SREG);
+ }
+ if (tb[NFTA_PAYLOAD_DREG]) {
+ payload->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_PAYLOAD_DREG);
+ }
+ if (tb[NFTA_PAYLOAD_BASE]) {
+ payload->base = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_BASE]));
+ e->flags |= (1 << NFTNL_EXPR_PAYLOAD_BASE);
+ }
+ if (tb[NFTA_PAYLOAD_OFFSET]) {
+ payload->offset = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_OFFSET]));
+ e->flags |= (1 << NFTNL_EXPR_PAYLOAD_OFFSET);
+ }
+ if (tb[NFTA_PAYLOAD_LEN]) {
+ payload->len = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_LEN]));
+ e->flags |= (1 << NFTNL_EXPR_PAYLOAD_LEN);
+ }
+ if (tb[NFTA_PAYLOAD_CSUM_TYPE]) {
+ payload->csum_type = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_CSUM_TYPE]));
+ e->flags |= (1 << NFTNL_EXPR_PAYLOAD_CSUM_TYPE);
+ }
+ if (tb[NFTA_PAYLOAD_CSUM_OFFSET]) {
+ payload->csum_offset = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_CSUM_OFFSET]));
+ e->flags |= (1 << NFTNL_EXPR_PAYLOAD_CSUM_OFFSET);
+ }
+ if (tb[NFTA_PAYLOAD_CSUM_FLAGS]) {
+ payload->csum_flags = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_CSUM_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_PAYLOAD_FLAGS);
+ }
+ return 0;
+}
+
+static const char *base2str_array[NFT_PAYLOAD_TUN_HEADER + 1] = {
+ [NFT_PAYLOAD_LL_HEADER] = "link",
+ [NFT_PAYLOAD_NETWORK_HEADER] = "network",
+ [NFT_PAYLOAD_TRANSPORT_HEADER] = "transport",
+ [NFT_PAYLOAD_INNER_HEADER] = "inner",
+ [NFT_PAYLOAD_TUN_HEADER] = "tunnel",
+};
+
+static const char *base2str(enum nft_payload_bases base)
+{
+ if (base > NFT_PAYLOAD_INNER_HEADER)
+ return "unknown";
+
+ return base2str_array[base];
+}
+
+static int
+nftnl_expr_payload_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_payload *payload = nftnl_expr_data(e);
+
+ if (payload->sreg)
+ return snprintf(buf, len, "write reg %u => %ub @ %s header + %u csum_type %u csum_off %u csum_flags 0x%x ",
+ payload->sreg,
+ payload->len, base2str(payload->base),
+ payload->offset, payload->csum_type,
+ payload->csum_offset,
+ payload->csum_flags);
+ else
+ return snprintf(buf, len, "load %ub @ %s header + %u => reg %u ",
+ payload->len, base2str(payload->base),
+ payload->offset, payload->dreg);
+}
+
+struct expr_ops expr_ops_payload = {
+ .name = "payload",
+ .alloc_len = sizeof(struct nftnl_expr_payload),
+ .max_attr = NFTA_PAYLOAD_MAX,
+ .set = nftnl_expr_payload_set,
+ .get = nftnl_expr_payload_get,
+ .parse = nftnl_expr_payload_parse,
+ .build = nftnl_expr_payload_build,
+ .output = nftnl_expr_payload_snprintf,
+};
diff --git a/src/expr/queue.c b/src/expr/queue.c
new file mode 100644
index 0000000..de287f2
--- /dev/null
+++ b/src/expr/queue.c
@@ -0,0 +1,197 @@
+/*
+ * (C) 2013 by Eric Leblond <eric@regit.org>
+ *
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_queue {
+ enum nft_registers sreg_qnum;
+ uint16_t queuenum;
+ uint16_t queues_total;
+ uint16_t flags;
+};
+
+static int nftnl_expr_queue_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_queue *queue = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_QUEUE_NUM:
+ memcpy(&queue->queuenum, data, sizeof(queue->queuenum));
+ break;
+ case NFTNL_EXPR_QUEUE_TOTAL:
+ memcpy(&queue->queues_total, data, sizeof(queue->queues_total));
+ break;
+ case NFTNL_EXPR_QUEUE_FLAGS:
+ memcpy(&queue->flags, data, sizeof(queue->flags));
+ break;
+ case NFTNL_EXPR_QUEUE_SREG_QNUM:
+ memcpy(&queue->sreg_qnum, data, sizeof(queue->sreg_qnum));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_queue_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_queue *queue = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_QUEUE_NUM:
+ *data_len = sizeof(queue->queuenum);
+ return &queue->queuenum;
+ case NFTNL_EXPR_QUEUE_TOTAL:
+ *data_len = sizeof(queue->queues_total);
+ return &queue->queues_total;
+ case NFTNL_EXPR_QUEUE_FLAGS:
+ *data_len = sizeof(queue->flags);
+ return &queue->flags;
+ case NFTNL_EXPR_QUEUE_SREG_QNUM:
+ *data_len = sizeof(queue->sreg_qnum);
+ return &queue->sreg_qnum;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_queue_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_QUEUE_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_QUEUE_NUM:
+ case NFTA_QUEUE_TOTAL:
+ case NFTA_QUEUE_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
+ abi_breakage();
+ break;
+ case NFTA_QUEUE_SREG_QNUM:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_queue_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_queue *queue = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_QUEUE_NUM))
+ mnl_attr_put_u16(nlh, NFTA_QUEUE_NUM, htons(queue->queuenum));
+ if (e->flags & (1 << NFTNL_EXPR_QUEUE_TOTAL))
+ mnl_attr_put_u16(nlh, NFTA_QUEUE_TOTAL, htons(queue->queues_total));
+ if (e->flags & (1 << NFTNL_EXPR_QUEUE_FLAGS))
+ mnl_attr_put_u16(nlh, NFTA_QUEUE_FLAGS, htons(queue->flags));
+ if (e->flags & (1 << NFTNL_EXPR_QUEUE_SREG_QNUM))
+ mnl_attr_put_u32(nlh, NFTA_QUEUE_SREG_QNUM, htonl(queue->sreg_qnum));
+}
+
+static int
+nftnl_expr_queue_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_queue *queue = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_QUEUE_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_queue_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_QUEUE_NUM]) {
+ queue->queuenum = ntohs(mnl_attr_get_u16(tb[NFTA_QUEUE_NUM]));
+ e->flags |= (1 << NFTNL_EXPR_QUEUE_NUM);
+ }
+ if (tb[NFTA_QUEUE_TOTAL]) {
+ queue->queues_total = ntohs(mnl_attr_get_u16(tb[NFTA_QUEUE_TOTAL]));
+ e->flags |= (1 << NFTNL_EXPR_QUEUE_TOTAL);
+ }
+ if (tb[NFTA_QUEUE_FLAGS]) {
+ queue->flags = ntohs(mnl_attr_get_u16(tb[NFTA_QUEUE_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_QUEUE_FLAGS);
+ }
+ if (tb[NFTA_QUEUE_SREG_QNUM]) {
+ queue->sreg_qnum = ntohl(mnl_attr_get_u32(tb[NFTA_QUEUE_SREG_QNUM]));
+ e->flags |= (1 << NFTNL_EXPR_QUEUE_SREG_QNUM);
+ }
+
+ return 0;
+}
+
+static int
+nftnl_expr_queue_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_queue *queue = nftnl_expr_data(e);
+ uint16_t total_queues;
+ int ret, offset = 0;
+
+ if (e->flags & (1 << NFTNL_EXPR_QUEUE_NUM)) {
+ total_queues = queue->queuenum + queue->queues_total - 1;
+
+ ret = snprintf(buf + offset, remain, "num %u", queue->queuenum);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (queue->queues_total && total_queues != queue->queuenum) {
+ ret = snprintf(buf + offset, remain, "-%u", total_queues);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ ret = snprintf(buf + offset, remain, " ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_QUEUE_SREG_QNUM)) {
+ ret = snprintf(buf + offset, remain, "sreg_qnum %u ",
+ queue->sreg_qnum);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_QUEUE_FLAGS)) {
+ if (queue->flags & (NFT_QUEUE_FLAG_BYPASS)) {
+ ret = snprintf(buf + offset, remain, "bypass ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (queue->flags & (NFT_QUEUE_FLAG_CPU_FANOUT)) {
+ ret = snprintf(buf + offset, remain, "fanout ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ }
+ return offset;
+}
+
+struct expr_ops expr_ops_queue = {
+ .name = "queue",
+ .alloc_len = sizeof(struct nftnl_expr_queue),
+ .max_attr = NFTA_QUEUE_MAX,
+ .set = nftnl_expr_queue_set,
+ .get = nftnl_expr_queue_get,
+ .parse = nftnl_expr_queue_parse,
+ .build = nftnl_expr_queue_build,
+ .output = nftnl_expr_queue_snprintf,
+};
diff --git a/src/expr/quota.c b/src/expr/quota.c
new file mode 100644
index 0000000..835729c
--- /dev/null
+++ b/src/expr/quota.c
@@ -0,0 +1,151 @@
+/*
+ * (C) 2016 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_quota {
+ uint64_t bytes;
+ uint64_t consumed;
+ uint32_t flags;
+};
+
+static int nftnl_expr_quota_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_quota *quota = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_QUOTA_BYTES:
+ memcpy(&quota->bytes, data, sizeof(quota->bytes));
+ break;
+ case NFTNL_EXPR_QUOTA_CONSUMED:
+ memcpy(&quota->consumed, data, sizeof(quota->consumed));
+ break;
+ case NFTNL_EXPR_QUOTA_FLAGS:
+ memcpy(&quota->flags, data, sizeof(quota->flags));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *nftnl_expr_quota_get(const struct nftnl_expr *e,
+ uint16_t type, uint32_t *data_len)
+{
+ struct nftnl_expr_quota *quota = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_QUOTA_BYTES:
+ *data_len = sizeof(quota->bytes);
+ return &quota->bytes;
+ case NFTNL_EXPR_QUOTA_CONSUMED:
+ *data_len = sizeof(quota->consumed);
+ return &quota->consumed;
+ case NFTNL_EXPR_QUOTA_FLAGS:
+ *data_len = sizeof(quota->flags);
+ return &quota->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_quota_cb(const struct nlattr *attr, void *data)
+{
+ int type = mnl_attr_get_type(attr);
+ const struct nlattr **tb = data;
+
+ if (mnl_attr_type_valid(attr, NFTA_QUOTA_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_QUOTA_BYTES:
+ case NFTA_QUOTA_CONSUMED:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ case NFTA_QUOTA_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_quota_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_quota *quota = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_QUOTA_BYTES))
+ mnl_attr_put_u64(nlh, NFTA_QUOTA_BYTES, htobe64(quota->bytes));
+ if (e->flags & (1 << NFTNL_EXPR_QUOTA_CONSUMED))
+ mnl_attr_put_u64(nlh, NFTA_QUOTA_CONSUMED, htobe64(quota->consumed));
+ if (e->flags & (1 << NFTNL_EXPR_QUOTA_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_QUOTA_FLAGS, htonl(quota->flags));
+}
+
+static int
+nftnl_expr_quota_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_quota *quota = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_QUOTA_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_quota_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_QUOTA_BYTES]) {
+ quota->bytes = be64toh(mnl_attr_get_u64(tb[NFTA_QUOTA_BYTES]));
+ e->flags |= (1 << NFTNL_EXPR_QUOTA_BYTES);
+ }
+ if (tb[NFTA_QUOTA_CONSUMED]) {
+ quota->consumed = be64toh(mnl_attr_get_u64(tb[NFTA_QUOTA_CONSUMED]));
+ e->flags |= (1 << NFTNL_EXPR_QUOTA_CONSUMED);
+ }
+ if (tb[NFTA_QUOTA_FLAGS]) {
+ quota->flags = ntohl(mnl_attr_get_u32(tb[NFTA_QUOTA_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_QUOTA_FLAGS);
+ }
+
+ return 0;
+}
+
+static int nftnl_expr_quota_snprintf(char *buf, size_t len,
+ uint32_t flags,
+ const struct nftnl_expr *e)
+{
+ struct nftnl_expr_quota *quota = nftnl_expr_data(e);
+
+ return snprintf(buf, len,
+ "bytes %"PRIu64" consumed %"PRIu64" flags %u ",
+ quota->bytes, quota->consumed, quota->flags);
+}
+
+struct expr_ops expr_ops_quota = {
+ .name = "quota",
+ .alloc_len = sizeof(struct nftnl_expr_quota),
+ .max_attr = NFTA_QUOTA_MAX,
+ .set = nftnl_expr_quota_set,
+ .get = nftnl_expr_quota_get,
+ .parse = nftnl_expr_quota_parse,
+ .build = nftnl_expr_quota_build,
+ .output = nftnl_expr_quota_snprintf,
+};
diff --git a/src/expr/range.c b/src/expr/range.c
new file mode 100644
index 0000000..473add8
--- /dev/null
+++ b/src/expr/range.c
@@ -0,0 +1,217 @@
+/*
+ * (C) 2016 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_range {
+ union nftnl_data_reg data_from;
+ union nftnl_data_reg data_to;
+ enum nft_registers sreg;
+ enum nft_range_ops op;
+};
+
+static int nftnl_expr_range_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_range *range = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_RANGE_SREG:
+ memcpy(&range->sreg, data, sizeof(range->sreg));
+ break;
+ case NFTNL_EXPR_RANGE_OP:
+ memcpy(&range->op, data, sizeof(range->op));
+ break;
+ case NFTNL_EXPR_RANGE_FROM_DATA:
+ memcpy(&range->data_from.val, data, data_len);
+ range->data_from.len = data_len;
+ break;
+ case NFTNL_EXPR_RANGE_TO_DATA:
+ memcpy(&range->data_to.val, data, data_len);
+ range->data_to.len = data_len;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *nftnl_expr_range_get(const struct nftnl_expr *e,
+ uint16_t type, uint32_t *data_len)
+{
+ struct nftnl_expr_range *range = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_RANGE_SREG:
+ *data_len = sizeof(range->sreg);
+ return &range->sreg;
+ case NFTNL_EXPR_RANGE_OP:
+ *data_len = sizeof(range->op);
+ return &range->op;
+ case NFTNL_EXPR_RANGE_FROM_DATA:
+ *data_len = range->data_from.len;
+ return &range->data_from.val;
+ case NFTNL_EXPR_RANGE_TO_DATA:
+ *data_len = range->data_to.len;
+ return &range->data_to.val;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_range_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_RANGE_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_RANGE_SREG:
+ case NFTA_RANGE_OP:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_RANGE_FROM_DATA:
+ case NFTA_RANGE_TO_DATA:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_range_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_range *range = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_RANGE_SREG))
+ mnl_attr_put_u32(nlh, NFTA_RANGE_SREG, htonl(range->sreg));
+ if (e->flags & (1 << NFTNL_EXPR_RANGE_OP))
+ mnl_attr_put_u32(nlh, NFTA_RANGE_OP, htonl(range->op));
+ if (e->flags & (1 << NFTNL_EXPR_RANGE_FROM_DATA)) {
+ struct nlattr *nest;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_RANGE_FROM_DATA);
+ mnl_attr_put(nlh, NFTA_DATA_VALUE, range->data_from.len,
+ range->data_from.val);
+ mnl_attr_nest_end(nlh, nest);
+ }
+ if (e->flags & (1 << NFTNL_EXPR_RANGE_TO_DATA)) {
+ struct nlattr *nest;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_RANGE_TO_DATA);
+ mnl_attr_put(nlh, NFTA_DATA_VALUE, range->data_to.len,
+ range->data_to.val);
+ mnl_attr_nest_end(nlh, nest);
+ }
+}
+
+static int
+nftnl_expr_range_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_range *range = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_RANGE_MAX+1] = {};
+ int ret = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_range_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_RANGE_SREG]) {
+ range->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_RANGE_SREG]));
+ e->flags |= (1 << NFTA_RANGE_SREG);
+ }
+ if (tb[NFTA_RANGE_OP]) {
+ range->op = ntohl(mnl_attr_get_u32(tb[NFTA_RANGE_OP]));
+ e->flags |= (1 << NFTA_RANGE_OP);
+ }
+ if (tb[NFTA_RANGE_FROM_DATA]) {
+ ret = nftnl_parse_data(&range->data_from,
+ tb[NFTA_RANGE_FROM_DATA], NULL);
+ e->flags |= (1 << NFTA_RANGE_FROM_DATA);
+ }
+ if (tb[NFTA_RANGE_TO_DATA]) {
+ ret = nftnl_parse_data(&range->data_to,
+ tb[NFTA_RANGE_TO_DATA], NULL);
+ e->flags |= (1 << NFTA_RANGE_TO_DATA);
+ }
+
+ return ret;
+}
+
+static const char *expr_range_str[] = {
+ [NFT_RANGE_EQ] = "eq",
+ [NFT_RANGE_NEQ] = "neq",
+};
+
+static const char *range2str(uint32_t op)
+{
+ if (op > NFT_RANGE_NEQ)
+ return "unknown";
+
+ return expr_range_str[op];
+}
+
+static inline int nftnl_str2range(const char *op)
+{
+ if (strcmp(op, "eq") == 0)
+ return NFT_RANGE_EQ;
+ else if (strcmp(op, "neq") == 0)
+ return NFT_RANGE_NEQ;
+ else {
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+static int nftnl_expr_range_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_range *range = nftnl_expr_data(e);
+ int offset = 0, ret;
+
+ ret = snprintf(buf, remain, "%s reg %u ",
+ range2str(range->op), range->sreg);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_data_reg_snprintf(buf + offset, remain, &range->data_from,
+ 0, DATA_VALUE);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_data_reg_snprintf(buf + offset, remain, &range->data_to,
+ 0, DATA_VALUE);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ return offset;
+}
+
+struct expr_ops expr_ops_range = {
+ .name = "range",
+ .alloc_len = sizeof(struct nftnl_expr_range),
+ .max_attr = NFTA_RANGE_MAX,
+ .set = nftnl_expr_range_set,
+ .get = nftnl_expr_range_get,
+ .parse = nftnl_expr_range_parse,
+ .build = nftnl_expr_range_build,
+ .output = nftnl_expr_range_snprintf,
+};
diff --git a/src/expr/redir.c b/src/expr/redir.c
new file mode 100644
index 0000000..87c2acc
--- /dev/null
+++ b/src/expr/redir.c
@@ -0,0 +1,171 @@
+/*
+ * (C) 2014 by Arturo Borrero Gonzalez <arturo@debian.org>
+ *
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_redir {
+ enum nft_registers sreg_proto_min;
+ enum nft_registers sreg_proto_max;
+ uint32_t flags;
+};
+
+static int
+nftnl_expr_redir_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_redir *redir = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_REDIR_REG_PROTO_MIN:
+ memcpy(&redir->sreg_proto_min, data, sizeof(redir->sreg_proto_min));
+ break;
+ case NFTNL_EXPR_REDIR_REG_PROTO_MAX:
+ memcpy(&redir->sreg_proto_max, data, sizeof(redir->sreg_proto_max));
+ break;
+ case NFTNL_EXPR_REDIR_FLAGS:
+ memcpy(&redir->flags, data, sizeof(redir->flags));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_redir_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_redir *redir = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_REDIR_REG_PROTO_MIN:
+ *data_len = sizeof(redir->sreg_proto_min);
+ return &redir->sreg_proto_min;
+ case NFTNL_EXPR_REDIR_REG_PROTO_MAX:
+ *data_len = sizeof(redir->sreg_proto_max);
+ return &redir->sreg_proto_max;
+ case NFTNL_EXPR_REDIR_FLAGS:
+ *data_len = sizeof(redir->flags);
+ return &redir->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_redir_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_REDIR_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_REDIR_REG_PROTO_MIN:
+ case NFTA_REDIR_REG_PROTO_MAX:
+ case NFTA_REDIR_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_redir_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_redir *redir = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_REDIR_REG_PROTO_MIN))
+ mnl_attr_put_u32(nlh, NFTA_REDIR_REG_PROTO_MIN,
+ htobe32(redir->sreg_proto_min));
+ if (e->flags & (1 << NFTNL_EXPR_REDIR_REG_PROTO_MAX))
+ mnl_attr_put_u32(nlh, NFTA_REDIR_REG_PROTO_MAX,
+ htobe32(redir->sreg_proto_max));
+ if (e->flags & (1 << NFTNL_EXPR_REDIR_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_REDIR_FLAGS, htobe32(redir->flags));
+}
+
+static int
+nftnl_expr_redir_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_redir *redir = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_REDIR_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_redir_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_REDIR_REG_PROTO_MIN]) {
+ redir->sreg_proto_min =
+ ntohl(mnl_attr_get_u32(tb[NFTA_REDIR_REG_PROTO_MIN]));
+ e->flags |= (1 << NFTNL_EXPR_REDIR_REG_PROTO_MIN);
+ }
+ if (tb[NFTA_REDIR_REG_PROTO_MAX]) {
+ redir->sreg_proto_max =
+ ntohl(mnl_attr_get_u32(tb[NFTA_REDIR_REG_PROTO_MAX]));
+ e->flags |= (1 << NFTNL_EXPR_REDIR_REG_PROTO_MAX);
+ }
+ if (tb[NFTA_REDIR_FLAGS]) {
+ redir->flags = be32toh(mnl_attr_get_u32(tb[NFTA_REDIR_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_REDIR_FLAGS);
+ }
+
+ return 0;
+}
+
+static int
+nftnl_expr_redir_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ int ret, offset = 0;
+ struct nftnl_expr_redir *redir = nftnl_expr_data(e);
+
+ if (nftnl_expr_is_set(e, NFTNL_EXPR_REDIR_REG_PROTO_MIN)) {
+ ret = snprintf(buf + offset, remain, "proto_min reg %u ",
+ redir->sreg_proto_min);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (nftnl_expr_is_set(e, NFTNL_EXPR_REDIR_REG_PROTO_MAX)) {
+ ret = snprintf(buf + offset, remain, "proto_max reg %u ",
+ redir->sreg_proto_max);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (nftnl_expr_is_set(e, NFTNL_EXPR_REDIR_FLAGS)) {
+ ret = snprintf(buf + offset, remain, "flags 0x%x ",
+ redir->flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+struct expr_ops expr_ops_redir = {
+ .name = "redir",
+ .alloc_len = sizeof(struct nftnl_expr_redir),
+ .max_attr = NFTA_REDIR_MAX,
+ .set = nftnl_expr_redir_set,
+ .get = nftnl_expr_redir_get,
+ .parse = nftnl_expr_redir_parse,
+ .build = nftnl_expr_redir_build,
+ .output = nftnl_expr_redir_snprintf,
+};
diff --git a/src/expr/reject.c b/src/expr/reject.c
new file mode 100644
index 0000000..c7c9441
--- /dev/null
+++ b/src/expr/reject.c
@@ -0,0 +1,138 @@
+/*
+ * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_reject {
+ uint32_t type;
+ uint8_t icmp_code;
+};
+
+static int nftnl_expr_reject_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_reject *reject = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_REJECT_TYPE:
+ memcpy(&reject->type, data, sizeof(reject->type));
+ break;
+ case NFTNL_EXPR_REJECT_CODE:
+ memcpy(&reject->icmp_code, data, sizeof(reject->icmp_code));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_reject_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_reject *reject = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_REJECT_TYPE:
+ *data_len = sizeof(reject->type);
+ return &reject->type;
+ case NFTNL_EXPR_REJECT_CODE:
+ *data_len = sizeof(reject->icmp_code);
+ return &reject->icmp_code;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_reject_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_REJECT_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_REJECT_TYPE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_REJECT_ICMP_CODE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_reject_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_reject *reject = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_REJECT_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_REJECT_TYPE, htonl(reject->type));
+ if (e->flags & (1 << NFTNL_EXPR_REJECT_CODE))
+ mnl_attr_put_u8(nlh, NFTA_REJECT_ICMP_CODE, reject->icmp_code);
+}
+
+static int
+nftnl_expr_reject_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_reject *reject = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_REJECT_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_reject_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_REJECT_TYPE]) {
+ reject->type = ntohl(mnl_attr_get_u32(tb[NFTA_REJECT_TYPE]));
+ e->flags |= (1 << NFTNL_EXPR_REJECT_TYPE);
+ }
+ if (tb[NFTA_REJECT_ICMP_CODE]) {
+ reject->icmp_code = mnl_attr_get_u8(tb[NFTA_REJECT_ICMP_CODE]);
+ e->flags |= (1 << NFTNL_EXPR_REJECT_CODE);
+ }
+
+ return 0;
+}
+
+static int
+nftnl_expr_reject_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_reject *reject = nftnl_expr_data(e);
+
+ return snprintf(buf, len, "type %u code %u ",
+ reject->type, reject->icmp_code);
+}
+
+struct expr_ops expr_ops_reject = {
+ .name = "reject",
+ .alloc_len = sizeof(struct nftnl_expr_reject),
+ .max_attr = NFTA_REJECT_MAX,
+ .set = nftnl_expr_reject_set,
+ .get = nftnl_expr_reject_get,
+ .parse = nftnl_expr_reject_parse,
+ .build = nftnl_expr_reject_build,
+ .output = nftnl_expr_reject_snprintf,
+};
diff --git a/src/expr/rt.c b/src/expr/rt.c
new file mode 100644
index 0000000..695a658
--- /dev/null
+++ b/src/expr/rt.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2016 Anders K. Pedersen <akp@cohaesio.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 <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_rt {
+ enum nft_rt_keys key;
+ enum nft_registers dreg;
+};
+
+static int
+nftnl_expr_rt_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_rt *rt = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_RT_KEY:
+ memcpy(&rt->key, data, sizeof(rt->key));
+ break;
+ case NFTNL_EXPR_RT_DREG:
+ memcpy(&rt->dreg, data, sizeof(rt->dreg));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_rt_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_rt *rt = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_RT_KEY:
+ *data_len = sizeof(rt->key);
+ return &rt->key;
+ case NFTNL_EXPR_RT_DREG:
+ *data_len = sizeof(rt->dreg);
+ return &rt->dreg;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_rt_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_RT_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_RT_KEY:
+ case NFTA_RT_DREG:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_rt_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_rt *rt = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_RT_KEY))
+ mnl_attr_put_u32(nlh, NFTA_RT_KEY, htonl(rt->key));
+ if (e->flags & (1 << NFTNL_EXPR_RT_DREG))
+ mnl_attr_put_u32(nlh, NFTA_RT_DREG, htonl(rt->dreg));
+}
+
+static int
+nftnl_expr_rt_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_rt *rt = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_RT_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_rt_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_RT_KEY]) {
+ rt->key = ntohl(mnl_attr_get_u32(tb[NFTA_RT_KEY]));
+ e->flags |= (1 << NFTNL_EXPR_RT_KEY);
+ }
+ if (tb[NFTA_RT_DREG]) {
+ rt->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_RT_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_RT_DREG);
+ }
+
+ return 0;
+}
+
+static const char *rt_key2str_array[NFT_RT_MAX + 1] = {
+ [NFT_RT_CLASSID] = "classid",
+ [NFT_RT_NEXTHOP4] = "nexthop4",
+ [NFT_RT_NEXTHOP6] = "nexthop6",
+ [NFT_RT_TCPMSS] = "tcpmss",
+ [NFT_RT_XFRM] = "ipsec",
+};
+
+static const char *rt_key2str(uint8_t key)
+{
+ if (key <= NFT_RT_MAX)
+ return rt_key2str_array[key];
+
+ return "unknown";
+}
+
+static inline int str2rt_key(const char *str)
+{
+ int i;
+
+ for (i = 0; i < NFT_RT_MAX; i++) {
+ if (strcmp(str, rt_key2str_array[i]) == 0)
+ return i;
+ }
+
+ errno = EINVAL;
+ return -1;
+}
+
+static int
+nftnl_expr_rt_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_rt *rt = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_RT_DREG)) {
+ return snprintf(buf, len, "load %s => reg %u ",
+ rt_key2str(rt->key), rt->dreg);
+ }
+ return 0;
+}
+
+struct expr_ops expr_ops_rt = {
+ .name = "rt",
+ .alloc_len = sizeof(struct nftnl_expr_rt),
+ .max_attr = NFTA_RT_MAX,
+ .set = nftnl_expr_rt_set,
+ .get = nftnl_expr_rt_get,
+ .parse = nftnl_expr_rt_parse,
+ .build = nftnl_expr_rt_build,
+ .output = nftnl_expr_rt_snprintf,
+};
diff --git a/src/expr/socket.c b/src/expr/socket.c
new file mode 100644
index 0000000..83045c0
--- /dev/null
+++ b/src/expr/socket.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2018 Máté Eckl <ecklm94@gmail.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 <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_socket {
+ enum nft_socket_keys key;
+ enum nft_registers dreg;
+ uint32_t level;
+};
+
+static int
+nftnl_expr_socket_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_socket *socket = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_SOCKET_KEY:
+ memcpy(&socket->key, data, sizeof(socket->key));
+ break;
+ case NFTNL_EXPR_SOCKET_DREG:
+ memcpy(&socket->dreg, data, sizeof(socket->dreg));
+ break;
+ case NFTNL_EXPR_SOCKET_LEVEL:
+ memcpy(&socket->level, data, sizeof(socket->level));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_socket_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_socket *socket = nftnl_expr_data(e);
+
+ switch (type) {
+ case NFTNL_EXPR_SOCKET_KEY:
+ *data_len = sizeof(socket->key);
+ return &socket->key;
+ case NFTNL_EXPR_SOCKET_DREG:
+ *data_len = sizeof(socket->dreg);
+ return &socket->dreg;
+ case NFTNL_EXPR_SOCKET_LEVEL:
+ *data_len = sizeof(socket->level);
+ return &socket->level;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_socket_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_SOCKET_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_SOCKET_KEY:
+ case NFTA_SOCKET_DREG:
+ case NFTA_SOCKET_LEVEL:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_socket_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_socket *socket = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_SOCKET_KEY))
+ mnl_attr_put_u32(nlh, NFTA_SOCKET_KEY, htonl(socket->key));
+ if (e->flags & (1 << NFTNL_EXPR_SOCKET_DREG))
+ mnl_attr_put_u32(nlh, NFTA_SOCKET_DREG, htonl(socket->dreg));
+ if (e->flags & (1 << NFTNL_EXPR_SOCKET_LEVEL))
+ mnl_attr_put_u32(nlh, NFTA_SOCKET_LEVEL, htonl(socket->level));
+}
+
+static int
+nftnl_expr_socket_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_socket *socket = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_SOCKET_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_socket_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_SOCKET_KEY]) {
+ socket->key = ntohl(mnl_attr_get_u32(tb[NFTA_SOCKET_KEY]));
+ e->flags |= (1 << NFTNL_EXPR_SOCKET_KEY);
+ }
+ if (tb[NFTA_SOCKET_DREG]) {
+ socket->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_SOCKET_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_SOCKET_DREG);
+ }
+ if (tb[NFTA_SOCKET_LEVEL]) {
+ socket->level = ntohl(mnl_attr_get_u32(tb[NFTA_SOCKET_LEVEL]));
+ e->flags |= (1 << NFTNL_EXPR_SOCKET_LEVEL);
+ }
+
+ return 0;
+}
+
+static const char *socket_key2str_array[NFT_SOCKET_MAX + 1] = {
+ [NFT_SOCKET_TRANSPARENT] = "transparent",
+ [NFT_SOCKET_MARK] = "mark",
+ [NFT_SOCKET_WILDCARD] = "wildcard",
+ [NFT_SOCKET_CGROUPV2] = "cgroupv2",
+};
+
+static const char *socket_key2str(uint8_t key)
+{
+ if (key < NFT_SOCKET_MAX + 1)
+ return socket_key2str_array[key];
+
+ return "unknown";
+}
+
+static int
+nftnl_expr_socket_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_socket *socket = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_SOCKET_DREG)) {
+ return snprintf(buf, len, "load %s => reg %u ",
+ socket_key2str(socket->key), socket->dreg);
+ }
+ if (e->flags & (1 << NFTNL_EXPR_SOCKET_LEVEL))
+ return snprintf(buf, len, "level %u ", socket->level);
+
+ return 0;
+}
+
+struct expr_ops expr_ops_socket = {
+ .name = "socket",
+ .alloc_len = sizeof(struct nftnl_expr_socket),
+ .max_attr = NFTA_SOCKET_MAX,
+ .set = nftnl_expr_socket_set,
+ .get = nftnl_expr_socket_get,
+ .parse = nftnl_expr_socket_parse,
+ .build = nftnl_expr_socket_build,
+ .output = nftnl_expr_socket_snprintf,
+};
diff --git a/src/expr/synproxy.c b/src/expr/synproxy.c
new file mode 100644
index 0000000..47fcaef
--- /dev/null
+++ b/src/expr/synproxy.c
@@ -0,0 +1,156 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_synproxy {
+ uint16_t mss;
+ uint8_t wscale;
+ uint32_t flags;
+};
+
+static int nftnl_expr_synproxy_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_synproxy *synproxy = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_SYNPROXY_MSS:
+ memcpy(&synproxy->mss, data, sizeof(synproxy->mss));
+ break;
+ case NFTNL_EXPR_SYNPROXY_WSCALE:
+ memcpy(&synproxy->wscale, data, sizeof(synproxy->wscale));
+ break;
+ case NFTNL_EXPR_SYNPROXY_FLAGS:
+ memcpy(&synproxy->flags, data, sizeof(synproxy->flags));
+ break;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_synproxy_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_synproxy *synproxy = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_SYNPROXY_MSS:
+ *data_len = sizeof(synproxy->mss);
+ return &synproxy->mss;
+ case NFTNL_EXPR_SYNPROXY_WSCALE:
+ *data_len = sizeof(synproxy->wscale);
+ return &synproxy->wscale;
+ case NFTNL_EXPR_SYNPROXY_FLAGS:
+ *data_len = sizeof(synproxy->flags);
+ return &synproxy->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_synproxy_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_SYNPROXY_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTNL_EXPR_SYNPROXY_MSS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
+ abi_breakage();
+ break;
+
+ case NFTNL_EXPR_SYNPROXY_WSCALE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+ abi_breakage();
+ break;
+
+ case NFTNL_EXPR_SYNPROXY_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_synproxy_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_synproxy *synproxy = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_SYNPROXY_MSS))
+ mnl_attr_put_u16(nlh, NFTNL_EXPR_SYNPROXY_MSS,
+ htons(synproxy->mss));
+ if (e->flags & (1 << NFTNL_EXPR_SYNPROXY_WSCALE))
+ mnl_attr_put_u8(nlh, NFTNL_EXPR_SYNPROXY_WSCALE,
+ synproxy->wscale);
+ if (e->flags & (1 << NFTNL_EXPR_SYNPROXY_FLAGS))
+ mnl_attr_put_u32(nlh, NFTNL_EXPR_SYNPROXY_FLAGS,
+ htonl(synproxy->flags));
+}
+
+static int
+nftnl_expr_synproxy_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_synproxy *synproxy = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_SYNPROXY_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_synproxy_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_SYNPROXY_MSS]) {
+ synproxy->mss = ntohs(mnl_attr_get_u16(tb[NFTA_SYNPROXY_MSS]));
+ e->flags |= (1 << NFTNL_EXPR_SYNPROXY_MSS);
+ }
+
+ if (tb[NFTA_SYNPROXY_WSCALE]) {
+ synproxy->wscale = mnl_attr_get_u8(tb[NFTA_SYNPROXY_WSCALE]);
+ e->flags |= (1 << NFTNL_EXPR_SYNPROXY_WSCALE);
+ }
+
+ if (tb[NFTA_SYNPROXY_FLAGS]) {
+ synproxy->flags = ntohl(mnl_attr_get_u32(tb[NFTA_SYNPROXY_FLAGS]));
+ e->flags |= (1 << NFTNL_EXPR_SYNPROXY_FLAGS);
+ }
+
+ return 0;
+}
+
+static int
+nftnl_expr_synproxy_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_synproxy *synproxy = nftnl_expr_data(e);
+ int ret, offset = 0;
+
+ if (e->flags & (1 << NFTNL_EXPR_SYNPROXY_MSS) &&
+ e->flags & (1 << NFTNL_EXPR_SYNPROXY_WSCALE)) {
+ ret = snprintf(buf, len, "mss %u wscale %u ", synproxy->mss,
+ synproxy->wscale);
+ SNPRINTF_BUFFER_SIZE(ret, len, offset);
+ }
+
+ return offset;
+}
+
+struct expr_ops expr_ops_synproxy = {
+ .name = "synproxy",
+ .alloc_len = sizeof(struct nftnl_expr_synproxy),
+ .max_attr = NFTA_SYNPROXY_MAX,
+ .set = nftnl_expr_synproxy_set,
+ .get = nftnl_expr_synproxy_get,
+ .parse = nftnl_expr_synproxy_parse,
+ .build = nftnl_expr_synproxy_build,
+ .output = nftnl_expr_synproxy_snprintf,
+};
diff --git a/src/expr/target.c b/src/expr/target.c
new file mode 100644
index 0000000..2a3fe8a
--- /dev/null
+++ b/src/expr/target.c
@@ -0,0 +1,193 @@
+/*
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h> /* for memcpy */
+#include <arpa/inet.h>
+#include <errno.h>
+#include <libmnl/libmnl.h>
+
+#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/nf_tables_compat.h>
+
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+/* From include/linux/netfilter/x_tables.h */
+#define XT_EXTENSION_MAXNAMELEN 29
+
+struct nftnl_expr_target {
+ char name[XT_EXTENSION_MAXNAMELEN];
+ uint32_t rev;
+ uint32_t data_len;
+ const void *data;
+};
+
+static int
+nftnl_expr_target_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_target *tg = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_TG_NAME:
+ snprintf(tg->name, sizeof(tg->name), "%.*s", data_len,
+ (const char *) data);
+ break;
+ case NFTNL_EXPR_TG_REV:
+ memcpy(&tg->rev, data, sizeof(tg->rev));
+ break;
+ case NFTNL_EXPR_TG_INFO:
+ if (e->flags & (1 << NFTNL_EXPR_TG_INFO))
+ xfree(tg->data);
+
+ tg->data = data;
+ tg->data_len = data_len;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_target_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_target *tg = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_TG_NAME:
+ *data_len = sizeof(tg->name);
+ return tg->name;
+ case NFTNL_EXPR_TG_REV:
+ *data_len = sizeof(tg->rev);
+ return &tg->rev;
+ case NFTNL_EXPR_TG_INFO:
+ *data_len = tg->data_len;
+ return tg->data;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_target_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_TARGET_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_TARGET_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0)
+ abi_breakage();
+ break;
+ case NFTA_TARGET_REV:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_TARGET_INFO:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_target_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_target *tg = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_TG_NAME))
+ mnl_attr_put_strz(nlh, NFTA_TARGET_NAME, tg->name);
+ if (e->flags & (1 << NFTNL_EXPR_TG_REV))
+ mnl_attr_put_u32(nlh, NFTA_TARGET_REV, htonl(tg->rev));
+ if (e->flags & (1 << NFTNL_EXPR_TG_INFO))
+ mnl_attr_put(nlh, NFTA_TARGET_INFO, tg->data_len, tg->data);
+}
+
+static int nftnl_expr_target_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_target *target = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_TARGET_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_target_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_TARGET_NAME]) {
+ snprintf(target->name, XT_EXTENSION_MAXNAMELEN, "%s",
+ mnl_attr_get_str(tb[NFTA_TARGET_NAME]));
+
+ target->name[XT_EXTENSION_MAXNAMELEN-1] = '\0';
+ e->flags |= (1 << NFTNL_EXPR_TG_NAME);
+ }
+
+ if (tb[NFTA_TARGET_REV]) {
+ target->rev = ntohl(mnl_attr_get_u32(tb[NFTA_TARGET_REV]));
+ e->flags |= (1 << NFTNL_EXPR_TG_REV);
+ }
+
+ if (tb[NFTA_TARGET_INFO]) {
+ uint32_t len = mnl_attr_get_payload_len(tb[NFTA_TARGET_INFO]);
+ void *target_data;
+
+ if (target->data)
+ xfree(target->data);
+
+ target_data = calloc(1, len);
+ if (target_data == NULL)
+ return -1;
+
+ memcpy(target_data, mnl_attr_get_payload(tb[NFTA_TARGET_INFO]), len);
+
+ target->data = target_data;
+ target->data_len = len;
+
+ e->flags |= (1 << NFTNL_EXPR_TG_INFO);
+ }
+
+ return 0;
+}
+
+static int
+nftnl_expr_target_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_target *target = nftnl_expr_data(e);
+
+ return snprintf(buf, len, "name %s rev %u ", target->name, target->rev);
+}
+
+static void nftnl_expr_target_free(const struct nftnl_expr *e)
+{
+ struct nftnl_expr_target *target = nftnl_expr_data(e);
+
+ xfree(target->data);
+}
+
+struct expr_ops expr_ops_target = {
+ .name = "target",
+ .alloc_len = sizeof(struct nftnl_expr_target),
+ .max_attr = NFTA_TARGET_MAX,
+ .free = nftnl_expr_target_free,
+ .set = nftnl_expr_target_set,
+ .get = nftnl_expr_target_get,
+ .parse = nftnl_expr_target_parse,
+ .build = nftnl_expr_target_build,
+ .output = nftnl_expr_target_snprintf,
+};
diff --git a/src/expr/tproxy.c b/src/expr/tproxy.c
new file mode 100644
index 0000000..bd5ffbf
--- /dev/null
+++ b/src/expr/tproxy.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2018 Máté Eckl <ecklm94@gmail.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 "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_tproxy {
+ enum nft_registers sreg_addr;
+ enum nft_registers sreg_port;
+ int family;
+};
+
+static int
+nftnl_expr_tproxy_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_tproxy *tproxy = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_TPROXY_FAMILY:
+ memcpy(&tproxy->family, data, sizeof(tproxy->family));
+ break;
+ case NFTNL_EXPR_TPROXY_REG_ADDR:
+ memcpy(&tproxy->sreg_addr, data, sizeof(tproxy->sreg_addr));
+ break;
+ case NFTNL_EXPR_TPROXY_REG_PORT:
+ memcpy(&tproxy->sreg_port, data, sizeof(tproxy->sreg_port));
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static const void *
+nftnl_expr_tproxy_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_tproxy *tproxy = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_TPROXY_FAMILY:
+ *data_len = sizeof(tproxy->family);
+ return &tproxy->family;
+ case NFTNL_EXPR_TPROXY_REG_ADDR:
+ *data_len = sizeof(tproxy->sreg_addr);
+ return &tproxy->sreg_addr;
+ case NFTNL_EXPR_TPROXY_REG_PORT:
+ *data_len = sizeof(tproxy->sreg_port);
+ return &tproxy->sreg_port;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_tproxy_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_TPROXY_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_TPROXY_FAMILY:
+ case NFTA_TPROXY_REG_ADDR:
+ case NFTA_TPROXY_REG_PORT:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int
+nftnl_expr_tproxy_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_tproxy *tproxy = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_TPROXY_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_tproxy_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_TPROXY_FAMILY]) {
+ tproxy->family = ntohl(mnl_attr_get_u32(tb[NFTA_TPROXY_FAMILY]));
+ e->flags |= (1 << NFTNL_EXPR_TPROXY_FAMILY);
+ }
+ if (tb[NFTA_TPROXY_REG_ADDR]) {
+ tproxy->sreg_addr =
+ ntohl(mnl_attr_get_u32(tb[NFTA_TPROXY_REG_ADDR]));
+ e->flags |= (1 << NFTNL_EXPR_TPROXY_REG_ADDR);
+ }
+ if (tb[NFTA_TPROXY_REG_PORT]) {
+ tproxy->sreg_port =
+ ntohl(mnl_attr_get_u32(tb[NFTA_TPROXY_REG_PORT]));
+ e->flags |= (1 << NFTNL_EXPR_TPROXY_REG_PORT);
+ }
+
+ return 0;
+}
+
+static void
+nftnl_expr_tproxy_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_tproxy *tproxy = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_TPROXY_FAMILY))
+ mnl_attr_put_u32(nlh, NFTA_TPROXY_FAMILY, htonl(tproxy->family));
+
+ if (e->flags & (1 << NFTNL_EXPR_TPROXY_REG_ADDR))
+ mnl_attr_put_u32(nlh, NFTA_TPROXY_REG_ADDR,
+ htonl(tproxy->sreg_addr));
+
+ if (e->flags & (1 << NFTNL_EXPR_TPROXY_REG_PORT))
+ mnl_attr_put_u32(nlh, NFTA_TPROXY_REG_PORT,
+ htonl(tproxy->sreg_port));
+}
+
+static int
+nftnl_expr_tproxy_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_tproxy *tproxy = nftnl_expr_data(e);
+ int offset = 0, ret = 0;
+
+ if (tproxy->family != NFTA_TPROXY_UNSPEC) {
+ ret = snprintf(buf + offset, remain, "%s ",
+ nftnl_family2str(tproxy->family));
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_TPROXY_REG_ADDR)) {
+ ret = snprintf(buf + offset, remain,
+ "addr reg %u ", tproxy->sreg_addr);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (e->flags & (1 << NFTNL_EXPR_TPROXY_REG_PORT)) {
+ ret = snprintf(buf + offset, remain,
+ "port reg %u ", tproxy->sreg_port);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+struct expr_ops expr_ops_tproxy = {
+ .name = "tproxy",
+ .alloc_len = sizeof(struct nftnl_expr_tproxy),
+ .max_attr = NFTA_TPROXY_MAX,
+ .set = nftnl_expr_tproxy_set,
+ .get = nftnl_expr_tproxy_get,
+ .parse = nftnl_expr_tproxy_parse,
+ .build = nftnl_expr_tproxy_build,
+ .output = nftnl_expr_tproxy_snprintf,
+};
diff --git a/src/expr/tunnel.c b/src/expr/tunnel.c
new file mode 100644
index 0000000..a00f620
--- /dev/null
+++ b/src/expr/tunnel.c
@@ -0,0 +1,149 @@
+/*
+ * (C) 2018 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_tunnel {
+ enum nft_tunnel_keys key;
+ enum nft_registers dreg;
+};
+
+static int nftnl_expr_tunnel_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_tunnel *tunnel = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_TUNNEL_KEY:
+ memcpy(&tunnel->key, data, sizeof(tunnel->key));
+ break;
+ case NFTNL_EXPR_TUNNEL_DREG:
+ memcpy(&tunnel->dreg, data, sizeof(tunnel->dreg));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_tunnel_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_tunnel *tunnel = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_TUNNEL_KEY:
+ *data_len = sizeof(tunnel->key);
+ return &tunnel->key;
+ case NFTNL_EXPR_TUNNEL_DREG:
+ *data_len = sizeof(tunnel->dreg);
+ return &tunnel->dreg;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_tunnel_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_TUNNEL_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_TUNNEL_KEY:
+ case NFTA_TUNNEL_DREG:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_tunnel_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_tunnel *tunnel = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_TUNNEL_KEY))
+ mnl_attr_put_u32(nlh, NFTA_TUNNEL_KEY, htonl(tunnel->key));
+ if (e->flags & (1 << NFTNL_EXPR_TUNNEL_DREG))
+ mnl_attr_put_u32(nlh, NFTA_TUNNEL_DREG, htonl(tunnel->dreg));
+}
+
+static int
+nftnl_expr_tunnel_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_tunnel *tunnel = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_TUNNEL_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_tunnel_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_TUNNEL_KEY]) {
+ tunnel->key = ntohl(mnl_attr_get_u32(tb[NFTA_TUNNEL_KEY]));
+ e->flags |= (1 << NFTNL_EXPR_TUNNEL_KEY);
+ }
+ if (tb[NFTA_TUNNEL_DREG]) {
+ tunnel->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_TUNNEL_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_TUNNEL_DREG);
+ }
+
+ return 0;
+}
+
+static const char *tunnel_key2str_array[NFT_TUNNEL_MAX + 1] = {
+ [NFT_TUNNEL_PATH] = "path",
+ [NFT_TUNNEL_ID] = "id",
+};
+
+static const char *tunnel_key2str(uint8_t key)
+{
+ if (key <= NFT_TUNNEL_MAX)
+ return tunnel_key2str_array[key];
+
+ return "unknown";
+}
+
+static int
+nftnl_expr_tunnel_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_tunnel *tunnel = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_TUNNEL_DREG)) {
+ return snprintf(buf, len, "load %s => reg %u ",
+ tunnel_key2str(tunnel->key), tunnel->dreg);
+ }
+ return 0;
+}
+
+struct expr_ops expr_ops_tunnel = {
+ .name = "tunnel",
+ .alloc_len = sizeof(struct nftnl_expr_tunnel),
+ .max_attr = NFTA_TUNNEL_MAX,
+ .set = nftnl_expr_tunnel_set,
+ .get = nftnl_expr_tunnel_get,
+ .parse = nftnl_expr_tunnel_parse,
+ .build = nftnl_expr_tunnel_build,
+ .output = nftnl_expr_tunnel_snprintf,
+};
diff --git a/src/expr/xfrm.c b/src/expr/xfrm.c
new file mode 100644
index 0000000..2db00d5
--- /dev/null
+++ b/src/expr/xfrm.c
@@ -0,0 +1,200 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/netfilter/nf_tables.h>
+#include <linux/xfrm.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_xfrm {
+ enum nft_registers dreg;
+ enum nft_xfrm_keys key;
+ uint32_t spnum;
+ uint8_t dir;
+};
+
+static int
+nftnl_expr_xfrm_set(struct nftnl_expr *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr_xfrm *x = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_XFRM_KEY:
+ memcpy(&x->key, data, sizeof(x->key));
+ break;
+ case NFTNL_EXPR_XFRM_DIR:
+ memcpy(&x->dir, data, sizeof(x->dir));
+ break;
+ case NFTNL_EXPR_XFRM_SPNUM:
+ memcpy(&x->spnum, data, sizeof(x->spnum));
+ break;
+ case NFTNL_EXPR_XFRM_DREG:
+ memcpy(&x->dreg, data, sizeof(x->dreg));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_expr_xfrm_get(const struct nftnl_expr *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_expr_xfrm *x = nftnl_expr_data(e);
+
+ switch(type) {
+ case NFTNL_EXPR_XFRM_KEY:
+ *data_len = sizeof(x->key);
+ return &x->key;
+ case NFTNL_EXPR_XFRM_DIR:
+ *data_len = sizeof(x->dir);
+ return &x->dir;
+ case NFTNL_EXPR_XFRM_SPNUM:
+ *data_len = sizeof(x->spnum);
+ return &x->spnum;
+ case NFTNL_EXPR_XFRM_DREG:
+ *data_len = sizeof(x->dreg);
+ return &x->dreg;
+ }
+ return NULL;
+}
+
+static int nftnl_expr_xfrm_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_XFRM_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_XFRM_DREG:
+ case NFTA_XFRM_KEY:
+ case NFTA_XFRM_SPNUM:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_XFRM_DIR:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_expr_xfrm_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_xfrm *x = nftnl_expr_data(e);
+
+ if (e->flags & (1 << NFTNL_EXPR_XFRM_KEY))
+ mnl_attr_put_u32(nlh, NFTA_XFRM_KEY, htonl(x->key));
+ if (e->flags & (1 << NFTNL_EXPR_XFRM_DIR))
+ mnl_attr_put_u8(nlh, NFTA_XFRM_DIR, x->dir);
+ if (e->flags & (1 << NFTNL_EXPR_XFRM_SPNUM))
+ mnl_attr_put_u32(nlh, NFTA_XFRM_SPNUM, htonl(x->spnum));
+ if (e->flags & (1 << NFTNL_EXPR_XFRM_DREG))
+ mnl_attr_put_u32(nlh, NFTA_XFRM_DREG, htonl(x->dreg));
+}
+
+static int
+nftnl_expr_xfrm_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+ struct nftnl_expr_xfrm *x = nftnl_expr_data(e);
+ struct nlattr *tb[NFTA_XFRM_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_expr_xfrm_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_XFRM_KEY]) {
+ x->key = ntohl(mnl_attr_get_u32(tb[NFTA_XFRM_KEY]));
+ e->flags |= (1 << NFTNL_EXPR_XFRM_KEY);
+ }
+ if (tb[NFTA_XFRM_DIR]) {
+ x->dir = mnl_attr_get_u8(tb[NFTA_XFRM_DIR]);
+ e->flags |= (1 << NFTNL_EXPR_XFRM_DIR);
+ }
+ if (tb[NFTA_XFRM_SPNUM]) {
+ x->spnum = ntohl(mnl_attr_get_u32(tb[NFTA_XFRM_SPNUM]));
+ e->flags |= (1 << NFTNL_EXPR_XFRM_SPNUM);
+ }
+ if (tb[NFTA_XFRM_DREG]) {
+ x->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_XFRM_DREG]));
+ e->flags |= (1 << NFTNL_EXPR_XFRM_DREG);
+ }
+ return 0;
+}
+
+static const char *xfrmkey2str_array[] = {
+ [NFT_XFRM_KEY_DADDR_IP4] = "daddr4",
+ [NFT_XFRM_KEY_SADDR_IP4] = "saddr4",
+ [NFT_XFRM_KEY_DADDR_IP6] = "daddr6",
+ [NFT_XFRM_KEY_SADDR_IP6] = "saddr6",
+ [NFT_XFRM_KEY_REQID] = "reqid",
+ [NFT_XFRM_KEY_SPI] = "spi",
+};
+
+static const char *xfrmkey2str(uint32_t key)
+{
+ if (key >= sizeof(xfrmkey2str_array) / sizeof(xfrmkey2str_array[0]))
+ return "unknown";
+
+ return xfrmkey2str_array[key];
+}
+
+static const char *xfrmdir2str_array[] = {
+ [XFRM_POLICY_IN] = "in",
+ [XFRM_POLICY_OUT] = "out",
+};
+
+static const char *xfrmdir2str(uint8_t dir)
+{
+ if (dir >= sizeof(xfrmdir2str_array) / sizeof(xfrmdir2str_array[0]))
+ return "unknown";
+
+ return xfrmdir2str_array[dir];
+}
+
+static int
+nftnl_expr_xfrm_snprintf(char *buf, size_t remain,
+ uint32_t flags, const struct nftnl_expr *e)
+{
+ struct nftnl_expr_xfrm *x = nftnl_expr_data(e);
+ int ret, offset = 0;
+
+ if (e->flags & (1 << NFTNL_EXPR_XFRM_DREG)) {
+ ret = snprintf(buf, remain, "load %s %u %s => reg %u ",
+ xfrmdir2str(x->dir),
+ x->spnum,
+ xfrmkey2str(x->key), x->dreg);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ return offset;
+}
+
+struct expr_ops expr_ops_xfrm = {
+ .name = "xfrm",
+ .alloc_len = sizeof(struct nftnl_expr_xfrm),
+ .max_attr = NFTA_XFRM_MAX,
+ .set = nftnl_expr_xfrm_set,
+ .get = nftnl_expr_xfrm_get,
+ .parse = nftnl_expr_xfrm_parse,
+ .build = nftnl_expr_xfrm_build,
+ .output = nftnl_expr_xfrm_snprintf,
+};
diff --git a/src/expr_ops.c b/src/expr_ops.c
new file mode 100644
index 0000000..b85f472
--- /dev/null
+++ b/src/expr_ops.c
@@ -0,0 +1,106 @@
+#include <string.h>
+#include <linux_list.h>
+
+#include "expr_ops.h"
+
+/* Unfortunately, __attribute__((constructor)) breaks library static linking */
+extern struct expr_ops expr_ops_bitwise;
+extern struct expr_ops expr_ops_byteorder;
+extern struct expr_ops expr_ops_cmp;
+extern struct expr_ops expr_ops_connlimit;
+extern struct expr_ops expr_ops_counter;
+extern struct expr_ops expr_ops_ct;
+extern struct expr_ops expr_ops_dup;
+extern struct expr_ops expr_ops_exthdr;
+extern struct expr_ops expr_ops_fwd;
+extern struct expr_ops expr_ops_immediate;
+extern struct expr_ops expr_ops_inner;
+extern struct expr_ops expr_ops_last;
+extern struct expr_ops expr_ops_limit;
+extern struct expr_ops expr_ops_log;
+extern struct expr_ops expr_ops_lookup;
+extern struct expr_ops expr_ops_masq;
+extern struct expr_ops expr_ops_match;
+extern struct expr_ops expr_ops_meta;
+extern struct expr_ops expr_ops_ng;
+extern struct expr_ops expr_ops_nat;
+extern struct expr_ops expr_ops_tproxy;
+extern struct expr_ops expr_ops_objref;
+extern struct expr_ops expr_ops_payload;
+extern struct expr_ops expr_ops_range;
+extern struct expr_ops expr_ops_redir;
+extern struct expr_ops expr_ops_reject;
+extern struct expr_ops expr_ops_rt;
+extern struct expr_ops expr_ops_queue;
+extern struct expr_ops expr_ops_quota;
+extern struct expr_ops expr_ops_target;
+extern struct expr_ops expr_ops_dynset;
+extern struct expr_ops expr_ops_hash;
+extern struct expr_ops expr_ops_fib;
+extern struct expr_ops expr_ops_flow;
+extern struct expr_ops expr_ops_socket;
+extern struct expr_ops expr_ops_synproxy;
+extern struct expr_ops expr_ops_tunnel;
+extern struct expr_ops expr_ops_osf;
+extern struct expr_ops expr_ops_xfrm;
+
+static struct expr_ops expr_ops_notrack = {
+ .name = "notrack",
+};
+
+static struct expr_ops *expr_ops[] = {
+ &expr_ops_bitwise,
+ &expr_ops_byteorder,
+ &expr_ops_cmp,
+ &expr_ops_connlimit,
+ &expr_ops_counter,
+ &expr_ops_ct,
+ &expr_ops_dup,
+ &expr_ops_exthdr,
+ &expr_ops_fwd,
+ &expr_ops_immediate,
+ &expr_ops_inner,
+ &expr_ops_last,
+ &expr_ops_limit,
+ &expr_ops_log,
+ &expr_ops_lookup,
+ &expr_ops_masq,
+ &expr_ops_match,
+ &expr_ops_meta,
+ &expr_ops_ng,
+ &expr_ops_nat,
+ &expr_ops_tproxy,
+ &expr_ops_notrack,
+ &expr_ops_payload,
+ &expr_ops_range,
+ &expr_ops_redir,
+ &expr_ops_reject,
+ &expr_ops_rt,
+ &expr_ops_queue,
+ &expr_ops_quota,
+ &expr_ops_target,
+ &expr_ops_dynset,
+ &expr_ops_hash,
+ &expr_ops_fib,
+ &expr_ops_objref,
+ &expr_ops_flow,
+ &expr_ops_socket,
+ &expr_ops_synproxy,
+ &expr_ops_tunnel,
+ &expr_ops_osf,
+ &expr_ops_xfrm,
+ NULL,
+};
+
+struct expr_ops *nftnl_expr_ops_lookup(const char *name)
+{
+ int i = 0;
+
+ while (expr_ops[i] != NULL) {
+ if (strcmp(expr_ops[i]->name, name) == 0)
+ return expr_ops[i];
+
+ i++;
+ }
+ return NULL;
+}
diff --git a/src/flowtable.c b/src/flowtable.c
new file mode 100644
index 0000000..e6c2475
--- /dev/null
+++ b/src/flowtable.c
@@ -0,0 +1,735 @@
+#include "internal.h"
+
+#include <time.h>
+#include <endian.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_arp.h>
+
+#include <libnftnl/flowtable.h>
+
+struct nftnl_flowtable {
+ struct list_head head;
+ const char *name;
+ const char *table;
+ int family;
+ uint32_t hooknum;
+ int32_t prio;
+ uint32_t size;
+ const char **dev_array;
+ uint32_t dev_array_len;
+ uint32_t ft_flags;
+ uint32_t use;
+ uint32_t flags;
+ uint64_t handle;
+};
+
+EXPORT_SYMBOL(nftnl_flowtable_alloc);
+struct nftnl_flowtable *nftnl_flowtable_alloc(void)
+{
+ return calloc(1, sizeof(struct nftnl_flowtable));
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_free);
+void nftnl_flowtable_free(const struct nftnl_flowtable *c)
+{
+ int i;
+
+ if (c->flags & (1 << NFTNL_FLOWTABLE_NAME))
+ xfree(c->name);
+ if (c->flags & (1 << NFTNL_FLOWTABLE_TABLE))
+ xfree(c->table);
+ if (c->flags & (1 << NFTNL_FLOWTABLE_DEVICES)) {
+ for (i = 0; i < c->dev_array_len; i++)
+ xfree(c->dev_array[i]);
+
+ xfree(c->dev_array);
+ }
+ xfree(c);
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_is_set);
+bool nftnl_flowtable_is_set(const struct nftnl_flowtable *c, uint16_t attr)
+{
+ return c->flags & (1 << attr);
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_unset);
+void nftnl_flowtable_unset(struct nftnl_flowtable *c, uint16_t attr)
+{
+ int i;
+
+ if (!(c->flags & (1 << attr)))
+ return;
+
+ switch (attr) {
+ case NFTNL_FLOWTABLE_NAME:
+ xfree(c->name);
+ break;
+ case NFTNL_FLOWTABLE_TABLE:
+ xfree(c->table);
+ break;
+ case NFTNL_FLOWTABLE_HOOKNUM:
+ case NFTNL_FLOWTABLE_PRIO:
+ case NFTNL_FLOWTABLE_USE:
+ case NFTNL_FLOWTABLE_FAMILY:
+ case NFTNL_FLOWTABLE_FLAGS:
+ case NFTNL_FLOWTABLE_HANDLE:
+ break;
+ case NFTNL_FLOWTABLE_DEVICES:
+ for (i = 0; i < c->dev_array_len; i++)
+ xfree(c->dev_array[i]);
+ xfree(c->dev_array);
+ break;
+ default:
+ return;
+ }
+
+ c->flags &= ~(1 << attr);
+}
+
+static uint32_t nftnl_flowtable_validate[NFTNL_FLOWTABLE_MAX + 1] = {
+ [NFTNL_FLOWTABLE_HOOKNUM] = sizeof(uint32_t),
+ [NFTNL_FLOWTABLE_PRIO] = sizeof(int32_t),
+ [NFTNL_FLOWTABLE_FAMILY] = sizeof(uint32_t),
+ [NFTNL_FLOWTABLE_FLAGS] = sizeof(uint32_t),
+ [NFTNL_FLOWTABLE_HANDLE] = sizeof(uint64_t),
+};
+
+EXPORT_SYMBOL(nftnl_flowtable_set_data);
+int nftnl_flowtable_set_data(struct nftnl_flowtable *c, uint16_t attr,
+ const void *data, uint32_t data_len)
+{
+ const char **dev_array;
+ int len = 0, i;
+
+ nftnl_assert_attr_exists(attr, NFTNL_FLOWTABLE_MAX);
+ nftnl_assert_validate(data, nftnl_flowtable_validate, attr, data_len);
+
+ switch(attr) {
+ case NFTNL_FLOWTABLE_NAME:
+ if (c->flags & (1 << NFTNL_FLOWTABLE_NAME))
+ xfree(c->name);
+
+ c->name = strdup(data);
+ if (!c->name)
+ return -1;
+ break;
+ case NFTNL_FLOWTABLE_TABLE:
+ if (c->flags & (1 << NFTNL_FLOWTABLE_TABLE))
+ xfree(c->table);
+
+ c->table = strdup(data);
+ if (!c->table)
+ return -1;
+ break;
+ case NFTNL_FLOWTABLE_HOOKNUM:
+ memcpy(&c->hooknum, data, sizeof(c->hooknum));
+ break;
+ case NFTNL_FLOWTABLE_PRIO:
+ memcpy(&c->prio, data, sizeof(c->prio));
+ break;
+ case NFTNL_FLOWTABLE_FAMILY:
+ memcpy(&c->family, data, sizeof(c->family));
+ break;
+ case NFTNL_FLOWTABLE_DEVICES:
+ dev_array = (const char **)data;
+ while (dev_array[len] != NULL)
+ len++;
+
+ if (c->flags & (1 << NFTNL_FLOWTABLE_DEVICES)) {
+ for (i = 0; i < c->dev_array_len; i++)
+ xfree(c->dev_array[i]);
+ xfree(c->dev_array);
+ }
+
+ c->dev_array = calloc(len + 1, sizeof(char *));
+ if (!c->dev_array)
+ return -1;
+
+ for (i = 0; i < len; i++)
+ c->dev_array[i] = strdup(dev_array[i]);
+
+ c->dev_array_len = len;
+ break;
+ case NFTNL_FLOWTABLE_SIZE:
+ memcpy(&c->size, data, sizeof(c->size));
+ break;
+ case NFTNL_FLOWTABLE_FLAGS:
+ memcpy(&c->ft_flags, data, sizeof(c->ft_flags));
+ break;
+ case NFTNL_FLOWTABLE_HANDLE:
+ memcpy(&c->handle, data, sizeof(c->handle));
+ break;
+ }
+ c->flags |= (1 << attr);
+ return 0;
+}
+
+void nftnl_flowtable_set(struct nftnl_flowtable *c, uint16_t attr, const void *data) __visible;
+void nftnl_flowtable_set(struct nftnl_flowtable *c, uint16_t attr, const void *data)
+{
+ nftnl_flowtable_set_data(c, attr, data, nftnl_flowtable_validate[attr]);
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_set_u32);
+void nftnl_flowtable_set_u32(struct nftnl_flowtable *c, uint16_t attr, uint32_t data)
+{
+ nftnl_flowtable_set_data(c, attr, &data, sizeof(uint32_t));
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_set_s32);
+void nftnl_flowtable_set_s32(struct nftnl_flowtable *c, uint16_t attr, int32_t data)
+{
+ nftnl_flowtable_set_data(c, attr, &data, sizeof(int32_t));
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_set_str);
+int nftnl_flowtable_set_str(struct nftnl_flowtable *c, uint16_t attr, const char *str)
+{
+ return nftnl_flowtable_set_data(c, attr, str, strlen(str) + 1);
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_set_u64);
+void nftnl_flowtable_set_u64(struct nftnl_flowtable *c, uint16_t attr, uint64_t data)
+{
+ nftnl_flowtable_set_data(c, attr, &data, sizeof(uint64_t));
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_set_array);
+int nftnl_flowtable_set_array(struct nftnl_flowtable *c, uint16_t attr,
+ const char **data)
+{
+ return nftnl_flowtable_set_data(c, attr, data, 0);
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_get_data);
+const void *nftnl_flowtable_get_data(const struct nftnl_flowtable *c,
+ uint16_t attr, uint32_t *data_len)
+{
+ if (!(c->flags & (1 << attr)))
+ return NULL;
+
+ switch(attr) {
+ case NFTNL_FLOWTABLE_NAME:
+ *data_len = strlen(c->name) + 1;
+ return c->name;
+ case NFTNL_FLOWTABLE_TABLE:
+ *data_len = strlen(c->table) + 1;
+ return c->table;
+ case NFTNL_FLOWTABLE_HOOKNUM:
+ *data_len = sizeof(uint32_t);
+ return &c->hooknum;
+ case NFTNL_FLOWTABLE_PRIO:
+ *data_len = sizeof(int32_t);
+ return &c->prio;
+ case NFTNL_FLOWTABLE_FAMILY:
+ *data_len = sizeof(int32_t);
+ return &c->family;
+ case NFTNL_FLOWTABLE_DEVICES:
+ *data_len = 0;
+ return &c->dev_array[0];
+ case NFTNL_FLOWTABLE_SIZE:
+ *data_len = sizeof(int32_t);
+ return &c->size;
+ case NFTNL_FLOWTABLE_FLAGS:
+ *data_len = sizeof(int32_t);
+ return &c->ft_flags;
+ case NFTNL_FLOWTABLE_HANDLE:
+ *data_len = sizeof(uint64_t);
+ return &c->handle;
+ }
+ return NULL;
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_get);
+const void *nftnl_flowtable_get(const struct nftnl_flowtable *c, uint16_t attr)
+{
+ uint32_t data_len;
+ return nftnl_flowtable_get_data(c, attr, &data_len);
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_get_str);
+const char *nftnl_flowtable_get_str(const struct nftnl_flowtable *c, uint16_t attr)
+{
+ return nftnl_flowtable_get(c, attr);
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_get_u32);
+uint32_t nftnl_flowtable_get_u32(const struct nftnl_flowtable *c, uint16_t attr)
+{
+ uint32_t data_len = 0;
+ const uint32_t *val = nftnl_flowtable_get_data(c, attr, &data_len);
+
+ nftnl_assert(val, attr, data_len == sizeof(uint32_t));
+
+ return val ? *val : 0;
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_get_u64);
+uint64_t nftnl_flowtable_get_u64(const struct nftnl_flowtable *c, uint16_t attr)
+{
+ uint32_t data_len = 0;
+ const uint64_t *val = nftnl_flowtable_get_data(c, attr, &data_len);
+
+ nftnl_assert(val, attr, data_len == sizeof(uint64_t));
+
+ return val ? *val : 0;
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_get_s32);
+int32_t nftnl_flowtable_get_s32(const struct nftnl_flowtable *c, uint16_t attr)
+{
+ uint32_t data_len = 0;
+ const int32_t *val = nftnl_flowtable_get_data(c, attr, &data_len);
+
+ nftnl_assert(val, attr, data_len == sizeof(int32_t));
+
+ return val ? *val : 0;
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_get_array);
+const char *const *nftnl_flowtable_get_array(const struct nftnl_flowtable *c, uint16_t attr)
+{
+ uint32_t data_len;
+ const char * const *val = nftnl_flowtable_get_data(c, attr, &data_len);
+
+ nftnl_assert(val, attr, attr == NFTNL_FLOWTABLE_DEVICES);
+
+ return val;
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_nlmsg_build_payload);
+void nftnl_flowtable_nlmsg_build_payload(struct nlmsghdr *nlh,
+ const struct nftnl_flowtable *c)
+{
+ struct nlattr *nest = NULL;
+ int i;
+
+ if (c->flags & (1 << NFTNL_FLOWTABLE_TABLE))
+ mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_TABLE, c->table);
+ if (c->flags & (1 << NFTNL_FLOWTABLE_NAME))
+ mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_NAME, c->name);
+
+ if (c->flags & (1 << NFTNL_FLOWTABLE_HOOKNUM) ||
+ c->flags & (1 << NFTNL_FLOWTABLE_PRIO) ||
+ c->flags & (1 << NFTNL_FLOWTABLE_DEVICES))
+ nest = mnl_attr_nest_start(nlh, NFTA_FLOWTABLE_HOOK);
+
+ if (c->flags & (1 << NFTNL_FLOWTABLE_HOOKNUM))
+ mnl_attr_put_u32(nlh, NFTA_FLOWTABLE_HOOK_NUM, htonl(c->hooknum));
+ if (c->flags & (1 << NFTNL_FLOWTABLE_PRIO))
+ mnl_attr_put_u32(nlh, NFTA_FLOWTABLE_HOOK_PRIORITY, htonl(c->prio));
+
+ if (c->flags & (1 << NFTNL_FLOWTABLE_DEVICES)) {
+ struct nlattr *nest_dev;
+
+ nest_dev = mnl_attr_nest_start(nlh, NFTA_FLOWTABLE_HOOK_DEVS);
+ for (i = 0; i < c->dev_array_len; i++) {
+ mnl_attr_put_strz(nlh, NFTA_DEVICE_NAME,
+ c->dev_array[i]);
+ }
+ mnl_attr_nest_end(nlh, nest_dev);
+ }
+
+ if (nest)
+ mnl_attr_nest_end(nlh, nest);
+
+ if (c->flags & (1 << NFTNL_FLOWTABLE_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_FLOWTABLE_FLAGS, htonl(c->ft_flags));
+ if (c->flags & (1 << NFTNL_FLOWTABLE_USE))
+ mnl_attr_put_u32(nlh, NFTA_FLOWTABLE_USE, htonl(c->use));
+ if (c->flags & (1 << NFTNL_FLOWTABLE_HANDLE))
+ mnl_attr_put_u64(nlh, NFTA_FLOWTABLE_HANDLE, htobe64(c->handle));
+}
+
+static int nftnl_flowtable_parse_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_FLOWTABLE_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_FLOWTABLE_NAME:
+ case NFTA_FLOWTABLE_TABLE:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ case NFTA_FLOWTABLE_HOOK:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ abi_breakage();
+ break;
+ case NFTA_FLOWTABLE_FLAGS:
+ case NFTA_FLOWTABLE_USE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_FLOWTABLE_HANDLE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int nftnl_flowtable_parse_hook_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_FLOWTABLE_HOOK_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_FLOWTABLE_HOOK_NUM:
+ case NFTA_FLOWTABLE_HOOK_PRIORITY:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_FLOWTABLE_HOOK_DEVS:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int nftnl_flowtable_parse_devs(struct nlattr *nest,
+ struct nftnl_flowtable *c)
+{
+ const char **dev_array, **tmp;
+ int len = 0, size = 8;
+ struct nlattr *attr;
+
+ dev_array = calloc(8, sizeof(char *));
+ if (!dev_array)
+ return -1;
+
+ mnl_attr_for_each_nested(attr, nest) {
+ if (mnl_attr_get_type(attr) != NFTA_DEVICE_NAME)
+ goto err;
+ dev_array[len++] = strdup(mnl_attr_get_str(attr));
+ if (len >= size) {
+ tmp = realloc(dev_array, size * 2 * sizeof(char *));
+ if (!tmp)
+ goto err;
+
+ size *= 2;
+ memset(&tmp[len], 0, (size - len) * sizeof(char *));
+ dev_array = tmp;
+ }
+ }
+
+ c->dev_array = dev_array;
+ c->dev_array_len = len;
+
+ return 0;
+err:
+ while (len--)
+ xfree(dev_array[len]);
+ xfree(dev_array);
+ return -1;
+}
+
+static int nftnl_flowtable_parse_hook(struct nlattr *attr, struct nftnl_flowtable *c)
+{
+ struct nlattr *tb[NFTA_FLOWTABLE_HOOK_MAX + 1] = {};
+ int ret;
+
+ if (mnl_attr_parse_nested(attr, nftnl_flowtable_parse_hook_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_FLOWTABLE_HOOK_NUM]) {
+ c->hooknum = ntohl(mnl_attr_get_u32(tb[NFTA_FLOWTABLE_HOOK_NUM]));
+ c->flags |= (1 << NFTNL_FLOWTABLE_HOOKNUM);
+ }
+ if (tb[NFTA_FLOWTABLE_HOOK_PRIORITY]) {
+ c->prio = ntohl(mnl_attr_get_u32(tb[NFTA_FLOWTABLE_HOOK_PRIORITY]));
+ c->flags |= (1 << NFTNL_FLOWTABLE_PRIO);
+ }
+ if (tb[NFTA_FLOWTABLE_HOOK_DEVS]) {
+ ret = nftnl_flowtable_parse_devs(tb[NFTA_FLOWTABLE_HOOK_DEVS], c);
+ if (ret < 0)
+ return -1;
+ c->flags |= (1 << NFTNL_FLOWTABLE_DEVICES);
+ }
+
+ return 0;
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_nlmsg_parse);
+int nftnl_flowtable_nlmsg_parse(const struct nlmsghdr *nlh, struct nftnl_flowtable *c)
+{
+ struct nlattr *tb[NFTA_FLOWTABLE_MAX + 1] = {};
+ struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
+ int ret = 0;
+
+ if (mnl_attr_parse(nlh, sizeof(*nfg), nftnl_flowtable_parse_attr_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_FLOWTABLE_NAME]) {
+ if (c->flags & (1 << NFTNL_FLOWTABLE_NAME))
+ xfree(c->name);
+ c->name = strdup(mnl_attr_get_str(tb[NFTA_FLOWTABLE_NAME]));
+ if (!c->name)
+ return -1;
+ c->flags |= (1 << NFTNL_FLOWTABLE_NAME);
+ }
+ if (tb[NFTA_FLOWTABLE_TABLE]) {
+ if (c->flags & (1 << NFTNL_FLOWTABLE_TABLE))
+ xfree(c->table);
+ c->table = strdup(mnl_attr_get_str(tb[NFTA_FLOWTABLE_TABLE]));
+ if (!c->table)
+ return -1;
+ c->flags |= (1 << NFTNL_FLOWTABLE_TABLE);
+ }
+ if (tb[NFTA_FLOWTABLE_HOOK]) {
+ ret = nftnl_flowtable_parse_hook(tb[NFTA_FLOWTABLE_HOOK], c);
+ if (ret < 0)
+ return ret;
+ }
+ if (tb[NFTA_FLOWTABLE_FLAGS]) {
+ c->ft_flags = ntohl(mnl_attr_get_u32(tb[NFTA_FLOWTABLE_FLAGS]));
+ c->flags |= (1 << NFTNL_FLOWTABLE_FLAGS);
+ }
+ if (tb[NFTA_FLOWTABLE_USE]) {
+ c->use = ntohl(mnl_attr_get_u32(tb[NFTA_FLOWTABLE_USE]));
+ c->flags |= (1 << NFTNL_FLOWTABLE_USE);
+ }
+ if (tb[NFTA_FLOWTABLE_HANDLE]) {
+ c->handle = be64toh(mnl_attr_get_u64(tb[NFTA_FLOWTABLE_HANDLE]));
+ c->flags |= (1 << NFTNL_FLOWTABLE_HANDLE);
+ }
+
+ c->family = nfg->nfgen_family;
+ c->flags |= (1 << NFTNL_FLOWTABLE_FAMILY);
+
+ return ret;
+}
+
+static const char *nftnl_hooknum2str(int family, int hooknum)
+{
+ switch (family) {
+ case NFPROTO_IPV4:
+ case NFPROTO_IPV6:
+ case NFPROTO_INET:
+ case NFPROTO_BRIDGE:
+ switch (hooknum) {
+ case NF_INET_PRE_ROUTING:
+ return "prerouting";
+ case NF_INET_LOCAL_IN:
+ return "input";
+ case NF_INET_FORWARD:
+ return "forward";
+ case NF_INET_LOCAL_OUT:
+ return "output";
+ case NF_INET_POST_ROUTING:
+ return "postrouting";
+ }
+ break;
+ case NFPROTO_ARP:
+ switch (hooknum) {
+ case NF_ARP_IN:
+ return "input";
+ case NF_ARP_OUT:
+ return "output";
+ case NF_ARP_FORWARD:
+ return "forward";
+ }
+ break;
+ case NFPROTO_NETDEV:
+ switch (hooknum) {
+ case NF_NETDEV_INGRESS:
+ return "ingress";
+ }
+ break;
+ }
+ return "unknown";
+}
+
+static inline int nftnl_str2hooknum(int family, const char *hook)
+{
+ int hooknum;
+
+ for (hooknum = 0; hooknum < NF_INET_NUMHOOKS; hooknum++) {
+ if (strcmp(hook, nftnl_hooknum2str(family, hooknum)) == 0)
+ return hooknum;
+ }
+ return -1;
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_parse);
+int nftnl_flowtable_parse(struct nftnl_flowtable *c, enum nftnl_parse_type type,
+ const char *data, struct nftnl_parse_err *err)
+{
+ errno = EOPNOTSUPP;
+ return -1;
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_parse_file);
+int nftnl_flowtable_parse_file(struct nftnl_flowtable *c,
+ enum nftnl_parse_type type,
+ FILE *fp, struct nftnl_parse_err *err)
+{
+ errno = EOPNOTSUPP;
+ return -1;
+}
+
+static int nftnl_flowtable_snprintf_default(char *buf, size_t remain,
+ const struct nftnl_flowtable *c)
+{
+ int ret, offset = 0, i;
+
+ ret = snprintf(buf, remain, "flow table %s %s use %u size %u flags %x",
+ c->table, c->name, c->use, c->size, c->ft_flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (c->flags & (1 << NFTNL_FLOWTABLE_HOOKNUM)) {
+ ret = snprintf(buf + offset, remain, " hook %s prio %d ",
+ nftnl_hooknum2str(c->family, c->hooknum),
+ c->prio);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (c->flags & (1 << NFTNL_FLOWTABLE_DEVICES)) {
+ ret = snprintf(buf + offset, remain, " dev { ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ for (i = 0; i < c->dev_array_len; i++) {
+ ret = snprintf(buf + offset, remain, " %s ",
+ c->dev_array[i]);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ ret = snprintf(buf + offset, remain, " } ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ }
+
+ return offset;
+}
+
+static int nftnl_flowtable_cmd_snprintf(char *buf, size_t remain,
+ const struct nftnl_flowtable *c,
+ uint32_t cmd, uint32_t type,
+ uint32_t flags)
+{
+ int ret, offset = 0;
+
+ if (type != NFTNL_OUTPUT_DEFAULT)
+ return -1;
+
+ ret = nftnl_flowtable_snprintf_default(buf + offset, remain, c);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ return offset;
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_snprintf);
+int nftnl_flowtable_snprintf(char *buf, size_t size, const struct nftnl_flowtable *c,
+ uint32_t type, uint32_t flags)
+{
+ if (size)
+ buf[0] = '\0';
+
+ return nftnl_flowtable_cmd_snprintf(buf, size, c, nftnl_flag2cmd(flags),
+ type, flags);
+}
+
+static int nftnl_flowtable_do_snprintf(char *buf, size_t size, const void *c,
+ uint32_t cmd, uint32_t type, uint32_t flags)
+{
+ return nftnl_flowtable_snprintf(buf, size, c, type, flags);
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_fprintf);
+int nftnl_flowtable_fprintf(FILE *fp, const struct nftnl_flowtable *c,
+ uint32_t type, uint32_t flags)
+{
+ return nftnl_fprintf(fp, c, NFTNL_CMD_UNSPEC, type, flags,
+ nftnl_flowtable_do_snprintf);
+}
+
+struct nftnl_flowtable_list {
+ struct list_head list;
+};
+
+EXPORT_SYMBOL(nftnl_flowtable_list_alloc);
+struct nftnl_flowtable_list *nftnl_flowtable_list_alloc(void)
+{
+ struct nftnl_flowtable_list *list;
+
+ list = calloc(1, sizeof(struct nftnl_flowtable_list));
+ if (list == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&list->list);
+
+ return list;
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_list_free);
+void nftnl_flowtable_list_free(struct nftnl_flowtable_list *list)
+{
+ struct nftnl_flowtable *s, *tmp;
+
+ list_for_each_entry_safe(s, tmp, &list->list, head) {
+ list_del(&s->head);
+ nftnl_flowtable_free(s);
+ }
+ xfree(list);
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_list_is_empty);
+int nftnl_flowtable_list_is_empty(const struct nftnl_flowtable_list *list)
+{
+ return list_empty(&list->list);
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_list_add);
+void nftnl_flowtable_list_add(struct nftnl_flowtable *s,
+ struct nftnl_flowtable_list *list)
+{
+ list_add(&s->head, &list->list);
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_list_add_tail);
+void nftnl_flowtable_list_add_tail(struct nftnl_flowtable *s,
+ struct nftnl_flowtable_list *list)
+{
+ list_add_tail(&s->head, &list->list);
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_list_del);
+void nftnl_flowtable_list_del(struct nftnl_flowtable *s)
+{
+ list_del(&s->head);
+}
+
+EXPORT_SYMBOL(nftnl_flowtable_list_foreach);
+int nftnl_flowtable_list_foreach(struct nftnl_flowtable_list *flowtable_list,
+ int (*cb)(struct nftnl_flowtable *t, void *data), void *data)
+{
+ struct nftnl_flowtable *cur, *tmp;
+ int ret;
+
+ list_for_each_entry_safe(cur, tmp, &flowtable_list->list, head) {
+ ret = cb(cur, data);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
diff --git a/src/gen.c b/src/gen.c
new file mode 100644
index 0000000..88efbaa
--- /dev/null
+++ b/src/gen.c
@@ -0,0 +1,196 @@
+/*
+ * (C) 2012-2014 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 "internal.h"
+
+#include <time.h>
+#include <endian.h>
+#include <stdint.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <errno.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftnl/gen.h>
+
+struct nftnl_gen {
+ uint32_t id;
+
+ uint32_t flags;
+};
+
+EXPORT_SYMBOL(nftnl_gen_alloc);
+struct nftnl_gen *nftnl_gen_alloc(void)
+{
+ return calloc(1, sizeof(struct nftnl_gen));
+}
+
+EXPORT_SYMBOL(nftnl_gen_free);
+void nftnl_gen_free(const struct nftnl_gen *gen)
+{
+ xfree(gen);
+}
+
+EXPORT_SYMBOL(nftnl_gen_is_set);
+bool nftnl_gen_is_set(const struct nftnl_gen *gen, uint16_t attr)
+{
+ return gen->flags & (1 << attr);
+}
+
+EXPORT_SYMBOL(nftnl_gen_unset);
+void nftnl_gen_unset(struct nftnl_gen *gen, uint16_t attr)
+{
+ if (!(gen->flags & (1 << attr)))
+ return;
+
+ switch (attr) {
+ case NFTNL_GEN_ID:
+ break;
+ }
+ gen->flags &= ~(1 << attr);
+}
+
+static uint32_t nftnl_gen_validate[NFTNL_GEN_MAX + 1] = {
+ [NFTNL_GEN_ID] = sizeof(uint32_t),
+};
+
+EXPORT_SYMBOL(nftnl_gen_set_data);
+int nftnl_gen_set_data(struct nftnl_gen *gen, uint16_t attr,
+ const void *data, uint32_t data_len)
+{
+ nftnl_assert_attr_exists(attr, NFTNL_GEN_MAX);
+ nftnl_assert_validate(data, nftnl_gen_validate, attr, data_len);
+
+ switch (attr) {
+ case NFTNL_GEN_ID:
+ memcpy(&gen->id, data, sizeof(gen->id));
+ break;
+ }
+ gen->flags |= (1 << attr);
+ return 0;
+}
+
+int nftnl_gen_set(struct nftnl_gen *gen, uint16_t attr, const void *data) __visible;
+int nftnl_gen_set(struct nftnl_gen *gen, uint16_t attr, const void *data)
+{
+ return nftnl_gen_set_data(gen, attr, data, nftnl_gen_validate[attr]);
+}
+
+EXPORT_SYMBOL(nftnl_gen_set_u32);
+void nftnl_gen_set_u32(struct nftnl_gen *gen, uint16_t attr, uint32_t val)
+{
+ nftnl_gen_set_data(gen, attr, &val, sizeof(uint32_t));
+}
+
+EXPORT_SYMBOL(nftnl_gen_get_data);
+const void *nftnl_gen_get_data(const struct nftnl_gen *gen, uint16_t attr,
+ uint32_t *data_len)
+{
+ if (!(gen->flags & (1 << attr)))
+ return NULL;
+
+ switch(attr) {
+ case NFTNL_GEN_ID:
+ *data_len = sizeof(gen->id);
+ return &gen->id;
+ }
+ return NULL;
+}
+
+EXPORT_SYMBOL(nftnl_gen_get);
+const void *nftnl_gen_get(const struct nftnl_gen *gen, uint16_t attr)
+{
+ uint32_t data_len;
+ return nftnl_gen_get_data(gen, attr, &data_len);
+}
+
+EXPORT_SYMBOL(nftnl_gen_get_u32);
+uint32_t nftnl_gen_get_u32(const struct nftnl_gen *gen, uint16_t attr)
+{
+ const void *ret = nftnl_gen_get(gen, attr);
+ return ret == NULL ? 0 : *((uint32_t *)ret);
+}
+
+static int nftnl_gen_parse_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_GEN_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_GEN_ID:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+EXPORT_SYMBOL(nftnl_gen_nlmsg_parse);
+int nftnl_gen_nlmsg_parse(const struct nlmsghdr *nlh, struct nftnl_gen *gen)
+{
+ struct nlattr *tb[NFTA_GEN_MAX + 1] = {};
+
+ if (mnl_attr_parse(nlh, sizeof(struct nfgenmsg),
+ nftnl_gen_parse_attr_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_GEN_ID]) {
+ gen->id = ntohl(mnl_attr_get_u32(tb[NFTA_GEN_ID]));
+ gen->flags |= (1 << NFTNL_GEN_ID);
+ }
+ return 0;
+}
+
+static int nftnl_gen_cmd_snprintf(char *buf, size_t remain,
+ const struct nftnl_gen *gen, uint32_t cmd,
+ uint32_t type, uint32_t flags)
+{
+ int ret, offset = 0;
+
+ if (type != NFTNL_OUTPUT_DEFAULT)
+ return -1;
+
+ ret = snprintf(buf, remain, "ruleset generation ID %u", gen->id);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ return offset;
+}
+
+EXPORT_SYMBOL(nftnl_gen_snprintf);
+int nftnl_gen_snprintf(char *buf, size_t size, const struct nftnl_gen *gen,
+ uint32_t type, uint32_t flags)
+{
+ if (size)
+ buf[0] = '\0';
+
+ return nftnl_gen_cmd_snprintf(buf, size, gen, nftnl_flag2cmd(flags), type,
+ flags);
+}
+
+static int nftnl_gen_do_snprintf(char *buf, size_t size, const void *gen,
+ uint32_t cmd, uint32_t type, uint32_t flags)
+{
+ return nftnl_gen_snprintf(buf, size, gen, type, flags);
+}
+
+EXPORT_SYMBOL(nftnl_gen_fprintf);
+int nftnl_gen_fprintf(FILE *fp, const struct nftnl_gen *gen, uint32_t type,
+ uint32_t flags)
+{
+ return nftnl_fprintf(fp, gen, NFTNL_CMD_UNSPEC, type, flags,
+ nftnl_gen_do_snprintf);
+}
diff --git a/src/libnftnl.map b/src/libnftnl.map
new file mode 100644
index 0000000..ad8f2af
--- /dev/null
+++ b/src/libnftnl.map
@@ -0,0 +1,389 @@
+LIBNFTNL_11 {
+global:
+ nftnl_table_alloc;
+ nftnl_table_free;
+ nftnl_table_is_set;
+ nftnl_table_unset;
+ nftnl_table_set;
+ nftnl_table_get;
+ nftnl_table_set_u8;
+ nftnl_table_set_u32;
+ nftnl_table_set_u64;
+ nftnl_table_set_str;
+ nftnl_table_get_u8;
+ nftnl_table_get_u32;
+ nftnl_table_get_u64;
+ nftnl_table_get_str;
+ nftnl_table_parse;
+ nftnl_table_parse_file;
+ nftnl_table_snprintf;
+ nftnl_table_fprintf;
+ nftnl_table_nlmsg_build_payload;
+ nftnl_table_nlmsg_parse;
+ nftnl_table_list_alloc;
+ nftnl_table_list_free;
+ nftnl_table_list_is_empty;
+ nftnl_table_list_foreach;
+ nftnl_table_list_add;
+ nftnl_table_list_add_tail;
+ nftnl_table_list_del;
+ nftnl_table_list_iter_create;
+ nftnl_table_list_iter_next;
+ nftnl_table_list_iter_destroy;
+
+ nftnl_chain_alloc;
+ nftnl_chain_free;
+ nftnl_chain_is_set;
+ nftnl_chain_unset;
+ nftnl_chain_set;
+ nftnl_chain_set_u8;
+ nftnl_chain_set_u32;
+ nftnl_chain_set_s32;
+ nftnl_chain_set_u64;
+ nftnl_chain_set_str;
+ nftnl_chain_get;
+ nftnl_chain_get_u8;
+ nftnl_chain_get_u32;
+ nftnl_chain_get_s32;
+ nftnl_chain_get_u64;
+ nftnl_chain_get_str;
+ nftnl_chain_parse;
+ nftnl_chain_parse_file;
+ nftnl_chain_snprintf;
+ nftnl_chain_fprintf;
+ nftnl_chain_nlmsg_build_payload;
+ nftnl_chain_nlmsg_parse;
+ nftnl_chain_list_alloc;
+ nftnl_chain_list_free;
+ nftnl_chain_list_is_empty;
+ nftnl_chain_list_add;
+ nftnl_chain_list_add_tail;
+ nftnl_chain_list_del;
+ nftnl_chain_list_foreach;
+ nftnl_chain_list_iter_create;
+ nftnl_chain_list_iter_next;
+ nftnl_chain_list_iter_destroy;
+
+ nftnl_rule_alloc;
+ nftnl_rule_free;
+ nftnl_rule_is_set;
+ nftnl_rule_unset;
+ nftnl_rule_set;
+ nftnl_rule_set_u32;
+ nftnl_rule_set_u64;
+ nftnl_rule_set_str;
+ nftnl_rule_get;
+ nftnl_rule_get_u8;
+ nftnl_rule_get_u32;
+ nftnl_rule_get_u64;
+ nftnl_rule_get_str;
+ nftnl_rule_parse;
+ nftnl_rule_parse_file;
+ nftnl_rule_snprintf;
+ nftnl_rule_fprintf;
+ nftnl_rule_nlmsg_build_payload;
+ nftnl_rule_nlmsg_parse;
+ nftnl_rule_add_expr;
+
+ nftnl_expr_foreach;
+ nftnl_expr_iter_create;
+ nftnl_expr_iter_next;
+ nftnl_expr_iter_destroy;
+
+ nftnl_expr_alloc;
+ nftnl_expr_is_set;
+ nftnl_expr_set;
+ nftnl_expr_set_u8;
+ nftnl_expr_set_u16;
+ nftnl_expr_set_u32;
+ nftnl_expr_set_u64;
+ nftnl_expr_set_str;
+ nftnl_expr_get;
+ nftnl_expr_get_u8;
+ nftnl_expr_get_u16;
+ nftnl_expr_get_u32;
+ nftnl_expr_get_u64;
+ nftnl_expr_get_str;
+ nftnl_expr_snprintf;
+ nftnl_expr_free;
+
+ nftnl_rule_list_alloc;
+ nftnl_rule_list_free;
+ nftnl_rule_list_is_empty;
+ nftnl_rule_list_add;
+ nftnl_rule_list_add_tail;
+ nftnl_rule_list_del;
+ nftnl_rule_list_foreach;
+ nftnl_rule_list_iter_create;
+ nftnl_rule_list_iter_cur;
+ nftnl_rule_list_iter_next;
+ nftnl_rule_list_iter_destroy;
+
+ nftnl_set_alloc;
+ nftnl_set_free;
+ nftnl_set_unset;
+ nftnl_set_is_set;
+ nftnl_set_set;
+ nftnl_set_set_u32;
+ nftnl_set_set_u64;
+ nftnl_set_set_str;
+ nftnl_set_get;
+ nftnl_set_get_str;
+ nftnl_set_get_u32;
+ nftnl_set_get_u64;
+ nftnl_set_nlmsg_build_payload;
+ nftnl_set_nlmsg_parse;
+ nftnl_set_parse;
+ nftnl_set_parse_file;
+ nftnl_set_snprintf;
+ nftnl_set_fprintf;
+
+ nftnl_set_list_alloc;
+ nftnl_set_list_free;
+ nftnl_set_list_add;
+ nftnl_set_list_add_tail;
+ nftnl_set_list_del;
+ nftnl_set_list_is_empty;
+ nftnl_set_list_foreach;
+
+ nftnl_set_list_iter_create;
+ nftnl_set_list_iter_cur;
+ nftnl_set_list_iter_next;
+ nftnl_set_list_iter_destroy;
+
+ nftnl_set_elem_alloc;
+ nftnl_set_elem_free;
+ nftnl_set_elem_add;
+ nftnl_set_elem_foreach;
+ nftnl_set_elem_is_set;
+ nftnl_set_elem_unset;
+ nftnl_set_elem_set;
+ nftnl_set_elem_set_u32;
+ nftnl_set_elem_set_u64;
+ nftnl_set_elem_set_str;
+ nftnl_set_elem_get;
+ nftnl_set_elem_get_str;
+ nftnl_set_elem_get_u32;
+ nftnl_set_elem_get_u64;
+ nftnl_set_elem_nlmsg_build_payload;
+ nftnl_set_elem_parse;
+ nftnl_set_elem_parse_file;
+ nftnl_set_elem_snprintf;
+ nftnl_set_elem_fprintf;
+
+ nftnl_set_elems_nlmsg_build_payload;
+ nftnl_set_elems_nlmsg_parse;
+
+ nftnl_set_elems_foreach;
+
+ nftnl_set_elems_iter_create;
+ nftnl_set_elems_iter_cur;
+ nftnl_set_elems_iter_next;
+ nftnl_set_elems_iter_destroy;
+
+ nftnl_ruleset_alloc;
+ nftnl_ruleset_free;
+ nftnl_ruleset_is_set;
+ nftnl_ruleset_unset;
+ nftnl_ruleset_set;
+ nftnl_ruleset_get;
+ nftnl_ruleset_parse;
+ nftnl_ruleset_parse_file;
+ nftnl_ruleset_snprintf;
+ nftnl_ruleset_fprintf;
+
+ nftnl_nlmsg_build_hdr;
+
+ nftnl_parse_err_alloc;
+ nftnl_parse_err_free;
+ nftnl_parse_perror;
+
+ nftnl_table_set_data;
+ nftnl_table_get_data;
+ nftnl_chain_set_data;
+ nftnl_chain_get_data;
+ nftnl_rule_set_data;
+ nftnl_rule_get_data;
+ nftnl_set_set_data;
+ nftnl_set_get_data;
+
+ nftnl_set_elems_nlmsg_build_payload_iter;
+ nftnl_batch_is_supported;
+ nftnl_batch_begin;
+ nftnl_batch_end;
+
+ nftnl_gen_alloc;
+ nftnl_gen_free;
+ nftnl_gen_is_set;
+ nftnl_gen_unset;
+ nftnl_gen_set_data;
+ nftnl_gen_set;
+ nftnl_gen_set_u32;
+ nftnl_gen_get_data;
+ nftnl_gen_get;
+ nftnl_gen_get_u32;
+ nftnl_gen_nlmsg_parse;
+ nftnl_gen_snprintf;
+ nftnl_gen_fprintf;
+
+ nftnl_ruleset_ctx_is_set;
+ nftnl_ruleset_ctx_get;
+ nftnl_ruleset_ctx_get_u32;
+ nftnl_ruleset_parse_file_cb;
+ nftnl_ruleset_parse_buffer_cb;
+ nftnl_ruleset_ctx_free;
+
+ nftnl_batch_alloc;
+ nftnl_batch_update;
+ nftnl_batch_free;
+ nftnl_batch_buffer;
+ nftnl_batch_buffer_len;
+ nftnl_batch_iovec_len;
+ nftnl_batch_iovec;
+
+ nftnl_trace_alloc;
+ nftnl_trace_free;
+
+ nftnl_trace_is_set;
+
+ nftnl_trace_get_u16;
+ nftnl_trace_get_u32;
+ nftnl_trace_get_u64;
+ nftnl_trace_get_str;
+ nftnl_trace_get_data;
+
+ nftnl_trace_nlmsg_parse;
+
+ nftnl_udata_buf_alloc;
+ nftnl_udata_buf_free;
+ nftnl_udata_buf_len;
+ nftnl_udata_buf_data;
+ nftnl_udata_buf_put;
+ nftnl_udata_start;
+ nftnl_udata_end;
+ nftnl_udata_put;
+ nftnl_udata_put_strz;
+ nftnl_udata_put_u32;
+ nftnl_udata_type;
+ nftnl_udata_len;
+ nftnl_udata_get;
+ nftnl_udata_get_u32;
+ nftnl_udata_next;
+ nftnl_udata_parse;
+
+ nftnl_obj_alloc;
+ nftnl_obj_free;
+ nftnl_obj_is_set;
+ nftnl_obj_unset;
+ nftnl_obj_set;
+ nftnl_obj_get;
+ nftnl_obj_set_u8;
+ nftnl_obj_set_u16;
+ nftnl_obj_set_u32;
+ nftnl_obj_set_u64;
+ nftnl_obj_set_str;
+ nftnl_obj_get_u8;
+ nftnl_obj_get_u16;
+ nftnl_obj_get_u32;
+ nftnl_obj_get_str;
+ nftnl_obj_get_u64;
+ nftnl_obj_parse;
+ nftnl_obj_parse_file;
+ nftnl_obj_snprintf;
+ nftnl_obj_fprintf;
+ nftnl_obj_nlmsg_build_payload;
+ nftnl_obj_nlmsg_parse;
+ nftnl_obj_list_alloc;
+ nftnl_obj_list_free;
+ nftnl_obj_list_is_empty;
+ nftnl_obj_list_foreach;
+ nftnl_obj_list_add;
+ nftnl_obj_list_add_tail;
+ nftnl_obj_list_del;
+ nftnl_obj_list_iter_create;
+ nftnl_obj_list_iter_next;
+ nftnl_obj_list_iter_destroy;
+
+ nftnl_expr_fprintf;
+
+ nftnl_flowtable_alloc;
+ nftnl_flowtable_free;
+ nftnl_flowtable_is_set;
+ nftnl_flowtable_unset;
+ nftnl_flowtable_set;
+ nftnl_flowtable_set_u32;
+ nftnl_flowtable_set_s32;
+ nftnl_flowtable_set_str;
+ nftnl_flowtable_set_u64;
+ nftnl_flowtable_get;
+ nftnl_flowtable_get_u32;
+ nftnl_flowtable_get_s32;
+ nftnl_flowtable_get_str;
+ nftnl_flowtable_get_u64;
+ nftnl_flowtable_parse;
+ nftnl_flowtable_parse_file;
+ nftnl_flowtable_snprintf;
+ nftnl_flowtable_fprintf;
+ nftnl_flowtable_nlmsg_build_payload;
+ nftnl_flowtable_nlmsg_parse;
+ nftnl_flowtable_list_alloc;
+ nftnl_flowtable_list_free;
+ nftnl_flowtable_list_is_empty;
+ nftnl_flowtable_list_add;
+ nftnl_flowtable_list_add_tail;
+ nftnl_flowtable_list_del;
+ nftnl_flowtable_list_foreach;
+
+ nftnl_rule_list_insert_at;
+
+local: *;
+};
+
+LIBNFTNL_12 {
+ nftnl_chain_rule_add;
+ nftnl_chain_rule_add_tail;
+ nftnl_chain_rule_del;
+ nftnl_chain_rule_insert_at;
+ nftnl_chain_rule_append_at;
+ nftnl_rule_foreach;
+ nftnl_rule_iter_create;
+ nftnl_rule_iter_next;
+ nftnl_rule_iter_destroy;
+
+ nftnl_chain_list_lookup_byname;
+ nftnl_rule_lookup_byindex;
+} LIBNFTNL_11;
+
+LIBNFTNL_13 {
+ nftnl_set_list_lookup_byname;
+ nftnl_obj_set_data;
+ nftnl_flowtable_set_data;
+} LIBNFTNL_12;
+
+LIBNFTNL_14 {
+ nftnl_udata_nest_start;
+ nftnl_udata_nest_end;
+ nftnl_chain_set_array;
+ nftnl_chain_get_array;
+ nftnl_flowtable_set_array;
+ nftnl_flowtable_get_array;
+} LIBNFTNL_13;
+
+LIBNFTNL_15 {
+ nftnl_obj_get_data;
+ nftnl_expr_build_payload;
+ nftnl_rule_del_expr;
+} LIBNFTNL_14;
+
+LIBNFTNL_16 {
+ nftnl_set_add_expr;
+ nftnl_set_expr_foreach;
+ nftnl_set_elem_add_expr;
+ nftnl_set_elem_expr_foreach;
+ nftnl_expr_add_expr;
+ nftnl_expr_expr_foreach;
+} LIBNFTNL_15;
+
+LIBNFTNL_17 {
+ nftnl_set_elem_nlmsg_build;
+} LIBNFTNL_16;
diff --git a/src/obj/counter.c b/src/obj/counter.c
new file mode 100644
index 0000000..ebf3e74
--- /dev/null
+++ b/src/obj/counter.c
@@ -0,0 +1,131 @@
+/*
+ * (C) 2012-2016 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include <libmnl/libmnl.h>
+#include <libnftnl/object.h>
+
+#include "internal.h"
+#include "obj.h"
+
+static int
+nftnl_obj_counter_set(struct nftnl_obj *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_obj_counter *ctr = nftnl_obj_data(e);
+
+ switch(type) {
+ case NFTNL_OBJ_CTR_BYTES:
+ memcpy(&ctr->bytes, data, sizeof(ctr->bytes));
+ break;
+ case NFTNL_OBJ_CTR_PKTS:
+ memcpy(&ctr->pkts, data, sizeof(ctr->pkts));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_obj_counter_get(const struct nftnl_obj *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_obj_counter *ctr = nftnl_obj_data(e);
+
+ switch(type) {
+ case NFTNL_OBJ_CTR_BYTES:
+ *data_len = sizeof(ctr->bytes);
+ return &ctr->bytes;
+ case NFTNL_OBJ_CTR_PKTS:
+ *data_len = sizeof(ctr->pkts);
+ return &ctr->pkts;
+ }
+ return NULL;
+}
+
+static int nftnl_obj_counter_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_COUNTER_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_COUNTER_BYTES:
+ case NFTA_COUNTER_PACKETS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_obj_counter_build(struct nlmsghdr *nlh, const struct nftnl_obj *e)
+{
+ struct nftnl_obj_counter *ctr = nftnl_obj_data(e);
+
+ if (e->flags & (1 << NFTNL_OBJ_CTR_BYTES))
+ mnl_attr_put_u64(nlh, NFTA_COUNTER_BYTES, htobe64(ctr->bytes));
+ if (e->flags & (1 << NFTNL_OBJ_CTR_PKTS))
+ mnl_attr_put_u64(nlh, NFTA_COUNTER_PACKETS, htobe64(ctr->pkts));
+}
+
+static int
+nftnl_obj_counter_parse(struct nftnl_obj *e, struct nlattr *attr)
+{
+ struct nftnl_obj_counter *ctr = nftnl_obj_data(e);
+ struct nlattr *tb[NFTA_COUNTER_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_obj_counter_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_COUNTER_BYTES]) {
+ ctr->bytes = be64toh(mnl_attr_get_u64(tb[NFTA_COUNTER_BYTES]));
+ e->flags |= (1 << NFTNL_OBJ_CTR_BYTES);
+ }
+ if (tb[NFTA_COUNTER_PACKETS]) {
+ ctr->pkts = be64toh(mnl_attr_get_u64(tb[NFTA_COUNTER_PACKETS]));
+ e->flags |= (1 << NFTNL_OBJ_CTR_PKTS);
+ }
+
+ return 0;
+}
+
+static int nftnl_obj_counter_snprintf(char *buf, size_t len, uint32_t flags,
+ const struct nftnl_obj *e)
+{
+ struct nftnl_obj_counter *ctr = nftnl_obj_data(e);
+
+ return snprintf(buf, len, "pkts %"PRIu64" bytes %"PRIu64" ",
+ ctr->pkts, ctr->bytes);
+}
+
+struct obj_ops obj_ops_counter = {
+ .name = "counter",
+ .type = NFT_OBJECT_COUNTER,
+ .alloc_len = sizeof(struct nftnl_obj_counter),
+ .max_attr = NFTA_COUNTER_MAX,
+ .set = nftnl_obj_counter_set,
+ .get = nftnl_obj_counter_get,
+ .parse = nftnl_obj_counter_parse,
+ .build = nftnl_obj_counter_build,
+ .output = nftnl_obj_counter_snprintf,
+};
diff --git a/src/obj/ct_expect.c b/src/obj/ct_expect.c
new file mode 100644
index 0000000..810ba9a
--- /dev/null
+++ b/src/obj/ct_expect.c
@@ -0,0 +1,200 @@
+/*
+ * (C) 2019 by Stéphane Veyret <sveyret@gmail.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 <arpa/inet.h>
+#include <errno.h>
+
+#include <libmnl/libmnl.h>
+
+#include "obj.h"
+
+static int nftnl_obj_ct_expect_set(struct nftnl_obj *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_obj_ct_expect *exp = nftnl_obj_data(e);
+
+ switch (type) {
+ case NFTNL_OBJ_CT_EXPECT_L3PROTO:
+ memcpy(&exp->l3proto, data, sizeof(exp->l3proto));
+ break;
+ case NFTNL_OBJ_CT_EXPECT_L4PROTO:
+ memcpy(&exp->l4proto, data, sizeof(exp->l4proto));
+ break;
+ case NFTNL_OBJ_CT_EXPECT_DPORT:
+ memcpy(&exp->dport, data, sizeof(exp->dport));
+ break;
+ case NFTNL_OBJ_CT_EXPECT_TIMEOUT:
+ memcpy(&exp->timeout, data, sizeof(exp->timeout));
+ break;
+ case NFTNL_OBJ_CT_EXPECT_SIZE:
+ memcpy(&exp->size, data, sizeof(exp->size));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *nftnl_obj_ct_expect_get(const struct nftnl_obj *e,
+ uint16_t type, uint32_t *data_len)
+{
+ struct nftnl_obj_ct_expect *exp = nftnl_obj_data(e);
+
+ switch (type) {
+ case NFTNL_OBJ_CT_EXPECT_L3PROTO:
+ *data_len = sizeof(exp->l3proto);
+ return &exp->l3proto;
+ case NFTNL_OBJ_CT_EXPECT_L4PROTO:
+ *data_len = sizeof(exp->l4proto);
+ return &exp->l4proto;
+ case NFTNL_OBJ_CT_EXPECT_DPORT:
+ *data_len = sizeof(exp->dport);
+ return &exp->dport;
+ case NFTNL_OBJ_CT_EXPECT_TIMEOUT:
+ *data_len = sizeof(exp->timeout);
+ return &exp->timeout;
+ case NFTNL_OBJ_CT_EXPECT_SIZE:
+ *data_len = sizeof(exp->size);
+ return &exp->size;
+ }
+ return NULL;
+}
+
+static int nftnl_obj_ct_expect_cb(const struct nlattr *attr, void *data)
+{
+ int type = mnl_attr_get_type(attr);
+ const struct nlattr **tb = data;
+
+ if (mnl_attr_type_valid(attr, NFTA_CT_EXPECT_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_CT_EXPECT_L3PROTO:
+ if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
+ abi_breakage();
+ break;
+ case NFTA_CT_EXPECT_L4PROTO:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+ abi_breakage();
+ break;
+ case NFTA_CT_EXPECT_DPORT:
+ if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
+ abi_breakage();
+ break;
+ case NFTA_CT_EXPECT_TIMEOUT:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_CT_EXPECT_SIZE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_obj_ct_expect_build(struct nlmsghdr *nlh, const struct nftnl_obj *e)
+{
+ struct nftnl_obj_ct_expect *exp = nftnl_obj_data(e);
+
+ if (e->flags & (1 << NFTNL_OBJ_CT_EXPECT_L3PROTO))
+ mnl_attr_put_u16(nlh, NFTA_CT_EXPECT_L3PROTO, htons(exp->l3proto));
+ if (e->flags & (1 << NFTNL_OBJ_CT_EXPECT_L4PROTO))
+ mnl_attr_put_u8(nlh, NFTA_CT_EXPECT_L4PROTO, exp->l4proto);
+ if (e->flags & (1 << NFTNL_OBJ_CT_EXPECT_DPORT))
+ mnl_attr_put_u16(nlh, NFTA_CT_EXPECT_DPORT, htons(exp->dport));
+ if (e->flags & (1 << NFTNL_OBJ_CT_EXPECT_TIMEOUT))
+ mnl_attr_put_u32(nlh, NFTA_CT_EXPECT_TIMEOUT, exp->timeout);
+ if (e->flags & (1 << NFTNL_OBJ_CT_EXPECT_SIZE))
+ mnl_attr_put_u8(nlh, NFTA_CT_EXPECT_SIZE, exp->size);
+}
+
+static int
+nftnl_obj_ct_expect_parse(struct nftnl_obj *e, struct nlattr *attr)
+{
+ struct nftnl_obj_ct_expect *exp = nftnl_obj_data(e);
+ struct nlattr *tb[NFTA_CT_EXPECT_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_obj_ct_expect_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_CT_EXPECT_L3PROTO]) {
+ exp->l3proto = ntohs(mnl_attr_get_u16(tb[NFTA_CT_EXPECT_L3PROTO]));
+ e->flags |= (1 << NFTNL_OBJ_CT_EXPECT_L3PROTO);
+ }
+ if (tb[NFTA_CT_EXPECT_L4PROTO]) {
+ exp->l4proto = mnl_attr_get_u8(tb[NFTA_CT_EXPECT_L4PROTO]);
+ e->flags |= (1 << NFTNL_OBJ_CT_EXPECT_L4PROTO);
+ }
+ if (tb[NFTA_CT_EXPECT_DPORT]) {
+ exp->dport = ntohs(mnl_attr_get_u16(tb[NFTA_CT_EXPECT_DPORT]));
+ e->flags |= (1 << NFTNL_OBJ_CT_EXPECT_DPORT);
+ }
+ if (tb[NFTA_CT_EXPECT_TIMEOUT]) {
+ exp->timeout = mnl_attr_get_u32(tb[NFTA_CT_EXPECT_TIMEOUT]);
+ e->flags |= (1 << NFTNL_OBJ_CT_EXPECT_TIMEOUT);
+ }
+ if (tb[NFTA_CT_EXPECT_SIZE]) {
+ exp->size = mnl_attr_get_u8(tb[NFTA_CT_EXPECT_SIZE]);
+ e->flags |= (1 << NFTNL_OBJ_CT_EXPECT_SIZE);
+ }
+
+ return 0;
+}
+
+static int nftnl_obj_ct_expect_snprintf(char *buf, size_t remain,
+ uint32_t flags,
+ const struct nftnl_obj *e)
+{
+ struct nftnl_obj_ct_expect *exp = nftnl_obj_data(e);
+ int ret = 0, offset = 0;
+
+ if (e->flags & (1 << NFTNL_OBJ_CT_EXPECT_L3PROTO)) {
+ ret = snprintf(buf + offset, remain,
+ "family %d ", exp->l3proto);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (e->flags & (1 << NFTNL_OBJ_CT_EXPECT_L4PROTO)) {
+ ret = snprintf(buf + offset, remain,
+ "protocol %d ", exp->l4proto);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (e->flags & (1 << NFTNL_OBJ_CT_EXPECT_DPORT)) {
+ ret = snprintf(buf + offset, remain,
+ "dport %d ", exp->dport);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (e->flags & (1 << NFTNL_OBJ_CT_EXPECT_TIMEOUT)) {
+ ret = snprintf(buf + offset, remain,
+ "timeout %d ", exp->timeout);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (e->flags & (1 << NFTNL_OBJ_CT_EXPECT_SIZE)) {
+ ret = snprintf(buf + offset, remain, "size %d ", exp->size);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ buf[offset] = '\0';
+ return offset;
+}
+
+struct obj_ops obj_ops_ct_expect = {
+ .name = "ct_expect",
+ .type = NFT_OBJECT_CT_EXPECT,
+ .alloc_len = sizeof(struct nftnl_obj_ct_expect),
+ .max_attr = NFTA_CT_EXPECT_MAX,
+ .set = nftnl_obj_ct_expect_set,
+ .get = nftnl_obj_ct_expect_get,
+ .parse = nftnl_obj_ct_expect_parse,
+ .build = nftnl_obj_ct_expect_build,
+ .output = nftnl_obj_ct_expect_snprintf,
+};
diff --git a/src/obj/ct_helper.c b/src/obj/ct_helper.c
new file mode 100644
index 0000000..a31bd6f
--- /dev/null
+++ b/src/obj/ct_helper.c
@@ -0,0 +1,154 @@
+/*
+ * (C) 2017 Red Hat GmbH
+ * Author: Florian Westphal <fw@strlen.de>
+ *
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/object.h>
+
+#include "obj.h"
+
+static int nftnl_obj_ct_helper_set(struct nftnl_obj *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_obj_ct_helper *helper = nftnl_obj_data(e);
+
+ switch (type) {
+ case NFTNL_OBJ_CT_HELPER_NAME:
+ snprintf(helper->name, sizeof(helper->name), "%s", (const char *)data);
+ break;
+ case NFTNL_OBJ_CT_HELPER_L3PROTO:
+ memcpy(&helper->l3proto, data, sizeof(helper->l3proto));
+ break;
+ case NFTNL_OBJ_CT_HELPER_L4PROTO:
+ memcpy(&helper->l4proto, data, sizeof(helper->l4proto));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *nftnl_obj_ct_helper_get(const struct nftnl_obj *e,
+ uint16_t type, uint32_t *data_len)
+{
+ struct nftnl_obj_ct_helper *helper = nftnl_obj_data(e);
+
+ switch (type) {
+ case NFTNL_OBJ_CT_HELPER_NAME:
+ *data_len = strlen(helper->name);
+ return helper->name;
+ case NFTNL_OBJ_CT_HELPER_L3PROTO:
+ *data_len = sizeof(helper->l3proto);
+ return &helper->l3proto;
+ case NFTNL_OBJ_CT_HELPER_L4PROTO:
+ *data_len = sizeof(helper->l4proto);
+ return &helper->l4proto;
+ }
+ return NULL;
+}
+
+static int nftnl_obj_ct_helper_cb(const struct nlattr *attr, void *data)
+{
+ const struct nftnl_obj_ct_helper *helper = NULL;
+ int type = mnl_attr_get_type(attr);
+ const struct nlattr **tb = data;
+
+ if (mnl_attr_type_valid(attr, NFTA_CT_HELPER_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_CT_HELPER_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ if (mnl_attr_get_payload_len(attr) >= sizeof(helper->name))
+ abi_breakage();
+ break;
+ case NFTA_CT_HELPER_L3PROTO:
+ if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
+ abi_breakage();
+ break;
+ case NFTA_CT_HELPER_L4PROTO:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_obj_ct_helper_build(struct nlmsghdr *nlh, const struct nftnl_obj *e)
+{
+ struct nftnl_obj_ct_helper *helper = nftnl_obj_data(e);
+
+ if (e->flags & (1 << NFTNL_OBJ_CT_HELPER_NAME))
+ mnl_attr_put_str(nlh, NFTA_CT_HELPER_NAME, helper->name);
+ if (e->flags & (1 << NFTNL_OBJ_CT_HELPER_L3PROTO))
+ mnl_attr_put_u16(nlh, NFTA_CT_HELPER_L3PROTO, htons(helper->l3proto));
+ if (e->flags & (1 << NFTNL_OBJ_CT_HELPER_L4PROTO))
+ mnl_attr_put_u8(nlh, NFTA_CT_HELPER_L4PROTO, helper->l4proto);
+}
+
+static int
+nftnl_obj_ct_helper_parse(struct nftnl_obj *e, struct nlattr *attr)
+{
+ struct nftnl_obj_ct_helper *helper = nftnl_obj_data(e);
+ struct nlattr *tb[NFTA_CT_HELPER_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_obj_ct_helper_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_CT_HELPER_NAME]) {
+ snprintf(helper->name, sizeof(helper->name), "%s",
+ mnl_attr_get_str(tb[NFTA_CT_HELPER_NAME]));
+ e->flags |= (1 << NFTNL_OBJ_CT_HELPER_NAME);
+ }
+ if (tb[NFTA_CT_HELPER_L3PROTO]) {
+ helper->l3proto = ntohs(mnl_attr_get_u16(tb[NFTA_CT_HELPER_L3PROTO]));
+ e->flags |= (1 << NFTNL_OBJ_CT_HELPER_L3PROTO);
+ }
+ if (tb[NFTA_CT_HELPER_L4PROTO]) {
+ helper->l4proto = mnl_attr_get_u8(tb[NFTA_CT_HELPER_L4PROTO]);
+ e->flags |= (1 << NFTNL_OBJ_CT_HELPER_L4PROTO);
+ }
+
+ return 0;
+}
+
+static int nftnl_obj_ct_helper_snprintf(char *buf, size_t len,
+ uint32_t flags,
+ const struct nftnl_obj *e)
+{
+ struct nftnl_obj_ct_helper *helper = nftnl_obj_data(e);
+
+ return snprintf(buf, len, "name %s family %d protocol %d ",
+ helper->name, helper->l3proto, helper->l4proto);
+}
+
+struct obj_ops obj_ops_ct_helper = {
+ .name = "ct_helper",
+ .type = NFT_OBJECT_CT_HELPER,
+ .alloc_len = sizeof(struct nftnl_obj_ct_helper),
+ .max_attr = NFTA_CT_HELPER_MAX,
+ .set = nftnl_obj_ct_helper_set,
+ .get = nftnl_obj_ct_helper_get,
+ .parse = nftnl_obj_ct_helper_parse,
+ .build = nftnl_obj_ct_helper_build,
+ .output = nftnl_obj_ct_helper_snprintf,
+};
diff --git a/src/obj/ct_timeout.c b/src/obj/ct_timeout.c
new file mode 100644
index 0000000..65b48bd
--- /dev/null
+++ b/src/obj/ct_timeout.c
@@ -0,0 +1,320 @@
+/*
+ * (C) 2018 by Harsha Sharma <harshasharmaiitr@gmail.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 <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/object.h>
+
+#include "obj.h"
+
+static const char *const tcp_state_to_name[] = {
+ [NFTNL_CTTIMEOUT_TCP_SYN_SENT] = "SYN_SENT",
+ [NFTNL_CTTIMEOUT_TCP_SYN_RECV] = "SYN_RECV",
+ [NFTNL_CTTIMEOUT_TCP_ESTABLISHED] = "ESTABLISHED",
+ [NFTNL_CTTIMEOUT_TCP_FIN_WAIT] = "FIN_WAIT",
+ [NFTNL_CTTIMEOUT_TCP_CLOSE_WAIT] = "CLOSE_WAIT",
+ [NFTNL_CTTIMEOUT_TCP_LAST_ACK] = "LAST_ACK",
+ [NFTNL_CTTIMEOUT_TCP_TIME_WAIT] = "TIME_WAIT",
+ [NFTNL_CTTIMEOUT_TCP_CLOSE] = "CLOSE",
+ [NFTNL_CTTIMEOUT_TCP_SYN_SENT2] = "SYN_SENT2",
+ [NFTNL_CTTIMEOUT_TCP_RETRANS] = "RETRANS",
+ [NFTNL_CTTIMEOUT_TCP_UNACK] = "UNACKNOWLEDGED",
+};
+
+static uint32_t tcp_dflt_timeout[] = {
+ [NFTNL_CTTIMEOUT_TCP_SYN_SENT] = 120,
+ [NFTNL_CTTIMEOUT_TCP_SYN_RECV] = 60,
+ [NFTNL_CTTIMEOUT_TCP_ESTABLISHED] = 432000,
+ [NFTNL_CTTIMEOUT_TCP_FIN_WAIT] = 120,
+ [NFTNL_CTTIMEOUT_TCP_CLOSE_WAIT] = 60,
+ [NFTNL_CTTIMEOUT_TCP_LAST_ACK] = 30,
+ [NFTNL_CTTIMEOUT_TCP_TIME_WAIT] = 120,
+ [NFTNL_CTTIMEOUT_TCP_CLOSE] = 10,
+ [NFTNL_CTTIMEOUT_TCP_SYN_SENT2] = 120,
+ [NFTNL_CTTIMEOUT_TCP_RETRANS] = 300,
+ [NFTNL_CTTIMEOUT_TCP_UNACK] = 300,
+};
+
+static const char *const udp_state_to_name[] = {
+ [NFTNL_CTTIMEOUT_UDP_UNREPLIED] = "UNREPLIED",
+ [NFTNL_CTTIMEOUT_UDP_REPLIED] = "REPLIED",
+};
+
+static uint32_t udp_dflt_timeout[] = {
+ [NFTNL_CTTIMEOUT_UDP_UNREPLIED] = 30,
+ [NFTNL_CTTIMEOUT_UDP_REPLIED] = 180,
+};
+
+static struct {
+ uint32_t attr_max;
+ const char *const *state_to_name;
+ uint32_t *dflt_timeout;
+} timeout_protocol[IPPROTO_MAX] = {
+ [IPPROTO_TCP] = {
+ .attr_max = NFTNL_CTTIMEOUT_TCP_MAX,
+ .state_to_name = tcp_state_to_name,
+ .dflt_timeout = tcp_dflt_timeout,
+ },
+ [IPPROTO_UDP] = {
+ .attr_max = NFTNL_CTTIMEOUT_UDP_MAX,
+ .state_to_name = udp_state_to_name,
+ .dflt_timeout = udp_dflt_timeout,
+ },
+};
+
+struct _container_policy_cb {
+ unsigned int nlattr_max;
+ void *tb;
+};
+
+static int
+nftnl_timeout_policy_attr_set_u32(struct nftnl_obj *e,
+ uint32_t type, uint32_t data)
+{
+ struct nftnl_obj_ct_timeout *t = nftnl_obj_data(e);
+
+ if (type >= NFTNL_CTTIMEOUT_ARRAY_MAX)
+ return -1;
+
+ t->timeout[type] = data;
+
+ if (!(e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_ARRAY)))
+ e->flags |= (1 << NFTNL_OBJ_CT_TIMEOUT_ARRAY);
+
+ return 0;
+}
+
+static int
+parse_timeout_attr_policy_cb(const struct nlattr *attr, void *data)
+{
+ struct _container_policy_cb *data_cb = data;
+ const struct nlattr **tb = data_cb->tb;
+ uint16_t type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, data_cb->nlattr_max) < 0)
+ return MNL_CB_OK;
+
+ if (type > 0 && type <= data_cb->nlattr_max) {
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ tb[type - 1] = attr;
+ }
+ return MNL_CB_OK;
+}
+
+static int
+timeout_parse_attr_data(struct nftnl_obj *e,
+ const struct nlattr *nest)
+{
+ struct nftnl_obj_ct_timeout *t = nftnl_obj_data(e);
+ unsigned int attr_max = timeout_protocol[t->l4proto].attr_max;
+ struct nlattr *tb[attr_max];
+ struct _container_policy_cb cnt = {
+ .nlattr_max = attr_max,
+ .tb = tb,
+ };
+ unsigned int i;
+
+ memset(tb, 0, sizeof(struct nlattr *) * attr_max);
+
+ if (mnl_attr_parse_nested(nest, parse_timeout_attr_policy_cb, &cnt) < 0)
+ return -1;
+
+ for (i = 0; i < array_size(tb); i++) {
+ if (tb[i]) {
+ nftnl_timeout_policy_attr_set_u32(e, i,
+ ntohl(mnl_attr_get_u32(tb[i])));
+ }
+ }
+ return 0;
+}
+
+static int nftnl_obj_ct_timeout_set(struct nftnl_obj *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_obj_ct_timeout *timeout = nftnl_obj_data(e);
+
+ switch (type) {
+ case NFTNL_OBJ_CT_TIMEOUT_L3PROTO:
+ memcpy(&timeout->l3proto, data, sizeof(timeout->l3proto));
+ break;
+ case NFTNL_OBJ_CT_TIMEOUT_L4PROTO:
+ memcpy(&timeout->l4proto, data, sizeof(timeout->l4proto));
+ break;
+ case NFTNL_OBJ_CT_TIMEOUT_ARRAY:
+ memcpy(timeout->timeout, data,
+ sizeof(uint32_t) * NFTNL_CTTIMEOUT_ARRAY_MAX);
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *nftnl_obj_ct_timeout_get(const struct nftnl_obj *e,
+ uint16_t type, uint32_t *data_len)
+{
+ struct nftnl_obj_ct_timeout *timeout = nftnl_obj_data(e);
+
+ switch (type) {
+ case NFTNL_OBJ_CT_TIMEOUT_L3PROTO:
+ *data_len = sizeof(timeout->l3proto);
+ return &timeout->l3proto;
+ case NFTNL_OBJ_CT_TIMEOUT_L4PROTO:
+ *data_len = sizeof(timeout->l4proto);
+ return &timeout->l4proto;
+ case NFTNL_OBJ_CT_TIMEOUT_ARRAY:
+ *data_len = sizeof(uint32_t) * NFTNL_CTTIMEOUT_ARRAY_MAX;
+ return timeout->timeout;
+ }
+ return NULL;
+}
+
+static int nftnl_obj_ct_timeout_cb(const struct nlattr *attr, void *data)
+{
+ int type = mnl_attr_get_type(attr);
+ const struct nlattr **tb = data;
+
+ if (mnl_attr_type_valid(attr, NFTA_CT_TIMEOUT_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_CT_TIMEOUT_L3PROTO:
+ if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
+ abi_breakage();
+ break;
+ case NFTA_CT_TIMEOUT_L4PROTO:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+ abi_breakage();
+ break;
+ case NFTA_CT_TIMEOUT_DATA:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_obj_ct_timeout_build(struct nlmsghdr *nlh, const struct nftnl_obj *e)
+{
+ struct nftnl_obj_ct_timeout *timeout = nftnl_obj_data(e);
+ struct nlattr *nest;
+
+ if (e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_L3PROTO))
+ mnl_attr_put_u16(nlh, NFTA_CT_TIMEOUT_L3PROTO, htons(timeout->l3proto));
+ if (e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_L4PROTO))
+ mnl_attr_put_u8(nlh, NFTA_CT_TIMEOUT_L4PROTO, timeout->l4proto);
+ if (e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_ARRAY)) {
+ int i;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_CT_TIMEOUT_DATA);
+ for (i = 0; i < timeout_protocol[timeout->l4proto].attr_max; i++)
+ mnl_attr_put_u32(nlh, i+1, htonl(timeout->timeout[i]));
+
+ mnl_attr_nest_end(nlh, nest);
+ }
+}
+
+static int
+nftnl_obj_ct_timeout_parse(struct nftnl_obj *e, struct nlattr *attr)
+{
+ struct nftnl_obj_ct_timeout *timeout = nftnl_obj_data(e);
+ struct nlattr *tb[NFTA_CT_TIMEOUT_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_obj_ct_timeout_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_CT_TIMEOUT_L3PROTO]) {
+ timeout->l3proto = ntohs(mnl_attr_get_u16(tb[NFTA_CT_TIMEOUT_L3PROTO]));
+ e->flags |= (1 << NFTNL_OBJ_CT_TIMEOUT_L3PROTO);
+ }
+ if (tb[NFTA_CT_TIMEOUT_L4PROTO]) {
+ timeout->l4proto = mnl_attr_get_u8(tb[NFTA_CT_TIMEOUT_L4PROTO]);
+ e->flags |= (1 << NFTNL_OBJ_CT_TIMEOUT_L4PROTO);
+ }
+ if (tb[NFTA_CT_TIMEOUT_DATA]) {
+ if (timeout_parse_attr_data(e, tb[NFTA_CT_TIMEOUT_DATA]) < 0)
+ return -1;
+ e->flags |= (1 << NFTNL_OBJ_CT_TIMEOUT_ARRAY);
+ }
+ return 0;
+}
+
+static int nftnl_obj_ct_timeout_snprintf(char *buf, size_t remain,
+ uint32_t flags,
+ const struct nftnl_obj *e)
+{
+ int ret = 0, offset = 0;
+
+ struct nftnl_obj_ct_timeout *timeout = nftnl_obj_data(e);
+
+ if (e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_L3PROTO)) {
+ ret = snprintf(buf + offset, remain, "family %d ",
+ timeout->l3proto);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_L4PROTO)) {
+ ret = snprintf(buf + offset, remain, "protocol %d ",
+ timeout->l4proto);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ if (e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_ARRAY)) {
+ uint8_t l4num = timeout->l4proto;
+ int i;
+
+ /* default to generic protocol tracker. */
+ if (timeout_protocol[timeout->l4proto].attr_max == 0)
+ l4num = IPPROTO_RAW;
+
+ ret = snprintf(buf + offset, remain, "policy = {");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ for (i = 0; i < timeout_protocol[l4num].attr_max; i++) {
+ const char *state_name =
+ timeout_protocol[l4num].state_to_name[i][0] ?
+ timeout_protocol[l4num].state_to_name[i] :
+ "UNKNOWN";
+
+ if (timeout->timeout[i] != timeout_protocol[l4num].dflt_timeout[i]) {
+ ret = snprintf(buf + offset, remain,
+ "%s = %u,", state_name, timeout->timeout[i]);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ }
+
+ ret = snprintf(buf + offset, remain, "}");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ buf[offset] = '\0';
+
+ return offset;
+}
+
+struct obj_ops obj_ops_ct_timeout = {
+ .name = "ct_timeout",
+ .type = NFT_OBJECT_CT_TIMEOUT,
+ .alloc_len = sizeof(struct nftnl_obj_ct_timeout),
+ .max_attr = NFTA_CT_TIMEOUT_MAX,
+ .set = nftnl_obj_ct_timeout_set,
+ .get = nftnl_obj_ct_timeout_get,
+ .parse = nftnl_obj_ct_timeout_parse,
+ .build = nftnl_obj_ct_timeout_build,
+ .output = nftnl_obj_ct_timeout_snprintf,
+};
diff --git a/src/obj/limit.c b/src/obj/limit.c
new file mode 100644
index 0000000..d7b1aed
--- /dev/null
+++ b/src/obj/limit.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2017 Pablo M. Bermudo Garay <pablombg@gmail.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 <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/object.h>
+
+#include "obj.h"
+
+static int nftnl_obj_limit_set(struct nftnl_obj *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_obj_limit *limit = nftnl_obj_data(e);
+
+ switch (type) {
+ case NFTNL_OBJ_LIMIT_RATE:
+ memcpy(&limit->rate, data, sizeof(limit->rate));
+ break;
+ case NFTNL_OBJ_LIMIT_UNIT:
+ memcpy(&limit->unit, data, sizeof(limit->unit));
+ break;
+ case NFTNL_OBJ_LIMIT_BURST:
+ memcpy(&limit->burst, data, sizeof(limit->burst));
+ break;
+ case NFTNL_OBJ_LIMIT_TYPE:
+ memcpy(&limit->type, data, sizeof(limit->type));
+ break;
+ case NFTNL_OBJ_LIMIT_FLAGS:
+ memcpy(&limit->flags, data, sizeof(limit->flags));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *nftnl_obj_limit_get(const struct nftnl_obj *e,
+ uint16_t type, uint32_t *data_len)
+{
+ struct nftnl_obj_limit *limit = nftnl_obj_data(e);
+
+ switch (type) {
+ case NFTNL_OBJ_LIMIT_RATE:
+ *data_len = sizeof(limit->rate);
+ return &limit->rate;
+ case NFTNL_OBJ_LIMIT_UNIT:
+ *data_len = sizeof(limit->unit);
+ return &limit->unit;
+ case NFTNL_OBJ_LIMIT_BURST:
+ *data_len = sizeof(limit->burst);
+ return &limit->burst;
+ case NFTNL_OBJ_LIMIT_TYPE:
+ *data_len = sizeof(limit->type);
+ return &limit->type;
+ case NFTNL_OBJ_LIMIT_FLAGS:
+ *data_len = sizeof(limit->flags);
+ return &limit->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_obj_limit_cb(const struct nlattr *attr, void *data)
+{
+ int type = mnl_attr_get_type(attr);
+ const struct nlattr **tb = data;
+
+ if (mnl_attr_type_valid(attr, NFTA_LIMIT_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_LIMIT_RATE:
+ case NFTA_LIMIT_UNIT:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ case NFTA_LIMIT_BURST:
+ case NFTA_LIMIT_TYPE:
+ case NFTA_LIMIT_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void nftnl_obj_limit_build(struct nlmsghdr *nlh,
+ const struct nftnl_obj *e)
+{
+ struct nftnl_obj_limit *limit = nftnl_obj_data(e);
+
+ if (e->flags & (1 << NFTNL_OBJ_LIMIT_RATE))
+ mnl_attr_put_u64(nlh, NFTA_LIMIT_RATE, htobe64(limit->rate));
+ if (e->flags & (1 << NFTNL_OBJ_LIMIT_UNIT))
+ mnl_attr_put_u64(nlh, NFTA_LIMIT_UNIT, htobe64(limit->unit));
+ if (e->flags & (1 << NFTNL_OBJ_LIMIT_BURST))
+ mnl_attr_put_u32(nlh, NFTA_LIMIT_BURST, htonl(limit->burst));
+ if (e->flags & (1 << NFTNL_OBJ_LIMIT_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_LIMIT_TYPE, htonl(limit->type));
+ if (e->flags & (1 << NFTNL_OBJ_LIMIT_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_LIMIT_FLAGS, htonl(limit->flags));
+}
+
+static int nftnl_obj_limit_parse(struct nftnl_obj *e, struct nlattr *attr)
+{
+ struct nftnl_obj_limit *limit = nftnl_obj_data(e);
+ struct nlattr *tb[NFTA_LIMIT_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_obj_limit_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_LIMIT_RATE]) {
+ limit->rate = be64toh(mnl_attr_get_u64(tb[NFTA_LIMIT_RATE]));
+ e->flags |= (1 << NFTNL_OBJ_LIMIT_RATE);
+ }
+ if (tb[NFTA_LIMIT_UNIT]) {
+ limit->unit = be64toh(mnl_attr_get_u64(tb[NFTA_LIMIT_UNIT]));
+ e->flags |= (1 << NFTNL_OBJ_LIMIT_UNIT);
+ }
+ if (tb[NFTA_LIMIT_BURST]) {
+ limit->burst = ntohl(mnl_attr_get_u32(tb[NFTA_LIMIT_BURST]));
+ e->flags |= (1 << NFTNL_OBJ_LIMIT_BURST);
+ }
+ if (tb[NFTA_LIMIT_TYPE]) {
+ limit->type = ntohl(mnl_attr_get_u32(tb[NFTA_LIMIT_TYPE]));
+ e->flags |= (1 << NFTNL_OBJ_LIMIT_TYPE);
+ }
+ if (tb[NFTA_LIMIT_FLAGS]) {
+ limit->flags = ntohl(mnl_attr_get_u32(tb[NFTA_LIMIT_FLAGS]));
+ e->flags |= (1 << NFTNL_OBJ_LIMIT_FLAGS);
+ }
+
+ return 0;
+}
+
+static int nftnl_obj_limit_snprintf(char *buf, size_t len,
+ uint32_t flags,
+ const struct nftnl_obj *e)
+{
+ struct nftnl_obj_limit *limit = nftnl_obj_data(e);
+
+ return snprintf(buf, len, "rate %"PRIu64" unit %"PRIu64" burst %u "
+ "type %u flags %u ", limit->rate, limit->unit,
+ limit->burst, limit->type, limit->flags);
+}
+
+struct obj_ops obj_ops_limit = {
+ .name = "limit",
+ .type = NFT_OBJECT_LIMIT,
+ .alloc_len = sizeof(struct nftnl_obj_limit),
+ .max_attr = NFTA_LIMIT_MAX,
+ .set = nftnl_obj_limit_set,
+ .get = nftnl_obj_limit_get,
+ .parse = nftnl_obj_limit_parse,
+ .build = nftnl_obj_limit_build,
+ .output = nftnl_obj_limit_snprintf,
+};
diff --git a/src/obj/quota.c b/src/obj/quota.c
new file mode 100644
index 0000000..6c7559a
--- /dev/null
+++ b/src/obj/quota.c
@@ -0,0 +1,148 @@
+/*
+ * (C) 2012-2016 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/object.h>
+
+#include "obj.h"
+
+static int nftnl_obj_quota_set(struct nftnl_obj *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_obj_quota *quota = nftnl_obj_data(e);
+
+ switch (type) {
+ case NFTNL_OBJ_QUOTA_BYTES:
+ memcpy(&quota->bytes, data, sizeof(quota->bytes));
+ break;
+ case NFTNL_OBJ_QUOTA_CONSUMED:
+ memcpy(&quota->consumed, data, sizeof(quota->consumed));
+ break;
+ case NFTNL_OBJ_QUOTA_FLAGS:
+ memcpy(&quota->flags, data, sizeof(quota->flags));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *nftnl_obj_quota_get(const struct nftnl_obj *e,
+ uint16_t type, uint32_t *data_len)
+{
+ struct nftnl_obj_quota *quota = nftnl_obj_data(e);
+
+ switch (type) {
+ case NFTNL_OBJ_QUOTA_BYTES:
+ *data_len = sizeof(quota->bytes);
+ return &quota->bytes;
+ case NFTNL_OBJ_QUOTA_CONSUMED:
+ *data_len = sizeof(quota->consumed);
+ return &quota->consumed;
+ case NFTNL_OBJ_QUOTA_FLAGS:
+ *data_len = sizeof(quota->flags);
+ return &quota->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_obj_quota_cb(const struct nlattr *attr, void *data)
+{
+ int type = mnl_attr_get_type(attr);
+ const struct nlattr **tb = data;
+
+ if (mnl_attr_type_valid(attr, NFTA_QUOTA_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_QUOTA_BYTES:
+ case NFTA_QUOTA_CONSUMED:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ case NFTA_QUOTA_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_obj_quota_build(struct nlmsghdr *nlh, const struct nftnl_obj *e)
+{
+ struct nftnl_obj_quota *quota = nftnl_obj_data(e);
+
+ if (e->flags & (1 << NFTNL_OBJ_QUOTA_BYTES))
+ mnl_attr_put_u64(nlh, NFTA_QUOTA_BYTES, htobe64(quota->bytes));
+ if (e->flags & (1 << NFTNL_OBJ_QUOTA_CONSUMED))
+ mnl_attr_put_u64(nlh, NFTA_QUOTA_CONSUMED,
+ htobe64(quota->consumed));
+ if (e->flags & (1 << NFTNL_OBJ_QUOTA_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_QUOTA_FLAGS, htonl(quota->flags));
+}
+
+static int
+nftnl_obj_quota_parse(struct nftnl_obj *e, struct nlattr *attr)
+{
+ struct nftnl_obj_quota *quota = nftnl_obj_data(e);
+ struct nlattr *tb[NFTA_QUOTA_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_obj_quota_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_QUOTA_BYTES]) {
+ quota->bytes = be64toh(mnl_attr_get_u64(tb[NFTA_QUOTA_BYTES]));
+ e->flags |= (1 << NFTNL_OBJ_QUOTA_BYTES);
+ }
+ if (tb[NFTA_QUOTA_CONSUMED]) {
+ quota->consumed =
+ be64toh(mnl_attr_get_u64(tb[NFTA_QUOTA_CONSUMED]));
+ e->flags |= (1 << NFTNL_OBJ_QUOTA_CONSUMED);
+ }
+ if (tb[NFTA_QUOTA_FLAGS]) {
+ quota->flags = ntohl(mnl_attr_get_u32(tb[NFTA_QUOTA_FLAGS]));
+ e->flags |= (1 << NFTNL_OBJ_QUOTA_FLAGS);
+ }
+
+ return 0;
+}
+
+static int nftnl_obj_quota_snprintf(char *buf, size_t len,
+ uint32_t flags,
+ const struct nftnl_obj *e)
+{
+ struct nftnl_obj_quota *quota = nftnl_obj_data(e);
+
+ return snprintf(buf, len, "bytes %"PRIu64" flags %u ",
+ quota->bytes, quota->flags);
+}
+
+struct obj_ops obj_ops_quota = {
+ .name = "quota",
+ .type = NFT_OBJECT_QUOTA,
+ .alloc_len = sizeof(struct nftnl_obj_quota),
+ .max_attr = NFTA_QUOTA_MAX,
+ .set = nftnl_obj_quota_set,
+ .get = nftnl_obj_quota_get,
+ .parse = nftnl_obj_quota_parse,
+ .build = nftnl_obj_quota_build,
+ .output = nftnl_obj_quota_snprintf,
+};
diff --git a/src/obj/secmark.c b/src/obj/secmark.c
new file mode 100644
index 0000000..e5c24b3
--- /dev/null
+++ b/src/obj/secmark.c
@@ -0,0 +1,120 @@
+/*
+ * (C) 2012-2016 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/object.h>
+
+#include "obj.h"
+
+static int nftnl_obj_secmark_set(struct nftnl_obj *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_obj_secmark *secmark = nftnl_obj_data(e);
+
+ switch (type) {
+ case NFTNL_OBJ_SECMARK_CTX:
+ snprintf(secmark->ctx, sizeof(secmark->ctx), "%s", (const char *)data);
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *nftnl_obj_secmark_get(const struct nftnl_obj *e,
+ uint16_t type, uint32_t *data_len)
+{
+ struct nftnl_obj_secmark *secmark = nftnl_obj_data(e);
+
+ switch (type) {
+ case NFTNL_OBJ_SECMARK_CTX:
+ *data_len = strlen(secmark->ctx);
+ return secmark->ctx;
+ }
+ return NULL;
+}
+
+static int nftnl_obj_secmark_cb(const struct nlattr *attr, void *data)
+{
+ const struct nftnl_obj_secmark *secmark = NULL;
+ int type = mnl_attr_get_type(attr);
+ const struct nlattr **tb = data;
+
+ if (mnl_attr_type_valid(attr, NFTA_SECMARK_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_SECMARK_CTX:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ if (mnl_attr_get_payload_len(attr) >= sizeof(secmark->ctx))
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_obj_secmark_build(struct nlmsghdr *nlh, const struct nftnl_obj *e)
+{
+ struct nftnl_obj_secmark *secmark = nftnl_obj_data(e);
+
+ if (e->flags & (1 << NFTNL_OBJ_SECMARK_CTX))
+ mnl_attr_put_str(nlh, NFTA_SECMARK_CTX, secmark->ctx);
+}
+
+static int
+nftnl_obj_secmark_parse(struct nftnl_obj *e, struct nlattr *attr)
+{
+ struct nftnl_obj_secmark *secmark = nftnl_obj_data(e);
+ struct nlattr *tb[NFTA_SECMARK_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_obj_secmark_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_SECMARK_CTX]) {
+ snprintf(secmark->ctx, sizeof(secmark->ctx), "%s",
+ mnl_attr_get_str(tb[NFTA_SECMARK_CTX]));
+ e->flags |= (1 << NFTNL_OBJ_SECMARK_CTX);
+ }
+
+ return 0;
+}
+
+static int nftnl_obj_secmark_snprintf(char *buf, size_t len,
+ uint32_t flags,
+ const struct nftnl_obj *e)
+{
+ struct nftnl_obj_secmark *secmark = nftnl_obj_data(e);
+
+ return snprintf(buf, len, "context %s ", secmark->ctx);
+}
+
+struct obj_ops obj_ops_secmark = {
+ .name = "secmark",
+ .type = NFT_OBJECT_SECMARK,
+ .alloc_len = sizeof(struct nftnl_obj_secmark),
+ .max_attr = NFTA_SECMARK_MAX,
+ .set = nftnl_obj_secmark_set,
+ .get = nftnl_obj_secmark_get,
+ .parse = nftnl_obj_secmark_parse,
+ .build = nftnl_obj_secmark_build,
+ .output = nftnl_obj_secmark_snprintf,
+};
diff --git a/src/obj/synproxy.c b/src/obj/synproxy.c
new file mode 100644
index 0000000..baef5c2
--- /dev/null
+++ b/src/obj/synproxy.c
@@ -0,0 +1,147 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include "internal.h"
+#include <libmnl/libmnl.h>
+#include <libnftnl/object.h>
+
+#include "obj.h"
+
+static int nftnl_obj_synproxy_set(struct nftnl_obj *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_obj_synproxy *synproxy = nftnl_obj_data(e);
+
+ switch (type) {
+ case NFTNL_OBJ_SYNPROXY_MSS:
+ synproxy->mss = *((uint16_t *)data);
+ break;
+ case NFTNL_OBJ_SYNPROXY_WSCALE:
+ synproxy->wscale = *((uint8_t *)data);
+ break;
+ case NFTNL_OBJ_SYNPROXY_FLAGS:
+ synproxy->flags = *((uint32_t *)data);
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *nftnl_obj_synproxy_get(const struct nftnl_obj *e,
+ uint16_t type, uint32_t *data_len)
+{
+ struct nftnl_obj_synproxy *synproxy = nftnl_obj_data(e);
+
+ switch (type) {
+ case NFTNL_OBJ_SYNPROXY_MSS:
+ *data_len = sizeof(synproxy->mss);
+ return &synproxy->mss;
+ case NFTNL_OBJ_SYNPROXY_WSCALE:
+ *data_len = sizeof(synproxy->wscale);
+ return &synproxy->wscale;
+ case NFTNL_OBJ_SYNPROXY_FLAGS:
+ *data_len = sizeof(synproxy->flags);
+ return &synproxy->flags;
+ }
+ return NULL;
+}
+
+static int nftnl_obj_synproxy_cb(const struct nlattr *attr, void *data)
+{
+ int type = mnl_attr_get_type(attr);
+ const struct nlattr **tb = data;
+
+ if (mnl_attr_type_valid(attr, NFTA_SYNPROXY_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_SYNPROXY_MSS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
+ abi_breakage();
+ break;
+ case NFTA_SYNPROXY_WSCALE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+ abi_breakage();
+ break;
+ case NFTA_SYNPROXY_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void nftnl_obj_synproxy_build(struct nlmsghdr *nlh,
+ const struct nftnl_obj *e)
+{
+ struct nftnl_obj_synproxy *synproxy = nftnl_obj_data(e);
+
+ if (e->flags & (1 << NFTNL_OBJ_SYNPROXY_MSS))
+ mnl_attr_put_u16(nlh, NFTA_SYNPROXY_MSS, htons(synproxy->mss));
+ if (e->flags & (1 << NFTNL_OBJ_SYNPROXY_WSCALE))
+ mnl_attr_put_u8(nlh, NFTA_SYNPROXY_WSCALE, synproxy->wscale);
+ if (e->flags & (1 << NFTNL_OBJ_SYNPROXY_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_SYNPROXY_FLAGS,
+ htonl(synproxy->flags));
+}
+
+static int nftnl_obj_synproxy_parse(struct nftnl_obj *e, struct nlattr *attr)
+{
+ struct nftnl_obj_synproxy *synproxy = nftnl_obj_data(e);
+ struct nlattr *tb[NFTA_SYNPROXY_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_obj_synproxy_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_SYNPROXY_MSS]) {
+ synproxy->mss = ntohs(mnl_attr_get_u16(tb[NFTA_SYNPROXY_MSS]));
+ e->flags |= (1 << NFTNL_OBJ_SYNPROXY_MSS);
+ }
+ if (tb[NFTA_SYNPROXY_WSCALE]) {
+ synproxy->wscale = mnl_attr_get_u8(tb[NFTA_SYNPROXY_WSCALE]);
+ e->flags |= (1 << NFTNL_OBJ_SYNPROXY_WSCALE);
+ }
+ if (tb[NFTA_SYNPROXY_FLAGS]) {
+ synproxy->flags = ntohl(mnl_attr_get_u32(tb[NFTA_SYNPROXY_FLAGS]));
+ e->flags |= (1 << NFTNL_OBJ_SYNPROXY_FLAGS);
+ }
+
+ return 0;
+}
+
+static int nftnl_obj_synproxy_snprintf(char *buf, size_t len,
+ uint32_t flags,
+ const struct nftnl_obj *e)
+{
+ struct nftnl_obj_synproxy *synproxy = nftnl_obj_data(e);
+ int ret, offset = 0;
+
+ if (e->flags & (1 << NFTNL_OBJ_SYNPROXY_MSS) &&
+ e->flags & (1 << NFTNL_OBJ_SYNPROXY_WSCALE)) {
+ ret = snprintf(buf, len, "mss %u wscale %u ", synproxy->mss,
+ synproxy->wscale);
+ SNPRINTF_BUFFER_SIZE(ret, len, offset);
+ }
+
+ return offset;
+}
+
+struct obj_ops obj_ops_synproxy = {
+ .name = "synproxy",
+ .type = NFT_OBJECT_SYNPROXY,
+ .alloc_len = sizeof(struct nftnl_obj_synproxy),
+ .max_attr = NFTA_SYNPROXY_MAX,
+ .set = nftnl_obj_synproxy_set,
+ .get = nftnl_obj_synproxy_get,
+ .parse = nftnl_obj_synproxy_parse,
+ .build = nftnl_obj_synproxy_build,
+ .output = nftnl_obj_synproxy_snprintf,
+};
diff --git a/src/obj/tunnel.c b/src/obj/tunnel.c
new file mode 100644
index 0000000..d2503dc
--- /dev/null
+++ b/src/obj/tunnel.c
@@ -0,0 +1,551 @@
+/*
+ * (C) 2018 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include <libmnl/libmnl.h>
+#include <libnftnl/object.h>
+
+#include "internal.h"
+#include "obj.h"
+
+static int
+nftnl_obj_tunnel_set(struct nftnl_obj *e, uint16_t type,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_obj_tunnel *tun = nftnl_obj_data(e);
+
+ switch (type) {
+ case NFTNL_OBJ_TUNNEL_ID:
+ memcpy(&tun->id, data, sizeof(tun->id));
+ break;
+ case NFTNL_OBJ_TUNNEL_IPV4_SRC:
+ memcpy(&tun->src_v4, data, sizeof(tun->src_v4));
+ break;
+ case NFTNL_OBJ_TUNNEL_IPV4_DST:
+ memcpy(&tun->dst_v4, data, sizeof(tun->dst_v4));
+ break;
+ case NFTNL_OBJ_TUNNEL_IPV6_SRC:
+ memcpy(&tun->src_v6, data, sizeof(struct in6_addr));
+ break;
+ case NFTNL_OBJ_TUNNEL_IPV6_DST:
+ memcpy(&tun->dst_v6, data, sizeof(struct in6_addr));
+ break;
+ case NFTNL_OBJ_TUNNEL_IPV6_FLOWLABEL:
+ memcpy(&tun->flowlabel, data, sizeof(tun->flowlabel));
+ break;
+ case NFTNL_OBJ_TUNNEL_SPORT:
+ memcpy(&tun->sport, data, sizeof(tun->sport));
+ break;
+ case NFTNL_OBJ_TUNNEL_DPORT:
+ memcpy(&tun->dport, data, sizeof(tun->dport));
+ break;
+ case NFTNL_OBJ_TUNNEL_FLAGS:
+ memcpy(&tun->tun_flags, data, sizeof(tun->tun_flags));
+ break;
+ case NFTNL_OBJ_TUNNEL_TOS:
+ memcpy(&tun->tun_tos, data, sizeof(tun->tun_tos));
+ break;
+ case NFTNL_OBJ_TUNNEL_TTL:
+ memcpy(&tun->tun_ttl, data, sizeof(tun->tun_ttl));
+ break;
+ case NFTNL_OBJ_TUNNEL_VXLAN_GBP:
+ memcpy(&tun->u.tun_vxlan.gbp, data, sizeof(tun->u.tun_vxlan.gbp));
+ break;
+ case NFTNL_OBJ_TUNNEL_ERSPAN_VERSION:
+ memcpy(&tun->u.tun_erspan.version, data, sizeof(tun->u.tun_erspan.version));
+ break;
+ case NFTNL_OBJ_TUNNEL_ERSPAN_V1_INDEX:
+ memcpy(&tun->u.tun_erspan.u.v1_index, data, sizeof(tun->u.tun_erspan.u.v1_index));
+ break;
+ case NFTNL_OBJ_TUNNEL_ERSPAN_V2_HWID:
+ memcpy(&tun->u.tun_erspan.u.v2.hwid, data, sizeof(tun->u.tun_erspan.u.v2.hwid));
+ break;
+ case NFTNL_OBJ_TUNNEL_ERSPAN_V2_DIR:
+ memcpy(&tun->u.tun_erspan.u.v2.dir, data, sizeof(tun->u.tun_erspan.u.v2.dir));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static const void *
+nftnl_obj_tunnel_get(const struct nftnl_obj *e, uint16_t type,
+ uint32_t *data_len)
+{
+ struct nftnl_obj_tunnel *tun = nftnl_obj_data(e);
+
+ switch (type) {
+ case NFTNL_OBJ_TUNNEL_ID:
+ *data_len = sizeof(tun->id);
+ return &tun->id;
+ case NFTNL_OBJ_TUNNEL_IPV4_SRC:
+ *data_len = sizeof(tun->src_v4);
+ return &tun->src_v4;
+ case NFTNL_OBJ_TUNNEL_IPV4_DST:
+ *data_len = sizeof(tun->dst_v4);
+ return &tun->dst_v4;
+ case NFTNL_OBJ_TUNNEL_IPV6_SRC:
+ *data_len = sizeof(tun->src_v6);
+ return &tun->src_v6;
+ case NFTNL_OBJ_TUNNEL_IPV6_DST:
+ *data_len = sizeof(tun->dst_v6);
+ return &tun->dst_v6;
+ case NFTNL_OBJ_TUNNEL_IPV6_FLOWLABEL:
+ *data_len = sizeof(tun->flowlabel);
+ return &tun->flowlabel;
+ case NFTNL_OBJ_TUNNEL_SPORT:
+ *data_len = sizeof(tun->sport);
+ return &tun->sport;
+ case NFTNL_OBJ_TUNNEL_DPORT:
+ *data_len = sizeof(tun->dport);
+ return &tun->dport;
+ case NFTNL_OBJ_TUNNEL_FLAGS:
+ *data_len = sizeof(tun->tun_flags);
+ return &tun->tun_flags;
+ case NFTNL_OBJ_TUNNEL_TOS:
+ *data_len = sizeof(tun->tun_tos);
+ return &tun->tun_tos;
+ case NFTNL_OBJ_TUNNEL_TTL:
+ *data_len = sizeof(tun->tun_ttl);
+ return &tun->tun_ttl;
+ case NFTNL_OBJ_TUNNEL_VXLAN_GBP:
+ *data_len = sizeof(tun->u.tun_vxlan.gbp);
+ return &tun->u.tun_vxlan.gbp;
+ case NFTNL_OBJ_TUNNEL_ERSPAN_VERSION:
+ *data_len = sizeof(tun->u.tun_erspan.version);
+ return &tun->u.tun_erspan.version;
+ case NFTNL_OBJ_TUNNEL_ERSPAN_V1_INDEX:
+ *data_len = sizeof(tun->u.tun_erspan.u.v1_index);
+ return &tun->u.tun_erspan.u.v1_index;
+ case NFTNL_OBJ_TUNNEL_ERSPAN_V2_HWID:
+ *data_len = sizeof(tun->u.tun_erspan.u.v2.hwid);
+ return &tun->u.tun_erspan.u.v2.hwid;
+ case NFTNL_OBJ_TUNNEL_ERSPAN_V2_DIR:
+ *data_len = sizeof(tun->u.tun_erspan.u.v2.dir);
+ return &tun->u.tun_erspan.u.v2.dir;
+ }
+ return NULL;
+}
+
+static int nftnl_obj_tunnel_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_TUNNEL_KEY_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_TUNNEL_KEY_ID:
+ case NFTA_TUNNEL_KEY_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_TUNNEL_KEY_IP:
+ case NFTA_TUNNEL_KEY_IP6:
+ case NFTA_TUNNEL_KEY_OPTS:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ abi_breakage();
+ break;
+ case NFTA_TUNNEL_KEY_SPORT:
+ case NFTA_TUNNEL_KEY_DPORT:
+ if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
+ abi_breakage();
+ break;
+ case NFTA_TUNNEL_KEY_TOS:
+ case NFTA_TUNNEL_KEY_TTL:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void
+nftnl_obj_tunnel_build(struct nlmsghdr *nlh, const struct nftnl_obj *e)
+{
+ struct nftnl_obj_tunnel *tun = nftnl_obj_data(e);
+ struct nlattr *nest;
+
+ if (e->flags & (1 << NFTNL_OBJ_TUNNEL_ID))
+ mnl_attr_put_u32(nlh, NFTA_TUNNEL_KEY_ID, htonl(tun->id));
+ if (e->flags & (1 << NFTNL_OBJ_TUNNEL_IPV4_SRC) ||
+ e->flags & (1 << NFTNL_OBJ_TUNNEL_IPV4_DST)) {
+ nest = mnl_attr_nest_start(nlh, NFTA_TUNNEL_KEY_IP);
+ if (e->flags & (1 << NFTNL_OBJ_TUNNEL_IPV4_SRC))
+ mnl_attr_put_u32(nlh, NFTA_TUNNEL_KEY_IP_SRC, tun->src_v4);
+ if (e->flags & (1 << NFTNL_OBJ_TUNNEL_IPV4_DST))
+ mnl_attr_put_u32(nlh, NFTA_TUNNEL_KEY_IP_DST, tun->dst_v4);
+ mnl_attr_nest_end(nlh, nest);
+ }
+ if (e->flags & (1 << NFTNL_OBJ_TUNNEL_IPV6_SRC) ||
+ e->flags & (1 << NFTNL_OBJ_TUNNEL_IPV6_DST)) {
+ nest = mnl_attr_nest_start(nlh, NFTA_TUNNEL_KEY_IP6);
+ if (e->flags & (1 << NFTNL_OBJ_TUNNEL_IPV6_SRC))
+ mnl_attr_put(nlh, NFTA_TUNNEL_KEY_IP6_SRC,
+ sizeof(tun->src_v6), &tun->src_v6);
+ if (e->flags & (1 << NFTNL_OBJ_TUNNEL_IPV6_DST))
+ mnl_attr_put(nlh, NFTA_TUNNEL_KEY_IP6_DST,
+ sizeof(tun->dst_v6), &tun->dst_v6);
+ if (e->flags & (1 << NFTNL_OBJ_TUNNEL_IPV6_FLOWLABEL))
+ mnl_attr_put_u32(nlh, NFTA_TUNNEL_KEY_IP6_FLOWLABEL,
+ htonl(tun->flowlabel));
+ mnl_attr_nest_end(nlh, nest);
+ }
+ if (e->flags & (1 << NFTNL_OBJ_TUNNEL_SPORT))
+ mnl_attr_put_u16(nlh, NFTA_TUNNEL_KEY_SPORT, htons(tun->sport));
+ if (e->flags & (1 << NFTNL_OBJ_TUNNEL_DPORT))
+ mnl_attr_put_u16(nlh, NFTA_TUNNEL_KEY_DPORT, htons(tun->dport));
+ if (e->flags & (1 << NFTNL_OBJ_TUNNEL_TOS))
+ mnl_attr_put_u8(nlh, NFTA_TUNNEL_KEY_TOS, tun->tun_tos);
+ if (e->flags & (1 << NFTNL_OBJ_TUNNEL_TTL))
+ mnl_attr_put_u8(nlh, NFTA_TUNNEL_KEY_TTL, tun->tun_ttl);
+ if (e->flags & (1 << NFTNL_OBJ_TUNNEL_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_TUNNEL_KEY_FLAGS, htonl(tun->tun_flags));
+ if (e->flags & (1 << NFTNL_OBJ_TUNNEL_VXLAN_GBP)) {
+ nest = mnl_attr_nest_start(nlh, NFTA_TUNNEL_KEY_OPTS);
+ mnl_attr_put_u32(nlh, NFTA_TUNNEL_KEY_VXLAN_GBP,
+ htonl(tun->u.tun_vxlan.gbp));
+ mnl_attr_nest_end(nlh, nest);
+ }
+ if (e->flags & (1 << NFTNL_OBJ_TUNNEL_ERSPAN_VERSION) &&
+ (e->flags & (1 << NFTNL_OBJ_TUNNEL_ERSPAN_V1_INDEX) ||
+ (e->flags & (1 << NFTNL_OBJ_TUNNEL_ERSPAN_V2_HWID) &&
+ e->flags & (1u << NFTNL_OBJ_TUNNEL_ERSPAN_V2_DIR)))) {
+ struct nlattr *nest_inner;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_TUNNEL_KEY_OPTS);
+ nest_inner = mnl_attr_nest_start(nlh, NFTA_TUNNEL_KEY_OPTS_ERSPAN);
+ mnl_attr_put_u32(nlh, NFTA_TUNNEL_KEY_ERSPAN_VERSION,
+ htonl(tun->u.tun_erspan.version));
+ if (e->flags & (1 << NFTNL_OBJ_TUNNEL_ERSPAN_V1_INDEX))
+ mnl_attr_put_u32(nlh, NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX,
+ htonl(tun->u.tun_erspan.u.v1_index));
+ if (e->flags & (1 << NFTNL_OBJ_TUNNEL_ERSPAN_V2_HWID))
+ mnl_attr_put_u8(nlh, NFTA_TUNNEL_KEY_ERSPAN_V2_HWID,
+ tun->u.tun_erspan.u.v2.hwid);
+ if (e->flags & (1u << NFTNL_OBJ_TUNNEL_ERSPAN_V2_DIR))
+ mnl_attr_put_u8(nlh, NFTA_TUNNEL_KEY_ERSPAN_V2_DIR,
+ tun->u.tun_erspan.u.v2.dir);
+ mnl_attr_nest_end(nlh, nest_inner);
+ mnl_attr_nest_end(nlh, nest);
+ }
+}
+
+static int nftnl_obj_tunnel_ip_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_TUNNEL_KEY_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_TUNNEL_KEY_IP_SRC:
+ case NFTA_TUNNEL_KEY_IP_DST:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int nftnl_obj_tunnel_parse_ip(struct nftnl_obj *e, struct nlattr *attr,
+ struct nftnl_obj_tunnel *tun)
+{
+ struct nlattr *tb[NFTA_TUNNEL_KEY_IP_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_obj_tunnel_ip_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_TUNNEL_KEY_IP_SRC]) {
+ tun->src_v4 = mnl_attr_get_u32(tb[NFTA_TUNNEL_KEY_IP_SRC]);
+ e->flags |= (1 << NFTNL_OBJ_TUNNEL_IPV4_SRC);
+ }
+ if (tb[NFTA_TUNNEL_KEY_IP_DST]) {
+ tun->dst_v4 = mnl_attr_get_u32(tb[NFTA_TUNNEL_KEY_IP_DST]);
+ e->flags |= (1 << NFTNL_OBJ_TUNNEL_IPV4_DST);
+ }
+
+ return 0;
+}
+
+static int nftnl_obj_tunnel_ip6_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_TUNNEL_KEY_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_TUNNEL_KEY_IP6_SRC:
+ case NFTA_TUNNEL_KEY_IP6_DST:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ abi_breakage();
+ break;
+ case NFTA_TUNNEL_KEY_IP6_FLOWLABEL:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int nftnl_obj_tunnel_parse_ip6(struct nftnl_obj *e, struct nlattr *attr,
+ struct nftnl_obj_tunnel *tun)
+{
+ struct nlattr *tb[NFTA_TUNNEL_KEY_IP6_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_obj_tunnel_ip6_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_TUNNEL_KEY_IP6_SRC]) {
+ memcpy(&tun->src_v6,
+ mnl_attr_get_payload(tb[NFTA_TUNNEL_KEY_IP6_SRC]),
+ sizeof(struct in6_addr));
+ e->flags |= (1 << NFTNL_OBJ_TUNNEL_IPV6_SRC);
+ }
+ if (tb[NFTA_TUNNEL_KEY_IP6_DST]) {
+ memcpy(&tun->dst_v6,
+ mnl_attr_get_payload(tb[NFTA_TUNNEL_KEY_IP6_DST]),
+ sizeof(struct in6_addr));
+ e->flags |= (1 << NFTNL_OBJ_TUNNEL_IPV6_DST);
+ }
+ if (tb[NFTA_TUNNEL_KEY_IP6_FLOWLABEL]) {
+ tun->flowlabel =
+ ntohl(mnl_attr_get_u32(tb[NFTA_TUNNEL_KEY_IP6_FLOWLABEL]));
+ e->flags |= (1 << NFTNL_OBJ_TUNNEL_IPV6_FLOWLABEL);
+ }
+
+ return 0;
+}
+
+static int nftnl_obj_tunnel_vxlan_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_TUNNEL_KEY_VXLAN_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_TUNNEL_KEY_VXLAN_GBP:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int
+nftnl_obj_tunnel_parse_vxlan(struct nftnl_obj *e, struct nlattr *attr,
+ struct nftnl_obj_tunnel *tun)
+{
+ struct nlattr *tb[NFTA_TUNNEL_KEY_VXLAN_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_obj_tunnel_vxlan_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_TUNNEL_KEY_VXLAN_GBP]) {
+ tun->u.tun_vxlan.gbp =
+ ntohl(mnl_attr_get_u32(tb[NFTA_TUNNEL_KEY_VXLAN_GBP]));
+ e->flags |= (1 << NFTNL_OBJ_TUNNEL_VXLAN_GBP);
+ }
+
+ return 0;
+}
+
+static int nftnl_obj_tunnel_erspan_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_TUNNEL_KEY_ERSPAN_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_TUNNEL_KEY_ERSPAN_VERSION:
+ case NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_TUNNEL_KEY_ERSPAN_V2_HWID:
+ case NFTA_TUNNEL_KEY_ERSPAN_V2_DIR:
+ if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int
+nftnl_obj_tunnel_parse_erspan(struct nftnl_obj *e, struct nlattr *attr,
+ struct nftnl_obj_tunnel *tun)
+{
+ struct nlattr *tb[NFTA_TUNNEL_KEY_ERSPAN_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(attr, nftnl_obj_tunnel_erspan_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_TUNNEL_KEY_ERSPAN_VERSION]) {
+ tun->u.tun_erspan.version =
+ ntohl(mnl_attr_get_u32(tb[NFTA_TUNNEL_KEY_ERSPAN_VERSION]));
+ e->flags |= (1 << NFTNL_OBJ_TUNNEL_ERSPAN_VERSION);
+ }
+ if (tb[NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX]) {
+ tun->u.tun_erspan.u.v1_index =
+ ntohl(mnl_attr_get_u32(tb[NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX]));
+ e->flags |= (1 << NFTNL_OBJ_TUNNEL_ERSPAN_V1_INDEX);
+ }
+ if (tb[NFTA_TUNNEL_KEY_ERSPAN_V2_HWID]) {
+ tun->u.tun_erspan.u.v2.hwid =
+ mnl_attr_get_u8(tb[NFTA_TUNNEL_KEY_ERSPAN_V2_HWID]);
+ e->flags |= (1 << NFTNL_OBJ_TUNNEL_ERSPAN_V2_HWID);
+ }
+ if (tb[NFTA_TUNNEL_KEY_ERSPAN_V2_DIR]) {
+ tun->u.tun_erspan.u.v2.dir =
+ mnl_attr_get_u8(tb[NFTA_TUNNEL_KEY_ERSPAN_V2_DIR]);
+ e->flags |= (1u << NFTNL_OBJ_TUNNEL_ERSPAN_V2_DIR);
+ }
+
+ return 0;
+}
+
+static int nftnl_obj_tunnel_opts_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_TUNNEL_KEY_OPTS_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_TUNNEL_KEY_OPTS_VXLAN:
+ case NFTA_TUNNEL_KEY_OPTS_ERSPAN:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int
+nftnl_obj_tunnel_parse_opts(struct nftnl_obj *e, struct nlattr *attr,
+ struct nftnl_obj_tunnel *tun)
+{
+ struct nlattr *tb[NFTA_TUNNEL_KEY_OPTS_MAX + 1] = {};
+ int err = 0;
+
+ if (mnl_attr_parse_nested(attr, nftnl_obj_tunnel_opts_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_TUNNEL_KEY_OPTS_VXLAN]) {
+ err = nftnl_obj_tunnel_parse_vxlan(e, tb[NFTA_TUNNEL_KEY_OPTS_VXLAN],
+ tun);
+ } else if (tb[NFTA_TUNNEL_KEY_OPTS_ERSPAN]) {
+ err = nftnl_obj_tunnel_parse_erspan(e, tb[NFTA_TUNNEL_KEY_OPTS_ERSPAN],
+ tun);
+ }
+
+ return err;
+}
+
+static int
+nftnl_obj_tunnel_parse(struct nftnl_obj *e, struct nlattr *attr)
+{
+ struct nftnl_obj_tunnel *tun = nftnl_obj_data(e);
+ struct nlattr *tb[NFTA_TUNNEL_KEY_MAX + 1] = {};
+ int err;
+
+ if (mnl_attr_parse_nested(attr, nftnl_obj_tunnel_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_TUNNEL_KEY_ID]) {
+ tun->id = ntohl(mnl_attr_get_u32(tb[NFTA_TUNNEL_KEY_ID]));
+ e->flags |= (1 << NFTNL_OBJ_TUNNEL_ID);
+ }
+ if (tb[NFTA_TUNNEL_KEY_IP]) {
+ err = nftnl_obj_tunnel_parse_ip(e, tb[NFTA_TUNNEL_KEY_IP], tun);
+ if (err < 0)
+ return err;
+ } else if (tb[NFTA_TUNNEL_KEY_IP6]) {
+ err = nftnl_obj_tunnel_parse_ip6(e, tb[NFTA_TUNNEL_KEY_IP6], tun);
+ if (err < 0)
+ return err;
+ }
+
+ if (tb[NFTA_TUNNEL_KEY_SPORT]) {
+ tun->sport = ntohs(mnl_attr_get_u16(tb[NFTA_TUNNEL_KEY_SPORT]));
+ e->flags |= (1 << NFTNL_OBJ_TUNNEL_SPORT);
+ }
+ if (tb[NFTA_TUNNEL_KEY_DPORT]) {
+ tun->dport = ntohs(mnl_attr_get_u16(tb[NFTA_TUNNEL_KEY_DPORT]));
+ e->flags |= (1 << NFTNL_OBJ_TUNNEL_DPORT);
+ }
+ if (tb[NFTA_TUNNEL_KEY_TOS]) {
+ tun->tun_tos = mnl_attr_get_u8(tb[NFTA_TUNNEL_KEY_TOS]);
+ e->flags |= (1 << NFTNL_OBJ_TUNNEL_TOS);
+ }
+ if (tb[NFTA_TUNNEL_KEY_TTL]) {
+ tun->tun_ttl = mnl_attr_get_u8(tb[NFTA_TUNNEL_KEY_TTL]);
+ e->flags |= (1 << NFTNL_OBJ_TUNNEL_TTL);
+ }
+ if (tb[NFTA_TUNNEL_KEY_FLAGS]) {
+ tun->tun_flags = mnl_attr_get_u8(tb[NFTA_TUNNEL_KEY_FLAGS]);
+ e->flags |= (1 << NFTNL_OBJ_TUNNEL_FLAGS);
+ }
+ if (tb[NFTA_TUNNEL_KEY_OPTS]) {
+ err = nftnl_obj_tunnel_parse_opts(e, tb[NFTA_TUNNEL_KEY_OPTS], tun);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int nftnl_obj_tunnel_snprintf(char *buf, size_t len,
+ uint32_t flags, const struct nftnl_obj *e)
+{
+ struct nftnl_obj_tunnel *tun = nftnl_obj_data(e);
+
+ return snprintf(buf, len, "id %u ", tun->id);
+}
+
+struct obj_ops obj_ops_tunnel = {
+ .name = "tunnel",
+ .type = NFT_OBJECT_TUNNEL,
+ .alloc_len = sizeof(struct nftnl_obj_tunnel),
+ .max_attr = NFTA_TUNNEL_KEY_MAX,
+ .set = nftnl_obj_tunnel_set,
+ .get = nftnl_obj_tunnel_get,
+ .parse = nftnl_obj_tunnel_parse,
+ .build = nftnl_obj_tunnel_build,
+ .output = nftnl_obj_tunnel_snprintf,
+};
diff --git a/src/object.c b/src/object.c
new file mode 100644
index 0000000..232b97a
--- /dev/null
+++ b/src/object.c
@@ -0,0 +1,562 @@
+/*
+ * (C) 2012-2016 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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 "internal.h"
+
+#include <time.h>
+#include <endian.h>
+#include <stdint.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <errno.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftnl/object.h>
+#include "obj.h"
+
+static struct obj_ops *obj_ops[__NFT_OBJECT_MAX] = {
+ [NFT_OBJECT_COUNTER] = &obj_ops_counter,
+ [NFT_OBJECT_QUOTA] = &obj_ops_quota,
+ [NFT_OBJECT_CT_HELPER] = &obj_ops_ct_helper,
+ [NFT_OBJECT_LIMIT] = &obj_ops_limit,
+ [NFT_OBJECT_TUNNEL] = &obj_ops_tunnel,
+ [NFT_OBJECT_CT_TIMEOUT] = &obj_ops_ct_timeout,
+ [NFT_OBJECT_SECMARK] = &obj_ops_secmark,
+ [NFT_OBJECT_CT_EXPECT] = &obj_ops_ct_expect,
+ [NFT_OBJECT_SYNPROXY] = &obj_ops_synproxy,
+};
+
+static struct obj_ops *nftnl_obj_ops_lookup(uint32_t type)
+{
+ if (type > NFT_OBJECT_MAX)
+ return NULL;
+
+ return obj_ops[type];
+}
+
+EXPORT_SYMBOL(nftnl_obj_alloc);
+struct nftnl_obj *nftnl_obj_alloc(void)
+{
+ return calloc(1, sizeof(struct nftnl_obj));
+}
+
+EXPORT_SYMBOL(nftnl_obj_free);
+void nftnl_obj_free(const struct nftnl_obj *obj)
+{
+ if (obj->flags & (1 << NFTNL_OBJ_TABLE))
+ xfree(obj->table);
+ if (obj->flags & (1 << NFTNL_OBJ_NAME))
+ xfree(obj->name);
+ if (obj->flags & (1 << NFTNL_OBJ_USERDATA))
+ xfree(obj->user.data);
+
+ xfree(obj);
+}
+
+EXPORT_SYMBOL(nftnl_obj_is_set);
+bool nftnl_obj_is_set(const struct nftnl_obj *obj, uint16_t attr)
+{
+ return obj->flags & (1 << attr);
+}
+
+static uint32_t nftnl_obj_validate[NFTNL_OBJ_MAX + 1] = {
+ [NFTNL_OBJ_FAMILY] = sizeof(uint32_t),
+ [NFTNL_OBJ_USE] = sizeof(uint32_t),
+ [NFTNL_OBJ_HANDLE] = sizeof(uint64_t),
+};
+
+EXPORT_SYMBOL(nftnl_obj_set_data);
+void nftnl_obj_set_data(struct nftnl_obj *obj, uint16_t attr,
+ const void *data, uint32_t data_len)
+{
+ if (attr < NFTNL_OBJ_MAX)
+ nftnl_assert_validate(data, nftnl_obj_validate, attr, data_len);
+
+ switch (attr) {
+ case NFTNL_OBJ_TABLE:
+ xfree(obj->table);
+ obj->table = strdup(data);
+ break;
+ case NFTNL_OBJ_NAME:
+ xfree(obj->name);
+ obj->name = strdup(data);
+ break;
+ case NFTNL_OBJ_TYPE:
+ obj->ops = nftnl_obj_ops_lookup(*((uint32_t *)data));
+ if (!obj->ops)
+ return;
+ break;
+ case NFTNL_OBJ_FAMILY:
+ memcpy(&obj->family, data, sizeof(obj->family));
+ break;
+ case NFTNL_OBJ_USE:
+ memcpy(&obj->use, data, sizeof(obj->use));
+ break;
+ case NFTNL_OBJ_HANDLE:
+ memcpy(&obj->handle, data, sizeof(obj->handle));
+ break;
+ case NFTNL_OBJ_USERDATA:
+ if (obj->flags & (1 << NFTNL_OBJ_USERDATA))
+ xfree(obj->user.data);
+
+ obj->user.data = malloc(data_len);
+ if (!obj->user.data)
+ return;
+ memcpy(obj->user.data, data, data_len);
+ obj->user.len = data_len;
+ break;
+ default:
+ if (obj->ops)
+ obj->ops->set(obj, attr, data, data_len);
+ break;
+ }
+ obj->flags |= (1 << attr);
+}
+
+void nftnl_obj_set(struct nftnl_obj *obj, uint16_t attr, const void *data) __visible;
+void nftnl_obj_set(struct nftnl_obj *obj, uint16_t attr, const void *data)
+{
+ nftnl_obj_set_data(obj, attr, data, nftnl_obj_validate[attr]);
+}
+
+EXPORT_SYMBOL(nftnl_obj_set_u8);
+void nftnl_obj_set_u8(struct nftnl_obj *obj, uint16_t attr, uint8_t val)
+{
+ nftnl_obj_set_data(obj, attr, &val, sizeof(uint8_t));
+}
+
+EXPORT_SYMBOL(nftnl_obj_set_u16);
+void nftnl_obj_set_u16(struct nftnl_obj *obj, uint16_t attr, uint16_t val)
+{
+ nftnl_obj_set_data(obj, attr, &val, sizeof(uint16_t));
+}
+
+EXPORT_SYMBOL(nftnl_obj_set_u32);
+void nftnl_obj_set_u32(struct nftnl_obj *obj, uint16_t attr, uint32_t val)
+{
+ nftnl_obj_set_data(obj, attr, &val, sizeof(uint32_t));
+}
+
+EXPORT_SYMBOL(nftnl_obj_set_u64);
+void nftnl_obj_set_u64(struct nftnl_obj *obj, uint16_t attr, uint64_t val)
+{
+ nftnl_obj_set_data(obj, attr, &val, sizeof(uint64_t));
+}
+
+EXPORT_SYMBOL(nftnl_obj_set_str);
+void nftnl_obj_set_str(struct nftnl_obj *obj, uint16_t attr, const char *str)
+{
+ nftnl_obj_set_data(obj, attr, str, 0);
+}
+
+EXPORT_SYMBOL(nftnl_obj_get_data);
+const void *nftnl_obj_get_data(struct nftnl_obj *obj, uint16_t attr,
+ uint32_t *data_len)
+{
+ if (!(obj->flags & (1 << attr)))
+ return NULL;
+
+ switch(attr) {
+ case NFTNL_OBJ_TABLE:
+ return obj->table;
+ case NFTNL_OBJ_NAME:
+ return obj->name;
+ case NFTNL_OBJ_TYPE:
+ if (!obj->ops)
+ return NULL;
+
+ *data_len = sizeof(uint32_t);
+ return &obj->ops->type;
+ case NFTNL_OBJ_FAMILY:
+ *data_len = sizeof(uint32_t);
+ return &obj->family;
+ case NFTNL_OBJ_USE:
+ *data_len = sizeof(uint32_t);
+ return &obj->use;
+ case NFTNL_OBJ_HANDLE:
+ *data_len = sizeof(uint64_t);
+ return &obj->handle;
+ case NFTNL_OBJ_USERDATA:
+ *data_len = obj->user.len;
+ return obj->user.data;
+ default:
+ if (obj->ops)
+ return obj->ops->get(obj, attr, data_len);
+ break;
+ }
+ return NULL;
+}
+
+EXPORT_SYMBOL(nftnl_obj_get);
+const void *nftnl_obj_get(struct nftnl_obj *obj, uint16_t attr)
+{
+ uint32_t data_len;
+ return nftnl_obj_get_data(obj, attr, &data_len);
+}
+
+EXPORT_SYMBOL(nftnl_obj_get_u8);
+uint8_t nftnl_obj_get_u8(struct nftnl_obj *obj, uint16_t attr)
+{
+ const void *ret = nftnl_obj_get(obj, attr);
+ return ret == NULL ? 0 : *((uint8_t *)ret);
+}
+
+EXPORT_SYMBOL(nftnl_obj_get_u16);
+uint16_t nftnl_obj_get_u16(struct nftnl_obj *obj, uint16_t attr)
+{
+ const void *ret = nftnl_obj_get(obj, attr);
+ return ret == NULL ? 0 : *((uint16_t *)ret);
+}
+
+EXPORT_SYMBOL(nftnl_obj_get_u32);
+uint32_t nftnl_obj_get_u32(struct nftnl_obj *obj, uint16_t attr)
+{
+ const void *ret = nftnl_obj_get(obj, attr);
+ return ret == NULL ? 0 : *((uint32_t *)ret);
+}
+
+EXPORT_SYMBOL(nftnl_obj_get_u64);
+uint64_t nftnl_obj_get_u64(struct nftnl_obj *obj, uint16_t attr)
+{
+ const void *ret = nftnl_obj_get(obj, attr);
+ return ret == NULL ? 0 : *((uint64_t *)ret);
+}
+
+EXPORT_SYMBOL(nftnl_obj_get_str);
+const char *nftnl_obj_get_str(struct nftnl_obj *obj, uint16_t attr)
+{
+ return nftnl_obj_get(obj, attr);
+}
+
+EXPORT_SYMBOL(nftnl_obj_nlmsg_build_payload);
+void nftnl_obj_nlmsg_build_payload(struct nlmsghdr *nlh,
+ const struct nftnl_obj *obj)
+{
+ if (obj->flags & (1 << NFTNL_OBJ_TABLE))
+ mnl_attr_put_strz(nlh, NFTA_OBJ_TABLE, obj->table);
+ if (obj->flags & (1 << NFTNL_OBJ_NAME))
+ mnl_attr_put_strz(nlh, NFTA_OBJ_NAME, obj->name);
+ if (obj->flags & (1 << NFTNL_OBJ_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_OBJ_TYPE, htonl(obj->ops->type));
+ if (obj->flags & (1 << NFTNL_OBJ_HANDLE))
+ mnl_attr_put_u64(nlh, NFTA_OBJ_HANDLE, htobe64(obj->handle));
+ if (obj->flags & (1 << NFTNL_OBJ_USERDATA))
+ mnl_attr_put(nlh, NFTA_OBJ_USERDATA, obj->user.len, obj->user.data);
+ if (obj->ops) {
+ struct nlattr *nest = mnl_attr_nest_start(nlh, NFTA_OBJ_DATA);
+
+ obj->ops->build(nlh, obj);
+ mnl_attr_nest_end(nlh, nest);
+ }
+}
+
+static int nftnl_obj_parse_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_OBJ_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_OBJ_TABLE:
+ case NFTA_OBJ_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ case NFTA_OBJ_HANDLE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ case NFTA_OBJ_DATA:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ abi_breakage();
+ break;
+ case NFTA_OBJ_USE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_OBJ_USERDATA:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+EXPORT_SYMBOL(nftnl_obj_nlmsg_parse);
+int nftnl_obj_nlmsg_parse(const struct nlmsghdr *nlh, struct nftnl_obj *obj)
+{
+ struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *tb[NFTA_OBJ_MAX + 1] = {};
+ int err;
+
+ if (mnl_attr_parse(nlh, sizeof(*nfg), nftnl_obj_parse_attr_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_OBJ_TABLE]) {
+ obj->table = strdup(mnl_attr_get_str(tb[NFTA_OBJ_TABLE]));
+ obj->flags |= (1 << NFTNL_OBJ_TABLE);
+ }
+ if (tb[NFTA_OBJ_NAME]) {
+ obj->name = strdup(mnl_attr_get_str(tb[NFTA_OBJ_NAME]));
+ obj->flags |= (1 << NFTNL_OBJ_NAME);
+ }
+ if (tb[NFTA_OBJ_TYPE]) {
+ uint32_t type = ntohl(mnl_attr_get_u32(tb[NFTA_OBJ_TYPE]));
+
+ obj->ops = nftnl_obj_ops_lookup(type);
+ if (obj->ops)
+ obj->flags |= (1 << NFTNL_OBJ_TYPE);
+ }
+ if (tb[NFTA_OBJ_DATA]) {
+ if (obj->ops) {
+ err = obj->ops->parse(obj, tb[NFTA_OBJ_DATA]);
+ if (err < 0)
+ return err;
+ }
+ }
+ if (tb[NFTA_OBJ_USE]) {
+ obj->use = ntohl(mnl_attr_get_u32(tb[NFTA_OBJ_USE]));
+ obj->flags |= (1 << NFTNL_OBJ_USE);
+ }
+ if (tb[NFTA_OBJ_HANDLE]) {
+ obj->handle = be64toh(mnl_attr_get_u64(tb[NFTA_OBJ_HANDLE]));
+ obj->flags |= (1 << NFTNL_OBJ_HANDLE);
+ }
+ if (tb[NFTA_OBJ_USERDATA]) {
+ nftnl_obj_set_data(obj, NFTNL_OBJ_USERDATA,
+ mnl_attr_get_payload(tb[NFTA_OBJ_USERDATA]),
+ mnl_attr_get_payload_len(tb[NFTA_OBJ_USERDATA]));
+ }
+
+ obj->family = nfg->nfgen_family;
+ obj->flags |= (1 << NFTNL_OBJ_FAMILY);
+
+ return 0;
+}
+
+static int nftnl_obj_do_parse(struct nftnl_obj *obj, enum nftnl_parse_type type,
+ const void *data, struct nftnl_parse_err *err,
+ enum nftnl_parse_input input)
+{
+ struct nftnl_parse_err perr = {};
+ int ret;
+
+ switch (type) {
+ case NFTNL_PARSE_JSON:
+ case NFTNL_PARSE_XML:
+ default:
+ ret = -1;
+ errno = EOPNOTSUPP;
+ break;
+ }
+
+ if (err != NULL)
+ *err = perr;
+
+ return ret;
+}
+
+EXPORT_SYMBOL(nftnl_obj_parse);
+int nftnl_obj_parse(struct nftnl_obj *obj, enum nftnl_parse_type type,
+ const char *data, struct nftnl_parse_err *err)
+{
+ return nftnl_obj_do_parse(obj, type, data, err, NFTNL_PARSE_BUFFER);
+}
+
+EXPORT_SYMBOL(nftnl_obj_parse_file);
+int nftnl_obj_parse_file(struct nftnl_obj *obj, enum nftnl_parse_type type,
+ FILE *fp, struct nftnl_parse_err *err)
+{
+ return nftnl_obj_do_parse(obj, type, fp, err, NFTNL_PARSE_FILE);
+}
+
+static int nftnl_obj_snprintf_dflt(char *buf, size_t remain,
+ const struct nftnl_obj *obj,
+ uint32_t type, uint32_t flags)
+{
+ const char *name = obj->ops ? obj->ops->name : "(unknown)";
+ int ret, offset = 0;
+
+ ret = snprintf(buf, remain, "table %s name %s use %u [ %s ",
+ obj->table, obj->name, obj->use, name);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (obj->ops) {
+ ret = obj->ops->output(buf + offset, remain, flags, obj);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+ ret = snprintf(buf + offset, remain, "]");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ return offset;
+}
+
+static int nftnl_obj_cmd_snprintf(char *buf, size_t remain,
+ const struct nftnl_obj *obj, uint32_t cmd,
+ uint32_t type, uint32_t flags)
+{
+ int ret, offset = 0;
+
+ if (type != NFTNL_OUTPUT_DEFAULT)
+ return -1;
+
+ ret = nftnl_obj_snprintf_dflt(buf + offset, remain, obj, type, flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ return offset;
+}
+
+EXPORT_SYMBOL(nftnl_obj_snprintf);
+int nftnl_obj_snprintf(char *buf, size_t size, const struct nftnl_obj *obj,
+ uint32_t type, uint32_t flags)
+{
+ if (size)
+ buf[0] = '\0';
+
+ return nftnl_obj_cmd_snprintf(buf, size, obj, nftnl_flag2cmd(flags),
+ type, flags);
+}
+
+static int nftnl_obj_do_snprintf(char *buf, size_t size, const void *obj,
+ uint32_t cmd, uint32_t type, uint32_t flags)
+{
+ return nftnl_obj_snprintf(buf, size, obj, type, flags);
+}
+
+EXPORT_SYMBOL(nftnl_obj_fprintf);
+int nftnl_obj_fprintf(FILE *fp, const struct nftnl_obj *obj, uint32_t type,
+ uint32_t flags)
+{
+ return nftnl_fprintf(fp, obj, NFTNL_CMD_UNSPEC, type, flags,
+ nftnl_obj_do_snprintf);
+}
+
+struct nftnl_obj_list {
+ struct list_head list;
+};
+
+EXPORT_SYMBOL(nftnl_obj_list_alloc);
+struct nftnl_obj_list *nftnl_obj_list_alloc(void)
+{
+ struct nftnl_obj_list *list;
+
+ list = calloc(1, sizeof(struct nftnl_obj_list));
+ if (list == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&list->list);
+
+ return list;
+}
+
+EXPORT_SYMBOL(nftnl_obj_list_free);
+void nftnl_obj_list_free(struct nftnl_obj_list *list)
+{
+ struct nftnl_obj *r, *tmp;
+
+ list_for_each_entry_safe(r, tmp, &list->list, head) {
+ list_del(&r->head);
+ nftnl_obj_free(r);
+ }
+ xfree(list);
+}
+
+EXPORT_SYMBOL(nftnl_obj_list_is_empty);
+int nftnl_obj_list_is_empty(struct nftnl_obj_list *list)
+{
+ return list_empty(&list->list);
+}
+
+EXPORT_SYMBOL(nftnl_obj_list_add);
+void nftnl_obj_list_add(struct nftnl_obj *r, struct nftnl_obj_list *list)
+{
+ list_add(&r->head, &list->list);
+}
+
+EXPORT_SYMBOL(nftnl_obj_list_add_tail);
+void nftnl_obj_list_add_tail(struct nftnl_obj *r,
+ struct nftnl_obj_list *list)
+{
+ list_add_tail(&r->head, &list->list);
+}
+
+EXPORT_SYMBOL(nftnl_obj_list_del);
+void nftnl_obj_list_del(struct nftnl_obj *t)
+{
+ list_del(&t->head);
+}
+
+EXPORT_SYMBOL(nftnl_obj_list_foreach);
+int nftnl_obj_list_foreach(struct nftnl_obj_list *table_list,
+ int (*cb)(struct nftnl_obj *t, void *data),
+ void *data)
+{
+ struct nftnl_obj *cur, *tmp;
+ int ret;
+
+ list_for_each_entry_safe(cur, tmp, &table_list->list, head) {
+ ret = cb(cur, data);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+struct nftnl_obj_list_iter {
+ struct nftnl_obj_list *list;
+ struct nftnl_obj *cur;
+};
+
+EXPORT_SYMBOL(nftnl_obj_list_iter_create);
+struct nftnl_obj_list_iter *
+nftnl_obj_list_iter_create(struct nftnl_obj_list *l)
+{
+ struct nftnl_obj_list_iter *iter;
+
+ iter = calloc(1, sizeof(struct nftnl_obj_list_iter));
+ if (iter == NULL)
+ return NULL;
+
+ iter->list = l;
+ if (nftnl_obj_list_is_empty(l))
+ iter->cur = NULL;
+ else
+ iter->cur = list_entry(l->list.next, struct nftnl_obj, head);
+
+ return iter;
+}
+
+EXPORT_SYMBOL(nftnl_obj_list_iter_next);
+struct nftnl_obj *nftnl_obj_list_iter_next(struct nftnl_obj_list_iter *iter)
+{
+ struct nftnl_obj *r = iter->cur;
+
+ if (r == NULL)
+ return NULL;
+
+ /* get next table, if any */
+ iter->cur = list_entry(iter->cur->head.next, struct nftnl_obj, head);
+ if (&iter->cur->head == iter->list->list.next)
+ return NULL;
+
+ return r;
+}
+
+EXPORT_SYMBOL(nftnl_obj_list_iter_destroy);
+void nftnl_obj_list_iter_destroy(struct nftnl_obj_list_iter *iter)
+{
+ xfree(iter);
+}
diff --git a/src/rule.c b/src/rule.c
new file mode 100644
index 0000000..a52012b
--- /dev/null
+++ b/src/rule.c
@@ -0,0 +1,876 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+#include "internal.h"
+
+#include <time.h>
+#include <endian.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <ctype.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftnl/rule.h>
+#include <libnftnl/set.h>
+#include <libnftnl/expr.h>
+
+EXPORT_SYMBOL(nftnl_rule_alloc);
+struct nftnl_rule *nftnl_rule_alloc(void)
+{
+ struct nftnl_rule *r;
+
+ r = calloc(1, sizeof(struct nftnl_rule));
+ if (r == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&r->expr_list);
+
+ return r;
+}
+
+EXPORT_SYMBOL(nftnl_rule_free);
+void nftnl_rule_free(const struct nftnl_rule *r)
+{
+ struct nftnl_expr *e, *tmp;
+
+ list_for_each_entry_safe(e, tmp, &r->expr_list, head)
+ nftnl_expr_free(e);
+
+ if (r->flags & (1 << (NFTNL_RULE_TABLE)))
+ xfree(r->table);
+ if (r->flags & (1 << (NFTNL_RULE_CHAIN)))
+ xfree(r->chain);
+ if (r->flags & (1 << (NFTNL_RULE_USERDATA)))
+ xfree(r->user.data);
+
+ xfree(r);
+}
+
+EXPORT_SYMBOL(nftnl_rule_is_set);
+bool nftnl_rule_is_set(const struct nftnl_rule *r, uint16_t attr)
+{
+ return r->flags & (1 << attr);
+}
+
+EXPORT_SYMBOL(nftnl_rule_unset);
+void nftnl_rule_unset(struct nftnl_rule *r, uint16_t attr)
+{
+ if (!(r->flags & (1 << attr)))
+ return;
+
+ switch (attr) {
+ case NFTNL_RULE_TABLE:
+ xfree(r->table);
+ break;
+ case NFTNL_RULE_CHAIN:
+ xfree(r->chain);
+ break;
+ case NFTNL_RULE_HANDLE:
+ case NFTNL_RULE_COMPAT_PROTO:
+ case NFTNL_RULE_COMPAT_FLAGS:
+ case NFTNL_RULE_POSITION:
+ case NFTNL_RULE_FAMILY:
+ case NFTNL_RULE_ID:
+ case NFTNL_RULE_POSITION_ID:
+ break;
+ case NFTNL_RULE_USERDATA:
+ xfree(r->user.data);
+ break;
+ }
+
+ r->flags &= ~(1 << attr);
+}
+
+static uint32_t nftnl_rule_validate[NFTNL_RULE_MAX + 1] = {
+ [NFTNL_RULE_HANDLE] = sizeof(uint64_t),
+ [NFTNL_RULE_COMPAT_PROTO] = sizeof(uint32_t),
+ [NFTNL_RULE_COMPAT_FLAGS] = sizeof(uint32_t),
+ [NFTNL_RULE_FAMILY] = sizeof(uint32_t),
+ [NFTNL_RULE_POSITION] = sizeof(uint64_t),
+ [NFTNL_RULE_ID] = sizeof(uint32_t),
+ [NFTNL_RULE_POSITION_ID] = sizeof(uint32_t),
+};
+
+EXPORT_SYMBOL(nftnl_rule_set_data);
+int nftnl_rule_set_data(struct nftnl_rule *r, uint16_t attr,
+ const void *data, uint32_t data_len)
+{
+ nftnl_assert_attr_exists(attr, NFTNL_RULE_MAX);
+ nftnl_assert_validate(data, nftnl_rule_validate, attr, data_len);
+
+ switch(attr) {
+ case NFTNL_RULE_TABLE:
+ if (r->flags & (1 << NFTNL_RULE_TABLE))
+ xfree(r->table);
+
+ r->table = strdup(data);
+ if (!r->table)
+ return -1;
+ break;
+ case NFTNL_RULE_CHAIN:
+ if (r->flags & (1 << NFTNL_RULE_CHAIN))
+ xfree(r->chain);
+
+ r->chain = strdup(data);
+ if (!r->chain)
+ return -1;
+ break;
+ case NFTNL_RULE_HANDLE:
+ memcpy(&r->handle, data, sizeof(r->handle));
+ break;
+ case NFTNL_RULE_COMPAT_PROTO:
+ memcpy(&r->compat.proto, data, sizeof(r->compat.proto));
+ break;
+ case NFTNL_RULE_COMPAT_FLAGS:
+ memcpy(&r->compat.flags, data, sizeof(r->compat.flags));
+ break;
+ case NFTNL_RULE_FAMILY:
+ memcpy(&r->family, data, sizeof(r->family));
+ break;
+ case NFTNL_RULE_POSITION:
+ memcpy(&r->position, data, sizeof(r->position));
+ break;
+ case NFTNL_RULE_USERDATA:
+ if (r->flags & (1 << NFTNL_RULE_USERDATA))
+ xfree(r->user.data);
+
+ r->user.data = malloc(data_len);
+ if (!r->user.data)
+ return -1;
+
+ memcpy(r->user.data, data, data_len);
+ r->user.len = data_len;
+ break;
+ case NFTNL_RULE_ID:
+ memcpy(&r->id, data, sizeof(r->id));
+ break;
+ case NFTNL_RULE_POSITION_ID:
+ memcpy(&r->position_id, data, sizeof(r->position_id));
+ break;
+ }
+ r->flags |= (1 << attr);
+ return 0;
+}
+
+int nftnl_rule_set(struct nftnl_rule *r, uint16_t attr, const void *data) __visible;
+int nftnl_rule_set(struct nftnl_rule *r, uint16_t attr, const void *data)
+{
+ return nftnl_rule_set_data(r, attr, data, nftnl_rule_validate[attr]);
+}
+
+EXPORT_SYMBOL(nftnl_rule_set_u32);
+void nftnl_rule_set_u32(struct nftnl_rule *r, uint16_t attr, uint32_t val)
+{
+ nftnl_rule_set_data(r, attr, &val, sizeof(uint32_t));
+}
+
+EXPORT_SYMBOL(nftnl_rule_set_u64);
+void nftnl_rule_set_u64(struct nftnl_rule *r, uint16_t attr, uint64_t val)
+{
+ nftnl_rule_set_data(r, attr, &val, sizeof(uint64_t));
+}
+
+EXPORT_SYMBOL(nftnl_rule_set_str);
+int nftnl_rule_set_str(struct nftnl_rule *r, uint16_t attr, const char *str)
+{
+ return nftnl_rule_set_data(r, attr, str, strlen(str) + 1);
+}
+
+EXPORT_SYMBOL(nftnl_rule_get_data);
+const void *nftnl_rule_get_data(const struct nftnl_rule *r, uint16_t attr,
+ uint32_t *data_len)
+{
+ if (!(r->flags & (1 << attr)))
+ return NULL;
+
+ switch(attr) {
+ case NFTNL_RULE_FAMILY:
+ *data_len = sizeof(uint32_t);
+ return &r->family;
+ case NFTNL_RULE_TABLE:
+ *data_len = strlen(r->table) + 1;
+ return r->table;
+ case NFTNL_RULE_CHAIN:
+ *data_len = strlen(r->chain) + 1;
+ return r->chain;
+ case NFTNL_RULE_HANDLE:
+ *data_len = sizeof(uint64_t);
+ return &r->handle;
+ case NFTNL_RULE_COMPAT_PROTO:
+ *data_len = sizeof(uint32_t);
+ return &r->compat.proto;
+ case NFTNL_RULE_COMPAT_FLAGS:
+ *data_len = sizeof(uint32_t);
+ return &r->compat.flags;
+ case NFTNL_RULE_POSITION:
+ *data_len = sizeof(uint64_t);
+ return &r->position;
+ case NFTNL_RULE_USERDATA:
+ *data_len = r->user.len;
+ return r->user.data;
+ case NFTNL_RULE_ID:
+ *data_len = sizeof(uint32_t);
+ return &r->id;
+ case NFTNL_RULE_POSITION_ID:
+ *data_len = sizeof(uint32_t);
+ return &r->position_id;
+ }
+ return NULL;
+}
+
+EXPORT_SYMBOL(nftnl_rule_get);
+const void *nftnl_rule_get(const struct nftnl_rule *r, uint16_t attr)
+{
+ uint32_t data_len;
+ return nftnl_rule_get_data(r, attr, &data_len);
+}
+
+EXPORT_SYMBOL(nftnl_rule_get_str);
+const char *nftnl_rule_get_str(const struct nftnl_rule *r, uint16_t attr)
+{
+ return nftnl_rule_get(r, attr);
+}
+
+EXPORT_SYMBOL(nftnl_rule_get_u32);
+uint32_t nftnl_rule_get_u32(const struct nftnl_rule *r, uint16_t attr)
+{
+ uint32_t data_len;
+ const uint32_t *val = nftnl_rule_get_data(r, attr, &data_len);
+
+ nftnl_assert(val, attr, data_len == sizeof(uint32_t));
+
+ return val ? *val : 0;
+}
+
+EXPORT_SYMBOL(nftnl_rule_get_u64);
+uint64_t nftnl_rule_get_u64(const struct nftnl_rule *r, uint16_t attr)
+{
+ uint32_t data_len;
+ const uint64_t *val = nftnl_rule_get_data(r, attr, &data_len);
+
+ nftnl_assert(val, attr, data_len == sizeof(uint64_t));
+
+ return val ? *val : 0;
+}
+
+EXPORT_SYMBOL(nftnl_rule_get_u8);
+uint8_t nftnl_rule_get_u8(const struct nftnl_rule *r, uint16_t attr)
+{
+ uint32_t data_len;
+ const uint8_t *val = nftnl_rule_get_data(r, attr, &data_len);
+
+ nftnl_assert(val, attr, data_len == sizeof(uint8_t));
+
+ return val ? *val : 0;
+}
+
+EXPORT_SYMBOL(nftnl_rule_nlmsg_build_payload);
+void nftnl_rule_nlmsg_build_payload(struct nlmsghdr *nlh, struct nftnl_rule *r)
+{
+ struct nftnl_expr *expr;
+ struct nlattr *nest, *nest2;
+
+ if (r->flags & (1 << NFTNL_RULE_TABLE))
+ mnl_attr_put_strz(nlh, NFTA_RULE_TABLE, r->table);
+ if (r->flags & (1 << NFTNL_RULE_CHAIN))
+ mnl_attr_put_strz(nlh, NFTA_RULE_CHAIN, r->chain);
+ if (r->flags & (1 << NFTNL_RULE_HANDLE))
+ mnl_attr_put_u64(nlh, NFTA_RULE_HANDLE, htobe64(r->handle));
+ if (r->flags & (1 << NFTNL_RULE_POSITION))
+ mnl_attr_put_u64(nlh, NFTA_RULE_POSITION, htobe64(r->position));
+ if (r->flags & (1 << NFTNL_RULE_USERDATA)) {
+ mnl_attr_put(nlh, NFTA_RULE_USERDATA, r->user.len,
+ r->user.data);
+ }
+
+ if (!list_empty(&r->expr_list)) {
+ nest = mnl_attr_nest_start(nlh, NFTA_RULE_EXPRESSIONS);
+ list_for_each_entry(expr, &r->expr_list, head) {
+ nest2 = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM);
+ nftnl_expr_build_payload(nlh, expr);
+ mnl_attr_nest_end(nlh, nest2);
+ }
+ mnl_attr_nest_end(nlh, nest);
+ }
+
+ if (r->flags & (1 << NFTNL_RULE_COMPAT_PROTO) &&
+ r->flags & (1 << NFTNL_RULE_COMPAT_FLAGS)) {
+
+ nest = mnl_attr_nest_start(nlh, NFTA_RULE_COMPAT);
+ mnl_attr_put_u32(nlh, NFTA_RULE_COMPAT_PROTO,
+ htonl(r->compat.proto));
+ mnl_attr_put_u32(nlh, NFTA_RULE_COMPAT_FLAGS,
+ htonl(r->compat.flags));
+ mnl_attr_nest_end(nlh, nest);
+ }
+ if (r->flags & (1 << NFTNL_RULE_ID))
+ mnl_attr_put_u32(nlh, NFTA_RULE_ID, htonl(r->id));
+ if (r->flags & (1 << NFTNL_RULE_POSITION_ID))
+ mnl_attr_put_u32(nlh, NFTA_RULE_POSITION_ID, htonl(r->position_id));
+}
+
+EXPORT_SYMBOL(nftnl_rule_add_expr);
+void nftnl_rule_add_expr(struct nftnl_rule *r, struct nftnl_expr *expr)
+{
+ list_add_tail(&expr->head, &r->expr_list);
+}
+
+EXPORT_SYMBOL(nftnl_rule_del_expr);
+void nftnl_rule_del_expr(struct nftnl_expr *expr)
+{
+ list_del(&expr->head);
+}
+
+static int nftnl_rule_parse_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_RULE_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_RULE_TABLE:
+ case NFTA_RULE_CHAIN:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ case NFTA_RULE_HANDLE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ case NFTA_RULE_COMPAT:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ abi_breakage();
+ break;
+ case NFTA_RULE_POSITION:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ case NFTA_RULE_USERDATA:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ abi_breakage();
+ break;
+ case NFTA_RULE_ID:
+ case NFTA_RULE_POSITION_ID:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int nftnl_rule_parse_expr(struct nlattr *nest, struct nftnl_rule *r)
+{
+ struct nftnl_expr *expr;
+ struct nlattr *attr;
+
+ mnl_attr_for_each_nested(attr, nest) {
+ if (mnl_attr_get_type(attr) != NFTA_LIST_ELEM)
+ return -1;
+
+ expr = nftnl_expr_parse(attr);
+ if (expr == NULL)
+ return -1;
+
+ list_add_tail(&expr->head, &r->expr_list);
+ }
+ return 0;
+}
+
+static int nftnl_rule_parse_compat_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_RULE_COMPAT_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_RULE_COMPAT_PROTO:
+ case NFTA_RULE_COMPAT_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int nftnl_rule_parse_compat(struct nlattr *nest, struct nftnl_rule *r)
+{
+ struct nlattr *tb[NFTA_RULE_COMPAT_MAX+1] = {};
+
+ if (mnl_attr_parse_nested(nest, nftnl_rule_parse_compat_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_RULE_COMPAT_PROTO]) {
+ r->compat.proto =
+ ntohl(mnl_attr_get_u32(tb[NFTA_RULE_COMPAT_PROTO]));
+ r->flags |= (1 << NFTNL_RULE_COMPAT_PROTO);
+ }
+ if (tb[NFTA_RULE_COMPAT_FLAGS]) {
+ r->compat.flags =
+ ntohl(mnl_attr_get_u32(tb[NFTA_RULE_COMPAT_FLAGS]));
+ r->flags |= (1 << NFTNL_RULE_COMPAT_FLAGS);
+ }
+ return 0;
+}
+
+EXPORT_SYMBOL(nftnl_rule_nlmsg_parse);
+int nftnl_rule_nlmsg_parse(const struct nlmsghdr *nlh, struct nftnl_rule *r)
+{
+ struct nlattr *tb[NFTA_RULE_MAX+1] = {};
+ struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
+ int ret;
+
+ if (mnl_attr_parse(nlh, sizeof(*nfg), nftnl_rule_parse_attr_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_RULE_TABLE]) {
+ if (r->flags & (1 << NFTNL_RULE_TABLE))
+ xfree(r->table);
+ r->table = strdup(mnl_attr_get_str(tb[NFTA_RULE_TABLE]));
+ if (!r->table)
+ return -1;
+ r->flags |= (1 << NFTNL_RULE_TABLE);
+ }
+ if (tb[NFTA_RULE_CHAIN]) {
+ if (r->flags & (1 << NFTNL_RULE_CHAIN))
+ xfree(r->chain);
+ r->chain = strdup(mnl_attr_get_str(tb[NFTA_RULE_CHAIN]));
+ if (!r->chain)
+ return -1;
+ r->flags |= (1 << NFTNL_RULE_CHAIN);
+ }
+ if (tb[NFTA_RULE_HANDLE]) {
+ r->handle = be64toh(mnl_attr_get_u64(tb[NFTA_RULE_HANDLE]));
+ r->flags |= (1 << NFTNL_RULE_HANDLE);
+ }
+ if (tb[NFTA_RULE_EXPRESSIONS]) {
+ ret = nftnl_rule_parse_expr(tb[NFTA_RULE_EXPRESSIONS], r);
+ if (ret < 0)
+ return ret;
+ }
+ if (tb[NFTA_RULE_COMPAT]) {
+ ret = nftnl_rule_parse_compat(tb[NFTA_RULE_COMPAT], r);
+ if (ret < 0)
+ return ret;
+ }
+ if (tb[NFTA_RULE_POSITION]) {
+ r->position = be64toh(mnl_attr_get_u64(tb[NFTA_RULE_POSITION]));
+ r->flags |= (1 << NFTNL_RULE_POSITION);
+ }
+ if (tb[NFTA_RULE_USERDATA]) {
+ const void *udata =
+ mnl_attr_get_payload(tb[NFTA_RULE_USERDATA]);
+
+ if (r->flags & (1 << NFTNL_RULE_USERDATA))
+ xfree(r->user.data);
+
+ r->user.len = mnl_attr_get_payload_len(tb[NFTA_RULE_USERDATA]);
+
+ r->user.data = malloc(r->user.len);
+ if (r->user.data == NULL)
+ return -1;
+
+ memcpy(r->user.data, udata, r->user.len);
+ r->flags |= (1 << NFTNL_RULE_USERDATA);
+ }
+ if (tb[NFTA_RULE_ID]) {
+ r->id = ntohl(mnl_attr_get_u32(tb[NFTA_RULE_ID]));
+ r->flags |= (1 << NFTNL_RULE_ID);
+ }
+ if (tb[NFTA_RULE_POSITION_ID]) {
+ r->position_id = ntohl(mnl_attr_get_u32(tb[NFTA_RULE_POSITION_ID]));
+ r->flags |= (1 << NFTNL_RULE_POSITION_ID);
+ }
+
+ r->family = nfg->nfgen_family;
+ r->flags |= (1 << NFTNL_RULE_FAMILY);
+
+ return 0;
+}
+
+static int nftnl_rule_do_parse(struct nftnl_rule *r, enum nftnl_parse_type type,
+ const void *data, struct nftnl_parse_err *err,
+ enum nftnl_parse_input input)
+{
+ int ret;
+ struct nftnl_parse_err perr = {};
+
+ switch (type) {
+ case NFTNL_PARSE_JSON:
+ case NFTNL_PARSE_XML:
+ default:
+ ret = -1;
+ errno = EOPNOTSUPP;
+ break;
+ }
+ if (err != NULL)
+ *err = perr;
+
+ return ret;
+}
+
+EXPORT_SYMBOL(nftnl_rule_parse);
+int nftnl_rule_parse(struct nftnl_rule *r, enum nftnl_parse_type type,
+ const char *data, struct nftnl_parse_err *err)
+{
+ return nftnl_rule_do_parse(r, type, data, err, NFTNL_PARSE_BUFFER);
+}
+
+EXPORT_SYMBOL(nftnl_rule_parse_file);
+int nftnl_rule_parse_file(struct nftnl_rule *r, enum nftnl_parse_type type,
+ FILE *fp, struct nftnl_parse_err *err)
+{
+ return nftnl_rule_do_parse(r, type, fp, err, NFTNL_PARSE_FILE);
+}
+
+static int nftnl_rule_snprintf_default(char *buf, size_t remain,
+ const struct nftnl_rule *r,
+ uint32_t type, uint32_t flags)
+{
+ struct nftnl_expr *expr;
+ int ret, offset = 0, i;
+ const char *sep = "";
+
+ if (r->flags & (1 << NFTNL_RULE_FAMILY)) {
+ ret = snprintf(buf + offset, remain, "%s%s", sep,
+ nftnl_family2str(r->family));
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ sep = " ";
+ }
+
+ if (r->flags & (1 << NFTNL_RULE_TABLE)) {
+ ret = snprintf(buf + offset, remain, "%s%s", sep,
+ r->table);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ sep = " ";
+ }
+
+ if (r->flags & (1 << NFTNL_RULE_CHAIN)) {
+ ret = snprintf(buf + offset, remain, "%s%s", sep,
+ r->chain);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ sep = " ";
+ }
+ if (r->flags & (1 << NFTNL_RULE_HANDLE)) {
+ ret = snprintf(buf + offset, remain, "%s%" PRIu64, sep,
+ r->handle);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ sep = " ";
+ }
+
+ if (r->flags & (1 << NFTNL_RULE_POSITION)) {
+ ret = snprintf(buf + offset, remain, "%s%" PRIu64, sep,
+ r->position);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ sep = " ";
+ }
+
+ if (r->flags & (1 << NFTNL_RULE_ID)) {
+ ret = snprintf(buf + offset, remain, "%s%u", sep, r->id);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ sep = " ";
+ }
+
+ if (r->flags & (1 << NFTNL_RULE_POSITION_ID)) {
+ ret = snprintf(buf + offset, remain, "%s%u", sep,
+ r->position_id);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ sep = " ";
+ }
+
+ ret = snprintf(buf + offset, remain, "\n");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ list_for_each_entry(expr, &r->expr_list, head) {
+ ret = snprintf(buf + offset, remain, " [ %s ", expr->ops->name);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_expr_snprintf(buf + offset, remain, expr,
+ type, flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = snprintf(buf + offset, remain, "]\n");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (r->user.len) {
+ ret = snprintf(buf + offset, remain, " userdata = { ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ for (i = 0; i < r->user.len; i++) {
+ char *c = r->user.data;
+
+ ret = snprintf(buf + offset, remain,
+ isprint(c[i]) ? "%c" : "\\x%02hhx",
+ c[i]);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ ret = snprintf(buf + offset, remain, " }");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ }
+
+ return offset;
+}
+
+static int nftnl_rule_cmd_snprintf(char *buf, size_t remain,
+ const struct nftnl_rule *r, uint32_t cmd,
+ uint32_t type, uint32_t flags)
+{
+ uint32_t inner_flags = flags;
+ int ret, offset = 0;
+
+ inner_flags &= ~NFTNL_OF_EVENT_ANY;
+
+ if (type != NFTNL_OUTPUT_DEFAULT)
+ return -1;
+
+ ret = nftnl_rule_snprintf_default(buf + offset, remain, r, type,
+ inner_flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ return offset;
+}
+
+EXPORT_SYMBOL(nftnl_rule_snprintf);
+int nftnl_rule_snprintf(char *buf, size_t size, const struct nftnl_rule *r,
+ uint32_t type, uint32_t flags)
+{
+ if (size)
+ buf[0] = '\0';
+
+ return nftnl_rule_cmd_snprintf(buf, size, r, nftnl_flag2cmd(flags), type,
+ flags);
+}
+
+static int nftnl_rule_do_snprintf(char *buf, size_t size, const void *r,
+ uint32_t cmd, uint32_t type, uint32_t flags)
+{
+ return nftnl_rule_snprintf(buf, size, r, type, flags);
+}
+
+EXPORT_SYMBOL(nftnl_rule_fprintf);
+int nftnl_rule_fprintf(FILE *fp, const struct nftnl_rule *r, uint32_t type,
+ uint32_t flags)
+{
+ return nftnl_fprintf(fp, r, NFTNL_CMD_UNSPEC, type, flags,
+ nftnl_rule_do_snprintf);
+}
+
+EXPORT_SYMBOL(nftnl_expr_foreach);
+int nftnl_expr_foreach(struct nftnl_rule *r,
+ int (*cb)(struct nftnl_expr *e, void *data),
+ void *data)
+{
+ struct nftnl_expr *cur, *tmp;
+ int ret;
+
+ list_for_each_entry_safe(cur, tmp, &r->expr_list, head) {
+ ret = cb(cur, data);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+struct nftnl_expr_iter {
+ const struct nftnl_rule *r;
+ struct nftnl_expr *cur;
+};
+
+static void nftnl_expr_iter_init(const struct nftnl_rule *r,
+ struct nftnl_expr_iter *iter)
+{
+ iter->r = r;
+ if (list_empty(&r->expr_list))
+ iter->cur = NULL;
+ else
+ iter->cur = list_entry(r->expr_list.next, struct nftnl_expr,
+ head);
+}
+
+EXPORT_SYMBOL(nftnl_expr_iter_create);
+struct nftnl_expr_iter *nftnl_expr_iter_create(const struct nftnl_rule *r)
+{
+ struct nftnl_expr_iter *iter;
+
+ iter = calloc(1, sizeof(struct nftnl_expr_iter));
+ if (iter == NULL)
+ return NULL;
+
+ nftnl_expr_iter_init(r, iter);
+
+ return iter;
+}
+
+EXPORT_SYMBOL(nftnl_expr_iter_next);
+struct nftnl_expr *nftnl_expr_iter_next(struct nftnl_expr_iter *iter)
+{
+ struct nftnl_expr *expr = iter->cur;
+
+ if (expr == NULL)
+ return NULL;
+
+ /* get next expression, if any */
+ iter->cur = list_entry(iter->cur->head.next, struct nftnl_expr, head);
+ if (&iter->cur->head == iter->r->expr_list.next)
+ return NULL;
+
+ return expr;
+}
+
+EXPORT_SYMBOL(nftnl_expr_iter_destroy);
+void nftnl_expr_iter_destroy(struct nftnl_expr_iter *iter)
+{
+ xfree(iter);
+}
+
+struct nftnl_rule_list {
+ struct list_head list;
+};
+
+EXPORT_SYMBOL(nftnl_rule_list_alloc);
+struct nftnl_rule_list *nftnl_rule_list_alloc(void)
+{
+ struct nftnl_rule_list *list;
+
+ list = calloc(1, sizeof(struct nftnl_rule_list));
+ if (list == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&list->list);
+
+ return list;
+}
+
+EXPORT_SYMBOL(nftnl_rule_list_free);
+void nftnl_rule_list_free(struct nftnl_rule_list *list)
+{
+ struct nftnl_rule *r, *tmp;
+
+ list_for_each_entry_safe(r, tmp, &list->list, head) {
+ list_del(&r->head);
+ nftnl_rule_free(r);
+ }
+ xfree(list);
+}
+
+EXPORT_SYMBOL(nftnl_rule_list_is_empty);
+int nftnl_rule_list_is_empty(const struct nftnl_rule_list *list)
+{
+ return list_empty(&list->list);
+}
+
+EXPORT_SYMBOL(nftnl_rule_list_add);
+void nftnl_rule_list_add(struct nftnl_rule *r, struct nftnl_rule_list *list)
+{
+ list_add(&r->head, &list->list);
+}
+
+EXPORT_SYMBOL(nftnl_rule_list_insert_at);
+void nftnl_rule_list_insert_at(struct nftnl_rule *r, struct nftnl_rule *pos)
+{
+ list_add(&r->head, &pos->head);
+}
+
+EXPORT_SYMBOL(nftnl_rule_list_add_tail);
+void nftnl_rule_list_add_tail(struct nftnl_rule *r, struct nftnl_rule_list *list)
+{
+ list_add_tail(&r->head, &list->list);
+}
+
+EXPORT_SYMBOL(nftnl_rule_list_del);
+void nftnl_rule_list_del(struct nftnl_rule *r)
+{
+ list_del(&r->head);
+}
+
+EXPORT_SYMBOL(nftnl_rule_list_foreach);
+int nftnl_rule_list_foreach(struct nftnl_rule_list *rule_list,
+ int (*cb)(struct nftnl_rule *r, void *data),
+ void *data)
+{
+ struct nftnl_rule *cur, *tmp;
+ int ret;
+
+ list_for_each_entry_safe(cur, tmp, &rule_list->list, head) {
+ ret = cb(cur, data);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+struct nftnl_rule_list_iter {
+ const struct nftnl_rule_list *list;
+ struct nftnl_rule *cur;
+};
+
+EXPORT_SYMBOL(nftnl_rule_list_iter_create);
+struct nftnl_rule_list_iter *
+nftnl_rule_list_iter_create(const struct nftnl_rule_list *l)
+{
+ struct nftnl_rule_list_iter *iter;
+
+ iter = calloc(1, sizeof(struct nftnl_rule_list_iter));
+ if (iter == NULL)
+ return NULL;
+
+ iter->list = l;
+ if (nftnl_rule_list_is_empty(l))
+ iter->cur = NULL;
+ else
+ iter->cur = list_entry(l->list.next, struct nftnl_rule, head);
+
+ return iter;
+}
+
+EXPORT_SYMBOL(nftnl_rule_list_iter_cur);
+struct nftnl_rule *nftnl_rule_list_iter_cur(struct nftnl_rule_list_iter *iter)
+{
+ return iter->cur;
+}
+
+EXPORT_SYMBOL(nftnl_rule_list_iter_next);
+struct nftnl_rule *nftnl_rule_list_iter_next(struct nftnl_rule_list_iter *iter)
+{
+ struct nftnl_rule *r = iter->cur;
+
+ if (r == NULL)
+ return NULL;
+
+ /* get next rule, if any */
+ iter->cur = list_entry(iter->cur->head.next, struct nftnl_rule, head);
+ if (&iter->cur->head == iter->list->list.next)
+ return NULL;
+
+ return r;
+}
+
+EXPORT_SYMBOL(nftnl_rule_list_iter_destroy);
+void nftnl_rule_list_iter_destroy(const struct nftnl_rule_list_iter *iter)
+{
+ xfree(iter);
+}
diff --git a/src/ruleset.c b/src/ruleset.c
new file mode 100644
index 0000000..185aa48
--- /dev/null
+++ b/src/ruleset.c
@@ -0,0 +1,719 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2013 by Arturo Borrero Gonzalez <arturo@debian.org>
+ * (C) 2013 by Alvaro Neira Ayuso <alvaroneay@gmail.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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <errno.h>
+
+#include "internal.h"
+#include <stdlib.h>
+
+#include <libmnl/libmnl.h>
+#include <libnftnl/ruleset.h>
+#include <libnftnl/table.h>
+#include <libnftnl/chain.h>
+#include <libnftnl/set.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_ruleset {
+ struct nftnl_table_list *table_list;
+ struct nftnl_chain_list *chain_list;
+ struct nftnl_set_list *set_list;
+ struct nftnl_rule_list *rule_list;
+
+ uint16_t flags;
+};
+
+struct nftnl_parse_ctx {
+ enum nftnl_cmd_type cmd;
+ enum nftnl_ruleset_type type;
+ union {
+ struct nftnl_table *table;
+ struct nftnl_chain *chain;
+ struct nftnl_rule *rule;
+ struct nftnl_set *set;
+ struct nftnl_set_elem *set_elem;
+ };
+ void *data;
+
+ /* These fields below are not exposed to the user */
+ uint32_t format;
+ uint32_t set_id;
+ struct nftnl_set_list *set_list;
+
+ int (*cb)(const struct nftnl_parse_ctx *ctx);
+ uint16_t flags;
+};
+
+EXPORT_SYMBOL(nftnl_ruleset_alloc);
+struct nftnl_ruleset *nftnl_ruleset_alloc(void)
+{
+ return calloc(1, sizeof(struct nftnl_ruleset));
+}
+
+EXPORT_SYMBOL(nftnl_ruleset_free);
+void nftnl_ruleset_free(const struct nftnl_ruleset *r)
+{
+ if (r->flags & (1 << NFTNL_RULESET_TABLELIST))
+ nftnl_table_list_free(r->table_list);
+ if (r->flags & (1 << NFTNL_RULESET_CHAINLIST))
+ nftnl_chain_list_free(r->chain_list);
+ if (r->flags & (1 << NFTNL_RULESET_SETLIST))
+ nftnl_set_list_free(r->set_list);
+ if (r->flags & (1 << NFTNL_RULESET_RULELIST))
+ nftnl_rule_list_free(r->rule_list);
+ xfree(r);
+}
+
+EXPORT_SYMBOL(nftnl_ruleset_is_set);
+bool nftnl_ruleset_is_set(const struct nftnl_ruleset *r, uint16_t attr)
+{
+ return r->flags & (1 << attr);
+}
+
+EXPORT_SYMBOL(nftnl_ruleset_unset);
+void nftnl_ruleset_unset(struct nftnl_ruleset *r, uint16_t attr)
+{
+ if (!(r->flags & (1 << attr)))
+ return;
+
+ switch (attr) {
+ case NFTNL_RULESET_TABLELIST:
+ nftnl_table_list_free(r->table_list);
+ break;
+ case NFTNL_RULESET_CHAINLIST:
+ nftnl_chain_list_free(r->chain_list);
+ break;
+ case NFTNL_RULESET_SETLIST:
+ nftnl_set_list_free(r->set_list);
+ break;
+ case NFTNL_RULESET_RULELIST:
+ nftnl_rule_list_free(r->rule_list);
+ break;
+ }
+ r->flags &= ~(1 << attr);
+}
+
+EXPORT_SYMBOL(nftnl_ruleset_set);
+void nftnl_ruleset_set(struct nftnl_ruleset *r, uint16_t attr, void *data)
+{
+ switch (attr) {
+ case NFTNL_RULESET_TABLELIST:
+ nftnl_ruleset_unset(r, NFTNL_RULESET_TABLELIST);
+ r->table_list = data;
+ break;
+ case NFTNL_RULESET_CHAINLIST:
+ nftnl_ruleset_unset(r, NFTNL_RULESET_CHAINLIST);
+ r->chain_list = data;
+ break;
+ case NFTNL_RULESET_SETLIST:
+ nftnl_ruleset_unset(r, NFTNL_RULESET_SETLIST);
+ r->set_list = data;
+ break;
+ case NFTNL_RULESET_RULELIST:
+ nftnl_ruleset_unset(r, NFTNL_RULESET_RULELIST);
+ r->rule_list = data;
+ break;
+ default:
+ return;
+ }
+ r->flags |= (1 << attr);
+}
+
+EXPORT_SYMBOL(nftnl_ruleset_get);
+void *nftnl_ruleset_get(const struct nftnl_ruleset *r, uint16_t attr)
+{
+ if (!(r->flags & (1 << attr)))
+ return NULL;
+
+ switch (attr) {
+ case NFTNL_RULESET_TABLELIST:
+ return r->table_list;
+ case NFTNL_RULESET_CHAINLIST:
+ return r->chain_list;
+ case NFTNL_RULESET_SETLIST:
+ return r->set_list;
+ case NFTNL_RULESET_RULELIST:
+ return r->rule_list;
+ default:
+ return NULL;
+ }
+}
+
+EXPORT_SYMBOL(nftnl_ruleset_ctx_free);
+void nftnl_ruleset_ctx_free(const struct nftnl_parse_ctx *ctx)
+{
+ switch (ctx->type) {
+ case NFTNL_RULESET_TABLE:
+ nftnl_table_free(ctx->table);
+ break;
+ case NFTNL_RULESET_CHAIN:
+ nftnl_chain_free(ctx->chain);
+ break;
+ case NFTNL_RULESET_RULE:
+ nftnl_rule_free(ctx->rule);
+ break;
+ case NFTNL_RULESET_SET:
+ case NFTNL_RULESET_SET_ELEMS:
+ nftnl_set_free(ctx->set);
+ break;
+ case NFTNL_RULESET_RULESET:
+ case NFTNL_RULESET_UNSPEC:
+ break;
+ }
+}
+
+EXPORT_SYMBOL(nftnl_ruleset_ctx_is_set);
+bool nftnl_ruleset_ctx_is_set(const struct nftnl_parse_ctx *ctx, uint16_t attr)
+{
+ return ctx->flags & (1 << attr);
+}
+
+EXPORT_SYMBOL(nftnl_ruleset_ctx_get);
+void *nftnl_ruleset_ctx_get(const struct nftnl_parse_ctx *ctx, uint16_t attr)
+{
+ if (!(ctx->flags & (1 << attr)))
+ return NULL;
+
+ switch (attr) {
+ case NFTNL_RULESET_CTX_CMD:
+ return (void *)&ctx->cmd;
+ case NFTNL_RULESET_CTX_TYPE:
+ return (void *)&ctx->type;
+ case NFTNL_RULESET_CTX_TABLE:
+ return ctx->table;
+ case NFTNL_RULESET_CTX_CHAIN:
+ return ctx->chain;
+ case NFTNL_RULESET_CTX_RULE:
+ return ctx->rule;
+ case NFTNL_RULESET_CTX_SET:
+ return ctx->set;
+ case NFTNL_RULESET_CTX_DATA:
+ return ctx->data;
+ default:
+ return NULL;
+ }
+}
+
+EXPORT_SYMBOL(nftnl_ruleset_ctx_get_u32);
+uint32_t nftnl_ruleset_ctx_get_u32(const struct nftnl_parse_ctx *ctx, uint16_t attr)
+{
+ const void *ret = nftnl_ruleset_ctx_get(ctx, attr);
+ return ret == NULL ? 0 : *((uint32_t *)ret);
+}
+
+
+EXPORT_SYMBOL(nftnl_ruleset_parse_file_cb);
+int nftnl_ruleset_parse_file_cb(enum nftnl_parse_type type, FILE *fp,
+ struct nftnl_parse_err *err, void *data,
+ int (*cb)(const struct nftnl_parse_ctx *ctx))
+{
+ errno = EOPNOTSUPP;
+ return -1;
+}
+
+EXPORT_SYMBOL(nftnl_ruleset_parse_buffer_cb);
+int nftnl_ruleset_parse_buffer_cb(enum nftnl_parse_type type, const char *buffer,
+ struct nftnl_parse_err *err, void *data,
+ int (*cb)(const struct nftnl_parse_ctx *ctx))
+{
+ errno = EOPNOTSUPP;
+ return -1;
+}
+
+static int nftnl_ruleset_cb(const struct nftnl_parse_ctx *ctx)
+{
+ struct nftnl_ruleset *r = ctx->data;
+
+ if (ctx->cmd != NFTNL_CMD_ADD)
+ return -1;
+
+ switch (ctx->type) {
+ case NFTNL_RULESET_TABLE:
+ if (r->table_list == NULL) {
+ r->table_list = nftnl_table_list_alloc();
+ if (r->table_list == NULL)
+ return -1;
+
+ nftnl_ruleset_set(r, NFTNL_RULESET_TABLELIST,
+ r->table_list);
+ }
+ nftnl_table_list_add_tail(ctx->table, r->table_list);
+ break;
+ case NFTNL_RULESET_CHAIN:
+ if (r->chain_list == NULL) {
+ r->chain_list = nftnl_chain_list_alloc();
+ if (r->chain_list == NULL)
+ return -1;
+
+ nftnl_ruleset_set(r, NFTNL_RULESET_CHAINLIST,
+ r->chain_list);
+ }
+ nftnl_chain_list_add_tail(ctx->chain, r->chain_list);
+ break;
+ case NFTNL_RULESET_SET:
+ if (r->set_list == NULL) {
+ r->set_list = nftnl_set_list_alloc();
+ if (r->set_list == NULL)
+ return -1;
+
+ nftnl_ruleset_set(r, NFTNL_RULESET_SETLIST,
+ r->set_list);
+ }
+ nftnl_set_list_add_tail(ctx->set, r->set_list);
+ break;
+ case NFTNL_RULESET_RULE:
+ if (r->rule_list == NULL) {
+ r->rule_list = nftnl_rule_list_alloc();
+ if (r->rule_list == NULL)
+ return -1;
+
+ nftnl_ruleset_set(r, NFTNL_RULESET_RULELIST,
+ r->rule_list);
+ }
+ nftnl_rule_list_add_tail(ctx->rule, r->rule_list);
+ break;
+ case NFTNL_RULESET_RULESET:
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+EXPORT_SYMBOL(nftnl_ruleset_parse);
+int nftnl_ruleset_parse(struct nftnl_ruleset *r, enum nftnl_parse_type type,
+ const char *data, struct nftnl_parse_err *err)
+{
+ errno = EOPNOTSUPP;
+ return -1;
+}
+
+EXPORT_SYMBOL(nftnl_ruleset_parse_file);
+int nftnl_ruleset_parse_file(struct nftnl_ruleset *rs, enum nftnl_parse_type type,
+ FILE *fp, struct nftnl_parse_err *err)
+{
+ return nftnl_ruleset_parse_file_cb(type, fp, err, rs, nftnl_ruleset_cb);
+}
+
+static int
+nftnl_ruleset_snprintf_table(char *buf, size_t remain,
+ const struct nftnl_ruleset *rs, uint32_t type,
+ uint32_t flags)
+{
+ struct nftnl_table *t;
+ struct nftnl_table_list_iter *ti;
+ const char *sep = "";
+ int ret, offset = 0;
+
+ ti = nftnl_table_list_iter_create(rs->table_list);
+ if (ti == NULL)
+ return 0;
+
+ t = nftnl_table_list_iter_next(ti);
+ while (t != NULL) {
+ ret = snprintf(buf + offset, remain, "%s", sep);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_table_snprintf(buf + offset, remain, t, type, flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ t = nftnl_table_list_iter_next(ti);
+ sep = "\n";
+ }
+ nftnl_table_list_iter_destroy(ti);
+
+ return offset;
+}
+
+static int
+nftnl_ruleset_snprintf_chain(char *buf, size_t remain,
+ const struct nftnl_ruleset *rs, uint32_t type,
+ uint32_t flags)
+{
+ struct nftnl_chain *c;
+ struct nftnl_chain_list_iter *ci;
+ const char *sep = "";
+ int ret, offset = 0;
+
+ ci = nftnl_chain_list_iter_create(rs->chain_list);
+ if (ci == NULL)
+ return 0;
+
+ c = nftnl_chain_list_iter_next(ci);
+ while (c != NULL) {
+ ret = snprintf(buf + offset, remain, "%s", sep);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_chain_snprintf(buf + offset, remain, c, type, flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ c = nftnl_chain_list_iter_next(ci);
+ sep = "\n";
+ }
+ nftnl_chain_list_iter_destroy(ci);
+
+ return offset;
+}
+
+static int
+nftnl_ruleset_snprintf_set(char *buf, size_t remain,
+ const struct nftnl_ruleset *rs, uint32_t type,
+ uint32_t flags)
+{
+ struct nftnl_set *s;
+ struct nftnl_set_list_iter *si;
+ const char *sep = "";
+ int ret, offset = 0;
+
+ si = nftnl_set_list_iter_create(rs->set_list);
+ if (si == NULL)
+ return 0;
+
+ s = nftnl_set_list_iter_next(si);
+ while (s != NULL) {
+ ret = snprintf(buf + offset, remain, "%s", sep);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_set_snprintf(buf + offset, remain, s, type, flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ s = nftnl_set_list_iter_next(si);
+ sep = "\n";
+ }
+ nftnl_set_list_iter_destroy(si);
+
+ return offset;
+}
+
+static int
+nftnl_ruleset_snprintf_rule(char *buf, size_t remain,
+ const struct nftnl_ruleset *rs, uint32_t type,
+ uint32_t flags)
+{
+ struct nftnl_rule *r;
+ struct nftnl_rule_list_iter *ri;
+ const char *sep = "";
+ int ret, offset = 0;
+
+ ri = nftnl_rule_list_iter_create(rs->rule_list);
+ if (ri == NULL)
+ return 0;
+
+ r = nftnl_rule_list_iter_next(ri);
+ while (r != NULL) {
+ ret = snprintf(buf + offset, remain, "%s", sep);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_rule_snprintf(buf + offset, remain, r, type, flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ r = nftnl_rule_list_iter_next(ri);
+ sep = "\n";
+ }
+ nftnl_rule_list_iter_destroy(ri);
+
+ return offset;
+}
+
+static int
+nftnl_ruleset_do_snprintf(char *buf, size_t remain,
+ const struct nftnl_ruleset *rs,
+ uint32_t cmd, uint32_t type, uint32_t flags)
+{
+ uint32_t inner_flags = flags;
+ const char *sep = "";
+ int ret, offset = 0;
+
+ /* dont pass events flags to child calls of _snprintf() */
+ inner_flags &= ~NFTNL_OF_EVENT_ANY;
+
+ if (nftnl_ruleset_is_set(rs, NFTNL_RULESET_TABLELIST) &&
+ (!nftnl_table_list_is_empty(rs->table_list))) {
+ ret = nftnl_ruleset_snprintf_table(buf + offset, remain, rs,
+ type, inner_flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (ret > 0)
+ sep = "\n";
+ }
+
+ if (nftnl_ruleset_is_set(rs, NFTNL_RULESET_CHAINLIST) &&
+ (!nftnl_chain_list_is_empty(rs->chain_list))) {
+ ret = snprintf(buf + offset, remain, "%s", sep);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_ruleset_snprintf_chain(buf + offset, remain, rs,
+ type, inner_flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (ret > 0)
+ sep = "\n";
+ }
+
+ if (nftnl_ruleset_is_set(rs, NFTNL_RULESET_SETLIST) &&
+ (!nftnl_set_list_is_empty(rs->set_list))) {
+ ret = snprintf(buf + offset, remain, "%s", sep);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_ruleset_snprintf_set(buf + offset, remain, rs,
+ type, inner_flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (ret > 0)
+ sep = "\n";
+ }
+
+ if (nftnl_ruleset_is_set(rs, NFTNL_RULESET_RULELIST) &&
+ (!nftnl_rule_list_is_empty(rs->rule_list))) {
+ ret = snprintf(buf + offset, remain, "%s", sep);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_ruleset_snprintf_rule(buf + offset, remain, rs,
+ type, inner_flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+EXPORT_SYMBOL(nftnl_ruleset_snprintf);
+int nftnl_ruleset_snprintf(char *buf, size_t size, const struct nftnl_ruleset *r,
+ uint32_t type, uint32_t flags)
+{
+ if (size)
+ buf[0] = '\0';
+
+ if (type != NFTNL_OUTPUT_DEFAULT) {
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+ return nftnl_ruleset_do_snprintf(buf, size, r, nftnl_flag2cmd(flags),
+ type, flags);
+}
+
+static int nftnl_ruleset_fprintf_tables(FILE *fp, const struct nftnl_ruleset *rs,
+ uint32_t type, uint32_t flags)
+{
+ int len = 0, ret = 0;
+ struct nftnl_table *t;
+ struct nftnl_table_list_iter *ti;
+ const char *sep = "";
+
+ ti = nftnl_table_list_iter_create(rs->table_list);
+ if (ti == NULL)
+ return -1;
+
+ t = nftnl_table_list_iter_next(ti);
+ while (t != NULL) {
+ ret = fprintf(fp, "%s", sep);
+ if (ret < 0)
+ goto err;
+
+ len += ret;
+
+ ret = nftnl_table_fprintf(fp, t, type, flags);
+ if (ret < 0)
+ goto err;
+
+ len += ret;
+
+ t = nftnl_table_list_iter_next(ti);
+ sep = "\n";
+
+ }
+ nftnl_table_list_iter_destroy(ti);
+
+ return len;
+err:
+ nftnl_table_list_iter_destroy(ti);
+ return -1;
+}
+
+static int nftnl_ruleset_fprintf_chains(FILE *fp, const struct nftnl_ruleset *rs,
+ uint32_t type, uint32_t flags)
+{
+ int len = 0, ret = 0;
+ struct nftnl_chain *o;
+ struct nftnl_chain_list_iter *i;
+ const char *sep = "";
+
+ i = nftnl_chain_list_iter_create(rs->chain_list);
+ if (i == NULL)
+ return -1;
+
+ o = nftnl_chain_list_iter_next(i);
+ while (o != NULL) {
+ ret = fprintf(fp, "%s", sep);
+ if (ret < 0)
+ goto err;
+
+ len += ret;
+
+ ret = nftnl_chain_fprintf(fp, o, type, flags);
+ if (ret < 0)
+ goto err;
+
+ len += ret;
+
+ o = nftnl_chain_list_iter_next(i);
+ sep = "\n";
+ }
+ nftnl_chain_list_iter_destroy(i);
+
+ return len;
+err:
+ nftnl_chain_list_iter_destroy(i);
+ return -1;
+}
+
+static int nftnl_ruleset_fprintf_sets(FILE *fp, const struct nftnl_ruleset *rs,
+ uint32_t type, uint32_t flags)
+{
+ int len = 0, ret = 0;
+ struct nftnl_set *o;
+ struct nftnl_set_list_iter *i;
+ const char *sep = "";
+
+ i = nftnl_set_list_iter_create(rs->set_list);
+ if (i == NULL)
+ return -1;
+
+ o = nftnl_set_list_iter_next(i);
+ while (o != NULL) {
+ ret = fprintf(fp, "%s", sep);
+ if (ret < 0)
+ goto err;
+
+ len += ret;
+
+ ret = nftnl_set_fprintf(fp, o, type, flags);
+ if (ret < 0)
+ goto err;
+
+ len += ret;
+
+ o = nftnl_set_list_iter_next(i);
+ sep = "\n";
+ }
+ nftnl_set_list_iter_destroy(i);
+
+ return len;
+err:
+ nftnl_set_list_iter_destroy(i);
+ return -1;
+}
+
+static int nftnl_ruleset_fprintf_rules(FILE *fp, const struct nftnl_ruleset *rs,
+ uint32_t type, uint32_t flags)
+{
+ int len = 0, ret = 0;
+ struct nftnl_rule *o;
+ struct nftnl_rule_list_iter *i;
+ const char *sep = "";
+
+ i = nftnl_rule_list_iter_create(rs->rule_list);
+ if (i == NULL)
+ return -1;
+
+ o = nftnl_rule_list_iter_next(i);
+ while (o != NULL) {
+ ret = fprintf(fp, "%s", sep);
+ if (ret < 0)
+ goto err;
+
+ len += ret;
+
+ ret = nftnl_rule_fprintf(fp, o, type, flags);
+ if (ret < 0)
+ goto err;
+
+ len += ret;
+
+ o = nftnl_rule_list_iter_next(i);
+ sep = "\n";
+ }
+ nftnl_rule_list_iter_destroy(i);
+
+ return len;
+err:
+ nftnl_rule_list_iter_destroy(i);
+ return -1;
+}
+
+#define NFTNL_FPRINTF_RETURN_OR_FIXLEN(ret, len) \
+ if (ret < 0) \
+ return -1; \
+ len += ret;
+
+static int nftnl_ruleset_cmd_fprintf(FILE *fp, const struct nftnl_ruleset *rs,
+ uint32_t cmd, uint32_t type, uint32_t flags)
+{
+ int len = 0, ret = 0;
+ uint32_t inner_flags = flags;
+ const char *sep = "";
+
+ /* dont pass events flags to child calls of _snprintf() */
+ inner_flags &= ~NFTNL_OF_EVENT_ANY;
+
+ if ((nftnl_ruleset_is_set(rs, NFTNL_RULESET_TABLELIST)) &&
+ (!nftnl_table_list_is_empty(rs->table_list))) {
+ ret = nftnl_ruleset_fprintf_tables(fp, rs, type, inner_flags);
+ NFTNL_FPRINTF_RETURN_OR_FIXLEN(ret, len);
+
+ if (ret > 0)
+ sep = "\n";
+ }
+
+ if ((nftnl_ruleset_is_set(rs, NFTNL_RULESET_CHAINLIST)) &&
+ (!nftnl_chain_list_is_empty(rs->chain_list))) {
+ ret = fprintf(fp, "%s", sep);
+ NFTNL_FPRINTF_RETURN_OR_FIXLEN(ret, len);
+
+ ret = nftnl_ruleset_fprintf_chains(fp, rs, type, inner_flags);
+ NFTNL_FPRINTF_RETURN_OR_FIXLEN(ret, len);
+
+ if (ret > 0)
+ sep = "\n";
+ }
+
+ if ((nftnl_ruleset_is_set(rs, NFTNL_RULESET_SETLIST)) &&
+ (!nftnl_set_list_is_empty(rs->set_list))) {
+ ret = fprintf(fp, "%s", sep);
+ NFTNL_FPRINTF_RETURN_OR_FIXLEN(ret, len);
+
+ ret = nftnl_ruleset_fprintf_sets(fp, rs, type, inner_flags);
+ NFTNL_FPRINTF_RETURN_OR_FIXLEN(ret, len);
+
+ if (ret > 0)
+ sep = "\n";
+ }
+
+ if ((nftnl_ruleset_is_set(rs, NFTNL_RULESET_RULELIST)) &&
+ (!nftnl_rule_list_is_empty(rs->rule_list))) {
+ ret = fprintf(fp, "%s", sep);
+ NFTNL_FPRINTF_RETURN_OR_FIXLEN(ret, len);
+
+ ret = nftnl_ruleset_fprintf_rules(fp, rs, type, inner_flags);
+ NFTNL_FPRINTF_RETURN_OR_FIXLEN(ret, len);
+ }
+
+ return len;
+}
+
+EXPORT_SYMBOL(nftnl_ruleset_fprintf);
+int nftnl_ruleset_fprintf(FILE *fp, const struct nftnl_ruleset *rs, uint32_t type,
+ uint32_t flags)
+{
+ return nftnl_ruleset_cmd_fprintf(fp, rs, nftnl_flag2cmd(flags), type,
+ flags);
+}
diff --git a/src/set.c b/src/set.c
new file mode 100644
index 0000000..c46f827
--- /dev/null
+++ b/src/set.c
@@ -0,0 +1,1066 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+#include "internal.h"
+
+#include <time.h>
+#include <endian.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <netinet/in.h>
+#include <limits.h>
+#include <errno.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftnl/set.h>
+#include <libnftnl/expr.h>
+
+EXPORT_SYMBOL(nftnl_set_alloc);
+struct nftnl_set *nftnl_set_alloc(void)
+{
+ struct nftnl_set *s;
+
+ s = calloc(1, sizeof(struct nftnl_set));
+ if (s == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&s->element_list);
+ INIT_LIST_HEAD(&s->expr_list);
+ return s;
+}
+
+EXPORT_SYMBOL(nftnl_set_free);
+void nftnl_set_free(const struct nftnl_set *s)
+{
+ struct nftnl_set_elem *elem, *tmp;
+ struct nftnl_expr *expr, *next;
+
+ if (s->flags & (1 << NFTNL_SET_TABLE))
+ xfree(s->table);
+ if (s->flags & (1 << NFTNL_SET_NAME))
+ xfree(s->name);
+ if (s->flags & (1 << NFTNL_SET_USERDATA))
+ xfree(s->user.data);
+
+ list_for_each_entry_safe(expr, next, &s->expr_list, head)
+ nftnl_expr_free(expr);
+
+ list_for_each_entry_safe(elem, tmp, &s->element_list, head) {
+ list_del(&elem->head);
+ nftnl_set_elem_free(elem);
+ }
+ xfree(s);
+}
+
+EXPORT_SYMBOL(nftnl_set_is_set);
+bool nftnl_set_is_set(const struct nftnl_set *s, uint16_t attr)
+{
+ return s->flags & (1 << attr);
+}
+
+EXPORT_SYMBOL(nftnl_set_unset);
+void nftnl_set_unset(struct nftnl_set *s, uint16_t attr)
+{
+ struct nftnl_expr *expr, *tmp;
+
+ if (!(s->flags & (1 << attr)))
+ return;
+
+ switch (attr) {
+ case NFTNL_SET_TABLE:
+ xfree(s->table);
+ break;
+ case NFTNL_SET_NAME:
+ xfree(s->name);
+ break;
+ case NFTNL_SET_HANDLE:
+ case NFTNL_SET_FLAGS:
+ case NFTNL_SET_KEY_TYPE:
+ case NFTNL_SET_KEY_LEN:
+ case NFTNL_SET_DATA_TYPE:
+ case NFTNL_SET_DATA_LEN:
+ case NFTNL_SET_OBJ_TYPE:
+ case NFTNL_SET_FAMILY:
+ case NFTNL_SET_ID:
+ case NFTNL_SET_POLICY:
+ case NFTNL_SET_DESC_SIZE:
+ case NFTNL_SET_DESC_CONCAT:
+ case NFTNL_SET_TIMEOUT:
+ case NFTNL_SET_GC_INTERVAL:
+ break;
+ case NFTNL_SET_USERDATA:
+ xfree(s->user.data);
+ break;
+ case NFTNL_SET_EXPR:
+ case NFTNL_SET_EXPRESSIONS:
+ list_for_each_entry_safe(expr, tmp, &s->expr_list, head)
+ nftnl_expr_free(expr);
+ break;
+ default:
+ return;
+ }
+
+ s->flags &= ~(1 << attr);
+}
+
+static uint32_t nftnl_set_validate[NFTNL_SET_MAX + 1] = {
+ [NFTNL_SET_HANDLE] = sizeof(uint64_t),
+ [NFTNL_SET_FLAGS] = sizeof(uint32_t),
+ [NFTNL_SET_KEY_TYPE] = sizeof(uint32_t),
+ [NFTNL_SET_KEY_LEN] = sizeof(uint32_t),
+ [NFTNL_SET_DATA_TYPE] = sizeof(uint32_t),
+ [NFTNL_SET_DATA_LEN] = sizeof(uint32_t),
+ [NFTNL_SET_OBJ_TYPE] = sizeof(uint32_t),
+ [NFTNL_SET_FAMILY] = sizeof(uint32_t),
+ [NFTNL_SET_POLICY] = sizeof(uint32_t),
+ [NFTNL_SET_DESC_SIZE] = sizeof(uint32_t),
+ [NFTNL_SET_TIMEOUT] = sizeof(uint64_t),
+ [NFTNL_SET_GC_INTERVAL] = sizeof(uint32_t),
+};
+
+EXPORT_SYMBOL(nftnl_set_set_data);
+int nftnl_set_set_data(struct nftnl_set *s, uint16_t attr, const void *data,
+ uint32_t data_len)
+{
+ struct nftnl_expr *expr, *tmp;
+
+ nftnl_assert_attr_exists(attr, NFTNL_SET_MAX);
+ nftnl_assert_validate(data, nftnl_set_validate, attr, data_len);
+
+ switch(attr) {
+ case NFTNL_SET_TABLE:
+ if (s->flags & (1 << NFTNL_SET_TABLE))
+ xfree(s->table);
+
+ s->table = strdup(data);
+ if (!s->table)
+ return -1;
+ break;
+ case NFTNL_SET_NAME:
+ if (s->flags & (1 << NFTNL_SET_NAME))
+ xfree(s->name);
+
+ s->name = strdup(data);
+ if (!s->name)
+ return -1;
+ break;
+ case NFTNL_SET_HANDLE:
+ memcpy(&s->handle, data, sizeof(s->handle));
+ break;
+ case NFTNL_SET_FLAGS:
+ memcpy(&s->set_flags, data, sizeof(s->set_flags));
+ break;
+ case NFTNL_SET_KEY_TYPE:
+ memcpy(&s->key_type, data, sizeof(s->key_type));
+ break;
+ case NFTNL_SET_KEY_LEN:
+ memcpy(&s->key_len, data, sizeof(s->key_len));
+ break;
+ case NFTNL_SET_DATA_TYPE:
+ memcpy(&s->data_type, data, sizeof(s->data_type));
+ break;
+ case NFTNL_SET_DATA_LEN:
+ memcpy(&s->data_len, data, sizeof(s->data_len));
+ break;
+ case NFTNL_SET_OBJ_TYPE:
+ memcpy(&s->obj_type, data, sizeof(s->obj_type));
+ break;
+ case NFTNL_SET_FAMILY:
+ memcpy(&s->family, data, sizeof(s->family));
+ break;
+ case NFTNL_SET_ID:
+ memcpy(&s->id, data, sizeof(s->id));
+ break;
+ case NFTNL_SET_POLICY:
+ memcpy(&s->policy, data, sizeof(s->policy));
+ break;
+ case NFTNL_SET_DESC_SIZE:
+ memcpy(&s->desc.size, data, sizeof(s->desc.size));
+ break;
+ case NFTNL_SET_DESC_CONCAT:
+ memcpy(&s->desc.field_len, data, data_len);
+ while (s->desc.field_len[++s->desc.field_count]);
+ break;
+ case NFTNL_SET_TIMEOUT:
+ memcpy(&s->timeout, data, sizeof(s->timeout));
+ break;
+ case NFTNL_SET_GC_INTERVAL:
+ memcpy(&s->gc_interval, data, sizeof(s->gc_interval));
+ break;
+ case NFTNL_SET_USERDATA:
+ if (s->flags & (1 << NFTNL_SET_USERDATA))
+ xfree(s->user.data);
+
+ s->user.data = malloc(data_len);
+ if (!s->user.data)
+ return -1;
+ memcpy(s->user.data, data, data_len);
+ s->user.len = data_len;
+ break;
+ case NFTNL_SET_EXPR:
+ list_for_each_entry_safe(expr, tmp, &s->expr_list, head)
+ nftnl_expr_free(expr);
+
+ expr = (void *)data;
+ list_add(&expr->head, &s->expr_list);
+ break;
+ }
+ s->flags |= (1 << attr);
+ return 0;
+}
+
+int nftnl_set_set(struct nftnl_set *s, uint16_t attr, const void *data) __visible;
+int nftnl_set_set(struct nftnl_set *s, uint16_t attr, const void *data)
+{
+ return nftnl_set_set_data(s, attr, data, nftnl_set_validate[attr]);
+}
+
+EXPORT_SYMBOL(nftnl_set_set_u32);
+void nftnl_set_set_u32(struct nftnl_set *s, uint16_t attr, uint32_t val)
+{
+ nftnl_set_set_data(s, attr, &val, sizeof(uint32_t));
+}
+
+EXPORT_SYMBOL(nftnl_set_set_u64);
+void nftnl_set_set_u64(struct nftnl_set *s, uint16_t attr, uint64_t val)
+{
+ nftnl_set_set_data(s, attr, &val, sizeof(uint64_t));
+}
+
+EXPORT_SYMBOL(nftnl_set_set_str);
+int nftnl_set_set_str(struct nftnl_set *s, uint16_t attr, const char *str)
+{
+ return nftnl_set_set_data(s, attr, str, strlen(str) + 1);
+}
+
+EXPORT_SYMBOL(nftnl_set_get_data);
+const void *nftnl_set_get_data(const struct nftnl_set *s, uint16_t attr,
+ uint32_t *data_len)
+{
+ struct nftnl_expr *expr;
+
+ if (!(s->flags & (1 << attr)))
+ return NULL;
+
+ switch(attr) {
+ case NFTNL_SET_TABLE:
+ *data_len = strlen(s->table) + 1;
+ return s->table;
+ case NFTNL_SET_NAME:
+ *data_len = strlen(s->name) + 1;
+ return s->name;
+ case NFTNL_SET_HANDLE:
+ *data_len = sizeof(uint64_t);
+ return &s->handle;
+ case NFTNL_SET_FLAGS:
+ *data_len = sizeof(uint32_t);
+ return &s->set_flags;
+ case NFTNL_SET_KEY_TYPE:
+ *data_len = sizeof(uint32_t);
+ return &s->key_type;
+ case NFTNL_SET_KEY_LEN:
+ *data_len = sizeof(uint32_t);
+ return &s->key_len;
+ case NFTNL_SET_DATA_TYPE:
+ *data_len = sizeof(uint32_t);
+ return &s->data_type;
+ case NFTNL_SET_DATA_LEN:
+ *data_len = sizeof(uint32_t);
+ return &s->data_len;
+ case NFTNL_SET_OBJ_TYPE:
+ *data_len = sizeof(uint32_t);
+ return &s->obj_type;
+ case NFTNL_SET_FAMILY:
+ *data_len = sizeof(uint32_t);
+ return &s->family;
+ case NFTNL_SET_ID:
+ *data_len = sizeof(uint32_t);
+ return &s->id;
+ case NFTNL_SET_POLICY:
+ *data_len = sizeof(uint32_t);
+ return &s->policy;
+ case NFTNL_SET_DESC_SIZE:
+ *data_len = sizeof(uint32_t);
+ return &s->desc.size;
+ case NFTNL_SET_DESC_CONCAT:
+ *data_len = s->desc.field_count;
+ return s->desc.field_len;
+ case NFTNL_SET_TIMEOUT:
+ *data_len = sizeof(uint64_t);
+ return &s->timeout;
+ case NFTNL_SET_GC_INTERVAL:
+ *data_len = sizeof(uint32_t);
+ return &s->gc_interval;
+ case NFTNL_SET_USERDATA:
+ *data_len = s->user.len;
+ return s->user.data;
+ case NFTNL_SET_EXPR:
+ list_for_each_entry(expr, &s->expr_list, head)
+ break;
+ return expr;
+ }
+ return NULL;
+}
+
+EXPORT_SYMBOL(nftnl_set_get);
+const void *nftnl_set_get(const struct nftnl_set *s, uint16_t attr)
+{
+ uint32_t data_len;
+ return nftnl_set_get_data(s, attr, &data_len);
+}
+
+EXPORT_SYMBOL(nftnl_set_get_str);
+const char *nftnl_set_get_str(const struct nftnl_set *s, uint16_t attr)
+{
+ return nftnl_set_get(s, attr);
+}
+
+EXPORT_SYMBOL(nftnl_set_get_u32);
+uint32_t nftnl_set_get_u32(const struct nftnl_set *s, uint16_t attr)
+{
+ uint32_t data_len;
+ const uint32_t *val = nftnl_set_get_data(s, attr, &data_len);
+
+ nftnl_assert(val, attr, data_len == sizeof(uint32_t));
+
+ return val ? *val : 0;
+}
+
+EXPORT_SYMBOL(nftnl_set_get_u64);
+uint64_t nftnl_set_get_u64(const struct nftnl_set *s, uint16_t attr)
+{
+ uint32_t data_len;
+ const uint64_t *val = nftnl_set_get_data(s, attr, &data_len);
+
+ nftnl_assert(val, attr, data_len == sizeof(uint64_t));
+
+ return val ? *val : 0;
+}
+
+struct nftnl_set *nftnl_set_clone(const struct nftnl_set *set)
+{
+ struct nftnl_set *newset;
+ struct nftnl_set_elem *elem, *newelem;
+
+ newset = nftnl_set_alloc();
+ if (newset == NULL)
+ return NULL;
+
+ memcpy(newset, set, sizeof(*set));
+
+ if (set->flags & (1 << NFTNL_SET_TABLE)) {
+ newset->table = strdup(set->table);
+ if (!newset->table)
+ goto err;
+ }
+ if (set->flags & (1 << NFTNL_SET_NAME)) {
+ newset->name = strdup(set->name);
+ if (!newset->name)
+ goto err;
+ }
+
+ INIT_LIST_HEAD(&newset->element_list);
+ list_for_each_entry(elem, &set->element_list, head) {
+ newelem = nftnl_set_elem_clone(elem);
+ if (newelem == NULL)
+ goto err;
+
+ list_add_tail(&newelem->head, &newset->element_list);
+ }
+
+ return newset;
+err:
+ nftnl_set_free(newset);
+ return NULL;
+}
+
+static void nftnl_set_nlmsg_build_desc_size_payload(struct nlmsghdr *nlh,
+ struct nftnl_set *s)
+{
+ mnl_attr_put_u32(nlh, NFTA_SET_DESC_SIZE, htonl(s->desc.size));
+}
+
+static void nftnl_set_nlmsg_build_desc_concat_payload(struct nlmsghdr *nlh,
+ struct nftnl_set *s)
+{
+ struct nlattr *nest;
+ int i;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_SET_DESC_CONCAT);
+ for (i = 0; i < NFT_REG32_COUNT && i < s->desc.field_count; i++) {
+ struct nlattr *nest_elem;
+
+ nest_elem = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM);
+ mnl_attr_put_u32(nlh, NFTA_SET_FIELD_LEN,
+ htonl(s->desc.field_len[i]));
+ mnl_attr_nest_end(nlh, nest_elem);
+ }
+ mnl_attr_nest_end(nlh, nest);
+}
+
+static void
+nftnl_set_nlmsg_build_desc_payload(struct nlmsghdr *nlh, struct nftnl_set *s)
+{
+ struct nlattr *nest;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_SET_DESC);
+
+ if (s->flags & (1 << NFTNL_SET_DESC_SIZE))
+ nftnl_set_nlmsg_build_desc_size_payload(nlh, s);
+ if (s->flags & (1 << NFTNL_SET_DESC_CONCAT))
+ nftnl_set_nlmsg_build_desc_concat_payload(nlh, s);
+
+ mnl_attr_nest_end(nlh, nest);
+}
+
+EXPORT_SYMBOL(nftnl_set_nlmsg_build_payload);
+void nftnl_set_nlmsg_build_payload(struct nlmsghdr *nlh, struct nftnl_set *s)
+{
+ int num_exprs = 0;
+
+ if (s->flags & (1 << NFTNL_SET_TABLE))
+ mnl_attr_put_strz(nlh, NFTA_SET_TABLE, s->table);
+ if (s->flags & (1 << NFTNL_SET_NAME))
+ mnl_attr_put_strz(nlh, NFTA_SET_NAME, s->name);
+ if (s->flags & (1 << NFTNL_SET_HANDLE))
+ mnl_attr_put_u64(nlh, NFTA_SET_HANDLE, htobe64(s->handle));
+ if (s->flags & (1 << NFTNL_SET_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_SET_FLAGS, htonl(s->set_flags));
+ if (s->flags & (1 << NFTNL_SET_KEY_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_SET_KEY_TYPE, htonl(s->key_type));
+ if (s->flags & (1 << NFTNL_SET_KEY_LEN))
+ mnl_attr_put_u32(nlh, NFTA_SET_KEY_LEN, htonl(s->key_len));
+ /* These are only used to map matching -> action (1:1) */
+ if (s->flags & (1 << NFTNL_SET_DATA_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_SET_DATA_TYPE, htonl(s->data_type));
+ if (s->flags & (1 << NFTNL_SET_DATA_LEN))
+ mnl_attr_put_u32(nlh, NFTA_SET_DATA_LEN, htonl(s->data_len));
+ if (s->flags & (1 << NFTNL_SET_OBJ_TYPE))
+ mnl_attr_put_u32(nlh, NFTA_SET_OBJ_TYPE, htonl(s->obj_type));
+ if (s->flags & (1 << NFTNL_SET_ID))
+ mnl_attr_put_u32(nlh, NFTA_SET_ID, htonl(s->id));
+ if (s->flags & (1 << NFTNL_SET_POLICY))
+ mnl_attr_put_u32(nlh, NFTA_SET_POLICY, htonl(s->policy));
+ if (s->flags & (1 << NFTNL_SET_DESC_SIZE | 1 << NFTNL_SET_DESC_CONCAT))
+ nftnl_set_nlmsg_build_desc_payload(nlh, s);
+ if (s->flags & (1 << NFTNL_SET_TIMEOUT))
+ mnl_attr_put_u64(nlh, NFTA_SET_TIMEOUT, htobe64(s->timeout));
+ if (s->flags & (1 << NFTNL_SET_GC_INTERVAL))
+ mnl_attr_put_u32(nlh, NFTA_SET_GC_INTERVAL, htonl(s->gc_interval));
+ if (s->flags & (1 << NFTNL_SET_USERDATA))
+ mnl_attr_put(nlh, NFTA_SET_USERDATA, s->user.len, s->user.data);
+ if (!list_empty(&s->expr_list)) {
+ struct nftnl_expr *expr;
+
+ list_for_each_entry(expr, &s->expr_list, head)
+ num_exprs++;
+
+ if (num_exprs == 1) {
+ struct nlattr *nest1;
+
+ nest1 = mnl_attr_nest_start(nlh, NFTA_SET_EXPR);
+ list_for_each_entry(expr, &s->expr_list, head)
+ nftnl_expr_build_payload(nlh, expr);
+
+ mnl_attr_nest_end(nlh, nest1);
+ } else if (num_exprs > 1) {
+ struct nlattr *nest1, *nest2;
+
+ nest1 = mnl_attr_nest_start(nlh, NFTA_SET_EXPRESSIONS);
+ list_for_each_entry(expr, &s->expr_list, head) {
+ nest2 = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM);
+ nftnl_expr_build_payload(nlh, expr);
+ mnl_attr_nest_end(nlh, nest2);
+ }
+ mnl_attr_nest_end(nlh, nest1);
+ }
+ }
+}
+
+EXPORT_SYMBOL(nftnl_set_add_expr);
+void nftnl_set_add_expr(struct nftnl_set *s, struct nftnl_expr *expr)
+{
+ list_add_tail(&expr->head, &s->expr_list);
+}
+
+EXPORT_SYMBOL(nftnl_set_expr_foreach);
+int nftnl_set_expr_foreach(const struct nftnl_set *s,
+ int (*cb)(struct nftnl_expr *e, void *data),
+ void *data)
+{
+ struct nftnl_expr *cur, *tmp;
+ int ret;
+
+ list_for_each_entry_safe(cur, tmp, &s->expr_list, head) {
+ ret = cb(cur, data);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int nftnl_set_parse_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_SET_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_SET_TABLE:
+ case NFTA_SET_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ case NFTA_SET_HANDLE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ case NFTA_SET_FLAGS:
+ case NFTA_SET_KEY_TYPE:
+ case NFTA_SET_KEY_LEN:
+ case NFTA_SET_DATA_TYPE:
+ case NFTA_SET_DATA_LEN:
+ case NFTA_SET_ID:
+ case NFTA_SET_POLICY:
+ case NFTA_SET_GC_INTERVAL:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_SET_USERDATA:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ abi_breakage();
+ break;
+ case NFTA_SET_TIMEOUT:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ case NFTA_SET_DESC:
+ case NFTA_SET_EXPR:
+ case NFTA_SET_EXPRESSIONS:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int
+nftnl_set_desc_concat_field_parse_attr_cb(const struct nlattr *attr, void *data)
+{
+ int type = mnl_attr_get_type(attr);
+ struct nftnl_set *s = data;
+
+ if (type != NFTA_SET_FIELD_LEN)
+ return MNL_CB_OK;
+
+ if (mnl_attr_validate(attr, MNL_TYPE_U32))
+ return MNL_CB_ERROR;
+
+ s->desc.field_len[s->desc.field_count] = ntohl(mnl_attr_get_u32(attr));
+ s->desc.field_count++;
+
+ return MNL_CB_OK;
+}
+
+static int
+nftnl_set_desc_concat_parse_attr_cb(const struct nlattr *attr, void *data)
+{
+ int type = mnl_attr_get_type(attr);
+ struct nftnl_set *s = data;
+
+ if (type != NFTA_LIST_ELEM)
+ return MNL_CB_OK;
+
+ return mnl_attr_parse_nested(attr,
+ nftnl_set_desc_concat_field_parse_attr_cb,
+ s);
+}
+
+static int nftnl_set_desc_parse_attr_cb(const struct nlattr *attr, void *data)
+{
+ int type = mnl_attr_get_type(attr), err;
+ struct nftnl_set *s = data;
+
+ if (mnl_attr_type_valid(attr, NFTA_SET_DESC_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_SET_DESC_SIZE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ abi_breakage();
+ break;
+ }
+
+ s->desc.size = ntohl(mnl_attr_get_u32(attr));
+ s->flags |= (1 << NFTNL_SET_DESC_SIZE);
+ break;
+ case NFTA_SET_DESC_CONCAT:
+ err = mnl_attr_parse_nested(attr,
+ nftnl_set_desc_concat_parse_attr_cb,
+ s);
+ if (err != MNL_CB_OK)
+ abi_breakage();
+
+ s->flags |= (1 << NFTNL_SET_DESC_CONCAT);
+ break;
+ default:
+ break;
+ }
+
+ return MNL_CB_OK;
+}
+
+static int nftnl_set_desc_parse(struct nftnl_set *s, const struct nlattr *attr)
+{
+ return mnl_attr_parse_nested(attr, nftnl_set_desc_parse_attr_cb, s);
+}
+
+EXPORT_SYMBOL(nftnl_set_nlmsg_parse);
+int nftnl_set_nlmsg_parse(const struct nlmsghdr *nlh, struct nftnl_set *s)
+{
+ struct nlattr *tb[NFTA_SET_MAX+1] = {};
+ struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
+ struct nftnl_expr *expr, *next;
+ int ret;
+
+ if (mnl_attr_parse(nlh, sizeof(*nfg), nftnl_set_parse_attr_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_SET_TABLE]) {
+ if (s->flags & (1 << NFTNL_SET_TABLE))
+ xfree(s->table);
+ s->table = strdup(mnl_attr_get_str(tb[NFTA_SET_TABLE]));
+ if (!s->table)
+ return -1;
+ s->flags |= (1 << NFTNL_SET_TABLE);
+ }
+ if (tb[NFTA_SET_NAME]) {
+ if (s->flags & (1 << NFTNL_SET_NAME))
+ xfree(s->name);
+ s->name = strdup(mnl_attr_get_str(tb[NFTA_SET_NAME]));
+ if (!s->name)
+ return -1;
+ s->flags |= (1 << NFTNL_SET_NAME);
+ }
+ if (tb[NFTA_SET_HANDLE]) {
+ s->handle = be64toh(mnl_attr_get_u64(tb[NFTA_SET_HANDLE]));
+ s->flags |= (1 << NFTNL_SET_HANDLE);
+ }
+ if (tb[NFTA_SET_FLAGS]) {
+ s->set_flags = ntohl(mnl_attr_get_u32(tb[NFTA_SET_FLAGS]));
+ s->flags |= (1 << NFTNL_SET_FLAGS);
+ }
+ if (tb[NFTA_SET_KEY_TYPE]) {
+ s->key_type = ntohl(mnl_attr_get_u32(tb[NFTA_SET_KEY_TYPE]));
+ s->flags |= (1 << NFTNL_SET_KEY_TYPE);
+ }
+ if (tb[NFTA_SET_KEY_LEN]) {
+ s->key_len = ntohl(mnl_attr_get_u32(tb[NFTA_SET_KEY_LEN]));
+ s->flags |= (1 << NFTNL_SET_KEY_LEN);
+ }
+ if (tb[NFTA_SET_DATA_TYPE]) {
+ s->data_type = ntohl(mnl_attr_get_u32(tb[NFTA_SET_DATA_TYPE]));
+ s->flags |= (1 << NFTNL_SET_DATA_TYPE);
+ }
+ if (tb[NFTA_SET_DATA_LEN]) {
+ s->data_len = ntohl(mnl_attr_get_u32(tb[NFTA_SET_DATA_LEN]));
+ s->flags |= (1 << NFTNL_SET_DATA_LEN);
+ }
+ if (tb[NFTA_SET_OBJ_TYPE]) {
+ s->obj_type = ntohl(mnl_attr_get_u32(tb[NFTA_SET_OBJ_TYPE]));
+ s->flags |= (1 << NFTNL_SET_OBJ_TYPE);
+ }
+ if (tb[NFTA_SET_ID]) {
+ s->id = ntohl(mnl_attr_get_u32(tb[NFTA_SET_ID]));
+ s->flags |= (1 << NFTNL_SET_ID);
+ }
+ if (tb[NFTA_SET_POLICY]) {
+ s->policy = ntohl(mnl_attr_get_u32(tb[NFTA_SET_POLICY]));
+ s->flags |= (1 << NFTNL_SET_POLICY);
+ }
+ if (tb[NFTA_SET_TIMEOUT]) {
+ s->timeout = be64toh(mnl_attr_get_u64(tb[NFTA_SET_TIMEOUT]));
+ s->flags |= (1 << NFTNL_SET_TIMEOUT);
+ }
+ if (tb[NFTA_SET_GC_INTERVAL]) {
+ s->gc_interval = ntohl(mnl_attr_get_u32(tb[NFTA_SET_GC_INTERVAL]));
+ s->flags |= (1 << NFTNL_SET_GC_INTERVAL);
+ }
+ if (tb[NFTA_SET_USERDATA]) {
+ ret = nftnl_set_set_data(s, NFTNL_SET_USERDATA,
+ mnl_attr_get_payload(tb[NFTA_SET_USERDATA]),
+ mnl_attr_get_payload_len(tb[NFTA_SET_USERDATA]));
+ if (ret < 0)
+ return ret;
+ }
+ if (tb[NFTA_SET_DESC]) {
+ ret = nftnl_set_desc_parse(s, tb[NFTA_SET_DESC]);
+ if (ret < 0)
+ return ret;
+ }
+ if (tb[NFTA_SET_EXPR]) {
+ expr = nftnl_expr_parse(tb[NFTA_SET_EXPR]);
+ if (!expr)
+ goto out_set_expr;
+
+ list_add(&expr->head, &s->expr_list);
+ s->flags |= (1 << NFTNL_SET_EXPR);
+ } else if (tb[NFTA_SET_EXPRESSIONS]) {
+ struct nlattr *attr;
+
+ mnl_attr_for_each_nested(attr, tb[NFTA_SET_EXPRESSIONS]) {
+ if (mnl_attr_get_type(attr) != NFTA_LIST_ELEM)
+ goto out_set_expr;
+
+ expr = nftnl_expr_parse(attr);
+ if (expr == NULL)
+ goto out_set_expr;
+
+ list_add_tail(&expr->head, &s->expr_list);
+ }
+ s->flags |= (1 << NFTNL_SET_EXPRESSIONS);
+ }
+
+ s->family = nfg->nfgen_family;
+ s->flags |= (1 << NFTNL_SET_FAMILY);
+
+ return 0;
+out_set_expr:
+ list_for_each_entry_safe(expr, next, &s->expr_list, head)
+ nftnl_expr_free(expr);
+
+ return -1;
+}
+
+static int nftnl_set_do_parse(struct nftnl_set *s, enum nftnl_parse_type type,
+ const void *data, struct nftnl_parse_err *err,
+ enum nftnl_parse_input input)
+{
+ int ret;
+ struct nftnl_parse_err perr = {};
+
+ switch (type) {
+ case NFTNL_PARSE_JSON:
+ case NFTNL_PARSE_XML:
+ default:
+ ret = -1;
+ errno = EOPNOTSUPP;
+ break;
+ }
+
+ if (err != NULL)
+ *err = perr;
+
+ return ret;
+}
+
+EXPORT_SYMBOL(nftnl_set_parse);
+int nftnl_set_parse(struct nftnl_set *s, enum nftnl_parse_type type,
+ const char *data, struct nftnl_parse_err *err)
+{
+ return nftnl_set_do_parse(s, type, data, err, NFTNL_PARSE_BUFFER);
+}
+
+EXPORT_SYMBOL(nftnl_set_parse_file);
+int nftnl_set_parse_file(struct nftnl_set *s, enum nftnl_parse_type type,
+ FILE *fp, struct nftnl_parse_err *err)
+{
+ return nftnl_set_do_parse(s, type, fp, err, NFTNL_PARSE_FILE);
+}
+
+static int nftnl_set_snprintf_default(char *buf, size_t remain,
+ const struct nftnl_set *s,
+ uint32_t type, uint32_t flags)
+{
+ struct nftnl_set_elem *elem;
+ int ret, offset = 0;
+
+ ret = snprintf(buf, remain, "%s %s %x",
+ s->name, s->table, s->set_flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (s->flags & (1 << NFTNL_SET_TIMEOUT)) {
+ ret = snprintf(buf + offset, remain, " timeout %"PRIu64"ms",
+ s->timeout);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (s->flags & (1 << NFTNL_SET_GC_INTERVAL)) {
+ ret = snprintf(buf + offset, remain, " gc_interval %ums",
+ s->gc_interval);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (s->flags & (1 << NFTNL_SET_POLICY)) {
+ ret = snprintf(buf + offset, remain, " policy %u", s->policy);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ if (s->flags & (1 << NFTNL_SET_DESC_SIZE)) {
+ ret = snprintf(buf + offset, remain, " size %u", s->desc.size);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ /* Empty set? Skip printinf of elements */
+ if (list_empty(&s->element_list))
+ return offset;
+
+ ret = snprintf(buf + offset, remain, "\n");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ list_for_each_entry(elem, &s->element_list, head) {
+ ret = snprintf(buf + offset, remain, "\t");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_set_elem_snprintf_default(buf + offset, remain,
+ elem);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+static int nftnl_set_cmd_snprintf(char *buf, size_t remain,
+ const struct nftnl_set *s, uint32_t cmd,
+ uint32_t type, uint32_t flags)
+{
+ uint32_t inner_flags = flags;
+ int ret, offset = 0;
+
+ if (type != NFTNL_OUTPUT_DEFAULT)
+ return -1;
+
+ /* prevent set_elems to print as events */
+ inner_flags &= ~NFTNL_OF_EVENT_ANY;
+
+ ret = nftnl_set_snprintf_default(buf + offset, remain, s, type,
+ inner_flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ return offset;
+}
+
+EXPORT_SYMBOL(nftnl_set_snprintf);
+int nftnl_set_snprintf(char *buf, size_t size, const struct nftnl_set *s,
+ uint32_t type, uint32_t flags)
+{
+ if (size)
+ buf[0] = '\0';
+
+ return nftnl_set_cmd_snprintf(buf, size, s, nftnl_flag2cmd(flags), type,
+ flags);
+}
+
+static int nftnl_set_do_snprintf(char *buf, size_t size, const void *s,
+ uint32_t cmd, uint32_t type, uint32_t flags)
+{
+ return nftnl_set_snprintf(buf, size, s, type, flags);
+}
+
+EXPORT_SYMBOL(nftnl_set_fprintf);
+int nftnl_set_fprintf(FILE *fp, const struct nftnl_set *s, uint32_t type,
+ uint32_t flags)
+{
+ return nftnl_fprintf(fp, s, NFTNL_CMD_UNSPEC, type, flags,
+ nftnl_set_do_snprintf);
+}
+
+EXPORT_SYMBOL(nftnl_set_elem_add);
+void nftnl_set_elem_add(struct nftnl_set *s, struct nftnl_set_elem *elem)
+{
+ list_add_tail(&elem->head, &s->element_list);
+}
+
+#define SET_NAME_HSIZE 512
+
+struct nftnl_set_list {
+ struct list_head list;
+ struct hlist_head name_hash[SET_NAME_HSIZE];
+};
+
+EXPORT_SYMBOL(nftnl_set_list_alloc);
+struct nftnl_set_list *nftnl_set_list_alloc(void)
+{
+ struct nftnl_set_list *list;
+ int i;
+
+ list = calloc(1, sizeof(struct nftnl_set_list));
+ if (list == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&list->list);
+ for (i = 0; i < SET_NAME_HSIZE; i++)
+ INIT_HLIST_HEAD(&list->name_hash[i]);
+
+ return list;
+}
+
+EXPORT_SYMBOL(nftnl_set_list_free);
+void nftnl_set_list_free(struct nftnl_set_list *list)
+{
+ struct nftnl_set *s, *tmp;
+
+ list_for_each_entry_safe(s, tmp, &list->list, head) {
+ list_del(&s->head);
+ hlist_del(&s->hnode);
+ nftnl_set_free(s);
+ }
+ xfree(list);
+}
+
+EXPORT_SYMBOL(nftnl_set_list_is_empty);
+int nftnl_set_list_is_empty(const struct nftnl_set_list *list)
+{
+ return list_empty(&list->list);
+}
+
+static uint32_t djb_hash(const char *key)
+{
+ uint32_t i, hash = 5381;
+
+ for (i = 0; i < strlen(key); i++)
+ hash = ((hash << 5) + hash) + key[i];
+
+ return hash;
+}
+
+EXPORT_SYMBOL(nftnl_set_list_add);
+void nftnl_set_list_add(struct nftnl_set *s, struct nftnl_set_list *list)
+{
+ int key = djb_hash(s->name) % SET_NAME_HSIZE;
+
+ hlist_add_head(&s->hnode, &list->name_hash[key]);
+ list_add(&s->head, &list->list);
+}
+
+EXPORT_SYMBOL(nftnl_set_list_add_tail);
+void nftnl_set_list_add_tail(struct nftnl_set *s, struct nftnl_set_list *list)
+{
+ int key = djb_hash(s->name) % SET_NAME_HSIZE;
+
+ hlist_add_head(&s->hnode, &list->name_hash[key]);
+ list_add_tail(&s->head, &list->list);
+}
+
+EXPORT_SYMBOL(nftnl_set_list_del);
+void nftnl_set_list_del(struct nftnl_set *s)
+{
+ list_del(&s->head);
+ hlist_del(&s->hnode);
+}
+
+EXPORT_SYMBOL(nftnl_set_list_foreach);
+int nftnl_set_list_foreach(struct nftnl_set_list *set_list,
+ int (*cb)(struct nftnl_set *t, void *data), void *data)
+{
+ struct nftnl_set *cur, *tmp;
+ int ret;
+
+ list_for_each_entry_safe(cur, tmp, &set_list->list, head) {
+ ret = cb(cur, data);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+struct nftnl_set_list_iter {
+ const struct nftnl_set_list *list;
+ struct nftnl_set *cur;
+};
+
+EXPORT_SYMBOL(nftnl_set_list_iter_create);
+struct nftnl_set_list_iter *
+nftnl_set_list_iter_create(const struct nftnl_set_list *l)
+{
+ struct nftnl_set_list_iter *iter;
+
+ iter = calloc(1, sizeof(struct nftnl_set_list_iter));
+ if (iter == NULL)
+ return NULL;
+
+ iter->list = l;
+ if (nftnl_set_list_is_empty(l))
+ iter->cur = NULL;
+ else
+ iter->cur = list_entry(l->list.next, struct nftnl_set, head);
+
+ return iter;
+}
+
+EXPORT_SYMBOL(nftnl_set_list_iter_cur);
+struct nftnl_set *
+nftnl_set_list_iter_cur(const struct nftnl_set_list_iter *iter)
+{
+ return iter->cur;
+}
+
+EXPORT_SYMBOL(nftnl_set_list_iter_next);
+struct nftnl_set *nftnl_set_list_iter_next(struct nftnl_set_list_iter *iter)
+{
+ struct nftnl_set *s = iter->cur;
+
+ if (s == NULL)
+ return NULL;
+
+ /* get next rule, if any */
+ iter->cur = list_entry(iter->cur->head.next, struct nftnl_set, head);
+ if (&iter->cur->head == iter->list->list.next)
+ return NULL;
+
+ return s;
+}
+
+EXPORT_SYMBOL(nftnl_set_list_iter_destroy);
+void nftnl_set_list_iter_destroy(const struct nftnl_set_list_iter *iter)
+{
+ xfree(iter);
+}
+
+EXPORT_SYMBOL(nftnl_set_list_lookup_byname);
+struct nftnl_set *
+nftnl_set_list_lookup_byname(struct nftnl_set_list *set_list, const char *set)
+{
+ int key = djb_hash(set) % SET_NAME_HSIZE;
+ struct hlist_node *n;
+ struct nftnl_set *s;
+
+ hlist_for_each_entry(s, n, &set_list->name_hash[key], hnode) {
+ if (!strcmp(set, s->name))
+ return s;
+ }
+ return NULL;
+}
+
+int nftnl_set_lookup_id(struct nftnl_expr *e,
+ struct nftnl_set_list *set_list, uint32_t *set_id)
+{
+ const char *set_name;
+ struct nftnl_set *s;
+
+ set_name = nftnl_expr_get_str(e, NFTNL_EXPR_LOOKUP_SET);
+ if (set_name == NULL)
+ return 0;
+
+ s = nftnl_set_list_lookup_byname(set_list, set_name);
+ if (s == NULL)
+ return 0;
+
+ *set_id = nftnl_set_get_u32(s, NFTNL_SET_ID);
+ return 1;
+}
diff --git a/src/set_elem.c b/src/set_elem.c
new file mode 100644
index 0000000..884faff
--- /dev/null
+++ b/src/set_elem.c
@@ -0,0 +1,918 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+#include "internal.h"
+
+#include <time.h>
+#include <endian.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftnl/set.h>
+#include <libnftnl/rule.h>
+#include <libnftnl/expr.h>
+
+EXPORT_SYMBOL(nftnl_set_elem_alloc);
+struct nftnl_set_elem *nftnl_set_elem_alloc(void)
+{
+ struct nftnl_set_elem *s;
+
+ s = calloc(1, sizeof(struct nftnl_set_elem));
+ if (s == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&s->expr_list);
+
+ return s;
+}
+
+EXPORT_SYMBOL(nftnl_set_elem_free);
+void nftnl_set_elem_free(struct nftnl_set_elem *s)
+{
+ struct nftnl_expr *e, *tmp;
+
+ if (s->flags & (1 << NFTNL_SET_ELEM_CHAIN))
+ xfree(s->data.chain);
+
+ list_for_each_entry_safe(e, tmp, &s->expr_list, head)
+ nftnl_expr_free(e);
+
+ if (s->flags & (1 << NFTNL_SET_ELEM_USERDATA))
+ xfree(s->user.data);
+
+ if (s->flags & (1 << NFTNL_SET_ELEM_OBJREF))
+ xfree(s->objref);
+
+ xfree(s);
+}
+
+EXPORT_SYMBOL(nftnl_set_elem_is_set);
+bool nftnl_set_elem_is_set(const struct nftnl_set_elem *s, uint16_t attr)
+{
+ return s->flags & (1 << attr);
+}
+
+EXPORT_SYMBOL(nftnl_set_elem_unset);
+void nftnl_set_elem_unset(struct nftnl_set_elem *s, uint16_t attr)
+{
+ struct nftnl_expr *expr, *tmp;
+
+ if (!(s->flags & (1 << attr)))
+ return;
+
+ switch (attr) {
+ case NFTNL_SET_ELEM_CHAIN:
+ xfree(s->data.chain);
+ break;
+ case NFTNL_SET_ELEM_FLAGS:
+ case NFTNL_SET_ELEM_KEY: /* NFTA_SET_ELEM_KEY */
+ case NFTNL_SET_ELEM_KEY_END: /* NFTA_SET_ELEM_KEY_END */
+ case NFTNL_SET_ELEM_VERDICT: /* NFTA_SET_ELEM_DATA */
+ case NFTNL_SET_ELEM_DATA: /* NFTA_SET_ELEM_DATA */
+ case NFTNL_SET_ELEM_TIMEOUT: /* NFTA_SET_ELEM_TIMEOUT */
+ case NFTNL_SET_ELEM_EXPIRATION: /* NFTA_SET_ELEM_EXPIRATION */
+ break;
+ case NFTNL_SET_ELEM_USERDATA: /* NFTA_SET_ELEM_USERDATA */
+ xfree(s->user.data);
+ break;
+ case NFTNL_SET_ELEM_EXPR:
+ case NFTNL_SET_ELEM_EXPRESSIONS:
+ list_for_each_entry_safe(expr, tmp, &s->expr_list, head)
+ nftnl_expr_free(expr);
+ break;
+ case NFTNL_SET_ELEM_OBJREF:
+ xfree(s->objref);
+ break;
+ default:
+ return;
+ }
+
+ s->flags &= ~(1 << attr);
+}
+
+static uint32_t nftnl_set_elem_validate[NFTNL_SET_ELEM_MAX + 1] = {
+ [NFTNL_SET_ELEM_FLAGS] = sizeof(uint32_t),
+ [NFTNL_SET_ELEM_VERDICT] = sizeof(uint32_t),
+ [NFTNL_SET_ELEM_TIMEOUT] = sizeof(uint64_t),
+ [NFTNL_SET_ELEM_EXPIRATION] = sizeof(uint64_t),
+};
+
+EXPORT_SYMBOL(nftnl_set_elem_set);
+int nftnl_set_elem_set(struct nftnl_set_elem *s, uint16_t attr,
+ const void *data, uint32_t data_len)
+{
+ struct nftnl_expr *expr, *tmp;
+
+ nftnl_assert_attr_exists(attr, NFTNL_SET_ELEM_MAX);
+ nftnl_assert_validate(data, nftnl_set_elem_validate, attr, data_len);
+
+ switch(attr) {
+ case NFTNL_SET_ELEM_FLAGS:
+ memcpy(&s->set_elem_flags, data, sizeof(s->set_elem_flags));
+ break;
+ case NFTNL_SET_ELEM_KEY: /* NFTA_SET_ELEM_KEY */
+ memcpy(&s->key.val, data, data_len);
+ s->key.len = data_len;
+ break;
+ case NFTNL_SET_ELEM_KEY_END: /* NFTA_SET_ELEM_KEY_END */
+ memcpy(&s->key_end.val, data, data_len);
+ s->key_end.len = data_len;
+ break;
+ case NFTNL_SET_ELEM_VERDICT: /* NFTA_SET_ELEM_DATA */
+ memcpy(&s->data.verdict, data, sizeof(s->data.verdict));
+ break;
+ case NFTNL_SET_ELEM_CHAIN: /* NFTA_SET_ELEM_DATA */
+ if (s->flags & (1 << NFTNL_SET_ELEM_CHAIN))
+ xfree(s->data.chain);
+
+ s->data.chain = strdup(data);
+ if (!s->data.chain)
+ return -1;
+ break;
+ case NFTNL_SET_ELEM_DATA: /* NFTA_SET_ELEM_DATA */
+ memcpy(s->data.val, data, data_len);
+ s->data.len = data_len;
+ break;
+ case NFTNL_SET_ELEM_TIMEOUT: /* NFTA_SET_ELEM_TIMEOUT */
+ memcpy(&s->timeout, data, sizeof(s->timeout));
+ break;
+ case NFTNL_SET_ELEM_EXPIRATION: /* NFTA_SET_ELEM_EXPIRATION */
+ memcpy(&s->expiration, data, sizeof(s->expiration));
+ break;
+ case NFTNL_SET_ELEM_USERDATA: /* NFTA_SET_ELEM_USERDATA */
+ if (s->flags & (1 << NFTNL_SET_ELEM_USERDATA))
+ xfree(s->user.data);
+
+ s->user.data = malloc(data_len);
+ if (!s->user.data)
+ return -1;
+ memcpy(s->user.data, data, data_len);
+ s->user.len = data_len;
+ break;
+ case NFTNL_SET_ELEM_OBJREF:
+ if (s->flags & (1 << NFTNL_SET_ELEM_OBJREF))
+ xfree(s->objref);
+
+ s->objref = strdup(data);
+ if (!s->objref)
+ return -1;
+ break;
+ case NFTNL_SET_ELEM_EXPR:
+ list_for_each_entry_safe(expr, tmp, &s->expr_list, head)
+ nftnl_expr_free(expr);
+
+ expr = (void *)data;
+ list_add(&expr->head, &s->expr_list);
+ break;
+ }
+ s->flags |= (1 << attr);
+ return 0;
+}
+
+EXPORT_SYMBOL(nftnl_set_elem_set_u32);
+void nftnl_set_elem_set_u32(struct nftnl_set_elem *s, uint16_t attr, uint32_t val)
+{
+ nftnl_set_elem_set(s, attr, &val, sizeof(uint32_t));
+}
+
+EXPORT_SYMBOL(nftnl_set_elem_set_u64);
+void nftnl_set_elem_set_u64(struct nftnl_set_elem *s, uint16_t attr, uint64_t val)
+{
+ nftnl_set_elem_set(s, attr, &val, sizeof(uint64_t));
+}
+
+EXPORT_SYMBOL(nftnl_set_elem_set_str);
+int nftnl_set_elem_set_str(struct nftnl_set_elem *s, uint16_t attr, const char *str)
+{
+ return nftnl_set_elem_set(s, attr, str, strlen(str) + 1);
+}
+
+EXPORT_SYMBOL(nftnl_set_elem_get);
+const void *nftnl_set_elem_get(struct nftnl_set_elem *s, uint16_t attr, uint32_t *data_len)
+{
+ struct nftnl_expr *expr;
+
+ if (!(s->flags & (1 << attr)))
+ return NULL;
+
+ switch(attr) {
+ case NFTNL_SET_ELEM_FLAGS:
+ *data_len = sizeof(s->set_elem_flags);
+ return &s->set_elem_flags;
+ case NFTNL_SET_ELEM_KEY: /* NFTA_SET_ELEM_KEY */
+ *data_len = s->key.len;
+ return &s->key.val;
+ case NFTNL_SET_ELEM_KEY_END: /* NFTA_SET_ELEM_KEY_END */
+ *data_len = s->key_end.len;
+ return &s->key_end.val;
+ case NFTNL_SET_ELEM_VERDICT: /* NFTA_SET_ELEM_DATA */
+ *data_len = sizeof(s->data.verdict);
+ return &s->data.verdict;
+ case NFTNL_SET_ELEM_CHAIN: /* NFTA_SET_ELEM_DATA */
+ *data_len = strlen(s->data.chain) + 1;
+ return s->data.chain;
+ case NFTNL_SET_ELEM_DATA: /* NFTA_SET_ELEM_DATA */
+ *data_len = s->data.len;
+ return &s->data.val;
+ case NFTNL_SET_ELEM_TIMEOUT: /* NFTA_SET_ELEM_TIMEOUT */
+ *data_len = sizeof(s->timeout);
+ return &s->timeout;
+ case NFTNL_SET_ELEM_EXPIRATION: /* NFTA_SET_ELEM_EXPIRATION */
+ *data_len = sizeof(s->expiration);
+ return &s->expiration;
+ case NFTNL_SET_ELEM_USERDATA:
+ *data_len = s->user.len;
+ return s->user.data;
+ case NFTNL_SET_ELEM_EXPR:
+ list_for_each_entry(expr, &s->expr_list, head)
+ break;
+ return expr;
+ case NFTNL_SET_ELEM_OBJREF:
+ *data_len = strlen(s->objref) + 1;
+ return s->objref;
+ }
+ return NULL;
+}
+
+EXPORT_SYMBOL(nftnl_set_elem_get_str);
+const char *nftnl_set_elem_get_str(struct nftnl_set_elem *s, uint16_t attr)
+{
+ uint32_t size;
+
+ return nftnl_set_elem_get(s, attr, &size);
+}
+
+EXPORT_SYMBOL(nftnl_set_elem_get_u32);
+uint32_t nftnl_set_elem_get_u32(struct nftnl_set_elem *s, uint16_t attr)
+{
+ uint32_t size, val;
+
+ memcpy(&val, nftnl_set_elem_get(s, attr, &size), sizeof(val));
+
+ return val;
+}
+
+EXPORT_SYMBOL(nftnl_set_elem_get_u64);
+uint64_t nftnl_set_elem_get_u64(struct nftnl_set_elem *s, uint16_t attr)
+{
+ uint32_t size;
+ uint64_t val;
+
+ memcpy(&val, nftnl_set_elem_get(s, attr, &size), sizeof(val));
+
+ return val;
+}
+
+struct nftnl_set_elem *nftnl_set_elem_clone(struct nftnl_set_elem *elem)
+{
+ struct nftnl_set_elem *newelem;
+
+ newelem = nftnl_set_elem_alloc();
+ if (newelem == NULL)
+ return NULL;
+
+ memcpy(newelem, elem, sizeof(*elem));
+
+ if (elem->flags & (1 << NFTNL_SET_ELEM_CHAIN)) {
+ newelem->data.chain = strdup(elem->data.chain);
+ if (!newelem->data.chain)
+ goto err;
+ }
+
+ return newelem;
+err:
+ nftnl_set_elem_free(newelem);
+ return NULL;
+}
+
+EXPORT_SYMBOL(nftnl_set_elem_nlmsg_build_payload);
+void nftnl_set_elem_nlmsg_build_payload(struct nlmsghdr *nlh,
+ struct nftnl_set_elem *e)
+{
+ struct nftnl_expr *expr;
+ int num_exprs = 0;
+
+ if (e->flags & (1 << NFTNL_SET_ELEM_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_SET_ELEM_FLAGS, htonl(e->set_elem_flags));
+ if (e->flags & (1 << NFTNL_SET_ELEM_TIMEOUT))
+ mnl_attr_put_u64(nlh, NFTA_SET_ELEM_TIMEOUT, htobe64(e->timeout));
+ if (e->flags & (1 << NFTNL_SET_ELEM_EXPIRATION))
+ mnl_attr_put_u64(nlh, NFTA_SET_ELEM_EXPIRATION, htobe64(e->expiration));
+ if (e->flags & (1 << NFTNL_SET_ELEM_KEY)) {
+ struct nlattr *nest1;
+
+ nest1 = mnl_attr_nest_start(nlh, NFTA_SET_ELEM_KEY);
+ mnl_attr_put(nlh, NFTA_DATA_VALUE, e->key.len, e->key.val);
+ mnl_attr_nest_end(nlh, nest1);
+ }
+ if (e->flags & (1 << NFTNL_SET_ELEM_KEY_END)) {
+ struct nlattr *nest1;
+
+ nest1 = mnl_attr_nest_start(nlh, NFTA_SET_ELEM_KEY_END);
+ mnl_attr_put(nlh, NFTA_DATA_VALUE, e->key_end.len,
+ e->key_end.val);
+ mnl_attr_nest_end(nlh, nest1);
+ }
+ if (e->flags & (1 << NFTNL_SET_ELEM_VERDICT)) {
+ struct nlattr *nest1, *nest2;
+
+ nest1 = mnl_attr_nest_start(nlh, NFTA_SET_ELEM_DATA);
+ nest2 = mnl_attr_nest_start(nlh, NFTA_DATA_VERDICT);
+ mnl_attr_put_u32(nlh, NFTA_VERDICT_CODE, htonl(e->data.verdict));
+ if (e->flags & (1 << NFTNL_SET_ELEM_CHAIN))
+ mnl_attr_put_strz(nlh, NFTA_VERDICT_CHAIN, e->data.chain);
+
+ mnl_attr_nest_end(nlh, nest1);
+ mnl_attr_nest_end(nlh, nest2);
+ }
+ if (e->flags & (1 << NFTNL_SET_ELEM_DATA)) {
+ struct nlattr *nest1;
+
+ nest1 = mnl_attr_nest_start(nlh, NFTA_SET_ELEM_DATA);
+ mnl_attr_put(nlh, NFTA_DATA_VALUE, e->data.len, e->data.val);
+ mnl_attr_nest_end(nlh, nest1);
+ }
+ if (e->flags & (1 << NFTNL_SET_ELEM_USERDATA))
+ mnl_attr_put(nlh, NFTA_SET_ELEM_USERDATA, e->user.len, e->user.data);
+ if (e->flags & (1 << NFTNL_SET_ELEM_OBJREF))
+ mnl_attr_put_strz(nlh, NFTA_SET_ELEM_OBJREF, e->objref);
+
+ if (!list_empty(&e->expr_list)) {
+ list_for_each_entry(expr, &e->expr_list, head)
+ num_exprs++;
+
+ if (num_exprs == 1) {
+ struct nlattr *nest1;
+
+ nest1 = mnl_attr_nest_start(nlh, NFTA_SET_ELEM_EXPR);
+ list_for_each_entry(expr, &e->expr_list, head)
+ nftnl_expr_build_payload(nlh, expr);
+
+ mnl_attr_nest_end(nlh, nest1);
+ } else if (num_exprs > 1) {
+ struct nlattr *nest1, *nest2;
+
+ nest1 = mnl_attr_nest_start(nlh, NFTA_SET_ELEM_EXPRESSIONS);
+ list_for_each_entry(expr, &e->expr_list, head) {
+ nest2 = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM);
+ nftnl_expr_build_payload(nlh, expr);
+ mnl_attr_nest_end(nlh, nest2);
+ }
+ mnl_attr_nest_end(nlh, nest1);
+ }
+ }
+}
+
+static void nftnl_set_elem_nlmsg_build_def(struct nlmsghdr *nlh,
+ const struct nftnl_set *s)
+{
+ if (s->flags & (1 << NFTNL_SET_NAME))
+ mnl_attr_put_strz(nlh, NFTA_SET_ELEM_LIST_SET, s->name);
+ if (s->flags & (1 << NFTNL_SET_ID))
+ mnl_attr_put_u32(nlh, NFTA_SET_ELEM_LIST_SET_ID, htonl(s->id));
+ if (s->flags & (1 << NFTNL_SET_TABLE))
+ mnl_attr_put_strz(nlh, NFTA_SET_ELEM_LIST_TABLE, s->table);
+}
+
+EXPORT_SYMBOL(nftnl_set_elem_nlmsg_build);
+struct nlattr *nftnl_set_elem_nlmsg_build(struct nlmsghdr *nlh,
+ struct nftnl_set_elem *elem, int i)
+{
+ struct nlattr *nest2;
+
+ nest2 = mnl_attr_nest_start(nlh, i);
+ nftnl_set_elem_nlmsg_build_payload(nlh, elem);
+ mnl_attr_nest_end(nlh, nest2);
+
+ return nest2;
+}
+
+EXPORT_SYMBOL(nftnl_set_elems_nlmsg_build_payload);
+void nftnl_set_elems_nlmsg_build_payload(struct nlmsghdr *nlh, struct nftnl_set *s)
+{
+ struct nftnl_set_elem *elem;
+ struct nlattr *nest1;
+ int i = 0;
+
+ nftnl_set_elem_nlmsg_build_def(nlh, s);
+
+ if (list_empty(&s->element_list))
+ return;
+
+ nest1 = mnl_attr_nest_start(nlh, NFTA_SET_ELEM_LIST_ELEMENTS);
+ list_for_each_entry(elem, &s->element_list, head)
+ nftnl_set_elem_nlmsg_build(nlh, elem, ++i);
+
+ mnl_attr_nest_end(nlh, nest1);
+}
+
+EXPORT_SYMBOL(nftnl_set_elem_add_expr);
+void nftnl_set_elem_add_expr(struct nftnl_set_elem *e, struct nftnl_expr *expr)
+{
+ list_add_tail(&expr->head, &e->expr_list);
+}
+
+EXPORT_SYMBOL(nftnl_set_elem_expr_foreach);
+int nftnl_set_elem_expr_foreach(struct nftnl_set_elem *e,
+ int (*cb)(struct nftnl_expr *e, void *data),
+ void *data)
+{
+ struct nftnl_expr *cur, *tmp;
+ int ret;
+
+ list_for_each_entry_safe(cur, tmp, &e->expr_list, head) {
+ ret = cb(cur, data);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int nftnl_set_elem_parse_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_SET_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_SET_ELEM_FLAGS:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_SET_ELEM_TIMEOUT:
+ case NFTA_SET_ELEM_EXPIRATION:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ case NFTA_SET_ELEM_KEY:
+ case NFTA_SET_ELEM_KEY_END:
+ case NFTA_SET_ELEM_DATA:
+ case NFTA_SET_ELEM_EXPR:
+ case NFTA_SET_ELEM_EXPRESSIONS:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ abi_breakage();
+ break;
+ case NFTA_SET_ELEM_USERDATA:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int nftnl_set_elems_parse2(struct nftnl_set *s, const struct nlattr *nest)
+{
+ struct nlattr *tb[NFTA_SET_ELEM_MAX+1] = {};
+ struct nftnl_set_elem *e;
+ int ret, type;
+
+ e = nftnl_set_elem_alloc();
+ if (e == NULL)
+ return -1;
+
+ ret = mnl_attr_parse_nested(nest, nftnl_set_elem_parse_attr_cb, tb);
+ if (ret < 0)
+ goto out_set_elem;
+
+ if (tb[NFTA_SET_ELEM_FLAGS]) {
+ e->set_elem_flags =
+ ntohl(mnl_attr_get_u32(tb[NFTA_SET_ELEM_FLAGS]));
+ e->flags |= (1 << NFTNL_SET_ELEM_FLAGS);
+ }
+ if (tb[NFTA_SET_ELEM_TIMEOUT]) {
+ e->timeout = be64toh(mnl_attr_get_u64(tb[NFTA_SET_ELEM_TIMEOUT]));
+ e->flags |= (1 << NFTNL_SET_ELEM_TIMEOUT);
+ }
+ if (tb[NFTA_SET_ELEM_EXPIRATION]) {
+ e->expiration = be64toh(mnl_attr_get_u64(tb[NFTA_SET_ELEM_EXPIRATION]));
+ e->flags |= (1 << NFTNL_SET_ELEM_EXPIRATION);
+ }
+ if (tb[NFTA_SET_ELEM_KEY]) {
+ ret = nftnl_parse_data(&e->key, tb[NFTA_SET_ELEM_KEY], &type);
+ if (ret < 0)
+ goto out_set_elem;
+ e->flags |= (1 << NFTNL_SET_ELEM_KEY);
+ }
+ if (tb[NFTA_SET_ELEM_KEY_END]) {
+ ret = nftnl_parse_data(&e->key_end, tb[NFTA_SET_ELEM_KEY_END],
+ &type);
+ if (ret < 0)
+ goto out_set_elem;
+ e->flags |= (1 << NFTNL_SET_ELEM_KEY_END);
+ }
+ if (tb[NFTA_SET_ELEM_DATA]) {
+ ret = nftnl_parse_data(&e->data, tb[NFTA_SET_ELEM_DATA], &type);
+ if (ret < 0)
+ goto out_set_elem;
+ switch(type) {
+ case DATA_VERDICT:
+ e->flags |= (1 << NFTNL_SET_ELEM_VERDICT);
+ break;
+ case DATA_CHAIN:
+ e->flags |= (1 << NFTNL_SET_ELEM_VERDICT) |
+ (1 << NFTNL_SET_ELEM_CHAIN);
+ break;
+ case DATA_VALUE:
+ e->flags |= (1 << NFTNL_SET_ELEM_DATA);
+ break;
+ }
+ }
+ if (tb[NFTA_SET_ELEM_EXPR]) {
+ struct nftnl_expr *expr;
+
+ expr = nftnl_expr_parse(tb[NFTA_SET_ELEM_EXPR]);
+ if (expr == NULL) {
+ ret = -1;
+ goto out_set_elem;
+ }
+ list_add_tail(&expr->head, &e->expr_list);
+ e->flags |= (1 << NFTNL_SET_ELEM_EXPR);
+ } else if (tb[NFTA_SET_ELEM_EXPRESSIONS]) {
+ struct nftnl_expr *expr;
+ struct nlattr *attr;
+
+ mnl_attr_for_each_nested(attr, tb[NFTA_SET_ELEM_EXPRESSIONS]) {
+ if (mnl_attr_get_type(attr) != NFTA_LIST_ELEM) {
+ ret = -1;
+ goto out_set_elem;
+ }
+ expr = nftnl_expr_parse(attr);
+ if (expr == NULL) {
+ ret = -1;
+ goto out_set_elem;
+ }
+ list_add_tail(&expr->head, &e->expr_list);
+ }
+ e->flags |= (1 << NFTNL_SET_ELEM_EXPRESSIONS);
+ }
+ if (tb[NFTA_SET_ELEM_USERDATA]) {
+ const void *udata =
+ mnl_attr_get_payload(tb[NFTA_SET_ELEM_USERDATA]);
+
+ if (e->flags & (1 << NFTNL_RULE_USERDATA))
+ xfree(e->user.data);
+
+ e->user.len = mnl_attr_get_payload_len(tb[NFTA_SET_ELEM_USERDATA]);
+ e->user.data = malloc(e->user.len);
+ if (e->user.data == NULL) {
+ ret = -1;
+ goto out_set_elem;
+ }
+ memcpy(e->user.data, udata, e->user.len);
+ e->flags |= (1 << NFTNL_RULE_USERDATA);
+ }
+ if (tb[NFTA_SET_ELEM_OBJREF]) {
+ e->objref = strdup(mnl_attr_get_str(tb[NFTA_SET_ELEM_OBJREF]));
+ if (e->objref == NULL) {
+ ret = -1;
+ goto out_set_elem;
+ }
+ e->flags |= (1 << NFTNL_SET_ELEM_OBJREF);
+ }
+
+ /* Add this new element to this set */
+ list_add_tail(&e->head, &s->element_list);
+
+ return 0;
+out_set_elem:
+ nftnl_set_elem_free(e);
+ return ret;
+}
+
+static int
+nftnl_set_elem_list_parse_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_SET_ELEM_LIST_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_SET_ELEM_LIST_TABLE:
+ case NFTA_SET_ELEM_LIST_SET:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ case NFTA_SET_ELEM_LIST_ELEMENTS:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int nftnl_set_elems_parse(struct nftnl_set *s, const struct nlattr *nest)
+{
+ struct nlattr *attr;
+ int ret = 0;
+
+ mnl_attr_for_each_nested(attr, nest) {
+ if (mnl_attr_get_type(attr) != NFTA_LIST_ELEM)
+ return -1;
+
+ ret = nftnl_set_elems_parse2(s, attr);
+ if (ret < 0)
+ return ret;
+ }
+ return ret;
+}
+
+EXPORT_SYMBOL(nftnl_set_elems_nlmsg_parse);
+int nftnl_set_elems_nlmsg_parse(const struct nlmsghdr *nlh, struct nftnl_set *s)
+{
+ struct nlattr *tb[NFTA_SET_ELEM_LIST_MAX+1] = {};
+ struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
+ int ret;
+
+ if (mnl_attr_parse(nlh, sizeof(*nfg),
+ nftnl_set_elem_list_parse_attr_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_SET_ELEM_LIST_TABLE]) {
+ if (s->flags & (1 << NFTNL_SET_TABLE))
+ xfree(s->table);
+ s->table =
+ strdup(mnl_attr_get_str(tb[NFTA_SET_ELEM_LIST_TABLE]));
+ if (!s->table)
+ return -1;
+ s->flags |= (1 << NFTNL_SET_TABLE);
+ }
+ if (tb[NFTA_SET_ELEM_LIST_SET]) {
+ if (s->flags & (1 << NFTNL_SET_NAME))
+ xfree(s->name);
+ s->name =
+ strdup(mnl_attr_get_str(tb[NFTA_SET_ELEM_LIST_SET]));
+ if (!s->name)
+ return -1;
+ s->flags |= (1 << NFTNL_SET_NAME);
+ }
+ if (tb[NFTA_SET_ELEM_LIST_SET_ID]) {
+ s->id = ntohl(mnl_attr_get_u32(tb[NFTA_SET_ELEM_LIST_SET_ID]));
+ s->flags |= (1 << NFTNL_SET_ID);
+ }
+ if (tb[NFTA_SET_ELEM_LIST_ELEMENTS]) {
+ ret = nftnl_set_elems_parse(s, tb[NFTA_SET_ELEM_LIST_ELEMENTS]);
+ if (ret < 0)
+ return ret;
+ }
+
+ s->family = nfg->nfgen_family;
+ s->flags |= (1 << NFTNL_SET_FAMILY);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(nftnl_set_elem_parse);
+int nftnl_set_elem_parse(struct nftnl_set_elem *e, enum nftnl_parse_type type,
+ const char *data, struct nftnl_parse_err *err)
+{
+ errno = EOPNOTSUPP;
+ return -1;
+}
+
+EXPORT_SYMBOL(nftnl_set_elem_parse_file);
+int nftnl_set_elem_parse_file(struct nftnl_set_elem *e, enum nftnl_parse_type type,
+ FILE *fp, struct nftnl_parse_err *err)
+{
+ errno = EOPNOTSUPP;
+ return -1;
+}
+
+int nftnl_set_elem_snprintf_default(char *buf, size_t remain,
+ const struct nftnl_set_elem *e)
+{
+ int ret, dregtype = DATA_VALUE, offset = 0, i;
+
+ ret = snprintf(buf, remain, "element ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_data_reg_snprintf(buf + offset, remain, &e->key,
+ DATA_F_NOPFX, DATA_VALUE);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (e->flags & (1 << NFTNL_SET_ELEM_KEY_END)) {
+ ret = snprintf(buf + offset, remain, " - ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = nftnl_data_reg_snprintf(buf + offset, remain, &e->key_end,
+ DATA_F_NOPFX, DATA_VALUE);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ ret = snprintf(buf + offset, remain, " : ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (e->flags & (1 << NFTNL_SET_ELEM_VERDICT))
+ dregtype = DATA_VERDICT;
+
+ ret = nftnl_data_reg_snprintf(buf + offset, remain, &e->data,
+ DATA_F_NOPFX, dregtype);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ ret = snprintf(buf + offset, remain, "%u [end]", e->set_elem_flags);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ if (e->user.len) {
+ ret = snprintf(buf + offset, remain, " userdata = { ");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ for (i = 0; i < e->user.len; i++) {
+ char *c = e->user.data;
+
+ ret = snprintf(buf + offset, remain,
+ isprint(c[i]) ? "%c" : "\\x%02hhx",
+ c[i]);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ ret = snprintf(buf + offset, remain, " }");
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ }
+
+ return offset;
+}
+
+static int nftnl_set_elem_cmd_snprintf(char *buf, size_t remain,
+ const struct nftnl_set_elem *e,
+ uint32_t cmd, uint32_t type,
+ uint32_t flags)
+{
+ int ret, offset = 0;
+
+ if (type != NFTNL_OUTPUT_DEFAULT)
+ return -1;
+
+ ret = nftnl_set_elem_snprintf_default(buf + offset, remain, e);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+ return offset;
+}
+
+EXPORT_SYMBOL(nftnl_set_elem_snprintf);
+int nftnl_set_elem_snprintf(char *buf, size_t size,
+ const struct nftnl_set_elem *e,
+ uint32_t type, uint32_t flags)
+{
+ if (size)
+ buf[0] = '\0';
+
+ return nftnl_set_elem_cmd_snprintf(buf, size, e, nftnl_flag2cmd(flags),
+ type, flags);
+}
+
+static int nftnl_set_elem_do_snprintf(char *buf, size_t size, const void *e,
+ uint32_t cmd, uint32_t type,
+ uint32_t flags)
+{
+ return nftnl_set_elem_snprintf(buf, size, e, type, flags);
+}
+
+EXPORT_SYMBOL(nftnl_set_elem_fprintf);
+int nftnl_set_elem_fprintf(FILE *fp, const struct nftnl_set_elem *se, uint32_t type,
+ uint32_t flags)
+{
+ return nftnl_fprintf(fp, se, NFTNL_CMD_UNSPEC, type, flags,
+ nftnl_set_elem_do_snprintf);
+}
+
+EXPORT_SYMBOL(nftnl_set_elem_foreach);
+int nftnl_set_elem_foreach(struct nftnl_set *s,
+ int (*cb)(struct nftnl_set_elem *e, void *data),
+ void *data)
+{
+ struct nftnl_set_elem *elem;
+ int ret;
+
+ list_for_each_entry(elem, &s->element_list, head) {
+ ret = cb(elem, data);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+struct nftnl_set_elems_iter {
+ const struct nftnl_set *set;
+ const struct list_head *list;
+ struct nftnl_set_elem *cur;
+};
+
+EXPORT_SYMBOL(nftnl_set_elems_iter_create);
+struct nftnl_set_elems_iter *
+nftnl_set_elems_iter_create(const struct nftnl_set *s)
+{
+ struct nftnl_set_elems_iter *iter;
+
+ iter = calloc(1, sizeof(struct nftnl_set_elems_iter));
+ if (iter == NULL)
+ return NULL;
+
+ iter->set = s;
+ iter->list = &s->element_list;
+ if (list_empty(&s->element_list))
+ iter->cur = NULL;
+ else
+ iter->cur = list_entry(s->element_list.next,
+ struct nftnl_set_elem, head);
+
+ return iter;
+}
+
+EXPORT_SYMBOL(nftnl_set_elems_iter_cur);
+struct nftnl_set_elem *
+nftnl_set_elems_iter_cur(const struct nftnl_set_elems_iter *iter)
+{
+ return iter->cur;
+}
+
+EXPORT_SYMBOL(nftnl_set_elems_iter_next);
+struct nftnl_set_elem *nftnl_set_elems_iter_next(struct nftnl_set_elems_iter *iter)
+{
+ struct nftnl_set_elem *s = iter->cur;
+
+ if (s == NULL)
+ return NULL;
+
+ iter->cur = list_entry(iter->cur->head.next, struct nftnl_set_elem, head);
+ if (&iter->cur->head == iter->list->next)
+ return NULL;
+
+ return s;
+}
+
+EXPORT_SYMBOL(nftnl_set_elems_iter_destroy);
+void nftnl_set_elems_iter_destroy(struct nftnl_set_elems_iter *iter)
+{
+ xfree(iter);
+}
+
+static bool nftnl_attr_nest_overflow(struct nlmsghdr *nlh,
+ const struct nlattr *from,
+ const struct nlattr *to)
+{
+ int len = (void *)to + to->nla_len - (void *)from;
+
+ /* The attribute length field is 16 bits long, thus the maximum payload
+ * that an attribute can convey is UINT16_MAX. In case of overflow,
+ * discard the last that did not fit into the attribute.
+ */
+ if (len > UINT16_MAX) {
+ nlh->nlmsg_len -= to->nla_len;
+ return true;
+ }
+ return false;
+}
+
+EXPORT_SYMBOL(nftnl_set_elems_nlmsg_build_payload_iter);
+int nftnl_set_elems_nlmsg_build_payload_iter(struct nlmsghdr *nlh,
+ struct nftnl_set_elems_iter *iter)
+{
+ struct nftnl_set_elem *elem;
+ struct nlattr *nest1, *nest2;
+ int i = 0, ret = 0;
+
+ nftnl_set_elem_nlmsg_build_def(nlh, iter->set);
+
+ /* This set is empty, don't add an empty list element nest. */
+ if (list_empty(&iter->set->element_list))
+ return ret;
+
+ nest1 = mnl_attr_nest_start(nlh, NFTA_SET_ELEM_LIST_ELEMENTS);
+ elem = nftnl_set_elems_iter_next(iter);
+ while (elem != NULL) {
+ nest2 = nftnl_set_elem_nlmsg_build(nlh, elem, ++i);
+ if (nftnl_attr_nest_overflow(nlh, nest1, nest2)) {
+ /* Go back to previous not to miss this element */
+ iter->cur = list_entry(iter->cur->head.prev,
+ struct nftnl_set_elem, head);
+ ret = 1;
+ break;
+ }
+ elem = nftnl_set_elems_iter_next(iter);
+ }
+ mnl_attr_nest_end(nlh, nest1);
+
+ return ret;
+}
diff --git a/src/table.c b/src/table.c
new file mode 100644
index 0000000..59e7053
--- /dev/null
+++ b/src/table.c
@@ -0,0 +1,523 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+#include "internal.h"
+
+#include <time.h>
+#include <endian.h>
+#include <stdint.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <errno.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftnl/table.h>
+
+struct nftnl_table {
+ struct list_head head;
+
+ const char *name;
+ uint32_t family;
+ uint32_t table_flags;
+ uint64_t handle;
+ uint32_t use;
+ uint32_t flags;
+ uint32_t owner;
+ struct {
+ void *data;
+ uint32_t len;
+ } user;
+};
+
+EXPORT_SYMBOL(nftnl_table_alloc);
+struct nftnl_table *nftnl_table_alloc(void)
+{
+ return calloc(1, sizeof(struct nftnl_table));
+}
+
+EXPORT_SYMBOL(nftnl_table_free);
+void nftnl_table_free(const struct nftnl_table *t)
+{
+ if (t->flags & (1 << NFTNL_TABLE_NAME))
+ xfree(t->name);
+ if (t->flags & (1 << NFTNL_TABLE_USERDATA))
+ xfree(t->user.data);
+
+ xfree(t);
+}
+
+EXPORT_SYMBOL(nftnl_table_is_set);
+bool nftnl_table_is_set(const struct nftnl_table *t, uint16_t attr)
+{
+ return t->flags & (1 << attr);
+}
+
+EXPORT_SYMBOL(nftnl_table_unset);
+void nftnl_table_unset(struct nftnl_table *t, uint16_t attr)
+{
+ if (!(t->flags & (1 << attr)))
+ return;
+
+ switch (attr) {
+ case NFTNL_TABLE_NAME:
+ xfree(t->name);
+ break;
+ case NFTNL_TABLE_FLAGS:
+ case NFTNL_TABLE_HANDLE:
+ case NFTNL_TABLE_FAMILY:
+ case NFTNL_TABLE_USE:
+ case NFTNL_TABLE_OWNER:
+ break;
+ }
+ t->flags &= ~(1 << attr);
+}
+
+static uint32_t nftnl_table_validate[NFTNL_TABLE_MAX + 1] = {
+ [NFTNL_TABLE_FLAGS] = sizeof(uint32_t),
+ [NFTNL_TABLE_FAMILY] = sizeof(uint32_t),
+ [NFTNL_TABLE_HANDLE] = sizeof(uint64_t),
+};
+
+EXPORT_SYMBOL(nftnl_table_set_data);
+int nftnl_table_set_data(struct nftnl_table *t, uint16_t attr,
+ const void *data, uint32_t data_len)
+{
+ nftnl_assert_attr_exists(attr, NFTNL_TABLE_MAX);
+ nftnl_assert_validate(data, nftnl_table_validate, attr, data_len);
+
+ switch (attr) {
+ case NFTNL_TABLE_NAME:
+ if (t->flags & (1 << NFTNL_TABLE_NAME))
+ xfree(t->name);
+
+ t->name = strdup(data);
+ if (!t->name)
+ return -1;
+ break;
+ case NFTNL_TABLE_HANDLE:
+ memcpy(&t->handle, data, sizeof(t->handle));
+ break;
+ case NFTNL_TABLE_FLAGS:
+ memcpy(&t->table_flags, data, sizeof(t->table_flags));
+ break;
+ case NFTNL_TABLE_FAMILY:
+ memcpy(&t->family, data, sizeof(t->family));
+ break;
+ case NFTNL_TABLE_USE:
+ memcpy(&t->use, data, sizeof(t->use));
+ break;
+ case NFTNL_TABLE_USERDATA:
+ if (t->flags & (1 << NFTNL_TABLE_USERDATA))
+ xfree(t->user.data);
+
+ t->user.data = malloc(data_len);
+ if (!t->user.data)
+ return -1;
+ memcpy(t->user.data, data, data_len);
+ t->user.len = data_len;
+ break;
+ case NFTNL_TABLE_OWNER:
+ memcpy(&t->owner, data, sizeof(t->owner));
+ break;
+ }
+ t->flags |= (1 << attr);
+ return 0;
+}
+
+void nftnl_table_set(struct nftnl_table *t, uint16_t attr, const void *data) __visible;
+void nftnl_table_set(struct nftnl_table *t, uint16_t attr, const void *data)
+{
+ nftnl_table_set_data(t, attr, data, nftnl_table_validate[attr]);
+}
+
+EXPORT_SYMBOL(nftnl_table_set_u32);
+void nftnl_table_set_u32(struct nftnl_table *t, uint16_t attr, uint32_t val)
+{
+ nftnl_table_set_data(t, attr, &val, sizeof(uint32_t));
+}
+
+EXPORT_SYMBOL(nftnl_table_set_u64);
+void nftnl_table_set_u64(struct nftnl_table *t, uint16_t attr, uint64_t val)
+{
+ nftnl_table_set_data(t, attr, &val, sizeof(uint64_t));
+}
+
+EXPORT_SYMBOL(nftnl_table_set_u8);
+void nftnl_table_set_u8(struct nftnl_table *t, uint16_t attr, uint8_t val)
+{
+ nftnl_table_set_data(t, attr, &val, sizeof(uint8_t));
+}
+
+EXPORT_SYMBOL(nftnl_table_set_str);
+int nftnl_table_set_str(struct nftnl_table *t, uint16_t attr, const char *str)
+{
+ return nftnl_table_set_data(t, attr, str, strlen(str) + 1);
+}
+
+EXPORT_SYMBOL(nftnl_table_get_data);
+const void *nftnl_table_get_data(const struct nftnl_table *t, uint16_t attr,
+ uint32_t *data_len)
+{
+ if (!(t->flags & (1 << attr)))
+ return NULL;
+
+ switch(attr) {
+ case NFTNL_TABLE_NAME:
+ *data_len = strlen(t->name) + 1;
+ return t->name;
+ case NFTNL_TABLE_HANDLE:
+ *data_len = sizeof(uint64_t);
+ return &t->handle;
+ case NFTNL_TABLE_FLAGS:
+ *data_len = sizeof(uint32_t);
+ return &t->table_flags;
+ case NFTNL_TABLE_FAMILY:
+ *data_len = sizeof(uint32_t);
+ return &t->family;
+ case NFTNL_TABLE_USE:
+ *data_len = sizeof(uint32_t);
+ return &t->use;
+ case NFTNL_TABLE_USERDATA:
+ *data_len = t->user.len;
+ return t->user.data;
+ case NFTNL_TABLE_OWNER:
+ *data_len = sizeof(uint32_t);
+ return &t->owner;
+ }
+ return NULL;
+}
+
+EXPORT_SYMBOL(nftnl_table_get);
+const void *nftnl_table_get(const struct nftnl_table *t, uint16_t attr)
+{
+ uint32_t data_len;
+ return nftnl_table_get_data(t, attr, &data_len);
+}
+
+EXPORT_SYMBOL(nftnl_table_get_u32);
+uint32_t nftnl_table_get_u32(const struct nftnl_table *t, uint16_t attr)
+{
+ const void *ret = nftnl_table_get(t, attr);
+ return ret == NULL ? 0 : *((uint32_t *)ret);
+}
+
+EXPORT_SYMBOL(nftnl_table_get_u64);
+uint64_t nftnl_table_get_u64(const struct nftnl_table *t, uint16_t attr)
+{
+ const void *ret = nftnl_table_get(t, attr);
+ return ret == NULL ? 0 : *((uint64_t *)ret);
+}
+
+EXPORT_SYMBOL(nftnl_table_get_u8);
+uint8_t nftnl_table_get_u8(const struct nftnl_table *t, uint16_t attr)
+{
+ const void *ret = nftnl_table_get(t, attr);
+ return ret == NULL ? 0 : *((uint8_t *)ret);
+}
+
+EXPORT_SYMBOL(nftnl_table_get_str);
+const char *nftnl_table_get_str(const struct nftnl_table *t, uint16_t attr)
+{
+ return nftnl_table_get(t, attr);
+}
+
+EXPORT_SYMBOL(nftnl_table_nlmsg_build_payload);
+void nftnl_table_nlmsg_build_payload(struct nlmsghdr *nlh, const struct nftnl_table *t)
+{
+ if (t->flags & (1 << NFTNL_TABLE_NAME))
+ mnl_attr_put_strz(nlh, NFTA_TABLE_NAME, t->name);
+ if (t->flags & (1 << NFTNL_TABLE_HANDLE))
+ mnl_attr_put_u64(nlh, NFTA_TABLE_HANDLE, htobe64(t->handle));
+ if (t->flags & (1 << NFTNL_TABLE_FLAGS))
+ mnl_attr_put_u32(nlh, NFTA_TABLE_FLAGS, htonl(t->table_flags));
+ if (t->flags & (1 << NFTNL_TABLE_USERDATA))
+ mnl_attr_put(nlh, NFTA_TABLE_USERDATA, t->user.len, t->user.data);
+}
+
+static int nftnl_table_parse_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_TABLE_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFTA_TABLE_NAME:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ case NFTA_TABLE_HANDLE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ case NFTA_TABLE_FLAGS:
+ case NFTA_TABLE_USE:
+ case NFTA_TABLE_OWNER:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_TABLE_USERDATA:
+ if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
+ abi_breakage();
+ break;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+EXPORT_SYMBOL(nftnl_table_nlmsg_parse);
+int nftnl_table_nlmsg_parse(const struct nlmsghdr *nlh, struct nftnl_table *t)
+{
+ struct nlattr *tb[NFTA_TABLE_MAX+1] = {};
+ struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
+ int ret;
+
+ if (mnl_attr_parse(nlh, sizeof(*nfg), nftnl_table_parse_attr_cb, tb) < 0)
+ return -1;
+
+ if (tb[NFTA_TABLE_NAME]) {
+ if (t->flags & (1 << NFTNL_TABLE_NAME))
+ xfree(t->name);
+ t->name = strdup(mnl_attr_get_str(tb[NFTA_TABLE_NAME]));
+ if (!t->name)
+ return -1;
+ t->flags |= (1 << NFTNL_TABLE_NAME);
+ }
+ if (tb[NFTA_TABLE_FLAGS]) {
+ t->table_flags = ntohl(mnl_attr_get_u32(tb[NFTA_TABLE_FLAGS]));
+ t->flags |= (1 << NFTNL_TABLE_FLAGS);
+ }
+ if (tb[NFTA_TABLE_USE]) {
+ t->use = ntohl(mnl_attr_get_u32(tb[NFTA_TABLE_USE]));
+ t->flags |= (1 << NFTNL_TABLE_USE);
+ }
+ if (tb[NFTA_TABLE_HANDLE]) {
+ t->handle = be64toh(mnl_attr_get_u64(tb[NFTA_TABLE_HANDLE]));
+ t->flags |= (1 << NFTNL_TABLE_HANDLE);
+ }
+ if (tb[NFTA_TABLE_USERDATA]) {
+ ret = nftnl_table_set_data(t, NFTNL_TABLE_USERDATA,
+ mnl_attr_get_payload(tb[NFTA_TABLE_USERDATA]),
+ mnl_attr_get_payload_len(tb[NFTA_TABLE_USERDATA]));
+ if (ret < 0)
+ return ret;
+ }
+ if (tb[NFTA_TABLE_OWNER]) {
+ t->owner = ntohl(mnl_attr_get_u32(tb[NFTA_TABLE_OWNER]));
+ t->flags |= (1 << NFTNL_TABLE_OWNER);
+ }
+
+ t->family = nfg->nfgen_family;
+ t->flags |= (1 << NFTNL_TABLE_FAMILY);
+
+ return 0;
+}
+
+static int nftnl_table_do_parse(struct nftnl_table *t, enum nftnl_parse_type type,
+ const void *data, struct nftnl_parse_err *err,
+ enum nftnl_parse_input input)
+{
+ int ret;
+
+ switch (type) {
+ case NFTNL_PARSE_JSON:
+ case NFTNL_PARSE_XML:
+ default:
+ ret = -1;
+ errno = EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+EXPORT_SYMBOL(nftnl_table_parse);
+int nftnl_table_parse(struct nftnl_table *t, enum nftnl_parse_type type,
+ const char *data, struct nftnl_parse_err *err)
+{
+ return nftnl_table_do_parse(t, type, data, err, NFTNL_PARSE_BUFFER);
+}
+
+EXPORT_SYMBOL(nftnl_table_parse_file);
+int nftnl_table_parse_file(struct nftnl_table *t, enum nftnl_parse_type type,
+ FILE *fp, struct nftnl_parse_err *err)
+{
+ return nftnl_table_do_parse(t, type, fp, err, NFTNL_PARSE_FILE);
+}
+
+static int nftnl_table_snprintf_default(char *buf, size_t size,
+ const struct nftnl_table *t)
+{
+ return snprintf(buf, size, "table %s %s flags %x use %d handle %llu",
+ t->name, nftnl_family2str(t->family),
+ t->table_flags, t->use, (unsigned long long)t->handle);
+}
+
+static int nftnl_table_cmd_snprintf(char *buf, size_t remain,
+ const struct nftnl_table *t, uint32_t cmd,
+ uint32_t type, uint32_t flags)
+{
+ int ret, offset = 0;
+
+ if (type != NFTNL_OUTPUT_DEFAULT)
+ return -1;
+
+ ret = nftnl_table_snprintf_default(buf + offset, remain, t);
+ SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+ return offset;
+}
+
+EXPORT_SYMBOL(nftnl_table_snprintf);
+int nftnl_table_snprintf(char *buf, size_t size, const struct nftnl_table *t,
+ uint32_t type, uint32_t flags)
+{
+ if (size)
+ buf[0] = '\0';
+
+ return nftnl_table_cmd_snprintf(buf, size, t, nftnl_flag2cmd(flags), type,
+ flags);
+}
+
+static int nftnl_table_do_snprintf(char *buf, size_t size, const void *t,
+ uint32_t cmd, uint32_t type, uint32_t flags)
+{
+ return nftnl_table_snprintf(buf, size, t, type, flags);
+}
+
+EXPORT_SYMBOL(nftnl_table_fprintf);
+int nftnl_table_fprintf(FILE *fp, const struct nftnl_table *t, uint32_t type,
+ uint32_t flags)
+{
+ return nftnl_fprintf(fp, t, NFTNL_CMD_UNSPEC, type, flags,
+ nftnl_table_do_snprintf);
+}
+
+struct nftnl_table_list {
+ struct list_head list;
+};
+
+EXPORT_SYMBOL(nftnl_table_list_alloc);
+struct nftnl_table_list *nftnl_table_list_alloc(void)
+{
+ struct nftnl_table_list *list;
+
+ list = calloc(1, sizeof(struct nftnl_table_list));
+ if (list == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&list->list);
+
+ return list;
+}
+
+EXPORT_SYMBOL(nftnl_table_list_free);
+void nftnl_table_list_free(struct nftnl_table_list *list)
+{
+ struct nftnl_table *r, *tmp;
+
+ list_for_each_entry_safe(r, tmp, &list->list, head) {
+ list_del(&r->head);
+ nftnl_table_free(r);
+ }
+ xfree(list);
+}
+
+EXPORT_SYMBOL(nftnl_table_list_is_empty);
+int nftnl_table_list_is_empty(const struct nftnl_table_list *list)
+{
+ return list_empty(&list->list);
+}
+
+EXPORT_SYMBOL(nftnl_table_list_add);
+void nftnl_table_list_add(struct nftnl_table *r, struct nftnl_table_list *list)
+{
+ list_add(&r->head, &list->list);
+}
+
+EXPORT_SYMBOL(nftnl_table_list_add_tail);
+void nftnl_table_list_add_tail(struct nftnl_table *r, struct nftnl_table_list *list)
+{
+ list_add_tail(&r->head, &list->list);
+}
+
+EXPORT_SYMBOL(nftnl_table_list_del);
+void nftnl_table_list_del(struct nftnl_table *t)
+{
+ list_del(&t->head);
+}
+
+EXPORT_SYMBOL(nftnl_table_list_foreach);
+int nftnl_table_list_foreach(struct nftnl_table_list *table_list,
+ int (*cb)(struct nftnl_table *t, void *data),
+ void *data)
+{
+ struct nftnl_table *cur, *tmp;
+ int ret;
+
+ list_for_each_entry_safe(cur, tmp, &table_list->list, head) {
+ ret = cb(cur, data);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+struct nftnl_table_list_iter {
+ const struct nftnl_table_list *list;
+ struct nftnl_table *cur;
+};
+
+EXPORT_SYMBOL(nftnl_table_list_iter_create);
+struct nftnl_table_list_iter *
+nftnl_table_list_iter_create(const struct nftnl_table_list *l)
+{
+ struct nftnl_table_list_iter *iter;
+
+ iter = calloc(1, sizeof(struct nftnl_table_list_iter));
+ if (iter == NULL)
+ return NULL;
+
+ iter->list = l;
+ if (nftnl_table_list_is_empty(l))
+ iter->cur = NULL;
+ else
+ iter->cur = list_entry(l->list.next, struct nftnl_table, head);
+
+ return iter;
+}
+
+EXPORT_SYMBOL(nftnl_table_list_iter_next);
+struct nftnl_table *nftnl_table_list_iter_next(struct nftnl_table_list_iter *iter)
+{
+ struct nftnl_table *r = iter->cur;
+
+ if (r == NULL)
+ return NULL;
+
+ /* get next table, if any */
+ iter->cur = list_entry(iter->cur->head.next, struct nftnl_table, head);
+ if (&iter->cur->head == iter->list->list.next)
+ return NULL;
+
+ return r;
+}
+
+EXPORT_SYMBOL(nftnl_table_list_iter_destroy);
+void nftnl_table_list_iter_destroy(const struct nftnl_table_list_iter *iter)
+{
+ xfree(iter);
+}
diff --git a/src/trace.c b/src/trace.c
new file mode 100644
index 0000000..f426437
--- /dev/null
+++ b/src/trace.c
@@ -0,0 +1,427 @@
+/*
+ * (C) 2015 Red Hat GmbH
+ * Author: Florian Westphal <fw@strlen.de>
+ *
+ * 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 "internal.h"
+
+#include <time.h>
+#include <endian.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+
+#include <libmnl/libmnl.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftnl/trace.h>
+
+struct nftnl_header_data {
+ char *data;
+ unsigned int len;
+};
+
+struct nftnl_trace {
+ char *table;
+ char *chain;
+ char *jump_target;
+ uint64_t rule_handle;
+ struct nftnl_header_data ll;
+ struct nftnl_header_data nh;
+ struct nftnl_header_data th;
+ uint32_t family;
+ uint32_t type;
+ uint32_t id;
+ uint32_t iif;
+ uint32_t oif;
+ uint32_t mark;
+ uint32_t verdict;
+ uint32_t nfproto;
+ uint32_t policy;
+ uint16_t iiftype;
+ uint16_t oiftype;
+
+ uint32_t flags;
+};
+
+EXPORT_SYMBOL(nftnl_trace_alloc);
+struct nftnl_trace *nftnl_trace_alloc(void)
+{
+ return calloc(1, sizeof(struct nftnl_trace));
+}
+
+EXPORT_SYMBOL(nftnl_trace_free);
+void nftnl_trace_free(const struct nftnl_trace *t)
+{
+ xfree(t->chain);
+ xfree(t->table);
+ xfree(t->jump_target);
+ xfree(t->ll.data);
+ xfree(t->nh.data);
+ xfree(t->th.data);
+ xfree(t);
+}
+
+EXPORT_SYMBOL(nftnl_trace_is_set);
+bool nftnl_trace_is_set(const struct nftnl_trace *t, uint16_t attr)
+{
+ return t->flags & (1 << attr);
+}
+
+static int nftnl_trace_parse_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ enum nft_trace_attributes type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, NFTA_TRACE_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch (type) {
+ case NFTA_TRACE_UNSPEC:
+ case __NFTA_TRACE_MAX:
+ break;
+ case NFTA_TRACE_VERDICT:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+ abi_breakage();
+ break;
+ case NFTA_TRACE_IIFTYPE:
+ case NFTA_TRACE_OIFTYPE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
+ abi_breakage();
+ break;
+ case NFTA_TRACE_ID:
+ case NFTA_TRACE_IIF:
+ case NFTA_TRACE_MARK:
+ case NFTA_TRACE_OIF:
+ case NFTA_TRACE_POLICY:
+ case NFTA_TRACE_NFPROTO:
+ case NFTA_TRACE_TYPE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ break;
+ case NFTA_TRACE_CHAIN:
+ case NFTA_TRACE_TABLE:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ break;
+ case NFTA_TRACE_RULE_HANDLE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
+ abi_breakage();
+ break;
+ case NFTA_TRACE_LL_HEADER: /* fallthrough */
+ case NFTA_TRACE_NETWORK_HEADER:
+ case NFTA_TRACE_TRANSPORT_HEADER:
+ if (mnl_attr_get_payload_len(attr) == 0)
+ abi_breakage();
+ break;
+ default:
+ return MNL_CB_OK;
+ };
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+EXPORT_SYMBOL(nftnl_trace_get_data);
+const void *nftnl_trace_get_data(const struct nftnl_trace *trace,
+ uint16_t type, uint32_t *data_len)
+{
+ enum nftnl_trace_attr attr = type;
+
+ if (!(trace->flags & (1 << type)))
+ return NULL;
+
+ switch (attr) {
+ case NFTNL_TRACE_FAMILY:
+ *data_len = sizeof(uint32_t);
+ return &trace->family;
+ case NFTNL_TRACE_ID:
+ *data_len = sizeof(uint32_t);
+ return &trace->id;
+ case NFTNL_TRACE_IIF:
+ *data_len = sizeof(uint32_t);
+ return &trace->iif;
+ case NFTNL_TRACE_OIF:
+ *data_len = sizeof(uint32_t);
+ return &trace->oif;
+ case NFTNL_TRACE_LL_HEADER:
+ *data_len = trace->ll.len;
+ return trace->ll.data;
+ case NFTNL_TRACE_MARK:
+ *data_len = sizeof(uint32_t);
+ return &trace->mark;
+ case NFTNL_TRACE_NETWORK_HEADER:
+ *data_len = trace->nh.len;
+ return trace->nh.data;
+ case NFTNL_TRACE_TYPE:
+ *data_len = sizeof(uint32_t);
+ return &trace->type;
+ case NFTNL_TRACE_CHAIN:
+ *data_len = strlen(trace->chain) + 1;
+ return trace->chain;
+ case NFTNL_TRACE_TABLE:
+ *data_len = strlen(trace->table) + 1;
+ return trace->table;
+ case NFTNL_TRACE_JUMP_TARGET:
+ *data_len = strlen(trace->jump_target) + 1;
+ return trace->jump_target;
+ case NFTNL_TRACE_TRANSPORT_HEADER:
+ *data_len = trace->th.len;
+ return trace->th.data;
+ case NFTNL_TRACE_RULE_HANDLE:
+ *data_len = sizeof(uint64_t);
+ return &trace->rule_handle;
+ case NFTNL_TRACE_VERDICT:
+ *data_len = sizeof(uint32_t);
+ return &trace->verdict;
+ case NFTNL_TRACE_IIFTYPE:
+ *data_len = sizeof(uint16_t);
+ return &trace->iiftype;
+ case NFTNL_TRACE_OIFTYPE:
+ *data_len = sizeof(uint16_t);
+ return &trace->oiftype;
+ case NFTNL_TRACE_NFPROTO:
+ *data_len = sizeof(uint32_t);
+ return &trace->nfproto;
+ case NFTNL_TRACE_POLICY:
+ *data_len = sizeof(uint32_t);
+ return &trace->policy;
+ case __NFTNL_TRACE_MAX:
+ break;
+ }
+
+ return NULL;
+}
+
+EXPORT_SYMBOL(nftnl_trace_get_str);
+const char *nftnl_trace_get_str(const struct nftnl_trace *trace, uint16_t type)
+{
+ if (!nftnl_trace_is_set(trace, type))
+ return NULL;
+
+ switch (type) {
+ case NFTNL_TRACE_CHAIN: return trace->chain;
+ case NFTNL_TRACE_TABLE: return trace->table;
+ case NFTNL_TRACE_JUMP_TARGET: return trace->jump_target;
+ default: break;
+ }
+ return NULL;
+}
+
+EXPORT_SYMBOL(nftnl_trace_get_u16);
+uint16_t nftnl_trace_get_u16(const struct nftnl_trace *trace, uint16_t type)
+{
+ const uint16_t *d;
+ uint32_t dlen;
+
+ d = nftnl_trace_get_data(trace, type, &dlen);
+ if (d && dlen == sizeof(*d))
+ return *d;
+
+ return 0;
+}
+
+EXPORT_SYMBOL(nftnl_trace_get_u32);
+uint32_t nftnl_trace_get_u32(const struct nftnl_trace *trace, uint16_t type)
+{
+ const uint32_t *d;
+ uint32_t dlen;
+
+ d = nftnl_trace_get_data(trace, type, &dlen);
+ if (d && dlen == sizeof(*d))
+ return *d;
+
+ return 0;
+}
+
+EXPORT_SYMBOL(nftnl_trace_get_u64);
+uint64_t nftnl_trace_get_u64(const struct nftnl_trace *trace, uint16_t type)
+{
+ const uint64_t *d;
+ uint32_t dlen;
+
+ d = nftnl_trace_get_data(trace, type, &dlen);
+ if (d && dlen == sizeof(*d))
+ return *d;
+
+ return 0;
+}
+
+static bool nftnl_trace_nlmsg_parse_hdrdata(struct nlattr *attr,
+ struct nftnl_header_data *header)
+{
+ uint32_t len;
+
+ if (!attr)
+ return false;
+
+ len = mnl_attr_get_payload_len(attr);
+
+ header->data = malloc(len);
+ if (header->data) {
+ memcpy(header->data, mnl_attr_get_payload(attr), len);
+ header->len = len;
+ return true;
+ }
+
+ return false;
+}
+
+static int nftnl_trace_parse_verdict_cb(const struct nlattr *attr, void *data)
+{
+ int type = mnl_attr_get_type(attr);
+ const struct nlattr **tb = data;
+
+ switch (type) {
+ case NFTA_VERDICT_CODE:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ abi_breakage();
+ tb[type] = attr;
+ break;
+ case NFTA_VERDICT_CHAIN:
+ if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
+ abi_breakage();
+ tb[type] = attr;
+ break;
+ }
+
+ return MNL_CB_OK;
+}
+
+static int nftnl_trace_parse_verdict(const struct nlattr *attr,
+ struct nftnl_trace *t)
+{
+ struct nlattr *tb[NFTA_VERDICT_MAX+1];
+
+ if (mnl_attr_parse_nested(attr, nftnl_trace_parse_verdict_cb, tb) < 0)
+ return -1;
+
+ if (!tb[NFTA_VERDICT_CODE])
+ abi_breakage();
+
+ t->verdict = ntohl(mnl_attr_get_u32(tb[NFTA_VERDICT_CODE]));
+ t->flags |= (1 << NFTNL_TRACE_VERDICT);
+
+ switch (t->verdict) {
+ case NFT_GOTO: /* fallthough */
+ case NFT_JUMP:
+ if (!tb[NFTA_VERDICT_CHAIN])
+ abi_breakage();
+ t->jump_target = strdup(mnl_attr_get_str(tb[NFTA_VERDICT_CHAIN]));
+ if (!t->jump_target)
+ return -1;
+
+ t->flags |= (1 << NFTNL_TRACE_JUMP_TARGET);
+ break;
+ }
+ return 0;
+}
+
+EXPORT_SYMBOL(nftnl_trace_nlmsg_parse);
+int nftnl_trace_nlmsg_parse(const struct nlmsghdr *nlh, struct nftnl_trace *t)
+{
+ struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *tb[NFTA_TRACE_MAX+1] = {};
+
+ if (mnl_attr_parse(nlh, sizeof(*nfg), nftnl_trace_parse_attr_cb, tb) < 0)
+ return -1;
+
+ if (!tb[NFTA_TRACE_ID])
+ abi_breakage();
+
+ if (!tb[NFTA_TRACE_TYPE])
+ abi_breakage();
+
+ t->family = nfg->nfgen_family;
+ t->flags |= (1 << NFTNL_TRACE_FAMILY);
+
+ t->type = ntohl(mnl_attr_get_u32(tb[NFTA_TRACE_TYPE]));
+ t->flags |= (1 << NFTNL_TRACE_TYPE);
+
+ t->id = ntohl(mnl_attr_get_u32(tb[NFTA_TRACE_ID]));
+ t->flags |= (1 << NFTNL_TRACE_ID);
+
+ if (tb[NFTA_TRACE_TABLE]) {
+ t->table = strdup(mnl_attr_get_str(tb[NFTA_TRACE_TABLE]));
+ if (!t->table)
+ return -1;
+
+ t->flags |= (1 << NFTNL_TRACE_TABLE);
+ }
+
+ if (tb[NFTA_TRACE_CHAIN]) {
+ t->chain = strdup(mnl_attr_get_str(tb[NFTA_TRACE_CHAIN]));
+ if (!t->chain)
+ return -1;
+
+ t->flags |= (1 << NFTNL_TRACE_CHAIN);
+ }
+
+ if (tb[NFTA_TRACE_IIFTYPE]) {
+ t->iiftype = ntohs(mnl_attr_get_u16(tb[NFTA_TRACE_IIFTYPE]));
+ t->flags |= (1 << NFTNL_TRACE_IIFTYPE);
+ }
+
+ if (tb[NFTA_TRACE_IIF]) {
+ t->iif = ntohl(mnl_attr_get_u32(tb[NFTA_TRACE_IIF]));
+ t->flags |= (1 << NFTNL_TRACE_IIF);
+ }
+
+ if (tb[NFTA_TRACE_OIFTYPE]) {
+ t->oiftype = ntohs(mnl_attr_get_u16(tb[NFTA_TRACE_OIFTYPE]));
+ t->flags |= (1 << NFTNL_TRACE_OIFTYPE);
+ }
+
+ if (tb[NFTA_TRACE_OIF]) {
+ t->oif = ntohl(mnl_attr_get_u32(tb[NFTA_TRACE_OIF]));
+ t->flags |= (1 << NFTNL_TRACE_OIF);
+ }
+
+ if (tb[NFTA_TRACE_MARK]) {
+ t->mark = ntohl(mnl_attr_get_u32(tb[NFTA_TRACE_MARK]));
+ t->flags |= (1 << NFTNL_TRACE_MARK);
+ }
+
+ if (tb[NFTA_TRACE_RULE_HANDLE]) {
+ t->rule_handle = be64toh(mnl_attr_get_u64(tb[NFTA_TRACE_RULE_HANDLE]));
+ t->flags |= (1 << NFTNL_TRACE_RULE_HANDLE);
+ }
+
+ if (tb[NFTA_TRACE_VERDICT] &&
+ nftnl_trace_parse_verdict(tb[NFTA_TRACE_VERDICT], t) < 0)
+ return -1;
+
+ if (nftnl_trace_nlmsg_parse_hdrdata(tb[NFTA_TRACE_LL_HEADER], &t->ll))
+ t->flags |= (1 << NFTNL_TRACE_LL_HEADER);
+
+ if (nftnl_trace_nlmsg_parse_hdrdata(tb[NFTA_TRACE_NETWORK_HEADER], &t->nh))
+ t->flags |= (1 << NFTNL_TRACE_NETWORK_HEADER);
+
+ if (nftnl_trace_nlmsg_parse_hdrdata(tb[NFTA_TRACE_TRANSPORT_HEADER], &t->th))
+ t->flags |= (1 << NFTNL_TRACE_TRANSPORT_HEADER);
+
+ if (tb[NFTA_TRACE_NFPROTO]) {
+ t->nfproto = ntohl(mnl_attr_get_u32(tb[NFTA_TRACE_NFPROTO]));
+ t->flags |= (1 << NFTNL_TRACE_NFPROTO);
+ }
+
+ if (tb[NFTA_TRACE_POLICY]) {
+ t->policy = ntohl(mnl_attr_get_u32(tb[NFTA_TRACE_POLICY]));
+ t->flags |= (1 << NFTNL_TRACE_POLICY);
+ }
+
+ if (tb[NFTA_TRACE_MARK]) {
+ t->mark = ntohl(mnl_attr_get_u32(tb[NFTA_TRACE_MARK]));
+ t->flags |= (1 << NFTNL_TRACE_MARK);
+ }
+
+ return 0;
+}
diff --git a/src/udata.c b/src/udata.c
new file mode 100644
index 0000000..0cc3520
--- /dev/null
+++ b/src/udata.c
@@ -0,0 +1,169 @@
+/*
+ * (C) 2012-2016 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2016 by Carlos Falgueras GarcĂ­a <carlosfg@riseup.net>
+ *
+ * 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 <libnftnl/udata.h>
+#include <udata.h>
+#include <utils.h>
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+EXPORT_SYMBOL(nftnl_udata_buf_alloc);
+struct nftnl_udata_buf *nftnl_udata_buf_alloc(uint32_t data_size)
+{
+ struct nftnl_udata_buf *buf;
+
+ buf = malloc(sizeof(struct nftnl_udata_buf) + data_size);
+ if (!buf)
+ return NULL;
+ buf->size = data_size;
+ buf->end = buf->data;
+
+ return buf;
+}
+
+EXPORT_SYMBOL(nftnl_udata_buf_free);
+void nftnl_udata_buf_free(const struct nftnl_udata_buf *buf)
+{
+ xfree(buf);
+}
+
+EXPORT_SYMBOL(nftnl_udata_buf_len);
+uint32_t nftnl_udata_buf_len(const struct nftnl_udata_buf *buf)
+{
+ return (uint32_t)(buf->end - buf->data);
+}
+
+EXPORT_SYMBOL(nftnl_udata_buf_data);
+void *nftnl_udata_buf_data(const struct nftnl_udata_buf *buf)
+{
+ return (void *)buf->data;
+}
+
+EXPORT_SYMBOL(nftnl_udata_buf_put);
+void nftnl_udata_buf_put(struct nftnl_udata_buf *buf, const void *data,
+ uint32_t len)
+{
+ memcpy(buf->data, data, len <= buf->size ? len : buf->size);
+ buf->end = buf->data + len;
+}
+
+EXPORT_SYMBOL(nftnl_udata_start);
+struct nftnl_udata *nftnl_udata_start(const struct nftnl_udata_buf *buf)
+{
+ return (struct nftnl_udata *)buf->data;
+}
+
+EXPORT_SYMBOL(nftnl_udata_end);
+struct nftnl_udata *nftnl_udata_end(const struct nftnl_udata_buf *buf)
+{
+ return (struct nftnl_udata *)buf->end;
+}
+
+EXPORT_SYMBOL(nftnl_udata_put);
+bool nftnl_udata_put(struct nftnl_udata_buf *buf, uint8_t type, uint32_t len,
+ const void *value)
+{
+ struct nftnl_udata *attr;
+
+ if (len > UINT8_MAX || buf->size < len + sizeof(struct nftnl_udata))
+ return false;
+
+ attr = (struct nftnl_udata *)buf->end;
+ attr->len = len;
+ attr->type = type;
+ memcpy(attr->value, value, len);
+
+ buf->end = (char *)nftnl_udata_next(attr);
+
+ return true;
+}
+
+EXPORT_SYMBOL(nftnl_udata_put_strz);
+bool nftnl_udata_put_strz(struct nftnl_udata_buf *buf, uint8_t type,
+ const char *strz)
+{
+ return nftnl_udata_put(buf, type, strlen(strz) + 1, strz);
+}
+
+EXPORT_SYMBOL(nftnl_udata_put_u32);
+bool nftnl_udata_put_u32(struct nftnl_udata_buf *buf, uint8_t type,
+ uint32_t data)
+{
+ return nftnl_udata_put(buf, type, sizeof(data), &data);
+}
+
+EXPORT_SYMBOL(nftnl_udata_type);
+uint8_t nftnl_udata_type(const struct nftnl_udata *attr)
+{
+ return attr->type;
+}
+
+EXPORT_SYMBOL(nftnl_udata_len);
+uint8_t nftnl_udata_len(const struct nftnl_udata *attr)
+{
+ return attr->len;
+}
+
+EXPORT_SYMBOL(nftnl_udata_get);
+void *nftnl_udata_get(const struct nftnl_udata *attr)
+{
+ return (void *)attr->value;
+}
+
+EXPORT_SYMBOL(nftnl_udata_get_u32);
+uint32_t nftnl_udata_get_u32(const struct nftnl_udata *attr)
+{
+ uint32_t data;
+
+ memcpy(&data, attr->value, sizeof(data));
+
+ return data;
+}
+
+EXPORT_SYMBOL(nftnl_udata_next);
+struct nftnl_udata *nftnl_udata_next(const struct nftnl_udata *attr)
+{
+ return (struct nftnl_udata *)&attr->value[attr->len];
+}
+
+EXPORT_SYMBOL(nftnl_udata_parse);
+int nftnl_udata_parse(const void *data, uint32_t data_len, nftnl_udata_cb_t cb,
+ void *cb_data)
+{
+ int ret = 0;
+ const struct nftnl_udata *attr;
+
+ nftnl_udata_for_each_data(data, data_len, attr) {
+ ret = cb(attr, cb_data);
+ if (ret < 0)
+ return ret;
+ }
+
+ return ret;
+}
+
+EXPORT_SYMBOL(nftnl_udata_nest_start);
+struct nftnl_udata *nftnl_udata_nest_start(struct nftnl_udata_buf *buf,
+ uint8_t type)
+{
+ struct nftnl_udata *ud = nftnl_udata_end(buf);
+
+ nftnl_udata_put(buf, type, 0, NULL);
+
+ return ud;
+}
+
+EXPORT_SYMBOL(nftnl_udata_nest_end);
+void nftnl_udata_nest_end(struct nftnl_udata_buf *buf, struct nftnl_udata *ud)
+{
+ ud->len = buf->end - (char *)ud->value;
+}
diff --git a/src/utils.c b/src/utils.c
new file mode 100644
index 0000000..3617837
--- /dev/null
+++ b/src/utils.c
@@ -0,0 +1,332 @@
+/*
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2013 by Arturo Borrero Gonzalez <arturo@debian.org>
+ *
+ * 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 <internal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <libnftnl/common.h>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+
+static const char *const nftnl_family_str[NFPROTO_NUMPROTO] = {
+ [NFPROTO_INET] = "inet",
+ [NFPROTO_IPV4] = "ip",
+ [NFPROTO_ARP] = "arp",
+ [NFPROTO_NETDEV] = "netdev",
+ [NFPROTO_BRIDGE] = "bridge",
+ [NFPROTO_IPV6] = "ip6",
+};
+
+const char *nftnl_family2str(uint32_t family)
+{
+ if (family >= NFPROTO_NUMPROTO || !nftnl_family_str[family])
+ return "unknown";
+
+ return nftnl_family_str[family];
+}
+
+int nftnl_str2family(const char *family)
+{
+ int i;
+
+ for (i = 0; i < NFPROTO_NUMPROTO; i++) {
+ if (nftnl_family_str[i] == NULL)
+ continue;
+
+ if (strcmp(nftnl_family_str[i], family) == 0)
+ return i;
+ }
+
+ errno = EAFNOSUPPORT;
+ return -1;
+}
+
+static struct {
+ int len;
+ int64_t min;
+ uint64_t max;
+} basetype[] = {
+ [NFTNL_TYPE_U8] = { .len = sizeof(uint8_t), .max = UINT8_MAX },
+ [NFTNL_TYPE_U16] = { .len = sizeof(uint16_t), .max = UINT16_MAX },
+ [NFTNL_TYPE_U32] = { .len = sizeof(uint32_t), .max = UINT32_MAX },
+ [NFTNL_TYPE_U64] = { .len = sizeof(uint64_t), .max = UINT64_MAX },
+ [NFTNL_TYPE_S8] = { .len = sizeof(int8_t), .min = INT8_MIN, .max = INT8_MAX },
+ [NFTNL_TYPE_S16] = { .len = sizeof(int16_t), .min = INT16_MIN, .max = INT16_MAX },
+ [NFTNL_TYPE_S32] = { .len = sizeof(int32_t), .min = INT32_MIN, .max = INT32_MAX },
+ [NFTNL_TYPE_S64] = { .len = sizeof(int64_t), .min = INT64_MIN, .max = INT64_MAX },
+};
+
+int nftnl_get_value(enum nftnl_type type, void *val, void *out)
+{
+ union {
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ int8_t s8;
+ int16_t s16;
+ int32_t s32;
+ } values;
+ void *valuep = NULL;
+ int64_t sval;
+ uint64_t uval;
+
+ switch (type) {
+ case NFTNL_TYPE_U8:
+ case NFTNL_TYPE_U16:
+ case NFTNL_TYPE_U32:
+ case NFTNL_TYPE_U64:
+ memcpy(&uval, val, sizeof(uval));
+ if (uval > basetype[type].max) {
+ errno = ERANGE;
+ return -1;
+ }
+ break;
+ case NFTNL_TYPE_S8:
+ case NFTNL_TYPE_S16:
+ case NFTNL_TYPE_S32:
+ case NFTNL_TYPE_S64:
+ memcpy(&sval, val, sizeof(sval));
+ if (sval < basetype[type].min ||
+ sval > (int64_t)basetype[type].max) {
+ errno = ERANGE;
+ return -1;
+ }
+ break;
+ }
+
+ switch (type) {
+ case NFTNL_TYPE_U8:
+ values.u8 = uval;
+ valuep = &values.u8;
+ break;
+ case NFTNL_TYPE_U16:
+ values.u16 = uval;
+ valuep = &values.u16;
+ break;
+ case NFTNL_TYPE_U32:
+ values.u32 = uval;
+ valuep = &values.u32;
+ break;
+ case NFTNL_TYPE_U64:
+ valuep = &uval;
+ break;
+ case NFTNL_TYPE_S8:
+ values.s8 = sval;
+ valuep = &values.s8;
+ break;
+ case NFTNL_TYPE_S16:
+ values.s16 = sval;
+ valuep = &values.s16;
+ break;
+ case NFTNL_TYPE_S32:
+ values.s32 = sval;
+ valuep = &values.s32;
+ break;
+ case NFTNL_TYPE_S64:
+ valuep = &sval;
+ break;
+ }
+ memcpy(out, valuep, basetype[type].len);
+ return 0;
+}
+
+int nftnl_strtoi(const char *string, int base, void *out, enum nftnl_type type)
+{
+ int ret;
+ int64_t sval = 0;
+ uint64_t uval = -1;
+ char *endptr;
+
+ switch (type) {
+ case NFTNL_TYPE_U8:
+ case NFTNL_TYPE_U16:
+ case NFTNL_TYPE_U32:
+ case NFTNL_TYPE_U64:
+ uval = strtoll(string, &endptr, base);
+ ret = nftnl_get_value(type, &uval, out);
+ break;
+ case NFTNL_TYPE_S8:
+ case NFTNL_TYPE_S16:
+ case NFTNL_TYPE_S32:
+ case NFTNL_TYPE_S64:
+ sval = strtoull(string, &endptr, base);
+ ret = nftnl_get_value(type, &sval, out);
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (*endptr) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ return ret;
+}
+
+const char *nftnl_verdict2str(uint32_t verdict)
+{
+ switch (verdict) {
+ case NF_ACCEPT:
+ return "accept";
+ case NF_DROP:
+ return "drop";
+ case NF_STOLEN:
+ return "stolen";
+ case NF_QUEUE:
+ return "queue";
+ case NF_REPEAT:
+ return "repeat";
+ case NF_STOP:
+ return "stop";
+ case NFT_RETURN:
+ return "return";
+ case NFT_JUMP:
+ return "jump";
+ case NFT_GOTO:
+ return "goto";
+ case NFT_CONTINUE:
+ return "continue";
+ case NFT_BREAK:
+ return "break";
+ default:
+ return "unknown";
+ }
+}
+
+int nftnl_str2verdict(const char *verdict, int *verdict_num)
+{
+ if (strcmp(verdict, "accept") == 0) {
+ *verdict_num = NF_ACCEPT;
+ return 0;
+ } else if (strcmp(verdict, "drop") == 0) {
+ *verdict_num = NF_DROP;
+ return 0;
+ } else if (strcmp(verdict, "return") == 0) {
+ *verdict_num = NFT_RETURN;
+ return 0;
+ } else if (strcmp(verdict, "jump") == 0) {
+ *verdict_num = NFT_JUMP;
+ return 0;
+ } else if (strcmp(verdict, "goto") == 0) {
+ *verdict_num = NFT_GOTO;
+ return 0;
+ }
+
+ return -1;
+}
+
+enum nftnl_cmd_type nftnl_flag2cmd(uint32_t flags)
+{
+ if (flags & NFTNL_OF_EVENT_NEW)
+ return NFTNL_CMD_ADD;
+ else if (flags & NFTNL_OF_EVENT_DEL)
+ return NFTNL_CMD_DELETE;
+
+ return NFTNL_CMD_UNSPEC;
+}
+
+static const char *cmd2tag[NFTNL_CMD_MAX] = {
+ [NFTNL_CMD_ADD] = "add",
+ [NFTNL_CMD_INSERT] = "insert",
+ [NFTNL_CMD_DELETE] = "delete",
+ [NFTNL_CMD_REPLACE] = "replace",
+ [NFTNL_CMD_FLUSH] = "flush",
+};
+
+const char *nftnl_cmd2tag(enum nftnl_cmd_type cmd)
+{
+ if (cmd >= NFTNL_CMD_MAX)
+ return "unknown";
+
+ return cmd2tag[cmd];
+}
+
+uint32_t nftnl_str2cmd(const char *cmd)
+{
+ if (strcmp(cmd, "add") == 0)
+ return NFTNL_CMD_ADD;
+ else if (strcmp(cmd, "insert") == 0)
+ return NFTNL_CMD_INSERT;
+ else if (strcmp(cmd, "delete") == 0)
+ return NFTNL_CMD_DELETE;
+ else if (strcmp(cmd, "replace") == 0)
+ return NFTNL_CMD_REPLACE;
+ else if (strcmp(cmd, "flush") == 0)
+ return NFTNL_CMD_FLUSH;
+
+ return NFTNL_CMD_UNSPEC;
+}
+
+int nftnl_fprintf(FILE *fp, const void *obj, uint32_t cmd, uint32_t type,
+ uint32_t flags,
+ int (*snprintf_cb)(char *buf, size_t bufsiz, const void *obj,
+ uint32_t cmd, uint32_t type,
+ uint32_t flags))
+{
+ char _buf[NFTNL_SNPRINTF_BUFSIZ];
+ char *buf = _buf;
+ size_t bufsiz = sizeof(_buf);
+ int ret;
+
+ ret = snprintf_cb(buf, bufsiz, obj, cmd, type, flags);
+ if (ret <= 0)
+ goto out;
+
+ if (ret >= NFTNL_SNPRINTF_BUFSIZ) {
+ bufsiz = ret + 1;
+
+ buf = malloc(bufsiz);
+ if (buf == NULL)
+ return -1;
+
+ ret = snprintf_cb(buf, bufsiz, obj, cmd, type, flags);
+ if (ret <= 0)
+ goto out;
+ }
+
+ ret = fprintf(fp, "%s", buf);
+
+out:
+ if (buf != _buf)
+ xfree(buf);
+
+ return ret;
+}
+
+void __nftnl_assert_attr_exists(uint16_t attr, uint16_t attr_max,
+ const char *filename, int line)
+{
+ fprintf(stderr, "libnftnl: attribute %d > %d (maximum) assertion failed in %s:%d\n",
+ attr, attr_max, filename, line);
+ exit(EXIT_FAILURE);
+}
+
+void __nftnl_assert_fail(uint16_t attr, const char *filename, int line)
+{
+ fprintf(stderr, "libnftnl: attribute %d assertion failed in %s:%d\n",
+ attr, filename, line);
+ exit(EXIT_FAILURE);
+}
+
+void __noreturn __abi_breakage(const char *file, int line, const char *reason)
+{
+ fprintf(stderr, "nf_tables kernel ABI is broken, contact your vendor.\n"
+ "%s:%d reason: %s\n", file, line, reason);
+ exit(EXIT_FAILURE);
+}