From ad31f852c3454136bdbfeb7f222cb9c175f13c1c Mon Sep 17 00:00:00 2001 From: "/C=EU/ST=EU/CN=Pablo Neira Ayuso/emailAddress=pablo@netfilter.org" Date: Mon, 16 Apr 2007 17:55:00 +0000 Subject: initial import of the conntrack daemon to Netfilter SVN --- daemon/AUTHORS | 1 + daemon/CHANGELOG | 184 ++++++ daemon/CONTRIBUTORS | 3 + daemon/INSTALL | 199 ++++++ daemon/Make_global.am | 1 + daemon/Makefile.am | 21 + daemon/TODO | 18 + daemon/autogen.sh | 18 + daemon/configure.in | 106 +++ daemon/examples/Makefile.am | 1 + daemon/examples/debian.conntrackd.init.d | 48 ++ daemon/examples/stats/Makefile.am | 1 + daemon/examples/stats/conntrackd.conf | 69 ++ daemon/examples/sync/Makefile.am | 1 + daemon/examples/sync/nack/Makefile.am | 2 + daemon/examples/sync/nack/README | 1 + daemon/examples/sync/nack/node1/Makefile.am | 1 + daemon/examples/sync/nack/node1/conntrackd.conf | 125 ++++ daemon/examples/sync/nack/node1/keepalived.conf | 38 ++ daemon/examples/sync/nack/node2/Makefile.am | 1 + daemon/examples/sync/nack/node2/conntrackd.conf | 124 ++++ daemon/examples/sync/nack/node2/keepalived.conf | 38 ++ daemon/examples/sync/nack/script_backup.sh | 3 + daemon/examples/sync/nack/script_master.sh | 5 + daemon/examples/sync/persistent/Makefile.am | 2 + daemon/examples/sync/persistent/README | 1 + daemon/examples/sync/persistent/node1/Makefile.am | 1 + .../examples/sync/persistent/node1/conntrackd.conf | 130 ++++ .../examples/sync/persistent/node1/keepalived.conf | 38 ++ daemon/examples/sync/persistent/node2/Makefile.am | 1 + .../examples/sync/persistent/node2/conntrackd.conf | 130 ++++ .../examples/sync/persistent/node2/keepalived.conf | 38 ++ daemon/examples/sync/persistent/script_backup.sh | 3 + daemon/examples/sync/persistent/script_master.sh | 4 + daemon/include/Makefile.am | 5 + daemon/include/alarm.h | 13 + daemon/include/buffer.h | 32 + daemon/include/cache.h | 92 +++ daemon/include/conntrackd.h | 174 +++++ daemon/include/debug.h | 53 ++ daemon/include/hash.h | 47 ++ daemon/include/ignore.h | 12 + daemon/include/jhash.h | 146 +++++ daemon/include/linux_list.h | 725 +++++++++++++++++++++ daemon/include/local.h | 29 + daemon/include/log.h | 10 + daemon/include/mcast.h | 48 ++ daemon/include/network.h | 34 + daemon/include/slist.h | 41 ++ daemon/include/state_helper.h | 20 + daemon/include/sync.h | 23 + daemon/include/us-conntrack.h | 13 + daemon/src/Makefile.am | 22 + daemon/src/alarm.c | 141 ++++ daemon/src/buffer.c | 136 ++++ daemon/src/cache.c | 446 +++++++++++++ daemon/src/cache_iterators.c | 229 +++++++ daemon/src/cache_lifetime.c | 65 ++ daemon/src/cache_timer.c | 72 ++ daemon/src/checksum.c | 32 + daemon/src/hash.c | 199 ++++++ daemon/src/ignore_pool.c | 136 ++++ daemon/src/local.c | 159 +++++ daemon/src/lock.c | 32 + daemon/src/log.c | 57 ++ daemon/src/main.c | 302 +++++++++ daemon/src/mcast.c | 287 ++++++++ daemon/src/netlink.c | 326 +++++++++ daemon/src/network.c | 282 ++++++++ daemon/src/proxy.c | 124 ++++ daemon/src/read_config_lex.l | 125 ++++ daemon/src/read_config_yy.y | 550 ++++++++++++++++ daemon/src/run.c | 227 +++++++ daemon/src/state_helper.c | 44 ++ daemon/src/state_helper_tcp.c | 35 + daemon/src/stats-mode.c | 151 +++++ daemon/src/sync-mode.c | 416 ++++++++++++ daemon/src/sync-nack.c | 309 +++++++++ daemon/src/sync-notrack.c | 127 ++++ daemon/src/traffic_stats.c | 54 ++ 80 files changed, 7959 insertions(+) create mode 100644 daemon/AUTHORS create mode 100644 daemon/CHANGELOG create mode 100644 daemon/CONTRIBUTORS create mode 100644 daemon/INSTALL create mode 100644 daemon/Make_global.am create mode 100644 daemon/Makefile.am create mode 100644 daemon/TODO create mode 100755 daemon/autogen.sh create mode 100644 daemon/configure.in create mode 100644 daemon/examples/Makefile.am create mode 100644 daemon/examples/debian.conntrackd.init.d create mode 100644 daemon/examples/stats/Makefile.am create mode 100644 daemon/examples/stats/conntrackd.conf create mode 100644 daemon/examples/sync/Makefile.am create mode 100644 daemon/examples/sync/nack/Makefile.am create mode 100644 daemon/examples/sync/nack/README create mode 100644 daemon/examples/sync/nack/node1/Makefile.am create mode 100644 daemon/examples/sync/nack/node1/conntrackd.conf create mode 100644 daemon/examples/sync/nack/node1/keepalived.conf create mode 100644 daemon/examples/sync/nack/node2/Makefile.am create mode 100644 daemon/examples/sync/nack/node2/conntrackd.conf create mode 100644 daemon/examples/sync/nack/node2/keepalived.conf create mode 100755 daemon/examples/sync/nack/script_backup.sh create mode 100755 daemon/examples/sync/nack/script_master.sh create mode 100644 daemon/examples/sync/persistent/Makefile.am create mode 100644 daemon/examples/sync/persistent/README create mode 100644 daemon/examples/sync/persistent/node1/Makefile.am create mode 100644 daemon/examples/sync/persistent/node1/conntrackd.conf create mode 100644 daemon/examples/sync/persistent/node1/keepalived.conf create mode 100644 daemon/examples/sync/persistent/node2/Makefile.am create mode 100644 daemon/examples/sync/persistent/node2/conntrackd.conf create mode 100644 daemon/examples/sync/persistent/node2/keepalived.conf create mode 100755 daemon/examples/sync/persistent/script_backup.sh create mode 100755 daemon/examples/sync/persistent/script_master.sh create mode 100644 daemon/include/Makefile.am create mode 100644 daemon/include/alarm.h create mode 100644 daemon/include/buffer.h create mode 100644 daemon/include/cache.h create mode 100644 daemon/include/conntrackd.h create mode 100644 daemon/include/debug.h create mode 100644 daemon/include/hash.h create mode 100644 daemon/include/ignore.h create mode 100644 daemon/include/jhash.h create mode 100644 daemon/include/linux_list.h create mode 100644 daemon/include/local.h create mode 100644 daemon/include/log.h create mode 100644 daemon/include/mcast.h create mode 100644 daemon/include/network.h create mode 100644 daemon/include/slist.h create mode 100644 daemon/include/state_helper.h create mode 100644 daemon/include/sync.h create mode 100644 daemon/include/us-conntrack.h create mode 100644 daemon/src/Makefile.am create mode 100644 daemon/src/alarm.c create mode 100644 daemon/src/buffer.c create mode 100644 daemon/src/cache.c create mode 100644 daemon/src/cache_iterators.c create mode 100644 daemon/src/cache_lifetime.c create mode 100644 daemon/src/cache_timer.c create mode 100644 daemon/src/checksum.c create mode 100644 daemon/src/hash.c create mode 100644 daemon/src/ignore_pool.c create mode 100644 daemon/src/local.c create mode 100644 daemon/src/lock.c create mode 100644 daemon/src/log.c create mode 100644 daemon/src/main.c create mode 100644 daemon/src/mcast.c create mode 100644 daemon/src/netlink.c create mode 100644 daemon/src/network.c create mode 100644 daemon/src/proxy.c create mode 100644 daemon/src/read_config_lex.l create mode 100644 daemon/src/read_config_yy.y create mode 100644 daemon/src/run.c create mode 100644 daemon/src/state_helper.c create mode 100644 daemon/src/state_helper_tcp.c create mode 100644 daemon/src/stats-mode.c create mode 100644 daemon/src/sync-mode.c create mode 100644 daemon/src/sync-nack.c create mode 100644 daemon/src/sync-notrack.c create mode 100644 daemon/src/traffic_stats.c (limited to 'daemon') diff --git a/daemon/AUTHORS b/daemon/AUTHORS new file mode 100644 index 0000000..e6c2f6b --- /dev/null +++ b/daemon/AUTHORS @@ -0,0 +1 @@ +Pablo Neira Ayuso diff --git a/daemon/CHANGELOG b/daemon/CHANGELOG new file mode 100644 index 0000000..afab61d --- /dev/null +++ b/daemon/CHANGELOG @@ -0,0 +1,184 @@ +version 0.9.3 (yet unreleased) +------------------------------ +o fix commit of confirmed expectations (reported by Nishit Shah) +o fix double increment of counters in cache_update_force() (Niko Tyni) +o nl_dump_handler must return NFCT_CB_CONTINUE (Niko Tyni) +o initialize buffer in nl_event_handler() and nl_dump_handler() (Niko Tyni) +o CacheCommit value can be set via conntrackd.conf for the NACK approach +o fix leaks in the hashtable/cache flush path (Niko Tyni) +o fix leak if a connection already exists in the cache (Niko Tyni) +o introduce a new header that encapsulates netlink messages +o remove all '_entry' tail from all functions in cache.c +o split cache.c: move cache iterators to file cache_iterators.c +o fix inconsistencies in the cache API related to counters +o cleanup 'usage' message +o fix typo in examples/sync/nack/node1/conntrackd.conf +o introduce message checksumming as described in RFC1071 (enabled by default) +o major cleanups in the synchronization code +o just warn once that the maximum netlink socket buffer has been reached +o fix ignore conntrack entries by IP and introduce ignore pool abstraction layer +o introduce netlink socket buffer overrun handler +o constification of hash, compare and hashtable_test functions in hash.c +o introduce ACKnowledgement mechanisms to reduce the size of the resend queue +o remove OK messages at startup since provide useless data +o fix compilation warning in mcast.c: recvfrom takes socklen_t not size_t +o add a lock per buffer: makes buffer code thread safe +o introduce 'Replicate' clause to explicitely set states to be replicated +o kill cache feature abuse: introduce nicer cache hooks for sync algorithms +o fix oversized buffer allocated in the stack in the cache functions +o add support to dump internal/external cache in XML format '-x' + +version 0.9.2 (2006/01/17) +-------------------------- +o remove spamming packet lost messages +o generalize network netlink sequence tracking +o fix bogus error message on resync `-R' +o fix endianess issues in the network netlink message +o introduce generic netlink multicast primitives to send and receive +o fix bogus replayed multicast message due to sequence numbering wraparound +o introduce counter for malformed netlink messages received +o introduce a new syntax for the `Sync' section in the configuration file +o several cleanups and remove unused variables +o add autostuff to include examples in the tarball (reported by Victor Lozano) +o use the new API available in libnetfilter_conntrack-0.0.50 +o implement a NACK based protocol for replication + +version 0.9.1 (2006/11/06) +-------------------------- +o conntrackd requires kernel >= 2.6.18 +o remove bogus TIMERS_MODE constant +o implement bulk mode '-B': first works to address the preemption issue +o fix minor reduction conflicts in the configfile grammar +o check for CAP_NET_ADMIN instead of requiring root privileges +o check that linux/capability.h exists +o fix formatting at dump statistics '-s' +o move dump traffic stats before multicast traffic stats +o move event and dump handler to a generic infrastructure: kill events.c file +o kill unused function inc_ct_stats +o kill file resync.h +o cleanup broadcast_sync: renamed to mcast_send_sync +o sed 's/perror/debug/g' local.c +o fix bogus increment of update_fail stats at dump stage +o display descriptive error if we can't connect to conntrackd via UNIX socket +o remove debugging message from alarm.c +o move dump_mcast_stats to mcast.c where it really belongs +o rename stats.c to traffic_stats.c +o check for replayed/lost multicast message: simple seq tracking w/o recovery +o reissue nfnl_catch on ENOENT error: a message for other subsystem +o remove test/ directory in tree +o improve cache commit stats +o kill last_commit and last_flush from cache statistics: use the logfile +o recover cache naming for dump stats `-s' +o display multicast sequence tracking statistics: packets lost and replayed +o zero ct_sync_state and ct_stats_state structures after allocation +o improve keepalived scripts: + - resync with conntrack table on transition to master + - send bulk on transition to backup +o implement alarm cascade of ten levels +o implement timer cache flavour: limited life of entries in the external cache +o implement a global lock that protects operation with conntrack entries +o remove debug checking in cache_del_entry +o set a reduced timeout for committed entries: 180 seconds by default +o update comments on the sync-mode code +o introduce delay destroy messages facility +o increase timer for external states from 60 to 180 seconds +o remove unused replicate/dont_replicated constants +o fix cache entry clashing issue (reported by Maik Hentsche) +o fix bogus increment of error stats in the external cache +o remove pollution generated by `[REQ] cache dump' message from logfile + +version 0.9.0 (2006/09/17) +-------------------------- +o implement initial for IPv6 (untested) +o implement generic extensible cache: kill the internal and external caches +o implement persistence cache feature +o implement lifetime cache feature +o modify UNIX facilities identification numbers: + separate master conntrack facilities and internal plugin facilities +o break backward compatibility of configuration file: + remove IgnoreLoopback, use IgnoreTrafficFor instead + remove IgnoreMulticastTraffic, use IgnoreTrafficFor instead +o merge event/event_subsys and sync/sync_subsys initialization to run.c +o improve control of the iteration process in the hashtables +o fix wrong locking in the alarm thread +o supersede AcceptNAT by StripNAT clause +o replace ignore traffic array by a hashtable +o move lockfile checking before daemonization +o on initialization error give a descriptive error +o introduce netlink socket size grown limitator +o introduce force resync with master conntrack table facility '-R' +o ignore SIGPIPE signal +o kill post_step since it is not used anymore + +version 0.8.3 (2006/09/03) +-------------------------- +Author: Maik Hentsche + +o Fix typo in conntrackd -h +o Disable debugging messages by default +o No signals while signals handlings +o Add extra checkings at forking +o Check maximum size for file passed via -C + +Author: Pablo Neira Ayuso + +o retry select() if EINTR is returned (Reported by Maik Hentsche) +o Fix bug in slist_for_each_entry (Reported by Maik Hetsche) +o Signal handler registration done after intialization +o Implement alarm thread (based on Maik Hentsche's patch) +o Fix segfault on conntrackd -k (Reported by Maik Hentsche) +o Fix bug on alarm removal (Reported by Maik Hentsche) +o configure stops if bison, flex or yacc are not installed + +version 0.8.2 (2006/07/05) +-------------------------- +o RelaxTransitions clause introduced in Sync mode +o multicast messages sequence tracking +o SocketBufferSize clause to set up the netlink socket buffer +o use new libnfnetlink API to solve limitations of nfnl_listen +o extra sanity checkings for netlink multicast messages +o improve statistics +o tons of cleanups 8) + +version 0.8.1 (2006/06/13) +-------------------------- +o -f now just flushes the internal and external caches +o -F flushes the master conntrack table +o fix segfault under heavy load and signal received +o added -S mode for statistics: still needs more thinking + +version 0.8.0 (2006/06/11) +-------------------------- +o more work to generalize the daemon: now it's ready to implement +modular support for adaptive timers and conntrack statistics, time +to implement them ;). This is *still* a work in progress. + +version 0.7.2 (2006/06/05) +-------------------------- +o stupid bug in normal and alarm caches initialization: flush unset +o fix racy signal handling + +version 0.7.1 (2006/06/05) +-------------------------- +o Bugfix for multicast sockets communication + +version 0.7 (2006/06/01) +------------------------ +o Major code re-structuration: internal and external cache abstraction +o sequence tracking for event messages +o expect more changes, I still dislike some stuff in its current status ;) + +version 0.6 (2006/05/31) +------------------------ +o Lock file support +o use new API nfct_conntrack_event_raw +o major code clean ups + +version 0.5 (2006/05/30) +------------------------- +o Fix multicast server binds to wrong interface +o Include clause `IgnoreProtocol', deprecates IgnoreUDP and IgnoreICMP + +version 0.4 (2006/05/29) +------------------------ +o Initial release diff --git a/daemon/CONTRIBUTORS b/daemon/CONTRIBUTORS new file mode 100644 index 0000000..c5e40b4 --- /dev/null +++ b/daemon/CONTRIBUTORS @@ -0,0 +1,3 @@ +Maik Hentsche : + - Feedback & Brainstorming + - Bug hunting diff --git a/daemon/INSTALL b/daemon/INSTALL new file mode 100644 index 0000000..0de8dc0 --- /dev/null +++ b/daemon/INSTALL @@ -0,0 +1,199 @@ +Copyright (C) 2006-2007 Pablo Neira Ayuso + +1.Basic Installation +==================== + + To compile and install 'conntrackd' just follow the classical steps: + + $ ./configure + $ make + # make install + # mkdir /etc/conntrackd/ + +2.1. Synchronization Mode +========================= + + Conntrackd can replicate the status of the connections that are currently + being processed by your stateful firewall based on Linux. This section + describes how to setup the daemon in synchronization mode: + +2.1.1. Requirements + + You have to install the following software in order to get conntrackd working, + make sure that you have installed them correctly before going forward: + + o linux kernel version >= 2.6.18 (http://www.kernel.org) with support for: + - connection tracking system (quite obvious ;) + - nfnetlink + - ctnetlink (ip_conntrack_netlink) + - connection tracking event notification API + + o libnfnetlink: the netfilter netlink library + + Since conntrackd version 0.9.2 you can used the official release availble at + http://www.netfilter.org/projects/libnfnetlink/files/ + + Up to conntrackd version 0.9.1 use the unofficial release available at the + download section + + o libnetfilter_conntrack: the netfilter conntrack library + + Since conntrackd version 0.9.2 you can used the official release availble at + http://www.netfilter.org/projects/libnetfilter_conntrack/files/ + + Up to conntrackd version 0.9.1 use the unnoficial release available at the + download section + + o Keepalived version 1.x (http://www.keepalived.org) + check if your distribution comes with a recent version + +2.1.2. Configuration + + 1) Setting up keepalived + + There is an example file available inside the conntrackd tarball: + + For node 1: conntrackd-x.x.x/examples/sync/node1/keepalived.conf + For node 2: conntrackd-x.x.x/examples/sync/node2/keepalived.conf + + These files can be used to set up a simple VRRP cluster composed of + two machines that hold the virtual IPs 192.168.0.100 on eth0 and + 192.168.1.100 on eth1. + + If you are not familiar with keepalived, please read the official + docs available at http://www.keepalived.org + + Please, make sure that keepalived is correctly working before passing + to step 2) + + 2) Setting up conntrackd + + To setup 'conntrackd' in synchronization mode, you have to put the + configuration file in the directory /etc/conntrackd. + + On node 1: + # cp examples/sync/_type_/node1/conntrackd.conf /etc/conntrackd.conf + + On node 2: + # cp examples/sync/_type_/node1/conntrackd.conf /etc/conntrackd.conf + + Where _type_ is the synchronization type selected, currently there are + two: the persistent mode and the NACK mode. The persistent mode consumes + more resources than the NACK mode, however the NACK mode is still + experimental + + Do not forget to edit the files in order to adapt them to the + setting that you are deploying. + + Note: If you don't want to put the config file under /etc/conntrackd, + just tell conntrackd where to find it passing the option -C + + 3) Running conntrackd + + Conntrackd can run in console mode, in that case just type 'conntrackd', + otherwise, if you want to run it in daemon mode the type 'conntrackd -d'. + + 4) Checking that conntrackd is working fine + + Conntrackd comes with several facilities to check its status: + + - Dump the cache of connections that are currently being processed by + this node (aka. internal cache): + + # conntrackd -i + + - Dump the cache of connections that has been transfered from + others active nodes in the network (aka. external cache) + + # conntrackd -e + + - Dump statistics collected by the replication daemon: + + # conntrackd -s + + 5) Setting up interaction with keepalived + + If keepalived detects the failure of the active node, then it designates + a candidate node that will replace the failing active. On such event, + the external cache, eg. the cache that contains the connections processed + by other nodes, must be commited. To commit the external cache, just type: + + # conntrackd -c + + See that keepalived provides a shell script interface to interact with + other programs, so we can automate the process of commiting the external + cache by introducing the following line in the keepalived file: + + notify_master /etc/conntrackd/script_master.sh + + The script 'script_master.sh' just the following: + + #!/bin/sh + /usr/sbin/conntrackd -c + + Therefore, on failure event, the candidate node takes over the virtual + IPs and the connections that the failing active was processing. Observe + that this file differs for the NACK mode. + + 6) Disable TCP window tracking + + Until the appropiate patches don't go into kernel mainline, you will have + to disable TCP window tracking, consider this as a temporary solution: + + # echo 1 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_be_liberal + +2.2. Statistics mode +==================== + + Conntrackd can also run as statistics daemon, if you are not interested in + this mode, just skip it. It is not required in order to get the + synchronization mode working. This section details how to setup the daemon + in statistics mode: + +2.2.1. Requirements + + You have to install the following software in order to get conntrackd working, + make sure that you have them installed correctly before going forward: + + o linux kernel version >= 2.6.18 (http://www.kernel.org) with support for: + - connection tracking system + - nfnetlink + - ctnetlink (ip_conntrack_netlink) + - connection tracking event notification API + + o libnfnetlink: the netfilter netlink library + + Since conntrackd version 0.9.2 you can used the official release availble at + http://www.netfilter.org/projects/libnfnetlink/files/ + + Up to conntrackd version 0.9.1 use the unofficial release available at the + download section + + o libnetfilter_conntrack: the netfilter conntrack library + + Since conntrackd version 0.9.2 you can used the official release availble at + http://www.netfilter.org/projects/libnetfilter_conntrack/files/ + + Up to conntrackd version 0.9.1 use the unnoficial release available at the + download section + +2.2.2. Configuration + + Setting up conntrackd in statistics mode is rather easy. Just copy the + configuration file + + # cp examples/stats/conntrackd.conf /etc/conntrackd.conf + +2.2.3. Running conntrackd in statistics mode + + To run conntrackd in statistics mode: + + # conntrackd -S + + Alternatively, you can run conntrackd in daemon mode: + + # conntrackd -S -d + + In order to dump the statistics, just type: + + # conntrackd -s diff --git a/daemon/Make_global.am b/daemon/Make_global.am new file mode 100644 index 0000000..685add7 --- /dev/null +++ b/daemon/Make_global.am @@ -0,0 +1 @@ +INCLUDES=$(all_includes) -I$(top_srcdir)/include diff --git a/daemon/Makefile.am b/daemon/Makefile.am new file mode 100644 index 0000000..998f4c6 --- /dev/null +++ b/daemon/Makefile.am @@ -0,0 +1,21 @@ +include Make_global.am + +# not a GNU package. You can remove this line, if +# have all needed files, that a GNU package needs +AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 + +# man_MANS = "" +# EXTRA_DIST = $(man_MANS) Make_global.am debian +EXTRA_DIST = Make_global.am CHANGELOG TODO + +SUBDIRS = src +DIST_SUBDIRS = include src examples +LINKOPTS = -lnfnetlink -lnetfilter_conntrack -lpthread +AM_CFLAGS = -g + +$(OBJECTS): libtool +libtool: $(LIBTOOL_DEPS) + $(SHELL) ./config.status --recheck + +dist-hook: + rm -rf `find $(distdir)/debian -name .svn` diff --git a/daemon/TODO b/daemon/TODO new file mode 100644 index 0000000..130b1f8 --- /dev/null +++ b/daemon/TODO @@ -0,0 +1,18 @@ +There are several tasks that are pending to be done, I have classified them +by dificulty levels: + +Relatively easy +=============== + +- test ipv6 support +- improve shell scripts +- test NACK based protocol +- manpage for conntrackd + +Requires some work +================== + +- study better keepalived transitions +- implement support for TCP window tracking (patches are on the table) + - at the moment you have to disable it: + echo 1 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_be_liberal diff --git a/daemon/autogen.sh b/daemon/autogen.sh new file mode 100755 index 0000000..e76d3ef --- /dev/null +++ b/daemon/autogen.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +run () +{ + echo "running: $*" + eval $* + + if test $? != 0 ; then + echo "error: while running '$*'" + exit 1 + fi +} + +run aclocal +run libtoolize -f +#run autoheader +run automake -a +run autoconf diff --git a/daemon/configure.in b/daemon/configure.in new file mode 100644 index 0000000..92e512a --- /dev/null +++ b/daemon/configure.in @@ -0,0 +1,106 @@ +AC_INIT(conntrackd, 0.9.2, pablo@netfilter.org) + +AC_CANONICAL_SYSTEM + +AM_INIT_AUTOMAKE + +AC_PROG_CC +AM_PROG_LIBTOOL +AC_PROG_INSTALL +AC_PROG_LN_S +AM_PROG_LEX +AC_PROG_YACC + +case $target in +*-*-linux*) ;; +*) AC_MSG_ERROR([Linux only, dude!]);; +esac + +AC_CHECK_PROGS(XYACC,$YACC bison yacc,none) +if test "$XYACC" = "none" +then + echo "*** Error: No suitable bison/yacc found. ***" + echo " Please install the 'bison' package." + exit 1 +fi +AC_CHECK_PROGS(XLEX,$LEX flex lex,none) +if test "$XLEX" = "none" +then + echo "*** Error: No suitable bison/yacc found. ***" + echo " Please install the 'bison' package." + exit 1 +fi + +AC_CHECK_HEADERS([linux/capability.h],, [AC_MSG_ERROR([Cannot find linux/capabibility.h])]) + +# Checks for libraries. +# FIXME: Replace `main' with a function in `-lc': +dnl AC_CHECK_LIB([c], [main]) +# FIXME: Replace `main' with a function in `-ldl': + +AC_CHECK_LIB([nfnetlink], [nfnl_talk] ,,,[-lnfnetlink]) +AC_CHECK_LIB([netfilter_conntrack], [nfct_dump_conntrack_table] ,,,[-lnetfilter_conntrack]) +AC_CHECK_LIB([pthread], [pthread_create] ,,,[-lpthread]) + +AC_CHECK_HEADERS(arpa/inet.h) +dnl check for inet_pton +AC_CHECK_FUNCS(inet_pton) +dnl Some systems have it, but not IPv6 +if test "$ac_cv_func_inet_pton" = "yes" ; then +AC_MSG_CHECKING(if inet_pton supports IPv6) +AC_TRY_RUN( + [ +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +int main() + { + struct in6_addr addr6; + if (inet_pton(AF_INET6, "::1", &addr6) < 1) + exit(1); + else + exit(0); + } + ], [ AC_MSG_RESULT(yes) + AC_DEFINE_UNQUOTED(HAVE_INET_PTON_IPV6, 1, [Define to 1 if inet_pton supports IPv6.]) + ], AC_MSG_RESULT(no), AC_MSG_RESULT(no)) +fi + +# Checks for header files. +dnl AC_HEADER_STDC +dnl AC_CHECK_HEADERS([netinet/in.h stdlib.h]) + +# Checks for typedefs, structures, and compiler characteristics. +dnl AC_C_CONST +dnl AC_C_INLINE + +# Checks for library functions. +dnl AC_FUNC_MALLOC +dnl AC_FUNC_VPRINTF +dnl AC_CHECK_FUNCS([memset]) + +dnl-------------------------------- + +dnl if test ! -z "$libdir"; then +dnl MODULE_DIR="\\\"$libdir/conntrack/\\\"" +dnl CFLAGS="$CFLAGS -DCONNTRACK_LIB_DIR=$MODULE_DIR" +dnl fi + +dnl-------------------------------- + +dnl AC_CONFIG_FILES([Makefile +dnl debug/Makefile +dnl debug/src/Makefile +dnl extensions/Makefile +dnl src/Makefile]) + +AC_OUTPUT(Makefile src/Makefile include/Makefile examples/Makefile examples/stats/Makefile examples/sync/Makefile examples/sync/persistent/Makefile examples/sync/nack/Makefile examples/sync/persistent/node1/Makefile examples/sync/persistent/node2/Makefile examples/sync/nack/node1/Makefile examples/sync/nack/node2/Makefile) diff --git a/daemon/examples/Makefile.am b/daemon/examples/Makefile.am new file mode 100644 index 0000000..be83d42 --- /dev/null +++ b/daemon/examples/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = stats sync diff --git a/daemon/examples/debian.conntrackd.init.d b/daemon/examples/debian.conntrackd.init.d new file mode 100644 index 0000000..ba847dd --- /dev/null +++ b/daemon/examples/debian.conntrackd.init.d @@ -0,0 +1,48 @@ +#!/bin/sh +# +# /etc/init.d/conntrackd +# +# Maximilian Wilhelm +# -- Mon, 06 Nov 2006 18:39:07 +0100 +# + +export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin + +NAME="conntrackd" +DAEMON=`command -v conntrackd` +CONFIG="/etc/conntrack/conntrackd.conf" +PIDFILE="/var/run/${NAME}.pid" + + +# Gracefully exit if there is no daemon (debian way of life) +if [ ! -x "${DAEMON}" ]; then + exit 0 +fi + +# Check for config file +if [ ! -f /etc/conntrackd/conntrackd.conf ]; then + echo "Error: There is no config file for $NAME" >&2 + exit 1; +fi + +case "$1" in + start) + echo -n "Starting $NAME: " + start-stop-daemon --start --quiet --make-pidfile --pidfile "/var/run/${NAME}.pid" --background --exec "${DAEMON}" && echo "done." || echo "FAILED!" + ;; + stop) + echo -n "Stopping $NAME:" + start-stop-daemon --stop --quiet --oknodo --pidfile "/var/run/${NAME}.pid" && echo "done." || echo "FAILED!" + ;; + + restart) + $0 start + $0 stop + ;; + + *) + echo "Usage: /etc/init.d/conntrackd {start|stop|restart}" + exit 1 +esac + +exit 0 diff --git a/daemon/examples/stats/Makefile.am b/daemon/examples/stats/Makefile.am new file mode 100644 index 0000000..b43c3b8 --- /dev/null +++ b/daemon/examples/stats/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = conntrackd.conf diff --git a/daemon/examples/stats/conntrackd.conf b/daemon/examples/stats/conntrackd.conf new file mode 100644 index 0000000..e514ac0 --- /dev/null +++ b/daemon/examples/stats/conntrackd.conf @@ -0,0 +1,69 @@ +# +# General settings +# +General { + # + # Number of buckets in the caches: hash table + # + HashSize 8192 + + # + # Maximum number of conntracks: + # it must be >= $ cat /proc/sys/net/ipv4/netfilter/ip_conntrack_max + # + HashLimit 65535 + + # + # Logfile + # + LogFile /var/log/conntrackd.log + + # + # Lockfile + # + LockFile /var/lock/conntrack.lock + + # + # Unix socket configuration + # + UNIX { + Path /tmp/sync.sock + Backlog 20 + } + + # + # Netlink socket buffer size + # + SocketBufferSize 262142 + + # + # Increase the socket buffer up to maximun if required + # + SocketBufferSizeMaxGrown 655355 +} + +# +# Ignore traffic for a certain set of IP's: Usually +# all the IP assigned to the firewall since local +# traffic must be ignored, just forwarded connections +# are worth to replicate +# +IgnoreTrafficFor { + IPv4_address 127.0.0.1 # loopback +} + +# +# Do not replicate certain protocol traffic +# +IgnoreProtocol { + UDP +# ICMP +# IGMP +# VRRP + # numeric numbers also valid +} + +# +# Strip NAT traffic +# +StripNAT diff --git a/daemon/examples/sync/Makefile.am b/daemon/examples/sync/Makefile.am new file mode 100644 index 0000000..28e7643 --- /dev/null +++ b/daemon/examples/sync/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = persistent nack diff --git a/daemon/examples/sync/nack/Makefile.am b/daemon/examples/sync/nack/Makefile.am new file mode 100644 index 0000000..6fd99b1 --- /dev/null +++ b/daemon/examples/sync/nack/Makefile.am @@ -0,0 +1,2 @@ +EXTRA_DIST = script_backup.sh script_master.sh +SUBDIRS = node1 node2 diff --git a/daemon/examples/sync/nack/README b/daemon/examples/sync/nack/README new file mode 100644 index 0000000..66987f7 --- /dev/null +++ b/daemon/examples/sync/nack/README @@ -0,0 +1 @@ +This directory contains the files for the NACK based protocol diff --git a/daemon/examples/sync/nack/node1/Makefile.am b/daemon/examples/sync/nack/node1/Makefile.am new file mode 100644 index 0000000..edc0ed7 --- /dev/null +++ b/daemon/examples/sync/nack/node1/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = conntrackd.conf keepalived.conf diff --git a/daemon/examples/sync/nack/node1/conntrackd.conf b/daemon/examples/sync/nack/node1/conntrackd.conf new file mode 100644 index 0000000..f24fa7e --- /dev/null +++ b/daemon/examples/sync/nack/node1/conntrackd.conf @@ -0,0 +1,125 @@ +# +# Synchronizer settings +# +Sync { + Mode NACK { + # + # Size of the buffer that hold destroy messages for + # possible resends (in bytes) + # + ResendBufferSize 262144 + + # + # Entries committed to the connection tracking table + # starts with a limited timeout of N seconds until the + # takeover process is completed. + # + CommitTimeout 180 + + # Set Acknowledgement window size + ACKWindowSize 20 + } + + # + # Multicast IP and interface where messages are + # broadcasted (dedicated link). IMPORTANT: Make sure + # that iptables accepts traffic for destination + # 225.0.0.50, eg: + # + # iptables -I INPUT -d 225.0.0.50 -j ACCEPT + # iptables -I OUTPUT -d 225.0.0.50 -j ACCEPT + # + Multicast { + IPv4_address 225.0.0.50 + IPv4_interface 192.168.100.100 # IP of dedicated link + Group 3780 + Backlog 20 + } + + # Enable/Disable message checksumming + Checksum on + + # Uncomment this if you want to replicate just certain TCP states. + # This option introduces a tradeoff in the replication: it reduces + # CPU consumption and lost messages rate at the cost of having + # backup replicas that don't contain the current state that the active + # replica holds. TCP states are: SYN_SENT, SYN_RECV, ESTABLISHED, + # FIN_WAIT, CLOSE_WAIT, LAST_ACK, TIME_WAIT, CLOSE, LISTEN. + # + # Replicate ESTABLISHED TIME_WAIT for TCP +} + +# +# General settings +# +General { + # + # Number of buckets in the caches: hash table + # + HashSize 8192 + + # + # Maximum number of conntracks: + # it must be >= $ cat /proc/sys/net/ipv4/netfilter/ip_conntrack_max + # + HashLimit 65535 + + # + # Logfile + # + LogFile /var/log/conntrackd.log + + # + # Lockfile + # + LockFile /var/lock/conntrack.lock + + # + # Unix socket configuration + # + UNIX { + Path /tmp/sync.sock + Backlog 20 + } + + # + # Netlink socket buffer size + # + SocketBufferSize 262142 + + # + # Increase the socket buffer up to maximum if required + # + SocketBufferSizeMaxGrown 655355 +} + +# +# Ignore traffic for a certain set of IP's: Usually +# all the IP assigned to the firewall since local +# traffic must be ignored, just forwarded connections +# are worth to replicate +# +IgnoreTrafficFor { + IPv4_address 127.0.0.1 # loopback + IPv4_address 192.168.0.1 + IPv4_address 192.168.1.1 + IPv4_address 192.168.100.100 # dedicated link ip + IPv4_address 192.168.0.100 # virtual IP 1 + IPv4_address 192.168.1.100 # virtual IP 2 +} + +# +# Do not replicate certain protocol traffic +# +IgnoreProtocol { + UDP + ICMP + IGMP + VRRP + # numeric numbers also valid +} + +# +# Strip NAT traffic +# +StripNAT diff --git a/daemon/examples/sync/nack/node1/keepalived.conf b/daemon/examples/sync/nack/node1/keepalived.conf new file mode 100644 index 0000000..41aa35b --- /dev/null +++ b/daemon/examples/sync/nack/node1/keepalived.conf @@ -0,0 +1,38 @@ +vrrp_sync_group G1 { # must be before vrrp_instance declaration + group { + VI_1 + VI_2 + } + notify_master /etc/conntrackd/script_master.sh + notify_backup /etc/conntrackd/script_backup.sh +} + +vrrp_instance VI_1 { + interface eth1 + state SLAVE + virtual_router_id 61 + priority 80 + advert_int 3 + authentication { + auth_type PASS + auth_pass papas_con_tomate + } + virtual_ipaddress { + 192.168.0.100 # default CIDR mask is /32 + } +} + +vrrp_instance VI_2 { + interface eth0 + state SLAVE + virtual_router_id 62 + priority 80 + advert_int 3 + authentication { + auth_type PASS + auth_pass papas_con_tomate + } + virtual_ipaddress { + 192.168.1.100 + } +} diff --git a/daemon/examples/sync/nack/node2/Makefile.am b/daemon/examples/sync/nack/node2/Makefile.am new file mode 100644 index 0000000..edc0ed7 --- /dev/null +++ b/daemon/examples/sync/nack/node2/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = conntrackd.conf keepalived.conf diff --git a/daemon/examples/sync/nack/node2/conntrackd.conf b/daemon/examples/sync/nack/node2/conntrackd.conf new file mode 100644 index 0000000..4f15773 --- /dev/null +++ b/daemon/examples/sync/nack/node2/conntrackd.conf @@ -0,0 +1,124 @@ +# +# Synchronizer settings +# +Sync { + Mode NACK { + # + # Size of the buffer that hold destroy messages for + # possible resends (in bytes) + # + ResendBufferSize 262144 + + # Entries committed to the connection tracking table + # starts with a limited timeout of N seconds until the + # takeover process is completed. + # + CommitTimeout 180 + + # Set Acknowledgement window size + ACKWindowSize 20 + } + + # + # Multicast IP and interface where messages are + # broadcasted (dedicated link). IMPORTANT: Make sure + # that iptables accepts traffic for destination + # 225.0.0.50, eg: + # + # iptables -I INPUT -d 225.0.0.50 -j ACCEPT + # iptables -I OUTPUT -d 225.0.0.50 -j ACCEPT + # + Multicast { + IPv4_address 225.0.0.50 + IPv4_interface 192.168.100.200 # IP of dedicated link + Group 3780 + Backlog 20 + } + + # Enable/Disable message checksumming + Checksum on + + # Uncomment this if you want to replicate just certain TCP states. + # This option introduces a tradeoff in the replication: it reduces + # CPU consumption and lost messages rate at the cost of having + # backup replicas that don't contain the current state that the active + # replica holds. TCP states are: SYN_SENT, SYN_RECV, ESTABLISHED, + # FIN_WAIT, CLOSE_WAIT, LAST_ACK, TIME_WAIT, CLOSE, LISTEN. + # + # Replicate ESTABLISHED TIME_WAIT for TCP +} + +# +# General settings +# +General { + # + # Number of buckets in the caches: hash table + # + HashSize 8192 + + # + # Maximum number of conntracks: + # it must be >= $ cat /proc/sys/net/ipv4/netfilter/ip_conntrack_max + # + HashLimit 65535 + + # + # Logfile + # + LogFile /var/log/conntrackd.log + + # + # Lockfile + # + LockFile /var/lock/conntrack.lock + + # + # Unix socket configuration + # + UNIX { + Path /tmp/sync.sock + Backlog 20 + } + + # + # Netlink socket buffer size + # + SocketBufferSize 262142 + + # + # Increase the socket buffer up to maximum if required + # + SocketBufferSizeMaxGrown 655355 +} + +# +# Ignore traffic for a certain set of IP's: Usually +# all the IP assigned to the firewall since local +# traffic must be ignored, just forwarded connections +# are worth to replicate +# +IgnoreTrafficFor { + IPv4_address 127.0.0.1 # loopback + IPv4_address 192.168.0.2 + IPv4_address 192.168.1.2 + IPv4_address 192.168.100.200 # dedicated link ip + IPv4_address 192.168.0.200 # virtual IP 1 + IPv4_address 192.168.1.200 # virtual IP 2 +} + +# +# Do not replicate certain protocol traffic +# +IgnoreProtocol { + UDP + ICMP + IGMP + VRRP + # numeric numbers also valid +} + +# +# Strip NAT traffic +# +StripNAT diff --git a/daemon/examples/sync/nack/node2/keepalived.conf b/daemon/examples/sync/nack/node2/keepalived.conf new file mode 100644 index 0000000..41aa35b --- /dev/null +++ b/daemon/examples/sync/nack/node2/keepalived.conf @@ -0,0 +1,38 @@ +vrrp_sync_group G1 { # must be before vrrp_instance declaration + group { + VI_1 + VI_2 + } + notify_master /etc/conntrackd/script_master.sh + notify_backup /etc/conntrackd/script_backup.sh +} + +vrrp_instance VI_1 { + interface eth1 + state SLAVE + virtual_router_id 61 + priority 80 + advert_int 3 + authentication { + auth_type PASS + auth_pass papas_con_tomate + } + virtual_ipaddress { + 192.168.0.100 # default CIDR mask is /32 + } +} + +vrrp_instance VI_2 { + interface eth0 + state SLAVE + virtual_router_id 62 + priority 80 + advert_int 3 + authentication { + auth_type PASS + auth_pass papas_con_tomate + } + virtual_ipaddress { + 192.168.1.100 + } +} diff --git a/daemon/examples/sync/nack/script_backup.sh b/daemon/examples/sync/nack/script_backup.sh new file mode 100755 index 0000000..813e375 --- /dev/null +++ b/daemon/examples/sync/nack/script_backup.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +/usr/sbin/conntrackd -n # request a resync from other nodes via multicast diff --git a/daemon/examples/sync/nack/script_master.sh b/daemon/examples/sync/nack/script_master.sh new file mode 100755 index 0000000..ff1dbc0 --- /dev/null +++ b/daemon/examples/sync/nack/script_master.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +/usr/sbin/conntrackd -c # commit the cache +/usr/sbin/conntrackd -f # flush the caches +/usr/sbin/conntrackd -R # resync with kernel conntrack table diff --git a/daemon/examples/sync/persistent/Makefile.am b/daemon/examples/sync/persistent/Makefile.am new file mode 100644 index 0000000..6fd99b1 --- /dev/null +++ b/daemon/examples/sync/persistent/Makefile.am @@ -0,0 +1,2 @@ +EXTRA_DIST = script_backup.sh script_master.sh +SUBDIRS = node1 node2 diff --git a/daemon/examples/sync/persistent/README b/daemon/examples/sync/persistent/README new file mode 100644 index 0000000..36b5989 --- /dev/null +++ b/daemon/examples/sync/persistent/README @@ -0,0 +1 @@ +This directory contains the files for the PERSISTENT based protocol diff --git a/daemon/examples/sync/persistent/node1/Makefile.am b/daemon/examples/sync/persistent/node1/Makefile.am new file mode 100644 index 0000000..edc0ed7 --- /dev/null +++ b/daemon/examples/sync/persistent/node1/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = conntrackd.conf keepalived.conf diff --git a/daemon/examples/sync/persistent/node1/conntrackd.conf b/daemon/examples/sync/persistent/node1/conntrackd.conf new file mode 100644 index 0000000..90afeb7 --- /dev/null +++ b/daemon/examples/sync/persistent/node1/conntrackd.conf @@ -0,0 +1,130 @@ +# +# Synchronizer settings +# +Sync { + Mode PERSISTENT { + # + # If a conntrack entry is not modified in <= 15 seconds, then + # a message is broadcasted. This mechanism is used to + # resynchronize nodes that just joined the multicast group + # + RefreshTime 15 + + # + # If we don't receive a notification about the state of + # an entry in the external cache after N seconds, then + # remove it. + # + CacheTimeout 180 + + # + # Entries committed to the connection tracking table + # starts with a limited timeout of N seconds until the + # takeover process is completed. + # + CommitTimeout 180 + } + + # + # Multicast IP and interface where messages are + # broadcasted (dedicated link). IMPORTANT: Make sure + # that iptables accepts traffic for destination + # 225.0.0.50, eg: + # + # iptables -I INPUT -d 225.0.0.50 -j ACCEPT + # iptables -I OUTPUT -d 225.0.0.50 -j ACCEPT + # + Multicast { + IPv4_address 225.0.0.50 + IPv4_interface 192.168.100.100 # IP of dedicated link + Group 3780 + Backlog 20 + } + + # Enable/Disable message checksumming + Checksum on + + # Uncomment this if you want to replicate just certain TCP states. + # This option introduces a tradeoff in the replication: it reduces + # CPU consumption and lost messages rate at the cost of having + # backup replicas that don't contain the current state that the active + # replica holds. TCP states are: SYN_SENT, SYN_RECV, ESTABLISHED, + # FIN_WAIT, CLOSE_WAIT, LAST_ACK, TIME_WAIT, CLOSE, LISTEN. + # + # Replicate ESTABLISHED TIME_WAIT for TCP +} + +# +# General settings +# +General { + # + # Number of buckets in the caches: hash table + # + HashSize 8192 + + # + # Maximum number of conntracks: + # it must be >= $ cat /proc/sys/net/ipv4/netfilter/ip_conntrack_max + # + HashLimit 65535 + + # + # Logfile + # + LogFile /var/log/conntrackd.log + + # + # Lockfile + # + LockFile /var/lock/conntrack.lock + + # + # Unix socket configuration + # + UNIX { + Path /tmp/sync.sock + Backlog 20 + } + + # + # Netlink socket buffer size + # + SocketBufferSize 262142 + + # + # Increase the socket buffer up to maximum if required + # + SocketBufferSizeMaxGrown 655355 +} + +# +# Ignore traffic for a certain set of IP's: Usually +# all the IP assigned to the firewall since local +# traffic must be ignored, just forwarded connections +# are worth to replicate +# +IgnoreTrafficFor { + IPv4_address 127.0.0.1 # loopback + IPv4_address 192.168.0.1 + IPv4_address 192.168.1.1 + IPv4_address 192.168.100.100 # dedicated link ip + IPv4_address 192.168.0.100 # virtual IP 1 + IPv4_address 192.168.1.100 # virtual IP 2 +} + +# +# Do not replicate certain protocol traffic +# +IgnoreProtocol { + UDP + ICMP + IGMP + VRRP + # numeric numbers also valid +} + +# +# Strip NAT traffic +# +StripNAT diff --git a/daemon/examples/sync/persistent/node1/keepalived.conf b/daemon/examples/sync/persistent/node1/keepalived.conf new file mode 100644 index 0000000..41aa35b --- /dev/null +++ b/daemon/examples/sync/persistent/node1/keepalived.conf @@ -0,0 +1,38 @@ +vrrp_sync_group G1 { # must be before vrrp_instance declaration + group { + VI_1 + VI_2 + } + notify_master /etc/conntrackd/script_master.sh + notify_backup /etc/conntrackd/script_backup.sh +} + +vrrp_instance VI_1 { + interface eth1 + state SLAVE + virtual_router_id 61 + priority 80 + advert_int 3 + authentication { + auth_type PASS + auth_pass papas_con_tomate + } + virtual_ipaddress { + 192.168.0.100 # default CIDR mask is /32 + } +} + +vrrp_instance VI_2 { + interface eth0 + state SLAVE + virtual_router_id 62 + priority 80 + advert_int 3 + authentication { + auth_type PASS + auth_pass papas_con_tomate + } + virtual_ipaddress { + 192.168.1.100 + } +} diff --git a/daemon/examples/sync/persistent/node2/Makefile.am b/daemon/examples/sync/persistent/node2/Makefile.am new file mode 100644 index 0000000..edc0ed7 --- /dev/null +++ b/daemon/examples/sync/persistent/node2/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = conntrackd.conf keepalived.conf diff --git a/daemon/examples/sync/persistent/node2/conntrackd.conf b/daemon/examples/sync/persistent/node2/conntrackd.conf new file mode 100644 index 0000000..aee4a29 --- /dev/null +++ b/daemon/examples/sync/persistent/node2/conntrackd.conf @@ -0,0 +1,130 @@ +# +# Synchronizer settings +# +Sync { + Mode PERSISTENT { + # + # If a conntrack entry is not modified in <= 15 seconds, then + # a message is broadcasted. This mechanism is used to + # resynchronize nodes that just joined the multicast group + # + RefreshTime 15 + + # + # If we don't receive a notification about the state of + # an entry in the external cache after N seconds, then + # remove it. + # + CacheTimeout 180 + + # + # Entries committed to the connection tracking table + # starts with a limited timeout of N seconds until the + # takeover process is completed. + # + CommitTimeout 180 + } + + # + # Multicast IP and interface where messages are + # broadcasted (dedicated link). IMPORTANT: Make sure + # that iptables accepts traffic for destination + # 225.0.0.50, eg: + # + # iptables -I INPUT -d 225.0.0.50 -j ACCEPT + # iptables -I OUTPUT -d 225.0.0.50 -j ACCEPT + # + Multicast { + IPv4_address 225.0.0.50 + IPv4_interface 192.168.100.200 # IP of dedicated link + Group 3780 + Backlog 20 + } + + # Enable/Disable message checksumming + Checksum on + + # Uncomment this if you want to replicate just certain TCP states. + # This option introduces a tradeoff in the replication: it reduces + # CPU consumption and lost messages rate at the cost of having + # backup replicas that don't contain the current state that the active + # replica holds. TCP states are: SYN_SENT, SYN_RECV, ESTABLISHED, + # FIN_WAIT, CLOSE_WAIT, LAST_ACK, TIME_WAIT, CLOSE, LISTEN. + # + # Replicate ESTABLISHED TIME_WAIT for TCP +} + +# +# General settings +# +General { + # + # Number of buckets in the caches: hash table + # + HashSize 8192 + + # + # Maximum number of conntracks: + # it must be >= $ cat /proc/sys/net/ipv4/netfilter/ip_conntrack_max + # + HashLimit 65535 + + # + # Logfile + # + LogFile /var/log/conntrackd.log + + # + # Lockfile + # + LockFile /var/lock/conntrack.lock + + # + # Unix socket configuration + # + UNIX { + Path /tmp/sync.sock + Backlog 20 + } + + # + # Netlink socket buffer size + # + SocketBufferSize 262142 + + # + # Increase the socket buffer up to maximum if required + # + SocketBufferSizeMaxGrown 655355 +} + +# +# Ignore traffic for a certain set of IP's: Usually +# all the IP assigned to the firewall since local +# traffic must be ignored, just forwarded connections +# are worth to replicate +# +IgnoreTrafficFor { + IPv4_address 127.0.0.1 # loopback + IPv4_address 192.168.0.2 + IPv4_address 192.168.1.2 + IPv4_address 192.168.100.200 # dedicated link ip + IPv4_address 192.168.0.200 # virtual IP 1 + IPv4_address 192.168.1.200 # virtual IP 2 +} + +# +# Do not replicate certain protocol traffic +# +IgnoreProtocol { + UDP + ICMP + IGMP + VRRP + # numeric numbers also valid +} + +# +# Strip NAT traffic +# +StripNAT diff --git a/daemon/examples/sync/persistent/node2/keepalived.conf b/daemon/examples/sync/persistent/node2/keepalived.conf new file mode 100644 index 0000000..41aa35b --- /dev/null +++ b/daemon/examples/sync/persistent/node2/keepalived.conf @@ -0,0 +1,38 @@ +vrrp_sync_group G1 { # must be before vrrp_instance declaration + group { + VI_1 + VI_2 + } + notify_master /etc/conntrackd/script_master.sh + notify_backup /etc/conntrackd/script_backup.sh +} + +vrrp_instance VI_1 { + interface eth1 + state SLAVE + virtual_router_id 61 + priority 80 + advert_int 3 + authentication { + auth_type PASS + auth_pass papas_con_tomate + } + virtual_ipaddress { + 192.168.0.100 # default CIDR mask is /32 + } +} + +vrrp_instance VI_2 { + interface eth0 + state SLAVE + virtual_router_id 62 + priority 80 + advert_int 3 + authentication { + auth_type PASS + auth_pass papas_con_tomate + } + virtual_ipaddress { + 192.168.1.100 + } +} diff --git a/daemon/examples/sync/persistent/script_backup.sh b/daemon/examples/sync/persistent/script_backup.sh new file mode 100755 index 0000000..8ea2ad8 --- /dev/null +++ b/daemon/examples/sync/persistent/script_backup.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +/usr/sbin/conntrackd -B diff --git a/daemon/examples/sync/persistent/script_master.sh b/daemon/examples/sync/persistent/script_master.sh new file mode 100755 index 0000000..70c26c9 --- /dev/null +++ b/daemon/examples/sync/persistent/script_master.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +/usr/sbin/conntrackd -c +/usr/sbin/conntrackd -R diff --git a/daemon/include/Makefile.am b/daemon/include/Makefile.am new file mode 100644 index 0000000..e669d73 --- /dev/null +++ b/daemon/include/Makefile.am @@ -0,0 +1,5 @@ + +noinst_HEADERS = alarm.h jhash.h slist.h cache.h linux_list.h \ + sync.h conntrackd.h local.h us-conntrack.h \ + debug.h log.h hash.h mcast.h buffer.h + diff --git a/daemon/include/alarm.h b/daemon/include/alarm.h new file mode 100644 index 0000000..93e6482 --- /dev/null +++ b/daemon/include/alarm.h @@ -0,0 +1,13 @@ +#ifndef _TIMER_H_ +#define _TIMER_H_ + +#include "linux_list.h" + +struct alarm_list { + struct list_head head; + unsigned long expires; + void *data; + void (*function)(struct alarm_list *a, void *data); +}; + +#endif diff --git a/daemon/include/buffer.h b/daemon/include/buffer.h new file mode 100644 index 0000000..8d72dfb --- /dev/null +++ b/daemon/include/buffer.h @@ -0,0 +1,32 @@ +#ifndef _BUFFER_H_ +#define _BUFFER_H_ + +#include +#include +#include +#include +#include "linux_list.h" + +struct buffer { + pthread_mutex_t lock; + size_t max_size; + size_t cur_size; + struct list_head head; +}; + +struct buffer_node { + struct list_head head; + size_t size; + char data[0]; +}; + +struct buffer *buffer_create(size_t max_size); +void buffer_destroy(struct buffer *b); +int buffer_add(struct buffer *b, const void *data, size_t size); +void buffer_del(struct buffer *b, void *data); +void __buffer_del(struct buffer *b, void *data); +void buffer_iterate(struct buffer *b, + void *data, + int (*iterate)(void *data1, void *data2)); + +#endif diff --git a/daemon/include/cache.h b/daemon/include/cache.h new file mode 100644 index 0000000..7d9559a --- /dev/null +++ b/daemon/include/cache.h @@ -0,0 +1,92 @@ +#ifndef _CACHE_H_ +#define _CACHE_H_ + +#include +#include + +/* cache features */ +enum { + NO_FEATURES = 0, + + TIMER_FEATURE = 0, + TIMER = (1 << TIMER_FEATURE), + + LIFETIME_FEATURE = 2, + LIFETIME = (1 << LIFETIME_FEATURE), + + __CACHE_MAX_FEATURE +}; +#define CACHE_MAX_FEATURE __CACHE_MAX_FEATURE + +struct cache; +struct us_conntrack; + +struct cache_feature { + size_t size; + void (*add)(struct us_conntrack *u, void *data); + void (*update)(struct us_conntrack *u, void *data); + void (*destroy)(struct us_conntrack *u, void *data); + int (*dump)(struct us_conntrack *u, void *data, char *buf, int type); +}; + +extern struct cache_feature lifetime_feature; +extern struct cache_feature timer_feature; + +#define CACHE_MAX_NAMELEN 32 + +struct cache { + char name[CACHE_MAX_NAMELEN]; + struct hashtable *h; + + unsigned int num_features; + struct cache_feature **features; + unsigned int feature_type[CACHE_MAX_FEATURE]; + unsigned int *feature_offset; + struct cache_extra *extra; + unsigned int extra_offset; + + /* statistics */ + unsigned int add_ok; + unsigned int del_ok; + unsigned int upd_ok; + + unsigned int add_fail; + unsigned int del_fail; + unsigned int upd_fail; + + unsigned int commit_ok; + unsigned int commit_exist; + unsigned int commit_fail; + + unsigned int flush; +}; + +struct cache_extra { + unsigned int size; + + void (*add)(struct us_conntrack *u, void *data); + void (*update)(struct us_conntrack *u, void *data); + void (*destroy)(struct us_conntrack *u, void *data); +}; + +struct nf_conntrack; + +struct cache *cache_create(char *name, unsigned int features, u_int8_t proto, struct cache_extra *extra); +void cache_destroy(struct cache *e); + +struct us_conntrack *cache_add(struct cache *c, struct nf_conntrack *ct); +struct us_conntrack *cache_update(struct cache *c, struct nf_conntrack *ct); +struct us_conntrack *cache_update_force(struct cache *c, struct nf_conntrack *ct); +int cache_del(struct cache *c, struct nf_conntrack *ct); +int cache_test(struct cache *c, struct nf_conntrack *ct); +void cache_stats(struct cache *c, int fd); +struct us_conntrack *cache_get_conntrack(struct cache *, void *); +void *cache_get_extra(struct cache *, void *); + +/* iterators */ +void cache_dump(struct cache *c, int fd, int type); +void cache_commit(struct cache *c); +void cache_flush(struct cache *c); +void cache_bulk(struct cache *c); + +#endif diff --git a/daemon/include/conntrackd.h b/daemon/include/conntrackd.h new file mode 100644 index 0000000..a5f7a3a --- /dev/null +++ b/daemon/include/conntrackd.h @@ -0,0 +1,174 @@ +#ifndef _CONNTRACKD_H_ +#define _CONNTRACKD_H_ + +#include "mcast.h" +#include "local.h" + +#include +#include +#include "cache.h" +#include "debug.h" +#include +#include "state_helper.h" +#include + +/* UNIX facilities */ +#define FLUSH_MASTER 0 /* flush kernel conntrack table */ +#define RESYNC_MASTER 1 /* resync with kernel conntrack table */ +#define DUMP_INTERNAL 16 /* dump internal cache */ +#define DUMP_EXTERNAL 17 /* dump external cache */ +#define COMMIT 18 /* commit external cache */ +#define FLUSH_CACHE 19 /* flush cache */ +#define KILL 20 /* kill conntrackd */ +#define STATS 21 /* dump statistics */ +#define SEND_BULK 22 /* send a bulk */ +#define REQUEST_DUMP 23 /* request dump */ +#define DUMP_INT_XML 24 /* dump internal cache in XML */ +#define DUMP_EXT_XML 25 /* dump external cache in XML */ + +#define DEFAULT_CONFIGFILE "/etc/conntrackd/conntrackd.conf" +#define DEFAULT_LOCKFILE "/var/lock/conntrackd.lock" + +enum { + STRIP_NAT_BIT = 0, + STRIP_NAT = (1 << STRIP_NAT_BIT), + + DELAY_DESTROY_MSG_BIT = 1, + DELAY_DESTROY_MSG = (1 << DELAY_DESTROY_MSG_BIT), + + RELAX_TRANSITIONS_BIT = 2, + RELAX_TRANSITIONS = (1 << RELAX_TRANSITIONS_BIT), + + SYNC_MODE_PERSISTENT_BIT = 3, + SYNC_MODE_PERSISTENT = (1 << SYNC_MODE_PERSISTENT_BIT), + + SYNC_MODE_NACK_BIT = 4, + SYNC_MODE_NACK = (1 << SYNC_MODE_NACK_BIT), + + DONT_CHECKSUM_BIT = 5, + DONT_CHECKSUM = (1 << DONT_CHECKSUM_BIT), +}; + +/* daemon/request modes */ +#define NOT_SET 0 +#define DAEMON 1 +#define REQUEST 2 + +/* conntrackd modes */ +#define SYNC_MODE 0 +#define STATS_MODE 1 + +/* FILENAME_MAX is 4096 on my system, perhaps too much? */ +#ifndef FILENAME_MAXLEN +#define FILENAME_MAXLEN 256 +#endif + +union inet_address { + u_int32_t ipv4; + u_int32_t ipv6[4]; + u_int32_t all[4]; +}; + +#define CONFIG(x) conf.x + +struct ct_conf { + char logfile[FILENAME_MAXLEN]; + char lockfile[FILENAME_MAXLEN]; + int hashsize; /* hashtable size */ + struct mcast_conf mcast; /* multicast settings */ + struct local_conf local; /* unix socket facilities */ + int limit; + int refresh; + int cache_timeout; /* cache entries timeout */ + int commit_timeout; /* committed entries timeout */ + unsigned int netlink_buffer_size; + unsigned int netlink_buffer_size_max_grown; + unsigned char ignore_protocol[IPPROTO_MAX]; + union inet_address *listen_to; + unsigned int listen_to_len; + unsigned int flags; + int family; /* protocol family */ + unsigned int resend_buffer_size;/* NACK protocol */ + unsigned int window_size; +}; + +#define STATE(x) st.x + +struct ct_general_state { + sigset_t block; + FILE *log; + int local; + struct ct_mode *mode; + struct ignore_pool *ignore_pool; + + struct nfnl_handle *event; /* event handler */ + struct nfnl_handle *sync; /* sync handler */ + struct nfnl_handle *dump; /* dump handler */ + + struct nfnl_subsys_handle *subsys_event; /* events */ + struct nfnl_subsys_handle *subsys_sync; /* resync */ + struct nfnl_subsys_handle *subsys_dump; /* dump */ + + /* statistics */ + u_int64_t malformed; + u_int64_t bytes[NFCT_DIR_MAX]; + u_int64_t packets[NFCT_DIR_MAX]; +}; + +#define STATE_SYNC(x) state.sync->x + +struct ct_sync_state { + struct cache *internal; /* internal events cache (netlink) */ + struct cache *external; /* external events cache (mcast) */ + + struct mcast_sock *mcast_server; /* multicast socket: incoming */ + struct mcast_sock *mcast_client; /* multicast socket: outgoing */ + + struct sync_mode *mcast_sync; + struct buffer *buffer; + + u_int32_t last_seq_sent; /* last sequence number sent */ + u_int32_t last_seq_recv; /* last sequence number recv */ + u_int64_t packets_replayed; /* number of replayed packets */ + u_int64_t packets_lost; /* lost packets: sequence tracking */ +}; + +#define STATE_STATS(x) state.stats->x + +struct ct_stats_state { + struct cache *cache; /* internal events cache (netlink) */ +}; + +union ct_state { + struct ct_sync_state *sync; + struct ct_stats_state *stats; +}; + +extern struct ct_conf conf; +extern union ct_state state; +extern struct ct_general_state st; + +#ifndef IPPROTO_VRRP +#define IPPROTO_VRRP 112 +#endif + +struct ct_mode { + int (*init)(void); + int (*add_fds_to_set)(fd_set *readfds); + void (*step)(fd_set *readfds); + int (*local)(int fd, int type, void *data); + void (*kill)(void); + void (*dump)(struct nf_conntrack *ct, struct nlmsghdr *nlh); + void (*overrun)(struct nf_conntrack *ct, struct nlmsghdr *nlh); + void (*event_new)(struct nf_conntrack *ct, struct nlmsghdr *nlh); + void (*event_upd)(struct nf_conntrack *ct, struct nlmsghdr *nlh); + int (*event_dst)(struct nf_conntrack *ct, struct nlmsghdr *nlh); +}; + +/* conntrackd modes */ +extern struct ct_mode sync_mode; +extern struct ct_mode stats_mode; + +#define MAX(x, y) x > y ? x : y + +#endif diff --git a/daemon/include/debug.h b/daemon/include/debug.h new file mode 100644 index 0000000..67f2c71 --- /dev/null +++ b/daemon/include/debug.h @@ -0,0 +1,53 @@ +#ifndef _DEBUG_H +#define _DEBUG_H + +#if 0 +#define debug printf +#else +#define debug +#endif + +#include +#include +#include + +static inline void debug_ct(struct nf_conntrack *ct, char *msg) +{ + struct in_addr addr, addr2, addr3, addr4; + + debug("----%s (%p) ----\n", msg, ct); + memcpy(&addr, + nfct_get_attr(ct, ATTR_ORIG_IPV4_SRC), + sizeof(u_int32_t)); + memcpy(&addr2, + nfct_get_attr(ct, ATTR_ORIG_IPV4_DST), + sizeof(u_int32_t)); + memcpy(&addr3, + nfct_get_attr(ct, ATTR_REPL_IPV4_SRC), + sizeof(u_int32_t)); + memcpy(&addr4, + nfct_get_attr(ct, ATTR_REPL_IPV4_DST), + sizeof(u_int32_t)); + + debug("status: %x\n", nfct_get_attr_u32(ct, ATTR_STATUS)); + debug("l3:%d l4:%d ", + nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO), + nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO)); + debug("%s:%hu ->", inet_ntoa(addr), + ntohs(nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC))); + debug("%s:%hu\n", + inet_ntoa(addr2), + ntohs(nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST))); + debug("l3:%d l4:%d ", + nfct_get_attr_u8(ct, ATTR_REPL_L3PROTO), + nfct_get_attr_u8(ct, ATTR_REPL_L4PROTO)); + debug("%s:%hu ->", + inet_ntoa(addr3), + ntohs(nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC))); + debug("%s:%hu\n", + inet_ntoa(addr4), + ntohs(nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST))); + debug("-------------------------\n"); +} + +#endif diff --git a/daemon/include/hash.h b/daemon/include/hash.h new file mode 100644 index 0000000..fd971e7 --- /dev/null +++ b/daemon/include/hash.h @@ -0,0 +1,47 @@ +#ifndef _NF_SET_HASH_H_ +#define _NF_SET_HASH_H_ + +#include +#include +#include "slist.h" +#include "linux_list.h" + +struct hashtable; +struct hashtable_node; + +struct hashtable { + u_int32_t hashsize; + u_int32_t limit; + u_int32_t count; + u_int32_t initval; + u_int32_t datasize; + + u_int32_t (*hash)(const void *data, struct hashtable *table); + int (*compare)(const void *data1, const void *data2); + + struct slist_head members[0]; +}; + +struct hashtable_node { + struct slist_head head; + char data[0]; +}; + +struct hashtable_node *hashtable_alloc_node(int datasize, void *data); +void hashtable_destroy_node(struct hashtable_node *h); + +struct hashtable * +hashtable_create(int hashsize, int limit, int datasize, + u_int32_t (*hash)(const void *data, struct hashtable *table), + int (*compare)(const void *data1, const void *data2)); +void hashtable_destroy(struct hashtable *h); + +void *hashtable_add(struct hashtable *table, void *data); +void *hashtable_test(struct hashtable *table, const void *data); +int hashtable_del(struct hashtable *table, void *data); +int hashtable_flush(struct hashtable *table); +int hashtable_iterate(struct hashtable *table, void *data, + int (*iterate)(void *data1, void *data2)); +unsigned int hashtable_counter(struct hashtable *table); + +#endif diff --git a/daemon/include/ignore.h b/daemon/include/ignore.h new file mode 100644 index 0000000..40cb02d --- /dev/null +++ b/daemon/include/ignore.h @@ -0,0 +1,12 @@ +#ifndef _IGNORE_H_ +#define _IGNORE_H_ + +struct ignore_pool { + struct hashtable *h; +}; + +struct ignore_pool *ignore_pool_create(u_int8_t family); +void ignore_pool_destroy(struct ignore_pool *ip); +int ignore_pool_add(struct ignore_pool *ip, void *data); + +#endif diff --git a/daemon/include/jhash.h b/daemon/include/jhash.h new file mode 100644 index 0000000..38b8780 --- /dev/null +++ b/daemon/include/jhash.h @@ -0,0 +1,146 @@ +#ifndef _LINUX_JHASH_H +#define _LINUX_JHASH_H + +#define u32 unsigned int +#define u8 char + +/* jhash.h: Jenkins hash support. + * + * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net) + * + * http://burtleburtle.net/bob/hash/ + * + * These are the credits from Bob's sources: + * + * lookup2.c, by Bob Jenkins, December 1996, Public Domain. + * hash(), hash2(), hash3, and mix() are externally useful functions. + * Routines to test the hash are included if SELF_TEST is defined. + * You can use this free for any purpose. It has no warranty. + * + * Copyright (C) 2003 David S. Miller (davem@redhat.com) + * + * I've modified Bob's hash to be useful in the Linux kernel, and + * any bugs present are surely my fault. -DaveM + */ + +/* NOTE: Arguments are modified. */ +#define __jhash_mix(a, b, c) \ +{ \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<<8); \ + c -= a; c -= b; c ^= (b>>13); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<16); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>3); \ + b -= c; b -= a; b ^= (a<<10); \ + c -= a; c -= b; c ^= (b>>15); \ +} + +/* The golden ration: an arbitrary value */ +#define JHASH_GOLDEN_RATIO 0x9e3779b9 + +/* The most generic version, hashes an arbitrary sequence + * of bytes. No alignment or length assumptions are made about + * the input key. + */ +static inline u32 jhash(const void *key, u32 length, u32 initval) +{ + u32 a, b, c, len; + const u8 *k = key; + + len = length; + a = b = JHASH_GOLDEN_RATIO; + c = initval; + + while (len >= 12) { + a += (k[0] +((u32)k[1]<<8) +((u32)k[2]<<16) +((u32)k[3]<<24)); + b += (k[4] +((u32)k[5]<<8) +((u32)k[6]<<16) +((u32)k[7]<<24)); + c += (k[8] +((u32)k[9]<<8) +((u32)k[10]<<16)+((u32)k[11]<<24)); + + __jhash_mix(a,b,c); + + k += 12; + len -= 12; + } + + c += length; + switch (len) { + case 11: c += ((u32)k[10]<<24); + case 10: c += ((u32)k[9]<<16); + case 9 : c += ((u32)k[8]<<8); + case 8 : b += ((u32)k[7]<<24); + case 7 : b += ((u32)k[6]<<16); + case 6 : b += ((u32)k[5]<<8); + case 5 : b += k[4]; + case 4 : a += ((u32)k[3]<<24); + case 3 : a += ((u32)k[2]<<16); + case 2 : a += ((u32)k[1]<<8); + case 1 : a += k[0]; + }; + + __jhash_mix(a,b,c); + + return c; +} + +/* A special optimized version that handles 1 or more of u32s. + * The length parameter here is the number of u32s in the key. + */ +static inline u32 jhash2(u32 *k, u32 length, u32 initval) +{ + u32 a, b, c, len; + + a = b = JHASH_GOLDEN_RATIO; + c = initval; + len = length; + + while (len >= 3) { + a += k[0]; + b += k[1]; + c += k[2]; + __jhash_mix(a, b, c); + k += 3; len -= 3; + } + + c += length * 4; + + switch (len) { + case 2 : b += k[1]; + case 1 : a += k[0]; + }; + + __jhash_mix(a,b,c); + + return c; +} + + +/* A special ultra-optimized versions that knows they are hashing exactly + * 3, 2 or 1 word(s). + * + * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally + * done at the end is not done here. + */ +static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval) +{ + a += JHASH_GOLDEN_RATIO; + b += JHASH_GOLDEN_RATIO; + c += initval; + + __jhash_mix(a, b, c); + + return c; +} + +static inline u32 jhash_2words(u32 a, u32 b, u32 initval) +{ + return jhash_3words(a, b, 0, initval); +} + +static inline u32 jhash_1word(u32 a, u32 initval) +{ + return jhash_3words(a, 0, 0, initval); +} + +#endif /* _LINUX_JHASH_H */ diff --git a/daemon/include/linux_list.h b/daemon/include/linux_list.h new file mode 100644 index 0000000..57b56d7 --- /dev/null +++ b/daemon/include/linux_list.h @@ -0,0 +1,725 @@ +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +#undef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +/** + * container_of - cast a member of a structure out to the containing structure + * + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +/* + * Check at compile time that something is of a particular type. + * Always evaluates to 1 so you may use it easily in comparisons. + */ +#define typecheck(type,x) \ +({ type __dummy; \ + typeof(x) __dummy2; \ + (void)(&__dummy == &__dummy2); \ + 1; \ +}) + +#define prefetch(x) 1 + +/* empty define to make this work in userspace -HW */ +#ifndef smp_wmb +#define smp_wmb() +#endif + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized list entries. + */ +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add_rcu(struct list_head * new, + struct list_head * prev, struct list_head * next) +{ + new->next = next; + new->prev = prev; + smp_wmb(); + next->prev = new; + prev->next = new; +} + +/** + * list_add_rcu - add a new entry to rcu-protected list + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + * + * The caller must take whatever precautions are necessary + * (such as holding appropriate locks) to avoid racing + * with another list-mutation primitive, such as list_add_rcu() + * or list_del_rcu(), running on this same list. + * However, it is perfectly legal to run concurrently with + * the _rcu list-traversal primitives, such as + * list_for_each_entry_rcu(). + */ +static inline void list_add_rcu(struct list_head *new, struct list_head *head) +{ + __list_add_rcu(new, head, head->next); +} + +/** + * list_add_tail_rcu - add a new entry to rcu-protected list + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + * + * The caller must take whatever precautions are necessary + * (such as holding appropriate locks) to avoid racing + * with another list-mutation primitive, such as list_add_tail_rcu() + * or list_del_rcu(), running on this same list. + * However, it is perfectly legal to run concurrently with + * the _rcu list-traversal primitives, such as + * list_for_each_entry_rcu(). + */ +static inline void list_add_tail_rcu(struct list_head *new, + struct list_head *head) +{ + __list_add_rcu(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +/** + * list_del_rcu - deletes entry from list without re-initialization + * @entry: the element to delete from the list. + * + * Note: list_empty on entry does not return true after this, + * the entry is in an undefined state. It is useful for RCU based + * lockfree traversal. + * + * In particular, it means that we can not poison the forward + * pointers that may still be used for walking the list. + * + * The caller must take whatever precautions are necessary + * (such as holding appropriate locks) to avoid racing + * with another list-mutation primitive, such as list_del_rcu() + * or list_add_rcu(), running on this same list. + * However, it is perfectly legal to run concurrently with + * the _rcu list-traversal primitives, such as + * list_for_each_entry_rcu(). + * + * Note that the caller is not permitted to immediately free + * the newly deleted entry. Instead, either synchronize_kernel() + * or call_rcu() must be used to defer freeing until an RCU + * grace period has elapsed. + */ +static inline void list_del_rcu(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->prev = LIST_POISON2; +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/** + * list_empty_careful - tests whether a list is + * empty _and_ checks that no other CPU might be + * in the process of still modifying either member + * + * NOTE: using list_empty_careful() without synchronization + * can only be safe if the only activity that can happen + * to the list entry is list_del_init(). Eg. it cannot be used + * if another CPU could re-list_add() it. + * + * @head: the list to test. + */ +static inline int list_empty_careful(const struct list_head *head) +{ + struct list_head *next = head->next; + return (next == head) && (next == head->prev); +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next, prefetch(pos->next); pos != (head); \ + pos = pos->next, prefetch(pos->next)) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code, no prefetching is done. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \ + pos = pos->prev, prefetch(pos->prev)) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member), \ + prefetch(pos->member.prev); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member), \ + prefetch(pos->member.prev)) + +/** + * list_prepare_entry - prepare a pos entry for use as a start point in + * list_for_each_entry_continue + * @pos: the type * to use as a start point + * @head: the head of the list + * @member: the name of the list_struct within the struct. + */ +#define list_prepare_entry(pos, head, member) \ + ((pos) ? : list_entry(head, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - iterate over list of given type + * continuing after existing point + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_rcu - iterate over an rcu-protected list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + * + * This list-traversal primitive may safely run concurrently with + * the _rcu list-mutation primitives such as list_add_rcu() + * as long as the traversal is guarded by rcu_read_lock(). + */ +#define list_for_each_rcu(pos, head) \ + for (pos = (head)->next, prefetch(pos->next); pos != (head); \ + pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next)) + +#define __list_for_each_rcu(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next, ({ smp_read_barrier_depends(); 0;})) + +/** + * list_for_each_safe_rcu - iterate over an rcu-protected list safe + * against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + * + * This list-traversal primitive may safely run concurrently with + * the _rcu list-mutation primitives such as list_add_rcu() + * as long as the traversal is guarded by rcu_read_lock(). + */ +#define list_for_each_safe_rcu(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next) + +/** + * list_for_each_entry_rcu - iterate over rcu list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * This list-traversal primitive may safely run concurrently with + * the _rcu list-mutation primitives such as list_add_rcu() + * as long as the traversal is guarded by rcu_read_lock(). + */ +#define list_for_each_entry_rcu(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member), \ + ({ smp_read_barrier_depends(); 0;}), \ + prefetch(pos->member.next)) + + +/** + * list_for_each_continue_rcu - iterate over an rcu-protected list + * continuing after existing point. + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + * + * This list-traversal primitive may safely run concurrently with + * the _rcu list-mutation primitives such as list_add_rcu() + * as long as the traversal is guarded by rcu_read_lock(). + */ +#define list_for_each_continue_rcu(pos, head) \ + for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \ + (pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next)) + +/* + * Double linked lists with a single pointer list head. + * Mostly useful for hash tables where the two pointer list head is + * too wasteful. + * You lose the ability to access the tail in O(1). + */ + +struct hlist_head { + struct hlist_node *first; +}; + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + +#define HLIST_HEAD_INIT { .first = NULL } +#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } +#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) +#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL) + +static inline int hlist_unhashed(const struct hlist_node *h) +{ + return !h->pprev; +} + +static inline int hlist_empty(const struct hlist_head *h) +{ + return !h->first; +} + +static inline void __hlist_del(struct hlist_node *n) +{ + struct hlist_node *next = n->next; + struct hlist_node **pprev = n->pprev; + *pprev = next; + if (next) + next->pprev = pprev; +} + +static inline void hlist_del(struct hlist_node *n) +{ + __hlist_del(n); + n->next = LIST_POISON1; + n->pprev = LIST_POISON2; +} + +/** + * hlist_del_rcu - deletes entry from hash list without re-initialization + * @n: the element to delete from the hash list. + * + * Note: list_unhashed() on entry does not return true after this, + * the entry is in an undefined state. It is useful for RCU based + * lockfree traversal. + * + * In particular, it means that we can not poison the forward + * pointers that may still be used for walking the hash list. + * + * The caller must take whatever precautions are necessary + * (such as holding appropriate locks) to avoid racing + * with another list-mutation primitive, such as hlist_add_head_rcu() + * or hlist_del_rcu(), running on this same list. + * However, it is perfectly legal to run concurrently with + * the _rcu list-traversal primitives, such as + * hlist_for_each_entry(). + */ +static inline void hlist_del_rcu(struct hlist_node *n) +{ + __hlist_del(n); + n->pprev = LIST_POISON2; +} + +static inline void hlist_del_init(struct hlist_node *n) +{ + if (n->pprev) { + __hlist_del(n); + INIT_HLIST_NODE(n); + } +} + +#define hlist_del_rcu_init hlist_del_init + +static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) +{ + struct hlist_node *first = h->first; + n->next = first; + if (first) + first->pprev = &n->next; + h->first = n; + n->pprev = &h->first; +} + + +/** + * hlist_add_head_rcu - adds the specified element to the specified hlist, + * while permitting racing traversals. + * @n: the element to add to the hash list. + * @h: the list to add to. + * + * The caller must take whatever precautions are necessary + * (such as holding appropriate locks) to avoid racing + * with another list-mutation primitive, such as hlist_add_head_rcu() + * or hlist_del_rcu(), running on this same list. + * However, it is perfectly legal to run concurrently with + * the _rcu list-traversal primitives, such as + * hlist_for_each_entry(), but only if smp_read_barrier_depends() + * is used to prevent memory-consistency problems on Alpha CPUs. + * Regardless of the type of CPU, the list-traversal primitive + * must be guarded by rcu_read_lock(). + * + * OK, so why don't we have an hlist_for_each_entry_rcu()??? + */ +static inline void hlist_add_head_rcu(struct hlist_node *n, + struct hlist_head *h) +{ + struct hlist_node *first = h->first; + n->next = first; + n->pprev = &h->first; + smp_wmb(); + if (first) + first->pprev = &n->next; + h->first = n; +} + +/* next must be != NULL */ +static inline void hlist_add_before(struct hlist_node *n, + struct hlist_node *next) +{ + n->pprev = next->pprev; + n->next = next; + next->pprev = &n->next; + *(n->pprev) = n; +} + +static inline void hlist_add_after(struct hlist_node *n, + struct hlist_node *next) +{ + next->next = n->next; + n->next = next; + next->pprev = &n->next; + + if(next->next) + next->next->pprev = &next->next; +} + +#define hlist_entry(ptr, type, member) container_of(ptr,type,member) + +#define hlist_for_each(pos, head) \ + for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \ + pos = pos->next) + +#define hlist_for_each_safe(pos, n, head) \ + for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \ + pos = n) + +/** + * hlist_for_each_entry - iterate over list of given type + * @tpos: the type * to use as a loop counter. + * @pos: the &struct hlist_node to use as a loop counter. + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry(tpos, pos, head, member) \ + for (pos = (head)->first; \ + pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point + * @tpos: the type * to use as a loop counter. + * @pos: the &struct hlist_node to use as a loop counter. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_continue(tpos, pos, member) \ + for (pos = (pos)->next; \ + pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_from - iterate over a hlist continuing from existing point + * @tpos: the type * to use as a loop counter. + * @pos: the &struct hlist_node to use as a loop counter. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_from(tpos, pos, member) \ + for (; pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @tpos: the type * to use as a loop counter. + * @pos: the &struct hlist_node to use as a loop counter. + * @n: another &struct hlist_node to use as temporary storage + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ + for (pos = (head)->first; \ + pos && ({ n = pos->next; 1; }) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = n) + +/** + * hlist_for_each_entry_rcu - iterate over rcu list of given type + * @pos: the type * to use as a loop counter. + * @pos: the &struct hlist_node to use as a loop counter. + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + * + * This list-traversal primitive may safely run concurrently with + * the _rcu list-mutation primitives such as hlist_add_rcu() + * as long as the traversal is guarded by rcu_read_lock(). + */ +#define hlist_for_each_entry_rcu(tpos, pos, head, member) \ + for (pos = (head)->first; \ + pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next, ({ smp_read_barrier_depends(); 0; }) ) + +#endif diff --git a/daemon/include/local.h b/daemon/include/local.h new file mode 100644 index 0000000..350b8bf --- /dev/null +++ b/daemon/include/local.h @@ -0,0 +1,29 @@ +#ifndef _LOCAL_SOCKET_H_ +#define _LOCAL_SOCKET_H_ + +#include + +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX 108 +#endif + +struct local_conf { + int backlog; + int reuseaddr; + char path[UNIX_PATH_MAX]; +}; + +/* local server */ +int local_server_create(struct local_conf *conf); +void local_server_destroy(int fd); +int do_local_server_step(int fd, void *data, + void (*process)(int fd, void *data)); + +/* local client */ +int local_client_create(struct local_conf *conf); +void local_client_destroy(int fd); +int do_local_client_step(int fd, void (*process)(char *buf)); +int do_local_request(int, struct local_conf *,void (*step)(char *buf)); +void local_step(char *buf); + +#endif diff --git a/daemon/include/log.h b/daemon/include/log.h new file mode 100644 index 0000000..9ecff30 --- /dev/null +++ b/daemon/include/log.h @@ -0,0 +1,10 @@ +#ifndef _LOG_H_ +#define _LOG_H_ + +#include + +FILE *init_log(char *filename); +void dlog(FILE *fd, char *format, ...); +void close_log(FILE *fd); + +#endif diff --git a/daemon/include/mcast.h b/daemon/include/mcast.h new file mode 100644 index 0000000..0f3e3cd --- /dev/null +++ b/daemon/include/mcast.h @@ -0,0 +1,48 @@ +#ifndef _MCAST_H_ +#define _MCAST_H_ + +#include + +struct mcast_conf { + int ipproto; + int backlog; + int reuseaddr; + unsigned short port; + union { + struct in_addr inet_addr; + struct in6_addr inet_addr6; + } in; + union { + struct in_addr interface_addr; + struct in6_addr interface_addr6; + } ifa; +}; + +struct mcast_stats { + u_int64_t bytes; + u_int64_t messages; + u_int64_t error; +}; + +struct mcast_sock { + int fd; + union { + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + } addr; + struct mcast_stats stats; +}; + +struct mcast_sock *mcast_server_create(struct mcast_conf *conf); +void mcast_server_destroy(struct mcast_sock *m); + +struct mcast_sock *mcast_client_create(struct mcast_conf *conf); +void mcast_client_destroy(struct mcast_sock *m); + +int mcast_send(struct mcast_sock *m, void *data, int size); +int mcast_recv(struct mcast_sock *m, void *data, int size); + +struct mcast_stats *mcast_get_stats(struct mcast_sock *m); +void mcast_dump_stats(int fd, struct mcast_sock *s, struct mcast_sock *r); + +#endif diff --git a/daemon/include/network.h b/daemon/include/network.h new file mode 100644 index 0000000..dab50db --- /dev/null +++ b/daemon/include/network.h @@ -0,0 +1,34 @@ +#ifndef _NETWORK_H_ +#define _NETWORK_H_ + +#include + +struct nlnetwork { + u_int16_t flags; + u_int16_t checksum; + u_int32_t seq; +}; + +struct nlnetwork_ack { + u_int16_t flags; + u_int16_t checksum; + u_int32_t seq; + u_int32_t from; + u_int32_t to; +}; + +enum { + NET_HELLO_BIT = 0, + NET_HELLO = (1 << NET_HELLO_BIT), + + NET_RESYNC_BIT = 1, + NET_RESYNC = (1 << NET_RESYNC_BIT), + + NET_NACK_BIT = 2, + NET_NACK = (1 << NET_NACK_BIT), + + NET_ACK_BIT = 3, + NET_ACK = (1 << NET_ACK_BIT), +}; + +#endif diff --git a/daemon/include/slist.h b/daemon/include/slist.h new file mode 100644 index 0000000..ab7fa34 --- /dev/null +++ b/daemon/include/slist.h @@ -0,0 +1,41 @@ +#ifndef _SLIST_H_ +#define _SLIST_H_ + +#include "linux_list.h" + +#define INIT_SLIST_HEAD(ptr) ((ptr).next = NULL) + +struct slist_head { + struct slist_head *next; +}; + +static inline int slist_empty(const struct slist_head *h) +{ + return !h->next; +} + +static inline void slist_del(struct slist_head *t, struct slist_head *prev) +{ + prev->next = t->next; + t->next = LIST_POISON1; +} + +static inline void slist_add(struct slist_head *head, struct slist_head *t) +{ + struct slist_head *tmp = head->next; + head->next = t; + t->next = tmp; +} + +#define slist_entry(ptr, type, member) container_of(ptr,type,member) + +#define slist_for_each(pos, head) \ + for (pos = (head)->next; pos && ({ prefetch(pos.next); 1; }); \ + pos = pos->next) + +#define slist_for_each_safe(pos, prev, next, head) \ + for (pos = (head)->next, prev = (head); \ + pos && ({ next = pos->next; 1; }); \ + ({ prev = (prev->next != next) ? prev->next : prev; }), pos = next) + +#endif diff --git a/daemon/include/state_helper.h b/daemon/include/state_helper.h new file mode 100644 index 0000000..1ed0b79 --- /dev/null +++ b/daemon/include/state_helper.h @@ -0,0 +1,20 @@ +#ifndef _STATE_HELPER_H_ +#define _STATE_HELPER_H_ + +enum { + ST_H_SKIP, + ST_H_REPLICATE +}; + +struct state_replication_helper { + u_int8_t proto; + unsigned int state; + + int (*verdict)(const struct state_replication_helper *h, + const struct nf_conntrack *ct); +}; + +int state_helper_verdict(int type, struct nf_conntrack *ct); +void state_helper_register(struct state_replication_helper *h, int state); + +#endif diff --git a/daemon/include/sync.h b/daemon/include/sync.h new file mode 100644 index 0000000..7756c87 --- /dev/null +++ b/daemon/include/sync.h @@ -0,0 +1,23 @@ +#ifndef _SYNC_HOOKS_H_ +#define _SYNC_HOOKS_H_ + +struct nlnetwork; +struct us_conntrack; + +struct sync_mode { + int internal_cache_flags; + int external_cache_flags; + struct cache_extra *internal_cache_extra; + struct cache_extra *external_cache_extra; + + int (*init)(void); + void (*kill)(void); + int (*local)(int fd, int type, void *data); + int (*pre_recv)(const struct nlnetwork *net); + void (*post_send)(const struct nlnetwork *net, struct us_conntrack *u); +}; + +extern struct sync_mode notrack; +extern struct sync_mode nack; + +#endif diff --git a/daemon/include/us-conntrack.h b/daemon/include/us-conntrack.h new file mode 100644 index 0000000..3d71e22 --- /dev/null +++ b/daemon/include/us-conntrack.h @@ -0,0 +1,13 @@ +#ifndef _US_CONNTRACK_H_ +#define _US_CONNTRACK_H_ + +#include + +/* be careful, do not modify the layout */ +struct us_conntrack { + struct nf_conntrack *ct; + struct cache *cache; /* add new attributes here */ + char data[0]; +}; + +#endif diff --git a/daemon/src/Makefile.am b/daemon/src/Makefile.am new file mode 100644 index 0000000..5d1c6cb --- /dev/null +++ b/daemon/src/Makefile.am @@ -0,0 +1,22 @@ +include $(top_srcdir)/Make_global.am + +YACC=@YACC@ -d + +CLEANFILES = read_config_yy.c read_config_lex.c + +sbin_PROGRAMS = conntrackd +conntrackd_SOURCES = alarm.c main.c run.c hash.c buffer.c \ + local.c log.c mcast.c netlink.c proxy.c lock.c \ + ignore_pool.c \ + cache.c cache_iterators.c \ + cache_lifetime.c cache_timer.c \ + sync-mode.c sync-notrack.c sync-nack.c \ + traffic_stats.c stats-mode.c \ + network.c checksum.c \ + state_helper.c state_helper_tcp.c \ + read_config_yy.y read_config_lex.l + +conntrackd_LDFLAGS = $(all_libraries) -lnfnetlink -lnetfilter_conntrack \ + -lpthread + +EXTRA_DIST = read_config_yy.h diff --git a/daemon/src/alarm.c b/daemon/src/alarm.c new file mode 100644 index 0000000..1a465c2 --- /dev/null +++ b/daemon/src/alarm.c @@ -0,0 +1,141 @@ +/* + * (C) 2006 by Pablo Neira Ayuso + * + * 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 +#include +#include "linux_list.h" +#include "conntrackd.h" +#include "alarm.h" +#include "jhash.h" +#include +#include +#include + +/* alarm cascade */ +#define ALARM_CASCADE_SIZE 10 +static struct list_head *alarm_cascade; + +/* thread stuff */ +static pthread_t alarm_thread; + +struct alarm_list *create_alarm() +{ + return (struct alarm_list *) malloc(sizeof(struct alarm_list)); +} + +void destroy_alarm(struct alarm_list *t) +{ + free(t); +} + +void set_alarm_expiration(struct alarm_list *t, unsigned long expires) +{ + t->expires = expires; +} + +void set_alarm_function(struct alarm_list *t, + void (*fcn)(struct alarm_list *a, void *data)) +{ + t->function = fcn; +} + +void set_alarm_data(struct alarm_list *t, void *data) +{ + t->data = data; +} + +void init_alarm(struct alarm_list *t) +{ + INIT_LIST_HEAD(&t->head); + + t->expires = 0; + t->data = 0; + t->function = NULL; +} + +void add_alarm(struct alarm_list *alarm) +{ + unsigned int pos = jhash(alarm, sizeof(alarm), 0) % ALARM_CASCADE_SIZE; + + list_add(&alarm->head, &alarm_cascade[pos]); +} + +void del_alarm(struct alarm_list *alarm) +{ + list_del(&alarm->head); +} + +int mod_alarm(struct alarm_list *alarm, unsigned long expires) +{ + alarm->expires = expires; + return 0; +} + +void __run_alarms() +{ + struct list_head *i, *tmp; + struct alarm_list *t; + struct timespec req = {0, 1000000000 / ALARM_CASCADE_SIZE}; + struct timespec rem; + static int step = 0; + +retry: + if (nanosleep(&req, &rem) == -1) { + /* interrupted syscall: retry with remaining time */ + if (errno == EINTR) { + memcpy(&req, &rem, sizeof(struct timespec)); + goto retry; + } + } + + lock(); + list_for_each_safe(i, tmp, &alarm_cascade[step]) { + t = (struct alarm_list *) i; + + t->expires--; + if (t->expires == 0) + t->function(t, t->data); + } + step = (step + 1) < ALARM_CASCADE_SIZE ? step + 1 : 0; + unlock(); +} + +void *run_alarms(void *foo) +{ + while(1) + __run_alarms(); +} + +int create_alarm_thread() +{ + int i; + + alarm_cascade = malloc(sizeof(struct list_head) * ALARM_CASCADE_SIZE); + if (alarm_cascade == NULL) + return -1; + + for (i=0; i + * + * 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 "buffer.h" + +struct buffer *buffer_create(size_t max_size) +{ + struct buffer *b; + + b = malloc(sizeof(struct buffer)); + if (b == NULL) + return NULL; + memset(b, 0, sizeof(struct buffer)); + + b->max_size = max_size; + INIT_LIST_HEAD(&b->head); + pthread_mutex_init(&b->lock, NULL); + + return b; +} + +void buffer_destroy(struct buffer *b) +{ + struct list_head *i, *tmp; + struct buffer_node *node; + + pthread_mutex_lock(&b->lock); + list_for_each_safe(i, tmp, &b->head) { + node = (struct buffer_node *) i; + list_del(i); + free(node); + } + pthread_mutex_unlock(&b->lock); + pthread_mutex_destroy(&b->lock); + free(b); +} + +static struct buffer_node *buffer_node_create(const void *data, size_t size) +{ + struct buffer_node *n; + + n = malloc(sizeof(struct buffer_node) + size); + if (n == NULL) + return NULL; + + INIT_LIST_HEAD(&n->head); + n->size = size; + memcpy(n->data, data, size); + + return n; +} + +int buffer_add(struct buffer *b, const void *data, size_t size) +{ + int ret = 0; + struct buffer_node *n; + + pthread_mutex_lock(&b->lock); + + /* does it fit this buffer? */ + if (size > b->max_size) { + errno = ENOSPC; + ret = -1; + goto err; + } + +retry: + /* buffer is full: kill the oldest entry */ + if (b->cur_size + size > b->max_size) { + n = (struct buffer_node *) b->head.prev; + list_del(b->head.prev); + b->cur_size -= n->size; + free(n); + goto retry; + } + + n = buffer_node_create(data, size); + if (n == NULL) { + ret = -1; + goto err; + } + + list_add(&n->head, &b->head); + b->cur_size += size; + +err: + pthread_mutex_unlock(&b->lock); + return ret; +} + +void __buffer_del(struct buffer *b, void *data) +{ + struct buffer_node *n = container_of(data, struct buffer_node, data); + + list_del(&n->head); + b->cur_size -= n->size; + free(n); +} + +void buffer_del(struct buffer *b, void *data) +{ + pthread_mutex_lock(&b->lock); + buffer_del(b, data); + pthread_mutex_unlock(&b->lock); +} + +void buffer_iterate(struct buffer *b, + void *data, + int (*iterate)(void *data1, void *data2)) +{ + struct list_head *i, *tmp; + struct buffer_node *n; + + pthread_mutex_lock(&b->lock); + list_for_each_safe(i, tmp, &b->head) { + n = (struct buffer_node *) i; + if (iterate(n->data, data)) + break; + } + pthread_mutex_unlock(&b->lock); +} diff --git a/daemon/src/cache.c b/daemon/src/cache.c new file mode 100644 index 0000000..6f7442b --- /dev/null +++ b/daemon/src/cache.c @@ -0,0 +1,446 @@ +/* + * (C) 2006-2007 by Pablo Neira Ayuso + * + * 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 "jhash.h" +#include "hash.h" +#include "conntrackd.h" +#include +#include +#include "us-conntrack.h" +#include "cache.h" +#include "debug.h" + +static u_int32_t hash(const void *data, struct hashtable *table) +{ + unsigned int a, b; + const struct us_conntrack *u = data; + struct nf_conntrack *ct = u->ct; + + a = jhash(nfct_get_attr(ct, ATTR_ORIG_IPV4_SRC), sizeof(u_int32_t), + ((nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO) << 16) | + (nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO)))); + + b = jhash(nfct_get_attr(ct, ATTR_ORIG_IPV4_DST), sizeof(u_int32_t), + ((nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) << 16) | + (nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST)))); + + return jhash_2words(a, b, 0) % table->hashsize; +} + +static u_int32_t hash6(const void *data, struct hashtable *table) +{ + unsigned int a, b; + const struct us_conntrack *u = data; + struct nf_conntrack *ct = u->ct; + + a = jhash(nfct_get_attr(ct, ATTR_ORIG_IPV6_SRC), sizeof(u_int32_t), + ((nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO) << 16) | + (nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO)))); + + b = jhash(nfct_get_attr(ct, ATTR_ORIG_IPV6_DST), sizeof(u_int32_t), + ((nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) << 16) | + (nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST)))); + + return jhash_2words(a, b, 0) % table->hashsize; +} + +static int __compare(const struct nf_conntrack *ct1, + const struct nf_conntrack *ct2) +{ + return ((nfct_get_attr_u8(ct1, ATTR_ORIG_L3PROTO) == + nfct_get_attr_u8(ct2, ATTR_ORIG_L3PROTO)) && + (nfct_get_attr_u8(ct1, ATTR_ORIG_L4PROTO) == + nfct_get_attr_u8(ct2, ATTR_ORIG_L4PROTO)) && + (nfct_get_attr_u16(ct1, ATTR_ORIG_PORT_SRC) == + nfct_get_attr_u16(ct2, ATTR_ORIG_PORT_SRC)) && + (nfct_get_attr_u16(ct1, ATTR_ORIG_PORT_DST) == + nfct_get_attr_u16(ct2, ATTR_ORIG_PORT_DST)) && + (nfct_get_attr_u16(ct1, ATTR_REPL_PORT_SRC) == + nfct_get_attr_u16(ct2, ATTR_REPL_PORT_SRC)) && + (nfct_get_attr_u16(ct1, ATTR_REPL_PORT_DST) == + nfct_get_attr_u16(ct2, ATTR_REPL_PORT_DST))); +} + +static int compare(const void *data1, const void *data2) +{ + const struct us_conntrack *u1 = data1; + const struct us_conntrack *u2 = data2; + + return ((nfct_get_attr_u32(u1->ct, ATTR_ORIG_IPV4_SRC) == + nfct_get_attr_u32(u2->ct, ATTR_ORIG_IPV4_SRC)) && + (nfct_get_attr_u32(u1->ct, ATTR_ORIG_IPV4_DST) == + nfct_get_attr_u32(u2->ct, ATTR_ORIG_IPV4_DST)) && + (nfct_get_attr_u32(u1->ct, ATTR_REPL_IPV4_SRC) == + nfct_get_attr_u32(u2->ct, ATTR_REPL_IPV4_SRC)) && + (nfct_get_attr_u32(u1->ct, ATTR_REPL_IPV4_DST) == + nfct_get_attr_u32(u2->ct, ATTR_REPL_IPV4_DST)) && + __compare(u1->ct, u2->ct)); +} + +static int compare6(const void *data1, const void *data2) +{ + const struct us_conntrack *u1 = data1; + const struct us_conntrack *u2 = data2; + + return ((nfct_get_attr_u32(u1->ct, ATTR_ORIG_IPV6_SRC) == + nfct_get_attr_u32(u2->ct, ATTR_ORIG_IPV6_SRC)) && + (nfct_get_attr_u32(u1->ct, ATTR_ORIG_IPV6_DST) == + nfct_get_attr_u32(u2->ct, ATTR_ORIG_IPV6_DST)) && + (nfct_get_attr_u32(u1->ct, ATTR_REPL_IPV6_SRC) == + nfct_get_attr_u32(u2->ct, ATTR_REPL_IPV6_SRC)) && + (nfct_get_attr_u32(u1->ct, ATTR_REPL_IPV6_DST) == + nfct_get_attr_u32(u2->ct, ATTR_REPL_IPV6_DST)) && + __compare(u1->ct, u2->ct)); +} + +struct cache_feature *cache_feature[CACHE_MAX_FEATURE] = { + [TIMER_FEATURE] = &timer_feature, + [LIFETIME_FEATURE] = &lifetime_feature, +}; + +struct cache *cache_create(char *name, + unsigned int features, + u_int8_t proto, + struct cache_extra *extra) +{ + size_t size = sizeof(struct us_conntrack); + int i, j = 0; + struct cache *c; + struct cache_feature *feature_array[CACHE_MAX_FEATURE] = {}; + unsigned int feature_offset[CACHE_MAX_FEATURE] = {}; + unsigned int feature_type[CACHE_MAX_FEATURE] = {}; + + c = malloc(sizeof(struct cache)); + if (!c) + return NULL; + memset(c, 0, sizeof(struct cache)); + + strcpy(c->name, name); + + for (i = 0; i < CACHE_MAX_FEATURE; i++) { + if ((1 << i) & features) { + feature_array[j] = cache_feature[i]; + feature_offset[j] = size; + feature_type[i] = j; + size += cache_feature[i]->size; + j++; + } + } + + memcpy(c->feature_type, feature_type, sizeof(feature_type)); + + c->features = malloc(sizeof(struct cache_feature) * j); + if (!c->features) { + free(c); + return NULL; + } + memcpy(c->features, feature_array, sizeof(struct cache_feature) * j); + c->num_features = j; + + c->extra_offset = size; + c->extra = extra; + if (extra) + size += extra->size; + + c->feature_offset = malloc(sizeof(unsigned int) * j); + if (!c->feature_offset) { + free(c->features); + free(c); + return NULL; + } + memcpy(c->feature_offset, feature_offset, sizeof(unsigned int) * j); + + switch(proto) { + case AF_INET: + c->h = hashtable_create(CONFIG(hashsize), + CONFIG(limit), + size, + hash, + compare); + break; + case AF_INET6: + c->h = hashtable_create(CONFIG(hashsize), + CONFIG(limit), + size, + hash6, + compare6); + break; + } + + if (!c->h) { + free(c->features); + free(c->feature_offset); + free(c); + return NULL; + } + + return c; +} + +void cache_destroy(struct cache *c) +{ + lock(); + hashtable_destroy(c->h); + unlock(); + free(c->features); + free(c->feature_offset); + free(c); +} + +static struct us_conntrack *__add(struct cache *c, struct nf_conntrack *ct) +{ + int i; + size_t size = c->h->datasize; + char buf[size]; + struct us_conntrack *u = (struct us_conntrack *) buf; + struct nf_conntrack *newct; + + memset(u, 0, size); + + u->cache = c; + if ((u->ct = newct = nfct_new()) == NULL) { + errno = ENOMEM; + return 0; + } + memcpy(u->ct, ct, nfct_sizeof(ct)); + + u = hashtable_add(c->h, u); + if (u) { + void *data = u->data; + + for (i = 0; i < c->num_features; i++) { + c->features[i]->add(u, data); + data += c->features[i]->size; + } + + if (c->extra) + c->extra->add(u, ((void *) u) + c->extra_offset); + + return u; + } + free(newct); + + return NULL; +} + +struct us_conntrack *__cache_add(struct cache *c, struct nf_conntrack *ct) +{ + struct us_conntrack *u; + + u = __add(c, ct); + if (u) { + c->add_ok++; + return u; + } + c->add_fail++; + + return NULL; +} + +struct us_conntrack *cache_add(struct cache *c, struct nf_conntrack *ct) +{ + struct us_conntrack *u; + + lock(); + u = __cache_add(c, ct); + unlock(); + + return u; +} + +static struct us_conntrack *__update(struct cache *c, struct nf_conntrack *ct) +{ + size_t size = c->h->datasize; + char buf[size]; + struct us_conntrack *u = (struct us_conntrack *) buf; + + u->ct = ct; + + u = (struct us_conntrack *) hashtable_test(c->h, u); + if (u) { + int i; + void *data = u->data; + + for (i = 0; i < c->num_features; i++) { + c->features[i]->update(u, data); + data += c->features[i]->size; + } + + if (c->extra) + c->extra->update(u, ((void *) u) + c->extra_offset); + + if (nfct_attr_is_set(ct, ATTR_STATUS)) + nfct_set_attr_u32(u->ct, ATTR_STATUS, + nfct_get_attr_u32(ct, ATTR_STATUS)); + if (nfct_attr_is_set(ct, ATTR_TCP_STATE)) + nfct_set_attr_u8(u->ct, ATTR_TCP_STATE, + nfct_get_attr_u8(ct, ATTR_TCP_STATE)); + if (nfct_attr_is_set(ct, ATTR_TIMEOUT)) + nfct_set_attr_u32(u->ct, ATTR_TIMEOUT, + nfct_get_attr_u32(ct, ATTR_TIMEOUT)); + + return u; + } + return NULL; +} + +struct us_conntrack *__cache_update(struct cache *c, struct nf_conntrack *ct) +{ + struct us_conntrack *u; + + u = __update(c, ct); + if (u) { + c->upd_ok++; + return u; + } + c->upd_fail++; + + return NULL; +} + +struct us_conntrack *cache_update(struct cache *c, struct nf_conntrack *ct) +{ + struct us_conntrack *u; + + lock(); + u = __cache_update(c, ct); + unlock(); + + return u; +} + +struct us_conntrack *cache_update_force(struct cache *c, + struct nf_conntrack *ct) +{ + struct us_conntrack *u; + + lock(); + if ((u = __update(c, ct)) != NULL) { + c->upd_ok++; + unlock(); + return u; + } + if ((u = __add(c, ct)) != NULL) { + c->add_ok++; + unlock(); + return u; + } + c->add_fail++; + unlock(); + return NULL; +} + +int cache_test(struct cache *c, struct nf_conntrack *ct) +{ + size_t size = c->h->datasize; + char buf[size]; + struct us_conntrack *u = (struct us_conntrack *) buf; + void *ret; + + u->ct = ct; + + lock(); + ret = hashtable_test(c->h, u); + unlock(); + + return ret != NULL; +} + +static int __del(struct cache *c, struct nf_conntrack *ct) +{ + size_t size = c->h->datasize; + char buf[size]; + struct us_conntrack *u = (struct us_conntrack *) buf; + + u->ct = ct; + + u = (struct us_conntrack *) hashtable_test(c->h, u); + if (u) { + int i; + void *data = u->data; + struct nf_conntrack *p = u->ct; + + for (i = 0; i < c->num_features; i++) { + c->features[i]->destroy(u, data); + data += c->features[i]->size; + } + + if (c->extra) + c->extra->destroy(u, ((void *) u) + c->extra_offset); + + hashtable_del(c->h, u); + free(p); + return 1; + } + return 0; +} + +int __cache_del(struct cache *c, struct nf_conntrack *ct) +{ + if (__del(c, ct)) { + c->del_ok++; + return 1; + } + c->del_fail++; + + return 0; +} + +int cache_del(struct cache *c, struct nf_conntrack *ct) +{ + int ret; + + lock(); + ret = __cache_del(c, ct); + unlock(); + + return ret; +} + +struct us_conntrack *cache_get_conntrack(struct cache *c, void *data) +{ + return data - c->extra_offset; +} + +void *cache_get_extra(struct cache *c, void *data) +{ + return data + c->extra_offset; +} + +void cache_stats(struct cache *c, int fd) +{ + char buf[512]; + int size; + + lock(); + size = sprintf(buf, "cache %s:\n" + "current active connections:\t%12u\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", + c->name, + hashtable_counter(c->h), + c->add_ok, + c->add_fail, + c->upd_ok, + c->upd_fail, + c->del_ok, + c->del_fail); + unlock(); + send(fd, buf, size, 0); +} diff --git a/daemon/src/cache_iterators.c b/daemon/src/cache_iterators.c new file mode 100644 index 0000000..5d5d22b --- /dev/null +++ b/daemon/src/cache_iterators.c @@ -0,0 +1,229 @@ +/* + * (C) 2006-2007 by Pablo Neira Ayuso + * + * 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 "jhash.h" +#include "hash.h" +#include "conntrackd.h" +#include +#include +#include "us-conntrack.h" +#include "debug.h" + +struct __dump_container { + int fd; + int type; +}; + +static int do_dump(void *data1, void *data2) +{ + char buf[1024]; + int size; + struct __dump_container *container = data1; + struct us_conntrack *u = data2; + void *data = u->data; + int i; + + memset(buf, 0, sizeof(buf)); + size = nfct_snprintf(buf, + sizeof(buf), + u->ct, + NFCT_T_UNKNOWN, + container->type, + 0); + + for (i = 0; i < u->cache->num_features; i++) { + if (u->cache->features[i]->dump) { + size += u->cache->features[i]->dump(u, + data, + buf+size, + container->type); + data += u->cache->features[i]->size; + } + } + size += sprintf(buf+size, "\n"); + if (send(container->fd, buf, size, 0) == -1) { + if (errno != EPIPE) + return -1; + } + + return 0; +} + +void cache_dump(struct cache *c, int fd, int type) +{ + struct __dump_container tmp = { + .fd = fd, + .type = type + }; + + lock(); + hashtable_iterate(c->h, (void *) &tmp, do_dump); + unlock(); +} + +static int do_commit(void *data1, void *data2) +{ + int ret; + struct cache *c = data1; + struct us_conntrack *u = data2; + struct nf_conntrack *ct; + char buf[4096]; + struct nlmsghdr *nlh = (struct nlmsghdr *)buf; + + ct = nfct_clone(u->ct); + if (ct == NULL) + return 0; + + if (nfct_attr_is_set(ct, ATTR_STATUS)) { + u_int32_t status = nfct_get_attr_u32(ct, ATTR_STATUS); + status &= ~IPS_EXPECTED; + nfct_set_attr_u32(ct, ATTR_STATUS, status); + } + + if (nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT)) + nfct_setobjopt(ct, NFCT_SOPT_UNDO_SNAT); + if (nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT)) + nfct_setobjopt(ct, NFCT_SOPT_UNDO_DNAT); + if (nfct_getobjopt(ct, NFCT_GOPT_IS_SPAT)) + nfct_setobjopt(ct, NFCT_SOPT_UNDO_SPAT); + if (nfct_getobjopt(ct, NFCT_GOPT_IS_DPAT)) + nfct_setobjopt(ct, NFCT_SOPT_UNDO_DPAT); + + /* + * Set a reduced timeout for candidate-to-be-committed + * conntracks that live in the external cache + */ + nfct_set_attr_u32(ct, ATTR_TIMEOUT, CONFIG(commit_timeout)); + + ret = nfct_build_query(STATE(subsys_sync), + NFCT_Q_CREATE, + ct, + nlh, + sizeof(buf)); + + free(ct); + + if (ret == -1) { + /* XXX: Please cleanup this debug crap, default in logfile */ + debug("--- failed to build: %s --- \n", strerror(errno)); + return 0; + } + + ret = nfnl_query(STATE(sync), nlh); + if (ret == -1) { + switch(errno) { + case EEXIST: + c->commit_exist++; + break; + default: + c->commit_fail++; + break; + } + debug("--- failed to commit: %s --- \n", strerror(errno)); + } else { + c->commit_ok++; + debug("----- commit -----\n"); + } + + /* keep iterating even if we have found errors */ + return 0; +} + +void cache_commit(struct cache *c) +{ + unsigned int commit_ok = c->commit_ok; + unsigned int commit_exist = c->commit_exist; + unsigned int commit_fail = c->commit_fail; + + lock(); + hashtable_iterate(c->h, c, do_commit); + unlock(); + + /* calculate new entries committed */ + commit_ok = c->commit_ok - commit_ok; + commit_fail = c->commit_fail - commit_fail; + commit_exist = c->commit_exist - commit_exist; + + /* log results */ + dlog(STATE(log), "Committed %u new entries", commit_ok); + + if (commit_exist) + dlog(STATE(log), "%u entries ignored, " + "already exist", commit_exist); + if (commit_fail) + dlog(STATE(log), "%u entries can't be " + "committed", commit_fail); +} + +static int do_flush(void *data1, void *data2) +{ + struct cache *c = data1; + struct us_conntrack *u = data2; + void *data = u->data; + int i; + + for (i = 0; i < c->num_features; i++) { + c->features[i]->destroy(u, data); + data += c->features[i]->size; + } + free(u->ct); + + return 0; +} + +void cache_flush(struct cache *c) +{ + lock(); + hashtable_iterate(c->h, c, do_flush); + hashtable_flush(c->h); + c->flush++; + unlock(); +} + +#include "sync.h" +#include "network.h" + +static int do_bulk(void *data1, void *data2) +{ + int ret; + struct us_conntrack *u = data2; + char buf[4096]; + struct nlnetwork *net = (struct nlnetwork *) buf; + + ret = build_network_msg(NFCT_Q_UPDATE, + STATE(subsys_sync), + u->ct, + buf, + sizeof(buf)); + if (ret == -1) + debug_ct(u->ct, "failed to build"); + + mcast_send_netmsg(STATE_SYNC(mcast_client), net); + STATE_SYNC(mcast_sync)->post_send(net, u); + + /* keep iterating even if we have found errors */ + return 0; +} + +void cache_bulk(struct cache *c) +{ + lock(); + hashtable_iterate(c->h, NULL, do_bulk); + unlock(); +} diff --git a/daemon/src/cache_lifetime.c b/daemon/src/cache_lifetime.c new file mode 100644 index 0000000..ae54df2 --- /dev/null +++ b/daemon/src/cache_lifetime.c @@ -0,0 +1,65 @@ +/* + * (C) 2006 by Pablo Neira Ayuso + * + * 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 +#include "conntrackd.h" +#include "us-conntrack.h" +#include "cache.h" +#include "alarm.h" + +static void lifetime_add(struct us_conntrack *u, void *data) +{ + long *lifetime = data; + struct timeval tv; + + gettimeofday(&tv, NULL); + + *lifetime = tv.tv_sec; +} + +static void lifetime_update(struct us_conntrack *u, void *data) +{ +} + +static void lifetime_destroy(struct us_conntrack *u, void *data) +{ +} + +static int lifetime_dump(struct us_conntrack *u, + void *data, + char *buf, + int type) +{ + long *lifetime = data; + struct timeval tv; + + if (type == NFCT_O_XML) + return 0; + + gettimeofday(&tv, NULL); + + return sprintf(buf, " [active since %lds]", tv.tv_sec - *lifetime); +} + +struct cache_feature lifetime_feature = { + .size = sizeof(long), + .add = lifetime_add, + .update = lifetime_update, + .destroy = lifetime_destroy, + .dump = lifetime_dump +}; diff --git a/daemon/src/cache_timer.c b/daemon/src/cache_timer.c new file mode 100644 index 0000000..213b59a --- /dev/null +++ b/daemon/src/cache_timer.c @@ -0,0 +1,72 @@ +/* + * (C) 2006 by Pablo Neira Ayuso + * + * 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 +#include "conntrackd.h" +#include "us-conntrack.h" +#include "cache.h" +#include "alarm.h" + +static void timeout(struct alarm_list *a, void *data) +{ + struct us_conntrack *u = data; + + debug_ct(u->ct, "expired timeout"); + __cache_del(u->cache, u->ct); +} + +static void timer_add(struct us_conntrack *u, void *data) +{ + struct alarm_list *alarm = data; + + init_alarm(alarm); + set_alarm_expiration(alarm, CONFIG(cache_timeout)); + set_alarm_data(alarm, u); + set_alarm_function(alarm, timeout); + add_alarm(alarm); +} + +static void timer_update(struct us_conntrack *u, void *data) +{ + struct alarm_list *alarm = data; + mod_alarm(alarm, CONFIG(cache_timeout)); +} + +static void timer_destroy(struct us_conntrack *u, void *data) +{ + struct alarm_list *alarm = data; + del_alarm(alarm); +} + +static int timer_dump(struct us_conntrack *u, void *data, char *buf, int type) +{ + struct alarm_list *alarm = data; + + if (type == NFCT_O_XML) + return 0; + + return sprintf(buf, " [expires in %ds]", alarm->expires); +} + +struct cache_feature timer_feature = { + .size = sizeof(struct alarm_list), + .add = timer_add, + .update = timer_update, + .destroy = timer_destroy, + .dump = timer_dump +}; diff --git a/daemon/src/checksum.c b/daemon/src/checksum.c new file mode 100644 index 0000000..41866ff --- /dev/null +++ b/daemon/src/checksum.c @@ -0,0 +1,32 @@ +/* + * Extracted from RFC 1071 with some minor changes to fix compilation on GCC, + * this can probably be improved + * --pablo 11/feb/07 + */ + +#include + +unsigned short do_csum(const void *addr, unsigned int count) +{ + unsigned int sum = 0; + + /* checksumming disabled, just skip */ + if (CONFIG(flags) & DONT_CHECKSUM) + return 0; + + while(count > 1) { + /* This is the inner loop */ + sum += *((unsigned short *) addr++); + count -= 2; + } + + /* Add left-over byte, if any */ + if(count > 0) + sum += *((unsigned char *) addr); + + /* Fold 32-bit sum to 16 bits */ + while (sum>>16) + sum = (sum & 0xffff) + (sum >> 16); + + return ~sum; +} diff --git a/daemon/src/hash.c b/daemon/src/hash.c new file mode 100644 index 0000000..274a140 --- /dev/null +++ b/daemon/src/hash.c @@ -0,0 +1,199 @@ +/* + * (C) 2006-2007 by Pablo Neira Ayuso + * + * 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. + * + * Description: generic hash table implementation + */ + +#include +#include +#include +#include +#include +#include "slist.h" +#include "hash.h" + + +struct hashtable_node *hashtable_alloc_node(int datasize, void *data) +{ + struct hashtable_node *n; + int size = sizeof(struct hashtable_node) + datasize; + + n = malloc(size); + if (!n) + return NULL; + memset(n, 0, size); + memcpy(n->data, data, datasize); + + return n; +} + +void hashtable_destroy_node(struct hashtable_node *h) +{ + free(h); +} + +struct hashtable * +hashtable_create(int hashsize, int limit, int datasize, + u_int32_t (*hash)(const void *data, struct hashtable *table), + int (*compare)(const void *data1, const void *data2)) +{ + int i; + struct hashtable *h; + struct hashtype *t; + int size = sizeof(struct hashtable) + + hashsize * sizeof(struct slist_head); + + h = (struct hashtable *) malloc(size); + if (!h) { + errno = ENOMEM; + return NULL; + } + + memset(h, 0, size); + for (i=0; imembers[i]); + + h->hashsize = hashsize; + h->limit = limit; + h->datasize = datasize; + h->hash = hash; + h->compare = compare; + + return h; +} + +void hashtable_destroy(struct hashtable *h) +{ + hashtable_flush(h); + free(h); +} + +void *hashtable_add(struct hashtable *table, void *data) +{ + struct slist_head *e; + struct hashtable_node *n; + u_int32_t id; + int i; + + /* hash table is full */ + if (table->count >= table->limit) { + errno = ENOSPC; + return NULL; + } + + id = table->hash(data, table); + + slist_for_each(e, &table->members[id]) { + n = slist_entry(e, struct hashtable_node, head); + if (table->compare(n->data, data)) { + errno = EEXIST; + return NULL; + } + } + + n = hashtable_alloc_node(table->datasize, data); + if (n == NULL) { + errno = ENOMEM; + return NULL; + } + + slist_add(&table->members[id], &n->head); + table->count++; + + return n->data; +} + +void *hashtable_test(struct hashtable *table, const void *data) +{ + struct slist_head *e; + u_int32_t id; + struct hashtable_node *n; + int i; + + id = table->hash(data, table); + + slist_for_each(e, &table->members[id]) { + n = slist_entry(e, struct hashtable_node, head); + if (table->compare(n->data, data)) + return n->data; + } + + errno = ENOENT; + return NULL; +} + +int hashtable_del(struct hashtable *table, void *data) +{ + struct slist_head *e, *next, *prev; + u_int32_t id; + struct hashtable_node *n; + int i; + + id = table->hash(data, table); + + slist_for_each_safe(e, prev, next, &table->members[id]) { + n = slist_entry(e, struct hashtable_node, head); + if (table->compare(n->data, data)) { + slist_del(e, prev); + hashtable_destroy_node(n); + table->count--; + return 0; + } + } + errno = ENOENT; + return -1; +} + +int hashtable_flush(struct hashtable *table) +{ + int i; + struct slist_head *e, *next, *prev; + struct hashtable_node *n; + + for (i=0; i < table->hashsize; i++) + slist_for_each_safe(e, prev, next, &table->members[i]) { + n = slist_entry(e, struct hashtable_node, head); + slist_del(e, prev); + hashtable_destroy_node(n); + } + + table->count = 0; + + return 0; +} + +int hashtable_iterate(struct hashtable *table, void *data, + int (*iterate)(void *data1, void *data2)) +{ + int i; + struct slist_head *e, *next, *prev; + struct hashtable_node *n; + + for (i=0; i < table->hashsize; i++) { + slist_for_each_safe(e, prev, next, &table->members[i]) { + n = slist_entry(e, struct hashtable_node, head); + if (iterate(data, n->data) == -1) + return -1; + } + } + return 0; +} + +unsigned int hashtable_counter(struct hashtable *table) +{ + return table->count; +} diff --git a/daemon/src/ignore_pool.c b/daemon/src/ignore_pool.c new file mode 100644 index 0000000..5946617 --- /dev/null +++ b/daemon/src/ignore_pool.c @@ -0,0 +1,136 @@ +/* + * (C) 2006-2007 by Pablo Neira Ayuso + * + * 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 "jhash.h" +#include "hash.h" +#include "conntrackd.h" +#include "ignore.h" +#include "debug.h" +#include + +#define IGNORE_POOL_SIZE 32 +#define IGNORE_POOL_LIMIT 1024 + +static u_int32_t hash(const void *data, struct hashtable *table) +{ + const u_int32_t *ip = data; + + return jhash_1word(*ip, 0) % table->hashsize; +} + +static u_int32_t hash6(const void *data, struct hashtable *table) +{ + return jhash(data, sizeof(u_int32_t)*4, 0) % table->hashsize; +} + +static int compare(const void *data1, const void *data2) +{ + const u_int32_t *ip1 = data1; + const u_int32_t *ip2 = data2; + + return *ip1 == *ip2; +} + +static int compare6(const void *data1, const void *data2) +{ + return memcmp(data1, data2, sizeof(u_int32_t)*4) == 0; +} + +struct ignore_pool *ignore_pool_create(u_int8_t proto) +{ + int i, j = 0; + struct ignore_pool *ip; + + ip = malloc(sizeof(struct ignore_pool)); + if (!ip) + return NULL; + memset(ip, 0, sizeof(struct ignore_pool)); + + switch(proto) { + case AF_INET: + ip->h = hashtable_create(IGNORE_POOL_SIZE, + IGNORE_POOL_LIMIT, + sizeof(u_int32_t), + hash, + compare); + break; + case AF_INET6: + ip->h = hashtable_create(IGNORE_POOL_SIZE, + IGNORE_POOL_LIMIT, + sizeof(u_int32_t)*4, + hash6, + compare6); + break; + } + + if (!ip->h) { + free(ip); + return NULL; + } + + return ip; +} + +void ignore_pool_destroy(struct ignore_pool *ip) +{ + hashtable_destroy(ip->h); + free(ip); +} + +int ignore_pool_add(struct ignore_pool *ip, void *data) +{ + if (!hashtable_add(ip->h, data)) + return 0; + + return 1; +} + +int __ignore_pool_test_ipv4(struct ignore_pool *ip, struct nf_conntrack *ct) +{ + return (hashtable_test(ip->h, nfct_get_attr(ct, ATTR_ORIG_IPV4_SRC)) || + hashtable_test(ip->h, nfct_get_attr(ct, ATTR_ORIG_IPV4_DST)) || + hashtable_test(ip->h, nfct_get_attr(ct, ATTR_REPL_IPV4_SRC)) || + hashtable_test(ip->h, nfct_get_attr(ct, ATTR_REPL_IPV4_DST))); +} + +int __ignore_pool_test_ipv6(struct ignore_pool *ip, struct nf_conntrack *ct) +{ + return (hashtable_test(ip->h, nfct_get_attr(ct, ATTR_ORIG_IPV6_SRC)) || + hashtable_test(ip->h, nfct_get_attr(ct, ATTR_ORIG_IPV6_DST)) || + hashtable_test(ip->h, nfct_get_attr(ct, ATTR_REPL_IPV6_SRC)) || + hashtable_test(ip->h, nfct_get_attr(ct, ATTR_REPL_IPV6_DST))); +} + +int ignore_pool_test(struct ignore_pool *ip, struct nf_conntrack *ct) +{ + int ret; + + switch(nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO)) { + case AF_INET: + ret = __ignore_pool_test_ipv4(ip, ct); + break; + case AF_INET6: + ret = __ignore_pool_test_ipv6(ip, ct); + break; + default: + dlog(STATE(log), "unknown conntrack layer 3 protocol?"); + break; + } + + return ret; +} diff --git a/daemon/src/local.c b/daemon/src/local.c new file mode 100644 index 0000000..eef70ad --- /dev/null +++ b/daemon/src/local.c @@ -0,0 +1,159 @@ +/* + * (C) 2006 by Pablo Neira Ayuso + * + * 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. + * + * Description: UNIX sockets library + */ + +#include +#include +#include +#include +#include "debug.h" + +#include "local.h" + +int local_server_create(struct local_conf *conf) +{ + int fd; + int len; + struct sockaddr_un local; + + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + debug("local_server_create:socket"); + return -1; + } + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &conf->reuseaddr, + sizeof(conf->reuseaddr)) == -1) { + debug("local_server_create:setsockopt"); + close(fd); + return -1; + } + + local.sun_family = AF_UNIX; + strcpy(local.sun_path, conf->path); + len = strlen(local.sun_path) + sizeof(local.sun_family); + unlink(conf->path); + + if (bind(fd, (struct sockaddr *) &local, len) == -1) { + debug("local_server_create:bind"); + close(fd); + return -1; + } + + if (listen(fd, conf->backlog) == -1) { + close(fd); + debug("local_server_create:listen"); + return -1; + } + + return fd; +} + +void local_server_destroy(int fd) +{ + close(fd); +} + +int do_local_server_step(int fd, void *data, + void (*process)(int fd, void *data)) +{ + int rfd; + struct sockaddr_un local; + size_t sin_size = sizeof(struct sockaddr_un); + + if ((rfd = accept(fd, (struct sockaddr *)&local, &sin_size)) == -1) { + debug("do_local_server_step:accept"); + return -1; + } + + process(rfd, data); + close(rfd); + + return 0; +} + +int local_client_create(struct local_conf *conf) +{ + int len; + struct sockaddr_un local; + int fd; + + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) + return -1; + + local.sun_family = AF_UNIX; + strcpy(local.sun_path, conf->path); + len = strlen(local.sun_path) + sizeof(local.sun_family); + + if (connect(fd, (struct sockaddr *) &local, len) == -1) { + close(fd); + debug("local_client_create: connect: "); + return -1; + } + + return fd; +} + +void local_client_destroy(int fd) +{ + close(fd); +} + +int do_local_client_step(int fd, void (*process)(char *buf)) +{ + int numbytes; + char buf[1024]; + + memset(buf, 0, sizeof(buf)); + while ((numbytes = recv(fd, buf, sizeof(buf)-1, 0)) > 0) { + buf[sizeof(buf)-1] = '\0'; + if (process) + process(buf); + memset(buf, 0, sizeof(buf)); + } + + return 0; +} + +void local_step(char *buf) +{ + printf(buf); +} + +int do_local_request(int request, + struct local_conf *conf, + void (*step)(char *buf)) +{ + int fd, ret; + + fd = local_client_create(conf); + if (fd == -1) + return -1; + + ret = send(fd, &request, sizeof(int), 0); + if (ret == -1) { + debug("send:"); + return -1; + } + + do_local_client_step(fd, step); + + local_client_destroy(fd); + + return 0; +} diff --git a/daemon/src/lock.c b/daemon/src/lock.c new file mode 100644 index 0000000..cd68baf --- /dev/null +++ b/daemon/src/lock.c @@ -0,0 +1,32 @@ +/* + * (C) 2006 by Pablo Neira Ayuso + * + * 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 +#include + +static pthread_mutex_t global_lock = PTHREAD_MUTEX_INITIALIZER; + +void lock() +{ + pthread_mutex_lock(&global_lock); +} + +void unlock() +{ + pthread_mutex_unlock(&global_lock); +} diff --git a/daemon/src/log.c b/daemon/src/log.c new file mode 100644 index 0000000..88cadea --- /dev/null +++ b/daemon/src/log.c @@ -0,0 +1,57 @@ +/* + * (C) 2006 by Pablo Neira Ayuso + * + * 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. + * + * Description: Logging support for the conntrack daemon + */ + +#include +#include +#include +#include + +FILE *init_log(char *filename) +{ + FILE *fd; + + fd = fopen(filename, "a+"); + if (fd == NULL) { + fprintf(stderr, "can't open log file `%s'\n", filename); + return NULL; + } + + return fd; +} + +void dlog(FILE *fd, char *format, ...) +{ + time_t t = time(NULL); + char *buf = ctime(&t); + va_list args; + + buf[strlen(buf)-1]='\0'; + va_start(args, format); + fprintf(fd, "[%s] (pid=%d) ", buf, getpid()); + vfprintf(fd, format, args); + va_end(args); + fprintf(fd, "\n"); + fflush(fd); +} + +void close_log(FILE *fd) +{ + fclose(fd); +} diff --git a/daemon/src/main.c b/daemon/src/main.c new file mode 100644 index 0000000..1c75970 --- /dev/null +++ b/daemon/src/main.c @@ -0,0 +1,302 @@ +/* + * (C) 2006-2007 by Pablo Neira Ayuso + * + * 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 +#include "conntrackd.h" +#include "log.h" +#include +#include +#include +#include +#include +#include +#include "hash.h" +#include "jhash.h" + +struct ct_general_state st; +union ct_state state; + +static const char usage_daemon_commands[] = + "Daemon mode commands:\n" + " -d [options]\t\tRun in daemon mode\n" + " -S [options]\t\tRun in statistics mode\n"; + +static const char usage_client_commands[] = + "Client mode commands:\n" + " -c, commit external cache to conntrack table\n" + " -f, 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" + " -k, kill conntrack daemon\n" + " -s, dump statistics\n" + " -R, resync with kernel conntrack table\n" + " -n, request resync with other node (only NACK mode)\n" + " -x, dump cache in XML format (requires -i or -e)"; + +static const char usage_options[] = + "Options:\n" + " -C [configfile], configuration file path\n"; + +void show_usage(char *progname) +{ + fprintf(stdout, "Connection tracking userspace daemon v%s\n", VERSION); + fprintf(stdout, "Usage: %s [commands] [options]\n\n", progname); + fprintf(stdout, "%s\n", usage_daemon_commands); + fprintf(stdout, "%s\n", usage_client_commands); + fprintf(stdout, "%s\n", usage_options); +} + +/* These live in run.c */ +int init(int); +void run(void); + +void set_operation_mode(int *current, int want, char *argv[]) +{ + if (*current == NOT_SET) { + *current = want; + return; + } + if (*current != want) { + show_usage(argv[0]); + fprintf(stderr, "\nError: Invalid parameters\n"); + exit(EXIT_FAILURE); + } +} + +static int check_capabilities(void) +{ + int ret; + cap_user_header_t hcap; + cap_user_data_t dcap; + + hcap = malloc(sizeof(cap_user_header_t)); + if (!hcap) + return -1; + + hcap->version = _LINUX_CAPABILITY_VERSION; + hcap->pid = getpid(); + + dcap = malloc(sizeof(cap_user_data_t)); + if (!dcap) { + free(hcap); + return -1; + } + + if (capget(hcap, dcap) == -1) { + free(hcap); + free(dcap); + return -1; + } + + ret = dcap->permitted & (1 << CAP_NET_ADMIN); + + free(hcap); + free(dcap); + + return ret; +} + +int main(int argc, char *argv[]) +{ + int ret, i, config_set = 0, action; + char config_file[PATH_MAX]; + int type = 0, mode = 0; + struct utsname u; + int version, major, minor; + + /* Check kernel version: it must be >= 2.6.18 */ + if (uname(&u) == -1) { + fprintf(stderr, "Can't retrieve kernel version via uname()\n"); + exit(EXIT_FAILURE); + } + sscanf(u.release, "%d.%d.%d", &version, &major, &minor); + if (version < 2 && major < 6) { + fprintf(stderr, "Linux kernel version must be >= 2.6.18\n"); + exit(EXIT_FAILURE); + } + + if (major == 6 && minor < 18) { + fprintf(stderr, "Linux kernel version must be >= 2.6.18\n"); + exit(EXIT_FAILURE); + } + + ret = check_capabilities(); + switch (ret) { + case -1: + fprintf(stderr, "Can't get capabilities\n"); + exit(EXIT_FAILURE); + break; + case 0: + fprintf(stderr, "You require CAP_NET_ADMIN in order " + "to run conntrackd\n"); + exit(EXIT_FAILURE); + break; + default: + break; + } + + for (i=1; i= PATH_MAX){ + config_file[PATH_MAX-1]='\0'; + fprintf(stderr, "Path to config file " + "to long. Cutting it " + "down to %d characters", + PATH_MAX); + } + config_set = 1; + break; + } + show_usage(argv[0]); + fprintf(stderr, "Missing config filename\n"); + break; + case 'F': + set_operation_mode(&type, REQUEST, argv); + action = FLUSH_MASTER; + case 'f': + set_operation_mode(&type, REQUEST, argv); + action = FLUSH_CACHE; + break; + case 'R': + set_operation_mode(&type, REQUEST, argv); + action = RESYNC_MASTER; + break; + case 'B': + set_operation_mode(&type, REQUEST, argv); + action = SEND_BULK; + break; + case 'k': + set_operation_mode(&type, REQUEST, argv); + action = KILL; + break; + case 's': + set_operation_mode(&type, REQUEST, argv); + action = STATS; + break; + case 'S': + set_operation_mode(&mode, STATS_MODE, argv); + break; + case 'n': + set_operation_mode(&type, REQUEST, argv); + action = REQUEST_DUMP; + break; + case 'x': + if (action == DUMP_INTERNAL) + action = DUMP_INT_XML; + else if (action == DUMP_EXTERNAL) + action = DUMP_EXT_XML; + else { + show_usage(argv[0]); + fprintf(stderr, "Error: Invalid parameters\n"); + exit(EXIT_FAILURE); + + } + break; + default: + show_usage(argv[0]); + fprintf(stderr, "Unknown option: %s\n", argv[i]); + return 0; + break; + } + } + + if (config_set == 0) + strcpy(config_file, DEFAULT_CONFIGFILE); + + if ((ret = init_config(config_file)) == -1) { + fprintf(stderr, "can't open config file `%s'\n", config_file); + exit(EXIT_FAILURE); + } + + /* + * Setting up logfile + */ + STATE(log) = init_log(CONFIG(logfile)); + if (!STATE(log)) { + fprintf(stdout, "can't open logfile `%s\n'", CONFIG(logfile)); + exit(EXIT_FAILURE); + } + + if (type == REQUEST) { + if (do_local_request(action, &conf.local, local_step) == -1) + fprintf(stderr, "can't connect: is conntrackd " + "running? appropiate permissions?\n"); + exit(EXIT_SUCCESS); + } + + /* + * lock file + */ + if ((ret = open(CONFIG(lockfile), O_CREAT | O_EXCL | O_TRUNC)) == -1) { + fprintf(stderr, "lockfile `%s' exists, perhaps conntrackd " + "already running?\n", CONFIG(lockfile)); + exit(EXIT_FAILURE); + } + close(ret); + + /* Daemonize conntrackd */ + if (type == DAEMON) { + pid_t pid; + + if ((pid = fork()) == -1) { + dlog(STATE(log), "fork() failed: " + "%s", strerror(errno)); + exit(EXIT_FAILURE); + } else if (pid) + exit(EXIT_SUCCESS); + + dlog(STATE(log), "--- starting in daemon mode ---"); + } else + dlog(STATE(log), "--- starting in console mode ---"); + + /* + * initialization process + */ + + if (init(mode) == -1) { + close_log(STATE(log)); + fprintf(stderr, "ERROR: conntrackd cannot start, please " + "check the logfile for more info\n"); + unlink(CONFIG(lockfile)); + exit(EXIT_FAILURE); + } + + /* + * run main process + */ + run(); +} diff --git a/daemon/src/mcast.c b/daemon/src/mcast.c new file mode 100644 index 0000000..9904544 --- /dev/null +++ b/daemon/src/mcast.c @@ -0,0 +1,287 @@ +/* + * (C) 2006 by Pablo Neira Ayuso + * + * 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. + * + * Description: multicast socket library + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mcast.h" +#include "debug.h" + +struct mcast_sock *mcast_server_create(struct mcast_conf *conf) +{ + int yes = 1; + union { + struct ip_mreq ipv4; + struct ipv6_mreq ipv6; + } mreq; + struct mcast_sock *m; + + m = (struct mcast_sock *) malloc(sizeof(struct mcast_sock)); + if (!m) + return NULL; + memset(m, 0, sizeof(struct mcast_sock)); + + switch(conf->ipproto) { + case AF_INET: + mreq.ipv4.imr_multiaddr.s_addr = conf->in.inet_addr.s_addr; + mreq.ipv4.imr_interface.s_addr =conf->ifa.interface_addr.s_addr; + + m->addr.ipv4.sin_family = AF_INET; + m->addr.ipv4.sin_port = htons(conf->port); + m->addr.ipv4.sin_addr.s_addr = htonl(INADDR_ANY); + break; + + case AF_INET6: + memcpy(&mreq.ipv6.ipv6mr_multiaddr, &conf->in.inet_addr6, + sizeof(u_int32_t) * 4); + memcpy(&mreq.ipv6.ipv6mr_interface, &conf->ifa.interface_addr6, + sizeof(u_int32_t) * 4); + + m->addr.ipv6.sin6_family = AF_INET6; + m->addr.ipv6.sin6_port = htons(conf->port); + m->addr.ipv6.sin6_addr = in6addr_any; + break; + } + + if ((m->fd = socket(conf->ipproto, SOCK_DGRAM, 0)) == -1) { + debug("mcast_sock_server_create:socket"); + free(m); + return NULL; + } + + if (setsockopt(m->fd, SOL_SOCKET, SO_REUSEADDR, &yes, + sizeof(int)) == -1) { + debug("mcast_sock_server_create:setsockopt1"); + close(m->fd); + free(m); + return NULL; + } + + if (bind(m->fd, (struct sockaddr *) &m->addr, + sizeof(struct sockaddr)) == -1) { + debug("mcast_sock_server_create:bind"); + close(m->fd); + free(m); + return NULL; + } + + + switch(conf->ipproto) { + case AF_INET: + if (setsockopt(m->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &mreq.ipv4, sizeof(mreq.ipv4)) < 0) { + debug("mcast_sock_server_create:setsockopt2"); + close(m->fd); + free(m); + return NULL; + } + break; + case AF_INET6: + if (setsockopt(m->fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, + &mreq.ipv6, sizeof(mreq.ipv6)) < 0) { + debug("mcast_sock_server_create:setsockopt2"); + close(m->fd); + free(m); + return NULL; + } + break; + } + + return m; +} + +void mcast_server_destroy(struct mcast_sock *m) +{ + close(m->fd); + free(m); +} + +static int +__mcast_client_create_ipv4(struct mcast_sock *m, struct mcast_conf *conf) +{ + int no = 0; + + m->addr.ipv4.sin_family = AF_INET; + m->addr.ipv4.sin_port = htons(conf->port); + m->addr.ipv4.sin_addr = conf->in.inet_addr; + + if (setsockopt(m->fd, IPPROTO_IP, IP_MULTICAST_LOOP, &no, + sizeof(int)) < 0) { + debug("mcast_sock_client_create:setsockopt2"); + close(m->fd); + return -1; + } + + if (setsockopt(m->fd, IPPROTO_IP, IP_MULTICAST_IF, + &conf->ifa.interface_addr, + sizeof(struct in_addr)) == -1) { + debug("mcast_sock_client_create:setsockopt3"); + close(m->fd); + free(m); + return -1; + } + + return 0; +} + +static int +__mcast_client_create_ipv6(struct mcast_sock *m, struct mcast_conf *conf) +{ + int no = 0; + + m->addr.ipv6.sin6_family = AF_INET6; + m->addr.ipv6.sin6_port = htons(conf->port); + memcpy(&m->addr.ipv6.sin6_addr, + &conf->in.inet_addr6, + sizeof(struct in6_addr)); + + if (setsockopt(m->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &no, + sizeof(int)) < 0) { + debug("mcast_sock_client_create:setsockopt2"); + close(m->fd); + return -1; + } + + if (setsockopt(m->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, + &conf->ifa.interface_addr, + sizeof(struct in_addr)) == -1) { + debug("mcast_sock_client_create:setsockopt3"); + close(m->fd); + free(m); + return -1; + } + + return 0; +} + +struct mcast_sock *mcast_client_create(struct mcast_conf *conf) +{ + int ret = 0; + struct sockaddr_in addr; + struct mcast_sock *m; + + m = (struct mcast_sock *) malloc(sizeof(struct mcast_sock)); + if (!m) + return NULL; + memset(m, 0, sizeof(struct mcast_sock)); + + if ((m->fd = socket(conf->ipproto, SOCK_DGRAM, 0)) == -1) { + debug("mcast_sock_client_create:socket"); + return NULL; + } + + switch(conf->ipproto) { + case AF_INET: + ret = __mcast_client_create_ipv4(m, conf); + break; + case AF_INET6: + ret = __mcast_client_create_ipv6(m, conf); + break; + default: + break; + } + + if (ret == -1) { + free(m); + m = NULL; + } + + return m; +} + +void mcast_client_destroy(struct mcast_sock *m) +{ + close(m->fd); + free(m); +} + +int mcast_send(struct mcast_sock *m, void *data, int size) +{ + int ret; + + ret = sendto(m->fd, + data, + size, + 0, + (struct sockaddr *) &m->addr, + sizeof(struct sockaddr)); + if (ret == -1) { + debug("mcast_sock_send"); + m->stats.error++; + return ret; + } + + m->stats.bytes += ret; + m->stats.messages++; + + return ret; +} + +int mcast_recv(struct mcast_sock *m, void *data, int size) +{ + int ret; + socklen_t sin_size = sizeof(struct sockaddr_in); + + ret = recvfrom(m->fd, + data, + size, + 0, + (struct sockaddr *)&m->addr, + &sin_size); + if (ret == -1) { + debug("mcast_sock_recv"); + m->stats.error++; + return ret; + } + + m->stats.bytes += ret; + m->stats.messages++; + + return ret; +} + +struct mcast_stats *mcast_get_stats(struct mcast_sock *m) +{ + return &m->stats; +} + +void mcast_dump_stats(int fd, struct mcast_sock *s, struct mcast_sock *r) +{ + char buf[512]; + int size; + + size = sprintf(buf, "multicast traffic:\n" + "%20llu Bytes sent " + "%20llu Bytes recv\n" + "%20llu Pckts sent " + "%20llu Pckts recv\n" + "%20llu Error send " + "%20llu Error recv\n\n", + s->stats.bytes, r->stats.bytes, + s->stats.messages, r->stats.messages, + s->stats.error, r->stats.error); + + send(fd, buf, size, 0); +} diff --git a/daemon/src/netlink.c b/daemon/src/netlink.c new file mode 100644 index 0000000..0bde632 --- /dev/null +++ b/daemon/src/netlink.c @@ -0,0 +1,326 @@ +/* + * (C) 2006 by Pablo Neira Ayuso + * + * 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 "conntrackd.h" +#include +#include +#include +#include "us-conntrack.h" +#include +#include +#include "network.h" + +static int ignore_conntrack(struct nf_conntrack *ct) +{ + /* ignore a certain protocol */ + if (CONFIG(ignore_protocol)[nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO)]) + return 1; + + /* Accept DNAT'ed traffic: not really coming to the local machine */ + if ((CONFIG(flags) & STRIP_NAT) && + nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT)) { + debug_ct(ct, "DNAT"); + return 0; + } + + /* Accept SNAT'ed traffic: not really coming to the local machine */ + if ((CONFIG(flags) & STRIP_NAT) && + nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT)) { + debug_ct(ct, "SNAT"); + return 0; + } + + /* Ignore traffic */ + if (ignore_pool_test(STATE(ignore_pool), ct)) { + debug_ct(ct, "ignore traffic"); + return 1; + } + + return 0; +} + +static int nl_event_handler(struct nlmsghdr *nlh, + struct nfattr *nfa[], + void *data) +{ + char tmp[1024]; + struct nf_conntrack *ct = (struct nf_conntrack *) tmp; + int type; + + memset(tmp, 0, sizeof(tmp)); + + if ((type = nfct_parse_conntrack(NFCT_T_ALL, nlh, ct)) == NFCT_T_ERROR) + return NFCT_CB_STOP; + + /* + * Ignore this conntrack: it talks about a + * connection that is not interesting for us. + */ + if (ignore_conntrack(ct)) + return NFCT_CB_STOP; + + switch(type) { + case NFCT_T_NEW: + STATE(mode)->event_new(ct, nlh); + break; + case NFCT_T_UPDATE: + STATE(mode)->event_upd(ct, nlh); + break; + case NFCT_T_DESTROY: + if (STATE(mode)->event_dst(ct, nlh)) + update_traffic_stats(ct); + break; + default: + dlog(STATE(log), "received unknown msg from ctnetlink\n"); + break; + } + + return NFCT_CB_STOP; +} + +int nl_init_event_handler(void) +{ + struct nfnl_callback cb_events = { + .call = nl_event_handler, + .attr_count = CTA_MAX + }; + + /* open event netlink socket */ + STATE(event) = nfnl_open(); + if (!STATE(event)) + return -1; + + /* set up socket buffer size */ + if (CONFIG(netlink_buffer_size)) + nfnl_rcvbufsiz(STATE(event), CONFIG(netlink_buffer_size)); + else { + socklen_t socklen = sizeof(unsigned int); + unsigned int read_size; + + /* get current buffer size */ + getsockopt(nfnl_fd(STATE(event)), SOL_SOCKET, + SO_RCVBUF, &read_size, &socklen); + + CONFIG(netlink_buffer_size) = read_size; + } + + /* ensure that maximum grown size is >= than maximum size */ + if (CONFIG(netlink_buffer_size_max_grown) < CONFIG(netlink_buffer_size)) + CONFIG(netlink_buffer_size_max_grown) = + CONFIG(netlink_buffer_size); + + /* open event subsystem */ + STATE(subsys_event) = nfnl_subsys_open(STATE(event), + NFNL_SUBSYS_CTNETLINK, + IPCTNL_MSG_MAX, + NFCT_ALL_CT_GROUPS); + if (STATE(subsys_event) == NULL) + return -1; + + /* register callback for new and update events */ + nfnl_callback_register(STATE(subsys_event), + IPCTNL_MSG_CT_NEW, + &cb_events); + + /* register callback for delete events */ + nfnl_callback_register(STATE(subsys_event), + IPCTNL_MSG_CT_DELETE, + &cb_events); + + return 0; +} + +static int nl_dump_handler(struct nlmsghdr *nlh, + struct nfattr *nfa[], + void *data) +{ + char buf[1024]; + struct nf_conntrack *ct = (struct nf_conntrack *) buf; + int type; + + memset(buf, 0, sizeof(buf)); + + if ((type = nfct_parse_conntrack(NFCT_T_ALL, nlh, ct)) == NFCT_T_ERROR) + return NFCT_CB_CONTINUE; + + /* + * Ignore this conntrack: it talks about a + * connection that is not interesting for us. + */ + if (ignore_conntrack(ct)) + return NFCT_CB_CONTINUE; + + switch(type) { + case NFCT_T_UPDATE: + STATE(mode)->dump(ct, nlh); + break; + default: + dlog(STATE(log), "received unknown msg from ctnetlink"); + break; + } + return NFCT_CB_CONTINUE; +} + +int nl_init_dump_handler(void) +{ + struct nfnl_callback cb_dump = { + .call = nl_dump_handler, + .attr_count = CTA_MAX + }; + + /* open dump netlink socket */ + STATE(dump) = nfnl_open(); + if (!STATE(dump)) + return -1; + + /* open dump subsystem */ + STATE(subsys_dump) = nfnl_subsys_open(STATE(dump), + NFNL_SUBSYS_CTNETLINK, + IPCTNL_MSG_MAX, + 0); + if (STATE(subsys_dump) == NULL) + return -1; + + /* register callback for dumped entries */ + nfnl_callback_register(STATE(subsys_dump), + IPCTNL_MSG_CT_NEW, + &cb_dump); + + if (nl_dump_conntrack_table(STATE(dump), STATE(subsys_dump)) == -1) + return -1; + + return 0; +} + +static int nl_overrun_handler(struct nlmsghdr *nlh, + struct nfattr *nfa[], + void *data) +{ + char buf[1024]; + struct nf_conntrack *ct = (struct nf_conntrack *) buf; + int type; + + memset(buf, 0, sizeof(buf)); + + if ((type = nfct_parse_conntrack(NFCT_T_ALL, nlh, ct)) == NFCT_T_ERROR) + return NFCT_CB_CONTINUE; + + /* + * Ignore this conntrack: it talks about a + * connection that is not interesting for us. + */ + if (ignore_conntrack(ct)) + return NFCT_CB_CONTINUE; + + switch(type) { + case NFCT_T_UPDATE: + if (STATE(mode)->overrun) + STATE(mode)->overrun(ct, nlh); + break; + default: + dlog(STATE(log), "received unknown msg from ctnetlink"); + break; + } + return NFCT_CB_CONTINUE; +} + +int nl_init_overrun_handler(void) +{ + struct nfnl_callback cb_sync = { + .call = nl_overrun_handler, + .attr_count = CTA_MAX + }; + + /* open sync netlink socket */ + STATE(sync) = nfnl_open(); + if (!STATE(sync)) + return -1; + + /* open synchronizer subsystem */ + STATE(subsys_sync) = nfnl_subsys_open(STATE(sync), + NFNL_SUBSYS_CTNETLINK, + IPCTNL_MSG_MAX, + 0); + if (STATE(subsys_sync) == NULL) + return -1; + + /* register callback for dumped entries */ + nfnl_callback_register(STATE(subsys_sync), + IPCTNL_MSG_CT_NEW, + &cb_sync); + + return 0; +} + +static int warned = 0; + +void nl_resize_socket_buffer(struct nfnl_handle *h) +{ + unsigned int s = CONFIG(netlink_buffer_size) * 2; + + /* already warned that we have reached the maximum buffer size */ + if (warned) + return; + + if (s > CONFIG(netlink_buffer_size_max_grown)) { + dlog(STATE(log), "maximum netlink socket buffer size reached"); + s = CONFIG(netlink_buffer_size_max_grown); + warned = 1; + } + + CONFIG(netlink_buffer_size) = nfnl_rcvbufsiz(h, s); + + /* notify the sysadmin */ + dlog(STATE(log), "netlink socket buffer size has been set to %u bytes", + CONFIG(netlink_buffer_size)); +} + +int nl_dump_conntrack_table(struct nfnl_handle *h, + struct nfnl_subsys_handle *subsys) +{ + struct nfnlhdr req; + + memset(&req, 0, sizeof(req)); + nfct_build_query(subsys, + NFCT_Q_DUMP, + &CONFIG(family), + &req, + sizeof(req)); + + if (nfnl_query(h, &req.nlh) == -1) + return -1; + + return 0; +} + +int nl_flush_master_conntrack_table(void) +{ + struct nfnlhdr req; + + memset(&req, 0, sizeof(req)); + nfct_build_query(STATE(subsys_sync), + NFCT_Q_FLUSH, + &CONFIG(family), + &req, + sizeof(req)); + + if (nfnl_query(STATE(sync), &req.nlh) == -1) + return -1; + + return 0; +} diff --git a/daemon/src/network.c b/daemon/src/network.c new file mode 100644 index 0000000..b9be318 --- /dev/null +++ b/daemon/src/network.c @@ -0,0 +1,282 @@ +/* + * (C) 2006-2007 by Pablo Neira Ayuso + * + * 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 "conntrackd.h" +#include "network.h" + +#if 0 +#define _TEST_DROP +#else +#undef _TEST_DROP +#endif + +static int drop = 0; /* debugging purposes */ +static unsigned int seq_set, cur_seq; + +static int send_netmsg(struct mcast_sock *m, void *data, unsigned int len) +{ + struct nlnetwork *net = data; + +#ifdef _TEST_DROP + if (++drop > 10) { + drop = 0; + printf("dropping resend (seq=%u)\n", ntohl(net->seq)); + return 0; + } +#endif + return mcast_send(m, net, len); +} + +int mcast_send_netmsg(struct mcast_sock *m, void *data) +{ + struct nlmsghdr *nlh = data + sizeof(struct nlnetwork); + unsigned int len = nlh->nlmsg_len + sizeof(struct nlnetwork); + struct nlnetwork *net = data; + + if (!seq_set) { + seq_set = 1; + cur_seq = time(NULL); + net->flags |= NET_HELLO; + } + + net->flags = htons(net->flags); + net->seq = htonl(cur_seq++); + + if (nlh_host2network(nlh) == -1) + return -1; + + net->checksum = 0; + net->checksum = ntohs(do_csum(data, len)); + + return send_netmsg(m, data, len); +} + +int mcast_resend_netmsg(struct mcast_sock *m, void *data) +{ + struct nlnetwork *net = data; + struct nlmsghdr *nlh = data + sizeof(struct nlnetwork); + unsigned int len = htonl(nlh->nlmsg_len) + sizeof(struct nlnetwork); + + net->flags = ntohs(net->flags); + + if (!seq_set) { + seq_set = 1; + cur_seq = time(NULL); + net->flags |= NET_HELLO; + } + + if (net->flags & NET_NACK || net->flags & NET_ACK) { + struct nlnetwork_ack *nack = (struct nlnetwork_ack *) net; + len = sizeof(struct nlnetwork_ack); + } + + net->flags = htons(net->flags); + net->seq = htonl(cur_seq++); + net->checksum = 0; + net->checksum = ntohs(do_csum(data, len)); + + return send_netmsg(m, data, len); +} + +int mcast_send_error(struct mcast_sock *m, void *data) +{ + struct nlnetwork *net = data; + unsigned int len = sizeof(struct nlnetwork); + + if (!seq_set) { + seq_set = 1; + cur_seq = time(NULL); + net->flags |= NET_HELLO; + } + + if (net->flags & NET_NACK || net->flags & NET_ACK) { + struct nlnetwork_ack *nack = (struct nlnetwork_ack *) net; + nack->from = htonl(nack->from); + nack->to = htonl(nack->to); + len = sizeof(struct nlnetwork_ack); + } + + net->flags = htons(net->flags); + net->seq = htonl(cur_seq++); + net->checksum = 0; + net->checksum = ntohs(do_csum(data, len)); + + return send_netmsg(m, data, len); +} + +static int valid_checksum(void *data, unsigned int len) +{ + struct nlnetwork *net = data; + unsigned short checksum, tmp; + + checksum = ntohs(net->checksum); + + /* no checksum, skip */ + if (!checksum) + return 1; + + net->checksum = 0; + tmp = do_csum(data, len); + + return tmp == checksum; +} + +int mcast_recv_netmsg(struct mcast_sock *m, void *data, int len) +{ + int ret; + struct nlnetwork *net = data; + struct nlmsghdr *nlh = data + sizeof(struct nlnetwork); + struct nfgenmsg *nfhdr; + + ret = mcast_recv(m, net, len); + if (ret <= 0) + return ret; + + if (ret < sizeof(struct nlnetwork)) + return -1; + + if (!valid_checksum(data, ret)) + return -1; + + net->flags = ntohs(net->flags); + net->seq = ntohl(net->seq); + + if (net->flags & NET_HELLO) + STATE_SYNC(last_seq_recv) = net->seq-1; + + if (net->flags & NET_NACK || net->flags & NET_ACK) { + struct nlnetwork_ack *nack = (struct nlnetwork_ack *) net; + + if (ret < sizeof(struct nlnetwork_ack)) + return -1; + + nack->from = ntohl(nack->from); + nack->to = ntohl(nack->to); + + return ret; + } + + if (net->flags & NET_RESYNC) + return ret; + + /* information received is too small */ + if (ret < NLMSG_SPACE(sizeof(struct nfgenmsg))) + return -1; + + /* information received and message length does not match */ + if (ret != ntohl(nlh->nlmsg_len) + sizeof(struct nlnetwork)) + return -1; + + /* this message does not come from ctnetlink */ + if (NFNL_SUBSYS_ID(ntohs(nlh->nlmsg_type)) != NFNL_SUBSYS_CTNETLINK) + return -1; + + nfhdr = NLMSG_DATA(nlh); + + /* only AF_INET and AF_INET6 are supported */ + if (nfhdr->nfgen_family != AF_INET && + nfhdr->nfgen_family != AF_INET6) + return -1; + + /* only process message coming from nfnetlink v0 */ + if (nfhdr->version != NFNETLINK_V0) + return -1; + + if (nlh_network2host(nlh) == -1) + return -1; + + return ret; +} + +int mcast_track_seq(u_int32_t seq, u_int32_t *exp_seq) +{ + static int seq_set = 0; + int ret = 1; + + /* netlink sequence tracking initialization */ + if (!seq_set) { + seq_set = 1; + goto out; + } + + /* fast path: we received the correct sequence */ + if (seq == STATE_SYNC(last_seq_recv)+1) + goto out; + + /* out of sequence: some messages got lost */ + if (seq > STATE_SYNC(last_seq_recv)+1) { + STATE_SYNC(packets_lost) += seq-STATE_SYNC(last_seq_recv)+1; + ret = 0; + goto out; + } + + /* out of sequence: replayed or sequence wrapped around issues */ + if (seq < STATE_SYNC(last_seq_recv)+1) { + /* + * Check if the sequence has wrapped around. + * Perhaps it can be a replayed packet. + */ + if (STATE_SYNC(last_seq_recv)+1-seq > ~0U/2) { + /* + * Indeed, it is a wrapped around + */ + STATE_SYNC(packets_lost) += + ~0U-STATE_SYNC(last_seq_recv)+1+seq; + } else { + /* + * It is a delayed packet + */ + dlog(STATE(log), "delayed packet? exp=%u rcv=%u", + STATE_SYNC(last_seq_recv)+1, seq); + } + ret = 0; + } + +out: + *exp_seq = STATE_SYNC(last_seq_recv)+1; + /* update expected sequence */ + STATE_SYNC(last_seq_recv) = seq; + + return ret; +} + +int build_network_msg(const int msg_type, + struct nfnl_subsys_handle *ssh, + struct nf_conntrack *ct, + void *buffer, + unsigned int size) +{ + memset(buffer, 0, size); + buffer += sizeof(struct nlnetwork); + return nfct_build_query(ssh, msg_type, ct, buffer, size); +} + +unsigned int parse_network_msg(struct nf_conntrack *ct, + const struct nlmsghdr *nlh) +{ + /* + * The parsing of netlink messages going through network is + * similar to the one that is done for messages coming from + * kernel, therefore do not replicate more code and use the + * function provided in the libraries. + * + * Yup, this is a hack 8) + */ + return nfct_parse_conntrack(NFCT_T_ALL, nlh, ct); +} + diff --git a/daemon/src/proxy.c b/daemon/src/proxy.c new file mode 100644 index 0000000..b9bb04e --- /dev/null +++ b/daemon/src/proxy.c @@ -0,0 +1,124 @@ +/* + * (C) 2006 by Pablo Neira Ayuso + * + * 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 +#include + +#if 0 +#define dprintf printf +#else +#define dprintf +#endif + +int nlh_payload_host2network(struct nfattr *nfa, int len) +{ + struct nfattr *__nfa; + + while (NFA_OK(nfa, len)) { + + dprintf("type=%d nfalen=%d len=%d [%s]\n", + nfa->nfa_type & 0x7fff, + nfa->nfa_len, len, + nfa->nfa_type & NFNL_NFA_NEST ? "NEST":""); + + if (nfa->nfa_type & NFNL_NFA_NEST) { + if (NFA_PAYLOAD(nfa) > len) + return -1; + + if (nlh_payload_host2network(NFA_DATA(nfa), + NFA_PAYLOAD(nfa)) == -1) + return -1; + } + + __nfa = NFA_NEXT(nfa, len); + + nfa->nfa_type = htons(nfa->nfa_type); + nfa->nfa_len = htons(nfa->nfa_len); + + nfa = __nfa; + } + return 0; +} + +int nlh_host2network(struct nlmsghdr *nlh) +{ + struct nfgenmsg *nfhdr = NLMSG_DATA(nlh); + struct nfattr *cda[CTA_MAX]; + unsigned int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg)); + unsigned int len = nlh->nlmsg_len - NLMSG_ALIGN(min_len); + + nlh->nlmsg_len = htonl(nlh->nlmsg_len); + nlh->nlmsg_type = htons(nlh->nlmsg_type); + nlh->nlmsg_flags = htons(nlh->nlmsg_flags); + nlh->nlmsg_seq = htonl(nlh->nlmsg_seq); + nlh->nlmsg_pid = htonl(nlh->nlmsg_pid); + + nfhdr->res_id = htons(nfhdr->res_id); + + return nlh_payload_host2network(NFM_NFA(NLMSG_DATA(nlh)), len); +} + +int nlh_payload_network2host(struct nfattr *nfa, int len) +{ + nfa->nfa_type = ntohs(nfa->nfa_type); + nfa->nfa_len = ntohs(nfa->nfa_len); + + while(NFA_OK(nfa, len)) { + + dprintf("type=%d nfalen=%d len=%d [%s]\n", + nfa->nfa_type & 0x7fff, + nfa->nfa_len, len, + nfa->nfa_type & NFNL_NFA_NEST ? "NEST":""); + + if (nfa->nfa_type & NFNL_NFA_NEST) { + if (NFA_PAYLOAD(nfa) > len) + return -1; + + if (nlh_payload_network2host(NFA_DATA(nfa), + NFA_PAYLOAD(nfa)) == -1) + return -1; + } + + nfa = NFA_NEXT(nfa,len); + + if (len < NFA_LENGTH(0)) + break; + + nfa->nfa_type = ntohs(nfa->nfa_type); + nfa->nfa_len = ntohs(nfa->nfa_len); + } + return 0; +} + +int nlh_network2host(struct nlmsghdr *nlh) +{ + struct nfgenmsg *nfhdr = NLMSG_DATA(nlh); + struct nfattr *cda[CTA_MAX]; + unsigned int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg)); + unsigned int len = ntohl(nlh->nlmsg_len) - NLMSG_ALIGN(min_len); + + nlh->nlmsg_len = ntohl(nlh->nlmsg_len); + nlh->nlmsg_type = ntohs(nlh->nlmsg_type); + nlh->nlmsg_flags = ntohs(nlh->nlmsg_flags); + nlh->nlmsg_seq = ntohl(nlh->nlmsg_seq); + nlh->nlmsg_pid = ntohl(nlh->nlmsg_pid); + + nfhdr->res_id = ntohs(nfhdr->res_id); + + return nlh_payload_network2host(NFM_NFA(NLMSG_DATA(nlh)), len); +} diff --git a/daemon/src/read_config_lex.l b/daemon/src/read_config_lex.l new file mode 100644 index 0000000..dee90c9 --- /dev/null +++ b/daemon/src/read_config_lex.l @@ -0,0 +1,125 @@ +%{ +/* + * (C) 2006 by Pablo Neira Ayuso + * + * 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. + * + * Description: configuration file syntax + */ + +#include "read_config_yy.h" +#include "conntrackd.h" +%} + +%option yylineno +%option nounput + +ws [ \t]+ +comment #.*$ +nl [\n\r] + +is_on [o|O][n|N] +is_off [o|O][f|F][f|F] +integer [0-9]+ +path \/[^\"\n ]* +ip4_end [0-9]*[0-9]+ +ip4_part [0-2]*{ip4_end} +ip4 {ip4_part}\.{ip4_part}\.{ip4_part}\.{ip4_part} +hex_255 [0-9a-fA-F]{1,4} +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_form2} +string [a-zA-Z]* +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] + +%% +"UNIX" { return T_UNIX; } +"IPv4_address" { return T_IPV4_ADDR; } +"IPv6_address" { return T_IPV6_ADDR; } +"IPv4_interface" { return T_IPV4_IFACE; } +"IPv6_interface" { return T_IPV6_IFACE; } +"Port" { return T_PORT; } +"Multicast" { return T_MULTICAST; } +"HashSize" { return T_HASHSIZE; } +"RefreshTime" { return T_REFRESH; } +"CacheTimeout" { return T_EXPIRE; } +"CommitTimeout" { return T_TIMEOUT; } +"DelayDestroyMessages" { return T_DELAY; } +"HashLimit" { return T_HASHLIMIT; } +"Path" { return T_PATH; } +"IgnoreProtocol" { return T_IGNORE_PROTOCOL; } +"UDP" { return T_UDP; } +"ICMP" { return T_ICMP; } +"VRRP" { return T_VRRP; } +"IGMP" { return T_IGMP; } +"TCP" { return T_TCP; } +"IgnoreTrafficFor" { return T_IGNORE_TRAFFIC; } +"StripNAT" { return T_STRIP_NAT; } +"Backlog" { return T_BACKLOG; } +"Group" { return T_GROUP; } +"LogFile" { return T_LOG; } +"LockFile" { return T_LOCK; } +"General" { return T_GENERAL; } +"Sync" { return T_SYNC; } +"Stats" { return T_STATS; } +"RelaxTransitions" { return T_RELAX_TRANSITIONS; } +"SocketBufferSize" { return T_BUFFER_SIZE; } +"SocketBufferSizeMaxGrown" { return T_BUFFER_SIZE_MAX_GROWN; } +"SocketBufferSizeMaxGrowth" { return T_BUFFER_SIZE_MAX_GROWN; } +"Mode" { return T_SYNC_MODE; } +"ListenTo" { return T_LISTEN_TO; } +"Family" { return T_FAMILY; } +"ResendBufferSize" { return T_RESEND_BUFFER_SIZE; } +"Checksum" { return T_CHECKSUM; } +"ACKWindowSize" { return T_WINDOWSIZE; } +"Replicate" { return T_REPLICATE; } +"for" { return T_FOR; } +"SYN_SENT" { return T_SYN_SENT; } +"SYN_RECV" { return T_SYN_RECV; } +"ESTABLISHED" { return T_ESTABLISHED; } +"FIN_WAIT" { return T_FIN_WAIT; } +"CLOSE_WAIT" { return T_CLOSE_WAIT; } +"LAST_ACK" { return T_LAST_ACK; } +"TIME_WAIT" { return T_TIME_WAIT; } +"CLOSE" { return T_CLOSE; } +"LISTEN" { return T_LISTEN; } + +{is_on} { return T_ON; } +{is_off} { return T_OFF; } +{integer} { yylval.val = atoi(yytext); return T_NUMBER; } +{ip4} { yylval.string = strdup(yytext); return T_IP; } +{ip6} { yylval.string = strdup(yytext); return T_IP; } +{path} { yylval.string = strdup(yytext); return T_PATH_VAL; } +{persistent} { return T_PERSISTENT; } +{nack} { return T_NACK; } +{string} { yylval.string = strdup(yytext); return T_STRING; } + +{comment} ; +{ws} ; +{nl} ; + +<> { yyterminate(); } + +. { return yytext[0]; } + +%% + +int +yywrap() +{ + return 1; +} diff --git a/daemon/src/read_config_yy.y b/daemon/src/read_config_yy.y new file mode 100644 index 0000000..1668919 --- /dev/null +++ b/daemon/src/read_config_yy.y @@ -0,0 +1,550 @@ +%{ +/* + * (C) 2006 by Pablo Neira Ayuso + * + * 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. + * + * Description: configuration file abstract grammar + */ + +#include +#include +#include +#include +#include "conntrackd.h" +#include "ignore.h" + +extern char *yytext; +extern int yylineno; + +struct ct_conf conf; +%} + +%union { + int val; + char *string; +} + +%token T_IPV4_ADDR T_IPV4_IFACE T_PORT T_HASHSIZE T_HASHLIMIT T_MULTICAST +%token T_PATH T_UNIX T_REFRESH T_IPV6_ADDR T_IPV6_IFACE +%token T_IGNORE_UDP T_IGNORE_ICMP T_IGNORE_TRAFFIC T_BACKLOG T_GROUP +%token T_LOG T_UDP T_ICMP T_IGMP T_VRRP T_TCP T_IGNORE_PROTOCOL +%token T_LOCK T_STRIP_NAT T_BUFFER_SIZE_MAX_GROWN T_EXPIRE T_TIMEOUT +%token T_GENERAL T_SYNC T_STATS T_RELAX_TRANSITIONS T_BUFFER_SIZE T_DELAY +%token T_SYNC_MODE T_LISTEN_TO T_FAMILY T_RESEND_BUFFER_SIZE +%token T_PERSISTENT T_NACK T_CHECKSUM T_WINDOWSIZE T_ON T_OFF +%token T_REPLICATE T_FOR +%token T_ESTABLISHED T_SYN_SENT T_SYN_RECV T_FIN_WAIT +%token T_CLOSE_WAIT T_LAST_ACK T_TIME_WAIT T_CLOSE T_LISTEN + + +%token T_IP T_PATH_VAL +%token T_NUMBER +%token T_STRING + +%% + +configfile : + | lines + ; + +lines : line + | lines line + ; + +line : ignore_protocol + | ignore_traffic + | strip_nat + | general + | sync + | stats + ; + +log : T_LOG T_PATH_VAL +{ + strncpy(conf.logfile, $2, FILENAME_MAXLEN); +}; + +lock : T_LOCK T_PATH_VAL +{ + strncpy(conf.lockfile, $2, FILENAME_MAXLEN); +}; + +strip_nat: T_STRIP_NAT +{ + conf.flags |= STRIP_NAT; +}; + +refreshtime : T_REFRESH T_NUMBER +{ + conf.refresh = $2; +}; + +expiretime: T_EXPIRE T_NUMBER +{ + conf.cache_timeout = $2; +}; + +timeout: T_TIMEOUT T_NUMBER +{ + conf.commit_timeout = $2; +}; + +checksum: T_CHECKSUM T_ON +{ +}; + +checksum: T_CHECKSUM T_OFF +{ + conf.flags |= DONT_CHECKSUM; +}; + +ignore_traffic : T_IGNORE_TRAFFIC '{' ignore_traffic_options '}'; + +ignore_traffic_options : + | ignore_traffic_options ignore_traffic_option; + +ignore_traffic_option : T_IPV4_ADDR T_IP +{ + union inet_address ip; + int family = 0; + + memset(&ip, 0, sizeof(union inet_address)); + + if (inet_aton($2, &ip.ipv4)) + family = AF_INET; +#ifdef HAVE_INET_PTON_IPV6 + else if (inet_pton(AF_INET6, $2, &ip.ipv6) > 0) + family = AF_INET6; +#endif + + if (!family) { + fprintf(stdout, "%s is not a valid IP, ignoring", $2); + return; + } + + if (!STATE(ignore_pool)) { + STATE(ignore_pool) = ignore_pool_create(family); + if (!STATE(ignore_pool)) { + fprintf(stdout, "Can't create ignore pool!\n"); + exit(EXIT_FAILURE); + } + } + + if (!ignore_pool_add(STATE(ignore_pool), &ip)) { + if (errno == EEXIST) + fprintf(stdout, "IP %s is repeated " + "in the ignore pool\n", $2); + if (errno == ENOSPC) + fprintf(stdout, "Too many IP in the ignore pool!\n"); + } +}; + +multicast_line : T_MULTICAST '{' multicast_options '}'; + +multicast_options : + | multicast_options multicast_option; + +multicast_option : T_IPV4_ADDR T_IP +{ + if (!inet_aton($2, &conf.mcast.in)) { + fprintf(stderr, "%s is not a valid IPv4 address\n"); + return; + } + + if (conf.mcast.ipproto == AF_INET6) { + fprintf(stderr, "Your multicast address is IPv4 but " + "is binded to an IPv6 interface? Surely " + "this is not what you want\n"); + return; + } + + conf.mcast.ipproto = AF_INET; +}; + +multicast_option : T_IPV6_ADDR T_IP +{ +#ifdef HAVE_INET_PTON_IPV6 + if (inet_pton(AF_INET6, $2, &conf.mcast.in) <= 0) + fprintf(stderr, "%s is not a valid IPv6 address\n", $2); +#endif + + if (conf.mcast.ipproto == AF_INET) { + fprintf(stderr, "Your multicast address is IPv6 but " + "is binded to an IPv4 interface? Surely " + "this is not what you want\n"); + return; + } + + conf.mcast.ipproto = AF_INET6; +}; + +multicast_option : T_IPV4_IFACE T_IP +{ + if (!inet_aton($2, &conf.mcast.ifa)) { + fprintf(stderr, "%s is not a valid IPv4 address\n"); + return; + } + + if (conf.mcast.ipproto == AF_INET6) { + fprintf(stderr, "Your multicast interface is IPv4 but " + "is binded to an IPv6 interface? Surely " + "this is not what you want\n"); + return; + } + + conf.mcast.ipproto = AF_INET; +}; + +multicast_option : T_IPV6_IFACE T_IP +{ +#ifdef HAVE_INET_PTON_IPV6 + if (inet_pton(AF_INET6, $2, &conf.mcast.ifa) <= 0) + fprintf(stderr, "%s is not a valid IPv6 address\n", $2); +#endif + + if (conf.mcast.ipproto == AF_INET) { + fprintf(stderr, "Your multicast interface is IPv6 but " + "is binded to an IPv4 interface? Surely " + "this is not what you want\n"); + return; + } + + conf.mcast.ipproto = AF_INET6; +}; + +multicast_option : T_BACKLOG T_NUMBER +{ + conf.mcast.backlog = $2; +}; + +multicast_option : T_GROUP T_NUMBER +{ + conf.mcast.port = $2; +}; + +hashsize : T_HASHSIZE T_NUMBER +{ + conf.hashsize = $2; +}; + +hashlimit: T_HASHLIMIT T_NUMBER +{ + conf.limit = $2; +}; + +unix_line: T_UNIX '{' unix_options '}'; + +unix_options: + | unix_options unix_option + ; + +unix_option : T_PATH T_PATH_VAL +{ + strcpy(conf.local.path, $2); +}; + +unix_option : T_BACKLOG T_NUMBER +{ + conf.local.backlog = $2; +}; + +ignore_protocol: T_IGNORE_PROTOCOL '{' ignore_proto_list '}'; + +ignore_proto_list: + | ignore_proto_list ignore_proto + ; + +ignore_proto: T_NUMBER +{ + if ($1 < IPPROTO_MAX) + conf.ignore_protocol[$1] = 1; + else + fprintf(stdout, "Protocol number `%d' is freak\n", $1); +}; + +ignore_proto: T_UDP +{ + conf.ignore_protocol[IPPROTO_UDP] = 1; +}; + +ignore_proto: T_ICMP +{ + conf.ignore_protocol[IPPROTO_ICMP] = 1; +}; + +ignore_proto: T_VRRP +{ + conf.ignore_protocol[IPPROTO_VRRP] = 1; +}; + +ignore_proto: T_IGMP +{ + conf.ignore_protocol[IPPROTO_IGMP] = 1; +}; + +sync: T_SYNC '{' sync_list '}'; + +sync_list: + | sync_list sync_line; + +sync_line: refreshtime + | expiretime + | timeout + | checksum + | multicast_line + | relax_transitions + | delay_destroy_msgs + | sync_mode_persistent + | sync_mode_nack + | listen_to + | state_replication + ; + +sync_mode_persistent: T_SYNC_MODE T_PERSISTENT '{' sync_mode_persistent_list '}' +{ + conf.flags |= SYNC_MODE_PERSISTENT; +}; + +sync_mode_nack: T_SYNC_MODE T_NACK '{' sync_mode_nack_list '}' +{ + conf.flags |= SYNC_MODE_NACK; +}; + +sync_mode_persistent_list: + | sync_mode_persistent_list sync_mode_persistent_line; + +sync_mode_persistent_line: refreshtime + | expiretime + | timeout + | relax_transitions + | delay_destroy_msgs + ; + +sync_mode_nack_list: + | sync_mode_nack_list sync_mode_nack_line; + +sync_mode_nack_line: resend_buffer_size + | timeout + | window_size + ; + +resend_buffer_size: T_RESEND_BUFFER_SIZE T_NUMBER +{ + conf.resend_buffer_size = $2; +}; + +window_size: T_WINDOWSIZE T_NUMBER +{ + conf.window_size = $2; +}; + +relax_transitions: T_RELAX_TRANSITIONS +{ + conf.flags |= RELAX_TRANSITIONS; +}; + +delay_destroy_msgs: T_DELAY +{ + conf.flags |= DELAY_DESTROY_MSG; +}; + +listen_to: T_LISTEN_TO T_IP +{ + union inet_address addr; + +#ifdef HAVE_INET_PTON_IPV6 + if (inet_pton(AF_INET6, $2, &addr.ipv6) <= 0) +#endif + if (inet_aton($2, &addr.ipv4) <= 0) { + fprintf(stderr, "%s is not a valid IP address\n", $2); + exit(EXIT_FAILURE); + } + + if (CONFIG(listen_to_len) == 0 || CONFIG(listen_to_len) % 16) { + CONFIG(listen_to) = realloc(CONFIG(listen_to), + sizeof(union inet_address) * + (CONFIG(listen_to_len) + 16)); + if (CONFIG(listen_to) == NULL) { + fprintf(stderr, "cannot init listen_to array\n"); + exit(EXIT_FAILURE); + } + + memset(CONFIG(listen_to) + + (CONFIG(listen_to_len) * sizeof(union inet_address)), + 0, sizeof(union inet_address) * 16); + + } +}; + +state_replication: T_REPLICATE states T_FOR state_proto; + +states: + | states state; + +state_proto: T_TCP; +state: tcp_state; + +tcp_state: T_SYN_SENT +{ + extern struct state_replication_helper tcp_state_helper; + state_helper_register(&tcp_state_helper, TCP_CONNTRACK_SYN_SENT); +}; +tcp_state: T_SYN_RECV +{ + extern struct state_replication_helper tcp_state_helper; + state_helper_register(&tcp_state_helper, TCP_CONNTRACK_SYN_RECV); +}; +tcp_state: T_ESTABLISHED +{ + extern struct state_replication_helper tcp_state_helper; + state_helper_register(&tcp_state_helper, TCP_CONNTRACK_ESTABLISHED); +}; +tcp_state: T_FIN_WAIT +{ + extern struct state_replication_helper tcp_state_helper; + state_helper_register(&tcp_state_helper, TCP_CONNTRACK_FIN_WAIT); +}; +tcp_state: T_CLOSE_WAIT +{ + extern struct state_replication_helper tcp_state_helper; + state_helper_register(&tcp_state_helper, TCP_CONNTRACK_CLOSE_WAIT); +}; +tcp_state: T_LAST_ACK +{ + extern struct state_replication_helper tcp_state_helper; + state_helper_register(&tcp_state_helper, TCP_CONNTRACK_LAST_ACK); +}; +tcp_state: T_TIME_WAIT +{ + extern struct state_replication_helper tcp_state_helper; + state_helper_register(&tcp_state_helper, TCP_CONNTRACK_TIME_WAIT); +}; +tcp_state: T_CLOSE +{ + extern struct state_replication_helper tcp_state_helper; + state_helper_register(&tcp_state_helper, TCP_CONNTRACK_CLOSE); +}; +tcp_state: T_LISTEN +{ + extern struct state_replication_helper tcp_state_helper; + state_helper_register(&tcp_state_helper, TCP_CONNTRACK_LISTEN); +}; + +general: T_GENERAL '{' general_list '}'; + +general_list: + | general_list general_line + ; + +general_line: hashsize + | hashlimit + | log + | lock + | unix_line + | netlink_buffer_size + | netlink_buffer_size_max_grown + | family + ; + +netlink_buffer_size: T_BUFFER_SIZE T_NUMBER +{ + conf.netlink_buffer_size = $2; +}; + +netlink_buffer_size_max_grown : T_BUFFER_SIZE_MAX_GROWN T_NUMBER +{ + conf.netlink_buffer_size_max_grown = $2; +}; + +family : T_FAMILY T_STRING +{ + if (strncmp($2, "IPv6", strlen("IPv6")) == 0) + conf.family = AF_INET6; + else + conf.family = AF_INET; +}; + +stats: T_SYNC '{' stats_list '}'; + +stats_list: + | stats_list stat_line + ; + +stat_line: + | + ; + +%% + +int +yyerror(char *msg) +{ + printf("Error parsing config file: "); + printf("line (%d), symbol '%s': %s\n", yylineno, yytext, msg); + exit(EXIT_FAILURE); +} + +int +init_config(char *filename) +{ + FILE *fp; + + fp = fopen(filename, "r"); + if (!fp) + return -1; + + yyrestart(fp); + yyparse(); + fclose(fp); + + /* default to IPv4 */ + if (CONFIG(family) == 0) + CONFIG(family) = AF_INET; + + /* set to default is not specified */ + if (strcmp(CONFIG(lockfile), "") == 0) + strncpy(CONFIG(lockfile), DEFAULT_LOCKFILE, FILENAME_MAXLEN); + + /* default to 180 seconds of expiration time: cache entries */ + if (CONFIG(cache_timeout) == 0) + CONFIG(cache_timeout) = 180; + + /* default to 180 seconds: committed entries */ + if (CONFIG(commit_timeout) == 0) + CONFIG(commit_timeout) = 180; + + /* default to 60 seconds of refresh time */ + if (CONFIG(refresh) == 0) + CONFIG(refresh) = 60; + + if (CONFIG(resend_buffer_size) == 0) + CONFIG(resend_buffer_size) = 262144; + + /* create empty pool */ + if (!STATE(ignore_pool)) { + STATE(ignore_pool) = ignore_pool_create(CONFIG(family)); + if (!STATE(ignore_pool)) { + fprintf(stdout, "Can't create ignore pool!\n"); + exit(EXIT_FAILURE); + } + } + + /* default to a window size of 20 packets */ + if (CONFIG(window_size) == 0) + CONFIG(window_size) = 20; + + return 0; +} diff --git a/daemon/src/run.c b/daemon/src/run.c new file mode 100644 index 0000000..67437d8 --- /dev/null +++ b/daemon/src/run.c @@ -0,0 +1,227 @@ +/* + * (C) 2006-2007 by Pablo Neira Ayuso + * + * 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. + * + * Description: run and init functions + */ + +#include "conntrackd.h" +#include +#include +#include "us-conntrack.h" +#include +#include + +void killer(int foo) +{ + /* no signals while handling signals */ + sigprocmask(SIG_BLOCK, &STATE(block), NULL); + + nfnl_subsys_close(STATE(subsys_event)); + nfnl_subsys_close(STATE(subsys_dump)); + nfnl_subsys_close(STATE(subsys_sync)); + nfnl_close(STATE(event)); + nfnl_close(STATE(dump)); + nfnl_close(STATE(sync)); + + ignore_pool_destroy(STATE(ignore_pool)); + local_server_destroy(STATE(local)); + STATE(mode)->kill(); + unlink(CONFIG(lockfile)); + dlog(STATE(log), "------- shutdown received ----"); + close_log(STATE(log)); + + sigprocmask(SIG_UNBLOCK, &STATE(block), NULL); + + exit(0); +} + +void local_handler(int fd, void *data) +{ + int ret; + int type; + + ret = read(fd, &type, sizeof(type)); + if (ret == -1) { + dlog(STATE(log), "can't read from unix socket\n"); + return; + } + if (ret == 0) { + debug("nothing to process\n"); + return; + } + + switch(type) { + case FLUSH_MASTER: + dlog(STATE(log), "[REQ] flushing master table"); + nl_flush_master_conntrack_table(); + return; + case RESYNC_MASTER: + dlog(STATE(log), "[REQ] resync with master table"); + nl_dump_conntrack_table(STATE(dump), STATE(subsys_dump)); + return; + } + + if (!STATE(mode)->local(fd, type, data)) + dlog(STATE(log), "[FAIL] unknown local request %d", type); +} + +int init(int mode) +{ + switch(mode) { + case STATS_MODE: + STATE(mode) = &stats_mode; + break; + case SYNC_MODE: + STATE(mode) = &sync_mode; + break; + default: + fprintf(stderr, "Unknown running mode! default " + "to synchronization mode\n"); + STATE(mode) = &sync_mode; + break; + } + + /* Initialization */ + if (STATE(mode)->init() == -1) { + dlog(STATE(log), "[FAIL] initialization failed"); + return -1; + } + + /* local UNIX socket */ + STATE(local) = local_server_create(&CONFIG(local)); + if (!STATE(local)) { + dlog(STATE(log), "[FAIL] can't open unix socket!"); + return -1; + } + + if (nl_init_event_handler() == -1) { + dlog(STATE(log), "[FAIL] can't open netlink handler! " + "no ctnetlink kernel support?"); + return -1; + } + + if (nl_init_dump_handler() == -1) { + dlog(STATE(log), "[FAIL] can't open netlink handler! " + "no ctnetlink kernel support?"); + return -1; + } + + if (nl_init_overrun_handler() == -1) { + dlog(STATE(log), "[FAIL] can't open netlink handler! " + "no ctnetlink kernel support?"); + return -1; + } + + /* Signals handling */ + sigemptyset(&STATE(block)); + sigaddset(&STATE(block), SIGTERM); + sigaddset(&STATE(block), SIGINT); + + if (signal(SIGINT, killer) == SIG_ERR) + return -1; + + if (signal(SIGTERM, killer) == SIG_ERR) + return -1; + + /* ignore connection reset by peer */ + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) + return -1; + + dlog(STATE(log), "[OK] initialization completed"); + + return 0; +} + +#define POLL_NSECS 1 + +static void __run(void) +{ + int max, ret; + fd_set readfds; + struct timeval tv = { + .tv_sec = POLL_NSECS, + .tv_usec = 0 + }; + + FD_ZERO(&readfds); + FD_SET(STATE(local), &readfds); + FD_SET(nfnl_fd(STATE(event)), &readfds); + + max = MAX(STATE(local), nfnl_fd(STATE(event))); + + if (STATE(mode)->add_fds_to_set) + max = MAX(max, STATE(mode)->add_fds_to_set(&readfds)); + + ret = select(max+1, &readfds, NULL, NULL, &tv); + if (ret == -1) { + /* interrupted syscall, retry */ + if (errno == EINTR) + return; + + dlog(STATE(log), "select() failed: %s", strerror(errno)); + return; + } + + /* signals are racy */ + sigprocmask(SIG_BLOCK, &STATE(block), NULL); + + /* order received via UNIX socket */ + if (FD_ISSET(STATE(local), &readfds)) + do_local_server_step(STATE(local), NULL, local_handler); + + /* conntrack event has happened */ + if (FD_ISSET(nfnl_fd(STATE(event)), &readfds)) { + ret = nfnl_catch(STATE(event)); + if (ret == -1) { + switch(errno) { + case ENOBUFS: + /* + * It seems that ctnetlink can't back off, + * it's likely that we're losing events. + * Solution: duplicate the socket buffer + * size and resync with master conntrack table. + */ + nl_resize_socket_buffer(STATE(event)); + nl_dump_conntrack_table(STATE(sync), + STATE(subsys_sync)); + break; + case ENOENT: + /* + * We received a message from another + * netfilter subsystem that we are not + * interested in. Just ignore it. + */ + break; + default: + dlog(STATE(log), "event catch says: %s", + strerror(errno)); + break; + } + } + } + + if (STATE(mode)->step) + STATE(mode)->step(&readfds); + + sigprocmask(SIG_UNBLOCK, &STATE(block), NULL); +} + +void run(void) +{ + while(1) + __run(); +} diff --git a/daemon/src/state_helper.c b/daemon/src/state_helper.c new file mode 100644 index 0000000..81b0d09 --- /dev/null +++ b/daemon/src/state_helper.c @@ -0,0 +1,44 @@ +/* + * (C) 2006-2007 by Pablo Neira Ayuso + * + * 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 "conntrackd.h" +#include "state_helper.h" + +static struct state_replication_helper *helper[IPPROTO_MAX]; + +int state_helper_verdict(int type, struct nf_conntrack *ct) +{ + u_int8_t l4proto; + + if (type == NFCT_T_DESTROY) + return ST_H_REPLICATE; + + l4proto = nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO); + if (helper[l4proto]) + return helper[l4proto]->verdict(helper[l4proto], ct); + + return ST_H_REPLICATE; +} + +void state_helper_register(struct state_replication_helper *h, int state) +{ + if (helper[h->proto] == NULL) + helper[h->proto] = h; + + helper[h->proto]->state |= (1 << state); +} diff --git a/daemon/src/state_helper_tcp.c b/daemon/src/state_helper_tcp.c new file mode 100644 index 0000000..af714dc --- /dev/null +++ b/daemon/src/state_helper_tcp.c @@ -0,0 +1,35 @@ +/* + * (C) 2006-2007 by Pablo Neira Ayuso + * + * 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 "conntrackd.h" +#include "state_helper.h" + +static int tcp_verdict(const struct state_replication_helper *h, + const struct nf_conntrack *ct) +{ + u_int8_t state = nfct_get_attr_u8(ct, ATTR_TCP_STATE); + if (h->state & (1 << state)) + return ST_H_REPLICATE; + + return ST_H_SKIP; +} + +struct state_replication_helper tcp_state_helper = { + .proto = IPPROTO_TCP, + .verdict = tcp_verdict, +}; diff --git a/daemon/src/stats-mode.c b/daemon/src/stats-mode.c new file mode 100644 index 0000000..9647bbf --- /dev/null +++ b/daemon/src/stats-mode.c @@ -0,0 +1,151 @@ +/* + * (C) 2006 by Pablo Neira Ayuso + * + * 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 +#include "cache.h" +#include "conntrackd.h" +#include +#include +#include +#include "us-conntrack.h" +#include +#include + +static int init_stats(void) +{ + int ret; + + state.stats = malloc(sizeof(struct ct_stats_state)); + if (!state.stats) { + dlog(STATE(log), "[FAIL] can't allocate memory for stats sync"); + return -1; + } + memset(state.stats, 0, sizeof(struct ct_stats_state)); + + STATE_STATS(cache) = cache_create("stats", + LIFETIME, + CONFIG(family), + NULL); + if (!STATE_STATS(cache)) { + dlog(STATE(log), "[FAIL] can't allocate memory for the " + "external cache"); + return -1; + } + + return 0; +} + +static void kill_stats() +{ + cache_destroy(STATE_STATS(cache)); +} + +/* handler for requests coming via UNIX socket */ +static int local_handler_stats(int fd, int type, void *data) +{ + int ret = 1; + + switch(type) { + case DUMP_INTERNAL: + cache_dump(STATE_STATS(cache), fd, NFCT_O_PLAIN); + break; + case DUMP_INT_XML: + cache_dump(STATE_SYNC(internal), fd, NFCT_O_XML); + break; + case FLUSH_CACHE: + dlog(STATE(log), "[REQ] flushing caches"); + cache_flush(STATE_STATS(cache)); + break; + case KILL: + killer(); + break; + case STATS: + cache_stats(STATE_STATS(cache), fd); + dump_traffic_stats(fd); + break; + default: + ret = 0; + break; + } + + return ret; +} + +static void dump_stats(struct nf_conntrack *ct, struct nlmsghdr *nlh) +{ + if (cache_update_force(STATE_STATS(cache), ct)) + debug_ct(ct, "resync entry"); +} + +static void event_new_stats(struct nf_conntrack *ct, struct nlmsghdr *nlh) +{ + debug_ct(ct, "debug event"); + if (cache_add(STATE_STATS(cache), ct)) { + debug_ct(ct, "cache new"); + } else { + dlog(STATE(log), "can't add to cache cache: " + "%s\n", strerror(errno)); + debug_ct(ct, "can't add"); + } +} + +static void event_update_stats(struct nf_conntrack *ct, struct nlmsghdr *nlh) +{ + debug_ct(ct, "update"); + + if (!cache_update(STATE_STATS(cache), ct)) { + /* + * Perhaps we are losing events. If we are working + * in relax mode then add a new entry to the cache. + * + * FIXME: relax transitions not implemented yet + */ + if ((CONFIG(flags) & RELAX_TRANSITIONS) + && cache_add(STATE_STATS(cache), ct)) { + debug_ct(ct, "forcing cache update"); + } else { + debug_ct(ct, "can't update"); + return; + } + } + debug_ct(ct, "update"); +} + +static int event_destroy_stats(struct nf_conntrack *ct, struct nlmsghdr *nlh) +{ + if (cache_del(STATE_STATS(cache), ct)) { + debug_ct(ct, "cache destroy"); + return 1; + } else { + debug_ct(ct, "can't destroy!"); + return 0; + } +} + +struct ct_mode stats_mode = { + .init = init_stats, + .add_fds_to_set = NULL, + .step = NULL, + .local = local_handler_stats, + .kill = kill_stats, + .dump = dump_stats, + .overrun = dump_stats, + .event_new = event_new_stats, + .event_upd = event_update_stats, + .event_dst = event_destroy_stats +}; diff --git a/daemon/src/sync-mode.c b/daemon/src/sync-mode.c new file mode 100644 index 0000000..b32bef7 --- /dev/null +++ b/daemon/src/sync-mode.c @@ -0,0 +1,416 @@ +/* + * (C) 2006 by Pablo Neira Ayuso + * + * 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 +#include "cache.h" +#include "conntrackd.h" +#include +#include +#include +#include "us-conntrack.h" +#include +#include +#include "sync.h" +#include "network.h" + +/* handler for multicast messages received */ +static void mcast_handler() +{ + int ret; + char buf[4096], tmp[256]; + struct mcast_sock *m = STATE_SYNC(mcast_server); + unsigned int type; + struct nlnetwork *net = (struct nlnetwork *) buf; + unsigned int size = sizeof(struct nlnetwork); + struct nlmsghdr *nlh = (struct nlmsghdr *) (buf + size); + struct nf_conntrack *ct = (struct nf_conntrack *) tmp; + struct us_conntrack *u = NULL; + + memset(tmp, 0, sizeof(tmp)); + + ret = mcast_recv_netmsg(m, buf, sizeof(buf)); + if (ret <= 0) { + STATE(malformed)++; + return; + } + + if (STATE_SYNC(mcast_sync)->pre_recv(net)) + return; + + if ((type = parse_network_msg(ct, nlh)) == NFCT_T_ERROR) { + STATE(malformed)++; + return; + } + + nfct_attr_unset(ct, ATTR_TIMEOUT); + nfct_attr_unset(ct, ATTR_ORIG_COUNTER_BYTES); + nfct_attr_unset(ct, ATTR_ORIG_COUNTER_PACKETS); + nfct_attr_unset(ct, ATTR_REPL_COUNTER_BYTES); + nfct_attr_unset(ct, ATTR_REPL_COUNTER_PACKETS); + + switch(type) { + case NFCT_T_NEW: +retry: + if ((u = cache_add(STATE_SYNC(external), ct))) { + debug_ct(u->ct, "external new"); + } else { + /* + * One certain connection A arrives to the cache but + * another existing connection B in the cache has + * the same configuration, therefore B clashes with A. + */ + if (errno == EEXIST) { + cache_del(STATE_SYNC(external), ct); + goto retry; + } + debug_ct(ct, "can't add"); + } + break; + case NFCT_T_UPDATE: + if ((u = cache_update_force(STATE_SYNC(external), ct))) { + debug_ct(u->ct, "external update"); + } else + debug_ct(ct, "can't update"); + break; + case NFCT_T_DESTROY: + if (cache_del(STATE_SYNC(external), ct)) + debug_ct(ct, "external destroy"); + else + debug_ct(ct, "can't destroy"); + break; + default: + debug("unknown type %d\n", type); + break; + } +} + +static int init_sync(void) +{ + int ret; + + state.sync = malloc(sizeof(struct ct_sync_state)); + if (!state.sync) { + dlog(STATE(log), "[FAIL] can't allocate memory for state sync"); + return -1; + } + memset(state.sync, 0, sizeof(struct ct_sync_state)); + + if (CONFIG(flags) & SYNC_MODE_NACK) + STATE_SYNC(mcast_sync) = &nack; + else + /* default to persistent mode */ + STATE_SYNC(mcast_sync) = ¬rack; + + if (STATE_SYNC(mcast_sync)->init) + STATE_SYNC(mcast_sync)->init(); + + STATE_SYNC(internal) = + cache_create("internal", + STATE_SYNC(mcast_sync)->internal_cache_flags, + CONFIG(family), + STATE_SYNC(mcast_sync)->internal_cache_extra); + + if (!STATE_SYNC(internal)) { + dlog(STATE(log), "[FAIL] can't allocate memory for " + "the internal cache"); + return -1; + } + + STATE_SYNC(external) = + cache_create("external", + STATE_SYNC(mcast_sync)->external_cache_flags, + CONFIG(family), + NULL); + + if (!STATE_SYNC(external)) { + dlog(STATE(log), "[FAIL] can't allocate memory for the " + "external cache"); + return -1; + } + + /* multicast server to receive events from the wire */ + STATE_SYNC(mcast_server) = mcast_server_create(&CONFIG(mcast)); + if (STATE_SYNC(mcast_server) == NULL) { + dlog(STATE(log), "[FAIL] can't open multicast server!"); + return -1; + } + + /* multicast client to send events on the wire */ + STATE_SYNC(mcast_client) = mcast_client_create(&CONFIG(mcast)); + if (STATE_SYNC(mcast_client) == NULL) { + dlog(STATE(log), "[FAIL] can't open client multicast socket!"); + return -1; + } + + /* initialization of multicast sequence generation */ + STATE_SYNC(last_seq_sent) = time(NULL); + + if (create_alarm_thread() == -1) { + dlog(STATE(log), "[FAIL] can't initialize alarm thread"); + return -1; + } + + return 0; +} + +static int add_fds_to_set_sync(fd_set *readfds) +{ + FD_SET(STATE_SYNC(mcast_server->fd), readfds); + + return STATE_SYNC(mcast_server->fd); +} + +static void step_sync(fd_set *readfds) +{ + /* multicast packet has been received */ + if (FD_ISSET(STATE_SYNC(mcast_server->fd), readfds)) + mcast_handler(); +} + +static void kill_sync() +{ + cache_destroy(STATE_SYNC(internal)); + cache_destroy(STATE_SYNC(external)); + + mcast_server_destroy(STATE_SYNC(mcast_server)); + mcast_client_destroy(STATE_SYNC(mcast_client)); + + destroy_alarm_thread(); + + if (STATE_SYNC(mcast_sync)->kill) + STATE_SYNC(mcast_sync)->kill(); +} + +static dump_stats_sync(int fd) +{ + char buf[512]; + int size; + + size = sprintf(buf, "multicast sequence tracking:\n" + "%20llu Pckts mfrm " + "%20llu Pckts lost\n\n", + STATE(malformed), + STATE_SYNC(packets_lost)); + + send(fd, buf, size, 0); +} + +/* handler for requests coming via UNIX socket */ +static int local_handler_sync(int fd, int type, void *data) +{ + int ret = 1; + + switch(type) { + case DUMP_INTERNAL: + cache_dump(STATE_SYNC(internal), fd, NFCT_O_PLAIN); + break; + case DUMP_EXTERNAL: + cache_dump(STATE_SYNC(external), fd, NFCT_O_PLAIN); + break; + case DUMP_INT_XML: + cache_dump(STATE_SYNC(internal), fd, NFCT_O_XML); + break; + case DUMP_EXT_XML: + cache_dump(STATE_SYNC(external), fd, NFCT_O_XML); + break; + case COMMIT: + dlog(STATE(log), "[REQ] commit external cache to master table"); + cache_commit(STATE_SYNC(external)); + break; + case FLUSH_CACHE: + dlog(STATE(log), "[REQ] flushing caches"); + cache_flush(STATE_SYNC(internal)); + cache_flush(STATE_SYNC(external)); + break; + case KILL: + killer(); + break; + case STATS: + cache_stats(STATE_SYNC(internal), fd); + cache_stats(STATE_SYNC(external), fd); + dump_traffic_stats(fd); + mcast_dump_stats(fd, STATE_SYNC(mcast_client), + STATE_SYNC(mcast_server)); + dump_stats_sync(fd); + break; + case SEND_BULK: + dlog(STATE(log), "[REQ] sending bulk update"); + cache_bulk(STATE_SYNC(internal)); + break; + default: + if (STATE_SYNC(mcast_sync)->local) + ret = STATE_SYNC(mcast_sync)->local(fd, type, data); + break; + } + + return ret; +} + +static void dump_sync(struct nf_conntrack *ct, struct nlmsghdr *nlh) +{ + /* This is required by kernels < 2.6.20 */ + nfct_attr_unset(ct, ATTR_TIMEOUT); + nfct_attr_unset(ct, ATTR_ORIG_COUNTER_BYTES); + nfct_attr_unset(ct, ATTR_ORIG_COUNTER_PACKETS); + nfct_attr_unset(ct, ATTR_REPL_COUNTER_BYTES); + nfct_attr_unset(ct, ATTR_REPL_COUNTER_PACKETS); + nfct_attr_unset(ct, ATTR_USE); + + if (cache_update_force(STATE_SYNC(internal), ct)) + debug_ct(ct, "resync"); +} + +static void mcast_send_sync(struct nlmsghdr *nlh, + struct us_conntrack *u, + struct nf_conntrack *ct, + int type) +{ + char buf[4096]; + struct nlnetwork *net = (struct nlnetwork *) buf; + int mangled = 0; + + memset(buf, 0, sizeof(buf)); + + if (!state_helper_verdict(type, ct)) + return; + + if (!mangled) + memcpy(buf + sizeof(struct nlnetwork), nlh, nlh->nlmsg_len); + + mcast_send_netmsg(STATE_SYNC(mcast_client), net); + STATE_SYNC(mcast_sync)->post_send(net, u); +} + +static void overrun_sync(struct nf_conntrack *ct, struct nlmsghdr *nlh) +{ + struct us_conntrack *u; + + /* This is required by kernels < 2.6.20 */ + nfct_attr_unset(ct, ATTR_TIMEOUT); + nfct_attr_unset(ct, ATTR_ORIG_COUNTER_BYTES); + nfct_attr_unset(ct, ATTR_ORIG_COUNTER_PACKETS); + nfct_attr_unset(ct, ATTR_REPL_COUNTER_BYTES); + nfct_attr_unset(ct, ATTR_REPL_COUNTER_PACKETS); + nfct_attr_unset(ct, ATTR_USE); + + if (!cache_test(STATE_SYNC(internal), ct)) { + if ((u = cache_update_force(STATE_SYNC(internal), ct))) { + debug_ct(ct, "overrun resync"); + mcast_send_sync(nlh, u, ct, NFCT_T_UPDATE); + } + } +} + +static void event_new_sync(struct nf_conntrack *ct, struct nlmsghdr *nlh) +{ + struct us_conntrack *u; + + /* required by linux kernel <= 2.6.20 */ + nfct_attr_unset(ct, ATTR_ORIG_COUNTER_BYTES); + nfct_attr_unset(ct, ATTR_ORIG_COUNTER_PACKETS); + nfct_attr_unset(ct, ATTR_REPL_COUNTER_BYTES); + nfct_attr_unset(ct, ATTR_REPL_COUNTER_PACKETS); + nfct_attr_unset(ct, ATTR_TIMEOUT); +retry: + if ((u = cache_add(STATE_SYNC(internal), ct))) { + mcast_send_sync(nlh, u, ct, NFCT_T_NEW); + debug_ct(u->ct, "internal new"); + } else { + if (errno == EEXIST) { + char buf[4096]; + struct nlmsghdr *nlh = (struct nlmsghdr *) buf; + + int ret = build_network_msg(NFCT_Q_DESTROY, + STATE(subsys_event), + ct, + buf, + sizeof(buf)); + if (ret == -1) + return; + + cache_del(STATE_SYNC(internal), ct); + mcast_send_sync(nlh, NULL, ct, NFCT_T_NEW); + goto retry; + } + dlog(STATE(log), "can't add to internal cache: " + "%s\n", strerror(errno)); + debug_ct(ct, "can't add"); + } +} + +static void event_update_sync(struct nf_conntrack *ct, struct nlmsghdr *nlh) +{ + struct us_conntrack *u; + + nfct_attr_unset(ct, ATTR_TIMEOUT); + + if ((u = cache_update(STATE_SYNC(internal), ct)) == NULL) { + /* + * Perhaps we are losing events. If we are working + * in relax mode then add a new entry to the cache. + * + * FIXME: relax transitions not implemented yet + */ + if ((CONFIG(flags) & RELAX_TRANSITIONS) + && (u = cache_add(STATE_SYNC(internal), ct))) { + debug_ct(u->ct, "forcing internal update"); + } else { + debug_ct(ct, "can't update"); + return; + } + } + debug_ct(u->ct, "internal update"); + mcast_send_sync(nlh, u, ct, NFCT_T_UPDATE); +} + +static int event_destroy_sync(struct nf_conntrack *ct, struct nlmsghdr *nlh) +{ + nfct_attr_unset(ct, ATTR_TIMEOUT); + + if (CONFIG(flags) & DELAY_DESTROY_MSG) { + + nfct_set_attr_u32(ct, ATTR_STATUS, IPS_DYING); + + if (cache_update(STATE_SYNC(internal), ct)) { + debug_ct(ct, "delay internal destroy"); + return 1; + } else { + debug_ct(ct, "can't delay destroy!"); + return 0; + } + } else { + if (cache_del(STATE_SYNC(internal), ct)) { + mcast_send_sync(nlh, NULL, ct, NFCT_T_DESTROY); + debug_ct(ct, "internal destroy"); + } else + debug_ct(ct, "can't destroy"); + } +} + +struct ct_mode sync_mode = { + .init = init_sync, + .add_fds_to_set = add_fds_to_set_sync, + .step = step_sync, + .local = local_handler_sync, + .kill = kill_sync, + .dump = dump_sync, + .overrun = overrun_sync, + .event_new = event_new_sync, + .event_upd = event_update_sync, + .event_dst = event_destroy_sync +}; diff --git a/daemon/src/sync-nack.c b/daemon/src/sync-nack.c new file mode 100644 index 0000000..288dba4 --- /dev/null +++ b/daemon/src/sync-nack.c @@ -0,0 +1,309 @@ +/* + * (C) 2006-2007 by Pablo Neira Ayuso + * + * 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 +#include "conntrackd.h" +#include "sync.h" +#include "linux_list.h" +#include "us-conntrack.h" +#include "buffer.h" +#include "debug.h" +#include "network.h" +#include +#include + +#if 0 +#define dp printf +#else +#define dp +#endif + +static LIST_HEAD(queue); + +struct cache_nack { + struct list_head head; + u_int32_t seq; +}; + +static void cache_nack_add(struct us_conntrack *u, void *data) +{ + struct cache_nack *cn = data; + + INIT_LIST_HEAD(&cn->head); + list_add(&cn->head, &queue); +} + +static void cache_nack_update(struct us_conntrack *u, void *data) +{ + struct cache_nack *cn = data; + + if (cn->head.next != LIST_POISON1 && + cn->head.prev != LIST_POISON2) + list_del(&cn->head); + + INIT_LIST_HEAD(&cn->head); + list_add(&cn->head, &queue); +} + +static void cache_nack_destroy(struct us_conntrack *u, void *data) +{ + struct cache_nack *cn = data; + + if (cn->head.next != LIST_POISON1 && + cn->head.prev != LIST_POISON2) + list_del(&cn->head); +} + +static struct cache_extra cache_nack_extra = { + .size = sizeof(struct cache_nack), + .add = cache_nack_add, + .update = cache_nack_update, + .destroy = cache_nack_destroy +}; + +static int nack_init() +{ + STATE_SYNC(buffer) = buffer_create(CONFIG(resend_buffer_size)); + if (STATE_SYNC(buffer) == NULL) + return -1; + + return 0; +} + +static void nack_kill() +{ + buffer_destroy(STATE_SYNC(buffer)); +} + +static void mcast_send_nack(u_int32_t expt_seq, u_int32_t recv_seq) +{ + struct nlnetwork_ack nack = { + .flags = NET_NACK, + .from = expt_seq, + .to = recv_seq, + }; + + mcast_send_error(STATE_SYNC(mcast_client), &nack); + buffer_add(STATE_SYNC(buffer), &nack, sizeof(struct nlnetwork_ack)); +} + +static void mcast_send_ack(u_int32_t from, u_int32_t to) +{ + struct nlnetwork_ack ack = { + .flags = NET_ACK, + .from = from, + .to = to, + }; + + mcast_send_error(STATE_SYNC(mcast_client), &ack); + buffer_add(STATE_SYNC(buffer), &ack, sizeof(struct nlnetwork_ack)); +} + +static void mcast_send_resync() +{ + struct nlnetwork net = { + .flags = NET_RESYNC, + }; + + mcast_send_error(STATE_SYNC(mcast_client), &net); + buffer_add(STATE_SYNC(buffer), &net, sizeof(struct nlnetwork)); +} + +int nack_local(int fd, int type, void *data) +{ + int ret = 1; + + switch(type) { + case REQUEST_DUMP: + mcast_send_resync(); + dlog(STATE(log), "[REQ] request resync"); + break; + default: + ret = 0; + break; + } + + return ret; +} + +static int buffer_compare(void *data1, void *data2) +{ + struct nlnetwork *net = data1; + struct nlnetwork_ack *nack = data2; + struct nlmsghdr *nlh = data1 + sizeof(struct nlnetwork); + + unsigned old_seq = ntohl(net->seq); + + if (ntohl(net->seq) >= nack->from && ntohl(net->seq) <= nack->to) { + if (mcast_resend_netmsg(STATE_SYNC(mcast_client), net)) + dp("resend destroy (old seq=%u) (seq=%u)\n", + old_seq, ntohl(net->seq)); + } + return 0; +} + +static int buffer_remove(void *data1, void *data2) +{ + struct nlnetwork *net = data1; + struct nlnetwork_ack *h = data2; + + if (ntohl(net->seq) >= h->from && ntohl(net->seq) <= h->to) { + dp("remove from buffer (seq=%u)\n", ntohl(net->seq)); + __buffer_del(STATE_SYNC(buffer), data1); + } + return 0; +} + +static void queue_resend(struct cache *c, unsigned int from, unsigned int to) +{ + struct list_head *n; + struct us_conntrack *u; + unsigned int *seq; + + lock(); + list_for_each(n, &queue) { + struct cache_nack *cn = (struct cache_nack *) n; + struct us_conntrack *u; + + u = cache_get_conntrack(STATE_SYNC(internal), cn); + + if (cn->seq >= from && cn->seq <= to) { + debug_ct(u->ct, "resend nack"); + dp("resending nack'ed (oldseq=%u) ", cn->seq); + + char buf[4096]; + struct nlnetwork *net = (struct nlnetwork *) buf; + + int ret = build_network_msg(NFCT_Q_UPDATE, + STATE(subsys_event), + u->ct, + buf, + sizeof(buf)); + if (ret == -1) { + unlock(); + break; + } + + mcast_send_netmsg(STATE_SYNC(mcast_client), buf); + STATE_SYNC(mcast_sync)->post_send(net, u); + dp("(newseq=%u)\n", *seq); + } + } + unlock(); +} + +static void queue_empty(struct cache *c, unsigned int from, unsigned int to) +{ + struct list_head *n, *tmp; + struct us_conntrack *u; + unsigned int *seq; + + lock(); + dp("ACK from %u to %u\n", from, to); + list_for_each_safe(n, tmp, &queue) { + struct cache_nack *cn = (struct cache_nack *) n; + + u = cache_get_conntrack(STATE_SYNC(internal), cn); + if (cn->seq >= from && cn->seq <= to) { + dp("remove %u\n", cn->seq); + debug_ct(u->ct, "ack received: empty queue"); + dp("queue: deleting from queue (seq=%u)\n", cn->seq); + list_del(&cn->head); + } + } + unlock(); +} + +static int nack_pre_recv(const struct nlnetwork *net) +{ + static unsigned int window = 0; + unsigned int exp_seq; + + if (window == 0) + window = CONFIG(window_size); + + if (!mcast_track_seq(net->seq, &exp_seq)) { + dp("OOS: sending nack (seq=%u)\n", exp_seq); + mcast_send_nack(exp_seq, net->seq - 1); + window = CONFIG(window_size); + } else { + /* received a window, send an acknowledgement */ + if (--window == 0) { + dp("sending ack (seq=%u)\n", net->seq); + mcast_send_ack(net->seq-CONFIG(window_size), net->seq); + } + } + + if (net->flags & NET_NACK) { + struct nlnetwork_ack *nack = (struct nlnetwork_ack *) net; + + dp("NACK: from seq=%u to seq=%u\n", nack->from, nack->to); + queue_resend(STATE_SYNC(internal), nack->from, nack->to); + buffer_iterate(STATE_SYNC(buffer), nack, buffer_compare); + return 1; + } else if (net->flags & NET_RESYNC) { + dp("RESYNC ALL\n"); + cache_bulk(STATE_SYNC(internal)); + return 1; + } else if (net->flags & NET_ACK) { + struct nlnetwork_ack *h = (struct nlnetwork_ack *) net; + + dp("ACK: from seq=%u to seq=%u\n", h->from, h->to); + queue_empty(STATE_SYNC(internal), h->from, h->to); + buffer_iterate(STATE_SYNC(buffer), h, buffer_remove); + return 1; + } + + return 0; +} + +static void nack_post_send(const struct nlnetwork *net, struct us_conntrack *u) +{ + unsigned int size = sizeof(struct nlnetwork); + struct nlmsghdr *nlh = (struct nlmsghdr *) ((void *) net + size); + + if (NFNL_MSG_TYPE(ntohs(nlh->nlmsg_type)) == IPCTNL_MSG_CT_DELETE) { + buffer_add(STATE_SYNC(buffer), net, + ntohl(nlh->nlmsg_len) + size); + } else if (u != NULL) { + unsigned int *seq; + struct list_head *n; + struct cache_nack *cn; + + cn = (struct cache_nack *) + cache_get_extra(STATE_SYNC(internal), u); + cn->seq = ntohl(net->seq); + if (cn->head.next != LIST_POISON1 && + cn->head.prev != LIST_POISON2) + list_del(&cn->head); + + INIT_LIST_HEAD(&cn->head); + list_add(&cn->head, &queue); + } +} + +struct sync_mode nack = { + .internal_cache_flags = LIFETIME, + .external_cache_flags = LIFETIME, + .internal_cache_extra = &cache_nack_extra, + .init = nack_init, + .kill = nack_kill, + .local = nack_local, + .pre_recv = nack_pre_recv, + .post_send = nack_post_send, +}; diff --git a/daemon/src/sync-notrack.c b/daemon/src/sync-notrack.c new file mode 100644 index 0000000..2b5ae38 --- /dev/null +++ b/daemon/src/sync-notrack.c @@ -0,0 +1,127 @@ +/* + * (C) 2006-2007 by Pablo Neira Ayuso + * + * 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 "conntrackd.h" +#include "sync.h" +#include "network.h" +#include "us-conntrack.h" +#include "alarm.h" + +static void refresher(struct alarm_list *a, void *data) +{ + struct us_conntrack *u = data; + char buf[8192]; + int size; + + if (nfct_get_attr_u32(u->ct, ATTR_STATUS) & IPS_DYING) { + + debug_ct(u->ct, "persistence destroy"); + + size = build_network_msg(NFCT_Q_DESTROY, + STATE(subsys_event), + u->ct, + buf, + sizeof(buf)); + + __cache_del(u->cache, u->ct); + mcast_send_netmsg(STATE_SYNC(mcast_client), buf); + } else { + + debug_ct(u->ct, "persistence update"); + + a->expires = random() % CONFIG(refresh) + 1; + size = build_network_msg(NFCT_Q_UPDATE, + STATE(subsys_event), + u->ct, + buf, + sizeof(buf)); + mcast_send_netmsg(STATE_SYNC(mcast_client), buf); + } +} + +static void cache_notrack_add(struct us_conntrack *u, void *data) +{ + struct alarm_list *alarm = data; + + init_alarm(alarm); + set_alarm_expiration(alarm, (random() % conf.refresh) + 1); + set_alarm_data(alarm, u); + set_alarm_function(alarm, refresher); + add_alarm(alarm); +} + +static void cache_notrack_update(struct us_conntrack *u, void *data) +{ + struct alarm_list *alarm = data; + mod_alarm(alarm, (random() % conf.refresh) + 1); +} + +static void cache_notrack_destroy(struct us_conntrack *u, void *data) +{ + struct alarm_list *alarm = data; + del_alarm(alarm); +} + +static struct cache_extra cache_notrack_extra = { + .size = sizeof(struct alarm_list), + .add = cache_notrack_add, + .update = cache_notrack_update, + .destroy = cache_notrack_destroy +}; + +static int notrack_pre_recv(const struct nlnetwork *net) +{ + unsigned int exp_seq; + + /* + * Ignore error messages: Although this message type is not ever + * generated in notrack mode, we don't want to crash the daemon + * if someone nuts mixes nack and notrack. + */ + if (net->flags & (NET_RESYNC | NET_NACK)) + return 1; + + /* + * Multicast sequence tracking: we keep track of multicast messages + * although we don't do any explicit message recovery. So, why do + * we do sequence tracking? Just to let know the sysadmin. + * + * Let t be 1 < t < RefreshTime. To ensure consistency, conntrackd + * retransmit every t seconds a message with the state of a certain + * entry even if such entry did not change. This mechanism also + * provides passive resynchronization, in other words, there is + * no facility to request a full synchronization from new nodes that + * just joined the cluster, instead they just get resynchronized in + * RefreshTime seconds at worst case. + */ + mcast_track_seq(net->seq, &exp_seq); + + return 0; +} + +static void notrack_post_send(const struct nlnetwork *n, struct us_conntrack *u) +{ +} + +struct sync_mode notrack = { + .internal_cache_flags = LIFETIME, + .external_cache_flags = TIMER | LIFETIME, + .internal_cache_extra = &cache_notrack_extra, + .pre_recv = notrack_pre_recv, + .post_send = notrack_post_send, +}; diff --git a/daemon/src/traffic_stats.c b/daemon/src/traffic_stats.c new file mode 100644 index 0000000..b510b77 --- /dev/null +++ b/daemon/src/traffic_stats.c @@ -0,0 +1,54 @@ +/* + * (C) 2006 by Pablo Neira Ayuso + * + * 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 "conntrackd.h" +#include +#include +#include +#include "us-conntrack.h" +#include + +void update_traffic_stats(struct nf_conntrack *ct) +{ + STATE(bytes)[NFCT_DIR_ORIGINAL] += + nfct_get_attr_u32(ct, ATTR_ORIG_COUNTER_BYTES); + STATE(bytes)[NFCT_DIR_REPLY] += + nfct_get_attr_u32(ct, ATTR_REPL_COUNTER_BYTES); + STATE(packets)[NFCT_DIR_ORIGINAL] += + nfct_get_attr_u32(ct, ATTR_ORIG_COUNTER_PACKETS); + STATE(packets)[NFCT_DIR_REPLY] += + nfct_get_attr_u32(ct, ATTR_REPL_COUNTER_PACKETS); +} + +void dump_traffic_stats(int fd) +{ + char buf[512]; + int size; + u_int64_t bytes = STATE(bytes)[NFCT_DIR_ORIGINAL] + + STATE(bytes)[NFCT_DIR_REPLY]; + u_int64_t packets = STATE(packets)[NFCT_DIR_ORIGINAL] + + STATE(packets)[NFCT_DIR_REPLY]; + + size = sprintf(buf, "traffic processed:\n"); + size += sprintf(buf+size, "%20llu Bytes ", bytes); + size += sprintf(buf+size, "%20llu Pckts\n\n", packets); + + send(fd, buf, size, 0); +} -- cgit v1.2.3