summaryrefslogtreecommitdiffstats
path: root/iptables/nft.c
diff options
context:
space:
mode:
Diffstat (limited to 'iptables/nft.c')
-rw-r--r--iptables/nft.c2271
1 files changed, 1580 insertions, 691 deletions
diff --git a/iptables/nft.c b/iptables/nft.c
index 83cf5fb7..884cc77e 100644
--- a/iptables/nft.c
+++ b/iptables/nft.c
@@ -39,6 +39,8 @@
#include <linux/netfilter/nf_tables_compat.h>
#include <linux/netfilter/xt_limit.h>
+#include <linux/netfilter/xt_NFLOG.h>
+#include <linux/netfilter/xt_mark.h>
#include <libmnl/libmnl.h>
#include <libnftnl/gen.h>
@@ -88,11 +90,11 @@ int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh,
#define NFT_NLMSG_MAXSIZE (UINT16_MAX + getpagesize())
-/* selected batch page is 256 Kbytes long to load ruleset of
- * half a million rules without hitting -EMSGSIZE due to large
- * iovec.
+/* Selected batch page is 2 Mbytes long to support loading a ruleset of 3.5M
+ * rules matching on source and destination address as well as input and output
+ * interfaces. This is what legacy iptables supports.
*/
-#define BATCH_PAGE_SIZE getpagesize() * 32
+#define BATCH_PAGE_SIZE 2 * 1024 * 1024
static struct nftnl_batch *mnl_batch_init(void)
{
@@ -107,7 +109,9 @@ static struct nftnl_batch *mnl_batch_init(void)
static void mnl_nft_batch_continue(struct nftnl_batch *batch)
{
- assert(nftnl_batch_update(batch) >= 0);
+ int ret = nftnl_batch_update(batch);
+
+ assert(ret >= 0);
}
static uint32_t mnl_batch_begin(struct nftnl_batch *batch, uint32_t genid, uint32_t seqnum)
@@ -143,7 +147,7 @@ struct mnl_err {
static void mnl_err_list_node_add(struct list_head *err_list, int error,
int seqnum)
{
- struct mnl_err *err = malloc(sizeof(struct mnl_err));
+ struct mnl_err *err = xtables_malloc(sizeof(struct mnl_err));
err->seqnum = seqnum;
err->err = error;
@@ -220,8 +224,10 @@ static int mnl_batch_talk(struct nft_handle *h, int numcmds)
int err = 0;
ret = mnl_nft_socket_sendmsg(h, numcmds);
- if (ret == -1)
+ if (ret == -1) {
+ fprintf(stderr, "sendmsg() failed: %s\n", strerror(errno));
return -1;
+ }
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
@@ -256,23 +262,6 @@ static int mnl_batch_talk(struct nft_handle *h, int numcmds)
return err;
}
-enum obj_update_type {
- NFT_COMPAT_TABLE_ADD,
- NFT_COMPAT_TABLE_FLUSH,
- NFT_COMPAT_CHAIN_ADD,
- NFT_COMPAT_CHAIN_USER_ADD,
- NFT_COMPAT_CHAIN_USER_DEL,
- NFT_COMPAT_CHAIN_USER_FLUSH,
- NFT_COMPAT_CHAIN_UPDATE,
- NFT_COMPAT_CHAIN_RENAME,
- NFT_COMPAT_CHAIN_ZERO,
- NFT_COMPAT_RULE_APPEND,
- NFT_COMPAT_RULE_INSERT,
- NFT_COMPAT_RULE_REPLACE,
- NFT_COMPAT_RULE_DELETE,
- NFT_COMPAT_RULE_FLUSH,
-};
-
enum obj_action {
NFT_COMPAT_COMMIT,
NFT_COMPAT_ABORT,
@@ -282,12 +271,12 @@ struct obj_update {
struct list_head head;
enum obj_update_type type:8;
uint8_t skip:1;
- uint8_t implicit:1;
unsigned int seq;
union {
struct nftnl_table *table;
struct nftnl_chain *chain;
struct nftnl_rule *rule;
+ struct nftnl_set *set;
void *ptr;
};
struct {
@@ -305,7 +294,7 @@ static int mnl_append_error(const struct nft_handle *h,
[NFT_COMPAT_TABLE_FLUSH] = "TABLE_FLUSH",
[NFT_COMPAT_CHAIN_ADD] = "CHAIN_ADD",
[NFT_COMPAT_CHAIN_USER_ADD] = "CHAIN_USER_ADD",
- [NFT_COMPAT_CHAIN_USER_DEL] = "CHAIN_USER_DEL",
+ [NFT_COMPAT_CHAIN_DEL] = "CHAIN_DEL",
[NFT_COMPAT_CHAIN_USER_FLUSH] = "CHAIN_USER_FLUSH",
[NFT_COMPAT_CHAIN_UPDATE] = "CHAIN_UPDATE",
[NFT_COMPAT_CHAIN_RENAME] = "CHAIN_RENAME",
@@ -315,6 +304,7 @@ static int mnl_append_error(const struct nft_handle *h,
[NFT_COMPAT_RULE_REPLACE] = "RULE_REPLACE",
[NFT_COMPAT_RULE_DELETE] = "RULE_DELETE",
[NFT_COMPAT_RULE_FLUSH] = "RULE_FLUSH",
+ [NFT_COMPAT_SET_ADD] = "SET_ADD",
};
char errmsg[256];
char tcr[128];
@@ -335,7 +325,7 @@ static int mnl_append_error(const struct nft_handle *h,
case NFT_COMPAT_CHAIN_ADD:
case NFT_COMPAT_CHAIN_ZERO:
case NFT_COMPAT_CHAIN_USER_ADD:
- case NFT_COMPAT_CHAIN_USER_DEL:
+ case NFT_COMPAT_CHAIN_DEL:
case NFT_COMPAT_CHAIN_USER_FLUSH:
case NFT_COMPAT_CHAIN_UPDATE:
case NFT_COMPAT_CHAIN_RENAME:
@@ -347,14 +337,27 @@ static int mnl_append_error(const struct nft_handle *h,
case NFT_COMPAT_RULE_REPLACE:
case NFT_COMPAT_RULE_DELETE:
case NFT_COMPAT_RULE_FLUSH:
+ case NFT_COMPAT_RULE_CHANGE_COUNTERS:
snprintf(tcr, sizeof(tcr), "rule in chain %s",
nftnl_rule_get_str(o->rule, NFTNL_RULE_CHAIN));
#if 0
{
- nft_rule_print_save(o->rule, NFT_RULE_APPEND, FMT_NOCOUNTS);
+ nft_rule_print_save(h, o->rule, NFT_RULE_APPEND, FMT_NOCOUNTS);
}
#endif
break;
+ case NFT_COMPAT_SET_ADD:
+ snprintf(tcr, sizeof(tcr), "set %s",
+ nftnl_set_get_str(o->set, NFTNL_SET_NAME));
+ break;
+ case NFT_COMPAT_RULE_LIST:
+ case NFT_COMPAT_RULE_CHECK:
+ case NFT_COMPAT_CHAIN_RESTORE:
+ case NFT_COMPAT_RULE_SAVE:
+ case NFT_COMPAT_RULE_ZERO:
+ case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE:
+ assert(0);
+ break;
}
return snprintf(buf, len, "%s: %s", errmsg, tcr);
@@ -364,10 +367,7 @@ static struct obj_update *batch_add(struct nft_handle *h, enum obj_update_type t
{
struct obj_update *obj;
- obj = calloc(1, sizeof(struct obj_update));
- if (obj == NULL)
- return NULL;
-
+ obj = xtables_calloc(1, sizeof(struct obj_update));
obj->ptr = ptr;
obj->error.lineno = h->error.lineno;
obj->type = type;
@@ -384,10 +384,18 @@ batch_table_add(struct nft_handle *h, enum obj_update_type type,
return batch_add(h, type, t);
}
-static int batch_chain_add(struct nft_handle *h, enum obj_update_type type,
+static struct obj_update *
+batch_set_add(struct nft_handle *h, enum obj_update_type type,
+ struct nftnl_set *s)
+{
+ return batch_add(h, type, s);
+}
+
+static struct obj_update *
+batch_chain_add(struct nft_handle *h, enum obj_update_type type,
struct nftnl_chain *c)
{
- return batch_add(h, type, c) ? 0 : -1;
+ return batch_add(h, type, c);
}
static struct obj_update *
@@ -397,7 +405,39 @@ batch_rule_add(struct nft_handle *h, enum obj_update_type type,
return batch_add(h, type, r);
}
-const struct builtin_table xtables_ipv4[NFT_TABLE_MAX] = {
+static void batch_obj_del(struct nft_handle *h, struct obj_update *o);
+
+static void batch_chain_flush(struct nft_handle *h,
+ const char *table, const char *chain)
+{
+ struct obj_update *obj, *tmp;
+
+ list_for_each_entry_safe(obj, tmp, &h->obj_list, head) {
+ struct nftnl_rule *r = obj->ptr;
+
+ switch (obj->type) {
+ case NFT_COMPAT_RULE_APPEND:
+ case NFT_COMPAT_RULE_INSERT:
+ case NFT_COMPAT_RULE_REPLACE:
+ case NFT_COMPAT_RULE_DELETE:
+ break;
+ default:
+ continue;
+ }
+
+ if (table &&
+ strcmp(table, nftnl_rule_get_str(r, NFTNL_RULE_TABLE)))
+ continue;
+
+ if (chain &&
+ strcmp(chain, nftnl_rule_get_str(r, NFTNL_RULE_CHAIN)))
+ continue;
+
+ batch_obj_del(h, obj);
+ }
+}
+
+static const struct builtin_table xtables_ipv4[NFT_TABLE_MAX] = {
[NFT_TABLE_RAW] = {
.name = "raw",
.type = NFT_TABLE_RAW,
@@ -534,7 +574,7 @@ const struct builtin_table xtables_ipv4[NFT_TABLE_MAX] = {
#include <linux/netfilter_arp.h>
-const struct builtin_table xtables_arp[NFT_TABLE_MAX] = {
+static const struct builtin_table xtables_arp[NFT_TABLE_MAX] = {
[NFT_TABLE_FILTER] = {
.name = "filter",
.type = NFT_TABLE_FILTER,
@@ -557,7 +597,7 @@ const struct builtin_table xtables_arp[NFT_TABLE_MAX] = {
#include <linux/netfilter_bridge.h>
-const struct builtin_table xtables_bridge[NFT_TABLE_MAX] = {
+static const struct builtin_table xtables_bridge[NFT_TABLE_MAX] = {
[NFT_TABLE_FILTER] = {
.name = "filter",
.type = NFT_TABLE_FILTER,
@@ -606,13 +646,20 @@ const struct builtin_table xtables_bridge[NFT_TABLE_MAX] = {
},
},
},
-};
+ [NFT_TABLE_BROUTE] = {
+ .name = "broute",
+ .type = NFT_TABLE_BROUTE,
+ .chains = {
+ {
+ .name = "BROUTING",
+ .type = "filter",
+ .prio = NF_BR_PRI_FIRST,
+ .hook = NF_BR_PRE_ROUTING,
+ },
+ },
+ },
-static bool nft_table_initialized(const struct nft_handle *h,
- enum nft_table_type type)
-{
- return h->cache->table[type].initialized;
-}
+};
static int nft_table_builtin_add(struct nft_handle *h,
const struct builtin_table *_t)
@@ -620,13 +667,14 @@ static int nft_table_builtin_add(struct nft_handle *h,
struct nftnl_table *t;
int ret;
- if (nft_table_initialized(h, _t->type))
+ if (h->cache->table[_t->type].exists)
return 0;
t = nftnl_table_alloc();
if (t == NULL)
return -1;
+ nftnl_table_set_u32(t, NFTNL_TABLE_FAMILY, h->family);
nftnl_table_set_str(t, NFTNL_TABLE_NAME, _t->name);
ret = batch_table_add(h, NFT_COMPAT_TABLE_ADD, t) ? 0 : - 1;
@@ -635,7 +683,7 @@ static int nft_table_builtin_add(struct nft_handle *h,
}
static struct nftnl_chain *
-nft_chain_builtin_alloc(const struct builtin_table *table,
+nft_chain_builtin_alloc(int family, const char *tname,
const struct builtin_chain *chain, int policy)
{
struct nftnl_chain *c;
@@ -644,28 +692,36 @@ nft_chain_builtin_alloc(const struct builtin_table *table,
if (c == NULL)
return NULL;
- nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, table->name);
+ nftnl_chain_set_u32(c, NFTNL_CHAIN_FAMILY, family);
+ nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, tname);
nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, chain->name);
nftnl_chain_set_u32(c, NFTNL_CHAIN_HOOKNUM, chain->hook);
nftnl_chain_set_u32(c, NFTNL_CHAIN_PRIO, chain->prio);
- nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, policy);
+ if (policy >= 0)
+ nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, policy);
+
nftnl_chain_set_str(c, NFTNL_CHAIN_TYPE, chain->type);
+ nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS, 0);
+ nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES, 0);
+
return c;
}
static void nft_chain_builtin_add(struct nft_handle *h,
const struct builtin_table *table,
- const struct builtin_chain *chain)
+ const struct builtin_chain *chain,
+ bool fake)
{
struct nftnl_chain *c;
- c = nft_chain_builtin_alloc(table, chain, NF_ACCEPT);
+ c = nft_chain_builtin_alloc(h->family, table->name, chain, NF_ACCEPT);
if (c == NULL)
return;
- batch_chain_add(h, NFT_COMPAT_CHAIN_ADD, c);
- nftnl_chain_list_add_tail(c, h->cache->table[table->type].chains);
+ if (!fake)
+ batch_chain_add(h, NFT_COMPAT_CHAIN_ADD, c);
+ nft_cache_add_chain(h, table, c);
}
/* find if built-in table already exists */
@@ -709,43 +765,64 @@ nft_chain_builtin_find(const struct builtin_table *t, const char *chain)
static void nft_chain_builtin_init(struct nft_handle *h,
const struct builtin_table *table)
{
- struct nftnl_chain_list *list;
- struct nftnl_chain *c;
int i;
/* Initialize built-in chains if they don't exist yet */
for (i=0; i < NF_INET_NUMHOOKS && table->chains[i].name != NULL; i++) {
- list = nft_chain_list_get(h, table->name,
- table->chains[i].name);
- if (!list)
+ if (nft_chain_find(h, table->name, table->chains[i].name))
continue;
- c = nftnl_chain_list_lookup_byname(list, table->chains[i].name);
- if (c != NULL)
- continue;
-
- nft_chain_builtin_add(h, table, &table->chains[i]);
+ nft_chain_builtin_add(h, table, &table->chains[i], false);
}
}
-static int nft_xt_builtin_init(struct nft_handle *h, const char *table)
+static const struct builtin_table *
+nft_xt_builtin_table_init(struct nft_handle *h, const char *table)
{
const struct builtin_table *t;
+ if (!h->cache_init)
+ return NULL;
+
t = nft_table_builtin_find(h, table);
if (t == NULL)
- return -1;
+ return NULL;
- if (nft_table_initialized(h, t->type))
+ if (nft_table_builtin_add(h, t) < 0)
+ return NULL;
+
+ return t;
+}
+
+static int nft_xt_builtin_init(struct nft_handle *h, const char *table,
+ const char *chain)
+{
+ const struct builtin_table *t;
+ const struct builtin_chain *c;
+
+ if (!h->cache_init)
return 0;
- if (nft_table_builtin_add(h, t) < 0)
+ t = nft_xt_builtin_table_init(h, table);
+ if (!t)
return -1;
- nft_chain_builtin_init(h, t);
+ if (h->cache_req.level < NFT_CL_CHAINS)
+ return 0;
- h->cache->table[t->type].initialized = true;
+ if (!chain) {
+ nft_chain_builtin_init(h, t);
+ return 0;
+ }
+ c = nft_chain_builtin_find(t, chain);
+ if (!c)
+ return -1;
+
+ if (h->cache->table[t->type].base_chains[c->hook])
+ return 0;
+
+ nft_chain_builtin_add(h, t, c, false);
return 0;
}
@@ -757,6 +834,40 @@ static bool nft_chain_builtin(struct nftnl_chain *c)
return nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM) != NULL;
}
+static int __nft_xt_fake_builtin_chains(struct nft_handle *h,
+ const char *table, void *data)
+{
+ const char *chain = data ? *(const char **)data : NULL;
+ const struct builtin_table *t;
+ struct nft_chain **bcp;
+ int i;
+
+ t = nft_table_builtin_find(h, table);
+ if (!t)
+ return -1;
+
+ bcp = h->cache->table[t->type].base_chains;
+ for (i = 0; i < NF_INET_NUMHOOKS && t->chains[i].name; i++) {
+ if (bcp[t->chains[i].hook])
+ continue;
+
+ if (chain && strcmp(chain, t->chains[i].name))
+ continue;
+
+ nft_chain_builtin_add(h, t, &t->chains[i], true);
+ }
+ return 0;
+}
+
+int nft_xt_fake_builtin_chains(struct nft_handle *h,
+ const char *table, const char *chain)
+{
+ if (table)
+ return __nft_xt_fake_builtin_chains(h, table, &chain);
+
+ return nft_for_each_table(h, __nft_xt_fake_builtin_chains, &chain);
+}
+
int nft_restart(struct nft_handle *h)
{
mnl_socket_close(h->nl);
@@ -775,8 +886,25 @@ int nft_restart(struct nft_handle *h)
return 0;
}
-int nft_init(struct nft_handle *h, const struct builtin_table *t)
+static const struct builtin_table *builtin_tables_lookup(int family)
{
+ switch (family) {
+ case AF_INET:
+ case AF_INET6:
+ return xtables_ipv4;
+ case NFPROTO_ARP:
+ return xtables_arp;
+ case NFPROTO_BRIDGE:
+ return xtables_bridge;
+ default:
+ return NULL;
+ }
+}
+
+int nft_init(struct nft_handle *h, int family)
+{
+ memset(h, 0, sizeof(*h));
+
h->nl = mnl_socket_open(NETLINK_NETFILTER);
if (h->nl == NULL)
return -1;
@@ -786,31 +914,50 @@ int nft_init(struct nft_handle *h, const struct builtin_table *t)
return -1;
}
+ h->ops = nft_family_ops_lookup(family);
+ if (!h->ops)
+ xtables_error(PARAMETER_PROBLEM, "Unknown family");
+
h->portid = mnl_socket_get_portid(h->nl);
- h->tables = t;
+ h->tables = builtin_tables_lookup(family);
h->cache = &h->__cache[0];
+ h->family = family;
INIT_LIST_HEAD(&h->obj_list);
INIT_LIST_HEAD(&h->err_list);
+ INIT_LIST_HEAD(&h->cmd_list);
+ INIT_LIST_HEAD(&h->cache_req.chain_list);
return 0;
}
void nft_fini(struct nft_handle *h)
{
- flush_chain_cache(h, NULL);
+ struct list_head *pos, *n;
+
+ list_for_each_safe(pos, n, &h->cmd_list)
+ nft_cmd_free(list_entry(pos, struct nft_cmd, head));
+
+ list_for_each_safe(pos, n, &h->obj_list)
+ batch_obj_del(h, list_entry(pos, struct obj_update, head));
+
+ list_for_each_safe(pos, n, &h->err_list)
+ mnl_err_list_free(list_entry(pos, struct mnl_err, head));
+
+ nft_release_cache(h);
mnl_socket_close(h->nl);
}
-static void nft_chain_print_debug(struct nftnl_chain *c, struct nlmsghdr *nlh)
+static void nft_chain_print_debug(struct nft_handle *h,
+ struct nftnl_chain *c, struct nlmsghdr *nlh)
{
-#ifdef NLDEBUG
- char tmp[1024];
-
- nftnl_chain_snprintf(tmp, sizeof(tmp), c, 0, 0);
- printf("DEBUG: chain: %s\n", tmp);
- mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg));
-#endif
+ if (h->verbose > 1) {
+ nftnl_chain_fprintf(stdout, c, 0, 0);
+ fprintf(stdout, "\n");
+ }
+ if (h->verbose > 2)
+ mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len,
+ sizeof(struct nfgenmsg));
}
static struct nftnl_chain *nft_chain_new(struct nft_handle *h,
@@ -818,6 +965,7 @@ static struct nftnl_chain *nft_chain_new(struct nft_handle *h,
int policy,
const struct xt_counters *counters)
{
+ static const struct xt_counters zero = {};
struct nftnl_chain *c;
const struct builtin_table *_t;
const struct builtin_chain *_c;
@@ -829,12 +977,12 @@ static struct nftnl_chain *nft_chain_new(struct nft_handle *h,
}
/* if this built-in table does not exists, create it */
- nft_table_builtin_add(h, _t);
+ nft_xt_builtin_init(h, table, chain);
_c = nft_chain_builtin_find(_t, chain);
if (_c != NULL) {
/* This is a built-in chain */
- c = nft_chain_builtin_alloc(_t, _c, policy);
+ c = nft_chain_builtin_alloc(h->family, _t->name, _c, policy);
if (c == NULL)
return NULL;
} else {
@@ -842,12 +990,10 @@ static struct nftnl_chain *nft_chain_new(struct nft_handle *h,
return NULL;
}
- if (counters) {
- nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES,
- counters->bcnt);
- nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS,
- counters->pcnt);
- }
+ if (!counters)
+ counters = &zero;
+ nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES, counters->bcnt);
+ nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS, counters->pcnt);
return c;
}
@@ -857,7 +1003,6 @@ int nft_chain_set(struct nft_handle *h, const char *table,
const struct xt_counters *counters)
{
struct nftnl_chain *c = NULL;
- int ret;
nft_fn = nft_chain_set;
@@ -865,16 +1010,19 @@ int nft_chain_set(struct nft_handle *h, const char *table,
c = nft_chain_new(h, table, chain, NF_DROP, counters);
else if (strcmp(policy, "ACCEPT") == 0)
c = nft_chain_new(h, table, chain, NF_ACCEPT, counters);
+ else if (strcmp(policy, "-") == 0)
+ c = nft_chain_new(h, table, chain, -1, counters);
else
errno = EINVAL;
if (c == NULL)
return 0;
- ret = batch_chain_add(h, NFT_COMPAT_CHAIN_UPDATE, c);
+ if (!batch_chain_add(h, NFT_COMPAT_CHAIN_UPDATE, c))
+ return 0;
/* the core expects 1 for success and 0 for error */
- return ret == 0 ? 1 : 0;
+ return 1;
}
static int __add_match(struct nftnl_expr *e, struct xt_entry_match *m)
@@ -884,10 +1032,7 @@ static int __add_match(struct nftnl_expr *e, struct xt_entry_match *m)
nftnl_expr_set(e, NFTNL_EXPR_MT_NAME, m->u.user.name, strlen(m->u.user.name));
nftnl_expr_set_u32(e, NFTNL_EXPR_MT_REV, m->u.user.revision);
- info = calloc(1, m->u.match_size);
- if (info == NULL)
- return -ENOMEM;
-
+ info = xtables_calloc(1, m->u.match_size);
memcpy(info, m->data, m->u.match_size - sizeof(*m));
nftnl_expr_set(e, NFTNL_EXPR_MT_INFO, info, m->u.match_size - sizeof(*m));
@@ -930,13 +1075,420 @@ static int add_nft_limit(struct nftnl_rule *r, struct xt_entry_match *m)
return 0;
}
-int add_match(struct nftnl_rule *r, struct xt_entry_match *m)
+static struct nftnl_set *add_anon_set(struct nft_handle *h, const char *table,
+ uint32_t flags, uint32_t key_type,
+ uint32_t key_len, uint32_t size)
+{
+ static uint32_t set_id = 0;
+ struct nftnl_set *s;
+ struct nft_cmd *cmd;
+
+ s = nftnl_set_alloc();
+ if (!s)
+ return NULL;
+
+ nftnl_set_set_u32(s, NFTNL_SET_FAMILY, h->family);
+ nftnl_set_set_str(s, NFTNL_SET_TABLE, table);
+ nftnl_set_set_str(s, NFTNL_SET_NAME, "__set%d");
+ nftnl_set_set_u32(s, NFTNL_SET_ID, ++set_id);
+ nftnl_set_set_u32(s, NFTNL_SET_FLAGS,
+ NFT_SET_ANONYMOUS | NFT_SET_CONSTANT | flags);
+ nftnl_set_set_u32(s, NFTNL_SET_KEY_TYPE, key_type);
+ nftnl_set_set_u32(s, NFTNL_SET_KEY_LEN, key_len);
+ nftnl_set_set_u32(s, NFTNL_SET_DESC_SIZE, size);
+
+ cmd = nft_cmd_new(h, NFT_COMPAT_SET_ADD, table, NULL, NULL, -1, false);
+ if (!cmd) {
+ nftnl_set_free(s);
+ return NULL;
+ }
+ cmd->obj.set = s;
+
+ return s;
+}
+
+static struct nftnl_expr *
+__gen_payload(uint32_t base, uint32_t offset, uint32_t len, uint8_t reg)
+{
+ struct nftnl_expr *e = nftnl_expr_alloc("payload");
+
+ if (!e)
+ return NULL;
+
+ nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_BASE, base);
+ nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET, offset);
+ nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_LEN, len);
+ nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_DREG, reg);
+
+ return e;
+}
+
+static struct nftnl_expr *
+gen_payload(struct nft_handle *h, uint32_t base, uint32_t offset, uint32_t len,
+ uint8_t *dreg)
+{
+ struct nftnl_expr *e;
+ uint8_t reg;
+
+ reg = NFT_REG_1;
+ e = __gen_payload(base, offset, len, reg);
+ *dreg = reg;
+
+ return e;
+}
+
+static struct nftnl_expr *
+gen_lookup(uint32_t sreg, const char *set_name, uint32_t set_id, uint32_t flags)
+{
+ struct nftnl_expr *e = nftnl_expr_alloc("lookup");
+
+ if (!e)
+ return NULL;
+ nftnl_expr_set_u32(e, NFTNL_EXPR_LOOKUP_SREG, sreg);
+ nftnl_expr_set_str(e, NFTNL_EXPR_LOOKUP_SET, set_name);
+ nftnl_expr_set_u32(e, NFTNL_EXPR_LOOKUP_SET_ID, set_id);
+ nftnl_expr_set_u32(e, NFTNL_EXPR_LOOKUP_FLAGS, flags);
+ return e;
+}
+
+/* from nftables:include/datatype.h, TYPE_BITS */
+#define CONCAT_TYPE_BITS 6
+
+/* from nftables:include/datatype.h, enum datatypes */
+#define NFT_DATATYPE_IPADDR 7
+#define NFT_DATATYPE_ETHERADDR 9
+
+static int __add_nft_among(struct nft_handle *h, const char *table,
+ struct nftnl_rule *r, struct nft_among_pair *pairs,
+ int cnt, bool dst, bool inv, bool ip)
+{
+ uint32_t set_id, type = NFT_DATATYPE_ETHERADDR, len = ETH_ALEN;
+ /* { !dst, dst } */
+ static const int eth_addr_off[] = {
+ offsetof(struct ether_header, ether_shost),
+ offsetof(struct ether_header, ether_dhost)
+ };
+ static const int ip_addr_off[] = {
+ offsetof(struct iphdr, saddr),
+ offsetof(struct iphdr, daddr)
+ };
+ struct nftnl_expr *e;
+ struct nftnl_set *s;
+ uint32_t flags = 0;
+ uint8_t reg;
+ int idx = 0;
+
+ if (ip) {
+ type = type << CONCAT_TYPE_BITS | NFT_DATATYPE_IPADDR;
+ len += sizeof(struct in_addr) + NETLINK_ALIGN - 1;
+ len &= ~(NETLINK_ALIGN - 1);
+ flags = NFT_SET_INTERVAL | NFT_SET_CONCAT;
+ }
+
+ s = add_anon_set(h, table, flags, type, len, cnt);
+ if (!s)
+ return -ENOMEM;
+ set_id = nftnl_set_get_u32(s, NFTNL_SET_ID);
+
+ if (ip) {
+ uint8_t field_len[2] = { ETH_ALEN, sizeof(struct in_addr) };
+
+ nftnl_set_set_data(s, NFTNL_SET_DESC_CONCAT,
+ field_len, sizeof(field_len));
+ }
+
+ for (idx = 0; idx < cnt; idx++) {
+ struct nftnl_set_elem *elem = nftnl_set_elem_alloc();
+
+ if (!elem)
+ return -ENOMEM;
+ nftnl_set_elem_set(elem, NFTNL_SET_ELEM_KEY,
+ &pairs[idx], len);
+ if (ip) {
+ struct in_addr tmp = pairs[idx].in;
+
+ if (tmp.s_addr == INADDR_ANY)
+ pairs[idx].in.s_addr = INADDR_BROADCAST;
+ nftnl_set_elem_set(elem, NFTNL_SET_ELEM_KEY_END,
+ &pairs[idx], len);
+ pairs[idx].in = tmp;
+ }
+ nftnl_set_elem_add(s, elem);
+ }
+
+ e = gen_payload(h, NFT_PAYLOAD_LL_HEADER,
+ eth_addr_off[dst], ETH_ALEN, &reg);
+ if (!e)
+ return -ENOMEM;
+ nftnl_rule_add_expr(r, e);
+
+ if (ip) {
+ reg = nft_get_next_reg(reg, ETH_ALEN);
+ e = __gen_payload(NFT_PAYLOAD_NETWORK_HEADER, ip_addr_off[dst],
+ sizeof(struct in_addr), reg);
+ if (!e)
+ return -ENOMEM;
+ nftnl_rule_add_expr(r, e);
+ }
+
+ reg = NFT_REG_1;
+ e = gen_lookup(reg, "__set%d", set_id, inv);
+ if (!e)
+ return -ENOMEM;
+ nftnl_rule_add_expr(r, e);
+
+ return 0;
+}
+
+static int add_nft_among(struct nft_handle *h,
+ struct nftnl_rule *r, struct xt_entry_match *m)
+{
+ struct nft_among_data *data = (struct nft_among_data *)m->data;
+ const char *table = nftnl_rule_get(r, NFTNL_RULE_TABLE);
+
+ if ((data->src.cnt && data->src.ip) ||
+ (data->dst.cnt && data->dst.ip)) {
+ uint16_t eth_p_ip = htons(ETH_P_IP);
+ uint8_t reg;
+
+ add_meta(h, r, NFT_META_PROTOCOL, &reg);
+ add_cmp_ptr(r, NFT_CMP_EQ, &eth_p_ip, 2, reg);
+ }
+
+ if (data->src.cnt)
+ __add_nft_among(h, table, r, data->pairs, data->src.cnt,
+ false, data->src.inv, data->src.ip);
+ if (data->dst.cnt)
+ __add_nft_among(h, table, r, data->pairs + data->src.cnt,
+ data->dst.cnt, true, data->dst.inv,
+ data->dst.ip);
+ return 0;
+}
+
+static int expr_gen_range_cmp16(struct nftnl_rule *r,
+ uint16_t lo,
+ uint16_t hi,
+ bool invert, uint8_t reg)
+{
+ struct nftnl_expr *e;
+
+ if (lo == hi) {
+ add_cmp_u16(r, htons(lo), invert ? NFT_CMP_NEQ : NFT_CMP_EQ, reg);
+ return 0;
+ }
+
+ if (lo == 0 && hi < 0xffff) {
+ add_cmp_u16(r, htons(hi) , invert ? NFT_CMP_GT : NFT_CMP_LTE, reg);
+ return 0;
+ }
+
+ e = nftnl_expr_alloc("range");
+ if (!e)
+ return -ENOMEM;
+
+ nftnl_expr_set_u32(e, NFTNL_EXPR_RANGE_SREG, reg);
+ nftnl_expr_set_u32(e, NFTNL_EXPR_RANGE_OP, invert ? NFT_RANGE_NEQ : NFT_RANGE_EQ);
+
+ lo = htons(lo);
+ nftnl_expr_set(e, NFTNL_EXPR_RANGE_FROM_DATA, &lo, sizeof(lo));
+ hi = htons(hi);
+ nftnl_expr_set(e, NFTNL_EXPR_RANGE_TO_DATA, &hi, sizeof(hi));
+
+ nftnl_rule_add_expr(r, e);
+ return 0;
+}
+
+static int add_nft_tcpudp(struct nft_handle *h,struct nftnl_rule *r,
+ uint16_t src[2], bool invert_src,
+ uint16_t dst[2], bool invert_dst)
{
struct nftnl_expr *expr;
+ uint8_t op = NFT_CMP_EQ;
+ uint8_t reg;
int ret;
- if (!strcmp(m->u.user.name, "limit"))
- return add_nft_limit(r, m);
+ if (!invert_src &&
+ src[0] && src[0] == src[1] &&
+ dst[0] && dst[0] == dst[1] &&
+ invert_src == invert_dst) {
+ uint32_t combined = dst[0] | (src[0] << 16);
+
+ expr = gen_payload(h, NFT_PAYLOAD_TRANSPORT_HEADER, 0, 4, &reg);
+ if (!expr)
+ return -ENOMEM;
+
+ nftnl_rule_add_expr(r, expr);
+ add_cmp_u32(r, htonl(combined), op, reg);
+ return 0;
+ }
+
+ if (src[0] || src[1] < UINT16_MAX || invert_src) {
+ expr = gen_payload(h, NFT_PAYLOAD_TRANSPORT_HEADER, 0, 2, &reg);
+ if (!expr)
+ return -ENOMEM;
+
+ nftnl_rule_add_expr(r, expr);
+ ret = expr_gen_range_cmp16(r, src[0], src[1], invert_src, reg);
+ if (ret)
+ return ret;
+ }
+
+ if (dst[0] || dst[1] < UINT16_MAX || invert_dst) {
+ expr = gen_payload(h, NFT_PAYLOAD_TRANSPORT_HEADER, 2, 2, &reg);
+ if (!expr)
+ return -ENOMEM;
+
+ nftnl_rule_add_expr(r, expr);
+ ret = expr_gen_range_cmp16(r, dst[0], dst[1], invert_dst, reg);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* without this, "iptables -A INPUT -m udp" is
+ * turned into "iptables -A INPUT", which isn't
+ * compatible with iptables-legacy behaviour.
+ */
+static bool udp_all_zero(const struct xt_udp *u)
+{
+ static const struct xt_udp zero = {
+ .spts[1] = 0xffff,
+ .dpts[1] = 0xffff,
+ };
+
+ return memcmp(u, &zero, sizeof(*u)) == 0;
+}
+
+static int add_nft_udp(struct nft_handle *h, struct nftnl_rule *r,
+ struct xt_entry_match *m)
+{
+ struct xt_udp *udp = (void *)m->data;
+
+ if (udp->invflags > XT_UDP_INV_MASK ||
+ udp_all_zero(udp)) {
+ struct nftnl_expr *expr = nftnl_expr_alloc("match");
+ int ret;
+
+ ret = __add_match(expr, m);
+ nftnl_rule_add_expr(r, expr);
+ return ret;
+ }
+
+ 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);
+}
+
+static int add_nft_tcpflags(struct nft_handle *h, struct nftnl_rule *r,
+ uint8_t cmp, uint8_t mask,
+ bool invert)
+{
+ struct nftnl_expr *e;
+ uint8_t reg;
+
+ e = gen_payload(h, NFT_PAYLOAD_TRANSPORT_HEADER, 13, 1, &reg);
+
+ if (!e)
+ return -ENOMEM;
+
+ nftnl_rule_add_expr(r, e);
+
+ add_bitwise(h, r, &mask, 1, reg, &reg);
+ add_cmp_u8(r, cmp, invert ? NFT_CMP_NEQ : NFT_CMP_EQ, reg);
+
+ return 0;
+}
+
+static bool tcp_all_zero(const struct xt_tcp *t)
+{
+ static const struct xt_tcp zero = {
+ .spts[1] = 0xffff,
+ .dpts[1] = 0xffff,
+ };
+
+ return memcmp(t, &zero, sizeof(*t)) == 0;
+}
+
+static int add_nft_tcp(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;
+ struct xt_tcp *tcp = (void *)m->data;
+
+ if (tcp->invflags & ~supported || tcp->option ||
+ tcp_all_zero(tcp)) {
+ struct nftnl_expr *expr = nftnl_expr_alloc("match");
+ int ret;
+
+ ret = __add_match(expr, m);
+ nftnl_rule_add_expr(r, expr);
+ return ret;
+ }
+
+ if (nftnl_rule_get_u32(r, NFTNL_RULE_COMPAT_PROTO) != IPPROTO_TCP)
+ 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);
+
+ 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);
+}
+
+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;
+ uint8_t reg;
+ int op;
+
+ add_meta(h, r, NFT_META_MARK, &reg);
+ if (mark->mask != 0xffffffff)
+ add_bitwise(h, r, (uint8_t *)&mark->mask, sizeof(uint32_t), reg, &reg);
+
+ if (mark->invert)
+ op = NFT_CMP_NEQ;
+ else
+ op = NFT_CMP_EQ;
+
+ add_cmp_u32(r, mark->mark, op, reg);
+
+ return 0;
+}
+
+int add_match(struct nft_handle *h, struct nft_rule_ctx *ctx,
+ struct nftnl_rule *r, struct xt_entry_match *m)
+{
+ struct nftnl_expr *expr;
+ int ret;
+
+ switch (ctx->command) {
+ case NFT_COMPAT_RULE_APPEND:
+ case NFT_COMPAT_RULE_INSERT:
+ case NFT_COMPAT_RULE_REPLACE:
+ if (!strcmp(m->u.user.name, "limit"))
+ return add_nft_limit(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"))
+ return add_nft_udp(h, r, m);
+ else if (!strcmp(m->u.user.name, "tcp"))
+ return add_nft_tcp(h, r, m);
+ else if (!strcmp(m->u.user.name, "mark"))
+ return add_nft_mark(h, r, m);
+ break;
+ default:
+ break;
+ }
expr = nftnl_expr_alloc("match");
if (expr == NULL)
@@ -956,10 +1508,7 @@ static int __add_target(struct nftnl_expr *e, struct xt_entry_target *t)
strlen(t->u.user.name));
nftnl_expr_set_u32(e, NFTNL_EXPR_TG_REV, t->u.user.revision);
- info = calloc(1, t->u.target_size);
- if (info == NULL)
- return -ENOMEM;
-
+ info = xtables_calloc(1, t->u.target_size);
memcpy(info, t->data, t->u.target_size - sizeof(*t));
nftnl_expr_set(e, NFTNL_EXPR_TG_INFO, info, t->u.target_size - sizeof(*t));
@@ -1040,38 +1589,66 @@ int add_verdict(struct nftnl_rule *r, int verdict)
int add_action(struct nftnl_rule *r, struct iptables_command_state *cs,
bool goto_set)
{
- int ret = 0;
-
- /* If no target at all, add nothing (default to continue) */
- if (cs->target != NULL) {
- /* Standard target? */
- if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0)
- ret = add_verdict(r, NF_ACCEPT);
- else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0)
- ret = add_verdict(r, NF_DROP);
- else if (strcmp(cs->jumpto, XTC_LABEL_RETURN) == 0)
- ret = add_verdict(r, NFT_RETURN);
- else
- ret = add_target(r, cs->target->t);
- } else if (strlen(cs->jumpto) > 0) {
- /* Not standard, then it's a go / jump to chain */
- if (goto_set)
- ret = add_jumpto(r, cs->jumpto, NFT_GOTO);
- else
- ret = add_jumpto(r, cs->jumpto, NFT_JUMP);
- }
- return ret;
-}
-
-static void nft_rule_print_debug(struct nftnl_rule *r, struct nlmsghdr *nlh)
-{
-#ifdef NLDEBUG
- char tmp[1024];
-
- nftnl_rule_snprintf(tmp, sizeof(tmp), r, 0, 0);
- printf("DEBUG: rule: %s\n", tmp);
- mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct nfgenmsg));
-#endif
+ int ret = 0;
+
+ /* If no target at all, add nothing (default to continue) */
+ if (cs->target != NULL) {
+ /* Standard target? */
+ if (strcmp(cs->jumpto, XTC_LABEL_ACCEPT) == 0)
+ ret = add_verdict(r, NF_ACCEPT);
+ else if (strcmp(cs->jumpto, XTC_LABEL_DROP) == 0)
+ ret = add_verdict(r, NF_DROP);
+ 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);
+ else
+ ret = add_target(r, cs->target->t);
+ } else if (strlen(cs->jumpto) > 0) {
+ /* Not standard, then it's a go / jump to chain */
+ if (goto_set)
+ ret = add_jumpto(r, cs->jumpto, NFT_GOTO);
+ else
+ ret = add_jumpto(r, cs->jumpto, NFT_JUMP);
+ }
+ return ret;
+}
+
+int add_log(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;
+
+ expr = nftnl_expr_alloc("log");
+ if (!expr)
+ return -ENOMEM;
+
+ if (info->prefix[0] != '\0')
+ nftnl_expr_set_str(expr, NFTNL_EXPR_LOG_PREFIX,
+ cs->target->udata);
+
+ nftnl_expr_set_u16(expr, NFTNL_EXPR_LOG_GROUP, info->group);
+ if (info->flags & XT_NFLOG_F_COPY_LEN)
+ nftnl_expr_set_u32(expr, NFTNL_EXPR_LOG_SNAPLEN,
+ info->len);
+ if (info->threshold)
+ nftnl_expr_set_u16(expr, NFTNL_EXPR_LOG_QTHRESHOLD,
+ info->threshold);
+
+ nftnl_rule_add_expr(r, expr);
+ return 0;
+}
+
+static void nft_rule_print_debug(struct nft_handle *h,
+ struct nftnl_rule *r, struct nlmsghdr *nlh)
+{
+ if (h->verbose > 1) {
+ nftnl_rule_fprintf(stdout, r, 0, 0);
+ fprintf(stdout, "\n");
+ }
+ if (h->verbose > 2)
+ mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len,
+ sizeof(struct nfgenmsg));
}
int add_counters(struct nftnl_rule *r, uint64_t packets, uint64_t bytes)
@@ -1138,9 +1715,10 @@ void add_compat(struct nftnl_rule *r, uint32_t proto, bool inv)
inv ? NFT_RULE_COMPAT_F_INV : 0);
}
-static struct nftnl_rule *
-nft_rule_new(struct nft_handle *h, const char *chain, const char *table,
- void *data)
+struct nftnl_rule *
+nft_rule_new(struct nft_handle *h, struct nft_rule_ctx *ctx,
+ const char *chain, const char *table,
+ struct iptables_command_state *cs)
{
struct nftnl_rule *r;
@@ -1152,7 +1730,7 @@ nft_rule_new(struct nft_handle *h, const char *chain, const char *table,
nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table);
nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain);
- if (h->ops->add(r, data) < 0)
+ if (h->ops->add(h, ctx, r, cs) < 0)
goto err;
return r;
@@ -1161,33 +1739,17 @@ err:
return NULL;
}
-static struct nftnl_chain *
-nft_chain_find(struct nft_handle *h, const char *table, const char *chain);
-
int
nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
- void *data, struct nftnl_rule *ref, bool verbose)
+ struct nftnl_rule *r, struct nftnl_rule *ref, bool verbose)
{
- struct nftnl_chain *c;
- struct nftnl_rule *r;
+ struct nft_chain *c;
int type;
- nft_xt_builtin_init(h, table);
-
- /* Since ebtables user-defined chain policies are implemented as last
- * rule in nftables, rule cache is required here to treat them right. */
- if (h->family == NFPROTO_BRIDGE) {
- c = nft_chain_find(h, table, chain);
- if (c && !nft_chain_builtin(c))
- nft_build_cache(h, c);
- }
+ nft_xt_builtin_init(h, table, chain);
nft_fn = nft_rule_append;
- r = nft_rule_new(h, chain, table, data);
- if (r == NULL)
- return 0;
-
if (ref) {
nftnl_rule_set_u64(r, NFTNL_RULE_HANDLE,
nftnl_rule_get_u64(ref, NFTNL_RULE_HANDLE));
@@ -1195,51 +1757,50 @@ nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
} else
type = NFT_COMPAT_RULE_APPEND;
- if (batch_rule_add(h, type, r) == NULL) {
- nftnl_rule_free(r);
+ if (batch_rule_add(h, type, r) == NULL)
return 0;
- }
if (verbose)
- h->ops->print_rule(r, 0, FMT_PRINT_RULE);
+ h->ops->print_rule(h, r, 0, FMT_PRINT_RULE);
if (ref) {
nftnl_chain_rule_insert_at(r, ref);
- nftnl_chain_rule_del(r);
+ nftnl_chain_rule_del(ref);
+ nftnl_rule_free(ref);
} else {
c = nft_chain_find(h, table, chain);
if (!c) {
errno = ENOENT;
return 0;
}
- nftnl_chain_rule_add_tail(r, c);
+ nftnl_chain_rule_add_tail(r, c->nftnl);
}
return 1;
}
-void
-nft_rule_print_save(const struct nftnl_rule *r, enum nft_rule_print type,
- unsigned int format)
+bool
+nft_rule_print_save(struct nft_handle *h, const struct nftnl_rule *r,
+ enum nft_rule_print type, unsigned int format)
{
const char *chain = nftnl_rule_get_str(r, NFTNL_RULE_CHAIN);
- int family = nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY);
struct iptables_command_state cs = {};
- struct nft_family_ops *ops;
+ struct nft_family_ops *ops = h->ops;
+ bool ret;
- ops = nft_family_ops_lookup(family);
- ops->rule_to_cs(r, &cs);
+ ret = ops->rule_to_cs(h, r, &cs);
- if (!(format & (FMT_NOCOUNTS | FMT_C_COUNTS)) && ops->save_counters)
- ops->save_counters(&cs);
+ if (!(format & (FMT_NOCOUNTS | FMT_C_COUNTS)))
+ printf("[%llu:%llu] ", (unsigned long long)cs.counters.pcnt,
+ (unsigned long long)cs.counters.bcnt);
/* print chain name */
switch(type) {
case NFT_RULE_APPEND:
- printf("-A %s ", chain);
+ printf("-A %s", chain);
break;
case NFT_RULE_DEL:
- printf("-D %s ", chain);
+ printf("-D %s", chain);
break;
}
@@ -1248,6 +1809,8 @@ nft_rule_print_save(const struct nftnl_rule *r, enum nft_rule_print type,
if (ops->clear_cs)
ops->clear_cs(&cs);
+
+ return ret;
}
static bool nft_rule_is_policy_rule(struct nftnl_rule *r)
@@ -1335,63 +1898,49 @@ static const char *policy_name[NF_ACCEPT+1] = {
[NF_ACCEPT] = "ACCEPT",
};
-int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list)
+int nft_chain_save(struct nft_chain *nc, void *data)
{
- struct nftnl_chain_list_iter *iter;
- struct nft_family_ops *ops;
- struct nftnl_chain *c;
-
- ops = nft_family_ops_lookup(h->family);
-
- iter = nftnl_chain_list_iter_create(list);
- if (iter == NULL)
- return 0;
-
- c = nftnl_chain_list_iter_next(iter);
- while (c != NULL) {
- const char *policy = NULL;
-
- if (nft_chain_builtin(c)) {
- uint32_t pol = NF_ACCEPT;
-
- if (nftnl_chain_get(c, NFTNL_CHAIN_POLICY))
- pol = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
- policy = policy_name[pol];
- } else if (h->family == NFPROTO_BRIDGE) {
- if (nftnl_chain_is_set(c, NFTNL_CHAIN_POLICY)) {
- uint32_t pol;
-
- pol = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
- policy = policy_name[pol];
- } else {
- policy = "RETURN";
- }
- }
-
- if (ops->save_chain)
- ops->save_chain(c, policy);
+ struct nftnl_chain *c = nc->nftnl;
+ struct nft_handle *h = data;
+ const char *policy = NULL;
- c = nftnl_chain_list_iter_next(iter);
+ if (nftnl_chain_is_set(c, NFTNL_CHAIN_POLICY)) {
+ policy = policy_name[nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY)];
+ } else if (nft_chain_builtin(c)) {
+ policy = "ACCEPT";
+ } else if (h->family == NFPROTO_BRIDGE) {
+ policy = "RETURN";
}
- nftnl_chain_list_iter_destroy(iter);
+ if (h->ops->save_chain)
+ h->ops->save_chain(c, policy);
- return 1;
+ return 0;
}
-static int nft_chain_save_rules(struct nft_handle *h,
- struct nftnl_chain *c, unsigned int format)
+struct nft_rule_save_data {
+ struct nft_handle *h;
+ unsigned int format;
+ unsigned int errors;
+};
+
+static int nft_rule_save_cb(struct nft_chain *c, void *data)
{
+ struct nft_rule_save_data *d = data;
struct nftnl_rule_iter *iter;
struct nftnl_rule *r;
- iter = nftnl_rule_iter_create(c);
+ iter = nftnl_rule_iter_create(c->nftnl);
if (iter == NULL)
return 1;
r = nftnl_rule_iter_next(iter);
while (r != NULL) {
- nft_rule_print_save(r, NFT_RULE_APPEND, format);
+ bool ret = nft_rule_print_save(d->h, r, NFT_RULE_APPEND, d->format);
+
+ if (!ret)
+ d->errors++;
+
r = nftnl_rule_iter_next(iter);
}
@@ -1401,38 +1950,38 @@ static int nft_chain_save_rules(struct nft_handle *h,
int nft_rule_save(struct nft_handle *h, const char *table, unsigned int format)
{
- struct nftnl_chain_list_iter *iter;
- struct nftnl_chain_list *list;
- struct nftnl_chain *c;
- int ret = 0;
+ struct nft_rule_save_data d = {
+ .h = h,
+ .format = format,
+ };
+ int ret;
- list = nft_chain_list_get(h, table, NULL);
- if (!list)
- return 0;
+ ret = nft_chain_foreach(h, table, nft_rule_save_cb, &d);
- iter = nftnl_chain_list_iter_create(list);
- if (!iter)
- return 0;
+ if (ret == 0 && d.errors)
+ xtables_error(VERSION_PROBLEM, "Cannot decode all rules provided by kernel");
- c = nftnl_chain_list_iter_next(iter);
- while (c) {
- nft_build_cache(h, c);
- ret = nft_chain_save_rules(h, c, format);
- if (ret != 0)
- break;
+ /* the core expects 1 for success and 0 for error */
+ return ret == 0 ? 1 : 0;
+}
- c = nftnl_chain_list_iter_next(iter);
- }
+struct nftnl_set *nft_set_batch_lookup_byid(struct nft_handle *h,
+ uint32_t set_id)
+{
+ struct obj_update *n;
- nftnl_chain_list_iter_destroy(iter);
+ list_for_each_entry(n, &h->obj_list, head) {
+ if (n->type == NFT_COMPAT_SET_ADD &&
+ nftnl_set_get_u32(n->set, NFTNL_SET_ID) == set_id)
+ return n->set;
+ }
- /* the core expects 1 for success and 0 for error */
- return ret == 0 ? 1 : 0;
+ return NULL;
}
static void
__nft_rule_flush(struct nft_handle *h, const char *table,
- const char *chain, bool verbose, bool implicit)
+ const char *chain, bool verbose, bool skip)
{
struct obj_update *obj;
struct nftnl_rule *r;
@@ -1454,31 +2003,46 @@ __nft_rule_flush(struct nft_handle *h, const char *table,
return;
}
- obj->implicit = implicit;
+ obj->skip = skip;
+}
+
+struct nft_rule_flush_data {
+ struct nft_handle *h;
+ const char *table;
+ bool verbose;
+};
+
+static int nft_rule_flush_cb(struct nft_chain *c, void *data)
+{
+ const char *chain = nftnl_chain_get_str(c->nftnl, NFTNL_CHAIN_NAME);
+ struct nft_rule_flush_data *d = data;
+
+ batch_chain_flush(d->h, d->table, chain);
+ __nft_rule_flush(d->h, d->table, chain, d->verbose, false);
+ flush_rule_cache(d->h, d->table, c);
+ return 0;
}
int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table,
bool verbose)
{
- struct nftnl_chain_list_iter *iter;
- struct nftnl_chain_list *list;
- struct nftnl_chain *c = NULL;
+ struct nft_rule_flush_data d = {
+ .h = h,
+ .table = table,
+ .verbose = verbose,
+ };
+ struct nft_chain *c = NULL;
int ret = 0;
- nft_xt_builtin_init(h, table);
-
nft_fn = nft_rule_flush;
- if (chain || verbose) {
- list = nft_chain_list_get(h, table, chain);
- if (list == NULL) {
- ret = 1;
- goto err;
- }
- }
+ if (chain || verbose)
+ nft_xt_builtin_init(h, table, chain);
+ else if (!nft_table_find(h, table))
+ return 1;
if (chain) {
- c = nftnl_chain_list_lookup_byname(list, chain);
+ c = nft_chain_find(h, table, chain);
if (!c) {
errno = ENOENT;
return 0;
@@ -1486,40 +2050,28 @@ int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table,
}
if (chain || !verbose) {
+ batch_chain_flush(h, table, chain);
__nft_rule_flush(h, table, chain, verbose, false);
flush_rule_cache(h, table, c);
return 1;
}
- iter = nftnl_chain_list_iter_create(list);
- if (iter == NULL) {
- ret = 1;
- goto err;
- }
+ nft_cache_sort_chains(h, table);
- c = nftnl_chain_list_iter_next(iter);
- while (c != NULL) {
- chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+ ret = nft_chain_foreach(h, table, nft_rule_flush_cb, &d);
- __nft_rule_flush(h, table, chain, verbose, false);
- flush_rule_cache(h, table, c);
- c = nftnl_chain_list_iter_next(iter);
- }
- nftnl_chain_list_iter_destroy(iter);
-err:
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}
int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *table)
{
- struct nftnl_chain_list *list;
+ const struct builtin_table *t;
struct nftnl_chain *c;
- int ret;
nft_fn = nft_chain_user_add;
- nft_xt_builtin_init(h, table);
+ t = nft_xt_builtin_table_init(h, table);
if (nft_chain_exists(h, table, chain)) {
errno = EEXIST;
@@ -1530,58 +2082,64 @@ int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *tabl
if (c == NULL)
return 0;
+ nftnl_chain_set_u32(c, NFTNL_CHAIN_FAMILY, h->family);
nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, table);
nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, chain);
if (h->family == NFPROTO_BRIDGE)
nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, NF_ACCEPT);
- ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c);
+ if (!batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c))
+ return 0;
- list = nft_chain_list_get(h, table, chain);
- if (list)
- nftnl_chain_list_add(c, list);
+ nft_cache_add_chain(h, t, c);
/* the core expects 1 for success and 0 for error */
- return ret == 0 ? 1 : 0;
+ return 1;
}
int nft_chain_restore(struct nft_handle *h, const char *chain, const char *table)
{
- struct nftnl_chain_list *list;
+ const struct builtin_table *t;
+ struct obj_update *obj;
struct nftnl_chain *c;
+ struct nft_chain *nc;
bool created = false;
- int ret;
- c = nft_chain_find(h, table, chain);
- if (c) {
- /* Apparently -n still flushes existing user defined
- * chains that are redefined.
- */
- if (h->noflush)
- __nft_rule_flush(h, table, chain, false, true);
- } else {
+ t = nft_xt_builtin_table_init(h, table);
+
+ nc = nft_chain_find(h, table, chain);
+ if (!nc) {
c = nftnl_chain_alloc();
if (!c)
- return -1;
+ return 0;
+ nftnl_chain_set_u32(c, NFTNL_CHAIN_FAMILY, h->family);
nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, table);
nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, chain);
created = true;
+
+ nft_cache_add_chain(h, t, c);
+ } else {
+ c = nc->nftnl;
+
+ /* If the chain should vanish meanwhile, kernel genid changes
+ * and the transaction is refreshed enabling the chain add
+ * object. With the handle still set, kernel interprets it as a
+ * chain replace job and errors since it is not found anymore.
+ */
+ nftnl_chain_unset(c, NFTNL_CHAIN_HANDLE);
}
- if (h->family == NFPROTO_BRIDGE)
- nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, NF_ACCEPT);
+ __nft_rule_flush(h, table, chain, false, created);
- if (!created)
+ obj = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c);
+ if (!obj)
return 0;
- ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c);
+ obj->skip = !created;
- list = nft_chain_list_get(h, table, chain);
- if (list)
- nftnl_chain_list_add(c, list);
-
- return ret;
+ /* the core expects 1 for success and 0 for error */
+ return 1;
}
/* From linux/netlink.h */
@@ -1589,88 +2147,92 @@ int nft_chain_restore(struct nft_handle *h, const char *chain, const char *table
#define NLM_F_NONREC 0x100 /* Do not delete recursively */
#endif
-struct chain_user_del_data {
+struct chain_del_data {
struct nft_handle *handle;
+ const char *chain;
bool verbose;
- int builtin_err;
};
-static int __nft_chain_user_del(struct nftnl_chain *c, void *data)
+static bool nft_may_delete_chain(struct nftnl_chain *c)
{
- struct chain_user_del_data *d = data;
- struct nft_handle *h = d->handle;
- int ret;
+ if (nftnl_chain_is_set(c, NFTNL_CHAIN_POLICY) &&
+ nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY) != NF_ACCEPT)
+ return false;
- /* don't delete built-in chain */
- if (nft_chain_builtin(c))
- return d->builtin_err;
+ return nftnl_rule_lookup_byindex(c, 0) == NULL;
+}
- if (d->verbose)
+static int __nft_chain_del(struct nft_chain *nc, void *data)
+{
+ struct chain_del_data *d = data;
+ struct nftnl_chain *c = nc->nftnl;
+ struct nft_handle *h = d->handle;
+ bool builtin = nft_chain_builtin(c);
+ struct obj_update *obj;
+ int ret = 0;
+
+ if (d->verbose && !builtin)
fprintf(stdout, "Deleting chain `%s'\n",
nftnl_chain_get_str(c, NFTNL_CHAIN_NAME));
- /* This triggers required policy rule deletion. */
- if (h->family == NFPROTO_BRIDGE)
- nft_build_cache(h, c);
/* XXX This triggers a fast lookup from the kernel. */
nftnl_chain_unset(c, NFTNL_CHAIN_HANDLE);
- ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_DEL, c);
- if (ret)
+ obj = batch_chain_add(h, NFT_COMPAT_CHAIN_DEL, c);
+ if (!obj)
return -1;
- nftnl_chain_list_del(c);
- return 0;
+ if (builtin) {
+ obj->skip = !nft_may_delete_chain(c);
+ if (obj->skip && d->chain) {
+ /* complain if explicitly requested */
+ errno = EBUSY;
+ ret = -1;
+ }
+ *nc->base_slot = NULL;
+ }
+
+ /* nftnl_chain is freed when deleting the batch object */
+ nc->nftnl = NULL;
+
+ nft_chain_list_del(nc);
+ nft_chain_free(nc);
+ return ret;
}
-int nft_chain_user_del(struct nft_handle *h, const char *chain,
- const char *table, bool verbose)
+int nft_chain_del(struct nft_handle *h, const char *chain,
+ const char *table, bool verbose)
{
- struct chain_user_del_data d = {
+ struct chain_del_data d = {
.handle = h,
+ .chain = chain,
.verbose = verbose,
};
- struct nftnl_chain_list *list;
- struct nftnl_chain *c;
+ struct nft_chain *c;
int ret = 0;
- nft_fn = nft_chain_user_del;
-
- list = nft_chain_list_get(h, table, chain);
- if (list == NULL)
- return 0;
+ nft_fn = nft_chain_del;
if (chain) {
- c = nftnl_chain_list_lookup_byname(list, chain);
+ c = nft_chain_find(h, table, chain);
if (!c) {
errno = ENOENT;
return 0;
}
- d.builtin_err = -2;
- ret = __nft_chain_user_del(c, &d);
- if (ret == -2)
- errno = EINVAL;
+
+ ret = __nft_chain_del(c, &d);
goto out;
}
- ret = nftnl_chain_list_foreach(list, __nft_chain_user_del, &d);
+ if (verbose)
+ nft_cache_sort_chains(h, table);
+
+ ret = nft_chain_foreach(h, table, __nft_chain_del, &d);
out:
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}
-static struct nftnl_chain *
-nft_chain_find(struct nft_handle *h, const char *table, const char *chain)
-{
- struct nftnl_chain_list *list;
-
- list = nft_chain_list_get(h, table, chain);
- if (list == NULL)
- return NULL;
-
- return nftnl_chain_list_lookup_byname(list, chain);
-}
-
bool nft_chain_exists(struct nft_handle *h,
const char *table, const char *chain)
{
@@ -1690,8 +2252,8 @@ int nft_chain_user_rename(struct nft_handle *h,const char *chain,
const char *table, const char *newname)
{
struct nftnl_chain *c;
+ struct nft_chain *nc;
uint64_t handle;
- int ret;
nft_fn = nft_chain_user_rename;
@@ -1700,95 +2262,55 @@ int nft_chain_user_rename(struct nft_handle *h,const char *chain,
return 0;
}
- nft_xt_builtin_init(h, table);
-
- /* Config load changed errno. Ensure genuine info for our callers. */
- errno = 0;
-
/* Find the old chain to be renamed */
- c = nft_chain_find(h, table, chain);
- if (c == NULL) {
+ nc = nft_chain_find(h, table, chain);
+ if (nc == NULL) {
errno = ENOENT;
return 0;
}
- handle = nftnl_chain_get_u64(c, NFTNL_CHAIN_HANDLE);
+ handle = nftnl_chain_get_u64(nc->nftnl, NFTNL_CHAIN_HANDLE);
/* Now prepare the new name for the chain */
c = nftnl_chain_alloc();
if (c == NULL)
return 0;
+ nftnl_chain_set_u32(c, NFTNL_CHAIN_FAMILY, h->family);
nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, table);
nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, newname);
nftnl_chain_set_u64(c, NFTNL_CHAIN_HANDLE, handle);
- ret = batch_chain_add(h, NFT_COMPAT_CHAIN_RENAME, c);
+ if (!batch_chain_add(h, NFT_COMPAT_CHAIN_RENAME, c))
+ return 0;
/* the core expects 1 for success and 0 for error */
- return ret == 0 ? 1 : 0;
+ return 1;
}
bool nft_table_find(struct nft_handle *h, const char *tablename)
{
- struct nftnl_table_list_iter *iter;
- struct nftnl_table_list *list;
- struct nftnl_table *t;
- bool ret = false;
-
- list = nftnl_table_list_get(h);
- if (list == NULL)
- goto err;
-
- iter = nftnl_table_list_iter_create(list);
- if (iter == NULL)
- goto err;
-
- t = nftnl_table_list_iter_next(iter);
- while (t != NULL) {
- const char *this_tablename =
- nftnl_table_get(t, NFTNL_TABLE_NAME);
-
- if (strcmp(tablename, this_tablename) == 0) {
- ret = true;
- break;
- }
-
- t = nftnl_table_list_iter_next(iter);
- }
-
- nftnl_table_list_iter_destroy(iter);
+ const struct builtin_table *t;
-err:
- return ret;
+ t = nft_table_builtin_find(h, tablename);
+ return t ? h->cache->table[t->type].exists : false;
}
int nft_for_each_table(struct nft_handle *h,
int (*func)(struct nft_handle *h, const char *tablename, void *data),
void *data)
{
- struct nftnl_table_list *list;
- struct nftnl_table_list_iter *iter;
- struct nftnl_table *t;
-
- list = nftnl_table_list_get(h);
- if (list == NULL)
- return -1;
-
- iter = nftnl_table_list_iter_create(list);
- if (iter == NULL)
- return -1;
+ int i;
- t = nftnl_table_list_iter_next(iter);
- while (t != NULL) {
- const char *tablename =
- nftnl_table_get(t, NFTNL_TABLE_NAME);
+ for (i = 0; i < NFT_TABLE_MAX; i++) {
+ if (h->tables[i].name == NULL)
+ continue;
- func(h, tablename, data);
+ if (!h->cache->table[h->tables[i].type].exists)
+ continue;
- t = nftnl_table_list_iter_next(iter);
+ func(h, h->tables[i].name, data);
}
- nftnl_table_list_iter_destroy(iter);
return 0;
}
@@ -1802,6 +2324,7 @@ static int __nft_table_flush(struct nft_handle *h, const char *table, bool exist
if (t == NULL)
return -1;
+ nftnl_table_set_u32(t, NFTNL_TABLE_FAMILY, h->family);
nftnl_table_set_str(t, NFTNL_TABLE_NAME, table);
obj = batch_table_add(h, NFT_COMPAT_TABLE_FLUSH, t);
@@ -1815,7 +2338,7 @@ static int __nft_table_flush(struct nft_handle *h, const char *table, bool exist
_t = nft_table_builtin_find(h, table);
assert(_t);
- h->cache->table[_t->type].initialized = false;
+ h->cache->table[_t->type].exists = false;
flush_chain_cache(h, table);
@@ -1824,59 +2347,29 @@ static int __nft_table_flush(struct nft_handle *h, const char *table, bool exist
int nft_table_flush(struct nft_handle *h, const char *table)
{
- struct nftnl_table_list_iter *iter;
- struct nftnl_table_list *list;
- struct nftnl_table *t;
- bool exists = false;
+ const struct builtin_table *t;
int ret = 0;
nft_fn = nft_table_flush;
- list = nftnl_table_list_get(h);
- if (list == NULL) {
- ret = -1;
- goto err_out;
- }
-
- iter = nftnl_table_list_iter_create(list);
- if (iter == NULL) {
- ret = -1;
- goto err_table_list;
- }
-
- t = nftnl_table_list_iter_next(iter);
- while (t != NULL) {
- const char *table_name =
- nftnl_table_get_str(t, NFTNL_TABLE_NAME);
+ t = nft_table_builtin_find(h, table);
+ if (!t)
+ return 0;
- if (strcmp(table_name, table) == 0) {
- exists = true;
- break;
- }
+ ret = __nft_table_flush(h, table, h->cache->table[t->type].exists);
- t = nftnl_table_list_iter_next(iter);
- }
-
- ret = __nft_table_flush(h, table, exists);
- nftnl_table_list_iter_destroy(iter);
-err_table_list:
-err_out:
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
}
-void nft_table_new(struct nft_handle *h, const char *table)
-{
- nft_xt_builtin_init(h, table);
-}
-
static int __nft_rule_del(struct nft_handle *h, struct nftnl_rule *r)
{
struct obj_update *obj;
nftnl_rule_list_del(r);
- if (!nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE))
+ if (!nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE) &&
+ !nftnl_rule_get_u32(r, NFTNL_RULE_ID))
nftnl_rule_set_u32(r, NFTNL_RULE_ID, ++h->rule_id);
obj = batch_rule_add(h, NFT_COMPAT_RULE_DELETE, r);
@@ -1887,15 +2380,57 @@ static int __nft_rule_del(struct nft_handle *h, struct nftnl_rule *r)
return 1;
}
+static bool nft_rule_cmp(struct nft_handle *h, struct nftnl_rule *r,
+ struct nftnl_rule *rule)
+{
+ struct iptables_command_state _cs = {}, this = {}, *cs = &_cs;
+ bool ret = false, ret_this, ret_that;
+
+ ret_this = h->ops->rule_to_cs(h, r, &this);
+ ret_that = h->ops->rule_to_cs(h, rule, cs);
+
+ DEBUGP("comparing with... ");
+#ifdef DEBUG_DEL
+ nft_rule_print_save(h, r, NFT_RULE_APPEND, 0);
+#endif
+ if (!ret_this || !ret_that)
+ DEBUGP("Cannot convert rules: %d %d\n", ret_this, ret_that);
+
+ if (!h->ops->is_same(cs, &this))
+ goto out;
+
+ if (!compare_matches(cs->matches, this.matches)) {
+ DEBUGP("Different matches\n");
+ goto out;
+ }
+
+ if (!compare_targets(cs->target, this.target)) {
+ DEBUGP("Different target\n");
+ goto out;
+ }
+
+ if ((!cs->target || !this.target) &&
+ strcmp(cs->jumpto, this.jumpto) != 0) {
+ DEBUGP("Different verdict\n");
+ goto out;
+ }
+
+ ret = true;
+out:
+ h->ops->clear_cs(&this);
+ h->ops->clear_cs(cs);
+ return ret;
+}
+
static struct nftnl_rule *
-nft_rule_find(struct nft_handle *h, struct nftnl_chain *c, void *data, int rulenum)
+nft_rule_find(struct nft_handle *h, struct nft_chain *nc,
+ struct nftnl_rule *rule, int rulenum)
{
+ struct nftnl_chain *c = nc->nftnl;
struct nftnl_rule *r;
struct nftnl_rule_iter *iter;
bool found = false;
- nft_build_cache(h, c);
-
if (rulenum >= 0)
/* Delete by rule number case */
return nftnl_rule_lookup_byindex(c, rulenum);
@@ -1906,7 +2441,7 @@ nft_rule_find(struct nft_handle *h, struct nftnl_chain *c, void *data, int rulen
r = nftnl_rule_iter_next(iter);
while (r != NULL) {
- found = h->ops->rule_find(h->ops, r, data);
+ found = nft_rule_cmp(h, r, rule);
if (found)
break;
r = nftnl_rule_iter_next(iter);
@@ -1918,10 +2453,10 @@ nft_rule_find(struct nft_handle *h, struct nftnl_chain *c, void *data, int rulen
}
int nft_rule_check(struct nft_handle *h, const char *chain,
- const char *table, void *data, bool verbose)
+ const char *table, struct nftnl_rule *rule, bool verbose)
{
- struct nftnl_chain *c;
struct nftnl_rule *r;
+ struct nft_chain *c;
nft_fn = nft_rule_check;
@@ -1929,12 +2464,12 @@ int nft_rule_check(struct nft_handle *h, const char *chain,
if (!c)
goto fail_enoent;
- r = nft_rule_find(h, c, data, -1);
+ r = nft_rule_find(h, c, rule, -1);
if (r == NULL)
goto fail_enoent;
if (verbose)
- h->ops->print_rule(r, 0, FMT_PRINT_RULE);
+ h->ops->print_rule(h, r, 0, FMT_PRINT_RULE);
return 1;
fail_enoent:
@@ -1943,11 +2478,11 @@ fail_enoent:
}
int nft_rule_delete(struct nft_handle *h, const char *chain,
- const char *table, void *data, bool verbose)
+ const char *table, struct nftnl_rule *rule, bool verbose)
{
int ret = 0;
- struct nftnl_chain *c;
struct nftnl_rule *r;
+ struct nft_chain *c;
nft_fn = nft_rule_delete;
@@ -1957,13 +2492,13 @@ int nft_rule_delete(struct nft_handle *h, const char *chain,
return 0;
}
- r = nft_rule_find(h, c, data, -1);
+ r = nft_rule_find(h, c, rule, -1);
if (r != NULL) {
ret =__nft_rule_del(h, r);
if (ret < 0)
errno = ENOMEM;
if (verbose)
- h->ops->print_rule(r, 0, FMT_PRINT_RULE);
+ h->ops->print_rule(h, r, 0, FMT_PRINT_RULE);
} else
errno = ENOENT;
@@ -1972,16 +2507,11 @@ int nft_rule_delete(struct nft_handle *h, const char *chain,
static struct nftnl_rule *
nft_rule_add(struct nft_handle *h, const char *chain,
- const char *table, struct iptables_command_state *cs,
+ const char *table, struct nftnl_rule *r,
struct nftnl_rule *ref, bool verbose)
{
- struct nftnl_rule *r;
uint64_t ref_id;
- r = nft_rule_new(h, chain, table, cs);
- if (r == NULL)
- return NULL;
-
if (ref) {
ref_id = nftnl_rule_get_u64(ref, NFTNL_RULE_HANDLE);
if (ref_id > 0) {
@@ -1998,24 +2528,23 @@ nft_rule_add(struct nft_handle *h, const char *chain,
}
}
- if (!batch_rule_add(h, NFT_COMPAT_RULE_INSERT, r)) {
- nftnl_rule_free(r);
+ if (!batch_rule_add(h, NFT_COMPAT_RULE_INSERT, r))
return NULL;
- }
if (verbose)
- h->ops->print_rule(r, 0, FMT_PRINT_RULE);
+ h->ops->print_rule(h, r, 0, FMT_PRINT_RULE);
return r;
}
int nft_rule_insert(struct nft_handle *h, const char *chain,
- const char *table, void *data, int rulenum, bool verbose)
+ const char *table, struct nftnl_rule *new_rule, int rulenum,
+ bool verbose)
{
- struct nftnl_rule *r = NULL, *new_rule;
- struct nftnl_chain *c;
+ struct nftnl_rule *r = NULL;
+ struct nft_chain *c;
- nft_xt_builtin_init(h, table);
+ nft_xt_builtin_init(h, table, chain);
nft_fn = nft_rule_insert;
@@ -2026,29 +2555,29 @@ int nft_rule_insert(struct nft_handle *h, const char *chain,
}
if (rulenum > 0) {
- r = nft_rule_find(h, c, data, rulenum);
+ r = nft_rule_find(h, c, new_rule, rulenum);
if (r == NULL) {
/* special case: iptables allows to insert into
* rule_count + 1 position.
*/
- r = nft_rule_find(h, c, data, rulenum - 1);
+ r = nft_rule_find(h, c, new_rule, rulenum - 1);
if (r != NULL)
- return nft_rule_append(h, chain, table, data,
- NULL, verbose);
+ return nft_rule_append(h, chain, table,
+ new_rule, NULL, verbose);
errno = E2BIG;
goto err;
}
}
- new_rule = nft_rule_add(h, chain, table, data, r, verbose);
+ new_rule = nft_rule_add(h, chain, table, new_rule, r, verbose);
if (!new_rule)
goto err;
if (r)
nftnl_chain_rule_insert_at(new_rule, r);
else
- nftnl_chain_rule_add(new_rule, c);
+ nftnl_chain_rule_add(new_rule, c->nftnl);
return 1;
err:
@@ -2059,8 +2588,8 @@ int nft_rule_delete_num(struct nft_handle *h, const char *chain,
const char *table, int rulenum, bool verbose)
{
int ret = 0;
- struct nftnl_chain *c;
struct nftnl_rule *r;
+ struct nft_chain *c;
nft_fn = nft_rule_delete_num;
@@ -2083,11 +2612,12 @@ int nft_rule_delete_num(struct nft_handle *h, const char *chain,
}
int nft_rule_replace(struct nft_handle *h, const char *chain,
- const char *table, void *data, int rulenum, bool verbose)
+ const char *table, struct nftnl_rule *rule,
+ int rulenum, bool verbose)
{
int ret = 0;
- struct nftnl_chain *c;
struct nftnl_rule *r;
+ struct nft_chain *c;
nft_fn = nft_rule_replace;
@@ -2097,24 +2627,76 @@ int nft_rule_replace(struct nft_handle *h, const char *chain,
return 0;
}
- r = nft_rule_find(h, c, data, rulenum);
+ r = nft_rule_find(h, c, rule, rulenum);
if (r != NULL) {
DEBUGP("replacing rule with handle=%llu\n",
(unsigned long long)
nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE));
- ret = nft_rule_append(h, chain, table, data, r, verbose);
+ ret = nft_rule_append(h, chain, table, rule, r, verbose);
} else
errno = E2BIG;
return ret;
}
+static int nft_rule_change_counters(struct nft_handle *h, const char *table,
+ const char *chain, struct nftnl_rule *rule,
+ int rulenum, struct xt_counters *counters,
+ uint8_t counter_op, bool verbose)
+{
+ struct iptables_command_state cs = {};
+ struct nftnl_rule *r, *new_rule;
+ struct nft_rule_ctx ctx = {
+ .command = NFT_COMPAT_RULE_APPEND,
+ };
+ struct nft_chain *c;
+
+ nft_fn = nft_rule_change_counters;
+
+ c = nft_chain_find(h, table, chain);
+ if (!c) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ r = nft_rule_find(h, c, rule, rulenum);
+ if (!r) {
+ errno = E2BIG;
+ return 0;
+ }
+
+ DEBUGP("changing counters of rule with handle=%llu\n",
+ (unsigned long long)
+ nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE));
+
+ h->ops->rule_to_cs(h, r, &cs);
+
+ if (counter_op & CTR_OP_INC_PKTS)
+ cs.counters.pcnt += counters->pcnt;
+ else if (counter_op & CTR_OP_DEC_PKTS)
+ cs.counters.pcnt -= counters->pcnt;
+ else
+ cs.counters.pcnt = counters->pcnt;
+
+ if (counter_op & CTR_OP_INC_BYTES)
+ cs.counters.bcnt += counters->bcnt;
+ else if (counter_op & CTR_OP_DEC_BYTES)
+ cs.counters.bcnt -= counters->bcnt;
+ else
+ cs.counters.bcnt = counters->bcnt;
+
+ new_rule = nft_rule_new(h, &ctx, chain, table, &cs);
+ h->ops->clear_cs(&cs);
+
+ return nft_rule_append(h, chain, table, new_rule, r, verbose);
+}
+
static int
__nft_rule_list(struct nft_handle *h, struct nftnl_chain *c,
int rulenum, unsigned int format,
- void (*cb)(struct nftnl_rule *r, unsigned int num,
- unsigned int format))
+ void (*cb)(struct nft_handle *h, struct nftnl_rule *r,
+ unsigned int num, unsigned int format))
{
struct nftnl_rule_iter *iter;
struct nftnl_rule *r;
@@ -2127,7 +2709,7 @@ __nft_rule_list(struct nft_handle *h, struct nftnl_chain *c,
* valid chain but invalid rule number
*/
return 1;
- cb(r, rulenum, format);
+ cb(h, r, rulenum, format);
return 1;
}
@@ -2137,7 +2719,7 @@ __nft_rule_list(struct nft_handle *h, struct nftnl_chain *c,
r = nftnl_rule_iter_next(iter);
while (r != NULL) {
- cb(r, ++rule_ctr, format);
+ cb(h, r, ++rule_ctr, format);
r = nftnl_rule_iter_next(iter);
}
@@ -2166,11 +2748,10 @@ static int nft_rule_count(struct nft_handle *h, struct nftnl_chain *c)
}
static void __nft_print_header(struct nft_handle *h,
- const struct nft_family_ops *ops,
- struct nftnl_chain *c, unsigned int format)
+ struct nft_chain *nc, unsigned int format)
{
+ struct nftnl_chain *c = nc->nftnl;
const char *chain_name = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
- bool basechain = !!nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM);
uint32_t refs = nftnl_chain_get_u32(c, NFTNL_CHAIN_USE);
uint32_t entries = nft_rule_count(h, c);
struct xt_counters ctrs = {
@@ -2179,75 +2760,124 @@ static void __nft_print_header(struct nft_handle *h,
};
const char *pname = NULL;
- if (nftnl_chain_is_set(c, NFTNL_CHAIN_POLICY))
+ if (nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM) &&
+ nftnl_chain_is_set(c, NFTNL_CHAIN_POLICY))
pname = policy_name[nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY)];
- ops->print_header(format, chain_name, pname,
- &ctrs, basechain, refs - entries, entries);
+ h->ops->print_header(format, chain_name, pname,
+ &ctrs, refs - entries, entries);
+}
+
+struct nft_rule_list_cb_data {
+ struct nft_handle *h;
+ unsigned int format;
+ int rulenum;
+ bool found;
+ bool save_fmt;
+ void (*cb)(struct nft_handle *h, struct nftnl_rule *r,
+ unsigned int num, unsigned int format);
+};
+
+static int nft_rule_list_cb(struct nft_chain *c, void *data)
+{
+ struct nft_rule_list_cb_data *d = data;
+
+ if (!d->save_fmt) {
+ if (d->found)
+ printf("\n");
+ d->found = true;
+
+ __nft_print_header(d->h, c, d->format);
+ }
+
+ return __nft_rule_list(d->h, c->nftnl, d->rulenum, d->format, d->cb);
}
int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
int rulenum, unsigned int format)
{
- const struct nft_family_ops *ops;
- struct nftnl_chain_list *list;
- struct nftnl_chain_list_iter *iter;
- struct nftnl_chain *c;
- bool found = false;
+ const struct nft_family_ops *ops = h->ops;
+ struct nft_rule_list_cb_data d = {
+ .h = h,
+ .format = format,
+ .rulenum = rulenum,
+ .cb = ops->print_rule,
+ };
+ struct nft_chain *c;
- nft_xt_builtin_init(h, table);
+ nft_xt_fake_builtin_chains(h, table, chain);
nft_assert_table_compatible(h, table, chain);
- ops = nft_family_ops_lookup(h->family);
-
- list = nft_chain_list_get(h, table, chain);
- if (!list)
- return 0;
-
if (chain) {
- c = nftnl_chain_list_lookup_byname(list, chain);
- if (!c)
+ c = nft_chain_find(h, table, chain);
+ if (!c) {
+ errno = ENOENT;
return 0;
-
- if (!rulenum) {
- if (ops->print_table_header)
- ops->print_table_header(table);
- __nft_print_header(h, ops, c, format);
}
- __nft_rule_list(h, c, rulenum, format, ops->print_rule);
+
+ if (rulenum)
+ d.save_fmt = true; /* skip header printing */
+ else if (ops->print_table_header)
+ ops->print_table_header(table);
+
+ nft_rule_list_cb(c, &d);
return 1;
}
- iter = nftnl_chain_list_iter_create(list);
- if (iter == NULL)
- return 0;
+ nft_cache_sort_chains(h, table);
if (ops->print_table_header)
ops->print_table_header(table);
- c = nftnl_chain_list_iter_next(iter);
- while (c != NULL) {
- if (found)
- printf("\n");
-
- __nft_print_header(h, ops, c, format);
- __nft_rule_list(h, c, rulenum, format, ops->print_rule);
-
- found = true;
- c = nftnl_chain_list_iter_next(iter);
- }
- nftnl_chain_list_iter_destroy(iter);
+ nft_chain_foreach(h, table, nft_rule_list_cb, &d);
return 1;
}
static void
-list_save(struct nftnl_rule *r, unsigned int num, unsigned int format)
+list_save(struct nft_handle *h, struct nftnl_rule *r,
+ unsigned int num, unsigned int format)
{
- nft_rule_print_save(r, NFT_RULE_APPEND, format);
+ nft_rule_print_save(h, r, NFT_RULE_APPEND, format);
}
-static int __nftnl_rule_list_chain_save(struct nftnl_chain *c, void *data)
+int nft_chain_foreach(struct nft_handle *h, const char *table,
+ int (*cb)(struct nft_chain *c, void *data),
+ void *data)
{
+ const struct builtin_table *t;
+ struct nft_chain_list *list;
+ struct nft_chain *c, *c_bak;
+ int i, ret;
+
+ t = nft_table_builtin_find(h, table);
+ if (!t)
+ return -1;
+
+ for (i = 0; i < NF_INET_NUMHOOKS; i++) {
+ c = h->cache->table[t->type].base_chains[i];
+ if (!c)
+ continue;
+
+ ret = cb(c, data);
+ if (ret < 0)
+ return ret;
+ }
+
+ list = h->cache->table[t->type].chains;
+ if (!list)
+ return -1;
+
+ list_for_each_entry_safe(c, c_bak, &list->list, head) {
+ ret = cb(c, data);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int nft_rule_list_chain_save(struct nft_chain *nc, void *data)
+{
+ struct nftnl_chain *c = nc->nftnl;
const char *chain_name = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
uint32_t policy = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
int *counters = data;
@@ -2268,85 +2898,67 @@ static int __nftnl_rule_list_chain_save(struct nftnl_chain *c, void *data)
return 0;
}
-static int
-nftnl_rule_list_chain_save(struct nft_handle *h, const char *chain,
- struct nftnl_chain_list *list, int counters)
-{
- struct nftnl_chain *c;
-
- if (chain) {
- c = nftnl_chain_list_lookup_byname(list, chain);
- if (!c)
- return 0;
-
- __nftnl_rule_list_chain_save(c, &counters);
- return 1;
- }
-
- nftnl_chain_list_foreach(list, __nftnl_rule_list_chain_save, &counters);
- return 1;
-}
-
int nft_rule_list_save(struct nft_handle *h, const char *chain,
const char *table, int rulenum, int counters)
{
- struct nftnl_chain_list *list;
- struct nftnl_chain_list_iter *iter;
- unsigned int format = 0;
- struct nftnl_chain *c;
+ struct nft_rule_list_cb_data d = {
+ .h = h,
+ .rulenum = rulenum,
+ .save_fmt = true,
+ .cb = list_save,
+ };
+ struct nft_chain *c;
int ret = 0;
- nft_xt_builtin_init(h, table);
+ nft_xt_fake_builtin_chains(h, table, chain);
nft_assert_table_compatible(h, table, chain);
- list = nft_chain_list_get(h, table, chain);
- if (!list)
- return 0;
-
- /* Dump policies and custom chains first */
- if (!rulenum)
- nftnl_rule_list_chain_save(h, chain, list, counters);
-
if (counters < 0)
- format = FMT_C_COUNTS;
+ d.format = FMT_C_COUNTS;
else if (counters == 0)
- format = FMT_NOCOUNTS;
+ d.format = FMT_NOCOUNTS;
if (chain) {
- c = nftnl_chain_list_lookup_byname(list, chain);
- if (!c)
+ c = nft_chain_find(h, table, chain);
+ if (!c) {
+ errno = ENOENT;
return 0;
+ }
- return __nft_rule_list(h, c, rulenum, format, list_save);
+ if (!rulenum)
+ nft_rule_list_chain_save(c, &counters);
+
+ return nft_rule_list_cb(c, &d);
}
- /* Now dump out rules in this table */
- iter = nftnl_chain_list_iter_create(list);
- if (iter == NULL)
- return 0;
+ nft_cache_sort_chains(h, table);
- c = nftnl_chain_list_iter_next(iter);
- while (c != NULL) {
- ret = __nft_rule_list(h, c, rulenum, format, list_save);
- c = nftnl_chain_list_iter_next(iter);
- }
- nftnl_chain_list_iter_destroy(iter);
- return ret;
+ /* Dump policies and custom chains first */
+ nft_chain_foreach(h, table, nft_rule_list_chain_save, &counters);
+
+ /* Now dump out rules in this table */
+ ret = nft_chain_foreach(h, table, nft_rule_list_cb, &d);
+ return ret == 0 ? 1 : 0;
}
int nft_rule_zero_counters(struct nft_handle *h, const char *chain,
const char *table, int rulenum)
{
struct iptables_command_state cs = {};
- struct nftnl_chain *c;
- struct nftnl_rule *r;
+ struct nftnl_rule *r, *new_rule;
+ struct nft_rule_ctx ctx = {
+ .command = NFT_COMPAT_RULE_APPEND,
+ };
+ struct nft_chain *c;
int ret = 0;
nft_fn = nft_rule_delete;
c = nft_chain_find(h, table, chain);
- if (!c)
+ if (!c) {
+ errno = ENOENT;
return 0;
+ }
r = nft_rule_find(h, c, NULL, rulenum);
if (r == NULL) {
@@ -2355,25 +2967,81 @@ int nft_rule_zero_counters(struct nft_handle *h, const char *chain,
goto error;
}
- nft_rule_to_iptables_command_state(r, &cs);
-
+ h->ops->rule_to_cs(h, r, &cs);
cs.counters.pcnt = cs.counters.bcnt = 0;
+ new_rule = nft_rule_new(h, &ctx, chain, table, &cs);
+ h->ops->clear_cs(&cs);
+
+ if (!new_rule)
+ return 1;
- ret = nft_rule_append(h, chain, table, &cs, r, false);
+ ret = nft_rule_append(h, chain, table, new_rule, r, false);
error:
return ret;
}
+static void nft_table_print_debug(struct nft_handle *h,
+ struct nftnl_table *t, struct nlmsghdr *nlh)
+{
+ if (h->verbose > 1) {
+ nftnl_table_fprintf(stdout, t, 0, 0);
+ fprintf(stdout, "\n");
+ }
+ if (h->verbose > 2)
+ mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len,
+ sizeof(struct nfgenmsg));
+}
+
static void nft_compat_table_batch_add(struct nft_handle *h, uint16_t type,
uint16_t flags, uint32_t seq,
struct nftnl_table *table)
{
struct nlmsghdr *nlh;
- nlh = nftnl_table_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
- type, h->family, flags, seq);
+ nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
+ type, h->family, flags, seq);
nftnl_table_nlmsg_build_payload(nlh, table);
+ nft_table_print_debug(h, table, nlh);
+}
+
+static void nft_compat_set_batch_add(struct nft_handle *h, uint16_t type,
+ uint16_t flags, uint32_t seq,
+ struct nftnl_set *set)
+{
+ struct nlmsghdr *nlh;
+
+ nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
+ type, h->family, flags, seq);
+ nftnl_set_nlmsg_build_payload(nlh, set);
+}
+
+static void nft_compat_setelem_batch_add(struct nft_handle *h, uint16_t type,
+ uint16_t flags, uint32_t *seq,
+ struct nftnl_set *set)
+{
+ struct nftnl_set_elems_iter *iter;
+ struct nlmsghdr *nlh;
+
+ iter = nftnl_set_elems_iter_create(set);
+ if (!iter)
+ return;
+
+ while (nftnl_set_elems_iter_cur(iter)) {
+ (*seq)++;
+ mnl_nft_batch_continue(h->batch);
+ nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
+ type, h->family, flags, *seq);
+ if (nftnl_set_elems_nlmsg_build_payload_iter(nlh, iter) <= 0)
+ break;
+ }
+ nftnl_set_elems_iter_destroy(iter);
+
+ if (h->verbose > 1) {
+ fprintf(stdout, "set ");
+ nftnl_set_fprintf(stdout, set, 0, 0);
+ fprintf(stdout, "\n");
+ }
}
static void nft_compat_chain_batch_add(struct nft_handle *h, uint16_t type,
@@ -2382,10 +3050,10 @@ static void nft_compat_chain_batch_add(struct nft_handle *h, uint16_t type,
{
struct nlmsghdr *nlh;
- nlh = nftnl_chain_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
- type, h->family, flags, seq);
+ nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
+ type, h->family, flags, seq);
nftnl_chain_nlmsg_build_payload(nlh, chain);
- nft_chain_print_debug(chain, nlh);
+ nft_chain_print_debug(h, chain, nlh);
}
static void nft_compat_rule_batch_add(struct nft_handle *h, uint16_t type,
@@ -2394,10 +3062,10 @@ static void nft_compat_rule_batch_add(struct nft_handle *h, uint16_t type,
{
struct nlmsghdr *nlh;
- nlh = nftnl_rule_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
- type, h->family, flags, seq);
+ nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
+ type, h->family, flags, seq);
nftnl_rule_nlmsg_build_payload(nlh, rule);
- nft_rule_print_debug(rule, nlh);
+ nft_rule_print_debug(h, rule, nlh);
}
static void batch_obj_del(struct nft_handle *h, struct obj_update *o)
@@ -2411,7 +3079,7 @@ static void batch_obj_del(struct nft_handle *h, struct obj_update *o)
case NFT_COMPAT_CHAIN_USER_ADD:
case NFT_COMPAT_CHAIN_ADD:
break;
- case NFT_COMPAT_CHAIN_USER_DEL:
+ case NFT_COMPAT_CHAIN_DEL:
case NFT_COMPAT_CHAIN_USER_FLUSH:
case NFT_COMPAT_CHAIN_UPDATE:
case NFT_COMPAT_CHAIN_RENAME:
@@ -2420,11 +3088,23 @@ static void batch_obj_del(struct nft_handle *h, struct obj_update *o)
case NFT_COMPAT_RULE_APPEND:
case NFT_COMPAT_RULE_INSERT:
case NFT_COMPAT_RULE_REPLACE:
- case NFT_COMPAT_RULE_DELETE:
+ case NFT_COMPAT_RULE_CHANGE_COUNTERS:
break;
+ case NFT_COMPAT_RULE_DELETE:
case NFT_COMPAT_RULE_FLUSH:
nftnl_rule_free(o->rule);
break;
+ case NFT_COMPAT_SET_ADD:
+ nftnl_set_free(o->set);
+ break;
+ case NFT_COMPAT_RULE_LIST:
+ case NFT_COMPAT_RULE_CHECK:
+ case NFT_COMPAT_CHAIN_RESTORE:
+ case NFT_COMPAT_RULE_SAVE:
+ case NFT_COMPAT_RULE_ZERO:
+ case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE:
+ assert(0);
+ break;
}
h->obj_list_num--;
list_del(&o->head);
@@ -2434,18 +3114,13 @@ static void batch_obj_del(struct nft_handle *h, struct obj_update *o)
static void nft_refresh_transaction(struct nft_handle *h)
{
const char *tablename, *chainname;
- const struct nftnl_chain *c;
+ const struct nft_chain *c;
struct obj_update *n, *tmp;
bool exists;
h->error.lineno = 0;
list_for_each_entry_safe(n, tmp, &h->obj_list, head) {
- if (n->implicit) {
- batch_obj_del(h, n);
- continue;
- }
-
switch (n->type) {
case NFT_COMPAT_TABLE_FLUSH:
tablename = nftnl_table_get_str(n->table, NFTNL_TABLE_NAME);
@@ -2471,26 +3146,45 @@ static void nft_refresh_transaction(struct nft_handle *h)
c = nft_chain_find(h, tablename, chainname);
if (c) {
- /* -restore -n flushes existing rules from redefined user-chain */
- __nft_rule_flush(h, tablename,
- chainname, false, true);
n->skip = 1;
} else if (!c) {
n->skip = 0;
}
break;
+ case NFT_COMPAT_RULE_FLUSH:
+ tablename = nftnl_rule_get_str(n->rule, NFTNL_RULE_TABLE);
+ if (!tablename)
+ continue;
+
+ chainname = nftnl_rule_get_str(n->rule, NFTNL_RULE_CHAIN);
+ if (!chainname)
+ continue;
+
+ n->skip = !nft_chain_find(h, tablename, chainname);
+ break;
+ case NFT_COMPAT_CHAIN_DEL:
+ if (!nftnl_chain_get(n->chain, NFTNL_CHAIN_HOOKNUM))
+ break;
+ n->skip = !nft_may_delete_chain(n->chain);
+ break;
case NFT_COMPAT_TABLE_ADD:
case NFT_COMPAT_CHAIN_ADD:
case NFT_COMPAT_CHAIN_ZERO:
- case NFT_COMPAT_CHAIN_USER_DEL:
case NFT_COMPAT_CHAIN_USER_FLUSH:
case NFT_COMPAT_CHAIN_UPDATE:
case NFT_COMPAT_CHAIN_RENAME:
case NFT_COMPAT_RULE_APPEND:
case NFT_COMPAT_RULE_INSERT:
case NFT_COMPAT_RULE_REPLACE:
+ case NFT_COMPAT_RULE_CHANGE_COUNTERS:
case NFT_COMPAT_RULE_DELETE:
- case NFT_COMPAT_RULE_FLUSH:
+ case NFT_COMPAT_SET_ADD:
+ case NFT_COMPAT_RULE_LIST:
+ case NFT_COMPAT_RULE_CHECK:
+ case NFT_COMPAT_CHAIN_RESTORE:
+ case NFT_COMPAT_RULE_SAVE:
+ case NFT_COMPAT_RULE_ZERO:
+ case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE:
break;
}
}
@@ -2514,9 +3208,10 @@ retry:
h->nft_genid++;
list_for_each_entry(n, &h->obj_list, head) {
-
- if (n->skip)
+ if (n->skip) {
+ n->seq = 0;
continue;
+ }
n->seq = seq++;
switch (n->type) {
@@ -2541,7 +3236,7 @@ retry:
NLM_F_EXCL, n->seq,
n->chain);
break;
- case NFT_COMPAT_CHAIN_USER_DEL:
+ case NFT_COMPAT_CHAIN_DEL:
nft_compat_chain_batch_add(h, NFT_MSG_DELCHAIN,
NLM_F_NONREC, n->seq,
n->chain);
@@ -2572,6 +3267,7 @@ retry:
n->rule);
break;
case NFT_COMPAT_RULE_REPLACE:
+ case NFT_COMPAT_RULE_CHANGE_COUNTERS:
nft_compat_rule_batch_add(h, NFT_MSG_NEWRULE,
NLM_F_CREATE | NLM_F_REPLACE,
n->seq, n->rule);
@@ -2581,6 +3277,21 @@ retry:
nft_compat_rule_batch_add(h, NFT_MSG_DELRULE, 0,
n->seq, n->rule);
break;
+ case NFT_COMPAT_SET_ADD:
+ nft_compat_set_batch_add(h, NFT_MSG_NEWSET,
+ NLM_F_CREATE, n->seq, n->set);
+ nft_compat_setelem_batch_add(h, NFT_MSG_NEWSETELEM,
+ NLM_F_CREATE, &n->seq, n->set);
+ seq = n->seq;
+ break;
+ case NFT_COMPAT_RULE_LIST:
+ case NFT_COMPAT_RULE_CHECK:
+ case NFT_COMPAT_CHAIN_RESTORE:
+ case NFT_COMPAT_RULE_SAVE:
+ case NFT_COMPAT_RULE_ZERO:
+ case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE:
+ assert(0);
+ return 0;
}
mnl_nft_batch_continue(h->batch);
@@ -2601,7 +3312,6 @@ retry:
nft_refresh_transaction(h);
- i=0;
list_for_each_entry_safe(err, ne, &h->err_list, head)
mnl_err_list_free(err);
@@ -2647,6 +3357,9 @@ static int ebt_add_policy_rule(struct nftnl_chain *c, void *data)
.eb.bitmask = EBT_NOPROTO,
};
struct nftnl_udata_buf *udata;
+ struct nft_rule_ctx ctx = {
+ .command = NFT_COMPAT_RULE_APPEND,
+ };
struct nft_handle *h = data;
struct nftnl_rule *r;
const char *pname;
@@ -2674,35 +3387,41 @@ static int ebt_add_policy_rule(struct nftnl_chain *c, void *data)
command_jump(&cs, pname);
- r = nft_rule_new(h, nftnl_chain_get_str(c, NFTNL_CHAIN_NAME),
+ r = nft_rule_new(h, &ctx, nftnl_chain_get_str(c, NFTNL_CHAIN_NAME),
nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE), &cs);
+ ebt_cs_clean(&cs);
+
if (!r)
return -1;
udata = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
if (!udata)
- return -1;
+ goto err_free_rule;
if (!nftnl_udata_put_u32(udata, UDATA_TYPE_EBTABLES_POLICY, 1))
- return -1;
+ goto err_free_rule;
nftnl_rule_set_data(r, NFTNL_RULE_USERDATA,
nftnl_udata_buf_data(udata),
nftnl_udata_buf_len(udata));
nftnl_udata_buf_free(udata);
- if (!batch_rule_add(h, NFT_COMPAT_RULE_APPEND, r)) {
- nftnl_rule_free(r);
- return -1;
- }
+ if (!batch_rule_add(h, NFT_COMPAT_RULE_APPEND, r))
+ goto err_free_rule;
+
+ /* add the rule to chain so it is freed later */
+ nftnl_chain_rule_add_tail(r, c);
return 0;
+err_free_rule:
+ nftnl_rule_free(r);
+ return -1;
}
int ebt_set_user_chain_policy(struct nft_handle *h, const char *table,
const char *chain, const char *policy)
{
- struct nftnl_chain *c = nft_chain_find(h, table, chain);
+ struct nft_chain *c = nft_chain_find(h, table, chain);
int pval;
if (!c)
@@ -2717,16 +3436,15 @@ int ebt_set_user_chain_policy(struct nft_handle *h, const char *table,
else
return 0;
- nft_build_cache(h, c);
-
- nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, pval);
+ nftnl_chain_set_u32(c->nftnl, NFTNL_CHAIN_POLICY, pval);
return 1;
}
static void nft_bridge_commit_prepare(struct nft_handle *h)
{
const struct builtin_table *t;
- struct nftnl_chain_list *list;
+ struct nft_chain_list *list;
+ struct nft_chain *c;
int i;
for (i = 0; i < NFT_TABLE_MAX; i++) {
@@ -2739,45 +3457,165 @@ static void nft_bridge_commit_prepare(struct nft_handle *h)
if (!list)
continue;
- nftnl_chain_list_foreach(list, ebt_add_policy_rule, h);
+ list_for_each_entry(c, &list->list, head) {
+ ebt_add_policy_rule(c->nftnl, h);
+ }
}
}
-int nft_commit(struct nft_handle *h)
+static void assert_chain_exists(struct nft_handle *h,
+ const char *table, const char *chain)
{
- return nft_action(h, NFT_COMPAT_COMMIT);
+ if (chain && !nft_chain_exists(h, table, chain))
+ xtables_error(PARAMETER_PROBLEM,
+ "Chain '%s' does not exist", chain);
}
-int nft_bridge_commit(struct nft_handle *h)
+static int nft_prepare(struct nft_handle *h)
{
- nft_bridge_commit_prepare(h);
- return nft_commit(h);
+ struct nft_cmd *cmd, *next;
+ int ret = 1;
+
+ nft_cache_build(h);
+
+ list_for_each_entry_safe(cmd, next, &h->cmd_list, head) {
+ h->error.lineno = cmd->error.lineno;
+
+ switch (cmd->command) {
+ case NFT_COMPAT_TABLE_FLUSH:
+ ret = nft_table_flush(h, cmd->table);
+ break;
+ case NFT_COMPAT_CHAIN_USER_ADD:
+ ret = nft_chain_user_add(h, cmd->chain, cmd->table);
+ break;
+ case NFT_COMPAT_CHAIN_DEL:
+ ret = nft_chain_del(h, cmd->chain, cmd->table,
+ cmd->verbose);
+ break;
+ case NFT_COMPAT_CHAIN_RESTORE:
+ ret = nft_chain_restore(h, cmd->chain, cmd->table);
+ break;
+ case NFT_COMPAT_CHAIN_UPDATE:
+ ret = nft_chain_set(h, cmd->table, cmd->chain,
+ cmd->policy, &cmd->counters);
+ break;
+ case NFT_COMPAT_CHAIN_RENAME:
+ ret = nft_chain_user_rename(h, cmd->chain, cmd->table,
+ cmd->rename);
+ break;
+ case NFT_COMPAT_CHAIN_ZERO:
+ ret = nft_chain_zero_counters(h, cmd->chain, cmd->table,
+ cmd->verbose);
+ break;
+ case NFT_COMPAT_RULE_APPEND:
+ assert_chain_exists(h, cmd->table, cmd->jumpto);
+ ret = nft_rule_append(h, cmd->chain, cmd->table,
+ cmd->obj.rule, NULL, cmd->verbose);
+ break;
+ case NFT_COMPAT_RULE_INSERT:
+ assert_chain_exists(h, cmd->table, cmd->jumpto);
+ ret = nft_rule_insert(h, cmd->chain, cmd->table,
+ cmd->obj.rule, cmd->rulenum,
+ cmd->verbose);
+ break;
+ case NFT_COMPAT_RULE_REPLACE:
+ assert_chain_exists(h, cmd->table, cmd->jumpto);
+ ret = nft_rule_replace(h, cmd->chain, cmd->table,
+ cmd->obj.rule, cmd->rulenum,
+ cmd->verbose);
+ break;
+ case NFT_COMPAT_RULE_DELETE:
+ assert_chain_exists(h, cmd->table, cmd->jumpto);
+ if (cmd->rulenum >= 0)
+ ret = nft_rule_delete_num(h, cmd->chain,
+ cmd->table,
+ cmd->rulenum,
+ cmd->verbose);
+ else
+ ret = nft_rule_delete(h, cmd->chain, cmd->table,
+ cmd->obj.rule, cmd->verbose);
+ break;
+ case NFT_COMPAT_RULE_FLUSH:
+ ret = nft_rule_flush(h, cmd->chain, cmd->table,
+ cmd->verbose);
+ break;
+ case NFT_COMPAT_RULE_LIST:
+ ret = nft_rule_list(h, cmd->chain, cmd->table,
+ cmd->rulenum, cmd->format);
+ break;
+ case NFT_COMPAT_RULE_CHECK:
+ assert_chain_exists(h, cmd->table, cmd->jumpto);
+ ret = nft_rule_check(h, cmd->chain, cmd->table,
+ cmd->obj.rule, cmd->verbose);
+ break;
+ case NFT_COMPAT_RULE_ZERO:
+ ret = nft_rule_zero_counters(h, cmd->chain, cmd->table,
+ cmd->rulenum);
+ break;
+ case NFT_COMPAT_RULE_SAVE:
+ ret = nft_rule_list_save(h, cmd->chain, cmd->table,
+ cmd->rulenum,
+ cmd->counters_save);
+ break;
+ case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE:
+ ret = ebt_set_user_chain_policy(h, cmd->table,
+ cmd->chain, cmd->policy);
+ break;
+ case NFT_COMPAT_SET_ADD:
+ nft_xt_builtin_table_init(h, cmd->table);
+ batch_set_add(h, NFT_COMPAT_SET_ADD, cmd->obj.set);
+ ret = 1;
+ break;
+ case NFT_COMPAT_TABLE_ADD:
+ case NFT_COMPAT_CHAIN_ADD:
+ assert(0);
+ return 0;
+ case NFT_COMPAT_RULE_CHANGE_COUNTERS:
+ ret = nft_rule_change_counters(h, cmd->table,
+ cmd->chain,
+ cmd->obj.rule,
+ cmd->rulenum,
+ &cmd->counters,
+ cmd->counter_op,
+ cmd->verbose);
+ break;
+ }
+
+ nft_cmd_free(cmd);
+
+ if (ret == 0)
+ return 0;
+ }
+
+ return 1;
}
-int nft_abort(struct nft_handle *h)
+int nft_commit(struct nft_handle *h)
{
- return nft_action(h, NFT_COMPAT_ABORT);
+ if (!nft_prepare(h))
+ return 0;
+
+ return nft_action(h, NFT_COMPAT_COMMIT);
}
-int nft_abort_policy_rule(struct nft_handle *h, const char *table)
+int nft_bridge_commit(struct nft_handle *h)
{
- struct obj_update *n, *tmp;
+ if (!nft_prepare(h))
+ return 0;
- list_for_each_entry_safe(n, tmp, &h->obj_list, head) {
- if (n->type != NFT_COMPAT_RULE_APPEND &&
- n->type != NFT_COMPAT_RULE_DELETE)
- continue;
+ nft_bridge_commit_prepare(h);
- if (strcmp(table,
- nftnl_rule_get_str(n->rule, NFTNL_RULE_TABLE)))
- continue;
+ return nft_action(h, NFT_COMPAT_COMMIT);
+}
- if (!nft_rule_is_policy_rule(n->rule))
- continue;
+int nft_abort(struct nft_handle *h)
+{
+ struct nft_cmd *cmd, *next;
- batch_obj_del(h, n);
- }
- return 0;
+ list_for_each_entry_safe(cmd, next, &h->cmd_list, head)
+ nft_cmd_free(cmd);
+
+ return nft_action(h, NFT_COMPAT_ABORT);
}
int nft_compatible_revision(const char *name, uint8_t rev, int opt)
@@ -2847,6 +3685,20 @@ int nft_compatible_revision(const char *name, uint8_t rev, int opt)
err:
mnl_socket_close(nl);
+ /* ignore EPERM and errors for revision 0 -
+ * this is required for printing extension help texts as user, also
+ * helps error messaging on unavailable kernel extension */
+ if (ret < 0) {
+ if (errno == EPERM)
+ return 1;
+ if (rev == 0) {
+ fprintf(stderr,
+ "Warning: Extension %s revision 0 not supported, missing kernel module?\n",
+ name);
+ return 1;
+ }
+ }
+
return ret < 0 ? 0 : 1;
}
@@ -2860,10 +3712,9 @@ const char *nft_strerror(int err)
const char *message;
} table[] =
{
- { nft_chain_user_del, ENOTEMPTY, "Chain is not empty" },
- { nft_chain_user_del, EINVAL, "Can't delete built-in chain" },
- { nft_chain_user_del, EBUSY, "Directory not empty" },
- { nft_chain_user_del, EMLINK,
+ { nft_chain_del, ENOTEMPTY, "Chain is not empty" },
+ { nft_chain_del, EBUSY, "Directory not empty" },
+ { nft_chain_del, EMLINK,
"Can't delete chain with references left" },
{ nft_chain_user_add, EEXIST, "Chain already exists" },
{ nft_chain_user_rename, EEXIST, "File exists" },
@@ -2897,13 +3748,71 @@ const char *nft_strerror(int err)
return strerror(err);
}
+static int l4proto_expr_get_dreg(struct nftnl_expr *e, uint32_t *dregp)
+{
+ const char *name = nftnl_expr_get_str(e, NFTNL_EXPR_NAME);
+ uint32_t poff = offsetof(struct iphdr, protocol);
+ uint32_t pbase = NFT_PAYLOAD_NETWORK_HEADER;
+
+ if (!strcmp(name, "payload") &&
+ nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_BASE) == pbase &&
+ nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET) == poff &&
+ nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_LEN) == sizeof(uint8_t)) {
+ *dregp = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_DREG);
+ return 0;
+ }
+ if (!strcmp(name, "meta") &&
+ nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY) == NFT_META_L4PROTO) {
+ *dregp = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG);
+ return 0;
+ }
+ return -1;
+}
+
+static int recover_rule_compat(struct nftnl_rule *r)
+{
+ struct nftnl_expr_iter *iter;
+ struct nftnl_expr *e;
+ uint32_t reg;
+ int ret = -1;
+
+ iter = nftnl_expr_iter_create(r);
+ if (!iter)
+ return -1;
+
+next_expr:
+ e = nftnl_expr_iter_next(iter);
+ if (!e)
+ goto out;
+
+ /* may be 'ip protocol' or 'meta l4proto' with identical RHS */
+ if (l4proto_expr_get_dreg(e, &reg) < 0)
+ goto next_expr;
+
+ e = nftnl_expr_iter_next(iter);
+ if (!e)
+ goto out;
+
+ if (strcmp("cmp", nftnl_expr_get_str(e, NFTNL_EXPR_NAME)) ||
+ reg != nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG))
+ goto next_expr;
+
+ add_compat(r, nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA),
+ nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ);
+ ret = 0;
+out:
+ nftnl_expr_iter_destroy(iter);
+ return ret;
+}
+
struct chain_zero_data {
struct nft_handle *handle;
bool verbose;
};
-static int __nft_chain_zero_counters(struct nftnl_chain *c, void *data)
+static int __nft_chain_zero_counters(struct nft_chain *nc, void *data)
{
+ struct nftnl_chain *c = nc->nftnl;
struct chain_zero_data *d = data;
struct nft_handle *h = d->handle;
struct nftnl_rule_iter *iter;
@@ -2911,19 +3820,17 @@ static int __nft_chain_zero_counters(struct nftnl_chain *c, void *data)
if (d->verbose)
fprintf(stdout, "Zeroing chain `%s'\n",
- nftnl_chain_get_str(c, NFTNL_CHAIN_NAME));
+ nftnl_chain_get_str(c, NFTNL_CHAIN_NAME));
if (nftnl_chain_is_set(c, NFTNL_CHAIN_HOOKNUM)) {
/* zero base chain counters. */
nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS, 0);
nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES, 0);
nftnl_chain_unset(c, NFTNL_CHAIN_HANDLE);
- if (batch_chain_add(h, NFT_COMPAT_CHAIN_ZERO, c))
+ if (!batch_chain_add(h, NFT_COMPAT_CHAIN_ZERO, c))
return -1;
}
- nft_build_cache(h, c);
-
iter = nftnl_rule_iter_create(c);
if (iter == NULL)
return -1;
@@ -2961,6 +3868,7 @@ static int __nft_chain_zero_counters(struct nftnl_chain *c, void *data)
* Unset RULE_POSITION for older kernels, we want to replace
* rule based on its handle only.
*/
+ recover_rule_compat(r);
nftnl_rule_unset(r, NFTNL_RULE_POSITION);
if (!batch_rule_add(h, NFT_COMPAT_RULE_REPLACE, r)) {
nftnl_rule_iter_destroy(iter);
@@ -2977,20 +3885,15 @@ static int __nft_chain_zero_counters(struct nftnl_chain *c, void *data)
int nft_chain_zero_counters(struct nft_handle *h, const char *chain,
const char *table, bool verbose)
{
- struct nftnl_chain_list *list;
struct chain_zero_data d = {
.handle = h,
.verbose = verbose,
};
- struct nftnl_chain *c;
+ struct nft_chain *c;
int ret = 0;
- list = nft_chain_list_get(h, table, chain);
- if (list == NULL)
- goto err;
-
if (chain) {
- c = nftnl_chain_list_lookup_byname(list, chain);
+ c = nft_chain_find(h, table, chain);
if (!c) {
errno = ENOENT;
return 0;
@@ -3000,7 +3903,10 @@ int nft_chain_zero_counters(struct nft_handle *h, const char *chain,
goto err;
}
- ret = nftnl_chain_list_foreach(list, __nft_chain_zero_counters, &d);
+ if (verbose)
+ nft_cache_sort_chains(h, table);
+
+ ret = nft_chain_foreach(h, table, __nft_chain_zero_counters, &d);
err:
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
@@ -3022,7 +3928,9 @@ static const char *supported_exprs[] = {
"cmp",
"bitwise",
"counter",
- "immediate"
+ "immediate",
+ "lookup",
+ "range",
};
@@ -3041,6 +3949,10 @@ static int nft_is_expr_compatible(struct nftnl_expr *expr, void *data)
nftnl_expr_get_u32(expr, NFTNL_EXPR_LIMIT_FLAGS) == 0)
return 0;
+ if (!strcmp(name, "log") &&
+ nftnl_expr_is_set(expr, NFTNL_EXPR_LOG_GROUP))
+ return 0;
+
return -1;
}
@@ -3049,57 +3961,30 @@ static int nft_is_rule_compatible(struct nftnl_rule *rule, void *data)
return nftnl_expr_foreach(rule, nft_is_expr_compatible, NULL);
}
-static int nft_is_chain_compatible(struct nftnl_chain *c, void *data)
+static int nft_is_chain_compatible(struct nft_chain *nc, void *data)
{
- const struct builtin_table *table;
- const struct builtin_chain *chain;
- const char *tname, *cname, *type;
- struct nft_handle *h = data;
- enum nf_inet_hooks hook;
- int prio;
-
- nft_build_cache(h, c);
+ struct nftnl_chain *c = nc->nftnl;
- if (nftnl_rule_foreach(c, nft_is_rule_compatible, NULL))
- return -1;
-
- if (!nft_chain_builtin(c))
- return 0;
-
- tname = nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
- table = nft_table_builtin_find(h, tname);
- if (!table)
- return -1;
-
- cname = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
- chain = nft_chain_builtin_find(table, cname);
- if (!chain)
- return -1;
-
- type = nftnl_chain_get_str(c, NFTNL_CHAIN_TYPE);
- prio = nftnl_chain_get_u32(c, NFTNL_CHAIN_PRIO);
- hook = nftnl_chain_get_u32(c, NFTNL_CHAIN_HOOKNUM);
- if (strcmp(type, chain->type) ||
- prio != chain->prio ||
- hook != chain->hook)
- return -1;
-
- return 0;
+ return nftnl_rule_foreach(c, nft_is_rule_compatible, NULL);
}
bool nft_is_table_compatible(struct nft_handle *h,
const char *table, const char *chain)
{
- struct nftnl_chain_list *clist;
+ if (chain) {
+ struct nft_chain *c = nft_chain_find(h, table, chain);
- clist = nft_chain_list_get(h, table, chain);
- if (clist == NULL)
- return false;
+ return !c || !nft_is_chain_compatible(c, h);
+ }
- if (nftnl_chain_list_foreach(clist, nft_is_chain_compatible, h))
- return false;
+ return !nft_chain_foreach(h, table, nft_is_chain_compatible, h);
+}
- return true;
+bool nft_is_table_tainted(struct nft_handle *h, const char *table)
+{
+ const struct builtin_table *t = nft_table_builtin_find(h, table);
+
+ return t ? h->cache->table[t->type].tainted : false;
}
void nft_assert_table_compatible(struct nft_handle *h,
@@ -3107,8 +3992,12 @@ void nft_assert_table_compatible(struct nft_handle *h,
{
const char *pfx = "", *sfx = "";
- if (nft_is_table_compatible(h, table, chain))
+ if (nft_is_table_compatible(h, table, chain)) {
+ if (nft_is_table_tainted(h, table))
+ printf("# Table `%s' contains incompatible base-chains, use 'nft' tool to list them.\n",
+ table);
return;
+ }
if (chain) {
pfx = "chain `";
@@ -3117,6 +4006,6 @@ void nft_assert_table_compatible(struct nft_handle *h,
chain = "";
}
xtables_error(OTHER_PROBLEM,
- "%s%s%stable `%s' is incompatible, use 'nft' tool.\n",
+ "%s%s%stable `%s' is incompatible, use 'nft' tool.",
pfx, chain, sfx, table);
}