summaryrefslogtreecommitdiffstats
path: root/iptables
diff options
context:
space:
mode:
Diffstat (limited to 'iptables')
-rw-r--r--iptables/Makefile.am7
-rw-r--r--iptables/ebtables-nft.862
-rw-r--r--iptables/ip6tables-apply.81
-rwxr-xr-xiptables/iptables-apply302
-rw-r--r--iptables/iptables-apply.8.in46
-rw-r--r--iptables/iptables-restore.8.in2
-rw-r--r--iptables/iptables-save.8.in2
-rw-r--r--iptables/iptables.8.in4
-rw-r--r--iptables/nft-arp.c13
-rw-r--r--iptables/nft-bridge.c232
-rw-r--r--iptables/nft-bridge.h56
-rw-r--r--iptables/nft-cache.c290
-rw-r--r--iptables/nft-cache.h2
-rw-r--r--iptables/nft-ipv4.c10
-rw-r--r--iptables/nft-ipv6.c10
-rw-r--r--iptables/nft-shared.c72
-rw-r--r--iptables/nft-shared.h26
-rw-r--r--iptables/nft.c336
-rw-r--r--iptables/nft.h13
-rwxr-xr-xiptables/tests/shell/testcases/arptables/0001-arptables-save-restore_02
-rwxr-xr-xiptables/tests/shell/testcases/arptables/0002-arptables-restore-defaults_02
-rwxr-xr-xiptables/tests/shell/testcases/arptables/0003-arptables-verbose-output_02
-rwxr-xr-xiptables/tests/shell/testcases/ebtables/0001-ebtables-basic_0135
-rwxr-xr-xiptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_02
-rwxr-xr-xiptables/tests/shell/testcases/ebtables/0003-ebtables-restore-defaults_02
-rwxr-xr-xiptables/tests/shell/testcases/ebtables/0004-save-counters_02
-rwxr-xr-xiptables/tests/shell/testcases/ebtables/0005-ifnamechecks_02
-rwxr-xr-xiptables/tests/shell/testcases/firewalld-restore/0001-firewalld_02
-rwxr-xr-xiptables/tests/shell/testcases/ipt-restore/0004-restore-race_02
-rwxr-xr-xiptables/tests/shell/testcases/ipt-restore/0010-noflush-new-chain_010
-rwxr-xr-xiptables/tests/shell/testcases/ipt-restore/0011-noflush-empty-line_016
-rwxr-xr-xiptables/tests/shell/testcases/iptables/0006-46-args_088
-rwxr-xr-xiptables/tests/shell/testcases/nft-only/0001compat_015
-rwxr-xr-xiptables/tests/shell/testcases/nft-only/0002invflags_02
-rwxr-xr-xiptables/tests/shell/testcases/nft-only/0003delete-with-comment_02
-rw-r--r--iptables/xtables-arp.c9
-rw-r--r--iptables/xtables-eb.c10
-rw-r--r--iptables/xtables-monitor.c17
-rw-r--r--iptables/xtables-restore.c37
-rw-r--r--iptables/xtables-save.c17
-rw-r--r--iptables/xtables-standalone.c6
-rw-r--r--iptables/xtables-translate.c38
-rw-r--r--iptables/xtables.c25
43 files changed, 1459 insertions, 472 deletions
diff --git a/iptables/Makefile.am b/iptables/Makefile.am
index fc834e0f..71b1b1d4 100644
--- a/iptables/Makefile.am
+++ b/iptables/Makefile.am
@@ -53,7 +53,11 @@ sbin_PROGRAMS += xtables-nft-multi
endif
man_MANS = iptables.8 iptables-restore.8 iptables-save.8 \
iptables-xml.1 ip6tables.8 ip6tables-restore.8 \
- ip6tables-save.8 iptables-extensions.8
+ ip6tables-save.8 iptables-extensions.8 \
+ iptables-apply.8 ip6tables-apply.8
+
+sbin_SCRIPT = iptables-apply
+
if ENABLE_NFTABLES
man_MANS += xtables-nft.8 xtables-translate.8 xtables-legacy.8 \
iptables-translate.8 ip6tables-translate.8 \
@@ -106,3 +110,4 @@ install-exec-hook:
for i in ${v4_sbin_links}; do ${LN_S} -f xtables-legacy-multi "${DESTDIR}${sbindir}/$$i"; done;
for i in ${v6_sbin_links}; do ${LN_S} -f xtables-legacy-multi "${DESTDIR}${sbindir}/$$i"; done;
for i in ${x_sbin_links}; do ${LN_S} -f xtables-nft-multi "${DESTDIR}${sbindir}/$$i"; done;
+ ${LN_S} -f iptables-apply "${DESTDIR}${sbindir}/ip6tables-apply"
diff --git a/iptables/ebtables-nft.8 b/iptables/ebtables-nft.8
index db8b2ab2..1fa5ad93 100644
--- a/iptables/ebtables-nft.8
+++ b/iptables/ebtables-nft.8
@@ -522,35 +522,35 @@ If the 802.3 DSAP and SSAP values are 0xaa then the SNAP type field must
be consulted to determine the payload protocol. This is a two byte
(hexadecimal) argument. Only 802.3 frames with DSAP/SSAP 0xaa are
checked for type.
-.\" .SS among
-.\" Match a MAC address or MAC/IP address pair versus a list of MAC addresses
-.\" and MAC/IP address pairs.
-.\" A list entry has the following format:
-.\" .IR xx:xx:xx:xx:xx:xx[=ip.ip.ip.ip][,] ". Multiple"
-.\" list entries are separated by a comma, specifying an IP address corresponding to
-.\" the MAC address is optional. Multiple MAC/IP address pairs with the same MAC address
-.\" but different IP address (and vice versa) can be specified. If the MAC address doesn't
-.\" match any entry from the list, the frame doesn't match the rule (unless "!" was used).
-.\" .TP
-.\" .BR "--among-dst " "[!] \fIlist\fP"
-.\" Compare the MAC destination to the given list. If the Ethernet frame has type
-.\" .IR IPv4 " or " ARP ,
-.\" then comparison with MAC/IP destination address pairs from the
-.\" list is possible.
-.\" .TP
-.\" .BR "--among-src " "[!] \fIlist\fP"
-.\" Compare the MAC source to the given list. If the Ethernet frame has type
-.\" .IR IPv4 " or " ARP ,
-.\" then comparison with MAC/IP source address pairs from the list
-.\" is possible.
-.\" .TP
-.\" .BR "--among-dst-file " "[!] \fIfile\fP"
-.\" Same as
-.\" .BR --among-dst " but the list is read in from the specified file."
-.\" .TP
-.\" .BR "--among-src-file " "[!] \fIfile\fP"
-.\" Same as
-.\" .BR --among-src " but the list is read in from the specified file."
+.SS among
+Match a MAC address or MAC/IP address pair versus a list of MAC addresses
+and MAC/IP address pairs.
+A list entry has the following format:
+.IR xx:xx:xx:xx:xx:xx[=ip.ip.ip.ip][,] ". Multiple"
+list entries are separated by a comma, specifying an IP address corresponding to
+the MAC address is optional. Multiple MAC/IP address pairs with the same MAC address
+but different IP address (and vice versa) can be specified. If the MAC address doesn't
+match any entry from the list, the frame doesn't match the rule (unless "!" was used).
+.TP
+.BR "--among-dst " "[!] \fIlist\fP"
+Compare the MAC destination to the given list. If the Ethernet frame has type
+.IR IPv4 " or " ARP ,
+then comparison with MAC/IP destination address pairs from the
+list is possible.
+.TP
+.BR "--among-src " "[!] \fIlist\fP"
+Compare the MAC source to the given list. If the Ethernet frame has type
+.IR IPv4 " or " ARP ,
+then comparison with MAC/IP source address pairs from the list
+is possible.
+.TP
+.BR "--among-dst-file " "[!] \fIfile\fP"
+Same as
+.BR --among-dst " but the list is read in from the specified file."
+.TP
+.BR "--among-src-file " "[!] \fIfile\fP"
+Same as
+.BR --among-src " but the list is read in from the specified file."
.SS arp
Specify (R)ARP fields. The protocol must be specified as
.IR ARP " or " RARP .
@@ -1108,8 +1108,8 @@ arp message and the hardware address length in the arp header is 6 bytes.
The version of ebtables this man page ships with does not support the
.B broute
table. Also there is no support for
-.BR among " and " string
-matches. And finally, this list is probably not complete.
+.B string
+match. And finally, this list is probably not complete.
.SH SEE ALSO
.BR xtables-nft "(8), " iptables "(8), " ip (8)
.PP
diff --git a/iptables/ip6tables-apply.8 b/iptables/ip6tables-apply.8
new file mode 100644
index 00000000..994b487a
--- /dev/null
+++ b/iptables/ip6tables-apply.8
@@ -0,0 +1 @@
+.so man8/iptables-apply.8
diff --git a/iptables/iptables-apply b/iptables/iptables-apply
index 819ca4a4..4683b1b4 100755
--- a/iptables/iptables-apply
+++ b/iptables/iptables-apply
@@ -1,174 +1,294 @@
#!/bin/bash
-#
# iptables-apply -- a safer way to update iptables remotely
#
-# Copyright © Martin F. Krafft <madduck@madduck.net>
+# Usage:
+# iptables-apply [-hV] [-t timeout] [-w savefile] {[rulesfile]|-c [runcmd]}
+#
+# Versions:
+# * 1.0 Copyright 2006 Martin F. Krafft <madduck@madduck.net>
+# Original version
+# * 1.1 Copyright 2010 GW <gw.2010@tnode.com or http://gw.tnode.com/>
+# Added parameter -c (run command)
+# Added parameter -w (save successfully applied rules to file)
+# Major code cleanup
+#
# Released under the terms of the Artistic Licence 2.0
#
set -eu
-PROGNAME="${0##*/}";
-VERSION=1.0
+PROGNAME="${0##*/}"
+VERSION=1.1
+
+
+### Default settings
+
+DEF_TIMEOUT=10
+
+MODE=0 # apply rulesfile mode
+# MODE=1 # run command mode
+
+case "$PROGNAME" in
+ (*6*)
+ SAVE=ip6tables-save
+ RESTORE=ip6tables-restore
+ DEF_RULESFILE="/etc/network/ip6tables.up.rules"
+ DEF_SAVEFILE="$DEF_RULESFILE"
+ DEF_RUNCMD="/etc/network/ip6tables.up.run"
+ ;;
+ (*)
+ SAVE=iptables-save
+ RESTORE=iptables-restore
+ DEF_RULESFILE="/etc/network/iptables.up.rules"
+ DEF_SAVEFILE="$DEF_RULESFILE"
+ DEF_RUNCMD="/etc/network/iptables.up.run"
+ ;;
+esac
+
-TIMEOUT=10
+### Functions
-function blurb()
-{
- cat <<-_eof
+function blurb() {
+ cat <<-__EOF__
$PROGNAME $VERSION -- a safer way to update iptables remotely
- _eof
+ __EOF__
}
-function copyright()
-{
- cat <<-_eof
- $PROGNAME is C Martin F. Krafft <madduck@madduck.net>.
+function copyright() {
+ cat <<-__EOF__
+ $PROGNAME has been published under the terms of the Artistic Licence 2.0.
- The program has been published under the terms of the Artistic Licence 2.0
- _eof
+ Original version - Copyright 2006 Martin F. Krafft <madduck@madduck.net>.
+ Version 1.1 - Copyright 2010 GW <gw.2010@tnode.com or http://gw.tnode.com/>.
+ __EOF__
}
-function about()
-{
+function about() {
blurb
echo
copyright
}
-function usage()
-{
- cat <<-_eof
- Usage: $PROGNAME [options] ruleset
+function usage() {
+ blurb
+ echo
+ cat <<-__EOF__
+ Usage:
+ $PROGNAME [-hV] [-t timeout] [-w savefile] {[rulesfile]|-c [runcmd]}
+
+ The script will try to apply a new rulesfile (as output by iptables-save,
+ read by iptables-restore) or run a command to configure iptables and then
+ prompt the user whether the changes are okay. If the new iptables rules cut
+ the existing connection, the user will not be able to answer affirmatively.
+ In this case, the script rolls back to the previous working iptables rules
+ after the timeout expires.
+
+ Successfully applied rules can also be written to savefile and later used
+ to roll back to this state. This can be used to implement a store last good
+ configuration mechanism when experimenting with an iptables setup script:
+ $PROGNAME -w $DEF_SAVEFILE -c $DEF_RUNCMD
- The script will try to apply a new ruleset (as output by iptables-save/read
- by iptables-restore) to iptables, then prompt the user whether the changes
- are okay. If the new ruleset cut the existing connection, the user will not
- be able to answer affirmatively. In this case, the script rolls back to the
- previous ruleset.
+ When called as ip6tables-apply, the script will use ip6tables-save/-restore
+ and IPv6 default values instead. Default value for rulesfile is
+ '$DEF_RULESFILE'.
+
+ Options:
+
+ -t seconds, --timeout seconds
+ Specify the timeout in seconds (default: $DEF_TIMEOUT).
+ -w savefile, --write savefile
+ Specify the savefile where successfully applied rules will be written to
+ (default if empty string is given: $DEF_SAVEFILE).
+ -c runcmd, --command runcmd
+ Run command runcmd to configure iptables instead of applying a rulesfile
+ (default: $DEF_RUNCMD).
+ -h, --help
+ Display this help text.
+ -V, --version
+ Display version information.
+
+ __EOF__
+}
- The following options may be specified, using standard conventions:
+function checkcommands() {
+ for cmd in "${COMMANDS[@]}"; do
+ if ! command -v "$cmd" >/dev/null; then
+ echo "Error: needed command not found: $cmd" >&2
+ exit 127
+ fi
+ done
+}
- -t | --timeout Specify the timeout in seconds (default: $TIMEOUT)
- -V | --version Display version information
- -h | --help Display this help text
- _eof
+function revertrules() {
+ echo -n "Reverting to old iptables rules... "
+ "$RESTORE" <"$TMPFILE"
+ echo "done."
}
-SHORTOPTS="t:Vh";
-LONGOPTS="timeout:,version,help";
+
+### Parsing and checking parameters
+
+TIMEOUT="$DEF_TIMEOUT"
+SAVEFILE=""
+
+SHORTOPTS="t:w:chV";
+LONGOPTS="timeout:,write:,command,help,version";
OPTS=$(getopt -s bash -o "$SHORTOPTS" -l "$LONGOPTS" -n "$PROGNAME" -- "$@") || exit $?
for opt in $OPTS; do
case "$opt" in
- (-*) unset OPT_STATE;;
+ (-*)
+ unset OPT_STATE
+ ;;
(*)
case "${OPT_STATE:-}" in
- (SET_TIMEOUT)
- eval TIMEOUT=$opt
- case "$TIMEOUT" in
- ([0-9]*) :;;
- (*)
- echo "E: non-numeric timeout value." >&2
- exit 1
- ;;
- esac
+ (SET_TIMEOUT) eval TIMEOUT=$opt;;
+ (SET_SAVEFILE)
+ eval SAVEFILE=$opt
+ [ -z "$SAVEFILE" ] && SAVEFILE="$DEF_SAVEFILE"
;;
esac
;;
esac
case "$opt" in
+ (-t|--timeout) OPT_STATE="SET_TIMEOUT";;
+ (-w|--write) OPT_STATE="SET_SAVEFILE";;
+ (-c|--command) MODE=1;;
(-h|--help) usage >&2; exit 0;;
(-V|--version) about >&2; exit 0;;
- (-t|--timeout) OPT_STATE=SET_TIMEOUT;;
(--) break;;
esac
shift
done
-case "$PROGNAME" in
- (*6*)
- SAVE=ip6tables-save
- RESTORE=ip6tables-restore
- DEFAULT_FILE=/etc/network/ip6tables
- ;;
- (*)
- SAVE=iptables-save
- RESTORE=iptables-restore
- DEFAULT_FILE=/etc/network/iptables
- ;;
-esac
-
-FILE="${1:-$DEFAULT_FILE}";
-
-if [[ -z "$FILE" ]]; then
- echo "E: missing file argument." >&2
+# Validate parameters
+if [ "$TIMEOUT" -ge 0 ] 2>/dev/null; then
+ TIMEOUT=$(($TIMEOUT))
+else
+ echo "Error: timeout must be a positive number" >&2
exit 1
fi
-if [[ ! -r "$FILE" ]]; then
- echo "E: cannot read $FILE" >&2
- exit 2
+if [ -n "$SAVEFILE" -a -e "$SAVEFILE" -a ! -w "$SAVEFILE" ]; then
+ echo "Error: savefile not writable: $SAVEFILE" >&2
+ exit 8
fi
-COMMANDS=(tempfile "$SAVE" "$RESTORE")
+case "$MODE" in
+ (1)
+ # Treat parameter as runcmd (run command mode)
+ RUNCMD="${1:-$DEF_RUNCMD}"
+ if [ ! -x "$RUNCMD" ]; then
+ echo "Error: runcmd not executable: $RUNCMD" >&2
+ exit 6
+ fi
+
+ # Needed commands
+ COMMANDS=(mktemp "$SAVE" "$RESTORE" "$RUNCMD")
+ checkcommands
+ ;;
+ (*)
+ # Treat parameter as rulesfile (apply rulesfile mode)
+ RULESFILE="${1:-$DEF_RULESFILE}";
+ if [ ! -r "$RULESFILE" ]; then
+ echo "Error: rulesfile not readable: $RULESFILE" >&2
+ exit 2
+ fi
+
+ # Needed commands
+ COMMANDS=(mktemp "$SAVE" "$RESTORE")
+ checkcommands
+ ;;
+esac
-for cmd in "${COMMANDS[@]}"; do
- if ! command -v $cmd >/dev/null; then
- echo "E: command not found: $cmd" >&2
- exit 127
- fi
-done
-umask 0700
+### Begin work
-TMPFILE=$(tempfile -p iptap)
+# Store old iptables rules to temporary file
+TMPFILE=`mktemp /tmp/$PROGNAME-XXXXXXXX`
trap "rm -f $TMPFILE" EXIT HUP INT QUIT ILL TRAP ABRT BUS \
FPE USR1 SEGV USR2 PIPE ALRM TERM
if ! "$SAVE" >"$TMPFILE"; then
+ # An error occured
if ! grep -q ipt /proc/modules 2>/dev/null; then
- echo "E: iptables support lacking from the kernel." >&2
+ echo "Error: iptables support lacking from the kernel" >&2
exit 3
else
- echo "E: unknown error saving current iptables ruleset." >&2
+ echo "Error: unknown error saving old iptables rules: $TMPFILE" >&2
exit 4
fi
fi
+# Legacy to stop the fail2ban daemon if present
[ -x /etc/init.d/fail2ban ] && /etc/init.d/fail2ban stop
-echo -n "Applying new ruleset... "
-if ! "$RESTORE" <"$FILE"; then
- echo "failed."
- echo "E: unknown error applying new iptables ruleset." >&2
- exit 5
-else
- echo "done."
-fi
+# Configure iptables
+case "$MODE" in
+ (1)
+ # Run command in background and kill it if it times out
+ echo -n "Running command '$RUNCMD'... "
+ "$RUNCMD" &
+ CMD_PID=$!
+ ( sleep "$TIMEOUT"; kill "$CMD_PID" 2>/dev/null; exit 0 ) &
+ CMDTIMEOUT_PID=$!
+ if ! wait "$CMD_PID"; then
+ echo "failed."
+ echo "Error: unknown error running command: $RUNCMD" >&2
+ revertrules
+ exit 7
+ else
+ echo "done."
+ fi
+ ;;
+ (*)
+ # Apply iptables rulesfile
+ echo -n "Applying new iptables rules from '$RULESFILE'... "
+ if ! "$RESTORE" <"$RULESFILE"; then
+ echo "failed."
+ echo "Error: unknown error applying new iptables rules: $RULESFILE" >&2
+ revertrules
+ exit 5
+ else
+ echo "done."
+ fi
+ ;;
+esac
+# Prompt user for confirmation
echo -n "Can you establish NEW connections to the machine? (y/N) "
-read -n1 -t "${TIMEOUT:-15}" ret 2>&1 || :
+read -n1 -t "$TIMEOUT" ret 2>&1 || :
case "${ret:-}" in
(y*|Y*)
+ # Success
echo
+
+ if [ ! -z "$SAVEFILE" ]; then
+ # Write successfully applied rules to the savefile
+ echo "Writing successfully applied rules to '$SAVEFILE'..."
+ if ! "$SAVE" >"$SAVEFILE"; then
+ echo "Error: unknown error writing successfully applied rules: $SAVEFILE" >&2
+ exit 9
+ fi
+ fi
+
echo "... then my job is done. See you next time."
;;
(*)
- if [[ -z "${ret:-}" ]]; then
- echo "apparently not..."
+ # Failed
+ echo
+ if [ -z "${ret:-}" ]; then
+ echo "Timeout! Something happened (or did not). Better play it safe..."
else
- echo
+ echo "No affirmative response! Better play it safe..."
fi
- echo "Timeout. Something happened (or did not). Better play it safe..."
- echo -n "Reverting to old ruleset... "
- "$RESTORE" <"$TMPFILE";
- echo "done."
+ revertrules
exit 255
;;
esac
+# Legacy to start the fail2ban daemon again
[ -x /etc/init.d/fail2ban ] && /etc/init.d/fail2ban start
exit 0
diff --git a/iptables/iptables-apply.8.in b/iptables/iptables-apply.8.in
index cdc9c447..f0ed4e5f 100644
--- a/iptables/iptables-apply.8.in
+++ b/iptables/iptables-apply.8.in
@@ -1,6 +1,6 @@
.\" Title: iptables-apply
-.\" Author: Martin F. Krafft
-.\" Date: Jun 04, 2006
+.\" Author: Martin F. Krafft, GW
+.\" Date: May 10, 2010
.\"
.TH IPTABLES\-APPLY 8 "" "@PACKAGE_STRING@" "@PACKAGE_STRING@"
.\" disable hyphenation
@@ -8,23 +8,37 @@
.SH NAME
iptables-apply \- a safer way to update iptables remotely
.SH SYNOPSIS
-\fBiptables\-apply\fP [\-\fBhV\fP] [\fB-t\fP \fItimeout\fP] \fIruleset\-file\fP
+\fBiptables\-apply\fP [\-\fBhV\fP] [\fB-t\fP \fItimeout\fP] [\fB-w\fP \fIsavefile\fP] {[\fIrulesfile]|-c [runcmd]}\fP
.SH "DESCRIPTION"
.PP
-iptables\-apply will try to apply a new ruleset (as output by
-iptables\-save/read by iptables\-restore) to iptables, then prompt the
-user whether the changes are okay. If the new ruleset cut the existing
-connection, the user will not be able to answer affirmatively. In this
-case, the script rolls back to the previous ruleset after the timeout
-expired. The timeout can be set with \fB\-t\fP.
+iptables\-apply will try to apply a new rulesfile (as output by
+iptables-save, read by iptables-restore) or run a command to configure
+iptables and then prompt the user whether the changes are okay. If the
+new iptables rules cut the existing connection, the user will not be
+able to answer affirmatively. In this case, the script rolls back to
+the previous working iptables rules after the timeout expires.
.PP
-When called as \fBip6tables\-apply\fP, the script will use
-ip6tables\-save/\-restore instead.
+Successfully applied rules can also be written to savefile and later used
+to roll back to this state. This can be used to implement a store last good
+configuration mechanism when experimenting with an iptables setup script:
+iptables-apply \-w /etc/network/iptables.up.rules \-c /etc/network/iptables.up.run
+.PP
+When called as ip6tables\-apply, the script will use
+ip6tables\-save/\-restore and IPv6 default values instead. Default
+value for rulesfile is '/etc/network/iptables.up.rules'.
.SH OPTIONS
.TP
\fB\-t\fP \fIseconds\fR, \fB\-\-timeout\fP \fIseconds\fR
-Sets the timeout after which the script will roll back to the previous
-ruleset.
+Sets the timeout in seconds after which the script will roll back
+to the previous ruleset (default: 10).
+.TP
+\fB\-w\fP \fIsavefile\fR, \fB\-\-write\fP \fIsavefile\fR
+Specify the savefile where successfully applied rules will be written to
+(default if empty string is given: /etc/network/iptables.up.rules).
+.TP
+\fB\-c\fP \fIruncmd\fR, \fB\-\-command\fP \fIruncmd\fR
+Run command runcmd to configure iptables instead of applying a rulesfile
+(default: /etc/network/iptables.up.run).
.TP
\fB\-h\fP, \fB\-\-help\fP
Display usage information.
@@ -36,9 +50,11 @@ Display version information.
\fBiptables-restore\fP(8), \fBiptables-save\fP(8), \fBiptables\fR(8).
.SH LEGALESE
.PP
-iptables\-apply is copyright by Martin F. Krafft.
+Original iptables-apply - Copyright 2006 Martin F. Krafft <madduck@madduck.net>.
+Version 1.1 - Copyright 2010 GW <gw.2010@tnode.com or http://gw.tnode.com/>.
.PP
-This manual page was written by Martin F. Krafft <madduck@madduck.net>
+This manual page was written by Martin F. Krafft <madduck@madduck.net> and
+extended by GW <gw.2010@tnode.com or http://gw.tnode.com/>.
.PP
Permission is granted to copy, distribute and/or modify this document
under the terms of the Artistic License 2.0.
diff --git a/iptables/iptables-restore.8.in b/iptables/iptables-restore.8.in
index f751492d..b4b62f92 100644
--- a/iptables/iptables-restore.8.in
+++ b/iptables/iptables-restore.8.in
@@ -87,7 +87,7 @@ from Rusty Russell.
.br
Andras Kis-Szabo <kisza@sch.bme.hu> contributed ip6tables-restore.
.SH SEE ALSO
-\fBiptables\-save\fP(8), \fBiptables\fP(8)
+\fBiptables\-apply\fP(8),\fBiptables\-save\fP(8), \fBiptables\fP(8)
.PP
The iptables-HOWTO, which details more iptables usage, the NAT-HOWTO,
which details NAT, and the netfilter-hacking-HOWTO which details the
diff --git a/iptables/iptables-save.8.in b/iptables/iptables-save.8.in
index 29ef2829..7683fd37 100644
--- a/iptables/iptables-save.8.in
+++ b/iptables/iptables-save.8.in
@@ -62,7 +62,7 @@ Rusty Russell <rusty@rustcorp.com.au>
.br
Andras Kis-Szabo <kisza@sch.bme.hu> contributed ip6tables-save.
.SH SEE ALSO
-\fBiptables\-restore\fP(8), \fBiptables\fP(8)
+\fBiptables\-apply\fP(8),\fBiptables\-restore\fP(8), \fBiptables\fP(8)
.PP
The iptables-HOWTO, which details more iptables usage, the NAT-HOWTO,
which details NAT, and the netfilter-hacking-HOWTO which details the
diff --git a/iptables/iptables.8.in b/iptables/iptables.8.in
index 78df8f08..054564b3 100644
--- a/iptables/iptables.8.in
+++ b/iptables/iptables.8.in
@@ -245,13 +245,13 @@ add, delete, insert, replace and append commands).
This option has no effect in iptables and iptables-restore.
If a rule using the \fB\-4\fP option is inserted with (and only with)
ip6tables-restore, it will be silently ignored. Any other uses will throw an
-error. This option allows to put both IPv4 and IPv6 rules in a single rule file
+error. This option allows IPv4 and IPv6 rules in a single rule file
for use with both iptables-restore and ip6tables-restore.
.TP
\fB\-6\fP, \fB\-\-ipv6\fP
If a rule using the \fB\-6\fP option is inserted with (and only with)
iptables-restore, it will be silently ignored. Any other uses will throw an
-error. This option allows to put both IPv4 and IPv6 rules in a single rule file
+error. This option allows IPv4 and IPv6 rules in a single rule file
for use with both iptables-restore and ip6tables-restore.
This option has no effect in ip6tables and ip6tables-restore.
.TP
diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c
index 7068f82c..d4a86610 100644
--- a/iptables/nft-arp.c
+++ b/iptables/nft-arp.c
@@ -126,7 +126,7 @@ static bool need_devaddr(struct arpt_devaddr_info *info)
return false;
}
-static int nft_arp_add(struct nftnl_rule *r, void *data)
+static int nft_arp_add(struct nft_handle *h, struct nftnl_rule *r, void *data)
{
struct iptables_command_state *cs = data;
struct arpt_entry *fw = &cs->arp;
@@ -582,14 +582,15 @@ nft_arp_save_rule(const void *data, unsigned int format)
}
static void
-nft_arp_print_rule(struct nftnl_rule *r, unsigned int num, unsigned int format)
+nft_arp_print_rule(struct nft_handle *h, struct nftnl_rule *r,
+ unsigned int num, unsigned int format)
{
struct iptables_command_state cs = {};
if (format & FMT_LINENUMBERS)
printf("%u ", num);
- nft_rule_to_iptables_command_state(r, &cs);
+ nft_rule_to_iptables_command_state(h, r, &cs);
nft_arp_print_rule_details(&cs, format);
print_matches_and_target(&cs, format);
@@ -632,7 +633,7 @@ static bool nft_arp_is_same(const void *data_a,
(unsigned char *)b->arp.outiface_mask);
}
-static bool nft_arp_rule_find(struct nft_family_ops *ops, struct nftnl_rule *r,
+static bool nft_arp_rule_find(struct nft_handle *h, struct nftnl_rule *r,
void *data)
{
const struct iptables_command_state *cs = data;
@@ -640,7 +641,7 @@ static bool nft_arp_rule_find(struct nft_family_ops *ops, struct nftnl_rule *r,
bool ret = false;
/* Delete by matching rule case */
- nft_rule_to_iptables_command_state(r, &this);
+ nft_rule_to_iptables_command_state(h, r, &this);
if (!nft_arp_is_same(&cs->arp, &this.arp))
goto out;
@@ -653,7 +654,7 @@ static bool nft_arp_rule_find(struct nft_family_ops *ops, struct nftnl_rule *r,
ret = true;
out:
- ops->clear_cs(&this);
+ h->ops->clear_cs(&this);
return ret;
}
diff --git a/iptables/nft-bridge.c b/iptables/nft-bridge.c
index 2e4b309b..3f85cbbf 100644
--- a/iptables/nft-bridge.c
+++ b/iptables/nft-bridge.c
@@ -17,8 +17,11 @@
#include <libiptc/libxtc.h>
#include <linux/netfilter/nf_tables.h>
+#include <libnftnl/set.h>
+
#include "nft-shared.h"
#include "nft-bridge.h"
+#include "nft-cache.h"
#include "nft.h"
void ebt_cs_clean(struct iptables_command_state *cs)
@@ -126,7 +129,8 @@ static int _add_action(struct nftnl_rule *r, struct iptables_command_state *cs)
return add_action(r, cs, false);
}
-static int nft_bridge_add(struct nftnl_rule *r, void *data)
+static int nft_bridge_add(struct nft_handle *h,
+ struct nftnl_rule *r, void *data)
{
struct iptables_command_state *cs = data;
struct ebt_match *iter;
@@ -182,7 +186,7 @@ static int nft_bridge_add(struct nftnl_rule *r, void *data)
for (iter = cs->match_list; iter; iter = iter->next) {
if (iter->ismatch) {
- if (add_match(r, iter->u.match->m))
+ if (add_match(h, r, iter->u.match->m))
break;
} else {
if (add_target(r, iter->u.watcher->t))
@@ -290,6 +294,212 @@ static void nft_bridge_parse_immediate(const char *jumpto, bool nft_goto,
cs->jumpto = jumpto;
}
+/* return 0 if saddr, 1 if daddr, -1 on error */
+static int
+lookup_check_ether_payload(uint32_t base, uint32_t offset, uint32_t len)
+{
+ if (base != 0 || len != ETH_ALEN)
+ return -1;
+
+ switch (offset) {
+ case offsetof(struct ether_header, ether_dhost):
+ return 1;
+ case offsetof(struct ether_header, ether_shost):
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+/* return 0 if saddr, 1 if daddr, -1 on error */
+static int
+lookup_check_iphdr_payload(uint32_t base, uint32_t offset, uint32_t len)
+{
+ if (base != 1 || len != 4)
+ return -1;
+
+ switch (offset) {
+ case offsetof(struct iphdr, daddr):
+ return 1;
+ case offsetof(struct iphdr, saddr):
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+/* Make sure previous payload expression(s) is/are consistent and extract if
+ * matching on source or destination address and if matching on MAC and IP or
+ * only MAC address. */
+static int lookup_analyze_payloads(const struct nft_xt_ctx *ctx,
+ bool *dst, bool *ip)
+{
+ int val, val2 = -1;
+
+ if (ctx->flags & NFT_XT_CTX_PREV_PAYLOAD) {
+ val = lookup_check_ether_payload(ctx->prev_payload.base,
+ ctx->prev_payload.offset,
+ ctx->prev_payload.len);
+ if (val < 0) {
+ DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
+ ctx->prev_payload.base, ctx->prev_payload.offset,
+ ctx->prev_payload.len);
+ return -1;
+ }
+ if (!(ctx->flags & NFT_XT_CTX_PAYLOAD)) {
+ DEBUGP("Previous but no current payload?\n");
+ return -1;
+ }
+ val2 = lookup_check_iphdr_payload(ctx->payload.base,
+ ctx->payload.offset,
+ ctx->payload.len);
+ if (val2 < 0) {
+ DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
+ ctx->payload.base, ctx->payload.offset,
+ ctx->payload.len);
+ return -1;
+ } else if (val != val2) {
+ DEBUGP("mismatching payload match offsets\n");
+ return -1;
+ }
+ } else if (ctx->flags & NFT_XT_CTX_PAYLOAD) {
+ val = lookup_check_ether_payload(ctx->payload.base,
+ ctx->payload.offset,
+ ctx->payload.len);
+ if (val < 0) {
+ DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
+ ctx->payload.base, ctx->payload.offset,
+ ctx->payload.len);
+ return -1;
+ }
+ } else {
+ DEBUGP("unknown LHS of lookup expression\n");
+ return -1;
+ }
+
+ if (dst)
+ *dst = (val == 1);
+ if (ip)
+ *ip = (val2 != -1);
+ return 0;
+}
+
+static int set_elems_to_among_pairs(struct nft_among_pair *pairs,
+ const struct nftnl_set *s, int cnt)
+{
+ struct nftnl_set_elems_iter *iter = nftnl_set_elems_iter_create(s);
+ struct nftnl_set_elem *elem;
+ size_t tmpcnt = 0;
+ const void *data;
+ uint32_t datalen;
+ int ret = -1;
+
+ if (!iter) {
+ fprintf(stderr, "BUG: set elems iter allocation failed\n");
+ return ret;
+ }
+
+ while ((elem = nftnl_set_elems_iter_next(iter))) {
+ data = nftnl_set_elem_get(elem, NFTNL_SET_ELEM_KEY, &datalen);
+ if (!data) {
+ fprintf(stderr, "BUG: set elem without key\n");
+ goto err;
+ }
+ if (datalen > sizeof(*pairs)) {
+ fprintf(stderr, "BUG: overlong set elem\n");
+ goto err;
+ }
+ nft_among_insert_pair(pairs, &tmpcnt, data);
+ }
+ ret = 0;
+err:
+ nftnl_set_elems_iter_destroy(iter);
+ return ret;
+}
+
+static struct nftnl_set *set_from_lookup_expr(struct nft_xt_ctx *ctx,
+ const struct nftnl_expr *e)
+{
+ const char *set_name = nftnl_expr_get_str(e, NFTNL_EXPR_LOOKUP_SET);
+ struct nftnl_set_list *slist;
+
+ slist = nft_set_list_get(ctx->h, ctx->table, set_name);
+ if (slist)
+ return nftnl_set_list_lookup_byname(slist, set_name);
+
+ return NULL;
+}
+
+static void nft_bridge_parse_lookup(struct nft_xt_ctx *ctx,
+ struct nftnl_expr *e, void *data)
+{
+ struct xtables_match *match = NULL;
+ struct nft_among_data *among_data;
+ bool is_dst, have_ip, inv;
+ struct ebt_match *ematch;
+ struct nftnl_set *s;
+ size_t poff, size;
+ uint32_t cnt;
+
+ if (lookup_analyze_payloads(ctx, &is_dst, &have_ip))
+ return;
+
+ s = set_from_lookup_expr(ctx, e);
+ if (!s)
+ xtables_error(OTHER_PROBLEM,
+ "BUG: lookup expression references unknown set");
+
+ cnt = nftnl_set_get_u32(s, NFTNL_SET_DESC_SIZE);
+
+ for (ematch = ctx->cs->match_list; ematch; ematch = ematch->next) {
+ if (!ematch->ismatch || strcmp(ematch->u.match->name, "among"))
+ continue;
+
+ match = ematch->u.match;
+ among_data = (struct nft_among_data *)match->m->data;
+
+ size = cnt + among_data->src.cnt + among_data->dst.cnt;
+ size *= sizeof(struct nft_among_pair);
+
+ size += XT_ALIGN(sizeof(struct xt_entry_match)) +
+ sizeof(struct nft_among_data);
+
+ match->m = xtables_realloc(match->m, size);
+ break;
+ }
+ if (!match) {
+ match = xtables_find_match("among", XTF_TRY_LOAD,
+ &ctx->cs->matches);
+
+ size = cnt * sizeof(struct nft_among_pair);
+ size += XT_ALIGN(sizeof(struct xt_entry_match)) +
+ sizeof(struct nft_among_data);
+
+ match->m = xtables_calloc(1, size);
+ strcpy(match->m->u.user.name, match->name);
+ match->m->u.user.revision = match->revision;
+ xs_init_match(match);
+
+ if (ctx->h->ops->parse_match != NULL)
+ ctx->h->ops->parse_match(match, ctx->cs);
+ }
+ if (!match)
+ return;
+
+ match->m->u.match_size = size;
+
+ inv = !!(nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_FLAGS) &
+ NFT_LOOKUP_F_INV);
+
+ among_data = (struct nft_among_data *)match->m->data;
+ poff = nft_among_prepare_data(among_data, is_dst, cnt, inv, have_ip);
+ if (set_elems_to_among_pairs(among_data->pairs + poff, s, cnt))
+ xtables_error(OTHER_PROBLEM,
+ "ebtables among pair parsing failed");
+
+ ctx->flags &= ~(NFT_XT_CTX_PAYLOAD | NFT_XT_CTX_PREV_PAYLOAD);
+}
+
static void parse_watcher(void *object, struct ebt_match **match_list,
bool ismatch)
{
@@ -332,11 +542,12 @@ static void nft_bridge_parse_target(struct xtables_target *t, void *data)
cs->target = t;
}
-static void nft_rule_to_ebtables_command_state(const struct nftnl_rule *r,
+static void nft_rule_to_ebtables_command_state(struct nft_handle *h,
+ const struct nftnl_rule *r,
struct iptables_command_state *cs)
{
cs->eb.bitmask = EBT_NOPROTO;
- nft_rule_to_iptables_command_state(r, cs);
+ nft_rule_to_iptables_command_state(h, r, cs);
}
static void print_iface(const char *option, const char *name, bool invert)
@@ -471,15 +682,15 @@ static void nft_bridge_save_rule(const void *data, unsigned int format)
fputc('\n', stdout);
}
-static void nft_bridge_print_rule(struct nftnl_rule *r, unsigned int num,
- unsigned int format)
+static void nft_bridge_print_rule(struct nft_handle *h, struct nftnl_rule *r,
+ unsigned int num, unsigned int format)
{
struct iptables_command_state cs = {};
if (format & FMT_LINENUMBERS)
printf("%d ", num);
- nft_rule_to_ebtables_command_state(r, &cs);
+ nft_rule_to_ebtables_command_state(h, r, &cs);
nft_bridge_save_rule(&cs, format);
ebt_cs_clean(&cs);
}
@@ -536,14 +747,14 @@ static bool nft_bridge_is_same(const void *data_a, const void *data_b)
return strcmp(a->in, b->in) == 0 && strcmp(a->out, b->out) == 0;
}
-static bool nft_bridge_rule_find(struct nft_family_ops *ops, struct nftnl_rule *r,
+static bool nft_bridge_rule_find(struct nft_handle *h, struct nftnl_rule *r,
void *data)
{
struct iptables_command_state *cs = data;
struct iptables_command_state this = {};
bool ret = false;
- nft_rule_to_ebtables_command_state(r, &this);
+ nft_rule_to_ebtables_command_state(h, r, &this);
DEBUGP("comparing with... ");
@@ -567,7 +778,7 @@ static bool nft_bridge_rule_find(struct nft_family_ops *ops, struct nftnl_rule *
ret = true;
out:
- ops->clear_cs(&this);
+ h->ops->clear_cs(&this);
return ret;
}
@@ -740,6 +951,7 @@ struct nft_family_ops nft_family_ops_bridge = {
.parse_meta = nft_bridge_parse_meta,
.parse_payload = nft_bridge_parse_payload,
.parse_immediate = nft_bridge_parse_immediate,
+ .parse_lookup = nft_bridge_parse_lookup,
.parse_match = nft_bridge_parse_match,
.parse_target = nft_bridge_parse_target,
.print_table_header = nft_bridge_print_table_header,
diff --git a/iptables/nft-bridge.h b/iptables/nft-bridge.h
index d90066f1..eb1b3928 100644
--- a/iptables/nft-bridge.h
+++ b/iptables/nft-bridge.h
@@ -122,4 +122,60 @@ void ebt_add_watcher(struct xtables_target *watcher,
struct iptables_command_state *cs);
int ebt_command_default(struct iptables_command_state *cs);
+struct nft_among_pair {
+ struct ether_addr ether;
+ struct in_addr in __attribute__((aligned (4)));
+};
+
+struct nft_among_data {
+ struct {
+ size_t cnt;
+ bool inv;
+ bool ip;
+ } src, dst;
+ /* first source, then dest pairs */
+ struct nft_among_pair pairs[0];
+};
+
+/* initialize fields, return offset into pairs array to write pairs to */
+static inline size_t
+nft_among_prepare_data(struct nft_among_data *data, bool dst,
+ size_t cnt, bool inv, bool ip)
+{
+ size_t poff;
+
+ if (dst) {
+ data->dst.cnt = cnt;
+ data->dst.inv = inv;
+ data->dst.ip = ip;
+ poff = data->src.cnt;
+ } else {
+ data->src.cnt = cnt;
+ data->src.inv = inv;
+ data->src.ip = ip;
+ poff = 0;
+ memmove(data->pairs + cnt, data->pairs,
+ data->dst.cnt * sizeof(*data->pairs));
+ }
+ return poff;
+}
+
+static inline void
+nft_among_insert_pair(struct nft_among_pair *pairs,
+ size_t *pcount, const struct nft_among_pair *new)
+{
+ int i;
+
+ /* nftables automatically sorts set elements from smallest to largest,
+ * insert sorted so extension comparison works */
+
+ for (i = 0; i < *pcount; i++) {
+ if (memcmp(new, &pairs[i], sizeof(*new)) < 0)
+ break;
+ }
+ memmove(&pairs[i + 1], &pairs[i], sizeof(*pairs) * (*pcount - i));
+ memcpy(&pairs[i], new, sizeof(*new));
+ (*pcount)++;
+}
+
#endif
diff --git a/iptables/nft-cache.c b/iptables/nft-cache.c
index c55970d0..a0c76705 100644
--- a/iptables/nft-cache.c
+++ b/iptables/nft-cache.c
@@ -18,6 +18,7 @@
#include <libmnl/libmnl.h>
#include <libnftnl/gen.h>
+#include <libnftnl/set.h>
#include <libnftnl/table.h>
#include "nft.h"
@@ -106,6 +107,23 @@ static int fetch_table_cache(struct nft_handle *h)
return 1;
}
+static int init_chain_cache(struct nft_handle *h)
+{
+ int i;
+
+ for (i = 0; i < NFT_TABLE_MAX; i++) {
+ enum nft_table_type type = h->tables[i].type;
+
+ if (!h->tables[i].name)
+ continue;
+
+ h->cache->table[type].chains = nftnl_chain_list_alloc();
+ if (!h->cache->table[type].chains)
+ return -1;
+ }
+ return 0;
+}
+
struct nftnl_chain_list_cb_data {
struct nft_handle *h;
const struct builtin_table *t;
@@ -152,16 +170,94 @@ err:
return MNL_CB_OK;
}
-static int fetch_chain_cache(struct nft_handle *h,
- const struct builtin_table *t,
- const char *chain)
+struct nftnl_set_list_cb_data {
+ struct nft_handle *h;
+ const struct builtin_table *t;
+};
+
+static int nftnl_set_list_cb(const struct nlmsghdr *nlh, void *data)
{
- struct nftnl_chain_list_cb_data d = {
+ struct nftnl_set_list_cb_data *d = data;
+ const struct builtin_table *t = d->t;
+ struct nftnl_set_list *list;
+ struct nft_handle *h = d->h;
+ const char *tname, *sname;
+ struct nftnl_set *s;
+
+ s = nftnl_set_alloc();
+ if (s == NULL)
+ return MNL_CB_OK;
+
+ if (nftnl_set_nlmsg_parse(nlh, s) < 0)
+ goto out_free;
+
+ tname = nftnl_set_get_str(s, NFTNL_SET_TABLE);
+
+ if (!t)
+ t = nft_table_builtin_find(h, tname);
+ else if (strcmp(t->name, tname))
+ goto out_free;
+
+ if (!t)
+ goto out_free;
+
+ list = h->cache->table[t->type].sets;
+ sname = nftnl_set_get_str(s, NFTNL_SET_NAME);
+
+ if (nftnl_set_list_lookup_byname(list, sname))
+ goto out_free;
+
+ nftnl_set_list_add_tail(s, list);
+
+ return MNL_CB_OK;
+out_free:
+ nftnl_set_free(s);
+ return MNL_CB_OK;
+}
+
+static int set_elem_cb(const struct nlmsghdr *nlh, void *data)
+{
+ return nftnl_set_elems_nlmsg_parse(nlh, data) ? -1 : MNL_CB_OK;
+}
+
+static bool set_has_elements(struct nftnl_set *s)
+{
+ struct nftnl_set_elems_iter *iter;
+ bool ret = false;
+
+ iter = nftnl_set_elems_iter_create(s);
+ if (iter) {
+ ret = !!nftnl_set_elems_iter_cur(iter);
+ nftnl_set_elems_iter_destroy(iter);
+ }
+ return ret;
+}
+
+static int set_fetch_elem_cb(struct nftnl_set *s, void *data)
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nft_handle *h = data;
+ struct nlmsghdr *nlh;
+
+ if (set_has_elements(s))
+ return 0;
+
+ nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETSETELEM, h->family,
+ NLM_F_DUMP, h->seq);
+ nftnl_set_elems_nlmsg_build_payload(nlh, s);
+
+ return mnl_talk(h, nlh, set_elem_cb, s);
+}
+
+static int fetch_set_cache(struct nft_handle *h,
+ const struct builtin_table *t, const char *set)
+{
+ struct nftnl_set_list_cb_data d = {
.h = h,
.t = t,
};
- char buf[16536];
struct nlmsghdr *nlh;
+ char buf[16536];
int i, ret;
if (!t) {
@@ -171,19 +267,72 @@ static int fetch_chain_cache(struct nft_handle *h,
if (!h->tables[i].name)
continue;
- if (h->cache->table[type].chains)
- continue;
-
- h->cache->table[type].chains = nftnl_chain_list_alloc();
- if (!h->cache->table[type].chains)
+ h->cache->table[type].sets = nftnl_set_list_alloc();
+ if (!h->cache->table[type].sets)
return -1;
}
- } else if (!h->cache->table[t->type].chains) {
- h->cache->table[t->type].chains = nftnl_chain_list_alloc();
- if (!h->cache->table[t->type].chains)
+ } else if (!h->cache->table[t->type].sets) {
+ h->cache->table[t->type].sets = nftnl_set_list_alloc();
+ }
+
+ if (t && set) {
+ struct nftnl_set *s = nftnl_set_alloc();
+
+ if (!s)
return -1;
+
+ nlh = nftnl_set_nlmsg_build_hdr(buf, NFT_MSG_GETSET, h->family,
+ NLM_F_ACK, h->seq);
+ nftnl_set_set_str(s, NFTNL_SET_TABLE, t->name);
+ nftnl_set_set_str(s, NFTNL_SET_NAME, set);
+ nftnl_set_nlmsg_build_payload(nlh, s);
+ nftnl_set_free(s);
+ } else {
+ nlh = nftnl_set_nlmsg_build_hdr(buf, NFT_MSG_GETSET, h->family,
+ NLM_F_DUMP, h->seq);
}
+ ret = mnl_talk(h, nlh, nftnl_set_list_cb, &d);
+ if (ret < 0 && errno == EINTR) {
+ assert(nft_restart(h) >= 0);
+ return ret;
+ }
+
+ if (t && set) {
+ struct nftnl_set *s;
+
+ s = nftnl_set_list_lookup_byname(h->cache->table[t->type].sets,
+ set);
+ set_fetch_elem_cb(s, h);
+ } else if (t) {
+ nftnl_set_list_foreach(h->cache->table[t->type].sets,
+ set_fetch_elem_cb, h);
+ } else {
+ for (i = 0; i < NFT_TABLE_MAX; i++) {
+ enum nft_table_type type = h->tables[i].type;
+
+ if (!h->tables[i].name)
+ continue;
+
+ nftnl_set_list_foreach(h->cache->table[type].sets,
+ set_fetch_elem_cb, h);
+ }
+ }
+ return ret;
+}
+
+static int fetch_chain_cache(struct nft_handle *h,
+ const struct builtin_table *t,
+ const char *chain)
+{
+ struct nftnl_chain_list_cb_data d = {
+ .h = h,
+ .t = t,
+ };
+ char buf[16536];
+ struct nlmsghdr *nlh;
+ int ret;
+
if (t && chain) {
struct nftnl_chain *c = nftnl_chain_alloc();
@@ -297,21 +446,19 @@ static int fetch_rule_cache(struct nft_handle *h,
static void
__nft_build_cache(struct nft_handle *h, enum nft_cache_level level,
- const struct builtin_table *t, const char *chain)
+ const struct builtin_table *t, const char *set,
+ const char *chain)
{
- uint32_t genid_start, genid_stop;
-
if (level <= h->cache_level)
return;
-retry:
- mnl_genid_get(h, &genid_start);
- if (h->cache_level && genid_start != h->nft_genid)
- flush_chain_cache(h, NULL);
+ if (!h->nft_genid)
+ mnl_genid_get(h, &h->nft_genid);
switch (h->cache_level) {
case NFT_CL_NONE:
fetch_table_cache(h);
+ init_chain_cache(h);
if (level == NFT_CL_TABLES)
break;
/* fall through */
@@ -321,26 +468,24 @@ retry:
break;
/* fall through */
case NFT_CL_CHAINS:
+ fetch_set_cache(h, t, set);
+ if (level == NFT_CL_SETS)
+ break;
+ /* fall through */
+ case NFT_CL_SETS:
fetch_rule_cache(h, t, chain);
if (level == NFT_CL_RULES)
break;
/* fall through */
case NFT_CL_RULES:
+ case NFT_CL_FAKE:
break;
}
- mnl_genid_get(h, &genid_stop);
- if (genid_start != genid_stop) {
- flush_chain_cache(h, NULL);
- goto retry;
- }
-
if (!t && !chain)
h->cache_level = level;
else if (h->cache_level < NFT_CL_TABLES)
h->cache_level = NFT_CL_TABLES;
-
- h->nft_genid = genid_start;
}
void nft_build_cache(struct nft_handle *h, struct nftnl_chain *c)
@@ -349,28 +494,20 @@ void nft_build_cache(struct nft_handle *h, struct nftnl_chain *c)
const char *table, *chain;
if (!c)
- return __nft_build_cache(h, NFT_CL_RULES, NULL, NULL);
+ return __nft_build_cache(h, NFT_CL_RULES, NULL, NULL, NULL);
table = nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
t = nft_table_builtin_find(h, table);
- __nft_build_cache(h, NFT_CL_RULES, t, chain);
+ __nft_build_cache(h, NFT_CL_RULES, t, NULL, chain);
}
void nft_fake_cache(struct nft_handle *h)
{
- int i;
-
fetch_table_cache(h);
- for (i = 0; i < NFT_TABLE_MAX; i++) {
- enum nft_table_type type = h->tables[i].type;
+ init_chain_cache(h);
- if (!h->tables[i].name)
- continue;
-
- h->cache->table[type].chains = nftnl_chain_list_alloc();
- }
- h->cache_level = NFT_CL_RULES;
+ h->cache_level = NFT_CL_FAKE;
mnl_genid_get(h, &h->nft_genid);
}
@@ -421,6 +558,14 @@ static int __flush_chain_cache(struct nftnl_chain *c, void *data)
return 0;
}
+static int __flush_set_cache(struct nftnl_set *s, void *data)
+{
+ nftnl_set_list_del(s);
+ nftnl_set_free(s);
+
+ return 0;
+}
+
static int flush_cache(struct nft_handle *h, struct nft_cache *c,
const char *tablename)
{
@@ -429,10 +574,14 @@ static int flush_cache(struct nft_handle *h, struct nft_cache *c,
if (tablename) {
table = nft_table_builtin_find(h, tablename);
- if (!table || !c->table[table->type].chains)
+ if (!table)
return 0;
- nftnl_chain_list_foreach(c->table[table->type].chains,
- __flush_chain_cache, NULL);
+ if (c->table[table->type].chains)
+ nftnl_chain_list_foreach(c->table[table->type].chains,
+ __flush_chain_cache, NULL);
+ if (c->table[table->type].sets)
+ nftnl_set_list_foreach(c->table[table->type].sets,
+ __flush_set_cache, NULL);
return 0;
}
@@ -440,14 +589,19 @@ static int flush_cache(struct nft_handle *h, struct nft_cache *c,
if (h->tables[i].name == NULL)
continue;
- if (!c->table[i].chains)
- continue;
-
- nftnl_chain_list_free(c->table[i].chains);
- c->table[i].chains = NULL;
+ if (c->table[i].chains) {
+ nftnl_chain_list_free(c->table[i].chains);
+ c->table[i].chains = NULL;
+ }
+ if (c->table[i].sets) {
+ nftnl_set_list_free(c->table[i].sets);
+ c->table[i].sets = NULL;
+ }
+ }
+ if (c->tables) {
+ nftnl_table_list_free(c->tables);
+ c->tables = NULL;
}
- nftnl_table_list_free(c->tables);
- c->tables = NULL;
return 1;
}
@@ -468,23 +622,47 @@ void nft_rebuild_cache(struct nft_handle *h)
if (h->cache_level)
__nft_flush_cache(h);
- h->cache_level = NFT_CL_NONE;
- __nft_build_cache(h, level, NULL, NULL);
+ if (h->cache_level == NFT_CL_FAKE) {
+ nft_fake_cache(h);
+ } else {
+ h->cache_level = NFT_CL_NONE;
+ __nft_build_cache(h, level, NULL, NULL, NULL);
+ }
}
void nft_release_cache(struct nft_handle *h)
{
- if (h->cache_index)
- flush_cache(h, &h->__cache[0], NULL);
+ if (!h->cache_index)
+ return;
+
+ flush_cache(h, &h->__cache[0], NULL);
+ memcpy(&h->__cache[0], &h->__cache[1], sizeof(h->__cache[0]));
+ memset(&h->__cache[1], 0, sizeof(h->__cache[1]));
+ h->cache_index = 0;
+ h->cache = &h->__cache[0];
}
struct nftnl_table_list *nftnl_table_list_get(struct nft_handle *h)
{
- __nft_build_cache(h, NFT_CL_TABLES, NULL, NULL);
+ __nft_build_cache(h, NFT_CL_TABLES, NULL, NULL, NULL);
return h->cache->tables;
}
+struct nftnl_set_list *
+nft_set_list_get(struct nft_handle *h, const char *table, const char *set)
+{
+ const struct builtin_table *t;
+
+ t = nft_table_builtin_find(h, table);
+ if (!t)
+ return NULL;
+
+ __nft_build_cache(h, NFT_CL_RULES, t, set, NULL);
+
+ return h->cache->table[t->type].sets;
+}
+
struct nftnl_chain_list *
nft_chain_list_get(struct nft_handle *h, const char *table, const char *chain)
{
@@ -494,7 +672,7 @@ nft_chain_list_get(struct nft_handle *h, const char *table, const char *chain)
if (!t)
return NULL;
- __nft_build_cache(h, NFT_CL_CHAINS, t, chain);
+ __nft_build_cache(h, NFT_CL_CHAINS, t, NULL, chain);
return h->cache->table[t->type].chains;
}
diff --git a/iptables/nft-cache.h b/iptables/nft-cache.h
index cb7a7688..ed498835 100644
--- a/iptables/nft-cache.h
+++ b/iptables/nft-cache.h
@@ -13,6 +13,8 @@ int flush_rule_cache(struct nft_handle *h, const char *table,
struct nftnl_chain_list *
nft_chain_list_get(struct nft_handle *h, const char *table, const char *chain);
+struct nftnl_set_list *
+nft_set_list_get(struct nft_handle *h, const char *table, const char *set);
struct nftnl_table_list *nftnl_table_list_get(struct nft_handle *h);
#endif /* _NFT_CACHE_H_ */
diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c
index 4497eb9b..70634f8f 100644
--- a/iptables/nft-ipv4.c
+++ b/iptables/nft-ipv4.c
@@ -26,7 +26,7 @@
#include "nft.h"
#include "nft-shared.h"
-static int nft_ipv4_add(struct nftnl_rule *r, void *data)
+static int nft_ipv4_add(struct nft_handle *h, struct nftnl_rule *r, void *data)
{
struct iptables_command_state *cs = data;
struct xtables_rule_match *matchp;
@@ -77,7 +77,7 @@ static int nft_ipv4_add(struct nftnl_rule *r, void *data)
add_compat(r, cs->fw.ip.proto, cs->fw.ip.invflags & XT_INV_PROTO);
for (matchp = cs->matches; matchp; matchp = matchp->next) {
- ret = add_match(r, matchp->match->m);
+ ret = add_match(h, r, matchp->match->m);
if (ret < 0)
return ret;
}
@@ -261,12 +261,12 @@ static void print_fragment(unsigned int flags, unsigned int invflags,
fputc(' ', stdout);
}
-static void nft_ipv4_print_rule(struct nftnl_rule *r, unsigned int num,
- unsigned int format)
+static void nft_ipv4_print_rule(struct nft_handle *h, struct nftnl_rule *r,
+ unsigned int num, unsigned int format)
{
struct iptables_command_state cs = {};
- nft_rule_to_iptables_command_state(r, &cs);
+ nft_rule_to_iptables_command_state(h, r, &cs);
print_rule_details(&cs, cs.jumpto, cs.fw.ip.flags,
cs.fw.ip.invflags, cs.fw.ip.proto, num, format);
diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c
index cacb1c9e..d01491bf 100644
--- a/iptables/nft-ipv6.c
+++ b/iptables/nft-ipv6.c
@@ -25,7 +25,7 @@
#include "nft.h"
#include "nft-shared.h"
-static int nft_ipv6_add(struct nftnl_rule *r, void *data)
+static int nft_ipv6_add(struct nft_handle *h, struct nftnl_rule *r, void *data)
{
struct iptables_command_state *cs = data;
struct xtables_rule_match *matchp;
@@ -66,7 +66,7 @@ static int nft_ipv6_add(struct nftnl_rule *r, void *data)
add_compat(r, cs->fw6.ipv6.proto, cs->fw6.ipv6.invflags & XT_INV_PROTO);
for (matchp = cs->matches; matchp; matchp = matchp->next) {
- ret = add_match(r, matchp->match->m);
+ ret = add_match(h, r, matchp->match->m);
if (ret < 0)
return ret;
}
@@ -187,12 +187,12 @@ static void nft_ipv6_parse_immediate(const char *jumpto, bool nft_goto,
cs->fw6.ipv6.flags |= IP6T_F_GOTO;
}
-static void nft_ipv6_print_rule(struct nftnl_rule *r, unsigned int num,
- unsigned int format)
+static void nft_ipv6_print_rule(struct nft_handle *h, struct nftnl_rule *r,
+ unsigned int num, unsigned int format)
{
struct iptables_command_state cs = {};
- nft_rule_to_iptables_command_state(r, &cs);
+ nft_rule_to_iptables_command_state(h, r, &cs);
print_rule_details(&cs, cs.jumpto, cs.fw6.ipv6.flags,
cs.fw6.ipv6.invflags, cs.fw6.ipv6.proto,
diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c
index 6fd8ade5..42676564 100644
--- a/iptables/nft-shared.c
+++ b/iptables/nft-shared.c
@@ -310,7 +310,6 @@ static void nft_parse_target(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
struct xtables_target *target;
struct xt_entry_target *t;
size_t size;
- struct nft_family_ops *ops;
void *data = ctx->cs;
target = xtables_find_target(targname, XTF_TRY_LOAD);
@@ -327,8 +326,7 @@ static void nft_parse_target(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
target->t = t;
- ops = nft_family_ops_lookup(ctx->family);
- ops->parse_target(target, data);
+ ctx->h->ops->parse_target(target, data);
}
static void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
@@ -339,9 +337,8 @@ static void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
struct xtables_match *match;
struct xtables_rule_match **matches;
struct xt_entry_match *m;
- struct nft_family_ops *ops;
- switch (ctx->family) {
+ switch (ctx->h->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
case NFPROTO_BRIDGE:
@@ -349,7 +346,7 @@ static void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
break;
default:
fprintf(stderr, "BUG: nft_parse_match() unknown family %d\n",
- ctx->family);
+ ctx->h->family);
exit(EXIT_FAILURE);
}
@@ -365,9 +362,8 @@ static void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
match->m = m;
- ops = nft_family_ops_lookup(ctx->family);
- if (ops->parse_match != NULL)
- ops->parse_match(match, ctx->cs);
+ if (ctx->h->ops->parse_match != NULL)
+ ctx->h->ops->parse_match(match, ctx->cs);
}
void print_proto(uint16_t proto, int invert)
@@ -400,7 +396,6 @@ void get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, bool *inv)
static void nft_meta_set_to_target(struct nft_xt_ctx *ctx)
{
- const struct nft_family_ops *ops;
struct xtables_target *target;
struct xt_entry_target *t;
unsigned int size;
@@ -429,8 +424,7 @@ static void nft_meta_set_to_target(struct nft_xt_ctx *ctx)
target->t = t;
- ops = nft_family_ops_lookup(ctx->family);
- ops->parse_target(target, ctx->cs);
+ ctx->h->ops->parse_target(target, ctx->cs);
}
static void nft_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
@@ -451,8 +445,16 @@ static void nft_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
static void nft_parse_payload(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
{
+ if (ctx->flags & NFT_XT_CTX_PAYLOAD) {
+ memcpy(&ctx->prev_payload, &ctx->payload,
+ sizeof(ctx->prev_payload));
+ ctx->flags |= NFT_XT_CTX_PREV_PAYLOAD;
+ }
+
ctx->reg = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG);
+ ctx->payload.base = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_BASE);
ctx->payload.offset = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET);
+ ctx->payload.len = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_LEN);
ctx->flags |= NFT_XT_CTX_PAYLOAD;
}
@@ -474,7 +476,6 @@ static void nft_parse_bitwise(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
static void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
{
- struct nft_family_ops *ops = nft_family_ops_lookup(ctx->family);
void *data = ctx->cs;
uint32_t reg;
@@ -483,12 +484,12 @@ static void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
return;
if (ctx->flags & NFT_XT_CTX_META) {
- ops->parse_meta(ctx, e, data);
+ ctx->h->ops->parse_meta(ctx, e, data);
ctx->flags &= ~NFT_XT_CTX_META;
}
/* bitwise context is interpreted from payload */
if (ctx->flags & NFT_XT_CTX_PAYLOAD) {
- ops->parse_payload(ctx, e, data);
+ ctx->h->ops->parse_payload(ctx, e, data);
ctx->flags &= ~NFT_XT_CTX_PAYLOAD;
}
}
@@ -502,7 +503,6 @@ static void nft_parse_counter(struct nftnl_expr *e, struct xt_counters *counters
static void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
{
const char *chain = nftnl_expr_get_str(e, NFTNL_EXPR_IMM_CHAIN);
- struct nft_family_ops *ops;
const char *jumpto = NULL;
bool nft_goto = false;
void *data = ctx->cs;
@@ -544,8 +544,7 @@ static void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
break;
}
- ops = nft_family_ops_lookup(ctx->family);
- ops->parse_immediate(jumpto, nft_goto, data);
+ ctx->h->ops->parse_immediate(jumpto, nft_goto, data);
}
static void nft_parse_limit(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
@@ -555,11 +554,10 @@ static void nft_parse_limit(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
__u64 rate = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_RATE);
struct xtables_rule_match **matches;
struct xtables_match *match;
- struct nft_family_ops *ops;
struct xt_rateinfo *rinfo;
size_t size;
- switch (ctx->family) {
+ switch (ctx->h->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
case NFPROTO_BRIDGE:
@@ -567,7 +565,7 @@ static void nft_parse_limit(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
break;
default:
fprintf(stderr, "BUG: nft_parse_limit() unknown family %d\n",
- ctx->family);
+ ctx->h->family);
exit(EXIT_FAILURE);
}
@@ -586,20 +584,27 @@ static void nft_parse_limit(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
rinfo->avg = XT_LIMIT_SCALE * unit / rate;
rinfo->burst = burst;
- ops = nft_family_ops_lookup(ctx->family);
- if (ops->parse_match != NULL)
- ops->parse_match(match, ctx->cs);
+ if (ctx->h->ops->parse_match != NULL)
+ ctx->h->ops->parse_match(match, ctx->cs);
+}
+
+static void nft_parse_lookup(struct nft_xt_ctx *ctx, struct nft_handle *h,
+ struct nftnl_expr *e)
+{
+ if (ctx->h->ops->parse_lookup)
+ ctx->h->ops->parse_lookup(ctx, e, NULL);
}
-void nft_rule_to_iptables_command_state(const struct nftnl_rule *r,
+void nft_rule_to_iptables_command_state(struct nft_handle *h,
+ const struct nftnl_rule *r,
struct iptables_command_state *cs)
{
struct nftnl_expr_iter *iter;
struct nftnl_expr *expr;
- int family = nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY);
struct nft_xt_ctx ctx = {
.cs = cs,
- .family = family,
+ .h = h,
+ .table = nftnl_rule_get_str(r, NFTNL_RULE_TABLE),
};
iter = nftnl_expr_iter_create(r);
@@ -630,6 +635,8 @@ void nft_rule_to_iptables_command_state(const struct nftnl_rule *r,
nft_parse_target(&ctx, expr);
else if (strcmp(name, "limit") == 0)
nft_parse_limit(&ctx, expr);
+ else if (strcmp(name, "lookup") == 0)
+ nft_parse_lookup(&ctx, h, expr);
expr = nftnl_expr_iter_next(iter);
}
@@ -982,19 +989,18 @@ void nft_ipv46_parse_target(struct xtables_target *t, void *data)
cs->target = t;
}
-bool nft_ipv46_rule_find(struct nft_family_ops *ops,
- struct nftnl_rule *r, void *data)
+bool nft_ipv46_rule_find(struct nft_handle *h, struct nftnl_rule *r, void *data)
{
struct iptables_command_state *cs = data, this = {};
bool ret = false;
- nft_rule_to_iptables_command_state(r, &this);
+ nft_rule_to_iptables_command_state(h, r, &this);
DEBUGP("comparing with... ");
#ifdef DEBUG_DEL
- nft_rule_print_save(r, NFT_RULE_APPEND, 0);
+ nft_rule_print_save(h, r, NFT_RULE_APPEND, 0);
#endif
- if (!ops->is_same(cs, &this))
+ if (!h->ops->is_same(cs, &this))
goto out;
if (!compare_matches(cs->matches, this.matches)) {
@@ -1014,7 +1020,7 @@ bool nft_ipv46_rule_find(struct nft_family_ops *ops,
ret = true;
out:
- ops->clear_cs(&this);
+ h->ops->clear_cs(&this);
return ret;
}
diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h
index 4ca551bd..bee99a7d 100644
--- a/iptables/nft-shared.h
+++ b/iptables/nft-shared.h
@@ -35,6 +35,7 @@
#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab))
struct xtables_args;
+struct nft_handle;
struct xt_xlate;
enum {
@@ -42,19 +43,22 @@ enum {
NFT_XT_CTX_META = (1 << 1),
NFT_XT_CTX_BITWISE = (1 << 2),
NFT_XT_CTX_IMMEDIATE = (1 << 3),
+ NFT_XT_CTX_PREV_PAYLOAD = (1 << 4),
};
struct nft_xt_ctx {
struct iptables_command_state *cs;
struct nftnl_expr_iter *iter;
- int family;
+ struct nft_handle *h;
uint32_t flags;
+ const char *table;
uint32_t reg;
struct {
+ uint32_t base;
uint32_t offset;
uint32_t len;
- } payload;
+ } payload, prev_payload;
struct {
uint32_t key;
} meta;
@@ -69,7 +73,7 @@ struct nft_xt_ctx {
};
struct nft_family_ops {
- int (*add)(struct nftnl_rule *r, void *data);
+ int (*add)(struct nft_handle *h, struct nftnl_rule *r, void *data);
bool (*is_same)(const void *data_a,
const void *data_b);
void (*print_payload)(struct nftnl_expr *e,
@@ -82,6 +86,8 @@ struct nft_family_ops {
void *data);
void (*parse_cmp)(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
void *data);
+ void (*parse_lookup)(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
+ void *data);
void (*parse_immediate)(const char *jumpto, bool nft_goto, void *data);
void (*print_table_header)(const char *tablename);
@@ -89,8 +95,8 @@ struct nft_family_ops {
const char *pol,
const struct xt_counters *counters, bool basechain,
uint32_t refs, uint32_t entries);
- void (*print_rule)(struct nftnl_rule *r, unsigned int num,
- unsigned int format);
+ void (*print_rule)(struct nft_handle *h, struct nftnl_rule *r,
+ unsigned int num, unsigned int format);
void (*save_rule)(const void *data, unsigned int format);
void (*save_counters)(const void *data);
void (*save_chain)(const struct nftnl_chain *c, const char *policy);
@@ -100,10 +106,10 @@ struct nft_family_ops {
struct xtables_args *args);
void (*parse_match)(struct xtables_match *m, void *data);
void (*parse_target)(struct xtables_target *t, void *data);
- void (*rule_to_cs)(const struct nftnl_rule *r,
+ void (*rule_to_cs)(struct nft_handle *h, const struct nftnl_rule *r,
struct iptables_command_state *cs);
void (*clear_cs)(struct iptables_command_state *cs);
- bool (*rule_find)(struct nft_family_ops *ops, struct nftnl_rule *r,
+ bool (*rule_find)(struct nft_handle *h, struct nftnl_rule *r,
void *data);
int (*xlate)(const void *data, struct xt_xlate *xl);
};
@@ -137,7 +143,8 @@ int parse_meta(struct nftnl_expr *e, uint8_t key, char *iniface,
unsigned char *outiface_mask, uint8_t *invflags);
void print_proto(uint16_t proto, int invert);
void get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, bool *inv);
-void nft_rule_to_iptables_command_state(const struct nftnl_rule *r,
+void nft_rule_to_iptables_command_state(struct nft_handle *h,
+ const struct nftnl_rule *r,
struct iptables_command_state *cs);
void nft_clear_iptables_command_state(struct iptables_command_state *cs);
void print_header(unsigned int format, const char *chain, const char *pol,
@@ -163,9 +170,8 @@ void save_matches_and_target(const struct iptables_command_state *cs,
struct nft_family_ops *nft_family_ops_lookup(int family);
-struct nft_handle;
void nft_ipv46_parse_target(struct xtables_target *t, void *data);
-bool nft_ipv46_rule_find(struct nft_family_ops *ops, struct nftnl_rule *r,
+bool nft_ipv46_rule_find(struct nft_handle *h, struct nftnl_rule *r,
void *data);
bool compare_matches(struct xtables_rule_match *mt1, struct xtables_rule_match *mt2);
diff --git a/iptables/nft.c b/iptables/nft.c
index 83cf5fb7..cf3ab9fe 100644
--- a/iptables/nft.c
+++ b/iptables/nft.c
@@ -271,6 +271,7 @@ enum obj_update_type {
NFT_COMPAT_RULE_REPLACE,
NFT_COMPAT_RULE_DELETE,
NFT_COMPAT_RULE_FLUSH,
+ NFT_COMPAT_SET_ADD,
};
enum obj_action {
@@ -288,6 +289,7 @@ struct obj_update {
struct nftnl_table *table;
struct nftnl_chain *chain;
struct nftnl_rule *rule;
+ struct nftnl_set *set;
void *ptr;
};
struct {
@@ -315,6 +317,7 @@ static int mnl_append_error(const struct nft_handle *h,
[NFT_COMPAT_RULE_REPLACE] = "RULE_REPLACE",
[NFT_COMPAT_RULE_DELETE] = "RULE_DELETE",
[NFT_COMPAT_RULE_FLUSH] = "RULE_FLUSH",
+ [NFT_COMPAT_SET_ADD] = "SET_ADD",
};
char errmsg[256];
char tcr[128];
@@ -351,10 +354,14 @@ static int mnl_append_error(const struct nft_handle *h,
nftnl_rule_get_str(o->rule, NFTNL_RULE_CHAIN));
#if 0
{
- nft_rule_print_save(o->rule, NFT_RULE_APPEND, FMT_NOCOUNTS);
+ nft_rule_print_save(h, o->rule, NFT_RULE_APPEND, FMT_NOCOUNTS);
}
#endif
break;
+ case NFT_COMPAT_SET_ADD:
+ snprintf(tcr, sizeof(tcr), "set %s",
+ nftnl_set_get_str(o->set, NFTNL_SET_NAME));
+ break;
}
return snprintf(buf, len, "%s: %s", errmsg, tcr);
@@ -384,6 +391,13 @@ batch_table_add(struct nft_handle *h, enum obj_update_type type,
return batch_add(h, type, t);
}
+static struct obj_update *
+batch_set_add(struct nft_handle *h, enum obj_update_type type,
+ struct nftnl_set *s)
+{
+ return batch_add(h, type, s);
+}
+
static int batch_chain_add(struct nft_handle *h, enum obj_update_type type,
struct nftnl_chain *c)
{
@@ -775,8 +789,10 @@ int nft_restart(struct nft_handle *h)
return 0;
}
-int nft_init(struct nft_handle *h, const struct builtin_table *t)
+int nft_init(struct nft_handle *h, int family, const struct builtin_table *t)
{
+ memset(h, 0, sizeof(*h));
+
h->nl = mnl_socket_open(NETLINK_NETFILTER);
if (h->nl == NULL)
return -1;
@@ -786,9 +802,14 @@ int nft_init(struct nft_handle *h, const struct builtin_table *t)
return -1;
}
+ h->ops = nft_family_ops_lookup(family);
+ if (!h->ops)
+ xtables_error(PARAMETER_PROBLEM, "Unknown family");
+
h->portid = mnl_socket_get_portid(h->nl);
h->tables = t;
h->cache = &h->__cache[0];
+ h->family = family;
INIT_LIST_HEAD(&h->obj_list);
INIT_LIST_HEAD(&h->err_list);
@@ -930,13 +951,181 @@ static int add_nft_limit(struct nftnl_rule *r, struct xt_entry_match *m)
return 0;
}
-int add_match(struct nftnl_rule *r, struct xt_entry_match *m)
+static struct nftnl_set *add_anon_set(struct nft_handle *h, const char *table,
+ uint32_t flags, uint32_t key_type,
+ uint32_t key_len, uint32_t size)
+{
+ static uint32_t set_id = 0;
+ struct nftnl_set *s;
+
+ s = nftnl_set_alloc();
+ if (!s)
+ return NULL;
+
+ nftnl_set_set_u32(s, NFTNL_SET_FAMILY, h->family);
+ nftnl_set_set_str(s, NFTNL_SET_TABLE, table);
+ nftnl_set_set_str(s, NFTNL_SET_NAME, "__set%d");
+ nftnl_set_set_u32(s, NFTNL_SET_ID, ++set_id);
+ nftnl_set_set_u32(s, NFTNL_SET_FLAGS,
+ NFT_SET_ANONYMOUS | NFT_SET_CONSTANT | flags);
+ nftnl_set_set_u32(s, NFTNL_SET_KEY_TYPE, key_type);
+ nftnl_set_set_u32(s, NFTNL_SET_KEY_LEN, key_len);
+ nftnl_set_set_u32(s, NFTNL_SET_DESC_SIZE, size);
+
+ return batch_set_add(h, NFT_COMPAT_SET_ADD, s) ? s : NULL;
+}
+
+static struct nftnl_expr *
+gen_payload(uint32_t base, uint32_t offset, uint32_t len, uint32_t dreg)
+{
+ struct nftnl_expr *e = nftnl_expr_alloc("payload");
+
+ if (!e)
+ return NULL;
+ nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_BASE, base);
+ nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET, offset);
+ nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_LEN, len);
+ nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_DREG, dreg);
+ return e;
+}
+
+static struct nftnl_expr *
+gen_lookup(uint32_t sreg, const char *set_name, uint32_t set_id, uint32_t flags)
+{
+ struct nftnl_expr *e = nftnl_expr_alloc("lookup");
+
+ if (!e)
+ return NULL;
+ nftnl_expr_set_u32(e, NFTNL_EXPR_LOOKUP_SREG, sreg);
+ nftnl_expr_set_str(e, NFTNL_EXPR_LOOKUP_SET, set_name);
+ nftnl_expr_set_u32(e, NFTNL_EXPR_LOOKUP_SET_ID, set_id);
+ nftnl_expr_set_u32(e, NFTNL_EXPR_LOOKUP_FLAGS, flags);
+ return e;
+}
+
+/* simplified nftables:include/netlink.h, netlink_padded_len() */
+#define NETLINK_ALIGN 4
+
+/* from nftables:include/datatype.h, TYPE_BITS */
+#define CONCAT_TYPE_BITS 6
+
+/* from nftables:include/datatype.h, enum datatypes */
+#define NFT_DATATYPE_IPADDR 7
+#define NFT_DATATYPE_ETHERADDR 9
+
+static int __add_nft_among(struct nft_handle *h, const char *table,
+ struct nftnl_rule *r, struct nft_among_pair *pairs,
+ int cnt, bool dst, bool inv, bool ip)
+{
+ uint32_t set_id, type = NFT_DATATYPE_ETHERADDR, len = ETH_ALEN;
+ /* { !dst, dst } */
+ static const int eth_addr_off[] = {
+ offsetof(struct ether_header, ether_shost),
+ offsetof(struct ether_header, ether_dhost)
+ };
+ static const int ip_addr_off[] = {
+ offsetof(struct iphdr, saddr),
+ offsetof(struct iphdr, daddr)
+ };
+ struct nftnl_expr *e;
+ struct nftnl_set *s;
+ uint32_t flags = 0;
+ int idx = 0;
+
+ if (ip) {
+ type = type << CONCAT_TYPE_BITS | NFT_DATATYPE_IPADDR;
+ len += sizeof(struct in_addr) + NETLINK_ALIGN - 1;
+ len &= ~(NETLINK_ALIGN - 1);
+ flags = NFT_SET_INTERVAL;
+ }
+
+ s = add_anon_set(h, table, flags, type, len, cnt);
+ if (!s)
+ return -ENOMEM;
+ set_id = nftnl_set_get_u32(s, NFTNL_SET_ID);
+
+ if (ip) {
+ uint8_t field_len[2] = { ETH_ALEN, sizeof(struct in_addr) };
+
+ nftnl_set_set_data(s, NFTNL_SET_DESC_CONCAT,
+ field_len, sizeof(field_len));
+ }
+
+ for (idx = 0; idx < cnt; idx++) {
+ struct nftnl_set_elem *elem = nftnl_set_elem_alloc();
+
+ if (!elem)
+ return -ENOMEM;
+ nftnl_set_elem_set(elem, NFTNL_SET_ELEM_KEY,
+ &pairs[idx], len);
+ if (ip) {
+ struct in_addr tmp = pairs[idx].in;
+
+ if (tmp.s_addr == INADDR_ANY)
+ pairs[idx].in.s_addr = INADDR_BROADCAST;
+ nftnl_set_elem_set(elem, NFTNL_SET_ELEM_KEY_END,
+ &pairs[idx], len);
+ pairs[idx].in = tmp;
+ }
+ nftnl_set_elem_add(s, elem);
+ }
+
+ e = gen_payload(NFT_PAYLOAD_LL_HEADER,
+ eth_addr_off[dst], ETH_ALEN, NFT_REG_1);
+ if (!e)
+ return -ENOMEM;
+ nftnl_rule_add_expr(r, e);
+
+ if (ip) {
+ e = gen_payload(NFT_PAYLOAD_NETWORK_HEADER, ip_addr_off[dst],
+ sizeof(struct in_addr), NFT_REG32_02);
+ if (!e)
+ return -ENOMEM;
+ nftnl_rule_add_expr(r, e);
+ }
+
+ e = gen_lookup(NFT_REG_1, "__set%d", set_id, inv);
+ if (!e)
+ return -ENOMEM;
+ nftnl_rule_add_expr(r, e);
+
+ return 0;
+}
+
+static int add_nft_among(struct nft_handle *h,
+ struct nftnl_rule *r, struct xt_entry_match *m)
+{
+ struct nft_among_data *data = (struct nft_among_data *)m->data;
+ const char *table = nftnl_rule_get(r, NFTNL_RULE_TABLE);
+
+ if ((data->src.cnt && data->src.ip) ||
+ (data->dst.cnt && data->dst.ip)) {
+ uint16_t eth_p_ip = htons(ETH_P_IP);
+
+ add_meta(r, NFT_META_PROTOCOL);
+ add_cmp_ptr(r, NFT_CMP_EQ, &eth_p_ip, 2);
+ }
+
+ if (data->src.cnt)
+ __add_nft_among(h, table, r, data->pairs, data->src.cnt,
+ false, data->src.inv, data->src.ip);
+ if (data->dst.cnt)
+ __add_nft_among(h, table, r, data->pairs + data->src.cnt,
+ data->dst.cnt, true, data->dst.inv,
+ data->dst.ip);
+ return 0;
+}
+
+int add_match(struct nft_handle *h,
+ struct nftnl_rule *r, struct xt_entry_match *m)
{
struct nftnl_expr *expr;
int ret;
if (!strcmp(m->u.user.name, "limit"))
return add_nft_limit(r, m);
+ else if (!strcmp(m->u.user.name, "among"))
+ return add_nft_among(h, r, m);
expr = nftnl_expr_alloc("match");
if (expr == NULL)
@@ -1152,7 +1341,7 @@ nft_rule_new(struct nft_handle *h, const char *chain, const char *table,
nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table);
nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain);
- if (h->ops->add(r, data) < 0)
+ if (h->ops->add(h, r, data) < 0)
goto err;
return r;
@@ -1201,7 +1390,7 @@ nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
}
if (verbose)
- h->ops->print_rule(r, 0, FMT_PRINT_RULE);
+ h->ops->print_rule(h, r, 0, FMT_PRINT_RULE);
if (ref) {
nftnl_chain_rule_insert_at(r, ref);
@@ -1219,16 +1408,14 @@ nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
}
void
-nft_rule_print_save(const struct nftnl_rule *r, enum nft_rule_print type,
- unsigned int format)
+nft_rule_print_save(struct nft_handle *h, const struct nftnl_rule *r,
+ enum nft_rule_print type, unsigned int format)
{
const char *chain = nftnl_rule_get_str(r, NFTNL_RULE_CHAIN);
- int family = nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY);
struct iptables_command_state cs = {};
- struct nft_family_ops *ops;
+ struct nft_family_ops *ops = h->ops;
- ops = nft_family_ops_lookup(family);
- ops->rule_to_cs(r, &cs);
+ ops->rule_to_cs(h, r, &cs);
if (!(format & (FMT_NOCOUNTS | FMT_C_COUNTS)) && ops->save_counters)
ops->save_counters(&cs);
@@ -1337,12 +1524,10 @@ static const char *policy_name[NF_ACCEPT+1] = {
int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list)
{
+ struct nft_family_ops *ops = h->ops;
struct nftnl_chain_list_iter *iter;
- struct nft_family_ops *ops;
struct nftnl_chain *c;
- ops = nft_family_ops_lookup(h->family);
-
iter = nftnl_chain_list_iter_create(list);
if (iter == NULL)
return 0;
@@ -1391,7 +1576,7 @@ static int nft_chain_save_rules(struct nft_handle *h,
r = nftnl_rule_iter_next(iter);
while (r != NULL) {
- nft_rule_print_save(r, NFT_RULE_APPEND, format);
+ nft_rule_print_save(h, r, NFT_RULE_APPEND, format);
r = nftnl_rule_iter_next(iter);
}
@@ -1906,7 +2091,7 @@ nft_rule_find(struct nft_handle *h, struct nftnl_chain *c, void *data, int rulen
r = nftnl_rule_iter_next(iter);
while (r != NULL) {
- found = h->ops->rule_find(h->ops, r, data);
+ found = h->ops->rule_find(h, r, data);
if (found)
break;
r = nftnl_rule_iter_next(iter);
@@ -1934,7 +2119,7 @@ int nft_rule_check(struct nft_handle *h, const char *chain,
goto fail_enoent;
if (verbose)
- h->ops->print_rule(r, 0, FMT_PRINT_RULE);
+ h->ops->print_rule(h, r, 0, FMT_PRINT_RULE);
return 1;
fail_enoent:
@@ -1963,7 +2148,7 @@ int nft_rule_delete(struct nft_handle *h, const char *chain,
if (ret < 0)
errno = ENOMEM;
if (verbose)
- h->ops->print_rule(r, 0, FMT_PRINT_RULE);
+ h->ops->print_rule(h, r, 0, FMT_PRINT_RULE);
} else
errno = ENOENT;
@@ -2004,7 +2189,7 @@ nft_rule_add(struct nft_handle *h, const char *chain,
}
if (verbose)
- h->ops->print_rule(r, 0, FMT_PRINT_RULE);
+ h->ops->print_rule(h, r, 0, FMT_PRINT_RULE);
return r;
}
@@ -2113,8 +2298,8 @@ int nft_rule_replace(struct nft_handle *h, const char *chain,
static int
__nft_rule_list(struct nft_handle *h, struct nftnl_chain *c,
int rulenum, unsigned int format,
- void (*cb)(struct nftnl_rule *r, unsigned int num,
- unsigned int format))
+ void (*cb)(struct nft_handle *h, struct nftnl_rule *r,
+ unsigned int num, unsigned int format))
{
struct nftnl_rule_iter *iter;
struct nftnl_rule *r;
@@ -2127,7 +2312,7 @@ __nft_rule_list(struct nft_handle *h, struct nftnl_chain *c,
* valid chain but invalid rule number
*/
return 1;
- cb(r, rulenum, format);
+ cb(h, r, rulenum, format);
return 1;
}
@@ -2137,7 +2322,7 @@ __nft_rule_list(struct nft_handle *h, struct nftnl_chain *c,
r = nftnl_rule_iter_next(iter);
while (r != NULL) {
- cb(r, ++rule_ctr, format);
+ cb(h, r, ++rule_ctr, format);
r = nftnl_rule_iter_next(iter);
}
@@ -2166,7 +2351,6 @@ static int nft_rule_count(struct nft_handle *h, struct nftnl_chain *c)
}
static void __nft_print_header(struct nft_handle *h,
- const struct nft_family_ops *ops,
struct nftnl_chain *c, unsigned int format)
{
const char *chain_name = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
@@ -2182,14 +2366,14 @@ static void __nft_print_header(struct nft_handle *h,
if (nftnl_chain_is_set(c, NFTNL_CHAIN_POLICY))
pname = policy_name[nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY)];
- ops->print_header(format, chain_name, pname,
+ h->ops->print_header(format, chain_name, pname,
&ctrs, basechain, refs - entries, entries);
}
int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
int rulenum, unsigned int format)
{
- const struct nft_family_ops *ops;
+ const struct nft_family_ops *ops = h->ops;
struct nftnl_chain_list *list;
struct nftnl_chain_list_iter *iter;
struct nftnl_chain *c;
@@ -2198,8 +2382,6 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
nft_xt_builtin_init(h, table);
nft_assert_table_compatible(h, table, chain);
- ops = nft_family_ops_lookup(h->family);
-
list = nft_chain_list_get(h, table, chain);
if (!list)
return 0;
@@ -2212,7 +2394,7 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
if (!rulenum) {
if (ops->print_table_header)
ops->print_table_header(table);
- __nft_print_header(h, ops, c, format);
+ __nft_print_header(h, c, format);
}
__nft_rule_list(h, c, rulenum, format, ops->print_rule);
return 1;
@@ -2230,7 +2412,7 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
if (found)
printf("\n");
- __nft_print_header(h, ops, c, format);
+ __nft_print_header(h, c, format);
__nft_rule_list(h, c, rulenum, format, ops->print_rule);
found = true;
@@ -2241,9 +2423,10 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
}
static void
-list_save(struct nftnl_rule *r, unsigned int num, unsigned int format)
+list_save(struct nft_handle *h, struct nftnl_rule *r,
+ unsigned int num, unsigned int format)
{
- nft_rule_print_save(r, NFT_RULE_APPEND, format);
+ nft_rule_print_save(h, r, NFT_RULE_APPEND, format);
}
static int __nftnl_rule_list_chain_save(struct nftnl_chain *c, void *data)
@@ -2355,7 +2538,7 @@ int nft_rule_zero_counters(struct nft_handle *h, const char *chain,
goto error;
}
- nft_rule_to_iptables_command_state(r, &cs);
+ nft_rule_to_iptables_command_state(h, r, &cs);
cs.counters.pcnt = cs.counters.bcnt = 0;
@@ -2376,6 +2559,39 @@ static void nft_compat_table_batch_add(struct nft_handle *h, uint16_t type,
nftnl_table_nlmsg_build_payload(nlh, table);
}
+static void nft_compat_set_batch_add(struct nft_handle *h, uint16_t type,
+ uint16_t flags, uint32_t seq,
+ struct nftnl_set *set)
+{
+ struct nlmsghdr *nlh;
+
+ nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
+ type, h->family, flags, seq);
+ nftnl_set_nlmsg_build_payload(nlh, set);
+}
+
+static void nft_compat_setelem_batch_add(struct nft_handle *h, uint16_t type,
+ uint16_t flags, uint32_t *seq,
+ struct nftnl_set *set)
+{
+ struct nftnl_set_elems_iter *iter;
+ struct nlmsghdr *nlh;
+
+ iter = nftnl_set_elems_iter_create(set);
+ if (!iter)
+ return;
+
+ while (nftnl_set_elems_iter_cur(iter)) {
+ (*seq)++;
+ mnl_nft_batch_continue(h->batch);
+ nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
+ type, h->family, flags, *seq);
+ if (nftnl_set_elems_nlmsg_build_payload_iter(nlh, iter) <= 0)
+ break;
+ }
+ nftnl_set_elems_iter_destroy(iter);
+}
+
static void nft_compat_chain_batch_add(struct nft_handle *h, uint16_t type,
uint16_t flags, uint32_t seq,
struct nftnl_chain *chain)
@@ -2425,6 +2641,9 @@ static void batch_obj_del(struct nft_handle *h, struct obj_update *o)
case NFT_COMPAT_RULE_FLUSH:
nftnl_rule_free(o->rule);
break;
+ case NFT_COMPAT_SET_ADD:
+ nftnl_set_free(o->set);
+ break;
}
h->obj_list_num--;
list_del(&o->head);
@@ -2491,6 +2710,7 @@ static void nft_refresh_transaction(struct nft_handle *h)
case NFT_COMPAT_RULE_REPLACE:
case NFT_COMPAT_RULE_DELETE:
case NFT_COMPAT_RULE_FLUSH:
+ case NFT_COMPAT_SET_ADD:
break;
}
}
@@ -2581,6 +2801,13 @@ retry:
nft_compat_rule_batch_add(h, NFT_MSG_DELRULE, 0,
n->seq, n->rule);
break;
+ case NFT_COMPAT_SET_ADD:
+ nft_compat_set_batch_add(h, NFT_MSG_NEWSET,
+ NLM_F_CREATE, n->seq, n->set);
+ nft_compat_setelem_batch_add(h, NFT_MSG_NEWSETELEM,
+ NLM_F_CREATE, &n->seq, n->set);
+ seq = n->seq;
+ break;
}
mnl_nft_batch_continue(h->batch);
@@ -2601,7 +2828,6 @@ retry:
nft_refresh_transaction(h);
- i=0;
list_for_each_entry_safe(err, ne, &h->err_list, head)
mnl_err_list_free(err);
@@ -2897,6 +3123,44 @@ const char *nft_strerror(int err)
return strerror(err);
}
+static int recover_rule_compat(struct nftnl_rule *r)
+{
+ struct nftnl_expr_iter *iter;
+ struct nftnl_expr *e;
+ uint32_t reg;
+ int ret = -1;
+
+ iter = nftnl_expr_iter_create(r);
+ if (!iter)
+ return -1;
+
+next_expr:
+ e = nftnl_expr_iter_next(iter);
+ if (!e)
+ goto out;
+
+ if (strcmp("meta", nftnl_expr_get_str(e, NFTNL_EXPR_NAME)) ||
+ nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY) != NFT_META_L4PROTO)
+ goto next_expr;
+
+ reg = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG);
+
+ e = nftnl_expr_iter_next(iter);
+ if (!e)
+ goto out;
+
+ if (strcmp("cmp", nftnl_expr_get_str(e, NFTNL_EXPR_NAME)) ||
+ reg != nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG))
+ goto next_expr;
+
+ add_compat(r, nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA),
+ nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ);
+ ret = 0;
+out:
+ nftnl_expr_iter_destroy(iter);
+ return ret;
+}
+
struct chain_zero_data {
struct nft_handle *handle;
bool verbose;
@@ -2961,6 +3225,7 @@ static int __nft_chain_zero_counters(struct nftnl_chain *c, void *data)
* Unset RULE_POSITION for older kernels, we want to replace
* rule based on its handle only.
*/
+ recover_rule_compat(r);
nftnl_rule_unset(r, NFTNL_RULE_POSITION);
if (!batch_rule_add(h, NFT_COMPAT_RULE_REPLACE, r)) {
nftnl_rule_iter_destroy(iter);
@@ -3022,7 +3287,8 @@ static const char *supported_exprs[] = {
"cmp",
"bitwise",
"counter",
- "immediate"
+ "immediate",
+ "lookup",
};
diff --git a/iptables/nft.h b/iptables/nft.h
index 4b8b3033..2094b014 100644
--- a/iptables/nft.h
+++ b/iptables/nft.h
@@ -31,13 +31,16 @@ enum nft_cache_level {
NFT_CL_NONE,
NFT_CL_TABLES,
NFT_CL_CHAINS,
- NFT_CL_RULES
+ NFT_CL_SETS,
+ NFT_CL_RULES,
+ NFT_CL_FAKE /* must be last entry */
};
struct nft_cache {
struct nftnl_table_list *tables;
struct {
struct nftnl_chain_list *chains;
+ struct nftnl_set_list *sets;
bool initialized;
} table[NFT_TABLE_MAX];
};
@@ -78,7 +81,7 @@ extern const struct builtin_table xtables_bridge[NFT_TABLE_MAX];
int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh,
int (*cb)(const struct nlmsghdr *nlh, void *data),
void *data);
-int nft_init(struct nft_handle *h, const struct builtin_table *t);
+int nft_init(struct nft_handle *h, int family, const struct builtin_table *t);
void nft_fini(struct nft_handle *h);
int nft_restart(struct nft_handle *h);
@@ -135,7 +138,7 @@ int nft_rule_zero_counters(struct nft_handle *h, const char *chain, const char *
*/
int add_counters(struct nftnl_rule *r, uint64_t packets, uint64_t bytes);
int add_verdict(struct nftnl_rule *r, int verdict);
-int add_match(struct nftnl_rule *r, struct xt_entry_match *m);
+int add_match(struct nft_handle *h, struct nftnl_rule *r, struct xt_entry_match *m);
int add_target(struct nftnl_rule *r, struct xt_entry_target *t);
int add_jumpto(struct nftnl_rule *r, const char *name, int verdict);
int add_action(struct nftnl_rule *r, struct iptables_command_state *cs, bool goto_set);
@@ -146,8 +149,8 @@ enum nft_rule_print {
NFT_RULE_DEL,
};
-void nft_rule_print_save(const struct nftnl_rule *r, enum nft_rule_print type,
- unsigned int format);
+void nft_rule_print_save(struct nft_handle *h, const struct nftnl_rule *r,
+ enum nft_rule_print type, unsigned int format);
uint32_t nft_invflags2cmp(uint32_t invflags, uint32_t flag);
diff --git a/iptables/tests/shell/testcases/arptables/0001-arptables-save-restore_0 b/iptables/tests/shell/testcases/arptables/0001-arptables-save-restore_0
index bf04dc0a..e64e9142 100755
--- a/iptables/tests/shell/testcases/arptables/0001-arptables-save-restore_0
+++ b/iptables/tests/shell/testcases/arptables/0001-arptables-save-restore_0
@@ -4,7 +4,7 @@ set -e
#set -x
# there is no legacy backend to test
-[[ $XT_MULTI == */xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
+[[ $XT_MULTI == *xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
# fill arptables manually
diff --git a/iptables/tests/shell/testcases/arptables/0002-arptables-restore-defaults_0 b/iptables/tests/shell/testcases/arptables/0002-arptables-restore-defaults_0
index 38d387f3..afd0fcb4 100755
--- a/iptables/tests/shell/testcases/arptables/0002-arptables-restore-defaults_0
+++ b/iptables/tests/shell/testcases/arptables/0002-arptables-restore-defaults_0
@@ -3,7 +3,7 @@
set -e
# there is no legacy backend to test
-[[ $XT_MULTI == */xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
+[[ $XT_MULTI == *xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
# arptables-restore reuses preloaded targets and matches, make sure defaults
# apply to consecutive rules using the same target/match as a previous one
diff --git a/iptables/tests/shell/testcases/arptables/0003-arptables-verbose-output_0 b/iptables/tests/shell/testcases/arptables/0003-arptables-verbose-output_0
index 10c5ec33..952cfa78 100755
--- a/iptables/tests/shell/testcases/arptables/0003-arptables-verbose-output_0
+++ b/iptables/tests/shell/testcases/arptables/0003-arptables-verbose-output_0
@@ -4,7 +4,7 @@ set -e
set -x
# there is no legacy backend to test
-[[ $XT_MULTI == */xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
+[[ $XT_MULTI == *xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
$XT_MULTI arptables -N foo
diff --git a/iptables/tests/shell/testcases/ebtables/0001-ebtables-basic_0 b/iptables/tests/shell/testcases/ebtables/0001-ebtables-basic_0
index c7f24a38..0c1eb4ca 100755
--- a/iptables/tests/shell/testcases/ebtables/0001-ebtables-basic_0
+++ b/iptables/tests/shell/testcases/ebtables/0001-ebtables-basic_0
@@ -1,86 +1,89 @@
#!/bin/sh
+case "$XT_MULTI" in
+*xtables-nft-multi)
+ ;;
+*)
+ echo "skip $XT_MULTI"
+ exit 0
+ ;;
+esac
+
get_entries_count() { # (chain)
$XT_MULTI ebtables -L $1 | sed -n 's/.*entries: \([0-9]*\).*/\1/p'
}
set -x
-case "$XT_MULTI" in
-*/xtables-nft-multi)
- for t in filter nat;do
- $XT_MULTI ebtables -t $t -L || exit 1
- $XT_MULTI ebtables -t $t -X || exit 1
- $XT_MULTI ebtables -t $t -F || exit 1
- done
-
- for t in broute foobar ;do
- $XT_MULTI ebtables -t $t -L &&
- $XT_MULTI ebtables -t $t -X &&
- $XT_MULTI ebtables -t $t -F
- if [ $? -eq 0 ]; then
- echo "Expect nonzero return for unsupported table"
- exit 1
- fi
- done
+for t in filter nat;do
+ $XT_MULTI ebtables -t $t -L || exit 1
+ $XT_MULTI ebtables -t $t -X || exit 1
+ $XT_MULTI ebtables -t $t -F || exit 1
+done
- $XT_MULTI ebtables -t filter -N FOO || exit 1
- $XT_MULTI ebtables -t filter -N FOO
+for t in broute foobar ;do
+ $XT_MULTI ebtables -t $t -L &&
+ $XT_MULTI ebtables -t $t -X &&
+ $XT_MULTI ebtables -t $t -F
if [ $? -eq 0 ]; then
- echo "Duplicate chain FOO"
- $XT_MULTI ebtables -t filter -L
+ echo "Expect nonzero return for unsupported table"
exit 1
fi
+done
- entries=$(get_entries_count FOO)
- if [ $entries -ne 0 ]; then
- echo "Unexpected entries count in empty unreferenced chain (expected 0, have $entries)"
- $XT_MULTI ebtables -L
- exit 1
- fi
- $XT_MULTI ebtables -A FORWARD -j FOO
- entries=$(get_entries_count FORWARD)
- if [ $entries -ne 1 ]; then
- echo "Unexpected entries count in FORWARD chain (expected 1, have $entries)"
- $XT_MULTI ebtables -L
- exit 1
- fi
+$XT_MULTI ebtables -t filter -N FOO || exit 1
+$XT_MULTI ebtables -t filter -N FOO
+if [ $? -eq 0 ]; then
+ echo "Duplicate chain FOO"
+ $XT_MULTI ebtables -t filter -L
+ exit 1
+fi
- entries=$(get_entries_count FOO)
- if [ $entries -ne 0 ]; then
- echo "Unexpected entries count in empty referenced chain (expected 0, have $entries)"
- $XT_MULTI ebtables -L
- exit 1
- fi
+entries=$(get_entries_count FOO)
+if [ $entries -ne 0 ]; then
+ echo "Unexpected entries count in empty unreferenced chain (expected 0, have $entries)"
+ $XT_MULTI ebtables -L
+ exit 1
+fi
- $XT_MULTI ebtables -A FOO -j ACCEPT
- entries=$(get_entries_count FOO)
- if [ $entries -ne 1 ]; then
- echo "Unexpected entries count in non-empty referenced chain (expected 1, have $entries)"
- $XT_MULTI ebtables -L
- exit 1
- fi
+$XT_MULTI ebtables -A FORWARD -j FOO
+entries=$(get_entries_count FORWARD)
+if [ $entries -ne 1 ]; then
+ echo "Unexpected entries count in FORWARD chain (expected 1, have $entries)"
+ $XT_MULTI ebtables -L
+ exit 1
+fi
- $XT_MULTI ebtables -t filter -N BAR || exit 1
- $XT_MULTI ebtables -t filter -N BAZ || exit 1
+entries=$(get_entries_count FOO)
+if [ $entries -ne 0 ]; then
+ echo "Unexpected entries count in empty referenced chain (expected 0, have $entries)"
+ $XT_MULTI ebtables -L
+ exit 1
+fi
- $XT_MULTI ebtables -t filter -L | grep -q FOO || exit 1
- $XT_MULTI ebtables -t filter -L | grep -q BAR || exit 1
- $XT_MULTI ebtables -t filter -L | grep -q BAZ || exit 1
+$XT_MULTI ebtables -A FOO -j ACCEPT
+entries=$(get_entries_count FOO)
+if [ $entries -ne 1 ]; then
+ echo "Unexpected entries count in non-empty referenced chain (expected 1, have $entries)"
+ $XT_MULTI ebtables -L
+ exit 1
+fi
- $XT_MULTI ebtables -t filter -L BAZ || exit 1
- $XT_MULTI ebtables -t filter -X BAZ || exit 1
- $XT_MULTI ebtables -t filter -L BAZ | grep -q BAZ
- if [ $? -eq 0 ]; then
- echo "Deleted chain -L BAZ ok, expected failure"
- $XT_MULTI ebtables -t filter -L
- exit 1
- fi
+$XT_MULTI ebtables -t filter -N BAR || exit 1
+$XT_MULTI ebtables -t filter -N BAZ || exit 1
- $XT_MULTI ebtables -t $t -F || exit 0
- ;;
-*)
- echo "skip $XT_MULTI"
- ;;
-esac
+$XT_MULTI ebtables -t filter -L | grep -q FOO || exit 1
+$XT_MULTI ebtables -t filter -L | grep -q BAR || exit 1
+$XT_MULTI ebtables -t filter -L | grep -q BAZ || exit 1
+
+$XT_MULTI ebtables -t filter -L BAZ || exit 1
+$XT_MULTI ebtables -t filter -X BAZ || exit 1
+$XT_MULTI ebtables -t filter -L BAZ | grep -q BAZ
+if [ $? -eq 0 ]; then
+ echo "Deleted chain -L BAZ ok, expected failure"
+ $XT_MULTI ebtables -t filter -L
+ exit 1
+fi
+
+$XT_MULTI ebtables -t $t -F || exit 0
diff --git a/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0 b/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0
index e18d4655..b84f63a7 100755
--- a/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0
+++ b/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0
@@ -4,7 +4,7 @@ set -e
#set -x
# there is no legacy backend to test
-[[ $XT_MULTI == */xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
+[[ $XT_MULTI == *xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
# fill ebtables manually
diff --git a/iptables/tests/shell/testcases/ebtables/0003-ebtables-restore-defaults_0 b/iptables/tests/shell/testcases/ebtables/0003-ebtables-restore-defaults_0
index 62d22413..63891c1b 100755
--- a/iptables/tests/shell/testcases/ebtables/0003-ebtables-restore-defaults_0
+++ b/iptables/tests/shell/testcases/ebtables/0003-ebtables-restore-defaults_0
@@ -3,7 +3,7 @@
set -e
# there is no legacy backend to test
-[[ $XT_MULTI == */xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
+[[ $XT_MULTI == *xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
# ebtables-restore reuses preloaded targets and matches, make sure defaults
# apply to consecutive rules using the same target/match as a previous one
diff --git a/iptables/tests/shell/testcases/ebtables/0004-save-counters_0 b/iptables/tests/shell/testcases/ebtables/0004-save-counters_0
index 46966f43..d52db900 100755
--- a/iptables/tests/shell/testcases/ebtables/0004-save-counters_0
+++ b/iptables/tests/shell/testcases/ebtables/0004-save-counters_0
@@ -3,7 +3,7 @@
set -e
# there is no legacy backend to test
-[[ $XT_MULTI == */xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
+[[ $XT_MULTI == *xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
$XT_MULTI ebtables --init-table
$XT_MULTI ebtables -A FORWARD -i nodev123 -o nodev432 -j ACCEPT
diff --git a/iptables/tests/shell/testcases/ebtables/0005-ifnamechecks_0 b/iptables/tests/shell/testcases/ebtables/0005-ifnamechecks_0
index 2163d364..0b3acfd7 100755
--- a/iptables/tests/shell/testcases/ebtables/0005-ifnamechecks_0
+++ b/iptables/tests/shell/testcases/ebtables/0005-ifnamechecks_0
@@ -3,7 +3,7 @@
set -e
# there is no legacy backend to test
-[[ $XT_MULTI == */xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
+[[ $XT_MULTI == *xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
EXPECT='*filter
:INPUT ACCEPT
diff --git a/iptables/tests/shell/testcases/firewalld-restore/0001-firewalld_0 b/iptables/tests/shell/testcases/firewalld-restore/0001-firewalld_0
index 8bf0c2c6..0174b03f 100755
--- a/iptables/tests/shell/testcases/firewalld-restore/0001-firewalld_0
+++ b/iptables/tests/shell/testcases/firewalld-restore/0001-firewalld_0
@@ -231,7 +231,7 @@ for table in nat mangle raw filter;do
done
case "$XT_MULTI" in
-*/xtables-nft-multi)
+*xtables-nft-multi)
# nft-multi displays chain names in different order, work around this for now
tmpfile2=$(mktemp)
sort "$tmpfile" > "$tmpfile2"
diff --git a/iptables/tests/shell/testcases/ipt-restore/0004-restore-race_0 b/iptables/tests/shell/testcases/ipt-restore/0004-restore-race_0
index 96a5e66d..9fc50615 100755
--- a/iptables/tests/shell/testcases/ipt-restore/0004-restore-race_0
+++ b/iptables/tests/shell/testcases/ipt-restore/0004-restore-race_0
@@ -86,7 +86,7 @@ if [ $LINES1 -ne $LINES2 ]; then
fi
case "$XT_MULTI" in
-*/xtables-nft-multi)
+*xtables-nft-multi)
attempts=$((RANDOM%10))
attempts=$((attempts+1))
;;
diff --git a/iptables/tests/shell/testcases/ipt-restore/0010-noflush-new-chain_0 b/iptables/tests/shell/testcases/ipt-restore/0010-noflush-new-chain_0
new file mode 100755
index 00000000..739e684a
--- /dev/null
+++ b/iptables/tests/shell/testcases/ipt-restore/0010-noflush-new-chain_0
@@ -0,0 +1,10 @@
+#!/bin/sh -e
+
+# assert input feed from buffer doesn't trip over
+# added nul-chars from parsing chain line.
+
+$XT_MULTI iptables-restore --noflush <<EOF
+*filter
+:foobar - [0:0]
+-A foobar -j ACCEPT
+COMMIT
diff --git a/iptables/tests/shell/testcases/ipt-restore/0011-noflush-empty-line_0 b/iptables/tests/shell/testcases/ipt-restore/0011-noflush-empty-line_0
new file mode 100755
index 00000000..bea1a690
--- /dev/null
+++ b/iptables/tests/shell/testcases/ipt-restore/0011-noflush-empty-line_0
@@ -0,0 +1,16 @@
+#!/bin/bash -e
+
+# make sure empty lines won't break --noflush
+
+cat <<EOF | $XT_MULTI iptables-restore --noflush
+# just a comment followed by innocent empty line
+
+*filter
+-A FORWARD -j ACCEPT
+COMMIT
+EOF
+
+EXPECT='Chain FORWARD (policy ACCEPT)
+target prot opt source destination
+ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 '
+diff -u <(echo "$EXPECT") <($XT_MULTI iptables -n -L FORWARD)
diff --git a/iptables/tests/shell/testcases/iptables/0006-46-args_0 b/iptables/tests/shell/testcases/iptables/0006-46-args_0
new file mode 100755
index 00000000..17a0a018
--- /dev/null
+++ b/iptables/tests/shell/testcases/iptables/0006-46-args_0
@@ -0,0 +1,88 @@
+#!/bin/bash
+
+RC=0
+
+$XT_MULTI iptables -6 -A FORWARD -j ACCEPT
+rc=$?
+if [[ $rc -ne 2 ]]; then
+ echo "'iptables -6' returned $rc instead of 2"
+ RC=1
+fi
+
+$XT_MULTI ip6tables -4 -A FORWARD -j ACCEPT
+rc=$?
+if [[ $rc -ne 2 ]]; then
+ echo "'ip6tables -4' returned $rc instead of 2"
+ RC=1
+fi
+
+RULESET='*filter
+-4 -A FORWARD -d 10.0.0.1 -j ACCEPT
+-6 -A FORWARD -d fec0:10::1 -j ACCEPT
+COMMIT
+'
+EXPECT4='-P FORWARD ACCEPT
+-A FORWARD -d 10.0.0.1/32 -j ACCEPT'
+EXPECT6='-P FORWARD ACCEPT
+-A FORWARD -d fec0:10::1/128 -j ACCEPT'
+EXPECT_EMPTY='-P FORWARD ACCEPT'
+
+echo "$RULESET" | $XT_MULTI iptables-restore || {
+ echo "iptables-restore failed!"
+ RC=1
+}
+diff -u -Z <(echo -e "$EXPECT4") <($XT_MULTI iptables -S FORWARD) || {
+ echo "unexpected iptables ruleset"
+ RC=1
+}
+diff -u -Z <(echo -e "$EXPECT_EMPTY") <($XT_MULTI ip6tables -S FORWARD) || {
+ echo "unexpected non-empty ip6tables ruleset"
+ RC=1
+}
+
+$XT_MULTI iptables -F FORWARD
+
+echo "$RULESET" | $XT_MULTI ip6tables-restore || {
+ echo "ip6tables-restore failed!"
+ RC=1
+}
+diff -u -Z <(echo -e "$EXPECT6") <($XT_MULTI ip6tables -S FORWARD) || {
+ echo "unexpected ip6tables ruleset"
+ RC=1
+}
+diff -u -Z <(echo -e "$EXPECT_EMPTY") <($XT_MULTI iptables -S FORWARD) || {
+ echo "unexpected non-empty iptables ruleset"
+ RC=1
+}
+
+$XT_MULTI ip6tables -F FORWARD
+
+$XT_MULTI iptables -4 -A FORWARD -d 10.0.0.1 -j ACCEPT || {
+ echo "iptables failed!"
+ RC=1
+}
+diff -u -Z <(echo -e "$EXPECT4") <($XT_MULTI iptables -S FORWARD) || {
+ echo "unexpected iptables ruleset"
+ RC=1
+}
+diff -u -Z <(echo -e "$EXPECT_EMPTY") <($XT_MULTI ip6tables -S FORWARD) || {
+ echo "unexpected non-empty ip6tables ruleset"
+ RC=1
+}
+
+$XT_MULTI iptables -F FORWARD
+
+$XT_MULTI ip6tables -6 -A FORWARD -d fec0:10::1 -j ACCEPT || {
+ echo "ip6tables failed!"
+ RC=1
+}
+diff -u -Z <(echo -e "$EXPECT6") <($XT_MULTI ip6tables -S FORWARD) || {
+ echo "unexpected ip6tables ruleset"
+ RC=1
+}
+diff -u -Z <(echo -e "$EXPECT_EMPTY") <($XT_MULTI iptables -S FORWARD) || {
+ echo "unexpected non-empty iptables ruleset"
+ RC=1
+}
+
+exit $RC
diff --git a/iptables/tests/shell/testcases/nft-only/0001compat_0 b/iptables/tests/shell/testcases/nft-only/0001compat_0
index 4319ea5a..a617c52f 100755
--- a/iptables/tests/shell/testcases/nft-only/0001compat_0
+++ b/iptables/tests/shell/testcases/nft-only/0001compat_0
@@ -5,17 +5,18 @@
# xtables: avoid bogus 'is incompatible' warning
case "$XT_MULTI" in
-*/xtables-nft-multi)
- nft -v >/dev/null || exit 0
- nft 'add table ip nft-test; add chain ip nft-test foobar { type filter hook forward priority 42; }' || exit 1
- nft 'add table ip6 nft-test; add chain ip6 nft-test foobar { type filter hook forward priority 42; }' || exit 1
-
- $XT_MULTI iptables -L -t filter || exit 1
- $XT_MULTI ip6tables -L -t filter || exit 1
+*xtables-nft-multi)
;;
*)
echo skip $XT_MULTI
+ exit 0
;;
esac
+nft -v >/dev/null || exit 0
+nft 'add table ip nft-test; add chain ip nft-test foobar { type filter hook forward priority 42; }' || exit 1
+nft 'add table ip6 nft-test; add chain ip6 nft-test foobar { type filter hook forward priority 42; }' || exit 1
+
+$XT_MULTI iptables -L -t filter || exit 1
+$XT_MULTI ip6tables -L -t filter || exit 1
exit 0
diff --git a/iptables/tests/shell/testcases/nft-only/0002invflags_0 b/iptables/tests/shell/testcases/nft-only/0002invflags_0
index 406b6081..fe33874d 100755
--- a/iptables/tests/shell/testcases/nft-only/0002invflags_0
+++ b/iptables/tests/shell/testcases/nft-only/0002invflags_0
@@ -2,7 +2,7 @@
set -e
-[[ $XT_MULTI == */xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
+[[ $XT_MULTI == *xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
$XT_MULTI iptables -A INPUT -p tcp --dport 53 ! -s 192.168.0.1 -j ACCEPT
$XT_MULTI ip6tables -A INPUT -p tcp --dport 53 ! -s feed:babe::1 -j ACCEPT
diff --git a/iptables/tests/shell/testcases/nft-only/0003delete-with-comment_0 b/iptables/tests/shell/testcases/nft-only/0003delete-with-comment_0
index 67af9fd8..ccb009e4 100755
--- a/iptables/tests/shell/testcases/nft-only/0003delete-with-comment_0
+++ b/iptables/tests/shell/testcases/nft-only/0003delete-with-comment_0
@@ -2,7 +2,7 @@
set -e
-[[ $XT_MULTI == */xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
+[[ $XT_MULTI == *xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
comment1="foo bar"
comment2="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
diff --git a/iptables/xtables-arp.c b/iptables/xtables-arp.c
index 9cfad762..c8196f08 100644
--- a/iptables/xtables-arp.c
+++ b/iptables/xtables-arp.c
@@ -500,17 +500,10 @@ int nft_init_arp(struct nft_handle *h, const char *pname)
init_extensionsa();
#endif
- memset(h, 0, sizeof(*h));
- h->family = NFPROTO_ARP;
-
- if (nft_init(h, xtables_arp) < 0)
+ if (nft_init(h, NFPROTO_ARP, xtables_arp) < 0)
xtables_error(OTHER_PROBLEM,
"Could not initialize nftables layer.");
- h->ops = nft_family_ops_lookup(h->family);
- if (h->ops == NULL)
- xtables_error(PARAMETER_PROBLEM, "Unknown family");
-
return 0;
}
diff --git a/iptables/xtables-eb.c b/iptables/xtables-eb.c
index fd7d601f..c006bc95 100644
--- a/iptables/xtables-eb.c
+++ b/iptables/xtables-eb.c
@@ -594,6 +594,7 @@ void ebt_load_match_extensions(void)
ebt_load_match("pkttype");
ebt_load_match("vlan");
ebt_load_match("stp");
+ ebt_load_match("among");
ebt_load_watcher("log");
ebt_load_watcher("nflog");
@@ -738,16 +739,9 @@ int nft_init_eb(struct nft_handle *h, const char *pname)
init_extensionsb();
#endif
- memset(h, 0, sizeof(*h));
-
- h->family = NFPROTO_BRIDGE;
-
- if (nft_init(h, xtables_bridge) < 0)
+ if (nft_init(h, NFPROTO_BRIDGE, xtables_bridge) < 0)
xtables_error(OTHER_PROBLEM,
"Could not initialize nftables layer.");
- h->ops = nft_family_ops_lookup(h->family);
- if (!h->ops)
- xtables_error(PARAMETER_PROBLEM, "Unknown family");
/* manually registering ebt matches, given the original ebtables parser
* don't use '-m matchname' and the match can't be loaded dynamically when
diff --git a/iptables/xtables-monitor.c b/iptables/xtables-monitor.c
index eb80bac8..c2b31dba 100644
--- a/iptables/xtables-monitor.c
+++ b/iptables/xtables-monitor.c
@@ -11,6 +11,7 @@
#define _GNU_SOURCE
#include "config.h"
+#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
@@ -41,6 +42,7 @@
struct cb_arg {
uint32_t nfproto;
bool is_event;
+ struct nft_handle *h;
};
static int table_cb(const struct nlmsghdr *nlh, void *data)
@@ -106,7 +108,7 @@ static int rule_cb(const struct nlmsghdr *nlh, void *data)
}
printf("-t %s ", nftnl_rule_get_str(r, NFTNL_RULE_TABLE));
- nft_rule_print_save(r, type == NFT_MSG_NEWRULE ? NFT_RULE_APPEND :
+ nft_rule_print_save(arg->h, r, type == NFT_MSG_NEWRULE ? NFT_RULE_APPEND :
NFT_RULE_DEL,
counters ? 0 : FMT_NOCOUNTS);
err_free:
@@ -593,7 +595,10 @@ int xtables_monitor_main(int argc, char *argv[])
struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
uint32_t nfgroup = 0;
- struct cb_arg cb_arg = {};
+ struct nft_handle h = {};
+ struct cb_arg cb_arg = {
+ .h = &h,
+ };
int ret, c;
xtables_globals.program_name = "xtables-monitor";
@@ -610,6 +615,14 @@ int xtables_monitor_main(int argc, char *argv[])
init_extensions4();
#endif
+ if (nft_init(&h, AF_INET, xtables_ipv4)) {
+ fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
+ xtables_globals.program_name,
+ xtables_globals.program_version,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
opterr = 0;
while ((c = getopt_long(argc, argv, "ceht46V", options, NULL)) != -1) {
switch (c) {
diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c
index 2f0fe7d4..c472ac9b 100644
--- a/iptables/xtables-restore.c
+++ b/iptables/xtables-restore.c
@@ -281,7 +281,7 @@ void xtables_restore_parse(struct nft_handle *h,
const struct nft_xt_restore_parse *p)
{
struct nft_xt_restore_state state = {};
- char preload_buffer[PREBUFSIZ] = {}, buffer[10240], *ptr;
+ char preload_buffer[PREBUFSIZ] = {}, buffer[10240] = {}, *ptr;
if (!h->noflush) {
nft_fake_cache(h);
@@ -293,11 +293,13 @@ void xtables_restore_parse(struct nft_handle *h,
while (fgets(buffer, sizeof(buffer), p->in)) {
size_t blen = strlen(buffer);
- /* drop trailing newline; xtables_restore_parse_line()
+ /* Drop trailing newline; xtables_restore_parse_line()
* uses strtok() which replaces them by nul-characters,
* causing unpredictable string delimiting in
- * preload_buffer */
- if (buffer[blen - 1] == '\n')
+ * preload_buffer.
+ * Unless this is an empty line which would fold into a
+ * spurious EoB indicator (double nul-char). */
+ if (buffer[blen - 1] == '\n' && blen > 1)
buffer[blen - 1] = '\0';
else
blen++;
@@ -327,10 +329,12 @@ void xtables_restore_parse(struct nft_handle *h,
line = 0;
ptr = preload_buffer;
while (*ptr) {
+ size_t len = strlen(ptr);
+
h->error.lineno = ++line;
DEBUGP("%s: buffered line %d: '%s'\n", __func__, line, ptr);
xtables_restore_parse_line(h, p, &state, ptr);
- ptr += strlen(ptr) + 1;
+ ptr += len + 1;
}
if (*buffer) {
h->error.lineno = ++line;
@@ -356,15 +360,13 @@ static int
xtables_restore_main(int family, const char *progname, int argc, char *argv[])
{
const struct builtin_table *tables;
- struct nft_handle h = {
- .family = family,
- .restore = true,
- };
- int c;
struct nft_xt_restore_parse p = {
.commit = true,
.cb = &restore_cb,
};
+ bool noflush = false;
+ struct nft_handle h;
+ int c;
line = 0;
@@ -377,7 +379,7 @@ xtables_restore_main(int family, const char *progname, int argc, char *argv[])
exit(1);
}
- while ((c = getopt_long(argc, argv, "bcvVthnM:T:46wW", options, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "bcvVthnM:T:wW", options, NULL)) != -1) {
switch (c) {
case 'b':
fprintf(stderr, "-b/--binary option is not implemented\n");
@@ -398,7 +400,7 @@ xtables_restore_main(int family, const char *progname, int argc, char *argv[])
print_usage(prog_name, PACKAGE_VERSION);
exit(0);
case 'n':
- h.noflush = 1;
+ noflush = true;
break;
case 'M':
xtables_modprobe_program = optarg;
@@ -406,13 +408,6 @@ xtables_restore_main(int family, const char *progname, int argc, char *argv[])
case 'T':
p.tablename = optarg;
break;
- case '4':
- h.family = AF_INET;
- break;
- case '6':
- h.family = AF_INET6;
- xtables_set_nfproto(AF_INET6);
- break;
case 'w': /* fallthrough. Ignored by xt-restore */
case 'W':
if (!optarg && xs_has_arg(argc, argv))
@@ -460,13 +455,15 @@ xtables_restore_main(int family, const char *progname, int argc, char *argv[])
return 1;
}
- if (nft_init(&h, tables) < 0) {
+ if (nft_init(&h, family, tables) < 0) {
fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
xtables_globals.program_name,
xtables_globals.program_version,
strerror(errno));
exit(EXIT_FAILURE);
}
+ h.noflush = noflush;
+ h.restore = true;
xtables_restore_parse(&h, &p);
diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c
index 44687f99..28f74902 100644
--- a/iptables/xtables-save.c
+++ b/iptables/xtables-save.c
@@ -32,7 +32,7 @@
#define prog_name xtables_globals.program_name
#define prog_vers xtables_globals.program_version
-static const char *ipt_save_optstring = "bcdt:M:f:46V";
+static const char *ipt_save_optstring = "bcdt:M:f:V";
static const struct option ipt_save_options[] = {
{.name = "counters", .has_arg = false, .val = 'c'},
{.name = "version", .has_arg = false, .val = 'V'},
@@ -40,8 +40,6 @@ static const struct option ipt_save_options[] = {
{.name = "table", .has_arg = true, .val = 't'},
{.name = "modprobe", .has_arg = true, .val = 'M'},
{.name = "file", .has_arg = true, .val = 'f'},
- {.name = "ipv4", .has_arg = false, .val = '4'},
- {.name = "ipv6", .has_arg = false, .val = '6'},
{NULL},
};
@@ -139,10 +137,8 @@ xtables_save_main(int family, int argc, char *argv[],
struct do_output_data d = {
.format = FMT_NOCOUNTS,
};
+ struct nft_handle h;
bool dump = false;
- struct nft_handle h = {
- .family = family,
- };
FILE *file = NULL;
int ret, c;
@@ -189,13 +185,6 @@ xtables_save_main(int family, int argc, char *argv[],
case 'd':
dump = true;
break;
- case '4':
- h.family = AF_INET;
- break;
- case '6':
- h.family = AF_INET6;
- xtables_set_nfproto(AF_INET6);
- break;
case 'V':
printf("%s v%s (nf_tables)\n", prog_name, prog_vers);
exit(0);
@@ -242,7 +231,7 @@ xtables_save_main(int family, int argc, char *argv[],
return 1;
}
- if (nft_init(&h, tables) < 0) {
+ if (nft_init(&h, family, tables) < 0) {
fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
xtables_globals.program_name,
xtables_globals.program_version,
diff --git a/iptables/xtables-standalone.c b/iptables/xtables-standalone.c
index 1a28c548..022d5dd4 100644
--- a/iptables/xtables-standalone.c
+++ b/iptables/xtables-standalone.c
@@ -44,9 +44,7 @@ xtables_main(int family, const char *progname, int argc, char *argv[])
{
int ret;
char *table = "filter";
- struct nft_handle h = {
- .family = family,
- };
+ struct nft_handle h;
xtables_globals.program_name = progname;
ret = xtables_init_all(&xtables_globals, family);
@@ -61,7 +59,7 @@ xtables_main(int family, const char *progname, int argc, char *argv[])
init_extensions4();
#endif
- if (nft_init(&h, xtables_ipv4) < 0) {
+ if (nft_init(&h, family, xtables_ipv4) < 0) {
fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
xtables_globals.program_name,
xtables_globals.program_version,
diff --git a/iptables/xtables-translate.c b/iptables/xtables-translate.c
index a42c60a3..76ad7eb6 100644
--- a/iptables/xtables-translate.c
+++ b/iptables/xtables-translate.c
@@ -32,16 +32,38 @@
void xlate_ifname(struct xt_xlate *xl, const char *nftmeta, const char *ifname,
bool invert)
{
- char iface[IFNAMSIZ];
- int ifaclen;
+ int ifaclen = strlen(ifname), i, j;
+ char iface[IFNAMSIZ * 2];
- if (ifname[0] == '\0')
+ if (ifaclen < 1 || ifaclen >= IFNAMSIZ)
return;
- strcpy(iface, ifname);
- ifaclen = strlen(iface);
- if (iface[ifaclen - 1] == '+')
- iface[ifaclen - 1] = '*';
+ for (i = 0, j = 0; i < ifaclen + 1; i++, j++) {
+ switch (ifname[i]) {
+ case '*':
+ iface[j++] = '\\';
+ /* fall through */
+ default:
+ iface[j] = ifname[i];
+ break;
+ }
+ }
+
+ if (ifaclen == 1 && ifname[0] == '+') {
+ /* Nftables does not support wildcard only string. Workaround
+ * is easy, given that this will match always or never
+ * depending on 'invert' value. To match always, simply don't
+ * generate an expression. To match never, use an invalid
+ * interface name (kernel doesn't accept '/' in names) to match
+ * against. */
+ if (!invert)
+ return;
+ strcpy(iface, "INVAL/D");
+ invert = false;
+ }
+
+ if (iface[j - 2] == '+')
+ iface[j - 2] = '*';
xt_xlate_add(xl, "%s %s\"%s\" ", nftmeta, invert ? "!= " : "", iface);
}
@@ -458,7 +480,7 @@ static int xtables_xlate_main_common(struct nft_handle *h,
return 1;
}
- if (nft_init(h, tables) < 0) {
+ if (nft_init(h, family, tables) < 0) {
fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
xtables_globals.program_name,
xtables_globals.program_version,
diff --git a/iptables/xtables.c b/iptables/xtables.c
index 8f9dc628..8c2d21d4 100644
--- a/iptables/xtables.c
+++ b/iptables/xtables.c
@@ -571,10 +571,6 @@ void do_parse(struct nft_handle *h, int argc, char *argv[],
demand-load a protocol. */
opterr = 0;
- h->ops = nft_family_ops_lookup(h->family);
- if (h->ops == NULL)
- xtables_error(PARAMETER_PROBLEM, "Unknown family");
-
opts = xt_params->orig_opts;
while ((cs->c = getopt_long(argc, argv,
"-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvw::W::nt:m:xc:g:46",
@@ -917,27 +913,22 @@ void do_parse(struct nft_handle *h, int argc, char *argv[],
break;
case '4':
+ if (args->family == AF_INET)
+ break;
+
if (p->restore && args->family == AF_INET6)
return;
- if (args->family != AF_INET)
- exit_tryhelp(2);
-
- h->ops = nft_family_ops_lookup(args->family);
- break;
+ exit_tryhelp(2);
case '6':
+ if (args->family == AF_INET6)
+ break;
+
if (p->restore && args->family == AF_INET)
return;
- args->family = AF_INET6;
- xtables_set_nfproto(AF_INET6);
-
- h->ops = nft_family_ops_lookup(args->family);
- if (h->ops == NULL)
- xtables_error(PARAMETER_PROBLEM,
- "Unknown family");
- break;
+ exit_tryhelp(2);
case 1: /* non option */
if (optarg[0] == '!' && optarg[1] == '\0') {