summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGaurav Sinha <gaurav.sinha@vyatta.com>2012-01-18 12:05:50 -0800
committerGaurav Sinha <gaurav.sinha@vyatta.com>2012-01-18 12:05:50 -0800
commite72ca98329a25414108e2af350c4014de9e8f736 (patch)
treedfd32da0647916edf9a055e44d9d59596a3721a8 /src
parentfa0e506210775233cb34513d45878991ef50aae6 (diff)
parentca37a710d526d17490ebdc3af760bfddd316426d (diff)
downloadconntrack-tools-e72ca98329a25414108e2af350c4014de9e8f736.tar.gz
conntrack-tools-e72ca98329a25414108e2af350c4014de9e8f736.zip
Creating development branch expect-sync, has merged content from oxnard and upstreamexpect-sync
Diffstat (limited to 'src')
-rw-r--r--src/.gitignore6
-rw-r--r--src/Makefile.am11
-rw-r--r--src/Makefile.in134
-rw-r--r--src/build.c191
-rw-r--r--src/cache-ct.c356
-rw-r--r--src/cache-exp.c308
-rw-r--r--src/cache.c163
-rw-r--r--src/conntrack.c915
-rw-r--r--src/external_cache.c130
-rw-r--r--src/external_inject.c134
-rw-r--r--src/filter.c98
-rw-r--r--src/internal_bypass.c222
-rw-r--r--src/internal_cache.c287
-rw-r--r--src/lock.c0
-rw-r--r--src/log.c37
-rw-r--r--src/main.c81
-rw-r--r--src/netlink.c139
-rw-r--r--src/network.c24
-rw-r--r--src/parse.c293
-rw-r--r--src/read_config_lex.l5
-rw-r--r--src/read_config_yy.y87
-rw-r--r--src/run.c449
-rw-r--r--src/stats-mode.c50
-rw-r--r--src/sync-alarm.c22
-rw-r--r--src/sync-ftfw.c49
-rw-r--r--src/sync-mode.c265
-rw-r--r--src/sync-notrack.c47
27 files changed, 3538 insertions, 965 deletions
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..6e6763d
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,6 @@
+/conntrack
+/conntrackd
+
+/read_config_lex.c
+/read_config_yy.c
+/read_config_yy.h
diff --git a/src/Makefile.am b/src/Makefile.am
index 76f0e73..7d7b2ac 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,19 +1,18 @@
include $(top_srcdir)/Make_global.am
-YACC=@YACC@ -d
+AM_YFLAGS = -d
CLEANFILES = read_config_yy.c read_config_lex.c
sbin_PROGRAMS = conntrack conntrackd
conntrack_SOURCES = conntrack.c
-conntrack_LDADD = ../extensions/libct_proto_tcp.la ../extensions/libct_proto_udp.la ../extensions/libct_proto_udplite.la ../extensions/libct_proto_icmp.la ../extensions/libct_proto_icmpv6.la ../extensions/libct_proto_sctp.la ../extensions/libct_proto_dccp.la ../extensions/libct_proto_gre.la ../extensions/libct_proto_unknown.la
-conntrack_LDFLAGS = $(all_libraries) @LIBNETFILTER_CONNTRACK_LIBS@
+conntrack_LDADD = ../extensions/libct_proto_tcp.la ../extensions/libct_proto_udp.la ../extensions/libct_proto_udplite.la ../extensions/libct_proto_icmp.la ../extensions/libct_proto_icmpv6.la ../extensions/libct_proto_sctp.la ../extensions/libct_proto_dccp.la ../extensions/libct_proto_gre.la ../extensions/libct_proto_unknown.la ${LIBNETFILTER_CONNTRACK_LIBS}
conntrackd_SOURCES = alarm.c main.c run.c hash.c queue.c rbtree.c \
local.c log.c mcast.c udp.c netlink.c vector.c \
filter.c fds.c event.c process.c origin.c date.c \
- cache.c cache_iterators.c \
+ cache.c cache-ct.c cache-exp.c \
cache_timer.c \
sync-mode.c sync-alarm.c sync-ftfw.c sync-notrack.c \
traffic_stats.c stats-mode.c \
@@ -22,12 +21,12 @@ conntrackd_SOURCES = alarm.c main.c run.c hash.c queue.c rbtree.c \
channel.c multichannel.c channel_mcast.c channel_udp.c \
tcp.c channel_tcp.c \
external_cache.c external_inject.c \
- internal_cache.c internal_bypass.c \
+ internal_cache.c internal_bypass.c \
read_config_yy.y read_config_lex.l
# yacc and lex generate dirty code
read_config_yy.o read_config_lex.o: AM_CFLAGS += -Wno-missing-prototypes -Wno-missing-declarations -Wno-implicit-function-declaration -Wno-nested-externs -Wno-undef -Wno-redundant-decls
-conntrackd_LDFLAGS = $(all_libraries) @LIBNETFILTER_CONNTRACK_LIBS@
+conntrackd_LDADD = ${LIBNETFILTER_CONNTRACK_LIBS}
EXTRA_DIST = read_config_yy.h
diff --git a/src/Makefile.in b/src/Makefile.in
index 94c36c3..571a811 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -1,4 +1,4 @@
-# Makefile.in generated by automake 1.11 from Makefile.am.
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
@@ -34,17 +34,16 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-target_triplet = @target@
DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
$(top_srcdir)/Make_global.am read_config_lex.c \
- read_config_yy.c
+ read_config_yy.c read_config_yy.h
sbin_PROGRAMS = conntrack$(EXEEXT) conntrackd$(EXEEXT)
subdir = src
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(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.in
+ $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(install_sh) -d
@@ -54,6 +53,7 @@ am__installdirs = "$(DESTDIR)$(sbindir)"
PROGRAMS = $(sbin_PROGRAMS)
am_conntrack_OBJECTS = conntrack.$(OBJEXT)
conntrack_OBJECTS = $(am_conntrack_OBJECTS)
+am__DEPENDENCIES_1 =
conntrack_DEPENDENCIES = ../extensions/libct_proto_tcp.la \
../extensions/libct_proto_udp.la \
../extensions/libct_proto_udplite.la \
@@ -62,17 +62,17 @@ conntrack_DEPENDENCIES = ../extensions/libct_proto_tcp.la \
../extensions/libct_proto_sctp.la \
../extensions/libct_proto_dccp.la \
../extensions/libct_proto_gre.la \
- ../extensions/libct_proto_unknown.la
-conntrack_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
- $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
- $(conntrack_LDFLAGS) $(LDFLAGS) -o $@
+ ../extensions/libct_proto_unknown.la $(am__DEPENDENCIES_1)
+AM_V_lt = $(am__v_lt_$(V))
+am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+am__v_lt_0 = --silent
am_conntrackd_OBJECTS = alarm.$(OBJEXT) main.$(OBJEXT) run.$(OBJEXT) \
hash.$(OBJEXT) queue.$(OBJEXT) rbtree.$(OBJEXT) \
local.$(OBJEXT) log.$(OBJEXT) mcast.$(OBJEXT) udp.$(OBJEXT) \
netlink.$(OBJEXT) vector.$(OBJEXT) filter.$(OBJEXT) \
fds.$(OBJEXT) event.$(OBJEXT) process.$(OBJEXT) \
origin.$(OBJEXT) date.$(OBJEXT) cache.$(OBJEXT) \
- cache_iterators.$(OBJEXT) cache_timer.$(OBJEXT) \
+ cache-ct.$(OBJEXT) cache-exp.$(OBJEXT) cache_timer.$(OBJEXT) \
sync-mode.$(OBJEXT) sync-alarm.$(OBJEXT) sync-ftfw.$(OBJEXT) \
sync-notrack.$(OBJEXT) traffic_stats.$(OBJEXT) \
stats-mode.$(OBJEXT) network.$(OBJEXT) cidr.$(OBJEXT) \
@@ -83,30 +83,46 @@ am_conntrackd_OBJECTS = alarm.$(OBJEXT) main.$(OBJEXT) run.$(OBJEXT) \
internal_cache.$(OBJEXT) internal_bypass.$(OBJEXT) \
read_config_yy.$(OBJEXT) read_config_lex.$(OBJEXT)
conntrackd_OBJECTS = $(am_conntrackd_OBJECTS)
-conntrackd_LDADD = $(LDADD)
-conntrackd_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
- $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
- $(conntrackd_LDFLAGS) $(LDFLAGS) -o $@
+conntrackd_DEPENDENCIES = $(am__DEPENDENCIES_1)
DEFAULT_INCLUDES = -I.@am__isrc@
-depcomp = $(SHELL) $(top_srcdir)/depcomp
+depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=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_$(V))
+am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+am__v_CC_0 = @echo " CC " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
- $(LDFLAGS) -o $@
+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_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo " CCLD " $@;
LEXCOMPILE = $(LEX) $(LFLAGS) $(AM_LFLAGS)
-LTLEXCOMPILE = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(LEX) $(LFLAGS) $(AM_LFLAGS)
-YLWRAP = $(top_srcdir)/ylwrap
+LTLEXCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(LEX) $(LFLAGS) $(AM_LFLAGS)
+AM_V_LEX = $(am__v_LEX_$(V))
+am__v_LEX_ = $(am__v_LEX_$(AM_DEFAULT_VERBOSITY))
+am__v_LEX_0 = @echo " LEX " $@;
+YLWRAP = $(top_srcdir)/build-aux/ylwrap
YACCCOMPILE = $(YACC) $(YFLAGS) $(AM_YFLAGS)
-LTYACCCOMPILE = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(YACC) $(YFLAGS) $(AM_YFLAGS)
+LTYACCCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(YACC) $(YFLAGS) $(AM_YFLAGS)
+AM_V_YACC = $(am__v_YACC_$(V))
+am__v_YACC_ = $(am__v_YACC_$(AM_DEFAULT_VERBOSITY))
+am__v_YACC_0 = @echo " YACC " $@;
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
SOURCES = $(conntrack_SOURCES) $(conntrackd_SOURCES)
DIST_SOURCES = $(conntrack_SOURCES) $(conntrackd_SOURCES)
ETAGS = etags
@@ -114,6 +130,7 @@ CTAGS = ctags
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
@@ -136,7 +153,6 @@ EGREP = @EGREP@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GREP = @GREP@
-HAVE_PKG_CONFIG = @HAVE_PKG_CONFIG@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
@@ -174,15 +190,15 @@ 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@
-XLEX = @XLEX@
-XYACC = @XYACC@
-YACC = @YACC@ -d
+YACC = @YACC@
YFLAGS = @YFLAGS@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
@@ -232,27 +248,24 @@ sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
-target = @target@
target_alias = @target_alias@
-target_cpu = @target_cpu@
-target_os = @target_os@
-target_vendor = @target_vendor@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
+AM_CPPFLAGS = -I$(top_srcdir)/include
AM_CFLAGS = -std=gnu99 -W -Wall \
-Wmissing-prototypes -Wwrite-strings -Wcast-qual -Wfloat-equal -Wshadow -Wpointer-arith -Wbad-function-cast -Wsign-compare -Waggregate-return -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wstrict-prototypes -Wundef \
- -Wno-unused-parameter
+ -Wno-unused-parameter ${LIBNFNETLINK_CFLAGS} \
+ ${LIBNETFILTER_CONNTRACK_CFLAGS}
+AM_YFLAGS = -d
CLEANFILES = read_config_yy.c read_config_lex.c
conntrack_SOURCES = conntrack.c
-conntrack_LDADD = ../extensions/libct_proto_tcp.la ../extensions/libct_proto_udp.la ../extensions/libct_proto_udplite.la ../extensions/libct_proto_icmp.la ../extensions/libct_proto_icmpv6.la ../extensions/libct_proto_sctp.la ../extensions/libct_proto_dccp.la ../extensions/libct_proto_gre.la ../extensions/libct_proto_unknown.la
-conntrack_LDFLAGS = $(all_libraries) @LIBNETFILTER_CONNTRACK_LIBS@
+conntrack_LDADD = ../extensions/libct_proto_tcp.la ../extensions/libct_proto_udp.la ../extensions/libct_proto_udplite.la ../extensions/libct_proto_icmp.la ../extensions/libct_proto_icmpv6.la ../extensions/libct_proto_sctp.la ../extensions/libct_proto_dccp.la ../extensions/libct_proto_gre.la ../extensions/libct_proto_unknown.la ${LIBNETFILTER_CONNTRACK_LIBS}
conntrackd_SOURCES = alarm.c main.c run.c hash.c queue.c rbtree.c \
local.c log.c mcast.c udp.c netlink.c vector.c \
filter.c fds.c event.c process.c origin.c date.c \
- cache.c cache_iterators.c \
+ cache.c cache-ct.c cache-exp.c \
cache_timer.c \
sync-mode.c sync-alarm.c sync-ftfw.c sync-notrack.c \
traffic_stats.c stats-mode.c \
@@ -264,7 +277,7 @@ conntrackd_SOURCES = alarm.c main.c run.c hash.c queue.c rbtree.c \
internal_cache.c internal_bypass.c \
read_config_yy.y read_config_lex.l
-conntrackd_LDFLAGS = $(all_libraries) @LIBNETFILTER_CONNTRACK_LIBS@
+conntrackd_LDADD = ${LIBNETFILTER_CONNTRACK_LIBS}
EXTRA_DIST = read_config_yy.h
all: all-am
@@ -279,9 +292,9 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/Make_global.am $(am_
exit 1;; \
esac; \
done; \
- echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \
$(am__cd) $(top_srcdir) && \
- $(AUTOMAKE) --gnu src/Makefile
+ $(AUTOMAKE) --foreign src/Makefile
.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
@@ -345,10 +358,15 @@ clean-sbinPROGRAMS:
rm -f $$list
conntrack$(EXEEXT): $(conntrack_OBJECTS) $(conntrack_DEPENDENCIES)
@rm -f conntrack$(EXEEXT)
- $(conntrack_LINK) $(conntrack_OBJECTS) $(conntrack_LDADD) $(LIBS)
+ $(AM_V_CCLD)$(LINK) $(conntrack_OBJECTS) $(conntrack_LDADD) $(LIBS)
+read_config_yy.h: read_config_yy.c
+ @if test ! -f $@; then \
+ rm -f read_config_yy.c; \
+ $(MAKE) $(AM_MAKEFLAGS) read_config_yy.c; \
+ else :; fi
conntrackd$(EXEEXT): $(conntrackd_OBJECTS) $(conntrackd_DEPENDENCIES)
@rm -f conntrackd$(EXEEXT)
- $(conntrackd_LINK) $(conntrackd_OBJECTS) $(conntrackd_LDADD) $(LIBS)
+ $(AM_V_CCLD)$(LINK) $(conntrackd_OBJECTS) $(conntrackd_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -358,8 +376,9 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alarm.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/build.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cache-ct.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cache-exp.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cache.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cache_iterators.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cache_timer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/channel.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/channel_mcast.Po@am__quote@
@@ -402,31 +421,37 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vector.Po@am__quote@
.c.o:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@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
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $<
.c.obj:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@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
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
-@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@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
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
.l.c:
- $(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE)
+ $(AM_V_LEX)$(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE)
.y.c:
- $(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h $*.h y.output $*.output -- $(YACCCOMPILE)
+ $(AM_V_YACC)$(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h $*.h y.output $*.output -- $(YACCCOMPILE)
mostlyclean-libtool:
-rm -f *.lo
@@ -551,6 +576,7 @@ maintainer-clean-generic:
@echo "it deletes files that may require special tools to rebuild."
-rm -f read_config_lex.c
-rm -f read_config_yy.c
+ -rm -f read_config_yy.h
clean: clean-am
clean-am: clean-generic clean-libtool clean-sbinPROGRAMS \
diff --git a/src/build.c b/src/build.c
index 8f9d0b1..3193884 100644
--- a/src/build.c
+++ b/src/build.c
@@ -1,6 +1,7 @@
/*
- * (C) 2006-2008 by Pablo Neira Ayuso <pablo@netfilter.org>
- *
+ * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2011 by Vyatta Inc. <http://www.vyatta.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
@@ -42,14 +43,14 @@ addattr(struct nethdr *n, int attr, const void *data, size_t len)
}
static inline void
-__build_u8(const struct nf_conntrack *ct, int a, struct nethdr *n, int b)
+ct_build_u8(const struct nf_conntrack *ct, int a, struct nethdr *n, int b)
{
void *ptr = put_header(n, b, sizeof(uint8_t));
memcpy(ptr, nfct_get_attr(ct, a), sizeof(uint8_t));
}
static inline void
-__build_u16(const struct nf_conntrack *ct, int a, struct nethdr *n, int b)
+ct_build_u16(const struct nf_conntrack *ct, int a, struct nethdr *n, int b)
{
uint16_t data = nfct_get_attr_u16(ct, a);
data = htons(data);
@@ -57,7 +58,7 @@ __build_u16(const struct nf_conntrack *ct, int a, struct nethdr *n, int b)
}
static inline void
-__build_u32(const struct nf_conntrack *ct, int a, struct nethdr *n, int b)
+ct_build_u32(const struct nf_conntrack *ct, int a, struct nethdr *n, int b)
{
uint32_t data = nfct_get_attr_u32(ct, a);
data = htonl(data);
@@ -65,7 +66,7 @@ __build_u32(const struct nf_conntrack *ct, int a, struct nethdr *n, int b)
}
static inline void
-__build_group(const struct nf_conntrack *ct, int a, struct nethdr *n,
+ct_build_group(const struct nf_conntrack *ct, int a, struct nethdr *n,
int b, int size)
{
void *ptr = put_header(n, b, size);
@@ -73,7 +74,7 @@ __build_group(const struct nf_conntrack *ct, int a, struct nethdr *n,
}
static inline void
-__build_natseqadj(const struct nf_conntrack *ct, struct nethdr *n)
+ct_build_natseqadj(const struct nf_conntrack *ct, struct nethdr *n)
{
struct nta_attr_natseqadj data = {
.orig_seq_correction_pos =
@@ -99,51 +100,55 @@ static enum nf_conntrack_attr nat_type[] =
static void build_l4proto_tcp(const struct nf_conntrack *ct, struct nethdr *n)
{
- __build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT,
- sizeof(struct nfct_attr_grp_port));
+ ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT,
+ sizeof(struct nfct_attr_grp_port));
if (!nfct_attr_is_set(ct, ATTR_TCP_STATE))
return;
- __build_u8(ct, ATTR_TCP_STATE, n, NTA_TCP_STATE);
+ ct_build_u8(ct, ATTR_TCP_STATE, n, NTA_TCP_STATE);
+ if (CONFIG(sync).tcp_window_tracking) {
+ ct_build_u8(ct, ATTR_TCP_WSCALE_ORIG, n, NTA_TCP_WSCALE_ORIG);
+ ct_build_u8(ct, ATTR_TCP_WSCALE_REPL, n, NTA_TCP_WSCALE_REPL);
+ }
}
static void build_l4proto_sctp(const struct nf_conntrack *ct, struct nethdr *n)
{
- __build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT,
- sizeof(struct nfct_attr_grp_port));
+ ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT,
+ sizeof(struct nfct_attr_grp_port));
if (!nfct_attr_is_set(ct, ATTR_SCTP_STATE))
return;
- __build_u8(ct, ATTR_SCTP_STATE, n, NTA_SCTP_STATE);
- __build_u32(ct, ATTR_SCTP_VTAG_ORIG, n, NTA_SCTP_VTAG_ORIG);
- __build_u32(ct, ATTR_SCTP_VTAG_REPL, n, NTA_SCTP_VTAG_REPL);
+ ct_build_u8(ct, ATTR_SCTP_STATE, n, NTA_SCTP_STATE);
+ ct_build_u32(ct, ATTR_SCTP_VTAG_ORIG, n, NTA_SCTP_VTAG_ORIG);
+ ct_build_u32(ct, ATTR_SCTP_VTAG_REPL, n, NTA_SCTP_VTAG_REPL);
}
static void build_l4proto_dccp(const struct nf_conntrack *ct, struct nethdr *n)
{
- __build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT,
- sizeof(struct nfct_attr_grp_port));
+ ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT,
+ sizeof(struct nfct_attr_grp_port));
if (!nfct_attr_is_set(ct, ATTR_DCCP_STATE))
return;
- __build_u8(ct, ATTR_DCCP_STATE, n, NTA_DCCP_STATE);
- __build_u8(ct, ATTR_DCCP_ROLE, n, NTA_DCCP_ROLE);
+ ct_build_u8(ct, ATTR_DCCP_STATE, n, NTA_DCCP_STATE);
+ ct_build_u8(ct, ATTR_DCCP_ROLE, n, NTA_DCCP_ROLE);
}
static void build_l4proto_icmp(const struct nf_conntrack *ct, struct nethdr *n)
{
- __build_u8(ct, ATTR_ICMP_TYPE, n, NTA_ICMP_TYPE);
- __build_u8(ct, ATTR_ICMP_CODE, n, NTA_ICMP_CODE);
- __build_u16(ct, ATTR_ICMP_ID, n, NTA_ICMP_ID);
+ ct_build_u8(ct, ATTR_ICMP_TYPE, n, NTA_ICMP_TYPE);
+ ct_build_u8(ct, ATTR_ICMP_CODE, n, NTA_ICMP_CODE);
+ ct_build_u16(ct, ATTR_ICMP_ID, n, NTA_ICMP_ID);
}
static void build_l4proto_udp(const struct nf_conntrack *ct, struct nethdr *n)
{
- __build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT,
- sizeof(struct nfct_attr_grp_port));
+ ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT,
+ sizeof(struct nfct_attr_grp_port));
}
#ifndef IPPROTO_DCCP
@@ -157,48 +162,49 @@ static struct build_l4proto {
[IPPROTO_SCTP] = { .build = build_l4proto_sctp },
[IPPROTO_DCCP] = { .build = build_l4proto_dccp },
[IPPROTO_ICMP] = { .build = build_l4proto_icmp },
- [IPPROTO_UDP] = { .build = build_l4proto_udp },
+ [IPPROTO_ICMPV6] = { .build = build_l4proto_icmp },
+ [IPPROTO_UDP] = { .build = build_l4proto_udp },
};
-void build_payload(const struct nf_conntrack *ct, struct nethdr *n)
+void ct2msg(const struct nf_conntrack *ct, struct nethdr *n)
{
uint8_t l4proto = nfct_get_attr_u8(ct, ATTR_L4PROTO);
if (nfct_attr_grp_is_set(ct, ATTR_GRP_ORIG_IPV4)) {
- __build_group(ct, ATTR_GRP_ORIG_IPV4, n, NTA_IPV4,
+ ct_build_group(ct, ATTR_GRP_ORIG_IPV4, n, NTA_IPV4,
sizeof(struct nfct_attr_grp_ipv4));
} else if (nfct_attr_grp_is_set(ct, ATTR_GRP_ORIG_IPV6)) {
- __build_group(ct, ATTR_GRP_ORIG_IPV6, n, NTA_IPV6,
+ ct_build_group(ct, ATTR_GRP_ORIG_IPV6, n, NTA_IPV6,
sizeof(struct nfct_attr_grp_ipv6));
}
- __build_u32(ct, ATTR_STATUS, n, NTA_STATUS);
- __build_u8(ct, ATTR_L4PROTO, n, NTA_L4PROTO);
+ ct_build_u32(ct, ATTR_STATUS, n, NTA_STATUS);
+ ct_build_u8(ct, ATTR_L4PROTO, n, NTA_L4PROTO);
if (l4proto_fcn[l4proto].build)
l4proto_fcn[l4proto].build(ct, n);
if (!CONFIG(commit_timeout) && nfct_attr_is_set(ct, ATTR_TIMEOUT))
- __build_u32(ct, ATTR_TIMEOUT, n, NTA_TIMEOUT);
+ ct_build_u32(ct, ATTR_TIMEOUT, n, NTA_TIMEOUT);
if (nfct_attr_is_set(ct, ATTR_MARK))
- __build_u32(ct, ATTR_MARK, n, NTA_MARK);
+ ct_build_u32(ct, ATTR_MARK, n, NTA_MARK);
/* setup the master conntrack */
if (nfct_attr_grp_is_set(ct, ATTR_GRP_MASTER_IPV4)) {
- __build_group(ct, ATTR_GRP_MASTER_IPV4, n, NTA_MASTER_IPV4,
+ ct_build_group(ct, ATTR_GRP_MASTER_IPV4, n, NTA_MASTER_IPV4,
sizeof(struct nfct_attr_grp_ipv4));
- __build_u8(ct, ATTR_MASTER_L4PROTO, n, NTA_MASTER_L4PROTO);
+ ct_build_u8(ct, ATTR_MASTER_L4PROTO, n, NTA_MASTER_L4PROTO);
if (nfct_attr_grp_is_set(ct, ATTR_GRP_MASTER_PORT)) {
- __build_group(ct, ATTR_GRP_MASTER_PORT,
+ ct_build_group(ct, ATTR_GRP_MASTER_PORT,
n, NTA_MASTER_PORT,
sizeof(struct nfct_attr_grp_port));
}
} else if (nfct_attr_grp_is_set(ct, ATTR_GRP_MASTER_IPV6)) {
- __build_group(ct, ATTR_GRP_MASTER_IPV6, n, NTA_MASTER_IPV6,
+ ct_build_group(ct, ATTR_GRP_MASTER_IPV6, n, NTA_MASTER_IPV6,
sizeof(struct nfct_attr_grp_ipv6));
- __build_u8(ct, ATTR_MASTER_L4PROTO, n, NTA_MASTER_L4PROTO);
+ ct_build_u8(ct, ATTR_MASTER_L4PROTO, n, NTA_MASTER_L4PROTO);
if (nfct_attr_grp_is_set(ct, ATTR_GRP_MASTER_PORT)) {
- __build_group(ct, ATTR_GRP_MASTER_PORT,
+ ct_build_group(ct, ATTR_GRP_MASTER_PORT,
n, NTA_MASTER_PORT,
sizeof(struct nfct_attr_grp_port));
}
@@ -206,15 +212,114 @@ void build_payload(const struct nf_conntrack *ct, struct nethdr *n)
/* NAT */
if (nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT))
- __build_u32(ct, ATTR_REPL_IPV4_DST, n, NTA_SNAT_IPV4);
+ ct_build_u32(ct, ATTR_REPL_IPV4_DST, n, NTA_SNAT_IPV4);
if (nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT))
- __build_u32(ct, ATTR_REPL_IPV4_SRC, n, NTA_DNAT_IPV4);
+ ct_build_u32(ct, ATTR_REPL_IPV4_SRC, n, NTA_DNAT_IPV4);
if (nfct_getobjopt(ct, NFCT_GOPT_IS_SPAT))
- __build_u16(ct, ATTR_REPL_PORT_DST, n, NTA_SPAT_PORT);
+ ct_build_u16(ct, ATTR_REPL_PORT_DST, n, NTA_SPAT_PORT);
if (nfct_getobjopt(ct, NFCT_GOPT_IS_DPAT))
- __build_u16(ct, ATTR_REPL_PORT_SRC, n, NTA_DPAT_PORT);
+ ct_build_u16(ct, ATTR_REPL_PORT_SRC, n, NTA_DPAT_PORT);
/* NAT sequence adjustment */
if (nfct_attr_is_set_array(ct, nat_type, 6))
- __build_natseqadj(ct, n);
+ ct_build_natseqadj(ct, n);
+}
+
+static void
+exp_build_l4proto_tcp(const struct nf_conntrack *ct, struct nethdr *n, int a)
+{
+ ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, a,
+ sizeof(struct nfct_attr_grp_port));
+}
+
+static void
+exp_build_l4proto_sctp(const struct nf_conntrack *ct, struct nethdr *n, int a)
+{
+ ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, a,
+ sizeof(struct nfct_attr_grp_port));
+}
+
+static void
+exp_build_l4proto_dccp(const struct nf_conntrack *ct, struct nethdr *n, int a)
+{
+ ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, a,
+ sizeof(struct nfct_attr_grp_port));
+}
+
+static void
+exp_build_l4proto_udp(const struct nf_conntrack *ct, struct nethdr *n, int a)
+{
+ ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, a,
+ sizeof(struct nfct_attr_grp_port));
+}
+
+static struct exp_build_l4proto {
+ void (*build)(const struct nf_conntrack *, struct nethdr *n, int a);
+} exp_l4proto_fcn[IPPROTO_MAX] = {
+ [IPPROTO_TCP] = { .build = exp_build_l4proto_tcp },
+ [IPPROTO_SCTP] = { .build = exp_build_l4proto_sctp },
+ [IPPROTO_DCCP] = { .build = exp_build_l4proto_dccp },
+ [IPPROTO_UDP] = { .build = exp_build_l4proto_udp },
+};
+
+static inline void
+exp_build_u32(const struct nf_expect *exp, int a, struct nethdr *n, int b)
+{
+ uint32_t data = nfexp_get_attr_u32(exp, a);
+ data = htonl(data);
+ addattr(n, b, &data, sizeof(uint32_t));
+}
+
+void exp2msg(const struct nf_expect *exp, struct nethdr *n)
+{
+ const struct nf_conntrack *ct = nfexp_get_attr(exp, ATTR_EXP_MASTER);
+ uint8_t l4proto = nfct_get_attr_u8(ct, ATTR_L4PROTO);
+
+ /* master conntrack for this expectation. */
+ if (nfct_attr_grp_is_set(ct, ATTR_GRP_ORIG_IPV4)) {
+ ct_build_group(ct, ATTR_GRP_ORIG_IPV4, n, NTA_EXP_MASTER_IPV4,
+ sizeof(struct nfct_attr_grp_ipv4));
+ } else if (nfct_attr_grp_is_set(ct, ATTR_GRP_ORIG_IPV6)) {
+ ct_build_group(ct, ATTR_GRP_ORIG_IPV6, n, NTA_EXP_MASTER_IPV6,
+ sizeof(struct nfct_attr_grp_ipv6));
+ }
+ ct_build_u8(ct, ATTR_L4PROTO, n, NTA_EXP_MASTER_L4PROTO);
+
+ if (exp_l4proto_fcn[l4proto].build)
+ exp_l4proto_fcn[l4proto].build(ct, n, NTA_EXP_MASTER_PORT);
+
+ /* the expectation itself. */
+ ct = nfexp_get_attr(exp, ATTR_EXP_EXPECTED);
+
+ if (nfct_attr_grp_is_set(ct, ATTR_GRP_ORIG_IPV4)) {
+ ct_build_group(ct, ATTR_GRP_ORIG_IPV4, n, NTA_EXP_EXPECT_IPV4,
+ sizeof(struct nfct_attr_grp_ipv4));
+ } else if (nfct_attr_grp_is_set(ct, ATTR_GRP_ORIG_IPV6)) {
+ ct_build_group(ct, ATTR_GRP_ORIG_IPV6, n, NTA_EXP_EXPECT_IPV6,
+ sizeof(struct nfct_attr_grp_ipv6));
+ }
+ ct_build_u8(ct, ATTR_L4PROTO, n, NTA_EXP_EXPECT_L4PROTO);
+
+ if (exp_l4proto_fcn[l4proto].build)
+ exp_l4proto_fcn[l4proto].build(ct, n, NTA_EXP_EXPECT_PORT);
+
+ /* mask for the expectation. */
+ ct = nfexp_get_attr(exp, ATTR_EXP_MASK);
+
+ if (nfct_attr_grp_is_set(ct, ATTR_GRP_ORIG_IPV4)) {
+ ct_build_group(ct, ATTR_GRP_ORIG_IPV4, n, NTA_EXP_MASK_IPV4,
+ sizeof(struct nfct_attr_grp_ipv4));
+ } else if (nfct_attr_grp_is_set(ct, ATTR_GRP_ORIG_IPV6)) {
+ ct_build_group(ct, ATTR_GRP_ORIG_IPV6, n, NTA_EXP_MASK_IPV6,
+ sizeof(struct nfct_attr_grp_ipv6));
+ }
+ ct_build_u8(ct, ATTR_L4PROTO, n, NTA_EXP_MASK_L4PROTO);
+
+ if (exp_l4proto_fcn[l4proto].build)
+ exp_l4proto_fcn[l4proto].build(ct, n, NTA_EXP_MASK_PORT);
+
+ if (!CONFIG(commit_timeout) && nfexp_attr_is_set(exp, ATTR_EXP_TIMEOUT))
+ exp_build_u32(exp, ATTR_EXP_TIMEOUT, n, NTA_EXP_TIMEOUT);
+
+ exp_build_u32(exp, ATTR_EXP_FLAGS, n, NTA_EXP_FLAGS);
}
diff --git a/src/cache-ct.c b/src/cache-ct.c
new file mode 100644
index 0000000..0ad8d2a
--- /dev/null
+++ b/src/cache-ct.c
@@ -0,0 +1,356 @@
+/*
+ * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2011 by Vyatta Inc. <http://www.vyatta.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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "cache.h"
+#include "hash.h"
+#include "log.h"
+#include "conntrackd.h"
+#include "netlink.h"
+#include "event.h"
+#include "jhash.h"
+#include "network.h"
+
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+
+static uint32_t
+cache_hash4_ct(const struct nf_conntrack *ct, const struct hashtable *table)
+{
+ uint32_t a[4] = {
+ [0] = nfct_get_attr_u32(ct, ATTR_IPV4_SRC),
+ [1] = nfct_get_attr_u32(ct, ATTR_IPV4_DST),
+ [2] = nfct_get_attr_u8(ct, ATTR_L3PROTO) << 16 |
+ nfct_get_attr_u8(ct, ATTR_L4PROTO),
+ [3] = nfct_get_attr_u16(ct, ATTR_PORT_SRC) << 16 |
+ nfct_get_attr_u16(ct, ATTR_PORT_DST),
+ };
+
+ /*
+ * Instead of returning hash % table->hashsize (implying a divide)
+ * we return the high 32 bits of the (hash * table->hashsize) that will
+ * give results between [0 and hashsize-1] and same hash distribution,
+ * but using a multiply, less expensive than a divide. See:
+ * http://www.mail-archive.com/netdev@vger.kernel.org/msg56623.html
+ */
+ return ((uint64_t)jhash2(a, 4, 0) * table->hashsize) >> 32;
+}
+
+static uint32_t
+cache_hash6_ct(const struct nf_conntrack *ct, const struct hashtable *table)
+{
+ uint32_t a[10];
+
+ memcpy(&a[0], nfct_get_attr(ct, ATTR_IPV6_SRC), sizeof(uint32_t)*4);
+ memcpy(&a[4], nfct_get_attr(ct, ATTR_IPV6_SRC), sizeof(uint32_t)*4);
+ a[8] = nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO) << 16 |
+ nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO);
+ a[9] = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) << 16 |
+ nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST);
+
+ return ((uint64_t)jhash2(a, 10, 0) * table->hashsize) >> 32;
+}
+
+static uint32_t
+cache_ct_hash(const void *data, const struct hashtable *table)
+{
+ int ret = 0;
+ const struct nf_conntrack *ct = data;
+
+ switch(nfct_get_attr_u8(ct, ATTR_L3PROTO)) {
+ case AF_INET:
+ ret = cache_hash4_ct(ct, table);
+ break;
+ case AF_INET6:
+ ret = cache_hash6_ct(ct, table);
+ break;
+ default:
+ dlog(LOG_ERR, "unknown layer 3 proto in hash");
+ break;
+ }
+ return ret;
+}
+
+static int cache_ct_cmp(const void *data1, const void *data2)
+{
+ const struct cache_object *obj = data1;
+ const struct nf_conntrack *ct = data2;
+
+ return nfct_cmp(obj->ptr, ct, NFCT_CMP_ORIG) &&
+ nfct_get_attr_u32(obj->ptr, ATTR_ID) ==
+ nfct_get_attr_u32(ct, ATTR_ID);
+}
+
+static void *cache_ct_alloc(void)
+{
+ return nfct_new();
+}
+
+static void cache_ct_free(void *ptr)
+{
+ nfct_destroy(ptr);
+}
+
+static void cache_ct_copy(void *dst, void *src, unsigned int flags)
+{
+ nfct_copy(dst, src, flags);
+}
+
+static int cache_ct_dump_step(void *data1, void *n)
+{
+ char buf[1024];
+ int size;
+ struct __dump_container *container = data1;
+ struct cache_object *obj = n;
+ char *data = obj->data;
+ unsigned i;
+
+ /*
+ * XXX: Do not dump the entries that are scheduled to expire.
+ * These entries talk about already destroyed connections
+ * that we keep for some time just in case that we have to
+ * resent some lost messages. We do not show them to the
+ * user as he may think that the firewall replicas are not
+ * in sync. The branch below is a hack as it is quite
+ * specific and it breaks conntrackd modularity. Probably
+ * there's a nicer way to do this but until I come up with it...
+ */
+ if (CONFIG(flags) & CTD_SYNC_FTFW && obj->status == C_OBJ_DEAD)
+ return 0;
+
+ /* do not show cached timeout, this may confuse users */
+ if (nfct_attr_is_set(obj->ptr, ATTR_TIMEOUT))
+ nfct_attr_unset(obj->ptr, ATTR_TIMEOUT);
+
+ memset(buf, 0, sizeof(buf));
+ size = nfct_snprintf(buf,
+ sizeof(buf),
+ obj->ptr,
+ NFCT_T_UNKNOWN,
+ container->type,
+ 0);
+
+ for (i = 0; i < obj->cache->num_features; i++) {
+ if (obj->cache->features[i]->dump) {
+ size += obj->cache->features[i]->dump(obj,
+ data,
+ buf+size,
+ container->type);
+ data += obj->cache->features[i]->size;
+ }
+ }
+ if (container->type != NFCT_O_XML) {
+ long tm = time(NULL);
+ size += sprintf(buf+size, " [active since %lds]",
+ tm - obj->lifetime);
+ }
+ size += sprintf(buf+size, "\n");
+ if (send(container->fd, buf, size, 0) == -1) {
+ if (errno != EPIPE)
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+cache_ct_commit_step(struct __commit_container *tmp, struct cache_object *obj)
+{
+ int ret, retry = 1, timeout;
+ struct nf_conntrack *ct = obj->ptr;
+
+ if (CONFIG(commit_timeout)) {
+ timeout = CONFIG(commit_timeout);
+ } else {
+ timeout = time(NULL) - obj->lastupdate;
+ if (timeout < 0) {
+ /* XXX: Arbitrarily set the timer to one minute, how
+ * can this happen? For example, an adjustment due to
+ * daylight-saving. Probably other situations can
+ * trigger this. */
+ timeout = 60;
+ }
+ /* calculate an estimation of the current timeout */
+ timeout = nfct_get_attr_u32(ct, ATTR_TIMEOUT) - timeout;
+ if (timeout < 0) {
+ timeout = 60;
+ }
+ }
+
+retry:
+ if (nl_create_conntrack(tmp->h, ct, timeout) == -1) {
+ if (errno == EEXIST && retry == 1) {
+ ret = nl_destroy_conntrack(tmp->h, ct);
+ if (ret == 0 || (ret == -1 && errno == ENOENT)) {
+ if (retry) {
+ retry = 0;
+ goto retry;
+ }
+ }
+ dlog(LOG_ERR, "commit-destroy: %s", strerror(errno));
+ dlog_ct(STATE(log), ct, NFCT_O_PLAIN);
+ tmp->c->stats.commit_fail++;
+ } else {
+ dlog(LOG_ERR, "commit-create: %s", strerror(errno));
+ dlog_ct(STATE(log), ct, NFCT_O_PLAIN);
+ tmp->c->stats.commit_fail++;
+ }
+ } else {
+ tmp->c->stats.commit_ok++;
+ }
+}
+
+static int cache_ct_commit_related(void *data, void *n)
+{
+ struct cache_object *obj = n;
+
+ if (ct_is_related(obj->ptr))
+ cache_ct_commit_step(data, obj);
+
+ /* keep iterating even if we have found errors */
+ return 0;
+}
+
+static int cache_ct_commit_master(void *data, void *n)
+{
+ struct cache_object *obj = n;
+
+ if (ct_is_related(obj->ptr))
+ return 0;
+
+ cache_ct_commit_step(data, obj);
+ return 0;
+}
+
+static int cache_ct_commit(struct cache *c, struct nfct_handle *h, int clientfd)
+{
+ unsigned int commit_ok, commit_fail;
+ struct __commit_container tmp = {
+ .h = h,
+ .c = c,
+ };
+ struct timeval commit_stop, res;
+
+ /* we already have one commit in progress, skip this. The clientfd
+ * descriptor has to be closed by the caller. */
+ if (clientfd && STATE_SYNC(commit).clientfd != -1)
+ return -1;
+
+ switch(STATE_SYNC(commit).state) {
+ case COMMIT_STATE_INACTIVE:
+ gettimeofday(&STATE_SYNC(commit).stats.start, NULL);
+ STATE_SYNC(commit).stats.ok = c->stats.commit_ok;
+ STATE_SYNC(commit).stats.fail = c->stats.commit_fail;
+ STATE_SYNC(commit).clientfd = clientfd;
+ case COMMIT_STATE_MASTER:
+ STATE_SYNC(commit).current =
+ hashtable_iterate_limit(c->h, &tmp,
+ STATE_SYNC(commit).current,
+ CONFIG(general).commit_steps,
+ cache_ct_commit_master);
+ if (STATE_SYNC(commit).current < CONFIG(hashsize)) {
+ STATE_SYNC(commit).state = COMMIT_STATE_MASTER;
+ /* give it another step as soon as possible */
+ write_evfd(STATE_SYNC(commit).evfd);
+ return 1;
+ }
+ STATE_SYNC(commit).current = 0;
+ STATE_SYNC(commit).state = COMMIT_STATE_RELATED;
+ case COMMIT_STATE_RELATED:
+ STATE_SYNC(commit).current =
+ hashtable_iterate_limit(c->h, &tmp,
+ STATE_SYNC(commit).current,
+ CONFIG(general).commit_steps,
+ cache_ct_commit_related);
+ if (STATE_SYNC(commit).current < CONFIG(hashsize)) {
+ STATE_SYNC(commit).state = COMMIT_STATE_RELATED;
+ /* give it another step as soon as possible */
+ write_evfd(STATE_SYNC(commit).evfd);
+ return 1;
+ }
+ /* calculate the time that commit has taken */
+ gettimeofday(&commit_stop, NULL);
+ timersub(&commit_stop, &STATE_SYNC(commit).stats.start, &res);
+
+ /* calculate new entries committed */
+ commit_ok = c->stats.commit_ok - STATE_SYNC(commit).stats.ok;
+ commit_fail =
+ c->stats.commit_fail - STATE_SYNC(commit).stats.fail;
+
+ /* log results */
+ dlog(LOG_NOTICE, "Committed %u new entries", commit_ok);
+
+ if (commit_fail)
+ dlog(LOG_NOTICE, "%u entries can't be "
+ "committed", commit_fail);
+
+ dlog(LOG_NOTICE, "commit has taken %lu.%06lu seconds",
+ res.tv_sec, res.tv_usec);
+
+ /* prepare the state machine for new commits */
+ STATE_SYNC(commit).current = 0;
+ STATE_SYNC(commit).state = COMMIT_STATE_INACTIVE;
+
+ return 0;
+ }
+ return 1;
+}
+
+static struct nethdr *
+cache_ct_build_msg(const struct cache_object *obj, int type)
+{
+ return BUILD_NETMSG_FROM_CT(obj->ptr, type);
+}
+
+/* template to cache conntracks coming from the kernel. */
+struct cache_ops cache_sync_internal_ct_ops = {
+ .hash = cache_ct_hash,
+ .cmp = cache_ct_cmp,
+ .alloc = cache_ct_alloc,
+ .free = cache_ct_free,
+ .copy = cache_ct_copy,
+ .dump_step = cache_ct_dump_step,
+ .commit = NULL,
+ .build_msg = cache_ct_build_msg,
+};
+
+/* template to cache conntracks coming from the network. */
+struct cache_ops cache_sync_external_ct_ops = {
+ .hash = cache_ct_hash,
+ .cmp = cache_ct_cmp,
+ .alloc = cache_ct_alloc,
+ .free = cache_ct_free,
+ .copy = cache_ct_copy,
+ .dump_step = cache_ct_dump_step,
+ .commit = cache_ct_commit,
+ .build_msg = NULL,
+};
+
+/* template to cache conntracks for the statistics mode. */
+struct cache_ops cache_stats_ct_ops = {
+ .hash = cache_ct_hash,
+ .cmp = cache_ct_cmp,
+ .alloc = cache_ct_alloc,
+ .free = cache_ct_free,
+ .copy = cache_ct_copy,
+ .dump_step = cache_ct_dump_step,
+ .commit = NULL,
+ .build_msg = NULL,
+};
diff --git a/src/cache-exp.c b/src/cache-exp.c
new file mode 100644
index 0000000..e88877a
--- /dev/null
+++ b/src/cache-exp.c
@@ -0,0 +1,308 @@
+/*
+ * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2011 by Vyatta Inc. <http://www.vyatta.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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "cache.h"
+#include "hash.h"
+#include "log.h"
+#include "conntrackd.h"
+#include "netlink.h"
+#include "event.h"
+#include "jhash.h"
+#include "network.h"
+
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+
+static uint32_t
+cache_hash4_exp(const struct nf_conntrack *ct, const struct hashtable *table)
+{
+ uint32_t a[4] = {
+ [0] = nfct_get_attr_u32(ct, ATTR_IPV4_SRC),
+ [1] = nfct_get_attr_u32(ct, ATTR_IPV4_DST),
+ [2] = nfct_get_attr_u8(ct, ATTR_L3PROTO) << 16 |
+ nfct_get_attr_u8(ct, ATTR_L4PROTO),
+ [3] = nfct_get_attr_u16(ct, ATTR_PORT_SRC) << 16 |
+ nfct_get_attr_u16(ct, ATTR_PORT_DST),
+ };
+
+ /*
+ * Instead of returning hash % table->hashsize (implying a divide)
+ * we return the high 32 bits of the (hash * table->hashsize) that will
+ * give results between [0 and hashsize-1] and same hash distribution,
+ * but using a multiply, less expensive than a divide. See:
+ * http://www.mail-archive.com/netdev@vger.kernel.org/msg56623.html
+ */
+ return ((uint64_t)jhash2(a, 4, 0) * table->hashsize) >> 32;
+}
+
+static uint32_t
+cache_hash6_exp(const struct nf_conntrack *ct, const struct hashtable *table)
+{
+ uint32_t a[10];
+
+ memcpy(&a[0], nfct_get_attr(ct, ATTR_IPV6_SRC), sizeof(uint32_t)*4);
+ memcpy(&a[4], nfct_get_attr(ct, ATTR_IPV6_SRC), sizeof(uint32_t)*4);
+ a[8] = nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO) << 16 |
+ nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO);
+ a[9] = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) << 16 |
+ nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST);
+
+ return ((uint64_t)jhash2(a, 10, 0) * table->hashsize) >> 32;
+}
+
+static uint32_t
+cache_exp_hash(const void *data, const struct hashtable *table)
+{
+ int ret = 0;
+ const struct nf_expect *exp = data;
+ const struct nf_conntrack *ct = nfexp_get_attr(exp, ATTR_EXP_MASTER);
+
+ switch(nfct_get_attr_u8(ct, ATTR_L3PROTO)) {
+ case AF_INET:
+ ret = cache_hash4_exp(ct, table);
+ break;
+ case AF_INET6:
+ ret = cache_hash6_exp(ct, table);
+ break;
+ default:
+ dlog(LOG_ERR, "unknown layer 3 proto in hash");
+ break;
+ }
+ return ret;
+}
+
+static int cache_exp_cmp(const void *data1, const void *data2)
+{
+ const struct cache_object *obj = data1;
+ const struct nf_expect *exp = data2;
+
+ return nfexp_cmp(obj->ptr, exp, 0);
+}
+
+static void *cache_exp_alloc(void)
+{
+ return nfexp_new();
+}
+
+static void cache_exp_free(void *ptr)
+{
+ nfexp_destroy(ptr);
+}
+
+static void cache_exp_copy(void *dst, void *src, unsigned int flags)
+{
+ /* XXX: add nfexp_copy(...) to libnetfilter_conntrack. */
+ memcpy(dst, src, nfexp_maxsize());
+}
+
+static int cache_exp_dump_step(void *data1, void *n)
+{
+ char buf[1024];
+ int size;
+ struct __dump_container *container = data1;
+ struct cache_object *obj = n;
+ char *data = obj->data;
+ unsigned i;
+
+ /*
+ * XXX: Do not dump the entries that are scheduled to expire.
+ * These entries talk about already destroyed connections
+ * that we keep for some time just in case that we have to
+ * resent some lost messages. We do not show them to the
+ * user as he may think that the firewall replicas are not
+ * in sync. The branch below is a hack as it is quite
+ * specific and it breaks conntrackd modularity. Probably
+ * there's a nicer way to do this but until I come up with it...
+ */
+ if (CONFIG(flags) & CTD_SYNC_FTFW && obj->status == C_OBJ_DEAD)
+ return 0;
+
+ /* do not show cached timeout, this may confuse users */
+ if (nfexp_attr_is_set(obj->ptr, ATTR_EXP_TIMEOUT))
+ nfexp_attr_unset(obj->ptr, ATTR_EXP_TIMEOUT);
+
+ memset(buf, 0, sizeof(buf));
+ size = nfexp_snprintf(buf, sizeof(buf),obj->ptr,
+ NFCT_T_UNKNOWN, container->type, 0);
+
+ for (i = 0; i < obj->cache->num_features; i++) {
+ if (obj->cache->features[i]->dump) {
+ size += obj->cache->features[i]->dump(obj, data,
+ buf+size,
+ container->type);
+ data += obj->cache->features[i]->size;
+ }
+ }
+ if (container->type != NFCT_O_XML) {
+ long tm = time(NULL);
+ size += sprintf(buf+size, " [active since %lds]",
+ tm - obj->lifetime);
+ }
+ size += sprintf(buf+size, "\n");
+ if (send(container->fd, buf, size, 0) == -1) {
+ if (errno != EPIPE)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int cache_exp_commit_step(void *data, void *n)
+{
+ struct cache_object *obj = n;
+ struct __commit_container *tmp = data;
+ int ret, retry = 1, timeout;
+ struct nf_expect *exp = obj->ptr;
+
+ if (CONFIG(commit_timeout)) {
+ timeout = CONFIG(commit_timeout);
+ } else {
+ timeout = time(NULL) - obj->lastupdate;
+ if (timeout < 0) {
+ /* XXX: Arbitrarily set the timer to one minute, how
+ * can this happen? For example, an adjustment due to
+ * daylight-saving. Probably other situations can
+ * trigger this. */
+ timeout = 60;
+ }
+ /* calculate an estimation of the current timeout */
+ timeout = nfexp_get_attr_u32(exp, ATTR_EXP_TIMEOUT) - timeout;
+ if (timeout < 0) {
+ timeout = 60;
+ }
+ }
+
+retry:
+ if (nl_create_expect(tmp->h, exp, timeout) == -1) {
+ if (errno == EEXIST && retry == 1) {
+ ret = nl_destroy_expect(tmp->h, exp);
+ if (ret == 0 || (ret == -1 && errno == ENOENT)) {
+ if (retry) {
+ retry = 0;
+ goto retry;
+ }
+ }
+ dlog(LOG_ERR, "commit-destroy: %s", strerror(errno));
+ dlog_exp(STATE(log), exp, NFCT_O_PLAIN);
+ tmp->c->stats.commit_fail++;
+ } else {
+ dlog(LOG_ERR, "commit-create: %s", strerror(errno));
+ dlog_exp(STATE(log), exp, NFCT_O_PLAIN);
+ tmp->c->stats.commit_fail++;
+ }
+ } else {
+ tmp->c->stats.commit_ok++;
+ }
+ /* keep iterating even if we have found errors */
+ return 0;
+}
+
+static int
+cache_exp_commit(struct cache *c, struct nfct_handle *h, int clientfd)
+{
+ unsigned int commit_ok, commit_fail;
+ struct timeval commit_stop, res;
+ struct __commit_container tmp = {
+ .h = h,
+ .c = c,
+ };
+
+ /* we already have one commit in progress, skip this. The clientfd
+ * descriptor has to be closed by the caller. */
+ if (clientfd && STATE_SYNC(commit).clientfd != -1)
+ return -1;
+
+ switch(STATE_SYNC(commit).state) {
+ case COMMIT_STATE_INACTIVE:
+ gettimeofday(&STATE_SYNC(commit).stats.start, NULL);
+ STATE_SYNC(commit).stats.ok = c->stats.commit_ok;
+ STATE_SYNC(commit).stats.fail = c->stats.commit_fail;
+ STATE_SYNC(commit).clientfd = clientfd;
+ case COMMIT_STATE_MASTER:
+ STATE_SYNC(commit).current =
+ hashtable_iterate_limit(c->h, &tmp,
+ STATE_SYNC(commit).current,
+ CONFIG(general).commit_steps,
+ cache_exp_commit_step);
+ if (STATE_SYNC(commit).current < CONFIG(hashsize)) {
+ STATE_SYNC(commit).state = COMMIT_STATE_MASTER;
+ /* give it another step as soon as possible */
+ write_evfd(STATE_SYNC(commit).evfd);
+ return 1;
+ }
+
+ /* calculate the time that commit has taken */
+ gettimeofday(&commit_stop, NULL);
+ timersub(&commit_stop, &STATE_SYNC(commit).stats.start, &res);
+
+ /* calculate new entries committed */
+ commit_ok = c->stats.commit_ok - STATE_SYNC(commit).stats.ok;
+ commit_fail =
+ c->stats.commit_fail - STATE_SYNC(commit).stats.fail;
+
+ /* log results */
+ dlog(LOG_NOTICE, "Committed %u new expectations", commit_ok);
+
+ if (commit_fail)
+ dlog(LOG_NOTICE, "%u expectations can't be "
+ "committed", commit_fail);
+
+ dlog(LOG_NOTICE, "commit has taken %lu.%06lu seconds",
+ res.tv_sec, res.tv_usec);
+
+ /* prepare the state machine for new commits */
+ STATE_SYNC(commit).current = 0;
+ STATE_SYNC(commit).state = COMMIT_STATE_INACTIVE;
+
+ return 0;
+ }
+ return 1;
+}
+
+static struct nethdr *
+cache_exp_build_msg(const struct cache_object *obj, int type)
+{
+ return BUILD_NETMSG_FROM_EXP(obj->ptr, type);
+}
+
+/* template to cache expectations coming from the kernel. */
+struct cache_ops cache_sync_internal_exp_ops = {
+ .hash = cache_exp_hash,
+ .cmp = cache_exp_cmp,
+ .alloc = cache_exp_alloc,
+ .free = cache_exp_free,
+ .copy = cache_exp_copy,
+ .dump_step = cache_exp_dump_step,
+ .commit = NULL,
+ .build_msg = cache_exp_build_msg,
+};
+
+/* template to cache expectations coming from the network. */
+struct cache_ops cache_sync_external_exp_ops = {
+ .hash = cache_exp_hash,
+ .cmp = cache_exp_cmp,
+ .alloc = cache_exp_alloc,
+ .free = cache_exp_free,
+ .copy = cache_exp_copy,
+ .dump_step = cache_exp_dump_step,
+ .commit = cache_exp_commit,
+ .build_msg = NULL,
+};
diff --git a/src/cache.c b/src/cache.c
index 74c5c4b..7c41e54 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -1,5 +1,6 @@
/*
- * (C) 2006-2009 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2011 by Vyatta Inc. <http://www.vyatta.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
@@ -28,80 +29,14 @@
#include <string.h>
#include <time.h>
-static uint32_t
-__hash4(const struct nf_conntrack *ct, const struct hashtable *table)
-{
- uint32_t a[4] = {
- [0] = nfct_get_attr_u32(ct, ATTR_IPV4_SRC),
- [1] = nfct_get_attr_u32(ct, ATTR_IPV4_DST),
- [2] = nfct_get_attr_u8(ct, ATTR_L3PROTO) << 16 |
- nfct_get_attr_u8(ct, ATTR_L4PROTO),
- [3] = nfct_get_attr_u16(ct, ATTR_PORT_SRC) << 16 |
- nfct_get_attr_u16(ct, ATTR_PORT_DST),
- };
-
- /*
- * Instead of returning hash % table->hashsize (implying a divide)
- * we return the high 32 bits of the (hash * table->hashsize) that will
- * give results between [0 and hashsize-1] and same hash distribution,
- * but using a multiply, less expensive than a divide. See:
- * http://www.mail-archive.com/netdev@vger.kernel.org/msg56623.html
- */
- return ((uint64_t)jhash2(a, 4, 0) * table->hashsize) >> 32;
-}
-
-static uint32_t
-__hash6(const struct nf_conntrack *ct, const struct hashtable *table)
-{
- uint32_t a[10];
-
- memcpy(&a[0], nfct_get_attr(ct, ATTR_IPV6_SRC), sizeof(uint32_t)*4);
- memcpy(&a[4], nfct_get_attr(ct, ATTR_IPV6_SRC), sizeof(uint32_t)*4);
- a[8] = nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO) << 16 |
- nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO);
- a[9] = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) << 16 |
- nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST);
-
- return ((uint64_t)jhash2(a, 10, 0) * table->hashsize) >> 32;
-}
-
-static uint32_t hash(const void *data, const struct hashtable *table)
-{
- int ret = 0;
- const struct nf_conntrack *ct = data;
-
- switch(nfct_get_attr_u8(ct, ATTR_L3PROTO)) {
- case AF_INET:
- ret = __hash4(ct, table);
- break;
- case AF_INET6:
- ret = __hash6(ct, table);
- break;
- default:
- dlog(LOG_ERR, "unknown layer 3 proto in hash");
- break;
- }
-
- return ret;
-}
-
-static int compare(const void *data1, const void *data2)
-{
- const struct cache_object *obj = data1;
- const struct nf_conntrack *ct = data2;
-
- return nfct_cmp(obj->ct, ct, NFCT_CMP_ORIG) &&
- nfct_get_attr_u32(obj->ct, ATTR_ID) ==
- nfct_get_attr_u32(ct, ATTR_ID);
-}
-
struct cache_feature *cache_feature[CACHE_MAX_FEATURE] = {
[TIMER_FEATURE] = &timer_feature,
};
-struct cache *cache_create(const char *name,
+struct cache *cache_create(const char *name, enum cache_type type,
unsigned int features,
- struct cache_extra *extra)
+ struct cache_extra *extra,
+ struct cache_ops *ops)
{
size_t size = sizeof(struct cache_object);
int i, j = 0;
@@ -110,12 +45,16 @@ struct cache *cache_create(const char *name,
unsigned int feature_offset[CACHE_MAX_FEATURE] = {};
unsigned int feature_type[CACHE_MAX_FEATURE] = {};
+ if (type == CACHE_T_NONE || type >= CACHE_T_MAX)
+ return NULL;
+
c = malloc(sizeof(struct cache));
if (!c)
return NULL;
memset(c, 0, sizeof(struct cache));
strcpy(c->name, name);
+ c->type = type;
for (i = 0; i < CACHE_MAX_FEATURE; i++) {
if ((1 << i) & features) {
@@ -150,11 +89,19 @@ struct cache *cache_create(const char *name,
}
memcpy(c->feature_offset, feature_offset, sizeof(unsigned int) * j);
+ if (!ops || !ops->hash || !ops->cmp ||
+ !ops->alloc || !ops->copy || !ops->free) {
+ free(c->feature_offset);
+ free(c->features);
+ free(c);
+ return NULL;
+ }
+ c->ops = ops;
+
c->h = hashtable_create(CONFIG(hashsize),
CONFIG(limit),
- hash,
- compare);
-
+ c->ops->hash,
+ c->ops->cmp);
if (!c->h) {
free(c->features);
free(c->feature_offset);
@@ -175,7 +122,7 @@ void cache_destroy(struct cache *c)
free(c);
}
-struct cache_object *cache_object_new(struct cache *c, struct nf_conntrack *ct)
+struct cache_object *cache_object_new(struct cache *c, void *ptr)
{
struct cache_object *obj;
@@ -187,13 +134,14 @@ struct cache_object *cache_object_new(struct cache *c, struct nf_conntrack *ct)
}
obj->cache = c;
- if ((obj->ct = nfct_new()) == NULL) {
+ obj->ptr = c->ops->alloc();
+ if (obj->ptr == NULL) {
free(obj);
errno = ENOMEM;
c->stats.add_fail_enomem++;
return NULL;
}
- memcpy(obj->ct, ct, nfct_sizeof(ct));
+ c->ops->copy(obj->ptr, ptr, NFCT_CP_OVERRIDE);
obj->status = C_OBJ_NONE;
c->stats.objects++;
@@ -203,7 +151,8 @@ struct cache_object *cache_object_new(struct cache *c, struct nf_conntrack *ct)
void cache_object_free(struct cache_object *obj)
{
obj->cache->stats.objects--;
- nfct_destroy(obj->ct);
+ obj->cache->ops->free(obj->ptr);
+
free(obj);
}
@@ -271,13 +220,12 @@ int cache_add(struct cache *c, struct cache_object *obj, int id)
return 0;
}
-void cache_update(struct cache *c, struct cache_object *obj, int id,
- struct nf_conntrack *ct)
+void cache_update(struct cache *c, struct cache_object *obj, int id, void *ptr)
{
char *data = obj->data;
unsigned int i;
- nfct_copy(obj->ct, ct, NFCT_CP_META);
+ c->ops->copy(obj->ptr, ptr, NFCT_CP_META);
for (i = 0; i < c->num_features; i++) {
c->features[i]->update(obj, data);
@@ -322,23 +270,22 @@ void cache_del(struct cache *c, struct cache_object *obj)
__del(c, obj);
}
-struct cache_object *
-cache_update_force(struct cache *c, struct nf_conntrack *ct)
+struct cache_object *cache_update_force(struct cache *c, void *ptr)
{
struct cache_object *obj;
int id;
- obj = cache_find(c, ct, &id);
+ obj = cache_find(c, ptr, &id);
if (obj) {
if (obj->status != C_OBJ_DEAD) {
- cache_update(c, obj, id, ct);
+ cache_update(c, obj, id, ptr);
return obj;
} else {
cache_del(c, obj);
cache_object_free(obj);
}
}
- obj = cache_object_new(c, ct);
+ obj = cache_object_new(c, ptr);
if (obj == NULL)
return NULL;
@@ -350,21 +297,15 @@ cache_update_force(struct cache *c, struct nf_conntrack *ct)
return obj;
}
-struct cache_object *
-cache_find(struct cache *c, struct nf_conntrack *ct, int *id)
+struct cache_object *cache_find(struct cache *c, void *ptr, int *id)
{
- *id = hashtable_hash(c->h, ct);
- return ((struct cache_object *) hashtable_find(c->h, ct, *id));
+ *id = hashtable_hash(c->h, ptr);
+ return ((struct cache_object *) hashtable_find(c->h, ptr, *id));
}
-struct cache_object *cache_data_get_object(struct cache *c, void *data)
+void *cache_get_extra(struct cache_object *obj)
{
- return (struct cache_object *)((char*)data - c->extra_offset);
-}
-
-void *cache_get_extra(struct cache *c, void *data)
-{
- return (char*)data + c->extra_offset;
+ return (char*)obj + obj->cache->extra_offset;
}
void cache_stats(const struct cache *c, int fd)
@@ -432,3 +373,33 @@ void cache_iterate_limit(struct cache *c, void *data,
{
hashtable_iterate_limit(c->h, data, from, steps, iterate);
}
+
+void cache_dump(struct cache *c, int fd, int type)
+{
+ struct __dump_container tmp = {
+ .fd = fd,
+ .type = type
+ };
+ hashtable_iterate(c->h, (void *) &tmp, c->ops->dump_step);
+}
+
+int cache_commit(struct cache *c, struct nfct_handle *h, int clientfd)
+{
+ return c->ops->commit(c, h, clientfd);
+}
+
+static int do_flush(void *data, void *n)
+{
+ struct cache *c = data;
+ struct cache_object *obj = n;
+
+ cache_del(c, obj);
+ cache_object_free(obj);
+ return 0;
+}
+
+void cache_flush(struct cache *c)
+{
+ hashtable_iterate(c->h, c, do_flush);
+ c->stats.flush++;
+}
diff --git a/src/conntrack.c b/src/conntrack.c
index b8806bd..f0c47db 100644
--- a/src/conntrack.c
+++ b/src/conntrack.c
@@ -58,10 +58,219 @@
#include <fcntl.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+struct u32_mask {
+ uint32_t value;
+ uint32_t mask;
+};
+
+/* These are the template objects that are used to send commands. */
+static struct {
+ struct nf_conntrack *ct;
+ struct nf_expect *exp;
+ /* Expectations require the expectation tuple and the mask. */
+ struct nf_conntrack *exptuple, *mask;
+
+ /* Allows filtering/setting specific bits in the ctmark */
+ struct u32_mask mark;
+} tmpl;
+
+static int alloc_tmpl_objects(void)
+{
+ tmpl.ct = nfct_new();
+ tmpl.exptuple = nfct_new();
+ tmpl.mask = nfct_new();
+ tmpl.exp = nfexp_new();
+
+ memset(&tmpl.mark, 0, sizeof(tmpl.mark));
+
+ return tmpl.ct != NULL && tmpl.exptuple != NULL &&
+ tmpl.mask != NULL && tmpl.exp != NULL;
+}
+
+static void free_tmpl_objects(void)
+{
+ if (tmpl.ct)
+ nfct_destroy(tmpl.ct);
+ if (tmpl.exptuple)
+ nfct_destroy(tmpl.exptuple);
+ if (tmpl.mask)
+ nfct_destroy(tmpl.mask);
+ if (tmpl.exp)
+ nfexp_destroy(tmpl.exp);
+}
+
+enum ct_command {
+ CT_NONE = 0,
+
+ CT_LIST_BIT = 0,
+ CT_LIST = (1 << CT_LIST_BIT),
+
+ CT_CREATE_BIT = 1,
+ CT_CREATE = (1 << CT_CREATE_BIT),
+
+ CT_UPDATE_BIT = 2,
+ CT_UPDATE = (1 << CT_UPDATE_BIT),
+
+ CT_DELETE_BIT = 3,
+ CT_DELETE = (1 << CT_DELETE_BIT),
+
+ CT_GET_BIT = 4,
+ CT_GET = (1 << CT_GET_BIT),
+
+ CT_FLUSH_BIT = 5,
+ CT_FLUSH = (1 << CT_FLUSH_BIT),
+
+ CT_EVENT_BIT = 6,
+ CT_EVENT = (1 << CT_EVENT_BIT),
+
+ CT_VERSION_BIT = 7,
+ CT_VERSION = (1 << CT_VERSION_BIT),
+
+ CT_HELP_BIT = 8,
+ CT_HELP = (1 << CT_HELP_BIT),
+
+ EXP_LIST_BIT = 9,
+ EXP_LIST = (1 << EXP_LIST_BIT),
+
+ EXP_CREATE_BIT = 10,
+ EXP_CREATE = (1 << EXP_CREATE_BIT),
+
+ EXP_DELETE_BIT = 11,
+ EXP_DELETE = (1 << EXP_DELETE_BIT),
+
+ EXP_GET_BIT = 12,
+ EXP_GET = (1 << EXP_GET_BIT),
+
+ EXP_FLUSH_BIT = 13,
+ EXP_FLUSH = (1 << EXP_FLUSH_BIT),
+
+ EXP_EVENT_BIT = 14,
+ EXP_EVENT = (1 << EXP_EVENT_BIT),
+
+ CT_COUNT_BIT = 15,
+ CT_COUNT = (1 << CT_COUNT_BIT),
+
+ EXP_COUNT_BIT = 16,
+ EXP_COUNT = (1 << EXP_COUNT_BIT),
+
+ X_STATS_BIT = 17,
+ X_STATS = (1 << X_STATS_BIT),
+};
+/* If you add a new command, you have to update NUMBER_OF_CMD in conntrack.h */
+
+enum ct_options {
+ CT_OPT_ORIG_SRC_BIT = 0,
+ CT_OPT_ORIG_SRC = (1 << CT_OPT_ORIG_SRC_BIT),
+
+ CT_OPT_ORIG_DST_BIT = 1,
+ CT_OPT_ORIG_DST = (1 << CT_OPT_ORIG_DST_BIT),
+
+ CT_OPT_ORIG = (CT_OPT_ORIG_SRC | CT_OPT_ORIG_DST),
+
+ CT_OPT_REPL_SRC_BIT = 2,
+ CT_OPT_REPL_SRC = (1 << CT_OPT_REPL_SRC_BIT),
+
+ CT_OPT_REPL_DST_BIT = 3,
+ CT_OPT_REPL_DST = (1 << CT_OPT_REPL_DST_BIT),
+
+ CT_OPT_REPL = (CT_OPT_REPL_SRC | CT_OPT_REPL_DST),
+
+ CT_OPT_PROTO_BIT = 4,
+ CT_OPT_PROTO = (1 << CT_OPT_PROTO_BIT),
+
+ CT_OPT_TUPLE_ORIG = (CT_OPT_ORIG | CT_OPT_PROTO),
+ CT_OPT_TUPLE_REPL = (CT_OPT_REPL | CT_OPT_PROTO),
+
+ CT_OPT_TIMEOUT_BIT = 5,
+ CT_OPT_TIMEOUT = (1 << CT_OPT_TIMEOUT_BIT),
+
+ CT_OPT_STATUS_BIT = 6,
+ CT_OPT_STATUS = (1 << CT_OPT_STATUS_BIT),
+
+ CT_OPT_ZERO_BIT = 7,
+ CT_OPT_ZERO = (1 << CT_OPT_ZERO_BIT),
+
+ CT_OPT_EVENT_MASK_BIT = 8,
+ CT_OPT_EVENT_MASK = (1 << CT_OPT_EVENT_MASK_BIT),
+
+ CT_OPT_EXP_SRC_BIT = 9,
+ CT_OPT_EXP_SRC = (1 << CT_OPT_EXP_SRC_BIT),
+
+ CT_OPT_EXP_DST_BIT = 10,
+ CT_OPT_EXP_DST = (1 << CT_OPT_EXP_DST_BIT),
+
+ CT_OPT_MASK_SRC_BIT = 11,
+ CT_OPT_MASK_SRC = (1 << CT_OPT_MASK_SRC_BIT),
+
+ CT_OPT_MASK_DST_BIT = 12,
+ CT_OPT_MASK_DST = (1 << CT_OPT_MASK_DST_BIT),
+
+ CT_OPT_NATRANGE_BIT = 13,
+ CT_OPT_NATRANGE = (1 << CT_OPT_NATRANGE_BIT),
+
+ CT_OPT_MARK_BIT = 14,
+ CT_OPT_MARK = (1 << CT_OPT_MARK_BIT),
+
+ CT_OPT_ID_BIT = 15,
+ CT_OPT_ID = (1 << CT_OPT_ID_BIT),
+
+ CT_OPT_FAMILY_BIT = 16,
+ CT_OPT_FAMILY = (1 << CT_OPT_FAMILY_BIT),
+
+ CT_OPT_SRC_NAT_BIT = 17,
+ CT_OPT_SRC_NAT = (1 << CT_OPT_SRC_NAT_BIT),
+
+ CT_OPT_DST_NAT_BIT = 18,
+ CT_OPT_DST_NAT = (1 << CT_OPT_DST_NAT_BIT),
+
+ CT_OPT_OUTPUT_BIT = 19,
+ CT_OPT_OUTPUT = (1 << CT_OPT_OUTPUT_BIT),
+
+ CT_OPT_SECMARK_BIT = 20,
+ CT_OPT_SECMARK = (1 << CT_OPT_SECMARK_BIT),
+
+ CT_OPT_BUFFERSIZE_BIT = 21,
+ CT_OPT_BUFFERSIZE = (1 << CT_OPT_BUFFERSIZE_BIT),
+
+ CT_OPT_ANY_NAT_BIT = 22,
+ CT_OPT_ANY_NAT = (1 << CT_OPT_ANY_NAT_BIT),
+
+ CT_OPT_ZONE_BIT = 23,
+ CT_OPT_ZONE = (1 << CT_OPT_ZONE_BIT),
+};
+/* If you add a new option, you have to update NUMBER_OF_OPT in conntrack.h */
+
+/* Update this mask to allow to filter based on new options. */
+#define CT_COMPARISON (CT_OPT_PROTO | CT_OPT_ORIG | CT_OPT_REPL | \
+ CT_OPT_MARK | CT_OPT_SECMARK | CT_OPT_STATUS | \
+ CT_OPT_ID | CT_OPT_ZONE)
+
static const char *optflags[NUMBER_OF_OPT] = {
-"src","dst","reply-src","reply-dst","protonum","timeout","status","zero",
-"event-mask","tuple-src","tuple-dst","mask-src","mask-dst","nat-range","mark",
-"id","family","src-nat","dst-nat","output","secmark","buffersize"};
+ [CT_OPT_ORIG_SRC_BIT] = "src",
+ [CT_OPT_ORIG_DST_BIT] = "dst",
+ [CT_OPT_REPL_SRC_BIT] = "reply-src",
+ [CT_OPT_REPL_DST_BIT] = "reply-dst",
+ [CT_OPT_PROTO_BIT] = "protonum",
+ [CT_OPT_TIMEOUT_BIT] = "timeout",
+ [CT_OPT_STATUS_BIT] = "status",
+ [CT_OPT_ZERO_BIT] = "zero",
+ [CT_OPT_EVENT_MASK_BIT] = "event-mask",
+ [CT_OPT_EXP_SRC_BIT] = "tuple-src",
+ [CT_OPT_EXP_DST_BIT] = "tuple-dst",
+ [CT_OPT_MASK_SRC_BIT] = "mask-src",
+ [CT_OPT_MASK_DST_BIT] = "mask-dst",
+ [CT_OPT_NATRANGE_BIT] = "nat-range",
+ [CT_OPT_MARK_BIT] = "mark",
+ [CT_OPT_ID_BIT] = "id",
+ [CT_OPT_FAMILY_BIT] = "family",
+ [CT_OPT_SRC_NAT_BIT] = "src-nat",
+ [CT_OPT_DST_NAT_BIT] = "dst-nat",
+ [CT_OPT_OUTPUT_BIT] = "output",
+ [CT_OPT_SECMARK_BIT] = "secmark",
+ [CT_OPT_BUFFERSIZE_BIT] = "buffer-size",
+ [CT_OPT_ANY_NAT_BIT] = "any-nat",
+ [CT_OPT_ZONE_BIT] = "zone",
+};
static struct option original_opts[] = {
{"dump", 2, 0, 'L'},
@@ -99,14 +308,14 @@ static struct option original_opts[] = {
{"dst-nat", 2, 0, 'g'},
{"output", 1, 0, 'o'},
{"buffer-size", 1, 0, 'b'},
+ {"any-nat", 2, 0, 'j'},
+ {"zone", 1, 0, 'w'},
{0, 0, 0, 0}
};
-#define OPTION_OFFSET 256
-
-static struct nfct_handle *cth, *ith;
-static struct option *opts = original_opts;
-static unsigned int global_option_offset = 0;
+static const char *getopt_str = "L::I::U::D::G::E::F::hVs:d:r:q:"
+ "p:t:u:e:a:z[:]:{:}:m:i:f:o:n::"
+ "g::c:b:C::Sj::w:";
/* Table of legal combinations of commands and options. If any of the
* given commands make an option legal, that option is legal (applies to
@@ -121,27 +330,143 @@ static unsigned int global_option_offset = 0;
static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
/* Well, it's better than "Re: Linux vs FreeBSD" */
{
- /* s d r q p t u z e [ ] { } a m i f n g o c b*/
-/*CT_LIST*/ {2,2,2,2,2,0,2,2,0,0,0,0,0,0,2,0,2,2,2,2,2,0},
-/*CT_CREATE*/ {3,3,3,3,1,1,2,0,0,0,0,0,0,2,2,0,0,2,2,0,0,0},
-/*CT_UPDATE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0},
-/*CT_DELETE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0},
-/*CT_GET*/ {3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2,0,0},
-/*CT_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*CT_EVENT*/ {2,2,2,2,2,0,0,0,2,0,0,0,0,0,2,0,0,2,2,2,2,2},
-/*VERSION*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*HELP*/ {0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_LIST*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0},
-/*EXP_CREATE*/{1,1,2,2,1,1,2,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0},
-/*EXP_DELETE*/{1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_GET*/ {1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_EVENT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*CT_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*X_STATS*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ /* s d r q p t u z e [ ] { } a m i f n g o c b j w*/
+/*CT_LIST*/ {2,2,2,2,2,0,2,2,0,0,0,0,0,0,2,0,2,2,2,2,2,0,2,2},
+/*CT_CREATE*/ {3,3,3,3,1,1,2,0,0,0,0,0,0,2,2,0,0,2,2,0,0,0,0,2},
+/*CT_UPDATE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,0},
+/*CT_DELETE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,2},
+/*CT_GET*/ {3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0},
+/*CT_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*CT_EVENT*/ {2,2,2,2,2,0,0,0,2,0,0,0,0,0,2,0,0,2,2,2,2,2,2,2},
+/*VERSION*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*HELP*/ {0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*EXP_LIST*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0},
+/*EXP_CREATE*/{1,1,2,2,1,1,2,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0},
+/*EXP_DELETE*/{1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*EXP_GET*/ {1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*EXP_EVENT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*CT_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*EXP_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*X_STATS*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+};
+
+static const int cmd2type[][2] = {
+ ['L'] = { CT_LIST, EXP_LIST },
+ ['I'] = { CT_CREATE, EXP_CREATE },
+ ['D'] = { CT_DELETE, EXP_DELETE },
+ ['G'] = { CT_GET, EXP_GET },
+ ['F'] = { CT_FLUSH, EXP_FLUSH },
+ ['E'] = { CT_EVENT, EXP_EVENT },
+ ['V'] = { CT_VERSION, CT_VERSION },
+ ['h'] = { CT_HELP, CT_HELP },
+ ['C'] = { CT_COUNT, EXP_COUNT },
+};
+
+static const int opt2type[] = {
+ ['s'] = CT_OPT_ORIG_SRC,
+ ['d'] = CT_OPT_ORIG_DST,
+ ['r'] = CT_OPT_REPL_SRC,
+ ['q'] = CT_OPT_REPL_DST,
+ ['{'] = CT_OPT_MASK_SRC,
+ ['}'] = CT_OPT_MASK_DST,
+ ['['] = CT_OPT_EXP_SRC,
+ [']'] = CT_OPT_EXP_DST,
+ ['n'] = CT_OPT_SRC_NAT,
+ ['g'] = CT_OPT_DST_NAT,
+ ['m'] = CT_OPT_MARK,
+ ['c'] = CT_OPT_SECMARK,
+ ['i'] = CT_OPT_ID,
+ ['j'] = CT_OPT_ANY_NAT,
+ ['w'] = CT_OPT_ZONE,
+};
+
+static const int opt2family_attr[][2] = {
+ ['s'] = { ATTR_ORIG_IPV4_SRC, ATTR_ORIG_IPV6_SRC },
+ ['d'] = { ATTR_ORIG_IPV4_DST, ATTR_ORIG_IPV6_DST },
+ ['r'] = { ATTR_REPL_IPV4_SRC, ATTR_REPL_IPV6_SRC },
+ ['q'] = { ATTR_REPL_IPV4_DST, ATTR_REPL_IPV6_DST },
+ ['{'] = { ATTR_ORIG_IPV4_SRC, ATTR_ORIG_IPV6_SRC },
+ ['}'] = { ATTR_ORIG_IPV4_DST, ATTR_ORIG_IPV6_DST },
+ ['['] = { ATTR_ORIG_IPV4_SRC, ATTR_ORIG_IPV6_SRC },
+ [']'] = { ATTR_ORIG_IPV4_DST, ATTR_ORIG_IPV6_DST },
};
+static const int opt2attr[] = {
+ ['s'] = ATTR_ORIG_L3PROTO,
+ ['d'] = ATTR_ORIG_L3PROTO,
+ ['r'] = ATTR_REPL_L3PROTO,
+ ['q'] = ATTR_REPL_L3PROTO,
+ ['m'] = ATTR_MARK,
+ ['c'] = ATTR_SECMARK,
+ ['i'] = ATTR_ID,
+ ['w'] = ATTR_ZONE,
+};
+
+static char exit_msg[NUMBER_OF_CMD][64] = {
+ [CT_LIST_BIT] = "%d flow entries have been shown.\n",
+ [CT_CREATE_BIT] = "%d flow entries have been created.\n",
+ [CT_UPDATE_BIT] = "%d flow entries have been updated.\n",
+ [CT_DELETE_BIT] = "%d flow entries have been deleted.\n",
+ [CT_GET_BIT] = "%d flow entries have been shown.\n",
+ [CT_EVENT_BIT] = "%d flow events have been shown.\n",
+ [EXP_LIST_BIT] = "%d expectations have been shown.\n",
+ [EXP_DELETE_BIT] = "%d expectations have been shown.\n",
+};
+
+static const char usage_commands[] =
+ "Commands:\n"
+ " -L [table] [options]\t\tList conntrack or expectation table\n"
+ " -G [table] parameters\t\tGet conntrack or expectation\n"
+ " -D [table] parameters\t\tDelete conntrack or expectation\n"
+ " -I [table] parameters\t\tCreate a conntrack or expectation\n"
+ " -U [table] parameters\t\tUpdate a conntrack\n"
+ " -E [table] [options]\t\tShow events\n"
+ " -F [table]\t\t\tFlush table\n"
+ " -C [table]\t\t\tShow counter\n"
+ " -S\t\t\t\tShow statistics\n";
+
+static const char usage_tables[] =
+ "Tables: conntrack, expect\n";
+
+static const char usage_conntrack_parameters[] =
+ "Conntrack parameters and options:\n"
+ " -n, --src-nat ip\t\t\tsource NAT ip\n"
+ " -g, --dst-nat ip\t\t\tdestination NAT ip\n"
+ " -j, --any-nat ip\t\t\tsource or destination NAT ip\n"
+ " -m, --mark mark\t\t\tSet mark\n"
+ " -c, --secmark secmark\t\t\tSet selinux secmark\n"
+ " -e, --event-mask eventmask\t\tEvent mask, eg. NEW,DESTROY\n"
+ " -z, --zero \t\t\t\tZero counters while listing\n"
+ " -o, --output type[,...]\t\tOutput format, eg. xml\n";
+
+static const char usage_expectation_parameters[] =
+ "Expectation parameters and options:\n"
+ " --tuple-src ip\tSource address in expect tuple\n"
+ " --tuple-dst ip\tDestination address in expect tuple\n"
+ " --mask-src ip\t\tSource mask address\n"
+ " --mask-dst ip\t\tDestination mask address\n";
+
+static const char usage_parameters[] =
+ "Common parameters and options:\n"
+ " -s, --orig-src ip\t\tSource address from original direction\n"
+ " -d, --orig-dst ip\t\tDestination address from original direction\n"
+ " -r, --reply-src ip\t\tSource addres from reply direction\n"
+ " -q, --reply-dst ip\t\tDestination address from reply direction\n"
+ " -p, --protonum proto\t\tLayer 4 Protocol, eg. 'tcp'\n"
+ " -f, --family proto\t\tLayer 3 Protocol, eg. 'ipv6'\n"
+ " -t, --timeout timeout\t\tSet timeout\n"
+ " -u, --status status\t\tSet status, eg. ASSURED\n"
+ " -w, --zone value\t\tSet conntrack zone\n"
+ " -b, --buffer-size\t\tNetlink socket buffer size\n"
+ ;
+
+#define OPTION_OFFSET 256
+
+static struct nfct_handle *cth, *ith;
+static struct option *opts = original_opts;
+static unsigned int global_option_offset = 0;
+
#define ADDR_VALID_FLAGS_MAX 2
static unsigned int addr_valid_flags[ADDR_VALID_FLAGS_MAX] = {
CT_OPT_ORIG_SRC | CT_OPT_ORIG_DST,
@@ -152,9 +477,6 @@ static LIST_HEAD(proto_list);
static unsigned int options;
-#define CT_COMPARISON (CT_OPT_PROTO | CT_OPT_ORIG | CT_OPT_REPL | CT_OPT_MARK |\
- CT_OPT_SECMARK | CT_OPT_STATUS | CT_OPT_ID)
-
void register_proto(struct ctproto_handler *h)
{
if (strcmp(h->version, VERSION) != 0) {
@@ -348,11 +670,11 @@ merge_options(struct option *oldopts, const struct option *newopts,
/* Translates errno numbers into more human-readable form than strerror. */
static const char *
-err2str(int err, enum action command)
+err2str(int err, enum ct_command command)
{
unsigned int i;
struct table_struct {
- enum action act;
+ enum ct_command act;
int err;
const char *message;
} table [] =
@@ -378,11 +700,32 @@ err2str(int err, enum action command)
return strerror(err);
}
+static int mark_cmp(const struct u32_mask *m, const struct nf_conntrack *ct)
+{
+ return nfct_attr_is_set(ct, ATTR_MARK) &&
+ (nfct_get_attr_u32(ct, ATTR_MARK) & m->mask) == m->value;
+}
+
#define PARSE_STATUS 0
#define PARSE_EVENT 1
#define PARSE_OUTPUT 2
#define PARSE_MAX 3
+enum {
+ _O_XML = (1 << 0),
+ _O_EXT = (1 << 1),
+ _O_TMS = (1 << 2),
+ _O_ID = (1 << 3),
+ _O_KTMS = (1 << 4),
+};
+
+enum {
+ CT_EVENT_F_NEW = (1 << 0),
+ CT_EVENT_F_UPD = (1 << 1),
+ CT_EVENT_F_DEL = (1 << 2),
+ CT_EVENT_F_ALL = CT_EVENT_F_NEW | CT_EVENT_F_UPD | CT_EVENT_F_DEL,
+};
+
static struct parse_parameter {
const char *parameter[6];
size_t size;
@@ -391,10 +734,9 @@ static struct parse_parameter {
{ {"ASSURED", "SEEN_REPLY", "UNSET", "FIXED_TIMEOUT", "EXPECTED"}, 5,
{ IPS_ASSURED, IPS_SEEN_REPLY, 0, IPS_FIXED_TIMEOUT, IPS_EXPECTED} },
{ {"ALL", "NEW", "UPDATES", "DESTROY"}, 4,
- {~0U, NF_NETLINK_CONNTRACK_NEW, NF_NETLINK_CONNTRACK_UPDATE,
- NF_NETLINK_CONNTRACK_DESTROY} },
- { {"xml", "extended", "timestamp", "id" }, 4,
- { _O_XML, _O_EXT, _O_TMS, _O_ID },
+ { CT_EVENT_F_ALL, CT_EVENT_F_NEW, CT_EVENT_F_UPD, CT_EVENT_F_DEL } },
+ { {"xml", "extended", "timestamp", "id", "ktimestamp"}, 5,
+ { _O_XML, _O_EXT, _O_TMS, _O_ID, _O_KTMS },
},
};
@@ -446,6 +788,19 @@ parse_parameter(const char *arg, unsigned int *status, int parse_type)
}
static void
+parse_u32_mask(const char *arg, struct u32_mask *m)
+{
+ char *end;
+
+ m->value = (uint32_t) strtoul(arg, &end, 0);
+
+ if (*end == '/')
+ m->mask = (uint32_t) strtoul(end+1, NULL, 0);
+ else
+ m->mask = ~0;
+}
+
+static void
add_command(unsigned int *cmd, const int newcmd)
{
if (*cmd)
@@ -503,8 +858,7 @@ parse_inetaddr(const char *cp, struct addr_parse *parse)
else if (inet_pton(AF_INET6, cp, &parse->addr6) > 0)
return AF_INET6;
#endif
-
- exit_error(PARAMETER_PROBLEM, "Invalid IP address `%s'", cp);
+ return AF_UNSPEC;
}
union ct_address {
@@ -526,9 +880,8 @@ parse_addr(const char *cp, union ct_address *address)
return ret;
}
-/* Shamelessly stolen from libipt_DNAT ;). Ranges expected in network order. */
static void
-nat_parse(char *arg, int portok, struct nf_conntrack *obj, int type)
+nat_parse(char *arg, struct nf_conntrack *obj, int type)
{
char *colon, *error;
union ct_address parse;
@@ -538,14 +891,18 @@ nat_parse(char *arg, int portok, struct nf_conntrack *obj, int type)
if (colon) {
uint16_t port;
- if (!portok)
- exit_error(PARAMETER_PROBLEM,
- "Need TCP or UDP with port specification");
+ *colon = '\0';
port = (uint16_t)atoi(colon+1);
- if (port == 0)
- exit_error(PARAMETER_PROBLEM,
- "Port `%s' not valid", colon+1);
+ if (port == 0) {
+ if (strlen(colon+1) == 0) {
+ exit_error(PARAMETER_PROBLEM,
+ "No port specified after `:'");
+ } else {
+ exit_error(PARAMETER_PROBLEM,
+ "Port `%s' not valid", colon+1);
+ }
+ }
error = strchr(colon+1, ':');
if (error)
@@ -553,66 +910,30 @@ nat_parse(char *arg, int portok, struct nf_conntrack *obj, int type)
"Invalid port:port syntax");
if (type == CT_OPT_SRC_NAT)
- nfct_set_attr_u16(obj, ATTR_SNAT_PORT, port);
+ nfct_set_attr_u16(tmpl.ct, ATTR_SNAT_PORT, ntohs(port));
else if (type == CT_OPT_DST_NAT)
- nfct_set_attr_u16(obj, ATTR_DNAT_PORT, port);
+ nfct_set_attr_u16(tmpl.ct, ATTR_DNAT_PORT, ntohs(port));
+ else if (type == CT_OPT_ANY_NAT) {
+ nfct_set_attr_u16(tmpl.ct, ATTR_SNAT_PORT, ntohs(port));
+ nfct_set_attr_u16(tmpl.ct, ATTR_DNAT_PORT, ntohs(port));
+ }
}
- if (parse_addr(arg, &parse) != AF_INET)
- return;
+ if (parse_addr(arg, &parse) == AF_UNSPEC) {
+ if (strlen(arg) == 0) {
+ exit_error(PARAMETER_PROBLEM, "No IP specified");
+ } else {
+ exit_error(PARAMETER_PROBLEM,
+ "Invalid IP address `%s'", arg);
+ }
+ }
- if (type == CT_OPT_SRC_NAT)
- nfct_set_attr_u32(obj, ATTR_SNAT_IPV4, parse.v4);
- else if (type == CT_OPT_DST_NAT)
- nfct_set_attr_u32(obj, ATTR_DNAT_IPV4, parse.v4);
+ if (type == CT_OPT_SRC_NAT || type == CT_OPT_ANY_NAT)
+ nfct_set_attr_u32(tmpl.ct, ATTR_SNAT_IPV4, parse.v4);
+ else if (type == CT_OPT_DST_NAT || type == CT_OPT_ANY_NAT)
+ nfct_set_attr_u32(tmpl.ct, ATTR_DNAT_IPV4, parse.v4);
}
-static const char usage_commands[] =
- "Commands:\n"
- " -L [table] [options]\t\tList conntrack or expectation table\n"
- " -G [table] parameters\t\tGet conntrack or expectation\n"
- " -D [table] parameters\t\tDelete conntrack or expectation\n"
- " -I [table] parameters\t\tCreate a conntrack or expectation\n"
- " -U [table] parameters\t\tUpdate a conntrack\n"
- " -E [table] [options]\t\tShow events\n"
- " -F [table]\t\t\tFlush table\n"
- " -C [table]\t\t\tShow counter\n"
- " -S\t\t\t\tShow statistics\n";
-
-static const char usage_tables[] =
- "Tables: conntrack, expect\n";
-
-static const char usage_conntrack_parameters[] =
- "Conntrack parameters and options:\n"
- " -n, --src-nat ip\t\t\tsource NAT ip\n"
- " -g, --dst-nat ip\t\t\tdestination NAT ip\n"
- " -m, --mark mark\t\t\tSet mark\n"
- " -c, --secmark secmark\t\t\tSet selinux secmark\n"
- " -e, --event-mask eventmask\t\tEvent mask, eg. NEW,DESTROY\n"
- " -z, --zero \t\t\t\tZero counters while listing\n"
- " -o, --output type[,...]\t\tOutput format, eg. xml\n";
-
-static const char usage_expectation_parameters[] =
- "Expectation parameters and options:\n"
- " --tuple-src ip\tSource address in expect tuple\n"
- " --tuple-dst ip\tDestination address in expect tuple\n"
- " --mask-src ip\t\tSource mask address\n"
- " --mask-dst ip\t\tDestination mask address\n";
-
-static const char usage_parameters[] =
- "Common parameters and options:\n"
- " -s, --orig-src ip\t\tSource address from original direction\n"
- " -d, --orig-dst ip\t\tDestination address from original direction\n"
- " -r, --reply-src ip\t\tSource addres from reply direction\n"
- " -q, --reply-dst ip\t\tDestination address from reply direction\n"
- " -p, --protonum proto\t\tLayer 4 Protocol, eg. 'tcp'\n"
- " -f, --family proto\t\tLayer 3 Protocol, eg. 'ipv6'\n"
- " -t, --timeout timeout\t\tSet timeout\n"
- " -u, --status status\t\tSet status, eg. ASSURED\n"
- " -b, --buffer-size\t\tNetlink socket buffer size\n"
- ;
-
-
static void
usage(char *prog)
{
@@ -629,41 +950,97 @@ usage(char *prog)
static unsigned int output_mask;
+
+static int
+filter_mark(const struct nf_conntrack *ct)
+{
+ if ((options & CT_OPT_MARK) &&
+ !mark_cmp(&tmpl.mark, ct))
+ return 1;
+ return 0;
+}
+
+
static int
filter_nat(const struct nf_conntrack *obj, const struct nf_conntrack *ct)
{
+ int check_srcnat = options & CT_OPT_SRC_NAT ? 1 : 0;
+ int check_dstnat = options & CT_OPT_DST_NAT ? 1 : 0;
+ int has_srcnat = 0, has_dstnat = 0;
uint32_t ip;
+ uint16_t port;
- if ((options & CT_OPT_SRC_NAT) && (options & CT_OPT_DST_NAT)) {
- if (nfct_attr_is_set(obj, ATTR_SNAT_IPV4) &&
- nfct_attr_is_set(obj, ATTR_DNAT_IPV4)) {
- uint32_t ip2;
+ if (options & CT_OPT_ANY_NAT)
+ check_srcnat = check_dstnat = 1;
+
+ if (check_srcnat) {
+ int check_address = 0, check_port = 0;
- ip = nfct_get_attr_u32(obj, ATTR_SNAT_IPV4);
- ip2 = nfct_get_attr_u32(obj, ATTR_DNAT_IPV4);
- if (ip == nfct_get_attr_u32(ct, ATTR_REPL_IPV4_DST) &&
- ip2 == nfct_get_attr_u32(ct, ATTR_REPL_IPV4_SRC)) {
- return 0;
- }
- } else if (nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT) &&
- nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT)) {
- return 0;
- }
- } else if (options & CT_OPT_SRC_NAT) {
if (nfct_attr_is_set(obj, ATTR_SNAT_IPV4)) {
+ check_address = 1;
ip = nfct_get_attr_u32(obj, ATTR_SNAT_IPV4);
- if (ip == nfct_get_attr_u32(ct, ATTR_REPL_IPV4_DST))
- return 0;
- } else if (nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT))
- return 0;
- } else if (options & CT_OPT_DST_NAT) {
+ if (nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT) &&
+ ip == nfct_get_attr_u32(ct, ATTR_REPL_IPV4_DST))
+ has_srcnat = 1;
+ }
+ if (nfct_attr_is_set(obj, ATTR_SNAT_PORT)) {
+ int ret = 0;
+
+ check_port = 1;
+ port = nfct_get_attr_u16(obj, ATTR_SNAT_PORT);
+ if (nfct_getobjopt(ct, NFCT_GOPT_IS_SPAT) &&
+ port == nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST))
+ ret = 1;
+
+ /* the address matches but the port does not. */
+ if (check_address && has_srcnat && !ret)
+ has_srcnat = 0;
+ if (!check_address && ret)
+ has_srcnat = 1;
+ }
+ if (!check_address && !check_port &&
+ (nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT) ||
+ nfct_getobjopt(ct, NFCT_GOPT_IS_SPAT)))
+ has_srcnat = 1;
+ }
+ if (check_dstnat) {
+ int check_address = 0, check_port = 0;
+
if (nfct_attr_is_set(obj, ATTR_DNAT_IPV4)) {
+ check_address = 1;
ip = nfct_get_attr_u32(obj, ATTR_DNAT_IPV4);
- if (ip == nfct_get_attr_u32(ct, ATTR_REPL_IPV4_SRC))
- return 0;
- } else if (nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT))
- return 0;
+ if (nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT) &&
+ ip == nfct_get_attr_u32(ct, ATTR_REPL_IPV4_SRC))
+ has_dstnat = 1;
+ }
+ if (nfct_attr_is_set(obj, ATTR_DNAT_PORT)) {
+ int ret = 0;
+
+ check_port = 1;
+ port = nfct_get_attr_u16(obj, ATTR_DNAT_PORT);
+ if (nfct_getobjopt(ct, NFCT_GOPT_IS_DPAT) &&
+ port == nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC))
+ ret = 1;
+
+ /* the address matches but the port does not. */
+ if (check_address && has_dstnat && !ret)
+ has_dstnat = 0;
+ if (!check_address && ret)
+ has_dstnat = 1;
+ }
+ if (!check_address && !check_port &&
+ (nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT) ||
+ nfct_getobjopt(ct, NFCT_GOPT_IS_DPAT)))
+ has_dstnat = 1;
}
+ if (options & CT_OPT_ANY_NAT)
+ return !(has_srcnat || has_dstnat);
+ else if ((options & CT_OPT_SRC_NAT) && (options & CT_OPT_DST_NAT))
+ return !(has_srcnat && has_dstnat);
+ else if (options & CT_OPT_SRC_NAT)
+ return !has_srcnat;
+ else if (options & CT_OPT_DST_NAT)
+ return !has_dstnat;
return (options & (CT_OPT_SRC_NAT | CT_OPT_DST_NAT)) ? 1 : 0;
}
@@ -697,6 +1074,9 @@ static int event_cb(enum nf_conntrack_msg_type type,
if (filter_nat(obj, ct))
return NFCT_CB_CONTINUE;
+ if (filter_mark(ct))
+ return NFCT_CB_CONTINUE;
+
if (options & CT_COMPARISON &&
!nfct_cmp(obj, ct, NFCT_CMP_ALL | NFCT_CMP_MASK))
return NFCT_CB_CONTINUE;
@@ -719,6 +1099,8 @@ static int event_cb(enum nf_conntrack_msg_type type,
} else
op_flags |= NFCT_OF_TIME;
}
+ if (output_mask & _O_KTMS)
+ op_flags |= NFCT_OF_TIMESTAMP;
if (output_mask & _O_ID)
op_flags |= NFCT_OF_ID;
@@ -744,6 +1126,9 @@ static int dump_cb(enum nf_conntrack_msg_type type,
if (filter_nat(obj, ct))
return NFCT_CB_CONTINUE;
+ if (filter_mark(ct))
+ return NFCT_CB_CONTINUE;
+
if (options & CT_COMPARISON &&
!nfct_cmp(obj, ct, NFCT_CMP_ALL | NFCT_CMP_MASK))
return NFCT_CB_CONTINUE;
@@ -758,6 +1143,8 @@ static int dump_cb(enum nf_conntrack_msg_type type,
}
if (output_mask & _O_EXT)
op_flags = NFCT_OF_SHOW_LAYER3;
+ if (output_mask & _O_KTMS)
+ op_flags |= NFCT_OF_TIMESTAMP;
if (output_mask & _O_ID)
op_flags |= NFCT_OF_ID;
@@ -782,6 +1169,9 @@ static int delete_cb(enum nf_conntrack_msg_type type,
if (filter_nat(obj, ct))
return NFCT_CB_CONTINUE;
+ if (filter_mark(ct))
+ return NFCT_CB_CONTINUE;
+
if (options & CT_COMPARISON &&
!nfct_cmp(obj, ct, NFCT_CMP_ALL | NFCT_CMP_MASK))
return NFCT_CB_CONTINUE;
@@ -828,16 +1218,23 @@ static int print_cb(enum nf_conntrack_msg_type type,
return NFCT_CB_CONTINUE;
}
+static void copy_mark(struct nf_conntrack *tmp,
+ const struct nf_conntrack *ct,
+ const struct u32_mask *m)
+{
+ if (options & CT_OPT_MARK) {
+ uint32_t mark = nfct_get_attr_u32(ct, ATTR_MARK);
+ mark = (mark & ~m->mask) ^ m->value;
+ nfct_set_attr_u32(tmp, ATTR_MARK, mark);
+ }
+}
+
static int update_cb(enum nf_conntrack_msg_type type,
struct nf_conntrack *ct,
void *data)
{
int res;
- struct nf_conntrack *obj = data;
- char __tmp[nfct_maxsize()];
- struct nf_conntrack *tmp = (struct nf_conntrack *) (void *)__tmp;
-
- memset(tmp, 0, sizeof(__tmp));
+ struct nf_conntrack *obj = data, *tmp;
if (filter_nat(obj, ct))
return NFCT_CB_CONTINUE;
@@ -851,30 +1248,42 @@ static int update_cb(enum nf_conntrack_msg_type type,
if (options & CT_OPT_TUPLE_REPL && !nfct_cmp(obj, ct, NFCT_CMP_REPL))
return NFCT_CB_CONTINUE;
+ tmp = nfct_new();
+ if (tmp == NULL)
+ exit_error(OTHER_PROBLEM, "out of memory");
+
nfct_copy(tmp, ct, NFCT_CP_ORIG);
nfct_copy(tmp, obj, NFCT_CP_META);
+ copy_mark(tmp, ct, &tmpl.mark);
+
+ /* do not send NFCT_Q_UPDATE if ct appears unchanged */
+ if (nfct_cmp(tmp, ct, NFCT_CMP_ALL | NFCT_CMP_MASK)) {
+ nfct_destroy(tmp);
+ return NFCT_CB_CONTINUE;
+ }
res = nfct_query(ith, NFCT_Q_UPDATE, tmp);
- if (res < 0)
+ if (res < 0) {
+ nfct_destroy(tmp);
exit_error(OTHER_PROBLEM,
"Operation failed: %s",
err2str(errno, CT_UPDATE));
-
+ }
nfct_callback_register(ith, NFCT_T_ALL, print_cb, NULL);
res = nfct_query(ith, NFCT_Q_GET, tmp);
if (res < 0) {
+ nfct_destroy(tmp);
/* the entry has vanish in middle of the update */
if (errno == ENOENT) {
nfct_callback_unregister(ith);
return NFCT_CB_CONTINUE;
}
-
exit_error(OTHER_PROBLEM,
"Operation failed: %s",
err2str(errno, CT_UPDATE));
}
-
+ nfct_destroy(tmp);
nfct_callback_unregister(ith);
counter++;
@@ -895,6 +1304,18 @@ static int dump_exp_cb(enum nf_conntrack_msg_type type,
return NFCT_CB_CONTINUE;
}
+static int event_exp_cb(enum nf_conntrack_msg_type type,
+ struct nf_expect *exp, void *data)
+{
+ char buf[1024];
+
+ nfexp_snprintf(buf,sizeof(buf), exp, type, NFCT_O_DEFAULT, 0);
+ printf("%s\n", buf);
+ counter++;
+
+ return NFCT_CB_CONTINUE;
+}
+
static int count_exp_cb(enum nf_conntrack_msg_type type,
struct nf_expect *exp,
void *data)
@@ -936,10 +1357,9 @@ static int display_proc_conntrack_stats(void)
/* trim off trailing \n */
nl = strchr(buf, '\n');
- if (nl != NULL) {
+ if (nl != NULL)
*nl = '\0';
- nl = strchr(buf, '\n');
- }
+
token = strtok(buf, " ");
for (i=0; token != NULL && i<CT_STATS_ENTRIES_MAX; i++) {
strncpy(output[i], token, CT_STATS_STRING_MAX);
@@ -974,66 +1394,6 @@ out_err:
static struct ctproto_handler *h;
-static const int cmd2type[][2] = {
- ['L'] = { CT_LIST, EXP_LIST },
- ['I'] = { CT_CREATE, EXP_CREATE },
- ['D'] = { CT_DELETE, EXP_DELETE },
- ['G'] = { CT_GET, EXP_GET },
- ['F'] = { CT_FLUSH, EXP_FLUSH },
- ['E'] = { CT_EVENT, EXP_EVENT },
- ['V'] = { CT_VERSION, CT_VERSION },
- ['h'] = { CT_HELP, CT_HELP },
- ['C'] = { CT_COUNT, EXP_COUNT },
-};
-
-static const int opt2type[] = {
- ['s'] = CT_OPT_ORIG_SRC,
- ['d'] = CT_OPT_ORIG_DST,
- ['r'] = CT_OPT_REPL_SRC,
- ['q'] = CT_OPT_REPL_DST,
- ['{'] = CT_OPT_MASK_SRC,
- ['}'] = CT_OPT_MASK_DST,
- ['['] = CT_OPT_EXP_SRC,
- [']'] = CT_OPT_EXP_DST,
- ['n'] = CT_OPT_SRC_NAT,
- ['g'] = CT_OPT_DST_NAT,
- ['m'] = CT_OPT_MARK,
- ['c'] = CT_OPT_SECMARK,
- ['i'] = CT_OPT_ID,
-};
-
-static const int opt2family_attr[][2] = {
- ['s'] = { ATTR_ORIG_IPV4_SRC, ATTR_ORIG_IPV6_SRC },
- ['d'] = { ATTR_ORIG_IPV4_DST, ATTR_ORIG_IPV6_DST },
- ['r'] = { ATTR_REPL_IPV4_SRC, ATTR_REPL_IPV6_SRC },
- ['q'] = { ATTR_REPL_IPV4_DST, ATTR_REPL_IPV6_DST },
- ['{'] = { ATTR_ORIG_IPV4_SRC, ATTR_ORIG_IPV6_SRC },
- ['}'] = { ATTR_ORIG_IPV4_DST, ATTR_ORIG_IPV6_DST },
- ['['] = { ATTR_ORIG_IPV4_SRC, ATTR_ORIG_IPV6_SRC },
- [']'] = { ATTR_ORIG_IPV4_DST, ATTR_ORIG_IPV6_DST },
-};
-
-static const int opt2attr[] = {
- ['s'] = ATTR_ORIG_L3PROTO,
- ['d'] = ATTR_ORIG_L3PROTO,
- ['r'] = ATTR_REPL_L3PROTO,
- ['q'] = ATTR_REPL_L3PROTO,
- ['m'] = ATTR_MARK,
- ['c'] = ATTR_SECMARK,
- ['i'] = ATTR_ID,
-};
-
-static char exit_msg[NUMBER_OF_CMD][64] = {
- [CT_LIST_BIT] = "%d flow entries have been shown.\n",
- [CT_CREATE_BIT] = "%d flow entries have been created.\n",
- [CT_UPDATE_BIT] = "%d flow entries have been updated.\n",
- [CT_DELETE_BIT] = "%d flow entries have been deleted.\n",
- [CT_GET_BIT] = "%d flow entries have been shown.\n",
- [CT_EVENT_BIT] = "%d flow events have been shown.\n",
- [EXP_LIST_BIT] = "%d expectations have been shown.\n",
- [EXP_DELETE_BIT] = "%d expectations have been shown.\n",
-};
-
int main(int argc, char *argv[])
{
int c, cmd;
@@ -1041,23 +1401,13 @@ int main(int argc, char *argv[])
int res = 0, partial;
size_t socketbuffersize = 0;
int family = AF_UNSPEC;
- char __obj[nfct_maxsize()];
- char __exptuple[nfct_maxsize()];
- char __mask[nfct_maxsize()];
- struct nf_conntrack *obj = (struct nf_conntrack *)(void*) __obj;
- struct nf_conntrack *exptuple =
- (struct nf_conntrack *)(void*) __exptuple;
- struct nf_conntrack *mask = (struct nf_conntrack *)(void*) __mask;
- char __exp[nfexp_maxsize()];
- struct nf_expect *exp = (struct nf_expect *)(void*) __exp;
int l3protonum, protonum = 0;
union ct_address ad;
unsigned int command = 0;
- memset(__obj, 0, sizeof(__obj));
- memset(__exptuple, 0, sizeof(__exptuple));
- memset(__mask, 0, sizeof(__mask));
- memset(__exp, 0, sizeof(__exp));
+ /* we release these objects in the exit_error() path. */
+ if (!alloc_tmpl_objects())
+ exit_error(OTHER_PROBLEM, "out of memory");
register_tcp();
register_udp();
@@ -1072,10 +1422,7 @@ int main(int argc, char *argv[])
/* disable explicit missing arguments error output from getopt_long */
opterr = 0;
- while ((c = getopt_long(argc, argv, "L::I::U::D::G::E::F::hVs:d:r:q:"
- "p:t:u:e:a:z[:]:{:}:m:i:f:o:n::"
- "g::c:b:C::S",
- opts, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, getopt_str, opts, NULL)) != -1) {
switch(c) {
/* commands */
case 'L':
@@ -1109,17 +1456,21 @@ int main(int argc, char *argv[])
options |= opt2type[c];
l3protonum = parse_addr(optarg, &ad);
+ if (l3protonum == AF_UNSPEC) {
+ exit_error(PARAMETER_PROBLEM,
+ "Invalid IP address `%s'", optarg);
+ }
set_family(&family, l3protonum);
if (l3protonum == AF_INET) {
- nfct_set_attr_u32(obj,
+ nfct_set_attr_u32(tmpl.ct,
opt2family_attr[c][0],
ad.v4);
} else if (l3protonum == AF_INET6) {
- nfct_set_attr(obj,
+ nfct_set_attr(tmpl.ct,
opt2family_attr[c][1],
&ad.v6);
}
- nfct_set_attr_u8(obj, opt2attr[c], l3protonum);
+ nfct_set_attr_u8(tmpl.ct, opt2attr[c], l3protonum);
break;
case '{':
case '}':
@@ -1127,17 +1478,22 @@ int main(int argc, char *argv[])
case ']':
options |= opt2type[c];
l3protonum = parse_addr(optarg, &ad);
+ if (l3protonum == AF_UNSPEC) {
+ exit_error(PARAMETER_PROBLEM,
+ "Invalid IP address `%s'", optarg);
+ }
set_family(&family, l3protonum);
if (l3protonum == AF_INET) {
- nfct_set_attr_u32(mask,
+ nfct_set_attr_u32(tmpl.mask,
opt2family_attr[c][0],
ad.v4);
} else if (l3protonum == AF_INET6) {
- nfct_set_attr(mask,
+ nfct_set_attr(tmpl.mask,
opt2family_attr[c][1],
&ad.v6);
}
- nfct_set_attr_u8(mask, ATTR_ORIG_L3PROTO, l3protonum);
+ nfct_set_attr_u8(tmpl.mask,
+ ATTR_ORIG_L3PROTO, l3protonum);
break;
case 'p':
options |= CT_OPT_PROTO;
@@ -1151,17 +1507,18 @@ int main(int argc, char *argv[])
if (opts == NULL)
exit_error(OTHER_PROBLEM, "out of memory");
- nfct_set_attr_u8(obj, ATTR_L4PROTO, protonum);
+ nfct_set_attr_u8(tmpl.ct, ATTR_L4PROTO, protonum);
break;
case 't':
options |= CT_OPT_TIMEOUT;
- nfct_set_attr_u32(obj, ATTR_TIMEOUT, atol(optarg));
- nfexp_set_attr_u32(exp, ATTR_EXP_TIMEOUT, atol(optarg));
+ nfct_set_attr_u32(tmpl.ct, ATTR_TIMEOUT, atol(optarg));
+ nfexp_set_attr_u32(tmpl.exp,
+ ATTR_EXP_TIMEOUT, atol(optarg));
break;
case 'u':
options |= CT_OPT_STATUS;
parse_parameter(optarg, &status, PARSE_STATUS);
- nfct_set_attr_u32(obj, ATTR_STATUS, status);
+ nfct_set_attr_u32(tmpl.ct, ATTR_STATUS, status);
break;
case 'e':
options |= CT_OPT_EVENT_MASK;
@@ -1175,7 +1532,8 @@ int main(int argc, char *argv[])
options |= CT_OPT_ZERO;
break;
case 'n':
- case 'g': {
+ case 'g':
+ case 'j': {
char *tmp = NULL;
options |= opt2type[c];
@@ -1190,17 +1548,26 @@ int main(int argc, char *argv[])
continue;
set_family(&family, AF_INET);
- nat_parse(tmp, 1, obj, opt2type[c]);
+ nat_parse(tmp, tmpl.ct, opt2type[c]);
break;
}
+ case 'w':
+ options |= opt2type[c];
+ nfct_set_attr_u16(tmpl.ct,
+ opt2attr[c],
+ strtoul(optarg, NULL, 0));
+ break;
case 'i':
- case 'm':
case 'c':
options |= opt2type[c];
- nfct_set_attr_u32(obj,
+ nfct_set_attr_u32(tmpl.ct,
opt2attr[c],
strtoul(optarg, NULL, 0));
break;
+ case 'm':
+ options |= opt2type[c];
+ parse_u32_mask(optarg, &tmpl.mark);
+ break;
case 'a':
fprintf(stderr, "WARNING: ignoring -%c, "
"deprecated option.\n", c);
@@ -1232,8 +1599,9 @@ int main(int argc, char *argv[])
break;
default:
if (h && h->parse_opts
- &&!h->parse_opts(c - h->option_offset, obj,
- exptuple, mask, &l4flags))
+ &&!h->parse_opts(c - h->option_offset, tmpl.ct,
+ tmpl.exptuple, tmpl.mask,
+ &l4flags))
exit_error(PARAMETER_PROBLEM, "parse error");
break;
}
@@ -1243,6 +1611,12 @@ int main(int argc, char *argv[])
if (family == AF_UNSPEC)
family = AF_INET;
+ /* we cannot check this combination with generic_opt_check. */
+ if (options & CT_OPT_ANY_NAT &&
+ ((options & CT_OPT_SRC_NAT) || (options & CT_OPT_DST_NAT))) {
+ exit_error(PARAMETER_PROBLEM, "cannot specify `--src-nat' or "
+ "`--dst-nat' with `--any-nat'");
+ }
cmd = bit2cmd(command);
res = generic_opt_check(options, NUMBER_OF_OPT,
commands_v_options[cmd], optflags,
@@ -1263,7 +1637,7 @@ int main(int argc, char *argv[])
}
}
if (!(command & CT_HELP) && h && h->final_check)
- h->final_check(l4flags, cmd, obj);
+ h->final_check(l4flags, cmd, tmpl.ct);
switch(command) {
@@ -1277,7 +1651,7 @@ int main(int argc, char *argv[])
exit_error(PARAMETER_PROBLEM, "Can't use -z with "
"filtering parameters");
- nfct_callback_register(cth, NFCT_T_ALL, dump_cb, obj);
+ nfct_callback_register(cth, NFCT_T_ALL, dump_cb, tmpl.ct);
if (options & CT_OPT_ZERO)
res = nfct_query(cth, NFCT_Q_DUMP_RESET, &family);
@@ -1304,30 +1678,33 @@ int main(int argc, char *argv[])
case CT_CREATE:
if ((options & CT_OPT_ORIG) && !(options & CT_OPT_REPL))
- nfct_setobjopt(obj, NFCT_SOPT_SETUP_REPLY);
+ nfct_setobjopt(tmpl.ct, NFCT_SOPT_SETUP_REPLY);
else if (!(options & CT_OPT_ORIG) && (options & CT_OPT_REPL))
- nfct_setobjopt(obj, NFCT_SOPT_SETUP_ORIGINAL);
+ nfct_setobjopt(tmpl.ct, NFCT_SOPT_SETUP_ORIGINAL);
+
+ if (options & CT_OPT_MARK)
+ nfct_set_attr_u32(tmpl.ct, ATTR_MARK, tmpl.mark.value);
cth = nfct_open(CONNTRACK, 0);
if (!cth)
exit_error(OTHER_PROBLEM, "Can't open handler");
- res = nfct_query(cth, NFCT_Q_CREATE, obj);
+ res = nfct_query(cth, NFCT_Q_CREATE, tmpl.ct);
if (res != -1)
counter++;
nfct_close(cth);
break;
case EXP_CREATE:
- nfexp_set_attr(exp, ATTR_EXP_MASTER, obj);
- nfexp_set_attr(exp, ATTR_EXP_EXPECTED, exptuple);
- nfexp_set_attr(exp, ATTR_EXP_MASK, mask);
+ nfexp_set_attr(tmpl.exp, ATTR_EXP_MASTER, tmpl.ct);
+ nfexp_set_attr(tmpl.exp, ATTR_EXP_EXPECTED, tmpl.exptuple);
+ nfexp_set_attr(tmpl.exp, ATTR_EXP_MASK, tmpl.mask);
cth = nfct_open(EXPECT, 0);
if (!cth)
exit_error(OTHER_PROBLEM, "Can't open handler");
- res = nfexp_query(cth, NFCT_Q_CREATE, exp);
+ res = nfexp_query(cth, NFCT_Q_CREATE, tmpl.exp);
nfct_close(cth);
break;
@@ -1338,7 +1715,7 @@ int main(int argc, char *argv[])
if (!cth || !ith)
exit_error(OTHER_PROBLEM, "Can't open handler");
- nfct_callback_register(cth, NFCT_T_ALL, update_cb, obj);
+ nfct_callback_register(cth, NFCT_T_ALL, update_cb, tmpl.ct);
res = nfct_query(cth, NFCT_Q_DUMP, &family);
nfct_close(ith);
@@ -1351,7 +1728,7 @@ int main(int argc, char *argv[])
if (!cth || !ith)
exit_error(OTHER_PROBLEM, "Can't open handler");
- nfct_callback_register(cth, NFCT_T_ALL, delete_cb, obj);
+ nfct_callback_register(cth, NFCT_T_ALL, delete_cb, tmpl.ct);
res = nfct_query(cth, NFCT_Q_DUMP, &family);
nfct_close(ith);
@@ -1359,13 +1736,13 @@ int main(int argc, char *argv[])
break;
case EXP_DELETE:
- nfexp_set_attr(exp, ATTR_EXP_EXPECTED, obj);
+ nfexp_set_attr(tmpl.exp, ATTR_EXP_EXPECTED, tmpl.ct);
cth = nfct_open(EXPECT, 0);
if (!cth)
exit_error(OTHER_PROBLEM, "Can't open handler");
- res = nfexp_query(cth, NFCT_Q_DESTROY, exp);
+ res = nfexp_query(cth, NFCT_Q_DESTROY, tmpl.exp);
nfct_close(cth);
break;
@@ -1374,20 +1751,20 @@ int main(int argc, char *argv[])
if (!cth)
exit_error(OTHER_PROBLEM, "Can't open handler");
- nfct_callback_register(cth, NFCT_T_ALL, dump_cb, obj);
- res = nfct_query(cth, NFCT_Q_GET, obj);
+ nfct_callback_register(cth, NFCT_T_ALL, dump_cb, tmpl.ct);
+ res = nfct_query(cth, NFCT_Q_GET, tmpl.ct);
nfct_close(cth);
break;
case EXP_GET:
- nfexp_set_attr(exp, ATTR_EXP_MASTER, obj);
+ nfexp_set_attr(tmpl.exp, ATTR_EXP_MASTER, tmpl.ct);
cth = nfct_open(EXPECT, 0);
if (!cth)
exit_error(OTHER_PROBLEM, "Can't open handler");
nfexp_callback_register(cth, NFCT_T_ALL, dump_exp_cb, NULL);
- res = nfexp_query(cth, NFCT_Q_GET, exp);
+ res = nfexp_query(cth, NFCT_Q_GET, tmpl.exp);
nfct_close(cth);
break;
@@ -1407,14 +1784,28 @@ int main(int argc, char *argv[])
exit_error(OTHER_PROBLEM, "Can't open handler");
res = nfexp_query(cth, NFCT_Q_FLUSH, &family);
nfct_close(cth);
+ fprintf(stderr, "%s v%s (conntrack-tools): ",PROGNAME,VERSION);
+ fprintf(stderr,"expectation table has been emptied.\n");
break;
case CT_EVENT:
- if (options & CT_OPT_EVENT_MASK)
+ if (options & CT_OPT_EVENT_MASK) {
+ unsigned int nl_events = 0;
+
+ if (event_mask & CT_EVENT_F_NEW)
+ nl_events |= NF_NETLINK_CONNTRACK_NEW;
+ if (event_mask & CT_EVENT_F_UPD)
+ nl_events |= NF_NETLINK_CONNTRACK_UPDATE;
+ if (event_mask & CT_EVENT_F_DEL)
+ nl_events |= NF_NETLINK_CONNTRACK_DESTROY;
+
+ cth = nfct_open(CONNTRACK, nl_events);
+ } else {
cth = nfct_open(CONNTRACK,
- event_mask & NFCT_ALL_CT_GROUPS);
- else
- cth = nfct_open(CONNTRACK, NFCT_ALL_CT_GROUPS);
+ NF_NETLINK_CONNTRACK_NEW |
+ NF_NETLINK_CONNTRACK_UPDATE |
+ NF_NETLINK_CONNTRACK_DESTROY);
+ }
if (!cth)
exit_error(OTHER_PROBLEM, "Can't open handler");
@@ -1427,7 +1818,7 @@ int main(int argc, char *argv[])
}
signal(SIGINT, event_sighandler);
signal(SIGTERM, event_sighandler);
- nfct_callback_register(cth, NFCT_T_ALL, event_cb, obj);
+ nfct_callback_register(cth, NFCT_T_ALL, event_cb, tmpl.ct);
res = nfct_catch(cth);
if (res == -1) {
if (errno == ENOBUFS) {
@@ -1444,12 +1835,29 @@ int main(int argc, char *argv[])
break;
case EXP_EVENT:
- cth = nfct_open(EXPECT, NF_NETLINK_CONNTRACK_EXP_NEW);
+ if (options & CT_OPT_EVENT_MASK) {
+ unsigned int nl_events = 0;
+
+ if (event_mask & CT_EVENT_F_NEW)
+ nl_events |= NF_NETLINK_CONNTRACK_EXP_NEW;
+ if (event_mask & CT_EVENT_F_UPD)
+ nl_events |= NF_NETLINK_CONNTRACK_EXP_UPDATE;
+ if (event_mask & CT_EVENT_F_DEL)
+ nl_events |= NF_NETLINK_CONNTRACK_EXP_DESTROY;
+
+ cth = nfct_open(CONNTRACK, nl_events);
+ } else {
+ cth = nfct_open(EXPECT,
+ NF_NETLINK_CONNTRACK_EXP_NEW |
+ NF_NETLINK_CONNTRACK_EXP_UPDATE |
+ NF_NETLINK_CONNTRACK_EXP_DESTROY);
+ }
+
if (!cth)
exit_error(OTHER_PROBLEM, "Can't open handler");
signal(SIGINT, event_sighandler);
signal(SIGTERM, event_sighandler);
- nfexp_callback_register(cth, NFCT_T_ALL, dump_exp_cb, NULL);
+ nfexp_callback_register(cth, NFCT_T_ALL, event_exp_cb, NULL);
res = nfexp_catch(cth);
nfct_close(cth);
break;
@@ -1501,6 +1909,7 @@ int main(int argc, char *argv[])
exit_error(OTHER_PROBLEM, "Operation failed: %s",
err2str(errno, command));
+ free_tmpl_objects();
free_options();
if (command && exit_msg[cmd][0]) {
diff --git a/src/external_cache.c b/src/external_cache.c
index c70c818..e290249 100644
--- a/src/external_cache.c
+++ b/src/external_cache.c
@@ -1,6 +1,7 @@
/*
- * (C) 2006-2009 by Pablo Neira Ayuso <pablo@netfilter.org>
- *
+ * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2011 by Vyatta Inc. <http://www.vyatta.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
@@ -25,25 +26,35 @@
#include <stdlib.h>
static struct cache *external;
+static struct cache *external_exp;
static int external_cache_init(void)
{
- external = cache_create("external",
+ external = cache_create("external", CACHE_T_CT,
STATE_SYNC(sync)->external_cache_flags,
- NULL);
+ NULL, &cache_sync_external_ct_ops);
if (external == NULL) {
dlog(LOG_ERR, "can't allocate memory for the external cache");
return -1;
}
+ external_exp = cache_create("external", CACHE_T_EXP,
+ STATE_SYNC(sync)->external_cache_flags,
+ NULL, &cache_sync_external_exp_ops);
+ if (external_exp == NULL) {
+ dlog(LOG_ERR, "can't allocate memory for the external cache");
+ return -1;
+ }
+
return 0;
}
static void external_cache_close(void)
{
cache_destroy(external);
+ cache_destroy(external_exp);
}
-static void external_cache_new(struct nf_conntrack *ct)
+static void external_cache_ct_new(struct nf_conntrack *ct)
{
struct cache_object *obj;
int id;
@@ -66,12 +77,12 @@ retry:
}
}
-static void external_cache_upd(struct nf_conntrack *ct)
+static void external_cache_ct_upd(struct nf_conntrack *ct)
{
cache_update_force(external, ct);
}
-static void external_cache_del(struct nf_conntrack *ct)
+static void external_cache_ct_del(struct nf_conntrack *ct)
{
struct cache_object *obj;
int id;
@@ -83,40 +94,117 @@ static void external_cache_del(struct nf_conntrack *ct)
}
}
-static void external_cache_dump(int fd, int type)
+static void external_cache_ct_dump(int fd, int type)
{
cache_dump(external, fd, type);
}
-static void external_cache_commit(struct nfct_handle *h, int fd)
+static int external_cache_ct_commit(struct nfct_handle *h, int fd)
{
- cache_commit(external, h, fd);
+ return cache_commit(external, h, fd);
}
-static void external_cache_flush(void)
+static void external_cache_ct_flush(void)
{
cache_flush(external);
}
-static void external_cache_stats(int fd)
+static void external_cache_ct_stats(int fd)
{
cache_stats(external, fd);
}
-static void external_cache_stats_ext(int fd)
+static void external_cache_ct_stats_ext(int fd)
{
cache_stats_extended(external, fd);
}
+static void external_cache_exp_new(struct nf_expect *exp)
+{
+ struct cache_object *obj;
+ int id;
+
+ obj = cache_find(external_exp, exp, &id);
+ if (obj == NULL) {
+retry:
+ obj = cache_object_new(external_exp, exp);
+ if (obj == NULL)
+ return;
+
+ if (cache_add(external_exp, obj, id) == -1) {
+ cache_object_free(obj);
+ return;
+ }
+ } else {
+ cache_del(external_exp, obj);
+ cache_object_free(obj);
+ goto retry;
+ }
+}
+
+static void external_cache_exp_upd(struct nf_expect *exp)
+{
+ cache_update_force(external_exp, exp);
+}
+
+static void external_cache_exp_del(struct nf_expect *exp)
+{
+ struct cache_object *obj;
+ int id;
+
+ obj = cache_find(external_exp, exp, &id);
+ if (obj) {
+ cache_del(external_exp, obj);
+ cache_object_free(obj);
+ }
+}
+
+static void external_cache_exp_dump(int fd, int type)
+{
+ cache_dump(external_exp, fd, type);
+}
+
+static int external_cache_exp_commit(struct nfct_handle *h, int fd)
+{
+ return cache_commit(external_exp, h, fd);
+}
+
+static void external_cache_exp_flush(void)
+{
+ cache_flush(external_exp);
+}
+
+static void external_cache_exp_stats(int fd)
+{
+ cache_stats(external_exp, fd);
+}
+
+static void external_cache_exp_stats_ext(int fd)
+{
+ cache_stats_extended(external_exp, fd);
+}
+
struct external_handler external_cache = {
.init = external_cache_init,
.close = external_cache_close,
- .new = external_cache_new,
- .update = external_cache_upd,
- .destroy = external_cache_del,
- .dump = external_cache_dump,
- .commit = external_cache_commit,
- .flush = external_cache_flush,
- .stats = external_cache_stats,
- .stats_ext = external_cache_stats_ext,
+ .ct = {
+ .new = external_cache_ct_new,
+ .upd = external_cache_ct_upd,
+ .del = external_cache_ct_del,
+ .dump = external_cache_ct_dump,
+ .commit = external_cache_ct_commit,
+ .flush = external_cache_ct_flush,
+ .stats = external_cache_ct_stats,
+ .stats_ext = external_cache_ct_stats_ext,
+ },
+ .exp = {
+ .new = external_cache_exp_new,
+ .upd = external_cache_exp_upd,
+ .del = external_cache_exp_del,
+ .dump = external_cache_exp_dump,
+ .commit = external_cache_exp_commit,
+ .flush = external_cache_exp_flush,
+ .stats = external_cache_exp_stats,
+ .stats_ext = external_cache_exp_stats_ext,
+ },
};
diff --git a/src/external_inject.c b/src/external_inject.c
index a54bb13..0ad3478 100644
--- a/src/external_inject.c
+++ b/src/external_inject.c
@@ -1,6 +1,7 @@
/*
- * (C) 2009 by Pablo Neira Ayuso <pablo@netfilter.org>
- *
+ * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2011 by Vyatta Inc. <http://www.vyatta.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
@@ -41,7 +42,7 @@ struct {
static int external_inject_init(void)
{
/* handler to directly inject conntracks into kernel-space */
- inject = nfct_open(CONNTRACK, 0);
+ inject = nfct_open(CONFIG(netlink).subsys_id, 0);
if (inject == NULL) {
dlog(LOG_ERR, "can't open netlink handler: %s",
strerror(errno));
@@ -59,7 +60,7 @@ static void external_inject_close(void)
nfct_close(inject);
}
-static void external_inject_new(struct nf_conntrack *ct)
+static void external_inject_ct_new(struct nf_conntrack *ct)
{
int ret, retry = 1;
@@ -87,7 +88,7 @@ retry:
}
}
-static void external_inject_upd(struct nf_conntrack *ct)
+static void external_inject_ct_upd(struct nf_conntrack *ct)
{
int ret;
@@ -128,7 +129,7 @@ static void external_inject_upd(struct nf_conntrack *ct)
dlog_ct(STATE(log), ct, NFCT_O_PLAIN);
}
-static void external_inject_del(struct nf_conntrack *ct)
+static void external_inject_ct_del(struct nf_conntrack *ct)
{
if (nl_destroy_conntrack(inject, ct) == -1) {
if (errno != ENOENT) {
@@ -141,19 +142,21 @@ static void external_inject_del(struct nf_conntrack *ct)
}
}
-static void external_inject_dump(int fd, int type)
+static void external_inject_ct_dump(int fd, int type)
{
}
-static void external_inject_commit(struct nfct_handle *h, int fd)
+static int external_inject_ct_commit(struct nfct_handle *h, int fd)
{
+ /* close the commit socket. */
+ return LOCAL_RET_OK;
}
-static void external_inject_flush(void)
+static void external_inject_ct_flush(void)
{
}
-static void external_inject_stats(int fd)
+static void external_inject_ct_stats(int fd)
{
char buf[512];
int size;
@@ -172,15 +175,110 @@ static void external_inject_stats(int fd)
send(fd, buf, size, 0);
}
+struct {
+ uint32_t add_ok;
+ uint32_t add_fail;
+ uint32_t upd_ok;
+ uint32_t upd_fail;
+ uint32_t del_ok;
+ uint32_t del_fail;
+} exp_external_inject_stat;
+
+static void external_inject_exp_new(struct nf_expect *exp)
+{
+ int ret, retry = 1;
+
+retry:
+ if (nl_create_expect(inject, exp, 0) == -1) {
+ /* if the state entry exists, we delete and try again */
+ if (errno == EEXIST && retry == 1) {
+ ret = nl_destroy_expect(inject, exp);
+ if (ret == 0 || (ret == -1 && errno == ENOENT)) {
+ if (retry) {
+ retry = 0;
+ goto retry;
+ }
+ }
+ exp_external_inject_stat.add_fail++;
+ dlog(LOG_ERR, "inject-add1: %s", strerror(errno));
+ dlog_exp(STATE(log), exp, NFCT_O_PLAIN);
+ return;
+ }
+ exp_external_inject_stat.add_fail++;
+ dlog(LOG_ERR, "inject-add2: %s", strerror(errno));
+ dlog_exp(STATE(log), exp, NFCT_O_PLAIN);
+ } else {
+ exp_external_inject_stat.add_ok++;
+ }
+}
+
+static void external_inject_exp_del(struct nf_expect *exp)
+{
+ if (nl_destroy_expect(inject, exp) == -1) {
+ if (errno != ENOENT) {
+ exp_external_inject_stat.del_fail++;
+ dlog(LOG_ERR, "inject-del: %s", strerror(errno));
+ dlog_exp(STATE(log), exp, NFCT_O_PLAIN);
+ }
+ } else {
+ exp_external_inject_stat.del_ok++;
+ }
+}
+
+static void external_inject_exp_dump(int fd, int type)
+{
+}
+
+static int external_inject_exp_commit(struct nfct_handle *h, int fd)
+{
+ /* close the commit socket. */
+ return LOCAL_RET_OK;
+}
+
+static void external_inject_exp_flush(void)
+{
+}
+
+static void external_inject_exp_stats(int fd)
+{
+ char buf[512];
+ int size;
+
+ size = sprintf(buf, "external inject:\n"
+ "connections created:\t\t%12u\tfailed:\t%12u\n"
+ "connections updated:\t\t%12u\tfailed:\t%12u\n"
+ "connections destroyed:\t\t%12u\tfailed:\t%12u\n\n",
+ exp_external_inject_stat.add_ok,
+ exp_external_inject_stat.add_fail,
+ exp_external_inject_stat.upd_ok,
+ exp_external_inject_stat.upd_fail,
+ exp_external_inject_stat.del_ok,
+ exp_external_inject_stat.del_fail);
+
+ send(fd, buf, size, 0);
+}
+
struct external_handler external_inject = {
.init = external_inject_init,
.close = external_inject_close,
- .new = external_inject_new,
- .update = external_inject_upd,
- .destroy = external_inject_del,
- .dump = external_inject_dump,
- .commit = external_inject_commit,
- .flush = external_inject_flush,
- .stats = external_inject_stats,
- .stats_ext = external_inject_stats,
+ .ct = {
+ .new = external_inject_ct_new,
+ .upd = external_inject_ct_upd,
+ .del = external_inject_ct_del,
+ .dump = external_inject_ct_dump,
+ .commit = external_inject_ct_commit,
+ .flush = external_inject_ct_flush,
+ .stats = external_inject_ct_stats,
+ .stats_ext = external_inject_ct_stats,
+ },
+ .exp = {
+ .new = external_inject_exp_new,
+ .upd = external_inject_exp_new,
+ .del = external_inject_exp_del,
+ .dump = external_inject_exp_dump,
+ .commit = external_inject_exp_commit,
+ .flush = external_inject_exp_flush,
+ .stats = external_inject_exp_stats,
+ .stats_ext = external_inject_exp_stats,
+ },
};
diff --git a/src/filter.c b/src/filter.c
index 6a09c77..e8515d6 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -235,7 +235,7 @@ void ct_filter_add_state(struct ct_filter *f, int protonum, int val)
}
static inline int
-__ct_filter_test_ipv4(struct ct_filter *f, struct nf_conntrack *ct)
+__ct_filter_test_ipv4(struct ct_filter *f, const struct nf_conntrack *ct)
{
int id_src, id_dst;
uint32_t src, dst;
@@ -252,7 +252,7 @@ __ct_filter_test_ipv4(struct ct_filter *f, struct nf_conntrack *ct)
}
static inline int
-__ct_filter_test_ipv6(struct ct_filter *f, struct nf_conntrack *ct)
+__ct_filter_test_ipv6(struct ct_filter *f, const struct nf_conntrack *ct)
{
int id_src, id_dst;
const uint32_t *src, *dst;
@@ -295,7 +295,8 @@ __ct_filter_test_mask6(const void *ptr, const void *ct)
(elem->ip[3] & elem->mask[3]) == (dst[3] & elem->mask[3])));
}
-static int __ct_filter_test_state(struct ct_filter *f, struct nf_conntrack *ct)
+static int
+__ct_filter_test_state(struct ct_filter *f, const struct nf_conntrack *ct)
{
uint16_t val = 0;
uint8_t protonum = nfct_get_attr_u8(ct, ATTR_L4PROTO);
@@ -314,7 +315,8 @@ static int __ct_filter_test_state(struct ct_filter *f, struct nf_conntrack *ct)
return test_bit_u16(val, &f->statemap[protonum]);
}
-static int ct_filter_check(struct ct_filter *f, struct nf_conntrack *ct)
+static int
+ct_filter_check(struct ct_filter *f, const struct nf_conntrack *ct)
{
int ret, protonum = nfct_get_attr_u8(ct, ATTR_L4PROTO);
@@ -361,7 +363,7 @@ static int ct_filter_check(struct ct_filter *f, struct nf_conntrack *ct)
return 1;
}
-static inline int ct_filter_sanity_check(struct nf_conntrack *ct)
+static inline int ct_filter_sanity_check(const struct nf_conntrack *ct)
{
if (!nfct_attr_is_set(ct, ATTR_L3PROTO)) {
dlog(LOG_ERR, "missing layer 3 protocol");
@@ -371,9 +373,7 @@ static inline int ct_filter_sanity_check(struct nf_conntrack *ct)
switch(nfct_get_attr_u8(ct, ATTR_L3PROTO)) {
case AF_INET:
if (!nfct_attr_is_set(ct, ATTR_IPV4_SRC) ||
- !nfct_attr_is_set(ct, ATTR_IPV4_DST) ||
- !nfct_attr_is_set(ct, ATTR_REPL_IPV4_SRC) ||
- !nfct_attr_is_set(ct, ATTR_REPL_IPV4_DST)) {
+ !nfct_attr_is_set(ct, ATTR_IPV4_DST)) {
dlog(LOG_ERR, "missing IPv4 address. "
"You forgot to load "
"nf_conntrack_ipv4?");
@@ -382,9 +382,7 @@ static inline int ct_filter_sanity_check(struct nf_conntrack *ct)
break;
case AF_INET6:
if (!nfct_attr_is_set(ct, ATTR_IPV6_SRC) ||
- !nfct_attr_is_set(ct, ATTR_IPV6_DST) ||
- !nfct_attr_is_set(ct, ATTR_REPL_IPV6_SRC) ||
- !nfct_attr_is_set(ct, ATTR_REPL_IPV6_DST)) {
+ !nfct_attr_is_set(ct, ATTR_IPV6_DST)) {
dlog(LOG_ERR, "missing IPv6 address. "
"You forgot to load "
"nf_conntrack_ipv6?");
@@ -396,7 +394,7 @@ static inline int ct_filter_sanity_check(struct nf_conntrack *ct)
}
/* we do user-space filtering for dump and resyncs */
-int ct_filter_conntrack(struct nf_conntrack *ct, int userspace)
+int ct_filter_conntrack(const struct nf_conntrack *ct, int userspace)
{
/* missing mandatory attributes in object */
if (!ct_filter_sanity_check(ct))
@@ -407,3 +405,79 @@ int ct_filter_conntrack(struct nf_conntrack *ct, int userspace)
return 0;
}
+
+struct exp_filter {
+ struct list_head list;
+};
+
+struct exp_filter *exp_filter_create(void)
+{
+ struct exp_filter *f;
+
+ f = calloc(1, sizeof(struct exp_filter));
+ if (f == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&f->list);
+ return f;
+}
+
+struct exp_filter_item {
+ struct list_head head;
+ char helper_name[NFCT_HELPER_NAME_MAX];
+};
+
+/* this is ugly, but it simplifies read_config_yy.y */
+static struct exp_filter *exp_filter_alloc(void)
+{
+ if (STATE(exp_filter) == NULL) {
+ STATE(exp_filter) = exp_filter_create();
+ if (STATE(exp_filter) == NULL) {
+ fprintf(stderr, "Can't init expectation filtering!\n");
+ return NULL;
+ }
+ }
+ return STATE(exp_filter);;
+}
+
+int exp_filter_add(struct exp_filter *f, const char *helper_name)
+{
+ struct exp_filter_item *item;
+
+ f = exp_filter_alloc();
+ if (f == NULL)
+ return -1;
+
+ list_for_each_entry(item, &f->list, head) {
+ if (strncmp(item->helper_name, helper_name,
+ NFCT_HELPER_NAME_MAX) == 0) {
+ return -1;
+ }
+ }
+ item = calloc(1, sizeof(struct exp_filter_item));
+ if (item == NULL)
+ return -1;
+
+ strncpy(item->helper_name, helper_name, NFCT_HELPER_NAME_MAX);
+ list_add(&item->head, &f->list);
+ return 0;
+}
+
+int exp_filter_find(struct exp_filter *f, const struct nf_expect *exp)
+{
+ struct exp_filter_item *item;
+
+ if (f == NULL)
+ return 0;
+
+ list_for_each_entry(item, &f->list, head) {
+ const char *name = nfexp_get_attr(exp, ATTR_EXP_HELPER_NAME);
+
+ /* we allow partial matching to support things like sip-PORT. */
+ if (strncmp(item->helper_name, name,
+ strlen(item->helper_name)) == 0) {
+ return 1;
+ }
+ }
+ return 0;
+}
diff --git a/src/internal_bypass.c b/src/internal_bypass.c
index 4caaf4f..5c83c21 100644
--- a/src/internal_bypass.c
+++ b/src/internal_bypass.c
@@ -1,6 +1,7 @@
/*
- * (C) 2009 by Pablo Neira Ayuso <pablo@netfilter.org>
- *
+ * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2011 by Vyatta Inc. <http://www.vyatta.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
@@ -16,21 +17,25 @@
#include "network.h"
#include "origin.h"
-static int _init(void)
+static int internal_bypass_init(void)
{
return 0;
}
-static void _close(void)
+static void internal_bypass_close(void)
{
}
-static int dump_cb(enum nf_conntrack_msg_type type,
- struct nf_conntrack *ct, void *data)
+static int
+internal_bypass_ct_dump_cb(enum nf_conntrack_msg_type type,
+ struct nf_conntrack *ct, void *data)
{
char buf[1024];
int size, *fd = data;
+ if (ct_filter_conntrack(ct, 1))
+ return NFCT_CB_CONTINUE;
+
size = nfct_snprintf(buf, 1024, ct, NFCT_T_UNKNOWN, NFCT_O_DEFAULT, 0);
if (size < 1024) {
buf[size] = '\n';
@@ -41,18 +46,18 @@ static int dump_cb(enum nf_conntrack_msg_type type,
return NFCT_CB_CONTINUE;
}
-static void dump(int fd, int type)
+static void internal_bypass_ct_dump(int fd, int type)
{
struct nfct_handle *h;
u_int32_t family = AF_UNSPEC;
int ret;
- h = nfct_open(CONNTRACK, 0);
+ h = nfct_open(CONFIG(netlink).subsys_id, 0);
if (h == NULL) {
dlog(LOG_ERR, "can't allocate memory for the internal cache");
return;
}
- nfct_callback_register(h, NFCT_T_ALL, dump_cb, &fd);
+ nfct_callback_register(h, NFCT_T_ALL, internal_bypass_ct_dump_cb, &fd);
ret = nfct_query(h, NFCT_Q_DUMP, &family);
if (ret == -1) {
dlog(LOG_ERR, "can't dump kernel table");
@@ -60,7 +65,7 @@ static void dump(int fd, int type)
nfct_close(h);
}
-static void flush(void)
+static void internal_bypass_ct_flush(void)
{
nl_flush_conntrack_table(STATE(flush));
}
@@ -71,7 +76,7 @@ struct {
uint32_t del;
} internal_bypass_stats;
-static void stats(int fd)
+static void internal_bypass_ct_stats(int fd)
{
char buf[512];
int size;
@@ -88,25 +93,24 @@ static void stats(int fd)
}
/* unused, INTERNAL_F_POPULATE is unset. No cache, nothing to populate. */
-static void populate(struct nf_conntrack *ct)
+static void internal_bypass_ct_populate(struct nf_conntrack *ct)
{
}
/* unused, INTERNAL_F_RESYNC is unset. */
-static void purge(void)
+static void internal_bypass_ct_purge(void)
{
}
/* unused, INTERNAL_F_RESYNC is unset. Nothing to resync, we have no cache. */
-static int resync(enum nf_conntrack_msg_type type,
- struct nf_conntrack *ct,
- void *data)
+static int
+internal_bypass_ct_resync(enum nf_conntrack_msg_type type,
+ struct nf_conntrack *ct, void *data)
{
return NFCT_CB_CONTINUE;
}
-static void
-event_new_sync(struct nf_conntrack *ct, int origin)
+static void internal_bypass_ct_event_new(struct nf_conntrack *ct, int origin)
{
struct nethdr *net;
@@ -114,13 +118,12 @@ event_new_sync(struct nf_conntrack *ct, int origin)
if (origin != CTD_ORIGIN_NOT_ME)
return;
- net = BUILD_NETMSG(ct, NET_T_STATE_NEW);
+ net = BUILD_NETMSG_FROM_CT(ct, NET_T_STATE_CT_NEW);
multichannel_send(STATE_SYNC(channel), net);
internal_bypass_stats.new++;
}
-static void
-event_update_sync(struct nf_conntrack *ct, int origin)
+static void internal_bypass_ct_event_upd(struct nf_conntrack *ct, int origin)
{
struct nethdr *net;
@@ -128,13 +131,12 @@ event_update_sync(struct nf_conntrack *ct, int origin)
if (origin != CTD_ORIGIN_NOT_ME)
return;
- net = BUILD_NETMSG(ct, NET_T_STATE_UPD);
+ net = BUILD_NETMSG_FROM_CT(ct, NET_T_STATE_CT_UPD);
multichannel_send(STATE_SYNC(channel), net);
internal_bypass_stats.upd++;
}
-static int
-event_destroy_sync(struct nf_conntrack *ct, int origin)
+static int internal_bypass_ct_event_del(struct nf_conntrack *ct, int origin)
{
struct nethdr *net;
@@ -142,24 +144,170 @@ event_destroy_sync(struct nf_conntrack *ct, int origin)
if (origin != CTD_ORIGIN_NOT_ME)
return 1;
- net = BUILD_NETMSG(ct, NET_T_STATE_DEL);
+ net = BUILD_NETMSG_FROM_CT(ct, NET_T_STATE_CT_DEL);
multichannel_send(STATE_SYNC(channel), net);
internal_bypass_stats.del++;
return 1;
}
+static int
+internal_bypass_exp_dump_cb(enum nf_conntrack_msg_type type,
+ struct nf_expect *exp, void *data)
+{
+ char buf[1024];
+ int size, *fd = data;
+ const struct nf_conntrack *master =
+ nfexp_get_attr(exp, ATTR_EXP_MASTER);
+
+ if (!exp_filter_find(STATE(exp_filter), exp))
+ return NFCT_CB_CONTINUE;
+
+ if (ct_filter_conntrack(master, 1))
+ return NFCT_CB_CONTINUE;
+
+ size = nfexp_snprintf(buf, 1024, exp,
+ NFCT_T_UNKNOWN, NFCT_O_DEFAULT, 0);
+ if (size < 1024) {
+ buf[size] = '\n';
+ size++;
+ }
+ send(*fd, buf, size, 0);
+
+ return NFCT_CB_CONTINUE;
+}
+
+static void internal_bypass_exp_dump(int fd, int type)
+{
+ struct nfct_handle *h;
+ u_int32_t family = AF_UNSPEC;
+ int ret;
+
+ h = nfct_open(CONFIG(netlink).subsys_id, 0);
+ if (h == NULL) {
+ dlog(LOG_ERR, "can't allocate memory for the internal cache");
+ return;
+ }
+ nfexp_callback_register(h, NFCT_T_ALL,
+ internal_bypass_exp_dump_cb, &fd);
+ ret = nfexp_query(h, NFCT_Q_DUMP, &family);
+ if (ret == -1) {
+ dlog(LOG_ERR, "can't dump kernel table");
+ }
+ nfct_close(h);
+}
+
+static void internal_bypass_exp_flush(void)
+{
+ nl_flush_expect_table(STATE(flush));
+}
+
+struct {
+ uint32_t new;
+ uint32_t upd;
+ uint32_t del;
+} exp_internal_bypass_stats;
+
+static void internal_bypass_exp_stats(int fd)
+{
+ char buf[512];
+ int size;
+
+ size = sprintf(buf, "internal bypass:\n"
+ "connections new:\t\t%12u\n"
+ "connections updated:\t\t%12u\n"
+ "connections destroyed:\t\t%12u\n\n",
+ exp_internal_bypass_stats.new,
+ exp_internal_bypass_stats.upd,
+ exp_internal_bypass_stats.del);
+
+ send(fd, buf, size, 0);
+}
+
+/* unused, INTERNAL_F_POPULATE is unset. No cache, nothing to populate. */
+static void internal_bypass_exp_populate(struct nf_expect *exp)
+{
+}
+
+/* unused, INTERNAL_F_RESYNC is unset. */
+static void internal_bypass_exp_purge(void)
+{
+}
+
+/* unused, INTERNAL_F_RESYNC is unset. Nothing to resync, we have no cache. */
+static int
+internal_bypass_exp_resync(enum nf_conntrack_msg_type type,
+ struct nf_expect *exp, void *data)
+{
+ return NFCT_CB_CONTINUE;
+}
+
+static void internal_bypass_exp_event_new(struct nf_expect *exp, int origin)
+{
+ struct nethdr *net;
+
+ /* this event has been triggered by me, skip */
+ if (origin != CTD_ORIGIN_NOT_ME)
+ return;
+
+ net = BUILD_NETMSG_FROM_EXP(exp, NET_T_STATE_EXP_NEW);
+ multichannel_send(STATE_SYNC(channel), net);
+ exp_internal_bypass_stats.new++;
+}
+
+static void internal_bypass_exp_event_upd(struct nf_expect *exp, int origin)
+{
+ struct nethdr *net;
+
+ /* this event has been triggered by me, skip */
+ if (origin != CTD_ORIGIN_NOT_ME)
+ return;
+
+ net = BUILD_NETMSG_FROM_EXP(exp, NET_T_STATE_EXP_UPD);
+ multichannel_send(STATE_SYNC(channel), net);
+ exp_internal_bypass_stats.upd++;
+}
+
+static int internal_bypass_exp_event_del(struct nf_expect *exp, int origin)
+{
+ struct nethdr *net;
+
+ /* this event has been triggered by me, skip */
+ if (origin != CTD_ORIGIN_NOT_ME)
+ return 1;
+
+ net = BUILD_NETMSG_FROM_EXP(exp, NET_T_STATE_EXP_DEL);
+ multichannel_send(STATE_SYNC(channel), net);
+ exp_internal_bypass_stats.del++;
+
+ return 1;
+}
+
struct internal_handler internal_bypass = {
- .init = _init,
- .close = _close,
- .dump = dump,
- .flush = flush,
- .stats = stats,
- .stats_ext = stats,
- .populate = populate,
- .purge = purge,
- .resync = resync,
- .new = event_new_sync,
- .update = event_update_sync,
- .destroy = event_destroy_sync,
+ .init = internal_bypass_init,
+ .close = internal_bypass_close,
+ .ct = {
+ .dump = internal_bypass_ct_dump,
+ .flush = internal_bypass_ct_flush,
+ .stats = internal_bypass_ct_stats,
+ .stats_ext = internal_bypass_ct_stats,
+ .populate = internal_bypass_ct_populate,
+ .purge = internal_bypass_ct_purge,
+ .resync = internal_bypass_ct_resync,
+ .new = internal_bypass_ct_event_new,
+ .upd = internal_bypass_ct_event_upd,
+ .del = internal_bypass_ct_event_del,
+ },
+ .exp = {
+ .dump = internal_bypass_exp_dump,
+ .flush = internal_bypass_exp_flush,
+ .stats = internal_bypass_exp_stats,
+ .stats_ext = internal_bypass_exp_stats,
+ .populate = internal_bypass_exp_populate,
+ .purge = internal_bypass_exp_purge,
+ .resync = internal_bypass_exp_resync,
+ .new = internal_bypass_exp_event_new,
+ .upd = internal_bypass_exp_event_upd,
+ .del = internal_bypass_exp_event_del,
+ },
};
diff --git a/src/internal_cache.c b/src/internal_cache.c
index e50e1db..ba2d74b 100644
--- a/src/internal_cache.c
+++ b/src/internal_cache.c
@@ -1,6 +1,7 @@
/*
- * (C) 2009 by Pablo Neira Ayuso <pablo@netfilter.org>
- *
+ * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2011 by Vyatta Inc. <http://www.vyatta.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
@@ -19,46 +20,60 @@ static inline void sync_send(struct cache_object *obj, int query)
STATE_SYNC(sync)->enqueue(obj, query);
}
-static int _init(void)
+static int internal_cache_init(void)
{
- STATE(mode)->internal->data =
- cache_create("internal",
+ STATE(mode)->internal->ct.data =
+ cache_create("internal", CACHE_T_CT,
STATE_SYNC(sync)->internal_cache_flags,
- STATE_SYNC(sync)->internal_cache_extra);
+ STATE_SYNC(sync)->internal_cache_extra,
+ &cache_sync_internal_ct_ops);
- if (!STATE(mode)->internal->data) {
+ if (!STATE(mode)->internal->ct.data) {
dlog(LOG_ERR, "can't allocate memory for the internal cache");
return -1;
}
+
+ STATE(mode)->internal->exp.data =
+ cache_create("internal", CACHE_T_EXP,
+ STATE_SYNC(sync)->internal_cache_flags,
+ STATE_SYNC(sync)->internal_cache_extra,
+ &cache_sync_internal_exp_ops);
+
+ if (!STATE(mode)->internal->exp.data) {
+ dlog(LOG_ERR, "can't allocate memory for the internal cache");
+ return -1;
+ }
+
return 0;
}
-static void _close(void)
+static void internal_cache_close(void)
{
- cache_destroy(STATE(mode)->internal->data);
+ cache_destroy(STATE(mode)->internal->ct.data);
+ cache_destroy(STATE(mode)->internal->exp.data);
}
-static void dump(int fd, int type)
+static void internal_cache_ct_dump(int fd, int type)
{
- cache_dump(STATE(mode)->internal->data, fd, type);
+ cache_dump(STATE(mode)->internal->ct.data, fd, type);
}
-static void flush(void)
+static void internal_cache_ct_flush(void)
{
- cache_flush(STATE(mode)->internal->data);
+ cache_flush(STATE(mode)->internal->ct.data);
}
-static void stats(int fd)
+static void internal_cache_ct_stats(int fd)
{
- cache_stats(STATE(mode)->internal->data, fd);
+ cache_stats(STATE(mode)->internal->ct.data, fd);
}
-static void stats_ext(int fd)
+static void internal_cache_ct_stats_ext(int fd)
{
- cache_stats_extended(STATE(mode)->internal->data, fd);
+ cache_stats_extended(STATE(mode)->internal->ct.data, fd);
}
-static void populate(struct nf_conntrack *ct)
+static void internal_cache_ct_populate(struct nf_conntrack *ct)
{
/* This is required by kernels < 2.6.20 */
nfct_attr_unset(ct, ATTR_ORIG_COUNTER_BYTES);
@@ -67,19 +82,19 @@ static void populate(struct nf_conntrack *ct)
nfct_attr_unset(ct, ATTR_REPL_COUNTER_PACKETS);
nfct_attr_unset(ct, ATTR_USE);
- cache_update_force(STATE(mode)->internal->data, ct);
+ cache_update_force(STATE(mode)->internal->ct.data, ct);
}
-static int purge_step(void *data1, void *data2)
+static int internal_cache_ct_purge_step(void *data1, void *data2)
{
struct cache_object *obj = data2;
STATE(get_retval) = 0;
- nl_get_conntrack(STATE(get), obj->ct); /* modifies STATE(get_reval) */
+ nl_get_conntrack(STATE(get), obj->ptr); /* modifies STATE(get_reval) */
if (!STATE(get_retval)) {
if (obj->status != C_OBJ_DEAD) {
cache_object_set_status(obj, C_OBJ_DEAD);
- sync_send(obj, NET_T_STATE_DEL);
+ sync_send(obj, NET_T_STATE_CT_DEL);
cache_object_put(obj);
}
}
@@ -87,14 +102,15 @@ static int purge_step(void *data1, void *data2)
return 0;
}
-static void purge(void)
+static void internal_cache_ct_purge(void)
{
- cache_iterate(STATE(mode)->internal->data, NULL, purge_step);
+ cache_iterate(STATE(mode)->internal->ct.data, NULL,
+ internal_cache_ct_purge_step);
}
-static int resync(enum nf_conntrack_msg_type type,
- struct nf_conntrack *ct,
- void *data)
+static int
+internal_cache_ct_resync(enum nf_conntrack_msg_type type,
+ struct nf_conntrack *ct, void *data)
{
struct cache_object *obj;
@@ -108,23 +124,22 @@ static int resync(enum nf_conntrack_msg_type type,
nfct_attr_unset(ct, ATTR_REPL_COUNTER_PACKETS);
nfct_attr_unset(ct, ATTR_USE);
- obj = cache_update_force(STATE(mode)->internal->data, ct);
+ obj = cache_update_force(STATE(mode)->internal->ct.data, ct);
if (obj == NULL)
return NFCT_CB_CONTINUE;
switch (obj->status) {
case C_OBJ_NEW:
- sync_send(obj, NET_T_STATE_NEW);
+ sync_send(obj, NET_T_STATE_CT_NEW);
break;
case C_OBJ_ALIVE:
- sync_send(obj, NET_T_STATE_UPD);
+ sync_send(obj, NET_T_STATE_CT_UPD);
break;
}
return NFCT_CB_CONTINUE;
}
-static void
-event_new_sync(struct nf_conntrack *ct, int origin)
+static void internal_cache_ct_event_new(struct nf_conntrack *ct, int origin)
{
struct cache_object *obj;
int id;
@@ -139,13 +154,13 @@ event_new_sync(struct nf_conntrack *ct, int origin)
nfct_attr_unset(ct, ATTR_REPL_COUNTER_BYTES);
nfct_attr_unset(ct, ATTR_REPL_COUNTER_PACKETS);
- obj = cache_find(STATE(mode)->internal->data, ct, &id);
+ obj = cache_find(STATE(mode)->internal->ct.data, ct, &id);
if (obj == NULL) {
retry:
- obj = cache_object_new(STATE(mode)->internal->data, ct);
+ obj = cache_object_new(STATE(mode)->internal->ct.data, ct);
if (obj == NULL)
return;
- if (cache_add(STATE(mode)->internal->data, obj, id) == -1) {
+ if (cache_add(STATE(mode)->internal->ct.data, obj, id) == -1) {
cache_object_free(obj);
return;
}
@@ -153,16 +168,15 @@ retry:
* processes or the kernel, but don't propagate events that
* have been triggered by conntrackd itself, eg. commits. */
if (origin == CTD_ORIGIN_NOT_ME)
- sync_send(obj, NET_T_STATE_NEW);
+ sync_send(obj, NET_T_STATE_CT_NEW);
} else {
- cache_del(STATE(mode)->internal->data, obj);
+ cache_del(STATE(mode)->internal->ct.data, obj);
cache_object_free(obj);
goto retry;
}
}
-static void
-event_update_sync(struct nf_conntrack *ct, int origin)
+static void internal_cache_ct_event_upd(struct nf_conntrack *ct, int origin)
{
struct cache_object *obj;
@@ -170,16 +184,163 @@ event_update_sync(struct nf_conntrack *ct, int origin)
if (origin == CTD_ORIGIN_INJECT)
return;
- obj = cache_update_force(STATE(mode)->internal->data, ct);
+ obj = cache_update_force(STATE(mode)->internal->ct.data, ct);
if (obj == NULL)
return;
if (origin == CTD_ORIGIN_NOT_ME)
- sync_send(obj, NET_T_STATE_UPD);
+ sync_send(obj, NET_T_STATE_CT_UPD);
+}
+
+static int internal_cache_ct_event_del(struct nf_conntrack *ct, int origin)
+{
+ struct cache_object *obj;
+ int id;
+
+ /* this event has been triggered by a direct inject, skip */
+ if (origin == CTD_ORIGIN_INJECT)
+ return 0;
+
+ /* we don't synchronize events for objects that are not in the cache */
+ obj = cache_find(STATE(mode)->internal->ct.data, ct, &id);
+ if (obj == NULL)
+ return 0;
+
+ if (obj->status != C_OBJ_DEAD) {
+ cache_object_set_status(obj, C_OBJ_DEAD);
+ if (origin == CTD_ORIGIN_NOT_ME) {
+ sync_send(obj, NET_T_STATE_CT_DEL);
+ }
+ cache_object_put(obj);
+ }
+ return 1;
+}
+
+static void internal_cache_exp_dump(int fd, int type)
+{
+ cache_dump(STATE(mode)->internal->exp.data, fd, type);
+}
+
+static void internal_cache_exp_flush(void)
+{
+ cache_flush(STATE(mode)->internal->exp.data);
+}
+
+static void internal_cache_exp_stats(int fd)
+{
+ cache_stats(STATE(mode)->internal->exp.data, fd);
+}
+
+static void internal_cache_exp_stats_ext(int fd)
+{
+ cache_stats_extended(STATE(mode)->internal->exp.data, fd);
+}
+
+static void internal_cache_exp_populate(struct nf_expect *exp)
+{
+ cache_update_force(STATE(mode)->internal->exp.data, exp);
+}
+
+static int internal_cache_exp_purge_step(void *data1, void *data2)
+{
+ struct cache_object *obj = data2;
+
+ STATE(get_retval) = 0;
+ nl_get_expect(STATE(get), obj->ptr); /* modifies STATE(get_reval) */
+ if (!STATE(get_retval)) {
+ if (obj->status != C_OBJ_DEAD) {
+ cache_object_set_status(obj, C_OBJ_DEAD);
+ sync_send(obj, NET_T_STATE_EXP_DEL);
+ cache_object_put(obj);
+ }
+ }
+
+ return 0;
+}
+
+static void internal_cache_exp_purge(void)
+{
+ cache_iterate(STATE(mode)->internal->exp.data, NULL,
+ internal_cache_exp_purge_step);
}
static int
-event_destroy_sync(struct nf_conntrack *ct, int origin)
+internal_cache_exp_resync(enum nf_conntrack_msg_type type,
+ struct nf_expect *exp, void *data)
+{
+ struct cache_object *obj;
+ const struct nf_conntrack *master =
+ nfexp_get_attr(exp, ATTR_EXP_MASTER);
+
+ if (!exp_filter_find(STATE(exp_filter), exp))
+ return NFCT_CB_CONTINUE;
+
+ if (ct_filter_conntrack(master, 1))
+ return NFCT_CB_CONTINUE;
+
+ obj = cache_update_force(STATE(mode)->internal->exp.data, exp);
+ if (obj == NULL)
+ return NFCT_CB_CONTINUE;
+
+ switch (obj->status) {
+ case C_OBJ_NEW:
+ sync_send(obj, NET_T_STATE_EXP_NEW);
+ break;
+ case C_OBJ_ALIVE:
+ sync_send(obj, NET_T_STATE_EXP_UPD);
+ break;
+ }
+ return NFCT_CB_CONTINUE;
+}
+
+static void internal_cache_exp_event_new(struct nf_expect *exp, int origin)
+{
+ struct cache_object *obj;
+ int id;
+
+ /* this event has been triggered by a direct inject, skip */
+ if (origin == CTD_ORIGIN_INJECT)
+ return;
+
+ obj = cache_find(STATE(mode)->internal->exp.data, exp, &id);
+ if (obj == NULL) {
+retry:
+ obj = cache_object_new(STATE(mode)->internal->exp.data, exp);
+ if (obj == NULL)
+ return;
+ if (cache_add(STATE(mode)->internal->exp.data, obj, id) == -1) {
+ cache_object_free(obj);
+ return;
+ }
+ /* only synchronize events that have been triggered by other
+ * processes or the kernel, but don't propagate events that
+ * have been triggered by conntrackd itself, eg. commits. */
+ if (origin == CTD_ORIGIN_NOT_ME)
+ sync_send(obj, NET_T_STATE_EXP_NEW);
+ } else {
+ cache_del(STATE(mode)->internal->exp.data, obj);
+ cache_object_free(obj);
+ goto retry;
+ }
+}
+
+static void internal_cache_exp_event_upd(struct nf_expect *exp, int origin)
+{
+ struct cache_object *obj;
+
+ /* this event has been triggered by a direct inject, skip */
+ if (origin == CTD_ORIGIN_INJECT)
+ return;
+
+ obj = cache_update_force(STATE(mode)->internal->exp.data, exp);
+ if (obj == NULL)
+ return;
+
+ if (origin == CTD_ORIGIN_NOT_ME)
+ sync_send(obj, NET_T_STATE_EXP_UPD);
+}
+
+static int internal_cache_exp_event_del(struct nf_expect *exp, int origin)
{
struct cache_object *obj;
int id;
@@ -189,14 +350,14 @@ event_destroy_sync(struct nf_conntrack *ct, int origin)
return 0;
/* we don't synchronize events for objects that are not in the cache */
- obj = cache_find(STATE(mode)->internal->data, ct, &id);
+ obj = cache_find(STATE(mode)->internal->exp.data, exp, &id);
if (obj == NULL)
return 0;
if (obj->status != C_OBJ_DEAD) {
cache_object_set_status(obj, C_OBJ_DEAD);
if (origin == CTD_ORIGIN_NOT_ME) {
- sync_send(obj, NET_T_STATE_DEL);
+ sync_send(obj, NET_T_STATE_EXP_DEL);
}
cache_object_put(obj);
}
@@ -205,16 +366,30 @@ event_destroy_sync(struct nf_conntrack *ct, int origin)
struct internal_handler internal_cache = {
.flags = INTERNAL_F_POPULATE | INTERNAL_F_RESYNC,
- .init = _init,
- .close = _close,
- .dump = dump,
- .flush = flush,
- .stats = stats,
- .stats_ext = stats_ext,
- .populate = populate,
- .purge = purge,
- .resync = resync,
- .new = event_new_sync,
- .update = event_update_sync,
- .destroy = event_destroy_sync,
+ .init = internal_cache_init,
+ .close = internal_cache_close,
+ .ct = {
+ .dump = internal_cache_ct_dump,
+ .flush = internal_cache_ct_flush,
+ .stats = internal_cache_ct_stats,
+ .stats_ext = internal_cache_ct_stats_ext,
+ .populate = internal_cache_ct_populate,
+ .purge = internal_cache_ct_purge,
+ .resync = internal_cache_ct_resync,
+ .new = internal_cache_ct_event_new,
+ .upd = internal_cache_ct_event_upd,
+ .del = internal_cache_ct_event_del,
+ },
+ .exp = {
+ .dump = internal_cache_exp_dump,
+ .flush = internal_cache_exp_flush,
+ .stats = internal_cache_exp_stats,
+ .stats_ext = internal_cache_exp_stats_ext,
+ .populate = internal_cache_exp_populate,
+ .purge = internal_cache_exp_purge,
+ .resync = internal_cache_exp_resync,
+ .new = internal_cache_exp_event_new,
+ .upd = internal_cache_exp_event_upd,
+ .del = internal_cache_exp_event_del,
+ },
};
diff --git a/src/lock.c b/src/lock.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/lock.c
diff --git a/src/log.c b/src/log.c
index 9fe5119..d4de111 100644
--- a/src/log.c
+++ b/src/log.c
@@ -145,6 +145,43 @@ void dlog_ct(FILE *fd, struct nf_conntrack *ct, unsigned int type)
}
}
+void dlog_exp(FILE *fd, struct nf_expect *exp, unsigned int type)
+{
+ time_t t;
+ char buf[1024];
+ char *tmp;
+ unsigned int flags = 0;
+
+ buf[0]='\0';
+
+ switch(type) {
+ case NFCT_O_PLAIN:
+ t = time(NULL);
+ ctime_r(&t, buf);
+ tmp = buf + strlen(buf);
+ buf[strlen(buf)-1]='\t';
+ break;
+ default:
+ return;
+ }
+ nfexp_snprintf(buf+strlen(buf), 1024-strlen(buf), exp, 0, type, flags);
+
+ if (fd) {
+ snprintf(buf+strlen(buf), 1024-strlen(buf), "\n");
+ fputs(buf, fd);
+ }
+
+ if (fd == STATE(log)) {
+ /* error reporting */
+ if (CONFIG(syslog_facility) != -1)
+ syslog(LOG_ERR, "%s", tmp);
+ } else if (fd == STATE(stats_log)) {
+ /* connection logging */
+ if (CONFIG(stats).syslog_facility != -1)
+ syslog(LOG_INFO, "%s", tmp);
+ }
+}
+
void close_log(void)
{
if (STATE(log) != NULL)
diff --git a/src/main.c b/src/main.c
index 4ead2ea..342ed45 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,5 +1,6 @@
/*
- * (C) 2006-2007 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2011 by Vyatta Inc. <http://www.vyatta.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
@@ -38,14 +39,15 @@ static const char usage_daemon_commands[] =
static const char usage_client_commands[] =
"Client mode commands:\n"
- " -c, commit external cache to conntrack table\n"
+ " -c [ct|expect], commit external cache to conntrack table\n"
" -f [|internal|external], flush internal and external cache\n"
- " -F, flush kernel conntrack table\n"
- " -i, display content of the internal cache\n"
- " -e, display the content of the external cache\n"
+ " -F [ct|expect], flush kernel conntrack table\n"
+ " -i [ct|expect], display content of the internal cache\n"
+ " -e [ct|expect], display the content of the external cache\n"
" -k, kill conntrack daemon\n"
- " -s [|network|cache|runtime|link|rsqueue|queue], dump statistics\n"
- " -R, resync with kernel conntrack table\n"
+ " -s [|network|cache|runtime|link|rsqueue|queue|ct|expect], "
+ "dump statistics\n"
+ " -R [ct|expect], resync with kernel conntrack table\n"
" -n, request resync with other node (only FT-FW and NOTRACK modes)\n"
" -x, dump cache in XML format (requires -i or -e)\n"
" -t, reset the kernel timeout (see PurgeTimeout clause)\n"
@@ -89,6 +91,25 @@ set_operation_mode(int *current, int want, char *argv[])
}
}
+static int
+set_action_by_table(int i, int argc, char *argv[],
+ int ct_action, int exp_action, int dfl_action, int *action)
+{
+ if (i+1 < argc && argv[i+1][0] != '-') {
+ if (strncmp(argv[i+1], "ct", strlen(argv[i+1])) == 0) {
+ *action = ct_action;
+ i++;
+ } else if (strncmp(argv[i+1], "expect",
+ strlen(argv[i+1])) == 0) {
+ *action = exp_action;
+ i++;
+ }
+ } else
+ *action = dfl_action;
+
+ return i;
+}
+
int main(int argc, char *argv[])
{
int ret, i, action = -1;
@@ -115,15 +136,23 @@ int main(int argc, char *argv[])
break;
case 'c':
set_operation_mode(&type, REQUEST, argv);
- action = COMMIT;
+ i = set_action_by_table(i, argc, argv,
+ CT_COMMIT, EXP_COMMIT,
+ ALL_COMMIT, &action);
break;
case 'i':
set_operation_mode(&type, REQUEST, argv);
- action = DUMP_INTERNAL;
+ i = set_action_by_table(i, argc, argv,
+ CT_DUMP_INTERNAL,
+ EXP_DUMP_INTERNAL,
+ CT_DUMP_INTERNAL, &action);
break;
case 'e':
set_operation_mode(&type, REQUEST, argv);
- action = DUMP_EXTERNAL;
+ i = set_action_by_table(i, argc, argv,
+ CT_DUMP_EXTERNAL,
+ EXP_DUMP_EXTERNAL,
+ CT_DUMP_EXTERNAL, &action);
break;
case 'C':
if (++i < argc) {
@@ -142,18 +171,21 @@ int main(int argc, char *argv[])
break;
case 'F':
set_operation_mode(&type, REQUEST, argv);
- action = FLUSH_MASTER;
+ i = set_action_by_table(i, argc, argv,
+ CT_FLUSH_MASTER,
+ EXP_FLUSH_MASTER,
+ ALL_FLUSH_MASTER, &action);
break;
case 'f':
set_operation_mode(&type, REQUEST, argv);
if (i+1 < argc && argv[i+1][0] != '-') {
if (strncmp(argv[i+1], "internal",
strlen(argv[i+1])) == 0) {
- action = FLUSH_INT_CACHE;
+ action = CT_FLUSH_INT_CACHE;
i++;
} else if (strncmp(argv[i+1], "external",
strlen(argv[i+1])) == 0) {
- action = FLUSH_EXT_CACHE;
+ action = CT_FLUSH_EXT_CACHE;
i++;
} else {
fprintf(stderr, "ERROR: unknown "
@@ -164,12 +196,15 @@ int main(int argc, char *argv[])
}
} else {
/* default to general flushing */
- action = FLUSH_CACHE;
+ action = ALL_FLUSH_CACHE;
}
break;
case 'R':
set_operation_mode(&type, REQUEST, argv);
- action = RESYNC_MASTER;
+ i = set_action_by_table(i, argc, argv,
+ CT_RESYNC_MASTER,
+ EXP_RESYNC_MASTER,
+ ALL_RESYNC_MASTER, &action);
break;
case 'B':
set_operation_mode(&type, REQUEST, argv);
@@ -222,6 +257,14 @@ int main(int argc, char *argv[])
strlen(argv[i+1])) == 0) {
action = STATS_QUEUE;
i++;
+ } else if (strncmp(argv[i+1], "ct",
+ strlen(argv[i+1])) == 0) {
+ action = STATS;
+ i++;
+ } else if (strncmp(argv[i+1], "expect",
+ strlen(argv[i+1])) == 0) {
+ action = EXP_STATS;
+ i++;
} else {
fprintf(stderr, "ERROR: unknown "
"parameter `%s' for "
@@ -243,10 +286,10 @@ int main(int argc, char *argv[])
action = REQUEST_DUMP;
break;
case 'x':
- if (action == DUMP_INTERNAL)
- action = DUMP_INT_XML;
- else if (action == DUMP_EXTERNAL)
- action = DUMP_EXT_XML;
+ if (action == CT_DUMP_INTERNAL)
+ action = CT_DUMP_INT_XML;
+ else if (action == CT_DUMP_EXTERNAL)
+ action = CT_DUMP_EXT_XML;
else {
show_usage(argv[0]);
fprintf(stderr, "Error: Invalid parameters\n");
diff --git a/src/netlink.c b/src/netlink.c
index 079a5c7..fe979e3 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -1,5 +1,6 @@
/*
- * (C) 2006 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2011 by Vyatta Inc. <http://www.vyatta.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
@@ -32,10 +33,23 @@ struct nfct_handle *nl_init_event_handler(void)
{
struct nfct_handle *h;
- h = nfct_open(CONNTRACK, NFCT_ALL_CT_GROUPS);
+ h = nfct_open(CONFIG(netlink).subsys_id, CONFIG(netlink).groups);
if (h == NULL)
return NULL;
+ if (CONFIG(netlink).events_reliable) {
+ int on = 1;
+
+ setsockopt(nfct_fd(h), SOL_NETLINK,
+ NETLINK_BROADCAST_SEND_ERROR, &on, sizeof(int));
+
+ setsockopt(nfct_fd(h), SOL_NETLINK,
+ NETLINK_NO_ENOBUFS, &on, sizeof(int));
+
+ dlog(LOG_NOTICE, "reliable ctnetlink event delivery "
+ "is ENABLED.");
+ }
+
if (STATE(filter)) {
if (CONFIG(filter_from_kernelspace)) {
if (nfct_filter_attach(nfct_fd(h),
@@ -54,14 +68,16 @@ struct nfct_handle *nl_init_event_handler(void)
/* set up socket buffer size */
if (CONFIG(netlink_buffer_size) &&
- CONFIG(netlink_buffer_size) <= CONFIG(netlink_buffer_size_max_grown)) {
+ CONFIG(netlink_buffer_size) <=
+ CONFIG(netlink_buffer_size_max_grown)) {
/* we divide netlink_buffer_size by 2 here since value passed
to kernel gets doubled in SO_RCVBUF; see net/core/sock.c */
CONFIG(netlink_buffer_size) =
- nfnl_rcvbufsiz(nfct_nfnlh(h), CONFIG(netlink_buffer_size)/2);
+ nfnl_rcvbufsiz(nfct_nfnlh(h), CONFIG(netlink_buffer_size)/2);
} else {
- dlog(LOG_NOTICE, "NetlinkBufferSize is either not set or is greater "
- "than NetlinkBufferSizeMaxGrowth. Using current system buffer size");
+ dlog(LOG_NOTICE, "NetlinkBufferSize is either not set or "
+ "is greater than NetlinkBufferSizeMaxGrowth. "
+ "Using current system buffer size");
socklen_t socklen = sizeof(unsigned int);
unsigned int read_size;
@@ -76,18 +92,6 @@ struct nfct_handle *nl_init_event_handler(void)
dlog(LOG_NOTICE, "netlink event socket buffer size has been set "
"to %u bytes", CONFIG(netlink_buffer_size));
- if (CONFIG(netlink).events_reliable) {
- int on = 1;
-
- setsockopt(nfct_fd(h), SOL_NETLINK,
- NETLINK_BROADCAST_SEND_ERROR, &on, sizeof(int));
-
- setsockopt(nfct_fd(h), SOL_NETLINK,
- NETLINK_NO_ENOBUFS, &on, sizeof(int));
-
- dlog(LOG_NOTICE, "reliable ctnetlink event delivery "
- "is ENABLED.");
- }
return h;
}
@@ -161,20 +165,21 @@ int nl_send_resync(struct nfct_handle *h)
/* if the handle has no callback, check for existence, otherwise, update */
int nl_get_conntrack(struct nfct_handle *h, const struct nf_conntrack *ct)
{
- int ret;
- char __tmp[nfct_maxsize()];
- struct nf_conntrack *tmp = (struct nf_conntrack *) (void *)__tmp;
+ int ret = 1;
+ struct nf_conntrack *tmp;
- memset(__tmp, 0, sizeof(__tmp));
+ tmp = nfct_new();
+ if (tmp == NULL)
+ return -1;
/* use the original tuple to check if it is there */
nfct_copy(tmp, ct, NFCT_CP_ORIG);
- ret = nfct_query(h, NFCT_Q_GET, tmp);
- if (ret == -1)
- return errno == ENOENT ? 0 : -1;
+ if (nfct_query(h, NFCT_Q_GET, tmp) == -1)
+ ret = (errno == ENOENT) ? 0 : -1;
- return 1;
+ nfct_destroy(tmp);
+ return ret;
}
int nl_create_conntrack(struct nfct_handle *h,
@@ -200,12 +205,14 @@ int nl_create_conntrack(struct nfct_handle *h,
nfct_setobjopt(ct, NFCT_SOPT_SETUP_REPLY);
- /*
- * TCP flags to overpass window tracking for recovered connections
- */
+ /* disable TCP window tracking for recovered connections if required */
if (nfct_attr_is_set(ct, ATTR_TCP_STATE)) {
- uint8_t flags = IP_CT_TCP_FLAG_BE_LIBERAL |
- IP_CT_TCP_FLAG_SACK_PERM;
+ uint8_t flags = IP_CT_TCP_FLAG_SACK_PERM;
+
+ if (!CONFIG(sync).tcp_window_tracking)
+ flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
+ else
+ flags |= IP_CT_TCP_FLAG_WINDOW_SCALE;
/* FIXME: workaround, we should send TCP flags in updates */
if (nfct_get_attr_u8(ct, ATTR_TCP_STATE) >=
@@ -265,12 +272,14 @@ int nl_update_conntrack(struct nfct_handle *h,
nfct_attr_unset(ct, ATTR_MASTER_PORT_DST);
}
- /*
- * TCP flags to overpass window tracking for recovered connections
- */
+ /* disable TCP window tracking for recovered connections if required */
if (nfct_attr_is_set(ct, ATTR_TCP_STATE)) {
- uint8_t flags = IP_CT_TCP_FLAG_BE_LIBERAL |
- IP_CT_TCP_FLAG_SACK_PERM;
+ uint8_t flags = IP_CT_TCP_FLAG_SACK_PERM;
+
+ if (!CONFIG(sync).tcp_window_tracking)
+ flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
+ else
+ flags |= IP_CT_TCP_FLAG_WINDOW_SCALE;
/* FIXME: workaround, we should send TCP flags in updates */
if (nfct_get_attr_u8(ct, ATTR_TCP_STATE) >=
@@ -293,3 +302,61 @@ int nl_destroy_conntrack(struct nfct_handle *h, const struct nf_conntrack *ct)
{
return nfct_query(h, NFCT_Q_DESTROY, ct);
}
+
+int nl_create_expect(struct nfct_handle *h, const struct nf_expect *orig,
+ int timeout)
+{
+ int ret;
+ struct nf_expect *exp;
+
+ exp = nfexp_clone(orig);
+ if (exp == NULL)
+ return -1;
+
+ if (timeout > 0)
+ nfexp_set_attr_u32(exp, ATTR_EXP_TIMEOUT, timeout);
+
+ ret = nfexp_query(h, NFCT_Q_CREATE, exp);
+ nfexp_destroy(exp);
+
+ return ret;
+}
+
+int nl_destroy_expect(struct nfct_handle *h, const struct nf_expect *exp)
+{
+ return nfexp_query(h, NFCT_Q_DESTROY, exp);
+}
+
+/* if the handle has no callback, check for existence, otherwise, update */
+int nl_get_expect(struct nfct_handle *h, const struct nf_expect *exp)
+{
+ int ret = 1;
+ struct nf_expect *tmp;
+
+ /* XXX: we only need the expectation, not the mask and the master. */
+ tmp = nfexp_clone(exp);
+ if (tmp == NULL)
+ return -1;
+
+ if (nfexp_query(h, NFCT_Q_GET, tmp) == -1)
+ ret = (errno == ENOENT) ? 0 : -1;
+
+ nfexp_destroy(tmp);
+ return ret;
+}
+
+int nl_dump_expect_table(struct nfct_handle *h)
+{
+ return nfexp_query(h, NFCT_Q_DUMP, &CONFIG(family));
+}
+
+int nl_flush_expect_table(struct nfct_handle *h)
+{
+ return nfexp_query(h, NFCT_Q_FLUSH, &CONFIG(family));
+}
+
+int nl_send_expect_resync(struct nfct_handle *h)
+{
+ int family = CONFIG(family);
+ return nfexp_send(h, NFCT_Q_DUMP, &family);
+}
diff --git a/src/network.c b/src/network.c
index 6a66a2b..13db37c 100644
--- a/src/network.c
+++ b/src/network.c
@@ -1,6 +1,7 @@
/*
- * (C) 2006-2007 by Pablo Neira Ayuso <pablo@netfilter.org>
- *
+ * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2011 by Vyatta Inc. <http://www.vyatta.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
@@ -119,13 +120,20 @@ int nethdr_track_is_seq_set()
#include "cache.h"
-static int status2type[] = {
- [C_OBJ_NEW] = NET_T_STATE_NEW,
- [C_OBJ_ALIVE] = NET_T_STATE_UPD,
- [C_OBJ_DEAD] = NET_T_STATE_DEL,
+static int status2type[CACHE_T_MAX][C_OBJ_MAX] = {
+ [CACHE_T_CT] = {
+ [C_OBJ_NEW] = NET_T_STATE_CT_NEW,
+ [C_OBJ_ALIVE] = NET_T_STATE_CT_UPD,
+ [C_OBJ_DEAD] = NET_T_STATE_CT_DEL,
+ },
+ [CACHE_T_EXP] = {
+ [C_OBJ_NEW] = NET_T_STATE_EXP_NEW,
+ [C_OBJ_ALIVE] = NET_T_STATE_EXP_UPD,
+ [C_OBJ_DEAD] = NET_T_STATE_EXP_DEL,
+ },
};
-int object_status_to_network_type(int status)
+int object_status_to_network_type(struct cache_object *obj)
{
- return status2type[status];
+ return status2type[obj->cache->type][obj->status];
}
diff --git a/src/parse.c b/src/parse.c
index e6eefe4..81e9c6b 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -1,6 +1,7 @@
/*
- * (C) 2006-2007 by Pablo Neira Ayuso <pablo@netfilter.org>
- *
+ * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2011 by Vyatta Inc. <http://www.vyatta.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
@@ -24,191 +25,201 @@
#define ssizeof(x) (int)sizeof(x)
#endif
-static void parse_u8(struct nf_conntrack *ct, int attr, void *data);
-static void parse_u16(struct nf_conntrack *ct, int attr, void *data);
-static void parse_u32(struct nf_conntrack *ct, int attr, void *data);
-static void parse_group(struct nf_conntrack *ct, int attr, void *data);
-static void parse_nat_seq_adj(struct nf_conntrack *ct, int attr, void *data);
+static void ct_parse_u8(struct nf_conntrack *ct, int attr, void *data);
+static void ct_parse_u16(struct nf_conntrack *ct, int attr, void *data);
+static void ct_parse_u32(struct nf_conntrack *ct, int attr, void *data);
+static void ct_parse_group(struct nf_conntrack *ct, int attr, void *data);
+static void ct_parse_nat_seq_adj(struct nf_conntrack *ct, int attr, void *data);
-struct parser {
+struct ct_parser {
void (*parse)(struct nf_conntrack *ct, int attr, void *data);
int attr;
int size;
};
-static struct parser h[NTA_MAX] = {
+static struct ct_parser h[NTA_MAX] = {
[NTA_IPV4] = {
- .parse = parse_group,
+ .parse = ct_parse_group,
.attr = ATTR_GRP_ORIG_IPV4,
.size = NTA_SIZE(sizeof(struct nfct_attr_grp_ipv4)),
},
[NTA_IPV6] = {
- .parse = parse_group,
+ .parse = ct_parse_group,
.attr = ATTR_GRP_ORIG_IPV6,
.size = NTA_SIZE(sizeof(struct nfct_attr_grp_ipv6)),
},
[NTA_PORT] = {
- .parse = parse_group,
+ .parse = ct_parse_group,
.attr = ATTR_GRP_ORIG_PORT,
.size = NTA_SIZE(sizeof(struct nfct_attr_grp_port)),
},
[NTA_L4PROTO] = {
- .parse = parse_u8,
+ .parse = ct_parse_u8,
.attr = ATTR_L4PROTO,
.size = NTA_SIZE(sizeof(uint8_t)),
},
[NTA_TCP_STATE] = {
- .parse = parse_u8,
+ .parse = ct_parse_u8,
.attr = ATTR_TCP_STATE,
.size = NTA_SIZE(sizeof(uint8_t)),
},
[NTA_STATUS] = {
- .parse = parse_u32,
+ .parse = ct_parse_u32,
.attr = ATTR_STATUS,
.size = NTA_SIZE(sizeof(uint32_t)),
},
[NTA_MARK] = {
- .parse = parse_u32,
+ .parse = ct_parse_u32,
.attr = ATTR_MARK,
.size = NTA_SIZE(sizeof(uint32_t)),
},
[NTA_TIMEOUT] = {
- .parse = parse_u32,
+ .parse = ct_parse_u32,
.attr = ATTR_TIMEOUT,
.size = NTA_SIZE(sizeof(uint32_t)),
},
[NTA_MASTER_IPV4] = {
- .parse = parse_group,
+ .parse = ct_parse_group,
.attr = ATTR_GRP_MASTER_IPV4,
.size = NTA_SIZE(sizeof(struct nfct_attr_grp_ipv4)),
},
[NTA_MASTER_IPV6] = {
- .parse = parse_group,
+ .parse = ct_parse_group,
.attr = ATTR_GRP_MASTER_IPV6,
.size = NTA_SIZE(sizeof(struct nfct_attr_grp_ipv6)),
},
[NTA_MASTER_L4PROTO] = {
- .parse = parse_u8,
+ .parse = ct_parse_u8,
.attr = ATTR_MASTER_L4PROTO,
.size = NTA_SIZE(sizeof(uint8_t)),
},
[NTA_MASTER_PORT] = {
- .parse = parse_group,
+ .parse = ct_parse_group,
.attr = ATTR_GRP_MASTER_PORT,
.size = NTA_SIZE(sizeof(struct nfct_attr_grp_port)),
},
[NTA_SNAT_IPV4] = {
- .parse = parse_u32,
+ .parse = ct_parse_u32,
.attr = ATTR_SNAT_IPV4,
.size = NTA_SIZE(sizeof(uint32_t)),
},
[NTA_DNAT_IPV4] = {
- .parse = parse_u32,
+ .parse = ct_parse_u32,
.attr = ATTR_DNAT_IPV4,
.size = NTA_SIZE(sizeof(uint32_t)),
},
[NTA_SPAT_PORT] = {
- .parse = parse_u16,
+ .parse = ct_parse_u16,
.attr = ATTR_SNAT_PORT,
.size = NTA_SIZE(sizeof(uint16_t)),
},
[NTA_DPAT_PORT] = {
- .parse = parse_u16,
+ .parse = ct_parse_u16,
.attr = ATTR_DNAT_PORT,
.size = NTA_SIZE(sizeof(uint16_t)),
},
[NTA_NAT_SEQ_ADJ] = {
- .parse = parse_nat_seq_adj,
+ .parse = ct_parse_nat_seq_adj,
.size = NTA_SIZE(sizeof(struct nta_attr_natseqadj)),
},
[NTA_SCTP_STATE] = {
- .parse = parse_u8,
+ .parse = ct_parse_u8,
.attr = ATTR_SCTP_STATE,
.size = NTA_SIZE(sizeof(uint8_t)),
},
[NTA_SCTP_VTAG_ORIG] = {
- .parse = parse_u32,
+ .parse = ct_parse_u32,
.attr = ATTR_SCTP_VTAG_ORIG,
.size = NTA_SIZE(sizeof(uint32_t)),
},
[NTA_SCTP_VTAG_REPL] = {
- .parse = parse_u32,
+ .parse = ct_parse_u32,
.attr = ATTR_SCTP_VTAG_REPL,
.size = NTA_SIZE(sizeof(uint32_t)),
},
[NTA_DCCP_STATE] = {
- .parse = parse_u8,
+ .parse = ct_parse_u8,
.attr = ATTR_DCCP_STATE,
.size = NTA_SIZE(sizeof(uint8_t)),
},
[NTA_DCCP_ROLE] = {
- .parse = parse_u8,
+ .parse = ct_parse_u8,
.attr = ATTR_DCCP_ROLE,
.size = NTA_SIZE(sizeof(uint8_t)),
},
[NTA_ICMP_TYPE] = {
- .parse = parse_u8,
+ .parse = ct_parse_u8,
.attr = ATTR_ICMP_TYPE,
.size = NTA_SIZE(sizeof(uint8_t)),
},
[NTA_ICMP_CODE] = {
- .parse = parse_u8,
+ .parse = ct_parse_u8,
.attr = ATTR_ICMP_CODE,
.size = NTA_SIZE(sizeof(uint8_t)),
},
[NTA_ICMP_ID] = {
- .parse = parse_u16,
+ .parse = ct_parse_u16,
.attr = ATTR_ICMP_ID,
.size = NTA_SIZE(sizeof(uint16_t)),
},
+ [NTA_TCP_WSCALE_ORIG] = {
+ .parse = ct_parse_u8,
+ .attr = ATTR_TCP_WSCALE_ORIG,
+ .size = NTA_SIZE(sizeof(uint8_t)),
+ },
+ [NTA_TCP_WSCALE_REPL] = {
+ .parse = ct_parse_u8,
+ .attr = ATTR_TCP_WSCALE_REPL,
+ .size = NTA_SIZE(sizeof(uint8_t)),
+ },
};
static void
-parse_u8(struct nf_conntrack *ct, int attr, void *data)
+ct_parse_u8(struct nf_conntrack *ct, int attr, void *data)
{
uint8_t *value = (uint8_t *) data;
nfct_set_attr_u8(ct, h[attr].attr, *value);
}
static void
-parse_u16(struct nf_conntrack *ct, int attr, void *data)
+ct_parse_u16(struct nf_conntrack *ct, int attr, void *data)
{
uint16_t *value = (uint16_t *) data;
nfct_set_attr_u16(ct, h[attr].attr, ntohs(*value));
}
static void
-parse_u32(struct nf_conntrack *ct, int attr, void *data)
+ct_parse_u32(struct nf_conntrack *ct, int attr, void *data)
{
uint32_t *value = (uint32_t *) data;
nfct_set_attr_u32(ct, h[attr].attr, ntohl(*value));
}
static void
-parse_group(struct nf_conntrack *ct, int attr, void *data)
+ct_parse_group(struct nf_conntrack *ct, int attr, void *data)
{
nfct_set_attr_grp(ct, h[attr].attr, data);
}
static void
-parse_nat_seq_adj(struct nf_conntrack *ct, int attr, void *data)
+ct_parse_nat_seq_adj(struct nf_conntrack *ct, int attr, void *data)
{
struct nta_attr_natseqadj *this = data;
nfct_set_attr_u32(ct, ATTR_ORIG_NAT_SEQ_CORRECTION_POS,
ntohl(this->orig_seq_correction_pos));
nfct_set_attr_u32(ct, ATTR_ORIG_NAT_SEQ_OFFSET_BEFORE,
- ntohl(this->orig_seq_correction_pos));
+ ntohl(this->orig_seq_offset_before));
nfct_set_attr_u32(ct, ATTR_ORIG_NAT_SEQ_OFFSET_AFTER,
- ntohl(this->orig_seq_correction_pos));
+ ntohl(this->orig_seq_offset_after));
nfct_set_attr_u32(ct, ATTR_REPL_NAT_SEQ_CORRECTION_POS,
- ntohl(this->orig_seq_correction_pos));
+ ntohl(this->repl_seq_correction_pos));
nfct_set_attr_u32(ct, ATTR_REPL_NAT_SEQ_OFFSET_BEFORE,
- ntohl(this->orig_seq_correction_pos));
+ ntohl(this->repl_seq_offset_before));
nfct_set_attr_u32(ct, ATTR_REPL_NAT_SEQ_OFFSET_AFTER,
- ntohl(this->orig_seq_correction_pos));
+ ntohl(this->repl_seq_offset_after));
}
-int parse_payload(struct nf_conntrack *ct, struct nethdr *net, size_t remain)
+int msg2ct(struct nf_conntrack *ct, struct nethdr *net, size_t remain)
{
int len;
struct netattr *attr;
@@ -237,3 +248,195 @@ int parse_payload(struct nf_conntrack *ct, struct nethdr *net, size_t remain)
return 0;
}
+
+static void exp_parse_ct_group(void *ct, int attr, void *data);
+static void exp_parse_ct_u8(void *ct, int attr, void *data);
+static void exp_parse_u32(void *exp, int attr, void *data);
+
+static struct exp_parser {
+ void (*parse)(void *obj, int attr, void *data);
+ int exp_attr;
+ int ct_attr;
+ int size;
+} exp_h[NTA_EXP_MAX] = {
+ [NTA_EXP_MASTER_IPV4] = {
+ .parse = exp_parse_ct_group,
+ .exp_attr = ATTR_EXP_MASTER,
+ .ct_attr = ATTR_GRP_ORIG_IPV4,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_ipv4)),
+ },
+ [NTA_EXP_MASTER_IPV6] = {
+ .parse = exp_parse_ct_group,
+ .exp_attr = ATTR_EXP_MASTER,
+ .ct_attr = ATTR_GRP_ORIG_IPV6,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_ipv6)),
+ },
+ [NTA_EXP_MASTER_L4PROTO] = {
+ .parse = exp_parse_ct_u8,
+ .exp_attr = ATTR_EXP_MASTER,
+ .ct_attr = ATTR_L4PROTO,
+ .size = NTA_SIZE(sizeof(uint8_t)),
+ },
+ [NTA_EXP_MASTER_PORT] = {
+ .parse = exp_parse_ct_group,
+ .exp_attr = ATTR_EXP_MASTER,
+ .ct_attr = ATTR_GRP_ORIG_PORT,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_port)),
+ },
+ [NTA_EXP_EXPECT_IPV4] = {
+ .parse = exp_parse_ct_group,
+ .exp_attr = ATTR_EXP_EXPECTED,
+ .ct_attr = ATTR_GRP_ORIG_IPV4,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_ipv4)),
+ },
+ [NTA_EXP_EXPECT_IPV6] = {
+ .parse = exp_parse_ct_group,
+ .exp_attr = ATTR_EXP_EXPECTED,
+ .ct_attr = ATTR_GRP_ORIG_IPV6,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_ipv6)),
+ },
+ [NTA_EXP_EXPECT_L4PROTO] = {
+ .parse = exp_parse_ct_u8,
+ .exp_attr = ATTR_EXP_EXPECTED,
+ .ct_attr = ATTR_L4PROTO,
+ .size = NTA_SIZE(sizeof(uint8_t)),
+ },
+ [NTA_EXP_EXPECT_PORT] = {
+ .parse = exp_parse_ct_group,
+ .exp_attr = ATTR_EXP_EXPECTED,
+ .ct_attr = ATTR_GRP_ORIG_PORT,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_port)),
+ },
+ [NTA_EXP_MASK_IPV4] = {
+ .parse = exp_parse_ct_group,
+ .exp_attr = ATTR_EXP_MASK,
+ .ct_attr = ATTR_GRP_ORIG_IPV4,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_ipv4)),
+ },
+ [NTA_EXP_MASK_IPV6] = {
+ .parse = exp_parse_ct_group,
+ .exp_attr = ATTR_EXP_MASK,
+ .ct_attr = ATTR_GRP_ORIG_IPV6,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_ipv6)),
+ },
+ [NTA_EXP_MASK_L4PROTO] = {
+ .parse = exp_parse_ct_u8,
+ .exp_attr = ATTR_EXP_MASK,
+ .ct_attr = ATTR_L4PROTO,
+ .size = NTA_SIZE(sizeof(uint8_t)),
+ },
+ [NTA_EXP_MASK_PORT] = {
+ .parse = exp_parse_ct_group,
+ .exp_attr = ATTR_EXP_MASK,
+ .ct_attr = ATTR_GRP_ORIG_PORT,
+ .size = NTA_SIZE(sizeof(struct nfct_attr_grp_port)),
+ },
+ [NTA_EXP_TIMEOUT] = {
+ .parse = exp_parse_u32,
+ .exp_attr = ATTR_EXP_TIMEOUT,
+ .size = NTA_SIZE(sizeof(uint32_t)),
+ },
+ [NTA_EXP_FLAGS] = {
+ .parse = exp_parse_u32,
+ .exp_attr = ATTR_EXP_FLAGS,
+ .size = NTA_SIZE(sizeof(uint32_t)),
+ },
+};
+
+static void exp_parse_ct_group(void *ct, int attr, void *data)
+{
+ nfct_set_attr_grp(ct, exp_h[attr].ct_attr, data);
+}
+
+static void exp_parse_ct_u8(void *ct, int attr, void *data)
+{
+ uint8_t *value = (uint8_t *) data;
+ nfct_set_attr_u8(ct, exp_h[attr].ct_attr, *value);
+}
+
+static void exp_parse_u32(void *exp, int attr, void *data)
+{
+ uint32_t *value = (uint32_t *) data;
+ nfexp_set_attr_u32(exp, exp_h[attr].exp_attr, ntohl(*value));
+}
+
+int msg2exp(struct nf_expect *exp, struct nethdr *net, size_t remain)
+{
+ int len;
+ struct netattr *attr;
+ struct nf_conntrack *master, *expected, *mask;
+
+ if (remain < net->len)
+ return -1;
+
+ len = net->len - NETHDR_SIZ;
+ attr = NETHDR_DATA(net);
+
+ master = nfct_new();
+ if (master == NULL)
+ goto err_master;
+
+ expected = nfct_new();
+ if (expected == NULL)
+ goto err_expected;
+
+ mask = nfct_new();
+ if (mask == NULL)
+ goto err_mask;
+
+ while (len > ssizeof(struct netattr)) {
+ ATTR_NETWORK2HOST(attr);
+ if (attr->nta_len > len)
+ goto err;
+ if (attr->nta_attr > NTA_MAX)
+ goto err;
+ if (attr->nta_len != exp_h[attr->nta_attr].size)
+ goto err;
+ if (exp_h[attr->nta_attr].parse == NULL) {
+ attr = NTA_NEXT(attr, len);
+ continue;
+ }
+ switch(exp_h[attr->nta_attr].exp_attr) {
+ case ATTR_EXP_MASTER:
+ exp_h[attr->nta_attr].parse(master, attr->nta_attr,
+ NTA_DATA(attr));
+ case ATTR_EXP_EXPECTED:
+ exp_h[attr->nta_attr].parse(expected, attr->nta_attr,
+ NTA_DATA(attr));
+ case ATTR_EXP_MASK:
+ exp_h[attr->nta_attr].parse(mask, attr->nta_attr,
+ NTA_DATA(attr));
+ break;
+ case ATTR_EXP_TIMEOUT:
+ case ATTR_EXP_FLAGS:
+ exp_h[attr->nta_attr].parse(exp, attr->nta_attr,
+ NTA_DATA(attr));
+ break;
+ }
+ attr = NTA_NEXT(attr, len);
+ }
+
+ nfexp_set_attr(exp, ATTR_EXP_MASTER, master);
+ nfexp_set_attr(exp, ATTR_EXP_EXPECTED, expected);
+ nfexp_set_attr(exp, ATTR_EXP_MASK, mask);
+
+ /* We can release the conntrack objects at this point because the
+ * setter makes a copy of them. This is not efficient, it would be
+ * better to save that extra copy but this is how the library works.
+ * I'm sorry, I cannot change it without breaking backward
+ * compatibility. Probably it is a good idea to think of adding new
+ * interfaces in the near future to get it better. */
+ nfct_destroy(mask);
+ nfct_destroy(expected);
+ nfct_destroy(master);
+
+ return 0;
+err:
+ nfct_destroy(mask);
+err_mask:
+ nfct_destroy(expected);
+err_expected:
+ nfct_destroy(master);
+err_master:
+ return -1;
+}
diff --git a/src/read_config_lex.l b/src/read_config_lex.l
index b2d4bdb..01fe4fc 100644
--- a/src/read_config_lex.l
+++ b/src/read_config_lex.l
@@ -47,7 +47,7 @@ ip6_part {hex_255}":"?
ip6_form1 {ip6_part}{0,16}"::"{ip6_part}{0,16}
ip6_form2 ({hex_255}":"){16}{hex_255}
ip6 {ip6_form1}{ip6_cidr}?|{ip6_form2}{ip6_cidr}?
-string [a-zA-Z][a-zA-Z0-9\.]*
+string [a-zA-Z][a-zA-Z0-9\.\-]*
persistent [P|p][E|e][R|r][S|s][I|i][S|s][T|t][E|e][N|n][T|T]
nack [N|n][A|a][C|c][K|k]
alarm [A|a][L|l][A|a][R|r][M|m]
@@ -138,6 +138,9 @@ notrack [N|n][O|o][T|t][R|r][A|a][C|c][K|k]
"NetlinkEventsReliable" { return T_NETLINK_EVENTS_RELIABLE; }
"DisableInternalCache" { return T_DISABLE_INTERNAL_CACHE; }
"DisableExternalCache" { return T_DISABLE_EXTERNAL_CACHE; }
+"Options" { return T_OPTIONS; }
+"TCPWindowTracking" { return T_TCP_WINDOW_TRACKING; }
+"ExpectationSync" { return T_EXPECT_SYNC; }
"ErrorQueueLength" { return T_ERROR_QUEUE_LENGTH; }
{is_on} { return T_ON; }
diff --git a/src/read_config_yy.y b/src/read_config_yy.y
index 5f4e6be..d94bd85 100644
--- a/src/read_config_yy.y
+++ b/src/read_config_yy.y
@@ -73,6 +73,7 @@ static void __max_dedicated_links_reached(void);
%token T_NETLINK_OVERRUN_RESYNC T_NICE T_IPV4_DEST_ADDR T_IPV6_DEST_ADDR
%token T_SCHEDULER T_TYPE T_PRIO T_NETLINK_EVENTS_RELIABLE
%token T_DISABLE_INTERNAL_CACHE T_DISABLE_EXTERNAL_CACHE T_ERROR_QUEUE_LENGTH
+%token T_OPTIONS T_TCP_WINDOW_TRACKING T_EXPECT_SYNC
%token <string> T_IP T_PATH_VAL
%token <val> T_NUMBER
@@ -808,8 +809,65 @@ sync_line: refreshtime
| state_replication
| cache_writethrough
| destroy_timeout
+ | option_line
;
+option_line: T_OPTIONS '{' options '}';
+
+options:
+ | options option
+ ;
+
+option: T_TCP_WINDOW_TRACKING T_ON
+{
+ CONFIG(sync).tcp_window_tracking = 1;
+};
+
+option: T_TCP_WINDOW_TRACKING T_OFF
+{
+ CONFIG(sync).tcp_window_tracking = 0;
+};
+
+option: T_EXPECT_SYNC T_ON
+{
+ CONFIG(flags) |= CTD_EXPECT;
+ CONFIG(netlink).subsys_id = NFNL_SUBSYS_NONE;
+ CONFIG(netlink).groups = NF_NETLINK_CONNTRACK_NEW |
+ NF_NETLINK_CONNTRACK_UPDATE |
+ NF_NETLINK_CONNTRACK_DESTROY |
+ NF_NETLINK_CONNTRACK_EXP_NEW |
+ NF_NETLINK_CONNTRACK_EXP_UPDATE |
+ NF_NETLINK_CONNTRACK_EXP_DESTROY;
+};
+
+option: T_EXPECT_SYNC T_OFF
+{
+ CONFIG(netlink).subsys_id = NFNL_SUBSYS_CTNETLINK;
+ CONFIG(netlink).groups = NF_NETLINK_CONNTRACK_NEW |
+ NF_NETLINK_CONNTRACK_UPDATE |
+ NF_NETLINK_CONNTRACK_DESTROY;
+};
+
+option: T_EXPECT_SYNC '{' expect_list '}'
+{
+ CONFIG(flags) |= CTD_EXPECT;
+ CONFIG(netlink).subsys_id = NFNL_SUBSYS_NONE;
+ CONFIG(netlink).groups = NF_NETLINK_CONNTRACK_NEW |
+ NF_NETLINK_CONNTRACK_UPDATE |
+ NF_NETLINK_CONNTRACK_DESTROY |
+ NF_NETLINK_CONNTRACK_EXP_NEW |
+ NF_NETLINK_CONNTRACK_EXP_UPDATE |
+ NF_NETLINK_CONNTRACK_EXP_DESTROY;
+};
+
+expect_list:
+ | expect_list expect_item ;
+
+expect_item: T_STRING
+{
+ exp_filter_add(STATE(exp_filter), $1);
+}
+
sync_mode_alarm: T_SYNC_MODE T_ALARM '{' sync_mode_alarm_list '}'
{
conf.flags |= CTD_SYNC_ALARM;
@@ -1240,6 +1298,25 @@ filter_protocol_item : T_UDP
pent->p_proto);
};
+filter_protocol_item : T_UDP
+{
+ struct protoent *pent;
+
+ pent = getprotobyname("udp");
+ if (pent == NULL) {
+ print_err(CTD_CFG_WARN, "getprotobyname() cannot find "
+ "protocol `udp' in /etc/protocols");
+ break;
+ }
+ ct_filter_add_proto(STATE(us_filter), pent->p_proto);
+
+ __kernel_filter_start();
+
+ nfct_filter_add_attr_u32(STATE(filter),
+ NFCT_FILTER_L4PROTO,
+ pent->p_proto);
+};
+
filter_item : T_ADDRESS T_ACCEPT '{' filter_address_list '}'
{
ct_filter_set_logic(STATE(us_filter),
@@ -1580,6 +1657,7 @@ init_config(char *filename)
/* Zero may be a valid facility */
CONFIG(syslog_facility) = -1;
CONFIG(stats).syslog_facility = -1;
+ CONFIG(netlink).subsys_id = -1;
yyrestart(fp);
yyparse();
@@ -1618,7 +1696,7 @@ init_config(char *filename)
/* default number of bucket of the hashtable that are committed in
one run loop. XXX: no option available to tune this value yet. */
if (CONFIG(general).commit_steps == 0)
- CONFIG(general).commit_steps = 64;
+ CONFIG(general).commit_steps = 8192;
/* if overrun, automatically resync with kernel after 30 seconds */
if (CONFIG(nl_overrun_resync) == 0)
@@ -1628,5 +1706,12 @@ init_config(char *filename)
if (CONFIG(channelc).error_queue_length == 0)
CONFIG(channelc).error_queue_length = 128;
+ if (CONFIG(netlink).subsys_id == -1) {
+ CONFIG(netlink).subsys_id = NFNL_SUBSYS_CTNETLINK;
+ CONFIG(netlink).groups = NF_NETLINK_CONNTRACK_NEW |
+ NF_NETLINK_CONNTRACK_UPDATE |
+ NF_NETLINK_CONNTRACK_DESTROY;
+ }
+
return 0;
}
diff --git a/src/run.c b/src/run.c
index 803bbcc..159ef30 100644
--- a/src/run.c
+++ b/src/run.c
@@ -1,6 +1,7 @@
/*
- * (C) 2006-2009 by Pablo Neira Ayuso <pablo@netfilter.org>
- *
+ * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2011 by Vyatta Inc. <http://www.vyatta.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
@@ -186,40 +187,91 @@ static void dump_stats_runtime(int fd)
send(fd, buf, size, 0);
}
+static void local_flush_master(void)
+{
+ STATE(stats).nl_kernel_table_flush++;
+ dlog(LOG_NOTICE, "flushing kernel conntrack table");
+
+ /* fork a child process that performs the flush operation,
+ * meanwhile the parent process handles events. */
+ if (fork_process_new(CTD_PROC_FLUSH, CTD_PROC_F_EXCL,
+ NULL, NULL) == 0) {
+ nl_flush_conntrack_table(STATE(flush));
+ exit(EXIT_SUCCESS);
+ }
+}
+
+static void local_resync_master(void)
+{
+ if (STATE(mode)->internal->flags & INTERNAL_F_POPULATE) {
+ STATE(stats).nl_kernel_table_resync++;
+ dlog(LOG_NOTICE, "resync with master conntrack table");
+ nl_dump_conntrack_table(STATE(dump));
+ } else {
+ dlog(LOG_NOTICE, "resync is unsupported in this mode");
+ }
+}
+
+static void local_exp_flush_master(void)
+{
+ if (!(CONFIG(flags) & CTD_EXPECT))
+ return;
+
+ STATE(stats).nl_kernel_table_flush++;
+ dlog(LOG_NOTICE, "flushing kernel expect table");
+
+ /* fork a child process that performs the flush operation,
+ * meanwhile the parent process handles events. */
+ if (fork_process_new(CTD_PROC_FLUSH, CTD_PROC_F_EXCL,
+ NULL, NULL) == 0) {
+ nl_flush_expect_table(STATE(flush));
+ exit(EXIT_SUCCESS);
+ }
+}
+
+static void local_exp_resync_master(void)
+{
+ if (!(CONFIG(flags) & CTD_EXPECT))
+ return;
+
+ if (STATE(mode)->internal->flags & INTERNAL_F_POPULATE) {
+ STATE(stats).nl_kernel_table_resync++;
+ dlog(LOG_NOTICE, "resync with master expect table");
+ nl_dump_expect_table(STATE(dump));
+ } else {
+ dlog(LOG_NOTICE, "resync is unsupported in this mode");
+ }
+}
+
static int local_handler(int fd, void *data)
{
int ret = LOCAL_RET_OK;
int type;
- ret = read(fd, &type, sizeof(type));
- if (ret == -1) {
+ if (read(fd, &type, sizeof(type)) <= 0) {
STATE(stats).local_read_failed++;
return LOCAL_RET_OK;
}
- if (ret == 0)
- return LOCAL_RET_OK;
-
switch(type) {
- case FLUSH_MASTER:
- STATE(stats).nl_kernel_table_flush++;
- dlog(LOG_NOTICE, "flushing kernel conntrack table");
-
- /* fork a child process that performs the flush operation,
- * meanwhile the parent process handles events. */
- if (fork_process_new(CTD_PROC_FLUSH, CTD_PROC_F_EXCL,
- NULL, NULL) == 0) {
- nl_flush_conntrack_table(STATE(flush));
- exit(EXIT_SUCCESS);
- }
+ case CT_FLUSH_MASTER:
+ local_flush_master();
break;
- case RESYNC_MASTER:
- if (STATE(mode)->internal->flags & INTERNAL_F_POPULATE) {
- STATE(stats).nl_kernel_table_resync++;
- dlog(LOG_NOTICE, "resync with master table");
- nl_dump_conntrack_table(STATE(dump));
- } else {
- dlog(LOG_NOTICE, "resync is unsupported in this mode");
- }
+ case CT_RESYNC_MASTER:
+ local_resync_master();
+ break;
+ case EXP_FLUSH_MASTER:
+ local_exp_flush_master();
+ break;
+ case EXP_RESYNC_MASTER:
+ local_exp_resync_master();
+ break;
+ case ALL_FLUSH_MASTER:
+ local_flush_master();
+ local_exp_flush_master();
+ break;
+ case ALL_RESYNC_MASTER:
+ local_resync_master();
+ local_exp_resync_master();
break;
case STATS_RUNTIME:
dump_stats_runtime(fd);
@@ -245,10 +297,14 @@ static void do_overrun_resync_alarm(struct alarm_block *a, void *data)
static void do_polling_alarm(struct alarm_block *a, void *data)
{
- if (STATE(mode)->internal->purge)
- STATE(mode)->internal->purge();
+ if (STATE(mode)->internal->ct.purge)
+ STATE(mode)->internal->ct.purge();
+
+ if (STATE(mode)->internal->exp.purge)
+ STATE(mode)->internal->exp.purge();
nl_send_resync(STATE(resync));
+ nl_send_expect_resync(STATE(resync));
add_alarm(&STATE(polling_alarm), CONFIG(poll_kernel_secs), 0);
}
@@ -271,13 +327,13 @@ static int event_handler(const struct nlmsghdr *nlh,
switch(type) {
case NFCT_T_NEW:
- STATE(mode)->internal->new(ct, origin_type);
+ STATE(mode)->internal->ct.new(ct, origin_type);
break;
case NFCT_T_UPDATE:
- STATE(mode)->internal->update(ct, origin_type);
+ STATE(mode)->internal->ct.upd(ct, origin_type);
break;
case NFCT_T_DESTROY:
- if (STATE(mode)->internal->destroy(ct, origin_type))
+ if (STATE(mode)->internal->ct.del(ct, origin_type))
update_traffic_stats(ct);
break;
default:
@@ -286,10 +342,52 @@ static int event_handler(const struct nlmsghdr *nlh,
}
out:
- if (STATE(event_iterations_limit)-- <= 0) {
- STATE(event_iterations_limit) = CONFIG(event_iterations_limit);
+ if (STATE(event_iterations_limit)-- <= 0)
return NFCT_CB_STOP;
- } else
+ else
+ return NFCT_CB_CONTINUE;
+}
+
+static int exp_event_handler(const struct nlmsghdr *nlh,
+ enum nf_conntrack_msg_type type,
+ struct nf_expect *exp,
+ void *data)
+{
+ int origin_type;
+ const struct nf_conntrack *master =
+ nfexp_get_attr(exp, ATTR_EXP_MASTER);
+
+ STATE(stats).nl_events_received++;
+
+ if (!exp_filter_find(STATE(exp_filter), exp)) {
+ STATE(stats).nl_events_filtered++;
+ goto out;
+ }
+ if (ct_filter_conntrack(master, 1))
+ return NFCT_CB_CONTINUE;
+
+ origin_type = origin_find(nlh);
+
+ switch(type) {
+ case NFCT_T_NEW:
+ STATE(mode)->internal->exp.new(exp, origin_type);
+ break;
+ case NFCT_T_UPDATE:
+ STATE(mode)->internal->exp.upd(exp, origin_type);
+ break;
+ case NFCT_T_DESTROY:
+ STATE(mode)->internal->exp.del(exp, origin_type);
+ break;
+ default:
+ STATE(stats).nl_events_unknown_type++;
+ break;
+ }
+
+out:
+ /* we reset the iteration limiter in the main select loop. */
+ if (STATE(event_iterations_limit)-- <= 0)
+ return NFCT_CB_STOP;
+ else
return NFCT_CB_CONTINUE;
}
@@ -302,7 +400,30 @@ static int dump_handler(enum nf_conntrack_msg_type type,
switch(type) {
case NFCT_T_UPDATE:
- STATE(mode)->internal->populate(ct);
+ STATE(mode)->internal->ct.populate(ct);
+ break;
+ default:
+ STATE(stats).nl_dump_unknown_type++;
+ break;
+ }
+ return NFCT_CB_CONTINUE;
+}
+
+static int exp_dump_handler(enum nf_conntrack_msg_type type,
+ struct nf_expect *exp, void *data)
+{
+ const struct nf_conntrack *master =
+ nfexp_get_attr(exp, ATTR_EXP_MASTER);
+
+ if (!exp_filter_find(STATE(exp_filter), exp))
+ return NFCT_CB_CONTINUE;
+
+ if (ct_filter_conntrack(master, 1))
+ return NFCT_CB_CONTINUE;
+
+ switch(type) {
+ case NFCT_T_UPDATE:
+ STATE(mode)->internal->exp.populate(exp);
break;
default:
STATE(stats).nl_dump_unknown_type++;
@@ -322,9 +443,27 @@ static int get_handler(enum nf_conntrack_msg_type type,
return NFCT_CB_CONTINUE;
}
+static int exp_get_handler(enum nf_conntrack_msg_type type,
+ struct nf_expect *exp, void *data)
+{
+ const struct nf_conntrack *master =
+ nfexp_get_attr(exp, ATTR_EXP_MASTER);
+
+ if (!exp_filter_find(STATE(exp_filter), exp))
+ return NFCT_CB_CONTINUE;
+
+ if (ct_filter_conntrack(master, 1))
+ return NFCT_CB_CONTINUE;
+
+ STATE(get_retval) = 1;
+ return NFCT_CB_CONTINUE;
+}
+
int
init(void)
{
+ do_gettimeofday();
+
if (CONFIG(flags) & CTD_STATS_MODE)
STATE(mode) = &stats_mode;
else if (CONFIG(flags) & CTD_SYNC_MODE)
@@ -355,21 +494,8 @@ init(void)
}
register_fd(STATE(local).fd, STATE(fds));
- if (!(CONFIG(flags) & CTD_POLL)) {
- STATE(event) = nl_init_event_handler();
- if (STATE(event) == NULL) {
- dlog(LOG_ERR, "can't open netlink handler: %s",
- strerror(errno));
- dlog(LOG_ERR, "no ctnetlink kernel support?");
- return -1;
- }
- nfct_callback_register2(STATE(event), NFCT_T_ALL,
- event_handler, NULL);
- register_fd(nfct_fd(STATE(event)), STATE(fds));
- }
-
/* resynchronize (like 'dump' socket) but it also purges old entries */
- STATE(resync) = nfct_open(CONNTRACK, 0);
+ STATE(resync) = nfct_open(CONFIG(netlink).subsys_id, 0);
if (STATE(resync)== NULL) {
dlog(LOG_ERR, "can't open netlink handler: %s",
strerror(errno));
@@ -378,13 +504,13 @@ init(void)
}
nfct_callback_register(STATE(resync),
NFCT_T_ALL,
- STATE(mode)->internal->resync,
+ STATE(mode)->internal->ct.resync,
NULL);
register_fd(nfct_fd(STATE(resync)), STATE(fds));
fcntl(nfct_fd(STATE(resync)), F_SETFL, O_NONBLOCK);
if (STATE(mode)->internal->flags & INTERNAL_F_POPULATE) {
- STATE(dump) = nfct_open(CONNTRACK, 0);
+ STATE(dump) = nfct_open(CONFIG(netlink).subsys_id, 0);
if (STATE(dump) == NULL) {
dlog(LOG_ERR, "can't open netlink handler: %s",
strerror(errno));
@@ -394,13 +520,26 @@ init(void)
nfct_callback_register(STATE(dump), NFCT_T_ALL,
dump_handler, NULL);
+ if (CONFIG(flags) & CTD_EXPECT) {
+ nfexp_callback_register(STATE(dump), NFCT_T_ALL,
+ exp_dump_handler, NULL);
+ }
+
if (nl_dump_conntrack_table(STATE(dump)) == -1) {
dlog(LOG_ERR, "can't get kernel conntrack table");
return -1;
}
+
+ if (CONFIG(flags) & CTD_EXPECT) {
+ if (nl_dump_expect_table(STATE(dump)) == -1) {
+ dlog(LOG_ERR, "can't get kernel "
+ "expect table");
+ return -1;
+ }
+ }
}
- STATE(get) = nfct_open(CONNTRACK, 0);
+ STATE(get) = nfct_open(CONFIG(netlink).subsys_id, 0);
if (STATE(get) == NULL) {
dlog(LOG_ERR, "can't open netlink handler: %s",
strerror(errno));
@@ -409,7 +548,12 @@ init(void)
}
nfct_callback_register(STATE(get), NFCT_T_ALL, get_handler, NULL);
- STATE(flush) = nfct_open(CONNTRACK, 0);
+ if (CONFIG(flags) & CTD_EXPECT) {
+ nfexp_callback_register(STATE(get), NFCT_T_ALL,
+ exp_get_handler, NULL);
+ }
+
+ STATE(flush) = nfct_open(CONFIG(netlink).subsys_id, 0);
if (STATE(flush) == NULL) {
dlog(LOG_ERR, "cannot open flusher handler");
return -1;
@@ -423,6 +567,29 @@ init(void)
dlog(LOG_NOTICE, "running in polling mode");
} else {
init_alarm(&STATE(resync_alarm), NULL, do_overrun_resync_alarm);
+ /*
+ * The last nfct handler that we register is the event handler.
+ * The reason to do this is that we may receive events while
+ * populating the internal cache. Thus, we hit ENOBUFS
+ * prematurely. However, if we open the event handler before
+ * populating the internal cache, we may still lose events
+ * that have occured during the population.
+ */
+ STATE(event) = nl_init_event_handler();
+ if (STATE(event) == NULL) {
+ dlog(LOG_ERR, "can't open netlink handler: %s",
+ strerror(errno));
+ dlog(LOG_ERR, "no ctnetlink kernel support?");
+ return -1;
+ }
+ nfct_callback_register2(STATE(event), NFCT_T_ALL,
+ event_handler, NULL);
+
+ if (CONFIG(flags) & CTD_EXPECT) {
+ nfexp_callback_register2(STATE(event), NFCT_T_ALL,
+ exp_event_handler, NULL);
+ }
+ register_fd(nfct_fd(STATE(event)), STATE(fds));
}
/* Signals handling */
@@ -451,7 +618,7 @@ init(void)
return 0;
}
-static void __run(struct timeval *next_alarm)
+static void run_events(struct timeval *next_alarm)
{
int ret;
fd_set readfds = STATE(fds)->readfds;
@@ -473,77 +640,70 @@ static void __run(struct timeval *next_alarm)
if (FD_ISSET(STATE(local).fd, &readfds))
do_local_server_step(&STATE(local), NULL, local_handler);
- if (!(CONFIG(flags) & CTD_POLL)) {
- /* conntrack event has happened */
- if (FD_ISSET(nfct_fd(STATE(event)), &readfds)) {
- ret = nfct_catch(STATE(event));
- /* reset event iteration limit counter */
- STATE(event_iterations_limit) =
- CONFIG(event_iterations_limit);
- if (ret == -1) {
- switch(errno) {
- case ENOBUFS:
- /* We have hit ENOBUFS, it's likely that we are
- * losing events. Two possible situations may
- * trigger this error:
- *
- * 1) The netlink receiver buffer is too small:
- * increasing the netlink buffer size should
- * be enough. However, some event messages
- * got lost. We have to resync ourselves
- * with the kernel table conntrack table to
- * resolve the inconsistency.
- *
- * 2) The receiver is too slow to process the
- * netlink messages so that the queue gets
- * full quickly. This generally happens
- * if the system is under heavy workload
- * (busy CPU). In this case, increasing the
- * size of the netlink receiver buffer
- * would not help anymore since we would
- * be delaying the overrun. Moreover, we
- * should avoid resynchronizations. We
- * should do our best here and keep
- * replicating as much states as possible.
- * If workload lowers at some point,
- * we resync ourselves.
- */
- nl_resize_socket_buffer(STATE(event));
- if (CONFIG(nl_overrun_resync) > 0 &&
- STATE(mode)->internal->flags &
- INTERNAL_F_RESYNC) {
- add_alarm(&STATE(resync_alarm),
- CONFIG(nl_overrun_resync),0);
- }
- STATE(stats).nl_catch_event_failed++;
- STATE(stats).nl_overrun++;
- break;
- case ENOENT:
- /*
- * We received a message from another
- * netfilter subsystem that we are not
- * interested in. Just ignore it.
- */
- break;
- case EAGAIN:
- break;
- default:
- STATE(stats).nl_catch_event_failed++;
- break;
- }
+ /* we have receive an event from ctnetlink */
+ if (FD_ISSET(nfct_fd(STATE(event)), &readfds)) {
+ ret = nfct_catch(STATE(event));
+ /* reset event iteration limit counter */
+ STATE(event_iterations_limit) = CONFIG(event_iterations_limit);
+ if (ret == -1) {
+ switch(errno) {
+ case ENOBUFS:
+ /* We have hit ENOBUFS, it's likely that we are
+ * losing events. Two possible situations may
+ * trigger this error:
+ *
+ * 1) The netlink receiver buffer is too small:
+ * increasing the netlink buffer size should
+ * be enough. However, some event messages
+ * got lost. We have to resync ourselves
+ * with the kernel table conntrack table to
+ * resolve the inconsistency.
+ *
+ * 2) The receiver is too slow to process the
+ * netlink messages so that the queue gets
+ * full quickly. This generally happens
+ * if the system is under heavy workload
+ * (busy CPU). In this case, increasing the
+ * size of the netlink receiver buffer
+ * would not help anymore since we would
+ * be delaying the overrun. Moreover, we
+ * should avoid resynchronizations. We
+ * should do our best here and keep
+ * replicating as much states as possible.
+ * If workload lowers at some point,
+ * we resync ourselves.
+ */
+ nl_resize_socket_buffer(STATE(event));
+ if (CONFIG(nl_overrun_resync) > 0 &&
+ STATE(mode)->internal->flags & INTERNAL_F_RESYNC) {
+ add_alarm(&STATE(resync_alarm),
+ CONFIG(nl_overrun_resync),0);
}
+ STATE(stats).nl_catch_event_failed++;
+ STATE(stats).nl_overrun++;
+ break;
+ case ENOENT:
+ /*
+ * We received a message from another
+ * netfilter subsystem that we are not
+ * interested in. Just ignore it.
+ */
+ break;
+ case EAGAIN:
+ /* No more events to receive, try later. */
+ break;
+ default:
+ STATE(stats).nl_catch_event_failed++;
+ break;
}
- if (FD_ISSET(nfct_fd(STATE(resync)), &readfds)) {
- nfct_catch(STATE(resync));
- if (STATE(mode)->internal->purge)
- STATE(mode)->internal->purge();
- }
- } else {
- /* using polling mode */
- if (FD_ISSET(nfct_fd(STATE(resync)), &readfds)) {
- nfct_catch(STATE(resync));
}
}
+ /* we previously requested a resync due to buffer overrun. */
+ if (FD_ISSET(nfct_fd(STATE(resync)), &readfds)) {
+ nfct_catch(STATE(resync));
+ if (STATE(mode)->internal->ct.purge)
+ STATE(mode)->internal->ct.purge();
+ }
if (STATE(mode)->run)
STATE(mode)->run(&readfds);
@@ -551,8 +711,40 @@ static void __run(struct timeval *next_alarm)
sigprocmask(SIG_UNBLOCK, &STATE(block), NULL);
}
-void __attribute__((noreturn))
-run(void)
+static void run_polling(struct timeval *next_alarm)
+{
+ int ret;
+ fd_set readfds = STATE(fds)->readfds;
+
+ ret = select(STATE(fds)->maxfd + 1, &readfds, NULL, NULL, next_alarm);
+ if (ret == -1) {
+ /* interrupted syscall, retry */
+ if (errno == EINTR)
+ return;
+
+ STATE(stats).select_failed++;
+ return;
+ }
+
+ /* signals are racy */
+ sigprocmask(SIG_BLOCK, &STATE(block), NULL);
+
+ /* order received via UNIX socket */
+ if (FD_ISSET(STATE(local).fd, &readfds))
+ do_local_server_step(&STATE(local), NULL, local_handler);
+
+ /* we requested a dump from the kernel via polling_alarm */
+ if (FD_ISSET(nfct_fd(STATE(resync)), &readfds))
+ nfct_catch(STATE(resync));
+
+ if (STATE(mode)->run)
+ STATE(mode)->run(&readfds);
+
+ sigprocmask(SIG_UNBLOCK, &STATE(block), NULL);
+}
+
+static void __attribute__((noreturn))
+do_run(void (*run_step)(struct timeval *next_alarm))
{
struct timeval next_alarm;
struct timeval *next = NULL;
@@ -567,6 +759,15 @@ run(void)
next = get_next_alarm_run(&next_alarm);
sigprocmask(SIG_UNBLOCK, &STATE(block), NULL);
- __run(next);
+ run_step(next);
+ }
+}
+
+void run(void)
+{
+ if (CONFIG(flags) & CTD_POLL) {
+ do_run(run_polling);
+ } else {
+ do_run(run_events);
}
}
diff --git a/src/stats-mode.c b/src/stats-mode.c
index 0403ce2..b768033 100644
--- a/src/stats-mode.c
+++ b/src/stats-mode.c
@@ -1,6 +1,7 @@
/*
- * (C) 2006-2007 by Pablo Neira Ayuso <pablo@netfilter.org>
- *
+ * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2011 by Vyatta Inc. <http://www.vyatta.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
@@ -37,7 +38,9 @@ static int init_stats(void)
}
memset(state.stats, 0, sizeof(struct ct_stats_state));
- STATE_STATS(cache) = cache_create("stats", NO_FEATURES, NULL);
+ STATE_STATS(cache) = cache_create("stats", CACHE_T_CT,
+ NO_FEATURES, NULL,
+ &cache_stats_ct_ops);
if (!STATE_STATS(cache)) {
dlog(LOG_ERR, "can't allocate memory for the "
"external cache");
@@ -59,14 +62,14 @@ static int local_handler_stats(int fd, int type, void *data)
int ret = LOCAL_RET_OK;
switch(type) {
- case DUMP_INTERNAL:
+ case CT_DUMP_INTERNAL:
cache_dump(STATE_STATS(cache), fd, NFCT_O_PLAIN);
break;
- case DUMP_INT_XML:
+ case CT_DUMP_INT_XML:
cache_dump(STATE_STATS(cache), fd, NFCT_O_XML);
break;
- case FLUSH_CACHE:
- case FLUSH_INT_CACHE:
+ case CT_FLUSH_CACHE:
+ case CT_FLUSH_INT_CACHE:
dlog(LOG_NOTICE, "flushing caches");
cache_flush(STATE_STATS(cache));
break;
@@ -88,7 +91,7 @@ static int local_handler_stats(int fd, int type, void *data)
return ret;
}
-static void populate_stats(struct nf_conntrack *ct)
+static void stats_populate(struct nf_conntrack *ct)
{
nfct_attr_unset(ct, ATTR_ORIG_COUNTER_BYTES);
nfct_attr_unset(ct, ATTR_ORIG_COUNTER_PACKETS);
@@ -100,7 +103,7 @@ static void populate_stats(struct nf_conntrack *ct)
cache_update_force(STATE_STATS(cache), ct);
}
-static int resync_stats(enum nf_conntrack_msg_type type,
+static int stats_resync(enum nf_conntrack_msg_type type,
struct nf_conntrack *ct,
void *data)
{
@@ -125,23 +128,22 @@ static int purge_step(void *data1, void *data2)
struct cache_object *obj = data2;
STATE(get_retval) = 0;
- nl_get_conntrack(STATE(get), obj->ct); /* modifies STATE(get_retval) */
+ nl_get_conntrack(STATE(get), obj->ptr); /* modifies STATE(get_retval) */
if (!STATE(get_retval)) {
cache_del(STATE_STATS(cache), obj);
- dlog_ct(STATE(stats_log), obj->ct, NFCT_O_PLAIN);
+ dlog_ct(STATE(stats_log), obj->ptr, NFCT_O_PLAIN);
cache_object_free(obj);
}
return 0;
}
-static void purge_stats(void)
+static void stats_purge(void)
{
cache_iterate(STATE_STATS(cache), NULL, purge_step);
}
-static void
-event_new_stats(struct nf_conntrack *ct, int origin)
+static void stats_event_new(struct nf_conntrack *ct, int origin)
{
int id;
struct cache_object *obj;
@@ -162,15 +164,13 @@ event_new_stats(struct nf_conntrack *ct, int origin)
return;
}
-static void
-event_update_stats(struct nf_conntrack *ct, int origin)
+static void stats_event_upd(struct nf_conntrack *ct, int origin)
{
nfct_attr_unset(ct, ATTR_TIMEOUT);
cache_update_force(STATE_STATS(cache), ct);
}
-static int
-event_destroy_stats(struct nf_conntrack *ct, int origin)
+static int stats_event_del(struct nf_conntrack *ct, int origin)
{
int id;
struct cache_object *obj;
@@ -189,12 +189,14 @@ event_destroy_stats(struct nf_conntrack *ct, int origin)
static struct internal_handler internal_cache_stats = {
.flags = INTERNAL_F_POPULATE | INTERNAL_F_RESYNC,
- .populate = populate_stats,
- .resync = resync_stats,
- .purge = purge_stats,
- .new = event_new_stats,
- .update = event_update_stats,
- .destroy = event_destroy_stats
+ .ct = {
+ .populate = stats_populate,
+ .resync = stats_resync,
+ .purge = stats_purge,
+ .new = stats_event_new,
+ .upd = stats_event_upd,
+ .del = stats_event_del,
+ },
};
struct ct_mode stats_mode = {
diff --git a/src/sync-alarm.c b/src/sync-alarm.c
index 0fc7943..acaf5e6 100644
--- a/src/sync-alarm.c
+++ b/src/sync-alarm.c
@@ -1,6 +1,7 @@
/*
- * (C) 2006-2007 by Pablo Neira Ayuso <pablo@netfilter.org>
- *
+ * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2011 by Vyatta Inc. <http://www.vyatta.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
@@ -28,6 +29,7 @@
struct cache_alarm {
struct queue_node qnode;
+ struct cache_object *obj;
struct alarm_block alarm;
};
@@ -41,7 +43,7 @@ static void refresher(struct alarm_block *a, void *data)
random() % CONFIG(refresh) + 1,
((random() % 5 + 1) * 200000) - 1);
- alarm_enqueue(obj, NET_T_STATE_UPD);
+ alarm_enqueue(obj, NET_T_STATE_CT_UPD);
}
static void cache_alarm_add(struct cache_object *obj, void *data)
@@ -49,6 +51,7 @@ static void cache_alarm_add(struct cache_object *obj, void *data)
struct cache_alarm *ca = data;
queue_node_init(&ca->qnode, Q_ELEM_OBJ);
+ ca->obj = obj;
init_alarm(&ca->alarm, obj, refresher);
add_alarm(&ca->alarm,
random() % CONFIG(refresh) + 1,
@@ -109,9 +112,8 @@ static int alarm_recv(const struct nethdr *net)
static void alarm_enqueue(struct cache_object *obj, int query)
{
- struct cache_alarm *ca =
- cache_get_extra(STATE(mode)->internal->data, obj);
- if (queue_add(STATE_SYNC(tx_queue), &ca->qnode))
+ struct cache_alarm *ca = cache_get_extra(obj);
+ if (queue_add(STATE_SYNC(tx_queue), &ca->qnode) > 0)
cache_object_get(obj);
}
@@ -131,15 +133,13 @@ static int tx_queue_xmit(struct queue_node *n, const void *data)
break;
case Q_ELEM_OBJ: {
struct cache_alarm *ca;
- struct cache_object *obj;
int type;
ca = (struct cache_alarm *)n;
- obj = cache_data_get_object(STATE(mode)->internal->data, ca);
- type = object_status_to_network_type(obj->status);
- net = BUILD_NETMSG(obj->ct, type);
+ type = object_status_to_network_type(ca->obj);
+ net = ca->obj->cache->ops->build_msg(ca->obj, type);
multichannel_send(STATE_SYNC(channel), net);
- cache_object_put(obj);
+ cache_object_put(ca->obj);
break;
}
}
diff --git a/src/sync-ftfw.c b/src/sync-ftfw.c
index 86edeab..1bc2d9f 100644
--- a/src/sync-ftfw.c
+++ b/src/sync-ftfw.c
@@ -1,6 +1,7 @@
/*
- * (C) 2006-2008 by Pablo Neira Ayuso <pablo@netfilter.org>
- *
+ * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2011 by Vyatta Inc. <http://www.vyatta.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
@@ -54,12 +55,14 @@ static int say_hello_back;
struct cache_ftfw {
struct queue_node qnode;
+ struct cache_object *obj;
uint32_t seq;
};
static void cache_ftfw_add(struct cache_object *obj, void *data)
{
struct cache_ftfw *cn = data;
+ cn->obj = obj;
/* These nodes are not inserted in the list */
queue_node_init(&cn->qnode, Q_ELEM_OBJ);
}
@@ -107,7 +110,8 @@ static void tx_queue_add_ctlmsg(uint32_t flags, uint32_t from, uint32_t to)
ack->from = from;
ack->to = to;
- queue_add(STATE_SYNC(tx_queue), &qobj->qnode);
+ if (queue_add(STATE_SYNC(tx_queue), &qobj->qnode) < 0)
+ queue_object_free(qobj);
}
static void tx_queue_add_ctlmsg2(uint32_t flags)
@@ -123,7 +127,8 @@ static void tx_queue_add_ctlmsg2(uint32_t flags)
ctl->type = NET_T_CTL;
ctl->flags = flags;
- queue_add(STATE_SYNC(tx_queue), &qobj->qnode);
+ if (queue_add(STATE_SYNC(tx_queue), &qobj->qnode) < 0)
+ queue_object_free(qobj);
}
/* this function is called from the alarm framework */
@@ -166,14 +171,13 @@ static void ftfw_kill(void)
static int do_cache_to_tx(void *data1, void *data2)
{
struct cache_object *obj = data2;
- struct cache_ftfw *cn =
- cache_get_extra(STATE(mode)->internal->data, obj);
+ struct cache_ftfw *cn = cache_get_extra(obj);
if (queue_in(rs_queue, &cn->qnode)) {
queue_del(&cn->qnode);
queue_add(STATE_SYNC(tx_queue), &cn->qnode);
} else {
- if (queue_add(STATE_SYNC(tx_queue), &cn->qnode))
+ if (queue_add(STATE_SYNC(tx_queue), &cn->qnode) > 0)
cache_object_get(obj);
}
return 0;
@@ -225,7 +229,9 @@ static int ftfw_local(int fd, int type, void *data)
break;
case SEND_BULK:
dlog(LOG_NOTICE, "sending bulk update");
- cache_iterate(STATE(mode)->internal->data,
+ cache_iterate(STATE(mode)->internal->ct.data,
+ NULL, do_cache_to_tx);
+ cache_iterate(STATE(mode)->internal->exp.data,
NULL, do_cache_to_tx);
break;
case STATS_RSQUEUE:
@@ -300,13 +306,11 @@ static int rs_queue_empty(struct queue_node *n, const void *data)
}
case Q_ELEM_OBJ: {
struct cache_ftfw *cn;
- struct cache_object *obj;
cn = (struct cache_ftfw *) n;
if (h == NULL) {
queue_del(n);
- obj = cache_data_get_object(STATE(mode)->internal->data, cn);
- cache_object_put(obj);
+ cache_object_put(cn->obj);
return 0;
}
if (before(cn->seq, h->from))
@@ -316,8 +320,7 @@ static int rs_queue_empty(struct queue_node *n, const void *data)
dp("queue: deleting from queue (seq=%u)\n", cn->seq);
queue_del(n);
- obj = cache_data_get_object(STATE(mode)->internal->data, cn);
- cache_object_put(obj);
+ cache_object_put(cn->obj);
break;
}
}
@@ -349,7 +352,10 @@ static int digest_msg(const struct nethdr *net)
} else if (IS_RESYNC(net)) {
dp("RESYNC ALL\n");
- cache_iterate(STATE(mode)->internal->data, NULL, do_cache_to_tx);
+ cache_iterate(STATE(mode)->internal->ct.data, NULL,
+ do_cache_to_tx);
+ cache_iterate(STATE(mode)->internal->exp.data, NULL,
+ do_cache_to_tx);
return MSG_CTL;
} else if (IS_ALIVE(net))
@@ -463,11 +469,9 @@ static void rs_queue_purge_full(void)
}
case Q_ELEM_OBJ: {
struct cache_ftfw *cn;
- struct cache_object *obj;
cn = (struct cache_ftfw *)n;
- obj = cache_data_get_object(STATE(mode)->internal->data, cn);
- cache_object_put(obj);
+ cache_object_put(cn->obj);
break;
}
}
@@ -509,14 +513,12 @@ static int tx_queue_xmit(struct queue_node *n, const void *data)
}
case Q_ELEM_OBJ: {
struct cache_ftfw *cn;
- struct cache_object *obj;
int type;
struct nethdr *net;
cn = (struct cache_ftfw *)n;
- obj = cache_data_get_object(STATE(mode)->internal->data, cn);
- type = object_status_to_network_type(obj->status);
- net = BUILD_NETMSG(obj->ct, type);
+ type = object_status_to_network_type(cn->obj);
+ net = cn->obj->cache->ops->build_msg(cn->obj, type);
nethdr_set_hello(net);
dp("tx_list sq: %u fl:%u len:%u\n",
@@ -548,13 +550,12 @@ static void ftfw_xmit(void)
static void ftfw_enqueue(struct cache_object *obj, int type)
{
- struct cache_ftfw *cn =
- cache_get_extra(STATE(mode)->internal->data, obj);
+ struct cache_ftfw *cn = cache_get_extra(obj);
if (queue_in(rs_queue, &cn->qnode)) {
queue_del(&cn->qnode);
queue_add(STATE_SYNC(tx_queue), &cn->qnode);
} else {
- if (queue_add(STATE_SYNC(tx_queue), &cn->qnode))
+ if (queue_add(STATE_SYNC(tx_queue), &cn->qnode) > 0)
cache_object_get(obj);
}
}
diff --git a/src/sync-mode.c b/src/sync-mode.c
index 3fa0d11..2505631 100644
--- a/src/sync-mode.c
+++ b/src/sync-mode.c
@@ -1,6 +1,7 @@
/*
- * (C) 2006-2007 by Pablo Neira Ayuso <pablo@netfilter.org>
- *
+ * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2011 by Vyatta Inc. <http://www.vyatta.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
@@ -40,11 +41,47 @@
#include <net/if.h>
#include <fcntl.h>
+static struct nf_conntrack *msg2ct_alloc(struct nethdr *net, size_t remain)
+{
+ struct nf_conntrack *ct;
+
+ /* TODO: add stats on ENOMEM errors in the future. */
+ ct = nfct_new();
+ if (ct == NULL)
+ return NULL;
+
+ if (msg2ct(ct, net, remain) == -1) {
+ STATE_SYNC(error).msg_rcv_malformed++;
+ STATE_SYNC(error).msg_rcv_bad_payload++;
+ nfct_destroy(ct);
+ return NULL;
+ }
+ return ct;
+}
+
+static struct nf_expect *msg2exp_alloc(struct nethdr *net, size_t remain)
+{
+ struct nf_expect *exp;
+
+ /* TODO: add stats on ENOMEM errors in the future. */
+ exp = nfexp_new();
+ if (exp == NULL)
+ return NULL;
+
+ if (msg2exp(exp, net, remain) == -1) {
+ STATE_SYNC(error).msg_rcv_malformed++;
+ STATE_SYNC(error).msg_rcv_bad_payload++;
+ nfexp_destroy(exp);
+ return NULL;
+ }
+ return exp;
+}
+
static void
do_channel_handler_step(int i, struct nethdr *net, size_t remain)
{
- char __ct[nfct_maxsize()];
- struct nf_conntrack *ct = (struct nf_conntrack *)(void*) __ct;
+ struct nf_conntrack *ct = NULL;
+ struct nf_expect *exp = NULL;
if (net->version != CONNTRACKD_PROTOCOL_VERSION) {
STATE_SYNC(error).msg_rcv_malformed++;
@@ -74,29 +111,53 @@ do_channel_handler_step(int i, struct nethdr *net, size_t remain)
STATE_SYNC(error).msg_rcv_bad_type++;
return;
}
- memset(ct, 0, sizeof(__ct));
-
- if (parse_payload(ct, net, remain) == -1) {
- STATE_SYNC(error).msg_rcv_malformed++;
- STATE_SYNC(error).msg_rcv_bad_payload++;
- return;
- }
switch(net->type) {
- case NET_T_STATE_NEW:
- STATE_SYNC(external)->new(ct);
+ case NET_T_STATE_CT_NEW:
+ ct = msg2ct_alloc(net, remain);
+ if (ct == NULL)
+ return;
+ STATE_SYNC(external)->ct.new(ct);
+ break;
+ case NET_T_STATE_CT_UPD:
+ ct = msg2ct_alloc(net, remain);
+ if (ct == NULL)
+ return;
+ STATE_SYNC(external)->ct.upd(ct);
break;
- case NET_T_STATE_UPD:
- STATE_SYNC(external)->update(ct);
+ case NET_T_STATE_CT_DEL:
+ ct = msg2ct_alloc(net, remain);
+ if (ct == NULL)
+ return;
+ STATE_SYNC(external)->ct.del(ct);
break;
- case NET_T_STATE_DEL:
- STATE_SYNC(external)->destroy(ct);
+ case NET_T_STATE_EXP_NEW:
+ exp = msg2exp_alloc(net, remain);
+ if (exp == NULL)
+ return;
+ STATE_SYNC(external)->exp.new(exp);
+ break;
+ case NET_T_STATE_EXP_UPD:
+ exp = msg2exp_alloc(net, remain);
+ if (exp == NULL)
+ return;
+ STATE_SYNC(external)->exp.upd(exp);
+ break;
+ case NET_T_STATE_EXP_DEL:
+ exp = msg2exp_alloc(net, remain);
+ if (exp == NULL)
+ return;
+ STATE_SYNC(external)->exp.del(exp);
break;
default:
STATE_SYNC(error).msg_rcv_malformed++;
STATE_SYNC(error).msg_rcv_bad_type++;
break;
}
+ if (ct != NULL)
+ nfct_destroy(ct);
+ if (exp != NULL)
+ nfexp_destroy(exp);
}
static char __net[65536]; /* XXX: maximum MTU for IPv4 */
@@ -247,7 +308,7 @@ static void do_reset_cache_alarm(struct alarm_block *a, void *data)
exit(EXIT_SUCCESS);
}
/* this is not required if events don't get lost */
- STATE(mode)->internal->flush();
+ STATE(mode)->internal->ct.flush();
}
static int init_sync(void)
@@ -330,7 +391,7 @@ static int init_sync(void)
STATE(fds)) == -1)
return -1;
- STATE_SYNC(commit).h = nfct_open(CONNTRACK, 0);
+ STATE_SYNC(commit).h = nfct_open(CONFIG(netlink).subsys_id, 0);
if (STATE_SYNC(commit).h == NULL) {
dlog(LOG_ERR, "can't create handler to commit");
return -1;
@@ -346,6 +407,7 @@ static int init_sync(void)
STATE(fds)) == -1) {
return -1;
}
+ STATE_SYNC(commit).clientfd = -1;
init_alarm(&STATE_SYNC(reset_cache_alarm), NULL, do_reset_cache_alarm);
@@ -380,8 +442,30 @@ static void run_sync(fd_set *readfds)
interface_handler();
if (FD_ISSET(get_read_evfd(STATE_SYNC(commit).evfd), readfds)) {
+ int ret;
+
read_evfd(STATE_SYNC(commit).evfd);
- STATE_SYNC(external)->commit(STATE_SYNC(commit).h, 0);
+
+ ret = STATE_SYNC(commit).rq[0].cb(STATE_SYNC(commit).h, 0);
+ if (ret == 0) {
+ /* we still have things in the callback queue. */
+ if (STATE_SYNC(commit).rq[1].cb) {
+ int fd = STATE_SYNC(commit).clientfd;
+
+ STATE_SYNC(commit).rq[0].cb =
+ STATE_SYNC(commit).rq[1].cb;
+
+ STATE_SYNC(commit).rq[1].cb = NULL;
+
+ STATE_SYNC(commit).clientfd = -1;
+ STATE_SYNC(commit).rq[0].cb(
+ STATE_SYNC(commit).h, fd);
+ } else {
+ /* Close the client socket now, we're done. */
+ close(STATE_SYNC(commit).clientfd);
+ STATE_SYNC(commit).clientfd = -1;
+ }
+ }
}
/* flush pending messages */
@@ -458,48 +542,62 @@ static void dump_stats_sync_extended(int fd)
send(fd, buf, size, 0);
}
+static int local_commit(int fd)
+{
+ int ret;
+
+ /* delete the reset alarm if any before committing */
+ del_alarm(&STATE_SYNC(reset_cache_alarm));
+
+ ret = STATE_SYNC(commit).rq[0].cb(STATE_SYNC(commit).h, fd);
+ if (ret == -1) {
+ dlog(LOG_NOTICE, "commit already in progress, skipping");
+ ret = LOCAL_RET_OK;
+ } else if (ret == 0) {
+ /* we've finished the commit. */
+ ret = LOCAL_RET_OK;
+ } else {
+ /* Keep open the client, we want synchronous commit. */
+ ret = LOCAL_RET_STOLEN;
+ }
+ return ret;
+}
+
/* handler for requests coming via UNIX socket */
static int local_handler_sync(int fd, int type, void *data)
{
int ret = LOCAL_RET_OK;
switch(type) {
- case DUMP_INTERNAL:
- ret = fork_process_new(CTD_PROC_ANY, 0, NULL, NULL);
- if (ret == 0) {
- STATE(mode)->internal->dump(fd, NFCT_O_PLAIN);
+ case CT_DUMP_INTERNAL:
+ if (fork_process_new(CTD_PROC_ANY, 0, NULL, NULL) == 0) {
+ STATE(mode)->internal->ct.dump(fd, NFCT_O_PLAIN);
exit(EXIT_SUCCESS);
}
break;
- case DUMP_EXTERNAL:
- ret = fork_process_new(CTD_PROC_ANY, 0, NULL, NULL);
- if (ret == 0) {
- STATE_SYNC(external)->dump(fd, NFCT_O_PLAIN);
+ case CT_DUMP_EXTERNAL:
+ if (fork_process_new(CTD_PROC_ANY, 0, NULL, NULL) == 0) {
+ STATE_SYNC(external)->ct.dump(fd, NFCT_O_PLAIN);
exit(EXIT_SUCCESS);
}
break;
- case DUMP_INT_XML:
- ret = fork_process_new(CTD_PROC_ANY, 0, NULL, NULL);
- if (ret == 0) {
- STATE(mode)->internal->dump(fd, NFCT_O_XML);
+ case CT_DUMP_INT_XML:
+ if (fork_process_new(CTD_PROC_ANY, 0, NULL, NULL) == 0) {
+ STATE(mode)->internal->ct.dump(fd, NFCT_O_XML);
exit(EXIT_SUCCESS);
}
break;
- case DUMP_EXT_XML:
- ret = fork_process_new(CTD_PROC_ANY, 0, NULL, NULL);
- if (ret == 0) {
- STATE_SYNC(external)->dump(fd, NFCT_O_XML);
+ case CT_DUMP_EXT_XML:
+ if (fork_process_new(CTD_PROC_ANY, 0, NULL, NULL) == 0) {
+ STATE_SYNC(external)->ct.dump(fd, NFCT_O_XML);
exit(EXIT_SUCCESS);
}
break;
- case COMMIT:
- /* delete the reset alarm if any before committing */
- del_alarm(&STATE_SYNC(reset_cache_alarm));
-
- dlog(LOG_NOTICE, "committing external cache");
- STATE_SYNC(external)->commit(STATE_SYNC(commit).h, fd);
- /* Keep the client socket open, we want synchronous commits. */
- ret = LOCAL_RET_STOLEN;
+ case CT_COMMIT:
+ dlog(LOG_NOTICE, "committing conntrack cache");
+ STATE_SYNC(commit).rq[0].cb = STATE_SYNC(external)->ct.commit;
+ STATE_SYNC(commit).rq[1].cb = NULL;
+ ret = local_commit(fd);
break;
case RESET_TIMERS:
if (!alarm_pending(&STATE_SYNC(reset_cache_alarm))) {
@@ -509,29 +607,29 @@ static int local_handler_sync(int fd, int type, void *data)
CONFIG(purge_timeout), 0);
}
break;
- case FLUSH_CACHE:
+ case CT_FLUSH_CACHE:
/* inmediate flush, remove pending flush scheduled if any */
del_alarm(&STATE_SYNC(reset_cache_alarm));
dlog(LOG_NOTICE, "flushing caches");
- STATE(mode)->internal->flush();
- STATE_SYNC(external)->flush();
+ STATE(mode)->internal->ct.flush();
+ STATE_SYNC(external)->ct.flush();
break;
- case FLUSH_INT_CACHE:
+ case CT_FLUSH_INT_CACHE:
/* inmediate flush, remove pending flush scheduled if any */
del_alarm(&STATE_SYNC(reset_cache_alarm));
dlog(LOG_NOTICE, "flushing internal cache");
- STATE(mode)->internal->flush();
+ STATE(mode)->internal->ct.flush();
break;
- case FLUSH_EXT_CACHE:
+ case CT_FLUSH_EXT_CACHE:
dlog(LOG_NOTICE, "flushing external cache");
- STATE_SYNC(external)->flush();
+ STATE_SYNC(external)->ct.flush();
break;
case KILL:
killer(0);
break;
case STATS:
- STATE(mode)->internal->stats(fd);
- STATE_SYNC(external)->stats(fd);
+ STATE(mode)->internal->ct.stats(fd);
+ STATE_SYNC(external)->ct.stats(fd);
dump_traffic_stats(fd);
multichannel_stats(STATE_SYNC(channel), fd);
dump_stats_sync(fd);
@@ -541,8 +639,8 @@ static int local_handler_sync(int fd, int type, void *data)
multichannel_stats(STATE_SYNC(channel), fd);
break;
case STATS_CACHE:
- STATE(mode)->internal->stats_ext(fd);
- STATE_SYNC(external)->stats_ext(fd);
+ STATE(mode)->internal->ct.stats_ext(fd);
+ STATE_SYNC(external)->ct.stats_ext(fd);
break;
case STATS_LINK:
multichannel_stats_extended(STATE_SYNC(channel),
@@ -551,6 +649,63 @@ static int local_handler_sync(int fd, int type, void *data)
case STATS_QUEUE:
queue_stats_show(fd);
break;
+ case EXP_STATS:
+ if (!(CONFIG(flags) & CTD_EXPECT))
+ break;
+
+ STATE(mode)->internal->exp.stats(fd);
+ STATE_SYNC(external)->exp.stats(fd);
+ dump_traffic_stats(fd);
+ multichannel_stats(STATE_SYNC(channel), fd);
+ dump_stats_sync(fd);
+ break;
+ case EXP_DUMP_INTERNAL:
+ if (!(CONFIG(flags) & CTD_EXPECT))
+ break;
+
+ if (fork_process_new(CTD_PROC_ANY, 0, NULL, NULL) == 0) {
+ STATE(mode)->internal->exp.dump(fd, NFCT_O_PLAIN);
+ exit(EXIT_SUCCESS);
+ }
+ break;
+ case EXP_DUMP_EXTERNAL:
+ if (!(CONFIG(flags) & CTD_EXPECT))
+ break;
+
+ if (fork_process_new(CTD_PROC_ANY, 0, NULL, NULL) == 0) {
+ STATE_SYNC(external)->exp.dump(fd, NFCT_O_PLAIN);
+ exit(EXIT_SUCCESS);
+ }
+ break;
+ case EXP_COMMIT:
+ if (!(CONFIG(flags) & CTD_EXPECT))
+ break;
+
+ dlog(LOG_NOTICE, "committing expectation cache");
+ STATE_SYNC(commit).rq[0].cb = STATE_SYNC(external)->exp.commit;
+ STATE_SYNC(commit).rq[1].cb = NULL;
+ local_commit(fd);
+ break;
+ case ALL_FLUSH_CACHE:
+ dlog(LOG_NOTICE, "flushing caches");
+ STATE(mode)->internal->ct.flush();
+ STATE_SYNC(external)->ct.flush();
+ if (CONFIG(flags) & CTD_EXPECT) {
+ STATE(mode)->internal->exp.flush();
+ STATE_SYNC(external)->exp.flush();
+ }
+ break;
+ case ALL_COMMIT:
+ dlog(LOG_NOTICE, "committing all external caches");
+ STATE_SYNC(commit).rq[0].cb = STATE_SYNC(external)->ct.commit;
+ if (CONFIG(flags) & CTD_EXPECT) {
+ STATE_SYNC(commit).rq[1].cb =
+ STATE_SYNC(external)->exp.commit;
+ } else {
+ STATE_SYNC(commit).rq[1].cb = NULL;
+ }
+ local_commit(fd);
+ break;
default:
if (STATE_SYNC(sync)->local)
ret = STATE_SYNC(sync)->local(fd, type, data);
diff --git a/src/sync-notrack.c b/src/sync-notrack.c
index c4ad941..a7df4e7 100644
--- a/src/sync-notrack.c
+++ b/src/sync-notrack.c
@@ -1,6 +1,7 @@
/*
- * (C) 2008 by Pablo Neira Ayuso <pablo@netfilter.org>
- *
+ * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2011 by Vyatta Inc. <http://www.vyatta.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
@@ -33,12 +34,14 @@ static struct alarm_block alive_alarm;
struct cache_notrack {
struct queue_node qnode;
+ struct cache_object *obj;
};
static void cache_notrack_add(struct cache_object *obj, void *data)
{
struct cache_notrack *cn = data;
queue_node_init(&cn->qnode, Q_ELEM_OBJ);
+ cn->obj = obj;
}
static void cache_notrack_del(struct cache_object *obj, void *data)
@@ -68,15 +71,15 @@ static void tx_queue_add_ctlmsg(uint32_t flags, uint32_t from, uint32_t to)
ack->from = from;
ack->to = to;
- queue_add(STATE_SYNC(tx_queue), &qobj->qnode);
+ if (queue_add(STATE_SYNC(tx_queue), &qobj->qnode) < 0)
+ queue_object_free(qobj);
}
static int do_cache_to_tx(void *data1, void *data2)
{
struct cache_object *obj = data2;
- struct cache_notrack *cn =
- cache_get_extra(STATE(mode)->internal->data, obj);
- if (queue_add(STATE_SYNC(tx_queue), &cn->qnode))
+ struct cache_notrack *cn = cache_get_extra(obj);
+ if (queue_add(STATE_SYNC(tx_queue), &cn->qnode) > 0)
cache_object_get(obj);
return 0;
}
@@ -86,7 +89,7 @@ static int kernel_resync_cb(enum nf_conntrack_msg_type type,
{
struct nethdr *net;
- net = BUILD_NETMSG(ct, NET_T_STATE_NEW);
+ net = BUILD_NETMSG_FROM_CT(ct, NET_T_STATE_CT_NEW);
multichannel_send(STATE_SYNC(channel), net);
return NFCT_CB_CONTINUE;
@@ -99,7 +102,7 @@ static void kernel_resync(void)
u_int32_t family = AF_UNSPEC;
int ret;
- h = nfct_open(CONNTRACK, 0);
+ h = nfct_open(CONFIG(netlink).subsys_id, 0);
if (h == NULL) {
dlog(LOG_ERR, "can't allocate memory for the internal cache");
return;
@@ -126,7 +129,9 @@ static int notrack_local(int fd, int type, void *data)
if (CONFIG(sync).internal_cache_disable) {
kernel_resync();
} else {
- cache_iterate(STATE(mode)->internal->data,
+ cache_iterate(STATE(mode)->internal->ct.data,
+ NULL, do_cache_to_tx);
+ cache_iterate(STATE(mode)->internal->exp.data,
NULL, do_cache_to_tx);
}
break;
@@ -147,7 +152,9 @@ static int digest_msg(const struct nethdr *net)
if (CONFIG(sync).internal_cache_disable) {
kernel_resync();
} else {
- cache_iterate(STATE(mode)->internal->data,
+ cache_iterate(STATE(mode)->internal->ct.data,
+ NULL, do_cache_to_tx);
+ cache_iterate(STATE(mode)->internal->exp.data,
NULL, do_cache_to_tx);
}
return MSG_CTL;
@@ -190,19 +197,17 @@ static int tx_queue_xmit(struct queue_node *n, const void *data2)
break;
}
case Q_ELEM_OBJ: {
- struct cache_ftfw *cn;
- struct cache_object *obj;
+ struct cache_notrack *cn;
int type;
struct nethdr *net;
- cn = (struct cache_ftfw *)n;
- obj = cache_data_get_object(STATE(mode)->internal->data, cn);
- type = object_status_to_network_type(obj->status);;
- net = BUILD_NETMSG(obj->ct, type);
+ cn = (struct cache_notrack *)n;
+ type = object_status_to_network_type(cn->obj);
+ net = cn->obj->cache->ops->build_msg(cn->obj, type);
multichannel_send(STATE_SYNC(channel), net);
queue_del(n);
- cache_object_put(obj);
+ cache_object_put(cn->obj);
break;
}
}
@@ -217,9 +222,8 @@ static void notrack_xmit(void)
static void notrack_enqueue(struct cache_object *obj, int query)
{
- struct cache_notrack *cn =
- cache_get_extra(STATE(mode)->internal->data, obj);
- if (queue_add(STATE_SYNC(tx_queue), &cn->qnode))
+ struct cache_notrack *cn = cache_get_extra(obj);
+ if (queue_add(STATE_SYNC(tx_queue), &cn->qnode) > 0)
cache_object_get(obj);
}
@@ -236,7 +240,8 @@ static void tx_queue_add_ctlmsg2(uint32_t flags)
ctl->type = NET_T_CTL;
ctl->flags = flags;
- queue_add(STATE_SYNC(tx_queue), &qobj->qnode);
+ if (queue_add(STATE_SYNC(tx_queue), &qobj->qnode) < 0)
+ queue_object_free(qobj);
}
static void do_alive_alarm(struct alarm_block *a, void *data)