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

Update upstream source from tag 'upstream/1.8.1'

Update to upstream version '1.8.1'
with Debian dir f7eefdbc289bc01f3d8a1522cd469a3564de9051
parents 698f2511 dab1e98e
......@@ -41,29 +41,15 @@
#include "xtables-multi.h"
extern struct xtables_globals ebtables_globals;
int xtables_eb_main(int argc, char *argv[])
{
int ret;
char *table = "filter";
struct nft_handle h = {
.family = NFPROTO_BRIDGE,
};
struct nft_handle h;
ebtables_globals.program_name = "ebtables";
ret = xtables_init_all(&ebtables_globals, NFPROTO_BRIDGE);
if (ret < 0) {
fprintf(stderr, "%s/%s Failed to initialize ebtables-compat\n",
ebtables_globals.program_name,
ebtables_globals.program_version);
exit(1);
}
nft_init_eb(&h, "ebtables");
#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
init_extensionsb();
#endif
ret = do_commandeb(&h, argc, argv, &table);
ret = do_commandeb(&h, argc, argv, &table, false);
if (ret)
ret = nft_commit(&h);
......
......@@ -14,7 +14,6 @@
#include <linux/netfilter_bridge.h>
#include <linux/netfilter/nf_tables.h>
#include <ebtables/ethernetdb.h>
#include <libiptc/libxtc.h>
#include "xshared.h"
......@@ -130,72 +129,6 @@ extern struct xtables_globals ebtables_globals;
#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"
......@@ -286,9 +219,10 @@ static int do_commandeb_xlate(struct nft_handle *h, int argc, char *argv[], char
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;
struct iptables_command_state cs = {
.argv = argv,
.eb.bitmask = EBT_NOPROTO,
};
char command = 'h';
const char *chain = NULL;
int exec_style = EXEC_STYLE_PRG;
......@@ -299,36 +233,8 @@ static int do_commandeb_xlate(struct nft_handle *h, int argc, char *argv[], char
.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 */
......@@ -425,7 +331,6 @@ print_zero:
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);
......@@ -507,7 +412,7 @@ print_zero:
} else if (c == 'j') {
ebt_check_option2(&flags, OPT_JUMP);
cs.jumpto = parse_target(optarg);
cs.target = command_jump(&cs, cs.jumpto);
cs.target = ebt_command_jump(cs.jumpto);
break;
} else if (c == 's') {
ebt_check_option2(&flags, OPT_SOURCE);
......@@ -559,16 +464,16 @@ print_zero:
xtables_error(PARAMETER_PROBLEM,
"Problem with the specified protocol");
if (*buffer != '\0') {
struct ethertypeent *ent;
struct xt_ethertypeent *ent;
if (!strcasecmp(optarg, "LENGTH")) {
cs.eb.bitmask |= EBT_802_3;
break;
}
ent = getethertypebyname(optarg);
ent = xtables_getethertypebyname(optarg);
if (!ent)
xtables_error(PARAMETER_PROBLEM,
"Problem with the specified Ethernet protocol '%s', perhaps "_PATH_ETHERTYPES " is missing", optarg);
"Problem with the specified Ethernet protocol '%s', perhaps "XT_PATH_ETHERTYPES " is missing", optarg);
cs.eb.ethproto = ent->e_ethertype;
} else
cs.eb.ethproto = i;
......@@ -621,34 +526,13 @@ print_zero:
optind--;
continue;
default:
/* Is it a target option? */
if (cs.target != NULL && cs.target->parse != NULL) {
int opt_offset = cs.target->option_offset;
if (cs.target->parse(c - opt_offset,
argv, ebt_invert,
&cs.target->tflags,
NULL, &cs.target->t))
goto check_extension;
}
ebt_check_inverse2(optarg, argc, argv);
/* Is it a match_option? */
for (m = xtables_matches; m; m = m->next) {
if (m->parse(c - m->option_offset, argv, ebt_check_inverse2(optarg, argc, argv), &m->mflags, NULL, &m->m)) {
ebt_add_match(m, &cs);
goto check_extension;
}
}
if (ebt_command_default(&cs))
xtables_error(PARAMETER_PROBLEM,
"Unknown argument: '%s'",
argv[optind - 1]);
/* Is it a watcher option? */
for (w = xtables_targets; w; w = w->next) {
if (w->parse(c - w->option_offset, argv,
ebt_invert, &w->tflags,
NULL, &w->t)) {
ebt_add_watcher(w, &cs);
goto check_extension;
}
}
check_extension:
if (command != 'A' && command != 'I' &&
command != 'D')
xtables_error(PARAMETER_PROBLEM,
......@@ -700,20 +584,11 @@ 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);
}
struct nft_handle h;
nft_init_eb(&h, argv[0]);
ebtables_globals.compat_rev = dummy_compat_rev;
ret = do_commandeb_xlate(&h, argc, argv, &table);
if (!ret)
fprintf(stderr, "Translation not implemented\n");
......
......@@ -37,7 +37,6 @@
#include <linux/netfilter_bridge.h>
#include <linux/netfilter/nf_tables.h>
#include <ebtables/ethernetdb.h>
#include <libiptc/libxtc.h>
#include "xshared.h"
#include "nft.h"
......@@ -203,7 +202,7 @@ delete_entry(struct nft_handle *h,
return ret;
}
static int get_current_chain(const char *chain)
int ebt_get_current_chain(const char *chain)
{
if (!chain)
return -1;
......@@ -380,33 +379,22 @@ static struct option *merge_options(struct option *oldopts,
/*
* More glue code.
*/
static struct xtables_target *command_jump(struct iptables_command_state *cs,
const char *jumpto)
struct xtables_target *ebt_command_jump(const char *jumpto)
{
struct xtables_target *target;
size_t size;
/* XTF_TRY_LOAD (may be chain name) */
target = xtables_find_target(jumpto, XTF_TRY_LOAD);
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;
unsigned int verdict;
xs_init_target(target);
/* Standard target? */
if (!ebt_fill_target(jumpto, &verdict))
jumpto = "standard";
opts = merge_options(opts, target->extra_opts, &target->option_offset);
if (opts == NULL)
xtables_error(OTHER_PROBLEM, "Can't alloc memory");
/* For ebtables, all targets are preloaded. Hence it is either in
* xtables_targets or a custom chain to jump to, in which case
* returning NULL is fine. */
for (target = xtables_targets; target; target = target->next) {
if (!strcmp(target->name, jumpto))
break;
}
return target;
}
......@@ -668,29 +656,30 @@ void ebt_load_match_extensions(void)
ebt_load_target("dnat");
ebt_load_target("snat");
ebt_load_target("redirect");
ebt_load_target("standard");
}
void ebt_add_match(struct xtables_match *m,
struct iptables_command_state *cs)
{
struct xtables_rule_match *i, **rule_matches = &cs->matches;
struct xtables_rule_match **rule_matches = &cs->matches;
struct xtables_match *newm;
struct ebt_match *newnode;
/* match already in rule_matches, skip inclusion */
for (i = *rule_matches; i; i = i->next) {
if (strcmp(m->name, i->match->name) == 0) {
i->match->mflags |= m->mflags;
return;
}
}
struct ebt_match *newnode, **matchp;
struct xt_entry_match *m2;
newm = xtables_find_match(m->name, XTF_LOAD_MUST_SUCCEED, rule_matches);
if (newm == NULL)
xtables_error(OTHER_PROBLEM,
"Unable to add match %s", m->name);
m2 = xtables_calloc(1, newm->m->u.match_size);
memcpy(m2, newm->m, newm->m->u.match_size);
memset(newm->m->data, 0, newm->size);
xs_init_match(newm);
newm->m = m2;
newm->mflags = m->mflags;
m->mflags = 0;
/* glue code for watchers */
newnode = calloc(1, sizeof(struct ebt_match));
......@@ -700,88 +689,153 @@ void ebt_add_match(struct xtables_match *m,
newnode->ismatch = true;
newnode->u.match = newm;
if (cs->match_list == NULL)
cs->match_list = newnode;
else
cs->match_list->next = newnode;
for (matchp = &cs->match_list; *matchp; matchp = &(*matchp)->next)
;
*matchp = newnode;
}
void ebt_add_watcher(struct xtables_target *watcher,
struct iptables_command_state *cs)
{
struct ebt_match *i, *newnode;
struct ebt_match *newnode, **matchp;
struct xtables_target *clone;
clone = xtables_malloc(sizeof(struct xtables_target));
memcpy(clone, watcher, sizeof(struct xtables_target));
clone->udata = NULL;
clone->tflags = watcher->tflags;
clone->next = clone;
clone->t = xtables_calloc(1, watcher->t->u.target_size);
memcpy(clone->t, watcher->t, watcher->t->u.target_size);
memset(watcher->t->data, 0, watcher->size);
xs_init_target(watcher);
watcher->tflags = 0;
for (i = cs->match_list; i; i = i->next) {
if (i->ismatch)
continue;
if (strcmp(i->u.watcher->name, watcher->name) == 0) {
i->u.watcher->tflags |= watcher->tflags;
return;
}
}
newnode = calloc(1, sizeof(struct ebt_match));
if (newnode == NULL)
xtables_error(OTHER_PROBLEM, "Unable to alloc memory");
newnode->u.watcher = watcher;
newnode->u.watcher = clone;
if (cs->match_list == NULL)
cs->match_list = newnode;
else
cs->match_list->next = newnode;
for (matchp = &cs->match_list; *matchp; matchp = &(*matchp)->next)
;
*matchp = newnode;
}
int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table)
int ebt_command_default(struct iptables_command_state *cs)
{
char *buffer;
int c, i;
int zerochain = -1; /* Needed for the -Z option (we can have -Z <this> -L <that>) */
int chcounter = 0; /* Needed for -C */
int rule_nr = 0;
int rule_nr_end = 0;
int ret = 0;
unsigned int flags = 0;
struct xtables_target *t, *w;
struct xtables_target *t = cs->target;
struct xtables_match *m;
struct iptables_command_state cs;
char command = 'h';
const char *chain = NULL;
const char *policy = NULL;
int selected_chain = -1;
struct xtables_rule_match *xtrm_i;
struct ebt_match *match;
struct ebt_match *matchp;
/* Is it a target option? */
if (t && t->parse) {
if (t->parse(cs->c - t->option_offset, cs->argv,
ebt_invert, &t->tflags, NULL, &t->t))
return 0;
}
memset(&cs, 0, sizeof(cs));
cs.argv = argv;
/* check previously added matches/watchers to this rule first */
for (matchp = cs->match_list; matchp; matchp = matchp->next) {
if (matchp->ismatch) {
m = matchp->u.match;
if (m->parse &&
m->parse(cs->c - m->option_offset, cs->argv,
ebt_invert, &m->mflags, NULL, &m->m))
return 0;
} else {
t = matchp->u.watcher;
if (t->parse &&
t->parse(cs->c - t->option_offset, cs->argv,
ebt_invert, &t->tflags, NULL, &t->t))
return 0;
}
}
/* Is it a match_option? */
for (m = xtables_matches; m; m = m->next) {
if (m->parse &&
m->parse(cs->c - m->option_offset, cs->argv,
ebt_invert, &m->mflags, NULL, &m->m)) {
ebt_add_match(m, cs);
return 0;
}
}
/* Is it a watcher option? */
for (t = xtables_targets; t; t = t->next) {
if (t->parse &&
t->parse(cs->c - t->option_offset, cs->argv,
ebt_invert, &t->tflags, NULL, &t->t)) {
ebt_add_watcher(t, cs);
return 0;
}
}
return 1;
}
int nft_init_eb(struct nft_handle *h, const char *pname)
{
ebtables_globals.program_name = pname;
if (xtables_init_all(&ebtables_globals, NFPROTO_BRIDGE) < 0) {
fprintf(stderr, "%s/%s Failed to initialize ebtables-compat\n",
ebtables_globals.program_name,
ebtables_globals.program_version);
exit(1);
}
#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
init_extensionsb();
#endif
memset(h, 0, sizeof(*h));
h->family = NFPROTO_BRIDGE;
if (nft_init(h, xtables_bridge) < 0)
xtables_error(OTHER_PROBLEM,
"Could not initialize nftables layer.");
h->ops = nft_family_ops_lookup(h->family);
if (h->ops == NULL)
if (!h->ops)
xtables_error(PARAMETER_PROBLEM, "Unknown family");
/* manually registering ebt matches, given the original ebtables parser
* don't use '-m matchname' and the match can't loaded dinamically when
* don't use '-m matchname' and the match can't be loaded dynamically when
* the user calls it.
*/
ebt_load_match_extensions();
/* clear mflags in case do_commandeb gets called a second time
* (we clear the global list of all matches for security)*/
for (m = xtables_matches; m; m = m->next)
m->mflags = 0;
return 0;
}
for (t = xtables_targets; t; t = t->next) {
t->tflags = 0;
t->used = 0;
}
int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table,
bool restore)
{
char *buffer;
int c, i;
int chcounter = 0; /* Needed for -C */
int rule_nr = 0;
int rule_nr_end = 0;
int ret = 0;
unsigned int flags = 0;
struct xtables_target *t;
struct iptables_command_state cs = {
.argv = argv,
.eb.bitmask = EBT_NOPROTO,
};
char command = 'h';
const char *chain = NULL;
const char *policy = NULL;
int selected_chain = -1;
struct xtables_rule_match *xtrm_i;
struct ebt_match *match;
/* prevent getopt to spoil our error reporting */
optind = 0;
opterr = false;
cs.eb.bitmask = EBT_NOPROTO;
/* Getopt saves the day */
while ((c = getopt_long(argc, argv,
......@@ -813,7 +867,7 @@ int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table)
if (optarg && (optarg[0] == '-' || !strcmp(optarg, "!")))
xtables_error(PARAMETER_PROBLEM, "No chain name specified");
chain = optarg;
selected_chain = get_current_chain(chain);
selected_chain = ebt_get_current_chain(chain);
flags |= OPT_COMMAND;
if (c == 'N') {
......@@ -825,7 +879,7 @@ int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table)
chain = argv[optind];
optind++;
}
ret = nft_chain_user_del(h, chain, *table);
ret = nft_chain_user_del(h, chain, *table, 0);
break;
}
......@@ -912,7 +966,6 @@ print_zero:
if (OPT_COMMANDS)
xtables_error(PARAMETER_PROBLEM,
"Multiple commands are not allowed");
command = 'V';
printf("%s %s (nf_tables)\n", prog_name, prog_vers);
exit(0);
case 'h': /* Help */
......@@ -1014,7 +1067,7 @@ print_zero:
} else if (c == 'j') {
ebt_check_option2(&flags, OPT_JUMP);
cs.jumpto = parse_target(optarg);
cs.target = command_jump(&cs, cs.jumpto);
cs.target = ebt_command_jump(cs.jumpto);
break;
} else if (c == 's') {
ebt_check_option2(&flags, OPT_SOURCE);
......@@ -1066,16 +1119,16 @@ print_zero:
xtables_error(PARAMETER_PROBLEM,
"Problem with the specified protocol");
if (*buffer != '\0') {
struct ethertypeent *ent;
struct xt_ethertypeent *ent;
if (!strcasecmp(optarg, "LENGTH")) {
cs.eb.bitmask |= EBT_802_3;
break;
}
ent = getethertypebyname(optarg);
ent = xtables_getethertypebyname(optarg);
if (!ent)
xtables_error(PARAMETER_PROBLEM,
"Problem with the specified Ethernet protocol '%s', perhaps "_PATH_ETHERTYPES " is missing", optarg);
"Problem with the specified Ethernet protocol '%s', perhaps "XT_PATH_ETHERTYPES " is missing", optarg);
cs.eb.ethproto = ent->e_ethertype;
} else
cs.eb.ethproto = i;
......@@ -1138,7 +1191,9 @@ print_zero:
break;*/
/*case 7 :*/ /* atomic-init */
/*case 10:*/ /* atomic-save */
/*case 11:*/ /* init-table */
case 11: /* init-table */
nft_table_flush(h, *table);
return 1;
/*
replace->command = c;
if (OPT_COMMANDS)
......@@ -1176,49 +1231,13 @@ print_zero:
optind--;
continue;
default:
/* Is it a target option? */
if (cs.target != NULL && cs.target->parse != NULL) {
int opt_offset = cs.target->option_offset;
if (cs.target->parse(c - opt_offset,
argv, ebt_invert,
&cs.target->tflags,
NULL, &cs.target->t))
goto check_extension;
}
ebt_check_inverse2(optarg, argc, argv);
/* Is it a match_option? */
for (m = xtables_matches; m; m = m->next) {
if (m->parse(c - m->option_offset, argv, ebt_check_inverse2(optarg, argc, argv), &m->mflags, NULL, &m->m)) {
ebt_add_match(m, &cs);
goto check_extension;
}
}
if (ebt_command_default(&cs))
xtables_error(PARAMETER_PROBLEM,
"Unknown argument: '%s'",
argv[optind - 1]);
/* Is it a watcher option? */
for (w = xtables_targets; w; w = w->next) {
if (w->parse(c - w->option_offset, argv,
ebt_invert, &w->tflags,
NULL, &w->t)) {
ebt_add_watcher(w, &cs);
goto check_extension;
}
}
/*
if (w == NULL && c == '?')
ebt_print_error2("Unknown argument: '%s'", argv[optind - 1], (char)optopt, (char)c);
else if (w == NULL) {
if (!strcmp(t->name, "standard"))
ebt_print_error2("Unknown argument: don't forget the -t option");
else
ebt_print_error2("Target-specific option does not correspond with specified target");
}
if (ebt_errormsg[0] != '\0')
return -1;
if (w->used == 0) {
ebt_add_watcher(new_entry, w);
w->used = 1;
}*/
check_extension:
if (command != 'A' && command != 'I' &&
command != 'D' && command != 'C')
xtables_error(PARAMETER_PROBLEM,
......@@ -1274,28 +1293,24 @@ check_extension:
xtables_error(PARAMETER_PROBLEM, "Wrong policy");
} else if (command == 'L') {
ret = list_rules(h, chain, *table, rule_nr,
flags&OPT_VERBOSE,
flags&OPT_NUMERIC,
0,
0,
/*flags&OPT_EXPANDED*/0,
flags&LIST_N,
flags&LIST_C);
if (!(flags & OPT_ZERO))
exit(0);
}
if (flags & OPT_ZERO) {
selected_chain = zerochain;
ret = nft_chain_zero_counters(h, chain, *table);
ret = nft_chain_zero_counters(h, chain, *table, 0);
} else if (command == 'F') {
ret = nft_rule_flush(h, chain, *table);
ret = nft_rule_flush(h, chain, *table, 0);
} else if (command == 'A') {
ret = append_entry(h, chain, *table, &cs, 0,
flags&OPT_VERBOSE, true);
ret = append_entry(h, chain, *table, &cs, 0, 0, true);
} else if (command == 'I') {
ret = append_entry(h, chain, *table, &cs, rule_nr - 1,
flags&OPT_VERBOSE, false);
0, false);
} else if (command == 'D') {
ret = delete_entry(h, chain, *table, &cs, rule_nr - 1,
rule_nr_end, flags&OPT_VERBOSE);
rule_nr_end, 0);
} /*else if (replace->command == 'C') {
ebt_change_counters(replace, new_entry, rule_nr, rule_nr_end, &(new_entry->cnt_surplus), chcounter);
if (ebt_errormsg[0] != '\0')
......
......@@ -25,10 +25,10 @@ Watch for trace events generated by packets that have been tagged
using the TRACE target.
.TP
\fB\-4\fP
Restrict output to ipv4.
Restrict output to IPv4.
.TP
\fB\-6\fP
Restrict output to ipv6.
Restrict output to IPv6.
.SH EXAMPLE OUTPUT
.TP
.B xtables-monitor \-\-trace
......@@ -55,10 +55,10 @@ PREROUTING chain, and is returning, followed by use the chain policy to make acc
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.
It then got DROPPED by the policy of the INPUT table, as shown by line six.
The last line shows another packet arriving \-\- the packet id is different.
When using the TRACE target, it is usally a good idea to only select packets
When using the TRACE target, it is usually a good idea to only select packets
that are relevant, for example via
.nf
iptables \-t raw \-A PREROUTING \-p tcp \-\-dport 80 \-\-syn \-m limit \-\-limit 1/s \-j TRACE
......
......@@ -73,12 +73,9 @@ 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();
......@@ -98,21 +95,16 @@ static int rule_cb(const struct nlmsghdr *nlh, void *data)
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_print_save(r, type == NFT_MSG_NEWRULE ? NFT_RULE_APPEND :
NFT_RULE_DEL,
counters ? 0 : FMT_NOCOUNTS);
err_free:
......@@ -576,6 +568,7 @@ static const struct option options[] = {
{.name = "ipv4", .has_arg = false, .val = '4'},
{.name = "ipv6", .has_arg = false, .val = '6'},
{.name = "version", .has_arg = false, .val = 'V'},
{.name = "help", .has_arg = false, .val = 'h'},
{NULL},
};
......@@ -585,10 +578,10 @@ static void print_usage(void)
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"
" --event -e show events that modify the ruleset\n"
"Optional arguments:\n"
" --ipv4 -4 only monitor ipv4\n"
" --ipv6 -6 only monitor ipv6\n"
" --ipv4 -4 only monitor IPv4\n"
" --ipv6 -6 only monitor IPv6\n"
" --counters -c show counters in rules\n"
, xtables_globals.program_name);
......@@ -600,7 +593,7 @@ int xtables_monitor_main(int argc, char *argv[])
struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
uint32_t nfgroup = 0;
struct cb_arg cb_arg;
struct cb_arg cb_arg = {};
int ret, c;
xtables_globals.program_name = "xtables-monitor";
......@@ -617,7 +610,6 @@ int xtables_monitor_main(int argc, char *argv[])
init_extensions4();
#endif
memset(&cb_arg, 0, sizeof(cb_arg));
opterr = 0;
while ((c = getopt_long(argc, argv, "ceht46V", options, NULL)) != -1) {
switch (c) {
......@@ -644,7 +636,7 @@ int xtables_monitor_main(int argc, char *argv[])
exit(0);
default:
fprintf(stderr, "xtables-monitor %s: Bad argument.\n", IPTABLES_VERSION);
fprintf(stderr, "Try `xtables-monitor -h' for more information.");
fprintf(stderr, "Try `xtables-monitor -h' for more information.\n");
exit(PARAMETER_PROBLEM);
}
}
......
......@@ -15,7 +15,11 @@ extern int xtables_eb_xlate_main(int, char **);
extern int xtables_ip4_xlate_restore_main(int, char **);
extern int xtables_ip6_xlate_restore_main(int, char **);
extern int xtables_arp_main(int, char **);
extern int xtables_arp_restore_main(int, char **);
extern int xtables_arp_save_main(int, char **);
extern int xtables_eb_main(int, char **);
extern int xtables_eb_restore_main(int, char **);
extern int xtables_eb_save_main(int, char **);
extern int xtables_config_main(int, char **);
extern int xtables_monitor_main(int, char **);
#endif
......
......@@ -31,8 +31,18 @@ static const struct subcommand multi_subcommands[] = {
{"iptables-restore-translate", xtables_ip4_xlate_restore_main},
{"ip6tables-restore-translate", xtables_ip6_xlate_restore_main},
{"arptables", xtables_arp_main},
{"arptables-nft", xtables_arp_main},
{"arptables-restore", xtables_arp_restore_main},
{"arptables-nft-restore", xtables_arp_restore_main},
{"arptables-save", xtables_arp_save_main},
{"arptables-nft-save", xtables_arp_save_main},
{"ebtables-translate", xtables_eb_xlate_main},
{"ebtables", xtables_eb_main},
{"ebtables-restore", xtables_eb_restore_main},
{"ebtables-save", xtables_eb_save_main},
{"ebtables-nft", xtables_eb_main},
{"ebtables-nft-restore", xtables_eb_restore_main},
{"ebtables-nft-save", xtables_eb_save_main},
{"xtables-monitor", xtables_monitor_main},
{NULL},
};
......
......@@ -16,14 +16,9 @@
#include "libiptc/libiptc.h"
#include "xtables-multi.h"
#include "nft.h"
#include "nft-bridge.h"
#include <libnftnl/chain.h>
#ifdef DEBUG
#define DEBUGP(x, args...) fprintf(stderr, x, ## args)
#else
#define DEBUGP(x, args...)
#endif
static int counters, verbose, noflush;
/* Keeping track of external matches and targets. */
......@@ -61,110 +56,6 @@ static void print_usage(const char *name, const char *version)
" [ --ipv6 ]\n", name);
}
static int parse_counters(char *string, struct xt_counters *ctr)
{
unsigned long long pcnt, bcnt;
int ret;
ret = sscanf(string, "[%llu:%llu]", &pcnt, &bcnt);
ctr->pcnt = pcnt;
ctr->bcnt = bcnt;
return ret == 2;
}
/* global new argv and argc */
static char *newargv[255];
static int newargc;
/* function adding one argument to newargv, updating newargc
* returns true if argument added, false otherwise */
static int add_argv(char *what) {
DEBUGP("add_argv: %s\n", what);
if (what && newargc + 1 < ARRAY_SIZE(newargv)) {
newargv[newargc] = strdup(what);
newargv[++newargc] = NULL;
return 1;
} else {
xtables_error(PARAMETER_PROBLEM,
"Parser cannot handle more arguments\n");
return 0;
}
}
static void free_argv(void) {
int i;
for (i = 0; i < newargc; i++)
free(newargv[i]);
}
static void add_param_to_argv(char *parsestart)
{
int quote_open = 0, escaped = 0, param_len = 0;
char param_buffer[1024], *curchar;
/* After fighting with strtok enough, here's now
* a 'real' parser. According to Rusty I'm now no
* longer a real hacker, but I can live with that */
for (curchar = parsestart; *curchar; curchar++) {
if (quote_open) {
if (escaped) {
param_buffer[param_len++] = *curchar;
escaped = 0;
continue;
} else if (*curchar == '\\') {
escaped = 1;
continue;
} else if (*curchar == '"') {
quote_open = 0;
*curchar = ' ';
} else {
param_buffer[param_len++] = *curchar;
continue;
}
} else {
if (*curchar == '"') {
quote_open = 1;
continue;
}
}
if (*curchar == ' '
|| *curchar == '\t'
|| * curchar == '\n') {
if (!param_len) {
/* two spaces? */
continue;
}
param_buffer[param_len] = '\0';
/* check if table name specified */
if ((param_buffer[0] == '-' &&
param_buffer[1] != '-' &&
strchr(param_buffer, 't')) ||
(!strncmp(param_buffer, "--t", 3) &&
!strncmp(param_buffer, "--table", strlen(param_buffer)))) {
xtables_error(PARAMETER_PROBLEM,
"The -t option (seen in line %u) cannot be "
"used in xtables-restore.\n", line);
exit(1);
}
add_argv(param_buffer);
param_len = 0;
} else {
/* regular character, copy to buffer */
param_buffer[param_len++] = *curchar;
if (param_len >= sizeof(param_buffer))
xtables_error(PARAMETER_PROBLEM,
"Parameter too long!");
}
}
}
static struct nftnl_chain_list *get_chain_list(struct nft_handle *h)
{
struct nftnl_chain_list *chain_list;
......@@ -215,7 +106,7 @@ void xtables_restore_parse(struct nft_handle *h,
{
char buffer[10240];
int in_table = 0;
char curtable[XT_TABLE_MAXNAMELEN + 1];
struct builtin_table *curtable = NULL;
const struct xtc_ops *ops = &xtc_ops;
struct nftnl_chain_list *chain_list = NULL;
......@@ -253,7 +144,7 @@ void xtables_restore_parse(struct nft_handle *h,
}
in_table = 0;
} else if ((buffer[0] == '*') && (!in_table)) {
} else if ((buffer[0] == '*') && (!in_table || !p->commit)) {
/* New table */
char *table;
......@@ -265,8 +156,11 @@ void xtables_restore_parse(struct nft_handle *h,
xt_params->program_name, line);
exit(1);
}
strncpy(curtable, table, XT_TABLE_MAXNAMELEN);
curtable[XT_TABLE_MAXNAMELEN] = '\0';
curtable = nft_table_builtin_find(h, table);
if (!curtable)
xtables_error(PARAMETER_PROBLEM,
"%s: line %u table name '%s' invalid\n",
xt_params->program_name, line, table);
if (p->tablename && (strcmp(p->tablename, table) != 0))
continue;
......@@ -288,6 +182,7 @@ void xtables_restore_parse(struct nft_handle *h,
/* New chain. */
char *policy, *chain = NULL;
struct xt_counters count = {};
bool chain_exists = false;
chain = strtok(buffer+1, " \t\n");
DEBUGP("line %u, chain '%s'\n", line, chain);
......@@ -300,16 +195,18 @@ void xtables_restore_parse(struct nft_handle *h,
if (noflush == 0) {
if (cb->chain_del)
cb->chain_del(chain_list, curtable,
cb->chain_del(chain_list, curtable->name,
chain);
} else {
} else if (nft_chain_list_find(chain_list,
curtable->name, chain)) {
chain_exists = true;
/* 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);
curtable->name, chain);
}
if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
......@@ -327,7 +224,7 @@ void xtables_restore_parse(struct nft_handle *h,
exit(1);
}
if (strcmp(policy, "-") != 0) {
if (nft_chain_builtin_find(curtable, chain)) {
if (counters) {
char *ctrs;
ctrs = strtok(NULL, " \t\n");
......@@ -339,7 +236,8 @@ void xtables_restore_parse(struct nft_handle *h,
}
if (cb->chain_set &&
cb->chain_set(h, curtable, chain, policy, &count) < 0) {
cb->chain_set(h, curtable->name,
chain, policy, &count) < 0) {
xtables_error(OTHER_PROBLEM,
"Can't set policy `%s'"
" on `%s' line %u: %s\n",
......@@ -351,8 +249,10 @@ void xtables_restore_parse(struct nft_handle *h,
ret = 1;
} else {
if (cb->chain_user_add &&
cb->chain_user_add(h, chain, curtable) < 0) {
if (!chain_exists &&
cb->chain_user_add &&
cb->chain_user_add(h, chain,
curtable->name) < 0) {
if (errno == EEXIST)
continue;
......@@ -366,7 +266,6 @@ void xtables_restore_parse(struct nft_handle *h,
} else if (in_table) {
int a;
char *ptr = buffer;
char *pcnt = NULL;
char *bcnt = NULL;
char *parsestart;
......@@ -376,7 +275,8 @@ void xtables_restore_parse(struct nft_handle *h,
if (buffer[0] == '[') {
/* we have counters in our input */
ptr = strchr(buffer, ']');
char *ptr = strchr(buffer, ']');
if (!ptr)
xtables_error(PARAMETER_PROBLEM,
"Bad line %u: need ]\n",
......@@ -401,20 +301,20 @@ void xtables_restore_parse(struct nft_handle *h,
parsestart = buffer;
}
add_argv(argv[0]);
add_argv("-t");
add_argv(curtable);
add_argv(argv[0], 0);
add_argv("-t", 0);
add_argv(curtable->name, 0);
if (counters && pcnt && bcnt) {
add_argv("--set-counters");
add_argv((char *) pcnt);
add_argv((char *) bcnt);
add_argv("--set-counters", 0);
add_argv((char *) pcnt, 0);
add_argv((char *) bcnt, 0);
}
add_param_to_argv(parsestart);
add_param_to_argv(parsestart, line);
DEBUGP("calling do_command4(%u, argv, &%s, handle):\n",
newargc, curtable);
newargc, curtable->name);
for (a = 0; a < newargc; a++)
DEBUGP("argv[%u]: %s\n", a, newargv[a]);
......@@ -437,7 +337,8 @@ void xtables_restore_parse(struct nft_handle *h,
free_argv();
fflush(stdout);
}
if (p->tablename && (strcmp(p->tablename, curtable) != 0))
if (p->tablename && curtable &&
(strcmp(p->tablename, curtable->name) != 0))
continue;
if (!ret) {
fprintf(stderr, "%s: line %u failed\n",
......@@ -445,10 +346,13 @@ void xtables_restore_parse(struct nft_handle *h,
exit(1);
}
}
if (in_table) {
if (in_table && p->commit) {
fprintf(stderr, "%s: COMMIT expected at line %u\n",
xt_params->program_name, line + 1);
exit(1);
} else if (in_table && cb->commit && !cb->commit(h)) {
xtables_error(OTHER_PROBLEM, "%s: final implicit COMMIT failed",
xt_params->program_name);
}
}
......@@ -461,7 +365,9 @@ xtables_restore_main(int family, const char *progname, int argc, char *argv[])
.restore = true,
};
int c;
struct nft_xt_restore_parse p = {};
struct nft_xt_restore_parse p = {
.commit = true,
};
line = 0;
......@@ -513,6 +419,8 @@ xtables_restore_main(int family, const char *progname, int argc, char *argv[])
break;
case 'w': /* fallthrough. Ignored by xt-restore */
case 'W':
if (!optarg && xs_has_arg(argc, argv))
optind++;
break;
default:
fprintf(stderr,
......@@ -581,3 +489,75 @@ int xtables_ip6_restore_main(int argc, char *argv[])
return xtables_restore_main(NFPROTO_IPV6, "ip6tables-restore",
argc, argv);
}
struct nft_xt_restore_cb ebt_restore_cb = {
.chain_list = get_chain_list,
.commit = nft_commit,
.table_new = nft_table_new,
.table_flush = nft_table_flush,
.chain_user_flush = nft_chain_user_flush,
.chain_del = chain_delete,
.do_command = do_commandeb,
.chain_set = nft_chain_set,
.chain_user_add = nft_chain_user_add,
};
static const struct option ebt_restore_options[] = {
{.name = "noflush", .has_arg = 0, .val = 'n'},
{ 0 }
};
int xtables_eb_restore_main(int argc, char *argv[])
{
struct nft_xt_restore_parse p = {
.in = stdin,
};
struct nft_handle h;
int c;
while ((c = getopt_long(argc, argv, "n",
ebt_restore_options, NULL)) != -1) {
switch(c) {
case 'n':
noflush = 1;
break;
default:
fprintf(stderr,
"Usage: ebtables-restore [ --noflush ]\n");
exit(1);
break;
}
}
nft_init_eb(&h, "ebtables-restore");
xtables_restore_parse(&h, &p, &ebt_restore_cb, argc, argv);
nft_fini(&h);
return 0;
}
struct nft_xt_restore_cb arp_restore_cb = {
.chain_list = get_chain_list,
.commit = nft_commit,
.table_new = nft_table_new,
.table_flush = nft_table_flush,
.chain_user_flush = nft_chain_user_flush,
.chain_del = chain_delete,
.do_command = do_commandarp,
.chain_set = nft_chain_set,
.chain_user_add = nft_chain_user_add,
};
int xtables_arp_restore_main(int argc, char *argv[])
{
struct nft_xt_restore_parse p = {
.in = stdin,
};
struct nft_handle h;
nft_init_arp(&h, "arptables-restore");
xtables_restore_parse(&h, &p, &arp_restore_cb, argc, argv);
nft_fini(&h);
return 0;
}
......@@ -49,13 +49,10 @@ __do_output(struct nft_handle *h, const char *tablename, bool counters)
struct nftnl_chain_list *chain_list;
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);
if (!nft_table_builtin_find(h, tablename))
printf("# Table `%s' is incompatible, use 'nft' tool.\n",
tablename);
return 0;
}
......@@ -70,7 +67,7 @@ __do_output(struct nft_handle *h, const char *tablename, bool counters)
/* Dump out chain names first,
* thereby preventing dependency conflicts */
nft_chain_save(h, chain_list, tablename);
nft_rule_save(h, tablename, counters);
nft_rule_save(h, tablename, counters ? 0 : FMT_NOCOUNTS);
now = time(NULL);
printf("COMMIT\n");
......@@ -89,6 +86,11 @@ do_output(struct nft_handle *h, const char *tablename, bool counters)
return !!ret;
}
if (!nft_table_find(h, tablename)) {
printf("Table `%s' does not exist\n", tablename);
return 1;
}
ret = __do_output(h, tablename, counters);
nft_check_xt_legacy(h->family, true);
return ret;
......@@ -203,12 +205,12 @@ xtables_save_main(int family, const char *progname, int argc, char *argv[])
exit(EXIT_FAILURE);
}
if (dump) {
do_output(&h, tablename, show_counters);
ret = do_output(&h, tablename, show_counters);
nft_fini(&h);
if (dump)
exit(0);
}
return do_output(&h, tablename, show_counters);
return ret;
}
int xtables_ip4_save_main(int argc, char *argv[])
......@@ -220,3 +222,111 @@ int xtables_ip6_save_main(int argc, char *argv[])
{
return xtables_save_main(NFPROTO_IPV6, "ip6tables-save", argc, argv);
}
static int __ebt_save(struct nft_handle *h, const char *tablename, bool counters)
{
struct nftnl_chain_list *chain_list;
static bool first = true;
time_t now;
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;
}
chain_list = nft_chain_dump(h);
if (first) {
now = time(NULL);
printf("# Generated by ebtables-save v%s on %s",
IPTABLES_VERSION, ctime(&now));
first = false;
}
printf("*%s\n", tablename);
/* Dump out chain names first,
* thereby preventing dependency conflicts */
nft_chain_save(h, chain_list, tablename);
nft_rule_save(h, tablename,
FMT_EBT_SAVE | (counters ? 0 : FMT_NOCOUNTS));
printf("\n");
return 0;
}
int xtables_eb_save_main(int argc_, char *argv_[])
{
const char *ctr = getenv("EBTABLES_SAVE_COUNTER");
struct nft_handle h = {
.family = NFPROTO_BRIDGE,
};
int c;
if (ctr && strcmp(ctr, "yes"))
ctr = NULL;
xtables_globals.program_name = "ebtables-save";
c = xtables_init_all(&xtables_globals, h.family);
if (c < 0) {
fprintf(stderr, "%s/%s Failed to initialize xtables\n",
xtables_globals.program_name,
xtables_globals.program_version);
exit(1);
}
if (nft_init(&h, xtables_bridge) < 0) {
fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
xtables_globals.program_name,
xtables_globals.program_version,
strerror(errno));
exit(EXIT_FAILURE);
}
nft_for_each_table(&h, __ebt_save, !!ctr);
nft_fini(&h);
return 0;
}
int xtables_arp_save_main(int argc, char **argv)
{
struct nft_handle h = {
.family = NFPROTO_ARP,
};
int c;
xtables_globals.program_name = "arptables-save";
c = xtables_init_all(&xtables_globals, h.family);
if (c < 0) {
fprintf(stderr, "%s/%s Failed to initialize xtables\n",
xtables_globals.program_name,
xtables_globals.program_version);
exit(1);
}
if (nft_init(&h, xtables_arp) < 0) {
fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
xtables_globals.program_name,
xtables_globals.program_version,
strerror(errno));
exit(EXIT_FAILURE);
}
if (!nft_table_find(&h, "filter"))
return 0;
if (!nft_is_table_compatible(&h, "filter")) {
printf("# Table `filter' is incompatible, use 'nft' tool.\n");
return 0;
}
printf("*filter\n");
nft_chain_save(&h, nft_chain_dump(&h), "filter");
nft_rule_save(&h, "filter", FMT_NOCOUNTS);
printf("\n");
nft_fini(&h);
return 0;
}
......@@ -156,9 +156,9 @@ static const int inverse_for_options[NUMBER_OF_OPT] =
/* -f */ IPT_INV_FRAG,
};
#define opts xtables_globals.opts
#define prog_name xtables_globals.program_name
#define prog_vers xtables_globals.program_version
#define opts xt_params->opts
#define prog_name xt_params->program_name
#define prog_vers xt_params->program_version
static void __attribute__((noreturn))
exit_tryhelp(int status)
......@@ -363,27 +363,6 @@ parse_rulenumber(const char *rule)
return rulenum;
}
static const char *
parse_target(const char *targetname)
{
const char *ptr;
if (strlen(targetname) < 1)
xtables_error(PARAMETER_PROBLEM,
"Invalid target name (too short)");
if (strlen(targetname) >= XT_EXTENSION_MAXNAMELEN)
xtables_error(PARAMETER_PROBLEM,
"Invalid target name `%s' (%u chars max)",
targetname, XT_EXTENSION_MAXNAMELEN - 1);
for (ptr = targetname; *ptr; ptr++)
if (isspace(*ptr))
xtables_error(PARAMETER_PROBLEM,
"Invalid target name `%s'", targetname);
return targetname;
}
static void
set_option(unsigned int *options, unsigned int option, uint8_t *invflg,
int invert)
......@@ -599,88 +578,7 @@ list_rules(struct nft_handle *h, const char *chain, const char *table,
if (counters)
counters = -1; /* iptables -c format */
nft_rule_list_save(h, chain, table, rulenum, counters);
/* iptables does not return error if rule number not found */
return 1;
}
static void command_jump(struct iptables_command_state *cs)
{
size_t size;
set_option(&cs->options, OPT_JUMP, &cs->fw.ip.invflags, cs->invert);
cs->jumpto = parse_target(optarg);
/* TRY_LOAD (may be chain name) */
cs->target = xtables_find_target(cs->jumpto, XTF_TRY_LOAD);
if (cs->target == NULL)
return;
size = XT_ALIGN(sizeof(struct xt_entry_target))
+ cs->target->size;
cs->target->t = xtables_calloc(1, size);
cs->target->t->u.target_size = size;
if (cs->target->real_name == NULL) {
strcpy(cs->target->t->u.user.name, cs->jumpto);
} else {
/* Alias support for userspace side */
strcpy(cs->target->t->u.user.name, cs->target->real_name);
if (!(cs->target->ext_flags & XTABLES_EXT_ALIAS))
fprintf(stderr, "Notice: The %s target is converted into %s target "
"in rule listing and saving.\n",
cs->jumpto, cs->target->real_name);
}
cs->target->t->u.user.revision = cs->target->revision;
xs_init_target(cs->target);
if (cs->target->x6_options != NULL)
opts = xtables_options_xfrm(xtables_globals.orig_opts, opts,
cs->target->x6_options,
&cs->target->option_offset);
else
opts = xtables_merge_options(xtables_globals.orig_opts, opts,
cs->target->extra_opts,
&cs->target->option_offset);
if (opts == NULL)
xtables_error(OTHER_PROBLEM, "can't alloc memory!");
}
static void command_match(struct iptables_command_state *cs)
{
struct xtables_match *m;
size_t size;
if (cs->invert)
xtables_error(PARAMETER_PROBLEM,
"unexpected ! flag before --match");
m = xtables_find_match(optarg, XTF_LOAD_MUST_SUCCEED, &cs->matches);
size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size;
m->m = xtables_calloc(1, size);
m->m->u.match_size = size;
if (m->real_name == NULL) {
strcpy(m->m->u.user.name, m->name);
} else {
strcpy(m->m->u.user.name, m->real_name);
if (!(m->ext_flags & XTABLES_EXT_ALIAS))
fprintf(stderr, "Notice: the %s match is converted into %s match "
"in rule listing and saving.\n", m->name, m->real_name);
}
m->m->u.user.revision = m->revision;
xs_init_match(m);
if (m == m->next)
return;
/* Merge options for non-cloned matches */
if (m->x6_options != NULL)
opts = xtables_options_xfrm(xtables_globals.orig_opts, opts,
m->x6_options, &m->option_offset);
else if (m->extra_opts != NULL)
opts = xtables_merge_options(xtables_globals.orig_opts, opts,
m->extra_opts, &m->option_offset);
if (opts == NULL)
xtables_error(OTHER_PROBLEM, "can't alloc memory!");
return nft_rule_list_save(h, chain, table, rulenum, counters);
}
void do_parse(struct nft_handle *h, int argc, char *argv[],
......@@ -915,11 +813,13 @@ void do_parse(struct nft_handle *h, int argc, char *argv[],
set_option(&cs->options, OPT_JUMP, &args->invflags,
cs->invert);
args->goto_set = true;
cs->jumpto = parse_target(optarg);
cs->jumpto = xt_parse_target(optarg);
break;
#endif
case 'j':
set_option(&cs->options, OPT_JUMP, &cs->fw.ip.invflags,
cs->invert);
command_jump(cs);
break;
......@@ -979,6 +879,10 @@ void do_parse(struct nft_handle *h, int argc, char *argv[],
if (cs->invert)
xtables_error(PARAMETER_PROBLEM,
"unexpected ! flag before --table");
if (!nft_table_builtin_find(h, optarg))
xtables_error(VERSION_PROBLEM,
"table '%s' does not exist",
optarg);
p->table = optarg;
break;
......@@ -1159,12 +1063,18 @@ void do_parse(struct nft_handle *h, int argc, char *argv[],
p->chain);
}
/*
* Contrary to what iptables does, we assume that any jumpto
* is a custom chain jumps (if no target is found). Later on,
* nf_table will spot the error if the chain does not exists.
*/
if (!nft_chain_exists(h, p->table, p->chain))
xtables_error(OTHER_PROBLEM,
"Chain '%s' does not exist", cs->jumpto);
if (!cs->target && strlen(cs->jumpto) > 0 &&
!nft_chain_exists(h, p->table, cs->jumpto))
xtables_error(PARAMETER_PROBLEM,
"Chain '%s' does not exist", cs->jumpto);
}
if (p->command == CMD_NEW_CHAIN &&
nft_chain_exists(h, p->table, p->chain))
xtables_error(OTHER_PROBLEM, "Chain already exists");
}
int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table,
......@@ -1213,10 +1123,12 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table,
cs.options&OPT_VERBOSE, h, false);
break;
case CMD_FLUSH:
ret = nft_rule_flush(h, p.chain, p.table);
ret = nft_rule_flush(h, p.chain, p.table,
cs.options & OPT_VERBOSE);
break;
case CMD_ZERO:
ret = nft_chain_zero_counters(h, p.chain, p.table);
ret = nft_chain_zero_counters(h, p.chain, p.table,
cs.options & OPT_VERBOSE);
break;
case CMD_ZERO_NUM:
ret = nft_rule_zero_counters(h, p.chain, p.table,
......@@ -1231,8 +1143,8 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table,
cs.options & OPT_EXPANDED,
cs.options & OPT_LINENUMBERS);
if (ret && (p.command & CMD_ZERO)) {
ret = nft_chain_zero_counters(h, p.chain,
p.table);
ret = nft_chain_zero_counters(h, p.chain, p.table,
cs.options & OPT_VERBOSE);
}
if (ret && (p.command & CMD_ZERO_NUM)) {
ret = nft_rule_zero_counters(h, p.chain, p.table,
......@@ -1246,8 +1158,8 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table,
ret = list_rules(h, p.chain, p.table, p.rulenum,
cs.options & OPT_VERBOSE);
if (ret && (p.command & CMD_ZERO)) {
ret = nft_chain_zero_counters(h, p.chain,
p.table);
ret = nft_chain_zero_counters(h, p.chain, p.table,
cs.options & OPT_VERBOSE);
}
if (ret && (p.command & CMD_ZERO_NUM)) {
ret = nft_rule_zero_counters(h, p.chain, p.table,
......@@ -1259,16 +1171,14 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table,
ret = nft_chain_user_add(h, p.chain, p.table);
break;
case CMD_DELETE_CHAIN:
ret = nft_chain_user_del(h, p.chain, p.table);
ret = nft_chain_user_del(h, p.chain, p.table,
cs.options & OPT_VERBOSE);
break;
case CMD_RENAME_CHAIN:
ret = nft_chain_user_rename(h, p.chain, p.table, p.newname);
break;
case CMD_SET_POLICY:
ret = nft_chain_set(h, p.table, p.chain, p.policy, NULL);
if (ret < 0)
xtables_error(PARAMETER_PROBLEM, "Wrong policy `%s'\n",
p.policy);
break;
default:
/* We should never reach this... */
......
......@@ -1115,8 +1115,9 @@ static inline int iptcc_compile_rule (struct xtc_handle *h, STRUCT_REPLACE *repl
STRUCT_STANDARD_TARGET *t;
t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
/* memset for memcmp convenience on delete/replace */
memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
memset(t->target.u.user.name, 0, XT_EXTENSION_MAXNAMELEN);
strcpy(t->target.u.user.name, STANDARD_TARGET);
t->target.u.user.revision = 0;
/* Jumps can only happen to builtin chains, so we
* can safely assume that they always have a header */
t->verdict = r->jump->head_offset + IPTCB_CHAIN_START_SIZE;
......@@ -1149,7 +1150,8 @@ static int iptcc_compile_chain(struct xtc_handle *h, STRUCT_REPLACE *repl, struc
strcpy(head->name.target.u.user.name, ERROR_TARGET);
head->name.target.u.target_size =
ALIGN(sizeof(struct xt_error_target));
strcpy(head->name.errorname, c->name);
strncpy(head->name.errorname, c->name, XT_FUNCTION_MAXNAMELEN);
head->name.errorname[XT_FUNCTION_MAXNAMELEN - 1] = '\0';
} else {
repl->hook_entry[c->hooknum-1] = c->head_offset;
repl->underflow[c->hooknum-1] = c->foot_offset;
......@@ -1269,7 +1271,7 @@ static int iptcc_compile_table(struct xtc_handle *h, STRUCT_REPLACE *repl)
/* Allocate handle of given size */
static struct xtc_handle *
alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
alloc_handle(STRUCT_GETINFO *infop)
{
struct xtc_handle *h;
......@@ -1280,14 +1282,14 @@ alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
}
memset(h, 0, sizeof(*h));
INIT_LIST_HEAD(&h->chains);
strcpy(h->info.name, tablename);
strcpy(h->info.name, infop->name);
h->entries = malloc(sizeof(STRUCT_GET_ENTRIES) + size);
h->entries = malloc(sizeof(STRUCT_GET_ENTRIES) + infop->size);
if (!h->entries)
goto out_free_handle;
strcpy(h->entries->name, tablename);
h->entries->size = size;
strcpy(h->entries->name, infop->name);
h->entries->size = infop->size;
return h;
......@@ -1336,8 +1338,8 @@ retry:
DEBUGP("valid_hooks=0x%08x, num_entries=%u, size=%u\n",
info.valid_hooks, info.num_entries, info.size);
if ((h = alloc_handle(info.name, info.size, info.num_entries))
== NULL) {
h = alloc_handle(&info);
if (h == NULL) {
close(sockfd);
return NULL;
}
......@@ -1675,8 +1677,9 @@ iptcc_standard_map(struct rule_head *r, int verdict)
return 0;
}
/* memset for memcmp convenience on delete/replace */
memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
memset(t->target.u.user.name, 0, XT_EXTENSION_MAXNAMELEN);
strcpy(t->target.u.user.name, STANDARD_TARGET);
t->target.u.user.revision = 0;
t->verdict = verdict;
r->type = IPTCC_R_STANDARD;
......
......@@ -4,7 +4,7 @@ AM_CFLAGS = ${regular_CFLAGS}
AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_srcdir}/include -I${top_srcdir}/iptables ${kinclude_CPPFLAGS}
lib_LTLIBRARIES = libxtables.la
libxtables_la_SOURCES = xtables.c xtoptions.c
libxtables_la_SOURCES = xtables.c xtoptions.c getethertype.c
libxtables_la_LDFLAGS = -version-info ${libxtables_vcurrent}:0:${libxtables_vage}
libxtables_la_LIBADD =
if ENABLE_STATIC
......
......@@ -140,7 +140,7 @@ am__DEPENDENCIES_1 =
libxtables_la_DEPENDENCIES = $(am__DEPENDENCIES_2) \
$(am__DEPENDENCIES_1)
am_libxtables_la_OBJECTS = libxtables_la-xtables.lo \
libxtables_la-xtoptions.lo
libxtables_la-xtoptions.lo libxtables_la-getethertype.lo
libxtables_la_OBJECTS = $(am_libxtables_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
......@@ -366,7 +366,7 @@ xtlibdir = @xtlibdir@
AM_CFLAGS = ${regular_CFLAGS}
AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_srcdir}/include -I${top_srcdir}/iptables ${kinclude_CPPFLAGS}
lib_LTLIBRARIES = libxtables.la
libxtables_la_SOURCES = xtables.c xtoptions.c
libxtables_la_SOURCES = xtables.c xtoptions.c getethertype.c
libxtables_la_LDFLAGS = -version-info ${libxtables_vcurrent}:0:${libxtables_vage}
libxtables_la_LIBADD = $(am__append_1) $(am__append_2)
@ENABLE_SHARED_FALSE@libxtables_la_CFLAGS = ${AM_CFLAGS} -DNO_SHARED_LIBS=1
......@@ -449,6 +449,7 @@ mostlyclean-compile:
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libxtables_la-getethertype.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libxtables_la-xtables.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libxtables_la-xtoptions.Plo@am__quote@
......@@ -487,6 +488,13 @@ libxtables_la-xtoptions.lo: xtoptions.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libxtables_la_CFLAGS) $(CFLAGS) -c -o libxtables_la-xtoptions.lo `test -f 'xtoptions.c' || echo '$(srcdir)/'`xtoptions.c
libxtables_la-getethertype.lo: getethertype.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libxtables_la_CFLAGS) $(CFLAGS) -MT libxtables_la-getethertype.lo -MD -MP -MF $(DEPDIR)/libxtables_la-getethertype.Tpo -c -o libxtables_la-getethertype.lo `test -f 'getethertype.c' || echo '$(srcdir)/'`getethertype.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libxtables_la-getethertype.Tpo $(DEPDIR)/libxtables_la-getethertype.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getethertype.c' object='libxtables_la-getethertype.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libxtables_la_CFLAGS) $(CFLAGS) -c -o libxtables_la-getethertype.lo `test -f 'getethertype.c' || echo '$(srcdir)/'`getethertype.c
mostlyclean-libtool:
-rm -f *.lo
......
......@@ -42,27 +42,26 @@
#include <string.h>
#include <netinet/ether.h>
#include <net/ethernet.h>
#include <ebtables/ethernetdb.h>
#include <xtables.h>
#define MAXALIASES 35
static FILE *etherf = NULL;
static char line[BUFSIZ + 1];
static struct ethertypeent et_ent;
static struct xt_ethertypeent et_ent;
static char *ethertype_aliases[MAXALIASES];
static int ethertype_stayopen;
void setethertypeent(int f)
static void setethertypeent(int f)
{
if (etherf == NULL)
etherf = fopen(_PATH_ETHERTYPES, "r");
etherf = fopen(XT_PATH_ETHERTYPES, "r");
else
rewind(etherf);
ethertype_stayopen |= f;
}
void endethertypeent(void)
static void endethertypeent(void)
{
if (etherf) {
fclose(etherf);
......@@ -71,14 +70,15 @@ void endethertypeent(void)
ethertype_stayopen = 0;
}
struct ethertypeent *getethertypeent(void)
static struct xt_ethertypeent *getethertypeent(void)
{
char *e;
char *endptr;
register char *cp, **q;
if (etherf == NULL
&& (etherf = fopen(_PATH_ETHERTYPES, "r")) == NULL) {
&& (etherf = fopen(XT_PATH_ETHERTYPES, "r")) == NULL) {
return (NULL);
}
......@@ -127,10 +127,9 @@ again:
return (&et_ent);
}
struct ethertypeent *getethertypebyname(const char *name)
struct xt_ethertypeent *xtables_getethertypebyname(const char *name)
{
register struct ethertypeent *e;
register struct xt_ethertypeent *e;
register char **cp;
setethertypeent(ethertype_stayopen);
......@@ -147,9 +146,9 @@ found:
return (e);
}
struct ethertypeent *getethertypebynumber(int type)
struct xt_ethertypeent *xtables_getethertypebynumber(int type)
{
register struct ethertypeent *e;
register struct xt_ethertypeent *e;
setethertypeent(ethertype_stayopen);
while ((e = getethertypeent()) != NULL)
......
......@@ -21,6 +21,7 @@
#include <fcntl.h>
#include <inttypes.h>
#include <netdb.h>
#include <spawn.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
......@@ -119,8 +120,10 @@ struct option *xtables_merge_options(struct option *orig_opts,
* Since @oldopts also has @orig_opts already (and does so at the
* start), skip these entries.
*/
oldopts += num_oold;
num_old -= num_oold;
if (oldopts != NULL) {
oldopts += num_oold;
num_old -= num_oold;
}
merge = malloc(sizeof(*mp) * (num_oold + num_old + num_new + 1));
if (merge == NULL)
......@@ -139,8 +142,10 @@ struct option *xtables_merge_options(struct option *orig_opts,
mp->val += *option_offset;
/* Third, the old options */
memcpy(mp, oldopts, sizeof(*mp) * num_old);
mp += num_old;
if (oldopts != NULL) {
memcpy(mp, oldopts, sizeof(*mp) * num_old);
mp += num_old;
}
xtables_free_opts(0);
/* Clear trailing entry */
......@@ -358,6 +363,7 @@ int xtables_insmod(const char *modname, const char *modprobe, bool quiet)
char *buf = NULL;
char *argv[4];
int status;
pid_t pid;
/* If they don't explicitly set it, read out of kernel */
if (!modprobe) {
......@@ -378,18 +384,11 @@ int xtables_insmod(const char *modname, const char *modprobe, bool quiet)
*/
fflush(stdout);
switch (vfork()) {
case 0:
execv(argv[0], argv);
/* not usually reached */
_exit(1);
case -1:
if (posix_spawn(&pid, argv[0], NULL, NULL, argv, NULL)) {
free(buf);
return -1;
default: /* parent */
wait(&status);
} else {
waitpid(pid, &status, 0);
}
free(buf);
......@@ -488,7 +487,7 @@ bool xtables_strtoui(const char *s, char **end, unsigned int *value,
bool ret;
ret = xtables_strtoul(s, end, &v, min, max);
if (value != NULL)
if (ret && value != NULL)
*value = v;
return ret;
}
......@@ -921,6 +920,12 @@ void xtables_register_match(struct xtables_match *me)
exit(1);
}
if (me->real_name && strlen(me->real_name) >= XT_EXTENSION_MAXNAMELEN) {
fprintf(stderr, "%s: match `%s' has invalid real name\n",
xt_params->program_name, me->real_name);
exit(1);
}
if (me->family >= NPROTO) {
fprintf(stderr,
"%s: BUG: match %s has invalid protocol family\n",
......@@ -1108,6 +1113,12 @@ void xtables_register_target(struct xtables_target *me)
exit(1);
}
if (me->real_name && strlen(me->real_name) >= XT_EXTENSION_MAXNAMELEN) {
fprintf(stderr, "%s: target `%s' has invalid real name\n",
xt_params->program_name, me->real_name);
exit(1);
}
if (me->family >= NPROTO) {
fprintf(stderr,
"%s: BUG: target %s has invalid protocol family\n",
......
......@@ -91,8 +91,10 @@ xtables_options_xfrm(struct option *orig_opts, struct option *oldopts,
* Since @oldopts also has @orig_opts already (and does so at the
* start), skip these entries.
*/
oldopts += num_orig;
num_old -= num_orig;
if (oldopts != NULL) {
oldopts += num_orig;
num_old -= num_orig;
}
merge = malloc(sizeof(*mp) * (num_orig + num_old + num_new + 1));
if (merge == NULL)
......@@ -114,8 +116,10 @@ xtables_options_xfrm(struct option *orig_opts, struct option *oldopts,
}
/* Third, the old options */
memcpy(mp, oldopts, sizeof(*mp) * num_old);
mp += num_old;
if (oldopts != NULL) {
memcpy(mp, oldopts, sizeof(*mp) * num_old);
mp += num_old;
}
xtables_free_opts(0);
/* Clear trailing entry */
......@@ -282,7 +286,7 @@ static void xtopt_mint_value_to_ptr(struct xt_option_call *cb, void **datap,
static void xtopt_parse_mint(struct xt_option_call *cb)
{
const struct xt_option_entry *entry = cb->entry;
const char *arg = cb->arg;
const char *arg;
size_t esize = xtopt_esize_by_type(entry->type);
const uintmax_t lmax = xtopt_max_by_type(entry->type);
void *put = XTOPT_MKPTR(cb);
......@@ -844,7 +848,7 @@ void xtables_option_parse(struct xt_option_call *cb)
* a *RC option type.
*/
cb->nvals = 1;
if (entry->type <= ARRAY_SIZE(xtopt_subparse) &&
if (entry->type < ARRAY_SIZE(xtopt_subparse) &&
xtopt_subparse[entry->type] != NULL)
xtopt_subparse[entry->type](cb);
/* Exclusion with other flags tested later in finalize. */
......
......@@ -141,7 +141,7 @@ static char *xt_osf_strchr(char *ptr, char c)
if (tmp)
*tmp = '\0';
while (tmp && tmp + 1 && isspace(*(tmp + 1)))
while (tmp && isspace(*(tmp + 1)))
tmp++;
return tmp;
......@@ -157,7 +157,6 @@ static void xt_osf_parse_opt(struct xt_osf_opt *opt, __u16 *optnum, char *obuf,
i = 0;
while (ptr != NULL && i < olen && *ptr != 0) {
val = 0;
op = 0;
wc = OSF_WSS_PLAIN;
switch (obuf[i]) {
case 'N':
......@@ -344,7 +343,7 @@ static int osf_load_line(char *buffer, int len, int del)
pend = xt_osf_strchr(pbeg, OSFPDEL);
if (pend) {
*pend = '\0';
cnt = snprintf(obuf, sizeof(obuf), "%s,", pbeg);
snprintf(obuf, sizeof(obuf), "%s,", pbeg);
pbeg = pend + 1;
}
......@@ -352,25 +351,23 @@ static int osf_load_line(char *buffer, int len, int del)
if (pend) {
*pend = '\0';
if (pbeg[0] == '@' || pbeg[0] == '*')
cnt = snprintf(f.genre, sizeof(f.genre), "%s", pbeg + 1);
snprintf(f.genre, sizeof(f.genre), "%s", pbeg + 1);
else
cnt = snprintf(f.genre, sizeof(f.genre), "%s", pbeg);
snprintf(f.genre, sizeof(f.genre), "%s", pbeg);
pbeg = pend + 1;
}
pend = xt_osf_strchr(pbeg, OSFPDEL);
if (pend) {
*pend = '\0';
cnt = snprintf(f.version, sizeof(f.version), "%s", pbeg);
snprintf(f.version, sizeof(f.version), "%s", pbeg);
pbeg = pend + 1;
}
pend = xt_osf_strchr(pbeg, OSFPDEL);
if (pend) {
*pend = '\0';
cnt =
snprintf(f.subtype, sizeof(f.subtype), "%s", pbeg);
pbeg = pend + 1;
snprintf(f.subtype, sizeof(f.subtype), "%s", pbeg);
}
xt_osf_parse_opt(f.opt, &f.opt_num, obuf, sizeof(obuf));
......@@ -384,7 +381,7 @@ static int osf_load_line(char *buffer, int len, int del)
nfnl_addattr_l(nmh, sizeof(buf), OSF_ATTR_FINGER, &f, sizeof(struct xt_osf_user_finger));
return nfnl_talk(nfnlh, nmh, 0, 0, NULL, NULL, NULL);
return nfnl_query(nfnlh, nmh);
}
static int osf_load_entries(char *path, int del)
......
......@@ -40,7 +40,7 @@ def run_test(name, payload):
for line in payload:
if line.startswith(keywords):
tests += 1
process = Popen([ os.path.abspath(os.path.curdir) + "/iptables/xtables-compat-multi" ] + shlex.split(line), stdout=PIPE, stderr=PIPE)
process = Popen([ os.path.abspath(os.path.curdir) + "/iptables/xtables-nft-multi" ] + shlex.split(line), stdout=PIPE, stderr=PIPE)
(output, error) = process.communicate()
if process.returncode == 0:
translation = output.decode("utf-8").rstrip(" \n")
......
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