diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:04:34 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:04:34 +0000 |
commit | 371c8a8bca2b6862226bc79c13d143e07c1d8fc3 (patch) | |
tree | 06c7dc8826596690422e79d5bde2f46c90492de3 /src | |
parent | Initial commit. (diff) | |
download | libnftnl-371c8a8bca2b6862226bc79c13d143e07c1d8fc3.tar.xz libnftnl-371c8a8bca2b6862226bc79c13d143e07c1d8fc3.zip |
Adding upstream version 1.2.6.upstream/1.2.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
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("a->bytes, data, sizeof(quota->bytes)); + break; + case NFTNL_EXPR_QUOTA_CONSUMED: + memcpy("a->consumed, data, sizeof(quota->consumed)); + break; + case NFTNL_EXPR_QUOTA_FLAGS: + memcpy("a->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 "a->bytes; + case NFTNL_EXPR_QUOTA_CONSUMED: + *data_len = sizeof(quota->consumed); + return "a->consumed; + case NFTNL_EXPR_QUOTA_FLAGS: + *data_len = sizeof(quota->flags); + return "a->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("a->bytes, data, sizeof(quota->bytes)); + break; + case NFTNL_OBJ_QUOTA_CONSUMED: + memcpy("a->consumed, data, sizeof(quota->consumed)); + break; + case NFTNL_OBJ_QUOTA_FLAGS: + memcpy("a->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 "a->bytes; + case NFTNL_OBJ_QUOTA_CONSUMED: + *data_len = sizeof(quota->consumed); + return "a->consumed; + case NFTNL_OBJ_QUOTA_FLAGS: + *data_len = sizeof(quota->flags); + return "a->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); +} |