.B APPEND,
.B REPLACE
operations).
+.TP
+.B --compat
+When creating a rule, attach compatibility data to the rule's userdata section
+for use as aid in parsing the rule by an older version of the program. The old
+version obviously needs to support this, though.
+Specifying this option a second time instructs the program to default to the
+rule's compatibility data when parsing, which is mostly useful for debugging or
+testing purposes.
+
+The \fBXTABLES_COMPAT\fP environment variable can be used to override the
+default setting. The expected value is a natural number representing the number
+of times \fB--compat\fP was specified.
.SS RULE-SPECIFICATIONS
The following command line arguments make up a rule specification (as used
.B --concurrent
This would use a file lock to support concurrent scripts updating the ebtables
kernel tables. It is not needed with \fBebtables-nft\fP though and thus ignored.
+.TP
+.B --compat
+When creating a rule, attach compatibility data to the rule's userdata section
+for use as aid in parsing the rule by an older version of the program. The old
+version obviously needs to support this, though.
+Specifying this option a second time instructs the program to default to the
+rule's compatibility data when parsing, which is mostly useful for debugging or
+testing purposes.
+
+The \fBXTABLES_COMPAT\fP environment variable can be used to override the
+default setting. The expected value is a natural number representing the number
+of times \fB--compat\fP was specified.
.SS
RULE SPECIFICATIONS
.TP
\fB\-T\fP, \fB\-\-table\fP \fIname\fP
Restore only the named table even if the input stream contains other ones.
+.TP
+\fB\-\-compat\fP (nft-variants only)
+When creating a rule, attach compatibility data to the rule's userdata section
+for use as aid in parsing the rule by an older version of the program. The old
+version obviously needs to support this, though.
+Specifying this option a second time instructs the program to default to the
+rule's compatibility data when parsing, which is mostly useful for debugging or
+testing purposes.
+
+The \fBXTABLES_COMPAT\fP environment variable can be used to override the
+default setting. The expected value is a natural number representing the number
+of times \fB--compat\fP was specified.
.SH BUGS
None known as of iptables-1.2.1 release
.SH AUTHORS
\fB\-\-modprobe=\fP\fIcommand\fP
When adding or inserting rules into a chain, use \fIcommand\fP
to load any necessary modules (targets, match extensions, etc).
+.TP
+\fB\-\-compat\fP (nft-variants only)
+When creating a rule, attach compatibility data to the rule's userdata section
+for use as aid in parsing the rule by an older version of the program. The old
+version obviously needs to support this, though.
+Specifying this option a second time instructs the program to default to the
+rule's compatibility data when parsing, which is mostly useful for debugging or
+testing purposes.
+
+The \fBXTABLES_COMPAT\fP environment variable can be used to override the
+default setting. The expected value is a natural number representing the number
+of times \fB--compat\fP was specified.
.SH LOCK FILE
iptables uses the \fI@XT_LOCK_NAME@\fP file to take an exclusive lock at
#include <libnftnl/udata.h>
+int nftnl_rule_expr_count(const struct nftnl_rule *r)
+{
+ struct nftnl_expr_iter *iter = nftnl_expr_iter_create(r);
+ int cnt = 0;
+
+ if (!iter)
+ return -1;
+
+ while (nftnl_expr_iter_next(iter))
+ cnt++;
+
+ nftnl_expr_iter_destroy(iter);
+ return cnt;
+}
+
static struct rule_udata_ext *
rule_get_udata_ext(const struct nftnl_rule *r, uint32_t *outlen)
{
return nftnl_udata_get(tb[UDATA_TYPE_COMPAT_EXT]);
}
+static void
+pack_rule_udata_ext_data(struct rule_udata_ext *rue,
+ const void *data, size_t datalen)
+{
+ size_t datalen_out = datalen;
+#ifdef HAVE_ZLIB
+ compress(rue->data, &datalen_out, data, datalen);
+ rue->flags |= RUE_FLAG_ZIP;
+#else
+ memcpy(rue->data, data, datalen);
+#endif
+ rue->size = datalen_out;
+}
+
+void rule_add_udata_ext(struct nft_handle *h, struct nftnl_rule *r,
+ uint16_t start_idx, uint16_t end_idx,
+ uint8_t flags, uint16_t size, const void *data)
+{
+ struct rule_udata_ext *ext = NULL;
+ uint32_t extlen = 0, newextlen;
+ char *newext;
+ void *udata;
+
+ if (!h->compat)
+ return;
+
+ ext = rule_get_udata_ext(r, &extlen);
+ if (!ext)
+ extlen = 0;
+
+ udata = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
+ if (!udata)
+ xtables_error(OTHER_PROBLEM, "can't alloc memory!");
+
+ newextlen = sizeof(*ext) + size;
+ newext = xtables_malloc(extlen + newextlen);
+ if (extlen)
+ memcpy(newext, ext, extlen);
+ memset(newext + extlen, 0, newextlen);
+
+ ext = (struct rule_udata_ext *)(newext + extlen);
+ ext->start_idx = start_idx;
+ ext->end_idx = end_idx;
+ ext->flags = flags;
+ ext->orig_size = size;
+ pack_rule_udata_ext_data(ext, data, size);
+ newextlen = sizeof(*ext) + ext->size;
+
+ if (!nftnl_udata_put(udata, UDATA_TYPE_COMPAT_EXT,
+ extlen + newextlen, newext) ||
+ nftnl_rule_set_data(r, NFTNL_RULE_USERDATA,
+ nftnl_udata_buf_data(udata),
+ nftnl_udata_buf_len(udata)))
+ xtables_error(OTHER_PROBLEM, "can't alloc memory!");
+
+ free(newext);
+ nftnl_udata_buf_free(udata);
+}
+
static struct nftnl_expr *
__nftnl_expr_from_udata_ext(struct rule_udata_ext *rue, const void *data)
{
#include <linux/netfilter/x_tables.h>
+int nftnl_rule_expr_count(const struct nftnl_rule *r);
+
enum rule_udata_ext_flags {
RUE_FLAG_MATCH_TYPE = (1 << 0),
RUE_FLAG_TARGET_TYPE = (1 << 1),
unsigned char data[];
};
+struct nft_handle;
+
+void rule_add_udata_ext(struct nft_handle *h, struct nftnl_rule *r,
+ uint16_t start_idx, uint16_t end_idx,
+ uint8_t flags, uint16_t size, const void *data);
+static inline void
+rule_add_udata_match(struct nft_handle *h, struct nftnl_rule *r,
+ uint16_t start_idx, uint16_t end_idx,
+ const struct xt_entry_match *m)
+{
+ rule_add_udata_ext(h, r, start_idx, end_idx,
+ RUE_FLAG_MATCH_TYPE, m->u.match_size, m);
+}
+
+static inline void
+rule_add_udata_target(struct nft_handle *h, struct nftnl_rule *r,
+ uint16_t start_idx, uint16_t end_idx,
+ const struct xt_entry_target *t)
+{
+ rule_add_udata_ext(h, r, start_idx, end_idx,
+ RUE_FLAG_TARGET_TYPE, t->u.target_size, t);
+}
+
struct nft_xt_ctx;
bool rule_has_udata_ext(const struct nftnl_rule *r);
ret = false;
expr = nftnl_expr_iter_next(ctx.iter);
}
- if (!ret && rule_has_udata_ext(r)) {
+ if ((!ret || h->compat > 1) && rule_has_udata_ext(r)) {
fprintf(stderr,
"Warning: Rule parser failed, trying compat fallback\n");
* This code has been sponsored by Sophos Astaro <http://www.sophos.com>
*/
+#include "config.h"
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include "nft-cache.h"
#include "nft-shared.h"
#include "nft-bridge.h" /* EBT_NOPROTO */
+#include "nft-compat.h"
static void *nft_fn;
nftnl_expr_set(e, NFTNL_EXPR_MT_INFO, info, m->u.match_size - sizeof(*m));
}
-static int add_nft_limit(struct nftnl_rule *r, struct xt_entry_match *m)
+static int add_nft_limit(struct nft_handle *h, struct nftnl_rule *r,
+ struct xt_entry_match *m)
{
struct xt_rateinfo *rinfo = (void *)m->data;
+ int i, ecnt = nftnl_rule_expr_count(r);
static const uint32_t mult[] = {
XT_LIMIT_SCALE*24*60*60, /* day */
XT_LIMIT_SCALE*60*60, /* hour */
XT_LIMIT_SCALE, /* sec */
};
struct nftnl_expr *expr;
- int i;
+
+ rule_add_udata_match(h, r, ecnt, ecnt + 1, m);
expr = nftnl_expr_alloc("limit");
if (!expr)
static int add_nft_udp(struct nft_handle *h, struct nftnl_rule *r,
struct xt_entry_match *m)
{
+ int ret, ecnt = nftnl_rule_expr_count(r);
struct xt_udp *udp = (void *)m->data;
if (udp->invflags > XT_UDP_INV_MASK ||
if (nftnl_rule_get_u32(r, NFTNL_RULE_COMPAT_PROTO) != IPPROTO_UDP)
xtables_error(PARAMETER_PROBLEM, "UDP match requires '-p udp'");
- return add_nft_tcpudp(h, r, udp->spts, udp->invflags & XT_UDP_INV_SRCPT,
- udp->dpts, udp->invflags & XT_UDP_INV_DSTPT);
+ ret = add_nft_tcpudp(h, r, udp->spts, udp->invflags & XT_UDP_INV_SRCPT,
+ udp->dpts, udp->invflags & XT_UDP_INV_DSTPT);
+
+ rule_add_udata_match(h, r, ecnt, nftnl_rule_expr_count(r), m);
+
+ return ret;
}
static int add_nft_tcpflags(struct nft_handle *h, struct nftnl_rule *r,
struct xt_entry_match *m)
{
static const uint8_t supported = XT_TCP_INV_SRCPT | XT_TCP_INV_DSTPT | XT_TCP_INV_FLAGS;
+ int ret, ecnt = nftnl_rule_expr_count(r);
struct xt_tcp *tcp = (void *)m->data;
if (tcp->invflags & ~supported || tcp->option ||
xtables_error(PARAMETER_PROBLEM, "TCP match requires '-p tcp'");
if (tcp->flg_mask) {
- int ret = add_nft_tcpflags(h, r, tcp->flg_cmp, tcp->flg_mask,
- tcp->invflags & XT_TCP_INV_FLAGS);
+ ret = add_nft_tcpflags(h, r, tcp->flg_cmp, tcp->flg_mask,
+ tcp->invflags & XT_TCP_INV_FLAGS);
if (ret < 0)
return ret;
}
- return add_nft_tcpudp(h, r, tcp->spts, tcp->invflags & XT_TCP_INV_SRCPT,
- tcp->dpts, tcp->invflags & XT_TCP_INV_DSTPT);
+ ret = add_nft_tcpudp(h, r, tcp->spts, tcp->invflags & XT_TCP_INV_SRCPT,
+ tcp->dpts, tcp->invflags & XT_TCP_INV_DSTPT);
+
+ rule_add_udata_match(h, r, ecnt, nftnl_rule_expr_count(r), m);
+
+ return ret;
}
static int add_nft_mark(struct nft_handle *h, struct nftnl_rule *r,
struct xt_entry_match *m)
{
struct xt_mark_mtinfo1 *mark = (void *)m->data;
+ int op, ecnt = nftnl_rule_expr_count(r);
uint8_t reg;
- int op;
add_meta(h, r, NFT_META_MARK, ®);
if (mark->mask != 0xffffffff)
add_cmp_u32(r, mark->mark, op, reg);
+ rule_add_udata_match(h, r, ecnt, nftnl_rule_expr_count(r), m);
+
return 0;
}
case NFT_COMPAT_RULE_INSERT:
case NFT_COMPAT_RULE_REPLACE:
if (!strcmp(m->u.user.name, "limit"))
- return add_nft_limit(r, m);
+ return add_nft_limit(h, r, m);
else if (!strcmp(m->u.user.name, "among"))
return add_nft_among(h, r, m);
else if (!strcmp(m->u.user.name, "udp"))
nftnl_expr_set(e, NFTNL_EXPR_TG_INFO, info, t->u.target_size - sizeof(*t));
}
-static int add_meta_nftrace(struct nftnl_rule *r)
+static int add_meta_nftrace(struct nft_handle *h, struct nftnl_rule *r,
+ struct xt_entry_target *t)
{
+ int ecnt = nftnl_rule_expr_count(r);
struct nftnl_expr *expr;
+ rule_add_udata_target(h, r, ecnt, ecnt + 2, t);
+
expr = nftnl_expr_alloc("immediate");
if (expr == NULL)
return -ENOMEM;
struct nftnl_expr *expr;
if (strcmp(t->u.user.name, "TRACE") == 0)
- return add_meta_nftrace(r);
+ return add_meta_nftrace(h, r, t);
expr = nftnl_expr_alloc("target");
if (expr == NULL)
return 0;
}
-static int add_log(struct nftnl_rule *r, struct iptables_command_state *cs);
+static int add_log(struct nft_handle *h, struct nftnl_rule *r,
+ struct iptables_command_state *cs);
int add_action(struct nft_handle *h, struct nftnl_rule *r,
struct iptables_command_state *cs, bool goto_set)
else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0)
ret = add_verdict(r, NFT_RETURN);
else if (strcmp(cs->jumpto, "NFLOG") == 0)
- ret = add_log(r, cs);
+ ret = add_log(h, r, cs);
else
ret = add_target(h, r, cs->target->t);
} else if (strlen(cs->jumpto) > 0) {
return ret;
}
-static int add_log(struct nftnl_rule *r, struct iptables_command_state *cs)
+static int add_log(struct nft_handle *h, struct nftnl_rule *r,
+ struct iptables_command_state *cs)
{
struct nftnl_expr *expr;
struct xt_nflog_info *info = (struct xt_nflog_info *)cs->target->t->data;
+ int ecnt = nftnl_rule_expr_count(r);
+
+ rule_add_udata_target(h, r, ecnt, ecnt + 1, cs->target->t);
expr = nftnl_expr_alloc("log");
if (!expr)
"%s%s%stable `%s' is incompatible, use 'nft' tool.",
pfx, chain, sfx, table);
}
+
+uint8_t compat_env_val(void)
+{
+ const char *val = getenv("XTABLES_COMPAT");
+
+ return val ? atoi(val) : 0;
+}
struct nft_cache_req cache_req;
bool restore;
bool noflush;
+ uint8_t compat;
int8_t config_done;
struct list_head cmd_list;
bool cache_init;
int parse_udata_cb(const struct nftnl_udata *attr, void *data);
+uint8_t compat_env_val(void);
+
#endif
printf(
"[!] --fragment -f match second or further fragments only\n");
+ if (strstr(xt_params->program_version, "nf_tables"))
+ printf(
+" --compat append compatibility data to new rules\n");
printf(
" --modprobe=<command> try to insert modules using this command\n"
" --set-counters -c PKTS BYTES set the counter during insert/append\n"
exit_tryhelp(2, p->line);
+ case 20: /* --compat */
+ p->compat++;
+ break;
+
case 1: /* non option */
if (optarg[0] == '!' && optarg[1] == '\0') {
if (invert)
bool restore;
int line;
int verbose;
+ uint8_t compat;
bool rule_ranges;
struct xt_cmd_parse_ops *ops;
};
{ "line-numbers", 0, 0, '0' },
{ "modprobe", 1, 0, 'M' },
{ "set-counters", 1, 0, 'c' },
+ { "compat", 0, 0, 20 },
{ 0 }
};
{ "init-table" , no_argument , 0, 11 },
{ "concurrent" , no_argument , 0, 13 },
{ "check" , required_argument, 0, 14 },
+ { "compat" , no_argument , 0, 20 },
{ 0 }
};
"[!] --logical-out name[+] : logical bridge output interface name\n"
"--set-counters -c chain\n"
" pcnt bcnt : set the counters of the to be added rule\n"
+"--compat : append compatibility data to new rules\n"
"--modprobe -M program : try to insert modules using this program\n"
"--concurrent : use a file lock to support concurrent scripts\n"
"--verbose -v : verbose mode\n"
.line = line,
.rule_ranges = true,
.ops = &h->ops->cmd_parse,
+ .compat = compat_env_val(),
};
int ret = 0;
do_parse(argc, argv, &p, &cs, &args);
h->verbose = p.verbose;
+ h->compat = p.compat;
t = nft_table_builtin_find(h, p.table);
if (!t)
.B xtables\-monitor(8)
in \-\-trace mode to obtain monitoring trace events.
+Some extensions are implemented via native nf_tables expressions instead of
+\fBnft_compat\fP module. This is transparent to the user as such parts of a
+rule are detected and parsed into an extension again before listing. Also,
+run-time behaviour is supposed to be identical. Implementing extensions this
+way is beneficial from a kernel maintainer's perspective as xtables extension
+modules may at some point become unused, so increasing extension conversion is
+to be expected. Since this may break older versions parsing the ruleset
+in-kernel (a possible scenario with containers sharing a network namespace),
+there is \fB--compat\fP flag which causes the replaced extensions to be
+appended to the rule in userdata storage for the parser to fall back to.
+
.SH EXAMPLES
One basic example is creating the skeleton ruleset in nf_tables from the
xtables-nft tools, in a fresh machine:
{.name = "ipv6", .has_arg = false, .val = '6'},
{.name = "wait", .has_arg = 2, .val = 'w'},
{.name = "wait-interval", .has_arg = 2, .val = 'W'},
+ {.name = "compat", .has_arg = false, .val = 20 },
{NULL},
};
" [ --noflush ]\n"
" [ --table=<TABLE> ]\n"
" [ --modprobe=<command> ]\n"
+ " [ --compat ]\n"
" [ --ipv4 ]\n"
" [ --ipv6 ]\n", name);
}
static int
xtables_restore_main(int family, const char *progname, int argc, char *argv[])
{
+ uint8_t compat = compat_env_val();
struct nft_xt_restore_parse p = {
.commit = true,
.cb = &restore_cb,
if (!optarg && xs_has_arg(argc, argv))
optind++;
break;
+ case 20:
+ compat++;
+ break;
default:
fprintf(stderr,
"Try `%s -h' for more information.\n",
}
h.noflush = noflush;
h.restore = true;
+ h.compat = compat;
xtables_restore_parse(&h, &p);
static const struct option ebt_restore_options[] = {
{.name = "noflush", .has_arg = 0, .val = 'n'},
{.name = "verbose", .has_arg = 0, .val = 'v'},
+ {.name = "compat", .has_arg = 0, .val = 20},
{ 0 }
};
int xtables_eb_restore_main(int argc, char *argv[])
{
+ uint8_t compat = compat_env_val();
struct nft_xt_restore_parse p = {
.in = stdin,
.cb = &ebt_restore_cb,
case 'v':
verbose++;
break;
+ case 20: /* --compat */
+ compat++;
+ break;
default:
fprintf(stderr,
- "Usage: ebtables-restore [ --verbose ] [ --noflush ]\n");
+ "Usage: ebtables-restore [ --verbose ] [ --noflush ] [ --compat ]\n");
exit(1);
break;
}
nft_init_eb(&h, "ebtables-restore");
h.noflush = noflush;
+ h.compat = compat;
xtables_restore_parse(&h, &p);
nft_fini_eb(&h);
{.name = "goto", .has_arg = 1, .val = 'g'},
{.name = "ipv4", .has_arg = 0, .val = '4'},
{.name = "ipv6", .has_arg = 0, .val = '6'},
+ {.name = "compat", .has_arg = 0, .val = 20},
{NULL},
};
.restore = restore,
.line = line,
.ops = &h->ops->cmd_parse,
+ .compat = compat_env_val(),
};
struct iptables_command_state cs = {
.jumpto = "",
do_parse(argc, argv, &p, &cs, &args);
h->verbose = p.verbose;
+ h->compat = p.compat;
if (!nft_table_builtin_find(h, p.table))
xtables_error(VERSION_PROBLEM,