summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--configure.ac45
-rw-r--r--conntrack.853
-rw-r--r--conntrackd.conf.5114
-rw-r--r--doc/helper/conntrackd.conf22
-rw-r--r--doc/manual/conntrack-tools.tmpl214
-rw-r--r--doc/misc/README187
-rw-r--r--doc/misc/clusterip.sh254
-rw-r--r--extensions/libct_proto_dccp.c50
-rw-r--r--extensions/libct_proto_gre.c42
-rw-r--r--extensions/libct_proto_icmp.c47
-rw-r--r--extensions/libct_proto_icmpv6.c47
-rw-r--r--extensions/libct_proto_sctp.c45
-rw-r--r--extensions/libct_proto_tcp.c43
-rw-r--r--extensions/libct_proto_udp.c42
-rw-r--r--extensions/libct_proto_udplite.c42
-rw-r--r--extensions/libct_proto_unknown.c11
-rw-r--r--include/Makefile.am2
-rw-r--r--include/conntrack.h98
-rw-r--r--include/conntrackd.h1
-rw-r--r--include/helper.h1
-rw-r--r--include/helpers/Makefile.am1
-rw-r--r--include/helpers/ftp.h14
-rw-r--r--include/helpers/rpc.h15
-rw-r--r--include/helpers/sane.h13
-rw-r--r--include/helpers/tns.h9
-rw-r--r--include/local.h4
-rw-r--r--include/network.h1
-rw-r--r--nfct.82
-rw-r--r--src/Makefile.am86
-rw-r--r--src/build.c39
-rw-r--r--src/cache-ct.c21
-rw-r--r--src/cache.c4
-rw-r--r--src/conntrack.c1889
-rw-r--r--src/cthelper.c4
-rw-r--r--src/external_inject.c28
-rw-r--r--src/filter.c2
-rw-r--r--src/hash.c1
-rw-r--r--src/helpers.c3
-rw-r--r--src/helpers/Makefile.am2
-rw-r--r--src/helpers/amanda.c1
-rw-r--r--src/helpers/dhcpv6.c1
-rw-r--r--src/helpers/ftp.c33
-rw-r--r--src/helpers/rpc.c14
-rw-r--r--src/helpers/sane.c11
-rw-r--r--src/helpers/slp.c1
-rw-r--r--src/helpers/ssdp.c10
-rw-r--r--src/helpers/tftp.c1
-rw-r--r--src/helpers/tns.c8
-rw-r--r--src/main.c7
-rw-r--r--src/network.c2
-rw-r--r--src/nfct-extensions/helper.c184
-rw-r--r--src/parse.c25
-rw-r--r--src/process.c2
-rw-r--r--src/queue.c4
-rw-r--r--src/read_config_lex.l12
-rw-r--r--src/read_config_yy.y309
-rw-r--r--src/run.c2
-rw-r--r--src/vector.c7
-rwxr-xr-xtests/conntrack/bulk-load-stress.sh163
-rw-r--r--tests/conntrack/load-stress.sh62
-rw-r--r--tests/conntrack/test-conntrack.c84
-rw-r--r--tests/conntrack/testsuite/00create41
-rw-r--r--tests/conntrack/testsuite/01delete5
-rw-r--r--tests/conntrack/testsuite/02filter8
-rw-r--r--tests/conntrack/testsuite/08stdin125
-rw-r--r--tests/conntrack/testsuite/09dumpopt173
-rw-r--r--tests/conntrack/testsuite/10add42
-rwxr-xr-xtests/conntrackd/conntrackd-tests.py263
-rw-r--r--tests/conntrackd/env.yaml2
-rwxr-xr-xtests/conntrackd/netns/conntrackd-netns-test.sh66
-rw-r--r--tests/conntrackd/netns/conntrackd-nsr1.conf37
-rw-r--r--tests/conntrackd/netns/conntrackd-nsr2.conf37
-rw-r--r--tests/conntrackd/netns/ruleset-nsr1.nft6
-rw-r--r--tests/conntrackd/scenarios.yaml100
-rwxr-xr-xtests/conntrackd/scenarios/basic/network-setup.sh59
-rw-r--r--tests/conntrackd/tests.yaml83
77 files changed, 4325 insertions, 1165 deletions
diff --git a/.gitignore b/.gitignore
index f7a5fc7..d061ad7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,5 @@ Makefile.in
/config.*
/configure
/libtool
+
+*.swp
diff --git a/configure.ac b/configure.ac
index d0565be..da852b1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,10 +1,10 @@
-AC_INIT(conntrack-tools, 1.4.6, pablo@netfilter.org)
+AC_INIT([conntrack-tools],[1.4.8],[pablo@netfilter.org])
AC_CONFIG_AUX_DIR([build-aux])
AC_CANONICAL_HOST
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([-Wall foreign subdir-objects
- tar-pax no-dist-gzip dist-bzip2 1.6])
+ tar-pax no-dist-gzip dist-xz 1.6])
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -13,12 +13,11 @@ AC_SEARCH_LIBS([dlopen], [dl], [libdl_LIBS="$LIBS"; LIBS=""])
AC_SUBST([libdl_LIBS])
AC_PROG_CC
-AC_DISABLE_STATIC
AM_PROG_AR
-AM_PROG_LIBTOOL
+LT_INIT([disable-static])
AC_PROG_INSTALL
AC_PROG_LN_S
-AM_PROG_LEX
+AC_PROG_LEX([noyywrap])
AC_PROG_YACC
case "$host" in
@@ -27,13 +26,13 @@ case "$host" in
esac
dnl Dependencies
-if test -z "$ac_cv_prog_YACC"
+if test -z "$ac_cv_prog_YACC" -a ! -f "${srcdir}/src/read_config_yy.c"
then
echo "*** Error: No suitable bison/yacc found. ***"
echo " Please install the 'bison' package."
exit 1
fi
-if test -z "$ac_cv_prog_LEX"
+if test -z "$ac_cv_prog_LEX" -a ! -f "${srcdir}/src/read_config_lex.c"
then
echo "*** Error: No suitable flex/lex found. ***"
echo " Please install the 'flex' package."
@@ -54,7 +53,7 @@ AC_CHECK_HEADER([rpc/rpc_msg.h], [AC_SUBST([LIBTIRPC_CFLAGS],'')], [PKG_CHECK_MO
PKG_CHECK_MODULES([LIBNFNETLINK], [libnfnetlink >= 1.0.1])
PKG_CHECK_MODULES([LIBMNL], [libmnl >= 1.0.3])
-PKG_CHECK_MODULES([LIBNETFILTER_CONNTRACK], [libnetfilter_conntrack >= 1.0.8])
+PKG_CHECK_MODULES([LIBNETFILTER_CONNTRACK], [libnetfilter_conntrack >= 1.0.9])
AS_IF([test "x$enable_cttimeout" = "xyes"], [
PKG_CHECK_MODULES([LIBNETFILTER_CTTIMEOUT], [libnetfilter_cttimeout >= 1.0.0])
])
@@ -75,43 +74,15 @@ AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$enable_systemd" = "xyes"])
AC_CHECK_HEADERS([linux/capability.h],, [AC_MSG_ERROR([Cannot find linux/capabibility.h])])
-# Checks for libraries.
-# FIXME: Replace `main' with a function in `-lc':
-dnl AC_CHECK_LIB([c], [main])
-# FIXME: Replace `main' with a function in `-ldl':
-
AC_CHECK_HEADERS(arpa/inet.h)
-dnl check for inet_pton
AC_CHECK_FUNCS(inet_pton)
-# Checks for header files.
-dnl AC_HEADER_STDC
-dnl AC_CHECK_HEADERS([netinet/in.h stdlib.h])
-
-# Checks for typedefs, structures, and compiler characteristics.
-dnl AC_C_CONST
-dnl AC_C_INLINE
-
-# Let nfct use dlopen() on helper libraries without resolving all symbols.
-AX_CHECK_LINK_FLAG([-Wl,-z,lazy], [AC_SUBST([LAZY_LDFLAGS], [-Wl,-z,lazy])])
-
-# Checks for library functions.
-dnl AC_FUNC_MALLOC
-dnl AC_FUNC_VPRINTF
-dnl AC_CHECK_FUNCS([memset])
-
-dnl AC_CONFIG_FILES([Makefile
-dnl debug/Makefile
-dnl debug/src/Makefile
-dnl extensions/Makefile
-dnl src/Makefile])
-
if test ! -z "$libdir"; then
MODULE_DIR="\\\"$libdir/conntrack-tools/\\\""
CFLAGS="$CFLAGS -DCONNTRACKD_LIB_DIR=$MODULE_DIR"
fi
-AC_CONFIG_FILES([Makefile src/Makefile include/Makefile include/linux/Makefile include/linux/netfilter/Makefile extensions/Makefile src/helpers/Makefile])
+AC_CONFIG_FILES([Makefile src/Makefile include/Makefile include/helpers/Makefile include/linux/Makefile include/linux/netfilter/Makefile extensions/Makefile src/helpers/Makefile])
AC_OUTPUT
echo "
diff --git a/conntrack.8 b/conntrack.8
index 1174c6c..3b6a15b 100644
--- a/conntrack.8
+++ b/conntrack.8
@@ -14,6 +14,8 @@ conntrack \- command line interface for netfilter connection tracking
.br
.BR "conntrack -I [table] parameters"
.br
+.BR "conntrack -A [table] parameters"
+.br
.BR "conntrack -U [table] parameters"
.br
.BR "conntrack -E [table] [options]"
@@ -23,8 +25,10 @@ conntrack \- command line interface for netfilter connection tracking
.BR "conntrack -C [table]"
.br
.BR "conntrack -S "
+.br
+.BR "conntrack -R file"
.SH DESCRIPTION
-The \fBconntrack\fP utilty provides a full featured userspace interface to the
+The \fBconntrack\fP utility provides a full\-featured userspace interface to the
Netfilter connection tracking system that is intended to replace the old
/proc/net/ip_conntrack interface. This tool can be used to search, list,
inspect and maintain the connection tracking subsystem of the Linux kernel.
@@ -86,7 +90,10 @@ Search for and show a particular (matching) entry in the given table.
Delete an entry from the given table.
.TP
.BI "-I, --create "
-Create a new entry from the given table.
+Create a new entry from the given table, it fails if it already exists.
+.TP
+.BI "-A, --add "
+Add a new entry from the given table.
.TP
.BI "-U, --update "
Update an entry from the given table.
@@ -102,6 +109,9 @@ Show the table counter.
.TP
.BI "-S, --stats "
Show the in-kernel connection tracking system statistics.
+.TP
+.BI "-R, --load-file "
+Load entries from a given file. To read from stdin, "\-" should be specified.
.SS PARAMETERS
.TP
@@ -109,19 +119,19 @@ Show the in-kernel connection tracking system statistics.
Atomically zero counters after reading them. This option is only valid in
combination with the "\-L, \-\-dump" command options.
.TP
-.BI "-o, --output [extended,xml,timestamp,id,ktimestamp,labels,userspace] "
+.BI "-o, --output [extended,xml,save,timestamp,id,ktimestamp,labels] "
Display output in a certain format. With the extended output option, this tool
displays the layer 3 information. With ktimestamp, it displays the in-kernel
timestamp available since 2.6.38 (you can enable it via the \fBsysctl(8)\fP
key \fBnet.netfilter.nf_conntrack_timestamp\fP).
The labels output option tells \fBconntrack\fP to show the names of connection
tracking labels that might be present.
-The userspace output options tells if the event has been triggered by a process.
+The userspace output option tells if the event has been triggered by a process.
.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.
+by the kernel to the types that you are actually interested in.
.
This option can only be used in conjunction with "\-E, \-\-event".
.TP
@@ -130,7 +140,7 @@ Set the Netlink socket buffer size in bytes. This option is useful if the
command line tool reports ENOBUFS errors. If you do not pass this option, the
default value available at \fBsysctl(8)\fP key \fBnet.core.rmem_default\fP is
used. The tool reports this problem if your process is too slow to handle all
-the event messages or, in other words, if the amount of events are big enough
+the event messages or, in other words, if the amount of events is big enough
to overrun the socket buffer. Note that using a big buffer reduces the chances
to hit ENOBUFS, however, this results in more memory consumption.
.
@@ -158,7 +168,7 @@ one specified as argument.
Specify layer four (TCP, UDP, ...) protocol.
.TP
.BI "-f, --family " "PROTO"
-Specify layer three (ipv4, ipv6) protocol
+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
@@ -170,20 +180,23 @@ Specify the conntrack mark. Optionally, a mask value can be specified.
In "\-\-update" mode, this mask specifies the bits that should be zeroed before
XORing the MARK value into the ctmark.
Otherwise, the mask is logically ANDed with the existing mark before the
-comparision. In "\-\-create" mode, the mask is ignored.
+comparison. In "\-\-create" mode, the mask is ignored.
.TP
.BI "-l, --label " "LABEL"
Specify a conntrack label.
This option is only available in conjunction with "\-L, \-\-dump",
"\-E, \-\-event", "\-U \-\-update" or "\-D \-\-delete".
-Match entries whose labels match at least those specified.
-Use multiple \-l commands to specify multiple labels that need to be set.
-Match entries whose labels matches at least those specified as arguments.
+Match entries whose labels include those specified as arguments.
+Use multiple \-l options to specify multiple labels that need to be set.
.TP
.BI "--label-add " "LABEL"
-Specify the conntrack label to add to to the selected conntracks.
-This option is only available in conjunction with "\-I, \-\-create" or
-"\-U, \-\-update".
+Specify the conntrack label to add to the selected conntracks.
+This option is only available in conjunction with "\-I, \-\-create",
+"\-A, \-\-add" or "\-U, \-\-update".
+As a rule of thumb, you must use either the 'connlabel' match in your iptables
+ruleset or the 'ct label' statement in your nftables ruleset, this turns on the
+ct label support in the kernel and it allows you to update labels via
+"\-U, \-\-update", otherwise label updates are ignored.
.TP
.BI "--label-del " "[LABEL]"
Specify the conntrack label to delete from the selected conntracks.
@@ -376,6 +389,9 @@ additional information.
.B conntrack \-L \-o xml
Show the connection tracking table in XML
.TP
+.B conntrack \-L \-o save
+Show the connection tracking table in conntrack syntax format
+.TP
.B conntrack \-L -f ipv6 -o extended
Only dump IPv6 connections in /proc/net/nf_conntrack format, with
additional information.
@@ -387,10 +403,13 @@ Show source NAT connections
Show connection events together with the timestamp
.TP
.B conntrack \-D \-s 1.2.3.4
-Delete all flow whose source address is 1.2.3.4
+Delete all flows whose source address is 1.2.3.4
.TP
.B conntrack \-U \-s 1.2.3.4 \-m 1
Set connmark to 1 of all the flows whose source address is 1.2.3.4
+.TP
+.B conntrack -L -w 11 -o save | sed "s/-w 11/-w 12/g" | conntrack --load-file -
+Copy all entries from ct zone 11 to ct zone 12
.SH BUGS
Please, report them to netfilter-devel@vger.kernel.org or file a bug in
@@ -406,8 +425,8 @@ See
Jay Schulist, Patrick McHardy, Harald Welte and Pablo Neira Ayuso wrote the
kernel-level "ctnetlink" interface that is used by the conntrack tool.
.PP
-Pablo Neira Ayuso wrote and maintain the conntrack tool, Harald Welte added
-support for conntrack based accounting counters.
+Pablo Neira Ayuso wrote and maintains the conntrack tool, Harald Welte added
+support for conntrack\-based accounting counters.
.PP
Man page written by Harald Welte <laforge@netfilter.org> and
Pablo Neira Ayuso <pablo@netfilter.org>.
diff --git a/conntrackd.conf.5 b/conntrackd.conf.5
index 673f895..50d7b98 100644
--- a/conntrackd.conf.5
+++ b/conntrackd.conf.5
@@ -22,7 +22,7 @@
.\" <http://www.gnu.org/licenses/>.
.\" %%%LICENSE_END
.\"
-.TH CONNTRACKD.CONF 5 "Jan 27, 2019"
+.TH CONNTRACKD.CONF 5 "Jan 20, 2021"
.SH NAME
conntrackd.conf \- configuration file for conntrackd daemon
@@ -52,7 +52,7 @@ You should consider this file as case-sensitive.
Empty lines and lines starting with the '#' character are ignored.
Before starting to develop a new configuration, you may want to learn the
-concepts behind this technlogy at
+concepts behind this technology at
\fIhttp://conntrack-tools.netfilter.org/manual.html\fP.
There are complete configuration examples at the end of this man page.
@@ -133,7 +133,7 @@ experiments measuring the cycles spent by the acknowledgment handling
with oprofile).
.TP
-.BI "DisableExternalCache <on|off>"
+.BI "DisableExternalCache <yes|no>"
This clause allows you to disable the external cache. Thus, the state entries
are directly injected into the kernel conntrack table. As a result, you save
memory in user-space but you consume slots in the kernel conntrack table for
@@ -144,19 +144,19 @@ If you are installing \fBconntrackd(8)\fP for first time, please read the user
manual and I encourage you to consider using the fail-over scripts instead of
enabling this option!
-By default, this clause is set off.
+By default this is set to no, meaning the external cache is enabled.
.TP
-.BI "StartupResync <on|off>"
+.BI "StartupResync <yes|no>"
Order conntrackd to request a complete conntrack table resync against the other
node at startup. A single request will be made.
This is useful to get in sync with another node which has been running while we
were down.
-Example: StartupResync on
+Example: StartupResync yes
-By default, this clause is set off.
+By default, this clause is set to no.
.SS Mode ALARM
@@ -201,14 +201,14 @@ In this synchronization mode you may configure \fBDisableInternalCache\fP,
\fBStartupResync\fP.
.TP
-.BI "DisableInternalCache <on|off>"
+.BI "DisableInternalCache <yes|no>"
This clause allows you to disable the internal cache. Thus, the synchronization
messages are directly sent through the dedicated link.
-This option is set off by default.
+This option is set to no by default.
.TP
-.BI "DisableExternalCache <on|off>"
+.BI "DisableExternalCache <yes|no>"
Same as in \fBFTFW\fP mode.
.TP
@@ -220,7 +220,7 @@ Same as in \fBFTFW\fP mode.
Same as in \fBFTFW\fP mode.
.TP
-.BI "StartupResync <on|off>"
+.BI "StartupResync <yes|no>"
Same as in \fBFTFW\fP mode.
.SS MULTICAST
@@ -326,7 +326,7 @@ to increase the buffer size.
Example: RcvSocketBuffer 1249280
.TP
-.BI "Checksum <on|off>"
+.BI "Checksum <yes|no>"
Enable/Disable message checksumming. This is a good property to achieve
fault-tolerance. In case of doubt, use it.
@@ -395,7 +395,7 @@ Same as in the \fBMulticast\fP transport protocol configuration.
Same as in the \fBMulticast\fP transport protocol configuration.
.TP
-.BI "Checksum <on|off>"
+.BI "Checksum <yes|no>"
Same as in the \fBMulticast\fP transport protocol configuration.
@@ -419,7 +419,7 @@ Example:
Interface eth2
SndSocketBuffer 1249280
RcvSocketBuffer 1249280
- Checksum on
+ Checksum yes
}
.fi
@@ -429,7 +429,7 @@ Other unsorted options that are related to the synchronization protocol
or transport mechanism.
.TP
-.BI "TCPWindowTracking <on|off>"
+.BI "TCPWindowTracking <yes|no>"
TCP state-entries have window tracking disabled by default, you can enable it
with this option. As said, default is off.
This feature requires a \fBLinux kernel >= 2.6.36\fP.
@@ -465,7 +465,7 @@ This top-level section contains generic configuration directives for the
\fBconntrackd(8)\fP daemon.
.TP
-.BI "Systemd <on|off>"
+.BI "Systemd <yes|no>"
Enable \fBsystemd(1)\fP runtime support if \fBconntrackd(8)\fP is compiled
with the proper configuration. Then you can use a service unit of
\fIType=notify\fP.
@@ -474,7 +474,7 @@ Obviously, this requires the init of your system to be \fBsystemd(1)\fP.
Note: \fBsystemd(1)\fP watchdog is supported as well.
-Example: Systemd on
+Example: Systemd yes
By default runtime support is enabled if conntrackd was built with the systemd
feature. Otherwise is off.
@@ -503,15 +503,15 @@ dead entries cached for possible retransmission during state synchronization.
Example: HashLimit 131072
.TP
-.BI "LogFile <on|off|filename>"
+.BI "LogFile <yes|no|filename>"
Enable \fBconntrackd(8)\fP to log to a file.
-Example: LogFile on
+Example: LogFile no
-Default is off. The default logfile is \fB/var/log/conntrackd.log\fP.
+Default is no. Default logfile is \fB/var/log/conntrackd.log\fP.
.TP
-.BI "Syslog <on|off|facility>"
+.BI "Syslog <yes|no|facility>"
Enable connection logging via Syslog. If you set the facility, use the same as
in the \fBStats\fP section, otherwise you'll get a warning message.
@@ -545,7 +545,7 @@ size growth that can be reached.
Example: NetlinkBufferSizeMaxGrowth 8388608
.TP
-.BI "NetlinkOverrunResync <on|off|value>"
+.BI "NetlinkOverrunResync <yes|no|value>"
If the daemon detects that Netlink is dropping state-change events, it
automatically schedules a resynchronization against the Kernel after 30 seconds
(default value). Resynchronizations are expensive in terms of CPU consumption
@@ -554,20 +554,20 @@ that do not exist anymore.
Note: Be careful of setting a very small value here.
-Example: NetlinkOverrunResync on
+Example: NetlinkOverrunResync yes
The default value is \fB30\fP seconds.
If not specified, the daemon assumes that this option is enabled and uses the
default value.
.TP
-.BI "NetlinkEventsReliable <on|off>"
+.BI "NetlinkEventsReliable <yes|no>"
If you want reliable event reporting over Netlink, set on this option. If you
set on this clause, it is a good idea to set off \fBNetlinkOverrunResync\fP.
You need \fBLinux Kernel >= 2.6.31\fP for this option to work.
-Example: NetlinkEventsReliable on
+Example: NetlinkEventsReliable yes
This option is off by default.
@@ -630,7 +630,7 @@ filter-sets in positive or negative logic depending on your needs.
You can select if \fBconntrackd(8)\fP filters the event messages from
user-space or kernel-space. The kernel-space event filtering saves some CPU
cycles by avoiding the copy of the event message from kernel-space to
-user-space. The kernel-space event filtering is prefered, however, you require
+user-space. The kernel-space event filtering is preferred, however, you require
a \fBLinux kernel >= 2.6.29\fP to filter from kernel-space.
The syntax for this section is: \fBFilter From <from> { }\fP.
@@ -758,29 +758,29 @@ This top-level section indicates \fBconntrackd(8)\fP to work as a statistic
collector for the nf_conntrack linux kernel subsystem.
.TP
-.BI "LogFile <on|off|filename>"
+.BI "LogFile <yes|no|filename>"
If you enable this option, the daemon writes the information about destroyed
connections to a logfile.
-Default is off. Default filename is \fB/var/log/conntrackd-stats.log\fP.
+Default is no. Default filename is \fB/var/log/conntrackd-stats.log\fP.
.TP
-.BI "NetlinkEventsReliable <on|off>"
+.BI "NetlinkEventsReliable <yes|no>"
If you want reliable event reporting over Netlink, set on this option. If
you set on this clause, it is a good idea to set off
\fBNetlinkOverrunResync\fP. This requires \fBLinux kernel >= 2.6.31\fP.
-Default is off.
+Default is no.
.TP
-.BI "Syslog <on|off|facility>"
+.BI "Syslog <yes|no|facility>"
Enable connection logging via Syslog.
If you set the facility, use the same as in the \fBGeneral\fP section,
otherwise you'll get a warning message.
Example: Syslog local0
-Default is off.
+Default is no.
.SH HELPER
Note: this configuration is very advanced and has nothing to do with
@@ -899,15 +899,15 @@ collector.
.nf
Stats {
- LogFile on
- NetlinkEventsReliable Off
- Syslog off
+ LogFile yes
+ NetlinkEventsReliable no
+ Syslog yes
}
General {
- Systemd on
+ Systemd yes
HashSize 8192
HashLimit 65535
- Syslog on
+ Syslog yes
LockFile /var/lock/conntrack.lock
UNIX {
Path /var/run/conntrackd.ctl
@@ -942,7 +942,7 @@ Sync {
ResendQueueSize 131072
PurgeTimeout 60
ACKWindowSize 300
- DisableExternalCache Off
+ DisableExternalCache no
}
Multicast {
IPv4_address 225.0.0.50
@@ -951,7 +951,7 @@ Sync {
Interface eth2
SndSocketBuffer 1249280
RcvSocketBuffer 1249280
- Checksum on
+ Checksum yes
}
Multicast Default {
IPv4_address 225.0.0.51
@@ -960,27 +960,27 @@ Sync {
Interface eth3
SndSocketBuffer 1249280
RcvSocketBuffer 1249280
- Checksum on
+ Checksum yes
}
Options {
- TCPWindowTracking Off
- ExpectationSync On
+ TCPWindowTracking no
+ ExpectationSync yes
}
}
General {
- Systemd on
+ Systemd yes
HashSize 32768
HashLimit 131072
- LogFile on
- Syslog off
+ LogFile yes
+ Syslog no
LockFile /var/lock/conntrack.lock
UNIX {
Path /var/run/conntrackd.ctl
}
NetlinkBufferSize 2097152
NetlinkBufferSizeMaxGrowth 8388608
- NetlinkOverrunResync On
- NetlinkEventsReliable Off
+ NetlinkOverrunResync yes
+ NetlinkEventsReliable no
EventIterationLimit 100
Filter From Userspace {
Protocol Accept {
@@ -1007,8 +1007,8 @@ It includes common general configuration as well.
.nf
Sync {
Mode NOTRACK {
- DisableInternalCache on
- DisableExternalCache on
+ DisableInternalCache yes
+ DisableExternalCache yes
}
TCP {
IPv4_address 192.168.2.100
@@ -1017,27 +1017,27 @@ Sync {
Interface eth2
SndSocketBuffer 1249280
RcvSocketBuffer 1249280
- Checksum on
+ Checksum yes
}
Options {
- TCPWindowTracking Off
- ExpectationSync On
+ TCPWindowTracking no
+ ExpectationSync yes
}
}
General {
- Systemd on
+ Systemd yes
HashSize 32768
HashLimit 131072
- LogFile on
- Syslog off
+ LogFile yes
+ Syslog no
LockFile /var/lock/conntrack.lock
UNIX {
Path /var/run/conntrackd.ctl
}
NetlinkBufferSize 2097152
NetlinkBufferSizeMaxGrowth 8388608
- NetlinkOverrunResync On
- NetlinkEventsReliable Off
+ NetlinkOverrunResync yes
+ NetlinkEventsReliable no
EventIterationLimit 100
Filter From Userspace {
Protocol Accept {
diff --git a/doc/helper/conntrackd.conf b/doc/helper/conntrackd.conf
index 6ffe008..efa318a 100644
--- a/doc/helper/conntrackd.conf
+++ b/doc/helper/conntrackd.conf
@@ -3,11 +3,21 @@
#
Helper {
- # Before this, you have to make sure you have registered the `ftp'
- # user-space helper stub via:
+ #
+ # Set up the userspace helpers when the daemon is started. If unset,
+ # you have manually set up the user-space helper stub, e.g.
#
# nfct add helper ftp inet tcp
#
+ # This new setting simplifies new deployment, so it is recommended to
+ # turn it on. On existing deployments, make sure to remove the nfct
+ # command invocation since it is not required anymore.
+ #
+ # Default: no (for backward compatibility reasons)
+ # Recommended: yes
+ #
+ Setup yes
+
Type ftp inet tcp {
#
# Set NFQUEUE number you want to use to receive traffic from
@@ -73,7 +83,7 @@ Helper {
}
}
Type mdns inet udp {
- QueueNum 6
+ QueueNum 5
QueueLen 10240
Policy mdns {
ExpectMax 8
@@ -81,7 +91,7 @@ Helper {
}
}
Type ssdp inet udp {
- QueueNum 5
+ QueueNum 6
QueueLen 10240
Policy ssdp {
ExpectMax 8
@@ -89,7 +99,7 @@ Helper {
}
}
Type ssdp inet tcp {
- QueueNum 5
+ QueueNum 7
QueueLen 10240
Policy ssdp {
ExpectMax 8
@@ -97,7 +107,7 @@ Helper {
}
}
Type slp inet udp {
- QueueNum 7
+ QueueNum 8
QueueLen 10240
Policy slp {
ExpectMax 8
diff --git a/doc/manual/conntrack-tools.tmpl b/doc/manual/conntrack-tools.tmpl
index 739b7f1..822dd49 100644
--- a/doc/manual/conntrack-tools.tmpl
+++ b/doc/manual/conntrack-tools.tmpl
@@ -19,7 +19,7 @@
</authorgroup>
<copyright>
- <year>2008-2012</year>
+ <year>2008-2020</year>
<holder>Pablo Neira Ayuso</holder>
</copyright>
@@ -35,10 +35,8 @@
</legalnotice>
<releaseinfo>
- This document details how to install and configure the
- <ulink url="http://conntrack-tools.netfilter.org">conntrack-tools</ulink>
- &gt;= 1.4.0. This document will evolve in the future to cover new features
- and changes.</releaseinfo>
+ This document details how to install and to configure the <ulink url="http://conntrack-tools.netfilter.org">conntrack-tools</ulink>.
+ </releaseinfo>
</bookinfo>
@@ -46,21 +44,13 @@
<chapter id="introduction"><title>Introduction</title>
- <para>This document should be a kick-off point to install and configure the
- <ulink url="http://conntrack-tools.netfilter.org">conntrack-tools</ulink>.
- If you find any error or imprecision in this document, please send an email
- to the author, it will be appreciated.</para>
+<para>This documentation provides a description on how to install and to configure the <ulink url="http://conntrack-tools.netfilter.org">conntrack-tools</ulink>.</para>
- <para>In this document, the author assumes that the reader is familiar with firewalling concepts and iptables in general. If this is not your case, I suggest you to read the iptables documentation before going ahead. Moreover, the reader must also understand the difference between <emphasis>stateful</emphasis> and <emphasis>stateless</emphasis> firewalls. If this is not your case, I strongly suggest you to read the article <ulink url="http://people.netfilter.org/pablo/docs/login.pdf">Netfilter's Connection Tracking System</ulink> published in <emphasis>:login; the USENIX magazine</emphasis>. That document contains a general description that should help to clarify the concepts.</para>
-
-<para>If you do not fulfill the previous requirements, this documentation is likely to be a source of frustration. Probably, you wonder why I'm insisting on these prerequisites too much, the fact is that if your iptables rule-set is <emphasis>stateless</emphasis>, it is very likely that the <emphasis>conntrack-tools</emphasis> will not be of any help for you. You have been warned!</para>
+<para>This documentation assumes that the reader is familiar with basic firewalling and Netfilter concepts. You also must understand the difference between <emphasis>stateless</emphasis> and <emphasis>stateful</emphasis> firewalls. Otherwise, please read <ulink url="http://people.netfilter.org/pablo/docs/login.pdf">Netfilter's Connection Tracking System</ulink> published in <emphasis>:login; the USENIX magazine</emphasis> for a quick reference.</para>
</chapter>
<chapter id="what"><title>What are the conntrack-tools?</title>
- <para>The conntrack-tools are a set of free software tools for GNU/Linux that allow system administrators interact, from user-space, with the in-kernel <ulink url="http://people.netfilter.org/pablo/docs/login.pdf">Connection Tracking System</ulink>, which is the module that enables stateful packet inspection for iptables. Probably, you did not hear about this module so far. However, if any of the rules of your rule-set use the <emphasis>state</emphasis> or <emphasis>ctstate</emphasis> iptables matches, you are indeed using it.
- </para>
-
<para>The <ulink url="http://conntrack-tools.netfilter.org">conntrack-tools</ulink> package contains two programs:</para>
<itemizedlist>
@@ -72,17 +62,18 @@
</listitem>
</itemizedlist>
- <para>Although the name of both tools is very similar - and you can blame me for that, I'm not a marketing guy - they are used for very different tasks.</para>
+<para>Mind the trailing <emphasis>d</emphasis> that refers to either the command line utility or the daemon.</para>
</chapter>
<chapter id="requirements"><title>Requirements</title>
- <para>You have to install the following software in order to get the <emphasis>conntrack-tools</emphasis> working. Make sure that you have installed them correctly before going ahead:</para>
+<para>If you are using the Linux kernel that your distribution provides, then you most likely can skip this.</para>
+
+<para>If you compile your own Linux kernel, then please make sure the following options are enabled.</para>
+
+<para>You require a <ulink url="http://www.kernel.org">Linux kernel</ulink> version &gt;= 2.6.18.</para>
- <itemizedlist>
- <listitem>
- <para><ulink url="http://www.kernel.org">Linux kernel</ulink> version &gt;= 2.6.18 that, at least, has support for:</para>
<itemizedlist>
<listitem>
<para>Connection Tracking System.</para>
@@ -123,19 +114,47 @@
</itemizedlist>
</listitem>
</itemizedlist>
- <note><title>Verifying kernel support</title>
- <para>
- Make sure you have loaded <emphasis>nf_conntrack</emphasis>, <emphasis>nf_conntrack_ipv4</emphasis> (if your setup also supports IPv6, <emphasis>nf_conntrack_ipv6</emphasis>) and <emphasis>nf_conntrack_netlink</emphasis>.
- </para>
- </note>
- </listitem>
+
+<note><title>Validating Linux kernel support</title>
+<para>You can validate that your Linux kernel support for the <emphasis>conntrack-tools</emphasis> through <emphasis>modinfo</emphasis>.</para>
+
+ <programlisting>
+ # modinfo nf_conntrack
+filename: /lib/modules/5.2.0/kernel/net/netfilter/nf_conntrack.ko
+license: GPL
+alias: nf_conntrack-10
+alias: nf_conntrack-2
+alias: ip_conntrack
+depends: nf_defrag_ipv6,libcrc32c,nf_defrag_ipv4
+retpoline: Y
+intree: Y
+name: nf_conntrack
+vermagic: 5.7.0+ SMP preempt mod_unload modversions
+parm: tstamp:Enable connection tracking flow timestamping. (bool)
+parm: acct:Enable connection tracking flow accounting. (bool)
+parm: nf_conntrack_helper:Enable automatic conntrack helper assignment (default 0) (bool)
+parm: expect_hashsize:uint
+parm: enable_hooks:Always enable conntrack hooks (bool)
+</programlisting>
+
+<para>Make sure <emphasis>nf_conntrack_netlink</emphasis> is also available.</para>
+</note>
+
+<para>You also need to install the following library dependencies:</para>
+
+ <itemizedlist>
<listitem>
- <para>libnfnetlink: the netfilter netlink library use the official release available in <ulink url="http://www.netfilter.org">netfilter.org</ulink></para>
+ <para>libnfnetlink: the netfilter netlink library use the official release available in <ulink url="http://www.netfilter.org/projects/libnfnetlink">netfilter.org</ulink></para>
</listitem>
<listitem>
- <para>libnetfilter_conntrack: the netfilter netlink library use the official release available in <ulink url="http://www.netfilter.org">netfilter.org</ulink></para>
+ <para>libnetfilter_conntrack: the netfilter netlink library use the official release available in <ulink url="http://www.netfilter.org/projects/libnetfilter_conntrack">netfilter.org</ulink></para>
</listitem>
</itemizedlist>
+
+<note><title>Installing library dependencies</title>
+<para>Your distribution most likely also provides packages for this software, so you do not have to compile it yourself.</para>
+</note>
+
</chapter>
<chapter id="Installation"><title>Installation</title>
@@ -148,18 +167,8 @@
(non-root)$ make
(root) # make install</programlisting>
-<note><title>Fedora Users</title>
- <para>If you are installing the libraries in /usr/local/, do not forget to do the following things:</para>
- <itemizedlist>
- <listitem><para>PKG_CONFIG_PATH=/usr/local/lib/pkgconfig; export PKG_CONFIG_PATH</para></listitem>
- <listitem><para>Add `/usr/local/lib' to your /etc/ld.so.conf file and run `ldconfig'</para></listitem>
- </itemizedlist>
- <para>Check `ldd' for trouble-shooting, read <ulink url="http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html">this</ulink> for more information on how libraries work.</para>
-</note>
-
-<note><title>Verifying kernel support</title>
- <para>To check that the modules are enabled in the kernel, run <emphasis>`conntrack -E'</emphasis> and generate traffic, you should see flow events reporting new connections and updates.
- </para>
+<note><title>Installing conntrack and conntrackd</title>
+<para>Your distribution most likely also provides packages for this software, so you do not have to compile it yourself.</para>
</note>
</chapter>
@@ -174,7 +183,7 @@
tcp 6 431698 ESTABLISHED src=192.168.2.100 dst=123.59.27.117 sport=34849 dport=993 packets=244 bytes=18723 src=123.59.27.117 dst=192.168.2.100 sport=993 dport=34849 packets=203 bytes=144731 [ASSURED] mark=0 use=1
</programlisting>
-<para>The command line tool <emphasis>conntrack</emphasis> can be used to display the same information:</para>
+<para>You can list the existing flows using the <emphasis>conntrack</emphasis> utility via <emphasis>-L</emphasis> command:</para>
<programlisting>
# conntrack -L
tcp 6 431982 ESTABLISHED src=192.168.2.100 dst=123.59.27.117 sport=34846 dport=993 packets=169 bytes=14322 src=123.59.27.117 dst=192.168.2.100 sport=993 dport=34846 packets=113 bytes=34787 [ASSURED] mark=0 use=1
@@ -182,25 +191,23 @@
conntrack v1.4.6 (conntrack-tools): 2 flow entries have been shown.
</programlisting>
-<para>You can natively filter the output without using <emphasis>grep</emphasis>:</para>
+ <para>The <emphasis>conntrack</emphasis> syntax is similar to <emphasis>iptables</emphasis>.</para>
+
+<para>You can filter out the listing without using <emphasis>grep</emphasis>:</para>
<programlisting>
# conntrack -L -p tcp --dport 993
tcp 6 431982 ESTABLISHED src=192.168.2.100 dst=123.59.27.117 sport=34846 dport=993 packets=169 bytes=14322 src=123.59.27.117 dst=192.168.2.100 sport=993 dport=34846 packets=113 bytes=34787 [ASSURED] mark=0 use=1
conntrack v1.4.6 (conntrack-tools): 1 flow entries have been shown.
</programlisting>
-<para>Update the mark based on a selection, this allows you to change the mark of an entry without using the CONNMARK target:</para>
+<para>You can update the ct mark, extending the previous example:</para>
<programlisting>
# conntrack -U -p tcp --dport 993 --mark 10
tcp 6 431982 ESTABLISHED src=192.168.2.100 dst=123.59.27.117 sport=34846 dport=993 packets=169 bytes=14322 src=123.59.27.117 dst=192.168.2.100 sport=993 dport=34846 packets=113 bytes=34787 [ASSURED] mark=10 use=1
conntrack v1.4.6 (conntrack-tools): 1 flow entries have been updated.
</programlisting>
-<para>Delete one entry, this can be used to block traffic if:</para>
-<itemizedlist>
- <listitem><para>You have a stateful rule-set that blocks traffic in INVALID state.</para></listitem>
- <listitem><para>You set <emphasis>/proc/sys/net/netfilter/nf_conntrack_tcp_loose</emphasis> to zero.</para></listitem>
-</itemizedlist>
+<para>You can also delete entries</para>
<programlisting>
# conntrack -D -p tcp --dport 993
@@ -208,7 +215,14 @@ conntrack v1.4.6 (conntrack-tools): 1 flow entries have been updated.
conntrack v1.4.6 (conntrack-tools): 1 flow entries have been deleted.
</programlisting>
-<para>Display the connection tracking events:</para>
+<para>
+This allows you to block TCP traffic if:</para>
+<itemizedlist>
+ <listitem><para>You have a stateful rule-set that drops traffic in INVALID state.</para></listitem>
+ <listitem><para>You set <emphasis>/proc/sys/net/netfilter/nf_conntrack_tcp_loose</emphasis> to zero.</para></listitem>
+</itemizedlist>
+
+<para>You can also listen to the connection tracking events:</para>
<programlisting>
# conntrack -E
[NEW] udp 17 30 src=192.168.2.100 dst=192.168.2.1 sport=57767 dport=53 [UNREPLIED] src=192.168.2.1 dst=192.168.2.100 sport=53 dport=57767
@@ -218,20 +232,23 @@ conntrack v1.4.6 (conntrack-tools): 1 flow entries have been deleted.
[UPDATE] tcp 6 432000 ESTABLISHED src=192.168.2.100 dst=66.102.9.104 sport=33379 dport=80 src=66.102.9.104 dst=192.168.2.100 sport=80 dport=33379 [ASSURED]
</programlisting>
-<para>You can also display the existing flows in XML format, filter the output based on the NAT handling applied, etc.</para>
+<para>There are many options, including support for XML output, more advanced filters, and so on. Please check the manpage for more information.</para>
</chapter>
<chapter id="settingup"><title>Setting up conntrackd: the daemon</title>
- <para>The daemon <emphasis>conntrackd</emphasis> supports two working modes:</para>
+ <para>The <emphasis>conntrackd</emphasis> daemon supports three modes:</para>
- <itemizedlist>
+ <itemizedlist>
<listitem>
- <para><emphasis>State table synchronization</emphasis>: the daemon can be used to synchronize the connection tracking state table between several firewall replicas. This can be used to deploy fault-tolerant stateful firewalls. This is the main feature of the daemon.</para>
+ <para><emphasis>State table synchronization</emphasis>, to synchronize the connection tracking state table between several firewalls in High Availability (HA) scenarios.</para>
</listitem>
<listitem>
- <para><emphasis>Flow-based statistics collection</emphasis>: the daemon can be used to collect flow-based statistics. This feature is similar to what <ulink url="http://www.netfilter.org/projects/ulogd/">ulogd-2.x</ulink> provides.</para>
+ <para><emphasis>Userspace connection tracking helpers</emphasis>, for layer 7 Application Layer Gateway (ALG) such as DHCPv6, MDNS, RPC, SLP and Oracle TNS. As an alternative to the in-kernel connection tracking helpers that are available in the Linux kernel.</para>
+ </listitem>
+ <listitem>
+ <para><emphasis>Flow-based statistics collection</emphasis>, to collect flow-based statistics as an alternative to <ulink url="http://www.netfilter.org/projects/ulogd/">ulogd2</ulink>, although <emphasis>ulogd2</emphasis> allows for more flexible statistics collection.</para>
</listitem>
</itemizedlist>
@@ -239,15 +256,12 @@ conntrack v1.4.6 (conntrack-tools): 1 flow entries have been deleted.
<sect2 id="sync-requirements"><title>Requirements</title>
- <para>In order to get <emphasis>conntrackd</emphasis> working in synchronization mode, you have to fulfill the following requirements:</para>
+ <para>If you would like to configure <emphasis>conntrackd</emphasis> to work in state synchronization mode, then you require:</para>
<orderedlist>
<listitem>
- <para>A <emphasis>high availability manager</emphasis> like <ulink url="http://www.keepalived.org">keepalived</ulink> that manages the virtual IPs of the
- firewall cluster, detects errors, and decide when to migrate the virtual IPs
- from one firewall replica to another. Without it, <emphasis>conntrackd</emphasis> will not work appropriately.</para>
- <para>The state synchronization setup requires a working installation of <ulink url="http://www.keepalived.org">keepalived</ulink>, preferibly a recent version. Check if your distribution comes with a recent packaged version. Otherwise, you may compile it from the sources.
+ <para>A working installation of <ulink url="http://www.keepalived.org">keepalived</ulink>, preferibly a recent version. Check if your distribution comes with a recent packaged version. Otherwise, you may compile it from the sources.
</para>
<para>
@@ -342,7 +356,7 @@ conntrack v1.4.6 (conntrack-tools): 1 flow entries have been deleted.
</sect2>
-<sect2 id="sync-pb"><title>Active-Backup setup</title>
+<sect2 id="sync-pb"><title>Active-Backup setups</title>
<note><title>Stateful firewall architectures</title>
<para>A good reading to extend the information about firewall architectures is <ulink url="http://1984.lsi.us.es/~pablo/docs/intcomp09.pdf">Demystifying cluster-based fault-tolerant firewalls</ulink> published in IEEE Internet Computing magazine.
@@ -380,19 +394,19 @@ conntrack v1.4.6 (conntrack-tools): 1 flow entries have been deleted.
</sect2>
-<sect2 id="sync-aa"><title>Active-Active setup</title>
+<sect2 id="sync-aa"><title>Active-Active setups</title>
<para>The Active-Active setup consists of having more than one stateful
- firewall replicas actively filtering traffic. Thus, we reduce the resource
- waste that implies to have a backup firewall which does nothing.</para>
+ firewall actively filtering traffic. Thus, we reduce the resource
+ waste that implies to have a backup firewall which is spare.</para>
<para>We can classify the type of Active-Active setups in several
families:</para>
<itemizedlist>
<listitem>
- <para><emphasis>Symmetric path routing</emphasis>: The stateful firewall
- replicas share the workload in terms of flows, ie. the packets that are
+ <para><emphasis>Symmetric path routing</emphasis>: The stateful firewalls
+ share the workload in terms of flows, ie. the packets that are
part of a flow are always filtered by the same firewall.</para>
</listitem>
<listitem>
@@ -406,24 +420,20 @@ conntrack v1.4.6 (conntrack-tools): 1 flow entries have been deleted.
</listitem>
</itemizedlist>
- <para>As for 0.9.8, the design of <emphasis>conntrackd</emphasis> allows you
- to deploy an symmetric Active-Active setup based on a static approach.
- For example, assume that you have two virtual IPs, vIP1 and vIP2, and two
- firewall replicas, FW1 and FW2. You can give the virtual vIP1 to the
- firewall FW1 and the vIP2 to the FW2.
+ <para><emphasis>conntrackd</emphasis> allows you to deploy an symmetric
+Active-Active setup based on a static approach. For example, assume that you
+have two virtual IPs, vIP1 and vIP2, and two firewall replicas, FW1 and FW2.
+You can give the virtual vIP1 to the firewall FW1 and the vIP2 to the FW2.
</para>
- <para>Unfortunately, you will have to wait for the support for the
- Active-Active setup based on dynamic approach, ie. a workload sharing setup
- without directors that allow the stateful firewall share the filtering.</para>
-
- <para>On the other hand, the asymmetric scenario may work if your setup
- fulfills several strong assumptions. However, in the opinion of the author
- of this work, the asymmetric setup goes against the design of stateful
- firewalls and <emphasis>conntrackd</emphasis>. Therefore, you have two
- choices here: you can deploy an Active-Backup setup or go back to your
- old stateless rule-set (in that case, the conntrack-tools will not be
- of any help anymore, of course).</para>
+ <para>The asymmetric path scenario is hard: races might occurs between state
+ synchronization and packet forwarding. If you would like to deploy an
+ Active-Active setup with an assymmetic multi-path routing configuration,
+ then, make sure the same firewall <emphasis>forwards</emphasis> packets
+ coming in the original and the reply directions. If you cannot guarantee
+ this and you still would like to deply an Active-Active setup, then you
+ might have to consider downgrading your firewall ruleset policy to stateless
+filtering.</para>
</sect2>
@@ -895,32 +905,13 @@ maintainance.</para></listitem>
<para>The following steps describe how to enable the RPC portmapper helper for NFSv3 (this is similar for other helpers):</para>
<orderedlist>
-<listitem><para>Register user-space helper:
-
-<programlisting>
-nfct add helper rpc inet udp
-nfct add helper rpc inet tcp
-</programlisting>
-
-This registers the portmapper helper for both UDP and TCP (NFSv3 traffic goes both over TCP and UDP).
-</para></listitem>
-
-<listitem><para>Add iptables rule using the CT target:
-
-<programlisting>
-# iptables -I OUTPUT -t raw -p udp --dport 111 -j CT --helper rpc
-# iptables -I OUTPUT -t raw -p tcp --dport 111 -j CT --helper rpc
-</programlisting>
-
-With this, packets matching port TCP/UDP/111 are passed to user-space for
-inspection. If there is no instance of conntrackd configured to support
-user-space helpers, no inspection happens and packets are not sent to
-user-space.</para></listitem>
<listitem><para>Add configuration to conntrackd.conf:
<programlisting>
Helper {
+ Setup yes
+
Type rpc inet udp {
QueueNum 1
QueueLen 10240
@@ -952,6 +943,25 @@ for inspection to user-space</para>
</listitem>
+<listitem><para>Run conntrackd:
+<programlisting>
+# conntrackd -d -C /path/to/conntrackd.conf
+</programlisting>
+</para>
+</listitem>
+
+<listitem><para>Add iptables rule using the CT target:
+
+<programlisting>
+# iptables -I OUTPUT -t raw -p udp --dport 111 -j CT --helper rpc
+# iptables -I OUTPUT -t raw -p tcp --dport 111 -j CT --helper rpc
+</programlisting>
+
+With this, packets matching port TCP/UDP/111 are passed to user-space for
+inspection. If there is no instance of conntrackd configured to support
+user-space helpers, no inspection happens and packets are not sent to
+user-space.</para></listitem>
+
</orderedlist>
<para>Now you can test this (assuming you have some working NFSv3 setup) with:
diff --git a/doc/misc/README b/doc/misc/README
new file mode 100644
index 0000000..7d0a1ae
--- /dev/null
+++ b/doc/misc/README
@@ -0,0 +1,187 @@
+= Setting up active-active load-sharing hash-based stateful firewall =
+ by Pablo Neira Ayuso <pablo@netfilter.org> in 2010
+
+If you want to know more about this configuration and other firewall
+architectures, please read:
+
+* Demystifying cluster-based fault-tolerant firewalls.
+ IEEE Internet Computing, 13(6):31-38, December 2009.
+ Available at: https://perso.ens-lyon.fr/laurent.lefevre/pdf/IC2009_Neira_Gasca_Lefevre.pdf
+
+== 0x0 intro ==
+
+Under this directory you can find a script that allows you to setup a simple
+active-active hash-based load-sharing firewall cluster based on the iptables'
+cluster match.
+
+== 0x1 testbed ==
+
+My testbed looks like the following:
+
+ ---------- eth1 eth2 ----------
+ client A ------| |--- firewall 1 ----| |
+ (192.168.0.2) | switch | (.0.5) (.1.5) | switch |--- server
+ | | | | (192.168.1.2)
+ client B ------| |--- firewall 2 ----| |
+ (192.168.0.11) ---------- (.0.5) (.1.5) ----------
+ eth1 eth2
+
+The firewalls perform SNAT to masquerade clients. Note that both cluster
+firewall have the same IP addresses. For administrative purposes, it is
+a good idea that each firewall has its one IP address to SSH them, make
+sure you add the appropriate rule to skip the cluster match rule-set!
+More comments: although the picture shows two switches, I'm actually
+using one and I separated the clients and the server in two different
+VLANs.
+
+The script also sets a multicast MAC address that is the same for both
+firewalls so that the switch floods the same packets to both firewalls.
+Using a multicast MAC address is a RFC violation [1], since network node
+must not include multicast MAC address in ARP replies, but:
+
+ a) it is the only way I found so far to obtain the behaviour from my
+ HP procurve switches.
+
+ b) the VRRP MAC address range is not supported appropritely by switch
+ vendors, at least by my HP procurve switches. If switch vendors
+ support this MAC address range appropriately, they will handle them
+ as multicast MAC address. As of 2011 I did not find any switch handling
+ VRRP MAC address range as multicast ports (they still handle them as
+ normal unicast MAC addresses, therefore my solution does not work with
+ two nodes with the same VRRP MAC address).
+
+The cluster match relies upon the Connection Tracking System (conntrack).
+Thus, traffic coming in the reply direction which does not belong this node
+is labeled as INVALID for TCP and ICMP protocols. The scripts add a rule to
+drop this traffic to avoid possible packet duplication. For UDP traffic,
+you will have to add a rule to drop NEW traffic in the reply direction
+because conntrack considers it valid. If you don't do this, both nodes
+may accept reply traffic, thus, sending duplicated packets to the client,
+which is not what you want.
+
+During my last experiments, I was using the Linux kernel 2.6.37 in the
+firewalls and the server. Everything you need to setup this configuration
+is available in stock Linux kernels. No external patches with new features
+are required.
+
+== 0x2 running scripts ==
+
+Copy the script to each node, then adjust the script variables to your
+configuration.
+
+On firewall 1:
+firewall1# ./clusterip-node1.sh start
+
+On firewall 2:
+firewall2# ./clusterip-node2.sh start
+
+== 0x3 trouble-shooting ==
+
+Some troubleshooting may help to understand how this setup works. Check
+the following if you experience problems:
+
+1) Check that Multicast MAC address are assigned to the NICs:
+
+firewall1$ ip maddr
+[...]
+2: eth1
+[...]
+ link 01:00:5e:00:01:01 static
+3: eth2
+[...]
+ link 01:00:5e:00:01:02 static
+
+The scripts add the multicast MAC addresses to the NICs, if this
+is not done the traffic will be discarded by the firewalls'
+networking stack.
+
+2) ICMP ping the server from one the clients:
+
+client$ ping -c 1 192.168.1.2
+PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data.
+64 bytes from 192.168.1.2: icmp_seq=1 ttl=63 time=0.220 ms
+
+--- 192.168.1.2 ping statistics ---
+1 packets transmitted, 1 received, 0% packet loss, time 0ms
+rtt min/avg/max/mdev = 0.220/0.220/0.220/0.000 ms
+
+If this does not work, make sure the firewalls are including the
+multicast MAC address in their ARP replies, you can check this
+by looking at the neigbour cache:
+
+client$ ip neighbour
+[...]
+192.168.0.5 dev eth1 lladdr 01:00:5e:00:01:01 REACHABLE
+
+server$ ip neighbour
+[...]
+192.168.1.5 dev eth1 lladdr 01:00:5e:00:01:02 REACHABLE
+
+firewall$ ip neighbour
+[...]
+192.168.0.5 dev eth1 lladdr 01:00:5e:00:01:01 REACHABLE
+192.168.1.5 dev eth2 lladdr 01:00:5e:00:01:02 REACHABLE
+
+3) Test TCP connections: you can use netcat to start simple connections
+between the client and the server.
+
+You can also use intensive HTTP traffic generation to test performance
+like injectX.c and httpterm from Willy Tarreau:
+
+http://1wt.eu/tools/inject/
+http://1wt.eu/tools/httpterm/
+
+clientA:~/http-client-benchmark# ./client -t 60 -u 200 -G 192.168.1.2:8000
+# hits hits/s ^h/s ^bytes kB/s errs rst tout mhtime
+ 266926 26692 26766 3881270 3779 0 0 0 0.237
+ 294067 26733 27141 3935621 3785 0 0 0 0.176
+
+clientB~/http-client-benchmark# ./client -t 30 -u 40 -G 192.168.1.2:8020
+# hits hits/s ^h/s ^bytes kB/s errs rst tout mhtime
+ 53250 17750 17368 2518448 2513 0 0 0 0.240
+ 70766 17691 17516 2539907 2505 0 0 0 0.297
+
+^h/s is the current number of HTTP petitions per second. This means
+that you get ~45000 HTTP petitions per second. In my setup, with only
+one firewall active I get ~27000 HTTP petitions per second. We obtain
+extra performance of ~66%, not that bad 8-).
+
+I have configured httpterm to send object of 0 bytes over HTTP
+to obtain the maximum number of HTTP flows. This is the worst case
+scenario in firewall load.
+
+I forgot to mention that I set CPU affinity for NICs IRQs. I've got
+two cores, one for each firewall NIC.
+
+== 0x4 report sucessful setups ==
+
+My testbed is composed of low-cost basic five years old HP proliant
+systems, you can see that the numbers are not great. I like knowing
+about numbers, I'd appreciate if you drop me a line to tell me the
+numbers that you get and your experience.
+
+== 0x5 conclusions and future works ==
+
+The cluster match allows to setup load-sharing hash-based stateful
+firewalls that is a way to avoid having a spare backup firewall as
+it happens in classical Primary-Backup setups.
+
+Still, there is some pending work to fully integrate conntrackd and HA
+managers with it (in case that you want high availability, of course).
+
+-o-
+
+[1] More specifically, it's a RFC 1812 (section 3.3.2) violation.
+It's been reported that this is a problem for CISCO routers:
+http://marc.info/?l=netfilter&m=128810399113170&w=2
+
+Michele Codutti: "The problem is the multicast MAC address that these
+routers doesn't "like". They discard any incoming packet with MAC
+multicast address to be compliant with RFC1812. The only documented
+(by Cisco) workaround is to put a fixed arp entry with the multicast
+address that maps the clustered IP in the router."
+
+If you keep reading the mailing thread, the reported problem affected
+Cisco 7200 VXR.
+
+--02/02/2010
diff --git a/doc/misc/clusterip.sh b/doc/misc/clusterip.sh
new file mode 100644
index 0000000..911f676
--- /dev/null
+++ b/doc/misc/clusterip.sh
@@ -0,0 +1,254 @@
+#!/bin/sh
+
+#
+# (C) 2009-2011 by Pablo Neira Ayuso <pneira@us.es>
+#
+# 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.
+#
+
+#
+# Here, you can find the variables that you have to change.
+#
+
+# enable this for debugging
+LOG_DEBUG=0
+
+# number of cluster node (must be unique, from 1 to N cluster nodes)
+NODE=1
+
+# this is the real MAC address of eth1
+REAL_HWADDR1=00:18:71:68:f2:34
+
+# this is the real MAC address of eth2
+REAL_HWADDR2=00:11:0a:60:e7:32
+
+#
+# These variables MUST have the same values in both cluster nodes
+#
+
+# number of nodes that belong this cluster
+TOTAL_NODES=2
+
+# this is the cluster multicast MAC address of eth1
+MC_HWADDR1=01:00:5e:00:01:01
+
+# this is the cluster multicast MAC address of eth2
+MC_HWADDR2=01:00:5e:00:01:02
+
+# cluster IP address of eth1
+ADDR1=192.168.0.5/24
+
+# cluster IP address of eth2
+ADDR2=192.168.1.5/24
+
+# random seed for hashing
+SEED=0xdeadbeef
+
+start_cluster_address()
+{
+ # set cluster IP addresses
+ ip a a $ADDR1 dev eth1
+ ip a a $ADDR2 dev eth2
+ # set cluster multicast MAC addresses
+ ip maddr add $MC_HWADDR1 dev eth1
+ ip maddr add $MC_HWADDR2 dev eth2
+ # mangle ARP replies to include the cluster multicast MAC addresses
+ arptables -I OUTPUT -o eth1 --h-length 6 \
+ -j mangle --mangle-mac-s $MC_HWADDR1
+ # mangle ARP request to use the original MAC address (otherwise the
+ # stack drops this packet).
+ arptables -I INPUT -i eth1 --h-length 6 --destination-mac \
+ $MC_HWADDR1 -j mangle --mangle-mac-d $REAL_HWADDR1
+ arptables -I OUTPUT -o eth2 --h-length 6 \
+ -j mangle --mangle-mac-s $MC_HWADDR2
+ arptables -I INPUT -i eth2 --h-length 6 --destination-mac \
+ $MC_HWADDR2 -j mangle --mangle-mac-d $REAL_HWADDR2
+}
+
+stop_cluster_address()
+{
+ # delete cluster IP addresses
+ ip a d $ADDR1 dev eth1
+ ip a d $ADDR2 dev eth2
+ # delete cluster multicast MAC addresses
+ ip maddr del $MC_HWADDR1 dev eth1
+ ip maddr del $MC_HWADDR2 dev eth2
+ # delete ARP replies mangling
+ arptables -D OUTPUT -o eth1 --h-length 6 \
+ -j mangle --mangle-mac-s $MC_HWADDR1
+ # delete ARP requests mangling
+ arptables -D INPUT -i eth1 --h-length 6 --destination-mac \
+ $MC_HWADDR1 -j mangle --mangle-mac-d $REAL_HWADDR1
+ arptables -D OUTPUT -o eth2 --h-length 6 \
+ -j mangle --mangle-mac-s $MC_HWADDR2
+ arptables -D INPUT -i eth2 --h-length 6 --destination-mac \
+ $MC_HWADDR2 -j mangle --mangle-mac-d $REAL_HWADDR2
+}
+
+start_nat()
+{
+ iptables -A POSTROUTING -t nat -s 192.168.0.11 \
+ -j SNAT --to-source 192.168.1.5
+ iptables -A POSTROUTING -t nat -s 192.168.0.2 \
+ -j SNAT --to-source 192.168.1.5
+}
+
+stop_nat()
+{
+ iptables -D POSTROUTING -t nat -s 192.168.0.11 \
+ -j SNAT --to-source 192.168.1.5
+ iptables -D POSTROUTING -t nat -s 192.168.0.2 \
+ -j SNAT --to-source 192.168.1.5
+}
+
+iptables_start_cluster_rules()
+{
+ # mark packets that belong to this node (go direction)
+ iptables -A CLUSTER-RULES -t mangle -i eth1 -m cluster \
+ --cluster-total-nodes $TOTAL_NODES --cluster-local-node $1 \
+ --cluster-hash-seed $SEED -j MARK --set-mark 0xffff
+
+ # mark packet that belong to this node (reply direction)
+ # note: we *do* need this to change the packet type to PACKET_HOST,
+ # otherwise the stack silently drops the packet.
+ iptables -A CLUSTER-RULES -t mangle -i eth2 -m cluster \
+ --cluster-total-nodes $TOTAL_NODES --cluster-local-node $1 \
+ --cluster-hash-seed $SEED -j MARK --set-mark 0xffff
+}
+
+iptables_stop_cluster_rules()
+{
+ iptables -D CLUSTER-RULES -t mangle -i eth1 -m cluster \
+ --cluster-total-nodes $TOTAL_NODES --cluster-local-node $1 \
+ --cluster-hash-seed $SEED -j MARK --set-mark 0xffff
+
+ iptables -D CLUSTER-RULES -t mangle -i eth2 -m cluster \
+ --cluster-total-nodes $TOTAL_NODES --cluster-local-node $1 \
+ --cluster-hash-seed $SEED -j MARK --set-mark 0xffff
+}
+
+start_cluster_ruleset() {
+ iptables -N CLUSTER-RULES -t mangle
+
+ iptables_start_cluster_rules $NODE
+
+ iptables -A PREROUTING -t mangle -j CLUSTER-RULES
+
+ if [ $LOG_DEBUG -eq 1 ]
+ then
+ iptables -A PREROUTING -t mangle -i eth1 -m mark \
+ --mark 0xffff -j LOG --log-prefix "cluster-accept: "
+ iptables -A PREROUTING -t mangle -i eth1 -m mark \
+ ! --mark 0xffff -j LOG --log-prefix "cluster-drop: "
+ iptables -A PREROUTING -t mangle -i eth2 -m mark \
+ --mark 0xffff \
+ -j LOG --log-prefix "cluster-reply-accept: "
+ iptables -A PREROUTING -t mangle -i eth2 -m mark \
+ ! --mark 0xffff \
+ -j LOG --log-prefix "cluster-reply-drop: "
+ fi
+
+ # drop packets that don't belong to us (go direction)
+ iptables -A PREROUTING -t mangle -i eth1 -m mark \
+ ! --mark 0xffff -j DROP
+
+ # drop packets that don't belong to us (reply direction)
+ iptables -A PREROUTING -t mangle -i eth2 -m mark \
+ ! --mark 0xffff -j DROP
+}
+
+stop_cluster_ruleset() {
+ iptables -D PREROUTING -t mangle -j CLUSTER-RULES
+
+ if [ $LOG_DEBUG -eq 1 ]
+ then
+ iptables -D PREROUTING -t mangle -i eth1 -m mark \
+ --mark 0xffff -j LOG --log-prefix "cluster-accept: "
+ iptables -D PREROUTING -t mangle -i eth1 -m mark \
+ ! --mark 0xffff -j LOG --log-prefix "cluster-drop: "
+ iptables -D PREROUTING -t mangle -i eth2 -m mark \
+ --mark 0xffff \
+ -j LOG --log-prefix "cluster-reply-accept: "
+ iptables -D PREROUTING -t mangle -i eth2 -m mark \
+ ! --mark 0xffff \
+ -j LOG --log-prefix "cluster-reply-drop: "
+ fi
+
+ iptables -D PREROUTING -t mangle -i eth1 -m mark \
+ ! --mark 0xffff -j DROP
+
+ iptables -D PREROUTING -t mangle -i eth2 -m mark \
+ ! --mark 0xffff -j DROP
+
+ iptables_stop_cluster_rules $NODE
+
+ iptables -F CLUSTER-RULES -t mangle
+ iptables -X CLUSTER-RULES -t mangle
+}
+
+case "$1" in
+start)
+ echo "starting cluster configuration for node $NODE."
+
+ # just in case that you forget it
+ echo 1 > /proc/sys/net/ipv4/ip_forward
+
+ # disable TCP pickup
+ echo 0 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_be_liberal
+ echo 0 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_loose
+
+ start_cluster_address
+ start_nat
+
+ # drop invalid flows from eth2 (not allowed). This is mandatory
+ # because traffic which does not belong to this node is always
+ # labeled as INVALID by TCP and ICMP state tracking. For protocols like
+ # UDP, you will have to drop NEW traffic from eth2, otherwise reply
+ # traffic may be accepted by both nodes, thus duplicating the traffic.
+ iptables -A PREROUTING -t mangle -i eth2 \
+ -m state --state INVALID -j DROP
+
+ start_cluster_ruleset
+ ;;
+stop)
+ echo "stopping cluster configuration for node $NODE."
+
+ stop_cluster_address
+ stop_nat
+
+ iptables -D PREROUTING -t mangle -i eth2 \
+ -m state --state INVALID -j DROP
+
+ stop_cluster_ruleset
+ ;;
+primary)
+ logger "cluster-match-script: entering MASTER state for node $2"
+ if [ -x $CONNTRACKD_SCRIPT ]
+ then
+ sh $CONNTRACKD_SCRIPT primary $NODE $2
+ fi
+ iptables_start_cluster_rules $2
+ ;;
+backup)
+ logger "cluster-match-script: entering BACKUP state for node $2"
+ if [ -x $CONNTRACKD_SCRIPT ]
+ then
+ sh $CONNTRACKD_SCRIPT backup $NODE $2
+ fi
+ iptables_stop_cluster_rules $2
+ ;;
+fault)
+ logger "cluster-match-script: entering FAULT state for node $2"
+ if [ -x $CONNTRACKD_SCRIPT ]
+ then
+ sh $CONNTRACKD_SCRIPT fault $NODE $2
+ fi
+ iptables_stop_cluster_rules $2
+ ;;
+*)
+ echo "$0 start|stop|add|del [nodeid]"
+ ;;
+esac
diff --git a/extensions/libct_proto_dccp.c b/extensions/libct_proto_dccp.c
index f6258ad..0204929 100644
--- a/extensions/libct_proto_dccp.c
+++ b/extensions/libct_proto_dccp.c
@@ -67,22 +67,23 @@ static const char *dccp_optflags[DCCP_OPT_MAX] = {
static char dccp_commands_v_options[NUMBER_OF_CMD][DCCP_OPT_MAX] =
/* Well, it's better than "Re: Sevilla vs Betis" */
{
- /* 1 2 3 4 5 6 7 8 9 10*/
-/*CT_LIST*/ {2,2,2,2,0,0,2,0,0,0},
-/*CT_CREATE*/ {3,3,3,3,0,0,1,0,0,1},
-/*CT_UPDATE*/ {2,2,2,2,0,0,2,0,0,0},
-/*CT_DELETE*/ {2,2,2,2,0,0,2,0,0,0},
-/*CT_GET*/ {3,3,3,3,0,0,2,0,0,0},
-/*CT_FLUSH*/ {0,0,0,0,0,0,0,0,0,0},
-/*CT_EVENT*/ {2,2,2,2,0,0,2,0,0,0},
-/*CT_VERSION*/ {0,0,0,0,0,0,0,0,0,0},
-/*CT_HELP*/ {0,0,0,0,0,0,0,0,0,0},
-/*EXP_LIST*/ {0,0,0,0,0,0,0,0,0,0},
-/*EXP_CREATE*/ {1,1,0,0,1,1,0,1,1,1},
-/*EXP_DELETE*/ {1,1,1,1,0,0,0,0,0,0},
-/*EXP_GET*/ {1,1,1,1,0,0,0,0,0,0},
-/*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0,0},
-/*EXP_EVENT*/ {0,0,0,0,0,0,0,0,0,0},
+ /* 1 2 3 4 5 6 7 8 9 10 */
+ [CT_LIST_BIT] = {2,2,2,2,0,0,2,0,0,0},
+ [CT_CREATE_BIT] = {3,3,3,3,0,0,1,0,0,1},
+ [CT_UPDATE_BIT] = {2,2,2,2,0,0,2,0,0,0},
+ [CT_DELETE_BIT] = {2,2,2,2,0,0,2,0,0,0},
+ [CT_GET_BIT] = {3,3,3,3,0,0,2,0,0,0},
+ [CT_FLUSH_BIT] = {0,0,0,0,0,0,0,0,0,0},
+ [CT_EVENT_BIT] = {2,2,2,2,0,0,2,0,0,0},
+ [CT_VERSION_BIT] = {0,0,0,0,0,0,0,0,0,0},
+ [CT_HELP_BIT] = {0,0,0,0,0,0,0,0,0,0},
+ [EXP_LIST_BIT] = {0,0,0,0,0,0,0,0,0,0},
+ [EXP_CREATE_BIT] = {1,1,0,0,1,1,0,1,1,1},
+ [EXP_DELETE_BIT] = {1,1,1,1,0,0,0,0,0,0},
+ [EXP_GET_BIT] = {1,1,1,1,0,0,0,0,0,0},
+ [EXP_FLUSH_BIT] = {0,0,0,0,0,0,0,0,0,0},
+ [EXP_EVENT_BIT] = {0,0,0,0,0,0,0,0,0,0},
+ [CT_ADD_BIT] = {3,3,3,3,0,0,1,0,0,1},
};
static const char *dccp_states[DCCP_CONNTRACK_MAX] = {
@@ -198,6 +199,22 @@ static int parse_options(char c,
return 1;
}
+
+static const char *dccp_roles[__DCCP_CONNTRACK_ROLE_MAX] = {
+ [DCCP_CONNTRACK_ROLE_CLIENT] = "client",
+ [DCCP_CONNTRACK_ROLE_SERVER] = "server",
+};
+
+static const struct ct_print_opts dccp_print_opts[] = {
+ { "--sport", ATTR_ORIG_PORT_SRC, CT_ATTR_TYPE_BE16, 0, NULL },
+ { "--dport", ATTR_ORIG_PORT_DST, CT_ATTR_TYPE_BE16, 0, NULL },
+ { "--reply-port-src", ATTR_REPL_PORT_SRC, CT_ATTR_TYPE_BE16, 0, NULL },
+ { "--reply-port-dst", ATTR_REPL_PORT_DST, CT_ATTR_TYPE_BE16, 0, NULL },
+ { "--state", ATTR_DCCP_STATE, CT_ATTR_TYPE_U8, DCCP_CONNTRACK_MAX, dccp_states },
+ { "--role", ATTR_DCCP_ROLE, CT_ATTR_TYPE_U8, __DCCP_CONNTRACK_ROLE_MAX, dccp_roles },
+ {},
+};
+
#define DCCP_VALID_FLAGS_MAX 2
static unsigned int dccp_valid_flags[DCCP_VALID_FLAGS_MAX] = {
CT_DCCP_ORIG_SPORT | CT_DCCP_ORIG_DPORT,
@@ -235,6 +252,7 @@ static struct ctproto_handler dccp = {
.protonum = IPPROTO_DCCP,
.parse_opts = parse_options,
.final_check = final_check,
+ .print_opts = dccp_print_opts,
.help = help,
.opts = opts,
.version = VERSION,
diff --git a/extensions/libct_proto_gre.c b/extensions/libct_proto_gre.c
index 2dc63d1..2f216b9 100644
--- a/extensions/libct_proto_gre.c
+++ b/extensions/libct_proto_gre.c
@@ -66,22 +66,23 @@ static void help(void)
static char gre_commands_v_options[NUMBER_OF_CMD][GRE_OPT_MAX] =
{
- /* 1 2 3 4 5 6 7 8 */
-/*CT_LIST*/ {2,2,2,2,0,0,0,0},
-/*CT_CREATE*/ {3,3,3,3,0,0,0,0},
-/*CT_UPDATE*/ {2,2,2,2,0,0,0,0},
-/*CT_DELETE*/ {2,2,2,2,0,0,0,0},
-/*CT_GET*/ {3,3,3,3,0,0,0,0},
-/*CT_FLUSH*/ {0,0,0,0,0,0,0,0},
-/*CT_EVENT*/ {2,2,2,2,0,0,0,0},
-/*CT_VERSION*/ {0,0,0,0,0,0,0,0},
-/*CT_HELP*/ {0,0,0,0,0,0,0,0},
-/*EXP_LIST*/ {0,0,0,0,0,0,0,0},
-/*EXP_CREATE*/ {1,1,1,1,1,1,1,1},
-/*EXP_DELETE*/ {1,1,1,1,0,0,0,0},
-/*EXP_GET*/ {1,1,1,1,0,0,0,0},
-/*EXP_FLUSH*/ {0,0,0,0,0,0,0,0},
-/*EXP_EVENT*/ {0,0,0,0,0,0,0,0},
+ /* 1 2 3 4 5 6 7 8 */
+ [CT_LIST_BIT] = {2,2,2,2,0,0,0,0},
+ [CT_CREATE_BIT] = {3,3,3,3,0,0,0,0},
+ [CT_UPDATE_BIT] = {2,2,2,2,0,0,0,0},
+ [CT_DELETE_BIT] = {2,2,2,2,0,0,0,0},
+ [CT_GET_BIT] = {3,3,3,3,0,0,0,0},
+ [CT_FLUSH_BIT] = {0,0,0,0,0,0,0,0},
+ [CT_EVENT_BIT] = {2,2,2,2,0,0,0,0},
+ [CT_VERSION_BIT] = {0,0,0,0,0,0,0,0},
+ [CT_HELP_BIT] = {0,0,0,0,0,0,0,0},
+ [EXP_LIST_BIT] = {0,0,0,0,0,0,0,0},
+ [EXP_CREATE_BIT] = {1,1,1,1,1,1,1,1},
+ [EXP_DELETE_BIT] = {1,1,1,1,0,0,0,0},
+ [EXP_GET_BIT] = {1,1,1,1,0,0,0,0},
+ [EXP_FLUSH_BIT] = {0,0,0,0,0,0,0,0},
+ [EXP_EVENT_BIT] = {0,0,0,0,0,0,0,0},
+ [CT_ADD_BIT] = {3,3,3,3,0,0,0,0},
};
static int parse_options(char c,
@@ -144,6 +145,14 @@ static int parse_options(char c,
return 1;
}
+static const struct ct_print_opts gre_print_opts[] = {
+ { "--srckey", ATTR_ORIG_PORT_SRC, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--dstkey", ATTR_ORIG_PORT_DST, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--reply-key-src", ATTR_REPL_PORT_SRC, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--reply-key-dst", ATTR_REPL_PORT_DST, CT_ATTR_TYPE_BE16, 0, 0 },
+ {},
+};
+
#define GRE_VALID_FLAGS_MAX 2
static unsigned int gre_valid_flags[GRE_VALID_FLAGS_MAX] = {
CT_GRE_ORIG_SKEY | CT_GRE_ORIG_DKEY,
@@ -181,6 +190,7 @@ static struct ctproto_handler gre = {
.protonum = IPPROTO_GRE,
.parse_opts = parse_options,
.final_check = final_check,
+ .print_opts = gre_print_opts,
.help = help,
.opts = opts,
.version = VERSION,
diff --git a/extensions/libct_proto_icmp.c b/extensions/libct_proto_icmp.c
index 2ce1c65..9f67cf4 100644
--- a/extensions/libct_proto_icmp.c
+++ b/extensions/libct_proto_icmp.c
@@ -40,22 +40,23 @@ static const char *icmp_optflags[ICMP_NUMBER_OF_OPT] = {
static char icmp_commands_v_options[NUMBER_OF_CMD][ICMP_NUMBER_OF_OPT] =
/* Well, it's better than "Re: Maradona vs Pele" */
{
- /* 1 2 3 */
-/*CT_LIST*/ {2,2,2},
-/*CT_CREATE*/ {1,1,2},
-/*CT_UPDATE*/ {2,2,2},
-/*CT_DELETE*/ {2,2,2},
-/*CT_GET*/ {1,1,2},
-/*CT_FLUSH*/ {0,0,0},
-/*CT_EVENT*/ {2,2,2},
-/*CT_VERSION*/ {0,0,0},
-/*CT_HELP*/ {0,0,0},
-/*EXP_LIST*/ {0,0,0},
-/*EXP_CREATE*/ {0,0,0},
-/*EXP_DELETE*/ {0,0,0},
-/*EXP_GET*/ {0,0,0},
-/*EXP_FLUSH*/ {0,0,0},
-/*EXP_EVENT*/ {0,0,0},
+ /* 1 2 3 */
+ [CT_LIST_BIT] = {2,2,2},
+ [CT_CREATE_BIT] = {1,1,2},
+ [CT_UPDATE_BIT] = {2,2,2},
+ [CT_DELETE_BIT] = {2,2,2},
+ [CT_GET_BIT] = {1,1,2},
+ [CT_FLUSH_BIT] = {0,0,0},
+ [CT_EVENT_BIT] = {2,2,2},
+ [CT_VERSION_BIT] = {0,0,0},
+ [CT_HELP_BIT] = {0,0,0},
+ [EXP_LIST_BIT] = {0,0,0},
+ [EXP_CREATE_BIT] = {0,0,0},
+ [EXP_DELETE_BIT] = {0,0,0},
+ [EXP_GET_BIT] = {0,0,0},
+ [EXP_FLUSH_BIT] = {0,0,0},
+ [EXP_EVENT_BIT] = {0,0,0},
+ [CT_ADD_BIT] = {1,1,2},
};
static void help(void)
@@ -78,24 +79,37 @@ static int parse(char c,
tmp = atoi(optarg);
nfct_set_attr_u8(ct, ATTR_ICMP_TYPE, tmp);
nfct_set_attr_u8(ct, ATTR_L4PROTO, IPPROTO_ICMP);
+ if (nfct_attr_is_set(ct, ATTR_REPL_L3PROTO))
+ nfct_set_attr_u8(ct, ATTR_REPL_L4PROTO, IPPROTO_ICMP);
*flags |= CT_ICMP_TYPE;
break;
case '2':
tmp = atoi(optarg);
nfct_set_attr_u8(ct, ATTR_ICMP_CODE, tmp);
nfct_set_attr_u8(ct, ATTR_L4PROTO, IPPROTO_ICMP);
+ if (nfct_attr_is_set(ct, ATTR_REPL_L3PROTO))
+ nfct_set_attr_u8(ct, ATTR_REPL_L4PROTO, IPPROTO_ICMP);
*flags |= CT_ICMP_CODE;
break;
case '3':
id = htons(atoi(optarg));
nfct_set_attr_u16(ct, ATTR_ICMP_ID, id);
nfct_set_attr_u8(ct, ATTR_L4PROTO, IPPROTO_ICMP);
+ if (nfct_attr_is_set(ct, ATTR_REPL_L3PROTO))
+ nfct_set_attr_u8(ct, ATTR_REPL_L4PROTO, IPPROTO_ICMP);
*flags |= CT_ICMP_ID;
break;
}
return 1;
}
+static const struct ct_print_opts icmp_print_opts[] = {
+ { "--icmp-type", ATTR_ICMP_TYPE, CT_ATTR_TYPE_U8, 0, 0 },
+ { "--icmp-code", ATTR_ICMP_CODE, CT_ATTR_TYPE_U8, 0, 0 },
+ { "--icmp-id", ATTR_ICMP_ID, CT_ATTR_TYPE_BE16, 0, 0 },
+ {}
+};
+
static void final_check(unsigned int flags,
unsigned int cmd,
struct nf_conntrack *ct)
@@ -111,6 +125,7 @@ static struct ctproto_handler icmp = {
.protonum = IPPROTO_ICMP,
.parse_opts = parse,
.final_check = final_check,
+ .print_opts = icmp_print_opts,
.help = help,
.opts = opts,
.version = VERSION,
diff --git a/extensions/libct_proto_icmpv6.c b/extensions/libct_proto_icmpv6.c
index 18dd3e5..216757e 100644
--- a/extensions/libct_proto_icmpv6.c
+++ b/extensions/libct_proto_icmpv6.c
@@ -43,22 +43,23 @@ static const char *icmpv6_optflags[ICMPV6_NUMBER_OF_OPT] = {
static char icmpv6_commands_v_options[NUMBER_OF_CMD][ICMPV6_NUMBER_OF_OPT] =
/* Well, it's better than "Re: Maradona vs Pele" */
{
- /* 1 2 3 */
-/*CT_LIST*/ {2,2,2},
-/*CT_CREATE*/ {1,1,2},
-/*CT_UPDATE*/ {2,2,2},
-/*CT_DELETE*/ {2,2,2},
-/*CT_GET*/ {1,1,2},
-/*CT_FLUSH*/ {0,0,0},
-/*CT_EVENT*/ {2,2,2},
-/*CT_VERSION*/ {0,0,0},
-/*CT_HELP*/ {0,0,0},
-/*EXP_LIST*/ {0,0,0},
-/*EXP_CREATE*/ {0,0,0},
-/*EXP_DELETE*/ {0,0,0},
-/*EXP_GET*/ {0,0,0},
-/*EXP_FLUSH*/ {0,0,0},
-/*EXP_EVENT*/ {0,0,0},
+ /* 1 2 3 */
+ [CT_LIST_BIT] = {2,2,2},
+ [CT_CREATE_BIT] = {1,1,2},
+ [CT_UPDATE_BIT] = {2,2,2},
+ [CT_DELETE_BIT] = {2,2,2},
+ [CT_GET_BIT] = {1,1,2},
+ [CT_FLUSH_BIT] = {0,0,0},
+ [CT_EVENT_BIT] = {2,2,2},
+ [CT_VERSION_BIT] = {0,0,0},
+ [CT_HELP_BIT] = {0,0,0},
+ [EXP_LIST_BIT] = {0,0,0},
+ [EXP_CREATE_BIT] = {0,0,0},
+ [EXP_DELETE_BIT] = {0,0,0},
+ [EXP_GET_BIT] = {0,0,0},
+ [EXP_FLUSH_BIT] = {0,0,0},
+ [EXP_EVENT_BIT] = {0,0,0},
+ [CT_ADD_BIT] = {1,1,2},
};
static void help(void)
@@ -81,24 +82,37 @@ static int parse(char c,
tmp = atoi(optarg);
nfct_set_attr_u8(ct, ATTR_ICMP_TYPE, tmp);
nfct_set_attr_u8(ct, ATTR_L4PROTO, IPPROTO_ICMPV6);
+ if (nfct_attr_is_set(ct, ATTR_REPL_L3PROTO))
+ nfct_set_attr_u8(ct, ATTR_REPL_L4PROTO, IPPROTO_ICMPV6);
*flags |= CT_ICMP_TYPE;
break;
case '2':
tmp = atoi(optarg);
nfct_set_attr_u8(ct, ATTR_ICMP_CODE, tmp);
nfct_set_attr_u8(ct, ATTR_L4PROTO, IPPROTO_ICMPV6);
+ if (nfct_attr_is_set(ct, ATTR_REPL_L3PROTO))
+ nfct_set_attr_u8(ct, ATTR_REPL_L4PROTO, IPPROTO_ICMPV6);
*flags |= CT_ICMP_CODE;
break;
case '3':
id = htons(atoi(optarg));
nfct_set_attr_u16(ct, ATTR_ICMP_ID, id);
nfct_set_attr_u8(ct, ATTR_L4PROTO, IPPROTO_ICMPV6);
+ if (nfct_attr_is_set(ct, ATTR_REPL_L3PROTO))
+ nfct_set_attr_u8(ct, ATTR_REPL_L4PROTO, IPPROTO_ICMPV6);
*flags |= CT_ICMP_ID;
break;
}
return 1;
}
+static const struct ct_print_opts icmpv6_print_opts[] = {
+ {"--icmpv6-type", ATTR_ICMP_TYPE, CT_ATTR_TYPE_U8, 0, 0},
+ {"--icmpv6-code", ATTR_ICMP_CODE, CT_ATTR_TYPE_U8, 0, 0},
+ {"--icmpv6-id", ATTR_ICMP_ID, CT_ATTR_TYPE_BE16, 0, 0},
+ {},
+};
+
static void final_check(unsigned int flags,
unsigned int cmd,
struct nf_conntrack *ct)
@@ -113,6 +127,7 @@ static struct ctproto_handler icmpv6 = {
.protonum = IPPROTO_ICMPV6,
.parse_opts = parse,
.final_check = final_check,
+ .print_opts = icmpv6_print_opts,
.help = help,
.opts = opts,
.version = VERSION,
diff --git a/extensions/libct_proto_sctp.c b/extensions/libct_proto_sctp.c
index 04828bf..8099b83 100644
--- a/extensions/libct_proto_sctp.c
+++ b/extensions/libct_proto_sctp.c
@@ -70,22 +70,23 @@ static const char *sctp_optflags[SCTP_OPT_MAX] = {
static char sctp_commands_v_options[NUMBER_OF_CMD][SCTP_OPT_MAX] =
/* Well, it's better than "Re: Sevilla vs Betis" */
{
- /* 1 2 3 4 5 6 7 8 9 10 11*/
-/*CT_LIST*/ {2,2,2,2,0,0,2,0,0,0,0},
-/*CT_CREATE*/ {3,3,3,3,0,0,1,0,0,1,1},
-/*CT_UPDATE*/ {2,2,2,2,0,0,2,0,0,2,2},
-/*CT_DELETE*/ {2,2,2,2,0,0,2,0,0,0,0},
-/*CT_GET*/ {3,3,3,3,0,0,2,0,0,2,2},
-/*CT_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0},
-/*CT_EVENT*/ {2,2,2,2,0,0,2,0,0,0,0},
-/*CT_VERSION*/ {0,0,0,0,0,0,0,0,0,0,0},
-/*CT_HELP*/ {0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_LIST*/ {0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_CREATE*/ {1,1,0,0,1,1,0,1,1,1,1},
-/*EXP_DELETE*/ {1,1,1,1,0,0,0,0,0,0,0},
-/*EXP_GET*/ {1,1,1,1,0,0,0,0,0,0,0},
-/*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_EVENT*/ {0,0,0,0,0,0,0,0,0,0,0},
+ /* 1 2 3 4 5 6 7 8 9 10 11 */
+ [CT_LIST_BIT] = {2,2,2,2,0,0,2,0,0,0,0},
+ [CT_CREATE_BIT] = {3,3,3,3,0,0,1,0,0,1,1},
+ [CT_UPDATE_BIT] = {2,2,2,2,0,0,2,0,0,2,2},
+ [CT_DELETE_BIT] = {2,2,2,2,0,0,2,0,0,0,0},
+ [CT_GET_BIT] = {3,3,3,3,0,0,2,0,0,2,2},
+ [CT_FLUSH_BIT] = {0,0,0,0,0,0,0,0,0,0,0},
+ [CT_EVENT_BIT] = {2,2,2,2,0,0,2,0,0,0,0},
+ [CT_VERSION_BIT] = {0,0,0,0,0,0,0,0,0,0,0},
+ [CT_HELP_BIT] = {0,0,0,0,0,0,0,0,0,0,0},
+ [EXP_LIST_BIT] = {0,0,0,0,0,0,0,0,0,0,0},
+ [EXP_CREATE_BIT] = {1,1,0,0,1,1,0,1,1,1,1},
+ [EXP_DELETE_BIT] = {1,1,1,1,0,0,0,0,0,0,0},
+ [EXP_GET_BIT] = {1,1,1,1,0,0,0,0,0,0,0},
+ [EXP_FLUSH_BIT] = {0,0,0,0,0,0,0,0,0,0,0},
+ [EXP_EVENT_BIT] = {0,0,0,0,0,0,0,0,0,0,0},
+ [CT_ADD_BIT] = {3,3,3,3,0,0,1,0,0,1,1},
};
static const char *sctp_states[SCTP_CONNTRACK_MAX] = {
@@ -198,6 +199,17 @@ parse_options(char c, struct nf_conntrack *ct,
return 1;
}
+static const struct ct_print_opts sctp_print_opts[] = {
+ { "--sport", ATTR_ORIG_PORT_SRC, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--dport", ATTR_ORIG_PORT_DST, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--reply-port-src", ATTR_REPL_PORT_SRC, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--reply-port-dst", ATTR_REPL_PORT_DST, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--state", ATTR_SCTP_STATE, CT_ATTR_TYPE_U8, SCTP_CONNTRACK_MAX, sctp_states },
+ { "--orig-vtag", ATTR_SCTP_VTAG_ORIG, CT_ATTR_TYPE_BE32, 0, 0 },
+ { "--reply-vtag", ATTR_SCTP_VTAG_REPL, CT_ATTR_TYPE_BE32, 0, 0 },
+ {},
+};
+
#define SCTP_VALID_FLAGS_MAX 2
static unsigned int dccp_valid_flags[SCTP_VALID_FLAGS_MAX] = {
CT_SCTP_ORIG_SPORT | CT_SCTP_ORIG_DPORT,
@@ -235,6 +247,7 @@ static struct ctproto_handler sctp = {
.protonum = IPPROTO_SCTP,
.parse_opts = parse_options,
.final_check = final_check,
+ .print_opts = sctp_print_opts,
.help = help,
.opts = opts,
.version = VERSION,
diff --git a/extensions/libct_proto_tcp.c b/extensions/libct_proto_tcp.c
index 8a37a55..27f5833 100644
--- a/extensions/libct_proto_tcp.c
+++ b/extensions/libct_proto_tcp.c
@@ -54,22 +54,23 @@ static const char *tcp_optflags[TCP_NUMBER_OF_OPT] = {
static char tcp_commands_v_options[NUMBER_OF_CMD][TCP_NUMBER_OF_OPT] =
/* Well, it's better than "Re: Sevilla vs Betis" */
{
- /* 1 2 3 4 5 6 7 8 9 */
-/*CT_LIST*/ {2,2,2,2,0,0,2,0,0},
-/*CT_CREATE*/ {3,3,3,3,0,0,1,0,0},
-/*CT_UPDATE*/ {2,2,2,2,0,0,2,0,0},
-/*CT_DELETE*/ {2,2,2,2,0,0,2,0,0},
-/*CT_GET*/ {3,3,3,3,0,0,2,0,0},
-/*CT_FLUSH*/ {0,0,0,0,0,0,0,0,0},
-/*CT_EVENT*/ {2,2,2,2,0,0,2,0,0},
-/*CT_VERSION*/ {0,0,0,0,0,0,0,0,0},
-/*CT_HELP*/ {0,0,0,0,0,0,0,0,0},
-/*EXP_LIST*/ {0,0,0,0,0,0,0,0,0},
-/*EXP_CREATE*/ {1,1,0,0,1,1,0,1,1},
-/*EXP_DELETE*/ {1,1,1,1,0,0,0,0,0},
-/*EXP_GET*/ {1,1,1,1,0,0,0,0,0},
-/*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0},
-/*EXP_EVENT*/ {0,0,0,0,0,0,0,0,0},
+ /* 1 2 3 4 5 6 7 8 9 */
+ [CT_LIST_BIT] = {2,2,2,2,0,0,2,0,0},
+ [CT_CREATE_BIT] = {3,3,3,3,0,0,1,0,0},
+ [CT_UPDATE_BIT] = {2,2,2,2,0,0,2,0,0},
+ [CT_DELETE_BIT] = {2,2,2,2,0,0,2,0,0},
+ [CT_GET_BIT] = {3,3,3,3,0,0,2,0,0},
+ [CT_FLUSH_BIT] = {0,0,0,0,0,0,0,0,0},
+ [CT_EVENT_BIT] = {2,2,2,2,0,0,2,0,0},
+ [CT_VERSION_BIT] = {0,0,0,0,0,0,0,0,0},
+ [CT_HELP_BIT] = {0,0,0,0,0,0,0,0,0},
+ [EXP_LIST_BIT] = {0,0,0,0,0,0,0,0,0},
+ [EXP_CREATE_BIT] = {1,1,0,0,1,1,0,1,1},
+ [EXP_DELETE_BIT] = {1,1,1,1,0,0,0,0,0},
+ [EXP_GET_BIT] = {1,1,1,1,0,0,0,0,0},
+ [EXP_FLUSH_BIT] = {0,0,0,0,0,0,0,0,0},
+ [EXP_EVENT_BIT] = {0,0,0,0,0,0,0,0,0},
+ [CT_ADD_BIT] = {3,3,3,3,0,0,1,0,0},
};
static const char *tcp_states[TCP_CONNTRACK_MAX] = {
@@ -177,6 +178,15 @@ static int parse_options(char c,
return 1;
}
+static const struct ct_print_opts tcp_print_opts[] = {
+ { "--sport", ATTR_ORIG_PORT_SRC, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--dport", ATTR_ORIG_PORT_DST, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--reply-port-src", ATTR_REPL_PORT_SRC, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--reply-port-dst", ATTR_REPL_PORT_DST, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--state", ATTR_TCP_STATE, CT_ATTR_TYPE_U8, TCP_CONNTRACK_MAX, tcp_states },
+ {},
+};
+
#define TCP_VALID_FLAGS_MAX 2
static unsigned int tcp_valid_flags[TCP_VALID_FLAGS_MAX] = {
CT_TCP_ORIG_SPORT | CT_TCP_ORIG_DPORT,
@@ -228,6 +238,7 @@ static struct ctproto_handler tcp = {
.protonum = IPPROTO_TCP,
.parse_opts = parse_options,
.final_check = final_check,
+ .print_opts = tcp_print_opts,
.help = help,
.opts = opts,
.version = VERSION,
diff --git a/extensions/libct_proto_udp.c b/extensions/libct_proto_udp.c
index e30637c..a78857f 100644
--- a/extensions/libct_proto_udp.c
+++ b/extensions/libct_proto_udp.c
@@ -62,22 +62,23 @@ static void help(void)
static char udp_commands_v_options[NUMBER_OF_CMD][UDP_NUMBER_OF_OPT] =
/* Well, it's better than "Re: Galeano vs Vargas Llosa" */
{
- /* 1 2 3 4 5 6 7 8 */
-/*CT_LIST*/ {2,2,2,2,0,0,0,0},
-/*CT_CREATE*/ {3,3,3,3,0,0,0,0},
-/*CT_UPDATE*/ {2,2,2,2,0,0,0,0},
-/*CT_DELETE*/ {2,2,2,2,0,0,0,0},
-/*CT_GET*/ {3,3,3,3,0,0,0,0},
-/*CT_FLUSH*/ {0,0,0,0,0,0,0,0},
-/*CT_EVENT*/ {2,2,2,2,0,0,0,0},
-/*CT_VERSION*/ {0,0,0,0,0,0,0,0},
-/*CT_HELP*/ {0,0,0,0,0,0,0,0},
-/*EXP_LIST*/ {0,0,0,0,0,0,0,0},
-/*EXP_CREATE*/ {1,1,0,0,1,1,1,1},
-/*EXP_DELETE*/ {1,1,1,1,0,0,0,0},
-/*EXP_GET*/ {1,1,1,1,0,0,0,0},
-/*EXP_FLUSH*/ {0,0,0,0,0,0,0,0},
-/*EXP_EVENT*/ {0,0,0,0,0,0,0,0},
+ /* 1 2 3 4 5 6 7 8 */
+ [CT_LIST_BIT] = {2,2,2,2,0,0,0,0},
+ [CT_CREATE_BIT] = {3,3,3,3,0,0,0,0},
+ [CT_UPDATE_BIT] = {2,2,2,2,0,0,0,0},
+ [CT_DELETE_BIT] = {2,2,2,2,0,0,0,0},
+ [CT_GET_BIT] = {3,3,3,3,0,0,0,0},
+ [CT_FLUSH_BIT] = {0,0,0,0,0,0,0,0},
+ [CT_EVENT_BIT] = {2,2,2,2,0,0,0,0},
+ [CT_VERSION_BIT] = {0,0,0,0,0,0,0,0},
+ [CT_HELP_BIT] = {0,0,0,0,0,0,0,0},
+ [EXP_LIST_BIT] = {0,0,0,0,0,0,0,0},
+ [EXP_CREATE_BIT] = {1,1,0,0,1,1,1,1},
+ [EXP_DELETE_BIT] = {1,1,1,1,0,0,0,0},
+ [EXP_GET_BIT] = {1,1,1,1,0,0,0,0},
+ [EXP_FLUSH_BIT] = {0,0,0,0,0,0,0,0},
+ [EXP_EVENT_BIT] = {0,0,0,0,0,0,0,0},
+ [CT_ADD_BIT] = {3,3,3,3,0,0,0,0},
};
static int parse_options(char c,
@@ -144,6 +145,14 @@ static int parse_options(char c,
return 1;
}
+static const struct ct_print_opts udp_print_opts[] = {
+ {"--sport", ATTR_ORIG_PORT_SRC, CT_ATTR_TYPE_BE16, 0, 0},
+ {"--dport", ATTR_ORIG_PORT_DST, CT_ATTR_TYPE_BE16, 0, 0},
+ {"--reply-port-src", ATTR_REPL_PORT_SRC, CT_ATTR_TYPE_BE16, 0, 0},
+ {"--reply-port-dst", ATTR_REPL_PORT_DST, CT_ATTR_TYPE_BE16, 0, 0},
+ {},
+};
+
#define UDP_VALID_FLAGS_MAX 2
static unsigned int udp_valid_flags[UDP_VALID_FLAGS_MAX] = {
CT_UDP_ORIG_SPORT | CT_UDP_ORIG_DPORT,
@@ -181,6 +190,7 @@ static struct ctproto_handler udp = {
.protonum = IPPROTO_UDP,
.parse_opts = parse_options,
.final_check = final_check,
+ .print_opts = udp_print_opts,
.help = help,
.opts = opts,
.version = VERSION,
diff --git a/extensions/libct_proto_udplite.c b/extensions/libct_proto_udplite.c
index f46cef0..3df3142 100644
--- a/extensions/libct_proto_udplite.c
+++ b/extensions/libct_proto_udplite.c
@@ -70,22 +70,23 @@ static void help(void)
static char udplite_commands_v_options[NUMBER_OF_CMD][UDP_OPT_MAX] =
{
- /* 1 2 3 4 5 6 7 8 */
-/*CT_LIST*/ {2,2,2,2,0,0,0,0},
-/*CT_CREATE*/ {3,3,3,3,0,0,0,0},
-/*CT_UPDATE*/ {2,2,2,2,0,0,0,0},
-/*CT_DELETE*/ {2,2,2,2,0,0,0,0},
-/*CT_GET*/ {3,3,3,3,0,0,0,0},
-/*CT_FLUSH*/ {0,0,0,0,0,0,0,0},
-/*CT_EVENT*/ {2,2,2,2,0,0,0,0},
-/*CT_VERSION*/ {0,0,0,0,0,0,0,0},
-/*CT_HELP*/ {0,0,0,0,0,0,0,0},
-/*EXP_LIST*/ {0,0,0,0,0,0,0,0},
-/*EXP_CREATE*/ {1,1,0,0,1,1,1,1},
-/*EXP_DELETE*/ {1,1,1,1,0,0,0,0},
-/*EXP_GET*/ {1,1,1,1,0,0,0,0},
-/*EXP_FLUSH*/ {0,0,0,0,0,0,0,0},
-/*EXP_EVENT*/ {0,0,0,0,0,0,0,0},
+ /* 1 2 3 4 5 6 7 8 */
+ [CT_LIST_BIT] = {2,2,2,2,0,0,0,0},
+ [CT_CREATE_BIT] = {3,3,3,3,0,0,0,0},
+ [CT_UPDATE_BIT] = {2,2,2,2,0,0,0,0},
+ [CT_DELETE_BIT] = {2,2,2,2,0,0,0,0},
+ [CT_GET_BIT] = {3,3,3,3,0,0,0,0},
+ [CT_FLUSH_BIT] = {0,0,0,0,0,0,0,0},
+ [CT_EVENT_BIT] = {2,2,2,2,0,0,0,0},
+ [CT_VERSION_BIT] = {0,0,0,0,0,0,0,0},
+ [CT_HELP_BIT] = {0,0,0,0,0,0,0,0},
+ [EXP_LIST_BIT] = {0,0,0,0,0,0,0,0},
+ [EXP_CREATE_BIT] = {1,1,0,0,1,1,1,1},
+ [EXP_DELETE_BIT] = {1,1,1,1,0,0,0,0},
+ [EXP_GET_BIT] = {1,1,1,1,0,0,0,0},
+ [EXP_FLUSH_BIT] = {0,0,0,0,0,0,0,0},
+ [EXP_EVENT_BIT] = {0,0,0,0,0,0,0,0},
+ [CT_ADD_BIT] = {3,3,3,3,0,0,0,0},
};
static int parse_options(char c,
@@ -148,6 +149,14 @@ static int parse_options(char c,
return 1;
}
+static const struct ct_print_opts udplite_print_opts[] = {
+ { "--sport", ATTR_ORIG_PORT_SRC, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--dport", ATTR_ORIG_PORT_DST, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--reply-port-src", ATTR_REPL_PORT_SRC, CT_ATTR_TYPE_BE16, 0, 0 },
+ { "--reply-port-dst", ATTR_REPL_PORT_DST, CT_ATTR_TYPE_BE16, 0, 0 },
+ {},
+};
+
#define UDPLITE_VALID_FLAGS_MAX 2
static unsigned int udplite_valid_flags[UDPLITE_VALID_FLAGS_MAX] = {
CT_UDPLITE_ORIG_SPORT | CT_UDPLITE_ORIG_DPORT,
@@ -186,6 +195,7 @@ static struct ctproto_handler udplite = {
.protonum = IPPROTO_UDPLITE,
.parse_opts = parse_options,
.final_check = final_check,
+ .print_opts = udplite_print_opts,
.help = help,
.opts = opts,
.version = VERSION,
diff --git a/extensions/libct_proto_unknown.c b/extensions/libct_proto_unknown.c
index 2a47704..b877c56 100644
--- a/extensions/libct_proto_unknown.c
+++ b/extensions/libct_proto_unknown.c
@@ -21,10 +21,21 @@ static void help(void)
fprintf(stdout, " no options (unsupported)\n");
}
+static void final_check(unsigned int flags,
+ unsigned int cmd,
+ struct nf_conntrack *ct)
+{
+ if (nfct_attr_is_set(ct, ATTR_REPL_L3PROTO) &&
+ nfct_attr_is_set(ct, ATTR_L4PROTO) &&
+ !nfct_attr_is_set(ct, ATTR_REPL_L4PROTO))
+ nfct_set_attr_u8(ct, ATTR_REPL_L4PROTO, nfct_get_attr_u8(ct, ATTR_L4PROTO));
+}
+
struct ctproto_handler ct_proto_unknown = {
.name = "unknown",
.help = help,
.opts = opts,
+ .final_check = final_check,
.version = VERSION,
};
diff --git a/include/Makefile.am b/include/Makefile.am
index 352054e..4741b50 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = linux
+SUBDIRS = linux helpers
noinst_HEADERS = alarm.h jhash.h cache.h linux_list.h linux_rbtree.h \
sync.h conntrackd.h local.h udp.h tcp.h \
diff --git a/include/conntrack.h b/include/conntrack.h
index 37ccf6e..6dad4a1 100644
--- a/include/conntrack.h
+++ b/include/conntrack.h
@@ -8,7 +8,76 @@
#include <netinet/in.h>
-#define NUMBER_OF_CMD 19
+#include <linux/netfilter/nf_conntrack_common.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+
+enum ct_command {
+ CT_NONE = 0,
+
+ CT_LIST_BIT = 0,
+ CT_LIST = (1 << CT_LIST_BIT),
+
+ CT_CREATE_BIT = 1,
+ CT_CREATE = (1 << CT_CREATE_BIT),
+
+ CT_UPDATE_BIT = 2,
+ CT_UPDATE = (1 << CT_UPDATE_BIT),
+
+ CT_DELETE_BIT = 3,
+ CT_DELETE = (1 << CT_DELETE_BIT),
+
+ CT_GET_BIT = 4,
+ CT_GET = (1 << CT_GET_BIT),
+
+ CT_FLUSH_BIT = 5,
+ CT_FLUSH = (1 << CT_FLUSH_BIT),
+
+ CT_EVENT_BIT = 6,
+ CT_EVENT = (1 << CT_EVENT_BIT),
+
+ CT_VERSION_BIT = 7,
+ CT_VERSION = (1 << CT_VERSION_BIT),
+
+ CT_HELP_BIT = 8,
+ CT_HELP = (1 << CT_HELP_BIT),
+
+ EXP_LIST_BIT = 9,
+ EXP_LIST = (1 << EXP_LIST_BIT),
+
+ EXP_CREATE_BIT = 10,
+ EXP_CREATE = (1 << EXP_CREATE_BIT),
+
+ EXP_DELETE_BIT = 11,
+ EXP_DELETE = (1 << EXP_DELETE_BIT),
+
+ EXP_GET_BIT = 12,
+ EXP_GET = (1 << EXP_GET_BIT),
+
+ EXP_FLUSH_BIT = 13,
+ EXP_FLUSH = (1 << EXP_FLUSH_BIT),
+
+ EXP_EVENT_BIT = 14,
+ EXP_EVENT = (1 << EXP_EVENT_BIT),
+
+ CT_COUNT_BIT = 15,
+ CT_COUNT = (1 << CT_COUNT_BIT),
+
+ EXP_COUNT_BIT = 16,
+ EXP_COUNT = (1 << EXP_COUNT_BIT),
+
+ CT_STATS_BIT = 17,
+ CT_STATS = (1 << CT_STATS_BIT),
+
+ EXP_STATS_BIT = 18,
+ EXP_STATS = (1 << EXP_STATS_BIT),
+
+ CT_ADD_BIT = 19,
+ CT_ADD = (1 << CT_ADD_BIT),
+
+ _CT_BIT_MAX = 20,
+};
+
+#define NUMBER_OF_CMD _CT_BIT_MAX
#define NUMBER_OF_OPT 29
struct nf_conntrack;
@@ -32,6 +101,8 @@ struct ctproto_handler {
unsigned int command,
struct nf_conntrack *ct);
+ const struct ct_print_opts *print_opts;
+
void (*help)(void);
struct option *opts;
@@ -53,6 +124,31 @@ void exit_error(enum exittype status, const char *msg, ...);
extern void register_proto(struct ctproto_handler *h);
+enum ct_attr_type {
+ CT_ATTR_TYPE_NONE = 0,
+ CT_ATTR_TYPE_U8,
+ CT_ATTR_TYPE_BE16,
+ CT_ATTR_TYPE_U16,
+ CT_ATTR_TYPE_BE32,
+ CT_ATTR_TYPE_U32,
+ CT_ATTR_TYPE_U64,
+ CT_ATTR_TYPE_U32_BITMAP,
+ CT_ATTR_TYPE_IPV4,
+ CT_ATTR_TYPE_IPV6,
+};
+
+struct ct_print_opts {
+ const char *name;
+ enum nf_conntrack_attr type;
+ enum ct_attr_type datatype;
+ short val_mapping_count;
+ const char **val_mapping;
+};
+
+extern int ct_snprintf_opts(char *buf, unsigned int len,
+ const struct nf_conntrack *ct,
+ const struct ct_print_opts *attrs);
+
extern void register_tcp(void);
extern void register_udp(void);
extern void register_udplite(void);
diff --git a/include/conntrackd.h b/include/conntrackd.h
index fe9ec18..3e0d095 100644
--- a/include/conntrackd.h
+++ b/include/conntrackd.h
@@ -138,6 +138,7 @@ struct ct_conf {
} stats;
struct {
struct list_head list;
+ bool setup;
} cthelper;
};
diff --git a/include/helper.h b/include/helper.h
index d540667..08d4cf4 100644
--- a/include/helper.h
+++ b/include/helper.h
@@ -56,6 +56,7 @@ extern int in4_pton(const char *src, int srclen, uint8_t *dst, int delim, const
extern int in6_pton(const char *src, int srclen, uint8_t *dst, int delim, const char **end);
extern void helper_register(struct ctd_helper *helper);
+struct ctd_helper *__helper_find(const char *helper_name, uint8_t l4proto);
struct ctd_helper *helper_find(const char *libdir_path, const char *name, uint8_t l4proto, int flags);
#define min_t(type, x, y) ({ \
diff --git a/include/helpers/Makefile.am b/include/helpers/Makefile.am
new file mode 100644
index 0000000..99a4257
--- /dev/null
+++ b/include/helpers/Makefile.am
@@ -0,0 +1 @@
+noinst_HEADERS = ftp.h rpc.h sane.h tns.h
diff --git a/include/helpers/ftp.h b/include/helpers/ftp.h
new file mode 100644
index 0000000..50e2d0c
--- /dev/null
+++ b/include/helpers/ftp.h
@@ -0,0 +1,14 @@
+#ifndef _CTD_FTP_H
+#define _CTD_FTP_H
+
+#define NUM_SEQ_TO_REMEMBER 2
+
+/* This structure exists only once per master */
+struct ftp_info {
+ /* Valid seq positions for cmd matching after newline */
+ uint32_t seq_aft_nl[MYCT_DIR_MAX][NUM_SEQ_TO_REMEMBER];
+ /* 0 means seq_match_aft_nl not set */
+ int seq_aft_nl_num[MYCT_DIR_MAX];
+};
+
+#endif
diff --git a/include/helpers/rpc.h b/include/helpers/rpc.h
new file mode 100644
index 0000000..b0b8d17
--- /dev/null
+++ b/include/helpers/rpc.h
@@ -0,0 +1,15 @@
+#ifndef _CTD_RPC_H
+#define _CTD_RPC_H
+
+struct rpc_info {
+ /* XID */
+ uint32_t xid;
+ /* program */
+ uint32_t pm_prog;
+ /* program version */
+ uint32_t pm_vers;
+ /* transport protocol: TCP|UDP */
+ uint32_t pm_prot;
+};
+
+#endif
diff --git a/include/helpers/sane.h b/include/helpers/sane.h
new file mode 100644
index 0000000..1e70ff6
--- /dev/null
+++ b/include/helpers/sane.h
@@ -0,0 +1,13 @@
+#ifndef _CTD_SANE_H
+#define _CTD_SANE_H
+
+enum sane_state {
+ SANE_STATE_NORMAL,
+ SANE_STATE_START_REQUESTED,
+};
+
+struct nf_ct_sane_master {
+ enum sane_state state;
+};
+
+#endif
diff --git a/include/helpers/tns.h b/include/helpers/tns.h
new file mode 100644
index 0000000..60dcf25
--- /dev/null
+++ b/include/helpers/tns.h
@@ -0,0 +1,9 @@
+#ifndef _CTD_TNS_H
+#define _CTD_TNS_H
+
+struct tns_info {
+ /* Scan next DATA|REDIRECT packet */
+ bool parse;
+};
+
+#endif
diff --git a/include/local.h b/include/local.h
index 9379446..22859d7 100644
--- a/include/local.h
+++ b/include/local.h
@@ -7,12 +7,12 @@
struct local_conf {
int reuseaddr;
- char path[UNIX_PATH_MAX + 1];
+ char path[UNIX_PATH_MAX];
};
struct local_server {
int fd;
- char path[UNIX_PATH_MAX + 1];
+ char path[UNIX_PATH_MAX];
};
/* callback return values */
diff --git a/include/network.h b/include/network.h
index 95aad82..20def34 100644
--- a/include/network.h
+++ b/include/network.h
@@ -232,6 +232,7 @@ enum nta_attr {
NTA_SNAT_IPV6, /* uint32_t * 4 */
NTA_DNAT_IPV6, /* uint32_t * 4 */
NTA_SYNPROXY, /* struct nft_attr_synproxy */
+ NTA_ZONE, /* uint16_t */
NTA_MAX
};
diff --git a/nfct.8 b/nfct.8
index c38bdbe..b130a88 100644
--- a/nfct.8
+++ b/nfct.8
@@ -8,7 +8,7 @@ nfct \- command line tool to configure with the connection tracking system
.BR "nfct command subsystem [parameters]"
.SH DESCRIPTION
.B nfct
-is the command line tool that allows to configure the Connection Tracking
+is the command line tool that allows you to configure the Connection Tracking
System.
.SH COMMANDS
.TP
diff --git a/src/Makefile.am b/src/Makefile.am
index 2e66ee9..352aa37 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -6,12 +6,23 @@ endif
AM_YFLAGS = -d
-CLEANFILES = read_config_yy.c read_config_lex.c
+BUILT_SOURCES = read_config_yy.h
sbin_PROGRAMS = conntrack conntrackd nfct
conntrack_SOURCES = conntrack.c
-conntrack_LDADD = ../extensions/libct_proto_tcp.la ../extensions/libct_proto_udp.la ../extensions/libct_proto_udplite.la ../extensions/libct_proto_icmp.la ../extensions/libct_proto_icmpv6.la ../extensions/libct_proto_sctp.la ../extensions/libct_proto_dccp.la ../extensions/libct_proto_gre.la ../extensions/libct_proto_unknown.la ${LIBNETFILTER_CONNTRACK_LIBS} ${LIBMNL_LIBS} ${LIBNFNETLINK_LIBS}
+conntrack_LDADD = ../extensions/libct_proto_dccp.la \
+ ../extensions/libct_proto_gre.la \
+ ../extensions/libct_proto_icmp.la \
+ ../extensions/libct_proto_icmpv6.la \
+ ../extensions/libct_proto_sctp.la \
+ ../extensions/libct_proto_tcp.la \
+ ../extensions/libct_proto_udp.la \
+ ../extensions/libct_proto_udplite.la \
+ ../extensions/libct_proto_unknown.la \
+ ${LIBMNL_LIBS} \
+ ${LIBNETFILTER_CONNTRACK_LIBS} \
+ ${LIBNFNETLINK_LIBS}
nfct_SOURCES = nfct.c
@@ -35,24 +46,54 @@ if HAVE_CTHELPER
nfct_LDADD += ${LIBNETFILTER_CTHELPER_LIBS}
endif
-nfct_LDFLAGS = -export-dynamic ${LAZY_LDFLAGS}
-
-conntrackd_SOURCES = alarm.c main.c run.c hash.c queue.c queue_tx.c rbtree.c \
- local.c log.c mcast.c udp.c netlink.c vector.c \
- filter.c fds.c event.c process.c origin.c date.c \
- cache.c cache-ct.c cache-exp.c \
- cache_timer.c \
- ctnl.c \
- sync-mode.c sync-alarm.c sync-ftfw.c sync-notrack.c \
- traffic_stats.c stats-mode.c \
- network.c cidr.c \
- build.c parse.c \
- channel.c multichannel.c channel_mcast.c channel_udp.c \
- tcp.c channel_tcp.c \
- external_cache.c external_inject.c \
- internal_cache.c internal_bypass.c \
- read_config_yy.y read_config_lex.l \
- stack.c resync.c
+conntrackd_SOURCES = alarm.c \
+ build.c \
+ cache.c \
+ cache-ct.c \
+ cache-exp.c \
+ cache_timer.c \
+ channel.c \
+ channel_mcast.c \
+ channel_tcp.c \
+ channel_udp.c \
+ cidr.c \
+ ctnl.c \
+ date.c \
+ event.c \
+ external_cache.c \
+ external_inject.c \
+ fds.c \
+ filter.c \
+ hash.c \
+ internal_bypass.c \
+ internal_cache.c \
+ local.c \
+ log.c \
+ main.c \
+ mcast.c \
+ multichannel.c \
+ netlink.c \
+ network.c \
+ origin.c \
+ parse.c \
+ process.c \
+ queue.c \
+ queue_tx.c \
+ rbtree.c \
+ read_config_lex.l \
+ read_config_yy.y \
+ resync.c \
+ run.c \
+ stack.c \
+ stats-mode.c \
+ sync-alarm.c \
+ sync-ftfw.c \
+ sync-mode.c \
+ sync-notrack.c \
+ tcp.c \
+ traffic_stats.c \
+ udp.c \
+ vector.c
if HAVE_CTHELPER
conntrackd_SOURCES += cthelper.c helpers.c utils.c expect.c
@@ -62,9 +103,6 @@ if HAVE_SYSTEMD
conntrackd_SOURCES += systemd.c
endif
-# yacc and lex generate dirty code
-read_config_yy.o read_config_lex.o: AM_CFLAGS += -Wno-missing-prototypes -Wno-missing-declarations -Wno-implicit-function-declaration -Wno-nested-externs -Wno-undef -Wno-redundant-decls -Wno-sign-compare
-
conntrackd_LDADD = ${LIBMNL_LIBS} ${LIBNETFILTER_CONNTRACK_LIBS} \
${libdl_LIBS} ${LIBNFNETLINK_LIBS}
@@ -77,5 +115,3 @@ conntrackd_LDADD += ${LIBSYSTEMD_LIBS}
endif
conntrackd_LDFLAGS = -export-dynamic
-
-EXTRA_DIST = read_config_yy.h
diff --git a/src/build.c b/src/build.c
index 99ff230..c07974f 100644
--- a/src/build.c
+++ b/src/build.c
@@ -66,7 +66,14 @@ ct_build_u32(const struct nf_conntrack *ct, int a, struct nethdr *n, int b)
}
static inline void
-ct_build_u128(const struct nf_conntrack *ct, int a, struct nethdr *n, int b)
+ct_build_be32(const struct nf_conntrack *ct, int a, struct nethdr *n, int b)
+{
+ uint32_t data = nfct_get_attr_u32(ct, a);
+ addattr(n, b, &data, sizeof(uint32_t));
+}
+
+static inline void
+ct_build_be128(const struct nf_conntrack *ct, int a, struct nethdr *n, int b)
{
const char *data = nfct_get_attr(ct, a);
addattr(n, b, data, sizeof(uint32_t) * 4);
@@ -126,11 +133,12 @@ static enum nf_conntrack_attr nat_type[] =
/* ICMP, UDP and TCP are always loaded with nf_conntrack_ipv4 */
static void build_l4proto_tcp(const struct nf_conntrack *ct, struct nethdr *n)
{
+ ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT,
+ sizeof(struct nfct_attr_grp_port));
+
if (!nfct_attr_is_set(ct, ATTR_TCP_STATE))
return;
- ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT,
- sizeof(struct nfct_attr_grp_port));
ct_build_u8(ct, ATTR_TCP_STATE, n, NTA_TCP_STATE);
if (CONFIG(sync).tcp_window_tracking) {
ct_build_u8(ct, ATTR_TCP_WSCALE_ORIG, n, NTA_TCP_WSCALE_ORIG);
@@ -140,12 +148,13 @@ static void build_l4proto_tcp(const struct nf_conntrack *ct, struct nethdr *n)
static void build_l4proto_sctp(const struct nf_conntrack *ct, struct nethdr *n)
{
+ ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT,
+ sizeof(struct nfct_attr_grp_port));
+
/* SCTP is optional, make sure nf_conntrack_sctp is loaded */
if (!nfct_attr_is_set(ct, ATTR_SCTP_STATE))
return;
- ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT,
- sizeof(struct nfct_attr_grp_port));
ct_build_u8(ct, ATTR_SCTP_STATE, n, NTA_SCTP_STATE);
ct_build_u32(ct, ATTR_SCTP_VTAG_ORIG, n, NTA_SCTP_VTAG_ORIG);
ct_build_u32(ct, ATTR_SCTP_VTAG_REPL, n, NTA_SCTP_VTAG_REPL);
@@ -153,12 +162,13 @@ static void build_l4proto_sctp(const struct nf_conntrack *ct, struct nethdr *n)
static void build_l4proto_dccp(const struct nf_conntrack *ct, struct nethdr *n)
{
+ ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT,
+ sizeof(struct nfct_attr_grp_port));
+
/* DCCP is optional, make sure nf_conntrack_dccp is loaded */
if (!nfct_attr_is_set(ct, ATTR_DCCP_STATE))
return;
- ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT,
- sizeof(struct nfct_attr_grp_port));
ct_build_u8(ct, ATTR_DCCP_STATE, n, NTA_DCCP_STATE);
ct_build_u8(ct, ATTR_DCCP_ROLE, n, NTA_DCCP_ROLE);
}
@@ -279,18 +289,18 @@ void ct2msg(const struct nf_conntrack *ct, struct nethdr *n)
switch (nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO)) {
case AF_INET:
if (nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT))
- ct_build_u32(ct, ATTR_REPL_IPV4_DST, n, NTA_SNAT_IPV4);
+ ct_build_be32(ct, ATTR_REPL_IPV4_DST, n, NTA_SNAT_IPV4);
if (nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT))
- ct_build_u32(ct, ATTR_REPL_IPV4_SRC, n, NTA_DNAT_IPV4);
+ ct_build_be32(ct, ATTR_REPL_IPV4_SRC, n, NTA_DNAT_IPV4);
break;
case AF_INET6:
if (nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT)) {
- ct_build_u128(ct, ATTR_REPL_IPV6_DST, n,
- NTA_SNAT_IPV6);
+ ct_build_be128(ct, ATTR_REPL_IPV6_DST, n,
+ NTA_SNAT_IPV6);
}
if (nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT)) {
- ct_build_u128(ct, ATTR_REPL_IPV6_SRC, n,
- NTA_DNAT_IPV6);
+ ct_build_be128(ct, ATTR_REPL_IPV6_SRC, n,
+ NTA_DNAT_IPV6);
}
break;
default:
@@ -315,6 +325,9 @@ void ct2msg(const struct nf_conntrack *ct, struct nethdr *n)
nfct_attr_is_set(ct, ATTR_SYNPROXY_ITS) &&
nfct_attr_is_set(ct, ATTR_SYNPROXY_TSOFF))
ct_build_synproxy(ct, n);
+
+ if (nfct_attr_is_set(ct, ATTR_ZONE))
+ ct_build_u16(ct, ATTR_ZONE, n, NTA_ZONE);
}
static void
diff --git a/src/cache-ct.c b/src/cache-ct.c
index abcfde4..f56e450 100644
--- a/src/cache-ct.c
+++ b/src/cache-ct.c
@@ -34,13 +34,14 @@
static uint32_t
cache_hash4_ct(const struct nf_conntrack *ct, const struct hashtable *table)
{
- uint32_t a[4] = {
+ uint32_t a[5] = {
[0] = nfct_get_attr_u32(ct, ATTR_IPV4_SRC),
[1] = nfct_get_attr_u32(ct, ATTR_IPV4_DST),
[2] = nfct_get_attr_u8(ct, ATTR_L3PROTO) << 16 |
nfct_get_attr_u8(ct, ATTR_L4PROTO),
[3] = nfct_get_attr_u16(ct, ATTR_PORT_SRC) << 16 |
nfct_get_attr_u16(ct, ATTR_PORT_DST),
+ [4] = nfct_get_attr_u16(ct, ATTR_ZONE),
};
/*
@@ -50,13 +51,13 @@ cache_hash4_ct(const struct nf_conntrack *ct, const struct hashtable *table)
* but using a multiply, less expensive than a divide. See:
* http://www.mail-archive.com/netdev@vger.kernel.org/msg56623.html
*/
- return ((uint64_t)jhash2(a, 4, 0) * table->hashsize) >> 32;
+ return ((uint64_t)jhash2(a, 5, 0) * table->hashsize) >> 32;
}
static uint32_t
cache_hash6_ct(const struct nf_conntrack *ct, const struct hashtable *table)
{
- uint32_t a[10];
+ uint32_t a[11];
memcpy(&a[0], nfct_get_attr(ct, ATTR_IPV6_SRC), sizeof(uint32_t)*4);
memcpy(&a[4], nfct_get_attr(ct, ATTR_IPV6_DST), sizeof(uint32_t)*4);
@@ -64,8 +65,9 @@ cache_hash6_ct(const struct nf_conntrack *ct, const struct hashtable *table)
nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO);
a[9] = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) << 16 |
nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST);
+ a[10] = nfct_get_attr_u16(ct, ATTR_ZONE);
- return ((uint64_t)jhash2(a, 10, 0) * table->hashsize) >> 32;
+ return ((uint64_t)jhash2(a, 11, 0) * table->hashsize) >> 32;
}
static uint32_t
@@ -88,21 +90,12 @@ cache_ct_hash(const void *data, const struct hashtable *table)
return ret;
}
-/* master conntrack of expectations have no ID */
-static inline int
-cache_ct_cmp_id(const struct nf_conntrack *ct1, const struct nf_conntrack *ct2)
-{
- return nfct_attr_is_set(ct2, ATTR_ID) ?
- nfct_get_attr_u32(ct1, ATTR_ID) == nfct_get_attr_u32(ct2, ATTR_ID) : 1;
-}
-
static int cache_ct_cmp(const void *data1, const void *data2)
{
const struct cache_object *obj = data1;
const struct nf_conntrack *ct = data2;
- return nfct_cmp(obj->ptr, ct, NFCT_CMP_ORIG) &&
- cache_ct_cmp_id(obj->ptr, ct);
+ return nfct_cmp(obj->ptr, ct, NFCT_CMP_ORIG);
}
static void *cache_ct_alloc(void)
diff --git a/src/cache.c b/src/cache.c
index 79a024f..9bc8d0f 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -69,12 +69,12 @@ struct cache *cache_create(const char *name, enum cache_type type,
memcpy(c->feature_type, feature_type, sizeof(feature_type));
- c->features = malloc(sizeof(struct cache_feature) * j);
+ c->features = malloc(sizeof(struct cache_feature *) * j);
if (!c->features) {
free(c);
return NULL;
}
- memcpy(c->features, feature_array, sizeof(struct cache_feature) * j);
+ memcpy(c->features, feature_array, sizeof(struct cache_feature *) * j);
c->num_features = j;
c->extra_offset = size;
diff --git a/src/conntrack.c b/src/conntrack.c
index fb4e5be..0d71352 100644
--- a/src/conntrack.c
+++ b/src/conntrack.c
@@ -41,6 +41,7 @@
#include "conntrack.h"
#include <stdio.h>
+#include <assert.h>
#include <getopt.h>
#include <stdlib.h>
#include <ctype.h>
@@ -53,6 +54,8 @@
#include <sys/socket.h>
#include <sys/time.h>
#include <time.h>
+#include <inttypes.h>
+#include <dirent.h>
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
@@ -65,10 +68,14 @@
#include <linux/netfilter/nf_conntrack_common.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
-static struct nfct_mnl_socket {
+struct nfct_mnl_socket {
struct mnl_socket *mnl;
uint32_t portid;
-} sock;
+};
+
+static struct nfct_mnl_socket _sock;
+static struct nfct_mnl_socket _modifier_sock;
+static struct nfct_mnl_socket _event_sock;
struct u32_mask {
uint32_t value;
@@ -76,7 +83,7 @@ struct u32_mask {
};
/* These are the template objects that are used to send commands. */
-static struct {
+struct ct_tmpl {
struct nf_conntrack *ct;
struct nf_expect *exp;
/* Expectations require the expectation tuple and the mask. */
@@ -89,104 +96,65 @@ static struct {
struct nfct_filter_dump_mark filter_mark_kernel;
bool filter_mark_kernel_set;
+ /* Allow to filter by status from kernel-space. */
+ struct nfct_filter_dump_mark filter_status_kernel;
+ bool filter_status_kernel_set;
+
/* Allows filtering by ctlabels */
struct nfct_bitmask *label;
/* Allows setting/removing specific ctlabels */
struct nfct_bitmask *label_modify;
-} tmpl;
+};
-static int alloc_tmpl_objects(void)
+static struct ct_tmpl *cur_tmpl;
+
+struct ct_cmd {
+ struct list_head list;
+ unsigned int command;
+ unsigned int cmd;
+ unsigned int type;
+ unsigned int event_mask;
+ int options;
+ int family;
+ int protonum;
+ size_t socketbuffersize;
+ struct ct_tmpl tmpl;
+};
+
+static int alloc_tmpl_objects(struct ct_tmpl *tmpl)
{
- tmpl.ct = nfct_new();
- tmpl.exptuple = nfct_new();
- tmpl.mask = nfct_new();
- tmpl.exp = nfexp_new();
+ tmpl->ct = nfct_new();
+ tmpl->exptuple = nfct_new();
+ tmpl->mask = nfct_new();
+ tmpl->exp = nfexp_new();
- memset(&tmpl.mark, 0, sizeof(tmpl.mark));
+ memset(&tmpl->mark, 0, sizeof(tmpl->mark));
- return tmpl.ct != NULL && tmpl.exptuple != NULL &&
- tmpl.mask != NULL && tmpl.exp != NULL;
+ cur_tmpl = tmpl;
+
+ return tmpl->ct != NULL && tmpl->exptuple != NULL &&
+ tmpl->mask != NULL && tmpl->exp != NULL;
}
-static void free_tmpl_objects(void)
+static void free_tmpl_objects(struct ct_tmpl *tmpl)
{
- if (tmpl.ct)
- nfct_destroy(tmpl.ct);
- if (tmpl.exptuple)
- nfct_destroy(tmpl.exptuple);
- if (tmpl.mask)
- nfct_destroy(tmpl.mask);
- if (tmpl.exp)
- nfexp_destroy(tmpl.exp);
- if (tmpl.label)
- nfct_bitmask_destroy(tmpl.label);
- if (tmpl.label_modify)
- nfct_bitmask_destroy(tmpl.label_modify);
+ if (!tmpl)
+ return;
+ if (tmpl->ct)
+ nfct_destroy(tmpl->ct);
+ if (tmpl->exptuple)
+ nfct_destroy(tmpl->exptuple);
+ if (tmpl->mask)
+ nfct_destroy(tmpl->mask);
+ if (tmpl->exp)
+ nfexp_destroy(tmpl->exp);
+ if (tmpl->label)
+ nfct_bitmask_destroy(tmpl->label);
+ if (tmpl->label_modify)
+ nfct_bitmask_destroy(tmpl->label_modify);
}
-enum ct_command {
- CT_NONE = 0,
-
- CT_LIST_BIT = 0,
- CT_LIST = (1 << CT_LIST_BIT),
-
- CT_CREATE_BIT = 1,
- CT_CREATE = (1 << CT_CREATE_BIT),
-
- CT_UPDATE_BIT = 2,
- CT_UPDATE = (1 << CT_UPDATE_BIT),
-
- CT_DELETE_BIT = 3,
- CT_DELETE = (1 << CT_DELETE_BIT),
-
- CT_GET_BIT = 4,
- CT_GET = (1 << CT_GET_BIT),
-
- CT_FLUSH_BIT = 5,
- CT_FLUSH = (1 << CT_FLUSH_BIT),
-
- CT_EVENT_BIT = 6,
- CT_EVENT = (1 << CT_EVENT_BIT),
-
- CT_VERSION_BIT = 7,
- CT_VERSION = (1 << CT_VERSION_BIT),
-
- CT_HELP_BIT = 8,
- CT_HELP = (1 << CT_HELP_BIT),
-
- EXP_LIST_BIT = 9,
- EXP_LIST = (1 << EXP_LIST_BIT),
-
- EXP_CREATE_BIT = 10,
- EXP_CREATE = (1 << EXP_CREATE_BIT),
-
- EXP_DELETE_BIT = 11,
- EXP_DELETE = (1 << EXP_DELETE_BIT),
-
- EXP_GET_BIT = 12,
- EXP_GET = (1 << EXP_GET_BIT),
-
- EXP_FLUSH_BIT = 13,
- EXP_FLUSH = (1 << EXP_FLUSH_BIT),
-
- EXP_EVENT_BIT = 14,
- EXP_EVENT = (1 << EXP_EVENT_BIT),
-
- CT_COUNT_BIT = 15,
- CT_COUNT = (1 << CT_COUNT_BIT),
-
- EXP_COUNT_BIT = 16,
- EXP_COUNT = (1 << EXP_COUNT_BIT),
-
- CT_STATS_BIT = 17,
- CT_STATS = (1 << CT_STATS_BIT),
-
- EXP_STATS_BIT = 18,
- EXP_STATS = (1 << EXP_STATS_BIT),
-};
-/* If you add a new command, you have to update NUMBER_OF_CMD in conntrack.h */
-
enum ct_options {
CT_OPT_ORIG_SRC_BIT = 0,
CT_OPT_ORIG_SRC = (1 << CT_OPT_ORIG_SRC_BIT),
@@ -325,6 +293,7 @@ static const char *optflags[NUMBER_OF_OPT] = {
static struct option original_opts[] = {
{"dump", 2, 0, 'L'},
{"create", 2, 0, 'I'},
+ {"add", 2, 0, 'A'},
{"delete", 2, 0, 'D'},
{"update", 2, 0, 'U'},
{"get", 2, 0, 'G'},
@@ -368,7 +337,7 @@ static struct option original_opts[] = {
{0, 0, 0, 0}
};
-static const char *getopt_str = ":L::I::U::D::G::E::F::hVs:d:r:q:"
+static const char *getopt_str = ":L::I::U::D::G::E::F::A::hVs:d:r:q:"
"p:t:u:e:a:z[:]:{:}:m:i:f:o:n::"
"g::c:b:C::Sj::w:l:<:>::(:):";
@@ -385,26 +354,27 @@ static const char *getopt_str = ":L::I::U::D::G::E::F::hVs:d:r:q:"
static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
/* Well, it's better than "Re: Linux vs FreeBSD" */
{
- /* s d r q p t u z e [ ] { } a m i f n g o c b j w l < > ( ) */
-/*CT_LIST*/ {2,2,2,2,2,0,2,2,0,0,0,2,2,0,2,0,2,2,2,2,2,0,2,2,2,0,0,2,2},
-/*CT_CREATE*/ {3,3,3,3,1,1,2,0,0,0,0,0,0,2,2,0,0,2,2,0,0,0,0,2,0,2,0,2,2},
-/*CT_UPDATE*/ {2,2,2,2,2,2,2,0,0,0,0,2,2,0,2,2,2,2,2,2,0,0,0,0,2,2,2,0,0},
-/*CT_DELETE*/ {2,2,2,2,2,2,2,0,0,0,0,2,2,0,2,2,2,2,2,2,0,0,0,2,2,0,0,2,2},
-/*CT_GET*/ {3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,2,0,0,0,0},
-/*CT_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*CT_EVENT*/ {2,2,2,2,2,0,0,0,2,0,0,2,2,0,2,0,0,2,2,2,2,2,2,2,2,0,0,2,2},
-/*VERSION*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*HELP*/ {0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_LIST*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0,0,0,0,0,0,0},
-/*EXP_CREATE*/{1,1,2,2,1,1,2,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_DELETE*/{1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_GET*/ {1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_EVENT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0},
-/*CT_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*CT_STATS*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_STATS*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ /* s d r q p t u z e [ ] { } a m i f n g o c b j w l < > ( ) */
+ [CT_LIST_BIT] = {2,2,2,2,2,0,2,2,0,0,0,2,2,0,2,0,2,2,2,2,2,0,2,2,2,0,0,2,2},
+ [CT_CREATE_BIT] = {3,3,3,3,1,1,2,0,0,0,0,0,0,2,2,0,0,2,2,0,0,0,0,2,0,2,0,2,2},
+ [CT_UPDATE_BIT] = {2,2,2,2,2,2,2,0,0,0,0,2,2,0,2,2,2,2,2,2,0,0,0,0,2,2,2,0,0},
+ [CT_DELETE_BIT] = {2,2,2,2,2,2,2,0,0,0,0,2,2,0,2,2,2,2,2,2,0,0,0,2,2,0,0,2,2},
+ [CT_GET_BIT] = {3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,2,0,0,0,0},
+ [CT_FLUSH_BIT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0},
+ [CT_EVENT_BIT] = {2,2,2,2,2,0,0,0,2,0,0,2,2,0,2,0,2,2,2,2,2,2,2,2,2,0,0,2,2},
+ [CT_VERSION_BIT]= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ [CT_HELP_BIT] = {0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ [EXP_LIST_BIT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0,0,0,0,0,0,0},
+ [EXP_CREATE_BIT]= {1,1,2,2,1,1,2,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ [EXP_DELETE_BIT]= {1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ [EXP_GET_BIT] = {1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ [EXP_FLUSH_BIT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ [EXP_EVENT_BIT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0},
+ [CT_COUNT_BIT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ [EXP_COUNT_BIT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ [CT_STATS_BIT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ [EXP_STATS_BIT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ [CT_ADD_BIT] = {3,3,3,3,1,1,2,0,0,0,0,0,0,2,2,0,0,2,2,0,0,0,0,2,0,2,0,2,2},
};
static const int cmd2type[][2] = {
@@ -418,6 +388,8 @@ static const int cmd2type[][2] = {
['h'] = { CT_HELP, CT_HELP },
['C'] = { CT_COUNT, EXP_COUNT },
['S'] = { CT_STATS, EXP_STATS },
+ ['U'] = { CT_UPDATE, 0 },
+ ['A'] = { CT_ADD, 0 },
};
static const int opt2type[] = {
@@ -521,6 +493,7 @@ static char exit_msg[NUMBER_OF_CMD][64] = {
[CT_EVENT_BIT] = "%d flow events have been shown.\n",
[EXP_LIST_BIT] = "%d expectations have been shown.\n",
[EXP_DELETE_BIT] = "%d expectations have been shown.\n",
+ [CT_ADD_BIT] = "%d flow entries have been added.\n",
};
static const char usage_commands[] =
@@ -565,7 +538,7 @@ static const char usage_parameters[] =
"Common parameters and options:\n"
" -s, --src, --orig-src ip\t\tSource address from original direction\n"
" -d, --dst, --orig-dst ip\t\tDestination address from original direction\n"
- " -r, --reply-src ip\t\tSource addres from reply direction\n"
+ " -r, --reply-src ip\t\tSource address 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"
@@ -581,7 +554,7 @@ static const char usage_parameters[] =
#define OPTION_OFFSET 256
-static struct nfct_handle *cth, *ith;
+static struct nfct_handle *cth;
static struct option *opts = original_opts;
static unsigned int global_option_offset = 0;
@@ -593,7 +566,6 @@ static unsigned int addr_valid_flags[ADDR_VALID_FLAGS_MAX] = {
static LIST_HEAD(proto_list);
-static unsigned int options;
static struct nfct_labelmap *labelmap;
static int filter_family;
@@ -607,8 +579,276 @@ void register_proto(struct ctproto_handler *h)
list_add(&h->head, &proto_list);
}
+#define BUFFER_SIZE(ret, size, len, offset) do { \
+ if ((int)ret > (int)len) \
+ ret = len; \
+ size += ret; \
+ offset += ret; \
+ len -= ret; \
+} while(0)
+
+static int ct_snprintf_u32_bitmap(char *buf, size_t size,
+ const struct nf_conntrack *ct,
+ const struct ct_print_opts *attr)
+{
+ unsigned int offset = 0, ret, len = size;
+ bool found = false;
+ uint32_t val;
+ int i;
+
+ val = nfct_get_attr_u32(ct, attr->type);
+
+ for (i = 0; i < attr->val_mapping_count; i++) {
+ if (!(val & (1 << i)))
+ continue;
+ if (!attr->val_mapping[i])
+ continue;
+
+ ret = snprintf(buf + offset, len, "%s,", attr->val_mapping[i]);
+ BUFFER_SIZE(ret, size, len, offset);
+ found = true;
+ }
+
+ if (found) {
+ offset--;
+ ret = snprintf(buf + offset, len, " ");
+ BUFFER_SIZE(ret, size, len, offset);
+ }
+
+ return offset;
+}
+
+static int ct_snprintf_attr(char *buf, size_t size,
+ const struct nf_conntrack *ct,
+ const struct ct_print_opts *attr)
+{
+ char ipstr[INET6_ADDRSTRLEN] = {};
+ unsigned int offset = 0;
+ int type = attr->type;
+ int len = size, ret;
+
+ ret = snprintf(buf, len, "%s ", attr->name);
+ BUFFER_SIZE(ret, size, len, offset);
+
+ switch (attr->datatype) {
+ case CT_ATTR_TYPE_U8:
+ if (attr->val_mapping)
+ ret = snprintf(buf + offset, len, "%s ",
+ attr->val_mapping[nfct_get_attr_u8(ct, type)]);
+ else
+ ret = snprintf(buf + offset, len, "%u ",
+ nfct_get_attr_u8(ct, type));
+ break;
+ case CT_ATTR_TYPE_BE16:
+ ret = snprintf(buf + offset, len, "%u ",
+ ntohs(nfct_get_attr_u16(ct, type)));
+ break;
+ case CT_ATTR_TYPE_U16:
+ ret = snprintf(buf + offset, len, "%u ",
+ nfct_get_attr_u16(ct, type));
+ break;
+ case CT_ATTR_TYPE_BE32:
+ ret = snprintf(buf + offset, len, "%u ",
+ ntohl(nfct_get_attr_u32(ct, type)));
+ break;
+ case CT_ATTR_TYPE_U32:
+ ret = snprintf(buf + offset, len, "%u ",
+ nfct_get_attr_u32(ct, type));
+ break;
+ case CT_ATTR_TYPE_U64:
+ ret = snprintf(buf + offset, len, "%lu ",
+ nfct_get_attr_u64(ct, type));
+ break;
+ case CT_ATTR_TYPE_IPV4:
+ inet_ntop(AF_INET, nfct_get_attr(ct, type),
+ ipstr, sizeof(ipstr));
+ ret = snprintf(buf + offset, len, "%s ", ipstr);
+ break;
+ case CT_ATTR_TYPE_IPV6:
+ inet_ntop(AF_INET6, nfct_get_attr(ct, type),
+ ipstr, sizeof(ipstr));
+ ret = snprintf(buf + offset, len, "%s ", ipstr);
+ break;
+ case CT_ATTR_TYPE_U32_BITMAP:
+ ret = ct_snprintf_u32_bitmap(buf + offset, len, ct, attr);
+ if (ret == 0 && type == ATTR_STATUS)
+ ret = snprintf(buf + offset, len, "UNSET ");
+ break;
+ default:
+ /* Unsupported datatype, should not ever happen */
+ ret = 0;
+ break;
+ }
+ BUFFER_SIZE(ret, size, len, offset);
+
+ return offset;
+}
+
+int ct_snprintf_opts(char *buf, unsigned int len, const struct nf_conntrack *ct,
+ const struct ct_print_opts *attrs)
+{
+ unsigned int size = 0, offset = 0, ret;
+ int i;
+
+ for (i = 0; attrs[i].name; ++i) {
+ if (!nfct_attr_is_set(ct, attrs[i].type))
+ continue;
+
+ ret = ct_snprintf_attr(buf + offset, len, ct, &attrs[i]);
+ BUFFER_SIZE(ret, size, len, offset);
+ }
+
+ return offset;
+}
+
+static const struct ct_print_opts attrs_ip_map[] = {
+ [ATTR_ORIG_IPV4_SRC] = { "-s", ATTR_ORIG_IPV4_SRC, CT_ATTR_TYPE_IPV4, 0, 0 },
+ [ATTR_ORIG_IPV4_DST] = { "-d", ATTR_ORIG_IPV4_DST, CT_ATTR_TYPE_IPV4, 0, 0 },
+ [ATTR_DNAT_IPV4] = { "-g", ATTR_REPL_IPV4_SRC, CT_ATTR_TYPE_IPV4, 0, 0 },
+ [ATTR_SNAT_IPV4] = { "-n", ATTR_REPL_IPV4_DST, CT_ATTR_TYPE_IPV4, 0, 0 },
+ [ATTR_REPL_IPV4_SRC] = { "-r", ATTR_REPL_IPV4_SRC, CT_ATTR_TYPE_IPV4, 0, 0 },
+ [ATTR_REPL_IPV4_DST] = { "-q", ATTR_REPL_IPV4_DST, CT_ATTR_TYPE_IPV4, 0, 0 },
+ [ATTR_ORIG_IPV6_SRC] = { "-s", ATTR_ORIG_IPV6_SRC, CT_ATTR_TYPE_IPV6, 0, 0 },
+ [ATTR_ORIG_IPV6_DST] = { "-d", ATTR_ORIG_IPV6_DST, CT_ATTR_TYPE_IPV6, 0, 0 },
+ [ATTR_DNAT_IPV6] = { "-g", ATTR_REPL_IPV6_SRC, CT_ATTR_TYPE_IPV6, 0, 0 },
+ [ATTR_SNAT_IPV6] = { "-n", ATTR_REPL_IPV6_DST, CT_ATTR_TYPE_IPV6, 0, 0 },
+ [ATTR_REPL_IPV6_SRC] = { "-r", ATTR_REPL_IPV6_SRC, CT_ATTR_TYPE_IPV6, 0, 0 },
+ [ATTR_REPL_IPV6_DST] = { "-q", ATTR_REPL_IPV6_DST, CT_ATTR_TYPE_IPV6, 0, 0 },
+};
+
+static const char *conntrack_status_map[] = {
+ [IPS_SEEN_REPLY_BIT] = "SEEN_REPLY",
+ [IPS_ASSURED_BIT] = "ASSURED",
+ [IPS_FIXED_TIMEOUT_BIT] = "FIXED_TIMEOUT",
+ [IPS_EXPECTED_BIT] = "EXPECTED"
+};
+
+static struct ct_print_opts attrs_generic[] = {
+ { "-t", ATTR_TIMEOUT, CT_ATTR_TYPE_U32, 0, 0 },
+ { "-u", ATTR_STATUS, CT_ATTR_TYPE_U32_BITMAP,
+ sizeof(conntrack_status_map)/sizeof(conntrack_status_map[0]),
+ conntrack_status_map },
+ { "-c", ATTR_SECMARK, CT_ATTR_TYPE_U32, 0, 0 },
+ { "-w", ATTR_ZONE, CT_ATTR_TYPE_U16, 0, 0 },
+ { "--orig-zone", ATTR_ORIG_ZONE, CT_ATTR_TYPE_U16, 0, 0 },
+ { "--reply-zone", ATTR_REPL_ZONE, CT_ATTR_TYPE_U16, 0, 0 },
+ {},
+};
+
+static int ct_save_snprintf(char *buf, size_t len,
+ const struct nf_conntrack *ct,
+ struct nfct_labelmap *map,
+ enum nf_conntrack_msg_type type)
+{
+ struct ct_print_opts tuple_attr_print[5] = {};
+ unsigned int size = 0, offset = 0;
+ struct ctproto_handler *cur;
+ uint8_t l3proto, l4proto;
+ int tuple_attrs[4] = {};
+ bool l4proto_set;
+ unsigned i;
+ int ret;
+
+ switch (type) {
+ case NFCT_T_NEW:
+ ret = snprintf(buf + offset, len, "-A ");
+ BUFFER_SIZE(ret, size, len, offset);
+ break;
+ case NFCT_T_UPDATE:
+ ret = snprintf(buf + offset, len, "-U ");
+ BUFFER_SIZE(ret, size, len, offset);
+ break;
+ case NFCT_T_DESTROY:
+ ret = snprintf(buf + offset, len, "-D ");
+ BUFFER_SIZE(ret, size, len, offset);
+ break;
+ default:
+ break;
+ }
+
+ ret = ct_snprintf_opts(buf + offset, len, ct, attrs_generic);
+ BUFFER_SIZE(ret, size, len, offset);
+
+ l3proto = nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO);
+ if (!l3proto)
+ l3proto = nfct_get_attr_u8(ct, ATTR_REPL_L3PROTO);
+ switch (l3proto) {
+ case AF_INET:
+ tuple_attrs[0] = ATTR_ORIG_IPV4_SRC;
+ tuple_attrs[1] = ATTR_ORIG_IPV4_DST;
+ tuple_attrs[2] = nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT) ?
+ ATTR_DNAT_IPV4 : ATTR_REPL_IPV4_SRC;
+ tuple_attrs[3] = nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT) ?
+ ATTR_SNAT_IPV4 : ATTR_REPL_IPV4_DST;
+ break;
+ case AF_INET6:
+ tuple_attrs[0] = ATTR_ORIG_IPV6_SRC;
+ tuple_attrs[1] = ATTR_ORIG_IPV6_DST;
+ tuple_attrs[2] = nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT) ?
+ ATTR_DNAT_IPV6 : ATTR_REPL_IPV6_SRC;
+ tuple_attrs[3] = nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT) ?
+ ATTR_SNAT_IPV6 : ATTR_REPL_IPV6_DST;
+ break;
+ default:
+ fprintf(stderr, "WARNING: unsupported l3proto %d, skipping.\n",
+ l3proto);
+ return 0;
+ }
+
+ for (i = 0; i < sizeof(tuple_attrs) / sizeof(tuple_attrs[0]); i++) {
+ memcpy(&tuple_attr_print[i], &attrs_ip_map[tuple_attrs[i]],
+ sizeof(tuple_attr_print[0]));
+ }
+
+ ret = ct_snprintf_opts(buf + offset, len, ct, tuple_attr_print);
+ BUFFER_SIZE(ret, size, len, offset);
+
+ l4proto = nfct_get_attr_u8(ct, ATTR_L4PROTO);
+
+ l4proto_set = false;
+ /* is it in the list of supported protocol? */
+ list_for_each_entry(cur, &proto_list, head) {
+ if (cur->protonum != l4proto)
+ continue;
+
+ ret = snprintf(buf + offset, len, "-p %s ", cur->name);
+ BUFFER_SIZE(ret, size, len, offset);
+
+ ret = ct_snprintf_opts(buf + offset, len, ct, cur->print_opts);
+ BUFFER_SIZE(ret, size, len, offset);
+
+ l4proto_set = true;
+ break;
+ }
+
+ if (!l4proto_set) {
+ ret = snprintf(buf + offset, len, "-p %d ", l4proto);
+ BUFFER_SIZE(ret, size, len, offset);
+ }
+
+ /* skip trailing space, if any */
+ for (; size && buf[size-1] == ' '; --size)
+ buf[size-1] = '\0';
+
+ return size;
+}
+
extern struct ctproto_handler ct_proto_unknown;
+static int parse_proto_num(const char *str)
+{
+ unsigned long val;
+ char *endptr;
+
+ val = strtoul(str, &endptr, 0);
+ if (val > IPPROTO_RAW ||
+ endptr == str ||
+ *endptr != '\0')
+ return -1;
+
+ return val;
+}
+
static struct ctproto_handler *findproto(char *name, int *pnum)
{
struct ctproto_handler *cur;
@@ -628,8 +868,8 @@ static struct ctproto_handler *findproto(char *name, int *pnum)
return &ct_proto_unknown;
}
/* using a protocol number? */
- protonum = atoi(name);
- if (protonum >= 0 && protonum <= IPPROTO_MAX) {
+ protonum = parse_proto_num(name);
+ if (protonum >= 0) {
/* try lookup by number, perhaps this protocol is supported */
list_for_each_entry(cur, &proto_list, head) {
if (cur->protonum == protonum) {
@@ -696,7 +936,7 @@ exit_error(enum exittype status, const char *msg, ...)
if (status == PARAMETER_PROBLEM)
exit_tryhelp(status);
/* release template objects that were allocated in the setup stage. */
- free_tmpl_objects();
+ free_tmpl_objects(cur_tmpl);
exit(status);
}
@@ -818,11 +1058,11 @@ err2str(int err, enum ct_command command)
{ { 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,
+ { CT_CREATE|CT_GET|CT_DELETE|CT_ADD, ENOENT,
"such conntrack doesn't exist" },
- { CT_CREATE|CT_GET, ENOMEM, "not enough memory" },
+ { CT_CREATE|CT_GET|CT_ADD, ENOMEM, "not enough memory" },
{ CT_GET, EAFNOSUPPORT, "protocol not supported" },
- { CT_CREATE, ETIME, "conntrack has expired" },
+ { CT_CREATE|CT_ADD, ETIME, "conntrack has expired" },
{ EXP_CREATE, ENOENT, "master conntrack not found" },
{ EXP_CREATE, EINVAL, "invalid parameters" },
{ ~0U, EPERM, "sorry, you must be root or get "
@@ -855,7 +1095,7 @@ enum {
_O_ID = (1 << 3),
_O_KTMS = (1 << 4),
_O_CL = (1 << 5),
- _O_US = (1 << 6),
+ _O_SAVE = (1 << 6),
};
enum {
@@ -866,7 +1106,7 @@ enum {
};
static struct parse_parameter {
- const char *parameter[7];
+ const char *parameter[8];
size_t size;
unsigned int value[8];
} parse_array[PARSE_MAX] = {
@@ -874,13 +1114,13 @@ static struct parse_parameter {
{ IPS_ASSURED, IPS_SEEN_REPLY, 0, IPS_FIXED_TIMEOUT, IPS_EXPECTED, IPS_OFFLOAD, IPS_HW_OFFLOAD} },
{ {"ALL", "NEW", "UPDATES", "DESTROY"}, 4,
{ CT_EVENT_F_ALL, CT_EVENT_F_NEW, CT_EVENT_F_UPD, CT_EVENT_F_DEL } },
- { {"xml", "extended", "timestamp", "id", "ktimestamp", "labels", "userspace" }, 7,
- { _O_XML, _O_EXT, _O_TMS, _O_ID, _O_KTMS, _O_CL, _O_US },
+ { {"xml", "extended", "timestamp", "id", "ktimestamp", "labels", "userspace", "save"}, 8,
+ { _O_XML, _O_EXT, _O_TMS, _O_ID, _O_KTMS, _O_CL, 0, _O_SAVE },
},
};
static int
-do_parse_parameter(const char *str, size_t str_length, unsigned int *value,
+do_parse_parameter(const char *str, size_t str_length, unsigned int *value,
int parse_type)
{
size_t i;
@@ -905,7 +1145,7 @@ do_parse_parameter(const char *str, size_t str_length, unsigned int *value,
ret = 1;
break;
}
-
+
return ret;
}
@@ -927,6 +1167,53 @@ parse_parameter(const char *arg, unsigned int *status, int parse_type)
}
static void
+parse_parameter_mask(const char *arg, unsigned int *status, unsigned int *mask, int parse_type)
+{
+ static const char unreplied[] = "UNREPLIED";
+ unsigned int *value;
+ const char *comma;
+ bool negated;
+
+ while ((comma = strchr(arg, ',')) != NULL) {
+ if (comma == arg)
+ exit_error(PARAMETER_PROBLEM,"Bad parameter `%s'", arg);
+
+ negated = *arg == '!';
+ if (negated)
+ arg++;
+ if (comma == arg)
+ exit_error(PARAMETER_PROBLEM,"Bad parameter `%s'", arg);
+
+ value = negated ? mask : status;
+
+ if (!negated && strncmp(arg, unreplied, strlen(unreplied)) == 0) {
+ *mask |= IPS_SEEN_REPLY;
+ arg = comma+1;
+ continue;
+ }
+
+ if (!do_parse_parameter(arg, comma-arg, value, parse_type))
+ exit_error(PARAMETER_PROBLEM,"Bad parameter `%s'", arg);
+ arg = comma+1;
+ }
+
+ negated = *arg == '!';
+ if (negated)
+ arg++;
+ value = negated ? mask : status;
+
+ if (!negated && strncmp(arg, unreplied, strlen(unreplied)) == 0) {
+ *mask |= IPS_SEEN_REPLY;
+ return;
+ }
+
+ if (strlen(arg) == 0
+ || !do_parse_parameter(arg, strlen(arg),
+ value, parse_type))
+ exit_error(PARAMETER_PROBLEM, "Bad parameter `%s'", arg);
+}
+
+static void
parse_u32_mask(const char *arg, struct u32_mask *m)
{
char *end;
@@ -1190,8 +1477,7 @@ split_address_and_port(const char *arg, char **address, char **port_str)
}
}
-static void
-usage(char *prog)
+static void usage(const char *prog)
{
fprintf(stdout, "Command line interface for the connection "
"tracking system. Version %s\n", VERSION);
@@ -1208,17 +1494,17 @@ usage(char *prog)
static unsigned int output_mask;
static int
-filter_label(const struct nf_conntrack *ct)
+filter_label(const struct nf_conntrack *ct, const struct ct_tmpl *tmpl)
{
- if (tmpl.label == NULL)
+ if (tmpl->label == NULL)
return 0;
const struct nfct_bitmask *ctb = nfct_get_attr(ct, ATTR_CONNLABELS);
if (ctb == NULL)
return 1;
- for (unsigned int i = 0; i <= nfct_bitmask_maxbit(tmpl.label); i++) {
- if (nfct_bitmask_test_bit(tmpl.label, i) &&
+ for (unsigned int i = 0; i <= nfct_bitmask_maxbit(tmpl->label); i++) {
+ if (nfct_bitmask_test_bit(tmpl->label, i) &&
!nfct_bitmask_test_bit(ctb, i))
return 1;
}
@@ -1226,25 +1512,26 @@ filter_label(const struct nf_conntrack *ct)
return 0;
}
-static int
-filter_mark(const struct nf_conntrack *ct)
+static int filter_mark(const struct ct_cmd *cmd, const struct nf_conntrack *ct)
{
- if ((options & CT_OPT_MARK) &&
- !mark_cmp(&tmpl.mark, ct))
+ const struct ct_tmpl *tmpl = &cmd->tmpl;
+
+ if ((cmd->options & CT_OPT_MARK) &&
+ !mark_cmp(&tmpl->mark, ct))
return 1;
return 0;
}
-static int
-filter_nat(const struct nf_conntrack *obj, const struct nf_conntrack *ct)
+static int filter_nat(const struct ct_cmd *cmd, const struct nf_conntrack *ct)
{
- int check_srcnat = options & CT_OPT_SRC_NAT ? 1 : 0;
- int check_dstnat = options & CT_OPT_DST_NAT ? 1 : 0;
+ int check_srcnat = cmd->options & CT_OPT_SRC_NAT ? 1 : 0;
+ int check_dstnat = cmd->options & CT_OPT_DST_NAT ? 1 : 0;
+ struct nf_conntrack *obj = cmd->tmpl.ct;
int has_srcnat = 0, has_dstnat = 0;
uint32_t ip;
uint16_t port;
- if (options & CT_OPT_ANY_NAT)
+ if (cmd->options & CT_OPT_ANY_NAT)
check_srcnat = check_dstnat = 1;
if (check_srcnat) {
@@ -1307,13 +1594,14 @@ filter_nat(const struct nf_conntrack *obj, const struct nf_conntrack *ct)
nfct_getobjopt(ct, NFCT_GOPT_IS_DPAT)))
has_dstnat = 1;
}
- if (options & CT_OPT_ANY_NAT)
+ if (cmd->options & CT_OPT_ANY_NAT)
return !(has_srcnat || has_dstnat);
- else if ((options & CT_OPT_SRC_NAT) && (options & CT_OPT_DST_NAT))
+ else if ((cmd->options & CT_OPT_SRC_NAT) &&
+ (cmd->options & CT_OPT_DST_NAT))
return !(has_srcnat && has_dstnat);
- else if (options & CT_OPT_SRC_NAT)
+ else if (cmd->options & CT_OPT_SRC_NAT)
return !has_srcnat;
- else if (options & CT_OPT_DST_NAT)
+ else if (cmd->options & CT_OPT_DST_NAT)
return !has_dstnat;
return 0;
@@ -1361,14 +1649,14 @@ nfct_filter_network_direction(const struct nf_conntrack *ct, enum ct_direction d
}
static int
-filter_network(const struct nf_conntrack *ct)
+filter_network(const struct ct_cmd *cmd, const struct nf_conntrack *ct)
{
- if (options & CT_OPT_MASK_SRC) {
+ if (cmd->options & CT_OPT_MASK_SRC) {
if (nfct_filter_network_direction(ct, DIR_SRC))
return 1;
}
- if (options & CT_OPT_MASK_DST) {
+ if (cmd->options & CT_OPT_MASK_DST) {
if (nfct_filter_network_direction(ct, DIR_DST))
return 1;
}
@@ -1376,15 +1664,18 @@ filter_network(const struct nf_conntrack *ct)
}
static int
-nfct_filter(struct nf_conntrack *obj, struct nf_conntrack *ct)
+nfct_filter(struct ct_cmd *cmd, struct nf_conntrack *ct,
+ const struct ct_tmpl *tmpl)
{
- if (filter_nat(obj, ct) ||
- filter_mark(ct) ||
- filter_label(ct) ||
- filter_network(ct))
+ struct nf_conntrack *obj = cmd->tmpl.ct;
+
+ if (filter_nat(cmd, ct) ||
+ filter_mark(cmd, ct) ||
+ filter_label(ct, tmpl) ||
+ filter_network(cmd, ct))
return 1;
- if (options & CT_COMPARISON &&
+ if (cmd->options & CT_COMPARISON &&
!nfct_cmp(obj, ct, NFCT_CMP_ALL | NFCT_CMP_MASK))
return 1;
@@ -1404,7 +1695,7 @@ event_sighandler(int s)
fprintf(stderr, "%s v%s (conntrack-tools): ", PROGNAME, VERSION);
fprintf(stderr, "%d flow events have been shown.\n", counter);
- mnl_socket_close(sock.mnl);
+ mnl_socket_close(_sock.mnl);
exit(0);
}
@@ -1422,14 +1713,177 @@ exp_event_sighandler(int s)
exit(0);
}
+static char *pid2name(pid_t pid)
+{
+ char procname[256], *prog;
+ FILE *fp;
+ int ret;
+
+ ret = snprintf(procname, sizeof(procname), "/proc/%lu/stat", (unsigned long)pid);
+ if (ret < 0 || ret > (int)sizeof(procname))
+ return NULL;
+
+ fp = fopen(procname, "r");
+ if (!fp)
+ return NULL;
+
+ ret = fscanf(fp, "%*u (%m[^)]", &prog);
+
+ fclose(fp);
+
+ if (ret == 1)
+ return prog;
+
+ return NULL;
+}
+
+static char *portid2name(pid_t pid, uint32_t portid, unsigned long inode)
+{
+ const struct dirent *ent;
+ char procname[256];
+ DIR *dir;
+ int ret;
+
+ ret = snprintf(procname, sizeof(procname), "/proc/%lu/fd/", (unsigned long)pid);
+ if (ret < 0 || ret >= (int)sizeof(procname))
+ return NULL;
+
+ dir = opendir(procname);
+ if (!dir)
+ return NULL;
+
+ for (;;) {
+ unsigned long ino;
+ char tmp[128];
+ ssize_t rl;
+
+ ent = readdir(dir);
+ if (!ent)
+ break;
+
+ if (ent->d_type != DT_LNK)
+ continue;
+
+ ret = snprintf(procname, sizeof(procname), "/proc/%d/fd/%s",
+ pid, ent->d_name);
+ if (ret < 0 || ret >= (int)sizeof(procname))
+ continue;
+
+ rl = readlink(procname, tmp, sizeof(tmp));
+ if (rl <= 0 || rl >= (ssize_t)sizeof(tmp))
+ continue;
+
+ tmp[rl] = 0;
+
+ ret = sscanf(tmp, "socket:[%lu]", &ino);
+ if (ret == 1 && ino == inode) {
+ closedir(dir);
+ return pid2name(pid);
+ }
+ }
+
+ closedir(dir);
+ return NULL;
+}
+
+static char *name_by_portid(uint32_t portid, unsigned long inode)
+{
+ const struct dirent *ent;
+ char *prog;
+ DIR *dir;
+
+ /* Many netlink users use their process ID to allocate the first port id. */
+ prog = portid2name(portid, portid, inode);
+ if (prog)
+ return prog;
+
+ /* no luck, search harder. */
+ dir = opendir("/proc");
+ if (!dir)
+ return NULL;
+
+ for (;;) {
+ unsigned long pid;
+ char *end;
+
+ ent = readdir(dir);
+ if (!ent)
+ break;
+
+ if (ent->d_type != DT_DIR)
+ continue;
+
+ pid = strtoul(ent->d_name, &end, 10);
+ if (pid <= 1 || *end)
+ continue;
+
+ if (pid == portid) /* already tried */
+ continue;
+
+ prog = portid2name(pid, portid, inode);
+ if (prog)
+ break;
+ }
+
+ closedir(dir);
+ return prog;
+}
+
+static char *get_progname(uint32_t portid)
+{
+ FILE *fp = fopen("/proc/net/netlink", "r");
+ uint32_t portid_check;
+ unsigned long inode;
+ int ret, prot;
+
+ if (!fp)
+ return NULL;
+
+ for (;;) {
+ char line[256];
+
+ if (!fgets(line, sizeof(line), fp))
+ break;
+
+ ret = sscanf(line, "%*x %d %u %*x %*d %*d %*x %*d %*u %lu\n",
+ &prot, &portid_check, &inode);
+
+ if (ret == EOF)
+ break;
+
+ if (ret == 3 && portid_check == portid && prot == NETLINK_NETFILTER) {
+ static uint32_t last_portid;
+ static uint32_t last_inode;
+ static char *last_program;
+ char *prog;
+
+ fclose(fp);
+
+ if (last_portid == portid && last_inode == inode)
+ return last_program;
+
+ prog = name_by_portid(portid, inode);
+
+ free(last_program);
+ last_program = prog;
+ last_portid = portid;
+ last_inode = inode;
+ return prog;
+ }
+ }
+
+ fclose(fp);
+ return NULL;
+}
+
static int event_cb(const struct nlmsghdr *nlh, void *data)
{
+ struct nfgenmsg *nfh = mnl_nlmsg_get_payload(nlh);
unsigned int op_type = NFCT_O_DEFAULT;
- struct nf_conntrack *obj = data;
enum nf_conntrack_msg_type type;
+ struct ct_cmd *cmd = data;
unsigned int op_flags = 0;
struct nf_conntrack *ct;
- bool userspace = false;
char buf[1024];
switch(nlh->nlmsg_type & 0xff) {
@@ -1455,9 +1909,16 @@ static int event_cb(const struct nlmsghdr *nlh, void *data)
if (nfct_nlmsg_parse(nlh, ct) < 0)
goto out;
- if (nfct_filter(obj, ct))
+ if ((filter_family != AF_UNSPEC &&
+ filter_family != nfh->nfgen_family) ||
+ nfct_filter(cmd, ct, cur_tmpl))
goto out;
+ if (output_mask & _O_SAVE) {
+ ct_save_snprintf(buf, sizeof(buf), ct, labelmap, type);
+ goto done;
+ }
+
if (output_mask & _O_XML) {
op_type = NFCT_O_XML;
if (dump_xml_header_done) {
@@ -1482,15 +1943,17 @@ static int event_cb(const struct nlmsghdr *nlh, void *data)
op_flags |= NFCT_OF_ID;
nfct_snprintf_labels(buf, sizeof(buf), ct, type, op_type, op_flags, labelmap);
+done:
+ if (nlh->nlmsg_pid && !(output_mask & _O_XML)) {
+ char *prog = get_progname(nlh->nlmsg_pid);
- if (output_mask & _O_US) {
- if (nlh->nlmsg_pid)
- userspace = true;
+ if (prog)
+ printf("%s [USERSPACE] portid=%u progname=%s\n", buf, nlh->nlmsg_pid, prog);
else
- userspace = false;
+ printf("%s [USERSPACE] portid=%u\n", buf, nlh->nlmsg_pid);
+ } else {
+ puts(buf);
}
-
- printf("%s%s\n", buf, userspace ? " [USERSPACE]" : "");
fflush(stdout);
counter++;
@@ -1499,59 +1962,46 @@ out:
return MNL_CB_OK;
}
-static int dump_cb(enum nf_conntrack_msg_type type,
- struct nf_conntrack *ct,
- void *data)
+static int nfct_mnl_request(struct nfct_mnl_socket *sock, uint16_t subsys,
+ int family, uint16_t type, uint16_t flags,
+ mnl_cb_t cb, const struct nf_conntrack *ct,
+ const struct ct_cmd *cmd);
+
+static int mnl_nfct_delete_cb(const struct nlmsghdr *nlh, void *data)
{
- char buf[1024];
- struct nf_conntrack *obj = data;
+ struct nfct_mnl_socket *modifier_sock = &_modifier_sock;
unsigned int op_type = NFCT_O_DEFAULT;
unsigned int op_flags = 0;
+ struct ct_cmd *cmd = data;
+ struct nf_conntrack *ct;
+ char buf[1024];
+ int res;
- if (nfct_filter(obj, ct))
- return NFCT_CB_CONTINUE;
-
- if (output_mask & _O_XML) {
- op_type = NFCT_O_XML;
- if (dump_xml_header_done) {
- dump_xml_header_done = 0;
- printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
- "<conntrack>\n");
- }
- }
- if (output_mask & _O_EXT)
- op_flags = NFCT_OF_SHOW_LAYER3;
- if (output_mask & _O_KTMS)
- op_flags |= NFCT_OF_TIMESTAMP;
- if (output_mask & _O_ID)
- op_flags |= NFCT_OF_ID;
-
- nfct_snprintf_labels(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags, labelmap);
- printf("%s\n", buf);
-
- counter++;
-
- return NFCT_CB_CONTINUE;
-}
+ ct = nfct_new();
+ if (ct == NULL)
+ return MNL_CB_OK;
-static int delete_cb(enum nf_conntrack_msg_type type,
- struct nf_conntrack *ct,
- void *data)
-{
- int res;
- char buf[1024];
- struct nf_conntrack *obj = data;
- unsigned int op_type = NFCT_O_DEFAULT;
- unsigned int op_flags = 0;
+ nfct_nlmsg_parse(nlh, ct);
- if (nfct_filter(obj, ct))
- return NFCT_CB_CONTINUE;
+ if (nfct_filter(cmd, ct, cur_tmpl))
+ goto destroy_ok;
- res = nfct_query(ith, NFCT_Q_DESTROY, ct);
- if (res < 0)
+ res = nfct_mnl_request(modifier_sock, NFNL_SUBSYS_CTNETLINK,
+ nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO),
+ IPCTNL_MSG_CT_DELETE, NLM_F_ACK, NULL, ct, NULL);
+ if (res < 0) {
+ /* the entry has vanish in middle of the delete */
+ if (errno == ENOENT)
+ goto done;
exit_error(OTHER_PROBLEM,
"Operation failed: %s",
err2str(errno, CT_DELETE));
+ }
+
+ if (output_mask & _O_SAVE) {
+ ct_save_snprintf(buf, sizeof(buf), ct, labelmap, NFCT_T_DESTROY);
+ goto done;
+ }
if (output_mask & _O_XML)
op_type = NFCT_O_XML;
@@ -1561,20 +2011,33 @@ static int delete_cb(enum nf_conntrack_msg_type type,
op_flags |= NFCT_OF_ID;
nfct_snprintf(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags);
+done:
printf("%s\n", buf);
counter++;
- return NFCT_CB_CONTINUE;
+destroy_ok:
+ nfct_destroy(ct);
+ return MNL_CB_OK;
}
-static int print_cb(enum nf_conntrack_msg_type type,
- struct nf_conntrack *ct,
- void *data)
+static int mnl_nfct_print_cb(const struct nlmsghdr *nlh, void *data)
{
- char buf[1024];
unsigned int op_type = NFCT_O_DEFAULT;
unsigned int op_flags = 0;
+ struct nf_conntrack *ct;
+ char buf[1024];
+
+ ct = nfct_new();
+ if (ct == NULL)
+ return MNL_CB_OK;
+
+ nfct_nlmsg_parse(nlh, ct);
+
+ if (output_mask & _O_SAVE) {
+ ct_save_snprintf(buf, sizeof(buf), ct, labelmap, NFCT_T_NEW);
+ goto done;
+ }
if (output_mask & _O_XML)
op_type = NFCT_O_XML;
@@ -1584,25 +2047,29 @@ static int print_cb(enum nf_conntrack_msg_type type,
op_flags |= NFCT_OF_ID;
nfct_snprintf_labels(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags, labelmap);
+done:
printf("%s\n", buf);
- return NFCT_CB_CONTINUE;
+ nfct_destroy(ct);
+
+ return MNL_CB_OK;
}
-static void copy_mark(struct nf_conntrack *tmp,
+static void copy_mark(const struct ct_cmd *cmd, struct nf_conntrack *tmp,
const struct nf_conntrack *ct,
const struct u32_mask *m)
{
- if (options & CT_OPT_MARK) {
+ if (cmd->options & CT_OPT_MARK) {
uint32_t mark = nfct_get_attr_u32(ct, ATTR_MARK);
mark = (mark & ~m->mask) ^ m->value;
nfct_set_attr_u32(tmp, ATTR_MARK, mark);
}
}
-static void copy_status(struct nf_conntrack *tmp, const struct nf_conntrack *ct)
+static void copy_status(const struct ct_cmd *cmd, struct nf_conntrack *tmp,
+ const struct nf_conntrack *ct)
{
- if (options & CT_OPT_STATUS) {
+ if (cmd->options & CT_OPT_STATUS) {
/* copy existing flags, we only allow setting them. */
uint32_t status = nfct_get_attr_u32(ct, ATTR_STATUS);
status |= nfct_get_attr_u32(tmp, ATTR_STATUS);
@@ -1618,21 +2085,23 @@ static struct nfct_bitmask *xnfct_bitmask_clone(const struct nfct_bitmask *a)
return b;
}
-static void copy_label(struct nf_conntrack *tmp, const struct nf_conntrack *ct)
+static void copy_label(const struct ct_cmd *cmd, struct nf_conntrack *tmp,
+ const struct nf_conntrack *ct,
+ const struct ct_tmpl *tmpl)
{
struct nfct_bitmask *ctb, *newmask;
unsigned int i;
- if ((options & (CT_OPT_ADD_LABEL|CT_OPT_DEL_LABEL)) == 0)
+ if ((cmd->options & (CT_OPT_ADD_LABEL|CT_OPT_DEL_LABEL)) == 0)
return;
nfct_copy_attr(tmp, ct, ATTR_CONNLABELS);
ctb = (void *) nfct_get_attr(tmp, ATTR_CONNLABELS);
- if (options & CT_OPT_ADD_LABEL) {
+ if (cmd->options & CT_OPT_ADD_LABEL) {
if (ctb == NULL) {
nfct_set_attr(tmp, ATTR_CONNLABELS,
- xnfct_bitmask_clone(tmpl.label_modify));
+ xnfct_bitmask_clone(tmpl->label_modify));
return;
}
/* If we send a bitmask shorter than the kernel sent to us, the bits we
@@ -1646,7 +2115,7 @@ static void copy_label(struct nf_conntrack *tmp, const struct nf_conntrack *ct)
newmask = nfct_bitmask_new(nfct_bitmask_maxbit(ctb));
for (i = 0; i <= nfct_bitmask_maxbit(ctb); i++) {
- if (nfct_bitmask_test_bit(tmpl.label_modify, i)) {
+ if (nfct_bitmask_test_bit(tmpl->label_modify, i)) {
nfct_bitmask_set_bit(ctb, i);
nfct_bitmask_set_bit(newmask, i);
} else if (nfct_bitmask_test_bit(ctb, i)) {
@@ -1659,7 +2128,7 @@ static void copy_label(struct nf_conntrack *tmp, const struct nf_conntrack *ct)
nfct_set_attr(tmp, ATTR_CONNLABELS_MASK, newmask);
} else if (ctb != NULL) {
/* CT_OPT_DEL_LABEL */
- if (tmpl.label_modify == NULL) {
+ if (tmpl->label_modify == NULL) {
newmask = nfct_bitmask_new(0);
if (newmask)
nfct_set_attr(tmp, ATTR_CONNLABELS, newmask);
@@ -1667,35 +2136,43 @@ static void copy_label(struct nf_conntrack *tmp, const struct nf_conntrack *ct)
}
for (i = 0; i <= nfct_bitmask_maxbit(ctb); i++) {
- if (nfct_bitmask_test_bit(tmpl.label_modify, i))
+ if (nfct_bitmask_test_bit(tmpl->label_modify, i))
nfct_bitmask_unset_bit(ctb, i);
}
- newmask = xnfct_bitmask_clone(tmpl.label_modify);
+ newmask = xnfct_bitmask_clone(tmpl->label_modify);
nfct_set_attr(tmp, ATTR_CONNLABELS_MASK, newmask);
}
}
-static int update_cb(enum nf_conntrack_msg_type type,
- struct nf_conntrack *ct,
- void *data)
+static int mnl_nfct_update_cb(const struct nlmsghdr *nlh, void *data)
{
+ struct ct_cmd *cmd = data;
+ struct nfct_mnl_socket *modifier_sock = &_modifier_sock;
+ struct nf_conntrack *ct, *obj = cmd->tmpl.ct, *tmp = NULL;
int res;
- struct nf_conntrack *obj = data, *tmp;
- if (filter_nat(obj, ct) ||
- filter_label(ct) ||
- filter_network(ct))
- return NFCT_CB_CONTINUE;
+ ct = nfct_new();
+ if (ct == NULL)
+ return MNL_CB_OK;
+
+ nfct_nlmsg_parse(nlh, ct);
+
+ if (filter_nat(cmd, ct) ||
+ filter_label(ct, cur_tmpl) ||
+ filter_network(cmd, ct))
+ goto destroy_ok;
if (nfct_attr_is_set(obj, ATTR_ID) && nfct_attr_is_set(ct, ATTR_ID) &&
nfct_get_attr_u32(obj, ATTR_ID) != nfct_get_attr_u32(ct, ATTR_ID))
- return NFCT_CB_CONTINUE;
+ goto destroy_ok;
- if (options & CT_OPT_TUPLE_ORIG && !nfct_cmp(obj, ct, NFCT_CMP_ORIG))
- return NFCT_CB_CONTINUE;
- if (options & CT_OPT_TUPLE_REPL && !nfct_cmp(obj, ct, NFCT_CMP_REPL))
- return NFCT_CB_CONTINUE;
+ if (cmd->options & CT_OPT_TUPLE_ORIG &&
+ !nfct_cmp(obj, ct, NFCT_CMP_ORIG))
+ goto destroy_ok;
+ if (cmd->options & CT_OPT_TUPLE_REPL &&
+ !nfct_cmp(obj, ct, NFCT_CMP_REPL))
+ goto destroy_ok;
tmp = nfct_new();
if (tmp == NULL)
@@ -1703,41 +2180,52 @@ static int update_cb(enum nf_conntrack_msg_type type,
nfct_copy(tmp, ct, NFCT_CP_ORIG);
nfct_copy(tmp, obj, NFCT_CP_META);
- copy_mark(tmp, ct, &tmpl.mark);
- copy_status(tmp, ct);
- copy_label(tmp, ct);
+ copy_mark(cmd, tmp, ct, &cur_tmpl->mark);
+ copy_status(cmd, tmp, ct);
+ copy_label(cmd, tmp, ct, cur_tmpl);
/* do not send NFCT_Q_UPDATE if ct appears unchanged */
- if (nfct_cmp(tmp, ct, NFCT_CMP_ALL | NFCT_CMP_MASK)) {
- nfct_destroy(tmp);
- return NFCT_CB_CONTINUE;
- }
+ if (nfct_cmp(tmp, ct, NFCT_CMP_ALL | NFCT_CMP_MASK))
+ goto destroy_ok;
- res = nfct_query(ith, NFCT_Q_UPDATE, tmp);
- if (res < 0)
- fprintf(stderr,
- "Operation failed: %s\n",
+ res = nfct_mnl_request(modifier_sock, NFNL_SUBSYS_CTNETLINK,
+ nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO),
+ IPCTNL_MSG_CT_NEW, NLM_F_ACK, NULL, tmp, NULL);
+ if (res < 0) {
+ /* the entry has vanish in middle of the update */
+ if (errno == ENOENT)
+ goto destroy_ok;
+ else if (cmd->options & (CT_OPT_ADD_LABEL | CT_OPT_DEL_LABEL) &&
+ !nfct_attr_is_set(ct, ATTR_CONNLABELS) &&
+ errno == ENOSPC)
+ goto destroy_ok;
+
+ exit_error(OTHER_PROBLEM,
+ "Operation failed: %s",
err2str(errno, CT_UPDATE));
- nfct_callback_register(ith, NFCT_T_ALL, print_cb, NULL);
+ }
- res = nfct_query(ith, NFCT_Q_GET, tmp);
+ res = nfct_mnl_request(modifier_sock, NFNL_SUBSYS_CTNETLINK,
+ nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO),
+ IPCTNL_MSG_CT_GET, 0,
+ mnl_nfct_print_cb, tmp, NULL);
if (res < 0) {
- nfct_destroy(tmp);
/* the entry has vanish in middle of the update */
- if (errno == ENOENT) {
- nfct_callback_unregister(ith);
- return NFCT_CB_CONTINUE;
- }
+ if (errno == ENOENT)
+ goto destroy_ok;
exit_error(OTHER_PROBLEM,
"Operation failed: %s",
err2str(errno, CT_UPDATE));
}
- nfct_destroy(tmp);
- nfct_callback_unregister(ith);
counter++;
- return NFCT_CB_CONTINUE;
+destroy_ok:
+ if (tmp)
+ nfct_destroy(tmp);
+ nfct_destroy(ct);
+
+ return MNL_CB_OK;
}
static int dump_exp_cb(enum nf_conntrack_msg_type type,
@@ -1882,32 +2370,33 @@ out_err:
return ret;
}
-static int nfct_mnl_socket_open(unsigned int events)
+static int nfct_mnl_socket_open(struct nfct_mnl_socket *socket,
+ unsigned int events)
{
- sock.mnl = mnl_socket_open(NETLINK_NETFILTER);
- if (sock.mnl == NULL) {
+ socket->mnl = mnl_socket_open(NETLINK_NETFILTER);
+ if (socket->mnl == NULL) {
perror("mnl_socket_open");
return -1;
}
- if (mnl_socket_bind(sock.mnl, events, MNL_SOCKET_AUTOPID) < 0) {
+ if (mnl_socket_bind(socket->mnl, events, MNL_SOCKET_AUTOPID) < 0) {
perror("mnl_socket_bind");
return -1;
}
- sock.portid = mnl_socket_get_portid(sock.mnl);
+ socket->portid = mnl_socket_get_portid(socket->mnl);
return 0;
}
static struct nlmsghdr *
nfct_mnl_nlmsghdr_put(char *buf, uint16_t subsys, uint16_t type,
- uint8_t family)
+ uint16_t flags, uint8_t family)
{
struct nlmsghdr *nlh;
struct nfgenmsg *nfh;
nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = (subsys << 8) | type;
- nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP;
+ nlh->nlmsg_flags = NLM_F_REQUEST | flags;
nlh->nlmsg_seq = time(NULL);
nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
@@ -1918,63 +2407,114 @@ nfct_mnl_nlmsghdr_put(char *buf, uint16_t subsys, uint16_t type,
return nlh;
}
-static void nfct_mnl_socket_close(void)
+static void nfct_mnl_socket_close(const struct nfct_mnl_socket *sock)
{
- mnl_socket_close(sock.mnl);
+ mnl_socket_close(sock->mnl);
}
-static int
-nfct_mnl_dump(uint16_t subsys, uint16_t type, mnl_cb_t cb, uint8_t family)
+static int nfct_mnl_socket_check_open(struct nfct_mnl_socket *socket,
+ unsigned int events)
+{
+ if (socket->mnl != NULL)
+ return 0;
+
+ return nfct_mnl_socket_open(socket, events);
+}
+
+static void nfct_mnl_socket_check_close(struct nfct_mnl_socket *sock)
+{
+ if (sock->mnl) {
+ nfct_mnl_socket_close(sock);
+ memset(sock, 0, sizeof(*sock));
+ }
+}
+
+static int __nfct_mnl_dump(struct nfct_mnl_socket *sock,
+ const struct nlmsghdr *nlh, mnl_cb_t cb, void *data)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
- struct nlmsghdr *nlh;
int res;
- nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type, family);
-
- res = mnl_socket_sendto(sock.mnl, nlh, nlh->nlmsg_len);
+ res = mnl_socket_sendto(sock->mnl, nlh, nlh->nlmsg_len);
if (res < 0)
return res;
- res = mnl_socket_recvfrom(sock.mnl, buf, sizeof(buf));
+ res = mnl_socket_recvfrom(sock->mnl, buf, sizeof(buf));
while (res > 0) {
- res = mnl_cb_run(buf, res, nlh->nlmsg_seq, sock.portid,
- cb, NULL);
+ res = mnl_cb_run(buf, res, nlh->nlmsg_seq, sock->portid,
+ cb, data);
if (res <= MNL_CB_STOP)
break;
- res = mnl_socket_recvfrom(sock.mnl, buf, sizeof(buf));
+ res = mnl_socket_recvfrom(sock->mnl, buf, sizeof(buf));
}
return res;
}
static int
-nfct_mnl_get(uint16_t subsys, uint16_t type, mnl_cb_t cb, uint8_t family)
+nfct_mnl_dump(struct nfct_mnl_socket *sock, uint16_t subsys, uint16_t type,
+ mnl_cb_t cb, struct ct_cmd *cmd,
+ const struct nfct_filter_dump *filter_dump)
{
+ uint8_t family = cmd ? cmd->family : AF_UNSPEC;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
- int res;
- nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type, family);
+ nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type, NLM_F_DUMP, family);
- res = mnl_socket_sendto(sock.mnl, nlh, nlh->nlmsg_len);
- if (res < 0)
- return res;
+ if (filter_dump)
+ nfct_nlmsg_build_filter(nlh, filter_dump);
- res = mnl_socket_recvfrom(sock.mnl, buf, sizeof(buf));
- if (res < 0)
- return res;
+ return __nfct_mnl_dump(sock, nlh, cb, cmd);
+}
+
+static int nfct_mnl_talk(struct nfct_mnl_socket *sock,
+ const struct nlmsghdr *nlh, mnl_cb_t cb,
+ const struct ct_cmd *cmd)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ int ret;
+
+ ret = mnl_socket_sendto(sock->mnl, nlh, nlh->nlmsg_len);
+ if (ret < 0)
+ return ret;
+
+ ret = mnl_socket_recvfrom(sock->mnl, buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+
+ return mnl_cb_run(buf, ret, nlh->nlmsg_seq, sock->portid, cb, (void *)cmd);
+}
+
+static int nfct_mnl_request(struct nfct_mnl_socket *sock, uint16_t subsys,
+ int family, uint16_t type, uint16_t flags,
+ mnl_cb_t cb, const struct nf_conntrack *ct,
+ const struct ct_cmd *cmd)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ int err;
- return mnl_cb_run(buf, res, nlh->nlmsg_seq, sock.portid, cb, NULL);
+ nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type, flags, family);
+
+ if (ct) {
+ err = nfct_nlmsg_build(nlh, ct);
+ if (err < 0)
+ return err;
+ }
+
+ return nfct_mnl_talk(sock, nlh, cb, cmd);
}
+#define UNKNOWN_STATS_NUM 4
+
static int nfct_stats_attr_cb(const struct nlattr *attr, void *data)
{
const struct nlattr **tb = data;
int type = mnl_attr_get_type(attr);
- if (mnl_attr_type_valid(attr, CTA_STATS_MAX) < 0)
+ if (mnl_attr_type_valid(attr, CTA_STATS_MAX + UNKNOWN_STATS_NUM) < 0)
return MNL_CB_OK;
if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
@@ -1988,9 +2528,9 @@ static int nfct_stats_attr_cb(const struct nlattr *attr, void *data)
static int nfct_stats_cb(const struct nlmsghdr *nlh, void *data)
{
- struct nlattr *tb[CTA_STATS_MAX+1] = {};
+ struct nlattr *tb[CTA_STATS_MAX + UNKNOWN_STATS_NUM + 1] = {};
struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh);
- const char *attr2name[CTA_STATS_MAX+1] = {
+ const char *attr2name[CTA_STATS_MAX + UNKNOWN_STATS_NUM + 1] = {
[CTA_STATS_SEARCHED] = "searched",
[CTA_STATS_FOUND] = "found",
[CTA_STATS_NEW] = "new",
@@ -2004,6 +2544,16 @@ static int nfct_stats_cb(const struct nlmsghdr *nlh, void *data)
[CTA_STATS_EARLY_DROP] = "early_drop",
[CTA_STATS_ERROR] = "error",
[CTA_STATS_SEARCH_RESTART] = "search_restart",
+ [CTA_STATS_CLASH_RESOLVE] = "clash_resolve",
+ [CTA_STATS_CHAIN_TOOLONG] = "chaintoolong",
+
+ /* leave at end. Allows to show counters supported
+ * by newer kernel with older conntrack-tools release.
+ */
+ [CTA_STATS_MAX + 1] = "unknown1",
+ [CTA_STATS_MAX + 2] = "unknown2",
+ [CTA_STATS_MAX + 3] = "unknown3",
+ [CTA_STATS_MAX + 4] = "unknown4",
};
int i;
@@ -2011,7 +2561,7 @@ static int nfct_stats_cb(const struct nlmsghdr *nlh, void *data)
printf("cpu=%-4u\t", ntohs(nfg->res_id));
- for (i=0; i<CTA_STATS_MAX+1; i++) {
+ for (i=0; i <= CTA_STATS_MAX + UNKNOWN_STATS_NUM; i++) {
if (tb[i]) {
printf("%s=%u ",
attr2name[i], ntohl(mnl_attr_get_u32(tb[i])));
@@ -2096,6 +2646,9 @@ static int nfct_global_stats_cb(const struct nlmsghdr *nlh, void *data)
static int mnl_nfct_dump_cb(const struct nlmsghdr *nlh, void *data)
{
+ unsigned int op_type = NFCT_O_DEFAULT;
+ unsigned int op_flags = 0;
+ struct ct_cmd *cmd = data;
struct nf_conntrack *ct;
char buf[4096];
@@ -2105,7 +2658,34 @@ static int mnl_nfct_dump_cb(const struct nlmsghdr *nlh, void *data)
nfct_nlmsg_parse(nlh, ct);
- nfct_snprintf(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, NFCT_O_DEFAULT, 0);
+ if (nfct_filter(cmd, ct, cur_tmpl)) {
+ nfct_destroy(ct);
+ return MNL_CB_OK;
+ }
+
+ if (output_mask & _O_SAVE) {
+ ct_save_snprintf(buf, sizeof(buf), ct, labelmap, NFCT_T_NEW);
+ goto done;
+ }
+
+ if (output_mask & _O_XML) {
+ op_type = NFCT_O_XML;
+ if (dump_xml_header_done) {
+ dump_xml_header_done = 0;
+ printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<conntrack>\n");
+ }
+ }
+ if (output_mask & _O_EXT)
+ op_flags = NFCT_OF_SHOW_LAYER3;
+ if (output_mask & _O_KTMS)
+ op_flags |= NFCT_OF_TIMESTAMP;
+ if (output_mask & _O_ID)
+ op_flags |= NFCT_OF_ID;
+
+ nfct_snprintf_labels(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type,
+ op_flags, labelmap);
+done:
printf("%s\n", buf);
nfct_destroy(ct);
@@ -2127,7 +2707,8 @@ static void labelmap_init(void)
}
static void
-nfct_network_attr_prepare(const int family, enum ct_direction dir)
+nfct_network_attr_prepare(const int family, enum ct_direction dir,
+ const struct ct_tmpl *tmpl)
{
const union ct_address *address, *netmask;
enum nf_conntrack_attr attr;
@@ -2136,8 +2717,8 @@ nfct_network_attr_prepare(const int family, enum ct_direction dir)
attr = famdir2attr[family == AF_INET6][dir];
- address = nfct_get_attr(tmpl.ct, attr);
- netmask = nfct_get_attr(tmpl.mask, attr);
+ address = nfct_get_attr(tmpl->ct, attr);
+ netmask = nfct_get_attr(tmpl->mask, attr);
switch(family) {
case AF_INET:
@@ -2152,25 +2733,29 @@ nfct_network_attr_prepare(const int family, enum ct_direction dir)
memcpy(&net->netmask, netmask, sizeof(union ct_address));
/* avoid exact source matching */
- nfct_attr_unset(tmpl.ct, attr);
+ nfct_attr_unset(tmpl->ct, attr);
}
-static void
-nfct_filter_init(const int family)
+static void nfct_filter_init(const struct ct_cmd *cmd)
{
+ const struct ct_tmpl *tmpl = &cmd->tmpl;
+ int family = cmd->family;
+
filter_family = family;
- if (options & CT_OPT_MASK_SRC) {
- if (!(options & CT_OPT_ORIG_SRC))
+ if (cmd->options & CT_OPT_MASK_SRC) {
+ assert(family != AF_UNSPEC);
+ if (!(cmd->options & CT_OPT_ORIG_SRC))
exit_error(PARAMETER_PROBLEM,
"Can't use --mask-src without --src");
- nfct_network_attr_prepare(family, DIR_SRC);
+ nfct_network_attr_prepare(family, DIR_SRC, tmpl);
}
- if (options & CT_OPT_MASK_DST) {
- if (!(options & CT_OPT_ORIG_DST))
+ if (cmd->options & CT_OPT_MASK_DST) {
+ assert(family != AF_UNSPEC);
+ if (!(cmd->options & CT_OPT_ORIG_DST))
exit_error(PARAMETER_PROBLEM,
"Can't use --mask-dst without --dst");
- nfct_network_attr_prepare(family, DIR_DST);
+ nfct_network_attr_prepare(family, DIR_DST, tmpl);
}
}
@@ -2238,9 +2823,9 @@ nfct_set_addr_only(const int opt, struct nf_conntrack *ct, union ct_address *ad,
static void
nfct_set_addr_opt(const int opt, struct nf_conntrack *ct, union ct_address *ad,
- const int l3protonum)
+ const int l3protonum, unsigned int *options)
{
- options |= opt2type[opt];
+ *options |= opt2type[opt];
nfct_set_addr_only(opt, ct, ad, l3protonum);
nfct_set_attr_u8(ct, opt2attr[opt], l3protonum);
}
@@ -2249,7 +2834,8 @@ static void
nfct_parse_addr_from_opt(const int opt, const char *arg,
struct nf_conntrack *ct,
struct nf_conntrack *ctmask,
- union ct_address *ad, int *family)
+ union ct_address *ad, int *family,
+ unsigned int *options)
{
int mask, maskopt;
@@ -2269,7 +2855,7 @@ nfct_parse_addr_from_opt(const int opt, const char *arg,
"Invalid netmask");
}
- nfct_set_addr_opt(opt, ct, ad, l3protonum);
+ nfct_set_addr_opt(opt, ct, ad, l3protonum, options);
/* bail if we don't have a netmask to set*/
if (mask == -1 || !maskopt || ctmask == NULL)
@@ -2288,7 +2874,7 @@ nfct_parse_addr_from_opt(const int opt, const char *arg,
break;
}
- nfct_set_addr_opt(maskopt, ctmask, ad, l3protonum);
+ nfct_set_addr_opt(maskopt, ctmask, ad, l3protonum, options);
}
static void
@@ -2309,33 +2895,41 @@ nfct_set_nat_details(const int opt, struct nf_conntrack *ct,
}
-int main(int argc, char *argv[])
+static int print_stats(const struct ct_cmd *cmd)
+{
+ if (cmd->command && exit_msg[cmd->cmd][0]) {
+ fprintf(stderr, "%s v%s (conntrack-tools): ",PROGNAME,VERSION);
+ fprintf(stderr, exit_msg[cmd->cmd], counter);
+ if (counter == 0 &&
+ !(cmd->command & (CT_LIST | EXP_LIST)))
+ return -1;
+ }
+
+ return 0;
+}
+
+static void do_parse(struct ct_cmd *ct_cmd, int argc, char *argv[])
{
- int c, cmd;
unsigned int type = 0, event_mask = 0, l4flags = 0, status = 0;
- int res = 0, partial;
+ int protonum = 0, family = AF_UNSPEC;
size_t socketbuffersize = 0;
- int family = AF_UNSPEC;
- int protonum = 0;
- union ct_address ad;
unsigned int command = 0;
+ unsigned int options = 0;
+ struct ct_tmpl *tmpl;
+ int res = 0, partial;
+ union ct_address ad;
+ int c, cmd;
/* we release these objects in the exit_error() path. */
- if (!alloc_tmpl_objects())
+ if (!alloc_tmpl_objects(&ct_cmd->tmpl))
exit_error(OTHER_PROBLEM, "out of memory");
- register_tcp();
- register_udp();
- register_udplite();
- register_sctp();
- register_dccp();
- register_icmp();
- register_icmpv6();
- register_gre();
- register_unknown();
+ tmpl = &ct_cmd->tmpl;
/* disable explicit missing arguments error output from getopt_long */
opterr = 0;
+ /* reset optind, for the case do_parse is called multiple times */
+ optind = 0;
while ((c = getopt_long(argc, argv, getopt_str, opts, NULL)) != -1) {
switch(c) {
@@ -2360,45 +2954,44 @@ int main(int argc, char *argv[])
case 'h':
case 'C':
case 'S':
- type = check_type(argc, argv);
- if (type == CT_TABLE_DYING ||
- type == CT_TABLE_UNCONFIRMED) {
- exit_error(PARAMETER_PROBLEM,
- "Can't do that command with "
- "tables `dying' and `unconfirmed'");
- }
- add_command(&command, cmd2type[c][type]);
- break;
case 'U':
+ case 'A':
type = check_type(argc, argv);
if (type == CT_TABLE_DYING ||
type == CT_TABLE_UNCONFIRMED) {
exit_error(PARAMETER_PROBLEM,
"Can't do that command with "
"tables `dying' and `unconfirmed'");
- } else if (type == CT_TABLE_CONNTRACK)
- add_command(&command, CT_UPDATE);
- else
+ }
+ if (cmd2type[c][type])
+ add_command(&command, cmd2type[c][type]);
+ else {
exit_error(PARAMETER_PROBLEM,
- "Can't update expectations");
+ "Can't do --%s on %s",
+ get_long_opt(c),
+ type == CT_TABLE_CONNTRACK ?
+ "conntrack" : "expectations");
+ }
break;
/* options */
case 's':
case 'd':
case 'r':
case 'q':
- nfct_parse_addr_from_opt(c, optarg, tmpl.ct,
- tmpl.mask, &ad, &family);
+ nfct_parse_addr_from_opt(c, optarg, tmpl->ct,
+ tmpl->mask, &ad, &family,
+ &options);
break;
case '[':
case ']':
- nfct_parse_addr_from_opt(c, optarg, tmpl.exptuple,
- tmpl.mask, &ad, &family);
+ nfct_parse_addr_from_opt(c, optarg, tmpl->exptuple,
+ tmpl->mask, &ad, &family,
+ &options);
break;
case '{':
case '}':
- nfct_parse_addr_from_opt(c, optarg, tmpl.mask,
- NULL, &ad, &family);
+ nfct_parse_addr_from_opt(c, optarg, tmpl->mask,
+ NULL, &ad, &family, &options);
break;
case 'p':
options |= CT_OPT_PROTO;
@@ -2412,18 +3005,25 @@ int main(int argc, char *argv[])
if (opts == NULL)
exit_error(OTHER_PROBLEM, "out of memory");
- nfct_set_attr_u8(tmpl.ct, ATTR_L4PROTO, protonum);
+ nfct_set_attr_u8(tmpl->ct, ATTR_L4PROTO, protonum);
break;
case 't':
options |= CT_OPT_TIMEOUT;
- nfct_set_attr_u32(tmpl.ct, ATTR_TIMEOUT, atol(optarg));
- nfexp_set_attr_u32(tmpl.exp,
+ nfct_set_attr_u32(tmpl->ct, ATTR_TIMEOUT, atol(optarg));
+ nfexp_set_attr_u32(tmpl->exp,
ATTR_EXP_TIMEOUT, atol(optarg));
break;
case 'u':
options |= CT_OPT_STATUS;
- parse_parameter(optarg, &status, PARSE_STATUS);
- nfct_set_attr_u32(tmpl.ct, ATTR_STATUS, status);
+ parse_parameter_mask(optarg, &status,
+ &tmpl->filter_status_kernel.mask,
+ PARSE_STATUS);
+ nfct_set_attr_u32(tmpl->ct, ATTR_STATUS, status);
+ if (tmpl->filter_status_kernel.mask == 0)
+ tmpl->filter_status_kernel.mask = status;
+
+ tmpl->filter_status_kernel.val = status;
+ tmpl->filter_status_kernel_set = true;
break;
case 'e':
options |= CT_OPT_EVENT_MASK;
@@ -2434,6 +3034,10 @@ int main(int argc, char *argv[])
parse_parameter(optarg, &output_mask, PARSE_OUTPUT);
if (output_mask & _O_CL)
labelmap_init();
+ if ((output_mask & _O_SAVE) &&
+ (output_mask & (_O_EXT |_O_TMS |_O_ID | _O_KTMS | _O_CL | _O_XML)))
+ exit_error(OTHER_PROBLEM,
+ "cannot combine save output with any other output type, use -o save only");
break;
case 'z':
options |= CT_OPT_ZERO;
@@ -2452,43 +3056,46 @@ int main(int argc, char *argv[])
&nat_address,
&port_str);
nfct_parse_addr_from_opt(c, nat_address,
- tmpl.ct, NULL,
- &ad, &family);
+ tmpl->ct, NULL,
+ &ad, &family,
+ &options);
if (c == 'j') {
/* Set details on both src and dst
* with any-nat
*/
- nfct_set_nat_details('g', tmpl.ct, &ad,
+ nfct_set_nat_details('g', tmpl->ct, &ad,
port_str, family);
- nfct_set_nat_details('n', tmpl.ct, &ad,
+ nfct_set_nat_details('n', tmpl->ct, &ad,
port_str, family);
} else {
- nfct_set_nat_details(c, tmpl.ct, &ad,
+ nfct_set_nat_details(c, tmpl->ct, &ad,
port_str, family);
}
+ free(port_str);
+ free(nat_address);
}
break;
case 'w':
case '(':
case ')':
options |= opt2type[c];
- nfct_set_attr_u16(tmpl.ct,
+ nfct_set_attr_u16(tmpl->ct,
opt2attr[c],
strtoul(optarg, NULL, 0));
break;
case 'i':
case 'c':
options |= opt2type[c];
- nfct_set_attr_u32(tmpl.ct,
+ nfct_set_attr_u32(tmpl->ct,
opt2attr[c],
strtoul(optarg, NULL, 0));
break;
case 'm':
options |= opt2type[c];
- parse_u32_mask(optarg, &tmpl.mark);
- tmpl.filter_mark_kernel.val = tmpl.mark.value;
- tmpl.filter_mark_kernel.mask = tmpl.mark.mask;
- tmpl.filter_mark_kernel_set = true;
+ parse_u32_mask(optarg, &tmpl->mark);
+ tmpl->filter_mark_kernel.val = tmpl->mark.value;
+ tmpl->filter_mark_kernel.mask = tmpl->mark.mask;
+ tmpl->filter_mark_kernel_set = true;
break;
case 'l':
case '<':
@@ -2519,9 +3126,9 @@ int main(int argc, char *argv[])
/* join "-l foo -l bar" into single bitmask object */
if (c == 'l') {
- merge_bitmasks(&tmpl.label, b);
+ merge_bitmasks(&tmpl->label, b);
} else {
- merge_bitmasks(&tmpl.label_modify, b);
+ merge_bitmasks(&tmpl->label_modify, b);
}
free(optarg2);
@@ -2554,18 +3161,31 @@ int main(int argc, char *argv[])
"unknown option `%s'", argv[optind-1]);
break;
default:
- if (h && h->parse_opts
- &&!h->parse_opts(c - h->option_offset, tmpl.ct,
- tmpl.exptuple, tmpl.mask,
- &l4flags))
+ if (h && h->parse_opts &&
+ !h->parse_opts(c - h->option_offset, tmpl->ct,
+ tmpl->exptuple, tmpl->mask,
+ &l4flags))
exit_error(PARAMETER_PROBLEM, "parse error");
break;
}
}
- /* default family */
- if (family == AF_UNSPEC)
- family = AF_INET;
+ /* default family only for the following commands */
+ if (family == AF_UNSPEC) {
+ switch (command) {
+ case CT_LIST:
+ case CT_UPDATE:
+ case CT_DELETE:
+ case CT_GET:
+ case CT_FLUSH:
+ case CT_EVENT:
+ break;
+ default:
+ family = AF_INET;
+ break;
+ }
+ }
+
/* we cannot check this combination with generic_opt_check. */
if (options & CT_OPT_ANY_NAT &&
@@ -2593,65 +3213,76 @@ int main(int argc, char *argv[])
}
}
if (!(command & CT_HELP) && h && h->final_check)
- h->final_check(l4flags, cmd, tmpl.ct);
+ h->final_check(l4flags, cmd, tmpl->ct);
- switch(command) {
+ free_options();
+
+ ct_cmd->command = command;
+ ct_cmd->cmd = cmd;
+ ct_cmd->options = options;
+ ct_cmd->family = family;
+ ct_cmd->type = type;
+ ct_cmd->protonum = protonum;
+ ct_cmd->event_mask = event_mask;
+ ct_cmd->socketbuffersize = socketbuffersize;
+}
+
+static int do_command_ct(const char *progname, struct ct_cmd *cmd,
+ struct nfct_mnl_socket *sock)
+{
+ struct nfct_mnl_socket *modifier_sock = &_modifier_sock;
+ struct nfct_mnl_socket *event_sock = &_event_sock;
struct nfct_filter_dump *filter_dump;
+ uint16_t nl_flags = 0;
+ int res = 0;
+ switch(cmd->command) {
case CT_LIST:
- if (type == CT_TABLE_DYING) {
- if (nfct_mnl_socket_open(0) < 0)
- exit_error(OTHER_PROBLEM, "Can't open handler");
-
- res = nfct_mnl_dump(NFNL_SUBSYS_CTNETLINK,
+ if (cmd->type == CT_TABLE_DYING) {
+ res = nfct_mnl_dump(sock, NFNL_SUBSYS_CTNETLINK,
IPCTNL_MSG_CT_GET_DYING,
- mnl_nfct_dump_cb, family);
-
- nfct_mnl_socket_close();
+ mnl_nfct_dump_cb, cmd, NULL);
break;
- } else if (type == CT_TABLE_UNCONFIRMED) {
- if (nfct_mnl_socket_open(0) < 0)
- exit_error(OTHER_PROBLEM, "Can't open handler");
-
- res = nfct_mnl_dump(NFNL_SUBSYS_CTNETLINK,
+ } else if (cmd->type == CT_TABLE_UNCONFIRMED) {
+ res = nfct_mnl_dump(sock, NFNL_SUBSYS_CTNETLINK,
IPCTNL_MSG_CT_GET_UNCONFIRMED,
- mnl_nfct_dump_cb, family);
-
- nfct_mnl_socket_close();
+ mnl_nfct_dump_cb, cmd, NULL);
break;
}
- cth = nfct_open(CONNTRACK, 0);
- if (!cth)
- exit_error(OTHER_PROBLEM, "Can't open handler");
-
- if (options & CT_COMPARISON &&
- options & CT_OPT_ZERO)
+ if (cmd->options & CT_COMPARISON &&
+ cmd->options & CT_OPT_ZERO)
exit_error(PARAMETER_PROBLEM, "Can't use -z with "
"filtering parameters");
- nfct_filter_init(family);
-
- nfct_callback_register(cth, NFCT_T_ALL, dump_cb, tmpl.ct);
+ nfct_filter_init(cmd);
filter_dump = nfct_filter_dump_create();
if (filter_dump == NULL)
exit_error(OTHER_PROBLEM, "OOM");
- if (tmpl.filter_mark_kernel_set) {
+ if (cmd->tmpl.filter_mark_kernel_set) {
nfct_filter_dump_set_attr(filter_dump,
NFCT_FILTER_DUMP_MARK,
- &tmpl.filter_mark_kernel);
+ &cmd->tmpl.filter_mark_kernel);
}
nfct_filter_dump_set_attr_u8(filter_dump,
NFCT_FILTER_DUMP_L3NUM,
- family);
-
- if (options & CT_OPT_ZERO)
- res = nfct_query(cth, NFCT_Q_DUMP_FILTER_RESET,
- filter_dump);
- else
- res = nfct_query(cth, NFCT_Q_DUMP_FILTER, filter_dump);
+ cmd->family);
+ if (cmd->tmpl.filter_status_kernel_set) {
+ nfct_filter_dump_set_attr(filter_dump,
+ NFCT_FILTER_DUMP_STATUS,
+ &cmd->tmpl.filter_status_kernel);
+ }
+ if (cmd->options & CT_OPT_ZERO) {
+ res = nfct_mnl_dump(sock, NFNL_SUBSYS_CTNETLINK,
+ IPCTNL_MSG_CT_GET_CTRZERO,
+ mnl_nfct_dump_cb, cmd, filter_dump);
+ } else {
+ res = nfct_mnl_dump(sock, NFNL_SUBSYS_CTNETLINK,
+ IPCTNL_MSG_CT_GET,
+ mnl_nfct_dump_cb, cmd, filter_dump);
+ }
nfct_filter_dump_destroy(filter_dump);
@@ -2659,17 +3290,14 @@ int main(int argc, char *argv[])
printf("</conntrack>\n");
fflush(stdout);
}
-
- nfct_close(cth);
break;
-
case EXP_LIST:
cth = nfct_open(EXPECT, 0);
if (!cth)
exit_error(OTHER_PROBLEM, "Can't open handler");
nfexp_callback_register(cth, NFCT_T_ALL, dump_exp_cb, NULL);
- res = nfexp_query(cth, NFCT_Q_DUMP, &family);
+ res = nfexp_query(cth, NFCT_Q_DUMP, &cmd->family);
nfct_close(cth);
if (dump_xml_header_done == 0) {
@@ -2679,127 +3307,112 @@ int main(int argc, char *argv[])
break;
case CT_CREATE:
- if ((options & CT_OPT_ORIG) && !(options & CT_OPT_REPL))
- nfct_setobjopt(tmpl.ct, NFCT_SOPT_SETUP_REPLY);
- else if (!(options & CT_OPT_ORIG) && (options & CT_OPT_REPL))
- nfct_setobjopt(tmpl.ct, NFCT_SOPT_SETUP_ORIGINAL);
-
- if (options & CT_OPT_MARK)
- nfct_set_attr_u32(tmpl.ct, ATTR_MARK, tmpl.mark.value);
-
- if (options & CT_OPT_ADD_LABEL)
- nfct_set_attr(tmpl.ct, ATTR_CONNLABELS,
- xnfct_bitmask_clone(tmpl.label_modify));
-
- cth = nfct_open(CONNTRACK, 0);
- if (!cth)
- exit_error(OTHER_PROBLEM, "Can't open handler");
-
- res = nfct_query(cth, NFCT_Q_CREATE, tmpl.ct);
- if (res != -1)
+ case CT_ADD:
+ if ((cmd->options & CT_OPT_ORIG) && !(cmd->options & CT_OPT_REPL))
+ nfct_setobjopt(cmd->tmpl.ct, NFCT_SOPT_SETUP_REPLY);
+ else if (!(cmd->options & CT_OPT_ORIG) && (cmd->options & CT_OPT_REPL))
+ nfct_setobjopt(cmd->tmpl.ct, NFCT_SOPT_SETUP_ORIGINAL);
+
+ if (cmd->options & CT_OPT_MARK)
+ nfct_set_attr_u32(cmd->tmpl.ct, ATTR_MARK, cmd->tmpl.mark.value);
+
+ if (cmd->options & CT_OPT_ADD_LABEL)
+ nfct_set_attr(cmd->tmpl.ct, ATTR_CONNLABELS,
+ xnfct_bitmask_clone(cmd->tmpl.label_modify));
+
+ if (cmd->command == CT_CREATE)
+ nl_flags = NLM_F_EXCL;
+
+ res = nfct_mnl_request(sock, NFNL_SUBSYS_CTNETLINK, cmd->family,
+ IPCTNL_MSG_CT_NEW,
+ NLM_F_CREATE | NLM_F_ACK | nl_flags,
+ NULL, cmd->tmpl.ct, NULL);
+ if (res >= 0)
counter++;
- nfct_close(cth);
break;
case EXP_CREATE:
- nfexp_set_attr(tmpl.exp, ATTR_EXP_MASTER, tmpl.ct);
- nfexp_set_attr(tmpl.exp, ATTR_EXP_EXPECTED, tmpl.exptuple);
- nfexp_set_attr(tmpl.exp, ATTR_EXP_MASK, tmpl.mask);
+ nfexp_set_attr(cmd->tmpl.exp, ATTR_EXP_MASTER, cmd->tmpl.ct);
+ nfexp_set_attr(cmd->tmpl.exp, ATTR_EXP_EXPECTED, cmd->tmpl.exptuple);
+ nfexp_set_attr(cmd->tmpl.exp, ATTR_EXP_MASK, cmd->tmpl.mask);
cth = nfct_open(EXPECT, 0);
if (!cth)
exit_error(OTHER_PROBLEM, "Can't open handler");
- res = nfexp_query(cth, NFCT_Q_CREATE, tmpl.exp);
+ res = nfexp_query(cth, NFCT_Q_CREATE, cmd->tmpl.exp);
nfct_close(cth);
break;
case CT_UPDATE:
- cth = nfct_open(CONNTRACK, 0);
- /* internal handler for delete_cb, otherwise we hit EILSEQ */
- ith = nfct_open(CONNTRACK, 0);
- if (!cth || !ith)
+ if (nfct_mnl_socket_check_open(modifier_sock, 0) < 0)
exit_error(OTHER_PROBLEM, "Can't open handler");
- nfct_filter_init(family);
-
- nfct_callback_register(cth, NFCT_T_ALL, update_cb, tmpl.ct);
-
- res = nfct_query(cth, NFCT_Q_DUMP, &family);
- nfct_close(ith);
- nfct_close(cth);
+ nfct_filter_init(cmd);
+ res = nfct_mnl_dump(sock, NFNL_SUBSYS_CTNETLINK,
+ IPCTNL_MSG_CT_GET, mnl_nfct_update_cb,
+ cmd, NULL);
break;
-
+
case CT_DELETE:
- cth = nfct_open(CONNTRACK, 0);
- ith = nfct_open(CONNTRACK, 0);
- if (!cth || !ith)
+ if (nfct_mnl_socket_check_open(modifier_sock, 0) < 0)
exit_error(OTHER_PROBLEM, "Can't open handler");
- nfct_filter_init(family);
-
- nfct_callback_register(cth, NFCT_T_ALL, delete_cb, tmpl.ct);
+ nfct_filter_init(cmd);
filter_dump = nfct_filter_dump_create();
if (filter_dump == NULL)
exit_error(OTHER_PROBLEM, "OOM");
- if (tmpl.filter_mark_kernel_set) {
+ if (cmd->tmpl.filter_mark_kernel_set) {
nfct_filter_dump_set_attr(filter_dump,
NFCT_FILTER_DUMP_MARK,
- &tmpl.filter_mark_kernel);
+ &cmd->tmpl.filter_mark_kernel);
}
nfct_filter_dump_set_attr_u8(filter_dump,
NFCT_FILTER_DUMP_L3NUM,
- family);
+ cmd->family);
- res = nfct_query(cth, NFCT_Q_DUMP_FILTER, filter_dump);
+ res = nfct_mnl_dump(sock, NFNL_SUBSYS_CTNETLINK,
+ IPCTNL_MSG_CT_GET, mnl_nfct_delete_cb,
+ cmd, filter_dump);
nfct_filter_dump_destroy(filter_dump);
-
- nfct_close(ith);
- nfct_close(cth);
break;
case EXP_DELETE:
- nfexp_set_attr(tmpl.exp, ATTR_EXP_EXPECTED, tmpl.ct);
+ nfexp_set_attr(cmd->tmpl.exp, ATTR_EXP_EXPECTED, cmd->tmpl.ct);
cth = nfct_open(EXPECT, 0);
if (!cth)
exit_error(OTHER_PROBLEM, "Can't open handler");
- res = nfexp_query(cth, NFCT_Q_DESTROY, tmpl.exp);
+ res = nfexp_query(cth, NFCT_Q_DESTROY, cmd->tmpl.exp);
nfct_close(cth);
break;
case CT_GET:
- cth = nfct_open(CONNTRACK, 0);
- if (!cth)
- exit_error(OTHER_PROBLEM, "Can't open handler");
-
- nfct_callback_register(cth, NFCT_T_ALL, dump_cb, tmpl.ct);
- res = nfct_query(cth, NFCT_Q_GET, tmpl.ct);
- nfct_close(cth);
+ res = nfct_mnl_request(sock, NFNL_SUBSYS_CTNETLINK, cmd->family,
+ IPCTNL_MSG_CT_GET, 0,
+ mnl_nfct_dump_cb, cmd->tmpl.ct, cmd);
break;
case EXP_GET:
- nfexp_set_attr(tmpl.exp, ATTR_EXP_MASTER, tmpl.ct);
+ nfexp_set_attr(cmd->tmpl.exp, ATTR_EXP_MASTER, cmd->tmpl.ct);
cth = nfct_open(EXPECT, 0);
if (!cth)
exit_error(OTHER_PROBLEM, "Can't open handler");
nfexp_callback_register(cth, NFCT_T_ALL, dump_exp_cb, NULL);
- res = nfexp_query(cth, NFCT_Q_GET, tmpl.exp);
+ res = nfexp_query(cth, NFCT_Q_GET, cmd->tmpl.exp);
nfct_close(cth);
break;
case CT_FLUSH:
- cth = nfct_open(CONNTRACK, 0);
- if (!cth)
- exit_error(OTHER_PROBLEM, "Can't open handler");
- res = nfct_query(cth, NFCT_Q_FLUSH, &family);
- nfct_close(cth);
+ res = nfct_mnl_request(sock, NFNL_SUBSYS_CTNETLINK, cmd->family,
+ IPCTNL_MSG_CT_DELETE, NLM_F_ACK, NULL, NULL, NULL);
+
fprintf(stderr, "%s v%s (conntrack-tools): ",PROGNAME,VERSION);
fprintf(stderr,"connection tracking table has been emptied.\n");
break;
@@ -2808,26 +3421,27 @@ int main(int argc, char *argv[])
cth = nfct_open(EXPECT, 0);
if (!cth)
exit_error(OTHER_PROBLEM, "Can't open handler");
- res = nfexp_query(cth, NFCT_Q_FLUSH, &family);
+ res = nfexp_query(cth, NFCT_Q_FLUSH, &cmd->family);
nfct_close(cth);
fprintf(stderr, "%s v%s (conntrack-tools): ",PROGNAME,VERSION);
fprintf(stderr,"expectation table has been emptied.\n");
break;
case CT_EVENT:
- if (options & CT_OPT_EVENT_MASK) {
+ if (cmd->options & CT_OPT_EVENT_MASK) {
unsigned int nl_events = 0;
- if (event_mask & CT_EVENT_F_NEW)
+ if (cmd->event_mask & CT_EVENT_F_NEW)
nl_events |= NF_NETLINK_CONNTRACK_NEW;
- if (event_mask & CT_EVENT_F_UPD)
+ if (cmd->event_mask & CT_EVENT_F_UPD)
nl_events |= NF_NETLINK_CONNTRACK_UPDATE;
- if (event_mask & CT_EVENT_F_DEL)
+ if (cmd->event_mask & CT_EVENT_F_DEL)
nl_events |= NF_NETLINK_CONNTRACK_DESTROY;
- res = nfct_mnl_socket_open(nl_events);
+ res = nfct_mnl_socket_open(event_sock, nl_events);
} else {
- res = nfct_mnl_socket_open(NF_NETLINK_CONNTRACK_NEW |
+ res = nfct_mnl_socket_open(event_sock,
+ NF_NETLINK_CONNTRACK_NEW |
NF_NETLINK_CONNTRACK_UPDATE |
NF_NETLINK_CONNTRACK_DESTROY);
}
@@ -2835,27 +3449,29 @@ int main(int argc, char *argv[])
if (res < 0)
exit_error(OTHER_PROBLEM, "Can't open netlink socket");
- if (options & CT_OPT_BUFFERSIZE) {
+ if (cmd->options & CT_OPT_BUFFERSIZE) {
+ size_t socketbuffersize = cmd->socketbuffersize;
+
socklen_t socklen = sizeof(socketbuffersize);
- res = setsockopt(mnl_socket_get_fd(sock.mnl),
+ res = setsockopt(mnl_socket_get_fd(sock->mnl),
SOL_SOCKET, SO_RCVBUFFORCE,
&socketbuffersize,
sizeof(socketbuffersize));
if (res < 0) {
- setsockopt(mnl_socket_get_fd(sock.mnl),
+ setsockopt(mnl_socket_get_fd(sock->mnl),
SOL_SOCKET, SO_RCVBUF,
&socketbuffersize,
- socketbuffersize);
+ sizeof(socketbuffersize));
}
- getsockopt(mnl_socket_get_fd(sock.mnl), SOL_SOCKET,
+ getsockopt(mnl_socket_get_fd(sock->mnl), SOL_SOCKET,
SO_RCVBUF, &socketbuffersize, &socklen);
fprintf(stderr, "NOTICE: Netlink socket buffer size "
"has been set to %zu bytes.\n",
socketbuffersize);
}
- nfct_filter_init(family);
+ nfct_filter_init(cmd);
signal(SIGINT, event_sighandler);
signal(SIGTERM, event_sighandler);
@@ -2863,7 +3479,7 @@ int main(int argc, char *argv[])
while (1) {
char buf[MNL_SOCKET_BUFFER_SIZE];
- res = mnl_socket_recvfrom(sock.mnl, buf, sizeof(buf));
+ res = mnl_socket_recvfrom(event_sock->mnl, buf, sizeof(buf));
if (res < 0) {
if (errno == ENOBUFS) {
fprintf(stderr,
@@ -2880,20 +3496,20 @@ int main(int argc, char *argv[])
strerror(errno));
break;
}
- res = mnl_cb_run(buf, res, 0, 0, event_cb, tmpl.ct);
+ mnl_cb_run(buf, res, 0, 0, event_cb, cmd);
}
- mnl_socket_close(sock.mnl);
+ mnl_socket_close(event_sock->mnl);
break;
case EXP_EVENT:
- if (options & CT_OPT_EVENT_MASK) {
+ if (cmd->options & CT_OPT_EVENT_MASK) {
unsigned int nl_events = 0;
- if (event_mask & CT_EVENT_F_NEW)
+ if (cmd->event_mask & CT_EVENT_F_NEW)
nl_events |= NF_NETLINK_CONNTRACK_EXP_NEW;
- if (event_mask & CT_EVENT_F_UPD)
+ if (cmd->event_mask & CT_EVENT_F_UPD)
nl_events |= NF_NETLINK_CONNTRACK_EXP_UPDATE;
- if (event_mask & CT_EVENT_F_DEL)
+ if (cmd->event_mask & CT_EVENT_F_DEL)
nl_events |= NF_NETLINK_CONNTRACK_EXP_DESTROY;
cth = nfct_open(CONNTRACK, nl_events);
@@ -2916,20 +3532,14 @@ int main(int argc, char *argv[])
/* If we fail with netlink, fall back to /proc to ensure
* backward compatibility.
*/
- if (nfct_mnl_socket_open(0) < 0)
- goto try_proc_count;
-
- res = nfct_mnl_get(NFNL_SUBSYS_CTNETLINK,
- IPCTNL_MSG_CT_GET_STATS,
- nfct_global_stats_cb, AF_UNSPEC);
-
- nfct_mnl_socket_close();
+ res = nfct_mnl_request(sock, NFNL_SUBSYS_CTNETLINK, AF_UNSPEC,
+ IPCTNL_MSG_CT_GET_STATS, 0,
+ nfct_global_stats_cb, NULL, NULL);
/* don't look at /proc, we got the information via ctnetlink */
if (res >= 0)
break;
-try_proc_count:
{
#define NF_CONNTRACK_COUNT_PROC "/proc/sys/net/netfilter/nf_conntrack_count"
FILE *fd;
@@ -2953,7 +3563,7 @@ try_proc_count:
exit_error(OTHER_PROBLEM, "Can't open handler");
nfexp_callback_register(cth, NFCT_T_ALL, count_exp_cb, NULL);
- res = nfexp_query(cth, NFCT_Q_DUMP, &family);
+ res = nfexp_query(cth, NFCT_Q_DUMP, &cmd->family);
nfct_close(cth);
printf("%d\n", counter);
break;
@@ -2961,14 +3571,9 @@ try_proc_count:
/* If we fail with netlink, fall back to /proc to ensure
* backward compatibility.
*/
- if (nfct_mnl_socket_open(0) < 0)
- goto try_proc;
-
- res = nfct_mnl_dump(NFNL_SUBSYS_CTNETLINK,
+ res = nfct_mnl_dump(sock, NFNL_SUBSYS_CTNETLINK,
IPCTNL_MSG_CT_GET_STATS_CPU,
- nfct_stats_cb, AF_UNSPEC);
-
- nfct_mnl_socket_close();
+ nfct_stats_cb, NULL, NULL);
/* don't look at /proc, we got the information via ctnetlink */
if (res >= 0)
@@ -2980,14 +3585,9 @@ try_proc_count:
/* If we fail with netlink, fall back to /proc to ensure
* backward compatibility.
*/
- if (nfct_mnl_socket_open(0) < 0)
- goto try_proc;
-
- res = nfct_mnl_dump(NFNL_SUBSYS_CTNETLINK_EXP,
+ res = nfct_mnl_dump(sock, NFNL_SUBSYS_CTNETLINK_EXP,
IPCTNL_MSG_EXP_GET_STATS_CPU,
- nfexp_stats_cb, AF_UNSPEC);
-
- nfct_mnl_socket_close();
+ nfexp_stats_cb, NULL, NULL);
/* don't look at /proc, we got the information via ctnetlink */
if (res >= 0)
@@ -3000,30 +3600,263 @@ try_proc:
printf("%s v%s (conntrack-tools)\n", PROGNAME, VERSION);
break;
case CT_HELP:
- usage(argv[0]);
- if (options & CT_OPT_PROTO)
- extension_help(h, protonum);
+ usage(progname);
+ if (cmd->options & CT_OPT_PROTO)
+ extension_help(h, cmd->protonum);
break;
default:
- usage(argv[0]);
+ usage(progname);
break;
}
if (res < 0)
exit_error(OTHER_PROBLEM, "Operation failed: %s",
- err2str(errno, command));
+ err2str(errno, cmd->command));
- free_tmpl_objects();
- free_options();
+ free_tmpl_objects(&cmd->tmpl);
if (labelmap)
nfct_labelmap_destroy(labelmap);
- if (command && exit_msg[cmd][0]) {
- fprintf(stderr, "%s v%s (conntrack-tools): ",PROGNAME,VERSION);
- fprintf(stderr, exit_msg[cmd], counter);
- if (counter == 0 && !(command & (CT_LIST | EXP_LIST)))
- return EXIT_FAILURE;
+ return EXIT_SUCCESS;
+}
+
+/* Taken from iptables/xshared.c:
+ * - add_argv()
+ * - add_param_to_argv()
+ * - free_argv()
+ */
+#define MAX_ARGC 255
+struct argv_store {
+ int argc;
+ char *argv[MAX_ARGC];
+ int argvattr[MAX_ARGC];
+};
+
+/* function adding one argument to store, updating argc
+ * returns if argument added, does not return otherwise */
+static void add_argv(struct argv_store *store, const char *what, int quoted)
+{
+ if (store->argc + 1 >= MAX_ARGC)
+ exit_error(PARAMETER_PROBLEM, "too many arguments");
+ if (!what)
+ exit_error(PARAMETER_PROBLEM, "invalid NULL argument");
+
+ store->argv[store->argc] = strdup(what);
+ store->argvattr[store->argc] = quoted;
+ store->argv[++store->argc] = NULL;
+}
+
+static void free_argv(struct argv_store *store)
+{
+ while (store->argc) {
+ store->argc--;
+ free(store->argv[store->argc]);
+ store->argvattr[store->argc] = 0;
}
+}
- return EXIT_SUCCESS;
+struct ct_param_buf {
+ char buffer[1024];
+ int len;
+};
+
+static void add_param(struct ct_param_buf *param, const char *curchar)
+{
+ param->buffer[param->len++] = *curchar;
+ if (param->len >= (int)sizeof(param->buffer))
+ exit_error(PARAMETER_PROBLEM, "Parameter too long!");
+}
+
+static void add_param_to_argv(struct argv_store *store, char *parsestart)
+{
+ int quote_open = 0, escaped = 0, quoted = 0;
+ struct ct_param_buf param = {};
+ char *curchar;
+
+ /* After fighting with strtok enough, here's now
+ * a 'real' parser. According to Rusty I'm now no
+ * longer a real hacker, but I can live with that */
+
+ for (curchar = parsestart; *curchar; curchar++) {
+ if (quote_open) {
+ if (escaped) {
+ add_param(&param, curchar);
+ escaped = 0;
+ continue;
+ } else if (*curchar == '\\') {
+ escaped = 1;
+ continue;
+ } else if (*curchar == '"') {
+ quote_open = 0;
+ } else {
+ add_param(&param, curchar);
+ continue;
+ }
+ } else {
+ if (*curchar == '"') {
+ quote_open = 1;
+ quoted = 1;
+ continue;
+ }
+ }
+
+ switch (*curchar) {
+ case '"':
+ break;
+ case ' ':
+ case '\t':
+ case '\n':
+ if (!param.len) {
+ /* two spaces? */
+ continue;
+ }
+ break;
+ default:
+ /* regular character, copy to buffer */
+ add_param(&param, curchar);
+ continue;
+ }
+
+ param.buffer[param.len] = '\0';
+ add_argv(store, param.buffer, quoted);
+ param.len = 0;
+ quoted = 0;
+ }
+ if (param.len) {
+ param.buffer[param.len] = '\0';
+ add_argv(store, param.buffer, 0);
+ }
+}
+
+static void ct_file_parse_line(struct list_head *cmd_list,
+ const char *progname, char *buffer)
+{
+ struct argv_store store = {};
+ struct ct_cmd *ct_cmd;
+
+ /* skip prepended tabs and spaces */
+ for (; *buffer == ' ' || *buffer == '\t'; buffer++);
+
+ if (buffer[0] == '\n' ||
+ buffer[0] == '#')
+ return;
+
+ add_argv(&store, progname, false);
+ add_param_to_argv(&store, buffer);
+
+ ct_cmd = calloc(1, sizeof(*ct_cmd));
+ if (!ct_cmd)
+ exit_error(OTHER_PROBLEM, "OOM");
+
+ do_parse(ct_cmd, store.argc, store.argv);
+ free_argv(&store);
+
+ list_add_tail(&ct_cmd->list, cmd_list);
+}
+
+static void ct_parse_file(struct list_head *cmd_list, const char *progname,
+ const char *file_name)
+{
+ char buffer[10240] = {};
+ FILE *file;
+
+ if (!strcmp(file_name, "-"))
+ file_name = "/dev/stdin";
+
+ file = fopen(file_name, "r");
+ if (!file)
+ exit_error(PARAMETER_PROBLEM,
+ "Failed to open file %s for reading", file_name);
+
+ while (fgets(buffer, sizeof(buffer), file))
+ ct_file_parse_line(cmd_list, progname, buffer);
+
+ fclose(file);
+}
+
+static struct {
+ uint32_t command;
+ const char *text;
+} ct_unsupp_cmd_parse_file[] = {
+ { CT_LIST, "-L" },
+ { CT_GET, "-G" },
+ { CT_EVENT, "-E" },
+ { CT_VERSION, "-V" },
+ { CT_HELP, "-h" },
+ { EXP_LIST, "-L expect" },
+ { EXP_CREATE, "-C expect" },
+ { EXP_DELETE, "-D expect" },
+ { EXP_GET, "-G expect" },
+ { EXP_FLUSH, "-F expect" },
+ { EXP_EVENT, "-E expect" },
+ { CT_COUNT, "-C" },
+ { EXP_COUNT, "-C expect" },
+ { CT_STATS, "-S" },
+ { EXP_STATS, "-S expect" },
+ { 0, NULL },
+};
+
+static const char *ct_unsupp_cmd_file(const struct ct_cmd *cmd)
+{
+ int i;
+
+ for (i = 0; ct_unsupp_cmd_parse_file[i].text; i++) {
+ if (cmd->command == ct_unsupp_cmd_parse_file[i].command)
+ return ct_unsupp_cmd_parse_file[i].text;
+ }
+
+ return "unknown";
+}
+
+int main(int argc, char *argv[])
+{
+ struct nfct_mnl_socket *modifier_sock = &_modifier_sock;
+ struct nfct_mnl_socket *sock = &_sock;
+ struct ct_cmd *cmd, *next;
+ LIST_HEAD(cmd_list);
+ int res = 0;
+
+ register_tcp();
+ register_udp();
+ register_udplite();
+ register_sctp();
+ register_dccp();
+ register_icmp();
+ register_icmpv6();
+ register_gre();
+ register_unknown();
+
+ if (nfct_mnl_socket_open(sock, 0) < 0)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+
+ if (argc > 2 &&
+ (!strcmp(argv[1], "-R") || !strcmp(argv[1], "--load-file"))) {
+ ct_parse_file(&cmd_list, argv[0], argv[2]);
+
+ list_for_each_entry(cmd, &cmd_list, list) {
+ if (!(cmd->command &
+ (CT_CREATE | CT_ADD | CT_UPDATE | CT_DELETE | CT_FLUSH)))
+ exit_error(PARAMETER_PROBLEM,
+ "Cannot use command `%s' with --load-file",
+ ct_unsupp_cmd_file(cmd));
+ }
+ list_for_each_entry_safe(cmd, next, &cmd_list, list) {
+ res |= do_command_ct(argv[0], cmd, sock);
+ list_del(&cmd->list);
+ free(cmd);
+ }
+ } else {
+ cmd = calloc(1, sizeof(*cmd));
+ if (!cmd)
+ exit_error(OTHER_PROBLEM, "OOM");
+
+ do_parse(cmd, argc, argv);
+ res = do_command_ct(argv[0], cmd, sock);
+ res |= print_stats(cmd);
+ free(cmd);
+ }
+ nfct_mnl_socket_close(sock);
+ nfct_mnl_socket_check_close(modifier_sock);
+
+ return res < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
diff --git a/src/cthelper.c b/src/cthelper.c
index f01c509..f133e4b 100644
--- a/src/cthelper.c
+++ b/src/cthelper.c
@@ -386,6 +386,10 @@ static int cthelper_setup(struct ctd_helper_instance *cur)
nfct_helper_attr_set_u32(t, NFCTH_ATTR_QUEUE_NUM, cur->queue_num);
nfct_helper_attr_set_u16(t, NFCTH_ATTR_PROTO_L3NUM, cur->l3proto);
nfct_helper_attr_set_u8(t, NFCTH_ATTR_PROTO_L4NUM, cur->l4proto);
+ if (CONFIG(cthelper).setup) {
+ nfct_helper_attr_set_u32(t, NFCTH_ATTR_PRIV_DATA_LEN,
+ cur->helper->priv_data_len);
+ }
nfct_helper_attr_set_u32(t, NFCTH_ATTR_STATUS,
NFCT_HELPER_STATUS_ENABLED);
diff --git a/src/external_inject.c b/src/external_inject.c
index 0ad3478..920d7c4 100644
--- a/src/external_inject.c
+++ b/src/external_inject.c
@@ -76,12 +76,14 @@ retry:
}
}
external_inject_stat.add_fail++;
- dlog(LOG_ERR, "inject-add1: %s", strerror(errno));
+ dlog(LOG_WARNING,
+ "could not add new ct entry, even when deleting it first: %s",
+ strerror(errno));
dlog_ct(STATE(log), ct, NFCT_O_PLAIN);
return;
}
external_inject_stat.add_fail++;
- dlog(LOG_ERR, "inject-add2: %s", strerror(errno));
+ dlog(LOG_WARNING, "could not add new ct entry: %s", strerror(errno));
dlog_ct(STATE(log), ct, NFCT_O_PLAIN);
} else {
external_inject_stat.add_ok++;
@@ -102,7 +104,9 @@ static void external_inject_ct_upd(struct nf_conntrack *ct)
if (errno == ENOENT) {
if (nl_create_conntrack(inject, ct, 0) == -1) {
external_inject_stat.upd_fail++;
- dlog(LOG_ERR, "inject-upd1: %s", strerror(errno));
+ dlog(LOG_WARNING,
+ "could not update ct entry, even if creating it instead: %s",
+ strerror(errno));
dlog_ct(STATE(log), ct, NFCT_O_PLAIN);
} else {
external_inject_stat.upd_ok++;
@@ -117,7 +121,9 @@ static void external_inject_ct_upd(struct nf_conntrack *ct)
if (ret == 0 || (ret == -1 && errno == ENOENT)) {
if (nl_create_conntrack(inject, ct, 0) == -1) {
external_inject_stat.upd_fail++;
- dlog(LOG_ERR, "inject-upd2: %s", strerror(errno));
+ dlog(LOG_WARNING,
+ "could not update ct entry, even when deleting it first: %s",
+ strerror(errno));
dlog_ct(STATE(log), ct, NFCT_O_PLAIN);
} else {
external_inject_stat.upd_ok++;
@@ -125,7 +131,7 @@ static void external_inject_ct_upd(struct nf_conntrack *ct)
return;
}
external_inject_stat.upd_fail++;
- dlog(LOG_ERR, "inject-upd3: %s", strerror(errno));
+ dlog(LOG_WARNING, "could not update ct entry: %s", strerror(errno));
dlog_ct(STATE(log), ct, NFCT_O_PLAIN);
}
@@ -134,7 +140,7 @@ static void external_inject_ct_del(struct nf_conntrack *ct)
if (nl_destroy_conntrack(inject, ct) == -1) {
if (errno != ENOENT) {
external_inject_stat.del_fail++;
- dlog(LOG_ERR, "inject-del: %s", strerror(errno));
+ dlog(LOG_WARNING, "could not destroy ct entry: %s", strerror(errno));
dlog_ct(STATE(log), ct, NFCT_O_PLAIN);
}
} else {
@@ -200,12 +206,15 @@ retry:
}
}
exp_external_inject_stat.add_fail++;
- dlog(LOG_ERR, "inject-add1: %s", strerror(errno));
+ dlog(LOG_WARNING,
+ "could not add new exp entry, even when deleting it first: %s",
+ strerror(errno));
dlog_exp(STATE(log), exp, NFCT_O_PLAIN);
return;
}
exp_external_inject_stat.add_fail++;
- dlog(LOG_ERR, "inject-add2: %s", strerror(errno));
+ dlog(LOG_WARNING,
+ "could not add new exp entry: %s", strerror(errno));
dlog_exp(STATE(log), exp, NFCT_O_PLAIN);
} else {
exp_external_inject_stat.add_ok++;
@@ -217,7 +226,8 @@ static void external_inject_exp_del(struct nf_expect *exp)
if (nl_destroy_expect(inject, exp) == -1) {
if (errno != ENOENT) {
exp_external_inject_stat.del_fail++;
- dlog(LOG_ERR, "inject-del: %s", strerror(errno));
+ dlog(LOG_WARNING,
+ "could not delete exp entry: %s", strerror(errno));
dlog_exp(STATE(log), exp, NFCT_O_PLAIN);
}
} else {
diff --git a/src/filter.c b/src/filter.c
index 6577102..ee316e7 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -476,7 +476,7 @@ struct exp_filter *exp_filter_create(void)
struct exp_filter_item {
struct list_head head;
- char helper_name[NFCT_HELPER_NAME_MAX];
+ char helper_name[NFCT_HELPER_NAME_MAX + 1];
};
/* this is ugly, but it simplifies read_config_yy.y */
diff --git a/src/hash.c b/src/hash.c
index fe6a047..a0f240c 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -55,6 +55,7 @@ hashtable_create(int hashsize, int limit,
void hashtable_destroy(struct hashtable *h)
{
+ hashtable_flush(h);
free(h);
}
diff --git a/src/helpers.c b/src/helpers.c
index 3e4e6c8..8ca78dc 100644
--- a/src/helpers.c
+++ b/src/helpers.c
@@ -26,8 +26,7 @@ void helper_register(struct ctd_helper *helper)
list_add(&helper->head, &helper_list);
}
-static struct ctd_helper *
-__helper_find(const char *helper_name, uint8_t l4proto)
+struct ctd_helper *__helper_find(const char *helper_name, uint8_t l4proto)
{
struct ctd_helper *cur, *helper = NULL;
diff --git a/src/helpers/Makefile.am b/src/helpers/Makefile.am
index e4f10c9..e458ab4 100644
--- a/src/helpers/Makefile.am
+++ b/src/helpers/Makefile.am
@@ -11,7 +11,7 @@ pkglib_LTLIBRARIES = ct_helper_amanda.la \
ct_helper_slp.la \
ct_helper_ssdp.la
-HELPER_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) $(LAZY_LDFLAGS)
+HELPER_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS)
HELPER_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS)
ct_helper_amanda_la_SOURCES = amanda.c
diff --git a/src/helpers/amanda.c b/src/helpers/amanda.c
index faee1cd..234d511 100644
--- a/src/helpers/amanda.c
+++ b/src/helpers/amanda.c
@@ -24,6 +24,7 @@
#include <netinet/udp.h>
#include <libmnl/libmnl.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <linux/netfilter/nfnetlink_queue.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <libnetfilter_queue/libnetfilter_queue_udp.h>
#include <libnetfilter_queue/pktbuff.h>
diff --git a/src/helpers/dhcpv6.c b/src/helpers/dhcpv6.c
index f87b6ce..4c5676d 100644
--- a/src/helpers/dhcpv6.c
+++ b/src/helpers/dhcpv6.c
@@ -25,6 +25,7 @@
#include <netinet/udp.h>
#include <libmnl/libmnl.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <linux/netfilter/nfnetlink_queue.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <libnetfilter_queue/libnetfilter_queue_udp.h>
#include <libnetfilter_queue/pktbuff.h>
diff --git a/src/helpers/ftp.c b/src/helpers/ftp.c
index c3aa284..2b34534 100644
--- a/src/helpers/ftp.c
+++ b/src/helpers/ftp.c
@@ -30,22 +30,15 @@
#include <libmnl/libmnl.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <linux/netfilter/nfnetlink_queue.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
#include <libnetfilter_queue/pktbuff.h>
#include <linux/netfilter.h>
-static bool loose; /* XXX: export this as config option. */
-
-#define NUM_SEQ_TO_REMEMBER 2
+#include "helpers/ftp.h"
-/* This structure exists only once per master */
-struct ftp_info {
- /* Valid seq positions for cmd matching after newline */
- uint32_t seq_aft_nl[MYCT_DIR_MAX][NUM_SEQ_TO_REMEMBER];
- /* 0 means seq_match_aft_nl not set */
- int seq_aft_nl_num[MYCT_DIR_MAX];
-};
+static bool loose; /* XXX: export this as config option. */
enum nf_ct_ftp_type {
/* PORT command from client */
@@ -339,23 +332,21 @@ static int nf_nat_ftp_fmt_cmd(enum nf_ct_ftp_type type,
char *buffer, size_t buflen,
uint32_t addr, uint16_t port)
{
+ union {
+ unsigned char c[4];
+ uint32_t d;
+ } tmp;
+
+ tmp.d = addr;
switch (type) {
case NF_CT_FTP_PORT:
case NF_CT_FTP_PASV:
return snprintf(buffer, buflen, "%u,%u,%u,%u,%u,%u",
- ((unsigned char *)&addr)[0],
- ((unsigned char *)&addr)[1],
- ((unsigned char *)&addr)[2],
- ((unsigned char *)&addr)[3],
- port >> 8,
- port & 0xFF);
+ tmp.c[0], tmp.c[1], tmp.c[2], tmp.c[3],
+ port >> 8, port & 0xFF);
case NF_CT_FTP_EPRT:
return snprintf(buffer, buflen, "|1|%u.%u.%u.%u|%u|",
- ((unsigned char *)&addr)[0],
- ((unsigned char *)&addr)[1],
- ((unsigned char *)&addr)[2],
- ((unsigned char *)&addr)[3],
- port);
+ tmp.c[0], tmp.c[1], tmp.c[2], tmp.c[3], port);
case NF_CT_FTP_EPSV:
return snprintf(buffer, buflen, "|||%u|", port);
}
diff --git a/src/helpers/rpc.c b/src/helpers/rpc.c
index bd24dd3..732e9ba 100644
--- a/src/helpers/rpc.c
+++ b/src/helpers/rpc.c
@@ -35,26 +35,18 @@
#include <libmnl/libmnl.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <linux/netfilter/nfnetlink_queue.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
#include <libnetfilter_queue/pktbuff.h>
#include <linux/netfilter.h>
+#include "helpers/rpc.h"
+
/* RFC 1050: RPC: Remote Procedure Call Protocol Specification Version 2 */
/* RFC 1014: XDR: External Data Representation Standard */
#define SUPPORTED_RPC_VERSION 2
-struct rpc_info {
- /* XID */
- uint32_t xid;
- /* program */
- uint32_t pm_prog;
- /* program version */
- uint32_t pm_vers;
- /* transport protocol: TCP|UDP */
- uint32_t pm_prot;
-};
-
/* So, this packet has hit the connection tracking matching code.
Mangle it, and change the expectation to match the new version. */
static unsigned int
diff --git a/src/helpers/sane.c b/src/helpers/sane.c
index c30f4ba..ebcb24c 100644
--- a/src/helpers/sane.c
+++ b/src/helpers/sane.c
@@ -34,15 +34,12 @@
#include <netinet/tcp.h>
#include <libmnl/libmnl.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <linux/netfilter/nfnetlink_queue.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
#include <libnetfilter_queue/pktbuff.h>
#include <linux/netfilter.h>
-
-enum sane_state {
- SANE_STATE_NORMAL,
- SANE_STATE_START_REQUESTED,
-};
+#include "helpers/sane.h"
struct sane_request {
uint32_t RPC_code;
@@ -60,10 +57,6 @@ struct sane_reply_net_start {
/* other fields aren't interesting for conntrack */
};
-struct nf_ct_sane_master {
- enum sane_state state;
-};
-
static int
sane_helper_cb(struct pkt_buff *pkt, uint32_t protoff,
struct myct *myct, uint32_t ctinfo)
diff --git a/src/helpers/slp.c b/src/helpers/slp.c
index b8339d6..a53485a 100644
--- a/src/helpers/slp.c
+++ b/src/helpers/slp.c
@@ -28,6 +28,7 @@
#include "myct.h"
#include "log.h"
+#include <linux/netfilter/nfnetlink_queue.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
#include <linux/netfilter.h>
diff --git a/src/helpers/ssdp.c b/src/helpers/ssdp.c
index 58658e3..527892c 100644
--- a/src/helpers/ssdp.c
+++ b/src/helpers/ssdp.c
@@ -48,12 +48,12 @@
#include <errno.h>
#include <stdlib.h>
#include <arpa/inet.h>
-#include <netinet/ip.h>
#define _GNU_SOURCE
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <libmnl/libmnl.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <linux/netfilter/nfnetlink_queue.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
#include <libnetfilter_queue/pktbuff.h>
@@ -158,11 +158,9 @@ static int handle_ssdp_new(struct pkt_buff *pkt, uint32_t protoff,
{
int ret = NF_ACCEPT;
union nfct_attr_grp_addr daddr, saddr, taddr;
- struct iphdr *net_hdr = (struct iphdr *)pktb_network_header(pkt);
int good_packet = 0;
struct nf_expect *exp;
uint16_t port;
- unsigned int dataoff;
void *sb_ptr;
cthelper_get_addr_dst(myct->ct, MYCT_DIR_ORIG, &daddr);
@@ -200,13 +198,12 @@ static int handle_ssdp_new(struct pkt_buff *pkt, uint32_t protoff,
}
/* No data? Ignore */
- dataoff = net_hdr->ihl*4 + sizeof(struct udphdr);
- if (dataoff >= pktb_len(pkt)) {
+ if (protoff + sizeof(struct udphdr) >= pktb_len(pkt)) {
pr_debug("ssdp_help: UDP payload too small for M-SEARCH; ignoring\n");
return NF_ACCEPT;
}
- sb_ptr = pktb_network_header(pkt) + dataoff;
+ sb_ptr = pktb_network_header(pkt) + protoff + sizeof(struct udphdr);
if (memcmp(sb_ptr, SSDP_M_SEARCH, SSDP_M_SEARCH_SIZE) != 0) {
pr_debug("ssdp_help: UDP payload does not begin with 'M-SEARCH'; ignoring\n");
@@ -259,7 +256,6 @@ static int find_hdr(const char *name, const uint8_t *data, int data_len,
data += i+2;
}
- data_len -= name_len;
data += name_len;
if (pos)
*pos = data;
diff --git a/src/helpers/tftp.c b/src/helpers/tftp.c
index 70dd28a..e61195f 100644
--- a/src/helpers/tftp.c
+++ b/src/helpers/tftp.c
@@ -20,6 +20,7 @@
#include <netinet/udp.h>
#include <libmnl/libmnl.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <linux/netfilter/nfnetlink_queue.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <libnetfilter_queue/libnetfilter_queue_udp.h>
#include <libnetfilter_queue/pktbuff.h>
diff --git a/src/helpers/tns.c b/src/helpers/tns.c
index 2b4fed4..5692f29 100644
--- a/src/helpers/tns.c
+++ b/src/helpers/tns.c
@@ -23,11 +23,14 @@
#include <libmnl/libmnl.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <linux/netfilter/nfnetlink_queue.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
#include <libnetfilter_queue/pktbuff.h>
#include <linux/netfilter.h>
+#include "helpers/tns.h"
+
/* TNS SQL*Net Version 2 */
enum tns_types {
TNS_TYPE_CONNECT = 1,
@@ -57,11 +60,6 @@ struct tns_redirect {
uint16_t data_len;
};
-struct tns_info {
- /* Scan next DATA|REDIRECT packet */
- bool parse;
-};
-
static int try_number(const char *data, size_t dlen, uint32_t array[],
int array_size, char sep, char term)
{
diff --git a/src/main.c b/src/main.c
index 31e0eed..c6b2600 100644
--- a/src/main.c
+++ b/src/main.c
@@ -175,7 +175,7 @@ int main(int argc, char *argv[])
}
show_usage(argv[0]);
dlog(LOG_ERR, "Missing config filename");
- break;
+ exit(EXIT_FAILURE);
case 'F':
set_operation_mode(&type, REQUEST, argv);
i = set_action_by_table(i, argc, argv,
@@ -309,8 +309,7 @@ int main(int argc, char *argv[])
default:
show_usage(argv[0]);
dlog(LOG_ERR, "Unknown option: %s", argv[i]);
- return 0;
- break;
+ exit(EXIT_FAILURE);
}
}
@@ -319,7 +318,7 @@ int main(int argc, char *argv[])
umask(0177);
- if ((ret = init_config(config_file)) == -1) {
+ if (init_config(config_file) == -1) {
dlog(LOG_ERR, "can't open config file `%s'", config_file);
exit(EXIT_FAILURE);
}
diff --git a/src/network.c b/src/network.c
index 13db37c..2560d97 100644
--- a/src/network.c
+++ b/src/network.c
@@ -113,7 +113,7 @@ void nethdr_track_update_seq(uint32_t seq)
STATE_SYNC(last_seq_recv) = seq;
}
-int nethdr_track_is_seq_set()
+int nethdr_track_is_seq_set(void)
{
return local_seq_set;
}
diff --git a/src/nfct-extensions/helper.c b/src/nfct-extensions/helper.c
index e5d8d0a..894bf26 100644
--- a/src/nfct-extensions/helper.c
+++ b/src/nfct-extensions/helper.c
@@ -180,7 +180,7 @@ static int nfct_cmd_helper_add(struct mnl_socket *nl, int argc, char *argv[])
return -1;
}
- helper = helper_find(CONNTRACKD_LIB_DIR, argv[3], l4proto, RTLD_LAZY);
+ helper = __helper_find(argv[3], l4proto);
if (helper == NULL) {
nfct_perror("that helper is not supported");
return -1;
@@ -430,7 +430,7 @@ nfct_cmd_helper_disable(struct mnl_socket *nl, int argc, char *argv[])
return -1;
}
- helper = helper_find(CONNTRACKD_LIB_DIR, argv[3], l4proto, RTLD_LAZY);
+ helper = __helper_find(argv[3], l4proto);
if (helper == NULL) {
nfct_perror("that helper is not supported");
return -1;
@@ -468,7 +468,187 @@ static struct nfct_extension helper = {
.parse_params = nfct_helper_parse_params,
};
+/*
+ * supported helpers: to set up helpers via nfct, the following definitions are
+ * provided for backward compatibility reasons since conntrackd does not depend
+ * on nfct anymore to set up the userspace helpers.
+ */
+
+static struct ctd_helper amanda_helper = {
+ .name = "amanda",
+ .l4proto = IPPROTO_UDP,
+ .policy = {
+ [0] = {
+ .name = "amanda",
+ .expect_max = 3,
+ .expect_timeout = 180,
+ },
+ },
+};
+
+static struct ctd_helper dhcpv6_helper = {
+ .name = "dhcpv6",
+ .l4proto = IPPROTO_UDP,
+ .policy = {
+ [0] = {
+ .name = "dhcpv6",
+ .expect_max = 1,
+ .expect_timeout = 300,
+ },
+ },
+};
+
+#include "helpers/ftp.h"
+
+static struct ctd_helper ftp_helper = {
+ .name = "ftp",
+ .l4proto = IPPROTO_TCP,
+ .priv_data_len = sizeof(struct ftp_info),
+ .policy = {
+ [0] = {
+ .name = "ftp",
+ .expect_max = 1,
+ .expect_timeout = 300,
+ },
+ },
+};
+
+static struct ctd_helper mdns_helper = {
+ .name = "mdns",
+ .l4proto = IPPROTO_UDP,
+ .priv_data_len = 0,
+ .policy = {
+ [0] = {
+ .name = "mdns",
+ .expect_max = 8,
+ .expect_timeout = 30,
+ },
+ },
+};
+
+#include "helpers/rpc.h"
+
+static struct ctd_helper rpc_helper_tcp = {
+ .name = "rpc",
+ .l4proto = IPPROTO_TCP,
+ .priv_data_len = sizeof(struct rpc_info),
+ .policy = {
+ {
+ .name = "rpc",
+ .expect_max = 1,
+ .expect_timeout = 300,
+ },
+ },
+};
+
+static struct ctd_helper rpc_helper_udp = {
+ .name = "rpc",
+ .l4proto = IPPROTO_UDP,
+ .priv_data_len = sizeof(struct rpc_info),
+ .policy = {
+ {
+ .name = "rpc",
+ .expect_max = 1,
+ .expect_timeout = 300,
+ },
+ },
+};
+
+#include "helpers/sane.h"
+
+static struct ctd_helper sane_helper = {
+ .name = "sane",
+ .l4proto = IPPROTO_TCP,
+ .priv_data_len = sizeof(struct nf_ct_sane_master),
+ .policy = {
+ [0] = {
+ .name = "sane",
+ .expect_max = 1,
+ .expect_timeout = 5 * 60,
+ },
+ },
+};
+
+static struct ctd_helper slp_helper = {
+ .name = "slp",
+ .l4proto = IPPROTO_UDP,
+ .priv_data_len = 0,
+ .policy = {
+ [0] = {
+ .name = "slp",
+ .expect_max = 8,
+ .expect_timeout = 16, /* default CONFIG_MC_MAX + 1 */
+ },
+ },
+};
+
+static struct ctd_helper ssdp_helper_udp = {
+ .name = "ssdp",
+ .l4proto = IPPROTO_UDP,
+ .priv_data_len = 0,
+ .policy = {
+ [0] = {
+ .name = "ssdp",
+ .expect_max = 8,
+ .expect_timeout = 5 * 60,
+ },
+ },
+};
+
+static struct ctd_helper ssdp_helper_tcp = {
+ .name = "ssdp",
+ .l4proto = IPPROTO_TCP,
+ .priv_data_len = 0,
+ .policy = {
+ [0] = {
+ .name = "ssdp",
+ .expect_max = 8,
+ .expect_timeout = 5 * 60,
+ },
+ },
+};
+
+static struct ctd_helper tftp_helper = {
+ .name = "tftp",
+ .l4proto = IPPROTO_UDP,
+ .policy = {
+ [0] = {
+ .name = "tftp",
+ .expect_max = 1,
+ .expect_timeout = 5 * 60,
+ },
+ },
+};
+
+#include "helpers/tns.h"
+
+static struct ctd_helper tns_helper = {
+ .name = "tns",
+ .l4proto = IPPROTO_TCP,
+ .priv_data_len = sizeof(struct tns_info),
+ .policy = {
+ [0] = {
+ .name = "tns",
+ .expect_max = 1,
+ .expect_timeout = 300,
+ },
+ },
+};
+
static void __init helper_init(void)
{
+ helper_register(&amanda_helper);
+ helper_register(&dhcpv6_helper);
+ helper_register(&ftp_helper);
+ helper_register(&mdns_helper);
+ helper_register(&rpc_helper_tcp);
+ helper_register(&rpc_helper_udp);
+ helper_register(&sane_helper);
+ helper_register(&slp_helper);
+ helper_register(&ssdp_helper_udp);
+ helper_register(&ssdp_helper_tcp);
+ helper_register(&tftp_helper);
+ helper_register(&tns_helper);
+
nfct_extension_register(&helper);
}
diff --git a/src/parse.c b/src/parse.c
index 7e524ed..eedba66 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -29,7 +29,8 @@
static void ct_parse_u8(struct nf_conntrack *ct, int attr, void *data);
static void ct_parse_u16(struct nf_conntrack *ct, int attr, void *data);
static void ct_parse_u32(struct nf_conntrack *ct, int attr, void *data);
-static void ct_parse_u128(struct nf_conntrack *ct, int attr, void *data);
+static void ct_parse_be32(struct nf_conntrack *ct, int attr, void *data);
+static void ct_parse_be128(struct nf_conntrack *ct, int attr, void *data);
static void ct_parse_str(struct nf_conntrack *ct,
const struct netattr *, void *data);
static void ct_parse_group(struct nf_conntrack *ct, int attr, void *data);
@@ -108,12 +109,12 @@ static struct ct_parser h[NTA_MAX] = {
.size = NTA_SIZE(sizeof(struct nfct_attr_grp_port)),
},
[NTA_SNAT_IPV4] = {
- .parse = ct_parse_u32,
+ .parse = ct_parse_be32,
.attr = ATTR_SNAT_IPV4,
.size = NTA_SIZE(sizeof(uint32_t)),
},
[NTA_DNAT_IPV4] = {
- .parse = ct_parse_u32,
+ .parse = ct_parse_be32,
.attr = ATTR_DNAT_IPV4,
.size = NTA_SIZE(sizeof(uint32_t)),
},
@@ -192,12 +193,12 @@ static struct ct_parser h[NTA_MAX] = {
.max_size = NTA_SIZE(NTA_LABELS_MAX_SIZE),
},
[NTA_SNAT_IPV6] = {
- .parse = ct_parse_u128,
+ .parse = ct_parse_be128,
.attr = ATTR_SNAT_IPV6,
.size = NTA_SIZE(sizeof(uint32_t) * 4),
},
[NTA_DNAT_IPV6] = {
- .parse = ct_parse_u128,
+ .parse = ct_parse_be128,
.attr = ATTR_DNAT_IPV6,
.size = NTA_SIZE(sizeof(uint32_t) * 4),
},
@@ -205,6 +206,11 @@ static struct ct_parser h[NTA_MAX] = {
.parse = ct_parse_synproxy,
.size = NTA_SIZE(sizeof(struct nta_attr_synproxy)),
},
+ [NTA_ZONE] = {
+ .parse = ct_parse_u16,
+ .attr = ATTR_ZONE,
+ .size = NTA_SIZE(sizeof(uint16_t)),
+ },
};
static void
@@ -229,7 +235,14 @@ ct_parse_u32(struct nf_conntrack *ct, int attr, void *data)
}
static void
-ct_parse_u128(struct nf_conntrack *ct, int attr, void *data)
+ct_parse_be32(struct nf_conntrack *ct, int attr, void *data)
+{
+ uint32_t *value = (uint32_t *) data;
+ nfct_set_attr_u32(ct, h[attr].attr, *value);
+}
+
+static void
+ct_parse_be128(struct nf_conntrack *ct, int attr, void *data)
{
nfct_set_attr(ct, h[attr].attr, data);
}
diff --git a/src/process.c b/src/process.c
index 3ddad5f..08598ee 100644
--- a/src/process.c
+++ b/src/process.c
@@ -84,7 +84,7 @@ void fork_process_dump(int fd)
int size = 0;
list_for_each_entry(this, &process_list, head) {
- size += snprintf(buf+size, sizeof(buf),
+ size += snprintf(buf + size, sizeof(buf) - size,
"PID=%u type=%s\n",
this->pid,
this->type < CTD_PROC_MAX ?
diff --git a/src/queue.c b/src/queue.c
index 76425b1..e94dc7c 100644
--- a/src/queue.c
+++ b/src/queue.c
@@ -69,12 +69,12 @@ void queue_stats_show(int fd)
int size = 0;
char buf[512];
- size += snprintf(buf+size, sizeof(buf),
+ size += snprintf(buf + size, sizeof(buf) - size,
"allocated queue nodes:\t\t%12u\n\n",
qobjects_num);
list_for_each_entry(this, &queue_list, list) {
- size += snprintf(buf+size, sizeof(buf),
+ size += snprintf(buf + size, sizeof(buf) - size,
"queue %s:\n"
"current elements:\t\t%12u\n"
"maximum elements:\t\t%12u\n"
diff --git a/src/read_config_lex.l b/src/read_config_lex.l
index b0d9e61..2708432 100644
--- a/src/read_config_lex.l
+++ b/src/read_config_lex.l
@@ -21,6 +21,7 @@
#include <string.h>
+#include "log.h"
#include "conntrackd.h"
#include "read_config_yy.h"
%}
@@ -35,6 +36,10 @@ nl [\n\r]
is_on [o|O][n|N]
is_off [o|O][f|F][f|F]
+is_yes [y|Y][e|E][s|S]
+is_no [n|N][o|O]
+is_true {is_on}|{is_yes}
+is_false {is_off}|{is_no}
integer [0-9]+
signed_integer [\-\+][0-9]+
path \/[^\"\n ]*
@@ -137,9 +142,10 @@ notrack [N|n][O|o][T|t][R|r][A|a][C|c][K|k]
"ExpectTimeout" { return T_HELPER_EXPECT_TIMEOUT; }
"Systemd" { return T_SYSTEMD; }
"StartupResync" { return T_STARTUP_RESYNC; }
+"Setup" { return T_SETUP; }
-{is_on} { return T_ON; }
-{is_off} { return T_OFF; }
+{is_true} { return T_ON; }
+{is_false} { return T_OFF; }
{integer} { yylval.val = atoi(yytext); return T_NUMBER; }
{signed_integer} { yylval.val = atoi(yytext); return T_SIGNED_NUMBER; }
{ip4} { yylval.string = strdup(yytext); return T_IP; }
@@ -169,7 +175,7 @@ notrack [N|n][O|o][T|t][R|r][A|a][C|c][K|k]
%%
int
-yywrap()
+yywrap(void)
{
return 1;
}
diff --git a/src/read_config_yy.y b/src/read_config_yy.y
index 31109c4..a116b0b 100644
--- a/src/read_config_yy.y
+++ b/src/read_config_yy.y
@@ -31,19 +31,40 @@
#include "cidr.h"
#include "helper.h"
#include "stack.h"
+#include "log.h"
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
#include <sched.h>
#include <dlfcn.h>
+
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack_tcp.h>
extern char *yytext;
extern int yylineno;
+int yylex (void);
+int yyerror (const char *msg);
+void yyrestart (FILE *input_file);
+
struct ct_conf conf;
static void __kernel_filter_start(void);
static void __kernel_filter_add_state(int value);
-static void __max_dedicated_links_reached(void);
+
+static struct channel_conf *conf_get_channel(void)
+{
+ if (conf.channel_num >= MULTICHANNEL_MAX) {
+ dlog(LOG_ERR, "too many dedicated links in the configuration "
+ "file (Maximum: %d)", MULTICHANNEL_MAX);
+ exit(EXIT_FAILURE);
+ }
+
+ return &conf.channel[conf.channel_num];
+}
struct stack symbol_stack;
@@ -63,7 +84,7 @@ enum {
%token T_IPV4_ADDR T_IPV4_IFACE T_PORT T_HASHSIZE T_HASHLIMIT T_MULTICAST
%token T_PATH T_UNIX T_REFRESH T_IPV6_ADDR T_IPV6_IFACE
-%token T_BACKLOG T_GROUP T_IGNORE
+%token T_BACKLOG T_GROUP T_IGNORE T_SETUP
%token T_LOG T_UDP T_ICMP T_IGMP T_VRRP T_TCP
%token T_LOCK T_BUFFER_SIZE_MAX_GROWN T_EXPIRE T_TIMEOUT
%token T_GENERAL T_SYNC T_STATS T_BUFFER_SIZE
@@ -121,7 +142,7 @@ logfile_path : T_LOG T_PATH_VAL
FILENAME_MAXLEN);
exit(EXIT_FAILURE);
}
- snprintf(conf.logfile, FILENAME_MAXLEN, "%s", $2);
+ snprintf(conf.logfile, sizeof(conf.logfile), "%s", $2);
free($2);
};
@@ -176,7 +197,7 @@ lock : T_LOCK T_PATH_VAL
FILENAME_MAXLEN);
exit(EXIT_FAILURE);
}
- snprintf(conf.lockfile, FILENAME_MAXLEN, "%s", $2);
+ snprintf(conf.lockfile, sizeof(conf.lockfile), "%s", $2);
free($2);
};
@@ -202,6 +223,8 @@ purge: T_PURGE T_NUMBER
multicast_line : T_MULTICAST '{' multicast_options '}'
{
+ struct channel_conf *channel_conf = conf_get_channel();
+
if (conf.channel_type_global != CHANNEL_NONE &&
conf.channel_type_global != CHANNEL_MCAST) {
dlog(LOG_ERR, "cannot use `Multicast' with other "
@@ -209,13 +232,15 @@ multicast_line : T_MULTICAST '{' multicast_options '}'
exit(EXIT_FAILURE);
}
conf.channel_type_global = CHANNEL_MCAST;
- conf.channel[conf.channel_num].channel_type = CHANNEL_MCAST;
- conf.channel[conf.channel_num].channel_flags = CHANNEL_F_BUFFERED;
+ channel_conf->channel_type = CHANNEL_MCAST;
+ channel_conf->channel_flags = CHANNEL_F_BUFFERED;
conf.channel_num++;
};
multicast_line : T_MULTICAST T_DEFAULT '{' multicast_options '}'
{
+ struct channel_conf *channel_conf = conf_get_channel();
+
if (conf.channel_type_global != CHANNEL_NONE &&
conf.channel_type_global != CHANNEL_MCAST) {
dlog(LOG_ERR, "cannot use `Multicast' with other "
@@ -223,9 +248,8 @@ multicast_line : T_MULTICAST T_DEFAULT '{' multicast_options '}'
exit(EXIT_FAILURE);
}
conf.channel_type_global = CHANNEL_MCAST;
- conf.channel[conf.channel_num].channel_type = CHANNEL_MCAST;
- conf.channel[conf.channel_num].channel_flags = CHANNEL_F_DEFAULT |
- CHANNEL_F_BUFFERED;
+ channel_conf->channel_type = CHANNEL_MCAST;
+ channel_conf->channel_flags = CHANNEL_F_DEFAULT | CHANNEL_F_BUFFERED;
conf.channel_default = conf.channel_num;
conf.channel_num++;
};
@@ -235,15 +259,15 @@ multicast_options :
multicast_option : T_IPV4_ADDR T_IP
{
- __max_dedicated_links_reached();
+ struct channel_conf *channel_conf = conf_get_channel();
- if (!inet_aton($2, &conf.channel[conf.channel_num].u.mcast.in)) {
+ if (!inet_aton($2, &channel_conf->u.mcast.in.inet_addr)) {
dlog(LOG_WARNING, "%s is not a valid IPv4 address", $2);
free($2);
break;
}
- if (conf.channel[conf.channel_num].u.mcast.ipproto == AF_INET6) {
+ if (channel_conf->u.mcast.ipproto == AF_INET6) {
dlog(LOG_WARNING, "your multicast address is IPv4 but "
"is binded to an IPv6 interface? "
"Surely, this is not what you want");
@@ -251,16 +275,15 @@ multicast_option : T_IPV4_ADDR T_IP
}
free($2);
- conf.channel[conf.channel_num].u.mcast.ipproto = AF_INET;
+ channel_conf->u.mcast.ipproto = AF_INET;
};
multicast_option : T_IPV6_ADDR T_IP
{
- __max_dedicated_links_reached();
+ struct channel_conf *channel_conf = conf_get_channel();
int err;
- err = inet_pton(AF_INET6, $2,
- &conf.channel[conf.channel_num].u.mcast.in);
+ err = inet_pton(AF_INET6, $2, &channel_conf->u.mcast.in);
if (err == 0) {
dlog(LOG_WARNING, "%s is not a valid IPv6 address", $2);
free($2);
@@ -270,7 +293,7 @@ multicast_option : T_IPV6_ADDR T_IP
exit(EXIT_FAILURE);
}
- if (conf.channel[conf.channel_num].u.mcast.ipproto == AF_INET) {
+ if (channel_conf->u.mcast.ipproto == AF_INET) {
dlog(LOG_WARNING, "your multicast address is IPv6 but "
"is binded to an IPv4 interface? "
"Surely this is not what you want");
@@ -278,10 +301,10 @@ multicast_option : T_IPV6_ADDR T_IP
break;
}
- conf.channel[conf.channel_num].u.mcast.ipproto = AF_INET6;
+ channel_conf->u.mcast.ipproto = AF_INET6;
- if (conf.channel[conf.channel_num].channel_ifname[0] &&
- !conf.channel[conf.channel_num].u.mcast.ifa.interface_index6) {
+ if (channel_conf->channel_ifname[0] &&
+ !channel_conf->u.mcast.ifa.interface_index6) {
unsigned int idx;
idx = if_nametoindex($2);
@@ -291,31 +314,31 @@ multicast_option : T_IPV6_ADDR T_IP
break;
}
- conf.channel[conf.channel_num].u.mcast.ifa.interface_index6 = idx;
- conf.channel[conf.channel_num].u.mcast.ipproto = AF_INET6;
+ channel_conf->u.mcast.ifa.interface_index6 = idx;
+ channel_conf->u.mcast.ipproto = AF_INET6;
}
free($2);
};
multicast_option : T_IPV4_IFACE T_IP
{
- __max_dedicated_links_reached();
+ struct channel_conf *channel_conf = conf_get_channel();
- if (!inet_aton($2, &conf.channel[conf.channel_num].u.mcast.ifa)) {
+ if (!inet_aton($2, &channel_conf->u.mcast.ifa.interface_addr)) {
dlog(LOG_WARNING, "%s is not a valid IPv4 address", $2);
free($2);
break;
}
free($2);
- if (conf.channel[conf.channel_num].u.mcast.ipproto == AF_INET6) {
+ if (channel_conf->u.mcast.ipproto == AF_INET6) {
dlog(LOG_WARNING, "your multicast interface is IPv4 but "
"is binded to an IPv6 interface? "
"Surely, this is not what you want");
break;
}
- conf.channel[conf.channel_num].u.mcast.ipproto = AF_INET;
+ channel_conf->u.mcast.ipproto = AF_INET;
};
multicast_option : T_IPV6_IFACE T_IP
@@ -326,11 +349,10 @@ multicast_option : T_IPV6_IFACE T_IP
multicast_option : T_IFACE T_STRING
{
+ struct channel_conf *channel_conf = conf_get_channel();
unsigned int idx;
- __max_dedicated_links_reached();
-
- strncpy(conf.channel[conf.channel_num].channel_ifname, $2, IFNAMSIZ);
+ strncpy(channel_conf->channel_ifname, $2, IFNAMSIZ);
idx = if_nametoindex($2);
if (!idx) {
@@ -339,9 +361,9 @@ multicast_option : T_IFACE T_STRING
break;
}
- if (conf.channel[conf.channel_num].u.mcast.ipproto == AF_INET6) {
- conf.channel[conf.channel_num].u.mcast.ifa.interface_index6 = idx;
- conf.channel[conf.channel_num].u.mcast.ipproto = AF_INET6;
+ if (channel_conf->u.mcast.ipproto == AF_INET6) {
+ channel_conf->u.mcast.ifa.interface_index6 = idx;
+ channel_conf->u.mcast.ipproto = AF_INET6;
}
free($2);
@@ -349,36 +371,43 @@ multicast_option : T_IFACE T_STRING
multicast_option : T_GROUP T_NUMBER
{
- __max_dedicated_links_reached();
- conf.channel[conf.channel_num].u.mcast.port = $2;
+ struct channel_conf *channel_conf = conf_get_channel();
+
+ channel_conf->u.mcast.port = $2;
};
multicast_option: T_SNDBUFF T_NUMBER
{
- __max_dedicated_links_reached();
- conf.channel[conf.channel_num].u.mcast.sndbuf = $2;
+ struct channel_conf *channel_conf = conf_get_channel();
+
+ channel_conf->u.mcast.sndbuf = $2;
};
multicast_option: T_RCVBUFF T_NUMBER
{
- __max_dedicated_links_reached();
- conf.channel[conf.channel_num].u.mcast.rcvbuf = $2;
+ struct channel_conf *channel_conf = conf_get_channel();
+
+ channel_conf->u.mcast.rcvbuf = $2;
};
-multicast_option: T_CHECKSUM T_ON
+multicast_option: T_CHECKSUM T_ON
{
- __max_dedicated_links_reached();
- conf.channel[conf.channel_num].u.mcast.checksum = 0;
+ struct channel_conf *channel_conf = conf_get_channel();
+
+ channel_conf->u.mcast.checksum = 0;
};
multicast_option: T_CHECKSUM T_OFF
{
- __max_dedicated_links_reached();
- conf.channel[conf.channel_num].u.mcast.checksum = 1;
+ struct channel_conf *channel_conf = conf_get_channel();
+
+ channel_conf->u.mcast.checksum = 1;
};
udp_line : T_UDP '{' udp_options '}'
{
+ struct channel_conf *channel_conf = conf_get_channel();
+
if (conf.channel_type_global != CHANNEL_NONE &&
conf.channel_type_global != CHANNEL_UDP) {
dlog(LOG_ERR, "cannot use `UDP' with other "
@@ -386,13 +415,15 @@ udp_line : T_UDP '{' udp_options '}'
exit(EXIT_FAILURE);
}
conf.channel_type_global = CHANNEL_UDP;
- conf.channel[conf.channel_num].channel_type = CHANNEL_UDP;
- conf.channel[conf.channel_num].channel_flags = CHANNEL_F_BUFFERED;
+ channel_conf->channel_type = CHANNEL_UDP;
+ channel_conf->channel_flags = CHANNEL_F_BUFFERED;
conf.channel_num++;
};
udp_line : T_UDP T_DEFAULT '{' udp_options '}'
{
+ struct channel_conf *channel_conf = conf_get_channel();
+
if (conf.channel_type_global != CHANNEL_NONE &&
conf.channel_type_global != CHANNEL_UDP) {
dlog(LOG_ERR, "cannot use `UDP' with other "
@@ -400,9 +431,8 @@ udp_line : T_UDP T_DEFAULT '{' udp_options '}'
exit(EXIT_FAILURE);
}
conf.channel_type_global = CHANNEL_UDP;
- conf.channel[conf.channel_num].channel_type = CHANNEL_UDP;
- conf.channel[conf.channel_num].channel_flags = CHANNEL_F_DEFAULT |
- CHANNEL_F_BUFFERED;
+ channel_conf->channel_type = CHANNEL_UDP;
+ channel_conf->channel_flags = CHANNEL_F_DEFAULT | CHANNEL_F_BUFFERED;
conf.channel_default = conf.channel_num;
conf.channel_num++;
};
@@ -412,24 +442,23 @@ udp_options :
udp_option : T_IPV4_ADDR T_IP
{
- __max_dedicated_links_reached();
+ struct channel_conf *channel_conf = conf_get_channel();
- if (!inet_aton($2, &conf.channel[conf.channel_num].u.udp.server.ipv4)) {
+ if (!inet_aton($2, &channel_conf->u.udp.server.ipv4.inet_addr)) {
dlog(LOG_WARNING, "%s is not a valid IPv4 address", $2);
free($2);
break;
}
free($2);
- conf.channel[conf.channel_num].u.udp.ipproto = AF_INET;
+ channel_conf->u.udp.ipproto = AF_INET;
};
udp_option : T_IPV6_ADDR T_IP
{
- __max_dedicated_links_reached();
+ struct channel_conf *channel_conf = conf_get_channel();
int err;
- err = inet_pton(AF_INET6, $2,
- &conf.channel[conf.channel_num].u.udp.server.ipv6);
+ err = inet_pton(AF_INET6, $2, &channel_conf->u.udp.server.ipv6);
if (err == 0) {
dlog(LOG_WARNING, "%s is not a valid IPv6 address", $2);
free($2);
@@ -440,29 +469,28 @@ udp_option : T_IPV6_ADDR T_IP
}
free($2);
- conf.channel[conf.channel_num].u.udp.ipproto = AF_INET6;
+ channel_conf->u.udp.ipproto = AF_INET6;
};
udp_option : T_IPV4_DEST_ADDR T_IP
{
- __max_dedicated_links_reached();
+ struct channel_conf *channel_conf = conf_get_channel();
- if (!inet_aton($2, &conf.channel[conf.channel_num].u.udp.client)) {
+ if (!inet_aton($2, &channel_conf->u.udp.client.inet_addr)) {
dlog(LOG_WARNING, "%s is not a valid IPv4 address", $2);
free($2);
break;
}
free($2);
- conf.channel[conf.channel_num].u.udp.ipproto = AF_INET;
+ channel_conf->u.udp.ipproto = AF_INET;
};
udp_option : T_IPV6_DEST_ADDR T_IP
{
- __max_dedicated_links_reached();
+ struct channel_conf *channel_conf = conf_get_channel();
int err;
- err = inet_pton(AF_INET6, $2,
- &conf.channel[conf.channel_num].u.udp.client);
+ err = inet_pton(AF_INET6, $2, &channel_conf->u.udp.client);
if (err == 0) {
dlog(LOG_WARNING, "%s is not a valid IPv6 address", $2);
free($2);
@@ -473,15 +501,15 @@ udp_option : T_IPV6_DEST_ADDR T_IP
}
free($2);
- conf.channel[conf.channel_num].u.udp.ipproto = AF_INET6;
+ channel_conf->u.udp.ipproto = AF_INET6;
};
udp_option : T_IFACE T_STRING
{
+ struct channel_conf *channel_conf = conf_get_channel();
int idx;
- __max_dedicated_links_reached();
- strncpy(conf.channel[conf.channel_num].channel_ifname, $2, IFNAMSIZ);
+ strncpy(channel_conf->channel_ifname, $2, IFNAMSIZ);
idx = if_nametoindex($2);
if (!idx) {
@@ -489,43 +517,50 @@ udp_option : T_IFACE T_STRING
free($2);
break;
}
- conf.channel[conf.channel_num].u.udp.server.ipv6.scope_id = idx;
+ channel_conf->u.udp.server.ipv6.scope_id = idx;
free($2);
};
udp_option : T_PORT T_NUMBER
{
- __max_dedicated_links_reached();
- conf.channel[conf.channel_num].u.udp.port = $2;
+ struct channel_conf *channel_conf = conf_get_channel();
+
+ channel_conf->u.udp.port = $2;
};
udp_option: T_SNDBUFF T_NUMBER
{
- __max_dedicated_links_reached();
- conf.channel[conf.channel_num].u.udp.sndbuf = $2;
+ struct channel_conf *channel_conf = conf_get_channel();
+
+ channel_conf->u.udp.sndbuf = $2;
};
udp_option: T_RCVBUFF T_NUMBER
{
- __max_dedicated_links_reached();
- conf.channel[conf.channel_num].u.udp.rcvbuf = $2;
+ struct channel_conf *channel_conf = conf_get_channel();
+
+ channel_conf->u.udp.rcvbuf = $2;
};
-udp_option: T_CHECKSUM T_ON
+udp_option: T_CHECKSUM T_ON
{
- __max_dedicated_links_reached();
- conf.channel[conf.channel_num].u.udp.checksum = 0;
+ struct channel_conf *channel_conf = conf_get_channel();
+
+ channel_conf->u.udp.checksum = 0;
};
udp_option: T_CHECKSUM T_OFF
{
- __max_dedicated_links_reached();
- conf.channel[conf.channel_num].u.udp.checksum = 1;
+ struct channel_conf *channel_conf = conf_get_channel();
+
+ channel_conf->u.udp.checksum = 1;
};
tcp_line : T_TCP '{' tcp_options '}'
{
+ struct channel_conf *channel_conf = conf_get_channel();
+
if (conf.channel_type_global != CHANNEL_NONE &&
conf.channel_type_global != CHANNEL_TCP) {
dlog(LOG_ERR, "cannot use `TCP' with other "
@@ -533,15 +568,17 @@ tcp_line : T_TCP '{' tcp_options '}'
exit(EXIT_FAILURE);
}
conf.channel_type_global = CHANNEL_TCP;
- conf.channel[conf.channel_num].channel_type = CHANNEL_TCP;
- conf.channel[conf.channel_num].channel_flags = CHANNEL_F_BUFFERED |
- CHANNEL_F_STREAM |
- CHANNEL_F_ERRORS;
+ channel_conf->channel_type = CHANNEL_TCP;
+ channel_conf->channel_flags = CHANNEL_F_BUFFERED |
+ CHANNEL_F_STREAM |
+ CHANNEL_F_ERRORS;
conf.channel_num++;
};
tcp_line : T_TCP T_DEFAULT '{' tcp_options '}'
{
+ struct channel_conf *channel_conf = conf_get_channel();
+
if (conf.channel_type_global != CHANNEL_NONE &&
conf.channel_type_global != CHANNEL_TCP) {
dlog(LOG_ERR, "cannot use `TCP' with other "
@@ -549,11 +586,11 @@ tcp_line : T_TCP T_DEFAULT '{' tcp_options '}'
exit(EXIT_FAILURE);
}
conf.channel_type_global = CHANNEL_TCP;
- conf.channel[conf.channel_num].channel_type = CHANNEL_TCP;
- conf.channel[conf.channel_num].channel_flags = CHANNEL_F_DEFAULT |
- CHANNEL_F_BUFFERED |
- CHANNEL_F_STREAM |
- CHANNEL_F_ERRORS;
+ channel_conf->channel_type = CHANNEL_TCP;
+ channel_conf->channel_flags = CHANNEL_F_DEFAULT |
+ CHANNEL_F_BUFFERED |
+ CHANNEL_F_STREAM |
+ CHANNEL_F_ERRORS;
conf.channel_default = conf.channel_num;
conf.channel_num++;
};
@@ -563,24 +600,23 @@ tcp_options :
tcp_option : T_IPV4_ADDR T_IP
{
- __max_dedicated_links_reached();
+ struct channel_conf *channel_conf = conf_get_channel();
- if (!inet_aton($2, &conf.channel[conf.channel_num].u.tcp.server.ipv4)) {
+ if (!inet_aton($2, &channel_conf->u.tcp.server.ipv4.inet_addr)) {
dlog(LOG_WARNING, "%s is not a valid IPv4 address", $2);
free($2);
break;
}
free($2);
- conf.channel[conf.channel_num].u.tcp.ipproto = AF_INET;
+ channel_conf->u.tcp.ipproto = AF_INET;
};
tcp_option : T_IPV6_ADDR T_IP
{
- __max_dedicated_links_reached();
+ struct channel_conf *channel_conf = conf_get_channel();
int err;
- err = inet_pton(AF_INET6, $2,
- &conf.channel[conf.channel_num].u.tcp.server.ipv6);
+ err = inet_pton(AF_INET6, $2, &channel_conf->u.tcp.server.ipv6);
if (err == 0) {
dlog(LOG_WARNING, "%s is not a valid IPv6 address", $2);
free($2);
@@ -591,29 +627,28 @@ tcp_option : T_IPV6_ADDR T_IP
}
free($2);
- conf.channel[conf.channel_num].u.tcp.ipproto = AF_INET6;
+ channel_conf->u.tcp.ipproto = AF_INET6;
};
tcp_option : T_IPV4_DEST_ADDR T_IP
{
- __max_dedicated_links_reached();
+ struct channel_conf *channel_conf = conf_get_channel();
- if (!inet_aton($2, &conf.channel[conf.channel_num].u.tcp.client)) {
+ if (!inet_aton($2, &channel_conf->u.tcp.client.inet_addr)) {
dlog(LOG_WARNING, "%s is not a valid IPv4 address", $2);
free($2);
break;
}
free($2);
- conf.channel[conf.channel_num].u.tcp.ipproto = AF_INET;
+ channel_conf->u.tcp.ipproto = AF_INET;
};
tcp_option : T_IPV6_DEST_ADDR T_IP
{
- __max_dedicated_links_reached();
+ struct channel_conf *channel_conf = conf_get_channel();
int err;
- err = inet_pton(AF_INET6, $2,
- &conf.channel[conf.channel_num].u.tcp.client);
+ err = inet_pton(AF_INET6, $2, &channel_conf->u.tcp.client);
if (err == 0) {
dlog(LOG_WARNING, "%s is not a valid IPv6 address", $2);
free($2);
@@ -624,15 +659,15 @@ tcp_option : T_IPV6_DEST_ADDR T_IP
}
free($2);
- conf.channel[conf.channel_num].u.tcp.ipproto = AF_INET6;
+ channel_conf->u.tcp.ipproto = AF_INET6;
};
tcp_option : T_IFACE T_STRING
{
+ struct channel_conf *channel_conf = conf_get_channel();
int idx;
- __max_dedicated_links_reached();
- strncpy(conf.channel[conf.channel_num].channel_ifname, $2, IFNAMSIZ);
+ strncpy(channel_conf->channel_ifname, $2, IFNAMSIZ);
idx = if_nametoindex($2);
if (!idx) {
@@ -640,44 +675,48 @@ tcp_option : T_IFACE T_STRING
free($2);
break;
}
- conf.channel[conf.channel_num].u.tcp.server.ipv6.scope_id = idx;
+ channel_conf->u.tcp.server.ipv6.scope_id = idx;
free($2);
};
tcp_option : T_PORT T_NUMBER
{
- __max_dedicated_links_reached();
- conf.channel[conf.channel_num].u.tcp.port = $2;
+ struct channel_conf *channel_conf = conf_get_channel();
+
+ channel_conf->u.tcp.port = $2;
};
tcp_option: T_SNDBUFF T_NUMBER
{
- __max_dedicated_links_reached();
- conf.channel[conf.channel_num].u.tcp.sndbuf = $2;
+ struct channel_conf *channel_conf = conf_get_channel();
+
+ channel_conf->u.tcp.sndbuf = $2;
};
tcp_option: T_RCVBUFF T_NUMBER
{
- __max_dedicated_links_reached();
- conf.channel[conf.channel_num].u.tcp.rcvbuf = $2;
+ struct channel_conf *channel_conf = conf_get_channel();
+
+ channel_conf->u.tcp.rcvbuf = $2;
};
-tcp_option: T_CHECKSUM T_ON
+tcp_option: T_CHECKSUM T_ON
{
- __max_dedicated_links_reached();
- conf.channel[conf.channel_num].u.tcp.checksum = 0;
+ struct channel_conf *channel_conf = conf_get_channel();
+
+ channel_conf->u.tcp.checksum = 0;
};
tcp_option: T_CHECKSUM T_OFF
{
- __max_dedicated_links_reached();
- conf.channel[conf.channel_num].u.tcp.checksum = 1;
+ struct channel_conf *channel_conf = conf_get_channel();
+
+ channel_conf->u.tcp.checksum = 1;
};
tcp_option: T_ERROR_QUEUE_LENGTH T_NUMBER
{
- __max_dedicated_links_reached();
CONFIG(channelc).error_queue_length = $2;
};
@@ -699,12 +738,12 @@ unix_options:
unix_option : T_PATH T_PATH_VAL
{
- if (strlen($2) > UNIX_PATH_MAX) {
+ if (strlen($2) >= UNIX_PATH_MAX) {
dlog(LOG_ERR, "Path is longer than %u characters",
- UNIX_PATH_MAX);
+ UNIX_PATH_MAX - 1);
exit(EXIT_FAILURE);
}
- snprintf(conf.local.path, UNIX_PATH_MAX, "%s", $2);
+ strcpy(conf.local.path, $2);
free($2);
};
@@ -1052,7 +1091,7 @@ scheduler_line : T_PRIO T_NUMBER
{
conf.sched.prio = $2;
if (conf.sched.prio < 0 || conf.sched.prio > 99) {
- dlog(LOG_ERR, "`Priority' must be [0, 99]\n", $2);
+ dlog(LOG_ERR, "`Priority' must be [0, 99]\n");
exit(EXIT_FAILURE);
}
};
@@ -1228,7 +1267,7 @@ filter_address_item : T_IPV4_ADDR T_IP
}
}
- if (!inet_aton($2, &ip.ipv4)) {
+ if (!inet_aton($2, (struct in_addr *) &ip.ipv4)) {
dlog(LOG_WARNING, "%s is not a valid IPv4, ignoring", $2);
free($2);
break;
@@ -1396,7 +1435,7 @@ stat_logfile_path : T_LOG T_PATH_VAL
FILENAME_MAXLEN);
exit(EXIT_FAILURE);
}
- snprintf(conf.stats.logfile, FILENAME_MAXLEN, "%s", $2);
+ snprintf(conf.stats.logfile, sizeof(conf.stats.logfile), "%s", $2);
free($2);
};
@@ -1454,6 +1493,7 @@ helper_list:
;
helper_line: helper_type
+ | helper_setup
;
helper_type: T_TYPE T_STRING T_STRING T_STRING '{' helper_type_list '}'
@@ -1562,6 +1602,16 @@ helper_type: T_TYPE T_STRING T_STRING T_STRING '{' helper_type_list '}'
list_add(&helper_inst->head, &CONFIG(cthelper).list);
};
+helper_setup : T_SETUP T_ON
+{
+ CONFIG(cthelper).setup = true;
+};
+
+helper_setup : T_SETUP T_OFF
+{
+ CONFIG(cthelper).setup = false;
+};
+
helper_type_list:
| helper_type_list helper_type_line
;
@@ -1611,7 +1661,7 @@ helper_type: T_HELPER_POLICY T_STRING '{' helper_policy_list '}'
}
policy = (struct ctd_helper_policy *) &e->data;
- snprintf(policy->name, CTD_HELPER_NAME_LEN, "%s", $2);
+ snprintf(policy->name, sizeof(policy->name), "%s", $2);
free($2);
/* Now object is complete. */
e->type = SYMBOL_HELPER_POLICY_EXPECT_ROOT;
@@ -1659,7 +1709,7 @@ helper_policy_expect_timeout: T_HELPER_EXPECT_TIMEOUT T_NUMBER
%%
int __attribute__((noreturn))
-yyerror(char *msg)
+yyerror(const char *msg)
{
dlog(LOG_ERR, "parsing config file in line (%d), symbol '%s': %s",
yylineno, yytext, msg);
@@ -1690,15 +1740,6 @@ static void __kernel_filter_add_state(int value)
&filter_proto);
}
-static void __max_dedicated_links_reached(void)
-{
- if (conf.channel_num >= MULTICHANNEL_MAX) {
- dlog(LOG_ERR, "too many dedicated links in the configuration "
- "file (Maximum: %d)", MULTICHANNEL_MAX);
- exit(EXIT_FAILURE);
- }
-}
-
int
init_config(char *filename)
{
@@ -1780,5 +1821,11 @@ init_config(char *filename)
NF_NETLINK_CONNTRACK_DESTROY;
}
+ /* default hashtable buckets and maximum number of entries */
+ if (!CONFIG(hashsize))
+ CONFIG(hashsize) = 65536;
+ if (!CONFIG(limit))
+ CONFIG(limit) = 262144;
+
return 0;
}
diff --git a/src/run.c b/src/run.c
index f11a532..37a0eb1 100644
--- a/src/run.c
+++ b/src/run.c
@@ -67,7 +67,7 @@ void killer(int signo)
close_log();
sd_ct_stop();
- exit(0);
+ _exit(0);
}
static void child(int foo)
diff --git a/src/vector.c b/src/vector.c
index c81e7ce..92a5436 100644
--- a/src/vector.c
+++ b/src/vector.c
@@ -60,13 +60,16 @@ void vector_destroy(struct vector *v)
int vector_add(struct vector *v, void *data)
{
+ void *ptr;
+
if (v->cur_elems >= v->max_elems) {
v->max_elems += DEFAULT_VECTOR_GROWTH;
- v->data = realloc(v->data, v->max_elems * v->size);
- if (v->data == NULL) {
+ ptr = realloc(v->data, v->max_elems * v->size);
+ if (!ptr) {
v->max_elems -= DEFAULT_VECTOR_GROWTH;
return -1;
}
+ v->data = ptr;
}
memcpy(v->data + (v->size * v->cur_elems), data, v->size);
v->cur_elems++;
diff --git a/tests/conntrack/bulk-load-stress.sh b/tests/conntrack/bulk-load-stress.sh
new file mode 100755
index 0000000..9250528
--- /dev/null
+++ b/tests/conntrack/bulk-load-stress.sh
@@ -0,0 +1,163 @@
+#!/bin/bash
+
+DEFAULT_CT="../../src/conntrack"
+DEFAULT_SPORT_COUNT=0xffff
+DEFAULT_DPORT_COUNT=0x2
+DEFAULT_TMP_FILE="./ct_data.txt"
+DEFAULT_CT_ZONE=123
+DEFAULT_GEN_ONLY=0
+DEFAULT_CLEANUP_INDIVIDUAL=0
+
+CT=$DEFAULT_CT
+SPORT_COUNT=$DEFAULT_SPORT_COUNT
+DPORT_COUNT=$DEFAULT_DPORT_COUNT
+TMP_FILE=$DEFAULT_TMP_FILE
+CT_ZONE=$DEFAULT_CT_ZONE
+GEN_ONLY=$DEFAULT_GEN_ONLY
+CLEANUP_INDIVIDUAL=$DEFAULT_CLEANUP_INDIVIDUAL
+
+print_help()
+{
+ me=$(basename "$0")
+
+ echo "Script for stress-testing bulk ct entries load (-R option)"
+ echo ""
+ echo "Usage: $me [options]"
+ echo ""
+ echo "Where options can be:"
+ echo ""
+ echo "-dpc <dst_port_count> - number of destination port values."
+ echo " Default is ${DEFAULT_DPORT_COUNT}."
+ echo ""
+ echo "-spc <src_port_count> - number of source port values."
+ echo " Default is ${DEFAULT_SPORT_COUNT}."
+ echo ""
+ echo "-ct <ct_tool_path> - path to the conntrack tool."
+ echo " Default is ${DEFAULT_CT}."
+ echo ""
+ echo "-z <ct_zone> - ct zone to be used."
+ echo " Default is ${DEFAULT_CT_ZONE}."
+ echo ""
+ echo "-f <tmp_file_name> - tmp file to be used to generate the ct data to."
+ echo " Default is ${DEFAULT_TMP_FILE}."
+ echo ""
+ echo "-g - Generate tmp file and exit."
+ echo ""
+ echo "-h - Print this help and exit."
+}
+
+function ct_data_gen()
+{
+ for (( d = 1; d <= $DPORT_COUNT; d++ )) do
+ for (( s = 1; s <= $SPORT_COUNT; s++ )) do
+ echo "-I -w $CT_ZONE -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport ${s} --dport ${d} --state LISTEN -u SEEN_REPLY -t 50"
+ done
+ done
+}
+
+if [ $UID -ne 0 ]
+then
+ echo "Run this test as root"
+ exit 1
+fi
+
+while [ $# -gt 0 ]
+do
+ case "$1" in
+ -spc)
+ SPORT_COUNT=${2:-}
+ if [ -z "$SPORT_COUNT" ]
+ then
+ echo "Source port must be specified!"
+ print_help
+ exit 1
+ fi
+ shift
+ ;;
+ -dpc)
+ DPORT_COUNT=${2:-}
+ if [ -z "$DPORT_COUNT" ]
+ then
+ echo "Destination port must be specified!"
+ print_help
+ exit 1
+ fi
+ shift
+ ;;
+ -ct)
+ CT=${2:-}
+ if [ -z "$CT" ]
+ then
+ echo "conntrack path must be specified!"
+ print_help
+ exit 1
+ fi
+ shift
+ ;;
+ -z)
+ CT_ZONE=${2:-}
+ if [ -z "$CT_ZONE" ]
+ then
+ echo "ct zone must be specified!"
+ print_help
+ exit 1
+ fi
+ shift
+ ;;
+ -f)
+ TMP_FILE=${2:-}
+ if [ -z "$TMP_FILE" ]
+ then
+ echo "Tmp file must be specified!"
+ print_help
+ exit 1
+ fi
+ shift
+ ;;
+ -g)
+ GEN_ONLY=1
+ ;;
+ -ci)
+ CLEANUP_INDIVIDUAL=1
+ ;;
+ -h)
+ print_help
+ exit 1
+ ;;
+ *)
+ echo "Unknown paramerer \"$1\""
+ print_help
+ exit 1
+ ;;
+ esac
+ shift
+done
+
+ct_data_gen > $TMP_FILE
+
+NUM_ENTRIES=$(cat ${TMP_FILE} | wc -l)
+
+echo "File ${TMP_FILE} is generated, number of entries: ${NUM_ENTRIES}."
+
+if [ "$GEN_ONLY" -eq "1" ]; then
+ exit 0
+fi
+
+echo "Loading ${NUM_ENTRIES} entries from ${TMP_FILE} .."
+time -p ${CT} -R $TMP_FILE
+
+if [ "$CLEANUP_INDIVIDUAL" -eq "1" ]; then
+ sed -i -e "s/-I/-D/g" -e "s/-t 50//g" $TMP_FILE
+
+ NUM_ENTRIES=$(cat ${TMP_FILE} | wc -l)
+
+ echo "File ${TMP_FILE} is updated, number of entries: ${NUM_ENTRIES}."
+
+ echo "Cleaning ${NUM_ENTRIES} entries from ${TMP_FILE} .."
+ time -p ${CT} -R $TMP_FILE
+fi
+
+
+echo "Cleaning up zone ${CT_ZONE}.."
+time -p ${CT} -D -w $CT_ZONE > /dev/null
+rm $TMP_FILE
diff --git a/tests/conntrack/load-stress.sh b/tests/conntrack/load-stress.sh
new file mode 100644
index 0000000..597c4c6
--- /dev/null
+++ b/tests/conntrack/load-stress.sh
@@ -0,0 +1,62 @@
+#!/bin/bash
+
+SPORT_COUNT=128
+DPORT_COUNT=128
+
+function ct_data_gen()
+{
+ for (( d = 1; d <= $DPORT_COUNT; d++ )) do
+ for (( s = 1; s <= $SPORT_COUNT; s++ )) do
+ ip netns exec ct-ns-test conntrack -I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport ${s} --dport ${d} --state LISTEN -u SEEN_REPLY -t 300 &> /dev/null
+ if [ $? -ne 0 ]
+ then
+ echo "[FAILED] cannot insert conntrack entries"
+ exit 1
+ fi
+ done
+ done
+}
+
+ip netns add ct-ns-test
+
+if [ $UID -ne 0 ]
+then
+ echo "Run this test as root"
+ exit 1
+fi
+
+echo "Creating conntrack entries, please wait..."
+ct_data_gen
+ip netns exec ct-ns-test conntrack -U -p tcp -m 1
+if [ $? -ne 0 ]
+then
+ echo "[FAILED] cannot update conntrack entries"
+ exit 1
+fi
+
+COUNT=`ip netns exec ct-ns-test conntrack -L | wc -l`
+if [ $COUNT -ne 16384 ]
+then
+ echo "$COUNT entries, expecting 131072"
+ exit 1
+fi
+
+ip netns exec ct-ns-test conntrack -F
+if [ $? -ne 0 ]
+then
+ echo "[FAILED] faild to flush conntrack entries"
+ exit 1
+fi
+
+COUNT=`ip netns exec ct-ns-test conntrack -L | wc -l`
+if [ $COUNT -ne 0 ]
+then
+ echo "$COUNT entries, expecting 0"
+ exit 1
+fi
+
+ip netns del ct-ns-test
+
+echo "[OK] test successful"
+
+exit 0
diff --git a/tests/conntrack/test-conntrack.c b/tests/conntrack/test-conntrack.c
index 76ab051..372e025 100644
--- a/tests/conntrack/test-conntrack.c
+++ b/tests/conntrack/test-conntrack.c
@@ -28,6 +28,23 @@ int main()
struct dirent *dent;
char file[1024];
int i,n;
+ char cmd_buf[1024 * 8];
+ int i_cmd_buf = 0;
+ char cmd, cur_cmd = 0;
+ char *cmd_opt;
+
+#define cmd_strappend(_s) do { \
+ char * pos = stpncpy(cmd_buf + i_cmd_buf, _s, sizeof(cmd_buf) - i_cmd_buf); \
+ i_cmd_buf = pos - cmd_buf; \
+ if (i_cmd_buf == sizeof(cmd_buf)) { \
+ printf("buffer full!\n"); \
+ exit(EXIT_FAILURE); \
+ } \
+} while (0)
+
+#define cmd_reset() do { \
+ i_cmd_buf = 0; \
+} while (0)
n = scandir("testsuite", &dents, NULL, alphasort);
@@ -48,9 +65,7 @@ int main()
}
while (fgets(buf, sizeof(buf), fp)) {
- char tmp[1024] = CT_PROG, *res;
- tmp[strlen(CT_PROG)] = ' ';
-
+ char *res;
line++;
if (buf[0] == '#' || buf[0] == ' ')
@@ -63,27 +78,72 @@ int main()
exit(EXIT_FAILURE);
}
*res = '\0';
- res+=2;
+ res++;
+ for (; *res == ' ' || *res == '\t'; res++);
+ cmd = res[0];
+ cmd_opt = &res[1];
+ for (; *cmd_opt == ' ' || *cmd_opt == '\t'; cmd_opt++);
+ res = strchr(cmd_opt, '\n');
+ if (res)
+ *res = '\0';
+
+ if (cur_cmd && cmd != cur_cmd) {
+ /* complete current multi-line command */
+ switch (cur_cmd) {
+ case '\n':
+ cmd_strappend("\" | ");
+ break;
+ default:
+ printf("Internal Error: unexpected multiline command %c",
+ cur_cmd);
+ exit(EXIT_FAILURE);
+ break;
+ }
+
+ cur_cmd = 0;
+ }
+
+ switch (cmd) {
+ case '\n':
+ if (!cur_cmd) {
+ cmd_strappend("echo \"");
+ cur_cmd = cmd;
+ } else
+ cmd_strappend("\n");
+ cmd_strappend(buf);
+ continue;
+ default:
+ cmd_strappend(CT_PROG);
+ cmd_strappend(" ");
+ cmd_strappend(buf);
+ if (cmd == '|') {
+ cmd_strappend(" | ");
+ if (cmd_opt[0]) {
+ cmd_strappend("sed \"");
+ cmd_strappend(cmd_opt);
+ cmd_strappend("\" | ");
+ }
+ continue;
+ }
+ cmd_reset();
+ break;
+ }
- strcpy(tmp + strlen(CT_PROG) + 1, buf);
- printf("(%d) Executing: %s\n", line, tmp);
+ printf("(%d) Executing: %s\n", line, cmd_buf);
fflush(stdout);
- ret = system(tmp);
+ ret = system(cmd_buf);
if (WIFEXITED(ret) &&
WEXITSTATUS(ret) == EXIT_SUCCESS) {
- if (res[0] == 'O' &&
- res[1] == 'K')
+ if (cmd == 'O')
ok++;
else {
bad++;
printf("^----- BAD\n");
}
} else {
- if (res[0] == 'B' &&
- res[1] == 'A' &&
- res[2] == 'D')
+ if (cmd == 'B')
ok++;
else {
bad++;
diff --git a/tests/conntrack/testsuite/00create b/tests/conntrack/testsuite/00create
index 4e55a7b..af22f18 100644
--- a/tests/conntrack/testsuite/00create
+++ b/tests/conntrack/testsuite/00create
@@ -30,3 +30,44 @@
-D -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 ; OK
# mismatched address family
-I -s 2001:DB8::1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+# creae icmp ping request entry
+-I -t 29 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; OK
+# delete icmp ping request entry
+-D -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; OK
+# Test protocols unknown by the conntrack tool
+# IGMP
+-I -t 10 -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p 2 ; OK
+# Create again - should fail
+-I -t 10 -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p 2 ; BAD
+# repeat using protocol name instead of the value, should fail as well
+-I -t 10 -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p igmp ; BAD
+# delete
+-D -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p 2 ; OK
+# delete again should fail
+-D -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p 2 ; BAD
+# create using protocol name instead of the value
+-I -t 10 -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p igmp ; OK
+# update
+-U -t 11 -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p 2 ; OK
+# delete
+-D -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p 2 ; OK
+# delete again should fail
+-D -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p igmp ; BAD
+# take some protocol that is not normally not in /etc/protocols
+-I -t 10 -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p 200 ; OK
+# update
+-U -t 11 -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p 200 ; OK
+# delete
+-D -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p 200 ; OK
+# delete again
+-D -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p 200 ; BAD
+# Invalid protocol values
+# 256 should fail
+-I -t 10 -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p 256 ; BAD
+# take some invalid protocol name
+-I -t 10 -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p foo ; BAD
+# take some other invalid protocol values
+-I -t 10 -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p -10 ; BAD
+-I -t 10 -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p 2000 ; BAD
+-I -t 10 -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p 20foo ; BAD
+-I -t 10 -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p foo20 ; BAD
diff --git a/tests/conntrack/testsuite/01delete b/tests/conntrack/testsuite/01delete
index 2755491..64dbb10 100644
--- a/tests/conntrack/testsuite/01delete
+++ b/tests/conntrack/testsuite/01delete
@@ -30,3 +30,8 @@
-D -s 1.1.1.0/24 -d 2.2.2.0/24 ; OK
# try same command again but with CIDR (no matching found)
-D -s 1.1.1.0/24 -d 2.2.2.0/24 ; BAD
+# try to delete mismatching address family
+-D -s ::1 -d 2.2.2.2 ; BAD
+# try to delete IPv6 address without specifying IPv6 family
+-I -s ::1 -d ::2 -p tcp --sport 20 --dport 10 --state LISTEN -u SEEN_REPLY -t 40 ; OK
+-D -s ::1 ; OK
diff --git a/tests/conntrack/testsuite/02filter b/tests/conntrack/testsuite/02filter
index 91a75eb..d58637f 100644
--- a/tests/conntrack/testsuite/02filter
+++ b/tests/conntrack/testsuite/02filter
@@ -23,5 +23,13 @@ conntrack -L --mark 0/0xffffffff; OK
conntrack -L -s 1.1.1.0 --mask-src 255.255.255.0 -d 2.0.0.0 --mask-dst 255.0.0.0 ; OK
conntrack -L -s 1.1.1.4/24 -d 2.3.4.5/8 ; OK
conntrack -L -s 1.1.2.0/24 -d 2.3.4.5/8 ; OK
+# filter filter mismatching address family
+conntrack -L -s 2.2.2.2 -d ::1 ; BAD
+# filter by IPv6 address, it implicitly sets IPv6 family
+conntrack -L -s ::1 ; OK
+# filter by IPv6 address mask, it implicitly sets IPv6 family
+conntrack -L -s abcd:abcd:abcd:: --mask-src ffff:ffff:ffff:: ; OK
+# filter filter mismatching address family
+conntrack -L --mask-src ffff:ffff:ffff:: --mask-dst 255.0.0.0 ; BAD
# delete dummy
conntrack -D -d 2.2.2.2 ; OK
diff --git a/tests/conntrack/testsuite/08stdin b/tests/conntrack/testsuite/08stdin
new file mode 100644
index 0000000..158c4c3
--- /dev/null
+++ b/tests/conntrack/testsuite/08stdin
@@ -0,0 +1,125 @@
+# create
+# create a conntrack
+-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ;
+# create from reply
+-I -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY -t 50 ;
+# create a v6 conntrack
+-I -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ;
+# creae icmp ping request entry
+-I -t 29 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ;
+-R - ; OK
+# create again
+-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+-I -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+-I -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+-I -t 29 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; BAD
+# make sure create again with stdio mode fails as well
+-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ;
+-R - ; BAD
+-I -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY -t 50 ;
+-R - ; BAD
+# empty lines are ignored
+;
+-I -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ;
+-R - ; BAD
+# spaces or tabs are ignored as well
+ ;
+ ;
+-I -t 29 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ;
+-R - ; BAD
+# delete
+-D -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 ;
+# empty lines should be just ignored
+;
+;
+# delete reverse
+-D -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 ;
+# empty lines with spaces or tabs should be ignored as well
+ ;
+ ;
+ ;
+ ;
+ ;
+ ;
+# delete v6 conntrack
+-D -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 ;
+# delete icmp ping request entry
+-D -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ;
+;
+;
+-R - ; OK
+# create again - should succeed now
+-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+-I -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+-I -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+-I -t 29 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; OK
+# delete again (for cleanup)
+-D -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 ;
+-D -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 ;
+-D -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 ;
+-D -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ;
+;
+-R - ; OK
+# delete no entries - should return err
+-D -w 123 ; BAD
+# delete no entries via stdin - should succeed since we do not count entries in stdin mode atm
+-D -w 123 ;
+-R - ; OK
+# delete no entries in parallel with adding entries via stdin - should succeed
+# -D and -I should work in parallel
+-D -w 123 ;
+-I -w 123 -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ;
+-R - ; OK
+# now deleting entries should return success
+-D -w 123 ;
+-R - ; OK
+# delete no entries via stdin - should succeed since we do not count entries in stdin mode atm
+-D -w 123 ;
+-R - ; OK
+# validate it via standard command line way
+-D -w 123 ; BAD
+# create with -A
+# create a conntrack
+-A -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ;
+# create from reply
+-A -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY -t 50 ;
+# create a v6 conntrack
+-A -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ;
+# creae icmp ping request entry
+-A -t 29 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ;
+-R - ; OK
+# create again
+-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+-I -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+-I -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+-I -t 29 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; BAD
+# repeat, it should succeed
+-A -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ;
+# create from reply
+-A -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY -t 50 ;
+# create a v6 conntrack
+-A -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ;
+# creae icmp ping request entry
+-A -t 29 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ;
+-R - ; OK
+# delete
+-D -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 ;
+# empty lines should be just ignored
+;
+;
+# delete reverse
+-D -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 ;
+# empty lines with spaces or tabs should be ignored as well
+ ;
+ ;
+ ;
+ ;
+ ;
+ ;
+# delete v6 conntrack
+-D -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 ;
+# delete icmp ping request entry
+-D -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ;
+;
+;
+-R - ; OK
diff --git a/tests/conntrack/testsuite/09dumpopt b/tests/conntrack/testsuite/09dumpopt
new file mode 100644
index 0000000..c1e0e6e
--- /dev/null
+++ b/tests/conntrack/testsuite/09dumpopt
@@ -0,0 +1,173 @@
+# test opts output for -L
+# create
+# create a conntrack
+-I -w 10 -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ;
+# create from reply
+-I -w 10 -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY -t 50 ;
+# create a v6 conntrack
+-I -w 10 -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ;
+# creae icmp ping request entry
+-I -w 10 -t 29 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ;
+-R - ; OK
+# copy ipv4 bits to zone 11
+-L -w 10 -o save -f ipv4 ; |s/-w 10/-w 11/g
+-R - ; OK
+# copy ipv6 bits to zone 11
+-L -w 10 -o save -f ipv6 ; |s/-w 10/-w 11/g
+-R - ; OK
+# create again in zone 11
+-I -w 11 -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+-I -w 11 -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+-I -w 11 -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+-I -w 11 -t 29 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; BAD
+# delete new entries
+-D -w 11 -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 ; OK
+# delete reverse
+-D -w 11 -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 ; OK
+# delete v6 conntrack
+-D -w 11-s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 ; OK
+# delete icmp ping request entry
+-D -w 11 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; OK
+# delete old entries
+-D -w 10 -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 ; OK
+# delete reverse
+-D -w 10 -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 ; OK
+# delete v6 conntrack
+-D -w 10-s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 ; OK
+# delete icmp ping request entry
+-D -w 10 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; OK
+#
+# now test opts output for -D
+# create entries again
+# create a conntrack
+-I -w 10 -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ;
+# create from reply
+-I -w 10 -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY -t 50 ;
+# create a v6 conntrack
+-I -w 10 -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ;
+# creae icmp ping request entry
+-I -w 10 -t 29 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ;
+-R - ; OK
+# move ipv4 bits to zone 11
+-D -w 10 -o save -f ipv4 ; |s/-w 10/-w 11/g; s/-D /-I /g
+-R - ; OK
+# move ipv6 bits to zone 11
+-D -w 10 -o save -f ipv6 ; |s/-w 10/-w 11/g; s/-D /-I /g
+-R - ; OK
+# create again in zone 11
+-I -w 11 -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+-I -w 11 -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+-I -w 11 -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+-I -w 11 -t 29 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; BAD
+# delete new entries
+-D -w 11 -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 ; OK
+# delete reverse
+-D -w 11 -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 ; OK
+# delete v6 conntrack
+-D -w 11-s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 ; OK
+# delete icmp ping request entry
+-D -w 11 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; OK
+# delete old entries
+-D -w 10 -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 ; BAD
+# delete reverse
+-D -w 10 -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 ; BAD
+# delete v6 conntrack
+-D -w 10-s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 ; BAD
+# delete icmp ping request entry
+-D -w 10 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; BAD
+#
+# Additional tests to check that family attribute is treated properly
+# for -L and -D commands
+# namely:
+# - if family (-f) is given - only entries of the given family are dumped/deleted
+# - if no family is given - entries of both ipv4 and ipv6 families are dumped/deleted
+# First create some ipv4 and ipv6 entries
+-I -w 10 -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+-I -w 10 -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+-I -w 10 -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+-I -w 10 -t 29 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; OK
+# dump all entries to zone 11
+-L -w 10 -o save; |s/-w 10/-w 11/g
+-R - ; OK
+# ensure that both ipv4 and ipv6 entries get copied (delete for each of them should succeed)
+-D -w 11 -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY ; OK
+-D -w 11 -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY ; OK
+-D -w 11 -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY; OK
+-D -w 11 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; OK
+# dump only ipv4 entries to zone 11
+-L -w 10 -o save -f ipv4; |s/-w 10/-w 11/g
+-R - ; OK
+# ensure that only ipv4 entries get copied (delete only for ipv4 entries should succeed)
+-D -w 11 -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY; OK
+-D -w 11 -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY; OK
+-D -w 11 -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY; BAD
+-D -w 11 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; OK
+# dump only ipv6 entries to zone 11
+-L -w 10 -o save -f ipv6; |s/-w 10/-w 11/g
+-R - ; OK
+# ensure that only ipv6 entries get copied (delete only for ipv6 entries should succeed)
+-D -w 11 -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY; BAD
+-D -w 11 -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY; BAD
+-D -w 11 -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY; OK
+-D -w 11 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; BAD
+# now test deleting w/ and /o family specified
+# for simplicity do it by re-creating entries in zone 11
+# by copying ezisting entries from zone 10 into it
+# re-create entries in ct zone 11
+-L -w 10 -o save; |s/-w 10/-w 11/g
+-R - ; OK
+# delete all entries in zone 11
+-D -w 11 ; OK
+# both ipv4 and ipv6 should be deleted
+-D -w 11 -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY; BAD
+-D -w 11 -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY; BAD
+-D -w 11 -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY; BAD
+-D -w 11 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; BAD
+# re-create entries in ct zone 11
+-L -w 10 -o save; |s/-w 10/-w 11/g
+-R - ; OK
+# delete only ipv4 entries in zone 11
+-D -w 11 -f ipv4 ; OK
+# ipv6 should remain
+-D -w 11 -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY; BAD
+-D -w 11 -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY; BAD
+-D -w 11 -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY; OK
+-D -w 11 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; BAD
+ # re-create entries in ct zone 11
+-L -w 10 -o save; |s/-w 10/-w 11/g
+-R - ; OK
+# delete only ipv6 entries in zone 11
+-D -w 11 -f ipv6 ; OK
+# ipv4 should remain
+-D -w 11 -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY; OK
+-D -w 11 -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY; OK
+-D -w 11 -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY; BAD
+-D -w 11 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; OK
+# clean up after yourself
+-D -w 10 ; OK
+# Cover protocols unknown to the conntrack tool
+# Create a conntrack entries
+# IGMP
+-I -w 10 -t 59 -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p 2 ;
+# Some fency protocol
+-I -w 10 -t 59 -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p 200 ;
+# Some fency protocol with IPv6
+-I -w 10 -t 59 -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p 200 ;
+-R - ; OK
+# copy to zone 11
+-L -w 10 -o save ; |s/-w 10/-w 11/g
+-R - ; OK
+# Delete stuff in zone 10, should succeed
+# IGMP
+-D -w 10 -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p 2 ; OK
+# Some fency protocol
+-D -w 10 -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p 200 ; OK
+# Some fency protocol with IPv6
+-D -w 10 -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p 200 ; OK
+# Delete stuff in zone 11, should succeed
+# IGMP
+-D -w 11 -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p 2 ; OK
+# Some fency protocol
+-D -w 11 -s 0.0.0.0 -d 224.0.0.22 -r 224.0.0.22 -q 0.0.0.0 -p 200 ; OK
+# Some fency protocol with IPv6
+-D -w 11 -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p 200 ; OK
diff --git a/tests/conntrack/testsuite/10add b/tests/conntrack/testsuite/10add
new file mode 100644
index 0000000..4f9f3b9
--- /dev/null
+++ b/tests/conntrack/testsuite/10add
@@ -0,0 +1,42 @@
+#missing destination
+-A -s 1.1.1.1 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+#missing source
+-A -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+#missing protocol
+-A -s 1.1.1.1 -d 2.2.2.2 --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+#missing source port
+-A -s 1.1.1.1 -d 2.2.2.2 -p tcp --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+#missing destination port
+-A -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+#missing timeout
+-A -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY ; BAD
+# create a conntrack
+-A -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+# create again
+-A -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+# delete
+-D -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 ; OK
+# delete again
+-D -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 ; BAD
+# create from reply
+-A -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+# create again from reply
+-A -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+# delete reverse
+-D -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 ; OK
+# delete reverse again
+-D -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 ; BAD
+# create a v6 conntrack
+-A -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+# create again a v6 conntrack
+-A -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK
+# delete v6 conntrack
+-D -s 2001:DB8::1.1.1.1 -d 2001:DB8::2.2.2.2 -p tcp --sport 10 --dport 20 ; OK
+# mismatched address family
+-A -s 2001:DB8::1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD
+# creae icmp ping request entry
+-A -t 29 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; OK
+# creae again icmp ping request entry
+-A -t 29 -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; OK
+# delete icmp ping request entry
+-D -u SEEN_REPLY -s 1.1.1.1 -d 2.2.2.2 -r 2.2.2.2 -q 1.1.1.1 -p icmp --icmp-type 8 --icmp-code 0 --icmp-id 1226 ; OK
diff --git a/tests/conntrackd/conntrackd-tests.py b/tests/conntrackd/conntrackd-tests.py
new file mode 100755
index 0000000..f760351
--- /dev/null
+++ b/tests/conntrackd/conntrackd-tests.py
@@ -0,0 +1,263 @@
+#!/usr/bin/env python3
+
+# (C) 2021 by Arturo Borrero Gonzalez <arturo@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 3 of the License, or
+# (at your option) any later version.
+#
+
+# tests.yaml file format:
+# - name: "test 1"
+# scenario: scenario1
+# test:
+# - test1 cmd1
+# - test1 cmd2
+
+# scenarios.yaml file format:
+# - name: scenario1
+# start:
+# - cmd1
+# - cmd2
+# stop:
+# - cmd1
+# - cmd2
+
+# env.yaml file format:
+# - VAR1: value1
+# - VAR2: value2
+
+import os
+import sys
+import argparse
+import subprocess
+import yaml
+import logging
+
+
+def read_yaml_file(file):
+ try:
+ with open(file, "r") as stream:
+ try:
+ return yaml.safe_load(stream)
+ except yaml.YAMLError as e:
+ logging.error(e)
+ exit(2)
+ except FileNotFoundError as e:
+ logging.error(e)
+ exit(2)
+
+
+def validate_dictionary(dictionary, keys):
+ if not isinstance(dictionary, dict):
+ logging.error("not a dictionary:\n{}".format(dictionary))
+ return False
+ for key in keys:
+ if dictionary.get(key) is None:
+ logging.error("missing key {} in dictionary:\n{}".format(key, dictionary))
+ return False
+ return True
+
+
+def stage_validate_config(args):
+ scenarios_dict = read_yaml_file(args.scenarios_file)
+ for definition in scenarios_dict:
+ if not validate_dictionary(definition, ["name", "start", "stop"]):
+ logging.error("couldn't validate file {}".format(args.scenarios_file))
+ return False
+
+ logging.debug("{} seems valid".format(args.scenarios_file))
+ ctx.scenarios_dict = scenarios_dict
+
+ tests_dict = read_yaml_file(args.tests_file)
+ for definition in tests_dict:
+ if not validate_dictionary(definition, ["name", "scenario", "test"]):
+ logging.error("couldn't validate file {}".format(args.tests_file))
+ return False
+
+ logging.debug("{} seems valid".format(args.tests_file))
+ ctx.tests_dict = tests_dict
+
+ env_list = read_yaml_file(args.env_file)
+ if not isinstance(env_list, list):
+ logging.error("couldn't validate file {}".format(args.env_file))
+ return False
+
+ # set env to default values if not overridden when calling this very script
+ for entry in env_list:
+ for key in entry:
+ os.environ[key] = os.getenv(key, entry[key])
+
+
+def cmd_run(cmd):
+ logging.debug("running command: {}".format(cmd))
+ r = subprocess.run(cmd, shell=True)
+ if r.returncode != 0:
+ logging.warning("failed command: {}".format(cmd))
+ return r.returncode
+
+
+def scenario_get(name):
+ for n in ctx.scenarios_dict:
+ if n["name"] == name:
+ return n
+
+ logging.error("couldn't find a definition for scenario '{}'".format(name))
+ exit(1)
+
+
+def scenario_start(scenario):
+ for cmd in scenario["start"]:
+ if cmd_run(cmd) == 0:
+ continue
+
+ logging.warning("--- failed scenario: {}".format(scenario["name"]))
+ ctx.counter_scenario_failed += 1
+ ctx.skip_current_test = True
+ return
+
+
+def scenario_stop(scenario):
+ for cmd in scenario["stop"]:
+ cmd_run(cmd)
+
+
+def test_get(name):
+ for n in ctx.tests_dict:
+ if n["name"] == name:
+ return n
+
+ logging.error("couldn't find a definition for test '{}'".format(name))
+ exit(1)
+
+
+def _test_run(test_definition):
+ if ctx.skip_current_test:
+ return
+
+ for cmd in test_definition["test"]:
+ if cmd_run(cmd) == 0:
+ continue
+
+ logging.warning("--- failed test: {}".format(test_definition["name"]))
+ ctx.counter_test_failed += 1
+ return
+
+ logging.info("--- passed test: {}".format(test_definition["name"]))
+ ctx.counter_test_ok += 1
+
+
+def test_run(test_definition):
+ scenario = scenario_get(test_definition["scenario"])
+
+ logging.info("--- running test: {}".format(test_definition["name"]))
+
+ scenario_start(scenario)
+ _test_run(test_definition)
+ scenario_stop(scenario)
+
+
+def stage_run_tests(args):
+ if args.start_scenario:
+ scenario_start(scenario_get(args.start_scenario))
+ return
+
+ if args.stop_scenario:
+ scenario_stop(scenario_get(args.stop_scenario))
+ return
+
+ if args.single:
+ test_run(test_get(args.single))
+ return
+
+ for test_definition in ctx.tests_dict:
+ ctx.skip_current_test = False
+ test_run(test_definition)
+
+
+def stage_report():
+ logging.info("---")
+ logging.info("--- finished")
+ total = ctx.counter_test_ok + ctx.counter_test_failed + ctx.counter_scenario_failed
+ logging.info("--- passed tests: {}".format(ctx.counter_test_ok))
+ logging.info("--- failed tests: {}".format(ctx.counter_test_failed))
+ logging.info("--- scenario failure: {}".format(ctx.counter_scenario_failed))
+ logging.info("--- total tests: {}".format(total))
+
+
+def parse_args():
+ description = "Utility to run tests for conntrack-tools"
+ parser = argparse.ArgumentParser(description=description)
+ parser.add_argument(
+ "--tests-file",
+ default="tests.yaml",
+ help="File with testcase definitions. Defaults to '%(default)s'",
+ )
+ parser.add_argument(
+ "--scenarios-file",
+ default="scenarios.yaml",
+ help="File with configuration scenarios for tests. Defaults to '%(default)s'",
+ )
+ parser.add_argument(
+ "--env-file",
+ default="env.yaml",
+ help="File with environment variables for scenarios/tests. Defaults to '%(default)s'",
+ )
+ parser.add_argument(
+ "--single",
+ help="Execute a single testcase and exit. Use this for developing testcases",
+ )
+ parser.add_argument(
+ "--start-scenario",
+ help="Execute scenario start commands and exit. Use this for developing testcases",
+ )
+ parser.add_argument(
+ "--stop-scenario",
+ help="Execute scenario stop commands and exit. Use this for cleanup",
+ )
+ parser.add_argument(
+ "--debug",
+ action="store_true",
+ help="debug mode",
+ )
+
+ return parser.parse_args()
+
+
+class Context:
+ def __init__(self):
+ self.scenarios_dict = None
+ self.tests_dict = None
+ self.counter_test_failed = 0
+ self.counter_test_ok = 0
+ self.counter_scenario_failed = 0
+ self.skip_current_test = False
+
+
+# global data
+ctx = Context()
+
+
+def main():
+ args = parse_args()
+
+ logging_format = "[%(filename)s] %(levelname)s: %(message)s"
+ if args.debug:
+ logging_level = logging.DEBUG
+ else:
+ logging_level = logging.INFO
+ logging.basicConfig(format=logging_format, level=logging_level, stream=sys.stdout)
+
+ if os.geteuid() != 0:
+ logging.error("root required")
+ exit(1)
+
+ stage_validate_config(args)
+ stage_run_tests(args)
+ stage_report()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/conntrackd/env.yaml b/tests/conntrackd/env.yaml
new file mode 100644
index 0000000..daf8ea1
--- /dev/null
+++ b/tests/conntrackd/env.yaml
@@ -0,0 +1,2 @@
+- CONNTRACKD: ../../src/conntrackd
+- CONNTRACK: ../../src/conntrack
diff --git a/tests/conntrackd/netns/conntrackd-netns-test.sh b/tests/conntrackd/netns/conntrackd-netns-test.sh
new file mode 100755
index 0000000..6f16587
--- /dev/null
+++ b/tests/conntrackd/netns/conntrackd-netns-test.sh
@@ -0,0 +1,66 @@
+#!/bin/bash
+
+if [ $UID -ne 0 ]
+then
+ echo "You must be root to run this test script"
+ exit 0
+fi
+
+start () {
+ ip netns add ns1
+ ip netns add ns2
+ ip netns add nsr1
+ ip netns add nsr2
+
+ ip link add veth0 netns ns1 type veth peer name veth1 netns nsr1
+ ip link add veth0 netns nsr1 type veth peer name veth0 netns ns2
+ ip link add veth2 netns nsr1 type veth peer name veth0 netns nsr2
+
+ ip -net ns1 addr add 192.168.10.2/24 dev veth0
+ ip -net ns1 link set up dev veth0
+ ip -net ns1 ro add 10.0.1.0/24 via 192.168.10.1 dev veth0
+
+ ip -net nsr1 addr add 10.0.1.1/24 dev veth0
+ ip -net nsr1 addr add 192.168.10.1/24 dev veth1
+ ip -net nsr1 link set up dev veth0
+ ip -net nsr1 link set up dev veth1
+ ip -net nsr1 route add default via 192.168.10.2
+ ip netns exec nsr1 sysctl net.ipv4.ip_forward=1
+
+ ip -net nsr1 addr add 192.168.100.2/24 dev veth2
+ ip -net nsr1 link set up dev veth2
+ ip -net nsr2 addr add 192.168.100.3/24 dev veth0
+ ip -net nsr2 link set up dev veth0
+
+ ip -net ns2 addr add 10.0.1.2/24 dev veth0
+ ip -net ns2 link set up dev veth0
+ ip -net ns2 route add default via 10.0.1.1
+
+ echo 1 > /proc/sys/net/netfilter/nf_log_all_netns
+
+ ip netns exec nsr1 nft -f ruleset-nsr1.nft
+ ip netns exec nsr1 conntrackd -C conntrackd-nsr1.conf -d
+ ip netns exec nsr2 conntrackd -C conntrackd-nsr2.conf -d
+}
+
+stop () {
+ ip netns del ns1
+ ip netns del ns2
+ ip netns del nsr1
+ ip netns del nsr2
+ killall -15 conntrackd
+}
+
+case $1 in
+start)
+ start
+ ;;
+stop)
+ stop
+ ;;
+*)
+ echo "$0 [start|stop]"
+ ;;
+esac
+
+exit 0
diff --git a/tests/conntrackd/netns/conntrackd-nsr1.conf b/tests/conntrackd/netns/conntrackd-nsr1.conf
new file mode 100644
index 0000000..c79eff5
--- /dev/null
+++ b/tests/conntrackd/netns/conntrackd-nsr1.conf
@@ -0,0 +1,37 @@
+Sync {
+ Mode FTFW {
+ }
+ Multicast {
+ IPv4_address 225.0.0.50
+ Group 3780
+ IPv4_interface 192.168.100.2
+ Interface veth2
+ SndSocketBuffer 1249280
+ RcvSocketBuffer 1249280
+ Checksum on
+ }
+}
+General {
+ HashSize 32768
+ HashLimit 131072
+ LogFile on
+ LockFile /var/lock/conntrack-nsr1.lock
+ UNIX {
+ Path /var/run/conntrackd-nsr1.ctl
+ }
+ NetlinkBufferSize 2097152
+ NetlinkBufferSizeMaxGrowth 8388608
+ Filter From Userspace {
+ Protocol Accept {
+ TCP
+ SCTP
+ DCCP
+ }
+ Address Ignore {
+ IPv4_address 127.0.0.1
+ IPv4_address 192.168.10.1
+ IPv4_address 10.0.10.1
+ IPv4_address 192.168.100.2
+ }
+ }
+}
diff --git a/tests/conntrackd/netns/conntrackd-nsr2.conf b/tests/conntrackd/netns/conntrackd-nsr2.conf
new file mode 100644
index 0000000..65fa0d6
--- /dev/null
+++ b/tests/conntrackd/netns/conntrackd-nsr2.conf
@@ -0,0 +1,37 @@
+Sync {
+ Mode FTFW {
+ }
+ Multicast {
+ IPv4_address 225.0.0.50
+ Group 3780
+ IPv4_interface 192.168.100.3
+ Interface veth0
+ SndSocketBuffer 1249280
+ RcvSocketBuffer 1249280
+ Checksum on
+ }
+}
+General {
+ HashSize 32768
+ HashLimit 131072
+ LogFile on
+ LockFile /var/lock/conntrack-nsr2.lock
+ UNIX {
+ Path /var/run/conntrackd-nsr2.ctl
+ }
+ NetlinkBufferSize 2097152
+ NetlinkBufferSizeMaxGrowth 8388608
+ Filter From Userspace {
+ Protocol Accept {
+ TCP
+ SCTP
+ DCCP
+ }
+ Address Ignore {
+ IPv4_address 127.0.0.1
+ IPv4_address 192.168.10.1
+ IPv4_address 10.0.10.1
+ IPv4_address 192.168.100.2
+ }
+ }
+}
diff --git a/tests/conntrackd/netns/ruleset-nsr1.nft b/tests/conntrackd/netns/ruleset-nsr1.nft
new file mode 100644
index 0000000..bd6f1b4
--- /dev/null
+++ b/tests/conntrackd/netns/ruleset-nsr1.nft
@@ -0,0 +1,6 @@
+table ip filter {
+ chain postrouting {
+ type nat hook postrouting priority srcnat; policy accept;
+ oif veth0 masquerade
+ }
+}
diff --git a/tests/conntrackd/scenarios.yaml b/tests/conntrackd/scenarios.yaml
new file mode 100644
index 0000000..65d6fa4
--- /dev/null
+++ b/tests/conntrackd/scenarios.yaml
@@ -0,0 +1,100 @@
+- name: empty
+ start:
+ - ":"
+ stop:
+ - ":"
+- name: simple_stats
+ start:
+ - rm -f /var/lock/conntrack.lock
+ - |
+ cat << EOF > /tmp/conntrackd_test_simple_stats
+ General {
+ HashSize 8192
+ LockFile /var/lock/conntrack.lock
+ UNIX { Path /var/run/conntrackd.ctl }
+ }
+ Stats {
+ LogFile on
+ }
+ EOF
+ - $CONNTRACKD -C /tmp/conntrackd_test_simple_stats -d
+ stop:
+ - $CONNTRACKD -C /tmp/conntrackd_test_simple_stats -k
+ - rm -f /var/lock/conntrack.lock
+ - rm -f /tmp/conntrackd_test_simple_stats
+
+- name: basic_2_peer_network_tcp_notrack
+ start:
+ - scenarios/basic/./network-setup.sh start
+ - |
+ cat << EOF > /tmp/ruleset.nft
+ table ip filter {
+ chain postrouting {
+ type nat hook postrouting priority srcnat; policy accept;
+ oif veth0 masquerade
+ }
+ }
+ EOF
+ - ip netns exec nsr1 nft -f /tmp/ruleset.nft
+ - |
+ cat << EOF > /tmp/nsr1.conf
+ Sync {
+ Mode NOTRACK {
+ DisableExternalCache on
+ DisableInternalCache on
+ }
+ TCP {
+ IPv4_address 192.168.100.2
+ IPv4_Destination_Address 192.168.100.3
+ Interface veth2
+ Port 3780
+ }
+ }
+ General {
+ LogFile on
+ LockFile /var/lock/conntrack-nsr1.lock
+ UNIX { Path /var/run/conntrackd-nsr1.ctl }
+ }
+ EOF
+ - |
+ cat << EOF > /tmp/nsr2.conf
+ Sync {
+ Mode NOTRACK {
+ DisableExternalCache on
+ DisableInternalCache on
+ }
+ TCP {
+ IPv4_address 192.168.100.3
+ IPv4_Destination_Address 192.168.100.2
+ Interface veth0
+ Port 3780
+ }
+ }
+ General {
+ LogFile on
+ LockFile /var/lock/conntrack-nsr2.lock
+ UNIX { Path /var/run/conntrackd-nsr2.ctl }
+ }
+ EOF
+ # finally run the daemons
+ - ip netns exec nsr1 $CONNTRACKD -C /tmp/nsr1.conf -d
+ - ip netns exec nsr2 $CONNTRACKD -C /tmp/nsr2.conf -d
+ # make sure they are alive and connected before considering the scenario started
+ - timeout 5 bash -c -- '
+ while ! ip netns exec nsr1 $CONNTRACKD -C /tmp/nsr1.conf -s | grep -q "server=connected"
+ ; do sleep 0.5 ; done'
+ - timeout 5 bash -c -- '
+ while ! ip netns exec nsr1 $CONNTRACKD -C /tmp/nsr1.conf -s | grep -q "client=connected"
+ ; do sleep 0.5 ; done'
+ - timeout 5 bash -c -- '
+ while ! ip netns exec nsr2 $CONNTRACKD -C /tmp/nsr2.conf -s | grep -q "server=connected"
+ ; do sleep 0.5 ; done'
+ - timeout 5 bash -c -- '
+ while ! ip netns exec nsr2 $CONNTRACKD -C /tmp/nsr2.conf -s | grep -q "client=connected"
+ ; do sleep 0.5 ; done'
+ stop:
+ - $CONNTRACKD -C /tmp/nsr1.conf -k 2>/dev/null
+ - $CONNTRACKD -C /tmp/nsr2.conf -k 2>/dev/null
+ - rm -f /tmp/ruleset.nft /tmp/nsr2.conf /tmp/nsr1.conf
+ - rm -f /var/lock/conntrack-nsr1.lock /var/lock/conntrack-nsr2.lock
+ - scenarios/basic/./network-setup.sh stop
diff --git a/tests/conntrackd/scenarios/basic/network-setup.sh b/tests/conntrackd/scenarios/basic/network-setup.sh
new file mode 100755
index 0000000..7f2f78a
--- /dev/null
+++ b/tests/conntrackd/scenarios/basic/network-setup.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+if [ $UID -ne 0 ]
+then
+ echo "You must be root to run this test script"
+ exit 0
+fi
+
+start () {
+ ip netns add ns1
+ ip netns add ns2
+ ip netns add nsr1
+ ip netns add nsr2
+
+ ip link add veth0 netns ns1 type veth peer name veth1 netns nsr1
+ ip link add veth0 netns nsr1 type veth peer name veth0 netns ns2
+ ip link add veth2 netns nsr1 type veth peer name veth0 netns nsr2
+
+ ip -net ns1 addr add 192.168.10.2/24 dev veth0
+ ip -net ns1 link set up dev veth0
+ ip -net ns1 ro add 10.0.1.0/24 via 192.168.10.1 dev veth0
+
+ ip -net nsr1 addr add 10.0.1.1/24 dev veth0
+ ip -net nsr1 addr add 192.168.10.1/24 dev veth1
+ ip -net nsr1 link set up dev veth0
+ ip -net nsr1 link set up dev veth1
+ ip -net nsr1 route add default via 192.168.10.2
+ ip netns exec nsr1 sysctl -q net.ipv4.ip_forward=1
+
+ ip -net nsr1 addr add 192.168.100.2/24 dev veth2
+ ip -net nsr1 link set up dev veth2
+ ip -net nsr2 addr add 192.168.100.3/24 dev veth0
+ ip -net nsr2 link set up dev veth0
+
+ ip -net ns2 addr add 10.0.1.2/24 dev veth0
+ ip -net ns2 link set up dev veth0
+ ip -net ns2 route add default via 10.0.1.1
+}
+
+stop () {
+ ip netns del ns1
+ ip netns del ns2
+ ip netns del nsr1
+ ip netns del nsr2
+}
+
+case $1 in
+start)
+ start
+ ;;
+stop)
+ stop
+ ;;
+*)
+ echo "$0 [start|stop]"
+ ;;
+esac
+
+exit 0
diff --git a/tests/conntrackd/tests.yaml b/tests/conntrackd/tests.yaml
new file mode 100644
index 0000000..307f38f
--- /dev/null
+++ b/tests/conntrackd/tests.yaml
@@ -0,0 +1,83 @@
+- name: stats_general
+ scenario: simple_stats
+ # check that we can obtain stats via unix socket: general
+ test:
+ - $CONNTRACKD -C /tmp/conntrackd_test_simple_stats -s | grep -q "cache stats"
+
+- name: stats_network
+ scenario: simple_stats
+ # check that we can obtain stats via unix socket: network (no output)
+ test:
+ - $CONNTRACKD -C /tmp/conntrackd_test_simple_stats -s network
+
+- name: stats_runtime
+ scenario: simple_stats
+ # check that we can obtain stats via unix socket: runtime
+ test:
+ - $CONNTRACKD -C /tmp/conntrackd_test_simple_stats -s runtime | grep -q uptime
+
+- name: stats_process
+ scenario: simple_stats
+ # check that we can obtain stats via unix socket: process (no output)
+ test:
+ - $CONNTRACKD -C /tmp/conntrackd_test_simple_stats -s process
+
+- name: stats_queue
+ scenario: simple_stats
+ # check that we can obtain stats via unix socket: queue (no output)
+ test:
+ - $CONNTRACKD -C /tmp/conntrackd_test_simple_stats -s queue
+
+- name: stats_ct
+ scenario: simple_stats
+ # check that we can obtain stats via unix socket: ct
+ test:
+ - $CONNTRACKD -C /tmp/conntrackd_test_simple_stats -s ct | grep -q traffic
+
+- name: stats_expect
+ scenario: simple_stats
+ # check that we can obtain stats via unix socket: expect (no output)
+ test:
+ - $CONNTRACKD -C /tmp/conntrackd_test_simple_stats -s expect
+
+- name: tcp_notrack_replicate_icmp
+ scenario: basic_2_peer_network_tcp_notrack
+ # check that we can replicate a ICMP conntrack entry in a 2 conntrackd TCP/NOTRACK setup
+ test:
+ # PING should inject an ICMP conntrack entry in nsr1
+ - ip netns exec ns1 ping -c1 10.0.1.2 >/dev/null
+ # verify conntrack entry is then replicated to nsr2, wait up to 5 seconds
+ - timeout 5 bash -c -- '
+ while ! ip netns exec nsr2 $CONNTRACK -L -p icmp 2>/dev/null | grep -q icmp
+ ; do sleep 0.5 ; done'
+
+- name: hash_defaults_segfault
+ scenario: empty
+ test:
+ - rm -f /var/lock/conntrack.lock
+ - |
+ cat << EOF > /tmp/conntrackd_notrack_hash_defaults
+ Sync {
+ Mode NOTRACK { }
+ Multicast {
+ IPv4_address 225.0.0.50
+ Group 3780
+ IPv4_interface 127.0.0.1
+ Interface lo
+ SndSocketBuffer 1249280
+ RcvSocketBuffer 1249280
+ Checksum on
+ }
+ }
+ General {
+ LogFile on
+ Syslog on
+ LockFile /var/lock/conntrackd.lock
+ UNIX { Path /var/run/conntrackd.sock }
+ NetlinkBufferSize 2097152
+ NetlinkBufferSizeMaxGrowth 8388608
+ }
+ EOF
+ - $CONNTRACKD -C /tmp/conntrackd_notrack_hash_defaults -d
+ - $CONNTRACKD -C /tmp/conntrackd_notrack_hash_defaults -s | grep -q "cache"
+ - $CONNTRACKD -C /tmp/conntrackd_notrack_hash_defaults -k