diff options
Diffstat (limited to 'iptables/nft.c')
-rw-r--r-- | iptables/nft.c | 1603 |
1 files changed, 1003 insertions, 600 deletions
diff --git a/iptables/nft.c b/iptables/nft.c index 0c5a74fc..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 @@ -350,7 +356,6 @@ static int mnl_append_error(const struct nft_handle *h, case NFT_COMPAT_RULE_SAVE: case NFT_COMPAT_RULE_ZERO: case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE: - case NFT_COMPAT_TABLE_NEW: assert(0); break; } @@ -362,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; @@ -389,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 * @@ -434,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, @@ -571,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, @@ -594,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, @@ -643,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) @@ -657,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; @@ -672,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; @@ -681,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 */ @@ -746,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) - continue; - - c = nftnl_chain_list_lookup_byname(list, table->chains[i].name); - if (c != NULL) + if (nft_chain_find(h, table->name, table->chains[i].name)) 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_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; 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; + } + + c = nft_chain_builtin_find(t, chain); + if (!c) + return -1; - h->cache->table[t->type].initialized = true; + if (h->cache->table[t->type].base_chains[c->hook]) + return 0; + nft_chain_builtin_add(h, t, c, false); return 0; } @@ -800,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); @@ -818,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)); @@ -836,7 +919,7 @@ 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; @@ -865,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, @@ -881,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; @@ -892,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 { @@ -905,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; } @@ -920,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; @@ -928,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) @@ -947,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)); @@ -1026,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; } @@ -1053,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 @@ -1080,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); @@ -1120,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, ®); 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); @@ -1151,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, ð_p_ip, 2); + add_meta(h, r, NFT_META_PROTOCOL, ®); + add_cmp_ptr(r, NFT_CMP_EQ, ð_p_ip, 2, reg); } if (data->src.cnt) @@ -1166,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, ®); + 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, ®); + 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, ®); + 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, ®); + + if (!e) + return -ENOMEM; + + nftnl_rule_add_expr(r, e); + + add_bitwise(h, r, &mask, 1, 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, ®); + if (mark->mask != 0xffffffff) + add_bitwise(h, r, (uint8_t *)&mark->mask, sizeof(uint32_t), 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) @@ -1195,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)); @@ -1279,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) @@ -1378,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; @@ -1391,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; @@ -1400,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; @@ -1437,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, @@ -1460,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; } @@ -1472,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) @@ -1559,61 +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 nft_family_ops *ops = h->ops; - struct nftnl_chain_list_iter *iter; - struct nftnl_chain *c; - - 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(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); } @@ -1623,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; @@ -1667,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; @@ -1689,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; @@ -1727,36 +2056,22 @@ int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table, 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); - 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; @@ -1767,59 +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 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 */ @@ -1827,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) { @@ -1924,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; @@ -1934,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; } @@ -2036,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); @@ -2049,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); @@ -2058,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); @@ -2125,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; @@ -2161,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; @@ -2192,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; @@ -2218,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; @@ -2279,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; @@ -2314,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: @@ -2325,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; @@ -2353,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; @@ -2377,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, @@ -2433,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 = { @@ -2445,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); - 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, 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, 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; } @@ -2511,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; @@ -2533,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; + } - 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, @@ -2604,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) { @@ -2620,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; @@ -2633,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, @@ -2675,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, @@ -2683,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, @@ -2695,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) @@ -2712,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: @@ -2721,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: @@ -2735,7 +3103,6 @@ static void batch_obj_del(struct nft_handle *h, struct obj_update *o) case NFT_COMPAT_RULE_SAVE: case NFT_COMPAT_RULE_ZERO: case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE: - case NFT_COMPAT_TABLE_NEW: assert(0); break; } @@ -2747,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); @@ -2784,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: @@ -2811,7 +3185,6 @@ static void nft_refresh_transaction(struct nft_handle *h) case NFT_COMPAT_RULE_SAVE: case NFT_COMPAT_RULE_ZERO: case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE: - case NFT_COMPAT_TABLE_NEW: break; } } @@ -2835,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) { @@ -2862,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); @@ -2893,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); @@ -2915,8 +3290,8 @@ retry: case NFT_COMPAT_RULE_SAVE: case NFT_COMPAT_RULE_ZERO: case NFT_COMPAT_BRIDGE_USER_CHAIN_UPDATE: - case NFT_COMPAT_TABLE_NEW: assert(0); + return 0; } mnl_nft_batch_continue(h->batch); @@ -2982,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; @@ -3009,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); @@ -3043,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) @@ -3058,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++) { @@ -3078,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); + } } } @@ -3098,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); @@ -3105,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); @@ -3163,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, @@ -3178,18 +3561,23 @@ static int nft_prepare(struct nft_handle *h) ret = ebt_set_user_chain_policy(h, cmd->table, cmd->chain, cmd->policy); break; - case NFT_COMPAT_TABLE_NEW: - nft_xt_builtin_init(h, cmd->table); - ret = 1; - 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; } @@ -3297,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; } @@ -3310,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" }, @@ -3347,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; @@ -3363,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, ®) < 0) goto next_expr; - reg = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG); - e = nftnl_expr_iter_next(iter); if (!e) goto out; @@ -3390,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; @@ -3399,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; } @@ -3464,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; @@ -3487,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; @@ -3511,6 +3930,7 @@ static const char *supported_exprs[] = { "counter", "immediate", "lookup", + "range", }; @@ -3529,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; } @@ -3537,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; + struct nftnl_chain *c = nc->nftnl; - 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, @@ -3593,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 `"; @@ -3603,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); } |