From 5d448803a16ef637d0cc715ffa2249d5b1a443c9 Mon Sep 17 00:00:00 2001 From: eabdullin Date: Tue, 11 Nov 2025 22:17:13 +0000 Subject: [PATCH] import UBI nftables-1.1.1-6.el10_1 --- ...allow-to-re-use-existing-metered-set.patch | 13 +- ...-monitor-trace-code-into-new-trace.c.patch | 807 ++++++++++++++++++ ...ck-information-to-trace-monitor-mode.patch | 268 ++++++ ...mleak-in-trace_alloc_list-error-path.patch | 38 + ...-Minor-NAT-STATEMENTS-section-review.patch | 78 ++ ...d-creating-nft-version-into-userdata.patch | 232 +++++ 0020-Makefile-Fix-for-make-CFLAGS.patch | 52 ++ nftables.spec | 18 +- 8 files changed, 1497 insertions(+), 9 deletions(-) create mode 100644 0015-src-split-monitor-trace-code-into-new-trace.c.patch create mode 100644 0016-src-add-conntrack-information-to-trace-monitor-mode.patch create mode 100644 0017-trace-Fix-for-memleak-in-trace_alloc_list-error-path.patch create mode 100644 0018-doc-nft.8-Minor-NAT-STATEMENTS-section-review.patch create mode 100644 0019-table-Embed-creating-nft-version-into-userdata.patch create mode 100644 0020-Makefile-Fix-for-make-CFLAGS.patch diff --git a/0014-evaluate-allow-to-re-use-existing-metered-set.patch b/0014-evaluate-allow-to-re-use-existing-metered-set.patch index 2d6f92e..484fb94 100644 --- a/0014-evaluate-allow-to-re-use-existing-metered-set.patch +++ b/0014-evaluate-allow-to-re-use-existing-metered-set.patch @@ -1,4 +1,4 @@ -From b3c1312b5815b004614d79eae2ad731c6883ce6f Mon Sep 17 00:00:00 2001 +From 75c95b2f59fb09c6375ca1e10277af9d0641e71d Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 22 Jan 2025 10:18:04 +0100 Subject: [PATCH] evaluate: allow to re-use existing metered set @@ -41,7 +41,7 @@ Signed-off-by: Eric Garver create mode 100755 tests/shell/testcases/sets/meter_set_reuse diff --git a/src/evaluate.c b/src/evaluate.c -index 593a0140e92a..c9cbaa6ae648 100644 +index 593a014..c9cbaa6 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -3338,7 +3338,7 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt) @@ -114,7 +114,7 @@ index 593a0140e92a..c9cbaa6ae648 100644 if (stmt_evaluate(ctx, stmt->meter.stmt) < 0) diff --git a/tests/shell/testcases/sets/dumps/meter_set_reuse.json-nft b/tests/shell/testcases/sets/dumps/meter_set_reuse.json-nft new file mode 100644 -index 000000000000..ab4ac06184d0 +index 0000000..ab4ac06 --- /dev/null +++ b/tests/shell/testcases/sets/dumps/meter_set_reuse.json-nft @@ -0,0 +1,105 @@ @@ -225,7 +225,7 @@ index 000000000000..ab4ac06184d0 +} diff --git a/tests/shell/testcases/sets/dumps/meter_set_reuse.nft b/tests/shell/testcases/sets/dumps/meter_set_reuse.nft new file mode 100644 -index 000000000000..f911acaffb85 +index 0000000..f911aca --- /dev/null +++ b/tests/shell/testcases/sets/dumps/meter_set_reuse.nft @@ -0,0 +1,11 @@ @@ -242,7 +242,7 @@ index 000000000000..f911acaffb85 +} diff --git a/tests/shell/testcases/sets/meter_set_reuse b/tests/shell/testcases/sets/meter_set_reuse new file mode 100755 -index 000000000000..94eccc1a7b82 +index 0000000..94eccc1 --- /dev/null +++ b/tests/shell/testcases/sets/meter_set_reuse @@ -0,0 +1,20 @@ @@ -266,6 +266,3 @@ index 000000000000..94eccc1a7b82 + +# This re-add should work. +addrule --- -2.48.1 - diff --git a/0015-src-split-monitor-trace-code-into-new-trace.c.patch b/0015-src-split-monitor-trace-code-into-new-trace.c.patch new file mode 100644 index 0000000..7ed9aa9 --- /dev/null +++ b/0015-src-split-monitor-trace-code-into-new-trace.c.patch @@ -0,0 +1,807 @@ +From bb46381b2d378729d709480806c9522aaa32deeb Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 15 Jul 2025 22:50:32 +0200 +Subject: [PATCH] src: split monitor trace code into new trace.c + +JIRA: https://issues.redhat.com/browse/RHEL-102994 +Upstream Status: nftables commit 8e03d59b5aa46b960454b4fd30541cee77125f77 + +commit 8e03d59b5aa46b960454b4fd30541cee77125f77 +Author: Florian Westphal +Date: Mon Jul 7 11:47:13 2025 +0200 + + src: split monitor trace code into new trace.c + + Preparation patch to avoid putting more trace functionality into + netlink.c. + + Signed-off-by: Florian Westphal + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + Makefile.am | 1 + + include/netlink.h | 5 - + include/trace.h | 8 ++ + src/monitor.c | 2 +- + src/netlink.c | 332 ------------------------------------------- + src/trace.c | 353 ++++++++++++++++++++++++++++++++++++++++++++++ + 6 files changed, 363 insertions(+), 338 deletions(-) + create mode 100644 include/trace.h + create mode 100644 src/trace.c + +diff --git a/Makefile.am b/Makefile.am +index fb64105..ba09e7f 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -220,6 +220,7 @@ src_libnftables_la_SOURCES = \ + src/misspell.c \ + src/mnl.c \ + src/monitor.c \ ++ src/trace.c \ + src/netlink.c \ + src/netlink_delinearize.c \ + src/netlink_linearize.c \ +diff --git a/include/netlink.h b/include/netlink.h +index e9667a2..609f213 100644 +--- a/include/netlink.h ++++ b/include/netlink.h +@@ -227,11 +227,6 @@ struct ruleset_parse { + struct cmd *cmd; + }; + +-struct nftnl_parse_ctx; +- +-int netlink_events_trace_cb(const struct nlmsghdr *nlh, int type, +- struct netlink_mon_handler *monh); +- + enum nft_data_types dtype_map_to_kernel(const struct datatype *dtype); + + void netlink_linearize_init(struct netlink_linearize_ctx *lctx, +diff --git a/include/trace.h b/include/trace.h +new file mode 100644 +index 0000000..ebebb47 +--- /dev/null ++++ b/include/trace.h +@@ -0,0 +1,8 @@ ++#ifndef NFTABLES_TRACE_H ++#define NFTABLES_TRACE_H ++#include ++ ++struct netlink_mon_handler; ++int netlink_events_trace_cb(const struct nlmsghdr *nlh, int type, ++ struct netlink_mon_handler *monh); ++#endif /* NFTABLES_TRACE_H */ +diff --git a/src/monitor.c b/src/monitor.c +index a787db8..01325c9 100644 +--- a/src/monitor.c ++++ b/src/monitor.c +@@ -16,7 +16,6 @@ + #include + + #include +-#include + #include + #include + #include +@@ -32,6 +31,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/netlink.c b/src/netlink.c +index 25ee341..2ced863 100644 +--- a/src/netlink.c ++++ b/src/netlink.c +@@ -18,7 +18,6 @@ + #include + + #include +-#include + #include + #include + #include +@@ -41,7 +40,6 @@ + #include + #include + #include +-#include + + #define nft_mon_print(monh, ...) nft_print(&monh->ctx->nft->output, __VA_ARGS__) + +@@ -1859,333 +1857,3 @@ int netlink_list_flowtables(struct netlink_ctx *ctx, const struct handle *h) + nftnl_flowtable_list_free(flowtable_cache); + return err; + } +- +-static void trace_print_hdr(const struct nftnl_trace *nlt, +- struct output_ctx *octx) +-{ +- nft_print(octx, "trace id %08x %s ", +- nftnl_trace_get_u32(nlt, NFTNL_TRACE_ID), +- family2str(nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY))); +- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_TABLE)) +- nft_print(octx, "%s ", +- nftnl_trace_get_str(nlt, NFTNL_TRACE_TABLE)); +- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_CHAIN)) +- nft_print(octx, "%s ", +- nftnl_trace_get_str(nlt, NFTNL_TRACE_CHAIN)); +-} +- +-static void trace_print_expr(const struct nftnl_trace *nlt, unsigned int attr, +- struct expr *lhs, struct output_ctx *octx) +-{ +- struct expr *rhs, *rel; +- const void *data; +- uint32_t len; +- +- data = nftnl_trace_get_data(nlt, attr, &len); +- rhs = constant_expr_alloc(&netlink_location, +- lhs->dtype, lhs->byteorder, +- len * BITS_PER_BYTE, data); +- rel = relational_expr_alloc(&netlink_location, OP_EQ, lhs, rhs); +- +- expr_print(rel, octx); +- nft_print(octx, " "); +- expr_free(rel); +-} +- +-static void trace_print_verdict(const struct nftnl_trace *nlt, +- struct output_ctx *octx) +-{ +- struct expr *chain_expr = NULL; +- const char *chain = NULL; +- unsigned int verdict; +- struct expr *expr; +- +- verdict = nftnl_trace_get_u32(nlt, NFTNL_TRACE_VERDICT); +- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_JUMP_TARGET)) { +- chain = xstrdup(nftnl_trace_get_str(nlt, NFTNL_TRACE_JUMP_TARGET)); +- chain_expr = constant_expr_alloc(&netlink_location, +- &string_type, +- BYTEORDER_HOST_ENDIAN, +- strlen(chain) * BITS_PER_BYTE, +- chain); +- } +- expr = verdict_expr_alloc(&netlink_location, verdict, chain_expr); +- +- nft_print(octx, "verdict "); +- expr_print(expr, octx); +- expr_free(expr); +-} +- +-static void trace_print_policy(const struct nftnl_trace *nlt, +- struct output_ctx *octx) +-{ +- unsigned int policy; +- struct expr *expr; +- +- policy = nftnl_trace_get_u32(nlt, NFTNL_TRACE_POLICY); +- +- expr = verdict_expr_alloc(&netlink_location, policy, NULL); +- +- nft_print(octx, "policy "); +- expr_print(expr, octx); +- expr_free(expr); +-} +- +-static struct rule *trace_lookup_rule(const struct nftnl_trace *nlt, +- uint64_t rule_handle, +- struct nft_cache *cache) +-{ +- struct chain *chain; +- struct table *table; +- struct handle h; +- +- h.family = nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY); +- h.table.name = nftnl_trace_get_str(nlt, NFTNL_TRACE_TABLE); +- h.chain.name = nftnl_trace_get_str(nlt, NFTNL_TRACE_CHAIN); +- +- if (!h.table.name) +- return NULL; +- +- table = table_cache_find(&cache->table_cache, h.table.name, h.family); +- if (!table) +- return NULL; +- +- chain = chain_cache_find(table, h.chain.name); +- if (!chain) +- return NULL; +- +- return rule_lookup(chain, rule_handle); +-} +- +-static void trace_print_rule(const struct nftnl_trace *nlt, +- struct output_ctx *octx, struct nft_cache *cache) +-{ +- uint64_t rule_handle; +- struct rule *rule; +- +- rule_handle = nftnl_trace_get_u64(nlt, NFTNL_TRACE_RULE_HANDLE); +- rule = trace_lookup_rule(nlt, rule_handle, cache); +- +- trace_print_hdr(nlt, octx); +- +- if (rule) { +- nft_print(octx, "rule "); +- rule_print(rule, octx); +- } else { +- nft_print(octx, "unknown rule handle %" PRIu64, rule_handle); +- } +- +- nft_print(octx, " ("); +- trace_print_verdict(nlt, octx); +- nft_print(octx, ")\n"); +-} +- +-static void trace_gen_stmts(struct list_head *stmts, +- struct proto_ctx *ctx, struct payload_dep_ctx *pctx, +- const struct nftnl_trace *nlt, unsigned int attr, +- enum proto_bases base) +-{ +- struct list_head unordered = LIST_HEAD_INIT(unordered); +- struct list_head list; +- struct expr *rel, *lhs, *rhs, *tmp, *nexpr; +- struct stmt *stmt; +- const struct proto_desc *desc; +- const void *hdr; +- uint32_t hlen; +- unsigned int n; +- +- if (!nftnl_trace_is_set(nlt, attr)) +- return; +- hdr = nftnl_trace_get_data(nlt, attr, &hlen); +- +- lhs = payload_expr_alloc(&netlink_location, NULL, 0); +- payload_init_raw(lhs, base, 0, hlen * BITS_PER_BYTE); +- rhs = constant_expr_alloc(&netlink_location, +- &invalid_type, BYTEORDER_INVALID, +- hlen * BITS_PER_BYTE, hdr); +- +-restart: +- init_list_head(&list); +- payload_expr_expand(&list, lhs, ctx); +- expr_free(lhs); +- +- desc = NULL; +- list_for_each_entry_safe(lhs, nexpr, &list, list) { +- if (desc && desc != ctx->protocol[base].desc) { +- /* Chained protocols */ +- lhs->payload.offset = 0; +- if (ctx->protocol[base].desc == NULL) +- break; +- goto restart; +- } +- +- tmp = constant_expr_splice(rhs, lhs->len); +- expr_set_type(tmp, lhs->dtype, lhs->byteorder); +- if (tmp->byteorder == BYTEORDER_HOST_ENDIAN) +- mpz_switch_byteorder(tmp->value, tmp->len / BITS_PER_BYTE); +- +- /* Skip unknown and filtered expressions */ +- desc = lhs->payload.desc; +- if (lhs->dtype == &invalid_type || +- lhs->payload.tmpl == &proto_unknown_template || +- desc->checksum_key == payload_hdr_field(lhs) || +- desc->format.filter & (1 << payload_hdr_field(lhs))) { +- expr_free(lhs); +- expr_free(tmp); +- continue; +- } +- +- rel = relational_expr_alloc(&lhs->location, OP_EQ, lhs, tmp); +- stmt = expr_stmt_alloc(&rel->location, rel); +- list_add_tail(&stmt->list, &unordered); +- +- desc = ctx->protocol[base].desc; +- relational_expr_pctx_update(ctx, rel); +- } +- +- expr_free(rhs); +- +- n = 0; +-next: +- list_for_each_entry(stmt, &unordered, list) { +- enum proto_bases b = base; +- +- rel = stmt->expr; +- lhs = rel->left; +- +- /* Move statements to result list in defined order */ +- desc = lhs->payload.desc; +- if (desc->format.order[n] && +- desc->format.order[n] != payload_hdr_field(lhs)) +- continue; +- +- list_move_tail(&stmt->list, stmts); +- n++; +- +- if (payload_is_stacked(desc, rel)) +- b--; +- +- /* Don't strip 'icmp type' from payload dump. */ +- if (pctx->icmp_type == 0) +- payload_dependency_kill(pctx, lhs, ctx->family); +- if (lhs->flags & EXPR_F_PROTOCOL) +- payload_dependency_store(pctx, stmt, b); +- +- goto next; +- } +-} +- +-static void trace_print_packet(const struct nftnl_trace *nlt, +- struct output_ctx *octx) +-{ +- struct list_head stmts = LIST_HEAD_INIT(stmts); +- const struct proto_desc *ll_desc; +- struct payload_dep_ctx pctx = {}; +- struct proto_ctx ctx; +- uint16_t dev_type; +- uint32_t nfproto; +- struct stmt *stmt, *next; +- +- trace_print_hdr(nlt, octx); +- +- nft_print(octx, "packet: "); +- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_IIF)) +- trace_print_expr(nlt, NFTNL_TRACE_IIF, +- meta_expr_alloc(&netlink_location, +- NFT_META_IIF), octx); +- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_OIF)) +- trace_print_expr(nlt, NFTNL_TRACE_OIF, +- meta_expr_alloc(&netlink_location, +- NFT_META_OIF), octx); +- +- proto_ctx_init(&ctx, nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY), 0, false); +- ll_desc = ctx.protocol[PROTO_BASE_LL_HDR].desc; +- if ((ll_desc == &proto_inet || ll_desc == &proto_netdev) && +- nftnl_trace_is_set(nlt, NFTNL_TRACE_NFPROTO)) { +- nfproto = nftnl_trace_get_u32(nlt, NFTNL_TRACE_NFPROTO); +- +- proto_ctx_update(&ctx, PROTO_BASE_LL_HDR, &netlink_location, NULL); +- proto_ctx_update(&ctx, PROTO_BASE_NETWORK_HDR, &netlink_location, +- proto_find_upper(ll_desc, nfproto)); +- } +- if (ctx.protocol[PROTO_BASE_LL_HDR].desc == NULL && +- nftnl_trace_is_set(nlt, NFTNL_TRACE_IIFTYPE)) { +- dev_type = nftnl_trace_get_u16(nlt, NFTNL_TRACE_IIFTYPE); +- proto_ctx_update(&ctx, PROTO_BASE_LL_HDR, &netlink_location, +- proto_dev_desc(dev_type)); +- } +- +- trace_gen_stmts(&stmts, &ctx, &pctx, nlt, NFTNL_TRACE_LL_HEADER, +- PROTO_BASE_LL_HDR); +- trace_gen_stmts(&stmts, &ctx, &pctx, nlt, NFTNL_TRACE_NETWORK_HEADER, +- PROTO_BASE_NETWORK_HDR); +- trace_gen_stmts(&stmts, &ctx, &pctx, nlt, NFTNL_TRACE_TRANSPORT_HEADER, +- PROTO_BASE_TRANSPORT_HDR); +- +- list_for_each_entry_safe(stmt, next, &stmts, list) { +- stmt_print(stmt, octx); +- nft_print(octx, " "); +- stmt_free(stmt); +- } +- nft_print(octx, "\n"); +-} +- +-int netlink_events_trace_cb(const struct nlmsghdr *nlh, int type, +- struct netlink_mon_handler *monh) +-{ +- struct nftnl_trace *nlt; +- +- assert(type == NFT_MSG_TRACE); +- +- nlt = nftnl_trace_alloc(); +- if (!nlt) +- memory_allocation_error(); +- +- if (nftnl_trace_nlmsg_parse(nlh, nlt) < 0) +- netlink_abi_error(); +- +- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_LL_HEADER) || +- nftnl_trace_is_set(nlt, NFTNL_TRACE_NETWORK_HEADER)) +- trace_print_packet(nlt, &monh->ctx->nft->output); +- +- switch (nftnl_trace_get_u32(nlt, NFTNL_TRACE_TYPE)) { +- case NFT_TRACETYPE_RULE: +- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_RULE_HANDLE)) +- trace_print_rule(nlt, &monh->ctx->nft->output, +- &monh->ctx->nft->cache); +- break; +- case NFT_TRACETYPE_POLICY: +- trace_print_hdr(nlt, &monh->ctx->nft->output); +- +- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_POLICY)) { +- trace_print_policy(nlt, &monh->ctx->nft->output); +- nft_mon_print(monh, " "); +- } +- +- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_MARK)) +- trace_print_expr(nlt, NFTNL_TRACE_MARK, +- meta_expr_alloc(&netlink_location, +- NFT_META_MARK), +- &monh->ctx->nft->output); +- nft_mon_print(monh, "\n"); +- break; +- case NFT_TRACETYPE_RETURN: +- trace_print_hdr(nlt, &monh->ctx->nft->output); +- +- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_VERDICT)) { +- trace_print_verdict(nlt, &monh->ctx->nft->output); +- nft_mon_print(monh, " "); +- } +- +- if (nftnl_trace_is_set(nlt, NFTNL_TRACE_MARK)) +- trace_print_expr(nlt, NFTNL_TRACE_MARK, +- meta_expr_alloc(&netlink_location, +- NFT_META_MARK), +- &monh->ctx->nft->output); +- nft_mon_print(monh, "\n"); +- break; +- } +- +- nftnl_trace_free(nlt); +- return MNL_CB_OK; +-} +diff --git a/src/trace.c b/src/trace.c +new file mode 100644 +index 0000000..a7cc8ff +--- /dev/null ++++ b/src/trace.c +@@ -0,0 +1,353 @@ ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define nft_mon_print(monh, ...) nft_print(&monh->ctx->nft->output, __VA_ARGS__) ++ ++static void trace_print_hdr(const struct nftnl_trace *nlt, ++ struct output_ctx *octx) ++{ ++ nft_print(octx, "trace id %08x %s ", ++ nftnl_trace_get_u32(nlt, NFTNL_TRACE_ID), ++ family2str(nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY))); ++ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_TABLE)) ++ nft_print(octx, "%s ", ++ nftnl_trace_get_str(nlt, NFTNL_TRACE_TABLE)); ++ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_CHAIN)) ++ nft_print(octx, "%s ", ++ nftnl_trace_get_str(nlt, NFTNL_TRACE_CHAIN)); ++} ++ ++static void trace_print_expr(const struct nftnl_trace *nlt, unsigned int attr, ++ struct expr *lhs, struct output_ctx *octx) ++{ ++ struct expr *rhs, *rel; ++ const void *data; ++ uint32_t len; ++ ++ data = nftnl_trace_get_data(nlt, attr, &len); ++ rhs = constant_expr_alloc(&netlink_location, ++ lhs->dtype, lhs->byteorder, ++ len * BITS_PER_BYTE, data); ++ rel = relational_expr_alloc(&netlink_location, OP_EQ, lhs, rhs); ++ ++ expr_print(rel, octx); ++ nft_print(octx, " "); ++ expr_free(rel); ++} ++ ++static void trace_print_verdict(const struct nftnl_trace *nlt, ++ struct output_ctx *octx) ++{ ++ struct expr *chain_expr = NULL; ++ const char *chain = NULL; ++ unsigned int verdict; ++ struct expr *expr; ++ ++ verdict = nftnl_trace_get_u32(nlt, NFTNL_TRACE_VERDICT); ++ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_JUMP_TARGET)) { ++ chain = xstrdup(nftnl_trace_get_str(nlt, NFTNL_TRACE_JUMP_TARGET)); ++ chain_expr = constant_expr_alloc(&netlink_location, ++ &string_type, ++ BYTEORDER_HOST_ENDIAN, ++ strlen(chain) * BITS_PER_BYTE, ++ chain); ++ } ++ expr = verdict_expr_alloc(&netlink_location, verdict, chain_expr); ++ ++ nft_print(octx, "verdict "); ++ expr_print(expr, octx); ++ expr_free(expr); ++} ++ ++static void trace_print_policy(const struct nftnl_trace *nlt, ++ struct output_ctx *octx) ++{ ++ unsigned int policy; ++ struct expr *expr; ++ ++ policy = nftnl_trace_get_u32(nlt, NFTNL_TRACE_POLICY); ++ ++ expr = verdict_expr_alloc(&netlink_location, policy, NULL); ++ ++ nft_print(octx, "policy "); ++ expr_print(expr, octx); ++ expr_free(expr); ++} ++ ++static struct rule *trace_lookup_rule(const struct nftnl_trace *nlt, ++ uint64_t rule_handle, ++ struct nft_cache *cache) ++{ ++ struct chain *chain; ++ struct table *table; ++ struct handle h; ++ ++ h.family = nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY); ++ h.table.name = nftnl_trace_get_str(nlt, NFTNL_TRACE_TABLE); ++ h.chain.name = nftnl_trace_get_str(nlt, NFTNL_TRACE_CHAIN); ++ ++ if (!h.table.name) ++ return NULL; ++ ++ table = table_cache_find(&cache->table_cache, h.table.name, h.family); ++ if (!table) ++ return NULL; ++ ++ chain = chain_cache_find(table, h.chain.name); ++ if (!chain) ++ return NULL; ++ ++ return rule_lookup(chain, rule_handle); ++} ++ ++static void trace_print_rule(const struct nftnl_trace *nlt, ++ struct output_ctx *octx, struct nft_cache *cache) ++{ ++ uint64_t rule_handle; ++ struct rule *rule; ++ ++ rule_handle = nftnl_trace_get_u64(nlt, NFTNL_TRACE_RULE_HANDLE); ++ rule = trace_lookup_rule(nlt, rule_handle, cache); ++ ++ trace_print_hdr(nlt, octx); ++ ++ if (rule) { ++ nft_print(octx, "rule "); ++ rule_print(rule, octx); ++ } else { ++ nft_print(octx, "unknown rule handle %" PRIu64, rule_handle); ++ } ++ ++ nft_print(octx, " ("); ++ trace_print_verdict(nlt, octx); ++ nft_print(octx, ")\n"); ++} ++ ++static void trace_gen_stmts(struct list_head *stmts, ++ struct proto_ctx *ctx, struct payload_dep_ctx *pctx, ++ const struct nftnl_trace *nlt, unsigned int attr, ++ enum proto_bases base) ++{ ++ struct list_head unordered = LIST_HEAD_INIT(unordered); ++ struct list_head list; ++ struct expr *rel, *lhs, *rhs, *tmp, *nexpr; ++ struct stmt *stmt; ++ const struct proto_desc *desc; ++ const void *hdr; ++ uint32_t hlen; ++ unsigned int n; ++ ++ if (!nftnl_trace_is_set(nlt, attr)) ++ return; ++ hdr = nftnl_trace_get_data(nlt, attr, &hlen); ++ ++ lhs = payload_expr_alloc(&netlink_location, NULL, 0); ++ payload_init_raw(lhs, base, 0, hlen * BITS_PER_BYTE); ++ rhs = constant_expr_alloc(&netlink_location, ++ &invalid_type, BYTEORDER_INVALID, ++ hlen * BITS_PER_BYTE, hdr); ++ ++restart: ++ init_list_head(&list); ++ payload_expr_expand(&list, lhs, ctx); ++ expr_free(lhs); ++ ++ desc = NULL; ++ list_for_each_entry_safe(lhs, nexpr, &list, list) { ++ if (desc && desc != ctx->protocol[base].desc) { ++ /* Chained protocols */ ++ lhs->payload.offset = 0; ++ if (ctx->protocol[base].desc == NULL) ++ break; ++ goto restart; ++ } ++ ++ tmp = constant_expr_splice(rhs, lhs->len); ++ expr_set_type(tmp, lhs->dtype, lhs->byteorder); ++ if (tmp->byteorder == BYTEORDER_HOST_ENDIAN) ++ mpz_switch_byteorder(tmp->value, tmp->len / BITS_PER_BYTE); ++ ++ /* Skip unknown and filtered expressions */ ++ desc = lhs->payload.desc; ++ if (lhs->dtype == &invalid_type || ++ lhs->payload.tmpl == &proto_unknown_template || ++ desc->checksum_key == payload_hdr_field(lhs) || ++ desc->format.filter & (1 << payload_hdr_field(lhs))) { ++ expr_free(lhs); ++ expr_free(tmp); ++ continue; ++ } ++ ++ rel = relational_expr_alloc(&lhs->location, OP_EQ, lhs, tmp); ++ stmt = expr_stmt_alloc(&rel->location, rel); ++ list_add_tail(&stmt->list, &unordered); ++ ++ desc = ctx->protocol[base].desc; ++ relational_expr_pctx_update(ctx, rel); ++ } ++ ++ expr_free(rhs); ++ ++ n = 0; ++next: ++ list_for_each_entry(stmt, &unordered, list) { ++ enum proto_bases b = base; ++ ++ rel = stmt->expr; ++ lhs = rel->left; ++ ++ /* Move statements to result list in defined order */ ++ desc = lhs->payload.desc; ++ if (desc->format.order[n] && ++ desc->format.order[n] != payload_hdr_field(lhs)) ++ continue; ++ ++ list_move_tail(&stmt->list, stmts); ++ n++; ++ ++ if (payload_is_stacked(desc, rel)) ++ b--; ++ ++ /* Don't strip 'icmp type' from payload dump. */ ++ if (pctx->icmp_type == 0) ++ payload_dependency_kill(pctx, lhs, ctx->family); ++ if (lhs->flags & EXPR_F_PROTOCOL) ++ payload_dependency_store(pctx, stmt, b); ++ ++ goto next; ++ } ++} ++ ++static void trace_print_packet(const struct nftnl_trace *nlt, ++ struct output_ctx *octx) ++{ ++ struct list_head stmts = LIST_HEAD_INIT(stmts); ++ const struct proto_desc *ll_desc; ++ struct payload_dep_ctx pctx = {}; ++ struct proto_ctx ctx; ++ uint16_t dev_type; ++ uint32_t nfproto; ++ struct stmt *stmt, *next; ++ ++ trace_print_hdr(nlt, octx); ++ ++ nft_print(octx, "packet: "); ++ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_IIF)) ++ trace_print_expr(nlt, NFTNL_TRACE_IIF, ++ meta_expr_alloc(&netlink_location, ++ NFT_META_IIF), octx); ++ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_OIF)) ++ trace_print_expr(nlt, NFTNL_TRACE_OIF, ++ meta_expr_alloc(&netlink_location, ++ NFT_META_OIF), octx); ++ ++ proto_ctx_init(&ctx, nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY), 0, false); ++ ll_desc = ctx.protocol[PROTO_BASE_LL_HDR].desc; ++ if ((ll_desc == &proto_inet || ll_desc == &proto_netdev) && ++ nftnl_trace_is_set(nlt, NFTNL_TRACE_NFPROTO)) { ++ nfproto = nftnl_trace_get_u32(nlt, NFTNL_TRACE_NFPROTO); ++ ++ proto_ctx_update(&ctx, PROTO_BASE_LL_HDR, &netlink_location, NULL); ++ proto_ctx_update(&ctx, PROTO_BASE_NETWORK_HDR, &netlink_location, ++ proto_find_upper(ll_desc, nfproto)); ++ } ++ if (ctx.protocol[PROTO_BASE_LL_HDR].desc == NULL && ++ nftnl_trace_is_set(nlt, NFTNL_TRACE_IIFTYPE)) { ++ dev_type = nftnl_trace_get_u16(nlt, NFTNL_TRACE_IIFTYPE); ++ proto_ctx_update(&ctx, PROTO_BASE_LL_HDR, &netlink_location, ++ proto_dev_desc(dev_type)); ++ } ++ ++ trace_gen_stmts(&stmts, &ctx, &pctx, nlt, NFTNL_TRACE_LL_HEADER, ++ PROTO_BASE_LL_HDR); ++ trace_gen_stmts(&stmts, &ctx, &pctx, nlt, NFTNL_TRACE_NETWORK_HEADER, ++ PROTO_BASE_NETWORK_HDR); ++ trace_gen_stmts(&stmts, &ctx, &pctx, nlt, NFTNL_TRACE_TRANSPORT_HEADER, ++ PROTO_BASE_TRANSPORT_HDR); ++ ++ list_for_each_entry_safe(stmt, next, &stmts, list) { ++ stmt_print(stmt, octx); ++ nft_print(octx, " "); ++ stmt_free(stmt); ++ } ++ nft_print(octx, "\n"); ++} ++ ++int netlink_events_trace_cb(const struct nlmsghdr *nlh, int type, ++ struct netlink_mon_handler *monh) ++{ ++ struct nftnl_trace *nlt; ++ ++ assert(type == NFT_MSG_TRACE); ++ ++ nlt = nftnl_trace_alloc(); ++ if (!nlt) ++ memory_allocation_error(); ++ ++ if (nftnl_trace_nlmsg_parse(nlh, nlt) < 0) ++ netlink_abi_error(); ++ ++ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_LL_HEADER) || ++ nftnl_trace_is_set(nlt, NFTNL_TRACE_NETWORK_HEADER)) ++ trace_print_packet(nlt, &monh->ctx->nft->output); ++ ++ switch (nftnl_trace_get_u32(nlt, NFTNL_TRACE_TYPE)) { ++ case NFT_TRACETYPE_RULE: ++ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_RULE_HANDLE)) ++ trace_print_rule(nlt, &monh->ctx->nft->output, ++ &monh->ctx->nft->cache); ++ break; ++ case NFT_TRACETYPE_POLICY: ++ trace_print_hdr(nlt, &monh->ctx->nft->output); ++ ++ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_POLICY)) { ++ trace_print_policy(nlt, &monh->ctx->nft->output); ++ nft_mon_print(monh, " "); ++ } ++ ++ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_MARK)) ++ trace_print_expr(nlt, NFTNL_TRACE_MARK, ++ meta_expr_alloc(&netlink_location, ++ NFT_META_MARK), ++ &monh->ctx->nft->output); ++ nft_mon_print(monh, "\n"); ++ break; ++ case NFT_TRACETYPE_RETURN: ++ trace_print_hdr(nlt, &monh->ctx->nft->output); ++ ++ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_VERDICT)) { ++ trace_print_verdict(nlt, &monh->ctx->nft->output); ++ nft_mon_print(monh, " "); ++ } ++ ++ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_MARK)) ++ trace_print_expr(nlt, NFTNL_TRACE_MARK, ++ meta_expr_alloc(&netlink_location, ++ NFT_META_MARK), ++ &monh->ctx->nft->output); ++ nft_mon_print(monh, "\n"); ++ break; ++ } ++ ++ nftnl_trace_free(nlt); ++ return MNL_CB_OK; ++} diff --git a/0016-src-add-conntrack-information-to-trace-monitor-mode.patch b/0016-src-add-conntrack-information-to-trace-monitor-mode.patch new file mode 100644 index 0000000..039fbed --- /dev/null +++ b/0016-src-add-conntrack-information-to-trace-monitor-mode.patch @@ -0,0 +1,268 @@ +From 0d28ee52a20e8441f66dc11b690fb595f63db6a3 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 15 Jul 2025 22:50:32 +0200 +Subject: [PATCH] src: add conntrack information to trace monitor mode + +JIRA: https://issues.redhat.com/browse/RHEL-102994 +Upstream Status: nftables commit cfd768615235bb89650f15498c70d19813502825 + +commit cfd768615235bb89650f15498c70d19813502825 +Author: Florian Westphal +Date: Mon Jul 7 22:38:13 2025 +0200 + + src: add conntrack information to trace monitor mode + + Upcoming kernel change provides the packets conntrack state in the + trace message data. + + This allows to see if packet is seen as original or reply, the conntrack + state (new, establieshed, related) and the status bits which show if e.g. + NAT was applied. Alsoi include conntrack ID so users can use conntrack + tool to query the kernel for more information via ctnetlink. + + This improves debugging when e.g. packets do not pick up the expected + NAT mapping, which could e.g. also happen because of expectations + following the NAT binding of the owning conntrack entry. + + Example output ("conntrack: " lines are new): + + trace id 32 t PRE_RAW packet: iif "enp0s3" ether saddr [..] + trace id 32 t PRE_RAW rule tcp flags syn meta nftrace set 1 (verdict continue) + trace id 32 t PRE_RAW policy accept + trace id 32 t PRE_MANGLE conntrack: ct direction original ct state new ct id 2641368242 + trace id 32 t PRE_MANGLE packet: iif "enp0s3" ether saddr [..] + trace id 32 t ct_new_pre rule jump rpfilter (verdict jump rpfilter) + trace id 32 t PRE_MANGLE policy accept + trace id 32 t INPUT conntrack: ct direction original ct state new ct status dnat-done ct id 2641368242 + trace id 32 t INPUT packet: iif "enp0s3" [..] + trace id 32 t public_in rule tcp dport 443 accept (verdict accept) + + v3: remove clash bit again, kernel won't expose it anymore. + v2: add more status bits: helper, clash, offload, hw-offload. + add flag explanation to documentation. + + Signed-off-by: Florian Westphal + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + doc/data-types.txt | 30 ++--- + include/linux/netfilter/nf_conntrack_common.h | 16 +++ + src/ct.c | 7 ++ + src/trace.c | 109 ++++++++++++++++++ + 4 files changed, 147 insertions(+), 15 deletions(-) + +diff --git a/doc/data-types.txt b/doc/data-types.txt +index 6c0e2f9..abbb7fd 100644 +--- a/doc/data-types.txt ++++ b/doc/data-types.txt +@@ -378,21 +378,21 @@ For each of the types above, keywords are available for convenience: + .conntrack status (ct_status) + [options="header"] + |================== +-|Keyword| Value +-|expected| +-1 +-|seen-reply| +-2 +-|assured| +-4 +-|confirmed| +-8 +-|snat| +-16 +-|dnat| +-32 +-|dying| +-512 ++|Keyword| Value | Description ++|expected|1| Expected connection; conntrack helper set it up ++|seen-reply|2| Conntrack has seen packets in both directions ++|assured| 4 |Conntrack entry will not be removed if hash table is full ++|confirmed | 8 | Initial packet processed ++|snat| 16 | Original source address differs from reply destination ++|dnat| 32 | Original destination differs from reply source ++|seq-adjust| 64 | tcp sequence number rewrite due to conntrack helper or synproxy ++|snat-done| 128 | tried to find matching snat/masquerade rule ++|dnat-done| 256 | tried to find matching dnat/redirect rule ++|dying| 512 | Connection about to be deleted ++|fixed-timeout | 1024 | entry expires even if traffic is active ++|helper | 8192 | connection is monitored by conntrack helper ++|offload | 16384 | connection is offloaded to a flow table ++|hw-offload | 32768 | connection is offloaded to hardware + |================ + + .conntrack event bits (ct_event) +diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h +index 768ff25..22bbb6c 100644 +--- a/include/linux/netfilter/nf_conntrack_common.h ++++ b/include/linux/netfilter/nf_conntrack_common.h +@@ -77,6 +77,22 @@ enum ip_conntrack_status { + /* Connection has fixed timeout. */ + IPS_FIXED_TIMEOUT_BIT = 10, + IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT), ++ ++ /* Conntrack is a fake untracked entry. Obsolete and not used anymore */ ++ IPS_UNTRACKED_BIT = 12, ++ IPS_UNTRACKED = (1 << IPS_UNTRACKED_BIT), ++ ++ /* Conntrack got a helper explicitly attached (ruleset, ctnetlink). */ ++ IPS_HELPER_BIT = 13, ++ IPS_HELPER = (1 << IPS_HELPER_BIT), ++ ++ /* Conntrack has been offloaded to flow table. */ ++ IPS_OFFLOAD_BIT = 14, ++ IPS_OFFLOAD = (1 << IPS_OFFLOAD_BIT), ++ ++ /* Conntrack has been offloaded to hardware. */ ++ IPS_HW_OFFLOAD_BIT = 15, ++ IPS_HW_OFFLOAD = (1 << IPS_HW_OFFLOAD_BIT), + }; + + /* Connection tracking event types */ +diff --git a/src/ct.c b/src/ct.c +index 6793464..cd97d82 100644 +--- a/src/ct.c ++++ b/src/ct.c +@@ -98,7 +98,14 @@ static const struct symbol_table ct_status_tbl = { + SYMBOL("confirmed", IPS_CONFIRMED), + SYMBOL("snat", IPS_SRC_NAT), + SYMBOL("dnat", IPS_DST_NAT), ++ SYMBOL("seq-adjust", IPS_SEQ_ADJUST), ++ SYMBOL("snat-done", IPS_SRC_NAT_DONE), ++ SYMBOL("dnat-done", IPS_DST_NAT_DONE), + SYMBOL("dying", IPS_DYING), ++ SYMBOL("fixed-timeout", IPS_FIXED_TIMEOUT), ++ SYMBOL("helper", IPS_HELPER_BIT), ++ SYMBOL("offload", IPS_OFFLOAD_BIT), ++ SYMBOL("hw-offload", IPS_HW_OFFLOAD_BIT), + SYMBOL_LIST_END + }, + }; +diff --git a/src/trace.c b/src/trace.c +index a7cc8ff..b270951 100644 +--- a/src/trace.c ++++ b/src/trace.c +@@ -237,6 +237,114 @@ next: + } + } + ++static struct expr *trace_alloc_list(const struct datatype *dtype, ++ enum byteorder byteorder, ++ unsigned int len, const void *data) ++{ ++ struct expr *list_expr; ++ unsigned int i; ++ mpz_t value; ++ uint32_t v; ++ ++ if (len != sizeof(v)) ++ return constant_expr_alloc(&netlink_location, ++ dtype, byteorder, ++ len * BITS_PER_BYTE, data); ++ ++ list_expr = list_expr_alloc(&netlink_location); ++ ++ mpz_init2(value, 32); ++ mpz_import_data(value, data, byteorder, len); ++ v = mpz_get_uint32(value); ++ if (v == 0) { ++ mpz_clear(value); ++ return NULL; ++ } ++ ++ for (i = 0; i < 32; i++) { ++ uint32_t bitv = v & (1 << i); ++ ++ if (bitv == 0) ++ continue; ++ ++ compound_expr_add(list_expr, ++ constant_expr_alloc(&netlink_location, ++ dtype, byteorder, ++ len * BITS_PER_BYTE, ++ &bitv)); ++ } ++ ++ mpz_clear(value); ++ return list_expr; ++} ++ ++static void trace_print_ct_expr(const struct nftnl_trace *nlt, unsigned int attr, ++ enum nft_ct_keys key, struct output_ctx *octx) ++{ ++ struct expr *lhs, *rhs, *rel; ++ const void *data; ++ uint32_t len; ++ ++ data = nftnl_trace_get_data(nlt, attr, &len); ++ lhs = ct_expr_alloc(&netlink_location, key, -1); ++ ++ switch (key) { ++ case NFT_CT_STATUS: ++ rhs = trace_alloc_list(lhs->dtype, lhs->byteorder, len, data); ++ if (!rhs) { ++ expr_free(lhs); ++ return; ++ } ++ rel = binop_expr_alloc(&netlink_location, OP_IMPLICIT, lhs, rhs); ++ break; ++ case NFT_CT_DIRECTION: ++ case NFT_CT_STATE: ++ case NFT_CT_ID: ++ /* fallthrough */ ++ default: ++ rhs = constant_expr_alloc(&netlink_location, ++ lhs->dtype, lhs->byteorder, ++ len * BITS_PER_BYTE, data); ++ rel = relational_expr_alloc(&netlink_location, OP_IMPLICIT, lhs, rhs); ++ break; ++ } ++ ++ expr_print(rel, octx); ++ nft_print(octx, " "); ++ expr_free(rel); ++} ++ ++static void trace_print_ct(const struct nftnl_trace *nlt, ++ struct output_ctx *octx) ++{ ++ bool ct = nftnl_trace_is_set(nlt, NFTNL_TRACE_CT_STATE); ++ ++ if (!ct) ++ return; ++ ++ trace_print_hdr(nlt, octx); ++ ++ nft_print(octx, "conntrack: "); ++ ++ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_CT_DIRECTION)) ++ trace_print_ct_expr(nlt, NFTNL_TRACE_CT_DIRECTION, ++ NFT_CT_DIRECTION, octx); ++ ++ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_CT_STATE)) ++ trace_print_ct_expr(nlt, NFTNL_TRACE_CT_STATE, ++ NFT_CT_STATE, octx); ++ ++ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_CT_STATUS)) ++ trace_print_ct_expr(nlt, NFTNL_TRACE_CT_STATUS, ++ NFT_CT_STATUS, octx); ++ ++ if (nftnl_trace_is_set(nlt, NFTNL_TRACE_CT_ID)) ++ trace_print_ct_expr(nlt, NFTNL_TRACE_CT_ID, ++ NFT_CT_ID, octx); ++ ++ nft_print(octx, "\n"); ++} ++ + static void trace_print_packet(const struct nftnl_trace *nlt, + struct output_ctx *octx) + { +@@ -248,6 +356,7 @@ static void trace_print_packet(const struct nftnl_trace *nlt, + uint32_t nfproto; + struct stmt *stmt, *next; + ++ trace_print_ct(nlt, octx); + trace_print_hdr(nlt, octx); + + nft_print(octx, "packet: "); diff --git a/0017-trace-Fix-for-memleak-in-trace_alloc_list-error-path.patch b/0017-trace-Fix-for-memleak-in-trace_alloc_list-error-path.patch new file mode 100644 index 0000000..82e6231 --- /dev/null +++ b/0017-trace-Fix-for-memleak-in-trace_alloc_list-error-path.patch @@ -0,0 +1,38 @@ +From 5ed024ecfaf596ec0298f8ad75c5695f9889464c Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 9 Sep 2025 16:27:52 +0200 +Subject: [PATCH] trace: Fix for memleak in trace_alloc_list() error path + +JIRA: https://issues.redhat.com/browse/RHEL-111205 +Upstream Status: nftables commit fdbb0ec57b5c891c1de17f367b693ab787ea9c2d + +commit fdbb0ec57b5c891c1de17f367b693ab787ea9c2d +Author: Phil Sutter +Date: Tue Aug 26 12:57:37 2025 +0200 + + trace: Fix for memleak in trace_alloc_list() error path + + The allocated 'list_expr' may leak. + + Fixes: cfd768615235b ("src: add conntrack information to trace monitor mode") + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + Signed-off-by: Florian Westphal + +Signed-off-by: Phil Sutter +--- + src/trace.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/trace.c b/src/trace.c +index b270951..47f4c21 100644 +--- a/src/trace.c ++++ b/src/trace.c +@@ -258,6 +258,7 @@ static struct expr *trace_alloc_list(const struct datatype *dtype, + v = mpz_get_uint32(value); + if (v == 0) { + mpz_clear(value); ++ expr_free(list_expr); + return NULL; + } + diff --git a/0018-doc-nft.8-Minor-NAT-STATEMENTS-section-review.patch b/0018-doc-nft.8-Minor-NAT-STATEMENTS-section-review.patch new file mode 100644 index 0000000..91eb680 --- /dev/null +++ b/0018-doc-nft.8-Minor-NAT-STATEMENTS-section-review.patch @@ -0,0 +1,78 @@ +From 3bb2e6c3d03fa60724ab72b96d1e97fa02d7eed9 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 9 Sep 2025 16:53:21 +0200 +Subject: [PATCH] doc: nft.8: Minor NAT STATEMENTS section review + +JIRA: https://issues.redhat.com/browse/RHEL-106743 +Upstream Status: nftables commit 9e1cbf667da2b9c30b41ff887de212b2c38b2eb7 + +commit 9e1cbf667da2b9c30b41ff887de212b2c38b2eb7 +Author: Phil Sutter +Date: Thu Jul 31 12:40:11 2025 +0200 + + doc: nft.8: Minor NAT STATEMENTS section review + + Synopsis insinuates an IP address argument is mandatory in snat/dnat + statements although specifying ports alone is perfectly fine. Adjust it + accordingly and add a paragraph briefly describing the behaviour. + + While at it, update the redirect statement description with more + relevant examples, the current one is wrong: To *only* alter the + destination port, dnat statement must be used, not redirect. + + Fixes: 6908a677ba04c ("nft.8: Enhance NAT documentation") + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + doc/statements.txt | 17 +++++++++++------ + 1 file changed, 11 insertions(+), 6 deletions(-) + +diff --git a/doc/statements.txt b/doc/statements.txt +index 74af1d1..7fe9ed3 100644 +--- a/doc/statements.txt ++++ b/doc/statements.txt +@@ -399,11 +399,12 @@ NAT STATEMENTS + ~~~~~~~~~~~~~~ + [verse] + ____ +-*snat* [[*ip* | *ip6*] [ *prefix* ] *to*] 'ADDR_SPEC' [*:*'PORT_SPEC'] ['FLAGS'] +-*dnat* [[*ip* | *ip6*] [ *prefix* ] *to*] 'ADDR_SPEC' [*:*'PORT_SPEC'] ['FLAGS'] ++*snat* [[*ip* | *ip6*] [ *prefix* ] *to*] 'TARGET_SPEC' ['FLAGS'] ++*dnat* [[*ip* | *ip6*] [ *prefix* ] *to*] 'TARGET_SPEC' ['FLAGS'] + *masquerade* [*to :*'PORT_SPEC'] ['FLAGS'] + *redirect* [*to :*'PORT_SPEC'] ['FLAGS'] + ++'TARGET_SPEC' := 'ADDR_SPEC' | ['ADDR_SPEC'] *:*'PORT_SPEC' + 'ADDR_SPEC' := 'address' | 'address' *-* 'address' + 'PORT_SPEC' := 'port' | 'port' *-* 'port' + +@@ -413,11 +414,11 @@ ____ + + The nat statements are only valid from nat chain types. + + +-The *snat* and *masquerade* statements specify that the source address of the ++The *snat* and *masquerade* statements specify that the source address/port of the + packet should be modified. While *snat* is only valid in the postrouting and + input chains, *masquerade* makes sense only in postrouting. The dnat and + redirect statements are only valid in the prerouting and output chains, they +-specify that the destination address of the packet should be modified. You can ++specify that the destination address/port of the packet should be modified. You can + use non-base chains which are called from base chains of nat chain type too. + All future packets in this connection will also be mangled, and rules should + cease being examined. +@@ -427,8 +428,12 @@ outgoing interface's IP address to translate to. It is particularly useful on + gateways with dynamic (public) IP addresses. + + The *redirect* statement is a special form of dnat which always translates the +-destination address to the local host's one. It comes in handy if one only wants +-to alter the destination port of incoming traffic on different interfaces. ++destination address to the local host's one. It comes in handy to intercept ++traffic passing a router and feeding it to a locally running daemon, e.g. when ++building a transparent proxy or application-layer gateway. ++ ++For 'TARGET_SPEC', one may specify addresses, ports, or both. If no address or ++no port is specified, the respective packet header field remains unchanged. + + When used in the inet family (available with kernel 5.2), the dnat and snat + statements require the use of the ip and ip6 keyword in case an address is diff --git a/0019-table-Embed-creating-nft-version-into-userdata.patch b/0019-table-Embed-creating-nft-version-into-userdata.patch new file mode 100644 index 0000000..8c65510 --- /dev/null +++ b/0019-table-Embed-creating-nft-version-into-userdata.patch @@ -0,0 +1,232 @@ +From 50afb84d7064806ad7acc8364455062fc0751528 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 9 Sep 2025 16:49:27 +0200 +Subject: [PATCH] table: Embed creating nft version into userdata + +JIRA: https://issues.redhat.com/browse/RHEL-108851 +Upstream Status: nftables commit 64c07e38f0494093a399a68a31056f5866c4d705 +Conflicts: Context change due to missing --with-unitdir option. + +commit 64c07e38f0494093a399a68a31056f5866c4d705 +Author: Phil Sutter +Date: Mon May 12 22:59:26 2025 +0200 + + table: Embed creating nft version into userdata + + Upon listing a table which was created by a newer version of nftables, + warn about the potentially incomplete content. + + Suggested-by: Florian Westphal + Cc: Dan Winship + Signed-off-by: Phil Sutter + Acked-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + Makefile.am | 3 +++ + configure.ac | 24 ++++++++++++++++++++++++ + include/rule.h | 1 + + src/mnl.c | 21 +++++++++++++++------ + src/netlink.c | 33 +++++++++++++++++++++++++++++++++ + src/rule.c | 4 ++++ + 6 files changed, 80 insertions(+), 6 deletions(-) + +diff --git a/Makefile.am b/Makefile.am +index ba09e7f..c2a6908 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -33,6 +33,7 @@ sbin_PROGRAMS = + check_PROGRAMS = + dist_man_MANS = + CLEANFILES = ++DISTCLEANFILES = + + ############################################################################### + +@@ -105,6 +106,8 @@ noinst_HEADERS = \ + \ + $(NULL) + ++DISTCLEANFILES += nftversion.h ++ + ############################################################################### + + AM_CPPFLAGS = \ +diff --git a/configure.ac b/configure.ac +index 816e920..bac8319 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -114,6 +114,30 @@ AC_CHECK_DECLS([getprotobyname_r, getprotobynumber_r, getservbyport_r], [], [], + #include + ]]) + ++AC_ARG_WITH([stable-release], [AS_HELP_STRING([--with-stable-release], ++ [Stable release number])], ++ [], [with_stable_release=0]) ++AC_CONFIG_COMMANDS([stable_release], ++ [STABLE_RELEASE=$stable_release], ++ [stable_release=$with_stable_release]) ++AC_CONFIG_COMMANDS([nftversion.h], [ ++( ++ echo "static char nftversion[[]] = {" ++ echo " ${VERSION}," | tr '.' ',' ++ echo " ${STABLE_RELEASE}" ++ echo "};" ++ echo "static char nftbuildstamp[[]] = {" ++ for ((i = 56; i >= 0; i-= 8)); do ++ echo " ((uint64_t)MAKE_STAMP >> $i) & 0xff," ++ done ++ echo "};" ++) >nftversion.h ++]) ++# Current date should be fetched exactly once per build, ++# so have 'make' call date and pass the value to every 'gcc' call ++AC_SUBST([MAKE_STAMP], ["\$(shell date +%s)"]) ++CFLAGS="${CFLAGS} -DMAKE_STAMP=\${MAKE_STAMP}" ++ + AC_CONFIG_FILES([ \ + Makefile \ + libnftables.pc \ +diff --git a/include/rule.h b/include/rule.h +index 238be23..1b52972 100644 +--- a/include/rule.h ++++ b/include/rule.h +@@ -170,6 +170,7 @@ struct table { + uint32_t owner; + const char *comment; + bool has_xt_stmts; ++ bool is_from_future; + }; + + extern struct table *table_alloc(void); +diff --git a/src/mnl.c b/src/mnl.c +index 12a6345..e748ab6 100644 +--- a/src/mnl.c ++++ b/src/mnl.c +@@ -10,6 +10,7 @@ + + #include + #include ++#include + + #include + #include +@@ -1054,24 +1055,32 @@ int mnl_nft_table_add(struct netlink_ctx *ctx, struct cmd *cmd, + if (nlt == NULL) + memory_allocation_error(); + ++ udbuf = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN); ++ if (!udbuf) ++ memory_allocation_error(); ++ + nftnl_table_set_u32(nlt, NFTNL_TABLE_FAMILY, cmd->handle.family); + if (cmd->table) { + nftnl_table_set_u32(nlt, NFTNL_TABLE_FLAGS, cmd->table->flags); + + if (cmd->table->comment) { +- udbuf = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN); +- if (!udbuf) +- memory_allocation_error(); + if (!nftnl_udata_put_strz(udbuf, NFTNL_UDATA_TABLE_COMMENT, cmd->table->comment)) + memory_allocation_error(); +- nftnl_table_set_data(nlt, NFTNL_TABLE_USERDATA, nftnl_udata_buf_data(udbuf), +- nftnl_udata_buf_len(udbuf)); +- nftnl_udata_buf_free(udbuf); + } + } else { + nftnl_table_set_u32(nlt, NFTNL_TABLE_FLAGS, 0); + } + ++ if (!nftnl_udata_put(udbuf, NFTNL_UDATA_TABLE_NFTVER, ++ sizeof(nftversion), nftversion) || ++ !nftnl_udata_put(udbuf, NFTNL_UDATA_TABLE_NFTBLD, ++ sizeof(nftbuildstamp), nftbuildstamp)) ++ memory_allocation_error(); ++ nftnl_table_set_data(nlt, NFTNL_TABLE_USERDATA, ++ nftnl_udata_buf_data(udbuf), ++ nftnl_udata_buf_len(udbuf)); ++ nftnl_udata_buf_free(udbuf); ++ + nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch), + NFT_MSG_NEWTABLE, + cmd->handle.family, +diff --git a/src/netlink.c b/src/netlink.c +index 2ced863..7f9730d 100644 +--- a/src/netlink.c ++++ b/src/netlink.c +@@ -10,6 +10,7 @@ + */ + + #include ++#include + + #include + #include +@@ -728,6 +729,14 @@ static int table_parse_udata_cb(const struct nftnl_udata *attr, void *data) + if (value[len - 1] != '\0') + return -1; + break; ++ case NFTNL_UDATA_TABLE_NFTVER: ++ if (len != sizeof(nftversion)) ++ return -1; ++ break; ++ case NFTNL_UDATA_TABLE_NFTBLD: ++ if (len != sizeof(nftbuildstamp)) ++ return -1; ++ break; + default: + return 0; + } +@@ -735,6 +744,29 @@ static int table_parse_udata_cb(const struct nftnl_udata *attr, void *data) + return 0; + } + ++static int version_cmp(const struct nftnl_udata **ud) ++{ ++ const char *udbuf; ++ size_t i; ++ ++ /* netlink attribute lengths checked by table_parse_udata_cb() */ ++ if (ud[NFTNL_UDATA_TABLE_NFTVER]) { ++ udbuf = nftnl_udata_get(ud[NFTNL_UDATA_TABLE_NFTVER]); ++ for (i = 0; i < sizeof(nftversion); i++) { ++ if (nftversion[i] != udbuf[i]) ++ return nftversion[i] - udbuf[i]; ++ } ++ } ++ if (ud[NFTNL_UDATA_TABLE_NFTBLD]) { ++ udbuf = nftnl_udata_get(ud[NFTNL_UDATA_TABLE_NFTBLD]); ++ for (i = 0; i < sizeof(nftbuildstamp); i++) { ++ if (nftbuildstamp[i] != udbuf[i]) ++ return nftbuildstamp[i] - udbuf[i]; ++ } ++ } ++ return 0; ++} ++ + struct table *netlink_delinearize_table(struct netlink_ctx *ctx, + const struct nftnl_table *nlt) + { +@@ -759,6 +791,7 @@ struct table *netlink_delinearize_table(struct netlink_ctx *ctx, + } + if (ud[NFTNL_UDATA_TABLE_COMMENT]) + table->comment = xstrdup(nftnl_udata_get(ud[NFTNL_UDATA_TABLE_COMMENT])); ++ table->is_from_future = version_cmp(ud) < 0; + } + + return table; +diff --git a/src/rule.c b/src/rule.c +index 151ed53..e4d6f53 100644 +--- a/src/rule.c ++++ b/src/rule.c +@@ -1274,6 +1274,10 @@ static void table_print(const struct table *table, struct output_ctx *octx) + fprintf(octx->error_fp, + "# Warning: table %s %s is managed by iptables-nft, do not touch!\n", + family, table->handle.table.name); ++ if (table->is_from_future) ++ fprintf(octx->error_fp, ++ "# Warning: table %s %s was created by a newer version of nftables? Content may be incomplete!\n", ++ family, table->handle.table.name); + + nft_print(octx, "table %s %s {", family, table->handle.table.name); + if (nft_output_handle(octx) || table->flags & TABLE_F_OWNER) diff --git a/0020-Makefile-Fix-for-make-CFLAGS.patch b/0020-Makefile-Fix-for-make-CFLAGS.patch new file mode 100644 index 0000000..8f74b90 --- /dev/null +++ b/0020-Makefile-Fix-for-make-CFLAGS.patch @@ -0,0 +1,52 @@ +From 575c0a20b143f5487a184c2c5c866dd8b14a69f5 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 9 Sep 2025 16:50:13 +0200 +Subject: [PATCH] Makefile: Fix for 'make CFLAGS=...' + +JIRA: https://issues.redhat.com/browse/RHEL-108851 +Upstream Status: nftables commit 229fa8b440b67debb12beca830e57a9ea25a2745 + +commit 229fa8b440b67debb12beca830e57a9ea25a2745 +Author: Phil Sutter +Date: Tue Sep 9 00:14:16 2025 +0200 + + Makefile: Fix for 'make CFLAGS=...' + + Appending to CFLAGS from configure.ac like this was too naive, passing + custom CFLAGS in make arguments overwrites it. Extend AM_CFLAGS instead. + + Fixes: 64c07e38f0494 ("table: Embed creating nft version into userdata") + Signed-off-by: Phil Sutter + Acked-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + Makefile.am | 2 ++ + configure.ac | 1 - + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/Makefile.am b/Makefile.am +index c2a6908..58c6959 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -154,6 +154,8 @@ AM_CFLAGS = \ + \ + $(GCC_FVISIBILITY_HIDDEN) \ + \ ++ -DMAKE_STAMP=$(MAKE_STAMP) \ ++ \ + $(NULL) + + AM_YFLAGS = -d -Wno-yacc +diff --git a/configure.ac b/configure.ac +index bac8319..c14aa67 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -136,7 +136,6 @@ AC_CONFIG_COMMANDS([nftversion.h], [ + # Current date should be fetched exactly once per build, + # so have 'make' call date and pass the value to every 'gcc' call + AC_SUBST([MAKE_STAMP], ["\$(shell date +%s)"]) +-CFLAGS="${CFLAGS} -DMAKE_STAMP=\${MAKE_STAMP}" + + AC_CONFIG_FILES([ \ + Makefile \ diff --git a/nftables.spec b/nftables.spec index 31f6f98..ba6f2b1 100644 --- a/nftables.spec +++ b/nftables.spec @@ -1,6 +1,6 @@ Name: nftables Version: 1.1.1 -Release: 4%{?dist} +Release: 6%{?dist} # Upstream released a 0.100 version, then 0.4. Need Epoch to get back on track. Epoch: 1 Summary: Netfilter Tables userspace utilities @@ -30,6 +30,12 @@ Patch11: 0011-json-Support-typeof-in-set-and-map-types.patch Patch12: 0012-tests-py-Fix-for-storing-payload-into-missing-file.patch Patch13: 0013-monitor-Recognize-flowtable-add-del-events.patch Patch14: 0014-evaluate-allow-to-re-use-existing-metered-set.patch +Patch15: 0015-src-split-monitor-trace-code-into-new-trace.c.patch +Patch16: 0016-src-add-conntrack-information-to-trace-monitor-mode.patch +Patch17: 0017-trace-Fix-for-memleak-in-trace_alloc_list-error-path.patch +Patch18: 0018-doc-nft.8-Minor-NAT-STATEMENTS-section-review.patch +Patch19: 0019-table-Embed-creating-nft-version-into-userdata.patch +Patch20: 0020-Makefile-Fix-for-make-CFLAGS.patch BuildRequires: autoconf BuildRequires: automake @@ -144,6 +150,16 @@ cd py/ %files -n python3-nftables -f %{pyproject_files} %changelog +* Wed Sep 10 2025 Phil Sutter [1.1.1-6.el10] +- Makefile: Fix for 'make CFLAGS=...' (Phil Sutter) [RHEL-108851] +- table: Embed creating nft version into userdata (Phil Sutter) [RHEL-108851] +- doc: nft.8: Minor NAT STATEMENTS section review (Phil Sutter) [RHEL-106743] +- trace: Fix for memleak in trace_alloc_list() error path (Phil Sutter) [RHEL-111205] + +* Wed Jul 16 2025 Phil Sutter [1.1.1-5.el10] +- src: add conntrack information to trace monitor mode (Phil Sutter) [RHEL-102994] +- src: split monitor trace code into new trace.c (Phil Sutter) [RHEL-102994] + * Mon Mar 03 2025 Eric Garver [1.1.1-4.el10] - evaluate: allow to re-use existing metered set [RHEL-75507]