diff options
Diffstat (limited to 'iptables/nft.c')
-rw-r--r-- | iptables/nft.c | 786 |
1 files changed, 609 insertions, 177 deletions
diff --git a/iptables/nft.c b/iptables/nft.c index 89dde9ec..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> @@ -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) @@ -333,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 @@ -432,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, @@ -569,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, @@ -592,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, @@ -641,6 +646,19 @@ 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 int nft_table_builtin_add(struct nft_handle *h, @@ -656,6 +674,7 @@ static int nft_table_builtin_add(struct nft_handle *h, 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; @@ -664,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; @@ -673,7 +692,8 @@ 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); @@ -682,6 +702,9 @@ nft_chain_builtin_alloc(const struct builtin_table *table, 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; } @@ -692,7 +715,7 @@ static void nft_chain_builtin_add(struct nft_handle *h, { 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; @@ -863,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)); @@ -881,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; @@ -910,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, @@ -926,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; @@ -942,7 +982,7 @@ static struct nftnl_chain *nft_chain_new(struct nft_handle *h, _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 { @@ -950,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; } @@ -1070,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; } @@ -1097,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 @@ -1124,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); @@ -1164,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); @@ -1195,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) @@ -1210,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) @@ -1320,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) @@ -1419,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; @@ -1432,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; @@ -1481,15 +1779,16 @@ nft_rule_append(struct nft_handle *h, const char *chain, const char *table, 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, @@ -1498,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; } @@ -1510,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) @@ -1620,6 +1921,7 @@ int nft_chain_save(struct nft_chain *nc, void *data) 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) @@ -1634,7 +1936,11 @@ static int nft_rule_save_cb(struct nft_chain *c, void *data) r = nftnl_rule_iter_next(iter); while (r != NULL) { - nft_rule_print_save(d->h, r, NFT_RULE_APPEND, d->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); } @@ -1652,6 +1958,9 @@ int nft_rule_save(struct nft_handle *h, const char *table, unsigned int format) ret = nft_chain_foreach(h, table, nft_rule_save_cb, &d); + 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; } @@ -1773,6 +2082,7 @@ 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) @@ -1803,6 +2113,7 @@ int nft_chain_restore(struct nft_handle *h, const char *chain, const char *table 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; @@ -1838,32 +2149,47 @@ int nft_chain_restore(struct nft_handle *h, const char *chain, const char *table struct chain_del_data { struct nft_handle *handle; - struct nft_cache *cache; - enum nft_table_type type; + const char *chain; bool verbose; }; +static bool nft_may_delete_chain(struct nftnl_chain *c) +{ + if (nftnl_chain_is_set(c, NFTNL_CHAIN_POLICY) && + nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY) != NF_ACCEPT) + return false; + + return nftnl_rule_lookup_byindex(c, 0) == NULL; +} + 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) + 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); - if (!batch_chain_add(h, NFT_COMPAT_CHAIN_DEL, c)) + obj = batch_chain_add(h, NFT_COMPAT_CHAIN_DEL, c); + if (!obj) return -1; - if (nft_chain_builtin(c)) { - uint32_t num = nftnl_chain_get_u32(c, NFTNL_CHAIN_HOOKNUM); - - if (nc == d->cache->table[d->type].base_chains[num]) - d->cache->table[d->type].base_chains[num] = NULL; + 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 */ @@ -1871,15 +2197,15 @@ static int __nft_chain_del(struct nft_chain *nc, void *data) nft_chain_list_del(nc); nft_chain_free(nc); - return 0; + return ret; } int nft_chain_del(struct nft_handle *h, const char *chain, - const char *table, bool verbose) + const char *table, bool verbose) { - const struct builtin_table *t; struct chain_del_data d = { .handle = h, + .chain = chain, .verbose = verbose, }; struct nft_chain *c; @@ -1894,32 +2220,10 @@ int nft_chain_del(struct nft_handle *h, const char *chain, return 0; } - if (nft_chain_builtin(c->nftnl)) { - t = nft_table_builtin_find(h, table); - if (!t) { - errno = EINVAL; - return 0; - } - - d.type = t->type; - d.cache = h->cache; - } - ret = __nft_chain_del(c, &d); - if (ret == -2) - errno = EINVAL; goto out; } - t = nft_table_builtin_find(h, table); - if (!t) { - errno = EINVAL; - return 0; - } - - d.type = t->type; - d.cache = h->cache; - if (verbose) nft_cache_sort_chains(h, table); @@ -1971,6 +2275,7 @@ int nft_chain_user_rename(struct nft_handle *h,const char *chain, 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); @@ -2019,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); @@ -2062,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); @@ -2077,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; @@ -2330,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, @@ -2390,7 +2752,6 @@ static void __nft_print_header(struct nft_handle *h, { 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 = { @@ -2399,11 +2760,12 @@ 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 { @@ -2448,8 +2810,10 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table, if (chain) { c = nft_chain_find(h, table, chain); - if (!c) + if (!c) { + errno = ENOENT; return 0; + } if (rulenum) d.save_fmt = true; /* skip header printing */ @@ -2556,8 +2920,10 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain, if (chain) { c = nft_chain_find(h, table, chain); - if (!c) + if (!c) { + errno = ENOENT; return 0; + } if (!rulenum) nft_rule_list_chain_save(c, &counters); @@ -2580,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 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) { @@ -2596,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; @@ -2609,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, @@ -2651,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, @@ -2659,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, @@ -2671,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) @@ -2697,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: @@ -2770,16 +3162,21 @@ static void nft_refresh_transaction(struct nft_handle *h) 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_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_SET_ADD: case NFT_COMPAT_RULE_LIST: @@ -2870,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); @@ -2893,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); @@ -2958,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; @@ -2985,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); @@ -3077,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); @@ -3165,6 +3569,15 @@ static int nft_prepare(struct nft_handle *h) 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; } @@ -3272,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; } @@ -3321,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; @@ -3337,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; @@ -3484,6 +3930,7 @@ static const char *supported_exprs[] = { "counter", "immediate", "lookup", + "range", }; @@ -3502,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; } @@ -3513,38 +3964,8 @@ static int nft_is_rule_compatible(struct nftnl_rule *rule, void *data) static int nft_is_chain_compatible(struct nft_chain *nc, void *data) { struct nftnl_chain *c = nc->nftnl; - 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; - - 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, @@ -3553,19 +3974,30 @@ bool nft_is_table_compatible(struct nft_handle *h, if (chain) { struct nft_chain *c = nft_chain_find(h, table, chain); - return c && !nft_is_chain_compatible(c, h); + return !c || !nft_is_chain_compatible(c, h); } return !nft_chain_foreach(h, table, nft_is_chain_compatible, h); } +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, const char *table, const char *chain) { 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 `"; @@ -3574,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); } |