nftables-1.0.9-8.el9

* Wed Jul 22 2026 Phil Sutter <psutter@redhat.com> [1.0.9-8.el9]
- spec: Update expected test suite results (Phil Sutter) [RHEL-190549]
- tests: monitor: Fix for out-of-path call (Phil Sutter) [RHEL-190549]
- tests: monitor: Excercise all syntaxes and variants by default (Phil Sutter) [RHEL-190549]
- tests: monitor: Test JSON echo mode as well (Phil Sutter) [RHEL-190549]
- tests: monitor: Become $PWD agnostic (Phil Sutter) [RHEL-190549]
- tests: monitor: Run in own netns (Phil Sutter) [RHEL-190549]
- tests: monitor: Label diffs to help users (Phil Sutter) [RHEL-190549]
- tests: monitor: Extend testcases a bit (Phil Sutter) [RHEL-190549]
- tests: monitor: enclose device names in quotes (Phil Sutter) [RHEL-190549]
- json: Dump flowtable hook spec only if present (Phil Sutter) [RHEL-190549]
- tests: py: Fix some JSON equivalents (Phil Sutter) [RHEL-190549]
- rule: constify set_is_non_concat_range() (Phil Sutter) [RHEL-190549]
- datatype: don't return a const string from cgroupv2_get_path() (Phil Sutter) [RHEL-190549]
- tests: py: Fix --keep test runner option (Phil Sutter) [RHEL-190549]
- segtree: fix get element command with open intervals (Phil Sutter) [RHEL-190549]
- segtree: replace default case by specific types in get_set_intervals() (Phil Sutter) [RHEL-190549]
- src: remove EXPR_SET_ELEM in range_expr_value_{low,high}() (Phil Sutter) [RHEL-190549]
- segtree: rename set_elem_add() to set_elem_expr_add() (Phil Sutter) [RHEL-190549]
- parser_json: fix map/set type confusion crash in map statement parser (Phil Sutter) [RHEL-190549]
- cache: honor -c/--check for reset commands (Phil Sutter) [RHEL-190549]
- tests: py: osf is ip-only (Phil Sutter) [RHEL-190549]
- tests: py: print the file name as intended (Phil Sutter) [RHEL-190549]
- tests: py: don't use a fixed filename (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: Respect family in all list commands (Phil Sutter) [RHEL-190549]
- cache: Include chains, flowtables and objects in netlink debug output (Phil Sutter) [RHEL-190549]
- cache: Relax chain_cache_dump filter application (Phil Sutter) [RHEL-190549]
- parser_bison: add range check for synproxy wscale (Phil Sutter) [RHEL-190549]
- json: complete multi-statement set element support (Phil Sutter) [RHEL-190549]
- segtree: Fix range aggregation on Big Endian (Phil Sutter) [RHEL-190549]
- mergesort: Align concatenation sort order with Big Endian (Phil Sutter) [RHEL-190549]
- mergesort: Fix sorting of string values (Phil Sutter) [RHEL-190549]
- tests: py: any/tcpopt.t.json: Fix JSON equivalent (Phil Sutter) [RHEL-190549]
- expression: expr_build_udata_recurse should recurse (Phil Sutter) [RHEL-190549]
- expression: propagate key datatype for anonymous sets (Phil Sutter) [RHEL-190549]
- netlink_delinearize: also consider exthdr type when trimming binops (Phil Sutter) [RHEL-190549]
- tcpopt: add symbol table for mptcp suboptions (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: Implement payload_record() (Phil Sutter) [RHEL-190549]
- tests: py: inet/osf.t: Fix element ordering in JSON equivalents (Phil Sutter) [RHEL-190549]
- tests: py: Do not rely upon '[end]' marker (Phil Sutter) [RHEL-190549]
- tests: py: Fix for using wrong payload path (Phil Sutter) [RHEL-190549]
- tests: py: any/ct.t.json.output: Drop leftover entry (Phil Sutter) [RHEL-190549]
- datatype: Fix boolean type on Big Endian (Phil Sutter) [RHEL-190549]
- optimize: Fix verdict expression comparison (Phil Sutter) [RHEL-190549]
- src: parser_json: fix format string bugs (Phil Sutter) [RHEL-190549]
- doc: fix tcpdump example (Phil Sutter) [RHEL-190549]
- tests: py: objects.t: must use input, not output (Phil Sutter) [RHEL-190549]
- fib: Fix for existence check on Big Endian (Phil Sutter) [RHEL-190549]
- tests: Prepare exit codes for automake (Phil Sutter) [RHEL-190549]
- monitor: Inform JSON printer when reporting an object delete event (Phil Sutter) [RHEL-190549]
- monitor: Recognize flowtable add/del events (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]
- tests: monitor: Fix for flag arrays in JSON output (Phil Sutter) [RHEL-190549]
- mnl: silence compiler warning (Phil Sutter) [RHEL-190549]
- fib: restore JSON output for relational expressions (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]
- json: Do not reduce single-item arrays on output (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: 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: shell: Fix ifname_based_hooks feature check (Phil Sutter) [RHEL-190549]
- json: reject too long interface names (Phil Sutter) [RHEL-190549]
- tests/py: clean up set backend support fallout (Phil Sutter) [RHEL-190549]
- cache: assert name is non-nul when looking up (Phil Sutter) [RHEL-190549]
- parser_bison: only reset by name is supported by now (Phil Sutter) [RHEL-190549]
- rule: skip fuzzy lookup if object name is not available (Phil Sutter) [RHEL-190549]
- parser_bison: allow delete command with map via handle (Phil Sutter) [RHEL-190549]
- debug: include kernel set information on cache fill (Phil Sutter) [RHEL-190549]
- tests/py: prepare for set debug change (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: shell: Adjust to ifname-based hooks (Phil Sutter) [RHEL-190549]
- tests: shell: combine dormant flag with netdevice removal (Phil Sutter) [RHEL-190549]
- tests: monitor: Fix for single flag array avoidance (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: work around fuzzer-induced assert crashes (Phil Sutter) [RHEL-190549]
- json: prevent null deref if chain->policy is not set (Phil Sutter) [RHEL-190549]
- tests: py: fix json single-flag output for fib & synproxy (Phil Sutter) [RHEL-190549]
- tests: shell: check for features not available in 5.4 (Phil Sutter) [RHEL-190549]
- netlink: Avoid potential NULL-ptr deref parsing set elem expressions (Phil Sutter) [RHEL-190549]
- netlink: Catch unknown types when deserializing objects (Phil Sutter) [RHEL-190549]
- json: Introduce json_add_array_new() (Phil Sutter) [RHEL-190549]
- json: Fix for memleak in __binop_expr_json (Phil Sutter) [RHEL-190549]
- json: Accept more than two operands in binary expressions (Phil Sutter) [RHEL-190549]
- json: Print single fib flag as non-array (Phil Sutter) [RHEL-190549]
- tests: shell: Add test case for JSON 'flags' arrays (Phil Sutter) [RHEL-190549]
- json: Print single set flag as non-array (Phil Sutter) [RHEL-190549]
- json: Print single synproxy flags as non-array (Phil Sutter) [RHEL-190549]
- parser_json: Introduce parse_flags_array() (Phil Sutter) [RHEL-190549]
- doc: Fix typo in nat statement 'prefix' description (Phil Sutter) [RHEL-190549]
- netlink: bogus concatenated set ranges with netlink message overrun (Phil Sutter) [RHEL-190549]
- parser_bison: add selector_expr rule to restrict typeof_expr (Phil Sutter) [RHEL-190549]
- optimize: invalidate merge in case of duplicated key in set/map (Phil Sutter) [RHEL-190549]
- evaluate: bail out if ct saddr/daddr dependency cannot be inserted (Phil Sutter) [RHEL-190549]
- parser_json: bail out on malformed statement in set (Phil Sutter) [RHEL-190549]
- parser_json: reject empty jump/goto chain (Phil Sutter) [RHEL-190549]
- parser_json: allow statement stateful statement only in set elements (Phil Sutter) [RHEL-190549]
- cache: prevent possible crash rule filter is NULL (Phil Sutter) [RHEL-190549]
- optimize: expand expression list when merging into concatenation (Phil Sutter) [RHEL-190549]
- cache: don't crash when filter is NULL (Phil Sutter) [RHEL-190549]
- evaluate: only allow stateful statements in set and map definitions (Phil Sutter) [RHEL-190549]
- evaluate: compact STMT_F_STATEFUL checks (Phil Sutter) [RHEL-190549]
- json: don't BUG when asked to list synproxies (Phil Sutter) [RHEL-190549]
- optimize: incorrect comparison for reject statement (Phil Sutter) [RHEL-190549]
- optimize: compact bitmask matching in set/map (Phil Sutter) [RHEL-190549]
- tests: shell: missing ct count elements in new set_stmt test (Phil Sutter) [RHEL-190549]
- evaluate: don't update cache for anonymous chains (Phil Sutter) [RHEL-190549]
- json: make sure timeout list is initialised (Phil Sutter) [RHEL-190549]
- parser_bison: consolidate connlimit grammar rule for set elements (Phil Sutter) [RHEL-190549]
- parser_bison: consolidate last grammar rule for set elements (Phil Sutter) [RHEL-190549]
- parser_bison: consolidate quota grammar rule for set elements (Phil Sutter) [RHEL-190549]
- parser_bison: consolidate limit grammar rule for set elements (Phil Sutter) [RHEL-190549]
- parser_bison: consolidate counter grammar rule for set elements (Phil Sutter) [RHEL-190549]
- tests: shell: extend coverage for set element statements (Phil Sutter) [RHEL-190549]
- evaluate: fix assertion failure with malformed map definitions (Phil Sutter) [RHEL-190549]
- evaluate: don't allow nat map with specified protocol (Phil Sutter) [RHEL-190549]
- parser_bison: reject non-serializeable typeof expressions (Phil Sutter) [RHEL-190549]
- netlink: fix stack buffer overrun when emitting ranged expressions (Phil Sutter) [RHEL-190549]
- src: print set element with multi-word description in single one line (Phil Sutter) [RHEL-190549]
- tests: shell: detach synproxy test (Phil Sutter) [RHEL-190549]
- src: do not merge a set with a erroneous one (Phil Sutter) [RHEL-190549]
- segtree: incomplete output in get element command with maps (Phil Sutter) [RHEL-190549]
- evaluate: release existing datatype when evaluating unary expression (Phil Sutter) [RHEL-190549]
- segtree: fix string data initialisation (Phil Sutter) [RHEL-190549]
- payload: honor inner payload description in payload_expr_cmp() (Phil Sutter) [RHEL-190549]
- payload: return early if dependency is not a payload expression (Phil Sutter) [RHEL-190549]
- evaluate: optimize zero length range (Phil Sutter) [RHEL-190549]
- fib: Change data type of fib oifname to "ifname" (Phil Sutter) [RHEL-190549]
- evaluate: auto-merge is only available for singleton interval sets (Phil Sutter) [RHEL-190549]
- parser_bison: compact and simplify list and reset syntax (Phil Sutter) [RHEL-190549]
- parser_bison: turn redundant ip option type field match into boolean (Phil Sutter) [RHEL-190549]
- datatype: clamp boolean value to 0 and 1 (Phil Sutter) [RHEL-190549]
- tests: shell: delete netdev chain after test (Phil Sutter) [RHEL-190549]
- ipopt: use ipv4 address datatype for address field in ip options (Phil Sutter) [RHEL-190549]
- netlink_delinarize: fix bogus munging of mask value (Phil Sutter) [RHEL-190549]
- evaluate: remove variable shadowing (Phil Sutter) [RHEL-190549]
- intervals: do not merge intervals with different timeout (Phil Sutter) [RHEL-190549]
- src: add EXPR_RANGE_VALUE expression and use it (Phil Sutter) [RHEL-190549]
- intervals: add helper function to set previous element (Phil Sutter) [RHEL-190549]
- parser_bison: fix UaF when reporting table parse error (Phil Sutter) [RHEL-190549]
- intervals: set internal element location with the deletion trigger (Phil Sutter) [RHEL-190549]
- optimize: compare expression length (Phil Sutter) [RHEL-190549]
- tests: py: Fix for storing payload into missing file (Phil Sutter) [RHEL-190549]
- json: Support typeof in set and map types (Phil Sutter) [RHEL-190549]
- json: collapse set element commands from parser (Phil Sutter) [RHEL-190549]
- doc: extend description of fib expression (Phil Sutter) [RHEL-190549]
- tests: monitor: fix up test case breakage (Phil Sutter) [RHEL-190549]
- src: fix extended netlink error reporting with large set elements (Phil Sutter) [RHEL-190549]
- mnl: rename to mnl_seqnum_alloc() to mnl_seqnum_inc() (Phil Sutter) [RHEL-190549]
- mnl: update cmd_add_loc() to take struct nlmsghdr (Phil Sutter) [RHEL-190549]
- rule: netlink attribute offset is uint32_t for struct nlerr_loc (Phil Sutter) [RHEL-190549]
- src: collapse set element commands from parser (Phil Sutter) [RHEL-190549]
- libnftables-json: fix raw payload expression documentation (Phil Sutter) [RHEL-190549]
- cache: initialize filter when fetching implicit chains (Phil Sutter) [RHEL-190549]
- tests: py: fix up udp csum fixup output (Phil Sutter) [RHEL-190549]
- proto: use NFT_PAYLOAD_L4CSUM_PSEUDOHDR flag to mangle UDP checksum (Phil Sutter) [RHEL-190549]
- tests: shell: stabilize packetpath/payload (Phil Sutter) [RHEL-190549]
- libnftables: Zero ctx->vars after freeing it (Phil Sutter) [RHEL-190549]
- cache: position does not require full cache (Phil Sutter) [RHEL-190549]
- cache: relax requirement for replace rule command (Phil Sutter) [RHEL-190549]
- cache: remove full cache requirement when echo flag is set on (Phil Sutter) [RHEL-190549]
- cache: assert filter when calling nft_cache_evaluate() (Phil Sutter) [RHEL-190549]
- cache: consolidate reset command (Phil Sutter) [RHEL-190549]
- cache: add filtering support for objects (Phil Sutter) [RHEL-190549]
- cache: only dump rules for the given table (Phil Sutter) [RHEL-190549]
- cache: accumulate flags in batch (Phil Sutter) [RHEL-190549]
- cache: reset filter for each command (Phil Sutter) [RHEL-190549]
- parser_json: fix several expression memleaks from error path (Phil Sutter) [RHEL-190549]
- parser_json: release buffer returned by json_dumps (Phil Sutter) [RHEL-190549]
- json: Support maps with concatenated data (Phil Sutter) [RHEL-190549]
- parser_json: fix crash in json_parse_set_stmt_list (Phil Sutter) [RHEL-190549]
- parser_bison: allow 0 burst in limit rate byte mode (Phil Sutter) [RHEL-190549]
- datatype: improve error reporting when time unit is not correct (Phil Sutter) [RHEL-190549]
- cache: rule by index requires full cache (Phil Sutter) [RHEL-190549]
- datatype: reject rate in quota statement (Phil Sutter) [RHEL-190549]
- optimize: skip variables in nat statements (Phil Sutter) [RHEL-190549]
- parser_json: use stdin buffer if available (Phil Sutter) [RHEL-190549]
- libnftables: skip useable checks for /dev/stdin (Phil Sutter) [RHEL-190549]
- optimize: clone counter before insertion into set element (Phil Sutter) [RHEL-190549]
- segtree: set on EXPR_F_KERNEL flag for catchall elements in the cache (Phil Sutter) [RHEL-190549]
- evaluate: set on expr->len for catchall set elements (Phil Sutter) [RHEL-190549]
- parser_bison: recursive table declaration in deprecated meter statement (Phil Sutter) [RHEL-190549]
- intervals: fix element deletions with maps (Phil Sutter) [RHEL-190549]
- src: add string preprocessor and use it for log prefix string (Phil Sutter) [RHEL-190549]
- tests: shell: skip ip option tests if kernel does not support it (Phil Sutter) [RHEL-190549]
- cmd: skip variable set elements when collapsing commands (Phil Sutter) [RHEL-190549]
- cmd: provide better hint if chain is already declared with different type/hook/priority (Phil Sutter) [RHEL-190549]
- monitor: too large shift exponent displaying payload expression (Phil Sutter) [RHEL-190549]
- scanner: inet_pton() allows for broader IPv4-Mapped IPv6 addresses (Phil Sutter) [RHEL-190549]
- evaluate: Fix incorrect checking the `base` variable in case of IPV6 (Phil Sutter) [RHEL-190549]
- evaluate: bogus protocol conflicts in vlan with implicit dependencies (Phil Sutter) [RHEL-190549]
- cache: check for NFT_CACHE_REFRESH in current requested cache too (Phil Sutter) [RHEL-190549]
- doc: nft.8: Fix markup in ct expectation synopsis (Phil Sutter) [RHEL-190549]
- mergesort: Avoid accidental set element reordering (Phil Sutter) [RHEL-190549]
- doc: nft.8: Two minor synopsis fixups (Phil Sutter) [RHEL-190549]
- tests: shell: check for reset tcp options support (Phil Sutter) [RHEL-190549]
- tests: shell: payload matching requires egress support (Phil Sutter) [RHEL-190549]
- tests: py: complete icmp and icmpv6 update (Phil Sutter) [RHEL-190549]
- src: disentangle ICMP code types (Phil Sutter) [RHEL-190549]
- evaluate: display "Range negative size" error (Phil Sutter) [RHEL-190549]
- netlink_delinearize: restore binop syntax when listing ruleset for flags (Phil Sutter) [RHEL-190549]
- doc: libnftables-json: Drop invalid ops from match expression (Phil Sutter) [RHEL-190549]
- parser: json: Support for synproxy objects (Phil Sutter) [RHEL-190549]
- tests: py: add payload merging test cases (Phil Sutter) [RHEL-190549]
- nftables: do mot merge payloads on negation (Phil Sutter) [RHEL-190549]
- rule: fix ASAN errors in chain priority to textual names (Phil Sutter) [RHEL-190549]
- parser: compact type/typeof set rules (Phil Sutter) [RHEL-190549]
- parser: compact interval typeof rules (Phil Sutter) [RHEL-190549]
- src: improve error reporting for destroy command (Phil Sutter) [RHEL-190549]
- tests: shell: permit use of host-endian constant values in set lookup keys (Phil Sutter) [RHEL-190549]
- evaluate: permit use of host-endian constant values in set lookup keys (Phil Sutter) [RHEL-190549]
- expression: missing line in describe command with invalid expression (Phil Sutter) [RHEL-190549]
- netlink_delinearize: move concat and value postprocessing to helpers (Phil Sutter) [RHEL-190549]
- evaluate: skip byteorder conversion for selector smaller than 2 bytes (Phil Sutter) [RHEL-190549]
- cache: Optimize caching for 'list tables' command (Phil Sutter) [RHEL-190549]
- evaluate: fix check for unknown in cmd_op_to_name (Phil Sutter) [RHEL-190549]
- evaluate: don't assert on net/transport header conflict (Phil Sutter) [RHEL-190549]
- json: Support sets' auto-merge option (Phil Sutter) [RHEL-190549]
- rule: fix sym refcount assertion (Phil Sutter) [RHEL-190549]
- evaluate: error out when store needs more than one 128bit register of align fixup (Phil Sutter) [RHEL-190549]
- evaluate: do not fetch next expression on runaway number of concatenation components (Phil Sutter) [RHEL-190549]
- evaluate: skip anonymous set optimization for concatenations (Phil Sutter) [RHEL-190549]
- evaluate: add missing range checks for dup,fwd and payload statements (Phil Sutter) [RHEL-190549]
- doc: incorrect datatype description for icmpv6_type and icmpvx_code (Phil Sutter) [RHEL-190549]
- tests: shell: prefer project nft to system-wide nft (Phil Sutter) [RHEL-190549]
- parser_bison: ensure all timeout policy names are released (Phil Sutter) [RHEL-190549]
- netlink: fix stack overflow due to erroneous rounding (Phil Sutter) [RHEL-190549]
- parser_bison: error out on duplicated type/typeof/element keywords (Phil Sutter) [RHEL-190549]
- tests: shell: add test to cover payload transport match and mangle (Phil Sutter) [RHEL-190549]
- evaluate: fix stack overflow with huge priority string (Phil Sutter) [RHEL-190549]
- src: reject large raw payload and concat expressions (Phil Sutter) [RHEL-190549]
- evaluate: exthdr: statement arg must be not be a range (Phil Sutter) [RHEL-190549]
- meta: fix tc classid parsing out-of-bounds access (Phil Sutter) [RHEL-190549]
- parser_bison: close chain scope before chain release (Phil Sutter) [RHEL-190549]
- evaluate: fix bogus assertion failure with boolean datatype (Phil Sutter) [RHEL-190549]
- parser_bison: fix objref statement corruption (Phil Sutter) [RHEL-190549]
- tests: py: missing json output in meta.t with vlan mapping (Phil Sutter) [RHEL-190549]
- evaluate: reset statement length context before evaluating statement (Phil Sutter) [RHEL-190549]
- parser: tcpopt: fix tcp option parsing with NUM + length field (Phil Sutter) [RHEL-190549]
- evaluate: reject set definition with no key (Phil Sutter) [RHEL-190549]
- monitor: add support for concatenated set ranges (Phil Sutter) [RHEL-190549]
- evaluate: disable meta set with ranges (Phil Sutter) [RHEL-190549]
- evaluate: prevent assert when evaluating very large shift values (Phil Sutter) [RHEL-190549]
- evaluate: reject sets with no key (Phil Sutter) [RHEL-190549]
- evaluate: clone unary expression datatype to deal with dynamic datatype (Phil Sutter) [RHEL-190549]
- tests: shell: split nat inet tests (Phil Sutter) [RHEL-190549]
- evaluate: bogus error when adding devices to flowtable (Phil Sutter) [RHEL-190549]
- tests: shell: flush connlimit sets (Phil Sutter) [RHEL-190549]
- tests: shell: adjust add-after-delete flowtable for older kernels (Phil Sutter) [RHEL-190549]
- evaluate: fix rule replacement with anon sets (Phil Sutter) [RHEL-190549]
- tests: shell: skip if kernel does not support flowtable counter (Phil Sutter) [RHEL-190549]
- tests: shell: restore pipapo and chain binding coverage in standalone 30s-stress (Phil Sutter) [RHEL-190549]
- json: fix use after free in table_flags_json() (Phil Sutter) [RHEL-190549]
- src: expand create commands (Phil Sutter) [RHEL-190549]
- tests: shell: split set NAT interval test (Phil Sutter) [RHEL-190549]
- tests: shell: split merge nat optimization in two tests (Phil Sutter) [RHEL-190549]
- netlink: fix buffer size for user data in netlink_delinearize_chain() (Phil Sutter) [RHEL-190549]
- src: remove xfree() and use plain free() (Phil Sutter) [RHEL-190549]
- src: add free_const() and use it instead of xfree() (Phil Sutter) [RHEL-190549]
- evaluate: place byteorder conversion before rshift in payload expressions (Phil Sutter) [RHEL-190549]
- evaluate: reset statement length context only for set mappings (Phil Sutter) [RHEL-190549]
- meta: fix hour decoding when timezone offset is negative (Phil Sutter) [RHEL-190549]
- tproxy: Drop artificial port printing restriction (Phil Sutter) [RHEL-190549]
- tests/shell: fix mount command in "test-wrapper.sh" (Phil Sutter) [RHEL-190549]
- parser_bison: fix length check for ifname in ifname_expr_alloc() (Phil Sutter) [RHEL-190549]
- tests/shell: cover long interface name in "0042chain_variable_0" test (Phil Sutter) [RHEL-190549]
- tests/shell: add missing "elem_opts_compat_0.nodump" file (Phil Sutter) [RHEL-190549]
- parser_bison: Fix for broken compatibility with older dumps (Phil Sutter) [RHEL-190549]
Resolves: RHEL-190549
This commit is contained in:
Phil Sutter 2026-07-22 11:24:30 +02:00
parent 2eea6c6aeb
commit 92f5b031b7
298 changed files with 38940 additions and 4 deletions

View File

@ -0,0 +1,83 @@
From c13051bb51a4fcc1322faed773ee1dd6f81ed5f5 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:18 +0200
Subject: [PATCH] parser_bison: Fix for broken compatibility with older dumps
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 22fab8681a50014174cdd02ace90f74b9e9eefe9
commit 22fab8681a50014174cdd02ace90f74b9e9eefe9
Author: Phil Sutter <phil@nwl.cc>
Date: Thu Oct 19 18:40:04 2023 +0200
parser_bison: Fix for broken compatibility with older dumps
Commit e6d1d0d611958 ("src: add set element multi-statement
support") changed the order of expressions and other state attached to set
elements are expected in input. This broke parsing of ruleset dumps
created by nft commands prior to that commit.
Restore compatibility by also accepting the old ordering.
Fixes: e6d1d0d611958 ("src: add set element multi-statement support")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_bison.y | 6 ++++
tests/shell/testcases/sets/elem_opts_compat_0 | 29 +++++++++++++++++++
2 files changed, 35 insertions(+)
create mode 100755 tests/shell/testcases/sets/elem_opts_compat_0
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 5ced6e1..8eff50c 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -4532,6 +4532,12 @@ meter_key_expr_alloc : concat_expr
set_elem_expr : set_elem_expr_alloc
| set_elem_expr_alloc set_elem_expr_options
+ | set_elem_expr_alloc set_elem_expr_options set_elem_stmt_list
+ {
+ $$ = $1;
+ list_splice_tail($3, &$$->stmt_list);
+ xfree($3);
+ }
;
set_elem_key_expr : set_lhs_expr { $$ = $1; }
diff --git a/tests/shell/testcases/sets/elem_opts_compat_0 b/tests/shell/testcases/sets/elem_opts_compat_0
new file mode 100755
index 0000000..e012953
--- /dev/null
+++ b/tests/shell/testcases/sets/elem_opts_compat_0
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# ordering of element options and expressions has changed, make sure parser
+# accepts both ways
+
+set -e
+
+$NFT -f - <<EOF
+table t {
+ set s {
+ type inet_service
+ counter;
+ timeout 30s;
+ }
+}
+EOF
+
+check() {
+ out=$($NFT list ruleset)
+ secs=$(sed -n 's/.*expires \([0-9]\+\)s.*/\1/p' <<< "$out")
+ [[ $secs -lt 11 ]]
+ grep -q 'counter packets 10 bytes 20' <<< "$out"
+}
+
+$NFT add element t s '{ 23 counter packets 10 bytes 20 expires 10s }'
+check
+$NFT flush set t s
+$NFT add element t s '{ 42 expires 10s counter packets 10 bytes 20 }'
+check

View File

@ -0,0 +1,31 @@
From 4bd9e30ecae19385b0e38fc1d5a82db87a351fb1 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:18 +0200
Subject: [PATCH] tests/shell: add missing "elem_opts_compat_0.nodump" file
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit f65b2d12236174d477c55e96c4027cd51185ba5e
commit f65b2d12236174d477c55e96c4027cd51185ba5e
Author: Thomas Haller <thaller@redhat.com>
Date: Mon Oct 23 14:40:25 2023 +0200
tests/shell: add missing "elem_opts_compat_0.nodump" file
This is an inconsistency. The test should have either a .nft or a
.nodump file. "./tools/check-tree.sh" enforces that and will in the
future run by `make check`.
Fixes: 22fab8681a50 ("parser_bison: Fix for broken compatibility with older dumps")
Signed-off-by: Thomas Haller <thaller@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/shell/testcases/sets/dumps/elem_opts_compat_0.nodump | 0
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 tests/shell/testcases/sets/dumps/elem_opts_compat_0.nodump
diff --git a/tests/shell/testcases/sets/dumps/elem_opts_compat_0.nodump b/tests/shell/testcases/sets/dumps/elem_opts_compat_0.nodump
new file mode 100644
index 0000000..e69de29

View File

@ -0,0 +1,126 @@
From 7d88deb4054d71dcd038ea003484be65226d8f15 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:18 +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 <thaller@redhat.com>
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 <thaller@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
.../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 <<EOF | $DIFF -u <($NFT list ruleset) -
+table netdev filter1 {
+ chain Main_Ingress1 {
+ type filter hook ingress device "lo" priority -500; policy accept;
+ }
+}
+table netdev filter2 {
+ chain Main_Ingress2 {
+ type filter hook ingress devices = { d23456789012345, lo } priority -500; policy accept;
+ }
+}
+EOF
+
+
EXPECTED="define if_main = \"lo\"
table netdev filter2 {
chain Main_Ingress2 {
- type filter hook ingress devices = { \$if_main, dummy0 } priority -500; policy accept;
+ type filter hook ingress devices = { \$if_main, d23456789012345 } priority -500; policy accept;
}
}"
$NFT -f - <<< $EXPECTED
+
if [ "$NFT_TEST_HAVE_netdev_egress" = n ] ; then
echo "Skip parts of the test due to NFT_TEST_HAVE_netdev_egress=n"
exit 77
fi
-EXPECTED="define if_main = { lo, dummy0 }
+
+EXPECTED="define if_main = { lo, d23456789012345 }
define lan_interfaces = { lo }
table netdev filter3 {
@@ -43,4 +72,3 @@ table netdev filter3 {
$NFT -f - <<< $EXPECTED
-
diff --git a/tests/shell/testcases/chains/dumps/0042chain_variable_0.nft b/tests/shell/testcases/chains/dumps/0042chain_variable_0.nft
index 5ec230d..84a908d 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 = { dummy0, 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 = { dummy0, lo } priority -500; policy accept;
+ type filter hook ingress devices = { d23456789012345, lo } priority -500; policy accept;
}
chain Main_Egress3 {

View File

@ -0,0 +1,78 @@
From 7f0483fec8d8fe12a8cb44f203f697f877e39b1a Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:18 +0200
Subject: [PATCH] parser_bison: fix length check for ifname in
ifname_expr_alloc()
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 122dce6b35205a3df419a5cae9acfd6e83e8725a
commit 122dce6b35205a3df419a5cae9acfd6e83e8725a
Author: Thomas Haller <thaller@redhat.com>
Date: Mon Oct 23 19:00:47 2023 +0200
parser_bison: fix length check for ifname in ifname_expr_alloc()
IFNAMSIZ is 16, and the allowed byte length of the name is one less than
that. Fix the length check and adjust a test for covering the longest
allowed interface name.
This is obviously a change in behavior, because previously interface
names with length 16 were accepted and were silently truncated along the
way. Now they are rejected as invalid.
Fixes: fa52bc225806 ("parser: reject zero-length interface names")
Signed-off-by: Thomas Haller <thaller@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_bison.y | 3 ++-
tests/shell/testcases/chains/0042chain_variable_0 | 7 +------
2 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 8eff50c..5c2d14b 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -16,6 +16,7 @@
#include <stdio.h>
#include <inttypes.h>
#include <syslog.h>
+#include <net/if.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/if_ether.h>
@@ -158,7 +159,7 @@ static struct expr *ifname_expr_alloc(const struct location *location,
return NULL;
}
- if (length > 16) {
+ if (length >= IFNAMSIZ) {
xfree(name);
erec_queue(error(location, "interface name too long"), queue);
return NULL;
diff --git a/tests/shell/testcases/chains/0042chain_variable_0 b/tests/shell/testcases/chains/0042chain_variable_0
index 739dc05..a4b929f 100755
--- a/tests/shell/testcases/chains/0042chain_variable_0
+++ b/tests/shell/testcases/chains/0042chain_variable_0
@@ -26,18 +26,13 @@ table netdev filter2 {
rc=0
$NFT -f - <<< $EXPECTED || rc=$?
-test "$rc" = 0
+test "$rc" = 1
cat <<EOF | $DIFF -u <($NFT list ruleset) -
table netdev filter1 {
chain Main_Ingress1 {
type filter hook ingress device "lo" priority -500; policy accept;
}
}
-table netdev filter2 {
- chain Main_Ingress2 {
- type filter hook ingress devices = { d23456789012345, lo } priority -500; policy accept;
- }
-}
EOF

View File

@ -0,0 +1,45 @@
From 70989b95e0a340781eadd99af05f5bccbb36b028 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:18 +0200
Subject: [PATCH] tests/shell: fix mount command in "test-wrapper.sh"
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 65d94af52f11a2c5a44d5ae8aa3891ccb648c666
commit 65d94af52f11a2c5a44d5ae8aa3891ccb648c666
Author: Thomas Haller <thaller@redhat.com>
Date: Thu Nov 2 09:15:41 2023 +0100
tests/shell: fix mount command in "test-wrapper.sh"
With Fedora 39 (util-linux-core-2.39.2-1.fc39), the mount command starts
to fail. It was still working with Fedora 38 (util-linux-core-2.38.1-4.fc38).
$ unshare -f -p -m --mount-proc -U --map-root-user -n bash -c 'mount -t tmpfs --make-private /var/run && mount'
mount: /run: mount failed: Invalid argument.
Not sure why this starts to fail. But arguably the command line
arguments were wrong. Fix it, we need a pseudo name for the device.
Fixes: df6f1a3e0803 ("tests/shell: bind mount private /var/run/netns in test container")
Signed-off-by: Thomas Haller <thaller@redhat.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/shell/helpers/test-wrapper.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/shell/helpers/test-wrapper.sh b/tests/shell/helpers/test-wrapper.sh
index 13b918f..898dcce 100755
--- a/tests/shell/helpers/test-wrapper.sh
+++ b/tests/shell/helpers/test-wrapper.sh
@@ -48,7 +48,7 @@ if [ "$NFT_TEST_HAS_UNSHARED_MOUNT" = y ] ; then
# Note that this also hides everything that might reside in /var/run.
# That is desirable, as tests should not depend on content there (or if
# they do, we need to explicitly handle it as appropriate).
- if mount -t tmpfs --make-private "/var/run" ; then
+ if mount -t tmpfs --make-private tmpfs "/var/run" ; then
CLEANUP_UMOUNT_VAR_RUN=y
fi
mkdir -p /var/run/netns

View File

@ -0,0 +1,119 @@
From 2927e76b0fb5b64e1a57467151fb76c3d513fa3a Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:18 +0200
Subject: [PATCH] tproxy: Drop artificial port printing restriction
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit e4c9f9f7e0d1f83be18f6c4a418da503e9021b24
commit e4c9f9f7e0d1f83be18f6c4a418da503e9021b24
Author: Phil Sutter <phil@nwl.cc>
Date: Thu Nov 2 14:48:10 2023 +0100
tproxy: Drop artificial port printing restriction
It does not make much sense to omit printing the port expression if it's
not a value expression: On one hand, input allows for more advanced
uses. On the other, if it is in-kernel, best nft can do is to try and
print it no matter what. Just ignoring ruleset elements can't be
correct.
Fixes: 2be1d52644cf7 ("src: Add tproxy support")
Closes: https://bugzilla.netfilter.org/show_bug.cgi?id=1721
Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/statement.c | 2 +-
tests/py/inet/tproxy.t | 2 ++
tests/py/inet/tproxy.t.json | 35 ++++++++++++++++++++++++++++++++++
tests/py/inet/tproxy.t.payload | 12 ++++++++++++
4 files changed, 50 insertions(+), 1 deletion(-)
diff --git a/src/statement.c b/src/statement.c
index 4756116..f5176e6 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -989,7 +989,7 @@ static void tproxy_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
expr_print(stmt->tproxy.addr, octx);
}
}
- if (stmt->tproxy.port && stmt->tproxy.port->etype == EXPR_VALUE) {
+ if (stmt->tproxy.port) {
if (!stmt->tproxy.addr)
nft_print(octx, " ");
nft_print(octx, ":");
diff --git a/tests/py/inet/tproxy.t b/tests/py/inet/tproxy.t
index d23bbcb..9901df7 100644
--- a/tests/py/inet/tproxy.t
+++ b/tests/py/inet/tproxy.t
@@ -19,3 +19,5 @@ meta l4proto 17 tproxy ip to :50080;ok
meta l4proto 17 tproxy ip6 to :50080;ok
meta l4proto 17 tproxy to :50080;ok
ip daddr 0.0.0.0/0 meta l4proto 6 tproxy ip to :2000;ok
+
+meta l4proto 6 tproxy ip to 127.0.0.1:symhash mod 2 map { 0 : 23, 1 : 42 };ok
diff --git a/tests/py/inet/tproxy.t.json b/tests/py/inet/tproxy.t.json
index 7b3b11c..71b6fd2 100644
--- a/tests/py/inet/tproxy.t.json
+++ b/tests/py/inet/tproxy.t.json
@@ -183,3 +183,38 @@
}
}
]
+
+# meta l4proto 6 tproxy ip to 127.0.0.1:symhash mod 2 map { 0 : 23, 1 : 42 }
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "op": "==",
+ "right": 6
+ }
+ },
+ {
+ "tproxy": {
+ "addr": "127.0.0.1",
+ "family": "ip",
+ "port": {
+ "map": {
+ "data": {
+ "set": [
+ [ 0, 23 ],
+ [ 1, 42 ]
+ ]
+ },
+ "key": {
+ "symhash": { "mod": 2 }
+ }
+ }
+ }
+ }
+ }
+]
+
diff --git a/tests/py/inet/tproxy.t.payload b/tests/py/inet/tproxy.t.payload
index 24bf8f6..2f41904 100644
--- a/tests/py/inet/tproxy.t.payload
+++ b/tests/py/inet/tproxy.t.payload
@@ -61,3 +61,15 @@ inet x y
[ immediate reg 1 0x0000d007 ]
[ tproxy ip port reg 1 ]
+# meta l4proto 6 tproxy ip to 127.0.0.1:symhash mod 2 map { 0 : 23, 1 : 42 }
+__map%d x b size 2
+__map%d x 0
+ element 00000000 : 00001700 0 [end] element 00000001 : 00002a00 0 [end]
+inet x y
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ immediate reg 1 0x0100007f ]
+ [ hash reg 2 = symhash() % mod 2 ]
+ [ lookup reg 2 set __map%d dreg 2 ]
+ [ tproxy ip addr reg 1 port reg 2 ]
+

View File

@ -0,0 +1,126 @@
From 2db221f432962517f777f225aadf4db1cf18e1e4 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:18 +0200
Subject: [PATCH] meta: fix hour decoding when timezone offset is negative
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit d392ddf243dcbf8a34726c777d2c669b1e8bfa85
commit d392ddf243dcbf8a34726c777d2c669b1e8bfa85
Author: Florian Westphal <fw@strlen.de>
Date: Thu Nov 2 15:34:13 2023 +0100
meta: fix hour decoding when timezone offset is negative
Brian Davidson says:
meta hour rules don't display properly after being created when the
hour is on or after 00:00 UTC. The netlink debug looks correct for
seconds past midnight UTC, but displaying the rules looks like an
overflow or a byte order problem. I am in UTC-0400, so today, 20:00
and later exhibits the problem, while 19:00 and earlier hours are
fine.
meta.c only ever worked when the delta to UTC is positive.
We need to add in case the second counter turns negative after
offset adjustment.
Also add a test case for this.
Fixes: f8f32deda31d ("meta: Introduce new conditions 'time', 'day' and 'hour'")
Reported-by: Brian Davidson <davidson.brian@gmail.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/meta.c | 11 +++-
.../testcases/listing/dumps/meta_time.nodump | 0
tests/shell/testcases/listing/meta_time | 52 +++++++++++++++++++
3 files changed, 61 insertions(+), 2 deletions(-)
create mode 100644 tests/shell/testcases/listing/dumps/meta_time.nodump
create mode 100755 tests/shell/testcases/listing/meta_time
diff --git a/src/meta.c b/src/meta.c
index b69dca2..1f1e33c 100644
--- a/src/meta.c
+++ b/src/meta.c
@@ -495,9 +495,16 @@ static void hour_type_print(const struct expr *expr, struct output_ctx *octx)
/* Obtain current tm, so that we can add tm_gmtoff */
ts = time(NULL);
- if (ts != ((time_t) -1) && localtime_r(&ts, &cur_tm))
- seconds = (seconds + cur_tm.tm_gmtoff) % SECONDS_PER_DAY;
+ if (ts != ((time_t) -1) && localtime_r(&ts, &cur_tm)) {
+ int32_t adj = seconds + cur_tm.tm_gmtoff;
+ if (adj < 0)
+ adj += SECONDS_PER_DAY;
+ else if (adj >= SECONDS_PER_DAY)
+ adj -= SECONDS_PER_DAY;
+
+ seconds = adj;
+ }
minutes = seconds / 60;
seconds %= 60;
hours = minutes / 60;
diff --git a/tests/shell/testcases/listing/dumps/meta_time.nodump b/tests/shell/testcases/listing/dumps/meta_time.nodump
new file mode 100644
index 0000000..e69de29
diff --git a/tests/shell/testcases/listing/meta_time b/tests/shell/testcases/listing/meta_time
new file mode 100755
index 0000000..a976199
--- /dev/null
+++ b/tests/shell/testcases/listing/meta_time
@@ -0,0 +1,52 @@
+#!/bin/bash
+
+set -e
+
+TMP1=$(mktemp)
+TMP2=$(mktemp)
+
+cleanup()
+{
+ rm -f "$TMP1"
+ rm -f "$TMP2"
+}
+
+check_decode()
+{
+ TZ=$1 $NFT list chain t c | grep meta > "$TMP2"
+ diff -u "$TMP1" "$TMP2"
+}
+
+trap cleanup EXIT
+
+$NFT -f - <<EOF
+table t {
+ chain c {
+ }
+}
+EOF
+
+for i in $(seq -w 0 23); do
+ TZ=UTC $NFT add rule t c meta hour "$i:00"-"$i:59"
+done
+
+# Check decoding in UTC, this mirrors 1:1 what should have been added.
+for i in $(seq 0 23); do
+ printf "\t\tmeta hour \"%02d:%02d\"-\"%02d:%02d\"\n" $i 0 $i 59 >> "$TMP1"
+done
+
+check_decode UTC
+
+printf "\t\tmeta hour \"%02d:%02d\"-\"%02d:%02d\"\n" 23 0 23 59 > "$TMP1"
+for i in $(seq 0 22); do
+ printf "\t\tmeta hour \"%02d:%02d\"-\"%02d:%02d\"\n" $i 0 $i 59 >> "$TMP1"
+done
+check_decode UTC+1
+
+printf "\t\tmeta hour \"%02d:%02d\"-\"%02d:%02d\"\n" 1 0 1 59 > "$TMP1"
+for i in $(seq 2 23); do
+ printf "\t\tmeta hour \"%02d:%02d\"-\"%02d:%02d\"\n" $i 0 $i 59 >> "$TMP1"
+done
+printf "\t\tmeta hour \"%02d:%02d\"-\"%02d:%02d\"\n" 0 0 0 59 >> "$TMP1"
+
+check_decode UTC-1

View File

@ -0,0 +1,218 @@
From 3f363237125fded5adb4bc9ed8890e518a518286 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:18 +0200
Subject: [PATCH] evaluate: reset statement length context only for set
mappings
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 57f092a87fc4bc61e29cff31dfff976e1f2005ab
commit 57f092a87fc4bc61e29cff31dfff976e1f2005ab
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Sun Nov 5 18:33:14 2023 +0100
evaluate: reset statement length context only for set mappings
map expression (which is used a key to look up for the mapping) needs to
consider the statement length context, otherwise incorrect bytecode is
generated when {ct,meta} statement is generated.
# nft -f - <<EOF
add table ip6 t
add chain ip6 t c
add map ip6 t mapv6 { typeof ip6 dscp : meta mark; }
EOF
# nft -d netlink add rule ip6 t c meta mark set ip6 dscp map @mapv6
ip6 t c
[ payload load 2b @ network header + 0 => reg 1 ]
[ bitwise reg 1 = ( reg 1 & 0x0000c00f ) ^ 0x00000000 ]
... missing byteorder conversion here before shift ...
[ bitwise reg 1 = ( reg 1 >> 0x00000006 ) ]
[ lookup reg 1 set mapv6 dreg 1 ]
[ meta set mark with reg 1 ]
Reset statement length context only for the mapping side for the
elements in the set.
Fixes: edecd58755a8 ("evaluate: support shifts larger than the width of the left operand")
Reported-by: Brian Davidson <davidson.brian@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 2 +-
tests/py/ip6/ip6.t | 5 +++
tests/py/ip6/ip6.t.json | 58 +++++++++++++++++++++++++++++++++
tests/py/ip6/ip6.t.payload.inet | 23 +++++++++++++
tests/py/ip6/ip6.t.payload.ip6 | 19 +++++++++++
5 files changed, 106 insertions(+), 1 deletion(-)
diff --git a/src/evaluate.c b/src/evaluate.c
index 711990a..1850043 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1913,13 +1913,13 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
}
expr_set_context(&ctx->ectx, NULL, 0);
- ctx->stmt_len = 0;
if (expr_evaluate(ctx, &map->map) < 0)
return -1;
if (expr_is_constant(map->map))
return expr_error(ctx->msgs, map->map,
"Map expression can not be constant");
+ ctx->stmt_len = 0;
mappings = map->mappings;
mappings->set_flags |= NFT_SET_MAP;
diff --git a/tests/py/ip6/ip6.t b/tests/py/ip6/ip6.t
index 2ffe318..60ea223 100644
--- a/tests/py/ip6/ip6.t
+++ b/tests/py/ip6/ip6.t
@@ -17,6 +17,11 @@ ip6 dscp != 0x20;ok;ip6 dscp != cs4
ip6 dscp {cs0, cs1, cs2, cs3, cs4, cs5, cs6, cs7, af11, af12, af13, af21, af22, af23, af31, af32, af33, af41, af42, af43, ef};ok
ip6 dscp vmap { 0x04 : accept, 0x3f : continue } counter;ok
+!map1 type dscp : mark;ok
+meta mark set ip6 dscp map @map1;ok
+!map2 type dscp . ipv6_addr : mark;ok
+meta mark set ip6 dscp . ip6 daddr map @map2;ok
+
ip6 flowlabel 22;ok
ip6 flowlabel != 233;ok
- ip6 flowlabel 33-45;ok
diff --git a/tests/py/ip6/ip6.t.json b/tests/py/ip6/ip6.t.json
index cf80217..5411190 100644
--- a/tests/py/ip6/ip6.t.json
+++ b/tests/py/ip6/ip6.t.json
@@ -135,6 +135,64 @@
}
]
+# meta mark set ip6 dscp map @map1
+[
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "map": {
+ "data": "@map1",
+ "key": {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip6"
+ }
+ }
+ }
+ }
+ }
+ }
+]
+
+# meta mark set ip6 dscp . ip6 daddr map @map2
+[
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "map": {
+ "data": "@map2",
+ "key": {
+ "concat": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip6"
+ }
+ },
+ {
+ "payload": {
+ "field": "daddr",
+ "protocol": "ip6"
+ }
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+]
+
# ip6 flowlabel 22
[
{
diff --git a/tests/py/ip6/ip6.t.payload.inet b/tests/py/ip6/ip6.t.payload.inet
index 20dfe54..214a0ed 100644
--- a/tests/py/ip6/ip6.t.payload.inet
+++ b/tests/py/ip6/ip6.t.payload.inet
@@ -53,6 +53,29 @@ ip6 test-ip6 input
[ lookup reg 1 set __map%d dreg 0 ]
[ counter pkts 0 bytes 0 ]
+# meta mark set ip6 dscp map @map1
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x0000000a ]
+ [ payload load 2b @ network header + 0 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000c00f ) ^ 0x00000000 ]
+ [ byteorder reg 1 = ntoh(reg 1, 2, 2) ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000006 ) ]
+ [ lookup reg 1 set map1 dreg 1 ]
+ [ meta set mark with reg 1 ]
+
+# meta mark set ip6 dscp . ip6 daddr map @map2
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x0000000a ]
+ [ payload load 2b @ network header + 0 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000c00f ) ^ 0x00000000 ]
+ [ byteorder reg 1 = ntoh(reg 1, 2, 2) ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000006 ) ]
+ [ payload load 16b @ network header + 24 => reg 9 ]
+ [ lookup reg 1 set map2 dreg 1 ]
+ [ meta set mark with reg 1 ]
+
# ip6 flowlabel 22
inet test-inet input
[ meta load nfproto => reg 1 ]
diff --git a/tests/py/ip6/ip6.t.payload.ip6 b/tests/py/ip6/ip6.t.payload.ip6
index f8e3ca3..428b8ea 100644
--- a/tests/py/ip6/ip6.t.payload.ip6
+++ b/tests/py/ip6/ip6.t.payload.ip6
@@ -41,6 +41,25 @@ ip6 test-ip6 input
[ lookup reg 1 set __map%d dreg 0 ]
[ counter pkts 0 bytes 0 ]
+# meta mark set ip6 dscp map @map1
+ip6 test-ip6 input
+ [ payload load 2b @ network header + 0 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000c00f ) ^ 0x00000000 ]
+ [ byteorder reg 1 = ntoh(reg 1, 2, 2) ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000006 ) ]
+ [ lookup reg 1 set map1 dreg 1 ]
+ [ meta set mark with reg 1 ]
+
+# meta mark set ip6 dscp . ip6 daddr map @map2
+ip6 test-ip6 input
+ [ payload load 2b @ network header + 0 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000c00f ) ^ 0x00000000 ]
+ [ byteorder reg 1 = ntoh(reg 1, 2, 2) ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000006 ) ]
+ [ payload load 16b @ network header + 24 => reg 9 ]
+ [ lookup reg 1 set map2 dreg 1 ]
+ [ meta set mark with reg 1 ]
+
# ip6 flowlabel 22
ip6 test-ip6 input
[ payload load 3b @ network header + 1 => reg 1 ]

View File

@ -0,0 +1,238 @@
From 2d5e3d1d7241a86d1555b3edfb020f20fc95748d Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:18 +0200
Subject: [PATCH] evaluate: place byteorder conversion before rshift in payload
expressions
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit cb9b72a43c5684379c027908d9f332170bf8dd15
commit cb9b72a43c5684379c027908d9f332170bf8dd15
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Sun Nov 5 21:54:25 2023 +0100
evaluate: place byteorder conversion before rshift in payload expressions
Use the key from the evaluation context to perform the byteorder
conversion in case that this expression is used for lookups and updates
on explicit sets.
# nft --debug=netlink add rule ip6 t output ip6 dscp @mapv6
ip6 t output
[ payload load 2b @ network header + 0 => reg 1 ]
[ bitwise reg 1 = ( reg 1 & 0x0000c00f ) ^ 0x00000000 ]
[ byteorder reg 1 = ntoh(reg 1, 2, 2) ] <-------------- this was missing!
[ bitwise reg 1 = ( reg 1 >> 0x00000006 ) ]
[ lookup reg 1 set mapv6 ]
Also with set statements (updates from packet path):
# nft --debug=netlink add rule ip6 t output update @mapv6 { ip6 dscp }
ip6 t output
[ payload load 2b @ network header + 0 => reg 1 ]
[ bitwise reg 1 = ( reg 1 & 0x0000c00f ) ^ 0x00000000 ]
[ byteorder reg 1 = ntoh(reg 1, 2, 2) ] <------------- also here!
[ bitwise reg 1 = ( reg 1 >> 0x00000006 ) ]
[ dynset update reg_key 1 set mapv6 ]
Simple matches on values and implicit sets rely on the binary transfer
mechanism to propagate the shift to the constant, no explicit byteorder
is required in such case.
Fixes: 668c18f67203 ("evaluate: place byteorder conversion before rshift in payload statement")
Reported-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 10 +++++++-
tests/py/ip6/ip6.t | 4 ++++
tests/py/ip6/ip6.t.json | 42 +++++++++++++++++++++++++++++++++
tests/py/ip6/ip6.t.payload.inet | 21 +++++++++++++++++
tests/py/ip6/ip6.t.payload.ip6 | 17 +++++++++++++
5 files changed, 93 insertions(+), 1 deletion(-)
diff --git a/src/evaluate.c b/src/evaluate.c
index 1850043..303e36c 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -545,7 +545,8 @@ static void expr_evaluate_bits(struct eval_ctx *ctx, struct expr **exprp)
and->len = masklen;
if (shift) {
- if (ctx->stmt_len > 0 && div_round_up(masklen, BITS_PER_BYTE) > 1) {
+ if ((ctx->ectx.key || ctx->stmt_len > 0) &&
+ div_round_up(masklen, BITS_PER_BYTE) > 1) {
int op = byteorder_conversion_op(expr, BYTEORDER_HOST_ENDIAN);
and = unary_expr_alloc(&expr->location, op, and);
and->len = masklen;
@@ -574,6 +575,7 @@ static void expr_evaluate_bits(struct eval_ctx *ctx, struct expr **exprp)
static int __expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
{
+ const struct expr *key = ctx->ectx.key;
struct expr *expr = *exprp;
if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
@@ -582,6 +584,8 @@ static int __expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
if (expr_evaluate_primary(ctx, exprp) < 0)
return -1;
+ ctx->ectx.key = key;
+
if (expr->exthdr.offset % BITS_PER_BYTE != 0 ||
expr->len % BITS_PER_BYTE != 0)
expr_evaluate_bits(ctx, exprp);
@@ -878,6 +882,7 @@ static bool payload_needs_adjustment(const struct expr *expr)
static int expr_evaluate_payload(struct eval_ctx *ctx, struct expr **exprp)
{
+ const struct expr *key = ctx->ectx.key;
struct expr *expr = *exprp;
if (expr->payload.evaluated)
@@ -889,6 +894,8 @@ static int expr_evaluate_payload(struct eval_ctx *ctx, struct expr **exprp)
if (expr_evaluate_primary(ctx, exprp) < 0)
return -1;
+ ctx->ectx.key = key;
+
if (payload_needs_adjustment(expr))
expr_evaluate_bits(ctx, exprp);
@@ -1508,6 +1515,7 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
}
__expr_set_context(&ctx->ectx, tmp, bo, dsize, 0);
+ ctx->ectx.key = i;
if (list_member_evaluate(ctx, &i) < 0)
return -1;
diff --git a/tests/py/ip6/ip6.t b/tests/py/ip6/ip6.t
index 60ea223..430dd57 100644
--- a/tests/py/ip6/ip6.t
+++ b/tests/py/ip6/ip6.t
@@ -21,6 +21,10 @@ ip6 dscp vmap { 0x04 : accept, 0x3f : continue } counter;ok
meta mark set ip6 dscp map @map1;ok
!map2 type dscp . ipv6_addr : mark;ok
meta mark set ip6 dscp . ip6 daddr map @map2;ok
+!map3 type dscp : mark;ok
+ip6 dscp @map3;ok
+!map4 type dscp . ipv6_addr : mark;ok
+ip6 dscp . ip6 daddr @map4;ok
ip6 flowlabel 22;ok
ip6 flowlabel != 233;ok
diff --git a/tests/py/ip6/ip6.t.json b/tests/py/ip6/ip6.t.json
index 5411190..49e5a2d 100644
--- a/tests/py/ip6/ip6.t.json
+++ b/tests/py/ip6/ip6.t.json
@@ -193,6 +193,48 @@
}
]
+# ip6 dscp @map3
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip6"
+ }
+ },
+ "op": "==",
+ "right": "@map3"
+ }
+ }
+]
+
+# ip6 dscp . ip6 daddr @map4
+[
+ {
+ "match": {
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip6"
+ }
+ },
+ {
+ "payload": {
+ "field": "daddr",
+ "protocol": "ip6"
+ }
+ }
+ ]
+ },
+ "op": "==",
+ "right": "@map4"
+ }
+ }
+]
+
# ip6 flowlabel 22
[
{
diff --git a/tests/py/ip6/ip6.t.payload.inet b/tests/py/ip6/ip6.t.payload.inet
index 214a0ed..dbb430a 100644
--- a/tests/py/ip6/ip6.t.payload.inet
+++ b/tests/py/ip6/ip6.t.payload.inet
@@ -76,6 +76,27 @@ inet test-inet input
[ lookup reg 1 set map2 dreg 1 ]
[ meta set mark with reg 1 ]
+# ip6 dscp @map3
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x0000000a ]
+ [ payload load 2b @ network header + 0 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000c00f ) ^ 0x00000000 ]
+ [ byteorder reg 1 = ntoh(reg 1, 2, 2) ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000006 ) ]
+ [ lookup reg 1 set map3 ]
+
+# ip6 dscp . ip6 daddr @map4
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x0000000a ]
+ [ payload load 2b @ network header + 0 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000c00f ) ^ 0x00000000 ]
+ [ byteorder reg 1 = ntoh(reg 1, 2, 2) ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000006 ) ]
+ [ payload load 16b @ network header + 24 => reg 9 ]
+ [ lookup reg 1 set map4 ]
+
# ip6 flowlabel 22
inet test-inet input
[ meta load nfproto => reg 1 ]
diff --git a/tests/py/ip6/ip6.t.payload.ip6 b/tests/py/ip6/ip6.t.payload.ip6
index 428b8ea..b128923 100644
--- a/tests/py/ip6/ip6.t.payload.ip6
+++ b/tests/py/ip6/ip6.t.payload.ip6
@@ -60,6 +60,23 @@ ip6 test-ip6 input
[ lookup reg 1 set map2 dreg 1 ]
[ meta set mark with reg 1 ]
+# ip6 dscp @map3
+ip6 test-ip6 input
+ [ payload load 2b @ network header + 0 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000c00f ) ^ 0x00000000 ]
+ [ byteorder reg 1 = ntoh(reg 1, 2, 2) ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000006 ) ]
+ [ lookup reg 1 set map3 ]
+
+# ip6 dscp . ip6 daddr @map4
+ip6 test-ip6 input
+ [ payload load 2b @ network header + 0 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000c00f ) ^ 0x00000000 ]
+ [ byteorder reg 1 = ntoh(reg 1, 2, 2) ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000006 ) ]
+ [ payload load 16b @ network header + 24 => reg 9 ]
+ [ lookup reg 1 set map4 ]
+
# ip6 flowlabel 22
ip6 test-ip6 input
[ payload load 3b @ network header + 1 => reg 1 ]

View File

@ -0,0 +1,992 @@
From 612339e3a96884fa9436ec56cfae355356a745e8 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:18 +0200
Subject: [PATCH] src: add free_const() and use it instead of xfree()
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit ffd6b4790a728bd879cc8e4532b54150febb58fa
Conflicts: One chunk omitted due to previous backport of commit
4955ae1a81b73 ("Add support for table's persist flag")
commit ffd6b4790a728bd879cc8e4532b54150febb58fa
Author: Thomas Haller <thaller@redhat.com>
Date: Tue Oct 24 11:57:09 2023 +0200
src: add free_const() and use it instead of xfree()
Almost everywhere xmalloc() and friends is used instead of malloc().
This is almost everywhere paired with xfree().
xfree() has two problems. First, it brings the wrong notion that
xmalloc() should be paired with xfree(), as if xmalloc() would not use
the plain malloc() allocator. In practices, xfree() just wraps free(),
and it wouldn't make sense any other way. xfree() should go away. This
will be addressed in the next commit.
The problem addressed by this commit is that xfree() accepts a const
pointer. Paired with the practice of almost always using xfree() instead
of free(), all our calls to xfree() cast away constness of the pointer,
regardless whether that is necessary. Declaring a pointer as const
should help us to catch wrong uses. If the xfree() function always casts
aways const, the compiler doesn't help.
There are many places that rightly cast away const during free. But not
all of them. Add a free_const() macro, which is like free(), but accepts
const pointers. We should always make an intentional choice whether to
use free() or free_const(). Having a free_const() macro makes this very
common choice clearer, instead of adding a (void*) cast at many places.
Note that we now pair xmalloc() allocations with a free() call (instead
of xfree(). That inconsistency will be resolved in the next commit.
Signed-off-by: Thomas Haller <thaller@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
include/nft.h | 6 ++
src/ct.c | 2 +-
src/datatype.c | 8 +--
src/evaluate.c | 8 +--
src/expression.c | 4 +-
src/libnftables.c | 12 ++--
src/mnl.c | 12 ++--
src/optimize.c | 2 +-
src/parser_bison.y | 138 ++++++++++++++++++++++-----------------------
src/rule.c | 36 ++++++------
src/scanner.l | 4 +-
src/statement.c | 2 +-
src/xt.c | 2 +-
13 files changed, 121 insertions(+), 115 deletions(-)
diff --git a/include/nft.h b/include/nft.h
index 3c894e5..a2d62db 100644
--- a/include/nft.h
+++ b/include/nft.h
@@ -9,4 +9,10 @@
#include <stdlib.h>
#include <string.h>
+/* Just free(), but casts to a (void*). This is for places where
+ * we have a const pointer that we know we want to free. We could just
+ * do the (void*) cast, but free_const() makes it clear that this is
+ * something we frequently need to do and it's intentional. */
+#define free_const(ptr) free((void *)(ptr))
+
#endif /* NFTABLES_NFT_H */
diff --git a/src/ct.c b/src/ct.c
index 1dda799..ebfd90a 100644
--- a/src/ct.c
+++ b/src/ct.c
@@ -570,7 +570,7 @@ static void flow_offload_stmt_print(const struct stmt *stmt,
static void flow_offload_stmt_destroy(struct stmt *stmt)
{
- xfree(stmt->flow.table_name);
+ free_const(stmt->flow.table_name);
}
static const struct stmt_ops flow_offload_stmt_ops = {
diff --git a/src/datatype.c b/src/datatype.c
index 9530ae7..9353ff2 100644
--- a/src/datatype.c
+++ b/src/datatype.c
@@ -938,8 +938,8 @@ void rt_symbol_table_free(const struct symbol_table *tbl)
const struct symbolic_constant *s;
for (s = tbl->symbols; s->identifier != NULL; s++)
- xfree(s->identifier);
- xfree(tbl);
+ free_const(s->identifier);
+ free_const(tbl);
}
void mark_table_init(struct nft_ctx *ctx)
@@ -1296,8 +1296,8 @@ void datatype_free(const struct datatype *ptr)
if (--dtype->refcnt > 0)
return;
- xfree(dtype->name);
- xfree(dtype->desc);
+ free_const(dtype->name);
+ free_const(dtype->desc);
xfree(dtype);
}
diff --git a/src/evaluate.c b/src/evaluate.c
index 303e36c..c41210b 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -4022,7 +4022,7 @@ static int stmt_evaluate_chain(struct eval_ctx *ctx, struct stmt *stmt)
memset(&h, 0, sizeof(h));
handle_merge(&h, &chain->handle);
h.family = ctx->rule->handle.family;
- xfree(h.table.name);
+ free_const(h.table.name);
h.table.name = xstrdup(ctx->rule->handle.table.name);
h.chain.location = stmt->location;
h.chain_id = chain->handle.chain_id;
@@ -4042,9 +4042,9 @@ static int stmt_evaluate_chain(struct eval_ctx *ctx, struct stmt *stmt)
struct handle h2 = {};
handle_merge(&rule->handle, &ctx->rule->handle);
- xfree(rule->handle.table.name);
+ free_const(rule->handle.table.name);
rule->handle.table.name = xstrdup(ctx->rule->handle.table.name);
- xfree(rule->handle.chain.name);
+ free_const(rule->handle.chain.name);
rule->handle.chain.name = NULL;
rule->handle.chain_id = chain->handle.chain_id;
if (rule_evaluate(&rule_ctx, rule, CMD_INVALID) < 0)
@@ -5147,7 +5147,7 @@ static int ct_timeout_evaluate(struct eval_ctx *ctx, struct obj *obj)
ct->timeout[ts->timeout_index] = ts->timeout_value;
list_del(&ts->head);
- xfree(ts->timeout_str);
+ free_const(ts->timeout_str);
xfree(ts);
}
diff --git a/src/expression.c b/src/expression.c
index a21dfec..0b4a537 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -314,7 +314,7 @@ static void symbol_expr_clone(struct expr *new, const struct expr *expr)
static void symbol_expr_destroy(struct expr *expr)
{
- xfree(expr->identifier);
+ free_const(expr->identifier);
}
static const struct expr_ops symbol_expr_ops = {
@@ -1335,7 +1335,7 @@ static void set_elem_expr_destroy(struct expr *expr)
{
struct stmt *stmt, *next;
- xfree(expr->comment);
+ free_const(expr->comment);
expr_free(expr->key);
list_for_each_entry_safe(stmt, next, &expr->stmt_list, list)
stmt_free(stmt);
diff --git a/src/libnftables.c b/src/libnftables.c
index 41f54c0..866b5c6 100644
--- a/src/libnftables.c
+++ b/src/libnftables.c
@@ -154,8 +154,8 @@ void nft_ctx_clear_vars(struct nft_ctx *ctx)
unsigned int i;
for (i = 0; i < ctx->num_vars; i++) {
- xfree(ctx->vars[i].key);
- xfree(ctx->vars[i].value);
+ free_const(ctx->vars[i].key);
+ free_const(ctx->vars[i].value);
}
ctx->num_vars = 0;
xfree(ctx->vars);
@@ -743,12 +743,12 @@ err:
list_for_each_entry_safe(indesc, next, &nft->vars_ctx.indesc_list, list) {
if (indesc->name)
- xfree(indesc->name);
+ free_const(indesc->name);
xfree(indesc);
}
}
- xfree(nft->vars_ctx.buf);
+ free_const(nft->vars_ctx.buf);
if (!rc &&
nft_output_json(&nft->output) &&
@@ -799,12 +799,12 @@ int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
if (nft->optimize_flags) {
ret = nft_run_optimized_file(nft, filename);
- xfree(nft->stdin_buf);
+ free_const(nft->stdin_buf);
return ret;
}
ret = __nft_run_cmd_from_filename(nft, filename);
- xfree(nft->stdin_buf);
+ free_const(nft->stdin_buf);
return ret;
}
diff --git a/src/mnl.c b/src/mnl.c
index 0fb36bd..0158924 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -776,9 +776,9 @@ static void nft_dev_array_free(const struct nft_dev *dev_array)
int i = 0;
while (dev_array[i].ifname != NULL)
- xfree(dev_array[i++].ifname);
+ free_const(dev_array[i++].ifname);
- xfree(dev_array);
+ free_const(dev_array);
}
static void mnl_nft_chain_devs_build(struct nlmsghdr *nlh, struct cmd *cmd)
@@ -2175,10 +2175,10 @@ static struct basehook *basehook_alloc(void)
static void basehook_free(struct basehook *b)
{
list_del(&b->list);
- xfree(b->module_name);
- xfree(b->hookfn);
- xfree(b->chain);
- xfree(b->table);
+ free_const(b->module_name);
+ free_const(b->hookfn);
+ free_const(b->chain);
+ free_const(b->table);
xfree(b);
}
diff --git a/src/optimize.c b/src/optimize.c
index 27e0ffe..9ae9283 100644
--- a/src/optimize.c
+++ b/src/optimize.c
@@ -1194,7 +1194,7 @@ static void merge_rules(const struct optimize_ctx *ctx,
}
if (ctx->rule[from]->comment) {
- xfree(ctx->rule[from]->comment);
+ free_const(ctx->rule[from]->comment);
ctx->rule[from]->comment = NULL;
}
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 5c2d14b..b485a48 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -154,13 +154,13 @@ static struct expr *ifname_expr_alloc(const struct location *location,
struct expr *expr;
if (length == 0) {
- xfree(name);
+ free_const(name);
erec_queue(error(location, "empty interface name"), queue);
return NULL;
}
if (length >= IFNAMSIZ) {
- xfree(name);
+ free_const(name);
erec_queue(error(location, "interface name too long"), queue);
return NULL;
}
@@ -168,7 +168,7 @@ static struct expr *ifname_expr_alloc(const struct location *location,
expr = constant_expr_alloc(location, &ifname_type, BYTEORDER_HOST_ENDIAN,
length * BITS_PER_BYTE, name);
- xfree(name);
+ free_const(name);
return expr;
}
@@ -358,7 +358,7 @@ int nft_lex(void *, void *, void *);
%token <string> STRING "string"
%token <string> QUOTED_STRING "quoted string"
%token <string> ASTERISK_STRING "string with a trailing asterisk"
-%destructor { xfree($$); } STRING QUOTED_STRING ASTERISK_STRING
+%destructor { free_const($$); } STRING QUOTED_STRING ASTERISK_STRING
%token LL_HDR "ll"
%token NETWORK_HDR "nh"
@@ -674,7 +674,7 @@ int nft_lex(void *, void *, void *);
%type <limit_rate> limit_rate_bytes
%type <string> identifier type_identifier string comment_spec
-%destructor { xfree($$); } identifier type_identifier string comment_spec
+%destructor { free_const($$); } identifier type_identifier string comment_spec
%type <val> time_spec time_spec_or_num_s quota_used
@@ -709,7 +709,7 @@ int nft_lex(void *, void *, void *);
%type <val32> int_num chain_policy
%type <prio_spec> extended_prio_spec prio_spec
%type <string> extended_prio_name quota_unit basehook_device_name
-%destructor { xfree($$); } extended_prio_name quota_unit basehook_device_name
+%destructor { free_const($$); } extended_prio_name quota_unit basehook_device_name
%type <expr> dev_spec
%destructor { xfree($$); } dev_spec
@@ -930,7 +930,7 @@ int nft_lex(void *, void *, void *);
%type <val> markup_format
%type <string> monitor_event
-%destructor { xfree($$); } monitor_event
+%destructor { free_const($$); } monitor_event
%type <val> monitor_object monitor_format
%type <val> synproxy_ts synproxy_sack
@@ -1055,10 +1055,10 @@ close_scope_xt : { scanner_pop_start_cond(nft->scanner, PARSER_SC_XT); }
common_block : INCLUDE QUOTED_STRING stmt_separator
{
if (scanner_include_file(nft, scanner, $2, &@$) < 0) {
- xfree($2);
+ free_const($2);
YYERROR;
}
- xfree($2);
+ free_const($2);
}
| DEFINE identifier '=' initializer_expr stmt_separator
{
@@ -1068,19 +1068,19 @@ common_block : INCLUDE QUOTED_STRING stmt_separator
erec_queue(error(&@2, "redefinition of symbol '%s'", $2),
state->msgs);
expr_free($4);
- xfree($2);
+ free_const($2);
YYERROR;
}
symbol_bind(scope, $2, $4);
- xfree($2);
+ free_const($2);
}
| REDEFINE identifier '=' initializer_expr stmt_separator
{
struct scope *scope = current_scope(state);
symbol_bind(scope, $2, $4);
- xfree($2);
+ free_const($2);
}
| UNDEFINE identifier stmt_separator
{
@@ -1089,10 +1089,10 @@ common_block : INCLUDE QUOTED_STRING stmt_separator
if (symbol_unbind(scope, $2) < 0) {
erec_queue(error(&@2, "undefined symbol '%s'", $2),
state->msgs);
- xfree($2);
+ free_const($2);
YYERROR;
}
- xfree($2);
+ free_const($2);
}
| error stmt_separator
{
@@ -1884,7 +1884,7 @@ table_options : FLAGS table_flags
| comment_spec
{
if (already_set($<table>0->comment, &@$, state)) {
- xfree($1);
+ free_const($1);
YYERROR;
}
$<table>0->comment = $1;
@@ -2073,7 +2073,7 @@ chain_block : /* empty */ { $$ = $<chain>-1; }
| chain_block comment_spec stmt_separator
{
if (already_set($1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$1->comment = $2;
@@ -2199,7 +2199,7 @@ set_block : /* empty */ { $$ = $<set>-1; }
| set_block comment_spec stmt_separator
{
if (already_set($1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$1->comment = $2;
@@ -2316,7 +2316,7 @@ map_block : /* empty */ { $$ = $<set>-1; }
| map_block comment_spec stmt_separator
{
if (already_set($1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$1->comment = $2;
@@ -2355,10 +2355,10 @@ flowtable_block : /* empty */ { $$ = $<flowtable>-1; }
if ($$->hook.name == NULL) {
erec_queue(error(&@3, "unknown chain hook"),
state->msgs);
- xfree($3);
+ free_const($3);
YYERROR;
}
- xfree($3);
+ free_const($3);
$$->priority = $4;
}
@@ -2432,12 +2432,12 @@ data_type_atom_expr : type_identifier
if (dtype == NULL) {
erec_queue(error(&@1, "unknown datatype %s", $1),
state->msgs);
- xfree($1);
+ free_const($1);
YYERROR;
}
$$ = constant_expr_alloc(&@1, dtype, dtype->byteorder,
dtype->size, NULL);
- xfree($1);
+ free_const($1);
}
| TIME
{
@@ -2474,7 +2474,7 @@ counter_block : /* empty */ { $$ = $<obj>-1; }
| counter_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2491,7 +2491,7 @@ quota_block : /* empty */ { $$ = $<obj>-1; }
| quota_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2508,7 +2508,7 @@ ct_helper_block : /* empty */ { $$ = $<obj>-1; }
| ct_helper_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2529,7 +2529,7 @@ ct_timeout_block : /*empty */
| ct_timeout_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2546,7 +2546,7 @@ ct_expect_block : /*empty */ { $$ = $<obj>-1; }
| ct_expect_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2563,7 +2563,7 @@ limit_block : /* empty */ { $$ = $<obj>-1; }
| limit_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2580,7 +2580,7 @@ secmark_block : /* empty */ { $$ = $<obj>-1; }
| secmark_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2597,7 +2597,7 @@ synproxy_block : /* empty */ { $$ = $<obj>-1; }
| synproxy_block comment_spec
{
if (already_set($<obj>1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$<obj>1->comment = $2;
@@ -2618,12 +2618,12 @@ hook_spec : TYPE close_scope_type STRING HOOK STRING dev_spec prio_spec
if (chain_type == NULL) {
erec_queue(error(&@3, "unknown chain type"),
state->msgs);
- xfree($3);
+ free_const($3);
YYERROR;
}
$<chain>0->type.loc = @3;
$<chain>0->type.str = xstrdup(chain_type);
- xfree($3);
+ free_const($3);
$<chain>0->loc = @$;
$<chain>0->hook.loc = @5;
@@ -2631,10 +2631,10 @@ hook_spec : TYPE close_scope_type STRING HOOK STRING dev_spec prio_spec
if ($<chain>0->hook.name == NULL) {
erec_queue(error(&@5, "unknown chain hook"),
state->msgs);
- xfree($5);
+ free_const($5);
YYERROR;
}
- xfree($5);
+ free_const($5);
$<chain>0->dev_expr = $6;
$<chain>0->priority = $7;
@@ -2681,7 +2681,7 @@ extended_prio_spec : int_num
BYTEORDER_HOST_ENDIAN,
strlen($1) * BITS_PER_BYTE,
$1);
- xfree($1);
+ free_const($1);
$$ = spec;
}
| extended_prio_name PLUS NUM
@@ -2694,7 +2694,7 @@ extended_prio_spec : int_num
BYTEORDER_HOST_ENDIAN,
strlen(str) * BITS_PER_BYTE,
str);
- xfree($1);
+ free_const($1);
$$ = spec;
}
| extended_prio_name DASH NUM
@@ -2707,7 +2707,7 @@ extended_prio_spec : int_num
BYTEORDER_HOST_ENDIAN,
strlen(str) * BITS_PER_BYTE,
str);
- xfree($1);
+ free_const($1);
$$ = spec;
}
;
@@ -2792,7 +2792,7 @@ time_spec : STRING
uint64_t res;
erec = time_parse(&@1, $1, &res);
- xfree($1);
+ free_const($1);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
@@ -2993,7 +2993,7 @@ comment_spec : COMMENT string
erec_queue(error(&@2, "comment too long, %d characters maximum allowed",
NFTNL_UDATA_COMMENT_MAXLEN),
state->msgs);
- xfree($2);
+ free_const($2);
YYERROR;
}
$$ = $2;
@@ -3094,8 +3094,8 @@ stmt : verdict_stmt
xt_stmt : XT STRING string
{
$$ = NULL;
- xfree($2);
- xfree($3);
+ free_const($2);
+ free_const($3);
erec_queue(error(&@$, "unsupported xtables compat expression, use iptables-nft with this ruleset"),
state->msgs);
YYERROR;
@@ -3253,7 +3253,7 @@ log_arg : PREFIX string
expr = constant_expr_alloc(&@$, &string_type,
BYTEORDER_HOST_ENDIAN,
(strlen($2) + 1) * BITS_PER_BYTE, $2);
- xfree($2);
+ free_const($2);
$<stmt>0->log.prefix = expr;
$<stmt>0->log.flags |= STMT_LOG_PREFIX;
break;
@@ -3327,7 +3327,7 @@ log_arg : PREFIX string
state->msgs);
}
expr_free(expr);
- xfree($2);
+ free_const($2);
YYERROR;
}
item = variable_expr_alloc(&@$, scope, sym);
@@ -3357,7 +3357,7 @@ log_arg : PREFIX string
}
}
- xfree($2);
+ free_const($2);
$<stmt>0->log.prefix = expr;
$<stmt>0->log.flags |= STMT_LOG_PREFIX;
}
@@ -3410,10 +3410,10 @@ level_type : string
else {
erec_queue(error(&@1, "invalid log level"),
state->msgs);
- xfree($1);
+ free_const($1);
YYERROR;
}
- xfree($1);
+ free_const($1);
}
;
@@ -3503,7 +3503,7 @@ quota_used : /* empty */ { $$ = 0; }
uint64_t rate;
erec = data_unit_parse(&@$, $3, &rate);
- xfree($3);
+ free_const($3);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
@@ -3518,7 +3518,7 @@ quota_stmt : QUOTA quota_mode NUM quota_unit quota_used close_scope_quota
uint64_t rate;
erec = data_unit_parse(&@$, $4, &rate);
- xfree($4);
+ free_const($4);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
@@ -3562,7 +3562,7 @@ limit_rate_bytes : NUM STRING
uint64_t rate, unit;
erec = rate_parse(&@$, $2, &rate, &unit);
- xfree($2);
+ free_const($2);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
@@ -3584,7 +3584,7 @@ limit_bytes : NUM BYTES { $$ = $1; }
uint64_t rate;
erec = data_unit_parse(&@$, $2, &rate);
- xfree($2);
+ free_const($2);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
@@ -3613,7 +3613,7 @@ reject_with_expr : STRING
{
$$ = symbol_expr_alloc(&@$, SYMBOL_VALUE,
current_scope(state), $1);
- xfree($1);
+ free_const($1);
}
| integer_expr { $$ = $1; }
;
@@ -4277,12 +4277,12 @@ variable_expr : '$' identifier
erec_queue(error(&@2, "unknown identifier '%s'", $2),
state->msgs);
}
- xfree($2);
+ free_const($2);
YYERROR;
}
$$ = variable_expr_alloc(&@$, scope, sym);
- xfree($2);
+ free_const($2);
}
;
@@ -4292,7 +4292,7 @@ symbol_expr : variable_expr
$$ = symbol_expr_alloc(&@$, SYMBOL_VALUE,
current_scope(state),
$1);
- xfree($1);
+ free_const($1);
}
;
@@ -4305,7 +4305,7 @@ set_ref_symbol_expr : AT identifier close_scope_at
$$ = symbol_expr_alloc(&@$, SYMBOL_SET,
current_scope(state),
$2);
- xfree($2);
+ free_const($2);
}
;
@@ -4402,10 +4402,10 @@ osf_ttl : /* empty */
else {
erec_queue(error(&@2, "invalid ttl option"),
state->msgs);
- xfree($2);
+ free_const($2);
YYERROR;
}
- xfree($2);
+ free_const($2);
}
;
@@ -4575,7 +4575,7 @@ set_elem_option : TIMEOUT time_spec
| comment_spec
{
if (already_set($<expr>0->comment, &@1, state)) {
- xfree($1);
+ free_const($1);
YYERROR;
}
$<expr>0->comment = $1;
@@ -4657,7 +4657,7 @@ set_elem_stmt : COUNTER close_scope_counter
uint64_t rate;
erec = data_unit_parse(&@$, $4, &rate);
- xfree($4);
+ free_const($4);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
@@ -4690,7 +4690,7 @@ set_elem_expr_option : TIMEOUT time_spec
| comment_spec
{
if (already_set($<expr>0->comment, &@1, state)) {
- xfree($1);
+ free_const($1);
YYERROR;
}
$<expr>0->comment = $1;
@@ -4742,7 +4742,7 @@ quota_config : quota_mode NUM quota_unit quota_used
uint64_t rate;
erec = data_unit_parse(&@$, $3, &rate);
- xfree($3);
+ free_const($3);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
@@ -4771,10 +4771,10 @@ secmark_config : string
ret = snprintf(secmark->ctx, sizeof(secmark->ctx), "%s", $1);
if (ret <= 0 || ret >= (int)sizeof(secmark->ctx)) {
erec_queue(error(&@1, "invalid context '%s', max length is %u\n", $1, (int)sizeof(secmark->ctx)), state->msgs);
- xfree($1);
+ free_const($1);
YYERROR;
}
- xfree($1);
+ free_const($1);
}
;
@@ -4811,7 +4811,7 @@ ct_helper_config : TYPE QUOTED_STRING PROTOCOL ct_l4protoname stmt_separator cl
erec_queue(error(&@2, "invalid name '%s', max length is %u\n", $2, (int)sizeof(ct->name)), state->msgs);
YYERROR;
}
- xfree($2);
+ free_const($2);
ct->l4proto = $4;
}
@@ -5206,7 +5206,7 @@ chain_expr : variable_expr
BYTEORDER_HOST_ENDIAN,
strlen($1) * BITS_PER_BYTE,
$1);
- xfree($1);
+ free_const($1);
}
;
@@ -5224,7 +5224,7 @@ meta_expr : META meta_key close_scope_meta
unsigned int key;
erec = meta_key_parse(&@$, $2, &key);
- xfree($2);
+ free_const($2);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
@@ -5301,7 +5301,7 @@ meta_stmt : META meta_key SET stmt_expr close_scope_meta
unsigned int key;
erec = meta_key_parse(&@$, $2, &key);
- xfree($2);
+ free_const($2);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
@@ -5612,10 +5612,10 @@ payload_base_spec : LL_HDR { $$ = PROTO_BASE_LL_HDR; }
$$ = PROTO_BASE_INNER_HDR;
} else {
erec_queue(error(&@1, "unknown raw payload base"), state->msgs);
- xfree($1);
+ free_const($1);
YYERROR;
}
- xfree($1);
+ free_const($1);
}
;
diff --git a/src/rule.c b/src/rule.c
index a0e151d..76cd58c 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -104,11 +104,11 @@ int timeout_str2num(uint16_t l4proto, struct timeout_state *ts)
void handle_free(struct handle *h)
{
- xfree(h->table.name);
- xfree(h->chain.name);
- xfree(h->set.name);
- xfree(h->flowtable.name);
- xfree(h->obj.name);
+ free_const(h->table.name);
+ free_const(h->chain.name);
+ free_const(h->set.name);
+ free_const(h->flowtable.name);
+ free_const(h->obj.name);
}
void handle_merge(struct handle *dst, const struct handle *src)
@@ -194,7 +194,7 @@ void set_free(struct set *set)
expr_free(set->init);
if (set->comment)
- xfree(set->comment);
+ free_const(set->comment);
handle_free(&set->handle);
list_for_each_entry_safe(stmt, next, &set->stmt_list, list)
stmt_free(stmt);
@@ -479,7 +479,7 @@ void rule_free(struct rule *rule)
return;
stmt_list_free(&rule->stmts);
handle_free(&rule->handle);
- xfree(rule->comment);
+ free_const(rule->comment);
xfree(rule);
}
@@ -557,7 +557,7 @@ void scope_release(const struct scope *scope)
list_for_each_entry_safe(sym, next, &scope->symbols, list) {
assert(sym->refcnt == 1);
list_del(&sym->list);
- xfree(sym->identifier);
+ free_const(sym->identifier);
expr_free(sym->expr);
xfree(sym);
}
@@ -597,7 +597,7 @@ struct symbol *symbol_get(const struct scope *scope, const char *identifier)
static void symbol_put(struct symbol *sym)
{
if (--sym->refcnt == 0) {
- xfree(sym->identifier);
+ free_const(sym->identifier);
expr_free(sym->expr);
xfree(sym);
}
@@ -730,14 +730,14 @@ void chain_free(struct chain *chain)
rule_free(rule);
handle_free(&chain->handle);
scope_release(&chain->scope);
- xfree(chain->type.str);
+ free_const(chain->type.str);
expr_free(chain->dev_expr);
for (i = 0; i < chain->dev_array_len; i++)
- xfree(chain->dev_array[i]);
+ free_const(chain->dev_array[i]);
xfree(chain->dev_array);
expr_free(chain->priority.expr);
expr_free(chain->policy);
- xfree(chain->comment);
+ free_const(chain->comment);
xfree(chain);
}
@@ -1151,7 +1151,7 @@ void table_free(struct table *table)
if (--table->refcnt > 0)
return;
if (table->comment)
- xfree(table->comment);
+ free_const(table->comment);
list_for_each_entry_safe(chain, next, &table->chains, list)
chain_free(chain);
list_for_each_entry_safe(chain, next, &table->chain_bindings, cache.list)
@@ -1360,7 +1360,7 @@ struct monitor *monitor_alloc(uint32_t format, uint32_t type, const char *event)
void monitor_free(struct monitor *m)
{
- xfree(m->event);
+ free_const(m->event);
xfree(m);
}
@@ -1416,7 +1416,7 @@ void cmd_free(struct cmd *cmd)
}
}
xfree(cmd->attr);
- xfree(cmd->arg);
+ free_const(cmd->arg);
xfree(cmd);
}
@@ -1654,14 +1654,14 @@ void obj_free(struct obj *obj)
{
if (--obj->refcnt > 0)
return;
- xfree(obj->comment);
+ free_const(obj->comment);
handle_free(&obj->handle);
if (obj->type == NFT_OBJECT_CT_TIMEOUT) {
struct timeout_state *ts, *next;
list_for_each_entry_safe(ts, next, &obj->ct_timeout.timeout_list, head) {
list_del(&ts->head);
- xfree(ts->timeout_str);
+ free_const(ts->timeout_str);
xfree(ts);
}
}
@@ -2074,7 +2074,7 @@ void flowtable_free(struct flowtable *flowtable)
if (flowtable->dev_array != NULL) {
for (i = 0; i < flowtable->dev_array_len; i++)
- xfree(flowtable->dev_array[i]);
+ free_const(flowtable->dev_array[i]);
xfree(flowtable->dev_array);
}
xfree(flowtable);
diff --git a/src/scanner.l b/src/scanner.l
index 88376b7..93a31f2 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -1261,8 +1261,8 @@ void *scanner_init(struct parser_state *state)
static void input_descriptor_destroy(const struct input_descriptor *indesc)
{
if (indesc->name)
- xfree(indesc->name);
- xfree(indesc);
+ free_const(indesc->name);
+ free_const(indesc);
}
static void input_descriptor_list_destroy(struct parser_state *state)
diff --git a/src/statement.c b/src/statement.c
index f5176e6..994b522 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -183,7 +183,7 @@ static void meter_stmt_destroy(struct stmt *stmt)
expr_free(stmt->meter.key);
expr_free(stmt->meter.set);
stmt_free(stmt->meter.stmt);
- xfree(stmt->meter.name);
+ free_const(stmt->meter.name);
}
static const struct stmt_ops meter_stmt_ops = {
diff --git a/src/xt.c b/src/xt.c
index 3cb5f02..48b2873 100644
--- a/src/xt.c
+++ b/src/xt.c
@@ -124,7 +124,7 @@ void xt_stmt_xlate(const struct stmt *stmt, struct output_ctx *octx)
void xt_stmt_destroy(struct stmt *stmt)
{
- xfree(stmt->xt.name);
+ free_const(stmt->xt.name);
xfree(stmt->xt.info);
}

View File

@ -0,0 +1,635 @@
From 91824e5e97716d8d433ea8c064bd7eb701e006f6 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:40 +0200
Subject: [PATCH] src: remove xfree() and use plain free()
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit d7af8ab26d62e602b73e1017183f899923d8d5ae
commit d7af8ab26d62e602b73e1017183f899923d8d5ae
Author: Thomas Haller <thaller@redhat.com>
Date: Tue Oct 24 11:57:10 2023 +0200
src: remove xfree() and use plain free()
xmalloc() (and similar x-functions) are used for allocation. They wrap
malloc()/realloc() but will abort the program on ENOMEM.
The meaning of xmalloc() is that it wraps malloc() but aborts on
failure. I don't think x-functions should have the notion, that this
were potentially a different memory allocator that must be paired
with a particular xfree().
Even if the original intent was that the allocator is abstracted (and
possibly not backed by standard malloc()/free()), then that doesn't seem
a good idea. Nowadays libc allocators are pretty good, and we would need
a very special use cases to switch to something else. In other words,
it will never happen that xmalloc() is not backed by malloc().
Also there were a few places, where a xmalloc() was already "wrongly"
paired with free() (for example, iface_cache_release(), exit_cookie(),
nft_run_cmd_from_buffer()).
Or note how pid2name() returns an allocated string from fscanf(), which
needs to be freed with free() (and not xfree()). This requirement
bubbles up the callers portid2name() and name_by_portid(). This case was
actually handled correctly and the buffer was freed with free(). But it
shows that mixing different allocators is cumbersome to get right. Of
course, we don't actually have different allocators and whether to use
free() or xfree() makes no different. The point is that xfree() serves
no actual purpose except raising irrelevant questions about whether
x-functions are correctly paired with xfree().
Note that xfree() also used to accept const pointers. It is bad to
unconditionally for all deallocations. Instead prefer to use plain
free(). To free a const pointer use free_const() which obviously wraps
free, as indicated by the name.
Signed-off-by: Thomas Haller <thaller@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
include/utils.h | 1 -
src/cache.c | 6 +++---
src/datatype.c | 4 ++--
src/erec.c | 6 +++---
src/evaluate.c | 4 ++--
src/expression.c | 2 +-
src/json.c | 2 +-
src/libnftables.c | 12 ++++++------
src/meta.c | 4 ++--
src/misspell.c | 2 +-
src/mnl.c | 4 ++--
src/netlink_linearize.c | 4 ++--
src/optimize.c | 10 +++++-----
src/parser_bison.y | 14 +++++++-------
src/rule.c | 32 ++++++++++++++++----------------
src/scanner.l | 2 +-
src/segtree.c | 4 ++--
src/statement.c | 2 +-
src/utils.c | 5 -----
src/xt.c | 8 ++++----
20 files changed, 61 insertions(+), 67 deletions(-)
diff --git a/include/utils.h b/include/utils.h
index 36a28f8..e18fabe 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -142,7 +142,6 @@ extern void __memory_allocation_error(const char *filename, uint32_t line) __nor
#define memory_allocation_error() \
__memory_allocation_error(__FILE__, __LINE__);
-extern void xfree(const void *ptr);
extern void *xmalloc(size_t size);
extern void *xmalloc_array(size_t nmemb, size_t size);
extern void *xrealloc(void *ptr, size_t size);
diff --git a/src/cache.c b/src/cache.c
index 0ac0f7c..a3cd795 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -126,9 +126,9 @@ void nft_cache_filter_fini(struct nft_cache_filter *filter)
struct nft_filter_obj *obj, *next;
list_for_each_entry_safe(obj, next, &filter->obj[i].head, list)
- xfree(obj);
+ free(obj);
}
- xfree(filter);
+ free(filter);
}
static void cache_filter_add(struct nft_cache_filter *filter,
@@ -1275,7 +1275,7 @@ void cache_init(struct cache *cache)
void cache_free(struct cache *cache)
{
- xfree(cache->ht);
+ free(cache->ht);
}
void cache_add(struct cache_item *item, struct cache *cache, uint32_t hash)
diff --git a/src/datatype.c b/src/datatype.c
index 9353ff2..a92f41d 100644
--- a/src/datatype.c
+++ b/src/datatype.c
@@ -1298,7 +1298,7 @@ void datatype_free(const struct datatype *ptr)
free_const(dtype->name);
free_const(dtype->desc);
- xfree(dtype);
+ free(dtype);
}
const struct datatype *concat_type_alloc(uint32_t type)
@@ -1545,7 +1545,7 @@ static void cgroupv2_type_print(const struct expr *expr,
else
nft_print(octx, "%" PRIu64, id);
- xfree(cgroup_path);
+ free(cgroup_path);
}
static struct error_record *cgroupv2_type_parse(struct parse_ctx *ctx,
diff --git a/src/erec.c b/src/erec.c
index cd9f62b..fe66abb 100644
--- a/src/erec.c
+++ b/src/erec.c
@@ -43,8 +43,8 @@ void erec_add_location(struct error_record *erec, const struct location *loc)
void erec_destroy(struct error_record *erec)
{
- xfree(erec->msg);
- xfree(erec);
+ free(erec->msg);
+ free(erec);
}
__attribute__((format(printf, 3, 0)))
@@ -203,7 +203,7 @@ void erec_print(struct output_ctx *octx, const struct error_record *erec,
}
pbuf[end] = '\0';
fprintf(f, "%s", pbuf);
- xfree(pbuf);
+ free(pbuf);
}
fprintf(f, "\n");
}
diff --git a/src/evaluate.c b/src/evaluate.c
index c41210b..27ecaa2 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -3249,7 +3249,7 @@ static int stmt_reject_gen_dependency(struct eval_ctx *ctx, struct stmt *stmt,
*/
list_add(&nstmt->list, &ctx->rule->stmts);
out:
- xfree(payload);
+ free(payload);
return ret;
}
@@ -5148,7 +5148,7 @@ static int ct_timeout_evaluate(struct eval_ctx *ctx, struct obj *obj)
ct->timeout[ts->timeout_index] = ts->timeout_value;
list_del(&ts->head);
free_const(ts->timeout_str);
- xfree(ts);
+ free(ts);
}
return 0;
diff --git a/src/expression.c b/src/expression.c
index 0b4a537..dde48b6 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -94,7 +94,7 @@ void expr_free(struct expr *expr)
*/
if (expr->etype != EXPR_INVALID)
expr_destroy(expr);
- xfree(expr);
+ free(expr);
}
void expr_print(const struct expr *expr, struct output_ctx *octx)
diff --git a/src/json.c b/src/json.c
index dad93e5..98fa026 100644
--- a/src/json.c
+++ b/src/json.c
@@ -83,7 +83,7 @@ static json_t *set_dtype_json(const struct expr *key)
json_array_append_new(root, jtok);
tok = strtok_r(NULL, " .", &tok_safe);
}
- xfree(namedup);
+ free(namedup);
return root;
}
diff --git a/src/libnftables.c b/src/libnftables.c
index 866b5c6..ec90200 100644
--- a/src/libnftables.c
+++ b/src/libnftables.c
@@ -158,7 +158,7 @@ void nft_ctx_clear_vars(struct nft_ctx *ctx)
free_const(ctx->vars[i].value);
}
ctx->num_vars = 0;
- xfree(ctx->vars);
+ free(ctx->vars);
}
EXPORT_SYMBOL(nft_ctx_add_include_path);
@@ -182,9 +182,9 @@ EXPORT_SYMBOL(nft_ctx_clear_include_paths);
void nft_ctx_clear_include_paths(struct nft_ctx *ctx)
{
while (ctx->num_include_paths)
- xfree(ctx->include_paths[--ctx->num_include_paths]);
+ free(ctx->include_paths[--ctx->num_include_paths]);
- xfree(ctx->include_paths);
+ free(ctx->include_paths);
ctx->include_paths = NULL;
}
@@ -343,9 +343,9 @@ void nft_ctx_free(struct nft_ctx *ctx)
nft_ctx_clear_vars(ctx);
nft_ctx_clear_include_paths(ctx);
scope_free(ctx->top_scope);
- xfree(ctx->state);
+ free(ctx->state);
nft_exit(ctx);
- xfree(ctx);
+ free(ctx);
}
EXPORT_SYMBOL(nft_ctx_set_output);
@@ -745,7 +745,7 @@ err:
if (indesc->name)
free_const(indesc->name);
- xfree(indesc);
+ free(indesc);
}
}
free_const(nft->vars_ctx.buf);
diff --git a/src/meta.c b/src/meta.c
index 1f1e33c..4e55e00 100644
--- a/src/meta.c
+++ b/src/meta.c
@@ -99,13 +99,13 @@ static struct error_record *tchandle_type_parse(struct parse_ctx *ctx,
handle = strtoull(sym->identifier, NULL, 0);
}
out:
- xfree(str);
+ free(str);
*res = constant_expr_alloc(&sym->location, sym->dtype,
BYTEORDER_HOST_ENDIAN,
sizeof(handle) * BITS_PER_BYTE, &handle);
return NULL;
err:
- xfree(str);
+ free(str);
return error(&sym->location, "Could not parse %s", sym->dtype->desc);
}
diff --git a/src/misspell.c b/src/misspell.c
index c1e58a0..f5354fa 100644
--- a/src/misspell.c
+++ b/src/misspell.c
@@ -72,7 +72,7 @@ static unsigned int string_distance(const char *a, const char *b)
ret = DISTANCE(len_a, len_b);
- xfree(distance);
+ free(distance);
return ret;
}
diff --git a/src/mnl.c b/src/mnl.c
index 0158924..9e4bfcd 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -242,7 +242,7 @@ static void mnl_err_list_node_add(struct list_head *err_list, int error,
void mnl_err_list_free(struct mnl_err *err)
{
list_del(&err->head);
- xfree(err);
+ free(err);
}
static void mnl_set_sndbuffer(struct netlink_ctx *ctx)
@@ -2179,7 +2179,7 @@ static void basehook_free(struct basehook *b)
free_const(b->hookfn);
free_const(b->chain);
free_const(b->table);
- xfree(b);
+ free(b);
}
static void basehook_list_add_tail(struct basehook *b, struct list_head *head)
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index 0c62341..df395ba 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -1743,9 +1743,9 @@ void netlink_linearize_fini(struct netlink_linearize_ctx *lctx)
for (i = 0; i < NFT_EXPR_LOC_HSIZE; i++) {
list_for_each_entry_safe(eloc, next, &lctx->expr_loc_htable[i], hlist)
- xfree(eloc);
+ free(eloc);
}
- xfree(lctx->expr_loc_htable);
+ free(lctx->expr_loc_htable);
}
void netlink_linearize_rule(struct netlink_ctx *ctx,
diff --git a/src/optimize.c b/src/optimize.c
index 9ae9283..b90dd99 100644
--- a/src/optimize.c
+++ b/src/optimize.c
@@ -1347,16 +1347,16 @@ static int chain_optimize(struct nft_ctx *nft, struct list_head *rules)
}
ret = 0;
for (i = 0; i < ctx->num_rules; i++)
- xfree(ctx->stmt_matrix[i]);
+ free(ctx->stmt_matrix[i]);
- xfree(ctx->stmt_matrix);
- xfree(merge);
+ free(ctx->stmt_matrix);
+ free(merge);
err:
for (i = 0; i < ctx->num_stmts; i++)
stmt_free(ctx->stmt[i]);
- xfree(ctx->rule);
- xfree(ctx);
+ free(ctx->rule);
+ free(ctx);
return ret;
}
diff --git a/src/parser_bison.y b/src/parser_bison.y
index b485a48..751b3e1 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -712,7 +712,7 @@ int nft_lex(void *, void *, void *);
%destructor { free_const($$); } extended_prio_name quota_unit basehook_device_name
%type <expr> dev_spec
-%destructor { xfree($$); } dev_spec
+%destructor { free($$); } dev_spec
%type <table> table_block_alloc table_block
%destructor { close_scope(state); table_free($$); } table_block_alloc
@@ -741,7 +741,7 @@ int nft_lex(void *, void *, void *);
%destructor { obj_free($$); } obj_block_alloc
%type <list> stmt_list stateful_stmt_list set_elem_stmt_list
-%destructor { stmt_list_free($$); xfree($$); } stmt_list stateful_stmt_list set_elem_stmt_list
+%destructor { stmt_list_free($$); free($$); } stmt_list stateful_stmt_list set_elem_stmt_list
%type <stmt> stmt match_stmt verdict_stmt set_elem_stmt
%destructor { stmt_free($$); } stmt match_stmt verdict_stmt set_elem_stmt
%type <stmt> counter_stmt counter_stmt_alloc stateful_stmt last_stmt
@@ -967,7 +967,7 @@ int nft_lex(void *, void *, void *);
%type <val> ct_l4protoname ct_obj_type ct_cmd_type
%type <list> timeout_states timeout_state
-%destructor { xfree($$); } timeout_states timeout_state
+%destructor { free($$); } timeout_states timeout_state
%type <val> xfrm_state_key xfrm_state_proto_key xfrm_dir xfrm_spnum
%type <expr> xfrm_expr
@@ -3030,7 +3030,7 @@ rule_alloc : stmt_list
list_for_each_entry(i, $1, list)
$$->num_stmts++;
list_splice_tail($1, &$$->stmts);
- xfree($1);
+ free($1);
}
;
@@ -4537,7 +4537,7 @@ set_elem_expr : set_elem_expr_alloc
{
$$ = $1;
list_splice_tail($3, &$$->stmt_list);
- xfree($3);
+ free($3);
}
;
@@ -4549,7 +4549,7 @@ set_elem_expr_alloc : set_elem_key_expr set_elem_stmt_list
{
$$ = set_elem_expr_alloc(&@1, $1);
list_splice_tail($2, &$$->stmt_list);
- xfree($2);
+ free($2);
}
| set_elem_key_expr
{
@@ -4861,7 +4861,7 @@ ct_timeout_config : PROTOCOL ct_l4protoname stmt_separator
ct = &$<obj>0->ct_timeout;
list_splice_tail($4, &ct->timeout_list);
- xfree($4);
+ free($4);
}
| L3PROTOCOL family_spec_explicit stmt_separator
{
diff --git a/src/rule.c b/src/rule.c
index 76cd58c..633fae7 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -200,7 +200,7 @@ void set_free(struct set *set)
stmt_free(stmt);
expr_free(set->key);
expr_free(set->data);
- xfree(set);
+ free(set);
}
struct set *set_lookup_fuzzy(const char *set_name,
@@ -480,7 +480,7 @@ void rule_free(struct rule *rule)
stmt_list_free(&rule->stmts);
handle_free(&rule->handle);
free_const(rule->comment);
- xfree(rule);
+ free(rule);
}
void rule_print(const struct rule *rule, struct output_ctx *octx)
@@ -559,14 +559,14 @@ void scope_release(const struct scope *scope)
list_del(&sym->list);
free_const(sym->identifier);
expr_free(sym->expr);
- xfree(sym);
+ free(sym);
}
}
void scope_free(struct scope *scope)
{
scope_release(scope);
- xfree(scope);
+ free(scope);
}
void symbol_bind(struct scope *scope, const char *identifier, struct expr *expr)
@@ -599,7 +599,7 @@ static void symbol_put(struct symbol *sym)
if (--sym->refcnt == 0) {
free_const(sym->identifier);
expr_free(sym->expr);
- xfree(sym);
+ free(sym);
}
}
@@ -734,11 +734,11 @@ void chain_free(struct chain *chain)
expr_free(chain->dev_expr);
for (i = 0; i < chain->dev_array_len; i++)
free_const(chain->dev_array[i]);
- xfree(chain->dev_array);
+ free(chain->dev_array);
expr_free(chain->priority.expr);
expr_free(chain->policy);
free_const(chain->comment);
- xfree(chain);
+ free(chain);
}
struct chain *chain_binding_lookup(const struct table *table,
@@ -1181,7 +1181,7 @@ void table_free(struct table *table)
cache_free(&table->set_cache);
cache_free(&table->obj_cache);
cache_free(&table->ft_cache);
- xfree(table);
+ free(table);
}
struct table *table_get(struct table *table)
@@ -1342,7 +1342,7 @@ struct markup *markup_alloc(uint32_t format)
void markup_free(struct markup *m)
{
- xfree(m);
+ free(m);
}
struct monitor *monitor_alloc(uint32_t format, uint32_t type, const char *event)
@@ -1361,7 +1361,7 @@ struct monitor *monitor_alloc(uint32_t format, uint32_t type, const char *event)
void monitor_free(struct monitor *m)
{
free_const(m->event);
- xfree(m);
+ free(m);
}
void cmd_free(struct cmd *cmd)
@@ -1415,9 +1415,9 @@ void cmd_free(struct cmd *cmd)
BUG("invalid command object type %u\n", cmd->obj);
}
}
- xfree(cmd->attr);
+ free(cmd->attr);
free_const(cmd->arg);
- xfree(cmd);
+ free(cmd);
}
#include <netlink.h>
@@ -1662,10 +1662,10 @@ void obj_free(struct obj *obj)
list_for_each_entry_safe(ts, next, &obj->ct_timeout.timeout_list, head) {
list_del(&ts->head);
free_const(ts->timeout_str);
- xfree(ts);
+ free(ts);
}
}
- xfree(obj);
+ free(obj);
}
struct obj *obj_lookup_fuzzy(const char *obj_name,
@@ -2075,9 +2075,9 @@ void flowtable_free(struct flowtable *flowtable)
if (flowtable->dev_array != NULL) {
for (i = 0; i < flowtable->dev_array_len; i++)
free_const(flowtable->dev_array[i]);
- xfree(flowtable->dev_array);
+ free(flowtable->dev_array);
}
- xfree(flowtable);
+ free(flowtable);
}
static void flowtable_print_declaration(const struct flowtable *flowtable,
diff --git a/src/scanner.l b/src/scanner.l
index 93a31f2..00a0948 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -1284,7 +1284,7 @@ void scanner_destroy(struct nft_ctx *nft)
struct parser_state *state = yyget_extra(nft->scanner);
input_descriptor_list_destroy(state);
- xfree(state->startcond_active);
+ free(state->startcond_active);
yylex_destroy(nft->scanner);
}
diff --git a/src/segtree.c b/src/segtree.c
index c4029f9..7d246de 100644
--- a/src/segtree.c
+++ b/src/segtree.c
@@ -656,6 +656,6 @@ out:
if (catchall)
compound_expr_add(set, catchall);
- xfree(ranges);
- xfree(elements);
+ free(ranges);
+ free(elements);
}
diff --git a/src/statement.c b/src/statement.c
index 994b522..ab144d6 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -51,7 +51,7 @@ void stmt_free(struct stmt *stmt)
return;
if (stmt->ops->destroy)
stmt->ops->destroy(stmt);
- xfree(stmt);
+ free(stmt);
}
void stmt_list_free(struct list_head *list)
diff --git a/src/utils.c b/src/utils.c
index e6ad8b8..2aa1eb4 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -24,11 +24,6 @@ void __noreturn __memory_allocation_error(const char *filename, uint32_t line)
exit(NFT_EXIT_NOMEM);
}
-void xfree(const void *ptr)
-{
- free((void *)ptr);
-}
-
void *xmalloc(size_t size)
{
void *ptr;
diff --git a/src/xt.c b/src/xt.c
index 48b2873..f7bee21 100644
--- a/src/xt.c
+++ b/src/xt.c
@@ -78,7 +78,7 @@ void xt_stmt_xlate(const struct stmt *stmt, struct output_ctx *octx)
rc = mt->xlate(xl, &params);
}
- xfree(m);
+ free(m);
break;
case NFT_XT_WATCHER:
case NFT_XT_TARGET:
@@ -108,14 +108,14 @@ void xt_stmt_xlate(const struct stmt *stmt, struct output_ctx *octx)
rc = tg->xlate(xl, &params);
}
- xfree(t);
+ free(t);
break;
}
if (rc == 1)
nft_print(octx, "%s", xt_xlate_get(xl));
xt_xlate_free(xl);
- xfree(entry);
+ free(entry);
#endif
if (!rc)
nft_print(octx, "xt %s \"%s\"",
@@ -125,7 +125,7 @@ void xt_stmt_xlate(const struct stmt *stmt, struct output_ctx *octx)
void xt_stmt_destroy(struct stmt *stmt)
{
free_const(stmt->xt.name);
- xfree(stmt->xt.info);
+ free(stmt->xt.info);
}
#ifdef HAVE_LIBXTABLES

View File

@ -0,0 +1,41 @@
From 643ad1a2a68d58fd7fbbaefd1af83c60f229ef0b Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:40 +0200
Subject: [PATCH] netlink: fix buffer size for user data in
netlink_delinearize_chain()
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 505a6794422238f9f1d590fe8c1ee3ea7fd46579
commit 505a6794422238f9f1d590fe8c1ee3ea7fd46579
Author: Thomas Haller <thaller@redhat.com>
Date: Wed Nov 8 19:22:20 2023 +0100
netlink: fix buffer size for user data in netlink_delinearize_chain()
The correct define is NFTNL_UDATA_CHAIN_MAX and not NFTNL_UDATA_OBJ_MAX.
In current libnftnl, they both are defined as 1, so (with current libnftnl)
there is no difference.
Fixes: 702ac2b72c0e ("src: add comment support for chains")
Signed-off-by: Thomas Haller <thaller@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/netlink.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/netlink.c b/src/netlink.c
index 04bba59..a366758 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -617,7 +617,7 @@ static int qsort_device_cmp(const void *a, const void *b)
struct chain *netlink_delinearize_chain(struct netlink_ctx *ctx,
const struct nftnl_chain *nlc)
{
- const struct nftnl_udata *ud[NFTNL_UDATA_OBJ_MAX + 1] = {};
+ const struct nftnl_udata *ud[NFTNL_UDATA_CHAIN_MAX + 1] = {};
int priority, policy, len = 0, i;
const char * const *dev_array;
struct chain *chain;

View File

@ -0,0 +1,110 @@
From 800e92dac205e2f7684414528e3605abb93861f7 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:40 +0200
Subject: [PATCH] tests: shell: split merge nat optimization in two tests
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 0f89958eefd5318f594a653b78b47cefec9e87e9
commit 0f89958eefd5318f594a653b78b47cefec9e87e9
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue Nov 7 13:03:56 2023 +0100
tests: shell: split merge nat optimization in two tests
One without pipapo support and another with not to harm existing
coverage.
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
.../optimizations/dumps/merge_nat.nft | 8 --------
.../optimizations/dumps/merge_nat_concat.nft | 8 ++++++++
tests/shell/testcases/optimizations/merge_nat | 13 -------------
.../testcases/optimizations/merge_nat_concat | 18 ++++++++++++++++++
4 files changed, 26 insertions(+), 21 deletions(-)
create mode 100644 tests/shell/testcases/optimizations/dumps/merge_nat_concat.nft
create mode 100755 tests/shell/testcases/optimizations/merge_nat_concat
diff --git a/tests/shell/testcases/optimizations/dumps/merge_nat.nft b/tests/shell/testcases/optimizations/dumps/merge_nat.nft
index 48d18a6..61feb27 100644
--- a/tests/shell/testcases/optimizations/dumps/merge_nat.nft
+++ b/tests/shell/testcases/optimizations/dumps/merge_nat.nft
@@ -11,14 +11,6 @@ table ip test2 {
ip saddr { 10.141.11.0/24, 10.141.13.0/24 } masquerade
}
}
-table ip test3 {
- chain y {
- oif "lo" accept
- snat to ip saddr . tcp sport map { 1.1.1.1 . 1024-65535 : 3.3.3.3, 2.2.2.2 . 1024-65535 : 4.4.4.4 }
- oifname "enp2s0" snat ip to ip saddr map { 10.1.1.0/24 : 72.2.3.66-72.2.3.78 }
- tcp dport { 8888, 9999 } redirect
- }
-}
table ip test4 {
chain y {
oif "lo" accept
diff --git a/tests/shell/testcases/optimizations/dumps/merge_nat_concat.nft b/tests/shell/testcases/optimizations/dumps/merge_nat_concat.nft
new file mode 100644
index 0000000..0faddfd
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_nat_concat.nft
@@ -0,0 +1,8 @@
+table ip test3 {
+ chain y {
+ oif "lo" accept
+ snat to ip saddr . tcp sport map { 1.1.1.1 . 1024-65535 : 3.3.3.3, 2.2.2.2 . 1024-65535 : 4.4.4.4 }
+ oifname "enp2s0" snat ip to ip saddr map { 10.1.1.0/24 : 72.2.3.66-72.2.3.78 }
+ tcp dport { 8888, 9999 } redirect
+ }
+}
diff --git a/tests/shell/testcases/optimizations/merge_nat b/tests/shell/testcases/optimizations/merge_nat
index 3a57d94..bfe9787 100755
--- a/tests/shell/testcases/optimizations/merge_nat
+++ b/tests/shell/testcases/optimizations/merge_nat
@@ -24,19 +24,6 @@ RULESET="table ip test2 {
$NFT -o -f - <<< $RULESET
-RULESET="table ip test3 {
- chain y {
- oif lo accept
- ip saddr 1.1.1.1 tcp sport 1024-65535 snat to 3.3.3.3
- ip saddr 2.2.2.2 tcp sport 1024-65535 snat to 4.4.4.4
- oifname enp2s0 snat ip to ip saddr map { 10.1.1.0/24 : 72.2.3.66-72.2.3.78 }
- tcp dport 8888 redirect
- tcp dport 9999 redirect
- }
-}"
-
-$NFT -o -f - <<< $RULESET
-
RULESET="table ip test4 {
chain y {
oif lo accept
diff --git a/tests/shell/testcases/optimizations/merge_nat_concat b/tests/shell/testcases/optimizations/merge_nat_concat
new file mode 100755
index 0000000..2e0a91a
--- /dev/null
+++ b/tests/shell/testcases/optimizations/merge_nat_concat
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_pipapo)
+
+set -e
+
+RULESET="table ip test3 {
+ chain y {
+ oif lo accept
+ ip saddr 1.1.1.1 tcp sport 1024-65535 snat to 3.3.3.3
+ ip saddr 2.2.2.2 tcp sport 1024-65535 snat to 4.4.4.4
+ oifname enp2s0 snat ip to ip saddr map { 10.1.1.0/24 : 72.2.3.66-72.2.3.78 }
+ tcp dport 8888 redirect
+ tcp dport 9999 redirect
+ }
+}"
+
+$NFT -o -f - <<< $RULESET

View File

@ -0,0 +1,123 @@
From 20b2a43a27321b052fb9d626b13eb6de3668a9c8 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:40 +0200
Subject: [PATCH] tests: shell: split set NAT interval test
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit b95bbe966bd2431fee7b19aa0902aba10f9be696
commit b95bbe966bd2431fee7b19aa0902aba10f9be696
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue Nov 7 11:41:19 2023 +0100
tests: shell: split set NAT interval test
Split test in two, one for interval sets and another with concatenation
+ intervals, so at least intervals are tested in older kernels with no
pipapo support.
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
.../testcases/sets/0067nat_concat_interval_0 | 17 ++---------------
tests/shell/testcases/sets/0067nat_interval_0 | 18 ++++++++++++++++++
.../sets/dumps/0067nat_concat_interval_0.nft | 7 -------
.../sets/dumps/0067nat_interval_0.nft | 12 ++++++++++++
4 files changed, 32 insertions(+), 22 deletions(-)
create mode 100755 tests/shell/testcases/sets/0067nat_interval_0
create mode 100644 tests/shell/testcases/sets/dumps/0067nat_interval_0.nft
diff --git a/tests/shell/testcases/sets/0067nat_concat_interval_0 b/tests/shell/testcases/sets/0067nat_concat_interval_0
index 55cc0d4..8162195 100755
--- a/tests/shell/testcases/sets/0067nat_concat_interval_0
+++ b/tests/shell/testcases/sets/0067nat_concat_interval_0
@@ -1,21 +1,8 @@
#!/bin/bash
-set -e
-
-EXPECTED="table ip nat {
- map ipportmap {
- type ipv4_addr : interval ipv4_addr . inet_service
- flags interval
- elements = { 192.168.1.2 : 10.141.10.1-10.141.10.3 . 8888-8999 }
- }
- chain prerouting {
- type nat hook prerouting priority dstnat; policy accept;
- ip protocol tcp dnat ip to ip saddr map @ipportmap
- }
-}"
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_pipapo)
-$NFT -f - <<< $EXPECTED
-$NFT add element ip nat ipportmap { 192.168.2.0/24 : 10.141.11.5-10.141.11.20 . 8888-8999 }
+set -e
EXPECTED="table ip nat {
map ipportmap2 {
diff --git a/tests/shell/testcases/sets/0067nat_interval_0 b/tests/shell/testcases/sets/0067nat_interval_0
new file mode 100755
index 0000000..c90203d
--- /dev/null
+++ b/tests/shell/testcases/sets/0067nat_interval_0
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+set -e
+
+EXPECTED="table ip nat {
+ map ipportmap {
+ type ipv4_addr : interval ipv4_addr . inet_service
+ flags interval
+ elements = { 192.168.1.2 : 10.141.10.1-10.141.10.3 . 8888-8999 }
+ }
+ chain prerouting {
+ type nat hook prerouting priority dstnat; policy accept;
+ ip protocol tcp dnat ip to ip saddr map @ipportmap
+ }
+}"
+
+$NFT -f - <<< $EXPECTED
+$NFT add element ip nat ipportmap { 192.168.2.0/24 : 10.141.11.5-10.141.11.20 . 8888-8999 }
diff --git a/tests/shell/testcases/sets/dumps/0067nat_concat_interval_0.nft b/tests/shell/testcases/sets/dumps/0067nat_concat_interval_0.nft
index 0215691..9ac3774 100644
--- a/tests/shell/testcases/sets/dumps/0067nat_concat_interval_0.nft
+++ b/tests/shell/testcases/sets/dumps/0067nat_concat_interval_0.nft
@@ -1,10 +1,4 @@
table ip nat {
- map ipportmap {
- type ipv4_addr : interval ipv4_addr . inet_service
- flags interval
- elements = { 192.168.1.2 : 10.141.10.1-10.141.10.3 . 8888-8999, 192.168.2.0/24 : 10.141.11.5-10.141.11.20 . 8888-8999 }
- }
-
map ipportmap2 {
type ipv4_addr . ipv4_addr : interval ipv4_addr . inet_service
flags interval
@@ -33,7 +27,6 @@ table ip nat {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
- ip protocol tcp dnat ip to ip saddr map @ipportmap
ip protocol tcp dnat ip to ip saddr . ip daddr map @ipportmap2
meta l4proto { tcp, udp } dnat ip to ip daddr . th dport map @fwdtoip_th
dnat ip to iifname . ip saddr map @ipportmap4
diff --git a/tests/shell/testcases/sets/dumps/0067nat_interval_0.nft b/tests/shell/testcases/sets/dumps/0067nat_interval_0.nft
new file mode 100644
index 0000000..b6d07fc
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0067nat_interval_0.nft
@@ -0,0 +1,12 @@
+table ip nat {
+ map ipportmap {
+ type ipv4_addr : interval ipv4_addr . inet_service
+ flags interval
+ elements = { 192.168.1.2 : 10.141.10.1-10.141.10.3 . 8888-8999, 192.168.2.0/24 : 10.141.11.5-10.141.11.20 . 8888-8999 }
+ }
+
+ chain prerouting {
+ type nat hook prerouting priority dstnat; policy accept;
+ ip protocol tcp dnat ip to ip saddr map @ipportmap
+ }
+}

View File

@ -0,0 +1,132 @@
From 6d277e247b5022f59f733acbbe74f609937d5b9d Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:40 +0200
Subject: [PATCH] src: expand create commands
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 04a1ddc2012964c0a00350973328f5954887cedb
commit 04a1ddc2012964c0a00350973328f5954887cedb
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Mon Nov 13 14:39:23 2023 +0100
src: expand create commands
create commands also need to be expanded, otherwise elements are never
evaluated:
# cat ruleset.nft
define ip-block-4 = { 1.1.1.1 }
create set netdev filter ip-block-4-test {
type ipv4_addr
flags interval
auto-merge
elements = $ip-block-4
}
# nft -f ruleset.nft
BUG: unhandled expression type 0
nft: src/intervals.c:211: interval_expr_key: Assertion `0' failed.
Aborted
Same applies to chains in the form of:
create chain x y {
counter
}
which is also accepted by the parser.
Update tests/shell to improve coverage for these use cases.
Fixes: 56c90a2dd2eb ("evaluate: expand sets and maps before evaluation")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/libnftables.c | 3 ++-
tests/shell/testcases/include/0020include_chain_0 | 7 +++++++
.../testcases/include/dumps/0020include_chain_0.nft | 5 +++++
tests/shell/testcases/sets/0049set_define_0 | 12 ++++++++++++
.../shell/testcases/sets/dumps/0049set_define_0.nft | 7 +++++++
5 files changed, 33 insertions(+), 1 deletion(-)
diff --git a/src/libnftables.c b/src/libnftables.c
index ec90200..0dee1ba 100644
--- a/src/libnftables.c
+++ b/src/libnftables.c
@@ -532,7 +532,8 @@ static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs,
collapsed = true;
list_for_each_entry(cmd, cmds, list) {
- if (cmd->op != CMD_ADD)
+ if (cmd->op != CMD_ADD &&
+ cmd->op != CMD_CREATE)
continue;
nft_cmd_expand(cmd);
diff --git a/tests/shell/testcases/include/0020include_chain_0 b/tests/shell/testcases/include/0020include_chain_0
index 8f78e8c..49b6f76 100755
--- a/tests/shell/testcases/include/0020include_chain_0
+++ b/tests/shell/testcases/include/0020include_chain_0
@@ -20,4 +20,11 @@ RULESET2="chain inet filter input2 {
echo "$RULESET2" > $tmpfile1
+RULESET3="create chain inet filter output2 {
+ type filter hook output priority filter; policy accept;
+ ip daddr 1.2.3.4 tcp dport { 22, 443, 123 } drop
+}"
+
+echo "$RULESET3" >> $tmpfile1
+
$NFT -o -f - <<< $RULESET
diff --git a/tests/shell/testcases/include/dumps/0020include_chain_0.nft b/tests/shell/testcases/include/dumps/0020include_chain_0.nft
index 3ad6db1..bf596ff 100644
--- a/tests/shell/testcases/include/dumps/0020include_chain_0.nft
+++ b/tests/shell/testcases/include/dumps/0020include_chain_0.nft
@@ -3,4 +3,9 @@ table inet filter {
type filter hook input priority filter; policy accept;
ip saddr 1.2.3.4 tcp dport { 22, 123, 443 } drop
}
+
+ chain output2 {
+ type filter hook output priority filter; policy accept;
+ ip daddr 1.2.3.4 tcp dport { 22, 123, 443 } drop
+ }
}
diff --git a/tests/shell/testcases/sets/0049set_define_0 b/tests/shell/testcases/sets/0049set_define_0
index 1d512f7..756afdc 100755
--- a/tests/shell/testcases/sets/0049set_define_0
+++ b/tests/shell/testcases/sets/0049set_define_0
@@ -14,3 +14,15 @@ table inet filter {
"
$NFT -f - <<< "$EXPECTED"
+
+EXPECTED="define ip-block-4 = { 1.1.1.1 }
+
+ create set inet filter ip-block-4-test {
+ type ipv4_addr
+ flags interval
+ auto-merge
+ elements = \$ip-block-4
+ }
+"
+
+$NFT -f - <<< "$EXPECTED"
diff --git a/tests/shell/testcases/sets/dumps/0049set_define_0.nft b/tests/shell/testcases/sets/dumps/0049set_define_0.nft
index 998b387..d654420 100644
--- a/tests/shell/testcases/sets/dumps/0049set_define_0.nft
+++ b/tests/shell/testcases/sets/dumps/0049set_define_0.nft
@@ -1,4 +1,11 @@
table inet filter {
+ set ip-block-4-test {
+ type ipv4_addr
+ flags interval
+ auto-merge
+ elements = { 1.1.1.1 }
+ }
+
chain input {
type filter hook input priority filter; policy drop;
tcp dport { 22, 80, 443 } ct state new counter packets 0 bytes 0 accept

View File

@ -0,0 +1,57 @@
From d5019748ad5b02980c7ff7349d6e55245c831a7c Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:40 +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 <thaller@redhat.com>
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 <thaller@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/json.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/json.c b/src/json.c
index 98fa026..6809cd5 100644
--- a/src/json.c
+++ b/src/json.c
@@ -497,7 +497,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;

View File

@ -0,0 +1,56 @@
From dd1a258c4dd517dd4b89a8913fd4e1ae47dc0c34 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:40 +0200
Subject: [PATCH] tests: shell: restore pipapo and chain binding coverage in
standalone 30s-stress
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 110f73023497720d4161263c03d8d7cc062af7ab
commit 110f73023497720d4161263c03d8d7cc062af7ab
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue Nov 14 20:16:08 2023 +0100
tests: shell: restore pipapo and chain binding coverage in standalone 30s-stress
Do not disable pipapo and chain binding coverage for standalone runs by
default. Instead, turn them on by default and allow users to disable them
through:
# export NFT_TEST_HAVE_chain_binding=n; bash tests/shell/testcases/transactions/30s-stress 3600
...
running standalone with:
NFT_TEST_HAVE_chain_binding=n
NFT_TEST_HAVE_pipapo=y
given feature detection is not available in this case, thus, user has to
provide an explicit hint on what this kernel supports.
Fixes: c5b5b1044fdd ("tests/shell: add feature probing via "features/*.nft" files")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/shell/testcases/transactions/30s-stress | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/tests/shell/testcases/transactions/30s-stress b/tests/shell/testcases/transactions/30s-stress
index 4c3c6a2..544eef1 100755
--- a/tests/shell/testcases/transactions/30s-stress
+++ b/tests/shell/testcases/transactions/30s-stress
@@ -6,6 +6,15 @@ runtime=30
# allow stand-alone execution as well, e.g. '$0 3600'
if [ x"$1" != "x" ] ;then
+ if [ -z "${NFT_TEST_HAVE_chain_binding+x}" ]; then
+ NFT_TEST_HAVE_chain_binding=y
+ fi
+ if [ -z "${NFT_TEST_HAVE_pipapo+x}" ]; then
+ NFT_TEST_HAVE_pipapo=y
+ fi
+ echo "running standalone with:"
+ echo "NFT_TEST_HAVE_chain_binding="$NFT_TEST_HAVE_chain_binding
+ echo "NFT_TEST_HAVE_pipapo="$NFT_TEST_HAVE_pipapo
if [ $1 -ge 0 ]; then
runtime="$1"
else

View File

@ -0,0 +1,61 @@
From 0a8f8baff7bdfe2001f63429de7d8317bf72f4fc Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:40 +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 <pablo@netfilter.org>
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 <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
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() {

View File

@ -0,0 +1,39 @@
From ecd609b492cbc755529e75a6af8fd9c06b89135b Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:40 +0200
Subject: [PATCH] evaluate: fix rule replacement with anon sets
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 256904b1ded6314974dddc75726149f7b19d33f4
commit 256904b1ded6314974dddc75726149f7b19d33f4
Author: Florian Westphal <fw@strlen.de>
Date: Sun Nov 19 13:05:55 2023 +0100
evaluate: fix rule replacement with anon sets
nft replace rule t c handle 3 'jhash ip protocol . ip saddr mod 170 vmap { 0-94 : goto wan1, 95-169 : goto wan2, 170-269 }"'
BUG: unhandled op 2
nft: src/evaluate.c:1748: interval_set_eval: Assertion `0' failed.
Fixes: 81e36530fcac ("src: replace interval segment tree overlap and automerge")
Reported-by: Tino Reichardt <milky-netfilter@mcmilk.de>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/evaluate.c b/src/evaluate.c
index 27ecaa2..30190ac 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1723,6 +1723,7 @@ static int interval_set_eval(struct eval_ctx *ctx, struct set *set,
switch (ctx->cmd->op) {
case CMD_CREATE:
case CMD_ADD:
+ case CMD_REPLACE:
case CMD_INSERT:
if (set->automerge) {
ret = set_automerge(ctx->msgs, ctx->cmd, set, init,

View File

@ -0,0 +1,74 @@
From 29629b90e74c3228434c4fc27e97c39c0761ed7e Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:40 +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 <pablo@netfilter.org>
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 <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
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
}
}

View File

@ -0,0 +1,59 @@
From 93fe20fda0915183e19c513515d3f98e2d2c2df3 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:40 +0200
Subject: [PATCH] tests: shell: flush connlimit sets
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit bab3ede002da778e18fa5f30ac7b05c4de5e1de8
commit bab3ede002da778e18fa5f30ac7b05c4de5e1de8
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue Nov 21 16:33:17 2023 +0100
tests: shell: flush connlimit sets
Restored elements via set declaration are removed almost inmediately by
GC, this is causing spurious failures in test runs.
Flush sets to ensure dump is always consistent. Still, cover that
restoring a set with connlimit elements do not.
Fixes: 95d348d55a9e ("tests: shell: extend connlimit test")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/shell/testcases/sets/0062set_connlimit_0 | 3 +++
tests/shell/testcases/sets/dumps/0062set_connlimit_0.nft | 2 --
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/tests/shell/testcases/sets/0062set_connlimit_0 b/tests/shell/testcases/sets/0062set_connlimit_0
index 48d589f..dab1da0 100755
--- a/tests/shell/testcases/sets/0062set_connlimit_0
+++ b/tests/shell/testcases/sets/0062set_connlimit_0
@@ -24,3 +24,6 @@ RULESET="table ip x {
}"
$NFT -f - <<< $RULESET
+
+$NFT flush set ip x est-connlimit
+$NFT flush set ip x new-connlimit
diff --git a/tests/shell/testcases/sets/dumps/0062set_connlimit_0.nft b/tests/shell/testcases/sets/dumps/0062set_connlimit_0.nft
index 080d675..13bbb95 100644
--- a/tests/shell/testcases/sets/dumps/0062set_connlimit_0.nft
+++ b/tests/shell/testcases/sets/dumps/0062set_connlimit_0.nft
@@ -3,7 +3,6 @@ table ip x {
type ipv4_addr
size 65535
flags dynamic
- elements = { 84.245.120.167 ct count over 20 }
}
set new-connlimit {
@@ -11,6 +10,5 @@ table ip x {
size 65535
flags dynamic
ct count over 20
- elements = { 84.245.120.167 ct count over 20 }
}
}

View File

@ -0,0 +1,77 @@
From 65c523bdd547ea90488adebefcba672f0e676aca Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:40 +0200
Subject: [PATCH] evaluate: bogus error when adding devices to flowtable
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 59d304f47a121afda867d792c709bc2c81946979
commit 59d304f47a121afda867d792c709bc2c81946979
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Wed Nov 22 09:43:04 2023 +0100
evaluate: bogus error when adding devices to flowtable
Bail out if flowtable declaration is missing and no devices are
specified.
Otherwise, this reports a bogus error when adding new devices to an
existing flowtable.
# nft -v
nftables v1.0.9 (Old Doc Yak #3)
# ip link add dummy1 type dummy
# ip link set dummy1 up
# nft 'create flowtable inet filter f1 { hook ingress priority 0; counter }'
# nft 'add flowtable inet filter f1 { devices = { dummy1 } ; }'
Error: missing hook and priority in flowtable declaration
add flowtable inet filter f1 { devices = { dummy1 } ; }
^^^^^^^^^^^^^^^^^^^^^^^^
Fixes: 5ad475fce5a1 ("evaluate: bail out if new flowtable does not specify hook and priority")
Reported-by: Martin Gignac <martin.gignac@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 2 +-
tests/shell/testcases/flowtable/0015destroy_0 | 8 ++++++++
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/src/evaluate.c b/src/evaluate.c
index 30190ac..d204d3e 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -4862,7 +4862,7 @@ static int flowtable_evaluate(struct eval_ctx *ctx, struct flowtable *ft)
return table_not_found(ctx);
if (!ft_cache_find(table, ft->handle.flowtable.name)) {
- if (!ft->hook.name)
+ if (!ft->hook.name && !ft->dev_expr)
return chain_error(ctx, ft, "missing hook and priority in flowtable declaration");
ft_cache_add(flowtable_get(ft), table);
diff --git a/tests/shell/testcases/flowtable/0015destroy_0 b/tests/shell/testcases/flowtable/0015destroy_0
index d2a87da..cea3352 100755
--- a/tests/shell/testcases/flowtable/0015destroy_0
+++ b/tests/shell/testcases/flowtable/0015destroy_0
@@ -2,6 +2,11 @@
# NFT_TEST_REQUIRES(NFT_TEST_HAVE_destroy)
+trap "ip link del dummy1" EXIT
+
+ip link add dummy1 type dummy
+ip link set dummy1 up
+
$NFT add table t
# pass for non-existent flowtable
@@ -9,4 +14,7 @@ $NFT destroy flowtable t f
# successfully delete existing flowtable
$NFT add flowtable t f '{ hook ingress priority 10; devices = { lo }; }'
+
+$NFT 'add flowtable t f { devices = { dummy1 } ; }'
+
$NFT destroy flowtable t f

View File

@ -0,0 +1,115 @@
From 2c05e5e6b0c3aedcefbe73dbab6ac7b4f1b13138 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] tests: shell: split nat inet tests
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit b975de80bd1dbf0a04c6cb7a42e46c323c865de8
commit b975de80bd1dbf0a04c6cb7a42e46c323c865de8
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue Nov 21 20:45:48 2023 +0100
tests: shell: split nat inet tests
Detach nat inet from existing tests not to reduce test coverage.
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
.../optimizations/dumps/merge_nat.nft | 11 ----------
.../optimizations/dumps/merge_nat_inet.nft | 11 ++++++++++
tests/shell/testcases/optimizations/merge_nat | 16 --------------
.../testcases/optimizations/merge_nat_inet | 21 +++++++++++++++++++
4 files changed, 32 insertions(+), 27 deletions(-)
create mode 100644 tests/shell/testcases/optimizations/dumps/merge_nat_inet.nft
create mode 100755 tests/shell/testcases/optimizations/merge_nat_inet
diff --git a/tests/shell/testcases/optimizations/dumps/merge_nat.nft b/tests/shell/testcases/optimizations/dumps/merge_nat.nft
index 61feb27..f6c119e 100644
--- a/tests/shell/testcases/optimizations/dumps/merge_nat.nft
+++ b/tests/shell/testcases/optimizations/dumps/merge_nat.nft
@@ -19,14 +19,3 @@ table ip test4 {
tcp dport 85 redirect
}
}
-table inet nat {
- chain prerouting {
- oif "lo" accept
- dnat ip to iifname . ip daddr . tcp dport map { "enp2s0" . 72.2.3.70 . 80 : 10.1.1.52 . 80, "enp2s0" . 72.2.3.66 . 53122 : 10.1.1.10 . 22, "enp2s0" . 72.2.3.66 . 443 : 10.1.1.52 . 443 }
- }
-
- chain postrouting {
- oif "lo" accept
- snat ip to ip daddr map { 72.2.3.66 : 10.2.2.2, 72.2.3.67 : 10.2.3.3 }
- }
-}
diff --git a/tests/shell/testcases/optimizations/dumps/merge_nat_inet.nft b/tests/shell/testcases/optimizations/dumps/merge_nat_inet.nft
new file mode 100644
index 0000000..a1a1135
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_nat_inet.nft
@@ -0,0 +1,11 @@
+table inet nat {
+ chain prerouting {
+ oif "lo" accept
+ dnat ip to iifname . ip daddr . tcp dport map { "enp2s0" . 72.2.3.70 . 80 : 10.1.1.52 . 80, "enp2s0" . 72.2.3.66 . 53122 : 10.1.1.10 . 22, "enp2s0" . 72.2.3.66 . 443 : 10.1.1.52 . 443 }
+ }
+
+ chain postrouting {
+ oif "lo" accept
+ snat ip to ip daddr map { 72.2.3.66 : 10.2.2.2, 72.2.3.67 : 10.2.3.3 }
+ }
+}
diff --git a/tests/shell/testcases/optimizations/merge_nat b/tests/shell/testcases/optimizations/merge_nat
index bfe9787..3ffcbd5 100755
--- a/tests/shell/testcases/optimizations/merge_nat
+++ b/tests/shell/testcases/optimizations/merge_nat
@@ -36,19 +36,3 @@ RULESET="table ip test4 {
}"
$NFT -o -f - <<< $RULESET
-
-RULESET="table inet nat {
- chain prerouting {
- oif lo accept
- iifname enp2s0 ip daddr 72.2.3.66 tcp dport 53122 dnat to 10.1.1.10:22
- iifname enp2s0 ip daddr 72.2.3.66 tcp dport 443 dnat to 10.1.1.52:443
- iifname enp2s0 ip daddr 72.2.3.70 tcp dport 80 dnat to 10.1.1.52:80
- }
- chain postrouting {
- oif lo accept
- ip daddr 72.2.3.66 snat to 10.2.2.2
- ip daddr 72.2.3.67 snat to 10.2.3.3
- }
-}"
-
-$NFT -o -f - <<< $RULESET
diff --git a/tests/shell/testcases/optimizations/merge_nat_inet b/tests/shell/testcases/optimizations/merge_nat_inet
new file mode 100755
index 0000000..ff1916d
--- /dev/null
+++ b/tests/shell/testcases/optimizations/merge_nat_inet
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_inet_nat)
+
+set -e
+
+RULESET="table inet nat {
+ chain prerouting {
+ oif lo accept
+ iifname enp2s0 ip daddr 72.2.3.66 tcp dport 53122 dnat to 10.1.1.10:22
+ iifname enp2s0 ip daddr 72.2.3.66 tcp dport 443 dnat to 10.1.1.52:443
+ iifname enp2s0 ip daddr 72.2.3.70 tcp dport 80 dnat to 10.1.1.52:80
+ }
+ chain postrouting {
+ oif lo accept
+ ip daddr 72.2.3.66 snat to 10.2.2.2
+ ip daddr 72.2.3.67 snat to 10.2.3.3
+ }
+}"
+
+$NFT -o -f - <<< $RULESET

View File

@ -0,0 +1,83 @@
From bf752ee621e61026a40f050ac9d8a1eda351b81c Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] evaluate: clone unary expression datatype to deal with
dynamic datatype
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit faa6908fad6053ae9549c45b88d0402cc69cf1ed
commit faa6908fad6053ae9549c45b88d0402cc69cf1ed
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Wed Nov 22 20:35:07 2023 +0100
evaluate: clone unary expression datatype to deal with dynamic datatype
When allocating a unary expression, clone the datatype to deal with
dynamic datatypes.
Fixes: 6b01bb9ff798 ("datatype: concat expression only releases dynamically allocated datatype")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 2 +-
tests/shell/testcases/maps/dumps/vmap_unary.nft | 11 +++++++++++
tests/shell/testcases/maps/vmap_unary | 17 +++++++++++++++++
3 files changed, 29 insertions(+), 1 deletion(-)
create mode 100644 tests/shell/testcases/maps/dumps/vmap_unary.nft
create mode 100755 tests/shell/testcases/maps/vmap_unary
diff --git a/src/evaluate.c b/src/evaluate.c
index d204d3e..6f0fba6 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1245,7 +1245,7 @@ static int expr_evaluate_unary(struct eval_ctx *ctx, struct expr **expr)
BUG("invalid unary operation %u\n", unary->op);
}
- unary->dtype = arg->dtype;
+ unary->dtype = datatype_clone(arg->dtype);
unary->byteorder = byteorder;
unary->len = arg->len;
return 0;
diff --git a/tests/shell/testcases/maps/dumps/vmap_unary.nft b/tests/shell/testcases/maps/dumps/vmap_unary.nft
new file mode 100644
index 0000000..46c538b
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/vmap_unary.nft
@@ -0,0 +1,11 @@
+table ip filter {
+ map ipsec_in {
+ typeof ipsec in reqid . iif : verdict
+ flags interval
+ }
+
+ chain INPUT {
+ type filter hook input priority filter; policy drop;
+ ipsec in reqid . iif vmap @ipsec_in
+ }
+}
diff --git a/tests/shell/testcases/maps/vmap_unary b/tests/shell/testcases/maps/vmap_unary
new file mode 100755
index 0000000..4038d1c
--- /dev/null
+++ b/tests/shell/testcases/maps/vmap_unary
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table ip filter {
+ map ipsec_in {
+ typeof ipsec in reqid . iif : verdict
+ flags interval
+ }
+
+ chain INPUT {
+ type filter hook input priority 0; policy drop
+ ipsec in reqid . iif vmap @ipsec_in
+ }
+}"
+
+$NFT -f - <<< $RULESET

View File

@ -0,0 +1,65 @@
From e0b42568ea971334ca2177f5275bfab4e8312e8c Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] evaluate: reject sets with no key
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 09c573053ff03ad0a2eeb12c2957881648062f50
commit 09c573053ff03ad0a2eeb12c2957881648062f50
Author: Florian Westphal <fw@strlen.de>
Date: Thu Nov 30 21:29:52 2023 +0100
evaluate: reject sets with no key
nft --check -f tests/shell/testcases/bogons/nft-f/set_without_key
Segmentation fault (core dumped)
Fixes: 56c90a2dd2eb ("evaluate: expand sets and maps before evaluation")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 3 +++
tests/shell/testcases/bogons/nft-f/map_without_key | 5 +++++
tests/shell/testcases/bogons/nft-f/set_without_key | 5 +++++
3 files changed, 13 insertions(+)
create mode 100644 tests/shell/testcases/bogons/nft-f/map_without_key
create mode 100644 tests/shell/testcases/bogons/nft-f/set_without_key
diff --git a/src/evaluate.c b/src/evaluate.c
index 6f0fba6..2955ac5 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -4616,6 +4616,9 @@ static int elems_evaluate(struct eval_ctx *ctx, struct set *set)
{
ctx->set = set;
if (set->init != NULL) {
+ if (set->key == NULL)
+ return set_error(ctx, set, "set definition does not specify key");
+
__expr_set_context(&ctx->ectx, set->key->dtype,
set->key->byteorder, set->key->len, 0);
if (expr_evaluate(ctx, &set->init) < 0)
diff --git a/tests/shell/testcases/bogons/nft-f/map_without_key b/tests/shell/testcases/bogons/nft-f/map_without_key
new file mode 100644
index 0000000..78f16b2
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/map_without_key
@@ -0,0 +1,5 @@
+table t {
+ map m {
+ elements = { 0x00000023 : 0x00001337 }
+ }
+}
diff --git a/tests/shell/testcases/bogons/nft-f/set_without_key b/tests/shell/testcases/bogons/nft-f/set_without_key
new file mode 100644
index 0000000..f194afb
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/set_without_key
@@ -0,0 +1,5 @@
+table ip t {
+ set s {
+ elements = { 0x00000023-0x00000142, 0x00001337 }
+ }
+}

View File

@ -0,0 +1,59 @@
From 8523567b0fc4d2c0f978ee43234d984d5e75795c Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] evaluate: prevent assert when evaluating very large shift
values
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 26723202e600604ab7cf48915507cfcb7a313620
commit 26723202e600604ab7cf48915507cfcb7a313620
Author: Florian Westphal <fw@strlen.de>
Date: Fri Dec 1 14:16:14 2023 +0100
evaluate: prevent assert when evaluating very large shift values
Error out instead of 'nft: gmputil.c:67: mpz_get_uint32: Assertion `cnt <= 1' failed.'.
Fixes: edecd58755a8 ("evaluate: support shifts larger than the width of the left operand")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 9 +++++++--
tests/shell/testcases/bogons/nft-f/huge_shift_assert | 5 +++++
2 files changed, 12 insertions(+), 2 deletions(-)
create mode 100644 tests/shell/testcases/bogons/nft-f/huge_shift_assert
diff --git a/src/evaluate.c b/src/evaluate.c
index 2955ac5..1d13db1 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1312,9 +1312,14 @@ static int constant_binop_simplify(struct eval_ctx *ctx, struct expr **expr)
static int expr_evaluate_shift(struct eval_ctx *ctx, struct expr **expr)
{
struct expr *op = *expr, *left = op->left, *right = op->right;
- unsigned int shift = mpz_get_uint32(right->value);
- unsigned int max_shift_len;
+ unsigned int shift, max_shift_len;
+ /* mpz_get_uint32 has assert() for huge values */
+ if (mpz_cmp_ui(right->value, UINT_MAX) > 0)
+ return expr_binary_error(ctx->msgs, right, left,
+ "shifts exceeding %u bits are not supported", UINT_MAX);
+
+ shift = mpz_get_uint32(right->value);
if (ctx->stmt_len > left->len)
max_shift_len = ctx->stmt_len;
else
diff --git a/tests/shell/testcases/bogons/nft-f/huge_shift_assert b/tests/shell/testcases/bogons/nft-f/huge_shift_assert
new file mode 100644
index 0000000..7599f85
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/huge_shift_assert
@@ -0,0 +1,5 @@
+table ip t {
+ chain c {
+ counter name meta mark >> 88888888888888888888
+ }
+}

View File

@ -0,0 +1,70 @@
From 04adeaaab4d640f4bb50e783db835909df9959d4 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] evaluate: disable meta set with ranges
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit d99b44adc5cfc455fdafd9b4bdabd413edf9a38a
commit d99b44adc5cfc455fdafd9b4bdabd413edf9a38a
Author: Florian Westphal <fw@strlen.de>
Date: Mon Dec 4 19:04:58 2023 +0100
evaluate: disable meta set with ranges
... this will cause an assertion in netlink linearization, catch this
at eval stage instead.
before:
BUG: unknown expression type range
nft: netlink_linearize.c:908: netlink_gen_expr: Assertion `0' failed.
after:
/unknown_expr_type_range_assert:3:31-40: Error: Meta expression cannot be a range
meta mark set 0x001-3434
^^^^^^^^^^
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 13 +++++++++++++
.../bogons/nft-f/unknown_expr_type_range_assert | 5 +++++
2 files changed, 18 insertions(+)
create mode 100644 tests/shell/testcases/bogons/nft-f/unknown_expr_type_range_assert
diff --git a/src/evaluate.c b/src/evaluate.c
index 1d13db1..9de71d7 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -3148,6 +3148,19 @@ static int stmt_evaluate_meta(struct eval_ctx *ctx, struct stmt *stmt)
&stmt->meta.expr);
ctx->stmt_len = 0;
+ if (ret < 0)
+ return ret;
+
+ switch (stmt->meta.expr->etype) {
+ case EXPR_RANGE:
+ ret = expr_error(ctx->msgs, stmt->meta.expr,
+ "Meta expression cannot be a range");
+ break;
+ default:
+ break;
+
+ }
+
return ret;
}
diff --git a/tests/shell/testcases/bogons/nft-f/unknown_expr_type_range_assert b/tests/shell/testcases/bogons/nft-f/unknown_expr_type_range_assert
new file mode 100644
index 0000000..234dd62
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/unknown_expr_type_range_assert
@@ -0,0 +1,5 @@
+table ip x {
+ chain k {
+ meta mark set 0x001-3434
+ }
+}

View File

@ -0,0 +1,78 @@
From 4559a6cc20f887f7763153790f01449da18bf1cd Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] monitor: add support for concatenated set ranges
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 0d9392eef5f2c79ac7c19f59754a0aee574b5617
commit 0d9392eef5f2c79ac7c19f59754a0aee574b5617
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue Dec 5 17:20:05 2023 +0100
monitor: add support for concatenated set ranges
monitor is missing concatenated set ranges support.
Fixes: 8ac2f3b2fca3 ("src: Add support for concatenated set ranges")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/monitor.c | 11 +++++++++--
tests/monitor/testcases/set-concat-interval.t | 12 ++++++++++++
2 files changed, 21 insertions(+), 2 deletions(-)
create mode 100644 tests/monitor/testcases/set-concat-interval.t
diff --git a/src/monitor.c b/src/monitor.c
index 82762a0..2fc16d6 100644
--- a/src/monitor.c
+++ b/src/monitor.c
@@ -390,13 +390,19 @@ static bool netlink_event_range_cache(struct set *cached_set,
/* don't cache half-open range elements */
elem = list_entry(dummyset->init->expressions.prev, struct expr, list);
- if (!set_elem_is_open_interval(elem)) {
+ if (!set_elem_is_open_interval(elem) &&
+ dummyset->desc.field_count <= 1) {
cached_set->rg_cache = expr_clone(elem);
return true;
}
out_decompose:
- interval_map_decompose(dummyset->init);
+ if (dummyset->flags & NFT_SET_INTERVAL &&
+ dummyset->desc.field_count > 1)
+ concat_range_aggregate(dummyset->init);
+ else
+ interval_map_decompose(dummyset->init);
+
return false;
}
@@ -437,6 +443,7 @@ static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type,
dummyset->data = expr_clone(set->data);
dummyset->flags = set->flags;
dummyset->init = set_expr_alloc(monh->loc, set);
+ dummyset->desc.field_count = set->desc.field_count;
nlsei = nftnl_set_elems_iter_create(nls);
if (nlsei == NULL)
diff --git a/tests/monitor/testcases/set-concat-interval.t b/tests/monitor/testcases/set-concat-interval.t
new file mode 100644
index 0000000..763dc31
--- /dev/null
+++ b/tests/monitor/testcases/set-concat-interval.t
@@ -0,0 +1,12 @@
+# setup first
+I add table ip t
+I add chain ip t c
+O -
+J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}}
+J {"add": {"chain": {"family": "ip", "table": "t", "name": "c", "handle": 0}}}
+
+# add set with elements, monitor output expectedly differs
+I add map ip t s { typeof udp length . @ih,32,32 : verdict; flags interval; elements = { 20-80 . 0x14 : accept, 1-10 . 0xa : drop }; }
+O add map ip t s { typeof udp length . @ih,32,32 : verdict; flags interval; }
+O add element ip t s { 20-80 . 0x14 : accept }
+O add element ip t s { 1-10 . 0xa : drop }

View File

@ -0,0 +1,55 @@
From 1ccb4065209aef5be9e922c8702317636ca67745 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] evaluate: reject set definition with no key
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 1949a63215b423b914d3a7a9de7511cb48af3c09
commit 1949a63215b423b914d3a7a9de7511cb48af3c09
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Wed Dec 6 13:40:22 2023 +0100
evaluate: reject set definition with no key
tests/shell/testcases/bogons/nft-f/set_definition_with_no_key_assert
BUG: unhandled key type 2
nft: src/intervals.c:59: setelem_expr_to_range: Assertion `0' failed.
This patch adds a new unit tests/shell courtesy of Florian Westphal.
Fixes: 3975430b12d9 ("src: expand table command before evaluation")
Reported-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/evaluate.c b/src/evaluate.c
index 9de71d7..c2a7a41 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -4664,6 +4664,12 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
struct stmt *stmt;
const char *type;
+ type = set_is_map(set->flags) ? "map" : "set";
+
+ if (set->key == NULL)
+ return set_error(ctx, set, "%s definition does not specify key",
+ type);
+
if (!set_is_anonymous(set->flags)) {
table = table_cache_find(&ctx->nft->cache.table_cache,
set->handle.table.name,
@@ -4687,8 +4693,6 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
if (!(set->flags & NFT_SET_INTERVAL) && set->automerge)
return set_error(ctx, set, "auto-merge only works with interval sets");
- type = set_is_map(set->flags) ? "map" : "set";
-
if (set->key == NULL)
return set_error(ctx, set, "%s definition does not specify key",
type);

View File

@ -0,0 +1,210 @@
From 039d41bcd5dc8728690f5e9a6cf7c1b68bd05905 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] parser: tcpopt: fix tcp option parsing with NUM + length
field
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 59a33d08ab3a75b2ae370b6816942793f49fa8db
commit 59a33d08ab3a75b2ae370b6816942793f49fa8db
Author: Florian Westphal <fw@strlen.de>
Date: Tue Dec 5 12:56:08 2023 +0100
parser: tcpopt: fix tcp option parsing with NUM + length field
tcp option 254 length ge 4
... will segfault.
The crash bug is that tcpopt_expr_alloc() can return NULL if we cannot
find a suitable template for the requested kind + field combination,
so add the needed error handling in the bison parser.
However, we can handle this. NOP and EOL have templates, all other
options (known or unknown) must also have a length field.
So also add a fallback template to handle both kind and length, even
if only a numeric option is given that nft doesn't recognize.
Don't bother with output, above will be printed via raw syntax, i.e.
tcp option @254,8,8 >= 4.
Fixes: 24d8da308342 ("tcpopt: allow to check for presence of any tcp option")
Reported-by: Maciej Żenczykowski <zenczykowski@gmail.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_bison.y | 4 ++
src/tcpopt.c | 44 +++++++++++----
.../packetpath/dumps/tcp_options.nodump | 0
tests/shell/testcases/packetpath/tcp_options | 55 +++++++++++++++++++
4 files changed, 93 insertions(+), 10 deletions(-)
create mode 100644 tests/shell/testcases/packetpath/dumps/tcp_options.nodump
create mode 100755 tests/shell/testcases/packetpath/tcp_options
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 751b3e1..9386a9c 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -5837,6 +5837,10 @@ tcp_hdr_expr : TCP tcp_hdr_field
| TCP OPTION tcp_hdr_option_kind_and_field
{
$$ = tcpopt_expr_alloc(&@$, $3.kind, $3.field);
+ if ($$ == NULL) {
+ erec_queue(error(&@1, "Could not find a tcp option template"), state->msgs);
+ YYERROR;
+ }
}
| TCP OPTION AT close_scope_at tcp_hdr_option_type COMMA NUM COMMA NUM
{
diff --git a/src/tcpopt.c b/src/tcpopt.c
index 3fcb273..8111a50 100644
--- a/src/tcpopt.c
+++ b/src/tcpopt.c
@@ -118,6 +118,13 @@ static const struct exthdr_desc tcpopt_mptcp = {
[TCPOPT_MPTCP_SUBTYPE] = PHT("subtype", 16, 4),
},
};
+
+static const struct exthdr_desc tcpopt_fallback = {
+ .templates = {
+ [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_COMMON_LENGTH] = PHT("length", 8, 8),
+ },
+};
#undef PHT
const struct exthdr_desc *tcpopt_protocols[] = {
@@ -133,6 +140,17 @@ const struct exthdr_desc *tcpopt_protocols[] = {
[TCPOPT_KIND_FASTOPEN] = &tcpopt_fastopen,
};
+static void tcpopt_assign_tmpl(struct expr *expr,
+ const struct proto_hdr_template *tmpl,
+ const struct exthdr_desc *desc)
+{
+ expr->exthdr.op = NFT_EXTHDR_OP_TCPOPT;
+
+ expr->exthdr.desc = desc;
+ expr->exthdr.tmpl = tmpl;
+ expr->exthdr.offset = tmpl->offset;
+}
+
/**
* tcpopt_expr_alloc - allocate tcp option extension expression
*
@@ -182,18 +200,26 @@ struct expr *tcpopt_expr_alloc(const struct location *loc,
desc = tcpopt_protocols[kind];
if (!desc) {
- if (field != TCPOPT_COMMON_KIND || kind > 255)
+ if (kind > 255)
return NULL;
+ desc = &tcpopt_fallback;
+
+ switch (field) {
+ case TCPOPT_COMMON_KIND:
+ case TCPOPT_COMMON_LENGTH:
+ tmpl = &desc->templates[field];
+ break;
+ default:
+ tmpl = &tcpopt_unknown_template;
+ break;
+ }
+
expr = expr_alloc(loc, EXPR_EXTHDR, &integer_type,
BYTEORDER_BIG_ENDIAN, 8);
- desc = tcpopt_protocols[TCPOPT_NOP];
- tmpl = &desc->templates[field];
- expr->exthdr.desc = desc;
- expr->exthdr.tmpl = tmpl;
- expr->exthdr.op = NFT_EXTHDR_OP_TCPOPT;
expr->exthdr.raw_type = kind;
+ tcpopt_assign_tmpl(expr, tmpl, desc);
return expr;
}
@@ -203,11 +229,9 @@ struct expr *tcpopt_expr_alloc(const struct location *loc,
expr = expr_alloc(loc, EXPR_EXTHDR, tmpl->dtype,
BYTEORDER_BIG_ENDIAN, tmpl->len);
- expr->exthdr.desc = desc;
- expr->exthdr.tmpl = tmpl;
- expr->exthdr.op = NFT_EXTHDR_OP_TCPOPT;
+
expr->exthdr.raw_type = desc->type;
- expr->exthdr.offset = tmpl->offset;
+ tcpopt_assign_tmpl(expr, tmpl, desc);
return expr;
}
diff --git a/tests/shell/testcases/packetpath/dumps/tcp_options.nodump b/tests/shell/testcases/packetpath/dumps/tcp_options.nodump
new file mode 100644
index 0000000..e69de29
diff --git a/tests/shell/testcases/packetpath/tcp_options b/tests/shell/testcases/packetpath/tcp_options
new file mode 100755
index 0000000..1c9ee53
--- /dev/null
+++ b/tests/shell/testcases/packetpath/tcp_options
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+have_socat="no"
+socat -h > /dev/null && have_socat="yes"
+
+ip link set lo up
+
+$NFT -f /dev/stdin <<EOF
+table inet t {
+ counter nomatchc {}
+ counter sackpermc {}
+ counter maxsegc {}
+ counter nopc {}
+
+ chain c {
+ type filter hook output priority 0;
+ tcp dport != 22345 accept
+ tcp flags syn / fin,syn,rst,ack tcp option 254 length ge 4 counter name nomatchc drop
+ tcp flags syn / fin,syn,rst,ack tcp option fastopen length ge 2 reset tcp option fastopen counter name nomatchc
+ tcp flags syn / fin,syn,rst,ack tcp option sack-perm missing counter name nomatchc
+ tcp flags syn / fin,syn,rst,ack tcp option sack-perm exists counter name sackpermc
+ tcp flags syn / fin,syn,rst,ack tcp option maxseg size gt 1400 counter name maxsegc
+ tcp flags syn / fin,syn,rst,ack tcp option nop missing counter name nomatchc
+ tcp flags syn / fin,syn,rst,ack tcp option nop exists counter name nopc
+ tcp flags syn / fin,syn,rst,ack drop
+ }
+}
+EOF
+
+if [ $? -ne 0 ]; then
+ exit 1
+fi
+
+if [ $have_socat != "yes" ]; then
+ echo "Ran partial test, socat not available (skipped)"
+ exit 77
+fi
+
+# This will fail (drop in output -> connect fails with eperm)
+socat -u STDIN TCP:127.0.0.1:22345,connect-timeout=1 < /dev/null > /dev/null
+
+# can't validate via dump file, syn rexmit can cause counters to be > 1 in rare cases.
+
+$NFT list counter inet t nomatchc
+
+# nomatchc must be 0.
+$NFT list counter inet t nomatchc | grep -q "packets 0" || exit 1
+
+# these counters must not be 0.
+for nz in sackpermc maxsegc nopc; do
+ $NFT list counter inet t $nz
+ $NFT list counter inet t $nz | grep -q "packets 0" && exit 1
+done
+
+exit 0

View File

@ -0,0 +1,344 @@
From dca8ddacdacbad26f92b7cd665dd150a341ced26 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] evaluate: reset statement length context before evaluating
statement
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 8d3de823b622136e1d05a6fed11ff2dc0e804f8a
commit 8d3de823b622136e1d05a6fed11ff2dc0e804f8a
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Wed Dec 6 18:48:29 2023 +0100
evaluate: reset statement length context before evaluating statement
This patch consolidates ctx->stmt_len reset in stmt_evaluate() to avoid
this problem. Note that stmt_evaluate_meta() and stmt_evaluate_ct()
already reset it after the statement evaluation.
Moreover, statement dependency can be generated while evaluating a meta
and ct statement. Payload statement dependency already manually stashes
this before calling stmt_evaluate(). Add a new stmt_dependency_evaluate()
function to stash statement length context when evaluating a new statement
dependency and use it for all of the existing statement dependencies.
Florian also says:
'meta mark set vlan id map { 1 : 0x00000001, 4095 : 0x00004095 }' will
crash. Reason is that the l2 dependency generated here is errounously
expanded to a 32bit-one, so the evaluation path won't recognize this
as a L2 dependency. Therefore, pctx->stacked_ll_count is 0 and
__expr_evaluate_payload() crashes with a null deref when
dereferencing pctx->stacked_ll[0].
nft-test.py gains a fugly hack to tolerate '!map typeof vlan id : meta mark'.
For more generic support we should find something more acceptable, e.g.
!map typeof( everything here is a key or data ) timeout ...
tests/py update and assert(pctx->stacked_ll_count) by Florian Westphal.
Fixes: edecd58755a8 ("evaluate: support shifts larger than the width of the left operand")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
include/statement.h | 1 +
src/evaluate.c | 26 ++++++++++++++++++--------
src/payload.c | 29 +++++++----------------------
tests/py/any/meta.t | 4 ++++
tests/py/any/meta.t.payload | 25 +++++++++++++++++++++++++
tests/py/any/meta.t.payload.bridge | 20 ++++++++++++++++++++
tests/py/nft-test.py | 17 +++++++++++++----
7 files changed, 88 insertions(+), 34 deletions(-)
create mode 100644 tests/py/any/meta.t.payload.bridge
diff --git a/include/statement.h b/include/statement.h
index 720a6ac..662f99d 100644
--- a/include/statement.h
+++ b/include/statement.h
@@ -416,6 +416,7 @@ struct stmt {
extern struct stmt *stmt_alloc(const struct location *loc,
const struct stmt_ops *ops);
int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt);
+int stmt_dependency_evaluate(struct eval_ctx *ctx, struct stmt *stmt);
extern void stmt_free(struct stmt *stmt);
extern void stmt_list_free(struct list_head *list);
extern void stmt_print(const struct stmt *stmt, struct output_ctx *octx);
diff --git a/src/evaluate.c b/src/evaluate.c
index c2a7a41..cfdc6c0 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -454,6 +454,18 @@ static int expr_evaluate_primary(struct eval_ctx *ctx, struct expr **expr)
return 0;
}
+int stmt_dependency_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ uint32_t stmt_len = ctx->stmt_len;
+
+ if (stmt_evaluate(ctx, stmt) < 0)
+ return stmt_error(ctx, stmt, "dependency statement is invalid");
+
+ ctx->stmt_len = stmt_len;
+
+ return 0;
+}
+
static int
conflict_resolution_gen_dependency(struct eval_ctx *ctx, int protocol,
const struct expr *expr,
@@ -479,7 +491,7 @@ conflict_resolution_gen_dependency(struct eval_ctx *ctx, int protocol,
dep = relational_expr_alloc(&expr->location, OP_EQ, left, right);
stmt = expr_stmt_alloc(&dep->location, dep);
- if (stmt_evaluate(ctx, stmt) < 0)
+ if (stmt_dependency_evaluate(ctx, stmt) < 0)
return expr_error(ctx->msgs, expr,
"dependency statement is invalid");
@@ -696,9 +708,8 @@ static int meta_iiftype_gen_dependency(struct eval_ctx *ctx,
"for this family");
nstmt = meta_stmt_meta_iiftype(&payload->location, type);
- if (stmt_evaluate(ctx, nstmt) < 0)
- return expr_error(ctx->msgs, payload,
- "dependency statement is invalid");
+ if (stmt_dependency_evaluate(ctx, nstmt) < 0)
+ return -1;
if (ctx->inner_desc)
nstmt->expr->left->meta.inner_desc = ctx->inner_desc;
@@ -809,6 +820,7 @@ static int __expr_evaluate_payload(struct eval_ctx *ctx, struct expr *expr)
desc->name,
payload->payload.desc->name);
+ assert(pctx->stacked_ll_count);
payload->payload.offset += pctx->stacked_ll[0]->length;
rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
return 1;
@@ -3146,8 +3158,6 @@ static int stmt_evaluate_meta(struct eval_ctx *ctx, struct stmt *stmt)
stmt->meta.tmpl->len,
stmt->meta.tmpl->byteorder,
&stmt->meta.expr);
- ctx->stmt_len = 0;
-
if (ret < 0)
return ret;
@@ -3175,8 +3185,6 @@ static int stmt_evaluate_ct(struct eval_ctx *ctx, struct stmt *stmt)
stmt->ct.tmpl->len,
stmt->ct.tmpl->byteorder,
&stmt->ct.expr);
- ctx->stmt_len = 0;
-
if (ret < 0)
return -1;
@@ -4468,6 +4476,8 @@ int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
erec_destroy(erec);
}
+ ctx->stmt_len = 0;
+
switch (stmt->ops->type) {
case STMT_CONNLIMIT:
case STMT_COUNTER:
diff --git a/src/payload.c b/src/payload.c
index 140ca50..5de3d32 100644
--- a/src/payload.c
+++ b/src/payload.c
@@ -407,7 +407,6 @@ static int payload_add_dependency(struct eval_ctx *ctx,
const struct proto_hdr_template *tmpl;
struct expr *dep, *left, *right;
struct proto_ctx *pctx;
- unsigned int stmt_len;
struct stmt *stmt;
int protocol;
@@ -429,15 +428,9 @@ static int payload_add_dependency(struct eval_ctx *ctx,
dep = relational_expr_alloc(&expr->location, OP_EQ, left, right);
- stmt_len = ctx->stmt_len;
- ctx->stmt_len = 0;
-
stmt = expr_stmt_alloc(&dep->location, dep);
- if (stmt_evaluate(ctx, stmt) < 0) {
- return expr_error(ctx->msgs, expr,
- "dependency statement is invalid");
- }
- ctx->stmt_len = stmt_len;
+ if (stmt_dependency_evaluate(ctx, stmt) < 0)
+ return -1;
if (ctx->inner_desc) {
if (tmpl->meta_key)
@@ -547,7 +540,6 @@ int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
const struct hook_proto_desc *h;
const struct proto_desc *desc;
struct proto_ctx *pctx;
- unsigned int stmt_len;
struct stmt *stmt;
uint16_t type;
@@ -564,17 +556,11 @@ int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
"protocol specification is invalid "
"for this family");
- stmt_len = ctx->stmt_len;
- ctx->stmt_len = 0;
-
stmt = meta_stmt_meta_iiftype(&expr->location, type);
- if (stmt_evaluate(ctx, stmt) < 0) {
- return expr_error(ctx->msgs, expr,
- "dependency statement is invalid");
- }
- *res = stmt;
+ if (stmt_dependency_evaluate(ctx, stmt) < 0)
+ return -1;
- ctx->stmt_len = stmt_len;
+ *res = stmt;
return 0;
}
@@ -1442,9 +1428,8 @@ int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr,
pctx->th_dep.icmp.type = type;
- if (stmt_evaluate(ctx, stmt) < 0)
- return expr_error(ctx->msgs, expr,
- "icmp dependency statement is invalid");
+ if (stmt_dependency_evaluate(ctx, stmt) < 0)
+ return -1;
done:
*res = stmt;
return 0;
diff --git a/tests/py/any/meta.t b/tests/py/any/meta.t
index 12fabb7..718c7ad 100644
--- a/tests/py/any/meta.t
+++ b/tests/py/any/meta.t
@@ -224,3 +224,7 @@ time > "2022-07-01 11:00:00" accept;ok;meta time > "2022-07-01 11:00:00" accept
meta time "meh";fail
meta hour "24:00" drop;fail
meta day 7 drop;fail
+
+meta mark set vlan id map { 1 : 0x00000001, 4095 : 0x00004095 };ok
+!map1 typeof vlan id : meta mark;ok
+meta mark set vlan id map @map1;ok
diff --git a/tests/py/any/meta.t.payload b/tests/py/any/meta.t.payload
index 16dc121..49dd729 100644
--- a/tests/py/any/meta.t.payload
+++ b/tests/py/any/meta.t.payload
@@ -1072,3 +1072,28 @@ ip test-ip4 input
[ byteorder reg 1 = hton(reg 1, 8, 8) ]
[ cmp gt reg 1 0xf3a8fd16 0x00a07719 ]
[ immediate reg 0 accept ]
+
+# meta mark set vlan id map { 1 : 0x00000001, 4095 : 0x00004095 }
+__map%d test-ip4 b size 2
+__map%d test-ip4 0
+ element 00000100 : 00000001 0 [end] element 0000ff0f : 00004095 0 [end]
+ip test-ip4 input
+ [ meta load iiftype => reg 1 ]
+ [ cmp eq reg 1 0x00000001 ]
+ [ payload load 2b @ link header + 12 => reg 1 ]
+ [ cmp eq reg 1 0x00000081 ]
+ [ payload load 2b @ link header + 14 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000ff0f ) ^ 0x00000000 ]
+ [ lookup reg 1 set __map%d dreg 1 ]
+ [ meta set mark with reg 1 ]
+
+# meta mark set vlan id map @map1
+ip test-ip4 input
+ [ meta load iiftype => reg 1 ]
+ [ cmp eq reg 1 0x00000001 ]
+ [ payload load 2b @ link header + 12 => reg 1 ]
+ [ cmp eq reg 1 0x00000081 ]
+ [ payload load 2b @ link header + 14 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000ff0f ) ^ 0x00000000 ]
+ [ lookup reg 1 set map1 dreg 1 ]
+ [ meta set mark with reg 1 ]
diff --git a/tests/py/any/meta.t.payload.bridge b/tests/py/any/meta.t.payload.bridge
new file mode 100644
index 0000000..5997ccc
--- /dev/null
+++ b/tests/py/any/meta.t.payload.bridge
@@ -0,0 +1,20 @@
+# meta mark set vlan id map { 1 : 0x00000001, 4095 : 0x00004095 }
+__map%d test-bridge b size 2
+__map%d test-bridge 0
+ element 00000100 : 00000001 0 [end] element 0000ff0f : 00004095 0 [end]
+bridge test-bridge input
+ [ payload load 2b @ link header + 12 => reg 1 ]
+ [ cmp eq reg 1 0x00000081 ]
+ [ payload load 2b @ link header + 14 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000ff0f ) ^ 0x00000000 ]
+ [ lookup reg 1 set __map%d dreg 1 ]
+ [ meta set mark with reg 1 ]
+
+# meta mark set vlan id map @map1
+bridge test-bridge input
+ [ payload load 2b @ link header + 12 => reg 1 ]
+ [ cmp eq reg 1 0x00000081 ]
+ [ payload load 2b @ link header + 14 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000ff0f ) ^ 0x00000000 ]
+ [ lookup reg 1 set map1 dreg 1 ]
+ [ meta set mark with reg 1 ]
diff --git a/tests/py/nft-test.py b/tests/py/nft-test.py
index 9a25503..a7d27c2 100755
--- a/tests/py/nft-test.py
+++ b/tests/py/nft-test.py
@@ -368,9 +368,9 @@ def set_add(s, test_result, filename, lineno):
flags = "flags %s; " % flags
if s.data == "":
- cmd = "add set %s %s { type %s;%s %s}" % (table, s.name, s.type, s.timeout, flags)
+ cmd = "add set %s %s { %s;%s %s}" % (table, s.name, s.type, s.timeout, flags)
else:
- cmd = "add map %s %s { type %s : %s;%s %s}" % (table, s.name, s.type, s.data, s.timeout, flags)
+ cmd = "add map %s %s { %s : %s;%s %s}" % (table, s.name, s.type, s.data, s.timeout, flags)
ret = execute_cmd(cmd, filename, lineno)
@@ -410,7 +410,7 @@ def map_add(s, test_result, filename, lineno):
if flags != "":
flags = "flags %s; " % flags
- cmd = "add map %s %s { type %s : %s;%s %s}" % (table, s.name, s.type, s.data, s.timeout, flags)
+ cmd = "add map %s %s { %s : %s;%s %s}" % (table, s.name, s.type, s.data, s.timeout, flags)
ret = execute_cmd(cmd, filename, lineno)
@@ -1144,11 +1144,16 @@ def set_process(set_line, filename, lineno):
tokens = set_line[0].split(" ")
set_name = tokens[0]
- set_type = tokens[2]
+ parse_typeof = tokens[1] == "typeof"
+ set_type = tokens[1] + " " + tokens[2]
set_data = ""
set_flags = ""
i = 3
+ if parse_typeof and tokens[i] == "id":
+ set_type += " " + tokens[i]
+ i += 1;
+
while len(tokens) > i and tokens[i] == ".":
set_type += " . " + tokens[i+1]
i += 2
@@ -1157,6 +1162,10 @@ def set_process(set_line, filename, lineno):
set_data = tokens[i+1]
i += 2
+ if parse_typeof and tokens[i] == "mark":
+ set_data += " " + tokens[i]
+ i += 1;
+
if len(tokens) == i+2 and tokens[i] == "timeout":
timeout = "timeout " + tokens[i+1] + ";"
i += 2

View File

@ -0,0 +1,95 @@
From 6b25abca848fe00a46536c61bd6770d219f965c8 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] tests: py: missing json output in meta.t with vlan mapping
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 5fec559727ffd2c6c8958748beab782096385758
commit 5fec559727ffd2c6c8958748beab782096385758
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Mon Dec 11 12:54:35 2023 +0100
tests: py: missing json output in meta.t with vlan mapping
Fix this warning due to missing coverage:
tests/py/any/meta.t.json.got: WARNING: line 2: Wrote JSON equivalent for rule meta mark set vlan id map { 1 : 0x00000001, 4095 : 0x00004095 }
ERROR: did not find JSON equivalent for rule 'meta mark set vlan id map @map1
Fixes: 8d3de823b622 ("evaluate: reset statement length context before evaluating statement")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/any/meta.t.json | 60 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 60 insertions(+)
diff --git a/tests/py/any/meta.t.json b/tests/py/any/meta.t.json
index 4734bbf..d50272d 100644
--- a/tests/py/any/meta.t.json
+++ b/tests/py/any/meta.t.json
@@ -2758,3 +2758,63 @@
"accept": null
}
]
+
+# meta mark set vlan id map { 1 : 0x00000001, 4095 : 0x00004095 }
+[
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "map": {
+ "data": {
+ "set": [
+ [
+ 1,
+ 1
+ ],
+ [
+ 4095,
+ 16533
+ ]
+ ]
+ },
+ "key": {
+ "payload": {
+ "field": "id",
+ "protocol": "vlan"
+ }
+ }
+ }
+ }
+ }
+ }
+]
+
+# meta mark set vlan id map @map1
+[
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "map": {
+ "data": "@map1",
+ "key": {
+ "payload": {
+ "field": "id",
+ "protocol": "vlan"
+ }
+ }
+ }
+ }
+ }
+ }
+]
+

View File

@ -0,0 +1,271 @@
From fdf39fe2cf3f15cbea9c26b1c5951f207cda6ea8 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] parser_bison: fix objref statement corruption
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 78dffb470fcf7b1c0b1b3d6f43fcc056c337a808
commit 78dffb470fcf7b1c0b1b3d6f43fcc056c337a808
Author: Florian Westphal <fw@strlen.de>
Date: Fri Dec 8 19:41:39 2023 +0100
parser_bison: fix objref statement corruption
Consider this:
counter_stmt : counter_stmt_alloc
| counter_stmt_alloc counter_args
counter_stmt_alloc : COUNTER { $$ = counter_stmt_alloc(&@$); }
| COUNTER NAME stmt_expr
{
$$ = objref_stmt_alloc(&@$);
$$->objref.type = NFT_OBJECT_COUNTER;
$$->objref.expr = $3;
}
;
counter_args : counter_arg { $<stmt>$ = $<stmt>0; }
| counter_args counter_arg
;
counter_arg : PACKETS NUM { $<stmt>0->counter.packets = $2; }
[..]
This has 'counter_stmt_alloc' EITHER return counter or objref statement.
Both are the same structure but with different (union'd) trailer content.
counter_stmt permits the 'packet' and 'byte' argument.
But the 'counter_arg' directive only works with a statement
coming from counter_stmt_alloc().
afl++ came up with following input:
table inet x {
chain y {
counter name ip saddr bytes 1.1.1. 1024
}
}
This clobbers $<stmt>->objref.expr pointer, we then crash when
calling expr_evaluate() on it.
Split the objref related statements into their own directive.
After this, the input will fail with:
"syntax error, unexpected bytes, expecting newline or semicolon".
Also split most of the other objref statements into their own blocks.
synproxy seems to have same problem, limit and quota appeared to be ok.
v1 added objref_stmt to stateful_stmt list, this is wrong, we will
assert when generating the 'counter' statement.
Place it in the normal statement list so netlink_gen_stmt_stateful_assert
throws the expected parser error.
Fixes: dccab4f646b4 ("parser_bison: consolidate stmt_expr rule")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_bison.y | 97 ++++++++++++-------
.../bogons/nft-f/counter_objref_crash | 5 +
.../nft-f/netlink_gen_stmt_stateful_assert | 6 ++
3 files changed, 71 insertions(+), 37 deletions(-)
create mode 100644 tests/shell/testcases/bogons/nft-f/counter_objref_crash
create mode 100644 tests/shell/testcases/bogons/nft-f/netlink_gen_stmt_stateful_assert
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 9386a9c..2c829f9 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -746,6 +746,9 @@ int nft_lex(void *, void *, void *);
%destructor { stmt_free($$); } stmt match_stmt verdict_stmt set_elem_stmt
%type <stmt> counter_stmt counter_stmt_alloc stateful_stmt last_stmt
%destructor { stmt_free($$); } counter_stmt counter_stmt_alloc stateful_stmt last_stmt
+%type <stmt> objref_stmt objref_stmt_counter objref_stmt_limit objref_stmt_quota objref_stmt_ct objref_stmt_synproxy
+%destructor { stmt_free($$); } objref_stmt objref_stmt_counter objref_stmt_limit objref_stmt_quota objref_stmt_ct objref_stmt_synproxy
+
%type <stmt> payload_stmt
%destructor { stmt_free($$); } payload_stmt
%type <stmt> ct_stmt
@@ -3060,6 +3063,60 @@ stateful_stmt_list : stateful_stmt
}
;
+objref_stmt_counter : COUNTER NAME stmt_expr close_scope_counter
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_COUNTER;
+ $$->objref.expr = $3;
+ }
+ ;
+
+objref_stmt_limit : LIMIT NAME stmt_expr close_scope_limit
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_LIMIT;
+ $$->objref.expr = $3;
+ }
+ ;
+
+objref_stmt_quota : QUOTA NAME stmt_expr close_scope_quota
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_QUOTA;
+ $$->objref.expr = $3;
+ }
+ ;
+
+objref_stmt_synproxy : SYNPROXY NAME stmt_expr close_scope_synproxy
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_SYNPROXY;
+ $$->objref.expr = $3;
+ }
+ ;
+
+objref_stmt_ct : CT TIMEOUT SET stmt_expr close_scope_ct
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_CT_TIMEOUT;
+ $$->objref.expr = $4;
+
+ }
+ | CT EXPECTATION SET stmt_expr close_scope_ct
+ {
+ $$ = objref_stmt_alloc(&@$);
+ $$->objref.type = NFT_OBJECT_CT_EXPECT;
+ $$->objref.expr = $4;
+ }
+ ;
+
+objref_stmt : objref_stmt_counter
+ | objref_stmt_limit
+ | objref_stmt_quota
+ | objref_stmt_synproxy
+ | objref_stmt_ct
+ ;
+
stateful_stmt : counter_stmt close_scope_counter
| limit_stmt
| quota_stmt
@@ -3089,6 +3146,7 @@ stmt : verdict_stmt
| chain_stmt
| optstrip_stmt
| xt_stmt close_scope_xt
+ | objref_stmt
;
xt_stmt : XT STRING string
@@ -3178,12 +3236,6 @@ counter_stmt_alloc : COUNTER
{
$$ = counter_stmt_alloc(&@$);
}
- | COUNTER NAME stmt_expr
- {
- $$ = objref_stmt_alloc(&@$);
- $$->objref.type = NFT_OBJECT_COUNTER;
- $$->objref.expr = $3;
- }
;
counter_args : counter_arg
@@ -3195,10 +3247,12 @@ counter_args : counter_arg
counter_arg : PACKETS NUM
{
+ assert($<stmt>0->ops->type == STMT_COUNTER);
$<stmt>0->counter.packets = $2;
}
| BYTES NUM
{
+ assert($<stmt>0->ops->type == STMT_COUNTER);
$<stmt>0->counter.bytes = $2;
}
;
@@ -3479,12 +3533,6 @@ limit_stmt : LIMIT RATE limit_mode limit_rate_pkts limit_burst_pkts close_scope
$$->limit.type = NFT_LIMIT_PKT_BYTES;
$$->limit.flags = $3;
}
- | LIMIT NAME stmt_expr close_scope_limit
- {
- $$ = objref_stmt_alloc(&@$);
- $$->objref.type = NFT_OBJECT_LIMIT;
- $$->objref.expr = $3;
- }
;
quota_mode : OVER { $$ = NFT_QUOTA_F_INV; }
@@ -3528,12 +3576,6 @@ quota_stmt : QUOTA quota_mode NUM quota_unit quota_used close_scope_quota
$$->quota.used = $5;
$$->quota.flags = $2;
}
- | QUOTA NAME stmt_expr close_scope_quota
- {
- $$ = objref_stmt_alloc(&@$);
- $$->objref.type = NFT_OBJECT_QUOTA;
- $$->objref.expr = $3;
- }
;
limit_mode : OVER { $$ = NFT_LIMIT_F_INV; }
@@ -3724,12 +3766,6 @@ synproxy_stmt_alloc : SYNPROXY
{
$$ = synproxy_stmt_alloc(&@$);
}
- | SYNPROXY NAME stmt_expr
- {
- $$ = objref_stmt_alloc(&@$);
- $$->objref.type = NFT_OBJECT_SYNPROXY;
- $$->objref.expr = $3;
- }
;
synproxy_args : synproxy_arg
@@ -5542,19 +5578,6 @@ ct_stmt : CT ct_key SET stmt_expr close_scope_ct
break;
}
}
- | CT TIMEOUT SET stmt_expr close_scope_ct
- {
- $$ = objref_stmt_alloc(&@$);
- $$->objref.type = NFT_OBJECT_CT_TIMEOUT;
- $$->objref.expr = $4;
-
- }
- | CT EXPECTATION SET stmt_expr close_scope_ct
- {
- $$ = objref_stmt_alloc(&@$);
- $$->objref.type = NFT_OBJECT_CT_EXPECT;
- $$->objref.expr = $4;
- }
| CT ct_dir ct_key_dir_optional SET stmt_expr close_scope_ct
{
$$ = ct_stmt_alloc(&@$, $3, $2, $5);
diff --git a/tests/shell/testcases/bogons/nft-f/counter_objref_crash b/tests/shell/testcases/bogons/nft-f/counter_objref_crash
new file mode 100644
index 0000000..3a4b981
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/counter_objref_crash
@@ -0,0 +1,5 @@
+table inet x {
+ chain y {
+ counter name ip saddr bytes 1.1.1. 1024
+ }
+}
diff --git a/tests/shell/testcases/bogons/nft-f/netlink_gen_stmt_stateful_assert b/tests/shell/testcases/bogons/nft-f/netlink_gen_stmt_stateful_assert
new file mode 100644
index 0000000..547b937
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/netlink_gen_stmt_stateful_assert
@@ -0,0 +1,6 @@
+table ip x {
+ map sctm_o1 {
+ type mark : counter
+ counter name meta mark
+ }
+}

View File

@ -0,0 +1,128 @@
From 62be3f747b22309adff113b95d6cadc2d7a0a470 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] evaluate: fix bogus assertion failure with boolean datatype
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 567937b5560fbcc7f6b74fb43c52e1cab2ac425a
commit 567937b5560fbcc7f6b74fb43c52e1cab2ac425a
Author: Florian Westphal <fw@strlen.de>
Date: Fri Dec 8 19:38:33 2023 +0100
evaluate: fix bogus assertion failure with boolean datatype
The assertion is too strict, as found by afl++:
typeof iifname . ip saddr . meta ipsec
elements = { "eth0" . 10.1.1.2 . 1 }
meta ipsec is boolean (1 bit), but datasize of 1 is set at 8 bit.
Fixes: 22b750aa6dc9 ("src: allow use of base integer types as set keys in concatenations")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 7 ++++---
.../testcases/sets/dumps/typeof_sets_0.nft | 9 +++++++++
tests/shell/testcases/sets/typeof_sets_0 | 17 +++++++++++++++++
3 files changed, 30 insertions(+), 3 deletions(-)
diff --git a/src/evaluate.c b/src/evaluate.c
index cfdc6c0..fa3512b 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -4618,14 +4618,15 @@ static int set_expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
"expressions",
i->dtype->name);
- if (i->dtype->size)
- assert(i->len == i->dtype->size);
-
flags &= i->flags;
ntype = concat_subtype_add(ntype, i->dtype->type);
dsize_bytes = div_round_up(i->len, BITS_PER_BYTE);
+
+ if (i->dtype->size)
+ assert(dsize_bytes == div_round_up(i->dtype->size, BITS_PER_BYTE));
+
(*expr)->field_len[(*expr)->field_count++] = dsize_bytes;
size += netlink_padded_len(i->len);
}
diff --git a/tests/shell/testcases/sets/dumps/typeof_sets_0.nft b/tests/shell/testcases/sets/dumps/typeof_sets_0.nft
index 6f5b83a..63fc5b1 100644
--- a/tests/shell/testcases/sets/dumps/typeof_sets_0.nft
+++ b/tests/shell/testcases/sets/dumps/typeof_sets_0.nft
@@ -55,6 +55,11 @@ table inet t {
elements = { 3567 . 1.2.3.4 }
}
+ set s12 {
+ typeof iifname . ip saddr . meta ipsec
+ elements = { "eth0" . 10.1.1.2 . exists }
+ }
+
chain c1 {
osf name @s1 accept
}
@@ -94,4 +99,8 @@ table inet t {
chain c11 {
vlan id . ip saddr @s11 accept
}
+
+ chain c12 {
+ iifname . ip saddr . meta ipsec @s12 accept
+ }
}
diff --git a/tests/shell/testcases/sets/typeof_sets_0 b/tests/shell/testcases/sets/typeof_sets_0
index 35c572c..943c9c2 100755
--- a/tests/shell/testcases/sets/typeof_sets_0
+++ b/tests/shell/testcases/sets/typeof_sets_0
@@ -92,6 +92,10 @@ INPUT="table inet t {$INPUT_OSF_SET
typeof vlan id . ip saddr
elements = { 3567 . 1.2.3.4 }
}
+ set s12 {
+ typeof meta iifname . ip saddr . meta ipsec
+ elements = { \"eth0\" . 10.1.1.2 . 1 }
+ }
$INPUT_OSF_CHAIN
chain c2 {
ether type vlan vlan id @s2 accept
@@ -124,6 +128,10 @@ $INPUT_SCTP_CHAIN
chain c11 {
ether type vlan vlan id . ip saddr @s11 accept
}
+
+ chain c12 {
+ meta iifname . ip saddr . meta ipsec @s12 accept
+ }
}"
EXPECTED="table inet t {$INPUT_OSF_SET
@@ -177,6 +185,11 @@ EXPECTED="table inet t {$INPUT_OSF_SET
typeof vlan id . ip saddr
elements = { 3567 . 1.2.3.4 }
}
+
+ set s12 {
+ typeof iifname . ip saddr . meta ipsec
+ elements = { \"eth0\" . 10.1.1.2 . exists }
+ }
$INPUT_OSF_CHAIN
chain c2 {
vlan id @s2 accept
@@ -209,6 +222,10 @@ $INPUT_SCTP_CHAIN
chain c11 {
vlan id . ip saddr @s11 accept
}
+
+ chain c12 {
+ iifname . ip saddr . meta ipsec @s12 accept
+ }
}"

View File

@ -0,0 +1,58 @@
From fdb03b033c3efcb28b4c51c469d3e93559d7bdb8 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] parser_bison: close chain scope before chain release
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 28b3bbec5a97aaa57f7138fee8b2f22ff0e5b960
commit 28b3bbec5a97aaa57f7138fee8b2f22ff0e5b960
Author: Florian Westphal <fw@strlen.de>
Date: Wed Dec 13 11:09:58 2023 +0100
parser_bison: close chain scope before chain release
cmd_alloc() will free the chain, so we must close the scope opened
in chain_block_alloc beforehand.
The included test file will cause a use-after-free because nft attempts
to search for an identifier in a scope that has been freed:
AddressSanitizer: heap-use-after-free on address 0x618000000368 at pc 0x7f1cbc0e6959 bp 0x7ffd3ccb7850 sp 0x7ffd3ccb7840
#0 0x7f1cbc0e6958 in symbol_lookup src/rule.c:629
#1 0x7f1cbc0e66a1 in symbol_get src/rule.c:588
#2 0x7f1cbc120d67 in nft_parse src/parser_bison.y:4325
Fixes: a66b5ad9540d ("src: allow for updating devices on existing netdev chain")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_bison.y | 1 +
.../testcases/bogons/nft-f/use_after_free_on_chain_removal | 5 +++++
2 files changed, 6 insertions(+)
create mode 100644 tests/shell/testcases/bogons/nft-f/use_after_free_on_chain_removal
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 2c829f9..83302fd 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -1397,6 +1397,7 @@ delete_cmd : TABLE table_or_id_spec
{
$5->location = @5;
handle_merge(&$3->handle, &$2);
+ close_scope(state);
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_CHAIN, &$2, &@$, $5);
}
| RULE ruleid_spec
diff --git a/tests/shell/testcases/bogons/nft-f/use_after_free_on_chain_removal b/tests/shell/testcases/bogons/nft-f/use_after_free_on_chain_removal
new file mode 100644
index 0000000..bb9632b
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/use_after_free_on_chain_removal
@@ -0,0 +1,5 @@
+delete chain d iUi {
+}}
+delete chain d hUi {
+delete chain o
+c b icmpv6 id$i

View File

@ -0,0 +1,112 @@
From ae85b206060f420d4eb8f2ba0a61bb6c6813b908 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] meta: fix tc classid parsing out-of-bounds access
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 7008b1200fb4988b7cd7ee1c5399cae071688d50
commit 7008b1200fb4988b7cd7ee1c5399cae071688d50
Author: Florian Westphal <fw@strlen.de>
Date: Wed Dec 13 17:37:11 2023 +0100
meta: fix tc classid parsing out-of-bounds access
AddressSanitizer: heap-buffer-overflow on address 0x6020000003af ...
#0 0x7f9a83cbb402 in tchandle_type_parse src/meta.c:89
#1 0x7f9a83c6753f in symbol_parse src/datatype.c:138
strlen() - 1 can underflow if length was 0.
Simplify the function, there is no need to duplicate the string
while scanning it.
Expect the first strtol to stop at ':', scan for the minor number next.
The second scan is required to stop at '\0'.
Fixes: 6f2eb8548e0d ("src: meta priority support using tc classid")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/meta.c | 29 ++++++-------------
.../nft-f/tchandle_type_parse_heap_overflow | 6 ++++
2 files changed, 15 insertions(+), 20 deletions(-)
create mode 100644 tests/shell/testcases/bogons/nft-f/tchandle_type_parse_heap_overflow
diff --git a/src/meta.c b/src/meta.c
index 4e55e00..6f76f00 100644
--- a/src/meta.c
+++ b/src/meta.c
@@ -62,50 +62,39 @@ static struct error_record *tchandle_type_parse(struct parse_ctx *ctx,
struct expr **res)
{
uint32_t handle;
- char *str = NULL;
if (strcmp(sym->identifier, "root") == 0)
handle = TC_H_ROOT;
else if (strcmp(sym->identifier, "none") == 0)
handle = TC_H_UNSPEC;
else if (strchr(sym->identifier, ':')) {
+ char *colon, *end;
uint32_t tmp;
- char *colon;
-
- str = xstrdup(sym->identifier);
-
- colon = strchr(str, ':');
- if (!colon)
- goto err;
-
- *colon = '\0';
errno = 0;
- tmp = strtoull(str, NULL, 16);
- if (errno != 0)
+ tmp = strtoul(sym->identifier, &colon, 16);
+ if (errno != 0 || sym->identifier == colon)
goto err;
- handle = (tmp << 16);
- if (str[strlen(str) - 1] == ':')
- goto out;
+ if (*colon != ':')
+ goto err;
+ handle = tmp << 16;
errno = 0;
- tmp = strtoull(colon + 1, NULL, 16);
- if (errno != 0)
+ tmp = strtoul(colon + 1, &end, 16);
+ if (errno != 0 || *end)
goto err;
handle |= tmp;
} else {
handle = strtoull(sym->identifier, NULL, 0);
}
-out:
- free(str);
+
*res = constant_expr_alloc(&sym->location, sym->dtype,
BYTEORDER_HOST_ENDIAN,
sizeof(handle) * BITS_PER_BYTE, &handle);
return NULL;
err:
- free(str);
return error(&sym->location, "Could not parse %s", sym->dtype->desc);
}
diff --git a/tests/shell/testcases/bogons/nft-f/tchandle_type_parse_heap_overflow b/tests/shell/testcases/bogons/nft-f/tchandle_type_parse_heap_overflow
new file mode 100644
index 0000000..ea7186b
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/tchandle_type_parse_heap_overflow
@@ -0,0 +1,6 @@
+table t {
+map m {
+ type ipv4_addr : classid
+ elements = { 1.1.26.3 : ::a }
+}
+}

View File

@ -0,0 +1,69 @@
From 9f755eed0849227f88d69b5efeb2809a8fc311a7 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] evaluate: exthdr: statement arg must be not be a range
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 8eeedce89d8bf0ad58da398782c2ca8a91d83a32
commit 8eeedce89d8bf0ad58da398782c2ca8a91d83a32
Author: Florian Westphal <fw@strlen.de>
Date: Thu Dec 14 17:56:59 2023 +0100
evaluate: exthdr: statement arg must be not be a range
Else we get:
BUG: unknown expression type range
nft: src/netlink_linearize.c:909: netlink_gen_expr: Assertion `0' failed.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 19 ++++++++++++++++---
.../bogons/nft-f/exthdr_with_range_bug | 1 +
2 files changed, 17 insertions(+), 3 deletions(-)
create mode 100644 tests/shell/testcases/bogons/nft-f/exthdr_with_range_bug
diff --git a/src/evaluate.c b/src/evaluate.c
index fa3512b..78a5d10 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -2964,14 +2964,27 @@ static bool stmt_evaluate_payload_need_csum(const struct expr *payload)
static int stmt_evaluate_exthdr(struct eval_ctx *ctx, struct stmt *stmt)
{
struct expr *exthdr;
+ int ret;
if (__expr_evaluate_exthdr(ctx, &stmt->exthdr.expr) < 0)
return -1;
exthdr = stmt->exthdr.expr;
- return stmt_evaluate_arg(ctx, stmt, exthdr->dtype, exthdr->len,
- BYTEORDER_BIG_ENDIAN,
- &stmt->exthdr.val);
+ ret = stmt_evaluate_arg(ctx, stmt, exthdr->dtype, exthdr->len,
+ BYTEORDER_BIG_ENDIAN,
+ &stmt->exthdr.val);
+ if (ret < 0)
+ return ret;
+
+ switch (stmt->exthdr.val->etype) {
+ case EXPR_RANGE:
+ return expr_error(ctx->msgs, stmt->exthdr.val,
+ "cannot be a range");
+ default:
+ break;
+ }
+
+ return 0;
}
static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
diff --git a/tests/shell/testcases/bogons/nft-f/exthdr_with_range_bug b/tests/shell/testcases/bogons/nft-f/exthdr_with_range_bug
new file mode 100644
index 0000000..e307e7c
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/exthdr_with_range_bug
@@ -0,0 +1 @@
+add rule t c ip option ra set 0-1

View File

@ -0,0 +1,121 @@
From 15e1ec4b465ba529675998d6da5d90ba2020f087 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] src: reject large raw payload and concat expressions
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit ef10d65db278d77208e960d210a1f4f532ebb552
commit ef10d65db278d77208e960d210a1f4f532ebb552
Author: Florian Westphal <fw@strlen.de>
Date: Tue Dec 12 19:13:14 2023 +0100
src: reject large raw payload and concat expressions
The kernel will reject this too, but unfortunately nft may try
to cram the data into the underlying libnftnl expr.
This causes heap corruption or
BUG: nld buffer overflow: want to copy 132, max 64
After:
Error: Concatenation of size 544 exceeds maximum size of 512
udp length . @th,0,512 . @th,512,512 { 47-63 . 0xe373135363130 . 0x33131303735353203 }
^^^^^^^^^
resp. same warning for an over-sized raw expression.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
include/expression.h | 3 +++
src/evaluate.c | 8 ++++++++
src/parser_bison.y | 7 +++++++
.../bogons/nft-f/stack_overflow_via_large_concat_expr | 5 +++++
.../bogons/nft-f/stack_overflow_via_large_raw_expr | 5 +++++
5 files changed, 28 insertions(+)
create mode 100644 tests/shell/testcases/bogons/nft-f/stack_overflow_via_large_concat_expr
create mode 100644 tests/shell/testcases/bogons/nft-f/stack_overflow_via_large_raw_expr
diff --git a/include/expression.h b/include/expression.h
index aede223..809089c 100644
--- a/include/expression.h
+++ b/include/expression.h
@@ -11,6 +11,9 @@
#include <json.h>
#include <libnftnl/udata.h>
+#define NFT_MAX_EXPR_LEN_BYTES (NFT_REG32_COUNT * sizeof(uint32_t))
+#define NFT_MAX_EXPR_LEN_BITS (NFT_MAX_EXPR_LEN_BYTES * BITS_PER_BYTE)
+
/**
* enum expr_types
*
diff --git a/src/evaluate.c b/src/evaluate.c
index 78a5d10..1a8c815 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1569,6 +1569,10 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
}
ctx->inner_desc = NULL;
+
+ if (size > NFT_MAX_EXPR_LEN_BITS)
+ return expr_error(ctx->msgs, i, "Concatenation of size %u exceeds maximum size of %u",
+ size, NFT_MAX_EXPR_LEN_BITS);
}
(*expr)->flags |= flags;
@@ -4642,6 +4646,10 @@ static int set_expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
(*expr)->field_len[(*expr)->field_count++] = dsize_bytes;
size += netlink_padded_len(i->len);
+
+ if (size > NFT_MAX_EXPR_LEN_BITS)
+ return expr_error(ctx->msgs, i, "Concatenation of size %u exceeds maximum size of %u",
+ size, NFT_MAX_EXPR_LEN_BITS);
}
(*expr)->flags |= flags;
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 83302fd..fa59207 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -5620,6 +5620,13 @@ payload_expr : payload_raw_expr
payload_raw_expr : AT payload_base_spec COMMA NUM COMMA NUM close_scope_at
{
+ if ($6 > NFT_MAX_EXPR_LEN_BITS) {
+ erec_queue(error(&@1, "raw payload length %u exceeds upper limit of %u",
+ $6, NFT_MAX_EXPR_LEN_BITS),
+ state->msgs);
+ YYERROR;
+ }
+
$$ = payload_expr_alloc(&@$, NULL, 0);
payload_init_raw($$, $2, $4, $6);
$$->byteorder = BYTEORDER_BIG_ENDIAN;
diff --git a/tests/shell/testcases/bogons/nft-f/stack_overflow_via_large_concat_expr b/tests/shell/testcases/bogons/nft-f/stack_overflow_via_large_concat_expr
new file mode 100644
index 0000000..8b0d274
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/stack_overflow_via_large_concat_expr
@@ -0,0 +1,5 @@
+table t {
+ chain c {
+ udp length . @th,0,512 . @th,512,512 { 47-63 . 0xe373135363130 . 0x33131303735353203 }
+ }
+}
diff --git a/tests/shell/testcases/bogons/nft-f/stack_overflow_via_large_raw_expr b/tests/shell/testcases/bogons/nft-f/stack_overflow_via_large_raw_expr
new file mode 100644
index 0000000..66bd6bf
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/stack_overflow_via_large_raw_expr
@@ -0,0 +1,5 @@
+table t {
+ chain c {
+ @th,160,1272 gt 0
+ }
+}

View File

@ -0,0 +1,51 @@
From 23a5147a32733f75548526e9b8769cd1157b34f8 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] evaluate: fix stack overflow with huge priority string
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit b626c86abaf294fcf1ec788f722071dc90da68c4
commit b626c86abaf294fcf1ec788f722071dc90da68c4
Author: Florian Westphal <fw@strlen.de>
Date: Fri Dec 15 10:19:02 2023 +0100
evaluate: fix stack overflow with huge priority string
Alternative would be to refactor this and move this into the parsers
(bison, json) instead of this hidden re-parsing.
Fixes: 627c451b2351 ("src: allow variables in the chain priority specification")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 2 +-
tests/shell/testcases/bogons/nft-f/huge_chain_prio | 5 +++++
2 files changed, 6 insertions(+), 1 deletion(-)
create mode 100644 tests/shell/testcases/bogons/nft-f/huge_chain_prio
diff --git a/src/evaluate.c b/src/evaluate.c
index 1a8c815..c655e3a 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -4820,7 +4820,7 @@ static bool evaluate_priority(struct eval_ctx *ctx, struct prio_spec *prio,
NFT_NAME_MAXLEN);
loc = prio->expr->location;
- if (sscanf(prio_str, "%s %c %d", prio_fst, &op, &prio_snd) < 3) {
+ if (sscanf(prio_str, "%255s %c %d", prio_fst, &op, &prio_snd) < 3) {
priority = std_prio_lookup(prio_str, family, hook);
if (priority == NF_IP_PRI_LAST)
return false;
diff --git a/tests/shell/testcases/bogons/nft-f/huge_chain_prio b/tests/shell/testcases/bogons/nft-f/huge_chain_prio
new file mode 100644
index 0000000..41f8061
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/huge_chain_prio
@@ -0,0 +1,5 @@
+table t {
+ chain c {
+ type filter hook input priority srcnDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD#DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD; policy accept;
+ }
+}

View File

@ -0,0 +1,220 @@
From dcd92e16a863ba062ff9792dd304b0eb958fb60c Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] tests: shell: add test to cover payload transport match and
mangle
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 84da729e067a68bd50be8c0791d16901360be5ad
commit 84da729e067a68bd50be8c0791d16901360be5ad
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Sun Dec 17 00:15:08 2023 +0100
tests: shell: add test to cover payload transport match and mangle
Exercise payload transport match and mangle for inet, bridge and netdev
families with IPv4 and IPv6 packets.
To cover kernel patch ("netfilter: nf_tables: set transport offset from
mac header for netdev/egress").
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
.../testcases/packetpath/dumps/payload.nodump | 0
tests/shell/testcases/packetpath/payload | 180 ++++++++++++++++++
2 files changed, 180 insertions(+)
create mode 100644 tests/shell/testcases/packetpath/dumps/payload.nodump
create mode 100755 tests/shell/testcases/packetpath/payload
diff --git a/tests/shell/testcases/packetpath/dumps/payload.nodump b/tests/shell/testcases/packetpath/dumps/payload.nodump
new file mode 100644
index 0000000..e69de29
diff --git a/tests/shell/testcases/packetpath/payload b/tests/shell/testcases/packetpath/payload
new file mode 100755
index 0000000..1a89d85
--- /dev/null
+++ b/tests/shell/testcases/packetpath/payload
@@ -0,0 +1,180 @@
+#!/bin/bash
+
+rnd=$(mktemp -u XXXXXXXX)
+ns1="nft1payload-$rnd"
+ns2="nft2payload-$rnd"
+
+cleanup()
+{
+ ip netns del "$ns1"
+ ip netns del "$ns2"
+}
+
+trap cleanup EXIT
+
+run_test()
+{
+ ns1_addr=$2
+ ns2_addr=$3
+ cidr=$4
+
+ # socat needs square brackets, ie. [abcd::2]
+ if [ $1 -eq 6 ]; then
+ nsx1_addr="["$ns1_addr"]"
+ nsx2_addr="["$ns2_addr"]"
+ else
+ nsx1_addr="$ns1_addr"
+ nsx2_addr="$ns2_addr"
+ fi
+
+ ip netns add "$ns1" || exit 111
+ ip netns add "$ns2" || exit 111
+
+ ip -net "$ns1" link set lo up
+ ip -net "$ns2" link set lo up
+
+ ip link add veth0 netns $ns1 type veth peer name veth0 netns $ns2
+
+ ip -net "$ns1" link set veth0 up
+ ip -net "$ns2" link set veth0 up
+ ip -net "$ns1" addr add $ns1_addr/$cidr dev veth0
+ ip -net "$ns2" addr add $ns2_addr/$cidr dev veth0
+
+RULESET="table netdev payload_netdev {
+ counter ingress {}
+ counter egress {}
+ counter mangle_ingress {}
+ counter mangle_egress {}
+ counter mangle_ingress_match {}
+ counter mangle_egress_match {}
+
+ chain ingress {
+ type filter hook ingress device veth0 priority 0;
+ tcp dport 7777 counter name ingress
+ tcp dport 7778 tcp dport set 7779 counter name mangle_ingress
+ tcp dport 7779 counter name mangle_ingress_match
+ }
+
+ chain egress {
+ type filter hook egress device veth0 priority 0;
+ tcp dport 8887 counter name egress
+ tcp dport 8888 tcp dport set 8889 counter name mangle_egress
+ tcp dport 8889 counter name mangle_egress_match
+ }
+}
+
+table inet payload_inet {
+ counter input {}
+ counter output {}
+ counter mangle_input {}
+ counter mangle_output {}
+ counter mangle_input_match {}
+ counter mangle_output_match {}
+
+ chain in {
+ type filter hook input priority 0;
+ tcp dport 7770 counter name input
+ tcp dport 7771 tcp dport set 7772 counter name mangle_input
+ tcp dport 7772 counter name mangle_input_match
+ }
+
+ chain out {
+ type filter hook output priority 0;
+ tcp dport 8880 counter name output
+ tcp dport 8881 tcp dport set 8882 counter name mangle_output
+ tcp dport 8882 counter name mangle_output_match
+ }
+}"
+
+ ip netns exec "$ns1" $NFT -f - <<< "$RULESET" || exit 1
+
+ ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8887,connect-timeout=2 < /dev/null > /dev/null
+ ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8888,connect-timeout=2 < /dev/null > /dev/null
+
+ ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8880,connect-timeout=2 < /dev/null > /dev/null
+ ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8881,connect-timeout=2 < /dev/null > /dev/null
+
+ ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7777,connect-timeout=2 < /dev/null > /dev/null
+ ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7778,connect-timeout=2 < /dev/null > /dev/null
+
+ ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7770,connect-timeout=2 < /dev/null > /dev/null
+ ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7771,connect-timeout=2 < /dev/null > /dev/null
+
+ ip netns exec "$ns1" $NFT list ruleset
+
+ ip netns exec "$ns1" nft list counter netdev payload_netdev ingress | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" nft list counter netdev payload_netdev mangle_ingress | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" nft list counter netdev payload_netdev mangle_ingress_match | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" nft list counter netdev payload_netdev egress | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" nft list counter netdev payload_netdev mangle_egress | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" nft list counter netdev payload_netdev mangle_egress_match | grep -v "packets 0" > /dev/null || exit 1
+
+ ip netns exec "$ns1" nft list counter inet payload_inet input | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" nft list counter inet payload_inet mangle_input | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" nft list counter inet payload_inet mangle_input_match | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" nft list counter inet payload_inet output | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" nft list counter inet payload_inet mangle_output | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" nft list counter inet payload_inet mangle_output_match | grep -v "packets 0" > /dev/null || exit 1
+
+ #
+ # ... next stage
+ #
+
+ ip netns exec "$ns1" $NFT flush ruleset
+
+ #
+ # bridge
+ #
+
+ ip -net "$ns1" addr del $ns1_addr/$cidr dev veth0
+
+ ip -net "$ns1" link add name br0 type bridge
+ ip -net "$ns1" link set veth0 master br0
+ ip -net "$ns1" addr add $ns1_addr/$cidr dev br0
+ ip -net "$ns1" link set up dev br0
+
+RULESET="table bridge payload_bridge {
+ counter input {}
+ counter output {}
+ counter mangle_input {}
+ counter mangle_output {}
+ counter mangle_input_match {}
+ counter mangle_output_match {}
+
+ chain in {
+ type filter hook input priority 0;
+ tcp dport 7770 counter name input
+ tcp dport 7771 tcp dport set 7772 counter name mangle_input
+ tcp dport 7772 counter name mangle_input_match
+ }
+
+ chain out {
+ type filter hook output priority 0;
+ tcp dport 8880 counter name output
+ tcp dport 8881 tcp dport set 8882 counter name mangle_output
+ tcp dport 8882 counter name mangle_output_match
+ }
+}"
+
+ ip netns exec "$ns1" $NFT -f - <<< "$RULESET" || exit 1
+
+ ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8880,connect-timeout=2 < /dev/null > /dev/null
+ ip netns exec "$ns1" socat -u STDIN TCP:$nsx2_addr:8881,connect-timeout=2 < /dev/null > /dev/null
+
+ ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7770,connect-timeout=2 < /dev/null > /dev/null
+ ip netns exec "$ns2" socat -u STDIN TCP:$nsx1_addr:7771,connect-timeout=2 < /dev/null > /dev/null
+
+ ip netns exec "$ns1" $NFT list ruleset
+
+ ip netns exec "$ns1" nft list counter bridge payload_bridge input | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" nft list counter bridge payload_bridge mangle_input | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" nft list counter bridge payload_bridge mangle_input_match | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" nft list counter bridge payload_bridge output | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" nft list counter bridge payload_bridge mangle_output | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" nft list counter bridge payload_bridge mangle_output_match | grep -v "packets 0" > /dev/null || exit 1
+}
+
+run_test "4" "10.141.10.2" "10.141.10.3" "24"
+cleanup
+run_test 6 "abcd::2" "abcd::3" "64"
+# trap calls cleanup

View File

@ -0,0 +1,120 @@
From e1c01c2858d276e16b8fea5c42014799d00258c9 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] parser_bison: error out on duplicated type/typeof/element
keywords
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 6c04e5ceb95068bb459b07307ecc3629d97a2043
commit 6c04e5ceb95068bb459b07307ecc3629d97a2043
Author: Florian Westphal <fw@strlen.de>
Date: Tue Dec 19 16:22:32 2023 +0100
parser_bison: error out on duplicated type/typeof/element keywords
Otherwise nft will leak the previous definition (expressions).
Also remove the nonsensical
datatype_set($1->key, $3->dtype);
This is a no-op, at this point: $1->key and $3 are identical.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_bison.y | 38 ++++++++++++++++++++++++++++++++++++--
1 file changed, 36 insertions(+), 2 deletions(-)
diff --git a/src/parser_bison.y b/src/parser_bison.y
index fa59207..0ab9dec 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -2159,11 +2159,20 @@ set_block : /* empty */ { $$ = $<set>-1; }
| set_block stmt_separator
| set_block TYPE data_type_expr stmt_separator close_scope_type
{
+ if (already_set($1->key, &@2, state)) {
+ expr_free($3);
+ YYERROR;
+ }
+
$1->key = $3;
$$ = $1;
}
| set_block TYPEOF typeof_expr stmt_separator
{
+ if (already_set($1->key, &@2, state)) {
+ expr_free($3);
+ YYERROR;
+ }
$1->key = $3;
datatype_set($1->key, $3->dtype);
$$ = $1;
@@ -2191,6 +2200,10 @@ set_block : /* empty */ { $$ = $<set>-1; }
}
| set_block ELEMENTS '=' set_block_expr
{
+ if (already_set($1->init, &@2, state)) {
+ expr_free($4);
+ YYERROR;
+ }
$1->init = $4;
$$ = $1;
}
@@ -2262,6 +2275,12 @@ map_block : /* empty */ { $$ = $<set>-1; }
data_type_expr COLON map_block_data_interval data_type_expr
stmt_separator close_scope_type
{
+ if (already_set($1->key, &@2, state)) {
+ expr_free($3);
+ expr_free($6);
+ YYERROR;
+ }
+
$1->key = $3;
$1->data = $6;
$1->data->flags |= $5;
@@ -2273,8 +2292,13 @@ map_block : /* empty */ { $$ = $<set>-1; }
typeof_expr COLON typeof_data_expr
stmt_separator
{
+ if (already_set($1->key, &@2, state)) {
+ expr_free($3);
+ expr_free($5);
+ YYERROR;
+ }
+
$1->key = $3;
- datatype_set($1->key, $3->dtype);
$1->data = $5;
$1->flags |= NFT_SET_MAP;
@@ -2284,8 +2308,13 @@ map_block : /* empty */ { $$ = $<set>-1; }
typeof_expr COLON INTERVAL typeof_expr
stmt_separator
{
+ if (already_set($1->key, &@2, state)) {
+ expr_free($3);
+ expr_free($6);
+ YYERROR;
+ }
+
$1->key = $3;
- datatype_set($1->key, $3->dtype);
$1->data = $6;
$1->data->flags |= EXPR_F_INTERVAL;
@@ -2296,6 +2325,11 @@ map_block : /* empty */ { $$ = $<set>-1; }
data_type_expr COLON map_block_obj_type
stmt_separator close_scope_type
{
+ if (already_set($1->key, &@2, state)) {
+ expr_free($3);
+ YYERROR;
+ }
+
$1->key = $3;
$1->objtype = $5;
$1->flags |= NFT_SET_OBJECT;

View File

@ -0,0 +1,81 @@
From 0ef6256eae2581795a6aa9070876ae4d909c7f50 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] netlink: fix stack overflow due to erroneous rounding
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit b9e19cc396347df8c7f8cf5d14ba1d6172040f16
commit b9e19cc396347df8c7f8cf5d14ba1d6172040f16
Author: Florian Westphal <fw@strlen.de>
Date: Wed Dec 20 15:40:54 2023 +0100
netlink: fix stack overflow due to erroneous rounding
Byteorder switch in this function may undersize the conversion
buffer by one byte, this needs to use div_round_up().
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/netlink.c | 11 ++++++++---
.../bogons/nft-f/byteorder_switch_stack_overflow | 6 ++++++
2 files changed, 14 insertions(+), 3 deletions(-)
create mode 100644 tests/shell/testcases/bogons/nft-f/byteorder_switch_stack_overflow
diff --git a/src/netlink.c b/src/netlink.c
index a366758..8637186 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -254,6 +254,11 @@ static int netlink_export_pad(unsigned char *data, const mpz_t v,
return netlink_padded_len(i->len) / BITS_PER_BYTE;
}
+static void byteorder_switch_expr_value(mpz_t v, const struct expr *e)
+{
+ mpz_switch_byteorder(v, div_round_up(e->len, BITS_PER_BYTE));
+}
+
static int __netlink_gen_concat_key(uint32_t flags, const struct expr *i,
unsigned char *data)
{
@@ -268,7 +273,7 @@ static int __netlink_gen_concat_key(uint32_t flags, const struct expr *i,
if (expr_basetype(expr)->type == TYPE_INTEGER &&
expr->byteorder == BYTEORDER_HOST_ENDIAN)
- mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE);
+ byteorder_switch_expr_value(expr->value, expr);
i = expr;
break;
@@ -280,7 +285,7 @@ static int __netlink_gen_concat_key(uint32_t flags, const struct expr *i,
mpz_init_bitmask(v, i->len - i->prefix_len);
if (i->byteorder == BYTEORDER_HOST_ENDIAN)
- mpz_switch_byteorder(v, i->len / BITS_PER_BYTE);
+ byteorder_switch_expr_value(v, i);
mpz_add(v, i->prefix->value, v);
count = netlink_export_pad(data, v, i);
@@ -298,7 +303,7 @@ static int __netlink_gen_concat_key(uint32_t flags, const struct expr *i,
expr = (struct expr *)i;
if (expr_basetype(expr)->type == TYPE_INTEGER &&
expr->byteorder == BYTEORDER_HOST_ENDIAN)
- mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE);
+ byteorder_switch_expr_value(expr->value, expr);
break;
default:
BUG("invalid expression type '%s' in set", expr_ops(i)->name);
diff --git a/tests/shell/testcases/bogons/nft-f/byteorder_switch_stack_overflow b/tests/shell/testcases/bogons/nft-f/byteorder_switch_stack_overflow
new file mode 100644
index 0000000..0164052
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/byteorder_switch_stack_overflow
@@ -0,0 +1,6 @@
+table inet x {
+ chain nat_dns_acme {
+ udp length . @th,260,118 vmap { 47-63 . 0xe373135363130333131303735353203 : goto nat_dns_dnstc, }
+ drop
+ }
+}

View File

@ -0,0 +1,125 @@
From 023ceb40acf7fd6dad65c149243b2dbc16838f87 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] parser_bison: ensure all timeout policy names are released
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 86a496928420046e9d32317f09db050e8351b10e
commit 86a496928420046e9d32317f09db050e8351b10e
Author: Florian Westphal <fw@strlen.de>
Date: Tue Dec 12 13:32:24 2023 +0100
parser_bison: ensure all timeout policy names are released
We need to add a custom destructor for this structure, it
contains the dynamically allocated names.
a:5:55-55: Error: syntax error, unexpected '}', expecting string
policy = { estabQisheestablished : 2m3s, cd : 2m3s, }
==562373==ERROR: LeakSanitizer: detected memory leaks
Indirect leak of 160 byte(s) in 2 object(s) allocated from:
#1 0x5a565b in xmalloc src/utils.c:31:8
#2 0x5a565b in xzalloc src/utils.c:70:8
#3 0x3d9352 in nft_parse_bison_filename src/libnftables.c:520:8
[..]
Fixes: c7c94802679c ("src: add ct timeout support")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_bison.y | 32 ++++++++++++++++---
.../testcases/bogons/nft-f/ct_timeout_memleak | 7 ++++
2 files changed, 34 insertions(+), 5 deletions(-)
create mode 100644 tests/shell/testcases/bogons/nft-f/ct_timeout_memleak
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 0ab9dec..e81336f 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -173,6 +173,24 @@ static struct expr *ifname_expr_alloc(const struct location *location,
return expr;
}
+static void timeout_state_free(struct timeout_state *s)
+{
+ free_const(s->timeout_str);
+ free(s);
+}
+
+static void timeout_states_free(struct list_head *list)
+{
+ struct timeout_state *ts, *next;
+
+ list_for_each_entry_safe(ts, next, list, head) {
+ list_del(&ts->head);
+ timeout_state_free(ts);
+ }
+
+ free(list);
+}
+
#define YYLLOC_DEFAULT(Current, Rhs, N) location_update(&Current, Rhs, N)
#define symbol_value(loc, str) \
@@ -230,6 +248,7 @@ int nft_lex(void *, void *, void *);
uint16_t kind; /* must allow > 255 for SACK1, 2.. hack */
uint8_t field;
} tcp_kind_field;
+ struct timeout_state *timeout_state;
}
%token TOKEN_EOF 0 "end of file"
@@ -969,8 +988,11 @@ int nft_lex(void *, void *, void *);
%type <val> ct_l4protoname ct_obj_type ct_cmd_type
-%type <list> timeout_states timeout_state
-%destructor { free($$); } timeout_states timeout_state
+%type <timeout_state> timeout_state
+%destructor { timeout_state_free($$); } timeout_state
+
+%type <list> timeout_states
+%destructor { timeout_states_free($$); } timeout_states
%type <val> xfrm_state_key xfrm_state_proto_key xfrm_dir xfrm_spnum
%type <expr> xfrm_expr
@@ -4896,11 +4918,11 @@ timeout_states : timeout_state
{
$$ = xmalloc(sizeof(*$$));
init_list_head($$);
- list_add_tail($1, $$);
+ list_add_tail(&$1->head, $$);
}
| timeout_states COMMA timeout_state
{
- list_add_tail($3, $1);
+ list_add_tail(&$3->head, $1);
$$ = $1;
}
;
@@ -4914,7 +4936,7 @@ timeout_state : STRING COLON time_spec_or_num_s
ts->timeout_value = $3;
ts->location = @1;
init_list_head(&ts->head);
- $$ = &ts->head;
+ $$ = ts;
}
;
diff --git a/tests/shell/testcases/bogons/nft-f/ct_timeout_memleak b/tests/shell/testcases/bogons/nft-f/ct_timeout_memleak
new file mode 100644
index 0000000..014525a
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/ct_timeout_memleak
@@ -0,0 +1,7 @@
+table ip filter {
+ ct timeout cttime {
+ protocol tcp
+ l3proto ip
+ policy = { estabQisheestablished : 2m3s, cd : 2m3s, }
+ }
+}

View File

@ -0,0 +1,94 @@
From 21c9b6d963d6aeff380324e411a0a871b422b923 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] tests: shell: prefer project nft to system-wide nft
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 8440983b7671feb3bb86c4c148af743addf3e00b
commit 8440983b7671feb3bb86c4c148af743addf3e00b
Author: Florian Westphal <fw@strlen.de>
Date: Sat Jan 6 13:23:24 2024 +0100
tests: shell: prefer project nft to system-wide nft
Use $NFT (src/nft, in-tree binary), not the one installed by the distro.
Else we may not find newly added bugs unless user did "make install" or
bug has propagated to release.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/shell/testcases/packetpath/payload | 38 +++++++++----------
tests/shell/testcases/parsing/large_rule_pipe | 2 +-
2 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/tests/shell/testcases/packetpath/payload b/tests/shell/testcases/packetpath/payload
index 1a89d85..9f4587d 100755
--- a/tests/shell/testcases/packetpath/payload
+++ b/tests/shell/testcases/packetpath/payload
@@ -102,19 +102,19 @@ table inet payload_inet {
ip netns exec "$ns1" $NFT list ruleset
- ip netns exec "$ns1" nft list counter netdev payload_netdev ingress | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" nft list counter netdev payload_netdev mangle_ingress | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" nft list counter netdev payload_netdev mangle_ingress_match | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" nft list counter netdev payload_netdev egress | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" nft list counter netdev payload_netdev mangle_egress | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" nft list counter netdev payload_netdev mangle_egress_match | grep -v "packets 0" > /dev/null || exit 1
-
- ip netns exec "$ns1" nft list counter inet payload_inet input | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" nft list counter inet payload_inet mangle_input | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" nft list counter inet payload_inet mangle_input_match | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" nft list counter inet payload_inet output | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" nft list counter inet payload_inet mangle_output | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" nft list counter inet payload_inet mangle_output_match | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter netdev payload_netdev ingress | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter netdev payload_netdev mangle_ingress | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter netdev payload_netdev mangle_ingress_match | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter netdev payload_netdev egress | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter netdev payload_netdev mangle_egress | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter netdev payload_netdev mangle_egress_match | grep -v "packets 0" > /dev/null || exit 1
+
+ ip netns exec "$ns1" $NFT list counter inet payload_inet input | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter inet payload_inet mangle_input | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter inet payload_inet mangle_input_match | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter inet payload_inet output | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter inet payload_inet mangle_output | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter inet payload_inet mangle_output_match | grep -v "packets 0" > /dev/null || exit 1
#
# ... next stage
@@ -166,12 +166,12 @@ RULESET="table bridge payload_bridge {
ip netns exec "$ns1" $NFT list ruleset
- ip netns exec "$ns1" nft list counter bridge payload_bridge input | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" nft list counter bridge payload_bridge mangle_input | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" nft list counter bridge payload_bridge mangle_input_match | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" nft list counter bridge payload_bridge output | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" nft list counter bridge payload_bridge mangle_output | grep -v "packets 0" > /dev/null || exit 1
- ip netns exec "$ns1" nft list counter bridge payload_bridge mangle_output_match | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter bridge payload_bridge input | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter bridge payload_bridge mangle_input | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter bridge payload_bridge mangle_input_match | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter bridge payload_bridge output | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter bridge payload_bridge mangle_output | grep -v "packets 0" > /dev/null || exit 1
+ ip netns exec "$ns1" $NFT list counter bridge payload_bridge mangle_output_match | grep -v "packets 0" > /dev/null || exit 1
}
run_test "4" "10.141.10.2" "10.141.10.3" "24"
diff --git a/tests/shell/testcases/parsing/large_rule_pipe b/tests/shell/testcases/parsing/large_rule_pipe
index fac0afa..b6760c0 100755
--- a/tests/shell/testcases/parsing/large_rule_pipe
+++ b/tests/shell/testcases/parsing/large_rule_pipe
@@ -566,6 +566,6 @@ table inet firewalld {
}
}"
-( echo "flush ruleset;"; echo "${RULESET}" ) | nft -f -
+( echo "flush ruleset;"; echo "${RULESET}" ) | $NFT -f -
exit 0

View File

@ -0,0 +1,49 @@
From 5104ecc5b5f0726ab58df27742c9c3cf8a65f1b3 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] doc: incorrect datatype description for icmpv6_type and
icmpvx_code
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 9caa5142f95557decc19925ad37411f1df4589a2
commit 9caa5142f95557decc19925ad37411f1df4589a2
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue Jan 9 12:42:20 2024 +0100
doc: incorrect datatype description for icmpv6_type and icmpvx_code
Fix incorrect description in manpage:
ICMPV6 TYPE TYPE is icmpv6_type
ICMPVX CODE TYPE is icmpx_code
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
doc/data-types.txt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/doc/data-types.txt b/doc/data-types.txt
index 961fc62..e5ee91a 100644
--- a/doc/data-types.txt
+++ b/doc/data-types.txt
@@ -270,7 +270,7 @@ ICMPV6 TYPE TYPE
|==================
|Name | Keyword | Size | Base type
|ICMPv6 Type |
-icmpx_code |
+icmpv6_type |
8 bit |
integer
|===================
@@ -364,7 +364,7 @@ ICMPVX CODE TYPE
|==================
|Name | Keyword | Size | Base type
|ICMPvX Code |
-icmpv6_type |
+icmpx_code |
8 bit |
integer
|===================

View File

@ -0,0 +1,228 @@
From aaaf1ed4ab6ecb027ea58f49812c05fed9651cba Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:41 +0200
Subject: [PATCH] evaluate: add missing range checks for dup,fwd and payload
statements
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 4121175cc243a15bdb8c226a335f67cedd98680e
commit 4121175cc243a15bdb8c226a335f67cedd98680e
Author: Florian Westphal <fw@strlen.de>
Date: Thu Jan 11 18:14:16 2024 +0100
evaluate: add missing range checks for dup,fwd and payload statements
Else we assert with:
BUG: unknown expression type range
nft: src/netlink_linearize.c:912: netlink_gen_expr: Assertion `0' failed.
While at it, condense meta and exthdr to reuse the same helper.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 88 +++++++++++--------
.../testcases/bogons/nft-f/dup_fwd_ranges | 14 +++
.../nft-f/unknown_expr_type_range_assert | 8 +-
3 files changed, 69 insertions(+), 41 deletions(-)
create mode 100644 tests/shell/testcases/bogons/nft-f/dup_fwd_ranges
diff --git a/src/evaluate.c b/src/evaluate.c
index c655e3a..06ad554 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -74,6 +74,33 @@ static int __fmtstring(3, 4) set_error(struct eval_ctx *ctx,
return -1;
}
+static const char *stmt_name(const struct stmt *stmt)
+{
+ switch (stmt->ops->type) {
+ case STMT_NAT:
+ switch (stmt->nat.type) {
+ case NFT_NAT_SNAT:
+ return "snat";
+ case NFT_NAT_DNAT:
+ return "dnat";
+ case NFT_NAT_REDIR:
+ return "redirect";
+ case NFT_NAT_MASQ:
+ return "masquerade";
+ }
+ break;
+ default:
+ break;
+ }
+
+ return stmt->ops->name;
+}
+
+static int stmt_error_range(struct eval_ctx *ctx, const struct stmt *stmt, const struct expr *e)
+{
+ return expr_error(ctx->msgs, e, "%s: range argument not supported", stmt_name(stmt));
+}
+
static void key_fix_dtype_byteorder(struct expr *key)
{
const struct datatype *dtype = key->dtype;
@@ -2980,13 +3007,8 @@ static int stmt_evaluate_exthdr(struct eval_ctx *ctx, struct stmt *stmt)
if (ret < 0)
return ret;
- switch (stmt->exthdr.val->etype) {
- case EXPR_RANGE:
- return expr_error(ctx->msgs, stmt->exthdr.val,
- "cannot be a range");
- default:
- break;
- }
+ if (stmt->exthdr.val->etype == EXPR_RANGE)
+ return stmt_error_range(ctx, stmt, stmt->exthdr.val);
return 0;
}
@@ -3019,6 +3041,9 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
payload->byteorder) < 0)
return -1;
+ if (stmt->payload.val->etype == EXPR_RANGE)
+ return stmt_error_range(ctx, stmt, stmt->payload.val);
+
need_csum = stmt_evaluate_payload_need_csum(payload);
if (!payload_needs_adjustment(payload)) {
@@ -3178,15 +3203,8 @@ static int stmt_evaluate_meta(struct eval_ctx *ctx, struct stmt *stmt)
if (ret < 0)
return ret;
- switch (stmt->meta.expr->etype) {
- case EXPR_RANGE:
- ret = expr_error(ctx->msgs, stmt->meta.expr,
- "Meta expression cannot be a range");
- break;
- default:
- break;
-
- }
+ if (stmt->meta.expr->etype == EXPR_RANGE)
+ return stmt_error_range(ctx, stmt, stmt->meta.expr);
return ret;
}
@@ -3209,6 +3227,9 @@ static int stmt_evaluate_ct(struct eval_ctx *ctx, struct stmt *stmt)
return stmt_error(ctx, stmt,
"ct secmark must not be set to constant value");
+ if (stmt->ct.expr->etype == EXPR_RANGE)
+ return stmt_error_range(ctx, stmt, stmt->ct.expr);
+
return 0;
}
@@ -3719,28 +3740,6 @@ static int nat_evaluate_transport(struct eval_ctx *ctx, struct stmt *stmt,
return 0;
}
-static const char *stmt_name(const struct stmt *stmt)
-{
- switch (stmt->ops->type) {
- case STMT_NAT:
- switch (stmt->nat.type) {
- case NFT_NAT_SNAT:
- return "snat";
- case NFT_NAT_DNAT:
- return "dnat";
- case NFT_NAT_REDIR:
- return "redirect";
- case NFT_NAT_MASQ:
- return "masquerade";
- }
- break;
- default:
- break;
- }
-
- return stmt->ops->name;
-}
-
static int stmt_evaluate_l3proto(struct eval_ctx *ctx,
struct stmt *stmt, uint8_t family)
{
@@ -4132,6 +4131,9 @@ static int stmt_evaluate_dup(struct eval_ctx *ctx, struct stmt *stmt)
&stmt->dup.dev);
if (err < 0)
return err;
+
+ if (stmt->dup.dev->etype == EXPR_RANGE)
+ return stmt_error_range(ctx, stmt, stmt->dup.dev);
}
break;
case NFPROTO_NETDEV:
@@ -4150,6 +4152,10 @@ static int stmt_evaluate_dup(struct eval_ctx *ctx, struct stmt *stmt)
default:
return stmt_error(ctx, stmt, "unsupported family");
}
+
+ if (stmt->dup.to->etype == EXPR_RANGE)
+ return stmt_error_range(ctx, stmt, stmt->dup.to);
+
return 0;
}
@@ -4171,6 +4177,9 @@ static int stmt_evaluate_fwd(struct eval_ctx *ctx, struct stmt *stmt)
if (err < 0)
return err;
+ if (stmt->fwd.dev->etype == EXPR_RANGE)
+ return stmt_error_range(ctx, stmt, stmt->fwd.dev);
+
if (stmt->fwd.addr != NULL) {
switch (stmt->fwd.family) {
case NFPROTO_IPV4:
@@ -4189,6 +4198,9 @@ static int stmt_evaluate_fwd(struct eval_ctx *ctx, struct stmt *stmt)
&stmt->fwd.addr);
if (err < 0)
return err;
+
+ if (stmt->fwd.addr->etype == EXPR_RANGE)
+ return stmt_error_range(ctx, stmt, stmt->fwd.addr);
}
break;
default:
diff --git a/tests/shell/testcases/bogons/nft-f/dup_fwd_ranges b/tests/shell/testcases/bogons/nft-f/dup_fwd_ranges
new file mode 100644
index 0000000..efaff9e
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/dup_fwd_ranges
@@ -0,0 +1,14 @@
+define dev = "1"-"2"
+
+table netdev t {
+ chain c {
+ fwd to 1-2
+ dup to 1-2
+ }
+}
+
+table ip t {
+ chain c {
+ dup to 1-2 device $dev
+ }
+}
diff --git a/tests/shell/testcases/bogons/nft-f/unknown_expr_type_range_assert b/tests/shell/testcases/bogons/nft-f/unknown_expr_type_range_assert
index 234dd62..e620673 100644
--- a/tests/shell/testcases/bogons/nft-f/unknown_expr_type_range_assert
+++ b/tests/shell/testcases/bogons/nft-f/unknown_expr_type_range_assert
@@ -1,5 +1,7 @@
table ip x {
- chain k {
- meta mark set 0x001-3434
- }
+ chain k {
+ meta mark set 0x001-3434
+ ct mark set 0x001-3434
+ tcp dport set 1-3
+ }
}

View File

@ -0,0 +1,57 @@
From 475315fb01bac6b53833c727add0cf1c427a75b7 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:42 +0200
Subject: [PATCH] evaluate: skip anonymous set optimization for concatenations
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 6bc6673fc88c8a3e3dd5504b2d24a6d6bc2f8427
commit 6bc6673fc88c8a3e3dd5504b2d24a6d6bc2f8427
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Wed Jan 10 18:18:50 2024 +0100
evaluate: skip anonymous set optimization for concatenations
Concatenation is only supported with sets. Moreover, stripping of the
set leads to broken ruleset listing, therefore, skip this optimization
for the concatenations.
Fixes: fa17b17ea74a ("evaluate: revisit anonymous set with single element optimization")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 20 +++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/src/evaluate.c b/src/evaluate.c
index 06ad554..8f98b48 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -2507,15 +2507,17 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
return expr_binary_error(ctx->msgs, right, left,
"Cannot be used with right hand side constant value");
- switch (rel->op) {
- case OP_EQ:
- case OP_IMPLICIT:
- case OP_NEQ:
- if (right->etype == EXPR_SET && right->size == 1)
- optimize_singleton_set(rel, &right);
- break;
- default:
- break;
+ if (left->etype != EXPR_CONCAT) {
+ switch (rel->op) {
+ case OP_EQ:
+ case OP_IMPLICIT:
+ case OP_NEQ:
+ if (right->etype == EXPR_SET && right->size == 1)
+ optimize_singleton_set(rel, &right);
+ break;
+ default:
+ break;
+ }
}
switch (rel->op) {

View File

@ -0,0 +1,45 @@
From bd77070f6c17a0dba479ed5a45b940d8605a91f3 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:42 +0200
Subject: [PATCH] evaluate: do not fetch next expression on runaway number of
concatenation components
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 955bb6d31c90453e43043346c917646ddc4e5c4e
commit 955bb6d31c90453e43043346c917646ddc4e5c4e
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Wed Jan 10 18:20:47 2024 +0100
evaluate: do not fetch next expression on runaway number of concatenation components
If this is the last expression, then the runaway flag is set on and
evaluation bails in the next iteration, do not fetch next list element
which refers to the list head.
I found this by code inspection, I could not trigger any crash with this
one.
Fixes: ae1d54d1343f ("evaluate: do not crash on runaway number of concatenation components")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/evaluate.c b/src/evaluate.c
index 8f98b48..079f340 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1591,8 +1591,8 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
if (key && expressions) {
if (list_is_last(&key->list, expressions))
runaway = true;
-
- key = list_next_entry(key, list);
+ else
+ key = list_next_entry(key, list);
}
ctx->inner_desc = NULL;

View File

@ -0,0 +1,62 @@
From 9f7891ddd76afb0472cad8cec8e7cfed12f55d99 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:42 +0200
Subject: [PATCH] evaluate: error out when store needs more than one 128bit
register of align fixup
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 8a66de2a15943b2fbf960967cdbcbd0a148cb114
commit 8a66de2a15943b2fbf960967cdbcbd0a148cb114
Author: Florian Westphal <fw@strlen.de>
Date: Mon Jan 15 14:11:17 2024 +0100
evaluate: error out when store needs more than one 128bit register of align fixup
Else this gives:
nft: evaluate.c:2983: stmt_evaluate_payload: Assertion `sizeof(data) * BITS_PER_BYTE >= masklen' failed.
For loads, this is already prevented via expr_evaluate_bits() which has:
if (masklen > NFT_REG_SIZE * BITS_PER_BYTE)
return expr_error(ctx->msgs, expr, "mask length %u exceeds allowed maximum of %u\n",
masklen, NFT_REG_SIZE * BITS_PER_BYTE);
But for the store path this isn't called.
The reproducer asks to store a 128 bit integer at bit offset 1, i.e.
17 bytes would need to be munged, but we can only handle up to 16 bytes
(one pseudo-register).
Fixes: 78936d50f306 ("evaluate: add support to set IPv6 non-byte header fields")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 5 +++++
.../testcases/bogons/nft-f/payload_expr_unaligned_store | 1 +
2 files changed, 6 insertions(+)
create mode 100644 tests/shell/testcases/bogons/nft-f/payload_expr_unaligned_store
diff --git a/src/evaluate.c b/src/evaluate.c
index 079f340..541299e 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -3065,6 +3065,11 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
payload_byte_size = div_round_up(payload->len + extra_len,
BITS_PER_BYTE);
+ if (payload_byte_size > sizeof(data))
+ return expr_error(ctx->msgs, stmt->payload.expr,
+ "uneven load cannot span more than %u bytes, got %u",
+ sizeof(data), payload_byte_size);
+
if (need_csum && payload_byte_size & 1) {
payload_byte_size++;
diff --git a/tests/shell/testcases/bogons/nft-f/payload_expr_unaligned_store b/tests/shell/testcases/bogons/nft-f/payload_expr_unaligned_store
new file mode 100644
index 0000000..c1358df
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/payload_expr_unaligned_store
@@ -0,0 +1 @@
+add rule f i @th,1,128 set 1

View File

@ -0,0 +1,64 @@
From a7072eb98d9549db87c4d76eb6ca3556728277b5 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:42 +0200
Subject: [PATCH] rule: fix sym refcount assertion
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit b73298405cda74b3a87a1818bb92f53298d34170
commit b73298405cda74b3a87a1818bb92f53298d34170
Author: Florian Westphal <fw@strlen.de>
Date: Mon Jan 15 14:27:15 2024 +0100
rule: fix sym refcount assertion
Scope release must happen last.
afl provided a reproducer where policy is a define, because
scope is released too early we get:
nft: src/rule.c:559: scope_release: Assertion `sym->refcnt == 1' failed.
... because chain->policy is EXPR_SYMBOL.
Fixes: 627c451b2351 ("src: allow variables in the chain priority specification")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/rule.c | 6 +++++-
tests/shell/testcases/bogons/nft-f/define_policy_assert | 3 +++
2 files changed, 8 insertions(+), 1 deletion(-)
create mode 100644 tests/shell/testcases/bogons/nft-f/define_policy_assert
diff --git a/src/rule.c b/src/rule.c
index 633fae7..e2d972c 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -729,7 +729,6 @@ void chain_free(struct chain *chain)
list_for_each_entry_safe(rule, next, &chain->rules, list)
rule_free(rule);
handle_free(&chain->handle);
- scope_release(&chain->scope);
free_const(chain->type.str);
expr_free(chain->dev_expr);
for (i = 0; i < chain->dev_array_len; i++)
@@ -738,6 +737,11 @@ void chain_free(struct chain *chain)
expr_free(chain->priority.expr);
expr_free(chain->policy);
free_const(chain->comment);
+
+ /* MUST be released after all expressions, they could
+ * hold refcounts.
+ */
+ scope_release(&chain->scope);
free(chain);
}
diff --git a/tests/shell/testcases/bogons/nft-f/define_policy_assert b/tests/shell/testcases/bogons/nft-f/define_policy_assert
new file mode 100644
index 0000000..f1e58b5
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/define_policy_assert
@@ -0,0 +1,3 @@
+chain y x { priority filter
+define p = foo
+policy $p

View File

@ -0,0 +1,85 @@
From 6242822b5f19721538fb9296a048ea84acee5092 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:42 +0200
Subject: [PATCH] json: Support sets' auto-merge option
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit a4034c66b03e4d526fbad78da9cd38da9f1f43a5
commit a4034c66b03e4d526fbad78da9cd38da9f1f43a5
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Jan 31 17:30:24 2024 +0100
json: Support sets' auto-merge option
If enabled, list the option as additional attribute with boolean value.
Fixes: e70354f53e9f6 ("libnftables: Implement JSON output support")
Closes: https://bugzilla.netfilter.org/show_bug.cgi?id=1734
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
doc/libnftables-json.adoc | 8 ++++++--
src/json.c | 2 ++
src/parser_json.c | 1 +
3 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/doc/libnftables-json.adoc b/doc/libnftables-json.adoc
index c5e5f23..3bbb6cd 100644
--- a/doc/libnftables-json.adoc
+++ b/doc/libnftables-json.adoc
@@ -321,7 +321,8 @@ ____
"elem":* 'SET_ELEMENTS'*,
"timeout":* 'NUMBER'*,
"gc-interval":* 'NUMBER'*,
- "size":* 'NUMBER'
+ "size":* 'NUMBER'*,
+ "auto-merge":* 'BOOLEAN'
*}}*
*{ "map": {
@@ -336,7 +337,8 @@ ____
"elem":* 'SET_ELEMENTS'*,
"timeout":* 'NUMBER'*,
"gc-interval":* 'NUMBER'*,
- "size":* 'NUMBER'
+ "size":* 'NUMBER'*,
+ "auto-merge":* 'BOOLEAN'
*}}*
'SET_TYPE' := 'STRING' | *[* 'SET_TYPE_LIST' *]*
@@ -375,6 +377,8 @@ that they translate a unique key to a value.
Garbage collector interval in seconds.
*size*::
Maximum number of elements supported.
+*auto-merge*::
+ Automatic merging of adjacent/overlapping set elements in interval sets.
==== TYPE
The set type might be a string, such as *"ipv4_addr"* or an array
diff --git a/src/json.c b/src/json.c
index 6809cd5..b3e1e4e 100644
--- a/src/json.c
+++ b/src/json.c
@@ -194,6 +194,8 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
tmp = json_pack("i", set->gc_int / 1000);
json_object_set_new(root, "gc-interval", tmp);
}
+ if (set->automerge)
+ json_object_set_new(root, "auto-merge", json_true());
if (!nft_output_terse(octx) && set->init && set->init->size > 0) {
json_t *array = json_array();
diff --git a/src/parser_json.c b/src/parser_json.c
index 2acc248..9537c9d 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -3407,6 +3407,7 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root,
if (!json_unpack(root, "{s:i}", "gc-interval", &set->gc_int))
set->gc_int *= 1000;
json_unpack(root, "{s:i}", "size", &set->desc.size);
+ json_unpack(root, "{s:b}", "auto-merge", &set->automerge);
if (!json_unpack(root, "{s:o}", "stmt", &stmt_json))
json_parse_set_stmt_list(ctx, &set->stmt_list, stmt_json);

View File

@ -0,0 +1,181 @@
From 9a0ab03e46bcfadf423d284bb90e11c8e6f04fe8 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:42 +0200
Subject: [PATCH] evaluate: don't assert on net/transport header conflict
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 3a734d60813193974a4a0e8ed0af3349f8857ec9
commit 3a734d60813193974a4a0e8ed0af3349f8857ec9
Author: Florian Westphal <fw@strlen.de>
Date: Tue Jan 16 15:21:00 2024 +0100
evaluate: don't assert on net/transport header conflict
before:
nft: evaluate.c:467: conflict_resolution_gen_dependency: Assertion `expr->payload.base == PROTO_BASE_LL_HDR' failed.
Aborted (core dumped)
conflict_resolution_gen_dependency() can only handle linklayer
conflicts, hence the assert.
Rename it accordingly. Also rename resolve_protocol_conflict, it doesn't
do anything for != PROTO_BASE_LL_HDR and extend the assertion to that
function too.
Callers now enforce PROTO_BASE_LL_HDR prerequisite.
after:
Error: conflicting transport layer protocols specified: comp vs. udp
ip6 nexthdr comp udp dport 4789
^^^^^^^^^
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 69 +++++++++----------
...solution_gen_dependency_base_ll_hdr_assert | 5 ++
2 files changed, 38 insertions(+), 36 deletions(-)
create mode 100644 tests/shell/testcases/bogons/nft-f/evaluate_conflict_resolution_gen_dependency_base_ll_hdr_assert
diff --git a/src/evaluate.c b/src/evaluate.c
index 541299e..98f97bd 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -494,9 +494,9 @@ int stmt_dependency_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
}
static int
-conflict_resolution_gen_dependency(struct eval_ctx *ctx, int protocol,
- const struct expr *expr,
- struct stmt **res)
+ll_conflict_resolution_gen_dependency(struct eval_ctx *ctx, int protocol,
+ const struct expr *expr,
+ struct stmt **res)
{
enum proto_bases base = expr->payload.base;
const struct proto_hdr_template *tmpl;
@@ -750,56 +750,52 @@ static bool proto_is_dummy(const struct proto_desc *desc)
return desc == &proto_inet || desc == &proto_netdev;
}
-static int resolve_protocol_conflict(struct eval_ctx *ctx,
- const struct proto_desc *desc,
- struct expr *payload)
+static int resolve_ll_protocol_conflict(struct eval_ctx *ctx,
+ const struct proto_desc *desc,
+ struct expr *payload)
{
enum proto_bases base = payload->payload.base;
struct stmt *nstmt = NULL;
struct proto_ctx *pctx;
+ unsigned int i;
int link, err;
+ assert(base == PROTO_BASE_LL_HDR);
+
pctx = eval_proto_ctx(ctx);
- if (payload->payload.base == PROTO_BASE_LL_HDR) {
- if (proto_is_dummy(desc)) {
- if (ctx->inner_desc) {
- proto_ctx_update(pctx, PROTO_BASE_LL_HDR, &payload->location, &proto_eth);
- } else {
- err = meta_iiftype_gen_dependency(ctx, payload, &nstmt);
- if (err < 0)
- return err;
-
- desc = payload->payload.desc;
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
- }
+ if (proto_is_dummy(desc)) {
+ if (ctx->inner_desc) {
+ proto_ctx_update(pctx, PROTO_BASE_LL_HDR, &payload->location, &proto_eth);
} else {
- unsigned int i;
+ err = meta_iiftype_gen_dependency(ctx, payload, &nstmt);
+ if (err < 0)
+ return err;
- /* payload desc stored in the L2 header stack? No conflict. */
- for (i = 0; i < pctx->stacked_ll_count; i++) {
- if (pctx->stacked_ll[i] == payload->payload.desc)
- return 0;
- }
+ desc = payload->payload.desc;
+ rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+ }
+ } else {
+ unsigned int i;
+
+ /* payload desc stored in the L2 header stack? No conflict. */
+ for (i = 0; i < pctx->stacked_ll_count; i++) {
+ if (pctx->stacked_ll[i] == payload->payload.desc)
+ return 0;
}
}
- assert(base <= PROTO_BASE_MAX);
/* This payload and the existing context don't match, conflict. */
if (pctx->protocol[base + 1].desc != NULL)
return 1;
link = proto_find_num(desc, payload->payload.desc);
if (link < 0 ||
- conflict_resolution_gen_dependency(ctx, link, payload, &nstmt) < 0)
+ ll_conflict_resolution_gen_dependency(ctx, link, payload, &nstmt) < 0)
return 1;
- if (base == PROTO_BASE_LL_HDR) {
- unsigned int i;
-
- for (i = 0; i < pctx->stacked_ll_count; i++)
- payload->payload.offset += pctx->stacked_ll[i]->length;
- }
+ for (i = 0; i < pctx->stacked_ll_count; i++)
+ payload->payload.offset += pctx->stacked_ll[i]->length;
rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
@@ -841,7 +837,7 @@ static int __expr_evaluate_payload(struct eval_ctx *ctx, struct expr *expr)
link = proto_find_num(desc, payload->payload.desc);
if (link < 0 ||
- conflict_resolution_gen_dependency(ctx, link, payload, &nstmt) < 0)
+ ll_conflict_resolution_gen_dependency(ctx, link, payload, &nstmt) < 0)
return expr_error(ctx->msgs, payload,
"conflicting protocols specified: %s vs. %s",
desc->name,
@@ -898,8 +894,8 @@ check_icmp:
/* If we already have context and this payload is on the same
* base, try to resolve the protocol conflict.
*/
- if (payload->payload.base == desc->base) {
- err = resolve_protocol_conflict(ctx, desc, payload);
+ if (base == PROTO_BASE_LL_HDR) {
+ err = resolve_ll_protocol_conflict(ctx, desc, payload);
if (err <= 0)
return err;
@@ -908,7 +904,8 @@ check_icmp:
return 0;
}
return expr_error(ctx->msgs, payload,
- "conflicting protocols specified: %s vs. %s",
+ "conflicting %s protocols specified: %s vs. %s",
+ proto_base_names[base],
pctx->protocol[base].desc->name,
payload->payload.desc->name);
}
diff --git a/tests/shell/testcases/bogons/nft-f/evaluate_conflict_resolution_gen_dependency_base_ll_hdr_assert b/tests/shell/testcases/bogons/nft-f/evaluate_conflict_resolution_gen_dependency_base_ll_hdr_assert
new file mode 100644
index 0000000..43d72c4
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/evaluate_conflict_resolution_gen_dependency_base_ll_hdr_assert
@@ -0,0 +1,5 @@
+table ip6 t {
+ chain c {
+ ip6 nexthdr comp udp dport 4789
+ }
+}

View File

@ -0,0 +1,48 @@
From e53d46d90f0b73be34c42abfd759b16306da03ac Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:42 +0200
Subject: [PATCH] evaluate: fix check for unknown in cmd_op_to_name
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 9f76bb63c0c706ea5c0d55931ee690ca5dccaf16
commit 9f76bb63c0c706ea5c0d55931ee690ca5dccaf16
Author: 谢致邦 (XIE Zhibang) <Yeking@Red54.com>
Date: Wed Feb 7 15:10:20 2024 +0000
evaluate: fix check for unknown in cmd_op_to_name
Example:
nft --debug=all destroy table ip missingtable
Before:
Evaluate unknown
After:
Evaluate destroy
Fixes: e1dfd5cc4c46 ("src: add support to command "destroy"")
Signed-off-by: 谢致邦 (XIE Zhibang) <Yeking@Red54.com>
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/evaluate.c b/src/evaluate.c
index 98f97bd..20950fd 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -5886,7 +5886,7 @@ static const char * const cmd_op_name[] = {
static const char *cmd_op_to_name(enum cmd_ops op)
{
- if (op > CMD_DESCRIBE)
+ if (op >= array_size(cmd_op_name))
return "unknown";
return cmd_op_name[op];

View File

@ -0,0 +1,49 @@
From 5bc0de5ef742c4746ca45a8940998fcfeef29b6f Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:42 +0200
Subject: [PATCH] cache: Optimize caching for 'list tables' command
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 674eb7fa2895813b25f6fbfcc9417fc0788fade1
commit 674eb7fa2895813b25f6fbfcc9417fc0788fade1
Author: Phil Sutter <phil@nwl.cc>
Date: Tue Feb 6 19:26:57 2024 +0100
cache: Optimize caching for 'list tables' command
No point in fetching anything other than existing tables from kernel:
'list tables' merely prints existing table names, no contents.
Also populate filter's family field to reduce overhead when listing
tables in one family with many tables in another one. It works without
further adjustments because nftnl_nlmsg_build_hdr() will use the value
for nfgen_family.
Reported-by: anton.khazan@gmail.com
Link: https://bugzilla.netfilter.org/show_bug.cgi?id=1735
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/cache.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/cache.c b/src/cache.c
index a3cd795..c000e32 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -203,8 +203,12 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
{
switch (cmd->obj) {
case CMD_OBJ_TABLE:
- if (filter && cmd->handle.table.name) {
+ if (filter)
filter->list.family = cmd->handle.family;
+ if (!cmd->handle.table.name) {
+ flags |= NFT_CACHE_TABLE;
+ break;
+ } else if (filter) {
filter->list.table = cmd->handle.table.name;
}
flags |= NFT_CACHE_FULL;

View File

@ -0,0 +1,209 @@
From ff07e944ab10459c7fa9b6beac5c622e48eaa2f5 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:42 +0200
Subject: [PATCH] evaluate: skip byteorder conversion for selector smaller than
2 bytes
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 9fe58952c45a1643dc7df1a0b1f5d88e8ae1a978
commit 9fe58952c45a1643dc7df1a0b1f5d88e8ae1a978
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Wed Feb 7 23:53:32 2024 +0100
evaluate: skip byteorder conversion for selector smaller than 2 bytes
Add unary expression to trigger byteorder conversion for host byteorder
selectors only if selectors length is larger or equal than 2 bytes.
# cat test.nft
table ip x {
set test {
type ipv4_addr . ether_addr . inet_proto
flags interval
}
chain y {
ip saddr . ether saddr . meta l4proto @test counter
}
}
# nft -f test.nft
ip x y
[ meta load iiftype => reg 1 ]
[ cmp eq reg 1 0x00000001 ]
[ payload load 4b @ network header + 12 => reg 1 ]
[ payload load 6b @ link header + 6 => reg 9 ]
[ meta load l4proto => reg 11 ]
[ byteorder reg 11 = hton(reg 11, 2, 1) ] <--- should not be here
[ lookup reg 1 set test ]
[ counter pkts 0 bytes 0 ]
Fixes: 1017d323cafa ("src: support for selectors with different byteorder with interval concatenations")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 12 ++++++----
tests/py/inet/meta.t | 1 +
tests/py/inet/meta.t.json | 41 ++++++++++++++++++++++++++++++++
tests/py/inet/meta.t.json.output | 41 ++++++++++++++++++++++++++++++++
tests/py/inet/meta.t.payload | 14 +++++++++++
5 files changed, 104 insertions(+), 5 deletions(-)
diff --git a/src/evaluate.c b/src/evaluate.c
index 20950fd..e64bc91 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -194,12 +194,14 @@ static int byteorder_conversion(struct eval_ctx *ctx, struct expr **expr,
assert(basetype == TYPE_INTEGER);
- op = byteorder_conversion_op(i, byteorder);
- unary = unary_expr_alloc(&i->location, op, i);
- if (expr_evaluate(ctx, &unary) < 0)
- return -1;
+ if (div_round_up(i->len, BITS_PER_BYTE) >= 2) {
+ op = byteorder_conversion_op(i, byteorder);
+ unary = unary_expr_alloc(&i->location, op, i);
+ if (expr_evaluate(ctx, &unary) < 0)
+ return -1;
- list_replace(&i->list, &unary->list);
+ list_replace(&i->list, &unary->list);
+ }
}
return 0;
diff --git a/tests/py/inet/meta.t b/tests/py/inet/meta.t
index 5c062b3..7d2515c 100644
--- a/tests/py/inet/meta.t
+++ b/tests/py/inet/meta.t
@@ -25,6 +25,7 @@ meta mark set ct mark >> 8;ok
meta mark . tcp dport { 0x0000000a-0x00000014 . 80-90, 0x00100000-0x00100123 . 100-120 };ok
ip saddr . meta mark { 1.2.3.4 . 0x00000100 , 1.2.3.6-1.2.3.8 . 0x00000200-0x00000300 };ok
ip saddr . meta mark { 1.2.3.4 . 0x00000100 , 5.6.7.8 . 0x00000200 };ok
+ip saddr . ether saddr . meta l4proto { 1.2.3.4 . aa:bb:cc:dd:ee:ff . 6 };ok
meta mark set ip dscp;ok
meta mark set ip dscp | 0x40;ok
diff --git a/tests/py/inet/meta.t.json b/tests/py/inet/meta.t.json
index 3ba0fd1..0fee165 100644
--- a/tests/py/inet/meta.t.json
+++ b/tests/py/inet/meta.t.json
@@ -526,3 +526,44 @@
}
]
+# ip saddr . ether saddr . meta l4proto { 1.2.3.4 . aa:bb:cc:dd:ee:ff . 6 }
+[
+ {
+ "match": {
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ },
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ether"
+ }
+ },
+ {
+ "meta": {
+ "key": "l4proto"
+ }
+ }
+ ]
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ {
+ "concat": [
+ "1.2.3.4",
+ "aa:bb:cc:dd:ee:ff",
+ "tcp"
+ ]
+ }
+ ]
+ }
+ }
+ }
+]
+
diff --git a/tests/py/inet/meta.t.json.output b/tests/py/inet/meta.t.json.output
index 3e7dd21..8697d5a 100644
--- a/tests/py/inet/meta.t.json.output
+++ b/tests/py/inet/meta.t.json.output
@@ -51,3 +51,44 @@
}
]
+# ip saddr . ether saddr . meta l4proto { 1.2.3.4 . aa:bb:cc:dd:ee:ff . 6 }
+[
+ {
+ "match": {
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ },
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ether"
+ }
+ },
+ {
+ "meta": {
+ "key": "l4proto"
+ }
+ }
+ ]
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ {
+ "concat": [
+ "1.2.3.4",
+ "aa:bb:cc:dd:ee:ff",
+ 6
+ ]
+ }
+ ]
+ }
+ }
+ }
+]
+
diff --git a/tests/py/inet/meta.t.payload b/tests/py/inet/meta.t.payload
index c53b507..7184fa0 100644
--- a/tests/py/inet/meta.t.payload
+++ b/tests/py/inet/meta.t.payload
@@ -173,3 +173,17 @@ inet test-inet input
[ bitwise reg 1 = ( reg 1 & 0xffffffbf ) ^ 0x00000040 ]
[ meta set mark with reg 1 ]
+# ip saddr . ether saddr . meta l4proto { 1.2.3.4 . aa:bb:cc:dd:ee:ff . 6 }
+__set%d test-inet 3 size 1
+__set%d test-inet 0
+ element 04030201 ddccbbaa 0000ffee 00000006 : 0 [end]
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x00000002 ]
+ [ meta load iiftype => reg 1 ]
+ [ cmp eq reg 1 0x00000001 ]
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ payload load 6b @ link header + 6 => reg 9 ]
+ [ meta load l4proto => reg 11 ]
+ [ lookup reg 1 set __set%d ]
+

View File

@ -0,0 +1,132 @@
From 633d5e7cf14961687216422323b6476775e0086e Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:42 +0200
Subject: [PATCH] netlink_delinearize: move concat and value postprocessing to
helpers
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 7ef933d16fd1b14afb276ab26f4114a2c9d6a3ea
commit 7ef933d16fd1b14afb276ab26f4114a2c9d6a3ea
Author: Florian Westphal <fw@strlen.de>
Date: Fri Jan 19 13:47:08 2024 +0100
netlink_delinearize: move concat and value postprocessing to helpers
No functional changes intended.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/netlink_delinearize.c | 82 ++++++++++++++++++++++-----------------
1 file changed, 47 insertions(+), 35 deletions(-)
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index e214510..27630a8 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -2720,6 +2720,50 @@ static struct expr *expr_postprocess_string(struct expr *expr)
return out;
}
+static void expr_postprocess_value(struct rule_pp_ctx *ctx, struct expr **exprp)
+{
+ struct expr *expr = *exprp;
+
+ // FIXME
+ if (expr->byteorder == BYTEORDER_HOST_ENDIAN)
+ mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE);
+
+ if (expr_basetype(expr)->type == TYPE_STRING)
+ *exprp = expr_postprocess_string(expr);
+
+ expr = *exprp;
+ if (expr->dtype->basetype != NULL &&
+ expr->dtype->basetype->type == TYPE_BITMASK)
+ *exprp = bitmask_expr_to_binops(expr);
+}
+
+static void expr_postprocess_concat(struct rule_pp_ctx *ctx, struct expr **exprp)
+{
+ struct expr *i, *n, *expr = *exprp;
+ unsigned int type = expr->dtype->type, ntype = 0;
+ int off = expr->dtype->subtypes;
+ const struct datatype *dtype;
+ LIST_HEAD(tmp);
+
+ assert(expr->etype == EXPR_CONCAT);
+
+ ctx->flags |= RULE_PP_IN_CONCATENATION;
+ list_for_each_entry_safe(i, n, &expr->expressions, list) {
+ if (type) {
+ dtype = concat_subtype_lookup(type, --off);
+ expr_set_type(i, dtype, dtype->byteorder);
+ }
+ list_del(&i->list);
+ expr_postprocess(ctx, &i);
+ list_add_tail(&i->list, &tmp);
+
+ ntype = concat_subtype_add(ntype, i->dtype->type);
+ }
+ ctx->flags &= ~RULE_PP_IN_CONCATENATION;
+ list_splice(&tmp, &expr->expressions);
+ __datatype_set(expr, concat_type_alloc(ntype));
+}
+
static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
{
struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
@@ -2746,30 +2790,9 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
list_for_each_entry(i, &expr->expressions, list)
expr_postprocess(ctx, &i);
break;
- case EXPR_CONCAT: {
- unsigned int type = expr->dtype->type, ntype = 0;
- int off = expr->dtype->subtypes;
- const struct datatype *dtype;
- LIST_HEAD(tmp);
- struct expr *n;
-
- ctx->flags |= RULE_PP_IN_CONCATENATION;
- list_for_each_entry_safe(i, n, &expr->expressions, list) {
- if (type) {
- dtype = concat_subtype_lookup(type, --off);
- expr_set_type(i, dtype, dtype->byteorder);
- }
- list_del(&i->list);
- expr_postprocess(ctx, &i);
- list_add_tail(&i->list, &tmp);
-
- ntype = concat_subtype_add(ntype, i->dtype->type);
- }
- ctx->flags &= ~RULE_PP_IN_CONCATENATION;
- list_splice(&tmp, &expr->expressions);
- __datatype_set(expr, concat_type_alloc(ntype));
+ case EXPR_CONCAT:
+ expr_postprocess_concat(ctx, exprp);
break;
- }
case EXPR_UNARY:
expr_postprocess(ctx, &expr->arg);
expr_set_type(expr, expr->arg->dtype, !expr->arg->byteorder);
@@ -2882,18 +2905,7 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
payload_dependency_kill(&dl->pdctx, expr, dl->pctx.family);
break;
case EXPR_VALUE:
- // FIXME
- if (expr->byteorder == BYTEORDER_HOST_ENDIAN)
- mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE);
-
- if (expr_basetype(expr)->type == TYPE_STRING)
- *exprp = expr_postprocess_string(expr);
-
- expr = *exprp;
- if (expr->dtype->basetype != NULL &&
- expr->dtype->basetype->type == TYPE_BITMASK)
- *exprp = bitmask_expr_to_binops(expr);
-
+ expr_postprocess_value(ctx, exprp);
break;
case EXPR_RANGE:
expr_postprocess(ctx, &expr->left);

View File

@ -0,0 +1,50 @@
From 7390323f695abb24d5886f671e4faccd8d44f1eb Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:42 +0200
Subject: [PATCH] expression: missing line in describe command with invalid
expression
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 2b24dd29c5fa1c7e4cf44f0753752d25106273a0
commit 2b24dd29c5fa1c7e4cf44f0753752d25106273a0
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue Feb 13 17:09:20 2024 +0100
expression: missing line in describe command with invalid expression
Before:
duh@testbed:~# nft describe blah
symbol expression, datatype invalid (invalid)duh@testbed:#
After:
duh@testbed:~# nft describe blah
symbol expression, datatype invalid (invalid)
duh@testbed:#
Fixes: 48aca2de80a7 ("iptopt: fix crash with invalid field/type combo")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/expression.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/expression.c b/src/expression.c
index dde48b6..cb2573f 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -140,8 +140,10 @@ void expr_describe(const struct expr *expr, struct output_ctx *octx)
nft_print(octx, "%s expression, datatype %s (%s)",
expr_name(expr), dtype->name, dtype->desc);
- if (dtype == &invalid_type)
+ if (dtype == &invalid_type) {
+ nft_print(octx, "\n");
return;
+ }
}
if (dtype->basetype != NULL) {

View File

@ -0,0 +1,137 @@
From 20e6758f2cbf8c42e3c58ec10207b12c490c8fb9 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:42 +0200
Subject: [PATCH] evaluate: permit use of host-endian constant values in set
lookup keys
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit c0080feb0d034913409944d23873cce4bf9edf9e
commit c0080feb0d034913409944d23873cce4bf9edf9e
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Mon Feb 12 16:46:29 2024 +0100
evaluate: permit use of host-endian constant values in set lookup keys
AFL found following crash:
table ip filter {
map ipsec_in {
typeof ipsec in reqid . iif : verdict
flags interval
}
chain INPUT {
type filter hook input priority filter; policy drop;
ipsec in reqid . 100 @ipsec_in
}
}
Which yields:
nft: evaluate.c:1213: expr_evaluate_unary: Assertion `!expr_is_constant(arg)' failed.
All existing test cases with constant values use big endian values, but
"iif" expects host endian values.
As raw values were not supported before, concat byteorder conversion
doesn't handle constants.
Fix this:
1. Add constant handling so that the number is converted in-place,
without unary expression.
2. Add the inverse handling on delinearization for non-interval set
types.
When dissecting the concat data soup, watch for integer constants where
the datatype indicates host endian integer.
Last, extend an existing test case with the afl input to cover
in/output.
A new test case is added to test linearization, delinearization and
matching.
Based on original patch from Florian Westphal, patch subject and
description wrote by him.
Fixes: b422b07ab2f9 ("src: permit use of constant values in set lookup keys")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
include/netlink.h | 1 +
src/evaluate.c | 19 +++++++++++++------
src/netlink_delinearize.c | 5 ++++-
3 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/include/netlink.h b/include/netlink.h
index 6766d7e..2ce4b39 100644
--- a/include/netlink.h
+++ b/include/netlink.h
@@ -61,6 +61,7 @@ struct rule_pp_ctx {
struct dl_proto_ctx *dl;
struct stmt *stmt;
unsigned int flags;
+ struct set *set;
};
extern const struct input_descriptor indesc_netlink;
diff --git a/src/evaluate.c b/src/evaluate.c
index e64bc91..89656eb 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -194,13 +194,20 @@ static int byteorder_conversion(struct eval_ctx *ctx, struct expr **expr,
assert(basetype == TYPE_INTEGER);
- if (div_round_up(i->len, BITS_PER_BYTE) >= 2) {
- op = byteorder_conversion_op(i, byteorder);
- unary = unary_expr_alloc(&i->location, op, i);
- if (expr_evaluate(ctx, &unary) < 0)
- return -1;
+ switch (i->etype) {
+ case EXPR_VALUE:
+ if (i->byteorder == BYTEORDER_HOST_ENDIAN)
+ mpz_switch_byteorder(i->value, div_round_up(i->len, BITS_PER_BYTE));
+ break;
+ default:
+ if (div_round_up(i->len, BITS_PER_BYTE) >= 2) {
+ op = byteorder_conversion_op(i, byteorder);
+ unary = unary_expr_alloc(&i->location, op, i);
+ if (expr_evaluate(ctx, &unary) < 0)
+ return -1;
- list_replace(&i->list, &unary->list);
+ list_replace(&i->list, &unary->list);
+ }
}
}
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 27630a8..1d30a78 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -2722,10 +2722,11 @@ static struct expr *expr_postprocess_string(struct expr *expr)
static void expr_postprocess_value(struct rule_pp_ctx *ctx, struct expr **exprp)
{
+ bool interval = (ctx->set && ctx->set->flags & NFT_SET_INTERVAL);
struct expr *expr = *exprp;
// FIXME
- if (expr->byteorder == BYTEORDER_HOST_ENDIAN)
+ if (expr->byteorder == BYTEORDER_HOST_ENDIAN && !interval)
mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE);
if (expr_basetype(expr)->type == TYPE_STRING)
@@ -2869,7 +2870,9 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
datatype_set(expr->left, expr->right->dtype);
}
+ ctx->set = expr->right->set;
expr_postprocess(ctx, &expr->left);
+ ctx->set = NULL;
break;
default:
expr_postprocess(ctx, &expr->left);

View File

@ -0,0 +1,179 @@
From e5d1dce48ec1e1b4e0f0a4ec552ca6a5af207ddc Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:42 +0200
Subject: [PATCH] tests: shell: permit use of host-endian constant values in
set lookup keys
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 40afa4bb2c7c7c8d488a8d44830c6b72cd98c640
commit 40afa4bb2c7c7c8d488a8d44830c6b72cd98c640
Author: Florian Westphal <fw@strlen.de>
Date: Wed Feb 14 11:41:30 2024 +0100
tests: shell: permit use of host-endian constant values in set lookup keys
extend an existing test case with the afl input to cover in/output.
A new test case is added to test linearization, delinearization and
matching
Fixes: c0080feb0d03 ("evaluate: permit use of host-endian constant values in set lookup keys")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
.../packetpath/dumps/set_lookups.nft | 51 +++++++++++++++
tests/shell/testcases/packetpath/set_lookups | 64 +++++++++++++++++++
.../sets/dumps/typeof_sets_concat.nft | 11 ++++
3 files changed, 126 insertions(+)
create mode 100644 tests/shell/testcases/packetpath/dumps/set_lookups.nft
create mode 100755 tests/shell/testcases/packetpath/set_lookups
diff --git a/tests/shell/testcases/packetpath/dumps/set_lookups.nft b/tests/shell/testcases/packetpath/dumps/set_lookups.nft
new file mode 100644
index 0000000..7566f55
--- /dev/null
+++ b/tests/shell/testcases/packetpath/dumps/set_lookups.nft
@@ -0,0 +1,51 @@
+table ip t {
+ set s {
+ type ipv4_addr . iface_index
+ flags interval
+ elements = { 127.0.0.1 . "lo",
+ 127.0.0.2 . "lo" }
+ }
+
+ set s2 {
+ typeof ip saddr . iif
+ elements = { 127.0.0.1 . "lo",
+ 127.0.0.2 . "lo" }
+ }
+
+ set s3 {
+ type iface_index
+ elements = { "lo" }
+ }
+
+ set s4 {
+ type iface_index
+ flags interval
+ elements = { "lo" }
+ }
+
+ set nomatch {
+ typeof ip saddr . iif
+ elements = { 127.0.0.3 . "lo" }
+ }
+
+ set nomatch2 {
+ type ipv4_addr . iface_index
+ elements = { 127.0.0.2 . 90000 }
+ }
+
+ chain c {
+ type filter hook input priority filter; policy accept;
+ icmp type echo-request ip saddr . iif @s counter packets 1 bytes 84
+ icmp type echo-request ip saddr . "lo" @s counter packets 1 bytes 84
+ icmp type echo-request ip saddr . "lo" @s counter packets 1 bytes 84
+ icmp type echo-request ip saddr . iif @s2 counter packets 1 bytes 84
+ icmp type echo-request ip saddr . "lo" @s2 counter packets 1 bytes 84
+ icmp type echo-request ip saddr . "lo" @s2 counter packets 1 bytes 84
+ icmp type echo-request ip daddr . "lo" @s counter packets 1 bytes 84
+ icmp type echo-request ip daddr . "lo" @s2 counter packets 1 bytes 84
+ icmp type echo-request iif @s3 counter packets 1 bytes 84
+ icmp type echo-request iif @s4 counter packets 1 bytes 84
+ ip daddr . "lo" @nomatch counter packets 0 bytes 0 drop
+ ip daddr . iif @nomatch2 counter packets 0 bytes 0 drop
+ }
+}
diff --git a/tests/shell/testcases/packetpath/set_lookups b/tests/shell/testcases/packetpath/set_lookups
new file mode 100755
index 0000000..84a0000
--- /dev/null
+++ b/tests/shell/testcases/packetpath/set_lookups
@@ -0,0 +1,64 @@
+#!/bin/bash
+
+set -e
+
+$NFT -f /dev/stdin <<"EOF"
+table ip t {
+ set s {
+ type ipv4_addr . iface_index
+ flags interval
+ elements = { 127.0.0.1 . 1 }
+ }
+
+ set s2 {
+ typeof ip saddr . meta iif
+ elements = { 127.0.0.1 . 1 }
+ }
+
+ set s3 {
+ type iface_index
+ elements = { "lo" }
+ }
+
+ set s4 {
+ type iface_index
+ flags interval
+ elements = { "lo" }
+ }
+
+ set nomatch {
+ typeof ip saddr . meta iif
+ elements = { 127.0.0.3 . 1 }
+ }
+
+ set nomatch2 {
+ type ipv4_addr . iface_index
+ elements = { 127.0.0.2 . 90000 }
+ }
+
+ chain c {
+ type filter hook input priority filter;
+ icmp type echo-request ip saddr . meta iif @s counter
+ icmp type echo-request ip saddr . 1 @s counter
+ icmp type echo-request ip saddr . "lo" @s counter
+ icmp type echo-request ip saddr . meta iif @s2 counter
+ icmp type echo-request ip saddr . 1 @s2 counter
+ icmp type echo-request ip saddr . "lo" @s2 counter
+
+ icmp type echo-request ip daddr . "lo" @s counter
+ icmp type echo-request ip daddr . "lo" @s2 counter
+
+ icmp type echo-request meta iif @s3 counter
+ icmp type echo-request meta iif @s4 counter
+
+ ip daddr . 1 @nomatch counter drop
+ ip daddr . meta iif @nomatch2 counter drop
+ }
+}
+EOF
+
+$NFT add element t s { 127.0.0.2 . 1 }
+$NFT add element t s2 { 127.0.0.2 . "lo" }
+
+ip link set lo up
+ping -q -c 1 127.0.0.2 > /dev/null
diff --git a/tests/shell/testcases/sets/dumps/typeof_sets_concat.nft b/tests/shell/testcases/sets/dumps/typeof_sets_concat.nft
index dbaf7cd..348b584 100644
--- a/tests/shell/testcases/sets/dumps/typeof_sets_concat.nft
+++ b/tests/shell/testcases/sets/dumps/typeof_sets_concat.nft
@@ -10,3 +10,14 @@ table netdev t {
ether type != 8021q update @s { ether daddr . 123 timeout 1m } counter packets 0 bytes 0 return
}
}
+table ip t {
+ set s {
+ typeof ipsec in reqid . iif
+ size 16
+ flags interval
+ }
+
+ chain c2 {
+ ipsec in reqid . "lo" @s
+ }
+}

View File

@ -0,0 +1,53 @@
From 1a81b2faa37e30603561d4b10ac007dc29e509b1 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:42 +0200
Subject: [PATCH] src: improve error reporting for destroy command
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit f27f032b8da584d4447dc03b3ccd3370433e2c0f
commit f27f032b8da584d4447dc03b3ccd3370433e2c0f
Author: 谢致邦 (XIE Zhibang) <Yeking@Red54.com>
Date: Wed Feb 21 15:17:09 2024 +0000
src: improve error reporting for destroy command
Example for older kernels (<6.3):
nft destroy table ip missingtable
Before:
Error: Could not process rule: Invalid argument
After:
Error: "destroy" command is not supported, perhaps kernel support is
missing?
Fixes: e1dfd5cc4c46 ("src: add support to command "destroy"")
Signed-off-by: 谢致邦 (XIE Zhibang) <Yeking@Red54.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/cmd.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/src/cmd.c b/src/cmd.c
index 68c476c..21533e0 100644
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -311,6 +311,12 @@ void nft_cmd_error(struct netlink_ctx *ctx, struct cmd *cmd,
break;
}
+ if (cmd->op == CMD_DESTROY && err->err == EINVAL) {
+ netlink_io_error(ctx, loc,
+ "\"destroy\" command is not supported, perhaps kernel support is missing?");
+ return;
+ }
+
netlink_io_error(ctx, loc, "Could not process rule: %s",
strerror(err->err));
}

View File

@ -0,0 +1,92 @@
From c9d37a13faaa05ee0d260b3828bc25e612ffae20 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:42 +0200
Subject: [PATCH] parser: compact interval typeof rules
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 81fc7aee0d523d61077518534036ff10beddb3e9
commit 81fc7aee0d523d61077518534036ff10beddb3e9
Author: Florian Westphal <fw@strlen.de>
Date: Tue Feb 27 15:50:05 2024 +0100
parser: compact interval typeof rules
There are two nearly identical blocks for typeof maps:
one with INTERVAL keyword present and one without.
Compact this into a single block.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_bison.y | 34 ++++++++++++++--------------------
1 file changed, 14 insertions(+), 20 deletions(-)
diff --git a/src/parser_bison.y b/src/parser_bison.y
index e81336f..82aac8d 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -812,8 +812,8 @@ int nft_lex(void *, void *, void *);
%type <expr> symbol_expr verdict_expr integer_expr variable_expr chain_expr policy_expr
%destructor { expr_free($$); } symbol_expr verdict_expr integer_expr variable_expr chain_expr policy_expr
-%type <expr> primary_expr shift_expr and_expr typeof_expr typeof_data_expr
-%destructor { expr_free($$); } primary_expr shift_expr and_expr typeof_expr typeof_data_expr
+%type <expr> primary_expr shift_expr and_expr typeof_expr typeof_data_expr typeof_verdict_expr
+%destructor { expr_free($$); } primary_expr shift_expr and_expr typeof_expr typeof_data_expr typeof_verdict_expr
%type <expr> exclusive_or_expr inclusive_or_expr
%destructor { expr_free($$); } exclusive_or_expr inclusive_or_expr
%type <expr> basic_expr
@@ -2115,7 +2115,7 @@ subchain_block : /* empty */ { $$ = $<chain>-1; }
}
;
-typeof_data_expr : primary_expr
+typeof_verdict_expr : primary_expr
{
struct expr *e = $1;
@@ -2147,6 +2147,17 @@ typeof_data_expr : primary_expr
}
;
+typeof_data_expr : INTERVAL typeof_expr
+ {
+ $2->flags |= EXPR_F_INTERVAL;
+ $$ = $2;
+ }
+ | typeof_verdict_expr
+ {
+ $$ = $1;
+ }
+ ;
+
typeof_expr : primary_expr
{
if (expr_ops($1)->build_udata == NULL) {
@@ -2326,23 +2337,6 @@ map_block : /* empty */ { $$ = $<set>-1; }
$1->flags |= NFT_SET_MAP;
$$ = $1;
}
- | map_block TYPEOF
- typeof_expr COLON INTERVAL typeof_expr
- stmt_separator
- {
- if (already_set($1->key, &@2, state)) {
- expr_free($3);
- expr_free($6);
- YYERROR;
- }
-
- $1->key = $3;
- $1->data = $6;
- $1->data->flags |= EXPR_F_INTERVAL;
-
- $1->flags |= NFT_SET_MAP;
- $$ = $1;
- }
| map_block TYPE
data_type_expr COLON map_block_obj_type
stmt_separator close_scope_type

View File

@ -0,0 +1,78 @@
From 6bc395ed503556ef05e3db62926226afd6b036a9 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:42 +0200
Subject: [PATCH] parser: compact type/typeof set rules
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit fb98cb91c575880617e5da2cb6f094525516cb78
commit fb98cb91c575880617e5da2cb6f094525516cb78
Author: Florian Westphal <fw@strlen.de>
Date: Tue Feb 27 15:53:19 2024 +0100
parser: compact type/typeof set rules
Set/maps keys can be declared either by 'type' or 'typeof' keyword.
Compact this to use a common block for both cases.
The datatype_set call is redundant, remove it:
at this point $3 == $1->key, so this is a no-op.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_bison.y | 24 +++++++++---------------
1 file changed, 9 insertions(+), 15 deletions(-)
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 82aac8d..23b64ce 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -812,8 +812,8 @@ int nft_lex(void *, void *, void *);
%type <expr> symbol_expr verdict_expr integer_expr variable_expr chain_expr policy_expr
%destructor { expr_free($$); } symbol_expr verdict_expr integer_expr variable_expr chain_expr policy_expr
-%type <expr> primary_expr shift_expr and_expr typeof_expr typeof_data_expr typeof_verdict_expr
-%destructor { expr_free($$); } primary_expr shift_expr and_expr typeof_expr typeof_data_expr typeof_verdict_expr
+%type <expr> primary_expr shift_expr and_expr typeof_expr typeof_data_expr typeof_key_expr typeof_verdict_expr
+%destructor { expr_free($$); } primary_expr shift_expr and_expr typeof_expr typeof_data_expr typeof_key_expr typeof_verdict_expr
%type <expr> exclusive_or_expr inclusive_or_expr
%destructor { expr_free($$); } exclusive_or_expr inclusive_or_expr
%type <expr> basic_expr
@@ -2187,27 +2187,21 @@ set_block_alloc : /* empty */
}
;
+typeof_key_expr : TYPEOF typeof_expr { $$ = $2; }
+ | TYPE data_type_expr close_scope_type { $$ = $2; }
+ ;
+
set_block : /* empty */ { $$ = $<set>-1; }
| set_block common_block
| set_block stmt_separator
- | set_block TYPE data_type_expr stmt_separator close_scope_type
+ | set_block typeof_key_expr stmt_separator
{
if (already_set($1->key, &@2, state)) {
- expr_free($3);
+ expr_free($2);
YYERROR;
}
- $1->key = $3;
- $$ = $1;
- }
- | set_block TYPEOF typeof_expr stmt_separator
- {
- if (already_set($1->key, &@2, state)) {
- expr_free($3);
- YYERROR;
- }
- $1->key = $3;
- datatype_set($1->key, $3->dtype);
+ $1->key = $2;
$$ = $1;
}
| set_block FLAGS set_flag_list stmt_separator

View File

@ -0,0 +1,82 @@
From cbd3c93551dab7359fa72f4a8140032f72a00262 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:42 +0200
Subject: [PATCH] rule: fix ASAN errors in chain priority to textual names
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit ff6135270616ccf4712990246cae850e64253516
commit ff6135270616ccf4712990246cae850e64253516
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Thu Feb 29 16:50:37 2024 +0100
rule: fix ASAN errors in chain priority to textual names
ASAN reports several errors when listing this ruleset:
table ip x {
chain y {
type filter hook input priority -2147483648; policy accept;
}
}
src/rule.c:1002:8: runtime error: negation of -2147483648 cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself
src/rule.c:1001:11: runtime error: signed integer overflow: -2147483648 - 50 cannot be represented in type 'int'
Use int64_t for the offset to avoid an underflow when calculating
closest existing priority definition.
Use llabs() because abs() is undefined with INT32_MIN.
Fixes: c8a0e8c90e2d ("src: Set/print standard chain prios with textual names")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/rule.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/src/rule.c b/src/rule.c
index e2d972c..85718c7 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -977,10 +977,11 @@ static const char *prio2str(const struct output_ctx *octx,
const struct expr *expr)
{
const struct prio_tag *prio_arr;
- int std_prio, offset, prio;
+ const uint32_t reach = 10;
const char *std_prio_str;
- const int reach = 10;
+ int std_prio, prio;
size_t i, arr_size;
+ int64_t offset;
mpz_export_data(&prio, expr->value, BYTEORDER_HOST_ENDIAN, sizeof(int));
if (family == NFPROTO_BRIDGE) {
@@ -995,19 +996,21 @@ static const char *prio2str(const struct output_ctx *octx,
for (i = 0; i < arr_size; ++i) {
std_prio = prio_arr[i].val;
std_prio_str = prio_arr[i].str;
- if (abs(prio - std_prio) <= reach) {
+
+ offset = (int64_t)prio - std_prio;
+ if (llabs(offset) <= reach) {
if (!std_prio_family_hook_compat(std_prio,
family, hook))
break;
- offset = prio - std_prio;
+
strncpy(buf, std_prio_str, bufsize);
if (offset > 0)
snprintf(buf + strlen(buf),
- bufsize - strlen(buf), " + %d",
+ bufsize - strlen(buf), " + %" PRIu64,
offset);
else if (offset < 0)
snprintf(buf + strlen(buf),
- bufsize - strlen(buf), " - %d",
+ bufsize - strlen(buf), " - %" PRIu64,
-offset);
return buf;
}

View File

@ -0,0 +1,40 @@
From b146a3efdff7e834790e3ee8b605e5e7033fb8c9 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:42 +0200
Subject: [PATCH] nftables: do mot merge payloads on negation
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit f35a0d78fe870737fa39d859bd2e3ac25bf1b12e
commit f35a0d78fe870737fa39d859bd2e3ac25bf1b12e
Author: Sriram Rajagopalan <bglsriram@gmail.com>
Date: Wed Mar 13 01:32:42 2024 -0700
nftables: do mot merge payloads on negation
else, a rule like
tcp sport != 22 tcp dport != 23
will match even if the destination is 23 as long as sport is != 22.
(or vice versa).
Signed-off-by: Sriram Rajagopalan <sriramr@arista.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/rule.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/rule.c b/src/rule.c
index 85718c7..89101f9 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -2778,7 +2778,6 @@ static void stmt_reduce(const struct rule *rule)
switch (stmt->expr->op) {
case OP_EQ:
case OP_IMPLICIT:
- case OP_NEQ:
break;
default:
continue;

View File

@ -0,0 +1,337 @@
From a2ec860212bf27ca45a5c9ec55b91b3eb1c8060f Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:42 +0200
Subject: [PATCH] tests: py: add payload merging test cases
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 04fc306359a49bc9f314ae82520218f5dfebfef1
commit 04fc306359a49bc9f314ae82520218f5dfebfef1
Author: Florian Westphal <fw@strlen.de>
Date: Fri Mar 8 14:40:12 2024 +0100
tests: py: add payload merging test cases
Add a test case that would fail without preceeding fix.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/inet/payloadmerge.t | 14 ++
tests/py/inet/payloadmerge.t.json | 211 +++++++++++++++++++++++++++
tests/py/inet/payloadmerge.t.payload | 66 +++++++++
3 files changed, 291 insertions(+)
create mode 100644 tests/py/inet/payloadmerge.t
create mode 100644 tests/py/inet/payloadmerge.t.json
create mode 100644 tests/py/inet/payloadmerge.t.payload
diff --git a/tests/py/inet/payloadmerge.t b/tests/py/inet/payloadmerge.t
new file mode 100644
index 0000000..04ba1ce
--- /dev/null
+++ b/tests/py/inet/payloadmerge.t
@@ -0,0 +1,14 @@
+:input;type filter hook input priority 0
+
+*ip;test-ip4;input
+*ip6;test-ip6;input
+*inet;test-inet;input
+
+tcp sport 1 tcp dport 2;ok
+tcp sport != 1 tcp dport != 2;ok
+tcp sport 1 tcp dport != 2;ok
+tcp sport != 1 tcp dport 2;ok
+meta l4proto != 6 th dport 2;ok
+meta l4proto 6 tcp dport 22;ok;tcp dport 22
+tcp sport > 1 tcp dport > 2;ok
+tcp sport 1 tcp dport > 2;ok
diff --git a/tests/py/inet/payloadmerge.t.json b/tests/py/inet/payloadmerge.t.json
new file mode 100644
index 0000000..e5b66cf
--- /dev/null
+++ b/tests/py/inet/payloadmerge.t.json
@@ -0,0 +1,211 @@
+# tcp sport 1 tcp dport 2
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "sport",
+ "protocol": "tcp"
+ }
+ },
+ "op": "==",
+ "right": 1
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ },
+ "op": "==",
+ "right": 2
+ }
+ }
+]
+
+# tcp sport != 1 tcp dport != 2
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "sport",
+ "protocol": "tcp"
+ }
+ },
+ "op": "!=",
+ "right": 1
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ },
+ "op": "!=",
+ "right": 2
+ }
+ }
+]
+
+# tcp sport 1 tcp dport != 2
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "sport",
+ "protocol": "tcp"
+ }
+ },
+ "op": "==",
+ "right": 1
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ },
+ "op": "!=",
+ "right": 2
+ }
+ }
+]
+
+# tcp sport != 1 tcp dport 2
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "sport",
+ "protocol": "tcp"
+ }
+ },
+ "op": "!=",
+ "right": 1
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ },
+ "op": "==",
+ "right": 2
+ }
+ }
+]
+
+# meta l4proto != 6 th dport 2
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "op": "!=",
+ "right": 6
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "th"
+ }
+ },
+ "op": "==",
+ "right": 2
+ }
+ }
+]
+
+# meta l4proto 6 tcp dport 22
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ },
+ "op": "==",
+ "right": 22
+ }
+ }
+]
+
+# tcp sport > 1 tcp dport > 2
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "sport",
+ "protocol": "tcp"
+ }
+ },
+ "op": ">",
+ "right": 1
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ },
+ "op": ">",
+ "right": 2
+ }
+ }
+]
+
+# tcp sport 1 tcp dport > 2
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "sport",
+ "protocol": "tcp"
+ }
+ },
+ "op": "==",
+ "right": 1
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ },
+ "op": ">",
+ "right": 2
+ }
+ }
+]
+
diff --git a/tests/py/inet/payloadmerge.t.payload b/tests/py/inet/payloadmerge.t.payload
new file mode 100644
index 0000000..a0465cd
--- /dev/null
+++ b/tests/py/inet/payloadmerge.t.payload
@@ -0,0 +1,66 @@
+# tcp sport 1 tcp dport 2
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 4b @ transport header + 0 => reg 1 ]
+ [ cmp eq reg 1 0x02000100 ]
+
+# tcp sport != 1 tcp dport != 2
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 2b @ transport header + 0 => reg 1 ]
+ [ cmp neq reg 1 0x00000100 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp neq reg 1 0x00000200 ]
+
+# tcp sport 1 tcp dport != 2
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 2b @ transport header + 0 => reg 1 ]
+ [ cmp eq reg 1 0x00000100 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp neq reg 1 0x00000200 ]
+
+# tcp sport != 1 tcp dport 2
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 2b @ transport header + 0 => reg 1 ]
+ [ cmp neq reg 1 0x00000100 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x00000200 ]
+
+# meta l4proto != 6 th dport 2
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp neq reg 1 0x00000006 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x00000200 ]
+
+# meta l4proto 6 tcp dport 22
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x00001600 ]
+
+# tcp sport > 1 tcp dport > 2
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 2b @ transport header + 0 => reg 1 ]
+ [ cmp gt reg 1 0x00000100 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp gt reg 1 0x00000200 ]
+
+# tcp sport 1 tcp dport > 2
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 2b @ transport header + 0 => reg 1 ]
+ [ cmp eq reg 1 0x00000100 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp gt reg 1 0x00000200 ]
+

View File

@ -0,0 +1,65 @@
From 215992d2a9c837410eb84bad86307d303d4cd103 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:42 +0200
Subject: [PATCH] parser: json: Support for synproxy objects
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 938a135f2200766661e42a20e02d87555f5bacfa
commit 938a135f2200766661e42a20e02d87555f5bacfa
Author: Phil Sutter <phil@nwl.cc>
Date: Sat Mar 9 00:29:36 2024 +0100
parser: json: Support for synproxy objects
Parsing code was there already, merely the entry in json_parse_cmd_add()
missing.
To support maps with synproxy target, an entry in string_to_nft_object()
is required. While being at it, add other missing entries as well.
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_json.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/src/parser_json.c b/src/parser_json.c
index 9537c9d..b8ed848 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -3255,14 +3255,18 @@ err_free_rule:
static int string_to_nft_object(const char *str)
{
const char *obj_tbl[__NFT_OBJECT_MAX] = {
- [NFT_OBJECT_COUNTER] = "counter",
- [NFT_OBJECT_QUOTA] = "quota",
- [NFT_OBJECT_LIMIT] = "limit",
- [NFT_OBJECT_SECMARK] = "secmark",
+ [NFT_OBJECT_COUNTER] = "counter",
+ [NFT_OBJECT_QUOTA] = "quota",
+ [NFT_OBJECT_CT_HELPER] = "ct helper",
+ [NFT_OBJECT_LIMIT] = "limit",
+ [NFT_OBJECT_CT_TIMEOUT] = "ct timeout",
+ [NFT_OBJECT_SECMARK] = "secmark",
+ [NFT_OBJECT_CT_EXPECT] = "ct expectation",
+ [NFT_OBJECT_SYNPROXY] = "synproxy",
};
unsigned int i;
- for (i = 0; i < NFT_OBJECT_MAX; i++) {
+ for (i = 0; i <= NFT_OBJECT_MAX; i++) {
if (obj_tbl[i] && !strcmp(str, obj_tbl[i]))
return i;
}
@@ -3789,7 +3793,8 @@ static struct cmd *json_parse_cmd_add(struct json_ctx *ctx,
{ "ct timeout", NFT_OBJECT_CT_TIMEOUT, json_parse_cmd_add_object },
{ "ct expectation", NFT_OBJECT_CT_EXPECT, json_parse_cmd_add_object },
{ "limit", CMD_OBJ_LIMIT, json_parse_cmd_add_object },
- { "secmark", CMD_OBJ_SECMARK, json_parse_cmd_add_object }
+ { "secmark", CMD_OBJ_SECMARK, json_parse_cmd_add_object },
+ { "synproxy", CMD_OBJ_SYNPROXY, json_parse_cmd_add_object }
};
unsigned int i;
json_t *tmp;

View File

@ -0,0 +1,40 @@
From 86c010da124c480215b3a69fd08d3081b08862b1 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:42 +0200
Subject: [PATCH] doc: libnftables-json: Drop invalid ops from match expression
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 840cb6df16973139a1981fcec276f59d3b92bb46
commit 840cb6df16973139a1981fcec276f59d3b92bb46
Author: Phil Sutter <phil@nwl.cc>
Date: Fri Sep 22 18:43:11 2023 +0200
doc: libnftables-json: Drop invalid ops from match expression
These make no sense there and are listed again in BINARY OPERATION.
Fixes: 872f373dc50f7 ("doc: Add JSON schema documentation")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
doc/libnftables-json.adoc | 5 -----
1 file changed, 5 deletions(-)
diff --git a/doc/libnftables-json.adoc b/doc/libnftables-json.adoc
index 3bbb6cd..cc70f0f 100644
--- a/doc/libnftables-json.adoc
+++ b/doc/libnftables-json.adoc
@@ -695,11 +695,6 @@ processing continues with the next rule in the same chain.
==== OPERATORS
[horizontal]
-*&*:: Binary AND
-*|*:: Binary OR
-*^*:: Binary XOR
-*<<*:: Left shift
-*>>*:: Right shift
*==*:: Equal
*!=*:: Not equal
*<*:: Less than

View File

@ -0,0 +1,604 @@
From 86c0cc20f38f8503ea670f8be664278c24f9edcf Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:42 +0200
Subject: [PATCH] netlink_delinearize: restore binop syntax when listing
ruleset for flags
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit b11b6c68e61ea294eb4c313705ccfe3e7b0eda87
commit b11b6c68e61ea294eb4c313705ccfe3e7b0eda87
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Mon Mar 18 13:10:55 2024 +0100
netlink_delinearize: restore binop syntax when listing ruleset for flags
c3d57114f119 ("parser_bison: add shortcut syntax for matching flags
without binary operations") provides a similar syntax to iptables using
a prefix representation for flag matching.
Restore original representation using binop when listing the ruleset.
The parser still accepts the prefix notation for backward compatibility.
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/netlink_delinearize.c | 65 ++--
tests/py/inet/tcp.t | 16 +-
tests/py/inet/tcp.t.json | 42 ++-
tests/py/inet/tcp.t.json.output | 279 ++++++++++++++++++
tests/py/inet/tcp.t.payload | 6 +-
.../testcases/nft-f/dumps/sample-ruleset.nft | 4 +-
tests/shell/testcases/packetpath/tcp_options | 16 +-
7 files changed, 346 insertions(+), 82 deletions(-)
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 1d30a78..405a065 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -2517,56 +2517,29 @@ static void relational_binop_postprocess(struct rule_pp_ctx *ctx,
if (binop->op == OP_AND && (expr->op == OP_NEQ || expr->op == OP_EQ) &&
right->dtype->basetype &&
- right->dtype->basetype->type == TYPE_BITMASK) {
- switch (right->etype) {
- case EXPR_VALUE:
- if (!mpz_cmp_ui(right->value, 0)) {
- /* Flag comparison: data & flags != 0
- *
- * Split the flags into a list of flag values and convert the
- * op to OP_EQ.
- */
- expr_free(right);
-
- expr->left = expr_get(binop->left);
- expr->right = binop_tree_to_list(NULL, binop->right);
- switch (expr->op) {
- case OP_NEQ:
- expr->op = OP_IMPLICIT;
- break;
- case OP_EQ:
- expr->op = OP_NEG;
- break;
- default:
- BUG("unknown operation type %d\n", expr->op);
- }
- expr_free(binop);
- } else if (binop->right->etype == EXPR_VALUE &&
- right->etype == EXPR_VALUE &&
- !mpz_cmp(right->value, binop->right->value)) {
- /* Skip flag / flag representation for:
- * data & flag == flag
- * data & flag != flag
- */
- ;
- } else {
- *exprp = flagcmp_expr_alloc(&expr->location, expr->op,
- expr_get(binop->left),
- binop_tree_to_list(NULL, binop->right),
- expr_get(right));
- expr_free(expr);
- }
+ right->dtype->basetype->type == TYPE_BITMASK &&
+ right->etype == EXPR_VALUE &&
+ !mpz_cmp_ui(right->value, 0)) {
+ /* Flag comparison: data & flags != 0
+ *
+ * Split the flags into a list of flag values and convert the
+ * op to OP_EQ.
+ */
+ expr_free(right);
+
+ expr->left = expr_get(binop->left);
+ expr->right = binop_tree_to_list(NULL, binop->right);
+ switch (expr->op) {
+ case OP_NEQ:
+ expr->op = OP_IMPLICIT;
break;
- case EXPR_BINOP:
- *exprp = flagcmp_expr_alloc(&expr->location, expr->op,
- expr_get(binop->left),
- binop_tree_to_list(NULL, binop->right),
- binop_tree_to_list(NULL, right));
- expr_free(expr);
+ case OP_EQ:
+ expr->op = OP_NEG;
break;
default:
- break;
+ BUG("unknown operation type %d\n", expr->op);
}
+ expr_free(binop);
} else if (binop->left->dtype->flags & DTYPE_F_PREFIX &&
binop->op == OP_AND && expr->right->etype == EXPR_VALUE &&
expr_mask_is_prefix(binop->right)) {
diff --git a/tests/py/inet/tcp.t b/tests/py/inet/tcp.t
index f51ebd3..f4bdac1 100644
--- a/tests/py/inet/tcp.t
+++ b/tests/py/inet/tcp.t
@@ -68,8 +68,8 @@ tcp flags != { fin, urg, ecn, cwr} drop;ok
tcp flags cwr;ok
tcp flags != cwr;ok
tcp flags == syn;ok
-tcp flags fin,syn / fin,syn;ok
-tcp flags != syn / fin,syn;ok
+tcp flags fin,syn / fin,syn;ok;tcp flags & (fin | syn) == fin | syn
+tcp flags != syn / fin,syn;ok;tcp flags & (fin | syn) != syn
tcp flags & syn != 0;ok;tcp flags syn
tcp flags & syn == 0;ok;tcp flags ! syn
tcp flags & (syn | ack) != 0;ok;tcp flags syn,ack
@@ -77,12 +77,12 @@ tcp flags & (syn | ack) == 0;ok;tcp flags ! syn,ack
# it should be possible to transform this to: tcp flags syn
tcp flags & syn == syn;ok
tcp flags & syn != syn;ok
-tcp flags & (fin | syn | rst | ack) syn;ok;tcp flags syn / fin,syn,rst,ack
-tcp flags & (fin | syn | rst | ack) == syn;ok;tcp flags syn / fin,syn,rst,ack
-tcp flags & (fin | syn | rst | ack) != syn;ok;tcp flags != syn / fin,syn,rst,ack
-tcp flags & (fin | syn | rst | ack) == (syn | ack);ok;tcp flags syn,ack / fin,syn,rst,ack
-tcp flags & (fin | syn | rst | ack) != (syn | ack);ok;tcp flags != syn,ack / fin,syn,rst,ack
-tcp flags & (syn | ack) == (syn | ack);ok;tcp flags syn,ack / syn,ack
+tcp flags & (fin | syn | rst | ack) syn;ok;tcp flags & (fin | syn | rst | ack) == syn
+tcp flags & (fin | syn | rst | ack) == syn;ok
+tcp flags & (fin | syn | rst | ack) != syn;ok
+tcp flags & (fin | syn | rst | ack) == syn | ack;ok
+tcp flags & (fin | syn | rst | ack) != syn | ack;ok
+tcp flags & (syn | ack) == syn | ack;ok
tcp flags & (fin | syn | rst | psh | ack | urg | ecn | cwr) == fin | syn | rst | psh | ack | urg | ecn | cwr;ok;tcp flags == 0xff
tcp flags { syn, syn | ack };ok
tcp flags & (fin | syn | rst | psh | ack | urg) == { fin, ack, psh | ack, fin | psh | ack };ok
diff --git a/tests/py/inet/tcp.t.json b/tests/py/inet/tcp.t.json
index 8439c2b..d3a846c 100644
--- a/tests/py/inet/tcp.t.json
+++ b/tests/py/inet/tcp.t.json
@@ -1712,7 +1712,7 @@
}
]
-# tcp flags & (fin | syn | rst | ack) == (syn | ack)
+# tcp flags & (fin | syn | rst | ack) == syn | ack
[
{
"match": {
@@ -1741,7 +1741,7 @@
}
]
-# tcp flags & (fin | syn | rst | ack) != (syn | ack)
+# tcp flags & (syn | ack) == syn | ack
[
{
"match": {
@@ -1754,14 +1754,12 @@
}
},
[
- "fin",
"syn",
- "rst",
"ack"
]
]
},
- "op": "!=",
+ "op": "==",
"right": [
"syn",
"ack"
@@ -1770,7 +1768,7 @@
}
]
-# tcp flags & (syn | ack) == (syn | ack)
+# tcp flags & (fin | syn | rst | ack) != syn | ack
[
{
"match": {
@@ -1782,17 +1780,31 @@
"protocol": "tcp"
}
},
- [
- "syn",
- "ack"
- ]
+ {
+ "|": [
+ {
+ "|": [
+ {
+ "|": [
+ "fin",
+ "syn"
+ ]
+ },
+ "rst"
+ ]
+ },
+ "ack"
+ ]
+ }
]
},
- "op": "==",
- "right": [
- "syn",
- "ack"
- ]
+ "op": "!=",
+ "right": {
+ "|": [
+ "syn",
+ "ack"
+ ]
+ }
}
}
]
diff --git a/tests/py/inet/tcp.t.json.output b/tests/py/inet/tcp.t.json.output
index c471e8d..e186e12 100644
--- a/tests/py/inet/tcp.t.json.output
+++ b/tests/py/inet/tcp.t.json.output
@@ -208,3 +208,282 @@
}
}
]
+
+# tcp flags fin,syn / fin,syn
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ {
+ "|": [
+ "fin",
+ "syn"
+ ]
+ }
+ ]
+ },
+ "op": "==",
+ "right": {
+ "|": [
+ "fin",
+ "syn"
+ ]
+ }
+ }
+ }
+]
+
+# tcp flags != syn / fin,syn
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ {
+ "|": [
+ "fin",
+ "syn"
+ ]
+ }
+ ]
+ },
+ "op": "!=",
+ "right": "syn"
+ }
+ }
+]
+
+# tcp flags & (fin | syn | rst | ack) syn
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ {
+ "|": [
+ {
+ "|": [
+ {
+ "|": [
+ "fin",
+ "syn"
+ ]
+ },
+ "rst"
+ ]
+ },
+ "ack"
+ ]
+ }
+ ]
+ },
+ "op": "==",
+ "right": "syn"
+ }
+ }
+]
+
+# tcp flags & (fin | syn | rst | ack) == syn
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ {
+ "|": [
+ {
+ "|": [
+ {
+ "|": [
+ "fin",
+ "syn"
+ ]
+ },
+ "rst"
+ ]
+ },
+ "ack"
+ ]
+ }
+ ]
+ },
+ "op": "==",
+ "right": "syn"
+ }
+ }
+]
+
+# tcp flags & (fin | syn | rst | ack) != syn
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ {
+ "|": [
+ {
+ "|": [
+ {
+ "|": [
+ "fin",
+ "syn"
+ ]
+ },
+ "rst"
+ ]
+ },
+ "ack"
+ ]
+ }
+ ]
+ },
+ "op": "!=",
+ "right": "syn"
+ }
+ }
+]
+
+# tcp flags & (fin | syn | rst | ack) == syn | ack
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ {
+ "|": [
+ {
+ "|": [
+ {
+ "|": [
+ "fin",
+ "syn"
+ ]
+ },
+ "rst"
+ ]
+ },
+ "ack"
+ ]
+ }
+ ]
+ },
+ "op": "==",
+ "right": {
+ "|": [
+ "syn",
+ "ack"
+ ]
+ }
+ }
+ }
+]
+
+# tcp flags & (fin | syn | rst | ack) != syn | ack
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ {
+ "|": [
+ {
+ "|": [
+ {
+ "|": [
+ "fin",
+ "syn"
+ ]
+ },
+ "rst"
+ ]
+ },
+ "ack"
+ ]
+ }
+ ]
+ },
+ "op": "!=",
+ "right": {
+ "|": [
+ "syn",
+ "ack"
+ ]
+ }
+ }
+ }
+]
+
+# tcp flags & (syn | ack) == syn | ack
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "flags",
+ "protocol": "tcp"
+ }
+ },
+ {
+ "|": [
+ "syn",
+ "ack"
+ ]
+ }
+ ]
+ },
+ "op": "==",
+ "right": {
+ "|": [
+ "syn",
+ "ack"
+ ]
+ }
+ }
+ }
+]
+
diff --git a/tests/py/inet/tcp.t.payload b/tests/py/inet/tcp.t.payload
index 1cfe500..bc6bb98 100644
--- a/tests/py/inet/tcp.t.payload
+++ b/tests/py/inet/tcp.t.payload
@@ -442,7 +442,7 @@ inet test-inet input
[ bitwise reg 1 = ( reg 1 & 0x00000017 ) ^ 0x00000000 ]
[ cmp neq reg 1 0x00000002 ]
-# tcp flags & (fin | syn | rst | ack) == (syn | ack)
+# tcp flags & (fin | syn | rst | ack) == syn | ack
inet test-inet input
[ meta load l4proto => reg 1 ]
[ cmp eq reg 1 0x00000006 ]
@@ -450,7 +450,7 @@ inet test-inet input
[ bitwise reg 1 = ( reg 1 & 0x00000017 ) ^ 0x00000000 ]
[ cmp eq reg 1 0x00000012 ]
-# tcp flags & (fin | syn | rst | ack) != (syn | ack)
+# tcp flags & (fin | syn | rst | ack) != syn | ack
inet test-inet input
[ meta load l4proto => reg 1 ]
[ cmp eq reg 1 0x00000006 ]
@@ -458,7 +458,7 @@ inet test-inet input
[ bitwise reg 1 = ( reg 1 & 0x00000017 ) ^ 0x00000000 ]
[ cmp neq reg 1 0x00000012 ]
-# tcp flags & (syn | ack) == (syn | ack)
+# tcp flags & (syn | ack) == syn | ack
inet test-inet input
[ meta load l4proto => reg 1 ]
[ cmp eq reg 1 0x00000006 ]
diff --git a/tests/shell/testcases/nft-f/dumps/sample-ruleset.nft b/tests/shell/testcases/nft-f/dumps/sample-ruleset.nft
index 480b694..1a9f4e7 100644
--- a/tests/shell/testcases/nft-f/dumps/sample-ruleset.nft
+++ b/tests/shell/testcases/nft-f/dumps/sample-ruleset.nft
@@ -73,7 +73,7 @@ table inet filter {
chain ct_new_pre {
jump rpfilter
- tcp flags != syn / fin,syn,rst,ack counter packets 0 bytes 0 drop
+ tcp flags & (fin | syn | rst | ack) != syn counter packets 0 bytes 0 drop
iifname "eth0" meta nfproto vmap { ipv4 : jump blacklist_input_ipv4, ipv6 : jump blacklist_input_ipv6 }
}
@@ -131,7 +131,7 @@ table inet filter {
type filter hook forward priority mangle; policy accept;
oifname "eth0" jump {
ct state new meta nfproto vmap { ipv4 : jump blacklist_output_ipv4, ipv6 : jump blacklist_output_ipv6 }
- tcp flags syn / syn,rst tcp option maxseg size set rt mtu
+ tcp flags & (syn | rst) == syn tcp option maxseg size set rt mtu
}
}
diff --git a/tests/shell/testcases/packetpath/tcp_options b/tests/shell/testcases/packetpath/tcp_options
index 1c9ee53..8855222 100755
--- a/tests/shell/testcases/packetpath/tcp_options
+++ b/tests/shell/testcases/packetpath/tcp_options
@@ -15,14 +15,14 @@ table inet t {
chain c {
type filter hook output priority 0;
tcp dport != 22345 accept
- tcp flags syn / fin,syn,rst,ack tcp option 254 length ge 4 counter name nomatchc drop
- tcp flags syn / fin,syn,rst,ack tcp option fastopen length ge 2 reset tcp option fastopen counter name nomatchc
- tcp flags syn / fin,syn,rst,ack tcp option sack-perm missing counter name nomatchc
- tcp flags syn / fin,syn,rst,ack tcp option sack-perm exists counter name sackpermc
- tcp flags syn / fin,syn,rst,ack tcp option maxseg size gt 1400 counter name maxsegc
- tcp flags syn / fin,syn,rst,ack tcp option nop missing counter name nomatchc
- tcp flags syn / fin,syn,rst,ack tcp option nop exists counter name nopc
- tcp flags syn / fin,syn,rst,ack drop
+ tcp flags & (fin | syn | rst | ack ) == syn tcp option 254 length ge 4 counter name nomatchc drop
+ tcp flags & (fin | syn | rst | ack ) == syn tcp option fastopen length ge 2 reset tcp option fastopen counter name nomatchc
+ tcp flags & (fin | syn | rst | ack ) == syn tcp option sack-perm missing counter name nomatchc
+ tcp flags & (fin | syn | rst | ack) == syn tcp option sack-perm exists counter name sackpermc
+ tcp flags & (fin | syn | rst | ack) == syn tcp option maxseg size gt 1400 counter name maxsegc
+ tcp flags & (fin | syn | rst | ack) == syn tcp option nop missing counter name nomatchc
+ tcp flags & (fin | syn | rst | ack) == syn tcp option nop exists counter name nopc
+ tcp flags & (fin | syn | rst | ack) == syn drop
}
}
EOF

View File

@ -0,0 +1,40 @@
From 8d0cfbc6790d4206414cf21e6cd65ad4fd275624 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:43 +0200
Subject: [PATCH] evaluate: display "Range negative size" error
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit c0a5b8c6a6433ec1d4e41646dc42ccb8444c96be
commit c0a5b8c6a6433ec1d4e41646dc42ccb8444c96be
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue Mar 19 19:50:00 2024 +0100
evaluate: display "Range negative size" error
zero length ranges now allowed, therefore, update error message to refer
to negative ranges which are not possible.
Fixes: 7a6e16040d65 ("evaluate: allow for zero length ranges")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/evaluate.c b/src/evaluate.c
index 89656eb..38a80f2 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1255,8 +1255,8 @@ static int expr_evaluate_range(struct eval_ctx *ctx, struct expr **expr)
right = range->right;
if (mpz_cmp(left->value, right->value) > 0)
- return expr_error(ctx->msgs, range,
- "Range has zero or negative size");
+ return expr_error(ctx->msgs, range, "Range negative size");
+
datatype_set(range, left->dtype);
range->flags |= EXPR_F_CONSTANT;
return 0;

View File

@ -0,0 +1,564 @@
From 094d220d2fcaae5b67789b58f54d59e7b016657e Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:43 +0200
Subject: [PATCH] src: disentangle ICMP code types
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 5fecd2a6ef614eca7b0829e684449ee25982c233
commit 5fecd2a6ef614eca7b0829e684449ee25982c233
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue Apr 2 00:28:24 2024 +0200
src: disentangle ICMP code types
Currently, ICMP{v4,v6,inet} code datatypes only describe those that are
supported by the reject statement, but they can also be used for icmp
code matching. Moreover, ICMP code types go hand-to-hand with ICMP
types, that is, ICMP code symbols depend on the ICMP type.
Thus, the output of:
nft describe icmp_code
look confusing because that only displays the values that are supported
by the reject statement.
Disentangle this by adding internal datatypes for the reject statement
to handle the ICMP code symbol conversion to value as well as ruleset
listing.
The existing icmp_code, icmpv6_code and icmpx_code remain in place. For
backward compatibility, a parser function is defined in case an existing
ruleset relies on these symbols.
As for the manpage, move existing ICMP code tables from the DATA TYPES
section to the REJECT STATEMENT section, where this really belongs to.
But the icmp_code and icmpv6_code table stubs remain in the DATA TYPES
section because that describe that this is an 8-bit integer field.
After this patch:
# nft describe icmp_code
datatype icmp_code (icmp code) (basetype integer), 8 bits
# nft describe icmpv6_code
datatype icmpv6_code (icmpv6 code) (basetype integer), 8 bits
# nft describe icmpx_code
datatype icmpx_code (icmpx code) (basetype integer), 8 bits
do not display the symbol table of the reject statement anymore.
icmpx_code_type is not used anymore, but keep it in place for backward
compatibility reasons.
And update tests/shell accordingly.
Fixes: 5fdd0b6a0600 ("nft: complete reject support")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
doc/data-types.txt | 68 -----------------------------------
doc/statements.txt | 74 ++++++++++++++++++++++++++++++---------
include/datatype.h | 5 +++
src/datatype.c | 71 +++++++++++++++++++++++++++++++++----
src/netlink_delinearize.c | 10 +++---
src/parser_bison.y | 12 +++----
src/parser_json.c | 6 ++--
tests/py/ip/icmp.t | 6 ++--
tests/py/ip6/icmpv6.t | 8 ++---
9 files changed, 147 insertions(+), 113 deletions(-)
diff --git a/doc/data-types.txt b/doc/data-types.txt
index e5ee91a..6c0e2f9 100644
--- a/doc/data-types.txt
+++ b/doc/data-types.txt
@@ -242,28 +242,6 @@ integer
The ICMP Code type is used to conveniently specify the ICMP header's code field.
-.Keywords may be used when specifying the ICMP code
-[options="header"]
-|==================
-|Keyword | Value
-|net-unreachable |
-0
-|host-unreachable |
-1
-|prot-unreachable|
-2
-|port-unreachable|
-3
-|frag-needed|
-4
-|net-prohibited|
-9
-|host-prohibited|
-10
-|admin-prohibited|
-13
-|===================
-
ICMPV6 TYPE TYPE
~~~~~~~~~~~~~~~~
[options="header"]
@@ -340,52 +318,6 @@ integer
The ICMPv6 Code type is used to conveniently specify the ICMPv6 header's code field.
-.keywords may be used when specifying the ICMPv6 code
-[options="header"]
-|==================
-|Keyword |Value
-|no-route|
-0
-|admin-prohibited|
-1
-|addr-unreachable|
-3
-|port-unreachable|
-4
-|policy-fail|
-5
-|reject-route|
-6
-|==================
-
-ICMPVX CODE TYPE
-~~~~~~~~~~~~~~~~
-[options="header"]
-|==================
-|Name | Keyword | Size | Base type
-|ICMPvX Code |
-icmpx_code |
-8 bit |
-integer
-|===================
-
-The ICMPvX Code type abstraction is a set of values which overlap between ICMP
-and ICMPv6 Code types to be used from the inet family.
-
-.keywords may be used when specifying the ICMPvX code
-[options="header"]
-|==================
-|Keyword |Value
-|no-route|
-0
-|port-unreachable|
-1
-|host-unreachable|
-2
-|admin-prohibited|
-3
-|=================
-
CONNTRACK TYPES
~~~~~~~~~~~~~~~
diff --git a/doc/statements.txt b/doc/statements.txt
index c29a2ff..834adb2 100644
--- a/doc/statements.txt
+++ b/doc/statements.txt
@@ -164,9 +164,9 @@ REJECT STATEMENT
____
*reject* [ *with* 'REJECT_WITH' ]
-'REJECT_WITH' := *icmp* 'icmp_code' |
- *icmpv6* 'icmpv6_code' |
- *icmpx* 'icmpx_code' |
+'REJECT_WITH' := *icmp* 'icmp_reject_code' |
+ *icmpv6* 'icmpv6_reject_code' |
+ *icmpx* 'icmpx_reject_code' |
*tcp reset*
____
@@ -177,24 +177,64 @@ using the *input*,
*forward* or *output* hooks, and user-defined chains which are only called from
those chains.
-.different ICMP reject variants are meant for use in different table families
+.Keywords may be used to reject when specifying the ICMP code
[options="header"]
|==================
-|Variant |Family | Type
-|icmp|
-ip|
-icmp_code
-|icmpv6|
-ip6|
-icmpv6_code
-|icmpx|
-inet|
-icmpx_code
+|Keyword | Value
+|net-unreachable |
+0
+|host-unreachable |
+1
+|prot-unreachable|
+2
+|port-unreachable|
+3
+|frag-needed|
+4
+|net-prohibited|
+9
+|host-prohibited|
+10
+|admin-prohibited|
+13
+|===================
+
+.keywords may be used to reject when specifying the ICMPv6 code
+[options="header"]
|==================
+|Keyword |Value
+|no-route|
+0
+|admin-prohibited|
+1
+|addr-unreachable|
+3
+|port-unreachable|
+4
+|policy-fail|
+5
+|reject-route|
+6
+|==================
+
+The ICMPvX Code type abstraction is a set of values which overlap between ICMP
+and ICMPv6 Code types to be used from the inet family.
+
+.keywords may be used when specifying the ICMPvX code
+[options="header"]
+|==================
+|Keyword |Value
+|no-route|
+0
+|port-unreachable|
+1
+|host-unreachable|
+2
+|admin-prohibited|
+3
+|=================
-For a description of the different types and a list of supported keywords refer
-to DATA TYPES section above. The common default reject value is
-*port-unreachable*. +
+The common default ICMP code to reject is *port-unreachable*.
Note that in bridge family, reject statement is only allowed in base chains
which hook into input or prerouting.
diff --git a/include/datatype.h b/include/datatype.h
index 09a7894..68e12d5 100644
--- a/include/datatype.h
+++ b/include/datatype.h
@@ -280,6 +280,11 @@ extern const struct datatype priority_type;
extern const struct datatype policy_type;
extern const struct datatype cgroupv2_type;
+/* private datatypes for reject statement. */
+extern const struct datatype reject_icmp_code_type;
+extern const struct datatype reject_icmpv6_code_type;
+extern const struct datatype reject_icmpx_code_type;
+
void inet_service_type_print(const struct expr *expr, struct output_ctx *octx);
extern const struct datatype *concat_type_alloc(uint32_t type);
diff --git a/src/datatype.c b/src/datatype.c
index a92f41d..46d77eb 100644
--- a/src/datatype.c
+++ b/src/datatype.c
@@ -978,6 +978,7 @@ const struct datatype mark_type = {
.flags = DTYPE_F_PREFIX,
};
+/* symbol table for private datatypes for reject statement. */
static const struct symbol_table icmp_code_tbl = {
.base = BASE_DECIMAL,
.symbols = {
@@ -993,16 +994,17 @@ static const struct symbol_table icmp_code_tbl = {
},
};
-const struct datatype icmp_code_type = {
- .type = TYPE_ICMP_CODE,
+/* private datatype for reject statement. */
+const struct datatype reject_icmp_code_type = {
.name = "icmp_code",
- .desc = "icmp code",
+ .desc = "reject icmp code",
.size = BITS_PER_BYTE,
.byteorder = BYTEORDER_BIG_ENDIAN,
.basetype = &integer_type,
.sym_tbl = &icmp_code_tbl,
};
+/* symbol table for private datatypes for reject statement. */
static const struct symbol_table icmpv6_code_tbl = {
.base = BASE_DECIMAL,
.symbols = {
@@ -1016,16 +1018,17 @@ static const struct symbol_table icmpv6_code_tbl = {
},
};
-const struct datatype icmpv6_code_type = {
- .type = TYPE_ICMPV6_CODE,
+/* private datatype for reject statement. */
+const struct datatype reject_icmpv6_code_type = {
.name = "icmpv6_code",
- .desc = "icmpv6 code",
+ .desc = "reject icmpv6 code",
.size = BITS_PER_BYTE,
.byteorder = BYTEORDER_BIG_ENDIAN,
.basetype = &integer_type,
.sym_tbl = &icmpv6_code_tbl,
};
+/* symbol table for private datatypes for reject statement. */
static const struct symbol_table icmpx_code_tbl = {
.base = BASE_DECIMAL,
.symbols = {
@@ -1037,6 +1040,60 @@ static const struct symbol_table icmpx_code_tbl = {
},
};
+/* private datatype for reject statement. */
+const struct datatype reject_icmpx_code_type = {
+ .name = "icmpx_code",
+ .desc = "reject icmpx code",
+ .size = BITS_PER_BYTE,
+ .byteorder = BYTEORDER_BIG_ENDIAN,
+ .basetype = &integer_type,
+ .sym_tbl = &icmpx_code_tbl,
+};
+
+/* Backward compatible parser for the reject statement. */
+static struct error_record *icmp_code_parse(struct parse_ctx *ctx,
+ const struct expr *sym,
+ struct expr **res)
+{
+ return symbolic_constant_parse(ctx, sym, &icmp_code_tbl, res);
+}
+
+const struct datatype icmp_code_type = {
+ .type = TYPE_ICMP_CODE,
+ .name = "icmp_code",
+ .desc = "icmp code",
+ .size = BITS_PER_BYTE,
+ .byteorder = BYTEORDER_BIG_ENDIAN,
+ .basetype = &integer_type,
+ .parse = icmp_code_parse,
+};
+
+/* Backward compatible parser for the reject statement. */
+static struct error_record *icmpv6_code_parse(struct parse_ctx *ctx,
+ const struct expr *sym,
+ struct expr **res)
+{
+ return symbolic_constant_parse(ctx, sym, &icmpv6_code_tbl, res);
+}
+
+const struct datatype icmpv6_code_type = {
+ .type = TYPE_ICMPV6_CODE,
+ .name = "icmpv6_code",
+ .desc = "icmpv6 code",
+ .size = BITS_PER_BYTE,
+ .byteorder = BYTEORDER_BIG_ENDIAN,
+ .basetype = &integer_type,
+ .parse = icmpv6_code_parse,
+};
+
+/* Backward compatible parser for the reject statement. */
+static struct error_record *icmpx_code_parse(struct parse_ctx *ctx,
+ const struct expr *sym,
+ struct expr **res)
+{
+ return symbolic_constant_parse(ctx, sym, &icmpx_code_tbl, res);
+}
+
const struct datatype icmpx_code_type = {
.type = TYPE_ICMPX_CODE,
.name = "icmpx_code",
@@ -1044,7 +1101,7 @@ const struct datatype icmpx_code_type = {
.size = BITS_PER_BYTE,
.byteorder = BYTEORDER_BIG_ENDIAN,
.basetype = &integer_type,
- .sym_tbl = &icmpx_code_tbl,
+ .parse = icmpx_code_parse,
};
void time_print(uint64_t ms, struct output_ctx *octx)
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 405a065..1af0278 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -2930,7 +2930,7 @@ static void stmt_reject_postprocess(struct rule_pp_ctx *rctx)
switch (dl->pctx.family) {
case NFPROTO_IPV4:
stmt->reject.family = dl->pctx.family;
- datatype_set(stmt->reject.expr, &icmp_code_type);
+ datatype_set(stmt->reject.expr, &reject_icmp_code_type);
if (stmt->reject.type == NFT_REJECT_TCP_RST &&
payload_dependency_exists(&dl->pdctx,
PROTO_BASE_TRANSPORT_HDR))
@@ -2939,7 +2939,7 @@ static void stmt_reject_postprocess(struct rule_pp_ctx *rctx)
break;
case NFPROTO_IPV6:
stmt->reject.family = dl->pctx.family;
- datatype_set(stmt->reject.expr, &icmpv6_code_type);
+ datatype_set(stmt->reject.expr, &reject_icmpv6_code_type);
if (stmt->reject.type == NFT_REJECT_TCP_RST &&
payload_dependency_exists(&dl->pdctx,
PROTO_BASE_TRANSPORT_HDR))
@@ -2950,7 +2950,7 @@ static void stmt_reject_postprocess(struct rule_pp_ctx *rctx)
case NFPROTO_BRIDGE:
case NFPROTO_NETDEV:
if (stmt->reject.type == NFT_REJECT_ICMPX_UNREACH) {
- datatype_set(stmt->reject.expr, &icmpx_code_type);
+ datatype_set(stmt->reject.expr, &reject_icmpx_code_type);
break;
}
@@ -2966,12 +2966,12 @@ static void stmt_reject_postprocess(struct rule_pp_ctx *rctx)
case NFPROTO_IPV4: /* INET */
case __constant_htons(ETH_P_IP): /* BRIDGE, NETDEV */
stmt->reject.family = NFPROTO_IPV4;
- datatype_set(stmt->reject.expr, &icmp_code_type);
+ datatype_set(stmt->reject.expr, &reject_icmp_code_type);
break;
case NFPROTO_IPV6: /* INET */
case __constant_htons(ETH_P_IPV6): /* BRIDGE, NETDEV */
stmt->reject.family = NFPROTO_IPV6;
- datatype_set(stmt->reject.expr, &icmpv6_code_type);
+ datatype_set(stmt->reject.expr, &reject_icmpv6_code_type);
break;
default:
break;
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 23b64ce..e3ac2fb 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -3715,40 +3715,40 @@ reject_opts : /* empty */
$<stmt>0->reject.family = NFPROTO_IPV4;
$<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
$<stmt>0->reject.expr = $4;
- datatype_set($<stmt>0->reject.expr, &icmp_code_type);
+ datatype_set($<stmt>0->reject.expr, &reject_icmp_code_type);
}
| WITH ICMP reject_with_expr
{
$<stmt>0->reject.family = NFPROTO_IPV4;
$<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
$<stmt>0->reject.expr = $3;
- datatype_set($<stmt>0->reject.expr, &icmp_code_type);
+ datatype_set($<stmt>0->reject.expr, &reject_icmp_code_type);
}
| WITH ICMP6 TYPE reject_with_expr close_scope_type close_scope_icmp
{
$<stmt>0->reject.family = NFPROTO_IPV6;
$<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
$<stmt>0->reject.expr = $4;
- datatype_set($<stmt>0->reject.expr, &icmpv6_code_type);
+ datatype_set($<stmt>0->reject.expr, &reject_icmpv6_code_type);
}
| WITH ICMP6 reject_with_expr
{
$<stmt>0->reject.family = NFPROTO_IPV6;
$<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
$<stmt>0->reject.expr = $3;
- datatype_set($<stmt>0->reject.expr, &icmpv6_code_type);
+ datatype_set($<stmt>0->reject.expr, &reject_icmpv6_code_type);
}
| WITH ICMPX TYPE reject_with_expr close_scope_type
{
$<stmt>0->reject.type = NFT_REJECT_ICMPX_UNREACH;
$<stmt>0->reject.expr = $4;
- datatype_set($<stmt>0->reject.expr, &icmpx_code_type);
+ datatype_set($<stmt>0->reject.expr, &reject_icmpx_code_type);
}
| WITH ICMPX reject_with_expr
{
$<stmt>0->reject.type = NFT_REJECT_ICMPX_UNREACH;
$<stmt>0->reject.expr = $3;
- datatype_set($<stmt>0->reject.expr, &icmpx_code_type);
+ datatype_set($<stmt>0->reject.expr, &reject_icmpx_code_type);
}
| WITH TCP close_scope_tcp RESET close_scope_reset
{
diff --git a/src/parser_json.c b/src/parser_json.c
index b8ed848..25483b1 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -2318,17 +2318,17 @@ static struct stmt *json_parse_reject_stmt(struct json_ctx *ctx,
stmt->reject.icmp_code = 0;
} else if (!strcmp(type, "icmpx")) {
stmt->reject.type = NFT_REJECT_ICMPX_UNREACH;
- dtype = &icmpx_code_type;
+ dtype = &reject_icmpx_code_type;
stmt->reject.icmp_code = 0;
} else if (!strcmp(type, "icmp")) {
stmt->reject.type = NFT_REJECT_ICMP_UNREACH;
stmt->reject.family = NFPROTO_IPV4;
- dtype = &icmp_code_type;
+ dtype = &reject_icmp_code_type;
stmt->reject.icmp_code = 0;
} else if (!strcmp(type, "icmpv6")) {
stmt->reject.type = NFT_REJECT_ICMP_UNREACH;
stmt->reject.family = NFPROTO_IPV6;
- dtype = &icmpv6_code_type;
+ dtype = &reject_icmpv6_code_type;
stmt->reject.icmp_code = 0;
}
}
diff --git a/tests/py/ip/icmp.t b/tests/py/ip/icmp.t
index 7ddf8b3..226c339 100644
--- a/tests/py/ip/icmp.t
+++ b/tests/py/ip/icmp.t
@@ -26,8 +26,8 @@ icmp code 111 accept;ok
icmp code != 111 accept;ok
icmp code 33-55;ok
icmp code != 33-55;ok
-icmp code { 2, 4, 54, 33, 56};ok;icmp code { prot-unreachable, frag-needed, 33, 54, 56}
-icmp code != { prot-unreachable, frag-needed, 33, 54, 56};ok
+icmp code { 2, 4, 54, 33, 56};ok
+icmp code != { prot-unreachable, frag-needed, 33, 54, 56};ok;icmp code != { 2, 4, 33, 54, 56}
icmp checksum 12343 accept;ok
icmp checksum != 12343 accept;ok
@@ -73,5 +73,5 @@ icmp gateway != { 33, 55, 67, 88};ok
icmp gateway != 34;ok
icmp gateway != { 333, 334};ok
-icmp code 1 icmp type 2;ok;icmp type 2 icmp code host-unreachable
+icmp code 1 icmp type 2;ok;icmp type 2 icmp code 1
icmp code != 1 icmp type 2 icmp mtu 5;fail
diff --git a/tests/py/ip6/icmpv6.t b/tests/py/ip6/icmpv6.t
index 35dad2b..7632bfd 100644
--- a/tests/py/ip6/icmpv6.t
+++ b/tests/py/ip6/icmpv6.t
@@ -28,10 +28,10 @@ icmpv6 type {router-renumbering, mld-listener-done, time-exceeded, nd-router-sol
icmpv6 type {mld-listener-query, time-exceeded, nd-router-advert} accept;ok
icmpv6 type != {mld-listener-query, time-exceeded, nd-router-advert} accept;ok
-icmpv6 code 4;ok;icmpv6 code port-unreachable
+icmpv6 code 4;ok
icmpv6 code 3-66;ok
-icmpv6 code {5, 6, 7} accept;ok;icmpv6 code {policy-fail, reject-route, 7} accept
-icmpv6 code != {policy-fail, reject-route, 7} accept;ok
+icmpv6 code {5, 6, 7} accept;ok
+icmpv6 code != {policy-fail, reject-route, 7} accept;ok;icmpv6 code != {5, 6, 7} accept
icmpv6 checksum 2222 log;ok
icmpv6 checksum != 2222 log;ok
@@ -84,7 +84,7 @@ icmpv6 max-delay != 33-45;ok
icmpv6 max-delay {33, 55, 67, 88};ok
icmpv6 max-delay != {33, 55, 67, 88};ok
-icmpv6 type parameter-problem icmpv6 code no-route;ok
+icmpv6 type parameter-problem icmpv6 code 0;ok
icmpv6 type mld-listener-query icmpv6 taddr 2001:db8::133;ok
icmpv6 type nd-neighbor-solicit icmpv6 taddr 2001:db8::133;ok

View File

@ -0,0 +1,146 @@
From 7a57053ed7c9fa767596b33521545c552be89dba Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:43 +0200
Subject: [PATCH] tests: py: complete icmp and icmpv6 update
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 1702bfd70b39fe955d5e2c4b93f7dc07acb79027
commit 1702bfd70b39fe955d5e2c4b93f7dc07acb79027
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Thu Apr 4 13:56:39 2024 +0200
tests: py: complete icmp and icmpv6 update
Update json update and leftover payload update to complete
5fecd2a6ef61 ("src: disentangle ICMP code types").
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/py/ip/icmp.t.json | 6 +++---
tests/py/ip/icmp.t.json.output | 4 ++--
tests/py/ip6/icmpv6.t.json | 8 ++++----
tests/py/ip6/icmpv6.t.json.output | 8 ++++----
tests/py/ip6/icmpv6.t.payload.ip6 | 2 +-
5 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/tests/py/ip/icmp.t.json b/tests/py/ip/icmp.t.json
index 4f05250..45e04c7 100644
--- a/tests/py/ip/icmp.t.json
+++ b/tests/py/ip/icmp.t.json
@@ -459,8 +459,8 @@
"op": "!=",
"right": {
"set": [
- "prot-unreachable",
- "frag-needed",
+ 2,
+ 4,
33,
54,
56
@@ -1488,7 +1488,7 @@
}
},
"op": "==",
- "right": "host-unreachable"
+ "right": 1
}
}
]
diff --git a/tests/py/ip/icmp.t.json.output b/tests/py/ip/icmp.t.json.output
index 5a07585..52fd601 100644
--- a/tests/py/ip/icmp.t.json.output
+++ b/tests/py/ip/icmp.t.json.output
@@ -11,8 +11,8 @@
"op": "==",
"right": {
"set": [
- "prot-unreachable",
- "frag-needed",
+ 2,
+ 4,
33,
54,
56
diff --git a/tests/py/ip6/icmpv6.t.json b/tests/py/ip6/icmpv6.t.json
index 224a8e8..9df886d 100644
--- a/tests/py/ip6/icmpv6.t.json
+++ b/tests/py/ip6/icmpv6.t.json
@@ -532,8 +532,8 @@
"op": "!=",
"right": {
"set": [
- "policy-fail",
- "reject-route",
+ 5,
+ 6,
7
]
}
@@ -1136,7 +1136,7 @@
}
]
-# icmpv6 type parameter-problem icmpv6 code no-route
+# icmpv6 type parameter-problem icmpv6 code 0
[
{
"match": {
@@ -1159,7 +1159,7 @@
}
},
"op": "==",
- "right": "no-route"
+ "right": 0
}
}
]
diff --git a/tests/py/ip6/icmpv6.t.json.output b/tests/py/ip6/icmpv6.t.json.output
index 7b8f5c1..f29b346 100644
--- a/tests/py/ip6/icmpv6.t.json.output
+++ b/tests/py/ip6/icmpv6.t.json.output
@@ -104,7 +104,7 @@
}
},
"op": "==",
- "right": "port-unreachable"
+ "right": 4
}
}
]
@@ -122,7 +122,7 @@
"op": "==",
"right": {
"range": [
- "addr-unreachable",
+ 3,
66
]
}
@@ -143,8 +143,8 @@
"op": "==",
"right": {
"set": [
- "policy-fail",
- "reject-route",
+ 5,
+ 6,
7
]
}
diff --git a/tests/py/ip6/icmpv6.t.payload.ip6 b/tests/py/ip6/icmpv6.t.payload.ip6
index fcaf481..5b6035d 100644
--- a/tests/py/ip6/icmpv6.t.payload.ip6
+++ b/tests/py/ip6/icmpv6.t.payload.ip6
@@ -554,7 +554,7 @@ ip6 test-ip6 input
[ payload load 2b @ transport header + 4 => reg 1 ]
[ lookup reg 1 set __set%d 0x1 ]
-# icmpv6 type parameter-problem icmpv6 code no-route
+# icmpv6 type parameter-problem icmpv6 code 0
ip6
[ meta load l4proto => reg 1 ]
[ cmp eq reg 1 0x0000003a ]

View File

@ -0,0 +1,36 @@
From 1b8794fe549b6e242b99eebf477a9508e25e6ac4 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:43 +0200
Subject: [PATCH] tests: shell: payload matching requires egress support
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit b13c0379bcf11caf64734240ec8fbcaa668c4016
commit b13c0379bcf11caf64734240ec8fbcaa668c4016
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Mon Apr 8 20:25:12 2024 +0200
tests: shell: payload matching requires egress support
Older kernels do not support for egress hook.
Fixes: 84da729e067a ("tests: shell: add test to cover payload transport match and mangle")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/shell/testcases/packetpath/payload | 2 ++
1 file changed, 2 insertions(+)
diff --git a/tests/shell/testcases/packetpath/payload b/tests/shell/testcases/packetpath/payload
index 9f4587d..4c5c42d 100755
--- a/tests/shell/testcases/packetpath/payload
+++ b/tests/shell/testcases/packetpath/payload
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_netdev_egress)
+
rnd=$(mktemp -u XXXXXXXX)
ns1="nft1payload-$rnd"
ns2="nft2payload-$rnd"

View File

@ -0,0 +1,47 @@
From 23fdf421ab37132dae2ed324f1e6d6f634d82f56 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:43 +0200
Subject: [PATCH] tests: shell: check for reset tcp options support
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 6a39fd3b68ac8a96340b87b9dc8380a9cd4e6398
commit 6a39fd3b68ac8a96340b87b9dc8380a9cd4e6398
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Mon Apr 8 23:05:28 2024 +0200
tests: shell: check for reset tcp options support
Fixes: 59a33d08ab3a ("parser: tcpopt: fix tcp option parsing with NUM + length field")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/shell/features/reset_tcp_options.nft | 5 +++++
tests/shell/testcases/packetpath/tcp_options | 2 ++
2 files changed, 7 insertions(+)
create mode 100644 tests/shell/features/reset_tcp_options.nft
diff --git a/tests/shell/features/reset_tcp_options.nft b/tests/shell/features/reset_tcp_options.nft
new file mode 100644
index 0000000..47d1c7b
--- /dev/null
+++ b/tests/shell/features/reset_tcp_options.nft
@@ -0,0 +1,5 @@
+table inet t {
+ chain c {
+ reset tcp option fastopen
+ }
+}
diff --git a/tests/shell/testcases/packetpath/tcp_options b/tests/shell/testcases/packetpath/tcp_options
index 8855222..57e228c 100755
--- a/tests/shell/testcases/packetpath/tcp_options
+++ b/tests/shell/testcases/packetpath/tcp_options
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_reset_tcp_options)
+
have_socat="no"
socat -h > /dev/null && have_socat="yes"

View File

@ -0,0 +1,51 @@
From dd80c4580464f4eb2f27b10d5a42f0e95499383d Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:43 +0200
Subject: [PATCH] doc: nft.8: Two minor synopsis fixups
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 3bccc478d27498f7ecc8a0233176accb1b91f584
commit 3bccc478d27498f7ecc8a0233176accb1b91f584
Author: Phil Sutter <phil@nwl.cc>
Date: Thu Mar 21 14:57:41 2024 +0100
doc: nft.8: Two minor synopsis fixups
The curly braces in 'add table' are to be put literally, so need to be
bold. Also, they are optional unless either one (or both) of 'comment'
and 'flags' are specified.
The 'add chain' synopsis contained a stray tick, messing up the
following markup.
Fixes: 7fd67ce121f86 ("doc: fix synopsis of named counter, quota and ct {helper,timeout,expect}")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
doc/nft.txt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/doc/nft.txt b/doc/nft.txt
index dba1b60..2080c07 100644
--- a/doc/nft.txt
+++ b/doc/nft.txt
@@ -321,7 +321,7 @@ Effectively, this is the nft-equivalent of *iptables-save* and
TABLES
------
[verse]
-{*add* | *create*} *table* ['family'] 'table' [ {*comment* 'comment' *;*'} *{ flags* 'flags' *; }*]
+{*add* | *create*} *table* ['family'] 'table' [*{* [*comment* 'comment' *;*] [*flags* 'flags' *;*] *}*]
{*delete* | *destroy* | *list* | *flush*} *table* ['family'] 'table'
*list tables* ['family']
*delete table* ['family'] *handle* 'handle'
@@ -385,7 +385,7 @@ add table inet mytable
CHAINS
------
[verse]
-{*add* | *create*} *chain* ['family'] 'table' 'chain' [*{ type* 'type' *hook* 'hook' [*device* 'device'] *priority* 'priority' *;* [*policy* 'policy' *;*] [*comment* 'comment' *;*'] *}*]
+{*add* | *create*} *chain* ['family'] 'table' 'chain' [*{ type* 'type' *hook* 'hook' [*device* 'device'] *priority* 'priority' *;* [*policy* 'policy' *;*] [*comment* 'comment' *;*] *}*]
{*delete* | *destroy* | *list* | *flush*} *chain* ['family'] 'table' 'chain'
*list chains* ['family']
*delete chain* ['family'] 'table' *handle* 'handle'

View File

@ -0,0 +1,94 @@
From 1e8d5235abba36fde530b3613cbea195c4f927f7 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:08:43 +0200
Subject: [PATCH] mergesort: Avoid accidental set element reordering
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit bf52af188b306acf5a30134d6a670f41f16a9459
Conflicts: Dropped changes to non-existent .json-nft dump
commit bf52af188b306acf5a30134d6a670f41f16a9459
Author: Phil Sutter <phil@nwl.cc>
Date: Fri Mar 22 13:31:10 2024 +0100
mergesort: Avoid accidental set element reordering
In corner cases, expr_msort_cmp() may return 0 for two non-identical
elements. An example are ORed tcp flags: 'syn' and 'syn | ack' are
considered the same value since expr_msort_value() reduces the latter to
its LHS.
Keeping the above in mind and looking at how list_expr_sort() works: The
list in 'head' is cut in half, the first half put into the temporary
list 'list' and finally 'list' is merged back into 'head' considering
each element's position. Shall expr_msort_cmp() return 0 for two
elements, the one from 'list' ends up after the one in 'head', thus
reverting their previous ordering.
The practical implication is that output never matches input for the
sample set '{ syn, syn | ack }' as the sorting after delinearization in
netlink_list_setelems() keeps swapping the elements. Out of coincidence,
the commit this fixes itself illustrates the use-case this breaks,
namely tracking a ruleset in git: Each ruleset reload will trigger an
update to the stored dump.
This change breaks interval set element deletion because __set_delete()
implicitly relies upon this reordering of duplicate entries by inserting
a clone of the one to delete into the start (via list_move()) and after
sorting assumes the clone will end up right behind the original. Fix
this by calling list_move_tail() instead.
Fixes: 14ee0a979b622 ("src: sort set elements in netlink_get_setelems()")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/intervals.c | 2 +-
src/mergesort.c | 2 +-
tests/shell/testcases/sets/dumps/0055tcpflags_0.nft | 8 ++++----
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/intervals.c b/src/intervals.c
index 85de019..d5406ef 100644
--- a/src/intervals.c
+++ b/src/intervals.c
@@ -461,7 +461,7 @@ static int __set_delete(struct list_head *msgs, struct expr *i, struct set *set,
unsigned int debug_mask)
{
i->flags |= EXPR_F_REMOVE;
- list_move(&i->list, &existing_set->init->expressions);
+ list_move_tail(&i->list, &existing_set->init->expressions);
list_expr_sort(&existing_set->init->expressions);
return setelem_delete(msgs, set, init, existing_set->init, debug_mask);
diff --git a/src/mergesort.c b/src/mergesort.c
index 4d0e280..5e676be 100644
--- a/src/mergesort.c
+++ b/src/mergesort.c
@@ -78,7 +78,7 @@ void list_splice_sorted(struct list_head *list, struct list_head *head)
while (l != list) {
if (h == head ||
expr_msort_cmp(list_entry(l, typeof(struct expr), list),
- list_entry(h, typeof(struct expr), list)) < 0) {
+ list_entry(h, typeof(struct expr), list)) <= 0) {
l = l->next;
list_add_tail(l->prev, h);
continue;
diff --git a/tests/shell/testcases/sets/dumps/0055tcpflags_0.nft b/tests/shell/testcases/sets/dumps/0055tcpflags_0.nft
index ffed542..22bf5c4 100644
--- a/tests/shell/testcases/sets/dumps/0055tcpflags_0.nft
+++ b/tests/shell/testcases/sets/dumps/0055tcpflags_0.nft
@@ -2,9 +2,9 @@ table ip test {
set tcp_good_flags {
type tcp_flag
flags constant
- elements = { fin | psh | ack | urg, fin | psh | ack, fin | ack | urg, fin | ack, syn | psh | ack | urg,
- syn | psh | ack, syn | ack | urg, syn | ack, syn, rst | psh | ack | urg,
- rst | psh | ack, rst | ack | urg, rst | ack, rst, psh | ack | urg,
- psh | ack, ack | urg, ack }
+ elements = { fin | ack, fin | ack | urg, fin | psh | ack, fin | psh | ack | urg, syn,
+ syn | ack, syn | ack | urg, syn | psh | ack, syn | psh | ack | urg, rst,
+ rst | ack, rst | ack | urg, rst | psh | ack, rst | psh | ack | urg, psh | ack,
+ psh | ack | urg, ack, ack | urg }
}
}

View File

@ -0,0 +1,37 @@
From 481a7da59f20b9c1322a5d26a9bce3e94f04443d Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:09:25 +0200
Subject: [PATCH] doc: nft.8: Fix markup in ct expectation synopsis
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit ef659df2a45c38ad18f703febeae8c4febf9dd04
commit ef659df2a45c38ad18f703febeae8c4febf9dd04
Author: Phil Sutter <phil@nwl.cc>
Date: Wed Apr 24 23:46:11 2024 +0200
doc: nft.8: Fix markup in ct expectation synopsis
Just a missing asterisk somewhere.
Fixes: 1dd08fcfa07a4 ("src: add ct expectations support")
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
doc/stateful-objects.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/stateful-objects.txt b/doc/stateful-objects.txt
index 00d3c5f..5824d53 100644
--- a/doc/stateful-objects.txt
+++ b/doc/stateful-objects.txt
@@ -119,7 +119,7 @@ sport=41360 dport=22
CT EXPECTATION
~~~~~~~~~~~~~~
[verse]
-*add* *ct expectation* ['family'] 'table' 'name' *{ protocol* 'protocol' *; dport* 'dport' *; timeout* 'timeout' *; size* 'size' *; [*l3proto* 'family' *;*] *}*
+*add* *ct expectation* ['family'] 'table' 'name' *{ protocol* 'protocol' *; dport* 'dport' *; timeout* 'timeout' *; size* 'size' *;* [*l3proto* 'family' *;*] *}*
*delete* *ct expectation* ['family'] 'table' 'name'
*list* *ct expectations*

View File

@ -0,0 +1,76 @@
From 694638b161288c479b28005db67c403903182a66 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:09:25 +0200
Subject: [PATCH] cache: check for NFT_CACHE_REFRESH in current requested cache
too
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit bbb0e944b59d355085e8f50b4b7b5057ae0d33a4
commit bbb0e944b59d355085e8f50b4b7b5057ae0d33a4
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Mon May 27 18:12:05 2024 +0200
cache: check for NFT_CACHE_REFRESH in current requested cache too
NFT_CACHE_REFRESH is set on inconditionally by ruleset list commands to
deal with stateful information in this ruleset. This flag results in
dropping the existing cache and fully fetching all objects from the
kernel.
Set on this flag for reset commands too, this is missing.
List/reset commands allow for filtering by specific family and object,
therefore, NFT_CACHE_REFRESH also signals that the cache is partially
populated.
Check if this flag is requested by the current list/reset command, as
well as cache->flags which represents the cache after the _previous_
list of commands.
A follow up patch allows to recycle the existing cache if the flags
report that the same objects are already available in the cache,
NFT_CACHE_REFRESH is useful to report that cache cannot be recycled.
Fixes: 407c54f71255 ("src: cache gets out of sync in interactive mode")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/cache.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/cache.c b/src/cache.c
index c000e32..e88cbae 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -297,6 +297,7 @@ static unsigned int evaluate_cache_reset(struct cmd *cmd, unsigned int flags,
flags |= NFT_CACHE_TABLE;
break;
}
+ flags |= NFT_CACHE_REFRESH;
return flags;
}
@@ -1177,9 +1178,10 @@ static bool nft_cache_is_complete(struct nft_cache *cache, unsigned int flags)
return (cache->flags & flags) == flags;
}
-static bool nft_cache_needs_refresh(struct nft_cache *cache)
+static bool nft_cache_needs_refresh(struct nft_cache *cache, unsigned int flags)
{
- return cache->flags & NFT_CACHE_REFRESH;
+ return (cache->flags & NFT_CACHE_REFRESH) ||
+ (flags & NFT_CACHE_REFRESH);
}
static bool nft_cache_is_updated(struct nft_cache *cache, uint16_t genid)
@@ -1207,7 +1209,7 @@ int nft_cache_update(struct nft_ctx *nft, unsigned int flags,
replay:
ctx.seqnum = cache->seqnum++;
genid = mnl_genid_get(&ctx);
- if (!nft_cache_needs_refresh(cache) &&
+ if (!nft_cache_needs_refresh(cache, flags) &&
nft_cache_is_complete(cache, flags) &&
nft_cache_is_updated(cache, genid))
return 0;

View File

@ -0,0 +1,227 @@
From da2b7b622c703aafa4fb42442b5b74da9828f842 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:09:25 +0200
Subject: [PATCH] evaluate: bogus protocol conflicts in vlan with implicit
dependencies
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit d1a7e74d1e065d244439fdb0f1c1cba83f921609
commit d1a7e74d1e065d244439fdb0f1c1cba83f921609
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Wed May 15 19:23:49 2024 +0200
evaluate: bogus protocol conflicts in vlan with implicit dependencies
The following command:
# nft add rule netdev x y ip saddr 10.1.1.1 icmp type echo-request vlan id set 321
fails with:
Error: conflicting link layer protocols specified: ether vs. vlan
netdev x y ip saddr 10.1.1.1 icmp type echo-request vlan id set 321
^^^^^^^
Users can work around this issue by prepending an explicit match for
vlan ethertype field, that is:
ether type vlan ip saddr 10.1.1.1 ...
^-------------^
but nft should really handle this itself.
The error above is triggered by the following check in
resolve_ll_protocol_conflict():
/* This payload and the existing context don't match, conflict. */
if (pctx->protocol[base + 1].desc != NULL)
return 1;
This check was added by 39f15c243912 ("nft: support listing expressions
that use non-byte header fields") and f7d5590688a6 ("tests: vlan tests")
to deal with conflicting link layer protocols, for instance:
ether type ip vlan id 1
this is matching ethertype ip at offset 12, but then it matches for vlan
id at offset 14 which is not present given the previous check.
One possibility is to remove such check, but nft does not bail out for
the example above and it results in bytecode that never matches:
# nft --debug=netlink netdev x y ether type ip vlan id 10
netdev x y
[ meta load iiftype => reg 1 ]
[ cmp eq reg 1 0x00000001 ]
[ payload load 2b @ link header + 12 => reg 1 ] <---- ether type
[ cmp eq reg 1 0x00000008 ] <---- ip
[ payload load 2b @ link header + 12 => reg 1 ] <---- ether type
[ cmp eq reg 1 0x00000081 ] <---- vlan
[ payload load 2b @ link header + 14 => reg 1 ]
[ bitwise reg 1 = ( reg 1 & 0x0000ff0f ) ^ 0x00000000 ]
[ cmp eq reg 1 0x00000a00 ]
This is due to resolve_ll_protocol_conflict() which deals with the
conflict by updating protocol context and emitting an implicit
dependency, but there is already an explicit match coming from the user.
This patch adds a new helper function to check if an implicit dependency
clashes with an existing statement, which results in:
# nft add rule netdev x y ether type ip vlan id 1
Error: conflicting statements
add rule netdev x y ether type ip vlan id 1
^^^^^^^^^^^^^ ~~~~~~~
Theoretically, no duplicated implicit dependency should ever be emitted
if protocol context is correctly handled.
Only implicit payload expressions are considered at this stage for this
conflict check, this patch can be extended to deal with other dependency
types.
Fixes: 39f15c243912 ("nft: support listing expressions that use non-byte header fields")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 69 +++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 57 insertions(+), 12 deletions(-)
diff --git a/src/evaluate.c b/src/evaluate.c
index 38a80f2..d8e5dfd 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -759,6 +759,46 @@ static bool proto_is_dummy(const struct proto_desc *desc)
return desc == &proto_inet || desc == &proto_netdev;
}
+static int stmt_dep_conflict(struct eval_ctx *ctx, const struct stmt *nstmt)
+{
+ struct stmt *stmt;
+
+ list_for_each_entry(stmt, &ctx->rule->stmts, list) {
+ if (stmt == nstmt)
+ break;
+
+ if (stmt->ops->type != STMT_EXPRESSION ||
+ stmt->expr->etype != EXPR_RELATIONAL ||
+ stmt->expr->right->etype != EXPR_VALUE ||
+ stmt->expr->left->etype != EXPR_PAYLOAD ||
+ stmt->expr->left->etype != nstmt->expr->left->etype ||
+ stmt->expr->left->len != nstmt->expr->left->len)
+ continue;
+
+ if (stmt->expr->left->payload.desc != nstmt->expr->left->payload.desc ||
+ stmt->expr->left->payload.inner_desc != nstmt->expr->left->payload.inner_desc ||
+ stmt->expr->left->payload.base != nstmt->expr->left->payload.base ||
+ stmt->expr->left->payload.offset != nstmt->expr->left->payload.offset)
+ continue;
+
+ return stmt_binary_error(ctx, stmt, nstmt,
+ "conflicting statements");
+ }
+
+ return 0;
+}
+
+static int rule_stmt_dep_add(struct eval_ctx *ctx,
+ struct stmt *nstmt, struct stmt *stmt)
+{
+ rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+
+ if (stmt_dep_conflict(ctx, nstmt) < 0)
+ return -1;
+
+ return 0;
+}
+
static int resolve_ll_protocol_conflict(struct eval_ctx *ctx,
const struct proto_desc *desc,
struct expr *payload)
@@ -782,7 +822,8 @@ static int resolve_ll_protocol_conflict(struct eval_ctx *ctx,
return err;
desc = payload->payload.desc;
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+ if (rule_stmt_dep_add(ctx, nstmt, ctx->stmt) < 0)
+ return -1;
}
} else {
unsigned int i;
@@ -794,10 +835,6 @@ static int resolve_ll_protocol_conflict(struct eval_ctx *ctx,
}
}
- /* This payload and the existing context don't match, conflict. */
- if (pctx->protocol[base + 1].desc != NULL)
- return 1;
-
link = proto_find_num(desc, payload->payload.desc);
if (link < 0 ||
ll_conflict_resolution_gen_dependency(ctx, link, payload, &nstmt) < 0)
@@ -806,7 +843,8 @@ static int resolve_ll_protocol_conflict(struct eval_ctx *ctx,
for (i = 0; i < pctx->stacked_ll_count; i++)
payload->payload.offset += pctx->stacked_ll[i]->length;
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+ if (rule_stmt_dep_add(ctx, nstmt, ctx->stmt) < 0)
+ return -1;
return 0;
}
@@ -834,7 +872,8 @@ static int __expr_evaluate_payload(struct eval_ctx *ctx, struct expr *expr)
if (payload_gen_dependency(ctx, payload, &nstmt) < 0)
return -1;
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+ if (rule_stmt_dep_add(ctx, nstmt, ctx->stmt) < 0)
+ return -1;
desc = pctx->protocol[base].desc;
@@ -854,7 +893,10 @@ static int __expr_evaluate_payload(struct eval_ctx *ctx, struct expr *expr)
assert(pctx->stacked_ll_count);
payload->payload.offset += pctx->stacked_ll[0]->length;
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+
+ if (rule_stmt_dep_add(ctx, nstmt, ctx->stmt) < 0)
+ return -1;
+
return 1;
}
goto check_icmp;
@@ -895,8 +937,8 @@ check_icmp:
if (payload_gen_icmp_dependency(ctx, expr, &nstmt) < 0)
return -1;
- if (nstmt)
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+ if (nstmt && rule_stmt_dep_add(ctx, nstmt, ctx->stmt) < 0)
+ return -1;
return 0;
}
@@ -968,7 +1010,8 @@ static int expr_evaluate_inner(struct eval_ctx *ctx, struct expr **exprp)
if (payload_gen_inner_dependency(ctx, expr, &nstmt) < 0)
return -1;
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+ if (rule_stmt_dep_add(ctx, nstmt, ctx->stmt) < 0)
+ return -1;
proto_ctx_update(pctx, PROTO_BASE_TRANSPORT_HDR, &expr->location, expr->payload.inner_desc);
}
@@ -1099,7 +1142,9 @@ static int ct_gen_nh_dependency(struct eval_ctx *ctx, struct expr *ct)
relational_expr_pctx_update(pctx, dep);
nstmt = expr_stmt_alloc(&dep->location, dep);
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+
+ if (rule_stmt_dep_add(ctx, nstmt, ctx->stmt) < 0)
+ return -1;
return 0;
}

View File

@ -0,0 +1,39 @@
From e4df7c0617f8ed9d9ef2afcbd5acd398f7582b7d Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:09:25 +0200
Subject: [PATCH] evaluate: Fix incorrect checking the `base` variable in case
of IPV6
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit f6b579344eee17e5587b6a7fcc444fe997cd8cb6
commit f6b579344eee17e5587b6a7fcc444fe997cd8cb6
Author: Maks Mishin <maks.mishinfz@gmail.com>
Date: Wed May 15 23:25:03 2024 +0300
evaluate: Fix incorrect checking the `base` variable in case of IPV6
Found by RASU JSC.
Fixes: 2b29ea5f3c3e ("src: ct: add eval part to inject dependencies for ct saddr/daddr")
Signed-off-by: Maks Mishin <maks.mishinFZ@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/evaluate.c b/src/evaluate.c
index d8e5dfd..7f5b64b 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1106,7 +1106,7 @@ static int ct_gen_nh_dependency(struct eval_ctx *ctx, struct expr *ct)
base = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if (base == &proto_ip)
ct->ct.nfproto = NFPROTO_IPV4;
- else if (base == &proto_ip)
+ else if (base == &proto_ip6)
ct->ct.nfproto = NFPROTO_IPV6;
if (base)

View File

@ -0,0 +1,106 @@
From b42e655bbc4f9068a450fadacca2e6c50bc7f13c Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:09:25 +0200
Subject: [PATCH] scanner: inet_pton() allows for broader IPv4-Mapped IPv6
addresses
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit fda913d712925e8a8d6460b361d49774dc5d34b8
commit fda913d712925e8a8d6460b361d49774dc5d34b8
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue Jun 4 20:01:51 2024 +0200
scanner: inet_pton() allows for broader IPv4-Mapped IPv6 addresses
inet_pton() allows for broader IPv4-Mapped IPv6 address syntax than
those specified by rfc4291 Sect.2.5.5. This patch extends the scanner to
support them for compatibility reasons. This allows to represent the
last 4 bytes of an IPv6 address as an IPv4 address.
Closes: https://bugzilla.netfilter.org/show_bug.cgi?id=1730
Fixes: fd513de78bc0 ("scanner: IPv4-Mapped IPv6 addresses support")
Fixes: 3f82ef3d0dbf ("scanner: Support rfc4291 IPv4-compatible addresses")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/scanner.l | 47 +++++++++++++++++++++++------------------------
1 file changed, 23 insertions(+), 24 deletions(-)
diff --git a/src/scanner.l b/src/scanner.l
index 00a0948..28d6da3 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -132,48 +132,47 @@ slash \/
timestring ([0-9]+d)?([0-9]+h)?([0-9]+m)?([0-9]+s)?([0-9]+ms)?
hex4 ([[:xdigit:]]{1,4})
+rfc4291_broader (((:{hex4}){2})|(:{ip4addr}))
v680 (({hex4}:){7}{hex4})
-v670 ((:)((:{hex4}){7}))
-v671 ((({hex4}:){1})((:{hex4}){6}))
-v672 ((({hex4}:){2})((:{hex4}){5}))
-v673 ((({hex4}:){3})((:{hex4}){4}))
-v674 ((({hex4}:){4})((:{hex4}){3}))
-v675 ((({hex4}:){5})((:{hex4}){2}))
+v670 ((:)((:{hex4}){5}){rfc4291_broader})
+v671 ((({hex4}:){1})((:{hex4}){4}){rfc4291_broader})
+v672 ((({hex4}:){2})((:{hex4}){3}){rfc4291_broader})
+v673 ((({hex4}:){3})((:{hex4}){2}){rfc4291_broader})
+v674 ((({hex4}:){4})((:{hex4}){1}){rfc4291_broader})
+v675 ((({hex4}:){5}){rfc4291_broader})
v676 ((({hex4}:){6})(:{hex4}{1}))
v677 ((({hex4}:){7})(:))
v67 ({v670}|{v671}|{v672}|{v673}|{v674}|{v675}|{v676}|{v677})
-v660 ((:)((:{hex4}){6}))
-v661 ((({hex4}:){1})((:{hex4}){5}))
-v662 ((({hex4}:){2})((:{hex4}){4}))
-v663 ((({hex4}:){3})((:{hex4}){3}))
-v664 ((({hex4}:){4})((:{hex4}){2}))
+v660 ((:)((:{hex4}){4}){rfc4291_broader})
+v661 ((({hex4}:){1})((:{hex4}){3}){rfc4291_broader})
+v662 ((({hex4}:){2})((:{hex4}){2}){rfc4291_broader})
+v663 ((({hex4}:){3})((:{hex4}){1}){rfc4291_broader})
+v664 ((({hex4}:){4}){rfc4291_broader})
v665 ((({hex4}:){5})((:{hex4}){1}))
v666 ((({hex4}:){6})(:))
v66 ({v660}|{v661}|{v662}|{v663}|{v664}|{v665}|{v666})
-v650 ((:)((:{hex4}){5}))
-v651 ((({hex4}:){1})((:{hex4}){4}))
-v652 ((({hex4}:){2})((:{hex4}){3}))
-v653 ((({hex4}:){3})((:{hex4}){2}))
+v650 ((:)((:{hex4}){3}){rfc4291_broader})
+v651 ((({hex4}:){1})((:{hex4}){2}){rfc4291_broader})
+v652 ((({hex4}:){2})((:{hex4}){1}){rfc4291_broader})
+v653 ((({hex4}:){3}){rfc4291_broader})
v654 ((({hex4}:){4})(:{hex4}{1}))
v655 ((({hex4}:){5})(:))
v65 ({v650}|{v651}|{v652}|{v653}|{v654}|{v655})
-v640 ((:)((:{hex4}){4}))
-v641 ((({hex4}:){1})((:{hex4}){3}))
-v642 ((({hex4}:){2})((:{hex4}){2}))
+v640 ((:)((:{hex4}){2}){rfc4291_broader})
+v641 ((({hex4}:){1})((:{hex4}){1}){rfc4291_broader})
+v642 ((({hex4}:){2}){rfc4291_broader})
v643 ((({hex4}:){3})((:{hex4}){1}))
v644 ((({hex4}:){4})(:))
v64 ({v640}|{v641}|{v642}|{v643}|{v644})
-v630 ((:)((:{hex4}){3}))
-v631 ((({hex4}:){1})((:{hex4}){2}))
+v630 ((:)((:{hex4}){1}){rfc4291_broader})
+v631 ((({hex4}:){1}){rfc4291_broader})
v632 ((({hex4}:){2})((:{hex4}){1}))
v633 ((({hex4}:){3})(:))
v63 ({v630}|{v631}|{v632}|{v633})
-v620 ((:)((:{hex4}){2}))
-v620_rfc4291 ((:)(:{ip4addr}))
+v620 ((:){rfc4291_broader})
v621 ((({hex4}:){1})((:{hex4}){1}))
v622 ((({hex4}:){2})(:))
-v62_rfc4291 ((:)(:[fF]{4})(:{ip4addr}))
-v62 ({v620}|{v621}|{v622}|{v62_rfc4291}|{v620_rfc4291})
+v62 ({v620}|{v621}|{v622})
v610 ((:)(:{hex4}{1}))
v611 ((({hex4}:){1})(:))
v61 ({v610}|{v611})

View File

@ -0,0 +1,42 @@
From ebe774e477b51465c58e58599a23872b82ec644c Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:09:25 +0200
Subject: [PATCH] monitor: too large shift exponent displaying payload
expression
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 016f37f1268fa1003c46c66655697d3f58d86598
commit 016f37f1268fa1003c46c66655697d3f58d86598
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Mon Jun 10 19:08:20 2024 +0200
monitor: too large shift exponent displaying payload expression
ASAN reports too large shift exponent when displaying traces for raw
payload expression:
trace id ec23e848 ip x y packet: oif "wlan0" src/netlink.c:2100:32: runtime error: shift exponent 1431657095 is too large for 32-bit type 'int'
skip if proto_unknown_template is set on in this payload expression.
Fixes: be5d9120e81e ("nft monitor [ trace ]")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/netlink.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/netlink.c b/src/netlink.c
index 8637186..72f4ba4 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -2082,6 +2082,7 @@ restart:
/* Skip unknown and filtered expressions */
desc = lhs->payload.desc;
if (lhs->dtype == &invalid_type ||
+ lhs->payload.tmpl == &proto_unknown_template ||
desc->checksum_key == payload_hdr_field(lhs) ||
desc->format.filter & (1 << payload_hdr_field(lhs))) {
expr_free(lhs);

View File

@ -0,0 +1,65 @@
From c296c3d263b6bd897fd807ad174d33a3bfd2e015 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:09:25 +0200
Subject: [PATCH] cmd: provide better hint if chain is already declared with
different type/hook/priority
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 1f321f86c45fce88a5bcd6f8eafa0157248c8b38
commit 1f321f86c45fce88a5bcd6f8eafa0157248c8b38
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Mon Jun 10 19:36:21 2024 +0200
cmd: provide better hint if chain is already declared with different type/hook/priority
Display the following error in such case:
ruleset.nft:7:9-52: Error: Chain "input" already exists in table ip 'filter' with different declaration
type filter hook postrouting priority filter;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
instead of reporting a misleading unsupported chain type when updating
an existing chain with different type/hook/priority.
Fixes: 573788e05363 ("src: improve error reporting for unsupported chain type")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/cmd.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/src/cmd.c b/src/cmd.c
index 21533e0..de11468 100644
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -256,7 +256,8 @@ static void nft_cmd_enoent(struct netlink_ctx *ctx, const struct cmd *cmd,
static int nft_cmd_chain_error(struct netlink_ctx *ctx, struct cmd *cmd,
struct mnl_err *err)
{
- struct chain *chain = cmd->chain;
+ struct chain *chain = cmd->chain, *existing_chain;
+ const struct table *table;
int priority;
switch (err->err) {
@@ -270,6 +271,18 @@ static int nft_cmd_chain_error(struct netlink_ctx *ctx, struct cmd *cmd,
return netlink_io_error(ctx, &chain->priority.loc,
"Chains of type \"nat\" must have a priority value above -200");
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ cmd->handle.table.name, cmd->handle.family);
+ if (table) {
+ existing_chain = chain_cache_find(table, cmd->handle.chain.name);
+ if (existing_chain && existing_chain != chain &&
+ !strcmp(existing_chain->handle.chain.name, chain->handle.chain.name))
+ return netlink_io_error(ctx, &chain->loc,
+ "Chain \"%s\" already exists in table %s '%s' with different declaration",
+ chain->handle.chain.name,
+ family2str(table->handle.family), table->handle.table.name);
+ }
+
return netlink_io_error(ctx, &chain->loc,
"Chain of type \"%s\" is not supported, perhaps kernel support is missing?",
chain->type.str);

View File

@ -0,0 +1,88 @@
From 5544e2dd7b3f219d54b06dfc779bdff4646ac420 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:09:25 +0200
Subject: [PATCH] cmd: skip variable set elements when collapsing commands
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit be055af5c58d9a4751990684d8f83b85082ca443
Conflicts: Dropped changes to non-existent .json-nft dump
commit be055af5c58d9a4751990684d8f83b85082ca443
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue Jun 11 17:40:23 2024 +0200
cmd: skip variable set elements when collapsing commands
ASAN reports an issue when collapsing commands that represent an element
through a variable:
include/list.h:60:13: runtime error: member access within null pointer of type 'struct list_head'
AddressSanitizer:DEADLYSIGNAL
=================================================================
==11398==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7ffb77cf09c2 bp 0x7ffc818267c0 sp 0x7ffc818267a0 T0)
==11398==The signal is caused by a WRITE memory access.
==11398==Hint: address points to the zero page.
#0 0x7ffb77cf09c2 in __list_add include/list.h:60
#1 0x7ffb77cf0ad9 in list_add_tail include/list.h:87
#2 0x7ffb77cf0e72 in list_move_tail include/list.h:169
#3 0x7ffb77cf86ad in nft_cmd_collapse src/cmd.c:478
#4 0x7ffb77da9f16 in nft_evaluate src/libnftables.c:531
#5 0x7ffb77dac471 in __nft_run_cmd_from_filename src/libnftables.c:720
#6 0x7ffb77dad703 in nft_run_cmd_from_filename src/libnftables.c:807
Skip such commands to address this issue.
This patch also extends tests/shell to cover for this bug.
Closes: https://bugzilla.netfilter.org/show_bug.cgi?id=1754
Fixes: 498a5f0c219d ("rule: collapse set element commands")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/cmd.c | 3 +++
tests/shell/testcases/sets/collapse_elem_0 | 6 ++++++
tests/shell/testcases/sets/dumps/collapse_elem_0.nft | 2 +-
3 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/src/cmd.c b/src/cmd.c
index de11468..8f465de 100644
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -455,6 +455,9 @@ bool nft_cmd_collapse(struct list_head *cmds)
continue;
}
+ if (cmd->expr->etype == EXPR_VARIABLE)
+ continue;
+
if (!elems) {
elems = cmd;
continue;
diff --git a/tests/shell/testcases/sets/collapse_elem_0 b/tests/shell/testcases/sets/collapse_elem_0
index 7699e9d..52a42c2 100755
--- a/tests/shell/testcases/sets/collapse_elem_0
+++ b/tests/shell/testcases/sets/collapse_elem_0
@@ -17,3 +17,9 @@ add element ip a x { 2 }
add element ip6 a x { 2 }"
$NFT -f - <<< $RULESET
+
+RULESET="define m = { 3, 4 }
+add element ip a x \$m
+add element ip a x { 5 }"
+
+$NFT -f - <<< $RULESET
diff --git a/tests/shell/testcases/sets/dumps/collapse_elem_0.nft b/tests/shell/testcases/sets/dumps/collapse_elem_0.nft
index a3244fc..775f0ab 100644
--- a/tests/shell/testcases/sets/dumps/collapse_elem_0.nft
+++ b/tests/shell/testcases/sets/dumps/collapse_elem_0.nft
@@ -1,7 +1,7 @@
table ip a {
set x {
type inet_service
- elements = { 1, 2 }
+ elements = { 1, 2, 3, 4, 5 }
}
}
table ip6 a {

View File

@ -0,0 +1,51 @@
From 09ae9247749aa7768655696fd099561322b1a90e Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:09:42 +0200
Subject: [PATCH] tests: shell: skip ip option tests if kernel does not support
it
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit c624578ee18131492e7f72b662d5faf7c042e0d8
commit c624578ee18131492e7f72b662d5faf7c042e0d8
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Thu Jun 13 00:37:48 2024 +0200
tests: shell: skip ip option tests if kernel does not support it
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
tests/shell/features/ip_options.nft | 8 ++++++++
tests/shell/testcases/sets/typeof_sets_0 | 2 ++
2 files changed, 10 insertions(+)
create mode 100644 tests/shell/features/ip_options.nft
diff --git a/tests/shell/features/ip_options.nft b/tests/shell/features/ip_options.nft
new file mode 100644
index 0000000..0b8cb09
--- /dev/null
+++ b/tests/shell/features/ip_options.nft
@@ -0,0 +1,8 @@
+# dbb5281a1f84 ("netfilter: nf_tables: add support for matching IPv4 options")
+# v5.3-rc1~140^2~153^2~1
+
+table ip x {
+ chain y {
+ ip option ra value 255
+ }
+}
diff --git a/tests/shell/testcases/sets/typeof_sets_0 b/tests/shell/testcases/sets/typeof_sets_0
index 943c9c2..3a4ed94 100755
--- a/tests/shell/testcases/sets/typeof_sets_0
+++ b/tests/shell/testcases/sets/typeof_sets_0
@@ -4,6 +4,8 @@
# s1 and s2 are identical, they just use different
# ways for declaration.
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_ip_options)
+
set -e
die() {

View File

@ -0,0 +1,607 @@
From cf8cedf79d49736dad61f01628a0cf738fefad26 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:09:42 +0200
Subject: [PATCH] src: add string preprocessor and use it for log prefix string
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 081bf5f0d7952a6e6ac0d23a365ccf1fd27010c0
Conflicts: Manually applied Makefile.am change due to missing commit
11e62138424ad ("build: no recursive make for "src/Makefile.am"")
commit 081bf5f0d7952a6e6ac0d23a365ccf1fd27010c0
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue Jun 18 14:26:31 2024 +0200
src: add string preprocessor and use it for log prefix string
Add a string preprocessor to identify and replace variables in a string.
Rework existing support to variables in log prefix strings to use it.
Fixes: e76bb3794018 ("src: allow for variables in the log prefix string")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
include/expression.h | 2 -
include/parser.h | 4 +
include/statement.h | 2 +-
src/Makefile.am | 1 +
src/evaluate.c | 45 +---------
src/expression.c | 9 --
src/json.c | 7 +-
src/netlink_delinearize.c | 6 +-
src/netlink_linearize.c | 7 +-
src/optimize.c | 6 +-
src/parser_bison.y | 126 ++--------------------------
src/parser_json.c | 4 +-
src/preprocess.c | 168 ++++++++++++++++++++++++++++++++++++++
src/statement.c | 10 +--
14 files changed, 198 insertions(+), 199 deletions(-)
create mode 100644 src/preprocess.c
diff --git a/include/expression.h b/include/expression.h
index 809089c..e143e71 100644
--- a/include/expression.h
+++ b/include/expression.h
@@ -414,8 +414,6 @@ extern const struct datatype *expr_basetype(const struct expr *expr);
extern void expr_set_type(struct expr *expr, const struct datatype *dtype,
enum byteorder byteorder);
-void expr_to_string(const struct expr *expr, char *string);
-
struct eval_ctx;
extern int expr_binary_error(struct list_head *msgs,
const struct expr *e1, const struct expr *e2,
diff --git a/include/parser.h b/include/parser.h
index f79a22f..576e5e4 100644
--- a/include/parser.h
+++ b/include/parser.h
@@ -112,4 +112,8 @@ extern void scanner_push_buffer(void *scanner,
extern void scanner_pop_start_cond(void *scanner, enum startcond_type sc);
+const char *str_preprocess(struct parser_state *state, struct location *loc,
+ struct scope *scope, const char *x,
+ struct error_record **rec);
+
#endif /* NFTABLES_PARSER_H */
diff --git a/include/statement.h b/include/statement.h
index 662f99d..9376911 100644
--- a/include/statement.h
+++ b/include/statement.h
@@ -90,7 +90,7 @@ enum {
};
struct log_stmt {
- struct expr *prefix;
+ const char *prefix;
unsigned int snaplen;
uint16_t group;
uint16_t qthreshold;
diff --git a/src/Makefile.am b/src/Makefile.am
index 63a4ef4..6d7bb89 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -75,6 +75,7 @@ libnftables_la_SOURCES = \
nfnl_osf.c \
tcpopt.c \
socket.c \
+ preprocess.c \
print.c \
sctp_chunk.c \
dccpopt.c \
diff --git a/src/evaluate.c b/src/evaluate.c
index 7f5b64b..c28f693 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -4292,49 +4292,12 @@ static int stmt_evaluate_queue(struct eval_ctx *ctx, struct stmt *stmt)
static int stmt_evaluate_log_prefix(struct eval_ctx *ctx, struct stmt *stmt)
{
- char tmp[NF_LOG_PREFIXLEN] = {};
- char prefix[NF_LOG_PREFIXLEN];
- size_t len = sizeof(prefix);
- size_t offset = 0;
- struct expr *expr;
-
- if (stmt->log.prefix->etype != EXPR_LIST) {
- if (stmt->log.prefix &&
- div_round_up(stmt->log.prefix->len, BITS_PER_BYTE) >= NF_LOG_PREFIXLEN)
- return expr_error(ctx->msgs, stmt->log.prefix, "log prefix is too long");
-
- return 0;
- }
-
- prefix[0] = '\0';
-
- list_for_each_entry(expr, &stmt->log.prefix->expressions, list) {
- int ret;
-
- switch (expr->etype) {
- case EXPR_VALUE:
- expr_to_string(expr, tmp);
- ret = snprintf(prefix + offset, len, "%s", tmp);
- break;
- case EXPR_VARIABLE:
- ret = snprintf(prefix + offset, len, "%s",
- expr->sym->expr->identifier);
- break;
- default:
- BUG("unknown expression type %s\n", expr_name(expr));
- break;
- }
- SNPRINTF_BUFFER_SIZE(ret, &len, &offset);
- }
+ unsigned int len = strlen(stmt->log.prefix);
- if (len == 0)
+ if (len >= NF_LOG_PREFIXLEN)
return stmt_error(ctx, stmt, "log prefix is too long");
-
- expr = constant_expr_alloc(&stmt->log.prefix->location, &string_type,
- BYTEORDER_HOST_ENDIAN,
- strlen(prefix) * BITS_PER_BYTE, prefix);
- expr_free(stmt->log.prefix);
- stmt->log.prefix = expr;
+ else if (len == 0)
+ return stmt_error(ctx, stmt, "log prefix must have a minimum length of 1 character");
return 0;
}
diff --git a/src/expression.c b/src/expression.c
index cb2573f..992f510 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -183,15 +183,6 @@ void expr_describe(const struct expr *expr, struct output_ctx *octx)
}
}
-void expr_to_string(const struct expr *expr, char *string)
-{
- int len = expr->len / BITS_PER_BYTE;
-
- assert(expr->dtype == &string_type);
-
- mpz_export_data(string, expr->value, BYTEORDER_HOST_ENDIAN, len);
-}
-
void expr_set_type(struct expr *expr, const struct datatype *dtype,
enum byteorder byteorder)
{
diff --git a/src/json.c b/src/json.c
index b3e1e4e..5faeb4d 100644
--- a/src/json.c
+++ b/src/json.c
@@ -1319,12 +1319,9 @@ json_t *log_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
json_t *root = json_object(), *flags;
- if (stmt->log.flags & STMT_LOG_PREFIX) {
- char prefix[NF_LOG_PREFIXLEN] = {};
+ if (stmt->log.flags & STMT_LOG_PREFIX)
+ json_object_set_new(root, "prefix", json_string(stmt->log.prefix));
- expr_to_string(stmt->log.prefix, prefix);
- json_object_set_new(root, "prefix", json_string(prefix));
- }
if (stmt->log.flags & STMT_LOG_GROUP)
json_object_set_new(root, "group",
json_integer(stmt->log.group));
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 1af0278..0ea10ee 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -1090,11 +1090,7 @@ static void netlink_parse_log(struct netlink_parse_ctx *ctx,
stmt = log_stmt_alloc(loc);
prefix = nftnl_expr_get_str(nle, NFTNL_EXPR_LOG_PREFIX);
if (nftnl_expr_is_set(nle, NFTNL_EXPR_LOG_PREFIX)) {
- stmt->log.prefix = constant_expr_alloc(&internal_location,
- &string_type,
- BYTEORDER_HOST_ENDIAN,
- (strlen(prefix) + 1) * BITS_PER_BYTE,
- prefix);
+ stmt->log.prefix = xstrdup(prefix);
stmt->log.flags |= STMT_LOG_PREFIX;
}
if (nftnl_expr_is_set(nle, NFTNL_EXPR_LOG_GROUP)) {
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index df395ba..a2a1cd5 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -1141,12 +1141,9 @@ static void netlink_gen_log_stmt(struct netlink_linearize_ctx *ctx,
struct nftnl_expr *nle;
nle = alloc_nft_expr("log");
- if (stmt->log.prefix != NULL) {
- char prefix[NF_LOG_PREFIXLEN] = {};
+ if (stmt->log.prefix != NULL)
+ nftnl_expr_set_str(nle, NFTNL_EXPR_LOG_PREFIX, stmt->log.prefix);
- expr_to_string(stmt->log.prefix, prefix);
- nftnl_expr_set_str(nle, NFTNL_EXPR_LOG_PREFIX, prefix);
- }
if (stmt->log.flags & STMT_LOG_GROUP) {
nftnl_expr_set_u16(nle, NFTNL_EXPR_LOG_GROUP, stmt->log.group);
if (stmt->log.flags & STMT_LOG_SNAPLEN)
diff --git a/src/optimize.c b/src/optimize.c
index b90dd99..1dd0858 100644
--- a/src/optimize.c
+++ b/src/optimize.c
@@ -215,9 +215,7 @@ static bool __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b,
if (!stmt_a->log.prefix)
return true;
- if (stmt_a->log.prefix->etype != EXPR_VALUE ||
- stmt_b->log.prefix->etype != EXPR_VALUE ||
- mpz_cmp(stmt_a->log.prefix->value, stmt_b->log.prefix->value))
+ if (strcmp(stmt_a->log.prefix, stmt_b->log.prefix))
return false;
break;
case STMT_REJECT:
@@ -406,7 +404,7 @@ static int rule_collect_stmts(struct optimize_ctx *ctx, struct rule *rule)
case STMT_LOG:
memcpy(&clone->log, &stmt->log, sizeof(clone->log));
if (stmt->log.prefix)
- clone->log.prefix = expr_get(stmt->log.prefix);
+ clone->log.prefix = xstrdup(stmt->log.prefix);
break;
case STMT_NAT:
if ((stmt->nat.addr &&
diff --git a/src/parser_bison.y b/src/parser_bison.y
index e3ac2fb..678dd50 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -3338,127 +3338,19 @@ log_args : log_arg
log_arg : PREFIX string
{
struct scope *scope = current_scope(state);
- bool done = false, another_var = false;
- char *start, *end, scratch = '\0';
- struct expr *expr, *item;
- struct symbol *sym;
- enum {
- PARSE_TEXT,
- PARSE_VAR,
- } prefix_state;
-
- /* No variables in log prefix, skip. */
- if (!strchr($2, '$')) {
- expr = constant_expr_alloc(&@$, &string_type,
- BYTEORDER_HOST_ENDIAN,
- (strlen($2) + 1) * BITS_PER_BYTE, $2);
- free_const($2);
- $<stmt>0->log.prefix = expr;
- $<stmt>0->log.flags |= STMT_LOG_PREFIX;
- break;
- }
-
- /* Parse variables in log prefix string using a
- * state machine parser with two states. This
- * parser creates list of expressions composed
- * of constant and variable expressions.
- */
- expr = compound_expr_alloc(&@$, EXPR_LIST);
-
- start = (char *)$2;
+ struct error_record *erec;
+ const char *prefix;
- if (*start != '$') {
- prefix_state = PARSE_TEXT;
- } else {
- prefix_state = PARSE_VAR;
- start++;
- }
- end = start;
-
- /* Not nice, but works. */
- while (!done) {
- switch (prefix_state) {
- case PARSE_TEXT:
- while (*end != '\0' && *end != '$')
- end++;
-
- if (*end == '\0')
- done = true;
-
- *end = '\0';
- item = constant_expr_alloc(&@$, &string_type,
- BYTEORDER_HOST_ENDIAN,
- (strlen(start) + 1) * BITS_PER_BYTE,
- start);
- compound_expr_add(expr, item);
-
- if (done)
- break;
-
- start = end + 1;
- end = start;
-
- /* fall through */
- case PARSE_VAR:
- while (isalnum(*end) || *end == '_')
- end++;
-
- if (*end == '\0')
- done = true;
- else if (*end == '$')
- another_var = true;
- else
- scratch = *end;
-
- *end = '\0';
-
- sym = symbol_get(scope, start);
- if (!sym) {
- sym = symbol_lookup_fuzzy(scope, start);
- if (sym) {
- erec_queue(error(&@2, "unknown identifier '%s'; "
- "did you mean identifier %s?",
- start, sym->identifier),
- state->msgs);
- } else {
- erec_queue(error(&@2, "unknown identifier '%s'",
- start),
- state->msgs);
- }
- expr_free(expr);
- free_const($2);
- YYERROR;
- }
- item = variable_expr_alloc(&@$, scope, sym);
- compound_expr_add(expr, item);
-
- if (done)
- break;
-
- /* Restore original byte after
- * symbol lookup.
- */
- if (scratch) {
- *end = scratch;
- scratch = '\0';
- }
-
- start = end;
- if (another_var) {
- another_var = false;
- start++;
- prefix_state = PARSE_VAR;
- } else {
- prefix_state = PARSE_TEXT;
- }
- end = start;
- break;
- }
+ prefix = str_preprocess(state, &@2, scope, $2, &erec);
+ if (!prefix) {
+ erec_queue(erec, state->msgs);
+ free_const($2);
+ YYERROR;
}
free_const($2);
- $<stmt>0->log.prefix = expr;
- $<stmt>0->log.flags |= STMT_LOG_PREFIX;
+ $<stmt>0->log.prefix = prefix;
+ $<stmt>0->log.flags |= STMT_LOG_PREFIX;
}
| GROUP NUM
{
diff --git a/src/parser_json.c b/src/parser_json.c
index 25483b1..ce36397 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -2540,9 +2540,7 @@ static struct stmt *json_parse_log_stmt(struct json_ctx *ctx,
stmt = log_stmt_alloc(int_loc);
if (!json_unpack(value, "{s:s}", "prefix", &tmpstr)) {
- stmt->log.prefix = constant_expr_alloc(int_loc, &string_type,
- BYTEORDER_HOST_ENDIAN,
- (strlen(tmpstr) + 1) * BITS_PER_BYTE, tmpstr);
+ stmt->log.prefix = xstrdup(tmpstr);
stmt->log.flags |= STMT_LOG_PREFIX;
}
if (!json_unpack(value, "{s:i}", "group", &tmp)) {
diff --git a/src/preprocess.c b/src/preprocess.c
new file mode 100644
index 0000000..619f67a
--- /dev/null
+++ b/src/preprocess.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2013-2024 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <utils.h>
+
+#include "list.h"
+#include "parser.h"
+#include "erec.h"
+
+struct str_buf {
+ uint8_t *str;
+ uint32_t len;
+ uint32_t size;
+};
+
+#define STR_BUF_LEN 128
+
+static struct str_buf *str_buf_alloc(void)
+{
+ struct str_buf *buf;
+
+ buf = xzalloc(sizeof(*buf));
+ buf->str = xzalloc_array(1, STR_BUF_LEN);
+ buf->size = STR_BUF_LEN;
+
+ return buf;
+}
+
+static int str_buf_add(struct str_buf *buf, const char *str, uint32_t len)
+{
+ uint8_t *tmp;
+
+ if (len + buf->len > buf->size) {
+ buf->size = (len + buf->len) * 2;
+ tmp = xrealloc(buf->str, buf->size);
+ buf->str = tmp;
+ }
+
+ memcpy(&buf->str[buf->len], str, len);
+ buf->len += len;
+
+ return 0;
+}
+
+struct str_chunk {
+ struct list_head list;
+ char *str;
+ uint32_t len;
+ bool is_sym;
+};
+
+static void add_str_chunk(const char *x, int from, int to, struct list_head *list, bool is_sym)
+{
+ struct str_chunk *chunk;
+ int len = to - from;
+
+ chunk = xzalloc_array(1, sizeof(*chunk));
+ chunk->str = xzalloc_array(1, len + 1);
+ chunk->is_sym = is_sym;
+ chunk->len = len;
+ memcpy(chunk->str, &x[from], len);
+
+ list_add_tail(&chunk->list, list);
+}
+
+static void free_str_chunk(struct str_chunk *chunk)
+{
+ free(chunk->str);
+ free(chunk);
+}
+
+const char *str_preprocess(struct parser_state *state, struct location *loc,
+ struct scope *scope, const char *x,
+ struct error_record **erec)
+{
+ struct str_chunk *chunk, *next;
+ struct str_buf *buf;
+ const char *str;
+ int i, j, start;
+ LIST_HEAD(list);
+
+ start = 0;
+ i = 0;
+ while (1) {
+ if (x[i] == '\0') {
+ i++;
+ break;
+ }
+
+ if (x[i] != '$') {
+ i++;
+ continue;
+ }
+
+ if (isdigit(x[++i]))
+ continue;
+
+ j = i;
+ while (1) {
+ if (isalpha(x[i]) ||
+ isdigit(x[i]) ||
+ x[i] == '_') {
+ i++;
+ continue;
+ }
+ break;
+ }
+ add_str_chunk(x, start, j-1, &list, false);
+ add_str_chunk(x, j, i, &list, true);
+ start = i;
+ }
+ if (start != i)
+ add_str_chunk(x, start, i, &list, false);
+
+ buf = str_buf_alloc();
+
+ list_for_each_entry_safe(chunk, next, &list, list) {
+ if (chunk->is_sym) {
+ struct symbol *sym;
+
+ sym = symbol_lookup(scope, chunk->str);
+ if (!sym) {
+ sym = symbol_lookup_fuzzy(scope, chunk->str);
+ if (sym) {
+ *erec = error(loc, "unknown identifier '%s'; "
+ "did you mean identifier '%s'?",
+ chunk->str, sym->identifier);
+ } else {
+ *erec = error(loc, "unknown identifier '%s'",
+ chunk->str);
+ }
+ goto err;
+ }
+ str_buf_add(buf, sym->expr->identifier,
+ strlen(sym->expr->identifier));
+ } else {
+ str_buf_add(buf, chunk->str, chunk->len);
+ }
+ list_del(&chunk->list);
+ free_str_chunk(chunk);
+ }
+
+ str = (char *)buf->str;
+
+ free(buf);
+
+ return (char *)str;
+err:
+ list_for_each_entry_safe(chunk, next, &list, list) {
+ list_del(&chunk->list);
+ free_str_chunk(chunk);
+ }
+ free(buf->str);
+ free(buf);
+
+ return NULL;
+}
diff --git a/src/statement.c b/src/statement.c
index ab144d6..551cd13 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -377,12 +377,8 @@ int log_level_parse(const char *level)
static void log_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
nft_print(octx, "log");
- if (stmt->log.flags & STMT_LOG_PREFIX) {
- char prefix[NF_LOG_PREFIXLEN] = {};
-
- expr_to_string(stmt->log.prefix, prefix);
- nft_print(octx, " prefix \"%s\"", prefix);
- }
+ if (stmt->log.flags & STMT_LOG_PREFIX)
+ nft_print(octx, " prefix \"%s\"", stmt->log.prefix);
if (stmt->log.flags & STMT_LOG_GROUP)
nft_print(octx, " group %u", stmt->log.group);
if (stmt->log.flags & STMT_LOG_SNAPLEN)
@@ -419,7 +415,7 @@ static void log_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
static void log_stmt_destroy(struct stmt *stmt)
{
- expr_free(stmt->log.prefix);
+ free_const(stmt->log.prefix);
}
static const struct stmt_ops log_stmt_ops = {

View File

@ -0,0 +1,110 @@
From bbfd033cb9014f854f6a4db3cec2aafe160847d4 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:10:08 +0200
Subject: [PATCH] intervals: fix element deletions with maps
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 551a4ad68b922fa6c942f5e79ac59f723a12e233
commit 551a4ad68b922fa6c942f5e79ac59f723a12e233
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Wed Jul 3 16:29:26 2024 +0200
intervals: fix element deletions with maps
Set element deletion in maps (including catchall elements) does not work.
# nft delete element ip x m { \* }
BUG: invalid range expression type catch-all set element
nft: src/expression.c:1472: range_expr_value_low: Assertion `0' failed.
Aborted
Call interval_expr_key() to fetch expr->left in the mapping but use the
expression that represents the mapping because it provides access to the
EXPR_F_REMOVE flags.
Moreover, assume maximum value for catchall expression by means of the
expr->len to reuse the existing code to check if the element to be
deleted really exists.
Fixes: 3e8d934e4f72 ("intervals: support to partial deletion with automerge")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/intervals.c | 31 ++++++++++++++++++-------------
1 file changed, 18 insertions(+), 13 deletions(-)
diff --git a/src/intervals.c b/src/intervals.c
index d5406ef..af1415a 100644
--- a/src/intervals.c
+++ b/src/intervals.c
@@ -378,7 +378,7 @@ static int setelem_delete(struct list_head *msgs, struct set *set,
struct expr *purge, struct expr *elems,
unsigned int debug_mask)
{
- struct expr *i, *next, *prev = NULL;
+ struct expr *i, *next, *elem, *prev = NULL;
struct range range, prev_range;
int err = 0;
mpz_t rop;
@@ -389,21 +389,26 @@ static int setelem_delete(struct list_head *msgs, struct set *set,
mpz_init(range.high);
mpz_init(rop);
- list_for_each_entry_safe(i, next, &elems->expressions, list) {
- if (i->key->etype == EXPR_SET_ELEM_CATCHALL)
- continue;
+ list_for_each_entry_safe(elem, next, &elems->expressions, list) {
+ i = interval_expr_key(elem);
- range_expr_value_low(range.low, i);
- range_expr_value_high(range.high, i);
+ if (i->key->etype == EXPR_SET_ELEM_CATCHALL) {
+ /* Assume max value to simplify handling. */
+ mpz_bitmask(range.low, i->len);
+ mpz_bitmask(range.high, i->len);
+ } else {
+ range_expr_value_low(range.low, i);
+ range_expr_value_high(range.high, i);
+ }
- if (!prev && i->flags & EXPR_F_REMOVE) {
+ if (!prev && elem->flags & EXPR_F_REMOVE) {
expr_error(msgs, i, "element does not exist");
err = -1;
goto err;
}
- if (!(i->flags & EXPR_F_REMOVE)) {
- prev = i;
+ if (!(elem->flags & EXPR_F_REMOVE)) {
+ prev = elem;
mpz_set(prev_range.low, range.low);
mpz_set(prev_range.high, range.high);
continue;
@@ -411,12 +416,12 @@ static int setelem_delete(struct list_head *msgs, struct set *set,
if (mpz_cmp(prev_range.low, range.low) == 0 &&
mpz_cmp(prev_range.high, range.high) == 0) {
- if (i->flags & EXPR_F_REMOVE) {
+ if (elem->flags & EXPR_F_REMOVE) {
if (prev->flags & EXPR_F_KERNEL)
list_move_tail(&prev->list, &purge->expressions);
- list_del(&i->list);
- expr_free(i);
+ list_del(&elem->list);
+ expr_free(elem);
}
} else if (set->automerge) {
if (setelem_adjust(set, purge, &prev_range, &range, prev, i) < 0) {
@@ -424,7 +429,7 @@ static int setelem_delete(struct list_head *msgs, struct set *set,
err = -1;
goto err;
}
- } else if (i->flags & EXPR_F_REMOVE) {
+ } else if (elem->flags & EXPR_F_REMOVE) {
expr_error(msgs, i, "element does not exist");
err = -1;
goto err;

View File

@ -0,0 +1,68 @@
From 765603a3236f4d50de140148bd2a0a950c334aab Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:10:08 +0200
Subject: [PATCH] parser_bison: recursive table declaration in deprecated meter
statement
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit a70a217079ef83482fc093d8549f8cdeaeaa3cae
commit a70a217079ef83482fc093d8549f8cdeaeaa3cae
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Wed Jul 3 00:08:01 2024 +0200
parser_bison: recursive table declaration in deprecated meter statement
This is allowing for recursive table NAME declarations such as:
... table xyz1 table xyz2 { ... }
remove it.
Fixes: 3ed5e31f4a32 ("src: add flow statement")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_bison.y | 20 ++++----------------
1 file changed, 4 insertions(+), 16 deletions(-)
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 678dd50..87cf9ae 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -4178,10 +4178,11 @@ map_stmt : set_stmt_op set_ref_expr '{' set_elem_expr_stmt COLON set_elem_expr_
}
;
-meter_stmt : flow_stmt_legacy_alloc flow_stmt_opts '{' meter_key_expr stmt '}'
+meter_stmt : flow_stmt_legacy_alloc TABLE identifier '{' meter_key_expr stmt '}'
{
- $1->meter.key = $4;
- $1->meter.stmt = $5;
+ $1->meter.name = $3;
+ $1->meter.key = $5;
+ $1->meter.stmt = $6;
$$->location = @$;
$$ = $1;
}
@@ -4194,19 +4195,6 @@ flow_stmt_legacy_alloc : FLOW
}
;
-flow_stmt_opts : flow_stmt_opt
- {
- $<stmt>$ = $<stmt>0;
- }
- | flow_stmt_opts flow_stmt_opt
- ;
-
-flow_stmt_opt : TABLE identifier
- {
- $<stmt>0->meter.name = $2;
- }
- ;
-
meter_stmt_alloc : METER identifier '{' meter_key_expr stmt '}'
{
$$ = meter_stmt_alloc(&@$);

View File

@ -0,0 +1,59 @@
From e9372be1f7b089f58c49d3093f0973c2bf5bfc9a Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:10:08 +0200
Subject: [PATCH] evaluate: set on expr->len for catchall set elements
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit b523008535f3de78ed5834a302ba07cda4b4c8fd
commit b523008535f3de78ed5834a302ba07cda4b4c8fd
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Thu Jul 4 16:38:22 2024 +0200
evaluate: set on expr->len for catchall set elements
Catchall elements coming from the parser provide expr->len == 0.
However, the existing mergesort implementation requires expr->len to be
set up to the length of the set key to properly sort elements.
In particular, set element deletion leverages such list sorting to find
if elements exists in the set.
Fixes: 419d19688688 ("src: add set element catch-all support")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/evaluate.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/src/evaluate.c b/src/evaluate.c
index c28f693..af811a7 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1802,6 +1802,16 @@ err_missing_flag:
set_is_map(ctx->set->flags) ? "map" : "set", expr_name(key));
}
+static int expr_evaluate_set_elem_catchall(struct eval_ctx *ctx, struct expr **expr)
+{
+ struct expr *elem = *expr;
+
+ if (ctx->set)
+ elem->len = ctx->set->key->len;
+
+ return 0;
+}
+
static const struct expr *expr_set_elem(const struct expr *expr)
{
if (expr->etype == EXPR_MAPPING)
@@ -2866,7 +2876,7 @@ static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
case EXPR_XFRM:
return expr_evaluate_xfrm(ctx, expr);
case EXPR_SET_ELEM_CATCHALL:
- return 0;
+ return expr_evaluate_set_elem_catchall(ctx, expr);
case EXPR_FLAGCMP:
return expr_evaluate_flagcmp(ctx, expr);
default:

View File

@ -0,0 +1,43 @@
From a49ad23fb841de5c9c7debd135eb3cb8fe47d444 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:10:08 +0200
Subject: [PATCH] segtree: set on EXPR_F_KERNEL flag for catchall elements in
the cache
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit dc6950a80110d6e6f63bd6f5c308d202db698f46
commit dc6950a80110d6e6f63bd6f5c308d202db698f46
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Thu Jul 4 14:35:07 2024 +0200
segtree: set on EXPR_F_KERNEL flag for catchall elements in the cache
Catchall set element deletion requires this flag to be set on,
otherwise it bogusly reports that such element does not exist
in the set.
Fixes: f1cc44edb218 ("src: add EXPR_F_KERNEL to identify expression in the kernel")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/segtree.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/segtree.c b/src/segtree.c
index 7d246de..c5f27c9 100644
--- a/src/segtree.c
+++ b/src/segtree.c
@@ -653,8 +653,10 @@ void interval_map_decompose(struct expr *set)
expr_free(i);
out:
- if (catchall)
+ if (catchall) {
+ catchall->flags |= EXPR_F_KERNEL;
compound_expr_add(set, catchall);
+ }
free(ranges);
free(elements);

View File

@ -0,0 +1,143 @@
From 6804a55496761b1366ffa5e554f9658cfd55548a Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:10:08 +0200
Subject: [PATCH] optimize: clone counter before insertion into set element
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit ac77f3805c71f14c51730a9c5cb726ee67f14159
commit ac77f3805c71f14c51730a9c5cb726ee67f14159
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Fri Jul 5 14:03:33 2024 +0200
optimize: clone counter before insertion into set element
The counter statement that is zapped from the rule needs to be cloned
before inserting it into each set element.
Fixes: 686ab8b6996e ("optimize: do not remove counter in verdict maps")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/optimize.c | 24 +++++++++++++------
.../optimizations/dumps/merge_counter.nft | 8 +++++++
.../testcases/optimizations/merge_counter | 20 ++++++++++++++++
3 files changed, 45 insertions(+), 7 deletions(-)
create mode 100644 tests/shell/testcases/optimizations/dumps/merge_counter.nft
create mode 100755 tests/shell/testcases/optimizations/merge_counter
diff --git a/src/optimize.c b/src/optimize.c
index 1dd0858..62dd908 100644
--- a/src/optimize.c
+++ b/src/optimize.c
@@ -692,29 +692,36 @@ static void build_verdict_map(struct expr *expr, struct stmt *verdict,
struct expr *set, struct stmt *counter)
{
struct expr *item, *elem, *mapping;
+ struct stmt *counter_elem;
switch (expr->etype) {
case EXPR_LIST:
list_for_each_entry(item, &expr->expressions, list) {
elem = set_elem_expr_alloc(&internal_location, expr_get(item));
- if (counter)
- list_add_tail(&counter->list, &elem->stmt_list);
+ if (counter) {
+ counter_elem = counter_stmt_alloc(&counter->location);
+ list_add_tail(&counter_elem->list, &elem->stmt_list);
+ }
mapping = mapping_expr_alloc(&internal_location, elem,
expr_get(verdict->expr));
compound_expr_add(set, mapping);
}
+ stmt_free(counter);
break;
case EXPR_SET:
list_for_each_entry(item, &expr->expressions, list) {
elem = set_elem_expr_alloc(&internal_location, expr_get(item->key));
- if (counter)
- list_add_tail(&counter->list, &elem->stmt_list);
+ if (counter) {
+ counter_elem = counter_stmt_alloc(&counter->location);
+ list_add_tail(&counter_elem->list, &elem->stmt_list);
+ }
mapping = mapping_expr_alloc(&internal_location, elem,
expr_get(verdict->expr));
compound_expr_add(set, mapping);
}
+ stmt_free(counter);
break;
case EXPR_PREFIX:
case EXPR_RANGE:
@@ -819,8 +826,8 @@ static void __merge_concat_stmts_vmap(const struct optimize_ctx *ctx,
struct expr *set, struct stmt *verdict)
{
struct expr *concat, *next, *elem, *mapping;
+ struct stmt *counter, *counter_elem;
LIST_HEAD(concat_list);
- struct stmt *counter;
counter = zap_counter(ctx, i);
__merge_concat(ctx, i, merge, &concat_list);
@@ -828,13 +835,16 @@ static void __merge_concat_stmts_vmap(const struct optimize_ctx *ctx,
list_for_each_entry_safe(concat, next, &concat_list, list) {
list_del(&concat->list);
elem = set_elem_expr_alloc(&internal_location, concat);
- if (counter)
- list_add_tail(&counter->list, &elem->stmt_list);
+ if (counter) {
+ counter_elem = counter_stmt_alloc(&counter->location);
+ list_add_tail(&counter_elem->list, &elem->stmt_list);
+ }
mapping = mapping_expr_alloc(&internal_location, elem,
expr_get(verdict->expr));
compound_expr_add(set, mapping);
}
+ stmt_free(counter);
}
static void merge_concat_stmts_vmap(const struct optimize_ctx *ctx,
diff --git a/tests/shell/testcases/optimizations/dumps/merge_counter.nft b/tests/shell/testcases/optimizations/dumps/merge_counter.nft
new file mode 100644
index 0000000..72eed5d
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_counter.nft
@@ -0,0 +1,8 @@
+table ip x {
+ chain y {
+ type filter hook input priority filter; policy drop;
+ ct state vmap { invalid counter packets 0 bytes 0 : drop, established counter packets 0 bytes 0 : accept, related counter packets 0 bytes 0 : accept }
+ tcp dport { 80, 123 } counter packets 0 bytes 0 accept
+ ip saddr . ip daddr vmap { 1.1.1.1 . 2.2.2.2 counter packets 0 bytes 0 : accept, 1.1.1.2 . 3.3.3.3 counter packets 0 bytes 0 : drop }
+ }
+}
diff --git a/tests/shell/testcases/optimizations/merge_counter b/tests/shell/testcases/optimizations/merge_counter
new file mode 100755
index 0000000..3b8bbad
--- /dev/null
+++ b/tests/shell/testcases/optimizations/merge_counter
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_set_expr)
+
+set -e
+
+RULESET="table ip x {
+ chain y {
+ type filter hook input priority 0; policy drop;
+
+ ct state invalid counter drop
+ ct state established,related counter accept
+ tcp dport 80 counter accept
+ tcp dport 123 counter accept
+ ip saddr 1.1.1.1 ip daddr 2.2.2.2 counter accept
+ ip saddr 1.1.1.2 ip daddr 3.3.3.3 counter drop
+ }
+}"
+
+$NFT -o -f - <<< $RULESET

View File

@ -0,0 +1,63 @@
From 36abb7d32f0f4af37112fb74b31a7235d029e3e3 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:10:08 +0200
Subject: [PATCH] libnftables: skip useable checks for /dev/stdin
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 477fd8218777b75bdfa3a5643f692adae4f002fe
commit 477fd8218777b75bdfa3a5643f692adae4f002fe
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue Jul 9 16:59:53 2024 +0200
libnftables: skip useable checks for /dev/stdin
/dev/stdin is a placeholder, read() from STDIN_FILENO is used to fetch
the standard input into a buffer.
Since 5c2b2b0a2ba7 ("src: error reporting with -f and read from stdin")
stdin is stored in a buffer to fix error reporting.
This patch requires: ("parser_json: use stdin buffer if available")
Fixes: 149b1c95d129 ("libnftables: refuse to open onput files other than named pipes or regular files")
Acked-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/libnftables.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/libnftables.c b/src/libnftables.c
index 0dee1ba..8f8acd1 100644
--- a/src/libnftables.c
+++ b/src/libnftables.c
@@ -664,6 +664,7 @@ retry:
/* need to use stat() to, fopen() will block for named fifos and
* libjansson makes no checks before or after open either.
+ * /dev/stdin is *never* used, read() from STDIN_FILENO is used instead.
*/
static struct error_record *filename_is_useable(struct nft_ctx *nft, const char *name)
{
@@ -671,6 +672,9 @@ static struct error_record *filename_is_useable(struct nft_ctx *nft, const char
struct stat sb;
int err;
+ if (!strcmp(name, "/dev/stdin"))
+ return NULL;
+
err = stat(name, &sb);
if (err)
return error(&internal_location, "Could not open file \"%s\": %s\n",
@@ -681,9 +685,6 @@ static struct error_record *filename_is_useable(struct nft_ctx *nft, const char
if (type == S_IFREG || type == S_IFIFO)
return NULL;
- if (type == S_IFCHR && 0 == strcmp(name, "/dev/stdin"))
- return NULL;
-
return error(&internal_location, "Not a regular file: \"%s\"\n", name);
}

View File

@ -0,0 +1,63 @@
From 50d322fb2d90b0a07b3f2e6544dec853ac22ac85 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:10:08 +0200
Subject: [PATCH] parser_json: use stdin buffer if available
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit e48f32701ff65d522c2f29f34bf4f3ce8e562057
commit e48f32701ff65d522c2f29f34bf4f3ce8e562057
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue Jul 9 16:59:52 2024 +0200
parser_json: use stdin buffer if available
Since 5c2b2b0a2ba7 ("src: error reporting with -f and read from stdin")
stdin is stored in a buffer, update json support to use it instead of
reading from /dev/stdin.
Some systems do not provide /dev/stdin symlink to /proc/self/fd/0
according to reporter (that mentions Yocto Linux as example).
Fixes: 935f82e7dd49 ("Support 'nft -f -' to read from stdin")
Acked-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/libnftables.c | 3 +--
src/parser_json.c | 7 +++++++
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/src/libnftables.c b/src/libnftables.c
index 8f8acd1..02570c4 100644
--- a/src/libnftables.c
+++ b/src/libnftables.c
@@ -795,8 +795,7 @@ int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
if (!strcmp(filename, "-"))
filename = "/dev/stdin";
- if (!strcmp(filename, "/dev/stdin") &&
- !nft_output_json(&nft->output))
+ if (!strcmp(filename, "/dev/stdin"))
nft->stdin_buf = stdin_to_buffer();
if (nft->optimize_flags) {
diff --git a/src/parser_json.c b/src/parser_json.c
index ce36397..047c6fa 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -4333,6 +4333,13 @@ int nft_parse_json_filename(struct nft_ctx *nft, const char *filename,
json_error_t err;
int ret;
+ if (nft->stdin_buf) {
+ json_indesc.type = INDESC_STDIN;
+ json_indesc.name = "/dev/stdin";
+
+ return nft_parse_json_buffer(nft, nft->stdin_buf, msgs, cmds);
+ }
+
json_indesc.type = INDESC_FILE;
json_indesc.name = filename;

View File

@ -0,0 +1,111 @@
From 0df57d34d73abae0b0a5e9530cc66aac0eda250b Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:10:08 +0200
Subject: [PATCH] optimize: skip variables in nat statements
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit bc1f910f502701f1a1d28c7bd723e4be3bac1d8c
commit bc1f910f502701f1a1d28c7bd723e4be3bac1d8c
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Thu Jul 18 18:06:22 2024 +0200
optimize: skip variables in nat statements
Do not hit assert():
nft: optimize.c:486: rule_build_stmt_matrix_stmts: Assertion `k >= 0' failed.
variables are not supported by -o/--optimize at this stage.
Fixes: 9be404a153bc ("optimize: ignore existing nat mapping")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/optimize.c | 6 ++-
tests/shell/testcases/optimizations/variables | 52 ++++++++++++++++---
2 files changed, 49 insertions(+), 9 deletions(-)
diff --git a/src/optimize.c b/src/optimize.c
index 62dd908..9f0965c 100644
--- a/src/optimize.c
+++ b/src/optimize.c
@@ -408,9 +408,11 @@ static int rule_collect_stmts(struct optimize_ctx *ctx, struct rule *rule)
break;
case STMT_NAT:
if ((stmt->nat.addr &&
- stmt->nat.addr->etype == EXPR_MAP) ||
+ (stmt->nat.addr->etype == EXPR_MAP ||
+ stmt->nat.addr->etype == EXPR_VARIABLE)) ||
(stmt->nat.proto &&
- stmt->nat.proto->etype == EXPR_MAP)) {
+ (stmt->nat.proto->etype == EXPR_MAP ||
+ stmt->nat.proto->etype == EXPR_VARIABLE))) {
clone->ops = &unsupported_stmt_ops;
break;
}
diff --git a/tests/shell/testcases/optimizations/variables b/tests/shell/testcases/optimizations/variables
index fa98606..4cb322d 100755
--- a/tests/shell/testcases/optimizations/variables
+++ b/tests/shell/testcases/optimizations/variables
@@ -2,14 +2,52 @@
set -e
-RULESET="define addrv4_vpnnet = 10.1.0.0/16
+RULESET='define addrv4_vpnnet = 10.1.0.0/16
+define wan = "eth0"
+define lan = "eth1"
+define vpn = "tun0"
+define server = "10.10.10.1"
-table ip nat {
- chain postrouting {
- type nat hook postrouting priority 0; policy accept;
+table inet filter {
+ chain input {
+ type filter hook input priority 0; policy drop;
+ }
+ chain forward {
+ type filter hook forward priority 1; policy drop;
- ip saddr \$addrv4_vpnnet counter masquerade fully-random comment \"masquerade ipv4\"
- }
-}"
+ iifname $lan oifname $lan accept;
+
+ iifname $lan oifname $wan ct state new accept
+ iifname $lan oifname $wan ct state {established, related} accept
+
+ iifname $wan oifname $lan ct state {established, related} accept
+
+ iifname $vpn oifname $wan accept
+ iifname $wan oifname $vpn accept
+ iifname $lan oifname $vpn accept
+ iifname $vpn oifname $lan accept
+
+ iifname $lan oifname $server accept
+ iifname $server oifname $lan accept
+ iifname $server oifname $wan accept
+ iifname $wan oifname $server accept
+ }
+ chain output {
+ type filter hook output priority 0; policy drop;
+ }
+}
+
+table nat {
+ chain prerouting {
+ type nat hook prerouting priority -100; policy accept;
+ iifname $wan tcp dport 10000 dnat to $server:10000;
+ }
+ chain postrouting {
+ type nat hook postrouting priority 100; policy accept;
+ ip saddr $addrv4_vpnnet counter masquerade fully-random comment "masquerade ipv4"
+ oifname $vpn masquerade
+ oifname $wan masquerade
+ }
+}'
$NFT -c -o -f - <<< $RULESET

View File

@ -0,0 +1,77 @@
From 2269f1c988af5f779c39b452231c6cd841bdc019 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:10:09 +0200
Subject: [PATCH] datatype: reject rate in quota statement
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 8ed6fa6d66b2df50d118423c1cb0e98cdd45cdbd
commit 8ed6fa6d66b2df50d118423c1cb0e98cdd45cdbd
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Wed Aug 14 13:02:02 2024 +0200
datatype: reject rate in quota statement
Bail out if rate are used:
ruleset.nft:5:77-106: Error: Wrong rate format, expecting bytes or kbytes or mbytes
add rule netdev firewall PROTECTED_IPS update @quota_temp_before { ip daddr quota over 45000 mbytes/second } add @quota_trigger { ip daddr }
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
improve error reporting while at this.
Fixes: 6615676d825e ("src: add per-bytes limit")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/datatype.c | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/src/datatype.c b/src/datatype.c
index 46d77eb..4cceeb8 100644
--- a/src/datatype.c
+++ b/src/datatype.c
@@ -1432,14 +1432,14 @@ static struct error_record *time_unit_parse(const struct location *loc,
struct error_record *data_unit_parse(const struct location *loc,
const char *str, uint64_t *rate)
{
- if (strncmp(str, "bytes", strlen("bytes")) == 0)
+ if (strcmp(str, "bytes") == 0)
*rate = 1ULL;
- else if (strncmp(str, "kbytes", strlen("kbytes")) == 0)
+ else if (strcmp(str, "kbytes") == 0)
*rate = 1024;
- else if (strncmp(str, "mbytes", strlen("mbytes")) == 0)
+ else if (strcmp(str, "mbytes") == 0)
*rate = 1024 * 1024;
else
- return error(loc, "Wrong rate format");
+ return error(loc, "Wrong unit format, expecting bytes, kbytes or mbytes");
return NULL;
}
@@ -1447,14 +1447,20 @@ struct error_record *data_unit_parse(const struct location *loc,
struct error_record *rate_parse(const struct location *loc, const char *str,
uint64_t *rate, uint64_t *unit)
{
+ const char *slash, *rate_str;
struct error_record *erec;
- const char *slash;
slash = strchr(str, '/');
if (!slash)
- return error(loc, "wrong rate format");
+ return error(loc, "wrong rate format, expecting {bytes,kbytes,mbytes}/{second,minute,hour,day,week}");
+
+ rate_str = strndup(str, slash - str);
+ if (!rate_str)
+ memory_allocation_error();
+
+ erec = data_unit_parse(loc, rate_str, rate);
+ free_const(rate_str);
- erec = data_unit_parse(loc, str, rate);
if (erec != NULL)
return erec;

View File

@ -0,0 +1,45 @@
From 65ef3c4192edf76e2744646e64003fbf8da01311 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:10:09 +0200
Subject: [PATCH] cache: rule by index requires full cache
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 161beaeacd2e5218d66febc3db825bf6a27119c5
commit 161beaeacd2e5218d66febc3db825bf6a27119c5
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Thu Aug 15 12:34:08 2024 +0200
cache: rule by index requires full cache
In preparation for on-demand cache population with errors, set on
NFT_CACHE_FULL if rule index is used since this requires a full cache
with rules.
This is not a fix, index is already fetching a full cache before this
patch.
But follow up patches relax cache requirements, so add this patch in
first place to make sure index does not break.
Tested-by: Eric Garver <eric@garver.life>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/cache.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/cache.c b/src/cache.c
index e88cbae..42e60df 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -68,7 +68,7 @@ static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags)
if (cmd->handle.index.id ||
cmd->handle.position.id)
- flags |= NFT_CACHE_RULE | NFT_CACHE_UPDATE;
+ flags |= NFT_CACHE_FULL | NFT_CACHE_UPDATE;
break;
default:
break;

View File

@ -0,0 +1,44 @@
From 1d307c03b99a145202aa08f28f1665fc739fd686 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:10:09 +0200
Subject: [PATCH] datatype: improve error reporting when time unit is not
correct
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 6bcaef6a1ea6dc60250ed6124f3b49a8cd29434c
commit 6bcaef6a1ea6dc60250ed6124f3b49a8cd29434c
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Wed Aug 14 13:05:54 2024 +0200
datatype: improve error reporting when time unit is not correct
Display:
Wrong unit format, expecting bytes or kbytes or mbytes
instead of:
Wrong rate format
Fixes: 6615676d825e ("src: add per-bytes limit")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/datatype.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/datatype.c b/src/datatype.c
index 4cceeb8..c94c839 100644
--- a/src/datatype.c
+++ b/src/datatype.c
@@ -1424,7 +1424,7 @@ static struct error_record *time_unit_parse(const struct location *loc,
else if (strcmp(str, "week") == 0)
*unit = 1ULL * 60 * 60 * 24 * 7;
else
- return error(loc, "Wrong rate format");
+ return error(loc, "Wrong time format, expecting second, minute, hour, day or week");
return NULL;
}

View File

@ -0,0 +1,91 @@
From 98811a10653288ee3ceae5b9b9324ff56d70f507 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:10:09 +0200
Subject: [PATCH] parser_bison: allow 0 burst in limit rate byte mode
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit cea05ae5bdc50949d4c734796d6db5717187055a
commit cea05ae5bdc50949d4c734796d6db5717187055a
Author: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Thu Aug 15 13:56:21 2024 +0200
parser_bison: allow 0 burst in limit rate byte mode
Unbreak restoring elements in set with rate limit that fail with:
> /dev/stdin:3618:61-61: Error: limit burst must be > 0
>                  elements = { 1.2.3.4 limit rate over 1000 kbytes/second timeout 1s,
no need for burst != 0 for limit rate byte mode.
Add tests/shell too.
Fixes: 702eff5b5b74 ("src: allow burst 0 for byte ratelimit and use it as default")
Fixes: 285baccfea46 ("src: disallow burst 0 in ratelimits")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_bison.y | 5 -----
.../shell/testcases/sets/dumps/elem_limit_0.nft | 7 +++++++
tests/shell/testcases/sets/elem_limit_0 | 17 +++++++++++++++++
3 files changed, 24 insertions(+), 5 deletions(-)
create mode 100644 tests/shell/testcases/sets/dumps/elem_limit_0.nft
create mode 100755 tests/shell/testcases/sets/elem_limit_0
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 87cf9ae..c18e739 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -4589,11 +4589,6 @@ set_elem_stmt : COUNTER close_scope_counter
}
| LIMIT RATE limit_mode limit_rate_bytes limit_burst_bytes close_scope_limit
{
- if ($5 == 0) {
- erec_queue(error(&@6, "limit burst must be > 0"),
- state->msgs);
- YYERROR;
- }
$$ = limit_stmt_alloc(&@$);
$$->limit.rate = $4.rate;
$$->limit.unit = $4.unit;
diff --git a/tests/shell/testcases/sets/dumps/elem_limit_0.nft b/tests/shell/testcases/sets/dumps/elem_limit_0.nft
new file mode 100644
index 0000000..ca5b2b5
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/elem_limit_0.nft
@@ -0,0 +1,7 @@
+table netdev filter {
+ set test123 {
+ typeof ip saddr
+ limit rate over 1 mbytes/second
+ elements = { 1.2.3.4 limit rate over 1 mbytes/second }
+ }
+}
diff --git a/tests/shell/testcases/sets/elem_limit_0 b/tests/shell/testcases/sets/elem_limit_0
new file mode 100755
index 0000000..b57f927
--- /dev/null
+++ b/tests/shell/testcases/sets/elem_limit_0
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+## requires EXPR
+
+set -e
+
+RULESET="table netdev filter {
+ set test123 {
+ typeof ip saddr
+ limit rate over 1024 kbytes/second
+ elements = { 1.2.3.4 limit rate over 1024 kbytes/second }
+ }
+}"
+
+$NFT -f - <<< $RULESET
+
+(echo "flush ruleset netdev"; $NFT --stateless list ruleset netdev) | $NFT -f -

View File

@ -0,0 +1,56 @@
From a15ffca5581d098e3f03cbbe48edae886d0d60df Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:10:09 +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) <sebastian.walz@secunet.com>
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) <sebastian.walz@secunet.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
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 047c6fa..523dbd2 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -2349,7 +2349,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;
@@ -2361,9 +2361,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;
}
}

View File

@ -0,0 +1,99 @@
From 47ee410b237d907322baed7f01b6dc0abbb6fe29 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:10:09 +0200
Subject: [PATCH] json: Support maps with concatenated data
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit c56d77cc82988391ab8f2514214c0088cbc7d89e
commit c56d77cc82988391ab8f2514214c0088cbc7d89e
Author: Phil Sutter <phil@nwl.cc>
Date: Sat Mar 9 00:27:38 2024 +0100
json: Support maps with concatenated data
Dump such maps with an array of types in "map" property, make the parser
aware of this.
Signed-off-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/json.c | 10 +++++-----
src/parser_json.c | 18 +++++++++---------
2 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/src/json.c b/src/json.c
index 5faeb4d..90d9795 100644
--- a/src/json.c
+++ b/src/json.c
@@ -130,15 +130,15 @@ static json_t *set_stmt_list_json(const struct list_head *stmt_list,
static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
{
- json_t *root, *tmp;
- const char *type, *datatype_ext = NULL;
+ json_t *root, *tmp, *datatype_ext = NULL;
+ const char *type;
if (set_is_datamap(set->flags)) {
type = "map";
- datatype_ext = set->data->dtype->name;
+ datatype_ext = set_dtype_json(set->data);
} else if (set_is_objmap(set->flags)) {
type = "map";
- datatype_ext = obj_type_name(set->objtype);
+ datatype_ext = json_string(obj_type_name(set->objtype));
} else if (set_is_meter(set->flags)) {
type = "meter";
} else {
@@ -155,7 +155,7 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
if (set->comment)
json_object_set_new(root, "comment", json_string(set->comment));
if (datatype_ext)
- json_object_set_new(root, "map", json_string(datatype_ext));
+ json_object_set_new(root, "map", datatype_ext);
if (!(set->flags & (NFT_SET_CONSTANT))) {
if (set->policy != NFT_SET_POL_PERFORMANCE) {
diff --git a/src/parser_json.c b/src/parser_json.c
index 523dbd2..a144f4c 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -3300,7 +3300,7 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root,
enum cmd_ops op, enum cmd_obj obj)
{
struct handle h = { 0 };
- const char *family = "", *policy, *dtype_ext = NULL;
+ const char *family = "", *policy;
json_t *tmp, *stmt_json;
struct set *set;
@@ -3353,19 +3353,19 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root,
return NULL;
}
- if (!json_unpack(root, "{s:s}", "map", &dtype_ext)) {
- const struct datatype *dtype;
+ if (!json_unpack(root, "{s:o}", "map", &tmp)) {
+ if (json_is_string(tmp)) {
+ const char *s = json_string_value(tmp);
- set->objtype = string_to_nft_object(dtype_ext);
+ set->objtype = string_to_nft_object(s);
+ }
if (set->objtype) {
set->flags |= NFT_SET_OBJECT;
- } else if ((dtype = datatype_lookup_byname(dtype_ext))) {
- set->data = constant_expr_alloc(&netlink_location,
- dtype, dtype->byteorder,
- dtype->size, NULL);
+ } else if ((set->data = json_parse_dtype_expr(ctx, tmp))) {
set->flags |= NFT_SET_MAP;
} else {
- json_error(ctx, "Invalid map type '%s'.", dtype_ext);
+ json_error(ctx, "Invalid map type '%s'.",
+ json_dumps(tmp, 0));
set_free(set);
handle_free(&h);
return NULL;

View File

@ -0,0 +1,62 @@
From 281ca241e91da463f1f7115acbe25322b5b28b64 Mon Sep 17 00:00:00 2001
From: Phil Sutter <psutter@redhat.com>
Date: Fri, 17 Jul 2026 11:10:09 +0200
Subject: [PATCH] parser_json: release buffer returned by json_dumps
JIRA: https://issues.redhat.com/browse/RHEL-190549
Upstream Status: nftables commit 46700fbdbbbaab0d7db716fce3a438334c58ac9e
commit 46700fbdbbbaab0d7db716fce3a438334c58ac9e
Author: Sebastian Walz (sivizius) <sebastian.walz@secunet.com>
Date: Mon Aug 19 19:58:14 2024 +0200
parser_json: release buffer returned by json_dumps
The signature of `json_dumps` is:
`char *json_dumps(const json_t *json, size_t flags)`:
It will return a pointer to an owned string, the caller must free it.
However, `json_error` just borrows the string to format it as `%s`, but
after printing the formatted error message, the pointer to the string is
lost and thus never freed.
Fixes: 586ad210368b ("libnftables: Implement JSON parser")
Signed-off-by: Sebastian Walz (sivizius) <sebastian.walz@secunet.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Phil Sutter <psutter@redhat.com>
---
src/parser_json.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/src/parser_json.c b/src/parser_json.c
index a144f4c..3473a2a 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -181,8 +181,11 @@ static int json_unpack_stmt(struct json_ctx *ctx, json_t *root,
assert(value);
if (json_object_size(root) != 1) {
+ const char *dump = json_dumps(root, 0);
+
json_error(ctx, "Malformed object (too many properties): '%s'.",
- json_dumps(root, 0));
+ dump);
+ free_const(dump);
return 1;
}
@@ -3364,8 +3367,10 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root,
} else if ((set->data = json_parse_dtype_expr(ctx, tmp))) {
set->flags |= NFT_SET_MAP;
} else {
- json_error(ctx, "Invalid map type '%s'.",
- json_dumps(tmp, 0));
+ const char *dump = json_dumps(tmp, 0);
+
+ json_error(ctx, "Invalid map type '%s'.", dump);
+ free_const(dump);
set_free(set);
handle_free(&h);
return NULL;

Some files were not shown because too many files have changed in this diff Show More