From 63fbb665897398c0523163a94a8aeee9a1f5da77 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Tue, 30 Jun 2026 19:42:31 +0200 Subject: [PATCH] nftables-1.0.9-8.el9 * Tue Jun 30 2026 Phil Sutter [1.0.9-8.el9] - parser_json: fix map/set type confusion crash in map statement parser (Phil Sutter) [RHEL-190549] - parser_json: bail out on malformed statement in set (Phil Sutter) [RHEL-190549] - parser_json: allow statement stateful statement only in set elements (Phil Sutter) [RHEL-190549] - parser_json: fix crash in json_parse_set_stmt_list (Phil Sutter) [RHEL-190549] - cache: honor -c/--check for reset commands (Phil Sutter) [RHEL-190549] - mnl: Fix ordering of hooks in 'list hooks' output (Phil Sutter) [RHEL-190549] - segtree: Fix for variable-sized object may not be initialized (Phil Sutter) [RHEL-190549] - cache: Filter for table when listing flowtables (Phil Sutter) [RHEL-190549] - cache: Filter for table when listing sets or maps (Phil Sutter) [RHEL-190549] - cache: Relax chain_cache_dump filter application (Phil Sutter) [RHEL-190549] - cache: Respect family in all list commands (Phil Sutter) [RHEL-190549] - json: complete multi-statement set element support (Phil Sutter) [RHEL-190549] - mergesort: Align concatenation sort order with Big Endian (Phil Sutter) [RHEL-190549] - optimize: expand expression list when merging into concatenation (Phil Sutter) [RHEL-190549] - mergesort: Fix sorting of string values (Phil Sutter) [RHEL-190549] - segtree: Fix range aggregation on Big Endian (Phil Sutter) [RHEL-190549] - monitor: fix memleak in setelem cb (Phil Sutter) [RHEL-190549] - tests: json_echo: Drop rule handle before multi-add (Phil Sutter) [RHEL-190549] - rule: skip CMD_OBJ_SETELEMS with no elements after set flush (Phil Sutter) [RHEL-190549] - doc: libnftables-json: Describe RULESET object (Phil Sutter) [RHEL-190549] - doc: don't suggest to disable GSO (Phil Sutter) [RHEL-190549] - tests: py: inet/osf.t: Fix element ordering in JSON equivalents (Phil Sutter) [RHEL-190549] - tests: py: any/ct.t.json.output: Drop leftover entry (Phil Sutter) [RHEL-190549] - optimize: Fix verdict expression comparison (Phil Sutter) [RHEL-190549] - datatype: Fix boolean type on Big Endian (Phil Sutter) [RHEL-190549] - datatype: clamp boolean value to 0 and 1 (Phil Sutter) [RHEL-190549] - src: parser_json: fix format string bugs (Phil Sutter) [RHEL-190549] - doc: fix tcpdump example (Phil Sutter) [RHEL-190549] - fib: Fix for existence check on Big Endian (Phil Sutter) [RHEL-190549] - monitor: Inform JSON printer when reporting an object delete event (Phil Sutter) [RHEL-190549] - tests: monitor: Fix regex collecting expected echo output (Phil Sutter) [RHEL-190549] - monitor: Quote device names in chain declarations, too (Phil Sutter) [RHEL-190549] - mnl: continue on ENOBUFS errors when processing batch (Phil Sutter) [RHEL-190549] - mnl: silence compiler warning (Phil Sutter) [RHEL-190549] - fib: restore JSON output for relational expressions (Phil Sutter) [RHEL-190549] - json: Do not reduce single-item arrays on output (Phil Sutter) [RHEL-190549] - src: ensure chain policy evaluation when specified (Phil Sutter) [RHEL-190549] - segtree: incorrect type when aggregating concatenated set ranges (Phil Sutter) [RHEL-190549] - tests: py: Fix tests added for 'icmpv6 taddr' support (Phil Sutter) [RHEL-190549] - tests: py: Drop stale entry from ip/snat.t.payload (Phil Sutter) [RHEL-190549] - tests: py: Drop stale entries from ip6/{ct,meta}.t.json (Phil Sutter) [RHEL-190549] - tests: py: Drop stale entry from ip/snat.t.json (Phil Sutter) [RHEL-190549] - tests: py: Drop duplicate test from inet/vxlan.t (Phil Sutter) [RHEL-190549] - tests: py: Drop stale entry from inet/tcp.t.json (Phil Sutter) [RHEL-190549] - tests: py: Drop duplicate test from inet/gretap.t (Phil Sutter) [RHEL-190549] - tests: py: Drop duplicate test from inet/gre.t (Phil Sutter) [RHEL-190549] - tests: py: Drop duplicate test from inet/geneve.t (Phil Sutter) [RHEL-190549] - tests: py: Drop stale entries since redundant test case removal (Phil Sutter) [RHEL-190549] - src: netlink: netlink_delinearize_table() may return NULL (Phil Sutter) [RHEL-190549] - doc: nft.8: Minor NAT STATEMENTS section review (Phil Sutter) [RHEL-190549] - mnl: Call mnl_attr_nest_end() just once (Phil Sutter) [RHEL-190549] - evaluate: validate set expression type before accessing flags (Phil Sutter) [RHEL-190549] - rule: print chain and flowtable devices in quotes (Phil Sutter) [RHEL-190549] - tests: shell: skip if kernel does not support flowtable counter (Phil Sutter) [RHEL-190549] - tests: shell: adjust add-after-delete flowtable for older kernels (Phil Sutter) [RHEL-190549] - tests/shell: cover long interface name in "0042chain_variable_0" test (Phil Sutter) [RHEL-190549] - tests: py: re-enables nft-test.py to load the local nftables.py (Phil Sutter) [RHEL-190549] - fib: allow to use it in set statements (Phil Sutter) [RHEL-190549] - fib: allow to check if route exists in maps (Phil Sutter) [RHEL-190549] - tests: py: fix json single-flag output for fib & synproxy (Phil Sutter) [RHEL-190549] - parser_json: Introduce parse_flags_array() (Phil Sutter) [RHEL-190549] - json: work around fuzzer-induced assert crashes (Phil Sutter) [RHEL-190549] - json: Introduce json_add_array_new() (Phil Sutter) [RHEL-190549] - json: Print single synproxy flags as non-array (Phil Sutter) [RHEL-190549] - json: Print single fib flag as non-array (Phil Sutter) [RHEL-190549] - json: fix use after free in table_flags_json() (Phil Sutter) [RHEL-190549] - doc: extend description of fib expression (Phil Sutter) [RHEL-190549] - json: reject too long interface names (Phil Sutter) [RHEL-190549] - parser_bison: allow delete command with map via handle (Phil Sutter) [RHEL-190549] - parser_bison: only reset by name is supported by now (Phil Sutter) [RHEL-190549] - cache: assert name is non-nul when looking up (Phil Sutter) [RHEL-190549] - rule: skip fuzzy lookup if object name is not available (Phil Sutter) [RHEL-190549] - src: BASECHAIN flag no longer implies presence of priority expression (Phil Sutter) [RHEL-190549] - netlink: Avoid crash upon missing NFTNL_OBJ_CT_TIMEOUT_ARRAY attribute (Phil Sutter) [RHEL-190549] - tests: py: Properly fix JSON equivalents for netdev/reject.t (Phil Sutter) [RHEL-190549] - tests: monitor: Fix for single flag array avoidance (Phil Sutter) [RHEL-190549] - json: Support typeof in set and map types (Phil Sutter) [RHEL-190549] - netlink: Do not allocate a bogus flowtable priority expr (Phil Sutter) [RHEL-190549] - netlink: Fix for potential crash parsing a flowtable (Phil Sutter) [RHEL-190549] - json: prevent null deref if chain->policy is not set (Phil Sutter) [RHEL-190549] - netlink: Catch unknown types when deserializing objects (Phil Sutter) [RHEL-190549] - netlink: Avoid potential NULL-ptr deref parsing set elem expressions (Phil Sutter) [RHEL-190549] - json: Print single set flag as non-array (Phil Sutter) [RHEL-190549] - doc: Fix typo in nat statement 'prefix' description (Phil Sutter) [RHEL-190549] Resolves: RHEL-190549 --- ...-in-nat-statement-prefix-description.patch | 37 + ...n-Print-single-set-flag-as-non-array.patch | 39 + ...tential-NULL-ptr-deref-parsing-set-e.patch | 40 + ...known-types-when-deserializing-objec.patch | 41 + ...ull-deref-if-chain-policy-is-not-set.patch | 99 ++ ...-potential-crash-parsing-a-flowtable.patch | 40 + ...llocate-a-bogus-flowtable-priority-e.patch | 49 + ...-Support-typeof-in-set-and-map-types.patch | 124 ++ ...-Fix-for-single-flag-array-avoidance.patch | 108 ++ ...y-fix-JSON-equivalents-for-netdev-re.patch | 232 ++++ ...ash-upon-missing-NFTNL_OBJ_CT_TIMEOU.patch | 44 + ...ag-no-longer-implies-presence-of-pri.patch | 77 ++ ...lookup-if-object-name-is-not-availab.patch | 73 ++ ...sert-name-is-non-nul-when-looking-up.patch | 75 ++ ...ly-reset-by-name-is-supported-by-now.patch | 55 + ...ow-delete-command-with-map-via-handl.patch | 66 ++ ...json-reject-too-long-interface-names.patch | 172 +++ ...extend-description-of-fib-expression.patch | 142 +++ ...x-use-after-free-in-table_flags_json.patch | 57 + ...n-Print-single-fib-flag-as-non-array.patch | 48 + ...t-single-synproxy-flags-as-non-array.patch | 42 + 0037-json-Introduce-json_add_array_new.patch | 221 ++++ ...around-fuzzer-induced-assert-crashes.patch | 1048 +++++++++++++++++ ...ser_json-Introduce-parse_flags_array.patch | 769 ++++++++++++ ...n-single-flag-output-for-fib-synprox.patch | 92 ++ ...low-to-check-if-route-exists-in-maps.patch | 434 +++++++ ...ib-allow-to-use-it-in-set-statements.patch | 134 +++ ...les-nft-test.py-to-load-the-local-nf.patch | 45 + ...r-long-interface-name-in-0042chain_v.patch | 126 ++ ...st-add-after-delete-flowtable-for-ol.patch | 74 ++ ...-if-kernel-does-not-support-flowtabl.patch | 61 + ...hain-and-flowtable-devices-in-quotes.patch | 263 +++++ ...e-set-expression-type-before-accessi.patch | 39 + ...mnl-Call-mnl_attr_nest_end-just-once.patch | 39 + ...-Minor-NAT-STATEMENTS-section-review.patch | 78 ++ ...ink_delinearize_table-may-return-NUL.patch | 54 + ...ale-entries-since-redundant-test-cas.patch | 340 ++++++ ...op-duplicate-test-from-inet-geneve.t.patch | 79 ++ ...-Drop-duplicate-test-from-inet-gre.t.patch | 67 ++ ...op-duplicate-test-from-inet-gretap.t.patch | 67 ++ ...rop-stale-entry-from-inet-tcp.t.json.patch | 69 ++ ...rop-duplicate-test-from-inet-vxlan.t.patch | 79 ++ ...Drop-stale-entry-from-ip-snat.t.json.patch | 70 ++ ...ale-entries-from-ip6-ct-meta-.t.json.patch | 268 +++++ ...p-stale-entry-from-ip-snat.t.payload.patch | 48 + ...tests-added-for-icmpv6-taddr-support.patch | 155 +++ ...t-type-when-aggregating-concatenated.patch | 44 + ...ain-policy-evaluation-when-specified.patch | 154 +++ ...-reduce-single-item-arrays-on-output.patch | 767 ++++++++++++ ...ON-output-for-relational-expressions.patch | 184 +++ 0066-mnl-silence-compiler-warning.patch | 55 + ...ENOBUFS-errors-when-processing-batch.patch | 89 ++ ...vice-names-in-chain-declarations-too.patch | 38 + ...x-regex-collecting-expected-echo-out.patch | 39 + ...SON-printer-when-reporting-an-object.patch | 189 +++ ...ix-for-existence-check-on-Big-Endian.patch | 67 ++ 0072-doc-fix-tcpdump-example.patch | 41 + ...c-parser_json-fix-format-string-bugs.patch | 60 + ...atype-clamp-boolean-value-to-0-and-1.patch | 80 ++ ...atype-Fix-boolean-type-on-Big-Endian.patch | 39 + ...ze-Fix-verdict-expression-comparison.patch | 56 + ...ct.t.json.output-Drop-leftover-entry.patch | 69 ++ ...f.t-Fix-element-ordering-in-JSON-equ.patch | 121 ++ 0079-doc-don-t-suggest-to-disable-GSO.patch | 69 ++ ...ftables-json-Describe-RULESET-object.patch | 48 + ...J_SETELEMS-with-no-elements-after-se.patch | 41 + ...ho-Drop-rule-handle-before-multi-add.patch | 38 + 0083-monitor-fix-memleak-in-setelem-cb.patch | 53 + ...-Fix-range-aggregation-on-Big-Endian.patch | 69 ++ ...rgesort-Fix-sorting-of-string-values.patch | 244 ++++ ...expression-list-when-merging-into-co.patch | 84 ++ ...concatenation-sort-order-with-Big-En.patch | 228 ++++ ...-multi-statement-set-element-support.patch | 41 + ...-Respect-family-in-all-list-commands.patch | 117 ++ ...-chain_cache_dump-filter-application.patch | 45 + ...-for-table-when-listing-sets-or-maps.patch | 62 + ...er-for-table-when-listing-flowtables.patch | 52 + ...variable-sized-object-may-not-be-ini.patch | 42 + ...dering-of-hooks-in-list-hooks-output.patch | 39 + ...che-honor-c-check-for-reset-commands.patch | 54 + ...ix-crash-in-json_parse_set_stmt_list.patch | 56 + ...w-statement-stateful-statement-only-.patch | 41 + ...il-out-on-malformed-statement-in-set.patch | 120 ++ ...map-set-type-confusion-crash-in-map-.patch | 50 + nftables.spec | 172 ++- 85 files changed, 10235 insertions(+), 1 deletion(-) create mode 100644 0016-doc-Fix-typo-in-nat-statement-prefix-description.patch create mode 100644 0017-json-Print-single-set-flag-as-non-array.patch create mode 100644 0018-netlink-Avoid-potential-NULL-ptr-deref-parsing-set-e.patch create mode 100644 0019-netlink-Catch-unknown-types-when-deserializing-objec.patch create mode 100644 0020-json-prevent-null-deref-if-chain-policy-is-not-set.patch create mode 100644 0021-netlink-Fix-for-potential-crash-parsing-a-flowtable.patch create mode 100644 0022-netlink-Do-not-allocate-a-bogus-flowtable-priority-e.patch create mode 100644 0023-json-Support-typeof-in-set-and-map-types.patch create mode 100644 0024-tests-monitor-Fix-for-single-flag-array-avoidance.patch create mode 100644 0025-tests-py-Properly-fix-JSON-equivalents-for-netdev-re.patch create mode 100644 0026-netlink-Avoid-crash-upon-missing-NFTNL_OBJ_CT_TIMEOU.patch create mode 100644 0027-src-BASECHAIN-flag-no-longer-implies-presence-of-pri.patch create mode 100644 0028-rule-skip-fuzzy-lookup-if-object-name-is-not-availab.patch create mode 100644 0029-cache-assert-name-is-non-nul-when-looking-up.patch create mode 100644 0030-parser_bison-only-reset-by-name-is-supported-by-now.patch create mode 100644 0031-parser_bison-allow-delete-command-with-map-via-handl.patch create mode 100644 0032-json-reject-too-long-interface-names.patch create mode 100644 0033-doc-extend-description-of-fib-expression.patch create mode 100644 0034-json-fix-use-after-free-in-table_flags_json.patch create mode 100644 0035-json-Print-single-fib-flag-as-non-array.patch create mode 100644 0036-json-Print-single-synproxy-flags-as-non-array.patch create mode 100644 0037-json-Introduce-json_add_array_new.patch create mode 100644 0038-json-work-around-fuzzer-induced-assert-crashes.patch create mode 100644 0039-parser_json-Introduce-parse_flags_array.patch create mode 100644 0040-tests-py-fix-json-single-flag-output-for-fib-synprox.patch create mode 100644 0041-fib-allow-to-check-if-route-exists-in-maps.patch create mode 100644 0042-fib-allow-to-use-it-in-set-statements.patch create mode 100644 0043-tests-py-re-enables-nft-test.py-to-load-the-local-nf.patch create mode 100644 0044-tests-shell-cover-long-interface-name-in-0042chain_v.patch create mode 100644 0045-tests-shell-adjust-add-after-delete-flowtable-for-ol.patch create mode 100644 0046-tests-shell-skip-if-kernel-does-not-support-flowtabl.patch create mode 100644 0047-rule-print-chain-and-flowtable-devices-in-quotes.patch create mode 100644 0048-evaluate-validate-set-expression-type-before-accessi.patch create mode 100644 0049-mnl-Call-mnl_attr_nest_end-just-once.patch create mode 100644 0050-doc-nft.8-Minor-NAT-STATEMENTS-section-review.patch create mode 100644 0051-src-netlink-netlink_delinearize_table-may-return-NUL.patch create mode 100644 0052-tests-py-Drop-stale-entries-since-redundant-test-cas.patch create mode 100644 0053-tests-py-Drop-duplicate-test-from-inet-geneve.t.patch create mode 100644 0054-tests-py-Drop-duplicate-test-from-inet-gre.t.patch create mode 100644 0055-tests-py-Drop-duplicate-test-from-inet-gretap.t.patch create mode 100644 0056-tests-py-Drop-stale-entry-from-inet-tcp.t.json.patch create mode 100644 0057-tests-py-Drop-duplicate-test-from-inet-vxlan.t.patch create mode 100644 0058-tests-py-Drop-stale-entry-from-ip-snat.t.json.patch create mode 100644 0059-tests-py-Drop-stale-entries-from-ip6-ct-meta-.t.json.patch create mode 100644 0060-tests-py-Drop-stale-entry-from-ip-snat.t.payload.patch create mode 100644 0061-tests-py-Fix-tests-added-for-icmpv6-taddr-support.patch create mode 100644 0062-segtree-incorrect-type-when-aggregating-concatenated.patch create mode 100644 0063-src-ensure-chain-policy-evaluation-when-specified.patch create mode 100644 0064-json-Do-not-reduce-single-item-arrays-on-output.patch create mode 100644 0065-fib-restore-JSON-output-for-relational-expressions.patch create mode 100644 0066-mnl-silence-compiler-warning.patch create mode 100644 0067-mnl-continue-on-ENOBUFS-errors-when-processing-batch.patch create mode 100644 0068-monitor-Quote-device-names-in-chain-declarations-too.patch create mode 100644 0069-tests-monitor-Fix-regex-collecting-expected-echo-out.patch create mode 100644 0070-monitor-Inform-JSON-printer-when-reporting-an-object.patch create mode 100644 0071-fib-Fix-for-existence-check-on-Big-Endian.patch create mode 100644 0072-doc-fix-tcpdump-example.patch create mode 100644 0073-src-parser_json-fix-format-string-bugs.patch create mode 100644 0074-datatype-clamp-boolean-value-to-0-and-1.patch create mode 100644 0075-datatype-Fix-boolean-type-on-Big-Endian.patch create mode 100644 0076-optimize-Fix-verdict-expression-comparison.patch create mode 100644 0077-tests-py-any-ct.t.json.output-Drop-leftover-entry.patch create mode 100644 0078-tests-py-inet-osf.t-Fix-element-ordering-in-JSON-equ.patch create mode 100644 0079-doc-don-t-suggest-to-disable-GSO.patch create mode 100644 0080-doc-libnftables-json-Describe-RULESET-object.patch create mode 100644 0081-rule-skip-CMD_OBJ_SETELEMS-with-no-elements-after-se.patch create mode 100644 0082-tests-json_echo-Drop-rule-handle-before-multi-add.patch create mode 100644 0083-monitor-fix-memleak-in-setelem-cb.patch create mode 100644 0084-segtree-Fix-range-aggregation-on-Big-Endian.patch create mode 100644 0085-mergesort-Fix-sorting-of-string-values.patch create mode 100644 0086-optimize-expand-expression-list-when-merging-into-co.patch create mode 100644 0087-mergesort-Align-concatenation-sort-order-with-Big-En.patch create mode 100644 0088-json-complete-multi-statement-set-element-support.patch create mode 100644 0089-cache-Respect-family-in-all-list-commands.patch create mode 100644 0090-cache-Relax-chain_cache_dump-filter-application.patch create mode 100644 0091-cache-Filter-for-table-when-listing-sets-or-maps.patch create mode 100644 0092-cache-Filter-for-table-when-listing-flowtables.patch create mode 100644 0093-segtree-Fix-for-variable-sized-object-may-not-be-ini.patch create mode 100644 0094-mnl-Fix-ordering-of-hooks-in-list-hooks-output.patch create mode 100644 0095-cache-honor-c-check-for-reset-commands.patch create mode 100644 0096-parser_json-fix-crash-in-json_parse_set_stmt_list.patch create mode 100644 0097-parser_json-allow-statement-stateful-statement-only-.patch create mode 100644 0098-parser_json-bail-out-on-malformed-statement-in-set.patch create mode 100644 0099-parser_json-fix-map-set-type-confusion-crash-in-map-.patch diff --git a/0016-doc-Fix-typo-in-nat-statement-prefix-description.patch b/0016-doc-Fix-typo-in-nat-statement-prefix-description.patch new file mode 100644 index 0000000..ceebefc --- /dev/null +++ b/0016-doc-Fix-typo-in-nat-statement-prefix-description.patch @@ -0,0 +1,37 @@ +From 5bd9f93cc070364eae782196112fe81953d8f872 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:23:48 +0200 +Subject: [PATCH] doc: Fix typo in nat statement 'prefix' description + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 1b3c33a7931967fb7afe4efdf68deb7781786489 + +commit 1b3c33a7931967fb7afe4efdf68deb7781786489 +Author: Phil Sutter +Date: Thu May 8 16:35:47 2025 +0200 + + doc: Fix typo in nat statement 'prefix' description + + No point in repeating 'to map' here. + + Fixes: 19d73ccdd39fa ("doc: add nat examples") + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + doc/statements.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/doc/statements.txt b/doc/statements.txt +index c29a2ff..c7529ee 100644 +--- a/doc/statements.txt ++++ b/doc/statements.txt +@@ -388,7 +388,7 @@ Before kernel 4.18 nat statements require both prerouting and postrouting base c + to be present since otherwise packets on the return path won't be seen by + netfilter and therefore no reverse translation will take place. + +-The optional *prefix* keyword allows to map to map *n* source addresses to *n* ++The optional *prefix* keyword allows to map *n* source addresses to *n* + destination addresses. See 'Advanced NAT examples' below. + + .NAT statement values diff --git a/0017-json-Print-single-set-flag-as-non-array.patch b/0017-json-Print-single-set-flag-as-non-array.patch new file mode 100644 index 0000000..c6a6d2f --- /dev/null +++ b/0017-json-Print-single-set-flag-as-non-array.patch @@ -0,0 +1,39 @@ +From 8e7d599a1d42add21949ff5c05ede51db9dc4db9 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:23:49 +0200 +Subject: [PATCH] json: Print single set flag as non-array + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 6bedb12af1658562f277ca68d74cf1e9e7433a08 +Conflicts: Dropped changes to non-existent dump files + +commit 6bedb12af1658562f277ca68d74cf1e9e7433a08 +Author: Phil Sutter +Date: Thu May 8 16:39:24 2025 +0200 + + json: Print single set flag as non-array + + The code obviously intended to do this already but got the array length + check wrong. + + Fixes: e70354f53e9f6 ("libnftables: Implement JSON output support") + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + src/json.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/json.c b/src/json.c +index dad93e5..0e6e39d 100644 +--- a/src/json.c ++++ b/src/json.c +@@ -178,7 +178,7 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set) + if (set->flags & NFT_SET_EVAL) + json_array_append_new(tmp, json_pack("s", "dynamic")); + +- if (json_array_size(tmp) > 0) { ++ if (json_array_size(tmp) > 1) { + json_object_set_new(root, "flags", tmp); + } else { + if (json_array_size(tmp)) diff --git a/0018-netlink-Avoid-potential-NULL-ptr-deref-parsing-set-e.patch b/0018-netlink-Avoid-potential-NULL-ptr-deref-parsing-set-e.patch new file mode 100644 index 0000000..26d27a0 --- /dev/null +++ b/0018-netlink-Avoid-potential-NULL-ptr-deref-parsing-set-e.patch @@ -0,0 +1,40 @@ +From 8e81e7293c38e19b4d9e2953b51adaab93a38c90 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:24:19 +0200 +Subject: [PATCH] netlink: Avoid potential NULL-ptr deref parsing set elem + expressions + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 89645d04106d45d5e23b6d5206777dad1fb3e6bf + +commit 89645d04106d45d5e23b6d5206777dad1fb3e6bf +Author: Phil Sutter +Date: Fri May 16 20:08:05 2025 +0200 + + netlink: Avoid potential NULL-ptr deref parsing set elem expressions + + Since netlink_parse_set_expr() may return NULL, the following deref must + be guarded. + + Fixes: e6d1d0d611958 ("src: add set element multi-statement support") + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + src/netlink.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/netlink.c b/src/netlink.c +index 04bba59..9fa95ea 100644 +--- a/src/netlink.c ++++ b/src/netlink.c +@@ -927,7 +927,8 @@ static int set_elem_parse_expressions(struct nftnl_expr *e, void *data) + struct stmt *stmt; + + stmt = netlink_parse_set_expr(set, cache, e); +- list_add_tail(&stmt->list, &setelem_parse_ctx->stmt_list); ++ if (stmt) ++ list_add_tail(&stmt->list, &setelem_parse_ctx->stmt_list); + + return 0; + } diff --git a/0019-netlink-Catch-unknown-types-when-deserializing-objec.patch b/0019-netlink-Catch-unknown-types-when-deserializing-objec.patch new file mode 100644 index 0000000..937f0ce --- /dev/null +++ b/0019-netlink-Catch-unknown-types-when-deserializing-objec.patch @@ -0,0 +1,41 @@ +From 548a3e9dbc1b111e9bdd0be5df0056d12b77836f Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:24:19 +0200 +Subject: [PATCH] netlink: Catch unknown types when deserializing objects + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 690f19eadde5cb607ec3d8d471c86d558c7229bd + +commit 690f19eadde5cb607ec3d8d471c86d558c7229bd +Author: Phil Sutter +Date: Fri May 16 19:41:19 2025 +0200 + + netlink: Catch unknown types when deserializing objects + + Print an error message and discard the object instead of returning it to + the caller. At least when trying to print it, we would hit an assert() + in obj_type_name() anyway. + + Fixes: 4756d92e517ae ("src: listing of stateful objects") + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + src/netlink.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/netlink.c b/src/netlink.c +index 9fa95ea..6130e56 100644 +--- a/src/netlink.c ++++ b/src/netlink.c +@@ -1733,6 +1733,10 @@ struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx, + obj->synproxy.flags = + nftnl_obj_get_u32(nlo, NFTNL_OBJ_SYNPROXY_FLAGS); + break; ++ default: ++ netlink_io_error(ctx, NULL, "Unknown object type %u", type); ++ obj_free(obj); ++ return NULL; + } + obj->type = type; + diff --git a/0020-json-prevent-null-deref-if-chain-policy-is-not-set.patch b/0020-json-prevent-null-deref-if-chain-policy-is-not-set.patch new file mode 100644 index 0000000..cd551a2 --- /dev/null +++ b/0020-json-prevent-null-deref-if-chain-policy-is-not-set.patch @@ -0,0 +1,99 @@ +From 7fe078fda20ce68b53147afcab2ed126ecf739c6 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:24:19 +0200 +Subject: [PATCH] json: prevent null deref if chain->policy is not set + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 69b90023c7220fe283ee38686c758e3494e853d9 + +commit 69b90023c7220fe283ee38686c758e3494e853d9 +Author: Florian Westphal +Date: Mon Jun 2 14:22:33 2025 +0200 + + json: prevent null deref if chain->policy is not set + + The two commits mentioned below resolved null dererence crashes when the + policy resp. priority keyword was missing in the chain/flowtable + specification. + + Same issue exists in the json output path, so apply similar fix there + and extend the existing test cases. + + Fixes: 5b37479b42b3 ("nftables: don't crash in 'list ruleset' if policy is not set") + Fixes: b40bebbcee36 ("rule: do not crash if to-be-printed flowtable lacks priority") + Signed-off-by: Florian Westphal + Acked-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + src/json.c | 19 ++++++++++++++----- + .../nft-j-f/flowtable-no-priority-crash | 6 ++++++ + .../shell/testcases/nft-f/0021list_ruleset_0 | 7 ++++++- + 3 files changed, 26 insertions(+), 6 deletions(-) + create mode 100644 tests/shell/testcases/bogons/nft-j-f/flowtable-no-priority-crash + +diff --git a/src/json.c b/src/json.c +index 0e6e39d..4836e6b 100644 +--- a/src/json.c ++++ b/src/json.c +@@ -272,8 +272,14 @@ static json_t *chain_print_json(const struct chain *chain) + if (chain->flags & CHAIN_F_BASECHAIN) { + mpz_export_data(&priority, chain->priority.expr->value, + BYTEORDER_HOST_ENDIAN, sizeof(int)); +- mpz_export_data(&policy, chain->policy->value, +- BYTEORDER_HOST_ENDIAN, sizeof(int)); ++ ++ if (chain->policy) { ++ mpz_export_data(&policy, chain->policy->value, ++ BYTEORDER_HOST_ENDIAN, sizeof(int)); ++ } else { ++ policy = NF_ACCEPT; ++ } ++ + tmp = json_pack("{s:s, s:s, s:i, s:s}", + "type", chain->type.str, + "hook", hooknum2str(chain->handle.family, +@@ -451,10 +457,13 @@ static json_t *obj_print_json(const struct obj *obj) + static json_t *flowtable_print_json(const struct flowtable *ftable) + { + json_t *root, *devs = NULL; +- int i, priority; ++ int i, priority = 0; ++ ++ if (ftable->priority.expr) { ++ mpz_export_data(&priority, ftable->priority.expr->value, ++ BYTEORDER_HOST_ENDIAN, sizeof(int)); ++ } + +- mpz_export_data(&priority, ftable->priority.expr->value, +- BYTEORDER_HOST_ENDIAN, sizeof(int)); + root = json_pack("{s:s, s:s, s:s, s:I, s:s, s:i}", + "family", family2str(ftable->handle.family), + "name", ftable->handle.flowtable.name, +diff --git a/tests/shell/testcases/bogons/nft-j-f/flowtable-no-priority-crash b/tests/shell/testcases/bogons/nft-j-f/flowtable-no-priority-crash +new file mode 100644 +index 0000000..f348da9 +--- /dev/null ++++ b/tests/shell/testcases/bogons/nft-j-f/flowtable-no-priority-crash +@@ -0,0 +1,6 @@ ++table ip filter { ++ flowtable ft1 { ++ devices = { lo } ++ } ++} ++list ruleset +diff --git a/tests/shell/testcases/nft-f/0021list_ruleset_0 b/tests/shell/testcases/nft-f/0021list_ruleset_0 +index 37729b4..f3c3749 100755 +--- a/tests/shell/testcases/nft-f/0021list_ruleset_0 ++++ b/tests/shell/testcases/nft-f/0021list_ruleset_0 +@@ -12,4 +12,9 @@ RULESET="table filter { + list ruleset + " + +-exec $NFT -f - <<< "$RULESET" ++$NFT -f - <<< "$RULESET" ++ ++if [ "$NFT_TEST_HAVE_json" != n ]; then ++ $NFT flush ruleset ++ $NFT -j -f - <<< "$RULESET" ++fi diff --git a/0021-netlink-Fix-for-potential-crash-parsing-a-flowtable.patch b/0021-netlink-Fix-for-potential-crash-parsing-a-flowtable.patch new file mode 100644 index 0000000..1ae0197 --- /dev/null +++ b/0021-netlink-Fix-for-potential-crash-parsing-a-flowtable.patch @@ -0,0 +1,40 @@ +From 8fe856057e51298ebaadbfa59b56923b5d169b22 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:24:19 +0200 +Subject: [PATCH] netlink: Fix for potential crash parsing a flowtable + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit d5ef04441eb1de3efc27aa70193fe3d7f0b5c408 + +commit d5ef04441eb1de3efc27aa70193fe3d7f0b5c408 +Author: Phil Sutter +Date: Wed Jun 11 13:12:56 2025 +0200 + + netlink: Fix for potential crash parsing a flowtable + + Kernel's flowtable message might not contain the + NFTA_FLOWTABLE_HOOK_DEVS attribute. In that case, nftnl_flowtable_get() + will return NULL for the respective nftnl attribute. + + Fixes: db0697ce7f602 ("src: support for flowtable listing") + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/netlink.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/netlink.c b/src/netlink.c +index 6130e56..9a01d8a 100644 +--- a/src/netlink.c ++++ b/src/netlink.c +@@ -1853,7 +1853,7 @@ netlink_delinearize_flowtable(struct netlink_ctx *ctx, + if (nftnl_flowtable_is_set(nlo, NFTNL_FLOWTABLE_FLAGS)) + flowtable->flags = nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_FLAGS); + dev_array = nftnl_flowtable_get(nlo, NFTNL_FLOWTABLE_DEVICES); +- while (dev_array[len]) ++ while (dev_array && dev_array[len]) + len++; + + if (len) diff --git a/0022-netlink-Do-not-allocate-a-bogus-flowtable-priority-e.patch b/0022-netlink-Do-not-allocate-a-bogus-flowtable-priority-e.patch new file mode 100644 index 0000000..5ceb23c --- /dev/null +++ b/0022-netlink-Do-not-allocate-a-bogus-flowtable-priority-e.patch @@ -0,0 +1,49 @@ +From 3130995d533b2c8155001ca592c4d6fd3e0fc6c7 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:24:19 +0200 +Subject: [PATCH] netlink: Do not allocate a bogus flowtable priority expr + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 10b9a85b3278e0933bf47226588fede8c9fcbcc8 + +commit 10b9a85b3278e0933bf47226588fede8c9fcbcc8 +Author: Phil Sutter +Date: Wed Jun 11 14:15:38 2025 +0200 + + netlink: Do not allocate a bogus flowtable priority expr + + Code accidentally treats missing NFTNL_FLOWTABLE_PRIO attribute as zero + prio value which may not be correct. + + Fixes: db0697ce7f602 ("src: support for flowtable listing") + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/netlink.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/src/netlink.c b/src/netlink.c +index 9a01d8a..b1dd31d 100644 +--- a/src/netlink.c ++++ b/src/netlink.c +@@ -1868,14 +1868,16 @@ netlink_delinearize_flowtable(struct netlink_ctx *ctx, + sizeof(char *), qsort_device_cmp); + } + +- priority = nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_PRIO); +- flowtable->priority.expr = ++ if (nftnl_flowtable_is_set(nlo, NFTNL_FLOWTABLE_PRIO)) { ++ priority = nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_PRIO); ++ flowtable->priority.expr = + constant_expr_alloc(&netlink_location, + &integer_type, + BYTEORDER_HOST_ENDIAN, + sizeof(int) * + BITS_PER_BYTE, + &priority); ++ } + flowtable->hook.num = + nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_HOOKNUM); + flowtable->flags = diff --git a/0023-json-Support-typeof-in-set-and-map-types.patch b/0023-json-Support-typeof-in-set-and-map-types.patch new file mode 100644 index 0000000..391f4b7 --- /dev/null +++ b/0023-json-Support-typeof-in-set-and-map-types.patch @@ -0,0 +1,124 @@ +From ae0e0face33e74339f4f8082d3e6119b2ad57dcb Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:24:19 +0200 +Subject: [PATCH] json: Support typeof in set and map types + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit bb6312484af93a83a9ec8716f3887a43566a775a +Conflicts: Dropped changes to non-existent test and dump files + +commit bb6312484af93a83a9ec8716f3887a43566a775a +Author: Phil Sutter +Date: Sat Sep 28 00:55:34 2024 +0200 + + json: Support typeof in set and map types + + Implement this as a special "type" property value which is an object + with sole property "typeof". The latter's value is the JSON + representation of the expression in set->key, so for concatenated + typeofs it is a concat expression. + + All this is a bit clumsy right now but it works and it should be + possible to tear it down a bit for more user-friendliness in a + compatible way by either replacing the concat expression by the array it + contains or even the whole "typeof" object - the parser would just + assume any object (or objects in an array) in the "type" property value + are expressions to extract a type from. + + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + doc/libnftables-json.adoc | 7 ++++--- + src/json.c | 13 ++++++++++++- + src/parser_json.c | 9 +++++++++ + tests/monitor/testcases/map-expr.t | 2 +- + 4 files changed, 26 insertions(+), 5 deletions(-) + +diff --git a/doc/libnftables-json.adoc b/doc/libnftables-json.adoc +index c5e5f23..c3e3bcc 100644 +--- a/doc/libnftables-json.adoc ++++ b/doc/libnftables-json.adoc +@@ -339,7 +339,7 @@ ____ + "size":* 'NUMBER' + *}}* + +-'SET_TYPE' := 'STRING' | *[* 'SET_TYPE_LIST' *]* ++'SET_TYPE' := 'STRING' | *[* 'SET_TYPE_LIST' *]* | *{ "typeof":* 'EXPRESSION' *}* + 'SET_TYPE_LIST' := 'STRING' [*,* 'SET_TYPE_LIST' ] + 'SET_POLICY' := *"performance"* | *"memory"* + 'SET_FLAG_LIST' := 'SET_FLAG' [*,* 'SET_FLAG_LIST' ] +@@ -377,8 +377,9 @@ that they translate a unique key to a value. + Maximum number of elements supported. + + ==== TYPE +-The set type might be a string, such as *"ipv4_addr"* or an array +-consisting of strings (for concatenated types). ++The set type might be a string, such as *"ipv4_addr"*, an array ++consisting of strings (for concatenated types) or a *typeof* object containing ++an expression to extract the type from. + + ==== ELEM + A single set element might be given as string, integer or boolean value for +diff --git a/src/json.c b/src/json.c +index 4836e6b..39b1ada 100644 +--- a/src/json.c ++++ b/src/json.c +@@ -87,6 +87,17 @@ static json_t *set_dtype_json(const struct expr *key) + return root; + } + ++static json_t *set_key_dtype_json(const struct set *set, ++ struct output_ctx *octx) ++{ ++ bool use_typeof = set->key_typeof_valid; ++ ++ if (!use_typeof) ++ return set_dtype_json(set->key); ++ ++ return json_pack("{s:o}", "typeof", expr_print_json(set->key, octx)); ++} ++ + static json_t *stmt_print_json(const struct stmt *stmt, struct output_ctx *octx) + { + char buf[1024]; +@@ -149,7 +160,7 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set) + "family", family2str(set->handle.family), + "name", set->handle.set.name, + "table", set->handle.table.name, +- "type", set_dtype_json(set->key), ++ "type", set_key_dtype_json(set, octx), + "handle", set->handle.handle.id); + + if (set->comment) +diff --git a/src/parser_json.c b/src/parser_json.c +index 2acc248..936c5aa 100644 +--- a/src/parser_json.c ++++ b/src/parser_json.c +@@ -1700,7 +1700,16 @@ static struct expr *json_parse_dtype_expr(struct json_ctx *ctx, json_t *root) + compound_expr_add(expr, i); + } + return expr; ++ } else if (json_is_object(root)) { ++ const char *key; ++ json_t *val; ++ ++ if (!json_unpack_stmt(ctx, root, &key, &val) && ++ !strcmp(key, "typeof")) { ++ return json_parse_expr(ctx, val); ++ } + } ++ + json_error(ctx, "Invalid set datatype."); + return NULL; + } +diff --git a/tests/monitor/testcases/map-expr.t b/tests/monitor/testcases/map-expr.t +index 8729c0b..d11ad0e 100644 +--- a/tests/monitor/testcases/map-expr.t ++++ b/tests/monitor/testcases/map-expr.t +@@ -3,4 +3,4 @@ I add table ip t + I add map ip t m { typeof meta day . meta hour : verdict; flags interval; counter; } + O - + J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}} +-J {"add": {"map": {"family": "ip", "name": "m", "table": "t", "type": ["day", "hour"], "handle": 0, "map": "verdict", "flags": ["interval"], "stmt": [{"counter": null}]}}} ++J {"add": {"map": {"family": "ip", "name": "m", "table": "t", "type": {"typeof": {"concat": [{"meta": {"key": "day"}}, {"meta": {"key": "hour"}}]}}, "handle": 0, "map": "verdict", "flags": ["interval"], "stmt": [{"counter": null}]}}} diff --git a/0024-tests-monitor-Fix-for-single-flag-array-avoidance.patch b/0024-tests-monitor-Fix-for-single-flag-array-avoidance.patch new file mode 100644 index 0000000..d851129 --- /dev/null +++ b/0024-tests-monitor-Fix-for-single-flag-array-avoidance.patch @@ -0,0 +1,108 @@ +From d5c438d2a4d502460ad83ec2f3224c7faf83ed8f Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:24:52 +0200 +Subject: [PATCH] tests: monitor: Fix for single flag array avoidance + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit b5f43dfc8b28989290a16818f00ca73c6cd8ff65 +Conflicts: Dropped changes to non-existent test case + +commit b5f43dfc8b28989290a16818f00ca73c6cd8ff65 +Author: Phil Sutter +Date: Wed Jun 11 17:15:22 2025 +0200 + + tests: monitor: Fix for single flag array avoidance + + Missed to update the JSON monitor expected output. + + Fixes: 6bedb12af1658 ("json: Print single set flag as non-array") + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + tests/monitor/testcases/map-expr.t | 2 +- + tests/monitor/testcases/set-interval.t | 2 +- + tests/monitor/testcases/set-maps.t | 2 +- + tests/monitor/testcases/set-mixed.t | 2 +- + tests/monitor/testcases/set-multiple.t | 4 ++-- + tests/monitor/testcases/set-simple.t | 2 +- + 6 files changed, 7 insertions(+), 7 deletions(-) + +diff --git a/tests/monitor/testcases/map-expr.t b/tests/monitor/testcases/map-expr.t +index d11ad0e..9042004 100644 +--- a/tests/monitor/testcases/map-expr.t ++++ b/tests/monitor/testcases/map-expr.t +@@ -3,4 +3,4 @@ I add table ip t + I add map ip t m { typeof meta day . meta hour : verdict; flags interval; counter; } + O - + J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}} +-J {"add": {"map": {"family": "ip", "name": "m", "table": "t", "type": {"typeof": {"concat": [{"meta": {"key": "day"}}, {"meta": {"key": "hour"}}]}}, "handle": 0, "map": "verdict", "flags": ["interval"], "stmt": [{"counter": null}]}}} ++J {"add": {"map": {"family": "ip", "name": "m", "table": "t", "type": {"typeof": {"concat": [{"meta": {"key": "day"}}, {"meta": {"key": "hour"}}]}}, "handle": 0, "map": "verdict", "flags": "interval", "stmt": [{"counter": null}]}}} +diff --git a/tests/monitor/testcases/set-interval.t b/tests/monitor/testcases/set-interval.t +index 5053c59..84cf98c 100644 +--- a/tests/monitor/testcases/set-interval.t ++++ b/tests/monitor/testcases/set-interval.t +@@ -10,7 +10,7 @@ I add set ip t s { type inet_service; flags interval; elements = { 20, 30-40 }; + O add set ip t s { type inet_service; flags interval; } + O add element ip t s { 20 } + O add element ip t s { 30-40 } +-J {"add": {"set": {"family": "ip", "name": "s", "table": "t", "type": "inet_service", "handle": 0, "flags": ["interval"]}}} ++J {"add": {"set": {"family": "ip", "name": "s", "table": "t", "type": "inet_service", "handle": 0, "flags": "interval"}}} + J {"add": {"element": {"family": "ip", "table": "t", "name": "s", "elem": {"set": [20]}}}} + J {"add": {"element": {"family": "ip", "table": "t", "name": "s", "elem": {"set": [{"range": [30, 40]}]}}}} + +diff --git a/tests/monitor/testcases/set-maps.t b/tests/monitor/testcases/set-maps.t +index acda480..aaf332f 100644 +--- a/tests/monitor/testcases/set-maps.t ++++ b/tests/monitor/testcases/set-maps.t +@@ -3,7 +3,7 @@ I add table ip t + I add map ip t portip { type inet_service: ipv4_addr; flags interval; } + O - + J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}} +-J {"add": {"map": {"family": "ip", "name": "portip", "table": "t", "type": "inet_service", "handle": 0, "map": "ipv4_addr", "flags": ["interval"]}}} ++J {"add": {"map": {"family": "ip", "name": "portip", "table": "t", "type": "inet_service", "handle": 0, "map": "ipv4_addr", "flags": "interval"}}} + + I add element ip t portip { 80-100: 10.0.0.1 } + O - +diff --git a/tests/monitor/testcases/set-mixed.t b/tests/monitor/testcases/set-mixed.t +index 08c2011..1cf3d38 100644 +--- a/tests/monitor/testcases/set-mixed.t ++++ b/tests/monitor/testcases/set-mixed.t +@@ -4,7 +4,7 @@ I add set ip t portrange { type inet_service; flags interval; } + I add set ip t ports { type inet_service; } + O - + J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}} +-J {"add": {"set": {"family": "ip", "name": "portrange", "table": "t", "type": "inet_service", "handle": 0, "flags": ["interval"]}}} ++J {"add": {"set": {"family": "ip", "name": "portrange", "table": "t", "type": "inet_service", "handle": 0, "flags": "interval"}}} + J {"add": {"set": {"family": "ip", "name": "ports", "table": "t", "type": "inet_service", "handle": 0}}} + + # make sure concurrent adds work +diff --git a/tests/monitor/testcases/set-multiple.t b/tests/monitor/testcases/set-multiple.t +index bd7a624..84de98e 100644 +--- a/tests/monitor/testcases/set-multiple.t ++++ b/tests/monitor/testcases/set-multiple.t +@@ -4,8 +4,8 @@ I add set ip t portrange { type inet_service; flags interval; } + I add set ip t portrange2 { type inet_service; flags interval; } + O - + J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}} +-J {"add": {"set": {"family": "ip", "name": "portrange", "table": "t", "type": "inet_service", "handle": 0, "flags": ["interval"]}}} +-J {"add": {"set": {"family": "ip", "name": "portrange2", "table": "t", "type": "inet_service", "handle": 0, "flags": ["interval"]}}} ++J {"add": {"set": {"family": "ip", "name": "portrange", "table": "t", "type": "inet_service", "handle": 0, "flags": "interval"}}} ++J {"add": {"set": {"family": "ip", "name": "portrange2", "table": "t", "type": "inet_service", "handle": 0, "flags": "interval"}}} + + # make sure concurrent adds work + I add element ip t portrange { 1024-65535 } +diff --git a/tests/monitor/testcases/set-simple.t b/tests/monitor/testcases/set-simple.t +index 8ca4f32..6bac08a 100644 +--- a/tests/monitor/testcases/set-simple.t ++++ b/tests/monitor/testcases/set-simple.t +@@ -3,7 +3,7 @@ I add table ip t + I add set ip t portrange { type inet_service; flags interval; } + O - + J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}} +-J {"add": {"set": {"family": "ip", "name": "portrange", "table": "t", "type": "inet_service", "handle": 0, "flags": ["interval"]}}} ++J {"add": {"set": {"family": "ip", "name": "portrange", "table": "t", "type": "inet_service", "handle": 0, "flags": "interval"}}} + + # adding some ranges + I add element ip t portrange { 1-10 } diff --git a/0025-tests-py-Properly-fix-JSON-equivalents-for-netdev-re.patch b/0025-tests-py-Properly-fix-JSON-equivalents-for-netdev-re.patch new file mode 100644 index 0000000..29f49fa --- /dev/null +++ b/0025-tests-py-Properly-fix-JSON-equivalents-for-netdev-re.patch @@ -0,0 +1,232 @@ +From 1bbd3a42f69d0eaaab66fede7d781013183ef064 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:25:19 +0200 +Subject: [PATCH] tests: py: Properly fix JSON equivalents for netdev/reject.t + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit b7a11c691d459d06a801a28ae9a52bc564584a1a + +commit b7a11c691d459d06a801a28ae9a52bc564584a1a +Author: Phil Sutter +Date: Thu Jun 12 12:59:29 2025 +0200 + + tests: py: Properly fix JSON equivalents for netdev/reject.t + + Revert commit d1a7b9e19fe65 ("tests: py: update netdev reject test + file"), the stored JSON equivalents were correct in that they matched + the standard syntax input. + + In fact, we missed a .json.output file recording the expected deviation + in JSON output. + + Fixes: d1a7b9e19fe65 ("tests: py: update netdev reject test file") + Fixes: 7ca3368cd7575 ("reject: Unify inet, netdev and bridge delinearization") + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + tests/py/netdev/reject.t.json | 66 +++++++++++++++-------- + tests/py/netdev/reject.t.json.output | 81 ++++++++++++++++++++++++++++ + 2 files changed, 126 insertions(+), 21 deletions(-) + create mode 100644 tests/py/netdev/reject.t.json.output + +diff --git a/tests/py/netdev/reject.t.json b/tests/py/netdev/reject.t.json +index 9968aaf..b80db03 100644 +--- a/tests/py/netdev/reject.t.json ++++ b/tests/py/netdev/reject.t.json +@@ -130,17 +130,6 @@ + + # mark 12345 reject with tcp reset + [ +- { +- "match": { +- "left": { +- "meta": { +- "key": "l4proto" +- } +- }, +- "op": "==", +- "right": 6 +- } +- }, + { + "match": { + "left": { +@@ -162,30 +151,43 @@ + # reject + [ + { +- "reject": { +- "expr": "port-unreachable", +- "type": "icmpx" +- } ++ "reject": null + } + ] + + # meta protocol ip reject + [ + { +- "reject": { +- "expr": "port-unreachable", +- "type": "icmp" ++ "match": { ++ "left": { ++ "meta": { ++ "key": "protocol" ++ } ++ }, ++ "op": "==", ++ "right": "ip" + } ++ }, ++ { ++ "reject": null + } + ] + + # meta protocol ip6 reject + [ + { +- "reject": { +- "expr": "port-unreachable", +- "type": "icmpv6" ++ "match": { ++ "left": { ++ "meta": { ++ "key": "protocol" ++ } ++ }, ++ "op": "==", ++ "right": "ip6" + } ++ }, ++ { ++ "reject": null + } + ] + +@@ -231,6 +233,17 @@ + + # meta protocol ip reject with icmp host-unreachable + [ ++ { ++ "match": { ++ "left": { ++ "meta": { ++ "key": "protocol" ++ } ++ }, ++ "op": "==", ++ "right": "ip" ++ } ++ }, + { + "reject": { + "expr": "host-unreachable", +@@ -241,6 +254,17 @@ + + # meta protocol ip6 reject with icmpv6 no-route + [ ++ { ++ "match": { ++ "left": { ++ "meta": { ++ "key": "protocol" ++ } ++ }, ++ "op": "==", ++ "right": "ip6" ++ } ++ }, + { + "reject": { + "expr": "no-route", +diff --git a/tests/py/netdev/reject.t.json.output b/tests/py/netdev/reject.t.json.output +new file mode 100644 +index 0000000..cbd7310 +--- /dev/null ++++ b/tests/py/netdev/reject.t.json.output +@@ -0,0 +1,81 @@ ++# mark 12345 reject with tcp reset ++[ ++ { ++ "match": { ++ "left": { ++ "meta": { ++ "key": "l4proto" ++ } ++ }, ++ "op": "==", ++ "right": 6 ++ } ++ }, ++ { ++ "match": { ++ "left": { ++ "meta": { ++ "key": "mark" ++ } ++ }, ++ "op": "==", ++ "right": 12345 ++ } ++ }, ++ { ++ "reject": { ++ "type": "tcp reset" ++ } ++ } ++] ++ ++# reject ++[ ++ { ++ "reject": { ++ "expr": "port-unreachable", ++ "type": "icmpx" ++ } ++ } ++] ++ ++# meta protocol ip reject ++[ ++ { ++ "reject": { ++ "expr": "port-unreachable", ++ "type": "icmp" ++ } ++ } ++] ++ ++# meta protocol ip6 reject ++[ ++ { ++ "reject": { ++ "expr": "port-unreachable", ++ "type": "icmpv6" ++ } ++ } ++] ++ ++# meta protocol ip reject with icmp host-unreachable ++[ ++ { ++ "reject": { ++ "expr": "host-unreachable", ++ "type": "icmp" ++ } ++ } ++] ++ ++# meta protocol ip6 reject with icmpv6 no-route ++[ ++ { ++ "reject": { ++ "expr": "no-route", ++ "type": "icmpv6" ++ } ++ } ++] ++ diff --git a/0026-netlink-Avoid-crash-upon-missing-NFTNL_OBJ_CT_TIMEOU.patch b/0026-netlink-Avoid-crash-upon-missing-NFTNL_OBJ_CT_TIMEOU.patch new file mode 100644 index 0000000..3b1912f --- /dev/null +++ b/0026-netlink-Avoid-crash-upon-missing-NFTNL_OBJ_CT_TIMEOU.patch @@ -0,0 +1,44 @@ +From 0f092c2cb0cc45ea48e8d8714ad2b5bf47a0f9ee Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:25:19 +0200 +Subject: [PATCH] netlink: Avoid crash upon missing NFTNL_OBJ_CT_TIMEOUT_ARRAY + attribute + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 2a38f458f12bc032dac1b3ba63f95ca5a3c03fbd + +commit 2a38f458f12bc032dac1b3ba63f95ca5a3c03fbd +Author: Phil Sutter +Date: Thu Jun 12 20:17:22 2025 +0200 + + netlink: Avoid crash upon missing NFTNL_OBJ_CT_TIMEOUT_ARRAY attribute + + If missing, the memcpy call ends up reading from address zero. + + Fixes: c7c94802679cd ("src: add ct timeout support") + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/netlink.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/src/netlink.c b/src/netlink.c +index b1dd31d..aafc583 100644 +--- a/src/netlink.c ++++ b/src/netlink.c +@@ -1697,9 +1697,10 @@ struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx, + init_list_head(&obj->ct_timeout.timeout_list); + obj->ct_timeout.l3proto = nftnl_obj_get_u16(nlo, NFTNL_OBJ_CT_TIMEOUT_L3PROTO); + obj->ct_timeout.l4proto = nftnl_obj_get_u8(nlo, NFTNL_OBJ_CT_TIMEOUT_L4PROTO); +- memcpy(obj->ct_timeout.timeout, +- nftnl_obj_get(nlo, NFTNL_OBJ_CT_TIMEOUT_ARRAY), +- NFTNL_CTTIMEOUT_ARRAY_MAX * sizeof(uint32_t)); ++ if (nftnl_obj_is_set(nlo, NFTNL_OBJ_CT_TIMEOUT_ARRAY)) ++ memcpy(obj->ct_timeout.timeout, ++ nftnl_obj_get(nlo, NFTNL_OBJ_CT_TIMEOUT_ARRAY), ++ NFTNL_CTTIMEOUT_ARRAY_MAX * sizeof(uint32_t)); + break; + case NFT_OBJECT_LIMIT: + obj->limit.rate = diff --git a/0027-src-BASECHAIN-flag-no-longer-implies-presence-of-pri.patch b/0027-src-BASECHAIN-flag-no-longer-implies-presence-of-pri.patch new file mode 100644 index 0000000..ee77a6b --- /dev/null +++ b/0027-src-BASECHAIN-flag-no-longer-implies-presence-of-pri.patch @@ -0,0 +1,77 @@ +From 708d27f80214211252181333ef680b15e3be8dd7 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:25:19 +0200 +Subject: [PATCH] src: BASECHAIN flag no longer implies presence of priority + expression + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 44ea1936463728475768861073ca4ba34a5c2f75 + +commit 44ea1936463728475768861073ca4ba34a5c2f75 +Author: Florian Westphal +Date: Thu Jun 12 20:17:15 2025 +0200 + + src: BASECHAIN flag no longer implies presence of priority expression + + The included bogon will crash nft because print side assumes that BASECHAIN + flag presence also means that priority expression is available. + + Make the print side conditional. + + Fixes: a66b5ad9540d ("src: allow for updating devices on existing netdev chain") + Suggested-by: Pablo Neira Ayuso + Signed-off-by: Florian Westphal + +Signed-off-by: Phil Sutter +--- + src/rule.c | 16 ++++++++++------ + .../bogons/nft-f/null_ingress_type_crash | 6 ++++++ + 2 files changed, 16 insertions(+), 6 deletions(-) + create mode 100644 tests/shell/testcases/bogons/nft-f/null_ingress_type_crash + +diff --git a/src/rule.c b/src/rule.c +index a0e151d..36d590a 100644 +--- a/src/rule.c ++++ b/src/rule.c +@@ -1029,8 +1029,10 @@ static void chain_print_declaration(const struct chain *chain, + nft_print(octx, "\n\t\tcomment \"%s\"", chain->comment); + nft_print(octx, "\n"); + if (chain->flags & CHAIN_F_BASECHAIN) { +- nft_print(octx, "\t\ttype %s hook %s", chain->type.str, +- hooknum2str(chain->handle.family, chain->hook.num)); ++ if (chain->type.str) ++ nft_print(octx, "\t\ttype %s hook %s", chain->type.str, ++ hooknum2str(chain->handle.family, chain->hook.num)); ++ + if (chain->dev_array_len == 1) { + nft_print(octx, " device \"%s\"", chain->dev_array[0]); + } else if (chain->dev_array_len > 1) { +@@ -1042,10 +1044,12 @@ static void chain_print_declaration(const struct chain *chain, + } + nft_print(octx, " }"); + } +- nft_print(octx, " priority %s;", +- prio2str(octx, priobuf, sizeof(priobuf), +- chain->handle.family, chain->hook.num, +- chain->priority.expr)); ++ ++ if (chain->priority.expr) ++ nft_print(octx, " priority %s;", ++ prio2str(octx, priobuf, sizeof(priobuf), ++ chain->handle.family, chain->hook.num, ++ chain->priority.expr)); + if (chain->policy) { + mpz_export_data(&policy, chain->policy->value, + BYTEORDER_HOST_ENDIAN, sizeof(int)); +diff --git a/tests/shell/testcases/bogons/nft-f/null_ingress_type_crash b/tests/shell/testcases/bogons/nft-f/null_ingress_type_crash +new file mode 100644 +index 0000000..2ed88af +--- /dev/null ++++ b/tests/shell/testcases/bogons/nft-f/null_ingress_type_crash +@@ -0,0 +1,6 @@ ++table netdev filter1 { ++ chain c { ++ devices = { lo } ++ } ++} ++list ruleset diff --git a/0028-rule-skip-fuzzy-lookup-if-object-name-is-not-availab.patch b/0028-rule-skip-fuzzy-lookup-if-object-name-is-not-availab.patch new file mode 100644 index 0000000..cd973a9 --- /dev/null +++ b/0028-rule-skip-fuzzy-lookup-if-object-name-is-not-availab.patch @@ -0,0 +1,73 @@ +From 41f3f87fff4e57993aaefde497c572fd15af2f82 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:25:19 +0200 +Subject: [PATCH] rule: skip fuzzy lookup if object name is not available + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit de8396358f869d6d7640eae6d6287c2f7fb0d3dc + +commit de8396358f869d6d7640eae6d6287c2f7fb0d3dc +Author: Pablo Neira Ayuso +Date: Sun Jun 15 11:33:42 2025 +0200 + + rule: skip fuzzy lookup if object name is not available + + Skip fuzzy lookup for suggestions when handles are used. + + Note that 4cf97abfee61 ("rule: Avoid segfault with anonymous chains") + already skips it for chain. + + Fixes: 285bb67a11ad ("src: introduce simple hints on incorrect set") + Fixes: 9f7817a4e022 ("src: introduce simple hints on incorrect chain") + Fixes: d7476ddd5f7d ("src: introduce simple hints on incorrect table") + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/rule.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/src/rule.c b/src/rule.c +index 36d590a..6f97f77 100644 +--- a/src/rule.c ++++ b/src/rule.c +@@ -211,6 +211,9 @@ struct set *set_lookup_fuzzy(const char *set_name, + struct table *table; + struct set *set; + ++ if (!set_name) ++ return NULL; ++ + string_misspell_init(&st); + + list_for_each_entry(table, &cache->table_cache.list, cache.list) { +@@ -1200,6 +1203,9 @@ struct table *table_lookup_fuzzy(const struct handle *h, + struct string_misspell_state st; + struct table *table; + ++ if (!h->table.name) ++ return NULL; ++ + string_misspell_init(&st); + + list_for_each_entry(table, &cache->table_cache.list, cache.list) { +@@ -1680,6 +1686,9 @@ struct obj *obj_lookup_fuzzy(const char *obj_name, + struct table *table; + struct obj *obj; + ++ if (!obj_name) ++ return NULL; ++ + string_misspell_init(&st); + + list_for_each_entry(table, &cache->table_cache.list, cache.list) { +@@ -2157,6 +2166,9 @@ struct flowtable *flowtable_lookup_fuzzy(const char *ft_name, + struct table *table; + struct flowtable *ft; + ++ if (!ft_name) ++ return NULL; ++ + string_misspell_init(&st); + + list_for_each_entry(table, &cache->table_cache.list, cache.list) { diff --git a/0029-cache-assert-name-is-non-nul-when-looking-up.patch b/0029-cache-assert-name-is-non-nul-when-looking-up.patch new file mode 100644 index 0000000..4c1b7e0 --- /dev/null +++ b/0029-cache-assert-name-is-non-nul-when-looking-up.patch @@ -0,0 +1,75 @@ +From b6f184c87e7ddf8262426f17ec3bb02295d422bd Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:25:19 +0200 +Subject: [PATCH] cache: assert name is non-nul when looking up + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit f15bc7d368b7c1d897fd830f91e7db6929175b27 + +commit f15bc7d368b7c1d897fd830f91e7db6929175b27 +Author: Pablo Neira Ayuso +Date: Sun Jun 15 11:33:49 2025 +0200 + + cache: assert name is non-nul when looking up + + {table,chain,set,obj,flowtable}_cache_find() should not be called when + handles are used + + Fixes: 5ec5c706d993 ("cache: add hashtable cache for table") + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/cache.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/src/cache.c b/src/cache.c +index 0ac0f7c..2330112 100644 +--- a/src/cache.c ++++ b/src/cache.c +@@ -487,8 +487,7 @@ struct table *table_cache_find(const struct cache *cache, + struct table *table; + uint32_t hash; + +- if (!name) +- return NULL; ++ assert(name); + + hash = djb_hash(name) % NFT_CACHE_HSIZE; + list_for_each_entry(table, &cache->ht[hash], cache.hlist) { +@@ -608,6 +607,8 @@ struct chain *chain_cache_find(const struct table *table, const char *name) + struct chain *chain; + uint32_t hash; + ++ assert(name); ++ + hash = djb_hash(name) % NFT_CACHE_HSIZE; + list_for_each_entry(chain, &table->chain_cache.ht[hash], cache.hlist) { + if (!strcmp(chain->handle.chain.name, name)) +@@ -765,6 +766,8 @@ struct set *set_cache_find(const struct table *table, const char *name) + struct set *set; + uint32_t hash; + ++ assert(name); ++ + hash = djb_hash(name) % NFT_CACHE_HSIZE; + list_for_each_entry(set, &table->set_cache.ht[hash], cache.hlist) { + if (!strcmp(set->handle.set.name, name)) +@@ -851,6 +854,8 @@ struct obj *obj_cache_find(const struct table *table, const char *name, + struct obj *obj; + uint32_t hash; + ++ assert(name); ++ + hash = djb_hash(name) % NFT_CACHE_HSIZE; + list_for_each_entry(obj, &table->obj_cache.ht[hash], cache.hlist) { + if (!strcmp(obj->handle.obj.name, name) && +@@ -955,6 +960,8 @@ struct flowtable *ft_cache_find(const struct table *table, const char *name) + struct flowtable *ft; + uint32_t hash; + ++ assert(name); ++ + hash = djb_hash(name) % NFT_CACHE_HSIZE; + list_for_each_entry(ft, &table->ft_cache.ht[hash], cache.hlist) { + if (!strcmp(ft->handle.flowtable.name, name)) diff --git a/0030-parser_bison-only-reset-by-name-is-supported-by-now.patch b/0030-parser_bison-only-reset-by-name-is-supported-by-now.patch new file mode 100644 index 0000000..1dd5ff3 --- /dev/null +++ b/0030-parser_bison-only-reset-by-name-is-supported-by-now.patch @@ -0,0 +1,55 @@ +From 9f153e8f0425b02169617aefa65eab6e6f594b9f Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:25:19 +0200 +Subject: [PATCH] parser_bison: only reset by name is supported by now + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit e8c7ba0aac7ce321e61008fe9b4b8f11c3ba7e1d + +commit e8c7ba0aac7ce321e61008fe9b4b8f11c3ba7e1d +Author: Pablo Neira Ayuso +Date: Sun Jun 15 11:34:11 2025 +0200 + + parser_bison: only reset by name is supported by now + + NFT_MSG_GETSET does not support for handle lookup yet, restrict this to + reset by name by now. + + Add a bogon test reported by Florian Westphal. + + Fixes: 83e0f4402fb7 ("Implement 'reset {set,map,element}' commands") + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/parser_bison.y | 4 ++-- + tests/shell/testcases/bogons/nft-f/null_set_name_crash | 2 ++ + 2 files changed, 4 insertions(+), 2 deletions(-) + create mode 100644 tests/shell/testcases/bogons/nft-f/null_set_name_crash + +diff --git a/src/parser_bison.y b/src/parser_bison.y +index 5ced6e1..ff6660c 100644 +--- a/src/parser_bison.y ++++ b/src/parser_bison.y +@@ -1750,11 +1750,11 @@ reset_cmd : COUNTERS ruleset_spec + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_ELEMENTS, &$2, &@$, $3); + } +- | SET set_or_id_spec ++ | SET set_spec + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_SET, &$2, &@$, NULL); + } +- | MAP set_or_id_spec ++ | MAP set_spec + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_MAP, &$2, &@$, NULL); + } +diff --git a/tests/shell/testcases/bogons/nft-f/null_set_name_crash b/tests/shell/testcases/bogons/nft-f/null_set_name_crash +new file mode 100644 +index 0000000..e5d85b2 +--- /dev/null ++++ b/tests/shell/testcases/bogons/nft-f/null_set_name_crash +@@ -0,0 +1,2 @@ ++table y { } ++reset set y handle 6 diff --git a/0031-parser_bison-allow-delete-command-with-map-via-handl.patch b/0031-parser_bison-allow-delete-command-with-map-via-handl.patch new file mode 100644 index 0000000..4c47270 --- /dev/null +++ b/0031-parser_bison-allow-delete-command-with-map-via-handl.patch @@ -0,0 +1,66 @@ +From 7e5c81ad7028802c7e61f304ccba8a932cc3fac8 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:25:19 +0200 +Subject: [PATCH] parser_bison: allow delete command with map via handle + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 640312b1529c548790117635c91886a6c83e83f2 + +commit 640312b1529c548790117635c91886a6c83e83f2 +Author: Pablo Neira Ayuso +Date: Sun Jun 15 11:36:28 2025 +0200 + + parser_bison: allow delete command with map via handle + + For consistency with sets, allow delete via handle for maps too. + + Fixes: f4a34d25f6d5 ("src: list set handle and delete set via set handle") + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/parser_bison.y | 2 +- + tests/shell/testcases/cache/0008_delete_by_handle_0 | 4 ++++ + tests/shell/testcases/cache/0009_delete_by_handle_incorrect_0 | 1 + + 3 files changed, 6 insertions(+), 1 deletion(-) + +diff --git a/src/parser_bison.y b/src/parser_bison.y +index ff6660c..e252113 100644 +--- a/src/parser_bison.y ++++ b/src/parser_bison.y +@@ -1403,7 +1403,7 @@ delete_cmd : TABLE table_or_id_spec + { + $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SET, &$2, &@$, NULL); + } +- | MAP set_spec ++ | MAP set_or_id_spec + { + $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SET, &$2, &@$, NULL); + } +diff --git a/tests/shell/testcases/cache/0008_delete_by_handle_0 b/tests/shell/testcases/cache/0008_delete_by_handle_0 +index 0db4c69..9eb75e6 100755 +--- a/tests/shell/testcases/cache/0008_delete_by_handle_0 ++++ b/tests/shell/testcases/cache/0008_delete_by_handle_0 +@@ -16,6 +16,10 @@ $NFT add set t s { type ipv4_addr\; } + HANDLE=`$NFT -a list ruleset | grep "set.*handle" | cut -d' ' -f6` + $NFT delete set t handle $HANDLE + ++$NFT add map t m { type ipv4_addr : ipv4_addr\; } ++HANDLE=`$NFT -a list ruleset | grep "map.*handle" | cut -d' ' -f6` ++$NFT delete map t handle $HANDLE ++ + $NFT add flowtable t f { hook ingress priority 0\; devices = { lo } \; } + HANDLE=`$NFT -a list ruleset | grep "flowtable.*handle" | cut -d' ' -f6` + $NFT delete flowtable t handle $HANDLE +diff --git a/tests/shell/testcases/cache/0009_delete_by_handle_incorrect_0 b/tests/shell/testcases/cache/0009_delete_by_handle_incorrect_0 +index f0bb02a..dd390e7 100755 +--- a/tests/shell/testcases/cache/0009_delete_by_handle_incorrect_0 ++++ b/tests/shell/testcases/cache/0009_delete_by_handle_incorrect_0 +@@ -3,6 +3,7 @@ + $NFT delete table handle 4000 && exit 1 + $NFT delete chain t handle 4000 && exit 1 + $NFT delete set t handle 4000 && exit 1 ++$NFT delete map t handle 4000 && exit 1 + $NFT delete flowtable t handle 4000 && exit 1 + $NFT delete counter t handle 4000 && exit 1 + exit 0 diff --git a/0032-json-reject-too-long-interface-names.patch b/0032-json-reject-too-long-interface-names.patch new file mode 100644 index 0000000..9af60c3 --- /dev/null +++ b/0032-json-reject-too-long-interface-names.patch @@ -0,0 +1,172 @@ +From 0a64846a869aab6c9bc5cb1adb268a16f53bb9f8 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:25:19 +0200 +Subject: [PATCH] json: reject too long interface names + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit bed99830c4c63eae205c28a7ff914737bedb199d + +commit bed99830c4c63eae205c28a7ff914737bedb199d +Author: Florian Westphal +Date: Tue Jun 24 23:46:59 2025 +0200 + + json: reject too long interface names + + Blamed commit added a length check on ifnames to the bison parser. + Unfortunately that wasn't enough, json parser has the same issue. + + Bogon results in: + BUG: Interface length 44 exceeds limit + nft: src/mnl.c:742: nft_dev_add: Assertion `0' failed. + + After patch, included bogon results in: + Error: Invalid device at index 0. name d2345678999999999999999999999999999999012345 too long + + I intentionally did not extend evaluate.c to catch this, past sentiment + was that frontends should not send garbage. + + I'll send a followup patch to also catch this from eval stage in case there + are further reports for frontends passing in such long names. + + Fixes: fa52bc225806 ("parser: reject zero-length interface names") + Signed-off-by: Florian Westphal + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/parser_json.c | 17 +++- + .../nft-j-f/dev_name_parser_overflow_crash | 90 +++++++++++++++++++ + 2 files changed, 105 insertions(+), 2 deletions(-) + create mode 100644 tests/shell/testcases/bogons/nft-j-f/dev_name_parser_overflow_crash + +diff --git a/src/parser_json.c b/src/parser_json.c +index 936c5aa..eefb8af 100644 +--- a/src/parser_json.c ++++ b/src/parser_json.c +@@ -3062,7 +3062,13 @@ static struct expr *json_parse_devs(struct json_ctx *ctx, json_t *root) + size_t index; + + if (!json_unpack(root, "s", &dev)) { +- tmp = constant_expr_alloc(int_loc, &string_type, ++ if (strlen(dev) >= IFNAMSIZ) { ++ json_error(ctx, "Device name %s too long", dev); ++ expr_free(expr); ++ return NULL; ++ } ++ ++ tmp = constant_expr_alloc(int_loc, &ifname_type, + BYTEORDER_HOST_ENDIAN, + strlen(dev) * BITS_PER_BYTE, dev); + compound_expr_add(expr, tmp); +@@ -3080,7 +3086,14 @@ static struct expr *json_parse_devs(struct json_ctx *ctx, json_t *root) + expr_free(expr); + return NULL; + } +- tmp = constant_expr_alloc(int_loc, &string_type, ++ ++ if (strlen(dev) >= IFNAMSIZ) { ++ json_error(ctx, "Device name %s too long at index %zu", dev, index); ++ expr_free(expr); ++ return NULL; ++ } ++ ++ tmp = constant_expr_alloc(int_loc, &ifname_type, + BYTEORDER_HOST_ENDIAN, + strlen(dev) * BITS_PER_BYTE, dev); + compound_expr_add(expr, tmp); +diff --git a/tests/shell/testcases/bogons/nft-j-f/dev_name_parser_overflow_crash b/tests/shell/testcases/bogons/nft-j-f/dev_name_parser_overflow_crash +new file mode 100644 +index 0000000..8303c5c +--- /dev/null ++++ b/tests/shell/testcases/bogons/nft-j-f/dev_name_parser_overflow_crash +@@ -0,0 +1,90 @@ ++{ ++ "nftables": [ ++ { ++ "metainfo": { ++ "version": "VERSION", ++ "release_name": "RELEASE_NAME", ++ "json_schema_version": 1 ++ } ++ }, ++ { ++ "table": { ++ "family": "netdev", ++ "name": "filter1", ++ "handle": 0 ++ } ++ }, ++ { ++ "chain": { ++ "family": "netdev", ++ "table": "filter1", ++ "name": "Main_Ingress1", ++ "handle": 0, ++ "dev": "lo", ++ "type": "filter", ++ "hook": "ingress", ++ "prio": -500, ++ "policy": "accept" ++ } ++ }, ++ { ++ "table": { ++ "family": "netdev", ++ "name": "filter2", ++ "handle": 0 ++ } ++ }, ++ { ++ "chain": { ++ "family": "netdev", ++ "table": "filter2", ++ "name": "Main_Ingress2", ++ "handle": 0, ++ "dev": [ ++ "d2345678999999999999999999999999999999012345", ++ "lo" ++ ], ++ "type": "filter", ++ "hook": "ingress", ++ "prio": -500, ++ "policy": "accept" ++ } ++ }, ++ { ++ "table": { ++ "family": "netdev", ++ "name": "filter3", ++ "handle": 0 ++ } ++ }, ++ { ++ "chain": { ++ "family": "netdev", ++ "table": "filter3", ++ "name": "Main_Ingress3", ++ "handle": 0, ++ "dev": [ ++ "d23456789012345", ++ "lo" ++ ], ++ "type": "filter", ++ "hook": "ingress", ++ "prio": -500, ++ "policy": "accept" ++ } ++ }, ++ { ++ "chain": { ++ "family": "netdev", ++ "table": "filter3", ++ "name": "Main_Egress3", ++ "handle": 0, ++ "dev": "lo", ++ "type": "filter", ++ "hook": "egress", ++ "prio": -500, ++ "policy": "accept" ++ } ++ } ++ ] ++} diff --git a/0033-doc-extend-description-of-fib-expression.patch b/0033-doc-extend-description-of-fib-expression.patch new file mode 100644 index 0000000..aea4ecd --- /dev/null +++ b/0033-doc-extend-description-of-fib-expression.patch @@ -0,0 +1,142 @@ +From 34c6ee9bfa7992d449be6436cf0e550ab68e3dd1 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:25:19 +0200 +Subject: [PATCH] doc: extend description of fib expression + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit be4b61c05a2491aad596aa9243b17b13c937b347 + +commit be4b61c05a2491aad596aa9243b17b13c937b347 +Author: Florian Westphal +Date: Thu Oct 10 15:37:42 2024 +0200 + + doc: extend description of fib expression + + Describe the input keys and the result types. + Mention which input keys are mandatory and which keys are mutually + exclusive. + + Describe which hooks can be used with the various lookup modifiers + and extend the examples with more information on fib expression + capabilities. + + Closes: https://bugzilla.netfilter.org/show_bug.cgi?id=1663 + Signed-off-by: Florian Westphal + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + doc/primary-expression.txt | 77 +++++++++++++++++++++++++++++++------- + 1 file changed, 63 insertions(+), 14 deletions(-) + +diff --git a/doc/primary-expression.txt b/doc/primary-expression.txt +index e13970c..e85fa58 100644 +--- a/doc/primary-expression.txt ++++ b/doc/primary-expression.txt +@@ -304,17 +304,48 @@ table inet x { + FIB EXPRESSIONS + ~~~~~~~~~~~~~~~ + [verse] +-*fib* {*saddr* | *daddr* | *mark* | *iif* | *oif*} [*.* ...] {*oif* | *oifname* | *type*} ++*fib* 'FIB_TUPLE' 'FIB_RESULT' ++'FIB_TUPLE' := { *saddr* | *daddr*} [ *.* { *iif* | *oif* } *.* *mark* ] ++'FIB_RESULT' := { *oif* | *oifname* | *type* } + +-A fib expression queries the fib (forwarding information base) to obtain +-information such as the output interface index a particular address would use. +-The input is a tuple of elements that is used as input to the fib lookup +-functions. + +-.fib expression specific types ++A fib expression queries the fib (forwarding information base) to obtain information ++such as the output interface index. ++ ++The first arguments to the *fib* expression are the input keys to be passed to the fib lookup function. ++One of *saddr* or *daddr* is mandatory, they are also mutually exclusive. ++ ++*mark*, *iif* and *oif* keywords are optional modifiers to influence the search result, see ++the *FIB_TUPLE* keyword table below for a description. ++The *iif* and *oif* tuple keywords are also mutually exclusive. ++ ++The last argument to the *fib* expression is the desired result type. ++ ++*oif* asks to obtain the interface index that would be used to send packets to the packets source ++(*saddr* key) or destination (*daddr* key). If no routing entry is found, the returned interface ++index is 0. ++ ++*oifname* is like *oif*, but it fills the interface name instead. This is useful to check dynamic ++interfaces such as ppp devices. If no entry is found, an empty interface name is returned. ++ ++*type* returns the address type such as unicast or multicast. A complete list of supported ++address types can be shown with *nft* *describe* *fib_addrtype*. ++ ++.FIB_TUPLE keywords + [options="header"] + |================== +-|Keyword| Description| Type ++|flag| Description ++|daddr| Perform a normal route lookup: search fib for route to the *destination address* of the packet. ++|saddr| Perform a reverse route lookup: search the fib for route to the *source address* of the packet. ++|mark | consider the packet mark (nfmark) when querying the fib. ++|iif | if fib lookups provides a route then check its output interface is identical to the packets *input* interface. ++|oif | if fib lookups provides a route then check its output interface is identical to the packets *output* interface. This flag can only be used with the *type* result. ++|======================= ++ ++.FIB_RESULT keywords ++[options="header"] ++|================== ++|Keyword| Description| Result Type + |oif| + Output interface index| + integer (32 bit) +@@ -323,25 +354,43 @@ Output interface name| + string + |type| + Address type | +-fib_addrtype ++fib_addrtype (see *nft* *describe* *fib_addrtype* for a list) + |======================= + +-Use *nft* *describe* *fib_addrtype* to get a list of all address types. ++The *oif* and *oifname* result is only valid in the *prerouting*, *input* and *forward* hooks. ++The *type* can be queried from any one of *prerouting*, *input*, *forward* *output* and *postrouting*. ++ ++For *type*, the presence of the *iif* keyword in the 'FIB_TUPLE' modifiers restrict the available ++hooks to those where the packet is associated with an incoming interface, i.e. *prerouting*, *input* and *forward*. ++Likewise, the *oif* keyword in the 'FIB_TUPLE' modifier list will limit the available hooks to ++*forward*, *output* and *postrouting*. + + .Using fib expressions + ---------------------- + # drop packets without a reverse path + filter prerouting fib saddr . iif oif missing drop + +-In this example, 'saddr . iif' looks up routing information based on the source address and the input interface. +-oif picks the output interface index from the routing information. ++In this example, 'saddr . iif' looks up a route to the *source address* of the packet and restricts matching ++results to the interface that the packet arrived on, then stores the output interface index from the obtained ++fib route result. ++ + If no route was found for the source address/input interface combination, the output interface index is zero. +-In case the input interface is specified as part of the input key, the output interface index is always the same as the input interface index or zero. +-If only 'saddr oif' is given, then oif can be any interface index or zero. ++Hence, this rule will drop all packets that do not have a strict reverse path (hypothetical reply packet ++would be sent via the interface the tested packet arrived on). ++ ++If only 'saddr oif' is used as the input key, then this rule would only drop packets where the fib cannot ++find a route. In most setups this will never drop packets because the default route is returned. + +-# drop packets to address not configured on incoming interface ++# drop packets if the destination ip address is not configured on the incoming interface + filter prerouting fib daddr . iif type != { local, broadcast, multicast } drop + ++This queries the fib based on the current packets' destination address and the incoming interface. ++ ++If the packet is sent to a unicast address that is configured on a different interface, then the packet ++will be dropped as such an address would be classified as 'unicast' type. ++Without the 'iif' modifier, any address configured on the local machine is 'local', and unicast addresses ++not configured on any interface would return the type 'unicast'. ++ + # perform lookup in a specific 'blackhole' table (0xdead, needs ip appropriate ip rule) + filter prerouting meta mark set 0xdead fib daddr . mark type vmap { blackhole : drop, prohibit : jump prohibited, unreachable : drop } + ---------------------- diff --git a/0034-json-fix-use-after-free-in-table_flags_json.patch b/0034-json-fix-use-after-free-in-table_flags_json.patch new file mode 100644 index 0000000..106c57b --- /dev/null +++ b/0034-json-fix-use-after-free-in-table_flags_json.patch @@ -0,0 +1,57 @@ +From e3bdd843cd1b342082c7b01a888c33d05a91117b Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:25:19 +0200 +Subject: [PATCH] json: fix use after free in table_flags_json() + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit b04512cf30de1ba6657facba5ebe2321e17c2727 + +commit b04512cf30de1ba6657facba5ebe2321e17c2727 +Author: Thomas Haller +Date: Tue Nov 14 16:29:25 2023 +0100 + + json: fix use after free in table_flags_json() + + Add `$NFT -j list ruleset` to the end of "tests/shell/testcases/transactions/table_onoff". + Then valgrind will find this issue: + + $ make -j && ./tests/shell/run-tests.sh tests/shell/testcases/transactions/table_onoff -V + + Gives: + + ==286== Invalid read of size 4 + ==286== at 0x49B0261: do_dump (dump.c:211) + ==286== by 0x49B08B8: do_dump (dump.c:378) + ==286== by 0x49B08B8: do_dump (dump.c:378) + ==286== by 0x49B04F7: do_dump (dump.c:273) + ==286== by 0x49B08B8: do_dump (dump.c:378) + ==286== by 0x49B0E84: json_dump_callback (dump.c:465) + ==286== by 0x48AF22A: do_command_list_json (json.c:2016) + ==286== by 0x48732F1: do_command_list (rule.c:2335) + ==286== by 0x48737F5: do_command (rule.c:2605) + ==286== by 0x48A867D: nft_netlink (libnftables.c:42) + ==286== by 0x48A92B1: nft_run_cmd_from_buffer (libnftables.c:597) + ==286== by 0x402CBA: main (main.c:533) + + Fixes: e70354f53e9f ("libnftables: Implement JSON output support") + Signed-off-by: Thomas Haller + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/json.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/json.c b/src/json.c +index 39b1ada..e5bec4f 100644 +--- a/src/json.c ++++ b/src/json.c +@@ -517,7 +517,7 @@ static json_t *table_flags_json(const struct table *table) + json_decref(root); + return NULL; + case 1: +- json_unpack(root, "[o]", &tmp); ++ json_unpack(root, "[O]", &tmp); + json_decref(root); + root = tmp; + break; diff --git a/0035-json-Print-single-fib-flag-as-non-array.patch b/0035-json-Print-single-fib-flag-as-non-array.patch new file mode 100644 index 0000000..b4e89db --- /dev/null +++ b/0035-json-Print-single-fib-flag-as-non-array.patch @@ -0,0 +1,48 @@ +From 1c481d182bca10f9384f00545a4e2b85acffb76c Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:25:19 +0200 +Subject: [PATCH] json: Print single fib flag as non-array + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit dbe5c44f2b891c1d305cc94a7ea9dd963a7b6100 + +commit dbe5c44f2b891c1d305cc94a7ea9dd963a7b6100 +Author: Phil Sutter +Date: Thu May 8 16:40:41 2025 +0200 + + json: Print single fib flag as non-array + + Check array size and reduce the array if possible. + + The zero array length check is dead code here due to the surrounding 'if + (flags)' block, but it's a common idiom one could replace by a shared + routine later. + + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + src/json.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/src/json.c b/src/json.c +index e5bec4f..9320336 100644 +--- a/src/json.c ++++ b/src/json.c +@@ -935,7 +935,15 @@ json_t *fib_expr_json(const struct expr *expr, struct output_ctx *octx) + } + if (flags) + json_array_append_new(tmp, json_integer(flags)); +- json_object_set_new(root, "flags", tmp); ++ ++ if (json_array_size(tmp) > 1) { ++ json_object_set_new(root, "flags", tmp); ++ } else { ++ if (json_array_size(tmp)) ++ json_object_set(root, "flags", ++ json_array_get(tmp, 0)); ++ json_decref(tmp); ++ } + } + return json_pack("{s:o}", "fib", root); + } diff --git a/0036-json-Print-single-synproxy-flags-as-non-array.patch b/0036-json-Print-single-synproxy-flags-as-non-array.patch new file mode 100644 index 0000000..df02a2c --- /dev/null +++ b/0036-json-Print-single-synproxy-flags-as-non-array.patch @@ -0,0 +1,42 @@ +From 399c60ed7706c110123aa28f282f45899a2cd56f Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:25:19 +0200 +Subject: [PATCH] json: Print single synproxy flags as non-array + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 38f99ee84fe6bd029fbc3d1389f42ab82fa9789c + +commit 38f99ee84fe6bd029fbc3d1389f42ab82fa9789c +Author: Phil Sutter +Date: Thu May 8 16:44:39 2025 +0200 + + json: Print single synproxy flags as non-array + + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + src/json.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/src/json.c b/src/json.c +index 9320336..9b3a639 100644 +--- a/src/json.c ++++ b/src/json.c +@@ -1685,10 +1685,14 @@ json_t *synproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + if (stmt->synproxy.flags & NF_SYNPROXY_OPT_SACK_PERM) + json_array_append_new(flags, json_string("sack-perm")); + +- if (json_array_size(flags) > 0) ++ if (json_array_size(flags) > 1) { + json_object_set_new(root, "flags", flags); +- else ++ } else { ++ if (json_array_size(flags)) ++ json_object_set(root, "flags", ++ json_array_get(flags, 0)); + json_decref(flags); ++ } + + if (!json_object_size(root)) { + json_decref(root); diff --git a/0037-json-Introduce-json_add_array_new.patch b/0037-json-Introduce-json_add_array_new.patch new file mode 100644 index 0000000..f16a1f9 --- /dev/null +++ b/0037-json-Introduce-json_add_array_new.patch @@ -0,0 +1,221 @@ +From a3f57e683bbf70bbba05bd495a2f5d8c32eb6aa4 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:25:20 +0200 +Subject: [PATCH] json: Introduce json_add_array_new() + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit a740f2036ad0d50b4a91e110611563809dac420a +Conflicts: Context change due to missing commit a0a15e4dd0576 + ("json: Fix for memleak in __binop_expr_json") + +commit a740f2036ad0d50b4a91e110611563809dac420a +Author: Phil Sutter +Date: Thu May 8 17:28:02 2025 +0200 + + json: Introduce json_add_array_new() + + Propagate nat_stmt_add_array() to a generic helper for use in all spots + adding an array property which may reduce to a single item or even not + exist at all. + + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + src/json.c | 99 +++++++++++++----------------------------------------- + 1 file changed, 24 insertions(+), 75 deletions(-) + +diff --git a/src/json.c b/src/json.c +index 9b3a639..c8abc56 100644 +--- a/src/json.c ++++ b/src/json.c +@@ -42,6 +42,18 @@ + }) + #endif + ++static void json_add_array_new(json_t *obj, const char *name, json_t *array) ++{ ++ if (json_array_size(array) > 1) { ++ json_object_set_new(obj, name, array); ++ } else { ++ if (json_array_size(array)) ++ json_object_set(obj, name, ++ json_array_get(array, 0)); ++ json_decref(array); ++ } ++} ++ + static json_t *expr_print_json(const struct expr *expr, struct output_ctx *octx) + { + const struct expr_ops *ops; +@@ -188,14 +200,7 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set) + json_array_append_new(tmp, json_pack("s", "timeout")); + if (set->flags & NFT_SET_EVAL) + json_array_append_new(tmp, json_pack("s", "dynamic")); +- +- if (json_array_size(tmp) > 1) { +- json_object_set_new(root, "flags", tmp); +- } else { +- if (json_array_size(tmp)) +- json_object_set(root, "flags", json_array_get(tmp, 0)); +- json_decref(tmp); +- } ++ json_add_array_new(root, "flags", tmp); + + if (set->timeout) { + tmp = json_integer(set->timeout / 1000); +@@ -443,19 +448,16 @@ static json_t *obj_print_json(const struct obj *obj) + json_decref(tmp); + break; + case NFT_OBJECT_SYNPROXY: +- flags = json_array(); + tmp = json_pack("{s:i, s:i}", + "mss", obj->synproxy.mss, + "wscale", obj->synproxy.wscale); ++ ++ flags = json_array(); + if (obj->synproxy.flags & NF_SYNPROXY_OPT_TIMESTAMP) + json_array_append_new(flags, json_string("timestamp")); + if (obj->synproxy.flags & NF_SYNPROXY_OPT_SACK_PERM) + json_array_append_new(flags, json_string("sack-perm")); +- +- if (json_array_size(flags) > 0) +- json_object_set_new(tmp, "flags", flags); +- else +- json_decref(flags); ++ json_add_array_new(tmp, "flags", flags); + + json_object_update(root, tmp); + json_decref(tmp); +@@ -512,31 +514,18 @@ static json_t *table_flags_json(const struct table *table) + flags >>= 1; + i++; + } +- switch (json_array_size(root)) { +- case 0: +- json_decref(root); +- return NULL; +- case 1: +- json_unpack(root, "[O]", &tmp); +- json_decref(root); +- root = tmp; +- break; +- } + return root; + } + + static json_t *table_print_json(const struct table *table) + { +- json_t *root, *tmp; ++ json_t *root; + + root = json_pack("{s:s, s:s, s:I}", + "family", family2str(table->handle.family), + "name", table->handle.table.name, + "handle", table->handle.handle.id); +- +- tmp = table_flags_json(table); +- if (tmp) +- json_object_set_new(root, "flags", tmp); ++ json_add_array_new(root, "flags", table_flags_json(table)); + + if (table->comment) + json_object_set_new(root, "comment", json_string(table->comment)); +@@ -936,14 +925,7 @@ json_t *fib_expr_json(const struct expr *expr, struct output_ctx *octx) + if (flags) + json_array_append_new(tmp, json_integer(flags)); + +- if (json_array_size(tmp) > 1) { +- json_object_set_new(root, "flags", tmp); +- } else { +- if (json_array_size(tmp)) +- json_object_set(root, "flags", +- json_array_get(tmp, 0)); +- json_decref(tmp); +- } ++ json_add_array_new(root, "flags", tmp); + } + return json_pack("{s:o}", "fib", root); + } +@@ -1383,14 +1365,7 @@ json_t *log_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + if (stmt->log.logflags & NF_LOG_MACDECODE) + json_array_append_new(flags, json_string("ether")); + } +- if (json_array_size(flags) > 1) { +- json_object_set_new(root, "flags", flags); +- } else { +- if (json_array_size(flags)) +- json_object_set(root, "flags", +- json_array_get(flags, 0)); +- json_decref(flags); +- } ++ json_add_array_new(root, "flags", flags); + + if (!json_object_size(root)) { + json_decref(root); +@@ -1425,18 +1400,6 @@ static json_t *nat_type_flags_json(uint32_t type_flags) + return array; + } + +-static void nat_stmt_add_array(json_t *root, const char *name, json_t *array) +-{ +- if (json_array_size(array) > 1) { +- json_object_set_new(root, name, array); +- } else { +- if (json_array_size(array)) +- json_object_set(root, name, +- json_array_get(array, 0)); +- json_decref(array); +- } +-} +- + json_t *nat_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + { + json_t *root = json_object(); +@@ -1458,12 +1421,12 @@ json_t *nat_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + json_object_set_new(root, "port", + expr_print_json(stmt->nat.proto, octx)); + +- nat_stmt_add_array(root, "flags", array); ++ json_add_array_new(root, "flags", array); + + if (stmt->nat.type_flags) { + array = nat_type_flags_json(stmt->nat.type_flags); + +- nat_stmt_add_array(root, "type_flags", array); ++ json_add_array_new(root, "type_flags", array); + } + + if (!json_object_size(root)) { +@@ -1615,14 +1578,7 @@ json_t *queue_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + json_array_append_new(flags, json_string("bypass")); + if (stmt->queue.flags & NFT_QUEUE_FLAG_CPU_FANOUT) + json_array_append_new(flags, json_string("fanout")); +- if (json_array_size(flags) > 1) { +- json_object_set_new(root, "flags", flags); +- } else { +- if (json_array_size(flags)) +- json_object_set(root, "flags", +- json_array_get(flags, 0)); +- json_decref(flags); +- } ++ json_add_array_new(root, "flags", flags); + + if (!json_object_size(root)) { + json_decref(root); +@@ -1685,14 +1641,7 @@ json_t *synproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + if (stmt->synproxy.flags & NF_SYNPROXY_OPT_SACK_PERM) + json_array_append_new(flags, json_string("sack-perm")); + +- if (json_array_size(flags) > 1) { +- json_object_set_new(root, "flags", flags); +- } else { +- if (json_array_size(flags)) +- json_object_set(root, "flags", +- json_array_get(flags, 0)); +- json_decref(flags); +- } ++ json_add_array_new(root, "flags", flags); + + if (!json_object_size(root)) { + json_decref(root); diff --git a/0038-json-work-around-fuzzer-induced-assert-crashes.patch b/0038-json-work-around-fuzzer-induced-assert-crashes.patch new file mode 100644 index 0000000..1b3fdbb --- /dev/null +++ b/0038-json-work-around-fuzzer-induced-assert-crashes.patch @@ -0,0 +1,1048 @@ +From 4434eeff06246e9be0a95ee054e8bb79412c166d Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:29:54 +0200 +Subject: [PATCH] json: work around fuzzer-induced assert crashes + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 18c24d89b9977ddc0900be64fe9e95e7ac1ce896 +Conflicts: Context change due to missing commit 0ac39384fd9e4 + ("json: Accept more than two operands in binary expressions") + +commit 18c24d89b9977ddc0900be64fe9e95e7ac1ce896 +Author: Florian Westphal +Date: Mon Mar 31 16:47:11 2025 +0200 + + json: work around fuzzer-induced assert crashes + + fuzzer can cause assert failures due to json_pack() returning a NULL + value and therefore triggering the assert(out) in __json_pack macro. + + All instances I saw are due to invalid UTF-8 strings, i.e., table/chain + names with non-text characters in them. + + Work around this for now, replace the assert with a plaintext error + message and return NULL instead of abort(). + + Signed-off-by: Florian Westphal + +Signed-off-by: Phil Sutter +--- + src/json.c | 271 +++++++++--------- + .../nft-j-f/Assertion__out_failed_assert | 6 + + 2 files changed, 146 insertions(+), 131 deletions(-) + create mode 100644 tests/shell/testcases/bogons/nft-j-f/Assertion__out_failed_assert + +diff --git a/src/json.c b/src/json.c +index c8abc56..1e2fe49 100644 +--- a/src/json.c ++++ b/src/json.c +@@ -33,14 +33,23 @@ + #include + #include + +-#ifdef DEBUG +-#define __json_pack json_pack +-#define json_pack(...) ({ \ +- json_t *__out = __json_pack(__VA_ARGS__); \ +- assert(__out); \ +- __out; \ +-}) +-#endif ++static json_t *__nft_json_pack(unsigned int line, const char *fmt, ...) ++{ ++ json_error_t error; ++ json_t *value; ++ va_list ap; ++ ++ va_start(ap, fmt); ++ value = json_vpack_ex(&error, 0, fmt, ap); ++ va_end(ap); ++ ++ if (value) ++ return value; ++ ++ fprintf(stderr, "%s:%d: json_pack failure (%s)\n", __FILE__, line, error.text); ++ return NULL; ++} ++#define nft_json_pack(...) __nft_json_pack(__LINE__, __VA_ARGS__) + + static void json_add_array_new(json_t *obj, const char *name, json_t *array) + { +@@ -75,7 +84,7 @@ static json_t *expr_print_json(const struct expr *expr, struct output_ctx *octx) + fclose(octx->output_fp); + octx->output_fp = fp; + +- return json_pack("s", buf); ++ return nft_json_pack("s", buf); + } + + static json_t *set_dtype_json(const struct expr *key) +@@ -90,7 +99,7 @@ static json_t *set_dtype_json(const struct expr *key) + if (!root) + root = jtok; + else if (json_is_string(root)) +- root = json_pack("[o, o]", root, jtok); ++ root = nft_json_pack("[o, o]", root, jtok); + else + json_array_append_new(root, jtok); + tok = strtok_r(NULL, " .", &tok_safe); +@@ -107,7 +116,7 @@ static json_t *set_key_dtype_json(const struct set *set, + if (!use_typeof) + return set_dtype_json(set->key); + +- return json_pack("{s:o}", "typeof", expr_print_json(set->key, octx)); ++ return nft_json_pack("{s:o}", "typeof", expr_print_json(set->key, octx)); + } + + static json_t *stmt_print_json(const struct stmt *stmt, struct output_ctx *octx) +@@ -129,7 +138,7 @@ static json_t *stmt_print_json(const struct stmt *stmt, struct output_ctx *octx) + fclose(octx->output_fp); + octx->output_fp = fp; + +- return json_pack("s", buf); ++ return nft_json_pack("s", buf); + } + + static json_t *set_stmt_list_json(const struct list_head *stmt_list, +@@ -168,7 +177,7 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set) + type = "set"; + } + +- root = json_pack("{s:s, s:s, s:s, s:o, s:I}", ++ root = nft_json_pack("{s:s, s:s, s:s, s:o, s:I}", + "family", family2str(set->handle.family), + "name", set->handle.set.name, + "table", set->handle.table.name, +@@ -182,24 +191,24 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set) + + if (!(set->flags & (NFT_SET_CONSTANT))) { + if (set->policy != NFT_SET_POL_PERFORMANCE) { +- tmp = json_pack("s", set_policy2str(set->policy)); ++ tmp = nft_json_pack("s", set_policy2str(set->policy)); + json_object_set_new(root, "policy", tmp); + } + if (set->desc.size) { +- tmp = json_pack("i", set->desc.size); ++ tmp = nft_json_pack("i", set->desc.size); + json_object_set_new(root, "size", tmp); + } + } + + tmp = json_array(); + if (set->flags & NFT_SET_CONSTANT) +- json_array_append_new(tmp, json_pack("s", "constant")); ++ json_array_append_new(tmp, nft_json_pack("s", "constant")); + if (set->flags & NFT_SET_INTERVAL) +- json_array_append_new(tmp, json_pack("s", "interval")); ++ json_array_append_new(tmp, nft_json_pack("s", "interval")); + if (set->flags & NFT_SET_TIMEOUT) +- json_array_append_new(tmp, json_pack("s", "timeout")); ++ json_array_append_new(tmp, nft_json_pack("s", "timeout")); + if (set->flags & NFT_SET_EVAL) +- json_array_append_new(tmp, json_pack("s", "dynamic")); ++ json_array_append_new(tmp, nft_json_pack("s", "dynamic")); + json_add_array_new(root, "flags", tmp); + + if (set->timeout) { +@@ -207,7 +216,7 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set) + json_object_set_new(root, "timeout", tmp); + } + if (set->gc_int) { +- tmp = json_pack("i", set->gc_int / 1000); ++ tmp = nft_json_pack("i", set->gc_int / 1000); + json_object_set_new(root, "gc-interval", tmp); + } + +@@ -226,7 +235,7 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set) + set_stmt_list_json(&set->stmt_list, octx)); + } + +- return json_pack("{s:o}", type, root); ++ return nft_json_pack("{s:o}", type, root); + } + + /* XXX: Merge with set_print_json()? */ +@@ -235,7 +244,7 @@ static json_t *element_print_json(struct output_ctx *octx, + { + json_t *root = expr_print_json(set->init, octx); + +- return json_pack("{s: {s:s, s:s, s:s, s:o}}", "element", ++ return nft_json_pack("{s: {s:s, s:s, s:s, s:o}}", "element", + "family", family2str(set->handle.family), + "table", set->handle.table.name, + "name", set->handle.set.name, +@@ -248,7 +257,7 @@ static json_t *rule_print_json(struct output_ctx *octx, + const struct stmt *stmt; + json_t *root, *tmp; + +- root = json_pack("{s:s, s:s, s:s, s:I}", ++ root = nft_json_pack("{s:s, s:s, s:s, s:I}", + "family", family2str(rule->handle.family), + "table", rule->handle.table.name, + "chain", rule->handle.chain.name, +@@ -268,7 +277,7 @@ static json_t *rule_print_json(struct output_ctx *octx, + json_decref(tmp); + } + +- return json_pack("{s:o}", "rule", root); ++ return nft_json_pack("{s:o}", "rule", root); + } + + static json_t *chain_print_json(const struct chain *chain) +@@ -276,7 +285,7 @@ static json_t *chain_print_json(const struct chain *chain) + json_t *root, *tmp, *devs = NULL; + int priority, policy, i; + +- root = json_pack("{s:s, s:s, s:s, s:I}", ++ root = nft_json_pack("{s:s, s:s, s:s, s:I}", + "family", family2str(chain->handle.family), + "table", chain->handle.table.name, + "name", chain->handle.chain.name, +@@ -296,7 +305,7 @@ static json_t *chain_print_json(const struct chain *chain) + policy = NF_ACCEPT; + } + +- tmp = json_pack("{s:s, s:s, s:i, s:s}", ++ tmp = nft_json_pack("{s:s, s:s, s:i, s:s}", + "type", chain->type.str, + "hook", hooknum2str(chain->handle.family, + chain->hook.num), +@@ -308,7 +317,7 @@ static json_t *chain_print_json(const struct chain *chain) + if (!devs) + devs = json_string(dev); + else if (json_is_string(devs)) +- devs = json_pack("[o, s]", devs, dev); ++ devs = nft_json_pack("[o, s]", devs, dev); + else + json_array_append_new(devs, json_string(dev)); + } +@@ -319,7 +328,7 @@ static json_t *chain_print_json(const struct chain *chain) + json_decref(tmp); + } + +- return json_pack("{s:o}", "chain", root); ++ return nft_json_pack("{s:o}", "chain", root); + } + + static json_t *proto_name_json(uint8_t proto) +@@ -355,28 +364,28 @@ static json_t *obj_print_json(const struct obj *obj) + json_t *root, *tmp, *flags; + uint64_t rate, burst; + +- root = json_pack("{s:s, s:s, s:s, s:I}", ++ root = nft_json_pack("{s:s, s:s, s:s, s:I}", + "family", family2str(obj->handle.family), + "name", obj->handle.obj.name, + "table", obj->handle.table.name, + "handle", obj->handle.handle.id); + + if (obj->comment) { +- tmp = json_pack("{s:s}", "comment", obj->comment); ++ tmp = nft_json_pack("{s:s}", "comment", obj->comment); + json_object_update(root, tmp); + json_decref(tmp); + } + + switch (obj->type) { + case NFT_OBJECT_COUNTER: +- tmp = json_pack("{s:I, s:I}", ++ tmp = nft_json_pack("{s:I, s:I}", + "packets", obj->counter.packets, + "bytes", obj->counter.bytes); + json_object_update(root, tmp); + json_decref(tmp); + break; + case NFT_OBJECT_QUOTA: +- tmp = json_pack("{s:I, s:I, s:b}", ++ tmp = nft_json_pack("{s:I, s:I, s:b}", + "bytes", obj->quota.bytes, + "used", obj->quota.used, + "inv", obj->quota.flags & NFT_QUOTA_F_INV); +@@ -384,13 +393,13 @@ static json_t *obj_print_json(const struct obj *obj) + json_decref(tmp); + break; + case NFT_OBJECT_SECMARK: +- tmp = json_pack("{s:s}", ++ tmp = nft_json_pack("{s:s}", + "context", obj->secmark.ctx); + json_object_update(root, tmp); + json_decref(tmp); + break; + case NFT_OBJECT_CT_HELPER: +- tmp = json_pack("{s:s, s:o, s:s}", ++ tmp = nft_json_pack("{s:s, s:o, s:s}", + "type", obj->ct_helper.name, "protocol", + proto_name_json(obj->ct_helper.l4proto), + "l3proto", family2str(obj->ct_helper.l3proto)); +@@ -400,7 +409,7 @@ static json_t *obj_print_json(const struct obj *obj) + case NFT_OBJECT_CT_TIMEOUT: + tmp = timeout_policy_json(obj->ct_timeout.l4proto, + obj->ct_timeout.timeout); +- tmp = json_pack("{s:o, s:s, s:o}", ++ tmp = nft_json_pack("{s:o, s:s, s:o}", + "protocol", + proto_name_json(obj->ct_timeout.l4proto), + "l3proto", family2str(obj->ct_timeout.l3proto), +@@ -409,7 +418,7 @@ static json_t *obj_print_json(const struct obj *obj) + json_decref(tmp); + break; + case NFT_OBJECT_CT_EXPECT: +- tmp = json_pack("{s:o, s:I, s:I, s:I, s:s}", ++ tmp = nft_json_pack("{s:o, s:I, s:I, s:I, s:s}", + "protocol", + proto_name_json(obj->ct_expect.l4proto), + "dport", obj->ct_expect.dport, +@@ -428,7 +437,7 @@ static json_t *obj_print_json(const struct obj *obj) + burst_unit = get_rate(obj->limit.burst, &burst); + } + +- tmp = json_pack("{s:I, s:s}", ++ tmp = nft_json_pack("{s:I, s:s}", + "rate", rate, + "per", get_unit(obj->limit.unit)); + +@@ -448,9 +457,9 @@ static json_t *obj_print_json(const struct obj *obj) + json_decref(tmp); + break; + case NFT_OBJECT_SYNPROXY: +- tmp = json_pack("{s:i, s:i}", +- "mss", obj->synproxy.mss, +- "wscale", obj->synproxy.wscale); ++ tmp = nft_json_pack("{s:i, s:i}", ++ "mss", obj->synproxy.mss, ++ "wscale", obj->synproxy.wscale); + + flags = json_array(); + if (obj->synproxy.flags & NF_SYNPROXY_OPT_TIMESTAMP) +@@ -464,7 +473,7 @@ static json_t *obj_print_json(const struct obj *obj) + break; + } + +- return json_pack("{s:o}", type, root); ++ return nft_json_pack("{s:o}", type, root); + } + + static json_t *flowtable_print_json(const struct flowtable *ftable) +@@ -477,7 +486,7 @@ static json_t *flowtable_print_json(const struct flowtable *ftable) + BYTEORDER_HOST_ENDIAN, sizeof(int)); + } + +- root = json_pack("{s:s, s:s, s:s, s:I, s:s, s:i}", ++ root = nft_json_pack("{s:s, s:s, s:s, s:I, s:s, s:i}", + "family", family2str(ftable->handle.family), + "name", ftable->handle.flowtable.name, + "table", ftable->handle.table.name, +@@ -490,14 +499,14 @@ static json_t *flowtable_print_json(const struct flowtable *ftable) + if (!devs) + devs = json_string(dev); + else if (json_is_string(devs)) +- devs = json_pack("[o, s]", devs, dev); ++ devs = nft_json_pack("[o, s]", devs, dev); + else + json_array_append_new(devs, json_string(dev)); + } + if (devs) + json_object_set_new(root, "dev", devs); + +- return json_pack("{s:o}", "flowtable", root); ++ return nft_json_pack("{s:o}", "flowtable", root); + } + + static json_t *table_flags_json(const struct table *table) +@@ -521,7 +530,7 @@ static json_t *table_print_json(const struct table *table) + { + json_t *root; + +- root = json_pack("{s:s, s:s, s:I}", ++ root = nft_json_pack("{s:s, s:s, s:I}", + "family", family2str(table->handle.family), + "name", table->handle.table.name, + "handle", table->handle.handle.id); +@@ -530,7 +539,7 @@ static json_t *table_print_json(const struct table *table) + if (table->comment) + json_object_set_new(root, "comment", json_string(table->comment)); + +- return json_pack("{s:o}", "table", root); ++ return nft_json_pack("{s:o}", "table", root); + } + + json_t *flagcmp_expr_json(const struct expr *expr, struct output_ctx *octx) +@@ -549,14 +558,14 @@ json_t *flagcmp_expr_json(const struct expr *expr, struct output_ctx *octx) + + json_t *binop_expr_json(const struct expr *expr, struct output_ctx *octx) + { +- return json_pack("{s:[o, o]}", expr_op_symbols[expr->op], ++ return nft_json_pack("{s:[o, o]}", expr_op_symbols[expr->op], + expr_print_json(expr->left, octx), + expr_print_json(expr->right, octx)); + } + + json_t *relational_expr_json(const struct expr *expr, struct output_ctx *octx) + { +- return json_pack("{s:{s:s, s:o, s:o}}", "match", ++ return nft_json_pack("{s:{s:s, s:o, s:o}}", "match", + "op", expr_op_symbols[expr->op] ? : "in", + "left", expr_print_json(expr->left, octx), + "right", expr_print_json(expr->right, octx)); +@@ -569,7 +578,7 @@ json_t *range_expr_json(const struct expr *expr, struct output_ctx *octx) + + octx->flags &= ~NFT_CTX_OUTPUT_SERVICE; + octx->flags |= NFT_CTX_OUTPUT_NUMERIC_PROTO; +- root = json_pack("{s:[o, o]}", "range", ++ root = nft_json_pack("{s:[o, o]}", "range", + expr_print_json(expr->left, octx), + expr_print_json(expr->right, octx)); + octx->flags = flags; +@@ -579,7 +588,7 @@ json_t *range_expr_json(const struct expr *expr, struct output_ctx *octx) + + json_t *meta_expr_json(const struct expr *expr, struct output_ctx *octx) + { +- return json_pack("{s:{s:s}}", "meta", ++ return nft_json_pack("{s:{s:s}}", "meta", + "key", meta_templates[expr->meta.key].token); + } + +@@ -589,23 +598,23 @@ json_t *payload_expr_json(const struct expr *expr, struct output_ctx *octx) + + if (payload_is_known(expr)) { + if (expr->payload.inner_desc) { +- root = json_pack("{s:s, s:s, s:s}", ++ root = nft_json_pack("{s:s, s:s, s:s}", + "tunnel", expr->payload.inner_desc->name, + "protocol", expr->payload.desc->name, + "field", expr->payload.tmpl->token); + } else { +- root = json_pack("{s:s, s:s}", ++ root = nft_json_pack("{s:s, s:s}", + "protocol", expr->payload.desc->name, + "field", expr->payload.tmpl->token); + } + } else { +- root = json_pack("{s:s, s:i, s:i}", ++ root = nft_json_pack("{s:s, s:i, s:i}", + "base", proto_base_tokens[expr->payload.base], + "offset", expr->payload.offset, + "len", expr->len); + } + +- return json_pack("{s:o}", "payload", root); ++ return nft_json_pack("{s:o}", "payload", root); + } + + json_t *ct_expr_json(const struct expr *expr, struct output_ctx *octx) +@@ -614,7 +623,7 @@ json_t *ct_expr_json(const struct expr *expr, struct output_ctx *octx) + enum nft_ct_keys key = expr->ct.key; + json_t *root; + +- root = json_pack("{s:s}", "key", ct_templates[key].token); ++ root = nft_json_pack("{s:s}", "key", ct_templates[key].token); + + if (expr->ct.direction < 0) + goto out; +@@ -622,7 +631,7 @@ json_t *ct_expr_json(const struct expr *expr, struct output_ctx *octx) + if (dirstr) + json_object_set_new(root, "dir", json_string(dirstr)); + out: +- return json_pack("{s:o}", "ct", root); ++ return nft_json_pack("{s:o}", "ct", root); + } + + json_t *concat_expr_json(const struct expr *expr, struct output_ctx *octx) +@@ -633,7 +642,7 @@ json_t *concat_expr_json(const struct expr *expr, struct output_ctx *octx) + list_for_each_entry(i, &expr->expressions, list) + json_array_append_new(array, expr_print_json(i, octx)); + +- return json_pack("{s:o}", "concat", array); ++ return nft_json_pack("{s:o}", "concat", array); + } + + json_t *set_expr_json(const struct expr *expr, struct output_ctx *octx) +@@ -644,7 +653,7 @@ json_t *set_expr_json(const struct expr *expr, struct output_ctx *octx) + list_for_each_entry(i, &expr->expressions, list) + json_array_append_new(array, expr_print_json(i, octx)); + +- return json_pack("{s:o}", "set", array); ++ return nft_json_pack("{s:o}", "set", array); + } + + json_t *set_ref_expr_json(const struct expr *expr, struct output_ctx *octx) +@@ -652,7 +661,7 @@ json_t *set_ref_expr_json(const struct expr *expr, struct output_ctx *octx) + if (set_is_anonymous(expr->set->flags)) { + return expr_print_json(expr->set->init, octx); + } else { +- return json_pack("s+", "@", expr->set->handle.set.name); ++ return nft_json_pack("s+", "@", expr->set->handle.set.name); + } + } + +@@ -668,7 +677,7 @@ json_t *set_elem_expr_json(const struct expr *expr, struct output_ctx *octx) + /* these element attributes require formal set elem syntax */ + if (expr->timeout || expr->expiration || expr->comment || + !list_empty(&expr->stmt_list)) { +- root = json_pack("{s:o}", "val", root); ++ root = nft_json_pack("{s:o}", "val", root); + + if (expr->timeout) { + tmp = json_integer(expr->timeout / 1000); +@@ -690,7 +699,7 @@ json_t *set_elem_expr_json(const struct expr *expr, struct output_ctx *octx) + /* TODO: only one statement per element. */ + break; + } +- return json_pack("{s:o}", "elem", root); ++ return nft_json_pack("{s:o}", "elem", root); + } + + return root; +@@ -700,7 +709,7 @@ json_t *prefix_expr_json(const struct expr *expr, struct output_ctx *octx) + { + json_t *root = expr_print_json(expr->prefix, octx); + +- return json_pack("{s:{s:o, s:i}}", "prefix", ++ return nft_json_pack("{s:{s:o, s:i}}", "prefix", + "addr", root, + "len", expr->prefix_len); + } +@@ -713,7 +722,7 @@ json_t *list_expr_json(const struct expr *expr, struct output_ctx *octx) + list_for_each_entry(i, &expr->expressions, list) + json_array_append_new(array, expr_print_json(i, octx)); + +- //return json_pack("{s:s, s:o}", "type", "list", "val", array); ++ //return nft_json_pack("{s:s, s:o}", "type", "list", "val", array); + return array; + } + +@@ -724,7 +733,7 @@ json_t *unary_expr_json(const struct expr *expr, struct output_ctx *octx) + + json_t *mapping_expr_json(const struct expr *expr, struct output_ctx *octx) + { +- return json_pack("[o, o]", ++ return nft_json_pack("[o, o]", + expr_print_json(expr->left, octx), + expr_print_json(expr->right, octx)); + } +@@ -737,7 +746,7 @@ json_t *map_expr_json(const struct expr *expr, struct output_ctx *octx) + expr->mappings->set->data->dtype->type == TYPE_VERDICT) + type = "vmap"; + +- return json_pack("{s:{s:o, s:o}}", type, ++ return nft_json_pack("{s:{s:o, s:o}}", type, + "key", expr_print_json(expr->map, octx), + "data", expr_print_json(expr->mappings, octx)); + } +@@ -759,36 +768,36 @@ json_t *exthdr_expr_json(const struct expr *expr, struct output_ctx *octx) + if (offset < 4) + offstr = offstrs[offset]; + +- root = json_pack("{s:s+}", "name", desc, offstr); ++ root = nft_json_pack("{s:s+}", "name", desc, offstr); + + if (!is_exists) + json_object_set_new(root, "field", json_string(field)); + } else { +- root = json_pack("{s:i, s:i, s:i}", ++ root = nft_json_pack("{s:i, s:i, s:i}", + "base", expr->exthdr.raw_type, + "offset", expr->exthdr.offset, + "len", expr->len); + } + +- return json_pack("{s:o}", "tcp option", root); ++ return nft_json_pack("{s:o}", "tcp option", root); + } + + if (expr->exthdr.op == NFT_EXTHDR_OP_DCCP) { +- root = json_pack("{s:i}", "type", expr->exthdr.raw_type); +- return json_pack("{s:o}", "dccp option", root); ++ root = nft_json_pack("{s:i}", "type", expr->exthdr.raw_type); ++ return nft_json_pack("{s:o}", "dccp option", root); + } + +- root = json_pack("{s:s}", "name", desc); ++ root = nft_json_pack("{s:s}", "name", desc); + if (!is_exists) + json_object_set_new(root, "field", json_string(field)); + + switch (expr->exthdr.op) { + case NFT_EXTHDR_OP_IPV4: +- return json_pack("{s:o}", "ip option", root); ++ return nft_json_pack("{s:o}", "ip option", root); + case NFT_EXTHDR_OP_SCTP: +- return json_pack("{s:o}", "sctp chunk", root); ++ return nft_json_pack("{s:o}", "sctp chunk", root); + default: +- return json_pack("{s:o}", "exthdr", root); ++ return nft_json_pack("{s:o}", "exthdr", root); + } + } + +@@ -825,15 +834,15 @@ json_t *verdict_expr_json(const struct expr *expr, struct output_ctx *octx) + return NULL; + } + if (chain) +- return json_pack("{s:{s:o}}", name, "target", chain); ++ return nft_json_pack("{s:{s:o}}", name, "target", chain); + else +- return json_pack("{s:n}", name); ++ return nft_json_pack("{s:n}", name); + } + + json_t *rt_expr_json(const struct expr *expr, struct output_ctx *octx) + { + const char *key = rt_templates[expr->rt.key].token; +- json_t *root = json_pack("{s:s}", "key", key); ++ json_t *root = nft_json_pack("{s:s}", "key", key); + const char *family = NULL; + + switch (expr->rt.key) { +@@ -850,7 +859,7 @@ json_t *rt_expr_json(const struct expr *expr, struct output_ctx *octx) + if (family) + json_object_set_new(root, "family", json_string(family)); + +- return json_pack("{s:o}", "rt", root); ++ return nft_json_pack("{s:o}", "rt", root); + } + + json_t *numgen_expr_json(const struct expr *expr, struct output_ctx *octx) +@@ -869,7 +878,7 @@ json_t *numgen_expr_json(const struct expr *expr, struct output_ctx *octx) + break; + } + +- return json_pack("{s:{s:s, s:i, s:i}}", "numgen", ++ return nft_json_pack("{s:{s:s, s:i, s:i}}", "numgen", + "mode", mode, + "mod", expr->numgen.mod, + "offset", expr->numgen.offset); +@@ -891,7 +900,7 @@ json_t *hash_expr_json(const struct expr *expr, struct output_ctx *octx) + break; + } + +- root = json_pack("{s:i}", "mod", expr->hash.mod); ++ root = nft_json_pack("{s:i}", "mod", expr->hash.mod); + if (expr->hash.seed_set) + json_object_set_new(root, "seed", + json_integer(expr->hash.seed)); +@@ -901,7 +910,7 @@ json_t *hash_expr_json(const struct expr *expr, struct output_ctx *octx) + if (jexpr) + json_object_set_new(root, "expr", jexpr); + +- return json_pack("{s:o}", type, root); ++ return nft_json_pack("{s:o}", type, root); + } + + json_t *fib_expr_json(const struct expr *expr, struct output_ctx *octx) +@@ -910,7 +919,7 @@ json_t *fib_expr_json(const struct expr *expr, struct output_ctx *octx) + unsigned int flags = expr->fib.flags & ~NFTA_FIB_F_PRESENT; + json_t *root; + +- root = json_pack("{s:s}", "result", fib_result_str(expr->fib.result)); ++ root = nft_json_pack("{s:s}", "result", fib_result_str(expr->fib.result)); + + if (flags) { + json_t *tmp = json_array(); +@@ -927,7 +936,7 @@ json_t *fib_expr_json(const struct expr *expr, struct output_ctx *octx) + + json_add_array_new(root, "flags", tmp); + } +- return json_pack("{s:o}", "fib", root); ++ return nft_json_pack("{s:o}", "fib", root); + } + + static json_t *symbolic_constant_json(const struct symbol_table *tbl, +@@ -1000,7 +1009,7 @@ json_t *constant_expr_json(const struct expr *expr, struct output_ctx *octx) + + json_t *socket_expr_json(const struct expr *expr, struct output_ctx *octx) + { +- return json_pack("{s:{s:s}}", "socket", "key", ++ return nft_json_pack("{s:{s:s}}", "socket", "key", + socket_templates[expr->socket.key].token); + } + +@@ -1009,9 +1018,9 @@ json_t *osf_expr_json(const struct expr *expr, struct output_ctx *octx) + json_t *root; + + if (expr->osf.flags & NFT_OSF_F_VERSION) +- root = json_pack("{s:s}", "key", "version"); ++ root = nft_json_pack("{s:s}", "key", "version"); + else +- root = json_pack("{s:s}", "key", "name"); ++ root = nft_json_pack("{s:s}", "key", "name"); + + switch (expr->osf.ttl) { + case 1: +@@ -1022,7 +1031,7 @@ json_t *osf_expr_json(const struct expr *expr, struct output_ctx *octx) + break; + } + +- return json_pack("{s:o}", "osf", root); ++ return nft_json_pack("{s:o}", "osf", root); + } + + json_t *xfrm_expr_json(const struct expr *expr, struct output_ctx *octx) +@@ -1059,7 +1068,7 @@ json_t *xfrm_expr_json(const struct expr *expr, struct output_ctx *octx) + break; + } + +- root = json_pack("{s:s}", "key", name); ++ root = nft_json_pack("{s:s}", "key", name); + + if (family) + json_object_set_new(root, "family", json_string(family)); +@@ -1067,7 +1076,7 @@ json_t *xfrm_expr_json(const struct expr *expr, struct output_ctx *octx) + json_object_set_new(root, "dir", json_string(dirstr)); + json_object_set_new(root, "spnum", json_integer(expr->xfrm.spnum)); + +- return json_pack("{s:o}", "ipsec", root); ++ return nft_json_pack("{s:o}", "ipsec", root); + } + + json_t *integer_type_json(const struct expr *expr, struct output_ctx *octx) +@@ -1189,21 +1198,21 @@ json_t *expr_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + + json_t *flow_offload_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + { +- return json_pack("{s:{s:s, s:s+}}", "flow", ++ return nft_json_pack("{s:{s:s, s:s+}}", "flow", + "op", "add", "flowtable", + "@", stmt->flow.table_name); + } + + json_t *payload_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + { +- return json_pack("{s: {s:o, s:o}}", "mangle", ++ return nft_json_pack("{s: {s:o, s:o}}", "mangle", + "key", expr_print_json(stmt->payload.expr, octx), + "value", expr_print_json(stmt->payload.val, octx)); + } + + json_t *exthdr_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + { +- return json_pack("{s: {s:o, s:o}}", "mangle", ++ return nft_json_pack("{s: {s:o, s:o}}", "mangle", + "key", expr_print_json(stmt->exthdr.expr, octx), + "value", expr_print_json(stmt->exthdr.val, octx)); + } +@@ -1215,7 +1224,7 @@ json_t *quota_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + json_t *root; + + data_unit = get_rate(stmt->quota.bytes, &bytes); +- root = json_pack("{s:I, s:s}", ++ root = nft_json_pack("{s:I, s:s}", + "val", bytes, + "val_unit", data_unit); + +@@ -1227,7 +1236,7 @@ json_t *quota_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + json_object_set_new(root, "used_unit", json_string(data_unit)); + } + +- return json_pack("{s:o}", "quota", root); ++ return nft_json_pack("{s:o}", "quota", root); + } + + json_t *ct_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +@@ -1240,7 +1249,7 @@ json_t *ct_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + }, + }; + +- return json_pack("{s:{s:o, s:o}}", "mangle", ++ return nft_json_pack("{s:{s:o, s:o}}", "mangle", + "key", ct_expr_json(&expr, octx), + "value", expr_print_json(stmt->ct.expr, octx)); + } +@@ -1258,7 +1267,7 @@ json_t *limit_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + burst_unit = get_rate(stmt->limit.burst, &burst); + } + +- root = json_pack("{s:I, s:I, s:s}", ++ root = nft_json_pack("{s:I, s:I, s:s}", + "rate", rate, + "burst", burst, + "per", get_unit(stmt->limit.unit)); +@@ -1270,14 +1279,14 @@ json_t *limit_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + json_object_set_new(root, "burst_unit", + json_string(burst_unit)); + +- return json_pack("{s:o}", "limit", root); ++ return nft_json_pack("{s:o}", "limit", root); + } + + json_t *fwd_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + { + json_t *root, *tmp; + +- root = json_pack("{s:o}", "dev", expr_print_json(stmt->fwd.dev, octx)); ++ root = nft_json_pack("{s:o}", "dev", expr_print_json(stmt->fwd.dev, octx)); + + if (stmt->fwd.addr) { + tmp = json_string(family2str(stmt->fwd.family)); +@@ -1287,12 +1296,12 @@ json_t *fwd_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + json_object_set_new(root, "addr", tmp); + } + +- return json_pack("{s:o}", "fwd", root); ++ return nft_json_pack("{s:o}", "fwd", root); + } + + json_t *notrack_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + { +- return json_pack("{s:n}", "notrack"); ++ return nft_json_pack("{s:n}", "notrack"); + } + + json_t *dup_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +@@ -1300,27 +1309,27 @@ json_t *dup_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + json_t *root; + + if (stmt->dup.to) { +- root = json_pack("{s:o}", "addr", expr_print_json(stmt->dup.to, octx)); ++ root = nft_json_pack("{s:o}", "addr", expr_print_json(stmt->dup.to, octx)); + if (stmt->dup.dev) + json_object_set_new(root, "dev", + expr_print_json(stmt->dup.dev, octx)); + } else { + root = json_null(); + } +- return json_pack("{s:o}", "dup", root); ++ return nft_json_pack("{s:o}", "dup", root); + } + + json_t *meta_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + { + json_t *root; + +- root = json_pack("{s:{s:s}}", "meta", ++ root = nft_json_pack("{s:{s:s}}", "meta", + "key", meta_templates[stmt->meta.key].token); +- root = json_pack("{s:o, s:o}", ++ root = nft_json_pack("{s:o, s:o}", + "key", root, + "value", expr_print_json(stmt->meta.expr, octx)); + +- return json_pack("{s:o}", "mangle", root); ++ return nft_json_pack("{s:o}", "mangle", root); + } + + json_t *log_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +@@ -1372,7 +1381,7 @@ json_t *log_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + root = json_null(); + } + +- return json_pack("{s:o}", "log", root); ++ return nft_json_pack("{s:o}", "log", root); + } + + static json_t *nat_flags_json(uint32_t flags) +@@ -1434,7 +1443,7 @@ json_t *nat_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + root = json_null(); + } + +- return json_pack("{s:o}", nat_etype2str(stmt->nat.type), root); ++ return nft_json_pack("{s:o}", nat_etype2str(stmt->nat.type), root); + } + + json_t *reject_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +@@ -1464,7 +1473,7 @@ json_t *reject_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + } + + if (!type && !jexpr) +- return json_pack("{s:n}", "reject"); ++ return nft_json_pack("{s:n}", "reject"); + + root = json_object(); + if (type) +@@ -1472,15 +1481,15 @@ json_t *reject_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + if (jexpr) + json_object_set_new(root, "expr", jexpr); + +- return json_pack("{s:o}", "reject", root); ++ return nft_json_pack("{s:o}", "reject", root); + } + + json_t *counter_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + { + if (nft_output_stateless(octx)) +- return json_pack("{s:n}", "counter"); ++ return nft_json_pack("{s:n}", "counter"); + +- return json_pack("{s:{s:I, s:I}}", "counter", ++ return nft_json_pack("{s:{s:I, s:I}}", "counter", + "packets", stmt->counter.packets, + "bytes", stmt->counter.bytes); + } +@@ -1488,16 +1497,16 @@ json_t *counter_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + json_t *last_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + { + if (nft_output_stateless(octx) || stmt->last.set == 0) +- return json_pack("{s:n}", "last"); ++ return nft_json_pack("{s:n}", "last"); + +- return json_pack("{s:{s:I}}", "last", "used", stmt->last.used); ++ return nft_json_pack("{s:{s:I}}", "last", "used", stmt->last.used); + } + + json_t *set_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + { + json_t *root; + +- root = json_pack("{s:s, s:o, s:s+}", ++ root = nft_json_pack("{s:s, s:o, s:s+}", + "op", set_stmt_op_names[stmt->set.op], + "elem", expr_print_json(stmt->set.key, octx), + "set", "@", stmt->set.set->set->handle.set.name); +@@ -1508,14 +1517,14 @@ json_t *set_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + octx)); + } + +- return json_pack("{s:o}", "set", root); ++ return nft_json_pack("{s:o}", "set", root); + } + + json_t *map_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + { + json_t *root; + +- root = json_pack("{s:s, s:o, s:o, s:s+}", ++ root = nft_json_pack("{s:s, s:o, s:o, s:s+}", + "op", set_stmt_op_names[stmt->map.op], + "elem", expr_print_json(stmt->map.key, octx), + "data", expr_print_json(stmt->map.data, octx), +@@ -1527,7 +1536,7 @@ json_t *map_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + octx)); + } + +- return json_pack("{s:o}", "map", root); ++ return nft_json_pack("{s:o}", "map", root); + } + + json_t *objref_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +@@ -1539,7 +1548,7 @@ json_t *objref_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + else + name = objref_type_name(stmt->objref.type); + +- return json_pack("{s:o}", name, expr_print_json(stmt->objref.expr, octx)); ++ return nft_json_pack("{s:o}", name, expr_print_json(stmt->objref.expr, octx)); + } + + json_t *meter_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +@@ -1551,7 +1560,7 @@ json_t *meter_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + tmp = stmt_print_json(stmt->meter.stmt, octx); + octx->flags = flags; + +- root = json_pack("{s:o, s:o, s:i}", ++ root = nft_json_pack("{s:o, s:o, s:i}", + "key", expr_print_json(stmt->meter.key, octx), + "stmt", tmp, + "size", stmt->meter.size); +@@ -1560,7 +1569,7 @@ json_t *meter_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + json_object_set_new(root, "name", tmp); + } + +- return json_pack("{s:o}", "meter", root); ++ return nft_json_pack("{s:o}", "meter", root); + } + + json_t *queue_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +@@ -1585,7 +1594,7 @@ json_t *queue_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + root = json_null(); + } + +- return json_pack("{s:o}", "queue", root); ++ return nft_json_pack("{s:o}", "queue", root); + } + + json_t *verdict_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +@@ -1595,12 +1604,12 @@ json_t *verdict_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + + json_t *connlimit_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + { +- json_t *root = json_pack("{s:i}", "val", stmt->connlimit.count); ++ json_t *root = nft_json_pack("{s:i}", "val", stmt->connlimit.count); + + if (stmt->connlimit.flags & NFT_CONNLIMIT_F_INV) + json_object_set_new(root, "inv", json_true()); + +- return json_pack("{s:o}", "ct count", root); ++ return nft_json_pack("{s:o}", "ct count", root); + } + + json_t *tproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +@@ -1623,7 +1632,7 @@ json_t *tproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + json_object_set_new(root, "port", tmp); + } + +- return json_pack("{s:o}", "tproxy", root); ++ return nft_json_pack("{s:o}", "tproxy", root); + } + + json_t *synproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +@@ -1648,12 +1657,12 @@ json_t *synproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + root = json_null(); + } + +- return json_pack("{s:o}", "synproxy", root); ++ return nft_json_pack("{s:o}", "synproxy", root); + } + + json_t *optstrip_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + { +- return json_pack("{s:o}", "reset", ++ return nft_json_pack("{s:o}", "reset", + expr_print_json(stmt->optstrip.expr, octx)); + } + +@@ -1665,7 +1674,7 @@ json_t *xt_stmt_json(const struct stmt *stmt, struct output_ctx *octx) + [NFT_XT_WATCHER] = "watcher", + }; + +- return json_pack("{s:{s:s, s:s}}", "xt", ++ return nft_json_pack("{s:{s:s, s:s}}", "xt", + "type", xt_typename[stmt->xt.type], + "name", stmt->xt.name); + } +@@ -1811,7 +1820,7 @@ static json_t *do_list_set_json(struct netlink_ctx *ctx, + return json_null(); + } + +- return json_pack("[o]", set_print_json(&ctx->nft->output, set)); ++ return nft_json_pack("[o]", set_print_json(&ctx->nft->output, set)); + } + + static json_t *do_list_sets_json(struct netlink_ctx *ctx, struct cmd *cmd) +@@ -1909,7 +1918,7 @@ static json_t *do_list_flowtables_json(struct netlink_ctx *ctx, struct cmd *cmd) + + static json_t *generate_json_metainfo(void) + { +- return json_pack("{s: {s:s, s:s, s:i}}", "metainfo", ++ return nft_json_pack("{s: {s:s, s:s, s:i}}", "metainfo", + "version", PACKAGE_VERSION, + "release_name", RELEASE_NAME, + "json_schema_version", JSON_SCHEMA_VERSION); +@@ -2000,7 +2009,7 @@ int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd) + + json_array_insert_new(root, 0, generate_json_metainfo()); + +- root = json_pack("{s:o}", "nftables", root); ++ root = nft_json_pack("{s:o}", "nftables", root); + json_dumpf(root, ctx->nft->output.output_fp, 0); + json_decref(root); + fprintf(ctx->nft->output.output_fp, "\n"); +@@ -2013,7 +2022,7 @@ static void monitor_print_json(struct netlink_mon_handler *monh, + { + struct nft_ctx *nft = monh->ctx->nft; + +- obj = json_pack("{s:o}", cmd, obj); ++ obj = nft_json_pack("{s:o}", cmd, obj); + if (nft_output_echo(&nft->output) && !nft->json_root) { + json_array_append_new(nft->json_echo, obj); + } else { +diff --git a/tests/shell/testcases/bogons/nft-j-f/Assertion__out_failed_assert b/tests/shell/testcases/bogons/nft-j-f/Assertion__out_failed_assert +new file mode 100644 +index 0000000..f8ce089 +--- /dev/null ++++ b/tests/shell/testcases/bogons/nft-j-f/Assertion__out_failed_assert +@@ -0,0 +1,6 @@ ++table ip test-ip { ++ quota htquota { comment "tŽst5" 5 kbytes ++ } ++} ++list ruleset ++add rule t c counter diff --git a/0039-parser_json-Introduce-parse_flags_array.patch b/0039-parser_json-Introduce-parse_flags_array.patch new file mode 100644 index 0000000..41b7379 --- /dev/null +++ b/0039-parser_json-Introduce-parse_flags_array.patch @@ -0,0 +1,769 @@ +From df090a258ef0753e76112b58ba3165a452fc24be Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:30:19 +0200 +Subject: [PATCH] parser_json: Introduce parse_flags_array() + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 3550dd69632f3a883ab6593daeffb514c67dfb8c + +commit 3550dd69632f3a883ab6593daeffb514c67dfb8c +Author: Phil Sutter +Date: Thu Apr 10 16:42:42 2025 +0200 + + parser_json: Introduce parse_flags_array() + + Various objects support a 'flags' property with value usually being an + array of strings. There is a special case, when merely a single flag is + set: The value may be a string representing this flag. + + Introduce a function assisting in parsing this polymorphic value. Have + callers pass a parser callback translating a single flag name into a + corresponding value. Luckily, these single flag parsers are very common + already. + + As a side-effect, enable the single flag spec for set flags as well and + update the documentation accordingly. + + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + doc/libnftables-json.adoc | 5 +- + src/parser_json.c | 466 +++++++++++--------------------------- + 2 files changed, 136 insertions(+), 335 deletions(-) + +diff --git a/doc/libnftables-json.adoc b/doc/libnftables-json.adoc +index c3e3bcc..27c96c3 100644 +--- a/doc/libnftables-json.adoc ++++ b/doc/libnftables-json.adoc +@@ -317,7 +317,7 @@ ____ + "handle":* 'NUMBER'*, + "type":* 'SET_TYPE'*, + "policy":* 'SET_POLICY'*, +- "flags": [* 'SET_FLAG_LIST' *], ++ "flags":* 'SET_FLAGS'*, + "elem":* 'SET_ELEMENTS'*, + "timeout":* 'NUMBER'*, + "gc-interval":* 'NUMBER'*, +@@ -332,7 +332,7 @@ ____ + "type":* 'SET_TYPE'*, + "map":* 'STRING'*, + "policy":* 'SET_POLICY'*, +- "flags": [* 'SET_FLAG_LIST' *], ++ "flags":* 'SET_FLAGS'*, + "elem":* 'SET_ELEMENTS'*, + "timeout":* 'NUMBER'*, + "gc-interval":* 'NUMBER'*, +@@ -342,6 +342,7 @@ ____ + 'SET_TYPE' := 'STRING' | *[* 'SET_TYPE_LIST' *]* | *{ "typeof":* 'EXPRESSION' *}* + 'SET_TYPE_LIST' := 'STRING' [*,* 'SET_TYPE_LIST' ] + 'SET_POLICY' := *"performance"* | *"memory"* ++'SET_FLAGS' := 'SET_FLAG' | *[* 'SET_FLAG_LIST' *]* + 'SET_FLAG_LIST' := 'SET_FLAG' [*,* 'SET_FLAG_LIST' ] + 'SET_FLAG' := *"constant"* | *"interval"* | *"timeout"* + 'SET_ELEMENTS' := 'EXPRESSION' | *[* 'EXPRESSION_LIST' *]* +diff --git a/src/parser_json.c b/src/parser_json.c +index eefb8af..0bf81d1 100644 +--- a/src/parser_json.c ++++ b/src/parser_json.c +@@ -193,6 +193,60 @@ static int json_unpack_stmt(struct json_ctx *ctx, json_t *root, + return 1; + } + ++/** ++ * parse_flags_array - parse JSON property as an array of flags ++ * ++ * @ctx: JSON parser context ++ * @obj: JSON object to extract property from ++ * @key: name of property containing the flags array ++ * @flag_parser: Callback parsing a single flag, returns 0 on error ++ * ++ * The property value may be a string representing a single flag or an array of ++ * strings representing a number of flags whose values are ORed together. ++ * ++ * @return: Combined flag value, 0 if no such property found or -1 if data is ++ * malformed or flag parsing failed. ++ */ ++static int parse_flags_array(struct json_ctx *ctx, json_t *obj, const char *key, ++ unsigned int (*flag_parser)(const char *flag)) ++{ ++ json_t *value = json_object_get(obj, key), *tmp; ++ size_t index; ++ int ret = 0; ++ ++ if (!value) ++ return 0; ++ ++ if (json_is_string(value)) { ++ ret = flag_parser(json_string_value(value)); ++ return ret ?: -1; ++ } ++ ++ if (!json_is_array(value)) { ++ json_error(ctx, ++ "Expecting string or array in '%s' property.", key); ++ return -1; ++ } ++ ++ json_array_foreach(value, index, tmp) { ++ int flag = 0; ++ ++ if (json_is_string(tmp)) ++ flag = flag_parser(json_string_value(tmp)); ++ ++ if (!flag) { ++ json_error(ctx, ++ "Invalid flag in '%s' property array at index %zu.", ++ key, index); ++ return -1; ++ } ++ ++ ret |= flag; ++ } ++ ++ return ret; ++} ++ + static int parse_family(const char *name, uint32_t *family) + { + unsigned int i; +@@ -1060,7 +1114,7 @@ static struct expr *json_parse_hash_expr(struct json_ctx *ctx, + return hash_expr; + } + +-static int fib_flag_parse(const char *name, int *flags) ++static unsigned int fib_flag_parse(const char *name) + { + const char *fib_flags[] = { + "saddr", +@@ -1072,12 +1126,10 @@ static int fib_flag_parse(const char *name, int *flags) + unsigned int i; + + for (i = 0; i < array_size(fib_flags); i++) { +- if (!strcmp(name, fib_flags[i])) { +- *flags |= (1 << i); +- return 0; +- } ++ if (!strcmp(name, fib_flags[i])) ++ return 1 << i; + } +- return 1; ++ return 0; + } + + static struct expr *json_parse_fib_expr(struct json_ctx *ctx, +@@ -1090,11 +1142,9 @@ static struct expr *json_parse_fib_expr(struct json_ctx *ctx, + [NFT_FIB_RESULT_ADDRTYPE] = "type", + }; + enum nft_fib_result resultval = NFT_FIB_RESULT_UNSPEC; +- json_t *flags, *value; + const char *result; +- unsigned int i; +- size_t index; + int flagval = 0; ++ unsigned int i; + + if (json_unpack_err(ctx, root, "{s:s}", "result", &result)) + return NULL; +@@ -1110,34 +1160,9 @@ static struct expr *json_parse_fib_expr(struct json_ctx *ctx, + return NULL; + } + +- if (!json_unpack(root, "{s:o}", "flags", &flags)) { +- const char *flag; +- +- if (json_is_string(flags)) { +- flag = json_string_value(flags); +- +- if (fib_flag_parse(flag, &flagval)) { +- json_error(ctx, "Invalid fib flag '%s'.", flag); +- return NULL; +- } +- } else if (!json_is_array(flags)) { +- json_error(ctx, "Unexpected object type in fib tuple."); +- return NULL; +- } +- +- json_array_foreach(flags, index, value) { +- if (!json_is_string(value)) { +- json_error(ctx, "Unexpected object type in fib flags array at index %zd.", index); +- return NULL; +- } +- flag = json_string_value(value); +- +- if (fib_flag_parse(flag, &flagval)) { +- json_error(ctx, "Invalid fib flag '%s'.", flag); +- return NULL; +- } +- } +- } ++ flagval = parse_flags_array(ctx, root, "flags", fib_flag_parse); ++ if (flagval < 0) ++ return NULL; + + /* sanity checks from fib_expr in parser_bison.y */ + +@@ -2090,8 +2115,7 @@ static struct stmt *json_parse_secmark_stmt(struct json_ctx *ctx, + return stmt; + } + +-static int json_parse_nat_flag(struct json_ctx *ctx, +- json_t *root, int *flags) ++static unsigned int json_parse_nat_flag(const char *flag) + { + const struct { + const char *flag; +@@ -2102,51 +2126,16 @@ static int json_parse_nat_flag(struct json_ctx *ctx, + { "persistent", NF_NAT_RANGE_PERSISTENT }, + { "netmap", NF_NAT_RANGE_NETMAP }, + }; +- const char *flag; + unsigned int i; + +- assert(flags); +- +- if (!json_is_string(root)) { +- json_error(ctx, "Invalid nat flag type %s, expected string.", +- json_typename(root)); +- return 1; +- } +- flag = json_string_value(root); + for (i = 0; i < array_size(flag_tbl); i++) { +- if (!strcmp(flag, flag_tbl[i].flag)) { +- *flags |= flag_tbl[i].val; +- return 0; +- } +- } +- json_error(ctx, "Unknown nat flag '%s'.", flag); +- return 1; +-} +- +-static int json_parse_nat_flags(struct json_ctx *ctx, json_t *root) +-{ +- int flags = 0; +- json_t *value; +- size_t index; +- +- if (json_is_string(root)) { +- json_parse_nat_flag(ctx, root, &flags); +- return flags; +- } else if (!json_is_array(root)) { +- json_error(ctx, "Invalid nat flags type %s.", +- json_typename(root)); +- return -1; +- } +- json_array_foreach(root, index, value) { +- if (json_parse_nat_flag(ctx, value, &flags)) +- json_error(ctx, "Parsing nat flag at index %zu failed.", +- index); ++ if (!strcmp(flag, flag_tbl[i].flag)) ++ return flag_tbl[i].val; + } +- return flags; ++ return 0; + } + +-static int json_parse_nat_type_flag(struct json_ctx *ctx, +- json_t *root, int *flags) ++static unsigned int json_parse_nat_type_flag(const char *flag) + { + const struct { + const char *flag; +@@ -2156,47 +2145,13 @@ static int json_parse_nat_type_flag(struct json_ctx *ctx, + { "prefix", STMT_NAT_F_PREFIX }, + { "concat", STMT_NAT_F_CONCAT }, + }; +- const char *flag; + unsigned int i; + +- assert(flags); +- +- if (!json_is_string(root)) { +- json_error(ctx, "Invalid nat type flag type %s, expected string.", +- json_typename(root)); +- return 1; +- } +- flag = json_string_value(root); + for (i = 0; i < array_size(flag_tbl); i++) { +- if (!strcmp(flag, flag_tbl[i].flag)) { +- *flags |= flag_tbl[i].val; +- return 0; +- } +- } +- json_error(ctx, "Unknown nat type flag '%s'.", flag); +- return 1; +-} +- +-static int json_parse_nat_type_flags(struct json_ctx *ctx, json_t *root) +-{ +- int flags = 0; +- json_t *value; +- size_t index; +- +- if (json_is_string(root)) { +- json_parse_nat_type_flag(ctx, root, &flags); +- return flags; +- } else if (!json_is_array(root)) { +- json_error(ctx, "Invalid nat flags type %s.", +- json_typename(root)); +- return -1; +- } +- json_array_foreach(root, index, value) { +- if (json_parse_nat_type_flag(ctx, value, &flags)) +- json_error(ctx, "Parsing nat type flag at index %zu failed.", +- index); ++ if (!strcmp(flag, flag_tbl[i].flag)) ++ return flag_tbl[i].val; + } +- return flags; ++ return 0; + } + + static int nat_type_parse(const char *type) +@@ -2219,7 +2174,7 @@ static int nat_type_parse(const char *type) + static struct stmt *json_parse_nat_stmt(struct json_ctx *ctx, + const char *key, json_t *value) + { +- int type, familyval; ++ int type, familyval, flags; + struct stmt *stmt; + json_t *tmp; + +@@ -2252,24 +2207,20 @@ static struct stmt *json_parse_nat_stmt(struct json_ctx *ctx, + return NULL; + } + } +- if (!json_unpack(value, "{s:o}", "flags", &tmp)) { +- int flags = json_parse_nat_flags(ctx, tmp); +- +- if (flags < 0) { +- stmt_free(stmt); +- return NULL; +- } +- stmt->nat.flags = flags; ++ flags = parse_flags_array(ctx, value, "flags", json_parse_nat_flag); ++ if (flags < 0) { ++ stmt_free(stmt); ++ return NULL; + } +- if (!json_unpack(value, "{s:o}", "type_flags", &tmp)) { +- int flags = json_parse_nat_type_flags(ctx, tmp); ++ stmt->nat.flags = flags; + +- if (flags < 0) { +- stmt_free(stmt); +- return NULL; +- } +- stmt->nat.type_flags = flags; ++ flags = parse_flags_array(ctx, value, "type_flags", ++ json_parse_nat_type_flag); ++ if (flags < 0) { ++ stmt_free(stmt); ++ return NULL; + } ++ stmt->nat.type_flags = flags; + + return stmt; + } +@@ -2481,8 +2432,7 @@ static struct stmt *json_parse_map_stmt(struct json_ctx *ctx, + return stmt; + } + +-static int json_parse_log_flag(struct json_ctx *ctx, +- json_t *root, int *flags) ++static unsigned int json_parse_log_flag(const char *flag) + { + const struct { + const char *flag; +@@ -2495,47 +2445,13 @@ static int json_parse_log_flag(struct json_ctx *ctx, + { "ether", NF_LOG_MACDECODE }, + { "all", NF_LOG_MASK }, + }; +- const char *flag; + unsigned int i; + +- assert(flags); +- +- if (!json_is_string(root)) { +- json_error(ctx, "Invalid log flag type %s, expected string.", +- json_typename(root)); +- return 1; +- } +- flag = json_string_value(root); + for (i = 0; i < array_size(flag_tbl); i++) { +- if (!strcmp(flag, flag_tbl[i].flag)) { +- *flags |= flag_tbl[i].val; +- return 0; +- } +- } +- json_error(ctx, "Unknown log flag '%s'.", flag); +- return 1; +-} +- +-static int json_parse_log_flags(struct json_ctx *ctx, json_t *root) +-{ +- int flags = 0; +- json_t *value; +- size_t index; +- +- if (json_is_string(root)) { +- json_parse_log_flag(ctx, root, &flags); +- return flags; +- } else if (!json_is_array(root)) { +- json_error(ctx, "Invalid log flags type %s.", +- json_typename(root)); +- return -1; +- } +- json_array_foreach(root, index, value) { +- if (json_parse_log_flag(ctx, value, &flags)) +- json_error(ctx, "Parsing log flag at index %zu failed.", +- index); ++ if (!strcmp(flag, flag_tbl[i].flag)) ++ return flag_tbl[i].val; + } +- return flags; ++ return 0; + } + + static struct stmt *json_parse_log_stmt(struct json_ctx *ctx, +@@ -2543,8 +2459,7 @@ static struct stmt *json_parse_log_stmt(struct json_ctx *ctx, + { + const char *tmpstr; + struct stmt *stmt; +- json_t *jflags; +- int tmp; ++ int tmp, flags; + + stmt = log_stmt_alloc(int_loc); + +@@ -2577,20 +2492,17 @@ static struct stmt *json_parse_log_stmt(struct json_ctx *ctx, + stmt->log.level = level; + stmt->log.flags |= STMT_LOG_LEVEL; + } +- if (!json_unpack(value, "{s:o}", "flags", &jflags)) { +- int flags = json_parse_log_flags(ctx, jflags); +- +- if (flags < 0) { +- stmt_free(stmt); +- return NULL; +- } +- stmt->log.logflags = flags; ++ flags = parse_flags_array(ctx, value, "flags", json_parse_log_flag); ++ if (flags < 0) { ++ stmt_free(stmt); ++ return NULL; + } ++ stmt->log.logflags = flags; ++ + return stmt; + } + +-static int json_parse_synproxy_flag(struct json_ctx *ctx, +- json_t *root, int *flags) ++static unsigned int json_parse_synproxy_flag(const char *flag) + { + const struct { + const char *flag; +@@ -2599,54 +2511,19 @@ static int json_parse_synproxy_flag(struct json_ctx *ctx, + { "timestamp", NF_SYNPROXY_OPT_TIMESTAMP }, + { "sack-perm", NF_SYNPROXY_OPT_SACK_PERM }, + }; +- const char *flag; + unsigned int i; + +- assert(flags); +- +- if (!json_is_string(root)) { +- json_error(ctx, "Invalid synproxy flag type %s, expected string.", +- json_typename(root)); +- return 1; +- } +- flag = json_string_value(root); + for (i = 0; i < array_size(flag_tbl); i++) { +- if (!strcmp(flag, flag_tbl[i].flag)) { +- *flags |= flag_tbl[i].val; +- return 0; +- } +- } +- json_error(ctx, "Unknown synproxy flag '%s'.", flag); +- return 1; +-} +- +-static int json_parse_synproxy_flags(struct json_ctx *ctx, json_t *root) +-{ +- int flags = 0; +- json_t *value; +- size_t index; +- +- if (json_is_string(root)) { +- json_parse_synproxy_flag(ctx, root, &flags); +- return flags; +- } else if (!json_is_array(root)) { +- json_error(ctx, "Invalid synproxy flags type %s.", +- json_typename(root)); +- return -1; +- } +- json_array_foreach(root, index, value) { +- if (json_parse_synproxy_flag(ctx, value, &flags)) +- json_error(ctx, "Parsing synproxy flag at index %zu failed.", +- index); ++ if (!strcmp(flag, flag_tbl[i].flag)) ++ return flag_tbl[i].val; + } +- return flags; ++ return 0; + } + + static struct stmt *json_parse_synproxy_stmt(struct json_ctx *ctx, + const char *key, json_t *value) + { + struct stmt *stmt = NULL; +- json_t *jflags; + int tmp, flags; + + if (json_typeof(value) == JSON_NULL) { +@@ -2676,15 +2553,16 @@ static struct stmt *json_parse_synproxy_stmt(struct json_ctx *ctx, + stmt->synproxy.wscale = tmp; + stmt->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE; + } +- if (!json_unpack(value, "{s:o}", "flags", &jflags)) { ++ ++ flags = parse_flags_array(ctx, value, "flags", ++ json_parse_synproxy_flag); ++ if (flags < 0) { ++ stmt_free(stmt); ++ return NULL; ++ } ++ if (flags) { + if (!stmt) + stmt = synproxy_stmt_alloc(int_loc); +- flags = json_parse_synproxy_flags(ctx, jflags); +- +- if (flags < 0) { +- stmt_free(stmt); +- return NULL; +- } + stmt->synproxy.flags |= flags; + } + +@@ -2779,14 +2657,12 @@ static struct stmt *json_parse_meter_stmt(struct json_ctx *ctx, + return stmt; + } + +-static int queue_flag_parse(const char *name, uint16_t *flags) ++static unsigned int queue_flag_parse(const char *name) + { + if (!strcmp(name, "bypass")) +- *flags |= NFT_QUEUE_FLAG_BYPASS; ++ return NFT_QUEUE_FLAG_BYPASS; + else if (!strcmp(name, "fanout")) +- *flags |= NFT_QUEUE_FLAG_CPU_FANOUT; +- else +- return 1; ++ return NFT_QUEUE_FLAG_CPU_FANOUT; + return 0; + } + +@@ -2794,8 +2670,8 @@ static struct stmt *json_parse_queue_stmt(struct json_ctx *ctx, + const char *key, json_t *value) + { + struct expr *qexpr = NULL; +- uint16_t flags = 0; + json_t *tmp; ++ int flags; + + if (!json_unpack(value, "{s:o}", "num", &tmp)) { + qexpr = json_parse_stmt_expr(ctx, tmp); +@@ -2804,43 +2680,13 @@ static struct stmt *json_parse_queue_stmt(struct json_ctx *ctx, + return NULL; + } + } +- if (!json_unpack(value, "{s:o}", "flags", &tmp)) { +- const char *flag; +- size_t index; +- json_t *val; +- +- if (json_is_string(tmp)) { +- flag = json_string_value(tmp); +- +- if (queue_flag_parse(flag, &flags)) { +- json_error(ctx, "Invalid queue flag '%s'.", +- flag); +- expr_free(qexpr); +- return NULL; +- } +- } else if (!json_is_array(tmp)) { +- json_error(ctx, "Unexpected object type in queue flags."); +- expr_free(qexpr); +- return NULL; +- } + +- json_array_foreach(tmp, index, val) { +- if (!json_is_string(val)) { +- json_error(ctx, "Invalid object in queue flag array at index %zu.", +- index); +- expr_free(qexpr); +- return NULL; +- } +- flag = json_string_value(val); +- +- if (queue_flag_parse(flag, &flags)) { +- json_error(ctx, "Invalid queue flag '%s'.", +- flag); +- expr_free(qexpr); +- return NULL; +- } +- } ++ flags = parse_flags_array(ctx, value, "flags", queue_flag_parse); ++ if (flags < 0) { ++ expr_free(qexpr); ++ return NULL; + } ++ + return queue_stmt_alloc(int_loc, qexpr, flags); + } + +@@ -2950,45 +2796,6 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root) + return NULL; + } + +-static int json_parse_table_flags(struct json_ctx *ctx, json_t *root, +- enum table_flags *flags) +-{ +- json_t *tmp, *tmp2; +- size_t index; +- int flag; +- +- if (json_unpack(root, "{s:o}", "flags", &tmp)) +- return 0; +- +- if (json_is_string(tmp)) { +- flag = parse_table_flag(json_string_value(tmp)); +- if (flag) { +- *flags = flag; +- return 0; +- } +- json_error(ctx, "Invalid table flag '%s'.", +- json_string_value(tmp)); +- return 1; +- } +- if (!json_is_array(tmp)) { +- json_error(ctx, "Unexpected table flags value."); +- return 1; +- } +- json_array_foreach(tmp, index, tmp2) { +- if (json_is_string(tmp2)) { +- flag = parse_table_flag(json_string_value(tmp2)); +- +- if (flag) { +- *flags |= flag; +- continue; +- } +- } +- json_error(ctx, "Invalid table flag at index %zu.", index); +- return 1; +- } +- return 0; +-} +- + static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root, + enum cmd_ops op, enum cmd_obj obj) + { +@@ -2997,7 +2804,7 @@ static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root, + .table.location = *int_loc, + }; + struct table *table = NULL; +- enum table_flags flags = 0; ++ int flags = 0; + + if (json_unpack_err(ctx, root, "{s:s}", + "family", &family)) +@@ -3008,9 +2815,10 @@ static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root, + return NULL; + + json_unpack(root, "{s:s}", "comment", &comment); +- if (json_parse_table_flags(ctx, root, &flags)) +- return NULL; + ++ flags = parse_flags_array(ctx, root, "flags", parse_table_flag); ++ if (flags < 0) ++ return NULL; + } else if (op == CMD_DELETE && + json_unpack(root, "{s:s}", "name", &h.table.name) && + json_unpack(root, "{s:I}", "handle", &h.handle.id)) { +@@ -3291,7 +3099,7 @@ static int string_to_nft_object(const char *str) + return 0; + } + +-static int string_to_set_flag(const char *str) ++static unsigned int string_to_set_flag(const char *str) + { + const struct { + enum nft_set_flags val; +@@ -3318,6 +3126,7 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root, + const char *family = "", *policy, *dtype_ext = NULL; + json_t *tmp, *stmt_json; + struct set *set; ++ int flags; + + if (json_unpack_err(ctx, root, "{s:s, s:s}", + "family", &family, +@@ -3398,23 +3207,16 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root, + return NULL; + } + } +- if (!json_unpack(root, "{s:o}", "flags", &tmp)) { +- json_t *value; +- size_t index; + +- json_array_foreach(tmp, index, value) { +- int flag; +- +- if (!json_is_string(value) || +- !(flag = string_to_set_flag(json_string_value(value)))) { +- json_error(ctx, "Invalid set flag at index %zu.", index); +- set_free(set); +- handle_free(&h); +- return NULL; +- } +- set->flags |= flag; +- } ++ flags = parse_flags_array(ctx, root, "flags", string_to_set_flag); ++ if (flags < 0) { ++ json_error(ctx, "Invalid set flags in set '%s'.", h.set.name); ++ set_free(set); ++ handle_free(&h); ++ return NULL; + } ++ set->flags |= flags; ++ + if (!json_unpack(root, "{s:o}", "elem", &tmp)) { + set->init = json_parse_set_expr(ctx, "elem", tmp); + if (!set->init) { +@@ -3585,7 +3387,6 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, + int inv = 0, flags = 0, i, j; + struct handle h = { 0 }; + struct obj *obj; +- json_t *jflags; + + if (json_unpack_err(ctx, root, "{s:s, s:s}", + "family", &family, +@@ -3765,13 +3566,12 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, + obj->synproxy.wscale = j; + obj->synproxy.flags |= NF_SYNPROXY_OPT_MSS; + obj->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE; +- if (!json_unpack(root, "{s:o}", "flags", &jflags)) { +- flags = json_parse_synproxy_flags(ctx, jflags); +- if (flags < 0) +- goto err_free_obj; ++ flags = parse_flags_array(ctx, root, "flags", ++ json_parse_synproxy_flag); ++ if (flags < 0) ++ goto err_free_obj; + +- obj->synproxy.flags |= flags; +- } ++ obj->synproxy.flags |= flags; + break; + default: + BUG("Invalid CMD '%d'", cmd_obj); diff --git a/0040-tests-py-fix-json-single-flag-output-for-fib-synprox.patch b/0040-tests-py-fix-json-single-flag-output-for-fib-synprox.patch new file mode 100644 index 0000000..c064346 --- /dev/null +++ b/0040-tests-py-fix-json-single-flag-output-for-fib-synprox.patch @@ -0,0 +1,92 @@ +From da4fc3c74125e4f5eb69ee4174713c7edb91f071 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:30:20 +0200 +Subject: [PATCH] tests: py: fix json single-flag output for fib & synproxy + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit c2ee1d5978bf6ea10e3b1a74125da5b5e8468f26 + +commit c2ee1d5978bf6ea10e3b1a74125da5b5e8468f26 +Author: Florian Westphal +Date: Mon Jun 2 14:12:16 2025 +0200 + + tests: py: fix json single-flag output for fib & synproxy + + Blamed commits change output format but did not adjust existing tests: + inet/fib.t: WARNING: line 16: '{"nftables": .. + + Fixes: 38f99ee84fe6 ("json: Print single synproxy flags as non-array") + Fixes: dbe5c44f2b89 ("json: Print single fib flag as non-array") + Signed-off-by: Florian Westphal + Acked-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + tests/py/inet/fib.t.json.output | 32 ++++++++++++++++++++++++++++ + tests/py/inet/synproxy.t.json.output | 17 +++++++++++++++ + 2 files changed, 49 insertions(+) + create mode 100644 tests/py/inet/synproxy.t.json.output + +diff --git a/tests/py/inet/fib.t.json.output b/tests/py/inet/fib.t.json.output +index 52cd46b..e21f1e7 100644 +--- a/tests/py/inet/fib.t.json.output ++++ b/tests/py/inet/fib.t.json.output +@@ -37,3 +37,35 @@ + } + ] + ++# fib daddr oif exists ++[ ++ { ++ "match": { ++ "left": { ++ "fib": { ++ "flags": "daddr", ++ "result": "oif" ++ } ++ }, ++ "op": "==", ++ "right": true ++ } ++ } ++] ++ ++# fib daddr oif missing ++[ ++ { ++ "match": { ++ "left": { ++ "fib": { ++ "flags": "daddr", ++ "result": "oif" ++ } ++ }, ++ "op": "==", ++ "right": false ++ } ++ } ++] ++ +diff --git a/tests/py/inet/synproxy.t.json.output b/tests/py/inet/synproxy.t.json.output +new file mode 100644 +index 0000000..e32cdfb +--- /dev/null ++++ b/tests/py/inet/synproxy.t.json.output +@@ -0,0 +1,17 @@ ++# synproxy timestamp ++[ ++ { ++ "synproxy": { ++ "flags": "timestamp" ++ } ++ } ++] ++ ++# synproxy sack-perm ++[ ++ { ++ "synproxy": { ++ "flags": "sack-perm" ++ } ++ } ++] diff --git a/0041-fib-allow-to-check-if-route-exists-in-maps.patch b/0041-fib-allow-to-check-if-route-exists-in-maps.patch new file mode 100644 index 0000000..b941148 --- /dev/null +++ b/0041-fib-allow-to-check-if-route-exists-in-maps.patch @@ -0,0 +1,434 @@ +From 7717bcbb0b5062f19645e459c69e7f79c6454f50 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:30:20 +0200 +Subject: [PATCH] fib: allow to check if route exists in maps + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit f4b646032acff4d743ad4f734aaca68e9264bdbb +Conflicts: + - Context change due to missing commit 316d992466442 + ("fib: Change data type of fib oifname to "ifname"") + - Drop changes to non-existent test/dump files + +commit f4b646032acff4d743ad4f734aaca68e9264bdbb +Author: Pablo Neira Ayuso +Date: Tue Jun 24 18:11:06 2025 +0200 + + fib: allow to check if route exists in maps + + f686a17eafa0 ("fib: Support existence check") adds EXPR_F_BOOLEAN as a + workaround to infer from the rhs of the relational expression if the fib + lookup wants to check for a specific output interface or, instead, + simply check for existence. This, however, does not work with maps. + + The NFT_FIB_F_PRESENT flag can be used both with NFT_FIB_RESULT_OIF and + NFT_FIB_RESULT_OFINAME, my understanding is that they serve the same + purpose which is to check if a route exists, so they are redundant. + + Add a 'check' fib result to check for routes while still keeping the + inference workaround for backward compatibility, but prefer the new + syntax in the listing. + + Update man nft(8) and tests/py. + + Fixes: f686a17eafa0 ("fib: Support existence check") + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + doc/data-types.txt | 2 +- + doc/primary-expression.txt | 5 ++- + include/fib.h | 2 +- + src/fib.c | 12 +++++-- + src/json.c | 2 +- + src/parser_bison.y | 21 +++++++---- + src/parser_json.c | 12 +++++-- + src/scanner.l | 4 +++ + tests/py/inet/fib.t | 6 ++-- + tests/py/inet/fib.t.json | 35 +++++++++++++++++-- + tests/py/inet/fib.t.json.output | 6 ++-- + tests/py/inet/fib.t.payload | 8 ++++- + .../parsing/dumps/large_rule_pipe.nft | 2 +- + tests/shell/testcases/parsing/large_rule_pipe | 2 +- + tests/shell/testcases/transactions/0049huge_0 | 2 +- + .../transactions/dumps/0049huge_0.nft | 2 +- + 16 files changed, 96 insertions(+), 27 deletions(-) + +diff --git a/doc/data-types.txt b/doc/data-types.txt +index 961fc62..280c538 100644 +--- a/doc/data-types.txt ++++ b/doc/data-types.txt +@@ -166,7 +166,7 @@ Check TCP option header existence. + .Boolean specification + ---------------------- + # match if route exists +-filter input fib daddr . iif oif exists ++filter input fib daddr . iif check exists + + # match only non-fragmented packets in IPv6 traffic + filter input exthdr frag missing +diff --git a/doc/primary-expression.txt b/doc/primary-expression.txt +index e85fa58..4a7062a 100644 +--- a/doc/primary-expression.txt ++++ b/doc/primary-expression.txt +@@ -306,7 +306,7 @@ FIB EXPRESSIONS + [verse] + *fib* 'FIB_TUPLE' 'FIB_RESULT' + 'FIB_TUPLE' := { *saddr* | *daddr*} [ *.* { *iif* | *oif* } *.* *mark* ] +-'FIB_RESULT' := { *oif* | *oifname* | *type* } ++'FIB_RESULT' := { *oif* | *oifname* | *check* | *type* } + + + A fib expression queries the fib (forwarding information base) to obtain information +@@ -349,6 +349,9 @@ address types can be shown with *nft* *describe* *fib_addrtype*. + |oif| + Output interface index| + integer (32 bit) ++|check| ++Output interface check| ++boolean + |oifname| + Output interface name| + string +diff --git a/include/fib.h b/include/fib.h +index 67edccf..07bb221 100644 +--- a/include/fib.h ++++ b/include/fib.h +@@ -3,7 +3,7 @@ + + #include + +-extern const char *fib_result_str(enum nft_fib_result result); ++extern const char *fib_result_str(const struct expr *expr); + extern struct expr *fib_expr_alloc(const struct location *loc, + unsigned int flags, + unsigned int result); +diff --git a/src/fib.c b/src/fib.c +index e95271c..aa75723 100644 +--- a/src/fib.c ++++ b/src/fib.c +@@ -53,8 +53,16 @@ const struct datatype fib_addr_type = { + .sym_tbl = &addrtype_tbl, + }; + +-const char *fib_result_str(enum nft_fib_result result) ++const char *fib_result_str(const struct expr *expr) + { ++ enum nft_fib_result result = expr->fib.result; ++ uint32_t flags = expr->fib.flags; ++ ++ /* Exception: check if route exists. */ ++ if (result == NFT_FIB_RESULT_OIF && ++ flags & NFTA_FIB_F_PRESENT) ++ return "check"; ++ + if (result <= NFT_FIB_RESULT_MAX) + return fib_result[result]; + +@@ -87,7 +95,7 @@ static void fib_expr_print(const struct expr *expr, struct output_ctx *octx) + if (flags) + nft_print(octx, "0x%x", flags); + +- nft_print(octx, " %s", fib_result_str(expr->fib.result)); ++ nft_print(octx, " %s", fib_result_str(expr)); + } + + static bool fib_expr_cmp(const struct expr *e1, const struct expr *e2) +diff --git a/src/json.c b/src/json.c +index 1e2fe49..a2523a4 100644 +--- a/src/json.c ++++ b/src/json.c +@@ -919,7 +919,7 @@ json_t *fib_expr_json(const struct expr *expr, struct output_ctx *octx) + unsigned int flags = expr->fib.flags & ~NFTA_FIB_F_PRESENT; + json_t *root; + +- root = nft_json_pack("{s:s}", "result", fib_result_str(expr->fib.result)); ++ root = nft_json_pack("{s:s}", "result", fib_result_str(expr)); + + if (flags) { + json_t *tmp = json_array(); +diff --git a/src/parser_bison.y b/src/parser_bison.y +index e252113..174a339 100644 +--- a/src/parser_bison.y ++++ b/src/parser_bison.y +@@ -265,6 +265,7 @@ int nft_lex(void *, void *, void *); + %token UNDEFINE "undefine" + + %token FIB "fib" ++%token CHECK "check" + + %token SOCKET "socket" + %token TRANSPARENT "transparent" +@@ -4338,30 +4339,38 @@ primary_expr : symbol_expr { $$ = $1; } + + fib_expr : FIB fib_tuple fib_result close_scope_fib + { +- if (($2 & (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) == 0) { ++ uint32_t flags = $2, result = $3; ++ ++ if (result == __NFT_FIB_RESULT_MAX) { ++ result = NFT_FIB_RESULT_OIF; ++ flags |= NFTA_FIB_F_PRESENT; ++ } ++ ++ if ((flags & (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) == 0) { + erec_queue(error(&@2, "fib: need either saddr or daddr"), state->msgs); + YYERROR; + } + +- if (($2 & (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) == +- (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) { ++ if ((flags & (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) == ++ (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) { + erec_queue(error(&@2, "fib: saddr and daddr are mutually exclusive"), state->msgs); + YYERROR; + } + +- if (($2 & (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) == +- (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) { ++ if ((flags & (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) == ++ (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) { + erec_queue(error(&@2, "fib: iif and oif are mutually exclusive"), state->msgs); + YYERROR; + } + +- $$ = fib_expr_alloc(&@$, $2, $3); ++ $$ = fib_expr_alloc(&@$, flags, result); + } + ; + + fib_result : OIF { $$ =NFT_FIB_RESULT_OIF; } + | OIFNAME { $$ =NFT_FIB_RESULT_OIFNAME; } + | TYPE close_scope_type { $$ =NFT_FIB_RESULT_ADDRTYPE; } ++ | CHECK { $$ = __NFT_FIB_RESULT_MAX; } /* actually, NFT_FIB_F_PRESENT. */ + ; + + fib_flag : SADDR { $$ = NFTA_FIB_F_SADDR; } +diff --git a/src/parser_json.c b/src/parser_json.c +index 0bf81d1..1b12445 100644 +--- a/src/parser_json.c ++++ b/src/parser_json.c +@@ -1140,6 +1140,7 @@ static struct expr *json_parse_fib_expr(struct json_ctx *ctx, + [NFT_FIB_RESULT_OIF] = "oif", + [NFT_FIB_RESULT_OIFNAME] = "oifname", + [NFT_FIB_RESULT_ADDRTYPE] = "type", ++ [__NFT_FIB_RESULT_MAX] = "check", /* Actually, NFT_FIB_F_PRESENT. */ + }; + enum nft_fib_result resultval = NFT_FIB_RESULT_UNSPEC; + const char *result; +@@ -1155,12 +1156,19 @@ static struct expr *json_parse_fib_expr(struct json_ctx *ctx, + break; + } + } +- if (resultval == NFT_FIB_RESULT_UNSPEC) { ++ switch (resultval) { ++ case NFT_FIB_RESULT_UNSPEC: + json_error(ctx, "Invalid fib result '%s'.", result); + return NULL; ++ case __NFT_FIB_RESULT_MAX: ++ resultval = NFT_FIB_RESULT_OIF; ++ flagval = NFTA_FIB_F_PRESENT; ++ break; ++ default: ++ break; + } + +- flagval = parse_flags_array(ctx, root, "flags", fib_flag_parse); ++ flagval |= parse_flags_array(ctx, root, "flags", fib_flag_parse); + if (flagval < 0) + return NULL; + +diff --git a/src/scanner.l b/src/scanner.l +index 88376b7..1778e82 100644 +--- a/src/scanner.l ++++ b/src/scanner.l +@@ -790,6 +790,10 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr}) + + "fib" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_FIB); return FIB; } + ++{ ++ "check" { return CHECK; } ++} ++ + "osf" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_OSF); return OSF; } + + "synproxy" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_SYNPROXY); return SYNPROXY; } +diff --git a/tests/py/inet/fib.t b/tests/py/inet/fib.t +index dbe45d9..f9c03b3 100644 +--- a/tests/py/inet/fib.t ++++ b/tests/py/inet/fib.t +@@ -13,5 +13,7 @@ fib daddr . iif type local;ok + fib daddr . iif type vmap { blackhole : drop, prohibit : drop, unicast : accept };ok + fib daddr . oif type local;fail + +-fib daddr oif exists;ok +-fib daddr oif missing;ok ++fib daddr check missing;ok ++fib daddr oif exists;ok;fib daddr check exists ++ ++fib daddr check vmap { missing : drop, exists : accept };ok +diff --git a/tests/py/inet/fib.t.json b/tests/py/inet/fib.t.json +index c298915..c2e9d45 100644 +--- a/tests/py/inet/fib.t.json ++++ b/tests/py/inet/fib.t.json +@@ -103,7 +103,7 @@ + "flags": [ + "daddr" + ], +- "result": "oif" ++ "result": "check" + } + }, + "op": "==", +@@ -112,7 +112,7 @@ + } + ] + +-# fib daddr oif missing ++# fib daddr check missing + [ + { + "match": { +@@ -121,7 +121,7 @@ + "flags": [ + "daddr" + ], +- "result": "oif" ++ "result": "check" + } + }, + "op": "==", +@@ -130,3 +130,32 @@ + } + ] + ++# fib daddr check vmap { missing : drop, exists : accept } ++[ ++ { ++ "vmap": { ++ "data": { ++ "set": [ ++ [ ++ false, ++ { ++ "drop": null ++ } ++ ], ++ [ ++ true, ++ { ++ "accept": null ++ } ++ ] ++ ] ++ }, ++ "key": { ++ "fib": { ++ "flags": "daddr", ++ "result": "check" ++ } ++ } ++ } ++ } ++] +diff --git a/tests/py/inet/fib.t.json.output b/tests/py/inet/fib.t.json.output +index e21f1e7..e8d0166 100644 +--- a/tests/py/inet/fib.t.json.output ++++ b/tests/py/inet/fib.t.json.output +@@ -44,7 +44,7 @@ + "left": { + "fib": { + "flags": "daddr", +- "result": "oif" ++ "result": "check" + } + }, + "op": "==", +@@ -53,14 +53,14 @@ + } + ] + +-# fib daddr oif missing ++# fib daddr check missing + [ + { + "match": { + "left": { + "fib": { + "flags": "daddr", +- "result": "oif" ++ "result": "check" + } + }, + "op": "==", +diff --git a/tests/py/inet/fib.t.payload b/tests/py/inet/fib.t.payload +index 050857d..e09a260 100644 +--- a/tests/py/inet/fib.t.payload ++++ b/tests/py/inet/fib.t.payload +@@ -26,7 +26,13 @@ ip test-ip prerouting + [ fib daddr oif present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + +-# fib daddr oif missing ++# fib daddr check missing + ip test-ip prerouting + [ fib daddr oif present => reg 1 ] + [ cmp eq reg 1 0x00000000 ] ++ ++# fib daddr check vmap { missing : drop, exists : accept } ++ element 00000000 : drop 0 [end] element 00000001 : accept 0 [end] ++ip test-ip prerouting ++ [ fib daddr oif present => reg 1 ] ++ [ lookup reg 1 set __map%d dreg 0 ] +diff --git a/tests/shell/testcases/parsing/dumps/large_rule_pipe.nft b/tests/shell/testcases/parsing/dumps/large_rule_pipe.nft +index 1583275..c85a636 100644 +--- a/tests/shell/testcases/parsing/dumps/large_rule_pipe.nft ++++ b/tests/shell/testcases/parsing/dumps/large_rule_pipe.nft +@@ -240,7 +240,7 @@ table inet firewalld { + chain raw_PREROUTING { + type filter hook prerouting priority raw + 10; policy accept; + icmpv6 type { nd-router-advert, nd-neighbor-solicit } accept +- meta nfproto ipv6 fib saddr . iif oif missing drop ++ meta nfproto ipv6 fib saddr . iif check missing drop + jump raw_PREROUTING_ZONES_SOURCE + jump raw_PREROUTING_ZONES + } +diff --git a/tests/shell/testcases/parsing/large_rule_pipe b/tests/shell/testcases/parsing/large_rule_pipe +index fac0afa..e958d75 100755 +--- a/tests/shell/testcases/parsing/large_rule_pipe ++++ b/tests/shell/testcases/parsing/large_rule_pipe +@@ -246,7 +246,7 @@ table inet firewalld { + chain raw_PREROUTING { + type filter hook prerouting priority -290; policy accept; + icmpv6 type { nd-router-advert, nd-neighbor-solicit } accept +- meta nfproto ipv6 fib saddr . iif oif missing drop ++ meta nfproto ipv6 fib saddr . iif check missing drop + jump raw_PREROUTING_ZONES_SOURCE + jump raw_PREROUTING_ZONES + } +diff --git a/tests/shell/testcases/transactions/0049huge_0 b/tests/shell/testcases/transactions/0049huge_0 +index f66953c..90049b6 100755 +--- a/tests/shell/testcases/transactions/0049huge_0 ++++ b/tests/shell/testcases/transactions/0049huge_0 +@@ -47,7 +47,7 @@ fi + $NFT flush ruleset + + RULESET='{"nftables": [{"metainfo": {"json_schema_version": 1}}, {"add": {"table": {"family": "inet", "name": "firewalld"}}}, {"add": {"table": {"family": "ip", "name": "firewalld"}}}, {"add": {"table": {"family": "ip6", "name": "firewalld"}}}, +-{"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PREROUTING", "type": "filter", "hook": "prerouting", "prio": -290}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PREROUTING_ZONES"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING", "expr": [{"jump": {"target": "raw_PREROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PREROUTING", "type": "filter", "hook": "prerouting", "prio": -140}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PREROUTING_ZONES"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PREROUTING", "expr": [{"jump": {"target": "mangle_PREROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PREROUTING", "type": "nat", "hook": "prerouting", "prio": -90}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PREROUTING_ZONES"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PREROUTING", "expr": [{"jump": {"target": "nat_PREROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POSTROUTING", "type": "nat", "hook": "postrouting", "prio": 110}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POSTROUTING_ZONES"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POSTROUTING", "expr": [{"jump": {"target": "nat_POSTROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PREROUTING", "type": "nat", "hook": "prerouting", "prio": -90}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PREROUTING_ZONES"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PREROUTING", "expr": [{"jump": {"target": "nat_PREROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POSTROUTING", "type": "nat", "hook": "postrouting", "prio": 110}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POSTROUTING_ZONES"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POSTROUTING", "expr": [{"jump": {"target": "nat_POSTROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_INPUT", "type": "filter", "hook": "input", "prio": 10}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FORWARD", "type": "filter", "hook": "forward", "prio": 10}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_OUTPUT", "type": "filter", "hook": "output", "prio": 10}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_INPUT_ZONES"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["established", "related"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"match": {"left": {"ct": {"key": "status"}}, "op": "in", "right": "dnat"}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "lo"}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"jump": {"target": "filter_INPUT_ZONES"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["invalid"]}}}, {"drop": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"reject": {"type": "icmpx", "expr": "admin-prohibited"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FORWARD_IN_ZONES"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FORWARD_OUT_ZONES"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["established", "related"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"match": {"left": {"ct": {"key": "status"}}, "op": "in", "right": "dnat"}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "lo"}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"jump": {"target": "filter_FORWARD_IN_ZONES"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"jump": {"target": "filter_FORWARD_OUT_ZONES"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["invalid"]}}}, {"drop": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"reject": {"type": "icmpx", "expr": "admin-prohibited"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_OUTPUT", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "lo"}}, {"accept": null}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING", "expr": [{"match": {"left": {"meta": {"key": "nfproto"}}, "op": "==", "right": "ipv6"}}, {"match": {"left": {"fib": {"flags": ["saddr", "iif"], "result": "oif"}}, "op": "==", "right": false}}, {"drop": null}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING", "expr": [{"match": {"left": {"payload": {"protocol": "icmpv6", "field": "type"}}, "op": "==", "right": {"set": ["nd-router-advert", "nd-neighbor-solicit"]}}}, {"accept": null}]}}}, ++{"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PREROUTING", "type": "filter", "hook": "prerouting", "prio": -290}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PREROUTING_ZONES"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING", "expr": [{"jump": {"target": "raw_PREROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PREROUTING", "type": "filter", "hook": "prerouting", "prio": -140}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PREROUTING_ZONES"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PREROUTING", "expr": [{"jump": {"target": "mangle_PREROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PREROUTING", "type": "nat", "hook": "prerouting", "prio": -90}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PREROUTING_ZONES"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PREROUTING", "expr": [{"jump": {"target": "nat_PREROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POSTROUTING", "type": "nat", "hook": "postrouting", "prio": 110}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POSTROUTING_ZONES"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POSTROUTING", "expr": [{"jump": {"target": "nat_POSTROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PREROUTING", "type": "nat", "hook": "prerouting", "prio": -90}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PREROUTING_ZONES"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PREROUTING", "expr": [{"jump": {"target": "nat_PREROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POSTROUTING", "type": "nat", "hook": "postrouting", "prio": 110}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POSTROUTING_ZONES"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POSTROUTING", "expr": [{"jump": {"target": "nat_POSTROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_INPUT", "type": "filter", "hook": "input", "prio": 10}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FORWARD", "type": "filter", "hook": "forward", "prio": 10}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_OUTPUT", "type": "filter", "hook": "output", "prio": 10}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_INPUT_ZONES"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["established", "related"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"match": {"left": {"ct": {"key": "status"}}, "op": "in", "right": "dnat"}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "lo"}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"jump": {"target": "filter_INPUT_ZONES"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["invalid"]}}}, {"drop": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"reject": {"type": "icmpx", "expr": "admin-prohibited"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FORWARD_IN_ZONES"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FORWARD_OUT_ZONES"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["established", "related"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"match": {"left": {"ct": {"key": "status"}}, "op": "in", "right": "dnat"}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "lo"}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"jump": {"target": "filter_FORWARD_IN_ZONES"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"jump": {"target": "filter_FORWARD_OUT_ZONES"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["invalid"]}}}, {"drop": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"reject": {"type": "icmpx", "expr": "admin-prohibited"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_OUTPUT", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "lo"}}, {"accept": null}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING", "expr": [{"match": {"left": {"meta": {"key": "nfproto"}}, "op": "==", "right": "ipv6"}}, {"match": {"left": {"fib": {"flags": ["saddr", "iif"], "result": "check"}}, "op": "==", "right": false}}, {"drop": null}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING", "expr": [{"match": {"left": {"payload": {"protocol": "icmpv6", "field": "type"}}, "op": "==", "right": {"set": ["nd-router-advert", "nd-neighbor-solicit"]}}}, {"accept": null}]}}}, + {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_OUTPUT", "index": 0, "expr": [{"match": {"left": {"payload": {"protocol": "ip6", "field": "daddr"}}, "op": "==", "right": {"set": [{"prefix": {"addr": "::0.0.0.0", "len": 96}}, {"prefix": {"addr": "::ffff:0.0.0.0", "len": 96}}, {"prefix": {"addr": "2002:0000::", "len": 24}}, {"prefix": {"addr": "2002:0a00::", "len": 24}}, {"prefix": {"addr": "2002:7f00::", "len": 24}}, {"prefix": {"addr": "2002:ac10::", "len": 28}}, {"prefix": {"addr": "2002:c0a8::", "len": 32}}, {"prefix": {"addr": "2002:a9fe::", "len": 32}}, {"prefix": {"addr": "2002:e000::", "len": 19}}]}}}, {"reject": {"type": "icmpv6", "expr": "addr-unreachable"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "index": 2, "expr": [{"match": {"left": {"payload": {"protocol": "ip6", "field": "daddr"}}, "op": "==", "right": {"set": [{"prefix": {"addr": "::0.0.0.0", "len": 96}}, {"prefix": {"addr": "::ffff:0.0.0.0", "len": 96}}, {"prefix": {"addr": "2002:0000::", "len": 24}}, {"prefix": {"addr": "2002:0a00::", "len": 24}}, {"prefix": {"addr": "2002:7f00::", "len": 24}}, {"prefix": {"addr": "2002:ac10::", "len": 28}}, {"prefix": {"addr": "2002:c0a8::", "len": 32}}, {"prefix": {"addr": "2002:a9fe::", "len": 32}}, {"prefix": {"addr": "2002:e000::", "len": 19}}]}}}, {"reject": {"type": "icmpv6", "expr": "addr-unreachable"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_public"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_public_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_public_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_public_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_public_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_public_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_public", "expr": [{"jump": {"target": "raw_PRE_public_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_public", "expr": [{"jump": {"target": "raw_PRE_public_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_public", "expr": [{"jump": {"target": "raw_PRE_public_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_public", "expr": [{"jump": {"target": "raw_PRE_public_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_public", "expr": [{"jump": {"target": "raw_PRE_public_post"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_public"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_public_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_public_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_public_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_public_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_public_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public", "expr": [{"jump": {"target": "filter_IN_public_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public", "expr": [{"jump": {"target": "filter_IN_public_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public", "expr": [{"jump": {"target": "filter_IN_public_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public", "expr": [{"jump": {"target": "filter_IN_public_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public", "expr": [{"jump": {"target": "filter_IN_public_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public_allow", "expr": [{"match": {"left": {"payload": {"protocol": "tcp", "field": "dport"}}, "op": "==", "right": 22}}, {"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["new", "untracked"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public_allow", "expr": [{"match": {"left": {"payload": {"protocol": "ip6", "field": "daddr"}}, "op": "==", "right": {"prefix": {"addr": "fe80::", "len": 64}}}}, {"match": {"left": {"payload": {"protocol": "udp", "field": "dport"}}, "op": "==", "right": 546}}, {"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["new", "untracked"]}}}, {"accept": null}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_public"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_public_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_public_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_public_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_public_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_public_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_public", "expr": [{"jump": {"target": "filter_FWDI_public_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_public", "expr": [{"jump": {"target": "filter_FWDI_public_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_public", "expr": [{"jump": {"target": "filter_FWDI_public_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_public", "expr": [{"jump": {"target": "filter_FWDI_public_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_public", "expr": [{"jump": {"target": "filter_FWDI_public_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public", "index": 4, "expr": [{"match": {"left": {"meta": {"key": "l4proto"}}, "op": "==", "right": {"set": ["icmp", "icmpv6"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_public", "index": 4, "expr": [{"match": {"left": {"meta": {"key": "l4proto"}}, "op": "==", "right": {"set": ["icmp", "icmpv6"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING_ZONES", "expr": [{"goto": {"target": "raw_PRE_public"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_public"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_public_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_public_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_public_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_public_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_public_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_public", "expr": [{"jump": {"target": "mangle_PRE_public_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_public", "expr": [{"jump": {"target": "mangle_PRE_public_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_public", "expr": [{"jump": {"target": "mangle_PRE_public_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_public", "expr": [{"jump": {"target": "mangle_PRE_public_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_public", "expr": [{"jump": {"target": "mangle_PRE_public_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PREROUTING_ZONES", "expr": [{"goto": {"target": "mangle_PRE_public"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_public"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_public_pre"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_public_log"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_public_deny"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_public_allow"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_public_post"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_pre"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_log"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_deny"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_allow"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_post"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_public"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_public_pre"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_public_log"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_public_deny"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_public_allow"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_public_post"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_pre"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_log"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_deny"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_allow"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_post"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PREROUTING_ZONES", "expr": [{"goto": {"target": "nat_PRE_public"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PREROUTING_ZONES", "expr": [{"goto": {"target": "nat_PRE_public"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_public"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_public_pre"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_public_log"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_public_deny"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_public_allow"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_public_post"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_pre"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_log"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_deny"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_allow"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_post"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_public"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_public_pre"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_public_log"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_public_deny"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_public_allow"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_public_post"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_pre"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_log"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_deny"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_allow"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_post"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POSTROUTING_ZONES", "expr": [{"goto": {"target": "nat_POST_public"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POSTROUTING_ZONES", "expr": [{"goto": {"target": "nat_POST_public"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT_ZONES", "expr": [{"goto": {"target": "filter_IN_public"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD_IN_ZONES", "expr": [{"goto": {"target": "filter_FWDI_public"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_public"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_public_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_public_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_public_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_public_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_public_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_public", "expr": [{"jump": {"target": "filter_FWDO_public_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_public", "expr": [{"jump": {"target": "filter_FWDO_public_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_public", "expr": [{"jump": {"target": "filter_FWDO_public_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_public", "expr": [{"jump": {"target": "filter_FWDO_public_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_public", "expr": [{"jump": {"target": "filter_FWDO_public_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD_OUT_ZONES", "expr": [{"goto": {"target": "filter_FWDO_public"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_trusted"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_trusted_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_trusted_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_trusted_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_trusted_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_trusted_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_trusted", "expr": [{"jump": {"target": "raw_PRE_trusted_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_trusted", "expr": [{"jump": {"target": "raw_PRE_trusted_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_trusted", "expr": [{"jump": {"target": "raw_PRE_trusted_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_trusted", "expr": [{"jump": {"target": "raw_PRE_trusted_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_trusted", "expr": [{"jump": {"target": "raw_PRE_trusted_post"}}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "raw_PRE_trusted"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_trusted"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_trusted_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_trusted_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_trusted_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_trusted_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_trusted_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_trusted", "expr": [{"jump": {"target": "mangle_PRE_trusted_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_trusted", "expr": [{"jump": {"target": "mangle_PRE_trusted_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_trusted", "expr": [{"jump": {"target": "mangle_PRE_trusted_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_trusted", "expr": [{"jump": {"target": "mangle_PRE_trusted_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_trusted", "expr": [{"jump": {"target": "mangle_PRE_trusted_post"}}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "mangle_PRE_trusted"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_trusted"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_trusted_pre"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_trusted_log"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_trusted_deny"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_trusted_allow"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_trusted_post"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_pre"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_log"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_deny"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_allow"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_post"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_trusted"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_trusted_pre"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_trusted_log"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_trusted_deny"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_trusted_allow"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_trusted_post"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_pre"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_log"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_deny"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_allow"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_post"}}]}}}, {"insert": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "nat_PRE_trusted"}}]}}}, {"insert": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "nat_PRE_trusted"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_trusted"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_trusted_pre"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_trusted_log"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_trusted_deny"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_trusted_allow"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_trusted_post"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_pre"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_log"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_deny"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_allow"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_post"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_trusted"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_trusted_pre"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_trusted_log"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_trusted_deny"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_trusted_allow"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_trusted_post"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_pre"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_log"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_deny"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_allow"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_post"}}]}}}, {"insert": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POSTROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "nat_POST_trusted"}}]}}}, {"insert": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POSTROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "nat_POST_trusted"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_trusted"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_trusted_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_trusted_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_trusted_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_trusted_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_trusted_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_trusted", "expr": [{"jump": {"target": "filter_IN_trusted_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_trusted", "expr": [{"jump": {"target": "filter_IN_trusted_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_trusted", "expr": [{"jump": {"target": "filter_IN_trusted_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_trusted", "expr": [{"jump": {"target": "filter_IN_trusted_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_trusted", "expr": [{"jump": {"target": "filter_IN_trusted_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_trusted", "expr": [{"accept": null}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "filter_IN_trusted"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_trusted"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_trusted_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_trusted_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_trusted_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_trusted_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_trusted_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_trusted", "expr": [{"jump": {"target": "filter_FWDI_trusted_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_trusted", "expr": [{"jump": {"target": "filter_FWDI_trusted_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_trusted", "expr": [{"jump": {"target": "filter_FWDI_trusted_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_trusted", "expr": [{"jump": {"target": "filter_FWDI_trusted_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_trusted", "expr": [{"jump": {"target": "filter_FWDI_trusted_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_trusted", "expr": [{"accept": null}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD_IN_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "filter_FWDI_trusted"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_trusted"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_trusted_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_trusted_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_trusted_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_trusted_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_trusted_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_trusted", "expr": [{"jump": {"target": "filter_FWDO_trusted_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_trusted", "expr": [{"jump": {"target": "filter_FWDO_trusted_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_trusted", "expr": [{"jump": {"target": "filter_FWDO_trusted_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_trusted", "expr": [{"jump": {"target": "filter_FWDO_trusted_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_trusted", "expr": [{"jump": {"target": "filter_FWDO_trusted_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_trusted", "expr": [{"accept": null}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD_OUT_ZONES", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "filter_FWDO_trusted"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_work"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_work_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_work_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_work_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_work_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_work_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_work", "expr": [{"jump": {"target": "raw_PRE_work_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_work", "expr": [{"jump": {"target": "raw_PRE_work_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_work", "expr": [{"jump": {"target": "raw_PRE_work_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_work", "expr": [{"jump": {"target": "raw_PRE_work_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_work", "expr": [{"jump": {"target": "raw_PRE_work_post"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_work"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_work_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_work_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_work_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_work_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_work_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work", "expr": [{"jump": {"target": "filter_IN_work_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work", "expr": [{"jump": {"target": "filter_IN_work_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work", "expr": [{"jump": {"target": "filter_IN_work_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work", "expr": [{"jump": {"target": "filter_IN_work_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work", "expr": [{"jump": {"target": "filter_IN_work_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work_allow", "expr": [{"match": {"left": {"payload": {"protocol": "tcp", "field": "dport"}}, "op": "==", "right": 22}}, {"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["new", "untracked"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work_allow", "expr": [{"match": {"left": {"payload": {"protocol": "ip6", "field": "daddr"}}, "op": "==", "right": {"prefix": {"addr": "fe80::", "len": 64}}}}, {"match": {"left": {"payload": {"protocol": "udp", "field": "dport"}}, "op": "==", "right": 546}}, {"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["new", "untracked"]}}}, {"accept": null}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "raw_PRE_work"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_work"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_work_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_work_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_work_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_work_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_work_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_work", "expr": [{"jump": {"target": "mangle_PRE_work_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_work", "expr": [{"jump": {"target": "mangle_PRE_work_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_work", "expr": [{"jump": {"target": "mangle_PRE_work_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_work", "expr": [{"jump": {"target": "mangle_PRE_work_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_work", "expr": [{"jump": {"target": "mangle_PRE_work_post"}}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "mangle_PRE_work"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_work"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_work_pre"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_work_log"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_work_deny"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_work_allow"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_work_post"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_pre"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_log"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_deny"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_allow"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_post"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_work"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_work_pre"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_work_log"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_work_deny"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_work_allow"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_work_post"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_pre"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_log"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_deny"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_allow"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_post"}}]}}}, {"insert": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "nat_PRE_work"}}]}}}, {"insert": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "nat_PRE_work"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_work"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_work_pre"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_work_log"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_work_deny"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_work_allow"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_work_post"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_pre"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_log"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_deny"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_allow"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_post"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_work"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_work_pre"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_work_log"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_work_deny"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_work_allow"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_work_post"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_pre"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_log"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_deny"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_allow"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_post"}}]}}}, {"insert": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POSTROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "nat_POST_work"}}]}}}, {"insert": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POSTROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "nat_POST_work"}}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "filter_IN_work"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_work"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_work_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_work_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_work_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_work_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_work_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_work", "expr": [{"jump": {"target": "filter_FWDI_work_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_work", "expr": [{"jump": {"target": "filter_FWDI_work_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_work", "expr": [{"jump": {"target": "filter_FWDI_work_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_work", "expr": [{"jump": {"target": "filter_FWDI_work_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_work", "expr": [{"jump": {"target": "filter_FWDI_work_post"}}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD_IN_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "filter_FWDI_work"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_work"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_work_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_work_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_work_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_work_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_work_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_work", "expr": [{"jump": {"target": "filter_FWDO_work_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_work", "expr": [{"jump": {"target": "filter_FWDO_work_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_work", "expr": [{"jump": {"target": "filter_FWDO_work_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_work", "expr": [{"jump": {"target": "filter_FWDO_work_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_work", "expr": [{"jump": {"target": "filter_FWDO_work_post"}}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD_OUT_ZONES", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "filter_FWDO_work"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work", "index": 4, "expr": [{"match": {"left": {"meta": {"key": "l4proto"}}, "op": "==", "right": {"set": ["icmp", "icmpv6"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_work", "index": 4, "expr": [{"match": {"left": {"meta": {"key": "l4proto"}}, "op": "==", "right": {"set": ["icmp", "icmpv6"]}}}, {"accept": null}]}}}]}' + + if [ "$NFT_TEST_HAVE_json" != n ]; then +diff --git a/tests/shell/testcases/transactions/dumps/0049huge_0.nft b/tests/shell/testcases/transactions/dumps/0049huge_0.nft +index 96f5a38..e42ad3e 100644 +--- a/tests/shell/testcases/transactions/dumps/0049huge_0.nft ++++ b/tests/shell/testcases/transactions/dumps/0049huge_0.nft +@@ -2,7 +2,7 @@ table inet firewalld { + chain raw_PREROUTING { + type filter hook prerouting priority raw + 10; policy accept; + icmpv6 type { nd-router-advert, nd-neighbor-solicit } accept +- meta nfproto ipv6 fib saddr . iif oif missing drop ++ meta nfproto ipv6 fib saddr . iif check missing drop + jump raw_PREROUTING_ZONES + } + diff --git a/0042-fib-allow-to-use-it-in-set-statements.patch b/0042-fib-allow-to-use-it-in-set-statements.patch new file mode 100644 index 0000000..25fcb6d --- /dev/null +++ b/0042-fib-allow-to-use-it-in-set-statements.patch @@ -0,0 +1,134 @@ +From 3d8fe285a67b3e4dc38a48622834151f0449e314 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:00 +0200 +Subject: [PATCH] fib: allow to use it in set statements + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 525b58568dca5ab9998595fc45313eac2764b6b1 + +commit 525b58568dca5ab9998595fc45313eac2764b6b1 +Author: Pablo Neira Ayuso +Date: Tue Jun 24 18:11:10 2025 +0200 + + fib: allow to use it in set statements + + Allow to use fib expression in set statements, eg. + + meta mark set ip saddr . fib daddr check map { 1.2.3.4 . exists : 0x00000001 } + + Fixes: 4a75ed32132d ("src: add fib expression") + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/parser_bison.y | 1 + + src/parser_json.c | 2 +- + tests/py/inet/fib.t | 2 ++ + tests/py/inet/fib.t.json | 45 +++++++++++++++++++++++++++++++++++++ + tests/py/inet/fib.t.payload | 8 +++++++ + 5 files changed, 57 insertions(+), 1 deletion(-) + +diff --git a/src/parser_bison.y b/src/parser_bison.y +index 174a339..a6a517f 100644 +--- a/src/parser_bison.y ++++ b/src/parser_bison.y +@@ -3825,6 +3825,7 @@ primary_stmt_expr : symbol_expr { $$ = $1; } + | payload_expr { $$ = $1; } + | keyword_expr { $$ = $1; } + | socket_expr { $$ = $1; } ++ | fib_expr { $$ = $1; } + | osf_expr { $$ = $1; } + | '(' basic_stmt_expr ')' { $$ = $2; } + ; +diff --git a/src/parser_json.c b/src/parser_json.c +index 1b12445..8c9d365 100644 +--- a/src/parser_json.c ++++ b/src/parser_json.c +@@ -1569,7 +1569,7 @@ static struct expr *json_parse_expr(struct json_ctx *ctx, json_t *root) + /* below two are hash expr */ + { "jhash", json_parse_hash_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, + { "symhash", json_parse_hash_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, +- { "fib", json_parse_fib_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, ++ { "fib", json_parse_fib_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, + { "|", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, + { "^", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, + { "&", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, +diff --git a/tests/py/inet/fib.t b/tests/py/inet/fib.t +index f9c03b3..60b77a4 100644 +--- a/tests/py/inet/fib.t ++++ b/tests/py/inet/fib.t +@@ -17,3 +17,5 @@ fib daddr check missing;ok + fib daddr oif exists;ok;fib daddr check exists + + fib daddr check vmap { missing : drop, exists : accept };ok ++ ++meta mark set fib daddr check . ct mark map { exists . 0x00000000 : 0x00000001 };ok +diff --git a/tests/py/inet/fib.t.json b/tests/py/inet/fib.t.json +index c2e9d45..14a6249 100644 +--- a/tests/py/inet/fib.t.json ++++ b/tests/py/inet/fib.t.json +@@ -159,3 +159,48 @@ + } + } + ] ++ ++# meta mark set fib daddr check . ct mark map { exists . 0x00000000 : 0x00000001 } ++[ ++ { ++ "mangle": { ++ "key": { ++ "meta": { ++ "key": "mark" ++ } ++ }, ++ "value": { ++ "map": { ++ "data": { ++ "set": [ ++ [ ++ { ++ "concat": [ ++ true, ++ 0 ++ ] ++ }, ++ 1 ++ ] ++ ] ++ }, ++ "key": { ++ "concat": [ ++ { ++ "fib": { ++ "flags": "daddr", ++ "result": "check" ++ } ++ }, ++ { ++ "ct": { ++ "key": "mark" ++ } ++ } ++ ] ++ } ++ } ++ } ++ } ++ } ++] +diff --git a/tests/py/inet/fib.t.payload b/tests/py/inet/fib.t.payload +index e09a260..02d92b5 100644 +--- a/tests/py/inet/fib.t.payload ++++ b/tests/py/inet/fib.t.payload +@@ -36,3 +36,11 @@ ip test-ip prerouting + ip test-ip prerouting + [ fib daddr oif present => reg 1 ] + [ lookup reg 1 set __map%d dreg 0 ] ++ ++# meta mark set fib daddr check . ct mark map { exists . 0x00000000 : 0x00000001 } ++ element 00000001 00000000 : 00000001 0 [end] ++ip test-ip prerouting ++ [ fib daddr oif present => reg 1 ] ++ [ ct load mark => reg 9 ] ++ [ lookup reg 1 set __map%d dreg 1 ] ++ [ meta set mark with reg 1 ] diff --git a/0043-tests-py-re-enables-nft-test.py-to-load-the-local-nf.patch b/0043-tests-py-re-enables-nft-test.py-to-load-the-local-nf.patch new file mode 100644 index 0000000..4995f66 --- /dev/null +++ b/0043-tests-py-re-enables-nft-test.py-to-load-the-local-nf.patch @@ -0,0 +1,45 @@ +From 6bcaa14dfd285275ae797e963459088e6d5365eb Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:00 +0200 +Subject: [PATCH] tests: py: re-enables nft-test.py to load the local + nftables.py + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 0f3965a38aecaf1fda66c2ce5dcf3b755d8191c6 + +commit 0f3965a38aecaf1fda66c2ce5dcf3b755d8191c6 +Author: Zhongqiu Duan +Date: Fri Jul 4 03:12:16 2025 +0000 + + tests: py: re-enables nft-test.py to load the local nftables.py + + This is a needed follow-up of commit ce443afc21455 ("py: move + package source into src directory") from 2023. Since that change, + nft-test.py started using the host's nftables.py instead of the local + one. But since nft-test.py passes the local src/.libs/libnftables.so.1 + as parameter when instantiating the Nftables class, we did nevertheless + use the local libnftables. + + Fixes: ce443afc21455 ("py: move package source into src directory") + Reviewed-by: Phil Sutter + Signed-off-by: Zhongqiu Duan + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + tests/py/nft-test.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/py/nft-test.py b/tests/py/nft-test.py +index 9a25503..35bf66e 100755 +--- a/tests/py/nft-test.py ++++ b/tests/py/nft-test.py +@@ -23,7 +23,7 @@ import traceback + import tempfile + + TESTS_PATH = os.path.dirname(os.path.abspath(__file__)) +-sys.path.insert(0, os.path.join(TESTS_PATH, '../../py/')) ++sys.path.insert(0, os.path.join(TESTS_PATH, '../../py/src')) + os.environ['TZ'] = 'UTC-2' + + from nftables import Nftables diff --git a/0044-tests-shell-cover-long-interface-name-in-0042chain_v.patch b/0044-tests-shell-cover-long-interface-name-in-0042chain_v.patch new file mode 100644 index 0000000..e808543 --- /dev/null +++ b/0044-tests-shell-cover-long-interface-name-in-0042chain_v.patch @@ -0,0 +1,126 @@ +From d376d61e94ec31d1e6b8b452f12eb895eb0fdbd3 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:00 +0200 +Subject: [PATCH] tests/shell: cover long interface name in + "0042chain_variable_0" test + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit f773041121d6d0d112fa9cb003fd791eacd6e43d + +commit f773041121d6d0d112fa9cb003fd791eacd6e43d +Author: Thomas Haller +Date: Mon Oct 23 19:00:46 2023 +0200 + + tests/shell: cover long interface name in "0042chain_variable_0" test + + IFNAMSIZ is 16. Adjust "0042chain_variable_0" to use an interface name + with the maximum allowed bytes length. + + Instead of adding an entirely different test, adjust an existing one to + use another interface name. The aspect for testing for a long interface + name is not special enough, to warrant a separate test. We can cover it + by extending an existing test. + + Note that the length check in "parser_bison.y" is wrong. The test checks + still for the wrong behavior and that "d23456789012345x" is accepted. + + Signed-off-by: Thomas Haller + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + .../testcases/chains/0042chain_variable_0 | 36 ++++++++++++++++--- + .../chains/dumps/0042chain_variable_0.nft | 4 +-- + 2 files changed, 34 insertions(+), 6 deletions(-) + +diff --git a/tests/shell/testcases/chains/0042chain_variable_0 b/tests/shell/testcases/chains/0042chain_variable_0 +index 1ea44e8..739dc05 100755 +--- a/tests/shell/testcases/chains/0042chain_variable_0 ++++ b/tests/shell/testcases/chains/0042chain_variable_0 +@@ -2,7 +2,8 @@ + + set -e + +-ip link add name dummy0 type dummy ++ip link add name d23456789012345 type dummy ++ + + EXPECTED="define if_main = \"lo\" + +@@ -14,22 +15,50 @@ table netdev filter1 { + + $NFT -f - <<< $EXPECTED + ++ ++EXPECTED="define if_main = \"lo\" ++ ++table netdev filter2 { ++ chain Main_Ingress2 { ++ type filter hook ingress devices = { \$if_main, d23456789012345x } priority -500; policy accept; ++ } ++}" ++ ++rc=0 ++$NFT -f - <<< $EXPECTED || rc=$? ++test "$rc" = 0 ++cat < +Date: Tue, 30 Jun 2026 14:31:00 +0200 +Subject: [PATCH] tests: shell: adjust add-after-delete flowtable for older + kernels + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 566182cd8259cfc22f9fd949e9ad3c15ed6a7945 + +commit 566182cd8259cfc22f9fd949e9ad3c15ed6a7945 +Author: Pablo Neira Ayuso +Date: Mon Nov 20 13:54:03 2023 +0100 + + tests: shell: adjust add-after-delete flowtable for older kernels + + Remove counter from flowtable, older kernels (<=5.4) do not support this + in testcases/flowtable/0013addafterdelete_0 so this bug is still + covered. + + Skip testcases/flowtable/0014addafterdelete_0 if flowtable counter + support is not available. + + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + tests/shell/testcases/flowtable/0013addafterdelete_0 | 2 -- + tests/shell/testcases/flowtable/0014addafterdelete_0 | 2 ++ + tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft | 1 - + 3 files changed, 2 insertions(+), 3 deletions(-) + +diff --git a/tests/shell/testcases/flowtable/0013addafterdelete_0 b/tests/shell/testcases/flowtable/0013addafterdelete_0 +index b23ab97..56c9834 100755 +--- a/tests/shell/testcases/flowtable/0013addafterdelete_0 ++++ b/tests/shell/testcases/flowtable/0013addafterdelete_0 +@@ -7,7 +7,6 @@ RULESET='table inet filter { + flowtable f { + hook ingress priority filter - 1 + devices = { lo } +- counter + } + }' + +@@ -20,7 +19,6 @@ table inet filter { + flowtable f { + hook ingress priority filter - 1 + devices = { lo } +- counter + } + }' + +diff --git a/tests/shell/testcases/flowtable/0014addafterdelete_0 b/tests/shell/testcases/flowtable/0014addafterdelete_0 +index 6a24c4b..1ac6510 100755 +--- a/tests/shell/testcases/flowtable/0014addafterdelete_0 ++++ b/tests/shell/testcases/flowtable/0014addafterdelete_0 +@@ -1,5 +1,7 @@ + #!/bin/bash + ++# NFT_TEST_REQUIRES(NFT_TEST_HAVE_flowtable_counter) ++ + set -e + + RULESET='table inet filter { +diff --git a/tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft b/tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft +index 83fdd5d..67db7d0 100644 +--- a/tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft ++++ b/tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft +@@ -2,6 +2,5 @@ table inet filter { + flowtable f { + hook ingress priority filter - 1 + devices = { lo } +- counter + } + } diff --git a/0046-tests-shell-skip-if-kernel-does-not-support-flowtabl.patch b/0046-tests-shell-skip-if-kernel-does-not-support-flowtabl.patch new file mode 100644 index 0000000..d828a59 --- /dev/null +++ b/0046-tests-shell-skip-if-kernel-does-not-support-flowtabl.patch @@ -0,0 +1,61 @@ +From 3c78f54adfd9d130aeca4c779a8d82b27bb69e60 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:01 +0200 +Subject: [PATCH] tests: shell: skip if kernel does not support flowtable + counter + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 279654904d5186430588b475501b2ca0d3ab6517 + +commit 279654904d5186430588b475501b2ca0d3ab6517 +Author: Pablo Neira Ayuso +Date: Tue Nov 14 16:57:22 2023 +0100 + + tests: shell: skip if kernel does not support flowtable counter + + Check if kernel provides flowtable counter supports which is available + since 53c2b2899af7 ("netfilter: flowtable: add counter support"). + + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + tests/shell/features/flowtable_counter.sh | 16 ++++++++++++++++ + .../testcases/flowtable/0012flowtable_variable_0 | 2 ++ + 2 files changed, 18 insertions(+) + create mode 100755 tests/shell/features/flowtable_counter.sh + +diff --git a/tests/shell/features/flowtable_counter.sh b/tests/shell/features/flowtable_counter.sh +new file mode 100755 +index 0000000..a4c4c62 +--- /dev/null ++++ b/tests/shell/features/flowtable_counter.sh +@@ -0,0 +1,16 @@ ++#!/bin/bash ++ ++# 53c2b2899af7 ("netfilter: flowtable: add counter support") ++# v5.7-rc1~146^2~12^2~16 ++ ++EXPECTED="table ip filter2 { ++ flowtable main_ft2 { ++ hook ingress priority filter ++ devices = { lo } ++ counter ++ } ++}" ++ ++$NFT -f - <<< $EXPECTED ++ ++diff -u <($NFT list ruleset) - <<<"$EXPECTED" +diff --git a/tests/shell/testcases/flowtable/0012flowtable_variable_0 b/tests/shell/testcases/flowtable/0012flowtable_variable_0 +index 080059d..9c03820 100755 +--- a/tests/shell/testcases/flowtable/0012flowtable_variable_0 ++++ b/tests/shell/testcases/flowtable/0012flowtable_variable_0 +@@ -1,5 +1,7 @@ + #!/bin/bash + ++# NFT_TEST_REQUIRES(NFT_TEST_HAVE_flowtable_counter) ++ + set -e + + iface_cleanup() { diff --git a/0047-rule-print-chain-and-flowtable-devices-in-quotes.patch b/0047-rule-print-chain-and-flowtable-devices-in-quotes.patch new file mode 100644 index 0000000..fedf381 --- /dev/null +++ b/0047-rule-print-chain-and-flowtable-devices-in-quotes.patch @@ -0,0 +1,263 @@ +From f456bdb07b559c8ff46e46701b0d61729549de73 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:01 +0200 +Subject: [PATCH] rule: print chain and flowtable devices in quotes + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit eb30f236d91a8d61ece789e28e6540b3a3fa2a6a + +commit eb30f236d91a8d61ece789e28e6540b3a3fa2a6a +Author: Pablo Neira Ayuso +Date: Wed Jul 9 00:13:56 2025 +0200 + + rule: print chain and flowtable devices in quotes + + Print devices in quotes, for consistency with: + + - the existing chain listing with single device: + + type filter hook ingress device "lo" priority filter; policy accept + + - the ifname datatype used in sets. + + In general, tokens that are user-defined, not coming in the datatype + symbol list, are enclosed in quotes. + + Fixes: 3fdc7541fba0 ("src: add multidevice support for netdev chain") + Acked-by: Florian Westphal + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/rule.c | 4 ++-- + tests/shell/features/flowtable_counter.sh | 2 +- + .../testcases/chains/dumps/0042chain_variable_0.nft | 4 ++-- + .../testcases/flowtable/dumps/0001flowtable_0.nft | 2 +- + .../flowtable/dumps/0002create_flowtable_0.nft | 2 +- + .../flowtable/dumps/0003add_after_flush_0.nft | 2 +- + .../flowtable/dumps/0005delete_in_use_1.nft | 2 +- + .../flowtable/dumps/0012flowtable_variable_0.nft | 4 ++-- + .../flowtable/dumps/0013addafterdelete_0.nft | 2 +- + .../flowtable/dumps/0014addafterdelete_0.nft | 2 +- + tests/shell/testcases/listing/0020flowtable_0 | 12 ++++++------ + .../testcases/listing/dumps/0020flowtable_0.nft | 4 ++-- + 12 files changed, 21 insertions(+), 21 deletions(-) + +diff --git a/src/rule.c b/src/rule.c +index 6f97f77..c026d8d 100644 +--- a/src/rule.c ++++ b/src/rule.c +@@ -1041,7 +1041,7 @@ static void chain_print_declaration(const struct chain *chain, + } else if (chain->dev_array_len > 1) { + nft_print(octx, " devices = { "); + for (i = 0; i < chain->dev_array_len; i++) { +- nft_print(octx, "%s", chain->dev_array[i]); ++ nft_print(octx, "\"%s\"", chain->dev_array[i]); + if (i + 1 != chain->dev_array_len) + nft_print(octx, ", "); + } +@@ -2123,7 +2123,7 @@ static void flowtable_print_declaration(const struct flowtable *flowtable, + if (flowtable->dev_array_len > 0) { + nft_print(octx, "%s%sdevices = { ", opts->tab, opts->tab); + for (i = 0; i < flowtable->dev_array_len; i++) { +- nft_print(octx, "%s", flowtable->dev_array[i]); ++ nft_print(octx, "\"%s\"", flowtable->dev_array[i]); + if (i + 1 != flowtable->dev_array_len) + nft_print(octx, ", "); + } +diff --git a/tests/shell/features/flowtable_counter.sh b/tests/shell/features/flowtable_counter.sh +index a4c4c62..5d47215 100755 +--- a/tests/shell/features/flowtable_counter.sh ++++ b/tests/shell/features/flowtable_counter.sh +@@ -6,7 +6,7 @@ + EXPECTED="table ip filter2 { + flowtable main_ft2 { + hook ingress priority filter +- devices = { lo } ++ devices = { \"lo\" } + counter + } + }" +diff --git a/tests/shell/testcases/chains/dumps/0042chain_variable_0.nft b/tests/shell/testcases/chains/dumps/0042chain_variable_0.nft +index 84a908d..08a1901 100644 +--- a/tests/shell/testcases/chains/dumps/0042chain_variable_0.nft ++++ b/tests/shell/testcases/chains/dumps/0042chain_variable_0.nft +@@ -5,12 +5,12 @@ table netdev filter1 { + } + table netdev filter2 { + chain Main_Ingress2 { +- type filter hook ingress devices = { d23456789012345, lo } priority -500; policy accept; ++ type filter hook ingress devices = { "d23456789012345", "lo" } priority -500; policy accept; + } + } + table netdev filter3 { + chain Main_Ingress3 { +- type filter hook ingress devices = { d23456789012345, lo } priority -500; policy accept; ++ type filter hook ingress devices = { "d23456789012345", "lo" } priority -500; policy accept; + } + + chain Main_Egress3 { +diff --git a/tests/shell/testcases/flowtable/dumps/0001flowtable_0.nft b/tests/shell/testcases/flowtable/dumps/0001flowtable_0.nft +index 629bfe8..79fa591 100644 +--- a/tests/shell/testcases/flowtable/dumps/0001flowtable_0.nft ++++ b/tests/shell/testcases/flowtable/dumps/0001flowtable_0.nft +@@ -1,7 +1,7 @@ + table inet t { + flowtable f { + hook ingress priority filter + 10 +- devices = { lo } ++ devices = { "lo" } + } + + chain c { +diff --git a/tests/shell/testcases/flowtable/dumps/0002create_flowtable_0.nft b/tests/shell/testcases/flowtable/dumps/0002create_flowtable_0.nft +index aecfb2a..2d0ea90 100644 +--- a/tests/shell/testcases/flowtable/dumps/0002create_flowtable_0.nft ++++ b/tests/shell/testcases/flowtable/dumps/0002create_flowtable_0.nft +@@ -1,6 +1,6 @@ + table ip t { + flowtable f { + hook ingress priority filter + 10 +- devices = { lo } ++ devices = { "lo" } + } + } +diff --git a/tests/shell/testcases/flowtable/dumps/0003add_after_flush_0.nft b/tests/shell/testcases/flowtable/dumps/0003add_after_flush_0.nft +index dd904f4..39de91b 100644 +--- a/tests/shell/testcases/flowtable/dumps/0003add_after_flush_0.nft ++++ b/tests/shell/testcases/flowtable/dumps/0003add_after_flush_0.nft +@@ -1,6 +1,6 @@ + table ip x { + flowtable y { + hook ingress priority filter +- devices = { lo } ++ devices = { "lo" } + } + } +diff --git a/tests/shell/testcases/flowtable/dumps/0005delete_in_use_1.nft b/tests/shell/testcases/flowtable/dumps/0005delete_in_use_1.nft +index c1d79e7..b01b302 100644 +--- a/tests/shell/testcases/flowtable/dumps/0005delete_in_use_1.nft ++++ b/tests/shell/testcases/flowtable/dumps/0005delete_in_use_1.nft +@@ -1,7 +1,7 @@ + table ip x { + flowtable y { + hook ingress priority filter +- devices = { lo } ++ devices = { "lo" } + } + + chain x { +diff --git a/tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.nft b/tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.nft +index df1c51a..7863822 100644 +--- a/tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.nft ++++ b/tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.nft +@@ -1,14 +1,14 @@ + table ip filter1 { + flowtable Main_ft1 { + hook ingress priority filter +- devices = { lo } ++ devices = { "lo" } + counter + } + } + table ip filter2 { + flowtable Main_ft2 { + hook ingress priority filter +- devices = { lo } ++ devices = { "lo" } + counter + } + } +diff --git a/tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft b/tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft +index 67db7d0..585f63b 100644 +--- a/tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft ++++ b/tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft +@@ -1,6 +1,6 @@ + table inet filter { + flowtable f { + hook ingress priority filter - 1 +- devices = { lo } ++ devices = { "lo" } + } + } +diff --git a/tests/shell/testcases/flowtable/dumps/0014addafterdelete_0.nft b/tests/shell/testcases/flowtable/dumps/0014addafterdelete_0.nft +index 145aa08..12f97a7 100644 +--- a/tests/shell/testcases/flowtable/dumps/0014addafterdelete_0.nft ++++ b/tests/shell/testcases/flowtable/dumps/0014addafterdelete_0.nft +@@ -1,7 +1,7 @@ + table inet filter { + flowtable f { + hook ingress priority filter - 1 +- devices = { lo } ++ devices = { "lo" } + counter + } + +diff --git a/tests/shell/testcases/listing/0020flowtable_0 b/tests/shell/testcases/listing/0020flowtable_0 +index 6eb82cf..8bd852b 100755 +--- a/tests/shell/testcases/listing/0020flowtable_0 ++++ b/tests/shell/testcases/listing/0020flowtable_0 +@@ -6,11 +6,11 @@ set -e + + FLOWTABLES="flowtable f { + hook ingress priority filter +- devices = { lo } ++ devices = { \"lo\" } + } + flowtable f2 { + hook ingress priority filter +- devices = { d0 } ++ devices = { \"d0\" } + }" + + RULESET="table inet filter { +@@ -23,23 +23,23 @@ table ip filter { + EXPECTED="table inet filter { + flowtable f { + hook ingress priority filter +- devices = { lo } ++ devices = { \"lo\" } + } + }" + EXPECTED2="table ip filter { + flowtable f2 { + hook ingress priority filter +- devices = { d0 } ++ devices = { \"d0\" } + } + }" + EXPECTED3="table ip filter { + flowtable f { + hook ingress priority filter +- devices = { lo } ++ devices = { \"lo\" } + } + flowtable f2 { + hook ingress priority filter +- devices = { d0 } ++ devices = { \"d0\" } + } + }" + +diff --git a/tests/shell/testcases/listing/dumps/0020flowtable_0.nft b/tests/shell/testcases/listing/dumps/0020flowtable_0.nft +index 4a64e53..0962e73 100644 +--- a/tests/shell/testcases/listing/dumps/0020flowtable_0.nft ++++ b/tests/shell/testcases/listing/dumps/0020flowtable_0.nft +@@ -1,7 +1,7 @@ + table inet filter { + flowtable f { + hook ingress priority filter +- devices = { lo } ++ devices = { "lo" } + } + + flowtable f2 { +@@ -11,7 +11,7 @@ table inet filter { + table ip filter { + flowtable f { + hook ingress priority filter +- devices = { lo } ++ devices = { "lo" } + } + + flowtable f2 { diff --git a/0048-evaluate-validate-set-expression-type-before-accessi.patch b/0048-evaluate-validate-set-expression-type-before-accessi.patch new file mode 100644 index 0000000..850eaa7 --- /dev/null +++ b/0048-evaluate-validate-set-expression-type-before-accessi.patch @@ -0,0 +1,39 @@ +From 59239ade08feeb6abe45304632d6349eefabb5bf Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:01 +0200 +Subject: [PATCH] evaluate: validate set expression type before accessing flags + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 2022e8bb5cf0e0fa81ab0a5087bd1ab6e20280ee + +commit 2022e8bb5cf0e0fa81ab0a5087bd1ab6e20280ee +Author: Pablo Neira Ayuso +Date: Wed Jul 9 00:21:49 2025 +0200 + + evaluate: validate set expression type before accessing flags + + Validate set->init is of EXPR_SET expression type before accessing + set_flags. + + Fixes: 81e36530fcac ("src: replace interval segment tree overlap and automerge") + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/evaluate.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/evaluate.c b/src/evaluate.c +index 711990a..5012dd2 100644 +--- a/src/evaluate.c ++++ b/src/evaluate.c +@@ -4715,7 +4715,8 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set) + set->flags |= NFT_SET_EXPR; + + if (set_is_anonymous(set->flags)) { +- if (set_is_interval(set->init->set_flags) && ++ if (set->init->etype == EXPR_SET && ++ set_is_interval(set->init->set_flags) && + !(set->init->set_flags & NFT_SET_CONCAT) && + interval_set_eval(ctx, set, set->init) < 0) + return -1; diff --git a/0049-mnl-Call-mnl_attr_nest_end-just-once.patch b/0049-mnl-Call-mnl_attr_nest_end-just-once.patch new file mode 100644 index 0000000..d2f8de3 --- /dev/null +++ b/0049-mnl-Call-mnl_attr_nest_end-just-once.patch @@ -0,0 +1,39 @@ +From 093c9db2a767666a54bfc500ff940302d44775cb Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:01 +0200 +Subject: [PATCH] mnl: Call mnl_attr_nest_end() just once + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 8a1de0ec9709b58a80a86a20cfba107b11c85f6e + +commit 8a1de0ec9709b58a80a86a20cfba107b11c85f6e +Author: Phil Sutter +Date: Wed Jul 16 14:26:08 2025 +0200 + + mnl: Call mnl_attr_nest_end() just once + + Calling the function after each added nested attribute is harmless but + pointless. + + Fixes: a66b5ad9540dd ("src: allow for updating devices on existing netdev chain") + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + src/mnl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/mnl.c b/src/mnl.c +index 0fb36bd..c62b140 100644 +--- a/src/mnl.c ++++ b/src/mnl.c +@@ -797,8 +797,8 @@ static void mnl_nft_chain_devs_build(struct nlmsghdr *nlh, struct cmd *cmd) + for (i = 0; i < num_devs; i++) { + cmd_add_loc(cmd, nlh->nlmsg_len, dev_array[i].location); + mnl_attr_put_strz(nlh, NFTA_DEVICE_NAME, dev_array[i].ifname); +- mnl_attr_nest_end(nlh, nest_dev); + } ++ mnl_attr_nest_end(nlh, nest_dev); + } + nft_dev_array_free(dev_array); + } diff --git a/0050-doc-nft.8-Minor-NAT-STATEMENTS-section-review.patch b/0050-doc-nft.8-Minor-NAT-STATEMENTS-section-review.patch new file mode 100644 index 0000000..2bc0b72 --- /dev/null +++ b/0050-doc-nft.8-Minor-NAT-STATEMENTS-section-review.patch @@ -0,0 +1,78 @@ +From 77a991b09586a95f4f8d583fcc798e7e861d36d7 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:01 +0200 +Subject: [PATCH] doc: nft.8: Minor NAT STATEMENTS section review + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +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 c7529ee..3aceb02 100644 +--- a/doc/statements.txt ++++ b/doc/statements.txt +@@ -349,11 +349,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' + +@@ -363,11 +364,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. +@@ -377,8 +378,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/0051-src-netlink-netlink_delinearize_table-may-return-NUL.patch b/0051-src-netlink-netlink_delinearize_table-may-return-NUL.patch new file mode 100644 index 0000000..e3690ce --- /dev/null +++ b/0051-src-netlink-netlink_delinearize_table-may-return-NUL.patch @@ -0,0 +1,54 @@ +From 123f57f8e49e53e8806e389cfd524f35dcfbc4a4 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:01 +0200 +Subject: [PATCH] src: netlink: netlink_delinearize_table() may return NULL + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit a69d552a005ba467d37e225032e35d01d9491241 + +commit a69d552a005ba467d37e225032e35d01d9491241 +Author: Phil Sutter +Date: Fri Aug 8 14:21:41 2025 +0200 + + src: netlink: netlink_delinearize_table() may return NULL + + Catch the error condition in callers to avoid crashes. + + Fixes: c156232a530b3 ("src: add comment support when adding tables") + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + src/monitor.c | 4 ++++ + src/netlink.c | 3 ++- + 2 files changed, 6 insertions(+), 1 deletion(-) + +diff --git a/src/monitor.c b/src/monitor.c +index 82762a0..bf59f2d 100644 +--- a/src/monitor.c ++++ b/src/monitor.c +@@ -224,6 +224,10 @@ static int netlink_events_table_cb(const struct nlmsghdr *nlh, int type, + + nlt = netlink_table_alloc(nlh); + t = netlink_delinearize_table(monh->ctx, nlt); ++ if (!t) { ++ nftnl_table_free(nlt); ++ return MNL_CB_ERROR; ++ } + cmd = netlink_msg2cmd(type, nlh->nlmsg_flags); + + switch (monh->format) { +diff --git a/src/netlink.c b/src/netlink.c +index aafc583..73123e9 100644 +--- a/src/netlink.c ++++ b/src/netlink.c +@@ -754,7 +754,8 @@ static int list_table_cb(struct nftnl_table *nlt, void *arg) + struct table *table; + + table = netlink_delinearize_table(ctx, nlt); +- list_add_tail(&table->list, &ctx->list); ++ if (table) ++ list_add_tail(&table->list, &ctx->list); + + return 0; + } diff --git a/0052-tests-py-Drop-stale-entries-since-redundant-test-cas.patch b/0052-tests-py-Drop-stale-entries-since-redundant-test-cas.patch new file mode 100644 index 0000000..8e40870 --- /dev/null +++ b/0052-tests-py-Drop-stale-entries-since-redundant-test-cas.patch @@ -0,0 +1,340 @@ +From 9206bb36c6c238cb8019f3d021e52b359e416623 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:01 +0200 +Subject: [PATCH] tests: py: Drop stale entries since redundant test case + removal + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 7db422da28349edcdeb3c96a410b242856ca74ac + +commit 7db422da28349edcdeb3c96a410b242856ca74ac +Author: Phil Sutter +Date: Wed Aug 13 14:14:45 2025 +0200 + + tests: py: Drop stale entries since redundant test case removal + + Fixed commit left stale JSON equivalents and payload records in place, + drop them. + + Fixes: ec1ea13314fa5 ("tests: remove redundant test cases") + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + tests/py/any/meta.t.json | 38 ---------- + tests/py/ip6/dst.t.json | 23 ------ + tests/py/ip6/dst.t.payload.inet | 10 --- + tests/py/ip6/dst.t.payload.ip6 | 8 --- + tests/py/ip6/frag.t.json | 121 -------------------------------- + tests/py/ip6/ip6.t.json | 40 ----------- + 6 files changed, 240 deletions(-) + +diff --git a/tests/py/any/meta.t.json b/tests/py/any/meta.t.json +index 4734bbf..6bde76b 100644 +--- a/tests/py/any/meta.t.json ++++ b/tests/py/any/meta.t.json +@@ -2294,44 +2294,6 @@ + } + ] + +-# meta cgroup {1048577-1048578} +-[ +- { +- "match": { +- "left": { +- "meta": { "key": "cgroup" } +- }, +- "op": "==", +- "right": { +- "set": [ +- { +- "range": [ 1048577, 1048578 ] +- } +- ] +- } +- } +- } +-] +- +-# meta cgroup != { 1048577-1048578} +-[ +- { +- "match": { +- "left": { +- "meta": { "key": "cgroup" } +- }, +- "op": "!=", +- "right": { +- "set": [ +- { +- "range": [ 1048577, 1048578 ] +- } +- ] +- } +- } +- } +-] +- + # meta iif . meta oif { "lo" . "lo" } + [ + { +diff --git a/tests/py/ip6/dst.t.json b/tests/py/ip6/dst.t.json +index e947a76..7ba4572 100644 +--- a/tests/py/ip6/dst.t.json ++++ b/tests/py/ip6/dst.t.json +@@ -290,26 +290,3 @@ + } + } + ] +- +-# dst hdrlength != { 33, 55, 67, 88} +-[ +- { +- "match": { +- "left": { +- "exthdr": { +- "field": "hdrlength", +- "name": "dst" +- } +- }, +- "op": "!=", +- "right": { +- "set": [ +- 33, +- 55, +- 67, +- 88 +- ] +- } +- } +- } +-] +diff --git a/tests/py/ip6/dst.t.payload.inet b/tests/py/ip6/dst.t.payload.inet +index 90d6bda..b2a79ed 100644 +--- a/tests/py/ip6/dst.t.payload.inet ++++ b/tests/py/ip6/dst.t.payload.inet +@@ -119,13 +119,3 @@ ip6 test-ip6 input + [ cmp eq reg 1 0x0000000a ] + [ exthdr load ipv6 1b @ 60 + 1 => reg 1 ] + [ lookup reg 1 set __set%d ] +- +-# dst hdrlength != { 33, 55, 67, 88} +-__set%d test-ip6 3 +-__set%d test-ip6 0 +- element 00000021 : 0 [end] element 00000037 : 0 [end] element 00000043 : 0 [end] element 00000058 : 0 [end] +-ip6 test-ip6 input +- [ meta load nfproto => reg 1 ] +- [ cmp eq reg 1 0x0000000a ] +- [ exthdr load ipv6 1b @ 60 + 1 => reg 1 ] +- [ lookup reg 1 set __set%d 0x1 ] +diff --git a/tests/py/ip6/dst.t.payload.ip6 b/tests/py/ip6/dst.t.payload.ip6 +index 941140d..5058b46 100644 +--- a/tests/py/ip6/dst.t.payload.ip6 ++++ b/tests/py/ip6/dst.t.payload.ip6 +@@ -89,11 +89,3 @@ __set%d test-ip6 0 + ip6 test-ip6 input + [ exthdr load ipv6 1b @ 60 + 1 => reg 1 ] + [ lookup reg 1 set __set%d ] +- +-# dst hdrlength != { 33, 55, 67, 88} +-__set%d test-ip6 3 +-__set%d test-ip6 0 +- element 00000021 : 0 [end] element 00000037 : 0 [end] element 00000043 : 0 [end] element 00000058 : 0 [end] +-ip6 test-ip6 input +- [ exthdr load ipv6 1b @ 60 + 1 => reg 1 ] +- [ lookup reg 1 set __set%d 0x1 ] +diff --git a/tests/py/ip6/frag.t.json b/tests/py/ip6/frag.t.json +index b8c06df..6953e87 100644 +--- a/tests/py/ip6/frag.t.json ++++ b/tests/py/ip6/frag.t.json +@@ -230,46 +230,6 @@ + } + ] + +-# frag reserved { 33-55} +-[ +- { +- "match": { +- "left": { +- "exthdr": { +- "field": "reserved", +- "name": "frag" +- } +- }, +- "op": "==", +- "right": { +- "set": [ +- { "range": [ 33, 55 ] } +- ] +- } +- } +- } +-] +- +-# frag reserved != { 33-55} +-[ +- { +- "match": { +- "left": { +- "exthdr": { +- "field": "reserved", +- "name": "frag" +- } +- }, +- "op": "!=", +- "right": { +- "set": [ +- { "range": [ 33, 55 ] } +- ] +- } +- } +- } +-] +- + # frag frag-off 22 + [ + { +@@ -384,46 +344,6 @@ + } + ] + +-# frag frag-off { 33-55} +-[ +- { +- "match": { +- "left": { +- "exthdr": { +- "field": "frag-off", +- "name": "frag" +- } +- }, +- "op": "==", +- "right": { +- "set": [ +- { "range": [ 33, 55 ] } +- ] +- } +- } +- } +-] +- +-# frag frag-off != { 33-55} +-[ +- { +- "match": { +- "left": { +- "exthdr": { +- "field": "frag-off", +- "name": "frag" +- } +- }, +- "op": "!=", +- "right": { +- "set": [ +- { "range": [ 33, 55 ] } +- ] +- } +- } +- } +-] +- + # frag reserved2 1 + [ + { +@@ -601,44 +521,3 @@ + } + } + ] +- +-# frag id { 33-55} +-[ +- { +- "match": { +- "left": { +- "exthdr": { +- "field": "id", +- "name": "frag" +- } +- }, +- "op": "==", +- "right": { +- "set": [ +- { "range": [ 33, 55 ] } +- ] +- } +- } +- } +-] +- +-# frag id != { 33-55} +-[ +- { +- "match": { +- "left": { +- "exthdr": { +- "field": "id", +- "name": "frag" +- } +- }, +- "op": "!=", +- "right": { +- "set": [ +- { "range": [ 33, 55 ] } +- ] +- } +- } +- } +-] +- +diff --git a/tests/py/ip6/ip6.t.json b/tests/py/ip6/ip6.t.json +index cf80217..46d347d 100644 +--- a/tests/py/ip6/ip6.t.json ++++ b/tests/py/ip6/ip6.t.json +@@ -471,46 +471,6 @@ + } + ] + +-# ip6 nexthdr { 33-44} +-[ +- { +- "match": { +- "left": { +- "payload": { +- "field": "nexthdr", +- "protocol": "ip6" +- } +- }, +- "op": "==", +- "right": { +- "set": [ +- { "range": [ 33, 44 ] } +- ] +- } +- } +- } +-] +- +-# ip6 nexthdr != { 33-44} +-[ +- { +- "match": { +- "left": { +- "payload": { +- "field": "nexthdr", +- "protocol": "ip6" +- } +- }, +- "op": "!=", +- "right": { +- "set": [ +- { "range": [ 33, 44 ] } +- ] +- } +- } +- } +-] +- + # ip6 nexthdr 33-44 + [ + { diff --git a/0053-tests-py-Drop-duplicate-test-from-inet-geneve.t.patch b/0053-tests-py-Drop-duplicate-test-from-inet-geneve.t.patch new file mode 100644 index 0000000..3f024c4 --- /dev/null +++ b/0053-tests-py-Drop-duplicate-test-from-inet-geneve.t.patch @@ -0,0 +1,79 @@ +From a1194b27b2f418e44608462212fbb90a0e2ded5d Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:01 +0200 +Subject: [PATCH] tests: py: Drop duplicate test from inet/geneve.t + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit a3b279794c2e26f9921445163e9bde0df5bda651 + +commit a3b279794c2e26f9921445163e9bde0df5bda651 +Author: Phil Sutter +Date: Wed Aug 13 14:19:31 2025 +0200 + + tests: py: Drop duplicate test from inet/geneve.t + + The test was duplicate since day 1. The duplicate JSON equivalent was + added later (semi-automated), remove it as well. + + Fixes: 2b9143bc7ab81 ("tests: py: add geneve tests") + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + tests/py/inet/geneve.t | 1 - + tests/py/inet/geneve.t.json | 29 ----------------------------- + 2 files changed, 30 deletions(-) + +diff --git a/tests/py/inet/geneve.t b/tests/py/inet/geneve.t +index 101f6df..ad46272 100644 +--- a/tests/py/inet/geneve.t ++++ b/tests/py/inet/geneve.t +@@ -17,7 +17,6 @@ udp dport 6081 geneve icmp type echo-reply;ok + udp dport 6081 geneve ether saddr 62:87:4d:d6:19:05;ok + udp dport 6081 geneve vlan id 10;ok + udp dport 6081 geneve ip dscp 0x02;ok +-udp dport 6081 geneve ip dscp 0x02;ok + udp dport 6081 geneve ip saddr . geneve ip daddr { 1.2.3.4 . 4.3.2.1 };ok + + udp dport 6081 geneve ip saddr set 1.2.3.4;fail +diff --git a/tests/py/inet/geneve.t.json b/tests/py/inet/geneve.t.json +index a299fcd..1589918 100644 +--- a/tests/py/inet/geneve.t.json ++++ b/tests/py/inet/geneve.t.json +@@ -264,35 +264,6 @@ + } + ] + +-# udp dport 6081 geneve ip dscp 0x02 +-[ +- { +- "match": { +- "left": { +- "payload": { +- "field": "dport", +- "protocol": "udp" +- } +- }, +- "op": "==", +- "right": 6081 +- } +- }, +- { +- "match": { +- "left": { +- "payload": { +- "field": "dscp", +- "protocol": "ip", +- "tunnel": "geneve" +- } +- }, +- "op": "==", +- "right": 2 +- } +- } +-] +- + # udp dport 6081 geneve ip saddr . geneve ip daddr { 1.2.3.4 . 4.3.2.1 } + [ + { diff --git a/0054-tests-py-Drop-duplicate-test-from-inet-gre.t.patch b/0054-tests-py-Drop-duplicate-test-from-inet-gre.t.patch new file mode 100644 index 0000000..3db59c7 --- /dev/null +++ b/0054-tests-py-Drop-duplicate-test-from-inet-gre.t.patch @@ -0,0 +1,67 @@ +From 564d6cdd405b352b08ecfb0f25ff43557dc94406 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:01 +0200 +Subject: [PATCH] tests: py: Drop duplicate test from inet/gre.t + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 7a566cb4e7b98c5f6509d9cb25d0281d5198da28 + +commit 7a566cb4e7b98c5f6509d9cb25d0281d5198da28 +Author: Phil Sutter +Date: Wed Aug 13 14:22:07 2025 +0200 + + tests: py: Drop duplicate test from inet/gre.t + + The test was duplicate since day 1. The duplicate JSON equivalent was + added later (semi-automated), remove it as well. + + Fixes: c04ef8d104ec6 ("tests: py: add gre tests") + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + tests/py/inet/gre.t | 1 - + tests/py/inet/gre.t.json | 17 ----------------- + 2 files changed, 18 deletions(-) + +diff --git a/tests/py/inet/gre.t b/tests/py/inet/gre.t +index a3e046a..a21e67e 100644 +--- a/tests/py/inet/gre.t ++++ b/tests/py/inet/gre.t +@@ -16,7 +16,6 @@ gre icmp type echo-reply;ok + gre ether saddr 62:87:4d:d6:19:05;fail + gre vlan id 10;fail + gre ip dscp 0x02;ok +-gre ip dscp 0x02;ok + gre ip saddr . gre ip daddr { 1.2.3.4 . 4.3.2.1 };ok + + gre ip saddr set 1.2.3.4;fail +diff --git a/tests/py/inet/gre.t.json b/tests/py/inet/gre.t.json +index c443176..a354e6b 100644 +--- a/tests/py/inet/gre.t.json ++++ b/tests/py/inet/gre.t.json +@@ -121,23 +121,6 @@ + } + ] + +-# gre ip dscp 0x02 +-[ +- { +- "match": { +- "left": { +- "payload": { +- "field": "dscp", +- "protocol": "ip", +- "tunnel": "gre" +- } +- }, +- "op": "==", +- "right": 2 +- } +- } +-] +- + # gre ip saddr . gre ip daddr { 1.2.3.4 . 4.3.2.1 } + [ + { diff --git a/0055-tests-py-Drop-duplicate-test-from-inet-gretap.t.patch b/0055-tests-py-Drop-duplicate-test-from-inet-gretap.t.patch new file mode 100644 index 0000000..cc46b52 --- /dev/null +++ b/0055-tests-py-Drop-duplicate-test-from-inet-gretap.t.patch @@ -0,0 +1,67 @@ +From b90837cd1236b7a76fd482ed48c611f8d1317763 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:01 +0200 +Subject: [PATCH] tests: py: Drop duplicate test from inet/gretap.t + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit e159957099746a294ce767c16665d77b5ef2d464 + +commit e159957099746a294ce767c16665d77b5ef2d464 +Author: Phil Sutter +Date: Wed Aug 13 14:23:30 2025 +0200 + + tests: py: Drop duplicate test from inet/gretap.t + + The test was duplicate since day 1. The duplicate JSON equivalent was + added later (semi-automated), remove it as well. + + Fixes: 39a68d9ffd25c ("tests: py: add gretap tests") + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + tests/py/inet/gretap.t | 1 - + tests/py/inet/gretap.t.json | 17 ----------------- + 2 files changed, 18 deletions(-) + +diff --git a/tests/py/inet/gretap.t b/tests/py/inet/gretap.t +index cd7ee21..f88896f 100644 +--- a/tests/py/inet/gretap.t ++++ b/tests/py/inet/gretap.t +@@ -15,7 +15,6 @@ gretap icmp type echo-reply;ok + gretap ether saddr 62:87:4d:d6:19:05;ok + gretap vlan id 10;ok + gretap ip dscp 0x02;ok +-gretap ip dscp 0x02;ok + gretap ip saddr . gretap ip daddr { 1.2.3.4 . 4.3.2.1 };ok + + gretap ip saddr set 1.2.3.4;fail +diff --git a/tests/py/inet/gretap.t.json b/tests/py/inet/gretap.t.json +index 36fa978..6c16a08 100644 +--- a/tests/py/inet/gretap.t.json ++++ b/tests/py/inet/gretap.t.json +@@ -139,23 +139,6 @@ + } + ] + +-# gretap ip dscp 0x02 +-[ +- { +- "match": { +- "left": { +- "payload": { +- "field": "dscp", +- "protocol": "ip", +- "tunnel": "gretap" +- } +- }, +- "op": "==", +- "right": 2 +- } +- } +-] +- + # gretap ip saddr . gretap ip daddr { 1.2.3.4 . 4.3.2.1 } + [ + { diff --git a/0056-tests-py-Drop-stale-entry-from-inet-tcp.t.json.patch b/0056-tests-py-Drop-stale-entry-from-inet-tcp.t.json.patch new file mode 100644 index 0000000..5b6d303 --- /dev/null +++ b/0056-tests-py-Drop-stale-entry-from-inet-tcp.t.json.patch @@ -0,0 +1,69 @@ +From e7191aee64f0ac14a9d0fc2856d25832e281c33a Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:01 +0200 +Subject: [PATCH] tests: py: Drop stale entry from inet/tcp.t.json + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit f8fe3d56194f06bea9cd82d1d707064b202f5202 + +commit f8fe3d56194f06bea9cd82d1d707064b202f5202 +Author: Phil Sutter +Date: Wed Aug 13 14:32:11 2025 +0200 + + tests: py: Drop stale entry from inet/tcp.t.json + + The test was changed but JSON equivalents not updated. Commit + c0b685951fabb ("json: fix parse of flagcmp expression") then added an + equivalent matching the changed test, so just drop the old one. + + Fixes: c3d57114f119b ("parser_bison: add shortcut syntax for matching flags without binary operations") + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + tests/py/inet/tcp.t.json | 31 ------------------------------- + 1 file changed, 31 deletions(-) + +diff --git a/tests/py/inet/tcp.t.json b/tests/py/inet/tcp.t.json +index 8439c2b..cc2562c 100644 +--- a/tests/py/inet/tcp.t.json ++++ b/tests/py/inet/tcp.t.json +@@ -910,37 +910,6 @@ + } + ] + +-# tcp flags & (syn|fin) == (syn|fin) +-[ +- { +- "match": { +- "left": { +- "&": [ +- { +- "payload": { +- "field": "flags", +- "protocol": "tcp" +- } +- }, +- { +- "|": [ +- "syn", +- "fin" +- ] +- } +- ] +- }, +- "op": "==", +- "right": { +- "|": [ +- "syn", +- "fin" +- ] +- } +- } +- } +-] +- + # tcp flags & (fin | syn | rst | psh | ack | urg | ecn | cwr) == fin | syn | rst | psh | ack | urg | ecn | cwr + [ + { diff --git a/0057-tests-py-Drop-duplicate-test-from-inet-vxlan.t.patch b/0057-tests-py-Drop-duplicate-test-from-inet-vxlan.t.patch new file mode 100644 index 0000000..6d79538 --- /dev/null +++ b/0057-tests-py-Drop-duplicate-test-from-inet-vxlan.t.patch @@ -0,0 +1,79 @@ +From 1f8f043819ce263f4462ee43c0c8bb89cd238be9 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:02 +0200 +Subject: [PATCH] tests: py: Drop duplicate test from inet/vxlan.t + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit bc66b02a6bb86a71d0a42ea7851f9aeea20edf1c + +commit bc66b02a6bb86a71d0a42ea7851f9aeea20edf1c +Author: Phil Sutter +Date: Wed Aug 13 14:38:22 2025 +0200 + + tests: py: Drop duplicate test from inet/vxlan.t + + The test was duplicate since day 1. The duplicate JSON equivalent was + added later (semi-automated), remove it as well. + + Fixes: df81baa4c2bef ("tests: py: add vxlan tests") + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + tests/py/inet/vxlan.t | 1 - + tests/py/inet/vxlan.t.json | 29 ----------------------------- + 2 files changed, 30 deletions(-) + +diff --git a/tests/py/inet/vxlan.t b/tests/py/inet/vxlan.t +index 10cdb7a..b6db0fe 100644 +--- a/tests/py/inet/vxlan.t ++++ b/tests/py/inet/vxlan.t +@@ -17,7 +17,6 @@ udp dport 4789 vxlan icmp type echo-reply;ok + udp dport 4789 vxlan ether saddr 62:87:4d:d6:19:05;ok + udp dport 4789 vxlan vlan id 10;ok + udp dport 4789 vxlan ip dscp 0x02;ok +-udp dport 4789 vxlan ip dscp 0x02;ok + udp dport 4789 vxlan ip saddr . vxlan ip daddr { 1.2.3.4 . 4.3.2.1 };ok + + udp dport 4789 vxlan ip saddr set 1.2.3.4;fail +diff --git a/tests/py/inet/vxlan.t.json b/tests/py/inet/vxlan.t.json +index 91b3d29..3c147cb 100644 +--- a/tests/py/inet/vxlan.t.json ++++ b/tests/py/inet/vxlan.t.json +@@ -264,35 +264,6 @@ + } + ] + +-# udp dport 4789 vxlan ip dscp 0x02 +-[ +- { +- "match": { +- "left": { +- "payload": { +- "field": "dport", +- "protocol": "udp" +- } +- }, +- "op": "==", +- "right": 4789 +- } +- }, +- { +- "match": { +- "left": { +- "payload": { +- "field": "dscp", +- "protocol": "ip", +- "tunnel": "vxlan" +- } +- }, +- "op": "==", +- "right": 2 +- } +- } +-] +- + # udp dport 4789 vxlan ip saddr . vxlan ip daddr { 1.2.3.4 . 4.3.2.1 } + [ + { diff --git a/0058-tests-py-Drop-stale-entry-from-ip-snat.t.json.patch b/0058-tests-py-Drop-stale-entry-from-ip-snat.t.json.patch new file mode 100644 index 0000000..b6b36d6 --- /dev/null +++ b/0058-tests-py-Drop-stale-entry-from-ip-snat.t.json.patch @@ -0,0 +1,70 @@ +From 4d71def249ec952bc0767061e5455244d7b1c823 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:02 +0200 +Subject: [PATCH] tests: py: Drop stale entry from ip/snat.t.json + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 71d90776fc56b14250f2306c4e19b50c23bd3ae3 + +commit 71d90776fc56b14250f2306c4e19b50c23bd3ae3 +Author: Phil Sutter +Date: Wed Aug 13 15:03:29 2025 +0200 + + tests: py: Drop stale entry from ip/snat.t.json + + The test syntax was changed, but the respective JSON equivalent remained + in place. + + Fixes: 9b169bfc650eb ("src: remove STMT_NAT_F_INTERVAL flags and interval keyword") + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + tests/py/ip/snat.t.json | 33 --------------------------------- + 1 file changed, 33 deletions(-) + +diff --git a/tests/py/ip/snat.t.json b/tests/py/ip/snat.t.json +index 967560e..60bd093 100644 +--- a/tests/py/ip/snat.t.json ++++ b/tests/py/ip/snat.t.json +@@ -283,39 +283,6 @@ + } + ] + +-# snat ip interval to ip saddr map { 10.141.11.4 : 192.168.2.2-192.168.2.4 } +-[ +- { +- "snat": { +- "addr": { +- "map": { +- "data": { +- "set": [ +- [ +- "10.141.11.4", +- { +- "range": [ +- "192.168.2.2", +- "192.168.2.4" +- ] +- } +- ] +- ] +- }, +- "key": { +- "payload": { +- "field": "saddr", +- "protocol": "ip" +- } +- } +- } +- }, +- "family": "ip", +- "type_flags": "interval" +- } +- } +-] +- + # snat ip prefix to ip saddr map { 10.141.11.0/24 : 192.168.2.0/24 } + [ + { diff --git a/0059-tests-py-Drop-stale-entries-from-ip6-ct-meta-.t.json.patch b/0059-tests-py-Drop-stale-entries-from-ip6-ct-meta-.t.json.patch new file mode 100644 index 0000000..6125af7 --- /dev/null +++ b/0059-tests-py-Drop-stale-entries-from-ip6-ct-meta-.t.json.patch @@ -0,0 +1,268 @@ +From 21df45dace5eafc3bab216e049357f712abf690a Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:02 +0200 +Subject: [PATCH] tests: py: Drop stale entries from ip6/{ct,meta}.t.json + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 3be0bbe9a5ba21a3f6e3baf051c3aaadc393c587 + +commit 3be0bbe9a5ba21a3f6e3baf051c3aaadc393c587 +Author: Phil Sutter +Date: Wed Aug 13 15:50:54 2025 +0200 + + tests: py: Drop stale entries from ip6/{ct,meta}.t.json + + Looks like these were added by accident, fixed commit did not add these + test cases. + + Fixes: 8221d86e616bd ("tests: py: add test-cases for ct and packet mark payload expressions") + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + tests/py/ip6/ct.t.json | 164 --------------------------------------- + tests/py/ip6/meta.t.json | 58 -------------- + 2 files changed, 222 deletions(-) + +diff --git a/tests/py/ip6/ct.t.json b/tests/py/ip6/ct.t.json +index 7d8c88b..a7511b9 100644 +--- a/tests/py/ip6/ct.t.json ++++ b/tests/py/ip6/ct.t.json +@@ -1,167 +1,3 @@ +-# ct mark set ip6 dscp lshift 2 or 0x10 +-[ +- { +- "mangle": { +- "key": { +- "ct": { +- "key": "mark" +- } +- }, +- "value": { +- "|": [ +- { +- "<<": [ +- { +- "payload": { +- "field": "dscp", +- "protocol": "ip6" +- } +- }, +- 2 +- ] +- }, +- 16 +- ] +- } +- } +- } +-] +- +-# ct mark set ip6 dscp lshift 26 or 0x10 +-[ +- { +- "mangle": { +- "key": { +- "ct": { +- "key": "mark" +- } +- }, +- "value": { +- "|": [ +- { +- "<<": [ +- { +- "payload": { +- "field": "dscp", +- "protocol": "ip6" +- } +- }, +- 26 +- ] +- }, +- 16 +- ] +- } +- } +- } +-] +- +-# ct mark set ip6 dscp << 2 | 0x10 +-[ +- { +- "mangle": { +- "key": { +- "ct": { +- "key": "mark" +- } +- }, +- "value": { +- "|": [ +- { +- "<<": [ +- { +- "payload": { +- "field": "dscp", +- "protocol": "ip6" +- } +- }, +- 2 +- ] +- }, +- 16 +- ] +- } +- } +- } +-] +- +-# ct mark set ip6 dscp << 26 | 0x10 +-[ +- { +- "mangle": { +- "key": { +- "ct": { +- "key": "mark" +- } +- }, +- "value": { +- "|": [ +- { +- "<<": [ +- { +- "payload": { +- "field": "dscp", +- "protocol": "ip6" +- } +- }, +- 26 +- ] +- }, +- 16 +- ] +- } +- } +- } +-] +- +-# ct mark set ip6 dscp | 0x04 +-[ +- { +- "mangle": { +- "key": { +- "ct": { +- "key": "mark" +- } +- }, +- "value": { +- "|": [ +- { +- "payload": { +- "field": "dscp", +- "protocol": "ip6" +- } +- }, +- 4 +- ] +- } +- } +- } +-] +- +-# ct mark set ip6 dscp | 0xff000000 +-[ +- { +- "mangle": { +- "key": { +- "ct": { +- "key": "mark" +- } +- }, +- "value": { +- "|": [ +- { +- "payload": { +- "field": "dscp", +- "protocol": "ip6" +- } +- }, +- 4278190080 +- ] +- } +- } +- } +-] +- + # ct mark set ip6 dscp << 2 | 0x10 + [ + { +diff --git a/tests/py/ip6/meta.t.json b/tests/py/ip6/meta.t.json +index 1a2394d..87251f0 100644 +--- a/tests/py/ip6/meta.t.json ++++ b/tests/py/ip6/meta.t.json +@@ -195,64 +195,6 @@ + } + ] + +-# meta mark set ip6 dscp lshift 2 or 0x10 +-[ +- { +- "mangle": { +- "key": { +- "meta": { +- "key": "mark" +- } +- }, +- "value": { +- "|": [ +- { +- "<<": [ +- { +- "payload": { +- "field": "dscp", +- "protocol": "ip6" +- } +- }, +- 2 +- ] +- }, +- 16 +- ] +- } +- } +- } +-] +- +-# meta mark set ip6 dscp lshift 26 or 0x10 +-[ +- { +- "mangle": { +- "key": { +- "meta": { +- "key": "mark" +- } +- }, +- "value": { +- "|": [ +- { +- "<<": [ +- { +- "payload": { +- "field": "dscp", +- "protocol": "ip6" +- } +- }, +- 26 +- ] +- }, +- 16 +- ] +- } +- } +- } +-] +- + # meta mark set ip6 dscp << 2 | 0x10 + [ + { diff --git a/0060-tests-py-Drop-stale-entry-from-ip-snat.t.payload.patch b/0060-tests-py-Drop-stale-entry-from-ip-snat.t.payload.patch new file mode 100644 index 0000000..136915b --- /dev/null +++ b/0060-tests-py-Drop-stale-entry-from-ip-snat.t.payload.patch @@ -0,0 +1,48 @@ +From 0e3d6eca63a637e22844a3461a06196fc0d00b66 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:02 +0200 +Subject: [PATCH] tests: py: Drop stale entry from ip/snat.t.payload + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit c3c60ac84a4f64a98f9c2c22d9032d177ae6a212 + +commit c3c60ac84a4f64a98f9c2c22d9032d177ae6a212 +Author: Phil Sutter +Date: Wed Aug 13 16:06:46 2025 +0200 + + tests: py: Drop stale entry from ip/snat.t.payload + + This payload actually belongs to ip/dnat.t.payload, fixed commit added + it to the wrong file. + + Fixes: 8f3048954d40d ("evaluate: postpone transport protocol match check after nat expression evaluation") + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + tests/py/ip/snat.t.payload | 14 -------------- + 1 file changed, 14 deletions(-) + +diff --git a/tests/py/ip/snat.t.payload b/tests/py/ip/snat.t.payload +index 71a5e2f..9a59b71 100644 +--- a/tests/py/ip/snat.t.payload ++++ b/tests/py/ip/snat.t.payload +@@ -138,17 +138,3 @@ ip + [ payload load 2b @ transport header + 2 => reg 9 ] + [ lookup reg 1 set __map%d dreg 1 ] + [ nat snat ip addr_min reg 1 proto_min reg 9 ] +- +-# ip daddr 192.168.0.1 dnat to tcp dport map { 443 : 10.141.10.4 . 8443, 80 : 10.141.10.4 . 8080 } +-__map%d x b size 2 +-__map%d x 0 +- element 0000bb01 : 040a8d0a 0000fb20 0 [end] element 00005000 : 040a8d0a 0000901f 0 [end] +-ip +- [ payload load 4b @ network header + 16 => reg 1 ] +- [ cmp eq reg 1 0x0100a8c0 ] +- [ meta load l4proto => reg 1 ] +- [ cmp eq reg 1 0x00000006 ] +- [ payload load 2b @ transport header + 2 => reg 1 ] +- [ lookup reg 1 set __map%d dreg 1 ] +- [ nat dnat ip addr_min reg 1 proto_min reg 9 ] +- diff --git a/0061-tests-py-Fix-tests-added-for-icmpv6-taddr-support.patch b/0061-tests-py-Fix-tests-added-for-icmpv6-taddr-support.patch new file mode 100644 index 0000000..e05a439 --- /dev/null +++ b/0061-tests-py-Fix-tests-added-for-icmpv6-taddr-support.patch @@ -0,0 +1,155 @@ +From 16de7abf588d74b78d9b8dc738ba2960ab90a3d2 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:02 +0200 +Subject: [PATCH] tests: py: Fix tests added for 'icmpv6 taddr' support + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit ef8b8ef36f80a2bfcc9df35cccb61ab490a8d6e7 + +commit ef8b8ef36f80a2bfcc9df35cccb61ab490a8d6e7 +Author: Phil Sutter +Date: Wed Aug 13 16:14:08 2025 +0200 + + tests: py: Fix tests added for 'icmpv6 taddr' support + + There was a duplicate test, also stored JSON equivalents should match + input as much as possible. The expected deviation in output (just like + with standard syntax) is stored in the .json.output file instead. + + Fixes: 2e86f45d0260a ("icmpv6: Allow matching target address in NS/NA, redirect and MLD") + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + tests/py/ip6/icmpv6.t | 2 -- + tests/py/ip6/icmpv6.t.json | 58 ------------------------------- + tests/py/ip6/icmpv6.t.json.output | 36 +++++++++++++++++++ + 3 files changed, 36 insertions(+), 60 deletions(-) + +diff --git a/tests/py/ip6/icmpv6.t b/tests/py/ip6/icmpv6.t +index 35dad2b..00e6661 100644 +--- a/tests/py/ip6/icmpv6.t ++++ b/tests/py/ip6/icmpv6.t +@@ -91,8 +91,6 @@ icmpv6 type nd-neighbor-solicit icmpv6 taddr 2001:db8::133;ok + icmpv6 type nd-neighbor-advert icmpv6 taddr 2001:db8::133;ok + icmpv6 taddr 2001:db8::133;ok;icmpv6 type { mld-listener-query, mld-listener-report, mld-listener-done, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect} icmpv6 taddr 2001:db8::133 + +-icmpv6 taddr 2001:db8::133;ok;icmpv6 type { mld-listener-query, mld-listener-report, mld-listener-done, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect} icmpv6 taddr 2001:db8::133 +- + icmpv6 type { mld-listener-query, mld-listener-report, mld-listener-done, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect} icmpv6 taddr 2001:db8::133;ok + icmpv6 type { nd-neighbor-solicit, nd-neighbor-advert } icmpv6 taddr 2001:db8::133;ok + icmpv6 daddr 2001:db8::133;ok +diff --git a/tests/py/ip6/icmpv6.t.json b/tests/py/ip6/icmpv6.t.json +index 224a8e8..b6f93b2 100644 +--- a/tests/py/ip6/icmpv6.t.json ++++ b/tests/py/ip6/icmpv6.t.json +@@ -1250,64 +1250,6 @@ + + # icmpv6 taddr 2001:db8::133 + [ +- { +- "match": { +- "left": { +- "payload": { +- "field": "type", +- "protocol": "icmpv6" +- } +- }, +- "op": "==", +- "right": { +- "set": [ +- "mld-listener-query", +- "mld-listener-report", +- "mld-listener-done", +- "nd-neighbor-solicit", +- "nd-neighbor-advert", +- "nd-redirect" +- ] +- } +- } +- }, +- { +- "match": { +- "left": { +- "payload": { +- "field": "taddr", +- "protocol": "icmpv6" +- } +- }, +- "op": "==", +- "right": "2001:db8::133" +- } +- } +-] +- +-# icmpv6 taddr 2001:db8::133 +-[ +- { +- "match": { +- "left": { +- "payload": { +- "field": "type", +- "protocol": "icmpv6" +- } +- }, +- "op": "==", +- "right": { +- "set": [ +- "mld-listener-query", +- "mld-listener-report", +- "mld-listener-done", +- "nd-neighbor-solicit", +- "nd-neighbor-advert", +- "nd-redirect" +- ] +- } +- } +- }, + { + "match": { + "left": { +diff --git a/tests/py/ip6/icmpv6.t.json.output b/tests/py/ip6/icmpv6.t.json.output +index 7b8f5c1..db25133 100644 +--- a/tests/py/ip6/icmpv6.t.json.output ++++ b/tests/py/ip6/icmpv6.t.json.output +@@ -758,3 +758,39 @@ + } + ] + ++# icmpv6 taddr 2001:db8::133 ++[ ++ { ++ "match": { ++ "left": { ++ "payload": { ++ "field": "type", ++ "protocol": "icmpv6" ++ } ++ }, ++ "op": "==", ++ "right": { ++ "set": [ ++ "mld-listener-query", ++ "mld-listener-report", ++ "mld-listener-done", ++ "nd-neighbor-solicit", ++ "nd-neighbor-advert", ++ "nd-redirect" ++ ] ++ } ++ } ++ }, ++ { ++ "match": { ++ "left": { ++ "payload": { ++ "field": "taddr", ++ "protocol": "icmpv6" ++ } ++ }, ++ "op": "==", ++ "right": "2001:db8::133" ++ } ++ } ++] diff --git a/0062-segtree-incorrect-type-when-aggregating-concatenated.patch b/0062-segtree-incorrect-type-when-aggregating-concatenated.patch new file mode 100644 index 0000000..0b67864 --- /dev/null +++ b/0062-segtree-incorrect-type-when-aggregating-concatenated.patch @@ -0,0 +1,44 @@ +From 3ab9ef7229ebe3cd66da95e3aabf474342f9eab9 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:02 +0200 +Subject: [PATCH] segtree: incorrect type when aggregating concatenated set + ranges + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit c44995e31ba60c4823bb81fecd29020452b9e702 + +commit c44995e31ba60c4823bb81fecd29020452b9e702 +Author: Pablo Neira Ayuso +Date: Wed Aug 13 15:19:23 2025 +0200 + + segtree: incorrect type when aggregating concatenated set ranges + + Uncovered by the compound_expr_remove() replacement by type safe function + coming after this patch. + + Add expression to the concatenation which is reachable via expr_value(). + + This bug is subtle, I could not spot any reproducible buggy behaviour + when using the wrong type when running the existing tests. + + Fixes: 8ac2f3b2fca3 ("src: Add support for concatenated set ranges") + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/segtree.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/segtree.c b/src/segtree.c +index c4029f9..a3a0fb4 100644 +--- a/src/segtree.c ++++ b/src/segtree.c +@@ -447,7 +447,7 @@ next: + mpz_clear(range); + + r2 = list_entry(r2_next, typeof(*r2), list); +- compound_expr_remove(start, r1); ++ compound_expr_remove(expr_value(start), r1); + + if (free_r1) + expr_free(r1); diff --git a/0063-src-ensure-chain-policy-evaluation-when-specified.patch b/0063-src-ensure-chain-policy-evaluation-when-specified.patch new file mode 100644 index 0000000..38e95a2 --- /dev/null +++ b/0063-src-ensure-chain-policy-evaluation-when-specified.patch @@ -0,0 +1,154 @@ +From 2970dcfbcb714b5e64d114229c9b46cbd9183e9d Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:02 +0200 +Subject: [PATCH] src: ensure chain policy evaluation when specified + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 01277922fede9fef8aacf5cc871bfbd55bbd78ef + +commit 01277922fede9fef8aacf5cc871bfbd55bbd78ef +Author: Pablo Neira Ayuso +Date: Sun Aug 17 21:01:30 2025 +0200 + + src: ensure chain policy evaluation when specified + + Set on CHAIN_F_BASECHAIN when policy is specified in chain, otherwise + chain priority is not evaluated. + + Toggling this flag requires needs three adjustments to work though: + + 1) chain_evaluate() needs skip evaluation of hook name and priority if + not specified to allow for updating the default chain policy, e.g. + + chain ip x y { policy accept; } + + 2) update netlink bytecode generation for chain to skip NFTA_CHAIN_HOOK + so update path is exercised in the kernel. + + 3) error reporting needs to check if basechain priority and type is + set on, otherwise skip further hints. + + Fixes: acdfae9c3126 ("src: allow to specify the default policy for base chains") + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/cmd.c | 3 +++ + src/evaluate.c | 27 ++++++++++--------- + src/mnl.c | 8 ++++-- + src/parser_bison.y | 1 + + .../bogons/nft-f/basechain_bad_policy | 2 ++ + .../bogons/nft-f/unexisting_chain_set_policy | 5 ++++ + 6 files changed, 32 insertions(+), 14 deletions(-) + create mode 100644 tests/shell/testcases/bogons/nft-f/basechain_bad_policy + create mode 100644 tests/shell/testcases/bogons/nft-f/unexisting_chain_set_policy + +diff --git a/src/cmd.c b/src/cmd.c +index 68c476c..f59e30c 100644 +--- a/src/cmd.c ++++ b/src/cmd.c +@@ -264,6 +264,9 @@ static int nft_cmd_chain_error(struct netlink_ctx *ctx, struct cmd *cmd, + if (!(chain->flags & CHAIN_F_BASECHAIN)) + break; + ++ if (!chain->priority.expr || !chain->type.str) ++ break; ++ + mpz_export_data(&priority, chain->priority.expr->value, + BYTEORDER_HOST_ENDIAN, sizeof(int)); + if (priority <= -200 && !strcmp(chain->type.str, "nat")) +diff --git a/src/evaluate.c b/src/evaluate.c +index 5012dd2..922821b 100644 +--- a/src/evaluate.c ++++ b/src/evaluate.c +@@ -5070,18 +5070,21 @@ static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain) + } + + if (chain->flags & CHAIN_F_BASECHAIN) { +- chain->hook.num = str2hooknum(chain->handle.family, +- chain->hook.name); +- if (chain->hook.num == NF_INET_NUMHOOKS) +- return __stmt_binary_error(ctx, &chain->hook.loc, NULL, +- "The %s family does not support this hook", +- family2str(chain->handle.family)); +- +- if (!evaluate_priority(ctx, &chain->priority, +- chain->handle.family, chain->hook.num)) +- return __stmt_binary_error(ctx, &chain->priority.loc, NULL, +- "invalid priority expression %s in this context.", +- expr_name(chain->priority.expr)); ++ if (chain->hook.name) { ++ chain->hook.num = str2hooknum(chain->handle.family, ++ chain->hook.name); ++ if (chain->hook.num == NF_INET_NUMHOOKS) ++ return __stmt_binary_error(ctx, &chain->hook.loc, NULL, ++ "The %s family does not support this hook", ++ family2str(chain->handle.family)); ++ } ++ if (chain->priority.expr) { ++ if (!evaluate_priority(ctx, &chain->priority, ++ chain->handle.family, chain->hook.num)) ++ return __stmt_binary_error(ctx, &chain->priority.loc, NULL, ++ "invalid priority expression %s in this context.", ++ expr_name(chain->priority.expr)); ++ } + if (chain->policy) { + expr_set_context(&ctx->ectx, &policy_type, + NFT_NAME_MAXLEN * BITS_PER_BYTE); +diff --git a/src/mnl.c b/src/mnl.c +index c62b140..6e8066a 100644 +--- a/src/mnl.c ++++ b/src/mnl.c +@@ -875,7 +875,9 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd, + mnl_attr_put_strz(nlh, NFTA_CHAIN_TYPE, cmd->chain->type.str); + } + +- nest = mnl_attr_nest_start(nlh, NFTA_CHAIN_HOOK); ++ if (cmd->chain->type.str || ++ (cmd->chain && cmd->chain->dev_expr)) ++ nest = mnl_attr_nest_start(nlh, NFTA_CHAIN_HOOK); + + if (cmd->chain->type.str) { + mnl_attr_put_u32(nlh, NFTA_HOOK_HOOKNUM, htonl(cmd->chain->hook.num)); +@@ -887,7 +889,9 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd, + if (cmd->chain && cmd->chain->dev_expr) + mnl_nft_chain_devs_build(nlh, cmd); + +- mnl_attr_nest_end(nlh, nest); ++ if (cmd->chain->type.str || ++ (cmd->chain && cmd->chain->dev_expr)) ++ mnl_attr_nest_end(nlh, nest); + } + + nftnl_chain_free(nlc); +diff --git a/src/parser_bison.y b/src/parser_bison.y +index a6a517f..c3086c8 100644 +--- a/src/parser_bison.y ++++ b/src/parser_bison.y +@@ -2756,6 +2756,7 @@ policy_spec : POLICY policy_expr close_scope_policy + } + $0->policy = $2; + $0->policy->location = @$; ++ $0->flags |= CHAIN_F_BASECHAIN; + } + ; + +diff --git a/tests/shell/testcases/bogons/nft-f/basechain_bad_policy b/tests/shell/testcases/bogons/nft-f/basechain_bad_policy +new file mode 100644 +index 0000000..998e423 +--- /dev/null ++++ b/tests/shell/testcases/bogons/nft-f/basechain_bad_policy +@@ -0,0 +1,2 @@ ++define MY_POLICY = deny ++table T { chain C { policy $MY_POLICY; };}; +diff --git a/tests/shell/testcases/bogons/nft-f/unexisting_chain_set_policy b/tests/shell/testcases/bogons/nft-f/unexisting_chain_set_policy +new file mode 100644 +index 0000000..0889559 +--- /dev/null ++++ b/tests/shell/testcases/bogons/nft-f/unexisting_chain_set_policy +@@ -0,0 +1,5 @@ ++table ip x { ++ chain y { ++ policy drop; ++ } ++} diff --git a/0064-json-Do-not-reduce-single-item-arrays-on-output.patch b/0064-json-Do-not-reduce-single-item-arrays-on-output.patch new file mode 100644 index 0000000..83c5fff --- /dev/null +++ b/0064-json-Do-not-reduce-single-item-arrays-on-output.patch @@ -0,0 +1,767 @@ +From c47e93f5fa3ca3204080a09edbf854a1c9d7cd64 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:02 +0200 +Subject: [PATCH] json: Do not reduce single-item arrays on output + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 5e492307c2c93b7eb654691ca87ddc7ad86640d8 +Conflicts: Dropped changes to non-existent test and dump files + +commit 5e492307c2c93b7eb654691ca87ddc7ad86640d8 +Author: Phil Sutter +Date: Tue Aug 12 17:31:47 2025 +0200 + + json: Do not reduce single-item arrays on output + + This is a partial revert of commit a740f2036ad0d ("json: Introduce + json_add_array_new()"), keeping the function but eliminating its primary + task which is to replace arrays of size 1 by their only item. While + support for this on input is convenient for users, it means extra casing + in JSON output parsers to cover for it. The minor reduction in output + size does not justify that. + + Fixes: a740f2036ad0d ("json: Introduce json_add_array_new()") + Closes: https://bugzilla.netfilter.org/show_bug.cgi?id=1806 + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/json.c | 8 +- + tests/py/any/log.t.json.output | 15 +++- + tests/py/any/queue.t.json.output | 117 +++++++++++++++++++++++++ + tests/py/inet/fib.t.json | 8 +- + tests/py/inet/fib.t.json.output | 87 ++++++++++++++++++- + tests/py/inet/snat.t.json.output | 22 +++++ + tests/py/inet/synproxy.t.json | 8 +- + tests/py/inet/synproxy.t.json.output | 8 +- + tests/py/ip/masquerade.t.json.output | 46 ++++++++++ + tests/py/ip/redirect.t.json.output | 118 ++++++++++++++++++++++++++ + tests/py/ip/snat.t.json.output | 8 +- + tests/py/ip6/masquerade.t.json.output | 46 ++++++++++ + tests/py/ip6/redirect.t.json.output | 70 +++++++++++++++ + 13 files changed, 536 insertions(+), 25 deletions(-) + create mode 100644 tests/py/inet/snat.t.json.output + +diff --git a/src/json.c b/src/json.c +index a2523a4..183047a 100644 +--- a/src/json.c ++++ b/src/json.c +@@ -53,14 +53,10 @@ static json_t *__nft_json_pack(unsigned int line, const char *fmt, ...) + + static void json_add_array_new(json_t *obj, const char *name, json_t *array) + { +- if (json_array_size(array) > 1) { ++ if (json_array_size(array)) + json_object_set_new(obj, name, array); +- } else { +- if (json_array_size(array)) +- json_object_set(obj, name, +- json_array_get(array, 0)); ++ else + json_decref(array); +- } + } + + static json_t *expr_print_json(const struct expr *expr, struct output_ctx *octx) +diff --git a/tests/py/any/log.t.json.output b/tests/py/any/log.t.json.output +index 051c448..bec70a3 100644 +--- a/tests/py/any/log.t.json.output ++++ b/tests/py/any/log.t.json.output +@@ -9,7 +9,20 @@ + [ + { + "log": { +- "flags": "all" ++ "flags": [ ++ "all" ++ ] ++ } ++ } ++] ++ ++# log flags all ++[ ++ { ++ "log": { ++ "flags": [ ++ "all" ++ ] + } + } + ] +diff --git a/tests/py/any/queue.t.json.output b/tests/py/any/queue.t.json.output +index 1104d76..ea37223 100644 +--- a/tests/py/any/queue.t.json.output ++++ b/tests/py/any/queue.t.json.output +@@ -7,3 +7,120 @@ + } + ] + ++# queue num 4-5 fanout ++[ ++ { ++ "queue": { ++ "flags": [ ++ "fanout" ++ ], ++ "num": { ++ "range": [ ++ 4, ++ 5 ++ ] ++ } ++ } ++ } ++] ++ ++# queue num 4-5 bypass ++[ ++ { ++ "queue": { ++ "flags": [ ++ "bypass" ++ ], ++ "num": { ++ "range": [ ++ 4, ++ 5 ++ ] ++ } ++ } ++ } ++] ++ ++# queue flags bypass to numgen inc mod 65536 ++[ ++ { ++ "queue": { ++ "flags": [ ++ "bypass" ++ ], ++ "num": { ++ "numgen": { ++ "mod": 65536, ++ "mode": "inc", ++ "offset": 0 ++ } ++ } ++ } ++ } ++] ++ ++# queue flags bypass to 65535 ++[ ++ { ++ "queue": { ++ "flags": [ ++ "bypass" ++ ], ++ "num": 65535 ++ } ++ } ++] ++ ++# queue flags bypass to 1-65535 ++[ ++ { ++ "queue": { ++ "flags": [ ++ "bypass" ++ ], ++ "num": { ++ "range": [ ++ 1, ++ 65535 ++ ] ++ } ++ } ++ } ++] ++ ++# queue flags bypass to oifname map { "eth0" : 0, "ppp0" : 2, "eth1" : 2 } ++[ ++ { ++ "queue": { ++ "flags": [ ++ "bypass" ++ ], ++ "num": { ++ "map": { ++ "data": { ++ "set": [ ++ [ ++ "eth0", ++ 0 ++ ], ++ [ ++ "ppp0", ++ 2 ++ ], ++ [ ++ "eth1", ++ 2 ++ ] ++ ] ++ }, ++ "key": { ++ "meta": { ++ "key": "oifname" ++ } ++ } ++ } ++ } ++ } ++ } ++] ++ +diff --git a/tests/py/inet/fib.t.json b/tests/py/inet/fib.t.json +index 14a6249..2bfe4f7 100644 +--- a/tests/py/inet/fib.t.json ++++ b/tests/py/inet/fib.t.json +@@ -100,9 +100,7 @@ + "match": { + "left": { + "fib": { +- "flags": [ +- "daddr" +- ], ++ "flags": "daddr", + "result": "check" + } + }, +@@ -118,9 +116,7 @@ + "match": { + "left": { + "fib": { +- "flags": [ +- "daddr" +- ], ++ "flags": "daddr", + "result": "check" + } + }, +diff --git a/tests/py/inet/fib.t.json.output b/tests/py/inet/fib.t.json.output +index e8d0166..d3396dd 100644 +--- a/tests/py/inet/fib.t.json.output ++++ b/tests/py/inet/fib.t.json.output +@@ -43,7 +43,9 @@ + "match": { + "left": { + "fib": { +- "flags": "daddr", ++ "flags": [ ++ "daddr" ++ ], + "result": "check" + } + }, +@@ -59,7 +61,9 @@ + "match": { + "left": { + "fib": { +- "flags": "daddr", ++ "flags": [ ++ "daddr" ++ ], + "result": "check" + } + }, +@@ -69,3 +73,82 @@ + } + ] + ++# fib daddr check vmap { missing : drop, exists : accept } ++[ ++ { ++ "vmap": { ++ "data": { ++ "set": [ ++ [ ++ false, ++ { ++ "drop": null ++ } ++ ], ++ [ ++ true, ++ { ++ "accept": null ++ } ++ ] ++ ] ++ }, ++ "key": { ++ "fib": { ++ "flags": [ ++ "daddr" ++ ], ++ "result": "check" ++ } ++ } ++ } ++ } ++] ++ ++# meta mark set fib daddr check . ct mark map { exists . 0x00000000 : 0x00000001 } ++[ ++ { ++ "mangle": { ++ "key": { ++ "meta": { ++ "key": "mark" ++ } ++ }, ++ "value": { ++ "map": { ++ "data": { ++ "set": [ ++ [ ++ { ++ "concat": [ ++ true, ++ 0 ++ ] ++ }, ++ 1 ++ ] ++ ] ++ }, ++ "key": { ++ "concat": [ ++ { ++ "fib": { ++ "flags": [ ++ "daddr" ++ ], ++ "result": "check" ++ } ++ }, ++ { ++ "ct": { ++ "key": "mark" ++ } ++ } ++ ] ++ } ++ } ++ } ++ } ++ } ++] ++ +diff --git a/tests/py/inet/snat.t.json.output b/tests/py/inet/snat.t.json.output +new file mode 100644 +index 0000000..5b95886 +--- /dev/null ++++ b/tests/py/inet/snat.t.json.output +@@ -0,0 +1,22 @@ ++# iifname "foo" masquerade random ++[ ++ { ++ "match": { ++ "left": { ++ "meta": { ++ "key": "iifname" ++ } ++ }, ++ "op": "==", ++ "right": "foo" ++ } ++ }, ++ { ++ "masquerade": { ++ "flags": [ ++ "random" ++ ] ++ } ++ } ++] ++ +diff --git a/tests/py/inet/synproxy.t.json b/tests/py/inet/synproxy.t.json +index 1dd85a6..b3cec09 100644 +--- a/tests/py/inet/synproxy.t.json ++++ b/tests/py/inet/synproxy.t.json +@@ -19,9 +19,7 @@ + [ + { + "synproxy": { +- "flags": [ +- "timestamp" +- ] ++ "flags": "timestamp" + } + } + ] +@@ -56,9 +54,7 @@ + [ + { + "synproxy": { +- "flags": [ +- "sack-perm" +- ] ++ "flags": "sack-perm" + } + } + ] +diff --git a/tests/py/inet/synproxy.t.json.output b/tests/py/inet/synproxy.t.json.output +index e32cdfb..a1d81bf 100644 +--- a/tests/py/inet/synproxy.t.json.output ++++ b/tests/py/inet/synproxy.t.json.output +@@ -2,7 +2,9 @@ + [ + { + "synproxy": { +- "flags": "timestamp" ++ "flags": [ ++ "timestamp" ++ ] + } + } + ] +@@ -11,7 +13,9 @@ + [ + { + "synproxy": { +- "flags": "sack-perm" ++ "flags": [ ++ "sack-perm" ++ ] + } + } + ] +diff --git a/tests/py/ip/masquerade.t.json.output b/tests/py/ip/masquerade.t.json.output +index 58e7e29..8ca5a42 100644 +--- a/tests/py/ip/masquerade.t.json.output ++++ b/tests/py/ip/masquerade.t.json.output +@@ -121,3 +121,49 @@ + } + ] + ++# udp dport 53 masquerade random ++[ ++ { ++ "match": { ++ "left": { ++ "payload": { ++ "field": "dport", ++ "protocol": "udp" ++ } ++ }, ++ "op": "==", ++ "right": 53 ++ } ++ }, ++ { ++ "masquerade": { ++ "flags": [ ++ "random" ++ ] ++ } ++ } ++] ++ ++# udp dport 53 masquerade persistent ++[ ++ { ++ "match": { ++ "left": { ++ "payload": { ++ "field": "dport", ++ "protocol": "udp" ++ } ++ }, ++ "op": "==", ++ "right": 53 ++ } ++ }, ++ { ++ "masquerade": { ++ "flags": [ ++ "persistent" ++ ] ++ } ++ } ++] ++ +diff --git a/tests/py/ip/redirect.t.json.output b/tests/py/ip/redirect.t.json.output +index 4646c60..09f1e48 100644 +--- a/tests/py/ip/redirect.t.json.output ++++ b/tests/py/ip/redirect.t.json.output +@@ -1,3 +1,49 @@ ++# udp dport 53 redirect random ++[ ++ { ++ "match": { ++ "left": { ++ "payload": { ++ "field": "dport", ++ "protocol": "udp" ++ } ++ }, ++ "op": "==", ++ "right": 53 ++ } ++ }, ++ { ++ "redirect": { ++ "flags": [ ++ "random" ++ ] ++ } ++ } ++] ++ ++# udp dport 53 redirect persistent ++[ ++ { ++ "match": { ++ "left": { ++ "payload": { ++ "field": "dport", ++ "protocol": "udp" ++ } ++ }, ++ "op": "==", ++ "right": 53 ++ } ++ }, ++ { ++ "redirect": { ++ "flags": [ ++ "persistent" ++ ] ++ } ++ } ++] ++ + # udp dport 53 redirect random,persistent,fully-random + [ + { +@@ -144,3 +190,75 @@ + } + ] + ++# tcp dport 9128 redirect to :993 random ++[ ++ { ++ "match": { ++ "left": { ++ "payload": { ++ "field": "dport", ++ "protocol": "tcp" ++ } ++ }, ++ "op": "==", ++ "right": 9128 ++ } ++ }, ++ { ++ "redirect": { ++ "flags": [ ++ "random" ++ ], ++ "port": 993 ++ } ++ } ++] ++ ++# tcp dport 9128 redirect to :993 fully-random ++[ ++ { ++ "match": { ++ "left": { ++ "payload": { ++ "field": "dport", ++ "protocol": "tcp" ++ } ++ }, ++ "op": "==", ++ "right": 9128 ++ } ++ }, ++ { ++ "redirect": { ++ "flags": [ ++ "fully-random" ++ ], ++ "port": 993 ++ } ++ } ++] ++ ++# tcp dport 9128 redirect to :123 persistent ++[ ++ { ++ "match": { ++ "left": { ++ "payload": { ++ "field": "dport", ++ "protocol": "tcp" ++ } ++ }, ++ "op": "==", ++ "right": 9128 ++ } ++ }, ++ { ++ "redirect": { ++ "flags": [ ++ "persistent" ++ ], ++ "port": 123 ++ } ++ } ++] ++ +diff --git a/tests/py/ip/snat.t.json.output b/tests/py/ip/snat.t.json.output +index 2a99780..19eba25 100644 +--- a/tests/py/ip/snat.t.json.output ++++ b/tests/py/ip/snat.t.json.output +@@ -241,8 +241,12 @@ + } + }, + "family": "ip", +- "flags": "netmap", +- "type_flags": "prefix" ++ "flags": [ ++ "netmap" ++ ], ++ "type_flags": [ ++ "prefix" ++ ] + } + } + ] +diff --git a/tests/py/ip6/masquerade.t.json.output b/tests/py/ip6/masquerade.t.json.output +index 31d0cd9..21ed4f6 100644 +--- a/tests/py/ip6/masquerade.t.json.output ++++ b/tests/py/ip6/masquerade.t.json.output +@@ -96,3 +96,49 @@ + } + ] + ++# udp dport 53 masquerade random ++[ ++ { ++ "match": { ++ "left": { ++ "payload": { ++ "field": "dport", ++ "protocol": "udp" ++ } ++ }, ++ "op": "==", ++ "right": 53 ++ } ++ }, ++ { ++ "masquerade": { ++ "flags": [ ++ "random" ++ ] ++ } ++ } ++] ++ ++# udp dport 53 masquerade persistent ++[ ++ { ++ "match": { ++ "left": { ++ "payload": { ++ "field": "dport", ++ "protocol": "udp" ++ } ++ }, ++ "op": "==", ++ "right": 53 ++ } ++ }, ++ { ++ "masquerade": { ++ "flags": [ ++ "persistent" ++ ] ++ } ++ } ++] ++ +diff --git a/tests/py/ip6/redirect.t.json.output b/tests/py/ip6/redirect.t.json.output +index 0174cc7..69c7b03 100644 +--- a/tests/py/ip6/redirect.t.json.output ++++ b/tests/py/ip6/redirect.t.json.output +@@ -144,3 +144,73 @@ + } + ] + ++# udp dport 53 redirect random ++[ ++ { ++ "match": { ++ "left": { ++ "payload": { ++ "field": "dport", ++ "protocol": "udp" ++ } ++ }, ++ "op": "==", ++ "right": 53 ++ } ++ }, ++ { ++ "redirect": { ++ "flags": [ ++ "random" ++ ] ++ } ++ } ++] ++ ++# udp dport 53 redirect persistent ++[ ++ { ++ "match": { ++ "left": { ++ "payload": { ++ "field": "dport", ++ "protocol": "udp" ++ } ++ }, ++ "op": "==", ++ "right": 53 ++ } ++ }, ++ { ++ "redirect": { ++ "flags": [ ++ "persistent" ++ ] ++ } ++ } ++] ++ ++# tcp dport 9128 redirect to :993 random ++[ ++ { ++ "match": { ++ "left": { ++ "payload": { ++ "field": "dport", ++ "protocol": "tcp" ++ } ++ }, ++ "op": "==", ++ "right": 9128 ++ } ++ }, ++ { ++ "redirect": { ++ "flags": [ ++ "random" ++ ], ++ "port": 993 ++ } ++ } ++] ++ diff --git a/0065-fib-restore-JSON-output-for-relational-expressions.patch b/0065-fib-restore-JSON-output-for-relational-expressions.patch new file mode 100644 index 0000000..8911b21 --- /dev/null +++ b/0065-fib-restore-JSON-output-for-relational-expressions.patch @@ -0,0 +1,184 @@ +From 107eca5c205685f3ebf7cf1840563009eae0b56b Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:33 +0200 +Subject: [PATCH] fib: restore JSON output for relational expressions + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit dbb92d3137d9ed684cb450ab3c2aef881a4e6006 +Conflicts: Dropped changes to non-existent test and dump files + +commit dbb92d3137d9ed684cb450ab3c2aef881a4e6006 +Author: Pablo Neira Ayuso +Date: Tue Aug 19 11:23:42 2025 +0200 + + fib: restore JSON output for relational expressions + + JSON output for the fib expression changed: + + - "result": "check" + + "result": "oif" + + This breaks third party JSON parsers, revert this change for relational + expressions only via workaround until there are clear rules on how to + proceed with JSON schema updates. + + As for set and map statements, keep this new "check" result type since + it is not possible to peek on rhs in such case to guess if the + NFT_FIB_F_PRESENT flag needs to be set on. + + Closes: https://bugzilla.netfilter.org/show_bug.cgi?id=1806 + Fixes: f4b646032acf ("fib: allow to check if route exists in maps") + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + include/fib.h | 2 +- + src/fib.c | 7 ++++--- + src/json.c | 20 ++++++++++++++----- + tests/py/inet/fib.t.json | 4 ++-- + tests/py/inet/fib.t.json.output | 4 ++-- + tests/shell/testcases/transactions/0049huge_0 | 2 +- + 6 files changed, 25 insertions(+), 14 deletions(-) + +diff --git a/include/fib.h b/include/fib.h +index 07bb221..4e39ec9 100644 +--- a/include/fib.h ++++ b/include/fib.h +@@ -3,7 +3,7 @@ + + #include + +-extern const char *fib_result_str(const struct expr *expr); ++extern const char *fib_result_str(const struct expr *expr, bool check); + extern struct expr *fib_expr_alloc(const struct location *loc, + unsigned int flags, + unsigned int result); +diff --git a/src/fib.c b/src/fib.c +index aa75723..80ff292 100644 +--- a/src/fib.c ++++ b/src/fib.c +@@ -53,13 +53,14 @@ const struct datatype fib_addr_type = { + .sym_tbl = &addrtype_tbl, + }; + +-const char *fib_result_str(const struct expr *expr) ++const char *fib_result_str(const struct expr *expr, bool check) + { + enum nft_fib_result result = expr->fib.result; + uint32_t flags = expr->fib.flags; + + /* Exception: check if route exists. */ +- if (result == NFT_FIB_RESULT_OIF && ++ if (check && ++ result == NFT_FIB_RESULT_OIF && + flags & NFTA_FIB_F_PRESENT) + return "check"; + +@@ -95,7 +96,7 @@ static void fib_expr_print(const struct expr *expr, struct output_ctx *octx) + if (flags) + nft_print(octx, "0x%x", flags); + +- nft_print(octx, " %s", fib_result_str(expr)); ++ nft_print(octx, " %s", fib_result_str(expr, true)); + } + + static bool fib_expr_cmp(const struct expr *e1, const struct expr *e2) +diff --git a/src/json.c b/src/json.c +index 183047a..57b7664 100644 +--- a/src/json.c ++++ b/src/json.c +@@ -559,12 +559,21 @@ json_t *binop_expr_json(const struct expr *expr, struct output_ctx *octx) + expr_print_json(expr->right, octx)); + } + ++/* Workaround to retain backwards compatibility in fib output. */ ++#define __NFT_CTX_OUTPUT_RELATIONAL (1 << 30) ++ + json_t *relational_expr_json(const struct expr *expr, struct output_ctx *octx) + { +- return nft_json_pack("{s:{s:s, s:o, s:o}}", "match", +- "op", expr_op_symbols[expr->op] ? : "in", +- "left", expr_print_json(expr->left, octx), +- "right", expr_print_json(expr->right, octx)); ++ json_t *ret; ++ ++ octx->flags |= __NFT_CTX_OUTPUT_RELATIONAL; ++ ret = nft_json_pack("{s:{s:s, s:o, s:o}}", "match", ++ "op", expr_op_symbols[expr->op] ? : "in", ++ "left", expr_print_json(expr->left, octx), ++ "right", expr_print_json(expr->right, octx)); ++ octx->flags &= ~__NFT_CTX_OUTPUT_RELATIONAL; ++ ++ return ret; + } + + json_t *range_expr_json(const struct expr *expr, struct output_ctx *octx) +@@ -913,9 +922,10 @@ json_t *fib_expr_json(const struct expr *expr, struct output_ctx *octx) + { + const char *fib_flags[] = { "saddr", "daddr", "mark", "iif", "oif" }; + unsigned int flags = expr->fib.flags & ~NFTA_FIB_F_PRESENT; ++ bool check = !(octx->flags & __NFT_CTX_OUTPUT_RELATIONAL); + json_t *root; + +- root = nft_json_pack("{s:s}", "result", fib_result_str(expr)); ++ root = nft_json_pack("{s:s}", "result", fib_result_str(expr, check)); + + if (flags) { + json_t *tmp = json_array(); +diff --git a/tests/py/inet/fib.t.json b/tests/py/inet/fib.t.json +index 2bfe4f7..b6953c8 100644 +--- a/tests/py/inet/fib.t.json ++++ b/tests/py/inet/fib.t.json +@@ -101,7 +101,7 @@ + "left": { + "fib": { + "flags": "daddr", +- "result": "check" ++ "result": "oif" + } + }, + "op": "==", +@@ -117,7 +117,7 @@ + "left": { + "fib": { + "flags": "daddr", +- "result": "check" ++ "result": "oif" + } + }, + "op": "==", +diff --git a/tests/py/inet/fib.t.json.output b/tests/py/inet/fib.t.json.output +index d3396dd..18a13b3 100644 +--- a/tests/py/inet/fib.t.json.output ++++ b/tests/py/inet/fib.t.json.output +@@ -46,7 +46,7 @@ + "flags": [ + "daddr" + ], +- "result": "check" ++ "result": "oif" + } + }, + "op": "==", +@@ -64,7 +64,7 @@ + "flags": [ + "daddr" + ], +- "result": "check" ++ "result": "oif" + } + }, + "op": "==", +diff --git a/tests/shell/testcases/transactions/0049huge_0 b/tests/shell/testcases/transactions/0049huge_0 +index 90049b6..f66953c 100755 +--- a/tests/shell/testcases/transactions/0049huge_0 ++++ b/tests/shell/testcases/transactions/0049huge_0 +@@ -47,7 +47,7 @@ fi + $NFT flush ruleset + + RULESET='{"nftables": [{"metainfo": {"json_schema_version": 1}}, {"add": {"table": {"family": "inet", "name": "firewalld"}}}, {"add": {"table": {"family": "ip", "name": "firewalld"}}}, {"add": {"table": {"family": "ip6", "name": "firewalld"}}}, +-{"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PREROUTING", "type": "filter", "hook": "prerouting", "prio": -290}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PREROUTING_ZONES"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING", "expr": [{"jump": {"target": "raw_PREROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PREROUTING", "type": "filter", "hook": "prerouting", "prio": -140}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PREROUTING_ZONES"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PREROUTING", "expr": [{"jump": {"target": "mangle_PREROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PREROUTING", "type": "nat", "hook": "prerouting", "prio": -90}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PREROUTING_ZONES"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PREROUTING", "expr": [{"jump": {"target": "nat_PREROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POSTROUTING", "type": "nat", "hook": "postrouting", "prio": 110}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POSTROUTING_ZONES"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POSTROUTING", "expr": [{"jump": {"target": "nat_POSTROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PREROUTING", "type": "nat", "hook": "prerouting", "prio": -90}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PREROUTING_ZONES"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PREROUTING", "expr": [{"jump": {"target": "nat_PREROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POSTROUTING", "type": "nat", "hook": "postrouting", "prio": 110}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POSTROUTING_ZONES"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POSTROUTING", "expr": [{"jump": {"target": "nat_POSTROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_INPUT", "type": "filter", "hook": "input", "prio": 10}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FORWARD", "type": "filter", "hook": "forward", "prio": 10}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_OUTPUT", "type": "filter", "hook": "output", "prio": 10}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_INPUT_ZONES"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["established", "related"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"match": {"left": {"ct": {"key": "status"}}, "op": "in", "right": "dnat"}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "lo"}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"jump": {"target": "filter_INPUT_ZONES"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["invalid"]}}}, {"drop": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"reject": {"type": "icmpx", "expr": "admin-prohibited"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FORWARD_IN_ZONES"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FORWARD_OUT_ZONES"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["established", "related"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"match": {"left": {"ct": {"key": "status"}}, "op": "in", "right": "dnat"}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "lo"}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"jump": {"target": "filter_FORWARD_IN_ZONES"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"jump": {"target": "filter_FORWARD_OUT_ZONES"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["invalid"]}}}, {"drop": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"reject": {"type": "icmpx", "expr": "admin-prohibited"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_OUTPUT", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "lo"}}, {"accept": null}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING", "expr": [{"match": {"left": {"meta": {"key": "nfproto"}}, "op": "==", "right": "ipv6"}}, {"match": {"left": {"fib": {"flags": ["saddr", "iif"], "result": "check"}}, "op": "==", "right": false}}, {"drop": null}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING", "expr": [{"match": {"left": {"payload": {"protocol": "icmpv6", "field": "type"}}, "op": "==", "right": {"set": ["nd-router-advert", "nd-neighbor-solicit"]}}}, {"accept": null}]}}}, ++{"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PREROUTING", "type": "filter", "hook": "prerouting", "prio": -290}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PREROUTING_ZONES"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING", "expr": [{"jump": {"target": "raw_PREROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PREROUTING", "type": "filter", "hook": "prerouting", "prio": -140}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PREROUTING_ZONES"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PREROUTING", "expr": [{"jump": {"target": "mangle_PREROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PREROUTING", "type": "nat", "hook": "prerouting", "prio": -90}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PREROUTING_ZONES"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PREROUTING", "expr": [{"jump": {"target": "nat_PREROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POSTROUTING", "type": "nat", "hook": "postrouting", "prio": 110}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POSTROUTING_ZONES"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POSTROUTING", "expr": [{"jump": {"target": "nat_POSTROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PREROUTING", "type": "nat", "hook": "prerouting", "prio": -90}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PREROUTING_ZONES"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PREROUTING", "expr": [{"jump": {"target": "nat_PREROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POSTROUTING", "type": "nat", "hook": "postrouting", "prio": 110}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POSTROUTING_ZONES"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POSTROUTING", "expr": [{"jump": {"target": "nat_POSTROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_INPUT", "type": "filter", "hook": "input", "prio": 10}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FORWARD", "type": "filter", "hook": "forward", "prio": 10}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_OUTPUT", "type": "filter", "hook": "output", "prio": 10}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_INPUT_ZONES"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["established", "related"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"match": {"left": {"ct": {"key": "status"}}, "op": "in", "right": "dnat"}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "lo"}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"jump": {"target": "filter_INPUT_ZONES"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["invalid"]}}}, {"drop": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"reject": {"type": "icmpx", "expr": "admin-prohibited"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FORWARD_IN_ZONES"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FORWARD_OUT_ZONES"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["established", "related"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"match": {"left": {"ct": {"key": "status"}}, "op": "in", "right": "dnat"}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "lo"}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"jump": {"target": "filter_FORWARD_IN_ZONES"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"jump": {"target": "filter_FORWARD_OUT_ZONES"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["invalid"]}}}, {"drop": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"reject": {"type": "icmpx", "expr": "admin-prohibited"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_OUTPUT", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "lo"}}, {"accept": null}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING", "expr": [{"match": {"left": {"meta": {"key": "nfproto"}}, "op": "==", "right": "ipv6"}}, {"match": {"left": {"fib": {"flags": ["saddr", "iif"], "result": "oif"}}, "op": "==", "right": false}}, {"drop": null}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING", "expr": [{"match": {"left": {"payload": {"protocol": "icmpv6", "field": "type"}}, "op": "==", "right": {"set": ["nd-router-advert", "nd-neighbor-solicit"]}}}, {"accept": null}]}}}, + {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_OUTPUT", "index": 0, "expr": [{"match": {"left": {"payload": {"protocol": "ip6", "field": "daddr"}}, "op": "==", "right": {"set": [{"prefix": {"addr": "::0.0.0.0", "len": 96}}, {"prefix": {"addr": "::ffff:0.0.0.0", "len": 96}}, {"prefix": {"addr": "2002:0000::", "len": 24}}, {"prefix": {"addr": "2002:0a00::", "len": 24}}, {"prefix": {"addr": "2002:7f00::", "len": 24}}, {"prefix": {"addr": "2002:ac10::", "len": 28}}, {"prefix": {"addr": "2002:c0a8::", "len": 32}}, {"prefix": {"addr": "2002:a9fe::", "len": 32}}, {"prefix": {"addr": "2002:e000::", "len": 19}}]}}}, {"reject": {"type": "icmpv6", "expr": "addr-unreachable"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "index": 2, "expr": [{"match": {"left": {"payload": {"protocol": "ip6", "field": "daddr"}}, "op": "==", "right": {"set": [{"prefix": {"addr": "::0.0.0.0", "len": 96}}, {"prefix": {"addr": "::ffff:0.0.0.0", "len": 96}}, {"prefix": {"addr": "2002:0000::", "len": 24}}, {"prefix": {"addr": "2002:0a00::", "len": 24}}, {"prefix": {"addr": "2002:7f00::", "len": 24}}, {"prefix": {"addr": "2002:ac10::", "len": 28}}, {"prefix": {"addr": "2002:c0a8::", "len": 32}}, {"prefix": {"addr": "2002:a9fe::", "len": 32}}, {"prefix": {"addr": "2002:e000::", "len": 19}}]}}}, {"reject": {"type": "icmpv6", "expr": "addr-unreachable"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_public"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_public_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_public_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_public_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_public_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_public_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_public", "expr": [{"jump": {"target": "raw_PRE_public_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_public", "expr": [{"jump": {"target": "raw_PRE_public_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_public", "expr": [{"jump": {"target": "raw_PRE_public_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_public", "expr": [{"jump": {"target": "raw_PRE_public_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_public", "expr": [{"jump": {"target": "raw_PRE_public_post"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_public"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_public_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_public_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_public_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_public_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_public_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public", "expr": [{"jump": {"target": "filter_IN_public_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public", "expr": [{"jump": {"target": "filter_IN_public_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public", "expr": [{"jump": {"target": "filter_IN_public_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public", "expr": [{"jump": {"target": "filter_IN_public_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public", "expr": [{"jump": {"target": "filter_IN_public_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public_allow", "expr": [{"match": {"left": {"payload": {"protocol": "tcp", "field": "dport"}}, "op": "==", "right": 22}}, {"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["new", "untracked"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public_allow", "expr": [{"match": {"left": {"payload": {"protocol": "ip6", "field": "daddr"}}, "op": "==", "right": {"prefix": {"addr": "fe80::", "len": 64}}}}, {"match": {"left": {"payload": {"protocol": "udp", "field": "dport"}}, "op": "==", "right": 546}}, {"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["new", "untracked"]}}}, {"accept": null}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_public"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_public_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_public_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_public_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_public_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_public_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_public", "expr": [{"jump": {"target": "filter_FWDI_public_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_public", "expr": [{"jump": {"target": "filter_FWDI_public_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_public", "expr": [{"jump": {"target": "filter_FWDI_public_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_public", "expr": [{"jump": {"target": "filter_FWDI_public_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_public", "expr": [{"jump": {"target": "filter_FWDI_public_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public", "index": 4, "expr": [{"match": {"left": {"meta": {"key": "l4proto"}}, "op": "==", "right": {"set": ["icmp", "icmpv6"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_public", "index": 4, "expr": [{"match": {"left": {"meta": {"key": "l4proto"}}, "op": "==", "right": {"set": ["icmp", "icmpv6"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING_ZONES", "expr": [{"goto": {"target": "raw_PRE_public"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_public"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_public_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_public_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_public_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_public_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_public_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_public", "expr": [{"jump": {"target": "mangle_PRE_public_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_public", "expr": [{"jump": {"target": "mangle_PRE_public_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_public", "expr": [{"jump": {"target": "mangle_PRE_public_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_public", "expr": [{"jump": {"target": "mangle_PRE_public_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_public", "expr": [{"jump": {"target": "mangle_PRE_public_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PREROUTING_ZONES", "expr": [{"goto": {"target": "mangle_PRE_public"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_public"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_public_pre"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_public_log"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_public_deny"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_public_allow"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_public_post"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_pre"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_log"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_deny"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_allow"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_post"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_public"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_public_pre"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_public_log"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_public_deny"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_public_allow"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_public_post"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_pre"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_log"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_deny"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_allow"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_post"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PREROUTING_ZONES", "expr": [{"goto": {"target": "nat_PRE_public"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PREROUTING_ZONES", "expr": [{"goto": {"target": "nat_PRE_public"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_public"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_public_pre"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_public_log"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_public_deny"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_public_allow"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_public_post"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_pre"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_log"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_deny"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_allow"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_post"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_public"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_public_pre"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_public_log"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_public_deny"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_public_allow"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_public_post"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_pre"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_log"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_deny"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_allow"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_post"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POSTROUTING_ZONES", "expr": [{"goto": {"target": "nat_POST_public"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POSTROUTING_ZONES", "expr": [{"goto": {"target": "nat_POST_public"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT_ZONES", "expr": [{"goto": {"target": "filter_IN_public"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD_IN_ZONES", "expr": [{"goto": {"target": "filter_FWDI_public"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_public"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_public_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_public_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_public_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_public_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_public_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_public", "expr": [{"jump": {"target": "filter_FWDO_public_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_public", "expr": [{"jump": {"target": "filter_FWDO_public_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_public", "expr": [{"jump": {"target": "filter_FWDO_public_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_public", "expr": [{"jump": {"target": "filter_FWDO_public_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_public", "expr": [{"jump": {"target": "filter_FWDO_public_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD_OUT_ZONES", "expr": [{"goto": {"target": "filter_FWDO_public"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_trusted"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_trusted_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_trusted_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_trusted_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_trusted_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_trusted_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_trusted", "expr": [{"jump": {"target": "raw_PRE_trusted_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_trusted", "expr": [{"jump": {"target": "raw_PRE_trusted_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_trusted", "expr": [{"jump": {"target": "raw_PRE_trusted_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_trusted", "expr": [{"jump": {"target": "raw_PRE_trusted_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_trusted", "expr": [{"jump": {"target": "raw_PRE_trusted_post"}}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "raw_PRE_trusted"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_trusted"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_trusted_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_trusted_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_trusted_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_trusted_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_trusted_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_trusted", "expr": [{"jump": {"target": "mangle_PRE_trusted_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_trusted", "expr": [{"jump": {"target": "mangle_PRE_trusted_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_trusted", "expr": [{"jump": {"target": "mangle_PRE_trusted_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_trusted", "expr": [{"jump": {"target": "mangle_PRE_trusted_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_trusted", "expr": [{"jump": {"target": "mangle_PRE_trusted_post"}}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "mangle_PRE_trusted"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_trusted"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_trusted_pre"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_trusted_log"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_trusted_deny"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_trusted_allow"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_trusted_post"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_pre"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_log"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_deny"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_allow"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_post"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_trusted"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_trusted_pre"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_trusted_log"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_trusted_deny"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_trusted_allow"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_trusted_post"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_pre"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_log"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_deny"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_allow"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_post"}}]}}}, {"insert": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "nat_PRE_trusted"}}]}}}, {"insert": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "nat_PRE_trusted"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_trusted"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_trusted_pre"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_trusted_log"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_trusted_deny"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_trusted_allow"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_trusted_post"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_pre"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_log"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_deny"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_allow"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_post"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_trusted"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_trusted_pre"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_trusted_log"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_trusted_deny"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_trusted_allow"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_trusted_post"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_pre"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_log"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_deny"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_allow"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_post"}}]}}}, {"insert": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POSTROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "nat_POST_trusted"}}]}}}, {"insert": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POSTROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "nat_POST_trusted"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_trusted"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_trusted_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_trusted_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_trusted_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_trusted_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_trusted_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_trusted", "expr": [{"jump": {"target": "filter_IN_trusted_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_trusted", "expr": [{"jump": {"target": "filter_IN_trusted_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_trusted", "expr": [{"jump": {"target": "filter_IN_trusted_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_trusted", "expr": [{"jump": {"target": "filter_IN_trusted_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_trusted", "expr": [{"jump": {"target": "filter_IN_trusted_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_trusted", "expr": [{"accept": null}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "filter_IN_trusted"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_trusted"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_trusted_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_trusted_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_trusted_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_trusted_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_trusted_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_trusted", "expr": [{"jump": {"target": "filter_FWDI_trusted_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_trusted", "expr": [{"jump": {"target": "filter_FWDI_trusted_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_trusted", "expr": [{"jump": {"target": "filter_FWDI_trusted_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_trusted", "expr": [{"jump": {"target": "filter_FWDI_trusted_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_trusted", "expr": [{"jump": {"target": "filter_FWDI_trusted_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_trusted", "expr": [{"accept": null}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD_IN_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "filter_FWDI_trusted"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_trusted"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_trusted_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_trusted_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_trusted_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_trusted_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_trusted_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_trusted", "expr": [{"jump": {"target": "filter_FWDO_trusted_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_trusted", "expr": [{"jump": {"target": "filter_FWDO_trusted_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_trusted", "expr": [{"jump": {"target": "filter_FWDO_trusted_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_trusted", "expr": [{"jump": {"target": "filter_FWDO_trusted_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_trusted", "expr": [{"jump": {"target": "filter_FWDO_trusted_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_trusted", "expr": [{"accept": null}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD_OUT_ZONES", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "filter_FWDO_trusted"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_work"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_work_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_work_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_work_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_work_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_work_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_work", "expr": [{"jump": {"target": "raw_PRE_work_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_work", "expr": [{"jump": {"target": "raw_PRE_work_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_work", "expr": [{"jump": {"target": "raw_PRE_work_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_work", "expr": [{"jump": {"target": "raw_PRE_work_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_work", "expr": [{"jump": {"target": "raw_PRE_work_post"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_work"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_work_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_work_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_work_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_work_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_work_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work", "expr": [{"jump": {"target": "filter_IN_work_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work", "expr": [{"jump": {"target": "filter_IN_work_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work", "expr": [{"jump": {"target": "filter_IN_work_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work", "expr": [{"jump": {"target": "filter_IN_work_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work", "expr": [{"jump": {"target": "filter_IN_work_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work_allow", "expr": [{"match": {"left": {"payload": {"protocol": "tcp", "field": "dport"}}, "op": "==", "right": 22}}, {"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["new", "untracked"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work_allow", "expr": [{"match": {"left": {"payload": {"protocol": "ip6", "field": "daddr"}}, "op": "==", "right": {"prefix": {"addr": "fe80::", "len": 64}}}}, {"match": {"left": {"payload": {"protocol": "udp", "field": "dport"}}, "op": "==", "right": 546}}, {"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["new", "untracked"]}}}, {"accept": null}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "raw_PRE_work"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_work"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_work_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_work_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_work_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_work_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_work_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_work", "expr": [{"jump": {"target": "mangle_PRE_work_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_work", "expr": [{"jump": {"target": "mangle_PRE_work_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_work", "expr": [{"jump": {"target": "mangle_PRE_work_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_work", "expr": [{"jump": {"target": "mangle_PRE_work_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_work", "expr": [{"jump": {"target": "mangle_PRE_work_post"}}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "mangle_PRE_work"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_work"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_work_pre"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_work_log"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_work_deny"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_work_allow"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_work_post"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_pre"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_log"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_deny"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_allow"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_post"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_work"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_work_pre"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_work_log"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_work_deny"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_work_allow"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_work_post"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_pre"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_log"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_deny"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_allow"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_post"}}]}}}, {"insert": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "nat_PRE_work"}}]}}}, {"insert": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "nat_PRE_work"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_work"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_work_pre"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_work_log"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_work_deny"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_work_allow"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_work_post"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_pre"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_log"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_deny"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_allow"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_post"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_work"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_work_pre"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_work_log"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_work_deny"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_work_allow"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_work_post"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_pre"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_log"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_deny"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_allow"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_post"}}]}}}, {"insert": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POSTROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "nat_POST_work"}}]}}}, {"insert": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POSTROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "nat_POST_work"}}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "filter_IN_work"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_work"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_work_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_work_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_work_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_work_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_work_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_work", "expr": [{"jump": {"target": "filter_FWDI_work_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_work", "expr": [{"jump": {"target": "filter_FWDI_work_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_work", "expr": [{"jump": {"target": "filter_FWDI_work_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_work", "expr": [{"jump": {"target": "filter_FWDI_work_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_work", "expr": [{"jump": {"target": "filter_FWDI_work_post"}}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD_IN_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "filter_FWDI_work"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_work"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_work_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_work_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_work_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_work_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_work_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_work", "expr": [{"jump": {"target": "filter_FWDO_work_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_work", "expr": [{"jump": {"target": "filter_FWDO_work_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_work", "expr": [{"jump": {"target": "filter_FWDO_work_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_work", "expr": [{"jump": {"target": "filter_FWDO_work_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_work", "expr": [{"jump": {"target": "filter_FWDO_work_post"}}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD_OUT_ZONES", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "filter_FWDO_work"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work", "index": 4, "expr": [{"match": {"left": {"meta": {"key": "l4proto"}}, "op": "==", "right": {"set": ["icmp", "icmpv6"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_work", "index": 4, "expr": [{"match": {"left": {"meta": {"key": "l4proto"}}, "op": "==", "right": {"set": ["icmp", "icmpv6"]}}}, {"accept": null}]}}}]}' + + if [ "$NFT_TEST_HAVE_json" != n ]; then diff --git a/0066-mnl-silence-compiler-warning.patch b/0066-mnl-silence-compiler-warning.patch new file mode 100644 index 0000000..c4d8a3c --- /dev/null +++ b/0066-mnl-silence-compiler-warning.patch @@ -0,0 +1,55 @@ +From 0ba786e77b069dee73f6fbfd910e6404d28193a8 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:50 +0200 +Subject: [PATCH] mnl: silence compiler warning + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit cd9f168875aca72caacdcfb552c15b6484a82bd5 + +commit cd9f168875aca72caacdcfb552c15b6484a82bd5 +Author: Florian Westphal +Date: Wed Aug 20 14:44:43 2025 +0200 + + mnl: silence compiler warning + + gcc 14.3.0 reports this: + + src/mnl.c: In function 'mnl_nft_chain_add': + src/mnl.c:916:25: warning: 'nest' may be used uninitialized [-Wmaybe-uninitialized] + 916 | mnl_attr_nest_end(nlh, nest); + + I guess its because compiler can't know that the conditions cannot change + in-between and assumes nest_end() can be called without nest_start(). + + Fixes: 01277922fede ("src: ensure chain policy evaluation when specified") + Signed-off-by: Florian Westphal + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/mnl.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/src/mnl.c b/src/mnl.c +index 6e8066a..6ba2d85 100644 +--- a/src/mnl.c ++++ b/src/mnl.c +@@ -868,7 +868,7 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd, + nftnl_chain_nlmsg_build_payload(nlh, nlc); + + if (cmd->chain && cmd->chain->flags & CHAIN_F_BASECHAIN) { +- struct nlattr *nest; ++ struct nlattr *nest = NULL; + + if (cmd->chain->type.str) { + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->chain->type.loc); +@@ -889,8 +889,7 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd, + if (cmd->chain && cmd->chain->dev_expr) + mnl_nft_chain_devs_build(nlh, cmd); + +- if (cmd->chain->type.str || +- (cmd->chain && cmd->chain->dev_expr)) ++ if (nest) + mnl_attr_nest_end(nlh, nest); + } + diff --git a/0067-mnl-continue-on-ENOBUFS-errors-when-processing-batch.patch b/0067-mnl-continue-on-ENOBUFS-errors-when-processing-batch.patch new file mode 100644 index 0000000..b8949a8 --- /dev/null +++ b/0067-mnl-continue-on-ENOBUFS-errors-when-processing-batch.patch @@ -0,0 +1,89 @@ +From 02efc5f458b73e02dd05ef1f6e4736b3a8999ac2 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:50 +0200 +Subject: [PATCH] mnl: continue on ENOBUFS errors when processing batch + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 47e9aaf0227daf16f43a7442e1dceae8851817a5 + +commit 47e9aaf0227daf16f43a7442e1dceae8851817a5 +Author: Pablo Neira Ayuso +Date: Tue Aug 26 10:09:13 2025 +0200 + + mnl: continue on ENOBUFS errors when processing batch + + A user reports that: + + nft -f ruleset.nft + + fails with: + + netlink: Error: Could not process rule: No buffer space available + + This was triggered by: + + table ip6 fule { + set domestic_ip6 { + type ipv6_addr + flags dynamic,interval + elements = $domestic_ip6 + } + chain prerouting { + type filter hook prerouting priority 0; + ip6 daddr @domestic_ip6 counter + } + } + + where $domestic_ip6 contains a large number of IPv6 addresses. + + This set declaration is not supported currently, because dynamic sets + with intervals are not supported, then every IPv6 address that is added + triggers an error, overruning the userspace socket buffer with lots of + NLMSG_ERROR messages (or too big NLMSG_ERROR message to fit into the + socket buffer). + + In the particular context of batch processing, ENOBUFS is just an + indication that too many errors have occurred. The kernel cannot store + any more NLMSG_ERROR messages into the userspace socket buffer. + + However, there are still NLMSG_ERROR messages in the socket buffer to be + processed that can provide a hint on what is going on. + + Instead of breaking on ENOBUFS in batches, continue error processing. + + After this patch, the ruleset above displays: + + ruleset.nft:2367:7-18: Error: Could not process rule: Operation not supported + set domestic_ip6 { + ^^^^^^^^^^^^ + ruleset.nft:2367:7-18: Error: Could not process rule: No such file or directory + set domestic_ip6 { + ^^^^^^^^^^^^ + + Fixes: a72315d2bad4 ("src: add rule batching support") + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/mnl.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/src/mnl.c b/src/mnl.c +index 6ba2d85..96a86aa 100644 +--- a/src/mnl.c ++++ b/src/mnl.c +@@ -445,8 +445,13 @@ int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list, + break; + + ret = mnl_socket_recvfrom(nl, rcv_buf, sizeof(rcv_buf)); +- if (ret == -1) ++ if (ret == -1) { ++ /* Too many errors, not all errors are displayed. */ ++ if (errno == ENOBUFS) ++ continue; ++ + return -1; ++ } + + /* Continue on error, make sure we get all acknowledgments */ + ret = mnl_cb_run2(rcv_buf, ret, 0, portid, diff --git a/0068-monitor-Quote-device-names-in-chain-declarations-too.patch b/0068-monitor-Quote-device-names-in-chain-declarations-too.patch new file mode 100644 index 0000000..4f72445 --- /dev/null +++ b/0068-monitor-Quote-device-names-in-chain-declarations-too.patch @@ -0,0 +1,38 @@ +From 50c0c4910132783578eee7b2aa767b88f504e366 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:50 +0200 +Subject: [PATCH] monitor: Quote device names in chain declarations, too + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit ed1b5b672b2eeb6244bed566227bee2aa7a1e4b4 + +commit ed1b5b672b2eeb6244bed566227bee2aa7a1e4b4 +Author: Phil Sutter +Date: Thu Aug 28 16:47:03 2025 +0200 + + monitor: Quote device names in chain declarations, too + + Fixed commit missed the fact that there are two routines printing chain + declarations. + + Fixes: eb30f236d91a8 ("rule: print chain and flowtable devices in quotes") + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + src/rule.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/rule.c b/src/rule.c +index c026d8d..3b75736 100644 +--- a/src/rule.c ++++ b/src/rule.c +@@ -1110,7 +1110,7 @@ void chain_print_plain(const struct chain *chain, struct output_ctx *octx) + + nft_print(octx, "devices = { "); + for (i = 0; i < chain->dev_array_len; i++) { +- nft_print(octx, "%s", chain->dev_array[i]); ++ nft_print(octx, "\"%s\"", chain->dev_array[i]); + if (i + 1 != chain->dev_array_len) + nft_print(octx, ", "); + } diff --git a/0069-tests-monitor-Fix-regex-collecting-expected-echo-out.patch b/0069-tests-monitor-Fix-regex-collecting-expected-echo-out.patch new file mode 100644 index 0000000..56dd152 --- /dev/null +++ b/0069-tests-monitor-Fix-regex-collecting-expected-echo-out.patch @@ -0,0 +1,39 @@ +From cae7e6924f2857b6e66db1425abc6beff07bb2ac Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:50 +0200 +Subject: [PATCH] tests: monitor: Fix regex collecting expected echo output + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 419338d96bdb19c10e241387c54f416c551a47c3 + +commit 419338d96bdb19c10e241387c54f416c551a47c3 +Author: Phil Sutter +Date: Wed Sep 3 15:23:12 2025 +0200 + + tests: monitor: Fix regex collecting expected echo output + + No input triggered this bug, but the match would accept "insert" and + "replace" keywords anywhere in the line not just at the beginning as was + intended. + + Fixes: b2506e5504fed ("tests: Merge monitor and echo test suites") + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + tests/monitor/run-tests.sh | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/monitor/run-tests.sh b/tests/monitor/run-tests.sh +index f1ac790..c6a3322 100755 +--- a/tests/monitor/run-tests.sh ++++ b/tests/monitor/run-tests.sh +@@ -53,7 +53,7 @@ echo_output_append() { + grep '^\(add\|replace\|insert\)' $command_file >>$output_file + return + } +- [[ "$*" =~ ^add|replace|insert ]] && echo "$*" >>$output_file ++ [[ "$*" =~ ^(add|replace|insert) ]] && echo "$*" >>$output_file + } + json_output_filter() { # (filename) + # unify handle values diff --git a/0070-monitor-Inform-JSON-printer-when-reporting-an-object.patch b/0070-monitor-Inform-JSON-printer-when-reporting-an-object.patch new file mode 100644 index 0000000..aabc7f6 --- /dev/null +++ b/0070-monitor-Inform-JSON-printer-when-reporting-an-object.patch @@ -0,0 +1,189 @@ +From c6b5059905d5e1ba029027cecd8e62d7b70c1c51 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:31:50 +0200 +Subject: [PATCH] monitor: Inform JSON printer when reporting an object delete + event + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 6c04d24d16f1d15f216f2b3c8e64c9062cd77487 +Conflicts: + - Context change due to missing commit 73a8adfc2432e + ("monitor: Recognize flowtable add/del events") + - Adjusted to missing commit 3a957f8f1ff1e + ("tunnel: add tunnel object and statement json support") + +commit 6c04d24d16f1d15f216f2b3c8e64c9062cd77487 +Author: Phil Sutter +Date: Fri Aug 29 01:07:05 2025 +0200 + + monitor: Inform JSON printer when reporting an object delete event + + Since kernel commit a1050dd07168 ("netfilter: nf_tables: Reintroduce + shortened deletion notifications"), type-specific data is no longer + dumped when notifying for a deleted object. JSON output was not aware of + this and tried to print bogus data. + + Fixes: 9e88aae28e9f4 ("monitor: Use libnftables JSON output") + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + include/json.h | 5 +++-- + src/json.c | 16 ++++++++++------ + src/monitor.c | 2 +- + tests/monitor/testcases/object.t | 10 +++++----- + 4 files changed, 19 insertions(+), 14 deletions(-) + +diff --git a/include/json.h b/include/json.h +index 39be892..23ca49e 100644 +--- a/include/json.h ++++ b/include/json.h +@@ -112,7 +112,7 @@ void monitor_print_set_json(struct netlink_mon_handler *monh, + void monitor_print_element_json(struct netlink_mon_handler *monh, + const char *cmd, struct set *s); + void monitor_print_obj_json(struct netlink_mon_handler *monh, +- const char *cmd, struct obj *o); ++ const char *cmd, struct obj *o, bool delete); + void monitor_print_rule_json(struct netlink_mon_handler *monh, + const char *cmd, struct rule *r); + +@@ -249,7 +249,8 @@ static inline void monitor_print_element_json(struct netlink_mon_handler *monh, + } + + static inline void monitor_print_obj_json(struct netlink_mon_handler *monh, +- const char *cmd, struct obj *o) ++ const char *cmd, struct obj *o, ++ bool delete) + { + /* empty */ + } +diff --git a/src/json.c b/src/json.c +index 57b7664..fc2e962 100644 +--- a/src/json.c ++++ b/src/json.c +@@ -353,7 +353,7 @@ static json_t *timeout_policy_json(uint8_t l4, const uint32_t *timeout) + return root ? : json_null(); + } + +-static json_t *obj_print_json(const struct obj *obj) ++static json_t *obj_print_json(const struct obj *obj, bool delete) + { + const char *rate_unit = NULL, *burst_unit = NULL; + const char *type = obj_type_name(obj->type); +@@ -366,6 +366,9 @@ static json_t *obj_print_json(const struct obj *obj) + "table", obj->handle.table.name, + "handle", obj->handle.handle.id); + ++ if (delete) ++ goto out; ++ + if (obj->comment) { + tmp = nft_json_pack("{s:s}", "comment", obj->comment); + json_object_update(root, tmp); +@@ -469,6 +472,7 @@ static json_t *obj_print_json(const struct obj *obj) + break; + } + ++out: + return nft_json_pack("{s:o}", type, root); + } + +@@ -1699,7 +1703,7 @@ static json_t *table_print_json_full(struct netlink_ctx *ctx, + json_array_append_new(root, tmp); + + list_for_each_entry(obj, &table->obj_cache.list, cache.list) { +- tmp = obj_print_json(obj); ++ tmp = obj_print_json(obj, false); + json_array_append_new(root, tmp); + } + list_for_each_entry(set, &table->set_cache.list, cache.list) { +@@ -1861,7 +1865,7 @@ static json_t *do_list_sets_json(struct netlink_ctx *ctx, struct cmd *cmd) + static json_t *do_list_obj_json(struct netlink_ctx *ctx, + struct cmd *cmd, uint32_t type) + { +- json_t *root = json_array(); ++ json_t *root = json_array(), *tmp; + struct table *table; + struct obj *obj; + +@@ -1880,7 +1884,7 @@ static json_t *do_list_obj_json(struct netlink_ctx *ctx, + strcmp(cmd->handle.obj.name, obj->handle.obj.name))) + continue; + +- json_array_append_new(root, obj_print_json(obj)); ++ json_array_append_new(root, obj_print_json(obj, false)); + } + } + +@@ -2066,9 +2070,9 @@ void monitor_print_element_json(struct netlink_mon_handler *monh, + } + + void monitor_print_obj_json(struct netlink_mon_handler *monh, +- const char *cmd, struct obj *o) ++ const char *cmd, struct obj *o, bool delete) + { +- monitor_print_json(monh, cmd, obj_print_json(o)); ++ monitor_print_json(monh, cmd, obj_print_json(o, delete)); + } + + void monitor_print_rule_json(struct netlink_mon_handler *monh, +diff --git a/src/monitor.c b/src/monitor.c +index bf59f2d..87ff2a3 100644 +--- a/src/monitor.c ++++ b/src/monitor.c +@@ -529,7 +529,7 @@ static int netlink_events_obj_cb(const struct nlmsghdr *nlh, int type, + nft_mon_print(monh, "\n"); + break; + case NFTNL_OUTPUT_JSON: +- monitor_print_obj_json(monh, cmd, obj); ++ monitor_print_obj_json(monh, cmd, obj, type == NFT_MSG_DELOBJ); + if (!nft_output_echo(&monh->ctx->nft->output)) + nft_mon_print(monh, "\n"); + break; +diff --git a/tests/monitor/testcases/object.t b/tests/monitor/testcases/object.t +index 53a9f8c..b60dc98 100644 +--- a/tests/monitor/testcases/object.t ++++ b/tests/monitor/testcases/object.t +@@ -9,7 +9,7 @@ J {"add": {"counter": {"family": "ip", "name": "c", "table": "t", "handle": 0, " + + I delete counter ip t c + O - +-J {"delete": {"counter": {"family": "ip", "name": "c", "table": "t", "handle": 0, "packets": 0, "bytes": 0}}} ++J {"delete": {"counter": {"family": "ip", "name": "c", "table": "t", "handle": 0}}} + + # FIXME: input/output shouldn't be asynchronous here + I add quota ip t q 25 mbytes +@@ -18,7 +18,7 @@ J {"add": {"quota": {"family": "ip", "name": "q", "table": "t", "handle": 0, "by + + I delete quota ip t q + O - +-J {"delete": {"quota": {"family": "ip", "name": "q", "table": "t", "handle": 0, "bytes": 26214400, "used": 0, "inv": false}}} ++J {"delete": {"quota": {"family": "ip", "name": "q", "table": "t", "handle": 0}}} + + # FIXME: input/output shouldn't be asynchronous here + I add limit ip t l rate 1/second +@@ -27,7 +27,7 @@ J {"add": {"limit": {"family": "ip", "name": "l", "table": "t", "handle": 0, "ra + + I delete limit ip t l + O - +-J {"delete": {"limit": {"family": "ip", "name": "l", "table": "t", "handle": 0, "rate": 1, "per": "second", "burst": 5}}} ++J {"delete": {"limit": {"family": "ip", "name": "l", "table": "t", "handle": 0}}} + + I add ct helper ip t cth { type "sip" protocol tcp; l3proto ip; } + O - +@@ -35,7 +35,7 @@ J {"add": {"ct helper": {"family": "ip", "name": "cth", "table": "t", "handle": + + I delete ct helper ip t cth + O - +-J {"delete": {"ct helper": {"family": "ip", "name": "cth", "table": "t", "handle": 0, "type": "sip", "protocol": "tcp", "l3proto": "ip"}}} ++J {"delete": {"ct helper": {"family": "ip", "name": "cth", "table": "t", "handle": 0}}} + + I add ct timeout ip t ctt { protocol udp; l3proto ip; policy = { unreplied : 15s, replied : 12s }; } + O - +@@ -43,4 +43,4 @@ J {"add": {"ct timeout": {"family": "ip", "name": "ctt", "table": "t", "handle": + + I delete ct timeout ip t ctt + O - +-J {"delete": {"ct timeout": {"family": "ip", "name": "ctt", "table": "t", "handle": 0, "protocol": "udp", "l3proto": "ip", "policy": {"unreplied": 15, "replied": 12}}}} ++J {"delete": {"ct timeout": {"family": "ip", "name": "ctt", "table": "t", "handle": 0}}} diff --git a/0071-fib-Fix-for-existence-check-on-Big-Endian.patch b/0071-fib-Fix-for-existence-check-on-Big-Endian.patch new file mode 100644 index 0000000..c9f9e5e --- /dev/null +++ b/0071-fib-Fix-for-existence-check-on-Big-Endian.patch @@ -0,0 +1,67 @@ +From 69e126ef0536e2b792b970edf0b132dc2aa3548f Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:32:19 +0200 +Subject: [PATCH] fib: Fix for existence check on Big Endian + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 98e51e687616a4b54efa3b723917c292e3acc380 + +commit 98e51e687616a4b54efa3b723917c292e3acc380 +Author: Phil Sutter +Date: Tue Sep 9 22:27:19 2025 +0200 + + fib: Fix for existence check on Big Endian + + Adjust the expression size to 1B so cmp expression value is correct. + Without this, the rule 'fib saddr . iif check exists' generates + following byte code on BE: + + | [ fib saddr . iif oif present => reg 1 ] + | [ cmp eq reg 1 0x00000001 ] + + Though with NFTA_FIB_F_PRESENT flag set, nft_fib.ko writes to the first + byte of reg 1 only (using nft_reg_store8()). With this patch in place, + byte code is correct: + + | [ fib saddr . iif oif present => reg 1 ] + | [ cmp eq reg 1 0x01000000 ] + + Fixes: f686a17eafa0b ("fib: Support existence check") + Cc: Yi Chen + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/evaluate.c | 1 + + src/fib.c | 4 +++- + 2 files changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/evaluate.c b/src/evaluate.c +index 922821b..6046716 100644 +--- a/src/evaluate.c ++++ b/src/evaluate.c +@@ -2574,6 +2574,7 @@ static int expr_evaluate_fib(struct eval_ctx *ctx, struct expr **exprp) + if (expr->flags & EXPR_F_BOOLEAN) { + expr->fib.flags |= NFTA_FIB_F_PRESENT; + datatype_set(expr, &boolean_type); ++ expr->len = BITS_PER_BYTE; + } + return expr_evaluate_primary(ctx, exprp); + } +diff --git a/src/fib.c b/src/fib.c +index 80ff292..bfedcb7 100644 +--- a/src/fib.c ++++ b/src/fib.c +@@ -198,8 +198,10 @@ struct expr *fib_expr_alloc(const struct location *loc, + BUG("Unknown result %d\n", result); + } + +- if (flags & NFTA_FIB_F_PRESENT) ++ if (flags & NFTA_FIB_F_PRESENT) { + type = &boolean_type; ++ len = BITS_PER_BYTE; ++ } + + expr = expr_alloc(loc, EXPR_FIB, type, + BYTEORDER_HOST_ENDIAN, len); diff --git a/0072-doc-fix-tcpdump-example.patch b/0072-doc-fix-tcpdump-example.patch new file mode 100644 index 0000000..e26f960 --- /dev/null +++ b/0072-doc-fix-tcpdump-example.patch @@ -0,0 +1,41 @@ +From 84642c28fab20ef3f0bef23919a45f7b7d7d18db Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:32:19 +0200 +Subject: [PATCH] doc: fix tcpdump example + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit b9516b0a4dfb6e16e3e11c3024683a2df1ea09ab + +commit b9516b0a4dfb6e16e3e11c3024683a2df1ea09ab +Author: Georg Pfuetzenreuter +Date: Mon Oct 13 19:17:31 2025 +0200 + + doc: fix tcpdump example + + The expression needs to be enclosed in a single string and combined with + a logical AND to have the desired effect. + + Fixes: 1188a69604c3 ("src: introduce SYNPROXY matching") + Signed-off-by: Georg Pfuetzenreuter + Reviewed-by: Fernando Fernandez Mancera + Signed-off-by: Florian Westphal + +Signed-off-by: Phil Sutter +--- + doc/statements.txt | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/doc/statements.txt b/doc/statements.txt +index 3aceb02..ad928eb 100644 +--- a/doc/statements.txt ++++ b/doc/statements.txt +@@ -592,8 +592,7 @@ needed for selective acknowledgement and window scaling). + --------------------------------------- + Determine tcp options used by backend, from an external system + +- tcpdump -pni eth0 -c 1 'tcp[tcpflags] == (tcp-syn|tcp-ack)' +- port 80 & ++ tcpdump -pni eth0 -c 1 'tcp[tcpflags] == (tcp-syn|tcp-ack) && port 80' & + telnet 192.0.2.42 80 + 18:57:24.693307 IP 192.0.2.42.80 > 192.0.2.43.48757: + Flags [S.], seq 360414582, ack 788841994, win 14480, diff --git a/0073-src-parser_json-fix-format-string-bugs.patch b/0073-src-parser_json-fix-format-string-bugs.patch new file mode 100644 index 0000000..f5cbf39 --- /dev/null +++ b/0073-src-parser_json-fix-format-string-bugs.patch @@ -0,0 +1,60 @@ +From 32ab7685f938713f927c1ed312ba6089cb2cf65b Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:32:19 +0200 +Subject: [PATCH] src: parser_json: fix format string bugs + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit b30ad0c25b7b4a289dd73e8da6fa101b66fbf0d7 + +commit b30ad0c25b7b4a289dd73e8da6fa101b66fbf0d7 +Author: Florian Westphal +Date: Thu Oct 23 14:17:00 2025 +0200 + + src: parser_json: fix format string bugs + + After adding fmt attribute annotation: + warning: format not a string literal and no format arguments [-Wformat-security] + 131 | erec_queue(error(&loc, err->text), ctx->msgs); + In function 'json_events_cb': + warning: format '%lu' expects argument of type 'long unsigned int', but argument 3 has type '__u32' {aka 'unsigned int'} [-Wformat=] + + Fix that up too. + + Fixes: 586ad210368b ("libnftables: Implement JSON parser") + Signed-off-by: Florian Westphal + +Signed-off-by: Phil Sutter +--- + src/parser_json.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/parser_json.c b/src/parser_json.c +index 8c9d365..cce5ee2 100644 +--- a/src/parser_json.c ++++ b/src/parser_json.c +@@ -127,7 +127,7 @@ static void json_lib_error(struct json_ctx *ctx, json_error_t *err) + .last_column = err->column, + }; + +- erec_queue(error(&loc, err->text), ctx->msgs); ++ erec_queue(error(&loc, "%s", err->text), ctx->msgs); + } + + __attribute__((format(printf, 2, 3))) +@@ -4176,6 +4176,7 @@ int nft_parse_json_filename(struct nft_ctx *nft, const char *filename, + return ret; + } + ++__attribute__((format(printf, 2, 3))) + static int json_echo_error(struct netlink_mon_handler *monh, + const char *fmt, ...) + { +@@ -4242,7 +4243,7 @@ int json_events_cb(const struct nlmsghdr *nlh, struct netlink_mon_handler *monh) + + json = seqnum_to_json(nlh->nlmsg_seq); + if (!json) { +- json_echo_error(monh, "No JSON command found with seqnum %lu\n", ++ json_echo_error(monh, "No JSON command found with seqnum %u\n", + nlh->nlmsg_seq); + return MNL_CB_OK; + } diff --git a/0074-datatype-clamp-boolean-value-to-0-and-1.patch b/0074-datatype-clamp-boolean-value-to-0-and-1.patch new file mode 100644 index 0000000..5b84b3e --- /dev/null +++ b/0074-datatype-clamp-boolean-value-to-0-and-1.patch @@ -0,0 +1,80 @@ +From 247ed03d1d9e2c03744d7c47db95be813f17ce54 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:32:19 +0200 +Subject: [PATCH] datatype: clamp boolean value to 0 and 1 + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit afb6a8e66a11178cbdbfc152c4aa9dda961b2140 + +commit afb6a8e66a11178cbdbfc152c4aa9dda961b2140 +Author: Pablo Neira Ayuso +Date: Fri Jan 31 12:54:32 2025 +0100 + + datatype: clamp boolean value to 0 and 1 + + If user provides a numeric value larger than 0 or 1, match never + happens: + + # nft --debug=netlink add rule x y tcp option sack-perm 4 + ip x y + [ exthdr load tcpopt 1b @ 4 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000004 ] + + After this update: + + # nft --debug=netlink add rule x y tcp option sack-perm 4 + ip x y + [ exthdr load tcpopt 1b @ 4 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + + This is to address a rare corner case, in case user specifies the + boolean value through the integer base type. + + Fixes: 9fd9baba43c8 ("Introduce boolean datatype and boolean expression") + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/datatype.c | 24 ++++++++++++++++++++++++ + 1 file changed, 24 insertions(+) + +diff --git a/src/datatype.c b/src/datatype.c +index 9530ae7..93703f8 100644 +--- a/src/datatype.c ++++ b/src/datatype.c +@@ -1417,11 +1417,35 @@ static const struct symbol_table boolean_tbl = { + }, + }; + ++static struct error_record *boolean_type_parse(struct parse_ctx *ctx, ++ const struct expr *sym, ++ struct expr **res) ++{ ++ struct error_record *erec; ++ int num; ++ ++ erec = integer_type_parse(ctx, sym, res); ++ if (erec) ++ return erec; ++ ++ if (mpz_cmp_ui((*res)->value, 0)) ++ num = 1; ++ else ++ num = 0; ++ ++ expr_free(*res); ++ ++ *res = constant_expr_alloc(&sym->location, &boolean_type, ++ BYTEORDER_HOST_ENDIAN, 1, &num); ++ return NULL; ++} ++ + const struct datatype boolean_type = { + .type = TYPE_BOOLEAN, + .name = "boolean", + .desc = "boolean type", + .size = 1, ++ .parse = boolean_type_parse, + .basetype = &integer_type, + .sym_tbl = &boolean_tbl, + .json = boolean_type_json, diff --git a/0075-datatype-Fix-boolean-type-on-Big-Endian.patch b/0075-datatype-Fix-boolean-type-on-Big-Endian.patch new file mode 100644 index 0000000..ee79de0 --- /dev/null +++ b/0075-datatype-Fix-boolean-type-on-Big-Endian.patch @@ -0,0 +1,39 @@ +From d48369a36876854039882dc8981f49c7e42574c5 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:32:19 +0200 +Subject: [PATCH] datatype: Fix boolean type on Big Endian + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit aec699af2a006b1dd6580abb95910c402e306fa9 + +commit aec699af2a006b1dd6580abb95910c402e306fa9 +Author: Phil Sutter +Date: Wed Oct 8 23:19:08 2025 +0200 + + datatype: Fix boolean type on Big Endian + + Pass a reference to a variable with correct size when creating the + expression, otherwise mpz_import_data() will read only the always zero + upper byte on Big Endian hosts. + + Fixes: afb6a8e66a111 ("datatype: clamp boolean value to 0 and 1") + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + src/datatype.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/datatype.c b/src/datatype.c +index 93703f8..bb93ef3 100644 +--- a/src/datatype.c ++++ b/src/datatype.c +@@ -1422,7 +1422,7 @@ static struct error_record *boolean_type_parse(struct parse_ctx *ctx, + struct expr **res) + { + struct error_record *erec; +- int num; ++ uint8_t num; + + erec = integer_type_parse(ctx, sym, res); + if (erec) diff --git a/0076-optimize-Fix-verdict-expression-comparison.patch b/0076-optimize-Fix-verdict-expression-comparison.patch new file mode 100644 index 0000000..a23822e --- /dev/null +++ b/0076-optimize-Fix-verdict-expression-comparison.patch @@ -0,0 +1,56 @@ +From 6fdf4d2a6a114044a9932d5031d6d461e12505df Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:32:19 +0200 +Subject: [PATCH] optimize: Fix verdict expression comparison + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 695ee5a8b174f86e2e64786530147e56d8d27f19 + +commit 695ee5a8b174f86e2e64786530147e56d8d27f19 +Author: Phil Sutter +Date: Wed Oct 22 14:03:37 2025 +0200 + + optimize: Fix verdict expression comparison + + In verdict expression, 'chain' points at a constant expression of + verdict_type, not a symbol expression. Therefore 'chain->identifier' + points eight bytes (on 64bit systems) into the mpz_t 'value' holding the + chain name. This matches the '_mp_d' data pointer, so works by accident. + + Fix this by copying what verdict_jump_chain_print() does and export + chain names before comparing. + + Fixes: fb298877ece27 ("src: add ruleset optimization infrastructure") + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + src/optimize.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/src/optimize.c b/src/optimize.c +index 27e0ffe..7b4d036 100644 +--- a/src/optimize.c ++++ b/src/optimize.c +@@ -302,13 +302,18 @@ static bool __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b, + + static bool expr_verdict_eq(const struct expr *expr_a, const struct expr *expr_b) + { ++ char chain_a[NFT_CHAIN_MAXNAMELEN]; ++ char chain_b[NFT_CHAIN_MAXNAMELEN]; ++ + if (expr_a->verdict != expr_b->verdict) + return false; + if (expr_a->chain && expr_b->chain) { +- if (expr_a->chain->etype != expr_b->chain->etype) ++ if (expr_a->chain->etype != EXPR_VALUE || ++ expr_a->chain->etype != expr_b->chain->etype) + return false; +- if (expr_a->chain->etype == EXPR_VALUE && +- strcmp(expr_a->chain->identifier, expr_b->chain->identifier)) ++ expr_chain_export(expr_a->chain, chain_a); ++ expr_chain_export(expr_b->chain, chain_b); ++ if (strcmp(chain_a, chain_b)) + return false; + } else if (expr_a->chain || expr_b->chain) { + return false; diff --git a/0077-tests-py-any-ct.t.json.output-Drop-leftover-entry.patch b/0077-tests-py-any-ct.t.json.output-Drop-leftover-entry.patch new file mode 100644 index 0000000..06d02bf --- /dev/null +++ b/0077-tests-py-any-ct.t.json.output-Drop-leftover-entry.patch @@ -0,0 +1,69 @@ +From 6f5224890b16acddb72ce4c02a6ffc3c2afa12cb Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:32:19 +0200 +Subject: [PATCH] tests: py: any/ct.t.json.output: Drop leftover entry + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 1801480314bf2baa6abf9cb15ccdef975b966867 + +commit 1801480314bf2baa6abf9cb15ccdef975b966867 +Author: Phil Sutter +Date: Thu Oct 16 16:23:12 2025 +0200 + + tests: py: any/ct.t.json.output: Drop leftover entry + + The rule with single element anonymous set was replaced, drop this + leftover. + + Fixes: 27f6a4c68b4fd ("tests: replace single element sets") + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + tests/py/any/ct.t.json.output | 33 --------------------------------- + 1 file changed, 33 deletions(-) + +diff --git a/tests/py/any/ct.t.json.output b/tests/py/any/ct.t.json.output +index 70ade7e..82634c2 100644 +--- a/tests/py/any/ct.t.json.output ++++ b/tests/py/any/ct.t.json.output +@@ -471,39 +471,6 @@ + } + ] + +-# ct state . ct mark { new . 0x12345678} +-[ +- { +- "match": { +- "left": { +- "concat": [ +- { +- "ct": { +- "key": "state" +- } +- }, +- { +- "ct": { +- "key": "mark" +- } +- } +- ] +- }, +- "op": "==", +- "right": { +- "set": [ +- { +- "concat": [ +- "new", +- 305419896 +- ] +- } +- ] +- } +- } +- } +-] +- + # ct state . ct mark { new . 0x12345678, new . 0x34127856, established . 0x12785634} + [ + { diff --git a/0078-tests-py-inet-osf.t-Fix-element-ordering-in-JSON-equ.patch b/0078-tests-py-inet-osf.t-Fix-element-ordering-in-JSON-equ.patch new file mode 100644 index 0000000..cf359eb --- /dev/null +++ b/0078-tests-py-inet-osf.t-Fix-element-ordering-in-JSON-equ.patch @@ -0,0 +1,121 @@ +From 9811fa070a0e2b0f33e719080e9f10cda366ca58 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:32:19 +0200 +Subject: [PATCH] tests: py: inet/osf.t: Fix element ordering in JSON + equivalents + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit b028f8ce616bb5a219a10844357b9a3822d99a8c + +commit b028f8ce616bb5a219a10844357b9a3822d99a8c +Author: Phil Sutter +Date: Thu Oct 9 02:06:54 2025 +0200 + + tests: py: inet/osf.t: Fix element ordering in JSON equivalents + + The original rules order set elements differently. Stick to that and add + entries to inet/osf.t.json.output to cover for nftables reordering + entries. + + Fixes: 92029c1282958 ("src: osf: add json support") + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + tests/py/inet/osf.t.json | 12 ++++---- + tests/py/inet/osf.t.json.output | 53 +++++++++++++++++++++++++++++++++ + 2 files changed, 59 insertions(+), 6 deletions(-) + create mode 100644 tests/py/inet/osf.t.json.output + +diff --git a/tests/py/inet/osf.t.json b/tests/py/inet/osf.t.json +index cedb7f6..b1bf747 100644 +--- a/tests/py/inet/osf.t.json ++++ b/tests/py/inet/osf.t.json +@@ -73,8 +73,8 @@ + "op": "==", + "right": { + "set": [ +- "MacOs", +- "Windows" ++ "Windows", ++ "MacOs" + ] + } + } +@@ -114,13 +114,13 @@ + "map": { + "data": { + "set": [ +- [ +- "MacOs", +- 2 +- ], + [ + "Windows", + 1 ++ ], ++ [ ++ "MacOs", ++ 2 + ] + ] + }, +diff --git a/tests/py/inet/osf.t.json.output b/tests/py/inet/osf.t.json.output +new file mode 100644 +index 0000000..922e395 +--- /dev/null ++++ b/tests/py/inet/osf.t.json.output +@@ -0,0 +1,53 @@ ++# osf name { "Windows", "MacOs" } ++[ ++ { ++ "match": { ++ "left": { ++ "osf": { ++ "key": "name" ++ } ++ }, ++ "op": "==", ++ "right": { ++ "set": [ ++ "MacOs", ++ "Windows" ++ ] ++ } ++ } ++ } ++] ++ ++# ct mark set osf name map { "Windows" : 0x00000001, "MacOs" : 0x00000002 } ++[ ++ { ++ "mangle": { ++ "key": { ++ "ct": { ++ "key": "mark" ++ } ++ }, ++ "value": { ++ "map": { ++ "data": { ++ "set": [ ++ [ ++ "MacOs", ++ 2 ++ ], ++ [ ++ "Windows", ++ 1 ++ ] ++ ] ++ }, ++ "key": { ++ "osf": { ++ "key": "name" ++ } ++ } ++ } ++ } ++ } ++ } ++] diff --git a/0079-doc-don-t-suggest-to-disable-GSO.patch b/0079-doc-don-t-suggest-to-disable-GSO.patch new file mode 100644 index 0000000..c459103 --- /dev/null +++ b/0079-doc-don-t-suggest-to-disable-GSO.patch @@ -0,0 +1,69 @@ +From 722fc9bde92c202e740ced94023fc2ad5a3ce343 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:32:19 +0200 +Subject: [PATCH] doc: don't suggest to disable GSO + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 35cd3e7cff079c561ba616c792d5fdf13f6bd331 + +commit 35cd3e7cff079c561ba616c792d5fdf13f6bd331 +Author: Ronan Pigott +Date: Sun Oct 6 09:36:03 2024 -0700 + + doc: don't suggest to disable GSO + + The kernel can form aggregate packets whether or not GSO is enabled. + Disabling GSO is not a useful suggestion in this case. + + Fixes: 05628cdd677d (doc: describe behaviour of {ip,ip6} length) + Signed-off-by: Florian Westphal + +Signed-off-by: Phil Sutter +--- + doc/payload-expression.txt | 26 ++++++++++++-------------- + 1 file changed, 12 insertions(+), 14 deletions(-) + +diff --git a/doc/payload-expression.txt b/doc/payload-expression.txt +index 8afeb71..6733846 100644 +--- a/doc/payload-expression.txt ++++ b/doc/payload-expression.txt +@@ -134,13 +134,12 @@ Destination address | + ipv4_addr + |====================== + +-Careful with matching on *ip length*: If GRO/GSO is enabled, then the Linux +-kernel might aggregate several packets into one big packet that is larger than +-MTU. Moreover, if GRO/GSO maximum size is larger than 65535 (see man ip-link(8), +-specifically gro_ipv6_max_size and gso_ipv6_max_size), then *ip length* might +-be 0 for such jumbo packets. *meta length* allows you to match on the packet +-length including the IP header size. If you want to perform heuristics on the +-*ip length* field, then disable GRO/GSO. ++Careful with matching on *ip length*: The Linux kernel might aggregate several ++packets into one big packet that is larger than MTU. Moreover, if GRO/GSO ++maximum size is larger than 65535 (see man ip-link(8), specifically ++gro_ipv4_max_size and gso_ipv4_max_size), then *ip length* might be 0 for such ++jumbo packets. *meta length* allows you to match on the packet length including ++the IP header size. + + ICMP HEADER EXPRESSION + ~~~~~~~~~~~~~~~~~~~~~~ +@@ -252,13 +251,12 @@ Destination address | + ipv6_addr + |======================= + +-Careful with matching on *ip6 length*: If GRO/GSO is enabled, then the Linux +-kernel might aggregate several packets into one big packet that is larger than +-MTU. Moreover, if GRO/GSO maximum size is larger than 65535 (see man ip-link(8), +-specifically gro_ipv6_max_size and gso_ipv6_max_size), then *ip6 length* might +-be 0 for such jumbo packets. *meta length* allows you to match on the packet +-length including the IP header size. If you want to perform heuristics on the +-*ip6 length* field, then disable GRO/GSO. ++Careful with matching on *ip6 length*: The Linux kernel might aggregate several ++packets into one big packet that is larger than MTU. Moreover, if GRO/GSO ++maximum size is larger than 65535 (see man ip-link(8), specifically ++gro_max_size and gso_max_size), then *ip6 length* might be 0 for such ++jumbo packets. *meta length* allows you to match on the packet length including ++the IPv6 header size. + + .Using ip6 header expressions + ----------------------------- diff --git a/0080-doc-libnftables-json-Describe-RULESET-object.patch b/0080-doc-libnftables-json-Describe-RULESET-object.patch new file mode 100644 index 0000000..13c6777 --- /dev/null +++ b/0080-doc-libnftables-json-Describe-RULESET-object.patch @@ -0,0 +1,48 @@ +From 8287f290fe2883de894cd1f4388bed5a6076cc18 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:32:19 +0200 +Subject: [PATCH] doc: libnftables-json: Describe RULESET object + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 454f361434522bbeba32e114a14c336e1ebf20a1 + +commit 454f361434522bbeba32e114a14c336e1ebf20a1 +Author: Phil Sutter +Date: Thu Nov 6 12:14:56 2025 +0100 + + doc: libnftables-json: Describe RULESET object + + Document the syntax of this meta-object used by "list" and "flush" + commands only. + + Fixes: 872f373dc50f7 ("doc: Add JSON schema documentation") + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + doc/libnftables-json.adoc | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/doc/libnftables-json.adoc b/doc/libnftables-json.adoc +index 27c96c3..ada53c4 100644 +--- a/doc/libnftables-json.adoc ++++ b/doc/libnftables-json.adoc +@@ -200,6 +200,18 @@ Rename a chain. The new name is expected in a dedicated property named + + == RULESET ELEMENTS + ++=== RULESET ++[verse] ++____ ++*{ "ruleset":* 'RULSET_PROPERTIES' *}* ++ ++'RULESET_PROPERTIES' := *null* | *{ "family":* 'STRING' *}* ++____ ++ ++This is a special object for use with *list* and *flush* commands which will ++then operate on either the whole ruleset or the parts of it belonging to the ++given family. ++ + === TABLE + [verse] + ____ diff --git a/0081-rule-skip-CMD_OBJ_SETELEMS-with-no-elements-after-se.patch b/0081-rule-skip-CMD_OBJ_SETELEMS-with-no-elements-after-se.patch new file mode 100644 index 0000000..2a4090a --- /dev/null +++ b/0081-rule-skip-CMD_OBJ_SETELEMS-with-no-elements-after-se.patch @@ -0,0 +1,41 @@ +From e4980d25274d5afc8360b0fd704f5d1b18d22ff6 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:32:19 +0200 +Subject: [PATCH] rule: skip CMD_OBJ_SETELEMS with no elements after set flush + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 8b7a533f8f8b276bfa71dcb306d6857e54015234 + +commit 8b7a533f8f8b276bfa71dcb306d6857e54015234 +Author: Pablo Neira Ayuso +Date: Thu Nov 20 00:41:13 2025 +0100 + + rule: skip CMD_OBJ_SETELEMS with no elements after set flush + + Set declaration + set flush results in a crash because CMD_OBJ_SETELEMS + does not expect no elements. This internal command only shows up if set + contains elements, however, evaluation flushes set content after the set + expansion. Skip this command CMD_OBJ_SETELEMS if set is empty. + + Fixes: d3c8051cb767 ("rule: rework CMD_OBJ_SETELEMS logic") + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/rule.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/rule.c b/src/rule.c +index 3b75736..0d6186a 100644 +--- a/src/rule.c ++++ b/src/rule.c +@@ -1461,6 +1461,9 @@ static int do_add_setelems(struct netlink_ctx *ctx, struct cmd *cmd, + { + struct set *set = cmd->set; + ++ if (!set->init) ++ return 0; ++ + return __do_add_elements(ctx, cmd, set, set->init, flags); + } + diff --git a/0082-tests-json_echo-Drop-rule-handle-before-multi-add.patch b/0082-tests-json_echo-Drop-rule-handle-before-multi-add.patch new file mode 100644 index 0000000..6af9a79 --- /dev/null +++ b/0082-tests-json_echo-Drop-rule-handle-before-multi-add.patch @@ -0,0 +1,38 @@ +From 00d91f581bd0f8df0796c10aa1bdaaa93097be69 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:32:19 +0200 +Subject: [PATCH] tests: json_echo: Drop rule handle before multi-add + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit ad74cbd63a9782e8285f7198a5055d7947998b7f + +commit ad74cbd63a9782e8285f7198a5055d7947998b7f +Author: Phil Sutter +Date: Tue Jan 20 22:47:02 2026 +0100 + + tests: json_echo: Drop rule handle before multi-add + + Now that JSON parser respects rule handles in explicit add commands, the + still present rule handle causes an error since the old rule does not + exist anymore. + + Fixes: 50b5b71ebeee3 ("parser_json: Rewrite echo support") + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + tests/json_echo/run-test.py | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tests/json_echo/run-test.py b/tests/json_echo/run-test.py +index a6bdfc6..7217746 100755 +--- a/tests/json_echo/run-test.py ++++ b/tests/json_echo/run-test.py +@@ -286,6 +286,7 @@ add_quota["add"]["quota"]["name"] = "q" + do_flush() + + print("doing multi add") ++del(add_rule["add"]["rule"]["handle"]) + add_multi = [ add_table, add_chain, add_set, add_rule ] + out = do_command(add_multi) + diff --git a/0083-monitor-fix-memleak-in-setelem-cb.patch b/0083-monitor-fix-memleak-in-setelem-cb.patch new file mode 100644 index 0000000..b72e1d3 --- /dev/null +++ b/0083-monitor-fix-memleak-in-setelem-cb.patch @@ -0,0 +1,53 @@ +From 62de57c6207d475b952b54a668db811ac0933d89 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:32:20 +0200 +Subject: [PATCH] monitor: fix memleak in setelem cb + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit d2a427c4abfadd2ce51bdab54c40fbf3d990c724 + +commit d2a427c4abfadd2ce51bdab54c40fbf3d990c724 +Author: Florian Westphal +Date: Wed Jan 21 14:33:21 2026 +0100 + + monitor: fix memleak in setelem cb + + since 4521732ebbf3 ("monitor: missing cache and set handle initialization") + these fields are set via handle_merge(), so don't clobber those + fields in json output case: + + ==31877==ERROR: LeakSanitizer: detected memory leaks + Direct leak of 16 byte(s) in 2 object(s) allocated from: + #0 0x7f0cb9f29d4b in strdup asan/asan_interceptors.cpp:593 + #1 0x7f0cb9b584fd in xstrdup src/utils.c:80 + #2 0x7f0cb9b355b3 in handle_merge src/rule.c:127 + #3 0x7f0cb9ae12b8 in netlink_events_setelem_cb src/monitor.c:457 + + Seen when running tests/monitor with asan enabled. + + Fixes: 4521732ebbf3 ("monitor: missing cache and set handle initialization") + Signed-off-by: Florian Westphal + +Signed-off-by: Phil Sutter +--- + src/monitor.c | 6 ------ + 1 file changed, 6 deletions(-) + +diff --git a/src/monitor.c b/src/monitor.c +index 87ff2a3..0b00207 100644 +--- a/src/monitor.c ++++ b/src/monitor.c +@@ -476,13 +476,7 @@ static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type, + nft_mon_print(monh, "\n"); + break; + case NFTNL_OUTPUT_JSON: +- dummyset->handle.family = family; +- dummyset->handle.set.name = setname; +- dummyset->handle.table.name = table; + monitor_print_element_json(monh, cmd, dummyset); +- /* prevent set_free() from trying to free those */ +- dummyset->handle.set.name = NULL; +- dummyset->handle.table.name = NULL; + if (!nft_output_echo(&monh->ctx->nft->output)) + nft_mon_print(monh, "\n"); + break; diff --git a/0084-segtree-Fix-range-aggregation-on-Big-Endian.patch b/0084-segtree-Fix-range-aggregation-on-Big-Endian.patch new file mode 100644 index 0000000..4e9537e --- /dev/null +++ b/0084-segtree-Fix-range-aggregation-on-Big-Endian.patch @@ -0,0 +1,69 @@ +From 5cf2ee0c11f8d985b7b060db9ddf769f7e8e8ed0 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:32:20 +0200 +Subject: [PATCH] segtree: Fix range aggregation on Big Endian + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit e8b17865833b8c2b9f31a4dfb98069651572494d + +commit e8b17865833b8c2b9f31a4dfb98069651572494d +Author: Phil Sutter +Date: Tue Oct 21 18:36:10 2025 +0200 + + segtree: Fix range aggregation on Big Endian + + Interface name wildcards are received as ranges from prefix with zero + padding to prefix with ones padding. E.g. with "abcd*" (in hex): + + 61626364000000000000000000000000 - 61626364ffffffffffffffffffffffff + + The faulty code tries to export the prefix from the lower boundary (r1) + into a buffer, append "*" and allocate a constant expression from the + resulting string. This does not work on Big Endian though: + mpz_export_data() seems to zero-pad data upon export and not necessarily + respect the passed length value. Moreover, this padding appears in the + first bytes of the buffer. The amount of padding seems illogical, too: + While a 6B prefix causes 2B padding and 8B prefix no padding, 10B prefix + causes 4B padding and 12B prefix even 8B padding. + + Work around the odd behaviour by exporting the full data into a larger + buffer. + + A similar issue is caused by increasing the constant expression's length + to match the upper boundary data length: Data export when printing puts + the padding upfront, so the resulting string starts with NUL-chars. + Since this length adjustment seems not to have any effect in practice, + just drop it. + + Fixes: 88b2345a215ef ("segtree: add pretty-print support for wildcard strings in concatenated sets") + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + src/segtree.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/segtree.c b/src/segtree.c +index a3a0fb4..2b4c4af 100644 +--- a/src/segtree.c ++++ b/src/segtree.c +@@ -408,16 +408,16 @@ void concat_range_aggregate(struct expr *set) + if (prefix_len >= 0 && + (prefix_len % BITS_PER_BYTE) == 0 && + string_type) { ++ unsigned int r1len = div_round_up(r1->len, BITS_PER_BYTE); + unsigned int str_len = prefix_len / BITS_PER_BYTE; +- char data[str_len + 2]; ++ char data[r1len + 1] = {}; + +- mpz_export_data(data, r1->value, BYTEORDER_HOST_ENDIAN, str_len); ++ mpz_export_data(data, r1->value, BYTEORDER_HOST_ENDIAN, r1len); + data[str_len] = '*'; + + tmp = constant_expr_alloc(&r1->location, r1->dtype, + BYTEORDER_HOST_ENDIAN, + (str_len + 1) * BITS_PER_BYTE, data); +- tmp->len = r2->len; + list_replace(&r2->list, &tmp->list); + r2_next = tmp->list.next; + expr_free(r2); diff --git a/0085-mergesort-Fix-sorting-of-string-values.patch b/0085-mergesort-Fix-sorting-of-string-values.patch new file mode 100644 index 0000000..3c5c6b3 --- /dev/null +++ b/0085-mergesort-Fix-sorting-of-string-values.patch @@ -0,0 +1,244 @@ +From 01bd864e3c9daf0121516109af0e7aca1ebfae38 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:32:20 +0200 +Subject: [PATCH] mergesort: Fix sorting of string values + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 340d904a974ed206fcc4d8ca32540762fd8e59e0 +Conflicts: Dropped changes to non-existent dump files + +commit 340d904a974ed206fcc4d8ca32540762fd8e59e0 +Author: Phil Sutter +Date: Thu Nov 13 00:03:37 2025 +0100 + + mergesort: Fix sorting of string values + + Sorting order was obviously wrong, e.g. "ppp0" ordered before "eth1". + Moreover, this happened on Little Endian only so sorting order actually + depended on host's byteorder. By reimporting string values as Big + Endian, both issues are fixed: On one hand, GMP-internal byteorder no + longer depends on host's byteorder, on the other comparing strings + really starts with the first character, not the last. + + Fixes: 14ee0a979b622 ("src: sort set elements in netlink_get_setelems()") + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + src/mergesort.c | 7 +++ + tests/py/any/meta.t.json.output | 54 ------------------- + tests/py/any/queue.t.json.output | 4 +- + tests/py/inet/osf.t.json.output | 54 +++++++++++++++++++ + .../shell/testcases/maps/dumps/0012map_0.nft | 8 +-- + .../sets/dumps/sets_with_ifnames.nft | 2 +- + 6 files changed, 68 insertions(+), 61 deletions(-) + +diff --git a/src/mergesort.c b/src/mergesort.c +index 4d0e280..4157b5b 100644 +--- a/src/mergesort.c ++++ b/src/mergesort.c +@@ -37,6 +37,13 @@ static mpz_srcptr expr_msort_value(const struct expr *expr, mpz_t value) + case EXPR_RANGE: + return expr_msort_value(expr->left, value); + case EXPR_VALUE: ++ if (expr_basetype(expr)->type == TYPE_STRING) { ++ char buf[expr->len]; ++ ++ mpz_export_data(buf, expr->value, BYTEORDER_HOST_ENDIAN, expr->len); ++ mpz_import_data(value, buf, BYTEORDER_BIG_ENDIAN, expr->len); ++ return value; ++ } + return expr->value; + case EXPR_CONCAT: + concat_expr_msort_value(expr, value); +diff --git a/tests/py/any/meta.t.json.output b/tests/py/any/meta.t.json.output +index 4e9e669..329073e 100644 +--- a/tests/py/any/meta.t.json.output ++++ b/tests/py/any/meta.t.json.output +@@ -233,60 +233,6 @@ + } + ] + +-# meta iifname {"dummy0", "lo"} +-[ +- { +- "match": { +- "left": { +- "meta": { "key": "iifname" } +- }, +- "op": "==", +- "right": { +- "set": [ +- "lo", +- "dummy0" +- ] +- } +- } +- } +-] +- +-# meta iifname != {"dummy0", "lo"} +-[ +- { +- "match": { +- "left": { +- "meta": { "key": "iifname" } +- }, +- "op": "!=", +- "right": { +- "set": [ +- "lo", +- "dummy0" +- ] +- } +- } +- } +-] +- +-# meta oifname { "dummy0", "lo"} +-[ +- { +- "match": { +- "left": { +- "meta": { "key": "oifname" } +- }, +- "op": "==", +- "right": { +- "set": [ +- "lo", +- "dummy0" +- ] +- } +- } +- } +-] +- + # meta skuid {"bin", "root", "daemon"} accept + [ + { +diff --git a/tests/py/any/queue.t.json.output b/tests/py/any/queue.t.json.output +index ea37223..90670cc 100644 +--- a/tests/py/any/queue.t.json.output ++++ b/tests/py/any/queue.t.json.output +@@ -104,11 +104,11 @@ + 0 + ], + [ +- "ppp0", ++ "eth1", + 2 + ], + [ +- "eth1", ++ "ppp0", + 2 + ] + ] +diff --git a/tests/py/inet/osf.t.json.output b/tests/py/inet/osf.t.json.output +index 922e395..77ca7e3 100644 +--- a/tests/py/inet/osf.t.json.output ++++ b/tests/py/inet/osf.t.json.output +@@ -18,6 +18,26 @@ + } + ] + ++# osf version { "Windows:XP", "MacOs:Sierra" } ++[ ++ { ++ "match": { ++ "left": { ++ "osf": { ++ "key": "version" ++ } ++ }, ++ "op": "==", ++ "right": { ++ "set": [ ++ "MacOs:Sierra", ++ "Windows:XP" ++ ] ++ } ++ } ++ } ++] ++ + # ct mark set osf name map { "Windows" : 0x00000001, "MacOs" : 0x00000002 } + [ + { +@@ -51,3 +71,37 @@ + } + } + ] ++ ++# ct mark set osf version map { "Windows:XP" : 0x00000003, "MacOs:Sierra" : 0x00000004 } ++[ ++ { ++ "mangle": { ++ "key": { ++ "ct": { ++ "key": "mark" ++ } ++ }, ++ "value": { ++ "map": { ++ "data": { ++ "set": [ ++ [ ++ "MacOs:Sierra", ++ 4 ++ ], ++ [ ++ "Windows:XP", ++ 3 ++ ] ++ ] ++ }, ++ "key": { ++ "osf": { ++ "key": "version" ++ } ++ } ++ } ++ } ++ } ++ } ++] +diff --git a/tests/shell/testcases/maps/dumps/0012map_0.nft b/tests/shell/testcases/maps/dumps/0012map_0.nft +index 895490c..b199a00 100644 +--- a/tests/shell/testcases/maps/dumps/0012map_0.nft ++++ b/tests/shell/testcases/maps/dumps/0012map_0.nft +@@ -1,9 +1,9 @@ + table ip x { + map z { + type ifname : verdict +- elements = { "lo" : accept, +- "eth0" : drop, +- "eth1" : drop } ++ elements = { "eth0" : drop, ++ "eth1" : drop, ++ "lo" : accept } + } + + map w { +@@ -14,7 +14,7 @@ table ip x { + } + + chain y { +- iifname vmap { "lo" : accept, "eth0" : drop, "eth1" : drop } ++ iifname vmap { "eth0" : drop, "eth1" : drop, "lo" : accept } + } + + chain k { +diff --git a/tests/shell/testcases/sets/dumps/sets_with_ifnames.nft b/tests/shell/testcases/sets/dumps/sets_with_ifnames.nft +index 77a8baf..8abca03 100644 +--- a/tests/shell/testcases/sets/dumps/sets_with_ifnames.nft ++++ b/tests/shell/testcases/sets/dumps/sets_with_ifnames.nft +@@ -39,7 +39,7 @@ table inet testifsets { + chain v4icmp { + iifname @simple counter packets 0 bytes 0 + iifname @simple_wild counter packets 0 bytes 0 +- iifname { "eth0", "abcdef0" } counter packets 0 bytes 0 ++ iifname { "abcdef0", "eth0" } counter packets 0 bytes 0 + iifname { "abcdef*", "eth0" } counter packets 0 bytes 0 + iifname vmap @map_wild + } diff --git a/0086-optimize-expand-expression-list-when-merging-into-co.patch b/0086-optimize-expand-expression-list-when-merging-into-co.patch new file mode 100644 index 0000000..af025d4 --- /dev/null +++ b/0086-optimize-expand-expression-list-when-merging-into-co.patch @@ -0,0 +1,84 @@ +From c0a28182c114262bc1d4f4557091b0f0ab71c656 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:32:36 +0200 +Subject: [PATCH] optimize: expand expression list when merging into + concatenation + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 0d17d28bb06bf2a04862d5cd879a14bcb9a2d2dc +Conflicts: Drop changes to non-existent dump file + +commit 0d17d28bb06bf2a04862d5cd879a14bcb9a2d2dc +Author: Pablo Neira Ayuso +Date: Tue Apr 1 18:11:45 2025 +0200 + + optimize: expand expression list when merging into concatenation + + The following rules: + + udp dport 137 ct state new,untracked accept + udp dport 138 ct state new,untracked accept + + results in: + + nft: src/optimize.c:670: __merge_concat: Assertion `0' failed. + + The logic to expand to the new,untracked list in the concatenation is + missing. + + Fixes: 187c6d01d357 ("optimize: expand implicit set element when merging into concatenation") + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/optimize.c | 10 ++++++++++ + .../optimizations/dumps/merge_stmts_concat.nft | 1 + + tests/shell/testcases/optimizations/merge_stmts_concat | 2 ++ + 3 files changed, 13 insertions(+) + +diff --git a/src/optimize.c b/src/optimize.c +index 7b4d036..dd96fc7 100644 +--- a/src/optimize.c ++++ b/src/optimize.c +@@ -635,6 +635,16 @@ static void __merge_concat(const struct optimize_ctx *ctx, uint32_t i, + clone = expr_clone(stmt_a->expr->right); + compound_expr_add(concat, clone); + break; ++ case EXPR_LIST: ++ list_for_each_entry(expr, &stmt_a->expr->right->expressions, list) { ++ concat_clone = expr_clone(concat); ++ clone = expr_clone(expr); ++ compound_expr_add(concat_clone, clone); ++ list_add_tail(&concat_clone->list, &pending_list); ++ } ++ list_del(&concat->list); ++ expr_free(concat); ++ break; + default: + assert(0); + break; +diff --git a/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft +index f56cea1..d00ac41 100644 +--- a/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft ++++ b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft +@@ -2,6 +2,7 @@ table ip x { + chain y { + iifname . ip saddr . ip daddr { "eth1" . 1.1.1.1 . 2.2.2.3, "eth1" . 1.1.1.2 . 2.2.2.4, "eth1" . 1.1.1.2 . 2.2.3.0/24, "eth1" . 1.1.1.2 . 2.2.4.0-2.2.4.10, "eth2" . 1.1.1.3 . 2.2.2.5 } accept + ip protocol . th dport { tcp . 22, udp . 67 } ++ udp dport . ct state { 137 . new, 138 . new, 137 . untracked, 138 . untracked } accept + } + + chain c1 { +diff --git a/tests/shell/testcases/optimizations/merge_stmts_concat b/tests/shell/testcases/optimizations/merge_stmts_concat +index 9679d86..1fd1a30 100755 +--- a/tests/shell/testcases/optimizations/merge_stmts_concat ++++ b/tests/shell/testcases/optimizations/merge_stmts_concat +@@ -10,6 +10,8 @@ RULESET="table ip x { + meta iifname eth1 ip saddr 1.1.1.2 ip daddr 2.2.4.0-2.2.4.10 accept + meta iifname eth2 ip saddr 1.1.1.3 ip daddr 2.2.2.5 accept + ip protocol . th dport { tcp . 22, udp . 67 } ++ udp dport 137 ct state new,untracked accept ++ udp dport 138 ct state new,untracked accept + } + }" + diff --git a/0087-mergesort-Align-concatenation-sort-order-with-Big-En.patch b/0087-mergesort-Align-concatenation-sort-order-with-Big-En.patch new file mode 100644 index 0000000..ff607a0 --- /dev/null +++ b/0087-mergesort-Align-concatenation-sort-order-with-Big-En.patch @@ -0,0 +1,228 @@ +From b676be66a623fd718cb7bc5875e9193e8c4f0b35 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:32:51 +0200 +Subject: [PATCH] mergesort: Align concatenation sort order with Big Endian + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 0d819bcbbf36d324b5344ac29bd82a5017098c4b +Conflicts: Drop changes to non-existent dumps and test cases + +commit 0d819bcbbf36d324b5344ac29bd82a5017098c4b +Author: Phil Sutter +Date: Thu Nov 13 00:14:43 2025 +0100 + + mergesort: Align concatenation sort order with Big Endian + + By exporting all concat components in a way independent from host + byteorder and importing that blob of data in the same way aligns sort + order between hosts of different Endianness. + + Fixes: 741a06ac15d2b ("mergesort: find base value expression type via recursion") + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + src/mergesort.c | 4 ++-- + tests/py/any/ct.t.json.output | 20 +++++++++---------- + .../testcases/maps/dumps/typeof_integer_0.nft | 4 ++-- + .../nft-f/dumps/0012different_defines_0.nft | 2 +- + .../optimizations/dumps/merge_reject.nft | 4 ++-- + .../dumps/merge_stmts_concat.nft | 8 ++++---- + .../dumps/merge_stmts_concat_vmap.nft | 2 +- + .../sets/dumps/0029named_ifname_dtype_0.nft | 4 ++-- + .../dumps/0037_set_with_inet_service_0.nft | 8 ++++---- + 9 files changed, 28 insertions(+), 28 deletions(-) + +diff --git a/src/mergesort.c b/src/mergesort.c +index 4157b5b..5c4cfeb 100644 +--- a/src/mergesort.c ++++ b/src/mergesort.c +@@ -20,11 +20,11 @@ static void concat_expr_msort_value(const struct expr *expr, mpz_t value) + + list_for_each_entry(i, &expr->expressions, list) { + ilen = div_round_up(i->len, BITS_PER_BYTE); +- mpz_export_data(data + len, i->value, i->byteorder, ilen); ++ mpz_export_data(data + len, i->value, BYTEORDER_BIG_ENDIAN, ilen); + len += ilen; + } + +- mpz_import_data(value, data, BYTEORDER_HOST_ENDIAN, len); ++ mpz_import_data(value, data, BYTEORDER_BIG_ENDIAN, len); + } + + static mpz_srcptr expr_msort_value(const struct expr *expr, mpz_t value) +diff --git a/tests/py/any/ct.t.json.output b/tests/py/any/ct.t.json.output +index 82634c2..3f79593 100644 +--- a/tests/py/any/ct.t.json.output ++++ b/tests/py/any/ct.t.json.output +@@ -494,14 +494,14 @@ + "set": [ + { + "concat": [ +- "new", +- 305419896 ++ "established", ++ 309876276 + ] + }, + { + "concat": [ +- "established", +- 309876276 ++ "new", ++ 305419896 + ] + }, + { +@@ -578,23 +578,23 @@ + [ + { + "concat": [ +- "new", +- 305419896 ++ "established", ++ 2271560481 + ] + }, + { +- "drop": null ++ "accept": null + } + ], + [ + { + "concat": [ +- "established", +- 2271560481 ++ "new", ++ 305419896 + ] + }, + { +- "accept": null ++ "drop": null + } + ] + ] +diff --git a/tests/shell/testcases/maps/dumps/typeof_integer_0.nft b/tests/shell/testcases/maps/dumps/typeof_integer_0.nft +index 19c24fe..7bd7daa 100644 +--- a/tests/shell/testcases/maps/dumps/typeof_integer_0.nft ++++ b/tests/shell/testcases/maps/dumps/typeof_integer_0.nft +@@ -8,8 +8,8 @@ table inet t { + + map m2 { + typeof udp length . @ih,32,32 : verdict +- elements = { 30 . 0x1e : drop, +- 20 . 0x24 : accept } ++ elements = { 20 . 0x24 : accept, ++ 30 . 0x1e : drop } + } + + chain c { +diff --git a/tests/shell/testcases/nft-f/dumps/0012different_defines_0.nft b/tests/shell/testcases/nft-f/dumps/0012different_defines_0.nft +index 4734b2f..a6e16e7 100644 +--- a/tests/shell/testcases/nft-f/dumps/0012different_defines_0.nft ++++ b/tests/shell/testcases/nft-f/dumps/0012different_defines_0.nft +@@ -8,7 +8,7 @@ table inet t { + ip6 daddr fe0::1 ip6 saddr fe0::2 + ip saddr vmap { 10.0.0.0 : drop, 10.0.0.2 : accept } + ip6 daddr vmap { fe0::1 : drop, fe0::2 : accept } +- ip6 saddr . ip6 nexthdr { fe0::2 . tcp, fe0::1 . udp } ++ ip6 saddr . ip6 nexthdr { fe0::1 . udp, fe0::2 . tcp } + ip daddr . iif vmap { 10.0.0.0 . "lo" : accept } + tcp dport 100-222 + udp dport vmap { 100-222 : accept } +diff --git a/tests/shell/testcases/optimizations/dumps/merge_reject.nft b/tests/shell/testcases/optimizations/dumps/merge_reject.nft +index c29ad6d..1727d02 100644 +--- a/tests/shell/testcases/optimizations/dumps/merge_reject.nft ++++ b/tests/shell/testcases/optimizations/dumps/merge_reject.nft +@@ -1,13 +1,13 @@ + table ip x { + chain y { + ip daddr 172.30.33.70 tcp dport 3306 counter packets 0 bytes 0 drop +- meta l4proto . ip daddr . tcp dport { tcp . 172.30.238.117 . 8080, tcp . 172.30.33.71 . 3306, tcp . 172.30.254.251 . 3306 } counter packets 0 bytes 0 reject ++ meta l4proto . ip daddr . tcp dport { tcp . 172.30.33.71 . 3306, tcp . 172.30.238.117 . 8080, tcp . 172.30.254.251 . 3306 } counter packets 0 bytes 0 reject + ip daddr 172.30.254.252 tcp dport 3306 counter packets 0 bytes 0 reject with tcp reset + } + } + table ip6 x { + chain y { +- meta l4proto . ip6 daddr . tcp dport { tcp . aaaa::3 . 8080, tcp . aaaa::2 . 3306, tcp . aaaa::4 . 3306 } counter packets 0 bytes 0 reject ++ meta l4proto . ip6 daddr . tcp dport { tcp . aaaa::2 . 3306, tcp . aaaa::3 . 8080, tcp . aaaa::4 . 3306 } counter packets 0 bytes 0 reject + ip6 daddr aaaa::5 tcp dport 3306 counter packets 0 bytes 0 reject with tcp reset + } + } +diff --git a/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft +index d00ac41..6150258 100644 +--- a/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft ++++ b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft +@@ -2,18 +2,18 @@ table ip x { + chain y { + iifname . ip saddr . ip daddr { "eth1" . 1.1.1.1 . 2.2.2.3, "eth1" . 1.1.1.2 . 2.2.2.4, "eth1" . 1.1.1.2 . 2.2.3.0/24, "eth1" . 1.1.1.2 . 2.2.4.0-2.2.4.10, "eth2" . 1.1.1.3 . 2.2.2.5 } accept + ip protocol . th dport { tcp . 22, udp . 67 } +- udp dport . ct state { 137 . new, 138 . new, 137 . untracked, 138 . untracked } accept ++ udp dport . ct state { 137 . new, 137 . untracked, 138 . new, 138 . untracked } accept + } + + chain c1 { +- udp dport . iifname { 51820 . "foo", 514 . "bar", 67 . "bar" } accept ++ udp dport . iifname { 67 . "bar", 514 . "bar", 51820 . "foo" } accept + } + + chain c2 { +- udp dport . iifname { 100 . "foo", 51820 . "foo", 514 . "bar", 67 . "bar" } accept ++ udp dport . iifname { 67 . "bar", 100 . "foo", 514 . "bar", 51820 . "foo" } accept + } + + chain c3 { +- udp dport . iifname { 100 . "foo", 51820 . "foo", 514 . "bar", 67 . "bar", 100 . "test", 51820 . "test" } accept ++ udp dport . iifname { 67 . "bar", 100 . "foo", 100 . "test", 514 . "bar", 51820 . "foo", 51820 . "test" } accept + } + } +diff --git a/tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.nft b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.nft +index 780aa09..81abb99 100644 +--- a/tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.nft ++++ b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.nft +@@ -1,6 +1,6 @@ + table ip x { + chain x { +- meta pkttype . udp dport vmap { broadcast . 547 : accept, broadcast . 67 : accept, multicast . 1900 : drop } ++ meta pkttype . udp dport vmap { broadcast . 67 : accept, broadcast . 547 : accept, multicast . 1900 : drop } + } + + chain y { +diff --git a/tests/shell/testcases/sets/dumps/0029named_ifname_dtype_0.nft b/tests/shell/testcases/sets/dumps/0029named_ifname_dtype_0.nft +index 55cd4f2..0ea2926 100644 +--- a/tests/shell/testcases/sets/dumps/0029named_ifname_dtype_0.nft ++++ b/tests/shell/testcases/sets/dumps/0029named_ifname_dtype_0.nft +@@ -12,8 +12,8 @@ table inet t { + type inet_service . ifname + elements = { 22 . "eth0", + 80 . "eth0", +- 81 . "eth0", +- 80 . "eth1" } ++ 80 . "eth1", ++ 81 . "eth0" } + } + + set nv { +diff --git a/tests/shell/testcases/sets/dumps/0037_set_with_inet_service_0.nft b/tests/shell/testcases/sets/dumps/0037_set_with_inet_service_0.nft +index 68b1f7b..0e85f7c 100644 +--- a/tests/shell/testcases/sets/dumps/0037_set_with_inet_service_0.nft ++++ b/tests/shell/testcases/sets/dumps/0037_set_with_inet_service_0.nft +@@ -1,11 +1,11 @@ + table inet filter { + set myset { + type ipv4_addr . inet_proto . inet_service +- elements = { 192.168.0.113 . tcp . 22, +- 192.168.0.12 . tcp . 53, +- 192.168.0.12 . udp . 53, ++ elements = { 192.168.0.12 . tcp . 53, + 192.168.0.12 . tcp . 80, +- 192.168.0.13 . tcp . 80 } ++ 192.168.0.12 . udp . 53, ++ 192.168.0.13 . tcp . 80, ++ 192.168.0.113 . tcp . 22 } + } + + chain forward { diff --git a/0088-json-complete-multi-statement-set-element-support.patch b/0088-json-complete-multi-statement-set-element-support.patch new file mode 100644 index 0000000..0d0e441 --- /dev/null +++ b/0088-json-complete-multi-statement-set-element-support.patch @@ -0,0 +1,41 @@ +From d4c926c17ef1383d37d1c101f12b0d4b5712546c Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:33:22 +0200 +Subject: [PATCH] json: complete multi-statement set element support + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 14dc90737a57bd1c6dc0ebc3eb30ab3090cc0afb +Conflicts: Drop changes to non-existent test cases + +commit 14dc90737a57bd1c6dc0ebc3eb30ab3090cc0afb +Author: Pablo Neira Ayuso +Date: Mon Feb 16 12:26:45 2026 +0100 + + json: complete multi-statement set element support + + Remove artificial limitation on the maximum number of statements per + element in listings. + + Moreover, update tests/shell which are currently incorrect. + + Fixes: e6d1d0d61195 ("src: add set element multi-statement support") + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/json.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/src/json.c b/src/json.c +index fc2e962..0d3b535 100644 +--- a/src/json.c ++++ b/src/json.c +@@ -705,8 +705,6 @@ json_t *set_elem_expr_json(const struct expr *expr, struct output_ctx *octx) + /* XXX: detect and complain about clashes? */ + json_object_update_missing(root, tmp); + json_decref(tmp); +- /* TODO: only one statement per element. */ +- break; + } + return nft_json_pack("{s:o}", "elem", root); + } diff --git a/0089-cache-Respect-family-in-all-list-commands.patch b/0089-cache-Respect-family-in-all-list-commands.patch new file mode 100644 index 0000000..858a532 --- /dev/null +++ b/0089-cache-Respect-family-in-all-list-commands.patch @@ -0,0 +1,117 @@ +From cdcae37b8fc86c232ccb64e9d6feb98b455abf39 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:33:37 +0200 +Subject: [PATCH] cache: Respect family in all list commands + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit f4a5334f503dc9d99bce45020c105b6812dd15f0 + +commit f4a5334f503dc9d99bce45020c105b6812dd15f0 +Author: Phil Sutter +Date: Sat Feb 14 14:58:03 2026 +0100 + + cache: Respect family in all list commands + + Some list commands did not set filter->list.family even if one was given + on command line, fix this. + + Fixes: b3ed8fd8c9f33 ("cache: missing family in cache filtering") + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + Tested-by: Eric Garver + +Signed-off-by: Phil Sutter +--- + src/cache.c | 6 ++- + tests/shell/testcases/listing/cache_filters | 46 +++++++++++++++++++++ + 2 files changed, 51 insertions(+), 1 deletion(-) + create mode 100755 tests/shell/testcases/listing/cache_filters + +diff --git a/src/cache.c b/src/cache.c +index 2330112..40b6c01 100644 +--- a/src/cache.c ++++ b/src/cache.c +@@ -234,10 +234,12 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd, + flags |= NFT_CACHE_FULL; + break; + case CMD_OBJ_CHAINS: ++ filter->list.family = cmd->handle.family; + flags |= NFT_CACHE_TABLE | NFT_CACHE_CHAIN; + break; + case CMD_OBJ_SETS: + case CMD_OBJ_MAPS: ++ filter->list.family = cmd->handle.family; + flags |= NFT_CACHE_TABLE | NFT_CACHE_SET; + if (!nft_output_terse(&nft->output)) + flags |= NFT_CACHE_SETELEM; +@@ -246,15 +248,17 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd, + if (filter && + cmd->handle.table.name && + cmd->handle.flowtable.name) { +- filter->list.family = cmd->handle.family; + filter->list.table = cmd->handle.table.name; + filter->list.ft = cmd->handle.flowtable.name; + } + /* fall through */ + case CMD_OBJ_FLOWTABLES: ++ filter->list.family = cmd->handle.family; + flags |= NFT_CACHE_TABLE | NFT_CACHE_FLOWTABLE; + break; + case CMD_OBJ_RULESET: ++ filter->list.family = cmd->handle.family; ++ /* fall through */ + default: + flags |= NFT_CACHE_FULL; + break; +diff --git a/tests/shell/testcases/listing/cache_filters b/tests/shell/testcases/listing/cache_filters +new file mode 100755 +index 0000000..37c8f84 +--- /dev/null ++++ b/tests/shell/testcases/listing/cache_filters +@@ -0,0 +1,46 @@ ++#!/bin/bash ++ ++set -e ++ ++fail() { ++ echo "$*" ++ exit 1 ++} ++ ++$NFT -f - < +Date: Tue, 30 Jun 2026 14:33:37 +0200 +Subject: [PATCH] cache: Relax chain_cache_dump filter application + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 18cd6edf86139621dc6bfe0f17a391e46218c027 + +commit 18cd6edf86139621dc6bfe0f17a391e46218c027 +Author: Phil Sutter +Date: Fri Mar 6 17:02:17 2026 +0100 + + cache: Relax chain_cache_dump filter application + + While populating chain cache, a filter was only effective if it limited + fetching to both a table and a chain. Make it apply to 'list chains' + command as well which at most specifies a family and table. + + Since the code is OK with filter->list fields being NULL, merely check + for filter to be non-NULL (which is the case if nft_cache_update() is + called by nft_cmd_enoent_chain()). + + Fixes: 17297d1acbbf ("cache: Filter chain list on kernel side") + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + Tested-by: Eric Garver + +Signed-off-by: Phil Sutter +--- + src/cache.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/cache.c b/src/cache.c +index 40b6c01..b8c065d 100644 +--- a/src/cache.c ++++ b/src/cache.c +@@ -559,7 +559,7 @@ chain_cache_dump(struct netlink_ctx *ctx, + const char *chain = NULL; + int family = NFPROTO_UNSPEC; + +- if (filter && filter->list.table && filter->list.chain) { ++ if (filter) { + family = filter->list.family; + table = filter->list.table; + chain = filter->list.chain; diff --git a/0091-cache-Filter-for-table-when-listing-sets-or-maps.patch b/0091-cache-Filter-for-table-when-listing-sets-or-maps.patch new file mode 100644 index 0000000..779bc9c --- /dev/null +++ b/0091-cache-Filter-for-table-when-listing-sets-or-maps.patch @@ -0,0 +1,62 @@ +From 8f721b98622ac4c68a2ad30b610037016ebc0312 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:33:37 +0200 +Subject: [PATCH] cache: Filter for table when listing sets or maps + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit b7225cda2a4388f38293fba281fc001dff70a16e + +commit b7225cda2a4388f38293fba281fc001dff70a16e +Author: Phil Sutter +Date: Sat Feb 14 15:02:04 2026 +0100 + + cache: Filter for table when listing sets or maps + + Respect an optionally specified table name to filter listed sets or maps + to by populating the filter accordingly. + + Fixes: a1a6b0a5c3c4 ("cache: finer grain cache population for list commands") + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + Tested-by: Eric Garver + +Signed-off-by: Phil Sutter +--- + src/cache.c | 1 + + tests/shell/testcases/listing/cache_filters | 4 ++++ + 2 files changed, 5 insertions(+) + +diff --git a/src/cache.c b/src/cache.c +index b8c065d..d448bb9 100644 +--- a/src/cache.c ++++ b/src/cache.c +@@ -240,6 +240,7 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd, + case CMD_OBJ_SETS: + case CMD_OBJ_MAPS: + filter->list.family = cmd->handle.family; ++ filter->list.table = cmd->handle.table.name; + flags |= NFT_CACHE_TABLE | NFT_CACHE_SET; + if (!nft_output_terse(&nft->output)) + flags |= NFT_CACHE_SETELEM; +diff --git a/tests/shell/testcases/listing/cache_filters b/tests/shell/testcases/listing/cache_filters +index 37c8f84..7a89330 100755 +--- a/tests/shell/testcases/listing/cache_filters ++++ b/tests/shell/testcases/listing/cache_filters +@@ -22,6 +22,8 @@ table ip ip_t { + chain ip_t_c2 { + } + } ++table ip ip_t2 { ++} + EOF + + $NFT --debug=netlink list ruleset | \ +@@ -38,6 +40,8 @@ $NFT --debug=netlink list sets | \ + grep -q 'family 2 ip_t_s ip_t' || fail "broken list sets" + $NFT --debug=netlink list sets ip6 | \ + grep -q 'family 2 ip_t_s ip_t' && fail "broken list sets family filter" ++$NFT --debug=netlink list sets ip ip_t2 | \ ++ grep -q 'family 2 ip_t_s ip_t' && fail "broken list sets table filter" + + $NFT --debug=netlink list flowtables | \ + grep -q 'flow table ip_t ip_t_ft' || fail "broken list flowtables" diff --git a/0092-cache-Filter-for-table-when-listing-flowtables.patch b/0092-cache-Filter-for-table-when-listing-flowtables.patch new file mode 100644 index 0000000..80384b4 --- /dev/null +++ b/0092-cache-Filter-for-table-when-listing-flowtables.patch @@ -0,0 +1,52 @@ +From e778b997601e2c23cf918905873e88ba22c8c06a Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:33:37 +0200 +Subject: [PATCH] cache: Filter for table when listing flowtables + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit fa5d7fba4bcc9b7c5c9ec37bbe8d3df3f8298745 + +commit fa5d7fba4bcc9b7c5c9ec37bbe8d3df3f8298745 +Author: Phil Sutter +Date: Wed Feb 25 21:07:32 2026 +0100 + + cache: Filter for table when listing flowtables + + Respect an optionally specified table name to filter listed flowtables + to by populating the filter accordingly. + + Fixes: a1a6b0a5c3c4 ("cache: finer grain cache population for list commands") + Signed-off-by: Phil Sutter + Reviewed-by: Pablo Neira Ayuso + Tested-by: Eric Garver + +Signed-off-by: Phil Sutter +--- + src/cache.c | 1 + + tests/shell/testcases/listing/cache_filters | 3 +++ + 2 files changed, 4 insertions(+) + +diff --git a/src/cache.c b/src/cache.c +index d448bb9..dd159ea 100644 +--- a/src/cache.c ++++ b/src/cache.c +@@ -255,6 +255,7 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd, + /* fall through */ + case CMD_OBJ_FLOWTABLES: + filter->list.family = cmd->handle.family; ++ filter->list.table = cmd->handle.table.name; + flags |= NFT_CACHE_TABLE | NFT_CACHE_FLOWTABLE; + break; + case CMD_OBJ_RULESET: +diff --git a/tests/shell/testcases/listing/cache_filters b/tests/shell/testcases/listing/cache_filters +index 7a89330..e3d0e5e 100755 +--- a/tests/shell/testcases/listing/cache_filters ++++ b/tests/shell/testcases/listing/cache_filters +@@ -47,4 +47,7 @@ $NFT --debug=netlink list flowtables | \ + grep -q 'flow table ip_t ip_t_ft' || fail "broken list flowtables" + $NFT --debug=netlink list flowtables ip6 | \ + grep -q 'flow table ip_t ip_t_ft' && fail "broken list flowtables family filter" ++$NFT --debug=netlink list flowtables ip ip_t2 | \ ++ grep -q 'flow table ip_t ip_t_ft' && fail "broken list flowtables table filter" ++ + exit 0 diff --git a/0093-segtree-Fix-for-variable-sized-object-may-not-be-ini.patch b/0093-segtree-Fix-for-variable-sized-object-may-not-be-ini.patch new file mode 100644 index 0000000..647c6c6 --- /dev/null +++ b/0093-segtree-Fix-for-variable-sized-object-may-not-be-ini.patch @@ -0,0 +1,42 @@ +From 3c99a1ec75bdc3adcecfbc150a842aace7ab9af3 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:33:37 +0200 +Subject: [PATCH] segtree: Fix for variable-sized object may not be initialized + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 96ba00889f9d4932d8bc2b08461f05793a9a2ac4 + +commit 96ba00889f9d4932d8bc2b08461f05793a9a2ac4 +Author: Phil Sutter +Date: Wed Mar 18 22:05:34 2026 +0100 + + segtree: Fix for variable-sized object may not be initialized + + Seen with gcc-11.5.0 on an aarch64 machine, build failed. Looking at the + code, r1len (or r1->len, actually) really seems variable. So use + memset() and fix build for that older compiler version at least. + + Fixes: e8b17865833b8 ("segtree: Fix range aggregation on Big Endian") + Signed-off-by: Phil Sutter + Acked-by: Florian Westphal + +Signed-off-by: Phil Sutter +--- + src/segtree.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/segtree.c b/src/segtree.c +index 2b4c4af..05664d1 100644 +--- a/src/segtree.c ++++ b/src/segtree.c +@@ -410,8 +410,9 @@ void concat_range_aggregate(struct expr *set) + string_type) { + unsigned int r1len = div_round_up(r1->len, BITS_PER_BYTE); + unsigned int str_len = prefix_len / BITS_PER_BYTE; +- char data[r1len + 1] = {}; ++ char data[r1len + 1]; + ++ memset(data, 0, r1len + 1); + mpz_export_data(data, r1->value, BYTEORDER_HOST_ENDIAN, r1len); + data[str_len] = '*'; + diff --git a/0094-mnl-Fix-ordering-of-hooks-in-list-hooks-output.patch b/0094-mnl-Fix-ordering-of-hooks-in-list-hooks-output.patch new file mode 100644 index 0000000..8c66439 --- /dev/null +++ b/0094-mnl-Fix-ordering-of-hooks-in-list-hooks-output.patch @@ -0,0 +1,39 @@ +From a0cdde12907855d417322df4acf177bfd9af2236 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:33:38 +0200 +Subject: [PATCH] mnl: Fix ordering of hooks in 'list hooks' output + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 2f3b8d37c2e46f2ddf086c50affcd224b1f7f7dd + +commit 2f3b8d37c2e46f2ddf086c50affcd224b1f7f7dd +Author: Phil Sutter +Date: Fri Mar 20 13:30:52 2026 +0100 + + mnl: Fix ordering of hooks in 'list hooks' output + + Hooks with same family, basehook and priority were inadvertently + inserted into the list in reverse ordering, fix that. + + Suggested-by: Florian Westphal + Fixes: b98fee20bfe23 ("mnl: revisit hook listing") + Signed-off-by: Phil Sutter + +Signed-off-by: Phil Sutter +--- + src/mnl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/mnl.c b/src/mnl.c +index 96a86aa..2b7e1cf 100644 +--- a/src/mnl.c ++++ b/src/mnl.c +@@ -2199,7 +2199,7 @@ static void basehook_list_add_tail(struct basehook *b, struct list_head *head) + continue; + if (hook->num != b->num) + continue; +- if (hook->prio < b->prio) ++ if (hook->prio <= b->prio) + continue; + + list_add(&b->list, &hook->list); diff --git a/0095-cache-honor-c-check-for-reset-commands.patch b/0095-cache-honor-c-check-for-reset-commands.patch new file mode 100644 index 0000000..93f9158 --- /dev/null +++ b/0095-cache-honor-c-check-for-reset-commands.patch @@ -0,0 +1,54 @@ +From 7a7dbc549c9297f93c393eb70c01c24c15cc36be Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:33:38 +0200 +Subject: [PATCH] cache: honor -c/--check for reset commands + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 25f2ff13cf6bc84a16febddcf8c3eda2018c7ece +Conflicts: Adjust to missing commit 68c8fb5f7c988 + ("cache: accumulate flags in batch") + +commit 25f2ff13cf6bc84a16febddcf8c3eda2018c7ece +Author: Pablo Neira Ayuso +Date: Mon May 11 21:51:43 2026 +0200 + + cache: honor -c/--check for reset commands + + Currently: + + nft -c reset rules ip x + + ignores -c/--check. + + The reset and list commands use the netlink GET/DUMP nfnetlink API which + provides no check semantics, compared to the NEW/DELETE nfnetlink batch + API which indeed does. + + Emulate -c/--check for the reset command by handling this as a list + command, so the state of the objects is just listed, not reset. This + allows to check for presence and dump the content of the objects. + + Fixes: dbff26bfba83 ("cache: consolidate reset command") + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/cache.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/cache.c b/src/cache.c +index dd159ea..eef5957 100644 +--- a/src/cache.c ++++ b/src/cache.c +@@ -447,7 +447,10 @@ int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds, + flags = evaluate_cache_get(cmd, flags); + break; + case CMD_RESET: +- flags |= evaluate_cache_reset(cmd, flags, filter); ++ if (nft->check) ++ flags |= evaluate_cache_list(nft, cmd, flags, filter); ++ else ++ flags |= evaluate_cache_reset(cmd, flags, filter); + break; + case CMD_LIST: + flags |= evaluate_cache_list(nft, cmd, flags, filter); diff --git a/0096-parser_json-fix-crash-in-json_parse_set_stmt_list.patch b/0096-parser_json-fix-crash-in-json_parse_set_stmt_list.patch new file mode 100644 index 0000000..701b54d --- /dev/null +++ b/0096-parser_json-fix-crash-in-json_parse_set_stmt_list.patch @@ -0,0 +1,56 @@ +From 0d1a648aa0a32dae21323c4bdcdc8d5184663ba2 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:34:06 +0200 +Subject: [PATCH] parser_json: fix crash in json_parse_set_stmt_list + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 26d9cbefb10e6bc3765df7e9e7a4fc3b951a80f3 + +commit 26d9cbefb10e6bc3765df7e9e7a4fc3b951a80f3 +Author: Sebastian Walz (sivizius) +Date: Tue Aug 20 00:09:26 2024 +0200 + + parser_json: fix crash in json_parse_set_stmt_list + + Due to missing `NULL`-check, there will be a segfault for invalid statements. + + Fixes: 07958ec53830 ("json: add set statement list support") + Signed-off-by: Sebastian Walz (sivizius) + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/parser_json.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/src/parser_json.c b/src/parser_json.c +index cce5ee2..7b75a88 100644 +--- a/src/parser_json.c ++++ b/src/parser_json.c +@@ -2317,7 +2317,7 @@ static void json_parse_set_stmt_list(struct json_ctx *ctx, + json_t *stmt_json) + { + struct list_head *head; +- struct stmt *tmp; ++ struct stmt *stmt; + json_t *value; + size_t index; + +@@ -2329,9 +2329,14 @@ static void json_parse_set_stmt_list(struct json_ctx *ctx, + + head = stmt_list; + json_array_foreach(stmt_json, index, value) { +- tmp = json_parse_stmt(ctx, value); +- list_add(&tmp->list, head); +- head = &tmp->list; ++ stmt = json_parse_stmt(ctx, value); ++ if (!stmt) { ++ json_error(ctx, "Parsing set statements array at index %zd failed.", index); ++ stmt_list_free(stmt_list); ++ return; ++ } ++ list_add(&stmt->list, head); ++ head = &stmt->list; + } + } + diff --git a/0097-parser_json-allow-statement-stateful-statement-only-.patch b/0097-parser_json-allow-statement-stateful-statement-only-.patch new file mode 100644 index 0000000..c4b3921 --- /dev/null +++ b/0097-parser_json-allow-statement-stateful-statement-only-.patch @@ -0,0 +1,41 @@ +From f2e3c291c8d1ca5879697abf87f96c5097cdf50b Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:34:06 +0200 +Subject: [PATCH] parser_json: allow statement stateful statement only in set + elements + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 85b5b051f0e64f106cee078fc6d66d0872770e79 + +commit 85b5b051f0e64f106cee078fc6d66d0872770e79 +Author: Pablo Neira Ayuso +Date: Tue Apr 1 09:49:48 2025 +0200 + + parser_json: allow statement stateful statement only in set elements + + Upfront reject of non stateful statements in set elements. + + Fixes: 07958ec53830 ("json: add set statement list support") + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/parser_json.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/src/parser_json.c b/src/parser_json.c +index 7b75a88..cfeef3c 100644 +--- a/src/parser_json.c ++++ b/src/parser_json.c +@@ -2335,6 +2335,11 @@ static void json_parse_set_stmt_list(struct json_ctx *ctx, + stmt_list_free(stmt_list); + return; + } ++ if (!(stmt->flags & STMT_F_STATEFUL)) { ++ stmt_free(stmt); ++ json_error(ctx, "Unsupported set statements array at index %zd failed.", index); ++ stmt_list_free(stmt_list); ++ } + list_add(&stmt->list, head); + head = &stmt->list; + } diff --git a/0098-parser_json-bail-out-on-malformed-statement-in-set.patch b/0098-parser_json-bail-out-on-malformed-statement-in-set.patch new file mode 100644 index 0000000..94176d2 --- /dev/null +++ b/0098-parser_json-bail-out-on-malformed-statement-in-set.patch @@ -0,0 +1,120 @@ +From ec4e326e8007b5cc0298ca164930961c2c12bae8 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:34:06 +0200 +Subject: [PATCH] parser_json: bail out on malformed statement in set + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit cc7a7d025a60dced5f5e894aa1688014c9deef3d + +commit cc7a7d025a60dced5f5e894aa1688014c9deef3d +Author: Pablo Neira Ayuso +Date: Tue Apr 1 09:57:59 2025 +0200 + + parser_json: bail out on malformed statement in set + + Propagate error to caller so it bails out on malformed set statements. + + Fixes: 07958ec53830 ("json: add set statement list support") + Signed-off-by: Pablo Neira Ayuso + +Signed-off-by: Phil Sutter +--- + src/parser_json.c | 39 +++++++++++++++++++++++++++------------ + 1 file changed, 27 insertions(+), 12 deletions(-) + +diff --git a/src/parser_json.c b/src/parser_json.c +index cfeef3c..1359b49 100644 +--- a/src/parser_json.c ++++ b/src/parser_json.c +@@ -2312,9 +2312,9 @@ static struct stmt *json_parse_reject_stmt(struct json_ctx *ctx, + return stmt; + } + +-static void json_parse_set_stmt_list(struct json_ctx *ctx, +- struct list_head *stmt_list, +- json_t *stmt_json) ++static int json_parse_set_stmt_list(struct json_ctx *ctx, ++ struct list_head *stmt_list, ++ json_t *stmt_json) + { + struct list_head *head; + struct stmt *stmt; +@@ -2322,10 +2322,12 @@ static void json_parse_set_stmt_list(struct json_ctx *ctx, + size_t index; + + if (!stmt_json) +- return; ++ return 0; + +- if (!json_is_array(stmt_json)) ++ if (!json_is_array(stmt_json)) { + json_error(ctx, "Unexpected object type in stmt"); ++ return -1; ++ } + + head = stmt_list; + json_array_foreach(stmt_json, index, value) { +@@ -2333,16 +2335,19 @@ static void json_parse_set_stmt_list(struct json_ctx *ctx, + if (!stmt) { + json_error(ctx, "Parsing set statements array at index %zd failed.", index); + stmt_list_free(stmt_list); +- return; ++ return -1; + } + if (!(stmt->flags & STMT_F_STATEFUL)) { + stmt_free(stmt); + json_error(ctx, "Unsupported set statements array at index %zd failed.", index); + stmt_list_free(stmt_list); ++ return -1; + } + list_add(&stmt->list, head); + head = &stmt->list; + } ++ ++ return 0; + } + + static struct stmt *json_parse_set_stmt(struct json_ctx *ctx, +@@ -2387,8 +2392,11 @@ static struct stmt *json_parse_set_stmt(struct json_ctx *ctx, + stmt->set.key = expr; + stmt->set.set = expr2; + +- if (!json_unpack(value, "{s:o}", "stmt", &stmt_json)) +- json_parse_set_stmt_list(ctx, &stmt->set.stmt_list, stmt_json); ++ if (!json_unpack(value, "{s:o}", "stmt", &stmt_json) && ++ json_parse_set_stmt_list(ctx, &stmt->set.stmt_list, stmt_json) < 0) { ++ stmt_free(stmt); ++ return NULL; ++ } + + return stmt; + } +@@ -2444,8 +2452,11 @@ static struct stmt *json_parse_map_stmt(struct json_ctx *ctx, + stmt->map.data = expr_data; + stmt->map.set = expr2; + +- if (!json_unpack(value, "{s:o}", "stmt", &stmt_json)) +- json_parse_set_stmt_list(ctx, &stmt->set.stmt_list, stmt_json); ++ if (!json_unpack(value, "{s:o}", "stmt", &stmt_json) && ++ json_parse_set_stmt_list(ctx, &stmt->set.stmt_list, stmt_json) < 0) { ++ stmt_free(stmt); ++ return NULL; ++ } + + return stmt; + } +@@ -3250,8 +3261,12 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root, + set->gc_int *= 1000; + json_unpack(root, "{s:i}", "size", &set->desc.size); + +- if (!json_unpack(root, "{s:o}", "stmt", &stmt_json)) +- json_parse_set_stmt_list(ctx, &set->stmt_list, stmt_json); ++ if (!json_unpack(root, "{s:o}", "stmt", &stmt_json) && ++ json_parse_set_stmt_list(ctx, &set->stmt_list, stmt_json) < 0) { ++ set_free(set); ++ handle_free(&h); ++ return NULL; ++ } + + handle_merge(&set->handle, &h); + diff --git a/0099-parser_json-fix-map-set-type-confusion-crash-in-map-.patch b/0099-parser_json-fix-map-set-type-confusion-crash-in-map-.patch new file mode 100644 index 0000000..c017884 --- /dev/null +++ b/0099-parser_json-fix-map-set-type-confusion-crash-in-map-.patch @@ -0,0 +1,50 @@ +From 72a9e1eaeb767056cd4925d98542218811db6f13 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 30 Jun 2026 14:34:06 +0200 +Subject: [PATCH] parser_json: fix map/set type confusion crash in map + statement parser + +JIRA: https://issues.redhat.com/browse/RHEL-190549 +Upstream Status: nftables commit 628701e796ab9a4da5541cf77fa809c2ce8e7720 + +commit 628701e796ab9a4da5541cf77fa809c2ce8e7720 +Author: Florian Westphal +Date: Thu May 21 16:18:55 2026 +0200 + + parser_json: fix map/set type confusion crash in map statement parser + + Quoting Alessandro Gario: + Anthropic is conducting research into the use of large language models + for automated vulnerability discovery in open source software. As part + of that work, Anthropic used Claude to scan a set of widely used open + source projects for security issues. Anthropic then engaged Trail of + Bits to independently triage, manually validate, and develop patches + for the findings. [..] + + json_parse_map_stmt() in src/parser_json.c:2584 passes + &stmt->set.stmt_list to json_parse_set_stmt_list(), but the + statement object is a map_stmt. The function reads as a copy/edit of + json_parse_set_stmt() that forgot to retarget the list-head pointer. + + Reported-by: Alessandro Gario + Fixes: 27a2da23d508 ("netlink_linearize: skip set element expression in map statement key") + Signed-off-by: Florian Westphal + +Signed-off-by: Phil Sutter +--- + src/parser_json.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/parser_json.c b/src/parser_json.c +index 1359b49..ca0cbc3 100644 +--- a/src/parser_json.c ++++ b/src/parser_json.c +@@ -2453,7 +2453,7 @@ static struct stmt *json_parse_map_stmt(struct json_ctx *ctx, + stmt->map.set = expr2; + + if (!json_unpack(value, "{s:o}", "stmt", &stmt_json) && +- json_parse_set_stmt_list(ctx, &stmt->set.stmt_list, stmt_json) < 0) { ++ json_parse_set_stmt_list(ctx, &stmt->map.stmt_list, stmt_json) < 0) { + stmt_free(stmt); + return NULL; + } diff --git a/nftables.spec b/nftables.spec index 962ea05..77cb5c2 100644 --- a/nftables.spec +++ b/nftables.spec @@ -1,5 +1,5 @@ %define nft_rpmversion 1.0.9 -%define nft_specrelease 7 +%define nft_specrelease 8 Name: nftables Version: %{nft_rpmversion} @@ -34,6 +34,90 @@ Patch12: 0012-Revert-doc-part-of-meta-introduce-meta-broute-suppor.pa Patch13: 0013-Revert-doc-part-of-exthdr-add-boolean-DCCP-option-ma.patch Patch14: 0014-src-netlink-fix-crash-when-ops-doesn-t-support-udata.patch Patch15: 0015-src-fix-reset-element-support-for-interval-set-type.patch +Patch16: 0016-doc-Fix-typo-in-nat-statement-prefix-description.patch +Patch17: 0017-json-Print-single-set-flag-as-non-array.patch +Patch18: 0018-netlink-Avoid-potential-NULL-ptr-deref-parsing-set-e.patch +Patch19: 0019-netlink-Catch-unknown-types-when-deserializing-objec.patch +Patch20: 0020-json-prevent-null-deref-if-chain-policy-is-not-set.patch +Patch21: 0021-netlink-Fix-for-potential-crash-parsing-a-flowtable.patch +Patch22: 0022-netlink-Do-not-allocate-a-bogus-flowtable-priority-e.patch +Patch23: 0023-json-Support-typeof-in-set-and-map-types.patch +Patch24: 0024-tests-monitor-Fix-for-single-flag-array-avoidance.patch +Patch25: 0025-tests-py-Properly-fix-JSON-equivalents-for-netdev-re.patch +Patch26: 0026-netlink-Avoid-crash-upon-missing-NFTNL_OBJ_CT_TIMEOU.patch +Patch27: 0027-src-BASECHAIN-flag-no-longer-implies-presence-of-pri.patch +Patch28: 0028-rule-skip-fuzzy-lookup-if-object-name-is-not-availab.patch +Patch29: 0029-cache-assert-name-is-non-nul-when-looking-up.patch +Patch30: 0030-parser_bison-only-reset-by-name-is-supported-by-now.patch +Patch31: 0031-parser_bison-allow-delete-command-with-map-via-handl.patch +Patch32: 0032-json-reject-too-long-interface-names.patch +Patch33: 0033-doc-extend-description-of-fib-expression.patch +Patch34: 0034-json-fix-use-after-free-in-table_flags_json.patch +Patch35: 0035-json-Print-single-fib-flag-as-non-array.patch +Patch36: 0036-json-Print-single-synproxy-flags-as-non-array.patch +Patch37: 0037-json-Introduce-json_add_array_new.patch +Patch38: 0038-json-work-around-fuzzer-induced-assert-crashes.patch +Patch39: 0039-parser_json-Introduce-parse_flags_array.patch +Patch40: 0040-tests-py-fix-json-single-flag-output-for-fib-synprox.patch +Patch41: 0041-fib-allow-to-check-if-route-exists-in-maps.patch +Patch42: 0042-fib-allow-to-use-it-in-set-statements.patch +Patch43: 0043-tests-py-re-enables-nft-test.py-to-load-the-local-nf.patch +Patch44: 0044-tests-shell-cover-long-interface-name-in-0042chain_v.patch +Patch45: 0045-tests-shell-adjust-add-after-delete-flowtable-for-ol.patch +Patch46: 0046-tests-shell-skip-if-kernel-does-not-support-flowtabl.patch +Patch47: 0047-rule-print-chain-and-flowtable-devices-in-quotes.patch +Patch48: 0048-evaluate-validate-set-expression-type-before-accessi.patch +Patch49: 0049-mnl-Call-mnl_attr_nest_end-just-once.patch +Patch50: 0050-doc-nft.8-Minor-NAT-STATEMENTS-section-review.patch +Patch51: 0051-src-netlink-netlink_delinearize_table-may-return-NUL.patch +Patch52: 0052-tests-py-Drop-stale-entries-since-redundant-test-cas.patch +Patch53: 0053-tests-py-Drop-duplicate-test-from-inet-geneve.t.patch +Patch54: 0054-tests-py-Drop-duplicate-test-from-inet-gre.t.patch +Patch55: 0055-tests-py-Drop-duplicate-test-from-inet-gretap.t.patch +Patch56: 0056-tests-py-Drop-stale-entry-from-inet-tcp.t.json.patch +Patch57: 0057-tests-py-Drop-duplicate-test-from-inet-vxlan.t.patch +Patch58: 0058-tests-py-Drop-stale-entry-from-ip-snat.t.json.patch +Patch59: 0059-tests-py-Drop-stale-entries-from-ip6-ct-meta-.t.json.patch +Patch60: 0060-tests-py-Drop-stale-entry-from-ip-snat.t.payload.patch +Patch61: 0061-tests-py-Fix-tests-added-for-icmpv6-taddr-support.patch +Patch62: 0062-segtree-incorrect-type-when-aggregating-concatenated.patch +Patch63: 0063-src-ensure-chain-policy-evaluation-when-specified.patch +Patch64: 0064-json-Do-not-reduce-single-item-arrays-on-output.patch +Patch65: 0065-fib-restore-JSON-output-for-relational-expressions.patch +Patch66: 0066-mnl-silence-compiler-warning.patch +Patch67: 0067-mnl-continue-on-ENOBUFS-errors-when-processing-batch.patch +Patch68: 0068-monitor-Quote-device-names-in-chain-declarations-too.patch +Patch69: 0069-tests-monitor-Fix-regex-collecting-expected-echo-out.patch +Patch70: 0070-monitor-Inform-JSON-printer-when-reporting-an-object.patch +Patch71: 0071-fib-Fix-for-existence-check-on-Big-Endian.patch +Patch72: 0072-doc-fix-tcpdump-example.patch +Patch73: 0073-src-parser_json-fix-format-string-bugs.patch +Patch74: 0074-datatype-clamp-boolean-value-to-0-and-1.patch +Patch75: 0075-datatype-Fix-boolean-type-on-Big-Endian.patch +Patch76: 0076-optimize-Fix-verdict-expression-comparison.patch +Patch77: 0077-tests-py-any-ct.t.json.output-Drop-leftover-entry.patch +Patch78: 0078-tests-py-inet-osf.t-Fix-element-ordering-in-JSON-equ.patch +Patch79: 0079-doc-don-t-suggest-to-disable-GSO.patch +Patch80: 0080-doc-libnftables-json-Describe-RULESET-object.patch +Patch81: 0081-rule-skip-CMD_OBJ_SETELEMS-with-no-elements-after-se.patch +Patch82: 0082-tests-json_echo-Drop-rule-handle-before-multi-add.patch +Patch83: 0083-monitor-fix-memleak-in-setelem-cb.patch +Patch84: 0084-segtree-Fix-range-aggregation-on-Big-Endian.patch +Patch85: 0085-mergesort-Fix-sorting-of-string-values.patch +Patch86: 0086-optimize-expand-expression-list-when-merging-into-co.patch +Patch87: 0087-mergesort-Align-concatenation-sort-order-with-Big-En.patch +Patch88: 0088-json-complete-multi-statement-set-element-support.patch +Patch89: 0089-cache-Respect-family-in-all-list-commands.patch +Patch90: 0090-cache-Relax-chain_cache_dump-filter-application.patch +Patch91: 0091-cache-Filter-for-table-when-listing-sets-or-maps.patch +Patch92: 0092-cache-Filter-for-table-when-listing-flowtables.patch +Patch93: 0093-segtree-Fix-for-variable-sized-object-may-not-be-ini.patch +Patch94: 0094-mnl-Fix-ordering-of-hooks-in-list-hooks-output.patch +Patch95: 0095-cache-honor-c-check-for-reset-commands.patch +Patch96: 0096-parser_json-fix-crash-in-json_parse_set_stmt_list.patch +Patch97: 0097-parser_json-allow-statement-stateful-statement-only-.patch +Patch98: 0098-parser_json-bail-out-on-malformed-statement-in-set.patch +Patch99: 0099-parser_json-fix-map-set-type-confusion-crash-in-map-.patch BuildRequires: autoconf BuildRequires: automake @@ -146,6 +230,92 @@ cd py/ %files -n python3-nftables -f %{pyproject_files} %changelog +* Tue Jun 30 2026 Phil Sutter [1.0.9-8.el9] +- parser_json: fix map/set type confusion crash in map statement parser (Phil Sutter) [RHEL-190549] +- parser_json: bail out on malformed statement in set (Phil Sutter) [RHEL-190549] +- parser_json: allow statement stateful statement only in set elements (Phil Sutter) [RHEL-190549] +- parser_json: fix crash in json_parse_set_stmt_list (Phil Sutter) [RHEL-190549] +- cache: honor -c/--check for reset commands (Phil Sutter) [RHEL-190549] +- mnl: Fix ordering of hooks in 'list hooks' output (Phil Sutter) [RHEL-190549] +- segtree: Fix for variable-sized object may not be initialized (Phil Sutter) [RHEL-190549] +- cache: Filter for table when listing flowtables (Phil Sutter) [RHEL-190549] +- cache: Filter for table when listing sets or maps (Phil Sutter) [RHEL-190549] +- cache: Relax chain_cache_dump filter application (Phil Sutter) [RHEL-190549] +- cache: Respect family in all list commands (Phil Sutter) [RHEL-190549] +- json: complete multi-statement set element support (Phil Sutter) [RHEL-190549] +- mergesort: Align concatenation sort order with Big Endian (Phil Sutter) [RHEL-190549] +- optimize: expand expression list when merging into concatenation (Phil Sutter) [RHEL-190549] +- mergesort: Fix sorting of string values (Phil Sutter) [RHEL-190549] +- segtree: Fix range aggregation on Big Endian (Phil Sutter) [RHEL-190549] +- monitor: fix memleak in setelem cb (Phil Sutter) [RHEL-190549] +- tests: json_echo: Drop rule handle before multi-add (Phil Sutter) [RHEL-190549] +- rule: skip CMD_OBJ_SETELEMS with no elements after set flush (Phil Sutter) [RHEL-190549] +- doc: libnftables-json: Describe RULESET object (Phil Sutter) [RHEL-190549] +- doc: don't suggest to disable GSO (Phil Sutter) [RHEL-190549] +- tests: py: inet/osf.t: Fix element ordering in JSON equivalents (Phil Sutter) [RHEL-190549] +- tests: py: any/ct.t.json.output: Drop leftover entry (Phil Sutter) [RHEL-190549] +- optimize: Fix verdict expression comparison (Phil Sutter) [RHEL-190549] +- datatype: Fix boolean type on Big Endian (Phil Sutter) [RHEL-190549] +- datatype: clamp boolean value to 0 and 1 (Phil Sutter) [RHEL-190549] +- src: parser_json: fix format string bugs (Phil Sutter) [RHEL-190549] +- doc: fix tcpdump example (Phil Sutter) [RHEL-190549] +- fib: Fix for existence check on Big Endian (Phil Sutter) [RHEL-190549] +- monitor: Inform JSON printer when reporting an object delete event (Phil Sutter) [RHEL-190549] +- tests: monitor: Fix regex collecting expected echo output (Phil Sutter) [RHEL-190549] +- monitor: Quote device names in chain declarations, too (Phil Sutter) [RHEL-190549] +- mnl: continue on ENOBUFS errors when processing batch (Phil Sutter) [RHEL-190549] +- mnl: silence compiler warning (Phil Sutter) [RHEL-190549] +- fib: restore JSON output for relational expressions (Phil Sutter) [RHEL-190549] +- json: Do not reduce single-item arrays on output (Phil Sutter) [RHEL-190549] +- src: ensure chain policy evaluation when specified (Phil Sutter) [RHEL-190549] +- segtree: incorrect type when aggregating concatenated set ranges (Phil Sutter) [RHEL-190549] +- tests: py: Fix tests added for 'icmpv6 taddr' support (Phil Sutter) [RHEL-190549] +- tests: py: Drop stale entry from ip/snat.t.payload (Phil Sutter) [RHEL-190549] +- tests: py: Drop stale entries from ip6/{ct,meta}.t.json (Phil Sutter) [RHEL-190549] +- tests: py: Drop stale entry from ip/snat.t.json (Phil Sutter) [RHEL-190549] +- tests: py: Drop duplicate test from inet/vxlan.t (Phil Sutter) [RHEL-190549] +- tests: py: Drop stale entry from inet/tcp.t.json (Phil Sutter) [RHEL-190549] +- tests: py: Drop duplicate test from inet/gretap.t (Phil Sutter) [RHEL-190549] +- tests: py: Drop duplicate test from inet/gre.t (Phil Sutter) [RHEL-190549] +- tests: py: Drop duplicate test from inet/geneve.t (Phil Sutter) [RHEL-190549] +- tests: py: Drop stale entries since redundant test case removal (Phil Sutter) [RHEL-190549] +- src: netlink: netlink_delinearize_table() may return NULL (Phil Sutter) [RHEL-190549] +- doc: nft.8: Minor NAT STATEMENTS section review (Phil Sutter) [RHEL-190549] +- mnl: Call mnl_attr_nest_end() just once (Phil Sutter) [RHEL-190549] +- evaluate: validate set expression type before accessing flags (Phil Sutter) [RHEL-190549] +- rule: print chain and flowtable devices in quotes (Phil Sutter) [RHEL-190549] +- tests: shell: skip if kernel does not support flowtable counter (Phil Sutter) [RHEL-190549] +- tests: shell: adjust add-after-delete flowtable for older kernels (Phil Sutter) [RHEL-190549] +- tests/shell: cover long interface name in "0042chain_variable_0" test (Phil Sutter) [RHEL-190549] +- tests: py: re-enables nft-test.py to load the local nftables.py (Phil Sutter) [RHEL-190549] +- fib: allow to use it in set statements (Phil Sutter) [RHEL-190549] +- fib: allow to check if route exists in maps (Phil Sutter) [RHEL-190549] +- tests: py: fix json single-flag output for fib & synproxy (Phil Sutter) [RHEL-190549] +- parser_json: Introduce parse_flags_array() (Phil Sutter) [RHEL-190549] +- json: work around fuzzer-induced assert crashes (Phil Sutter) [RHEL-190549] +- json: Introduce json_add_array_new() (Phil Sutter) [RHEL-190549] +- json: Print single synproxy flags as non-array (Phil Sutter) [RHEL-190549] +- json: Print single fib flag as non-array (Phil Sutter) [RHEL-190549] +- json: fix use after free in table_flags_json() (Phil Sutter) [RHEL-190549] +- doc: extend description of fib expression (Phil Sutter) [RHEL-190549] +- json: reject too long interface names (Phil Sutter) [RHEL-190549] +- parser_bison: allow delete command with map via handle (Phil Sutter) [RHEL-190549] +- parser_bison: only reset by name is supported by now (Phil Sutter) [RHEL-190549] +- cache: assert name is non-nul when looking up (Phil Sutter) [RHEL-190549] +- rule: skip fuzzy lookup if object name is not available (Phil Sutter) [RHEL-190549] +- src: BASECHAIN flag no longer implies presence of priority expression (Phil Sutter) [RHEL-190549] +- netlink: Avoid crash upon missing NFTNL_OBJ_CT_TIMEOUT_ARRAY attribute (Phil Sutter) [RHEL-190549] +- tests: py: Properly fix JSON equivalents for netdev/reject.t (Phil Sutter) [RHEL-190549] +- tests: monitor: Fix for single flag array avoidance (Phil Sutter) [RHEL-190549] +- json: Support typeof in set and map types (Phil Sutter) [RHEL-190549] +- netlink: Do not allocate a bogus flowtable priority expr (Phil Sutter) [RHEL-190549] +- netlink: Fix for potential crash parsing a flowtable (Phil Sutter) [RHEL-190549] +- json: prevent null deref if chain->policy is not set (Phil Sutter) [RHEL-190549] +- netlink: Catch unknown types when deserializing objects (Phil Sutter) [RHEL-190549] +- netlink: Avoid potential NULL-ptr deref parsing set elem expressions (Phil Sutter) [RHEL-190549] +- json: Print single set flag as non-array (Phil Sutter) [RHEL-190549] +- doc: Fix typo in nat statement 'prefix' description (Phil Sutter) [RHEL-190549] + * Wed Mar 18 2026 Phil Sutter [1.0.9-7.el9] - src: fix reset element support for interval set type (Phil Sutter) [RHEL-153461] - src: netlink: fix crash when ops doesn't support udata (Phil Sutter) [RHEL-153461]