Commit 278668fa authored by Arturo Borrero Gonzalez's avatar Arturo Borrero Gonzalez
Browse files

New upstream version 1.8.0

parent 5beab31f
......@@ -841,7 +841,7 @@ static struct xtables_target *command_jump(struct arpt_entry *fw,
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));
strncpy(target->t->u.user.name, jumpto, sizeof(target->t->u.user.name) - 1);
target->t->u.user.name[sizeof(target->t->u.user.name)-1] = '\0';
target->t->u.user.revision = target->revision;
......@@ -1259,7 +1259,7 @@ 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);
......
#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 <ebtables/ethernetdb.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
#define OPTION_OFFSET 256
static struct option *merge_options(struct option *oldopts,
const struct option *newopts,
unsigned int *options_offset)
{
unsigned int num_old, num_new, i;
struct option *merge;
if (!newopts || !oldopts || !options_offset)
return oldopts;
for (num_old = 0; oldopts[num_old].name; num_old++);
for (num_new = 0; newopts[num_new].name; num_new++);
ebtables_globals.option_offset += OPTION_OFFSET;
*options_offset = ebtables_globals.option_offset;
merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
if (!merge)
return NULL;
memcpy(merge, oldopts, num_old * sizeof(struct option));
for (i = 0; i < num_new; i++) {
merge[num_old + i] = newopts[i];
merge[num_old + i].val += *options_offset;
}
memset(merge + num_old + num_new, 0, sizeof(struct option));
/* Only free dynamically allocated stuff */
if (oldopts != ebt_original_options)
free(oldopts);
return merge;
}
/*
* More glue code.
*/
static struct xtables_target *command_jump(struct iptables_command_state *cs,
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;
snprintf(target->t->u.user.name,
sizeof(target->t->u.user.name), "%s", jumpto);
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");
return target;
}
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 xtables_target *t, *w;
struct xtables_match *m;
struct iptables_command_state cs;
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,
};
memset(&cs, 0, sizeof(cs));
cs.argv = argv;
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)
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
* 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;
for (t = xtables_targets; t; t = t->next) {
t->tflags = 0;
t->used = 0;
}
/* prevent getopt to spoil our error reporting */
opterr = false;
cs.eb.bitmask = EBT_NOPROTO;
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");
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);
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 = command_jump(&cs, 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 ethertypeent *ent;
if (!strcasecmp(optarg, "LENGTH")) {
cs.eb.bitmask |= EBT_802_3;
break;
}
ent = getethertypebyname(optarg);
if (!ent)
xtables_error(PARAMETER_PROBLEM,
"Problem with the specified Ethernet protocol '%s', perhaps "_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:
/* 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;
}
/* Is it a match_option? */
for (m = xtables_matches; m; m = m->next) {
if (m->parse(c - m->option_offset, argv, ebt_check_inverse2(optarg, argc, argv), &m->mflags, NULL, &m->m)) {
ebt_add_match(m, &cs);
goto check_extension;
}
}
/* 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;
}
}
check_extension:
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 = {
.family = NFPROTO_BRIDGE,
};
ebtables_globals.program_name = argv[0];
ret = xtables_init_all(&ebtables_globals, NFPROTO_BRIDGE);
if (ret < 0) {
fprintf(stderr, "%s/%s Failed to initialize xtables\n",
ebtables_globals.program_name,
ebtables_globals.program_version);
exit(EXIT_FAILURE);
}
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);
}
......@@ -46,9 +46,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 +165,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 +183,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)
......@@ -208,6 +205,9 @@ delete_entry(struct nft_handle *h,
static int 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 +247,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,7 +380,7 @@ static struct option *merge_options(struct option *oldopts,
/*
* More glue code.
*/
static struct xtables_target *command_jump(struct ebtables_command_state *cs,
static struct xtables_target *command_jump(struct iptables_command_state *cs,
const char *jumpto)
{
struct xtables_target *target;
......@@ -397,7 +397,8 @@ static struct xtables_target *command_jump(struct ebtables_command_state *cs,
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));
snprintf(target->t->u.user.name,
sizeof(target->t->u.user.name), "%s", jumpto);
target->t->u.user.name[sizeof(target->t->u.user.name)-1] = '\0';
target->t->u.user.revision = target->revision;
......@@ -524,13 +525,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 +544,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 +556,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 +572,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 +592,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 +610,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,20 +638,40 @@ 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");
}
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_match *newm;
......@@ -696,8 +706,8 @@ static void ebt_add_match(struct xtables_match *m,
cs->match_list->next = 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;
......@@ -722,7 +732,6 @@ static void ebt_add_watcher(struct xtables_target *watcher,
cs->match_list->next = 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)
{
char *buffer;
......@@ -735,11 +744,10 @@ int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table)
unsigned int flags = 0;
struct xtables_target *t, *w;
struct xtables_match *m;
struct ebtables_command_state cs;
struct iptables_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;
......@@ -773,6 +781,7 @@ int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table)
/* prevent getopt to spoil our error reporting */
opterr = false;
cs.eb.bitmask = EBT_NOPROTO;
/* Getopt saves the day */
while ((c = getopt_long(argc, argv,
......@@ -801,17 +810,21 @@ 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);
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') {
/* 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);
break;
}
......@@ -842,7 +855,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 +903,19 @@ 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 +977,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 +987,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 +997,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,13 +1007,9 @@ 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);
......@@ -1046,20 +1019,20 @@ big_iface_length:
} 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,9 +1058,9 @@ 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,
......@@ -1096,27 +1069,22 @@ big_iface_length:
struct ethertypeent *ent;
if (!strcasecmp(optarg, "LENGTH")) {
cs.fw.bitmask |= EBT_802_3;
cs.eb.bitmask |= EBT_802_3;
break;
}
ent = 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;
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 +1092,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 +1102,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 +1112,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 +1119,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");
......@@ -1187,13 +1139,7 @@ big_iface_length:
/*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");
}
/*
replace->command = c;
if (OPT_COMMANDS)
ebt_print_error2("Multiple commands are not allowed");
......@@ -1210,20 +1156,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);
......@@ -1246,7 +1188,7 @@ big_iface_length:
/* 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)) {
if (m->parse(c - m->option_offset, argv, ebt_check_inverse2(optarg, argc, argv), &m->mflags, NULL, &m->m)) {
ebt_add_match(m, &cs);
goto check_extension;
}
......@@ -1294,8 +1236,7 @@ check_extension:
if (command == 'h' && !(flags & OPT_ZERO)) {
print_help(cs.target, cs.matches, *table);
if (exec_style == EXEC_STYLE_PRG)
exit(0);
exit(0);
}
/* Do the final checks */
......@@ -1316,7 +1257,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) {
......@@ -1338,7 +1279,7 @@ check_extension:
/*flags&OPT_EXPANDED*/0,
flags&LIST_N,
flags&LIST_C);
if (!(flags & OPT_ZERO) && exec_style == EXEC_STYLE_PRG)
if (!(flags & OPT_ZERO))
exit(0);
}
if (flags & OPT_ZERO) {
......@@ -1360,19 +1301,6 @@ check_extension:
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 hown by line six.
The last line shows another packet arriving \-\- the packet id is different.
When using the TRACE target, it is usally 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)
{
struct arptables_command_state cs_arp = {};
struct iptables_command_state cs = {};
uint32_t type = nlh->nlmsg_type & 0xFF;
const struct cb_arg *arg = data;
struct nftnl_rule *r;
void *fw = NULL;
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');
nft_rule_to_iptables_command_state(r, &cs);
fw = &cs;
break;
case NFPROTO_ARP:
printf("-0 ");
nft_rule_to_arptables_command_state(r, &cs_arp);
fw = &cs_arp;
break;
default:
goto err_free;
}
printf("-t %s ", nftnl_rule_get_str(r, NFTNL_RULE_TABLE));
nft_rule_print_save(fw, 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'},
{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 taht 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
memset(&cb_arg, 0, sizeof(cb_arg));
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.");
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,13 @@ 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_eb_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,31 @@ 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},
{"ebtables-translate", xtables_eb_xlate_main},
{"ebtables", xtables_eb_main},
{"xtables-monitor", xtables_monitor_main},
{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-NFT 8 "June 2018"
.SH NAME
xtables-nft \(em iptables using nftables kernel api
.SH DESCRIPTION
\fBxtables-nft\fP are versions of iptables that use the nftables API.
This is a set of tools to help the system administrator migrate the
ruleset from \fBiptables(8)\fP, \fBip6tables(8)\fP, \fBarptables(8)\fP, and
\fBebtables(8)\fP to \fBnftables(8)\fP.
The \fBxtables-nft\fP set is composed of several commands:
.IP \[bu] 2
iptables\-nft
.IP \[bu]
iptables\-nft\-save
.IP \[bu]
iptables\-nft\-restore
.IP \[bu]
ip6tables\-nft
.IP \[bu]
ip6tables\-nft\-save
.IP \[bu]
ip6tables\-nft\-restore
.IP \[bu]
arptables\-nft
.IP \[bu]
ebtables\-nft
These tools use the libxtables framework extensions and hook to the nf_tables
kernel subsystem using the \fBnft_compat\fP module.
.SH USAGE
The xtables-nft tools allow you to manage the nf_tables backend using the
native syntax of \fBiptables(8)\fP, \fBip6tables(8)\fP, \fBarptables(8)\fP, and
\fBebtables(8)\fP.
You should use the xtables-nft tools exactly the same way as you would use the
corresponding original tools.
Adding a rule will result in that rule being added to the nf_tables kernel
subsystem instead.
Listing the ruleset will use the nf_tables backend as well.
When these tools were designed, the main idea was to replace each legacy binary
with a symlink to the xtables-nft program, for example:
.nf
/sbin/iptables -> /usr/sbin/iptables\-nft\-multi
/sbin/ip6tables -> /usr/sbin/ip6tables\-nft\-multi
/sbin/arptables -> /usr/sbin/arptables\-nft\-multi
/sbin/ebtables -> /usr/sbin/ebtables\-nft\-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 (nf_tables)
.fi
.SH DIFFERENCES TO LEGACY IPTABLES
Because the xtables-nft tools use the nf_tables kernel API, rule additions
and deletions are always atomic. Unlike iptables-legacy, iptables-nft \-A ..
will NOT need to retrieve the current ruleset from the kernel, change it, and
re-load the altered ruleset. Instead, iptables-nft will tell the kernel to add
one rule. For this reason, the iptables-legacy \-\-wait option is a no-op in
iptables-nft.
Use of the xtables-nft tools allow monitoring ruleset changes using the
.B xtables\-monitor(8)
command.
When using \-j TRACE to debug packet traversal to the ruleset, note that you will need to use
.B xtables\-monitor(8)
in \-\-trace mode to obtain monitoring trace events.
.SH EXAMPLES
One basic example is creating the skeleton ruleset in nf_tables from the
xtables-nft tools, in a fresh machine:
.nf
root@machine:~# iptables\-nft \-L
[...]
root@machine:~# ip6tables\-nft \-L
[...]
root@machine:~# arptables\-nft \-L
[...]
root@machine:~# ebtables\-nft \-L
[...]
root@machine:~# nft list ruleset
table ip filter {
chain INPUT {
type filter hook input priority 0; policy accept;
}
chain FORWARD {
type filter hook forward priority 0; policy accept;
}
chain OUTPUT {
type filter hook output priority 0; policy accept;
}
}
table ip6 filter {
chain INPUT {
type filter hook input priority 0; policy accept;
}
chain FORWARD {
type filter hook forward priority 0; policy accept;
}
chain OUTPUT {
type filter hook output priority 0; policy accept;
}
}
table bridge filter {
chain INPUT {
type filter hook input priority \-200; policy accept;
}
chain FORWARD {
type filter hook forward priority \-200; policy accept;
}
chain OUTPUT {
type filter hook output priority \-200; policy accept;
}
}
table arp filter {
chain INPUT {
type filter hook input priority 0; policy accept;
}
chain FORWARD {
type filter hook forward priority 0; policy accept;
}
chain OUTPUT {
type filter hook output priority 0; policy accept;
}
}
.fi
(please note that in fresh machines, listing the ruleset for the first time
results in all tables an chain being created).
To migrate your complete filter ruleset, in the case of \fBiptables(8)\fP,
you would use:
.nf
root@machine:~# iptables\-legacy\-save > myruleset # reads from x_tables
root@machine:~# iptables\-nft\-restore myruleset # writes to nf_tables
.fi
or
.nf
root@machine:~# iptables\-legacy\-save | iptables-translate-restore | less
.fi
to see how rules would look like in the nft
\fBnft(8)\fP
syntax.
.SH LIMITATIONS
You should use \fBLinux kernel >= 4.17\fP.
The CLUSTERIP target is not supported.
To get up-to-date information about this, please head to
\fBhttp://wiki.nftables.org/\fP.
.SH SEE ALSO
\fBnft(8)\fP, \fBxtables\-translate(8)\fP, \fBxtables\-monitor(8)\fP
.SH AUTHORS
The nftables framework is written by the Netfilter project
(https://www.netfilter.org).
This manual page was written by Arturo Borrero Gonzalez
<arturo@debian.org> for the Debian project, but may be used by others.
This documentation is free/libre under the terms of the GPLv2+.
......@@ -30,6 +30,7 @@ static int counters, verbose, noflush;
static const struct option options[] = {
{.name = "counters", .has_arg = false, .val = 'c'},
{.name = "verbose", .has_arg = false, .val = 'v'},
{.name = "version", .has_arg = 0, .val = 'V'},
{.name = "test", .has_arg = false, .val = 't'},
{.name = "help", .has_arg = false, .val = 'h'},
{.name = "noflush", .has_arg = false, .val = 'n'},
......@@ -37,16 +38,20 @@ static const struct option options[] = {
{.name = "table", .has_arg = true, .val = 'T'},
{.name = "ipv4", .has_arg = false, .val = '4'},
{.name = "ipv6", .has_arg = false, .val = '6'},
{.name = "wait", .has_arg = 2, .val = 'w'},
{.name = "wait-interval", .has_arg = 2, .val = 'W'},
{NULL},
};
#define prog_name xtables_globals.program_name
#define prog_vers xtables_globals.program_version
static void print_usage(const char *name, const char *version)
{
fprintf(stderr, "Usage: %s [-c] [-v] [-t] [-h] [-n] [-T table] [-M command] [-4] [-6]\n"
fprintf(stderr, "Usage: %s [-c] [-v] [-V] [-t] [-h] [-n] [-T table] [-M command] [-4] [-6]\n"
" [ --counters ]\n"
" [ --verbose ]\n"
" [ --version]\n"
" [ --test ]\n"
" [ --help ]\n"
" [ --noflush ]\n"
......@@ -190,8 +195,9 @@ struct nft_xt_restore_cb restore_cb = {
.chain_list = get_chain_list,
.commit = nft_commit,
.abort = nft_abort,
.chains_purge = nft_table_purge_chains,
.rule_flush = nft_rule_flush,
.table_new = nft_table_new,
.table_flush = nft_table_flush,
.chain_user_flush = nft_chain_user_flush,
.chain_del = chain_delete,
.do_command = do_commandx,
.chain_set = nft_chain_set,
......@@ -223,6 +229,8 @@ void xtables_restore_parse(struct nft_handle *h,
int ret = 0;
line++;
h->error.lineno = line;
if (buffer[0] == '\n')
continue;
else if (buffer[0] == '#') {
......@@ -245,10 +253,6 @@ void xtables_restore_parse(struct nft_handle *h,
}
in_table = 0;
/* Purge out unused chains in this table */
if (!p->testing && cb->chains_purge)
cb->chains_purge(h, curtable, chain_list);
} else if ((buffer[0] == '*') && (!in_table)) {
/* New table */
char *table;
......@@ -270,8 +274,8 @@ void xtables_restore_parse(struct nft_handle *h,
if (noflush == 0) {
DEBUGP("Cleaning all chains of table '%s'\n",
table);
if (cb->rule_flush)
cb->rule_flush(h, NULL, table);
if (cb->table_flush)
cb->table_flush(h, table);
}
ret = 1;
......@@ -294,8 +298,19 @@ void xtables_restore_parse(struct nft_handle *h,
exit(1);
}
if (cb->chain_del)
cb->chain_del(chain_list, curtable, chain);
if (noflush == 0) {
if (cb->chain_del)
cb->chain_del(chain_list, curtable,
chain);
} else {
/* Apparently -n still flushes existing user
* defined chains that are redefined. Otherwise,
* leave them as is.
*/
if (cb->chain_user_flush)
cb->chain_user_flush(h, chain_list,
curtable, chain);
}
if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
xtables_error(PARAMETER_PROBLEM,
......@@ -435,14 +450,12 @@ void xtables_restore_parse(struct nft_handle *h,
xt_params->program_name, line + 1);
exit(1);
}
if (chain_list)
nftnl_chain_list_free(chain_list);
}
static int
xtables_restore_main(int family, const char *progname, int argc, char *argv[])
{
struct builtin_table *tables;
struct nft_handle h = {
.family = family,
.restore = true,
......@@ -460,20 +473,8 @@ xtables_restore_main(int family, const char *progname, int argc, char *argv[])
xtables_globals.program_version);
exit(1);
}
#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
init_extensions();
init_extensions4();
#endif
if (nft_init(&h, xtables_ipv4) < 0) {
fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
xtables_globals.program_name,
xtables_globals.program_version,
strerror(errno));
exit(EXIT_FAILURE);
}
while ((c = getopt_long(argc, argv, "bcvthnM:T:46", options, NULL)) != -1) {
while ((c = getopt_long(argc, argv, "bcvVthnM:T:46wW", options, NULL)) != -1) {
switch (c) {
case 'b':
fprintf(stderr, "-b/--binary option is not implemented\n");
......@@ -484,6 +485,9 @@ xtables_restore_main(int family, const char *progname, int argc, char *argv[])
case 'v':
verbose = 1;
break;
case 'V':
printf("%s v%s (nf_tables)\n", prog_name, prog_vers);
exit(0);
case 't':
p.testing = 1;
break;
......@@ -507,6 +511,9 @@ xtables_restore_main(int family, const char *progname, int argc, char *argv[])
h.family = AF_INET6;
xtables_set_nfproto(AF_INET6);
break;
case 'w': /* fallthrough. Ignored by xt-restore */
case 'W':
break;
default:
fprintf(stderr,
"Try `xtables-restore -h' for more information.\n");
......@@ -528,6 +535,34 @@ xtables_restore_main(int family, const char *progname, int argc, char *argv[])
p.in = stdin;
}
switch (family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6: /* fallthough, same table */
tables = xtables_ipv4;
#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
init_extensions();
init_extensions4();
#endif
break;
case NFPROTO_ARP:
tables = xtables_arp;
break;
case NFPROTO_BRIDGE:
tables = xtables_bridge;
break;
default:
fprintf(stderr, "Unknown family %d\n", family);
return 1;
}
if (nft_init(&h, tables) < 0) {
fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
xtables_globals.program_name,
xtables_globals.program_version,
strerror(errno));
exit(EXIT_FAILURE);
}
xtables_restore_parse(&h, &p, &restore_cb, argc, argv);
nft_fini(&h);
......
......@@ -26,10 +26,14 @@
#include <dlfcn.h>
#endif
#define prog_name xtables_globals.program_name
#define prog_vers xtables_globals.program_version
static bool show_counters = false;
static const struct option options[] = {
{.name = "counters", .has_arg = false, .val = 'c'},
{.name = "version", .has_arg = false, .val = 'V'},
{.name = "dump", .has_arg = false, .val = 'd'},
{.name = "table", .has_arg = true, .val = 't'},
{.name = "modprobe", .has_arg = true, .val = 'M'},
......@@ -40,15 +44,18 @@ static const struct option options[] = {
};
static int
do_output(struct nft_handle *h, const char *tablename, bool counters)
__do_output(struct nft_handle *h, const char *tablename, bool counters)
{
struct nftnl_chain_list *chain_list;
if (!tablename)
return nft_for_each_table(h, do_output, counters);
if (!nft_table_find(h, tablename)) {
printf("Table `%s' does not exist\n", tablename);
return 1;
}
if (!nft_is_table_compatible(h, tablename)) {
printf("# Table `%s' is incompatible, use 'nft' tool.\n", tablename);
return 0;
}
......@@ -68,8 +75,23 @@ do_output(struct nft_handle *h, const char *tablename, bool counters)
now = time(NULL);
printf("COMMIT\n");
printf("# Completed on %s", ctime(&now));
return 0;
}
static int
do_output(struct nft_handle *h, const char *tablename, bool counters)
{
int ret;
if (!tablename) {
ret = nft_for_each_table(h, __do_output, counters);
nft_check_xt_legacy(h->family, true);
return !!ret;
}
return 1;
ret = __do_output(h, tablename, counters);
nft_check_xt_legacy(h->family, true);
return ret;
}
/* Format:
......@@ -79,6 +101,7 @@ do_output(struct nft_handle *h, const char *tablename, bool counters)
static int
xtables_save_main(int family, const char *progname, int argc, char *argv[])
{
struct builtin_table *tables;
const char *tablename = NULL;
bool dump = false;
struct nft_handle h = {
......@@ -95,19 +118,8 @@ xtables_save_main(int family, const char *progname, int argc, char *argv[])
xtables_globals.program_version);
exit(1);
}
#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
init_extensions();
init_extensions4();
#endif
if (nft_init(&h, xtables_ipv4) < 0) {
fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
xtables_globals.program_name,
xtables_globals.program_version,
strerror(errno));
exit(EXIT_FAILURE);
}
while ((c = getopt_long(argc, argv, "bcdt:M:f:46", options, NULL)) != -1) {
while ((c = getopt_long(argc, argv, "bcdt:M:f:46V", options, NULL)) != -1) {
switch (c) {
case 'b':
fprintf(stderr, "-b/--binary option is not implemented\n");
......@@ -148,6 +160,9 @@ xtables_save_main(int family, const char *progname, int argc, char *argv[])
h.family = AF_INET6;
xtables_set_nfproto(AF_INET6);
break;
case 'V':
printf("%s v%s (nf_tables)\n", prog_name, prog_vers);
exit(0);
default:
fprintf(stderr,
"Look at manual page `xtables-save.8' for more information.\n");
......@@ -160,8 +175,31 @@ xtables_save_main(int family, const char *progname, int argc, char *argv[])
exit(1);
}
if (nft_is_ruleset_compatible(&h) == 1) {
printf("ERROR: You're using nft features that cannot be mapped to iptables, please keep using nft.\n");
switch (family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6: /* fallthough, same table */
#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
init_extensions();
init_extensions4();
#endif
tables = xtables_ipv4;
break;
case NFPROTO_ARP:
tables = xtables_arp;
break;
case NFPROTO_BRIDGE:
tables = xtables_bridge;
break;
default:
fprintf(stderr, "Unknown family %d\n", family);
return 1;
}
if (nft_init(&h, tables) < 0) {
fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
xtables_globals.program_name,
xtables_globals.program_version,
strerror(errno));
exit(EXIT_FAILURE);
}
......@@ -170,7 +208,7 @@ xtables_save_main(int family, const char *progname, int argc, char *argv[])
exit(0);
}
return !do_output(&h, tablename, show_counters);
return do_output(&h, tablename, show_counters);
}
int xtables_ip4_save_main(int argc, char *argv[])
......
......@@ -66,7 +66,6 @@ xtables_main(int family, const char *progname, int argc, char *argv[])
xtables_globals.program_name,
xtables_globals.program_version,
strerror(errno));
nft_fini(&h);
exit(EXIT_FAILURE);
}
......
.\"
.\" (C) Copyright 2018, 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-TRANSLATE 8 "Mar 16, 2018"
.SH NAME
xtables-translate \- translation tools to migrate from iptables to nftables
.SH DESCRIPTION
There is a set of tools to help the system administrator translate a given
ruleset from \fBiptables(8)\fP and \fBip6tables(8)\fP to \fBnftables(8)\fP.
The available commands are:
.IP \[bu] 2
iptables-translate
.IP \[bu]
iptables-restore-translate
.IP \[bu] 2
ip6tables-translate
.IP \[bu]
ip6tables-restore-translate
.SH USAGE
They take as input the original \fBiptables(8)\fP/\fBip6tables(8)\fP syntax and
output the native \fBnftables(8)\fP syntax.
The \fBiptables-restore-translate\fP tool reads a ruleset in the syntax
produced by \fBiptables-save(8)\fP. Likewise, the
\fBip6tables-restore-translate\fP tool reads one produced by
\fBip6tables-save(8)\fP. No ruleset modifications occur, these tools are
text converters only.
The \fBiptables-translate\fP reads a command line as if it was entered to
\fBiptables(8)\fP, and \fBip6tables-translate\fP reads a command like as if it
was entered to \fBip6tables(8)\fP.
.SH EXAMPLES
Basic operation examples.
Single command translation:
.nf
root@machine:~# iptables-translate -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
nft add rule ip filter INPUT tcp dport 22 ct state new counter accept
root@machine:~# ip6tables-translate -A FORWARD -i eth0 -o eth3 -p udp -m multiport --dports 111,222 -j ACCEPT
nft add rule ip6 filter FORWARD iifname eth0 oifname eth3 meta l4proto udp udp dport { 111,222} counter accept
.fi
Whole ruleset translation:
.nf
root@machine:~# iptables-save > save.txt
root@machine:~# cat save.txt
# Generated by iptables-save v1.6.0 on Sat Dec 24 14:26:40 2016
*filter
:INPUT ACCEPT [5166:1752111]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [5058:628693]
-A FORWARD -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
COMMIT
# Completed on Sat Dec 24 14:26:40 2016
root@machine:~# iptables-restore-translate -f save.txt
# Translated by iptables-restore-translate v1.6.0 on Sat Dec 24 14:26:59 2016
add table ip filter
add chain ip filter INPUT { type filter hook input priority 0; }
add chain ip filter FORWARD { type filter hook forward priority 0; }
add chain ip filter OUTPUT { type filter hook output priority 0; }
add rule ip filter FORWARD tcp dport 22 ct state new counter accept
root@machine:~# iptables-restore-translate -f save.txt > ruleset.nft
root@machine:~# nft -f ruleset.nft
root@machine:~# nft list ruleset
table ip filter {
chain INPUT {
type filter hook input priority 0; policy accept;
}
chain FORWARD {
type filter hook forward priority 0; policy accept;
tcp dport ssh ct state new counter packets 0 bytes 0 accept
}
chain OUTPUT {
type filter hook output priority 0; policy accept;
}
}
.fi
.SH LIMITATIONS
Some (few) extensions may be not supported (or fully-supported) for whatever
reason (for example, they were considered obsolete, or we didn't have the time
to work on them).
There are no translations available for \fBebtables(8)\fP and
\fBarptables(8)\fP.
To get up-to-date information about this, please head to
\fBhttps://wiki.nftables.org/\fP.
.SH SEE ALSO
\fBnft(8)\fP, \fBxtables-compat(8)\fP
.SH AUTHORS
The nftables framework is written by the Netfilter project
(https://www.netfilter.org).
This manual page was written by Arturo Borrero Gonzalez
<arturo@netfilter.org>.
This documentation is free/libre under the terms of the GPLv2+.
......@@ -7,11 +7,6 @@
* (at your option) any later version.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <iptables.h>
#include <time.h>
#include "xtables-multi.h"
#include "nft.h"
......@@ -48,7 +43,7 @@ void xlate_ifname(struct xt_xlate *xl, const char *nftmeta, const char *ifname,
if (iface[ifaclen - 1] == '+')
iface[ifaclen - 1] = '*';
xt_xlate_add(xl, "%s %s%s ", nftmeta, invert ? "!= " : "", iface);
xt_xlate_add(xl, "%s %s\"%s\" ", nftmeta, invert ? "!= " : "", iface);
}
int xlate_action(const struct iptables_command_state *cs, bool goto_set,
......@@ -329,6 +324,7 @@ static void print_usage(const char *name, const char *version)
static const struct option options[] = {
{ .name = "help", .has_arg = false, .val = 'h' },
{ .name = "file", .has_arg = true, .val = 'f' },
{ .name = "version", .has_arg = false, .val = 'V' },
{ NULL },
};
......@@ -349,11 +345,36 @@ static void xlate_table_new(struct nft_handle *h, const char *table)
printf("add table %s %s\n", family2str[h->family], table);
}
static int get_hook_prio(const char *table, const char *chain)
{
int prio = 0;
if (strcmp("nat", table) == 0) {
if (strcmp(chain, "PREROUTING") == 0)
prio = NF_IP_PRI_NAT_DST;
if (strcmp(chain, "INPUT") == 0)
prio = NF_IP_PRI_NAT_SRC;
if (strcmp(chain, "OUTPUT") == 0)
prio = NF_IP_PRI_NAT_DST;
if (strcmp(chain, "POSTROUTING") == 0)
prio = NF_IP_PRI_NAT_SRC;
} else if (strcmp("mangle", table) == 0) {
prio = NF_IP_PRI_MANGLE;
} else if (strcmp("raw", table) == 0) {
prio = NF_IP_PRI_RAW;
} else if (strcmp(chain, "security") == 0) {
prio = NF_IP_PRI_SECURITY;
}
return prio;
}
static int xlate_chain_set(struct nft_handle *h, const char *table,
const char *chain, const char *policy,
const struct xt_counters *counters)
{
const char *type = "filter";
int prio;
if (strcmp(table, "nat") == 0)
type = "nat";
......@@ -362,16 +383,17 @@ static int xlate_chain_set(struct nft_handle *h, const char *table,
printf("add chain %s %s %s { type %s ",
family2str[h->family], table, chain, type);
prio = get_hook_prio(table, chain);
if (strcmp(chain, "PREROUTING") == 0)
printf("hook prerouting priority 0; ");
printf("hook prerouting priority %d; ", prio);
else if (strcmp(chain, "INPUT") == 0)
printf("hook input priority 0; ");
printf("hook input priority %d; ", prio);
else if (strcmp(chain, "FORWARD") == 0)
printf("hook forward priority 0; ");
printf("hook forward priority %d; ", prio);
else if (strcmp(chain, "OUTPUT") == 0)
printf("hook output priority 0; ");
printf("hook output priority %d; ", prio);
else if (strcmp(chain, "POSTROUTING") == 0)
printf("hook postrouting priority 0; ");
printf("hook postrouting priority %d; ", prio);
if (strcmp(policy, "ACCEPT") == 0)
printf("policy accept; ");
......@@ -399,38 +421,66 @@ static struct nft_xt_restore_cb cb_xlate = {
.abort = commit,
};
static int xtables_xlate_main(int family, const char *progname, int argc,
char *argv[])
static int xtables_xlate_main_common(struct nft_handle *h,
int family,
const char *progname)
{
struct builtin_table *tables;
int ret;
char *table = "filter";
struct nft_handle h = {
.family = family,
};
xtables_globals.program_name = progname;
xtables_globals.compat_rev = dummy_compat_rev;
ret = xtables_init_all(&xtables_globals, family);
if (ret < 0) {
fprintf(stderr, "%s/%s Failed to initialize xtables\n",
xtables_globals.program_name,
xtables_globals.program_version);
exit(1);
xtables_globals.program_name,
xtables_globals.program_version);
return 1;
}
switch (family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6: /* fallthrough: same table */
#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
init_extensions();
init_extensions4();
#endif
tables = xtables_ipv4;
break;
case NFPROTO_ARP:
tables = xtables_arp;
break;
case NFPROTO_BRIDGE:
tables = xtables_bridge;
break;
default:
fprintf(stderr, "Unknown family %d\n", family);
return 1;
}
if (nft_init(&h, xtables_ipv4) < 0) {
if (nft_init(h, tables) < 0) {
fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
xtables_globals.program_name,
xtables_globals.program_version,
strerror(errno));
nft_fini(&h);
exit(EXIT_FAILURE);
return 1;
}
return 0;
}
static int xtables_xlate_main(int family, const char *progname, int argc,
char *argv[])
{
int ret;
char *table = "filter";
struct nft_handle h = {
.family = family,
};
ret = xtables_xlate_main_common(&h, family, progname);
if (ret < 0)
exit(EXIT_FAILURE);
ret = do_command_xlate(&h, argc, argv, &table, false);
if (!ret)
fprintf(stderr, "Translation not implemented\n");
......@@ -451,31 +501,12 @@ static int xtables_restore_xlate_main(int family, const char *progname,
time_t now = time(NULL);
int c;
xtables_globals.program_name = progname;
xtables_globals.compat_rev = dummy_compat_rev;
ret = xtables_init_all(&xtables_globals, family);
if (ret < 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
if (nft_init(&h, xtables_ipv4) < 0) {
fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
xtables_globals.program_name,
xtables_globals.program_version,
strerror(errno));
nft_fini(&h);
ret = xtables_xlate_main_common(&h, family, progname);
if (ret < 0)
exit(EXIT_FAILURE);
}
opterr = 0;
while ((c = getopt_long(argc, argv, "hf:", options, NULL)) != -1) {
while ((c = getopt_long(argc, argv, "hf:V", options, NULL)) != -1) {
switch (c) {
case 'h':
print_usage(argv[0], IPTABLES_VERSION);
......@@ -483,6 +514,9 @@ static int xtables_restore_xlate_main(int family, const char *progname,
case 'f':
file = optarg;
break;
case 'V':
printf("%s v%s\n", argv[0], IPTABLES_VERSION);
exit(0);
}
}
......
......@@ -260,7 +260,7 @@ xtables_exit_error(enum xtables_exittype status, const char *msg, ...)
va_list args;
va_start(args, msg);
fprintf(stderr, "%s v%s: ", prog_name, prog_vers);
fprintf(stderr, "%s v%s (nf_tables): ", prog_name, prog_vers);
vfprintf(stderr, msg, args);
va_end(args);
fprintf(stderr, "\n");
......@@ -991,7 +991,7 @@ void do_parse(struct nft_handle *h, int argc, char *argv[],
if (cs->invert)
printf("Not %s ;-)\n", prog_vers);
else
printf("%s v%s\n",
printf("%s v%s (nf_tables)\n",
prog_name, prog_vers);
exit(0);
......@@ -1225,11 +1225,6 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table,
case CMD_LIST:
case CMD_LIST|CMD_ZERO:
case CMD_LIST|CMD_ZERO_NUM:
if (nft_is_ruleset_compatible(h) == 1) {
printf("ERROR: You're using nft features that cannot be mapped to iptables, please keep using nft.\n");
exit(EXIT_FAILURE);
}
ret = list_entries(h, p.chain, p.table, p.rulenum,
cs.options & OPT_VERBOSE,
cs.options & OPT_NUMERIC,
......@@ -1243,6 +1238,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table,
ret = nft_rule_zero_counters(h, p.chain, p.table,
p.rulenum - 1);
}
nft_check_xt_legacy(h->family, false);
break;
case CMD_LIST_RULES:
case CMD_LIST_RULES|CMD_ZERO:
......@@ -1257,6 +1253,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table,
ret = nft_rule_zero_counters(h, p.chain, p.table,
p.rulenum - 1);
}
nft_check_xt_legacy(h->family, false);
break;
case CMD_NEW_CHAIN:
ret = nft_chain_user_add(h, p.chain, p.table);
......
......@@ -198,8 +198,8 @@ struct xtables_match *xtables_matches;
struct xtables_target *xtables_targets;
/* Fully register a match/target which was previously partially registered. */
static void xtables_fully_register_pending_match(struct xtables_match *me);
static void xtables_fully_register_pending_target(struct xtables_target *me);
static bool xtables_fully_register_pending_match(struct xtables_match *me);
static bool xtables_fully_register_pending_target(struct xtables_target *me);
void xtables_init(void)
{
......@@ -638,11 +638,11 @@ xtables_find_match(const char *name, enum xtables_tryload tryload,
if (extension_cmp(name, (*dptr)->name, (*dptr)->family)) {
ptr = *dptr;
*dptr = (*dptr)->next;
ptr->next = NULL;
xtables_fully_register_pending_match(ptr);
} else {
dptr = &((*dptr)->next);
if (xtables_fully_register_pending_match(ptr))
continue;
*dptr = ptr;
}
dptr = &((*dptr)->next);
}
for (ptr = xtables_matches; ptr; ptr = ptr->next) {
......@@ -709,6 +709,27 @@ xtables_find_match(const char *name, enum xtables_tryload tryload,
return ptr;
}
struct xtables_match *
xtables_find_match_revision(const char *name, enum xtables_tryload tryload,
struct xtables_match *match, int revision)
{
if (!match) {
match = xtables_find_match(name, tryload, NULL);
if (!match)
return NULL;
}
while (1) {
if (match->revision == revision)
return match;
match = match->next;
if (!match)
return NULL;
if (!extension_cmp(name, match->name, match->family))
return NULL;
}
}
struct xtables_target *
xtables_find_target(const char *name, enum xtables_tryload tryload)
{
......@@ -728,11 +749,11 @@ xtables_find_target(const char *name, enum xtables_tryload tryload)
if (extension_cmp(name, (*dptr)->name, (*dptr)->family)) {
ptr = *dptr;
*dptr = (*dptr)->next;
ptr->next = NULL;
xtables_fully_register_pending_target(ptr);
} else {
dptr = &((*dptr)->next);
if (xtables_fully_register_pending_target(ptr))
continue;
*dptr = ptr;
}
dptr = &((*dptr)->next);
}
for (ptr = xtables_targets; ptr; ptr = ptr->next) {
......@@ -769,6 +790,27 @@ xtables_find_target(const char *name, enum xtables_tryload tryload)
return ptr;
}
struct xtables_target *
xtables_find_target_revision(const char *name, enum xtables_tryload tryload,
struct xtables_target *target, int revision)
{
if (!target) {
target = xtables_find_target(name, tryload);
if (!target)
return NULL;
}
while (1) {
if (target->revision == revision)
return target;
target = target->next;
if (!target)
return NULL;
if (!extension_cmp(name, target->name, target->family))
return NULL;
}
}
int xtables_compatible_revision(const char *name, uint8_t revision, int opt)
{
struct xt_get_revision rev;
......@@ -846,11 +888,25 @@ static void xtables_check_options(const char *name, const struct option *opt)
void xtables_register_match(struct xtables_match *me)
{
if (me->next) {
fprintf(stderr, "%s: match \"%s\" already registered\n",
xt_params->program_name, me->name);
exit(1);
}
if (me->version == NULL) {
fprintf(stderr, "%s: match %s<%u> is missing a version\n",
xt_params->program_name, me->name, me->revision);
exit(1);
}
if (me->size != XT_ALIGN(me->size)) {
fprintf(stderr, "%s: match \"%s\" has invalid size %u.\n",
xt_params->program_name, me->name,
(unsigned int)me->size);
exit(1);
}
if (strcmp(me->version, XTABLES_VERSION) != 0) {
fprintf(stderr, "%s: match \"%s\" has version \"%s\", "
"but \"%s\" is required.\n",
......@@ -877,9 +933,6 @@ void xtables_register_match(struct xtables_match *me)
if (me->extra_opts != NULL)
xtables_check_options(me->name, me->extra_opts);
/* ignore not interested match */
if (me->family != afinfo->family && me->family != AF_UNSPEC)
return;
/* place on linked list of matches pending full registration */
me->next = xtables_pending_matches;
......@@ -947,14 +1000,19 @@ static int xtables_target_prefer(const struct xtables_target *a,
b->revision, b->family);
}
static void xtables_fully_register_pending_match(struct xtables_match *me)
static bool xtables_fully_register_pending_match(struct xtables_match *me)
{
struct xtables_match **i, *old;
struct xtables_match **i, *old, *pos = NULL;
const char *rn;
int compare;
/* See if new match can be used. */
rn = (me->real_name != NULL) ? me->real_name : me->name;
if (!compatible_match_revision(rn, me->revision))
return false;
old = xtables_find_match(me->name, XTF_DURING_LOAD, NULL);
if (old) {
while (old) {
compare = xtables_match_prefer(old, me);
if (compare == 0) {
fprintf(stderr,
......@@ -965,34 +1023,47 @@ static void xtables_fully_register_pending_match(struct xtables_match *me)
/* Now we have two (or more) options, check compatibility. */
rn = (old->real_name != NULL) ? old->real_name : old->name;
if (compare > 0 &&
compatible_match_revision(rn, old->revision))
return;
/* See if new match can be used. */
rn = (me->real_name != NULL) ? me->real_name : me->name;
if (!compatible_match_revision(rn, me->revision))
return;
if (compare > 0) {
/* Kernel tells old isn't compatible anymore??? */
if (!compatible_match_revision(rn, old->revision)) {
/* Delete old one. */
for (i = &xtables_matches; *i != old;)
i = &(*i)->next;
*i = old->next;
}
pos = old;
old = old->next;
if (!old)
break;
if (!extension_cmp(me->name, old->name, old->family))
break;
continue;
}
/* Delete old one. */
for (i = &xtables_matches; *i!=old; i = &(*i)->next);
*i = old->next;
/* Found right old */
pos = old;
break;
}
if (me->size != XT_ALIGN(me->size)) {
fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
xt_params->program_name, me->name,
(unsigned int)me->size);
exit(1);
if (!pos) {
/* Append to list. */
for (i = &xtables_matches; *i; i = &(*i)->next);
} else if (compare < 0) {
/* Prepend it */
for (i = &xtables_matches; *i != pos; i = &(*i)->next);
} else if (compare > 0) {
/* Append it */
i = &pos->next;
pos = pos->next;
}
/* Append to list. */
for (i = &xtables_matches; *i; i = &(*i)->next);
me->next = NULL;
me->next = pos;
*i = me;
me->m = NULL;
me->mflags = 0;
return true;
}
void xtables_register_matches(struct xtables_match *match, unsigned int n)
......@@ -1004,11 +1075,25 @@ void xtables_register_matches(struct xtables_match *match, unsigned int n)
void xtables_register_target(struct xtables_target *me)
{
if (me->next) {
fprintf(stderr, "%s: target \"%s\" already registered\n",
xt_params->program_name, me->name);
exit(1);
}
if (me->version == NULL) {
fprintf(stderr, "%s: target %s<%u> is missing a version\n",
xt_params->program_name, me->name, me->revision);
exit(1);
}
if (me->size != XT_ALIGN(me->size)) {
fprintf(stderr, "%s: target \"%s\" has invalid size %u.\n",
xt_params->program_name, me->name,
(unsigned int)me->size);
exit(1);
}
if (strcmp(me->version, XTABLES_VERSION) != 0) {
fprintf(stderr, "%s: target \"%s\" has version \"%s\", "
"but \"%s\" is required.\n",
......@@ -1044,16 +1129,21 @@ void xtables_register_target(struct xtables_target *me)
xtables_pending_targets = me;
}
static void xtables_fully_register_pending_target(struct xtables_target *me)
static bool xtables_fully_register_pending_target(struct xtables_target *me)
{
struct xtables_target *old;
struct xtables_target **i, *old, *pos = NULL;
const char *rn;
int compare;
old = xtables_find_target(me->name, XTF_DURING_LOAD);
if (old) {
struct xtables_target **i;
if (strcmp(me->name, "standard") != 0) {
/* See if new target can be used. */
rn = (me->real_name != NULL) ? me->real_name : me->name;
if (!compatible_target_revision(rn, me->revision))
return false;
}
old = xtables_find_target(me->name, XTF_DURING_LOAD);
while (old) {
compare = xtables_target_prefer(old, me);
if (compare == 0) {
fprintf(stderr,
......@@ -1064,32 +1154,48 @@ static void xtables_fully_register_pending_target(struct xtables_target *me)
/* Now we have two (or more) options, check compatibility. */
rn = (old->real_name != NULL) ? old->real_name : old->name;
if (compare > 0 &&
compatible_target_revision(rn, old->revision))
return;
/* See if new target can be used. */
rn = (me->real_name != NULL) ? me->real_name : me->name;
if (!compatible_target_revision(rn, me->revision))
return;
if (compare > 0) {
/* Kernel tells old isn't compatible anymore??? */
if (!compatible_target_revision(rn, old->revision)) {
/* Delete old one. */
for (i = &xtables_targets; *i != old;)
i = &(*i)->next;
*i = old->next;
}
pos = old;
old = old->next;
if (!old)
break;
if (!extension_cmp(me->name, old->name, old->family))
break;
continue;
}
/* Delete old one. */
for (i = &xtables_targets; *i!=old; i = &(*i)->next);
*i = old->next;
/* Found right old */
pos = old;
break;
}
if (me->size != XT_ALIGN(me->size)) {
fprintf(stderr, "%s: target `%s' has invalid size %u.\n",
xt_params->program_name, me->name,
(unsigned int)me->size);
exit(1);
if (!pos) {
/* Prepend to list. */
i = &xtables_targets;
pos = xtables_targets;
} else if (compare < 0) {
/* Prepend it */
for (i = &xtables_targets; *i != pos; i = &(*i)->next);
} else if (compare > 0) {
/* Append it */
i = &pos->next;
pos = pos->next;
}
/* Prepend to list. */
me->next = xtables_targets;
xtables_targets = me;
me->next = pos;
*i = me;
me->t = NULL;
me->tflags = 0;
return true;
}
void xtables_register_targets(struct xtables_target *target, unsigned int n)
......@@ -1960,6 +2066,78 @@ void xtables_print_num(uint64_t number, unsigned int format)
printf(FMT("%4lluT ","%lluT "), (unsigned long long)number);
}
void xtables_parse_val_mask(struct xt_option_call *cb,
unsigned int *val, unsigned int *mask,
const struct xtables_lmap *lmap)
{
char *end;
*mask = ~0U;
if (!xtables_strtoui(cb->arg, &end, val, 0, UINT32_MAX)) {
if (lmap)
goto name2val;
else
goto bad_val;
}
if (*end == '\0')
return;
if (*end != '/') {
if (lmap)
goto name2val;
else
goto garbage;
}
if (!xtables_strtoui(end + 1, &end, mask, 0, UINT32_MAX))
goto bad_val;
if (*end == '\0')
return;
garbage:
xt_params->exit_err(PARAMETER_PROBLEM,
"%s: trailing garbage after value "
"for option \"--%s\".\n",
cb->ext_name, cb->entry->name);
bad_val:
xt_params->exit_err(PARAMETER_PROBLEM,
"%s: bad integer value for option \"--%s\", "
"or out of range.\n",
cb->ext_name, cb->entry->name);
name2val:
*val = xtables_lmap_name2id(lmap, cb->arg);
if ((int)*val == -1)
xt_params->exit_err(PARAMETER_PROBLEM,
"%s: could not map name %s to an integer value "
"for option \"--%s\".\n",
cb->ext_name, cb->arg, cb->entry->name);
}
void xtables_print_val_mask(unsigned int val, unsigned int mask,
const struct xtables_lmap *lmap)
{
if (mask != ~0U) {
printf(" 0x%x/0x%x", val, mask);
return;
}
if (lmap) {
const char *name = xtables_lmap_id2name(lmap, val);
if (name) {
printf(" %s", name);
return;
}
}
printf(" 0x%x", val);
}
int kernel_version;
void get_kernel_version(void)
......
......@@ -428,27 +428,7 @@ static void xtopt_parse_tosmask(struct xt_option_call *cb)
*/
static void xtopt_parse_markmask(struct xt_option_call *cb)
{
unsigned int mark = 0, mask = ~0U;
char *end;
if (!xtables_strtoui(cb->arg, &end, &mark, 0, UINT32_MAX))
xt_params->exit_err(PARAMETER_PROBLEM,
"%s: bad mark value for option \"--%s\", "
"or out of range.\n",
cb->ext_name, cb->entry->name);
if (*end == '/' &&
!xtables_strtoui(end + 1, &end, &mask, 0, UINT32_MAX))
xt_params->exit_err(PARAMETER_PROBLEM,
"%s: bad mask value for option \"--%s\", "
"or out of range.\n",
cb->ext_name, cb->entry->name);
if (*end != '\0')
xt_params->exit_err(PARAMETER_PROBLEM,
"%s: trailing garbage after value "
"for option \"--%s\".\n",
cb->ext_name, cb->entry->name);
cb->val.mark = mark;
cb->val.mask = mask;
xtables_parse_mark_mask(cb, &cb->val.mark, &cb->val.mask);
}
static int xtopt_sysloglvl_compare(const void *a, const void *b)
......
......@@ -7,7 +7,7 @@ import shlex
import argparse
from subprocess import Popen, PIPE
keywords = ("iptables-translate", "ip6tables-translate")
keywords = ("iptables-translate", "ip6tables-translate", "ebtables-translate")
if sys.stdout.isatty():
colors = {"magenta": "\033[95m", "green": "\033[92m", "yellow": "\033[93m",
......@@ -40,7 +40,7 @@ def run_test(name, payload):
for line in payload:
if line.startswith(keywords):
tests += 1
process = Popen(shlex.split(line), stdout=PIPE, stderr=PIPE)
process = Popen([ os.path.abspath(os.path.curdir) + "/iptables/xtables-compat-multi" ] + shlex.split(line), stdout=PIPE, stderr=PIPE)
(output, error) = process.communicate()
if process.returncode == 0:
translation = output.decode("utf-8").rstrip(" \n")
......@@ -86,6 +86,8 @@ def load_test_files():
print("%d test files, %d tests, %d tests passed, %d tests failed, %d errors" % (test_files, total_tests, total_passed, total_failed, total_error))
def main():
os.putenv("XTABLES_LIBDIR", os.path.abspath("extensions"))
os.putenv("PATH", "%s/iptables:%s" % (os.path.abspath(os.path.curdir), os.getenv("PATH")))
if args.test:
if not args.test.endswith(".txlate"):
args.test += ".txlate"
......
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