summaryrefslogtreecommitdiffstats
path: root/iptables/nft.c
diff options
context:
space:
mode:
Diffstat (limited to 'iptables/nft.c')
-rw-r--r--iptables/nft.c823
1 files changed, 635 insertions, 188 deletions
diff --git a/iptables/nft.c b/iptables/nft.c
index f1deb82f..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)
@@ -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;
@@ -290,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",
@@ -321,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:
@@ -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
@@ -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;
@@ -435,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,
@@ -572,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,
@@ -595,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,
@@ -644,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,
@@ -659,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;
@@ -667,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;
@@ -676,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);
@@ -685,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;
}
@@ -695,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;
@@ -866,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));
@@ -884,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;
@@ -913,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,
@@ -929,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;
@@ -945,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 {
@@ -953,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;
}
@@ -997,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));
@@ -1076,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;
}
@@ -1103,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
@@ -1130,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);
@@ -1170,21 +1216,23 @@ static int __add_nft_among(struct nft_handle *h, const char *table,
nftnl_set_elem_add(s, elem);
}
- e = gen_payload(NFT_PAYLOAD_LL_HEADER,
- eth_addr_off[dst], ETH_ALEN, NFT_REG_1);
+ e = gen_payload(h, NFT_PAYLOAD_LL_HEADER,
+ eth_addr_off[dst], ETH_ALEN, &reg);
if (!e)
return -ENOMEM;
nftnl_rule_add_expr(r, e);
if (ip) {
- e = gen_payload(NFT_PAYLOAD_NETWORK_HEADER, ip_addr_off[dst],
- sizeof(struct in_addr), NFT_REG32_02);
+ reg = nft_get_next_reg(reg, ETH_ALEN);
+ e = __gen_payload(NFT_PAYLOAD_NETWORK_HEADER, ip_addr_off[dst],
+ sizeof(struct in_addr), reg);
if (!e)
return -ENOMEM;
nftnl_rule_add_expr(r, e);
}
- e = gen_lookup(NFT_REG_1, "__set%d", set_id, inv);
+ reg = NFT_REG_1;
+ e = gen_lookup(reg, "__set%d", set_id, inv);
if (!e)
return -ENOMEM;
nftnl_rule_add_expr(r, e);
@@ -1201,9 +1249,10 @@ static int add_nft_among(struct nft_handle *h,
if ((data->src.cnt && data->src.ip) ||
(data->dst.cnt && data->dst.ip)) {
uint16_t eth_p_ip = htons(ETH_P_IP);
+ uint8_t reg;
- add_meta(r, NFT_META_PROTOCOL);
- add_cmp_ptr(r, NFT_CMP_EQ, &eth_p_ip, 2);
+ add_meta(h, r, NFT_META_PROTOCOL, &reg);
+ add_cmp_ptr(r, NFT_CMP_EQ, &eth_p_ip, 2, reg);
}
if (data->src.cnt)
@@ -1216,16 +1265,230 @@ static int add_nft_among(struct nft_handle *h,
return 0;
}
-int add_match(struct nft_handle *h,
+static int expr_gen_range_cmp16(struct nftnl_rule *r,
+ uint16_t lo,
+ uint16_t hi,
+ bool invert, uint8_t reg)
+{
+ struct nftnl_expr *e;
+
+ if (lo == hi) {
+ add_cmp_u16(r, htons(lo), invert ? NFT_CMP_NEQ : NFT_CMP_EQ, reg);
+ return 0;
+ }
+
+ if (lo == 0 && hi < 0xffff) {
+ add_cmp_u16(r, htons(hi) , invert ? NFT_CMP_GT : NFT_CMP_LTE, reg);
+ return 0;
+ }
+
+ e = nftnl_expr_alloc("range");
+ if (!e)
+ return -ENOMEM;
+
+ nftnl_expr_set_u32(e, NFTNL_EXPR_RANGE_SREG, reg);
+ nftnl_expr_set_u32(e, NFTNL_EXPR_RANGE_OP, invert ? NFT_RANGE_NEQ : NFT_RANGE_EQ);
+
+ lo = htons(lo);
+ nftnl_expr_set(e, NFTNL_EXPR_RANGE_FROM_DATA, &lo, sizeof(lo));
+ hi = htons(hi);
+ nftnl_expr_set(e, NFTNL_EXPR_RANGE_TO_DATA, &hi, sizeof(hi));
+
+ nftnl_rule_add_expr(r, e);
+ return 0;
+}
+
+static int add_nft_tcpudp(struct nft_handle *h,struct nftnl_rule *r,
+ uint16_t src[2], bool invert_src,
+ uint16_t dst[2], bool invert_dst)
+{
+ struct nftnl_expr *expr;
+ uint8_t op = NFT_CMP_EQ;
+ uint8_t reg;
+ int ret;
+
+ if (!invert_src &&
+ src[0] && src[0] == src[1] &&
+ dst[0] && dst[0] == dst[1] &&
+ invert_src == invert_dst) {
+ uint32_t combined = dst[0] | (src[0] << 16);
+
+ expr = gen_payload(h, NFT_PAYLOAD_TRANSPORT_HEADER, 0, 4, &reg);
+ if (!expr)
+ return -ENOMEM;
+
+ nftnl_rule_add_expr(r, expr);
+ add_cmp_u32(r, htonl(combined), op, reg);
+ return 0;
+ }
+
+ if (src[0] || src[1] < UINT16_MAX || invert_src) {
+ expr = gen_payload(h, NFT_PAYLOAD_TRANSPORT_HEADER, 0, 2, &reg);
+ if (!expr)
+ return -ENOMEM;
+
+ nftnl_rule_add_expr(r, expr);
+ ret = expr_gen_range_cmp16(r, src[0], src[1], invert_src, reg);
+ if (ret)
+ return ret;
+ }
+
+ if (dst[0] || dst[1] < UINT16_MAX || invert_dst) {
+ expr = gen_payload(h, NFT_PAYLOAD_TRANSPORT_HEADER, 2, 2, &reg);
+ if (!expr)
+ return -ENOMEM;
+
+ nftnl_rule_add_expr(r, expr);
+ ret = expr_gen_range_cmp16(r, dst[0], dst[1], invert_dst, reg);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* without this, "iptables -A INPUT -m udp" is
+ * turned into "iptables -A INPUT", which isn't
+ * compatible with iptables-legacy behaviour.
+ */
+static bool udp_all_zero(const struct xt_udp *u)
+{
+ static const struct xt_udp zero = {
+ .spts[1] = 0xffff,
+ .dpts[1] = 0xffff,
+ };
+
+ return memcmp(u, &zero, sizeof(*u)) == 0;
+}
+
+static int add_nft_udp(struct nft_handle *h, struct nftnl_rule *r,
+ struct xt_entry_match *m)
+{
+ struct xt_udp *udp = (void *)m->data;
+
+ if (udp->invflags > XT_UDP_INV_MASK ||
+ udp_all_zero(udp)) {
+ struct nftnl_expr *expr = nftnl_expr_alloc("match");
+ int ret;
+
+ ret = __add_match(expr, m);
+ nftnl_rule_add_expr(r, expr);
+ return ret;
+ }
+
+ if (nftnl_rule_get_u32(r, NFTNL_RULE_COMPAT_PROTO) != IPPROTO_UDP)
+ xtables_error(PARAMETER_PROBLEM, "UDP match requires '-p udp'");
+
+ return add_nft_tcpudp(h, r, udp->spts, udp->invflags & XT_UDP_INV_SRCPT,
+ udp->dpts, udp->invflags & XT_UDP_INV_DSTPT);
+}
+
+static int add_nft_tcpflags(struct nft_handle *h, struct nftnl_rule *r,
+ uint8_t cmp, uint8_t mask,
+ bool invert)
+{
+ struct nftnl_expr *e;
+ uint8_t reg;
+
+ e = gen_payload(h, NFT_PAYLOAD_TRANSPORT_HEADER, 13, 1, &reg);
+
+ if (!e)
+ return -ENOMEM;
+
+ nftnl_rule_add_expr(r, e);
+
+ add_bitwise(h, r, &mask, 1, reg, &reg);
+ add_cmp_u8(r, cmp, invert ? NFT_CMP_NEQ : NFT_CMP_EQ, reg);
+
+ return 0;
+}
+
+static bool tcp_all_zero(const struct xt_tcp *t)
+{
+ static const struct xt_tcp zero = {
+ .spts[1] = 0xffff,
+ .dpts[1] = 0xffff,
+ };
+
+ return memcmp(t, &zero, sizeof(*t)) == 0;
+}
+
+static int add_nft_tcp(struct nft_handle *h, struct nftnl_rule *r,
+ struct xt_entry_match *m)
+{
+ static const uint8_t supported = XT_TCP_INV_SRCPT | XT_TCP_INV_DSTPT | XT_TCP_INV_FLAGS;
+ struct xt_tcp *tcp = (void *)m->data;
+
+ if (tcp->invflags & ~supported || tcp->option ||
+ tcp_all_zero(tcp)) {
+ struct nftnl_expr *expr = nftnl_expr_alloc("match");
+ int ret;
+
+ ret = __add_match(expr, m);
+ nftnl_rule_add_expr(r, expr);
+ return ret;
+ }
+
+ if (nftnl_rule_get_u32(r, NFTNL_RULE_COMPAT_PROTO) != IPPROTO_TCP)
+ xtables_error(PARAMETER_PROBLEM, "TCP match requires '-p tcp'");
+
+ if (tcp->flg_mask) {
+ int ret = add_nft_tcpflags(h, r, tcp->flg_cmp, tcp->flg_mask,
+ tcp->invflags & XT_TCP_INV_FLAGS);
+
+ if (ret < 0)
+ return ret;
+ }
+
+ return add_nft_tcpudp(h, r, tcp->spts, tcp->invflags & XT_TCP_INV_SRCPT,
+ tcp->dpts, tcp->invflags & XT_TCP_INV_DSTPT);
+}
+
+static int add_nft_mark(struct nft_handle *h, struct nftnl_rule *r,
+ struct xt_entry_match *m)
+{
+ struct xt_mark_mtinfo1 *mark = (void *)m->data;
+ uint8_t reg;
+ int op;
+
+ add_meta(h, r, NFT_META_MARK, &reg);
+ if (mark->mask != 0xffffffff)
+ add_bitwise(h, r, (uint8_t *)&mark->mask, sizeof(uint32_t), reg, &reg);
+
+ if (mark->invert)
+ op = NFT_CMP_NEQ;
+ else
+ op = NFT_CMP_EQ;
+
+ add_cmp_u32(r, mark->mark, op, reg);
+
+ return 0;
+}
+
+int add_match(struct nft_handle *h, struct nft_rule_ctx *ctx,
struct nftnl_rule *r, struct xt_entry_match *m)
{
struct nftnl_expr *expr;
int ret;
- if (!strcmp(m->u.user.name, "limit"))
- return add_nft_limit(r, m);
- else if (!strcmp(m->u.user.name, "among"))
- return add_nft_among(h, r, m);
+ switch (ctx->command) {
+ case NFT_COMPAT_RULE_APPEND:
+ case NFT_COMPAT_RULE_INSERT:
+ case NFT_COMPAT_RULE_REPLACE:
+ if (!strcmp(m->u.user.name, "limit"))
+ return add_nft_limit(r, m);
+ else if (!strcmp(m->u.user.name, "among"))
+ return add_nft_among(h, r, m);
+ else if (!strcmp(m->u.user.name, "udp"))
+ return add_nft_udp(h, r, m);
+ else if (!strcmp(m->u.user.name, "tcp"))
+ return add_nft_tcp(h, r, m);
+ else if (!strcmp(m->u.user.name, "mark"))
+ return add_nft_mark(h, r, m);
+ break;
+ default:
+ break;
+ }
expr = nftnl_expr_alloc("match");
if (expr == NULL)
@@ -1245,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));
@@ -1329,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)
@@ -1428,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;
@@ -1441,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;
@@ -1490,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,
@@ -1507,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;
}
@@ -1519,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)
@@ -1629,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)
@@ -1643,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);
}
@@ -1661,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;
}
@@ -1782,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)
@@ -1812,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;
@@ -1845,51 +2147,71 @@ 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 nft_chain *nc, void *data)
+static bool nft_may_delete_chain(struct nftnl_chain *c)
{
- struct chain_user_del_data *d = data;
+ 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;
- /* don't delete built-in chain */
- if (nft_chain_builtin(c))
- return d->builtin_err;
-
- 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_USER_DEL, c))
+ obj = batch_chain_add(h, NFT_COMPAT_CHAIN_DEL, c);
+ if (!obj)
return -1;
+ 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 0;
+ 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 nft_chain *c;
int ret = 0;
- nft_fn = nft_chain_user_del;
+ nft_fn = nft_chain_del;
if (chain) {
c = nft_chain_find(h, table, chain);
@@ -1897,17 +2219,15 @@ int nft_chain_user_del(struct nft_handle *h, const char *chain,
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;
}
if (verbose)
nft_cache_sort_chains(h, table);
- ret = nft_chain_foreach(h, table, __nft_chain_user_del, &d);
+ 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;
@@ -1955,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);
@@ -2003,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);
@@ -2046,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);
@@ -2061,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;
@@ -2314,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,
@@ -2374,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 = {
@@ -2383,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 {
@@ -2432,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 */
@@ -2540,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);
@@ -2564,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) {
@@ -2580,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;
@@ -2593,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,
@@ -2635,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,
@@ -2643,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,
@@ -2655,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)
@@ -2672,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:
@@ -2681,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:
@@ -2754,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_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_SET_ADD:
case NFT_COMPAT_RULE_LIST:
@@ -2823,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);
@@ -2854,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);
@@ -2877,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);
@@ -2942,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;
@@ -2969,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);
@@ -3061,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);
@@ -3068,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);
@@ -3126,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,
@@ -3149,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;
}
@@ -3256,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;
}
@@ -3269,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" },
@@ -3306,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;
@@ -3322,12 +3785,10 @@ next_expr:
if (!e)
goto out;
- if (strcmp("meta", nftnl_expr_get_str(e, NFTNL_EXPR_NAME)) ||
- nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY) != NFT_META_L4PROTO)
+ /* may be 'ip protocol' or 'meta l4proto' with identical RHS */
+ if (l4proto_expr_get_dreg(e, &reg) < 0)
goto next_expr;
- reg = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG);
-
e = nftnl_expr_iter_next(iter);
if (!e)
goto out;
@@ -3469,6 +3930,7 @@ static const char *supported_exprs[] = {
"counter",
"immediate",
"lookup",
+ "range",
};
@@ -3487,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;
}
@@ -3498,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,
@@ -3538,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 `";
@@ -3559,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);
}