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

New upstream version 1.8.1

parent f1f129da
#!/bin/bash
# make sure rules are not counted in references of iptables output
set -e
$XT_MULTI iptables -N foo
$XT_MULTI iptables -L | grep 'Chain foo (0 references)'
$XT_MULTI iptables -A foo -j ACCEPT
$XT_MULTI iptables -L | grep 'Chain foo (0 references)'
$XT_MULTI iptables -A FORWARD -j foo
$XT_MULTI iptables -L | grep 'Chain foo (1 references)'
#!/bin/bash
set -e
#set -x
# ensure verbose output is identical between legacy and nft tools
RULE1='-i eth2 -o eth3 -s 10.0.0.1 -d 10.0.0.2 -j ACCEPT'
VOUT1='ACCEPT all opt -- in eth2 out eth3 10.0.0.1 -> 10.0.0.2'
RULE2='-i eth2 -o eth3 -s 10.0.0.4 -d 10.0.0.5 -j ACCEPT'
VOUT2='ACCEPT all opt -- in eth2 out eth3 10.0.0.4 -> 10.0.0.5'
diff -u -Z <(echo -e "$VOUT1") <($XT_MULTI iptables -v -A FORWARD $RULE1)
diff -u -Z <(echo -e "$VOUT2") <($XT_MULTI iptables -v -I FORWARD 2 $RULE2)
diff -u -Z <(echo -e "$VOUT1") <($XT_MULTI iptables -v -C FORWARD $RULE1)
diff -u -Z <(echo -e "$VOUT2") <($XT_MULTI iptables -v -C FORWARD $RULE2)
EXPECT='Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 ACCEPT all -- eth2 eth3 10.0.0.1 10.0.0.2
0 0 ACCEPT all -- eth2 eth3 10.0.0.4 10.0.0.5
Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination'
diff -u -Z <(echo -e "$EXPECT") <($XT_MULTI iptables -v -n -L)
diff -u -Z <(echo -e "$VOUT1") <($XT_MULTI iptables -v -D FORWARD $RULE1)
diff -u -Z <(echo -e "$VOUT2") <($XT_MULTI iptables -v -D FORWARD $RULE2)
EXPECT="Flushing chain \`INPUT'
Flushing chain \`FORWARD'
Flushing chain \`OUTPUT'"
diff -u <(echo -e "$EXPECT") <($XT_MULTI iptables -v -F)
EXPECT="Zeroing chain \`INPUT'
Zeroing chain \`FORWARD'
Zeroing chain \`OUTPUT'"
diff -u <(echo -e "$EXPECT") <($XT_MULTI iptables -v -Z)
diff -u <(echo "Flushing chain \`OUTPUT'") <($XT_MULTI iptables -v -F OUTPUT)
diff -u <(echo "Zeroing chain \`OUTPUT'") <($XT_MULTI iptables -v -Z OUTPUT)
$XT_MULTI iptables -N foo
diff -u <(echo "Deleting chain \`foo'") <($XT_MULTI iptables -v -X foo)
#!/bin/bash
set -e
$XT_MULTI iptables -N foo
$XT_MULTI iptables -A FORWARD -i eth23 -o eth42 -j ACCEPT
$XT_MULTI iptables -A FORWARD -i eth42 -o eth23 -g foo
$XT_MULTI iptables -t nat -A OUTPUT -o eth123 -m mark --mark 0x42 -j ACCEPT
EXPECT='-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-N foo
-A FORWARD -i eth23 -o eth42 -j ACCEPT
-A FORWARD -i eth42 -o eth23 -g foo'
diff -u -Z <(echo -e "$EXPECT") <($XT_MULTI iptables -S)
EXPECT='-P INPUT ACCEPT -c 0 0
-P FORWARD ACCEPT -c 0 0
-P OUTPUT ACCEPT -c 0 0
-N foo
-A FORWARD -i eth23 -o eth42 -c 0 0 -j ACCEPT
-A FORWARD -i eth42 -o eth23 -c 0 0 -g foo'
diff -u -Z <(echo -e "$EXPECT") <($XT_MULTI iptables -v -S)
EXPECT='-P FORWARD ACCEPT
-A FORWARD -i eth23 -o eth42 -j ACCEPT
-A FORWARD -i eth42 -o eth23 -g foo'
diff -u -Z <(echo -e "$EXPECT") <($XT_MULTI iptables -S FORWARD)
EXPECT='-P FORWARD ACCEPT -c 0 0
-A FORWARD -i eth23 -o eth42 -c 0 0 -j ACCEPT
-A FORWARD -i eth42 -o eth23 -c 0 0 -g foo'
diff -u -Z <(echo -e "$EXPECT") <($XT_MULTI iptables -v -S FORWARD)
EXPECT='-P OUTPUT ACCEPT
-A OUTPUT -o eth123 -m mark --mark 0x42 -j ACCEPT'
diff -u -Z <(echo -e "$EXPECT") <($XT_MULTI iptables -t nat -S OUTPUT)
EXPECT='-P OUTPUT ACCEPT -c 0 0
-A OUTPUT -o eth123 -m mark --mark 0x42 -c 0 0 -j ACCEPT'
diff -u -Z <(echo -e "$EXPECT") <($XT_MULTI iptables -v -t nat -S OUTPUT)
# some of the following commands are supposed to fail
set +e
$XT_MULTI iptables -S nonexistent && {
echo "list-rules in non-existent chain should fail"
exit 1
}
$XT_MULTI iptables -S nonexistent 23 && {
echo "list-rules in non-existent chain with given rule number should fail"
exit 1
}
$XT_MULTI iptables -S FORWARD 234 || {
echo "list-rules in existent chain with invalid rule number should succeed"
exit 1
}
#!/bin/sh
# make sure error return codes are as expected useful cases
# (e.g. commands to check ruleset state)
global_rc=0
cmd() { # (rc, cmd, [args ...])
rc_exp=$1; shift
$XT_MULTI "$@"
rc=$?
[ $rc -eq $rc_exp ] || {
echo "---> expected $rc_exp, got $rc for command '$@'"
global_rc=1
}
}
# test chain creation
cmd 0 iptables -N foo
cmd 1 iptables -N foo
# iptables-nft allows this - bug or feature?
#cmd 2 iptables -N "invalid name"
# test rule adding
cmd 0 iptables -A INPUT -j ACCEPT
cmd 1 iptables -A noexist -j ACCEPT
# test rule checking
cmd 0 iptables -C INPUT -j ACCEPT
cmd 1 iptables -C FORWARD -j ACCEPT
cmd 1 iptables -C nonexist -j ACCEPT
cmd 2 iptables -C INPUT -j foobar
cmd 2 iptables -C INPUT -m foobar -j ACCEPT
cmd 3 iptables -t foobar -C INPUT -j ACCEPT
exit $global_rc
#!/bin/sh
# test case for bug fixed in
# commit 873c5d5d293991ee3c06aed2b1dfc5764872582f (HEAD -> master)
# 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
;;
*)
echo skip $XT_MULTI
;;
esac
exit 0
#!/bin/sh
set -e
[[ $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
$XT_MULTI ebtables -A INPUT -p IPv4 --ip-src 10.0.0.1 ! -i lo -j ACCEPT
#!/bin/bash
set -e
[[ $XT_MULTI == */xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
comment1="foo bar"
comment2="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
for ipt in iptables ip6tables; do
for comment in "$comment1" "$comment2"; do
$XT_MULTI $ipt -A INPUT -m comment --comment "$comment" -j ACCEPT
$XT_MULTI $ipt -D INPUT -m comment --comment "$comment" -j ACCEPT
done
done
#include <config.h>
#include <ctype.h>
#include <getopt.h>
#include <errno.h>
#include <libgen.h>
#include <netdb.h>
#include <stdbool.h>
......@@ -16,9 +19,6 @@
#include <math.h>
#include "xshared.h"
#define XT_LOCK_NAME "/run/xtables.lock"
#define BASE_MICROSECONDS 100000
/*
* Print out any special helps. A user might like to be able to add a --help
* to the commandline, and see expected results. So we call help for all
......@@ -247,51 +247,106 @@ void xs_init_match(struct xtables_match *match)
match->init(match->m);
}
bool xtables_lock(int wait, struct timeval *wait_interval)
static int xtables_lock(int wait, struct timeval *wait_interval)
{
struct timeval time_left, wait_time, waited_time;
struct timeval time_left, wait_time;
int fd, i = 0;
time_left.tv_sec = wait;
time_left.tv_usec = 0;
waited_time.tv_sec = 0;
waited_time.tv_usec = 0;
fd = open(XT_LOCK_NAME, O_CREAT, 0600);
if (fd < 0)
return true;
if (fd < 0) {
fprintf(stderr, "Fatal: can't open lock file %s: %s\n",
XT_LOCK_NAME, strerror(errno));
return XT_LOCK_FAILED;
}
if (wait == -1) {
if (flock(fd, LOCK_EX) == 0)
return fd;
fprintf(stderr, "Can't lock %s: %s\n", XT_LOCK_NAME,
strerror(errno));
return XT_LOCK_BUSY;
}
while (1) {
if (flock(fd, LOCK_EX | LOCK_NB) == 0)
return true;
return fd;
else if (timercmp(&time_left, wait_interval, <))
return XT_LOCK_BUSY;
if (++i % 10 == 0) {
if (wait != -1)
fprintf(stderr, "Another app is currently holding the xtables lock; "
"still %lds %ldus time ahead to have a chance to grab the lock...\n",
time_left.tv_sec, time_left.tv_usec);
else
fprintf(stderr, "Another app is currently holding the xtables lock; "
"waiting for it to exit...\n");
}
wait_time = *wait_interval;
select(0, NULL, NULL, NULL, &wait_time);
if (wait == -1)
continue;
timeradd(&waited_time, wait_interval, &waited_time);
timersub(&time_left, wait_interval, &time_left);
if (!timerisset(&time_left))
return false;
}
}
void parse_wait_interval(const char *str, struct timeval *wait_interval)
void xtables_unlock(int lock)
{
if (lock >= 0)
close(lock);
}
int xtables_lock_or_exit(int wait, struct timeval *wait_interval)
{
int lock = xtables_lock(wait, wait_interval);
if (lock == XT_LOCK_FAILED) {
xtables_free_opts(1);
exit(RESOURCE_PROBLEM);
}
if (lock == XT_LOCK_BUSY) {
fprintf(stderr, "Another app is currently holding the xtables lock. ");
if (wait == 0)
fprintf(stderr, "Perhaps you want to use the -w option?\n");
else
fprintf(stderr, "Stopped waiting after %ds.\n", wait);
xtables_free_opts(1);
exit(RESOURCE_PROBLEM);
}
return lock;
}
int parse_wait_time(int argc, char *argv[])
{
int wait = -1;
if (optarg) {
if (sscanf(optarg, "%i", &wait) != 1)
xtables_error(PARAMETER_PROBLEM,
"wait seconds not numeric");
} else if (xs_has_arg(argc, argv))
if (sscanf(argv[optind++], "%i", &wait) != 1)
xtables_error(PARAMETER_PROBLEM,
"wait seconds not numeric");
return wait;
}
void parse_wait_interval(int argc, char *argv[], struct timeval *wait_interval)
{
const char *arg;
unsigned int usec;
int ret;
ret = sscanf(str, "%u", &usec);
if (optarg)
arg = optarg;
else if (xs_has_arg(argc, argv))
arg = argv[optind++];
else
xtables_error(PARAMETER_PROBLEM, "wait interval value required");
ret = sscanf(arg, "%u", &usec);
if (ret == 1) {
if (usec > 999999)
xtables_error(PARAMETER_PROBLEM,
......@@ -304,3 +359,338 @@ void parse_wait_interval(const char *str, struct timeval *wait_interval)
}
xtables_error(PARAMETER_PROBLEM, "wait interval not numeric");
}
int parse_counters(const char *string, struct xt_counters *ctr)
{
int ret;
if (!string)
return 0;
ret = sscanf(string, "[%llu:%llu]",
(unsigned long long *)&ctr->pcnt,
(unsigned long long *)&ctr->bcnt);
return ret == 2;
}
inline bool xs_has_arg(int argc, char *argv[])
{
return optind < argc &&
argv[optind][0] != '-' &&
argv[optind][0] != '!';
}
/* global new argv and argc */
char *newargv[255];
int newargc = 0;
/* saved newargv and newargc from save_argv() */
char *oldargv[255];
int oldargc = 0;
/* arg meta data, were they quoted, frinstance */
int newargvattr[255];
/* function adding one argument to newargv, updating newargc
* returns true if argument added, false otherwise */
int add_argv(const char *what, int quoted)
{
DEBUGP("add_argv: %s\n", what);
if (what && newargc + 1 < ARRAY_SIZE(newargv)) {
newargv[newargc] = strdup(what);
newargvattr[newargc] = quoted;
newargv[++newargc] = NULL;
return 1;
} else {
xtables_error(PARAMETER_PROBLEM,
"Parser cannot handle more arguments\n");
}
}
void free_argv(void)
{
while (newargc)
free(newargv[--newargc]);
while (oldargc)
free(oldargv[--oldargc]);
}
/* Save parsed rule for comparison with next rule to perform action aggregation
* on duplicate conditions.
*/
void save_argv(void)
{
unsigned int i;
while (oldargc)
free(oldargv[--oldargc]);
oldargc = newargc;
newargc = 0;
for (i = 0; i < oldargc; i++) {
oldargv[i] = newargv[i];
}
}
void add_param_to_argv(char *parsestart, int line)
{
int quote_open = 0, escaped = 0, param_len = 0;
char param_buffer[1024], *curchar;
/* After fighting with strtok enough, here's now
* a 'real' parser. According to Rusty I'm now no
* longer a real hacker, but I can live with that */
for (curchar = parsestart; *curchar; curchar++) {
if (quote_open) {
if (escaped) {
param_buffer[param_len++] = *curchar;
escaped = 0;
continue;
} else if (*curchar == '\\') {
escaped = 1;
continue;
} else if (*curchar == '"') {
quote_open = 0;
*curchar = '"';
} else {
param_buffer[param_len++] = *curchar;
continue;
}
} else {
if (*curchar == '"') {
quote_open = 1;
continue;
}
}
switch (*curchar) {
case '"':
break;
case ' ':
case '\t':
case '\n':
if (!param_len) {
/* two spaces? */
continue;
}
break;
default:
/* regular character, copy to buffer */
param_buffer[param_len++] = *curchar;
if (param_len >= sizeof(param_buffer))
xtables_error(PARAMETER_PROBLEM,
"Parameter too long!");
continue;
}
param_buffer[param_len] = '\0';
/* check if table name specified */
if ((param_buffer[0] == '-' &&
param_buffer[1] != '-' &&
strchr(param_buffer, 't')) ||
(!strncmp(param_buffer, "--t", 3) &&
!strncmp(param_buffer, "--table", strlen(param_buffer)))) {
xtables_error(PARAMETER_PROBLEM,
"The -t option (seen in line %u) cannot be used in %s.\n",
line, xt_params->program_name);
}
add_argv(param_buffer, 0);
param_len = 0;
}
}
static const char *ipv4_addr_to_string(const struct in_addr *addr,
const struct in_addr *mask,
unsigned int format)
{
static char buf[BUFSIZ];
if (!mask->s_addr && !(format & FMT_NUMERIC))
return "anywhere";
if (format & FMT_NUMERIC)
strncpy(buf, xtables_ipaddr_to_numeric(addr), BUFSIZ - 1);
else
strncpy(buf, xtables_ipaddr_to_anyname(addr), BUFSIZ - 1);
buf[BUFSIZ - 1] = '\0';
strncat(buf, xtables_ipmask_to_numeric(mask),
BUFSIZ - strlen(buf) - 1);
return buf;
}
void print_ipv4_addresses(const struct ipt_entry *fw, unsigned int format)
{
fputc(fw->ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout);
printf(FMT("%-19s ", "%s "),
ipv4_addr_to_string(&fw->ip.src, &fw->ip.smsk, format));
fputc(fw->ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout);
printf(FMT("%-19s ", "-> %s"),
ipv4_addr_to_string(&fw->ip.dst, &fw->ip.dmsk, format));
}
static const char *ipv6_addr_to_string(const struct in6_addr *addr,
const struct in6_addr *mask,
unsigned int format)
{
static char buf[BUFSIZ];
if (IN6_IS_ADDR_UNSPECIFIED(addr) && !(format & FMT_NUMERIC))
return "anywhere";
if (format & FMT_NUMERIC)
strncpy(buf, xtables_ip6addr_to_numeric(addr), BUFSIZ - 1);
else
strncpy(buf, xtables_ip6addr_to_anyname(addr), BUFSIZ - 1);
buf[BUFSIZ - 1] = '\0';
strncat(buf, xtables_ip6mask_to_numeric(mask),
BUFSIZ - strlen(buf) - 1);
return buf;
}
void print_ipv6_addresses(const struct ip6t_entry *fw6, unsigned int format)
{
fputc(fw6->ipv6.invflags & IP6T_INV_SRCIP ? '!' : ' ', stdout);
printf(FMT("%-19s ", "%s "),
ipv6_addr_to_string(&fw6->ipv6.src,
&fw6->ipv6.smsk, format));
fputc(fw6->ipv6.invflags & IP6T_INV_DSTIP ? '!' : ' ', stdout);
printf(FMT("%-19s ", "-> %s"),
ipv6_addr_to_string(&fw6->ipv6.dst,
&fw6->ipv6.dmsk, format));
}
/* Luckily, IPT_INV_VIA_IN and IPT_INV_VIA_OUT
* have the same values as IP6T_INV_VIA_IN and IP6T_INV_VIA_OUT
* so this function serves for both iptables and ip6tables */
void print_ifaces(const char *iniface, const char *outiface, uint8_t invflags,
unsigned int format)
{
const char *anyname = format & FMT_NUMERIC ? "*" : "any";
char iface[IFNAMSIZ + 2];
if (!(format & FMT_VIA))
return;
snprintf(iface, IFNAMSIZ + 2, "%s%s",
invflags & IPT_INV_VIA_IN ? "!" : "",
iniface[0] != '\0' ? iniface : anyname);
printf(FMT(" %-6s ", "in %s "), iface);
snprintf(iface, IFNAMSIZ + 2, "%s%s",
invflags & IPT_INV_VIA_OUT ? "!" : "",
outiface[0] != '\0' ? outiface : anyname);
printf(FMT("%-6s ", "out %s "), iface);
}
void command_match(struct iptables_command_state *cs)
{
struct option *opts = xt_params->opts;
struct xtables_match *m;
size_t size;
if (cs->invert)
xtables_error(PARAMETER_PROBLEM,
"unexpected ! flag before --match");
m = xtables_find_match(optarg, XTF_LOAD_MUST_SUCCEED, &cs->matches);
size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size;
m->m = xtables_calloc(1, size);
m->m->u.match_size = size;
if (m->real_name == NULL) {
strcpy(m->m->u.user.name, m->name);
} else {
strcpy(m->m->u.user.name, m->real_name);
if (!(m->ext_flags & XTABLES_EXT_ALIAS))
fprintf(stderr, "Notice: the %s match is converted into %s match "
"in rule listing and saving.\n", m->name, m->real_name);
}
m->m->u.user.revision = m->revision;
xs_init_match(m);
if (m == m->next)
return;
/* Merge options for non-cloned matches */
if (m->x6_options != NULL)
opts = xtables_options_xfrm(xt_params->orig_opts, opts,
m->x6_options, &m->option_offset);
else if (m->extra_opts != NULL)
opts = xtables_merge_options(xt_params->orig_opts, opts,
m->extra_opts, &m->option_offset);
if (opts == NULL)
xtables_error(OTHER_PROBLEM, "can't alloc memory!");
xt_params->opts = opts;
}
const char *xt_parse_target(const char *targetname)
{
const char *ptr;
if (strlen(targetname) < 1)
xtables_error(PARAMETER_PROBLEM,
"Invalid target name (too short)");
if (strlen(targetname) >= XT_EXTENSION_MAXNAMELEN)
xtables_error(PARAMETER_PROBLEM,
"Invalid target name `%s' (%u chars max)",
targetname, XT_EXTENSION_MAXNAMELEN - 1);
for (ptr = targetname; *ptr; ptr++)
if (isspace(*ptr))
xtables_error(PARAMETER_PROBLEM,
"Invalid target name `%s'", targetname);
return targetname;
}
void command_jump(struct iptables_command_state *cs)
{
struct option *opts = xt_params->opts;
size_t size;
cs->jumpto = xt_parse_target(optarg);
/* TRY_LOAD (may be chain name) */
cs->target = xtables_find_target(cs->jumpto, XTF_TRY_LOAD);
if (cs->target == NULL)
return;
size = XT_ALIGN(sizeof(struct xt_entry_target)) + cs->target->size;
cs->target->t = xtables_calloc(1, size);
cs->target->t->u.target_size = size;
if (cs->target->real_name == NULL) {
strcpy(cs->target->t->u.user.name, cs->jumpto);
} else {
/* Alias support for userspace side */
strcpy(cs->target->t->u.user.name, cs->target->real_name);
if (!(cs->target->ext_flags & XTABLES_EXT_ALIAS))
fprintf(stderr, "Notice: The %s target is converted into %s target "
"in rule listing and saving.\n",
cs->jumpto, cs->target->real_name);
}
cs->target->t->u.user.revision = cs->target->revision;
xs_init_target(cs->target);
if (cs->target->x6_options != NULL)
opts = xtables_options_xfrm(xt_params->orig_opts, opts,
cs->target->x6_options,
&cs->target->option_offset);
else
opts = xtables_merge_options(xt_params->orig_opts, opts,
cs->target->extra_opts,
&cs->target->option_offset);
if (opts == NULL)
xtables_error(OTHER_PROBLEM, "can't alloc memory!");
xt_params->opts = opts;
}
......@@ -6,9 +6,16 @@
#include <stdint.h>
#include <netinet/in.h>
#include <net/if.h>
#include <linux/netfilter_arp/arp_tables.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv6/ip6_tables.h>
#ifdef DEBUG
#define DEBUGP(x, args...) fprintf(stdout, x, ## args)
#else
#define DEBUGP(x, args...)
#endif
enum {
OPT_NONE = 0,
OPT_NUMERIC = 1 << 0,
......@@ -48,15 +55,48 @@ struct xtables_afinfo {
int so_rev_target;
};
/* trick for ebtables-compat, since watchers are targets */
struct ebt_match {
struct ebt_match *next;
union {
struct xtables_match *match;
struct xtables_target *watcher;
} u;
bool ismatch;
};
/* Fake ebt_entry */
struct ebt_entry {
/* this needs to be the first field */
unsigned int bitmask;
unsigned int invflags;
uint16_t ethproto;
/* the physical in-dev */
char in[IFNAMSIZ];
/* the logical in-dev */
char logical_in[IFNAMSIZ];
/* the physical out-dev */
char out[IFNAMSIZ];
/* the logical out-dev */
char logical_out[IFNAMSIZ];
unsigned char sourcemac[6];
unsigned char sourcemsk[6];
unsigned char destmac[6];
unsigned char destmsk[6];
};
struct iptables_command_state {
union {
struct ebt_entry eb;
struct ipt_entry fw;
struct ip6t_entry fw6;
struct arpt_entry arp;
};
int invert;
int c;
unsigned int options;
struct xtables_rule_match *matches;
struct ebt_match *match_list;
struct xtables_target *target;
struct xt_counters counters;
char *protocol;
......@@ -86,10 +126,56 @@ extern struct xtables_match *load_proto(struct iptables_command_state *);
extern int subcmd_main(int, char **, const struct subcommand *);
extern void xs_init_target(struct xtables_target *);
extern void xs_init_match(struct xtables_match *);
bool xtables_lock(int wait, struct timeval *wait_interval);
void parse_wait_interval(const char *str, struct timeval *wait_interval);
/**
* Values for the iptables lock.
*
* A value >= 0 indicates the lock filedescriptor. Other values are:
*
* XT_LOCK_FAILED : The lock could not be acquired.
*
* XT_LOCK_BUSY : The lock was held by another process. xtables_lock only
* returns this value when |wait| == false. If |wait| == true, xtables_lock
* will not return unless the lock has been acquired.
*
* XT_LOCK_NOT_ACQUIRED : We have not yet attempted to acquire the lock.
*/
enum {
XT_LOCK_BUSY = -1,
XT_LOCK_FAILED = -2,
XT_LOCK_NOT_ACQUIRED = -3,
};
extern void xtables_unlock(int lock);
extern int xtables_lock_or_exit(int wait, struct timeval *tv);
int parse_wait_time(int argc, char *argv[]);
void parse_wait_interval(int argc, char *argv[], struct timeval *wait_interval);
int parse_counters(const char *string, struct xt_counters *ctr);
bool xs_has_arg(int argc, char *argv[]);
extern const struct xtables_afinfo *afinfo;
extern char *newargv[];
extern int newargc;
extern char *oldargv[];
extern int oldargc;
extern int newargvattr[];
int add_argv(const char *what, int quoted);
void free_argv(void);
void save_argv(void);
void add_param_to_argv(char *parsestart, int line);
void print_ipv4_addresses(const struct ipt_entry *fw, unsigned int format);
void print_ipv6_addresses(const struct ip6t_entry *fw6, unsigned int format);
void print_ifaces(const char *iniface, const char *outiface, uint8_t invflags,
unsigned int format);
void command_match(struct iptables_command_state *cs);
const char *xt_parse_target(const char *targetname);
void command_jump(struct iptables_command_state *cs);
#endif /* IPTABLES_XSHARED_H */
......@@ -47,24 +47,11 @@ int xtables_arp_main(int argc, char *argv[])
{
int ret;
char *table = "filter";
struct nft_handle h = {
.family = NFPROTO_ARP,
};
struct nft_handle h;
arptables_globals.program_name = "arptables";
ret = xtables_init_all(&arptables_globals, NFPROTO_ARP);
if (ret < 0) {
fprintf(stderr, "%s/%s Failed to initialize arptables-compat\n",
arptables_globals.program_name,
arptables_globals.program_version);
exit(1);
}
nft_init_arp(&h, "arptables");
#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
init_extensionsa();
#endif
ret = do_commandarp(&h, argc, argv, &table);
ret = do_commandarp(&h, argc, argv, &table, false);
if (ret)
ret = nft_commit(&h);
......
......@@ -149,8 +149,7 @@ static struct option original_opts[] = {
int RUNTIME_NF_ARP_NUMHOOKS = 3;
static struct option *opts = original_opts;
static unsigned int global_option_offset = 0;
#define opts xt_params->opts
extern void xtables_exit_error(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3)));
struct xtables_globals arptables_globals = {
......@@ -588,16 +587,15 @@ static struct in_addr *
host_to_addr(const char *name, unsigned int *naddr)
{
struct in_addr *addr;
struct addrinfo hints;
struct addrinfo hints = {
.ai_flags = AI_CANONNAME,
.ai_family = AF_INET,
.ai_socktype = SOCK_RAW,
};;
struct addrinfo *res, *p;
int err;
unsigned int i;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_CANONNAME;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_RAW;
*naddr = 0;
err = getaddrinfo(name, NULL, &hints, &res);
if (err != 0)
......@@ -757,27 +755,6 @@ parse_rulenumber(const char *rule)
return rulenum;
}
static const char *
parse_target(const char *targetname)
{
const char *ptr;
if (strlen(targetname) < 1)
xtables_error(PARAMETER_PROBLEM,
"Invalid target name (too short)");
if (strlen(targetname)+1 > sizeof(arpt_chainlabel))
xtables_error(PARAMETER_PROBLEM,
"Invalid target name `%s' (%zu chars max)",
targetname, sizeof(arpt_chainlabel)-1);
for (ptr = targetname; *ptr; ptr++)
if (isspace(*ptr))
xtables_error(PARAMETER_PROBLEM,
"Invalid target name `%s'", targetname);
return targetname;
}
static void
set_option(unsigned int *options, unsigned int option, u_int16_t *invflg,
int invert)
......@@ -824,46 +801,11 @@ list_entries(struct nft_handle *h, const char *chain, const char *table,
return nft_rule_list(h, chain, table, rulenum, format);
}
static struct xtables_target *command_jump(struct arpt_entry *fw,
const char *jumpto)
{
struct xtables_target *target;
size_t size;
/* XTF_TRY_LOAD (may be chain name) */
target = xtables_find_target(jumpto, XTF_TRY_LOAD);
if (!target)
return NULL;
size = XT_ALIGN(sizeof(struct xt_entry_target))
+ target->size;
target->t = xtables_calloc(1, size);
target->t->u.target_size = size;
strncpy(target->t->u.user.name, jumpto, sizeof(target->t->u.user.name));
target->t->u.user.name[sizeof(target->t->u.user.name)-1] = '\0';
target->t->u.user.revision = target->revision;
xs_init_target(target);
if (target->x6_options != NULL)
opts = xtables_options_xfrm(arptables_globals.orig_opts,
opts, target->x6_options,
&target->option_offset);
else
opts = xtables_merge_options(arptables_globals.orig_opts,
opts, target->extra_opts,
&target->option_offset);
return target;
}
static int
append_entry(struct nft_handle *h,
const char *chain,
const char *table,
struct arptables_command_state *cs,
struct iptables_command_state *cs,
int rulenum,
unsigned int nsaddrs,
const struct in_addr saddrs[],
......@@ -875,9 +817,9 @@ append_entry(struct nft_handle *h,
int ret = 1;
for (i = 0; i < nsaddrs; i++) {
cs->fw.arp.src.s_addr = saddrs[i].s_addr;
cs->arp.arp.src.s_addr = saddrs[i].s_addr;
for (j = 0; j < ndaddrs; j++) {
cs->fw.arp.tgt.s_addr = daddrs[j].s_addr;
cs->arp.arp.tgt.s_addr = daddrs[j].s_addr;
if (append) {
ret = nft_rule_append(h, chain, table, cs, 0,
verbose);
......@@ -894,14 +836,14 @@ append_entry(struct nft_handle *h,
static int
replace_entry(const char *chain,
const char *table,
struct arptables_command_state *cs,
struct iptables_command_state *cs,
unsigned int rulenum,
const struct in_addr *saddr,
const struct in_addr *daddr,
bool verbose, struct nft_handle *h)
{
cs->fw.arp.src.s_addr = saddr->s_addr;
cs->fw.arp.tgt.s_addr = daddr->s_addr;
cs->arp.arp.src.s_addr = saddr->s_addr;
cs->arp.arp.tgt.s_addr = daddr->s_addr;
return nft_rule_replace(h, chain, table, cs, rulenum, verbose);
}
......@@ -909,7 +851,7 @@ replace_entry(const char *chain,
static int
delete_entry(const char *chain,
const char *table,
struct arptables_command_state *cs,
struct iptables_command_state *cs,
unsigned int nsaddrs,
const struct in_addr saddrs[],
unsigned int ndaddrs,
......@@ -920,9 +862,9 @@ delete_entry(const char *chain,
int ret = 1;
for (i = 0; i < nsaddrs; i++) {
cs->fw.arp.src.s_addr = saddrs[i].s_addr;
cs->arp.arp.src.s_addr = saddrs[i].s_addr;
for (j = 0; j < ndaddrs; j++) {
cs->fw.arp.tgt.s_addr = daddrs[j].s_addr;
cs->arp.arp.tgt.s_addr = daddrs[j].s_addr;
ret = nft_rule_delete(h, chain, table, cs, verbose);
}
}
......@@ -930,9 +872,40 @@ delete_entry(const char *chain,
return ret;
}
int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
int nft_init_arp(struct nft_handle *h, const char *pname)
{
arptables_globals.program_name = pname;
if (xtables_init_all(&arptables_globals, NFPROTO_ARP) < 0) {
fprintf(stderr, "%s/%s Failed to initialize arptables-compat\n",
arptables_globals.program_name,
arptables_globals.program_version);
exit(1);
}
#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
init_extensionsa();
#endif
memset(h, 0, sizeof(*h));
h->family = NFPROTO_ARP;
if (nft_init(h, 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;
}
int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table,
bool restore)
{
struct arptables_command_state cs;
struct iptables_command_state cs = {
.jumpto = "",
};
int invert = 0;
unsigned int nsaddrs = 0, ndaddrs = 0;
struct in_addr *saddrs = NULL, *daddrs = NULL;
......@@ -946,14 +919,6 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
int ret = 1;
struct xtables_target *t;
memset(&cs, 0, sizeof(cs));
cs.jumpto = "";
opts = original_opts;
global_option_offset = 0;
xtables_globals.orig_opts = original_opts;
/* re-set optind to 0 in case do_command gets called
* a second time */
optind = 0;
......@@ -967,6 +932,7 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
demand-load a protocol. */
opterr = 0;
opts = xt_params->orig_opts;
while ((c = getopt_long(argc, argv,
"-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:l:i:vnt:m:c:",
opts, NULL)) != -1) {
......@@ -984,8 +950,7 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
add_command(&command, CMD_DELETE, CMD_NONE,
invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!') {
if (xs_has_arg(argc, argv)) {
rulenum = parse_rulenumber(argv[optind++]);
command = CMD_DELETE_NUM;
}
......@@ -995,8 +960,7 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
add_command(&command, CMD_REPLACE, CMD_NONE,
invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
if (xs_has_arg(argc, argv))
rulenum = parse_rulenumber(argv[optind++]);
else
xtables_error(PARAMETER_PROBLEM,
......@@ -1008,8 +972,7 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
add_command(&command, CMD_INSERT, CMD_NONE,
invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
if (xs_has_arg(argc, argv))
rulenum = parse_rulenumber(argv[optind++]);
else rulenum = 1;
break;
......@@ -1018,8 +981,7 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
add_command(&command, CMD_LIST, CMD_ZERO,
invert);
if (optarg) chain = optarg;
else if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
else if (xs_has_arg(argc, argv))
chain = argv[optind++];
break;
......@@ -1027,8 +989,7 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
add_command(&command, CMD_FLUSH, CMD_NONE,
invert);
if (optarg) chain = optarg;
else if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
else if (xs_has_arg(argc, argv))
chain = argv[optind++];
break;
......@@ -1036,8 +997,7 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
add_command(&command, CMD_ZERO, CMD_LIST,
invert);
if (optarg) chain = optarg;
else if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
else if (xs_has_arg(argc, argv))
chain = argv[optind++];
break;
......@@ -1059,8 +1019,7 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
invert);
if (optarg) chain = optarg;
else if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
else if (xs_has_arg(argc, argv))
chain = argv[optind++];
break;
......@@ -1068,8 +1027,7 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
if (xs_has_arg(argc, argv))
newname = argv[optind++];
else
xtables_error(PARAMETER_PROBLEM,
......@@ -1082,8 +1040,7 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
add_command(&command, CMD_SET_POLICY, CMD_NONE,
invert);
chain = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
if (xs_has_arg(argc, argv))
policy = argv[optind++];
else
xtables_error(PARAMETER_PROBLEM,
......@@ -1099,47 +1056,47 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
break;
case 's':
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_S_IP, &cs.fw.arp.invflags,
set_option(&options, OPT_S_IP, &cs.arp.arp.invflags,
invert);
shostnetworkmask = argv[optind-1];
break;
case 'd':
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_D_IP, &cs.fw.arp.invflags,
set_option(&options, OPT_D_IP, &cs.arp.arp.invflags,
invert);
dhostnetworkmask = argv[optind-1];
break;
case 2:/* src-mac */
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_S_MAC, &cs.fw.arp.invflags,
set_option(&options, OPT_S_MAC, &cs.arp.arp.invflags,
invert);
if (getmac_and_mask(argv[optind - 1],
cs.fw.arp.src_devaddr.addr, cs.fw.arp.src_devaddr.mask))
cs.arp.arp.src_devaddr.addr, cs.arp.arp.src_devaddr.mask))
xtables_error(PARAMETER_PROBLEM, "Problem with specified "
"source mac");
break;
case 3:/* dst-mac */
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_D_MAC, &cs.fw.arp.invflags,
set_option(&options, OPT_D_MAC, &cs.arp.arp.invflags,
invert);
if (getmac_and_mask(argv[optind - 1],
cs.fw.arp.tgt_devaddr.addr, cs.fw.arp.tgt_devaddr.mask))
cs.arp.arp.tgt_devaddr.addr, cs.arp.arp.tgt_devaddr.mask))
xtables_error(PARAMETER_PROBLEM, "Problem with specified "
"destination mac");
break;
case 'l':/* hardware length */
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_H_LENGTH, &cs.fw.arp.invflags,
set_option(&options, OPT_H_LENGTH, &cs.arp.arp.invflags,
invert);
getlength_and_mask(argv[optind - 1], &cs.fw.arp.arhln,
&cs.fw.arp.arhln_mask);
getlength_and_mask(argv[optind - 1], &cs.arp.arp.arhln,
&cs.arp.arp.arhln_mask);
if (cs.fw.arp.arhln != 6) {
if (cs.arp.arp.arhln != 6) {
xtables_error(PARAMETER_PROBLEM,
"Only harware address length of"
" 6 is supported currently.");
......@@ -1151,20 +1108,20 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
xtables_error(PARAMETER_PROBLEM, "not supported");
/*
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_P_LENGTH, &cs.fw.arp.invflags,
set_option(&options, OPT_P_LENGTH, &cs.arp.arp.invflags,
invert);
getlength_and_mask(argv[optind - 1], &cs.fw.arp.arpln,
&cs.fw.arp.arpln_mask);
getlength_and_mask(argv[optind - 1], &cs.arp.arp.arpln,
&cs.arp.arp.arpln_mask);
break;
*/
case 4:/* opcode */
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_OPCODE, &cs.fw.arp.invflags,
set_option(&options, OPT_OPCODE, &cs.arp.arp.invflags,
invert);
if (get16_and_mask(argv[optind - 1], &cs.fw.arp.arpop,
&cs.fw.arp.arpop_mask, 10)) {
if (get16_and_mask(argv[optind - 1], &cs.arp.arp.arpop,
&cs.arp.arp.arpop_mask, 10)) {
int i;
for (i = 0; i < NUMOPCODES; i++)
......@@ -1172,65 +1129,64 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
break;
if (i == NUMOPCODES)
xtables_error(PARAMETER_PROBLEM, "Problem with specified opcode");
cs.fw.arp.arpop = htons(i+1);
cs.arp.arp.arpop = htons(i+1);
}
break;
case 5:/* h-type */
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_H_TYPE, &cs.fw.arp.invflags,
set_option(&options, OPT_H_TYPE, &cs.arp.arp.invflags,
invert);
if (get16_and_mask(argv[optind - 1], &cs.fw.arp.arhrd,
&cs.fw.arp.arhrd_mask, 16)) {
if (get16_and_mask(argv[optind - 1], &cs.arp.arp.arhrd,
&cs.arp.arp.arhrd_mask, 16)) {
if (strcasecmp(argv[optind-1], "Ethernet"))
xtables_error(PARAMETER_PROBLEM, "Problem with specified hardware type");
cs.fw.arp.arhrd = htons(1);
cs.arp.arp.arhrd = htons(1);
}
break;
case 6:/* proto-type */
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_P_TYPE, &cs.fw.arp.invflags,
set_option(&options, OPT_P_TYPE, &cs.arp.arp.invflags,
invert);
if (get16_and_mask(argv[optind - 1], &cs.fw.arp.arpro,
&cs.fw.arp.arpro_mask, 0)) {
if (get16_and_mask(argv[optind - 1], &cs.arp.arp.arpro,
&cs.arp.arp.arpro_mask, 0)) {
if (strcasecmp(argv[optind-1], "ipv4"))
xtables_error(PARAMETER_PROBLEM, "Problem with specified protocol type");
cs.fw.arp.arpro = htons(0x800);
cs.arp.arp.arpro = htons(0x800);
}
break;
case 'j':
set_option(&options, OPT_JUMP, &cs.fw.arp.invflags,
set_option(&options, OPT_JUMP, &cs.arp.arp.invflags,
invert);
cs.jumpto = parse_target(optarg);
cs.target = command_jump(&cs.fw, cs.jumpto);
command_jump(&cs);
break;
case 'i':
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_VIANAMEIN, &cs.fw.arp.invflags,
set_option(&options, OPT_VIANAMEIN, &cs.arp.arp.invflags,
invert);
parse_interface(argv[optind-1],
cs.fw.arp.iniface,
cs.fw.arp.iniface_mask);
/* cs.fw.nfcache |= NFC_IP_IF_IN; */
cs.arp.arp.iniface,
cs.arp.arp.iniface_mask);
/* cs.arp.nfcache |= NFC_IP_IF_IN; */
break;
case 'o':
check_inverse(optarg, &invert, &optind, argc);
set_option(&options, OPT_VIANAMEOUT, &cs.fw.arp.invflags,
set_option(&options, OPT_VIANAMEOUT, &cs.arp.arp.invflags,
invert);
parse_interface(argv[optind-1],
cs.fw.arp.outiface,
cs.fw.arp.outiface_mask);
/* cs.fw.nfcache |= NFC_IP_IF_OUT; */
cs.arp.arp.outiface,
cs.arp.arp.outiface_mask);
/* cs.arp.nfcache |= NFC_IP_IF_OUT; */
break;
case 'v':
if (!verbose)
set_option(&options, OPT_VERBOSE,
&cs.fw.arp.invflags, invert);
&cs.arp.arp.invflags, invert);
verbose++;
break;
......@@ -1253,7 +1209,7 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
break;
case 'n':
set_option(&options, OPT_NUMERIC, &cs.fw.arp.invflags,
set_option(&options, OPT_NUMERIC, &cs.arp.arp.invflags,
invert);
break;
......@@ -1268,12 +1224,12 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
if (invert)
printf("Not %s ;-)\n", program_version);
else
printf("%s v%s\n",
printf("%s v%s (nf_tables)\n",
program_name, program_version);
exit(0);
case '0':
set_option(&options, OPT_LINENUMBERS, &cs.fw.arp.invflags,
set_option(&options, OPT_LINENUMBERS, &cs.arp.arp.invflags,
invert);
break;
......@@ -1283,23 +1239,22 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
case 'c':
set_option(&options, OPT_COUNTERS, &cs.fw.arp.invflags,
set_option(&options, OPT_COUNTERS, &cs.arp.arp.invflags,
invert);
pcnt = optarg;
if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
if (xs_has_arg(argc, argv))
bcnt = argv[optind++];
else
xtables_error(PARAMETER_PROBLEM,
"-%c requires packet and byte counter",
opt2char(OPT_COUNTERS));
if (sscanf(pcnt, "%llu", &cs.fw.counters.pcnt) != 1)
if (sscanf(pcnt, "%llu", &cs.arp.counters.pcnt) != 1)
xtables_error(PARAMETER_PROBLEM,
"-%c packet counter not numeric",
opt2char(OPT_COUNTERS));
if (sscanf(bcnt, "%llu", &cs.fw.counters.bcnt) != 1)
if (sscanf(bcnt, "%llu", &cs.arp.counters.bcnt) != 1)
xtables_error(PARAMETER_PROBLEM,
"-%c byte counter not numeric",
opt2char(OPT_COUNTERS));
......@@ -1323,7 +1278,7 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
default:
if (cs.target) {
xtables_option_tpcall(c, argv,
invert, cs.target, &cs.fw);
invert, cs.target, &cs.arp);
}
break;
}
......@@ -1351,14 +1306,14 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
if (shostnetworkmask)
parse_hostnetworkmask(shostnetworkmask, &saddrs,
&(cs.fw.arp.smsk), &nsaddrs);
&(cs.arp.arp.smsk), &nsaddrs);
if (dhostnetworkmask)
parse_hostnetworkmask(dhostnetworkmask, &daddrs,
&(cs.fw.arp.tmsk), &ndaddrs);
&(cs.arp.arp.tmsk), &ndaddrs);
if ((nsaddrs > 1 || ndaddrs > 1) &&
(cs.fw.arp.invflags & (ARPT_INV_SRCIP | ARPT_INV_TGTIP)))
(cs.arp.arp.invflags & (ARPT_INV_SRCIP | ARPT_INV_TGTIP)))
xtables_error(PARAMETER_PROBLEM, "! not allowed with multiple"
" source or destination IP addresses");
......@@ -1373,14 +1328,6 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
"chain name `%s' too long (must be under %i chars)",
chain, ARPT_FUNCTION_MAXNAMELEN);
if (nft_init(h, 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");
if (command == CMD_APPEND
|| command == CMD_DELETE
|| command == CMD_INSERT
......@@ -1404,17 +1351,6 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
opt2char(OPT_VIANAMEIN),
chain);
}
if (!cs.target && strlen(cs.jumpto) != 0) {
size_t size;
cs.target = xtables_find_target(XT_STANDARD_TARGET,
XTF_LOAD_MUST_SUCCEED);
size = sizeof(struct arpt_entry_target) + cs.target->size;
cs.target->t = xtables_calloc(1, size);
cs.target->t->u.target_size = size;
strcpy(cs.target->t->u.user.name, cs.jumpto);
}
}
switch (command) {
......@@ -1449,10 +1385,11 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
options&OPT_LINENUMBERS);
break;
case CMD_FLUSH:
ret = nft_rule_flush(h, chain, *table);
ret = nft_rule_flush(h, chain, *table, options & OPT_VERBOSE);
break;
case CMD_ZERO:
ret = nft_chain_zero_counters(h, chain, *table);
ret = nft_chain_zero_counters(h, chain, *table,
options & OPT_VERBOSE);
break;
case CMD_LIST|CMD_ZERO:
ret = list_entries(h, chain, *table, rulenum,
......@@ -1461,13 +1398,15 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
/*options&OPT_EXPANDED*/0,
options&OPT_LINENUMBERS);
if (ret)
ret = nft_chain_zero_counters(h, chain, *table);
ret = nft_chain_zero_counters(h, chain, *table,
options & OPT_VERBOSE);
break;
case CMD_NEW_CHAIN:
ret = nft_chain_user_add(h, chain, *table);
break;
case CMD_DELETE_CHAIN:
ret = nft_chain_user_del(h, chain, *table);
ret = nft_chain_user_del(h, chain, *table,
options & OPT_VERBOSE);
break;
case CMD_RENAME_CHAIN:
ret = nft_chain_user_rename(h, chain, *table, newname);
......@@ -1483,6 +1422,16 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table)
exit_tryhelp(2);
}
if (nsaddrs)
free(saddrs);
if (ndaddrs)
free(daddrs);
if (cs.target)
free(cs.target->t);
xtables_free_opts(1);
/* if (verbose > 1)
dump_entries(*handle);*/
......
......@@ -41,29 +41,15 @@
#include "xtables-multi.h"
extern struct xtables_globals ebtables_globals;
int xtables_eb_main(int argc, char *argv[])
{
int ret;
char *table = "filter";
struct nft_handle h = {
.family = NFPROTO_BRIDGE,
};
struct nft_handle h;
ebtables_globals.program_name = "ebtables";
ret = xtables_init_all(&ebtables_globals, NFPROTO_BRIDGE);
if (ret < 0) {
fprintf(stderr, "%s/%s Failed to initialize ebtables-compat\n",
ebtables_globals.program_name,
ebtables_globals.program_version);
exit(1);
}
nft_init_eb(&h, "ebtables");
#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
init_extensionsb();
#endif
ret = do_commandeb(&h, argc, argv, &table);
ret = do_commandeb(&h, argc, argv, &table, false);
if (ret)
ret = nft_commit(&h);
......
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdarg.h>
#include <string.h>
#include <fcntl.h>
#include <getopt.h>
#include <iptables.h>
#include <xtables.h>
#include <netinet/ether.h>
#include <linux/netfilter_bridge.h>
#include <linux/netfilter/nf_tables.h>
#include <libiptc/libxtc.h>
#include "xshared.h"
#include "xtables-multi.h"
#include "nft-bridge.h"
#include "nft.h"
#include "nft-shared.h"
/*
* From include/ebtables_u.h
*/
#define EXEC_STYLE_PRG 0
#define EXEC_STYLE_DAEMON 1
#define ebt_check_option2(flags, mask) EBT_CHECK_OPTION(flags, mask)
extern int ebt_invert;
static int ebt_check_inverse2(const char option[], int argc, char **argv)
{
if (!option)
return ebt_invert;
if (strcmp(option, "!") == 0) {
if (ebt_invert == 1)
xtables_error(PARAMETER_PROBLEM,
"Double use of '!' not allowed");
if (optind >= argc)
optarg = NULL;
else
optarg = argv[optind];
optind++;
ebt_invert = 1;
return 1;
}
return ebt_invert;
}
/*
* Glue code to use libxtables
*/
static int parse_rule_number(const char *rule)
{
unsigned int rule_nr;
if (!xtables_strtoui(rule, NULL, &rule_nr, 1, INT_MAX))
xtables_error(PARAMETER_PROBLEM,
"Invalid rule number `%s'", rule);
return rule_nr;
}
static const char *
parse_target(const char *targetname)
{
const char *ptr;
if (strlen(targetname) < 1)
xtables_error(PARAMETER_PROBLEM,
"Invalid target name (too short)");
if (strlen(targetname)+1 > EBT_CHAIN_MAXNAMELEN)
xtables_error(PARAMETER_PROBLEM,
"Invalid target '%s' (%d chars max)",
targetname, EBT_CHAIN_MAXNAMELEN);
for (ptr = targetname; *ptr; ptr++)
if (isspace(*ptr))
xtables_error(PARAMETER_PROBLEM,
"Invalid target name `%s'", targetname);
return targetname;
}
static int get_current_chain(const char *chain)
{
if (strcmp(chain, "PREROUTING") == 0)
return NF_BR_PRE_ROUTING;
else if (strcmp(chain, "INPUT") == 0)
return NF_BR_LOCAL_IN;
else if (strcmp(chain, "FORWARD") == 0)
return NF_BR_FORWARD;
else if (strcmp(chain, "OUTPUT") == 0)
return NF_BR_LOCAL_OUT;
else if (strcmp(chain, "POSTROUTING") == 0)
return NF_BR_POST_ROUTING;
return -1;
}
/*
* The original ebtables parser
*/
/* Checks whether a command has already been specified */
#define OPT_COMMANDS (flags & OPT_COMMAND || flags & OPT_ZERO)
#define OPT_COMMAND 0x01
#define OPT_TABLE 0x02
#define OPT_IN 0x04
#define OPT_OUT 0x08
#define OPT_JUMP 0x10
#define OPT_PROTOCOL 0x20
#define OPT_SOURCE 0x40
#define OPT_DEST 0x80
#define OPT_ZERO 0x100
#define OPT_LOGICALIN 0x200
#define OPT_LOGICALOUT 0x400
#define OPT_COUNT 0x1000 /* This value is also defined in libebtc.c */
/* Default command line options. Do not mess around with the already
* assigned numbers unless you know what you are doing */
extern struct option ebt_original_options[];
extern struct xtables_globals ebtables_globals;
#define opts ebtables_globals.opts
#define prog_name ebtables_globals.program_name
#define prog_vers ebtables_globals.program_version
static void print_help(void)
{
fprintf(stderr, "%s: Translate ebtables command to nft syntax\n"
"no side effects occur, the translated command is written "
"to standard output.\n"
"A '#' followed by input means no translation "
"is available.\n", prog_name);
exit(0);
}
static int parse_rule_range(const char *argv, int *rule_nr, int *rule_nr_end)
{
char *colon = strchr(argv, ':'), *buffer;
if (colon) {
*colon = '\0';
if (*(colon + 1) == '\0')
*rule_nr_end = -1; /* Until the last rule */
else {
*rule_nr_end = strtol(colon + 1, &buffer, 10);
if (*buffer != '\0' || *rule_nr_end == 0)
return -1;
}
}
if (colon == argv)
*rule_nr = 1; /* Beginning with the first rule */
else {
*rule_nr = strtol(argv, &buffer, 10);
if (*buffer != '\0' || *rule_nr == 0)
return -1;
}
if (!colon)
*rule_nr_end = *rule_nr;
return 0;
}
static void ebtables_parse_interface(const char *arg, char *vianame)
{
unsigned char mask[IFNAMSIZ];
char *c;
xtables_parse_interface(arg, vianame, mask);
if ((c = strchr(vianame, '+'))) {
if (*(c + 1) != '\0')
xtables_error(PARAMETER_PROBLEM,
"Spurious characters after '+' wildcard");
}
}
static void print_ebt_cmd(int argc, char *argv[])
{
int i;
printf("# ");
for (i = 1; i < argc; i++)
printf("%s ", argv[i]);
printf("\n");
}
static int nft_rule_eb_xlate_add(struct nft_handle *h, const struct nft_xt_cmd_parse *p,
const struct iptables_command_state *cs, bool append)
{
struct xt_xlate *xl = xt_xlate_alloc(10240);
int ret;
if (append) {
xt_xlate_add(xl, "add rule bridge %s %s ", p->table, p->chain);
} else {
xt_xlate_add(xl, "insert rule bridge %s %s ", p->table, p->chain);
}
ret = h->ops->xlate(cs, xl);
if (ret)
printf("%s\n", xt_xlate_get(xl));
xt_xlate_free(xl);
return ret;
}
/* We use exec_style instead of #ifdef's because ebtables.so is a shared object. */
static int do_commandeb_xlate(struct nft_handle *h, int argc, char *argv[], char **table)
{
char *buffer;
int c, i;
int rule_nr = 0;
int rule_nr_end = 0;
int ret = 0;
unsigned int flags = 0;
struct iptables_command_state cs = {
.argv = argv,
.eb.bitmask = EBT_NOPROTO,
};
char command = 'h';
const char *chain = NULL;
int exec_style = EXEC_STYLE_PRG;
int selected_chain = -1;
struct xtables_rule_match *xtrm_i;
struct ebt_match *match;
struct nft_xt_cmd_parse p = {
.table = *table,
};
/* prevent getopt to spoil our error reporting */
opterr = false;
printf("nft ");
/* Getopt saves the day */
while ((c = getopt_long(argc, argv,
"-A:D:I:N:E:X::L::Z::F::P:Vhi:o:j:c:p:s:d:t:M:", opts, NULL)) != -1) {
cs.c = c;
cs.invert = ebt_invert;
switch (c) {
case 'A': /* Add a rule */
case 'D': /* Delete a rule */
case 'P': /* Define policy */
case 'I': /* Insert a rule */
case 'N': /* Make a user defined chain */
case 'E': /* Rename chain */
case 'X': /* Delete chain */
/* We allow -N chainname -P policy */
/* XXX: Not in ebtables-compat */
if (command == 'N' && c == 'P') {
command = c;
optind--; /* No table specified */
break;
}
if (OPT_COMMANDS)
xtables_error(PARAMETER_PROBLEM,
"Multiple commands are not allowed");
command = c;
chain = optarg;
selected_chain = get_current_chain(chain);
p.chain = chain;
flags |= OPT_COMMAND;
if (c == 'N') {
printf("add chain bridge %s %s\n", p.table, p.chain);
ret = 1;
break;
} else if (c == 'X') {
printf("delete chain bridge %s %s\n", p.table, p.chain);
ret = 1;
break;
}
if (c == 'E') {
break;
} else if (c == 'D' && optind < argc && (argv[optind][0] != '-' || (argv[optind][1] >= '0' && argv[optind][1] <= '9'))) {
if (optind != argc - 1)
xtables_error(PARAMETER_PROBLEM,
"No extra options allowed with -D start_nr[:end_nr]");
if (parse_rule_range(argv[optind], &rule_nr, &rule_nr_end))
xtables_error(PARAMETER_PROBLEM,
"Problem with the specified rule number(s) '%s'", argv[optind]);
optind++;
} else if (c == 'I') {
if (optind >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9')))
rule_nr = 1;
else {
rule_nr = parse_rule_number(argv[optind]);
optind++;
}
p.rulenum = rule_nr;
} else if (c == 'P') {
break;
}
break;
case 'L': /* List */
printf("list table bridge %s\n", p.table);
ret = 1;
break;
case 'F': /* Flush */
if (p.chain) {
printf("flush chain bridge %s %s\n", p.table, p.chain);
} else {
printf("flush table bridge %s\n", p.table);
}
ret = 1;
break;
case 'Z': /* Zero counters */
if (c == 'Z') {
if ((flags & OPT_ZERO) || (flags & OPT_COMMAND && command != 'L'))
print_zero:
xtables_error(PARAMETER_PROBLEM,
"Command -Z only allowed together with command -L");
flags |= OPT_ZERO;
} else {
if (flags & OPT_COMMAND)
xtables_error(PARAMETER_PROBLEM,
"Multiple commands are not allowed");
command = c;
flags |= OPT_COMMAND;
if (flags & OPT_ZERO && c != 'L')
goto print_zero;
}
break;
case 'V': /* Version */
if (OPT_COMMANDS)
xtables_error(PARAMETER_PROBLEM,
"Multiple commands are not allowed");
if (exec_style == EXEC_STYLE_DAEMON)
xtables_error(PARAMETER_PROBLEM,
"%s %s\n", prog_name, prog_vers);
printf("%s %s\n", prog_name, prog_vers);
exit(0);
case 'h':
if (OPT_COMMANDS)
xtables_error(PARAMETER_PROBLEM,
"Multiple commands are not allowed");
print_help();
break;
case 't': /* Table */
if (OPT_COMMANDS)
xtables_error(PARAMETER_PROBLEM,
"Please put the -t option first");
ebt_check_option2(&flags, OPT_TABLE);
if (strlen(optarg) > EBT_TABLE_MAXNAMELEN - 1)
xtables_error(PARAMETER_PROBLEM,
"Table name length cannot exceed %d characters",
EBT_TABLE_MAXNAMELEN - 1);
*table = optarg;
p.table = optarg;
break;
case 'i': /* Input interface */
case 2 : /* Logical input interface */
case 'o': /* Output interface */
case 3 : /* Logical output interface */
case 'j': /* Target */
case 'p': /* Net family protocol */
case 's': /* Source mac */
case 'd': /* Destination mac */
case 'c': /* Set counters */
if (!OPT_COMMANDS)
xtables_error(PARAMETER_PROBLEM,
"No command specified");
if (command != 'A' && command != 'D' && command != 'I')
xtables_error(PARAMETER_PROBLEM,
"Command and option do not match");
if (c == 'i') {
ebt_check_option2(&flags, OPT_IN);
if (selected_chain > 2 && selected_chain < NF_BR_BROUTING)
xtables_error(PARAMETER_PROBLEM,
"Use -i only in INPUT, FORWARD, PREROUTING and BROUTING chains");
if (ebt_check_inverse2(optarg, argc, argv))
cs.eb.invflags |= EBT_IIN;
ebtables_parse_interface(optarg, cs.eb.in);
break;
} else if (c == 2) {
ebt_check_option2(&flags, OPT_LOGICALIN);
if (selected_chain > 2 && selected_chain < NF_BR_BROUTING)
xtables_error(PARAMETER_PROBLEM,
"Use --logical-in only in INPUT, FORWARD, PREROUTING and BROUTING chains");
if (ebt_check_inverse2(optarg, argc, argv))
cs.eb.invflags |= EBT_ILOGICALIN;
ebtables_parse_interface(optarg, cs.eb.logical_in);
break;
} else if (c == 'o') {
ebt_check_option2(&flags, OPT_OUT);
if (selected_chain < 2 || selected_chain == NF_BR_BROUTING)
xtables_error(PARAMETER_PROBLEM,
"Use -o only in OUTPUT, FORWARD and POSTROUTING chains");
if (ebt_check_inverse2(optarg, argc, argv))
cs.eb.invflags |= EBT_IOUT;
ebtables_parse_interface(optarg, cs.eb.out);
break;
} else if (c == 3) {
ebt_check_option2(&flags, OPT_LOGICALOUT);
if (selected_chain < 2 || selected_chain == NF_BR_BROUTING)
xtables_error(PARAMETER_PROBLEM,
"Use --logical-out only in OUTPUT, FORWARD and POSTROUTING chains");
if (ebt_check_inverse2(optarg, argc, argv))
cs.eb.invflags |= EBT_ILOGICALOUT;
ebtables_parse_interface(optarg, cs.eb.logical_out);
break;
} else if (c == 'j') {
ebt_check_option2(&flags, OPT_JUMP);
cs.jumpto = parse_target(optarg);
cs.target = ebt_command_jump(cs.jumpto);
break;
} else if (c == 's') {
ebt_check_option2(&flags, OPT_SOURCE);
if (ebt_check_inverse2(optarg, argc, argv))
cs.eb.invflags |= EBT_ISOURCE;
if (ebt_get_mac_and_mask(optarg, cs.eb.sourcemac, cs.eb.sourcemsk))
xtables_error(PARAMETER_PROBLEM, "Problem with specified source mac '%s'", optarg);
cs.eb.bitmask |= EBT_SOURCEMAC;
break;
} else if (c == 'd') {
ebt_check_option2(&flags, OPT_DEST);
if (ebt_check_inverse2(optarg, argc, argv))
cs.eb.invflags |= EBT_IDEST;
if (ebt_get_mac_and_mask(optarg, cs.eb.destmac, cs.eb.destmsk))
xtables_error(PARAMETER_PROBLEM, "Problem with specified destination mac '%s'", optarg);
cs.eb.bitmask |= EBT_DESTMAC;
break;
} else if (c == 'c') {
ebt_check_option2(&flags, OPT_COUNT);
if (ebt_check_inverse2(optarg, argc, argv))
xtables_error(PARAMETER_PROBLEM,
"Unexpected '!' after -c");
if (optind >= argc || optarg[0] == '-' || argv[optind][0] == '-')
xtables_error(PARAMETER_PROBLEM,
"Option -c needs 2 arguments");
cs.counters.pcnt = strtoull(optarg, &buffer, 10);
if (*buffer != '\0')
xtables_error(PARAMETER_PROBLEM,
"Packet counter '%s' invalid",
optarg);
cs.counters.bcnt = strtoull(argv[optind], &buffer, 10);
if (*buffer != '\0')
xtables_error(PARAMETER_PROBLEM,
"Packet counter '%s' invalid",
argv[optind]);
optind++;
break;
}
ebt_check_option2(&flags, OPT_PROTOCOL);
if (ebt_check_inverse2(optarg, argc, argv))
cs.eb.invflags |= EBT_IPROTO;
cs.eb.bitmask &= ~((unsigned int)EBT_NOPROTO);
i = strtol(optarg, &buffer, 16);
if (*buffer == '\0' && (i < 0 || i > 0xFFFF))
xtables_error(PARAMETER_PROBLEM,
"Problem with the specified protocol");
if (*buffer != '\0') {
struct xt_ethertypeent *ent;
if (!strcasecmp(optarg, "LENGTH")) {
cs.eb.bitmask |= EBT_802_3;
break;
}
ent = xtables_getethertypebyname(optarg);
if (!ent)
xtables_error(PARAMETER_PROBLEM,
"Problem with the specified Ethernet protocol '%s', perhaps "XT_PATH_ETHERTYPES " is missing", optarg);
cs.eb.ethproto = ent->e_ethertype;
} else
cs.eb.ethproto = i;
if (cs.eb.ethproto < 0x0600)
xtables_error(PARAMETER_PROBLEM,
"Sorry, protocols have values above or equal to 0x0600");
break;
case 4 : /* Lc */
ebt_check_option2(&flags, LIST_C);
if (command != 'L')
xtables_error(PARAMETER_PROBLEM,
"Use --Lc with -L");
flags |= LIST_C;
break;
case 5 : /* Ln */
ebt_check_option2(&flags, LIST_N);
if (command != 'L')
xtables_error(PARAMETER_PROBLEM,
"Use --Ln with -L");
if (flags & LIST_X)
xtables_error(PARAMETER_PROBLEM,
"--Lx is not compatible with --Ln");
flags |= LIST_N;
break;
case 6 : /* Lx */
ebt_check_option2(&flags, LIST_X);
if (command != 'L')
xtables_error(PARAMETER_PROBLEM,
"Use --Lx with -L");
if (flags & LIST_N)
xtables_error(PARAMETER_PROBLEM,
"--Lx is not compatible with --Ln");
flags |= LIST_X;
break;
case 12 : /* Lmac2 */
ebt_check_option2(&flags, LIST_MAC2);
if (command != 'L')
xtables_error(PARAMETER_PROBLEM,
"Use --Lmac2 with -L");
flags |= LIST_MAC2;
break;
case 1 :
if (!strcmp(optarg, "!"))
ebt_check_inverse2(optarg, argc, argv);
else
xtables_error(PARAMETER_PROBLEM,
"Bad argument : '%s'", optarg);
/* ebt_ebt_check_inverse2() did optind++ */
optind--;
continue;
default:
ebt_check_inverse2(optarg, argc, argv);
if (ebt_command_default(&cs))
xtables_error(PARAMETER_PROBLEM,
"Unknown argument: '%s'",
argv[optind - 1]);
if (command != 'A' && command != 'I' &&
command != 'D')
xtables_error(PARAMETER_PROBLEM,
"Extensions only for -A, -I, -D");
}
ebt_invert = 0;
}
/* Do the final checks */
if (command == 'A' || command == 'I' || command == 'D') {
for (xtrm_i = cs.matches; xtrm_i; xtrm_i = xtrm_i->next)
xtables_option_mfcall(xtrm_i->match);
for (match = cs.match_list; match; match = match->next) {
if (match->ismatch)
continue;
xtables_option_tfcall(match->u.watcher);
}
if (cs.target != NULL)
xtables_option_tfcall(cs.target);
}
cs.eb.ethproto = htons(cs.eb.ethproto);
if (command == 'P') {
return 0;
} else if (command == 'A') {
ret = nft_rule_eb_xlate_add(h, &p, &cs, true);
if (!ret)
print_ebt_cmd(argc, argv);
} else if (command == 'I') {
ret = nft_rule_eb_xlate_add(h, &p, &cs, false);
if (!ret)
print_ebt_cmd(argc, argv);
}
ebt_cs_clean(&cs);
return ret;
}
static int dummy_compat_rev(const char *name, uint8_t rev, int opt)
{
return 1;
}
int xtables_eb_xlate_main(int argc, char *argv[])
{
int ret;
char *table = "filter";
struct nft_handle h;
nft_init_eb(&h, argv[0]);
ebtables_globals.compat_rev = dummy_compat_rev;
ret = do_commandeb_xlate(&h, argc, argv, &table);
if (!ret)
fprintf(stderr, "Translation not implemented\n");
exit(!ret);
}
......@@ -37,7 +37,6 @@
#include <linux/netfilter_bridge.h>
#include <linux/netfilter/nf_tables.h>
#include <ebtables/ethernetdb.h>
#include <libiptc/libxtc.h>
#include "xshared.h"
#include "nft.h"
......@@ -46,9 +45,6 @@
/*
* From include/ebtables_u.h
*/
#define EXEC_STYLE_PRG 0
#define EXEC_STYLE_DAEMON 1
#define ebt_check_option2(flags, mask) EBT_CHECK_OPTION(flags, mask)
/*
......@@ -168,7 +164,7 @@ static int
append_entry(struct nft_handle *h,
const char *chain,
const char *table,
struct ebtables_command_state *cs,
struct iptables_command_state *cs,
int rule_nr,
bool verbose, bool append)
{
......@@ -186,7 +182,7 @@ static int
delete_entry(struct nft_handle *h,
const char *chain,
const char *table,
struct ebtables_command_state *cs,
struct iptables_command_state *cs,
int rule_nr,
int rule_nr_end,
bool verbose)
......@@ -206,8 +202,11 @@ delete_entry(struct nft_handle *h,
return ret;
}
static int get_current_chain(const char *chain)
int ebt_get_current_chain(const char *chain)
{
if (!chain)
return -1;
if (strcmp(chain, "PREROUTING") == 0)
return NF_BR_PRE_ROUTING;
else if (strcmp(chain, "INPUT") == 0)
......@@ -247,7 +246,7 @@ static int get_current_chain(const char *chain)
/* Default command line options. Do not mess around with the already
* assigned numbers unless you know what you are doing */
static struct option ebt_original_options[] =
struct option ebt_original_options[] =
{
{ "append" , required_argument, 0, 'A' },
{ "insert" , required_argument, 0, 'I' },
......@@ -380,32 +379,22 @@ static struct option *merge_options(struct option *oldopts,
/*
* More glue code.
*/
static struct xtables_target *command_jump(struct ebtables_command_state *cs,
const char *jumpto)
struct xtables_target *ebt_command_jump(const char *jumpto)
{
struct xtables_target *target;
size_t size;
/* XTF_TRY_LOAD (may be chain name) */
target = xtables_find_target(jumpto, XTF_TRY_LOAD);
unsigned int verdict;
if (!target)
return NULL;
/* Standard target? */
if (!ebt_fill_target(jumpto, &verdict))
jumpto = "standard";
size = XT_ALIGN(sizeof(struct xt_entry_target))
+ target->size;
target->t = xtables_calloc(1, size);
target->t->u.target_size = size;
strncpy(target->t->u.user.name, jumpto, sizeof(target->t->u.user.name));
target->t->u.user.name[sizeof(target->t->u.user.name)-1] = '\0';
target->t->u.user.revision = target->revision;
xs_init_target(target);
opts = merge_options(opts, target->extra_opts, &target->option_offset);
if (opts == NULL)
xtables_error(OTHER_PROBLEM, "Can't alloc memory");
/* For ebtables, all targets are preloaded. Hence it is either in
* xtables_targets or a custom chain to jump to, in which case
* returning NULL is fine. */
for (target = xtables_targets; target; target = target->next) {
if (!strcmp(target->name, jumpto))
break;
}
return target;
}
......@@ -524,13 +513,12 @@ static int parse_rule_range(const char *argv, int *rule_nr, int *rule_nr_end)
/* Incrementing or decrementing rules in daemon mode is not supported as the
* involved code overload is not worth it (too annoying to take the increased
* counters in the kernel into account). */
static int parse_change_counters_rule(int argc, char **argv, int *rule_nr, int *rule_nr_end, int exec_style, struct ebtables_command_state *cs)
static int parse_change_counters_rule(int argc, char **argv, int *rule_nr, int *rule_nr_end, struct iptables_command_state *cs)
{
char *buffer;
int ret = 0;
if (optind + 1 >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9')) ||
(argv[optind + 1][0] == '-' && (argv[optind + 1][1] < '0' && argv[optind + 1][1] > '9')))
if (optind + 1 >= argc || argv[optind][0] == '-' || argv[optind + 1][0] == '-')
xtables_error(PARAMETER_PROBLEM,
"The command -C needs at least 2 arguments");
if (optind + 2 < argc && (argv[optind + 2][0] != '-' || (argv[optind + 2][1] >= '0' && argv[optind + 2][1] <= '9'))) {
......@@ -544,17 +532,9 @@ static int parse_change_counters_rule(int argc, char **argv, int *rule_nr, int *
}
if (argv[optind][0] == '+') {
if (exec_style == EXEC_STYLE_DAEMON)
daemon_incr:
xtables_error(PARAMETER_PROBLEM,
"Incrementing rule counters (%s) not allowed in daemon mode", argv[optind]);
ret += 1;
cs->counters.pcnt = strtoull(argv[optind] + 1, &buffer, 10);
} else if (argv[optind][0] == '-') {
if (exec_style == EXEC_STYLE_DAEMON)
daemon_decr:
xtables_error(PARAMETER_PROBLEM,
"Decrementing rule counters (%s) not allowed in daemon mode", argv[optind]);
ret += 2;
cs->counters.pcnt = strtoull(argv[optind] + 1, &buffer, 10);
} else
......@@ -564,13 +544,9 @@ daemon_decr:
goto invalid;
optind++;
if (argv[optind][0] == '+') {
if (exec_style == EXEC_STYLE_DAEMON)
goto daemon_incr;
ret += 3;
cs->counters.bcnt = strtoull(argv[optind] + 1, &buffer, 10);
} else if (argv[optind][0] == '-') {
if (exec_style == EXEC_STYLE_DAEMON)
goto daemon_decr;
ret += 6;
cs->counters.bcnt = strtoull(argv[optind] + 1, &buffer, 10);
} else
......@@ -584,19 +560,18 @@ invalid:
xtables_error(PARAMETER_PROBLEM,"Packet counter '%s' invalid", argv[optind]);
}
static int parse_iface(char *iface, char *option)
static void ebtables_parse_interface(const char *arg, char *vianame)
{
unsigned char mask[IFNAMSIZ];
char *c;
if ((c = strchr(iface, '+'))) {
if (*(c + 1) != '\0') {
xtables_parse_interface(arg, vianame, mask);
if ((c = strchr(vianame, '+'))) {
if (*(c + 1) != '\0')
xtables_error(PARAMETER_PROBLEM,
"Spurious characters after '+' wildcard for '%s'", option);
return -1;
} else
*c = IF_WILDCARD;
"Spurious characters after '+' wildcard");
}
return 0;
}
/* This code is very similar to iptables/xtables.c:command_match() */
......@@ -605,9 +580,11 @@ static void ebt_load_match(const char *name)
struct xtables_match *m;
size_t size;
m = xtables_find_match(name, XTF_LOAD_MUST_SUCCEED, NULL);
if (m == NULL)
xtables_error(OTHER_PROBLEM, "Unable to load %s match", name);
m = xtables_find_match(name, XTF_TRY_LOAD, NULL);
if (m == NULL) {
fprintf(stderr, "Unable to load %s match\n", name);
return;
}
size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size;
m->m = xtables_calloc(1, size);
......@@ -621,22 +598,23 @@ static void ebt_load_match(const char *name)
xtables_error(OTHER_PROBLEM, "Can't alloc memory");
}
static void ebt_load_watcher(const char *name)
static void __ebt_load_watcher(const char *name, const char *typename)
{
struct xtables_target *watcher;
size_t size;
watcher = xtables_find_target(name, XTF_LOAD_MUST_SUCCEED);
if (!watcher)
xtables_error(OTHER_PROBLEM,
"Unable to load %s watcher", name);
watcher = xtables_find_target(name, XTF_TRY_LOAD);
if (!watcher) {
fprintf(stderr, "Unable to load %s %s\n", name, typename);
return;
}
size = XT_ALIGN(sizeof(struct xt_entry_target)) + watcher->size;
watcher->t = xtables_calloc(1, size);
watcher->t->u.target_size = size;
strncpy(watcher->t->u.user.name, name,
sizeof(watcher->t->u.user.name));
snprintf(watcher->t->u.user.name,
sizeof(watcher->t->u.user.name), "%s", name);
watcher->t->u.user.name[sizeof(watcher->t->u.user.name)-1] = '\0';
watcher->t->u.user.revision = watcher->revision;
......@@ -648,39 +626,60 @@ static void ebt_load_watcher(const char *name)
xtables_error(OTHER_PROBLEM, "Can't alloc memory");
}
static void ebt_load_match_extensions(void)
static void ebt_load_watcher(const char *name)
{
return __ebt_load_watcher(name, "watcher");
}
static void ebt_load_target(const char *name)
{
return __ebt_load_watcher(name, "target");
}
void ebt_load_match_extensions(void)
{
opts = ebt_original_options;
ebt_load_match("802_3");
ebt_load_match("arp");
ebt_load_match("ip");
ebt_load_match("ip6");
ebt_load_match("mark_m");
ebt_load_match("limit");
ebt_load_match("pkttype");
ebt_load_match("vlan");
ebt_load_match("stp");
ebt_load_watcher("log");
ebt_load_watcher("nflog");
ebt_load_target("mark");
ebt_load_target("dnat");
ebt_load_target("snat");
ebt_load_target("redirect");
ebt_load_target("standard");
}
static void ebt_add_match(struct xtables_match *m,
struct ebtables_command_state *cs)
void ebt_add_match(struct xtables_match *m,
struct iptables_command_state *cs)
{
struct xtables_rule_match *i, **rule_matches = &cs->matches;
struct xtables_rule_match **rule_matches = &cs->matches;
struct xtables_match *newm;
struct ebt_match *newnode;
/* match already in rule_matches, skip inclusion */
for (i = *rule_matches; i; i = i->next) {
if (strcmp(m->name, i->match->name) == 0) {
i->match->mflags |= m->mflags;
return;
}
}
struct ebt_match *newnode, **matchp;
struct xt_entry_match *m2;
newm = xtables_find_match(m->name, XTF_LOAD_MUST_SUCCEED, rule_matches);
if (newm == NULL)
xtables_error(OTHER_PROBLEM,
"Unable to add match %s", m->name);
m2 = xtables_calloc(1, newm->m->u.match_size);
memcpy(m2, newm->m, newm->m->u.match_size);
memset(newm->m->data, 0, newm->size);
xs_init_match(newm);
newm->m = m2;
newm->mflags = m->mflags;
m->mflags = 0;
/* glue code for watchers */
newnode = calloc(1, sizeof(struct ebt_match));
......@@ -690,88 +689,152 @@ static void ebt_add_match(struct xtables_match *m,
newnode->ismatch = true;
newnode->u.match = newm;
if (cs->match_list == NULL)
cs->match_list = newnode;
else
cs->match_list->next = newnode;
for (matchp = &cs->match_list; *matchp; matchp = &(*matchp)->next)
;
*matchp = newnode;
}
static void ebt_add_watcher(struct xtables_target *watcher,
struct ebtables_command_state *cs)
void ebt_add_watcher(struct xtables_target *watcher,
struct iptables_command_state *cs)
{
struct ebt_match *i, *newnode;
struct ebt_match *newnode, **matchp;
struct xtables_target *clone;
clone = xtables_malloc(sizeof(struct xtables_target));
memcpy(clone, watcher, sizeof(struct xtables_target));
clone->udata = NULL;
clone->tflags = watcher->tflags;
clone->next = clone;
clone->t = xtables_calloc(1, watcher->t->u.target_size);
memcpy(clone->t, watcher->t, watcher->t->u.target_size);
memset(watcher->t->data, 0, watcher->size);
xs_init_target(watcher);
watcher->tflags = 0;
for (i = cs->match_list; i; i = i->next) {
if (i->ismatch)
continue;
if (strcmp(i->u.watcher->name, watcher->name) == 0) {
i->u.watcher->tflags |= watcher->tflags;
return;
}
}
newnode = calloc(1, sizeof(struct ebt_match));
if (newnode == NULL)
xtables_error(OTHER_PROBLEM, "Unable to alloc memory");
newnode->u.watcher = watcher;
newnode->u.watcher = clone;
if (cs->match_list == NULL)
cs->match_list = newnode;
else
cs->match_list->next = newnode;
for (matchp = &cs->match_list; *matchp; matchp = &(*matchp)->next)
;
*matchp = newnode;
}
/* We use exec_style instead of #ifdef's because ebtables.so is a shared object. */
int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table)
int ebt_command_default(struct iptables_command_state *cs)
{
char *buffer;
int c, i;
int zerochain = -1; /* Needed for the -Z option (we can have -Z <this> -L <that>) */
int chcounter = 0; /* Needed for -C */
int rule_nr = 0;
int rule_nr_end = 0;
int ret = 0;
unsigned int flags = 0;
struct xtables_target *t, *w;
struct xtables_target *t = cs->target;
struct xtables_match *m;
struct ebtables_command_state cs;
char command = 'h';
const char *chain = NULL;
const char *policy = NULL;
int exec_style = EXEC_STYLE_PRG;
int selected_chain = -1;
struct xtables_rule_match *xtrm_i;
struct ebt_match *match;
struct ebt_match *matchp;
/* Is it a target option? */
if (t && t->parse) {
if (t->parse(cs->c - t->option_offset, cs->argv,
ebt_invert, &t->tflags, NULL, &t->t))
return 0;
}
/* check previously added matches/watchers to this rule first */
for (matchp = cs->match_list; matchp; matchp = matchp->next) {
if (matchp->ismatch) {
m = matchp->u.match;
if (m->parse &&
m->parse(cs->c - m->option_offset, cs->argv,
ebt_invert, &m->mflags, NULL, &m->m))
return 0;
} else {
t = matchp->u.watcher;
if (t->parse &&
t->parse(cs->c - t->option_offset, cs->argv,
ebt_invert, &t->tflags, NULL, &t->t))
return 0;
}
}
/* Is it a match_option? */
for (m = xtables_matches; m; m = m->next) {
if (m->parse &&
m->parse(cs->c - m->option_offset, cs->argv,
ebt_invert, &m->mflags, NULL, &m->m)) {
ebt_add_match(m, cs);
return 0;
}
}
/* Is it a watcher option? */
for (t = xtables_targets; t; t = t->next) {
if (t->parse &&
t->parse(cs->c - t->option_offset, cs->argv,
ebt_invert, &t->tflags, NULL, &t->t)) {
ebt_add_watcher(t, cs);
return 0;
}
}
return 1;
}
memset(&cs, 0, sizeof(cs));
cs.argv = argv;
int nft_init_eb(struct nft_handle *h, const char *pname)
{
ebtables_globals.program_name = pname;
if (xtables_init_all(&ebtables_globals, NFPROTO_BRIDGE) < 0) {
fprintf(stderr, "%s/%s Failed to initialize ebtables-compat\n",
ebtables_globals.program_name,
ebtables_globals.program_version);
exit(1);
}
#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
init_extensionsb();
#endif
memset(h, 0, sizeof(*h));
h->family = NFPROTO_BRIDGE;
if (nft_init(h, xtables_bridge) < 0)
xtables_error(OTHER_PROBLEM,
"Could not initialize nftables layer.");
h->ops = nft_family_ops_lookup(h->family);
if (h->ops == NULL)
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 loaded dinamically when
* don't use '-m matchname' and the match can't be loaded dynamically when
* the user calls it.
*/
ebt_load_match_extensions();
/* clear mflags in case do_commandeb gets called a second time
* (we clear the global list of all matches for security)*/
for (m = xtables_matches; m; m = m->next)
m->mflags = 0;
return 0;
}
for (t = xtables_targets; t; t = t->next) {
t->tflags = 0;
t->used = 0;
}
int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table,
bool restore)
{
char *buffer;
int c, i;
int chcounter = 0; /* Needed for -C */
int rule_nr = 0;
int rule_nr_end = 0;
int ret = 0;
unsigned int flags = 0;
struct xtables_target *t;
struct iptables_command_state cs = {
.argv = argv,
.eb.bitmask = EBT_NOPROTO,
};
char command = 'h';
const char *chain = NULL;
const char *policy = NULL;
int selected_chain = -1;
struct xtables_rule_match *xtrm_i;
struct ebt_match *match;
/* prevent getopt to spoil our error reporting */
optind = 0;
opterr = false;
/* Getopt saves the day */
......@@ -801,18 +864,22 @@ int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table)
"Multiple commands are not allowed");
command = c;
if (optarg && (optarg[0] == '-' || !strcmp(optarg, "!")))
xtables_error(PARAMETER_PROBLEM, "No chain name specified");
chain = optarg;
selected_chain = get_current_chain(chain);
selected_chain = ebt_get_current_chain(chain);
flags |= OPT_COMMAND;
/*if (!(replace->flags & OPT_KERNELDATA))
ebt_get_kernel_table(replace, 0);*/
/*if (optarg && (optarg[0] == '-' || !strcmp(optarg, "!")))
ebt_print_error2("No chain name specified");*/
if (c == 'N') {
ret = nft_chain_user_add(h, chain, *table);
break;
} else if (c == 'X') {
ret = nft_chain_user_del(h, chain, *table);
/* X arg is optional, optarg is NULL */
if (!chain && optind < argc && argv[optind][0] != '-') {
chain = argv[optind];
optind++;
}
ret = nft_chain_user_del(h, chain, *table, 0);
break;
}
......@@ -842,7 +909,7 @@ int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table)
"Problem with the specified rule number(s) '%s'", argv[optind]);
optind++;
} else if (c == 'C') {
if ((chcounter = parse_change_counters_rule(argc, argv, &rule_nr, &rule_nr_end, exec_style, &cs)) == -1)
if ((chcounter = parse_change_counters_rule(argc, argv, &rule_nr, &rule_nr_end, &cs)) == -1)
return -1;
} else if (c == 'I') {
if (optind >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9')))
......@@ -890,43 +957,18 @@ print_zero:
goto print_zero;
}
#ifdef SILENT_DAEMON
if (c== 'L' && exec_style == EXEC_STYLE_DAEMON)
xtables_error(PARAMETER_PROBLEM,
"-L not supported in daemon mode");
#endif
/*if (!(replace->flags & OPT_KERNELDATA))
ebt_get_kernel_table(replace, 0);
i = -1;
if (optind < argc && argv[optind][0] != '-') {
if ((i = ebt_get_chainnr(replace, argv[optind])) == -1)
ebt_print_error2("Chain '%s' doesn't exist", argv[optind]);
chain = argv[optind];
optind++;
}
if (i != -1) {
if (c == 'Z')
zerochain = i;
else
replace->selected_chain = i;
}*/
break;
case 'V': /* Version */
if (OPT_COMMANDS)
xtables_error(PARAMETER_PROBLEM,
"Multiple commands are not allowed");
command = 'V';
if (exec_style == EXEC_STYLE_DAEMON)
xtables_error(PARAMETER_PROBLEM,
"%s %s\n", prog_name, prog_vers);
printf("%s %s\n", prog_name, prog_vers);
printf("%s %s (nf_tables)\n", prog_name, prog_vers);
exit(0);
case 'h': /* Help */
#ifdef SILENT_DAEMON
if (exec_style == EXEC_STYLE_DAEMON)
xtables_error(PARAMETER_PROBLEM,
"-h not supported in daemon mode");
#endif
if (OPT_COMMANDS)
xtables_error(PARAMETER_PROBLEM,
"Multiple commands are not allowed");
......@@ -988,14 +1030,9 @@ print_zero:
xtables_error(PARAMETER_PROBLEM,
"Use -i only in INPUT, FORWARD, PREROUTING and BROUTING chains");
if (ebt_check_inverse2(optarg, argc, argv))
cs.fw.invflags |= EBT_IIN;
cs.eb.invflags |= EBT_IIN;
if (strlen(optarg) >= IFNAMSIZ)
big_iface_length:
xtables_error(PARAMETER_PROBLEM,
"Interface name length cannot exceed %d characters",
IFNAMSIZ - 1);
xtables_parse_interface(optarg, cs.fw.in, cs.fw.in_mask);
ebtables_parse_interface(optarg, cs.eb.in);
break;
} else if (c == 2) {
ebt_check_option2(&flags, OPT_LOGICALIN);
......@@ -1003,13 +1040,9 @@ big_iface_length:
xtables_error(PARAMETER_PROBLEM,
"Use --logical-in only in INPUT, FORWARD, PREROUTING and BROUTING chains");
if (ebt_check_inverse2(optarg, argc, argv))
cs.fw.invflags |= EBT_ILOGICALIN;
cs.eb.invflags |= EBT_ILOGICALIN;
if (strlen(optarg) >= IFNAMSIZ)
goto big_iface_length;
strcpy(cs.fw.logical_in, optarg);
if (parse_iface(cs.fw.logical_in, "--logical-in"))
return -1;
ebtables_parse_interface(optarg, cs.eb.logical_in);
break;
} else if (c == 'o') {
ebt_check_option2(&flags, OPT_OUT);
......@@ -1017,12 +1050,9 @@ big_iface_length:
xtables_error(PARAMETER_PROBLEM,
"Use -o only in OUTPUT, FORWARD and POSTROUTING chains");
if (ebt_check_inverse2(optarg, argc, argv))
cs.fw.invflags |= EBT_IOUT;
if (strlen(optarg) >= IFNAMSIZ)
goto big_iface_length;
cs.eb.invflags |= EBT_IOUT;
xtables_parse_interface(optarg, cs.fw.out, cs.fw.out_mask);
ebtables_parse_interface(optarg, cs.eb.out);
break;
} else if (c == 3) {
ebt_check_option2(&flags, OPT_LOGICALOUT);
......@@ -1030,36 +1060,32 @@ big_iface_length:
xtables_error(PARAMETER_PROBLEM,
"Use --logical-out only in OUTPUT, FORWARD and POSTROUTING chains");
if (ebt_check_inverse2(optarg, argc, argv))
cs.fw.invflags |= EBT_ILOGICALOUT;
cs.eb.invflags |= EBT_ILOGICALOUT;
if (strlen(optarg) >= IFNAMSIZ)
goto big_iface_length;
strcpy(cs.fw.logical_out, optarg);
if (parse_iface(cs.fw.logical_out, "--logical-out"))
return -1;
ebtables_parse_interface(optarg, cs.eb.logical_out);
break;
} else if (c == 'j') {
ebt_check_option2(&flags, OPT_JUMP);
cs.jumpto = parse_target(optarg);
cs.target = command_jump(&cs, cs.jumpto);
cs.target = ebt_command_jump(cs.jumpto);
break;
} else if (c == 's') {
ebt_check_option2(&flags, OPT_SOURCE);
if (ebt_check_inverse2(optarg, argc, argv))
cs.fw.invflags |= EBT_ISOURCE;
cs.eb.invflags |= EBT_ISOURCE;
if (ebt_get_mac_and_mask(optarg, cs.fw.sourcemac, cs.fw.sourcemsk))
if (ebt_get_mac_and_mask(optarg, cs.eb.sourcemac, cs.eb.sourcemsk))
xtables_error(PARAMETER_PROBLEM, "Problem with specified source mac '%s'", optarg);
cs.fw.bitmask |= EBT_SOURCEMAC;
cs.eb.bitmask |= EBT_SOURCEMAC;
break;
} else if (c == 'd') {
ebt_check_option2(&flags, OPT_DEST);
if (ebt_check_inverse2(optarg, argc, argv))
cs.fw.invflags |= EBT_IDEST;
cs.eb.invflags |= EBT_IDEST;
if (ebt_get_mac_and_mask(optarg, cs.fw.destmac, cs.fw.destmsk))
if (ebt_get_mac_and_mask(optarg, cs.eb.destmac, cs.eb.destmsk))
xtables_error(PARAMETER_PROBLEM, "Problem with specified destination mac '%s'", optarg);
cs.fw.bitmask |= EBT_DESTMAC;
cs.eb.bitmask |= EBT_DESTMAC;
break;
} else if (c == 'c') {
ebt_check_option2(&flags, OPT_COUNT);
......@@ -1085,38 +1111,33 @@ big_iface_length:
}
ebt_check_option2(&flags, OPT_PROTOCOL);
if (ebt_check_inverse2(optarg, argc, argv))
cs.fw.invflags |= EBT_IPROTO;
cs.eb.invflags |= EBT_IPROTO;
cs.fw.bitmask &= ~((unsigned int)EBT_NOPROTO);
cs.eb.bitmask &= ~((unsigned int)EBT_NOPROTO);
i = strtol(optarg, &buffer, 16);
if (*buffer == '\0' && (i < 0 || i > 0xFFFF))
xtables_error(PARAMETER_PROBLEM,
"Problem with the specified protocol");
if (*buffer != '\0') {
struct ethertypeent *ent;
struct xt_ethertypeent *ent;
if (!strcasecmp(optarg, "LENGTH")) {
cs.fw.bitmask |= EBT_802_3;
cs.eb.bitmask |= EBT_802_3;
break;
}
ent = getethertypebyname(optarg);
ent = xtables_getethertypebyname(optarg);
if (!ent)
xtables_error(PARAMETER_PROBLEM,
"Problem with the specified Ethernet protocol '%s', perhaps "_PATH_ETHERTYPES " is missing", optarg);
cs.fw.ethproto = ent->e_ethertype;
"Problem with the specified Ethernet protocol '%s', perhaps "XT_PATH_ETHERTYPES " is missing", optarg);
cs.eb.ethproto = ent->e_ethertype;
} else
cs.fw.ethproto = i;
cs.eb.ethproto = i;
if (cs.fw.ethproto < 0x0600)
if (cs.eb.ethproto < 0x0600)
xtables_error(PARAMETER_PROBLEM,
"Sorry, protocols have values above or equal to 0x0600");
break;
case 4 : /* Lc */
#ifdef SILENT_DAEMON
if (exec_style == EXEC_STYLE_DAEMON)
xtables_error(PARAMETER_PROBLEM,
"--Lc is not supported in daemon mode");
#endif
ebt_check_option2(&flags, LIST_C);
if (command != 'L')
xtables_error(PARAMETER_PROBLEM,
......@@ -1124,11 +1145,6 @@ big_iface_length:
flags |= LIST_C;
break;
case 5 : /* Ln */
#ifdef SILENT_DAEMON
if (exec_style == EXEC_STYLE_DAEMON)
xtables_error(PARAMETER_PROBLEM,
"--Ln is not supported in daemon mode");
#endif
ebt_check_option2(&flags, LIST_N);
if (command != 'L')
xtables_error(PARAMETER_PROBLEM,
......@@ -1139,11 +1155,6 @@ big_iface_length:
flags |= LIST_N;
break;
case 6 : /* Lx */
#ifdef SILENT_DAEMON
if (exec_style == EXEC_STYLE_DAEMON)
xtables_error(PARAMETER_PROBLEM,
"--Lx is not supported in daemon mode");
#endif
ebt_check_option2(&flags, LIST_X);
if (command != 'L')
xtables_error(PARAMETER_PROBLEM,
......@@ -1154,11 +1165,6 @@ big_iface_length:
flags |= LIST_X;
break;
case 12 : /* Lmac2 */
#ifdef SILENT_DAEMON
if (exec_style == EXEC_STYLE_DAEMON)
xtables_error(PARAMETER_PROBLEM,
"--Lmac2 is not supported in daemon mode");
#endif
ebt_check_option2(&flags, LIST_MAC2);
if (command != 'L')
xtables_error(PARAMETER_PROBLEM,
......@@ -1166,8 +1172,7 @@ big_iface_length:
flags |= LIST_MAC2;
break;
case 8 : /* atomic-commit */
/* if (exec_style == EXEC_STYLE_DAEMON)
ebt_print_error2("--atomic-commit is not supported in daemon mode");
/*
replace->command = c;
if (OPT_COMMANDS)
ebt_print_error2("Multiple commands are not allowed");
......@@ -1186,14 +1191,10 @@ big_iface_length:
break;*/
/*case 7 :*/ /* atomic-init */
/*case 10:*/ /* atomic-save */
/*case 11:*/ /* init-table */
/* if (exec_style == EXEC_STYLE_DAEMON) {
if (c == 7) {
ebt_print_error2("--atomic-init is not supported in daemon mode");
} else if (c == 10)
ebt_print_error2("--atomic-save is not supported in daemon mode");
ebt_print_error2("--init-table is not supported in daemon mode");
}
case 11: /* init-table */
nft_table_flush(h, *table);
return 1;
/*
replace->command = c;
if (OPT_COMMANDS)
ebt_print_error2("Multiple commands are not allowed");
......@@ -1210,20 +1211,16 @@ big_iface_length:
}
break;
case 9 :*/ /* atomic */
/*if (exec_style == EXEC_STYLE_DAEMON)
ebt_print_error2("--atomic is not supported in daemon mode");
/*
if (OPT_COMMANDS)
ebt_print_error2("--atomic has to come before the command");*/
/* A possible memory leak here, but this is not
* executed in daemon mode */
/*replace->filename = (char *)malloc(strlen(optarg) + 1);
strcpy(replace->filename, optarg);
break; */
case 13 :
break;
case 13 : *//* concurrent */
/*signal(SIGINT, sighandler);
signal(SIGTERM, sighandler);
use_lockfd = 1;
break;*/
case 1 :
if (!strcmp(optarg, "!"))
ebt_check_inverse2(optarg, argc, argv);
......@@ -1234,49 +1231,13 @@ big_iface_length:
optind--;
continue;
default:
/* Is it a target option? */
if (cs.target != NULL && cs.target->parse != NULL) {
int opt_offset = cs.target->option_offset;
if (cs.target->parse(c - opt_offset,
argv, ebt_invert,
&cs.target->tflags,
NULL, &cs.target->t))
goto check_extension;
}
ebt_check_inverse2(optarg, argc, argv);
/* Is it a match_option? */
for (m = xtables_matches; m; m = m->next) {
if (m->parse(c - m->option_offset, argv, ebt_invert, &m->mflags, NULL, &m->m)) {
ebt_add_match(m, &cs);
goto check_extension;
}
}
if (ebt_command_default(&cs))
xtables_error(PARAMETER_PROBLEM,
"Unknown argument: '%s'",
argv[optind - 1]);
/* Is it a watcher option? */
for (w = xtables_targets; w; w = w->next) {
if (w->parse(c - w->option_offset, argv,
ebt_invert, &w->tflags,
NULL, &w->t)) {
ebt_add_watcher(w, &cs);
goto check_extension;
}
}
/*
if (w == NULL && c == '?')
ebt_print_error2("Unknown argument: '%s'", argv[optind - 1], (char)optopt, (char)c);
else if (w == NULL) {
if (!strcmp(t->name, "standard"))
ebt_print_error2("Unknown argument: don't forget the -t option");
else
ebt_print_error2("Target-specific option does not correspond with specified target");
}
if (ebt_errormsg[0] != '\0')
return -1;
if (w->used == 0) {
ebt_add_watcher(new_entry, w);
w->used = 1;
}*/
check_extension:
if (command != 'A' && command != 'I' &&
command != 'D' && command != 'C')
xtables_error(PARAMETER_PROBLEM,
......@@ -1294,7 +1255,6 @@ check_extension:
if (command == 'h' && !(flags & OPT_ZERO)) {
print_help(cs.target, cs.matches, *table);
if (exec_style == EXEC_STYLE_PRG)
exit(0);
}
......@@ -1316,7 +1276,7 @@ check_extension:
}
/* So, the extensions can work with the host endian.
* The kernel does not have to do this of course */
cs.fw.ethproto = htons(cs.fw.ethproto);
cs.eb.ethproto = htons(cs.eb.ethproto);
if (command == 'P') {
if (selected_chain < 0) {
......@@ -1333,46 +1293,29 @@ check_extension:
xtables_error(PARAMETER_PROBLEM, "Wrong policy");
} else if (command == 'L') {
ret = list_rules(h, chain, *table, rule_nr,
flags&OPT_VERBOSE,
flags&OPT_NUMERIC,
0,
0,
/*flags&OPT_EXPANDED*/0,
flags&LIST_N,
flags&LIST_C);
if (!(flags & OPT_ZERO) && exec_style == EXEC_STYLE_PRG)
exit(0);
}
if (flags & OPT_ZERO) {
selected_chain = zerochain;
ret = nft_chain_zero_counters(h, chain, *table);
ret = nft_chain_zero_counters(h, chain, *table, 0);
} else if (command == 'F') {
ret = nft_rule_flush(h, chain, *table);
ret = nft_rule_flush(h, chain, *table, 0);
} else if (command == 'A') {
ret = append_entry(h, chain, *table, &cs, 0,
flags&OPT_VERBOSE, true);
ret = append_entry(h, chain, *table, &cs, 0, 0, true);
} else if (command == 'I') {
ret = append_entry(h, chain, *table, &cs, rule_nr - 1,
flags&OPT_VERBOSE, false);
0, false);
} else if (command == 'D') {
ret = delete_entry(h, chain, *table, &cs, rule_nr - 1,
rule_nr_end, flags&OPT_VERBOSE);
rule_nr_end, 0);
} /*else if (replace->command == 'C') {
ebt_change_counters(replace, new_entry, rule_nr, rule_nr_end, &(new_entry->cnt_surplus), chcounter);
if (ebt_errormsg[0] != '\0')
return -1;
}*/
/* Commands -N, -E, -X, --atomic-commit, --atomic-commit, --atomic-save,
* --init-table fall through */
/*if (ebt_errormsg[0] != '\0')
return -1;
if (table->check)
table->check(replace);
if (exec_style == EXEC_STYLE_PRG) {*//* Implies ebt_errormsg[0] == '\0' */
/*ebt_deliver_table(replace);
if (replace->nentries)
ebt_deliver_counters(replace);*/
ebt_cs_clean(&cs);
return ret;
......
......@@ -25,6 +25,11 @@ static const struct subcommand multi_subcommands[] = {
{"save4", iptables_save_main},
{"iptables-restore", iptables_restore_main},
{"restore4", iptables_restore_main},
{"iptables-legacy", iptables_main},
{"iptables-legacy-save",iptables_save_main},
{"iptables-legacy-restore",iptables_restore_main},
#endif
{"iptables-xml", iptables_xml_main},
{"xml", iptables_xml_main},
......@@ -35,15 +40,9 @@ static const struct subcommand multi_subcommands[] = {
{"save6", ip6tables_save_main},
{"ip6tables-restore", ip6tables_restore_main},
{"restore6", ip6tables_restore_main},
#endif
#ifdef ENABLE_NFTABLES
{"xtables", xtables_main},
{"xtables-save", xtables_save_main},
{"xtables-restore", xtables_restore_main},
{"xtables-config", xtables_config_main},
{"xtables-events", xtables_events_main},
{"xtables-arp", xtables_arp_main},
{"xtables-ebtables", xtables_eb_main},
{"ip6tables-legacy", ip6tables_main},
{"ip6tables-legacy-save",ip6tables_save_main},
{"ip6tables-legacy-restore",ip6tables_restore_main},
#endif
{NULL},
};
......
.\"
.\" (C) Copyright 2016-2017, Arturo Borrero Gonzalez <arturo@netfilter.org>
.\"
.\" %%%LICENSE_START(GPLv2+_DOC_FULL)
.\" This is free documentation; 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.
.\"
.\" The GNU General Public License's references to "object code"
.\" and "executables" are to be interpreted as the output of any
.\" document formatting or typesetting system, including
.\" intermediate and printed output.
.\"
.\" This manual is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
.\" GNU General Public License for more details.
.\"
.\" You should have received a copy of the GNU General Public
.\" License along with this manual; if not, see
.\" <http://www.gnu.org/licenses/>.
.\" %%%LICENSE_END
.\"
.TH XTABLES-LEGACY 8 "June 2018"
.SH NAME
xtables-legacy \(em iptables using old getsockopt/setsockopt-based kernel api
.SH DESCRIPTION
\fBxtables-legacy\fP are the original versions of iptables that use
old getsockopt/setsockopt-based kernel interface.
This kernel interface has some limitations, therefore iptables can also
be used with the newer nf_tables based API.
See
.B xtables\-nft(8)
for information about the xtables-nft variants of iptables.
.SH USAGE
The xtables-legacy-multi binary can be linked to the traditional names:
.nf
/sbin/iptables -> /sbin/iptables\-legacy\-multi
/sbin/ip6tables -> /sbin/ip6tables\-legacy\-multi
/sbin/iptables\-save -> /sbin/ip6tables\-legacy\-multi
/sbin/iptables\-restore -> /sbin/ip6tables\-legacy\-multi
.fi
The iptables version string will indicate whether the legacy API (get/setsockopt) or
the new nf_tables API is used:
.nf
iptables \-V
iptables v1.7 (legacy)
.fi
.SH LIMITATIONS
When inserting a rule using
iptables \-A or iptables \-I, iptables first needs to retrieve the current active
ruleset, change it to include the new rule, and then commit back the result.
This means that if two instances of iptables are running concurrently, one of the
updates might be lost. This can be worked around partially with the \-\-wait option.
There is also no method to monitor changes to the ruleset, except periodically calling
iptables-legacy-save and checking for any differences in output.
.B xtables\-monitor(8)
will need the
.B xtables\-nft(8)
versions to work, it cannot display changes made using the.
.B iptables-legacy
tools.
.SH SEE ALSO
\fBxtables\-nft(8)\fP, \fBxtables\-translate(8)\fP
.SH AUTHORS
Rusty Russell originally wrote iptables, in early consultation with Michael Neuling.
.TH XTABLES\-MONITOR 8 "" "@PACKAGE_STRING@" "@PACKAGE_STRING@"
.SH NAME
xtables-monitor \(em show changes to rule set and trace-events
.SH SYNOPSIS
\fBxtables\-monitor\fP [\fB\-t\fP] [\fB\-e\fP] [\fB\-4\fP|\fB|\-6\fB]
.PP
\
.SH DESCRIPTION
.PP
.B xtables-monitor
is used to monitor changes to the ruleset or to show rule evaluation events
for packets tagged using the TRACE target.
.B xtables-monitor
will run until the user aborts execution, typically by using CTRL-C.
.RE
.SH OPTIONS
\fB\-e\fP, \fB\-\-event\fP
.TP
Watch for updates to the rule set.
Updates include creation of new tables, chains and rules and
the name of the program that caused the rule update.
.TP
\fB\-t\fP, \fB\-\-trace\fP
Watch for trace events generated by packets that have been tagged
using the TRACE target.
.TP
\fB\-4\fP
Restrict output to IPv4.
.TP
\fB\-6\fP
Restrict output to IPv6.
.SH EXAMPLE OUTPUT
.TP
.B xtables-monitor \-\-trace
1 TRACE: 2 fc475095 raw:PREROUTING:rule:0x3:CONTINUE \-4 \-t raw \-A PREROUTING \-p icmp \-j TRACE
2 PACKET: 0 fc475095 IN=lo LL=0x304 0000000000000000000000000800 SRC=127.0.0.1 DST=127.0.0.1 LEN=84 TOS=0x0 TTL=64 ID=38349DF
3 TRACE: 2 fc475095 raw:PREROUTING:return:
4 TRACE: 2 fc475095 raw:PREROUTING:policy:ACCEPT
5 TRACE: 2 fc475095 filter:INPUT:return:
6 TRACE: 2 fc475095 filter:INPUT:policy:DROP
7 TRACE: 2 0df9d3d8 raw:PREROUTING:rule:0x3:CONTINUE \-4 \-t raw \-A PREROUTING \-p icmp \-j TRACE
.PP
The first line shows a packet entering rule set evaluation.
The protocol number is shown (AF_INET in this case), then a packet
identifier number that allows to correlate messages coming from rule set evaluation of
this packet. After this, the rule that was matched by the packet is shown.
This is the TRACE rule that turns on tracing events for this packet.
The second line dumps information about the packet. Incoming interface
and packet headers such as source and destination addresses are shown.
The third line shows that the packet completed traversal of the raw table
PREROUTING chain, and is returning, followed by use the chain policy to make accept/drop
decision (the example shows accept being applied).
The fifth line shows that the packet leaves the filter INPUT chain, i.e., no rules in the filter tables
INPUT chain matched the packet.
It then got DROPPED by the policy of the INPUT table, as shown by line six.
The last line shows another packet arriving \-\- the packet id is different.
When using the TRACE target, it is usually a good idea to only select packets
that are relevant, for example via
.nf
iptables \-t raw \-A PREROUTING \-p tcp \-\-dport 80 \-\-syn \-m limit \-\-limit 1/s \-j TRACE
.fi
.TP
.B xtables-monitor \-\-event
1 EVENT: nft: NEW table: table filter ip flags 0 use 4 handle 444
2 EVENT: # nft: ip filter INPUT use 2 type filter hook input prio 0 policy drop packets 0 bytes 0
3 EVENT: # nft: ip filter FORWARD use 0 type filter hook forward prio 0 policy accept packets 0 bytes 0
4 EVENT: # nft: ip filter OUTPUT use 0 type filter hook output prio 0 policy accept packets 0 bytes 0
5 EVENT: \-4 \-t filter \-N TCP
6 EVENT: \-4 \-t filter \-A TCP \-s 192.168.0.0/16 \-p tcp \-m tcp \-\-dport 22 \-j ACCEPT
7 EVENT: \-4 \-t filter \-A TCP \-p tcp \-m multiport \-\-dports 80,443 \-j ACCEPT
8 EVENT: \-4 \-t filter \-A INPUT \-p tcp \-j TCP
9 EVENT: \-4 \-t filter \-A INPUT \-m conntrack \-\-ctstate RELATED,ESTABLISHED \-j ACCEPT
10 NEWGEN: GENID=13904 PID=25167 NAME=iptables-nftables-restore
.PP
This example shows event monitoring. Line one shows creation of a table (filter in this case), followed
by three base hooks INPUT, FORWARD and OUTPUT. The iptables-nftables tools all create tables and base
chains automatically when needed, so this is expected when a table was not yet initialized or when it is
re-created from scratch by iptables-nftables-restore. Line five shows a new user-defined chain (TCP)
being added, followed by addition a few rules. the last line shows that a new ruleset generation has
become active, i.e., the rule set changes are now active. This also lists the process id and the programs name.
.SH LIMITATIONS
.B xtables-monitor
only works with rules added using iptables-nftables, rules added using
iptables-legacy cannot be monitored.
.SH BUGS
Should be reported or by sending email to netfilter-devel@vger.kernel.org or
by filing a report on https://bugzilla.netfilter.org/.
.SH SEE ALSO
\fBiptables\fP(8), \fBxtables\fP(8), \fBnft\fP(8)
/*
* (C) 2012-2013 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 software has been sponsored by Sophos Astaro <http://www.sophos.com>
*/
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <netinet/ether.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <net/if_arp.h>
#include <getopt.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nf_tables.h>
#include <libmnl/libmnl.h>
#include <libnftnl/table.h>
#include <libnftnl/trace.h>
#include <libnftnl/chain.h>
#include <libnftnl/rule.h>
#include <include/xtables.h>
#include "iptables.h" /* for xtables_globals */
#include "xtables-multi.h"
#include "nft.h"
#include "nft-arp.h"
struct cb_arg {
uint32_t nfproto;
bool is_event;
};
static int table_cb(const struct nlmsghdr *nlh, void *data)
{
uint32_t type = nlh->nlmsg_type & 0xFF;
const struct cb_arg *arg = data;
struct nftnl_table *t;
char buf[4096];
t = nftnl_table_alloc();
if (t == NULL)
goto err;
if (nftnl_table_nlmsg_parse(nlh, t) < 0)
goto err_free;
if (arg->nfproto && arg->nfproto != nftnl_table_get_u32(t, NFTNL_TABLE_FAMILY))
goto err_free;
nftnl_table_snprintf(buf, sizeof(buf), t, NFTNL_OUTPUT_DEFAULT, 0);
printf(" EVENT: ");
printf("nft: %s table: %s\n", type == NFT_MSG_NEWTABLE ? "NEW" : "DEL", buf);
err_free:
nftnl_table_free(t);
err:
return MNL_CB_OK;
}
static bool counters;
static bool trace;
static bool events;
static int rule_cb(const struct nlmsghdr *nlh, void *data)
{
uint32_t type = nlh->nlmsg_type & 0xFF;
const struct cb_arg *arg = data;
struct nftnl_rule *r;
uint8_t family;
r = nftnl_rule_alloc();
if (r == NULL)
goto err;
if (nftnl_rule_nlmsg_parse(nlh, r) < 0)
goto err_free;
family = nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY);
if (arg->nfproto && arg->nfproto != family)
goto err_free;
if (arg->is_event)
printf(" EVENT: ");
switch (family) {
case AF_INET:
case AF_INET6:
printf("-%c ", family == AF_INET ? '4' : '6');
break;
case NFPROTO_ARP:
printf("-0 ");
break;
default:
goto err_free;
}
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_DEL,
counters ? 0 : FMT_NOCOUNTS);
err_free:
nftnl_rule_free(r);
err:
return MNL_CB_OK;
}
static int chain_cb(const struct nlmsghdr *nlh, void *data)
{
uint32_t type = nlh->nlmsg_type & 0xFF;
const struct cb_arg *arg = data;
struct nftnl_chain *c;
char buf[4096];
int family;
c = nftnl_chain_alloc();
if (c == NULL)
goto err;
if (nftnl_chain_nlmsg_parse(nlh, c) < 0)
goto err_free;
family = nftnl_chain_get_u32(c, NFTNL_CHAIN_FAMILY);
if (arg->nfproto && arg->nfproto != family)
goto err_free;
if (nftnl_chain_is_set(c, NFTNL_CHAIN_PRIO))
family = -1;
printf(" EVENT: ");
switch (family) {
case NFPROTO_IPV4:
family = 4;
break;
case NFPROTO_IPV6:
family = 6;
break;
default:
nftnl_chain_snprintf(buf, sizeof(buf), c, NFTNL_OUTPUT_DEFAULT, 0);
printf("# nft: %s\n", buf);
goto err_free;
}
printf("-%d -t %s -%c %s\n",
family,
nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE),
type == NFT_MSG_NEWCHAIN ? 'N' : 'X',
nftnl_chain_get_str(c, NFTNL_CHAIN_NAME));
err_free:
nftnl_chain_free(c);
err:
return MNL_CB_OK;
}
static int newgen_cb(const struct nlmsghdr *nlh, void *data)
{
uint32_t genid = 0, pid = 0;
const struct nlattr *attr;
const char *name = NULL;
mnl_attr_for_each(attr, nlh, sizeof(struct nfgenmsg)) {
switch (mnl_attr_get_type(attr)) {
case NFTA_GEN_ID:
if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
break;
genid = ntohl(mnl_attr_get_u32(attr));
break;
case NFTA_GEN_PROC_NAME:
if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0)
break;
name = mnl_attr_get_str(attr);
break;
case NFTA_GEN_PROC_PID:
if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
break;
pid = ntohl(mnl_attr_get_u32(attr));
break;
}
}
if (name)
printf("NEWGEN: GENID=%u PID=%u NAME=%s\n", genid, pid, name);
return MNL_CB_OK;
}
static void trace_print_return(const struct nftnl_trace *nlt)
{
const char *chain = NULL;
if (nftnl_trace_is_set(nlt, NFTNL_TRACE_JUMP_TARGET)) {
chain = nftnl_trace_get_str(nlt, NFTNL_TRACE_JUMP_TARGET);
printf("%s", chain);
}
}
static void trace_print_rule(const struct nftnl_trace *nlt, struct cb_arg *args)
{
uint64_t handle = nftnl_trace_get_u64(nlt, NFTNL_TRACE_RULE_HANDLE);
uint32_t family = nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY);
const char *table = nftnl_trace_get_str(nlt, NFTNL_TRACE_TABLE);
const char *chain = nftnl_trace_get_str(nlt, NFTNL_TRACE_CHAIN);
struct nftnl_rule *r;
struct mnl_socket *nl;
struct nlmsghdr *nlh;
uint32_t portid;
char buf[16536];
int ret;
r = nftnl_rule_alloc();
if (r == NULL) {
perror("OOM");
exit(EXIT_FAILURE);
}
nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, family, NLM_F_DUMP, 0);
nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family);
nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain);
nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table);
nftnl_rule_set_u64(r, NFTNL_RULE_POSITION, handle);
nftnl_rule_nlmsg_build_payload(nlh, r);
nftnl_rule_free(r);
nl = mnl_socket_open(NETLINK_NETFILTER);
if (nl == NULL) {
perror("mnl_socket_open");
exit(EXIT_FAILURE);
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
perror("mnl_socket_bind");
exit(EXIT_FAILURE);
}
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
perror("mnl_socket_send");
exit(EXIT_FAILURE);
}
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
while (ret > 0) {
args->is_event = false;
ret = mnl_cb_run(buf, ret, 0, portid, rule_cb, args);
if (ret <= 0)
break;
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
}
if (ret == -1) {
perror("error");
exit(EXIT_FAILURE);
}
mnl_socket_close(nl);
}
static void trace_print_packet(const struct nftnl_trace *nlt, struct cb_arg *args)
{
struct list_head stmts = LIST_HEAD_INIT(stmts);
uint32_t nfproto, family;
uint16_t l4proto = 0;
uint32_t mark;
char name[IFNAMSIZ];
printf("PACKET: %d %08x ", args->nfproto, nftnl_trace_get_u32(nlt, NFTNL_TRACE_ID));
if (nftnl_trace_is_set(nlt, NFTNL_TRACE_IIF))
printf("IN=%s ", if_indextoname(nftnl_trace_get_u32(nlt, NFTNL_TRACE_IIF), name));
if (nftnl_trace_is_set(nlt, NFTNL_TRACE_OIF))
printf("OUT=%s ", if_indextoname(nftnl_trace_get_u32(nlt, NFTNL_TRACE_OIF), name));
family = nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY);
nfproto = family;
if (nftnl_trace_is_set(nlt, NFTNL_TRACE_NFPROTO)) {
nfproto = nftnl_trace_get_u32(nlt, NFTNL_TRACE_NFPROTO);
if (family != nfproto)
printf("NFPROTO=%d ", nfproto);
}
if (nftnl_trace_is_set(nlt, NFTNL_TRACE_LL_HEADER)) {
const struct ethhdr *eh;
const char *linklayer;
uint32_t i, len;
uint16_t type = nftnl_trace_get_u16(nlt, NFTNL_TRACE_IIFTYPE);
linklayer = nftnl_trace_get_data(nlt, NFTNL_TRACE_LL_HEADER, &len);
switch (type) {
case ARPHRD_ETHER:
if (len < sizeof(*eh))
break;
eh = (const void *)linklayer;
printf("MACSRC=%s ", ether_ntoa((const void *)eh->h_source));
printf("MACDST=%s ", ether_ntoa((const void *)eh->h_dest));
printf("MACPROTO=%04x ", ntohs(eh->h_proto));
break;
default:
printf("LL=0x%x ", type);
for (i = 0 ; i < len; i++)
printf("%02x", linklayer[i]);
printf(" ");
break;
}
}
if (nftnl_trace_is_set(nlt, NFTNL_TRACE_NETWORK_HEADER)) {
const struct ip6_hdr *ip6h;
const struct iphdr *iph;
uint32_t i, len;
const char *nh;
ip6h = nftnl_trace_get_data(nlt, NFTNL_TRACE_NETWORK_HEADER, &len);
switch (nfproto) {
case NFPROTO_IPV4: {
char addrbuf[INET_ADDRSTRLEN];
if (len < sizeof(*iph))
break;
iph = (const void *)ip6h;
inet_ntop(AF_INET, &iph->saddr, addrbuf, sizeof(addrbuf));
printf("SRC=%s ", addrbuf);
inet_ntop(AF_INET, &iph->daddr, addrbuf, sizeof(addrbuf));
printf("DST=%s ", addrbuf);
printf("LEN=%d TOS=0x%x TTL=%d ID=%d", ntohs(iph->tot_len), iph->tos, iph->ttl, ntohs(iph->id));
if (iph->frag_off & htons(0x8000))
printf("CE ");
if (iph->frag_off & htons(IP_DF))
printf("DF ");
if (iph->frag_off & htons(IP_MF))
printf("MF ");
if (ntohs(iph->frag_off) & 0x1fff)
printf("FRAG:%u ", ntohs(iph->frag_off) & 0x1fff);
l4proto = iph->protocol;
if (iph->ihl * 4 > sizeof(*iph)) {
unsigned int optsize;
const char *op;
optsize = iph->ihl * 4 - sizeof(*iph);
op = (const char *)iph;
op += sizeof(*iph);
printf("OPT (");
for (i = 0; i < optsize; i++)
printf("%02X", op[i]);
printf(")");
}
break;
}
case NFPROTO_IPV6: {
uint32_t flowlabel = ntohl(*(uint32_t *)ip6h);
char addrbuf[INET6_ADDRSTRLEN];
if (len < sizeof(*ip6h))
break;
inet_ntop(AF_INET6, &ip6h->ip6_src, addrbuf, sizeof(addrbuf));
printf("SRC=%s ", addrbuf);
inet_ntop(AF_INET6, &ip6h->ip6_dst, addrbuf, sizeof(addrbuf));
printf("DST=%s ", addrbuf);
printf("LEN=%zu TC=%u HOPLIMIT=%u FLOWLBL=%u ",
ntohs(ip6h->ip6_plen) + sizeof(*iph),
(flowlabel & 0x0ff00000) >> 20,
ip6h->ip6_hops,
flowlabel & 0x000fffff);
l4proto = ip6h->ip6_nxt;
break;
}
default:
nh = (const char *)ip6h;
printf("NH=");
for (i = 0 ; i < len; i++)
printf("%02x", nh[i]);
printf(" ");
}
}
if (nftnl_trace_is_set(nlt, NFTNL_TRACE_TRANSPORT_HEADER)) {
const struct tcphdr *tcph;
uint32_t len;
tcph = nftnl_trace_get_data(nlt, NFTNL_TRACE_TRANSPORT_HEADER, &len);
switch (l4proto) {
case IPPROTO_DCCP:
case IPPROTO_SCTP:
case IPPROTO_UDPLITE:
case IPPROTO_UDP:
if (len < 4)
break;
printf("SPORT=%d DPORT=%d ", ntohs(tcph->th_sport), ntohs(tcph->th_dport));
break;
case IPPROTO_TCP:
if (len < sizeof(*tcph))
break;
printf("SPORT=%d DPORT=%d ", ntohs(tcph->th_sport), ntohs(tcph->th_dport));
if (tcph->th_flags & (TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG)) {
if (tcph->th_flags & TH_SYN)
printf("SYN ");
if (tcph->th_flags & TH_ACK)
printf("ACK ");
if (tcph->th_flags & TH_FIN)
printf("FIN ");
if (tcph->th_flags & TH_RST)
printf("RST ");
if (tcph->th_flags & TH_PUSH)
printf("PSH ");
if (tcph->th_flags & TH_URG)
printf("URG ");
}
break;
default:
break;
}
}
mark = nftnl_trace_get_u32(nlt, NFTNL_TRACE_MARK);
if (mark)
printf("MARK=0x%x ", mark);
}
static void print_verdict(struct nftnl_trace *nlt, uint32_t verdict)
{
const char *chain;
switch (verdict) {
case NF_ACCEPT:
printf("ACCEPT");
break;
case NF_DROP:
printf("DROP");
break;
case NF_QUEUE:
printf("QUEUE");
break;
case NF_STOLEN:
printf("STOLEN");
break;
case NFT_BREAK:
printf("BREAK");
break;
case NFT_CONTINUE:
printf("CONTINUE");
break;
case NFT_GOTO:
printf("GOTO");
if (nftnl_trace_is_set(nlt, NFTNL_TRACE_JUMP_TARGET)) {
chain = nftnl_trace_get_str(nlt, NFTNL_TRACE_JUMP_TARGET);
printf(":%s", chain);
}
break;
case NFT_JUMP:
printf("JUMP");
if (nftnl_trace_is_set(nlt, NFTNL_TRACE_JUMP_TARGET)) {
chain = nftnl_trace_get_str(nlt, NFTNL_TRACE_JUMP_TARGET);
printf(":%s", chain);
}
break;
default:
printf("0x%x", verdict);
break;
}
printf(" ");
}
static int trace_cb(const struct nlmsghdr *nlh, struct cb_arg *arg)
{
struct nftnl_trace *nlt;
uint32_t verdict;
nlt = nftnl_trace_alloc();
if (nlt == NULL)
goto err;
if (nftnl_trace_nlmsg_parse(nlh, nlt) < 0)
goto err_free;
if (arg->nfproto &&
arg->nfproto != nftnl_trace_get_u32(nlt, NFTNL_TABLE_FAMILY))
goto err_free;
printf(" TRACE: %d %08x %s:%s", nftnl_trace_get_u32(nlt, NFTNL_TABLE_FAMILY),
nftnl_trace_get_u32(nlt, NFTNL_TRACE_ID),
nftnl_trace_get_str(nlt, NFTNL_TRACE_TABLE),
nftnl_trace_get_str(nlt, NFTNL_TRACE_CHAIN));
switch (nftnl_trace_get_u32(nlt, NFTNL_TRACE_TYPE)) {
case NFT_TRACETYPE_RULE:
verdict = nftnl_trace_get_u32(nlt, NFTNL_TRACE_VERDICT);
printf(":rule:0x%llx:", (unsigned long long)nftnl_trace_get_u64(nlt, NFTNL_TRACE_RULE_HANDLE));
print_verdict(nlt, verdict);
if (nftnl_trace_is_set(nlt, NFTNL_TRACE_RULE_HANDLE))
trace_print_rule(nlt, arg);
if (nftnl_trace_is_set(nlt, NFTNL_TRACE_LL_HEADER) ||
nftnl_trace_is_set(nlt, NFTNL_TRACE_NETWORK_HEADER))
trace_print_packet(nlt, arg);
break;
case NFT_TRACETYPE_POLICY:
printf(":policy:");
verdict = nftnl_trace_get_u32(nlt, NFTNL_TRACE_POLICY);
print_verdict(nlt, verdict);
break;
case NFT_TRACETYPE_RETURN:
printf(":return:");
trace_print_return(nlt);
break;
}
puts("");
err_free:
nftnl_trace_free(nlt);
err:
return MNL_CB_OK;
}
static int monitor_cb(const struct nlmsghdr *nlh, void *data)
{
uint32_t type = nlh->nlmsg_type & 0xFF;
struct cb_arg *arg = data;
int ret = MNL_CB_OK;
switch(type) {
case NFT_MSG_NEWTABLE:
case NFT_MSG_DELTABLE:
ret = table_cb(nlh, data);
break;
case NFT_MSG_NEWCHAIN:
case NFT_MSG_DELCHAIN:
ret = chain_cb(nlh, data);
break;
case NFT_MSG_NEWRULE:
case NFT_MSG_DELRULE:
arg->is_event = true;
ret = rule_cb(nlh, data);
break;
case NFT_MSG_NEWGEN:
ret = newgen_cb(nlh, data);
break;
case NFT_MSG_TRACE:
ret = trace_cb(nlh, data);
break;
}
return ret;
}
static const struct option options[] = {
{.name = "counters", .has_arg = false, .val = 'c'},
{.name = "trace", .has_arg = false, .val = 't'},
{.name = "event", .has_arg = false, .val = 'e'},
{.name = "ipv4", .has_arg = false, .val = '4'},
{.name = "ipv6", .has_arg = false, .val = '6'},
{.name = "version", .has_arg = false, .val = 'V'},
{.name = "help", .has_arg = false, .val = 'h'},
{NULL},
};
static void print_usage(void)
{
printf("%s %s\n", xtables_globals.program_name,
xtables_globals.program_version);
printf("Usage: %s [ -t | -e ]\n"
" --trace -t trace ruleset traversal of packets tagged via -j TRACE rule\n"
" --event -e show events that modify the ruleset\n"
"Optional arguments:\n"
" --ipv4 -4 only monitor IPv4\n"
" --ipv6 -6 only monitor IPv6\n"
" --counters -c show counters in rules\n"
, xtables_globals.program_name);
exit(EXIT_FAILURE);
}
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 = {};
int ret, c;
xtables_globals.program_name = "xtables-monitor";
/* XXX xtables_init_all does several things we don't want */
c = xtables_init_all(&xtables_globals, NFPROTO_IPV4);
if (c < 0) {
fprintf(stderr, "%s/%s Failed to initialize xtables\n",
xtables_globals.program_name,
xtables_globals.program_version);
exit(1);
}
#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
init_extensions();
init_extensions4();
#endif
opterr = 0;
while ((c = getopt_long(argc, argv, "ceht46V", options, NULL)) != -1) {
switch (c) {
case 'c':
counters = true;
break;
case 't':
trace = true;
break;
case 'e':
events = true;
break;
case 'h':
print_usage();
exit(0);
case '4':
cb_arg.nfproto = NFPROTO_IPV4;
break;
case '6':
cb_arg.nfproto = NFPROTO_IPV6;
break;
case 'V':
printf("xtables-monitor %s\n", IPTABLES_VERSION);
exit(0);
default:
fprintf(stderr, "xtables-monitor %s: Bad argument.\n", IPTABLES_VERSION);
fprintf(stderr, "Try `xtables-monitor -h' for more information.\n");
exit(PARAMETER_PROBLEM);
}
}
if (trace)
nfgroup |= 1 << (NFNLGRP_NFTRACE - 1);
if (events)
nfgroup |= 1 << (NFNLGRP_NFTABLES - 1);
if (nfgroup == 0) {
print_usage();
exit(EXIT_FAILURE);
}
nl = mnl_socket_open(NETLINK_NETFILTER);
if (nl == NULL) {
perror("cannot open nfnetlink socket");
exit(EXIT_FAILURE);
}
if (mnl_socket_bind(nl, nfgroup, MNL_SOCKET_AUTOPID) < 0) {
perror("cannot bind to nfnetlink socket");
exit(EXIT_FAILURE);
}
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
while (ret > 0) {
ret = mnl_cb_run(buf, ret, 0, 0, monitor_cb, &cb_arg);
if (ret <= 0)
break;
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
}
if (ret == -1) {
perror("cannot receive from nfnetlink socket");
exit(EXIT_FAILURE);
}
mnl_socket_close(nl);
return EXIT_SUCCESS;
}
......@@ -11,12 +11,17 @@ extern int xtables_ip6_save_main(int, char **);
extern int xtables_ip6_restore_main(int, char **);
extern int xtables_ip4_xlate_main(int, char **);
extern int xtables_ip6_xlate_main(int, char **);
extern int xtables_eb_xlate_main(int, char **);
extern int xtables_ip4_xlate_restore_main(int, char **);
extern int xtables_ip6_xlate_restore_main(int, char **);
extern int xtables_arp_main(int, char **);
extern int xtables_arp_restore_main(int, char **);
extern int xtables_arp_save_main(int, char **);
extern int xtables_eb_main(int, char **);
extern int xtables_eb_restore_main(int, char **);
extern int xtables_eb_save_main(int, char **);
extern int xtables_config_main(int, char **);
extern int xtables_events_main(int, char **);
extern int xtables_monitor_main(int, char **);
#endif
#endif /* _XTABLES_MULTI_H */
......@@ -9,30 +9,41 @@ static const struct subcommand multi_subcommands[] = {
{"iptables-xml", iptables_xml_main},
{"xml", iptables_xml_main},
{"iptables", xtables_ip4_main},
{"iptables-compat", xtables_ip4_main},
{"iptables-nft", xtables_ip4_main},
{"main4", xtables_ip4_main},
{"save4", xtables_ip4_save_main},
{"restore4", xtables_ip4_restore_main},
{"iptables-save", xtables_ip4_save_main},
{"iptables-restore", xtables_ip4_restore_main},
{"iptables-compat-save", xtables_ip4_save_main},
{"iptables-compat-restore", xtables_ip4_restore_main},
{"iptables-nft-save", xtables_ip4_save_main},
{"iptables-nft-restore", xtables_ip4_restore_main},
{"ip6tables", xtables_ip6_main},
{"ip6tables-compat", xtables_ip6_main},
{"ip6tables-nft", xtables_ip6_main},
{"main6", xtables_ip6_main},
{"save6", xtables_ip6_save_main},
{"restore6", xtables_ip6_restore_main},
{"ip6tables-save", xtables_ip6_save_main},
{"ip6tables-restore", xtables_ip6_restore_main},
{"ip6tables-compat-save", xtables_ip6_save_main},
{"ip6tables-compat-restore", xtables_ip6_restore_main},
{"ip6tables-nft-save", xtables_ip6_save_main},
{"ip6tables-nft-restore", xtables_ip6_restore_main},
{"iptables-translate", xtables_ip4_xlate_main},
{"ip6tables-translate", xtables_ip6_xlate_main},
{"iptables-restore-translate", xtables_ip4_xlate_restore_main},
{"ip6tables-restore-translate", xtables_ip6_xlate_restore_main},
{"arptables", xtables_arp_main},
{"arptables-compat", xtables_arp_main},
{"ebtables-compat", xtables_eb_main},
{"arptables-nft", xtables_arp_main},
{"arptables-restore", xtables_arp_restore_main},
{"arptables-nft-restore", xtables_arp_restore_main},
{"arptables-save", xtables_arp_save_main},
{"arptables-nft-save", xtables_arp_save_main},
{"ebtables-translate", xtables_eb_xlate_main},
{"ebtables", xtables_eb_main},
{"ebtables-restore", xtables_eb_restore_main},
{"ebtables-save", xtables_eb_save_main},
{"ebtables-nft", xtables_eb_main},
{"ebtables-nft-restore", xtables_eb_restore_main},
{"ebtables-nft-save", xtables_eb_save_main},
{"xtables-monitor", xtables_monitor_main},
{NULL},
};
......
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