Commit eb1d7c5f authored by Arturo Borrero Gonzalez's avatar Arturo Borrero Gonzalez
Browse files

New upstream version 1.8.5

parent 290749d4
#!/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
TIMEOUT=10
MODE=0 # apply rulesfile mode
# MODE=1 # run command mode
function blurb()
{
cat <<-_eof
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
### Functions
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 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.
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.
The following options may be specified, using standard conventions:
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
-t | --timeout Specify the timeout in seconds (default: $TIMEOUT)
-V | --version Display version information
-h | --help Display this help text
_eof
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__
}
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
}
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
for cmd in "${COMMANDS[@]}"; do
if ! command -v $cmd >/dev/null; then
echo "E: command not found: $cmd" >&2
exit 127
# 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
done
umask 0700
# Needed commands
COMMANDS=(mktemp "$SAVE" "$RESTORE")
checkcommands
;;
esac
TMPFILE=$(tempfile -p iptap)
### Begin work
# 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
# 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 "E: unknown error applying new iptables ruleset." >&2
echo "Error: unknown error applying new iptables rules: $RULESFILE" >&2
revertrules
exit 5
else
else
echo "done."
fi
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..."
else
# Failed
echo
if [ -z "${ret:-}" ]; then
echo "Timeout! Something happened (or did not). Better play it safe..."
else
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
......
.\" 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.
......@@ -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
......
......@@ -178,8 +178,10 @@ ip46tables_restore_main(const struct iptables_restore_cb *cb,
if (buffer[0] == '\n')
continue;
else if (buffer[0] == '#') {
if (verbose)
if (verbose) {
fputs(buffer, stdout);
fflush(stdout);
}
continue;
} else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
if (!testing) {
......@@ -370,7 +372,7 @@ static const struct iptables_restore_cb ipt_restore_cb = {
int
iptables_restore_main(int argc, char *argv[])
{
int c;
int c, ret;
iptables_globals.program_name = "iptables-restore";
c = xtables_init_all(&iptables_globals, NFPROTO_IPV4);
......@@ -385,7 +387,10 @@ iptables_restore_main(int argc, char *argv[])
init_extensions4();
#endif
return ip46tables_restore_main(&ipt_restore_cb, argc, argv);
ret = ip46tables_restore_main(&ipt_restore_cb, argc, argv);
xtables_fini();
return ret;
}
#endif
......@@ -401,7 +406,7 @@ static const struct iptables_restore_cb ip6t_restore_cb = {
int
ip6tables_restore_main(int argc, char *argv[])
{
int c;
int c, ret;
ip6tables_globals.program_name = "ip6tables-restore";
c = xtables_init_all(&ip6tables_globals, NFPROTO_IPV6);
......@@ -416,6 +421,9 @@ ip6tables_restore_main(int argc, char *argv[])
init_extensions6();
#endif
return ip46tables_restore_main(&ip6t_restore_cb, argc, argv);
ret = ip46tables_restore_main(&ip6t_restore_cb, argc, argv);
xtables_fini();
return ret;
}
#endif
......@@ -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
......
......@@ -218,6 +218,8 @@ struct iptables_save_cb ipt_save_cb = {
int
iptables_save_main(int argc, char *argv[])
{
int ret;
iptables_globals.program_name = "iptables-save";
if (xtables_init_all(&iptables_globals, NFPROTO_IPV4) < 0) {
fprintf(stderr, "%s/%s Failed to initialize xtables\n",
......@@ -230,7 +232,10 @@ iptables_save_main(int argc, char *argv[])
init_extensions4();
#endif
return do_iptables_save(&ipt_save_cb, argc, argv);
ret = do_iptables_save(&ipt_save_cb, argc, argv);
xtables_fini();
return ret;
}
#endif /* ENABLE_IPV4 */
......@@ -259,6 +264,8 @@ struct iptables_save_cb ip6t_save_cb = {
int
ip6tables_save_main(int argc, char *argv[])
{
int ret;
ip6tables_globals.program_name = "ip6tables-save";
if (xtables_init_all(&ip6tables_globals, NFPROTO_IPV6) < 0) {
fprintf(stderr, "%s/%s Failed to initialize xtables\n",
......@@ -271,6 +278,9 @@ ip6tables_save_main(int argc, char *argv[])
init_extensions6();
#endif
return do_iptables_save(&ip6t_save_cb, argc, argv);
ret = do_iptables_save(&ip6t_save_cb, argc, argv);
xtables_fini();
return ret;
}
#endif /* ENABLE_IPV6 */
......@@ -64,6 +64,8 @@ iptables_main(int argc, char *argv[])
iptc_free(handle);
}
xtables_fini();
if (!ret) {
if (errno == EINVAL) {
fprintf(stderr, "iptables: %s. "
......
......@@ -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
......
......@@ -604,6 +604,8 @@ nft_arp_print_rule(struct nft_handle *h, struct nftnl_rule *r,
if (!(format & FMT_NONEWLINE))
fputc('\n', stdout);
nft_clear_iptables_command_state(&cs);
}
static bool nft_arp_is_same(const void *data_a,
......@@ -633,31 +635,6 @@ static bool nft_arp_is_same(const void *data_a,
(unsigned char *)b->arp.outiface_mask);
}
static bool nft_arp_rule_find(struct nft_handle *h, struct nftnl_rule *r,
void *data)
{
const struct iptables_command_state *cs = data;
struct iptables_command_state this = {};
bool ret = false;
/* Delete by matching rule case */
nft_rule_to_iptables_command_state(h, r, &this);
if (!nft_arp_is_same(&cs->arp, &this.arp))
goto out;
if (!compare_targets(cs->target, this.target))
goto out;
if (this.jumpto && strcmp(cs->jumpto, this.jumpto) != 0)
goto out;
ret = true;
out:
h->ops->clear_cs(&this);
return ret;
}
static void nft_arp_save_chain(const struct nftnl_chain *c, const char *policy)
{
const char *chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
......@@ -675,11 +652,9 @@ struct nft_family_ops nft_family_ops_arp = {
.print_header = nft_arp_print_header,
.print_rule = nft_arp_print_rule,
.save_rule = nft_arp_save_rule,
.save_counters = save_counters,
.save_chain = nft_arp_save_chain,
.post_parse = NULL,
.rule_to_cs = nft_rule_to_iptables_command_state,
.clear_cs = nft_clear_iptables_command_state,
.rule_find = nft_arp_rule_find,
.parse_target = nft_ipv46_parse_target,
};
......@@ -421,11 +421,20 @@ 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);
uint32_t set_id = nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_SET_ID);
struct nftnl_set_list *slist;
struct nftnl_set *set;
slist = nft_set_list_get(ctx->h, ctx->table, set_name);
if (slist)
return nftnl_set_list_lookup_byname(slist, set_name);
if (slist) {
set = nftnl_set_list_lookup_byname(slist, set_name);
if (set)
return set;
set = nft_set_batch_lookup_byid(ctx->h, set_id);
if (set)
return set;
}
return NULL;
}
......@@ -747,41 +756,6 @@ 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_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(h, r, &this);
DEBUGP("comparing with... ");
if (!nft_bridge_is_same(cs, &this))
goto out;
if (!compare_matches(cs->matches, this.matches)) {
DEBUGP("Different matches\n");
goto out;
}
if (!compare_targets(cs->target, this.target)) {
DEBUGP("Different target\n");
goto out;
}
if (cs->jumpto != NULL && strcmp(cs->jumpto, this.jumpto) != 0) {
DEBUGP("Different verdict\n");
goto out;
}
ret = true;
out:
h->ops->clear_cs(&this);
return ret;
}
static int xlate_ebmatches(const struct iptables_command_state *cs, struct xt_xlate *xl)
{
int ret = 1, numeric = cs->options & OPT_NUMERIC;
......@@ -958,11 +932,9 @@ struct nft_family_ops nft_family_ops_bridge = {
.print_header = nft_bridge_print_header,
.print_rule = nft_bridge_print_rule,
.save_rule = nft_bridge_save_rule,
.save_counters = save_counters,
.save_chain = nft_bridge_save_chain,
.post_parse = NULL,
.rule_to_cs = nft_rule_to_ebtables_command_state,
.clear_cs = ebt_cs_clean,
.rule_find = nft_bridge_rule_find,
.xlate = nft_bridge_xlate,
};
......@@ -11,6 +11,7 @@
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <xtables.h>
......@@ -24,6 +25,52 @@
#include "nft.h"
#include "nft-cache.h"
static void cache_chain_list_insert(struct list_head *list, const char *name)
{
struct cache_chain *pos = NULL, *new;
list_for_each_entry(pos, list, head) {
int cmp = strcmp(pos->name, name);
if (!cmp)
return;
if (cmp > 0)
break;
}
new = xtables_malloc(sizeof(*new));
new->name = strdup(name);
list_add_tail(&new->head, pos ? &pos->head : list);
}
void nft_cache_level_set(struct nft_handle *h, int level,
const struct nft_cmd *cmd)
{
struct nft_cache_req *req = &h->cache_req;
if (level > req->level)
req->level = level;
if (!cmd || !cmd->table || req->all_chains)
return;
if (!req->table)
req->table = strdup(cmd->table);
else
assert(!strcmp(req->table, cmd->table));
if (!cmd->chain) {
req->all_chains = true;
return;
}
cache_chain_list_insert(&req->chain_list, cmd->chain);
if (cmd->rename)
cache_chain_list_insert(&req->chain_list, cmd->rename);
if (cmd->jumpto)
cache_chain_list_insert(&req->chain_list, cmd->jumpto);
}
static int genid_cb(const struct nlmsghdr *nlh, void *data)
{
uint32_t *genid = data;
......@@ -86,7 +133,7 @@ static int fetch_table_cache(struct nft_handle *h)
char buf[16536];
struct nlmsghdr *nlh;
struct nftnl_table_list *list;
int ret;
int i, ret;
if (h->cache->tables)
return 0;
......@@ -104,6 +151,21 @@ static int fetch_table_cache(struct nft_handle *h)
h->cache->tables = list;
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 0;
h->cache->table[type].sets = nftnl_set_list_alloc();
if (!h->cache->table[type].sets)
return 0;
}
return 1;
}
......@@ -239,40 +301,31 @@ static int fetch_set_cache(struct nft_handle *h,
.h = h,
.t = t,
};
uint16_t flags = NLM_F_DUMP;
struct nftnl_set *s = NULL;
struct nlmsghdr *nlh;
char buf[16536];
int i, ret;
if (!t) {
for (i = 0; i < NFT_TABLE_MAX; i++) {
enum nft_table_type type = h->tables[i].type;
if (t) {
s = nftnl_set_alloc();
if (!s)
return -1;
if (!h->tables[i].name)
continue;
nftnl_set_set_str(s, NFTNL_SET_TABLE, t->name);
h->cache->table[type].sets = nftnl_set_list_alloc();
if (!h->cache->table[type].sets)
return -1;
if (set) {
nftnl_set_set_str(s, NFTNL_SET_NAME, set);
flags = NLM_F_ACK;
}
} 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();
nlh = nftnl_set_nlmsg_build_hdr(buf, NFT_MSG_GETSET,
h->family, flags, h->seq);
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);
if (s) {
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);
......@@ -281,13 +334,7 @@ static int fetch_set_cache(struct nft_handle *h,
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) {
if (t) {
nftnl_set_list_foreach(h->cache->table[t->type].sets,
set_fetch_elem_cb, h);
} else {
......@@ -304,9 +351,9 @@ static int fetch_set_cache(struct nft_handle *h,
return ret;
}
static int fetch_chain_cache(struct nft_handle *h,
static int __fetch_chain_cache(struct nft_handle *h,
const struct builtin_table *t,
const char *chain)
const struct nftnl_chain *c)
{
struct nftnl_chain_list_cb_data d = {
.h = h,
......@@ -314,51 +361,47 @@ static int fetch_chain_cache(struct nft_handle *h,
};
char buf[16536];
struct nlmsghdr *nlh;
int i, ret;
int ret;
if (!t) {
for (i = 0; i < NFT_TABLE_MAX; i++) {
enum nft_table_type type = h->tables[i].type;
nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, h->family,
c ? NLM_F_ACK : NLM_F_DUMP, h->seq);
if (c)
nftnl_chain_nlmsg_build_payload(nlh, c);
if (!h->tables[i].name)
continue;
ret = mnl_talk(h, nlh, nftnl_chain_list_cb, &d);
if (ret < 0 && errno == EINTR)
assert(nft_restart(h) >= 0);
if (h->cache->table[type].chains)
continue;
return ret;
}
h->cache->table[type].chains = nftnl_chain_list_alloc();
if (!h->cache->table[type].chains)
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)
return -1;
}
static int fetch_chain_cache(struct nft_handle *h,
const struct builtin_table *t,
struct list_head *chains)
{
struct cache_chain *cc;
struct nftnl_chain *c;
int rc, ret = 0;
if (t && chain) {
struct nftnl_chain *c = nftnl_chain_alloc();
if (!chains)
return __fetch_chain_cache(h, t, NULL);
assert(t);
c = nftnl_chain_alloc();
if (!c)
return -1;
nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN,
h->family, NLM_F_ACK,
h->seq);
nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, t->name);
nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, chain);
nftnl_chain_nlmsg_build_payload(nlh, c);
nftnl_chain_free(c);
} else {
nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN,
h->family, NLM_F_DUMP,
h->seq);
}
ret = mnl_talk(h, nlh, nftnl_chain_list_cb, &d);
if (ret < 0 && errno == EINTR)
assert(nft_restart(h) >= 0);
list_for_each_entry(cc, chains, head) {
nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, cc->name);
rc = __fetch_chain_cache(h, t, c);
if (rc)
ret = rc;
}
nftnl_chain_free(c);
return ret;
}
......@@ -417,20 +460,14 @@ static int nft_rule_list_update(struct nftnl_chain *c, void *data)
}
static int fetch_rule_cache(struct nft_handle *h,
const struct builtin_table *t, const char *chain)
const struct builtin_table *t)
{
int i;
if (t) {
struct nftnl_chain_list *list;
struct nftnl_chain *c;
struct nftnl_chain_list *list =
h->cache->table[t->type].chains;
list = h->cache->table[t->type].chains;
if (chain) {
c = nftnl_chain_list_lookup_byname(list, chain);
return nft_rule_list_update(c, h);
}
return nftnl_chain_list_foreach(list, nft_rule_list_update, h);
}
......@@ -447,89 +484,46 @@ static int fetch_rule_cache(struct nft_handle *h,
return 0;
}
static int flush_cache(struct nft_handle *h, struct nft_cache *c,
const char *tablename);
static void
__nft_build_cache(struct nft_handle *h, enum nft_cache_level level,
const struct builtin_table *t, const char *set,
const char *chain)
__nft_build_cache(struct nft_handle *h)
{
uint32_t genid_start, genid_stop;
struct nft_cache_req *req = &h->cache_req;
const struct builtin_table *t = NULL;
struct list_head *chains = NULL;
uint32_t genid_check;
if (level <= h->cache_level)
if (h->cache_init)
return;
retry:
mnl_genid_get(h, &genid_start);
if (h->cache_level && genid_start != h->nft_genid)
flush_chain_cache(h, NULL);
switch (h->cache_level) {
case NFT_CL_NONE:
fetch_table_cache(h);
if (level == NFT_CL_TABLES)
break;
/* fall through */
case NFT_CL_TABLES:
fetch_chain_cache(h, t, chain);
if (level == NFT_CL_CHAINS)
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:
break;
if (req->table) {
t = nft_table_builtin_find(h, req->table);
if (!req->all_chains)
chains = &req->chain_list;
}
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)
{
const struct builtin_table *t;
const char *table, *chain;
if (!c)
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, NULL, chain);
}
void nft_fake_cache(struct nft_handle *h)
{
int i;
h->cache_init = true;
retry:
mnl_genid_get(h, &h->nft_genid);
if (req->level >= NFT_CL_TABLES)
fetch_table_cache(h);
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 (req->level == NFT_CL_FAKE)
return;
if (req->level >= NFT_CL_CHAINS)
fetch_chain_cache(h, t, chains);
if (req->level >= NFT_CL_SETS)
fetch_set_cache(h, t, NULL);
if (req->level >= NFT_CL_RULES)
fetch_rule_cache(h, t);
mnl_genid_get(h, &genid_check);
if (h->nft_genid != genid_check) {
flush_cache(h, h->cache, NULL);
goto retry;
}
h->cache_level = NFT_CL_RULES;
mnl_genid_get(h, &h->nft_genid);
}
static void __nft_flush_cache(struct nft_handle *h)
......@@ -610,51 +604,93 @@ 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;
if (c->table[i].chains) {
nftnl_chain_list_free(c->table[i].chains);
c->table[i].chains = NULL;
if (c->table[i].sets)
}
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;
}
return 1;
}
void flush_chain_cache(struct nft_handle *h, const char *tablename)
{
if (!h->cache_level)
if (!h->cache_init)
return;
if (flush_cache(h, h->cache, tablename))
h->cache_level = NFT_CL_NONE;
h->cache_init = false;
}
void nft_rebuild_cache(struct nft_handle *h)
{
enum nft_cache_level level = h->cache_level;
if (h->cache_level)
if (h->cache_init) {
__nft_flush_cache(h);
h->cache_init = false;
}
__nft_build_cache(h);
}
void nft_cache_build(struct nft_handle *h)
{
struct nft_cache_req *req = &h->cache_req;
const struct builtin_table *t = NULL;
int i;
if (req->table)
t = nft_table_builtin_find(h, req->table);
/* fetch builtin chains as well (if existing) so nft_xt_builtin_init()
* doesn't override policies by accident */
if (t && !req->all_chains) {
for (i = 0; i < NF_INET_NUMHOOKS; i++) {
const char *cname = t->chains[i].name;
h->cache_level = NFT_CL_NONE;
__nft_build_cache(h, level, NULL, NULL, NULL);
if (!cname)
break;
cache_chain_list_insert(&req->chain_list, cname);
}
}
__nft_build_cache(h);
}
void nft_release_cache(struct nft_handle *h)
{
if (h->cache_index)
struct nft_cache_req *req = &h->cache_req;
struct cache_chain *cc, *cc_tmp;
while (h->cache_index)
flush_cache(h, &h->__cache[h->cache_index--], NULL);
flush_cache(h, &h->__cache[0], NULL);
h->cache = &h->__cache[0];
h->cache_init = false;
if (req->level != NFT_CL_FAKE)
req->level = NFT_CL_TABLES;
if (req->table) {
free(req->table);
req->table = NULL;
}
req->all_chains = false;
list_for_each_entry_safe(cc, cc_tmp, &req->chain_list, head) {
list_del(&cc->head);
free(cc->name);
free(cc);
}
}
struct nftnl_table_list *nftnl_table_list_get(struct nft_handle *h)
{
__nft_build_cache(h, NFT_CL_TABLES, NULL, NULL, NULL);
return h->cache->tables;
}
......@@ -667,8 +703,6 @@ nft_set_list_get(struct nft_handle *h, const char *table, const char *set)
if (!t)
return NULL;
__nft_build_cache(h, NFT_CL_RULES, t, set, NULL);
return h->cache->table[t->type].sets;
}
......@@ -681,8 +715,6 @@ 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, NULL, chain);
return h->cache->table[t->type].chains;
}
......@@ -2,14 +2,16 @@
#define _NFT_CACHE_H_
struct nft_handle;
struct nft_cmd;
void nft_fake_cache(struct nft_handle *h);
void nft_build_cache(struct nft_handle *h, struct nftnl_chain *c);
void nft_cache_level_set(struct nft_handle *h, int level,
const struct nft_cmd *cmd);
void nft_rebuild_cache(struct nft_handle *h);
void nft_release_cache(struct nft_handle *h);
void flush_chain_cache(struct nft_handle *h, const char *tablename);
int flush_rule_cache(struct nft_handle *h, const char *table,
struct nftnl_chain *c);
void nft_cache_build(struct nft_handle *h);
struct nftnl_chain_list *
nft_chain_list_get(struct nft_handle *h, const char *table, const char *chain);
......
/*
* (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This code has been sponsored by Sophos Astaro <http://www.sophos.com>
*/
#include <stdlib.h>
#include <string.h>
#include "nft.h"
#include "nft-cmd.h"
struct nft_cmd *nft_cmd_new(struct nft_handle *h, int command,
const char *table, const char *chain,
struct iptables_command_state *state,
int rulenum, bool verbose)
{
struct nftnl_rule *rule;
struct nft_cmd *cmd;
cmd = calloc(1, sizeof(struct nft_cmd));
if (!cmd)
return NULL;
cmd->command = command;
cmd->table = strdup(table);
if (chain)
cmd->chain = strdup(chain);
cmd->rulenum = rulenum;
cmd->verbose = verbose;
if (state) {
rule = nft_rule_new(h, chain, table, state);
if (!rule)
return NULL;
cmd->obj.rule = rule;
if (!state->target && strlen(state->jumpto) > 0)
cmd->jumpto = strdup(state->jumpto);
}
list_add_tail(&cmd->head, &h->cmd_list);
return cmd;
}
void nft_cmd_free(struct nft_cmd *cmd)
{
free((void *)cmd->table);
free((void *)cmd->chain);
free((void *)cmd->policy);
free((void *)cmd->rename);
free((void *)cmd->jumpto);
switch (cmd->command) {
case NFT_COMPAT_RULE_CHECK:
case NFT_COMPAT_RULE_DELETE:
if (cmd->obj.rule)
nftnl_rule_free(cmd->obj.rule);
break;
default:
break;
}
list_del(&cmd->head);
free(cmd);
}
static void nft_cmd_rule_bridge(struct nft_handle *h, const struct nft_cmd *cmd)
{
const struct builtin_table *t;
t = nft_table_builtin_find(h, cmd->table);
if (!t)
return;
/* Since ebtables user-defined chain policies are implemented as last
* rule in nftables, rule cache is required here to treat them right.
*/
if (h->family == NFPROTO_BRIDGE &&
!nft_chain_builtin_find(t, cmd->chain))
nft_cache_level_set(h, NFT_CL_RULES, cmd);
else
nft_cache_level_set(h, NFT_CL_CHAINS, cmd);
}
int nft_cmd_rule_append(struct nft_handle *h, const char *chain,
const char *table, struct iptables_command_state *state,
void *ref, bool verbose)
{
struct nft_cmd *cmd;
cmd = nft_cmd_new(h, NFT_COMPAT_RULE_APPEND, table, chain, state, -1,
verbose);
if (!cmd)
return 0;
nft_cmd_rule_bridge(h, cmd);
return 1;
}
int nft_cmd_rule_insert(struct nft_handle *h, const char *chain,
const char *table, struct iptables_command_state *state,
int rulenum, bool verbose)
{
struct nft_cmd *cmd;
cmd = nft_cmd_new(h, NFT_COMPAT_RULE_INSERT, table, chain, state,
rulenum, verbose);
if (!cmd)
return 0;
nft_cmd_rule_bridge(h, cmd);
if (cmd->rulenum > 0)
nft_cache_level_set(h, NFT_CL_RULES, cmd);
else
nft_cache_level_set(h, NFT_CL_CHAINS, cmd);
return 1;
}
int nft_cmd_rule_delete(struct nft_handle *h, const char *chain,
const char *table, struct iptables_command_state *state,
bool verbose)
{
struct nft_cmd *cmd;
cmd = nft_cmd_new(h, NFT_COMPAT_RULE_DELETE, table, chain, state,
-1, verbose);
if (!cmd)
return 0;
nft_cache_level_set(h, NFT_CL_RULES, cmd);
return 1;
}
int nft_cmd_rule_delete_num(struct nft_handle *h, const char *chain,
const char *table, int rulenum, bool verbose)
{
struct nft_cmd *cmd;
cmd = nft_cmd_new(h, NFT_COMPAT_RULE_DELETE, table, chain, NULL,
rulenum, verbose);
if (!cmd)
return 0;
nft_cache_level_set(h, NFT_CL_RULES, cmd);
return 1;
}
int nft_cmd_rule_flush(struct nft_handle *h, const char *chain,
const char *table, bool verbose)
{
struct nft_cmd *cmd;
cmd = nft_cmd_new(h, NFT_COMPAT_RULE_FLUSH, table, chain, NULL, -1,
verbose);
if (!cmd)
return 0;
if (chain || verbose)
nft_cache_level_set(h, NFT_CL_CHAINS, cmd);
else
nft_cache_level_set(h, NFT_CL_TABLES, cmd);
return 1;
}
int nft_cmd_chain_zero_counters(struct nft_handle *h, const char *chain,
const char *table, bool verbose)
{
struct nft_cmd *cmd;
cmd = nft_cmd_new(h, NFT_COMPAT_CHAIN_ZERO, table, chain, NULL, -1,
verbose);
if (!cmd)
return 0;
nft_cache_level_set(h, NFT_CL_CHAINS, cmd);
return 1;
}
int nft_cmd_chain_user_add(struct nft_handle *h, const char *chain,
const char *table)
{
struct nft_cmd *cmd;
cmd = nft_cmd_new(h, NFT_COMPAT_CHAIN_USER_ADD, table, chain, NULL, -1,
false);
if (!cmd)
return 0;
nft_cache_level_set(h, NFT_CL_CHAINS, cmd);
return 1;
}
int nft_cmd_chain_user_del(struct nft_handle *h, const char *chain,
const char *table, bool verbose)
{
struct nft_cmd *cmd;
cmd = nft_cmd_new(h, NFT_COMPAT_CHAIN_USER_DEL, table, chain, NULL, -1,
verbose);
if (!cmd)
return 0;
/* This triggers nft_bridge_chain_postprocess() when fetching the
* rule cache.
*/
if (h->family == NFPROTO_BRIDGE)
nft_cache_level_set(h, NFT_CL_RULES, cmd);
else
nft_cache_level_set(h, NFT_CL_CHAINS, cmd);
return 1;
}
int nft_cmd_chain_user_rename(struct nft_handle *h,const char *chain,
const char *table, const char *newname)
{
struct nft_cmd *cmd;
cmd = nft_cmd_new(h, NFT_COMPAT_CHAIN_RENAME, table, chain, NULL, -1,
false);
if (!cmd)
return 0;
cmd->rename = strdup(newname);
nft_cache_level_set(h, NFT_CL_CHAINS, cmd);
return 1;
}
int nft_cmd_rule_list(struct nft_handle *h, const char *chain,
const char *table, int rulenum, unsigned int format)
{
struct nft_cmd *cmd;
cmd = nft_cmd_new(h, NFT_COMPAT_RULE_LIST, table, chain, NULL, rulenum,
false);
if (!cmd)
return 0;
cmd->format = format;
nft_cache_level_set(h, NFT_CL_RULES, cmd);
return 1;
}
int nft_cmd_rule_replace(struct nft_handle *h, const char *chain,
const char *table, void *data, int rulenum,
bool verbose)
{
struct nft_cmd *cmd;
cmd = nft_cmd_new(h, NFT_COMPAT_RULE_REPLACE, table, chain, data,
rulenum, verbose);
if (!cmd)
return 0;
nft_cache_level_set(h, NFT_CL_RULES, cmd);
return 1;
}
int nft_cmd_rule_check(struct nft_handle *h, const char *chain,
const char *table, void *data, bool verbose)
{
struct nft_cmd *cmd;
cmd = nft_cmd_new(h, NFT_COMPAT_RULE_CHECK, table, chain, data, -1,
verbose);
if (!cmd)
return 0;
nft_cache_level_set(h, NFT_CL_RULES, cmd);
return 1;
}
int nft_cmd_chain_set(struct nft_handle *h, const char *table,
const char *chain, const char *policy,
const struct xt_counters *counters)
{
struct nft_cmd *cmd;
cmd = nft_cmd_new(h, NFT_COMPAT_CHAIN_UPDATE, table, chain, NULL, -1,
false);
if (!cmd)
return 0;
cmd->policy = strdup(policy);
if (counters)
cmd->counters = *counters;
nft_cache_level_set(h, NFT_CL_CHAINS, cmd);
return 1;
}
int nft_cmd_table_flush(struct nft_handle *h, const char *table)
{
struct nft_cmd *cmd;
cmd = nft_cmd_new(h, NFT_COMPAT_TABLE_FLUSH, table, NULL, NULL, -1,
false);
if (!cmd)
return 0;
nft_cache_level_set(h, NFT_CL_TABLES, cmd);
return 1;
}
int nft_cmd_chain_restore(struct nft_handle *h, const char *chain,
const char *table)
{
struct nft_cmd *cmd;
cmd = nft_cmd_new(h, NFT_COMPAT_CHAIN_RESTORE, table, chain, NULL, -1,
false);
if (!cmd)
return 0;
nft_cache_level_set(h, NFT_CL_CHAINS, cmd);
return 1;
}
int nft_cmd_rule_zero_counters(struct nft_handle *h, const char *chain,
const char *table, int rulenum)
{
struct nft_cmd *cmd;
cmd = nft_cmd_new(h, NFT_COMPAT_RULE_ZERO, table, chain, NULL, rulenum,
false);
if (!cmd)
return 0;
nft_cache_level_set(h, NFT_CL_RULES, cmd);
return 1;
}
int nft_cmd_rule_list_save(struct nft_handle *h, const char *chain,
const char *table, int rulenum, int counters)
{
struct nft_cmd *cmd;
cmd = nft_cmd_new(h, NFT_COMPAT_RULE_SAVE, table, chain, NULL, rulenum,
false);
if (!cmd)
return 0;
cmd->counters_save = counters;
nft_cache_level_set(h, NFT_CL_RULES, cmd);
return 1;
}
int ebt_cmd_user_chain_policy(struct nft_handle *h, const char *table,
const char *chain, const char *policy)
{
struct nft_cmd *cmd;
cmd = nft_cmd_new(h, NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE, table, chain,
NULL, -1, false);
if (!cmd)
return 0;
cmd->policy = strdup(policy);
nft_cache_level_set(h, NFT_CL_RULES, cmd);
return 1;
}
void nft_cmd_table_new(struct nft_handle *h, const char *table)
{
nft_cmd_new(h, NFT_COMPAT_TABLE_NEW, table, NULL, NULL, -1, false);
}
#ifndef _NFT_CMD_H_
#define _NFT_CMD_H_
#include <libiptc/linux_list.h>
#include <stdbool.h>
#include "nft.h"
struct nftnl_rule;
struct nft_cmd {
struct list_head head;
int command;
const char *table;
const char *chain;
const char *jumpto;
int rulenum;
bool verbose;
unsigned int format;
struct {
struct nftnl_rule *rule;
struct nftnl_set *set;
} obj;
const char *policy;
struct xt_counters counters;
const char *rename;
int counters_save;
};
struct nft_cmd *nft_cmd_new(struct nft_handle *h, int command,
const char *table, const char *chain,
struct iptables_command_state *state,
int rulenum, bool verbose);
void nft_cmd_free(struct nft_cmd *cmd);
int nft_cmd_rule_append(struct nft_handle *h, const char *chain,
const char *table, struct iptables_command_state *state,
void *ref, bool verbose);
int nft_cmd_rule_insert(struct nft_handle *h, const char *chain,
const char *table, struct iptables_command_state *state,
int rulenum, bool verbose);
int nft_cmd_rule_delete(struct nft_handle *h, const char *chain,
const char *table, struct iptables_command_state *state,
bool verbose);
int nft_cmd_rule_delete_num(struct nft_handle *h, const char *chain,
const char *table, int rulenum, bool verbose);
int nft_cmd_rule_flush(struct nft_handle *h, const char *chain,
const char *table, bool verbose);
int nft_cmd_zero_counters(struct nft_handle *h, const char *chain,
const char *table, bool verbose);
int nft_cmd_chain_user_add(struct nft_handle *h, const char *chain,
const char *table);
int nft_cmd_chain_user_del(struct nft_handle *h, const char *chain,
const char *table, bool verbose);
int nft_cmd_chain_zero_counters(struct nft_handle *h, const char *chain,
const char *table, bool verbose);
int nft_cmd_rule_list(struct nft_handle *h, const char *chain,
const char *table, int rulenum, unsigned int format);
int nft_cmd_rule_check(struct nft_handle *h, const char *chain,
const char *table, void *data, bool verbose);
int nft_cmd_chain_set(struct nft_handle *h, const char *table,
const char *chain, const char *policy,
const struct xt_counters *counters);
int nft_cmd_chain_user_rename(struct nft_handle *h,const char *chain,
const char *table, const char *newname);
int nft_cmd_rule_replace(struct nft_handle *h, const char *chain,
const char *table, void *data, int rulenum,
bool verbose);
int nft_cmd_table_flush(struct nft_handle *h, const char *table);
int nft_cmd_chain_restore(struct nft_handle *h, const char *chain,
const char *table);
int nft_cmd_rule_zero_counters(struct nft_handle *h, const char *chain,
const char *table, int rulenum);
int nft_cmd_rule_list_save(struct nft_handle *h, const char *chain,
const char *table, int rulenum, int counters);
int ebt_cmd_user_chain_policy(struct nft_handle *h, const char *table,
const char *chain, const char *policy);
void nft_cmd_table_new(struct nft_handle *h, const char *table);
#endif /* _NFT_CMD_H_ */
......@@ -288,7 +288,7 @@ static void nft_ipv4_print_rule(struct nft_handle *h, struct nftnl_rule *r,
if (!(format & FMT_NONEWLINE))
fputc('\n', stdout);
xtables_rule_matches_free(&cs.matches);
nft_clear_iptables_command_state(&cs);
}
static void save_ipv4_addr(char letter, const struct in_addr *addr,
......@@ -450,13 +450,11 @@ struct nft_family_ops nft_family_ops_ipv4 = {
.print_header = print_header,
.print_rule = nft_ipv4_print_rule,
.save_rule = nft_ipv4_save_rule,
.save_counters = save_counters,
.save_chain = nft_ipv46_save_chain,
.proto_parse = nft_ipv4_proto_parse,
.post_parse = nft_ipv4_post_parse,
.parse_target = nft_ipv46_parse_target,
.rule_to_cs = nft_rule_to_iptables_command_state,
.clear_cs = nft_clear_iptables_command_state,
.rule_find = nft_ipv46_rule_find,
.xlate = nft_ipv4_xlate,
};
......@@ -217,7 +217,7 @@ static void nft_ipv6_print_rule(struct nft_handle *h, struct nftnl_rule *r,
if (!(format & FMT_NONEWLINE))
fputc('\n', stdout);
xtables_rule_matches_free(&cs.matches);
nft_clear_iptables_command_state(&cs);
}
static void save_ipv6_addr(char letter, const struct in6_addr *addr,
......@@ -402,13 +402,11 @@ struct nft_family_ops nft_family_ops_ipv6 = {
.print_header = print_header,
.print_rule = nft_ipv6_print_rule,
.save_rule = nft_ipv6_save_rule,
.save_counters = save_counters,
.save_chain = nft_ipv46_save_chain,
.proto_parse = nft_ipv6_proto_parse,
.post_parse = nft_ipv6_post_parse,
.parse_target = nft_ipv46_parse_target,
.rule_to_cs = nft_rule_to_iptables_command_state,
.clear_cs = nft_clear_iptables_command_state,
.rule_find = nft_ipv46_rule_find,
.xlate = nft_ipv6_xlate,
};
......@@ -831,14 +831,6 @@ void save_rule_details(const struct iptables_command_state *cs,
}
}
void save_counters(const void *data)
{
const struct iptables_command_state *cs = data;
printf("[%llu:%llu] ", (unsigned long long)cs->counters.pcnt,
(unsigned long long)cs->counters.bcnt);
}
void nft_ipv46_save_chain(const struct nftnl_chain *c, const char *policy)
{
const char *chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
......@@ -989,41 +981,6 @@ void nft_ipv46_parse_target(struct xtables_target *t, void *data)
cs->target = t;
}
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(h, r, &this);
DEBUGP("comparing with... ");
#ifdef DEBUG_DEL
nft_rule_print_save(r, NFT_RULE_APPEND, 0);
#endif
if (!h->ops->is_same(cs, &this))
goto out;
if (!compare_matches(cs->matches, this.matches)) {
DEBUGP("Different matches\n");
goto out;
}
if (!compare_targets(cs->target, this.target)) {
DEBUGP("Different target\n");
goto out;
}
if (strcmp(cs->jumpto, this.jumpto) != 0) {
DEBUGP("Different verdict\n");
goto out;
}
ret = true;
out:
h->ops->clear_cs(&this);
return ret;
}
void nft_check_xt_legacy(int family, bool is_ipt_save)
{
static const char tables6[] = "/proc/net/ip6_tables_names";
......
......@@ -98,7 +98,6 @@ struct nft_family_ops {
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);
void (*proto_parse)(struct iptables_command_state *cs,
struct xtables_args *args);
......@@ -109,8 +108,6 @@ struct nft_family_ops {
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_handle *h, struct nftnl_rule *r,
void *data);
int (*xlate)(const void *data, struct xt_xlate *xl);
};
......@@ -162,7 +159,6 @@ void save_rule_details(const struct iptables_command_state *cs,
unsigned const char *iniface_mask,
const char *outiface,
unsigned const char *outiface_mask);
void save_counters(const void *data);
void nft_ipv46_save_chain(const struct nftnl_chain *c, const char *policy);
void save_matches_and_target(const struct iptables_command_state *cs,
bool goto_flag, const void *fw,
......@@ -171,8 +167,6 @@ void save_matches_and_target(const struct iptables_command_state *cs,
struct nft_family_ops *nft_family_ops_lookup(int family);
void nft_ipv46_parse_target(struct xtables_target *t, void *data);
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);
bool compare_targets(struct xtables_target *tg1, struct xtables_target *tg2);
......
......@@ -256,24 +256,6 @@ static int mnl_batch_talk(struct nft_handle *h, int numcmds)
return err;
}
enum obj_update_type {
NFT_COMPAT_TABLE_ADD,
NFT_COMPAT_TABLE_FLUSH,
NFT_COMPAT_CHAIN_ADD,
NFT_COMPAT_CHAIN_USER_ADD,
NFT_COMPAT_CHAIN_USER_DEL,
NFT_COMPAT_CHAIN_USER_FLUSH,
NFT_COMPAT_CHAIN_UPDATE,
NFT_COMPAT_CHAIN_RENAME,
NFT_COMPAT_CHAIN_ZERO,
NFT_COMPAT_RULE_APPEND,
NFT_COMPAT_RULE_INSERT,
NFT_COMPAT_RULE_REPLACE,
NFT_COMPAT_RULE_DELETE,
NFT_COMPAT_RULE_FLUSH,
NFT_COMPAT_SET_ADD,
};
enum obj_action {
NFT_COMPAT_COMMIT,
NFT_COMPAT_ABORT,
......@@ -362,6 +344,15 @@ static int mnl_append_error(const struct nft_handle *h,
snprintf(tcr, sizeof(tcr), "set %s",
nftnl_set_get_str(o->set, NFTNL_SET_NAME));
break;
case NFT_COMPAT_RULE_LIST:
case NFT_COMPAT_RULE_CHECK:
case NFT_COMPAT_CHAIN_RESTORE:
case NFT_COMPAT_RULE_SAVE:
case NFT_COMPAT_RULE_ZERO:
case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE:
case NFT_COMPAT_TABLE_NEW:
assert(0);
break;
}
return snprintf(buf, len, "%s: %s", errmsg, tcr);
......@@ -411,6 +402,38 @@ batch_rule_add(struct nft_handle *h, enum obj_update_type type,
return batch_add(h, type, r);
}
static void batch_obj_del(struct nft_handle *h, struct obj_update *o);
static void batch_chain_flush(struct nft_handle *h,
const char *table, const char *chain)
{
struct obj_update *obj, *tmp;
list_for_each_entry_safe(obj, tmp, &h->obj_list, head) {
struct nftnl_rule *r = obj->ptr;
switch (obj->type) {
case NFT_COMPAT_RULE_APPEND:
case NFT_COMPAT_RULE_INSERT:
case NFT_COMPAT_RULE_REPLACE:
case NFT_COMPAT_RULE_DELETE:
break;
default:
continue;
}
if (table &&
strcmp(table, nftnl_rule_get_str(r, NFTNL_RULE_TABLE)))
continue;
if (chain &&
strcmp(chain, nftnl_rule_get_str(r, NFTNL_RULE_CHAIN)))
continue;
batch_obj_del(h, obj);
}
}
const struct builtin_table xtables_ipv4[NFT_TABLE_MAX] = {
[NFT_TABLE_RAW] = {
.name = "raw",
......@@ -746,6 +769,9 @@ static int nft_xt_builtin_init(struct nft_handle *h, const char *table)
{
const struct builtin_table *t;
if (!h->cache_init)
return 0;
t = nft_table_builtin_find(h, table);
if (t == NULL)
return -1;
......@@ -756,6 +782,9 @@ static int nft_xt_builtin_init(struct nft_handle *h, const char *table)
if (nft_table_builtin_add(h, t) < 0)
return -1;
if (h->cache_req.level < NFT_CL_CHAINS)
return 0;
nft_chain_builtin_init(h, t);
h->cache->table[t->type].initialized = true;
......@@ -789,8 +818,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;
......@@ -800,19 +831,37 @@ 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);
INIT_LIST_HEAD(&h->cmd_list);
INIT_LIST_HEAD(&h->cache_req.chain_list);
return 0;
}
void nft_fini(struct nft_handle *h)
{
flush_chain_cache(h, NULL);
struct list_head *pos, *n;
list_for_each_safe(pos, n, &h->cmd_list)
nft_cmd_free(list_entry(pos, struct nft_cmd, head));
list_for_each_safe(pos, n, &h->obj_list)
batch_obj_del(h, list_entry(pos, struct obj_update, head));
list_for_each_safe(pos, n, &h->err_list)
mnl_err_list_free(list_entry(pos, struct mnl_err, head));
nft_release_cache(h);
mnl_socket_close(h->nl);
}
......@@ -950,6 +999,7 @@ static struct nftnl_set *add_anon_set(struct nft_handle *h, const char *table,
{
static uint32_t set_id = 0;
struct nftnl_set *s;
struct nft_cmd *cmd;
s = nftnl_set_alloc();
if (!s)
......@@ -965,7 +1015,14 @@ static struct nftnl_set *add_anon_set(struct nft_handle *h, const char *table,
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;
cmd = nft_cmd_new(h, NFT_COMPAT_SET_ADD, table, NULL, NULL, -1, false);
if (!cmd) {
nftnl_set_free(s);
return NULL;
}
cmd->obj.set = s;
return s;
}
static struct nftnl_expr *
......@@ -1022,19 +1079,28 @@ static int __add_nft_among(struct nft_handle *h, const char *table,
};
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, 0, type, len, cnt);
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();
......@@ -1042,6 +1108,15 @@ static int __add_nft_among(struct nft_handle *h, const char *table,
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);
}
......@@ -1302,7 +1377,7 @@ void add_compat(struct nftnl_rule *r, uint32_t proto, bool inv)
inv ? NFT_RULE_COMPAT_F_INV : 0);
}
static struct nftnl_rule *
struct nftnl_rule *
nft_rule_new(struct nft_handle *h, const char *chain, const char *table,
void *data)
{
......@@ -1330,28 +1405,15 @@ nft_chain_find(struct nft_handle *h, const char *table, const char *chain);
int
nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
void *data, struct nftnl_rule *ref, bool verbose)
struct nftnl_rule *r, struct nftnl_rule *ref, bool verbose)
{
struct nftnl_chain *c;
struct nftnl_rule *r;
int type;
nft_xt_builtin_init(h, table);
/* Since ebtables user-defined chain policies are implemented as last
* rule in nftables, rule cache is required here to treat them right. */
if (h->family == NFPROTO_BRIDGE) {
c = nft_chain_find(h, table, chain);
if (c && !nft_chain_builtin(c))
nft_build_cache(h, c);
}
nft_fn = nft_rule_append;
r = nft_rule_new(h, chain, table, data);
if (r == NULL)
return 0;
if (ref) {
nftnl_rule_set_u64(r, NFTNL_RULE_HANDLE,
nftnl_rule_get_u64(ref, NFTNL_RULE_HANDLE));
......@@ -1359,17 +1421,16 @@ nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
} else
type = NFT_COMPAT_RULE_APPEND;
if (batch_rule_add(h, type, r) == NULL) {
nftnl_rule_free(r);
if (batch_rule_add(h, type, r) == NULL)
return 0;
}
if (verbose)
h->ops->print_rule(h, r, 0, FMT_PRINT_RULE);
if (ref) {
nftnl_chain_rule_insert_at(r, ref);
nftnl_chain_rule_del(r);
nftnl_chain_rule_del(ref);
nftnl_rule_free(ref);
} else {
c = nft_chain_find(h, table, chain);
if (!c) {
......@@ -1392,8 +1453,9 @@ nft_rule_print_save(struct nft_handle *h, const struct nftnl_rule *r,
ops->rule_to_cs(h, r, &cs);
if (!(format & (FMT_NOCOUNTS | FMT_C_COUNTS)) && ops->save_counters)
ops->save_counters(&cs);
if (!(format & (FMT_NOCOUNTS | FMT_C_COUNTS)))
printf("[%llu:%llu] ", (unsigned long long)cs.counters.pcnt,
(unsigned long long)cs.counters.bcnt);
/* print chain name */
switch(type) {
......@@ -1576,7 +1638,6 @@ int nft_rule_save(struct nft_handle *h, const char *table, unsigned int format)
c = nftnl_chain_list_iter_next(iter);
while (c) {
nft_build_cache(h, c);
ret = nft_chain_save_rules(h, c, format);
if (ret != 0)
break;
......@@ -1590,6 +1651,20 @@ int nft_rule_save(struct nft_handle *h, const char *table, unsigned int format)
return ret == 0 ? 1 : 0;
}
struct nftnl_set *nft_set_batch_lookup_byid(struct nft_handle *h,
uint32_t set_id)
{
struct obj_update *n;
list_for_each_entry(n, &h->obj_list, head) {
if (n->type == NFT_COMPAT_SET_ADD &&
nftnl_set_get_u32(n->set, NFTNL_SET_ID) == set_id)
return n->set;
}
return NULL;
}
static void
__nft_rule_flush(struct nft_handle *h, const char *table,
const char *chain, bool verbose, bool implicit)
......@@ -1646,6 +1721,7 @@ int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table,
}
if (chain || !verbose) {
batch_chain_flush(h, table, chain);
__nft_rule_flush(h, table, chain, verbose, false);
flush_rule_cache(h, table, c);
return 1;
......@@ -1661,6 +1737,7 @@ int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table,
while (c != NULL) {
chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
batch_chain_flush(h, table, chain);
__nft_rule_flush(h, table, chain, verbose, false);
flush_rule_cache(h, table, c);
c = nftnl_chain_list_iter_next(iter);
......@@ -1722,7 +1799,7 @@ int nft_chain_restore(struct nft_handle *h, const char *chain, const char *table
} else {
c = nftnl_chain_alloc();
if (!c)
return -1;
return 0;
nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, table);
nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, chain);
......@@ -1733,7 +1810,7 @@ int nft_chain_restore(struct nft_handle *h, const char *chain, const char *table
nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, NF_ACCEPT);
if (!created)
return 0;
return 1;
ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c);
......@@ -1741,7 +1818,8 @@ int nft_chain_restore(struct nft_handle *h, const char *chain, const char *table
if (list)
nftnl_chain_list_add(c, list);
return ret;
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}
/* From linux/netlink.h */
......@@ -1769,10 +1847,6 @@ static int __nft_chain_user_del(struct nftnl_chain *c, void *data)
fprintf(stdout, "Deleting chain `%s'\n",
nftnl_chain_get_str(c, NFTNL_CHAIN_NAME));
/* This triggers required policy rule deletion. */
if (h->family == NFPROTO_BRIDGE)
nft_build_cache(h, c);
/* XXX This triggers a fast lookup from the kernel. */
nftnl_chain_unset(c, NFTNL_CHAIN_HANDLE);
ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_DEL, c);
......@@ -2047,15 +2121,53 @@ static int __nft_rule_del(struct nft_handle *h, struct nftnl_rule *r)
return 1;
}
static bool nft_rule_cmp(struct nft_handle *h, struct nftnl_rule *r,
struct nftnl_rule *rule)
{
struct iptables_command_state _cs = {}, this = {}, *cs = &_cs;
bool ret = false;
h->ops->rule_to_cs(h, r, &this);
h->ops->rule_to_cs(h, rule, cs);
DEBUGP("comparing with... ");
#ifdef DEBUG_DEL
nft_rule_print_save(h, r, NFT_RULE_APPEND, 0);
#endif
if (!h->ops->is_same(cs, &this))
goto out;
if (!compare_matches(cs->matches, this.matches)) {
DEBUGP("Different matches\n");
goto out;
}
if (!compare_targets(cs->target, this.target)) {
DEBUGP("Different target\n");
goto out;
}
if ((!cs->target || !this.target) &&
strcmp(cs->jumpto, this.jumpto) != 0) {
DEBUGP("Different verdict\n");
goto out;
}
ret = true;
out:
h->ops->clear_cs(&this);
h->ops->clear_cs(cs);
return ret;
}
static struct nftnl_rule *
nft_rule_find(struct nft_handle *h, struct nftnl_chain *c, void *data, int rulenum)
nft_rule_find(struct nft_handle *h, struct nftnl_chain *c,
struct nftnl_rule *rule, int rulenum)
{
struct nftnl_rule *r;
struct nftnl_rule_iter *iter;
bool found = false;
nft_build_cache(h, c);
if (rulenum >= 0)
/* Delete by rule number case */
return nftnl_rule_lookup_byindex(c, rulenum);
......@@ -2066,7 +2178,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, r, data);
found = nft_rule_cmp(h, r, rule);
if (found)
break;
r = nftnl_rule_iter_next(iter);
......@@ -2078,7 +2190,7 @@ nft_rule_find(struct nft_handle *h, struct nftnl_chain *c, void *data, int rulen
}
int nft_rule_check(struct nft_handle *h, const char *chain,
const char *table, void *data, bool verbose)
const char *table, struct nftnl_rule *rule, bool verbose)
{
struct nftnl_chain *c;
struct nftnl_rule *r;
......@@ -2089,7 +2201,7 @@ int nft_rule_check(struct nft_handle *h, const char *chain,
if (!c)
goto fail_enoent;
r = nft_rule_find(h, c, data, -1);
r = nft_rule_find(h, c, rule, -1);
if (r == NULL)
goto fail_enoent;
......@@ -2103,7 +2215,7 @@ fail_enoent:
}
int nft_rule_delete(struct nft_handle *h, const char *chain,
const char *table, void *data, bool verbose)
const char *table, struct nftnl_rule *rule, bool verbose)
{
int ret = 0;
struct nftnl_chain *c;
......@@ -2117,7 +2229,7 @@ int nft_rule_delete(struct nft_handle *h, const char *chain,
return 0;
}
r = nft_rule_find(h, c, data, -1);
r = nft_rule_find(h, c, rule, -1);
if (r != NULL) {
ret =__nft_rule_del(h, r);
if (ret < 0)
......@@ -2132,16 +2244,11 @@ int nft_rule_delete(struct nft_handle *h, const char *chain,
static struct nftnl_rule *
nft_rule_add(struct nft_handle *h, const char *chain,
const char *table, struct iptables_command_state *cs,
const char *table, struct nftnl_rule *r,
struct nftnl_rule *ref, bool verbose)
{
struct nftnl_rule *r;
uint64_t ref_id;
r = nft_rule_new(h, chain, table, cs);
if (r == NULL)
return NULL;
if (ref) {
ref_id = nftnl_rule_get_u64(ref, NFTNL_RULE_HANDLE);
if (ref_id > 0) {
......@@ -2158,10 +2265,8 @@ nft_rule_add(struct nft_handle *h, const char *chain,
}
}
if (!batch_rule_add(h, NFT_COMPAT_RULE_INSERT, r)) {
nftnl_rule_free(r);
if (!batch_rule_add(h, NFT_COMPAT_RULE_INSERT, r))
return NULL;
}
if (verbose)
h->ops->print_rule(h, r, 0, FMT_PRINT_RULE);
......@@ -2170,9 +2275,10 @@ nft_rule_add(struct nft_handle *h, const char *chain,
}
int nft_rule_insert(struct nft_handle *h, const char *chain,
const char *table, void *data, int rulenum, bool verbose)
const char *table, struct nftnl_rule *new_rule, int rulenum,
bool verbose)
{
struct nftnl_rule *r = NULL, *new_rule;
struct nftnl_rule *r = NULL;
struct nftnl_chain *c;
nft_xt_builtin_init(h, table);
......@@ -2186,22 +2292,22 @@ int nft_rule_insert(struct nft_handle *h, const char *chain,
}
if (rulenum > 0) {
r = nft_rule_find(h, c, data, rulenum);
r = nft_rule_find(h, c, new_rule, rulenum);
if (r == NULL) {
/* special case: iptables allows to insert into
* rule_count + 1 position.
*/
r = nft_rule_find(h, c, data, rulenum - 1);
r = nft_rule_find(h, c, new_rule, rulenum - 1);
if (r != NULL)
return nft_rule_append(h, chain, table, data,
NULL, verbose);
return nft_rule_append(h, chain, table,
new_rule, NULL, verbose);
errno = E2BIG;
goto err;
}
}
new_rule = nft_rule_add(h, chain, table, data, r, verbose);
new_rule = nft_rule_add(h, chain, table, new_rule, r, verbose);
if (!new_rule)
goto err;
......@@ -2243,7 +2349,8 @@ int nft_rule_delete_num(struct nft_handle *h, const char *chain,
}
int nft_rule_replace(struct nft_handle *h, const char *chain,
const char *table, void *data, int rulenum, bool verbose)
const char *table, struct nftnl_rule *rule,
int rulenum, bool verbose)
{
int ret = 0;
struct nftnl_chain *c;
......@@ -2257,13 +2364,13 @@ int nft_rule_replace(struct nft_handle *h, const char *chain,
return 0;
}
r = nft_rule_find(h, c, data, rulenum);
r = nft_rule_find(h, c, rule, rulenum);
if (r != NULL) {
DEBUGP("replacing rule with handle=%llu\n",
(unsigned long long)
nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE));
ret = nft_rule_append(h, chain, table, data, r, verbose);
ret = nft_rule_append(h, chain, table, rule, r, verbose);
} else
errno = E2BIG;
......@@ -2496,8 +2603,8 @@ int nft_rule_zero_counters(struct nft_handle *h, const char *chain,
const char *table, int rulenum)
{
struct iptables_command_state cs = {};
struct nftnl_rule *r, *new_rule;
struct nftnl_chain *c;
struct nftnl_rule *r;
int ret = 0;
nft_fn = nft_rule_delete;
......@@ -2516,8 +2623,11 @@ int nft_rule_zero_counters(struct nft_handle *h, const char *chain,
nft_rule_to_iptables_command_state(h, r, &cs);
cs.counters.pcnt = cs.counters.bcnt = 0;
new_rule = nft_rule_new(h, chain, table, &cs);
if (!new_rule)
return 1;
ret = nft_rule_append(h, chain, table, &cs, r, false);
ret = nft_rule_append(h, chain, table, new_rule, r, false);
error:
return ret;
......@@ -2611,14 +2721,23 @@ static void batch_obj_del(struct nft_handle *h, struct obj_update *o)
case NFT_COMPAT_RULE_APPEND:
case NFT_COMPAT_RULE_INSERT:
case NFT_COMPAT_RULE_REPLACE:
case NFT_COMPAT_RULE_DELETE:
break;
case NFT_COMPAT_RULE_DELETE:
case NFT_COMPAT_RULE_FLUSH:
nftnl_rule_free(o->rule);
break;
case NFT_COMPAT_SET_ADD:
nftnl_set_free(o->set);
break;
case NFT_COMPAT_RULE_LIST:
case NFT_COMPAT_RULE_CHECK:
case NFT_COMPAT_CHAIN_RESTORE:
case NFT_COMPAT_RULE_SAVE:
case NFT_COMPAT_RULE_ZERO:
case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE:
case NFT_COMPAT_TABLE_NEW:
assert(0);
break;
}
h->obj_list_num--;
list_del(&o->head);
......@@ -2686,6 +2805,13 @@ static void nft_refresh_transaction(struct nft_handle *h)
case NFT_COMPAT_RULE_DELETE:
case NFT_COMPAT_RULE_FLUSH:
case NFT_COMPAT_SET_ADD:
case NFT_COMPAT_RULE_LIST:
case NFT_COMPAT_RULE_CHECK:
case NFT_COMPAT_CHAIN_RESTORE:
case NFT_COMPAT_RULE_SAVE:
case NFT_COMPAT_RULE_ZERO:
case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE:
case NFT_COMPAT_TABLE_NEW:
break;
}
}
......@@ -2783,6 +2909,14 @@ retry:
NLM_F_CREATE, &n->seq, n->set);
seq = n->seq;
break;
case NFT_COMPAT_RULE_LIST:
case NFT_COMPAT_RULE_CHECK:
case NFT_COMPAT_CHAIN_RESTORE:
case NFT_COMPAT_RULE_SAVE:
case NFT_COMPAT_RULE_ZERO:
case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE:
case NFT_COMPAT_TABLE_NEW:
assert(0);
}
mnl_nft_batch_continue(h->batch);
......@@ -2803,7 +2937,6 @@ retry:
nft_refresh_transaction(h);
i=0;
list_for_each_entry_safe(err, ne, &h->err_list, head)
mnl_err_list_free(err);
......@@ -2878,27 +3011,33 @@ static int ebt_add_policy_rule(struct nftnl_chain *c, void *data)
r = nft_rule_new(h, nftnl_chain_get_str(c, NFTNL_CHAIN_NAME),
nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE), &cs);
ebt_cs_clean(&cs);
if (!r)
return -1;
udata = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
if (!udata)
return -1;
goto err_free_rule;
if (!nftnl_udata_put_u32(udata, UDATA_TYPE_EBTABLES_POLICY, 1))
return -1;
goto err_free_rule;
nftnl_rule_set_data(r, NFTNL_RULE_USERDATA,
nftnl_udata_buf_data(udata),
nftnl_udata_buf_len(udata));
nftnl_udata_buf_free(udata);
if (!batch_rule_add(h, NFT_COMPAT_RULE_APPEND, r)) {
nftnl_rule_free(r);
return -1;
}
if (!batch_rule_add(h, NFT_COMPAT_RULE_APPEND, r))
goto err_free_rule;
/* add the rule to chain so it is freed later */
nftnl_chain_rule_add_tail(r, c);
return 0;
err_free_rule:
nftnl_rule_free(r);
return -1;
}
int ebt_set_user_chain_policy(struct nft_handle *h, const char *table,
......@@ -2919,8 +3058,6 @@ int ebt_set_user_chain_policy(struct nft_handle *h, const char *table,
else
return 0;
nft_build_cache(h, c);
nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, pval);
return 1;
}
......@@ -2945,41 +3082,152 @@ static void nft_bridge_commit_prepare(struct nft_handle *h)
}
}
int nft_commit(struct nft_handle *h)
static void assert_chain_exists(struct nft_handle *h,
const char *table, const char *chain)
{
return nft_action(h, NFT_COMPAT_COMMIT);
if (chain && !nft_chain_exists(h, table, chain))
xtables_error(PARAMETER_PROBLEM,
"Chain '%s' does not exist", chain);
}
int nft_bridge_commit(struct nft_handle *h)
static int nft_prepare(struct nft_handle *h)
{
nft_bridge_commit_prepare(h);
return nft_commit(h);
struct nft_cmd *cmd, *next;
int ret = 1;
nft_cache_build(h);
list_for_each_entry_safe(cmd, next, &h->cmd_list, head) {
switch (cmd->command) {
case NFT_COMPAT_TABLE_FLUSH:
ret = nft_table_flush(h, cmd->table);
break;
case NFT_COMPAT_CHAIN_USER_ADD:
ret = nft_chain_user_add(h, cmd->chain, cmd->table);
break;
case NFT_COMPAT_CHAIN_USER_DEL:
ret = nft_chain_user_del(h, cmd->chain, cmd->table,
cmd->verbose);
break;
case NFT_COMPAT_CHAIN_RESTORE:
ret = nft_chain_restore(h, cmd->chain, cmd->table);
break;
case NFT_COMPAT_CHAIN_UPDATE:
ret = nft_chain_set(h, cmd->table, cmd->chain,
cmd->policy, &cmd->counters);
break;
case NFT_COMPAT_CHAIN_RENAME:
ret = nft_chain_user_rename(h, cmd->chain, cmd->table,
cmd->rename);
break;
case NFT_COMPAT_CHAIN_ZERO:
ret = nft_chain_zero_counters(h, cmd->chain, cmd->table,
cmd->verbose);
break;
case NFT_COMPAT_RULE_APPEND:
assert_chain_exists(h, cmd->table, cmd->jumpto);
ret = nft_rule_append(h, cmd->chain, cmd->table,
cmd->obj.rule, NULL, cmd->verbose);
break;
case NFT_COMPAT_RULE_INSERT:
assert_chain_exists(h, cmd->table, cmd->jumpto);
ret = nft_rule_insert(h, cmd->chain, cmd->table,
cmd->obj.rule, cmd->rulenum,
cmd->verbose);
break;
case NFT_COMPAT_RULE_REPLACE:
assert_chain_exists(h, cmd->table, cmd->jumpto);
ret = nft_rule_replace(h, cmd->chain, cmd->table,
cmd->obj.rule, cmd->rulenum,
cmd->verbose);
break;
case NFT_COMPAT_RULE_DELETE:
assert_chain_exists(h, cmd->table, cmd->jumpto);
if (cmd->rulenum >= 0)
ret = nft_rule_delete_num(h, cmd->chain,
cmd->table,
cmd->rulenum,
cmd->verbose);
else
ret = nft_rule_delete(h, cmd->chain, cmd->table,
cmd->obj.rule, cmd->verbose);
break;
case NFT_COMPAT_RULE_FLUSH:
ret = nft_rule_flush(h, cmd->chain, cmd->table,
cmd->verbose);
break;
case NFT_COMPAT_RULE_LIST:
ret = nft_rule_list(h, cmd->chain, cmd->table,
cmd->rulenum, cmd->format);
break;
case NFT_COMPAT_RULE_CHECK:
assert_chain_exists(h, cmd->table, cmd->jumpto);
ret = nft_rule_check(h, cmd->chain, cmd->table,
cmd->obj.rule, cmd->rulenum);
break;
case NFT_COMPAT_RULE_ZERO:
ret = nft_rule_zero_counters(h, cmd->chain, cmd->table,
cmd->rulenum);
break;
case NFT_COMPAT_RULE_SAVE:
ret = nft_rule_list_save(h, cmd->chain, cmd->table,
cmd->rulenum,
cmd->counters_save);
break;
case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE:
ret = ebt_set_user_chain_policy(h, cmd->table,
cmd->chain, cmd->policy);
break;
case NFT_COMPAT_TABLE_NEW:
nft_xt_builtin_init(h, cmd->table);
ret = 1;
break;
case NFT_COMPAT_SET_ADD:
nft_xt_builtin_init(h, cmd->table);
batch_set_add(h, NFT_COMPAT_SET_ADD, cmd->obj.set);
ret = 1;
break;
case NFT_COMPAT_TABLE_ADD:
case NFT_COMPAT_CHAIN_ADD:
assert(0);
break;
}
nft_cmd_free(cmd);
if (ret == 0)
return 0;
}
return 1;
}
int nft_abort(struct nft_handle *h)
int nft_commit(struct nft_handle *h)
{
return nft_action(h, NFT_COMPAT_ABORT);
if (!nft_prepare(h))
return 0;
return nft_action(h, NFT_COMPAT_COMMIT);
}
int nft_abort_policy_rule(struct nft_handle *h, const char *table)
int nft_bridge_commit(struct nft_handle *h)
{
struct obj_update *n, *tmp;
if (!nft_prepare(h))
return 0;
list_for_each_entry_safe(n, tmp, &h->obj_list, head) {
if (n->type != NFT_COMPAT_RULE_APPEND &&
n->type != NFT_COMPAT_RULE_DELETE)
continue;
nft_bridge_commit_prepare(h);
if (strcmp(table,
nftnl_rule_get_str(n->rule, NFTNL_RULE_TABLE)))
continue;
return nft_action(h, NFT_COMPAT_COMMIT);
}
if (!nft_rule_is_policy_rule(n->rule))
continue;
int nft_abort(struct nft_handle *h)
{
struct nft_cmd *cmd, *next;
batch_obj_del(h, n);
}
return 0;
list_for_each_entry_safe(cmd, next, &h->cmd_list, head)
nft_cmd_free(cmd);
return nft_action(h, NFT_COMPAT_ABORT);
}
int nft_compatible_revision(const char *name, uint8_t rev, int opt)
......@@ -3162,8 +3410,6 @@ static int __nft_chain_zero_counters(struct nftnl_chain *c, void *data)
return -1;
}
nft_build_cache(h, c);
iter = nftnl_rule_iter_create(c);
if (iter == NULL)
return -1;
......@@ -3300,8 +3546,6 @@ static int nft_is_chain_compatible(struct nftnl_chain *c, void *data)
enum nf_inet_hooks hook;
int prio;
nft_build_cache(h, c);
if (nftnl_rule_foreach(c, nft_is_rule_compatible, NULL))
return -1;
......
......@@ -3,6 +3,8 @@
#include "xshared.h"
#include "nft-shared.h"
#include "nft-cache.h"
#include "nft-cmd.h"
#include <libiptc/linux_list.h>
enum nft_table_type {
......@@ -28,11 +30,11 @@ struct builtin_table {
};
enum nft_cache_level {
NFT_CL_NONE,
NFT_CL_TABLES,
NFT_CL_CHAINS,
NFT_CL_SETS,
NFT_CL_RULES
NFT_CL_RULES,
NFT_CL_FAKE /* must be last entry */
};
struct nft_cache {
......@@ -44,6 +46,43 @@ struct nft_cache {
} table[NFT_TABLE_MAX];
};
enum obj_update_type {
NFT_COMPAT_TABLE_ADD,
NFT_COMPAT_TABLE_FLUSH,
NFT_COMPAT_CHAIN_ADD,
NFT_COMPAT_CHAIN_USER_ADD,
NFT_COMPAT_CHAIN_USER_DEL,
NFT_COMPAT_CHAIN_USER_FLUSH,
NFT_COMPAT_CHAIN_UPDATE,
NFT_COMPAT_CHAIN_RENAME,
NFT_COMPAT_CHAIN_ZERO,
NFT_COMPAT_RULE_APPEND,
NFT_COMPAT_RULE_INSERT,
NFT_COMPAT_RULE_REPLACE,
NFT_COMPAT_RULE_DELETE,
NFT_COMPAT_RULE_FLUSH,
NFT_COMPAT_SET_ADD,
NFT_COMPAT_RULE_LIST,
NFT_COMPAT_RULE_CHECK,
NFT_COMPAT_CHAIN_RESTORE,
NFT_COMPAT_RULE_SAVE,
NFT_COMPAT_RULE_ZERO,
NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE,
NFT_COMPAT_TABLE_NEW,
};
struct cache_chain {
struct list_head head;
char *name;
};
struct nft_cache_req {
enum nft_cache_level level;
char *table;
bool all_chains;
struct list_head chain_list;
};
struct nft_handle {
int family;
struct mnl_socket *nl;
......@@ -62,10 +101,12 @@ struct nft_handle {
unsigned int cache_index;
struct nft_cache __cache[2];
struct nft_cache *cache;
enum nft_cache_level cache_level;
struct nft_cache_req cache_req;
bool restore;
bool noflush;
int8_t config_done;
struct list_head cmd_list;
bool cache_init;
/* meta data, for error reporting */
struct {
......@@ -80,7 +121,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);
......@@ -115,17 +156,24 @@ void nft_bridge_chain_postprocess(struct nft_handle *h,
struct nftnl_chain *c);
/*
* Operations with sets.
*/
struct nftnl_set *nft_set_batch_lookup_byid(struct nft_handle *h,
uint32_t set_id);
/*
* Operations with rule-set.
*/
struct nftnl_rule;
int nft_rule_append(struct nft_handle *h, const char *chain, const char *table, void *data, struct nftnl_rule *ref, bool verbose);
int nft_rule_insert(struct nft_handle *h, const char *chain, const char *table, void *data, int rulenum, bool verbose);
int nft_rule_check(struct nft_handle *h, const char *chain, const char *table, void *data, bool verbose);
int nft_rule_delete(struct nft_handle *h, const char *chain, const char *table, void *data, bool verbose);
struct nftnl_rule *nft_rule_new(struct nft_handle *h, const char *chain, const char *table, void *data);
int nft_rule_append(struct nft_handle *h, const char *chain, const char *table, struct nftnl_rule *r, struct nftnl_rule *ref, bool verbose);
int nft_rule_insert(struct nft_handle *h, const char *chain, const char *table, struct nftnl_rule *r, int rulenum, bool verbose);
int nft_rule_check(struct nft_handle *h, const char *chain, const char *table, struct nftnl_rule *r, bool verbose);
int nft_rule_delete(struct nft_handle *h, const char *chain, const char *table, struct nftnl_rule *r, bool verbose);
int nft_rule_delete_num(struct nft_handle *h, const char *chain, const char *table, int rulenum, bool verbose);
int nft_rule_replace(struct nft_handle *h, const char *chain, const char *table, void *data, int rulenum, bool verbose);
int nft_rule_replace(struct nft_handle *h, const char *chain, const char *table, struct nftnl_rule *r, int rulenum, bool verbose);
int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, int rulenum, unsigned int format);
int nft_rule_list_save(struct nft_handle *h, const char *chain, const char *table, int rulenum, int counters);
int nft_rule_save(struct nft_handle *h, const char *table, unsigned int format);
......@@ -159,7 +207,6 @@ uint32_t nft_invflags2cmp(uint32_t invflags, uint32_t flag);
int nft_commit(struct nft_handle *h);
int nft_bridge_commit(struct nft_handle *h);
int nft_abort(struct nft_handle *h);
int nft_abort_policy_rule(struct nft_handle *h, const char *table);
/*
* revision compatibility.
......@@ -178,6 +225,7 @@ int nft_init_arp(struct nft_handle *h, const char *pname);
int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table, bool restore);
/* For xtables-eb.c */
int nft_init_eb(struct nft_handle *h, const char *pname);
void nft_fini_eb(struct nft_handle *h);
int ebt_get_current_chain(const char *chain);
int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table, bool restore);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment