diff options
author | Gaurav Sinha <gaurav.sinha@vyatta.com> | 2012-01-18 12:05:50 -0800 |
---|---|---|
committer | Gaurav Sinha <gaurav.sinha@vyatta.com> | 2012-01-18 12:05:50 -0800 |
commit | e72ca98329a25414108e2af350c4014de9e8f736 (patch) | |
tree | dfd32da0647916edf9a055e44d9d59596a3721a8 /src | |
parent | fa0e506210775233cb34513d45878991ef50aae6 (diff) | |
parent | ca37a710d526d17490ebdc3af760bfddd316426d (diff) | |
download | conntrack-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/.gitignore | 6 | ||||
-rw-r--r-- | src/Makefile.am | 11 | ||||
-rw-r--r-- | src/Makefile.in | 134 | ||||
-rw-r--r-- | src/build.c | 191 | ||||
-rw-r--r-- | src/cache-ct.c | 356 | ||||
-rw-r--r-- | src/cache-exp.c | 308 | ||||
-rw-r--r-- | src/cache.c | 163 | ||||
-rw-r--r-- | src/conntrack.c | 915 | ||||
-rw-r--r-- | src/external_cache.c | 130 | ||||
-rw-r--r-- | src/external_inject.c | 134 | ||||
-rw-r--r-- | src/filter.c | 98 | ||||
-rw-r--r-- | src/internal_bypass.c | 222 | ||||
-rw-r--r-- | src/internal_cache.c | 287 | ||||
-rw-r--r-- | src/lock.c | 0 | ||||
-rw-r--r-- | src/log.c | 37 | ||||
-rw-r--r-- | src/main.c | 81 | ||||
-rw-r--r-- | src/netlink.c | 139 | ||||
-rw-r--r-- | src/network.c | 24 | ||||
-rw-r--r-- | src/parse.c | 293 | ||||
-rw-r--r-- | src/read_config_lex.l | 5 | ||||
-rw-r--r-- | src/read_config_yy.y | 87 | ||||
-rw-r--r-- | src/run.c | 449 | ||||
-rw-r--r-- | src/stats-mode.c | 50 | ||||
-rw-r--r-- | src/sync-alarm.c | 22 | ||||
-rw-r--r-- | src/sync-ftfw.c | 49 | ||||
-rw-r--r-- | src/sync-mode.c | 265 | ||||
-rw-r--r-- | src/sync-notrack.c | 47 |
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 @@ -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) @@ -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; } @@ -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) |