summaryrefslogtreecommitdiffstats
path: root/iptables/nft.c
diff options
context:
space:
mode:
Diffstat (limited to 'iptables/nft.c')
-rw-r--r--iptables/nft.c1496
1 files changed, 986 insertions, 510 deletions
diff --git a/iptables/nft.c b/iptables/nft.c
index 634d02fe..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);
@@ -265,7 +271,6 @@ 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;
@@ -289,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",
@@ -320,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:
@@ -332,6 +337,7 @@ 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
@@ -361,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;
@@ -388,10 +391,11 @@ batch_set_add(struct nft_handle *h, enum obj_update_type type,
return batch_add(h, type, s);
}
-static int batch_chain_add(struct nft_handle *h, enum obj_update_type type,
+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 *
@@ -433,7 +437,7 @@ static void batch_chain_flush(struct nft_handle *h,
}
}
-const struct builtin_table xtables_ipv4[NFT_TABLE_MAX] = {
+static const struct builtin_table xtables_ipv4[NFT_TABLE_MAX] = {
[NFT_TABLE_RAW] = {
.name = "raw",
.type = NFT_TABLE_RAW,
@@ -570,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,
@@ -593,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,
@@ -642,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)
@@ -656,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;
@@ -671,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;
@@ -680,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 */
@@ -745,49 +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 0;
+ return NULL;
t = nft_table_builtin_find(h, table);
if (t == NULL)
- return -1;
+ return NULL;
+
+ if (nft_table_builtin_add(h, t) < 0)
+ return NULL;
- if (nft_table_initialized(h, t->type))
+ 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;
if (h->cache_req.level < NFT_CL_CHAINS)
return 0;
- nft_chain_builtin_init(h, t);
+ if (!chain) {
+ nft_chain_builtin_init(h, t);
+ return 0;
+ }
- h->cache->table[t->type].initialized = true;
+ 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;
}
@@ -799,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);
@@ -817,7 +886,22 @@ int nft_restart(struct nft_handle *h)
return 0;
}
-int nft_init(struct nft_handle *h, int family, 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));
@@ -835,15 +919,13 @@ int nft_init(struct nft_handle *h, int family, const struct builtin_table *t)
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[0].tables);
- INIT_LIST_HEAD(&h->__cache[1].tables);
INIT_LIST_HEAD(&h->cache_req.chain_list);
return 0;
@@ -866,15 +948,16 @@ void nft_fini(struct nft_handle *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,
@@ -882,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;
@@ -893,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_xt_builtin_init(h, table);
+ 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 {
@@ -906,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;
}
@@ -921,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;
@@ -929,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)
@@ -948,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));
@@ -1027,16 +1108,32 @@ static struct nftnl_set *add_anon_set(struct nft_handle *h, const char *table,
}
static struct nftnl_expr *
-gen_payload(uint32_t base, uint32_t offset, uint32_t len, uint32_t dreg)
+__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, dreg);
+ 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;
}
@@ -1054,9 +1151,6 @@ gen_lookup(uint32_t sreg, const char *set_name, uint32_t set_id, uint32_t flags)
return e;
}
-/* simplified nftables:include/netlink.h, netlink_padded_len() */
-#define NETLINK_ALIGN 4
-
/* from nftables:include/datatype.h, TYPE_BITS */
#define CONCAT_TYPE_BITS 6
@@ -1081,13 +1175,14 @@ static int __add_nft_among(struct nft_handle *h, const char *table,
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;
+ flags = NFT_SET_INTERVAL | NFT_SET_CONCAT;
}
s = add_anon_set(h, table, flags, type, len, cnt);
@@ -1121,21 +1216,23 @@ static int __add_nft_among(struct nft_handle *h, const char *table,
nftnl_set_elem_add(s, elem);
}
- e = gen_payload(NFT_PAYLOAD_LL_HEADER,
- eth_addr_off[dst], ETH_ALEN, NFT_REG_1);
+ 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) {
- e = gen_payload(NFT_PAYLOAD_NETWORK_HEADER, ip_addr_off[dst],
- sizeof(struct in_addr), NFT_REG32_02);
+ 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);
}
- e = gen_lookup(NFT_REG_1, "__set%d", set_id, inv);
+ reg = NFT_REG_1;
+ e = gen_lookup(reg, "__set%d", set_id, inv);
if (!e)
return -ENOMEM;
nftnl_rule_add_expr(r, e);
@@ -1152,9 +1249,10 @@ static int add_nft_among(struct nft_handle *h,
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(r, NFT_META_PROTOCOL);
- add_cmp_ptr(r, NFT_CMP_EQ, &eth_p_ip, 2);
+ add_meta(h, r, NFT_META_PROTOCOL, &reg);
+ add_cmp_ptr(r, NFT_CMP_EQ, &eth_p_ip, 2, reg);
}
if (data->src.cnt)
@@ -1167,16 +1265,230 @@ static int add_nft_among(struct nft_handle *h,
return 0;
}
-int add_match(struct nft_handle *h,
+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 (!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;
- 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);
+ 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)
@@ -1196,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));
@@ -1280,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)
@@ -1379,8 +1716,9 @@ void add_compat(struct nftnl_rule *r, uint32_t proto, bool inv)
}
struct nftnl_rule *
-nft_rule_new(struct nft_handle *h, const char *chain, const char *table,
- void *data)
+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;
@@ -1392,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(h, r, data) < 0)
+ if (h->ops->add(h, ctx, r, cs) < 0)
goto err;
return r;
@@ -1401,17 +1739,14 @@ 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,
struct nftnl_rule *r, struct nftnl_rule *ref, bool verbose)
{
- struct nftnl_chain *c;
+ struct nft_chain *c;
int type;
- nft_xt_builtin_init(h, table);
+ nft_xt_builtin_init(h, table, chain);
nft_fn = nft_rule_append;
@@ -1438,21 +1773,22 @@ nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
errno = ENOENT;
return 0;
}
- nftnl_chain_rule_add_tail(r, c);
+ nftnl_chain_rule_add_tail(r, c->nftnl);
}
return 1;
}
-void
+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);
struct iptables_command_state cs = {};
struct nft_family_ops *ops = h->ops;
+ bool ret;
- ops->rule_to_cs(h, r, &cs);
+ ret = ops->rule_to_cs(h, r, &cs);
if (!(format & (FMT_NOCOUNTS | FMT_C_COUNTS)))
printf("[%llu:%llu] ", (unsigned long long)cs.counters.pcnt,
@@ -1461,10 +1797,10 @@ nft_rule_print_save(struct nft_handle *h, const struct nftnl_rule *r,
/* 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;
}
@@ -1473,6 +1809,8 @@ nft_rule_print_save(struct nft_handle *h, const struct nftnl_rule *r,
if (ops->clear_cs)
ops->clear_cs(&cs);
+
+ return ret;
}
static bool nft_rule_is_policy_rule(struct nftnl_rule *r)
@@ -1560,8 +1898,9 @@ static const char *policy_name[NF_ACCEPT+1] = {
[NF_ACCEPT] = "ACCEPT",
};
-int nft_chain_save(struct nftnl_chain *c, void *data)
+int nft_chain_save(struct nft_chain *nc, void *data)
{
+ struct nftnl_chain *c = nc->nftnl;
struct nft_handle *h = data;
const char *policy = NULL;
@@ -1579,19 +1918,29 @@ int nft_chain_save(struct nftnl_chain *c, void *data)
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(h, 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);
}
@@ -1601,29 +1950,16 @@ 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;
-
- list = nft_chain_list_get(h, table, NULL);
- if (!list)
- return 0;
-
- iter = nftnl_chain_list_iter_create(list);
- if (!iter)
- return 0;
-
- c = nftnl_chain_list_iter_next(iter);
- while (c) {
- ret = nft_chain_save_rules(h, c, format);
- if (ret != 0)
- break;
+ struct nft_rule_save_data d = {
+ .h = h,
+ .format = format,
+ };
+ int ret;
- c = nftnl_chain_list_iter_next(iter);
- }
+ ret = nft_chain_foreach(h, table, nft_rule_save_cb, &d);
- nftnl_chain_list_iter_destroy(iter);
+ if (ret == 0 && d.errors)
+ xtables_error(VERSION_PROBLEM, "Cannot decode all rules provided by kernel");
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
@@ -1645,7 +1981,7 @@ struct nftnl_set *nft_set_batch_lookup_byid(struct nft_handle *h,
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;
@@ -1667,21 +2003,41 @@ __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_fn = nft_rule_flush;
if (chain || verbose)
- nft_xt_builtin_init(h, table);
+ nft_xt_builtin_init(h, table, chain);
else if (!nft_table_find(h, table))
return 1;
@@ -1700,42 +2056,22 @@ int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table,
return 1;
}
- list = nft_chain_list_get(h, table, chain);
- if (list == NULL) {
- ret = 1;
- goto err;
- }
-
- 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);
- batch_chain_flush(h, table, chain);
- __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;
@@ -1746,61 +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;
- nft_xt_builtin_init(h, table);
+ t = nft_xt_builtin_table_init(h, table);
- 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 {
+ nc = nft_chain_find(h, table, chain);
+ if (!nc) {
c = nftnl_chain_alloc();
if (!c)
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;
- }
- if (h->family == NFPROTO_BRIDGE)
- nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, NF_ACCEPT);
+ nft_cache_add_chain(h, t, c);
+ } else {
+ c = nc->nftnl;
- if (!created)
- return 1;
+ /* 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);
+ }
- ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c);
+ __nft_rule_flush(h, table, chain, false, created);
- list = nft_chain_list_get(h, table, chain);
- if (list)
- nftnl_chain_list_add(c, list);
+ obj = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c);
+ if (!obj)
+ return 0;
+
+ obj->skip = !created;
/* the core expects 1 for success and 0 for error */
- return ret == 0 ? 1 : 0;
+ return 1;
}
/* From linux/netlink.h */
@@ -1808,84 +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));
+
/* 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)
{
@@ -1905,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;
@@ -1915,67 +2262,53 @@ int nft_chain_user_rename(struct nft_handle *h,const char *chain,
return 0;
}
- /* 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 list_head *list;
- struct nft_table *t;
- bool ret = false;
-
- list = nft_table_list_get(h);
-
- list_for_each_entry(t, list, list) {
- const char *this_tablename =
- nftnl_table_get(t->nftnl, NFTNL_TABLE_NAME);
-
- if (strcmp(tablename, this_tablename) == 0) {
- ret = true;
- break;
- }
- }
+ const struct builtin_table *t;
- 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 list_head *list;
- struct nft_table *t;
+ int i;
- list = nft_table_list_get(h);
+ for (i = 0; i < NFT_TABLE_MAX; i++) {
+ if (h->tables[i].name == NULL)
+ continue;
- list_for_each_entry(t, list, list) {
- const char *tablename =
- nftnl_table_get(t->nftnl, NFTNL_TABLE_NAME);
+ if (!h->cache->table[h->tables[i].type].exists)
+ continue;
- func(h, tablename, data);
+ func(h, h->tables[i].name, data);
}
return 0;
@@ -1991,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);
@@ -2004,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);
@@ -2013,26 +2347,16 @@ 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 list_head *list;
- struct nft_table *t;
- bool exists = false;
+ const struct builtin_table *t;
int ret = 0;
nft_fn = nft_table_flush;
- list = nft_table_list_get(h);
-
- list_for_each_entry(t, list, list) {
- const char *table_name =
- nftnl_table_get_str(t->nftnl, NFTNL_TABLE_NAME);
-
- if (strcmp(table_name, table) == 0) {
- exists = true;
- break;
- }
- }
+ t = nft_table_builtin_find(h, table);
+ if (!t)
+ return 0;
- ret = __nft_table_flush(h, table, exists);
+ ret = __nft_table_flush(h, table, h->cache->table[t->type].exists);
/* the core expects 1 for success and 0 for error */
return ret == 0 ? 1 : 0;
@@ -2044,7 +2368,8 @@ static int __nft_rule_del(struct nft_handle *h, struct nftnl_rule *r)
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);
@@ -2059,15 +2384,18 @@ 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;
+ bool ret = false, ret_this, ret_that;
- h->ops->rule_to_cs(h, r, &this);
- h->ops->rule_to_cs(h, rule, cs);
+ 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;
@@ -2095,9 +2423,10 @@ out:
}
static struct nftnl_rule *
-nft_rule_find(struct nft_handle *h, struct nftnl_chain *c,
+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;
@@ -2126,8 +2455,8 @@ nft_rule_find(struct nft_handle *h, struct nftnl_chain *c,
int nft_rule_check(struct nft_handle *h, const char *chain,
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;
@@ -2152,8 +2481,8 @@ int nft_rule_delete(struct nft_handle *h, const char *chain,
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;
@@ -2213,9 +2542,9 @@ int nft_rule_insert(struct nft_handle *h, const char *chain,
bool verbose)
{
struct nftnl_rule *r = NULL;
- struct nftnl_chain *c;
+ struct nft_chain *c;
- nft_xt_builtin_init(h, table);
+ nft_xt_builtin_init(h, table, chain);
nft_fn = nft_rule_insert;
@@ -2248,7 +2577,7 @@ int nft_rule_insert(struct nft_handle *h, const char *chain,
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:
@@ -2259,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;
@@ -2287,8 +2616,8 @@ int nft_rule_replace(struct nft_handle *h, const char *chain,
int rulenum, bool verbose)
{
int ret = 0;
- struct nftnl_chain *c;
struct nftnl_rule *r;
+ struct nft_chain *c;
nft_fn = nft_rule_replace;
@@ -2311,6 +2640,58 @@ int nft_rule_replace(struct nft_handle *h, const char *chain,
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,
@@ -2367,10 +2748,10 @@ static int nft_rule_count(struct nft_handle *h, struct nftnl_chain *c)
}
static void __nft_print_header(struct nft_handle *h,
- 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 = {
@@ -2379,62 +2760,76 @@ 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)];
h->ops->print_header(format, chain_name, pname,
- &ctrs, basechain, refs - entries, entries);
+ &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 = h->ops;
- struct nftnl_chain_list *list;
- struct nftnl_chain_list_iter *iter;
- struct nftnl_chain *c;
- bool found = false;
+ 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);
if (chain) {
c = nft_chain_find(h, table, chain);
- if (!c)
+ if (!c) {
+ errno = ENOENT;
return 0;
-
- if (!rulenum) {
- if (ops->print_table_header)
- ops->print_table_header(table);
- __nft_print_header(h, 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;
}
- list = nft_chain_list_get(h, table, chain);
- if (!list)
- return 0;
-
- 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, 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;
}
@@ -2445,8 +2840,44 @@ list_save(struct nft_handle *h, struct nftnl_rule *r,
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;
@@ -2467,70 +2898,47 @@ 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;
+ }
+
+ if (!rulenum)
+ nft_rule_list_chain_save(c, &counters);
- return __nft_rule_list(h, c, rulenum, format, list_save);
+ 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,
@@ -2538,14 +2946,19 @@ int nft_rule_zero_counters(struct nft_handle *h, const char *chain,
{
struct iptables_command_state cs = {};
struct nftnl_rule *r, *new_rule;
- struct nftnl_chain *c;
+ 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) {
@@ -2554,10 +2967,11 @@ int nft_rule_zero_counters(struct nft_handle *h, const char *chain,
goto error;
}
- nft_rule_to_iptables_command_state(h, r, &cs);
-
+ h->ops->rule_to_cs(h, r, &cs);
cs.counters.pcnt = cs.counters.bcnt = 0;
- new_rule = nft_rule_new(h, chain, table, &cs);
+ new_rule = nft_rule_new(h, &ctx, chain, table, &cs);
+ h->ops->clear_cs(&cs);
+
if (!new_rule)
return 1;
@@ -2567,15 +2981,28 @@ 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,
@@ -2609,6 +3036,12 @@ static void nft_compat_setelem_batch_add(struct nft_handle *h, uint16_t type,
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,
@@ -2617,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,
@@ -2629,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)
@@ -2646,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:
@@ -2655,6 +3088,7 @@ 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_CHANGE_COUNTERS:
break;
case NFT_COMPAT_RULE_DELETE:
case NFT_COMPAT_RULE_FLUSH:
@@ -2680,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);
@@ -2717,26 +3146,38 @@ 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:
@@ -2767,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) {
@@ -2794,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);
@@ -2825,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);
@@ -2848,6 +3291,7 @@ retry:
case NFT_COMPAT_RULE_ZERO:
case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE:
assert(0);
+ return 0;
}
mnl_nft_batch_continue(h->batch);
@@ -2913,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;
@@ -2940,7 +3387,7 @@ 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);
@@ -2974,7 +3421,7 @@ err_free_rule:
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)
@@ -2989,14 +3436,15 @@ int ebt_set_user_chain_policy(struct nft_handle *h, const char *table,
else
return 0;
- 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++) {
@@ -3009,7 +3457,9 @@ 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);
+ }
}
}
@@ -3029,6 +3479,8 @@ static int nft_prepare(struct nft_handle *h)
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);
@@ -3036,9 +3488,9 @@ static int nft_prepare(struct nft_handle *h)
case NFT_COMPAT_CHAIN_USER_ADD:
ret = nft_chain_user_add(h, cmd->chain, cmd->table);
break;
- case NFT_COMPAT_CHAIN_USER_DEL:
- ret = nft_chain_user_del(h, cmd->chain, cmd->table,
- cmd->verbose);
+ 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);
@@ -3094,7 +3546,7 @@ static int nft_prepare(struct nft_handle *h)
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->rulenum);
+ cmd->obj.rule, cmd->verbose);
break;
case NFT_COMPAT_RULE_ZERO:
ret = nft_rule_zero_counters(h, cmd->chain, cmd->table,
@@ -3110,13 +3562,22 @@ static int nft_prepare(struct nft_handle *h)
cmd->chain, cmd->policy);
break;
case NFT_COMPAT_SET_ADD:
- nft_xt_builtin_init(h, cmd->table);
+ 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;
}
@@ -3224,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;
}
@@ -3237,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" },
@@ -3274,6 +3748,27 @@ 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;
@@ -3290,12 +3785,10 @@ next_expr:
if (!e)
goto out;
- if (strcmp("meta", nftnl_expr_get_str(e, NFTNL_EXPR_NAME)) ||
- nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY) != NFT_META_L4PROTO)
+ /* may be 'ip protocol' or 'meta l4proto' with identical RHS */
+ if (l4proto_expr_get_dreg(e, &reg) < 0)
goto next_expr;
- reg = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG);
-
e = nftnl_expr_iter_next(iter);
if (!e)
goto out;
@@ -3317,8 +3810,9 @@ struct chain_zero_data {
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;
@@ -3326,14 +3820,14 @@ 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;
}
@@ -3391,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;
@@ -3414,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;
@@ -3438,6 +3930,7 @@ static const char *supported_exprs[] = {
"counter",
"immediate",
"lookup",
+ "range",
};
@@ -3456,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;
}
@@ -3464,55 +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;
-
- if (nftnl_rule_foreach(c, nft_is_rule_compatible, NULL))
- return -1;
-
- if (!nft_chain_builtin(c))
- return 0;
+ struct nftnl_chain *c = nc->nftnl;
- 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,
@@ -3520,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 `";
@@ -3530,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);
}