summaryrefslogtreecommitdiffstats
path: root/cli
diff options
context:
space:
mode:
Diffstat (limited to 'cli')
-rw-r--r--cli/AUTHORS2
-rw-r--r--cli/ChangeLog243
-rw-r--r--cli/INSTALL229
-rw-r--r--cli/Make_global.am1
-rw-r--r--cli/Makefile.am21
-rwxr-xr-xcli/autogen.sh18
-rw-r--r--cli/configure.in72
-rw-r--r--cli/conntrack.8142
-rw-r--r--cli/extensions/Makefile.am16
-rw-r--r--cli/extensions/libct_proto_icmp.c108
-rw-r--r--cli/extensions/libct_proto_icmp.man10
-rw-r--r--cli/extensions/libct_proto_sctp.c164
-rw-r--r--cli/extensions/libct_proto_tcp.c180
-rw-r--r--cli/extensions/libct_proto_tcp.man16
-rw-r--r--cli/extensions/libct_proto_udp.c141
-rw-r--r--cli/extensions/libct_proto_udp.man13
-rw-r--r--cli/include/Makefile.am2
-rw-r--r--cli/include/conntrack.h160
-rw-r--r--cli/include/linux_list.h725
-rw-r--r--cli/src/Makefile.am7
-rw-r--r--cli/src/conntrack.c1131
-rw-r--r--cli/test.sh110
22 files changed, 3511 insertions, 0 deletions
diff --git a/cli/AUTHORS b/cli/AUTHORS
new file mode 100644
index 0000000..d1cb6fa
--- /dev/null
+++ b/cli/AUTHORS
@@ -0,0 +1,2 @@
+Pablo Neira Ayuso <pablo@eurodev.net>
+Harald Welte <laforge@netfilter.org>
diff --git a/cli/ChangeLog b/cli/ChangeLog
new file mode 100644
index 0000000..1524ef6
--- /dev/null
+++ b/cli/ChangeLog
@@ -0,0 +1,243 @@
+2006-03-20
+<hidden@sch.bme.hu>
+ o fix ICMP protocol extension parse callback
+
+2006-01-15
+<pablo@netfilter.org>
+ o Added missing parameters to set the ports of an expectation tuple
+ o Add support to filter dumped entries.
+ ie: conntrack -L -p tcp --orig-port-dst 993
+ display all the connections to IMAPS servers
+ conntrack -L -m 2
+ display all the connection marked with 2
+ o Bumped version to 1.00beta2
+
+2005-12-26
+<pablo@netfilter.org>
+ o add IPv6 support: main change
+ o removed dead code: iptables_insmod and get_modprobe
+ o compact the commands vs. options table
+ o move working vars from the stack to the BSS section
+ o update manpage
+ o Bumped version to 1.0beta1
+<yasuyuki.kozakai@toshiba.co.jp>
+ o check address family mismatch
+ o fix incomplete copying IPv6 addresses
+
+2005-12-19
+<pablo@netfilter.org>
+ o We only support ipv4 at the moment: set l3protonum to AF_INET
+ o Minor changes to prepare upcoming ipv6 support
+
+2005-12-03
+<pablo@netfilter.org>
+ o Add support to filter events. ie: -p tcp --orig-port-dst 80 in
+ conjuction with -E to get all the requests to HTTP servers
+ o Update manpage
+ o Missing static function declaration in the protocol handlers
+ o Use protocol flags defined in libnetfilter_conntrack
+ o Bumped version to 0.991
+
+2005-11-22
+<marcus@ingate.com>
+ o Fix oversized number of options
+
+2005-11-11
+<laforge@netfilter.org>
+ o don't check for kernel header path in configure, since we don't use
+ kernel headers
+ o don't check for libnfnetlink, we don't use it directly
+ o move plugins into pkglibdir
+ o remove 'lib' prefix of plugins, they're not really libraries
+ o remove version information from plugin filenames
+ o Bumped version to 0.99
+2005-11-09
+<pablo@netfilter.org>
+ o set status to zero, libnetfilter_conntrack now activate
+ IPS_CONFIRMED since all conntrack in hash must be confirmed.
+ o Bumped version to 0.98
+
+2005-11-08
+<olenf@ans.pl>
+ o Fix warnings generated by gcc -Wall
+ o Fix conntrack exit value at error
+ o Replace obsolete inet_addr by inet_aton
+
+2005-11-05
+<olenf@ans.pl>
+ o Improved conntrack -h output
+ o add htons for icmp id.
+<pablo@eurodev.net>
+ o -t and -u are optional at update.
+ o Fixed versioning :(
+ o Bumped version to 0.97
+
+2005-11-03
+<laforge@netfilter.org>
+ o Use extra 'data' argument of nfct_register_callback() function that
+ I've introduced in libetfilter_conntrack.
+<olenf@ans.pl>
+ o moves conntrack tool from bin to sbin directory since this
+ application is an administration utility and it requires uid==0 or
+ CAP_NET_ADMIN
+<pablo@eurodev.net>
+ o check if --state missing when -p is passed
+ o command type is passed to final_check: checkings based on the
+ command can be done now.
+ o kill duplicated definition of IPS_* bits: Already present in
+ libnetfilter_conntrack.
+ o Move action and command enum to conntrack.h
+ o kill NIPQUAD macro
+ o make conntrack handler cth static.
+ o Bumped version to 0.96
+
+2005-11-01
+<pablo@eurodev.net>
+ o Fix error message describing illegal option -E -i
+ o -D -i ID requires tuple information: Display an error message
+ o Use NFCT_ALL_CT_GROUPS flag instead of NFCT_ALL_GROUPS
+ o Event mask doesn't make sense for expectations, kill dead code
+ o Bumped version to 0.95
+<olenf@ans.pl>
+ o Fix wrong formating in conntrack -h
+
+2005-10-30
+<pablo@eurodev.net>
+ Special thanks to Deti Fiegl from the Leibniz Supercomputing Centre in
+ Munich, Germany for providing the "fast" hardware to reproduce
+ spurious bugs ;)
+
+ o Replace misleading message "Not enough memory" by "Can't open handler"
+ o New option -i for expectation dumping: conntrack -L expect [-i]
+ o sed 's/VERSION/CONNTRACK_VERSION/g'
+ o Fix nfct_open flags, now uses NFCT_ALL_GROUPS when needed
+ o Bumped version to 0.94
+
+2005-10-28
+<pablo@eurodev.net>
+ o New option -i for dumping: conntrack -L [-i]
+ o Fixed warning in findproto due to a stupid wrong type definition
+ o sed 's/nfct_set_callback/nfct_register_callback/g'
+ o killed the 'retry' logic, *sigh* it is broken in some cases
+ o killed broken and unneeded protocol handler destructors (fini)
+ o killed unregister_proto
+ o Fixed code indentation in the command selector
+ o Bumped version to 0.93
+
+2005-10-27
+<pablo@eurodev.net>
+ o Use conntrack VERSION instead of the old LIBCT_VERSION
+ o proto_list and lib_dir are now static
+ o kill dead code: function dump_tuple
+ o Bumped version to 0.92
+
+2005-10-25
+<eleblond@inl.fr>
+ o Add missing autogen.sh file
+
+2005-10-24
+<pablo@eurodev.net>
+ o use NFCT_ANY_GROUP flag in nfct_open()
+
+2005-10-21
+<pablo@eurodev.net>
+ o Bumped version to 0.90
+ o Add support for id and marks
+
+2005-10-20
+<pablo@eurodev.net>
+ o Kill some more files that generated by the autocrap
+ o Resync with the lastest libnetfilter_conntrack API changes
+
+2005-10-16
+<pablo@netfilter.org>
+ o Rename libct_proto.h to conntrack.h
+ o Remove config.h.in from svn, it's autogenerated by the autocrap :)
+ o Remove dead functions in the SCTP protocol helper
+
+2005-10-14
+<pablo@netfilter.org>
+ o Kill config.h.in, it's generated by the autocrap
+ o The conntrack tool now uses libnetfilter_conntrack :)
+ o libct.c has been killed, now it's in libnetfilter_conntrack
+ o Check if you're root or CAP_NET_ADMIN
+ o Bumped version number to 0.86
+
+2005-10-07
+<chentschel@iplan.com.ar>
+ o Fixed ICMP options
+<pablo@netfilter.org>
+ o Multiple fixes for the ICMP protocol handler
+ o Fix ICMP output: wrong output. type and code were set to zero.
+
+2005-10-05
+<pablo@netfilter.org>
+ o Fix up counters
+ o Fix up compilation (IPS_* stuff missing), still need a proper fix
+ o Bumped version number to 0.82
+
+2005-09-24
+<laforge@netfilter.org>
+ o Get rid of C++ style comments
+ o Remove remaining bits of "-A --action", group-mask and dump-mask
+ o Clean up #include's
+ o Fix double-free when exiting via signal handler (Ctrl+C)
+ o Add "version" member to plugins
+ o Fix some Endianness issues when printing CTA_STATUS
+
+2005-08-31
+<pablo@netfilter.org>
+ o Fix packet and bytes counters (use __be64_to_cpu)
+ o Fix ip_conntrack_netlink load-on-demand
+
+2005-07-12
+<pablo@eurodev.net>
+ o Use conntrack netlink attributes: Major change
+ o Kill action setting: Mask based dumping
+ o Fix ChangeLog
+
+2005-05-23
+<laforge@netfilter.org>
+ o Fixed syntax error (tab/space issue) in help message
+ o Fixed getopt handling on big endian machines
+ o Fixed possible future read-over-end-of-array in TCP extension
+ o Add manpage
+ o Add missing space at output of libct_proto_icmp.c
+ o Add status bits that were introduced in 2.6.11
+ o Add SCTP extension
+ o Add support for expect creation
+ o Bump version number to 0.63
+
+2005-05-17
+<pablo@eurodev.net>
+ o Added descriptive error messages.
+ o Fix wrong flags check in [tcp|udp] proto helpers.
+
+2005-05-16
+<pablo@eurodev.net>
+ o Implemented ICMP proto helper
+ o Added help() and final_check() functions for proto helpers.
+
+2005-05-01
+<pablo@eurodev.net>
+ o Created changelog file
+ o Deleted libctnetlink.h and libnfnetlink.h from the include/ dir.
+ o Added support for version (-V) and help (-h)
+ o Added event mask based support
+ o Added GPLv2 headers
+ o Use fprintf instead of printf
+ o Defined print_tuple and print_proto output interfaces
+ o ctnl_[get|del]_conntrack handles return value from kernel via msgerr
+ o Added support for conntrack table flushing
+ o Added test case file (test.sh)
+ o Improve dump output
+
+<azez@ufomechanic.net>
+ o Autoconf stuff for conntrack + some pablo's modifications.
+ o Fixed packet counters formatting (use %llu instead of %lu)
+
+2005-04-25
+<pablo@eurodev.net>
+ o Added support for mask based event dumping
+ o Added support for mask based event notification
+ o On-demand autoload of ip_conntrack_netlink
diff --git a/cli/INSTALL b/cli/INSTALL
new file mode 100644
index 0000000..54caf7c
--- /dev/null
+++ b/cli/INSTALL
@@ -0,0 +1,229 @@
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software
+Foundation, Inc.
+
+ This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+ These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the `configure' script does not know about. Run `./configure --help'
+for details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory. After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+ By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=PATH' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+ Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+ There may be some features `configure' cannot figure out
+automatically, but needs to determine by the type of machine the package
+will run on. Usually, assuming the package is built to be run on the
+_same_ architectures, `configure' can figure that out, but if it prints
+a message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the `--target=TYPE' option to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+ If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+ Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+will cause the specified gcc to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+`configure' Invocation
+======================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/cli/Make_global.am b/cli/Make_global.am
new file mode 100644
index 0000000..685add7
--- /dev/null
+++ b/cli/Make_global.am
@@ -0,0 +1 @@
+INCLUDES=$(all_includes) -I$(top_srcdir)/include
diff --git a/cli/Makefile.am b/cli/Makefile.am
new file mode 100644
index 0000000..d3b4ceb
--- /dev/null
+++ b/cli/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 = conntrack.8
+
+EXTRA_DIST = $(man_MANS) Make_global.am debian
+
+SUBDIRS = src extensions
+DIST_SUBDIRS = include src extensions
+LINKOPTS = -ldl -lnetfilter_conntrack
+AM_CFLAGS = -g
+
+$(OBJECTS): libtool
+libtool: $(LIBTOOL_DEPS)
+ $(SHELL) ./config.status --recheck
+
+dist-hook:
+ rm -rf `find $(distdir)/debian -name .svn`
diff --git a/cli/autogen.sh b/cli/autogen.sh
new file mode 100755
index 0000000..e76d3ef
--- /dev/null
+++ b/cli/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/cli/configure.in b/cli/configure.in
new file mode 100644
index 0000000..1b1b391
--- /dev/null
+++ b/cli/configure.in
@@ -0,0 +1,72 @@
+AC_INIT
+
+AC_CANONICAL_SYSTEM
+
+AM_INIT_AUTOMAKE(conntrack, 1.00beta2)
+
+AC_PROG_CC
+AM_PROG_LIBTOOL
+AC_PROG_INSTALL
+AC_PROG_LN_S
+
+case $target in
+*-*-linux*) ;;
+*) AC_MSG_ERROR([Linux only, dude!]);;
+esac
+
+dnl Dependencies
+LIBNFCONNTRACK_REQUIRED=0.0.31
+
+AC_CHECK_LIB(dl, dlopen)
+
+PKG_CHECK_MODULES(LIBNFCONNTRACK, libnetfilter_conntrack >= $LIBNFCONNTRACK_REQUIRED,,
+ AC_MSG_ERROR(Cannot find libnetfilter_conntrack >= $LIBNFCONNTRACK_REQUIRED))
+
+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 <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#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
+
+dnl--------------------------------
+
+if test ! -z "$libdir"; then
+ MODULE_DIR="\\\"$libdir/conntrack/\\\""
+ CFLAGS="$CFLAGS -DCONNTRACK_LIB_DIR=$MODULE_DIR"
+fi
+
+dnl--------------------------------
+
+CFLAGS="$CFLAGS $LIBNFCONNTRACK_CFLAGS"
+CONNTRACK_LIBS="$LIBNFCONNTRACK_LIBS"
+
+AC_SUBST(CONNTRACK_LIBS)
+
+AC_OUTPUT(Makefile src/Makefile extensions/Makefile include/Makefile)
diff --git a/cli/conntrack.8 b/cli/conntrack.8
new file mode 100644
index 0000000..307180b
--- /dev/null
+++ b/cli/conntrack.8
@@ -0,0 +1,142 @@
+.TH CONNTRACK 8 "Jun 23, 2005" "" ""
+
+.\" Man page written by Harald Welte <laforge@netfilter.org (Jun 2005)
+
+.SH NAME
+conntrack \- administration tool for netfilter connection tracking
+.SH SYNOPSIS
+.BR "conntrack -L [table] [-z]"
+.br
+.BR "conntrack -G [table] parameters"
+.br
+.BR "conntrack -D [table] paramaters"
+.br
+.BR "conntrack -I [table] parameters"
+.br
+.BR "conntrack -E [table] parameters"
+.br
+.BR "conntrack -F [table]"
+.SH DESCRIPTION
+.B conntrack
+is used to search, list, inspect and maintain the netfilter connection tracking
+subsystem of the Linux kernel.
+.PP
+Using
+.B conntrack
+, you can dump a list of all (or a filtered selection of) currently tracked
+connections, delete connections from the state table, and even add new ones.
+.PP
+In addition, you can also monitor connection tracking events, e.g. show an
+event message (one line) per newly established connection.
+.SH TABLES
+The connection tracking subsystem maintains two internal tables:
+.TP
+.BR "conntrack" :
+This is the default table. It contains a list of all currently tracked
+connections through the system. If you don't use connection tracking
+exemptions (NOTRACK iptables target), this means all connections that go
+through the system.
+.TP
+.BR "expect" :
+This is the table of expectations. Connection tracking expectations are the
+mechanism used to "expect" RELATED connections to existing ones. Expectations
+are generally used by "connection tracking helpers" (sometimes called
+application level gateways [ALGs]) for more complex protocols such as FTP,
+SIP, H.323.
+.SH OPTIONS
+The options recognized by
+.B conntrack
+can be divided into several different groups.
+.SS COMMANDS
+These options specify the particular operation to perform. Only one of them
+can be specified at any given time.
+.TP
+.BI "-L --dump "
+List connection tacking or expectation table
+.TP
+.BI "-G, --get "
+Search for and show a particular (matching) entry in the given table.
+.TP
+.BI "-D, --delete "
+Delete an entry from the given table.
+.TP
+.BI "-I, --create "
+Create a new entry from the given table.
+.TP
+.BI "-E, --event "
+Display a real-time event log.
+.TP
+.BI "-F, --flush "
+Flush the whole given table
+.SS PARAMETERS
+.TP
+.BI "-z, --zero "
+Atomically zero counters after reading them. This option is only valid in
+combination with the "-L, --dump" command options.
+.TP
+.BI "-e, --event-mask " "[ALL|NEW|UPDATES|DESTROY][,...]"
+Set the bitmask of events that are to be generated by the in-kernel ctnetlink
+event code. Using this parameter, you can reduce the event messages generated
+by the kernel to those types to those that you are actually interested in.
+.
+This option can only be used in conjunction with "-E, --event".
+.SS FILTER PARAMETERS
+.TP
+.BI "-s, --orig-src " IP_ADDRESS
+Match only entries whose source address in the original direction equals the one specified as argument.
+.TP
+.BI "-d, --orig-dst " IP_ADDRESS
+Match only entries whose destination address in the original direction equals the one specified as argument.
+.TP
+.BI "-r, --reply-src " IP_ADDRESS
+Match only entries whose source address in the reply direction equals the one specified as argument.
+.TP
+.BI "-q, --reply-dst " IP_ADDRESS
+Match only entries whose destination address in the reply direction equals the one specified as argument.
+.TP
+.BI "-p, --proto " "PROTO "
+Specify layer four (TCP, UDP, ...) protocol.
+.TP
+.BI "-f, --family " "PROTO"
+Specify layer three (ipv4, ipv6) protocol
+This option is only required in conjunction with "-L, --dump". If this option is not passed, the default layer 3 protocol will be IPv4.
+.TP
+.BI "-t, --timeout " "TIMEOUT"
+Specify the timeout.
+.TP
+.BI "-u, --status " "[ASSURED|SEEN_REPLY|UNSET|SRC_NAT|DST_NAT][,...]"
+Specify the conntrack status.
+.TP
+.BI "-i, --id " "ID"
+Specify the conntrack ID.
+.
+This option can only be used in conjunction with "-L, --dump" to display the conntrack IDs.
+.TP
+.BI "--tuple-src " IP_ADDRESS
+Specify the tuple source address of an expectation.
+.TP
+.BI "--tuple-dst " IP_ADDRESS
+Specify the tuple destination address of an expectation.
+.TP
+.BI "--mask-src " IP_ADDRESS
+Specify the source address mask of an expectation.
+.TP
+.BI "--mask-dst " IP_ADDRESS
+Specify the destination address mask of an expectation.
+.SH DIAGNOSTICS
+The exit code is 0 for correct function. Errors which appear to be caused by
+invalid command line parameters cause an exit code of 2. Any other errors
+cause an exit code of 1.
+.SH BUGS
+Bugs? What's this ;-)
+.SH SEE ALSO
+.BR iptables (8)
+.br
+See
+.BR "http://netfilter.org/" .
+.SH AUTHORS
+Jay Schulist, Patrick McHardy, Harald Welte and Pablo Neira wrote the kernel-level "ctnetlink" interface that is used by the conntrack tool.
+.PP
+Pablo Neira wrote the conntrack tool, Harald Welte added support for conntrack based accounting counters.
+.PP
+Man page written by Harald Welte <laforge@netfilter.org>.
diff --git a/cli/extensions/Makefile.am b/cli/extensions/Makefile.am
new file mode 100644
index 0000000..5366ee3
--- /dev/null
+++ b/cli/extensions/Makefile.am
@@ -0,0 +1,16 @@
+include $(top_srcdir)/Make_global.am
+
+AM_CFLAGS=-fPIC -Wall
+LIBS=
+
+pkglib_LTLIBRARIES = ct_proto_tcp.la ct_proto_udp.la \
+ ct_proto_icmp.la ct_proto_sctp.la
+
+ct_proto_tcp_la_SOURCES = libct_proto_tcp.c
+ct_proto_tcp_la_LDFLAGS = -module -avoid-version
+ct_proto_udp_la_SOURCES = libct_proto_udp.c
+ct_proto_udp_la_LDFLAGS = -module -avoid-version
+ct_proto_icmp_la_SOURCES = libct_proto_icmp.c
+ct_proto_icmp_la_LDFLAGS = -module -avoid-version
+ct_proto_sctp_la_SOURCES = libct_proto_sctp.c
+ct_proto_sctp_la_LDFLAGS = -module -avoid-version
diff --git a/cli/extensions/libct_proto_icmp.c b/cli/extensions/libct_proto_icmp.c
new file mode 100644
index 0000000..e7cb04d
--- /dev/null
+++ b/cli/extensions/libct_proto_icmp.c
@@ -0,0 +1,108 @@
+/*
+ * (C) 2005 by Pablo Neira Ayuso <pablo@eurodev.net>
+ * Harald Welte <laforge@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <stdio.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <netinet/in.h> /* For htons */
+#include <netinet/ip_icmp.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack_icmp.h>
+#include "conntrack.h"
+
+static struct option opts[] = {
+ {"icmp-type", 1, 0, '1'},
+ {"icmp-code", 1, 0, '2'},
+ {"icmp-id", 1, 0, '3'},
+ {0, 0, 0, 0}
+};
+
+static void help()
+{
+ fprintf(stdout, "--icmp-type icmp type\n");
+ fprintf(stdout, "--icmp-code icmp code\n");
+ fprintf(stdout, "--icmp-id icmp id\n");
+}
+
+/* Add 1; spaces filled with 0. */
+static u_int8_t invmap[]
+ = { [ICMP_ECHO] = ICMP_ECHOREPLY + 1,
+ [ICMP_ECHOREPLY] = ICMP_ECHO + 1,
+ [ICMP_TIMESTAMP] = ICMP_TIMESTAMPREPLY + 1,
+ [ICMP_TIMESTAMPREPLY] = ICMP_TIMESTAMP + 1,
+ [ICMP_INFO_REQUEST] = ICMP_INFO_REPLY + 1,
+ [ICMP_INFO_REPLY] = ICMP_INFO_REQUEST + 1,
+ [ICMP_ADDRESS] = ICMP_ADDRESSREPLY + 1,
+ [ICMP_ADDRESSREPLY] = ICMP_ADDRESS + 1};
+
+static int parse(char c, char *argv[],
+ struct nfct_tuple *orig,
+ struct nfct_tuple *reply,
+ struct nfct_tuple *exptuple,
+ struct nfct_tuple *mask,
+ union nfct_protoinfo *proto,
+ unsigned int *flags)
+{
+ switch(c) {
+ case '1':
+ if (optarg) {
+ orig->l4dst.icmp.type = atoi(optarg);
+ reply->l4dst.icmp.type =
+ invmap[orig->l4dst.icmp.type] - 1;
+ *flags |= ICMP_TYPE;
+ }
+ break;
+ case '2':
+ if (optarg) {
+ orig->l4dst.icmp.code = atoi(optarg);
+ reply->l4dst.icmp.code = 0;
+ *flags |= ICMP_CODE;
+ }
+ break;
+ case '3':
+ if (optarg) {
+ orig->l4src.icmp.id = htons(atoi(optarg));
+ reply->l4dst.icmp.id = 0;
+ *flags |= ICMP_ID;
+ }
+ break;
+ }
+ return 1;
+}
+
+static int final_check(unsigned int flags,
+ unsigned int command,
+ struct nfct_tuple *orig,
+ struct nfct_tuple *reply)
+{
+ if (!(flags & ICMP_TYPE))
+ return 0;
+ else if (!(flags & ICMP_CODE))
+ return 0;
+
+ return 1;
+}
+
+static struct ctproto_handler icmp = {
+ .name = "icmp",
+ .protonum = IPPROTO_ICMP,
+ .parse_opts = parse,
+ .final_check = final_check,
+ .help = help,
+ .opts = opts,
+ .version = VERSION,
+};
+
+static void __attribute__ ((constructor)) init(void);
+
+static void init(void)
+{
+ register_proto(&icmp);
+}
diff --git a/cli/extensions/libct_proto_icmp.man b/cli/extensions/libct_proto_icmp.man
new file mode 100644
index 0000000..3b860d0
--- /dev/null
+++ b/cli/extensions/libct_proto_icmp.man
@@ -0,0 +1,10 @@
+This module matches on ICMP-specific fields.
+.TP
+.BI "--icmp-type " "TYPE"
+ICMP Type. Has to be specified numerically.
+.TP
+.BI "--icmp-code " "CODE"
+ICMP Code. Has to be specified numerically.
+.TP
+.BI "--icmp-id " "ID"
+ICMP Id. Has to be specified numerically.
diff --git a/cli/extensions/libct_proto_sctp.c b/cli/extensions/libct_proto_sctp.c
new file mode 100644
index 0000000..1c8f0d1
--- /dev/null
+++ b/cli/extensions/libct_proto_sctp.c
@@ -0,0 +1,164 @@
+/*
+ * (C) 2005 by Harald Welte <laforge@netfilter.org>
+ * 2006 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <stdio.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h> /* For htons */
+#include "conntrack.h"
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack_sctp.h>
+
+static struct option opts[] = {
+ {"orig-port-src", 1, 0, '1'},
+ {"orig-port-dst", 1, 0, '2'},
+ {"reply-port-src", 1, 0, '3'},
+ {"reply-port-dst", 1, 0, '4'},
+ {"state", 1, 0, '5'},
+ {"tuple-port-src", 1, 0, '6'},
+ {"tuple-port-dst", 1, 0, '7'},
+ {0, 0, 0, 0}
+};
+
+static const char *states[] = {
+ "NONE",
+ "CLOSED",
+ "COOKIE_WAIT",
+ "COOKIE_ECHOED",
+ "ESTABLISHED",
+ "SHUTDOWN_SENT",
+ "SHUTDOWN_RECV",
+ "SHUTDOWN_ACK_SENT",
+};
+
+static void help()
+{
+ fprintf(stdout, "--orig-port-src original source port\n");
+ fprintf(stdout, "--orig-port-dst original destination port\n");
+ fprintf(stdout, "--reply-port-src reply source port\n");
+ fprintf(stdout, "--reply-port-dst reply destination port\n");
+ fprintf(stdout, "--state SCTP state, fe. ESTABLISHED\n");
+ fprintf(stdout, "--tuple-port-src expectation tuple src port\n");
+ fprintf(stdout, "--tuple-port-src expectation tuple dst port\n");
+}
+
+static int parse_options(char c, char *argv[],
+ struct nfct_tuple *orig,
+ struct nfct_tuple *reply,
+ struct nfct_tuple *exptuple,
+ struct nfct_tuple *mask,
+ union nfct_protoinfo *proto,
+ unsigned int *flags)
+{
+ switch(c) {
+ case '1':
+ if (optarg) {
+ orig->l4src.sctp.port = htons(atoi(optarg));
+ *flags |= SCTP_ORIG_SPORT;
+ }
+ break;
+ case '2':
+ if (optarg) {
+ orig->l4dst.sctp.port = htons(atoi(optarg));
+ *flags |= SCTP_ORIG_DPORT;
+ }
+ break;
+ case '3':
+ if (optarg) {
+ reply->l4src.sctp.port = htons(atoi(optarg));
+ *flags |= SCTP_REPL_SPORT;
+ }
+ break;
+ case '4':
+ if (optarg) {
+ reply->l4dst.sctp.port = htons(atoi(optarg));
+ *flags |= SCTP_REPL_DPORT;
+ }
+ break;
+ case '5':
+ if (optarg) {
+ int i;
+ for (i=0; i<10; i++) {
+ if (strcmp(optarg, states[i]) == 0) {
+ /* FIXME: Add state to
+ * nfct_protoinfo
+ proto->sctp.state = i; */
+ break;
+ }
+ }
+ if (i == 10) {
+ printf("doh?\n");
+ return 0;
+ }
+ *flags |= SCTP_STATE;
+ }
+ break;
+ case '6':
+ if (optarg) {
+ exptuple->l4src.sctp.port = htons(atoi(optarg));
+ *flags |= SCTP_EXPTUPLE_SPORT;
+ }
+ break;
+ case '7':
+ if (optarg) {
+ exptuple->l4dst.sctp.port = htons(atoi(optarg));
+ *flags |= SCTP_EXPTUPLE_DPORT;
+ }
+
+ }
+ return 1;
+}
+
+static int final_check(unsigned int flags,
+ unsigned int command,
+ struct nfct_tuple *orig,
+ struct nfct_tuple *reply)
+{
+ int ret = 0;
+
+ if ((flags & (SCTP_ORIG_SPORT|SCTP_ORIG_DPORT))
+ && !(flags & (SCTP_REPL_SPORT|SCTP_REPL_DPORT))) {
+ reply->l4src.sctp.port = orig->l4dst.sctp.port;
+ reply->l4dst.sctp.port = orig->l4src.sctp.port;
+ ret = 1;
+ } else if (!(flags & (SCTP_ORIG_SPORT|SCTP_ORIG_DPORT))
+ && (flags & (SCTP_REPL_SPORT|SCTP_REPL_DPORT))) {
+ orig->l4src.sctp.port = reply->l4dst.sctp.port;
+ orig->l4dst.sctp.port = reply->l4src.sctp.port;
+ ret = 1;
+ }
+ if ((flags & (SCTP_ORIG_SPORT|SCTP_ORIG_DPORT))
+ && ((flags & (SCTP_REPL_SPORT|SCTP_REPL_DPORT))))
+ ret = 1;
+
+ /* --state is missing and we are trying to create a conntrack */
+ if (ret && (command & CT_CREATE) && (!(flags & SCTP_STATE)))
+ ret = 0;
+
+ return ret;
+}
+
+static struct ctproto_handler sctp = {
+ .name = "sctp",
+ .protonum = IPPROTO_SCTP,
+ .parse_opts = parse_options,
+ .final_check = final_check,
+ .help = help,
+ .opts = opts,
+ .version = VERSION,
+};
+
+static void __attribute__ ((constructor)) init(void);
+
+static void init(void)
+{
+ register_proto(&sctp);
+}
diff --git a/cli/extensions/libct_proto_tcp.c b/cli/extensions/libct_proto_tcp.c
new file mode 100644
index 0000000..ee24206
--- /dev/null
+++ b/cli/extensions/libct_proto_tcp.c
@@ -0,0 +1,180 @@
+/*
+ * (C) 2005 by Pablo Neira Ayuso <pablo@eurodev.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <stdio.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h> /* For htons */
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack_tcp.h>
+
+#include "conntrack.h"
+
+static struct option opts[] = {
+ {"orig-port-src", 1, 0, '1'},
+ {"orig-port-dst", 1, 0, '2'},
+ {"reply-port-src", 1, 0, '3'},
+ {"reply-port-dst", 1, 0, '4'},
+ {"mask-port-src", 1, 0, '5'},
+ {"mask-port-dst", 1, 0, '6'},
+ {"state", 1, 0, '7'},
+ {"tuple-port-src", 1, 0, '8'},
+ {"tuple-port-dst", 1, 0, '9'},
+ {0, 0, 0, 0}
+};
+
+static const char *states[] = {
+ "NONE",
+ "SYN_SENT",
+ "SYN_RECV",
+ "ESTABLISHED",
+ "FIN_WAIT",
+ "CLOSE_WAIT",
+ "LAST_ACK",
+ "TIME_WAIT",
+ "CLOSE",
+ "LISTEN"
+};
+
+static void help()
+{
+ fprintf(stdout, "--orig-port-src original source port\n");
+ fprintf(stdout, "--orig-port-dst original destination port\n");
+ fprintf(stdout, "--reply-port-src reply source port\n");
+ fprintf(stdout, "--reply-port-dst reply destination port\n");
+ fprintf(stdout, "--mask-port-src mask source port\n");
+ fprintf(stdout, "--mask-port-dst mask destination port\n");
+ fprintf(stdout, "--tuple-port-src expectation tuple src port\n");
+ fprintf(stdout, "--tuple-port-src expectation tuple dst port\n");
+ fprintf(stdout, "--state TCP state, fe. ESTABLISHED\n");
+}
+
+static int parse_options(char c, char *argv[],
+ struct nfct_tuple *orig,
+ struct nfct_tuple *reply,
+ struct nfct_tuple *exptuple,
+ struct nfct_tuple *mask,
+ union nfct_protoinfo *proto,
+ unsigned int *flags)
+{
+ switch(c) {
+ case '1':
+ if (optarg) {
+ orig->l4src.tcp.port = htons(atoi(optarg));
+ *flags |= TCP_ORIG_SPORT;
+ }
+ break;
+ case '2':
+ if (optarg) {
+ orig->l4dst.tcp.port = htons(atoi(optarg));
+ *flags |= TCP_ORIG_DPORT;
+ }
+ break;
+ case '3':
+ if (optarg) {
+ reply->l4src.tcp.port = htons(atoi(optarg));
+ *flags |= TCP_REPL_SPORT;
+ }
+ break;
+ case '4':
+ if (optarg) {
+ reply->l4dst.tcp.port = htons(atoi(optarg));
+ *flags |= TCP_REPL_DPORT;
+ }
+ break;
+ case '5':
+ if (optarg) {
+ mask->l4src.tcp.port = htons(atoi(optarg));
+ *flags |= TCP_MASK_SPORT;
+ }
+ break;
+ case '6':
+ if (optarg) {
+ mask->l4dst.tcp.port = htons(atoi(optarg));
+ *flags |= TCP_MASK_DPORT;
+ }
+ break;
+ case '7':
+ if (optarg) {
+ int i;
+ for (i=0; i<10; i++) {
+ if (strcmp(optarg, states[i]) == 0) {
+ proto->tcp.state = i;
+ break;
+ }
+ }
+ if (i == 10) {
+ printf("doh?\n");
+ return 0;
+ }
+ *flags |= TCP_STATE;
+ }
+ break;
+ case '8':
+ if (optarg) {
+ exptuple->l4src.tcp.port = htons(atoi(optarg));
+ *flags |= TCP_EXPTUPLE_SPORT;
+ }
+ break;
+ case '9':
+ if (optarg) {
+ exptuple->l4dst.tcp.port = htons(atoi(optarg));
+ *flags |= TCP_EXPTUPLE_DPORT;
+ }
+ break;
+ }
+ return 1;
+}
+
+static int final_check(unsigned int flags,
+ unsigned int command,
+ struct nfct_tuple *orig,
+ struct nfct_tuple *reply)
+{
+ int ret = 0;
+
+ if ((flags & (TCP_ORIG_SPORT|TCP_ORIG_DPORT))
+ && !(flags & (TCP_REPL_SPORT|TCP_REPL_DPORT))) {
+ reply->l4src.tcp.port = orig->l4dst.tcp.port;
+ reply->l4dst.tcp.port = orig->l4src.tcp.port;
+ ret = 1;
+ } else if (!(flags & (TCP_ORIG_SPORT|TCP_ORIG_DPORT))
+ && (flags & (TCP_REPL_SPORT|TCP_REPL_DPORT))) {
+ orig->l4src.tcp.port = reply->l4dst.tcp.port;
+ orig->l4dst.tcp.port = reply->l4src.tcp.port;
+ ret = 1;
+ }
+ if ((flags & (TCP_ORIG_SPORT|TCP_ORIG_DPORT))
+ && ((flags & (TCP_REPL_SPORT|TCP_REPL_DPORT))))
+ ret = 1;
+
+ /* --state is missing and we are trying to create a conntrack */
+ if (ret && (command & CT_CREATE) && (!(flags & TCP_STATE)))
+ ret = 0;
+
+ return ret;
+}
+
+static struct ctproto_handler tcp = {
+ .name = "tcp",
+ .protonum = IPPROTO_TCP,
+ .parse_opts = parse_options,
+ .final_check = final_check,
+ .help = help,
+ .opts = opts,
+ .version = VERSION,
+};
+
+static void __attribute__ ((constructor)) init(void);
+
+static void init(void)
+{
+ register_proto(&tcp);
+}
diff --git a/cli/extensions/libct_proto_tcp.man b/cli/extensions/libct_proto_tcp.man
new file mode 100644
index 0000000..41783f8
--- /dev/null
+++ b/cli/extensions/libct_proto_tcp.man
@@ -0,0 +1,16 @@
+This module matches on TCP-specific fields.
+.TP
+.BI "--orig-port-src " "PORT"
+Source port in original direction
+.TP
+.BI "--orig-port-dst " "PORT"
+Destination port in original direction
+.TP
+.BI "--reply-port-src " "PORT"
+Source port in reply direction
+.TP
+.BI "--reply-port-dst " "PORT"
+Destination port in reply direction
+.TP
+.BI "--state " "[NONE|SYN_SENT|SYN_RECV|ESTABLISHED|FIN_WAIT|CLOSE_WAIT|LAST_ACK|TIME_WAIT|CLOSE|LISTEN]"
+TCP state
diff --git a/cli/extensions/libct_proto_udp.c b/cli/extensions/libct_proto_udp.c
new file mode 100644
index 0000000..48079e0
--- /dev/null
+++ b/cli/extensions/libct_proto_udp.c
@@ -0,0 +1,141 @@
+/*
+ * (C) 2005 by Pablo Neira Ayuso <pablo@eurodev.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <stdio.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <netinet/in.h> /* For htons */
+#include "conntrack.h"
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack_udp.h>
+
+static struct option opts[] = {
+ {"orig-port-src", 1, 0, '1'},
+ {"orig-port-dst", 1, 0, '2'},
+ {"reply-port-src", 1, 0, '3'},
+ {"reply-port-dst", 1, 0, '4'},
+ {"mask-port-src", 1, 0, '5'},
+ {"mask-port-dst", 1, 0, '6'},
+ {"tuple-port-src", 1, 0, '7'},
+ {"tuple-port-dst", 1, 0, '8'},
+ {0, 0, 0, 0}
+};
+
+static void help()
+{
+ fprintf(stdout, "--orig-port-src original source port\n");
+ fprintf(stdout, "--orig-port-dst original destination port\n");
+ fprintf(stdout, "--reply-port-src reply source port\n");
+ fprintf(stdout, "--reply-port-dst reply destination port\n");
+ fprintf(stdout, "--mask-port-src mask source port\n");
+ fprintf(stdout, "--mask-port-dst mask destination port\n");
+ fprintf(stdout, "--tuple-port-src expectation tuple src port\n");
+ fprintf(stdout, "--tuple-port-src expectation tuple dst port\n");
+}
+
+static int parse_options(char c, char *argv[],
+ struct nfct_tuple *orig,
+ struct nfct_tuple *reply,
+ struct nfct_tuple *exptuple,
+ struct nfct_tuple *mask,
+ union nfct_protoinfo *proto,
+ unsigned int *flags)
+{
+ switch(c) {
+ case '1':
+ if (optarg) {
+ orig->l4src.udp.port = htons(atoi(optarg));
+ *flags |= UDP_ORIG_SPORT;
+ }
+ break;
+ case '2':
+ if (optarg) {
+ orig->l4dst.udp.port = htons(atoi(optarg));
+ *flags |= UDP_ORIG_DPORT;
+ }
+ break;
+ case '3':
+ if (optarg) {
+ reply->l4src.udp.port = htons(atoi(optarg));
+ *flags |= UDP_REPL_SPORT;
+ }
+ break;
+ case '4':
+ if (optarg) {
+ reply->l4dst.udp.port = htons(atoi(optarg));
+ *flags |= UDP_REPL_DPORT;
+ }
+ break;
+ case '5':
+ if (optarg) {
+ mask->l4src.udp.port = htons(atoi(optarg));
+ *flags |= UDP_MASK_SPORT;
+ }
+ break;
+ case '6':
+ if (optarg) {
+ mask->l4dst.udp.port = htons(atoi(optarg));
+ *flags |= UDP_MASK_DPORT;
+ }
+ break;
+ case '7':
+ if (optarg) {
+ exptuple->l4src.udp.port = htons(atoi(optarg));
+ *flags |= UDP_EXPTUPLE_SPORT;
+ }
+ break;
+ case '8':
+ if (optarg) {
+ exptuple->l4dst.udp.port = htons(atoi(optarg));
+ *flags |= UDP_EXPTUPLE_DPORT;
+ }
+
+ }
+ return 1;
+}
+
+static int final_check(unsigned int flags,
+ unsigned int command,
+ struct nfct_tuple *orig,
+ struct nfct_tuple *reply)
+{
+ if ((flags & (UDP_ORIG_SPORT|UDP_ORIG_DPORT))
+ && !(flags & (UDP_REPL_SPORT|UDP_REPL_DPORT))) {
+ reply->l4src.udp.port = orig->l4dst.udp.port;
+ reply->l4dst.udp.port = orig->l4src.udp.port;
+ return 1;
+ } else if (!(flags & (UDP_ORIG_SPORT|UDP_ORIG_DPORT))
+ && (flags & (UDP_REPL_SPORT|UDP_REPL_DPORT))) {
+ orig->l4src.udp.port = reply->l4dst.udp.port;
+ orig->l4dst.udp.port = reply->l4src.udp.port;
+ return 1;
+ }
+ if ((flags & (UDP_ORIG_SPORT|UDP_ORIG_DPORT))
+ && ((flags & (UDP_REPL_SPORT|UDP_REPL_DPORT))))
+ return 1;
+
+ return 0;
+}
+
+static struct ctproto_handler udp = {
+ .name = "udp",
+ .protonum = IPPROTO_UDP,
+ .parse_opts = parse_options,
+ .final_check = final_check,
+ .help = help,
+ .opts = opts,
+ .version = VERSION,
+};
+
+static void __attribute__ ((constructor)) init(void);
+
+static void init(void)
+{
+ register_proto(&udp);
+}
diff --git a/cli/extensions/libct_proto_udp.man b/cli/extensions/libct_proto_udp.man
new file mode 100644
index 0000000..c67fedf
--- /dev/null
+++ b/cli/extensions/libct_proto_udp.man
@@ -0,0 +1,13 @@
+This module matches on UDP-specific fields.
+.TP
+.BI "--orig-port-src " "PORT"
+Source port in original direction
+.TP
+.BI "--orig-port-dst " "PORT"
+Destination port in original direction
+.TP
+.BI "--reply-port-src " "PORT"
+Source port in reply direction
+.TP
+.BI "--reply-port-dst " "PORT"
+Destination port in reply direction
diff --git a/cli/include/Makefile.am b/cli/include/Makefile.am
new file mode 100644
index 0000000..ef7ce45
--- /dev/null
+++ b/cli/include/Makefile.am
@@ -0,0 +1,2 @@
+
+noinst_HEADERS = conntrack.h linux_list.h
diff --git a/cli/include/conntrack.h b/cli/include/conntrack.h
new file mode 100644
index 0000000..fb3b9b6
--- /dev/null
+++ b/cli/include/conntrack.h
@@ -0,0 +1,160 @@
+#ifndef _CONNTRACK_H
+#define _CONNTRACK_H
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+#include "linux_list.h"
+#include <getopt.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+
+#define PROGNAME "conntrack"
+
+#include <netinet/in.h>
+#ifndef IPPROTO_SCTP
+#define IPPROTO_SCTP 132
+#endif
+
+enum action {
+ CT_NONE = 0,
+
+ CT_LIST_BIT = 0,
+ CT_LIST = (1 << CT_LIST_BIT),
+
+ CT_CREATE_BIT = 1,
+ CT_CREATE = (1 << CT_CREATE_BIT),
+
+ CT_UPDATE_BIT = 2,
+ CT_UPDATE = (1 << CT_UPDATE_BIT),
+
+ CT_DELETE_BIT = 3,
+ CT_DELETE = (1 << CT_DELETE_BIT),
+
+ CT_GET_BIT = 4,
+ CT_GET = (1 << CT_GET_BIT),
+
+ CT_FLUSH_BIT = 5,
+ CT_FLUSH = (1 << CT_FLUSH_BIT),
+
+ CT_EVENT_BIT = 6,
+ CT_EVENT = (1 << CT_EVENT_BIT),
+
+ CT_VERSION_BIT = 7,
+ CT_VERSION = (1 << CT_VERSION_BIT),
+
+ CT_HELP_BIT = 8,
+ CT_HELP = (1 << CT_HELP_BIT),
+
+ EXP_LIST_BIT = 9,
+ EXP_LIST = (1 << EXP_LIST_BIT),
+
+ EXP_CREATE_BIT = 10,
+ EXP_CREATE = (1 << EXP_CREATE_BIT),
+
+ EXP_DELETE_BIT = 11,
+ EXP_DELETE = (1 << EXP_DELETE_BIT),
+
+ EXP_GET_BIT = 12,
+ EXP_GET = (1 << EXP_GET_BIT),
+
+ EXP_FLUSH_BIT = 13,
+ EXP_FLUSH = (1 << EXP_FLUSH_BIT),
+
+ EXP_EVENT_BIT = 14,
+ EXP_EVENT = (1 << EXP_EVENT_BIT),
+};
+#define NUMBER_OF_CMD 15
+
+enum options {
+ CT_OPT_ORIG_SRC_BIT = 0,
+ CT_OPT_ORIG_SRC = (1 << CT_OPT_ORIG_SRC_BIT),
+
+ CT_OPT_ORIG_DST_BIT = 1,
+ CT_OPT_ORIG_DST = (1 << CT_OPT_ORIG_DST_BIT),
+
+ CT_OPT_ORIG = (CT_OPT_ORIG_SRC | CT_OPT_ORIG_DST),
+
+ CT_OPT_REPL_SRC_BIT = 2,
+ CT_OPT_REPL_SRC = (1 << CT_OPT_REPL_SRC_BIT),
+
+ CT_OPT_REPL_DST_BIT = 3,
+ CT_OPT_REPL_DST = (1 << CT_OPT_REPL_DST_BIT),
+
+ CT_OPT_REPL = (CT_OPT_REPL_SRC | CT_OPT_REPL_DST),
+
+ CT_OPT_PROTO_BIT = 4,
+ CT_OPT_PROTO = (1 << CT_OPT_PROTO_BIT),
+
+ CT_OPT_TIMEOUT_BIT = 5,
+ CT_OPT_TIMEOUT = (1 << CT_OPT_TIMEOUT_BIT),
+
+ CT_OPT_STATUS_BIT = 6,
+ CT_OPT_STATUS = (1 << CT_OPT_STATUS_BIT),
+
+ CT_OPT_ZERO_BIT = 7,
+ CT_OPT_ZERO = (1 << CT_OPT_ZERO_BIT),
+
+ CT_OPT_EVENT_MASK_BIT = 8,
+ CT_OPT_EVENT_MASK = (1 << CT_OPT_EVENT_MASK_BIT),
+
+ CT_OPT_EXP_SRC_BIT = 9,
+ CT_OPT_EXP_SRC = (1 << CT_OPT_EXP_SRC_BIT),
+
+ CT_OPT_EXP_DST_BIT = 10,
+ CT_OPT_EXP_DST = (1 << CT_OPT_EXP_DST_BIT),
+
+ CT_OPT_MASK_SRC_BIT = 11,
+ CT_OPT_MASK_SRC = (1 << CT_OPT_MASK_SRC_BIT),
+
+ CT_OPT_MASK_DST_BIT = 12,
+ CT_OPT_MASK_DST = (1 << CT_OPT_MASK_DST_BIT),
+
+ CT_OPT_NATRANGE_BIT = 13,
+ CT_OPT_NATRANGE = (1 << CT_OPT_NATRANGE_BIT),
+
+ CT_OPT_MARK_BIT = 14,
+ CT_OPT_MARK = (1 << CT_OPT_MARK_BIT),
+
+ CT_OPT_ID_BIT = 15,
+ CT_OPT_ID = (1 << CT_OPT_ID_BIT),
+
+ CT_OPT_FAMILY_BIT = 16,
+ CT_OPT_FAMILY = (1 << CT_OPT_FAMILY_BIT),
+
+ CT_OPT_MAX_BIT = CT_OPT_FAMILY_BIT
+};
+#define NUMBER_OF_OPT CT_OPT_MAX_BIT+1
+
+struct ctproto_handler {
+ struct list_head head;
+
+ char *name;
+ u_int16_t protonum;
+ char *version;
+
+ enum ctattr_protoinfo protoinfo_attr;
+
+ int (*parse_opts)(char c, char *argv[],
+ struct nfct_tuple *orig,
+ struct nfct_tuple *reply,
+ struct nfct_tuple *exptuple,
+ struct nfct_tuple *mask,
+ union nfct_protoinfo *proto,
+ unsigned int *flags);
+
+ int (*final_check)(unsigned int flags,
+ unsigned int command,
+ struct nfct_tuple *orig,
+ struct nfct_tuple *reply);
+
+ void (*help)();
+
+ struct option *opts;
+
+ unsigned int option_offset;
+};
+
+extern void register_proto(struct ctproto_handler *h);
+
+#endif
diff --git a/cli/include/linux_list.h b/cli/include/linux_list.h
new file mode 100644
index 0000000..57b56d7
--- /dev/null
+++ b/cli/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/cli/src/Makefile.am b/cli/src/Makefile.am
new file mode 100644
index 0000000..83cad99
--- /dev/null
+++ b/cli/src/Makefile.am
@@ -0,0 +1,7 @@
+include $(top_srcdir)/Make_global.am
+LIBS = @CONNTRACK_LIBS@
+
+sbin_PROGRAMS = conntrack
+conntrack_SOURCES = conntrack.c
+conntrack_LDFLAGS = -rdynamic
+
diff --git a/cli/src/conntrack.c b/cli/src/conntrack.c
new file mode 100644
index 0000000..30fbf69
--- /dev/null
+++ b/cli/src/conntrack.c
@@ -0,0 +1,1131 @@
+/*
+ * (C) 2005 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This 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.
+ *
+ * Note:
+ * Yes, portions of this code has been stolen from iptables ;)
+ * Special thanks to the the Netfilter Core Team.
+ * Thanks to Javier de Miguel Rodriguez <jmiguel at talika.eii.us.es>
+ * for introducing me to advanced firewalling stuff.
+ *
+ * --pablo 13/04/2005
+ *
+ * 2005-04-16 Harald Welte <laforge@netfilter.org>:
+ * Add support for conntrack accounting and conntrack mark
+ * 2005-06-23 Harald Welte <laforge@netfilter.org>:
+ * Add support for expect creation
+ * 2005-09-24 Harald Welte <laforge@netfilter.org>:
+ * Remove remaints of "-A"
+ *
+ */
+#include <stdio.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <string.h>
+#include "linux_list.h"
+#include "conntrack.h"
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack_ipv4.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack_ipv6.h>
+
+static const char cmdflags[NUMBER_OF_CMD]
+= {'L','I','U','D','G','F','E','V','h','L','I','D','G','F','E'};
+
+static const char cmd_need_param[NUMBER_OF_CMD]
+= { 2, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2 };
+
+static const char optflags[NUMBER_OF_OPT]
+= {'s','d','r','q','p','t','u','z','e','[',']','{','}','a','m','i','f'};
+
+static struct option original_opts[] = {
+ {"dump", 2, 0, 'L'},
+ {"create", 1, 0, 'I'},
+ {"delete", 1, 0, 'D'},
+ {"update", 1, 0, 'U'},
+ {"get", 1, 0, 'G'},
+ {"flush", 1, 0, 'F'},
+ {"event", 1, 0, 'E'},
+ {"version", 0, 0, 'V'},
+ {"help", 0, 0, 'h'},
+ {"orig-src", 1, 0, 's'},
+ {"orig-dst", 1, 0, 'd'},
+ {"reply-src", 1, 0, 'r'},
+ {"reply-dst", 1, 0, 'q'},
+ {"protonum", 1, 0, 'p'},
+ {"timeout", 1, 0, 't'},
+ {"status", 1, 0, 'u'},
+ {"zero", 0, 0, 'z'},
+ {"event-mask", 1, 0, 'e'},
+ {"tuple-src", 1, 0, '['},
+ {"tuple-dst", 1, 0, ']'},
+ {"mask-src", 1, 0, '{'},
+ {"mask-dst", 1, 0, '}'},
+ {"nat-range", 1, 0, 'a'},
+ {"mark", 1, 0, 'm'},
+ {"id", 2, 0, 'i'},
+ {"family", 1, 0, 'f'},
+ {0, 0, 0, 0}
+};
+
+#define OPTION_OFFSET 256
+
+static struct nfct_handle *cth;
+static struct option *opts = original_opts;
+static unsigned int global_option_offset = 0;
+
+/* Table of legal combinations of commands and options. If any of the
+ * given commands make an option legal, that option is legal (applies to
+ * CMD_LIST and CMD_ZERO only).
+ * Key:
+ * 0 illegal
+ * 1 compulsory
+ * 2 optional
+ */
+
+static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
+/* Well, it's better than "Re: Linux vs FreeBSD" */
+{
+ /* s d r q p t u z e x y k l a m i f*/
+/*CT_LIST*/ {2,2,2,2,2,0,0,2,0,0,0,0,0,0,2,2,2},
+/*CT_CREATE*/ {2,2,2,2,1,1,1,0,0,0,0,0,0,2,2,0,0},
+/*CT_UPDATE*/ {2,2,2,2,1,2,2,0,0,0,0,0,0,0,2,2,0},
+/*CT_DELETE*/ {2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,2,0},
+/*CT_GET*/ {2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,2,0},
+/*CT_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*CT_EVENT*/ {2,2,2,2,2,0,0,0,2,0,0,0,0,0,2,0,0},
+/*VERSION*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*HELP*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*EXP_LIST*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2},
+/*EXP_CREATE*/{1,1,2,2,1,1,2,0,0,1,1,1,1,0,0,0,0},
+/*EXP_DELETE*/{1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0},
+/*EXP_GET*/ {1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0},
+/*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*EXP_EVENT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+};
+
+static char *lib_dir = CONNTRACK_LIB_DIR;
+
+static LIST_HEAD(proto_list);
+
+void register_proto(struct ctproto_handler *h)
+{
+ if (strcmp(h->version, VERSION) != 0) {
+ fprintf(stderr, "plugin `%s': version %s (I'm %s)\n",
+ h->name, h->version, VERSION);
+ exit(1);
+ }
+ list_add(&h->head, &proto_list);
+}
+
+static struct ctproto_handler *findproto(char *name)
+{
+ struct list_head *i;
+ struct ctproto_handler *cur = NULL, *handler = NULL;
+
+ if (!name)
+ return handler;
+
+ lib_dir = getenv("CONNTRACK_LIB_DIR");
+ if (!lib_dir)
+ lib_dir = CONNTRACK_LIB_DIR;
+
+ list_for_each(i, &proto_list) {
+ cur = (struct ctproto_handler *) i;
+ if (strcmp(cur->name, name) == 0) {
+ handler = cur;
+ break;
+ }
+ }
+
+ if (!handler) {
+ char path[sizeof("ct_proto_.so")
+ + strlen(name) + strlen(lib_dir)];
+ sprintf(path, "%s/ct_proto_%s.so", lib_dir, name);
+ if (dlopen(path, RTLD_NOW))
+ handler = findproto(name);
+ else
+ fprintf(stderr, "%s\n", dlerror());
+ }
+
+ return handler;
+}
+
+enum exittype {
+ OTHER_PROBLEM = 1,
+ PARAMETER_PROBLEM,
+ VERSION_PROBLEM
+};
+
+void extension_help(struct ctproto_handler *h)
+{
+ fprintf(stdout, "\n");
+ fprintf(stdout, "Proto `%s' help:\n", h->name);
+ h->help();
+}
+
+void
+exit_tryhelp(int status)
+{
+ fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
+ PROGNAME, PROGNAME);
+ exit(status);
+}
+
+static void
+exit_error(enum exittype status, char *msg, ...)
+{
+ va_list args;
+
+ /* On error paths, make sure that we don't leak the memory
+ * reserved during options merging */
+ if (opts != original_opts) {
+ free(opts);
+ opts = original_opts;
+ global_option_offset = 0;
+ }
+ va_start(args, msg);
+ fprintf(stderr,"%s v%s: ", PROGNAME, VERSION);
+ vfprintf(stderr, msg, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ if (status == PARAMETER_PROBLEM)
+ exit_tryhelp(status);
+ exit(status);
+}
+
+static void
+generic_cmd_check(int command, int options)
+{
+ int i;
+
+ for (i = 0; i < NUMBER_OF_CMD; i++) {
+ if (!(command & (1<<i)))
+ continue;
+
+ if (cmd_need_param[i] == 0 && !options)
+ exit_error(PARAMETER_PROBLEM,
+ "You need to supply parameters to `-%c'\n",
+ cmdflags[i]);
+ }
+}
+
+static void
+generic_opt_check(int command, int options)
+{
+ int i, j, legal = 0;
+
+ /* Check that commands are valid with options. Complicated by the
+ * fact that if an option is legal with *any* command given, it is
+ * legal overall (ie. -z and -l).
+ */
+ for (i = 0; i < NUMBER_OF_OPT; i++) {
+ legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */
+
+ for (j = 0; j < NUMBER_OF_CMD; j++) {
+ if (!(command & (1<<j)))
+ continue;
+
+ if (!(options & (1<<i))) {
+ if (commands_v_options[j][i] == 1)
+ exit_error(PARAMETER_PROBLEM,
+ "You need to supply the "
+ "`-%c' option for this "
+ "command\n", optflags[i]);
+ } else {
+ if (commands_v_options[j][i] != 0)
+ legal = 1;
+ else if (legal == 0)
+ legal = -1;
+ }
+ }
+ if (legal == -1)
+ exit_error(PARAMETER_PROBLEM, "Illegal option `-%c' "
+ "with this command\n", optflags[i]);
+ }
+}
+
+static struct option *
+merge_options(struct option *oldopts, const struct option *newopts,
+ unsigned int *option_offset)
+{
+ unsigned int num_old, num_new, i;
+ struct option *merge;
+
+ for (num_old = 0; oldopts[num_old].name; num_old++);
+ for (num_new = 0; newopts[num_new].name; num_new++);
+
+ global_option_offset += OPTION_OFFSET;
+ *option_offset = global_option_offset;
+
+ merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
+ memcpy(merge, oldopts, num_old * sizeof(struct option));
+ for (i = 0; i < num_new; i++) {
+ merge[num_old + i] = newopts[i];
+ merge[num_old + i].val += *option_offset;
+ }
+ memset(merge + num_old + num_new, 0, sizeof(struct option));
+
+ return merge;
+}
+
+/* From linux/errno.h */
+#define ENOTSUPP 524 /* Operation is not supported */
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *
+err2str(int err, enum action command)
+{
+ unsigned int i;
+ struct table_struct {
+ enum action act;
+ int err;
+ const char *message;
+ } table [] =
+ { { CT_LIST, -ENOTSUPP, "function not implemented" },
+ { 0xFFFF, -EINVAL, "invalid parameters" },
+ { CT_CREATE, -EEXIST, "Such conntrack exists, try -U to update" },
+ { CT_CREATE|CT_GET|CT_DELETE, -ENOENT,
+ "such conntrack doesn't exist" },
+ { CT_CREATE|CT_GET, -ENOMEM, "not enough memory" },
+ { CT_GET, -EAFNOSUPPORT, "protocol not supported" },
+ { CT_CREATE, -ETIME, "conntrack has expired" },
+ { EXP_CREATE, -ENOENT, "master conntrack not found" },
+ { EXP_CREATE, -EINVAL, "invalid parameters" },
+ { ~0UL, -EPERM, "sorry, you must be root or get "
+ "CAP_NET_ADMIN capability to do this"}
+ };
+
+ for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
+ if ((table[i].act & command) && table[i].err == err)
+ return table[i].message;
+ }
+
+ return strerror(err);
+}
+
+#define PARSE_STATUS 0
+#define PARSE_EVENT 1
+#define PARSE_MAX 2
+
+static struct parse_parameter {
+ char *parameter[6];
+ size_t size;
+ unsigned int value[6];
+} parse_array[PARSE_MAX] = {
+ { {"ASSURED", "SEEN_REPLY", "UNSET", "SRC_NAT", "DST_NAT","FIXED_TIMEOUT"}, 6,
+ { IPS_ASSURED, IPS_SEEN_REPLY, 0,
+ IPS_SRC_NAT_DONE, IPS_DST_NAT_DONE, IPS_FIXED_TIMEOUT} },
+ { {"ALL", "NEW", "UPDATES", "DESTROY"}, 4,
+ {~0U, NF_NETLINK_CONNTRACK_NEW, NF_NETLINK_CONNTRACK_UPDATE,
+ NF_NETLINK_CONNTRACK_DESTROY} },
+};
+
+static int
+do_parse_parameter(const char *str, size_t strlen, unsigned int *value,
+ int parse_type)
+{
+ int i, ret = 0;
+ struct parse_parameter *p = &parse_array[parse_type];
+
+ for (i = 0; i < p->size; i++)
+ if (strncasecmp(str, p->parameter[i], strlen) == 0) {
+ *value |= p->value[i];
+ ret = 1;
+ break;
+ }
+
+ return ret;
+}
+
+static void
+parse_parameter(const char *arg, unsigned int *status, int parse_type)
+{
+ const char *comma;
+
+ while ((comma = strchr(arg, ',')) != NULL) {
+ if (comma == arg
+ || !do_parse_parameter(arg, comma-arg, status, parse_type))
+ exit_error(PARAMETER_PROBLEM,"Bad parameter `%s'", arg);
+ arg = comma+1;
+ }
+
+ if (strlen(arg) == 0
+ || !do_parse_parameter(arg, strlen(arg), status, parse_type))
+ exit_error(PARAMETER_PROBLEM, "Bad parameter `%s'", arg);
+}
+
+static void
+add_command(unsigned int *cmd, const int newcmd, const int othercmds)
+{
+ if (*cmd & (~othercmds))
+ exit_error(PARAMETER_PROBLEM, "Invalid commands combination\n");
+ *cmd |= newcmd;
+}
+
+unsigned int check_type(int argc, char *argv[])
+{
+ char *table = NULL;
+
+ /* Nasty bug or feature in getopt_long ?
+ * It seems that it behaves badly with optional arguments.
+ * Fortunately, I just stole the fix from iptables ;) */
+ if (optarg)
+ return 0;
+ else if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ table = argv[optind++];
+
+ if (!table)
+ return 0;
+
+ if (strncmp("expect", table, 6) == 0)
+ return 1;
+ else if (strncmp("conntrack", table, 9) == 0)
+ return 0;
+ else
+ exit_error(PARAMETER_PROBLEM, "unknown type `%s'\n", table);
+
+ return 0;
+}
+
+static void set_family(int *family, int new)
+{
+ if (*family == AF_UNSPEC)
+ *family = new;
+ else if (*family != new)
+ exit_error(PARAMETER_PROBLEM, "mismatched address family\n");
+}
+
+struct addr_parse {
+ struct in_addr addr;
+ struct in6_addr addr6;
+ unsigned int family;
+};
+
+int __parse_inetaddr(const char *cp, struct addr_parse *parse)
+{
+ if (inet_aton(cp, &parse->addr))
+ return AF_INET;
+#ifdef HAVE_INET_PTON_IPV6
+ else if (inet_pton(AF_INET6, cp, &parse->addr6) > 0)
+ return AF_INET6;
+#endif
+
+ exit_error(PARAMETER_PROBLEM, "Invalid IP address `%s'.", cp);
+}
+
+int parse_inetaddr(const char *cp, union nfct_address *address)
+{
+ struct addr_parse parse;
+ int ret;
+
+ if ((ret = __parse_inetaddr(cp, &parse)) == AF_INET)
+ address->v4 = parse.addr.s_addr;
+ else if (ret == AF_INET6)
+ memcpy(address->v6, &parse.addr6, sizeof(parse.addr6));
+
+ return ret;
+}
+
+/* Shamelessly stolen from libipt_DNAT ;). Ranges expected in network order. */
+static void
+nat_parse(char *arg, int portok, struct nfct_nat *range)
+{
+ char *colon, *dash, *error;
+ struct addr_parse parse;
+
+ memset(range, 0, sizeof(range));
+ colon = strchr(arg, ':');
+
+ if (colon) {
+ int port;
+
+ if (!portok)
+ exit_error(PARAMETER_PROBLEM,
+ "Need TCP or UDP with port specification");
+
+ port = atoi(colon+1);
+ if (port == 0 || port > 65535)
+ exit_error(PARAMETER_PROBLEM,
+ "Port `%s' not valid\n", colon+1);
+
+ error = strchr(colon+1, ':');
+ if (error)
+ exit_error(PARAMETER_PROBLEM,
+ "Invalid port:port syntax - use dash\n");
+
+ dash = strchr(colon, '-');
+ if (!dash) {
+ range->l4min.tcp.port
+ = range->l4max.tcp.port
+ = htons(port);
+ } else {
+ int maxport;
+
+ maxport = atoi(dash + 1);
+ if (maxport == 0 || maxport > 65535)
+ exit_error(PARAMETER_PROBLEM,
+ "Port `%s' not valid\n", dash+1);
+ if (maxport < port)
+ /* People are stupid. */
+ exit_error(PARAMETER_PROBLEM,
+ "Port range `%s' funky\n", colon+1);
+ range->l4min.tcp.port = htons(port);
+ range->l4max.tcp.port = htons(maxport);
+ }
+ /* Starts with a colon? No IP info... */
+ if (colon == arg)
+ return;
+ *colon = '\0';
+ }
+
+ dash = strchr(arg, '-');
+ if (colon && dash && dash > colon)
+ dash = NULL;
+
+ if (dash)
+ *dash = '\0';
+
+ if (__parse_inetaddr(arg, &parse) != AF_INET)
+ return;
+
+ range->min_ip = parse.addr.s_addr;
+ if (dash) {
+ if (__parse_inetaddr(dash+1, &parse) != AF_INET)
+ return;
+ range->max_ip = parse.addr.s_addr;
+ } else
+ range->max_ip = parse.addr.s_addr;
+}
+
+static void event_sighandler(int s)
+{
+ fprintf(stdout, "Now closing conntrack event dumping...\n");
+ nfct_close(cth);
+ exit(0);
+}
+
+static const char usage_commands[] =
+ "Commands:\n"
+ " -L [table] [options]\t\tList conntrack or expectation table\n"
+ " -G [table] parameters\t\tGet conntrack or expectation\n"
+ " -D [table] parameters\t\tDelete conntrack or expectation\n"
+ " -I [table] parameters\t\tCreate a conntrack or expectation\n"
+ " -U [table] parameters\t\tUpdate a conntrack\n"
+ " -E [table] [options]\t\tShow events\n"
+ " -F [table]\t\t\tFlush table\n";
+
+static const char usage_tables[] =
+ "Tables: conntrack, expect\n";
+
+static const char usage_conntrack_parameters[] =
+ "Conntrack parameters and options:\n"
+ " -a, --nat-range min_ip[-max_ip]\tNAT ip range\n"
+ " -m, --mark mark\t\t\tSet mark\n"
+ " -e, --event-mask eventmask\t\tEvent mask, eg. NEW,DESTROY\n"
+ " -z, --zero \t\t\t\tZero counters while listing\n"
+ ;
+
+static const char usage_expectation_parameters[] =
+ "Expectation parameters and options:\n"
+ " --tuple-src ip\tSource address in expect tuple\n"
+ " --tuple-dst ip\tDestination address in expect tuple\n"
+ " --mask-src ip\t\tSource mask address\n"
+ " --mask-dst ip\t\tDestination mask address\n";
+
+static const char usage_parameters[] =
+ "Common parameters and options:\n"
+ " -s, --orig-src ip\t\tSource address from original direction\n"
+ " -d, --orig-dst ip\t\tDestination address from original direction\n"
+ " -r, --reply-src ip\t\tSource addres from reply direction\n"
+ " -q, --reply-dst ip\t\tDestination address from reply direction\n"
+ " -p, --protonum proto\t\tLayer 4 Protocol, eg. 'tcp'\n"
+ " -f, --family proto\t\tLayer 3 Protocol, eg. 'ipv6'\n"
+ " -t, --timeout timeout\t\tSet timeout\n"
+ " -u, --status status\t\tSet status, eg. ASSURED\n"
+ " -i, --id [id]\t\t\tShow or set conntrack ID\n"
+ ;
+
+
+void usage(char *prog) {
+ fprintf(stdout, "Tool to manipulate conntrack and expectations. Version %s\n", VERSION);
+ fprintf(stdout, "Usage: %s [commands] [options]\n", prog);
+
+ fprintf(stdout, "\n%s", usage_commands);
+ fprintf(stdout, "\n%s", usage_tables);
+ fprintf(stdout, "\n%s", usage_conntrack_parameters);
+ fprintf(stdout, "\n%s", usage_expectation_parameters);
+ fprintf(stdout, "\n%s", usage_parameters);
+}
+
+#define CT_COMPARISON (CT_OPT_PROTO | CT_OPT_ORIG | CT_OPT_REPL | CT_OPT_MARK)
+
+static struct nfct_tuple orig, reply, mask;
+static struct nfct_tuple exptuple;
+static struct ctproto_handler *h;
+static union nfct_protoinfo proto;
+static struct nfct_nat range;
+static struct nfct_conntrack *ct;
+static struct nfct_expect *exp;
+static unsigned long timeout;
+static unsigned int status;
+static unsigned int mark;
+static unsigned int id = NFCT_ANY_ID;
+static struct nfct_conntrack_compare cmp;
+
+int main(int argc, char *argv[])
+{
+ int c;
+ unsigned int command = 0, options = 0;
+ unsigned int type = 0, event_mask = 0;
+ unsigned int l3flags = 0, l4flags = 0, metaflags = 0;
+ int res = 0;
+ int family = AF_UNSPEC;
+ struct nfct_conntrack_compare *pcmp;
+
+ while ((c = getopt_long(argc, argv,
+ "L::I::U::D::G::E::F::hVs:d:r:q:p:t:u:e:a:z[:]:{:}:m:i::f:",
+ opts, NULL)) != -1) {
+ switch(c) {
+ case 'L':
+ type = check_type(argc, argv);
+ if (type == 0)
+ add_command(&command, CT_LIST, CT_NONE);
+ else if (type == 1)
+ add_command(&command, EXP_LIST, CT_NONE);
+ break;
+ case 'I':
+ type = check_type(argc, argv);
+ if (type == 0)
+ add_command(&command, CT_CREATE, CT_NONE);
+ else if (type == 1)
+ add_command(&command, EXP_CREATE, CT_NONE);
+ break;
+ case 'U':
+ type = check_type(argc, argv);
+ if (type == 0)
+ add_command(&command, CT_UPDATE, CT_NONE);
+ else
+ exit_error(PARAMETER_PROBLEM, "Can't update "
+ "expectations");
+ break;
+ case 'D':
+ type = check_type(argc, argv);
+ if (type == 0)
+ add_command(&command, CT_DELETE, CT_NONE);
+ else if (type == 1)
+ add_command(&command, EXP_DELETE, CT_NONE);
+ break;
+ case 'G':
+ type = check_type(argc, argv);
+ if (type == 0)
+ add_command(&command, CT_GET, CT_NONE);
+ else if (type == 1)
+ add_command(&command, EXP_GET, CT_NONE);
+ break;
+ case 'F':
+ type = check_type(argc, argv);
+ if (type == 0)
+ add_command(&command, CT_FLUSH, CT_NONE);
+ else if (type == 1)
+ add_command(&command, EXP_FLUSH, CT_NONE);
+ break;
+ case 'E':
+ type = check_type(argc, argv);
+ if (type == 0)
+ add_command(&command, CT_EVENT, CT_NONE);
+ else if (type == 1)
+ add_command(&command, EXP_EVENT, CT_NONE);
+ break;
+ case 'V':
+ add_command(&command, CT_VERSION, CT_NONE);
+ break;
+ case 'h':
+ add_command(&command, CT_HELP, CT_NONE);
+ break;
+ case 's':
+ options |= CT_OPT_ORIG_SRC;
+ if (optarg) {
+ orig.l3protonum =
+ parse_inetaddr(optarg, &orig.src);
+ set_family(&family, orig.l3protonum);
+ if (orig.l3protonum == AF_INET)
+ l3flags |= IPV4_ORIG_SRC;
+ else if (orig.l3protonum == AF_INET6)
+ l3flags |= IPV6_ORIG_SRC;
+ }
+ break;
+ case 'd':
+ options |= CT_OPT_ORIG_DST;
+ if (optarg) {
+ orig.l3protonum =
+ parse_inetaddr(optarg, &orig.dst);
+ set_family(&family, orig.l3protonum);
+ if (orig.l3protonum == AF_INET)
+ l3flags |= IPV4_ORIG_DST;
+ else if (orig.l3protonum == AF_INET6)
+ l3flags |= IPV6_ORIG_DST;
+ }
+ break;
+ case 'r':
+ options |= CT_OPT_REPL_SRC;
+ if (optarg) {
+ reply.l3protonum =
+ parse_inetaddr(optarg, &reply.src);
+ set_family(&family, reply.l3protonum);
+ if (orig.l3protonum == AF_INET)
+ l3flags |= IPV4_REPL_SRC;
+ else if (orig.l3protonum == AF_INET6)
+ l3flags |= IPV6_REPL_SRC;
+ }
+ break;
+ case 'q':
+ options |= CT_OPT_REPL_DST;
+ if (optarg) {
+ reply.l3protonum =
+ parse_inetaddr(optarg, &reply.dst);
+ set_family(&family, reply.l3protonum);
+ if (orig.l3protonum == AF_INET)
+ l3flags |= IPV4_REPL_DST;
+ else if (orig.l3protonum == AF_INET6)
+ l3flags |= IPV6_REPL_DST;
+ }
+ break;
+ case 'p':
+ options |= CT_OPT_PROTO;
+ h = findproto(optarg);
+ if (!h)
+ exit_error(PARAMETER_PROBLEM, "proto needed\n");
+ orig.protonum = h->protonum;
+ reply.protonum = h->protonum;
+ exptuple.protonum = h->protonum;
+ mask.protonum = h->protonum;
+ opts = merge_options(opts, h->opts,
+ &h->option_offset);
+ break;
+ case 't':
+ options |= CT_OPT_TIMEOUT;
+ if (optarg)
+ timeout = atol(optarg);
+ break;
+ case 'u': {
+ if (!optarg)
+ continue;
+
+ options |= CT_OPT_STATUS;
+ parse_parameter(optarg, &status, PARSE_STATUS);
+ break;
+ }
+ case 'e':
+ options |= CT_OPT_EVENT_MASK;
+ parse_parameter(optarg, &event_mask, PARSE_EVENT);
+ break;
+ case 'z':
+ options |= CT_OPT_ZERO;
+ break;
+ case '{':
+ options |= CT_OPT_MASK_SRC;
+ if (optarg) {
+ mask.l3protonum =
+ parse_inetaddr(optarg, &mask.src);
+ set_family(&family, mask.l3protonum);
+ }
+ break;
+ case '}':
+ options |= CT_OPT_MASK_DST;
+ if (optarg) {
+ mask.l3protonum =
+ parse_inetaddr(optarg, &mask.dst);
+ set_family(&family, mask.l3protonum);
+ }
+ break;
+ case '[':
+ options |= CT_OPT_EXP_SRC;
+ if (optarg) {
+ exptuple.l3protonum =
+ parse_inetaddr(optarg, &exptuple.src);
+ set_family(&family, exptuple.l3protonum);
+ }
+ break;
+ case ']':
+ options |= CT_OPT_EXP_DST;
+ if (optarg) {
+ exptuple.l3protonum =
+ parse_inetaddr(optarg, &exptuple.dst);
+ set_family(&family, exptuple.l3protonum);
+ }
+ break;
+ case 'a':
+ options |= CT_OPT_NATRANGE;
+ set_family(&family, AF_INET);
+ nat_parse(optarg, 1, &range);
+ break;
+ case 'm':
+ options |= CT_OPT_MARK;
+ mark = atol(optarg);
+ metaflags |= NFCT_MARK;
+ break;
+ case 'i': {
+ char *s = NULL;
+ options |= CT_OPT_ID;
+ if (optarg)
+ break;
+ else if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ s = argv[optind++];
+
+ if (s)
+ id = atol(s);
+ break;
+ }
+ case 'f':
+ options |= CT_OPT_FAMILY;
+ if (strncmp(optarg, "ipv4", strlen("ipv4")) == 0)
+ set_family(&family, AF_INET);
+ else if (strncmp(optarg, "ipv6", strlen("ipv6")) == 0)
+ set_family(&family, AF_INET6);
+ else
+ exit_error(PARAMETER_PROBLEM, "Unknown "
+ "protocol family\n");
+ break;
+ default:
+ if (h && h->parse_opts
+ &&!h->parse_opts(c - h->option_offset, argv, &orig,
+ &reply, &exptuple, &mask, &proto,
+ &l4flags))
+ exit_error(PARAMETER_PROBLEM, "parse error\n");
+
+ /* Unknown argument... */
+ if (!h) {
+ usage(argv[0]);
+ exit_error(PARAMETER_PROBLEM, "Missing "
+ "arguments...\n");
+ }
+ break;
+ }
+ }
+
+ /* default family */
+ if (family == AF_UNSPEC)
+ family = AF_INET;
+
+ generic_cmd_check(command, options);
+ generic_opt_check(command, options);
+
+ if (!(command & CT_HELP)
+ && h && h->final_check
+ && !h->final_check(l4flags, command, &orig, &reply)) {
+ usage(argv[0]);
+ extension_help(h);
+ exit_error(PARAMETER_PROBLEM, "Missing protocol arguments!\n");
+ }
+
+ switch(command) {
+
+ case CT_LIST:
+ cth = nfct_open(CONNTRACK, 0);
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+
+ if (options & CT_COMPARISON) {
+
+ if (options & CT_OPT_ZERO)
+ exit_error(PARAMETER_PROBLEM, "Can't use -z "
+ "with filtering parameters");
+
+ ct = nfct_conntrack_alloc(&orig, &reply, timeout,
+ &proto, status, mark, id,
+ NULL);
+ if (!ct)
+ exit_error(OTHER_PROBLEM, "Not enough memory");
+
+ cmp.ct = ct;
+ cmp.flags = metaflags;
+ cmp.l3flags = l3flags;
+ cmp.l4flags = l4flags;
+ pcmp = &cmp;
+ }
+
+ if (options & CT_OPT_ID)
+ nfct_register_callback(cth,
+ nfct_default_conntrack_display_id,
+ (void *) pcmp);
+ else
+ nfct_register_callback(cth,
+ nfct_default_conntrack_display,
+ (void *) pcmp);
+
+ if (options & CT_OPT_ZERO)
+ res =
+ nfct_dump_conntrack_table_reset_counters(cth, family);
+ else
+ res = nfct_dump_conntrack_table(cth, family);
+ nfct_close(cth);
+ break;
+
+ case EXP_LIST:
+ cth = nfct_open(EXPECT, 0);
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+ if (options & CT_OPT_ID)
+ nfct_register_callback(cth,
+ nfct_default_expect_display_id,
+ NULL);
+ else
+ nfct_register_callback(cth,
+ nfct_default_expect_display,
+ NULL);
+ res = nfct_dump_expect_list(cth, family);
+ nfct_close(cth);
+ break;
+
+ case CT_CREATE:
+ if ((options & CT_OPT_ORIG)
+ && !(options & CT_OPT_REPL)) {
+ reply.l3protonum = orig.l3protonum;
+ memcpy(&reply.src, &orig.dst, sizeof(reply.src));
+ memcpy(&reply.dst, &orig.src, sizeof(reply.dst));
+ } else if (!(options & CT_OPT_ORIG)
+ && (options & CT_OPT_REPL)) {
+ orig.l3protonum = reply.l3protonum;
+ memcpy(&orig.src, &reply.dst, sizeof(orig.src));
+ memcpy(&orig.dst, &reply.src, sizeof(orig.dst));
+ }
+ if (options & CT_OPT_NATRANGE)
+ ct = nfct_conntrack_alloc(&orig, &reply, timeout,
+ &proto, status, mark, id,
+ &range);
+ else
+ ct = nfct_conntrack_alloc(&orig, &reply, timeout,
+ &proto, status, mark, id,
+ NULL);
+ if (!ct)
+ exit_error(OTHER_PROBLEM, "Not Enough memory");
+
+ cth = nfct_open(CONNTRACK, 0);
+ if (!cth) {
+ nfct_conntrack_free(ct);
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+ }
+ res = nfct_create_conntrack(cth, ct);
+ nfct_close(cth);
+ nfct_conntrack_free(ct);
+ break;
+
+ case EXP_CREATE:
+ if (options & CT_OPT_ORIG)
+ exp = nfct_expect_alloc(&orig, &exptuple,
+ &mask, timeout, id);
+ else if (options & CT_OPT_REPL)
+ exp = nfct_expect_alloc(&reply, &exptuple,
+ &mask, timeout, id);
+ if (!exp)
+ exit_error(OTHER_PROBLEM, "Not enough memory");
+
+ cth = nfct_open(EXPECT, 0);
+ if (!cth) {
+ nfct_expect_free(exp);
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+ }
+ res = nfct_create_expectation(cth, exp);
+ nfct_expect_free(exp);
+ nfct_close(cth);
+ break;
+
+ case CT_UPDATE:
+ if ((options & CT_OPT_ORIG)
+ && !(options & CT_OPT_REPL)) {
+ reply.l3protonum = orig.l3protonum;
+ memcpy(&reply.src, &orig.dst, sizeof(reply.src));
+ memcpy(&reply.dst, &orig.src, sizeof(reply.dst));
+ } else if (!(options & CT_OPT_ORIG)
+ && (options & CT_OPT_REPL)) {
+ orig.l3protonum = reply.l3protonum;
+ memcpy(&orig.src, &reply.dst, sizeof(orig.src));
+ memcpy(&orig.dst, &reply.src, sizeof(orig.dst));
+ }
+ ct = nfct_conntrack_alloc(&orig, &reply, timeout,
+ &proto, status, mark, id,
+ NULL);
+ if (!ct)
+ exit_error(OTHER_PROBLEM, "Not enough memory");
+
+ cth = nfct_open(CONNTRACK, 0);
+ if (!cth) {
+ nfct_conntrack_free(ct);
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+ }
+ res = nfct_update_conntrack(cth, ct);
+ nfct_conntrack_free(ct);
+ nfct_close(cth);
+ break;
+
+ case CT_DELETE:
+ if (!(options & CT_OPT_ORIG) && !(options & CT_OPT_REPL))
+ exit_error(PARAMETER_PROBLEM, "Can't kill conntracks "
+ "just by its ID");
+ cth = nfct_open(CONNTRACK, 0);
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+ if (options & CT_OPT_ORIG)
+ res = nfct_delete_conntrack(cth, &orig,
+ NFCT_DIR_ORIGINAL,
+ id);
+ else if (options & CT_OPT_REPL)
+ res = nfct_delete_conntrack(cth, &reply,
+ NFCT_DIR_REPLY,
+ id);
+ nfct_close(cth);
+ break;
+
+ case EXP_DELETE:
+ cth = nfct_open(EXPECT, 0);
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+ if (options & CT_OPT_ORIG)
+ res = nfct_delete_expectation(cth, &orig, id);
+ else if (options & CT_OPT_REPL)
+ res = nfct_delete_expectation(cth, &reply, id);
+ nfct_close(cth);
+ break;
+
+ case CT_GET:
+ cth = nfct_open(CONNTRACK, 0);
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+ nfct_register_callback(cth, nfct_default_conntrack_display,
+ NULL);
+ if (options & CT_OPT_ORIG)
+ res = nfct_get_conntrack(cth, &orig,
+ NFCT_DIR_ORIGINAL, id);
+ else if (options & CT_OPT_REPL)
+ res = nfct_get_conntrack(cth, &reply,
+ NFCT_DIR_REPLY, id);
+ nfct_close(cth);
+ break;
+
+ case EXP_GET:
+ cth = nfct_open(EXPECT, 0);
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+ nfct_register_callback(cth, nfct_default_expect_display,
+ NULL);
+ if (options & CT_OPT_ORIG)
+ res = nfct_get_expectation(cth, &orig, id);
+ else if (options & CT_OPT_REPL)
+ res = nfct_get_expectation(cth, &reply, id);
+ nfct_close(cth);
+ break;
+
+ case CT_FLUSH:
+ cth = nfct_open(CONNTRACK, 0);
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+ res = nfct_flush_conntrack_table(cth, AF_INET);
+ nfct_close(cth);
+ break;
+
+ case EXP_FLUSH:
+ cth = nfct_open(EXPECT, 0);
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+ res = nfct_flush_expectation_table(cth, AF_INET);
+ nfct_close(cth);
+ break;
+
+ case CT_EVENT:
+ if (options & CT_OPT_EVENT_MASK)
+ cth = nfct_open(CONNTRACK, event_mask);
+ else
+ cth = nfct_open(CONNTRACK, NFCT_ALL_CT_GROUPS);
+
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+ signal(SIGINT, event_sighandler);
+
+ if (options & CT_COMPARISON) {
+ ct = nfct_conntrack_alloc(&orig, &reply, timeout,
+ &proto, status, mark, id,
+ NULL);
+ if (!ct)
+ exit_error(OTHER_PROBLEM, "Not enough memory");
+
+ cmp.ct = ct;
+ cmp.flags = metaflags;
+ cmp.l3flags = l3flags;
+ cmp.l4flags = l4flags;
+ pcmp = &cmp;
+ }
+
+ nfct_register_callback(cth,
+ nfct_default_conntrack_event_display,
+ (void *) pcmp);
+ res = nfct_event_conntrack(cth);
+ nfct_close(cth);
+ break;
+
+ case EXP_EVENT:
+ cth = nfct_open(EXPECT, NF_NETLINK_CONNTRACK_EXP_NEW);
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+ signal(SIGINT, event_sighandler);
+ nfct_register_callback(cth, nfct_default_expect_display,
+ NULL);
+ res = nfct_event_expectation(cth);
+ nfct_close(cth);
+ break;
+
+ case CT_VERSION:
+ fprintf(stdout, "%s v%s\n", PROGNAME, VERSION);
+ break;
+ case CT_HELP:
+ usage(argv[0]);
+ if (options & CT_OPT_PROTO)
+ extension_help(h);
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+
+ if (opts != original_opts) {
+ free(opts);
+ opts = original_opts;
+ global_option_offset = 0;
+ }
+
+ if (res < 0) {
+ fprintf(stderr, "Operation failed: %s\n", err2str(res, command));
+ exit(OTHER_PROBLEM);
+ }
+
+ return 0;
+}
diff --git a/cli/test.sh b/cli/test.sh
new file mode 100644
index 0000000..4694236
--- /dev/null
+++ b/cli/test.sh
@@ -0,0 +1,110 @@
+CONNTRACK=conntrack
+
+SRC=1.1.1.1
+DST=2.2.2.2
+SPORT=2005
+DPORT=21
+
+case $1 in
+ dump)
+ echo "Dumping conntrack table"
+ $CONNTRACK -L
+ ;;
+ flush)
+ echo "Flushing conntrack table"
+ $CONNTRACK -F
+ ;;
+ new)
+ echo "creating a new conntrack"
+ $CONNTRACK -I --orig-src $SRC --orig-dst $DST \
+ --reply-src $DST --reply-dst $SRC -p tcp \
+ --orig-port-src $SPORT --orig-port-dst $DPORT \
+ --reply-port-src $DPORT --reply-port-dst $SPORT \
+ --state LISTEN -u SEEN_REPLY -t 50
+ ;;
+ new-simple)
+ echo "creating a new conntrack (simplified)"
+ $CONNTRACK -I --orig-src $SRC --orig-dst $DST \
+ -p tcp --orig-port-src $SPORT --orig-port-dst $DPORT \
+ --state LISTEN -u SEEN_REPLY -t 50
+ ;;
+ new-nat)
+ echo "creating a new conntrack (NAT)"
+ $CONNTRACK -I --orig-src $SRC --orig-dst $DST \
+ -p tcp --orig-port-src $SPORT --orig-port-dst $DPORT \
+ --state LISTEN -u SEEN_REPLY,SRC_NAT -t 50 -a 8.8.8.8
+ ;;
+ get)
+ echo "getting a conntrack"
+ $CONNTRACK -G --orig-src $SRC --orig-dst $DST \
+ -p tcp --orig-port-src $SPORT --orig-port-dst $DPORT \
+ --reply-port-src $DPORT --reply-port-dst $SPORT
+ ;;
+ change)
+ echo "change a conntrack"
+ $CONNTRACK -U --orig-src $SRC --orig-dst $DST \
+ --reply-src $DST --reply-dst $SRC -p tcp \
+ --orig-port-src $SPORT --orig-port-dst $DPORT \
+ --reply-port-src $DPORT --reply-port-dst $SPORT \
+ --state TIME_WAIT -u ASSURED,SEEN_REPLY -t 500
+ ;;
+ delete)
+ $CONNTRACK -D --orig-src $SRC --orig-dst $DST \
+ -p tcp --orig-port-src $SPORT --orig-port-dst $DPORT
+ ;;
+ output)
+ proc=$(cat /proc/net/ip_conntrack | wc -l)
+ netl=$($CONNTRACK -L | wc -l)
+ count=$(cat /proc/sys/net/ipv4/netfilter/ip_conntrack_count)
+ if [ $proc -ne $netl ]; then
+ echo "proc is $proc and netl is $netl and count is $count"
+ else
+ if [ $proc -ne $count ]; then
+ echo "proc is $proc and netl is $netl and count is $count"
+ else
+ echo "now $proc"
+ fi
+ fi
+ ;;
+ dump-expect)
+ $CONNTRACK -L expect
+ ;;
+ flush-expect)
+ $CONNTRACK -F expect
+ ;;
+ create-expect)
+ # requires modprobe ip_conntrack_ftp
+ $CONNTRACK -I expect --orig-src $SRC --orig-dst $DST \
+ --tuple-src 4.4.4.4 --tuple-dst 5.5.5.5 \
+ --mask-src 255.255.255.0 --mask-dst 255.255.255.255 \
+ -p tcp --orig-port-src $SPORT --orig-port-dst $DPORT \
+ -t 200 --tuple-port-src 10 --tuple-port-dst 300 \
+ --mask-port-src 10 --mask-port-dst 300
+ ;;
+ get-expect)
+ $CONNTRACK -G expect --orig-src 4.4.4.4 --orig-dst 5.5.5.5 \
+ --p tcp --orig-port-src 0 --orig-port-dst 0 \
+ --mask-port-src 10 --mask-port-dst 11
+ ;;
+ delete-expect)
+ $CONNTRACK -D expect --orig-src 4.4.4.4 \
+ --orig-dst 5.5.5.5 -p tcp --orig-port-src 0 \
+ --orig-port-dst 0 --mask-port-src 10 --mask-port-dst 11
+ ;;
+ *)
+ echo "Usage: $0 [dump"
+ echo " |new"
+ echo " |new-simple"
+ echo " |new-nat"
+ echo " |get"
+ echo " |change"
+ echo " |delete"
+ echo " |output"
+ echo " |flush"
+ echo " |dump-expect"
+ echo " |flush-expect"
+ echo " |create-expect"
+ echo " |get-expect"
+ echo " |delete-expect]"
+ ;;
+esac