summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--COPYING8
-rw-r--r--INSTALL55
-rw-r--r--Make_global.am21
-rw-r--r--Makefile.am414
-rw-r--r--configure.ac63
-rw-r--r--doc/Makefile.am30
-rw-r--r--doc/data-types.txt70
-rw-r--r--doc/libnftables-json.adoc62
-rw-r--r--doc/libnftables.adoc49
-rw-r--r--doc/nft.txt136
-rw-r--r--doc/payload-expression.txt223
-rw-r--r--doc/primary-expression.txt20
-rw-r--r--doc/stateful-objects.txt13
-rw-r--r--doc/statements.txt204
-rw-r--r--examples/.gitignore5
-rw-r--r--examples/json-ruleset.nft43
-rw-r--r--examples/nft-buffer.c34
-rw-r--r--examples/nft-json-file.c30
-rw-r--r--files/Makefile.am3
-rw-r--r--files/examples/Makefile.am5
-rw-r--r--files/nftables/Makefile.am14
-rw-r--r--files/osf/Makefile.am2
-rw-r--r--include/Makefile.am41
-rw-r--r--include/cache.h52
-rw-r--r--include/cli.h3
-rw-r--r--include/cmd.h6
-rw-r--r--include/ct.h2
-rw-r--r--include/datatype.h23
-rw-r--r--include/dccpopt.h41
-rw-r--r--include/erec.h5
-rw-r--r--include/expression.h32
-rw-r--r--include/exthdr.h1
-rw-r--r--include/gmputil.h4
-rw-r--r--include/headers.h2
-rw-r--r--include/intervals.h11
-rw-r--r--include/ipopt.h2
-rw-r--r--include/json.h10
-rw-r--r--include/linux/Makefile.am12
-rw-r--r--include/linux/netfilter.h1
-rw-r--r--include/linux/netfilter/Makefile.am10
-rw-r--r--include/linux/netfilter/nf_tables.h77
-rw-r--r--include/linux/netfilter/nfnetlink_hook.h24
-rw-r--r--include/linux/netfilter_arp/Makefile.am1
-rw-r--r--include/linux/netfilter_bridge/Makefile.am1
-rw-r--r--include/linux/netfilter_ipv4/Makefile.am1
-rw-r--r--include/linux/netfilter_ipv6/Makefile.am1
-rw-r--r--include/meta.h2
-rw-r--r--include/mnl.h30
-rw-r--r--include/netlink.h39
-rw-r--r--include/nft.h18
-rw-r--r--include/nftables.h25
-rw-r--r--include/nftables/Makefile.am1
-rw-r--r--include/nftables/libnftables.h16
-rw-r--r--include/parser.h37
-rw-r--r--include/payload.h19
-rw-r--r--include/proto.h66
-rw-r--r--include/rbtree.h98
-rw-r--r--include/rule.h51
-rw-r--r--include/statement.h31
-rw-r--r--include/tcpopt.h13
-rw-r--r--include/utils.h42
-rw-r--r--py/Makefile.am28
-rw-r--r--py/pyproject.toml3
-rw-r--r--py/setup.cfg24
-rwxr-xr-xpy/setup.py25
-rw-r--r--py/src/__init__.py (renamed from py/__init__.py)0
-rw-r--r--py/src/nftables.py (renamed from py/nftables.py)204
-rw-r--r--py/src/schema.json (renamed from py/schema.json)0
-rw-r--r--src/Makefile.am120
-rw-r--r--src/cache.c605
-rw-r--r--src/cli.c84
-rw-r--r--src/cmd.c261
-rw-r--r--src/ct.c27
-rw-r--r--src/datatype.c475
-rw-r--r--src/dccpopt.c277
-rw-r--r--src/erec.c101
-rw-r--r--src/evaluate.c1746
-rw-r--r--src/expression.c120
-rw-r--r--src/exthdr.c127
-rw-r--r--src/fib.c7
-rw-r--r--src/gmputil.c27
-rw-r--r--src/hash.c6
-rw-r--r--src/iface.c58
-rw-r--r--src/intervals.c755
-rw-r--r--src/ipopt.c29
-rw-r--r--src/json.c300
-rw-r--r--src/libnftables.c290
-rw-r--r--src/libnftables.map10
-rw-r--r--src/main.c91
-rw-r--r--src/mergesort.c43
-rw-r--r--src/meta.c215
-rw-r--r--src/mini-gmp.c4
-rw-r--r--src/misspell.c14
-rw-r--r--src/mnl.c668
-rw-r--r--src/monitor.c44
-rw-r--r--src/netlink.c436
-rw-r--r--src/netlink_delinearize.c849
-rw-r--r--src/netlink_linearize.c166
-rw-r--r--src/nfnl_osf.c4
-rw-r--r--src/nftutils.c100
-rw-r--r--src/nftutils.h20
-rw-r--r--src/numgen.c6
-rw-r--r--src/optimize.c1406
-rw-r--r--src/osf.c11
-rw-r--r--src/owner.c13
-rw-r--r--src/parser_bison.y1402
-rw-r--r--src/parser_json.c633
-rw-r--r--src/payload.c482
-rw-r--r--src/print.c7
-rw-r--r--src/proto.c178
-rw-r--r--src/rbtree.c388
-rw-r--r--src/rt.c15
-rw-r--r--src/rule.c428
-rw-r--r--src/scanner.l491
-rw-r--r--src/sctp_chunk.c3
-rw-r--r--src/segtree.c966
-rw-r--r--src/socket.c6
-rw-r--r--src/statement.c82
-rw-r--r--src/tcpopt.c89
-rw-r--r--src/utils.c14
-rw-r--r--src/xfrm.c9
-rw-r--r--src/xt.c246
-rwxr-xr-xtests/build/run-tests.sh36
-rwxr-xr-xtests/monitor/run-tests.sh13
-rw-r--r--tests/monitor/testcases/map-expr.t6
-rw-r--r--tests/monitor/testcases/object.t2
-rw-r--r--tests/monitor/testcases/set-concat-interval.t12
-rw-r--r--tests/monitor/testcases/set-interval.t5
-rw-r--r--tests/monitor/testcases/simple.t4
-rw-r--r--tests/py/README31
-rw-r--r--tests/py/any/ct.t3
-rw-r--r--tests/py/any/ct.t.json19
-rw-r--r--tests/py/any/ct.t.payload8
-rw-r--r--tests/py/any/icmpX.t.netdev3
-rw-r--r--tests/py/any/last.t13
-rw-r--r--tests/py/any/last.t.json16
-rw-r--r--tests/py/any/last.t.json.output7
-rw-r--r--tests/py/any/last.t.payload8
-rw-r--r--tests/py/any/limit.t32
-rw-r--r--tests/py/any/limit.t.json72
-rw-r--r--tests/py/any/limit.t.json.output48
-rw-r--r--tests/py/any/limit.t.payload48
-rw-r--r--tests/py/any/meta.t13
-rw-r--r--tests/py/any/meta.t.json152
-rw-r--r--tests/py/any/meta.t.json.output180
-rw-r--r--tests/py/any/meta.t.payload53
-rw-r--r--tests/py/any/meta.t.payload.bridge20
-rw-r--r--tests/py/any/objects.t3
-rw-r--r--tests/py/any/queue.t1
-rw-r--r--tests/py/any/quota.t3
-rw-r--r--tests/py/any/rawpayload.t17
-rw-r--r--tests/py/any/rawpayload.t.json56
-rw-r--r--tests/py/any/rawpayload.t.json.output18
-rw-r--r--tests/py/any/rawpayload.t.payload20
-rw-r--r--tests/py/any/tcpopt.t27
-rw-r--r--tests/py/any/tcpopt.t.json190
-rw-r--r--tests/py/any/tcpopt.t.payload76
-rw-r--r--tests/py/arp/arp.t3
-rw-r--r--tests/py/bridge/meta.t2
-rw-r--r--tests/py/bridge/redirect.t5
-rw-r--r--tests/py/bridge/redirect.t.json12
-rw-r--r--tests/py/bridge/redirect.t.payload4
-rw-r--r--tests/py/bridge/vlan.t10
-rw-r--r--tests/py/bridge/vlan.t.json131
-rw-r--r--tests/py/bridge/vlan.t.json.output31
-rw-r--r--tests/py/bridge/vlan.t.payload36
-rw-r--r--tests/py/bridge/vlan.t.payload.netdev44
-rw-r--r--tests/py/inet/ah.t3
-rw-r--r--tests/py/inet/comp.t3
-rw-r--r--tests/py/inet/dccp.t8
-rw-r--r--tests/py/inet/dccp.t.json44
-rw-r--r--tests/py/inet/dccp.t.payload14
-rw-r--r--tests/py/inet/esp.t3
-rw-r--r--tests/py/inet/ether-ip.t3
-rw-r--r--tests/py/inet/ether.t9
-rw-r--r--tests/py/inet/ether.t.json32
-rw-r--r--tests/py/inet/ether.t.payload20
-rw-r--r--tests/py/inet/ether.t.payload.bridge16
-rw-r--r--tests/py/inet/ether.t.payload.ip20
-rw-r--r--tests/py/inet/geneve.t23
-rw-r--r--tests/py/inet/geneve.t.json344
-rw-r--r--tests/py/inet/geneve.t.payload114
-rw-r--r--tests/py/inet/gre.t22
-rw-r--r--tests/py/inet/gre.t.json177
-rw-r--r--tests/py/inet/gre.t.payload78
-rw-r--r--tests/py/inet/gretap.t21
-rw-r--r--tests/py/inet/gretap.t.json195
-rw-r--r--tests/py/inet/gretap.t.payload87
-rw-r--r--tests/py/inet/icmpX.t2
-rw-r--r--tests/py/inet/icmpX.t.json.output9
-rw-r--r--tests/py/inet/ip.t3
-rw-r--r--tests/py/inet/ip.t.payload.bridge2
-rw-r--r--tests/py/inet/ip_tcp.t7
-rw-r--r--tests/py/inet/ip_tcp.t.json.output12
-rw-r--r--tests/py/inet/ipsec.t2
-rw-r--r--tests/py/inet/ipsec.t.json21
-rw-r--r--tests/py/inet/ipsec.t.payload6
-rw-r--r--tests/py/inet/map.t3
-rw-r--r--tests/py/inet/meta.t10
-rw-r--r--tests/py/inet/meta.t.json278
-rw-r--r--tests/py/inet/meta.t.json.output41
-rw-r--r--tests/py/inet/meta.t.payload90
-rw-r--r--tests/py/inet/payloadmerge.t14
-rw-r--r--tests/py/inet/payloadmerge.t.json211
-rw-r--r--tests/py/inet/payloadmerge.t.payload66
-rw-r--r--tests/py/inet/reject.t2
-rw-r--r--tests/py/inet/reject.t.json34
-rw-r--r--tests/py/inet/reject.t.payload.inet10
-rw-r--r--tests/py/inet/sctp.t3
-rw-r--r--tests/py/inet/sets.t3
-rw-r--r--tests/py/inet/sets.t.json11
-rw-r--r--tests/py/inet/sets.t.payload.netdev6
-rw-r--r--tests/py/inet/tcp.t19
-rw-r--r--tests/py/inet/tcp.t.json199
-rw-r--r--tests/py/inet/tcp.t.json.output60
-rw-r--r--tests/py/inet/tcp.t.payload6
-rw-r--r--tests/py/inet/tproxy.t2
-rw-r--r--tests/py/inet/tproxy.t.json35
-rw-r--r--tests/py/inet/tproxy.t.payload12
-rw-r--r--tests/py/inet/udp.t3
-rw-r--r--tests/py/inet/udplite.t3
-rw-r--r--tests/py/inet/vmap.t10
-rw-r--r--tests/py/inet/vmap.t.json144
-rw-r--r--tests/py/inet/vmap.t.payload34
-rw-r--r--tests/py/inet/vmap.t.payload.netdev34
-rw-r--r--tests/py/inet/vxlan.t23
-rw-r--r--tests/py/inet/vxlan.t.json344
-rw-r--r--tests/py/inet/vxlan.t.payload114
-rw-r--r--tests/py/ip/ct.t6
-rw-r--r--tests/py/ip/ct.t.json154
-rw-r--r--tests/py/ip/ct.t.payload50
-rw-r--r--tests/py/ip/dnat.t3
-rw-r--r--tests/py/ip/dnat.t.json479
-rw-r--r--tests/py/ip/dnat.t.payload.ip35
-rw-r--r--tests/py/ip/flowtable.t5
-rw-r--r--tests/py/ip/flowtable.t.json24
-rw-r--r--tests/py/ip/flowtable.t.payload7
-rw-r--r--tests/py/ip/icmp.t6
-rw-r--r--tests/py/ip/icmp.t.json6
-rw-r--r--tests/py/ip/icmp.t.json.output4
-rw-r--r--tests/py/ip/ip.t23
-rw-r--r--tests/py/ip/ip.t.json157
-rw-r--r--tests/py/ip/ip.t.json.output31
-rw-r--r--tests/py/ip/ip.t.payload55
-rw-r--r--tests/py/ip/ip.t.payload.bridge66
-rw-r--r--tests/py/ip/ip.t.payload.inet66
-rw-r--r--tests/py/ip/ip.t.payload.netdev66
-rw-r--r--tests/py/ip/ip_tcp.t1
-rw-r--r--tests/py/ip/meta.t5
-rw-r--r--tests/py/ip/meta.t.json78
-rw-r--r--tests/py/ip/meta.t.payload25
-rw-r--r--tests/py/ip/numgen.t2
-rw-r--r--tests/py/ip/numgen.t.json30
-rw-r--r--tests/py/ip/numgen.t.payload11
-rw-r--r--tests/py/ip/redirect.t2
-rw-r--r--tests/py/ip/redirect.t.json14
-rw-r--r--tests/py/ip/redirect.t.payload4
-rw-r--r--tests/py/ip/sets.t6
-rw-r--r--tests/py/ip/sets.t.json31
-rw-r--r--tests/py/ip/sets.t.payload.inet9
-rw-r--r--tests/py/ip/sets.t.payload.ip8
-rw-r--r--tests/py/ip/sets.t.payload.netdev10
-rw-r--r--tests/py/ip/snat.t.json170
-rw-r--r--tests/py/ip/snat.t.json.output177
-rw-r--r--tests/py/ip/snat.t.payload13
-rw-r--r--tests/py/ip6/ct.t9
-rw-r--r--tests/py/ip6/ct.t.json293
-rw-r--r--tests/py/ip6/ct.t.payload46
-rw-r--r--tests/py/ip6/exthdr.t.json.output30
-rw-r--r--tests/py/ip6/flowtable.t6
-rw-r--r--tests/py/ip6/flowtable.t.json62
-rw-r--r--tests/py/ip6/flowtable.t.json.output62
-rw-r--r--tests/py/ip6/flowtable.t.payload16
-rw-r--r--tests/py/ip6/frag.t2
-rw-r--r--tests/py/ip6/frag.t.payload.netdev232
-rw-r--r--tests/py/ip6/icmpv6.t20
-rw-r--r--tests/py/ip6/icmpv6.t.json268
-rw-r--r--tests/py/ip6/icmpv6.t.json.output8
-rw-r--r--tests/py/ip6/icmpv6.t.payload.ip682
-rw-r--r--tests/py/ip6/ip6.t9
-rw-r--r--tests/py/ip6/ip6.t.json100
-rw-r--r--tests/py/ip6/ip6.t.payload.inet44
-rw-r--r--tests/py/ip6/ip6.t.payload.ip636
-rw-r--r--tests/py/ip6/meta.t3
-rw-r--r--tests/py/ip6/meta.t.json117
-rw-r--r--tests/py/ip6/meta.t.json.output16
-rw-r--r--tests/py/ip6/meta.t.payload20
-rw-r--r--tests/py/ip6/redirect.t2
-rw-r--r--tests/py/ip6/redirect.t.json14
-rw-r--r--tests/py/ip6/redirect.t.payload.ip64
-rw-r--r--tests/py/ip6/sets.t7
-rw-r--r--tests/py/ip6/sets.t.json32
-rw-r--r--tests/py/ip6/sets.t.payload.inet9
-rw-r--r--tests/py/ip6/sets.t.payload.ip67
-rw-r--r--tests/py/ip6/sets.t.payload.netdev9
-rw-r--r--tests/py/ip6/srh.t.payload6
-rw-r--r--tests/py/ip6/vmap.t3
-rw-r--r--tests/py/ip6/vmap.t.payload.inet2
-rw-r--r--tests/py/ip6/vmap.t.payload.ip62
-rw-r--r--tests/py/ip6/vmap.t.payload.netdev2
-rw-r--r--tests/py/netdev/dup.t3
-rw-r--r--tests/py/netdev/fwd.t3
-rwxr-xr-xtests/py/nft-test.py114
-rw-r--r--tests/shell/README29
-rw-r--r--tests/shell/features/bitshift.nft7
-rw-r--r--tests/shell/features/catchall_element.nft8
-rw-r--r--tests/shell/features/chain_binding.nft7
-rwxr-xr-xtests/shell/features/comment.sh14
-rw-r--r--tests/shell/features/ctexpect.nft10
-rw-r--r--tests/shell/features/cttimeout.nft8
-rw-r--r--tests/shell/features/destroy.nft3
-rw-r--r--tests/shell/features/dynset_op_delete.nft12
-rwxr-xr-xtests/shell/features/flowtable_counter.sh16
-rwxr-xr-xtests/shell/features/flowtable_no_devices.nft8
-rw-r--r--tests/shell/features/inet_ingress.nft7
-rw-r--r--tests/shell/features/inet_nat.nft7
-rw-r--r--tests/shell/features/inner_matching.nft7
-rwxr-xr-xtests/shell/features/json.sh6
-rw-r--r--tests/shell/features/map_lookup.nft11
-rw-r--r--tests/shell/features/meta_time.nft7
-rwxr-xr-xtests/shell/features/netdev_chain_multidevice.sh17
-rw-r--r--tests/shell/features/netdev_chain_without_device.nft7
-rw-r--r--tests/shell/features/netdev_egress.nft7
-rw-r--r--tests/shell/features/netmap.nft8
-rw-r--r--tests/shell/features/osf.nft7
-rw-r--r--tests/shell/features/pipapo.nft9
-rw-r--r--tests/shell/features/prerouting_reject.nft8
-rwxr-xr-xtests/shell/features/reset_rule.sh8
-rwxr-xr-xtests/shell/features/reset_set.sh10
-rw-r--r--tests/shell/features/reset_tcp_options.nft5
-rw-r--r--tests/shell/features/sctp_chunks.nft7
-rw-r--r--tests/shell/features/secmark.nft7
-rwxr-xr-xtests/shell/features/set_expr.sh19
-rw-r--r--tests/shell/features/set_with_two_expressions.nft9
-rwxr-xr-xtests/shell/features/setelem_expiration.sh18
-rwxr-xr-xtests/shell/features/stateful_object_update.sh21
-rw-r--r--tests/shell/features/synproxy.nft9
-rw-r--r--tests/shell/features/table_flag_owner.nft5
-rwxr-xr-xtests/shell/helpers/json-diff-pretty.sh17
-rwxr-xr-xtests/shell/helpers/json-pretty.sh30
-rwxr-xr-xtests/shell/helpers/json-sanitize-ruleset.sh30
-rwxr-xr-xtests/shell/helpers/nft-valgrind-wrapper.sh31
-rwxr-xr-xtests/shell/helpers/random-source.sh40
-rwxr-xr-xtests/shell/helpers/test-wrapper.sh328
-rwxr-xr-xtests/shell/run-tests.sh1009
-rwxr-xr-xtests/shell/testcases/bitwise/0040mark_binop_013
-rwxr-xr-xtests/shell/testcases/bitwise/0040mark_binop_1 (renamed from tests/shell/testcases/chains/0040mark_shift_1)4
-rwxr-xr-xtests/shell/testcases/bitwise/0040mark_binop_213
-rwxr-xr-xtests/shell/testcases/bitwise/0040mark_binop_313
-rwxr-xr-xtests/shell/testcases/bitwise/0040mark_binop_413
-rwxr-xr-xtests/shell/testcases/bitwise/0040mark_binop_513
-rwxr-xr-xtests/shell/testcases/bitwise/0040mark_binop_613
-rwxr-xr-xtests/shell/testcases/bitwise/0040mark_binop_713
-rwxr-xr-xtests/shell/testcases/bitwise/0040mark_binop_813
-rwxr-xr-xtests/shell/testcases/bitwise/0040mark_binop_913
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_0.json-nft75
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_0.nft (renamed from tests/shell/testcases/chains/dumps/0040mark_shift_0.nft)2
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_1.json-nft86
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_1.nft (renamed from tests/shell/testcases/chains/dumps/0040mark_shift_1.nft)2
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_2.json-nft65
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_2.nft6
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_3.json-nft65
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_3.nft6
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_4.json-nft65
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_4.nft6
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_5.json-nft65
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_5.nft6
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_6.json-nft65
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_6.nft6
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_7.json-nft65
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_7.nft6
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_8.json-nft65
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_8.nft6
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_9.json-nft65
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_9.nft6
-rwxr-xr-xtests/shell/testcases/bogons/assert_failures12
-rw-r--r--tests/shell/testcases/bogons/dumps/assert_failures.json-nft11
-rw-r--r--tests/shell/testcases/bogons/dumps/assert_failures.nft0
-rw-r--r--tests/shell/testcases/bogons/nft-f/add_to_a_set_crash11
-rw-r--r--tests/shell/testcases/bogons/nft-f/binop_with_different_basetype_assert5
-rw-r--r--tests/shell/testcases/bogons/nft-f/bitwise_masklen_assert5
-rw-r--r--tests/shell/testcases/bogons/nft-f/byteorder_switch_stack_overflow6
-rw-r--r--tests/shell/testcases/bogons/nft-f/counter_objref_crash5
-rw-r--r--tests/shell/testcases/bogons/nft-f/ct_helper_yystate_underflow14
-rw-r--r--tests/shell/testcases/bogons/nft-f/ct_timeout_memleak7
-rw-r--r--tests/shell/testcases/bogons/nft-f/ct_timeout_memleak_objfree5
-rw-r--r--tests/shell/testcases/bogons/nft-f/define_policy_assert3
-rw-r--r--tests/shell/testcases/bogons/nft-f/double-free-on-binop-dtype_assert6
-rw-r--r--tests/shell/testcases/bogons/nft-f/dup_fwd_ranges14
-rw-r--r--tests/shell/testcases/bogons/nft-f/dynamic-stack-buffer-overflow_gen_prefix5
-rw-r--r--tests/shell/testcases/bogons/nft-f/evaluate_conflict_resolution_gen_dependency_base_ll_hdr_assert5
-rw-r--r--tests/shell/testcases/bogons/nft-f/exthdr_with_range_bug1
-rw-r--r--tests/shell/testcases/bogons/nft-f/huge_binop_expr_chain_crash5
-rw-r--r--tests/shell/testcases/bogons/nft-f/huge_chain_name_assert5
-rw-r--r--tests/shell/testcases/bogons/nft-f/huge_chain_name_define_assert7
-rw-r--r--tests/shell/testcases/bogons/nft-f/huge_chain_prio5
-rw-r--r--tests/shell/testcases/bogons/nft-f/huge_shift_assert5
-rw-r--r--tests/shell/testcases/bogons/nft-f/icmp_reject_type_uint8_assert1
-rw-r--r--tests/shell/testcases/bogons/nft-f/include-device1
-rw-r--r--tests/shell/testcases/bogons/nft-f/invalid_mapping_expr_binop_assert1
-rw-r--r--tests/shell/testcases/bogons/nft-f/invalid_range_expr_type_binop12
-rw-r--r--tests/shell/testcases/bogons/nft-f/map_without_key5
-rw-r--r--tests/shell/testcases/bogons/nft-f/mapping_with_invalid_datatype_crash1
-rw-r--r--tests/shell/testcases/bogons/nft-f/memleak_on_hookspec_error21
-rw-r--r--tests/shell/testcases/bogons/nft-f/memleak_on_meta_set_errpath5
-rw-r--r--tests/shell/testcases/bogons/nft-f/nat_prefix_map_with_set_element_assert7
-rw-r--r--tests/shell/testcases/bogons/nft-f/nat_stmt_with_set_instead_of_map10
-rw-r--r--tests/shell/testcases/bogons/nft-f/netlink_gen_stmt_stateful_assert6
-rw-r--r--tests/shell/testcases/bogons/nft-f/no_integer_basetype_crash1
-rw-r--r--tests/shell/testcases/bogons/nft-f/objmap_to_prefix_assert6
-rw-r--r--tests/shell/testcases/bogons/nft-f/payload_expr_pctx_update_assert1
-rw-r--r--tests/shell/testcases/bogons/nft-f/payload_expr_unaligned_store1
-rw-r--r--tests/shell/testcases/bogons/nft-f/payload_expr_with_0_length_assert1
-rw-r--r--tests/shell/testcases/bogons/nft-f/scope_underflow_assert6
-rw-r--r--tests/shell/testcases/bogons/nft-f/set_definition_with_no_key_assert12
-rw-r--r--tests/shell/testcases/bogons/nft-f/set_without_key5
-rw-r--r--tests/shell/testcases/bogons/nft-f/stack_overflow_via_large_concat_expr5
-rw-r--r--tests/shell/testcases/bogons/nft-f/stack_overflow_via_large_raw_expr5
-rw-r--r--tests/shell/testcases/bogons/nft-f/tchandle_type_parse_heap_overflow6
-rw-r--r--tests/shell/testcases/bogons/nft-f/tcp_option_without_template1
-rw-r--r--tests/shell/testcases/bogons/nft-f/tproxy_ranges8
-rw-r--r--tests/shell/testcases/bogons/nft-f/unhandled_key_type_13_assert5
-rw-r--r--tests/shell/testcases/bogons/nft-f/unhandled_key_type_13_assert_map5
-rw-r--r--tests/shell/testcases/bogons/nft-f/unhandled_key_type_13_assert_vmap5
-rw-r--r--tests/shell/testcases/bogons/nft-f/unknown_expr_type_range_assert7
-rw-r--r--tests/shell/testcases/bogons/nft-f/use_after_free_on_chain_removal5
-rw-r--r--tests/shell/testcases/bogons/nft-f/zero_length_devicename2_assert5
-rw-r--r--tests/shell/testcases/bogons/nft-f/zero_length_devicename_assert5
-rw-r--r--tests/shell/testcases/bogons/nft-f/zero_length_devicename_flowtable_assert5
-rwxr-xr-xtests/shell/testcases/cache/0008_delete_by_handle_02
-rwxr-xr-xtests/shell/testcases/cache/0010_implicit_chain_021
-rwxr-xr-xtests/shell/testcases/cache/0011_index_012
-rw-r--r--tests/shell/testcases/cache/dumps/0001_cache_handling_0.json-nft142
-rw-r--r--tests/shell/testcases/cache/dumps/0002_interval_0.json-nft38
-rw-r--r--tests/shell/testcases/cache/dumps/0003_cache_update_0.json-nft137
-rw-r--r--tests/shell/testcases/cache/dumps/0003_cache_update_0.nft18
-rw-r--r--tests/shell/testcases/cache/dumps/0004_cache_update_0.json-nft42
-rw-r--r--tests/shell/testcases/cache/dumps/0004_cache_update_0.nft5
-rw-r--r--tests/shell/testcases/cache/dumps/0005_cache_chain_flush.json-nft77
-rw-r--r--tests/shell/testcases/cache/dumps/0005_cache_chain_flush.nft14
-rw-r--r--tests/shell/testcases/cache/dumps/0006_cache_table_flush.json-nft77
-rw-r--r--tests/shell/testcases/cache/dumps/0006_cache_table_flush.nft14
-rw-r--r--tests/shell/testcases/cache/dumps/0007_echo_cache_init_0.json-nft68
-rw-r--r--tests/shell/testcases/cache/dumps/0008_delete_by_handle_0.json-nft18
-rw-r--r--tests/shell/testcases/cache/dumps/0008_delete_by_handle_0.nft2
-rw-r--r--tests/shell/testcases/cache/dumps/0009_delete_by_handle_incorrect_0.json-nft11
-rw-r--r--tests/shell/testcases/cache/dumps/0009_delete_by_handle_incorrect_0.nft0
-rw-r--r--tests/shell/testcases/cache/dumps/0010_implicit_chain_0.nft7
-rw-r--r--tests/shell/testcases/cache/dumps/0011_index_0.json-nft93
-rw-r--r--tests/shell/testcases/cache/dumps/0011_index_0.nft8
-rwxr-xr-xtests/shell/testcases/chains/0014rename_02
-rwxr-xr-xtests/shell/testcases/chains/0021prio_06
-rwxr-xr-xtests/shell/testcases/chains/0023prio_inet_srcnat_12
-rwxr-xr-xtests/shell/testcases/chains/0024prio_inet_dstnat_12
-rwxr-xr-xtests/shell/testcases/chains/0026prio_netdev_14
-rwxr-xr-xtests/shell/testcases/chains/0040mark_shift_011
-rwxr-xr-xtests/shell/testcases/chains/0041chain_binding_011
-rwxr-xr-xtests/shell/testcases/chains/0042chain_variable_042
-rwxr-xr-xtests/shell/testcases/chains/0043chain_ingress_09
-rwxr-xr-xtests/shell/testcases/chains/0044chain_destroy_012
-rw-r--r--tests/shell/testcases/chains/dumps/0001jumps_0.json-nft371
-rw-r--r--tests/shell/testcases/chains/dumps/0002jumps_1.json-nft383
-rw-r--r--tests/shell/testcases/chains/dumps/0002jumps_1.nft68
-rw-r--r--tests/shell/testcases/chains/dumps/0003jump_loop_1.json-nft371
-rw-r--r--tests/shell/testcases/chains/dumps/0003jump_loop_1.nft64
-rw-r--r--tests/shell/testcases/chains/dumps/0004busy_1.json-nft49
-rw-r--r--tests/shell/testcases/chains/dumps/0004busy_1.nft8
-rw-r--r--tests/shell/testcases/chains/dumps/0005busy_map_1.json-nft66
-rw-r--r--tests/shell/testcases/chains/dumps/0005busy_map_1.nft8
-rw-r--r--tests/shell/testcases/chains/dumps/0006masquerade_0.json-nft43
-rw-r--r--tests/shell/testcases/chains/dumps/0007masquerade_1.json-nft30
-rw-r--r--tests/shell/testcases/chains/dumps/0007masquerade_1.nft5
-rw-r--r--tests/shell/testcases/chains/dumps/0008masquerade_jump_1.json-nft51
-rw-r--r--tests/shell/testcases/chains/dumps/0008masquerade_jump_1.nft9
-rw-r--r--tests/shell/testcases/chains/dumps/0009masquerade_jump_1.json-nft51
-rw-r--r--tests/shell/testcases/chains/dumps/0009masquerade_jump_1.nft9
-rw-r--r--tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.json-nft26
-rw-r--r--tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.nft4
-rw-r--r--tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.json-nft75
-rw-r--r--tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.nft13
-rw-r--r--tests/shell/testcases/chains/dumps/0013rename_0.json-nft26
-rw-r--r--tests/shell/testcases/chains/dumps/0014rename_0.json-nft34
-rw-r--r--tests/shell/testcases/chains/dumps/0014rename_0.nft7
-rw-r--r--tests/shell/testcases/chains/dumps/0015check_jump_loop_1.json-nft49
-rw-r--r--tests/shell/testcases/chains/dumps/0015check_jump_loop_1.nft8
-rw-r--r--tests/shell/testcases/chains/dumps/0016delete_handle_0.json-nft57
-rw-r--r--tests/shell/testcases/chains/dumps/0017masquerade_jump_1.json-nft53
-rw-r--r--tests/shell/testcases/chains/dumps/0017masquerade_jump_1.nft9
-rw-r--r--tests/shell/testcases/chains/dumps/0018check_jump_loop_1.json-nft49
-rw-r--r--tests/shell/testcases/chains/dumps/0018check_jump_loop_1.nft8
-rw-r--r--tests/shell/testcases/chains/dumps/0019masquerade_jump_1.json-nft70
-rw-r--r--tests/shell/testcases/chains/dumps/0019masquerade_jump_1.nft9
-rw-r--r--tests/shell/testcases/chains/dumps/0020depth_1.json-nft475
-rw-r--r--tests/shell/testcases/chains/dumps/0020depth_1.nft84
-rw-r--r--tests/shell/testcases/chains/dumps/0021prio_0.json-nft4743
-rw-r--r--tests/shell/testcases/chains/dumps/0021prio_0.nft20
-rw-r--r--tests/shell/testcases/chains/dumps/0022prio_dummy_1.json-nft18
-rw-r--r--tests/shell/testcases/chains/dumps/0022prio_dummy_1.nft2
-rw-r--r--tests/shell/testcases/chains/dumps/0023prio_inet_srcnat_1.json-nft32
-rw-r--r--tests/shell/testcases/chains/dumps/0023prio_inet_srcnat_1.nft6
-rw-r--r--tests/shell/testcases/chains/dumps/0024prio_inet_dstnat_1.json-nft32
-rw-r--r--tests/shell/testcases/chains/dumps/0024prio_inet_dstnat_1.nft6
-rw-r--r--tests/shell/testcases/chains/dumps/0025prio_arp_1.json-nft18
-rw-r--r--tests/shell/testcases/chains/dumps/0025prio_arp_1.nft2
-rw-r--r--tests/shell/testcases/chains/dumps/0026prio_netdev_1.json-nft18
-rw-r--r--tests/shell/testcases/chains/dumps/0026prio_netdev_1.nft2
-rw-r--r--tests/shell/testcases/chains/dumps/0027prio_bridge_dstnat_1.json-nft18
-rw-r--r--tests/shell/testcases/chains/dumps/0027prio_bridge_dstnat_1.nft2
-rw-r--r--tests/shell/testcases/chains/dumps/0028prio_bridge_out_1.json-nft18
-rw-r--r--tests/shell/testcases/chains/dumps/0028prio_bridge_out_1.nft2
-rw-r--r--tests/shell/testcases/chains/dumps/0029prio_bridge_srcnat_1.json-nft18
-rw-r--r--tests/shell/testcases/chains/dumps/0029prio_bridge_srcnat_1.nft2
-rw-r--r--tests/shell/testcases/chains/dumps/0030create_0.json-nft26
-rw-r--r--tests/shell/testcases/chains/dumps/0031priority_variable_0.json-nft30
-rw-r--r--tests/shell/testcases/chains/dumps/0032priority_variable_0.json-nft54
-rw-r--r--tests/shell/testcases/chains/dumps/0033priority_variable_1.json-nft11
-rw-r--r--tests/shell/testcases/chains/dumps/0033priority_variable_1.nft0
-rw-r--r--tests/shell/testcases/chains/dumps/0034priority_variable_1.json-nft11
-rw-r--r--tests/shell/testcases/chains/dumps/0034priority_variable_1.nft0
-rw-r--r--tests/shell/testcases/chains/dumps/0035policy_variable_0.json-nft30
-rw-r--r--tests/shell/testcases/chains/dumps/0036policy_variable_0.json-nft30
-rw-r--r--tests/shell/testcases/chains/dumps/0036policy_variable_0.nft (renamed from tests/shell/testcases/nft-f/dumps/0026policy_variable_0.nft)0
-rw-r--r--tests/shell/testcases/chains/dumps/0037policy_variable_1.json-nft11
-rw-r--r--tests/shell/testcases/chains/dumps/0037policy_variable_1.nft0
-rw-r--r--tests/shell/testcases/chains/dumps/0038policy_variable_1.json-nft11
-rw-r--r--tests/shell/testcases/chains/dumps/0038policy_variable_1.nft0
-rw-r--r--tests/shell/testcases/chains/dumps/0039negative_priority_0.json-nft30
-rw-r--r--tests/shell/testcases/chains/dumps/0039negative_priority_0.nft5
-rw-r--r--tests/shell/testcases/chains/dumps/0042chain_variable_0.json-nft90
-rw-r--r--tests/shell/testcases/chains/dumps/0042chain_variable_0.nft8
-rw-r--r--tests/shell/testcases/chains/dumps/0043chain_ingress_0.json-nft55
-rw-r--r--tests/shell/testcases/chains/dumps/0043chain_ingress_0.nft (renamed from tests/shell/testcases/chains/dumps/0043chain_ingress.nft)4
-rw-r--r--tests/shell/testcases/chains/dumps/0044chain_destroy_0.json-nft18
-rw-r--r--tests/shell/testcases/chains/dumps/0044chain_destroy_0.nft2
-rw-r--r--tests/shell/testcases/chains/dumps/netdev_chain_0.json-nft18
-rw-r--r--tests/shell/testcases/chains/dumps/netdev_chain_0.nft2
-rw-r--r--tests/shell/testcases/chains/dumps/netdev_chain_autoremove.json-nft11
-rw-r--r--tests/shell/testcases/chains/dumps/netdev_chain_autoremove.nft0
-rw-r--r--tests/shell/testcases/chains/dumps/netdev_chain_dev_gone.nodump0
-rw-r--r--tests/shell/testcases/chains/dumps/netdev_chain_multidev_gone.nodump0
-rw-r--r--tests/shell/testcases/chains/dumps/netdev_multidev_netns_gone.nodump0
-rw-r--r--tests/shell/testcases/chains/dumps/netdev_netns_gone.nodump0
-rwxr-xr-xtests/shell/testcases/chains/netdev_chain_029
-rwxr-xr-xtests/shell/testcases/chains/netdev_chain_autoremove9
-rwxr-xr-xtests/shell/testcases/chains/netdev_chain_dev_gone34
-rwxr-xr-xtests/shell/testcases/chains/netdev_chain_multidev_gone41
-rwxr-xr-xtests/shell/testcases/chains/netdev_multidev_netns_gone43
-rwxr-xr-xtests/shell/testcases/chains/netdev_netns_gone37
-rwxr-xr-xtests/shell/testcases/comments/comments_044
-rw-r--r--tests/shell/testcases/comments/dumps/comments_0.json-nft135
-rw-r--r--tests/shell/testcases/comments/dumps/comments_0.nft12
-rwxr-xr-xtests/shell/testcases/flowtable/0012flowtable_variable_08
-rwxr-xr-xtests/shell/testcases/flowtable/0013addafterdelete_02
-rwxr-xr-xtests/shell/testcases/flowtable/0014addafterdelete_02
-rwxr-xr-xtests/shell/testcases/flowtable/0015destroy_020
-rw-r--r--tests/shell/testcases/flowtable/dumps/0001flowtable_0.json-nft53
-rw-r--r--tests/shell/testcases/flowtable/dumps/0002create_flowtable_0.json-nft29
-rw-r--r--tests/shell/testcases/flowtable/dumps/0002create_flowtable_0.nft6
-rw-r--r--tests/shell/testcases/flowtable/dumps/0003add_after_flush_0.json-nft29
-rw-r--r--tests/shell/testcases/flowtable/dumps/0003add_after_flush_0.nft6
-rw-r--r--tests/shell/testcases/flowtable/dumps/0004delete_after_add_0.json-nft18
-rw-r--r--tests/shell/testcases/flowtable/dumps/0004delete_after_add_0.nft2
-rw-r--r--tests/shell/testcases/flowtable/dumps/0005delete_in_use_1.json-nft53
-rw-r--r--tests/shell/testcases/flowtable/dumps/0005delete_in_use_1.nft10
-rw-r--r--tests/shell/testcases/flowtable/dumps/0006segfault_0.json-nft18
-rw-r--r--tests/shell/testcases/flowtable/dumps/0006segfault_0.nft2
-rw-r--r--tests/shell/testcases/flowtable/dumps/0007prio_0.json-nft18
-rw-r--r--tests/shell/testcases/flowtable/dumps/0007prio_0.nft2
-rw-r--r--tests/shell/testcases/flowtable/dumps/0008prio_1.json-nft18
-rw-r--r--tests/shell/testcases/flowtable/dumps/0008prio_1.nft2
-rw-r--r--tests/shell/testcases/flowtable/dumps/0009deleteafterflush_0.json-nft26
-rw-r--r--tests/shell/testcases/flowtable/dumps/0009deleteafterflush_0.nft4
-rw-r--r--tests/shell/testcases/flowtable/dumps/0010delete_handle_0.json-nft18
-rw-r--r--tests/shell/testcases/flowtable/dumps/0010delete_handle_0.nft2
-rw-r--r--tests/shell/testcases/flowtable/dumps/0011deleteafterflush_0.json-nft26
-rw-r--r--tests/shell/testcases/flowtable/dumps/0011deleteafterflush_0.nft4
-rw-r--r--tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.json-nft47
-rw-r--r--tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.nft4
-rw-r--r--tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.json-nft29
-rw-r--r--tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft6
-rw-r--r--tests/shell/testcases/flowtable/dumps/0014addafterdelete_0.json-nft63
-rw-r--r--tests/shell/testcases/flowtable/dumps/0014addafterdelete_0.nft12
-rw-r--r--tests/shell/testcases/flowtable/dumps/0015destroy_0.json-nft18
-rw-r--r--tests/shell/testcases/flowtable/dumps/0015destroy_0.nft2
-rwxr-xr-xtests/shell/testcases/include/0003includepath_04
-rwxr-xr-xtests/shell/testcases/include/0020include_chain_030
-rw-r--r--tests/shell/testcases/include/dumps/0001absolute_0.json-nft18
-rw-r--r--tests/shell/testcases/include/dumps/0002relative_0.json-nft18
-rw-r--r--tests/shell/testcases/include/dumps/0003includepath_0.json-nft18
-rw-r--r--tests/shell/testcases/include/dumps/0004endlessloop_1.json-nft11
-rw-r--r--tests/shell/testcases/include/dumps/0004endlessloop_1.nft0
-rw-r--r--tests/shell/testcases/include/dumps/0005glob_empty_0.json-nft11
-rw-r--r--tests/shell/testcases/include/dumps/0005glob_empty_0.nft0
-rw-r--r--tests/shell/testcases/include/dumps/0006glob_single_0.json-nft18
-rw-r--r--tests/shell/testcases/include/dumps/0007glob_double_0.json-nft25
-rw-r--r--tests/shell/testcases/include/dumps/0008glob_nofile_wildcard_0.json-nft11
-rw-r--r--tests/shell/testcases/include/dumps/0008glob_nofile_wildcard_0.nft0
-rw-r--r--tests/shell/testcases/include/dumps/0009glob_nofile_1.json-nft11
-rw-r--r--tests/shell/testcases/include/dumps/0009glob_nofile_1.nft0
-rw-r--r--tests/shell/testcases/include/dumps/0010glob_broken_file_1.json-nft11
-rw-r--r--tests/shell/testcases/include/dumps/0010glob_broken_file_1.nft0
-rw-r--r--tests/shell/testcases/include/dumps/0011glob_dependency_0.json-nft26
-rw-r--r--tests/shell/testcases/include/dumps/0012glob_dependency_1.json-nft11
-rw-r--r--tests/shell/testcases/include/dumps/0012glob_dependency_1.nft0
-rw-r--r--tests/shell/testcases/include/dumps/0013glob_dotfile_0.json-nft18
-rw-r--r--tests/shell/testcases/include/dumps/0013input_descriptors_included_files_0.json-nft11
-rw-r--r--tests/shell/testcases/include/dumps/0013input_descriptors_included_files_0.nft0
-rw-r--r--tests/shell/testcases/include/dumps/0014glob_directory_0.json-nft11
-rw-r--r--tests/shell/testcases/include/dumps/0014glob_directory_0.nft0
-rw-r--r--tests/shell/testcases/include/dumps/0015doubleincludepath_0.json-nft26
-rw-r--r--tests/shell/testcases/include/dumps/0016maxdepth_0.json-nft11
-rw-r--r--tests/shell/testcases/include/dumps/0016maxdepth_0.nft0
-rw-r--r--tests/shell/testcases/include/dumps/0017glob_more_than_maxdepth_1.json-nft11
-rw-r--r--tests/shell/testcases/include/dumps/0017glob_more_than_maxdepth_1.nft0
-rw-r--r--tests/shell/testcases/include/dumps/0018include_error_0.json-nft11
-rw-r--r--tests/shell/testcases/include/dumps/0018include_error_0.nft0
-rw-r--r--tests/shell/testcases/include/dumps/0019include_error_0.json-nft11
-rw-r--r--tests/shell/testcases/include/dumps/0019include_error_0.nft0
-rw-r--r--tests/shell/testcases/include/dumps/0020include_chain_0.json-nft128
-rw-r--r--tests/shell/testcases/include/dumps/0020include_chain_0.nft11
-rwxr-xr-xtests/shell/testcases/json/0001set_statements_011
-rwxr-xr-xtests/shell/testcases/json/0002table_map_012
-rwxr-xr-xtests/shell/testcases/json/0003json_schema_version_011
-rwxr-xr-xtests/shell/testcases/json/0004json_schema_version_113
-rwxr-xr-xtests/shell/testcases/json/0005secmark_objref_012
-rwxr-xr-xtests/shell/testcases/json/0006obj_comment_012
-rw-r--r--tests/shell/testcases/json/dumps/0001set_statements_0.json-nft100
-rw-r--r--tests/shell/testcases/json/dumps/0001set_statements_0.nft12
-rw-r--r--tests/shell/testcases/json/dumps/0002table_map_0.json-nft33
-rw-r--r--tests/shell/testcases/json/dumps/0002table_map_0.nft6
-rw-r--r--tests/shell/testcases/json/dumps/0003json_schema_version_0.json-nft11
-rw-r--r--tests/shell/testcases/json/dumps/0003json_schema_version_0.nft0
-rw-r--r--tests/shell/testcases/json/dumps/0004json_schema_version_1.json-nft11
-rw-r--r--tests/shell/testcases/json/dumps/0004json_schema_version_1.nft0
-rw-r--r--tests/shell/testcases/json/dumps/0005secmark_objref_0.json-nft233
-rw-r--r--tests/shell/testcases/json/dumps/0005secmark_objref_0.nft18
-rw-r--r--tests/shell/testcases/json/dumps/0006obj_comment_0.json-nft29
-rw-r--r--tests/shell/testcases/json/dumps/0006obj_comment_0.nft6
-rw-r--r--tests/shell/testcases/json/dumps/netdev.json-nft18
-rw-r--r--tests/shell/testcases/json/dumps/netdev.nft2
-rwxr-xr-xtests/shell/testcases/json/netdev28
-rwxr-xr-xtests/shell/testcases/listing/0013objects_050
-rwxr-xr-xtests/shell/testcases/listing/0020flowtable_057
-rwxr-xr-xtests/shell/testcases/listing/0021ruleset_json_terse_013
-rwxr-xr-xtests/shell/testcases/listing/0022terse_04
-rw-r--r--tests/shell/testcases/listing/dumps/0001ruleset_0.json-nft18
-rw-r--r--tests/shell/testcases/listing/dumps/0002ruleset_0.json-nft11
-rw-r--r--tests/shell/testcases/listing/dumps/0002ruleset_0.nft0
-rw-r--r--tests/shell/testcases/listing/dumps/0003table_0.json-nft18
-rw-r--r--tests/shell/testcases/listing/dumps/0003table_0.nft2
-rw-r--r--tests/shell/testcases/listing/dumps/0004table_0.json-nft25
-rw-r--r--tests/shell/testcases/listing/dumps/0004table_0.nft4
-rw-r--r--tests/shell/testcases/listing/dumps/0005ruleset_ip_0.json-nft46
-rw-r--r--tests/shell/testcases/listing/dumps/0005ruleset_ip_0.nft10
-rw-r--r--tests/shell/testcases/listing/dumps/0006ruleset_ip6_0.json-nft46
-rw-r--r--tests/shell/testcases/listing/dumps/0006ruleset_ip6_0.nft10
-rw-r--r--tests/shell/testcases/listing/dumps/0007ruleset_inet_0.json-nft46
-rw-r--r--tests/shell/testcases/listing/dumps/0007ruleset_inet_0.nft10
-rw-r--r--tests/shell/testcases/listing/dumps/0008ruleset_arp_0.json-nft46
-rw-r--r--tests/shell/testcases/listing/dumps/0008ruleset_arp_0.nft10
-rw-r--r--tests/shell/testcases/listing/dumps/0009ruleset_bridge_0.json-nft46
-rw-r--r--tests/shell/testcases/listing/dumps/0009ruleset_bridge_0.nft10
-rw-r--r--tests/shell/testcases/listing/dumps/0010sets_0.json-nft124
-rw-r--r--tests/shell/testcases/listing/dumps/0010sets_0.nft39
-rw-r--r--tests/shell/testcases/listing/dumps/0011sets_0.json-nft220
-rw-r--r--tests/shell/testcases/listing/dumps/0011sets_0.nft25
-rw-r--r--tests/shell/testcases/listing/dumps/0012sets_0.json-nft124
-rw-r--r--tests/shell/testcases/listing/dumps/0012sets_0.nft39
-rw-r--r--tests/shell/testcases/listing/dumps/0013objects_0.json-nft75
-rw-r--r--tests/shell/testcases/listing/dumps/0013objects_0.nft27
-rw-r--r--tests/shell/testcases/listing/dumps/0014objects_0.json-nft47
-rw-r--r--tests/shell/testcases/listing/dumps/0014objects_0.nft12
-rw-r--r--tests/shell/testcases/listing/dumps/0015dynamic_0.json-nft38
-rw-r--r--tests/shell/testcases/listing/dumps/0015dynamic_0.nft7
-rw-r--r--tests/shell/testcases/listing/dumps/0016anonymous_0.json-nft85
-rw-r--r--tests/shell/testcases/listing/dumps/0016anonymous_0.nft6
-rw-r--r--tests/shell/testcases/listing/dumps/0017objects_0.json-nft28
-rw-r--r--tests/shell/testcases/listing/dumps/0017objects_0.nft5
-rw-r--r--tests/shell/testcases/listing/dumps/0018data_0.json-nft28
-rw-r--r--tests/shell/testcases/listing/dumps/0018data_0.nft5
-rw-r--r--tests/shell/testcases/listing/dumps/0019set_0.json-nft27
-rw-r--r--tests/shell/testcases/listing/dumps/0019set_0.nft5
-rw-r--r--tests/shell/testcases/listing/dumps/0020flowtable_0.json-nft67
-rw-r--r--tests/shell/testcases/listing/dumps/0020flowtable_0.nft20
-rw-r--r--tests/shell/testcases/listing/dumps/0021ruleset_json_terse_0.json-nft39
-rw-r--r--tests/shell/testcases/listing/dumps/0021ruleset_json_terse_0.nft9
-rw-r--r--tests/shell/testcases/listing/dumps/0022terse_0.json-nft88
-rw-r--r--tests/shell/testcases/listing/dumps/0022terse_0.nft12
-rw-r--r--tests/shell/testcases/listing/dumps/meta_time.nodump0
-rwxr-xr-xtests/shell/testcases/listing/meta_time67
-rwxr-xr-xtests/shell/testcases/maps/0004interval_map_create_once_08
-rwxr-xr-xtests/shell/testcases/maps/0009vmap_04
-rwxr-xr-xtests/shell/testcases/maps/0010concat_map_02
-rwxr-xr-xtests/shell/testcases/maps/0011vmap_010
-rwxr-xr-xtests/shell/testcases/maps/0012map_concat_024
-rwxr-xr-xtests/shell/testcases/maps/0013map_02
-rwxr-xr-xtests/shell/testcases/maps/0014destroy_012
-rwxr-xr-xtests/shell/testcases/maps/0016map_leak_038
-rwxr-xr-xtests/shell/testcases/maps/0017_map_variable_032
-rwxr-xr-xtests/shell/testcases/maps/0018map_leak_timeout_050
-rwxr-xr-xtests/shell/testcases/maps/0024named_objects_0 (renamed from tests/shell/testcases/sets/0024named_objects_0)0
-rwxr-xr-xtests/shell/testcases/maps/anon_objmap_concat8
-rw-r--r--tests/shell/testcases/maps/dumps/0003map_add_many_elements_0.json-nft3874
-rw-r--r--tests/shell/testcases/maps/dumps/0003map_add_many_elements_0.nft486
-rw-r--r--tests/shell/testcases/maps/dumps/0004interval_map_create_once_0.nodump0
-rw-r--r--tests/shell/testcases/maps/dumps/0005interval_map_add_many_elements_0.json-nft69
-rw-r--r--tests/shell/testcases/maps/dumps/0006interval_map_overlap_0.json-nft51
-rw-r--r--tests/shell/testcases/maps/dumps/0007named_ifname_dtype_0.json-nft102
-rw-r--r--tests/shell/testcases/maps/dumps/0008interval_map_delete_0.json-nft159
-rw-r--r--tests/shell/testcases/maps/dumps/0008interval_map_delete_0.nft15
-rw-r--r--tests/shell/testcases/maps/dumps/0009vmap_0.json-nft117
-rw-r--r--tests/shell/testcases/maps/dumps/0009vmap_0.nft2
-rw-r--r--tests/shell/testcases/maps/dumps/0010concat_map_0.json-nft106
-rw-r--r--tests/shell/testcases/maps/dumps/0010concat_map_0.nft2
-rw-r--r--tests/shell/testcases/maps/dumps/0011vmap_0.json-nft145
-rw-r--r--tests/shell/testcases/maps/dumps/0012map_0.json-nft97
-rw-r--r--tests/shell/testcases/maps/dumps/0012map_concat_0.json-nft132
-rw-r--r--tests/shell/testcases/maps/dumps/0012map_concat_0.nft14
-rw-r--r--tests/shell/testcases/maps/dumps/0013map_0.json-nft128
-rw-r--r--tests/shell/testcases/maps/dumps/0014destroy_0.json-nft18
-rw-r--r--tests/shell/testcases/maps/dumps/0014destroy_0.nft2
-rw-r--r--tests/shell/testcases/maps/dumps/0016map_leak_0.json-nft11
-rw-r--r--tests/shell/testcases/maps/dumps/0016map_leak_0.nft0
-rw-r--r--tests/shell/testcases/maps/dumps/0017_map_variable_0.json-nft58
-rw-r--r--tests/shell/testcases/maps/dumps/0017_map_variable_0.nft11
-rw-r--r--tests/shell/testcases/maps/dumps/0018map_leak_timeout_0.json-nft11
-rw-r--r--tests/shell/testcases/maps/dumps/0018map_leak_timeout_0.nft0
-rw-r--r--tests/shell/testcases/maps/dumps/0024named_objects_0.json-nft165
-rw-r--r--tests/shell/testcases/maps/dumps/0024named_objects_0.nft (renamed from tests/shell/testcases/sets/dumps/0024named_objects_0.nft)0
-rw-r--r--tests/shell/testcases/maps/dumps/anon_objmap_concat.json-nft116
-rw-r--r--tests/shell/testcases/maps/dumps/anon_objmap_concat.nft16
-rw-r--r--tests/shell/testcases/maps/dumps/anonymous_snat_map_0.json-nft58
-rw-r--r--tests/shell/testcases/maps/dumps/different_map_types_1.json-nft30
-rw-r--r--tests/shell/testcases/maps/dumps/different_map_types_1.nft5
-rw-r--r--tests/shell/testcases/maps/dumps/map_catchall_double_deactivate.json-nft26
-rw-r--r--tests/shell/testcases/maps/dumps/map_catchall_double_deactivate.nft4
-rw-r--r--tests/shell/testcases/maps/dumps/map_catchall_double_free.nodump0
-rw-r--r--tests/shell/testcases/maps/dumps/map_catchall_double_free_2.json-nft46
-rw-r--r--tests/shell/testcases/maps/dumps/map_catchall_double_free_2.nft9
-rw-r--r--tests/shell/testcases/maps/dumps/map_with_flags_0.json-nft31
-rw-r--r--tests/shell/testcases/maps/dumps/named_ct_objects.nft71
-rw-r--r--tests/shell/testcases/maps/dumps/named_limits.json-nft328
-rw-r--r--tests/shell/testcases/maps/dumps/named_limits.nft55
-rw-r--r--tests/shell/testcases/maps/dumps/named_snat_map_0.json-nft67
-rw-r--r--tests/shell/testcases/maps/dumps/nat_addr_port.nft8
-rw-r--r--tests/shell/testcases/maps/dumps/pipapo_double_flush.json-nft42
-rw-r--r--tests/shell/testcases/maps/dumps/pipapo_double_flush.nft9
-rw-r--r--tests/shell/testcases/maps/dumps/typeof_integer_0.nft20
-rw-r--r--tests/shell/testcases/maps/dumps/typeof_maps_0.nft9
-rw-r--r--tests/shell/testcases/maps/dumps/typeof_maps_add_delete.json-nft283
-rw-r--r--tests/shell/testcases/maps/dumps/typeof_maps_add_delete.nft22
-rw-r--r--tests/shell/testcases/maps/dumps/typeof_maps_concat.nft11
-rw-r--r--tests/shell/testcases/maps/dumps/typeof_maps_concat_update_0.nft13
-rw-r--r--tests/shell/testcases/maps/dumps/typeof_maps_update_0.json-nft110
-rw-r--r--tests/shell/testcases/maps/dumps/typeof_raw_0.nft13
-rw-r--r--tests/shell/testcases/maps/dumps/vmap_mark_bitwise_0.json-nft158
-rw-r--r--tests/shell/testcases/maps/dumps/vmap_mark_bitwise_0.nft26
-rw-r--r--tests/shell/testcases/maps/dumps/vmap_timeout.json-nft229
-rw-r--r--tests/shell/testcases/maps/dumps/vmap_timeout.nft36
-rw-r--r--tests/shell/testcases/maps/dumps/vmap_unary.nft11
-rwxr-xr-xtests/shell/testcases/maps/map_catchall_double_deactivate13
-rwxr-xr-xtests/shell/testcases/maps/map_catchall_double_free13
-rwxr-xr-xtests/shell/testcases/maps/map_catchall_double_free_227
-rwxr-xr-xtests/shell/testcases/maps/named_ct_objects94
-rwxr-xr-xtests/shell/testcases/maps/named_limits61
-rwxr-xr-xtests/shell/testcases/maps/pipapo_double_flush25
-rwxr-xr-xtests/shell/testcases/maps/typeof_integer_029
-rwxr-xr-xtests/shell/testcases/maps/typeof_maps_075
-rwxr-xr-xtests/shell/testcases/maps/typeof_maps_add_delete56
-rwxr-xr-xtests/shell/testcases/maps/typeof_maps_concat6
-rwxr-xr-xtests/shell/testcases/maps/typeof_maps_concat_update_019
-rwxr-xr-xtests/shell/testcases/maps/typeof_raw_018
-rwxr-xr-xtests/shell/testcases/maps/vmap_mark_bitwise_040
-rwxr-xr-xtests/shell/testcases/maps/vmap_timeout53
-rwxr-xr-xtests/shell/testcases/maps/vmap_unary19
-rw-r--r--tests/shell/testcases/netns/dumps/0001nft-f_0.json-nft11
-rw-r--r--tests/shell/testcases/netns/dumps/0001nft-f_0.nft0
-rw-r--r--tests/shell/testcases/netns/dumps/0002loosecommands_0.json-nft11
-rw-r--r--tests/shell/testcases/netns/dumps/0002loosecommands_0.nft0
-rw-r--r--tests/shell/testcases/netns/dumps/0003many_0.json-nft11
-rw-r--r--tests/shell/testcases/netns/dumps/0003many_0.nft0
-rwxr-xr-xtests/shell/testcases/nft-f/0011manydefines_016
-rwxr-xr-xtests/shell/testcases/nft-f/0017ct_timeout_obj_02
-rwxr-xr-xtests/shell/testcases/nft-f/0018ct_expectation_obj_02
-rwxr-xr-xtests/shell/testcases/nft-f/0025empty_dynset_08
-rwxr-xr-xtests/shell/testcases/nft-f/0030variable_reuse_019
-rwxr-xr-xtests/shell/testcases/nft-f/0031vmap_string_021
-rwxr-xr-xtests/shell/testcases/nft-f/0032pknock_034
-rw-r--r--tests/shell/testcases/nft-f/dumps/0001define_slash_0.json-nft11
-rw-r--r--tests/shell/testcases/nft-f/dumps/0001define_slash_0.nft0
-rw-r--r--tests/shell/testcases/nft-f/dumps/0002rollback_rule_0.json-nft134
-rw-r--r--tests/shell/testcases/nft-f/dumps/0003rollback_jump_0.json-nft134
-rw-r--r--tests/shell/testcases/nft-f/dumps/0004rollback_set_0.json-nft134
-rw-r--r--tests/shell/testcases/nft-f/dumps/0005rollback_map_0.json-nft134
-rw-r--r--tests/shell/testcases/nft-f/dumps/0006action_object_0.json-nft11
-rw-r--r--tests/shell/testcases/nft-f/dumps/0006action_object_0.nft0
-rw-r--r--tests/shell/testcases/nft-f/dumps/0007action_object_set_segfault_1.json-nft11
-rw-r--r--tests/shell/testcases/nft-f/dumps/0007action_object_set_segfault_1.nft0
-rw-r--r--tests/shell/testcases/nft-f/dumps/0008split_tables_0.json-nft67
-rw-r--r--tests/shell/testcases/nft-f/dumps/0009variable_0.json-nft44
-rw-r--r--tests/shell/testcases/nft-f/dumps/0010variable_0.json-nft30
-rw-r--r--tests/shell/testcases/nft-f/dumps/0011manydefines_0.nodump0
-rw-r--r--tests/shell/testcases/nft-f/dumps/0012different_defines_0.json-nft778
-rw-r--r--tests/shell/testcases/nft-f/dumps/0013defines_1.json-nft11
-rw-r--r--tests/shell/testcases/nft-f/dumps/0013defines_1.nft0
-rw-r--r--tests/shell/testcases/nft-f/dumps/0014defines_1.json-nft11
-rw-r--r--tests/shell/testcases/nft-f/dumps/0014defines_1.nft0
-rw-r--r--tests/shell/testcases/nft-f/dumps/0015defines_1.json-nft11
-rw-r--r--tests/shell/testcases/nft-f/dumps/0015defines_1.nft0
-rw-r--r--tests/shell/testcases/nft-f/dumps/0016redefines_1.json-nft80
-rw-r--r--tests/shell/testcases/nft-f/dumps/0016redefines_1.nft6
-rw-r--r--tests/shell/testcases/nft-f/dumps/0017ct_timeout_obj_0.json-nft53
-rw-r--r--tests/shell/testcases/nft-f/dumps/0017ct_timeout_obj_0.nft2
-rw-r--r--tests/shell/testcases/nft-f/dumps/0018ct_expectation_obj_0.json-nft52
-rw-r--r--tests/shell/testcases/nft-f/dumps/0018ct_expectation_obj_0.nft13
-rw-r--r--tests/shell/testcases/nft-f/dumps/0018jump_variable_0.json-nft49
-rw-r--r--tests/shell/testcases/nft-f/dumps/0019jump_variable_1.json-nft11
-rw-r--r--tests/shell/testcases/nft-f/dumps/0019jump_variable_1.nft0
-rw-r--r--tests/shell/testcases/nft-f/dumps/0020jump_variable_1.json-nft11
-rw-r--r--tests/shell/testcases/nft-f/dumps/0020jump_variable_1.nft0
-rw-r--r--tests/shell/testcases/nft-f/dumps/0021list_ruleset_0.json-nft30
-rw-r--r--tests/shell/testcases/nft-f/dumps/0022variables_0.json-nft115
-rw-r--r--tests/shell/testcases/nft-f/dumps/0023check_1.json-nft30
-rw-r--r--tests/shell/testcases/nft-f/dumps/0023check_1.nft5
-rw-r--r--tests/shell/testcases/nft-f/dumps/0024priority_0.json-nft95
-rw-r--r--tests/shell/testcases/nft-f/dumps/0025empty_dynset_0.json-nft111
-rw-r--r--tests/shell/testcases/nft-f/dumps/0025empty_dynset_0.nft2
-rw-r--r--tests/shell/testcases/nft-f/dumps/0026listing_0.json-nft56
-rw-r--r--tests/shell/testcases/nft-f/dumps/0026listing_0.nft5
-rw-r--r--tests/shell/testcases/nft-f/dumps/0027split_chains_0.json-nft53
-rw-r--r--tests/shell/testcases/nft-f/dumps/0028variable_cmdline_0.json-nft34
-rw-r--r--tests/shell/testcases/nft-f/dumps/0029split_file_0.json-nft61
-rw-r--r--tests/shell/testcases/nft-f/dumps/0029split_file_0.nft10
-rw-r--r--tests/shell/testcases/nft-f/dumps/0030variable_reuse_0.json-nft44
-rw-r--r--tests/shell/testcases/nft-f/dumps/0030variable_reuse_0.nft11
-rw-r--r--tests/shell/testcases/nft-f/dumps/0031vmap_string_0.json-nft11
-rw-r--r--tests/shell/testcases/nft-f/dumps/0031vmap_string_0.nft0
-rw-r--r--tests/shell/testcases/nft-f/dumps/0032pknock_0.json-nft484
-rw-r--r--tests/shell/testcases/nft-f/dumps/0032pknock_0.nft25
-rw-r--r--tests/shell/testcases/nft-f/dumps/sample-ruleset.nft239
-rwxr-xr-xtests/shell/testcases/nft-f/sample-ruleset262
-rw-r--r--tests/shell/testcases/nft-i/dumps/0001define_0.json-nft11
-rw-r--r--tests/shell/testcases/nft-i/dumps/0001define_0.nft0
-rw-r--r--tests/shell/testcases/optimizations/dumps/dependency_kill.json-nft776
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_nat.json-nft379
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_nat.nft21
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_nat_concat.json-nft200
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_nat_concat.nft8
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_nat_inet.json-nft208
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_nat_inet.nft11
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_reject.json-nft320
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_reject.nft13
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_stmts.json-nft63
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_stmts.nft5
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_stmts_concat.json-nft374
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft18
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.json-nft167
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.nft9
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_stmts_vmap.json-nft182
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_stmts_vmap.nft13
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_vmap_raw.json-nft438
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_vmap_raw.nft31
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_vmaps.json-nft205
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_vmaps.nft20
-rw-r--r--tests/shell/testcases/optimizations/dumps/not_mergeable.json-nft140
-rw-r--r--tests/shell/testcases/optimizations/dumps/not_mergeable.nft19
-rw-r--r--tests/shell/testcases/optimizations/dumps/ruleset.json-nft11
-rw-r--r--tests/shell/testcases/optimizations/dumps/ruleset.nft0
-rw-r--r--tests/shell/testcases/optimizations/dumps/single_anon_set.json-nft360
-rw-r--r--tests/shell/testcases/optimizations/dumps/single_anon_set.nft.input35
-rw-r--r--tests/shell/testcases/optimizations/dumps/single_anon_set_expr.json-nft59
-rw-r--r--tests/shell/testcases/optimizations/dumps/single_anon_set_expr.nft5
-rw-r--r--tests/shell/testcases/optimizations/dumps/skip_merge.json-nft235
-rw-r--r--tests/shell/testcases/optimizations/dumps/skip_merge.nft23
-rw-r--r--tests/shell/testcases/optimizations/dumps/skip_non_eq.json-nft108
-rw-r--r--tests/shell/testcases/optimizations/dumps/skip_non_eq.nft6
-rw-r--r--tests/shell/testcases/optimizations/dumps/skip_unsupported.json-nft256
-rw-r--r--tests/shell/testcases/optimizations/dumps/skip_unsupported.nft18
-rw-r--r--tests/shell/testcases/optimizations/dumps/variables.json-nft11
-rw-r--r--tests/shell/testcases/optimizations/dumps/variables.nft0
-rwxr-xr-xtests/shell/testcases/optimizations/merge_nat38
-rwxr-xr-xtests/shell/testcases/optimizations/merge_nat_concat18
-rwxr-xr-xtests/shell/testcases/optimizations/merge_nat_inet21
-rwxr-xr-xtests/shell/testcases/optimizations/merge_reject26
-rwxr-xr-xtests/shell/testcases/optimizations/merge_stmts13
-rwxr-xr-xtests/shell/testcases/optimizations/merge_stmts_concat37
-rwxr-xr-xtests/shell/testcases/optimizations/merge_stmts_concat_vmap17
-rwxr-xr-xtests/shell/testcases/optimizations/merge_stmts_vmap23
-rwxr-xr-xtests/shell/testcases/optimizations/merge_vmap_raw34
-rwxr-xr-xtests/shell/testcases/optimizations/merge_vmaps31
-rwxr-xr-xtests/shell/testcases/optimizations/not_mergeable22
-rwxr-xr-xtests/shell/testcases/optimizations/ruleset170
-rwxr-xr-xtests/shell/testcases/optimizations/single_anon_set44
-rwxr-xr-xtests/shell/testcases/optimizations/single_anon_set_expr26
-rwxr-xr-xtests/shell/testcases/optimizations/skip_merge34
-rwxr-xr-xtests/shell/testcases/optimizations/skip_non_eq12
-rwxr-xr-xtests/shell/testcases/optimizations/skip_unsupported25
-rwxr-xr-xtests/shell/testcases/optimizations/variables15
-rwxr-xr-xtests/shell/testcases/optionals/comments_chain_02
-rwxr-xr-xtests/shell/testcases/optionals/comments_objects_024
-rwxr-xr-xtests/shell/testcases/optionals/comments_table_02
-rw-r--r--tests/shell/testcases/optionals/dumps/comments_0.json-nft58
-rw-r--r--tests/shell/testcases/optionals/dumps/comments_chain_0.json-nft27
-rw-r--r--tests/shell/testcases/optionals/dumps/comments_handles_0.json-nft58
-rw-r--r--tests/shell/testcases/optionals/dumps/comments_objects_0.json-nft102
-rw-r--r--tests/shell/testcases/optionals/dumps/comments_objects_0.nft7
-rw-r--r--tests/shell/testcases/optionals/dumps/comments_objects_dup_0.json-nft11
-rw-r--r--tests/shell/testcases/optionals/dumps/comments_objects_dup_0.nft0
-rw-r--r--tests/shell/testcases/optionals/dumps/comments_table_0.json-nft19
-rw-r--r--tests/shell/testcases/optionals/dumps/delete_object_handles_0.json-nft67
-rw-r--r--tests/shell/testcases/optionals/dumps/delete_object_handles_0.nft18
-rw-r--r--tests/shell/testcases/optionals/dumps/handles_0.json-nft57
-rw-r--r--tests/shell/testcases/optionals/dumps/handles_1.json-nft57
-rw-r--r--tests/shell/testcases/optionals/dumps/handles_1.nft5
-rw-r--r--tests/shell/testcases/optionals/dumps/log_prefix_0.json-nft52
-rw-r--r--tests/shell/testcases/optionals/dumps/update_object_handles_0.json-nft39
-rw-r--r--tests/shell/testcases/optionals/dumps/update_object_handles_0.nft9
-rwxr-xr-xtests/shell/testcases/optionals/update_object_handles_02
-rwxr-xr-xtests/shell/testcases/owner/0001-flowtable-uaf26
-rw-r--r--tests/shell/testcases/owner/dumps/0001-flowtable-uaf.json-nft11
-rw-r--r--tests/shell/testcases/owner/dumps/0001-flowtable-uaf.nft0
-rw-r--r--tests/shell/testcases/owner/dumps/0002-persist.json-nft19
-rw-r--r--tests/shell/testcases/owner/dumps/0002-persist.nft3
-rw-r--r--tests/shell/testcases/packetpath/dumps/payload.nodump0
-rw-r--r--tests/shell/testcases/packetpath/dumps/set_lookups.json-nft674
-rw-r--r--tests/shell/testcases/packetpath/dumps/set_lookups.nft51
-rw-r--r--tests/shell/testcases/packetpath/dumps/tcp_options.nodump0
-rw-r--r--tests/shell/testcases/packetpath/dumps/vlan_8021ad_tag.nodump0
-rwxr-xr-xtests/shell/testcases/packetpath/flowtables96
-rwxr-xr-xtests/shell/testcases/packetpath/payload182
-rwxr-xr-xtests/shell/testcases/packetpath/set_lookups66
-rwxr-xr-xtests/shell/testcases/packetpath/tcp_options57
-rwxr-xr-xtests/shell/testcases/packetpath/vlan_8021ad_tag50
-rwxr-xr-xtests/shell/testcases/parsing/describe7
-rw-r--r--tests/shell/testcases/parsing/dumps/describe.json-nft11
-rw-r--r--tests/shell/testcases/parsing/dumps/describe.nft0
-rw-r--r--tests/shell/testcases/parsing/dumps/large_rule_pipe.json-nft4079
-rw-r--r--tests/shell/testcases/parsing/dumps/large_rule_pipe.nft561
-rw-r--r--tests/shell/testcases/parsing/dumps/log.json-nft11
-rw-r--r--tests/shell/testcases/parsing/dumps/log.nft0
-rw-r--r--tests/shell/testcases/parsing/dumps/octal.json-nft11
-rw-r--r--tests/shell/testcases/parsing/dumps/octal.nft0
-rwxr-xr-xtests/shell/testcases/parsing/large_rule_pipe571
-rwxr-xr-xtests/shell/testcases/parsing/log10
-rwxr-xr-xtests/shell/testcases/parsing/octal13
-rwxr-xr-xtests/shell/testcases/rule_management/0003insert_04
-rwxr-xr-xtests/shell/testcases/rule_management/0010replace_02
-rwxr-xr-xtests/shell/testcases/rule_management/0011reset_0170
-rwxr-xr-xtests/shell/testcases/rule_management/0012destroy_014
-rw-r--r--tests/shell/testcases/rule_management/dumps/0001addinsertposition_0.json-nft65
-rw-r--r--tests/shell/testcases/rule_management/dumps/0001addinsertposition_0.nft7
-rw-r--r--tests/shell/testcases/rule_management/dumps/0002addinsertlocation_1.json-nft52
-rw-r--r--tests/shell/testcases/rule_management/dumps/0002addinsertlocation_1.nft6
-rw-r--r--tests/shell/testcases/rule_management/dumps/0003insert_0.json-nft102
-rw-r--r--tests/shell/testcases/rule_management/dumps/0003insert_0.nft1
-rw-r--r--tests/shell/testcases/rule_management/dumps/0004replace_0.json-nft39
-rw-r--r--tests/shell/testcases/rule_management/dumps/0005replace_1.json-nft26
-rw-r--r--tests/shell/testcases/rule_management/dumps/0005replace_1.nft4
-rw-r--r--tests/shell/testcases/rule_management/dumps/0006replace_1.json-nft26
-rw-r--r--tests/shell/testcases/rule_management/dumps/0006replace_1.nft4
-rw-r--r--tests/shell/testcases/rule_management/dumps/0007delete_0.json-nft39
-rw-r--r--tests/shell/testcases/rule_management/dumps/0008delete_1.json-nft26
-rw-r--r--tests/shell/testcases/rule_management/dumps/0008delete_1.nft4
-rw-r--r--tests/shell/testcases/rule_management/dumps/0009delete_1.json-nft26
-rw-r--r--tests/shell/testcases/rule_management/dumps/0009delete_1.nft4
-rw-r--r--tests/shell/testcases/rule_management/dumps/0010replace_0.json-nft11
-rw-r--r--tests/shell/testcases/rule_management/dumps/0010replace_0.nft0
-rw-r--r--tests/shell/testcases/rule_management/dumps/0011reset_0.json-nft257
-rw-r--r--tests/shell/testcases/rule_management/dumps/0011reset_0.nft31
-rw-r--r--tests/shell/testcases/rule_management/dumps/0012destroy_0.json-nft26
-rw-r--r--tests/shell/testcases/rule_management/dumps/0012destroy_0.nft4
-rwxr-xr-xtests/shell/testcases/sets/0011add_many_elements_015
-rwxr-xr-xtests/shell/testcases/sets/0012add_delete_many_elements_014
-rwxr-xr-xtests/shell/testcases/sets/0013add_delete_many_elements_014
-rwxr-xr-xtests/shell/testcases/sets/0020comments_02
-rwxr-xr-xtests/shell/testcases/sets/0022type_selective_flush_02
-rwxr-xr-xtests/shell/testcases/sets/0024synproxy_031
-rwxr-xr-xtests/shell/testcases/sets/0030add_many_elements_interval_014
-rwxr-xr-xtests/shell/testcases/sets/0031set_timeout_size_02
-rwxr-xr-xtests/shell/testcases/sets/0034get_element_02
-rwxr-xr-xtests/shell/testcases/sets/0036add_set_element_expiration_018
-rwxr-xr-xtests/shell/testcases/sets/0038meter_list_07
-rwxr-xr-xtests/shell/testcases/sets/0043concatenated_ranges_018
-rwxr-xr-xtests/shell/testcases/sets/0043concatenated_ranges_14
-rwxr-xr-xtests/shell/testcases/sets/0044interval_overlap_016
-rwxr-xr-xtests/shell/testcases/sets/0044interval_overlap_14
-rwxr-xr-xtests/shell/testcases/sets/0046netmap_08
-rwxr-xr-xtests/shell/testcases/sets/0047nat_022
-rwxr-xr-xtests/shell/testcases/sets/0048set_counters_02
-rwxr-xr-xtests/shell/testcases/sets/0049set_define_012
-rwxr-xr-xtests/shell/testcases/sets/0051set_interval_counter_02
-rwxr-xr-xtests/shell/testcases/sets/0059set_update_multistmt_02
-rwxr-xr-xtests/shell/testcases/sets/0060set_multistmt_02
-rwxr-xr-xtests/shell/testcases/sets/0060set_multistmt_140
-rwxr-xr-xtests/shell/testcases/sets/0062set_connlimit_05
-rwxr-xr-xtests/shell/testcases/sets/0063set_catchall_02
-rwxr-xr-xtests/shell/testcases/sets/0064map_catchall_07
-rwxr-xr-xtests/shell/testcases/sets/0067nat_concat_interval_044
-rwxr-xr-xtests/shell/testcases/sets/0067nat_interval_018
-rwxr-xr-xtests/shell/testcases/sets/0068interval_stack_overflow_022
-rwxr-xr-xtests/shell/testcases/sets/0069interval_merge_028
-rwxr-xr-xtests/shell/testcases/sets/0070stacked_l2_headers6
-rwxr-xr-xtests/shell/testcases/sets/0071unclosed_prefix_interval_023
-rwxr-xr-xtests/shell/testcases/sets/0072destroy_012
-rwxr-xr-xtests/shell/testcases/sets/0073flat_interval_set11
-rwxr-xr-xtests/shell/testcases/sets/0074nested_interval_set6
-rwxr-xr-xtests/shell/testcases/sets/automerge_0131
-rwxr-xr-xtests/shell/testcases/sets/collapse_elem_019
-rwxr-xr-xtests/shell/testcases/sets/concat_interval_026
-rw-r--r--tests/shell/testcases/sets/dumps/0001named_interval_0.json-nft261
-rw-r--r--tests/shell/testcases/sets/dumps/0002named_interval_automerging_0.json-nft44
-rw-r--r--tests/shell/testcases/sets/dumps/0003named_interval_missing_flag_0.json-nft27
-rw-r--r--tests/shell/testcases/sets/dumps/0004named_interval_shadow_0.json-nft38
-rw-r--r--tests/shell/testcases/sets/dumps/0005named_interval_shadow_0.json-nft38
-rw-r--r--tests/shell/testcases/sets/dumps/0006create_set_0.json-nft27
-rw-r--r--tests/shell/testcases/sets/dumps/0007create_element_0.json-nft30
-rw-r--r--tests/shell/testcases/sets/dumps/0008comments_interval_0.json-nft38
-rw-r--r--tests/shell/testcases/sets/dumps/0008create_verdict_map_0.json-nft78
-rw-r--r--tests/shell/testcases/sets/dumps/0009comments_timeout_0.json-nft38
-rw-r--r--tests/shell/testcases/sets/dumps/0010comments_0.json-nft35
-rw-r--r--tests/shell/testcases/sets/dumps/0011add_many_elements_0.nodump0
-rw-r--r--tests/shell/testcases/sets/dumps/0012add_delete_many_elements_0.json-nft27
-rw-r--r--tests/shell/testcases/sets/dumps/0013add_delete_many_elements_0.json-nft27
-rw-r--r--tests/shell/testcases/sets/dumps/0014malformed_set_is_not_defined_0.json-nft11
-rw-r--r--tests/shell/testcases/sets/dumps/0014malformed_set_is_not_defined_0.nft0
-rw-r--r--tests/shell/testcases/sets/dumps/0015rulesetflush_0.json-nft53
-rw-r--r--tests/shell/testcases/sets/dumps/0016element_leak_0.json-nft31
-rw-r--r--tests/shell/testcases/sets/dumps/0017add_after_flush_0.json-nft31
-rw-r--r--tests/shell/testcases/sets/dumps/0018set_check_size_1.json-nft32
-rw-r--r--tests/shell/testcases/sets/dumps/0018set_check_size_1.nft7
-rw-r--r--tests/shell/testcases/sets/dumps/0019set_check_size_0.json-nft32
-rw-r--r--tests/shell/testcases/sets/dumps/0020comments_0.json-nft35
-rw-r--r--tests/shell/testcases/sets/dumps/0021nesting_0.json-nft69
-rw-r--r--tests/shell/testcases/sets/dumps/0022type_selective_flush_0.json-nft101
-rw-r--r--tests/shell/testcases/sets/dumps/0022type_selective_flush_0.nft8
-rw-r--r--tests/shell/testcases/sets/dumps/0023incomplete_add_set_command_0.json-nft18
-rw-r--r--tests/shell/testcases/sets/dumps/0024named_objects_0.json-nft165
-rw-r--r--tests/shell/testcases/sets/dumps/0024synproxy_0.json-nft131
-rw-r--r--tests/shell/testcases/sets/dumps/0024synproxy_0.nft23
-rw-r--r--tests/shell/testcases/sets/dumps/0025anonymous_set_0.json-nft102
-rw-r--r--tests/shell/testcases/sets/dumps/0026named_limit_0.json-nft75
-rw-r--r--tests/shell/testcases/sets/dumps/0027ipv6_maps_ipv4_0.json-nft38
-rw-r--r--tests/shell/testcases/sets/dumps/0028autoselect_0.json-nft168
-rw-r--r--tests/shell/testcases/sets/dumps/0028autoselect_0.nft26
-rw-r--r--tests/shell/testcases/sets/dumps/0028delete_handle_0.json-nft53
-rw-r--r--tests/shell/testcases/sets/dumps/0028delete_handle_0.nft15
-rw-r--r--tests/shell/testcases/sets/dumps/0030add_many_elements_interval_0.nodump0
-rw-r--r--tests/shell/testcases/sets/dumps/0031set_timeout_size_0.nodump0
-rw-r--r--tests/shell/testcases/sets/dumps/0032restore_set_simple_0.json-nft49
-rw-r--r--tests/shell/testcases/sets/dumps/0033add_set_simple_flat_0.json-nft49
-rw-r--r--tests/shell/testcases/sets/dumps/0033add_set_simple_flat_0.nft11
-rw-r--r--tests/shell/testcases/sets/dumps/0034get_element_0.json-nft140
-rw-r--r--tests/shell/testcases/sets/dumps/0034get_element_0.nft23
-rw-r--r--tests/shell/testcases/sets/dumps/0035add_set_elements_flat_0.json-nft30
-rw-r--r--tests/shell/testcases/sets/dumps/0035add_set_elements_flat_0.nft6
-rw-r--r--tests/shell/testcases/sets/dumps/0036add_set_element_expiration_0.nodump0
-rw-r--r--tests/shell/testcases/sets/dumps/0037_set_with_inet_service_0.json-nft159
-rw-r--r--tests/shell/testcases/sets/dumps/0038meter_list_0.json-nft96
-rw-r--r--tests/shell/testcases/sets/dumps/0038meter_list_0.nft17
-rw-r--r--tests/shell/testcases/sets/dumps/0039delete_interval_0.json-nft39
-rw-r--r--tests/shell/testcases/sets/dumps/0039delete_interval_0.nft7
-rw-r--r--tests/shell/testcases/sets/dumps/0040get_host_endian_elements_0.json-nft39
-rw-r--r--tests/shell/testcases/sets/dumps/0040get_host_endian_elements_0.nft7
-rw-r--r--tests/shell/testcases/sets/dumps/0041interval_0.json-nft33
-rw-r--r--tests/shell/testcases/sets/dumps/0041interval_0.nft7
-rw-r--r--tests/shell/testcases/sets/dumps/0042update_set_0.json-nft87
-rw-r--r--tests/shell/testcases/sets/dumps/0042update_set_0.nft15
-rw-r--r--tests/shell/testcases/sets/dumps/0043concatenated_ranges_0.json-nft98
-rw-r--r--tests/shell/testcases/sets/dumps/0043concatenated_ranges_0.nft11
-rw-r--r--tests/shell/testcases/sets/dumps/0043concatenated_ranges_1.json-nft1723
-rw-r--r--tests/shell/testcases/sets/dumps/0043concatenated_ranges_1.nft116
-rw-r--r--tests/shell/testcases/sets/dumps/0044interval_overlap_0.nodump0
-rw-r--r--tests/shell/testcases/sets/dumps/0044interval_overlap_1.json-nft529
-rw-r--r--tests/shell/testcases/sets/dumps/0044interval_overlap_1.nft106
-rw-r--r--tests/shell/testcases/sets/dumps/0045concat_ipv4_service.json-nft95
-rw-r--r--tests/shell/testcases/sets/dumps/0046netmap_0.json-nft167
-rw-r--r--tests/shell/testcases/sets/dumps/0046netmap_0.nft6
-rw-r--r--tests/shell/testcases/sets/dumps/0047nat_0.nft17
-rw-r--r--tests/shell/testcases/sets/dumps/0048set_counters_0.json-nft95
-rw-r--r--tests/shell/testcases/sets/dumps/0049set_define_0.json-nft94
-rw-r--r--tests/shell/testcases/sets/dumps/0049set_define_0.nft7
-rw-r--r--tests/shell/testcases/sets/dumps/0050set_define_1.json-nft11
-rw-r--r--tests/shell/testcases/sets/dumps/0050set_define_1.nft0
-rw-r--r--tests/shell/testcases/sets/dumps/0051set_interval_counter_0.json-nft85
-rw-r--r--tests/shell/testcases/sets/dumps/0052overlap_0.json-nft35
-rw-r--r--tests/shell/testcases/sets/dumps/0053echo_0.json-nft101
-rw-r--r--tests/shell/testcases/sets/dumps/0054comments_set_0.json-nft45
-rw-r--r--tests/shell/testcases/sets/dumps/0055tcpflags_0.json-nft138
-rw-r--r--tests/shell/testcases/sets/dumps/0055tcpflags_0.nft8
-rw-r--r--tests/shell/testcases/sets/dumps/0056dynamic_limit_0.json-nft11
-rw-r--r--tests/shell/testcases/sets/dumps/0056dynamic_limit_0.nft0
-rw-r--r--tests/shell/testcases/sets/dumps/0057set_create_fails_0.json-nft31
-rw-r--r--tests/shell/testcases/sets/dumps/0057set_create_fails_0.nft7
-rw-r--r--tests/shell/testcases/sets/dumps/0058_setupdate_timeout_0.json-nft68
-rw-r--r--tests/shell/testcases/sets/dumps/0059set_update_multistmt_0.json-nft79
-rw-r--r--tests/shell/testcases/sets/dumps/0059set_update_multistmt_0.nft2
-rw-r--r--tests/shell/testcases/sets/dumps/0060set_multistmt_0.json-nft105
-rw-r--r--tests/shell/testcases/sets/dumps/0060set_multistmt_0.nft6
-rw-r--r--tests/shell/testcases/sets/dumps/0060set_multistmt_1.json-nft105
-rw-r--r--tests/shell/testcases/sets/dumps/0060set_multistmt_1.nft15
-rw-r--r--tests/shell/testcases/sets/dumps/0061anonymous_automerge_0.json-nft57
-rw-r--r--tests/shell/testcases/sets/dumps/0062set_connlimit_0.json-nft52
-rw-r--r--tests/shell/testcases/sets/dumps/0062set_connlimit_0.nft14
-rw-r--r--tests/shell/testcases/sets/dumps/0063set_catchall_0.json-nft94
-rw-r--r--tests/shell/testcases/sets/dumps/0064map_catchall_0.json-nft220
-rw-r--r--tests/shell/testcases/sets/dumps/0064map_catchall_0.nft6
-rw-r--r--tests/shell/testcases/sets/dumps/0065_icmp_postprocessing.json-nft78
-rw-r--r--tests/shell/testcases/sets/dumps/0065_icmp_postprocessing.nft6
-rw-r--r--tests/shell/testcases/sets/dumps/0067nat_concat_interval_0.nft23
-rw-r--r--tests/shell/testcases/sets/dumps/0067nat_interval_0.nft12
-rw-r--r--tests/shell/testcases/sets/dumps/0068interval_stack_overflow_0.nodump0
-rw-r--r--tests/shell/testcases/sets/dumps/0069interval_merge_0.json-nft51
-rw-r--r--tests/shell/testcases/sets/dumps/0069interval_merge_0.nft9
-rw-r--r--tests/shell/testcases/sets/dumps/0070stacked_l2_headers.nft28
-rw-r--r--tests/shell/testcases/sets/dumps/0071unclosed_prefix_interval_0.json-nft128
-rw-r--r--tests/shell/testcases/sets/dumps/0071unclosed_prefix_interval_0.nft19
-rw-r--r--tests/shell/testcases/sets/dumps/0072destroy_0.json-nft18
-rw-r--r--tests/shell/testcases/sets/dumps/0072destroy_0.nft2
-rw-r--r--tests/shell/testcases/sets/dumps/0073flat_interval_set.json-nft52
-rw-r--r--tests/shell/testcases/sets/dumps/0073flat_interval_set.nft11
-rw-r--r--tests/shell/testcases/sets/dumps/0074nested_interval_set.json-nft52
-rw-r--r--tests/shell/testcases/sets/dumps/0074nested_interval_set.nft11
-rw-r--r--tests/shell/testcases/sets/dumps/automerge_0.nodump0
-rw-r--r--tests/shell/testcases/sets/dumps/collapse_elem_0.json-nft50
-rw-r--r--tests/shell/testcases/sets/dumps/collapse_elem_0.nft12
-rw-r--r--tests/shell/testcases/sets/dumps/concat_interval_0.json-nft68
-rw-r--r--tests/shell/testcases/sets/dumps/concat_interval_0.nft14
-rw-r--r--tests/shell/testcases/sets/dumps/dynset_missing.json-nft83
-rw-r--r--tests/shell/testcases/sets/dumps/dynset_missing.nft12
-rw-r--r--tests/shell/testcases/sets/dumps/elem_opts_compat_0.nodump0
-rw-r--r--tests/shell/testcases/sets/dumps/errors_0.json-nft11
-rw-r--r--tests/shell/testcases/sets/dumps/errors_0.nft0
-rw-r--r--tests/shell/testcases/sets/dumps/exact_overlap_0.json-nft110
-rw-r--r--tests/shell/testcases/sets/dumps/exact_overlap_0.nft13
-rw-r--r--tests/shell/testcases/sets/dumps/inner_0.json-nft207
-rw-r--r--tests/shell/testcases/sets/dumps/inner_0.nft18
-rw-r--r--tests/shell/testcases/sets/dumps/meter_0.json-nft203
-rw-r--r--tests/shell/testcases/sets/dumps/meter_0.nft29
-rw-r--r--tests/shell/testcases/sets/dumps/reset_command_0.nodump0
-rw-r--r--tests/shell/testcases/sets/dumps/set_eval_0.json-nft85
-rw-r--r--tests/shell/testcases/sets/dumps/set_eval_0.nft11
-rw-r--r--tests/shell/testcases/sets/dumps/sets_with_ifnames.json-nft551
-rw-r--r--tests/shell/testcases/sets/dumps/sets_with_ifnames.nft62
-rw-r--r--tests/shell/testcases/sets/dumps/type_set_symbol.json-nft114
-rw-r--r--tests/shell/testcases/sets/dumps/type_set_symbol.nft16
-rw-r--r--tests/shell/testcases/sets/dumps/typeof_raw_0.nft12
-rw-r--r--tests/shell/testcases/sets/dumps/typeof_sets_0.nft82
-rw-r--r--tests/shell/testcases/sets/dumps/typeof_sets_1.nft15
-rw-r--r--tests/shell/testcases/sets/dumps/typeof_sets_concat.nft23
-rwxr-xr-xtests/shell/testcases/sets/dynset_missing32
-rwxr-xr-xtests/shell/testcases/sets/elem_opts_compat_031
-rwxr-xr-xtests/shell/testcases/sets/errors_069
-rwxr-xr-xtests/shell/testcases/sets/exact_overlap_022
-rwxr-xr-xtests/shell/testcases/sets/inner_027
-rwxr-xr-xtests/shell/testcases/sets/meter_018
-rwxr-xr-xtests/shell/testcases/sets/reset_command_087
-rwxr-xr-xtests/shell/testcases/sets/set_eval_017
-rwxr-xr-xtests/shell/testcases/sets/sets_with_ifnames152
-rwxr-xr-xtests/shell/testcases/sets/type_set_symbol6
-rwxr-xr-xtests/shell/testcases/sets/typeof_raw_017
-rwxr-xr-xtests/shell/testcases/sets/typeof_sets_0219
-rwxr-xr-xtests/shell/testcases/sets/typeof_sets_122
-rwxr-xr-xtests/shell/testcases/sets/typeof_sets_concat8
-rwxr-xr-xtests/shell/testcases/transactions/0049huge_030
-rwxr-xr-xtests/shell/testcases/transactions/0051map_0122
-rwxr-xr-xtests/shell/testcases/transactions/30s-stress683
-rwxr-xr-xtests/shell/testcases/transactions/anon_chain_loop19
-rwxr-xr-xtests/shell/testcases/transactions/bad_expression38
-rwxr-xr-xtests/shell/testcases/transactions/concat_range_abort28
-rwxr-xr-xtests/shell/testcases/transactions/doubled-set22
-rw-r--r--tests/shell/testcases/transactions/dumps/0001table_0.json-nft25
-rw-r--r--tests/shell/testcases/transactions/dumps/0002table_0.json-nft31
-rw-r--r--tests/shell/testcases/transactions/dumps/0003table_0.json-nft25
-rw-r--r--tests/shell/testcases/transactions/dumps/0003table_0.nft4
-rw-r--r--tests/shell/testcases/transactions/dumps/0010chain_0.json-nft26
-rw-r--r--tests/shell/testcases/transactions/dumps/0011chain_0.json-nft30
-rw-r--r--tests/shell/testcases/transactions/dumps/0012chain_0.json-nft30
-rw-r--r--tests/shell/testcases/transactions/dumps/0013chain_0.json-nft30
-rw-r--r--tests/shell/testcases/transactions/dumps/0014chain_1.json-nft11
-rw-r--r--tests/shell/testcases/transactions/dumps/0014chain_1.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/0015chain_0.json-nft11
-rw-r--r--tests/shell/testcases/transactions/dumps/0015chain_0.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/0020rule_0.json-nft11
-rw-r--r--tests/shell/testcases/transactions/dumps/0020rule_0.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/0021rule_0.json-nft54
-rw-r--r--tests/shell/testcases/transactions/dumps/0022rule_1.json-nft11
-rw-r--r--tests/shell/testcases/transactions/dumps/0022rule_1.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/0023rule_1.json-nft11
-rw-r--r--tests/shell/testcases/transactions/dumps/0023rule_1.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/0024rule_0.json-nft82
-rw-r--r--tests/shell/testcases/transactions/dumps/0025rule_0.json-nft52
-rw-r--r--tests/shell/testcases/transactions/dumps/0030set_0.json-nft18
-rw-r--r--tests/shell/testcases/transactions/dumps/0031set_0.json-nft27
-rw-r--r--tests/shell/testcases/transactions/dumps/0032set_0.json-nft27
-rw-r--r--tests/shell/testcases/transactions/dumps/0033set_0.json-nft18
-rw-r--r--tests/shell/testcases/transactions/dumps/0034set_0.json-nft27
-rw-r--r--tests/shell/testcases/transactions/dumps/0035set_0.json-nft30
-rw-r--r--tests/shell/testcases/transactions/dumps/0036set_1.json-nft11
-rw-r--r--tests/shell/testcases/transactions/dumps/0036set_1.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/0037set_0.json-nft30
-rw-r--r--tests/shell/testcases/transactions/dumps/0038set_0.json-nft38
-rw-r--r--tests/shell/testcases/transactions/dumps/0039set_0.json-nft38
-rw-r--r--tests/shell/testcases/transactions/dumps/0040set_0.json-nft84
-rw-r--r--tests/shell/testcases/transactions/dumps/0041nat_restore_0.json-nft30
-rw-r--r--tests/shell/testcases/transactions/dumps/0041nat_restore_0.nft5
-rw-r--r--tests/shell/testcases/transactions/dumps/0042_stateful_expr_0.json-nft28
-rw-r--r--tests/shell/testcases/transactions/dumps/0042_stateful_expr_0.nft5
-rw-r--r--tests/shell/testcases/transactions/dumps/0043set_1.json-nft11
-rw-r--r--tests/shell/testcases/transactions/dumps/0043set_1.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/0044rule_0.json-nft11
-rw-r--r--tests/shell/testcases/transactions/dumps/0044rule_0.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/0045anon-unbind_0.json-nft11
-rw-r--r--tests/shell/testcases/transactions/dumps/0045anon-unbind_0.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/0046set_0.json-nft18
-rw-r--r--tests/shell/testcases/transactions/dumps/0046set_0.nft2
-rw-r--r--tests/shell/testcases/transactions/dumps/0047set_0.json-nft73
-rw-r--r--tests/shell/testcases/transactions/dumps/0047set_0.nft11
-rw-r--r--tests/shell/testcases/transactions/dumps/0048helpers_0.json-nft18
-rw-r--r--tests/shell/testcases/transactions/dumps/0048helpers_0.nft2
-rw-r--r--tests/shell/testcases/transactions/dumps/0049huge_0.json-nft5121
-rw-r--r--tests/shell/testcases/transactions/dumps/0049huge_0.nft749
-rw-r--r--tests/shell/testcases/transactions/dumps/0050rule_1.json-nft11
-rw-r--r--tests/shell/testcases/transactions/dumps/0050rule_1.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/0051map_0.nodump0
-rw-r--r--tests/shell/testcases/transactions/dumps/30s-stress.json-nft11
-rw-r--r--tests/shell/testcases/transactions/dumps/30s-stress.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/anon_chain_loop.json-nft11
-rw-r--r--tests/shell/testcases/transactions/dumps/anon_chain_loop.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/bad_expression.json-nft11
-rw-r--r--tests/shell/testcases/transactions/dumps/bad_expression.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/concat_range_abort.json-nft47
-rw-r--r--tests/shell/testcases/transactions/dumps/concat_range_abort.nft8
-rw-r--r--tests/shell/testcases/transactions/dumps/doubled-set.json-nft41
-rw-r--r--tests/shell/testcases/transactions/dumps/doubled-set.nft7
-rw-r--r--tests/shell/testcases/transactions/dumps/table_onoff.json-nft59
-rw-r--r--tests/shell/testcases/transactions/dumps/table_onoff.nft8
-rwxr-xr-xtests/shell/testcases/transactions/table_onoff44
-rwxr-xr-xtools/check-tree.sh134
1239 files changed, 79758 insertions, 6100 deletions
diff --git a/.gitignore b/.gitignore
index 6b37b123..a62e31f3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
# Generated by autoconf/configure/automake
*.m4
+.dirstamp
Makefile
Makefile.in
stamp-h1
@@ -14,8 +15,12 @@ build-aux/
libnftables.pc
libtool
+# cscope files
+/cscope.*
+
# Generated by tests
*.payload.got
+tests/build/tests.log
# Debian package build temporary files
build-stamp
diff --git a/COPYING b/COPYING
index bf7f06eb..a0dd81b9 100644
--- a/COPYING
+++ b/COPYING
@@ -1,8 +1,8 @@
+Original author of nftables distributed the code under the terms of the
+GPL version 2 *only*. New code though is moving to GPL version 2 or any
+later which is the preferred license for this project these days.
-nftables is distributed under the terms of the GPL version 2. Note that
-*only* version 2 of the GPL applies, not "any later version".
-
-Patrick McHardy <kaber@trash.net>
+Pablo Neira Ayuso <pablo@netfilter.org>
-------------------------------------------------------------------------------
diff --git a/INSTALL b/INSTALL
index a3f10c37..5d45ec98 100644
--- a/INSTALL
+++ b/INSTALL
@@ -4,7 +4,7 @@ Installation instructions for nftables
Prerequisites
=============
- - standard glibc headers, gcc etc.
+ - build tooling: glibc headers, gcc, autotools, automake, libtool, pkg-config.
- libmnl: git://git.netfilter.org/libmnl.git
@@ -14,17 +14,15 @@ Installation instructions for nftables
- bison
- - libgmp
+ - libgmp: alternatively, see mini-gmp support below.
- - libreadline
-
- - pkg-config
-
- - libtool
+ - libreadline or libedit or linenoise: required by interactive command line
- optional: libxtables: required to interact with iptables-compat
- - optional: docbook2x: required for building man-page
+ - optional: libjansson: required to build JSON support
+
+ - optional: asciidoc: required for building man-page
Configuring and compiling
=========================
@@ -60,17 +58,50 @@ Installation instructions for nftables
For libxtables support to interact with the iptables-compat
utility.
- Suggested configuration options: --prefix=/ --datarootdir=/usr/share
+ --without-cli
+
+ To disable interactive command line support, ie. -i/--interactive.
+
+ --with-cli=readline
+
+ To enable interactive command line support with libreadline.
+
+ --with-cli=linenoise
+
+ To enable interactive command line support with linenoise.
+
+ --with-cli=editline
+
+ To enable interactive command line support with libedit.
+
+ --with-json
+
+ To enable JSON support, this requires libjansson.
Run "make" to compile nftables, "make install" to install it in the
configured paths.
- Other notes
+ Python support
+ ==============
+
+ CPython bindings are available for nftables under the py/ folder. They can be
+ installed using pip:
+
+ python -m pip install py/
+
+ A legacy setup.py script can also be used:
+
+ ( cd py && python setup.py install )
+
+ However, this method is deprecated.
+
+ Source code
===========
- The nftables kernel tree can be found at:
+ Netfilter's Linux kernel tree can be found at:
- git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nftables.git
+ git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf.git/
+ https://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf.git
The latest version of this code can be found at:
diff --git a/Make_global.am b/Make_global.am
deleted file mode 100644
index 5bb541f6..00000000
--- a/Make_global.am
+++ /dev/null
@@ -1,21 +0,0 @@
-# This is _NOT_ the library release version, it's an API version.
-# Extracted from Chapter 6 "Library interface versions" of the libtool docs.
-#
-# <snippet>
-# Here are a set of rules to help you update your library version information:
-#
-# 1. Start with version information of `0:0:0' for each libtool library.
-# 2. Update the version information only immediately before a public release
-# of your software. More frequent updates are unnecessary, and only guarantee
-# that the current interface number gets larger faster.
-# 3. If the library source code has changed at all since the last update,
-# then increment revision (`c:r:a' becomes `c:r+1:a').
-# 4. If any interfaces have been added, removed, or changed since the last
-# update, increment current, and set revision to 0.
-# 5. If any interfaces have been added since the last public release, then
-# increment age.
-# 6. If any interfaces have been removed since the last public release, then
-# set age to 0.
-# </snippet>
-#
-libnftables_LIBVERSION=2:0:1
diff --git a/Makefile.am b/Makefile.am
index 4a17424d..fef1d8d1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,15 +1,411 @@
-ACLOCAL_AMFLAGS = -I m4
+# This is _NOT_ the library release version, it's an API version.
+# Extracted from Chapter 6 "Library interface versions" of the libtool docs.
+#
+# <snippet>
+# Here are a set of rules to help you update your library version information:
+#
+# 1. Start with version information of `0:0:0' for each libtool library.
+# 2. Update the version information only immediately before a public release
+# of your software. More frequent updates are unnecessary, and only guarantee
+# that the current interface number gets larger faster.
+# 3. If the library source code has changed at all since the last update,
+# then increment revision (`c:r:a' becomes `c:r+1:a').
+# 4. If any interfaces have been added, removed, or changed since the last
+# update, increment current, and set revision to 0.
+# 5. If any interfaces have been added since the last public release, then
+# increment age.
+# 6. If any interfaces have been removed since the last public release, then
+# set age to 0.
+# </snippet>
+#
+libnftables_LIBVERSION = 2:0:1
-SUBDIRS = src \
- include \
- files \
- doc
-if HAVE_PYTHON
-SUBDIRS += py
+###############################################################################
+
+ACLOCAL_AMFLAGS = -I m4
+
+EXTRA_DIST =
+BUILT_SOURCES =
+LDADD =
+lib_LTLIBRARIES =
+noinst_LTLIBRARIES =
+sbin_PROGRAMS =
+check_PROGRAMS =
+dist_man_MANS =
+CLEANFILES =
+
+###############################################################################
+
+pkginclude_HEADERS = \
+ include/nftables/libnftables.h \
+ $(NULL)
+
+noinst_HEADERS = \
+ \
+ include/linux/netfilter.h \
+ include/linux/netfilter/nf_conntrack_common.h \
+ include/linux/netfilter/nf_conntrack_tuple_common.h \
+ include/linux/netfilter/nf_log.h \
+ include/linux/netfilter/nf_nat.h \
+ include/linux/netfilter/nf_synproxy.h \
+ include/linux/netfilter/nf_tables.h \
+ include/linux/netfilter/nf_tables_compat.h \
+ include/linux/netfilter/nfnetlink.h \
+ include/linux/netfilter/nfnetlink_hook.h \
+ include/linux/netfilter/nfnetlink_osf.h \
+ include/linux/netfilter_arp.h \
+ include/linux/netfilter_arp/arp_tables.h \
+ include/linux/netfilter_bridge.h \
+ include/linux/netfilter_bridge/ebtables.h \
+ include/linux/netfilter_decnet.h \
+ include/linux/netfilter_ipv4.h \
+ include/linux/netfilter_ipv4/ip_tables.h \
+ include/linux/netfilter_ipv6.h \
+ include/linux/netfilter_ipv6/ip6_tables.h \
+ \
+ include/cache.h \
+ include/cli.h \
+ include/cmd.h \
+ include/ct.h \
+ include/datatype.h \
+ include/dccpopt.h \
+ include/erec.h \
+ include/expression.h \
+ include/exthdr.h \
+ include/fib.h \
+ include/gmputil.h \
+ include/hash.h \
+ include/headers.h \
+ include/iface.h \
+ include/intervals.h \
+ include/ipopt.h \
+ include/json.h \
+ include/list.h \
+ include/meta.h \
+ include/mini-gmp.h \
+ include/misspell.h \
+ include/mnl.h \
+ include/netlink.h \
+ include/nft.h \
+ include/nftables.h \
+ include/numgen.h \
+ include/osf.h \
+ include/owner.h \
+ include/parser.h \
+ include/payload.h \
+ include/proto.h \
+ include/rt.h \
+ include/rule.h \
+ include/sctp_chunk.h \
+ include/socket.h \
+ include/statement.h \
+ include/tcpopt.h \
+ include/utils.h \
+ include/xfrm.h \
+ include/xt.h \
+ \
+ $(NULL)
+
+###############################################################################
+
+AM_CPPFLAGS = \
+ "-I$(srcdir)/include" \
+ "-DDEFAULT_INCLUDE_PATH=\"${sysconfdir}\"" \
+ $(LIBMNL_CFLAGS) \
+ $(LIBNFTNL_CFLAGS) \
+ $(NULL)
+
+if BUILD_DEBUG
+AM_CPPFLAGS += -g -DDEBUG
+endif
+if BUILD_XTABLES
+AM_CPPFLAGS += $(XTABLES_CFLAGS)
+endif
+if BUILD_MINIGMP
+AM_CPPFLAGS += -DHAVE_MINIGMP
+endif
+if BUILD_JSON
+AM_CPPFLAGS += -DHAVE_JSON
+endif
+if BUILD_XTABLES
+AM_CPPFLAGS += -DHAVE_XTABLES
+endif
+
+AM_CFLAGS = \
+ -Wall \
+ \
+ -Waggregate-return \
+ -Wbad-function-cast \
+ -Wcast-align \
+ -Wdeclaration-after-statement \
+ -Wformat-nonliteral \
+ -Wformat-security \
+ -Winit-self \
+ -Wmissing-declarations \
+ -Wmissing-format-attribute \
+ -Wmissing-prototypes \
+ -Wsign-compare \
+ -Wstrict-prototypes \
+ -Wundef \
+ -Wunused \
+ -Wwrite-strings \
+ \
+ $(GCC_FVISIBILITY_HIDDEN) \
+ \
+ $(NULL)
+
+AM_YFLAGS = -d -Wno-yacc
+
+###############################################################################
+
+BUILT_SOURCES += src/parser_bison.h
+
+# yacc and lex generate dirty code
+noinst_LTLIBRARIES += src/libparser.la
+
+src_libparser_la_SOURCES = \
+ src/parser_bison.y \
+ src/scanner.l \
+ $(NULL)
+
+src_libparser_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ -Wno-missing-declarations \
+ -Wno-missing-prototypes \
+ -Wno-nested-externs \
+ -Wno-redundant-decls \
+ -Wno-undef \
+ -Wno-unused-but-set-variable \
+ $(NULL)
+
+###############################################################################
+
+if BUILD_MINIGMP
+
+noinst_LTLIBRARIES += src/libminigmp.la
+
+src_libminigmp_la_SOURCES = src/mini-gmp.c
+
+src_libminigmp_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ -Wno-sign-compare \
+ $(NULL)
+
+endif
+
+###############################################################################
+
+lib_LTLIBRARIES += src/libnftables.la
+
+src_libnftables_la_SOURCES = \
+ src/libnftables.map \
+ \
+ src/cache.c \
+ src/cmd.c \
+ src/ct.c \
+ src/datatype.c \
+ src/dccpopt.c \
+ src/erec.c \
+ src/evaluate.c \
+ src/expression.c \
+ src/exthdr.c \
+ src/fib.c \
+ src/gmputil.c \
+ src/hash.c \
+ src/iface.c \
+ src/intervals.c \
+ src/ipopt.c \
+ src/libnftables.c \
+ src/mergesort.c \
+ src/meta.c \
+ src/misspell.c \
+ src/mnl.c \
+ src/monitor.c \
+ src/netlink.c \
+ src/netlink_delinearize.c \
+ src/netlink_linearize.c \
+ src/nfnl_osf.c \
+ src/nftutils.c \
+ src/nftutils.h \
+ src/numgen.c \
+ src/optimize.c \
+ src/osf.c \
+ src/owner.c \
+ src/payload.c \
+ src/print.c \
+ src/proto.c \
+ src/rt.c \
+ src/rule.c \
+ src/sctp_chunk.c \
+ src/segtree.c \
+ src/socket.c \
+ src/statement.c \
+ src/tcpopt.c \
+ src/utils.c \
+ src/xfrm.c \
+ $(NULL)
+
+src_libnftables_la_SOURCES += src/xt.c
+
+if BUILD_JSON
+src_libnftables_la_SOURCES += \
+ src/json.c \
+ src/parser_json.c \
+ $(NULL)
+endif
+
+src_libnftables_la_LDFLAGS = \
+ -version-info "${libnftables_LIBVERSION}" \
+ -Wl,--version-script="$(srcdir)/src//libnftables.map" \
+ $(NULL)
+
+src_libnftables_la_LIBADD = \
+ $(LIBMNL_LIBS) \
+ $(LIBNFTNL_LIBS) \
+ src/libparser.la \
+ $(NULL)
+
+if BUILD_MINIGMP
+src_libnftables_la_LIBADD += src/libminigmp.la
+endif
+
+if BUILD_XTABLES
+src_libnftables_la_LIBADD += $(XTABLES_LIBS)
endif
-EXTRA_DIST = tests \
- files
+if BUILD_JSON
+src_libnftables_la_LIBADD += $(JANSSON_LIBS)
+endif
+
+###############################################################################
+
+sbin_PROGRAMS += src/nft
+
+src_nft_SOURCES = src/main.c
+
+if BUILD_CLI
+src_nft_SOURCES += src/cli.c
+endif
+
+src_nft_LDADD = src/libnftables.la
+
+###############################################################################
+
+check_PROGRAMS += examples/nft-buffer
+
+examples_nft_buffer_AM_CPPFLAGS = -I$(srcdir)/include
+examples_nft_buffer_LDADD = src/libnftables.la
+
+check_PROGRAMS += examples/nft-json-file
+
+examples_nft_json_file_AM_CPPFLAGS = -I$(srcdir)/include
+examples_nft_json_file_LDADD = src/libnftables.la
+
+###############################################################################
+
+if BUILD_MAN
+
+dist_man_MANS += \
+ doc/nft.8 \
+ doc/libnftables-json.5 \
+ doc/libnftables.3 \
+ $(NULL)
+
+A2X_OPTS_MANPAGE = \
+ -L \
+ --doctype manpage \
+ --format manpage \
+ -D "${builddir}/doc" \
+ $(NULL)
+
+ASCIIDOC_MAIN = doc/nft.txt
+
+ASCIIDOC_INCLUDES = \
+ doc/data-types.txt \
+ doc/payload-expression.txt \
+ doc/primary-expression.txt \
+ doc/stateful-objects.txt \
+ doc/statements.txt \
+ $(NULL)
+
+ASCIIDOCS = \
+ $(ASCIIDOC_MAIN) \
+ $(ASCIIDOC_INCLUDES) \
+ $(NULL)
+
+EXTRA_DIST += \
+ $(ASCIIDOCS) \
+ doc/libnftables-json.adoc \
+ doc/libnftables.adoc \
+ $(NULL)
+
+CLEANFILES += doc/*~
+
+doc/nft.8: $(ASCIIDOCS)
+ mkdir -p ${builddir}/doc
+ $(AM_V_GEN)$(A2X) $(A2X_OPTS_MANPAGE) $<
+
+.adoc.3:
+ $(AM_V_GEN)$(A2X) $(A2X_OPTS_MANPAGE) $<
+
+.adoc.5:
+ $(AM_V_GEN)$(A2X) $(A2X_OPTS_MANPAGE) $<
+
+MAINTAINERCLEANFILES = ${dist_man_MANS}
+
+endif
+
+###############################################################################
+
+dist_pkgdata_DATA = \
+ files/nftables/all-in-one.nft \
+ files/nftables/arp-filter.nft \
+ files/nftables/bridge-filter.nft \
+ files/nftables/inet-filter.nft \
+ files/nftables/inet-nat.nft \
+ files/nftables/ipv4-filter.nft \
+ files/nftables/ipv4-mangle.nft \
+ files/nftables/ipv4-nat.nft \
+ files/nftables/ipv4-raw.nft \
+ files/nftables/ipv6-filter.nft \
+ files/nftables/ipv6-mangle.nft \
+ files/nftables/ipv6-nat.nft \
+ files/nftables/ipv6-raw.nft \
+ files/nftables/netdev-ingress.nft \
+ $(NULL)
+
+pkgdocdir = ${docdir}/examples
+
+dist_pkgdoc_SCRIPTS = \
+ files/examples/ct_helpers.nft \
+ files/examples/load_balancing.nft \
+ files/examples/secmark.nft \
+ files/examples/sets_and_maps.nft \
+ $(NULL)
+
+pkgsysconfdir = ${sysconfdir}/nftables/osf
+
+dist_pkgsysconf_DATA = \
+ files/osf/pf.os \
+ $(NULL)
+
+###############################################################################
+
+EXTRA_DIST += \
+ py/pyproject.toml \
+ py/setup.cfg \
+ py/setup.py \
+ py/src/__init__.py \
+ py/src/nftables.py \
+ py/src/schema.json \
+ $(NULL)
+
+###############################################################################
+
+EXTRA_DIST += \
+ files \
+ tests \
+ $(NULL)
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libnftables.pc
diff --git a/configure.ac b/configure.ac
index 96cb91c7..724a4ae7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,15 +1,15 @@
-AC_INIT([nftables], [1.0.0], [netfilter-devel@vger.kernel.org])
-AC_DEFINE([RELEASE_NAME], ["Fearless Fosdick #2"], [Release name])
+AC_INIT([nftables], [1.0.9], [netfilter-devel@vger.kernel.org])
+AC_DEFINE([RELEASE_NAME], ["Old Doc Yak #3"], [Release name])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([-Wall foreign subdir-objects
- tar-pax no-dist-gzip dist-bzip2 1.6])
+ tar-pax no-dist-gzip dist-xz 1.6])
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
-AC_CONFIG_HEADER([config.h])
+AC_CONFIG_HEADERS([config.h])
AC_ARG_ENABLE([debug],
AS_HELP_STRING([--disable-debug], [Disable debugging symbols]),
@@ -23,10 +23,13 @@ AM_CONDITIONAL([BUILD_MAN], [test "x$enable_man_doc" = "xyes" ])
# Checks for programs.
AC_PROG_CC
+
+AC_USE_SYSTEM_EXTENSIONS
+
AC_PROG_MKDIR_P
AC_PROG_INSTALL
AC_PROG_SED
-AM_PROG_LEX
+AC_PROG_LEX([noyywrap])
AC_PROG_YACC
if test -z "$ac_cv_prog_YACC" -a ! -f "${srcdir}/src/parser_bison.c"
@@ -43,11 +46,8 @@ then
fi
AM_PROG_AR
-AM_PROG_LIBTOOL
-LT_INIT
-AM_PROG_CC_C_O
+LT_INIT([disable-static])
AC_EXEEXT
-AC_DISABLE_STATIC
CHECK_GCC_FVISIBILITY
AS_IF([test "x$enable_man_doc" = "xyes"], [
@@ -57,7 +57,7 @@ AS_IF([test "x$enable_man_doc" = "xyes"], [
])
PKG_CHECK_MODULES([LIBMNL], [libmnl >= 1.0.4])
-PKG_CHECK_MODULES([LIBNFTNL], [libnftnl >= 1.2.0])
+PKG_CHECK_MODULES([LIBNFTNL], [libnftnl >= 1.2.6])
AC_ARG_WITH([mini-gmp], [AS_HELP_STRING([--with-mini-gmp],
[Use builtin mini-gmp (for embedded builds)])],
@@ -110,43 +110,13 @@ AC_DEFINE([HAVE_LIBJANSSON], [1], [Define if you have libjansson])
])
AM_CONDITIONAL([BUILD_JSON], [test "x$with_json" != xno])
-AC_ARG_ENABLE(python,
- AS_HELP_STRING([--enable-python], [Enable python]),,[enable_python=check]
- )
-
-AC_ARG_WITH([python_bin],
- [AS_HELP_STRING([--with-python-bin], [Specify Python binary to use])],
- [PYTHON_BIN="$withval"], [AC_PATH_PROGS(PYTHON_BIN, python python2 python2.7 python3)]
- )
-
-AS_IF([test "x$PYTHON_BIN" = "x"], [
- AS_IF([test "x$enable_python" = "xyes"], [AC_MSG_ERROR([Python asked but not found])],
- [test "x$enable_python" = "xcheck"], [
- AC_MSG_WARN([Python not found, continuing anyway])
- enable_python=no
- ])
-])
-
-AM_CONDITIONAL([HAVE_PYTHON], [test "$enable_python" != "no"])
+AC_CHECK_DECLS([getprotobyname_r, getprotobynumber_r, getservbyport_r], [], [], [[
+#include <netdb.h>
+]])
AC_CONFIG_FILES([ \
Makefile \
libnftables.pc \
- src/Makefile \
- include/Makefile \
- include/nftables/Makefile \
- include/linux/Makefile \
- include/linux/netfilter/Makefile \
- include/linux/netfilter_arp/Makefile \
- include/linux/netfilter_bridge/Makefile \
- include/linux/netfilter_ipv4/Makefile \
- include/linux/netfilter_ipv6/Makefile \
- files/Makefile \
- files/examples/Makefile \
- files/nftables/Makefile \
- files/osf/Makefile \
- doc/Makefile \
- py/Makefile \
])
AC_OUTPUT
@@ -158,10 +128,3 @@ nft configuration:
enable man page: ${enable_man_doc}
libxtables support: ${with_xtables}
json output support: ${with_json}"
-
-AS_IF([test "$enable_python" != "no"], [
- echo " enable Python: yes (with $PYTHON_BIN)"
- ], [
- echo " enable Python: no"
- ]
- )
diff --git a/doc/Makefile.am b/doc/Makefile.am
deleted file mode 100644
index 21482320..00000000
--- a/doc/Makefile.am
+++ /dev/null
@@ -1,30 +0,0 @@
-if BUILD_MAN
-man_MANS = nft.8 libnftables-json.5 libnftables.3
-
-A2X_OPTS_MANPAGE = -L --doctype manpage --format manpage -D ${builddir}
-
-ASCIIDOC_MAIN = nft.txt
-ASCIIDOC_INCLUDES = \
- data-types.txt \
- payload-expression.txt \
- primary-expression.txt \
- stateful-objects.txt \
- statements.txt
-ASCIIDOCS = ${ASCIIDOC_MAIN} ${ASCIIDOC_INCLUDES}
-
-EXTRA_DIST = ${ASCIIDOCS} ${man_MANS} libnftables-json.adoc libnftables.adoc
-
-CLEANFILES = \
- *~
-
-nft.8: ${ASCIIDOCS}
- ${AM_V_GEN}${A2X} ${A2X_OPTS_MANPAGE} $<
-
-.adoc.3:
- ${AM_V_GEN}${A2X} ${A2X_OPTS_MANPAGE} $<
-
-.adoc.5:
- ${AM_V_GEN}${A2X} ${A2X_OPTS_MANPAGE} $<
-
-CLEANFILES += ${man_MANS}
-endif
diff --git a/doc/data-types.txt b/doc/data-types.txt
index 961fc624..6c0e2f94 100644
--- a/doc/data-types.txt
+++ b/doc/data-types.txt
@@ -242,35 +242,13 @@ 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"]
|==================
|Name | Keyword | Size | Base type
|ICMPv6 Type |
-icmpx_code |
+icmpv6_type |
8 bit |
integer
|===================
@@ -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 |
-icmpv6_type |
-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/libnftables-json.adoc b/doc/libnftables-json.adoc
index 9cc17ff2..e3b24cc4 100644
--- a/doc/libnftables-json.adoc
+++ b/doc/libnftables-json.adoc
@@ -175,7 +175,7 @@ kind, optionally filtered by *family* and for some, also *table*.
____
*{ "reset":* 'RESET_OBJECT' *}*
-'RESET_OBJECT' := 'COUNTER' | 'COUNTERS' | 'QUOTA' | 'QUOTAS'
+'RESET_OBJECT' := 'COUNTER' | 'COUNTERS' | 'QUOTA' | 'QUOTAS' | 'RULE' | 'RULES' | 'SET' | 'MAP' | 'ELEMENT'
____
Reset state in suitable objects, i.e. zero their internal counter.
@@ -312,7 +312,8 @@ ____
"elem":* 'SET_ELEMENTS'*,
"timeout":* 'NUMBER'*,
"gc-interval":* 'NUMBER'*,
- "size":* 'NUMBER'
+ "size":* 'NUMBER'*,
+ "auto-merge":* 'BOOLEAN'
*}}*
*{ "map": {
@@ -327,7 +328,8 @@ ____
"elem":* 'SET_ELEMENTS'*,
"timeout":* 'NUMBER'*,
"gc-interval":* 'NUMBER'*,
- "size":* 'NUMBER'
+ "size":* 'NUMBER'*,
+ "auto-merge":* 'BOOLEAN'
*}}*
'SET_TYPE' := 'STRING' | *[* 'SET_TYPE_LIST' *]*
@@ -366,6 +368,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
@@ -682,11 +686,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
@@ -1059,10 +1058,22 @@ Assign connection tracking expectation.
=== XT
[verse]
-*{ "xt": null }*
+____
+*{ "xt": {
+ "type":* 'TYPENAME'*,
+ "name":* 'STRING'
+*}}*
+
+'TYPENAME' := *match* | *target* | *watcher*
+____
+
+This represents an xt statement from xtables compat interface. It is a
+fallback if translation is not available or not complete.
+
+Seeing this means the ruleset (or parts of it) were created by *iptables-nft*
+and one should use that to manage it.
-This represents an xt statement from xtables compat interface. Sadly, at this
-point, it is not possible to provide any further information about its content.
+*BEWARE:* nftables won't restore these statements.
== EXPRESSIONS
Expressions are the building blocks of (most) statements. In their most basic
@@ -1172,7 +1183,7 @@ point (*base*). The following *base* values are accepted:
*"th"*::
The offset is relative to Transport Layer header start offset.
-The second form allows to reference a field by name (*field*) in a named packet
+The second form allows one to reference a field by name (*field*) in a named packet
header (*protocol*).
=== EXTHDR
@@ -1214,6 +1225,17 @@ If the *field* property is not given, the expression is to be used as an SCTP
chunk existence check in a *match* statement with a boolean on the right hand
side.
+=== DCCP OPTION
+[verse]
+*{ "dccp option": {
+ "type":* 'NUMBER'*
+*}}*
+
+Create a reference to a DCCP option (*type*).
+
+The expression is to be used as a DCCP option existence check in a *match*
+statement with a boolean on the right hand side.
+
=== META
[verse]
____
@@ -1321,15 +1343,17 @@ Perform kernel Forwarding Information Base lookups.
=== BINARY OPERATION
[verse]
-*{ "|": [* 'EXPRESSION'*,* 'EXPRESSION' *] }*
-*{ "^": [* 'EXPRESSION'*,* 'EXPRESSION' *] }*
-*{ "&": [* 'EXPRESSION'*,* 'EXPRESSION' *] }*
-*{ "+<<+": [* 'EXPRESSION'*,* 'EXPRESSION' *] }*
-*{ ">>": [* 'EXPRESSION'*,* 'EXPRESSION' *] }*
+*{ "|": [* 'EXPRESSION'*,* 'EXPRESSIONS' *] }*
+*{ "^": [* 'EXPRESSION'*,* 'EXPRESSIONS' *] }*
+*{ "&": [* 'EXPRESSION'*,* 'EXPRESSIONS' *] }*
+*{ "+<<+": [* 'EXPRESSION'*,* 'EXPRESSIONS' *] }*
+*{ ">>": [* 'EXPRESSION'*,* 'EXPRESSIONS' *] }*
+'EXPRESSIONS' := 'EXPRESSION' | 'EXPRESSION'*,* 'EXPRESSIONS'
-All binary operations expect an array of exactly two expressions, of which the
+All binary operations expect an array of at least two expressions, of which the
first element denotes the left hand side and the second one the right hand
-side.
+side. Extra elements are accepted in the given array and appended to the term
+accordingly.
=== VERDICT
[verse]
diff --git a/doc/libnftables.adoc b/doc/libnftables.adoc
index 3abb9595..2cf78d7a 100644
--- a/doc/libnftables.adoc
+++ b/doc/libnftables.adoc
@@ -18,6 +18,9 @@ void nft_ctx_free(struct nft_ctx* '\*ctx'*);
bool nft_ctx_get_dry_run(struct nft_ctx* '\*ctx'*);
void nft_ctx_set_dry_run(struct nft_ctx* '\*ctx'*, bool* 'dry'*);
+unsigned int nft_ctx_input_get_flags(struct nft_ctx* '\*ctx'*);
+unsigned int nft_ctx_input_set_flags(struct nft_ctx* '\*ctx'*, unsigned int* 'flags'*);
+
unsigned int nft_ctx_output_get_flags(struct nft_ctx* '\*ctx'*);
void nft_ctx_output_set_flags(struct nft_ctx* '\*ctx'*, unsigned int* 'flags'*);
@@ -37,6 +40,9 @@ const char *nft_ctx_get_error_buffer(struct nft_ctx* '\*ctx'*);
int nft_ctx_add_include_path(struct nft_ctx* '\*ctx'*, const char* '\*path'*);
void nft_ctx_clear_include_paths(struct nft_ctx* '\*ctx'*);
+int nft_ctx_add_var(struct nft_ctx* '\*ctx'*, const char* '\*var'*);
+void nft_ctx_clear_vars(struct nft_ctx '\*ctx'*);
+
int nft_run_cmd_from_buffer(struct nft_ctx* '\*nft'*, const char* '\*buf'*);
int nft_run_cmd_from_filename(struct nft_ctx* '\*nft'*,
const char* '\*filename'*);*
@@ -68,13 +74,37 @@ The *nft_ctx_free*() function frees the context object pointed to by 'ctx', incl
=== nft_ctx_get_dry_run() and nft_ctx_set_dry_run()
Dry-run setting controls whether ruleset changes are actually committed on kernel side or not.
-It allows to check whether a given operation would succeed without making actual changes to the ruleset.
+It allows one to check whether a given operation would succeed without making actual changes to the ruleset.
The default setting is *false*.
The *nft_ctx_get_dry_run*() function returns the dry-run setting's value contained in 'ctx'.
The *nft_ctx_set_dry_run*() function sets the dry-run setting in 'ctx' to the value of 'dry'.
+=== nft_ctx_input_get_flags() and nft_ctx_input_set_flags()
+The flags setting controls the input format.
+
+----
+enum {
+ NFT_CTX_INPUT_NO_DNS = (1 << 0),
+ NFT_CTX_INPUT_JSON = (1 << 1),
+};
+----
+
+NFT_CTX_INPUT_NO_DNS::
+ Avoid resolving IP addresses with blocking getaddrinfo(). In that case,
+ only plain IP addresses are accepted.
+
+NFT_CTX_INPUT_JSON:
+ When parsing the input, first try to interpret the input as JSON before
+ falling back to the nftables format. This behavior is implied when setting
+ the NFT_CTX_OUTPUT_JSON flag.
+
+The *nft_ctx_input_get_flags*() function returns the input flags setting's value in 'ctx'.
+
+The *nft_ctx_input_set_flags*() function sets the input flags setting in 'ctx' to the value of 'val'
+and returns the previous flags.
+
=== nft_ctx_output_get_flags() and nft_ctx_output_set_flags()
The flags setting controls the output format.
@@ -115,10 +145,11 @@ NFT_CTX_OUTPUT_HANDLE::
NFT_CTX_OUTPUT_JSON::
If enabled at compile-time, libnftables accepts input in JSON format and is able to print output in JSON format as well.
See *libnftables-json*(5) for a description of the supported schema.
- This flag controls JSON output format, input is auto-detected.
+ This flag enables JSON output format. If the flag is set, the input will first be tried as JSON format,
+ before falling back to nftables format. This flag implies NFT_CTX_INPUT_JSON.
NFT_CTX_OUTPUT_ECHO::
The echo setting makes libnftables print the changes once they are committed to the kernel, just like a running instance of *nft monitor* would.
- Amongst other things, this allows to retrieve an added rule's handle atomically.
+ Amongst other things, this allows one to retrieve an added rule's handle atomically.
NFT_CTX_OUTPUT_GUID::
Display UID and GID as described in the /etc/passwd and /etc/group files.
NFT_CTX_OUTPUT_NUMERIC_PROTO::
@@ -196,9 +227,9 @@ On failure, the functions return non-zero which may only happen if buffering was
The *nft_ctx_get_output_buffer*() and *nft_ctx_get_error_buffer*() functions return a pointer to the buffered output (which may be empty).
=== nft_ctx_add_include_path() and nft_ctx_clear_include_path()
-The *include* command in nftables rulesets allows to outsource parts of the ruleset into a different file.
+The *include* command in nftables rulesets allows one to outsource parts of the ruleset into a different file.
The include path defines where these files are searched for.
-Libnftables allows to have a list of those paths which are searched in order.
+Libnftables allows one to have a list of those paths which are searched in order.
The default include path list contains a single compile-time defined entry (typically '/etc/').
The *nft_ctx_add_include_path*() function extends the list of include paths in 'ctx' by the one given in 'path'.
@@ -206,6 +237,14 @@ The function returns zero on success or non-zero if memory allocation failed.
The *nft_ctx_clear_include_paths*() function removes all include paths, even the built-in default one.
+=== nft_ctx_add_var() and nft_ctx_clear_vars()
+The *define* command in nftables ruleset allows one to define variables.
+
+The *nft_ctx_add_var*() function extends the list of variables in 'ctx'. The variable must be given in the format 'key=value'.
+The function returns zero on success or non-zero if the variable is malformed.
+
+The *nft_ctx_clear_vars*() function removes all variables.
+
=== nft_run_cmd_from_buffer() and nft_run_cmd_from_filename()
These functions perform the actual work of parsing user input into nftables commands and executing them.
diff --git a/doc/nft.txt b/doc/nft.txt
index c9bb901b..248b29af 100644
--- a/doc/nft.txt
+++ b/doc/nft.txt
@@ -9,7 +9,7 @@ nft - Administration tool of the nftables framework for packet filtering and cla
SYNOPSIS
--------
[verse]
-*nft* [ *-nNscaeSupyjt* ] [ *-I* 'directory' ] [ *-f* 'filename' | *-i* | 'cmd' ...]
+*nft* [ *-nNscaeSupyjtT* ] [ *-I* 'directory' ] [ *-f* 'filename' | *-i* | 'cmd' ...]
*nft* *-h*
*nft* *-v*
@@ -62,6 +62,11 @@ understanding of their meaning. You can get information about options by running
*--check*::
Check commands validity without actually applying the changes.
+*-o*::
+*--optimize*::
+ Optimize your ruleset. You can combine this option with '-c' to inspect
+ the proposed optimizations.
+
.Ruleset list output formatting that modify the output of the list ruleset command:
*-a*::
@@ -165,17 +170,23 @@ SYMBOLIC VARIABLES
~~~~~~~~~~~~~~~~~~
[verse]
*define* 'variable' *=* 'expr'
+*undefine* 'variable'
+*redefine* 'variable' *=* 'expr'
*$variable*
Symbolic variables can be defined using the *define* statement. Variable
references are expressions and can be used to initialize other variables. The scope
of a definition is the current block and all blocks contained within.
+Symbolic variables can be undefined using the *undefine* statement, and modified
+using the *redefine* statement.
.Using symbolic variables
---------------------------------------
define int_if1 = eth0
define int_if2 = eth1
define int_ifs = { $int_if1, $int_if2 }
+redefine int_if2 = wlan0
+undefine int_if2
filter input iif $int_ifs accept
---------------------------------------
@@ -193,7 +204,7 @@ packet processing paths, which invoke nftables if rules for these hooks exist.
*inet*:: Internet (IPv4/IPv6) address family.
*arp*:: ARP address family, handling IPv4 ARP packets.
*bridge*:: Bridge address family, handling packets which traverse a bridge device.
-*netdev*:: Netdev address family, handling packets from ingress.
+*netdev*:: Netdev address family, handling packets on ingress and egress.
All nftables objects exist in address family specific namespaces, therefore all
identifiers include an address family. If an identifier is specified without an
@@ -251,9 +262,9 @@ The list of supported hooks is identical to IPv4/IPv6/Inet address families abov
NETDEV ADDRESS FAMILY
~~~~~~~~~~~~~~~~~~~~
-The Netdev address family handles packets from the device ingress path. This
-family allows you to filter packets of any ethertype such as ARP, VLAN 802.1q,
-VLAN 802.1ad (Q-in-Q) as well as IPv4 and IPv6 packets.
+The Netdev address family handles packets from the device ingress and egress
+path. This family allows you to filter packets of any ethertype such as ARP,
+VLAN 802.1q, VLAN 802.1ad (Q-in-Q) as well as IPv4 and IPv6 packets.
.Netdev address family hooks
[options="header"]
@@ -263,8 +274,27 @@ VLAN 802.1ad (Q-in-Q) as well as IPv4 and IPv6 packets.
All packets entering the system are processed by this hook. It is invoked after
the network taps (ie. *tcpdump*), right after *tc* ingress and before layer 3
protocol handlers, it can be used for early filtering and policing.
+|egress |
+All packets leaving the system are processed by this hook. It is invoked after
+layer 3 protocol handlers and before *tc* egress. It can be used for late
+filtering and policing.
|=================
+Tunneled packets (such as *vxlan*) are processed by netdev family hooks both
+in decapsulated and encapsulated (tunneled) form. So a packet can be filtered
+on the overlay network as well as on the underlying network.
+
+Note that the order of netfilter and *tc* is mirrored on ingress versus egress.
+This ensures symmetry for NAT and other packet mangling.
+
+Ingress packets which are redirected out some other interface are only
+processed by netfilter on egress if they have passed through netfilter ingress
+processing before. Thus, ingress packets which are redirected by *tc* are not
+subjected to netfilter. But they are if they are redirected by *netfilter* on
+ingress. Conceptually, tc and netfilter can be thought of as layers, with
+netfilter layered above tc: If the packet hasn't been passed up from the
+tc layer to the netfilter layer, it's not subjected to netfilter on egress.
+
RULESET
-------
[verse]
@@ -291,10 +321,11 @@ Effectively, this is the nft-equivalent of *iptables-save* and
TABLES
------
[verse]
-{*add* | *create*} *table* ['family'] 'table' [ {*comment* 'comment' *;*'} *{ flags* 'flags' *; }*]
-{*delete* | *list* | *flush*} *table* ['family'] 'table'
+{*add* | *create*} *table* ['family'] 'table' [*{* [*comment* 'comment' *;*] [*flags* 'flags' *;*] *}*]
+{*delete* | *destroy* | *list* | *flush*} *table* ['family'] 'table'
*list tables* ['family']
*delete table* ['family'] *handle* 'handle'
+*destroy table* ['family'] *handle* 'handle'
Tables are containers for chains, sets and stateful objects. They are identified
by their address family and their name. The address family must be one of *ip*,
@@ -338,16 +369,18 @@ add table inet mytable
[horizontal]
*add*:: Add a new table for the given family with the given name.
*delete*:: Delete the specified table.
+*destroy*:: Delete the specified table, it does not fail if it does not exist.
*list*:: List all chains and rules of the specified table.
*flush*:: Flush all chains and rules of the specified table.
CHAINS
------
[verse]
-{*add* | *create*} *chain* ['family'] 'table' 'chain' [*{ type* 'type' *hook* 'hook' [*device* 'device'] *priority* 'priority' *;* [*policy* 'policy' *;*] [*comment* 'comment' *;*'] *}*]
-{*delete* | *list* | *flush*} *chain* ['family'] 'table' 'chain'
+{*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'
+*destroy chain* ['family'] 'table' *handle* 'handle'
*rename chain* ['family'] 'table' 'chain' 'newname'
Chains are containers for rules. They exist in two kinds, base chains and
@@ -360,6 +393,7 @@ organization.
are specified, the chain is created as a base chain and hooked up to the networking stack.
*create*:: Similar to the *add* command, but returns an error if the chain already exists.
*delete*:: Delete the specified chain. The chain must not contain any rules or be used as jump target.
+*destroy*:: Delete the specified chain, it does not fail if it does not exist. The chain must not contain any rules or be used as jump target.
*rename*:: Rename the specified chain.
*list*:: List all rules of the specified chain.
*flush*:: Flush all rules of the specified chain.
@@ -381,16 +415,17 @@ statements for instance).
|route | ip, ip6 | output |
If a packet has traversed a chain of this type and is about to be accepted, a
new route lookup is performed if relevant parts of the IP header have changed.
-This allows to e.g. implement policy routing selectors in nftables.
+This allows one to e.g. implement policy routing selectors in nftables.
|=================
Apart from the special cases illustrated above (e.g. *nat* type not supporting
*forward* hook or *route* type only supporting *output* hook), there are three
further quirks worth noticing:
-* The netdev family supports merely a single combination, namely *filter* type and
- *ingress* hook. Base chains in this family also require the *device* parameter
- to be present since they exist per incoming interface only.
+* The netdev family supports merely two combinations, namely *filter* type with
+ *ingress* hook and *filter* type with *egress* hook. Base chains in this
+ family also require the *device* parameter to be present since they exist per
+ interface only.
* The arp family supports only the *input* and *output* hooks, both in chains of type
*filter*.
* The inet family also supports the *ingress* hook (since Linux kernel 5.10),
@@ -399,11 +434,19 @@ further quirks worth noticing:
*prerouting*, *input*, *forward*, *output*, *postrouting* and this *ingress*
hook.
+The *device* parameter accepts a network interface name as a string, and is
+required when adding a base chain that filters traffic on the ingress or
+egress hooks. Any ingress or egress chains will only filter traffic from the
+interface specified in the *device* parameter.
+
The *priority* parameter accepts a signed integer value or a standard priority
name which specifies the order in which chains with the same *hook* value are
traversed. The ordering is ascending, i.e. lower priority values have precedence
over higher ones.
+With *nat* type chains, there's a lower excluding limit of -200 for *priority*
+values, because conntrack hooks at this priority and NAT requires it.
+
Standard priority values can be replaced with easily memorizable names. Not all
names make sense in every family with every hook (see the compatibility matrices
below) but their numerical value can still be used for prioritizing chains.
@@ -441,7 +484,7 @@ with these standard names to ease relative prioritizing, e.g. *mangle - 5* stand
for *-155*. Values will also be printed like this until the value is not
further than 10 from the standard value.
-Base chains also allow to set the chain's *policy*, i.e. what happens to
+Base chains also allow one to set the chain's *policy*, i.e. what happens to
packets not explicitly accepted or refused in contained rules. Supported policy
values are *accept* (which is the default) or *drop*.
@@ -450,7 +493,9 @@ RULES
[verse]
{*add* | *insert*} *rule* ['family'] 'table' 'chain' [*handle* 'handle' | *index* 'index'] 'statement' ... [*comment* 'comment']
*replace rule* ['family'] 'table' 'chain' *handle* 'handle' 'statement' ... [*comment* 'comment']
-*delete rule* ['family'] 'table' 'chain' *handle* 'handle'
+{*delete* | *reset*} *rule* ['family'] 'table' 'chain' *handle* 'handle'
+*destroy rule* ['family'] 'table' 'chain' *handle* 'handle'
+*reset rules* ['family'] ['table' ['chain']]
Rules are added to chains in the given table. If the family is not specified, the
ip family is used. Rules are constructed from two kinds of components according
@@ -478,6 +523,8 @@ case the rule is inserted after the specified rule.
beginning of the chain or before the specified rule.
*replace*:: Similar to *add*, but the rule replaces the specified rule.
*delete*:: Delete the specified rule.
+*destroy*:: Delete the specified rule, it does not fail if it does not exist.
+*reset*:: Reset rule-contained state, e.g. counter and quota statement values.
.*add a rule to ip table output chain*
-------------
@@ -528,10 +575,10 @@ section describes nft set syntax in more detail.
[verse]
*add set* ['family'] 'table' 'set' *{ type* 'type' | *typeof* 'expression' *;* [*flags* 'flags' *;*] [*timeout* 'timeout' *;*] [*gc-interval* 'gc-interval' *;*] [*elements = {* 'element'[*,* ...] *} ;*] [*size* 'size' *;*] [*comment* 'comment' *;*'] [*policy* 'policy' *;*] [*auto-merge ;*] *}*
-{*delete* | *list* | *flush*} *set* ['family'] 'table' 'set'
+{*delete* | *destroy* | *list* | *flush* | *reset* } *set* ['family'] 'table' 'set'
*list sets* ['family']
*delete set* ['family'] 'table' *handle* 'handle'
-{*add* | *delete*} *element* ['family'] 'table' 'set' *{* 'element'[*,* ...] *}*
+{*add* | *delete* | *destroy* } *element* ['family'] 'table' 'set' *{* 'element'[*,* ...] *}*
Sets are element containers of a user-defined data type, they are uniquely
identified by a user-defined name and attached to tables. Their behaviour can
@@ -540,8 +587,10 @@ be tuned with the flags that can be specified at set creation time.
[horizontal]
*add*:: Add a new set in the specified table. See the Set specification table below for more information about how to specify properties of a set.
*delete*:: Delete the specified set.
+*destroy*:: Delete the specified set, it does not fail if it does not exist.
*list*:: Display the elements in the specified set.
*flush*:: Remove all elements from the specified set.
+*reset*:: Reset state in all contained elements, e.g. counter and quota statement values.
.Set specifications
[options="header"]
@@ -554,8 +603,7 @@ string: ipv4_addr, ipv6_addr, ether_addr, inet_proto, inet_service, mark
data type of set element |
expression to derive the data type from
|flags |
-set flags |
-string: constant, dynamic, interval, timeout
+set flags | string: constant, dynamic, interval, timeout. Used to describe the sets properties.
|timeout |
time an element stays in the set, mandatory if set is added to from the packet path (ruleset)|
string, decimal followed by unit. Units are: d, h, m, s
@@ -581,7 +629,7 @@ MAPS
-----
[verse]
*add map* ['family'] 'table' 'map' *{ type* 'type' | *typeof* 'expression' [*flags* 'flags' *;*] [*elements = {* 'element'[*,* ...] *} ;*] [*size* 'size' *;*] [*comment* 'comment' *;*'] [*policy* 'policy' *;*] *}*
-{*delete* | *list* | *flush*} *map* ['family'] 'table' 'map'
+{*delete* | *destroy* | *list* | *flush* | *reset* } *map* ['family'] 'table' 'map'
*list maps* ['family']
Maps store data based on some specific key used as input. They are uniquely identified by a user-defined name and attached to tables.
@@ -589,10 +637,10 @@ Maps store data based on some specific key used as input. They are uniquely iden
[horizontal]
*add*:: Add a new map in the specified table.
*delete*:: Delete the specified map.
+*destroy*:: Delete the specified map, it does not fail if it does not exist.
*list*:: Display the elements in the specified map.
*flush*:: Remove all elements from the specified map.
-*add element*:: Comma-separated list of elements to add into the specified map.
-*delete element*:: Comma-separated list of element keys to delete from the specified map.
+*reset*:: Reset state in all contained elements, e.g. counter and quota statement values.
.Map specifications
[options="header"]
@@ -606,7 +654,7 @@ data type of set element |
expression to derive the data type from
|flags |
map flags |
-string: constant, interval
+string, same as set flags
|elements |
elements contained by the map |
map data type
@@ -618,18 +666,34 @@ map policy |
string: performance [default], memory
|=================
+Users can specifiy the properties/features that the set/map must support.
+This allows the kernel to pick an optimal internal representation.
+If a required flag is missing, the ruleset might still work, as
+nftables will auto-enable features if it can infer this from the ruleset.
+This may not work for all cases, however, so it is recommended to
+specify all required features in the set/map definition manually.
+
+.Set and Map flags
+[options="header"]
+|=================
+|Flag | Description
+|constant | Set contents will never change after creation
+|dynamic | Set must support updates from the packet path with the *add*, *update* or *delete* keywords.
+|interval | Set must be able to store intervals (ranges)
+|timeout | Set must support element timeouts (auto-removal of elements once they expire).
+|=================
ELEMENTS
--------
[verse]
____
-{*add* | *create* | *delete* | *get* } *element* ['family'] 'table' 'set' *{* 'ELEMENT'[*,* ...] *}*
+{*add* | *create* | *delete* | *destroy* | *get* | *reset* } *element* ['family'] 'table' 'set' *{* 'ELEMENT'[*,* ...] *}*
'ELEMENT' := 'key_expression' 'OPTIONS' [*:* 'value_expression']
'OPTIONS' := [*timeout* 'TIMESPEC'] [*expires* 'TIMESPEC'] [*comment* 'string']
'TIMESPEC' := ['num'*d*]['num'*h*]['num'*m*]['num'[*s*]]
____
-Element-related commands allow to change contents of named sets and maps.
+Element-related commands allow one to change contents of named sets and maps.
'key_expression' is typically a value matching the set type.
'value_expression' is not allowed in sets but mandatory when adding to maps, where it
matches the data part in its type definition. When deleting from maps, it may
@@ -643,6 +707,9 @@ listed elements may already exist.
be non-trivial in very large and/or interval sets. In the latter case, the
containing interval is returned instead of just the element itself.
+*reset* command resets state attached to the given element(s), e.g. counter and
+quota statement values.
+
.Element options
[options="header"]
|=================
@@ -661,7 +728,7 @@ FLOWTABLES
[verse]
{*add* | *create*} *flowtable* ['family'] 'table' 'flowtable' *{ hook* 'hook' *priority* 'priority' *; devices = {* 'device'[*,* ...] *} ; }*
*list flowtables* ['family']
-{*delete* | *list*} *flowtable* ['family'] 'table' 'flowtable'
+{*delete* | *destroy* | *list*} *flowtable* ['family'] 'table' 'flowtable'
*delete* *flowtable* ['family'] 'table' *handle* 'handle'
Flowtables allow you to accelerate packet forwarding in software. Flowtables
@@ -685,6 +752,7 @@ and subtraction can be used to set relative priority, e.g. filter + 5 equals to
[horizontal]
*add*:: Add a new flowtable for the given family with the given name.
*delete*:: Delete the specified flowtable.
+*destroy*:: Delete the specified flowtable, it does not fail if it does not exist.
*list*:: List all flowtables.
LISTING
@@ -701,11 +769,22 @@ kernel modules, such as nf_conntrack.
STATEFUL OBJECTS
----------------
[verse]
-{*add* | *delete* | *list* | *reset*} 'type' ['family'] 'table' 'object'
-*delete* 'type' ['family'] 'table' *handle* 'handle'
+{*add* | *delete* | *destroy* | *list* | *reset*} *counter* ['family'] 'table' 'object'
+{*add* | *delete* | *destroy* | *list* | *reset*} *quota* ['family'] 'table' 'object'
+{*add* | *delete* | *destroy* | *list*} *limit* ['family'] 'table' 'object'
+*delete* 'counter' ['family'] 'table' *handle* 'handle'
+*delete* 'quota' ['family'] 'table' *handle* 'handle'
+*delete* 'limit' ['family'] 'table' *handle* 'handle'
+*destroy* 'counter' ['family'] 'table' *handle* 'handle'
+*destroy* 'quota' ['family'] 'table' *handle* 'handle'
+*destroy* 'limit' ['family'] 'table' *handle* 'handle'
*list counters* ['family']
*list quotas* ['family']
*list limits* ['family']
+*reset counters* ['family']
+*reset quotas* ['family']
+*reset counters* ['family'] 'table'
+*reset quotas* ['family'] 'table'
Stateful objects are attached to tables and are identified by a unique name.
They group stateful information from rules, to reference them in rules the
@@ -714,6 +793,7 @@ keywords "type name" are used e.g. "counter name".
[horizontal]
*add*:: Add a new stateful object in the specified table.
*delete*:: Delete the specified object.
+*destroy*:: Delete the specified object, it does not fail if it does not exist.
*list*:: Display stateful information the object holds.
*reset*:: List-and-reset stateful object.
diff --git a/doc/payload-expression.txt b/doc/payload-expression.txt
index 930a1807..c7c267da 100644
--- a/doc/payload-expression.txt
+++ b/doc/payload-expression.txt
@@ -23,6 +23,14 @@ VLAN HEADER EXPRESSION
[verse]
*vlan* {*id* | *dei* | *pcp* | *type*}
+The vlan expression is used to match on the vlan header fields.
+This expression will not work in the *ip*, *ip6* and *inet* families,
+unless the vlan interface is configured with the *reorder_hdr off* setting.
+The default is *reorder_hdr on* which will automatically remove the vlan tag
+from the packet. See ip-link(8) for more information.
+For these families its easier to match the vlan interface name
+instead, using the *meta iif* or *meta iifname* expression.
+
.VLAN header expression
[options="header"]
|==================
@@ -126,6 +134,14 @@ Destination address |
ipv4_addr
|======================
+Careful with matching on *ip length*: If GRO/GSO is enabled, then the Linux
+kernel might aggregate several packets into one big packet that is larger than
+MTU. Moreover, if GRO/GSO maximum size is larger than 65535 (see man ip-link(8),
+specifically gro_ipv6_max_size and gso_ipv6_max_size), then *ip length* might
+be 0 for such jumbo packets. *meta length* allows you to match on the packet
+length including the IP header size. If you want to perform heuristics on the
+*ip length* field, then disable GRO/GSO.
+
ICMP HEADER EXPRESSION
~~~~~~~~~~~~~~~~~~~~~~
[verse]
@@ -236,6 +252,14 @@ Destination address |
ipv6_addr
|=======================
+Careful with matching on *ip6 length*: If GRO/GSO is enabled, then the Linux
+kernel might aggregate several packets into one big packet that is larger than
+MTU. Moreover, if GRO/GSO maximum size is larger than 65535 (see man ip-link(8),
+specifically gro_ipv6_max_size and gso_ipv6_max_size), then *ip6 length* might
+be 0 for such jumbo packets. *meta length* allows you to match on the packet
+length including the IP header size. If you want to perform heuristics on the
+*ip6 length* field, then disable GRO/GSO.
+
.Using ip6 header expressions
-----------------------------
# matching if first extension header indicates a fragment
@@ -245,7 +269,7 @@ ip6 nexthdr ipv6-frag
ICMPV6 HEADER EXPRESSION
~~~~~~~~~~~~~~~~~~~~~~~~
[verse]
-*icmpv6* {*type* | *code* | *checksum* | *parameter-problem* | *packet-too-big* | *id* | *sequence* | *max-delay*}
+*icmpv6* {*type* | *code* | *checksum* | *parameter-problem* | *packet-too-big* | *id* | *sequence* | *max-delay* | *taddr* | *daddr*}
This expression refers to ICMPv6 header fields. When using it in *inet*,
*bridge* or *netdev* families, it will cause an implicit dependency on IPv6 to
@@ -280,6 +304,12 @@ integer (16 bit)
|max-delay|
maximum response delay of MLD queries|
integer (16 bit)
+|taddr|
+target address of neighbor solicit/advert, redirect or MLD|
+ipv6_addr
+|daddr|
+destination address of redirect|
+ipv6_addr
|==============================
TCP HEADER EXPRESSION
@@ -524,6 +554,160 @@ compression Parameter Index |
integer (16 bit)
|============================
+GRE HEADER EXPRESSION
+~~~~~~~~~~~~~~~~~~~~~~~
+[verse]
+*gre* {*flags* | *version* | *protocol*}
+*gre* *ip* {*version* | *hdrlength* | *dscp* | *ecn* | *length* | *id* | *frag-off* | *ttl* | *protocol* | *checksum* | *saddr* | *daddr* }
+*gre* *ip6* {*version* | *dscp* | *ecn* | *flowlabel* | *length* | *nexthdr* | *hoplimit* | *saddr* | *daddr*}
+
+The gre expression is used to match on the gre header fields. This expression
+also allows to match on the IPv4 or IPv6 packet within the gre header.
+
+.GRE header expression
+[options="header"]
+|==================
+|Keyword| Description| Type
+|flags|
+checksum, routing, key, sequence and strict source route flags|
+integer (5 bit)
+|version|
+gre version field, 0 for GRE and 1 for PPTP|
+integer (3 bit)
+|protocol|
+EtherType of encapsulated packet|
+integer (16 bit)
+|==================
+
+.Matching inner IPv4 destination address encapsulated in gre
+------------------------------------------------------------
+netdev filter ingress gre ip daddr 9.9.9.9 counter
+------------------------------------------------------------
+
+GENEVE HEADER EXPRESSION
+~~~~~~~~~~~~~~~~~~~~~~~~
+[verse]
+*geneve* {*vni* | *flags*}
+*geneve* *ether* {*daddr* | *saddr* | *type*}
+*geneve* *vlan* {*id* | *dei* | *pcp* | *type*}
+*geneve* *ip* {*version* | *hdrlength* | *dscp* | *ecn* | *length* | *id* | *frag-off* | *ttl* | *protocol* | *checksum* | *saddr* | *daddr* }
+*geneve* *ip6* {*version* | *dscp* | *ecn* | *flowlabel* | *length* | *nexthdr* | *hoplimit* | *saddr* | *daddr*}
+*geneve* *tcp* {*sport* | *dport* | *sequence* | *ackseq* | *doff* | *reserved* | *flags* | *window* | *checksum* | *urgptr*}
+*geneve* *udp* {*sport* | *dport* | *length* | *checksum*}
+
+The geneve expression is used to match on the geneve header fields. The geneve
+header encapsulates a ethernet frame within a *udp* packet. This expression
+requires that you restrict the matching to *udp* packets (usually at
+port 6081 according to IANA-assigned ports).
+
+.GENEVE header expression
+[options="header"]
+|==================
+|Keyword| Description| Type
+|protocol|
+EtherType of encapsulated packet|
+integer (16 bit)
+|vni|
+Virtual Network ID (VNI)|
+integer (24 bit)
+|==================
+
+.Matching inner TCP destination port encapsulated in geneve
+----------------------------------------------------------
+netdev filter ingress udp dport 4789 geneve tcp dport 80 counter
+----------------------------------------------------------
+
+GRETAP HEADER EXPRESSION
+~~~~~~~~~~~~~~~~~~~~~~~~
+[verse]
+*gretap* {*vni* | *flags*}
+*gretap* *ether* {*daddr* | *saddr* | *type*}
+*gretap* *vlan* {*id* | *dei* | *pcp* | *type*}
+*gretap* *ip* {*version* | *hdrlength* | *dscp* | *ecn* | *length* | *id* | *frag-off* | *ttl* | *protocol* | *checksum* | *saddr* | *daddr* }
+*gretap* *ip6* {*version* | *dscp* | *ecn* | *flowlabel* | *length* | *nexthdr* | *hoplimit* | *saddr* | *daddr*}
+*gretap* *tcp* {*sport* | *dport* | *sequence* | *ackseq* | *doff* | *reserved* | *flags* | *window* | *checksum* | *urgptr*}
+*gretap* *udp* {*sport* | *dport* | *length* | *checksum*}
+
+The gretap expression is used to match on the encapsulated ethernet frame
+within the gre header. Use the *gre* expression to match on the *gre* header
+fields.
+
+.Matching inner TCP destination port encapsulated in gretap
+----------------------------------------------------------
+netdev filter ingress gretap tcp dport 80 counter
+----------------------------------------------------------
+
+VXLAN HEADER EXPRESSION
+~~~~~~~~~~~~~~~~~~~~~~~
+[verse]
+*vxlan* {*vni* | *flags*}
+*vxlan* *ether* {*daddr* | *saddr* | *type*}
+*vxlan* *vlan* {*id* | *dei* | *pcp* | *type*}
+*vxlan* *ip* {*version* | *hdrlength* | *dscp* | *ecn* | *length* | *id* | *frag-off* | *ttl* | *protocol* | *checksum* | *saddr* | *daddr* }
+*vxlan* *ip6* {*version* | *dscp* | *ecn* | *flowlabel* | *length* | *nexthdr* | *hoplimit* | *saddr* | *daddr*}
+*vxlan* *tcp* {*sport* | *dport* | *sequence* | *ackseq* | *doff* | *reserved* | *flags* | *window* | *checksum* | *urgptr*}
+*vxlan* *udp* {*sport* | *dport* | *length* | *checksum*}
+
+The vxlan expression is used to match on the vxlan header fields. The vxlan
+header encapsulates a ethernet frame within a *udp* packet. This expression
+requires that you restrict the matching to *udp* packets (usually at
+port 4789 according to IANA-assigned ports).
+
+.VXLAN header expression
+[options="header"]
+|==================
+|Keyword| Description| Type
+|flags|
+vxlan flags|
+integer (8 bit)
+|vni|
+Virtual Network ID (VNI)|
+integer (24 bit)
+|==================
+
+.Matching inner TCP destination port encapsulated in vxlan
+----------------------------------------------------------
+netdev filter ingress udp dport 4789 vxlan tcp dport 80 counter
+----------------------------------------------------------
+
+ARP HEADER EXPRESSION
+~~~~~~~~~~~~~~~~~~~~~
+[verse]
+*arp* {*htype* | *ptype* | *hlen* | *plen* | *operation* | *saddr* { *ip* | *ether* } | *daddr* { *ip* | *ether* }
+
+.ARP header expression
+[options="header"]
+|==================
+|Keyword| Description| Type
+|htype|
+ARP hardware type|
+integer (16 bit)
+|ptype|
+EtherType|
+ether_type
+|hlen|
+Hardware address len|
+integer (8 bit)
+|plen|
+Protocol address len |
+integer (8 bit)
+|operation|
+Operation |
+arp_op
+|saddr ether|
+Ethernet sender address|
+ether_addr
+|daddr ether|
+Ethernet target address|
+ether_addr
+|saddr ip|
+IPv4 sender address|
+ipv4_addr
+|daddr ip|
+IPv4 target address|
+ipv4_addr
+|======================
+
RAW PAYLOAD EXPRESSION
~~~~~~~~~~~~~~~~~~~~~~
[verse]
@@ -548,6 +732,8 @@ Link layer, for example the Ethernet header
Network header, for example IPv4 or IPv6
|th|
Transport Header, for example TCP
+|ih|
+Inner Header / Payload, i.e. after the L4 transport level header
|==============================
.Matching destination port of both UDP and TCP
@@ -589,6 +775,7 @@ The following syntaxes are valid only in a relational expression with boolean ty
*exthdr* {*hbh* | *frag* | *rt* | *dst* | *mh*}
*tcp option* {*eol* | *nop* | *maxseg* | *window* | *sack-perm* | *sack* | *sack0* | *sack1* | *sack2* | *sack3* | *timestamp*}
*ip option* { lsrr | ra | rr | ssrr }
+*dccp option* 'dccp_option_type'
.IPv6 extension headers
[options="header"]
@@ -614,37 +801,37 @@ Segment Routing Header
|Keyword| Description | TCP option fields
|eol|
End if option list|
-kind
+-
|nop|
1 Byte TCP Nop padding option |
-kind
+-
|maxseg|
TCP Maximum Segment Size|
-kind, length, size
+length, size
|window|
TCP Window Scaling |
-kind, length, count
+length, count
|sack-perm |
TCP SACK permitted |
-kind, length
+length
|sack|
TCP Selective Acknowledgement (alias of block 0) |
-kind, length, left, right
+length, left, right
|sack0|
TCP Selective Acknowledgement (block 0) |
-kind, length, left, right
+length, left, right
|sack1|
TCP Selective Acknowledgement (block 1) |
-kind, length, left, right
+length, left, right
|sack2|
TCP Selective Acknowledgement (block 2) |
-kind, length, left, right
+length, left, right
|sack3|
TCP Selective Acknowledgement (block 3) |
-kind, length, left, right
+length, left, right
|timestamp|
TCP Timestamps |
-kind, length, tsval, tsecr
+length, tsval, tsecr
|============================
TCP option matching also supports raw expression syntax to access arbitrary options:
@@ -673,7 +860,12 @@ type, length, ptr, addr
.finding TCP options
--------------------
-filter input tcp option sack-perm kind 1 counter
+filter input tcp option sack-perm exists counter
+--------------------
+
+.matching TCP options
+--------------------
+filter input tcp option maxseg size lt 536
--------------------
.matching IPv6 exthdr
@@ -686,6 +878,11 @@ ip6 filter input frag more-fragments 1 counter
filter input ip option lsrr exists counter
---------------------------------------
+.finding DCCP option
+------------------
+filter input dccp option 40 exists counter
+---------------------------------------
+
CONNTRACK EXPRESSIONS
~~~~~~~~~~~~~~~~~~~~~
Conntrack expressions refer to meta data of the connection tracking entry associated with a packet. +
diff --git a/doc/primary-expression.txt b/doc/primary-expression.txt
index f97778b9..782494bd 100644
--- a/doc/primary-expression.txt
+++ b/doc/primary-expression.txt
@@ -168,15 +168,18 @@ Either an integer or a date in ISO format. For example: "2019-06-06 17:00".
Hour and seconds are optional and can be omitted if desired. If omitted,
midnight will be assumed.
The following three would be equivalent: "2019-06-06", "2019-06-06 00:00"
-and "2019-06-06 00:00:00".
+and "2019-06-06 00:00:00". Use a range expression such as
+"2019-06-06 10:00"-"2019-06-10 14:00" for matching a time range.
When an integer is given, it is assumed to be a UNIX timestamp.
|day|
Either a day of week ("Monday", "Tuesday", etc.), or an integer between 0 and 6.
Strings are matched case-insensitively, and a full match is not expected (e.g. "Mon" would match "Monday").
-When an integer is given, 0 is Sunday and 6 is Saturday.
+When an integer is given, 0 is Sunday and 6 is Saturday. Use a range expression
+such as "Monday"-"Wednesday" for matching a week day range.
|hour|
A string representing an hour in 24-hour format. Seconds can optionally be specified.
-For example, 17:00 and 17:00:00 would be equivalent.
+For example, 17:00 and 17:00:00 would be equivalent. Use a range expression such
+as "17:00"-"19:00" for matching a time range.
|=============================
.Using meta expressions
@@ -190,6 +193,9 @@ filter output oif eth0
# incoming packet was subject to ipsec processing
raw prerouting meta ipsec exists accept
+
+# match incoming packet from 03:00 to 14:00 local time
+raw prerouting meta hour "03:00"-"14:00" counter accept
-----------------------
SOCKET EXPRESSION
@@ -428,6 +434,10 @@ Destination address of the tunnel|
ipv4_addr/ipv6_addr
|=================================
+*Note:* When using xfrm_interface, this expression is not useable in output
+hook as the plain packet does not traverse it with IPsec info attached - use a
+chain in postrouting hook instead.
+
NUMGEN EXPRESSION
~~~~~~~~~~~~~~~~~
@@ -438,7 +448,7 @@ Create a number generator. The *inc* or *random* keywords control its
operation mode: In *inc* mode, the last returned value is simply incremented.
In *random* mode, a new random number is returned. The value after *mod*
keyword specifies an upper boundary (read: modulus) which is not reached by
-returned numbers. The optional *offset* allows to increment the returned value
+returned numbers. The optional *offset* allows one to increment the returned value
by a fixed offset.
A typical use-case for *numgen* is load-balancing:
@@ -468,7 +478,7 @@ header to apply the hashing, concatenations are possible as well. The value
after *mod* keyword specifies an upper boundary (read: modulus) which is
not reached by returned numbers. The optional *seed* is used to specify an
init value used as seed in the hashing function. The optional *offset*
-allows to increment the returned value by a fixed offset.
+allows one to increment the returned value by a fixed offset.
A typical use-case for *jhash* and *symhash* is load-balancing:
diff --git a/doc/stateful-objects.txt b/doc/stateful-objects.txt
index 4972969e..00d3c5f1 100644
--- a/doc/stateful-objects.txt
+++ b/doc/stateful-objects.txt
@@ -77,13 +77,24 @@ per ct timeout comment field |
string
|=================
+tcp connection state names that can have a specific timeout value are:
+
+'close', 'close_wait', 'established', 'fin_wait', 'last_ack', 'retrans', 'syn_recv', 'syn_sent', 'time_wait' and 'unack'.
+
+You can use 'sysctl -a |grep net.netfilter.nf_conntrack_tcp_timeout_' to view and change the system-wide defaults.
+'ct timeout' allows for flow-specific settings, without changing the global timeouts.
+
+For example, tcp port 53 could have much lower settings than other traffic.
+
+udp state names that can have a specific timeout value are 'replied' and 'unreplied'.
+
.defining and assigning ct timeout policy
----------------------------------
table ip filter {
ct timeout customtimeout {
protocol tcp;
l3proto ip
- policy = { established: 120, close: 20 }
+ policy = { established: 2m, close: 20s }
}
chain output {
diff --git a/doc/statements.txt b/doc/statements.txt
index d402da70..39b31fd2 100644
--- a/doc/statements.txt
+++ b/doc/statements.txt
@@ -11,7 +11,7 @@ The verdict statement alters control flow in the ruleset and issues policy decis
[horizontal]
*accept*:: Terminate ruleset evaluation and accept the packet.
The packet can still be dropped later by another hook, for instance accept
-in the forward hook still allows to drop the packet later in the postrouting hook,
+in the forward hook still allows one to drop the packet later in the postrouting hook,
or another forward base chain that has a higher priority number and is evaluated
afterwards in the processing pipeline.
*drop*:: Terminate ruleset evaluation and drop the packet.
@@ -71,7 +71,7 @@ EXTENSION HEADER STATEMENT
The extension header statement alters packet content in variable-sized headers.
This can currently be used to alter the TCP Maximum segment size of packets,
-similar to TCPMSS.
+similar to the TCPMSS target in iptables.
.change tcp mss
---------------
@@ -80,6 +80,13 @@ tcp flags syn tcp option maxseg size set 1360
tcp flags syn tcp option maxseg size set rt mtu
---------------
+You can also remove tcp options via reset keyword.
+
+.remove tcp option
+---------------
+tcp flags syn reset tcp option sack-perm
+---------------
+
LOG STATEMENT
~~~~~~~~~~~~~
[verse]
@@ -164,37 +171,77 @@ 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*
____
A reject statement is used to send back an error packet in response to the
matched packet otherwise it is equivalent to drop so it is a terminating
statement, ending rule traversal. This statement is only valid in base chains
-using the *input*,
+using the *prerouting*, *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
|==================
-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 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
+|=================
+
+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.
@@ -271,7 +318,7 @@ ct event set new,related,destroy
NOTRACK STATEMENT
~~~~~~~~~~~~~~~~~
-The notrack statement allows to disable connection tracking for certain
+The notrack statement allows one to disable connection tracking for certain
packets.
[verse]
@@ -289,7 +336,7 @@ A meta statement sets the value of a meta expression. The existing meta fields
are: priority, mark, pkttype, nftrace. +
[verse]
-*meta* {*mark* | *priority* | *pkttype* | *nftrace*} *set* 'value'
+*meta* {*mark* | *priority* | *pkttype* | *nftrace* | *broute*} *set* 'value'
A meta statement sets meta data associated with a packet. +
@@ -309,6 +356,9 @@ pkt_type
|nftrace |
ruleset packet tracing on/off. Use *monitor trace* command to watch traces|
0, 1
+|broute |
+broute on/off. packets are routed instead of being bridged|
+0, 1
|==========================
LIMIT STATEMENT
@@ -325,8 +375,13 @@ ____
A limit statement matches at a limited rate using a token bucket filter. A rule
using this statement will match until this limit is reached. It can be used in
combination with the log statement to give limited logging. The optional
-*over* keyword makes it match over the specified rate. Default *burst* is 5.
-if you specify *burst*, it must be non-zero value.
+*over* keyword makes it match over the specified rate.
+
+The *burst* value influences the bucket size, i.e. jitter tolerance. With
+packet-based *limit*, the bucket holds exactly *burst* packets, by default
+five. If you specify packet *burst*, it must be a non-zero value. With
+byte-based *limit*, the bucket's minimum size is the given rate's byte value
+and the *burst* value adds to that, by default zero bytes.
.limit statement values
[options="header"]
@@ -344,8 +399,8 @@ NAT STATEMENTS
~~~~~~~~~~~~~~
[verse]
____
-*snat* [[*ip* | *ip6*] *to*] 'ADDR_SPEC' [*:*'PORT_SPEC'] ['FLAGS']
-*dnat* [[*ip* | *ip6*] *to*] 'ADDR_SPEC' [*:*'PORT_SPEC'] ['FLAGS']
+*snat* [[*ip* | *ip6*] [ *prefix* ] *to*] 'ADDR_SPEC' [*:*'PORT_SPEC'] ['FLAGS']
+*dnat* [[*ip* | *ip6*] [ *prefix* ] *to*] 'ADDR_SPEC' [*:*'PORT_SPEC'] ['FLAGS']
*masquerade* [*to :*'PORT_SPEC'] ['FLAGS']
*redirect* [*to :*'PORT_SPEC'] ['FLAGS']
@@ -383,6 +438,9 @@ Before kernel 4.18 nat statements require both prerouting and postrouting base c
to be present since otherwise packets on the return path won't be seen by
netfilter and therefore no reverse translation will take place.
+The optional *prefix* keyword allows to map to map *n* source addresses to *n*
+destination addresses. See 'Advanced NAT examples' below.
+
.NAT statement values
[options="header"]
|==================
@@ -393,7 +451,7 @@ You may specify a mapping to relate a list of tuples composed of arbitrary
expression key with address value. |
ipv4_addr, ipv6_addr, e.g. abcd::1234, or you can use a mapping, e.g. meta mark map { 10 : 192.168.1.2, 20 : 192.168.1.3 }
|port|
-Specifies that the source/destination address of the packet should be modified. |
+Specifies that the source/destination port of the packet should be modified. |
port number (16 bit)
|===============================
@@ -442,6 +500,52 @@ add rule inet nat postrouting meta oif ppp0 masquerade
------------------------
+.Advanced NAT examples
+----------------------
+
+# map prefixes in one network to that of another, e.g. 10.141.11.4 is mangled to 192.168.2.4,
+# 10.141.11.5 is mangled to 192.168.2.5 and so on.
+add rule nat postrouting snat ip prefix to ip saddr map { 10.141.11.0/24 : 192.168.2.0/24 }
+
+# map a source address, source port combination to a pool of destination addresses and ports:
+add rule nat postrouting dnat to ip saddr . tcp dport map { 192.168.1.2 . 80 : 10.141.10.2-10.141.10.5 . 8888-8999 }
+
+# The above example generates the following NAT expression:
+#
+# [ nat dnat ip addr_min reg 1 addr_max reg 10 proto_min reg 9 proto_max reg 11 ]
+#
+# which expects to obtain the following tuple:
+# IP address (min), source port (min), IP address (max), source port (max)
+# to be obtained from the map. The given addresses and ports are inclusive.
+
+# This also works with named maps and in combination with both concatenations and ranges:
+table ip nat {
+ map ipportmap {
+ typeof ip saddr : interval ip daddr . tcp dport
+ 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
+ }
+}
+
+@ipportmap maps network prefixes to a range of hosts and ports.
+The new destination is taken from the range provided by the map element.
+Same for the destination port.
+
+Note the use of the "interval" keyword in the typeof description.
+This is required so nftables knows that it has to ask for twice the
+amount of storage for each key-value pair in the map.
+
+": ipv4_addr . inet_service" would allow associating one address and one port
+with each key. But for this case, for each key, two addresses and two ports
+(The minimum and maximum values for both) have to be stored.
+
+------------------------
+
TPROXY STATEMENT
~~~~~~~~~~~~~~~~
Tproxy redirects the packet to a local socket without changing the packet header
@@ -601,7 +705,7 @@ ____
QUEUE_EXPRESSION can be used to compute a queue number
at run-time with the hash or numgen expressions. It also
-allows to use the map statement to assign fixed queue numbers
+allows one to use the map statement to assign fixed queue numbers
based on external inputs such as the source ip address or interface names.
.queue statement values
@@ -658,7 +762,7 @@ string
ip filter forward dup to 10.2.3.4 device "eth0"
# copy raw frame to another interface
-netdetv ingress dup to "eth0"
+netdev ingress dup to "eth0"
dup to "eth0"
# combine with map dst addr to gateways
@@ -668,10 +772,27 @@ dup to ip daddr map { 192.168.7.1 : "eth0", 192.168.7.2 : "eth1" }
FWD STATEMENT
~~~~~~~~~~~~~
The fwd statement is used to redirect a raw packet to another interface. It is
-only available in the netdev family ingress hook. It is similar to the dup
-statement except that no copy is made.
+only available in the netdev family ingress and egress hooks. It is similar to
+the dup statement except that no copy is made.
+You can also specify the address of the next hop and the device to forward the
+packet to. This updates the source and destination MAC address of the packet by
+transmitting it through the neighboring layer. This also decrements the ttl
+field of the IP packet. This provides a way to effectively bypass the classical
+forwarding path, thus skipping the fib (forwarding information base) lookup.
+
+[verse]
*fwd to* 'device'
+*fwd* [*ip* | *ip6*] *to* 'address' *device* 'device'
+
+.Using the fwd statement
+------------------------
+# redirect raw packet to device
+netdev ingress fwd to "eth0"
+
+# forward packet to next hop 192.168.200.1 via eth0 device
+netdev ingress ether saddr set fwd ip to 192.168.200.1 device "eth0"
+-----------------------------------
SET STATEMENT
~~~~~~~~~~~~~
@@ -687,6 +808,10 @@ will not grow indefinitely) either from the set definition or from the statement
that adds or updates them. The set statement can be used to e.g. create dynamic
blacklists.
+Dynamic updates are also supported with maps. In this case, the *add* or
+*update* rule needs to provide both the key and the data element (value),
+separated via ':'.
+
[verse]
{*add* | *update*} *@*'setname' *{* 'expression' [*timeout* 'timeout'] [*comment* 'string'] *}*
@@ -771,3 +896,20 @@ ____
# jump to different chains depending on layer 4 protocol type:
nft add rule ip filter input ip protocol vmap { tcp : jump tcp-chain, udp : jump udp-chain , icmp : jump icmp-chain }
------------------------
+
+XT STATEMENT
+~~~~~~~~~~~~
+This represents an xt statement from xtables compat interface. It is a
+fallback if translation is not available or not complete.
+
+[verse]
+____
+*xt* 'TYPE' 'NAME'
+
+'TYPE' := *match* | *target* | *watcher*
+____
+
+Seeing this means the ruleset (or parts of it) were created by *iptables-nft*
+and one should use that to manage it.
+
+*BEWARE:* nftables won't restore these statements.
diff --git a/examples/.gitignore b/examples/.gitignore
new file mode 100644
index 00000000..7b1a583c
--- /dev/null
+++ b/examples/.gitignore
@@ -0,0 +1,5 @@
+/.deps/
+/.libs/
+/nft-buffer
+/nft-json-file
+/*.o
diff --git a/examples/json-ruleset.nft b/examples/json-ruleset.nft
new file mode 100644
index 00000000..acea1786
--- /dev/null
+++ b/examples/json-ruleset.nft
@@ -0,0 +1,43 @@
+{
+ "nftables": [
+ {
+ "flush": {
+ "ruleset": {
+ "family": "ip"
+ }
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "expr": [
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/examples/nft-buffer.c b/examples/nft-buffer.c
new file mode 100644
index 00000000..1c4b1e04
--- /dev/null
+++ b/examples/nft-buffer.c
@@ -0,0 +1,34 @@
+/* gcc nft-buffer.c -o nft-buffer -lnftables */
+#include <stdlib.h>
+#include <nftables/libnftables.h>
+
+const char ruleset[] =
+ "flush ruleset;"
+ "add table x;"
+ "add chain x y { type filter hook input priority 0; };"
+ "add rule x y counter;";
+
+int main(void)
+{
+ struct nft_ctx *ctx;
+ int err;
+
+ ctx = nft_ctx_new(0);
+ if (!ctx) {
+ perror("cannot allocate nft context");
+ return EXIT_FAILURE;
+ }
+
+ /* create ruleset: all commands in the buffer are atomically applied */
+ err = nft_run_cmd_from_buffer(ctx, ruleset);
+ if (err < 0)
+ fprintf(stderr, "failed to run nftables command\n");
+
+ err = nft_run_cmd_from_buffer(ctx, "list ruleset");
+ if (err < 0)
+ fprintf(stderr, "failed to run nftables command\n");
+
+ nft_ctx_free(ctx);
+
+ return EXIT_SUCCESS;
+}
diff --git a/examples/nft-json-file.c b/examples/nft-json-file.c
new file mode 100644
index 00000000..0c832f22
--- /dev/null
+++ b/examples/nft-json-file.c
@@ -0,0 +1,30 @@
+/* gcc nft-json-file.c -o nft-json-file -lnftables */
+#include <stdlib.h>
+#include <nftables/libnftables.h>
+
+int main(void)
+{
+ struct nft_ctx *ctx;
+ int err;
+
+ ctx = nft_ctx_new(0);
+ if (!ctx) {
+ perror("cannot allocate nft context");
+ return EXIT_FAILURE;
+ }
+
+ nft_ctx_output_set_flags(ctx, NFT_CTX_OUTPUT_JSON);
+
+ /* create ruleset: all commands in the buffer are atomically applied */
+ err = nft_run_cmd_from_filename(ctx, "json-ruleset.nft");
+ if (err < 0)
+ fprintf(stderr, "failed to run nftables command\n");
+
+ err = nft_run_cmd_from_buffer(ctx, "list ruleset");
+ if (err < 0)
+ fprintf(stderr, "failed to run nftables command\n");
+
+ nft_ctx_free(ctx);
+
+ return EXIT_SUCCESS;
+}
diff --git a/files/Makefile.am b/files/Makefile.am
deleted file mode 100644
index 7deec151..00000000
--- a/files/Makefile.am
+++ /dev/null
@@ -1,3 +0,0 @@
-SUBDIRS = nftables \
- examples \
- osf
diff --git a/files/examples/Makefile.am b/files/examples/Makefile.am
deleted file mode 100644
index b29e9f61..00000000
--- a/files/examples/Makefile.am
+++ /dev/null
@@ -1,5 +0,0 @@
-pkgdocdir = ${docdir}/examples
-dist_pkgdoc_SCRIPTS = ct_helpers.nft \
- load_balancing.nft \
- secmark.nft \
- sets_and_maps.nft
diff --git a/files/nftables/Makefile.am b/files/nftables/Makefile.am
deleted file mode 100644
index ee88dd89..00000000
--- a/files/nftables/Makefile.am
+++ /dev/null
@@ -1,14 +0,0 @@
-dist_pkgdata_DATA = all-in-one.nft \
- arp-filter.nft \
- bridge-filter.nft \
- inet-filter.nft \
- inet-nat.nft \
- ipv4-filter.nft \
- ipv4-mangle.nft \
- ipv4-nat.nft \
- ipv4-raw.nft \
- ipv6-filter.nft \
- ipv6-mangle.nft \
- ipv6-nat.nft \
- ipv6-raw.nft \
- netdev-ingress.nft
diff --git a/files/osf/Makefile.am b/files/osf/Makefile.am
deleted file mode 100644
index d80196dd..00000000
--- a/files/osf/Makefile.am
+++ /dev/null
@@ -1,2 +0,0 @@
-pkgsysconfdir = ${sysconfdir}/nftables/osf
-dist_pkgsysconf_DATA = pf.os
diff --git a/include/Makefile.am b/include/Makefile.am
deleted file mode 100644
index b997f46b..00000000
--- a/include/Makefile.am
+++ /dev/null
@@ -1,41 +0,0 @@
-SUBDIRS = linux \
- nftables
-
-noinst_HEADERS = cli.h \
- cache.h \
- cmd.h \
- datatype.h \
- expression.h \
- fib.h \
- hash.h \
- ipopt.h \
- json.h \
- mini-gmp.h \
- gmputil.h \
- iface.h \
- mnl.h \
- nftables.h \
- payload.h \
- rbtree.h \
- tcpopt.h \
- statement.h \
- ct.h \
- erec.h \
- exthdr.h \
- headers.h \
- list.h \
- meta.h \
- misspell.h \
- numgen.h \
- netlink.h \
- osf.h \
- owner.h \
- parser.h \
- proto.h \
- sctp_chunk.h \
- socket.h \
- rule.h \
- rt.h \
- utils.h \
- xfrm.h \
- xt.h
diff --git a/include/cache.h b/include/cache.h
index 05233588..8ca4a9a7 100644
--- a/include/cache.h
+++ b/include/cache.h
@@ -1,7 +1,9 @@
#ifndef _NFT_CACHE_H_
#define _NFT_CACHE_H_
-#include <string.h>
+#include <list.h>
+
+struct handle;
enum cache_level_bits {
NFT_CACHE_TABLE_BIT = (1 << 0),
@@ -32,23 +34,45 @@ enum cache_level_flags {
NFT_CACHE_CHAIN_BIT |
NFT_CACHE_RULE_BIT,
NFT_CACHE_FULL = __NFT_CACHE_MAX_BIT - 1,
+ NFT_CACHE_TERSE = (1 << 27),
NFT_CACHE_SETELEM_MAYBE = (1 << 28),
NFT_CACHE_REFRESH = (1 << 29),
NFT_CACHE_UPDATE = (1 << 30),
NFT_CACHE_FLUSHED = (1 << 31),
};
+struct nft_filter_obj {
+ struct list_head list;
+ uint32_t family;
+ const char *table;
+ const char *set;
+};
+
+#define NFT_CACHE_HSIZE 8192
+
struct nft_cache_filter {
- const char *table;
- const char *set;
+ struct {
+ uint32_t family;
+ const char *table;
+ const char *chain;
+ const char *set;
+ const char *ft;
+ uint64_t rule_handle;
+ } list;
+
+ struct {
+ struct list_head head;
+ } obj[NFT_CACHE_HSIZE];
};
struct nft_cache;
+struct nft_ctx;
enum cmd_ops;
-unsigned int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds,
- struct nft_cache_filter *filter);
-int nft_cache_update(struct nft_ctx *ctx, enum cmd_ops cmd,
+int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds,
+ struct list_head *msgs, struct nft_cache_filter *filter,
+ unsigned int *flags);
+int nft_cache_update(struct nft_ctx *ctx, unsigned int flags,
struct list_head *msgs,
const struct nft_cache_filter *filter);
bool nft_cache_needs_update(struct nft_cache *cache);
@@ -64,7 +88,8 @@ static inline uint32_t djb_hash(const char *key)
return hash;
}
-#define NFT_CACHE_HSIZE 8192
+struct nft_cache_filter *nft_cache_filter_init(void);
+void nft_cache_filter_fini(struct nft_cache_filter *filter);
struct table;
struct chain;
@@ -73,6 +98,8 @@ void chain_cache_add(struct chain *chain, struct table *table);
void chain_cache_del(struct chain *chain);
struct chain *chain_cache_find(const struct table *table, const char *name);
+struct set;
+
void set_cache_add(struct set *set, struct table *table);
void set_cache_del(struct set *set);
struct set *set_cache_find(const struct table *table, const char *name);
@@ -97,6 +124,8 @@ void table_cache_del(struct table *table);
struct table *table_cache_find(const struct cache *cache, const char *name,
uint32_t family);
+struct obj;
+
void obj_cache_add(struct obj *obj, struct table *table);
void obj_cache_del(struct obj *obj);
struct obj *obj_cache_find(const struct table *table, const char *name,
@@ -114,4 +143,13 @@ struct nft_cache {
uint32_t flags;
};
+struct netlink_ctx;
+
+void nft_chain_cache_update(struct netlink_ctx *ctx, struct table *table,
+ const char *chain);
+
+int rule_cache_dump(struct netlink_ctx *ctx, const struct handle *h,
+ const struct nft_cache_filter *filter,
+ bool dump, bool reset);
+
#endif /* _NFT_CACHE_H_ */
diff --git a/include/cli.h b/include/cli.h
index 609ed2ed..f0a0d47a 100644
--- a/include/cli.h
+++ b/include/cli.h
@@ -2,7 +2,6 @@
#define _NFT_CLI_H_
#include <nftables/libnftables.h>
-#include <config.h>
#if defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDIT) || defined(HAVE_LIBLINENOISE)
extern int cli_init(struct nft_ctx *nft);
@@ -12,6 +11,6 @@ static inline int cli_init(struct nft_ctx *nft)
return -1;
}
#endif
-extern void cli_exit(void);
+extern void cli_exit(int rc);
#endif
diff --git a/include/cmd.h b/include/cmd.h
index 27fa6087..92a4152b 100644
--- a/include/cmd.h
+++ b/include/cmd.h
@@ -1,7 +1,13 @@
#ifndef _NFT_CMD_H_
#define _NFT_CMD_H_
+void cmd_add_loc(struct cmd *cmd, uint16_t offset, const struct location *loc);
void nft_cmd_error(struct netlink_ctx *ctx, struct cmd *cmd,
struct mnl_err *err);
+void nft_cmd_expand(struct cmd *cmd);
+void nft_cmd_post_expand(struct cmd *cmd);
+bool nft_cmd_collapse(struct list_head *cmds);
+void nft_cmd_uncollapse(struct list_head *cmds);
+
#endif
diff --git a/include/ct.h b/include/ct.h
index efb2d418..0a705fd0 100644
--- a/include/ct.h
+++ b/include/ct.h
@@ -39,5 +39,7 @@ extern const char *ct_label2str(const struct symbol_table *tbl,
extern const struct datatype ct_dir_type;
extern const struct datatype ct_state_type;
extern const struct datatype ct_status_type;
+extern const struct datatype ct_label_type;
+extern const struct datatype ct_event_type;
#endif /* NFTABLES_CT_H */
diff --git a/include/datatype.h b/include/datatype.h
index 448be57f..d4b4737c 100644
--- a/include/datatype.h
+++ b/include/datatype.h
@@ -22,7 +22,7 @@
* @TYPE_INET_SERVICE: internet service (integer subtype)
* @TYPE_ICMP_TYPE: ICMP type codes (integer subtype)
* @TYPE_TCP_FLAG: TCP flag (bitmask subtype)
- * @TCPE_DCCP_PKTTYPE: DCCP packet type (integer subtype)
+ * @TYPE_DCCP_PKTTYPE: DCCP packet type (integer subtype)
* @TYPE_MH_TYPE: Mobility Header type (integer subtype)
* @TYPE_TIME: relative time
* @TYPE_MARK: packet mark (integer subtype)
@@ -166,6 +166,7 @@ struct datatype {
struct error_record *(*parse)(struct parse_ctx *ctx,
const struct expr *sym,
struct expr **res);
+ struct error_record *(*err)(const struct expr *sym);
void (*describe)(struct output_ctx *octx);
const struct symbol_table *sym_tbl;
unsigned int refcnt;
@@ -173,12 +174,15 @@ struct datatype {
extern const struct datatype *datatype_lookup(enum datatypes type);
extern const struct datatype *datatype_lookup_byname(const char *name);
-extern struct datatype *datatype_get(const struct datatype *dtype);
+extern const struct datatype *datatype_get(const struct datatype *dtype);
extern void datatype_set(struct expr *expr, const struct datatype *dtype);
+extern void __datatype_set(struct expr *expr, const struct datatype *dtype);
extern void datatype_free(const struct datatype *dtype);
+struct datatype *datatype_clone(const struct datatype *orig_dtype);
struct parse_ctx {
struct symbol_tables *tbl;
+ const struct input_ctx *input;
};
extern struct error_record *symbol_parse(struct parse_ctx *ctx,
@@ -248,12 +252,16 @@ extern void symbol_table_print(const struct symbol_table *tbl,
extern struct symbol_table *rt_symbol_table_init(const char *filename);
extern void rt_symbol_table_free(const struct symbol_table *tbl);
+extern void rt_symbol_table_describe(struct output_ctx *octx, const char *name,
+ const struct symbol_table *tbl,
+ const struct datatype *type);
extern const struct datatype invalid_type;
extern const struct datatype verdict_type;
extern const struct datatype nfproto_type;
extern const struct datatype bitmask_type;
extern const struct datatype integer_type;
+extern const struct datatype xinteger_type;
extern const struct datatype string_type;
extern const struct datatype lladdr_type;
extern const struct datatype ipaddr_type;
@@ -275,6 +283,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);
@@ -296,7 +309,7 @@ concat_subtype_lookup(uint32_t type, unsigned int n)
}
extern const struct datatype *
-set_datatype_alloc(const struct datatype *orig_dtype, unsigned int byteorder);
+set_datatype_alloc(const struct datatype *orig_dtype, enum byteorder byteorder);
extern void time_print(uint64_t msec, struct output_ctx *octx);
extern struct error_record *time_parse(const struct location *loc,
@@ -309,6 +322,10 @@ extern struct error_record *rate_parse(const struct location *loc,
extern struct error_record *data_unit_parse(const struct location *loc,
const char *str, uint64_t *rate);
+struct limit_rate {
+ uint64_t rate, unit;
+};
+
extern void expr_chain_export(const struct expr *e, char *chain);
#endif /* NFTABLES_DATATYPE_H */
diff --git a/include/dccpopt.h b/include/dccpopt.h
new file mode 100644
index 00000000..3617fc1a
--- /dev/null
+++ b/include/dccpopt.h
@@ -0,0 +1,41 @@
+#ifndef NFTABLES_DCCPOPT_H
+#define NFTABLES_DCCPOPT_H
+
+#include <nftables.h>
+
+#define DCCPOPT_TYPE_MIN 0
+#define DCCPOPT_TYPE_MAX UINT8_MAX
+
+enum dccpopt_fields {
+ DCCPOPT_FIELD_INVALID,
+ DCCPOPT_FIELD_TYPE,
+};
+
+enum dccpopt_types {
+ DCCPOPT_PADDING = 0,
+ DCCPOPT_MANDATORY = 1,
+ DCCPOPT_SLOW_RECEIVER = 2,
+ DCCPOPT_RESERVED_SHORT = 3,
+ DCCPOPT_CHANGE_L = 32,
+ DCCPOPT_CONFIRM_L = 33,
+ DCCPOPT_CHANGE_R = 34,
+ DCCPOPT_CONFIRM_R = 35,
+ DCCPOPT_INIT_COOKIE = 36,
+ DCCPOPT_NDP_COUNT = 37,
+ DCCPOPT_ACK_VECTOR_NONCE_0 = 38,
+ DCCPOPT_ACK_VECTOR_NONCE_1 = 39,
+ DCCPOPT_DATA_DROPPED = 40,
+ DCCPOPT_TIMESTAMP = 41,
+ DCCPOPT_TIMESTAMP_ECHO = 42,
+ DCCPOPT_ELAPSED_TIME = 43,
+ DCCPOPT_DATA_CHECKSUM = 44,
+ DCCPOPT_RESERVED_LONG = 45,
+ DCCPOPT_CCID_SPECIFIC = 128,
+};
+
+const struct exthdr_desc *dccpopt_find_desc(uint8_t type);
+struct expr *dccpopt_expr_alloc(const struct location *loc, uint8_t type);
+void dccpopt_init_raw(struct expr *expr, uint8_t type, unsigned int offset,
+ unsigned int len);
+
+#endif /* NFTABLES_DCCPOPT_H */
diff --git a/include/erec.h b/include/erec.h
index 79a16290..c17f5def 100644
--- a/include/erec.h
+++ b/include/erec.h
@@ -76,4 +76,9 @@ extern int __fmtstring(4, 5) __stmt_binary_error(struct eval_ctx *ctx,
#define stmt_binary_error(ctx, s1, s2, fmt, args...) \
__stmt_binary_error(ctx, &(s1)->location, &(s2)->location, fmt, ## args)
+void print_location(FILE *f, const struct input_descriptor *indesc,
+ const struct location *loc);
+const char *line_location(const struct input_descriptor *indesc,
+ const struct location *loc, char *buf, size_t bufsiz);
+
#endif /* NFTABLES_EREC_H */
diff --git a/include/expression.h b/include/expression.h
index 742fcdd7..01b45b7c 100644
--- a/include/expression.h
+++ b/include/expression.h
@@ -1,7 +1,6 @@
#ifndef NFTABLES_EXPRESSION_H
#define NFTABLES_EXPRESSION_H
-#include <stdbool.h>
#include <gmputil.h>
#include <linux/netfilter/nf_tables.h>
@@ -12,6 +11,10 @@
#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)
+#define NFT_MAX_EXPR_RECURSION 16
+
/**
* enum expr_types
*
@@ -41,6 +44,10 @@
* @EXPR_NUMGEN: number generation expression
* @EXPR_HASH: hash expression
* @EXPR_RT: routing expression
+ * @EXPR_FIB forward information base expression
+ * @EXPR_XFRM XFRM (ipsec) expression
+ * @EXPR_SET_ELEM_CATCHALL catchall element expression
+ * @EXPR_FLAGCMP flagcmp expression
*/
enum expr_types {
EXPR_INVALID,
@@ -73,8 +80,9 @@ enum expr_types {
EXPR_XFRM,
EXPR_SET_ELEM_CATCHALL,
EXPR_FLAGCMP,
+
+ EXPR_MAX = EXPR_FLAGCMP
};
-#define EXPR_MAX EXPR_XFRM
enum ops {
OP_INVALID,
@@ -116,10 +124,15 @@ enum symbol_types {
* @maxval: expected maximum value
*/
struct expr_ctx {
+ /* expr_ctx does not own the reference to dtype. The caller must ensure
+ * the valid lifetime.
+ */
const struct datatype *dtype;
+
enum byteorder byteorder;
unsigned int len;
unsigned int maxval;
+ const struct expr *key;
};
static inline void __expr_set_context(struct expr_ctx *ctx,
@@ -131,6 +144,7 @@ static inline void __expr_set_context(struct expr_ctx *ctx,
ctx->byteorder = byteorder;
ctx->len = len;
ctx->maxval = maxval;
+ ctx->key = NULL;
}
static inline void expr_set_context(struct expr_ctx *ctx,
@@ -179,7 +193,7 @@ struct expr_ops {
};
const struct expr_ops *expr_ops(const struct expr *e);
-const struct expr_ops *expr_ops_by_type(enum expr_types etype);
+const struct expr_ops *expr_ops_by_type_u32(uint32_t value);
/**
* enum expr_flags
@@ -190,6 +204,7 @@ const struct expr_ops *expr_ops_by_type(enum expr_types etype);
* @EXPR_F_INTERVAL_END: set member ends an open interval
* @EXPR_F_BOOLEAN: expression is boolean (set by relational expr on LHS)
* @EXPR_F_INTERVAL: expression describes a interval
+ * @EXPR_F_KERNEL: expression resides in the kernel
*/
enum expr_flags {
EXPR_F_CONSTANT = 0x1,
@@ -198,6 +213,8 @@ enum expr_flags {
EXPR_F_INTERVAL_END = 0x8,
EXPR_F_BOOLEAN = 0x10,
EXPR_F_INTERVAL = 0x20,
+ EXPR_F_KERNEL = 0x40,
+ EXPR_F_REMOVE = 0x80,
};
#include <payload.h>
@@ -238,6 +255,7 @@ struct expr {
enum expr_types etype:8;
enum ops op:8;
unsigned int len;
+ struct cmd *cmd;
union {
struct {
@@ -305,6 +323,7 @@ struct expr {
/* EXPR_PAYLOAD */
const struct proto_desc *desc;
const struct proto_hdr_template *tmpl;
+ const struct proto_desc *inner_desc;
enum proto_bases base;
unsigned int offset;
bool is_raw;
@@ -323,6 +342,7 @@ struct expr {
/* EXPR_META */
enum nft_meta_keys key;
enum proto_bases base;
+ const struct proto_desc *inner_desc;
} meta;
struct {
/* SOCKET */
@@ -475,6 +495,7 @@ extern struct expr *compound_expr_alloc(const struct location *loc,
extern void compound_expr_add(struct expr *compound, struct expr *expr);
extern void compound_expr_remove(struct expr *compound, struct expr *expr);
extern void list_expr_sort(struct list_head *head);
+extern void list_splice_sorted(struct list_head *list, struct list_head *head);
extern struct expr *concat_expr_alloc(const struct location *loc);
@@ -482,10 +503,6 @@ extern struct expr *list_expr_alloc(const struct location *loc);
extern struct expr *set_expr_alloc(const struct location *loc,
const struct set *set);
-extern int set_to_intervals(struct list_head *msgs, struct set *set,
- struct expr *init, bool add,
- unsigned int debug_mask, bool merge,
- struct output_ctx *octx);
extern void concat_range_aggregate(struct expr *set);
extern void interval_map_decompose(struct expr *set);
@@ -513,5 +530,6 @@ struct expr *flagcmp_expr_alloc(const struct location *loc, enum ops op,
extern void range_expr_value_low(mpz_t rop, const struct expr *expr);
extern void range_expr_value_high(mpz_t rop, const struct expr *expr);
+void range_expr_swap_values(struct expr *range);
#endif /* NFTABLES_EXPRESSION_H */
diff --git a/include/exthdr.h b/include/exthdr.h
index 1bc756f9..084daba5 100644
--- a/include/exthdr.h
+++ b/include/exthdr.h
@@ -4,6 +4,7 @@
#include <proto.h>
#include <tcpopt.h>
#include <ipopt.h>
+#include <dccpopt.h>
enum exthdr_desc_id {
EXTHDR_DESC_UNKNOWN = 0,
diff --git a/include/gmputil.h b/include/gmputil.h
index 0cb85a7d..d1f4dcd2 100644
--- a/include/gmputil.h
+++ b/include/gmputil.h
@@ -1,8 +1,6 @@
#ifndef NFTABLES_GMPUTIL_H
#define NFTABLES_GMPUTIL_H
-#include <config.h>
-
#ifdef HAVE_LIBGMP
#include <gmp.h>
#else
@@ -79,4 +77,6 @@ extern void __mpz_switch_byteorder(mpz_t rop, unsigned int len);
__mpz_switch_byteorder(rop, len); \
}
+void nft_gmp_free(void *ptr);
+
#endif /* NFTABLES_GMPUTIL_H */
diff --git a/include/headers.h b/include/headers.h
index 759f93bf..13324c72 100644
--- a/include/headers.h
+++ b/include/headers.h
@@ -1,6 +1,8 @@
#ifndef NFTABLES_HEADERS_H
#define NFTABLES_HEADERS_H
+#include <netinet/in.h>
+
#ifndef IPPROTO_UDPLITE
# define IPPROTO_UDPLITE 136
#endif
diff --git a/include/intervals.h b/include/intervals.h
new file mode 100644
index 00000000..ef0fb53e
--- /dev/null
+++ b/include/intervals.h
@@ -0,0 +1,11 @@
+#ifndef NFTABLES_INTERVALS_H
+#define NFTABLES_INTERVALS_H
+
+int set_automerge(struct list_head *msgs, struct cmd *cmd, struct set *set,
+ struct expr *init, unsigned int debug_mask);
+int set_delete(struct list_head *msgs, struct cmd *cmd, struct set *set,
+ struct expr *init, unsigned int debug_mask);
+int set_overlap(struct list_head *msgs, struct set *set, struct expr *init);
+int set_to_intervals(const struct set *set, struct expr *init, bool add);
+
+#endif
diff --git a/include/ipopt.h b/include/ipopt.h
index d8d48066..03420dc6 100644
--- a/include/ipopt.h
+++ b/include/ipopt.h
@@ -6,7 +6,7 @@
#include <statement.h>
extern struct expr *ipopt_expr_alloc(const struct location *loc,
- uint8_t type, uint8_t field, uint8_t ptr);
+ uint8_t type, uint8_t field);
extern void ipopt_init_raw(struct expr *expr, uint8_t type,
unsigned int offset, unsigned int len,
diff --git a/include/json.h b/include/json.h
index 3db9f278..39be8928 100644
--- a/include/json.h
+++ b/include/json.h
@@ -69,10 +69,12 @@ json_t *uid_type_json(const struct expr *expr, struct output_ctx *octx);
json_t *gid_type_json(const struct expr *expr, struct output_ctx *octx);
json_t *expr_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
+json_t *flow_offload_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *payload_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *exthdr_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *quota_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *ct_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
+json_t *last_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *limit_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *fwd_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *notrack_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
@@ -82,6 +84,7 @@ json_t *nat_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *reject_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *counter_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *set_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
+json_t *map_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *log_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *objref_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *meter_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
@@ -90,6 +93,8 @@ json_t *verdict_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *connlimit_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *tproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *synproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
+json_t *optstrip_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
+json_t *xt_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd);
@@ -169,10 +174,12 @@ EXPR_PRINT_STUB(uid_type)
EXPR_PRINT_STUB(gid_type)
STMT_PRINT_STUB(expr)
+STMT_PRINT_STUB(flow_offload)
STMT_PRINT_STUB(payload)
STMT_PRINT_STUB(exthdr)
STMT_PRINT_STUB(quota)
STMT_PRINT_STUB(ct)
+STMT_PRINT_STUB(last)
STMT_PRINT_STUB(limit)
STMT_PRINT_STUB(fwd)
STMT_PRINT_STUB(notrack)
@@ -182,6 +189,7 @@ STMT_PRINT_STUB(nat)
STMT_PRINT_STUB(reject)
STMT_PRINT_STUB(counter)
STMT_PRINT_STUB(set)
+STMT_PRINT_STUB(map)
STMT_PRINT_STUB(log)
STMT_PRINT_STUB(objref)
STMT_PRINT_STUB(meter)
@@ -190,6 +198,8 @@ STMT_PRINT_STUB(verdict)
STMT_PRINT_STUB(connlimit)
STMT_PRINT_STUB(tproxy)
STMT_PRINT_STUB(synproxy)
+STMT_PRINT_STUB(optstrip)
+STMT_PRINT_STUB(xt)
#undef STMT_PRINT_STUB
#undef EXPR_PRINT_STUB
diff --git a/include/linux/Makefile.am b/include/linux/Makefile.am
deleted file mode 100644
index eb9fc4e4..00000000
--- a/include/linux/Makefile.am
+++ /dev/null
@@ -1,12 +0,0 @@
-SUBDIRS = netfilter \
- netfilter_arp \
- netfilter_bridge \
- netfilter_ipv4 \
- netfilter_ipv6
-
-noinst_HEADERS = netfilter_arp.h \
- netfilter_bridge.h \
- netfilter_decnet.h \
- netfilter.h \
- netfilter_ipv4.h \
- netfilter_ipv6.h
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
index feb6287c..9e078880 100644
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -54,6 +54,7 @@ enum nf_inet_hooks {
enum nf_dev_hooks {
NF_NETDEV_INGRESS,
+ NF_NETDEV_EGRESS,
NF_NETDEV_NUMHOOKS
};
diff --git a/include/linux/netfilter/Makefile.am b/include/linux/netfilter/Makefile.am
deleted file mode 100644
index 22f66a7e..00000000
--- a/include/linux/netfilter/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@
-noinst_HEADERS = nf_conntrack_common.h \
- nf_conntrack_tuple_common.h \
- nf_log.h \
- nf_nat.h \
- nf_tables.h \
- nf_tables_compat.h \
- nf_synproxy.h \
- nfnetlink_osf.h \
- nfnetlink_hook.h \
- nfnetlink.h
diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
index 75df968d..c62e6ac5 100644
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/linux/netfilter/nf_tables.h
@@ -97,6 +97,15 @@ enum nft_verdicts {
* @NFT_MSG_NEWFLOWTABLE: add new flow table (enum nft_flowtable_attributes)
* @NFT_MSG_GETFLOWTABLE: get flow table (enum nft_flowtable_attributes)
* @NFT_MSG_DELFLOWTABLE: delete flow table (enum nft_flowtable_attributes)
+ * @NFT_MSG_GETRULE_RESET: get rules and reset stateful expressions (enum nft_obj_attributes)
+ * @NFT_MSG_DESTROYTABLE: destroy a table (enum nft_table_attributes)
+ * @NFT_MSG_DESTROYCHAIN: destroy a chain (enum nft_chain_attributes)
+ * @NFT_MSG_DESTROYRULE: destroy a rule (enum nft_rule_attributes)
+ * @NFT_MSG_DESTROYSET: destroy a set (enum nft_set_attributes)
+ * @NFT_MSG_DESTROYSETELEM: destroy a set element (enum nft_set_elem_attributes)
+ * @NFT_MSG_DESTROYOBJ: destroy a stateful object (enum nft_object_attributes)
+ * @NFT_MSG_DESTROYFLOWTABLE: destroy flow table (enum nft_flowtable_attributes)
+ * @NFT_MSG_GETSETELEM_RESET: get set elements and reset attached stateful expressio ns (enum nft_set_elem_attributes)
*/
enum nf_tables_msg_types {
NFT_MSG_NEWTABLE,
@@ -124,6 +133,15 @@ enum nf_tables_msg_types {
NFT_MSG_NEWFLOWTABLE,
NFT_MSG_GETFLOWTABLE,
NFT_MSG_DELFLOWTABLE,
+ NFT_MSG_GETRULE_RESET,
+ NFT_MSG_DESTROYTABLE,
+ NFT_MSG_DESTROYCHAIN,
+ NFT_MSG_DESTROYRULE,
+ NFT_MSG_DESTROYSET,
+ NFT_MSG_DESTROYSETELEM,
+ NFT_MSG_DESTROYOBJ,
+ NFT_MSG_DESTROYFLOWTABLE,
+ NFT_MSG_GETSETELEM_RESET,
NFT_MSG_MAX,
};
@@ -164,7 +182,10 @@ enum nft_hook_attributes {
*/
enum nft_table_flags {
NFT_TABLE_F_DORMANT = 0x1,
+ NFT_TABLE_F_OWNER = 0x2,
};
+#define NFT_TABLE_F_MASK (NFT_TABLE_F_DORMANT | \
+ NFT_TABLE_F_OWNER)
/**
* enum nft_table_attributes - nf_tables table netlink attributes
@@ -173,6 +194,7 @@ enum nft_table_flags {
* @NFTA_TABLE_FLAGS: bitmask of enum nft_table_flags (NLA_U32)
* @NFTA_TABLE_USE: number of chains in this table (NLA_U32)
* @NFTA_TABLE_USERDATA: user data (NLA_BINARY)
+ * @NFTA_TABLE_OWNER: owner of this table through netlink portID (NLA_U32)
*/
enum nft_table_attributes {
NFTA_TABLE_UNSPEC,
@@ -182,6 +204,7 @@ enum nft_table_attributes {
NFTA_TABLE_HANDLE,
NFTA_TABLE_PAD,
NFTA_TABLE_USERDATA,
+ NFTA_TABLE_OWNER,
__NFTA_TABLE_MAX
};
#define NFTA_TABLE_MAX (__NFTA_TABLE_MAX - 1)
@@ -748,11 +771,14 @@ enum nft_dynset_attributes {
* @NFT_PAYLOAD_LL_HEADER: link layer header
* @NFT_PAYLOAD_NETWORK_HEADER: network header
* @NFT_PAYLOAD_TRANSPORT_HEADER: transport header
+ * @NFT_PAYLOAD_INNER_HEADER: inner header / payload
*/
enum nft_payload_bases {
NFT_PAYLOAD_LL_HEADER,
NFT_PAYLOAD_NETWORK_HEADER,
NFT_PAYLOAD_TRANSPORT_HEADER,
+ NFT_PAYLOAD_INNER_HEADER,
+ NFT_PAYLOAD_TUN_HEADER,
};
/**
@@ -772,6 +798,32 @@ enum nft_payload_csum_flags {
NFT_PAYLOAD_L4CSUM_PSEUDOHDR = (1 << 0),
};
+enum nft_inner_type {
+ NFT_INNER_UNSPEC = 0,
+ NFT_INNER_VXLAN,
+ NFT_INNER_GENEVE,
+};
+
+enum nft_inner_flags {
+ NFT_INNER_HDRSIZE = (1 << 0),
+ NFT_INNER_LL = (1 << 1),
+ NFT_INNER_NH = (1 << 2),
+ NFT_INNER_TH = (1 << 3),
+};
+#define NFT_INNER_MASK (NFT_INNER_HDRSIZE | NFT_INNER_LL | \
+ NFT_INNER_NH | NFT_INNER_TH)
+
+enum nft_inner_attributes {
+ NFTA_INNER_UNSPEC,
+ NFTA_INNER_NUM,
+ NFTA_INNER_TYPE,
+ NFTA_INNER_FLAGS,
+ NFTA_INNER_HDRSIZE,
+ NFTA_INNER_EXPR,
+ __NFTA_INNER_MAX
+};
+#define NFTA_INNER_MAX (__NFTA_INNER_MAX - 1)
+
/**
* enum nft_payload_attributes - nf_tables payload expression netlink attributes
*
@@ -809,12 +861,14 @@ enum nft_exthdr_flags {
* @NFT_EXTHDR_OP_TCP: match against tcp options
* @NFT_EXTHDR_OP_IPV4: match against ipv4 options
* @NFT_EXTHDR_OP_SCTP: match against sctp chunks
+ * @NFT_EXTHDR_OP_DCCP: match against dccp options
*/
enum nft_exthdr_op {
NFT_EXTHDR_OP_IPV6,
NFT_EXTHDR_OP_TCPOPT,
NFT_EXTHDR_OP_IPV4,
NFT_EXTHDR_OP_SCTP,
+ NFT_EXTHDR_OP_DCCP,
__NFT_EXTHDR_OP_MAX
};
#define NFT_EXTHDR_OP_MAX (__NFT_EXTHDR_OP_MAX - 1)
@@ -881,6 +935,7 @@ enum nft_exthdr_attributes {
* @NFT_META_TIME_HOUR: hour of day (in seconds)
* @NFT_META_SDIF: slave device interface index
* @NFT_META_SDIFNAME: slave device interface name
+ * @NFT_META_BRI_BROUTE: packet br_netfilter_broute bit
*/
enum nft_meta_keys {
NFT_META_LEN,
@@ -891,7 +946,8 @@ enum nft_meta_keys {
NFT_META_OIF,
NFT_META_IIFNAME,
NFT_META_OIFNAME,
- NFT_META_IIFTYPE,
+ NFT_META_IFTYPE,
+#define NFT_META_IIFTYPE NFT_META_IFTYPE
NFT_META_OIFTYPE,
NFT_META_SKUID,
NFT_META_SKGID,
@@ -918,6 +974,8 @@ enum nft_meta_keys {
NFT_META_TIME_HOUR,
NFT_META_SDIF,
NFT_META_SDIFNAME,
+ NFT_META_BRI_BROUTE,
+ __NFT_META_IIFTYPE,
};
/**
@@ -1013,6 +1071,7 @@ enum nft_rt_attributes {
*
* @NFTA_SOCKET_KEY: socket key to match
* @NFTA_SOCKET_DREG: destination register
+ * @NFTA_SOCKET_LEVEL: cgroups2 ancestor level (only for cgroupsv2)
*/
enum nft_socket_attributes {
NFTA_SOCKET_UNSPEC,
@@ -1029,6 +1088,7 @@ enum nft_socket_attributes {
* @NFT_SOCKET_TRANSPARENT: Value of the IP(V6)_TRANSPARENT socket option
* @NFT_SOCKET_MARK: Value of the socket mark
* @NFT_SOCKET_WILDCARD: Whether the socket is zero-bound (e.g. 0.0.0.0 or ::0)
+ * @NFT_SOCKET_CGROUPV2: Match on cgroups version 2
*/
enum nft_socket_keys {
NFT_SOCKET_TRANSPARENT,
@@ -1189,6 +1249,21 @@ enum nft_counter_attributes {
#define NFTA_COUNTER_MAX (__NFTA_COUNTER_MAX - 1)
/**
+ * enum nft_last_attributes - nf_tables last expression netlink attributes
+ *
+ * @NFTA_LAST_SET: last update has been set, zero means never updated (NLA_U32)
+ * @NFTA_LAST_MSECS: milliseconds since last update (NLA_U64)
+ */
+enum nft_last_attributes {
+ NFTA_LAST_UNSPEC,
+ NFTA_LAST_SET,
+ NFTA_LAST_MSECS,
+ NFTA_LAST_PAD,
+ __NFTA_LAST_MAX
+};
+#define NFTA_LAST_MAX (__NFTA_LAST_MAX - 1)
+
+/**
* enum nft_log_attributes - nf_tables log expression netlink attributes
*
* @NFTA_LOG_GROUP: netlink group to send messages to (NLA_U32)
diff --git a/include/linux/netfilter/nfnetlink_hook.h b/include/linux/netfilter/nfnetlink_hook.h
index bbcd285b..84a561a7 100644
--- a/include/linux/netfilter/nfnetlink_hook.h
+++ b/include/linux/netfilter/nfnetlink_hook.h
@@ -32,8 +32,12 @@ enum nfnl_hook_attributes {
/**
* enum nfnl_hook_chain_info_attributes - chain description
*
- * NFNLA_HOOK_INFO_DESC: nft chain and table name (enum nft_table_attributes) (NLA_NESTED)
- * NFNLA_HOOK_INFO_TYPE: chain type (enum nfnl_hook_chaintype) (NLA_U32)
+ * @NFNLA_HOOK_INFO_DESC: nft chain and table name (NLA_NESTED)
+ * @NFNLA_HOOK_INFO_TYPE: chain type (enum nfnl_hook_chaintype) (NLA_U32)
+ *
+ * NFNLA_HOOK_INFO_DESC depends on NFNLA_HOOK_INFO_TYPE value:
+ * NFNL_HOOK_TYPE_NFTABLES: enum nft_table_attributes
+ * NFNL_HOOK_TYPE_BPF: enum nfnl_hook_bpf_attributes
*/
enum nfnl_hook_chain_info_attributes {
NFNLA_HOOK_INFO_UNSPEC,
@@ -55,10 +59,24 @@ enum nfnl_hook_chain_desc_attributes {
/**
* enum nfnl_hook_chaintype - chain type
*
- * @NFNL_HOOK_TYPE_NFTABLES nf_tables base chain
+ * @NFNL_HOOK_TYPE_NFTABLES: nf_tables base chain
+ * @NFNL_HOOK_TYPE_BPF: bpf program
*/
enum nfnl_hook_chaintype {
NFNL_HOOK_TYPE_NFTABLES = 0x1,
+ NFNL_HOOK_TYPE_BPF,
+};
+
+/**
+ * enum nfnl_hook_bpf_attributes - bpf prog description
+ *
+ * @NFNLA_HOOK_BPF_ID: bpf program id (NLA_U32)
+ */
+enum nfnl_hook_bpf_attributes {
+ NFNLA_HOOK_BPF_UNSPEC,
+ NFNLA_HOOK_BPF_ID,
+ __NFNLA_HOOK_BPF_MAX,
};
+#define NFNLA_HOOK_BPF_MAX (__NFNLA_HOOK_BPF_MAX - 1)
#endif /* _NFNL_HOOK_H */
diff --git a/include/linux/netfilter_arp/Makefile.am b/include/linux/netfilter_arp/Makefile.am
deleted file mode 100644
index 0a16c1ab..00000000
--- a/include/linux/netfilter_arp/Makefile.am
+++ /dev/null
@@ -1 +0,0 @@
-noinst_HEADERS = arp_tables.h
diff --git a/include/linux/netfilter_bridge/Makefile.am b/include/linux/netfilter_bridge/Makefile.am
deleted file mode 100644
index d2e8b38b..00000000
--- a/include/linux/netfilter_bridge/Makefile.am
+++ /dev/null
@@ -1 +0,0 @@
-noinst_HEADERS = ebtables.h
diff --git a/include/linux/netfilter_ipv4/Makefile.am b/include/linux/netfilter_ipv4/Makefile.am
deleted file mode 100644
index fec42533..00000000
--- a/include/linux/netfilter_ipv4/Makefile.am
+++ /dev/null
@@ -1 +0,0 @@
-noinst_HEADERS = ip_tables.h
diff --git a/include/linux/netfilter_ipv6/Makefile.am b/include/linux/netfilter_ipv6/Makefile.am
deleted file mode 100644
index bec6c3f1..00000000
--- a/include/linux/netfilter_ipv6/Makefile.am
+++ /dev/null
@@ -1 +0,0 @@
-noinst_HEADERS = ip6_tables.h
diff --git a/include/meta.h b/include/meta.h
index 1478902e..af2d772b 100644
--- a/include/meta.h
+++ b/include/meta.h
@@ -45,4 +45,6 @@ extern const struct datatype date_type;
extern const struct datatype hour_type;
extern const struct datatype day_type;
+bool lhs_is_meta_hour(const struct expr *meta);
+
#endif /* NFTABLES_META_H */
diff --git a/include/mnl.h b/include/mnl.h
index 68ec80cd..cd5a2053 100644
--- a/include/mnl.h
+++ b/include/mnl.h
@@ -33,8 +33,10 @@ int mnl_nft_rule_add(struct netlink_ctx *ctx, struct cmd *cmd,
int mnl_nft_rule_del(struct netlink_ctx *ctx, struct cmd *cmd);
int mnl_nft_rule_replace(struct netlink_ctx *ctx, struct cmd *cmd);
-struct nftnl_rule_list *mnl_nft_rule_dump(struct netlink_ctx *ctx,
- int family);
+struct nftnl_rule_list *mnl_nft_rule_dump(struct netlink_ctx *ctx, int family,
+ const char *table, const char *chain,
+ uint64_t rule_handle,
+ bool dump, bool reset);
int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags);
@@ -43,29 +45,34 @@ int mnl_nft_chain_rename(struct netlink_ctx *ctx, const struct cmd *cmd,
const struct chain *chain);
struct nftnl_chain_list *mnl_nft_chain_dump(struct netlink_ctx *ctx,
- int family);
+ int family, const char *table,
+ const char *chain);
int mnl_nft_table_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags);
int mnl_nft_table_del(struct netlink_ctx *ctx, struct cmd *cmd);
struct nftnl_table_list *mnl_nft_table_dump(struct netlink_ctx *ctx,
- int family);
+ int family, const char *table);
int mnl_nft_set_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags);
int mnl_nft_set_del(struct netlink_ctx *ctx, struct cmd *cmd);
struct nftnl_set_list *mnl_nft_set_dump(struct netlink_ctx *ctx, int family,
- const char *table);
+ const char *table, const char *set);
-int mnl_nft_setelem_add(struct netlink_ctx *ctx, const struct set *set,
- const struct expr *expr, unsigned int flags);
-int mnl_nft_setelem_del(struct netlink_ctx *ctx, const struct cmd *cmd);
+int mnl_nft_setelem_add(struct netlink_ctx *ctx, struct cmd *cmd,
+ const struct set *set, const struct expr *expr,
+ unsigned int flags);
+int mnl_nft_setelem_del(struct netlink_ctx *ctx, struct cmd *cmd,
+ const struct handle *h, const struct expr *init);
int mnl_nft_setelem_flush(struct netlink_ctx *ctx, const struct cmd *cmd);
-int mnl_nft_setelem_get(struct netlink_ctx *ctx, struct nftnl_set *nls);
+int mnl_nft_setelem_get(struct netlink_ctx *ctx, struct nftnl_set *nls,
+ bool reset);
struct nftnl_set *mnl_nft_setelem_get_one(struct netlink_ctx *ctx,
- struct nftnl_set *nls);
+ struct nftnl_set *nls,
+ bool reset);
struct nftnl_obj_list *mnl_nft_obj_dump(struct netlink_ctx *ctx, int family,
const char *table,
@@ -76,7 +83,8 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, struct cmd *cmd,
int mnl_nft_obj_del(struct netlink_ctx *ctx, struct cmd *cmd, int type);
struct nftnl_flowtable_list *
-mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family, const char *table);
+mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family,
+ const char *table, const char *ft);
int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags);
diff --git a/include/netlink.h b/include/netlink.h
index 2467ff82..27a62462 100644
--- a/include/netlink.h
+++ b/include/netlink.h
@@ -39,12 +39,29 @@ struct netlink_parse_ctx {
struct stmt *stmt;
struct expr *registers[MAX_REGS + 1];
unsigned int debug_mask;
+ struct netlink_ctx *nlctx;
+ bool inner;
+ uint8_t inner_reg;
};
-struct rule_pp_ctx {
+
+#define RULE_PP_IN_CONCATENATION (1 << 0)
+#define RULE_PP_IN_SET_ELEM (1 << 1)
+
+#define RULE_PP_REMOVE_OP_AND (RULE_PP_IN_CONCATENATION | \
+ RULE_PP_IN_SET_ELEM)
+
+struct dl_proto_ctx {
struct proto_ctx pctx;
struct payload_dep_ctx pdctx;
+};
+
+struct rule_pp_ctx {
+ struct dl_proto_ctx _dl[2];
+ struct dl_proto_ctx *dl;
struct stmt *stmt;
+ unsigned int flags;
+ struct set *set;
};
extern const struct input_descriptor indesc_netlink;
@@ -68,10 +85,13 @@ struct netlink_ctx {
const void *data;
uint32_t seqnum;
struct nftnl_batch *batch;
+ int maybe_emsgsize;
};
extern struct nftnl_expr *alloc_nft_expr(const char *name);
extern void alloc_setelem_cache(const struct expr *set, struct nftnl_set *nls);
+struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
+ const struct expr *expr);
extern struct nftnl_table *netlink_table_alloc(const struct nlmsghdr *nlh);
extern struct nftnl_chain *netlink_chain_alloc(const struct nlmsghdr *nlh);
@@ -81,7 +101,7 @@ extern struct nftnl_rule *netlink_rule_alloc(const struct nlmsghdr *nlh);
struct nft_data_linearize {
uint32_t len;
- uint32_t value[4];
+ uint32_t value[NFT_REG32_COUNT];
char chain[NFT_CHAIN_MAXNAMELEN];
uint32_t chain_id;
int verdict;
@@ -122,8 +142,6 @@ extern struct expr *netlink_alloc_data(const struct location *loc,
const struct nft_data_delinearize *nld,
enum nft_registers dreg);
-extern int netlink_list_rules(struct netlink_ctx *ctx, const struct handle *h);
-
struct netlink_linearize_ctx;
extern void netlink_linearize_rule(struct netlink_ctx *ctx,
const struct rule *rule,
@@ -135,7 +153,8 @@ extern int netlink_list_chains(struct netlink_ctx *ctx, const struct handle *h);
extern struct chain *netlink_delinearize_chain(struct netlink_ctx *ctx,
const struct nftnl_chain *nlc);
-extern int netlink_list_tables(struct netlink_ctx *ctx, const struct handle *h);
+extern int netlink_list_tables(struct netlink_ctx *ctx, const struct handle *h,
+ const struct nft_cache_filter *filter);
extern struct table *netlink_delinearize_table(struct netlink_ctx *ctx,
const struct nftnl_table *nlt);
@@ -147,10 +166,11 @@ extern struct stmt *netlink_parse_set_expr(const struct set *set,
const struct nftnl_expr *nle);
extern int netlink_list_setelems(struct netlink_ctx *ctx,
- const struct handle *h, struct set *set);
+ const struct handle *h, struct set *set,
+ bool reset);
extern int netlink_get_setelem(struct netlink_ctx *ctx, const struct handle *h,
const struct location *loc, struct set *cache_set,
- struct set *set, struct expr *init);
+ struct set *set, struct expr *init, bool reset);
extern int netlink_delinearize_setelem(struct nftnl_set_elem *nlse,
struct set *set,
struct nft_cache *cache);
@@ -166,6 +186,9 @@ extern int netlink_list_flowtables(struct netlink_ctx *ctx,
extern struct flowtable *netlink_delinearize_flowtable(struct netlink_ctx *ctx,
struct nftnl_flowtable *nlo);
+extern int netlink_reset_rules(struct netlink_ctx *ctx, const struct cmd *cmd,
+ bool dump);
+
extern void netlink_dump_chain(const struct nftnl_chain *nlc,
struct netlink_ctx *ctx);
extern void netlink_dump_rule(const struct nftnl_rule *nlr,
@@ -236,4 +259,6 @@ struct nft_expr_loc {
struct nft_expr_loc *nft_expr_loc_find(const struct nftnl_expr *nle,
struct netlink_linearize_ctx *ctx);
+struct dl_proto_ctx *dl_proto_ctx(struct rule_pp_ctx *ctx);
+
#endif /* NFTABLES_NETLINK_H */
diff --git a/include/nft.h b/include/nft.h
new file mode 100644
index 00000000..a2d62dbf
--- /dev/null
+++ b/include/nft.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef NFTABLES_NFT_H
+#define NFTABLES_NFT_H
+
+#include <config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#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/include/nftables.h b/include/nftables.h
index 7b633905..4b7c3359 100644
--- a/include/nftables.h
+++ b/include/nftables.h
@@ -1,7 +1,6 @@
#ifndef NFTABLES_NFTABLES_H
#define NFTABLES_NFTABLES_H
-#include <stdbool.h>
#include <stdarg.h>
#include <limits.h>
#include <utils.h>
@@ -23,6 +22,20 @@ struct symbol_tables {
const struct symbol_table *realm;
};
+struct input_ctx {
+ unsigned int flags;
+};
+
+static inline bool nft_input_no_dns(const struct input_ctx *ictx)
+{
+ return ictx->flags & NFT_CTX_INPUT_NO_DNS;
+}
+
+static inline bool nft_input_json(const struct input_ctx *ictx)
+{
+ return ictx->flags & NFT_CTX_INPUT_JSON;
+}
+
struct output_ctx {
unsigned int flags;
union {
@@ -119,15 +132,18 @@ struct nft_ctx {
unsigned int num_vars;
unsigned int parser_max_errors;
unsigned int debug_mask;
+ struct input_ctx input;
struct output_ctx output;
bool check;
struct nft_cache cache;
uint32_t flags;
+ uint32_t optimize_flags;
struct parser_state *state;
void *scanner;
struct scope *top_scope;
void *json_root;
json_t *json_echo;
+ const char *stdin_buf;
};
enum nftables_exit_codes {
@@ -175,6 +191,7 @@ enum input_descriptor_types {
INDESC_FILE,
INDESC_CLI,
INDESC_NETLINK,
+ INDESC_STDIN,
};
/**
@@ -207,7 +224,6 @@ struct input_descriptor {
void ct_label_table_init(struct nft_ctx *ctx);
void mark_table_init(struct nft_ctx *ctx);
-void gmp_init(void);
void realm_table_rt_init(struct nft_ctx *ctx);
void devgroup_table_init(struct nft_ctx *ctx);
void xt_init(void);
@@ -219,8 +235,9 @@ void realm_table_rt_exit(struct nft_ctx *ctx);
int nft_print(struct output_ctx *octx, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
-int nft_gmp_print(struct output_ctx *octx, const char *fmt, ...)
- __attribute__((format(printf, 2, 0)));
+int nft_gmp_print(struct output_ctx *octx, const char *fmt, ...);
+
+int nft_optimize(struct nft_ctx *nft, struct list_head *cmds);
#define __NFT_OUTPUT_NOTSUPP UINT_MAX
diff --git a/include/nftables/Makefile.am b/include/nftables/Makefile.am
deleted file mode 100644
index 5cfb0c6c..00000000
--- a/include/nftables/Makefile.am
+++ /dev/null
@@ -1 +0,0 @@
-pkginclude_HEADERS = libnftables.h
diff --git a/include/nftables/libnftables.h b/include/nftables/libnftables.h
index 957b5fbe..c1d48d76 100644
--- a/include/nftables/libnftables.h
+++ b/include/nftables/libnftables.h
@@ -9,7 +9,6 @@
#ifndef LIB_NFTABLES_H
#define LIB_NFTABLES_H
-#define _GNU_SOURCE
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
@@ -41,6 +40,21 @@ void nft_ctx_free(struct nft_ctx *ctx);
bool nft_ctx_get_dry_run(struct nft_ctx *ctx);
void nft_ctx_set_dry_run(struct nft_ctx *ctx, bool dry);
+enum nft_optimize_flags {
+ NFT_OPTIMIZE_ENABLED = 0x1,
+};
+
+uint32_t nft_ctx_get_optimize(struct nft_ctx *ctx);
+void nft_ctx_set_optimize(struct nft_ctx *ctx, uint32_t flags);
+
+enum {
+ NFT_CTX_INPUT_NO_DNS = (1 << 0),
+ NFT_CTX_INPUT_JSON = (1 << 1),
+};
+
+unsigned int nft_ctx_input_get_flags(struct nft_ctx *ctx);
+unsigned int nft_ctx_input_set_flags(struct nft_ctx *ctx, unsigned int flags);
+
enum {
NFT_CTX_OUTPUT_REVERSEDNS = (1 << 0),
NFT_CTX_OUTPUT_SERVICE = (1 << 1),
diff --git a/include/parser.h b/include/parser.h
index e8635b4c..f79a22f3 100644
--- a/include/parser.h
+++ b/include/parser.h
@@ -22,36 +22,73 @@ struct parser_state {
struct scope *scopes[SCOPE_NEST_MAX];
unsigned int scope;
+ bool scope_err;
unsigned int flex_state_pop;
unsigned int startcond_type;
struct list_head *cmds;
+ unsigned int *startcond_active;
};
enum startcond_type {
PARSER_SC_BEGIN,
PARSER_SC_ARP,
+ PARSER_SC_AT,
PARSER_SC_CT,
PARSER_SC_COUNTER,
PARSER_SC_ETH,
+ PARSER_SC_GRE,
+ PARSER_SC_ICMP,
+ PARSER_SC_IGMP,
PARSER_SC_IP,
PARSER_SC_IP6,
+ PARSER_SC_LAST,
PARSER_SC_LIMIT,
+ PARSER_SC_META,
+ PARSER_SC_POLICY,
PARSER_SC_QUOTA,
PARSER_SC_SCTP,
PARSER_SC_SECMARK,
+ PARSER_SC_TCP,
+ PARSER_SC_TYPE,
PARSER_SC_VLAN,
+ PARSER_SC_XT,
+ PARSER_SC_CMD_DESTROY,
+ PARSER_SC_CMD_EXPORT,
+ PARSER_SC_CMD_IMPORT,
PARSER_SC_CMD_LIST,
+ PARSER_SC_CMD_MONITOR,
+ PARSER_SC_CMD_RESET,
+ PARSER_SC_EXPR_AH,
+ PARSER_SC_EXPR_COMP,
+ PARSER_SC_EXPR_DCCP,
+ PARSER_SC_EXPR_DST,
+ PARSER_SC_EXPR_ESP,
PARSER_SC_EXPR_FIB,
+ PARSER_SC_EXPR_FRAG,
PARSER_SC_EXPR_HASH,
+ PARSER_SC_EXPR_HBH,
PARSER_SC_EXPR_IPSEC,
+ PARSER_SC_EXPR_MH,
PARSER_SC_EXPR_NUMGEN,
+ PARSER_SC_EXPR_OSF,
PARSER_SC_EXPR_QUEUE,
PARSER_SC_EXPR_RT,
PARSER_SC_EXPR_SCTP_CHUNK,
PARSER_SC_EXPR_SOCKET,
+ PARSER_SC_EXPR_TH,
+ PARSER_SC_EXPR_UDP,
+ PARSER_SC_EXPR_UDPLITE,
+ PARSER_SC_STMT_DUP,
+ PARSER_SC_STMT_FWD,
PARSER_SC_STMT_LOG,
+ PARSER_SC_STMT_NAT,
+ PARSER_SC_STMT_REJECT,
+ PARSER_SC_STMT_SYNPROXY,
+ PARSER_SC_STMT_TPROXY,
+
+ __SC_MAX
};
struct mnl_socket;
diff --git a/include/payload.h b/include/payload.h
index 8bc3fb9a..08e45f7f 100644
--- a/include/payload.h
+++ b/include/payload.h
@@ -15,6 +15,8 @@ struct eval_ctx;
struct stmt;
extern int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
struct stmt **res);
+int payload_gen_inner_dependency(struct eval_ctx *ctx, const struct expr *expr,
+ struct stmt **res);
extern int payload_gen_icmp_dependency(struct eval_ctx *ctx,
const struct expr *expr,
struct stmt **res);
@@ -25,16 +27,14 @@ extern int exthdr_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
/**
* struct payload_dep_ctx - payload protocol dependency tracking
*
- * @pbase: protocol base of last dependency match
* @icmp_type: extra info for icmp(6) decoding
- * @pdep: last dependency match
* @prev: previous statement
+ * @pdeps: last dependency match per protocol layer
*/
struct payload_dep_ctx {
- enum proto_bases pbase:8;
- uint8_t icmp_type;
- struct stmt *pdep;
- struct stmt *prev;
+ uint8_t icmp_type;
+ struct stmt *prev;
+ struct stmt *pdeps[PROTO_BASE_MAX + 1];
};
extern bool payload_is_known(const struct expr *expr);
@@ -47,7 +47,10 @@ extern void payload_dependency_store(struct payload_dep_ctx *ctx,
enum proto_bases base);
extern bool payload_dependency_exists(const struct payload_dep_ctx *ctx,
enum proto_bases base);
-extern void payload_dependency_release(struct payload_dep_ctx *ctx);
+extern struct expr *payload_dependency_get(struct payload_dep_ctx *ctx,
+ enum proto_bases base);
+extern void payload_dependency_release(struct payload_dep_ctx *ctx,
+ enum proto_bases base);
extern void payload_dependency_kill(struct payload_dep_ctx *ctx,
struct expr *expr, unsigned int family);
extern void exthdr_dependency_kill(struct payload_dep_ctx *ctx,
@@ -66,4 +69,6 @@ extern void payload_expr_complete(struct expr *expr,
bool payload_expr_cmp(const struct expr *e1, const struct expr *e2);
+const struct proto_desc *find_proto_desc(const struct nftnl_udata *ud);
+
#endif /* NFTABLES_PAYLOAD_H */
diff --git a/include/proto.h b/include/proto.h
index 580e4090..9c98a0b7 100644
--- a/include/proto.h
+++ b/include/proto.h
@@ -18,6 +18,7 @@ enum proto_bases {
PROTO_BASE_LL_HDR,
PROTO_BASE_NETWORK_HDR,
PROTO_BASE_TRANSPORT_HDR,
+ PROTO_BASE_INNER_HDR,
__PROTO_BASE_MAX
};
#define PROTO_BASE_MAX (__PROTO_BASE_MAX - 1)
@@ -34,6 +35,8 @@ enum icmp_hdr_field_type {
PROTO_ICMP6_PPTR,
PROTO_ICMP6_ECHO,
PROTO_ICMP6_MGMQ,
+ PROTO_ICMP6_ADDRESS, /* neighbor solicit/advert, redirect and MLD */
+ PROTO_ICMP6_REDIRECT,
};
/**
@@ -95,6 +98,10 @@ enum proto_desc_id {
PROTO_DESC_ARP,
PROTO_DESC_VLAN,
PROTO_DESC_ETHER,
+ PROTO_DESC_VXLAN,
+ PROTO_DESC_GENEVE,
+ PROTO_DESC_GRE,
+ PROTO_DESC_GRETAP,
__PROTO_DESC_MAX
};
#define PROTO_DESC_MAX (__PROTO_DESC_MAX - 1)
@@ -130,7 +137,11 @@ struct proto_desc {
uint32_t filter;
} format;
unsigned int pseudohdr[PROTO_HDRS_MAX];
-
+ struct {
+ uint32_t hdrsize;
+ uint32_t flags;
+ enum nft_inner_type type;
+ } inner;
};
#define PROTO_LINK(__num, __desc) { .num = (__num), .desc = (__desc), }
@@ -184,6 +195,7 @@ extern const struct proto_desc *proto_dev_desc(uint16_t type);
struct proto_ctx {
unsigned int debug_mask;
uint8_t family;
+ bool inner;
union {
struct {
uint8_t type;
@@ -192,17 +204,18 @@ struct proto_ctx {
struct {
struct location location;
const struct proto_desc *desc;
- unsigned int offset;
struct {
struct location location;
const struct proto_desc *desc;
} protos[PROTO_CTX_NUM_PROTOS];
unsigned int num_protos;
} protocol[PROTO_BASE_MAX + 1];
+ const struct proto_desc *stacked_ll[PROTO_CTX_NUM_PROTOS];
+ uint8_t stacked_ll_count;
};
extern void proto_ctx_init(struct proto_ctx *ctx, unsigned int family,
- unsigned int debug_mask);
+ unsigned int debug_mask, bool inner);
extern void proto_ctx_update(struct proto_ctx *ctx, enum proto_bases base,
const struct location *loc,
const struct proto_desc *desc);
@@ -214,6 +227,8 @@ extern const struct proto_desc *proto_find_upper(const struct proto_desc *base,
unsigned int num);
extern int proto_find_num(const struct proto_desc *base,
const struct proto_desc *desc);
+const struct proto_desc *proto_find_inner(uint32_t type, uint32_t hdrsize,
+ uint32_t flags);
extern const struct proto_desc *proto_find_desc(enum proto_desc_id desc_id);
@@ -261,6 +276,7 @@ enum ip_hdr_fields {
IPHDR_SADDR,
IPHDR_DADDR,
};
+#define IPHDR_MAX IPHDR_DADDR
enum icmp_hdr_fields {
ICMPHDR_INVALID,
@@ -291,6 +307,8 @@ enum icmp6_hdr_fields {
ICMP6HDR_ID,
ICMP6HDR_SEQ,
ICMP6HDR_MAXDELAY,
+ ICMP6HDR_TADDR,
+ ICMP6HDR_DADDR,
};
enum ip6_hdr_fields {
@@ -374,6 +392,45 @@ enum th_hdr_fields {
THDR_DPORT,
};
+struct vxlanhdr {
+ uint32_t vx_flags;
+ uint32_t vx_vni;
+};
+
+enum vxlan_hdr_fields {
+ VXLANHDR_INVALID,
+ VXLANHDR_VNI,
+ VXLANHDR_FLAGS,
+};
+
+struct gnvhdr {
+ uint16_t flags;
+ uint16_t type;
+ uint32_t vni;
+};
+enum geneve_hdr_fields {
+ GNVHDR_INVALID,
+ GNVHDR_VNI,
+ GNVHDR_TYPE,
+};
+
+struct grehdr {
+ uint16_t flags;
+ uint16_t protocol;
+};
+
+enum gre_hdr_fields {
+ GREHDR_INVALID,
+ GREHDR_VERSION,
+ GREHDR_FLAGS,
+ GREHDR_PROTOCOL,
+};
+
+extern const struct proto_desc proto_vxlan;
+extern const struct proto_desc proto_geneve;
+extern const struct proto_desc proto_gre;
+extern const struct proto_desc proto_gretap;
+
extern const struct proto_desc proto_icmp;
extern const struct proto_desc proto_igmp;
extern const struct proto_desc proto_ah;
@@ -411,4 +468,7 @@ extern const struct datatype icmp6_type_type;
extern const struct datatype dscp_type;
extern const struct datatype ecn_type;
+struct eval_ctx;
+struct proto_ctx *eval_proto_ctx(struct eval_ctx *ctx);
+
#endif /* NFTABLES_PROTO_H */
diff --git a/include/rbtree.h b/include/rbtree.h
deleted file mode 100644
index ac65283f..00000000
--- a/include/rbtree.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Red Black Trees
- * (C) 1999 Andrea Arcangeli <andrea@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#ifndef NFTABLES_RBTREE_H
-#define NFTABLES_RBTREE_H
-
-#include <stddef.h>
-
-struct rb_node
-{
- unsigned long rb_parent_color;
-#define RB_RED 0
-#define RB_BLACK 1
- struct rb_node *rb_right;
- struct rb_node *rb_left;
-};
-
-struct rb_root
-{
- struct rb_node *rb_node;
-};
-
-#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3))
-#define rb_color(r) ((r)->rb_parent_color & 1)
-#define rb_is_red(r) (!rb_color(r))
-#define rb_is_black(r) rb_color(r)
-#define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0)
-#define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0)
-
-static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p)
-{
- rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p;
-}
-static inline void rb_set_color(struct rb_node *rb, int color)
-{
- rb->rb_parent_color = (rb->rb_parent_color & ~1) | color;
-}
-
-#define RB_ROOT (struct rb_root) { NULL, }
-#define rb_entry(ptr, type, member) container_of(ptr, type, member)
-
-#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL)
-#define RB_EMPTY_NODE(node) (rb_parent(node) == node)
-#define RB_CLEAR_NODE(node) (rb_set_parent(node, node))
-
-extern void rb_insert_color(struct rb_node *, struct rb_root *);
-extern void rb_erase(struct rb_node *, struct rb_root *);
-
-/* Find logical next and previous nodes in a tree */
-extern struct rb_node *rb_next(struct rb_node *);
-extern struct rb_node *rb_prev(struct rb_node *);
-extern struct rb_node *rb_first(struct rb_root *);
-extern struct rb_node *rb_last(struct rb_root *);
-
-/* Fast replacement of a single node without remove/rebalance/add/rebalance */
-extern void rb_replace_node(struct rb_node *victim, struct rb_node *new,
- struct rb_root *root);
-
-static inline void rb_link_node(struct rb_node * node, struct rb_node * parent,
- struct rb_node ** rb_link)
-{
- node->rb_parent_color = (unsigned long )parent;
- node->rb_left = node->rb_right = NULL;
-
- *rb_link = node;
-}
-
-#define rb_for_each_entry(pos, root, member) \
- for ((pos) = (root)->rb_node ? \
- rb_entry(rb_first(root), typeof(*pos), member) : NULL; \
- (pos) != NULL; \
- (pos) = rb_entry(rb_next(&(pos)->member), typeof(*pos), member))
-
-#define rb_for_each_entry_safe(pos, node, next, root, member) \
- for ((node) = rb_first(root); \
- (pos) = (node) ? rb_entry((node), typeof(*pos), member) : NULL, \
- (next) = (node) ? rb_next(node) : NULL, \
- (pos) != NULL; \
- (node) = (next))
-
-#endif /* NFTABLES_RBTREE_H */
diff --git a/include/rule.h b/include/rule.h
index be316956..3a833cf3 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -1,13 +1,11 @@
#ifndef NFTABLES_RULE_H
#define NFTABLES_RULE_H
-#include <stdint.h>
#include <nftables.h>
#include <list.h>
#include <netinet/in.h>
#include <libnftnl/object.h> /* For NFTNL_CTTIMEOUT_ARRAY_MAX. */
#include <linux/netfilter/nf_tables.h>
-#include <string.h>
#include <cache.h>
/**
@@ -169,6 +167,7 @@ struct table {
unsigned int refcnt;
uint32_t owner;
const char *comment;
+ bool has_xt_stmts;
};
extern struct table *table_alloc(void);
@@ -260,7 +259,7 @@ struct chain {
extern int std_prio_lookup(const char *std_prio_name, int family, int hook);
extern const char *chain_type_name_lookup(const char *name);
extern const char *chain_hookname_lookup(const char *name);
-extern struct chain *chain_alloc(const char *name);
+extern struct chain *chain_alloc(void);
extern struct chain *chain_get(struct chain *chain);
extern void chain_free(struct chain *chain);
extern struct chain *chain_lookup_fuzzy(const struct handle *h,
@@ -311,7 +310,6 @@ void rule_stmt_append(struct rule *rule, struct stmt *stmt);
void rule_stmt_insert_at(struct rule *rule, struct stmt *nstmt,
struct stmt *stmt);
-
/**
* struct set - nftables set
*
@@ -325,11 +323,13 @@ void rule_stmt_insert_at(struct rule *rule, struct stmt *nstmt,
* @key: key expression (data type, length))
* @data: mapping data expression
* @objtype: mapping object type
+ * @existing_set: reference to existing set in the kernel
* @init: initializer
* @rg_cache: cached range element (left)
* @policy: set mechanism policy
* @automerge: merge adjacents and overlapping elements, if possible
* @comment: comment
+ * @errors: expr evaluation errors seen
* @desc.size: count of set elements
* @desc.field_len: length of single concatenated fields, bytes
* @desc.field_count: count of concatenated fields
@@ -346,6 +346,7 @@ struct set {
struct expr *key;
struct expr *data;
uint32_t objtype;
+ struct set *existing_set;
struct expr *init;
struct expr *rg_cache;
uint32_t policy;
@@ -353,6 +354,7 @@ struct set {
bool root;
bool automerge;
bool key_typeof_valid;
+ bool errors;
const char *comment;
struct {
uint32_t size;
@@ -409,6 +411,11 @@ static inline bool set_is_meter(uint32_t set_flags)
return set_is_anonymous(set_flags) && (set_flags & NFT_SET_EVAL);
}
+static inline bool set_is_meter_compat(uint32_t set_flags)
+{
+ return set_flags & NFT_SET_EVAL;
+}
+
static inline bool set_is_interval(uint32_t set_flags)
{
return set_flags & NFT_SET_INTERVAL;
@@ -517,7 +524,7 @@ struct obj *obj_lookup_fuzzy(const char *obj_name,
void obj_print(const struct obj *n, struct output_ctx *octx);
void obj_print_plain(const struct obj *obj, struct output_ctx *octx);
const char *obj_type_name(uint32_t type);
-uint32_t obj_type_to_cmd(uint32_t type);
+enum cmd_obj obj_type_to_cmd(uint32_t type);
struct flowtable {
struct list_head list;
@@ -561,6 +568,7 @@ void flowtable_print(const struct flowtable *n, struct output_ctx *octx);
* @CMD_EXPORT: export the ruleset in a given format
* @CMD_MONITOR: event listener
* @CMD_DESCRIBE: describe an expression
+ * @CMD_DESTROY: destroy object
*/
enum cmd_ops {
CMD_INVALID,
@@ -578,6 +586,7 @@ enum cmd_ops {
CMD_EXPORT,
CMD_MONITOR,
CMD_DESCRIBE,
+ CMD_DESTROY,
};
/**
@@ -618,6 +627,7 @@ enum cmd_obj {
CMD_OBJ_SETELEMS,
CMD_OBJ_SETS,
CMD_OBJ_RULE,
+ CMD_OBJ_RULES,
CMD_OBJ_CHAIN,
CMD_OBJ_CHAINS,
CMD_OBJ_TABLE,
@@ -640,9 +650,11 @@ enum cmd_obj {
CMD_OBJ_FLOWTABLE,
CMD_OBJ_FLOWTABLES,
CMD_OBJ_CT_TIMEOUT,
+ CMD_OBJ_CT_TIMEOUTS,
CMD_OBJ_SECMARK,
CMD_OBJ_SECMARKS,
CMD_OBJ_CT_EXPECT,
+ CMD_OBJ_CT_EXPECTATIONS,
CMD_OBJ_SYNPROXY,
CMD_OBJ_SYNPROXYS,
CMD_OBJ_HOOKS,
@@ -680,6 +692,11 @@ void monitor_free(struct monitor *m);
#define NFT_NLATTR_LOC_MAX 32
+struct nlerr_loc {
+ uint16_t offset;
+ const struct location *location;
+};
+
/**
* struct cmd - command statement
*
@@ -699,6 +716,7 @@ struct cmd {
enum cmd_obj obj;
struct handle handle;
uint32_t seqnum;
+ struct list_head collapse_list;
union {
void *data;
struct expr *expr;
@@ -715,25 +733,20 @@ struct cmd {
struct markup *markup;
struct obj *object;
};
- struct {
- uint16_t offset;
- const struct location *location;
- } attr[NFT_NLATTR_LOC_MAX];
- int num_attrs;
+ struct nlerr_loc *attr;
+ uint32_t attr_array_len;
+ uint32_t num_attrs;
const void *arg;
};
extern struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
const struct handle *h, const struct location *loc,
void *data);
-extern void nft_cmd_expand(struct cmd *cmd);
extern struct cmd *cmd_alloc_obj_ct(enum cmd_ops op, int type,
const struct handle *h,
const struct location *loc, struct obj *obj);
extern void cmd_free(struct cmd *cmd);
-void cmd_add_loc(struct cmd *cmd, uint16_t offset, const struct location *loc);
-
#include <payload.h>
#include <expression.h>
@@ -747,10 +760,13 @@ void cmd_add_loc(struct cmd *cmd, uint16_t offset, const struct location *loc);
* @rule: current rule
* @set: current set
* @stmt: current statement
+ * @stmt_len: current statement template length
+ * @recursion: expr evaluation recursion counter
* @cache: cache context
* @debug_mask: debugging bitmask
* @ectx: expression context
- * @pctx: payload context
+ * @_pctx: payload contexts
+ * @inner_desc: inner header description
*/
struct eval_ctx {
struct nft_ctx *nft;
@@ -760,8 +776,11 @@ struct eval_ctx {
struct rule *rule;
struct set *set;
struct stmt *stmt;
+ uint32_t stmt_len;
+ uint32_t recursion;
struct expr_ctx ectx;
- struct proto_ctx pctx;
+ struct proto_ctx _pctx[2];
+ const struct proto_desc *inner_desc;
};
extern int cmd_evaluate(struct eval_ctx *ctx, struct cmd *cmd);
@@ -777,7 +796,7 @@ struct timeout_protocol {
uint32_t *dflt_timeout;
};
-extern struct timeout_protocol timeout_protocol[IPPROTO_MAX];
+extern struct timeout_protocol timeout_protocol[UINT8_MAX + 1];
extern int timeout_str2num(uint16_t l4proto, struct timeout_state *ts);
#endif /* NFTABLES_RULE_H */
diff --git a/include/statement.h b/include/statement.h
index 06221040..662f99dd 100644
--- a/include/statement.h
+++ b/include/statement.h
@@ -47,6 +47,13 @@ struct counter_stmt {
extern struct stmt *counter_stmt_alloc(const struct location *loc);
+struct last_stmt {
+ uint64_t used;
+ uint32_t set;
+};
+
+extern struct stmt *last_stmt_alloc(const struct location *loc);
+
struct exthdr_stmt {
struct expr *expr;
struct expr *val;
@@ -145,6 +152,12 @@ struct nat_stmt {
extern struct stmt *nat_stmt_alloc(const struct location *loc,
enum nft_nat_etypes type);
+struct optstrip_stmt {
+ struct expr *expr;
+};
+
+extern struct stmt *optstrip_stmt_alloc(const struct location *loc, struct expr *e);
+
struct tproxy_stmt {
struct expr *addr;
struct expr *port;
@@ -249,8 +262,8 @@ enum nft_xt_type {
NFT_XT_MATCH = 0,
NFT_XT_TARGET,
NFT_XT_WATCHER,
- NFT_XT_MAX
};
+#define NFT_XT_MAX (NFT_XT_WATCHER + 1)
struct xtables_match;
struct xtables_target;
@@ -258,12 +271,11 @@ struct xtables_target;
struct xt_stmt {
const char *name;
enum nft_xt_type type;
+ uint32_t rev;
+ uint32_t family;
+ size_t infolen;
+ void *info;
uint32_t proto;
- union {
- struct xtables_match *match;
- struct xtables_target *target;
- };
- void *entry;
};
extern struct stmt *xt_stmt_alloc(const struct location *loc);
@@ -297,6 +309,8 @@ extern struct stmt *xt_stmt_alloc(const struct location *loc);
* @STMT_MAP: map statement
* @STMT_SYNPROXY: synproxy statement
* @STMT_CHAIN: chain statement
+ * @STMT_OPTSTRIP: optstrip statement
+ * @STMT_LAST: last statement
*/
enum stmt_types {
STMT_INVALID,
@@ -326,6 +340,8 @@ enum stmt_types {
STMT_MAP,
STMT_SYNPROXY,
STMT_CHAIN,
+ STMT_OPTSTRIP,
+ STMT_LAST,
};
/**
@@ -375,11 +391,13 @@ struct stmt {
struct counter_stmt counter;
struct payload_stmt payload;
struct meta_stmt meta;
+ struct last_stmt last;
struct log_stmt log;
struct limit_stmt limit;
struct reject_stmt reject;
struct nat_stmt nat;
struct tproxy_stmt tproxy;
+ struct optstrip_stmt optstrip;
struct queue_stmt queue;
struct quota_stmt quota;
struct ct_stmt ct;
@@ -398,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/include/tcpopt.h b/include/tcpopt.h
index 667c8a77..3a0b8424 100644
--- a/include/tcpopt.h
+++ b/include/tcpopt.h
@@ -12,8 +12,8 @@ extern void tcpopt_init_raw(struct expr *expr, uint8_t type,
unsigned int offset, unsigned int len,
uint32_t flags);
-extern bool tcpopt_find_template(struct expr *expr, const struct expr *mask,
- unsigned int *shift);
+extern bool tcpopt_find_template(struct expr *expr, unsigned int offset,
+ unsigned int len);
/* TCP option numbers used on wire */
enum tcpopt_kind {
@@ -25,6 +25,9 @@ enum tcpopt_kind {
TCPOPT_KIND_SACK = 5,
TCPOPT_KIND_TIMESTAMP = 8,
TCPOPT_KIND_ECHO = 8,
+ TCPOPT_KIND_MD5SIG = 19,
+ TCPOPT_KIND_MPTCP = 30,
+ TCPOPT_KIND_FASTOPEN = 34,
__TCPOPT_KIND_MAX,
/* extra oob info, internal to nft */
@@ -71,6 +74,12 @@ enum tcpopt_hdr_field_sack {
TCPOPT_SACK_RIGHT3,
};
+enum tcpopt_hdr_mptcp_common {
+ TCPOPT_MPTCP_KIND,
+ TCPOPT_MPTCP_LENGTH,
+ TCPOPT_MPTCP_SUBTYPE,
+};
+
extern const struct exthdr_desc *tcpopt_protocols[__TCPOPT_KIND_MAX];
#endif /* NFTABLES_TCPOPT_H */
diff --git a/include/utils.h b/include/utils.h
index ffbe2cbb..e18fabec 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -2,8 +2,6 @@
#define NFTABLES_UTILS_H
#include <asm/byteorder.h>
-#include <stdint.h>
-#include <stdbool.h>
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
@@ -11,7 +9,6 @@
#include <list.h>
#include <gmputil.h>
-#include "config.h"
#ifdef HAVE_VISIBILITY_HIDDEN
# define __visible __attribute__((visibility("default")))
# define EXPORT_SYMBOL(x) typeof(x) (x) __visible;
@@ -39,7 +36,7 @@
#define __must_check __attribute__((warn_unused_result))
#define __noreturn __attribute__((__noreturn__))
-#define BUG(fmt, arg...) ({ fprintf(stderr, "BUG: " fmt, ##arg); assert(0); })
+#define BUG(fmt, arg...) ({ fprintf(stderr, "BUG: " fmt, ##arg); assert(0); abort(); })
#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
#define BUILD_BUG_ON_ZERO(e) (sizeof(char[1 - 2 * !!(e)]) - 1)
@@ -75,15 +72,32 @@
#define max(_x, _y) ({ \
_x > _y ? _x : _y; })
-#define SNPRINTF_BUFFER_SIZE(ret, size, len, offset) \
- if (ret < 0) \
- abort(); \
- offset += ret; \
- assert(ret < len); \
- if (ret > len) \
- ret = len; \
- size += ret; \
- len -= ret;
+#define SNPRINTF_BUFFER_SIZE(ret, len, offset) \
+ ({ \
+ const int _ret = (ret); \
+ size_t *const _len = (len); \
+ size_t *const _offset = (offset); \
+ bool _not_truncated = true; \
+ size_t _ret2; \
+ \
+ assert(_ret >= 0); \
+ \
+ if ((size_t) _ret >= *_len) { \
+ /* Truncated.
+ *
+ * We will leave "len" at zero and increment
+ * "offset" to point one byte after the buffer
+ * (after the terminating NUL byte). */ \
+ _ret2 = *_len; \
+ _not_truncated = false; \
+ } else \
+ _ret2 = (size_t) _ret; \
+ \
+ *_offset += _ret2; \
+ *_len -= _ret2; \
+ \
+ _not_truncated; \
+ })
#define MSEC_PER_SEC 1000L
@@ -128,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);
@@ -136,5 +149,6 @@ extern void *xzalloc(size_t size);
extern void *xzalloc_array(size_t nmemb, size_t size);
extern char *xstrdup(const char *s);
extern void xstrunescape(const char *in, char *out);
+extern int round_pow_2(unsigned int value);
#endif /* NFTABLES_UTILS_H */
diff --git a/py/Makefile.am b/py/Makefile.am
deleted file mode 100644
index 215ecd9e..00000000
--- a/py/Makefile.am
+++ /dev/null
@@ -1,28 +0,0 @@
-EXTRA_DIST = setup.py __init__.py nftables.py schema.json
-
-all-local:
- cd $(srcdir) && \
- $(PYTHON_BIN) setup.py build --build-base $(abs_builddir)
-
-install-exec-local:
- cd $(srcdir) && \
- $(PYTHON_BIN) setup.py build --build-base $(abs_builddir) \
- install --prefix $(DESTDIR)$(prefix)
-
-uninstall-local:
- rm -rf $(DESTDIR)$(prefix)/lib*/python*/site-packages/nftables
- rm -rf $(DESTDIR)$(prefix)/lib*/python*/dist-packages/nftables
- rm -rf $(DESTDIR)$(prefix)/lib*/python*/site-packages/nftables-[0-9]*.egg-info
- rm -rf $(DESTDIR)$(prefix)/lib*/python*/dist-packages/nftables-[0-9]*.egg-info
- rm -rf $(DESTDIR)$(prefix)/lib*/python*/site-packages/nftables-[0-9]*.egg
- rm -rf $(DESTDIR)$(prefix)/lib*/python*/dist-packages/nftables-[0-9]*.egg
-
-clean-local:
- cd $(srcdir) && \
- $(PYTHON_BIN) setup.py clean \
- --build-base $(abs_builddir)
- rm -rf scripts-* lib* build dist bdist.* nftables.egg-info
- find . -name \*.pyc -delete
-
-distclean-local:
- rm -f version
diff --git a/py/pyproject.toml b/py/pyproject.toml
new file mode 100644
index 00000000..fed528d4
--- /dev/null
+++ b/py/pyproject.toml
@@ -0,0 +1,3 @@
+[build-system]
+requires = ["setuptools"]
+build-backend = "setuptools.build_meta"
diff --git a/py/setup.cfg b/py/setup.cfg
new file mode 100644
index 00000000..953b7f41
--- /dev/null
+++ b/py/setup.cfg
@@ -0,0 +1,24 @@
+[metadata]
+name = nftables
+version = attr: nftables.NFTABLES_VERSION
+description = Libnftables binding
+author = Netfilter project
+author_email = coreteam@netfilter.org
+url = https://netfilter.org/projects/nftables/index.html
+provides = nftables
+classifiers =
+ Development Status :: 4 - Beta
+ Environment :: Console
+ Intended Audience :: Developers
+ License :: OSI Approved :: GNU General Public License v2 (GPLv2)
+ Operating System :: POSIX :: Linux
+ Programming Language :: Python
+ Topic :: System :: Networking :: Firewalls
+
+[options]
+packages = nftables
+package_dir =
+ nftables = src
+
+[options.package_data]
+nftables = schema.json
diff --git a/py/setup.py b/py/setup.py
index 72fc8fd9..beda28e8 100755
--- a/py/setup.py
+++ b/py/setup.py
@@ -1,24 +1,5 @@
#!/usr/bin/env python
-from distutils.core import setup
-from nftables import NFTABLES_VERSION
-setup(name='nftables',
- version=NFTABLES_VERSION,
- description='Libnftables binding',
- author='Netfilter project',
- author_email='coreteam@netfilter.org',
- url='https://netfilter.org/projects/nftables/index.html',
- packages=['nftables'],
- provides=['nftables'],
- package_dir={'nftables':'.'},
- package_data={'nftables':['schema.json']},
- classifiers=[
- 'Development Status :: 4 - Beta',
- 'Environment :: Console',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: GNU General Public License v2 (GPLv2)',
- 'Operating System :: POSIX :: Linux',
- 'Programming Language :: Python',
- 'Topic :: System :: Networking :: Firewalls',
- ],
- )
+from setuptools import setup
+
+setup()
diff --git a/py/__init__.py b/py/src/__init__.py
index 7567f095..7567f095 100644
--- a/py/__init__.py
+++ b/py/src/__init__.py
diff --git a/py/nftables.py b/py/src/nftables.py
index 2a0a1e89..f1e43ade 100644
--- a/py/nftables.py
+++ b/py/src/nftables.py
@@ -37,6 +37,11 @@ class SchemaValidator:
class Nftables:
"""A class representing libnftables interface"""
+ input_flags = {
+ "no-dns": 0x1,
+ "json": 0x2,
+ }
+
debug_flags = {
"scanner": 0x1,
"parser": 0x2,
@@ -74,6 +79,8 @@ class Nftables:
is requested from the library and buffering of output and error streams
is turned on.
"""
+ self.__ctx = None
+
lib = cdll.LoadLibrary(sofile)
### API function definitions
@@ -82,6 +89,14 @@ class Nftables:
self.nft_ctx_new.restype = c_void_p
self.nft_ctx_new.argtypes = [c_int]
+ self.nft_ctx_input_get_flags = lib.nft_ctx_input_get_flags
+ self.nft_ctx_input_get_flags.restype = c_uint
+ self.nft_ctx_input_get_flags.argtypes = [c_void_p]
+
+ self.nft_ctx_input_set_flags = lib.nft_ctx_input_set_flags
+ self.nft_ctx_input_set_flags.restype = c_uint
+ self.nft_ctx_input_set_flags.argtypes = [c_void_p, c_uint]
+
self.nft_ctx_output_get_flags = lib.nft_ctx_output_get_flags
self.nft_ctx_output_get_flags.restype = c_uint
self.nft_ctx_output_get_flags.argtypes = [c_void_p]
@@ -116,6 +131,31 @@ class Nftables:
self.nft_run_cmd_from_buffer.restype = c_int
self.nft_run_cmd_from_buffer.argtypes = [c_void_p, c_char_p]
+ self.nft_run_cmd_from_filename = lib.nft_run_cmd_from_filename
+ self.nft_run_cmd_from_filename.restype = c_int
+ self.nft_run_cmd_from_filename.argtypes = [c_void_p, c_char_p]
+
+ self.nft_ctx_add_include_path = lib.nft_ctx_add_include_path
+ self.nft_ctx_add_include_path.restype = c_int
+ self.nft_ctx_add_include_path.argtypes = [c_void_p, c_char_p]
+
+ self.nft_ctx_clear_include_paths = lib.nft_ctx_clear_include_paths
+ self.nft_ctx_clear_include_paths.argtypes = [c_void_p]
+
+ self.nft_ctx_get_dry_run = lib.nft_ctx_get_dry_run
+ self.nft_ctx_get_dry_run.restype = c_bool
+ self.nft_ctx_get_dry_run.argtypes = [c_void_p]
+
+ self.nft_ctx_set_dry_run = lib.nft_ctx_set_dry_run
+ self.nft_ctx_set_dry_run.argtypes = [c_void_p, c_bool]
+
+ self.nft_ctx_add_var = lib.nft_ctx_add_var
+ self.nft_ctx_add_var.restype = c_int
+ self.nft_ctx_add_var.argtypes = [c_void_p, c_char_p]
+
+ self.nft_ctx_clear_vars = lib.nft_ctx_clear_vars
+ self.nft_ctx_clear_vars.argtypes = [c_void_p]
+
self.nft_ctx_free = lib.nft_ctx_free
lib.nft_ctx_free.argtypes = [c_void_p]
@@ -125,11 +165,72 @@ class Nftables:
self.nft_ctx_buffer_error(self.__ctx)
def __del__(self):
- self.nft_ctx_free(self.__ctx)
+ if self.__ctx is not None:
+ self.nft_ctx_free(self.__ctx)
+ self.__ctx = None
+
+ def _flags_from_numeric(self, flags_dict, val):
+ names = []
+ for n, v in flags_dict.items():
+ if val & v:
+ names.append(n)
+ val &= ~v
+ if val:
+ names.append(val)
+ return names
+
+ def _flags_to_numeric(self, flags_dict, values):
+ if isinstance(values, (str, int)):
+ values = (values,)
+
+ val = 0
+ for v in values:
+ if isinstance(v, str):
+ v = flags_dict.get(v)
+ if v is None:
+ raise ValueError("Invalid argument")
+ elif isinstance(v, int):
+ if v < 0 or v > 0xFFFFFFFF:
+ raise ValueError("Invalid argument")
+ else:
+ raise TypeError("Not a valid flag")
+ val |= v
+
+ return val
+
+ def get_input_flags(self):
+ """Get currently active input flags.
+
+ Returns a set of flag names. See set_input_flags() for details.
+ """
+ val = self.nft_ctx_input_get_flags(self.__ctx)
+ return self._flags_from_numeric(self.input_flags, val)
+
+ def set_input_flags(self, values):
+ """Set input flags.
+
+ Resets all input flags to values. Accepts either a single flag or a list
+ of flags. Each flag might be given either as string or integer value as
+ shown in the following table:
+
+ Name | Value (hex)
+ -----------------------
+ "no-dns" | 0x1
+ "json" | 0x2
+
+ "no-dns" disables blocking address lookup.
+ "json" enables JSON mode for input.
+
+ Returns a set of previously active input flags, as returned by
+ get_input_flags() method.
+ """
+ val = self._flags_to_numeric(self.input_flags, values)
+ old = self.nft_ctx_input_set_flags(self.__ctx, val)
+ return self._flags_from_numeric(self.input_flags, old)
def __get_output_flag(self, name):
flag = self.output_flags[name]
- return self.nft_ctx_output_get_flags(self.__ctx) & flag
+ return (self.nft_ctx_output_get_flags(self.__ctx) & flag) != 0
def __set_output_flag(self, name, val):
flag = self.output_flags[name]
@@ -139,7 +240,7 @@ class Nftables:
else:
new_flags = flags & ~flag
self.nft_ctx_output_set_flags(self.__ctx, new_flags)
- return flags & flag
+ return (flags & flag) != 0
def get_reversedns_output(self):
"""Get the current state of reverse DNS output.
@@ -346,16 +447,7 @@ class Nftables:
Returns a set of flag names. See set_debug() for details.
"""
val = self.nft_ctx_output_get_debug(self.__ctx)
-
- names = []
- for n,v in self.debug_flags.items():
- if val & v:
- names.append(n)
- val &= ~v
- if val:
- names.append(val)
-
- return names
+ return self._flags_from_numeric(self.debug_flags, val)
def set_debug(self, values):
"""Set debug output flags.
@@ -377,19 +469,9 @@ class Nftables:
Returns a set of previously active debug flags, as returned by
get_debug() method.
"""
+ val = self._flags_to_numeric(self.debug_flags, values)
old = self.get_debug()
-
- if type(values) in [str, int]:
- values = [values]
-
- val = 0
- for v in values:
- if type(v) is str:
- v = self.debug_flags[v]
- val |= v
-
self.nft_ctx_output_set_debug(self.__ctx, val)
-
return old
def cmd(self, cmdline):
@@ -446,3 +528,77 @@ class Nftables:
self.validator.validate(json_root)
return True
+
+ def cmd_from_file(self, filename):
+ """Run a nftables command set from a file
+
+ filename can be a str or a Path
+
+ Returns a tuple (rc, output, error):
+ rc -- return code as returned by nft_run_cmd_from_filename() function
+ output -- a string containing output written to stdout
+ error -- a string containing output written to stderr
+ """
+ filename_is_unicode = False
+ if not isinstance(filename, bytes):
+ filename_is_unicode = True
+ filename = str(filename)
+ filename= filename.encode("utf-8")
+ rc = self.nft_run_cmd_from_filename(self.__ctx, filename)
+ output = self.nft_ctx_get_output_buffer(self.__ctx)
+ error = self.nft_ctx_get_error_buffer(self.__ctx)
+ if filename_is_unicode:
+ output = output.decode("utf-8")
+ error = error.decode("utf-8")
+ return (rc, output, error)
+
+ def add_include_path(self, filename):
+ """Add a path to the include file list
+ The default list includes the built-in default one
+
+ Returns True on success, False if memory allocation fails
+ """
+ if not isinstance(filename, bytes):
+ filename = str(filename)
+ filename= filename.encode("utf-8")
+ rc = self.nft_ctx_add_include_path(self.__ctx, filename)
+ return rc == 0
+
+ def clear_include_paths(self):
+ """Clear include path list
+
+ Will also remove the built-in default one
+ """
+ self.nft_ctx_clear_include_paths(self.__ctx)
+
+ def get_dry_run(self):
+ """Get dry run state
+
+ Returns True if set, False otherwise
+ """
+ return self.nft_ctx_get_dry_run(self.__ctx)
+
+ def set_dry_run(self, onoff):
+ """ Set dry run state
+
+ Returns the previous dry run state
+ """
+ old = self.get_dry_run()
+ self.nft_ctx_set_dry_run(self.__ctx, onoff)
+
+ return old
+
+ def add_var(self, var):
+ """Add a variable to the variable list
+
+ Returns True if added, False otherwise
+ """
+ if not isinstance(var, bytes):
+ var = var.encode("utf-8")
+ rc = self.nft_ctx_add_var(self.__ctx, var)
+ return rc == 0
+
+ def clear_vars(self):
+ """Clear variable list
+ """
+ self.nft_ctx_clear_vars(self.__ctx)
diff --git a/py/schema.json b/py/src/schema.json
index 460e2156..460e2156 100644
--- a/py/schema.json
+++ b/py/src/schema.json
diff --git a/src/Makefile.am b/src/Makefile.am
deleted file mode 100644
index 01c12c81..00000000
--- a/src/Makefile.am
+++ /dev/null
@@ -1,120 +0,0 @@
-include $(top_srcdir)/Make_global.am
-
-sbin_PROGRAMS = nft
-
-CLEANFILES = scanner.c parser_bison.c
-
-AM_CPPFLAGS = -I$(top_srcdir)/include
-AM_CPPFLAGS += -DDEFAULT_INCLUDE_PATH="\"${sysconfdir}\"" \
- ${LIBMNL_CFLAGS} ${LIBNFTNL_CFLAGS}
-if BUILD_DEBUG
-AM_CPPFLAGS += -g -DDEBUG
-endif
-if BUILD_XTABLES
-AM_CPPFLAGS += ${XTABLES_CFLAGS}
-endif
-if BUILD_MINIGMP
-AM_CPPFLAGS += -DHAVE_MINIGMP
-endif
-if BUILD_JSON
-AM_CPPFLAGS += -DHAVE_JSON
-endif
-if BUILD_XTABLES
-AM_CPPFLAGS += -DHAVE_XTABLES
-endif
-
-AM_CFLAGS = -Wall \
- -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations \
- -Wdeclaration-after-statement -Wsign-compare -Winit-self \
- -Wformat-nonliteral -Wformat-security -Wmissing-format-attribute \
- -Wcast-align -Wundef -Wbad-function-cast \
- -Waggregate-return -Wunused -Wwrite-strings ${GCC_FVISIBILITY_HIDDEN}
-
-
-AM_YFLAGS = -d -Wno-yacc
-
-BUILT_SOURCES = parser_bison.h
-
-lib_LTLIBRARIES = libnftables.la
-
-libnftables_la_SOURCES = \
- rule.c \
- statement.c \
- cache.c \
- cmd.c \
- datatype.c \
- expression.c \
- evaluate.c \
- proto.c \
- payload.c \
- exthdr.c \
- fib.c \
- hash.c \
- ipopt.c \
- meta.c \
- rt.c \
- numgen.c \
- ct.c \
- xfrm.c \
- netlink.c \
- netlink_linearize.c \
- netlink_delinearize.c \
- misspell.c \
- monitor.c \
- owner.c \
- segtree.c \
- rbtree.c \
- gmputil.c \
- utils.c \
- erec.c \
- mnl.c \
- iface.c \
- mergesort.c \
- osf.c \
- nfnl_osf.c \
- tcpopt.c \
- socket.c \
- print.c \
- sctp_chunk.c \
- libnftables.c \
- libnftables.map
-
-# yacc and lex generate dirty code
-noinst_LTLIBRARIES = libparser.la
-libparser_la_SOURCES = parser_bison.y scanner.l
-libparser_la_CFLAGS = ${AM_CFLAGS} \
- -Wno-missing-prototypes \
- -Wno-missing-declarations \
- -Wno-implicit-function-declaration \
- -Wno-nested-externs \
- -Wno-undef \
- -Wno-redundant-decls
-
-libnftables_la_LIBADD = ${LIBMNL_LIBS} ${LIBNFTNL_LIBS} libparser.la
-libnftables_la_LDFLAGS = -version-info ${libnftables_LIBVERSION} \
- --version-script=$(srcdir)/libnftables.map
-
-if BUILD_MINIGMP
-noinst_LTLIBRARIES += libminigmp.la
-libminigmp_la_SOURCES = mini-gmp.c
-libminigmp_la_CFLAGS = ${AM_CFLAGS} -Wno-sign-compare
-libnftables_la_LIBADD += libminigmp.la
-endif
-
-libnftables_la_SOURCES += xt.c
-if BUILD_XTABLES
-libnftables_la_LIBADD += ${XTABLES_LIBS}
-endif
-
-nft_SOURCES = main.c
-
-if BUILD_CLI
-nft_SOURCES += cli.c
-endif
-
-if BUILD_JSON
-libnftables_la_SOURCES += json.c parser_json.c
-libnftables_la_LIBADD += ${JANSSON_LIBS}
-endif
-
-nft_LDADD = libnftables.la
diff --git a/src/cache.c b/src/cache.c
index 0cddd1e1..c000e32c 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -6,6 +6,8 @@
* later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <expression.h>
#include <statement.h>
#include <rule.h>
@@ -16,9 +18,12 @@
#include <mnl.h>
#include <libnftnl/chain.h>
#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags)
{
+ struct set *set;
+
switch (cmd->obj) {
case CMD_OBJ_TABLE:
if (!cmd->table)
@@ -29,6 +34,10 @@ static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags)
NFT_CACHE_SET |
NFT_CACHE_OBJECT |
NFT_CACHE_FLOWTABLE;
+ list_for_each_entry(set, &cmd->table->sets, list) {
+ if (set->automerge)
+ flags |= NFT_CACHE_SETELEM_MAYBE;
+ }
break;
case CMD_OBJ_CHAIN:
case CMD_OBJ_SET:
@@ -96,13 +105,74 @@ static unsigned int evaluate_cache_get(struct cmd *cmd, unsigned int flags)
return flags;
}
-static unsigned int evaluate_cache_flush(struct cmd *cmd, unsigned int flags)
+struct nft_cache_filter *nft_cache_filter_init(void)
+{
+ struct nft_cache_filter *filter;
+ int i;
+
+ filter = xzalloc(sizeof(struct nft_cache_filter));
+ memset(&filter->list, 0, sizeof(filter->list));
+ for (i = 0; i < NFT_CACHE_HSIZE; i++)
+ init_list_head(&filter->obj[i].head);
+
+ return filter;
+}
+
+void nft_cache_filter_fini(struct nft_cache_filter *filter)
+{
+ int i;
+
+ for (i = 0; i < NFT_CACHE_HSIZE; i++) {
+ struct nft_filter_obj *obj, *next;
+
+ list_for_each_entry_safe(obj, next, &filter->obj[i].head, list)
+ free(obj);
+ }
+ free(filter);
+}
+
+static void cache_filter_add(struct nft_cache_filter *filter,
+ const struct cmd *cmd)
+{
+ struct nft_filter_obj *obj;
+ uint32_t hash;
+
+ obj = xmalloc(sizeof(struct nft_filter_obj));
+ obj->family = cmd->handle.family;
+ obj->table = cmd->handle.table.name;
+ obj->set = cmd->handle.set.name;
+
+ hash = djb_hash(cmd->handle.set.name) % NFT_CACHE_HSIZE;
+ list_add_tail(&obj->list, &filter->obj[hash].head);
+}
+
+static bool cache_filter_find(const struct nft_cache_filter *filter,
+ const struct handle *handle)
+{
+ struct nft_filter_obj *obj;
+ uint32_t hash;
+
+ hash = djb_hash(handle->set.name) % NFT_CACHE_HSIZE;
+
+ list_for_each_entry(obj, &filter->obj[hash].head, list) {
+ if (obj->family == handle->family &&
+ !strcmp(obj->table, handle->table.name) &&
+ !strcmp(obj->set, handle->set.name))
+ return true;
+ }
+
+ return false;
+}
+
+static unsigned int evaluate_cache_flush(struct cmd *cmd, unsigned int flags,
+ struct nft_cache_filter *filter)
{
switch (cmd->obj) {
case CMD_OBJ_SET:
case CMD_OBJ_MAP:
case CMD_OBJ_METER:
flags |= NFT_CACHE_SET;
+ cache_filter_add(filter, cmd);
break;
case CMD_OBJ_RULESET:
flags |= NFT_CACHE_FLUSHED;
@@ -133,20 +203,36 @@ 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)
- filter->table = 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;
+ break;
+ case CMD_OBJ_CHAIN:
+ if (filter && cmd->handle.chain.name) {
+ filter->list.family = cmd->handle.family;
+ filter->list.table = cmd->handle.table.name;
+ filter->list.chain = cmd->handle.chain.name;
+ /* implicit terse listing to fetch content of anonymous
+ * sets only when chain name is specified.
+ */
+ flags |= NFT_CACHE_TERSE;
+ }
flags |= NFT_CACHE_FULL;
break;
case CMD_OBJ_SET:
case CMD_OBJ_MAP:
if (filter && cmd->handle.table.name && cmd->handle.set.name) {
- filter->table = cmd->handle.table.name;
- filter->set = cmd->handle.set.name;
+ filter->list.family = cmd->handle.family;
+ filter->list.table = cmd->handle.table.name;
+ filter->list.set = cmd->handle.set.name;
}
- if (nft_output_terse(&nft->output))
- flags |= (NFT_CACHE_FULL & ~NFT_CACHE_SETELEM_BIT);
- else if (filter->table && filter->set)
+ if (filter->list.table && filter->list.set)
flags |= NFT_CACHE_TABLE | NFT_CACHE_SET | NFT_CACHE_SETELEM;
else
flags |= NFT_CACHE_FULL;
@@ -157,34 +243,182 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
case CMD_OBJ_SETS:
case CMD_OBJ_MAPS:
flags |= NFT_CACHE_TABLE | NFT_CACHE_SET;
+ if (!nft_output_terse(&nft->output))
+ flags |= NFT_CACHE_SETELEM;
break;
+ case CMD_OBJ_FLOWTABLE:
+ if (filter &&
+ cmd->handle.table.name &&
+ cmd->handle.flowtable.name) {
+ filter->list.family = cmd->handle.family;
+ filter->list.table = cmd->handle.table.name;
+ filter->list.ft = cmd->handle.flowtable.name;
+ }
+ /* fall through */
case CMD_OBJ_FLOWTABLES:
flags |= NFT_CACHE_TABLE | NFT_CACHE_FLOWTABLE;
break;
case CMD_OBJ_RULESET:
- if (nft_output_terse(&nft->output))
- flags |= (NFT_CACHE_FULL & ~NFT_CACHE_SETELEM_BIT);
- else
- flags |= NFT_CACHE_FULL;
- break;
default:
flags |= NFT_CACHE_FULL;
break;
}
flags |= NFT_CACHE_REFRESH;
+ if (nft_output_terse(&nft->output))
+ flags |= NFT_CACHE_TERSE;
+
return flags;
}
-unsigned int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds,
- struct nft_cache_filter *filter)
+static unsigned int evaluate_cache_reset(struct cmd *cmd, unsigned int flags,
+ struct nft_cache_filter *filter)
+{
+ switch (cmd->obj) {
+ case CMD_OBJ_RULES:
+ case CMD_OBJ_RULE:
+ if (filter) {
+ if (cmd->handle.table.name) {
+ filter->list.family = cmd->handle.family;
+ filter->list.table = cmd->handle.table.name;
+ }
+ if (cmd->handle.chain.name)
+ filter->list.chain = cmd->handle.chain.name;
+ }
+ flags |= NFT_CACHE_SET | NFT_CACHE_FLOWTABLE |
+ NFT_CACHE_OBJECT | NFT_CACHE_CHAIN;
+ break;
+ case CMD_OBJ_ELEMENTS:
+ case CMD_OBJ_SET:
+ case CMD_OBJ_MAP:
+ flags |= NFT_CACHE_SET;
+ break;
+ default:
+ flags |= NFT_CACHE_TABLE;
+ break;
+ }
+
+ return flags;
+}
+
+static int nft_handle_validate(const struct cmd *cmd, struct list_head *msgs)
+{
+ const struct handle *h = &cmd->handle;
+ const struct location *loc;
+
+ switch (cmd->obj) {
+ case CMD_OBJ_TABLE:
+ if (h->table.name &&
+ strlen(h->table.name) > NFT_NAME_MAXLEN) {
+ loc = &h->table.location;
+ goto err_name_too_long;
+ }
+ break;
+ case CMD_OBJ_RULE:
+ case CMD_OBJ_RULES:
+ case CMD_OBJ_CHAIN:
+ case CMD_OBJ_CHAINS:
+ if (h->table.name &&
+ strlen(h->table.name) > NFT_NAME_MAXLEN) {
+ loc = &h->table.location;
+ goto err_name_too_long;
+ }
+ if (h->chain.name &&
+ strlen(h->chain.name) > NFT_NAME_MAXLEN) {
+ loc = &h->chain.location;
+ goto err_name_too_long;
+ }
+ break;
+ case CMD_OBJ_ELEMENTS:
+ case CMD_OBJ_SET:
+ case CMD_OBJ_SETS:
+ case CMD_OBJ_MAP:
+ case CMD_OBJ_MAPS:
+ case CMD_OBJ_METER:
+ case CMD_OBJ_METERS:
+ if (h->table.name &&
+ strlen(h->table.name) > NFT_NAME_MAXLEN) {
+ loc = &h->table.location;
+ goto err_name_too_long;
+ }
+ if (h->set.name &&
+ strlen(h->set.name) > NFT_NAME_MAXLEN) {
+ loc = &h->set.location;
+ goto err_name_too_long;
+ }
+ break;
+ case CMD_OBJ_FLOWTABLE:
+ case CMD_OBJ_FLOWTABLES:
+ if (h->table.name &&
+ strlen(h->table.name) > NFT_NAME_MAXLEN) {
+ loc = &h->table.location;
+ goto err_name_too_long;
+ }
+ if (h->flowtable.name &&
+ strlen(h->flowtable.name) > NFT_NAME_MAXLEN) {
+ loc = &h->flowtable.location;
+ goto err_name_too_long;
+ }
+ break;
+ case CMD_OBJ_INVALID:
+ case CMD_OBJ_EXPR:
+ case CMD_OBJ_RULESET:
+ case CMD_OBJ_MARKUP:
+ case CMD_OBJ_MONITOR:
+ case CMD_OBJ_SETELEMS:
+ case CMD_OBJ_HOOKS:
+ break;
+ case CMD_OBJ_COUNTER:
+ case CMD_OBJ_COUNTERS:
+ case CMD_OBJ_QUOTA:
+ case CMD_OBJ_QUOTAS:
+ case CMD_OBJ_LIMIT:
+ case CMD_OBJ_LIMITS:
+ case CMD_OBJ_SECMARK:
+ case CMD_OBJ_SECMARKS:
+ case CMD_OBJ_SYNPROXY:
+ case CMD_OBJ_SYNPROXYS:
+ case CMD_OBJ_CT_HELPER:
+ case CMD_OBJ_CT_HELPERS:
+ case CMD_OBJ_CT_TIMEOUT:
+ case CMD_OBJ_CT_TIMEOUTS:
+ case CMD_OBJ_CT_EXPECT:
+ case CMD_OBJ_CT_EXPECTATIONS:
+ if (h->table.name &&
+ strlen(h->table.name) > NFT_NAME_MAXLEN) {
+ loc = &h->table.location;
+ goto err_name_too_long;
+ }
+ if (h->obj.name &&
+ strlen(h->obj.name) > NFT_NAME_MAXLEN) {
+ loc = &h->obj.location;
+ goto err_name_too_long;
+ }
+ break;
+ }
+
+ return 0;
+
+err_name_too_long:
+ erec_queue(error(loc, "name too long, %d characters maximum allowed",
+ NFT_NAME_MAXLEN),
+ msgs);
+ return -1;
+}
+
+int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds,
+ struct list_head *msgs, struct nft_cache_filter *filter,
+ unsigned int *pflags)
{
unsigned int flags = NFT_CACHE_EMPTY;
struct cmd *cmd;
list_for_each_entry(cmd, cmds, list) {
- if (filter->table && cmd->op != CMD_LIST)
- memset(filter, 0, sizeof(*filter));
+ if (nft_handle_validate(cmd, msgs) < 0)
+ return -1;
+
+ if (filter->list.table && cmd->op != CMD_LIST)
+ memset(&filter->list, 0, sizeof(filter->list));
switch (cmd->op) {
case CMD_ADD:
@@ -198,6 +432,7 @@ unsigned int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds,
flags = NFT_CACHE_FULL;
break;
case CMD_DELETE:
+ case CMD_DESTROY:
flags |= NFT_CACHE_TABLE |
NFT_CACHE_CHAIN |
NFT_CACHE_SET |
@@ -210,7 +445,7 @@ unsigned int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds,
flags = evaluate_cache_get(cmd, flags);
break;
case CMD_RESET:
- flags |= NFT_CACHE_TABLE;
+ flags |= evaluate_cache_reset(cmd, flags, filter);
break;
case CMD_LIST:
flags |= evaluate_cache_list(nft, cmd, flags, filter);
@@ -219,7 +454,7 @@ unsigned int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds,
flags |= NFT_CACHE_FULL;
break;
case CMD_FLUSH:
- flags = evaluate_cache_flush(cmd, flags);
+ flags = evaluate_cache_flush(cmd, flags, filter);
break;
case CMD_RENAME:
flags = evaluate_cache_rename(cmd, flags);
@@ -232,8 +467,9 @@ unsigned int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds,
break;
}
}
+ *pflags = flags;
- return flags;
+ return 0;
}
void table_cache_add(struct table *table, struct nft_cache *cache)
@@ -281,13 +517,13 @@ static int chain_cache_cb(struct nftnl_chain *nlc, void *arg)
struct chain *chain;
table_name = nftnl_chain_get_str(nlc, NFTNL_CHAIN_TABLE);
- chain_name = nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME);
family = nftnl_chain_get_u32(nlc, NFTNL_CHAIN_FAMILY);
- if (strcmp(table_name, ctx->table->handle.table.name) ||
- family != ctx->table->handle.family)
+ if (family != ctx->table->handle.family ||
+ strcmp(table_name, ctx->table->handle.table.name))
return 0;
+ chain_name = nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME);
hash = djb_hash(chain_name) % NFT_CACHE_HSIZE;
chain = netlink_delinearize_chain(ctx->nlctx, nlc);
@@ -315,12 +551,22 @@ static int chain_cache_init(struct netlink_ctx *ctx, struct table *table,
return 0;
}
-static struct nftnl_chain_list *chain_cache_dump(struct netlink_ctx *ctx,
- int *err)
+static struct nftnl_chain_list *
+chain_cache_dump(struct netlink_ctx *ctx,
+ const struct nft_cache_filter *filter, int *err)
{
struct nftnl_chain_list *chain_list;
+ const char *table = NULL;
+ const char *chain = NULL;
+ int family = NFPROTO_UNSPEC;
+
+ if (filter && filter->list.table && filter->list.chain) {
+ family = filter->list.family;
+ table = filter->list.table;
+ chain = filter->list.chain;
+ }
- chain_list = mnl_nft_chain_dump(ctx, AF_UNSPEC);
+ chain_list = mnl_nft_chain_dump(ctx, family, table, chain);
if (chain_list == NULL) {
if (errno == EINTR) {
*err = -1;
@@ -333,6 +579,21 @@ static struct nftnl_chain_list *chain_cache_dump(struct netlink_ctx *ctx,
return chain_list;
}
+void nft_chain_cache_update(struct netlink_ctx *ctx, struct table *table,
+ const char *chain)
+{
+ struct nftnl_chain_list *chain_list;
+
+ chain_list = mnl_nft_chain_dump(ctx, table->handle.family,
+ table->handle.table.name, chain);
+ if (!chain_list)
+ return;
+
+ chain_cache_init(ctx, table, chain_list);
+
+ nftnl_chain_list_free(chain_list);
+}
+
void chain_cache_add(struct chain *chain, struct table *table)
{
uint32_t hash;
@@ -360,59 +621,124 @@ struct chain *chain_cache_find(const struct table *table, const char *name)
return NULL;
}
+static int list_rule_cb(struct nftnl_rule *nlr, void *data)
+{
+ struct netlink_ctx *ctx = data;
+ const struct handle *h = ctx->data;
+ const char *table, *chain;
+ struct rule *rule;
+ uint32_t family;
+
+ family = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY);
+ table = nftnl_rule_get_str(nlr, NFTNL_RULE_TABLE);
+ chain = nftnl_rule_get_str(nlr, NFTNL_RULE_CHAIN);
+
+ if ((h->family != NFPROTO_UNSPEC && h->family != family) ||
+ (h->table.name && strcmp(table, h->table.name) != 0) ||
+ (h->chain.name && strcmp(chain, h->chain.name) != 0))
+ return 0;
+
+ netlink_dump_rule(nlr, ctx);
+ rule = netlink_delinearize_rule(ctx, nlr);
+ assert(rule);
+ list_add_tail(&rule->list, &ctx->list);
+
+ return 0;
+}
+
+int rule_cache_dump(struct netlink_ctx *ctx, const struct handle *h,
+ const struct nft_cache_filter *filter,
+ bool dump, bool reset)
+{
+ struct nftnl_rule_list *rule_cache;
+ const char *table = NULL;
+ const char *chain = NULL;
+ uint64_t rule_handle = 0;
+
+ if (filter) {
+ table = filter->list.table;
+ chain = filter->list.chain;
+ rule_handle = filter->list.rule_handle;
+ }
+
+ rule_cache = mnl_nft_rule_dump(ctx, h->family,
+ table, chain, rule_handle, dump, reset);
+ if (rule_cache == NULL) {
+ if (errno == EINTR)
+ return -1;
+
+ return 0;
+ }
+
+ ctx->data = h;
+ nftnl_rule_list_foreach(rule_cache, list_rule_cb, ctx);
+ nftnl_rule_list_free(rule_cache);
+ return 0;
+}
+
struct set_cache_dump_ctx {
struct netlink_ctx *nlctx;
struct table *table;
- const struct nft_cache_filter *filter;
};
static int set_cache_cb(struct nftnl_set *nls, void *arg)
{
struct set_cache_dump_ctx *ctx = arg;
+ const char *set_table;
const char *set_name;
+ uint32_t set_family;
struct set *set;
uint32_t hash;
+ set_table = nftnl_set_get_str(nls, NFTNL_SET_TABLE);
+ set_family = nftnl_set_get_u32(nls, NFTNL_SET_FAMILY);
+
+ if (set_family != ctx->table->handle.family ||
+ strcmp(set_table, ctx->table->handle.table.name))
+ return 0;
+
set = netlink_delinearize_set(ctx->nlctx, nls);
if (!set)
return -1;
- if (ctx->filter && ctx->filter->set &&
- (strcmp(ctx->filter->table, set->handle.table.name) ||
- strcmp(ctx->filter->set, set->handle.set.name))) {
- set_free(set);
- return 0;
- }
-
set_name = nftnl_set_get_str(nls, NFTNL_SET_NAME);
hash = djb_hash(set_name) % NFT_CACHE_HSIZE;
cache_add(&set->cache, &ctx->table->set_cache, hash);
+ nftnl_set_list_del(nls);
+ nftnl_set_free(nls);
return 0;
}
static int set_cache_init(struct netlink_ctx *ctx, struct table *table,
- struct nftnl_set_list *set_list,
- const struct nft_cache_filter *filter)
+ struct nftnl_set_list *set_list)
{
struct set_cache_dump_ctx dump_ctx = {
.nlctx = ctx,
.table = table,
- .filter = filter,
};
+
nftnl_set_list_foreach(set_list, set_cache_cb, &dump_ctx);
return 0;
}
-static struct nftnl_set_list *set_cache_dump(struct netlink_ctx *ctx,
- const struct table *table,
- int *err)
+static struct nftnl_set_list *
+set_cache_dump(struct netlink_ctx *ctx,
+ const struct nft_cache_filter *filter, int *err)
{
struct nftnl_set_list *set_list;
+ int family = NFPROTO_UNSPEC;
+ const char *table = NULL;
+ const char *set = NULL;
+
+ if (filter) {
+ family = filter->list.family;
+ table = filter->list.table;
+ set = filter->list.set;
+ }
- set_list = mnl_nft_set_dump(ctx, table->handle.family,
- table->handle.table.name);
+ set_list = mnl_nft_set_dump(ctx, family, table, set);
if (!set_list) {
if (errno == EINTR) {
*err = -1;
@@ -547,10 +873,19 @@ struct ft_cache_dump_ctx {
static int ft_cache_cb(struct nftnl_flowtable *nlf, void *arg)
{
struct ft_cache_dump_ctx *ctx = arg;
- const char *ft_name;
struct flowtable *ft;
+ const char *ft_table;
+ const char *ft_name;
+ uint32_t ft_family;
uint32_t hash;
+ ft_family = nftnl_flowtable_get_u32(nlf, NFTNL_FLOWTABLE_FAMILY);
+ ft_table = nftnl_flowtable_get_str(nlf, NFTNL_FLOWTABLE_TABLE);
+
+ if (ft_family != ctx->table->handle.family ||
+ strcmp(ft_table, ctx->table->handle.table.name))
+ return 0;
+
ft = netlink_delinearize_flowtable(ctx->nlctx, nlf);
if (!ft)
return -1;
@@ -559,6 +894,8 @@ static int ft_cache_cb(struct nftnl_flowtable *nlf, void *arg)
hash = djb_hash(ft_name) % NFT_CACHE_HSIZE;
cache_add(&ft->cache, &ctx->table->ft_cache, hash);
+ nftnl_flowtable_list_del(nlf);
+ nftnl_flowtable_free(nlf);
return 0;
}
@@ -574,13 +911,21 @@ static int ft_cache_init(struct netlink_ctx *ctx, struct table *table,
return 0;
}
-static struct nftnl_flowtable_list *ft_cache_dump(struct netlink_ctx *ctx,
- const struct table *table)
+static struct nftnl_flowtable_list *
+ft_cache_dump(struct netlink_ctx *ctx, const struct nft_cache_filter *filter)
{
struct nftnl_flowtable_list *ft_list;
+ int family = NFPROTO_UNSPEC;
+ const char *table = NULL;
+ const char *ft = NULL;
+
+ if (filter) {
+ family = filter->list.family;
+ table = filter->list.table;
+ ft = filter->list.ft;
+ }
- ft_list = mnl_nft_flowtable_dump(ctx, table->handle.family,
- table->handle.table.name);
+ ft_list = mnl_nft_flowtable_dump(ctx, family, table, ft);
if (!ft_list) {
if (errno == EINTR)
return NULL;
@@ -630,24 +975,68 @@ static int cache_init_tables(struct netlink_ctx *ctx, struct handle *h,
struct table *table, *next;
int ret;
- ret = netlink_list_tables(ctx, h);
+ ret = netlink_list_tables(ctx, h, filter);
if (ret < 0)
return -1;
list_for_each_entry_safe(table, next, &ctx->list, list) {
list_del(&table->list);
-
- if (filter && filter->table &&
- (strcmp(filter->table, table->handle.table.name))) {
- table_free(table);
- continue;
- }
table_cache_add(table, cache);
}
return 0;
}
+static int rule_init_cache(struct netlink_ctx *ctx, struct table *table,
+ const struct nft_cache_filter *filter)
+{
+ struct rule *rule, *nrule;
+ struct chain *chain;
+ int ret;
+
+ ret = rule_cache_dump(ctx, &table->handle, filter, true, false);
+
+ list_for_each_entry_safe(rule, nrule, &ctx->list, list) {
+ chain = chain_cache_find(table, rule->handle.chain.name);
+ if (!chain)
+ chain = chain_binding_lookup(table,
+ rule->handle.chain.name);
+ if (!chain)
+ goto err_ctx_list;
+
+ list_move_tail(&rule->list, &chain->rules);
+ }
+
+ return ret;
+
+err_ctx_list:
+ list_for_each_entry_safe(rule, nrule, &ctx->list, list) {
+ list_del(&rule->list);
+ rule_free(rule);
+ }
+ errno = EINTR;
+
+ return -1;
+}
+
+static int implicit_chain_cache(struct netlink_ctx *ctx, struct table *table,
+ const char *chain_name)
+{
+ struct nft_cache_filter filter;
+ struct chain *chain;
+ int ret = 0;
+
+ list_for_each_entry(chain, &table->chain_bindings, cache.list) {
+ filter.list = (typeof(filter.list)) {
+ .table = table->handle.table.name,
+ .chain = chain->handle.chain.name,
+ };
+ ret = rule_init_cache(ctx, table, &filter);
+ }
+
+ return ret;
+}
+
static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
const struct nft_cache_filter *filter)
{
@@ -655,77 +1044,72 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
struct nftnl_chain_list *chain_list = NULL;
struct nftnl_set_list *set_list = NULL;
struct nftnl_obj_list *obj_list;
- struct rule *rule, *nrule;
struct table *table;
- struct chain *chain;
struct set *set;
int ret = 0;
if (flags & NFT_CACHE_CHAIN_BIT) {
- chain_list = chain_cache_dump(ctx, &ret);
+ chain_list = chain_cache_dump(ctx, filter, &ret);
if (!chain_list)
return -1;
}
+ if (flags & NFT_CACHE_SET_BIT) {
+ set_list = set_cache_dump(ctx, filter, &ret);
+ if (!set_list) {
+ ret = -1;
+ goto cache_fails;
+ }
+ }
+ if (flags & NFT_CACHE_FLOWTABLE_BIT) {
+ ft_list = ft_cache_dump(ctx, filter);
+ if (!ft_list) {
+ ret = -1;
+ goto cache_fails;
+ }
+ }
list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
if (flags & NFT_CACHE_SET_BIT) {
- set_list = set_cache_dump(ctx, table, &ret);
- if (!set_list) {
- ret = -1;
+ ret = set_cache_init(ctx, table, set_list);
+ if (ret < 0)
goto cache_fails;
- }
- ret = set_cache_init(ctx, table, set_list, filter);
-
- nftnl_set_list_free(set_list);
-
- if (ret < 0) {
- ret = -1;
- goto cache_fails;
- }
}
if (flags & NFT_CACHE_SETELEM_BIT) {
list_for_each_entry(set, &table->set_cache.list, cache.list) {
+ if (cache_filter_find(filter, &set->handle))
+ continue;
+ if (!set_is_anonymous(set->flags) &&
+ flags & NFT_CACHE_TERSE)
+ continue;
+
ret = netlink_list_setelems(ctx, &set->handle,
- set);
- if (ret < 0) {
- ret = -1;
+ set, false);
+ if (ret < 0)
goto cache_fails;
- }
}
} else if (flags & NFT_CACHE_SETELEM_MAYBE) {
list_for_each_entry(set, &table->set_cache.list, cache.list) {
+ if (cache_filter_find(filter, &set->handle))
+ continue;
+
if (!set_is_non_concat_range(set))
continue;
ret = netlink_list_setelems(ctx, &set->handle,
- set);
- if (ret < 0) {
- ret = -1;
+ set, false);
+ if (ret < 0)
goto cache_fails;
- }
}
}
if (flags & NFT_CACHE_CHAIN_BIT) {
ret = chain_cache_init(ctx, table, chain_list);
- if (ret < 0) {
- ret = -1;
+ if (ret < 0)
goto cache_fails;
- }
}
if (flags & NFT_CACHE_FLOWTABLE_BIT) {
- ft_list = ft_cache_dump(ctx, table);
- if (!ft_list) {
- ret = -1;
- goto cache_fails;
- }
ret = ft_cache_init(ctx, table, ft_list);
-
- nftnl_flowtable_list_free(ft_list);
-
- if (ret < 0) {
- ret = -1;
+ if (ret < 0)
goto cache_fails;
- }
}
if (flags & NFT_CACHE_OBJECT_BIT) {
obj_list = obj_cache_dump(ctx, table);
@@ -737,34 +1121,29 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
nftnl_obj_list_free(obj_list);
- if (ret < 0) {
- ret = -1;
+ if (ret < 0)
goto cache_fails;
- }
}
if (flags & NFT_CACHE_RULE_BIT) {
- ret = netlink_list_rules(ctx, &table->handle);
- list_for_each_entry_safe(rule, nrule, &ctx->list, list) {
- chain = chain_cache_find(table, rule->handle.chain.name);
- if (!chain)
- chain = chain_binding_lookup(table,
- rule->handle.chain.name);
- if (!chain) {
- ret = -1;
- goto cache_fails;
- }
-
- list_move_tail(&rule->list, &chain->rules);
- }
- if (ret < 0) {
- ret = -1;
+ ret = rule_init_cache(ctx, table, filter);
+ if (ret < 0)
goto cache_fails;
+
+ if (filter && filter->list.table && filter->list.chain) {
+ ret = implicit_chain_cache(ctx, table, filter->list.chain);
+ if (ret < 0)
+ goto cache_fails;
}
}
}
cache_fails:
+ if (set_list)
+ nftnl_set_list_free(set_list);
+ if (ft_list)
+ nftnl_flowtable_list_free(ft_list);
+
if (flags & NFT_CACHE_CHAIN_BIT)
nftnl_chain_list_free(chain_list);
@@ -851,7 +1230,11 @@ replay:
goto replay;
}
+ erec_queue(error(&netlink_location, "cache initialization failed: %s",
+ strerror(errno)),
+ msgs);
nft_cache_release(cache);
+
return -1;
}
@@ -896,7 +1279,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/cli.c b/src/cli.c
index 4845e5cf..448c25c2 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -12,13 +12,12 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
-#include <config.h>
-#include <stdlib.h>
+#include <nft.h>
+
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <errno.h>
-#include <string.h>
#include <ctype.h>
#include <limits.h>
#ifdef HAVE_LIBREADLINE
@@ -26,7 +25,6 @@
#include <readline/history.h>
#elif defined(HAVE_LIBEDIT)
#include <editline/readline.h>
-#include <editline/history.h>
#else
#include <linenoise.h>
#endif
@@ -38,6 +36,15 @@
#define CMDLINE_PROMPT "nft> "
#define CMDLINE_QUIT "quit"
+static bool cli_quit;
+static int cli_rc;
+
+static void __cli_exit(int rc)
+{
+ cli_quit = true;
+ cli_rc = rc;
+}
+
static char histfile[PATH_MAX];
static void
@@ -101,8 +108,8 @@ static char *cli_append_multiline(char *line)
if (!s) {
fprintf(stderr, "%s:%u: Memory allocation failure\n",
__FILE__, __LINE__);
- cli_exit();
- exit(EXIT_FAILURE);
+ cli_exit(EXIT_FAILURE);
+ return NULL;
}
snprintf(s, len + 1, "%s%s", multiline, line);
free(multiline);
@@ -126,8 +133,7 @@ static void cli_complete(char *line)
if (line == NULL) {
printf("\n");
- cli_exit();
- exit(0);
+ return cli_exit(0);
}
line = cli_append_multiline(line);
@@ -140,10 +146,8 @@ static void cli_complete(char *line)
if (*c == '\0')
return;
- if (!strcmp(line, CMDLINE_QUIT)) {
- cli_exit();
- exit(0);
- }
+ if (!strcmp(line, CMDLINE_QUIT))
+ return cli_exit(0);
/* avoid duplicate history entries */
hist = history_get(history_length);
@@ -153,13 +157,6 @@ static void cli_complete(char *line)
nft_run_cmd_from_buffer(cli_nft, line);
free(line);
}
-
-void cli_exit(void)
-{
- rl_callback_handler_remove();
- rl_deprep_terminal();
- write_history(histfile);
-}
#endif
#if defined(HAVE_LIBREADLINE)
@@ -184,9 +181,19 @@ int cli_init(struct nft_ctx *nft)
read_history(histfile);
history_set_pos(history_length);
- while (true)
+ while (!cli_quit)
rl_callback_read_char();
- return 0;
+
+ return cli_rc;
+}
+
+void cli_exit(int rc)
+{
+ rl_callback_handler_remove();
+ rl_deprep_terminal();
+ write_history(histfile);
+
+ __cli_exit(rc);
}
#elif defined(HAVE_LIBEDIT)
@@ -206,7 +213,12 @@ int cli_init(struct nft_ctx *nft)
history_set_pos(history_length);
rl_set_prompt(CMDLINE_PROMPT);
- while ((line = readline(rl_prompt)) != NULL) {
+ while (!cli_quit) {
+ line = readline(rl_prompt);
+ if (!line) {
+ cli_exit(0);
+ break;
+ }
line = cli_append_multiline(line);
if (!line)
continue;
@@ -214,36 +226,50 @@ int cli_init(struct nft_ctx *nft)
cli_complete(line);
}
- return 0;
+ return cli_rc;
+}
+
+void cli_exit(int rc)
+{
+ rl_deprep_terminal();
+ write_history(histfile);
+
+ __cli_exit(rc);
}
#else /* HAVE_LINENOISE */
int cli_init(struct nft_ctx *nft)
{
- int quit = 0;
char *line;
init_histfile();
linenoiseHistoryLoad(histfile);
linenoiseSetMultiLine(1);
- while (!quit && (line = linenoise(CMDLINE_PROMPT)) != NULL) {
+ while (!cli_quit) {
+ line = linenoise(CMDLINE_PROMPT);
+ if (!line) {
+ cli_exit(0);
+ break;
+ }
if (strcmp(line, CMDLINE_QUIT) == 0) {
- quit = 1;
+ cli_exit(0);
} else if (line[0] != '\0') {
linenoiseHistoryAdd(line);
nft_run_cmd_from_buffer(nft, line);
}
linenoiseFree(line);
}
- cli_exit();
- exit(0);
+
+ return cli_rc;
}
-void cli_exit(void)
+void cli_exit(int rc)
{
linenoiseHistorySave(histfile);
+
+ __cli_exit(rc);
}
#endif /* HAVE_LINENOISE */
diff --git a/src/cmd.c b/src/cmd.c
index f6a8aa11..14cb1b51 100644
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -1,3 +1,13 @@
+/*
+ * Copyright (c) 2020 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 <nft.h>
+
#include <erec.h>
#include <mnl.h>
#include <cmd.h>
@@ -5,9 +15,19 @@
#include <utils.h>
#include <iface.h>
#include <errno.h>
-#include <stdlib.h>
#include <cache.h>
-#include <string.h>
+
+void cmd_add_loc(struct cmd *cmd, uint16_t offset, const struct location *loc)
+{
+ if (cmd->num_attrs >= cmd->attr_array_len) {
+ cmd->attr_array_len *= 2;
+ cmd->attr = xrealloc(cmd->attr, sizeof(struct nlerr_loc) * cmd->attr_array_len);
+ }
+
+ cmd->attr[cmd->num_attrs].offset = offset;
+ cmd->attr[cmd->num_attrs].location = loc;
+ cmd->num_attrs++;
+}
static int nft_cmd_enoent_table(struct netlink_ctx *ctx, const struct cmd *cmd,
const struct location *loc)
@@ -21,7 +41,7 @@ static int nft_cmd_enoent_table(struct netlink_ctx *ctx, const struct cmd *cmd,
if (!table)
return 0;
- netlink_io_error(ctx, loc, "%s; did you mean table ‘%s’ in family %s?",
+ netlink_io_error(ctx, loc, "%s; did you mean table '%s' in family %s?",
strerror(ENOENT), table->handle.table.name,
family2str(table->handle.family));
return 1;
@@ -37,7 +57,7 @@ static int table_fuzzy_check(struct netlink_ctx *ctx, const struct cmd *cmd,
if (strcmp(cmd->handle.table.name, table->handle.table.name) ||
cmd->handle.family != table->handle.family) {
netlink_io_error(ctx, &cmd->handle.table.location,
- "%s; did you mean table ‘%s’ in family %s?",
+ "%s; did you mean table '%s' in family %s?",
strerror(ENOENT), table->handle.table.name,
family2str(table->handle.family));
return 1;
@@ -66,7 +86,7 @@ static int nft_cmd_enoent_chain(struct netlink_ctx *ctx, const struct cmd *cmd,
if (!chain)
return 0;
- netlink_io_error(ctx, loc, "%s; did you mean chain ‘%s’ in table %s ‘%s’?",
+ netlink_io_error(ctx, loc, "%s; did you mean chain '%s' in table %s '%s'?",
strerror(ENOENT), chain->handle.chain.name,
family2str(table->handle.family),
table->handle.table.name);
@@ -96,7 +116,7 @@ static int nft_cmd_enoent_rule(struct netlink_ctx *ctx, const struct cmd *cmd,
return 0;
if (strcmp(cmd->handle.chain.name, chain->handle.chain.name)) {
- netlink_io_error(ctx, loc, "%s; did you mean chain ‘%s’ in table %s ‘%s’?",
+ netlink_io_error(ctx, loc, "%s; did you mean chain '%s' in table %s '%s'?",
strerror(ENOENT),
chain->handle.chain.name,
family2str(table->handle.family),
@@ -127,7 +147,7 @@ static int nft_cmd_enoent_set(struct netlink_ctx *ctx, const struct cmd *cmd,
if (!set)
return 0;
- netlink_io_error(ctx, loc, "%s; did you mean %s ‘%s’ in table %s ‘%s’?",
+ netlink_io_error(ctx, loc, "%s; did you mean %s '%s' in table %s '%s'?",
strerror(ENOENT),
set_is_map(set->flags) ? "map" : "set",
set->handle.set.name,
@@ -156,7 +176,7 @@ static int nft_cmd_enoent_obj(struct netlink_ctx *ctx, const struct cmd *cmd,
if (!obj)
return 0;
- netlink_io_error(ctx, loc, "%s; did you mean obj ‘%s’ in table %s ‘%s’?",
+ netlink_io_error(ctx, loc, "%s; did you mean obj '%s' in table %s '%s'?",
strerror(ENOENT), obj->handle.obj.name,
family2str(obj->handle.family),
table->handle.table.name);
@@ -185,7 +205,7 @@ static int nft_cmd_enoent_flowtable(struct netlink_ctx *ctx,
if (!ft)
return 0;
- netlink_io_error(ctx, loc, "%s; did you mean flowtable ‘%s’ in table %s ‘%s’?",
+ netlink_io_error(ctx, loc, "%s; did you mean flowtable '%s' in table %s '%s'?",
strerror(ENOENT), ft->handle.flowtable.name,
family2str(ft->handle.family),
table->handle.table.name);
@@ -233,11 +253,38 @@ static void nft_cmd_enoent(struct netlink_ctx *ctx, const struct cmd *cmd,
netlink_io_error(ctx, loc, "Could not process rule: %s", strerror(err));
}
+static int nft_cmd_chain_error(struct netlink_ctx *ctx, struct cmd *cmd,
+ struct mnl_err *err)
+{
+ struct chain *chain = cmd->chain;
+ int priority;
+
+ switch (err->err) {
+ case EOPNOTSUPP:
+ if (!(chain->flags & CHAIN_F_BASECHAIN))
+ break;
+
+ mpz_export_data(&priority, chain->priority.expr->value,
+ BYTEORDER_HOST_ENDIAN, sizeof(int));
+ if (priority <= -200 && !strcmp(chain->type.str, "nat"))
+ return netlink_io_error(ctx, &chain->priority.loc,
+ "Chains of type \"nat\" must have a priority value above -200");
+
+ return netlink_io_error(ctx, &chain->loc,
+ "Chain of type \"%s\" is not supported, perhaps kernel support is missing?",
+ chain->type.str);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
void nft_cmd_error(struct netlink_ctx *ctx, struct cmd *cmd,
struct mnl_err *err)
{
const struct location *loc = NULL;
- int i;
+ uint32_t i;
for (i = 0; i < cmd->num_attrs; i++) {
if (!cmd->attr[i].offset)
@@ -255,6 +302,200 @@ void nft_cmd_error(struct netlink_ctx *ctx, struct cmd *cmd,
loc = &cmd->location;
}
+ switch (cmd->obj) {
+ case CMD_OBJ_CHAIN:
+ if (nft_cmd_chain_error(ctx, cmd, err) < 0)
+ return;
+ break;
+ default:
+ 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));
}
+
+static void nft_cmd_expand_chain(struct chain *chain, struct list_head *new_cmds)
+{
+ struct rule *rule, *next;
+ struct handle h;
+ struct cmd *new;
+
+ list_for_each_entry_safe(rule, next, &chain->rules, list) {
+ list_del(&rule->list);
+ handle_merge(&rule->handle, &chain->handle);
+ memset(&h, 0, sizeof(h));
+ handle_merge(&h, &chain->handle);
+ if (chain->flags & CHAIN_F_BINDING) {
+ rule->handle.chain_id = chain->handle.chain_id;
+ rule->handle.chain.location = chain->location;
+ }
+ new = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &h,
+ &rule->location, rule);
+ list_add_tail(&new->list, new_cmds);
+ }
+}
+
+void nft_cmd_expand(struct cmd *cmd)
+{
+ struct list_head new_cmds;
+ struct flowtable *ft;
+ struct table *table;
+ struct chain *chain;
+ struct set *set;
+ struct obj *obj;
+ struct cmd *new;
+ struct handle h;
+
+ init_list_head(&new_cmds);
+
+ switch (cmd->obj) {
+ case CMD_OBJ_TABLE:
+ table = cmd->table;
+ if (!table)
+ return;
+
+ list_for_each_entry(chain, &table->chains, list) {
+ handle_merge(&chain->handle, &table->handle);
+ memset(&h, 0, sizeof(h));
+ handle_merge(&h, &chain->handle);
+ h.chain_id = chain->handle.chain_id;
+ new = cmd_alloc(CMD_ADD, CMD_OBJ_CHAIN, &h,
+ &chain->location, chain_get(chain));
+ list_add_tail(&new->list, &new_cmds);
+ }
+ list_for_each_entry(obj, &table->objs, list) {
+ handle_merge(&obj->handle, &table->handle);
+ memset(&h, 0, sizeof(h));
+ handle_merge(&h, &obj->handle);
+ new = cmd_alloc(CMD_ADD, obj_type_to_cmd(obj->type), &h,
+ &obj->location, obj_get(obj));
+ list_add_tail(&new->list, &new_cmds);
+ }
+ list_for_each_entry(set, &table->sets, list) {
+ handle_merge(&set->handle, &table->handle);
+ memset(&h, 0, sizeof(h));
+ handle_merge(&h, &set->handle);
+ new = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &h,
+ &set->location, set_get(set));
+ list_add_tail(&new->list, &new_cmds);
+ }
+ list_for_each_entry(ft, &table->flowtables, list) {
+ handle_merge(&ft->handle, &table->handle);
+ memset(&h, 0, sizeof(h));
+ handle_merge(&h, &ft->handle);
+ new = cmd_alloc(CMD_ADD, CMD_OBJ_FLOWTABLE, &h,
+ &ft->location, flowtable_get(ft));
+ list_add_tail(&new->list, &new_cmds);
+ }
+ list_for_each_entry(chain, &table->chains, list)
+ nft_cmd_expand_chain(chain, &new_cmds);
+
+ list_splice(&new_cmds, &cmd->list);
+ break;
+ case CMD_OBJ_CHAIN:
+ chain = cmd->chain;
+ if (!chain || list_empty(&chain->rules))
+ break;
+
+ nft_cmd_expand_chain(chain, &new_cmds);
+ list_splice(&new_cmds, &cmd->list);
+ break;
+ case CMD_OBJ_SET:
+ case CMD_OBJ_MAP:
+ set = cmd->set;
+ if (!set->init)
+ break;
+
+ memset(&h, 0, sizeof(h));
+ handle_merge(&h, &set->handle);
+ new = cmd_alloc(CMD_ADD, CMD_OBJ_SETELEMS, &h,
+ &set->location, set_get(set));
+ list_add(&new->list, &cmd->list);
+ break;
+ default:
+ break;
+ }
+}
+
+bool nft_cmd_collapse(struct list_head *cmds)
+{
+ struct cmd *cmd, *next, *elems = NULL;
+ struct expr *expr, *enext;
+ bool collapse = false;
+
+ list_for_each_entry_safe(cmd, next, cmds, list) {
+ if (cmd->op != CMD_ADD &&
+ cmd->op != CMD_CREATE) {
+ elems = NULL;
+ continue;
+ }
+
+ if (cmd->obj != CMD_OBJ_ELEMENTS) {
+ elems = NULL;
+ continue;
+ }
+
+ if (!elems) {
+ elems = cmd;
+ continue;
+ }
+
+ if (cmd->op != elems->op) {
+ elems = cmd;
+ continue;
+ }
+
+ if (elems->handle.family != cmd->handle.family ||
+ strcmp(elems->handle.table.name, cmd->handle.table.name) ||
+ strcmp(elems->handle.set.name, cmd->handle.set.name)) {
+ elems = cmd;
+ continue;
+ }
+
+ collapse = true;
+ list_for_each_entry_safe(expr, enext, &cmd->expr->expressions, list) {
+ expr->cmd = cmd;
+ list_move_tail(&expr->list, &elems->expr->expressions);
+ }
+ elems->expr->size += cmd->expr->size;
+ list_move_tail(&cmd->list, &elems->collapse_list);
+ }
+
+ return collapse;
+}
+
+void nft_cmd_uncollapse(struct list_head *cmds)
+{
+ struct cmd *cmd, *cmd_next, *collapse_cmd, *collapse_cmd_next;
+ struct expr *expr, *next;
+
+ list_for_each_entry_safe(cmd, cmd_next, cmds, list) {
+ if (list_empty(&cmd->collapse_list))
+ continue;
+
+ assert(cmd->obj == CMD_OBJ_ELEMENTS);
+
+ list_for_each_entry_safe(expr, next, &cmd->expr->expressions, list) {
+ if (!expr->cmd)
+ continue;
+
+ list_move_tail(&expr->list, &expr->cmd->expr->expressions);
+ cmd->expr->size--;
+ expr->cmd = NULL;
+ }
+
+ list_for_each_entry_safe(collapse_cmd, collapse_cmd_next, &cmd->collapse_list, list) {
+ if (cmd->elem.set)
+ collapse_cmd->elem.set = set_get(cmd->elem.set);
+
+ list_add(&collapse_cmd->list, &cmd->list);
+ }
+ }
+}
diff --git a/src/ct.c b/src/ct.c
index 2218ecc7..67934648 100644
--- a/src/ct.c
+++ b/src/ct.c
@@ -10,11 +10,11 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
-#include <string.h>
#include <netinet/ip.h>
#include <linux/netfilter.h>
@@ -131,7 +131,7 @@ static const struct symbol_table ct_events_tbl = {
},
};
-static const struct datatype ct_event_type = {
+const struct datatype ct_event_type = {
.type = TYPE_CT_EVENTBIT,
.name = "ct_event",
.desc = "conntrack event bits",
@@ -176,7 +176,7 @@ static struct error_record *ct_label_type_parse(struct parse_ctx *ctx,
{
const struct symbolic_constant *s;
const struct datatype *dtype;
- uint8_t data[CT_LABEL_BIT_SIZE];
+ uint8_t data[CT_LABEL_BIT_SIZE / BITS_PER_BYTE];
uint64_t bit;
mpz_t value;
@@ -211,16 +211,22 @@ static struct error_record *ct_label_type_parse(struct parse_ctx *ctx,
mpz_export_data(data, value, BYTEORDER_HOST_ENDIAN, sizeof(data));
*res = constant_expr_alloc(&sym->location, dtype,
- dtype->byteorder, sizeof(data),
- data);
+ dtype->byteorder, CT_LABEL_BIT_SIZE, data);
mpz_clear(value);
return NULL;
}
-static const struct datatype ct_label_type = {
+static void ct_label_type_describe(struct output_ctx *octx)
+{
+ rt_symbol_table_describe(octx, CONNLABEL_CONF,
+ octx->tbl.ct_label, &ct_label_type);
+}
+
+const struct datatype ct_label_type = {
.type = TYPE_CT_LABEL,
.name = "ct_label",
.desc = "conntrack label",
+ .describe = ct_label_type_describe,
.byteorder = BYTEORDER_HOST_ENDIAN,
.size = CT_LABEL_BIT_SIZE,
.basetype = &bitmask_type,
@@ -272,10 +278,10 @@ const struct ct_template ct_templates[__NFT_CT_MAX] = {
[NFT_CT_PROTOCOL] = CT_TEMPLATE("protocol", &inet_protocol_type,
BYTEORDER_BIG_ENDIAN,
BITS_PER_BYTE),
- [NFT_CT_PROTO_SRC] = CT_TEMPLATE("proto-src", &invalid_type,
+ [NFT_CT_PROTO_SRC] = CT_TEMPLATE("proto-src", &inet_service_type,
BYTEORDER_BIG_ENDIAN,
2 * BITS_PER_BYTE),
- [NFT_CT_PROTO_DST] = CT_TEMPLATE("proto-dst", &invalid_type,
+ [NFT_CT_PROTO_DST] = CT_TEMPLATE("proto-dst", &inet_service_type,
BYTEORDER_BIG_ENDIAN,
2 * BITS_PER_BYTE),
[NFT_CT_LABELS] = CT_TEMPLATE("label", &ct_label_type,
@@ -571,7 +577,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 = {
@@ -579,6 +585,7 @@ static const struct stmt_ops flow_offload_stmt_ops = {
.name = "flow_offload",
.print = flow_offload_stmt_print,
.destroy = flow_offload_stmt_destroy,
+ .json = flow_offload_stmt_json,
};
struct stmt *flow_offload_stmt_alloc(const struct location *loc,
diff --git a/src/datatype.c b/src/datatype.c
index b849f708..d398a9c8 100644
--- a/src/datatype.c
+++ b/src/datatype.c
@@ -8,8 +8,8 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
-#include <stdlib.h>
-#include <string.h>
+#include <nft.h>
+
#include <inttypes.h>
#include <ctype.h> /* isdigit */
#include <errno.h>
@@ -28,6 +28,8 @@
#include <erec.h>
#include <netlink.h>
#include <json.h>
+#include <misspell.h>
+#include "nftutils.h"
#include <netinet/ip_icmp.h>
@@ -62,6 +64,7 @@ static const struct datatype *datatypes[TYPE_MAX + 1] = {
[TYPE_CT_DIR] = &ct_dir_type,
[TYPE_CT_STATUS] = &ct_status_type,
[TYPE_ICMP6_TYPE] = &icmp6_type_type,
+ [TYPE_CT_LABEL] = &ct_label_type,
[TYPE_PKTTYPE] = &pkttype_type,
[TYPE_ICMP_CODE] = &icmp_code_type,
[TYPE_ICMPV6_CODE] = &icmpv6_code_type,
@@ -71,6 +74,7 @@ static const struct datatype *datatypes[TYPE_MAX + 1] = {
[TYPE_ECN] = &ecn_type,
[TYPE_FIB_ADDR] = &fib_addr_type,
[TYPE_BOOLEAN] = &boolean_type,
+ [TYPE_CT_EVENTBIT] = &ct_event_type,
[TYPE_IFNAME] = &ifname_type,
[TYPE_IGMP_TYPE] = &igmp_type_type,
[TYPE_TIME_DATE] = &date_type,
@@ -123,6 +127,7 @@ struct error_record *symbol_parse(struct parse_ctx *ctx, const struct expr *sym,
struct expr **res)
{
const struct datatype *dtype = sym->dtype;
+ struct error_record *erec;
assert(sym->etype == EXPR_SYMBOL);
@@ -136,11 +141,54 @@ struct error_record *symbol_parse(struct parse_ctx *ctx, const struct expr *sym,
res);
} while ((dtype = dtype->basetype));
- return error(&sym->location,
- "Can't parse symbolic %s expressions",
+ dtype = sym->dtype;
+ if (dtype->err) {
+ erec = dtype->err(sym);
+ if (erec)
+ return erec;
+ }
+
+ return error(&sym->location, "Could not parse symbolic %s expression",
sym->dtype->desc);
}
+static struct error_record *__symbol_parse_fuzzy(const struct expr *sym,
+ const struct symbol_table *tbl)
+{
+ const struct symbolic_constant *s;
+ struct string_misspell_state st;
+
+ string_misspell_init(&st);
+
+ for (s = tbl->symbols; s->identifier != NULL; s++) {
+ string_misspell_update(sym->identifier, s->identifier,
+ (void *)s->identifier, &st);
+ }
+
+ if (st.obj) {
+ return error(&sym->location,
+ "Could not parse %s expression; did you you mean `%s`?",
+ sym->dtype->desc, st.obj);
+ }
+
+ return NULL;
+}
+
+static struct error_record *symbol_parse_fuzzy(const struct expr *sym,
+ const struct symbol_table *tbl)
+{
+ struct error_record *erec;
+
+ if (!tbl)
+ return NULL;
+
+ erec = __symbol_parse_fuzzy(sym, tbl);
+ if (erec)
+ return erec;
+
+ return NULL;
+}
+
struct error_record *symbolic_constant_parse(struct parse_ctx *ctx,
const struct expr *sym,
const struct symbol_table *tbl,
@@ -163,8 +211,16 @@ struct error_record *symbolic_constant_parse(struct parse_ctx *ctx,
do {
if (dtype->basetype->parse) {
erec = dtype->basetype->parse(ctx, sym, res);
- if (erec != NULL)
- return erec;
+ if (erec != NULL) {
+ struct error_record *fuzzy_erec;
+
+ fuzzy_erec = symbol_parse_fuzzy(sym, tbl);
+ if (!fuzzy_erec)
+ return erec;
+
+ erec_destroy(erec);
+ return fuzzy_erec;
+ }
if (*res)
return NULL;
goto out;
@@ -321,15 +377,33 @@ static void verdict_type_print(const struct expr *expr, struct output_ctx *octx)
}
}
-static struct error_record *verdict_type_parse(struct parse_ctx *ctx,
- const struct expr *sym,
- struct expr **res)
+static struct error_record *verdict_type_error(const struct expr *sym)
{
- *res = constant_expr_alloc(&sym->location, &string_type,
- BYTEORDER_HOST_ENDIAN,
- (strlen(sym->identifier) + 1) * BITS_PER_BYTE,
- sym->identifier);
- return NULL;
+ /* Skip jump and goto from fuzzy match to provide better error
+ * reporting, fall back to `jump chain' if no clue.
+ */
+ static const char *verdict_array[] = {
+ "continue", "break", "return", "accept", "drop", "queue",
+ "stolen", NULL,
+ };
+ struct string_misspell_state st;
+ int i;
+
+ string_misspell_init(&st);
+
+ for (i = 0; verdict_array[i] != NULL; i++) {
+ string_misspell_update(sym->identifier, verdict_array[i],
+ (void *)verdict_array[i], &st);
+ }
+
+ if (st.obj) {
+ return error(&sym->location, "Could not parse %s; did you mean `%s'?",
+ sym->dtype->desc, st.obj);
+ }
+
+ /* assume user would like to jump to chain as a hint. */
+ return error(&sym->location, "Could not parse %s; did you mean `jump %s'?",
+ sym->dtype->desc, sym->identifier);
}
const struct datatype verdict_type = {
@@ -337,7 +411,7 @@ const struct datatype verdict_type = {
.name = "verdict",
.desc = "netfilter verdict",
.print = verdict_type_print,
- .parse = verdict_type_parse,
+ .err = verdict_type_error,
};
static const struct symbol_table nfproto_tbl = {
@@ -410,6 +484,22 @@ const struct datatype integer_type = {
.parse = integer_type_parse,
};
+static void xinteger_type_print(const struct expr *expr, struct output_ctx *octx)
+{
+ nft_gmp_print(octx, "0x%Zx", expr->value);
+}
+
+/* Alias of integer_type to print raw payload expressions in hexadecimal. */
+const struct datatype xinteger_type = {
+ .type = TYPE_INTEGER,
+ .name = "integer",
+ .desc = "integer",
+ .basetype = &integer_type,
+ .print = xinteger_type_print,
+ .json = integer_type_json,
+ .parse = integer_type_parse,
+};
+
static void string_type_print(const struct expr *expr, struct output_ctx *octx)
{
unsigned int len = div_round_up(expr->len, BITS_PER_BYTE);
@@ -512,27 +602,34 @@ static struct error_record *ipaddr_type_parse(struct parse_ctx *ctx,
const struct expr *sym,
struct expr **res)
{
- struct addrinfo *ai, hints = { .ai_family = AF_INET,
- .ai_socktype = SOCK_DGRAM};
- struct in_addr *addr;
- int err;
+ struct in_addr addr;
+
+ if (nft_input_no_dns(ctx->input)) {
+ if (inet_pton(AF_INET, sym->identifier, &addr) != 1)
+ return error(&sym->location, "Invalid IPv4 address");
+ } else {
+ struct addrinfo *ai, hints = { .ai_family = AF_INET,
+ .ai_socktype = SOCK_DGRAM};
+ int err;
- err = getaddrinfo(sym->identifier, NULL, &hints, &ai);
- if (err != 0)
- return error(&sym->location, "Could not resolve hostname: %s",
- gai_strerror(err));
+ err = getaddrinfo(sym->identifier, NULL, &hints, &ai);
+ if (err != 0)
+ return error(&sym->location, "Could not resolve hostname: %s",
+ gai_strerror(err));
- if (ai->ai_next != NULL) {
+ if (ai->ai_next != NULL) {
+ freeaddrinfo(ai);
+ return error(&sym->location,
+ "Hostname resolves to multiple addresses");
+ }
+ assert(ai->ai_addr->sa_family == AF_INET);
+ addr = ((struct sockaddr_in *) (void *) ai->ai_addr)->sin_addr;
freeaddrinfo(ai);
- return error(&sym->location,
- "Hostname resolves to multiple addresses");
}
- addr = &((struct sockaddr_in *)ai->ai_addr)->sin_addr;
*res = constant_expr_alloc(&sym->location, &ipaddr_type,
BYTEORDER_BIG_ENDIAN,
- sizeof(*addr) * BITS_PER_BYTE, addr);
- freeaddrinfo(ai);
+ sizeof(addr) * BITS_PER_BYTE, &addr);
return NULL;
}
@@ -571,27 +668,35 @@ static struct error_record *ip6addr_type_parse(struct parse_ctx *ctx,
const struct expr *sym,
struct expr **res)
{
- struct addrinfo *ai, hints = { .ai_family = AF_INET6,
- .ai_socktype = SOCK_DGRAM};
- struct in6_addr *addr;
- int err;
+ struct in6_addr addr;
+
+ if (nft_input_no_dns(ctx->input)) {
+ if (inet_pton(AF_INET6, sym->identifier, &addr) != 1)
+ return error(&sym->location, "Invalid IPv6 address");
+ } else {
+ struct addrinfo *ai, hints = { .ai_family = AF_INET6,
+ .ai_socktype = SOCK_DGRAM};
+ int err;
+
+ err = getaddrinfo(sym->identifier, NULL, &hints, &ai);
+ if (err != 0)
+ return error(&sym->location, "Could not resolve hostname: %s",
+ gai_strerror(err));
- err = getaddrinfo(sym->identifier, NULL, &hints, &ai);
- if (err != 0)
- return error(&sym->location, "Could not resolve hostname: %s",
- gai_strerror(err));
+ if (ai->ai_next != NULL) {
+ freeaddrinfo(ai);
+ return error(&sym->location,
+ "Hostname resolves to multiple addresses");
+ }
- if (ai->ai_next != NULL) {
+ assert(ai->ai_addr->sa_family == AF_INET6);
+ addr = ((struct sockaddr_in6 *)(void *)ai->ai_addr)->sin6_addr;
freeaddrinfo(ai);
- return error(&sym->location,
- "Hostname resolves to multiple addresses");
}
- addr = &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr;
*res = constant_expr_alloc(&sym->location, &ip6addr_type,
BYTEORDER_BIG_ENDIAN,
- sizeof(*addr) * BITS_PER_BYTE, addr);
- freeaddrinfo(ai);
+ sizeof(addr) * BITS_PER_BYTE, &addr);
return NULL;
}
@@ -610,12 +715,12 @@ const struct datatype ip6addr_type = {
static void inet_protocol_type_print(const struct expr *expr,
struct output_ctx *octx)
{
- struct protoent *p;
+ if (!nft_output_numeric_proto(octx) &&
+ mpz_cmp_ui(expr->value, UINT8_MAX) <= 0) {
+ char name[NFT_PROTONAME_MAXSIZE];
- if (!nft_output_numeric_proto(octx)) {
- p = getprotobynumber(mpz_get_uint8(expr->value));
- if (p != NULL) {
- nft_print(octx, "%s", p->p_name);
+ if (nft_getprotobynumber(mpz_get_uint8(expr->value), name, sizeof(name))) {
+ nft_print(octx, "%s", name);
return;
}
}
@@ -624,15 +729,15 @@ static void inet_protocol_type_print(const struct expr *expr,
static void inet_protocol_type_describe(struct output_ctx *octx)
{
- struct protoent *p;
uint8_t protonum;
for (protonum = 0; protonum < UINT8_MAX; protonum++) {
- p = getprotobynumber(protonum);
- if (!p)
+ char name[NFT_PROTONAME_MAXSIZE];
+
+ if (!nft_getprotobynumber(protonum, name, sizeof(name)))
continue;
- nft_print(octx, "\t%-30s\t%u\n", p->p_name, protonum);
+ nft_print(octx, "\t%-30s\t%u\n", name, protonum);
}
}
@@ -640,7 +745,6 @@ static struct error_record *inet_protocol_type_parse(struct parse_ctx *ctx,
const struct expr *sym,
struct expr **res)
{
- struct protoent *p;
uint8_t proto;
uintmax_t i;
char *end;
@@ -653,11 +757,13 @@ static struct error_record *inet_protocol_type_parse(struct parse_ctx *ctx,
proto = i;
} else {
- p = getprotobyname(sym->identifier);
- if (p == NULL)
+ int r;
+
+ r = nft_getprotobyname(sym->identifier);
+ if (r < 0)
return error(&sym->location, "Could not resolve protocol name");
- proto = p->p_proto;
+ proto = r;
}
*res = constant_expr_alloc(&sym->location, &inet_protocol_type,
@@ -681,17 +787,18 @@ const struct datatype inet_protocol_type = {
static void inet_service_print(const struct expr *expr, struct output_ctx *octx)
{
uint16_t port = mpz_get_be16(expr->value);
- const struct servent *s = getservbyport(port, NULL);
+ char name[NFT_SERVNAME_MAXSIZE];
- if (s == NULL)
+ if (!nft_getservbyport(port, NULL, name, sizeof(name)))
nft_print(octx, "%hu", ntohs(port));
else
- nft_print(octx, "\"%s\"", s->s_name);
+ nft_print(octx, "\"%s\"", name);
}
void inet_service_type_print(const struct expr *expr, struct output_ctx *octx)
{
- if (nft_output_service(octx)) {
+ if (nft_output_service(octx) &&
+ mpz_cmp_ui(expr->value, UINT16_MAX) <= 0) {
inet_service_print(expr, octx);
return;
}
@@ -721,7 +828,12 @@ static struct error_record *inet_service_type_parse(struct parse_ctx *ctx,
return error(&sym->location, "Could not resolve service: %s",
gai_strerror(err));
- port = ((struct sockaddr_in *)ai->ai_addr)->sin_port;
+ if (ai->ai_addr->sa_family == AF_INET) {
+ port = ((struct sockaddr_in *)(void *)ai->ai_addr)->sin_port;
+ } else {
+ assert(ai->ai_addr->sa_family == AF_INET6);
+ port = ((struct sockaddr_in6 *)(void *)ai->ai_addr)->sin6_port;
+ }
freeaddrinfo(ai);
}
@@ -745,19 +857,48 @@ const struct datatype inet_service_type = {
#define RT_SYM_TAB_INITIAL_SIZE 16
+static FILE *open_iproute2_db(const char *filename, char **path)
+{
+ FILE *ret;
+
+ if (filename[0] == '/')
+ return fopen(filename, "r");
+
+ if (asprintf(path, "/etc/iproute2/%s", filename) == -1)
+ goto fail;
+
+ ret = fopen(*path, "r");
+ if (ret)
+ return ret;
+
+ free(*path);
+ if (asprintf(path, "/usr/share/iproute2/%s", filename) == -1)
+ goto fail;
+
+ ret = fopen(*path, "r");
+ if (ret)
+ return ret;
+
+ free(*path);
+fail:
+ *path = NULL;
+ return NULL;
+}
+
struct symbol_table *rt_symbol_table_init(const char *filename)
{
+ char buf[512], namebuf[512], *p, *path = NULL;
struct symbolic_constant s;
struct symbol_table *tbl;
unsigned int size, nelems, val;
- char buf[512], namebuf[512], *p;
FILE *f;
size = RT_SYM_TAB_INITIAL_SIZE;
tbl = xmalloc(sizeof(*tbl) + size * sizeof(s));
+ tbl->base = BASE_DECIMAL;
nelems = 0;
- f = fopen(filename, "r");
+ f = open_iproute2_db(filename, &path);
if (f == NULL)
goto out;
@@ -767,12 +908,15 @@ struct symbol_table *rt_symbol_table_init(const char *filename)
p++;
if (*p == '#' || *p == '\n' || *p == '\0')
continue;
- if (sscanf(p, "0x%x %511s\n", &val, namebuf) != 2 &&
- sscanf(p, "0x%x %511s #", &val, namebuf) != 2 &&
- sscanf(p, "%u %511s\n", &val, namebuf) != 2 &&
- sscanf(p, "%u %511s #", &val, namebuf) != 2) {
+ if (sscanf(p, "0x%x %511s\n", &val, namebuf) == 2 ||
+ sscanf(p, "0x%x %511s #", &val, namebuf) == 2) {
+ tbl->base = BASE_HEXADECIMAL;
+ } else if (sscanf(p, "%u %511s\n", &val, namebuf) == 2 ||
+ sscanf(p, "%u %511s #", &val, namebuf) == 2) {
+ tbl->base = BASE_DECIMAL;
+ } else {
fprintf(stderr, "iproute database '%s' corrupted\n",
- filename);
+ path ?: filename);
break;
}
@@ -789,6 +933,8 @@ struct symbol_table *rt_symbol_table_init(const char *filename)
fclose(f);
out:
+ if (path)
+ free(path);
tbl->symbols[nelems] = SYMBOL_LIST_END;
return tbl;
}
@@ -798,13 +944,40 @@ 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 rt_symbol_table_describe(struct output_ctx *octx, const char *name,
+ const struct symbol_table *tbl,
+ const struct datatype *type)
+{
+ char *path = NULL;
+ FILE *f;
+
+ if (!tbl || !tbl->symbols[0].identifier)
+ return;
+
+ f = open_iproute2_db(name, &path);
+ if (f)
+ fclose(f);
+ if (!path && asprintf(&path, "%s%s",
+ name[0] == '/' ? "" : "unknown location of ",
+ name) < 0)
+ return;
+
+ nft_print(octx, "\npre-defined symbolic constants from %s ", path);
+ if (tbl->base == BASE_DECIMAL)
+ nft_print(octx, "(in decimal):\n");
+ else
+ nft_print(octx, "(in hexadecimal):\n");
+ symbol_table_print(tbl, type, type->byteorder, octx);
+ free(path);
}
void mark_table_init(struct nft_ctx *ctx)
{
- ctx->output.tbl.mark = rt_symbol_table_init("/etc/iproute2/rt_marks");
+ ctx->output.tbl.mark = rt_symbol_table_init("rt_marks");
}
void mark_table_exit(struct nft_ctx *ctx)
@@ -824,10 +997,17 @@ static struct error_record *mark_type_parse(struct parse_ctx *ctx,
return symbolic_constant_parse(ctx, sym, ctx->tbl->mark, res);
}
+static void mark_type_describe(struct output_ctx *octx)
+{
+ rt_symbol_table_describe(octx, "rt_marks",
+ octx->tbl.mark, &mark_type);
+}
+
const struct datatype mark_type = {
.type = TYPE_MARK,
.name = "mark",
.desc = "packet mark",
+ .describe = mark_type_describe,
.size = 4 * BITS_PER_BYTE,
.byteorder = BYTEORDER_HOST_ENDIAN,
.basetype = &integer_type,
@@ -835,9 +1015,9 @@ const struct datatype mark_type = {
.print = mark_type_print,
.json = mark_type_json,
.parse = mark_type_parse,
- .flags = DTYPE_F_PREFIX,
};
+/* symbol table for private datatypes for reject statement. */
static const struct symbol_table icmp_code_tbl = {
.base = BASE_DECIMAL,
.symbols = {
@@ -853,16 +1033,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 = {
@@ -876,16 +1057,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 = {
@@ -897,6 +1079,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",
@@ -904,12 +1140,13 @@ 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)
{
uint64_t days, hours, minutes, seconds;
+ bool printed = false;
if (nft_output_seconds(octx)) {
nft_print(octx, "%" PRIu64 "s", ms / 1000);
@@ -928,16 +1165,29 @@ void time_print(uint64_t ms, struct output_ctx *octx)
seconds = ms / 1000;
ms %= 1000;
- if (days > 0)
+ if (days > 0) {
nft_print(octx, "%" PRIu64 "d", days);
- if (hours > 0)
+ printed = true;
+ }
+ if (hours > 0) {
nft_print(octx, "%" PRIu64 "h", hours);
- if (minutes > 0)
+ printed = true;
+ }
+ if (minutes > 0) {
nft_print(octx, "%" PRIu64 "m", minutes);
- if (seconds > 0)
+ printed = true;
+ }
+ if (seconds > 0) {
nft_print(octx, "%" PRIu64 "s", seconds);
- if (ms > 0)
+ printed = true;
+ }
+ if (ms > 0) {
nft_print(octx, "%" PRIu64 "ms", ms);
+ printed = true;
+ }
+
+ if (!printed)
+ nft_print(octx, "0s");
}
enum {
@@ -1052,6 +1302,7 @@ static struct error_record *time_type_parse(struct parse_ctx *ctx,
struct expr **res)
{
struct error_record *erec;
+ uint32_t s32;
uint64_t s;
erec = time_parse(&sym->location, sym->identifier, &s);
@@ -1061,9 +1312,10 @@ static struct error_record *time_type_parse(struct parse_ctx *ctx,
if (s > UINT32_MAX)
return error(&sym->location, "value too large");
+ s32 = s;
*res = constant_expr_alloc(&sym->location, &time_type,
BYTEORDER_HOST_ENDIAN,
- sizeof(uint32_t) * BITS_PER_BYTE, &s);
+ sizeof(uint32_t) * BITS_PER_BYTE, &s32);
return NULL;
}
@@ -1072,7 +1324,7 @@ const struct datatype time_type = {
.name = "time",
.desc = "relative time",
.byteorder = BYTEORDER_HOST_ENDIAN,
- .size = 8 * BITS_PER_BYTE,
+ .size = 4 * BITS_PER_BYTE,
.basetype = &integer_type,
.print = time_type_print,
.json = time_type_json,
@@ -1087,17 +1339,18 @@ static struct error_record *concat_type_parse(struct parse_ctx *ctx,
sym->dtype->desc);
}
-static struct datatype *dtype_alloc(void)
+static struct datatype *datatype_alloc(void)
{
struct datatype *dtype;
dtype = xzalloc(sizeof(*dtype));
dtype->flags = DTYPE_F_ALLOC;
+ dtype->refcnt = 1;
return dtype;
}
-struct datatype *datatype_get(const struct datatype *ptr)
+const struct datatype *datatype_get(const struct datatype *ptr)
{
struct datatype *dtype = (struct datatype *)ptr;
@@ -1110,24 +1363,31 @@ struct datatype *datatype_get(const struct datatype *ptr)
return dtype;
}
+void __datatype_set(struct expr *expr, const struct datatype *dtype)
+{
+ const struct datatype *dtype_free;
+
+ dtype_free = expr->dtype;
+ expr->dtype = dtype;
+ datatype_free(dtype_free);
+}
+
void datatype_set(struct expr *expr, const struct datatype *dtype)
{
- if (dtype == expr->dtype)
- return;
- datatype_free(expr->dtype);
- expr->dtype = datatype_get(dtype);
+ if (dtype != expr->dtype)
+ __datatype_set(expr, datatype_get(dtype));
}
-static struct datatype *dtype_clone(const struct datatype *orig_dtype)
+struct datatype *datatype_clone(const struct datatype *orig_dtype)
{
struct datatype *dtype;
- dtype = xzalloc(sizeof(*dtype));
+ dtype = xmalloc(sizeof(*dtype));
*dtype = *orig_dtype;
dtype->name = xstrdup(orig_dtype->name);
dtype->desc = xstrdup(orig_dtype->desc);
dtype->flags = DTYPE_F_ALLOC | orig_dtype->flags;
- dtype->refcnt = 0;
+ dtype->refcnt = 1;
return dtype;
}
@@ -1140,12 +1400,15 @@ void datatype_free(const struct datatype *ptr)
return;
if (!(dtype->flags & DTYPE_F_ALLOC))
return;
+
+ assert(dtype->refcnt != 0);
+
if (--dtype->refcnt > 0)
return;
- xfree(dtype->name);
- xfree(dtype->desc);
- xfree(dtype);
+ free_const(dtype->name);
+ free_const(dtype->desc);
+ free(dtype);
}
const struct datatype *concat_type_alloc(uint32_t type)
@@ -1174,7 +1437,7 @@ const struct datatype *concat_type_alloc(uint32_t type)
}
strncat(desc, ")", sizeof(desc) - strlen(desc) - 1);
- dtype = dtype_alloc();
+ dtype = datatype_alloc();
dtype->type = type;
dtype->size = size;
dtype->subtypes = subtypes;
@@ -1186,15 +1449,15 @@ const struct datatype *concat_type_alloc(uint32_t type)
}
const struct datatype *set_datatype_alloc(const struct datatype *orig_dtype,
- unsigned int byteorder)
+ enum byteorder byteorder)
{
struct datatype *dtype;
/* Restrict dynamic datatype allocation to generic integer datatype. */
if (orig_dtype != &integer_type)
- return orig_dtype;
+ return datatype_get(orig_dtype);
- dtype = dtype_clone(orig_dtype);
+ dtype = datatype_clone(orig_dtype);
dtype->byteorder = byteorder;
return dtype;
@@ -1342,10 +1605,10 @@ const struct datatype policy_type = {
#define SYSFS_CGROUPSV2_PATH "/sys/fs/cgroup"
-static const char *cgroupv2_get_path(const char *path, uint64_t id)
+static char *cgroupv2_get_path(const char *path, uint64_t id)
{
- const char *cgroup_path = NULL;
char dent_name[PATH_MAX + 1];
+ char *cgroup_path = NULL;
struct dirent *dent;
struct stat st;
DIR *d;
@@ -1383,7 +1646,7 @@ static void cgroupv2_type_print(const struct expr *expr,
struct output_ctx *octx)
{
uint64_t id = mpz_get_uint64(expr->value);
- const char *cgroup_path;
+ char *cgroup_path;
cgroup_path = cgroupv2_get_path(SYSFS_CGROUPSV2_PATH, id);
if (cgroup_path)
@@ -1392,7 +1655,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/dccpopt.c b/src/dccpopt.c
new file mode 100644
index 00000000..ebb645a9
--- /dev/null
+++ b/src/dccpopt.c
@@ -0,0 +1,277 @@
+#include <nft.h>
+
+#include <stddef.h>
+
+#include <datatype.h>
+#include <dccpopt.h>
+#include <expression.h>
+#include <nftables.h>
+#include <utils.h>
+
+#define PHT(__token, __offset, __len) \
+ PROTO_HDR_TEMPLATE(__token, &integer_type, BYTEORDER_BIG_ENDIAN, \
+ __offset, __len)
+
+static const struct proto_hdr_template dccpopt_unknown_template =
+ PROTO_HDR_TEMPLATE("unknown", &invalid_type, BYTEORDER_INVALID, 0, 0);
+
+/*
+ * Option DCCP- Section
+ * Type Length Meaning Data? Reference
+ * ---- ------ ------- ----- ---------
+ * 0 1 Padding Y 5.8.1
+ * 1 1 Mandatory N 5.8.2
+ * 2 1 Slow Receiver Y 11.6
+ * 3-31 1 Reserved
+ * 32 variable Change L N 6.1
+ * 33 variable Confirm L N 6.2
+ * 34 variable Change R N 6.1
+ * 35 variable Confirm R N 6.2
+ * 36 variable Init Cookie N 8.1.4
+ * 37 3-8 NDP Count Y 7.7
+ * 38 variable Ack Vector [Nonce 0] N 11.4
+ * 39 variable Ack Vector [Nonce 1] N 11.4
+ * 40 variable Data Dropped N 11.7
+ * 41 6 Timestamp Y 13.1
+ * 42 6/8/10 Timestamp Echo Y 13.3
+ * 43 4/6 Elapsed Time N 13.2
+ * 44 6 Data Checksum Y 9.3
+ * 45-127 variable Reserved
+ * 128-255 variable CCID-specific options - 10.3
+ */
+
+static const struct exthdr_desc dccpopt_padding = {
+ .name = "padding",
+ .type = DCCPOPT_PADDING,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_mandatory = {
+ .name = "mandatory",
+ .type = DCCPOPT_MANDATORY,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_slow_receiver = {
+ .name = "slow_receiver",
+ .type = DCCPOPT_SLOW_RECEIVER,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_reserved_short = {
+ .name = "reserved_short",
+ .type = DCCPOPT_RESERVED_SHORT,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_change_l = {
+ .name = "change_l",
+ .type = DCCPOPT_CHANGE_L,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8)
+ },
+};
+
+static const struct exthdr_desc dccpopt_confirm_l = {
+ .name = "confirm_l",
+ .type = DCCPOPT_CONFIRM_L,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_change_r = {
+ .name = "change_r",
+ .type = DCCPOPT_CHANGE_R,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_confirm_r = {
+ .name = "confirm_r",
+ .type = DCCPOPT_CONFIRM_R,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_init_cookie = {
+ .name = "init_cookie",
+ .type = DCCPOPT_INIT_COOKIE,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_ndp_count = {
+ .name = "ndp_count",
+ .type = DCCPOPT_NDP_COUNT,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_ack_vector_nonce_0 = {
+ .name = "ack_vector_nonce_0",
+ .type = DCCPOPT_ACK_VECTOR_NONCE_0,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_ack_vector_nonce_1 = {
+ .name = "ack_vector_nonce_1",
+ .type = DCCPOPT_ACK_VECTOR_NONCE_1,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_data_dropped = {
+ .name = "data_dropped",
+ .type = DCCPOPT_DATA_DROPPED,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_timestamp = {
+ .name = "timestamp",
+ .type = DCCPOPT_TIMESTAMP,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_timestamp_echo = {
+ .name = "timestamp_echo",
+ .type = DCCPOPT_TIMESTAMP_ECHO,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_elapsed_time = {
+ .name = "elapsed_time",
+ .type = DCCPOPT_ELAPSED_TIME,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_data_checksum = {
+ .name = "data_checksum",
+ .type = DCCPOPT_DATA_CHECKSUM,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_reserved_long = {
+ .name = "reserved_long",
+ .type = DCCPOPT_RESERVED_LONG,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+static const struct exthdr_desc dccpopt_ccid_specific = {
+ .name = "ccid_specific",
+ .type = DCCPOPT_CCID_SPECIFIC,
+ .templates = {
+ [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8),
+ },
+};
+
+const struct exthdr_desc *dccpopt_protocols[1 + UINT8_MAX] = {
+ [DCCPOPT_PADDING] = &dccpopt_padding,
+ [DCCPOPT_MANDATORY] = &dccpopt_mandatory,
+ [DCCPOPT_SLOW_RECEIVER] = &dccpopt_slow_receiver,
+ [DCCPOPT_RESERVED_SHORT] = &dccpopt_reserved_short,
+ [DCCPOPT_CHANGE_L] = &dccpopt_change_l,
+ [DCCPOPT_CONFIRM_L] = &dccpopt_confirm_l,
+ [DCCPOPT_CHANGE_R] = &dccpopt_change_r,
+ [DCCPOPT_CONFIRM_R] = &dccpopt_confirm_r,
+ [DCCPOPT_INIT_COOKIE] = &dccpopt_init_cookie,
+ [DCCPOPT_NDP_COUNT] = &dccpopt_ndp_count,
+ [DCCPOPT_ACK_VECTOR_NONCE_0] = &dccpopt_ack_vector_nonce_0,
+ [DCCPOPT_ACK_VECTOR_NONCE_1] = &dccpopt_ack_vector_nonce_1,
+ [DCCPOPT_DATA_DROPPED] = &dccpopt_data_dropped,
+ [DCCPOPT_TIMESTAMP] = &dccpopt_timestamp,
+ [DCCPOPT_TIMESTAMP_ECHO] = &dccpopt_timestamp_echo,
+ [DCCPOPT_ELAPSED_TIME] = &dccpopt_elapsed_time,
+ [DCCPOPT_DATA_CHECKSUM] = &dccpopt_data_checksum,
+ [DCCPOPT_RESERVED_LONG] = &dccpopt_reserved_long,
+ [DCCPOPT_CCID_SPECIFIC] = &dccpopt_ccid_specific,
+};
+
+const struct exthdr_desc *
+dccpopt_find_desc(uint8_t type)
+{
+ enum dccpopt_types proto_idx =
+ 3 <= type && type <= 31 ? DCCPOPT_RESERVED_SHORT :
+ 45 <= type && type <= 127 ? DCCPOPT_RESERVED_LONG :
+ 128 <= type ? DCCPOPT_CCID_SPECIFIC : type;
+
+ return dccpopt_protocols[proto_idx];
+}
+
+struct expr *
+dccpopt_expr_alloc(const struct location *loc, uint8_t type)
+{
+ const struct proto_hdr_template *tmpl;
+ const struct exthdr_desc *desc;
+ struct expr *expr;
+
+ desc = dccpopt_find_desc(type);
+ tmpl = &desc->templates[DCCPOPT_FIELD_TYPE];
+
+ expr = expr_alloc(loc, EXPR_EXTHDR, tmpl->dtype,
+ BYTEORDER_BIG_ENDIAN, BITS_PER_BYTE);
+ expr->exthdr.desc = desc;
+ expr->exthdr.tmpl = tmpl;
+ expr->exthdr.offset = tmpl->offset;
+ expr->exthdr.raw_type = type;
+ expr->exthdr.flags = NFT_EXTHDR_F_PRESENT;
+ expr->exthdr.op = NFT_EXTHDR_OP_DCCP;
+
+ return expr;
+}
+
+void
+dccpopt_init_raw(struct expr *expr, uint8_t type, unsigned int offset,
+ unsigned int len)
+{
+ const struct proto_hdr_template *tmpl;
+ const struct exthdr_desc *desc;
+
+ assert(expr->etype == EXPR_EXTHDR);
+
+ desc = dccpopt_find_desc(type);
+ tmpl = &desc->templates[DCCPOPT_FIELD_TYPE];
+
+ expr->len = len;
+ datatype_set(expr, &boolean_type);
+
+ expr->exthdr.offset = offset;
+ expr->exthdr.desc = desc;
+ expr->exthdr.flags = NFT_EXTHDR_F_PRESENT;
+ expr->exthdr.op = NFT_EXTHDR_OP_DCCP;
+
+ /* Make sure that it's the right template based on offset and
+ * len
+ */
+ if (tmpl->offset != offset || tmpl->len != len)
+ expr->exthdr.tmpl = &dccpopt_unknown_template;
+ else
+ expr->exthdr.tmpl = tmpl;
+}
diff --git a/src/erec.c b/src/erec.c
index 5c3351a5..fe66abbe 100644
--- a/src/erec.c
+++ b/src/erec.c
@@ -8,12 +8,10 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
-#define _GNU_SOURCE
-#include <config.h>
+#include <nft.h>
+
#include <stdio.h>
-#include <string.h>
#include <stdarg.h>
-#include <stdlib.h>
#include <netlink.h>
#include <gmputil.h>
@@ -45,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)))
@@ -81,11 +79,58 @@ struct error_record *erec_create(enum error_record_types type,
return erec;
}
+void print_location(FILE *f, const struct input_descriptor *indesc,
+ const struct location *loc)
+{
+ const struct input_descriptor *tmp;
+ const struct location *iloc;
+
+ if (indesc->location.indesc != NULL) {
+ const char *prefix = "In file included from";
+ iloc = &indesc->location;
+ for (tmp = iloc->indesc;
+ tmp != NULL && tmp->type != INDESC_INTERNAL;
+ tmp = iloc->indesc) {
+ fprintf(f, "%s %s:%u:%u-%u:\n", prefix,
+ tmp->name,
+ iloc->first_line, iloc->first_column,
+ iloc->last_column);
+ prefix = " from";
+ iloc = &tmp->location;
+ }
+ }
+ if (indesc->type != INDESC_BUFFER && indesc->name) {
+ fprintf(f, "%s:%u:%u-%u: ", indesc->name,
+ loc->first_line, loc->first_column,
+ loc->last_column);
+ }
+}
+
+const char *line_location(const struct input_descriptor *indesc,
+ const struct location *loc, char *buf, size_t bufsiz)
+{
+ const char *line = NULL;
+ FILE *f;
+
+ f = fopen(indesc->name, "r");
+ if (!f)
+ return NULL;
+
+ if (!fseek(f, loc->line_offset, SEEK_SET) &&
+ fread(buf, 1, bufsiz - 1, f) > 0) {
+ *strchrnul(buf, '\n') = '\0';
+ line = buf;
+ }
+ fclose(f);
+
+ return line;
+}
+
void erec_print(struct output_ctx *octx, const struct error_record *erec,
unsigned int debug_mask)
{
- const struct location *loc = erec->locations, *iloc;
- const struct input_descriptor *indesc = loc->indesc, *tmp;
+ const struct location *loc = erec->locations;
+ const struct input_descriptor *indesc = loc->indesc;
const char *line = NULL;
char buf[1024] = {};
char *pbuf = NULL;
@@ -99,17 +144,13 @@ void erec_print(struct output_ctx *octx, const struct error_record *erec,
line = indesc->data;
*strchrnul(line, '\n') = '\0';
break;
+ case INDESC_STDIN:
+ line = indesc->data;
+ line += loc->line_offset;
+ *strchrnul(line, '\n') = '\0';
+ break;
case INDESC_FILE:
- f = fopen(indesc->name, "r");
- if (!f)
- break;
-
- if (!fseek(f, loc->line_offset, SEEK_SET) &&
- fread(buf, 1, sizeof(buf) - 1, f) > 0) {
- *strchrnul(buf, '\n') = '\0';
- line = buf;
- }
- fclose(f);
+ line = line_location(indesc, loc, buf, sizeof(buf));
break;
case INDESC_INTERNAL:
case INDESC_NETLINK:
@@ -127,29 +168,15 @@ void erec_print(struct output_ctx *octx, const struct error_record *erec,
fprintf(f, "%s\n", erec->msg);
for (l = 0; l < (int)erec->num_locations; l++) {
loc = &erec->locations[l];
+ if (!loc->nle)
+ continue;
netlink_dump_expr(loc->nle, f, debug_mask);
}
return;
}
- if (indesc->location.indesc != NULL) {
- const char *prefix = "In file included from";
- iloc = &indesc->location;
- for (tmp = iloc->indesc;
- tmp != NULL && tmp->type != INDESC_INTERNAL;
- tmp = iloc->indesc) {
- fprintf(f, "%s %s:%u:%u-%u:\n", prefix,
- tmp->name,
- iloc->first_line, iloc->first_column,
- iloc->last_column);
- prefix = " from";
- iloc = &tmp->location;
- }
- }
- if (indesc->name != NULL)
- fprintf(f, "%s:%u:%u-%u: ", indesc->name,
- loc->first_line, loc->first_column,
- loc->last_column);
+ print_location(f, indesc, loc);
+
if (error_record_names[erec->type])
fprintf(f, "%s: ", error_record_names[erec->type]);
fprintf(f, "%s\n", erec->msg);
@@ -176,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 0bc799eb..1682ba58 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -8,11 +8,10 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
#include <arpa/inet.h>
#include <linux/netfilter.h>
#include <linux/netfilter_arp.h>
@@ -29,6 +28,7 @@
#include <expression.h>
#include <statement.h>
+#include <intervals.h>
#include <netlink.h>
#include <time.h>
#include <rule.h>
@@ -38,6 +38,13 @@
#include <utils.h>
#include <xt.h>
+struct proto_ctx *eval_proto_ctx(struct eval_ctx *ctx)
+{
+ uint8_t idx = ctx->inner_desc ? 1 : 0;
+
+ return &ctx->_pctx[idx];
+}
+
static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr);
static const char * const byteorder_names[] = {
@@ -67,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;
@@ -74,7 +108,7 @@ static void key_fix_dtype_byteorder(struct expr *key)
if (dtype->byteorder == key->byteorder)
return;
- datatype_set(key, set_datatype_alloc(dtype, key->byteorder));
+ __datatype_set(key, set_datatype_alloc(dtype, key->byteorder));
}
static int set_evaluate(struct eval_ctx *ctx, struct set *set);
@@ -82,7 +116,8 @@ static struct expr *implicit_set_declaration(struct eval_ctx *ctx,
const char *name,
struct expr *key,
struct expr *data,
- struct expr *expr)
+ struct expr *expr,
+ uint32_t flags)
{
struct cmd *cmd;
struct set *set;
@@ -92,17 +127,25 @@ static struct expr *implicit_set_declaration(struct eval_ctx *ctx,
key_fix_dtype_byteorder(key);
set = set_alloc(&expr->location);
- set->flags = NFT_SET_ANONYMOUS | expr->set_flags;
+ set->flags = expr->set_flags | flags;
set->handle.set.name = xstrdup(name);
set->key = key;
set->data = data;
set->init = expr;
set->automerge = set->flags & NFT_SET_INTERVAL;
+ handle_merge(&set->handle, &ctx->cmd->handle);
+
+ if (set_evaluate(ctx, set) < 0) {
+ if (set->flags & NFT_SET_MAP)
+ set->init = NULL;
+ set_free(set);
+ return NULL;
+ }
+
if (ctx->table != NULL)
list_add_tail(&set->list, &ctx->table->sets);
else {
- handle_merge(&set->handle, &ctx->cmd->handle);
memset(&h, 0, sizeof(h));
handle_merge(&h, &set->handle);
h.set.location = expr->location;
@@ -111,8 +154,6 @@ static struct expr *implicit_set_declaration(struct eval_ctx *ctx,
list_add_tail(&cmd->list, &ctx->cmd->list);
}
- set_evaluate(ctx, set);
-
return set_ref_expr_alloc(&expr->location, set);
}
@@ -138,6 +179,7 @@ static enum ops byteorder_conversion_op(struct expr *expr,
static int byteorder_conversion(struct eval_ctx *ctx, struct expr **expr,
enum byteorder byteorder)
{
+ enum datatypes basetype;
enum ops op;
assert(!expr_is_constant(*expr) || expr_is_singleton(*expr));
@@ -146,16 +188,54 @@ static int byteorder_conversion(struct eval_ctx *ctx, struct expr **expr,
return 0;
/* Conversion for EXPR_CONCAT is handled for single composing ranges */
- if ((*expr)->etype == EXPR_CONCAT)
+ if ((*expr)->etype == EXPR_CONCAT) {
+ struct expr *i, *next, *unary;
+
+ list_for_each_entry_safe(i, next, &(*expr)->expressions, list) {
+ if (i->byteorder == BYTEORDER_BIG_ENDIAN)
+ continue;
+
+ basetype = expr_basetype(i)->type;
+ if (basetype == TYPE_STRING)
+ continue;
+
+ assert(basetype == TYPE_INTEGER);
+
+ 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);
+ }
+ }
+ }
+
return 0;
+ }
- if (expr_basetype(*expr)->type != TYPE_INTEGER)
+ basetype = expr_basetype(*expr)->type;
+ switch (basetype) {
+ case TYPE_INTEGER:
+ break;
+ case TYPE_STRING:
+ return 0;
+ default:
return expr_error(ctx->msgs, *expr,
- "Byteorder mismatch: expected %s, got %s",
+ "Byteorder mismatch: %s expected %s, %s got %s",
byteorder_names[byteorder],
+ expr_name(*expr),
byteorder_names[(*expr)->byteorder]);
+ }
- if (expr_is_constant(*expr) || (*expr)->len / BITS_PER_BYTE < 2)
+ if (expr_is_constant(*expr) || div_round_up((*expr)->len, BITS_PER_BYTE) < 2)
(*expr)->byteorder = byteorder;
else {
op = byteorder_conversion_op(*expr, byteorder);
@@ -176,7 +256,7 @@ static int table_not_found(struct eval_ctx *ctx)
"%s", strerror(ENOENT));
return cmd_error(ctx, &ctx->cmd->handle.table.location,
- "%s; did you mean table ‘%s’ in family %s?",
+ "%s; did you mean table '%s' in family %s?",
strerror(ENOENT), table->handle.table.name,
family2str(table->handle.family));
}
@@ -192,7 +272,7 @@ static int chain_not_found(struct eval_ctx *ctx)
"%s", strerror(ENOENT));
return cmd_error(ctx, &ctx->cmd->handle.chain.location,
- "%s; did you mean chain ‘%s’ in table %s ‘%s’?",
+ "%s; did you mean chain '%s' in table %s '%s'?",
strerror(ENOENT), chain->handle.chain.name,
family2str(chain->handle.family),
table->handle.table.name);
@@ -209,7 +289,7 @@ static int set_not_found(struct eval_ctx *ctx, const struct location *loc,
return cmd_error(ctx, loc, "%s", strerror(ENOENT));
return cmd_error(ctx, loc,
- "%s; did you mean %s ‘%s’ in table %s ‘%s’?",
+ "%s; did you mean %s '%s' in table %s '%s'?",
strerror(ENOENT),
set_is_map(set->flags) ? "map" : "set",
set->handle.set.name,
@@ -228,7 +308,7 @@ static int flowtable_not_found(struct eval_ctx *ctx, const struct location *loc,
return cmd_error(ctx, loc, "%s", strerror(ENOENT));
return cmd_error(ctx, loc,
- "%s; did you mean flowtable ‘%s’ in table %s ‘%s’?",
+ "%s; did you mean flowtable '%s' in table %s '%s'?",
strerror(ENOENT), ft->handle.flowtable.name,
family2str(ft->handle.family),
table->handle.table.name);
@@ -239,7 +319,10 @@ static int flowtable_not_found(struct eval_ctx *ctx, const struct location *loc,
*/
static int expr_evaluate_symbol(struct eval_ctx *ctx, struct expr **expr)
{
- struct parse_ctx parse_ctx = { .tbl = &ctx->nft->output.tbl, };
+ struct parse_ctx parse_ctx = {
+ .tbl = &ctx->nft->output.tbl,
+ .input = &ctx->nft->input,
+ };
struct error_record *erec;
struct table *table;
struct set *set;
@@ -329,15 +412,18 @@ static int expr_evaluate_string(struct eval_ctx *ctx, struct expr **exprp)
*exprp = value;
return 0;
}
+
+ data[datalen] = 0;
value = constant_expr_alloc(&expr->location, ctx->ectx.dtype,
BYTEORDER_HOST_ENDIAN,
- datalen * BITS_PER_BYTE, data);
+ expr->len, data);
prefix = prefix_expr_alloc(&expr->location, value,
datalen * BITS_PER_BYTE);
datatype_set(prefix, ctx->ectx.dtype);
prefix->flags |= EXPR_F_CONSTANT;
prefix->byteorder = BYTEORDER_HOST_ENDIAN;
+ prefix->len = expr->len;
expr_free(expr);
*exprp = prefix;
@@ -348,6 +434,7 @@ static int expr_evaluate_integer(struct eval_ctx *ctx, struct expr **exprp)
{
struct expr *expr = *exprp;
char *valstr, *rangestr;
+ uint32_t masklen;
mpz_t mask;
if (ctx->ectx.maxval > 0 &&
@@ -356,24 +443,29 @@ static int expr_evaluate_integer(struct eval_ctx *ctx, struct expr **exprp)
expr_error(ctx->msgs, expr,
"Value %s exceeds valid range 0-%u",
valstr, ctx->ectx.maxval);
- free(valstr);
+ nft_gmp_free(valstr);
return -1;
}
- mpz_init_bitmask(mask, ctx->ectx.len);
+ if (ctx->stmt_len > ctx->ectx.len)
+ masklen = ctx->stmt_len;
+ else
+ masklen = ctx->ectx.len;
+
+ mpz_init_bitmask(mask, masklen);
if (mpz_cmp(expr->value, mask) > 0) {
valstr = mpz_get_str(NULL, 10, expr->value);
rangestr = mpz_get_str(NULL, 10, mask);
expr_error(ctx->msgs, expr,
"Value %s exceeds valid range 0-%s",
valstr, rangestr);
- free(valstr);
- free(rangestr);
+ nft_gmp_free(valstr);
+ nft_gmp_free(rangestr);
mpz_clear(mask);
return -1;
}
expr->byteorder = ctx->ectx.byteorder;
- expr->len = ctx->ectx.len;
+ expr->len = masklen;
mpz_clear(mask);
return 0;
}
@@ -405,20 +497,34 @@ 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,
- 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;
const struct proto_desc *desc = NULL;
struct expr *dep, *left, *right;
+ struct proto_ctx *pctx;
struct stmt *stmt;
assert(expr->payload.base == PROTO_BASE_LL_HDR);
- desc = ctx->pctx.protocol[base].desc;
+ pctx = eval_proto_ctx(ctx);
+ desc = pctx->protocol[base].desc;
tmpl = &desc->templates[desc->protocol_key];
left = payload_expr_alloc(&expr->location, desc, desc->protocol_key);
@@ -428,10 +534,13 @@ 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");
+ if (ctx->inner_desc)
+ left->payload.inner_desc = ctx->inner_desc;
+
*res = stmt;
return 0;
}
@@ -452,10 +561,11 @@ static uint8_t expr_offset_shift(const struct expr *expr, unsigned int offset,
return shift;
}
-static void expr_evaluate_bits(struct eval_ctx *ctx, struct expr **exprp)
+static int expr_evaluate_bits(struct eval_ctx *ctx, struct expr **exprp)
{
struct expr *expr = *exprp, *and, *mask, *rshift, *off;
unsigned masklen, len = expr->len, extra_len = 0;
+ enum byteorder byteorder;
uint8_t shift;
mpz_t bitmask;
@@ -473,7 +583,10 @@ static void expr_evaluate_bits(struct eval_ctx *ctx, struct expr **exprp)
}
masklen = len + shift;
- assert(masklen <= NFT_REG_SIZE * BITS_PER_BYTE);
+
+ 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);
mpz_init2(bitmask, masklen);
mpz_bitmask(bitmask, len);
@@ -490,6 +603,16 @@ static void expr_evaluate_bits(struct eval_ctx *ctx, struct expr **exprp)
and->len = masklen;
if (shift) {
+ 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;
+ byteorder = BYTEORDER_HOST_ENDIAN;
+ } else {
+ byteorder = expr->byteorder;
+ }
+
off = constant_expr_alloc(&expr->location,
expr_basetype(expr),
BYTEORDER_HOST_ENDIAN,
@@ -497,7 +620,7 @@ static void expr_evaluate_bits(struct eval_ctx *ctx, struct expr **exprp)
rshift = binop_expr_alloc(&expr->location, OP_RSHIFT, and, off);
rshift->dtype = expr->dtype;
- rshift->byteorder = expr->byteorder;
+ rshift->byteorder = byteorder;
rshift->len = masklen;
*exprp = rshift;
@@ -506,10 +629,13 @@ static void expr_evaluate_bits(struct eval_ctx *ctx, struct expr **exprp)
if (extra_len)
expr->len += extra_len;
+
+ return 0;
}
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)
@@ -518,9 +644,15 @@ 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);
+ expr->len % BITS_PER_BYTE != 0) {
+ int err = expr_evaluate_bits(ctx, exprp);
+
+ if (err)
+ return err;
+ }
switch (expr->exthdr.op) {
case NFT_EXTHDR_OP_TCPOPT: {
@@ -564,11 +696,13 @@ static int expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
const struct proto_desc *base, *dependency = NULL;
enum proto_bases pb = PROTO_BASE_NETWORK_HDR;
struct expr *expr = *exprp;
+ struct proto_ctx *pctx;
struct stmt *nstmt;
switch (expr->exthdr.op) {
case NFT_EXTHDR_OP_TCPOPT:
case NFT_EXTHDR_OP_SCTP:
+ case NFT_EXTHDR_OP_DCCP:
return __expr_evaluate_exthdr(ctx, exprp);
case NFT_EXTHDR_OP_IPV4:
dependency = &proto_ip;
@@ -581,7 +715,8 @@ static int expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
assert(dependency);
- base = ctx->pctx.protocol[pb].desc;
+ pctx = eval_proto_ctx(ctx);
+ base = pctx->protocol[pb].desc;
if (base == dependency)
return __expr_evaluate_exthdr(ctx, exprp);
@@ -599,7 +734,7 @@ static int expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
/* dependency supersede.
*
- * 'inet' is a 'phony' l2 dependency used by NFPROTO_INET to fulfill network
+ * 'inet' is a 'phony' l2 dependency used by NFPROTO_INET to fulfil network
* header dependency, i.e. ensure that 'ip saddr 1.2.3.4' only sees ip headers.
*
* If a match expression that depends on a particular L2 header, e.g. ethernet,
@@ -611,7 +746,7 @@ static int expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
* Example: inet filter in ip saddr 1.2.3.4 ether saddr a:b:c:d:e:f
*
* ip saddr adds meta dependency on ipv4 packets
- * ether saddr adds another dependeny on ethernet frames.
+ * ether saddr adds another dependency on ethernet frames.
*/
static int meta_iiftype_gen_dependency(struct eval_ctx *ctx,
struct expr *payload, struct stmt **res)
@@ -625,9 +760,11 @@ 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;
*res = nstmt;
return 0;
@@ -638,34 +775,53 @@ 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;
- if (payload->payload.base == PROTO_BASE_LL_HDR &&
- proto_is_dummy(desc)) {
- err = meta_iiftype_gen_dependency(ctx, payload, &nstmt);
- if (err < 0)
- return err;
+ assert(base == PROTO_BASE_LL_HDR);
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+ pctx = eval_proto_ctx(ctx);
+
+ 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);
+ }
+ } 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 (ctx->pctx.protocol[base + 1].desc != NULL)
+ 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;
- payload->payload.offset += ctx->pctx.protocol[base].offset;
+ 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);
return 0;
@@ -681,25 +837,48 @@ static int __expr_evaluate_payload(struct eval_ctx *ctx, struct expr *expr)
struct expr *payload = expr;
enum proto_bases base = payload->payload.base;
const struct proto_desc *desc;
+ struct proto_ctx *pctx;
struct stmt *nstmt;
int err;
if (expr->etype == EXPR_PAYLOAD && expr->payload.is_raw)
return 0;
- desc = ctx->pctx.protocol[base].desc;
+ pctx = eval_proto_ctx(ctx);
+ desc = pctx->protocol[base].desc;
if (desc == NULL) {
if (payload_gen_dependency(ctx, payload, &nstmt) < 0)
return -1;
rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
- desc = ctx->pctx.protocol[base].desc;
+
+ desc = pctx->protocol[base].desc;
+
+ if (desc == expr->payload.desc)
+ goto check_icmp;
+
+ if (base == PROTO_BASE_LL_HDR) {
+ int link;
+
+ link = proto_find_num(desc, payload->payload.desc);
+ if (link < 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,
+ 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;
+ }
goto check_icmp;
}
if (payload->payload.base == desc->base &&
- proto_ctx_is_ambiguous(&ctx->pctx, base)) {
- desc = proto_ctx_find_conflict(&ctx->pctx, base, payload->payload.desc);
+ proto_ctx_is_ambiguous(pctx, base)) {
+ desc = proto_ctx_find_conflict(pctx, base, payload->payload.desc);
assert(desc);
return expr_error(ctx->msgs, payload,
@@ -714,7 +893,12 @@ static int __expr_evaluate_payload(struct eval_ctx *ctx, struct expr *expr)
if (desc == payload->payload.desc) {
const struct proto_hdr_template *tmpl;
- payload->payload.offset += ctx->pctx.protocol[base].offset;
+ if (desc->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;
+ }
check_icmp:
if (desc != &proto_icmp && desc != &proto_icmp6)
return 0;
@@ -735,18 +919,19 @@ 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;
- desc = ctx->pctx.protocol[base].desc;
+ desc = pctx->protocol[base].desc;
if (desc == payload->payload.desc)
return 0;
}
return expr_error(ctx->msgs, payload,
- "conflicting protocols specified: %s vs. %s",
- ctx->pctx.protocol[base].desc->name,
+ "conflicting %s protocols specified: %s vs. %s",
+ proto_base_names[base],
+ pctx->protocol[base].desc->name,
payload->payload.desc->name);
}
@@ -758,6 +943,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)
@@ -769,14 +955,81 @@ static int expr_evaluate_payload(struct eval_ctx *ctx, struct expr **exprp)
if (expr_evaluate_primary(ctx, exprp) < 0)
return -1;
- if (payload_needs_adjustment(expr))
- expr_evaluate_bits(ctx, exprp);
+ ctx->ectx.key = key;
+
+ if (payload_needs_adjustment(expr)) {
+ int err = expr_evaluate_bits(ctx, exprp);
+
+ if (err)
+ return err;
+ }
expr->payload.evaluated = true;
return 0;
}
+static int expr_evaluate_inner(struct eval_ctx *ctx, struct expr **exprp)
+{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
+ const struct proto_desc *desc = NULL;
+ struct expr *expr = *exprp;
+ int ret;
+
+ assert(expr->etype == EXPR_PAYLOAD);
+
+ pctx = eval_proto_ctx(ctx);
+ desc = pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc;
+
+ if (desc == NULL &&
+ expr->payload.inner_desc->base < PROTO_BASE_INNER_HDR) {
+ struct stmt *nstmt;
+
+ if (payload_gen_inner_dependency(ctx, expr, &nstmt) < 0)
+ return -1;
+
+ rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+
+ proto_ctx_update(pctx, PROTO_BASE_TRANSPORT_HDR, &expr->location, expr->payload.inner_desc);
+ }
+
+ if (expr->payload.inner_desc->base == PROTO_BASE_INNER_HDR) {
+ desc = pctx->protocol[expr->payload.inner_desc->base - 1].desc;
+ if (!desc) {
+ return expr_error(ctx->msgs, expr,
+ "no transport protocol specified");
+ }
+
+ if (proto_find_num(desc, expr->payload.inner_desc) < 0) {
+ return expr_error(ctx->msgs, expr,
+ "unexpected transport protocol %s",
+ desc->name);
+ }
+
+ proto_ctx_update(pctx, expr->payload.inner_desc->base, &expr->location,
+ expr->payload.inner_desc);
+ }
+
+ if (expr->payload.base != PROTO_BASE_INNER_HDR)
+ ctx->inner_desc = expr->payload.inner_desc;
+
+ ret = expr_evaluate_payload(ctx, exprp);
+
+ return ret;
+}
+
+static int expr_evaluate_payload_inner(struct eval_ctx *ctx, struct expr **exprp)
+{
+ int ret;
+
+ if ((*exprp)->payload.inner_desc)
+ ret = expr_evaluate_inner(ctx, exprp);
+ else
+ ret = expr_evaluate_payload(ctx, exprp);
+
+ return ret;
+}
+
/*
* RT expression: validate protocol dependencies.
*/
@@ -784,20 +1037,22 @@ static int expr_evaluate_rt(struct eval_ctx *ctx, struct expr **expr)
{
static const char emsg[] = "cannot determine ip protocol version, use \"ip nexthop\" or \"ip6 nexthop\" instead";
struct expr *rt = *expr;
+ struct proto_ctx *pctx;
- rt_expr_update_type(&ctx->pctx, rt);
+ pctx = eval_proto_ctx(ctx);
+ rt_expr_update_type(pctx, rt);
switch (rt->rt.key) {
case NFT_RT_NEXTHOP4:
if (rt->dtype != &ipaddr_type)
return expr_error(ctx->msgs, rt, "%s", emsg);
- if (ctx->pctx.family == NFPROTO_IPV6)
+ if (pctx->family == NFPROTO_IPV6)
return expr_error(ctx->msgs, rt, "%s nexthop will not match", "ip");
break;
case NFT_RT_NEXTHOP6:
if (rt->dtype != &ip6addr_type)
return expr_error(ctx->msgs, rt, "%s", emsg);
- if (ctx->pctx.family == NFPROTO_IPV4)
+ if (pctx->family == NFPROTO_IPV4)
return expr_error(ctx->msgs, rt, "%s nexthop will not match", "ip6");
break;
default:
@@ -812,8 +1067,10 @@ static int ct_gen_nh_dependency(struct eval_ctx *ctx, struct expr *ct)
const struct proto_desc *base, *base_now;
struct expr *left, *right, *dep;
struct stmt *nstmt = NULL;
+ struct proto_ctx *pctx;
- base_now = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ pctx = eval_proto_ctx(ctx);
+ base_now = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
switch (ct->ct.nfproto) {
case NFPROTO_IPV4:
@@ -823,7 +1080,7 @@ static int ct_gen_nh_dependency(struct eval_ctx *ctx, struct expr *ct)
base = &proto_ip6;
break;
default:
- base = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ base = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if (base == &proto_ip)
ct->ct.nfproto = NFPROTO_IPV4;
else if (base == &proto_ip)
@@ -845,8 +1102,8 @@ static int ct_gen_nh_dependency(struct eval_ctx *ctx, struct expr *ct)
return expr_error(ctx->msgs, ct,
"conflicting dependencies: %s vs. %s\n",
base->name,
- ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc->name);
- switch (ctx->pctx.family) {
+ pctx->protocol[PROTO_BASE_NETWORK_HDR].desc->name);
+ switch (pctx->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
return 0;
@@ -859,7 +1116,7 @@ static int ct_gen_nh_dependency(struct eval_ctx *ctx, struct expr *ct)
constant_data_ptr(ct->ct.nfproto, left->len));
dep = relational_expr_alloc(&ct->location, OP_EQ, left, right);
- relational_expr_pctx_update(&ctx->pctx, dep);
+ relational_expr_pctx_update(pctx, dep);
nstmt = expr_stmt_alloc(&dep->location, dep);
rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
@@ -875,8 +1132,10 @@ static int expr_evaluate_ct(struct eval_ctx *ctx, struct expr **expr)
{
const struct proto_desc *base, *error;
struct expr *ct = *expr;
+ struct proto_ctx *pctx;
- base = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ pctx = eval_proto_ctx(ctx);
+ base = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
switch (ct->ct.key) {
case NFT_CT_SRC:
@@ -901,13 +1160,13 @@ static int expr_evaluate_ct(struct eval_ctx *ctx, struct expr **expr)
break;
}
- ct_expr_update_type(&ctx->pctx, ct);
+ ct_expr_update_type(pctx, ct);
return expr_evaluate_primary(ctx, expr);
err_conflict:
return stmt_binary_error(ctx, ct,
- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+ &pctx->protocol[PROTO_BASE_NETWORK_HDR],
"conflicting protocols specified: %s vs. %s",
base->name, error->name);
}
@@ -954,7 +1213,6 @@ static int expr_evaluate_prefix(struct eval_ctx *ctx, struct expr **expr)
mpz_prefixmask(mask->value, base->len, prefix->prefix_len);
break;
case TYPE_STRING:
- mpz_init2(mask->value, base->len);
mpz_bitmask(mask->value, prefix->prefix_len);
break;
}
@@ -965,7 +1223,7 @@ static int expr_evaluate_prefix(struct eval_ctx *ctx, struct expr **expr)
base = prefix->prefix;
assert(expr_is_constant(base));
- prefix->dtype = base->dtype;
+ prefix->dtype = datatype_get(base->dtype);
prefix->byteorder = base->byteorder;
prefix->len = base->len;
prefix->flags |= EXPR_F_CONSTANT;
@@ -1016,9 +1274,9 @@ static int expr_evaluate_range(struct eval_ctx *ctx, struct expr **expr)
left = range->left;
right = range->right;
- if (mpz_cmp(left->value, right->value) >= 0)
- return expr_error(ctx->msgs, range,
- "Range has zero or negative size");
+ if (mpz_cmp(left->value, right->value) > 0)
+ return expr_error(ctx->msgs, range, "Range negative size");
+
datatype_set(range, left->dtype);
range->flags |= EXPR_F_CONSTANT;
return 0;
@@ -1030,12 +1288,10 @@ static int expr_evaluate_range(struct eval_ctx *ctx, struct expr **expr)
*/
static int expr_evaluate_unary(struct eval_ctx *ctx, struct expr **expr)
{
- struct expr *unary = *expr, *arg;
+ struct expr *unary = *expr, *arg = unary->arg;
enum byteorder byteorder;
- if (expr_evaluate(ctx, &unary->arg) < 0)
- return -1;
- arg = unary->arg;
+ /* unary expression arguments has already been evaluated. */
assert(!expr_is_constant(arg));
assert(expr_basetype(arg)->type == TYPE_INTEGER);
@@ -1054,7 +1310,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;
@@ -1121,14 +1377,24 @@ 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, max_shift_len;
- if (mpz_get_uint32(right->value) >= left->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,
- "%s shift of %u bits is undefined "
- "for type of %u bits width",
+ "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
+ max_shift_len = left->len;
+
+ if (shift >= max_shift_len)
+ return expr_binary_error(ctx->msgs, right, left,
+ "%s shift of %u bits is undefined for type of %u bits width",
op->op == OP_LSHIFT ? "Left" : "Right",
- mpz_get_uint32(right->value),
- left->len);
+ shift, max_shift_len);
/* Both sides need to be in host byte order */
if (byteorder_conversion(ctx, &op->left, BYTEORDER_HOST_ENDIAN) < 0)
@@ -1137,9 +1403,9 @@ static int expr_evaluate_shift(struct eval_ctx *ctx, struct expr **expr)
if (byteorder_conversion(ctx, &op->right, BYTEORDER_HOST_ENDIAN) < 0)
return -1;
- op->dtype = &integer_type;
+ datatype_set(op, &integer_type);
op->byteorder = BYTEORDER_HOST_ENDIAN;
- op->len = left->len;
+ op->len = max_shift_len;
if (expr_is_constant(left))
return constant_binop_simplify(ctx, expr);
@@ -1149,13 +1415,32 @@ static int expr_evaluate_shift(struct eval_ctx *ctx, struct expr **expr)
static int expr_evaluate_bitwise(struct eval_ctx *ctx, struct expr **expr)
{
struct expr *op = *expr, *left = op->left;
+ const struct datatype *dtype;
+ enum byteorder byteorder;
+ unsigned int max_len;
+
+ if (ctx->stmt_len > left->len) {
+ max_len = ctx->stmt_len;
+ byteorder = BYTEORDER_HOST_ENDIAN;
+ dtype = &integer_type;
+
+ /* Both sides need to be in host byte order */
+ if (byteorder_conversion(ctx, &op->left, BYTEORDER_HOST_ENDIAN) < 0)
+ return -1;
- if (byteorder_conversion(ctx, &op->right, left->byteorder) < 0)
+ left = op->left;
+ } else {
+ max_len = left->len;
+ byteorder = left->byteorder;
+ dtype = left->dtype;
+ }
+
+ if (byteorder_conversion(ctx, &op->right, byteorder) < 0)
return -1;
- op->dtype = left->dtype;
- op->byteorder = left->byteorder;
- op->len = left->len;
+ datatype_set(op, dtype);
+ op->byteorder = byteorder;
+ op->len = max_len;
if (expr_is_constant(left))
return constant_binop_simplify(ctx, expr);
@@ -1172,14 +1457,27 @@ static int expr_evaluate_binop(struct eval_ctx *ctx, struct expr **expr)
{
struct expr *op = *expr, *left, *right;
const char *sym = expr_op_symbols[op->op];
+ unsigned int max_shift_len = ctx->ectx.len;
+ int ret = -1;
+
+ if (ctx->recursion >= USHRT_MAX)
+ return expr_binary_error(ctx->msgs, op, NULL,
+ "Binary operation limit %u reached ",
+ ctx->recursion);
+ ctx->recursion++;
if (expr_evaluate(ctx, &op->left) < 0)
return -1;
left = op->left;
- if (op->op == OP_LSHIFT || op->op == OP_RSHIFT)
+ if (op->op == OP_LSHIFT || op->op == OP_RSHIFT) {
+ if (left->len > max_shift_len)
+ max_shift_len = left->len;
+
__expr_set_context(&ctx->ectx, &integer_type,
- left->byteorder, ctx->ectx.len, 0);
+ left->byteorder, max_shift_len, 0);
+ }
+
if (expr_evaluate(ctx, &op->right) < 0)
return -1;
right = op->right;
@@ -1212,20 +1510,51 @@ static int expr_evaluate_binop(struct eval_ctx *ctx, struct expr **expr)
"for %s expressions",
sym, expr_name(right));
- /* The grammar guarantees this */
- assert(expr_basetype(left) == expr_basetype(right));
+ if (!datatype_equal(expr_basetype(left), expr_basetype(right)))
+ return expr_binary_error(ctx->msgs, left, op,
+ "Binary operation (%s) with different base types "
+ "(%s vs %s) is not supported",
+ sym, expr_basetype(left)->name, expr_basetype(right)->name);
switch (op->op) {
case OP_LSHIFT:
case OP_RSHIFT:
- return expr_evaluate_shift(ctx, expr);
+ ret = expr_evaluate_shift(ctx, expr);
+ break;
case OP_AND:
case OP_XOR:
case OP_OR:
- return expr_evaluate_bitwise(ctx, expr);
+ ret = expr_evaluate_bitwise(ctx, expr);
+ break;
default:
BUG("invalid binary operation %u\n", op->op);
}
+
+
+ if (ctx->recursion == 0)
+ BUG("recursion counter underflow");
+
+ /* can't check earlier: evaluate functions might do constant-merging + expr_free.
+ *
+ * So once we've evaluate everything check for remaining length of the
+ * binop chain.
+ */
+ if (--ctx->recursion == 0) {
+ unsigned int to_linearize = 0;
+
+ op = *expr;
+ while (op && op->etype == EXPR_BINOP && op->left != NULL) {
+ to_linearize++;
+ op = op->left;
+
+ if (to_linearize >= NFT_MAX_EXPR_RECURSION)
+ return expr_binary_error(ctx->msgs, op, NULL,
+ "Binary operation limit %u reached ",
+ NFT_MAX_EXPR_RECURSION);
+ }
+ }
+
+ return ret;
}
static int list_member_evaluate(struct eval_ctx *ctx, struct expr **expr)
@@ -1246,10 +1575,27 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
uint32_t type = dtype ? dtype->type : 0, ntype = 0;
int off = dtype ? dtype->subtypes : 0;
unsigned int flags = EXPR_F_CONSTANT | EXPR_F_SINGLETON;
- struct expr *i, *next;
+ const struct list_head *expressions = NULL;
+ struct expr *i, *next, *key = NULL;
+ const struct expr *key_ctx = NULL;
+ bool runaway = false;
+ uint32_t size = 0;
+
+ if (ctx->ectx.key && ctx->ectx.key->etype == EXPR_CONCAT) {
+ key_ctx = ctx->ectx.key;
+ assert(!list_empty(&ctx->ectx.key->expressions));
+ key = list_first_entry(&ctx->ectx.key->expressions, struct expr, list);
+ expressions = &ctx->ectx.key->expressions;
+ }
list_for_each_entry_safe(i, next, &(*expr)->expressions, list) {
- unsigned dsize_bytes;
+ enum byteorder bo = BYTEORDER_INVALID;
+ unsigned dsize_bytes, dsize = 0;
+
+ if (runaway) {
+ return expr_binary_error(ctx->msgs, *expr, key_ctx,
+ "too many concatenation components");
+ }
if (i->etype == EXPR_CT &&
(i->ct.key == NFT_CT_SRC ||
@@ -1263,32 +1609,77 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
"expecting %s",
dtype->desc);
- if (dtype == NULL)
+ if (key) {
+ tmp = key->dtype;
+ dsize = key->len;
+ bo = key->byteorder;
+ off--;
+ } else if (dtype == NULL || off == 0) {
tmp = datatype_lookup(TYPE_INVALID);
- else
+ } else {
tmp = concat_subtype_lookup(type, --off);
- expr_set_context(&ctx->ectx, tmp, tmp->size);
+ dsize = tmp->size;
+ bo = tmp->byteorder;
+ }
+
+ __expr_set_context(&ctx->ectx, tmp, bo, dsize, 0);
+ ctx->ectx.key = i;
if (list_member_evaluate(ctx, &i) < 0)
return -1;
+
+ if (i->etype == EXPR_SET)
+ return expr_error(ctx->msgs, i,
+ "cannot use %s in concatenation",
+ expr_name(i));
+
+ if (!i->dtype)
+ return expr_error(ctx->msgs, i,
+ "cannot use %s in concatenation, lacks datatype",
+ expr_name(i));
+
flags &= i->flags;
+ if (!key && i->dtype->type == TYPE_INTEGER) {
+ struct datatype *clone;
+
+ clone = datatype_clone(i->dtype);
+ clone->size = i->len;
+ clone->byteorder = i->byteorder;
+ __datatype_set(i, clone);
+ }
+
if (dtype == NULL && i->dtype->size == 0)
return expr_binary_error(ctx->msgs, i, *expr,
"can not use variable sized "
"data types (%s) in concat "
"expressions",
i->dtype->name);
+ if (dsize == 0) /* reload after evaluation or clone above */
+ dsize = i->dtype->size;
ntype = concat_subtype_add(ntype, i->dtype->type);
- dsize_bytes = div_round_up(i->dtype->size, BITS_PER_BYTE);
+ dsize_bytes = div_round_up(dsize, BITS_PER_BYTE);
(*expr)->field_len[(*expr)->field_count++] = dsize_bytes;
+ size += netlink_padded_len(dsize);
+ if (key && expressions) {
+ if (list_is_last(&key->list, expressions))
+ runaway = true;
+ else
+ key = list_next_entry(key, list);
+ }
+
+ 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;
- datatype_set(*expr, concat_type_alloc(ntype));
- (*expr)->len = (*expr)->dtype->size;
+ __datatype_set(*expr, concat_type_alloc(ntype));
+ (*expr)->len = size;
if (off > 0)
return expr_error(ctx->msgs, *expr,
@@ -1297,6 +1688,10 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
dtype->desc, (*expr)->dtype->desc);
expr_set_context(&ctx->ectx, (*expr)->dtype, (*expr)->len);
+ if (!key_ctx)
+ ctx->ectx.key = *expr;
+ else
+ ctx->ectx.key = key_ctx;
return 0;
}
@@ -1308,16 +1703,22 @@ static int expr_evaluate_list(struct eval_ctx *ctx, struct expr **expr)
mpz_init_set_ui(val, 0);
list_for_each_entry_safe(i, next, &list->expressions, list) {
- if (list_member_evaluate(ctx, &i) < 0)
+ if (list_member_evaluate(ctx, &i) < 0) {
+ mpz_clear(val);
return -1;
- if (i->etype != EXPR_VALUE)
+ }
+ if (i->etype != EXPR_VALUE) {
+ mpz_clear(val);
return expr_error(ctx->msgs, i,
"List member must be a constant "
"value");
- if (i->dtype->basetype->type != TYPE_BITMASK)
+ }
+ if (datatype_basetype(i->dtype)->type != TYPE_BITMASK) {
+ mpz_clear(val);
return expr_error(ctx->msgs, i,
"Basetype of type %s is not bitmask",
i->dtype->desc);
+ }
mpz_ior(val, val, i->value);
}
@@ -1351,7 +1752,8 @@ static int __expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr *elem)
"but element has %d", num_set_exprs,
num_elem_exprs);
} else if (num_set_exprs == 0) {
- if (!(set->flags & NFT_SET_EVAL)) {
+ if (!(set->flags & NFT_SET_ANONYMOUS) &&
+ !(set->flags & NFT_SET_EVAL)) {
elem_stmt = list_first_entry(&elem->stmt_list, struct stmt, list);
return stmt_error(ctx, elem_stmt,
"missing statement in %s declaration",
@@ -1381,15 +1783,15 @@ static int __expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr *elem)
static int expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr **expr)
{
struct expr *elem = *expr;
+ const struct expr *key;
if (ctx->set) {
- const struct expr *key;
-
if (__expr_evaluate_set_elem(ctx, elem) < 0)
return -1;
key = ctx->set->key;
__expr_set_context(&ctx->ectx, key->dtype, key->byteorder, key->len, 0);
+ ctx->ectx.key = key;
}
if (expr_evaluate(ctx, &elem->key) < 0)
@@ -1400,9 +1802,19 @@ static int expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr **expr)
switch (elem->key->etype) {
case EXPR_PREFIX:
case EXPR_RANGE:
- return expr_error(ctx->msgs, elem,
- "You must add 'flags interval' to your %s declaration if you want to add %s elements",
- set_is_map(ctx->set->flags) ? "map" : "set", expr_name(elem->key));
+ key = elem->key;
+ goto err_missing_flag;
+ case EXPR_CONCAT:
+ list_for_each_entry(key, &elem->key->expressions, list) {
+ switch (key->etype) {
+ case EXPR_PREFIX:
+ case EXPR_RANGE:
+ goto err_missing_flag;
+ default:
+ break;
+ }
+ }
+ break;
default:
break;
}
@@ -1411,7 +1823,13 @@ static int expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr **expr)
datatype_set(elem, elem->key->dtype);
elem->len = elem->key->len;
elem->flags = elem->key->flags;
+
return 0;
+
+err_missing_flag:
+ return expr_error(ctx->msgs, key,
+ "You must add 'flags interval' to your %s declaration if you want to add %s elements",
+ set_is_map(ctx->set->flags) ? "map" : "set", expr_name(key));
}
static const struct expr *expr_set_elem(const struct expr *expr)
@@ -1422,6 +1840,50 @@ static const struct expr *expr_set_elem(const struct expr *expr)
return expr;
}
+static int interval_set_eval(struct eval_ctx *ctx, struct set *set,
+ struct expr *init)
+{
+ int ret;
+
+ if (!init)
+ return 0;
+
+ ret = 0;
+ 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,
+ ctx->nft->debug_mask);
+ } else {
+ ret = set_overlap(ctx->msgs, set, init);
+ }
+ break;
+ case CMD_DELETE:
+ case CMD_DESTROY:
+ ret = set_delete(ctx->msgs, ctx->cmd, set, init,
+ ctx->nft->debug_mask);
+ break;
+ case CMD_GET:
+ break;
+ default:
+ BUG("unhandled op %d\n", ctx->cmd->op);
+ break;
+ }
+
+ return ret;
+}
+
+static void expr_evaluate_set_ref(struct eval_ctx *ctx, struct expr *expr)
+{
+ struct set *set = expr->set;
+
+ expr_set_context(&ctx->ectx, set->key->dtype, set->key->len);
+ ctx->ectx.key = set->key;
+}
+
static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr)
{
struct expr *set = *expr, *i, *next;
@@ -1439,7 +1901,7 @@ static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr)
list_for_each_entry(j, &i->left->key->expressions, list) {
new = mapping_expr_alloc(&i->location,
expr_get(j),
- expr_clone(i->right));
+ expr_get(i->right));
list_add_tail(&new->list, &set->expressions);
set->size++;
}
@@ -1457,7 +1919,7 @@ static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr)
if (elem->etype == EXPR_SET_ELEM &&
elem->key->etype == EXPR_SET) {
- struct expr *new = expr_clone(elem->key);
+ struct expr *new = expr_get(elem->key);
set->set_flags |= elem->key->set_flags;
list_replace(&i->list, &new->list);
@@ -1487,21 +1949,6 @@ static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr)
if (ctx->set) {
if (ctx->set->flags & NFT_SET_CONCAT)
set->set_flags |= NFT_SET_CONCAT;
- } else if (set->size == 1) {
- i = list_first_entry(&set->expressions, struct expr, list);
- if (i->etype == EXPR_SET_ELEM) {
- switch (i->key->etype) {
- case EXPR_PREFIX:
- case EXPR_RANGE:
- case EXPR_VALUE:
- *expr = i->key;
- i->key = NULL;
- expr_free(set);
- return 0;
- default:
- break;
- }
- }
}
set->set_flags |= NFT_SET_CONSTANT;
@@ -1509,15 +1956,80 @@ static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr)
datatype_set(set, ctx->ectx.dtype);
set->len = ctx->ectx.len;
set->flags |= EXPR_F_CONSTANT;
+
return 0;
}
static int binop_transfer(struct eval_ctx *ctx, struct expr **expr);
+
+static void map_set_concat_info(struct expr *map)
+{
+ map->mappings->set->flags |= map->mappings->set->init->set_flags;
+
+ if (map->mappings->set->flags & NFT_SET_INTERVAL &&
+ map->map->etype == EXPR_CONCAT) {
+ memcpy(&map->mappings->set->desc.field_len, &map->map->field_len,
+ sizeof(map->mappings->set->desc.field_len));
+ map->mappings->set->desc.field_count = map->map->field_count;
+ map->mappings->flags |= NFT_SET_CONCAT;
+ }
+}
+
+static void __mapping_expr_expand(struct expr *i)
+{
+ struct expr *j, *range, *next;
+
+ assert(i->etype == EXPR_MAPPING);
+ switch (i->right->etype) {
+ case EXPR_VALUE:
+ range = range_expr_alloc(&i->location, expr_get(i->right), expr_get(i->right));
+ expr_free(i->right);
+ i->right = range;
+ break;
+ case EXPR_CONCAT:
+ list_for_each_entry_safe(j, next, &i->right->expressions, list) {
+ if (j->etype != EXPR_VALUE)
+ continue;
+
+ range = range_expr_alloc(&j->location, expr_get(j), expr_get(j));
+ list_replace(&j->list, &range->list);
+ expr_free(j);
+ }
+ i->right->flags &= ~EXPR_F_SINGLETON;
+ break;
+ default:
+ break;
+ }
+}
+
+static int mapping_expr_expand(struct eval_ctx *ctx)
+{
+ struct expr *i;
+
+ if (!set_is_anonymous(ctx->set->flags))
+ return 0;
+
+ list_for_each_entry(i, &ctx->set->init->expressions, list) {
+ if (i->etype != EXPR_MAPPING)
+ return expr_error(ctx->msgs, i,
+ "expected mapping, not %s", expr_name(i));
+ __mapping_expr_expand(i);
+ }
+
+ return 0;
+}
+
+static bool datatype_compatible(const struct datatype *a, const struct datatype *b)
+{
+ return (a->type == TYPE_MARK &&
+ datatype_equal(datatype_basetype(a), datatype_basetype(b))) ||
+ datatype_equal(a, b);
+}
+
static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
{
- struct expr_ctx ectx = ctx->ectx;
struct expr *map = *expr, *mappings;
- const struct datatype *dtype;
+ struct expr_ctx ectx = ctx->ectx;
struct expr *key, *data;
if (map->map->etype == EXPR_CT &&
@@ -1544,23 +2056,44 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
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;
switch (map->mappings->etype) {
case EXPR_SET:
- key = constant_expr_alloc(&map->location,
- ctx->ectx.dtype,
- ctx->ectx.byteorder,
- ctx->ectx.len, NULL);
+ if (ctx->ectx.key && ctx->ectx.key->etype == EXPR_CONCAT) {
+ key = expr_clone(ctx->ectx.key);
+ } else {
+ key = constant_expr_alloc(&map->location,
+ ctx->ectx.dtype,
+ ctx->ectx.byteorder,
+ ctx->ectx.len, NULL);
+ }
- dtype = set_datatype_alloc(ectx.dtype, ectx.byteorder);
- data = constant_expr_alloc(&netlink_location, dtype,
- dtype->byteorder, ectx.len, NULL);
+ if (!ectx.dtype) {
+ expr_free(key);
+ return expr_error(ctx->msgs, map,
+ "Implicit map expression without known datatype");
+ }
+
+ if (ectx.dtype->type == TYPE_VERDICT) {
+ data = verdict_expr_alloc(&netlink_location, 0, NULL);
+ } else {
+ const struct datatype *dtype;
+
+ dtype = set_datatype_alloc(ectx.dtype, ectx.byteorder);
+ data = constant_expr_alloc(&netlink_location, dtype,
+ dtype->byteorder, ectx.len, NULL);
+ datatype_free(dtype);
+ }
mappings = implicit_set_declaration(ctx, "__map%d",
key, data,
- mappings);
+ mappings,
+ NFT_SET_ANONYMOUS);
+ if (!mappings)
+ return -1;
if (ectx.len && mappings->set->data->len != ectx.len)
BUG("%d vs %d\n", mappings->set->data->len, ectx.len);
@@ -1570,25 +2103,28 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
ctx->set = mappings->set;
if (expr_evaluate(ctx, &map->mappings->set->init) < 0)
return -1;
+
+ if (set_is_interval(map->mappings->set->init->set_flags) &&
+ !(map->mappings->set->init->set_flags & NFT_SET_CONCAT) &&
+ interval_set_eval(ctx, ctx->set, map->mappings->set->init) < 0)
+ return -1;
+
expr_set_context(&ctx->ectx, ctx->set->key->dtype, ctx->set->key->len);
if (binop_transfer(ctx, expr) < 0)
return -1;
- if (ctx->set->data->flags & EXPR_F_INTERVAL)
+ if (ctx->set->data->flags & EXPR_F_INTERVAL) {
ctx->set->data->len *= 2;
+ if (mapping_expr_expand(ctx))
+ return -1;
+ }
+
ctx->set->key->len = ctx->ectx.len;
ctx->set = NULL;
map = *expr;
- map->mappings->set->flags |= map->mappings->set->init->set_flags;
-
- if (map->mappings->set->flags & NFT_SET_INTERVAL &&
- map->map->etype == EXPR_CONCAT) {
- memcpy(&map->mappings->set->desc.field_len, &map->map->field_len,
- sizeof(map->mappings->set->desc.field_len));
- map->mappings->set->desc.field_count = map->map->field_count;
- map->mappings->flags |= NFT_SET_CONCAT;
- }
+
+ map_set_concat_info(map);
break;
case EXPR_SYMBOL:
if (expr_evaluate(ctx, &map->mappings) < 0)
@@ -1600,13 +2136,16 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
break;
case EXPR_SET_REF:
/* symbol has been already evaluated to set reference */
+ if (!set_is_map(mappings->set->flags))
+ return expr_error(ctx->msgs, map->mappings,
+ "Expression is not a map");
break;
default:
- BUG("invalid mapping expression %s\n",
- expr_name(map->mappings));
+ return expr_binary_error(ctx->msgs, map->mappings, map->map,
+ "invalid mapping expression %s", expr_name(map->mappings));
}
- if (!datatype_equal(map->map->dtype, map->mappings->set->key->dtype))
+ if (!datatype_compatible(map->mappings->set->key->dtype, map->map->dtype))
return expr_binary_error(ctx->msgs, map->mappings, map->map,
"datatype mismatch, map expects %s, "
"mapping expression has type %s",
@@ -1664,17 +2203,14 @@ static int expr_evaluate_mapping(struct eval_ctx *ctx, struct expr **expr)
"Key must be a constant");
mapping->flags |= mapping->left->flags & EXPR_F_SINGLETON;
- if (set->data) {
- if (!set_is_anonymous(set->flags) &&
- set->data->flags & EXPR_F_INTERVAL)
- datalen = set->data->len / 2;
- else
- datalen = set->data->len;
-
- __expr_set_context(&ctx->ectx, set->data->dtype, set->data->byteorder, datalen, 0);
- } else {
- assert((set->flags & NFT_SET_MAP) == 0);
- }
+ assert(set->data != NULL);
+ if (!set_is_anonymous(set->flags) &&
+ set->data->flags & EXPR_F_INTERVAL)
+ datalen = set->data->len / 2;
+ else
+ datalen = set->data->len;
+ __expr_set_context(&ctx->ectx, set->data->dtype,
+ set->data->byteorder, datalen, 0);
if (expr_evaluate(ctx, &mapping->right) < 0)
return -1;
@@ -1686,11 +2222,20 @@ static int expr_evaluate_mapping(struct eval_ctx *ctx, struct expr **expr)
data_mapping_has_interval(mapping->right))
set->data->flags |= EXPR_F_INTERVAL;
+ if (!set_is_anonymous(set->flags) &&
+ set->data->flags & EXPR_F_INTERVAL)
+ __mapping_expr_expand(mapping);
+
if (!(set->data->flags & EXPR_F_INTERVAL) &&
!expr_is_singleton(mapping->right))
return expr_error(ctx->msgs, mapping->right,
"Value must be a singleton");
+ if (set_is_objmap(set->flags) && mapping->right->etype != EXPR_VALUE)
+ return expr_error(ctx->msgs, mapping->right,
+ "Object mapping data should be a value, not %s",
+ expr_name(mapping->right));
+
mapping->flags |= EXPR_F_CONSTANT;
return 0;
}
@@ -1925,7 +2470,7 @@ static int binop_transfer(struct eval_ctx *ctx, struct expr **expr)
return 0;
}
-static bool lhs_is_meta_hour(const struct expr *meta)
+bool lhs_is_meta_hour(const struct expr *meta)
{
if (meta->etype != EXPR_META)
return false;
@@ -1934,7 +2479,7 @@ static bool lhs_is_meta_hour(const struct expr *meta)
meta->meta.key == NFT_META_TIME_DAY;
}
-static void swap_values(struct expr *range)
+void range_expr_swap_values(struct expr *range)
{
struct expr *left_tmp;
@@ -1951,16 +2496,54 @@ static bool range_needs_swap(const struct expr *range)
return mpz_cmp(left->value, right->value) > 0;
}
+static void optimize_singleton_set(struct expr *rel, struct expr **expr)
+{
+ struct expr *set = rel->right, *i;
+
+ i = list_first_entry(&set->expressions, struct expr, list);
+ if (i->etype == EXPR_SET_ELEM &&
+ list_empty(&i->stmt_list)) {
+
+ switch (i->key->etype) {
+ case EXPR_PREFIX:
+ case EXPR_RANGE:
+ case EXPR_VALUE:
+ rel->right = *expr = i->key;
+ i->key = NULL;
+ expr_free(set);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (rel->op == OP_IMPLICIT &&
+ rel->right->dtype->basetype &&
+ rel->right->dtype->basetype->type == TYPE_BITMASK &&
+ rel->right->dtype->type != TYPE_CT_STATE) {
+ rel->op = OP_EQ;
+ }
+}
+
static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
{
struct expr *rel = *expr, *left, *right;
+ struct proto_ctx *pctx;
struct expr *range;
int ret;
+ right = rel->right;
+ if (right->etype == EXPR_SYMBOL &&
+ right->symtype == SYMBOL_SET &&
+ expr_evaluate(ctx, &rel->right) < 0)
+ return -1;
+
if (expr_evaluate(ctx, &rel->left) < 0)
return -1;
left = rel->left;
+ pctx = eval_proto_ctx(ctx);
+
if (rel->right->etype == EXPR_RANGE && lhs_is_meta_hour(rel->left)) {
ret = __expr_evaluate_range(ctx, &rel->right);
if (ret)
@@ -1978,10 +2561,10 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
"Inverting range values for cross-day hour matching\n\n");
if (rel->op == OP_EQ || rel->op == OP_IMPLICIT) {
- swap_values(range);
+ range_expr_swap_values(range);
rel->op = OP_NEQ;
} else if (rel->op == OP_NEQ) {
- swap_values(range);
+ range_expr_swap_values(range);
rel->op = OP_EQ;
}
}
@@ -2021,6 +2604,19 @@ 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");
+ 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) {
case OP_EQ:
case OP_IMPLICIT:
@@ -2028,7 +2624,7 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
* Update protocol context for payload and meta iiftype
* equality expressions.
*/
- relational_expr_pctx_update(&ctx->pctx, rel);
+ relational_expr_pctx_update(pctx, rel);
/* fall through */
case OP_NEQ:
@@ -2041,7 +2637,7 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
right->dtype->basetype == NULL ||
right->dtype->basetype->type != TYPE_BITMASK)
return expr_binary_error(ctx->msgs, left, right,
- "negation can only be used with singleton bitmask values");
+ "negation can only be used with singleton bitmask values. Did you mean \"!=\"?");
}
switch (right->etype) {
@@ -2068,7 +2664,11 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
right = rel->right =
implicit_set_declaration(ctx, "__set%d",
expr_get(left), NULL,
- right);
+ right,
+ NFT_SET_ANONYMOUS);
+ if (!right)
+ return -1;
+
/* fall through */
case EXPR_SET_REF:
if (rel->left->etype == EXPR_CT &&
@@ -2140,11 +2740,12 @@ static int expr_evaluate_fib(struct eval_ctx *ctx, struct expr **exprp)
static int expr_evaluate_meta(struct eval_ctx *ctx, struct expr **exprp)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
struct expr *meta = *exprp;
switch (meta->meta.key) {
case NFT_META_NFPROTO:
- if (ctx->pctx.family != NFPROTO_INET &&
+ if (pctx->family != NFPROTO_INET &&
meta->flags & EXPR_F_PROTOCOL)
return expr_error(ctx->msgs, meta,
"meta nfproto is only useful in the inet family");
@@ -2187,7 +2788,16 @@ static int expr_evaluate_osf(struct eval_ctx *ctx, struct expr **expr)
static int expr_evaluate_variable(struct eval_ctx *ctx, struct expr **exprp)
{
- struct expr *new = expr_clone((*exprp)->sym->expr);
+ struct symbol *sym = (*exprp)->sym;
+ struct expr *new;
+
+ /* If variable is reused from different locations in the ruleset, then
+ * clone expression.
+ */
+ if (sym->refcnt > 2)
+ new = expr_clone(sym->expr);
+ else
+ new = expr_get(sym->expr);
if (expr_evaluate(ctx, &new) < 0) {
expr_free(new);
@@ -2202,9 +2812,10 @@ static int expr_evaluate_variable(struct eval_ctx *ctx, struct expr **exprp)
static int expr_evaluate_xfrm(struct eval_ctx *ctx, struct expr **exprp)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
struct expr *expr = *exprp;
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
case NFPROTO_INET:
@@ -2236,6 +2847,35 @@ static int expr_evaluate_flagcmp(struct eval_ctx *ctx, struct expr **exprp)
return expr_evaluate(ctx, exprp);
}
+static int verdict_validate_chainlen(struct eval_ctx *ctx,
+ struct expr *chain)
+{
+ if (chain->len > NFT_CHAIN_MAXNAMELEN * BITS_PER_BYTE)
+ return expr_error(ctx->msgs, chain,
+ "chain name too long (%u, max %u)",
+ chain->len / BITS_PER_BYTE,
+ NFT_CHAIN_MAXNAMELEN);
+
+ return 0;
+}
+
+static int expr_evaluate_verdict(struct eval_ctx *ctx, struct expr **exprp)
+{
+ struct expr *expr = *exprp;
+
+ switch (expr->verdict) {
+ case NFT_GOTO:
+ case NFT_JUMP:
+ if (expr->chain->etype == EXPR_VALUE &&
+ verdict_validate_chainlen(ctx, expr->chain))
+ return -1;
+
+ break;
+ }
+
+ return expr_evaluate_primary(ctx, exprp);
+}
+
static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
{
if (ctx->nft->debug_mask & NFT_DEBUG_EVALUATION) {
@@ -2254,13 +2894,14 @@ static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
case EXPR_VARIABLE:
return expr_evaluate_variable(ctx, expr);
case EXPR_SET_REF:
+ expr_evaluate_set_ref(ctx, *expr);
return 0;
case EXPR_VALUE:
return expr_evaluate_value(ctx, expr);
case EXPR_EXTHDR:
return expr_evaluate_exthdr(ctx, expr);
case EXPR_VERDICT:
- return expr_evaluate_primary(ctx, expr);
+ return expr_evaluate_verdict(ctx, expr);
case EXPR_META:
return expr_evaluate_meta(ctx, expr);
case EXPR_SOCKET:
@@ -2270,7 +2911,7 @@ static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
case EXPR_FIB:
return expr_evaluate_fib(ctx, expr);
case EXPR_PAYLOAD:
- return expr_evaluate_payload(ctx, expr);
+ return expr_evaluate_payload_inner(ctx, expr);
case EXPR_RT:
return expr_evaluate_rt(ctx, expr);
case EXPR_CT:
@@ -2377,13 +3018,18 @@ static int __stmt_evaluate_arg(struct eval_ctx *ctx, struct stmt *stmt,
"expression has type %s with length %d",
dtype->desc, (*expr)->dtype->desc,
(*expr)->len);
- else if ((*expr)->dtype->type != TYPE_INTEGER &&
- !datatype_equal((*expr)->dtype, dtype))
+
+ if (!datatype_compatible(dtype, (*expr)->dtype))
return stmt_binary_error(ctx, *expr, stmt, /* verdict vs invalid? */
"datatype mismatch: expected %s, "
"expression has type %s",
dtype->desc, (*expr)->dtype->desc);
+ if (dtype->type == TYPE_MARK &&
+ datatype_equal(datatype_basetype(dtype), datatype_basetype((*expr)->dtype)) &&
+ !expr_is_constant(*expr))
+ return byteorder_conversion(ctx, expr, byteorder);
+
/* we are setting a value, we can't use a set */
switch ((*expr)->etype) {
case EXPR_SET:
@@ -2398,6 +3044,10 @@ static int __stmt_evaluate_arg(struct eval_ctx *ctx, struct stmt *stmt,
return byteorder_conversion(ctx, expr, byteorder);
case EXPR_PREFIX:
return stmt_prefix_conversion(ctx, expr, byteorder);
+ case EXPR_NUMGEN:
+ if (dtype->type == TYPE_IPADDR)
+ return byteorder_conversion(ctx, expr, byteorder);
+ break;
default:
break;
}
@@ -2416,6 +3066,25 @@ static int stmt_evaluate_arg(struct eval_ctx *ctx, struct stmt *stmt,
return __stmt_evaluate_arg(ctx, stmt, dtype, len, byteorder, expr);
}
+/* like stmt_evaluate_arg, but keep existing context created
+ * by previous expr_evaluate().
+ *
+ * This is needed for add/update statements:
+ * ctx->ectx.key has the set key, which may be needed for 'typeof'
+ * sets: the 'add/update' expression might contain integer data types.
+ *
+ * Without the key we cannot derive the element size.
+ */
+static int stmt_evaluate_key(struct eval_ctx *ctx, struct stmt *stmt,
+ const struct datatype *dtype, unsigned int len,
+ enum byteorder byteorder, struct expr **expr)
+{
+ if (expr_evaluate(ctx, expr) < 0)
+ return -1;
+
+ return __stmt_evaluate_arg(ctx, stmt, dtype, len, byteorder, expr);
+}
+
static int stmt_evaluate_verdict(struct eval_ctx *ctx, struct stmt *stmt)
{
if (stmt_evaluate_arg(ctx, stmt, &verdict_type, 0, 0, &stmt->expr) < 0)
@@ -2426,12 +3095,16 @@ static int stmt_evaluate_verdict(struct eval_ctx *ctx, struct stmt *stmt)
if (stmt->expr->verdict != NFT_CONTINUE)
stmt->flags |= STMT_F_TERMINAL;
if (stmt->expr->chain != NULL) {
- if (expr_evaluate(ctx, &stmt->expr->chain) < 0)
+ if (stmt_evaluate_arg(ctx, stmt, &string_type, 0, 0,
+ &stmt->expr->chain) < 0)
return -1;
if (stmt->expr->chain->etype != EXPR_VALUE) {
return expr_error(ctx->msgs, stmt->expr->chain,
"not a value expression");
}
+
+ if (verdict_validate_chainlen(ctx, stmt->expr->chain))
+ return -1;
}
break;
case EXPR_MAP:
@@ -2446,6 +3119,9 @@ static bool stmt_evaluate_payload_need_csum(const struct expr *payload)
{
const struct proto_desc *desc;
+ if (payload->payload.base == PROTO_BASE_INNER_HDR)
+ return true;
+
desc = payload->payload.desc;
return desc && desc->checksum_key;
@@ -2454,14 +3130,22 @@ 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;
+
+ if (stmt->exthdr.val->etype == EXPR_RANGE)
+ return stmt_error_range(ctx, stmt, stmt->exthdr.val);
+
+ return 0;
}
static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
@@ -2474,6 +3158,11 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
mpz_t bitmask, ff;
bool need_csum;
+ if (stmt->payload.expr->payload.inner_desc) {
+ return expr_error(ctx->msgs, stmt->payload.expr,
+ "payload statement for this expression is not supported");
+ }
+
if (__expr_evaluate_payload(ctx, stmt->payload.expr) < 0)
return -1;
@@ -2487,6 +3176,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)) {
@@ -2506,6 +3198,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++;
@@ -2544,9 +3241,9 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
mpz_clear(ff);
assert(sizeof(data) * BITS_PER_BYTE >= masklen);
- mpz_export_data(data, bitmask, BYTEORDER_HOST_ENDIAN, sizeof(data));
+ mpz_export_data(data, bitmask, payload->byteorder, payload_byte_size);
mask = constant_expr_alloc(&payload->location, expr_basetype(payload),
- BYTEORDER_HOST_ENDIAN, masklen, data);
+ payload->byteorder, masklen, data);
mpz_clear(bitmask);
payload_bytes = payload_expr_alloc(&payload->location, NULL, 0);
@@ -2581,6 +3278,24 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
static int stmt_evaluate_meter(struct eval_ctx *ctx, struct stmt *stmt)
{
struct expr *key, *set, *setref;
+ struct set *existing_set;
+ struct table *table;
+
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ ctx->cmd->handle.table.name,
+ ctx->cmd->handle.family);
+ if (table == NULL)
+ return table_not_found(ctx);
+
+ existing_set = set_cache_find(table, stmt->meter.name);
+ if (existing_set)
+ return cmd_error(ctx, &stmt->location,
+ "%s; meter '%s' overlaps an existing %s '%s' in family %s",
+ strerror(EEXIST),
+ stmt->meter.name,
+ set_is_map(existing_set->flags) ? "map" : "set",
+ existing_set->handle.set.name,
+ family2str(existing_set->handle.family));
expr_set_context(&ctx->ectx, NULL, 0);
if (expr_evaluate(ctx, &stmt->meter.key) < 0)
@@ -2600,7 +3315,9 @@ static int stmt_evaluate_meter(struct eval_ctx *ctx, struct stmt *stmt)
set->set_flags |= NFT_SET_TIMEOUT;
setref = implicit_set_declaration(ctx, stmt->meter.name,
- expr_get(key), NULL, set);
+ expr_get(key), NULL, set, 0);
+ if (!setref)
+ return -1;
setref->set->desc.size = stmt->meter.size;
stmt->meter.set = setref;
@@ -2616,26 +3333,45 @@ static int stmt_evaluate_meter(struct eval_ctx *ctx, struct stmt *stmt)
static int stmt_evaluate_meta(struct eval_ctx *ctx, struct stmt *stmt)
{
- return stmt_evaluate_arg(ctx, stmt,
- stmt->meta.tmpl->dtype,
- stmt->meta.tmpl->len,
- stmt->meta.tmpl->byteorder,
- &stmt->meta.expr);
+ int ret;
+
+ ctx->stmt_len = stmt->meta.tmpl->len;
+
+ ret = stmt_evaluate_arg(ctx, stmt,
+ stmt->meta.tmpl->dtype,
+ stmt->meta.tmpl->len,
+ stmt->meta.tmpl->byteorder,
+ &stmt->meta.expr);
+ if (ret < 0)
+ return ret;
+
+ if (stmt->meta.expr->etype == EXPR_RANGE)
+ return stmt_error_range(ctx, stmt, stmt->meta.expr);
+
+ return ret;
}
static int stmt_evaluate_ct(struct eval_ctx *ctx, struct stmt *stmt)
{
- if (stmt_evaluate_arg(ctx, stmt,
- stmt->ct.tmpl->dtype,
- stmt->ct.tmpl->len,
- stmt->ct.tmpl->byteorder,
- &stmt->ct.expr) < 0)
+ int ret;
+
+ ctx->stmt_len = stmt->ct.tmpl->len;
+
+ ret = stmt_evaluate_arg(ctx, stmt,
+ stmt->ct.tmpl->dtype,
+ stmt->ct.tmpl->len,
+ stmt->ct.tmpl->byteorder,
+ &stmt->ct.expr);
+ if (ret < 0)
return -1;
if (stmt->ct.key == NFT_CT_SECMARK && expr_is_constant(stmt->ct.expr))
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;
}
@@ -2643,9 +3379,10 @@ static int reject_payload_gen_dependency_tcp(struct eval_ctx *ctx,
struct stmt *stmt,
struct expr **payload)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *desc;
- desc = ctx->pctx.protocol[PROTO_BASE_TRANSPORT_HDR].desc;
+ desc = pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc;
if (desc != NULL)
return 0;
*payload = payload_expr_alloc(&stmt->location, &proto_tcp,
@@ -2657,9 +3394,10 @@ static int reject_payload_gen_dependency_family(struct eval_ctx *ctx,
struct stmt *stmt,
struct expr **payload)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *base;
- base = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ base = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if (base != NULL)
return 0;
@@ -2718,7 +3456,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;
}
@@ -2726,6 +3464,7 @@ static int stmt_evaluate_reject_inet_family(struct eval_ctx *ctx,
struct stmt *stmt,
const struct proto_desc *desc)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *base;
int protocol;
@@ -2735,23 +3474,26 @@ static int stmt_evaluate_reject_inet_family(struct eval_ctx *ctx,
case NFT_REJECT_ICMPX_UNREACH:
break;
case NFT_REJECT_ICMP_UNREACH:
- base = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ base = pctx->protocol[PROTO_BASE_LL_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
case NFPROTO_IPV4:
+ case __constant_htons(ETH_P_IP):
if (stmt->reject.family == NFPROTO_IPV4)
break;
return stmt_binary_error(ctx, stmt->reject.expr,
- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+ &pctx->protocol[PROTO_BASE_NETWORK_HDR],
"conflicting protocols specified: ip vs ip6");
case NFPROTO_IPV6:
+ case __constant_htons(ETH_P_IPV6):
if (stmt->reject.family == NFPROTO_IPV6)
break;
return stmt_binary_error(ctx, stmt->reject.expr,
- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+ &pctx->protocol[PROTO_BASE_NETWORK_HDR],
"conflicting protocols specified: ip vs ip6");
default:
- BUG("unsupported family");
+ return stmt_error(ctx, stmt,
+ "cannot infer ICMP reject variant to use: explicit value required.\n");
}
break;
}
@@ -2762,9 +3504,10 @@ static int stmt_evaluate_reject_inet_family(struct eval_ctx *ctx,
static int stmt_evaluate_reject_inet(struct eval_ctx *ctx, struct stmt *stmt,
struct expr *expr)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *desc;
- desc = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ desc = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if (desc != NULL &&
stmt_evaluate_reject_inet_family(ctx, stmt, desc) < 0)
return -1;
@@ -2779,13 +3522,14 @@ static int stmt_evaluate_reject_bridge_family(struct eval_ctx *ctx,
struct stmt *stmt,
const struct proto_desc *desc)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *base;
int protocol;
switch (stmt->reject.type) {
case NFT_REJECT_ICMPX_UNREACH:
case NFT_REJECT_TCP_RST:
- base = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ base = pctx->protocol[PROTO_BASE_LL_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
case __constant_htons(ETH_P_IP):
@@ -2793,29 +3537,29 @@ static int stmt_evaluate_reject_bridge_family(struct eval_ctx *ctx,
break;
default:
return stmt_binary_error(ctx, stmt,
- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+ &pctx->protocol[PROTO_BASE_NETWORK_HDR],
"cannot reject this network family");
}
break;
case NFT_REJECT_ICMP_UNREACH:
- base = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ base = pctx->protocol[PROTO_BASE_LL_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
case __constant_htons(ETH_P_IP):
if (NFPROTO_IPV4 == stmt->reject.family)
break;
return stmt_binary_error(ctx, stmt->reject.expr,
- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+ &pctx->protocol[PROTO_BASE_NETWORK_HDR],
"conflicting protocols specified: ip vs ip6");
case __constant_htons(ETH_P_IPV6):
if (NFPROTO_IPV6 == stmt->reject.family)
break;
return stmt_binary_error(ctx, stmt->reject.expr,
- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+ &pctx->protocol[PROTO_BASE_NETWORK_HDR],
"conflicting protocols specified: ip vs ip6");
default:
return stmt_binary_error(ctx, stmt,
- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+ &pctx->protocol[PROTO_BASE_NETWORK_HDR],
"cannot reject this network family");
}
break;
@@ -2827,14 +3571,15 @@ static int stmt_evaluate_reject_bridge_family(struct eval_ctx *ctx,
static int stmt_evaluate_reject_bridge(struct eval_ctx *ctx, struct stmt *stmt,
struct expr *expr)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *desc;
- desc = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ desc = pctx->protocol[PROTO_BASE_LL_HDR].desc;
if (desc != &proto_eth && desc != &proto_vlan && desc != &proto_netdev)
return __stmt_binary_error(ctx, &stmt->location, NULL,
"cannot reject from this link layer protocol");
- desc = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ desc = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if (desc != NULL &&
stmt_evaluate_reject_bridge_family(ctx, stmt, desc) < 0)
return -1;
@@ -2848,7 +3593,9 @@ static int stmt_evaluate_reject_bridge(struct eval_ctx *ctx, struct stmt *stmt,
static int stmt_evaluate_reject_family(struct eval_ctx *ctx, struct stmt *stmt,
struct expr *expr)
{
- switch (ctx->pctx.family) {
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
+
+ switch (pctx->family) {
case NFPROTO_ARP:
return stmt_error(ctx, stmt, "cannot use reject with arp");
case NFPROTO_IPV4:
@@ -2862,7 +3609,7 @@ static int stmt_evaluate_reject_family(struct eval_ctx *ctx, struct stmt *stmt,
return stmt_binary_error(ctx, stmt->reject.expr, stmt,
"abstracted ICMP unreachable not supported");
case NFT_REJECT_ICMP_UNREACH:
- if (stmt->reject.family == ctx->pctx.family)
+ if (stmt->reject.family == pctx->family)
break;
return stmt_binary_error(ctx, stmt->reject.expr, stmt,
"conflicting protocols specified: ip vs ip6");
@@ -2886,35 +3633,38 @@ static int stmt_evaluate_reject_family(struct eval_ctx *ctx, struct stmt *stmt,
static int stmt_evaluate_reject_default(struct eval_ctx *ctx,
struct stmt *stmt)
{
- int protocol;
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *desc, *base;
+ int protocol;
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
stmt->reject.type = NFT_REJECT_ICMP_UNREACH;
- stmt->reject.family = ctx->pctx.family;
- if (ctx->pctx.family == NFPROTO_IPV4)
+ stmt->reject.family = pctx->family;
+ if (pctx->family == NFPROTO_IPV4)
stmt->reject.icmp_code = ICMP_PORT_UNREACH;
else
stmt->reject.icmp_code = ICMP6_DST_UNREACH_NOPORT;
break;
case NFPROTO_INET:
- desc = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ desc = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if (desc == NULL) {
stmt->reject.type = NFT_REJECT_ICMPX_UNREACH;
stmt->reject.icmp_code = NFT_REJECT_ICMPX_PORT_UNREACH;
break;
}
stmt->reject.type = NFT_REJECT_ICMP_UNREACH;
- base = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ base = pctx->protocol[PROTO_BASE_LL_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
case NFPROTO_IPV4:
+ case __constant_htons(ETH_P_IP):
stmt->reject.family = NFPROTO_IPV4;
stmt->reject.icmp_code = ICMP_PORT_UNREACH;
break;
case NFPROTO_IPV6:
+ case __constant_htons(ETH_P_IPV6):
stmt->reject.family = NFPROTO_IPV6;
stmt->reject.icmp_code = ICMP6_DST_UNREACH_NOPORT;
break;
@@ -2922,14 +3672,14 @@ static int stmt_evaluate_reject_default(struct eval_ctx *ctx,
break;
case NFPROTO_BRIDGE:
case NFPROTO_NETDEV:
- desc = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ desc = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if (desc == NULL) {
stmt->reject.type = NFT_REJECT_ICMPX_UNREACH;
stmt->reject.icmp_code = NFT_REJECT_ICMPX_PORT_UNREACH;
break;
}
stmt->reject.type = NFT_REJECT_ICMP_UNREACH;
- base = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ base = pctx->protocol[PROTO_BASE_LL_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
case __constant_htons(ETH_P_IP):
@@ -2948,7 +3698,10 @@ static int stmt_evaluate_reject_default(struct eval_ctx *ctx,
static int stmt_evaluate_reject_icmp(struct eval_ctx *ctx, struct stmt *stmt)
{
- struct parse_ctx parse_ctx = { .tbl = &ctx->nft->output.tbl, };
+ struct parse_ctx parse_ctx = {
+ .tbl = &ctx->nft->output.tbl,
+ .input = &ctx->nft->input,
+ };
struct error_record *erec;
struct expr *code;
@@ -2957,6 +3710,13 @@ static int stmt_evaluate_reject_icmp(struct eval_ctx *ctx, struct stmt *stmt)
erec_queue(erec, ctx->msgs);
return -1;
}
+
+ if (mpz_cmp_ui(code->value, UINT8_MAX) > 0) {
+ expr_free(code);
+ return expr_error(ctx->msgs, stmt->reject.expr,
+ "reject code must be integer in range 0-255");
+ }
+
stmt->reject.icmp_code = mpz_get_uint8(code->value);
expr_free(code);
@@ -2965,9 +3725,9 @@ static int stmt_evaluate_reject_icmp(struct eval_ctx *ctx, struct stmt *stmt)
static int stmt_evaluate_reset(struct eval_ctx *ctx, struct stmt *stmt)
{
- int protonum;
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *desc, *base;
- struct proto_ctx *pctx = &ctx->pctx;
+ int protonum;
desc = pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc;
if (desc == NULL)
@@ -2984,7 +3744,7 @@ static int stmt_evaluate_reset(struct eval_ctx *ctx, struct stmt *stmt)
default:
if (stmt->reject.type == NFT_REJECT_TCP_RST) {
return stmt_binary_error(ctx, stmt,
- &ctx->pctx.protocol[PROTO_BASE_TRANSPORT_HDR],
+ &pctx->protocol[PROTO_BASE_TRANSPORT_HDR],
"you cannot use tcp reset with this protocol");
}
break;
@@ -3012,13 +3772,14 @@ static int stmt_evaluate_reject(struct eval_ctx *ctx, struct stmt *stmt)
static int nat_evaluate_family(struct eval_ctx *ctx, struct stmt *stmt)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *nproto;
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
if (stmt->nat.family == NFPROTO_UNSPEC)
- stmt->nat.family = ctx->pctx.family;
+ stmt->nat.family = pctx->family;
return 0;
case NFPROTO_INET:
if (!stmt->nat.addr) {
@@ -3028,7 +3789,7 @@ static int nat_evaluate_family(struct eval_ctx *ctx, struct stmt *stmt)
if (stmt->nat.family != NFPROTO_UNSPEC)
return 0;
- nproto = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ nproto = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if (nproto == &proto_ip)
stmt->nat.family = NFPROTO_IPV4;
@@ -3057,7 +3818,7 @@ static const struct datatype *get_addr_dtype(uint8_t family)
static int evaluate_addr(struct eval_ctx *ctx, struct stmt *stmt,
struct expr **expr)
{
- struct proto_ctx *pctx = &ctx->pctx;
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct datatype *dtype;
dtype = get_addr_dtype(pctx->family);
@@ -3110,7 +3871,14 @@ static bool nat_evaluate_addr_has_th_expr(const struct expr *map)
static int nat_evaluate_transport(struct eval_ctx *ctx, struct stmt *stmt,
struct expr **expr)
{
- struct proto_ctx *pctx = &ctx->pctx;
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
+ int err;
+
+ err = stmt_evaluate_arg(ctx, stmt,
+ &inet_service_type, 2 * BITS_PER_BYTE,
+ BYTEORDER_BIG_ENDIAN, expr);
+ if (err < 0)
+ return err;
if (pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc == NULL &&
!nat_evaluate_addr_has_th_expr(stmt->nat.addr))
@@ -3118,41 +3886,75 @@ static int nat_evaluate_transport(struct eval_ctx *ctx, struct stmt *stmt,
"transport protocol mapping is only "
"valid after transport protocol match");
- return stmt_evaluate_arg(ctx, stmt,
- &inet_service_type, 2 * BITS_PER_BYTE,
- BYTEORDER_BIG_ENDIAN, expr);
+ return 0;
}
static int stmt_evaluate_l3proto(struct eval_ctx *ctx,
struct stmt *stmt, uint8_t family)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *nproto;
- nproto = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ nproto = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if ((nproto == &proto_ip && family != NFPROTO_IPV4) ||
(nproto == &proto_ip6 && family != NFPROTO_IPV6))
return stmt_binary_error(ctx, stmt,
- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+ &pctx->protocol[PROTO_BASE_NETWORK_HDR],
"conflicting protocols specified: %s vs. %s. You must specify ip or ip6 family in %s statement",
- ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc->name,
+ pctx->protocol[PROTO_BASE_NETWORK_HDR].desc->name,
family2str(family),
stmt->ops->name);
return 0;
}
+static void expr_family_infer(struct proto_ctx *pctx, const struct expr *expr,
+ uint8_t *family)
+{
+ struct expr *i;
+
+ if (expr->etype == EXPR_MAP) {
+ switch (expr->map->etype) {
+ case EXPR_CONCAT:
+ list_for_each_entry(i, &expr->map->expressions, list) {
+ if (i->etype == EXPR_PAYLOAD) {
+ if (i->payload.desc == &proto_ip)
+ *family = NFPROTO_IPV4;
+ else if (i->payload.desc == &proto_ip6)
+ *family = NFPROTO_IPV6;
+ }
+ }
+ break;
+ case EXPR_PAYLOAD:
+ if (expr->map->payload.desc == &proto_ip)
+ *family = NFPROTO_IPV4;
+ else if (expr->map->payload.desc == &proto_ip6)
+ *family = NFPROTO_IPV6;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
static int stmt_evaluate_addr(struct eval_ctx *ctx, struct stmt *stmt,
- uint8_t family,
- struct expr **addr)
+ uint8_t *family, struct expr **addr)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct datatype *dtype;
int err;
- if (ctx->pctx.family == NFPROTO_INET) {
- dtype = get_addr_dtype(family);
- if (dtype->size == 0)
+ if (pctx->family == NFPROTO_INET) {
+ if (*family == NFPROTO_INET ||
+ *family == NFPROTO_UNSPEC)
+ expr_family_infer(pctx, *addr, family);
+
+ dtype = get_addr_dtype(*family);
+ if (dtype->size == 0) {
return stmt_error(ctx, stmt,
- "ip or ip6 must be specified with address for inet tables.");
+ "specify `%s ip' or '%s ip6' in %s table to disambiguate",
+ stmt_name(stmt), stmt_name(stmt), family2str(pctx->family));
+ }
err = stmt_evaluate_arg(ctx, stmt, dtype, dtype->size,
BYTEORDER_BIG_ENDIAN, addr);
@@ -3165,16 +3967,15 @@ static int stmt_evaluate_addr(struct eval_ctx *ctx, struct stmt *stmt,
static int stmt_evaluate_nat_map(struct eval_ctx *ctx, struct stmt *stmt)
{
- struct proto_ctx *pctx = &ctx->pctx;
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
struct expr *one, *two, *data, *tmp;
- const struct datatype *dtype;
- int addr_type, err;
+ const struct datatype *dtype = NULL;
+ const struct datatype *dtype2;
+ int addr_type;
+ int err;
- if (pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc == NULL &&
- !nat_evaluate_addr_has_th_expr(stmt->nat.addr))
- return stmt_binary_error(ctx, stmt->nat.addr, stmt,
- "transport protocol mapping is only "
- "valid after transport protocol match");
+ if (stmt->nat.family == NFPROTO_INET)
+ expr_family_infer(pctx, stmt->nat.addr, &stmt->nat.family);
switch (stmt->nat.family) {
case NFPROTO_IPV4:
@@ -3184,16 +3985,30 @@ static int stmt_evaluate_nat_map(struct eval_ctx *ctx, struct stmt *stmt)
addr_type = TYPE_IP6ADDR;
break;
default:
- return -1;
+ return stmt_error(ctx, stmt,
+ "specify `%s ip' or '%s ip6' in %s table to disambiguate",
+ stmt_name(stmt), stmt_name(stmt), family2str(pctx->family));
}
dtype = concat_type_alloc((addr_type << TYPE_BITS) | TYPE_INET_SERVICE);
expr_set_context(&ctx->ectx, dtype, dtype->size);
- if (expr_evaluate(ctx, &stmt->nat.addr))
- return -1;
+ if (expr_evaluate(ctx, &stmt->nat.addr)) {
+ err = -1;
+ goto out;
+ }
- if (stmt->nat.addr->etype != EXPR_MAP)
- return 0;
+ if (pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc == NULL &&
+ !nat_evaluate_addr_has_th_expr(stmt->nat.addr)) {
+ err = stmt_binary_error(ctx, stmt->nat.addr, stmt,
+ "transport protocol mapping is only "
+ "valid after transport protocol match");
+ goto out;
+ }
+
+ if (stmt->nat.addr->etype != EXPR_MAP) {
+ err = 0;
+ goto out;
+ }
data = stmt->nat.addr->mappings->set->data;
if (data->flags & EXPR_F_INTERVAL)
@@ -3201,36 +4016,42 @@ static int stmt_evaluate_nat_map(struct eval_ctx *ctx, struct stmt *stmt)
datatype_set(data, dtype);
- if (expr_ops(data)->type != EXPR_CONCAT)
- return __stmt_evaluate_arg(ctx, stmt, dtype, dtype->size,
+ if (expr_ops(data)->type != EXPR_CONCAT) {
+ err = __stmt_evaluate_arg(ctx, stmt, dtype, dtype->size,
BYTEORDER_BIG_ENDIAN,
&stmt->nat.addr);
+ goto out;
+ }
one = list_first_entry(&data->expressions, struct expr, list);
two = list_entry(one->list.next, struct expr, list);
- if (one == two || !list_is_last(&two->list, &data->expressions))
- return __stmt_evaluate_arg(ctx, stmt, dtype, dtype->size,
+ if (one == two || !list_is_last(&two->list, &data->expressions)) {
+ err = __stmt_evaluate_arg(ctx, stmt, dtype, dtype->size,
BYTEORDER_BIG_ENDIAN,
&stmt->nat.addr);
+ goto out;
+ }
- dtype = get_addr_dtype(stmt->nat.family);
+ dtype2 = get_addr_dtype(stmt->nat.family);
tmp = one;
- err = __stmt_evaluate_arg(ctx, stmt, dtype, dtype->size,
+ err = __stmt_evaluate_arg(ctx, stmt, dtype2, dtype2->size,
BYTEORDER_BIG_ENDIAN,
&tmp);
if (err < 0)
- return err;
+ goto out;
if (tmp != one)
BUG("Internal error: Unexpected alteration of l3 expression");
tmp = two;
err = nat_evaluate_transport(ctx, stmt, &tmp);
if (err < 0)
- return err;
+ goto out;
if (tmp != two)
BUG("Internal error: Unexpected alteration of l4 expression");
+out:
+ datatype_free(dtype);
return err;
}
@@ -3256,6 +4077,12 @@ static bool nat_concat_map(struct eval_ctx *ctx, struct stmt *stmt)
if (expr_evaluate(ctx, &stmt->nat.addr->mappings))
return false;
+ if (!set_is_datamap(stmt->nat.addr->mappings->set->flags)) {
+ expr_error(ctx->msgs, stmt->nat.addr->mappings,
+ "Expression is not a map");
+ return false;
+ }
+
if (stmt->nat.addr->mappings->set->data->etype == EXPR_CONCAT ||
stmt->nat.addr->mappings->set->data->dtype->subtypes) {
stmt->nat.type_flags |= STMT_NAT_F_CONCAT;
@@ -3293,7 +4120,7 @@ static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt)
return 0;
}
- err = stmt_evaluate_addr(ctx, stmt, stmt->nat.family,
+ err = stmt_evaluate_addr(ctx, stmt, &stmt->nat.family,
&stmt->nat.addr);
if (err < 0)
return err;
@@ -3313,13 +4140,14 @@ static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt)
static int stmt_evaluate_tproxy(struct eval_ctx *ctx, struct stmt *stmt)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
int err;
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6: /* fallthrough */
if (stmt->tproxy.family == NFPROTO_UNSPEC)
- stmt->tproxy.family = ctx->pctx.family;
+ stmt->tproxy.family = pctx->family;
break;
case NFPROTO_INET:
break;
@@ -3328,7 +4156,7 @@ static int stmt_evaluate_tproxy(struct eval_ctx *ctx, struct stmt *stmt)
"tproxy is only supported for IPv4/IPv6/INET");
}
- if (ctx->pctx.protocol[PROTO_BASE_TRANSPORT_HDR].desc == NULL)
+ if (pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc == NULL)
return stmt_error(ctx, stmt, "Transparent proxy support requires"
" transport protocol match");
@@ -3340,22 +4168,22 @@ static int stmt_evaluate_tproxy(struct eval_ctx *ctx, struct stmt *stmt)
return err;
if (stmt->tproxy.addr != NULL) {
- if (stmt->tproxy.addr->etype == EXPR_RANGE)
- return stmt_error(ctx, stmt, "Address ranges are not supported for tproxy.");
-
- err = stmt_evaluate_addr(ctx, stmt, stmt->tproxy.family,
+ err = stmt_evaluate_addr(ctx, stmt, &stmt->tproxy.family,
&stmt->tproxy.addr);
-
if (err < 0)
return err;
+
+ if (stmt->tproxy.addr->etype == EXPR_RANGE)
+ return stmt_error(ctx, stmt, "Address ranges are not supported for tproxy.");
}
if (stmt->tproxy.port != NULL) {
- if (stmt->tproxy.port->etype == EXPR_RANGE)
- return stmt_error(ctx, stmt, "Port ranges are not supported for tproxy.");
err = nat_evaluate_transport(ctx, stmt, &stmt->tproxy.port);
if (err < 0)
return err;
+
+ if (stmt->tproxy.port->etype == EXPR_RANGE)
+ return stmt_error(ctx, stmt, "Port ranges are not supported for tproxy.");
}
return 0;
@@ -3392,7 +4220,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;
@@ -3407,13 +4235,14 @@ static int stmt_evaluate_chain(struct eval_ctx *ctx, struct stmt *stmt)
struct eval_ctx rule_ctx = {
.nft = ctx->nft,
.msgs = ctx->msgs,
+ .cmd = ctx->cmd,
};
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)
@@ -3430,11 +4259,17 @@ static int stmt_evaluate_chain(struct eval_ctx *ctx, struct stmt *stmt)
return 0;
}
+static int stmt_evaluate_optstrip(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ return expr_evaluate(ctx, &stmt->optstrip.expr);
+}
+
static int stmt_evaluate_dup(struct eval_ctx *ctx, struct stmt *stmt)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
int err;
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
if (stmt->dup.to == NULL)
@@ -3451,6 +4286,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:
@@ -3469,15 +4307,20 @@ 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;
}
static int stmt_evaluate_fwd(struct eval_ctx *ctx, struct stmt *stmt)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct datatype *dtype;
int err, len;
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_NETDEV:
if (stmt->fwd.dev == NULL)
return stmt_error(ctx, stmt,
@@ -3489,6 +4332,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:
@@ -3507,6 +4353,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:
@@ -3540,15 +4389,25 @@ 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 prefix[NF_LOG_PREFIXLEN] = {}, tmp[NF_LOG_PREFIXLEN] = {};
- int len = sizeof(prefix), offset = 0, ret;
+ char tmp[NF_LOG_PREFIXLEN] = {};
+ char prefix[NF_LOG_PREFIXLEN];
+ size_t len = sizeof(prefix);
+ size_t offset = 0;
struct expr *expr;
- size_t size = 0;
- if (stmt->log.prefix->etype != EXPR_LIST)
+ 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);
@@ -3559,13 +4418,13 @@ static int stmt_evaluate_log_prefix(struct eval_ctx *ctx, struct stmt *stmt)
expr->sym->expr->identifier);
break;
default:
- BUG("unknown expresion type %s\n", expr_name(expr));
+ BUG("unknown expression type %s\n", expr_name(expr));
break;
}
- SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+ SNPRINTF_BUFFER_SIZE(ret, &len, &offset);
}
- if (len == NF_LOG_PREFIXLEN)
+ if (len == 0)
return stmt_error(ctx, stmt, "log prefix is too long");
expr = constant_expr_alloc(&stmt->log.prefix->location, &string_type,
@@ -3603,6 +4462,7 @@ static int stmt_evaluate_log(struct eval_ctx *ctx, struct stmt *stmt)
static int stmt_evaluate_set(struct eval_ctx *ctx, struct stmt *stmt)
{
+ struct set *this_set;
struct stmt *this;
expr_set_context(&ctx->ectx, NULL, 0);
@@ -3612,7 +4472,7 @@ static int stmt_evaluate_set(struct eval_ctx *ctx, struct stmt *stmt)
return expr_error(ctx->msgs, stmt->set.set,
"Expression does not refer to a set");
- if (stmt_evaluate_arg(ctx, stmt,
+ if (stmt_evaluate_key(ctx, stmt,
stmt->set.set->set->key->dtype,
stmt->set.set->set->key->len,
stmt->set.set->set->key->byteorder,
@@ -3632,6 +4492,15 @@ static int stmt_evaluate_set(struct eval_ctx *ctx, struct stmt *stmt)
"statement must be stateful");
}
+ this_set = stmt->set.set->set;
+
+ /* Make sure EVAL flag is set on set definition so that kernel
+ * picks a set that allows updates from the packet path.
+ *
+ * Alternatively we could error out in case 'flags dynamic' was
+ * not given, but we can repair this here.
+ */
+ this_set->flags |= NFT_SET_EVAL;
return 0;
}
@@ -3646,7 +4515,11 @@ static int stmt_evaluate_map(struct eval_ctx *ctx, struct stmt *stmt)
return expr_error(ctx->msgs, stmt->map.set,
"Expression does not refer to a set");
- if (stmt_evaluate_arg(ctx, stmt,
+ if (!set_is_map(stmt->map.set->set->flags))
+ return expr_error(ctx->msgs, stmt->map.set,
+ "%s is not a map", stmt->map.set->set->handle.set.name);
+
+ if (stmt_evaluate_key(ctx, stmt,
stmt->map.set->set->key->dtype,
stmt->map.set->set->key->len,
stmt->map.set->set->key->byteorder,
@@ -3671,6 +4544,9 @@ static int stmt_evaluate_map(struct eval_ctx *ctx, struct stmt *stmt)
if (stmt->map.data->comment != NULL)
return expr_error(ctx->msgs, stmt->map.data,
"Data expression comments are not supported");
+ if (stmt->map.data->timeout > 0)
+ return expr_error(ctx->msgs, stmt->map.data,
+ "Data expression timeouts are not supported");
list_for_each_entry(this, &stmt->map.stmt_list, list) {
if (stmt_evaluate(ctx, this) < 0)
@@ -3707,7 +4583,10 @@ static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)
ctx->ectx.len, NULL);
mappings = implicit_set_declaration(ctx, "__objmap%d",
- key, NULL, mappings);
+ key, NULL, mappings,
+ NFT_SET_ANONYMOUS);
+ if (!mappings)
+ return -1;
mappings->set->objtype = stmt->objref.type;
map->mappings = mappings;
@@ -3715,10 +4594,15 @@ static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)
ctx->set = mappings->set;
if (expr_evaluate(ctx, &map->mappings->set->init) < 0)
return -1;
+
+ if (set_is_interval(map->mappings->set->init->set_flags) &&
+ !(map->mappings->set->init->set_flags & NFT_SET_CONCAT) &&
+ interval_set_eval(ctx, ctx->set, map->mappings->set->init) < 0)
+ return -1;
+
ctx->set = NULL;
- map->mappings->set->flags |=
- map->mappings->set->init->set_flags;
+ map_set_concat_info(map);
/* fall through */
case EXPR_SYMBOL:
if (expr_evaluate(ctx, &map->mappings) < 0)
@@ -3735,7 +4619,7 @@ static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)
expr_name(map->mappings));
}
- if (!datatype_equal(map->map->dtype, map->mappings->set->key->dtype))
+ if (!datatype_compatible(map->mappings->set->key->dtype, map->map->dtype))
return expr_binary_error(ctx->msgs, map->mappings, map->map,
"datatype mismatch, map expects %s, "
"mapping expression has type %s",
@@ -3783,9 +4667,12 @@ 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:
+ case STMT_LAST:
case STMT_LIMIT:
case STMT_QUOTA:
case STMT_NOTRACK:
@@ -3829,6 +4716,8 @@ int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
return stmt_evaluate_synproxy(ctx, stmt);
case STMT_CHAIN:
return stmt_evaluate_chain(ctx, stmt);
+ case STMT_OPTSTRIP:
+ return stmt_evaluate_optstrip(ctx, stmt);
default:
BUG("unknown statement type %s\n", stmt->ops->name);
}
@@ -3850,13 +4739,26 @@ static int setelem_evaluate(struct eval_ctx *ctx, struct cmd *cmd)
return set_not_found(ctx, &ctx->cmd->handle.set.location,
ctx->cmd->handle.set.name);
+ if (set->key == NULL)
+ return -1;
+
+ set->existing_set = set;
ctx->set = set;
expr_set_context(&ctx->ectx, set->key->dtype, set->key->len);
if (expr_evaluate(ctx, &cmd->expr) < 0)
return -1;
- ctx->set = NULL;
cmd->elem.set = set_get(set);
+ if (set_is_interval(ctx->set->flags)) {
+ if (!(set->flags & NFT_SET_CONCAT) &&
+ interval_set_eval(ctx, ctx->set, cmd->expr) < 0)
+ return -1;
+
+ assert(cmd->expr->etype == EXPR_SET);
+ cmd->expr->set_flags |= NFT_SET_INTERVAL;
+ }
+
+ ctx->set = NULL;
return 0;
}
@@ -3878,8 +4780,8 @@ static int set_key_data_error(struct eval_ctx *ctx, const struct set *set,
static int set_expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
{
unsigned int flags = EXPR_F_CONSTANT | EXPR_F_SINGLETON;
+ uint32_t ntype = 0, size = 0;
struct expr *i, *next;
- uint32_t ntype = 0;
list_for_each_entry_safe(i, next, &(*expr)->expressions, list) {
unsigned dsize_bytes;
@@ -3890,7 +4792,17 @@ static int set_expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
return expr_error(ctx->msgs, i,
"specify either ip or ip6 for address matching");
- if (i->dtype->size == 0)
+ if (i->etype == EXPR_PAYLOAD &&
+ i->dtype->type == TYPE_INTEGER) {
+ struct datatype *dtype;
+
+ dtype = datatype_clone(i->dtype);
+ dtype->size = i->len;
+ dtype->byteorder = i->byteorder;
+ __datatype_set(i, dtype);
+ }
+
+ if (i->dtype->size == 0 && i->len == 0)
return expr_binary_error(ctx->msgs, i, *expr,
"can not use variable sized "
"data types (%s) in concat "
@@ -3901,26 +4813,71 @@ static int set_expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
ntype = concat_subtype_add(ntype, i->dtype->type);
- dsize_bytes = div_round_up(i->dtype->size, BITS_PER_BYTE);
+ 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);
+
+ 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;
- datatype_set(*expr, concat_type_alloc(ntype));
- (*expr)->len = (*expr)->dtype->size;
+ __datatype_set(*expr, concat_type_alloc(ntype));
+ (*expr)->len = size;
expr_set_context(&ctx->ectx, (*expr)->dtype, (*expr)->len);
+ ctx->ectx.key = *expr;
+
+ return 0;
+}
+
+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) {
+ set->errors = true;
+ return -1;
+ }
+ if (set->init->etype != EXPR_SET)
+ return expr_error(ctx->msgs, set->init, "Set %s: Unexpected initial type %s, missing { }?",
+ set->handle.set.name, expr_name(set->init));
+ }
+
+ if (set_is_interval(ctx->set->flags) &&
+ !(ctx->set->flags & NFT_SET_CONCAT) &&
+ interval_set_eval(ctx, ctx->set, set->init) < 0)
+ return -1;
+
+ ctx->set = NULL;
return 0;
}
static int set_evaluate(struct eval_ctx *ctx, struct set *set)
{
+ struct set *existing_set = NULL;
unsigned int num_stmts = 0;
struct table *table;
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,
@@ -3928,15 +4885,22 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
if (table == NULL)
return table_not_found(ctx);
- if (!set_cache_find(table, set->handle.set.name))
+ existing_set = set_cache_find(table, set->handle.set.name);
+ if (!existing_set)
set_cache_add(set_get(set), table);
+
+ if (existing_set && existing_set->flags & NFT_SET_EVAL) {
+ uint32_t existing_flags = existing_set->flags & ~NFT_SET_EVAL;
+ uint32_t new_flags = set->flags & ~NFT_SET_EVAL;
+
+ if (existing_flags == new_flags)
+ set->flags |= NFT_SET_EVAL;
+ }
}
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);
@@ -3958,6 +4922,21 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
set->flags |= NFT_SET_CONCAT;
}
+ if (set_is_anonymous(set->flags) && set->key->etype == EXPR_CONCAT) {
+ struct expr *i;
+
+ list_for_each_entry(i, &set->init->expressions, list) {
+ if ((i->etype == EXPR_SET_ELEM &&
+ i->key->etype != EXPR_CONCAT &&
+ i->key->etype != EXPR_SET_ELEM_CATCHALL) ||
+ (i->etype == EXPR_MAPPING &&
+ i->left->etype == EXPR_SET_ELEM &&
+ i->left->key->etype != EXPR_CONCAT &&
+ i->left->key->etype != EXPR_SET_ELEM_CATCHALL))
+ return expr_error(ctx->msgs, i, "expression is not a concatenation");
+ }
+ }
+
if (set_is_datamap(set->flags)) {
if (set->data == NULL)
return set_error(ctx, set, "map definition does not "
@@ -3992,20 +4971,16 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
if (num_stmts > 1)
set->flags |= NFT_SET_EXPR;
- if (set_is_anonymous(set->flags))
- return 0;
-
- ctx->set = set;
- if (set->init != NULL) {
- __expr_set_context(&ctx->ectx, set->key->dtype,
- set->key->byteorder, set->key->len, 0);
- if (expr_evaluate(ctx, &set->init) < 0)
+ if (set_is_anonymous(set->flags)) {
+ if (set_is_interval(set->init->set_flags) &&
+ !(set->init->set_flags & NFT_SET_CONCAT) &&
+ interval_set_eval(ctx, set, set->init) < 0)
return -1;
- if (set->init->etype != EXPR_SET)
- return expr_error(ctx->msgs, set->init, "Set %s: Unexpected initial type %s, missing { }?",
- set->handle.set.name, expr_name(set->init));
+
+ return 0;
}
- ctx->set = NULL;
+
+ set->existing_set = existing_set;
return 0;
}
@@ -4036,7 +5011,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;
@@ -4111,7 +5086,7 @@ static bool evaluate_device_expr(struct eval_ctx *ctx, struct expr **dev_expr)
case EXPR_VALUE:
break;
default:
- BUG("invalid expresion type %s\n", expr_name(expr));
+ BUG("invalid expression type %s\n", expr_name(expr));
break;
}
@@ -4134,8 +5109,12 @@ static int flowtable_evaluate(struct eval_ctx *ctx, struct flowtable *ft)
if (table == NULL)
return table_not_found(ctx);
- if (!ft_cache_find(table, ft->handle.flowtable.name))
+ if (!ft_cache_find(table, ft->handle.flowtable.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);
+ }
if (ft->hook.name) {
ft->hook.num = str2hooknum(NFPROTO_NETDEV, ft->hook.name);
@@ -4245,7 +5224,9 @@ static int rule_evaluate(struct eval_ctx *ctx, struct rule *rule,
struct stmt *stmt, *tstmt = NULL;
struct error_record *erec;
- proto_ctx_init(&ctx->pctx, rule->handle.family, ctx->nft->debug_mask);
+ proto_ctx_init(&ctx->_pctx[0], rule->handle.family, ctx->nft->debug_mask, false);
+ /* use NFPROTO_BRIDGE to set up proto_eth as base protocol. */
+ proto_ctx_init(&ctx->_pctx[1], NFPROTO_BRIDGE, ctx->nft->debug_mask, true);
memset(&ctx->ectx, 0, sizeof(ctx->ectx));
ctx->rule = rule;
@@ -4260,6 +5241,8 @@ static int rule_evaluate(struct eval_ctx *ctx, struct rule *rule,
return -1;
if (stmt->flags & STMT_F_TERMINAL)
tstmt = stmt;
+
+ ctx->inner_desc = NULL;
}
erec = rule_postprocess(rule);
@@ -4310,6 +5293,8 @@ static uint32_t str2hooknum(uint32_t family, const char *hook)
case NFPROTO_NETDEV:
if (!strcmp(hook, "ingress"))
return NF_NETDEV_INGRESS;
+ else if (!strcmp(hook, "egress"))
+ return NF_NETDEV_EGRESS;
break;
default:
break;
@@ -4321,7 +5306,6 @@ static uint32_t str2hooknum(uint32_t family, const char *hook)
static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
{
struct table *table;
- struct rule *rule;
table = table_cache_find(&ctx->nft->cache.table_cache,
ctx->cmd->handle.table.name,
@@ -4331,7 +5315,7 @@ static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
if (chain == NULL) {
if (!chain_cache_find(table, ctx->cmd->handle.chain.name)) {
- chain = chain_alloc(NULL);
+ chain = chain_alloc();
handle_merge(&chain->handle, &ctx->cmd->handle);
chain_cache_add(chain, table);
}
@@ -4361,15 +5345,17 @@ static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
return chain_error(ctx, chain, "invalid policy expression %s",
expr_name(chain->policy));
}
+ }
+
+ if (chain->dev_expr) {
+ if (!(chain->flags & CHAIN_F_BASECHAIN))
+ chain->flags |= CHAIN_F_BASECHAIN;
if (chain->handle.family == NFPROTO_NETDEV ||
(chain->handle.family == NFPROTO_INET &&
chain->hook.num == NF_INET_INGRESS)) {
- if (!chain->dev_expr)
- return __stmt_binary_error(ctx, &chain->loc, NULL,
- "Missing `device' in this chain definition");
-
- if (!evaluate_device_expr(ctx, &chain->dev_expr))
+ if (chain->dev_expr &&
+ !evaluate_device_expr(ctx, &chain->dev_expr))
return -1;
} else if (chain->dev_expr) {
return __stmt_binary_error(ctx, &chain->dev_expr->location, NULL,
@@ -4377,11 +5363,6 @@ static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
}
}
- list_for_each_entry(rule, &chain->rules, list) {
- handle_merge(&rule->handle, &chain->handle);
- if (rule_evaluate(ctx, rule, CMD_INVALID) < 0)
- return -1;
- }
return 0;
}
@@ -4415,8 +5396,8 @@ 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);
- xfree(ts);
+ free_const(ts->timeout_str);
+ free(ts);
}
return 0;
@@ -4449,11 +5430,6 @@ static int obj_evaluate(struct eval_ctx *ctx, struct obj *obj)
static int table_evaluate(struct eval_ctx *ctx, struct table *table)
{
- struct flowtable *ft;
- struct chain *chain;
- struct set *set;
- struct obj *obj;
-
if (!table_cache_find(&ctx->nft->cache.table_cache,
ctx->cmd->handle.table.name,
ctx->cmd->handle.family)) {
@@ -4466,34 +5442,6 @@ static int table_evaluate(struct eval_ctx *ctx, struct table *table)
}
}
- if (ctx->cmd->table == NULL)
- return 0;
-
- ctx->table = table;
- list_for_each_entry(set, &table->sets, list) {
- expr_set_context(&ctx->ectx, NULL, 0);
- handle_merge(&set->handle, &table->handle);
- if (set_evaluate(ctx, set) < 0)
- return -1;
- }
- list_for_each_entry(chain, &table->chains, list) {
- handle_merge(&chain->handle, &table->handle);
- ctx->cmd->handle.chain.location = chain->location;
- if (chain_evaluate(ctx, chain) < 0)
- return -1;
- }
- list_for_each_entry(ft, &table->flowtables, list) {
- handle_merge(&ft->handle, &table->handle);
- if (flowtable_evaluate(ctx, ft) < 0)
- return -1;
- }
- list_for_each_entry(obj, &table->objs, list) {
- handle_merge(&obj->handle, &table->handle);
- if (obj_evaluate(ctx, obj) < 0)
- return -1;
- }
-
- ctx->table = NULL;
return 0;
}
@@ -4505,6 +5453,8 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_SET:
handle_merge(&cmd->set->handle, &cmd->handle);
return set_evaluate(ctx, cmd->set);
+ case CMD_OBJ_SETELEMS:
+ return elems_evaluate(ctx, cmd->set);
case CMD_OBJ_RULE:
handle_merge(&cmd->rule->handle, &cmd->handle);
return rule_evaluate(ctx, cmd->rule, cmd->op);
@@ -4704,7 +5654,7 @@ static int obj_not_found(struct eval_ctx *ctx, const struct location *loc,
return cmd_error(ctx, loc, "%s", strerror(ENOENT));
return cmd_error(ctx, loc,
- "%s; did you mean obj ‘%s’ in table %s ‘%s’?",
+ "%s; did you mean obj '%s' in table %s '%s'?",
strerror(ENOENT), obj->handle.obj.name,
family2str(obj->handle.family),
table->handle.table.name);
@@ -4750,38 +5700,8 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd)
return 0;
case CMD_OBJ_SET:
- table = table_cache_find(&ctx->nft->cache.table_cache,
- cmd->handle.table.name,
- cmd->handle.family);
- if (!table)
- return table_not_found(ctx);
-
- set = set_cache_find(table, cmd->handle.set.name);
- if (set == NULL)
- return set_not_found(ctx, &ctx->cmd->handle.set.location,
- ctx->cmd->handle.set.name);
- else if (!set_is_literal(set->flags))
- return cmd_error(ctx, &ctx->cmd->handle.set.location,
- "%s", strerror(ENOENT));
-
- return 0;
- case CMD_OBJ_METER:
- table = table_cache_find(&ctx->nft->cache.table_cache,
- cmd->handle.table.name,
- cmd->handle.family);
- if (!table)
- return table_not_found(ctx);
-
- set = set_cache_find(table, cmd->handle.set.name);
- if (set == NULL)
- return set_not_found(ctx, &ctx->cmd->handle.set.location,
- ctx->cmd->handle.set.name);
- else if (!set_is_meter(set->flags))
- return cmd_error(ctx, &ctx->cmd->handle.set.location,
- "%s", strerror(ENOENT));
-
- return 0;
case CMD_OBJ_MAP:
+ case CMD_OBJ_METER:
table = table_cache_find(&ctx->nft->cache.table_cache,
cmd->handle.table.name,
cmd->handle.family);
@@ -4792,10 +5712,13 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd)
if (set == NULL)
return set_not_found(ctx, &ctx->cmd->handle.set.location,
ctx->cmd->handle.set.name);
- else if (!map_is_literal(set->flags))
+ if ((cmd->obj == CMD_OBJ_SET && !set_is_literal(set->flags)) ||
+ (cmd->obj == CMD_OBJ_MAP && !map_is_literal(set->flags)) ||
+ (cmd->obj == CMD_OBJ_METER && !set_is_meter_compat(set->flags)))
return cmd_error(ctx, &ctx->cmd->handle.set.location,
"%s", strerror(ENOENT));
+ cmd->set = set_get(set);
return 0;
case CMD_OBJ_CHAIN:
table = table_cache_find(&ctx->nft->cache.table_cache,
@@ -4845,6 +5768,8 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_FLOWTABLES:
case CMD_OBJ_SECMARKS:
case CMD_OBJ_SYNPROXYS:
+ case CMD_OBJ_CT_TIMEOUTS:
+ case CMD_OBJ_CT_EXPECTATIONS:
if (cmd->handle.table.name == NULL)
return 0;
if (!table_cache_find(&ctx->nft->cache.table_cache,
@@ -4880,6 +5805,8 @@ static int cmd_evaluate_reset(struct eval_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_QUOTA:
case CMD_OBJ_COUNTERS:
case CMD_OBJ_QUOTAS:
+ case CMD_OBJ_RULES:
+ case CMD_OBJ_RULE:
if (cmd->handle.table.name == NULL)
return 0;
if (!table_cache_find(&ctx->nft->cache.table_cache,
@@ -4888,6 +5815,11 @@ static int cmd_evaluate_reset(struct eval_ctx *ctx, struct cmd *cmd)
return table_not_found(ctx);
return 0;
+ case CMD_OBJ_ELEMENTS:
+ return setelem_evaluate(ctx, cmd);
+ case CMD_OBJ_SET:
+ case CMD_OBJ_MAP:
+ return cmd_evaluate_list(ctx, cmd);
default:
BUG("invalid command object type %u\n", cmd->obj);
}
@@ -4961,7 +5893,7 @@ static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd)
if (set == NULL)
return set_not_found(ctx, &ctx->cmd->handle.set.location,
ctx->cmd->handle.set.name);
- else if (!set_is_meter(set->flags))
+ else if (!set_is_meter_compat(set->flags))
return cmd_error(ctx, &ctx->cmd->handle.set.location,
"%s", strerror(ENOENT));
@@ -5124,11 +6056,12 @@ static const char * const cmd_op_name[] = {
[CMD_EXPORT] = "export",
[CMD_MONITOR] = "monitor",
[CMD_DESCRIBE] = "describe",
+ [CMD_DESTROY] = "destroy",
};
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];
@@ -5156,6 +6089,7 @@ int cmd_evaluate(struct eval_ctx *ctx, struct cmd *cmd)
case CMD_INSERT:
return cmd_evaluate_add(ctx, cmd);
case CMD_DELETE:
+ case CMD_DESTROY:
return cmd_evaluate_delete(ctx, cmd);
case CMD_GET:
return cmd_evaluate_get(ctx, cmd);
diff --git a/src/expression.c b/src/expression.c
index 4c0874fe..cb2573fe 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -8,16 +8,16 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
#include <limits.h>
#include <expression.h>
#include <statement.h>
#include <datatype.h>
+#include <netlink.h>
#include <rule.h>
#include <gmputil.h>
#include <utils.h>
@@ -28,6 +28,7 @@
extern const struct expr_ops ct_expr_ops;
extern const struct expr_ops fib_expr_ops;
extern const struct expr_ops hash_expr_ops;
+extern const struct expr_ops inner_expr_ops;
extern const struct expr_ops meta_expr_ops;
extern const struct expr_ops numgen_expr_ops;
extern const struct expr_ops osf_expr_ops;
@@ -93,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)
@@ -135,12 +136,14 @@ void expr_describe(const struct expr *expr, struct output_ctx *octx)
nft_print(octx, "datatype %s (%s)",
dtype->name, dtype->desc);
len = dtype->size;
- } else if (dtype != &invalid_type) {
+ } else {
nft_print(octx, "%s expression, datatype %s (%s)",
expr_name(expr), dtype->name, dtype->desc);
- } else {
- nft_print(octx, "datatype %s is invalid\n", expr->identifier);
- return;
+
+ if (dtype == &invalid_type) {
+ nft_print(octx, "\n");
+ return;
+ }
}
if (dtype->basetype != NULL) {
@@ -268,6 +271,7 @@ static struct expr *verdict_expr_parse_udata(const struct nftnl_udata *attr)
struct expr *e;
e = symbol_expr_alloc(&internal_location, SYMBOL_VALUE, NULL, "verdict");
+ e->dtype = &verdict_type;
e->len = NFT_REG_SIZE * BITS_PER_BYTE;
return e;
}
@@ -300,8 +304,7 @@ struct expr *verdict_expr_alloc(const struct location *loc,
static void symbol_expr_print(const struct expr *expr, struct output_ctx *octx)
{
- nft_print(octx, "%s%s", expr->scope != NULL ? "$" : "",
- expr->identifier);
+ nft_print(octx, "%s", expr->identifier);
}
static void symbol_expr_clone(struct expr *new, const struct expr *expr)
@@ -313,7 +316,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 = {
@@ -878,17 +881,30 @@ static void concat_expr_print(const struct expr *expr, struct output_ctx *octx)
#define NFTNL_UDATA_SET_KEY_CONCAT_SUB_DATA 1
#define NFTNL_UDATA_SET_KEY_CONCAT_SUB_MAX 2
+static struct expr *expr_build_udata_recurse(struct expr *e)
+{
+ switch (e->etype) {
+ case EXPR_BINOP:
+ return e->left;
+ default:
+ break;
+ }
+
+ return e;
+}
+
static int concat_expr_build_udata(struct nftnl_udata_buf *udbuf,
const struct expr *concat_expr)
{
struct nftnl_udata *nest;
+ struct expr *expr, *tmp;
unsigned int i = 0;
- struct expr *expr;
- list_for_each_entry(expr, &concat_expr->expressions, list) {
+ list_for_each_entry_safe(expr, tmp, &concat_expr->expressions, list) {
struct nftnl_udata *nest_expr;
int err;
+ expr = expr_build_udata_recurse(expr);
if (!expr_ops(expr)->build_udata || i >= NFT_REG32_SIZE)
return -1;
@@ -950,7 +966,7 @@ static struct expr *concat_expr_parse_udata(const struct nftnl_udata *attr)
const struct nftnl_udata *ud[NFTNL_UDATA_SET_KEY_CONCAT_NEST_MAX] = {};
const struct datatype *dtype;
struct expr *concat_expr;
- uint32_t dt = 0;
+ uint32_t dt = 0, len = 0;
unsigned int i;
int err;
@@ -980,7 +996,7 @@ static struct expr *concat_expr_parse_udata(const struct nftnl_udata *attr)
goto err_free;
etype = nftnl_udata_get_u32(nest_ud[NFTNL_UDATA_SET_KEY_CONCAT_SUB_TYPE]);
- ops = expr_ops_by_type(etype);
+ ops = expr_ops_by_type_u32(etype);
if (!ops || !ops->parse_udata)
goto err_free;
@@ -991,14 +1007,15 @@ static struct expr *concat_expr_parse_udata(const struct nftnl_udata *attr)
dt = concat_subtype_add(dt, expr->dtype->type);
compound_expr_add(concat_expr, expr);
+ len += netlink_padded_len(expr->len);
}
dtype = concat_type_alloc(dt);
if (!dtype)
goto err_free;
- concat_expr->dtype = datatype_get(dtype);
- concat_expr->len = dtype->size;
+ __datatype_set(concat_expr, dtype);
+ concat_expr->len = len;
return concat_expr;
@@ -1186,14 +1203,40 @@ struct expr *mapping_expr_alloc(const struct location *loc,
return expr;
}
+static bool __set_expr_is_vmap(const struct expr *mappings)
+{
+ const struct expr *mapping;
+
+ if (list_empty(&mappings->expressions))
+ return false;
+
+ mapping = list_first_entry(&mappings->expressions, struct expr, list);
+ if (mapping->etype == EXPR_MAPPING &&
+ mapping->right->etype == EXPR_VERDICT)
+ return true;
+
+ return false;
+}
+
+static bool set_expr_is_vmap(const struct expr *expr)
+{
+
+ if (expr->mappings->etype == EXPR_SET)
+ return __set_expr_is_vmap(expr->mappings);
+
+ return false;
+}
+
static void map_expr_print(const struct expr *expr, struct output_ctx *octx)
{
expr_print(expr->map, octx);
- if (expr->mappings->etype == EXPR_SET_REF &&
- expr->mappings->set->data->dtype->type == TYPE_VERDICT)
+ if ((expr->mappings->etype == EXPR_SET_REF &&
+ expr->mappings->set->data->dtype->type == TYPE_VERDICT) ||
+ set_expr_is_vmap(expr))
nft_print(octx, " vmap ");
else
nft_print(octx, " map ");
+
expr_print(expr->mappings, octx);
}
@@ -1294,15 +1337,14 @@ 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);
}
-static void set_elem_expr_clone(struct expr *new, const struct expr *expr)
+static void __set_elem_expr_clone(struct expr *new, const struct expr *expr)
{
- new->key = expr_clone(expr->key);
new->expiration = expr->expiration;
new->timeout = expr->timeout;
if (expr->comment)
@@ -1310,6 +1352,12 @@ static void set_elem_expr_clone(struct expr *new, const struct expr *expr)
init_list_head(&new->stmt_list);
}
+static void set_elem_expr_clone(struct expr *new, const struct expr *expr)
+{
+ new->key = expr_clone(expr->key);
+ __set_elem_expr_clone(new, expr);
+}
+
static const struct expr_ops set_elem_expr_ops = {
.type = EXPR_SET_ELEM,
.name = "set element",
@@ -1337,11 +1385,17 @@ static void set_elem_catchall_expr_print(const struct expr *expr,
nft_print(octx, "*");
}
+static void set_elem_catchall_expr_clone(struct expr *new, const struct expr *expr)
+{
+ __set_elem_expr_clone(new, expr);
+}
+
static const struct expr_ops set_elem_catchall_expr_ops = {
.type = EXPR_SET_ELEM_CATCHALL,
.name = "catch-all set element",
.print = set_elem_catchall_expr_print,
.json = set_elem_catchall_expr_json,
+ .clone = set_elem_catchall_expr_clone,
};
struct expr *set_elem_catchall_expr_alloc(const struct location *loc)
@@ -1437,6 +1491,7 @@ void range_expr_value_high(mpz_t rop, const struct expr *expr)
return mpz_set(rop, expr->value);
case EXPR_PREFIX:
range_expr_value_low(rop, expr->prefix);
+ assert(expr->len >= expr->prefix_len);
mpz_init_bitmask(tmp, expr->len - expr->prefix_len);
mpz_add(rop, rop, tmp);
mpz_clear(tmp);
@@ -1455,9 +1510,7 @@ void range_expr_value_high(mpz_t rop, const struct expr *expr)
static const struct expr_ops *__expr_ops_by_type(enum expr_types etype)
{
switch (etype) {
- case EXPR_INVALID:
- BUG("Invalid expression ops requested");
- break;
+ case EXPR_INVALID: break;
case EXPR_VERDICT: return &verdict_expr_ops;
case EXPR_SYMBOL: return &symbol_expr_ops;
case EXPR_VARIABLE: return &variable_expr_ops;
@@ -1489,20 +1542,23 @@ static const struct expr_ops *__expr_ops_by_type(enum expr_types etype)
case EXPR_FLAGCMP: return &flagcmp_expr_ops;
}
- BUG("Unknown expression type %d\n", etype);
+ return NULL;
}
const struct expr_ops *expr_ops(const struct expr *e)
{
- return __expr_ops_by_type(e->etype);
+ const struct expr_ops *ops;
+
+ ops = __expr_ops_by_type(e->etype);
+ if (!ops)
+ BUG("Unknown expression type %d\n", e->etype);
+
+ return ops;
}
-const struct expr_ops *expr_ops_by_type(uint32_t value)
+const struct expr_ops *expr_ops_by_type_u32(uint32_t value)
{
- /* value might come from unreliable source, such as "udata"
- * annotation of set keys. Avoid BUG() assertion.
- */
- if (value == EXPR_INVALID || value > EXPR_MAX)
+ if (value > EXPR_MAX)
return NULL;
return __expr_ops_by_type(value);
diff --git a/src/exthdr.c b/src/exthdr.c
index 22a08b0c..60c7cd1e 100644
--- a/src/exthdr.c
+++ b/src/exthdr.c
@@ -10,11 +10,10 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
@@ -46,6 +45,9 @@ static const struct exthdr_desc *exthdr_find_desc(enum exthdr_desc_id desc_id)
static void exthdr_expr_print(const struct expr *expr, struct output_ctx *octx)
{
+ const char *name = expr->exthdr.desc ?
+ expr->exthdr.desc->name : "unknown-exthdr";
+
if (expr->exthdr.op == NFT_EXTHDR_OP_TCPOPT) {
/* Offset calculation is a bit hacky at this point.
* There might be a tcp option one day with another
@@ -65,14 +67,14 @@ static void exthdr_expr_print(const struct expr *expr, struct output_ctx *octx)
return;
}
- nft_print(octx, "tcp option %s", expr->exthdr.desc->name);
+ nft_print(octx, "tcp option %s", name);
if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
return;
if (offset)
nft_print(octx, "%d", offset);
nft_print(octx, " %s", expr->exthdr.tmpl->token);
} else if (expr->exthdr.op == NFT_EXTHDR_OP_IPV4) {
- nft_print(octx, "ip option %s", expr->exthdr.desc->name);
+ nft_print(octx, "ip option %s", name);
if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
return;
nft_print(octx, " %s", expr->exthdr.tmpl->token);
@@ -81,12 +83,14 @@ static void exthdr_expr_print(const struct expr *expr, struct output_ctx *octx)
if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
return;
nft_print(octx, " %s", expr->exthdr.tmpl->token);
+ } else if (expr->exthdr.op == NFT_EXTHDR_OP_DCCP) {
+ nft_print(octx, "dccp option %d", expr->exthdr.raw_type);
+ return;
} else {
if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
- nft_print(octx, "exthdr %s", expr->exthdr.desc->name);
+ nft_print(octx, "exthdr %s", name);
else {
- nft_print(octx, "%s %s",
- expr->exthdr.desc ? expr->exthdr.desc->name : "unknown-exthdr",
+ nft_print(octx, "%s %s", name,
expr->exthdr.tmpl->token);
}
}
@@ -113,7 +117,8 @@ static void exthdr_expr_clone(struct expr *new, const struct expr *expr)
#define NFTNL_UDATA_EXTHDR_DESC 0
#define NFTNL_UDATA_EXTHDR_TYPE 1
-#define NFTNL_UDATA_EXTHDR_MAX 2
+#define NFTNL_UDATA_EXTHDR_OP 2
+#define NFTNL_UDATA_EXTHDR_MAX 3
static int exthdr_parse_udata(const struct nftnl_udata *attr, void *data)
{
@@ -124,6 +129,7 @@ static int exthdr_parse_udata(const struct nftnl_udata *attr, void *data)
switch (type) {
case NFTNL_UDATA_EXTHDR_DESC:
case NFTNL_UDATA_EXTHDR_TYPE:
+ case NFTNL_UDATA_EXTHDR_OP:
if (len != sizeof(uint32_t))
return -1;
break;
@@ -138,6 +144,7 @@ static int exthdr_parse_udata(const struct nftnl_udata *attr, void *data)
static struct expr *exthdr_expr_parse_udata(const struct nftnl_udata *attr)
{
const struct nftnl_udata *ud[NFTNL_UDATA_EXTHDR_MAX + 1] = {};
+ enum nft_exthdr_op op = NFT_EXTHDR_OP_IPV6;
const struct exthdr_desc *desc;
unsigned int type;
uint32_t desc_id;
@@ -152,22 +159,39 @@ static struct expr *exthdr_expr_parse_udata(const struct nftnl_udata *attr)
!ud[NFTNL_UDATA_EXTHDR_TYPE])
return NULL;
- desc_id = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_DESC]);
- desc = exthdr_find_desc(desc_id);
- if (!desc)
- return NULL;
+ if (ud[NFTNL_UDATA_EXTHDR_OP])
+ op = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_OP]);
+ desc_id = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_DESC]);
type = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_TYPE]);
- return exthdr_expr_alloc(&internal_location, desc, type);
+ switch (op) {
+ case NFT_EXTHDR_OP_IPV6:
+ desc = exthdr_find_desc(desc_id);
+
+ return exthdr_expr_alloc(&internal_location, desc, type);
+ case NFT_EXTHDR_OP_TCPOPT:
+ return tcpopt_expr_alloc(&internal_location,
+ desc_id, type);
+ case NFT_EXTHDR_OP_IPV4:
+ return ipopt_expr_alloc(&internal_location,
+ desc_id, type);
+ case NFT_EXTHDR_OP_SCTP:
+ return sctp_chunk_expr_alloc(&internal_location,
+ desc_id, type);
+ case NFT_EXTHDR_OP_DCCP:
+ return dccpopt_expr_alloc(&internal_location, type);
+ case __NFT_EXTHDR_OP_MAX:
+ return NULL;
+ }
+
+ return NULL;
}
static unsigned int expr_exthdr_type(const struct exthdr_desc *desc,
const struct proto_hdr_template *tmpl)
{
- unsigned int offset = (unsigned int)(tmpl - &desc->templates[0]);
-
- return offset / sizeof(*tmpl);
+ return (unsigned int)(tmpl - &desc->templates[0]);
}
static int exthdr_expr_build_udata(struct nftnl_udata_buf *udbuf,
@@ -176,9 +200,23 @@ static int exthdr_expr_build_udata(struct nftnl_udata_buf *udbuf,
const struct proto_hdr_template *tmpl = expr->exthdr.tmpl;
const struct exthdr_desc *desc = expr->exthdr.desc;
unsigned int type = expr_exthdr_type(desc, tmpl);
+ enum nft_exthdr_op op = expr->exthdr.op;
- nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_DESC, desc->id);
nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_TYPE, type);
+ switch (op) {
+ case NFT_EXTHDR_OP_IPV6:
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_DESC, desc->id);
+ break;
+ case NFT_EXTHDR_OP_TCPOPT:
+ case NFT_EXTHDR_OP_IPV4:
+ case NFT_EXTHDR_OP_SCTP:
+ case NFT_EXTHDR_OP_DCCP:
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_OP, op);
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_DESC, expr->exthdr.raw_type);
+ break;
+ default:
+ return -1;
+ }
return 0;
}
@@ -250,7 +288,7 @@ struct stmt *exthdr_stmt_alloc(const struct location *loc,
return stmt;
}
-static const struct exthdr_desc *exthdr_protocols[IPPROTO_MAX] = {
+static const struct exthdr_desc *exthdr_protocols[UINT8_MAX + 1] = {
[IPPROTO_HOPOPTS] = &exthdr_hbh,
[IPPROTO_ROUTING] = &exthdr_rt,
[IPPROTO_FRAGMENT] = &exthdr_frag,
@@ -299,14 +337,15 @@ void exthdr_init_raw(struct expr *expr, uint8_t type,
return ipopt_init_raw(expr, type, offset, len, flags, true);
if (op == NFT_EXTHDR_OP_SCTP)
return sctp_chunk_init_raw(expr, type, offset, len, flags);
+ if (op == NFT_EXTHDR_OP_DCCP)
+ return dccpopt_init_raw(expr, type, offset, len);
expr->len = len;
expr->exthdr.flags = flags;
expr->exthdr.offset = offset;
expr->exthdr.desc = NULL;
- if (type < array_size(exthdr_protocols))
- expr->exthdr.desc = exthdr_protocols[type];
+ expr->exthdr.desc = exthdr_protocols[type];
if (expr->exthdr.desc == NULL)
goto out;
@@ -348,16 +387,7 @@ static unsigned int mask_length(const struct expr *mask)
bool exthdr_find_template(struct expr *expr, const struct expr *mask, unsigned int *shift)
{
unsigned int off, mask_offset, mask_len;
-
- if (expr->exthdr.op != NFT_EXTHDR_OP_IPV4 &&
- expr->exthdr.tmpl != &exthdr_unknown_template)
- return false;
-
- /* In case we are handling tcp options instead of the default ipv6
- * extension headers.
- */
- if (expr->exthdr.op == NFT_EXTHDR_OP_TCPOPT)
- return tcpopt_find_template(expr, mask, shift);
+ bool found;
mask_offset = mpz_scan1(mask->value, 0);
mask_len = mask_length(mask);
@@ -366,24 +396,31 @@ bool exthdr_find_template(struct expr *expr, const struct expr *mask, unsigned i
off += round_up(mask->len, BITS_PER_BYTE) - mask_len;
/* Handle ip options after the offset and mask have been calculated. */
- if (expr->exthdr.op == NFT_EXTHDR_OP_IPV4) {
- if (ipopt_find_template(expr, off, mask_len - mask_offset)) {
- *shift = mask_offset;
- return true;
- } else {
+ switch (expr->exthdr.op) {
+ case NFT_EXTHDR_OP_IPV4:
+ found = ipopt_find_template(expr, off, mask_len - mask_offset);
+ break;
+ case NFT_EXTHDR_OP_TCPOPT:
+ found = tcpopt_find_template(expr, off, mask_len - mask_offset);
+ break;
+ case NFT_EXTHDR_OP_IPV6:
+ exthdr_init_raw(expr, expr->exthdr.raw_type,
+ off, mask_len - mask_offset, expr->exthdr.op, 0);
+
+ /* still failed to find a template... Bug. */
+ if (expr->exthdr.tmpl == &exthdr_unknown_template)
return false;
- }
+ found = true;
+ break;
+ default:
+ found = false;
+ break;
}
- exthdr_init_raw(expr, expr->exthdr.desc->type,
- off, mask_len - mask_offset, expr->exthdr.op, 0);
-
- /* still failed to find a template... Bug. */
- if (expr->exthdr.tmpl == &exthdr_unknown_template)
- return false;
+ if (found)
+ *shift = mask_offset;
- *shift = mask_offset;
- return true;
+ return found;
}
#define HDR_TEMPLATE(__name, __dtype, __type, __member) \
diff --git a/src/fib.c b/src/fib.c
index c6ad0f9c..e95271c9 100644
--- a/src/fib.c
+++ b/src/fib.c
@@ -4,17 +4,18 @@
* Copyright (c) Red Hat GmbH. Author: Florian Westphal <fw@strlen.de>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <nftables.h>
#include <erec.h>
#include <expression.h>
#include <datatype.h>
#include <gmputil.h>
#include <utils.h>
-#include <string.h>
#include <fib.h>
#include <linux/rtnetlink.h>
diff --git a/src/gmputil.c b/src/gmputil.c
index b356460f..b4529259 100644
--- a/src/gmputil.c
+++ b/src/gmputil.c
@@ -8,12 +8,12 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
-#include <string.h>
#include <nftables.h>
#include <datatype.h>
@@ -184,7 +184,7 @@ int mpz_vfprintf(FILE *fp, const char *f, va_list args)
str = mpz_get_str(NULL, base, *value);
ok = str && fwrite(str, 1, len, fp) == len;
- free(str);
+ nft_gmp_free(str);
if (!ok)
return -1;
@@ -197,12 +197,21 @@ int mpz_vfprintf(FILE *fp, const char *f, va_list args)
}
#endif
-static void *gmp_xrealloc(void *ptr, size_t old_size, size_t new_size)
+void nft_gmp_free(void *ptr)
{
- return xrealloc(ptr, new_size);
-}
+ void (*free_fcn)(void *, size_t);
-void gmp_init(void)
-{
- mp_set_memory_functions(xmalloc, gmp_xrealloc, NULL);
+ /* When we get allocated memory from gmp, it was allocated via the
+ * allocator() from mp_set_memory_functions(). We should pair the free
+ * with the corresponding free function, which we get via
+ * mp_get_memory_functions().
+ *
+ * It's not clear what the correct blk_size is. The default allocator
+ * function of gmp just wraps free() and ignores the extra argument.
+ * Assume 0 is fine.
+ */
+
+ mp_get_memory_functions(NULL, NULL, &free_fcn);
+
+ (*free_fcn)(ptr, 0);
}
diff --git a/src/hash.c b/src/hash.c
index 42c50407..1c8c00aa 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -4,10 +4,12 @@
* Copyright (c) 2016 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 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <nftables.h>
#include <expression.h>
#include <datatype.h>
diff --git a/src/iface.c b/src/iface.c
index d0e1834c..428acaae 100644
--- a/src/iface.c
+++ b/src/iface.c
@@ -2,15 +2,15 @@
* Copyright (c) 2015 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 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <stdio.h>
-#include <stdlib.h>
#include <net/if.h>
#include <time.h>
-#include <string.h>
#include <errno.h>
#include <libmnl/libmnl.h>
@@ -59,13 +59,13 @@ static int data_cb(const struct nlmsghdr *nlh, void *data)
return MNL_CB_OK;
}
-void iface_cache_update(void)
+static int iface_mnl_talk(struct mnl_socket *nl, uint32_t portid)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
- struct mnl_socket *nl;
struct nlmsghdr *nlh;
struct rtgenmsg *rt;
- uint32_t seq, portid;
+ bool eintr = false;
+ uint32_t seq;
int ret;
nlh = mnl_nlmsg_put_header(buf);
@@ -75,6 +75,38 @@ void iface_cache_update(void)
rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
rt->rtgen_family = AF_PACKET;
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
+ return -1;
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL);
+ if (ret == 0)
+ break;
+ if (ret < 0) {
+ if (errno != EINTR)
+ return ret;
+
+ /* process all pending messages before reporting EINTR */
+ eintr = true;
+ }
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+
+ if (eintr) {
+ ret = -1;
+ errno = EINTR;
+ }
+
+ return ret;
+}
+
+void iface_cache_update(void)
+{
+ struct mnl_socket *nl;
+ uint32_t portid;
+ int ret;
+
nl = mnl_socket_open(NETLINK_ROUTE);
if (nl == NULL)
netlink_init_error();
@@ -84,16 +116,10 @@ void iface_cache_update(void)
portid = mnl_socket_get_portid(nl);
- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
- netlink_init_error();
+ do {
+ ret = iface_mnl_talk(nl, portid);
+ } while (ret < 0 && errno == EINTR);
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- while (ret > 0) {
- ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL);
- if (ret <= MNL_CB_STOP)
- break;
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- }
if (ret == -1)
netlink_init_error();
diff --git a/src/intervals.c b/src/intervals.c
new file mode 100644
index 00000000..6c3f36fe
--- /dev/null
+++ b/src/intervals.c
@@ -0,0 +1,755 @@
+/*
+ * Copyright (c) 2022 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 <nft.h>
+
+#include <nftables.h>
+#include <expression.h>
+#include <intervals.h>
+#include <rule.h>
+
+static void set_to_range(struct expr *init);
+
+static void setelem_expr_to_range(struct expr *expr)
+{
+ unsigned char data[sizeof(struct in6_addr) * BITS_PER_BYTE];
+ struct expr *key, *value;
+ mpz_t rop;
+
+ assert(expr->etype == EXPR_SET_ELEM);
+
+ switch (expr->key->etype) {
+ case EXPR_SET_ELEM_CATCHALL:
+ case EXPR_RANGE:
+ break;
+ case EXPR_PREFIX:
+ if (expr->key->prefix->etype != EXPR_VALUE)
+ BUG("Prefix for unexpected type %d", expr->key->prefix->etype);
+
+ mpz_init(rop);
+ mpz_bitmask(rop, expr->key->len - expr->key->prefix_len);
+ if (expr_basetype(expr)->type == TYPE_STRING)
+ mpz_switch_byteorder(expr->key->prefix->value, expr->len / BITS_PER_BYTE);
+
+ mpz_ior(rop, rop, expr->key->prefix->value);
+ mpz_export_data(data, rop, expr->key->prefix->byteorder,
+ expr->key->prefix->len / BITS_PER_BYTE);
+ mpz_clear(rop);
+ value = constant_expr_alloc(&expr->location,
+ expr->key->prefix->dtype,
+ expr->key->prefix->byteorder,
+ expr->key->prefix->len, data);
+ key = range_expr_alloc(&expr->location,
+ expr_get(expr->key->prefix),
+ value);
+ expr_free(expr->key);
+ expr->key = key;
+ break;
+ case EXPR_VALUE:
+ if (expr_basetype(expr)->type == TYPE_STRING)
+ mpz_switch_byteorder(expr->key->value, expr->len / BITS_PER_BYTE);
+
+ key = range_expr_alloc(&expr->location,
+ expr_clone(expr->key),
+ expr_get(expr->key));
+ expr_free(expr->key);
+ expr->key = key;
+ break;
+ default:
+ BUG("unhandled key type %d\n", expr->key->etype);
+ }
+}
+
+struct set_automerge_ctx {
+ struct set *set;
+ struct expr *init;
+ struct expr *purge;
+ unsigned int debug_mask;
+};
+
+static void purge_elem(struct set_automerge_ctx *ctx, struct expr *i)
+{
+ if (ctx->debug_mask & NFT_DEBUG_SEGTREE) {
+ pr_gmp_debug("remove: [%Zx-%Zx]\n",
+ i->key->left->value,
+ i->key->right->value);
+ }
+ list_move_tail(&i->list, &ctx->purge->expressions);
+}
+
+static void remove_overlapping_range(struct set_automerge_ctx *ctx,
+ struct expr *prev, struct expr *i)
+{
+ if (i->flags & EXPR_F_KERNEL) {
+ purge_elem(ctx, i);
+ return;
+ }
+ list_del(&i->list);
+ expr_free(i);
+ ctx->init->size--;
+}
+
+struct range {
+ mpz_t low;
+ mpz_t high;
+};
+
+static bool merge_ranges(struct set_automerge_ctx *ctx,
+ struct expr *prev, struct expr *i,
+ struct range *prev_range, struct range *range)
+{
+ if (prev->flags & EXPR_F_KERNEL) {
+ purge_elem(ctx, prev);
+ expr_free(i->key->left);
+ i->key->left = expr_get(prev->key->left);
+ mpz_set(prev_range->high, range->high);
+ return true;
+ } else if (i->flags & EXPR_F_KERNEL) {
+ purge_elem(ctx, i);
+ expr_free(prev->key->right);
+ prev->key->right = expr_get(i->key->right);
+ mpz_set(prev_range->high, range->high);
+ } else {
+ expr_free(prev->key->right);
+ prev->key->right = expr_get(i->key->right);
+ mpz_set(prev_range->high, range->high);
+ list_del(&i->list);
+ expr_free(i);
+ ctx->init->size--;
+ }
+ return false;
+}
+
+static void set_sort_splice(struct expr *init, struct set *set)
+{
+ struct set *existing_set = set->existing_set;
+
+ set_to_range(init);
+ list_expr_sort(&init->expressions);
+
+ if (!existing_set || existing_set->errors)
+ return;
+
+ if (existing_set->init) {
+ set_to_range(existing_set->init);
+ list_splice_sorted(&existing_set->init->expressions,
+ &init->expressions);
+ init_list_head(&existing_set->init->expressions);
+ } else {
+ existing_set->init = set_expr_alloc(&internal_location, set);
+ }
+}
+
+static void setelem_automerge(struct set_automerge_ctx *ctx)
+{
+ struct expr *i, *next, *prev = NULL;
+ struct range range, prev_range;
+ mpz_t rop;
+
+ mpz_init(prev_range.low);
+ mpz_init(prev_range.high);
+ mpz_init(range.low);
+ mpz_init(range.high);
+ mpz_init(rop);
+
+ list_for_each_entry_safe(i, next, &ctx->init->expressions, list) {
+ if (i->key->etype == EXPR_SET_ELEM_CATCHALL)
+ continue;
+
+ range_expr_value_low(range.low, i);
+ range_expr_value_high(range.high, i);
+
+ if (!prev) {
+ prev = i;
+ mpz_set(prev_range.low, range.low);
+ mpz_set(prev_range.high, range.high);
+ continue;
+ }
+
+ if (mpz_cmp(prev_range.low, range.low) <= 0 &&
+ mpz_cmp(prev_range.high, range.high) >= 0) {
+ remove_overlapping_range(ctx, prev, i);
+ continue;
+ } else if (mpz_cmp(range.low, prev_range.high) <= 0) {
+ if (merge_ranges(ctx, prev, i, &prev_range, &range))
+ prev = i;
+ continue;
+ } else if (ctx->set->automerge) {
+ mpz_sub(rop, range.low, prev_range.high);
+ /* two contiguous ranges */
+ if (mpz_cmp_ui(rop, 1) == 0) {
+ if (merge_ranges(ctx, prev, i, &prev_range, &range))
+ prev = i;
+ continue;
+ }
+ }
+
+ prev = i;
+ mpz_set(prev_range.low, range.low);
+ mpz_set(prev_range.high, range.high);
+ }
+
+ mpz_clear(prev_range.low);
+ mpz_clear(prev_range.high);
+ mpz_clear(range.low);
+ mpz_clear(range.high);
+ mpz_clear(rop);
+}
+
+static struct expr *interval_expr_key(struct expr *i)
+{
+ struct expr *elem;
+
+ switch (i->etype) {
+ case EXPR_MAPPING:
+ elem = i->left;
+ break;
+ case EXPR_SET_ELEM:
+ elem = i;
+ break;
+ default:
+ BUG("unhandled expression type %d\n", i->etype);
+ return NULL;
+ }
+
+ return elem;
+}
+
+static void set_to_range(struct expr *init)
+{
+ struct expr *i, *elem;
+
+ list_for_each_entry(i, &init->expressions, list) {
+ elem = interval_expr_key(i);
+ setelem_expr_to_range(elem);
+ }
+}
+
+int set_automerge(struct list_head *msgs, struct cmd *cmd, struct set *set,
+ struct expr *init, unsigned int debug_mask)
+{
+ struct set *existing_set = set->existing_set;
+ struct set_automerge_ctx ctx = {
+ .set = set,
+ .init = init,
+ .debug_mask = debug_mask,
+ };
+ struct expr *i, *next, *clone;
+ struct cmd *purge_cmd;
+ struct handle h = {};
+
+ if (set->flags & NFT_SET_MAP) {
+ set_to_range(init);
+ list_expr_sort(&init->expressions);
+ return 0;
+ }
+
+ set_sort_splice(init, set);
+
+ ctx.purge = set_expr_alloc(&internal_location, set);
+
+ setelem_automerge(&ctx);
+
+ list_for_each_entry_safe(i, next, &init->expressions, list) {
+ if (i->flags & EXPR_F_KERNEL) {
+ list_move_tail(&i->list, &existing_set->init->expressions);
+ } else if (existing_set) {
+ if (debug_mask & NFT_DEBUG_SEGTREE) {
+ pr_gmp_debug("add: [%Zx-%Zx]\n",
+ i->key->left->value, i->key->right->value);
+ }
+ clone = expr_clone(i);
+ clone->flags |= EXPR_F_KERNEL;
+ list_add_tail(&clone->list, &existing_set->init->expressions);
+ }
+ }
+
+ if (list_empty(&ctx.purge->expressions)) {
+ expr_free(ctx.purge);
+ return 0;
+ }
+
+ handle_merge(&h, &set->handle);
+ purge_cmd = cmd_alloc(CMD_DELETE, CMD_OBJ_ELEMENTS, &h, &init->location, ctx.purge);
+ purge_cmd->elem.set = set_get(set);
+ list_add_tail(&purge_cmd->list, &cmd->list);
+
+ return 0;
+}
+
+static void remove_elem(struct expr *prev, struct set *set, struct expr *purge)
+{
+ struct expr *clone;
+
+ if (prev->flags & EXPR_F_KERNEL) {
+ clone = expr_clone(prev);
+ list_move_tail(&clone->list, &purge->expressions);
+ }
+}
+
+static void __adjust_elem_left(struct set *set, struct expr *prev, struct expr *i)
+{
+ prev->flags &= ~EXPR_F_KERNEL;
+ expr_free(prev->key->left);
+ prev->key->left = expr_get(i->key->right);
+ mpz_add_ui(prev->key->left->value, prev->key->left->value, 1);
+ list_move(&prev->list, &set->existing_set->init->expressions);
+}
+
+static void adjust_elem_left(struct set *set, struct expr *prev, struct expr *i,
+ struct expr *purge)
+{
+ remove_elem(prev, set, purge);
+ __adjust_elem_left(set, prev, i);
+
+ list_del(&i->list);
+ expr_free(i);
+}
+
+static void __adjust_elem_right(struct set *set, struct expr *prev, struct expr *i)
+{
+ prev->flags &= ~EXPR_F_KERNEL;
+ expr_free(prev->key->right);
+ prev->key->right = expr_get(i->key->left);
+ mpz_sub_ui(prev->key->right->value, prev->key->right->value, 1);
+ list_move(&prev->list, &set->existing_set->init->expressions);
+}
+
+static void adjust_elem_right(struct set *set, struct expr *prev, struct expr *i,
+ struct expr *purge)
+{
+ remove_elem(prev, set, purge);
+ __adjust_elem_right(set, prev, i);
+
+ list_del(&i->list);
+ expr_free(i);
+}
+
+static void split_range(struct set *set, struct expr *prev, struct expr *i,
+ struct expr *purge)
+{
+ struct expr *clone;
+
+ if (prev->flags & EXPR_F_KERNEL) {
+ clone = expr_clone(prev);
+ list_move_tail(&clone->list, &purge->expressions);
+ }
+
+ prev->flags &= ~EXPR_F_KERNEL;
+ clone = expr_clone(prev);
+ expr_free(clone->key->left);
+ clone->key->left = expr_get(i->key->right);
+ mpz_add_ui(clone->key->left->value, i->key->right->value, 1);
+ list_add_tail(&clone->list, &set->existing_set->init->expressions);
+
+ expr_free(prev->key->right);
+ prev->key->right = expr_get(i->key->left);
+ mpz_sub_ui(prev->key->right->value, i->key->left->value, 1);
+ list_move(&prev->list, &set->existing_set->init->expressions);
+
+ list_del(&i->list);
+ expr_free(i);
+}
+
+static int setelem_adjust(struct set *set, struct expr *purge,
+ struct range *prev_range, struct range *range,
+ struct expr *prev, struct expr *i)
+{
+ if (mpz_cmp(prev_range->low, range->low) == 0 &&
+ mpz_cmp(prev_range->high, range->high) > 0) {
+ if (i->flags & EXPR_F_REMOVE)
+ adjust_elem_left(set, prev, i, purge);
+ } else if (mpz_cmp(prev_range->low, range->low) < 0 &&
+ mpz_cmp(prev_range->high, range->high) == 0) {
+ if (i->flags & EXPR_F_REMOVE)
+ adjust_elem_right(set, prev, i, purge);
+ } else if (mpz_cmp(prev_range->low, range->low) < 0 &&
+ mpz_cmp(prev_range->high, range->high) > 0) {
+ if (i->flags & EXPR_F_REMOVE)
+ split_range(set, prev, i, purge);
+ } else {
+ return -1;
+ }
+
+ return 0;
+}
+
+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 range range, prev_range;
+ int err = 0;
+ mpz_t rop;
+
+ mpz_init(prev_range.low);
+ mpz_init(prev_range.high);
+ mpz_init(range.low);
+ 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;
+
+ range_expr_value_low(range.low, i);
+ range_expr_value_high(range.high, i);
+
+ if (!prev && i->flags & EXPR_F_REMOVE) {
+ expr_error(msgs, i, "element does not exist");
+ err = -1;
+ goto err;
+ }
+
+ if (!(i->flags & EXPR_F_REMOVE)) {
+ prev = i;
+ mpz_set(prev_range.low, range.low);
+ mpz_set(prev_range.high, range.high);
+ continue;
+ }
+
+ if (mpz_cmp(prev_range.low, range.low) == 0 &&
+ mpz_cmp(prev_range.high, range.high) == 0) {
+ if (i->flags & EXPR_F_REMOVE) {
+ if (prev->flags & EXPR_F_KERNEL)
+ list_move_tail(&prev->list, &purge->expressions);
+
+ list_del(&i->list);
+ expr_free(i);
+ }
+ } else if (set->automerge) {
+ if (setelem_adjust(set, purge, &prev_range, &range, prev, i) < 0) {
+ expr_error(msgs, i, "element does not exist");
+ err = -1;
+ goto err;
+ }
+ } else if (i->flags & EXPR_F_REMOVE) {
+ expr_error(msgs, i, "element does not exist");
+ err = -1;
+ goto err;
+ }
+ prev = NULL;
+ }
+err:
+ mpz_clear(prev_range.low);
+ mpz_clear(prev_range.high);
+ mpz_clear(range.low);
+ mpz_clear(range.high);
+ mpz_clear(rop);
+
+ return err;
+}
+
+static void automerge_delete(struct list_head *msgs, struct set *set,
+ struct expr *init, unsigned int debug_mask)
+{
+ struct set_automerge_ctx ctx = {
+ .set = set,
+ .init = init,
+ .debug_mask = debug_mask,
+ };
+
+ ctx.purge = set_expr_alloc(&internal_location, set);
+ list_expr_sort(&init->expressions);
+ setelem_automerge(&ctx);
+ expr_free(ctx.purge);
+}
+
+static int __set_delete(struct list_head *msgs, struct expr *i, struct set *set,
+ struct expr *init, struct set *existing_set,
+ unsigned int debug_mask)
+{
+ i->flags |= EXPR_F_REMOVE;
+ 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);
+}
+
+/* detection for unexisting intervals already exists in Linux kernels >= 5.7. */
+int set_delete(struct list_head *msgs, struct cmd *cmd, struct set *set,
+ struct expr *init, unsigned int debug_mask)
+{
+ struct set *existing_set = set->existing_set;
+ struct expr *i, *next, *add, *clone;
+ struct handle h = {};
+ struct cmd *add_cmd;
+ LIST_HEAD(del_list);
+ int err;
+
+ set_to_range(init);
+ if (set->automerge)
+ automerge_delete(msgs, set, init, debug_mask);
+
+ if (existing_set->init) {
+ set_to_range(existing_set->init);
+ } else {
+ existing_set->init = set_expr_alloc(&internal_location, set);
+ }
+
+ list_splice_init(&init->expressions, &del_list);
+
+ list_for_each_entry_safe(i, next, &del_list, list) {
+ err = __set_delete(msgs, i, set, init, existing_set, debug_mask);
+ if (err < 0) {
+ list_splice(&del_list, &init->expressions);
+ return err;
+ }
+ }
+
+ add = set_expr_alloc(&internal_location, set);
+ list_for_each_entry(i, &existing_set->init->expressions, list) {
+ if (!(i->flags & EXPR_F_KERNEL)) {
+ clone = expr_clone(i);
+ list_add_tail(&clone->list, &add->expressions);
+ i->flags |= EXPR_F_KERNEL;
+ }
+ }
+
+ if (debug_mask & NFT_DEBUG_SEGTREE) {
+ list_for_each_entry(i, &init->expressions, list)
+ pr_gmp_debug("remove: [%Zx-%Zx]\n",
+ i->key->left->value, i->key->right->value);
+ list_for_each_entry(i, &add->expressions, list)
+ pr_gmp_debug("add: [%Zx-%Zx]\n",
+ i->key->left->value, i->key->right->value);
+ list_for_each_entry(i, &existing_set->init->expressions, list)
+ pr_gmp_debug("existing: [%Zx-%Zx]\n",
+ i->key->left->value, i->key->right->value);
+ }
+
+ if (list_empty(&add->expressions)) {
+ expr_free(add);
+ return 0;
+ }
+
+ handle_merge(&h, &cmd->handle);
+ add_cmd = cmd_alloc(CMD_ADD, CMD_OBJ_ELEMENTS, &h, &cmd->location, add);
+ add_cmd->elem.set = set_get(set);
+ list_add(&add_cmd->list, &cmd->list);
+
+ return 0;
+}
+
+static int setelem_overlap(struct list_head *msgs, struct set *set,
+ struct expr *init)
+{
+ struct expr *i, *next, *elem, *prev = NULL;
+ struct range range, prev_range;
+ int err = 0;
+ mpz_t rop;
+
+ mpz_init(prev_range.low);
+ mpz_init(prev_range.high);
+ mpz_init(range.low);
+ mpz_init(range.high);
+ mpz_init(rop);
+
+ list_for_each_entry_safe(elem, next, &init->expressions, list) {
+ i = interval_expr_key(elem);
+
+ if (i->key->etype == EXPR_SET_ELEM_CATCHALL)
+ continue;
+
+ range_expr_value_low(range.low, i);
+ range_expr_value_high(range.high, i);
+
+ if (!prev) {
+ prev = elem;
+ mpz_set(prev_range.low, range.low);
+ mpz_set(prev_range.high, range.high);
+ continue;
+ }
+
+ if (mpz_cmp(prev_range.low, range.low) == 0 &&
+ mpz_cmp(prev_range.high, range.high) == 0)
+ goto next;
+
+ if (mpz_cmp(prev_range.low, range.low) <= 0 &&
+ mpz_cmp(prev_range.high, range.high) >= 0) {
+ if (prev->flags & EXPR_F_KERNEL)
+ expr_error(msgs, i, "interval overlaps with an existing one");
+ else if (elem->flags & EXPR_F_KERNEL)
+ expr_error(msgs, prev, "interval overlaps with an existing one");
+ else
+ expr_binary_error(msgs, i, prev,
+ "conflicting intervals specified");
+ err = -1;
+ goto err_out;
+ } else if (mpz_cmp(range.low, prev_range.high) <= 0) {
+ if (prev->flags & EXPR_F_KERNEL)
+ expr_error(msgs, i, "interval overlaps with an existing one");
+ else if (elem->flags & EXPR_F_KERNEL)
+ expr_error(msgs, prev, "interval overlaps with an existing one");
+ else
+ expr_binary_error(msgs, i, prev,
+ "conflicting intervals specified");
+ err = -1;
+ goto err_out;
+ }
+next:
+ prev = elem;
+ mpz_set(prev_range.low, range.low);
+ mpz_set(prev_range.high, range.high);
+ }
+
+err_out:
+ mpz_clear(prev_range.low);
+ mpz_clear(prev_range.high);
+ mpz_clear(range.low);
+ mpz_clear(range.high);
+ mpz_clear(rop);
+
+ return err;
+}
+
+/* overlap detection for intervals already exists in Linux kernels >= 5.7. */
+int set_overlap(struct list_head *msgs, struct set *set, struct expr *init)
+{
+ struct set *existing_set = set->existing_set;
+ struct expr *i, *n, *clone;
+ int err;
+
+ set_sort_splice(init, set);
+
+ err = setelem_overlap(msgs, set, init);
+
+ list_for_each_entry_safe(i, n, &init->expressions, list) {
+ if (i->flags & EXPR_F_KERNEL)
+ list_move_tail(&i->list, &existing_set->init->expressions);
+ else if (existing_set) {
+ clone = expr_clone(i);
+ clone->flags |= EXPR_F_KERNEL;
+ list_add_tail(&clone->list, &existing_set->init->expressions);
+ }
+ }
+
+ return err;
+}
+
+static bool segtree_needs_first_segment(const struct set *set,
+ const struct expr *init, bool add)
+{
+ if (add && !set->root) {
+ /* Add the first segment in four situations:
+ *
+ * 1) This is an anonymous set.
+ * 2) This set exists and it is empty.
+ * 3) New empty set and, separately, new elements are added.
+ * 4) This set is created with a number of initial elements.
+ */
+ if ((set_is_anonymous(set->flags)) ||
+ (set->init && set->init->size == 0) ||
+ (set->init == NULL && init) ||
+ (set->init == init)) {
+ return true;
+ }
+ }
+ /* This is an update for a set that already contains elements, so don't
+ * add the first non-matching elements otherwise we hit EEXIST.
+ */
+ return false;
+}
+
+int set_to_intervals(const struct set *set, struct expr *init, bool add)
+{
+ struct expr *i, *n, *prev = NULL, *elem, *newelem = NULL, *root, *expr;
+ LIST_HEAD(intervals);
+ uint32_t flags;
+ mpz_t p, q;
+
+ mpz_init2(p, set->key->len);
+ mpz_init2(q, set->key->len);
+
+ list_for_each_entry_safe(i, n, &init->expressions, list) {
+ flags = 0;
+
+ elem = interval_expr_key(i);
+
+ if (elem->key->etype == EXPR_SET_ELEM_CATCHALL)
+ continue;
+
+ if (!prev && segtree_needs_first_segment(set, init, add) &&
+ mpz_cmp_ui(elem->key->left->value, 0)) {
+ mpz_set_ui(p, 0);
+ expr = constant_expr_alloc(&internal_location,
+ set->key->dtype,
+ set->key->byteorder,
+ set->key->len, NULL);
+ mpz_set(expr->value, p);
+ root = set_elem_expr_alloc(&internal_location, expr);
+ if (i->etype == EXPR_MAPPING) {
+ root = mapping_expr_alloc(&internal_location,
+ root,
+ expr_get(i->right));
+ }
+ root->flags |= EXPR_F_INTERVAL_END;
+ list_add(&root->list, &intervals);
+ init->size++;
+ }
+
+ if (newelem) {
+ mpz_set(p, interval_expr_key(newelem)->key->value);
+ if (set->key->byteorder == BYTEORDER_HOST_ENDIAN)
+ mpz_switch_byteorder(p, set->key->len / BITS_PER_BYTE);
+
+ if (!(set->flags & NFT_SET_ANONYMOUS) ||
+ mpz_cmp(p, elem->key->left->value) != 0)
+ list_add_tail(&newelem->list, &intervals);
+ else
+ expr_free(newelem);
+ }
+ newelem = NULL;
+
+ if (mpz_scan0(elem->key->right->value, 0) != set->key->len) {
+ mpz_add_ui(p, elem->key->right->value, 1);
+ expr = constant_expr_alloc(&elem->key->location, set->key->dtype,
+ set->key->byteorder, set->key->len,
+ NULL);
+ mpz_set(expr->value, p);
+ if (set->key->byteorder == BYTEORDER_HOST_ENDIAN)
+ mpz_switch_byteorder(expr->value, set->key->len / BITS_PER_BYTE);
+
+ newelem = set_elem_expr_alloc(&expr->location, expr);
+ if (i->etype == EXPR_MAPPING) {
+ newelem = mapping_expr_alloc(&expr->location,
+ newelem,
+ expr_get(i->right));
+ }
+ newelem->flags |= EXPR_F_INTERVAL_END;
+ } else {
+ flags = NFTNL_SET_ELEM_F_INTERVAL_OPEN;
+ }
+
+ expr = constant_expr_alloc(&elem->key->location, set->key->dtype,
+ set->key->byteorder, set->key->len, NULL);
+
+ mpz_set(expr->value, elem->key->left->value);
+ if (set->key->byteorder == BYTEORDER_HOST_ENDIAN)
+ mpz_switch_byteorder(expr->value, set->key->len / BITS_PER_BYTE);
+
+ expr_free(elem->key);
+ elem->key = expr;
+ i->elem_flags |= flags;
+ init->size++;
+ list_move_tail(&i->list, &intervals);
+
+ prev = i;
+ }
+
+ if (newelem)
+ list_add_tail(&newelem->list, &intervals);
+
+ list_splice_init(&intervals, &init->expressions);
+
+ mpz_clear(p);
+ mpz_clear(q);
+
+ return 0;
+}
diff --git a/src/ipopt.c b/src/ipopt.c
index 5f9f908c..37f779d4 100644
--- a/src/ipopt.c
+++ b/src/ipopt.c
@@ -1,4 +1,5 @@
-#include <stdint.h>
+#include <nft.h>
+
#include <netinet/in.h>
#include <netinet/ip.h>
@@ -66,27 +67,8 @@ const struct exthdr_desc *ipopt_protocols[UINT8_MAX] = {
[IPOPT_RA] = &ipopt_ra,
};
-static unsigned int calc_offset(const struct exthdr_desc *desc,
- const struct proto_hdr_template *tmpl,
- unsigned int arg)
-{
- if (!desc || tmpl == &ipopt_unknown_template)
- return 0;
-
- switch (desc->type) {
- case IPOPT_RR:
- case IPOPT_LSRR:
- case IPOPT_SSRR:
- if (tmpl == &desc->templates[IPOPT_FIELD_ADDR_0])
- return (tmpl->offset < 24) ? 0 : arg;
- return 0;
- default:
- return 0;
- }
-}
-
struct expr *ipopt_expr_alloc(const struct location *loc, uint8_t type,
- uint8_t field, uint8_t ptr)
+ uint8_t field)
{
const struct proto_hdr_template *tmpl;
const struct exthdr_desc *desc;
@@ -97,12 +79,15 @@ struct expr *ipopt_expr_alloc(const struct location *loc, uint8_t type,
if (!tmpl)
return NULL;
+ if (!tmpl->len)
+ return NULL;
+
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_IPV4;
- expr->exthdr.offset = tmpl->offset + calc_offset(desc, tmpl, ptr);
+ expr->exthdr.offset = tmpl->offset;
expr->exthdr.raw_type = desc->type;
return expr;
diff --git a/src/json.c b/src/json.c
index 63b325af..37530171 100644
--- a/src/json.c
+++ b/src/json.c
@@ -1,12 +1,21 @@
-#define _GNU_SOURCE
+/*
+ * Copyright (c) Red Hat GmbH. Author: Phil Sutter <phil@nwl.cc>
+ *
+ * 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 <nft.h>
+
#include <stdio.h>
-#include <string.h>
#include <expression.h>
#include <list.h>
#include <netlink.h>
#include <rule.h>
#include <rt.h>
+#include "nftutils.h"
#include <netdb.h>
#include <netinet/icmp6.h>
@@ -61,8 +70,9 @@ static json_t *set_dtype_json(const struct expr *key)
{
char *namedup = xstrdup(key->dtype->name), *tok;
json_t *root = NULL;
+ char *tok_safe;
- tok = strtok(namedup, " .");
+ tok = strtok_r(namedup, " .", &tok_safe);
while (tok) {
json_t *jtok = json_string(tok);
if (!root)
@@ -71,23 +81,64 @@ static json_t *set_dtype_json(const struct expr *key)
root = json_pack("[o, o]", root, jtok);
else
json_array_append_new(root, jtok);
- tok = strtok(NULL, " .");
+ tok = strtok_r(NULL, " .", &tok_safe);
}
- xfree(namedup);
+ free(namedup);
return root;
}
-static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
+static json_t *stmt_print_json(const struct stmt *stmt, struct output_ctx *octx)
{
+ char buf[1024];
+ FILE *fp;
+
+ if (stmt->ops->json)
+ return stmt->ops->json(stmt, octx);
+
+ fprintf(stderr, "warning: stmt ops %s have no json callback\n",
+ stmt->ops->name);
+
+ fp = octx->output_fp;
+ octx->output_fp = fmemopen(buf, 1024, "w");
+
+ stmt->ops->print(stmt, octx);
+
+ fclose(octx->output_fp);
+ octx->output_fp = fp;
+
+ return json_pack("s", buf);
+}
+
+static json_t *set_stmt_list_json(const struct list_head *stmt_list,
+ struct output_ctx *octx)
+{
+ unsigned int flags = octx->flags;
json_t *root, *tmp;
- const char *type, *datatype_ext = NULL;
+ struct stmt *i;
+
+ root = json_array();
+ octx->flags |= NFT_CTX_OUTPUT_STATELESS;
+
+ list_for_each_entry(i, stmt_list, list) {
+ tmp = stmt_print_json(i, octx);
+ json_array_append_new(root, tmp);
+ }
+ octx->flags = flags;
+
+ return root;
+}
+
+static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
+{
+ 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 {
@@ -104,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) {
@@ -124,6 +175,8 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
json_array_append_new(tmp, json_pack("s", "interval"));
if (set->flags & NFT_SET_TIMEOUT)
json_array_append_new(tmp, json_pack("s", "timeout"));
+ if (set->flags & NFT_SET_EVAL)
+ json_array_append_new(tmp, json_pack("s", "dynamic"));
if (json_array_size(tmp) > 0) {
json_object_set_new(root, "flags", tmp);
@@ -141,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();
@@ -152,6 +207,11 @@ static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
json_object_set_new(root, "elem", array);
}
+ if (!list_empty(&set->stmt_list)) {
+ json_object_set_new(root, "stmt",
+ set_stmt_list_json(&set->stmt_list, octx));
+ }
+
return json_pack("{s:o}", type, root);
}
@@ -168,34 +228,6 @@ static json_t *element_print_json(struct output_ctx *octx,
"elem", root);
}
-static json_t *stmt_print_json(const struct stmt *stmt, struct output_ctx *octx)
-{
- char buf[1024];
- FILE *fp;
-
- /* XXX: Can't be supported at this point:
- * xt_stmt_xlate() ignores output_fp.
- */
- if (stmt->ops->type == STMT_XT)
- return json_pack("{s:n}", "xt");
-
- if (stmt->ops->json)
- return stmt->ops->json(stmt, octx);
-
- fprintf(stderr, "warning: stmt ops %s have no json callback\n",
- stmt->ops->name);
-
- fp = octx->output_fp;
- octx->output_fp = fmemopen(buf, 1024, "w");
-
- stmt->ops->print(stmt, octx);
-
- fclose(octx->output_fp);
- octx->output_fp = fp;
-
- return json_pack("s", buf);
-}
-
static json_t *rule_print_json(struct output_ctx *octx,
const struct rule *rule)
{
@@ -227,9 +259,8 @@ static json_t *rule_print_json(struct output_ctx *octx,
static json_t *chain_print_json(const struct chain *chain)
{
- int priority, policy, n = 0;
- struct expr *dev, *expr;
- json_t *root, *tmp;
+ json_t *root, *tmp, *devs = NULL;
+ int priority, policy, i;
root = json_pack("{s:s, s:s, s:s, s:I}",
"family", family2str(chain->handle.family),
@@ -237,6 +268,9 @@ static json_t *chain_print_json(const struct chain *chain)
"name", chain->handle.chain.name,
"handle", chain->handle.handle.id);
+ if (chain->comment)
+ json_object_set_new(root, "comment", json_string(chain->comment));
+
if (chain->flags & CHAIN_F_BASECHAIN) {
mpz_export_data(&priority, chain->priority.expr->value,
BYTEORDER_HOST_ENDIAN, sizeof(int));
@@ -248,17 +282,19 @@ static json_t *chain_print_json(const struct chain *chain)
chain->hook.num),
"prio", priority,
"policy", chain_policy2str(policy));
- if (chain->dev_expr) {
- list_for_each_entry(expr, &chain->dev_expr->expressions, list) {
- dev = expr;
- n++;
- }
- }
- if (n == 1) {
- json_object_set_new(tmp, "dev",
- json_string(dev->identifier));
+ for (i = 0; i < chain->dev_array_len; i++) {
+ const char *dev = chain->dev_array[i];
+ if (!devs)
+ devs = json_string(dev);
+ else if (json_is_string(devs))
+ devs = json_pack("[o, s]", devs, dev);
+ else
+ json_array_append_new(devs, json_string(dev));
}
+ if (devs)
+ json_object_set_new(root, "dev", devs);
+
json_object_update(root, tmp);
json_decref(tmp);
}
@@ -268,10 +304,10 @@ static json_t *chain_print_json(const struct chain *chain)
static json_t *proto_name_json(uint8_t proto)
{
- const struct protoent *p = getprotobynumber(proto);
+ char name[NFT_PROTONAME_MAXSIZE];
- if (p)
- return json_string(p->p_name);
+ if (nft_getprotobynumber(proto, name, sizeof(name)))
+ return json_string(name);
return json_integer(proto);
}
@@ -305,6 +341,12 @@ static json_t *obj_print_json(const struct obj *obj)
"table", obj->handle.table.name,
"handle", obj->handle.handle.id);
+ if (obj->comment) {
+ tmp = json_pack("{s:s}", "comment", obj->comment);
+ json_object_update(root, tmp);
+ json_decref(tmp);
+ }
+
switch (obj->type) {
case NFT_OBJECT_COUNTER:
tmp = json_pack("{s:I, s:I}",
@@ -457,7 +499,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;
@@ -478,6 +520,9 @@ static json_t *table_print_json(const struct table *table)
if (tmp)
json_object_set_new(root, "flags", tmp);
+ if (table->comment)
+ json_object_set_new(root, "comment", json_string(table->comment));
+
return json_pack("{s:o}", "table", root);
}
@@ -495,11 +540,24 @@ json_t *flagcmp_expr_json(const struct expr *expr, struct output_ctx *octx)
"right", expr_print_json(expr->flagcmp.value, octx));
}
+static json_t *
+__binop_expr_json(int op, const struct expr *expr, struct output_ctx *octx)
+{
+ json_t *a = json_array();
+
+ if (expr->etype == EXPR_BINOP && expr->op == op) {
+ json_array_extend(a, __binop_expr_json(op, expr->left, octx));
+ json_array_extend(a, __binop_expr_json(op, expr->right, octx));
+ } else {
+ json_array_append_new(a, expr_print_json(expr, octx));
+ }
+ return a;
+}
+
json_t *binop_expr_json(const struct expr *expr, struct output_ctx *octx)
{
- return json_pack("{s:[o, o]}", expr_op_symbols[expr->op],
- expr_print_json(expr->left, octx),
- expr_print_json(expr->right, octx));
+ return json_pack("{s:o}", expr_op_symbols[expr->op],
+ __binop_expr_json(expr->op, expr, octx));
}
json_t *relational_expr_json(const struct expr *expr, struct output_ctx *octx)
@@ -535,15 +593,23 @@ json_t *payload_expr_json(const struct expr *expr, struct output_ctx *octx)
{
json_t *root;
- if (payload_is_known(expr))
- root = json_pack("{s:s, s:s}",
- "protocol", expr->payload.desc->name,
- "field", expr->payload.tmpl->token);
- else
+ if (payload_is_known(expr)) {
+ if (expr->payload.inner_desc) {
+ root = json_pack("{s:s, s:s, s:s}",
+ "tunnel", expr->payload.inner_desc->name,
+ "protocol", expr->payload.desc->name,
+ "field", expr->payload.tmpl->token);
+ } else {
+ root = json_pack("{s:s, s:s}",
+ "protocol", expr->payload.desc->name,
+ "field", expr->payload.tmpl->token);
+ }
+ } else {
root = json_pack("{s:s, s:i, s:i}",
"base", proto_base_tokens[expr->payload.base],
"offset", expr->payload.offset,
"len", expr->len);
+ }
return json_pack("{s:o}", "payload", root);
}
@@ -713,6 +779,11 @@ json_t *exthdr_expr_json(const struct expr *expr, struct output_ctx *octx)
return json_pack("{s:o}", "tcp option", root);
}
+ if (expr->exthdr.op == NFT_EXTHDR_OP_DCCP) {
+ root = json_pack("{s:i}", "type", expr->exthdr.raw_type);
+ return json_pack("{s:o}", "dccp option", root);
+ }
+
root = json_pack("{s:s}", "name", desc);
if (!is_exists)
json_object_set_new(root, "field", json_string(field));
@@ -1042,12 +1113,11 @@ json_t *boolean_type_json(const struct expr *expr, struct output_ctx *octx)
json_t *inet_protocol_type_json(const struct expr *expr,
struct output_ctx *octx)
{
- struct protoent *p;
-
if (!nft_output_numeric_proto(octx)) {
- p = getprotobynumber(mpz_get_uint8(expr->value));
- if (p != NULL)
- return json_string(p->p_name);
+ char name[NFT_PROTONAME_MAXSIZE];
+
+ if (nft_getprotobynumber(mpz_get_uint8(expr->value), name, sizeof(name)))
+ return json_string(name);
}
return integer_type_json(expr, octx);
}
@@ -1055,13 +1125,13 @@ json_t *inet_protocol_type_json(const struct expr *expr,
json_t *inet_service_type_json(const struct expr *expr, struct output_ctx *octx)
{
uint16_t port = mpz_get_be16(expr->value);
- const struct servent *s = NULL;
+ char name[NFT_SERVNAME_MAXSIZE];
if (!nft_output_service(octx) ||
- (s = getservbyport(port, NULL)) == NULL)
+ !nft_getservbyport(port, NULL, name, sizeof(name)))
return json_integer(ntohs(port));
- return json_string(s->s_name);
+ return json_string(name);
}
json_t *mark_type_json(const struct expr *expr, struct output_ctx *octx)
@@ -1122,6 +1192,13 @@ json_t *expr_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
return expr_print_json(stmt->expr, octx);
}
+json_t *flow_offload_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
+{
+ return json_pack("{s:{s:s, s:s+}}", "flow",
+ "op", "add", "flowtable",
+ "@", stmt->flow.table_name);
+}
+
json_t *payload_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
return json_pack("{s: {s:o, s:o}}", "mangle",
@@ -1432,12 +1509,49 @@ json_t *counter_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
"bytes", stmt->counter.bytes);
}
+json_t *last_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
+{
+ if (nft_output_stateless(octx) || stmt->last.set == 0)
+ return json_pack("{s:n}", "last");
+
+ return json_pack("{s:{s:I}}", "last", "used", stmt->last.used);
+}
+
json_t *set_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
- return json_pack("{s:{s:s, s:o, s:s+}}", "set",
+ json_t *root;
+
+ root = json_pack("{s:s, s:o, s:s+}",
"op", set_stmt_op_names[stmt->set.op],
"elem", expr_print_json(stmt->set.key, octx),
"set", "@", stmt->set.set->set->handle.set.name);
+
+ if (!list_empty(&stmt->set.stmt_list)) {
+ json_object_set_new(root, "stmt",
+ set_stmt_list_json(&stmt->set.stmt_list,
+ octx));
+ }
+
+ return json_pack("{s:o}", "set", root);
+}
+
+json_t *map_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
+{
+ json_t *root;
+
+ root = json_pack("{s:s, s:o, s:o, s:s+}",
+ "op", set_stmt_op_names[stmt->map.op],
+ "elem", expr_print_json(stmt->map.key, octx),
+ "data", expr_print_json(stmt->map.data, octx),
+ "map", "@", stmt->map.set->set->handle.set.name);
+
+ if (!list_empty(&stmt->map.stmt_list)) {
+ json_object_set_new(root, "stmt",
+ set_stmt_list_json(&stmt->map.stmt_list,
+ octx));
+ }
+
+ return json_pack("{s:o}", "map", root);
}
json_t *objref_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
@@ -1571,10 +1685,29 @@ json_t *synproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
return json_pack("{s:o}", "synproxy", root);
}
+json_t *optstrip_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
+{
+ return json_pack("{s:o}", "reset",
+ expr_print_json(stmt->optstrip.expr, octx));
+}
+
+json_t *xt_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
+{
+ static const char *xt_typename[NFT_XT_MAX] = {
+ [NFT_XT_MATCH] = "match",
+ [NFT_XT_TARGET] = "target",
+ [NFT_XT_WATCHER] = "watcher",
+ };
+
+ return json_pack("{s:{s:s, s:s}}", "xt",
+ "type", xt_typename[stmt->xt.type],
+ "name", stmt->xt.name);
+}
+
static json_t *table_print_json_full(struct netlink_ctx *ctx,
struct table *table)
{
- json_t *root = json_array(), *tmp;
+ json_t *root = json_array(), *rules = json_array(), *tmp;
struct flowtable *flowtable;
struct chain *chain;
struct rule *rule;
@@ -1584,6 +1717,11 @@ static json_t *table_print_json_full(struct netlink_ctx *ctx,
tmp = table_print_json(table);
json_array_append_new(root, tmp);
+ /* both maps and rules may refer to chains, list them first */
+ list_for_each_entry(chain, &table->chain_cache.list, cache.list) {
+ tmp = chain_print_json(chain);
+ json_array_append_new(root, tmp);
+ }
list_for_each_entry(obj, &table->obj_cache.list, cache.list) {
tmp = obj_print_json(obj);
json_array_append_new(root, tmp);
@@ -1599,15 +1737,15 @@ static json_t *table_print_json_full(struct netlink_ctx *ctx,
json_array_append_new(root, tmp);
}
list_for_each_entry(chain, &table->chain_cache.list, cache.list) {
- tmp = chain_print_json(chain);
- json_array_append_new(root, tmp);
-
list_for_each_entry(rule, &chain->rules, list) {
tmp = rule_print_json(&ctx->nft->output, rule);
- json_array_append_new(root, tmp);
+ json_array_append_new(rules, tmp);
}
}
+ json_array_extend(root, rules);
+ json_decref(rules);
+
return root;
}
@@ -1701,10 +1839,13 @@ static json_t *do_list_chains_json(struct netlink_ctx *ctx, struct cmd *cmd)
static json_t *do_list_set_json(struct netlink_ctx *ctx,
struct cmd *cmd, struct table *table)
{
- struct set *set = set_cache_find(table, cmd->handle.set.name);
+ struct set *set = cmd->set;
- if (set == NULL)
- return json_null();
+ if (!set) {
+ set = set_cache_find(table, cmd->handle.set.name);
+ if (set == NULL)
+ return json_null();
+ }
return json_pack("[o]", set_print_json(&ctx->nft->output, set));
}
@@ -1840,6 +1981,7 @@ int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_SET:
root = do_list_set_json(ctx, cmd, table);
break;
+ case CMD_OBJ_RULES:
case CMD_OBJ_RULESET:
root = do_list_ruleset_json(ctx, cmd);
break;
diff --git a/src/libnftables.c b/src/libnftables.c
index 2b2ed1a4..0dee1bac 100644
--- a/src/libnftables.c
+++ b/src/libnftables.c
@@ -2,10 +2,12 @@
* Copyright (c) 2017 Eric Leblond <eric@regit.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 as
- * published by the Free Software Foundation.
- *
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
+
+#include <nft.h>
+
#include <nftables/libnftables.h>
#include <erec.h>
#include <mnl.h>
@@ -14,12 +16,10 @@
#include <iface.h>
#include <cmd.h>
#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
+#include <sys/stat.h>
static int nft_netlink(struct nft_ctx *nft,
- struct list_head *cmds, struct list_head *msgs,
- struct mnl_socket *nf_sock)
+ struct list_head *cmds, struct list_head *msgs)
{
uint32_t batch_seqnum, seqnum = 0, last_seqnum = UINT32_MAX, num_cmds = 0;
struct netlink_ctx ctx = {
@@ -56,6 +56,13 @@ static int nft_netlink(struct nft_ctx *nft,
ret = mnl_batch_talk(&ctx, &err_list, num_cmds);
if (ret < 0) {
+ if (ctx.maybe_emsgsize && errno == EMSGSIZE) {
+ netlink_io_error(&ctx, NULL,
+ "Could not process rule: %s\n"
+ "Please, rise /proc/sys/net/core/wmem_max on the host namespace. Hint: %d bytes",
+ strerror(errno), round_pow_2(ctx.maybe_emsgsize));
+ goto out;
+ }
netlink_io_error(&ctx, NULL,
"Could not process rule: %s", strerror(errno));
goto out;
@@ -128,9 +135,7 @@ int nft_ctx_add_var(struct nft_ctx *ctx, const char *var)
if (!separator)
return -1;
- tmp = realloc(ctx->vars, (pcount + 1) * sizeof(struct nft_vars));
- if (!tmp)
- return -1;
+ tmp = xrealloc(ctx->vars, (pcount + 1) * sizeof(struct nft_vars));
*separator = '\0';
value = separator + 1;
@@ -149,11 +154,11 @@ 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);
+ free(ctx->vars);
}
EXPORT_SYMBOL(nft_ctx_add_include_path);
@@ -162,9 +167,7 @@ int nft_ctx_add_include_path(struct nft_ctx *ctx, const char *path)
char **tmp;
int pcount = ctx->num_include_paths;
- tmp = realloc(ctx->include_paths, (pcount + 1) * sizeof(char *));
- if (!tmp)
- return -1;
+ tmp = xrealloc(ctx->include_paths, (pcount + 1) * sizeof(char *));
ctx->include_paths = tmp;
@@ -179,30 +182,20 @@ 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;
}
-static void nft_ctx_netlink_init(struct nft_ctx *ctx)
-{
- ctx->nf_sock = nft_mnl_socket_open();
-}
-
EXPORT_SYMBOL(nft_ctx_new);
struct nft_ctx *nft_ctx_new(uint32_t flags)
{
- static bool init_once;
struct nft_ctx *ctx;
- if (!init_once) {
- init_once = true;
- gmp_init();
#ifdef HAVE_LIBXTABLES
- xt_init();
+ xt_init();
#endif
- }
ctx = xzalloc(sizeof(struct nft_ctx));
nft_init(ctx);
@@ -217,8 +210,7 @@ struct nft_ctx *nft_ctx_new(uint32_t flags)
ctx->output.error_fp = stderr;
init_list_head(&ctx->vars_ctx.indesc_list);
- if (flags == NFT_CTX_DEFAULT)
- nft_ctx_netlink_init(ctx);
+ ctx->nf_sock = nft_mnl_socket_open();
return ctx;
}
@@ -342,8 +334,7 @@ const char *nft_ctx_get_error_buffer(struct nft_ctx *ctx)
EXPORT_SYMBOL(nft_ctx_free);
void nft_ctx_free(struct nft_ctx *ctx)
{
- if (ctx->nf_sock)
- mnl_socket_close(ctx->nf_sock);
+ mnl_socket_close(ctx->nf_sock);
exit_cookie(&ctx->output.output_cookie);
exit_cookie(&ctx->output.error_cookie);
@@ -352,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);
@@ -395,6 +386,34 @@ void nft_ctx_set_dry_run(struct nft_ctx *ctx, bool dry)
ctx->check = dry;
}
+EXPORT_SYMBOL(nft_ctx_get_optimize);
+uint32_t nft_ctx_get_optimize(struct nft_ctx *ctx)
+{
+ return ctx->optimize_flags;
+}
+
+EXPORT_SYMBOL(nft_ctx_set_optimize);
+void nft_ctx_set_optimize(struct nft_ctx *ctx, uint32_t flags)
+{
+ ctx->optimize_flags = flags;
+}
+
+EXPORT_SYMBOL(nft_ctx_input_get_flags);
+unsigned int nft_ctx_input_get_flags(struct nft_ctx *ctx)
+{
+ return ctx->input.flags;
+}
+
+EXPORT_SYMBOL(nft_ctx_input_set_flags);
+unsigned int nft_ctx_input_set_flags(struct nft_ctx *ctx, unsigned int flags)
+{
+ unsigned int old_flags;
+
+ old_flags = ctx->input.flags;
+ ctx->input.flags = flags;
+ return old_flags;
+}
+
EXPORT_SYMBOL(nft_ctx_output_get_flags);
unsigned int nft_ctx_output_get_flags(struct nft_ctx *ctx)
{
@@ -424,13 +443,14 @@ static const struct input_descriptor indesc_cmdline = {
};
static int nft_parse_bison_buffer(struct nft_ctx *nft, const char *buf,
- struct list_head *msgs, struct list_head *cmds)
+ struct list_head *msgs, struct list_head *cmds,
+ const struct input_descriptor *indesc)
{
int ret;
parser_init(nft, nft->state, msgs, cmds, nft->top_scope);
nft->scanner = scanner_init(nft->state);
- scanner_push_buffer(nft->scanner, &indesc_cmdline, buf);
+ scanner_push_buffer(nft->scanner, indesc, buf);
ret = nft_parse(nft, nft->scanner, nft->state);
if (ret != 0 || nft->state->nerrs > 0)
@@ -439,11 +459,42 @@ static int nft_parse_bison_buffer(struct nft_ctx *nft, const char *buf,
return 0;
}
+static char *stdin_to_buffer(void)
+{
+ unsigned int bufsiz = 16384, consumed = 0;
+ int numbytes;
+ char *buf;
+
+ buf = xmalloc(bufsiz);
+
+ numbytes = read(STDIN_FILENO, buf, bufsiz);
+ while (numbytes > 0) {
+ consumed += numbytes;
+ if (consumed == bufsiz) {
+ bufsiz *= 2;
+ buf = xrealloc(buf, bufsiz);
+ }
+ numbytes = read(STDIN_FILENO, buf + consumed, bufsiz - consumed);
+ }
+ buf[consumed] = '\0';
+
+ return buf;
+}
+
+static const struct input_descriptor indesc_stdin = {
+ .type = INDESC_STDIN,
+ .name = "/dev/stdin",
+};
+
static int nft_parse_bison_filename(struct nft_ctx *nft, const char *filename,
struct list_head *msgs, struct list_head *cmds)
{
int ret;
+ if (nft->stdin_buf)
+ return nft_parse_bison_buffer(nft, nft->stdin_buf, msgs, cmds,
+ &indesc_stdin);
+
parser_init(nft, nft->state, msgs, cmds, nft->top_scope);
nft->scanner = scanner_init(nft->state);
if (scanner_read_file(nft, filename, &internal_location) < 0)
@@ -459,33 +510,53 @@ static int nft_parse_bison_filename(struct nft_ctx *nft, const char *filename,
static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs,
struct list_head *cmds)
{
- struct nft_cache_filter filter = {};
+ struct nft_cache_filter *filter;
+ struct cmd *cmd, *next;
+ bool collapsed = false;
unsigned int flags;
- struct cmd *cmd;
+ int err = 0;
- flags = nft_cache_evaluate(nft, cmds, &filter);
- if (nft_cache_update(nft, flags, msgs, &filter) < 0)
+ filter = nft_cache_filter_init();
+ if (nft_cache_evaluate(nft, cmds, msgs, filter, &flags) < 0) {
+ nft_cache_filter_fini(filter);
return -1;
+ }
+ if (nft_cache_update(nft, flags, msgs, filter) < 0) {
+ nft_cache_filter_fini(filter);
+ return -1;
+ }
+
+ nft_cache_filter_fini(filter);
+
+ if (nft_cmd_collapse(cmds))
+ collapsed = true;
list_for_each_entry(cmd, cmds, list) {
+ if (cmd->op != CMD_ADD &&
+ cmd->op != CMD_CREATE)
+ continue;
+
+ nft_cmd_expand(cmd);
+ }
+
+ list_for_each_entry_safe(cmd, next, cmds, list) {
struct eval_ctx ectx = {
.nft = nft,
.msgs = msgs,
};
+
if (cmd_evaluate(&ectx, cmd) < 0 &&
- ++nft->state->nerrs == nft->parser_max_errors)
- return -1;
+ ++nft->state->nerrs == nft->parser_max_errors) {
+ err = -1;
+ break;
+ }
}
- if (nft->state->nerrs)
- return -1;
+ if (collapsed)
+ nft_cmd_uncollapse(cmds);
- list_for_each_entry(cmd, cmds, list) {
- if (cmd->op != CMD_ADD)
- continue;
-
- nft_cmd_expand(cmd);
- }
+ if (err < 0 || nft->state->nerrs)
+ return -1;
return 0;
}
@@ -502,10 +573,11 @@ int nft_run_cmd_from_buffer(struct nft_ctx *nft, const char *buf)
nlbuf = xzalloc(strlen(buf) + 2);
sprintf(nlbuf, "%s\n", buf);
- if (nft_output_json(&nft->output))
+ if (nft_output_json(&nft->output) || nft_input_json(&nft->input))
rc = nft_parse_json_buffer(nft, nlbuf, &msgs, &cmds);
if (rc == -EINVAL)
- rc = nft_parse_bison_buffer(nft, nlbuf, &msgs, &cmds);
+ rc = nft_parse_bison_buffer(nft, nlbuf, &msgs, &cmds,
+ &indesc_cmdline);
parser_rc = rc;
@@ -523,7 +595,7 @@ int nft_run_cmd_from_buffer(struct nft_ctx *nft, const char *buf)
goto err;
}
- if (nft_netlink(nft, &cmds, &msgs, nft->nf_sock) != 0)
+ if (nft_netlink(nft, &cmds, &msgs) != 0)
rc = -1;
err:
erec_print_list(&nft->output, &msgs, nft->debug_mask);
@@ -542,8 +614,10 @@ err:
nft_output_json(&nft->output) &&
nft_output_echo(&nft->output))
json_print_echo(nft);
- if (rc)
+
+ if (rc || nft->check)
nft_cache_release(&nft->cache);
+
return rc;
}
@@ -573,7 +647,7 @@ retry:
}
snprintf(buf + offset, bufsize - offset, "\n");
- rc = nft_parse_bison_buffer(ctx, buf, msgs, &cmds);
+ rc = nft_parse_bison_buffer(ctx, buf, msgs, &cmds, &indesc_cmdline);
assert(list_empty(&cmds));
/* Stash the buffer that contains the variable definitions and zap the
@@ -588,29 +662,61 @@ retry:
return rc;
}
-EXPORT_SYMBOL(nft_run_cmd_from_filename);
-int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
+/* need to use stat() to, fopen() will block for named fifos and
+ * libjansson makes no checks before or after open either.
+ */
+static struct error_record *filename_is_useable(struct nft_ctx *nft, const char *name)
{
+ unsigned int type;
+ struct stat sb;
+ int err;
+
+ err = stat(name, &sb);
+ if (err)
+ return error(&internal_location, "Could not open file \"%s\": %s\n",
+ name, strerror(errno));
+
+ type = sb.st_mode & S_IFMT;
+
+ 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);
+}
+
+static int __nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
+{
+ struct error_record *erec;
struct cmd *cmd, *next;
int rc, parser_rc;
LIST_HEAD(msgs);
LIST_HEAD(cmds);
+ erec = filename_is_useable(nft, filename);
+ if (erec) {
+ erec_print(&nft->output, erec, nft->debug_mask);
+ erec_destroy(erec);
+ return -1;
+ }
+
rc = load_cmdline_vars(nft, &msgs);
if (rc < 0)
goto err;
- if (!strcmp(filename, "-"))
- filename = "/dev/stdin";
-
rc = -EINVAL;
- if (nft_output_json(&nft->output))
+ if (nft_output_json(&nft->output) || nft_input_json(&nft->input))
rc = nft_parse_json_filename(nft, filename, &msgs, &cmds);
if (rc == -EINVAL)
rc = nft_parse_bison_filename(nft, filename, &msgs, &cmds);
parser_rc = rc;
+ if (nft->optimize_flags)
+ nft_optimize(nft, &cmds);
+
rc = nft_evaluate(nft, &msgs, &cmds);
if (rc < 0)
goto err;
@@ -620,7 +726,7 @@ int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
goto err;
}
- if (nft_netlink(nft, &cmds, &msgs, nft->nf_sock) != 0)
+ if (nft_netlink(nft, &cmds, &msgs) != 0)
rc = -1;
err:
erec_print_list(&nft->output, &msgs, nft->debug_mask);
@@ -638,18 +744,68 @@ 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);
+ free(indesc);
}
}
- xfree(nft->vars_ctx.buf);
+ free_const(nft->vars_ctx.buf);
if (!rc &&
nft_output_json(&nft->output) &&
nft_output_echo(&nft->output))
json_print_echo(nft);
- if (rc)
+
+ if (rc || nft->check)
nft_cache_release(&nft->cache);
+
+ scope_release(nft->state->scopes[0]);
+
return rc;
}
+
+static int nft_run_optimized_file(struct nft_ctx *nft, const char *filename)
+{
+ uint32_t optimize_flags;
+ bool check;
+ int ret;
+
+ check = nft->check;
+ nft->check = true;
+ optimize_flags = nft->optimize_flags;
+ nft->optimize_flags = 0;
+
+ /* First check the original ruleset loads fine as is. */
+ ret = __nft_run_cmd_from_filename(nft, filename);
+ if (ret < 0)
+ return ret;
+
+ nft->check = check;
+ nft->optimize_flags = optimize_flags;
+
+ return __nft_run_cmd_from_filename(nft, filename);
+}
+
+EXPORT_SYMBOL(nft_run_cmd_from_filename);
+int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
+{
+ int ret;
+
+ if (!strcmp(filename, "-"))
+ filename = "/dev/stdin";
+
+ if (!strcmp(filename, "/dev/stdin") &&
+ !nft_output_json(&nft->output))
+ nft->stdin_buf = stdin_to_buffer();
+
+ if (nft->optimize_flags) {
+ ret = nft_run_optimized_file(nft, filename);
+ free_const(nft->stdin_buf);
+ return ret;
+ }
+
+ ret = __nft_run_cmd_from_filename(nft, filename);
+ free_const(nft->stdin_buf);
+
+ return ret;
+}
diff --git a/src/libnftables.map b/src/libnftables.map
index d3a795ce..9369f44f 100644
--- a/src/libnftables.map
+++ b/src/libnftables.map
@@ -28,3 +28,13 @@ LIBNFTABLES_2 {
nft_ctx_add_var;
nft_ctx_clear_vars;
} LIBNFTABLES_1;
+
+LIBNFTABLES_3 {
+ nft_ctx_set_optimize;
+ nft_ctx_get_optimize;
+} LIBNFTABLES_2;
+
+LIBNFTABLES_4 {
+ nft_ctx_input_get_flags;
+ nft_ctx_input_set_flags;
+} LIBNFTABLES_3;
diff --git a/src/main.c b/src/main.c
index 5847fc4a..d3491cda 100644
--- a/src/main.c
+++ b/src/main.c
@@ -8,12 +8,12 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
-#include <stdlib.h>
+#include <nft.h>
+
#include <stddef.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
-#include <string.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/types.h>
@@ -36,7 +36,8 @@ enum opt_indices {
IDX_INTERACTIVE,
IDX_INCLUDEPATH,
IDX_CHECK,
-#define IDX_RULESET_INPUT_END IDX_CHECK
+ IDX_OPTIMIZE,
+#define IDX_RULESET_INPUT_END IDX_OPTIMIZE
/* Ruleset list formatting */
IDX_HANDLE,
#define IDX_RULESET_LIST_START IDX_HANDLE
@@ -80,6 +81,7 @@ enum opt_vals {
OPT_NUMERIC_PROTO = 'p',
OPT_NUMERIC_TIME = 'T',
OPT_TERSE = 't',
+ OPT_OPTIMIZE = 'o',
OPT_INVALID = '?',
};
@@ -136,6 +138,8 @@ static const struct nft_opt nft_options[] = {
"Format output in JSON"),
[IDX_DEBUG] = NFT_OPT("debug", OPT_DEBUG, "<level [,level...]>",
"Specify debugging level (scanner, parser, eval, netlink, mnl, proto-ctx, segtree, all)"),
+ [IDX_OPTIMIZE] = NFT_OPT("optimize", OPT_OPTIMIZE, NULL,
+ "Optimize ruleset"),
};
#define NR_NFT_OPTIONS (sizeof(nft_options) / sizeof(nft_options[0]))
@@ -321,6 +325,7 @@ static bool nft_options_check(int argc, char * const argv[])
{
bool skip = false, nonoption = false;
int pos = 0, i;
+ size_t j;
for (i = 1; i < argc; i++) {
pos += strlen(argv[i - 1]) + 1;
@@ -329,23 +334,22 @@ static bool nft_options_check(int argc, char * const argv[])
} else if (skip) {
skip = false;
continue;
- } else if (argv[i][0] == '-') {
- if (nonoption) {
- nft_options_error(argc, argv, pos);
- return false;
- } else if (argv[i][1] == 'd' ||
- argv[i][1] == 'I' ||
- argv[i][1] == 'f' ||
- argv[i][1] == 'D' ||
- !strcmp(argv[i], "--debug") ||
- !strcmp(argv[i], "--includepath") ||
- !strcmp(argv[i], "--define") ||
- !strcmp(argv[i], "--file")) {
- skip = true;
- continue;
- }
} else if (argv[i][0] != '-') {
nonoption = true;
+ continue;
+ }
+ if (nonoption) {
+ nft_options_error(argc, argv, pos);
+ return false;
+ }
+ for (j = 0; j < NR_NFT_OPTIONS; j++) {
+ if (nft_options[j].arg &&
+ (argv[i][1] == (char)nft_options[j].val ||
+ (argv[i][1] == '-' &&
+ !strcmp(argv[i] + 2, nft_options[j].name)))) {
+ skip = true;
+ break;
+ }
}
}
@@ -357,11 +361,11 @@ int main(int argc, char * const *argv)
const struct option *options = get_options();
bool interactive = false, define = false;
const char *optstring = get_optstring();
- char *buf = NULL, *filename = NULL;
unsigned int output_flags = 0;
+ int i, val, rc = EXIT_SUCCESS;
unsigned int debug_mask;
+ char *filename = NULL;
unsigned int len;
- int i, val, rc;
/* nftables cannot be used with setuid in a safe way. */
if (getuid() != geteuid())
@@ -380,20 +384,20 @@ int main(int argc, char * const *argv)
switch (val) {
case OPT_HELP:
show_help(argv[0]);
- exit(EXIT_SUCCESS);
+ goto out;
case OPT_VERSION:
printf("%s v%s (%s)\n",
PACKAGE_NAME, PACKAGE_VERSION, RELEASE_NAME);
- exit(EXIT_SUCCESS);
+ goto out;
case OPT_VERSION_LONG:
show_version();
- exit(EXIT_SUCCESS);
+ goto out;
case OPT_DEFINE:
if (nft_ctx_add_var(nft, optarg)) {
fprintf(stderr,
"Failed to define variable '%s'\n",
optarg);
- exit(EXIT_FAILURE);
+ goto out_fail;
}
define = true;
break;
@@ -401,9 +405,19 @@ int main(int argc, char * const *argv)
nft_ctx_set_dry_run(nft, true);
break;
case OPT_FILE:
+ if (interactive) {
+ fprintf(stderr,
+ "Error: -i/--interactive and -f/--file options cannot be combined\n");
+ goto out_fail;
+ }
filename = optarg;
break;
case OPT_INTERACTIVE:
+ if (filename) {
+ fprintf(stderr,
+ "Error: -i/--interactive and -f/--file options cannot be combined\n");
+ goto out_fail;
+ }
interactive = true;
break;
case OPT_INCLUDEPATH:
@@ -411,7 +425,7 @@ int main(int argc, char * const *argv)
fprintf(stderr,
"Failed to add include path '%s'\n",
optarg);
- exit(EXIT_FAILURE);
+ goto out_fail;
}
break;
case OPT_NUMERIC:
@@ -446,7 +460,7 @@ int main(int argc, char * const *argv)
if (i == array_size(debug_param)) {
fprintf(stderr, "invalid debug parameter `%s'\n",
optarg);
- exit(EXIT_FAILURE);
+ goto out_fail;
}
if (end == NULL)
@@ -466,7 +480,7 @@ int main(int argc, char * const *argv)
output_flags |= NFT_CTX_OUTPUT_JSON;
#else
fprintf(stderr, "JSON support not compiled-in\n");
- exit(EXIT_FAILURE);
+ goto out_fail;
#endif
break;
case OPT_GUID:
@@ -484,19 +498,24 @@ int main(int argc, char * const *argv)
case OPT_TERSE:
output_flags |= NFT_CTX_OUTPUT_TERSE;
break;
+ case OPT_OPTIMIZE:
+ nft_ctx_set_optimize(nft, 0x1);
+ break;
case OPT_INVALID:
- exit(EXIT_FAILURE);
+ goto out_fail;
}
}
if (!filename && define) {
fprintf(stderr, "Error: -D/--define can only be used with -f/--filename\n");
- exit(EXIT_FAILURE);
+ goto out_fail;
}
nft_ctx_output_set_flags(nft, output_flags);
if (optind != argc) {
+ char *buf;
+
for (len = 0, i = optind; i < argc; i++)
len += strlen(argv[i]) + strlen(" ");
@@ -504,7 +523,7 @@ int main(int argc, char * const *argv)
if (buf == NULL) {
fprintf(stderr, "%s:%u: Memory allocation failure\n",
__FILE__, __LINE__);
- exit(EXIT_FAILURE);
+ goto out_fail;
}
for (i = optind; i < argc; i++) {
strcat(buf, argv[i]);
@@ -512,22 +531,24 @@ int main(int argc, char * const *argv)
strcat(buf, " ");
}
rc = !!nft_run_cmd_from_buffer(nft, buf);
+ free(buf);
} else if (filename != NULL) {
rc = !!nft_run_cmd_from_filename(nft, filename);
} else if (interactive) {
if (cli_init(nft) < 0) {
fprintf(stderr, "%s: interactive CLI not supported in this build\n",
argv[0]);
- exit(EXIT_FAILURE);
+ goto out_fail;
}
- return EXIT_SUCCESS;
} else {
fprintf(stderr, "%s: no command specified\n", argv[0]);
- exit(EXIT_FAILURE);
+ goto out_fail;
}
- free(buf);
+out:
nft_ctx_free(nft);
-
return rc;
+out_fail:
+ nft_ctx_free(nft);
+ return EXIT_FAILURE;
}
diff --git a/src/mergesort.c b/src/mergesort.c
index 152b0556..5e676be1 100644
--- a/src/mergesort.c
+++ b/src/mergesort.c
@@ -2,17 +2,16 @@
* Copyright (c) 2017 Elise Lennion <elise.lennion@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
-#include <stdint.h>
+#include <nft.h>
+
#include <expression.h>
#include <gmputil.h>
#include <list.h>
-static void expr_msort_value(const struct expr *expr, mpz_t value);
-
static void concat_expr_msort_value(const struct expr *expr, mpz_t value)
{
unsigned int len = 0, ilen;
@@ -28,19 +27,17 @@ static void concat_expr_msort_value(const struct expr *expr, mpz_t value)
mpz_import_data(value, data, BYTEORDER_HOST_ENDIAN, len);
}
-static void expr_msort_value(const struct expr *expr, mpz_t value)
+static mpz_srcptr expr_msort_value(const struct expr *expr, mpz_t value)
{
switch (expr->etype) {
case EXPR_SET_ELEM:
- expr_msort_value(expr->key, value);
- break;
+ return expr_msort_value(expr->key, value);
case EXPR_BINOP:
case EXPR_MAPPING:
- expr_msort_value(expr->left, value);
- break;
+ case EXPR_RANGE:
+ return expr_msort_value(expr->left, value);
case EXPR_VALUE:
- mpz_set(value, expr->value);
- break;
+ return expr->value;
case EXPR_CONCAT:
concat_expr_msort_value(expr, value);
break;
@@ -51,25 +48,29 @@ static void expr_msort_value(const struct expr *expr, mpz_t value)
default:
BUG("Unknown expression %s\n", expr_name(expr));
}
+ return value;
}
static int expr_msort_cmp(const struct expr *e1, const struct expr *e2)
{
- mpz_t value1, value2;
+ mpz_srcptr value1;
+ mpz_srcptr value2;
+ mpz_t value1_tmp;
+ mpz_t value2_tmp;
int ret;
- mpz_init(value1);
- mpz_init(value2);
- expr_msort_value(e1, value1);
- expr_msort_value(e2, value2);
+ mpz_init(value1_tmp);
+ mpz_init(value2_tmp);
+ value1 = expr_msort_value(e1, value1_tmp);
+ value2 = expr_msort_value(e2, value2_tmp);
ret = mpz_cmp(value1, value2);
- mpz_clear(value1);
- mpz_clear(value2);
+ mpz_clear(value1_tmp);
+ mpz_clear(value2_tmp);
return ret;
}
-static void list_splice_sorted(struct list_head *list, struct list_head *head)
+void list_splice_sorted(struct list_head *list, struct list_head *head)
{
struct list_head *h = head->next;
struct list_head *l = list->next;
@@ -77,7 +78,7 @@ static 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/src/meta.c b/src/meta.c
index bdd10269..a17bacf0 100644
--- a/src/meta.c
+++ b/src/meta.c
@@ -10,13 +10,12 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <errno.h>
#include <limits.h>
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <pwd.h>
@@ -25,6 +24,7 @@
#include <linux/netfilter.h>
#include <linux/pkt_sched.h>
#include <linux/if_packet.h>
+#include <time.h>
#include <nftables.h>
#include <expression.h>
@@ -37,10 +37,6 @@
#include <iface.h>
#include <json.h>
-#define _XOPEN_SOURCE
-#define __USE_XOPEN
-#include <time.h>
-
static void tchandle_type_print(const struct expr *expr,
struct output_ctx *octx)
{
@@ -66,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:
- xfree(str);
+
*res = constant_expr_alloc(&sym->location, sym->dtype,
BYTEORDER_HOST_ENDIAN,
sizeof(handle) * BITS_PER_BYTE, &handle);
return NULL;
err:
- xfree(str);
return error(&sym->location, "Could not parse %s", sym->dtype->desc);
}
@@ -220,18 +205,20 @@ static struct error_record *uid_type_parse(struct parse_ctx *ctx,
struct expr **res)
{
struct passwd *pw;
- uint64_t uid;
+ uid_t uid;
char *endptr = NULL;
pw = getpwnam(sym->identifier);
if (pw != NULL)
uid = pw->pw_uid;
else {
- uid = strtoull(sym->identifier, &endptr, 10);
- if (uid > UINT32_MAX)
+ uint64_t _uid = strtoull(sym->identifier, &endptr, 10);
+
+ if (_uid > UINT32_MAX)
return error(&sym->location, "Value too large");
else if (*endptr)
return error(&sym->location, "User does not exist");
+ uid = _uid;
}
*res = constant_expr_alloc(&sym->location, sym->dtype,
@@ -274,18 +261,20 @@ static struct error_record *gid_type_parse(struct parse_ctx *ctx,
struct expr **res)
{
struct group *gr;
- uint64_t gid;
+ gid_t gid;
char *endptr = NULL;
gr = getgrnam(sym->identifier);
if (gr != NULL)
gid = gr->gr_gid;
else {
- gid = strtoull(sym->identifier, &endptr, 0);
- if (gid > UINT32_MAX)
+ uint64_t _gid = strtoull(sym->identifier, &endptr, 0);
+
+ if (_gid > UINT32_MAX)
return error(&sym->location, "Value too large");
else if (*endptr)
return error(&sym->location, "Group does not exist");
+ gid = _gid;
}
*res = constant_expr_alloc(&sym->location, sym->dtype,
@@ -336,7 +325,7 @@ const struct datatype pkttype_type = {
void devgroup_table_init(struct nft_ctx *ctx)
{
- ctx->output.tbl.devgroup = rt_symbol_table_init("/etc/iproute2/group");
+ ctx->output.tbl.devgroup = rt_symbol_table_init("group");
}
void devgroup_table_exit(struct nft_ctx *ctx)
@@ -357,17 +346,23 @@ static struct error_record *devgroup_type_parse(struct parse_ctx *ctx,
return symbolic_constant_parse(ctx, sym, ctx->tbl->devgroup, res);
}
+static void devgroup_type_describe(struct output_ctx *octx)
+{
+ rt_symbol_table_describe(octx, "group",
+ octx->tbl.devgroup, &devgroup_type);
+}
+
const struct datatype devgroup_type = {
.type = TYPE_DEVGROUP,
.name = "devgroup",
.desc = "devgroup name",
+ .describe = devgroup_type_describe,
.byteorder = BYTEORDER_HOST_ENDIAN,
.size = 4 * BITS_PER_BYTE,
.basetype = &integer_type,
.print = devgroup_type_print,
.json = devgroup_type_json,
.parse = devgroup_type_parse,
- .flags = DTYPE_F_PREFIX,
};
const struct datatype ifname_type = {
@@ -381,41 +376,43 @@ const struct datatype ifname_type = {
static void date_type_print(const struct expr *expr, struct output_ctx *octx)
{
- uint64_t tstamp = mpz_get_uint64(expr->value);
- struct tm *tm, *cur_tm;
+ uint64_t tstamp64 = mpz_get_uint64(expr->value);
char timestr[21];
+ time_t tstamp;
+ struct tm tm;
/* Convert from nanoseconds to seconds */
- tstamp /= 1000000000L;
+ tstamp64 /= 1000000000L;
/* Obtain current tm, to add tm_gmtoff to the timestamp */
- cur_tm = localtime((time_t *) &tstamp);
-
- if (cur_tm)
- tstamp += cur_tm->tm_gmtoff;
+ tstamp = tstamp64;
+ if (localtime_r(&tstamp, &tm))
+ tstamp64 += tm.tm_gmtoff;
- if ((tm = gmtime((time_t *) &tstamp)) != NULL &&
- strftime(timestr, sizeof(timestr) - 1, "%F %T", tm))
+ tstamp = tstamp64;
+ if (gmtime_r(&tstamp, &tm) &&
+ strftime(timestr, sizeof(timestr) - 1, "%Y-%m-%d %T", &tm))
nft_print(octx, "\"%s\"", timestr);
else
nft_print(octx, "Error converting timestamp to printed time");
}
-static time_t parse_iso_date(const char *sym)
+static bool parse_iso_date(uint64_t *tstamp, const char *sym)
{
- struct tm tm, *cur_tm;
+ struct tm cur_tm;
+ struct tm tm;
time_t ts;
memset(&tm, 0, sizeof(struct tm));
- if (strptime(sym, "%F %T", &tm))
+ if (strptime(sym, "%Y-%m-%d %T", &tm))
goto success;
- if (strptime(sym, "%F %R", &tm))
+ if (strptime(sym, "%Y-%m-%d %R", &tm))
goto success;
- if (strptime(sym, "%F", &tm))
+ if (strptime(sym, "%Y-%m-%d", &tm))
goto success;
- return -1;
+ return false;
success:
/*
@@ -425,14 +422,17 @@ success:
*/
ts = timegm(&tm);
- /* Obtain current tm as well (at the specified time), so that we can substract tm_gmtoff */
- cur_tm = localtime(&ts);
+ if (ts == (time_t) -1)
+ return false;
- if (ts == (time_t) -1 || cur_tm == NULL)
- return ts;
+ /* Obtain current tm as well (at the specified time), so that we can substract tm_gmtoff */
+ if (!localtime_r(&ts, &cur_tm))
+ return false;
/* Substract tm_gmtoff to get the current time */
- return ts - cur_tm->tm_gmtoff;
+ *tstamp = ts - cur_tm.tm_gmtoff;
+
+ return true;
}
static struct error_record *date_type_parse(struct parse_ctx *ctx,
@@ -440,9 +440,9 @@ static struct error_record *date_type_parse(struct parse_ctx *ctx,
struct expr **res)
{
const char *endptr = sym->identifier;
- time_t tstamp;
+ uint64_t tstamp;
- if ((tstamp = parse_iso_date(sym->identifier)) != -1)
+ if (parse_iso_date(&tstamp, sym->identifier))
goto success;
tstamp = strtoul(sym->identifier, (char **) &endptr, 10);
@@ -485,16 +485,21 @@ static void day_type_print(const struct expr *expr, struct output_ctx *octx)
static void hour_type_print(const struct expr *expr, struct output_ctx *octx)
{
uint32_t seconds = mpz_get_uint32(expr->value), minutes, hours;
- struct tm *cur_tm;
+ struct tm cur_tm;
time_t ts;
/* Obtain current tm, so that we can add tm_gmtoff */
ts = time(NULL);
- cur_tm = localtime(&ts);
+ if (ts != ((time_t) -1) && localtime_r(&ts, &cur_tm)) {
+ int32_t adj = seconds + cur_tm.tm_gmtoff;
- if (cur_tm)
- seconds = (seconds + cur_tm->tm_gmtoff) % SECONDS_PER_DAY;
+ 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;
@@ -511,9 +516,12 @@ static struct error_record *hour_type_parse(struct parse_ctx *ctx,
struct expr **res)
{
struct error_record *er;
- struct tm tm, *cur_tm;
- uint64_t result = 0;
+ struct tm cur_tm_data;
+ struct tm *cur_tm;
+ uint32_t result;
+ uint64_t tmp;
char *endptr;
+ struct tm tm;
time_t ts;
memset(&tm, 0, sizeof(struct tm));
@@ -527,7 +535,10 @@ static struct error_record *hour_type_parse(struct parse_ctx *ctx,
/* Obtain current tm, so that we can substract tm_gmtoff */
ts = time(NULL);
- cur_tm = localtime(&ts);
+ if (ts != ((time_t) -1) && localtime_r(&ts, &cur_tm_data))
+ cur_tm = &cur_tm_data;
+ else
+ cur_tm = NULL;
endptr = strptime(sym->identifier, "%T", &tm);
if (endptr && *endptr == '\0')
@@ -540,8 +551,8 @@ static struct error_record *hour_type_parse(struct parse_ctx *ctx,
if (endptr && *endptr)
return error(&sym->location, "Can't parse trailing input: \"%s\"\n", endptr);
- if ((er = time_parse(&sym->location, sym->identifier, &result)) == NULL) {
- result /= 1000;
+ if ((er = time_parse(&sym->location, sym->identifier, &tmp)) == NULL) {
+ result = tmp / 1000;
goto convert;
}
@@ -595,7 +606,7 @@ const struct datatype hour_type = {
.name = "hour",
.desc = "Hour of day of packet reception",
.byteorder = BYTEORDER_HOST_ENDIAN,
- .size = sizeof(uint64_t) * BITS_PER_BYTE,
+ .size = sizeof(uint32_t) * BITS_PER_BYTE,
.basetype = &integer_type,
.print = hour_type_print,
.parse = hour_type_parse,
@@ -691,6 +702,8 @@ const struct meta_template meta_templates[] = {
[NFT_META_SDIFNAME] = META_TEMPLATE("sdifname", &ifname_type,
IFNAMSIZ * BITS_PER_BYTE,
BYTEORDER_HOST_ENDIAN),
+ [NFT_META_BRI_BROUTE] = META_TEMPLATE("broute", &integer_type,
+ 1 , BYTEORDER_HOST_ENDIAN),
};
static bool meta_key_is_unqualified(enum nft_meta_keys key)
@@ -710,12 +723,16 @@ static bool meta_key_is_unqualified(enum nft_meta_keys key)
static void meta_expr_print(const struct expr *expr, struct output_ctx *octx)
{
- if (meta_key_is_unqualified(expr->meta.key))
- nft_print(octx, "%s",
- meta_templates[expr->meta.key].token);
+ const char *token = "unknown";
+ uint32_t key = expr->meta.key;
+
+ if (key < array_size(meta_templates))
+ token = meta_templates[key].token;
+
+ if (meta_key_is_unqualified(key))
+ nft_print(octx, "%s", token);
else
- nft_print(octx, "meta %s",
- meta_templates[expr->meta.key].token);
+ nft_print(octx, "meta %s", token);
}
static bool meta_expr_cmp(const struct expr *e1, const struct expr *e2)
@@ -727,6 +744,7 @@ static void meta_expr_clone(struct expr *new, const struct expr *expr)
{
new->meta.key = expr->meta.key;
new->meta.base = expr->meta.base;
+ new->meta.inner_desc = expr->meta.inner_desc;
}
/**
@@ -761,6 +779,11 @@ static void meta_expr_pctx_update(struct proto_ctx *ctx,
break;
case NFT_META_NFPROTO:
protonum = mpz_get_uint8(right->value);
+ if (protonum == NFPROTO_IPV4 && h->desc == &proto_ip)
+ break;
+ else if (protonum == NFPROTO_IPV6 && h->desc == &proto_ip6)
+ break;
+
desc = proto_find_upper(h->desc, protonum);
if (desc == NULL) {
desc = &proto_unknown;
@@ -800,13 +823,19 @@ static void meta_expr_pctx_update(struct proto_ctx *ctx,
}
#define NFTNL_UDATA_META_KEY 0
-#define NFTNL_UDATA_META_MAX 1
+#define NFTNL_UDATA_META_INNER_DESC 1
+#define NFTNL_UDATA_META_MAX 2
static int meta_expr_build_udata(struct nftnl_udata_buf *udbuf,
const struct expr *expr)
{
nftnl_udata_put_u32(udbuf, NFTNL_UDATA_META_KEY, expr->meta.key);
+ if (expr->meta.inner_desc) {
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_META_INNER_DESC,
+ expr->meta.inner_desc->id);
+ }
+
return 0;
}
@@ -818,6 +847,7 @@ static int meta_parse_udata(const struct nftnl_udata *attr, void *data)
switch (type) {
case NFTNL_UDATA_META_KEY:
+ case NFTNL_UDATA_META_INNER_DESC:
if (len != sizeof(uint32_t))
return -1;
break;
@@ -832,6 +862,8 @@ static int meta_parse_udata(const struct nftnl_udata *attr, void *data)
static struct expr *meta_expr_parse_udata(const struct nftnl_udata *attr)
{
const struct nftnl_udata *ud[NFTNL_UDATA_META_MAX + 1] = {};
+ const struct proto_desc *desc;
+ struct expr *expr;
uint32_t key;
int err;
@@ -845,7 +877,14 @@ static struct expr *meta_expr_parse_udata(const struct nftnl_udata *attr)
key = nftnl_udata_get_u32(ud[NFTNL_UDATA_META_KEY]);
- return meta_expr_alloc(&internal_location, key);
+ expr = meta_expr_alloc(&internal_location, key);
+
+ if (ud[NFTNL_UDATA_META_INNER_DESC]) {
+ desc = find_proto_desc(ud[NFTNL_UDATA_META_INNER_DESC]);
+ expr->meta.inner_desc = desc;
+ }
+
+ return expr;
}
const struct expr_ops meta_expr_ops = {
@@ -894,12 +933,16 @@ struct expr *meta_expr_alloc(const struct location *loc, enum nft_meta_keys key)
static void meta_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
+ const char *token = "unknown";
+ uint32_t key = stmt->meta.key;
+
+ if (key < array_size(meta_templates))
+ token = meta_templates[key].token;
+
if (meta_key_is_unqualified(stmt->meta.key))
- nft_print(octx, "%s set ",
- meta_templates[stmt->meta.key].token);
+ nft_print(octx, "%s set ", token);
else
- nft_print(octx, "meta %s set ",
- meta_templates[stmt->meta.key].token);
+ nft_print(octx, "meta %s set ", token);
expr_print(stmt->meta.expr, octx);
}
@@ -924,8 +967,11 @@ struct stmt *meta_stmt_alloc(const struct location *loc, enum nft_meta_keys key,
stmt = stmt_alloc(loc, &meta_stmt_ops);
stmt->meta.key = key;
- stmt->meta.tmpl = &meta_templates[key];
stmt->meta.expr = expr;
+
+ if (key < array_size(meta_templates))
+ stmt->meta.tmpl = &meta_templates[key];
+
return stmt;
}
@@ -953,11 +999,11 @@ struct error_record *meta_key_parse(const struct location *loc,
const char *str,
unsigned int *value)
{
- int ret, len, offset = 0;
const char *sep = "";
+ size_t offset = 0;
unsigned int i;
char buf[1024];
- size_t size;
+ size_t len;
for (i = 0; i < array_size(meta_templates); i++) {
if (!meta_templates[i].token || strcmp(meta_templates[i].token, str))
@@ -980,9 +1026,10 @@ struct error_record *meta_key_parse(const struct location *loc,
}
len = (int)sizeof(buf);
- size = sizeof(buf);
for (i = 0; i < array_size(meta_templates); i++) {
+ int ret;
+
if (!meta_templates[i].token)
continue;
@@ -990,8 +1037,8 @@ struct error_record *meta_key_parse(const struct location *loc,
sep = ", ";
ret = snprintf(buf+offset, len, "%s%s", sep, meta_templates[i].token);
- SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
- assert(offset < (int)sizeof(buf));
+ SNPRINTF_BUFFER_SIZE(ret, &len, &offset);
+ assert(len > 0);
}
return error(loc, "syntax error, unexpected %s, known keys are %s", str, buf);
diff --git a/src/mini-gmp.c b/src/mini-gmp.c
index 04bed3f5..186dc3a4 100644
--- a/src/mini-gmp.c
+++ b/src/mini-gmp.c
@@ -41,12 +41,12 @@ see https://www.gnu.org/licenses/. */
mpn/generic/sbpi1_div_qr.c, mpn/generic/sub_n.c,
mpn/generic/submul_1.c. */
+#include <nft.h>
+
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
#include "mini-gmp.h"
diff --git a/src/misspell.c b/src/misspell.c
index 6536d755..f5354fa8 100644
--- a/src/misspell.c
+++ b/src/misspell.c
@@ -1,5 +1,13 @@
-#include <stdlib.h>
-#include <string.h>
+/*
+ * Copyright (c) 2018 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 <nft.h>
+
#include <limits.h>
#include <utils.h>
#include <misspell.h>
@@ -64,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 2d5afdfe..9e4bfcd9 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -2,12 +2,14 @@
* Copyright (c) 2013-2017 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 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <libmnl/libmnl.h>
#include <libnftnl/common.h>
#include <libnftnl/ruleset.h>
@@ -26,13 +28,12 @@
#include <linux/netfilter/nf_tables.h>
#include <mnl.h>
-#include <string.h>
+#include <cmd.h>
#include <net/if.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
-#include <stdlib.h>
#include <utils.h>
#include <nftables.h>
#include <linux/netfilter.h>
@@ -241,12 +242,13 @@ 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(const struct mnl_socket *nl,
- struct nftnl_batch *batch)
+static void mnl_set_sndbuffer(struct netlink_ctx *ctx)
{
+ struct mnl_socket *nl = ctx->nft->nf_sock;
+ struct nftnl_batch *batch = ctx->batch;
socklen_t len = sizeof(int);
int sndnlbuffsiz = 0;
int newbuffsiz;
@@ -259,9 +261,15 @@ static void mnl_set_sndbuffer(const struct mnl_socket *nl,
return;
/* Rise sender buffer length to avoid hitting -EMSGSIZE */
+ setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_SNDBUF,
+ &newbuffsiz, sizeof(socklen_t));
+
+ /* unpriviledged containers check for CAP_NET_ADMIN on the init_user_ns. */
if (setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_SNDBUFFORCE,
- &newbuffsiz, sizeof(socklen_t)) < 0)
- return;
+ &newbuffsiz, sizeof(socklen_t)) < 0) {
+ if (errno == EPERM)
+ ctx->maybe_emsgsize = newbuffsiz;
+ }
}
static unsigned int nlsndbufsiz;
@@ -377,7 +385,7 @@ static int mnl_batch_extack_cb(const struct nlmsghdr *nlh, void *data)
return MNL_CB_ERROR;
}
-#define NFT_MNL_ECHO_RCVBUFF_DEFAULT (MNL_SOCKET_BUFFER_SIZE * 1024)
+#define NFT_MNL_ECHO_RCVBUFF_DEFAULT (MNL_SOCKET_BUFFER_SIZE * 1024U)
#define NFT_MNL_ACK_MAXSIZE ((sizeof(struct nlmsghdr) + \
sizeof(struct nfgenmsg) + (1 << 16)) + \
MNL_SOCKET_BUFFER_SIZE)
@@ -408,7 +416,7 @@ int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list,
.nl_ctx = ctx,
};
- mnl_set_sndbuffer(ctx->nft->nf_sock, ctx->batch);
+ mnl_set_sndbuffer(ctx);
mnl_nft_batch_to_msg(ctx, &msg, &snl, iov, iov_len);
@@ -592,6 +600,7 @@ int mnl_nft_rule_replace(struct netlink_ctx *ctx, struct cmd *cmd)
int mnl_nft_rule_del(struct netlink_ctx *ctx, struct cmd *cmd)
{
+ enum nf_tables_msg_types msg_type = NFT_MSG_DELRULE;
struct handle *h = &cmd->handle;
struct nftnl_rule *nlr;
struct nlmsghdr *nlh;
@@ -602,8 +611,11 @@ int mnl_nft_rule_del(struct netlink_ctx *ctx, struct cmd *cmd)
nftnl_rule_set_u32(nlr, NFTNL_RULE_FAMILY, h->family);
+ if (cmd->op == CMD_DESTROY)
+ msg_type = NFT_MSG_DESTROYRULE;
+
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
- NFT_MSG_DELRULE,
+ msg_type,
nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY),
0, ctx->seqnum);
@@ -653,20 +665,45 @@ err_free:
return MNL_CB_OK;
}
-struct nftnl_rule_list *mnl_nft_rule_dump(struct netlink_ctx *ctx,
- int family)
+struct nftnl_rule_list *mnl_nft_rule_dump(struct netlink_ctx *ctx, int family,
+ const char *table, const char *chain,
+ uint64_t rule_handle,
+ bool dump, bool reset)
{
+ uint16_t nl_flags = dump ? NLM_F_DUMP : NLM_F_ACK;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nftnl_rule_list *nlr_list;
+ struct nftnl_rule *nlr = NULL;
struct nlmsghdr *nlh;
- int ret;
+ int msg_type, ret;
+
+ if (reset)
+ msg_type = NFT_MSG_GETRULE_RESET;
+ else
+ msg_type = NFT_MSG_GETRULE;
+
+ if (table) {
+ nlr = nftnl_rule_alloc();
+ if (!nlr)
+ memory_allocation_error();
+
+ nftnl_rule_set_str(nlr, NFTNL_RULE_TABLE, table);
+ if (chain)
+ nftnl_rule_set_str(nlr, NFTNL_RULE_CHAIN, chain);
+ if (rule_handle)
+ nftnl_rule_set_u64(nlr, NFTNL_RULE_HANDLE, rule_handle);
+ }
nlr_list = nftnl_rule_list_alloc();
if (nlr_list == NULL)
memory_allocation_error();
- nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, family,
- NLM_F_DUMP, ctx->seqnum);
+ nlh = nftnl_nlmsg_build_hdr(buf, msg_type, family,
+ nl_flags, ctx->seqnum);
+ if (nlr) {
+ nftnl_rule_nlmsg_build_payload(nlh, nlr);
+ nftnl_rule_free(nlr);
+ }
ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, rule_cb, nlr_list);
if (ret < 0)
@@ -681,18 +718,98 @@ err:
/*
* Chain
*/
+
+struct nft_dev {
+ const char *ifname;
+ const struct location *location;
+};
+
+static void nft_dev_add(struct nft_dev *dev_array, const struct expr *expr, int i)
+{
+ unsigned int ifname_len;
+ char ifname[IFNAMSIZ];
+
+ ifname_len = div_round_up(expr->len, BITS_PER_BYTE);
+ memset(ifname, 0, sizeof(ifname));
+ mpz_export_data(ifname, expr->value, BYTEORDER_HOST_ENDIAN, ifname_len);
+ dev_array[i].ifname = xstrdup(ifname);
+ dev_array[i].location = &expr->location;
+}
+
+static struct nft_dev *nft_dev_array(const struct expr *dev_expr, int *num_devs)
+{
+ struct nft_dev *dev_array;
+ int i = 0, len = 1;
+ struct expr *expr;
+
+ switch (dev_expr->etype) {
+ case EXPR_SET:
+ case EXPR_LIST:
+ list_for_each_entry(expr, &dev_expr->expressions, list)
+ len++;
+
+ dev_array = xmalloc(sizeof(struct nft_dev) * len);
+
+ list_for_each_entry(expr, &dev_expr->expressions, list) {
+ nft_dev_add(dev_array, expr, i);
+ i++;
+ }
+ break;
+ case EXPR_VALUE:
+ len++;
+ dev_array = xmalloc(sizeof(struct nft_dev) * len);
+ nft_dev_add(dev_array, dev_expr, i);
+ i++;
+ break;
+ default:
+ assert(0);
+ }
+
+ dev_array[i].ifname = NULL;
+ *num_devs = i;
+
+ return dev_array;
+}
+
+static void nft_dev_array_free(const struct nft_dev *dev_array)
+{
+ int i = 0;
+
+ while (dev_array[i].ifname != NULL)
+ free_const(dev_array[i++].ifname);
+
+ free_const(dev_array);
+}
+
+static void mnl_nft_chain_devs_build(struct nlmsghdr *nlh, struct cmd *cmd)
+{
+ const struct expr *dev_expr = cmd->chain->dev_expr;
+ const struct nft_dev *dev_array;
+ struct nlattr *nest_dev;
+ int i, num_devs = 0;
+
+ dev_array = nft_dev_array(dev_expr, &num_devs);
+ if (num_devs == 1) {
+ cmd_add_loc(cmd, nlh->nlmsg_len, dev_array[0].location);
+ mnl_attr_put_strz(nlh, NFTA_HOOK_DEV, dev_array[0].ifname);
+ } else {
+ nest_dev = mnl_attr_nest_start(nlh, NFTA_HOOK_DEVS);
+ for (i = 0; i < num_devs; i++) {
+ cmd_add_loc(cmd, nlh->nlmsg_len, dev_array[i].location);
+ mnl_attr_put_strz(nlh, NFTA_DEVICE_NAME, dev_array[i].ifname);
+ mnl_attr_nest_end(nlh, nest_dev);
+ }
+ }
+ nft_dev_array_free(dev_array);
+}
+
int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags)
{
struct nftnl_udata_buf *udbuf;
- int priority, policy, i = 0;
struct nftnl_chain *nlc;
- unsigned int ifname_len;
- const char **dev_array;
- char ifname[IFNAMSIZ];
struct nlmsghdr *nlh;
- struct expr *expr;
- int dev_array_len;
+ int priority, policy;
nlc = nftnl_chain_alloc();
if (nlc == NULL)
@@ -705,46 +822,6 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
nftnl_chain_set_u32(nlc, NFTNL_CHAIN_FLAGS,
CHAIN_F_HW_OFFLOAD);
}
- if (cmd->chain->flags & CHAIN_F_BASECHAIN) {
- nftnl_chain_set_u32(nlc, NFTNL_CHAIN_HOOKNUM,
- cmd->chain->hook.num);
- mpz_export_data(&priority,
- cmd->chain->priority.expr->value,
- BYTEORDER_HOST_ENDIAN, sizeof(int));
- nftnl_chain_set_s32(nlc, NFTNL_CHAIN_PRIO, priority);
- nftnl_chain_set_str(nlc, NFTNL_CHAIN_TYPE,
- cmd->chain->type.str);
- }
- if (cmd->chain->dev_expr) {
- dev_array = xmalloc(sizeof(char *) * 8);
- dev_array_len = 8;
- list_for_each_entry(expr, &cmd->chain->dev_expr->expressions, list) {
- ifname_len = div_round_up(expr->len, BITS_PER_BYTE);
- memset(ifname, 0, sizeof(ifname));
- mpz_export_data(ifname, expr->value,
- BYTEORDER_HOST_ENDIAN,
- ifname_len);
- dev_array[i++] = xstrdup(ifname);
- if (i == dev_array_len) {
- dev_array_len *= 2;
- dev_array = xrealloc(dev_array,
- dev_array_len * sizeof(char *));
- }
- }
-
- dev_array[i] = NULL;
- if (i == 1)
- nftnl_chain_set_str(nlc, NFTNL_CHAIN_DEV, dev_array[0]);
- else if (i > 1)
- nftnl_chain_set_data(nlc, NFTNL_CHAIN_DEVICES, dev_array,
- sizeof(char *) * dev_array_len);
-
- i = 0;
- while (dev_array[i] != NULL)
- xfree(dev_array[i++]);
-
- xfree(dev_array);
- }
if (cmd->chain->comment) {
udbuf = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
if (!udbuf)
@@ -779,12 +856,6 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
nftnl_chain_set_u32(nlc, NFTNL_CHAIN_FLAGS, cmd->chain->flags);
}
- if (cmd->chain && cmd->chain->flags & CHAIN_F_BASECHAIN) {
- nftnl_chain_unset(nlc, NFTNL_CHAIN_TYPE);
- cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->chain->type.loc);
- mnl_attr_put_strz(nlh, NFTA_CHAIN_TYPE, cmd->chain->type.str);
- }
-
if (cmd->chain && cmd->chain->policy) {
mpz_export_data(&policy, cmd->chain->policy->value,
BYTEORDER_HOST_ENDIAN, sizeof(int));
@@ -792,7 +863,33 @@ int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
mnl_attr_put_u32(nlh, NFTA_CHAIN_POLICY, htonl(policy));
}
+ nftnl_chain_unset(nlc, NFTNL_CHAIN_TYPE);
+
nftnl_chain_nlmsg_build_payload(nlh, nlc);
+
+ if (cmd->chain && cmd->chain->flags & CHAIN_F_BASECHAIN) {
+ struct nlattr *nest;
+
+ if (cmd->chain->type.str) {
+ cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->chain->type.loc);
+ mnl_attr_put_strz(nlh, NFTA_CHAIN_TYPE, cmd->chain->type.str);
+ }
+
+ nest = mnl_attr_nest_start(nlh, NFTA_CHAIN_HOOK);
+
+ if (cmd->chain->type.str) {
+ mnl_attr_put_u32(nlh, NFTA_HOOK_HOOKNUM, htonl(cmd->chain->hook.num));
+ mpz_export_data(&priority, cmd->chain->priority.expr->value,
+ BYTEORDER_HOST_ENDIAN, sizeof(int));
+ mnl_attr_put_u32(nlh, NFTA_HOOK_PRIORITY, htonl(priority));
+ }
+
+ if (cmd->chain && cmd->chain->dev_expr)
+ mnl_nft_chain_devs_build(nlh, cmd);
+
+ mnl_attr_nest_end(nlh, nest);
+ }
+
nftnl_chain_free(nlc);
mnl_nft_batch_continue(ctx->batch);
@@ -832,6 +929,7 @@ int mnl_nft_chain_rename(struct netlink_ctx *ctx, const struct cmd *cmd,
int mnl_nft_chain_del(struct netlink_ctx *ctx, struct cmd *cmd)
{
+ enum nf_tables_msg_types msg_type = NFT_MSG_DELCHAIN;
struct nftnl_chain *nlc;
struct nlmsghdr *nlh;
@@ -841,8 +939,11 @@ int mnl_nft_chain_del(struct netlink_ctx *ctx, struct cmd *cmd)
nftnl_chain_set_u32(nlc, NFTNL_CHAIN_FAMILY, cmd->handle.family);
+ if (cmd->op == CMD_DESTROY)
+ msg_type = NFT_MSG_DESTROYCHAIN;
+
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
- NFT_MSG_DELCHAIN,
+ msg_type,
cmd->handle.family,
0, ctx->seqnum);
@@ -857,6 +958,15 @@ int mnl_nft_chain_del(struct netlink_ctx *ctx, struct cmd *cmd)
htobe64(cmd->handle.handle.id));
}
+ if (cmd->op == CMD_DELETE &&
+ cmd->chain && cmd->chain->dev_expr) {
+ struct nlattr *nest;
+
+ nest = mnl_attr_nest_start(nlh, NFTA_CHAIN_HOOK);
+ mnl_nft_chain_devs_build(nlh, cmd);
+ mnl_attr_nest_end(nlh, nest);
+ }
+
nftnl_chain_nlmsg_build_payload(nlh, nlc);
nftnl_chain_free(nlc);
@@ -889,10 +999,12 @@ err_free:
}
struct nftnl_chain_list *mnl_nft_chain_dump(struct netlink_ctx *ctx,
- int family)
+ int family, const char *table,
+ const char *chain)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nftnl_chain_list *nlc_list;
+ struct nftnl_chain *nlc = NULL;
struct nlmsghdr *nlh;
int ret;
@@ -900,11 +1012,24 @@ struct nftnl_chain_list *mnl_nft_chain_dump(struct netlink_ctx *ctx,
if (nlc_list == NULL)
memory_allocation_error();
+ if (table && chain) {
+ nlc = nftnl_chain_alloc();
+ if (!nlc)
+ memory_allocation_error();
+
+ nftnl_chain_set_str(nlc, NFTNL_CHAIN_TABLE, table);
+ nftnl_chain_set_str(nlc, NFTNL_CHAIN_NAME, chain);
+ }
+
nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, family,
- NLM_F_DUMP, ctx->seqnum);
+ nlc ? NLM_F_ACK : NLM_F_DUMP, ctx->seqnum);
+ if (nlc) {
+ nftnl_chain_nlmsg_build_payload(nlh, nlc);
+ nftnl_chain_free(nlc);
+ }
ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, chain_cb, nlc_list);
- if (ret < 0)
+ if (ret < 0 && errno != ENOENT)
goto err;
return nlc_list;
@@ -962,6 +1087,7 @@ int mnl_nft_table_add(struct netlink_ctx *ctx, struct cmd *cmd,
int mnl_nft_table_del(struct netlink_ctx *ctx, struct cmd *cmd)
{
+ enum nf_tables_msg_types msg_type = NFT_MSG_DELTABLE;
struct nftnl_table *nlt;
struct nlmsghdr *nlh;
@@ -971,10 +1097,11 @@ int mnl_nft_table_del(struct netlink_ctx *ctx, struct cmd *cmd)
nftnl_table_set_u32(nlt, NFTNL_TABLE_FAMILY, cmd->handle.family);
- nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
- NFT_MSG_DELTABLE,
- cmd->handle.family,
- 0, ctx->seqnum);
+ if (cmd->op == CMD_DESTROY)
+ msg_type = NFT_MSG_DESTROYTABLE;
+
+ nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch), msg_type,
+ cmd->handle.family, 0, ctx->seqnum);
if (cmd->handle.table.name) {
cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
@@ -1016,10 +1143,12 @@ err_free:
}
struct nftnl_table_list *mnl_nft_table_dump(struct netlink_ctx *ctx,
- int family)
+ int family, const char *table)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nftnl_table_list *nlt_list;
+ struct nftnl_table *nlt = NULL;
+ int flags = NLM_F_DUMP;
struct nlmsghdr *nlh;
int ret;
@@ -1027,11 +1156,25 @@ struct nftnl_table_list *mnl_nft_table_dump(struct netlink_ctx *ctx,
if (nlt_list == NULL)
return NULL;
+ if (table) {
+ nlt = nftnl_table_alloc();
+ if (!nlt)
+ memory_allocation_error();
+
+ nftnl_table_set_u32(nlt, NFTNL_TABLE_FAMILY, family);
+ nftnl_table_set_str(nlt, NFTNL_TABLE_NAME, table);
+ flags = NLM_F_ACK;
+ }
+
nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, family,
- NLM_F_DUMP, ctx->seqnum);
+ flags, ctx->seqnum);
+ if (nlt) {
+ nftnl_table_nlmsg_build_payload(nlh, nlt);
+ nftnl_table_free(nlt);
+ }
ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, table_cb, nlt_list);
- if (ret < 0)
+ if (ret < 0 && errno != ENOENT)
goto err;
return nlt_list;
@@ -1047,9 +1190,7 @@ static void set_key_expression(struct netlink_ctx *ctx,
{
struct nftnl_udata *nest1, *nest2;
- if (expr->flags & EXPR_F_CONSTANT ||
- set_is_anonymous(set_flags) ||
- !expr_ops(expr)->build_udata)
+ if (!expr_ops(expr)->build_udata)
return;
nest1 = nftnl_udata_nest_start(udbuf, type);
@@ -1194,6 +1335,7 @@ int mnl_nft_set_add(struct netlink_ctx *ctx, struct cmd *cmd,
int mnl_nft_set_del(struct netlink_ctx *ctx, struct cmd *cmd)
{
+ enum nf_tables_msg_types msg_type = NFT_MSG_DELSET;
const struct handle *h = &cmd->handle;
struct nftnl_set *nls;
struct nlmsghdr *nlh;
@@ -1204,8 +1346,11 @@ int mnl_nft_set_del(struct netlink_ctx *ctx, struct cmd *cmd)
nftnl_set_set_u32(nls, NFTNL_SET_FAMILY, h->family);
+ if (cmd->op == CMD_DESTROY)
+ msg_type = NFT_MSG_DESTROYSET;
+
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
- NFT_MSG_DELSET,
+ msg_type,
h->family,
0, ctx->seqnum);
@@ -1252,10 +1397,12 @@ err_free:
}
struct nftnl_set_list *
-mnl_nft_set_dump(struct netlink_ctx *ctx, int family, const char *table)
+mnl_nft_set_dump(struct netlink_ctx *ctx, int family,
+ const char *table, const char *set)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nftnl_set_list *nls_list;
+ int flags = NLM_F_DUMP;
struct nlmsghdr *nlh;
struct nftnl_set *s;
int ret;
@@ -1264,10 +1411,15 @@ mnl_nft_set_dump(struct netlink_ctx *ctx, int family, const char *table)
if (s == NULL)
memory_allocation_error();
- nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETSET, family,
- NLM_F_DUMP, ctx->seqnum);
if (table != NULL)
nftnl_set_set_str(s, NFTNL_SET_TABLE, table);
+ if (set) {
+ nftnl_set_set_str(s, NFTNL_SET_NAME, set);
+ flags = NLM_F_ACK;
+ }
+
+ nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETSET, family,
+ flags, ctx->seqnum);
nftnl_set_nlmsg_build_payload(nlh, s);
nftnl_set_free(s);
@@ -1276,7 +1428,7 @@ mnl_nft_set_dump(struct netlink_ctx *ctx, int family, const char *table)
memory_allocation_error();
ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, set_cb, nls_list);
- if (ret < 0)
+ if (ret < 0 && errno != ENOENT)
goto err;
return nls_list;
@@ -1402,6 +1554,7 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, struct cmd *cmd,
int mnl_nft_obj_del(struct netlink_ctx *ctx, struct cmd *cmd, int type)
{
+ enum nf_tables_msg_types msg_type = NFT_MSG_DELOBJ;
struct nftnl_obj *nlo;
struct nlmsghdr *nlh;
@@ -1412,8 +1565,11 @@ int mnl_nft_obj_del(struct netlink_ctx *ctx, struct cmd *cmd, int type)
nftnl_obj_set_u32(nlo, NFTNL_OBJ_FAMILY, cmd->handle.family);
nftnl_obj_set_u32(nlo, NFTNL_OBJ_TYPE, type);
+ if (cmd->op == CMD_DESTROY)
+ msg_type = NFT_MSG_DESTROYOBJ;
+
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
- NFT_MSG_DELOBJ, cmd->handle.family,
+ msg_type, cmd->handle.family,
0, ctx->seqnum);
cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
@@ -1518,39 +1674,114 @@ static int set_elem_cb(const struct nlmsghdr *nlh, void *data)
return MNL_CB_OK;
}
-static int mnl_nft_setelem_batch(struct nftnl_set *nls,
+static bool mnl_nft_attr_nest_overflow(struct nlmsghdr *nlh,
+ const struct nlattr *from,
+ const struct nlattr *to)
+{
+ int len = (void *)to + to->nla_len - (void *)from;
+
+ /* The attribute length field is 16 bits long, thus the maximum payload
+ * that an attribute can convey is UINT16_MAX. In case of overflow,
+ * discard the last attribute that did not fit into the nest.
+ */
+ if (len > UINT16_MAX) {
+ nlh->nlmsg_len -= to->nla_len;
+ return true;
+ }
+ return false;
+}
+
+static void netlink_dump_setelem(const struct nftnl_set_elem *nlse,
+ struct netlink_ctx *ctx)
+{
+ FILE *fp = ctx->nft->output.output_fp;
+ char buf[4096];
+
+ if (!(ctx->nft->debug_mask & NFT_DEBUG_NETLINK) || !fp)
+ return;
+
+ nftnl_set_elem_snprintf(buf, sizeof(buf), nlse, NFTNL_OUTPUT_DEFAULT, 0);
+ fprintf(fp, "\t%s", buf);
+}
+
+static void netlink_dump_setelem_done(struct netlink_ctx *ctx)
+{
+ FILE *fp = ctx->nft->output.output_fp;
+
+ if (!(ctx->nft->debug_mask & NFT_DEBUG_NETLINK) || !fp)
+ return;
+
+ fprintf(fp, "\n");
+}
+
+static int mnl_nft_setelem_batch(const struct nftnl_set *nls, struct cmd *cmd,
struct nftnl_batch *batch,
- enum nf_tables_msg_types cmd,
- unsigned int flags, uint32_t seqnum)
+ enum nf_tables_msg_types msg_type,
+ unsigned int flags, uint32_t seqnum,
+ const struct expr *set,
+ struct netlink_ctx *ctx)
{
+ struct nlattr *nest1, *nest2;
+ struct nftnl_set_elem *nlse;
struct nlmsghdr *nlh;
- struct nftnl_set_elems_iter *iter;
- int ret;
-
- iter = nftnl_set_elems_iter_create(nls);
- if (iter == NULL)
- memory_allocation_error();
+ struct expr *expr = NULL;
+ int i = 0;
- if (cmd == NFT_MSG_NEWSETELEM)
+ if (msg_type == NFT_MSG_NEWSETELEM)
flags |= NLM_F_CREATE;
- while (nftnl_set_elems_iter_cur(iter)) {
- nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(batch), cmd,
- nftnl_set_get_u32(nls, NFTNL_SET_FAMILY),
- flags, seqnum);
- ret = nftnl_set_elems_nlmsg_build_payload_iter(nlh, iter);
- mnl_nft_batch_continue(batch);
- if (ret <= 0)
- break;
+ if (set)
+ expr = list_first_entry(&set->expressions, struct expr, list);
+
+next:
+ nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(batch), msg_type,
+ nftnl_set_get_u32(nls, NFTNL_SET_FAMILY),
+ flags, seqnum);
+
+ if (nftnl_set_is_set(nls, NFTNL_SET_TABLE)) {
+ mnl_attr_put_strz(nlh, NFTA_SET_ELEM_LIST_TABLE,
+ nftnl_set_get_str(nls, NFTNL_SET_TABLE));
}
+ if (nftnl_set_is_set(nls, NFTNL_SET_NAME)) {
+ mnl_attr_put_strz(nlh, NFTA_SET_ELEM_LIST_SET,
+ nftnl_set_get_str(nls, NFTNL_SET_NAME));
+ }
+ if (nftnl_set_is_set(nls, NFTNL_SET_ID)) {
+ mnl_attr_put_u32(nlh, NFTA_SET_ELEM_LIST_SET_ID,
+ htonl(nftnl_set_get_u32(nls, NFTNL_SET_ID)));
+ }
+
+ if (!set || list_empty(&set->expressions))
+ return 0;
- nftnl_set_elems_iter_destroy(iter);
+ assert(expr);
+ nest1 = mnl_attr_nest_start(nlh, NFTA_SET_ELEM_LIST_ELEMENTS);
+ list_for_each_entry_from(expr, &set->expressions, list) {
+ nlse = alloc_nftnl_setelem(set, expr);
+
+ cmd_add_loc(cmd, nlh->nlmsg_len, &expr->location);
+ nest2 = mnl_attr_nest_start(nlh, ++i);
+ nftnl_set_elem_nlmsg_build_payload(nlh, nlse);
+ mnl_attr_nest_end(nlh, nest2);
+
+ netlink_dump_setelem(nlse, ctx);
+ nftnl_set_elem_free(nlse);
+ if (mnl_nft_attr_nest_overflow(nlh, nest1, nest2)) {
+ mnl_attr_nest_end(nlh, nest1);
+ mnl_nft_batch_continue(batch);
+ goto next;
+ }
+ }
+ mnl_attr_nest_end(nlh, nest1);
+ mnl_nft_batch_continue(batch);
+ netlink_dump_setelem_done(ctx);
return 0;
}
-int mnl_nft_setelem_add(struct netlink_ctx *ctx, const struct set *set,
- const struct expr *expr, unsigned int flags)
+int mnl_nft_setelem_add(struct netlink_ctx *ctx, struct cmd *cmd,
+ const struct set *set, const struct expr *expr,
+ unsigned int flags)
{
const struct handle *h = &set->handle;
struct nftnl_set *nls;
@@ -1569,11 +1800,10 @@ int mnl_nft_setelem_add(struct netlink_ctx *ctx, const struct set *set,
nftnl_set_set_u32(nls, NFTNL_SET_DATA_TYPE,
dtype_map_to_kernel(set->data->dtype));
- alloc_setelem_cache(expr, nls);
netlink_dump_set(nls, ctx);
- err = mnl_nft_setelem_batch(nls, ctx->batch, NFT_MSG_NEWSETELEM, flags,
- ctx->seqnum);
+ err = mnl_nft_setelem_batch(nls, cmd, ctx->batch, NFT_MSG_NEWSETELEM,
+ flags, ctx->seqnum, expr, ctx);
nftnl_set_free(nls);
return err;
@@ -1609,9 +1839,10 @@ int mnl_nft_setelem_flush(struct netlink_ctx *ctx, const struct cmd *cmd)
return 0;
}
-int mnl_nft_setelem_del(struct netlink_ctx *ctx, const struct cmd *cmd)
+int mnl_nft_setelem_del(struct netlink_ctx *ctx, struct cmd *cmd,
+ const struct handle *h, const struct expr *init)
{
- const struct handle *h = &cmd->handle;
+ enum nf_tables_msg_types msg_type = NFT_MSG_DELSETELEM;
struct nftnl_set *nls;
int err;
@@ -1626,26 +1857,34 @@ int mnl_nft_setelem_del(struct netlink_ctx *ctx, const struct cmd *cmd)
else if (h->handle.id)
nftnl_set_set_u64(nls, NFTNL_SET_HANDLE, h->handle.id);
- if (cmd->expr)
- alloc_setelem_cache(cmd->expr, nls);
netlink_dump_set(nls, ctx);
- err = mnl_nft_setelem_batch(nls, ctx->batch, NFT_MSG_DELSETELEM, 0,
- ctx->seqnum);
+ if (cmd->op == CMD_DESTROY)
+ msg_type = NFT_MSG_DESTROYSETELEM;
+
+ err = mnl_nft_setelem_batch(nls, cmd, ctx->batch, msg_type, 0,
+ ctx->seqnum, init, ctx);
nftnl_set_free(nls);
return err;
}
struct nftnl_set *mnl_nft_setelem_get_one(struct netlink_ctx *ctx,
- struct nftnl_set *nls_in)
+ struct nftnl_set *nls_in,
+ bool reset)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nftnl_set *nls_out;
struct nlmsghdr *nlh;
+ int msg_type;
int err;
- nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETSETELEM,
+ if (reset)
+ msg_type = NFT_MSG_GETSETELEM_RESET;
+ else
+ msg_type = NFT_MSG_GETSETELEM;
+
+ nlh = nftnl_nlmsg_build_hdr(buf, msg_type,
nftnl_set_get_u32(nls_in, NFTNL_SET_FAMILY),
NLM_F_ACK, ctx->seqnum);
nftnl_set_elems_nlmsg_build_payload(nlh, nls_in);
@@ -1668,12 +1907,19 @@ struct nftnl_set *mnl_nft_setelem_get_one(struct netlink_ctx *ctx,
return nls_out;
}
-int mnl_nft_setelem_get(struct netlink_ctx *ctx, struct nftnl_set *nls)
+int mnl_nft_setelem_get(struct netlink_ctx *ctx, struct nftnl_set *nls,
+ bool reset)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
+ int msg_type;
+
+ if (reset)
+ msg_type = NFT_MSG_GETSETELEM_RESET;
+ else
+ msg_type = NFT_MSG_GETSETELEM;
- nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETSETELEM,
+ nlh = nftnl_nlmsg_build_hdr(buf, msg_type,
nftnl_set_get_u32(nls, NFTNL_SET_FAMILY),
NLM_F_DUMP, ctx->seqnum);
nftnl_set_elems_nlmsg_build_payload(nlh, nls);
@@ -1705,11 +1951,13 @@ err_free:
}
struct nftnl_flowtable_list *
-mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family, const char *table)
+mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family,
+ const char *table, const char *ft)
{
struct nftnl_flowtable_list *nln_list;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nftnl_flowtable *n;
+ int flags = NLM_F_DUMP;
struct nlmsghdr *nlh;
int ret;
@@ -1717,10 +1965,14 @@ mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family, const char *table)
if (n == NULL)
memory_allocation_error();
- nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETFLOWTABLE, family,
- NLM_F_DUMP, ctx->seqnum);
if (table != NULL)
nftnl_flowtable_set_str(n, NFTNL_FLOWTABLE_TABLE, table);
+ if (ft) {
+ nftnl_flowtable_set_str(n, NFTNL_FLOWTABLE_NAME, ft);
+ flags = NLM_F_ACK;
+ }
+ nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETFLOWTABLE, family,
+ flags, ctx->seqnum);
nftnl_flowtable_nlmsg_build_payload(nlh, n);
nftnl_flowtable_free(n);
@@ -1729,7 +1981,7 @@ mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family, const char *table)
memory_allocation_error();
ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, flowtable_cb, nln_list);
- if (ret < 0)
+ if (ret < 0 && errno != ENOENT)
goto err;
return nln_list;
@@ -1738,48 +1990,30 @@ err:
return NULL;
}
-static const char **nft_flowtable_dev_array(struct cmd *cmd)
+static void mnl_nft_ft_devs_build(struct nlmsghdr *nlh, struct cmd *cmd)
{
- unsigned int ifname_len;
- const char **dev_array;
- char ifname[IFNAMSIZ];
- int i = 0, len = 1;
- struct expr *expr;
+ const struct expr *dev_expr = cmd->flowtable->dev_expr;
+ const struct nft_dev *dev_array;
+ struct nlattr *nest_dev;
+ int i, num_devs= 0;
- list_for_each_entry(expr, &cmd->flowtable->dev_expr->expressions, list)
- len++;
-
- dev_array = xmalloc(sizeof(char *) * len);
-
- list_for_each_entry(expr, &cmd->flowtable->dev_expr->expressions, list) {
- ifname_len = div_round_up(expr->len, BITS_PER_BYTE);
- memset(ifname, 0, sizeof(ifname));
- mpz_export_data(ifname, expr->value, BYTEORDER_HOST_ENDIAN,
- ifname_len);
- dev_array[i++] = xstrdup(ifname);
+ dev_array = nft_dev_array(dev_expr, &num_devs);
+ nest_dev = mnl_attr_nest_start(nlh, NFTA_FLOWTABLE_HOOK_DEVS);
+ for (i = 0; i < num_devs; i++) {
+ cmd_add_loc(cmd, nlh->nlmsg_len, dev_array[i].location);
+ mnl_attr_put_strz(nlh, NFTA_DEVICE_NAME, dev_array[i].ifname);
}
- dev_array[i] = NULL;
-
- return dev_array;
-}
-
-static void nft_flowtable_dev_array_free(const char **dev_array)
-{
- int i = 0;
-
- while (dev_array[i] != NULL)
- xfree(dev_array[i++]);
-
- free(dev_array);
+ mnl_attr_nest_end(nlh, nest_dev);
+ nft_dev_array_free(dev_array);
}
int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags)
{
struct nftnl_flowtable *flo;
- const char **dev_array;
struct nlmsghdr *nlh;
+ struct nlattr *nest;
int priority;
flo = nftnl_flowtable_alloc();
@@ -1789,24 +2023,6 @@ int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd,
nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_FAMILY,
cmd->handle.family);
- if (cmd->flowtable->hook.name) {
- nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_HOOKNUM,
- cmd->flowtable->hook.num);
- mpz_export_data(&priority, cmd->flowtable->priority.expr->value,
- BYTEORDER_HOST_ENDIAN, sizeof(int));
- nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_PRIO, priority);
- } else {
- nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_HOOKNUM, 0);
- nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_PRIO, 0);
- }
-
- if (cmd->flowtable->dev_expr) {
- dev_array = nft_flowtable_dev_array(cmd);
- nftnl_flowtable_set_data(flo, NFTNL_FLOWTABLE_DEVICES,
- dev_array, 0);
- nft_flowtable_dev_array_free(dev_array);
- }
-
nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_FLAGS,
cmd->flowtable->flags);
@@ -1822,6 +2038,21 @@ int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd,
mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_NAME, cmd->handle.flowtable.name);
nftnl_flowtable_nlmsg_build_payload(nlh, flo);
+
+ nest = mnl_attr_nest_start(nlh, NFTA_FLOWTABLE_HOOK);
+
+ if (cmd->flowtable && cmd->flowtable->priority.expr) {
+ mnl_attr_put_u32(nlh, NFTA_FLOWTABLE_HOOK_NUM, htonl(cmd->flowtable->hook.num));
+ mpz_export_data(&priority, cmd->flowtable->priority.expr->value,
+ BYTEORDER_HOST_ENDIAN, sizeof(int));
+ mnl_attr_put_u32(nlh, NFTA_FLOWTABLE_HOOK_PRIORITY, htonl(priority));
+ }
+
+ if (cmd->flowtable->dev_expr)
+ mnl_nft_ft_devs_build(nlh, cmd);
+
+ mnl_attr_nest_end(nlh, nest);
+
nftnl_flowtable_free(flo);
mnl_nft_batch_continue(ctx->batch);
@@ -1831,9 +2062,10 @@ int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd,
int mnl_nft_flowtable_del(struct netlink_ctx *ctx, struct cmd *cmd)
{
+ enum nf_tables_msg_types msg_type = NFT_MSG_DELFLOWTABLE;
struct nftnl_flowtable *flo;
- const char **dev_array;
struct nlmsghdr *nlh;
+ struct nlattr *nest;
flo = nftnl_flowtable_alloc();
if (!flo)
@@ -1842,18 +2074,11 @@ int mnl_nft_flowtable_del(struct netlink_ctx *ctx, struct cmd *cmd)
nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_FAMILY,
cmd->handle.family);
- if (cmd->flowtable && cmd->flowtable->dev_expr) {
- nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_HOOKNUM, 0);
- nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_PRIO, 0);
-
- dev_array = nft_flowtable_dev_array(cmd);
- nftnl_flowtable_set_data(flo, NFTNL_FLOWTABLE_DEVICES,
- dev_array, 0);
- nft_flowtable_dev_array_free(dev_array);
- }
+ if (cmd->op == CMD_DESTROY)
+ msg_type = NFT_MSG_DESTROYFLOWTABLE;
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
- NFT_MSG_DELFLOWTABLE, cmd->handle.family,
+ msg_type, cmd->handle.family,
0, ctx->seqnum);
cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
@@ -1871,6 +2096,14 @@ int mnl_nft_flowtable_del(struct netlink_ctx *ctx, struct cmd *cmd)
}
nftnl_flowtable_nlmsg_build_payload(nlh, flo);
+
+ if (cmd->op == CMD_DELETE &&
+ cmd->flowtable && cmd->flowtable->dev_expr) {
+ nest = mnl_attr_nest_start(nlh, NFTA_FLOWTABLE_HOOK);
+ mnl_nft_ft_devs_build(nlh, cmd);
+ mnl_attr_nest_end(nlh, nest);
+ }
+
nftnl_flowtable_free(flo);
mnl_nft_batch_continue(ctx->batch);
@@ -1942,11 +2175,11 @@ 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);
- xfree(b);
+ free_const(b->module_name);
+ free_const(b->hookfn);
+ free_const(b->chain);
+ free_const(b->table);
+ free(b);
}
static void basehook_list_add_tail(struct basehook *b, struct list_head *head)
@@ -2054,6 +2287,27 @@ static int dump_nf_attr_chain_cb(const struct nlattr *attr, void *data)
return MNL_CB_OK;
}
+static int dump_nf_attr_bpf_cb(const struct nlattr *attr, void *data)
+{
+ int type = mnl_attr_get_type(attr);
+ const struct nlattr **tb = data;
+
+ if (mnl_attr_type_valid(attr, NFNLA_HOOK_BPF_MAX) < 0)
+ return MNL_CB_OK;
+
+ switch(type) {
+ case NFNLA_HOOK_BPF_ID:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+ return MNL_CB_ERROR;
+ break;
+ default:
+ return MNL_CB_OK;
+ }
+
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
struct dump_nf_hook_data {
struct list_head *hook_list;
int family;
@@ -2088,16 +2342,23 @@ static int dump_nf_hooks(const struct nlmsghdr *nlh, void *_data)
struct nlattr *nested[NFNLA_HOOK_INFO_MAX + 1] = {};
uint32_t type;
- if (mnl_attr_parse_nested(tb[NFNLA_HOOK_CHAIN_INFO], dump_nf_chain_info_cb, nested) < 0)
+ if (mnl_attr_parse_nested(tb[NFNLA_HOOK_CHAIN_INFO],
+ dump_nf_chain_info_cb, nested) < 0) {
+ basehook_free(hook);
return -1;
+ }
type = ntohl(mnl_attr_get_u32(nested[NFNLA_HOOK_INFO_TYPE]));
if (type == NFNL_HOOK_TYPE_NFTABLES) {
struct nlattr *info[NFNLA_CHAIN_MAX + 1] = {};
const char *tablename, *chainname;
- if (mnl_attr_parse_nested(nested[NFNLA_HOOK_INFO_DESC], dump_nf_attr_chain_cb, info) < 0)
+ if (mnl_attr_parse_nested(nested[NFNLA_HOOK_INFO_DESC],
+ dump_nf_attr_chain_cb,
+ info) < 0) {
+ basehook_free(hook);
return -1;
+ }
tablename = mnl_attr_get_str(info[NFNLA_CHAIN_TABLE]);
chainname = mnl_attr_get_str(info[NFNLA_CHAIN_NAME]);
@@ -2106,6 +2367,23 @@ static int dump_nf_hooks(const struct nlmsghdr *nlh, void *_data)
hook->chain = xstrdup(chainname);
}
hook->chain_family = mnl_attr_get_u8(info[NFNLA_CHAIN_FAMILY]);
+ } else if (type == NFNL_HOOK_TYPE_BPF) {
+ struct nlattr *info[NFNLA_HOOK_BPF_MAX + 1] = {};
+
+ if (mnl_attr_parse_nested(nested[NFNLA_HOOK_INFO_DESC],
+ dump_nf_attr_bpf_cb, info) < 0) {
+ basehook_free(hook);
+ return -1;
+ }
+
+ if (info[NFNLA_HOOK_BPF_ID]) {
+ char tmpbuf[16];
+
+ snprintf(tmpbuf, sizeof(tmpbuf), "id %u",
+ ntohl(mnl_attr_get_u32(info[NFNLA_HOOK_BPF_ID])));
+
+ hook->chain = xstrdup(tmpbuf);
+ }
}
}
if (tb[NFNLA_HOOK_HOOKNUM])
@@ -2227,6 +2505,8 @@ static void print_hooks(struct netlink_ctx *ctx, int family, struct list_head *h
if (hook->table && hook->chain)
fprintf(fp, " chain %s %s %s", family2str(hook->chain_family), hook->table, hook->chain);
+ else if (hook->hookfn && hook->chain)
+ fprintf(fp, " %s %s", hook->hookfn, hook->chain);
else if (hook->hookfn) {
fprintf(fp, " %s", hook->hookfn);
}
diff --git a/src/monitor.c b/src/monitor.c
index 8ecb7d19..2fc16d67 100644
--- a/src/monitor.c
+++ b/src/monitor.c
@@ -2,17 +2,17 @@
* Copyright (c) 2015 Arturo Borrero Gonzalez <arturo@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 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
-#include <string.h>
+#include <nft.h>
+
#include <fcntl.h>
#include <errno.h>
#include <libmnl/libmnl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
-#include <stdlib.h>
#include <inttypes.h>
#include <libnftnl/table.h>
@@ -272,10 +272,13 @@ static int netlink_events_chain_cb(const struct nlmsghdr *nlh, int type,
chain_print_plain(c, &monh->ctx->nft->output);
break;
case NFT_MSG_DELCHAIN:
- nft_mon_print(monh, "chain %s %s %s",
- family2str(c->handle.family),
- c->handle.table.name,
- c->handle.chain.name);
+ if (c->dev_array_len > 0)
+ chain_print_plain(c, &monh->ctx->nft->output);
+ else
+ nft_mon_print(monh, "chain %s %s %s",
+ family2str(c->handle.family),
+ c->handle.table.name,
+ c->handle.chain.name);
break;
}
nft_mon_print(monh, "\n");
@@ -387,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;
}
@@ -428,11 +437,13 @@ static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type,
* used by named sets, so use a dummy set.
*/
dummyset = set_alloc(monh->loc);
+ handle_merge(&dummyset->handle, &set->handle);
dummyset->key = expr_clone(set->key);
if (set->data)
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)
@@ -533,8 +544,13 @@ static int netlink_events_obj_cb(const struct nlmsghdr *nlh, int type,
static void rule_map_decompose_cb(struct set *s, void *data)
{
- if (set_is_interval(s->flags) && set_is_anonymous(s->flags))
+ if (!set_is_anonymous(s->flags))
+ return;
+
+ if (set_is_non_concat_range(s))
interval_map_decompose(s->init);
+ else if (set_is_interval(s->flags))
+ concat_range_aggregate(s->init);
}
static int netlink_events_rule_cb(const struct nlmsghdr *nlh, int type,
@@ -546,6 +562,10 @@ static int netlink_events_rule_cb(const struct nlmsghdr *nlh, int type,
nlr = netlink_rule_alloc(nlh);
r = netlink_delinearize_rule(monh->ctx, nlr);
+ if (!r) {
+ fprintf(stderr, "W: Received event for an unknown table.\n");
+ goto out_free_nlr;
+ }
nlr_for_each_set(nlr, rule_map_decompose_cb, NULL,
&monh->ctx->nft->cache);
cmd = netlink_msg2cmd(type, nlh->nlmsg_flags);
@@ -582,6 +602,7 @@ static int netlink_events_rule_cb(const struct nlmsghdr *nlh, int type,
break;
}
rule_free(r);
+out_free_nlr:
nftnl_rule_free(nlr);
return MNL_CB_OK;
}
@@ -633,6 +654,7 @@ static void netlink_events_cache_addset(struct netlink_mon_handler *monh,
memset(&set_tmpctx, 0, sizeof(set_tmpctx));
init_list_head(&set_tmpctx.list);
init_list_head(&msgs);
+ set_tmpctx.nft = monh->ctx->nft;
set_tmpctx.msgs = &msgs;
nls = netlink_set_alloc(nlh);
diff --git a/src/netlink.c b/src/netlink.c
index 28a5514a..0088b742 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -9,12 +9,12 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
-#include <string.h>
+#include <nft.h>
+
#include <errno.h>
#include <libmnl/libmnl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
-#include <stdlib.h>
#include <inttypes.h>
#include <libnftnl/table.h>
@@ -59,7 +59,7 @@ void __noreturn __netlink_abi_error(const char *file, int line,
{
fprintf(stderr, "E: Contact urgently your Linux kernel vendor. "
"Netlink ABI is broken: %s:%d %s\n", file, line, reason);
- exit(NFT_EXIT_FAILURE);
+ abort();
}
int netlink_io_error(struct netlink_ctx *ctx, const struct location *loc,
@@ -96,12 +96,13 @@ struct nftnl_expr *alloc_nft_expr(const char *name)
return nle;
}
+static void netlink_gen_key(const struct expr *expr,
+ struct nft_data_linearize *data);
+static void __netlink_gen_data(const struct expr *expr,
+ struct nft_data_linearize *data, bool expand);
-void __netlink_gen_data(const struct expr *expr,
- struct nft_data_linearize *data, bool expand);
-
-static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
- const struct expr *expr)
+struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
+ const struct expr *expr)
{
const struct expr *elem, *data;
struct nftnl_set_elem *nlse;
@@ -133,15 +134,23 @@ static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
case EXPR_SET_ELEM_CATCHALL:
break;
default:
- __netlink_gen_data(key, &nld, false);
- nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY, &nld.value, nld.len);
- if (set->set_flags & NFT_SET_INTERVAL && key->field_count > 1) {
+ if (set->set_flags & NFT_SET_INTERVAL &&
+ key->etype == EXPR_CONCAT && key->field_count > 1) {
+ key->flags |= EXPR_F_INTERVAL;
+ netlink_gen_key(key, &nld);
+ key->flags &= ~EXPR_F_INTERVAL;
+
+ nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY, &nld.value, nld.len);
+
key->flags |= EXPR_F_INTERVAL_END;
- __netlink_gen_data(key, &nld, false);
+ netlink_gen_key(key, &nld);
key->flags &= ~EXPR_F_INTERVAL_END;
nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY_END,
&nld.value, nld.len);
+ } else {
+ netlink_gen_key(key, &nld);
+ nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY, &nld.value, nld.len);
}
break;
}
@@ -245,19 +254,39 @@ static int netlink_export_pad(unsigned char *data, const mpz_t v,
return netlink_padded_len(i->len) / BITS_PER_BYTE;
}
-static int netlink_gen_concat_data_expr(int end, const struct expr *i,
- unsigned char *data)
+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)
+{
+ struct expr *expr;
+
switch (i->etype) {
case EXPR_RANGE:
- i = end ? i->right : i->left;
+ if (flags & EXPR_F_INTERVAL_END)
+ expr = i->right;
+ else
+ expr = i->left;
+
+ if (expr_basetype(expr)->type == TYPE_INTEGER &&
+ expr->byteorder == BYTEORDER_HOST_ENDIAN)
+ byteorder_switch_expr_value(expr->value, expr);
+
+ i = expr;
break;
case EXPR_PREFIX:
- if (end) {
+ if (flags & EXPR_F_INTERVAL_END) {
int count;
mpz_t v;
mpz_init_bitmask(v, i->len - i->prefix_len);
+
+ if (i->byteorder == BYTEORDER_HOST_ENDIAN)
+ byteorder_switch_expr_value(v, i);
+
mpz_add(v, i->prefix->value, v);
count = netlink_export_pad(data, v, i);
mpz_clear(v);
@@ -265,6 +294,16 @@ static int netlink_gen_concat_data_expr(int end, const struct expr *i,
}
return netlink_export_pad(data, i->prefix->value, i);
case EXPR_VALUE:
+ /* Switch byteorder only once for singleton values when the set
+ * contains concatenation of intervals.
+ */
+ if (!(flags & EXPR_F_INTERVAL))
+ break;
+
+ expr = (struct expr *)i;
+ if (expr_basetype(expr)->type == TYPE_INTEGER &&
+ expr->byteorder == BYTEORDER_HOST_ENDIAN)
+ byteorder_switch_expr_value(expr->value, expr);
break;
default:
BUG("invalid expression type '%s' in set", expr_ops(i)->name);
@@ -273,21 +312,57 @@ static int netlink_gen_concat_data_expr(int end, const struct expr *i,
return netlink_export_pad(data, i->value, i);
}
-static void __netlink_gen_concat(const struct expr *expr,
- struct nft_data_linearize *nld)
+static void nft_data_memcpy(struct nft_data_linearize *nld,
+ const void *src, unsigned int len)
+{
+ if (len > sizeof(nld->value))
+ BUG("nld buffer overflow: want to copy %u, max %u\n", len, (unsigned int)sizeof(nld->value));
+
+ memcpy(nld->value, src, len);
+ nld->len = len;
+}
+
+static void netlink_gen_concat_key(const struct expr *expr,
+ struct nft_data_linearize *nld)
{
unsigned int len = expr->len / BITS_PER_BYTE, offset = 0;
- int end = expr->flags & EXPR_F_INTERVAL_END;
unsigned char data[len];
const struct expr *i;
memset(data, 0, len);
list_for_each_entry(i, &expr->expressions, list)
- offset += netlink_gen_concat_data_expr(end, i, data + offset);
+ offset += __netlink_gen_concat_key(expr->flags, i, data + offset);
- memcpy(nld->value, data, len);
- nld->len = len;
+ nft_data_memcpy(nld, data, len);
+}
+
+static int __netlink_gen_concat_data(int end, const struct expr *i,
+ unsigned char *data)
+{
+ switch (i->etype) {
+ case EXPR_RANGE:
+ i = end ? i->right : i->left;
+ break;
+ case EXPR_PREFIX:
+ if (end) {
+ int count;
+ mpz_t v;
+
+ mpz_init_bitmask(v, i->len - i->prefix_len);
+ mpz_add(v, i->prefix->value, v);
+ count = netlink_export_pad(data, v, i);
+ mpz_clear(v);
+ return count;
+ }
+ return netlink_export_pad(data, i->prefix->value, i);
+ case EXPR_VALUE:
+ break;
+ default:
+ BUG("invalid expression type '%s' in set", expr_ops(i)->name);
+ }
+
+ return netlink_export_pad(data, i->value, i);
}
static void __netlink_gen_concat_expand(const struct expr *expr,
@@ -300,18 +375,31 @@ static void __netlink_gen_concat_expand(const struct expr *expr,
memset(data, 0, len);
list_for_each_entry(i, &expr->expressions, list)
- offset += netlink_gen_concat_data_expr(false, i, data + offset);
+ offset += __netlink_gen_concat_data(false, i, data + offset);
list_for_each_entry(i, &expr->expressions, list)
- offset += netlink_gen_concat_data_expr(true, i, data + offset);
+ offset += __netlink_gen_concat_data(true, i, data + offset);
- memcpy(nld->value, data, len);
- nld->len = len;
+ nft_data_memcpy(nld, data, len);
+}
+
+static void __netlink_gen_concat(const struct expr *expr,
+ struct nft_data_linearize *nld)
+{
+ unsigned int len = expr->len / BITS_PER_BYTE, offset = 0;
+ unsigned char data[len];
+ const struct expr *i;
+
+ memset(data, 0, len);
+
+ list_for_each_entry(i, &expr->expressions, list)
+ offset += __netlink_gen_concat_data(expr->flags, i, data + offset);
+
+ nft_data_memcpy(nld, data, len);
}
static void netlink_gen_concat_data(const struct expr *expr,
- struct nft_data_linearize *nld,
- bool expand)
+ struct nft_data_linearize *nld, bool expand)
{
if (expand)
__netlink_gen_concat_expand(expr, nld);
@@ -376,30 +464,48 @@ static void netlink_gen_range(const struct expr *expr,
memset(data, 0, len);
offset = netlink_export_pad(data, expr->left->value, expr->left);
netlink_export_pad(data + offset, expr->right->value, expr->right);
- memcpy(nld->value, data, len);
- nld->len = len;
+ nft_data_memcpy(nld, data, len);
}
static void netlink_gen_prefix(const struct expr *expr,
struct nft_data_linearize *nld)
{
- unsigned int len = div_round_up(expr->len, BITS_PER_BYTE) * 2;
- unsigned char data[len];
+ unsigned int len = (netlink_padded_len(expr->len) / BITS_PER_BYTE) * 2;
+ unsigned char data[NFT_MAX_EXPR_LEN_BYTES];
int offset;
mpz_t v;
+ if (len > sizeof(data))
+ BUG("Value export of %u bytes would overflow", len);
+
offset = netlink_export_pad(data, expr->prefix->value, expr);
mpz_init_bitmask(v, expr->len - expr->prefix_len);
mpz_add(v, expr->prefix->value, v);
netlink_export_pad(data + offset, v, expr->prefix);
mpz_clear(v);
- memcpy(nld->value, data, len);
- nld->len = len;
+ nft_data_memcpy(nld, data, len);
}
-void __netlink_gen_data(const struct expr *expr,
- struct nft_data_linearize *data, bool expand)
+static void netlink_gen_key(const struct expr *expr,
+ struct nft_data_linearize *data)
+{
+ switch (expr->etype) {
+ case EXPR_VALUE:
+ return netlink_gen_constant_data(expr, data);
+ case EXPR_CONCAT:
+ return netlink_gen_concat_key(expr, data);
+ case EXPR_RANGE:
+ return netlink_gen_range(expr, data);
+ case EXPR_PREFIX:
+ return netlink_gen_prefix(expr, data);
+ default:
+ BUG("invalid data expression type %s\n", expr_name(expr));
+ }
+}
+
+static void __netlink_gen_data(const struct expr *expr,
+ struct nft_data_linearize *data, bool expand)
{
switch (expr->etype) {
case EXPR_VALUE:
@@ -483,48 +589,6 @@ void netlink_dump_expr(const struct nftnl_expr *nle,
fprintf(fp, "\n");
}
-static int list_rule_cb(struct nftnl_rule *nlr, void *arg)
-{
- struct netlink_ctx *ctx = arg;
- const struct handle *h = ctx->data;
- struct rule *rule;
- const char *table, *chain;
- uint32_t family;
-
- family = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY);
- table = nftnl_rule_get_str(nlr, NFTNL_RULE_TABLE);
- chain = nftnl_rule_get_str(nlr, NFTNL_RULE_CHAIN);
-
- if (h->family != family ||
- strcmp(table, h->table.name) != 0 ||
- (h->chain.name && strcmp(chain, h->chain.name) != 0))
- return 0;
-
- netlink_dump_rule(nlr, ctx);
- rule = netlink_delinearize_rule(ctx, nlr);
- list_add_tail(&rule->list, &ctx->list);
-
- return 0;
-}
-
-int netlink_list_rules(struct netlink_ctx *ctx, const struct handle *h)
-{
- struct nftnl_rule_list *rule_cache;
-
- rule_cache = mnl_nft_rule_dump(ctx, h->family);
- if (rule_cache == NULL) {
- if (errno == EINTR)
- return -1;
-
- return 0;
- }
-
- ctx->data = h;
- nftnl_rule_list_foreach(rule_cache, list_rule_cb, ctx);
- nftnl_rule_list_free(rule_cache);
- return 0;
-}
-
void netlink_dump_chain(const struct nftnl_chain *nlc, struct netlink_ctx *ctx)
{
FILE *fp = ctx->nft->output.output_fp;
@@ -566,18 +630,20 @@ 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;
const char *udata;
uint32_t ulen;
- chain = chain_alloc(nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME));
+ chain = chain_alloc();
chain->handle.family =
nftnl_chain_get_u32(nlc, NFTNL_CHAIN_FAMILY);
chain->handle.table.name =
xstrdup(nftnl_chain_get_str(nlc, NFTNL_CHAIN_TABLE));
+ chain->handle.chain.name =
+ xstrdup(nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME));
chain->handle.handle.id =
nftnl_chain_get_u64(nlc, NFTNL_CHAIN_HANDLE);
if (nftnl_chain_is_set(nlc, NFTNL_CHAIN_FLAGS))
@@ -706,11 +772,19 @@ static int list_table_cb(struct nftnl_table *nlt, void *arg)
return 0;
}
-int netlink_list_tables(struct netlink_ctx *ctx, const struct handle *h)
+int netlink_list_tables(struct netlink_ctx *ctx, const struct handle *h,
+ const struct nft_cache_filter *filter)
{
struct nftnl_table_list *table_cache;
+ uint32_t family = h->family;
+ const char *table = NULL;
- table_cache = mnl_nft_table_dump(ctx, h->family);
+ if (filter) {
+ family = filter->list.family;
+ table = filter->list.table;
+ }
+
+ table_cache = mnl_nft_table_dump(ctx, family, table);
if (table_cache == NULL) {
if (errno == EINTR)
return -1;
@@ -736,13 +810,17 @@ enum nft_data_types dtype_map_to_kernel(const struct datatype *dtype)
static const struct datatype *dtype_map_from_kernel(enum nft_data_types type)
{
+ /* The function always returns ownership of a reference. But for
+ * &verdict_Type and datatype_lookup(), those are static instances,
+ * we can omit the datatype_get() call.
+ */
switch (type) {
case NFT_DATA_VERDICT:
return &verdict_type;
default:
if (type & ~TYPE_MASK)
return concat_type_alloc(type);
- return datatype_lookup(type);
+ return datatype_lookup((enum datatypes) type);
}
}
@@ -812,8 +890,8 @@ static struct expr *set_make_key(const struct nftnl_udata *attr)
{
const struct nftnl_udata *ud[NFTNL_UDATA_SET_TYPEOF_MAX + 1] = {};
const struct expr_ops *ops;
- enum expr_types etype;
struct expr *expr;
+ uint32_t etype;
int err;
if (!attr)
@@ -829,7 +907,9 @@ static struct expr *set_make_key(const struct nftnl_udata *attr)
return NULL;
etype = nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_TYPEOF_EXPR]);
- ops = expr_ops_by_type(etype);
+ ops = expr_ops_by_type_u32(etype);
+ if (!ops)
+ return NULL;
expr = ops->parse_udata(ud[NFTNL_UDATA_SET_TYPEOF_DATA]);
if (!expr)
@@ -838,7 +918,7 @@ static struct expr *set_make_key(const struct nftnl_udata *attr)
return expr;
}
-static bool set_udata_key_valid(const struct expr *e, const struct datatype *d, uint32_t len)
+static bool set_udata_key_valid(const struct expr *e, uint32_t len)
{
if (!e)
return false;
@@ -871,21 +951,21 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
const struct nftnl_udata *ud[NFTNL_UDATA_SET_MAX + 1] = {};
enum byteorder keybyteorder = BYTEORDER_INVALID;
enum byteorder databyteorder = BYTEORDER_INVALID;
- const struct datatype *keytype, *datatype = NULL;
- struct expr *typeof_expr_key, *typeof_expr_data;
struct setelem_parse_ctx set_parse_ctx;
+ const struct datatype *datatype = NULL;
+ const struct datatype *keytype = NULL;
+ const struct datatype *dtype2 = NULL;
+ const struct datatype *dtype = NULL;
+ struct expr *typeof_expr_data = NULL;
+ struct expr *typeof_expr_key = NULL;
const char *udata, *comment = NULL;
uint32_t flags, key, objtype = 0;
- const struct datatype *dtype;
uint32_t data_interval = 0;
bool automerge = false;
struct set *set;
uint32_t ulen;
uint32_t klen;
- typeof_expr_key = NULL;
- typeof_expr_data = NULL;
-
if (nftnl_set_is_set(nls, NFTNL_SET_USERDATA)) {
udata = nftnl_set_get_data(nls, NFTNL_SET_USERDATA, &ulen);
if (nftnl_udata_parse(udata, ulen, set_parse_udata_cb, ud) < 0) {
@@ -928,8 +1008,8 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
netlink_io_error(ctx, NULL,
"Unknown data type in set key %u",
data);
- datatype_free(keytype);
- return NULL;
+ set = NULL;
+ goto out;
}
}
@@ -964,17 +1044,28 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
}
list_splice_tail(&set_parse_ctx.stmt_list, &set->stmt_list);
+ set->flags = nftnl_set_get_u32(nls, NFTNL_SET_FLAGS);
+
if (datatype) {
- dtype = set_datatype_alloc(datatype, databyteorder);
+ uint32_t dlen;
+
+ dtype2 = set_datatype_alloc(datatype, databyteorder);
klen = nftnl_set_get_u32(nls, NFTNL_SET_DATA_LEN) * BITS_PER_BYTE;
- if (set_udata_key_valid(typeof_expr_data, dtype, klen)) {
- datatype_free(datatype_get(dtype));
+ dlen = data_interval ? klen / 2 : klen;
+
+ if (set_udata_key_valid(typeof_expr_data, dlen)) {
+ typeof_expr_data->len = klen;
set->data = typeof_expr_data;
+ typeof_expr_data = NULL;
+ } else if (set->flags & NFT_SET_OBJECT) {
+ set->data = constant_expr_alloc(&netlink_location,
+ dtype2,
+ databyteorder, klen,
+ NULL);
} else {
- expr_free(typeof_expr_data);
set->data = constant_expr_alloc(&netlink_location,
- dtype,
+ dtype2,
databyteorder, klen,
NULL);
@@ -985,29 +1076,21 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
if (data_interval)
set->data->flags |= EXPR_F_INTERVAL;
-
- if (dtype != datatype)
- datatype_free(datatype);
}
dtype = set_datatype_alloc(keytype, keybyteorder);
klen = nftnl_set_get_u32(nls, NFTNL_SET_KEY_LEN) * BITS_PER_BYTE;
- if (set_udata_key_valid(typeof_expr_key, dtype, klen)) {
- datatype_free(datatype_get(dtype));
+ if (set_udata_key_valid(typeof_expr_key, klen)) {
set->key = typeof_expr_key;
+ typeof_expr_key = NULL;
set->key_typeof_valid = true;
} else {
- expr_free(typeof_expr_key);
set->key = constant_expr_alloc(&netlink_location, dtype,
keybyteorder, klen,
NULL);
}
- if (dtype != keytype)
- datatype_free(keytype);
-
- set->flags = nftnl_set_get_u32(nls, NFTNL_SET_FLAGS);
set->handle.handle.id = nftnl_set_get_u64(nls, NFTNL_SET_HANDLE);
set->objtype = objtype;
@@ -1034,6 +1117,13 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
}
}
+out:
+ expr_free(typeof_expr_data);
+ expr_free(typeof_expr_key);
+ datatype_free(datatype);
+ datatype_free(keytype);
+ datatype_free(dtype2);
+ datatype_free(dtype);
return set;
}
@@ -1138,26 +1228,40 @@ static struct expr *netlink_parse_interval_elem(const struct set *set,
return range_expr_to_prefix(range);
}
-static struct expr *concat_elem_expr(struct expr *expr,
+static struct expr *concat_elem_expr(const struct set *set, struct expr *key,
const struct datatype *dtype,
struct expr *data, int *off)
{
const struct datatype *subtype;
+ unsigned int sub_length;
+ struct expr *expr;
- subtype = concat_subtype_lookup(dtype->type, --(*off));
+ if (key) {
+ (*off)--;
+ sub_length = round_up(key->len, BITS_PER_BYTE);
- expr = constant_expr_splice(data, subtype->size);
- expr->dtype = subtype;
- expr->byteorder = subtype->byteorder;
+ expr = constant_expr_splice(data, sub_length);
+ expr->dtype = datatype_get(key->dtype);
+ expr->byteorder = key->byteorder;
+ expr->len = key->len;
+ } else {
+ subtype = concat_subtype_lookup(dtype->type, --(*off));
+ sub_length = round_up(subtype->size, BITS_PER_BYTE);
+ expr = constant_expr_splice(data, sub_length);
+ expr->dtype = subtype;
+ expr->byteorder = subtype->byteorder;
+ }
- if (expr->byteorder == BYTEORDER_HOST_ENDIAN)
+ if (expr_basetype(expr)->type == TYPE_STRING ||
+ (!(set->flags & NFT_SET_INTERVAL) &&
+ expr->byteorder == BYTEORDER_HOST_ENDIAN))
mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE);
if (expr->dtype->basetype != NULL &&
expr->dtype->basetype->type == TYPE_BITMASK)
expr = bitmask_expr_to_binops(expr);
- data->len -= netlink_padding_len(expr->len);
+ data->len -= netlink_padding_len(sub_length);
return expr;
}
@@ -1166,13 +1270,18 @@ static struct expr *netlink_parse_concat_elem_key(const struct set *set,
struct expr *data)
{
const struct datatype *dtype = set->key->dtype;
- struct expr *concat, *expr;
+ struct expr *concat, *expr, *n = NULL;
int off = dtype->subtypes;
+ if (set->key->etype == EXPR_CONCAT)
+ n = list_first_entry(&set->key->expressions, struct expr, list);
+
concat = concat_expr_alloc(&data->location);
while (off > 0) {
- expr = concat_elem_expr(expr, dtype, data, &off);
+ expr = concat_elem_expr(set, n, dtype, data, &off);
compound_expr_add(concat, expr);
+ if (set->key->etype == EXPR_CONCAT)
+ n = list_next_entry(n, list);
}
expr_free(data);
@@ -1192,7 +1301,7 @@ static struct expr *netlink_parse_concat_elem(const struct set *set,
concat = concat_expr_alloc(&data->location);
while (off > 0) {
- expr = concat_elem_expr(expr, dtype, data, &off);
+ expr = concat_elem_expr(set, NULL, dtype, data, &off);
list_add_tail(&expr->list, &expressions);
}
@@ -1204,7 +1313,7 @@ static struct expr *netlink_parse_concat_elem(const struct set *set,
while (off > 0) {
left = list_first_entry(&expressions, struct expr, list);
- expr = concat_elem_expr(expr, dtype, data, &off);
+ expr = concat_elem_expr(set, NULL, dtype, data, &off);
list_del(&left->list);
range = range_expr_alloc(&data->location, left, expr);
@@ -1306,6 +1415,7 @@ key_end:
}
expr = set_elem_expr_alloc(&netlink_location, key);
+ expr->flags |= EXPR_F_KERNEL;
if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_TIMEOUT))
expr->timeout = nftnl_set_elem_get_u64(nlse, NFTNL_SET_ELEM_TIMEOUT);
@@ -1431,7 +1541,7 @@ static int list_setelements(struct nftnl_set *s, struct netlink_ctx *ctx)
}
int netlink_list_setelems(struct netlink_ctx *ctx, const struct handle *h,
- struct set *set)
+ struct set *set, bool reset)
{
struct nftnl_set *nls;
int err;
@@ -1446,7 +1556,7 @@ int netlink_list_setelems(struct netlink_ctx *ctx, const struct handle *h,
if (h->handle.id)
nftnl_set_set_u64(nls, NFTNL_SET_HANDLE, h->handle.id);
- err = mnl_nft_setelem_get(ctx, nls);
+ err = mnl_nft_setelem_get(ctx, nls, reset);
if (err < 0) {
nftnl_set_free(nls);
if (errno == EINTR)
@@ -1474,7 +1584,7 @@ int netlink_list_setelems(struct netlink_ctx *ctx, const struct handle *h,
int netlink_get_setelem(struct netlink_ctx *ctx, const struct handle *h,
const struct location *loc, struct set *cache_set,
- struct set *set, struct expr *init)
+ struct set *set, struct expr *init, bool reset)
{
struct nftnl_set *nls, *nls_out = NULL;
int err = 0;
@@ -1493,7 +1603,7 @@ int netlink_get_setelem(struct netlink_ctx *ctx, const struct handle *h,
netlink_dump_set(nls, ctx);
- nls_out = mnl_nft_setelem_get_one(ctx, nls);
+ nls_out = mnl_nft_setelem_get_one(ctx, nls, reset);
if (!nls_out) {
nftnl_set_free(nls);
return -1;
@@ -1688,6 +1798,55 @@ int netlink_reset_objs(struct netlink_ctx *ctx, const struct cmd *cmd,
return err;
}
+int netlink_reset_rules(struct netlink_ctx *ctx, const struct cmd *cmd,
+ bool dump)
+{
+ const struct handle *h = &cmd->handle;
+ struct nft_cache_filter f = {
+ .list.table = h->table.name,
+ .list.chain = h->chain.name,
+ .list.rule_handle = h->handle.id,
+ };
+ struct rule *rule, *next, *crule, *cnext;
+ struct table *table;
+ struct chain *chain;
+ int ret;
+
+ ret = rule_cache_dump(ctx, h, &f, dump, true);
+
+ list_for_each_entry_safe(rule, next, &ctx->list, list) {
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ rule->handle.table.name,
+ rule->handle.family);
+ if (!table)
+ continue;
+
+ chain = chain_cache_find(table, rule->handle.chain.name);
+ if (!chain)
+ continue;
+
+ list_del(&rule->list);
+ list_for_each_entry_safe(crule, cnext, &chain->rules, list) {
+ if (crule->handle.handle.id != rule->handle.handle.id)
+ continue;
+
+ list_replace(&crule->list, &rule->list);
+ rule_free(crule);
+ rule = NULL;
+ break;
+ }
+ if (rule) {
+ list_add_tail(&rule->list, &chain->rules);
+ }
+ }
+ list_for_each_entry_safe(rule, next, &ctx->list, list) {
+ list_del(&rule->list);
+ rule_free(rule);
+ }
+
+ return ret;
+}
+
struct flowtable *
netlink_delinearize_flowtable(struct netlink_ctx *ctx,
struct nftnl_flowtable *nlo)
@@ -1711,7 +1870,8 @@ netlink_delinearize_flowtable(struct netlink_ctx *ctx,
while (dev_array[len])
len++;
- flowtable->dev_array = calloc(1, len * sizeof(char *));
+ if (len)
+ flowtable->dev_array = xmalloc(len * sizeof(char *));
for (i = 0; i < len; i++)
flowtable->dev_array[i] = xstrdup(dev_array[i]);
@@ -1755,7 +1915,8 @@ int netlink_list_flowtables(struct netlink_ctx *ctx, const struct handle *h)
struct nftnl_flowtable_list *flowtable_cache;
int err;
- flowtable_cache = mnl_nft_flowtable_dump(ctx, h->family, h->table.name);
+ flowtable_cache = mnl_nft_flowtable_dump(ctx, h->family,
+ h->table.name, NULL);
if (flowtable_cache == NULL) {
if (errno == EINTR)
return -1;
@@ -1901,7 +2062,6 @@ static void trace_gen_stmts(struct list_head *stmts,
const void *hdr;
uint32_t hlen;
unsigned int n;
- bool stacked;
if (!nftnl_trace_is_set(nlt, attr))
return;
@@ -1956,6 +2116,8 @@ restart:
n = 0;
next:
list_for_each_entry(stmt, &unordered, list) {
+ enum proto_bases b = base;
+
rel = stmt->expr;
lhs = rel->left;
@@ -1968,18 +2130,14 @@ next:
list_move_tail(&stmt->list, stmts);
n++;
- stacked = payload_is_stacked(desc, rel);
+ if (payload_is_stacked(desc, rel))
+ b--;
- if (lhs->flags & EXPR_F_PROTOCOL &&
- pctx->pbase == PROTO_BASE_INVALID) {
- payload_dependency_store(pctx, stmt, base - stacked);
- } else {
- /* Don't strip 'icmp type' from payload dump. */
- if (pctx->icmp_type == 0)
- payload_dependency_kill(pctx, lhs, ctx->family);
- if (lhs->flags & EXPR_F_PROTOCOL)
- payload_dependency_store(pctx, stmt, base - stacked);
- }
+ /* Don't strip 'icmp type' from payload dump. */
+ if (pctx->icmp_type == 0)
+ payload_dependency_kill(pctx, lhs, ctx->family);
+ if (lhs->flags & EXPR_F_PROTOCOL)
+ payload_dependency_store(pctx, stmt, b);
goto next;
}
@@ -2008,7 +2166,7 @@ static void trace_print_packet(const struct nftnl_trace *nlt,
meta_expr_alloc(&netlink_location,
NFT_META_OIF), octx);
- proto_ctx_init(&ctx, nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY), 0);
+ proto_ctx_init(&ctx, nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY), 0, false);
ll_desc = ctx.protocol[PROTO_BASE_LL_HDR].desc;
if ((ll_desc == &proto_inet || ll_desc == &proto_netdev) &&
nftnl_trace_is_set(nlt, NFTNL_TRACE_NFPROTO)) {
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 0c2b439e..da9f7a91 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -9,9 +9,8 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
-#include <stdlib.h>
-#include <stdbool.h>
-#include <string.h>
+#include <nft.h>
+
#include <limits.h>
#include <linux/netfilter/nf_tables.h>
#include <arpa/inet.h>
@@ -30,6 +29,16 @@
#include <cache.h>
#include <xt.h>
+struct dl_proto_ctx *dl_proto_ctx(struct rule_pp_ctx *ctx)
+{
+ return ctx->dl;
+}
+
+static struct dl_proto_ctx *dl_proto_ctx_outer(struct rule_pp_ctx *ctx)
+{
+ return &ctx->_dl[0];
+}
+
static int netlink_parse_expr(const struct nftnl_expr *nle,
struct netlink_parse_ctx *ctx);
@@ -72,8 +81,7 @@ static void netlink_set_register(struct netlink_parse_ctx *ctx,
return;
}
- if (ctx->registers[reg] != NULL)
- expr_free(ctx->registers[reg]);
+ expr_free(ctx->registers[reg]);
ctx->registers[reg] = expr;
}
@@ -100,7 +108,7 @@ static void netlink_release_registers(struct netlink_parse_ctx *ctx)
{
int i;
- for (i = 0; i < MAX_REGS; i++)
+ for (i = 0; i <= MAX_REGS; i++)
expr_free(ctx->registers[i]);
}
@@ -200,6 +208,7 @@ static struct expr *netlink_parse_concat_data(struct netlink_parse_ctx *ctx,
len -= netlink_padded_len(expr->len);
reg += netlink_register_space(expr->len);
+ expr_free(expr);
}
return concat;
@@ -218,6 +227,13 @@ static void netlink_parse_chain_verdict(struct netlink_parse_ctx *ctx,
expr_chain_export(expr->chain, chain_name);
chain = chain_binding_lookup(ctx->table, chain_name);
+
+ /* Special case: 'nft list chain x y' needs to pull in implicit chains */
+ if (!chain && !strncmp(chain_name, "__chain", strlen("__chain"))) {
+ nft_chain_cache_update(ctx->nlctx, ctx->table, chain_name);
+ chain = chain_binding_lookup(ctx->table, chain_name);
+ }
+
if (chain) {
ctx->stmt = chain_stmt_alloc(loc, chain, verdict);
expr_free(expr);
@@ -471,7 +487,7 @@ static struct expr *netlink_parse_bitwise_bool(struct netlink_parse_ctx *ctx,
mpz_ior(m, m, o);
}
- if (left->len > 0 && mpz_scan0(m, 0) == left->len) {
+ if (left->len > 0 && mpz_scan0(m, 0) >= left->len) {
/* mask encompasses the entire value */
expr_free(mask);
} else {
@@ -519,7 +535,7 @@ static struct expr *netlink_parse_bitwise_shift(struct netlink_parse_ctx *ctx,
right->byteorder = BYTEORDER_HOST_ENDIAN;
expr = binop_expr_alloc(loc, op, left, right);
- expr->len = left->len;
+ expr->len = nftnl_expr_get_u32(nle, NFTNL_EXPR_BITWISE_LEN) * BITS_PER_BYTE;
return expr;
}
@@ -605,6 +621,10 @@ static void netlink_parse_payload_expr(struct netlink_parse_ctx *ctx,
struct expr *expr;
base = nftnl_expr_get_u32(nle, NFTNL_EXPR_PAYLOAD_BASE) + 1;
+
+ if (base == NFT_PAYLOAD_TUN_HEADER + 1)
+ base = NFT_PAYLOAD_INNER_HEADER + 1;
+
offset = nftnl_expr_get_u32(nle, NFTNL_EXPR_PAYLOAD_OFFSET) * BITS_PER_BYTE;
len = nftnl_expr_get_u32(nle, NFTNL_EXPR_PAYLOAD_LEN) * BITS_PER_BYTE;
@@ -612,9 +632,80 @@ static void netlink_parse_payload_expr(struct netlink_parse_ctx *ctx,
payload_init_raw(expr, base, offset, len);
dreg = netlink_parse_register(nle, NFTNL_EXPR_PAYLOAD_DREG);
+
+ if (ctx->inner)
+ ctx->inner_reg = dreg;
+
netlink_set_register(ctx, dreg, expr);
}
+static void netlink_parse_inner(struct netlink_parse_ctx *ctx,
+ const struct location *loc,
+ const struct nftnl_expr *nle)
+{
+ const struct proto_desc *inner_desc;
+ const struct nftnl_expr *inner_nle;
+ uint32_t hdrsize, flags, type;
+ struct expr *expr;
+
+ hdrsize = nftnl_expr_get_u32(nle, NFTNL_EXPR_INNER_HDRSIZE);
+ type = nftnl_expr_get_u32(nle, NFTNL_EXPR_INNER_TYPE);
+ flags = nftnl_expr_get_u32(nle, NFTNL_EXPR_INNER_FLAGS);
+
+ inner_nle = nftnl_expr_get(nle, NFTNL_EXPR_INNER_EXPR, NULL);
+ if (!inner_nle) {
+ netlink_error(ctx, loc, "Could not parse inner expression");
+ return;
+ }
+
+ inner_desc = proto_find_inner(type, hdrsize, flags);
+
+ ctx->inner = true;
+ if (netlink_parse_expr(inner_nle, ctx) < 0) {
+ ctx->inner = false;
+ return;
+ }
+ ctx->inner = false;
+
+ expr = netlink_get_register(ctx, loc, ctx->inner_reg);
+ assert(expr);
+
+ if (expr->etype == EXPR_PAYLOAD &&
+ expr->payload.base == PROTO_BASE_INNER_HDR) {
+ const struct proto_hdr_template *tmpl;
+ unsigned int i;
+
+ for (i = 1; i < array_size(inner_desc->templates); i++) {
+ tmpl = &inner_desc->templates[i];
+
+ if (tmpl->len == 0)
+ return;
+
+ if (tmpl->offset != expr->payload.offset ||
+ tmpl->len != expr->len)
+ continue;
+
+ expr->payload.desc = inner_desc;
+ expr->payload.tmpl = tmpl;
+ break;
+ }
+ }
+
+ switch (expr->etype) {
+ case EXPR_PAYLOAD:
+ expr->payload.inner_desc = inner_desc;
+ break;
+ case EXPR_META:
+ expr->meta.inner_desc = inner_desc;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ netlink_set_register(ctx, ctx->inner_reg, expr);
+}
+
static void netlink_parse_payload_stmt(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
@@ -690,6 +781,10 @@ static void netlink_parse_exthdr(struct netlink_parse_ctx *ctx,
stmt = exthdr_stmt_alloc(loc, expr, val);
rule_stmt_append(ctx->rule, stmt);
+ } else {
+ struct stmt *stmt = optstrip_stmt_alloc(loc, expr);
+
+ rule_stmt_append(ctx->rule, stmt);
}
}
@@ -767,6 +862,9 @@ static void netlink_parse_meta_expr(struct netlink_parse_ctx *ctx,
expr = meta_expr_alloc(loc, key);
dreg = netlink_parse_register(nle, NFTNL_EXPR_META_DREG);
+ if (ctx->inner)
+ ctx->inner_reg = dreg;
+
netlink_set_register(ctx, dreg, expr);
}
@@ -820,7 +918,9 @@ static void netlink_parse_meta_stmt(struct netlink_parse_ctx *ctx,
key = nftnl_expr_get_u32(nle, NFTNL_EXPR_META_KEY);
stmt = meta_stmt_alloc(loc, key, expr);
- expr_set_type(expr, stmt->meta.tmpl->dtype, stmt->meta.tmpl->byteorder);
+
+ if (stmt->meta.tmpl)
+ expr_set_type(expr, stmt->meta.tmpl->dtype, stmt->meta.tmpl->byteorder);
ctx->stmt = stmt;
}
@@ -967,6 +1067,19 @@ static void netlink_parse_counter(struct netlink_parse_ctx *ctx,
ctx->stmt = stmt;
}
+static void netlink_parse_last(struct netlink_parse_ctx *ctx,
+ const struct location *loc,
+ const struct nftnl_expr *nle)
+{
+ struct stmt *stmt;
+
+ stmt = last_stmt_alloc(loc);
+ stmt->last.used = nftnl_expr_get_u64(nle, NFTNL_EXPR_LAST_MSECS);
+ stmt->last.set = nftnl_expr_get_u32(nle, NFTNL_EXPR_LAST_SET);
+
+ ctx->stmt = stmt;
+}
+
static void netlink_parse_log(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
@@ -1619,6 +1732,8 @@ static void netlink_parse_dynset(struct netlink_parse_ctx *ctx,
expr = netlink_parse_concat_key(ctx, loc, sreg, set->key);
if (expr == NULL)
return;
+ } else if (expr->dtype == &invalid_type) {
+ expr_set_type(expr, datatype_get(set->key->dtype), set->key->byteorder);
}
expr = set_elem_expr_alloc(&expr->location, expr);
@@ -1648,6 +1763,14 @@ static void netlink_parse_dynset(struct netlink_parse_ctx *ctx,
if (nftnl_expr_is_set(nle, NFTNL_EXPR_DYNSET_SREG_DATA)) {
sreg_data = netlink_parse_register(nle, NFTNL_EXPR_DYNSET_SREG_DATA);
expr_data = netlink_get_register(ctx, loc, sreg_data);
+
+ if (expr_data && expr_data->len < set->data->len) {
+ expr_free(expr_data);
+ expr_data = netlink_parse_concat_expr(ctx, loc, sreg_data, set->data->len);
+ if (expr_data == NULL)
+ netlink_error(ctx, loc,
+ "Could not parse dynset map data expressions");
+ }
}
if (expr_data != NULL) {
@@ -1760,6 +1883,7 @@ static const struct expr_handler netlink_parsers[] = {
{ .name = "bitwise", .parse = netlink_parse_bitwise },
{ .name = "byteorder", .parse = netlink_parse_byteorder },
{ .name = "payload", .parse = netlink_parse_payload },
+ { .name = "inner", .parse = netlink_parse_inner },
{ .name = "exthdr", .parse = netlink_parse_exthdr },
{ .name = "meta", .parse = netlink_parse_meta },
{ .name = "socket", .parse = netlink_parse_socket },
@@ -1768,6 +1892,7 @@ static const struct expr_handler netlink_parsers[] = {
{ .name = "ct", .parse = netlink_parse_ct },
{ .name = "connlimit", .parse = netlink_parse_connlimit },
{ .name = "counter", .parse = netlink_parse_counter },
+ { .name = "last", .parse = netlink_parse_last },
{ .name = "log", .parse = netlink_parse_log },
{ .name = "limit", .parse = netlink_parse_limit },
{ .name = "range", .parse = netlink_parse_range },
@@ -1856,6 +1981,37 @@ struct stmt *netlink_parse_set_expr(const struct set *set,
return pctx->stmt;
}
+static bool meta_outer_may_dependency_kill(struct rule_pp_ctx *ctx,
+ const struct expr *expr)
+{
+ struct dl_proto_ctx *dl_outer = dl_proto_ctx_outer(ctx);
+ struct stmt *stmt = dl_outer->pdctx.pdeps[expr->payload.inner_desc->base];
+ struct expr *dep;
+ uint8_t l4proto;
+
+ if (!stmt)
+ return false;
+
+ dep = stmt->expr;
+
+ if (dep->left->meta.key != NFT_META_L4PROTO)
+ return false;
+
+ l4proto = mpz_get_uint8(dep->right->value);
+
+ switch (l4proto) {
+ case IPPROTO_GRE:
+ if (expr->payload.inner_desc == &proto_gre ||
+ expr->payload.inner_desc == &proto_gretap)
+ return true;
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
+
static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp);
static void payload_match_expand(struct rule_pp_ctx *ctx,
@@ -1864,12 +2020,12 @@ static void payload_match_expand(struct rule_pp_ctx *ctx,
{
struct expr *left = payload, *right = expr->right, *tmp;
struct list_head list = LIST_HEAD_INIT(list);
- struct stmt *nstmt;
- struct expr *nexpr = NULL;
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
enum proto_bases base = left->payload.base;
- bool stacked;
+ struct expr *nexpr = NULL;
+ struct stmt *nstmt;
- payload_expr_expand(&list, left, &ctx->pctx);
+ payload_expr_expand(&list, left, &dl->pctx);
list_for_each_entry(left, &list, list) {
tmp = constant_expr_splice(right, left->len);
@@ -1884,7 +2040,7 @@ static void payload_match_expand(struct rule_pp_ctx *ctx,
nexpr = relational_expr_alloc(&expr->location, expr->op,
left, tmp);
if (expr->op == OP_EQ)
- relational_expr_pctx_update(&ctx->pctx, nexpr);
+ relational_expr_pctx_update(&dl->pctx, nexpr);
nstmt = expr_stmt_alloc(&ctx->stmt->location, nexpr);
list_add_tail(&nstmt->list, &ctx->stmt->list);
@@ -1893,22 +2049,31 @@ static void payload_match_expand(struct rule_pp_ctx *ctx,
assert(left->payload.base);
assert(base == left->payload.base);
- stacked = payload_is_stacked(ctx->pctx.protocol[base].desc, nexpr);
+ if (expr->left->payload.inner_desc) {
+ if (expr->left->payload.inner_desc == expr->left->payload.desc) {
+ nexpr->left->payload.desc = expr->left->payload.desc;
+ nexpr->left->payload.tmpl = expr->left->payload.tmpl;
+ }
+ nexpr->left->payload.inner_desc = expr->left->payload.inner_desc;
+
+ if (meta_outer_may_dependency_kill(ctx, expr->left)) {
+ struct dl_proto_ctx *dl_outer = dl_proto_ctx_outer(ctx);
+
+ payload_dependency_release(&dl_outer->pdctx, expr->left->payload.inner_desc->base);
+ }
+ }
+
+ if (payload_is_stacked(dl->pctx.protocol[base].desc, nexpr))
+ base--;
/* Remember the first payload protocol expression to
* kill it later on if made redundant by a higher layer
* payload expression.
*/
- if (ctx->pdctx.pbase == PROTO_BASE_INVALID &&
- expr->op == OP_EQ &&
- left->flags & EXPR_F_PROTOCOL) {
- payload_dependency_store(&ctx->pdctx, nstmt, base - stacked);
- } else {
- payload_dependency_kill(&ctx->pdctx, nexpr->left,
- ctx->pctx.family);
- if (expr->op == OP_EQ && left->flags & EXPR_F_PROTOCOL)
- payload_dependency_store(&ctx->pdctx, nstmt, base - stacked);
- }
+ payload_dependency_kill(&dl->pdctx, nexpr->left,
+ dl->pctx.family);
+ if (expr->op == OP_EQ && left->flags & EXPR_F_PROTOCOL)
+ payload_dependency_store(&dl->pdctx, nstmt, base);
}
list_del(&ctx->stmt->list);
stmt_free(ctx->stmt);
@@ -1917,6 +2082,7 @@ static void payload_match_expand(struct rule_pp_ctx *ctx,
static void payload_icmp_check(struct rule_pp_ctx *rctx, struct expr *expr, const struct expr *value)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(rctx);
const struct proto_hdr_template *tmpl;
const struct proto_desc *desc;
uint8_t icmp_type;
@@ -1931,10 +2097,10 @@ static void payload_icmp_check(struct rule_pp_ctx *rctx, struct expr *expr, cons
/* icmp(v6) type is 8 bit, if value is smaller or larger, this is not
* a protocol dependency.
*/
- if (expr->len != 8 || value->len != 8 || rctx->pctx.th_dep.icmp.type)
+ if (expr->len != 8 || value->len != 8 || dl->pctx.th_dep.icmp.type)
return;
- desc = rctx->pctx.protocol[expr->payload.base].desc;
+ desc = dl->pctx.protocol[expr->payload.base].desc;
if (desc == NULL)
return;
@@ -1962,7 +2128,7 @@ static void payload_icmp_check(struct rule_pp_ctx *rctx, struct expr *expr, cons
expr->payload.desc = desc;
expr->payload.tmpl = tmpl;
- rctx->pctx.th_dep.icmp.type = icmp_type;
+ dl->pctx.th_dep.icmp.type = icmp_type;
return;
}
}
@@ -1971,10 +2137,7 @@ static void payload_match_postprocess(struct rule_pp_ctx *ctx,
struct expr *expr,
struct expr *payload)
{
- enum proto_bases base = payload->payload.base;
-
- assert(payload->payload.offset >= ctx->pctx.protocol[base].offset);
- payload->payload.offset -= ctx->pctx.protocol[base].offset;
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
switch (expr->op) {
case OP_EQ:
@@ -1999,10 +2162,10 @@ static void payload_match_postprocess(struct rule_pp_ctx *ctx,
}
/* Fall through */
default:
- payload_expr_complete(payload, &ctx->pctx);
+ payload_expr_complete(payload, &dl->pctx);
expr_set_type(expr->right, payload->dtype,
payload->byteorder);
- payload_dependency_kill(&ctx->pdctx, payload, ctx->pctx.family);
+ payload_dependency_kill(&dl->pdctx, payload, dl->pctx.family);
break;
}
}
@@ -2057,7 +2220,7 @@ static bool __meta_dependency_may_kill(const struct expr *dep, uint8_t *nfproto)
}
/* We have seen a protocol key expression that restricts matching at the network
- * base, leave it in place since this is meaninful in bridge, inet and netdev
+ * base, leave it in place since this is meaningful in bridge, inet and netdev
* families. Exceptions are ICMP and ICMPv6 where this code assumes that can
* only happen with IPv4 and IPv6.
*/
@@ -2066,9 +2229,9 @@ static bool meta_may_dependency_kill(struct payload_dep_ctx *ctx,
const struct expr *expr)
{
uint8_t l4proto, nfproto = NFPROTO_UNSPEC;
- struct expr *dep = ctx->pdep->expr;
+ struct expr *dep = payload_dependency_get(ctx, PROTO_BASE_NETWORK_HDR);
- if (ctx->pbase != PROTO_BASE_NETWORK_HDR)
+ if (!dep)
return true;
if (__meta_dependency_may_kill(dep, &nfproto))
@@ -2079,14 +2242,10 @@ static bool meta_may_dependency_kill(struct payload_dep_ctx *ctx,
case NFPROTO_NETDEV:
case NFPROTO_BRIDGE:
break;
+ case NFPROTO_IPV4:
+ case NFPROTO_IPV6:
+ return family == nfproto;
default:
- if (family == NFPROTO_IPV4 &&
- nfproto != NFPROTO_IPV4)
- return false;
- else if (family == NFPROTO_IPV6 &&
- nfproto != NFPROTO_IPV6)
- return false;
-
return true;
}
@@ -2114,6 +2273,7 @@ static void ct_meta_common_postprocess(struct rule_pp_ctx *ctx,
const struct expr *expr,
enum proto_bases base)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
const struct expr *left = expr->left;
struct expr *right = expr->right;
@@ -2127,18 +2287,16 @@ static void ct_meta_common_postprocess(struct rule_pp_ctx *ctx,
expr->right->etype == EXPR_SET_REF)
break;
- relational_expr_pctx_update(&ctx->pctx, expr);
+ relational_expr_pctx_update(&dl->pctx, expr);
+
+ if (base < PROTO_BASE_TRANSPORT_HDR) {
+ if (payload_dependency_exists(&dl->pdctx, base) &&
+ meta_may_dependency_kill(&dl->pdctx,
+ dl->pctx.family, expr))
+ payload_dependency_release(&dl->pdctx, base);
- if (ctx->pdctx.pbase == PROTO_BASE_INVALID &&
- left->flags & EXPR_F_PROTOCOL) {
- payload_dependency_store(&ctx->pdctx, ctx->stmt, base);
- } else if (ctx->pdctx.pbase < PROTO_BASE_TRANSPORT_HDR) {
- if (payload_dependency_exists(&ctx->pdctx, base) &&
- meta_may_dependency_kill(&ctx->pdctx,
- ctx->pctx.family, expr))
- payload_dependency_release(&ctx->pdctx);
if (left->flags & EXPR_F_PROTOCOL)
- payload_dependency_store(&ctx->pdctx, ctx->stmt, base);
+ payload_dependency_store(&dl->pdctx, ctx->stmt, base);
}
break;
default:
@@ -2223,8 +2381,8 @@ static void binop_adjust_one(const struct expr *binop, struct expr *value,
}
}
-static void __binop_adjust(const struct expr *binop, struct expr *right,
- unsigned int shift)
+static void binop_adjust(const struct expr *binop, struct expr *right,
+ unsigned int shift)
{
struct expr *i;
@@ -2233,6 +2391,9 @@ static void __binop_adjust(const struct expr *binop, struct expr *right,
binop_adjust_one(binop, right, shift);
break;
case EXPR_SET_REF:
+ if (!set_is_anonymous(right->set->flags))
+ break;
+
list_for_each_entry(i, &right->set->init->expressions, list) {
switch (i->key->etype) {
case EXPR_VALUE:
@@ -2243,7 +2404,7 @@ static void __binop_adjust(const struct expr *binop, struct expr *right,
binop_adjust_one(binop, i->key->right, shift);
break;
case EXPR_SET_ELEM:
- __binop_adjust(binop, i->key->key, shift);
+ binop_adjust(binop, i->key->key, shift);
break;
default:
BUG("unknown expression type %s\n", expr_name(i->key));
@@ -2260,22 +2421,24 @@ static void __binop_adjust(const struct expr *binop, struct expr *right,
}
}
-static void binop_adjust(struct expr *expr, unsigned int shift)
+static void __binop_postprocess(struct rule_pp_ctx *ctx,
+ struct expr *expr,
+ struct expr *left,
+ struct expr *mask,
+ struct expr **expr_binop)
{
- __binop_adjust(expr->left, expr->right, shift);
-}
-
-static void binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr)
-{
- struct expr *binop = expr->left;
- struct expr *left = binop->left;
- struct expr *mask = binop->right;
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
+ struct expr *binop = *expr_binop;
unsigned int shift;
+ assert(binop->etype == EXPR_BINOP);
+
if ((left->etype == EXPR_PAYLOAD &&
- payload_expr_trim(left, mask, &ctx->pctx, &shift)) ||
+ payload_expr_trim(left, mask, &dl->pctx, &shift)) ||
(left->etype == EXPR_EXTHDR &&
exthdr_find_template(left, mask, &shift))) {
+ struct expr *right = NULL;
+
/* mask is implicit, binop needs to be removed.
*
* Fix all values of the expression according to the mask
@@ -2285,96 +2448,106 @@ static void binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr)
* Finally, convert the expression to 1) by replacing
* the binop with the binop payload/exthdr expression.
*/
- binop_adjust(expr, shift);
+ switch (expr->etype) {
+ case EXPR_BINOP:
+ case EXPR_RELATIONAL:
+ right = expr->right;
+ binop_adjust(binop, right, shift);
+ break;
+ case EXPR_MAP:
+ right = expr->mappings;
+ binop_adjust(binop, right, shift);
+ break;
+ default:
+ break;
+ }
- assert(expr->left->etype == EXPR_BINOP);
assert(binop->left == left);
- expr->left = expr_get(left);
- expr_free(binop);
+ *expr_binop = expr_get(left);
+
if (left->etype == EXPR_PAYLOAD)
payload_match_postprocess(ctx, expr, left);
- else if (left->etype == EXPR_EXTHDR)
- expr_set_type(expr->right, left->dtype, left->byteorder);
+ else if (left->etype == EXPR_EXTHDR && right)
+ expr_set_type(right, left->dtype, left->byteorder);
+
+ expr_free(binop);
}
}
+static void binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr,
+ struct expr **expr_binop)
+{
+ struct expr *binop = *expr_binop;
+ struct expr *left = binop->left;
+ struct expr *mask = binop->right;
+
+ __binop_postprocess(ctx, expr, left, mask, expr_binop);
+}
+
static void map_binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr)
{
- struct expr *binop = expr->left;
+ struct expr *binop = expr->map;
if (binop->op != OP_AND)
return;
if (binop->left->etype == EXPR_PAYLOAD &&
binop->right->etype == EXPR_VALUE)
- binop_postprocess(ctx, expr);
+ binop_postprocess(ctx, expr, &expr->map);
+}
+
+static bool is_shift_by_zero(const struct expr *binop)
+{
+ struct expr *rhs;
+
+ if (binop->op != OP_RSHIFT && binop->op != OP_LSHIFT)
+ return false;
+
+ rhs = binop->right;
+ if (rhs->etype != EXPR_VALUE || rhs->len > 64)
+ return false;
+
+ return mpz_get_uint64(rhs->value) == 0;
}
static void relational_binop_postprocess(struct rule_pp_ctx *ctx,
struct expr **exprp)
{
- struct expr *expr = *exprp, *binop = expr->left, *value = expr->right;
+ struct expr *expr = *exprp, *binop = expr->left, *right = expr->right;
if (binop->op == OP_AND && (expr->op == OP_NEQ || expr->op == OP_EQ) &&
- value->dtype->basetype &&
- value->dtype->basetype->type == TYPE_BITMASK) {
- switch (value->etype) {
- case EXPR_VALUE:
- if (!mpz_cmp_ui(value->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(value);
+ right->dtype->basetype &&
+ 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 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 &&
- value->etype == EXPR_VALUE &&
- !mpz_cmp(value->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(value));
- expr_free(expr);
- }
+ 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, value));
- 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)) {
expr->left = expr_get(binop->left);
expr->right = prefix_expr_alloc(&expr->location,
- expr_get(value),
+ expr_get(right),
expr_mask_to_prefix(binop->right));
- expr_free(value);
+ expr_free(right);
expr_free(binop);
} else if (binop->op == OP_AND &&
binop->right->etype == EXPR_VALUE) {
@@ -2401,8 +2574,58 @@ static void relational_binop_postprocess(struct rule_pp_ctx *ctx,
* payload_expr_trim will figure out if the mask is needed to match
* templates.
*/
- binop_postprocess(ctx, expr);
+ binop_postprocess(ctx, expr, &expr->left);
+ } else if (binop->op == OP_RSHIFT && binop->left->op == OP_AND &&
+ binop->right->etype == EXPR_VALUE && binop->left->right->etype == EXPR_VALUE) {
+ /* Handle 'ip version @s4' and similar, i.e. set lookups where the lhs needs
+ * fixups to mask out unwanted bits AND a shift.
+ */
+
+ binop_postprocess(ctx, binop, &binop->left);
+ if (is_shift_by_zero(binop)) {
+ struct expr *lhs = binop->left;
+
+ expr_get(lhs);
+ expr_free(binop);
+ expr->left = lhs;
+ }
+ }
+}
+
+static bool payload_binop_postprocess(struct rule_pp_ctx *ctx,
+ struct expr **exprp)
+{
+ struct expr *expr = *exprp;
+
+ if (expr->op != OP_RSHIFT)
+ return false;
+
+ if (expr->left->etype == EXPR_UNARY) {
+ /*
+ * If the payload value was originally in a different byte-order
+ * from the payload expression, there will be a byte-order
+ * conversion to remove.
+ */
+ struct expr *left = expr_get(expr->left->arg);
+ expr_free(expr->left);
+ expr->left = left;
}
+
+ if (expr->left->etype != EXPR_BINOP || expr->left->op != OP_AND)
+ return false;
+
+ if (expr->left->left->etype != EXPR_PAYLOAD)
+ return false;
+
+ expr_set_type(expr->right, &integer_type,
+ BYTEORDER_HOST_ENDIAN);
+ expr_postprocess(ctx, &expr->right);
+
+ binop_postprocess(ctx, expr, &expr->left);
+ *exprp = expr_get(expr->left);
+ expr_free(expr);
+
+ return true;
}
static struct expr *string_wildcard_expr_alloc(struct location *loc,
@@ -2422,56 +2645,33 @@ static struct expr *string_wildcard_expr_alloc(struct location *loc,
expr->len + BITS_PER_BYTE, data);
}
-static void escaped_string_wildcard_expr_alloc(struct expr **exprp,
- unsigned int len)
-{
- struct expr *expr = *exprp, *tmp;
- char data[len + 3];
- int pos;
-
- mpz_export_data(data, expr->value, BYTEORDER_HOST_ENDIAN, len);
- pos = div_round_up(len, BITS_PER_BYTE);
- data[pos - 1] = '\\';
- data[pos] = '*';
-
- tmp = constant_expr_alloc(&expr->location, expr->dtype,
- BYTEORDER_HOST_ENDIAN,
- expr->len + BITS_PER_BYTE, data);
- expr_free(expr);
- *exprp = tmp;
-}
-
/* This calculates the string length and checks if it is nul-terminated, this
* function is quite a hack :)
*/
static bool __expr_postprocess_string(struct expr **exprp)
{
struct expr *expr = *exprp;
- unsigned int len = expr->len;
- bool nulterminated = false;
- mpz_t tmp;
-
- mpz_init(tmp);
- while (len >= BITS_PER_BYTE) {
- mpz_bitmask(tmp, BITS_PER_BYTE);
- mpz_lshift_ui(tmp, len - BITS_PER_BYTE);
- mpz_and(tmp, tmp, expr->value);
- if (mpz_cmp_ui(tmp, 0))
- break;
- else
- nulterminated = true;
- len -= BITS_PER_BYTE;
- }
+ unsigned int len = div_round_up(expr->len, BITS_PER_BYTE);
+ char data[len + 1];
- mpz_rshift_ui(tmp, len - BITS_PER_BYTE);
+ mpz_export_data(data, expr->value, BYTEORDER_HOST_ENDIAN, len);
- if (nulterminated &&
- mpz_cmp_ui(tmp, '*') == 0)
- escaped_string_wildcard_expr_alloc(exprp, len);
+ if (data[len - 1] != '\0')
+ return false;
- mpz_clear(tmp);
+ len = strlen(data);
+ if (len && data[len - 1] == '*') {
+ data[len - 1] = '\\';
+ data[len] = '*';
+ data[len + 1] = '\0';
+ expr = constant_expr_alloc(&expr->location, expr->dtype,
+ BYTEORDER_HOST_ENDIAN,
+ (len + 2) * BITS_PER_BYTE, data);
+ expr_free(*exprp);
+ *exprp = expr;
+ }
- return nulterminated;
+ return true;
}
static struct expr *expr_postprocess_string(struct expr *expr)
@@ -2493,8 +2693,54 @@ static struct expr *expr_postprocess_string(struct expr *expr)
return out;
}
+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 && !interval)
+ 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);
struct expr *expr = *exprp, *i;
switch (expr->etype) {
@@ -2518,28 +2764,17 @@ 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_for_each_entry(i, &expr->expressions, list) {
- if (type) {
- dtype = concat_subtype_lookup(type, --off);
- expr_set_type(i, dtype, dtype->byteorder);
- }
- expr_postprocess(ctx, &i);
-
- ntype = concat_subtype_add(ntype, i->dtype->type);
- }
- 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);
break;
case EXPR_BINOP:
+ if (payload_binop_postprocess(ctx, exprp))
+ break;
+
expr_postprocess(ctx, &expr->left);
switch (expr->op) {
case OP_LSHIFT:
@@ -2547,20 +2782,89 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
expr_set_type(expr->right, &integer_type,
BYTEORDER_HOST_ENDIAN);
break;
+ case OP_AND:
+ if (expr->right->len > expr->left->len) {
+ expr_set_type(expr->right, expr->left->dtype,
+ BYTEORDER_HOST_ENDIAN);
+ } else {
+ expr_set_type(expr->right, expr->left->dtype,
+ expr->left->byteorder);
+ }
+
+ /* Do not process OP_AND in ordinary rule context.
+ *
+ * Removal needs to be performed as part of the relational
+ * operation because the RHS constant might need to be adjusted
+ * (shifted).
+ *
+ * This is different in set element context or concatenations:
+ * There is no relational operation (eq, neq and so on), thus
+ * it needs to be processed right away.
+ */
+ if ((ctx->flags & RULE_PP_REMOVE_OP_AND) &&
+ expr->left->etype == EXPR_PAYLOAD &&
+ expr->right->etype == EXPR_VALUE) {
+ __binop_postprocess(ctx, expr, expr->left, expr->right, exprp);
+ return;
+ }
+ break;
+ default:
+ if (expr->right->len > expr->left->len) {
+ expr_set_type(expr->right, expr->left->dtype,
+ BYTEORDER_HOST_ENDIAN);
+ } else {
+ expr_set_type(expr->right, expr->left->dtype,
+ expr->left->byteorder);
+ }
+ }
+ expr_postprocess(ctx, &expr->right);
+
+ switch (expr->op) {
+ case OP_LSHIFT:
+ case OP_RSHIFT:
+ expr_set_type(expr, &xinteger_type,
+ BYTEORDER_HOST_ENDIAN);
+ break;
default:
- expr_set_type(expr->right, expr->left->dtype,
+ expr_set_type(expr, expr->left->dtype,
expr->left->byteorder);
}
- expr_postprocess(ctx, &expr->right);
- expr_set_type(expr, expr->left->dtype,
- expr->left->byteorder);
break;
case EXPR_RELATIONAL:
switch (expr->left->etype) {
case EXPR_PAYLOAD:
payload_match_postprocess(ctx, expr, expr->left);
return;
+ case EXPR_CONCAT:
+ if (expr->right->etype == EXPR_SET_REF) {
+ assert(expr->left->dtype == &invalid_type);
+ assert(expr->right->dtype != &invalid_type);
+
+ datatype_set(expr->left, expr->right->dtype);
+ }
+ ctx->set = expr->right->set;
+ expr_postprocess(ctx, &expr->left);
+ ctx->set = NULL;
+ break;
+ case EXPR_UNARY:
+ if (lhs_is_meta_hour(expr->left->arg) &&
+ expr->right->etype == EXPR_RANGE) {
+ struct expr *range = expr->right;
+
+ /* Cross-day range needs to be reversed.
+ * Kernel handles time in UTC. Therefore,
+ * 03:00-14:00 AEDT (Sidney, Australia) time
+ * is a cross-day range.
+ */
+ if (mpz_cmp(range->left->value,
+ range->right->value) <= 0 &&
+ expr->op == OP_NEQ) {
+ range_expr_swap_values(range);
+ expr->op = OP_IMPLICIT;
+ }
+ }
+ /* fallthrough */
default:
expr_postprocess(ctx, &expr->left);
break;
@@ -2584,22 +2888,18 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
}
break;
case EXPR_PAYLOAD:
- payload_expr_complete(expr, &ctx->pctx);
- payload_dependency_kill(&ctx->pdctx, expr, ctx->pctx.family);
+ payload_expr_complete(expr, &dl->pctx);
+ if (expr->payload.inner_desc) {
+ if (meta_outer_may_dependency_kill(ctx, expr)) {
+ struct dl_proto_ctx *dl_outer = dl_proto_ctx_outer(ctx);
+
+ payload_dependency_release(&dl_outer->pdctx, expr->payload.inner_desc->base);
+ }
+ }
+ 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);
@@ -2609,10 +2909,12 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
expr_postprocess(ctx, &expr->prefix);
break;
case EXPR_SET_ELEM:
+ ctx->flags |= RULE_PP_IN_SET_ELEM;
expr_postprocess(ctx, &expr->key);
+ ctx->flags &= ~RULE_PP_IN_SET_ELEM;
break;
case EXPR_EXTHDR:
- exthdr_dependency_kill(&ctx->pdctx, expr, ctx->pctx.family);
+ exthdr_dependency_kill(&dl->pdctx, expr, dl->pctx.family);
break;
case EXPR_SET_REF:
case EXPR_META:
@@ -2629,7 +2931,7 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
expr_postprocess(ctx, &expr->hash.expr);
break;
case EXPR_CT:
- ct_expr_update_type(&ctx->pctx, expr);
+ ct_expr_update_type(&dl->pctx, expr);
break;
default:
BUG("unknown expression type %s\n", expr_name(expr));
@@ -2638,32 +2940,35 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
static void stmt_reject_postprocess(struct rule_pp_ctx *rctx)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(rctx);
const struct proto_desc *desc, *base;
struct stmt *stmt = rctx->stmt;
int protocol;
- switch (rctx->pctx.family) {
+ switch (dl->pctx.family) {
case NFPROTO_IPV4:
- stmt->reject.family = rctx->pctx.family;
- datatype_set(stmt->reject.expr, &icmp_code_type);
+ stmt->reject.family = dl->pctx.family;
+ datatype_set(stmt->reject.expr, &reject_icmp_code_type);
if (stmt->reject.type == NFT_REJECT_TCP_RST &&
- payload_dependency_exists(&rctx->pdctx,
+ payload_dependency_exists(&dl->pdctx,
PROTO_BASE_TRANSPORT_HDR))
- payload_dependency_release(&rctx->pdctx);
+ payload_dependency_release(&dl->pdctx,
+ PROTO_BASE_TRANSPORT_HDR);
break;
case NFPROTO_IPV6:
- stmt->reject.family = rctx->pctx.family;
- datatype_set(stmt->reject.expr, &icmpv6_code_type);
+ stmt->reject.family = dl->pctx.family;
+ datatype_set(stmt->reject.expr, &reject_icmpv6_code_type);
if (stmt->reject.type == NFT_REJECT_TCP_RST &&
- payload_dependency_exists(&rctx->pdctx,
+ payload_dependency_exists(&dl->pdctx,
PROTO_BASE_TRANSPORT_HDR))
- payload_dependency_release(&rctx->pdctx);
+ payload_dependency_release(&dl->pdctx,
+ PROTO_BASE_TRANSPORT_HDR);
break;
case NFPROTO_INET:
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;
}
@@ -2672,26 +2977,27 @@ static void stmt_reject_postprocess(struct rule_pp_ctx *rctx)
*/
stmt->reject.verbose_print = 1;
- base = rctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
- desc = rctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ base = dl->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ desc = dl->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
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;
}
- if (payload_dependency_exists(&rctx->pdctx, PROTO_BASE_NETWORK_HDR))
- payload_dependency_release(&rctx->pdctx);
+ if (payload_dependency_exists(&dl->pdctx, PROTO_BASE_NETWORK_HDR))
+ payload_dependency_release(&dl->pdctx,
+ PROTO_BASE_NETWORK_HDR);
break;
default:
break;
@@ -2734,23 +3040,24 @@ static bool expr_may_merge_range(struct expr *expr, struct expr *prev,
static void expr_postprocess_range(struct rule_pp_ctx *ctx, enum ops op)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
struct stmt *nstmt, *stmt = ctx->stmt;
struct expr *nexpr, *rel;
- nexpr = range_expr_alloc(&ctx->pdctx.prev->location,
- expr_clone(ctx->pdctx.prev->expr->right),
+ nexpr = range_expr_alloc(&dl->pdctx.prev->location,
+ expr_clone(dl->pdctx.prev->expr->right),
expr_clone(stmt->expr->right));
expr_set_type(nexpr, stmt->expr->right->dtype,
stmt->expr->right->byteorder);
- rel = relational_expr_alloc(&ctx->pdctx.prev->location, op,
+ rel = relational_expr_alloc(&dl->pdctx.prev->location, op,
expr_clone(stmt->expr->left), nexpr);
nstmt = expr_stmt_alloc(&stmt->location, rel);
list_add_tail(&nstmt->list, &stmt->list);
- list_del(&ctx->pdctx.prev->list);
- stmt_free(ctx->pdctx.prev);
+ list_del(&dl->pdctx.prev->list);
+ stmt_free(dl->pdctx.prev);
list_del(&stmt->list);
stmt_free(stmt);
@@ -2759,26 +3066,28 @@ static void expr_postprocess_range(struct rule_pp_ctx *ctx, enum ops op)
static void stmt_expr_postprocess(struct rule_pp_ctx *ctx)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
enum ops op;
expr_postprocess(ctx, &ctx->stmt->expr);
- if (ctx->pdctx.prev && ctx->stmt &&
- ctx->stmt->ops->type == ctx->pdctx.prev->ops->type &&
- expr_may_merge_range(ctx->stmt->expr, ctx->pdctx.prev->expr, &op))
+ if (dl->pdctx.prev && ctx->stmt &&
+ ctx->stmt->ops->type == dl->pdctx.prev->ops->type &&
+ expr_may_merge_range(ctx->stmt->expr, dl->pdctx.prev->expr, &op))
expr_postprocess_range(ctx, op);
}
static void stmt_payload_binop_pp(struct rule_pp_ctx *ctx, struct expr *binop)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
struct expr *payload = binop->left;
struct expr *mask = binop->right;
unsigned int shift;
assert(payload->etype == EXPR_PAYLOAD);
- if (payload_expr_trim(payload, mask, &ctx->pctx, &shift)) {
- __binop_adjust(binop, mask, shift);
- payload_expr_complete(payload, &ctx->pctx);
+ if (payload_expr_trim(payload, mask, &dl->pctx, &shift)) {
+ binop_adjust(binop, mask, shift);
+ payload_expr_complete(payload, &dl->pctx);
expr_set_type(mask, payload->dtype,
payload->byteorder);
}
@@ -2872,7 +3181,7 @@ static void stmt_payload_binop_postprocess(struct rule_pp_ctx *ctx)
mpz_set(mask->value, bitmask);
mpz_clear(bitmask);
- binop_postprocess(ctx, expr);
+ binop_postprocess(ctx, expr, &expr->left);
if (!payload_is_known(payload)) {
mpz_set(mask->value, tmp);
mpz_clear(tmp);
@@ -2933,17 +3242,19 @@ static void stmt_payload_binop_postprocess(struct rule_pp_ctx *ctx)
static void stmt_payload_postprocess(struct rule_pp_ctx *ctx)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
struct stmt *stmt = ctx->stmt;
+ payload_expr_complete(stmt->payload.expr, &dl->pctx);
+ if (!payload_is_known(stmt->payload.expr))
+ stmt_payload_binop_postprocess(ctx);
+
expr_postprocess(ctx, &stmt->payload.expr);
expr_set_type(stmt->payload.val,
stmt->payload.expr->dtype,
stmt->payload.expr->byteorder);
- if (!payload_is_known(stmt->payload.expr))
- stmt_payload_binop_postprocess(ctx);
-
expr_postprocess(ctx, &stmt->payload.val);
}
@@ -2980,19 +3291,75 @@ rule_maybe_reset_payload_deps(struct payload_dep_ctx *pdctx, enum stmt_types t)
payload_dependency_reset(pdctx);
}
+static bool has_inner_desc(const struct expr *expr)
+{
+ struct expr *i;
+
+ switch (expr->etype) {
+ case EXPR_BINOP:
+ return has_inner_desc(expr->left);
+ case EXPR_CONCAT:
+ list_for_each_entry(i, &expr->expressions, list) {
+ if (has_inner_desc(i))
+ return true;
+ }
+ break;
+ case EXPR_META:
+ return expr->meta.inner_desc;
+ case EXPR_PAYLOAD:
+ return expr->payload.inner_desc;
+ case EXPR_SET_ELEM:
+ return has_inner_desc(expr->key);
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static struct dl_proto_ctx *rule_update_dl_proto_ctx(struct rule_pp_ctx *rctx)
+{
+ const struct stmt *stmt = rctx->stmt;
+ bool inner = false;
+
+ switch (stmt->ops->type) {
+ case STMT_EXPRESSION:
+ if (has_inner_desc(stmt->expr->left))
+ inner = true;
+ break;
+ case STMT_SET:
+ if (has_inner_desc(stmt->set.key))
+ inner = true;
+ break;
+ default:
+ break;
+ }
+
+ if (inner)
+ rctx->dl = &rctx->_dl[1];
+ else
+ rctx->dl = &rctx->_dl[0];
+
+ return rctx->dl;
+}
+
static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *rule)
{
struct stmt *stmt, *next;
+ struct dl_proto_ctx *dl;
struct rule_pp_ctx rctx;
struct expr *expr;
memset(&rctx, 0, sizeof(rctx));
- proto_ctx_init(&rctx.pctx, rule->handle.family, ctx->debug_mask);
+ proto_ctx_init(&rctx._dl[0].pctx, rule->handle.family, ctx->debug_mask, false);
+ /* use NFPROTO_BRIDGE to set up proto_eth as base protocol. */
+ proto_ctx_init(&rctx._dl[1].pctx, NFPROTO_BRIDGE, ctx->debug_mask, true);
list_for_each_entry_safe(stmt, next, &rule->stmts, list) {
enum stmt_types type = stmt->ops->type;
rctx.stmt = stmt;
+ dl = rule_update_dl_proto_ctx(&rctx);
switch (type) {
case STMT_EXPRESSION:
@@ -3023,16 +3390,14 @@ static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *r
case STMT_NAT:
if (stmt->nat.addr != NULL)
expr_postprocess(&rctx, &stmt->nat.addr);
- if (stmt->nat.proto != NULL) {
- payload_dependency_reset(&rctx.pdctx);
+ if (stmt->nat.proto != NULL)
expr_postprocess(&rctx, &stmt->nat.proto);
- }
break;
case STMT_TPROXY:
if (stmt->tproxy.addr)
expr_postprocess(&rctx, &stmt->tproxy.addr);
if (stmt->tproxy.port) {
- payload_dependency_reset(&rctx.pdctx);
+ payload_dependency_reset(&dl->pdctx);
expr_postprocess(&rctx, &stmt->tproxy.port);
}
break;
@@ -3070,9 +3435,9 @@ static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *r
break;
}
- rctx.pdctx.prev = rctx.stmt;
+ dl->pdctx.prev = rctx.stmt;
- rule_maybe_reset_payload_deps(&rctx.pdctx, type);
+ rule_maybe_reset_payload_deps(&dl->pdctx, type);
}
}
@@ -3124,6 +3489,7 @@ struct rule *netlink_delinearize_rule(struct netlink_ctx *ctx,
memset(&_ctx, 0, sizeof(_ctx));
_ctx.msgs = ctx->msgs;
_ctx.debug_mask = ctx->nft->debug_mask;
+ _ctx.nlctx = ctx;
memset(&h, 0, sizeof(h));
h.family = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY);
@@ -3137,7 +3503,10 @@ struct rule *netlink_delinearize_rule(struct netlink_ctx *ctx,
pctx->rule = rule_alloc(&netlink_location, &h);
pctx->table = table_cache_find(&ctx->nft->cache.table_cache,
h.table.name, h.family);
- assert(pctx->table != NULL);
+ if (!pctx->table) {
+ errno = ENOENT;
+ return NULL;
+ }
pctx->rule->comment = nftnl_rule_get_comment(nlr);
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index 454b9ba3..6204d8fd 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -9,10 +9,11 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/nf_log.h>
-#include <string.h>
#include <rule.h>
#include <statement.h>
#include <expression.h>
@@ -178,9 +179,8 @@ static void netlink_gen_hash(struct netlink_linearize_ctx *ctx,
nft_rule_add_expr(ctx, nle, &expr->location);
}
-static void netlink_gen_payload(struct netlink_linearize_ctx *ctx,
- const struct expr *expr,
- enum nft_registers dreg)
+static struct nftnl_expr *
+__netlink_gen_payload(const struct expr *expr, enum nft_registers dreg)
{
struct nftnl_expr *nle;
@@ -193,6 +193,72 @@ static void netlink_gen_payload(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_LEN,
div_round_up(expr->len, BITS_PER_BYTE));
+ return nle;
+}
+
+static struct nftnl_expr *
+__netlink_gen_meta(const struct expr *expr, enum nft_registers dreg)
+{
+ struct nftnl_expr *nle;
+
+ nle = alloc_nft_expr("meta");
+ netlink_put_register(nle, NFTNL_EXPR_META_DREG, dreg);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_META_KEY, expr->meta.key);
+
+ return nle;
+}
+
+static struct nftnl_expr *netlink_gen_inner_expr(const struct expr *expr,
+ enum nft_registers dreg)
+{
+ struct expr *_expr = (struct expr *)expr;
+ struct nftnl_expr *nle;
+
+ switch (expr->etype) {
+ case EXPR_PAYLOAD:
+ if (expr->payload.base == NFT_PAYLOAD_INNER_HEADER + 1)
+ _expr->payload.base = NFT_PAYLOAD_TUN_HEADER + 1;
+
+ nle = __netlink_gen_payload(expr, dreg);
+ break;
+ case EXPR_META:
+ nle = __netlink_gen_meta(expr, dreg);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ return nle;
+}
+
+static void netlink_gen_inner(struct netlink_linearize_ctx *ctx,
+ const struct expr *expr,
+ enum nft_registers dreg,
+ const struct proto_desc *desc)
+{
+ struct nftnl_expr *nle;
+
+ nle = alloc_nft_expr("inner");
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_INNER_HDRSIZE, desc->inner.hdrsize);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_INNER_FLAGS, desc->inner.flags);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_INNER_TYPE, desc->inner.type);
+ nftnl_expr_set(nle, NFTNL_EXPR_INNER_EXPR, netlink_gen_inner_expr(expr, dreg), 0);
+ nft_rule_add_expr(ctx, nle, &expr->location);
+}
+
+static void netlink_gen_payload(struct netlink_linearize_ctx *ctx,
+ const struct expr *expr,
+ enum nft_registers dreg)
+{
+ struct nftnl_expr *nle;
+
+ if (expr->payload.inner_desc) {
+ netlink_gen_inner(ctx, expr, dreg, expr->payload.inner_desc);
+ return;
+ }
+
+ nle = __netlink_gen_payload(expr, dreg);
nft_rule_add_expr(ctx, nle, &expr->location);
}
@@ -221,9 +287,12 @@ static void netlink_gen_meta(struct netlink_linearize_ctx *ctx,
{
struct nftnl_expr *nle;
- nle = alloc_nft_expr("meta");
- netlink_put_register(nle, NFTNL_EXPR_META_DREG, dreg);
- nftnl_expr_set_u32(nle, NFTNL_EXPR_META_KEY, expr->meta.key);
+ if (expr->meta.inner_desc) {
+ netlink_gen_inner(ctx, expr, dreg, expr->meta.inner_desc);
+ return;
+ }
+
+ nle = __netlink_gen_meta(expr, dreg);
nft_rule_add_expr(ctx, nle, &expr->location);
}
@@ -391,7 +460,8 @@ static struct expr *netlink_gen_prefix(struct netlink_linearize_ctx *ctx,
mpz_init(mask);
mpz_prefixmask(mask, expr->right->len, expr->right->prefix_len);
netlink_gen_raw_data(mask, expr->right->byteorder,
- expr->right->len / BITS_PER_BYTE, &nld);
+ div_round_up(expr->right->len, BITS_PER_BYTE),
+ &nld);
mpz_clear(mask);
zero.len = nld.len;
@@ -625,10 +695,10 @@ static void netlink_gen_bitwise(struct netlink_linearize_ctx *ctx,
const struct expr *expr,
enum nft_registers dreg)
{
+ struct expr *binops[NFT_MAX_EXPR_RECURSION];
struct nftnl_expr *nle;
struct nft_data_linearize nld;
struct expr *left, *i;
- struct expr *binops[16];
mpz_t mask, xor, val, tmp;
unsigned int len;
int n = 0;
@@ -640,16 +710,18 @@ static void netlink_gen_bitwise(struct netlink_linearize_ctx *ctx,
binops[n++] = left = (struct expr *) expr;
while (left->etype == EXPR_BINOP && left->left != NULL &&
- (left->op == OP_AND || left->op == OP_OR || left->op == OP_XOR))
+ (left->op == OP_AND || left->op == OP_OR || left->op == OP_XOR)) {
+ if (n == array_size(binops))
+ BUG("NFT_MAX_EXPR_RECURSION limit reached");
binops[n++] = left = left->left;
- n--;
+ }
- netlink_gen_expr(ctx, binops[n--], dreg);
+ netlink_gen_expr(ctx, binops[--n], dreg);
mpz_bitmask(mask, expr->len);
mpz_set_ui(xor, 0);
- for (; n >= 0; n--) {
- i = binops[n];
+ while (n > 0) {
+ i = binops[--n];
mpz_set(val, i->right->value);
switch (i->op) {
@@ -725,6 +797,8 @@ static void netlink_gen_unary(struct netlink_linearize_ctx *ctx,
struct nftnl_expr *nle;
int byte_size;
+ assert(div_round_up(expr->arg->len, BITS_PER_BYTE) != 1);
+
if ((expr->arg->len % 64) == 0)
byte_size = 8;
else if ((expr->arg->len % 32) == 0)
@@ -738,7 +812,7 @@ static void netlink_gen_unary(struct netlink_linearize_ctx *ctx,
netlink_put_register(nle, NFTNL_EXPR_BYTEORDER_SREG, dreg);
netlink_put_register(nle, NFTNL_EXPR_BYTEORDER_DREG, dreg);
nftnl_expr_set_u32(nle, NFTNL_EXPR_BYTEORDER_LEN,
- expr->len / BITS_PER_BYTE);
+ div_round_up(expr->len, BITS_PER_BYTE));
nftnl_expr_set_u32(nle, NFTNL_EXPR_BYTEORDER_SIZE,
byte_size);
nftnl_expr_set_u32(nle, NFTNL_EXPR_BYTEORDER_OP,
@@ -933,6 +1007,17 @@ static struct nftnl_expr *netlink_gen_quota_stmt(const struct stmt *stmt)
return nle;
}
+static struct nftnl_expr *netlink_gen_last_stmt(const struct stmt *stmt)
+{
+ struct nftnl_expr *nle;
+
+ nle = alloc_nft_expr("last");
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_LAST_SET, stmt->last.set);
+ nftnl_expr_set_u64(nle, NFTNL_EXPR_LAST_MSECS, stmt->last.used);
+
+ return nle;
+}
+
struct nftnl_expr *netlink_gen_stmt_stateful(const struct stmt *stmt)
{
switch (stmt->ops->type) {
@@ -944,6 +1029,8 @@ struct nftnl_expr *netlink_gen_stmt_stateful(const struct stmt *stmt)
return netlink_gen_limit_stmt(stmt);
case STMT_QUOTA:
return netlink_gen_quota_stmt(stmt);
+ case STMT_LAST:
+ return netlink_gen_last_stmt(stmt);
default:
BUG("unknown stateful statement type %s\n", stmt->ops->name);
}
@@ -986,7 +1073,7 @@ static void netlink_gen_exthdr_stmt(struct netlink_linearize_ctx *ctx,
nle = alloc_nft_expr("exthdr");
netlink_put_register(nle, NFTNL_EXPR_EXTHDR_SREG, sreg);
nftnl_expr_set_u8(nle, NFTNL_EXPR_EXTHDR_TYPE,
- expr->exthdr.desc->type);
+ expr->exthdr.raw_type);
nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_OFFSET, offset / BITS_PER_BYTE);
nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_LEN,
div_round_up(expr->len, BITS_PER_BYTE));
@@ -1028,8 +1115,9 @@ static void netlink_gen_payload_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_CSUM_OFFSET,
csum_off / BITS_PER_BYTE);
}
- if (expr->payload.base == PROTO_BASE_NETWORK_HDR && desc &&
- payload_needs_l4csum_update_pseudohdr(expr, desc))
+ if ((expr->payload.base == PROTO_BASE_NETWORK_HDR && desc &&
+ payload_needs_l4csum_update_pseudohdr(expr, desc)) ||
+ expr->payload.base == PROTO_BASE_INNER_HDR)
nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_FLAGS,
NFT_PAYLOAD_L4CSUM_PSEUDOHDR);
@@ -1352,6 +1440,18 @@ static void netlink_gen_fwd_stmt(struct netlink_linearize_ctx *ctx,
nft_rule_add_expr(ctx, nle, &stmt->location);
}
+static void netlink_gen_optstrip_stmt(struct netlink_linearize_ctx *ctx,
+ const struct stmt *stmt)
+{
+ struct nftnl_expr *nle = alloc_nft_expr("exthdr");
+ struct expr *expr = stmt->optstrip.expr;
+
+ nftnl_expr_set_u8(nle, NFTNL_EXPR_EXTHDR_TYPE,
+ expr->exthdr.raw_type);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_OP, expr->exthdr.op);
+ nft_rule_add_expr(ctx, nle, &expr->location);
+}
+
static void netlink_gen_queue_stmt(struct netlink_linearize_ctx *ctx,
const struct stmt *stmt)
{
@@ -1489,13 +1589,13 @@ static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx,
int num_stmts = 0;
struct stmt *this;
- sreg_key = get_register(ctx, stmt->map.key);
- netlink_gen_expr(ctx, stmt->map.key, sreg_key);
+ sreg_key = get_register(ctx, stmt->map.key->key);
+ netlink_gen_expr(ctx, stmt->map.key->key, sreg_key);
sreg_data = get_register(ctx, stmt->map.data);
netlink_gen_expr(ctx, stmt->map.data, sreg_data);
- release_register(ctx, stmt->map.key);
+ release_register(ctx, stmt->map.key->key);
release_register(ctx, stmt->map.data);
nle = alloc_nft_expr("dynset");
@@ -1507,6 +1607,10 @@ static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_SET_ID, set->handle.set_id);
nft_rule_add_expr(ctx, nle, &stmt->location);
+ if (stmt->map.key->timeout > 0)
+ nftnl_expr_set_u64(nle, NFTNL_EXPR_DYNSET_TIMEOUT,
+ stmt->map.key->timeout);
+
list_for_each_entry(this, &stmt->map.stmt_list, list)
num_stmts++;
@@ -1602,6 +1706,7 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
case STMT_COUNTER:
case STMT_LIMIT:
case STMT_QUOTA:
+ case STMT_LAST:
nle = netlink_gen_stmt_stateful(stmt);
nft_rule_add_expr(ctx, nle, &stmt->location);
break;
@@ -1615,6 +1720,8 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
return netlink_gen_map_stmt(ctx, stmt);
case STMT_CHAIN:
return netlink_gen_chain_stmt(ctx, stmt);
+ case STMT_OPTSTRIP:
+ return netlink_gen_optstrip_stmt(ctx, stmt);
default:
BUG("unknown statement type %s\n", stmt->ops->name);
}
@@ -1641,9 +1748,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,
@@ -1672,5 +1779,16 @@ void netlink_linearize_rule(struct netlink_ctx *ctx,
nftnl_udata_buf_free(udata);
}
- netlink_dump_rule(lctx->nlr, ctx);
+ if (ctx->nft->debug_mask & NFT_DEBUG_NETLINK) {
+ nftnl_rule_set_str(lctx->nlr, NFTNL_RULE_TABLE,
+ rule->handle.table.name);
+ if (rule->handle.chain.name)
+ nftnl_rule_set_str(lctx->nlr, NFTNL_RULE_CHAIN,
+ rule->handle.chain.name);
+
+ netlink_dump_rule(lctx->nlr, ctx);
+
+ nftnl_rule_unset(lctx->nlr, NFTNL_RULE_CHAIN);
+ nftnl_rule_unset(lctx->nlr, NFTNL_RULE_TABLE);
+ }
}
diff --git a/src/nfnl_osf.c b/src/nfnl_osf.c
index 08e978de..20a1bfe7 100644
--- a/src/nfnl_osf.c
+++ b/src/nfnl_osf.c
@@ -19,12 +19,12 @@
* Based on iptables/utils/nfnl_osf.c.
*/
+#include <nft.h>
+
#include <sys/time.h>
#include <ctype.h>
#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
#include <time.h>
#include <netinet/ip.h>
diff --git a/src/nftutils.c b/src/nftutils.c
new file mode 100644
index 00000000..ca178aa0
--- /dev/null
+++ b/src/nftutils.c
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <nft.h>
+
+#include "nftutils.h"
+
+#include <netdb.h>
+
+/* Buffer size used for getprotobynumber_r() and similar. The manual comments
+ * that a buffer of 1024 should be sufficient "for most applications"(??), so
+ * let's double it. It still fits reasonably on the stack, so no need to
+ * choose a smaller one. */
+#define NETDB_BUFSIZE 2048
+
+bool nft_getprotobynumber(int proto, char *out_name, size_t name_len)
+{
+ const struct protoent *result;
+
+#if HAVE_DECL_GETPROTOBYNUMBER_R
+ struct protoent result_buf;
+ char buf[NETDB_BUFSIZE];
+ int r;
+
+ r = getprotobynumber_r(proto,
+ &result_buf,
+ buf,
+ sizeof(buf),
+ (struct protoent **) &result);
+ if (r != 0 || result != &result_buf)
+ result = NULL;
+#else
+ result = getprotobynumber(proto);
+#endif
+
+ if (!result)
+ return false;
+
+ if (strlen(result->p_name) >= name_len)
+ return false;
+ strcpy(out_name, result->p_name);
+ return true;
+}
+
+int nft_getprotobyname(const char *name)
+{
+ const struct protoent *result;
+
+#if HAVE_DECL_GETPROTOBYNAME_R
+ struct protoent result_buf;
+ char buf[NETDB_BUFSIZE];
+ int r;
+
+ r = getprotobyname_r(name,
+ &result_buf,
+ buf,
+ sizeof(buf),
+ (struct protoent **) &result);
+ if (r != 0 || result != &result_buf)
+ result = NULL;
+#else
+ result = getprotobyname(name);
+#endif
+
+ if (!result)
+ return -1;
+
+ if (result->p_proto < 0 || result->p_proto > UINT8_MAX)
+ return -1;
+ return (uint8_t) result->p_proto;
+}
+
+bool nft_getservbyport(int port, const char *proto, char *out_name, size_t name_len)
+{
+ const struct servent *result;
+
+#if HAVE_DECL_GETSERVBYPORT_R
+ struct servent result_buf;
+ char buf[NETDB_BUFSIZE];
+ int r;
+
+ r = getservbyport_r(port,
+ proto,
+ &result_buf,
+ buf,
+ sizeof(buf),
+ (struct servent**) &result);
+ if (r != 0 || result != &result_buf)
+ result = NULL;
+#else
+ result = getservbyport(port, proto);
+#endif
+
+ if (!result)
+ return false;
+
+ if (strlen(result->s_name) >= name_len)
+ return false;
+ strcpy(out_name, result->s_name);
+ return true;
+}
diff --git a/src/nftutils.h b/src/nftutils.h
new file mode 100644
index 00000000..7db56f42
--- /dev/null
+++ b/src/nftutils.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef NFTUTILS_H
+#define NFTUTILS_H
+
+#include <stddef.h>
+
+/* The maximum buffer size for (struct protoent).p_name. It is excessively large,
+ * while still reasonably fitting on the stack. Arbitrarily chosen. */
+#define NFT_PROTONAME_MAXSIZE 1024
+
+bool nft_getprotobynumber(int number, char *out_name, size_t name_len);
+int nft_getprotobyname(const char *name);
+
+/* The maximum buffer size for (struct servent).s_name. It is excessively large,
+ * while still reasonably fitting on the stack. Arbitrarily chosen. */
+#define NFT_SERVNAME_MAXSIZE 1024
+
+bool nft_getservbyport(int port, const char *proto, char *out_name, size_t name_len);
+
+#endif /* NFTUTILS_H */
diff --git a/src/numgen.c b/src/numgen.c
index ea2b2626..3029fa58 100644
--- a/src/numgen.c
+++ b/src/numgen.c
@@ -4,10 +4,12 @@
* Copyright (c) 2016 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 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <nftables.h>
#include <expression.h>
#include <datatype.h>
diff --git a/src/optimize.c b/src/optimize.c
new file mode 100644
index 00000000..b90dd995
--- /dev/null
+++ b/src/optimize.c
@@ -0,0 +1,1406 @@
+/*
+ * Copyright (c) 2021 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.
+ */
+
+/* Funded through the NGI0 PET Fund established by NLnet (https://nlnet.nl)
+ * with support from the European Commission's Next Generation Internet
+ * programme.
+ */
+
+#include <nft.h>
+
+#include <errno.h>
+#include <inttypes.h>
+#include <nftables.h>
+#include <parser.h>
+#include <expression.h>
+#include <statement.h>
+#include <utils.h>
+#include <erec.h>
+#include <linux/netfilter.h>
+
+#define MAX_STMTS 32
+
+struct optimize_ctx {
+ struct stmt *stmt[MAX_STMTS];
+ uint32_t num_stmts;
+
+ struct stmt ***stmt_matrix;
+ struct rule **rule;
+ uint32_t num_rules;
+};
+
+static bool __expr_cmp(const struct expr *expr_a, const struct expr *expr_b)
+{
+ if (expr_a->etype != expr_b->etype)
+ return false;
+
+ switch (expr_a->etype) {
+ case EXPR_PAYLOAD:
+ if (expr_a->payload.base != expr_b->payload.base)
+ return false;
+ if (expr_a->payload.offset != expr_b->payload.offset)
+ return false;
+ if (expr_a->payload.desc != expr_b->payload.desc)
+ return false;
+ if (expr_a->payload.inner_desc != expr_b->payload.inner_desc)
+ return false;
+ if (expr_a->payload.tmpl != expr_b->payload.tmpl)
+ return false;
+ break;
+ case EXPR_EXTHDR:
+ if (expr_a->exthdr.desc != expr_b->exthdr.desc)
+ return false;
+ if (expr_a->exthdr.tmpl != expr_b->exthdr.tmpl)
+ return false;
+ break;
+ case EXPR_META:
+ if (expr_a->meta.key != expr_b->meta.key)
+ return false;
+ if (expr_a->meta.base != expr_b->meta.base)
+ return false;
+ break;
+ case EXPR_CT:
+ if (expr_a->ct.key != expr_b->ct.key)
+ return false;
+ if (expr_a->ct.base != expr_b->ct.base)
+ return false;
+ if (expr_a->ct.direction != expr_b->ct.direction)
+ return false;
+ if (expr_a->ct.nfproto != expr_b->ct.nfproto)
+ return false;
+ break;
+ case EXPR_RT:
+ if (expr_a->rt.key != expr_b->rt.key)
+ return false;
+ break;
+ case EXPR_SOCKET:
+ if (expr_a->socket.key != expr_b->socket.key)
+ return false;
+ if (expr_a->socket.level != expr_b->socket.level)
+ return false;
+ break;
+ case EXPR_OSF:
+ if (expr_a->osf.ttl != expr_b->osf.ttl)
+ return false;
+ if (expr_a->osf.flags != expr_b->osf.flags)
+ return false;
+ break;
+ case EXPR_XFRM:
+ if (expr_a->xfrm.key != expr_b->xfrm.key)
+ return false;
+ if (expr_a->xfrm.direction != expr_b->xfrm.direction)
+ return false;
+ break;
+ case EXPR_FIB:
+ if (expr_a->fib.flags != expr_b->fib.flags)
+ return false;
+ if (expr_a->fib.result != expr_b->fib.result)
+ return false;
+ break;
+ case EXPR_NUMGEN:
+ if (expr_a->numgen.type != expr_b->numgen.type)
+ return false;
+ if (expr_a->numgen.mod != expr_b->numgen.mod)
+ return false;
+ if (expr_a->numgen.offset != expr_b->numgen.offset)
+ return false;
+ break;
+ case EXPR_HASH:
+ if (expr_a->hash.mod != expr_b->hash.mod)
+ return false;
+ if (expr_a->hash.seed_set != expr_b->hash.seed_set)
+ return false;
+ if (expr_a->hash.seed != expr_b->hash.seed)
+ return false;
+ if (expr_a->hash.offset != expr_b->hash.offset)
+ return false;
+ if (expr_a->hash.type != expr_b->hash.type)
+ return false;
+ break;
+ case EXPR_BINOP:
+ return __expr_cmp(expr_a->left, expr_b->left);
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+static bool stmt_expr_supported(const struct expr *expr)
+{
+ switch (expr->right->etype) {
+ case EXPR_SYMBOL:
+ case EXPR_RANGE:
+ case EXPR_PREFIX:
+ case EXPR_SET:
+ case EXPR_LIST:
+ case EXPR_VALUE:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static bool expr_symbol_set(const struct expr *expr)
+{
+ return expr->right->etype == EXPR_SYMBOL &&
+ expr->right->symtype == SYMBOL_SET;
+}
+
+static bool __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b,
+ bool fully_compare)
+{
+ struct expr *expr_a, *expr_b;
+
+ if (stmt_a->ops->type != stmt_b->ops->type)
+ return false;
+
+ switch (stmt_a->ops->type) {
+ case STMT_EXPRESSION:
+ expr_a = stmt_a->expr;
+ expr_b = stmt_b->expr;
+
+ if (expr_a->op != expr_b->op)
+ return false;
+ if (expr_a->op != OP_IMPLICIT && expr_a->op != OP_EQ)
+ return false;
+
+ if (fully_compare) {
+ if (!stmt_expr_supported(expr_a) ||
+ !stmt_expr_supported(expr_b))
+ return false;
+
+ if (expr_symbol_set(expr_a) ||
+ expr_symbol_set(expr_b))
+ return false;
+ }
+
+ return __expr_cmp(expr_a->left, expr_b->left);
+ case STMT_COUNTER:
+ case STMT_NOTRACK:
+ break;
+ case STMT_VERDICT:
+ if (!fully_compare)
+ break;
+
+ expr_a = stmt_a->expr;
+ expr_b = stmt_b->expr;
+
+ if (expr_a->etype != expr_b->etype)
+ return false;
+
+ if (expr_a->etype == EXPR_MAP &&
+ expr_b->etype == EXPR_MAP)
+ return __expr_cmp(expr_a->map, expr_b->map);
+ break;
+ case STMT_LOG:
+ if (stmt_a->log.snaplen != stmt_b->log.snaplen ||
+ stmt_a->log.group != stmt_b->log.group ||
+ stmt_a->log.qthreshold != stmt_b->log.qthreshold ||
+ stmt_a->log.level != stmt_b->log.level ||
+ stmt_a->log.logflags != stmt_b->log.logflags ||
+ stmt_a->log.flags != stmt_b->log.flags)
+ return false;
+
+ if (!!stmt_a->log.prefix ^ !!stmt_b->log.prefix)
+ return false;
+
+ 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))
+ return false;
+ break;
+ case STMT_REJECT:
+ if (stmt_a->reject.family != stmt_b->reject.family ||
+ stmt_a->reject.type != stmt_b->reject.type ||
+ stmt_a->reject.icmp_code != stmt_b->reject.icmp_code)
+ return false;
+
+ if (!!stmt_a->reject.expr ^ !!stmt_b->reject.expr)
+ return false;
+
+ if (!stmt_a->reject.expr)
+ return true;
+
+ if (__expr_cmp(stmt_a->reject.expr, stmt_b->reject.expr))
+ return false;
+ break;
+ case STMT_NAT:
+ if (stmt_a->nat.type != stmt_b->nat.type ||
+ stmt_a->nat.flags != stmt_b->nat.flags ||
+ stmt_a->nat.family != stmt_b->nat.family ||
+ stmt_a->nat.type_flags != stmt_b->nat.type_flags)
+ return false;
+
+ switch (stmt_a->nat.type) {
+ case NFT_NAT_SNAT:
+ case NFT_NAT_DNAT:
+ if ((stmt_a->nat.addr &&
+ stmt_a->nat.addr->etype != EXPR_SYMBOL &&
+ stmt_a->nat.addr->etype != EXPR_RANGE) ||
+ (stmt_b->nat.addr &&
+ stmt_b->nat.addr->etype != EXPR_SYMBOL &&
+ stmt_b->nat.addr->etype != EXPR_RANGE) ||
+ (stmt_a->nat.proto &&
+ stmt_a->nat.proto->etype != EXPR_SYMBOL &&
+ stmt_a->nat.proto->etype != EXPR_RANGE) ||
+ (stmt_b->nat.proto &&
+ stmt_b->nat.proto->etype != EXPR_SYMBOL &&
+ stmt_b->nat.proto->etype != EXPR_RANGE))
+ return false;
+ break;
+ case NFT_NAT_MASQ:
+ break;
+ case NFT_NAT_REDIR:
+ if ((stmt_a->nat.proto &&
+ stmt_a->nat.proto->etype != EXPR_SYMBOL &&
+ stmt_a->nat.proto->etype != EXPR_RANGE) ||
+ (stmt_b->nat.proto &&
+ stmt_b->nat.proto->etype != EXPR_SYMBOL &&
+ stmt_b->nat.proto->etype != EXPR_RANGE))
+ return false;
+
+ /* it should be possible to infer implicit redirections
+ * such as:
+ *
+ * tcp dport 1234 redirect
+ * tcp dport 3456 redirect to :7890
+ * merge:
+ * redirect to tcp dport map { 1234 : 1234, 3456 : 7890 }
+ *
+ * currently not implemented.
+ */
+ if (fully_compare &&
+ stmt_a->nat.type == NFT_NAT_REDIR &&
+ stmt_b->nat.type == NFT_NAT_REDIR &&
+ (!!stmt_a->nat.proto ^ !!stmt_b->nat.proto))
+ return false;
+
+ break;
+ default:
+ assert(0);
+ }
+
+ return true;
+ default:
+ /* ... Merging anything else is yet unsupported. */
+ return false;
+ }
+
+ return true;
+}
+
+static bool expr_verdict_eq(const struct expr *expr_a, const struct expr *expr_b)
+{
+ if (expr_a->verdict != expr_b->verdict)
+ return false;
+ if (expr_a->chain && expr_b->chain) {
+ if (expr_a->chain->etype != expr_b->chain->etype)
+ return false;
+ if (expr_a->chain->etype == EXPR_VALUE &&
+ strcmp(expr_a->chain->identifier, expr_b->chain->identifier))
+ return false;
+ } else if (expr_a->chain || expr_b->chain) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool stmt_verdict_eq(const struct stmt *stmt_a, const struct stmt *stmt_b)
+{
+ struct expr *expr_a, *expr_b;
+
+ assert (stmt_a->ops->type == STMT_VERDICT);
+
+ expr_a = stmt_a->expr;
+ expr_b = stmt_b->expr;
+ if (expr_a->etype == EXPR_VERDICT &&
+ expr_b->etype == EXPR_VERDICT)
+ return expr_verdict_eq(expr_a, expr_b);
+
+ if (expr_a->etype == EXPR_MAP &&
+ expr_b->etype == EXPR_MAP)
+ return __expr_cmp(expr_a->map, expr_b->map);
+
+ return false;
+}
+
+static bool stmt_type_find(struct optimize_ctx *ctx, const struct stmt *stmt)
+{
+ bool unsupported_exists = false;
+ uint32_t i;
+
+ for (i = 0; i < ctx->num_stmts; i++) {
+ if (ctx->stmt[i]->ops->type == STMT_INVALID)
+ unsupported_exists = true;
+
+ if (__stmt_type_eq(stmt, ctx->stmt[i], false))
+ return true;
+ }
+
+ switch (stmt->ops->type) {
+ case STMT_EXPRESSION:
+ case STMT_VERDICT:
+ case STMT_COUNTER:
+ case STMT_NOTRACK:
+ case STMT_LOG:
+ case STMT_NAT:
+ case STMT_REJECT:
+ break;
+ default:
+ /* add unsupported statement only once to statement matrix. */
+ if (unsupported_exists)
+ return true;
+ break;
+ }
+
+ return false;
+}
+
+static struct stmt_ops unsupported_stmt_ops = {
+ .type = STMT_INVALID,
+ .name = "unsupported",
+};
+
+static int rule_collect_stmts(struct optimize_ctx *ctx, struct rule *rule)
+{
+ struct stmt *stmt, *clone;
+
+ list_for_each_entry(stmt, &rule->stmts, list) {
+ if (stmt_type_find(ctx, stmt))
+ continue;
+
+ /* No refcounter available in statement objects, clone it to
+ * to store in the array of selectors.
+ */
+ clone = stmt_alloc(&internal_location, stmt->ops);
+ switch (stmt->ops->type) {
+ case STMT_EXPRESSION:
+ if (stmt->expr->op != OP_IMPLICIT &&
+ stmt->expr->op != OP_EQ) {
+ clone->ops = &unsupported_stmt_ops;
+ break;
+ }
+ if (stmt->expr->left->etype == EXPR_CONCAT) {
+ clone->ops = &unsupported_stmt_ops;
+ break;
+ }
+ /* fall-through */
+ case STMT_VERDICT:
+ clone->expr = expr_get(stmt->expr);
+ break;
+ case STMT_COUNTER:
+ case STMT_NOTRACK:
+ break;
+ case STMT_LOG:
+ memcpy(&clone->log, &stmt->log, sizeof(clone->log));
+ if (stmt->log.prefix)
+ clone->log.prefix = expr_get(stmt->log.prefix);
+ break;
+ case STMT_NAT:
+ if ((stmt->nat.addr &&
+ stmt->nat.addr->etype == EXPR_MAP) ||
+ (stmt->nat.proto &&
+ stmt->nat.proto->etype == EXPR_MAP)) {
+ clone->ops = &unsupported_stmt_ops;
+ break;
+ }
+ clone->nat.type = stmt->nat.type;
+ clone->nat.family = stmt->nat.family;
+ if (stmt->nat.addr)
+ clone->nat.addr = expr_clone(stmt->nat.addr);
+ if (stmt->nat.proto)
+ clone->nat.proto = expr_clone(stmt->nat.proto);
+ clone->nat.flags = stmt->nat.flags;
+ clone->nat.type_flags = stmt->nat.type_flags;
+ break;
+ case STMT_REJECT:
+ if (stmt->reject.expr)
+ clone->reject.expr = expr_get(stmt->reject.expr);
+ clone->reject.type = stmt->reject.type;
+ clone->reject.icmp_code = stmt->reject.icmp_code;
+ clone->reject.family = stmt->reject.family;
+ break;
+ default:
+ clone->ops = &unsupported_stmt_ops;
+ break;
+ }
+
+ ctx->stmt[ctx->num_stmts++] = clone;
+ if (ctx->num_stmts >= MAX_STMTS)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int unsupported_in_stmt_matrix(const struct optimize_ctx *ctx)
+{
+ uint32_t i;
+
+ for (i = 0; i < ctx->num_stmts; i++) {
+ if (ctx->stmt[i]->ops->type == STMT_INVALID)
+ return i;
+ }
+ /* this should not happen. */
+ return -1;
+}
+
+static int cmd_stmt_find_in_stmt_matrix(struct optimize_ctx *ctx, struct stmt *stmt)
+{
+ uint32_t i;
+
+ for (i = 0; i < ctx->num_stmts; i++) {
+ if (__stmt_type_eq(stmt, ctx->stmt[i], false))
+ return i;
+ }
+
+ return -1;
+}
+
+static struct stmt unsupported_stmt = {
+ .ops = &unsupported_stmt_ops,
+};
+
+static void rule_build_stmt_matrix_stmts(struct optimize_ctx *ctx,
+ struct rule *rule, uint32_t *i)
+{
+ struct stmt *stmt;
+ int k;
+
+ list_for_each_entry(stmt, &rule->stmts, list) {
+ k = cmd_stmt_find_in_stmt_matrix(ctx, stmt);
+ if (k < 0) {
+ k = unsupported_in_stmt_matrix(ctx);
+ assert(k >= 0);
+ ctx->stmt_matrix[*i][k] = &unsupported_stmt;
+ continue;
+ }
+ ctx->stmt_matrix[*i][k] = stmt;
+ }
+ ctx->rule[(*i)++] = rule;
+}
+
+static int stmt_verdict_find(const struct optimize_ctx *ctx)
+{
+ uint32_t i;
+
+ for (i = 0; i < ctx->num_stmts; i++) {
+ if (ctx->stmt[i]->ops->type != STMT_VERDICT)
+ continue;
+
+ return i;
+ }
+
+ return -1;
+}
+
+struct merge {
+ /* interval of rules to be merged */
+ uint32_t rule_from;
+ uint32_t num_rules;
+ /* statements to be merged (index relative to statement matrix) */
+ uint32_t stmt[MAX_STMTS];
+ uint32_t num_stmts;
+};
+
+static void merge_expr_stmts(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to,
+ const struct merge *merge,
+ struct stmt *stmt_a)
+{
+ struct expr *expr_a, *expr_b, *set, *elem;
+ struct stmt *stmt_b;
+ uint32_t i;
+
+ set = set_expr_alloc(&internal_location, NULL);
+ set->set_flags |= NFT_SET_ANONYMOUS;
+
+ expr_a = stmt_a->expr->right;
+ elem = set_elem_expr_alloc(&internal_location, expr_get(expr_a));
+ compound_expr_add(set, elem);
+
+ for (i = from + 1; i <= to; i++) {
+ stmt_b = ctx->stmt_matrix[i][merge->stmt[0]];
+ expr_b = stmt_b->expr->right;
+ elem = set_elem_expr_alloc(&internal_location, expr_get(expr_b));
+ compound_expr_add(set, elem);
+ }
+
+ expr_free(stmt_a->expr->right);
+ stmt_a->expr->right = set;
+}
+
+static void merge_vmap(const struct optimize_ctx *ctx,
+ struct stmt *stmt_a, const struct stmt *stmt_b)
+{
+ struct expr *mappings, *mapping, *expr;
+
+ mappings = stmt_b->expr->mappings;
+ list_for_each_entry(expr, &mappings->expressions, list) {
+ mapping = expr_clone(expr);
+ compound_expr_add(stmt_a->expr->mappings, mapping);
+ }
+}
+
+static void merge_verdict_stmts(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to,
+ const struct merge *merge,
+ struct stmt *stmt_a)
+{
+ struct stmt *stmt_b;
+ uint32_t i;
+
+ for (i = from + 1; i <= to; i++) {
+ stmt_b = ctx->stmt_matrix[i][merge->stmt[0]];
+ switch (stmt_b->ops->type) {
+ case STMT_VERDICT:
+ switch (stmt_b->expr->etype) {
+ case EXPR_MAP:
+ merge_vmap(ctx, stmt_a, stmt_b);
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+}
+
+static void merge_stmts(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to, const struct merge *merge)
+{
+ struct stmt *stmt_a = ctx->stmt_matrix[from][merge->stmt[0]];
+
+ switch (stmt_a->ops->type) {
+ case STMT_EXPRESSION:
+ merge_expr_stmts(ctx, from, to, merge, stmt_a);
+ break;
+ case STMT_VERDICT:
+ merge_verdict_stmts(ctx, from, to, merge, stmt_a);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+static void __merge_concat(const struct optimize_ctx *ctx, uint32_t i,
+ const struct merge *merge, struct list_head *concat_list)
+{
+ struct expr *concat, *next, *expr, *concat_clone, *clone;
+ LIST_HEAD(pending_list);
+ struct stmt *stmt_a;
+ uint32_t k;
+
+ concat = concat_expr_alloc(&internal_location);
+ list_add(&concat->list, concat_list);
+
+ for (k = 0; k < merge->num_stmts; k++) {
+ list_for_each_entry_safe(concat, next, concat_list, list) {
+ stmt_a = ctx->stmt_matrix[i][merge->stmt[k]];
+ switch (stmt_a->expr->right->etype) {
+ case EXPR_SET:
+ list_for_each_entry(expr, &stmt_a->expr->right->expressions, list) {
+ concat_clone = expr_clone(concat);
+ clone = expr_clone(expr->key);
+ compound_expr_add(concat_clone, clone);
+ list_add_tail(&concat_clone->list, &pending_list);
+ }
+ list_del(&concat->list);
+ expr_free(concat);
+ break;
+ case EXPR_SYMBOL:
+ case EXPR_VALUE:
+ case EXPR_PREFIX:
+ case EXPR_RANGE:
+ clone = expr_clone(stmt_a->expr->right);
+ compound_expr_add(concat, clone);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+ list_splice_init(&pending_list, concat_list);
+ }
+}
+
+static void __merge_concat_stmts(const struct optimize_ctx *ctx, uint32_t i,
+ const struct merge *merge, struct expr *set)
+{
+ struct expr *concat, *next, *elem;
+ LIST_HEAD(concat_list);
+
+ __merge_concat(ctx, i, merge, &concat_list);
+
+ list_for_each_entry_safe(concat, next, &concat_list, list) {
+ list_del(&concat->list);
+ elem = set_elem_expr_alloc(&internal_location, concat);
+ compound_expr_add(set, elem);
+ }
+}
+
+static void merge_concat_stmts(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to,
+ const struct merge *merge)
+{
+ struct stmt *stmt, *stmt_a;
+ struct expr *concat, *set;
+ uint32_t i, k;
+
+ stmt = ctx->stmt_matrix[from][merge->stmt[0]];
+ /* build concatenation of selectors, eg. ifname . ip daddr . tcp dport */
+ concat = concat_expr_alloc(&internal_location);
+
+ for (k = 0; k < merge->num_stmts; k++) {
+ stmt_a = ctx->stmt_matrix[from][merge->stmt[k]];
+ compound_expr_add(concat, expr_get(stmt_a->expr->left));
+ }
+ expr_free(stmt->expr->left);
+ stmt->expr->left = concat;
+
+ /* build set data contenation, eg. { eth0 . 1.1.1.1 . 22 } */
+ set = set_expr_alloc(&internal_location, NULL);
+ set->set_flags |= NFT_SET_ANONYMOUS;
+
+ for (i = from; i <= to; i++)
+ __merge_concat_stmts(ctx, i, merge, set);
+
+ expr_free(stmt->expr->right);
+ stmt->expr->right = set;
+
+ for (k = 1; k < merge->num_stmts; k++) {
+ stmt_a = ctx->stmt_matrix[from][merge->stmt[k]];
+ list_del(&stmt_a->list);
+ stmt_free(stmt_a);
+ }
+}
+
+static void build_verdict_map(struct expr *expr, struct stmt *verdict,
+ struct expr *set, struct stmt *counter)
+{
+ struct expr *item, *elem, *mapping;
+
+ 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);
+
+ mapping = mapping_expr_alloc(&internal_location, elem,
+ expr_get(verdict->expr));
+ compound_expr_add(set, mapping);
+ }
+ 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);
+
+ mapping = mapping_expr_alloc(&internal_location, elem,
+ expr_get(verdict->expr));
+ compound_expr_add(set, mapping);
+ }
+ break;
+ case EXPR_PREFIX:
+ case EXPR_RANGE:
+ case EXPR_VALUE:
+ case EXPR_SYMBOL:
+ case EXPR_CONCAT:
+ elem = set_elem_expr_alloc(&internal_location, expr_get(expr));
+ if (counter)
+ list_add_tail(&counter->list, &elem->stmt_list);
+
+ mapping = mapping_expr_alloc(&internal_location, elem,
+ expr_get(verdict->expr));
+ compound_expr_add(set, mapping);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+}
+
+static void remove_counter(const struct optimize_ctx *ctx, uint32_t from)
+{
+ struct stmt *stmt;
+ uint32_t i;
+
+ /* remove counter statement */
+ for (i = 0; i < ctx->num_stmts; i++) {
+ stmt = ctx->stmt_matrix[from][i];
+ if (!stmt)
+ continue;
+
+ if (stmt->ops->type == STMT_COUNTER) {
+ list_del(&stmt->list);
+ stmt_free(stmt);
+ }
+ }
+}
+
+static struct stmt *zap_counter(const struct optimize_ctx *ctx, uint32_t from)
+{
+ struct stmt *stmt;
+ uint32_t i;
+
+ /* remove counter statement */
+ for (i = 0; i < ctx->num_stmts; i++) {
+ stmt = ctx->stmt_matrix[from][i];
+ if (!stmt)
+ continue;
+
+ if (stmt->ops->type == STMT_COUNTER) {
+ list_del(&stmt->list);
+ return stmt;
+ }
+ }
+
+ return NULL;
+}
+
+static void merge_stmts_vmap(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to,
+ const struct merge *merge)
+{
+ struct stmt *stmt_a = ctx->stmt_matrix[from][merge->stmt[0]];
+ struct stmt *stmt_b, *verdict_a, *verdict_b, *stmt;
+ struct expr *expr_a, *expr_b, *expr, *left, *set;
+ struct stmt *counter;
+ uint32_t i;
+ int k;
+
+ k = stmt_verdict_find(ctx);
+ assert(k >= 0);
+
+ set = set_expr_alloc(&internal_location, NULL);
+ set->set_flags |= NFT_SET_ANONYMOUS;
+
+ expr_a = stmt_a->expr->right;
+ verdict_a = ctx->stmt_matrix[from][k];
+ counter = zap_counter(ctx, from);
+ build_verdict_map(expr_a, verdict_a, set, counter);
+
+ for (i = from + 1; i <= to; i++) {
+ stmt_b = ctx->stmt_matrix[i][merge->stmt[0]];
+ expr_b = stmt_b->expr->right;
+ verdict_b = ctx->stmt_matrix[i][k];
+ counter = zap_counter(ctx, i);
+ build_verdict_map(expr_b, verdict_b, set, counter);
+ }
+
+ left = expr_get(stmt_a->expr->left);
+ expr = map_expr_alloc(&internal_location, left, set);
+ stmt = verdict_stmt_alloc(&internal_location, expr);
+
+ list_add(&stmt->list, &stmt_a->list);
+ list_del(&stmt_a->list);
+ stmt_free(stmt_a);
+ list_del(&verdict_a->list);
+ stmt_free(verdict_a);
+}
+
+static void __merge_concat_stmts_vmap(const struct optimize_ctx *ctx,
+ uint32_t i, const struct merge *merge,
+ struct expr *set, struct stmt *verdict)
+{
+ struct expr *concat, *next, *elem, *mapping;
+ LIST_HEAD(concat_list);
+ struct stmt *counter;
+
+ counter = zap_counter(ctx, i);
+ __merge_concat(ctx, i, merge, &concat_list);
+
+ 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);
+
+ mapping = mapping_expr_alloc(&internal_location, elem,
+ expr_get(verdict->expr));
+ compound_expr_add(set, mapping);
+ }
+}
+
+static void merge_concat_stmts_vmap(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to,
+ const struct merge *merge)
+{
+ struct stmt *orig_stmt = ctx->stmt_matrix[from][merge->stmt[0]];
+ struct stmt *stmt, *stmt_a, *verdict;
+ struct expr *concat_a, *expr, *set;
+ uint32_t i;
+ int k;
+
+ k = stmt_verdict_find(ctx);
+ assert(k >= 0);
+
+ /* build concatenation of selectors, eg. ifname . ip daddr . tcp dport */
+ concat_a = concat_expr_alloc(&internal_location);
+ for (i = 0; i < merge->num_stmts; i++) {
+ stmt_a = ctx->stmt_matrix[from][merge->stmt[i]];
+ compound_expr_add(concat_a, expr_get(stmt_a->expr->left));
+ }
+
+ /* build set data contenation, eg. { eth0 . 1.1.1.1 . 22 : accept } */
+ set = set_expr_alloc(&internal_location, NULL);
+ set->set_flags |= NFT_SET_ANONYMOUS;
+
+ for (i = from; i <= to; i++) {
+ verdict = ctx->stmt_matrix[i][k];
+ __merge_concat_stmts_vmap(ctx, i, merge, set, verdict);
+ }
+
+ expr = map_expr_alloc(&internal_location, concat_a, set);
+ stmt = verdict_stmt_alloc(&internal_location, expr);
+
+ list_add(&stmt->list, &orig_stmt->list);
+ list_del(&orig_stmt->list);
+ stmt_free(orig_stmt);
+
+ for (i = 1; i < merge->num_stmts; i++) {
+ stmt_a = ctx->stmt_matrix[from][merge->stmt[i]];
+ list_del(&stmt_a->list);
+ stmt_free(stmt_a);
+ }
+
+ verdict = ctx->stmt_matrix[from][k];
+ list_del(&verdict->list);
+ stmt_free(verdict);
+}
+
+static bool stmt_verdict_cmp(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to)
+{
+ struct stmt *stmt_a, *stmt_b;
+ uint32_t i;
+ int k;
+
+ k = stmt_verdict_find(ctx);
+ if (k < 0)
+ return true;
+
+ for (i = from; i + 1 <= to; i++) {
+ stmt_a = ctx->stmt_matrix[i][k];
+ stmt_b = ctx->stmt_matrix[i + 1][k];
+ if (!stmt_a && !stmt_b)
+ continue;
+ if (!stmt_a || !stmt_b)
+ return false;
+ if (!stmt_verdict_eq(stmt_a, stmt_b))
+ return false;
+ }
+
+ return true;
+}
+
+static int stmt_nat_type(const struct optimize_ctx *ctx, int from,
+ enum nft_nat_etypes *nat_type)
+{
+ uint32_t j;
+
+ for (j = 0; j < ctx->num_stmts; j++) {
+ if (!ctx->stmt_matrix[from][j])
+ continue;
+
+ if (ctx->stmt_matrix[from][j]->ops->type == STMT_NAT) {
+ *nat_type = ctx->stmt_matrix[from][j]->nat.type;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int stmt_nat_find(const struct optimize_ctx *ctx, int from)
+{
+ enum nft_nat_etypes nat_type;
+ uint32_t i;
+
+ if (stmt_nat_type(ctx, from, &nat_type) < 0)
+ return -1;
+
+ for (i = 0; i < ctx->num_stmts; i++) {
+ if (ctx->stmt[i]->ops->type != STMT_NAT ||
+ ctx->stmt[i]->nat.type != nat_type)
+ continue;
+
+ return i;
+ }
+
+ return -1;
+}
+
+static struct expr *stmt_nat_expr(struct stmt *nat_stmt)
+{
+ struct expr *nat_expr;
+
+ assert(nat_stmt->ops->type == STMT_NAT);
+
+ if (nat_stmt->nat.proto) {
+ if (nat_stmt->nat.addr) {
+ nat_expr = concat_expr_alloc(&internal_location);
+ compound_expr_add(nat_expr, expr_get(nat_stmt->nat.addr));
+ compound_expr_add(nat_expr, expr_get(nat_stmt->nat.proto));
+ } else {
+ nat_expr = expr_get(nat_stmt->nat.proto);
+ }
+ expr_free(nat_stmt->nat.proto);
+ nat_stmt->nat.proto = NULL;
+ } else {
+ nat_expr = expr_get(nat_stmt->nat.addr);
+ }
+
+ assert(nat_expr);
+
+ return nat_expr;
+}
+
+static void merge_nat(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to,
+ const struct merge *merge)
+{
+ struct expr *expr, *set, *elem, *nat_expr, *mapping, *left;
+ int k, family = NFPROTO_UNSPEC;
+ struct stmt *stmt, *nat_stmt;
+ uint32_t i;
+
+ k = stmt_nat_find(ctx, from);
+ assert(k >= 0);
+
+ set = set_expr_alloc(&internal_location, NULL);
+ set->set_flags |= NFT_SET_ANONYMOUS;
+
+ for (i = from; i <= to; i++) {
+ stmt = ctx->stmt_matrix[i][merge->stmt[0]];
+ expr = stmt->expr->right;
+
+ nat_stmt = ctx->stmt_matrix[i][k];
+ nat_expr = stmt_nat_expr(nat_stmt);
+
+ elem = set_elem_expr_alloc(&internal_location, expr_get(expr));
+ mapping = mapping_expr_alloc(&internal_location, elem, nat_expr);
+ compound_expr_add(set, mapping);
+ }
+
+ stmt = ctx->stmt_matrix[from][merge->stmt[0]];
+ left = expr_get(stmt->expr->left);
+ if (left->etype == EXPR_PAYLOAD) {
+ if (left->payload.desc == &proto_ip)
+ family = NFPROTO_IPV4;
+ else if (left->payload.desc == &proto_ip6)
+ family = NFPROTO_IPV6;
+ }
+ expr = map_expr_alloc(&internal_location, left, set);
+
+ nat_stmt = ctx->stmt_matrix[from][k];
+ if (nat_stmt->nat.family == NFPROTO_UNSPEC)
+ nat_stmt->nat.family = family;
+
+ expr_free(nat_stmt->nat.addr);
+ if (nat_stmt->nat.type == NFT_NAT_REDIR)
+ nat_stmt->nat.proto = expr;
+ else
+ nat_stmt->nat.addr = expr;
+
+ remove_counter(ctx, from);
+ list_del(&stmt->list);
+ stmt_free(stmt);
+}
+
+static void merge_concat_nat(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to,
+ const struct merge *merge)
+{
+ struct expr *expr, *set, *elem, *nat_expr, *mapping, *left, *concat;
+ int k, family = NFPROTO_UNSPEC;
+ struct stmt *stmt, *nat_stmt;
+ uint32_t i, j;
+
+ k = stmt_nat_find(ctx, from);
+ assert(k >= 0);
+
+ set = set_expr_alloc(&internal_location, NULL);
+ set->set_flags |= NFT_SET_ANONYMOUS;
+
+ for (i = from; i <= to; i++) {
+
+ concat = concat_expr_alloc(&internal_location);
+ for (j = 0; j < merge->num_stmts; j++) {
+ stmt = ctx->stmt_matrix[i][merge->stmt[j]];
+ expr = stmt->expr->right;
+ compound_expr_add(concat, expr_get(expr));
+ }
+
+ nat_stmt = ctx->stmt_matrix[i][k];
+ nat_expr = stmt_nat_expr(nat_stmt);
+
+ elem = set_elem_expr_alloc(&internal_location, concat);
+ mapping = mapping_expr_alloc(&internal_location, elem, nat_expr);
+ compound_expr_add(set, mapping);
+ }
+
+ concat = concat_expr_alloc(&internal_location);
+ for (j = 0; j < merge->num_stmts; j++) {
+ stmt = ctx->stmt_matrix[from][merge->stmt[j]];
+ left = stmt->expr->left;
+ if (left->etype == EXPR_PAYLOAD) {
+ if (left->payload.desc == &proto_ip)
+ family = NFPROTO_IPV4;
+ else if (left->payload.desc == &proto_ip6)
+ family = NFPROTO_IPV6;
+ }
+ compound_expr_add(concat, expr_get(left));
+ }
+ expr = map_expr_alloc(&internal_location, concat, set);
+
+ nat_stmt = ctx->stmt_matrix[from][k];
+ if (nat_stmt->nat.family == NFPROTO_UNSPEC)
+ nat_stmt->nat.family = family;
+
+ expr_free(nat_stmt->nat.addr);
+ nat_stmt->nat.addr = expr;
+
+ remove_counter(ctx, from);
+ for (j = 0; j < merge->num_stmts; j++) {
+ stmt = ctx->stmt_matrix[from][merge->stmt[j]];
+ list_del(&stmt->list);
+ stmt_free(stmt);
+ }
+}
+
+static void rule_optimize_print(struct output_ctx *octx,
+ const struct rule *rule)
+{
+ const struct location *loc = &rule->location;
+ const struct input_descriptor *indesc = loc->indesc;
+ const char *line = "";
+ char buf[1024];
+
+ switch (indesc->type) {
+ case INDESC_BUFFER:
+ case INDESC_CLI:
+ line = indesc->data;
+ *strchrnul(line, '\n') = '\0';
+ break;
+ case INDESC_STDIN:
+ line = indesc->data;
+ line += loc->line_offset;
+ *strchrnul(line, '\n') = '\0';
+ break;
+ case INDESC_FILE:
+ line = line_location(indesc, loc, buf, sizeof(buf));
+ break;
+ case INDESC_INTERNAL:
+ case INDESC_NETLINK:
+ break;
+ default:
+ BUG("invalid input descriptor type %u\n", indesc->type);
+ }
+
+ print_location(octx->error_fp, indesc, loc);
+ fprintf(octx->error_fp, "%s\n", line);
+}
+
+enum {
+ MERGE_BY_VERDICT,
+ MERGE_BY_NAT_MAP,
+ MERGE_BY_NAT,
+};
+
+static uint32_t merge_stmt_type(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to)
+{
+ const struct stmt *stmt;
+ uint32_t i, j;
+
+ for (i = from; i <= to; i++) {
+ for (j = 0; j < ctx->num_stmts; j++) {
+ stmt = ctx->stmt_matrix[i][j];
+ if (!stmt)
+ continue;
+ if (stmt->ops->type == STMT_NAT) {
+ if ((stmt->nat.type == NFT_NAT_REDIR &&
+ !stmt->nat.proto) ||
+ stmt->nat.type == NFT_NAT_MASQ)
+ return MERGE_BY_NAT;
+
+ return MERGE_BY_NAT_MAP;
+ }
+ }
+ }
+
+ /* merge by verdict, even if no verdict is specified. */
+ return MERGE_BY_VERDICT;
+}
+
+static void merge_rules(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to,
+ const struct merge *merge,
+ struct output_ctx *octx)
+{
+ uint32_t merge_type;
+ bool same_verdict;
+ uint32_t i;
+
+ merge_type = merge_stmt_type(ctx, from, to);
+
+ switch (merge_type) {
+ case MERGE_BY_VERDICT:
+ same_verdict = stmt_verdict_cmp(ctx, from, to);
+ if (merge->num_stmts > 1) {
+ if (same_verdict)
+ merge_concat_stmts(ctx, from, to, merge);
+ else
+ merge_concat_stmts_vmap(ctx, from, to, merge);
+ } else {
+ if (same_verdict)
+ merge_stmts(ctx, from, to, merge);
+ else
+ merge_stmts_vmap(ctx, from, to, merge);
+ }
+ break;
+ case MERGE_BY_NAT_MAP:
+ if (merge->num_stmts > 1)
+ merge_concat_nat(ctx, from, to, merge);
+ else
+ merge_nat(ctx, from, to, merge);
+ break;
+ case MERGE_BY_NAT:
+ if (merge->num_stmts > 1)
+ merge_concat_stmts(ctx, from, to, merge);
+ else
+ merge_stmts(ctx, from, to, merge);
+ break;
+ default:
+ assert(0);
+ }
+
+ if (ctx->rule[from]->comment) {
+ free_const(ctx->rule[from]->comment);
+ ctx->rule[from]->comment = NULL;
+ }
+
+ octx->flags |= NFT_CTX_OUTPUT_STATELESS;
+
+ fprintf(octx->error_fp, "Merging:\n");
+ rule_optimize_print(octx, ctx->rule[from]);
+
+ for (i = from + 1; i <= to; i++) {
+ rule_optimize_print(octx, ctx->rule[i]);
+ list_del(&ctx->rule[i]->list);
+ rule_free(ctx->rule[i]);
+ }
+
+ fprintf(octx->error_fp, "into:\n\t");
+ rule_print(ctx->rule[from], octx);
+ fprintf(octx->error_fp, "\n");
+
+ octx->flags &= ~NFT_CTX_OUTPUT_STATELESS;
+}
+
+static bool stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b)
+{
+ if (!stmt_a && !stmt_b)
+ return true;
+ else if (!stmt_a)
+ return false;
+ else if (!stmt_b)
+ return false;
+
+ return __stmt_type_eq(stmt_a, stmt_b, true);
+}
+
+static bool stmt_is_mergeable(const struct stmt *stmt)
+{
+ if (!stmt)
+ return false;
+
+ switch (stmt->ops->type) {
+ case STMT_VERDICT:
+ if (stmt->expr->etype == EXPR_MAP)
+ return true;
+ break;
+ case STMT_EXPRESSION:
+ case STMT_NAT:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static bool rules_eq(const struct optimize_ctx *ctx, int i, int j)
+{
+ uint32_t k, mergeable = 0;
+
+ for (k = 0; k < ctx->num_stmts; k++) {
+ if (stmt_is_mergeable(ctx->stmt_matrix[i][k]))
+ mergeable++;
+
+ if (!stmt_type_eq(ctx->stmt_matrix[i][k], ctx->stmt_matrix[j][k]))
+ return false;
+ }
+
+ if (mergeable == 0)
+ return false;
+
+ return true;
+}
+
+static int chain_optimize(struct nft_ctx *nft, struct list_head *rules)
+{
+ struct optimize_ctx *ctx;
+ uint32_t num_merges = 0;
+ struct merge *merge;
+ uint32_t i, j, m, k;
+ struct rule *rule;
+ int ret;
+
+ ctx = xzalloc(sizeof(*ctx));
+
+ /* Step 1: collect statements in rules */
+ list_for_each_entry(rule, rules, list) {
+ ret = rule_collect_stmts(ctx, rule);
+ if (ret < 0)
+ goto err;
+
+ ctx->num_rules++;
+ }
+
+ ctx->rule = xzalloc(sizeof(*ctx->rule) * ctx->num_rules);
+ ctx->stmt_matrix = xzalloc(sizeof(*ctx->stmt_matrix) * ctx->num_rules);
+ for (i = 0; i < ctx->num_rules; i++)
+ ctx->stmt_matrix[i] = xzalloc_array(MAX_STMTS,
+ sizeof(**ctx->stmt_matrix));
+
+ merge = xzalloc(sizeof(*merge) * ctx->num_rules);
+
+ /* Step 2: Build matrix of statements */
+ i = 0;
+ list_for_each_entry(rule, rules, list)
+ rule_build_stmt_matrix_stmts(ctx, rule, &i);
+
+ /* Step 3: Look for common selectors for possible rule mergers */
+ for (i = 0; i < ctx->num_rules; i++) {
+ for (j = i + 1; j < ctx->num_rules; j++) {
+ if (!rules_eq(ctx, i, j)) {
+ if (merge[num_merges].num_rules > 0)
+ num_merges++;
+
+ i = j - 1;
+ break;
+ }
+ if (merge[num_merges].num_rules > 0) {
+ merge[num_merges].num_rules++;
+ } else {
+ merge[num_merges].rule_from = i;
+ merge[num_merges].num_rules = 2;
+ }
+ }
+ if (j == ctx->num_rules && merge[num_merges].num_rules > 0) {
+ num_merges++;
+ break;
+ }
+ }
+
+ /* Step 4: Infer how to merge the candidate rules */
+ for (k = 0; k < num_merges; k++) {
+ i = merge[k].rule_from;
+
+ for (m = 0; m < ctx->num_stmts; m++) {
+ if (!ctx->stmt_matrix[i][m])
+ continue;
+ switch (ctx->stmt_matrix[i][m]->ops->type) {
+ case STMT_EXPRESSION:
+ merge[k].stmt[merge[k].num_stmts++] = m;
+ break;
+ case STMT_VERDICT:
+ if (ctx->stmt_matrix[i][m]->expr->etype == EXPR_MAP)
+ merge[k].stmt[merge[k].num_stmts++] = m;
+ break;
+ default:
+ break;
+ }
+ }
+
+ j = merge[k].num_rules - 1;
+ merge_rules(ctx, i, i + j, &merge[k], &nft->output);
+ }
+ ret = 0;
+ for (i = 0; i < ctx->num_rules; i++)
+ free(ctx->stmt_matrix[i]);
+
+ free(ctx->stmt_matrix);
+ free(merge);
+err:
+ for (i = 0; i < ctx->num_stmts; i++)
+ stmt_free(ctx->stmt[i]);
+
+ free(ctx->rule);
+ free(ctx);
+
+ return ret;
+}
+
+static int cmd_optimize(struct nft_ctx *nft, struct cmd *cmd)
+{
+ struct table *table;
+ struct chain *chain;
+ int ret = 0;
+
+ switch (cmd->obj) {
+ case CMD_OBJ_TABLE:
+ table = cmd->table;
+ if (!table)
+ break;
+
+ list_for_each_entry(chain, &table->chains, list) {
+ if (chain->flags & CHAIN_F_HW_OFFLOAD)
+ continue;
+
+ chain_optimize(nft, &chain->rules);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+int nft_optimize(struct nft_ctx *nft, struct list_head *cmds)
+{
+ struct cmd *cmd;
+ int ret = 0;
+
+ list_for_each_entry(cmd, cmds, list) {
+ switch (cmd->op) {
+ case CMD_ADD:
+ ret = cmd_optimize(nft, cmd);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return ret;
+}
diff --git a/src/osf.c b/src/osf.c
index cb58315d..a8f80b2b 100644
--- a/src/osf.c
+++ b/src/osf.c
@@ -1,7 +1,16 @@
+/*
+ * Copyright (c) 2018 Fernando Fernandez Mancera <ffmancera@riseup.net>
+ *
+ * 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 <nft.h>
+
#include <nftables.h>
#include <expression.h>
#include <utils.h>
-#include <string.h>
#include <osf.h>
#include <json.h>
diff --git a/src/owner.c b/src/owner.c
index 2d98a2e9..65eaad3e 100644
--- a/src/owner.c
+++ b/src/owner.c
@@ -1,6 +1,15 @@
+/*
+ * Copyright (c) 2021 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 <nft.h>
+
#include <stdio.h>
#include <unistd.h>
-#include <stdlib.h>
#include <sys/time.h>
#include <time.h>
#include <inttypes.h>
@@ -66,7 +75,7 @@ static char *portid2name(pid_t pid, uint32_t portid, unsigned long inode)
continue;
rl = readlink(procname, tmp, sizeof(tmp));
- if (rl <= 0 || rl > (ssize_t)sizeof(tmp))
+ if (rl <= 0 || rl >= (ssize_t)sizeof(tmp))
continue;
tmp[rl] = 0;
diff --git a/src/parser_bison.y b/src/parser_bison.y
index c25af6ba..61bed761 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -9,12 +9,14 @@
*/
%{
+#include <nft.h>
#include <ctype.h>
#include <stddef.h>
#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>
@@ -65,16 +67,26 @@ static struct scope *current_scope(const struct parser_state *state)
return state->scopes[state->scope];
}
-static void open_scope(struct parser_state *state, struct scope *scope)
+static int open_scope(struct parser_state *state, struct scope *scope)
{
- assert(state->scope < array_size(state->scopes) - 1);
+ if (state->scope >= array_size(state->scopes) - 1) {
+ state->scope_err = true;
+ return -1;
+ }
+
scope_init(scope, current_scope(state));
state->scopes[++state->scope] = scope;
+
+ return 0;
}
static void close_scope(struct parser_state *state)
{
- assert(state->scope > 0);
+ if (state->scope_err || state->scope == 0) {
+ state->scope_err = false;
+ return;
+ }
+
state->scope--;
}
@@ -134,6 +146,51 @@ static bool already_set(const void *attr, const struct location *loc,
return true;
}
+static struct expr *ifname_expr_alloc(const struct location *location,
+ struct list_head *queue,
+ const char *name)
+{
+ size_t length = strlen(name);
+ struct expr *expr;
+
+ if (length == 0) {
+ free_const(name);
+ erec_queue(error(location, "empty interface name"), queue);
+ return NULL;
+ }
+
+ if (length >= IFNAMSIZ) {
+ free_const(name);
+ erec_queue(error(location, "interface name too long"), queue);
+ return NULL;
+ }
+
+ expr = constant_expr_alloc(location, &ifname_type, BYTEORDER_HOST_ENDIAN,
+ length * BITS_PER_BYTE, name);
+
+ free_const(name);
+
+ 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) \
@@ -186,6 +243,12 @@ int nft_lex(void *, void *, void *);
struct handle_spec handle_spec;
struct position_spec position_spec;
struct prio_spec prio_spec;
+ struct limit_rate limit_rate;
+ struct tcp_kind_field {
+ 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"
@@ -275,6 +338,8 @@ int nft_lex(void *, void *, void *);
%token DESCRIBE "describe"
%token IMPORT "import"
%token EXPORT "export"
+%token DESTROY "destroy"
+
%token MONITOR "monitor"
%token ALL "all"
@@ -312,7 +377,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"
@@ -377,6 +442,7 @@ int nft_lex(void *, void *, void *);
%token ICMP6 "icmpv6"
%token PPTR "param-problem"
%token MAXDELAY "max-delay"
+%token TADDR "taddr"
%token AH "ah"
%token RESERVED "reserved"
@@ -403,6 +469,7 @@ int nft_lex(void *, void *, void *);
%token OPTION "option"
%token ECHO "echo"
%token EOL "eol"
+%token MPTCP "mptcp"
%token NOP "nop"
%token SACK "sack"
%token SACK0 "sack0"
@@ -410,16 +477,26 @@ int nft_lex(void *, void *, void *);
%token SACK2 "sack2"
%token SACK3 "sack3"
%token SACK_PERM "sack-permitted"
+%token FASTOPEN "fastopen"
+%token MD5SIG "md5sig"
%token TIMESTAMP "timestamp"
-%token KIND "kind"
%token COUNT "count"
%token LEFT "left"
%token RIGHT "right"
%token TSVAL "tsval"
%token TSECR "tsecr"
+%token SUBTYPE "subtype"
%token DCCP "dccp"
+%token VXLAN "vxlan"
+%token VNI "vni"
+
+%token GRE "gre"
+%token GRETAP "gretap"
+
+%token GENEVE "geneve"
+
%token SCTP "sctp"
%token CHUNK "chunk"
%token DATA "data"
@@ -525,6 +602,9 @@ int nft_lex(void *, void *, void *);
%token BYTES "bytes"
%token AVGPKT "avgpkt"
+%token LAST "last"
+%token NEVER "never"
+
%token COUNTERS "counters"
%token QUOTAS "quotas"
%token LIMITS "limits"
@@ -607,10 +687,15 @@ int nft_lex(void *, void *, void *);
%token IN "in"
%token OUT "out"
+%token XT "xt"
+
+%type <limit_rate> limit_rate_pkts
+%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 quota_used
+%type <val> time_spec time_spec_or_num_s quota_used
%type <expr> data_type_expr data_type_atom_expr
%destructor { expr_free($$); } data_type_expr data_type_atom_expr
@@ -618,8 +703,8 @@ int nft_lex(void *, void *, void *);
%type <cmd> line
%destructor { cmd_free($$); } line
-%type <cmd> base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd get_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd
-%destructor { cmd_free($$); } base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd get_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd
+%type <cmd> base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd get_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd destroy_cmd
+%destructor { cmd_free($$); } base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd get_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd destroy_cmd
%type <handle> table_spec tableid_spec table_or_id_spec
%destructor { handle_free(&$$); } table_spec tableid_spec table_or_id_spec
@@ -642,11 +727,13 @@ int nft_lex(void *, void *, void *);
%type <val> family_spec family_spec_explicit
%type <val32> int_num chain_policy
%type <prio_spec> extended_prio_spec prio_spec
+%destructor { expr_free($$.expr); } 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
+%destructor { free($$); } dev_spec
%type <table> table_block_alloc table_block
%destructor { close_scope(state); table_free($$); } table_block_alloc
@@ -664,7 +751,7 @@ int nft_lex(void *, void *, void *);
%type <set> map_block_alloc map_block
%destructor { set_free($$); } map_block_alloc
-%type <val> map_block_obj_type
+%type <val> map_block_obj_type map_block_obj_typeof map_block_data_interval
%type <flowtable> flowtable_block_alloc flowtable_block
%destructor { flowtable_free($$); } flowtable_block_alloc
@@ -673,11 +760,14 @@ 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
-%destructor { stmt_free($$); } counter_stmt counter_stmt_alloc stateful_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
@@ -689,7 +779,7 @@ int nft_lex(void *, void *, void *);
%type <val> level_type log_flags log_flags_tcp log_flag_tcp
%type <stmt> limit_stmt quota_stmt connlimit_stmt
%destructor { stmt_free($$); } limit_stmt quota_stmt connlimit_stmt
-%type <val> limit_burst_pkts limit_burst_bytes limit_mode time_unit quota_mode
+%type <val> limit_burst_pkts limit_burst_bytes limit_mode limit_bytes time_unit quota_mode
%type <stmt> reject_stmt reject_stmt_alloc
%destructor { stmt_free($$); } reject_stmt reject_stmt_alloc
%type <stmt> nat_stmt nat_stmt_alloc masq_stmt masq_stmt_alloc redir_stmt redir_stmt_alloc
@@ -722,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_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
@@ -793,6 +883,8 @@ int nft_lex(void *, void *, void *);
%type <expr> payload_expr payload_raw_expr
%destructor { expr_free($$); } payload_expr payload_raw_expr
%type <val> payload_base_spec
+%type <val> payload_raw_len
+
%type <expr> eth_hdr_expr vlan_hdr_expr
%destructor { expr_free($$); } eth_hdr_expr vlan_hdr_expr
%type <val> eth_hdr_field vlan_hdr_field
@@ -862,7 +954,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
@@ -870,7 +962,23 @@ int nft_lex(void *, void *, void *);
%type <expr> tcp_hdr_expr
%destructor { expr_free($$); } tcp_hdr_expr
%type <val> tcp_hdr_field
-%type <val> tcp_hdr_option_type tcp_hdr_option_field
+%type <val> tcp_hdr_option_type
+%type <val> tcp_hdr_option_sack
+%type <val> tcpopt_field_maxseg tcpopt_field_mptcp tcpopt_field_sack tcpopt_field_tsopt tcpopt_field_window
+%type <tcp_kind_field> tcp_hdr_option_kind_and_field
+
+%type <expr> inner_eth_expr inner_inet_expr inner_expr
+%destructor { expr_free($$); } inner_eth_expr inner_inet_expr inner_expr
+
+%type <expr> vxlan_hdr_expr geneve_hdr_expr gre_hdr_expr gretap_hdr_expr
+%destructor { expr_free($$); } vxlan_hdr_expr geneve_hdr_expr gre_hdr_expr gretap_hdr_expr
+%type <val> vxlan_hdr_field geneve_hdr_field gre_hdr_field
+
+%type <stmt> optstrip_stmt
+%destructor { stmt_free($$); } optstrip_stmt
+
+%type <stmt> xt_stmt
+%destructor { stmt_free($$); } xt_stmt
%type <expr> boolean_expr
%destructor { expr_free($$); } boolean_expr
@@ -880,10 +988,13 @@ int nft_lex(void *, void *, void *);
%destructor { expr_free($$); } exthdr_exists_expr
%type <val> exthdr_key
-%type <val> ct_l4protoname ct_obj_type ct_cmd_type
+%type <val> ct_l4protoname ct_obj_type ct_cmd_type ct_obj_type_map
-%type <list> timeout_states timeout_state
-%destructor { xfree($$); } 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
@@ -912,36 +1023,69 @@ opt_newline : NEWLINE
| /* empty */
;
+close_scope_ah : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_AH); };
close_scope_arp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_ARP); };
+close_scope_at : { scanner_pop_start_cond(nft->scanner, PARSER_SC_AT); };
+close_scope_comp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_COMP); };
close_scope_ct : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CT); };
close_scope_counter : { scanner_pop_start_cond(nft->scanner, PARSER_SC_COUNTER); };
+close_scope_last : { scanner_pop_start_cond(nft->scanner, PARSER_SC_LAST); };
+close_scope_dccp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_DCCP); };
+close_scope_destroy : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_DESTROY); };
+close_scope_dst : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_DST); };
+close_scope_dup : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_DUP); };
+close_scope_esp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_ESP); };
close_scope_eth : { scanner_pop_start_cond(nft->scanner, PARSER_SC_ETH); };
+close_scope_export : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_EXPORT); };
close_scope_fib : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_FIB); };
+close_scope_frag : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_FRAG); };
+close_scope_fwd : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_FWD); };
+close_scope_gre : { scanner_pop_start_cond(nft->scanner, PARSER_SC_GRE); };
close_scope_hash : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_HASH); };
+close_scope_hbh : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_HBH); };
close_scope_ip : { scanner_pop_start_cond(nft->scanner, PARSER_SC_IP); };
close_scope_ip6 : { scanner_pop_start_cond(nft->scanner, PARSER_SC_IP6); };
close_scope_vlan : { scanner_pop_start_cond(nft->scanner, PARSER_SC_VLAN); };
+close_scope_icmp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_ICMP); };
+close_scope_igmp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_IGMP); };
+close_scope_import : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_IMPORT); };
close_scope_ipsec : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_IPSEC); };
close_scope_list : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_LIST); };
close_scope_limit : { scanner_pop_start_cond(nft->scanner, PARSER_SC_LIMIT); };
+close_scope_meta : { scanner_pop_start_cond(nft->scanner, PARSER_SC_META); };
+close_scope_mh : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_MH); };
+close_scope_monitor : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_MONITOR); };
+close_scope_nat : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_NAT); };
close_scope_numgen : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_NUMGEN); };
+close_scope_osf : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_OSF); };
+close_scope_policy : { scanner_pop_start_cond(nft->scanner, PARSER_SC_POLICY); };
close_scope_quota : { scanner_pop_start_cond(nft->scanner, PARSER_SC_QUOTA); };
close_scope_queue : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_QUEUE); };
+close_scope_reject : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_REJECT); };
+close_scope_reset : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_RESET); };
close_scope_rt : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_RT); };
close_scope_sctp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_SCTP); };
close_scope_sctp_chunk : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_SCTP_CHUNK); };
close_scope_secmark : { scanner_pop_start_cond(nft->scanner, PARSER_SC_SECMARK); };
close_scope_socket : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_SOCKET); }
+close_scope_tcp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_TCP); };
+close_scope_tproxy : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_TPROXY); };
+close_scope_type : { scanner_pop_start_cond(nft->scanner, PARSER_SC_TYPE); };
+close_scope_th : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_TH); };
+close_scope_udp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_UDP); };
+close_scope_udplite : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_UDPLITE); };
close_scope_log : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_LOG); }
+close_scope_synproxy : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_SYNPROXY); }
+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
{
@@ -951,19 +1095,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
{
@@ -972,10 +1116,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
{
@@ -1016,13 +1160,14 @@ base_cmd : /* empty */ add_cmd { $$ = $1; }
| DELETE delete_cmd { $$ = $2; }
| GET get_cmd { $$ = $2; }
| LIST list_cmd close_scope_list { $$ = $2; }
- | RESET reset_cmd { $$ = $2; }
+ | RESET reset_cmd close_scope_reset { $$ = $2; }
| FLUSH flush_cmd { $$ = $2; }
| RENAME rename_cmd { $$ = $2; }
- | IMPORT import_cmd { $$ = $2; }
- | EXPORT export_cmd { $$ = $2; }
- | MONITOR monitor_cmd { $$ = $2; }
+ | IMPORT import_cmd close_scope_import { $$ = $2; }
+ | EXPORT export_cmd close_scope_export { $$ = $2; }
+ | MONITOR monitor_cmd close_scope_monitor { $$ = $2; }
| DESCRIBE describe_cmd { $$ = $2; }
+ | DESTROY destroy_cmd close_scope_destroy { $$ = $2; }
;
add_cmd : TABLE table_spec
@@ -1134,11 +1279,11 @@ add_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_ADD, CMD_OBJ_SECMARK, &$2, &@$, $3);
}
- | SYNPROXY obj_spec synproxy_obj synproxy_config
+ | SYNPROXY obj_spec synproxy_obj synproxy_config close_scope_synproxy
{
$$ = cmd_alloc(CMD_ADD, CMD_OBJ_SYNPROXY, &$2, &@$, $3);
}
- | SYNPROXY obj_spec synproxy_obj '{' synproxy_block '}'
+ | SYNPROXY obj_spec synproxy_obj '{' synproxy_block '}' close_scope_synproxy
{
$$ = cmd_alloc(CMD_ADD, CMD_OBJ_SYNPROXY, &$2, &@$, $3);
}
@@ -1235,7 +1380,7 @@ create_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SECMARK, &$2, &@$, $3);
}
- | SYNPROXY obj_spec synproxy_obj synproxy_config
+ | SYNPROXY obj_spec synproxy_obj synproxy_config close_scope_synproxy
{
$$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SYNPROXY, &$2, &@$, $3);
}
@@ -1271,6 +1416,14 @@ delete_cmd : TABLE table_or_id_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_CHAIN, &$2, &@$, NULL);
}
+ | CHAIN chain_spec chain_block_alloc
+ '{' chain_block '}'
+ {
+ $5->location = @5;
+ handle_merge(&$3->handle, &$2);
+ close_scope(state);
+ $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_CHAIN, &$2, &@$, $5);
+ }
| RULE ruleid_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_RULE, &$2, &@$, NULL);
@@ -1324,12 +1477,80 @@ delete_cmd : TABLE table_or_id_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SECMARK, &$2, &@$, NULL);
}
- | SYNPROXY obj_or_id_spec
+ | SYNPROXY obj_or_id_spec close_scope_synproxy
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
}
;
+destroy_cmd : TABLE table_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_TABLE, &$2, &@$, NULL);
+ }
+ | CHAIN chain_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_CHAIN, &$2, &@$, NULL);
+ }
+ | RULE ruleid_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_RULE, &$2, &@$, NULL);
+ }
+ | SET set_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SET, &$2, &@$, NULL);
+ }
+ | MAP set_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SET, &$2, &@$, NULL);
+ }
+ | ELEMENT set_spec set_block_expr
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
+ }
+ | FLOWTABLE flowtable_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL);
+ }
+ | FLOWTABLE flowtableid_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL);
+ }
+ | FLOWTABLE flowtable_spec flowtable_block_alloc
+ '{' flowtable_block '}'
+ {
+ $5->location = @5;
+ handle_merge(&$3->handle, &$2);
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_FLOWTABLE, &$2, &@$, $5);
+ }
+ | COUNTER obj_or_id_spec close_scope_counter
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_COUNTER, &$2, &@$, NULL);
+ }
+ | QUOTA obj_or_id_spec close_scope_quota
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_QUOTA, &$2, &@$, NULL);
+ }
+ | CT ct_obj_type obj_spec ct_obj_alloc close_scope_ct
+ {
+ $$ = cmd_alloc_obj_ct(CMD_DESTROY, $2, &$3, &@$, $4);
+ if ($2 == NFT_OBJECT_CT_TIMEOUT)
+ init_list_head(&$4->ct_timeout.timeout_list);
+ }
+ | LIMIT obj_or_id_spec close_scope_limit
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_LIMIT, &$2, &@$, NULL);
+ }
+ | SECMARK obj_or_id_spec close_scope_secmark
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SECMARK, &$2, &@$, NULL);
+ }
+ | SYNPROXY obj_or_id_spec close_scope_synproxy
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
+ }
+ ;
+
+
get_cmd : ELEMENT set_spec set_block_expr
{
$$ = cmd_alloc(CMD_GET, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
@@ -1420,7 +1641,7 @@ list_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXYS, &$3, &@$, NULL);
}
- | SYNPROXY obj_spec
+ | SYNPROXY obj_spec close_scope_synproxy
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
}
@@ -1498,8 +1719,13 @@ reset_cmd : COUNTERS ruleset_spec
{
$$ = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTERS, &$2, &@$, NULL);
}
+ | COUNTERS table_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTERS, &$2, &@$, NULL);
+ }
| COUNTERS TABLE table_spec
{
+ /* alias of previous rule. */
$$ = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTERS, &$3, &@$, NULL);
}
| COUNTER obj_spec close_scope_counter
@@ -1514,10 +1740,53 @@ reset_cmd : COUNTERS ruleset_spec
{
$$ = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTAS, &$3, &@$, NULL);
}
+ | QUOTAS table_spec
+ {
+ /* alias of previous rule. */
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTAS, &$2, &@$, NULL);
+ }
| QUOTA obj_spec close_scope_quota
{
$$ = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTA, &$2, &@$, NULL);
}
+ | RULES ruleset_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$2, &@$, NULL);
+ }
+ | RULES table_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$2, &@$, NULL);
+ }
+ | RULES TABLE table_spec
+ {
+ /* alias of previous rule. */
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$3, &@$, NULL);
+ }
+ | RULES chain_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$2, &@$, NULL);
+ }
+ | RULES CHAIN chain_spec
+ {
+ /* alias of previous rule. */
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$3, &@$, NULL);
+ }
+ | RULE ruleid_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULE, &$2, &@$, NULL);
+ }
+ | ELEMENT set_spec set_block_expr
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
+ }
+ | SET set_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_SET, &$2, &@$, NULL);
+ }
+ | MAP set_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_MAP, &$2, &@$, NULL);
+ }
;
flush_cmd : TABLE table_spec
@@ -1628,7 +1897,11 @@ describe_cmd : primary_expr
table_block_alloc : /* empty */
{
$$ = table_alloc();
- open_scope(state, &$$->scope);
+ if (open_scope(state, &$$->scope) < 0) {
+ erec_queue(error(&@$, "too many levels of nesting"),
+ state->msgs);
+ state->nerrs++;
+ }
}
;
@@ -1636,21 +1909,21 @@ table_options : FLAGS STRING
{
if (strcmp($2, "dormant") == 0) {
$<table>0->flags |= TABLE_F_DORMANT;
- xfree($2);
+ free_const($2);
} else if (strcmp($2, "owner") == 0) {
$<table>0->flags |= TABLE_F_OWNER;
- xfree($2);
+ free_const($2);
} else {
erec_queue(error(&@2, "unknown table option %s", $2),
state->msgs);
- xfree($2);
+ free_const($2);
YYERROR;
}
}
| comment_spec
{
if (already_set($<table>0->comment, &@$, state)) {
- xfree($1);
+ free_const($1);
YYERROR;
}
$<table>0->comment = $1;
@@ -1725,7 +1998,7 @@ table_block : /* empty */ { $$ = $<table>-1; }
list_add_tail(&$4->list, &$1->objs);
$$ = $1;
}
- | table_block CT HELPER obj_identifier obj_block_alloc '{' ct_helper_block '}' close_scope_ct stmt_separator
+ | table_block CT HELPER obj_identifier obj_block_alloc '{' ct_helper_block '}' stmt_separator close_scope_ct
{
$5->location = @4;
$5->type = NFT_OBJECT_CT_HELPER;
@@ -1734,7 +2007,7 @@ table_block : /* empty */ { $$ = $<table>-1; }
list_add_tail(&$5->list, &$1->objs);
$$ = $1;
}
- | table_block CT TIMEOUT obj_identifier obj_block_alloc '{' ct_timeout_block '}' close_scope_ct stmt_separator
+ | table_block CT TIMEOUT obj_identifier obj_block_alloc '{' ct_timeout_block '}' stmt_separator close_scope_ct
{
$5->location = @4;
$5->type = NFT_OBJECT_CT_TIMEOUT;
@@ -1743,7 +2016,7 @@ table_block : /* empty */ { $$ = $<table>-1; }
list_add_tail(&$5->list, &$1->objs);
$$ = $1;
}
- | table_block CT EXPECTATION obj_identifier obj_block_alloc '{' ct_expect_block '}' close_scope_ct stmt_separator
+ | table_block CT EXPECTATION obj_identifier obj_block_alloc '{' ct_expect_block '}' stmt_separator close_scope_ct
{
$5->location = @4;
$5->type = NFT_OBJECT_CT_EXPECT;
@@ -1776,7 +2049,7 @@ table_block : /* empty */ { $$ = $<table>-1; }
}
| table_block SYNPROXY obj_identifier
obj_block_alloc '{' synproxy_block '}'
- stmt_separator
+ stmt_separator close_scope_synproxy
{
$4->location = @3;
$4->type = NFT_OBJECT_SYNPROXY;
@@ -1789,8 +2062,12 @@ table_block : /* empty */ { $$ = $<table>-1; }
chain_block_alloc : /* empty */
{
- $$ = chain_alloc(NULL);
- open_scope(state, &$$->scope);
+ $$ = chain_alloc();
+ if (open_scope(state, &$$->scope) < 0) {
+ erec_queue(error(&@$, "too many levels of nesting"),
+ state->msgs);
+ state->nerrs++;
+ }
}
;
@@ -1805,10 +2082,19 @@ chain_block : /* empty */ { $$ = $<chain>-1; }
list_add_tail(&$2->list, &$1->rules);
$$ = $1;
}
+ | chain_block DEVICES '=' flowtable_expr stmt_separator
+ {
+ if ($$->dev_expr) {
+ list_splice_init(&$4->expressions, &$$->dev_expr->expressions);
+ expr_free($4);
+ break;
+ }
+ $$->dev_expr = $4;
+ }
| chain_block comment_spec stmt_separator
{
if (already_set($1->comment, &@2, state)) {
- xfree($2);
+ free_const($2);
YYERROR;
}
$1->comment = $2;
@@ -1824,7 +2110,7 @@ subchain_block : /* empty */ { $$ = $<chain>-1; }
}
;
-typeof_data_expr : primary_expr
+typeof_verdict_expr : primary_expr
{
struct expr *e = $1;
@@ -1856,6 +2142,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) {
@@ -1881,22 +2178,25 @@ typeof_expr : primary_expr
set_block_alloc : /* empty */
{
- $$ = set_alloc(NULL);
+ $$ = set_alloc(&internal_location);
}
;
+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
+ | set_block typeof_key_expr stmt_separator
{
- $1->key = $3;
- $$ = $1;
- }
- | set_block TYPEOF typeof_expr stmt_separator
- {
- $1->key = $3;
- datatype_set($1->key, $3->dtype);
+ if (already_set($1->key, &@2, state)) {
+ expr_free($2);
+ YYERROR;
+ }
+
+ $1->key = $2;
$$ = $1;
}
| set_block FLAGS set_flag_list stmt_separator
@@ -1922,6 +2222,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;
}
@@ -1934,7 +2238,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;
@@ -1961,14 +2265,27 @@ set_flag : CONSTANT { $$ = NFT_SET_CONSTANT; }
map_block_alloc : /* empty */
{
- $$ = set_alloc(NULL);
+ $$ = set_alloc(&internal_location);
}
;
+ct_obj_type_map : TIMEOUT { $$ = NFT_OBJECT_CT_TIMEOUT; }
+ | EXPECTATION { $$ = NFT_OBJECT_CT_EXPECT; }
+ ;
+
map_block_obj_type : COUNTER close_scope_counter { $$ = NFT_OBJECT_COUNTER; }
| QUOTA close_scope_quota { $$ = NFT_OBJECT_QUOTA; }
| LIMIT close_scope_limit { $$ = NFT_OBJECT_LIMIT; }
| SECMARK close_scope_secmark { $$ = NFT_OBJECT_SECMARK; }
+ | SYNPROXY close_scope_synproxy { $$ = NFT_OBJECT_SYNPROXY; }
+ ;
+
+map_block_obj_typeof : map_block_obj_type
+ | CT ct_obj_type_map close_scope_ct { $$ = $2; }
+ ;
+
+map_block_data_interval : INTERVAL { $$ = EXPR_F_INTERVAL; }
+ | { $$ = 0; }
;
map_block : /* empty */ { $$ = $<set>-1; }
@@ -1979,23 +2296,24 @@ map_block : /* empty */ { $$ = $<set>-1; }
$1->timeout = $3;
$$ = $1;
}
- | map_block TYPE
- data_type_expr COLON data_type_expr
- stmt_separator
+ | map_block GC_INTERVAL time_spec stmt_separator
{
- $1->key = $3;
- $1->data = $5;
-
- $1->flags |= NFT_SET_MAP;
+ $1->gc_int = $3;
$$ = $1;
}
| map_block TYPE
- data_type_expr COLON INTERVAL data_type_expr
- stmt_separator
+ 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 |= EXPR_F_INTERVAL;
+ $1->data->flags |= $5;
$1->flags |= NFT_SET_MAP;
$$ = $1;
@@ -2004,27 +2322,41 @@ 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;
+ if ($5->etype == EXPR_CT && $5->ct.key == NFT_CT_HELPER) {
+ $1->objtype = NFT_OBJECT_CT_HELPER;
+ $1->flags |= NFT_SET_OBJECT;
+ expr_free($5);
+ } else {
+ $1->data = $5;
+ $1->flags |= NFT_SET_MAP;
+ }
+
$$ = $1;
}
- | map_block TYPEOF
- typeof_expr COLON INTERVAL typeof_expr
- stmt_separator
+ | map_block TYPE
+ data_type_expr COLON map_block_obj_type
+ stmt_separator close_scope_type
{
- $1->key = $3;
- datatype_set($1->key, $3->dtype);
- $1->data = $6;
- $1->data->flags |= EXPR_F_INTERVAL;
+ if (already_set($1->key, &@2, state)) {
+ expr_free($3);
+ YYERROR;
+ }
- $1->flags |= NFT_SET_MAP;
+ $1->key = $3;
+ $1->objtype = $5;
+ $1->flags |= NFT_SET_OBJECT;
$$ = $1;
}
- | map_block TYPE
- data_type_expr COLON map_block_obj_type
+ | map_block TYPEOF
+ typeof_expr COLON map_block_obj_typeof
stmt_separator
{
$1->key = $3;
@@ -2051,7 +2383,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;
@@ -2060,7 +2392,7 @@ map_block : /* empty */ { $$ = $<set>-1; }
| map_block set_mechanism stmt_separator
;
-set_mechanism : POLICY set_policy_spec
+set_mechanism : POLICY set_policy_spec close_scope_policy
{
$<set>0->policy = $2;
}
@@ -2076,7 +2408,7 @@ set_policy_spec : PERFORMANCE { $$ = NFT_SET_POL_PERFORMANCE; }
flowtable_block_alloc : /* empty */
{
- $$ = flowtable_alloc(NULL);
+ $$ = flowtable_alloc(&internal_location);
}
;
@@ -2090,10 +2422,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;
}
@@ -2136,12 +2468,23 @@ flowtable_list_expr : flowtable_expr_member
| flowtable_list_expr COMMA opt_newline
;
-flowtable_expr_member : STRING
+flowtable_expr_member : QUOTED_STRING
{
- $$ = constant_expr_alloc(&@$, &string_type,
- BYTEORDER_HOST_ENDIAN,
- strlen($1) * BITS_PER_BYTE, $1);
- xfree($1);
+ struct expr *expr = ifname_expr_alloc(&@$, state->msgs, $1);
+
+ if (!expr)
+ YYERROR;
+
+ $$ = expr;
+ }
+ | STRING
+ {
+ struct expr *expr = ifname_expr_alloc(&@$, state->msgs, $1);
+
+ if (!expr)
+ YYERROR;
+
+ $$ = expr;
}
| variable_expr
{
@@ -2156,12 +2499,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
{
@@ -2184,7 +2527,7 @@ data_type_expr : data_type_atom_expr
obj_block_alloc : /* empty */
{
- $$ = obj_alloc(NULL);
+ $$ = obj_alloc(&internal_location);
}
;
@@ -2198,7 +2541,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;
@@ -2215,7 +2558,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;
@@ -2232,7 +2575,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;
@@ -2243,6 +2586,7 @@ ct_timeout_block : /*empty */
{
$$ = $<obj>-1;
init_list_head(&$$->ct_timeout.timeout_list);
+ $$->type = NFT_OBJECT_CT_TIMEOUT;
}
| ct_timeout_block common_block
| ct_timeout_block stmt_separator
@@ -2253,7 +2597,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;
@@ -2270,7 +2614,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;
@@ -2287,7 +2631,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;
@@ -2304,7 +2648,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;
@@ -2321,7 +2665,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;
@@ -2335,33 +2679,38 @@ type_identifier : STRING { $$ = $1; }
| CLASSID { $$ = xstrdup("classid"); }
;
-hook_spec : TYPE STRING HOOK STRING dev_spec prio_spec
+hook_spec : TYPE close_scope_type STRING HOOK STRING dev_spec prio_spec
{
- const char *chain_type = chain_type_name_lookup($2);
+ const char *chain_type = chain_type_name_lookup($3);
if (chain_type == NULL) {
- erec_queue(error(&@2, "unknown chain type"),
+ erec_queue(error(&@3, "unknown chain type"),
state->msgs);
- xfree($2);
+ free_const($3);
+ free_const($5);
+ expr_free($6);
+ expr_free($7.expr);
YYERROR;
}
- $<chain>0->type.loc = @2;
+ $<chain>0->type.loc = @3;
$<chain>0->type.str = xstrdup(chain_type);
- xfree($2);
+ free_const($3);
$<chain>0->loc = @$;
- $<chain>0->hook.loc = @4;
- $<chain>0->hook.name = chain_hookname_lookup($4);
+ $<chain>0->hook.loc = @5;
+ $<chain>0->hook.name = chain_hookname_lookup($5);
if ($<chain>0->hook.name == NULL) {
- erec_queue(error(&@4, "unknown chain hook"),
+ erec_queue(error(&@5, "unknown chain hook"),
state->msgs);
- xfree($4);
+ free_const($5);
+ expr_free($6);
+ expr_free($7.expr);
YYERROR;
}
- xfree($4);
+ free_const($5);
- $<chain>0->dev_expr = $5;
- $<chain>0->priority = $6;
+ $<chain>0->dev_expr = $6;
+ $<chain>0->priority = $7;
$<chain>0->flags |= CHAIN_F_BASECHAIN;
}
;
@@ -2405,7 +2754,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
@@ -2418,7 +2767,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
@@ -2431,7 +2780,7 @@ extended_prio_spec : int_num
BYTEORDER_HOST_ENDIAN,
strlen(str) * BITS_PER_BYTE,
str);
- xfree($1);
+ free_const($1);
$$ = spec;
}
;
@@ -2442,12 +2791,11 @@ int_num : NUM { $$ = $1; }
dev_spec : DEVICE string
{
- struct expr *expr;
+ struct expr *expr = ifname_expr_alloc(&@$, state->msgs, $2);
+
+ if (!expr)
+ YYERROR;
- expr = constant_expr_alloc(&@$, &string_type,
- BYTEORDER_HOST_ENDIAN,
- strlen($2) * BITS_PER_BYTE, $2);
- xfree($2);
$$ = compound_expr_alloc(&@$, EXPR_LIST);
compound_expr_add($$, expr);
@@ -2471,7 +2819,7 @@ flags_spec : FLAGS OFFLOAD
}
;
-policy_spec : POLICY policy_expr
+policy_spec : POLICY policy_expr close_scope_policy
{
if ($<chain>0->policy) {
erec_queue(error(&@$, "you cannot set chain policy twice"),
@@ -2503,6 +2851,7 @@ chain_policy : ACCEPT { $$ = NF_ACCEPT; }
;
identifier : STRING
+ | LAST { $$ = xstrdup("last"); }
;
string : STRING
@@ -2516,7 +2865,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;
@@ -2525,6 +2874,11 @@ time_spec : STRING
}
;
+/* compatibility kludge to allow either 60, 60s, 1m, ... */
+time_spec_or_num_s : NUM
+ | time_spec { $$ = $1 / 1000u; }
+ ;
+
family_spec : /* empty */ { $$ = NFPROTO_IPV4; }
| family_spec_explicit
;
@@ -2712,7 +3066,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;
@@ -2749,7 +3103,7 @@ rule_alloc : stmt_list
list_for_each_entry(i, $1, list)
$$->num_stmts++;
list_splice_tail($1, &$$->stmts);
- xfree($1);
+ free($1);
}
;
@@ -2779,10 +3133,65 @@ 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
| connlimit_stmt
+ | last_stmt close_scope_last
;
stmt : verdict_stmt
@@ -2792,19 +3201,33 @@ stmt : verdict_stmt
| stateful_stmt
| meta_stmt
| log_stmt close_scope_log
- | reject_stmt
- | nat_stmt
- | tproxy_stmt
+ | reject_stmt close_scope_reject
+ | nat_stmt close_scope_nat
+ | tproxy_stmt close_scope_tproxy
| queue_stmt
| ct_stmt
- | masq_stmt
- | redir_stmt
- | dup_stmt
- | fwd_stmt
+ | masq_stmt close_scope_nat
+ | redir_stmt close_scope_nat
+ | dup_stmt close_scope_dup
+ | fwd_stmt close_scope_fwd
| set_stmt
| map_stmt
- | synproxy_stmt
+ | synproxy_stmt close_scope_synproxy
| chain_stmt
+ | optstrip_stmt
+ | xt_stmt close_scope_xt
+ | objref_stmt
+ ;
+
+xt_stmt : XT STRING string
+ {
+ $$ = NULL;
+ free_const($2);
+ free_const($3);
+ erec_queue(error(&@$, "unsupported xtables compat expression, use iptables-nft with this ruleset"),
+ state->msgs);
+ YYERROR;
+ }
;
chain_stmt_type : JUMP { $$ = NFT_JUMP; }
@@ -2859,7 +3282,7 @@ verdict_map_list_expr : verdict_map_list_member_expr
verdict_map_list_member_expr: opt_newline set_elem_expr COLON verdict_expr opt_newline
{
- $$ = mapping_expr_alloc(&@$, $2, $4);
+ $$ = mapping_expr_alloc(&@2, $2, $4);
}
;
@@ -2883,12 +3306,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
@@ -2900,14 +3317,32 @@ 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;
}
;
+last_stmt : LAST
+ {
+ $$ = last_stmt_alloc(&@$);
+ }
+ | LAST USED NEVER
+ {
+ $$ = last_stmt_alloc(&@$);
+ }
+ | LAST USED time_spec
+ {
+ $$ = last_stmt_alloc(&@$);
+ $$->last.used = $3;
+ $$->last.set = true;
+ }
+ ;
+
log_stmt : log_stmt_alloc
| log_stmt_alloc log_args
;
@@ -2942,7 +3377,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;
@@ -3016,7 +3451,7 @@ log_arg : PREFIX string
state->msgs);
}
expr_free(expr);
- xfree($2);
+ free_const($2);
YYERROR;
}
item = variable_expr_alloc(&@$, scope, sym);
@@ -3046,7 +3481,7 @@ log_arg : PREFIX string
}
}
- xfree($2);
+ free_const($2);
$<stmt>0->log.prefix = expr;
$<stmt>0->log.flags |= STMT_LOG_PREFIX;
}
@@ -3099,14 +3534,14 @@ level_type : string
else {
erec_queue(error(&@1, "invalid log level"),
state->msgs);
- xfree($1);
+ free_const($1);
YYERROR;
}
- xfree($1);
+ free_const($1);
}
;
-log_flags : TCP log_flags_tcp
+log_flags : TCP log_flags_tcp close_scope_tcp
{
$$ = $2;
}
@@ -3145,51 +3580,29 @@ log_flag_tcp : SEQUENCE
}
;
-limit_stmt : LIMIT RATE limit_mode NUM SLASH time_unit limit_burst_pkts close_scope_limit
+limit_stmt : LIMIT RATE limit_mode limit_rate_pkts limit_burst_pkts close_scope_limit
{
- if ($7 == 0) {
- erec_queue(error(&@7, "limit burst must be > 0"),
+ if ($5 == 0) {
+ erec_queue(error(&@5, "packet limit burst must be > 0"),
state->msgs);
YYERROR;
}
$$ = limit_stmt_alloc(&@$);
- $$->limit.rate = $4;
- $$->limit.unit = $6;
- $$->limit.burst = $7;
+ $$->limit.rate = $4.rate;
+ $$->limit.unit = $4.unit;
+ $$->limit.burst = $5;
$$->limit.type = NFT_LIMIT_PKTS;
$$->limit.flags = $3;
}
- | LIMIT RATE limit_mode NUM STRING limit_burst_bytes close_scope_limit
+ | LIMIT RATE limit_mode limit_rate_bytes limit_burst_bytes close_scope_limit
{
- struct error_record *erec;
- uint64_t rate, unit;
-
- if ($6 == 0) {
- erec_queue(error(&@6, "limit burst must be > 0"),
- state->msgs);
- YYERROR;
- }
-
- erec = rate_parse(&@$, $5, &rate, &unit);
- xfree($5);
- if (erec != NULL) {
- erec_queue(erec, state->msgs);
- YYERROR;
- }
-
$$ = limit_stmt_alloc(&@$);
- $$->limit.rate = rate * $4;
- $$->limit.unit = unit;
- $$->limit.burst = $6;
+ $$->limit.rate = $4.rate;
+ $$->limit.unit = $4.unit;
+ $$->limit.burst = $5;
$$->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; }
@@ -3208,7 +3621,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;
@@ -3223,7 +3636,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;
@@ -3233,12 +3646,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; }
@@ -3250,20 +3657,51 @@ limit_burst_pkts : /* empty */ { $$ = 5; }
| BURST NUM PACKETS { $$ = $2; }
;
-limit_burst_bytes : /* empty */ { $$ = 5; }
- | BURST NUM BYTES { $$ = $2; }
- | BURST NUM STRING
+limit_rate_pkts : NUM SLASH time_unit
+ {
+ $$.rate = $1;
+ $$.unit = $3;
+ }
+ ;
+
+limit_burst_bytes : /* empty */ { $$ = 0; }
+ | BURST limit_bytes { $$ = $2; }
+ ;
+
+limit_rate_bytes : NUM STRING
+ {
+ struct error_record *erec;
+ uint64_t rate, unit;
+
+ erec = rate_parse(&@$, $2, &rate, &unit);
+ free_const($2);
+ if (erec != NULL) {
+ erec_queue(erec, state->msgs);
+ YYERROR;
+ }
+ $$.rate = rate * $1;
+ $$.unit = unit;
+ }
+ | limit_bytes SLASH time_unit
+ {
+ $$.rate = $1;
+ $$.unit = $3;
+ }
+ ;
+
+limit_bytes : NUM BYTES { $$ = $1; }
+ | NUM STRING
{
struct error_record *erec;
uint64_t rate;
- erec = data_unit_parse(&@$, $3, &rate);
- xfree($3);
+ erec = data_unit_parse(&@$, $2, &rate);
+ free_const($2);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
}
- $$ = $2 * rate;
+ $$ = $1 * rate;
}
;
@@ -3287,7 +3725,7 @@ reject_with_expr : STRING
{
$$ = symbol_expr_alloc(&@$, SYMBOL_VALUE,
current_scope(state), $1);
- xfree($1);
+ free_const($1);
}
| integer_expr { $$ = $1; }
;
@@ -3297,47 +3735,47 @@ reject_opts : /* empty */
$<stmt>0->reject.type = -1;
$<stmt>0->reject.icmp_code = -1;
}
- | WITH ICMP TYPE reject_with_expr
+ | WITH ICMP TYPE reject_with_expr close_scope_type close_scope_icmp
{
$<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
+ | 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
+ | 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 RESET
+ | WITH TCP close_scope_tcp RESET close_scope_reset
{
$<stmt>0->reject.type = NFT_REJECT_TCP_RST;
}
@@ -3346,8 +3784,8 @@ reject_opts : /* empty */
nat_stmt : nat_stmt_alloc nat_stmt_args
;
-nat_stmt_alloc : SNAT { $$ = nat_stmt_alloc(&@$, NFT_NAT_SNAT); }
- | DNAT { $$ = nat_stmt_alloc(&@$, NFT_NAT_DNAT); }
+nat_stmt_alloc : SNAT { $$ = nat_stmt_alloc(&@$, __NFT_NAT_SNAT); }
+ | DNAT { $$ = nat_stmt_alloc(&@$, __NFT_NAT_DNAT); }
;
tproxy_stmt : TPROXY TO stmt_expr
@@ -3398,12 +3836,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
@@ -3951,12 +4383,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);
}
;
@@ -3966,7 +4398,7 @@ symbol_expr : variable_expr
$$ = symbol_expr_alloc(&@$, SYMBOL_VALUE,
current_scope(state),
$1);
- xfree($1);
+ free_const($1);
}
;
@@ -3974,12 +4406,12 @@ set_ref_expr : set_ref_symbol_expr
| variable_expr
;
-set_ref_symbol_expr : AT identifier
+set_ref_symbol_expr : AT identifier close_scope_at
{
$$ = symbol_expr_alloc(&@$, SYMBOL_SET,
current_scope(state),
$2);
- xfree($2);
+ free_const($2);
}
;
@@ -4036,7 +4468,7 @@ fib_expr : FIB fib_tuple fib_result close_scope_fib
fib_result : OIF { $$ =NFT_FIB_RESULT_OIF; }
| OIFNAME { $$ =NFT_FIB_RESULT_OIFNAME; }
- | TYPE { $$ =NFT_FIB_RESULT_ADDRTYPE; }
+ | TYPE close_scope_type { $$ =NFT_FIB_RESULT_ADDRTYPE; }
;
fib_flag : SADDR { $$ = NFTA_FIB_F_SADDR; }
@@ -4053,11 +4485,11 @@ fib_tuple : fib_flag DOT fib_tuple
| fib_flag
;
-osf_expr : OSF osf_ttl HDRVERSION
+osf_expr : OSF osf_ttl HDRVERSION close_scope_osf
{
$$ = osf_expr_alloc(&@$, $2, NFT_OSF_F_VERSION);
}
- | OSF osf_ttl NAME
+ | OSF osf_ttl NAME close_scope_osf
{
$$ = osf_expr_alloc(&@$, $2, 0);
}
@@ -4076,10 +4508,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);
}
;
@@ -4187,7 +4619,7 @@ set_list_member_expr : opt_newline set_expr opt_newline
}
| opt_newline set_elem_expr COLON set_rhs_expr opt_newline
{
- $$ = mapping_expr_alloc(&@$, $2, $4);
+ $$ = mapping_expr_alloc(&@2, $2, $4);
}
;
@@ -4207,6 +4639,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);
+ free($3);
+ }
;
set_elem_key_expr : set_lhs_expr { $$ = $1; }
@@ -4217,7 +4655,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
{
@@ -4243,7 +4681,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;
@@ -4280,44 +4718,34 @@ set_elem_stmt : COUNTER close_scope_counter
$$->counter.packets = $3;
$$->counter.bytes = $5;
}
- | LIMIT RATE limit_mode NUM SLASH time_unit limit_burst_pkts close_scope_limit
+ | LIMIT RATE limit_mode limit_rate_pkts limit_burst_pkts close_scope_limit
{
- if ($7 == 0) {
- erec_queue(error(&@7, "limit burst must be > 0"),
+ if ($5 == 0) {
+ erec_queue(error(&@5, "limit burst must be > 0"),
state->msgs);
YYERROR;
}
$$ = limit_stmt_alloc(&@$);
- $$->limit.rate = $4;
- $$->limit.unit = $6;
- $$->limit.burst = $7;
+ $$->limit.rate = $4.rate;
+ $$->limit.unit = $4.unit;
+ $$->limit.burst = $5;
$$->limit.type = NFT_LIMIT_PKTS;
$$->limit.flags = $3;
}
- | LIMIT RATE limit_mode NUM STRING limit_burst_bytes close_scope_limit
+ | LIMIT RATE limit_mode limit_rate_bytes limit_burst_bytes close_scope_limit
{
- struct error_record *erec;
- uint64_t rate, unit;
-
- if ($6 == 0) {
+ if ($5 == 0) {
erec_queue(error(&@6, "limit burst must be > 0"),
state->msgs);
YYERROR;
}
- erec = rate_parse(&@$, $5, &rate, &unit);
- xfree($5);
- if (erec != NULL) {
- erec_queue(erec, state->msgs);
- YYERROR;
- }
-
$$ = limit_stmt_alloc(&@$);
- $$->limit.rate = rate * $4;
- $$->limit.unit = unit;
- $$->limit.burst = $6;
+ $$->limit.rate = $4.rate;
+ $$->limit.unit = $4.unit;
+ $$->limit.burst = $5;
$$->limit.type = NFT_LIMIT_PKT_BYTES;
$$->limit.flags = $3;
- }
+ }
| CT COUNT NUM close_scope_ct
{
$$ = connlimit_stmt_alloc(&@$);
@@ -4329,6 +4757,32 @@ set_elem_stmt : COUNTER close_scope_counter
$$->connlimit.count = $4;
$$->connlimit.flags = NFT_CONNLIMIT_F_INV;
}
+ | QUOTA quota_mode NUM quota_unit quota_used close_scope_quota
+ {
+ struct error_record *erec;
+ uint64_t rate;
+
+ erec = data_unit_parse(&@$, $4, &rate);
+ free_const($4);
+ if (erec != NULL) {
+ erec_queue(erec, state->msgs);
+ YYERROR;
+ }
+ $$ = quota_stmt_alloc(&@$);
+ $$->quota.bytes = $3 * rate;
+ $$->quota.used = $5;
+ $$->quota.flags = $2;
+ }
+ | LAST USED NEVER close_scope_last
+ {
+ $$ = last_stmt_alloc(&@$);
+ }
+ | LAST USED time_spec close_scope_last
+ {
+ $$ = last_stmt_alloc(&@$);
+ $$->last.used = $3;
+ $$->last.set = true;
+ }
;
set_elem_expr_option : TIMEOUT time_spec
@@ -4342,7 +4796,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;
@@ -4394,7 +4848,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;
@@ -4423,10 +4877,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);
}
;
@@ -4443,27 +4897,34 @@ ct_obj_type : HELPER { $$ = NFT_OBJECT_CT_HELPER; }
;
ct_cmd_type : HELPERS { $$ = CMD_OBJ_CT_HELPERS; }
- | TIMEOUT { $$ = CMD_OBJ_CT_TIMEOUT; }
- | EXPECTATION { $$ = CMD_OBJ_CT_EXPECT; }
+ | TIMEOUT { $$ = CMD_OBJ_CT_TIMEOUTS; }
+ | EXPECTATION { $$ = CMD_OBJ_CT_EXPECTATIONS; }
;
-ct_l4protoname : TCP { $$ = IPPROTO_TCP; }
- | UDP { $$ = IPPROTO_UDP; }
+ct_l4protoname : TCP close_scope_tcp { $$ = IPPROTO_TCP; }
+ | UDP close_scope_udp { $$ = IPPROTO_UDP; }
;
-ct_helper_config : TYPE QUOTED_STRING PROTOCOL ct_l4protoname stmt_separator
+ct_helper_config : TYPE QUOTED_STRING PROTOCOL ct_l4protoname stmt_separator close_scope_type
{
struct ct_helper *ct;
int ret;
ct = &$<obj>0->ct_helper;
+ if (ct->l4proto) {
+ erec_queue(error(&@2, "You can only specify this once. This statement is already set for %s.", ct->name), state->msgs);
+ free_const($2);
+ YYERROR;
+ }
+
ret = snprintf(ct->name, sizeof(ct->name), "%s", $2);
if (ret <= 0 || ret >= (int)sizeof(ct->name)) {
erec_queue(error(&@2, "invalid name '%s', max length is %u\n", $2, (int)sizeof(ct->name)), state->msgs);
+ free_const($2);
YYERROR;
}
- xfree($2);
+ free_const($2);
ct->l4proto = $4;
}
@@ -4477,17 +4938,16 @@ 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;
}
;
-timeout_state : STRING COLON NUM
-
+timeout_state : STRING COLON time_spec_or_num_s
{
struct timeout_state *ts;
@@ -4496,7 +4956,7 @@ timeout_state : STRING COLON NUM
ts->timeout_value = $3;
ts->location = @1;
init_list_head(&ts->head);
- $$ = &ts->head;
+ $$ = ts;
}
;
@@ -4508,13 +4968,13 @@ ct_timeout_config : PROTOCOL ct_l4protoname stmt_separator
ct = &$<obj>0->ct_timeout;
ct->l4proto = l4proto;
}
- | POLICY '=' '{' timeout_states '}' stmt_separator
+ | POLICY '=' '{' timeout_states '}' stmt_separator close_scope_policy
{
struct ct_timeout *ct;
ct = &$<obj>0->ct_timeout;
list_splice_tail($4, &ct->timeout_list);
- xfree($4);
+ free($4);
}
| L3PROTOCOL family_spec_explicit stmt_separator
{
@@ -4550,34 +5010,25 @@ ct_obj_alloc : /* empty */
}
;
-limit_config : RATE limit_mode NUM SLASH time_unit limit_burst_pkts
+limit_config : RATE limit_mode limit_rate_pkts limit_burst_pkts
{
struct limit *limit;
limit = &$<obj>0->limit;
- limit->rate = $3;
- limit->unit = $5;
- limit->burst = $6;
+ limit->rate = $3.rate;
+ limit->unit = $3.unit;
+ limit->burst = $4;
limit->type = NFT_LIMIT_PKTS;
limit->flags = $2;
}
- | RATE limit_mode NUM STRING limit_burst_bytes
+ | RATE limit_mode limit_rate_bytes limit_burst_bytes
{
struct limit *limit;
- struct error_record *erec;
- uint64_t rate, unit;
-
- erec = rate_parse(&@$, $4, &rate, &unit);
- xfree($4);
- if (erec != NULL) {
- erec_queue(erec, state->msgs);
- YYERROR;
- }
limit = &$<obj>0->limit;
- limit->rate = rate * $3;
- limit->unit = unit;
- limit->burst = $5;
+ limit->rate = $3.rate;
+ limit->unit = $3.unit;
+ limit->burst = $4;
limit->type = NFT_LIMIT_PKT_BYTES;
limit->flags = $2;
}
@@ -4717,55 +5168,57 @@ keyword_expr : ETHER close_scope_eth { $$ = symbol_value(&@$, "ether"); }
| IP6 close_scope_ip6 { $$ = symbol_value(&@$, "ip6"); }
| VLAN close_scope_vlan { $$ = symbol_value(&@$, "vlan"); }
| ARP close_scope_arp { $$ = symbol_value(&@$, "arp"); }
- | DNAT { $$ = symbol_value(&@$, "dnat"); }
- | SNAT { $$ = symbol_value(&@$, "snat"); }
+ | DNAT close_scope_nat { $$ = symbol_value(&@$, "dnat"); }
+ | SNAT close_scope_nat { $$ = symbol_value(&@$, "snat"); }
| ECN { $$ = symbol_value(&@$, "ecn"); }
- | RESET { $$ = symbol_value(&@$, "reset"); }
+ | RESET close_scope_reset { $$ = symbol_value(&@$, "reset"); }
+ | DESTROY close_scope_destroy { $$ = symbol_value(&@$, "destroy"); }
| ORIGINAL { $$ = symbol_value(&@$, "original"); }
| REPLY { $$ = symbol_value(&@$, "reply"); }
| LABEL { $$ = symbol_value(&@$, "label"); }
+ | LAST close_scope_last { $$ = symbol_value(&@$, "last"); }
;
primary_rhs_expr : symbol_expr { $$ = $1; }
| integer_expr { $$ = $1; }
| boolean_expr { $$ = $1; }
| keyword_expr { $$ = $1; }
- | TCP
+ | TCP close_scope_tcp
{
uint8_t data = IPPROTO_TCP;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | UDP
+ | UDP close_scope_udp
{
uint8_t data = IPPROTO_UDP;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | UDPLITE
+ | UDPLITE close_scope_udplite
{
uint8_t data = IPPROTO_UDPLITE;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | ESP
+ | ESP close_scope_esp
{
uint8_t data = IPPROTO_ESP;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | AH
+ | AH close_scope_ah
{
uint8_t data = IPPROTO_AH;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | ICMP
+ | ICMP close_scope_icmp
{
uint8_t data = IPPROTO_ICMP;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
@@ -4779,21 +5232,28 @@ primary_rhs_expr : symbol_expr { $$ = $1; }
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | ICMP6
+ | ICMP6 close_scope_icmp
{
uint8_t data = IPPROTO_ICMPV6;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | COMP
+ | GRE close_scope_gre
+ {
+ uint8_t data = IPPROTO_GRE;
+ $$ = constant_expr_alloc(&@$, &inet_protocol_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(data) * BITS_PER_BYTE, &data);
+ }
+ | COMP close_scope_comp
{
uint8_t data = IPPROTO_COMP;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | DCCP
+ | DCCP close_scope_dccp
{
uint8_t data = IPPROTO_DCCP;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
@@ -4807,7 +5267,7 @@ primary_rhs_expr : symbol_expr { $$ = $1; }
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | REDIRECT
+ | REDIRECT close_scope_nat
{
uint8_t data = ICMP_REDIRECT;
$$ = constant_expr_alloc(&@$, &icmp_type_type,
@@ -4859,11 +5319,11 @@ chain_expr : variable_expr
BYTEORDER_HOST_ENDIAN,
strlen($1) * BITS_PER_BYTE,
$1);
- xfree($1);
+ free_const($1);
}
;
-meta_expr : META meta_key
+meta_expr : META meta_key close_scope_meta
{
$$ = meta_expr_alloc(&@$, $2);
}
@@ -4871,13 +5331,13 @@ meta_expr : META meta_key
{
$$ = meta_expr_alloc(&@$, $1);
}
- | META STRING
+ | META STRING close_scope_meta
{
struct error_record *erec;
unsigned int key;
erec = meta_key_parse(&@$, $2, &key);
- xfree($2);
+ free_const($2);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
@@ -4924,7 +5384,7 @@ meta_key_unqualified : MARK { $$ = NFT_META_MARK; }
| HOUR { $$ = NFT_META_TIME_HOUR; }
;
-meta_stmt : META meta_key SET stmt_expr
+meta_stmt : META meta_key SET stmt_expr close_scope_meta
{
switch ($2) {
case NFT_META_SECMARK:
@@ -4948,15 +5408,16 @@ meta_stmt : META meta_key SET stmt_expr
{
$$ = meta_stmt_alloc(&@$, $1, $3);
}
- | META STRING SET stmt_expr
+ | META STRING SET stmt_expr close_scope_meta
{
struct error_record *erec;
unsigned int key;
erec = meta_key_parse(&@$, $2, &key);
- xfree($2);
+ free_const($2);
if (erec != NULL) {
erec_queue(erec, state->msgs);
+ expr_free($4);
YYERROR;
}
@@ -4966,11 +5427,11 @@ meta_stmt : META meta_key SET stmt_expr
{
$$ = notrack_stmt_alloc(&@$);
}
- | FLOW OFFLOAD AT string
+ | FLOW OFFLOAD AT string close_scope_at
{
$$ = flow_offload_stmt_alloc(&@$, $4);
}
- | FLOW ADD AT string
+ | FLOW ADD AT string close_scope_at
{
$$ = flow_offload_stmt_alloc(&@$, $4);
}
@@ -5195,19 +5656,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);
@@ -5237,13 +5685,35 @@ payload_expr : payload_raw_expr
| comp_hdr_expr
| udp_hdr_expr
| udplite_hdr_expr
- | tcp_hdr_expr
+ | tcp_hdr_expr close_scope_tcp
| dccp_hdr_expr
| sctp_hdr_expr
| th_hdr_expr
+ | vxlan_hdr_expr
+ | geneve_hdr_expr
+ | gre_hdr_expr
+ | gretap_hdr_expr
;
-payload_raw_expr : AT payload_base_spec COMMA NUM COMMA NUM
+payload_raw_len : NUM
+ {
+ if ($1 > NFT_MAX_EXPR_LEN_BITS) {
+ erec_queue(error(&@1, "raw payload length %u exceeds upper limit of %u",
+ $1, NFT_MAX_EXPR_LEN_BITS),
+ state->msgs);
+ YYERROR;
+ }
+
+ if ($1 == 0) {
+ erec_queue(error(&@1, "raw payload length cannot be 0"), state->msgs);
+ YYERROR;
+ }
+
+ $$ = $1;
+ }
+ ;
+
+payload_raw_expr : AT payload_base_spec COMMA NUM COMMA payload_raw_len close_scope_at
{
$$ = payload_expr_alloc(&@$, NULL, 0);
payload_init_raw($$, $2, $4, $6);
@@ -5254,7 +5724,18 @@ payload_raw_expr : AT payload_base_spec COMMA NUM COMMA NUM
payload_base_spec : LL_HDR { $$ = PROTO_BASE_LL_HDR; }
| NETWORK_HDR { $$ = PROTO_BASE_NETWORK_HDR; }
- | TRANSPORT_HDR { $$ = PROTO_BASE_TRANSPORT_HDR; }
+ | TRANSPORT_HDR close_scope_th { $$ = PROTO_BASE_TRANSPORT_HDR; }
+ | STRING
+ {
+ if (!strcmp($1, "ih")) {
+ $$ = PROTO_BASE_INNER_HDR;
+ } else {
+ erec_queue(error(&@1, "unknown raw payload base"), state->msgs);
+ free_const($1);
+ YYERROR;
+ }
+ free_const($1);
+ }
;
eth_hdr_expr : ETHER eth_hdr_field close_scope_eth
@@ -5265,7 +5746,7 @@ eth_hdr_expr : ETHER eth_hdr_field close_scope_eth
eth_hdr_field : SADDR { $$ = ETHHDR_SADDR; }
| DADDR { $$ = ETHHDR_DADDR; }
- | TYPE { $$ = ETHHDR_TYPE; }
+ | TYPE close_scope_type { $$ = ETHHDR_TYPE; }
;
vlan_hdr_expr : VLAN vlan_hdr_field close_scope_vlan
@@ -5278,7 +5759,7 @@ vlan_hdr_field : ID { $$ = VLANHDR_VID; }
| CFI { $$ = VLANHDR_CFI; }
| DEI { $$ = VLANHDR_DEI; }
| PCP { $$ = VLANHDR_PCP; }
- | TYPE { $$ = VLANHDR_TYPE; }
+ | TYPE close_scope_type { $$ = VLANHDR_TYPE; }
;
arp_hdr_expr : ARP arp_hdr_field close_scope_arp
@@ -5304,11 +5785,15 @@ ip_hdr_expr : IP ip_hdr_field close_scope_ip
}
| IP OPTION ip_option_type ip_option_field close_scope_ip
{
- $$ = ipopt_expr_alloc(&@$, $3, $4, 0);
+ $$ = ipopt_expr_alloc(&@$, $3, $4);
+ if (!$$) {
+ erec_queue(error(&@1, "unknown ip option type/field"), state->msgs);
+ YYERROR;
+ }
}
| IP OPTION ip_option_type close_scope_ip
{
- $$ = ipopt_expr_alloc(&@$, $3, IPOPT_FIELD_TYPE, 0);
+ $$ = ipopt_expr_alloc(&@$, $3, IPOPT_FIELD_TYPE);
$$->exthdr.flags = NFT_EXTHDR_F_PRESENT;
}
;
@@ -5333,20 +5818,20 @@ ip_option_type : LSRR { $$ = IPOPT_LSRR; }
| RA { $$ = IPOPT_RA; }
;
-ip_option_field : TYPE { $$ = IPOPT_FIELD_TYPE; }
+ip_option_field : TYPE close_scope_type { $$ = IPOPT_FIELD_TYPE; }
| LENGTH { $$ = IPOPT_FIELD_LENGTH; }
| VALUE { $$ = IPOPT_FIELD_VALUE; }
| PTR { $$ = IPOPT_FIELD_PTR; }
| ADDR { $$ = IPOPT_FIELD_ADDR_0; }
;
-icmp_hdr_expr : ICMP icmp_hdr_field
+icmp_hdr_expr : ICMP icmp_hdr_field close_scope_icmp
{
$$ = payload_expr_alloc(&@$, &proto_icmp, $2);
}
;
-icmp_hdr_field : TYPE { $$ = ICMPHDR_TYPE; }
+icmp_hdr_field : TYPE close_scope_type { $$ = ICMPHDR_TYPE; }
| CODE { $$ = ICMPHDR_CODE; }
| CHECKSUM { $$ = ICMPHDR_CHECKSUM; }
| ID { $$ = ICMPHDR_ID; }
@@ -5355,13 +5840,13 @@ icmp_hdr_field : TYPE { $$ = ICMPHDR_TYPE; }
| MTU { $$ = ICMPHDR_MTU; }
;
-igmp_hdr_expr : IGMP igmp_hdr_field
+igmp_hdr_expr : IGMP igmp_hdr_field close_scope_igmp
{
$$ = payload_expr_alloc(&@$, &proto_igmp, $2);
}
;
-igmp_hdr_field : TYPE { $$ = IGMPHDR_TYPE; }
+igmp_hdr_field : TYPE close_scope_type { $$ = IGMPHDR_TYPE; }
| CHECKSUM { $$ = IGMPHDR_CHECKSUM; }
| MRT { $$ = IGMPHDR_MRT; }
| GROUP { $$ = IGMPHDR_GROUP; }
@@ -5383,13 +5868,13 @@ ip6_hdr_field : HDRVERSION { $$ = IP6HDR_VERSION; }
| SADDR { $$ = IP6HDR_SADDR; }
| DADDR { $$ = IP6HDR_DADDR; }
;
-icmp6_hdr_expr : ICMP6 icmp6_hdr_field
+icmp6_hdr_expr : ICMP6 icmp6_hdr_field close_scope_icmp
{
$$ = payload_expr_alloc(&@$, &proto_icmp6, $2);
}
;
-icmp6_hdr_field : TYPE { $$ = ICMP6HDR_TYPE; }
+icmp6_hdr_field : TYPE close_scope_type { $$ = ICMP6HDR_TYPE; }
| CODE { $$ = ICMP6HDR_CODE; }
| CHECKSUM { $$ = ICMP6HDR_CHECKSUM; }
| PPTR { $$ = ICMP6HDR_PPTR; }
@@ -5397,9 +5882,11 @@ icmp6_hdr_field : TYPE { $$ = ICMP6HDR_TYPE; }
| ID { $$ = ICMP6HDR_ID; }
| SEQUENCE { $$ = ICMP6HDR_SEQ; }
| MAXDELAY { $$ = ICMP6HDR_MAXDELAY; }
+ | TADDR { $$ = ICMP6HDR_TADDR; }
+ | DADDR { $$ = ICMP6HDR_DADDR; }
;
-auth_hdr_expr : AH auth_hdr_field
+auth_hdr_expr : AH auth_hdr_field close_scope_ah
{
$$ = payload_expr_alloc(&@$, &proto_ah, $2);
}
@@ -5412,7 +5899,7 @@ auth_hdr_field : NEXTHDR { $$ = AHHDR_NEXTHDR; }
| SEQUENCE { $$ = AHHDR_SEQUENCE; }
;
-esp_hdr_expr : ESP esp_hdr_field
+esp_hdr_expr : ESP esp_hdr_field close_scope_esp
{
$$ = payload_expr_alloc(&@$, &proto_esp, $2);
}
@@ -5422,7 +5909,7 @@ esp_hdr_field : SPI { $$ = ESPHDR_SPI; }
| SEQUENCE { $$ = ESPHDR_SEQUENCE; }
;
-comp_hdr_expr : COMP comp_hdr_field
+comp_hdr_expr : COMP comp_hdr_field close_scope_comp
{
$$ = payload_expr_alloc(&@$, &proto_comp, $2);
}
@@ -5433,7 +5920,7 @@ comp_hdr_field : NEXTHDR { $$ = COMPHDR_NEXTHDR; }
| CPI { $$ = COMPHDR_CPI; }
;
-udp_hdr_expr : UDP udp_hdr_field
+udp_hdr_expr : UDP udp_hdr_field close_scope_udp
{
$$ = payload_expr_alloc(&@$, &proto_udp, $2);
}
@@ -5445,7 +5932,7 @@ udp_hdr_field : SPORT { $$ = UDPHDR_SPORT; }
| CHECKSUM { $$ = UDPHDR_CHECKSUM; }
;
-udplite_hdr_expr : UDPLITE udplite_hdr_field
+udplite_hdr_expr : UDPLITE udplite_hdr_field close_scope_udplite
{
$$ = payload_expr_alloc(&@$, &proto_udplite, $2);
}
@@ -5461,19 +5948,118 @@ tcp_hdr_expr : TCP tcp_hdr_field
{
$$ = payload_expr_alloc(&@$, &proto_tcp, $2);
}
- | TCP OPTION tcp_hdr_option_type tcp_hdr_option_field
- {
- $$ = tcpopt_expr_alloc(&@$, $3, $4);
- }
| TCP OPTION tcp_hdr_option_type
{
$$ = tcpopt_expr_alloc(&@$, $3, TCPOPT_COMMON_KIND);
$$->exthdr.flags = NFT_EXTHDR_F_PRESENT;
}
- | TCP OPTION AT tcp_hdr_option_type COMMA NUM COMMA NUM
+ | 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 payload_raw_len
{
- $$ = tcpopt_expr_alloc(&@$, $4, 0);
- tcpopt_init_raw($$, $4, $6, $8, 0);
+ $$ = tcpopt_expr_alloc(&@$, $5, 0);
+ tcpopt_init_raw($$, $5, $7, $9, 0);
+ }
+ ;
+
+inner_inet_expr : ip_hdr_expr
+ | icmp_hdr_expr
+ | igmp_hdr_expr
+ | ip6_hdr_expr
+ | icmp6_hdr_expr
+ | auth_hdr_expr
+ | esp_hdr_expr
+ | comp_hdr_expr
+ | udp_hdr_expr
+ | udplite_hdr_expr
+ | tcp_hdr_expr close_scope_tcp
+ | dccp_hdr_expr
+ | sctp_hdr_expr
+ | th_hdr_expr
+ ;
+
+inner_eth_expr : eth_hdr_expr
+ | vlan_hdr_expr
+ | arp_hdr_expr
+ ;
+
+inner_expr : inner_eth_expr
+ | inner_inet_expr
+ ;
+
+vxlan_hdr_expr : VXLAN vxlan_hdr_field
+ {
+ struct expr *expr;
+
+ expr = payload_expr_alloc(&@$, &proto_vxlan, $2);
+ expr->payload.inner_desc = &proto_vxlan;
+ $$ = expr;
+ }
+ | VXLAN inner_expr
+ {
+ $$ = $2;
+ $$->location = @$;
+ $$->payload.inner_desc = &proto_vxlan;
+ }
+ ;
+
+vxlan_hdr_field : VNI { $$ = VXLANHDR_VNI; }
+ | FLAGS { $$ = VXLANHDR_FLAGS; }
+ ;
+
+geneve_hdr_expr : GENEVE geneve_hdr_field
+ {
+ struct expr *expr;
+
+ expr = payload_expr_alloc(&@$, &proto_geneve, $2);
+ expr->payload.inner_desc = &proto_geneve;
+ $$ = expr;
+ }
+ | GENEVE inner_expr
+ {
+ $$ = $2;
+ $$->location = @$;
+ $$->payload.inner_desc = &proto_geneve;
+ }
+ ;
+
+geneve_hdr_field : VNI { $$ = GNVHDR_VNI; }
+ | TYPE { $$ = GNVHDR_TYPE; }
+ ;
+
+gre_hdr_expr : GRE gre_hdr_field close_scope_gre
+ {
+ $$ = payload_expr_alloc(&@$, &proto_gre, $2);
+ }
+ | GRE close_scope_gre inner_inet_expr
+ {
+ $$ = $3;
+ $$->payload.inner_desc = &proto_gre;
+ }
+ ;
+
+gre_hdr_field : HDRVERSION { $$ = GREHDR_VERSION; }
+ | FLAGS { $$ = GREHDR_FLAGS; }
+ | PROTOCOL { $$ = GREHDR_PROTOCOL; }
+ ;
+
+gretap_hdr_expr : GRETAP close_scope_gre inner_expr
+ {
+ $$ = $3;
+ $$->payload.inner_desc = &proto_gretap;
+ }
+ ;
+
+optstrip_stmt : RESET TCP OPTION tcp_hdr_option_type close_scope_tcp
+ {
+ $$ = optstrip_stmt_alloc(&@$, tcpopt_expr_alloc(&@$,
+ $4, TCPOPT_COMMON_KIND));
}
;
@@ -5489,19 +6075,57 @@ tcp_hdr_field : SPORT { $$ = TCPHDR_SPORT; }
| URGPTR { $$ = TCPHDR_URGPTR; }
;
-tcp_hdr_option_type : EOL { $$ = TCPOPT_KIND_EOL; }
- | NOP { $$ = TCPOPT_KIND_NOP; }
- | MSS { $$ = TCPOPT_KIND_MAXSEG; }
- | WINDOW { $$ = TCPOPT_KIND_WINDOW; }
- | SACK_PERM { $$ = TCPOPT_KIND_SACK_PERMITTED; }
- | SACK { $$ = TCPOPT_KIND_SACK; }
+tcp_hdr_option_kind_and_field : MSS tcpopt_field_maxseg
+ {
+ struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_MAXSEG, .field = $2 };
+ $$ = kind_field;
+ }
+ | tcp_hdr_option_sack tcpopt_field_sack
+ {
+ struct tcp_kind_field kind_field = { .kind = $1, .field = $2 };
+ $$ = kind_field;
+ }
+ | WINDOW tcpopt_field_window
+ {
+ struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_WINDOW, .field = $2 };
+ $$ = kind_field;
+ }
+ | TIMESTAMP tcpopt_field_tsopt
+ {
+ struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_TIMESTAMP, .field = $2 };
+ $$ = kind_field;
+ }
+ | tcp_hdr_option_type LENGTH
+ {
+ struct tcp_kind_field kind_field = { .kind = $1, .field = TCPOPT_COMMON_LENGTH };
+ $$ = kind_field;
+ }
+ | MPTCP tcpopt_field_mptcp
+ {
+ struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_MPTCP, .field = $2 };
+ $$ = kind_field;
+ }
+ ;
+
+tcp_hdr_option_sack : SACK { $$ = TCPOPT_KIND_SACK; }
| SACK0 { $$ = TCPOPT_KIND_SACK; }
| SACK1 { $$ = TCPOPT_KIND_SACK1; }
| SACK2 { $$ = TCPOPT_KIND_SACK2; }
| SACK3 { $$ = TCPOPT_KIND_SACK3; }
- | ECHO { $$ = TCPOPT_KIND_ECHO; }
- | TIMESTAMP { $$ = TCPOPT_KIND_TIMESTAMP; }
- | NUM {
+ ;
+
+tcp_hdr_option_type : ECHO { $$ = TCPOPT_KIND_ECHO; }
+ | EOL { $$ = TCPOPT_KIND_EOL; }
+ | FASTOPEN { $$ = TCPOPT_KIND_FASTOPEN; }
+ | MD5SIG { $$ = TCPOPT_KIND_MD5SIG; }
+ | MPTCP { $$ = TCPOPT_KIND_MPTCP; }
+ | MSS { $$ = TCPOPT_KIND_MAXSEG; }
+ | NOP { $$ = TCPOPT_KIND_NOP; }
+ | SACK_PERM { $$ = TCPOPT_KIND_SACK_PERMITTED; }
+ | TIMESTAMP { $$ = TCPOPT_KIND_TIMESTAMP; }
+ | WINDOW { $$ = TCPOPT_KIND_WINDOW; }
+ | tcp_hdr_option_sack { $$ = $1; }
+ | NUM {
if ($1 > 255) {
erec_queue(error(&@1, "value too large"), state->msgs);
YYERROR;
@@ -5510,25 +6134,41 @@ tcp_hdr_option_type : EOL { $$ = TCPOPT_KIND_EOL; }
}
;
-tcp_hdr_option_field : KIND { $$ = TCPOPT_COMMON_KIND; }
- | LENGTH { $$ = TCPOPT_COMMON_LENGTH; }
- | SIZE { $$ = TCPOPT_MAXSEG_SIZE; }
- | COUNT { $$ = TCPOPT_WINDOW_COUNT; }
- | LEFT { $$ = TCPOPT_SACK_LEFT; }
+tcpopt_field_sack : LEFT { $$ = TCPOPT_SACK_LEFT; }
| RIGHT { $$ = TCPOPT_SACK_RIGHT; }
- | TSVAL { $$ = TCPOPT_TS_TSVAL; }
+ ;
+
+tcpopt_field_window : COUNT { $$ = TCPOPT_WINDOW_COUNT; }
+ ;
+
+tcpopt_field_tsopt : TSVAL { $$ = TCPOPT_TS_TSVAL; }
| TSECR { $$ = TCPOPT_TS_TSECR; }
;
-dccp_hdr_expr : DCCP dccp_hdr_field
+tcpopt_field_maxseg : SIZE { $$ = TCPOPT_MAXSEG_SIZE; }
+ ;
+
+tcpopt_field_mptcp : SUBTYPE { $$ = TCPOPT_MPTCP_SUBTYPE; }
+ ;
+
+dccp_hdr_expr : DCCP dccp_hdr_field close_scope_dccp
{
$$ = payload_expr_alloc(&@$, &proto_dccp, $2);
}
+ | DCCP OPTION NUM close_scope_dccp
+ {
+ if ($3 > DCCPOPT_TYPE_MAX) {
+ erec_queue(error(&@1, "value too large"),
+ state->msgs);
+ YYERROR;
+ }
+ $$ = dccpopt_expr_alloc(&@$, $3);
+ }
;
dccp_hdr_field : SPORT { $$ = DCCPHDR_SPORT; }
| DPORT { $$ = DCCPHDR_DPORT; }
- | TYPE { $$ = DCCPHDR_TYPE; }
+ | TYPE close_scope_type { $$ = DCCPHDR_TYPE; }
;
sctp_chunk_type : DATA { $$ = SCTP_CHUNK_TYPE_DATA; }
@@ -5551,7 +6191,7 @@ sctp_chunk_type : DATA { $$ = SCTP_CHUNK_TYPE_DATA; }
| ASCONF { $$ = SCTP_CHUNK_TYPE_ASCONF; }
;
-sctp_chunk_common_field : TYPE { $$ = SCTP_CHUNK_COMMON_TYPE; }
+sctp_chunk_common_field : TYPE close_scope_type { $$ = SCTP_CHUNK_COMMON_TYPE; }
| FLAGS { $$ = SCTP_CHUNK_COMMON_FLAGS; }
| LENGTH { $$ = SCTP_CHUNK_COMMON_LENGTH; }
;
@@ -5648,7 +6288,7 @@ sctp_hdr_field : SPORT { $$ = SCTPHDR_SPORT; }
| CHECKSUM { $$ = SCTPHDR_CHECKSUM; }
;
-th_hdr_expr : TRANSPORT_HDR th_hdr_field
+th_hdr_expr : TRANSPORT_HDR th_hdr_field close_scope_th
{
$$ = payload_expr_alloc(&@$, &proto_th, $2);
if ($$)
@@ -5670,7 +6310,7 @@ exthdr_expr : hbh_hdr_expr
| mh_hdr_expr
;
-hbh_hdr_expr : HBH hbh_hdr_field
+hbh_hdr_expr : HBH hbh_hdr_field close_scope_hbh
{
$$ = exthdr_expr_alloc(&@$, &exthdr_hbh, $2);
}
@@ -5688,11 +6328,11 @@ rt_hdr_expr : RT rt_hdr_field close_scope_rt
rt_hdr_field : NEXTHDR { $$ = RTHDR_NEXTHDR; }
| HDRLENGTH { $$ = RTHDR_HDRLENGTH; }
- | TYPE { $$ = RTHDR_TYPE; }
+ | TYPE close_scope_type { $$ = RTHDR_TYPE; }
| SEG_LEFT { $$ = RTHDR_SEG_LEFT; }
;
-rt0_hdr_expr : RT0 rt0_hdr_field
+rt0_hdr_expr : RT0 rt0_hdr_field close_scope_rt
{
$$ = exthdr_expr_alloc(&@$, &exthdr_rt0, $2);
}
@@ -5704,7 +6344,7 @@ rt0_hdr_field : ADDR '[' NUM ']'
}
;
-rt2_hdr_expr : RT2 rt2_hdr_field
+rt2_hdr_expr : RT2 rt2_hdr_field close_scope_rt
{
$$ = exthdr_expr_alloc(&@$, &exthdr_rt2, $2);
}
@@ -5713,7 +6353,7 @@ rt2_hdr_expr : RT2 rt2_hdr_field
rt2_hdr_field : ADDR { $$ = RT2HDR_ADDR; }
;
-rt4_hdr_expr : RT4 rt4_hdr_field
+rt4_hdr_expr : RT4 rt4_hdr_field close_scope_rt
{
$$ = exthdr_expr_alloc(&@$, &exthdr_rt4, $2);
}
@@ -5728,7 +6368,7 @@ rt4_hdr_field : LAST_ENT { $$ = RT4HDR_LASTENT; }
}
;
-frag_hdr_expr : FRAG frag_hdr_field
+frag_hdr_expr : FRAG frag_hdr_field close_scope_frag
{
$$ = exthdr_expr_alloc(&@$, &exthdr_frag, $2);
}
@@ -5742,7 +6382,7 @@ frag_hdr_field : NEXTHDR { $$ = FRAGHDR_NEXTHDR; }
| ID { $$ = FRAGHDR_ID; }
;
-dst_hdr_expr : DST dst_hdr_field
+dst_hdr_expr : DST dst_hdr_field close_scope_dst
{
$$ = exthdr_expr_alloc(&@$, &exthdr_dst, $2);
}
@@ -5752,7 +6392,7 @@ dst_hdr_field : NEXTHDR { $$ = DSTHDR_NEXTHDR; }
| HDRLENGTH { $$ = DSTHDR_HDRLENGTH; }
;
-mh_hdr_expr : MH mh_hdr_field
+mh_hdr_expr : MH mh_hdr_field close_scope_mh
{
$$ = exthdr_expr_alloc(&@$, &exthdr_mh, $2);
}
@@ -5760,7 +6400,7 @@ mh_hdr_expr : MH mh_hdr_field
mh_hdr_field : NEXTHDR { $$ = MHHDR_NEXTHDR; }
| HDRLENGTH { $$ = MHHDR_HDRLENGTH; }
- | TYPE { $$ = MHHDR_TYPE; }
+ | TYPE close_scope_type { $$ = MHHDR_TYPE; }
| RESERVED { $$ = MHHDR_RESERVED; }
| CHECKSUM { $$ = MHHDR_CHECKSUM; }
;
@@ -5772,18 +6412,18 @@ exthdr_exists_expr : EXTHDR exthdr_key
desc = exthdr_find_proto($2);
/* Assume that NEXTHDR template is always
- * the fist one in list of templates.
+ * the first one in list of templates.
*/
$$ = exthdr_expr_alloc(&@$, desc, 1);
$$->exthdr.flags = NFT_EXTHDR_F_PRESENT;
}
;
-exthdr_key : HBH { $$ = IPPROTO_HOPOPTS; }
+exthdr_key : HBH close_scope_hbh { $$ = IPPROTO_HOPOPTS; }
| RT close_scope_rt { $$ = IPPROTO_ROUTING; }
- | FRAG { $$ = IPPROTO_FRAGMENT; }
- | DST { $$ = IPPROTO_DSTOPTS; }
- | MH { $$ = IPPROTO_MH; }
+ | FRAG close_scope_frag { $$ = IPPROTO_FRAGMENT; }
+ | DST close_scope_dst { $$ = IPPROTO_DSTOPTS; }
+ | MH close_scope_mh { $$ = IPPROTO_MH; }
;
%%
diff --git a/src/parser_json.c b/src/parser_json.c
index 3cd21175..418d4ad7 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -1,7 +1,14 @@
-#define _GNU_SOURCE
+/*
+ * Copyright (c) Red Hat GmbH. Author: Phil Sutter <phil@nwl.cc>
+ *
+ * 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 <nft.h>
+
#include <errno.h>
-#include <stdint.h> /* needed by gmputil.h */
-#include <string.h>
#include <syslog.h>
#include <erec.h>
@@ -533,6 +540,27 @@ static const struct proto_desc *proto_lookup_byname(const char *name)
&proto_dccp,
&proto_sctp,
&proto_th,
+ &proto_vxlan,
+ &proto_gre,
+ &proto_gretap,
+ &proto_geneve,
+ };
+ unsigned int i;
+
+ for (i = 0; i < array_size(proto_tbl); i++) {
+ if (!strcmp(proto_tbl[i]->name, name))
+ return proto_tbl[i];
+ }
+ return NULL;
+}
+
+static const struct proto_desc *inner_proto_lookup_byname(const char *name)
+{
+ const struct proto_desc *proto_tbl[] = {
+ &proto_geneve,
+ &proto_gre,
+ &proto_gretap,
+ &proto_vxlan,
};
unsigned int i;
@@ -546,7 +574,7 @@ static const struct proto_desc *proto_lookup_byname(const char *name)
static struct expr *json_parse_payload_expr(struct json_ctx *ctx,
const char *type, json_t *root)
{
- const char *protocol, *field, *base;
+ const char *tunnel, *protocol, *field, *base;
int offset, len, val;
struct expr *expr;
@@ -558,15 +586,51 @@ static struct expr *json_parse_payload_expr(struct json_ctx *ctx,
val = PROTO_BASE_NETWORK_HDR;
} else if (!strcmp(base, "th")) {
val = PROTO_BASE_TRANSPORT_HDR;
+ } else if (!strcmp(base, "ih")) {
+ val = PROTO_BASE_INNER_HDR;
} else {
json_error(ctx, "Invalid payload base '%s'.", base);
return NULL;
}
+
+ if (len <= 0 || len > (int)NFT_MAX_EXPR_LEN_BITS) {
+ json_error(ctx, "Payload length must be between 0 and %lu, got %d",
+ NFT_MAX_EXPR_LEN_BITS, len);
+ return NULL;
+ }
+
expr = payload_expr_alloc(int_loc, NULL, 0);
payload_init_raw(expr, val, offset, len);
expr->byteorder = BYTEORDER_BIG_ENDIAN;
expr->payload.is_raw = true;
return expr;
+ } else if (!json_unpack(root, "{s:s, s:s, s:s}",
+ "tunnel", &tunnel, "protocol", &protocol, "field", &field)) {
+ const struct proto_desc *proto = proto_lookup_byname(protocol);
+ const struct proto_desc *inner_proto = inner_proto_lookup_byname(tunnel);
+
+ if (!inner_proto) {
+ json_error(ctx, "Unknown payload tunnel protocol '%s'.",
+ tunnel);
+ return NULL;
+ }
+ if (!proto) {
+ json_error(ctx, "Unknown payload protocol '%s'.",
+ protocol);
+ return NULL;
+ }
+ if (json_parse_payload_field(proto, field, &val)) {
+ json_error(ctx, "Unknown %s field '%s'.",
+ protocol, field);
+ return NULL;
+ }
+ expr = payload_expr_alloc(int_loc, proto, val);
+ expr->payload.inner_desc = inner_proto;
+
+ if (proto == &proto_th)
+ expr->payload.is_raw = true;
+
+ return expr;
} else if (!json_unpack(root, "{s:s, s:s}",
"protocol", &protocol, "field", &field)) {
const struct proto_desc *proto = proto_lookup_byname(protocol);
@@ -600,12 +664,18 @@ static struct expr *json_parse_tcp_option_expr(struct json_ctx *ctx,
struct expr *expr;
if (!json_unpack(root, "{s:i, s:i, s:i}",
- "base", &kind, "offset", &offset, "len", &len)) {
+ "base", &kind, "offset", &offset, "len", &len)) {
uint32_t flag = 0;
if (kind < 0 || kind > 255)
return NULL;
+ if (len < 0 || len > (int)NFT_MAX_EXPR_LEN_BITS) {
+ json_error(ctx, "option length must be between 0 and %lu, got %d",
+ NFT_MAX_EXPR_LEN_BITS, len);
+ return NULL;
+ }
+
expr = tcpopt_expr_alloc(int_loc, kind,
TCPOPT_COMMON_KIND);
@@ -671,7 +741,7 @@ static int json_parse_ip_option_field(int type, const char *name, int *val)
}
static struct expr *json_parse_ip_option_expr(struct json_ctx *ctx,
- const char *type, json_t *root)
+ const char *type, json_t *root)
{
const char *desc, *field;
int descval, fieldval;
@@ -687,7 +757,7 @@ static struct expr *json_parse_ip_option_expr(struct json_ctx *ctx,
if (json_unpack(root, "{s:s}", "field", &field)) {
expr = ipopt_expr_alloc(int_loc, descval,
- IPOPT_FIELD_TYPE, 0);
+ IPOPT_FIELD_TYPE);
expr->exthdr.flags = NFT_EXTHDR_F_PRESENT;
return expr;
@@ -696,7 +766,7 @@ static struct expr *json_parse_ip_option_expr(struct json_ctx *ctx,
json_error(ctx, "Unknown ip option field '%s'.", field);
return NULL;
}
- return ipopt_expr_alloc(int_loc, descval, fieldval, 0);
+ return ipopt_expr_alloc(int_loc, descval, fieldval);
}
static int json_parse_sctp_chunk_field(const struct exthdr_desc *desc,
@@ -746,6 +816,22 @@ static struct expr *json_parse_sctp_chunk_expr(struct json_ctx *ctx,
return sctp_chunk_expr_alloc(int_loc, desc->type, fieldval);
}
+static struct expr *json_parse_dccp_option_expr(struct json_ctx *ctx,
+ const char *type, json_t *root)
+{
+ int opt_type;
+
+ if (json_unpack_err(ctx, root, "{s:i}", "type", &opt_type))
+ return NULL;
+
+ if (opt_type < DCCPOPT_TYPE_MIN || opt_type > DCCPOPT_TYPE_MAX) {
+ json_error(ctx, "Unknown dccp option type '%d'.", opt_type);
+ return NULL;
+ }
+
+ return dccpopt_expr_alloc(int_loc, opt_type);
+}
+
static const struct exthdr_desc *exthdr_lookup_byname(const char *name)
{
const struct exthdr_desc *exthdr_tbl[] = {
@@ -1074,13 +1160,13 @@ static struct expr *json_parse_fib_expr(struct json_ctx *ctx,
}
if ((flagval & (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) ==
- (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) {
+ (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) {
json_error(ctx, "fib: saddr and daddr are mutually exclusive");
return NULL;
}
if ((flagval & (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) ==
- (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) {
+ (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) {
json_error(ctx, "fib: iif and oif are mutually exclusive");
return NULL;
}
@@ -1118,6 +1204,18 @@ static struct expr *json_parse_binop_expr(struct json_ctx *ctx,
return NULL;
}
+ if (json_array_size(root) > 2) {
+ left = json_parse_primary_expr(ctx, json_array_get(root, 0));
+ right = json_parse_primary_expr(ctx, json_array_get(root, 1));
+ left = binop_expr_alloc(int_loc, thisop, left, right);
+ for (i = 2; i < json_array_size(root); i++) {
+ jright = json_array_get(root, i);
+ right = json_parse_primary_expr(ctx, jright);
+ left = binop_expr_alloc(int_loc, thisop, left, right);
+ }
+ return left;
+ }
+
if (json_unpack_err(ctx, root, "[o, o!]", &jleft, &jright))
return NULL;
@@ -1452,6 +1550,7 @@ static struct expr *json_parse_expr(struct json_ctx *ctx, json_t *root)
{ "tcp option", json_parse_tcp_option_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_CONCAT },
{ "ip option", json_parse_ip_option_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_CONCAT },
{ "sctp chunk", json_parse_sctp_chunk_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_CONCAT },
+ { "dccp option", json_parse_dccp_option_expr, CTX_F_PRIMARY },
{ "meta", json_parse_meta_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT },
{ "osf", json_parse_osf_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_MAP | CTX_F_CONCAT },
{ "ipsec", json_parse_xfrm_expr, CTX_F_PRIMARY | CTX_F_MAP | CTX_F_CONCAT },
@@ -1650,13 +1749,18 @@ static struct stmt *json_parse_match_stmt(struct json_ctx *ctx,
!strcmp(opstr, expr_op_symbols[op]))
break;
}
- if (op == __OP_MAX) {
+ switch (op) {
+ case OP_EQ ... OP_NEG:
+ break;
+ case __OP_MAX:
if (!strcmp(opstr, "in")) {
op = OP_IMPLICIT;
- } else {
- json_error(ctx, "Unknown relational op '%s'.", opstr);
- return NULL;
+ break;
}
+ /* fall through */
+ default:
+ json_error(ctx, "Invalid relational op '%s'.", opstr);
+ return NULL;
}
left = json_parse_expr(ctx, jleft);
@@ -1676,7 +1780,7 @@ static struct stmt *json_parse_match_stmt(struct json_ctx *ctx,
}
static struct stmt *json_parse_counter_stmt(struct json_ctx *ctx,
- const char *key, json_t *value)
+ const char *key, json_t *value)
{
uint64_t packets, bytes;
struct stmt *stmt;
@@ -1685,8 +1789,8 @@ static struct stmt *json_parse_counter_stmt(struct json_ctx *ctx,
return counter_stmt_alloc(int_loc);
if (!json_unpack(value, "{s:I, s:I}",
- "packets", &packets,
- "bytes", &bytes)) {
+ "packets", &packets,
+ "bytes", &bytes)) {
stmt = counter_stmt_alloc(int_loc);
stmt->counter.packets = packets;
stmt->counter.bytes = bytes;
@@ -1704,6 +1808,27 @@ static struct stmt *json_parse_counter_stmt(struct json_ctx *ctx,
return stmt;
}
+static struct stmt *json_parse_last_stmt(struct json_ctx *ctx,
+ const char *key, json_t *value)
+{
+ struct stmt *stmt;
+ int64_t used;
+
+ if (json_is_null(value))
+ return last_stmt_alloc(int_loc);
+
+ if (!json_unpack(value, "{s:I}", "used", &used)) {
+ stmt = last_stmt_alloc(int_loc);
+ if (used != -1) {
+ stmt->last.used = used;
+ stmt->last.set = 1;
+ }
+ return stmt;
+ }
+
+ return NULL;
+}
+
static struct stmt *json_parse_verdict_stmt(struct json_ctx *ctx,
const char *key, json_t *value)
{
@@ -1717,14 +1842,14 @@ static struct stmt *json_parse_verdict_stmt(struct json_ctx *ctx,
}
static struct stmt *json_parse_mangle_stmt(struct json_ctx *ctx,
- const char *type, json_t *root)
+ const char *type, json_t *root)
{
json_t *jkey, *jvalue;
struct expr *key, *value;
struct stmt *stmt;
if (json_unpack_err(ctx, root, "{s:o, s:o}",
- "key", &jkey, "value", &jvalue))
+ "key", &jkey, "value", &jvalue))
return NULL;
key = json_parse_mangle_lhs_expr(ctx, jkey);
@@ -1777,7 +1902,7 @@ static uint64_t rate_to_bytes(uint64_t val, const char *unit)
}
static struct stmt *json_parse_quota_stmt(struct json_ctx *ctx,
- const char *key, json_t *value)
+ const char *key, json_t *value)
{
struct stmt *stmt;
int inv = 0;
@@ -1824,7 +1949,7 @@ static struct stmt *json_parse_limit_stmt(struct json_ctx *ctx,
const char *key, json_t *value)
{
struct stmt *stmt;
- uint64_t rate, burst = 5;
+ uint64_t rate, burst = 0;
const char *rate_unit = "packets", *time, *burst_unit = "bytes";
int inv = 0;
@@ -1838,6 +1963,9 @@ static struct stmt *json_parse_limit_stmt(struct json_ctx *ctx,
stmt = limit_stmt_alloc(int_loc);
if (!strcmp(rate_unit, "packets")) {
+ if (burst == 0)
+ burst = 5;
+
stmt->limit.type = NFT_LIMIT_PKTS;
stmt->limit.rate = rate;
stmt->limit.burst = burst;
@@ -1901,8 +2029,30 @@ out_err:
return NULL;
}
+static struct stmt *json_parse_flow_offload_stmt(struct json_ctx *ctx,
+ const char *key, json_t *value)
+{
+ const char *opstr, *flowtable;
+
+ if (json_unpack_err(ctx, value, "{s:s, s:s}",
+ "op", &opstr, "flowtable", &flowtable))
+ return NULL;
+
+ if (strcmp(opstr, "add")) {
+ json_error(ctx, "Unknown flow offload statement op '%s'.", opstr);
+ return NULL;
+ }
+
+ if (flowtable[0] != '@') {
+ json_error(ctx, "Illegal flowtable reference in flow offload statement.");
+ return NULL;
+ }
+
+ return flow_offload_stmt_alloc(int_loc, xstrdup(flowtable + 1));
+}
+
static struct stmt *json_parse_notrack_stmt(struct json_ctx *ctx,
- const char *key, json_t *value)
+ const char *key, json_t *value)
{
return notrack_stmt_alloc(int_loc);
}
@@ -1939,6 +2089,23 @@ static struct stmt *json_parse_dup_stmt(struct json_ctx *ctx,
return stmt;
}
+static struct stmt *json_parse_secmark_stmt(struct json_ctx *ctx,
+ const char *key, json_t *value)
+{
+ struct stmt *stmt;
+
+ stmt = objref_stmt_alloc(int_loc);
+ stmt->objref.type = NFT_OBJECT_SECMARK;
+ stmt->objref.expr = json_parse_stmt_expr(ctx, value);
+ if (!stmt->objref.expr) {
+ json_error(ctx, "Invalid secmark reference.");
+ stmt_free(stmt);
+ return NULL;
+ }
+
+ return stmt;
+}
+
static int json_parse_nat_flag(struct json_ctx *ctx,
json_t *root, int *flags)
{
@@ -1995,7 +2162,7 @@ static int json_parse_nat_flags(struct json_ctx *ctx, json_t *root)
}
static int json_parse_nat_type_flag(struct json_ctx *ctx,
- json_t *root, int *flags)
+ json_t *root, int *flags)
{
const struct {
const char *flag;
@@ -2110,7 +2277,6 @@ static struct stmt *json_parse_nat_stmt(struct json_ctx *ctx,
}
stmt->nat.flags = flags;
}
-
if (!json_unpack(value, "{s:o}", "type_flags", &tmp)) {
int flags = json_parse_nat_type_flags(ctx, tmp);
@@ -2125,7 +2291,7 @@ static struct stmt *json_parse_nat_stmt(struct json_ctx *ctx,
}
static struct stmt *json_parse_tproxy_stmt(struct json_ctx *ctx,
- const char *key, json_t *value)
+ const char *key, json_t *value)
{
json_t *jaddr, *tmp;
struct stmt *stmt;
@@ -2161,7 +2327,7 @@ out_free:
}
static struct stmt *json_parse_reject_stmt(struct json_ctx *ctx,
- const char *key, json_t *value)
+ const char *key, json_t *value)
{
struct stmt *stmt = reject_stmt_alloc(int_loc);
const struct datatype *dtype = NULL;
@@ -2177,17 +2343,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;
}
}
@@ -2203,13 +2369,36 @@ static struct stmt *json_parse_reject_stmt(struct json_ctx *ctx,
return stmt;
}
+static void json_parse_set_stmt_list(struct json_ctx *ctx,
+ struct list_head *stmt_list,
+ json_t *stmt_json)
+{
+ struct list_head *head;
+ struct stmt *tmp;
+ json_t *value;
+ size_t index;
+
+ if (!stmt_json)
+ return;
+
+ if (!json_is_array(stmt_json))
+ json_error(ctx, "Unexpected object type in stmt");
+
+ head = stmt_list;
+ json_array_foreach(stmt_json, index, value) {
+ tmp = json_parse_stmt(ctx, value);
+ list_add(&tmp->list, head);
+ head = &tmp->list;
+ }
+}
+
static struct stmt *json_parse_set_stmt(struct json_ctx *ctx,
- const char *key, json_t *value)
+ const char *key, json_t *value)
{
const char *opstr, *set;
struct expr *expr, *expr2;
+ json_t *elem, *stmt_json;
struct stmt *stmt;
- json_t *elem;
int op;
if (json_unpack_err(ctx, value, "{s:s, s:o, s:s}",
@@ -2244,6 +2433,67 @@ static struct stmt *json_parse_set_stmt(struct json_ctx *ctx,
stmt->set.op = op;
stmt->set.key = expr;
stmt->set.set = expr2;
+
+ if (!json_unpack(value, "{s:o}", "stmt", &stmt_json))
+ json_parse_set_stmt_list(ctx, &stmt->set.stmt_list, stmt_json);
+
+ return stmt;
+}
+
+static struct stmt *json_parse_map_stmt(struct json_ctx *ctx,
+ const char *key, json_t *value)
+{
+ struct expr *expr, *expr2, *expr_data;
+ json_t *elem, *data, *stmt_json;
+ const char *opstr, *set;
+ struct stmt *stmt;
+ int op;
+
+ if (json_unpack_err(ctx, value, "{s:s, s:o, s:o, s:s}",
+ "op", &opstr, "elem", &elem, "data", &data, "map", &set))
+ return NULL;
+
+ if (!strcmp(opstr, "add")) {
+ op = NFT_DYNSET_OP_ADD;
+ } else if (!strcmp(opstr, "update")) {
+ op = NFT_DYNSET_OP_UPDATE;
+ } else if (!strcmp(opstr, "delete")) {
+ op = NFT_DYNSET_OP_DELETE;
+ } else {
+ json_error(ctx, "Unknown map statement op '%s'.", opstr);
+ return NULL;
+ }
+
+ expr = json_parse_set_elem_expr_stmt(ctx, elem);
+ if (!expr) {
+ json_error(ctx, "Illegal map statement element.");
+ return NULL;
+ }
+
+ expr_data = json_parse_set_elem_expr_stmt(ctx, data);
+ if (!expr_data) {
+ json_error(ctx, "Illegal map expression data.");
+ expr_free(expr);
+ return NULL;
+ }
+
+ if (set[0] != '@') {
+ json_error(ctx, "Illegal map reference in map statement.");
+ expr_free(expr);
+ expr_free(expr_data);
+ return NULL;
+ }
+ expr2 = symbol_expr_alloc(int_loc, SYMBOL_SET, NULL, set + 1);
+
+ stmt = map_stmt_alloc(int_loc);
+ stmt->map.op = op;
+ stmt->map.key = expr;
+ stmt->map.data = expr_data;
+ stmt->map.set = expr2;
+
+ if (!json_unpack(value, "{s:o}", "stmt", &stmt_json))
+ json_parse_set_stmt_list(ctx, &stmt->set.stmt_list, stmt_json);
+
return stmt;
}
@@ -2483,7 +2733,7 @@ static struct stmt *json_parse_cthelper_stmt(struct json_ctx *ctx,
}
static struct stmt *json_parse_cttimeout_stmt(struct json_ctx *ctx,
- const char *key, json_t *value)
+ const char *key, json_t *value)
{
struct stmt *stmt = objref_stmt_alloc(int_loc);
@@ -2518,7 +2768,7 @@ static struct stmt *json_parse_meter_stmt(struct json_ctx *ctx,
json_t *jkey, *jstmt;
struct stmt *stmt;
const char *name;
- uint32_t size = 0xffff;
+ uint32_t size = 0;
if (json_unpack_err(ctx, value, "{s:s, s:o, s:o}",
"name", &name, "key", &jkey, "stmt", &jstmt))
@@ -2628,6 +2878,21 @@ static struct stmt *json_parse_connlimit_stmt(struct json_ctx *ctx,
return stmt;
}
+static struct stmt *json_parse_optstrip_stmt(struct json_ctx *ctx,
+ const char *key, json_t *value)
+{
+ struct expr *expr = json_parse_expr(ctx, value);
+
+ if (!expr ||
+ expr->etype != EXPR_EXTHDR ||
+ expr->exthdr.op != NFT_EXTHDR_OP_TCPOPT) {
+ json_error(ctx, "Illegal TCP optstrip argument");
+ return NULL;
+ }
+
+ return optstrip_stmt_alloc(int_loc, expr);
+}
+
static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
{
struct {
@@ -2644,7 +2909,9 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
{ "counter", json_parse_counter_stmt },
{ "mangle", json_parse_mangle_stmt },
{ "quota", json_parse_quota_stmt },
+ { "last", json_parse_last_stmt },
{ "limit", json_parse_limit_stmt },
+ { "flow", json_parse_flow_offload_stmt },
{ "fwd", json_parse_fwd_stmt },
{ "notrack", json_parse_notrack_stmt },
{ "dup", json_parse_dup_stmt },
@@ -2654,6 +2921,7 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
{ "redirect", json_parse_nat_stmt },
{ "reject", json_parse_reject_stmt },
{ "set", json_parse_set_stmt },
+ { "map", json_parse_map_stmt },
{ "log", json_parse_log_stmt },
{ "ct helper", json_parse_cthelper_stmt },
{ "ct timeout", json_parse_cttimeout_stmt },
@@ -2663,6 +2931,8 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
{ "ct count", json_parse_connlimit_stmt },
{ "tproxy", json_parse_tproxy_stmt },
{ "synproxy", json_parse_synproxy_stmt },
+ { "reset", json_parse_optstrip_stmt },
+ { "secmark", json_parse_secmark_stmt },
};
const char *type;
unsigned int i;
@@ -2682,6 +2952,11 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
return verdict_stmt_alloc(int_loc, expr);
}
+ if (!strcmp(type, "xt")) {
+ json_error(ctx, "unsupported xtables compat expression, use iptables-nft with this ruleset");
+ return NULL;
+ }
+
for (i = 0; i < array_size(stmt_parser_tbl); i++) {
if (!strcmp(type, stmt_parser_tbl[i].key))
return stmt_parser_tbl[i].cb(ctx, stmt_parser_tbl[i].key, tmp);
@@ -2694,17 +2969,21 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root,
enum cmd_ops op, enum cmd_obj obj)
{
+ const char *family = "", *comment = NULL;
struct handle h = {
.table.location = *int_loc,
};
- const char *family = "";
+ struct table *table = NULL;
if (json_unpack_err(ctx, root, "{s:s}",
"family", &family))
return NULL;
- if (op != CMD_DELETE &&
- json_unpack_err(ctx, root, "{s:s}", "name", &h.table.name)) {
- return NULL;
+
+ if (op != CMD_DELETE) {
+ if (json_unpack_err(ctx, root, "{s:s}", "name", &h.table.name))
+ return NULL;
+
+ json_unpack(root, "{s:s}", "comment", &comment);
} else if (op == CMD_DELETE &&
json_unpack(root, "{s:s}", "name", &h.table.name) &&
json_unpack(root, "{s:I}", "handle", &h.handle.id)) {
@@ -2718,10 +2997,16 @@ static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root,
if (h.table.name)
h.table.name = xstrdup(h.table.name);
+ if (comment) {
+ table = table_alloc();
+ handle_merge(&table->handle, &h);
+ table->comment = xstrdup(comment);
+ }
+
if (op == CMD_ADD)
json_object_del(root, "handle");
- return cmd_alloc(op, obj, &h, int_loc, NULL);
+ return cmd_alloc(op, obj, &h, int_loc, table);
}
static struct expr *parse_policy(const char *policy)
@@ -2740,24 +3025,60 @@ static struct expr *parse_policy(const char *policy)
sizeof(int) * BITS_PER_BYTE, &policy_num);
}
+static struct expr *json_parse_devs(struct json_ctx *ctx, json_t *root)
+{
+ struct expr *tmp, *expr = compound_expr_alloc(int_loc, EXPR_LIST);
+ const char *dev;
+ json_t *value;
+ size_t index;
+
+ if (!json_unpack(root, "s", &dev)) {
+ tmp = constant_expr_alloc(int_loc, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ strlen(dev) * BITS_PER_BYTE, dev);
+ compound_expr_add(expr, tmp);
+ return expr;
+ }
+ if (!json_is_array(root)) {
+ expr_free(expr);
+ return NULL;
+ }
+
+ json_array_foreach(root, index, value) {
+ if (json_unpack(value, "s", &dev)) {
+ json_error(ctx, "Invalid device at index %zu.",
+ index);
+ expr_free(expr);
+ return NULL;
+ }
+ tmp = constant_expr_alloc(int_loc, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ strlen(dev) * BITS_PER_BYTE, dev);
+ compound_expr_add(expr, tmp);
+ }
+ return expr;
+}
+
static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root,
enum cmd_ops op, enum cmd_obj obj)
{
struct handle h = {
.table.location = *int_loc,
};
- const char *family = "", *policy = "", *type, *hookstr;
- const char name[IFNAMSIZ];
- struct chain *chain;
+ const char *family = "", *policy = "", *type, *hookstr, *comment = NULL;
+ struct chain *chain = NULL;
+ json_t *devs = NULL;
int prio;
if (json_unpack_err(ctx, root, "{s:s, s:s}",
"family", &family,
"table", &h.table.name))
return NULL;
- if (op != CMD_DELETE &&
- json_unpack_err(ctx, root, "{s:s}", "name", &h.chain.name)) {
- return NULL;
+ if (op != CMD_DELETE) {
+ if (json_unpack_err(ctx, root, "{s:s}", "name", &h.chain.name))
+ return NULL;
+
+ json_unpack(root, "{s:s}", "comment", &comment);
} else if (op == CMD_DELETE &&
json_unpack(root, "{s:s}", "name", &h.chain.name) &&
json_unpack(root, "{s:I}", "handle", &h.handle.id)) {
@@ -2772,14 +3093,22 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root,
if (h.chain.name)
h.chain.name = xstrdup(h.chain.name);
+ if (comment) {
+ chain = chain_alloc();
+ handle_merge(&chain->handle, &h);
+ chain->comment = xstrdup(comment);
+ }
+
if (op == CMD_DELETE ||
op == CMD_LIST ||
op == CMD_FLUSH ||
json_unpack(root, "{s:s, s:s, s:i}",
"type", &type, "hook", &hookstr, "prio", &prio))
- return cmd_alloc(op, obj, &h, int_loc, NULL);
+ return cmd_alloc(op, obj, &h, int_loc, chain);
+
+ if (!chain)
+ chain = chain_alloc();
- chain = chain_alloc(NULL);
chain->flags |= CHAIN_F_BASECHAIN;
chain->type.str = xstrdup(type);
chain->priority.expr = constant_expr_alloc(int_loc, &integer_type,
@@ -2793,16 +3122,15 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root,
return NULL;
}
- if (!json_unpack(root, "{s:s}", "dev", &name)) {
- struct expr *dev_expr, *expr;
+ json_unpack(root, "{s:o}", "dev", &devs);
- dev_expr = compound_expr_alloc(int_loc, EXPR_LIST);
- expr = constant_expr_alloc(int_loc, &integer_type,
- BYTEORDER_HOST_ENDIAN,
- strlen(name) * BITS_PER_BYTE,
- name);
- compound_expr_add(dev_expr, expr);
- chain->dev_expr = dev_expr;
+ if (devs) {
+ chain->dev_expr = json_parse_devs(ctx, devs);
+ if (!chain->dev_expr) {
+ json_error(ctx, "Invalid chain dev.");
+ chain_free(chain);
+ return NULL;
+ }
}
if (!json_unpack(root, "{s:s}", "policy", &policy)) {
@@ -2842,7 +3170,7 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root,
if (op != CMD_DELETE &&
json_unpack_err(ctx, root, "{s:o}", "expr", &tmp))
return NULL;
- else if (op == CMD_DELETE &&
+ else if ((op == CMD_DELETE || op == CMD_DESTROY) &&
json_unpack_err(ctx, root, "{s:I}", "handle", &h.handle.id))
return NULL;
@@ -2853,7 +3181,7 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root,
h.table.name = xstrdup(h.table.name);
h.chain.name = xstrdup(h.chain.name);
- if (op == CMD_DELETE)
+ if (op == CMD_DELETE || op == CMD_DESTROY)
return cmd_alloc(op, obj, &h, int_loc, NULL);
if (!json_is_array(tmp)) {
@@ -2901,14 +3229,18 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root,
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;
}
@@ -2924,6 +3256,7 @@ static int string_to_set_flag(const char *str)
{ NFT_SET_CONSTANT, "constant" },
{ NFT_SET_INTERVAL, "interval" },
{ NFT_SET_TIMEOUT, "timeout" },
+ { NFT_SET_EVAL, "dynamic" },
};
unsigned int i;
@@ -2938,9 +3271,9 @@ 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;
- json_t *tmp;
if (json_unpack_err(ctx, root, "{s:s, s:s}",
"family", &family,
@@ -2949,7 +3282,7 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root,
if (op != CMD_DELETE &&
json_unpack_err(ctx, root, "{s:s}", "name", &h.set.name)) {
return NULL;
- } else if (op == CMD_DELETE &&
+ } else if ((op == CMD_DELETE || op == CMD_DESTROY) &&
json_unpack(root, "{s:s}", "name", &h.set.name) &&
json_unpack(root, "{s:I}", "handle", &h.handle.id)) {
json_error(ctx, "Either name or handle required to delete a set.");
@@ -2966,14 +3299,16 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root,
switch (op) {
case CMD_DELETE:
+ case CMD_DESTROY:
case CMD_LIST:
case CMD_FLUSH:
+ case CMD_RESET:
return cmd_alloc(op, obj, &h, int_loc, NULL);
default:
break;
}
- set = set_alloc(NULL);
+ set = set_alloc(&internal_location);
if (json_unpack(root, "{s:o}", "type", &tmp)) {
json_error(ctx, "Invalid set type.");
@@ -2989,19 +3324,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;
@@ -3050,6 +3385,10 @@ 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);
handle_merge(&set->handle, &h);
@@ -3091,37 +3430,6 @@ static struct cmd *json_parse_cmd_add_element(struct json_ctx *ctx,
return cmd_alloc(op, cmd_obj, &h, int_loc, expr);
}
-static struct expr *json_parse_flowtable_devs(struct json_ctx *ctx,
- json_t *root)
-{
- struct expr *tmp, *expr = compound_expr_alloc(int_loc, EXPR_LIST);
- const char *dev;
- json_t *value;
- size_t index;
-
- if (!json_unpack(root, "s", &dev)) {
- tmp = symbol_expr_alloc(int_loc, SYMBOL_VALUE, NULL, dev);
- compound_expr_add(expr, tmp);
- return expr;
- }
- if (!json_is_array(root)) {
- expr_free(expr);
- return NULL;
- }
-
- json_array_foreach(root, index, value) {
- if (json_unpack(value, "s", &dev)) {
- json_error(ctx, "Invalid flowtable dev at index %zu.",
- index);
- expr_free(expr);
- return NULL;
- }
- tmp = symbol_expr_alloc(int_loc, SYMBOL_VALUE, NULL, dev);
- compound_expr_add(expr, tmp);
- }
- return expr;
-}
-
static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx,
json_t *root, enum cmd_ops op,
enum cmd_obj cmd_obj)
@@ -3129,7 +3437,7 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx,
const char *family, *hook, *hookstr;
struct flowtable *flowtable;
struct handle h = { 0 };
- json_t *devs;
+ json_t *devs = NULL;
int prio;
if (json_unpack_err(ctx, root, "{s:s, s:s}",
@@ -3140,7 +3448,7 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx,
if (op != CMD_DELETE &&
json_unpack_err(ctx, root, "{s:s}", "name", &h.flowtable.name)) {
return NULL;
- } else if (op == CMD_DELETE &&
+ } else if ((op == CMD_DELETE || op == CMD_DESTROY) &&
json_unpack(root, "{s:s}", "name", &h.flowtable.name) &&
json_unpack(root, "{s:I}", "handle", &h.handle.id)) {
json_error(ctx, "Either name or handle required to delete a flowtable.");
@@ -3155,17 +3463,18 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx,
if (h.flowtable.name)
h.flowtable.name = xstrdup(h.flowtable.name);
- if (op == CMD_DELETE || op == CMD_LIST)
+ if (op == CMD_DELETE || op == CMD_LIST || op == CMD_DESTROY)
return cmd_alloc(op, cmd_obj, &h, int_loc, NULL);
- if (json_unpack_err(ctx, root, "{s:s, s:I, s:o}",
+ if (json_unpack_err(ctx, root, "{s:s, s:i}",
"hook", &hook,
- "prio", &prio,
- "dev", &devs)) {
+ "prio", &prio)) {
handle_free(&h);
return NULL;
}
+ json_unpack(root, "{s:o}", "dev", &devs);
+
hookstr = chain_hookname_lookup(hook);
if (!hookstr) {
json_error(ctx, "Invalid flowtable hook '%s'.", hook);
@@ -3180,12 +3489,14 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx,
BYTEORDER_HOST_ENDIAN,
sizeof(int) * BITS_PER_BYTE, &prio);
- flowtable->dev_expr = json_parse_flowtable_devs(ctx, devs);
- if (!flowtable->dev_expr) {
- json_error(ctx, "Invalid flowtable dev.");
- flowtable_free(flowtable);
- handle_free(&h);
- return NULL;
+ if (devs) {
+ flowtable->dev_expr = json_parse_devs(ctx, devs);
+ if (!flowtable->dev_expr) {
+ json_error(ctx, "Invalid flowtable dev.");
+ flowtable_free(flowtable);
+ handle_free(&h);
+ return NULL;
+ }
}
return cmd_alloc(op, cmd_obj, &h, int_loc, flowtable);
}
@@ -3196,7 +3507,7 @@ static int json_parse_ct_timeout_policy(struct json_ctx *ctx,
json_t *tmp, *val;
const char *key;
- if (!json_unpack(root, "{s:o}", "policy", &tmp))
+ if (json_unpack(root, "{s:o}", "policy", &tmp))
return 0;
if (!json_is_object(tmp)) {
@@ -3228,8 +3539,8 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
{
const char *family, *tmp, *rate_unit = "packets", *burst_unit = "bytes";
uint32_t l3proto = NFPROTO_UNSPEC;
+ int inv = 0, flags = 0, i, j;
struct handle h = { 0 };
- int inv = 0, flags = 0;
struct obj *obj;
json_t *jflags;
@@ -3241,7 +3552,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
cmd_obj == NFT_OBJECT_CT_HELPER) &&
json_unpack_err(ctx, root, "{s:s}", "name", &h.obj.name)) {
return NULL;
- } else if (op == CMD_DELETE &&
+ } else if ((op == CMD_DELETE || op == CMD_DESTROY) &&
cmd_obj != NFT_OBJECT_CT_HELPER &&
json_unpack(root, "{s:s}", "name", &h.obj.name) &&
json_unpack(root, "{s:I}", "handle", &h.handle.id)) {
@@ -3257,7 +3568,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
if (h.obj.name)
h.obj.name = xstrdup(h.obj.name);
- if (op == CMD_DELETE || op == CMD_LIST) {
+ if (op == CMD_DELETE || op == CMD_LIST || op == CMD_DESTROY) {
if (cmd_obj == NFT_OBJECT_CT_HELPER)
return cmd_alloc_obj_ct(op, NFT_OBJECT_CT_HELPER,
&h, int_loc, obj_alloc(int_loc));
@@ -3266,6 +3577,9 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
obj = obj_alloc(int_loc);
+ if (!json_unpack(root, "{s:s}", "comment", &obj->comment))
+ obj->comment = xstrdup(obj->comment);
+
switch (cmd_obj) {
case CMD_OBJ_COUNTER:
obj->type = NFT_OBJECT_COUNTER;
@@ -3348,7 +3662,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
obj_free(obj);
return NULL;
}
- obj->ct_helper.l3proto = l3proto;
+ obj->ct_timeout.l3proto = l3proto;
init_list_head(&obj->ct_timeout.timeout_list);
if (json_parse_ct_timeout_policy(ctx, root, obj)) {
@@ -3377,11 +3691,12 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
return NULL;
}
}
- if (!json_unpack(root, "{s:o}", "dport", &tmp))
- obj->ct_expect.dport = atoi(tmp);
- json_unpack(root, "{s:I}", "timeout", &obj->ct_expect.timeout);
- if (!json_unpack(root, "{s:o}", "size", &tmp))
- obj->ct_expect.size = atoi(tmp);
+ if (!json_unpack(root, "{s:i}", "dport", &i))
+ obj->ct_expect.dport = i;
+ if (!json_unpack(root, "{s:i}", "timeout", &i))
+ obj->ct_expect.timeout = i;
+ if (!json_unpack(root, "{s:i}", "size", &i))
+ obj->ct_expect.size = i;
break;
case CMD_OBJ_LIMIT:
obj->type = NFT_OBJECT_LIMIT;
@@ -3393,7 +3708,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
}
json_unpack(root, "{s:s}", "rate_unit", &rate_unit);
json_unpack(root, "{s:b}", "inv", &inv);
- json_unpack(root, "{s:I}", "burst", &obj->limit.burst);
+ json_unpack(root, "{s:i}", "burst", &obj->limit.burst);
json_unpack(root, "{s:s}", "burst_unit", &burst_unit);
if (!strcmp(rate_unit, "packets")) {
@@ -3411,11 +3726,12 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
case CMD_OBJ_SYNPROXY:
obj->type = NFT_OBJECT_SYNPROXY;
if (json_unpack_err(ctx, root, "{s:i, s:i}",
- "mss", &obj->synproxy.mss,
- "wscale", &obj->synproxy.wscale)) {
+ "mss", &i, "wscale", &j)) {
obj_free(obj);
return NULL;
}
+ obj->synproxy.mss = i;
+ obj->synproxy.wscale = j;
obj->synproxy.flags |= NF_SYNPROXY_OPT_MSS;
obj->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE;
if (!json_unpack(root, "{s:o}", "flags", &jflags)) {
@@ -3459,7 +3775,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;
@@ -3656,6 +3973,39 @@ static struct cmd *json_parse_cmd_list(struct json_ctx *ctx,
return NULL;
}
+static struct cmd *json_parse_cmd_reset_rule(struct json_ctx *ctx,
+ json_t *root, enum cmd_ops op,
+ enum cmd_obj obj)
+{
+ struct handle h = {
+ .family = NFPROTO_UNSPEC,
+ };
+ const char *family = NULL, *table = NULL, *chain = NULL;
+
+
+ if (obj == CMD_OBJ_RULE &&
+ json_unpack_err(ctx, root, "{s:s, s:s, s:s, s:I}",
+ "family", &family, "table", &table,
+ "chain", &chain, "handle", &h.handle.id))
+ return NULL;
+ else if (obj == CMD_OBJ_RULES) {
+ json_unpack(root, "{s:s}", "family", &family);
+ json_unpack(root, "{s:s}", "table", &table);
+ json_unpack(root, "{s:s}", "chain", &chain);
+ }
+
+ if (family && parse_family(family, &h.family)) {
+ json_error(ctx, "Unknown family '%s'.", family);
+ return NULL;
+ }
+ if (table) {
+ h.table.name = xstrdup(table);
+ if (chain)
+ h.chain.name = xstrdup(chain);
+ }
+ return cmd_alloc(op, obj, &h, int_loc, NULL);
+}
+
static struct cmd *json_parse_cmd_reset(struct json_ctx *ctx,
json_t *root, enum cmd_ops op)
{
@@ -3669,6 +4019,11 @@ static struct cmd *json_parse_cmd_reset(struct json_ctx *ctx,
{ "counters", CMD_OBJ_COUNTERS, json_parse_cmd_list_multiple },
{ "quota", CMD_OBJ_QUOTA, json_parse_cmd_add_object },
{ "quotas", CMD_OBJ_QUOTAS, json_parse_cmd_list_multiple },
+ { "rule", CMD_OBJ_RULE, json_parse_cmd_reset_rule },
+ { "rules", CMD_OBJ_RULES, json_parse_cmd_reset_rule },
+ { "element", CMD_OBJ_ELEMENTS, json_parse_cmd_add_element },
+ { "set", CMD_OBJ_SET, json_parse_cmd_add_set },
+ { "map", CMD_OBJ_MAP, json_parse_cmd_add_set },
};
unsigned int i;
json_t *tmp;
@@ -3767,6 +4122,7 @@ static struct cmd *json_parse_cmd(struct json_ctx *ctx, json_t *root)
{ "reset", CMD_RESET, json_parse_cmd_reset },
{ "flush", CMD_FLUSH, json_parse_cmd_flush },
{ "rename", CMD_RENAME, json_parse_cmd_rename },
+ { "destroy", CMD_DESTROY, json_parse_cmd_add },
//{ "export", CMD_EXPORT, json_parse_cmd_export },
//{ "monitor", CMD_MONITOR, json_parse_cmd_monitor },
//{ "describe", CMD_DESCRIBE, json_parse_cmd_describe }
@@ -3789,13 +4145,14 @@ static int json_verify_metainfo(struct json_ctx *ctx, json_t *root)
{
int schema_version;
- if (!json_unpack(root, "{s:i}", "json_schema_version", &schema_version))
- return 0;
-
- if (schema_version > JSON_SCHEMA_VERSION) {
- json_error(ctx, "Schema version %d not supported, maximum supported version is %d\n",
- schema_version, JSON_SCHEMA_VERSION);
- return 1;
+ if (!json_unpack(root, "{s:i}", "json_schema_version", &schema_version)) {
+ if (schema_version > JSON_SCHEMA_VERSION) {
+ json_error(ctx,
+ "Schema version %d not supported, maximum"
+ " supported version is %d\n",
+ schema_version, JSON_SCHEMA_VERSION);
+ return 1;
+ }
}
return 0;
diff --git a/src/payload.c b/src/payload.c
index c662900b..44aa834c 100644
--- a/src/payload.c
+++ b/src/payload.c
@@ -10,11 +10,10 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
#include <net/if_arp.h>
#include <arpa/inet.h>
#include <linux/netfilter.h>
@@ -47,6 +46,10 @@ static void payload_expr_print(const struct expr *expr, struct output_ctx *octx)
const struct proto_desc *desc;
const struct proto_hdr_template *tmpl;
+ if (expr->payload.inner_desc &&
+ expr->payload.inner_desc != expr->payload.desc)
+ nft_print(octx, "%s ", expr->payload.inner_desc->name);
+
desc = expr->payload.desc;
tmpl = expr->payload.tmpl;
if (payload_is_known(expr))
@@ -67,6 +70,7 @@ bool payload_expr_cmp(const struct expr *e1, const struct expr *e2)
static void payload_expr_clone(struct expr *new, const struct expr *expr)
{
+ new->payload.inner_desc = expr->payload.inner_desc;
new->payload.desc = expr->payload.desc;
new->payload.tmpl = expr->payload.tmpl;
new->payload.base = expr->payload.base;
@@ -114,21 +118,32 @@ static void payload_expr_pctx_update(struct proto_ctx *ctx,
assert(desc->base <= PROTO_BASE_MAX);
if (desc->base == base->base) {
- assert(base->length > 0);
-
- if (!left->payload.is_raw)
- ctx->protocol[base->base].offset += base->length;
+ if (!left->payload.is_raw) {
+ if (desc->base == PROTO_BASE_LL_HDR &&
+ ctx->stacked_ll_count < PROTO_CTX_NUM_PROTOS) {
+ assert(base->length > 0);
+ ctx->stacked_ll[ctx->stacked_ll_count] = base;
+ ctx->stacked_ll_count++;
+ }
+ }
}
proto_ctx_update(ctx, desc->base, loc, desc);
}
#define NFTNL_UDATA_SET_KEY_PAYLOAD_DESC 0
#define NFTNL_UDATA_SET_KEY_PAYLOAD_TYPE 1
-#define NFTNL_UDATA_SET_KEY_PAYLOAD_MAX 2
+#define NFTNL_UDATA_SET_KEY_PAYLOAD_BASE 2
+#define NFTNL_UDATA_SET_KEY_PAYLOAD_OFFSET 3
+#define NFTNL_UDATA_SET_KEY_PAYLOAD_LEN 4
+#define NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC 5
+#define NFTNL_UDATA_SET_KEY_PAYLOAD_MAX 6
static unsigned int expr_payload_type(const struct proto_desc *desc,
const struct proto_hdr_template *tmpl)
{
+ if (desc->id == PROTO_DESC_UNKNOWN)
+ return 0;
+
return (unsigned int)(tmpl - &desc->templates[0]);
}
@@ -142,10 +157,24 @@ static int payload_expr_build_udata(struct nftnl_udata_buf *udbuf,
nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_DESC, desc->id);
nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_TYPE, type);
+ if (desc->id == 0) {
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_BASE,
+ expr->payload.base);
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_OFFSET,
+ expr->payload.offset);
+ }
+ if (expr->dtype->type == TYPE_INTEGER)
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_LEN, expr->len);
+
+ if (expr->payload.inner_desc) {
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC,
+ expr->payload.inner_desc->id);
+ }
+
return 0;
}
-static const struct proto_desc *find_proto_desc(const struct nftnl_udata *ud)
+const struct proto_desc *find_proto_desc(const struct nftnl_udata *ud)
{
return proto_find_desc(nftnl_udata_get_u32(ud));
}
@@ -159,6 +188,10 @@ static int payload_parse_udata(const struct nftnl_udata *attr, void *data)
switch (type) {
case NFTNL_UDATA_SET_KEY_PAYLOAD_DESC:
case NFTNL_UDATA_SET_KEY_PAYLOAD_TYPE:
+ case NFTNL_UDATA_SET_KEY_PAYLOAD_BASE:
+ case NFTNL_UDATA_SET_KEY_PAYLOAD_OFFSET:
+ case NFTNL_UDATA_SET_KEY_PAYLOAD_LEN:
+ case NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC:
if (len != sizeof(uint32_t))
return -1;
break;
@@ -173,8 +206,10 @@ static int payload_parse_udata(const struct nftnl_udata *attr, void *data)
static struct expr *payload_expr_parse_udata(const struct nftnl_udata *attr)
{
const struct nftnl_udata *ud[NFTNL_UDATA_SET_KEY_PAYLOAD_MAX + 1] = {};
+ unsigned int type, base, offset, len = 0;
const struct proto_desc *desc;
- unsigned int type;
+ bool is_raw = false;
+ struct expr *expr;
int err;
err = nftnl_udata_parse(nftnl_udata_get(attr), nftnl_udata_len(attr),
@@ -187,12 +222,44 @@ static struct expr *payload_expr_parse_udata(const struct nftnl_udata *attr)
return NULL;
desc = find_proto_desc(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_DESC]);
- if (!desc)
- return NULL;
+ if (!desc) {
+ if (!ud[NFTNL_UDATA_SET_KEY_PAYLOAD_BASE] ||
+ !ud[NFTNL_UDATA_SET_KEY_PAYLOAD_OFFSET])
+ return NULL;
+
+ base = nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_BASE]);
+ offset = nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_OFFSET]);
+ is_raw = true;
+ }
type = nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_TYPE]);
+ if (ud[NFTNL_UDATA_SET_KEY_PAYLOAD_LEN])
+ len = nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_LEN]);
+
+ expr = payload_expr_alloc(&internal_location, desc, type);
+
+ if (len)
+ expr->len = len;
+
+ if (is_raw) {
+ struct datatype *dtype;
- return payload_expr_alloc(&internal_location, desc, type);
+ expr->payload.base = base;
+ expr->payload.offset = offset;
+ expr->payload.is_raw = true;
+ expr->len = len;
+ dtype = datatype_clone(&xinteger_type);
+ dtype->size = len;
+ dtype->byteorder = BYTEORDER_BIG_ENDIAN;
+ __datatype_set(expr, dtype);
+ }
+
+ if (ud[NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC]) {
+ desc = find_proto_desc(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC]);
+ expr->payload.inner_desc = desc;
+ }
+
+ return expr;
}
const struct expr_ops payload_expr_ops = {
@@ -269,7 +336,7 @@ void payload_init_raw(struct expr *expr, enum proto_bases base,
expr->payload.base = base;
expr->payload.offset = offset;
expr->len = len;
- expr->dtype = &integer_type;
+ expr->dtype = &xinteger_type;
if (base != PROTO_BASE_TRANSPORT_HDR)
return;
@@ -338,9 +405,11 @@ static int payload_add_dependency(struct eval_ctx *ctx,
{
const struct proto_hdr_template *tmpl;
struct expr *dep, *left, *right;
+ struct proto_ctx *pctx;
struct stmt *stmt;
- int protocol = proto_find_num(desc, upper);
+ int protocol;
+ protocol = proto_find_num(desc, upper);
if (protocol < 0)
return expr_error(ctx->msgs, expr,
"conflicting protocols specified: %s vs. %s",
@@ -357,20 +426,28 @@ static int payload_add_dependency(struct eval_ctx *ctx,
constant_data_ptr(protocol, tmpl->len));
dep = relational_expr_alloc(&expr->location, OP_EQ, left, right);
+
stmt = expr_stmt_alloc(&dep->location, dep);
- if (stmt_evaluate(ctx, stmt) < 0) {
- return expr_error(ctx->msgs, expr,
- "dependency statement is invalid");
+ if (stmt_dependency_evaluate(ctx, stmt) < 0)
+ return -1;
+
+ if (ctx->inner_desc) {
+ if (tmpl->meta_key)
+ left->meta.inner_desc = ctx->inner_desc;
+ else
+ left->payload.inner_desc = ctx->inner_desc;
}
- relational_expr_pctx_update(&ctx->pctx, dep);
+
+ pctx = eval_proto_ctx(ctx);
+ relational_expr_pctx_update(pctx, dep);
*res = stmt;
return 0;
}
static const struct proto_desc *
-payload_get_get_ll_hdr(const struct eval_ctx *ctx)
+payload_get_get_ll_hdr(const struct proto_ctx *pctx)
{
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_INET:
return &proto_inet;
case NFPROTO_BRIDGE:
@@ -387,9 +464,11 @@ payload_get_get_ll_hdr(const struct eval_ctx *ctx)
static const struct proto_desc *
payload_gen_special_dependency(struct eval_ctx *ctx, const struct expr *expr)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
+
switch (expr->payload.base) {
case PROTO_BASE_LL_HDR:
- return payload_get_get_ll_hdr(ctx);
+ return payload_get_get_ll_hdr(pctx);
case PROTO_BASE_TRANSPORT_HDR:
if (expr->payload.desc == &proto_icmp ||
expr->payload.desc == &proto_icmp6 ||
@@ -397,13 +476,21 @@ payload_gen_special_dependency(struct eval_ctx *ctx, const struct expr *expr)
const struct proto_desc *desc, *desc_upper;
struct stmt *nstmt;
- desc = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ desc = pctx->protocol[PROTO_BASE_LL_HDR].desc;
if (!desc) {
- desc = payload_get_get_ll_hdr(ctx);
+ desc = payload_get_get_ll_hdr(pctx);
if (!desc)
break;
}
+ /* this tunnel protocol does not encapsulate an inner
+ * link layer, use proto_netdev which relies on
+ * NFT_META_PROTOCOL for dependencies.
+ */
+ if (expr->payload.inner_desc &&
+ !(expr->payload.inner_desc->inner.flags & NFT_INNER_LL))
+ desc = &proto_netdev;
+
desc_upper = &proto_ip6;
if (expr->payload.desc == &proto_icmp ||
expr->payload.desc == &proto_igmp)
@@ -449,11 +536,14 @@ payload_gen_special_dependency(struct eval_ctx *ctx, const struct expr *expr)
int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
struct stmt **res)
{
- const struct hook_proto_desc *h = &hook_proto_desc[ctx->pctx.family];
+ const struct hook_proto_desc *h;
const struct proto_desc *desc;
+ struct proto_ctx *pctx;
struct stmt *stmt;
uint16_t type;
+ pctx = eval_proto_ctx(ctx);
+ h = &hook_proto_desc[pctx->family];
if (expr->payload.base < h->base) {
if (expr->payload.base < h->base - 1)
return expr_error(ctx->msgs, expr,
@@ -466,15 +556,15 @@ int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
"for this family");
stmt = meta_stmt_meta_iiftype(&expr->location, type);
- if (stmt_evaluate(ctx, stmt) < 0) {
- return expr_error(ctx->msgs, expr,
- "dependency statement is invalid");
- }
+ if (stmt_dependency_evaluate(ctx, stmt) < 0)
+ return -1;
+
*res = stmt;
+
return 0;
}
- desc = ctx->pctx.protocol[expr->payload.base - 1].desc;
+ desc = pctx->protocol[expr->payload.base - 1].desc;
/* Special case for mixed IPv4/IPv6 and bridge tables */
if (desc == NULL)
desc = payload_gen_special_dependency(ctx, expr);
@@ -485,7 +575,7 @@ int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
"no %s protocol specified",
proto_base_names[expr->payload.base - 1]);
- if (ctx->pctx.family == NFPROTO_BRIDGE && desc == &proto_eth) {
+ if (pctx->family == NFPROTO_BRIDGE && desc == &proto_eth) {
/* prefer netdev proto, which adds dependencies based
* on skb->protocol.
*
@@ -510,11 +600,13 @@ int exthdr_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
enum proto_bases pb, struct stmt **res)
{
const struct proto_desc *desc;
+ struct proto_ctx *pctx;
- desc = ctx->pctx.protocol[pb].desc;
+ pctx = eval_proto_ctx(ctx);
+ desc = pctx->protocol[pb].desc;
if (desc == NULL) {
if (expr->exthdr.op == NFT_EXTHDR_OP_TCPOPT) {
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_NETDEV:
case NFPROTO_BRIDGE:
case NFPROTO_INET:
@@ -610,8 +702,7 @@ void payload_dependency_store(struct payload_dep_ctx *ctx,
if (ignore_dep)
return;
- ctx->pdep = stmt;
- ctx->pbase = base + 1;
+ ctx->pdeps[base + 1] = stmt;
}
/**
@@ -626,20 +717,53 @@ void payload_dependency_store(struct payload_dep_ctx *ctx,
bool payload_dependency_exists(const struct payload_dep_ctx *ctx,
enum proto_bases base)
{
- return ctx->pbase != PROTO_BASE_INVALID &&
- ctx->pdep != NULL &&
- (ctx->pbase == base || (base == PROTO_BASE_TRANSPORT_HDR && ctx->pbase == base + 1));
+ if (ctx->pdeps[base])
+ return true;
+
+ return base == PROTO_BASE_TRANSPORT_HDR &&
+ ctx->pdeps[PROTO_BASE_INNER_HDR];
}
-void payload_dependency_release(struct payload_dep_ctx *ctx)
+/**
+ * payload_dependency_get - return a payload dependency if available
+ * @ctx: payload dependency context
+ * @base: payload protocol base
+ *
+ * If we have seen a protocol key payload expression for this base, we return
+ * it.
+ */
+struct expr *payload_dependency_get(struct payload_dep_ctx *ctx,
+ enum proto_bases base)
+{
+ if (ctx->pdeps[base])
+ return ctx->pdeps[base]->expr;
+
+ if (base == PROTO_BASE_TRANSPORT_HDR &&
+ ctx->pdeps[PROTO_BASE_INNER_HDR])
+ return ctx->pdeps[PROTO_BASE_INNER_HDR]->expr;
+
+ return NULL;
+}
+
+static void __payload_dependency_release(struct payload_dep_ctx *ctx,
+ enum proto_bases base)
{
- list_del(&ctx->pdep->list);
- stmt_free(ctx->pdep);
+ list_del(&ctx->pdeps[base]->list);
+ stmt_free(ctx->pdeps[base]);
- ctx->pbase = PROTO_BASE_INVALID;
- if (ctx->pdep == ctx->prev)
+ if (ctx->pdeps[base] == ctx->prev)
ctx->prev = NULL;
- ctx->pdep = NULL;
+ ctx->pdeps[base] = NULL;
+}
+
+void payload_dependency_release(struct payload_dep_ctx *ctx,
+ enum proto_bases base)
+{
+ if (ctx->pdeps[base])
+ __payload_dependency_release(ctx, base);
+ else if (base == PROTO_BASE_TRANSPORT_HDR &&
+ ctx->pdeps[PROTO_BASE_INNER_HDR])
+ __payload_dependency_release(ctx, PROTO_BASE_INNER_HDR);
}
static uint8_t icmp_dep_to_type(enum icmp_hdr_field_type t)
@@ -654,33 +778,67 @@ static uint8_t icmp_dep_to_type(enum icmp_hdr_field_type t)
case PROTO_ICMP6_MTU: return ICMP6_PACKET_TOO_BIG;
case PROTO_ICMP6_MGMQ: return MLD_LISTENER_QUERY;
case PROTO_ICMP6_PPTR: return ICMP6_PARAM_PROB;
+ case PROTO_ICMP6_REDIRECT: return ND_REDIRECT;
+ case PROTO_ICMP6_ADDRESS: return ND_NEIGHBOR_SOLICIT;
}
BUG("Missing icmp type mapping");
}
+static bool icmp_dep_type_match(enum icmp_hdr_field_type t, uint8_t type)
+{
+ switch (t) {
+ case PROTO_ICMP_ECHO:
+ return type == ICMP_ECHO || type == ICMP_ECHOREPLY;
+ case PROTO_ICMP6_ECHO:
+ return type == ICMP6_ECHO_REQUEST || type == ICMP6_ECHO_REPLY;
+ case PROTO_ICMP6_ADDRESS:
+ return type == ND_NEIGHBOR_SOLICIT ||
+ type == ND_NEIGHBOR_ADVERT ||
+ type == ND_REDIRECT ||
+ type == MLD_LISTENER_QUERY ||
+ type == MLD_LISTENER_REPORT ||
+ type == MLD_LISTENER_REDUCTION;
+ case PROTO_ICMP_ADDRESS:
+ case PROTO_ICMP_MTU:
+ case PROTO_ICMP6_MTU:
+ case PROTO_ICMP6_MGMQ:
+ case PROTO_ICMP6_PPTR:
+ case PROTO_ICMP6_REDIRECT:
+ return icmp_dep_to_type(t) == type;
+ case PROTO_ICMP_ANY:
+ return true;
+ }
+ BUG("Missing icmp type mapping");
+}
+
static bool payload_may_dependency_kill_icmp(struct payload_dep_ctx *ctx, struct expr *expr)
{
- const struct expr *dep = ctx->pdep->expr;
- uint8_t icmp_type;
+ const struct expr *dep = payload_dependency_get(ctx, expr->payload.base);
+ enum icmp_hdr_field_type icmp_dep;
- icmp_type = expr->payload.tmpl->icmp_dep;
- if (icmp_type == PROTO_ICMP_ANY)
+ icmp_dep = expr->payload.tmpl->icmp_dep;
+ if (icmp_dep == PROTO_ICMP_ANY)
return false;
if (dep->left->payload.desc != expr->payload.desc)
return false;
- icmp_type = icmp_dep_to_type(expr->payload.tmpl->icmp_dep);
+ if (expr->payload.tmpl->icmp_dep == PROTO_ICMP_ECHO ||
+ expr->payload.tmpl->icmp_dep == PROTO_ICMP6_ECHO ||
+ expr->payload.tmpl->icmp_dep == PROTO_ICMP6_ADDRESS)
+ return false;
- return ctx->icmp_type == icmp_type;
+ return ctx->icmp_type == icmp_dep_to_type(icmp_dep);
}
static bool payload_may_dependency_kill_ll(struct payload_dep_ctx *ctx, struct expr *expr)
{
- const struct expr *dep = ctx->pdep->expr;
+ const struct expr *dep = payload_dependency_get(ctx, expr->payload.base);
- /* Never remove a 'vlan type 0x...' expression, they are never added implicitly */
+ /* Never remove a 'vlan type 0x...' expression, they are never added
+ * implicitly
+ */
if (dep->left->payload.desc == &proto_vlan)
return false;
@@ -697,7 +855,7 @@ static bool payload_may_dependency_kill_ll(struct payload_dep_ctx *ctx, struct e
static bool payload_may_dependency_kill(struct payload_dep_ctx *ctx,
unsigned int family, struct expr *expr)
{
- struct expr *dep = ctx->pdep->expr;
+ struct expr *dep = payload_dependency_get(ctx, expr->payload.base);
/* Protocol key payload expression at network base such as 'ip6 nexthdr'
* need to be left in place since it implicitly restricts matching to
@@ -733,13 +891,17 @@ static bool payload_may_dependency_kill(struct payload_dep_ctx *ctx,
break;
}
- if (expr->payload.base == PROTO_BASE_TRANSPORT_HDR &&
- dep->left->payload.base == PROTO_BASE_TRANSPORT_HDR) {
- if (dep->left->payload.desc == &proto_icmp)
- return payload_may_dependency_kill_icmp(ctx, expr);
- if (dep->left->payload.desc == &proto_icmp6)
- return payload_may_dependency_kill_icmp(ctx, expr);
- }
+ if (expr->payload.base != PROTO_BASE_TRANSPORT_HDR)
+ return true;
+
+ if (dep->left->payload.base != PROTO_BASE_TRANSPORT_HDR)
+ return true;
+
+ if (dep->left->payload.desc == &proto_icmp)
+ return payload_may_dependency_kill_icmp(ctx, expr);
+
+ if (dep->left->payload.desc == &proto_icmp6)
+ return payload_may_dependency_kill_icmp(ctx, expr);
return true;
}
@@ -757,9 +919,10 @@ static bool payload_may_dependency_kill(struct payload_dep_ctx *ctx,
void payload_dependency_kill(struct payload_dep_ctx *ctx, struct expr *expr,
unsigned int family)
{
- if (payload_dependency_exists(ctx, expr->payload.base) &&
+ if (expr->payload.desc != &proto_unknown &&
+ payload_dependency_exists(ctx, expr->payload.base) &&
payload_may_dependency_kill(ctx, family, expr))
- payload_dependency_release(ctx);
+ payload_dependency_release(ctx, expr->payload.base);
}
void exthdr_dependency_kill(struct payload_dep_ctx *ctx, struct expr *expr,
@@ -768,21 +931,53 @@ void exthdr_dependency_kill(struct payload_dep_ctx *ctx, struct expr *expr,
switch (expr->exthdr.op) {
case NFT_EXTHDR_OP_TCPOPT:
if (payload_dependency_exists(ctx, PROTO_BASE_TRANSPORT_HDR))
- payload_dependency_release(ctx);
+ payload_dependency_release(ctx, PROTO_BASE_TRANSPORT_HDR);
break;
case NFT_EXTHDR_OP_IPV6:
if (payload_dependency_exists(ctx, PROTO_BASE_NETWORK_HDR))
- payload_dependency_release(ctx);
+ payload_dependency_release(ctx, PROTO_BASE_NETWORK_HDR);
break;
case NFT_EXTHDR_OP_IPV4:
if (payload_dependency_exists(ctx, PROTO_BASE_NETWORK_HDR))
- payload_dependency_release(ctx);
+ payload_dependency_release(ctx, PROTO_BASE_NETWORK_HDR);
break;
default:
break;
}
}
+static const struct proto_desc *get_stacked_desc(const struct proto_ctx *ctx,
+ const struct proto_desc *top,
+ const struct expr *e,
+ unsigned int *skip)
+{
+ unsigned int i, total, payload_offset = e->payload.offset;
+
+ assert(e->etype == EXPR_PAYLOAD);
+
+ if (e->payload.base != PROTO_BASE_LL_HDR ||
+ payload_offset < top->length) {
+ *skip = 0;
+ return top;
+ }
+
+ for (i = 0, total = 0; i < ctx->stacked_ll_count; i++) {
+ const struct proto_desc *stacked;
+
+ stacked = ctx->stacked_ll[i];
+ if (payload_offset < stacked->length) {
+ *skip = total;
+ return stacked;
+ }
+
+ payload_offset -= stacked->length;
+ total += stacked->length;
+ }
+
+ *skip = total;
+ return top;
+}
+
/**
* payload_expr_complete - fill in type information of a raw payload expr
*
@@ -794,9 +989,10 @@ void exthdr_dependency_kill(struct payload_dep_ctx *ctx, struct expr *expr,
*/
void payload_expr_complete(struct expr *expr, const struct proto_ctx *ctx)
{
+ unsigned int payload_offset = expr->payload.offset;
const struct proto_desc *desc;
const struct proto_hdr_template *tmpl;
- unsigned int i;
+ unsigned int i, total;
assert(expr->etype == EXPR_PAYLOAD);
@@ -805,18 +1001,26 @@ void payload_expr_complete(struct expr *expr, const struct proto_ctx *ctx)
return;
assert(desc->base == expr->payload.base);
+ desc = get_stacked_desc(ctx, desc, expr, &total);
+ payload_offset -= total;
+
for (i = 0; i < array_size(desc->templates); i++) {
tmpl = &desc->templates[i];
- if (tmpl->offset != expr->payload.offset ||
+ if (tmpl->offset != payload_offset ||
tmpl->len != expr->len)
continue;
+ if (tmpl->meta_key && i == 0)
+ continue;
+
if (tmpl->icmp_dep && ctx->th_dep.icmp.type &&
- ctx->th_dep.icmp.type != icmp_dep_to_type(tmpl->icmp_dep))
+ !icmp_dep_type_match(tmpl->icmp_dep,
+ ctx->th_dep.icmp.type))
continue;
expr->dtype = tmpl->dtype;
expr->payload.desc = desc;
+ expr->byteorder = tmpl->byteorder;
expr->payload.tmpl = tmpl;
return;
}
@@ -861,6 +1065,7 @@ bool payload_expr_trim(struct expr *expr, struct expr *mask,
unsigned int payload_len = expr->len;
const struct proto_desc *desc;
unsigned int off, i, len = 0;
+ unsigned int total;
assert(expr->etype == EXPR_PAYLOAD);
@@ -870,10 +1075,8 @@ bool payload_expr_trim(struct expr *expr, struct expr *mask,
assert(desc->base == expr->payload.base);
- if (ctx->protocol[expr->payload.base].offset) {
- assert(payload_offset >= ctx->protocol[expr->payload.base].offset);
- payload_offset -= ctx->protocol[expr->payload.base].offset;
- }
+ desc = get_stacked_desc(ctx, desc, expr, &total);
+ payload_offset -= total;
off = round_up(mask->len, BITS_PER_BYTE) - mask_len;
payload_offset += off;
@@ -920,29 +1123,35 @@ bool payload_expr_trim(struct expr *expr, struct expr *mask,
void payload_expr_expand(struct list_head *list, struct expr *expr,
const struct proto_ctx *ctx)
{
+ unsigned int payload_offset = expr->payload.offset;
const struct proto_hdr_template *tmpl;
const struct proto_desc *desc;
+ unsigned int i, total;
struct expr *new;
- unsigned int i;
assert(expr->etype == EXPR_PAYLOAD);
desc = ctx->protocol[expr->payload.base].desc;
- if (desc == NULL)
+ if (desc == NULL || desc == &proto_unknown)
goto raw;
+
assert(desc->base == expr->payload.base);
+ desc = get_stacked_desc(ctx, desc, expr, &total);
+ payload_offset -= total;
+
for (i = 1; i < array_size(desc->templates); i++) {
tmpl = &desc->templates[i];
if (tmpl->len == 0)
break;
- if (tmpl->offset != expr->payload.offset)
+ if (tmpl->offset != payload_offset)
continue;
if (tmpl->icmp_dep && ctx->th_dep.icmp.type &&
- ctx->th_dep.icmp.type != icmp_dep_to_type(tmpl->icmp_dep))
+ !icmp_dep_type_match(tmpl->icmp_dep,
+ ctx->th_dep.icmp.type))
continue;
if (tmpl->len <= expr->len) {
@@ -950,6 +1159,7 @@ void payload_expr_expand(struct list_head *list, struct expr *expr,
list_add_tail(&new->list, list);
expr->len -= tmpl->len;
expr->payload.offset += tmpl->len;
+ payload_offset += tmpl->len;
if (expr->len == 0)
return;
} else if (expr->len > 0) {
@@ -962,8 +1172,12 @@ void payload_expr_expand(struct list_head *list, struct expr *expr,
}
raw:
new = payload_expr_alloc(&expr->location, NULL, 0);
- payload_init_raw(new, expr->payload.base, expr->payload.offset,
+ payload_init_raw(new, expr->payload.base, payload_offset,
expr->len);
+
+ if (expr->payload.inner_desc)
+ new->dtype = &integer_type;
+
list_add_tail(&new->list, list);
}
@@ -985,6 +1199,9 @@ bool payload_can_merge(const struct expr *e1, const struct expr *e2)
{
unsigned int total;
+ if (e1->payload.inner_desc != e2->payload.inner_desc)
+ return false;
+
if (!payload_is_adjacent(e1, e2))
return false;
@@ -1041,6 +1258,8 @@ struct expr *payload_expr_join(const struct expr *e1, const struct expr *e2)
expr->payload.base = e1->payload.base;
expr->payload.offset = e1->payload.offset;
expr->len = e1->len + e2->len;
+ expr->payload.inner_desc = e1->payload.inner_desc;
+
return expr;
}
@@ -1089,9 +1308,42 @@ __payload_gen_icmp_echo_dependency(struct eval_ctx *ctx, const struct expr *expr
return expr_stmt_alloc(&dep->location, dep);
}
+static struct stmt *
+__payload_gen_icmp6_addr_dependency(struct eval_ctx *ctx, const struct expr *expr,
+ const struct proto_desc *desc)
+{
+ static const uint8_t icmp_addr_types[] = {
+ MLD_LISTENER_QUERY,
+ MLD_LISTENER_REPORT,
+ MLD_LISTENER_REDUCTION,
+ ND_NEIGHBOR_SOLICIT,
+ ND_NEIGHBOR_ADVERT,
+ ND_REDIRECT
+ };
+ struct expr *left, *right, *dep, *set;
+ size_t i;
+
+ left = payload_expr_alloc(&expr->location, desc, desc->protocol_key);
+
+ set = set_expr_alloc(&expr->location, NULL);
+
+ for (i = 0; i < array_size(icmp_addr_types); ++i) {
+ right = constant_expr_alloc(&expr->location, &icmp6_type_type,
+ BYTEORDER_BIG_ENDIAN, BITS_PER_BYTE,
+ constant_data_ptr(icmp_addr_types[i],
+ BITS_PER_BYTE));
+ right = set_elem_expr_alloc(&expr->location, right);
+ compound_expr_add(set, right);
+ }
+
+ dep = relational_expr_alloc(&expr->location, OP_IMPLICIT, left, set);
+ return expr_stmt_alloc(&dep->location, dep);
+}
+
int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr,
struct stmt **res)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_hdr_template *tmpl;
const struct proto_desc *desc;
struct stmt *stmt = NULL;
@@ -1108,11 +1360,11 @@ int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr,
break;
case PROTO_ICMP_ECHO:
/* do not test ICMP_ECHOREPLY here: its 0 */
- if (ctx->pctx.th_dep.icmp.type == ICMP_ECHO)
+ if (pctx->th_dep.icmp.type == ICMP_ECHO)
goto done;
type = ICMP_ECHO;
- if (ctx->pctx.th_dep.icmp.type)
+ if (pctx->th_dep.icmp.type)
goto bad_proto;
stmt = __payload_gen_icmp_echo_dependency(ctx, expr,
@@ -1123,21 +1375,21 @@ int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr,
case PROTO_ICMP_MTU:
case PROTO_ICMP_ADDRESS:
type = icmp_dep_to_type(tmpl->icmp_dep);
- if (ctx->pctx.th_dep.icmp.type == type)
+ if (pctx->th_dep.icmp.type == type)
goto done;
- if (ctx->pctx.th_dep.icmp.type)
+ if (pctx->th_dep.icmp.type)
goto bad_proto;
stmt = __payload_gen_icmp_simple_dependency(ctx, expr,
&icmp_type_type,
desc, type);
break;
case PROTO_ICMP6_ECHO:
- if (ctx->pctx.th_dep.icmp.type == ICMP6_ECHO_REQUEST ||
- ctx->pctx.th_dep.icmp.type == ICMP6_ECHO_REPLY)
+ if (pctx->th_dep.icmp.type == ICMP6_ECHO_REQUEST ||
+ pctx->th_dep.icmp.type == ICMP6_ECHO_REPLY)
goto done;
type = ICMP6_ECHO_REQUEST;
- if (ctx->pctx.th_dep.icmp.type)
+ if (pctx->th_dep.icmp.type)
goto bad_proto;
stmt = __payload_gen_icmp_echo_dependency(ctx, expr,
@@ -1146,13 +1398,23 @@ int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr,
&icmp6_type_type,
desc);
break;
+ case PROTO_ICMP6_ADDRESS:
+ if (icmp_dep_type_match(PROTO_ICMP6_ADDRESS,
+ pctx->th_dep.icmp.type))
+ goto done;
+ type = ND_NEIGHBOR_SOLICIT;
+ if (pctx->th_dep.icmp.type)
+ goto bad_proto;
+ stmt = __payload_gen_icmp6_addr_dependency(ctx, expr, desc);
+ break;
+ case PROTO_ICMP6_REDIRECT:
case PROTO_ICMP6_MTU:
case PROTO_ICMP6_MGMQ:
case PROTO_ICMP6_PPTR:
type = icmp_dep_to_type(tmpl->icmp_dep);
- if (ctx->pctx.th_dep.icmp.type == type)
+ if (pctx->th_dep.icmp.type == type)
goto done;
- if (ctx->pctx.th_dep.icmp.type)
+ if (pctx->th_dep.icmp.type)
goto bad_proto;
stmt = __payload_gen_icmp_simple_dependency(ctx, expr,
&icmp6_type_type,
@@ -1163,16 +1425,54 @@ int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr,
BUG("Unhandled icmp dependency code");
}
- ctx->pctx.th_dep.icmp.type = type;
+ 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;
bad_proto:
return expr_error(ctx->msgs, expr, "incompatible icmp match: rule has %d, need %u",
- ctx->pctx.th_dep.icmp.type, type);
+ pctx->th_dep.icmp.type, type);
+}
+
+int payload_gen_inner_dependency(struct eval_ctx *ctx, const struct expr *expr,
+ struct stmt **res)
+{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
+ const struct proto_hdr_template *tmpl;
+ const struct proto_desc *desc, *inner_desc;
+ struct expr *left, *right, *dep;
+ struct stmt *stmt = NULL;
+ int protocol;
+
+ assert(expr->etype == EXPR_PAYLOAD);
+
+ inner_desc = expr->payload.inner_desc;
+ desc = pctx->protocol[inner_desc->base - 1].desc;
+ if (desc == NULL)
+ desc = &proto_ip;
+
+ tmpl = &inner_desc->templates[0];
+ assert(tmpl);
+
+ protocol = proto_find_num(desc, inner_desc);
+ if (protocol < 0)
+ return expr_error(ctx->msgs, expr,
+ "conflicting protocols specified: %s vs. %s",
+ desc->name, inner_desc->name);
+
+ left = meta_expr_alloc(&expr->location, tmpl->meta_key);
+
+ right = constant_expr_alloc(&expr->location, tmpl->dtype,
+ tmpl->dtype->byteorder, tmpl->len,
+ constant_data_ptr(protocol, tmpl->len));
+
+ dep = relational_expr_alloc(&expr->location, OP_EQ, left, right);
+ stmt = expr_stmt_alloc(&dep->location, dep);
+
+ *res = stmt;
+ return 0;
}
diff --git a/src/print.c b/src/print.c
index d1b25e8b..8aefa961 100644
--- a/src/print.c
+++ b/src/print.c
@@ -2,11 +2,12 @@
* Copyright (c) 2017 Phil Sutter <phil@nwl.cc>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <stdarg.h>
#include <nftables.h>
#include <utils.h>
diff --git a/src/proto.c b/src/proto.c
index 2b61e0ba..553b6a44 100644
--- a/src/proto.c
+++ b/src/proto.c
@@ -9,10 +9,9 @@
*
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
#include <net/if_arp.h>
#include <arpa/inet.h>
#include <linux/netfilter.h>
@@ -28,6 +27,7 @@ const char *proto_base_names[] = {
[PROTO_BASE_LL_HDR] = "link layer",
[PROTO_BASE_NETWORK_HDR] = "network layer",
[PROTO_BASE_TRANSPORT_HDR] = "transport layer",
+ [PROTO_BASE_INNER_HDR] = "payload data",
};
const char *proto_base_tokens[] = {
@@ -35,6 +35,7 @@ const char *proto_base_tokens[] = {
[PROTO_BASE_LL_HDR] = "ll",
[PROTO_BASE_NETWORK_HDR] = "nh",
[PROTO_BASE_TRANSPORT_HDR] = "th",
+ [PROTO_BASE_INNER_HDR] = "ih",
};
const struct proto_hdr_template proto_unknown_template =
@@ -58,6 +59,8 @@ proto_find_upper(const struct proto_desc *base, unsigned int num)
unsigned int i;
for (i = 0; i < array_size(base->protocols); i++) {
+ if (!base->protocols[i].desc)
+ break;
if (base->protocols[i].num == num)
return base->protocols[i].desc;
}
@@ -76,12 +79,38 @@ int proto_find_num(const struct proto_desc *base,
unsigned int i;
for (i = 0; i < array_size(base->protocols); i++) {
+ if (!base->protocols[i].desc)
+ break;
if (base->protocols[i].desc == desc)
return base->protocols[i].num;
}
return -1;
}
+static const struct proto_desc *inner_protocols[] = {
+ &proto_vxlan,
+ &proto_geneve,
+ &proto_gre,
+ &proto_gretap,
+};
+
+const struct proto_desc *proto_find_inner(uint32_t type, uint32_t hdrsize,
+ uint32_t flags)
+{
+ const struct proto_desc *desc;
+ unsigned int i;
+
+ for (i = 0; i < array_size(inner_protocols); i++) {
+ desc = inner_protocols[i];
+ if (desc->inner.type == type &&
+ desc->inner.hdrsize == hdrsize &&
+ desc->inner.flags == flags)
+ return inner_protocols[i];
+ }
+
+ return &proto_unknown;
+}
+
static const struct dev_proto_desc dev_proto_desc[] = {
DEV_PROTO_DESC(ARPHRD_ETHER, &proto_eth),
};
@@ -104,6 +133,8 @@ int proto_dev_type(const struct proto_desc *desc, uint16_t *res)
return 0;
}
for (j = 0; j < array_size(base->protocols); j++) {
+ if (!base->protocols[j].desc)
+ break;
if (base->protocols[j].desc == desc) {
*res = dev_proto_desc[i].type;
return 0;
@@ -146,14 +177,20 @@ static void proto_ctx_debug(const struct proto_ctx *ctx, enum proto_bases base,
if (!(debug_mask & NFT_DEBUG_PROTO_CTX))
return;
- pr_debug("update %s protocol context:\n", proto_base_names[base]);
+ if (base == PROTO_BASE_LL_HDR && ctx->stacked_ll_count) {
+ pr_debug(" saved ll headers:");
+ for (i = 0; i < ctx->stacked_ll_count; i++)
+ pr_debug(" %s", ctx->stacked_ll[i]->name);
+ }
+
+ pr_debug("update %s protocol context%s:\n",
+ proto_base_names[base], ctx->inner ? " (inner)" : "");
+
for (i = PROTO_BASE_LL_HDR; i <= PROTO_BASE_MAX; i++) {
pr_debug(" %-20s: %s",
proto_base_names[i],
ctx->protocol[i].desc ? ctx->protocol[i].desc->name :
"none");
- if (ctx->protocol[i].offset)
- pr_debug(" (offset: %u)", ctx->protocol[i].offset);
if (i == base)
pr_debug(" <-");
pr_debug("\n");
@@ -169,7 +206,7 @@ static void proto_ctx_debug(const struct proto_ctx *ctx, enum proto_bases base,
* @debug_mask: display debugging information
*/
void proto_ctx_init(struct proto_ctx *ctx, unsigned int family,
- unsigned int debug_mask)
+ unsigned int debug_mask, bool inner)
{
const struct hook_proto_desc *h = &hook_proto_desc[family];
@@ -177,6 +214,7 @@ void proto_ctx_init(struct proto_ctx *ctx, unsigned int family,
ctx->family = family;
ctx->protocol[h->base].desc = h->desc;
ctx->debug_mask = debug_mask;
+ ctx->inner = inner;
proto_ctx_debug(ctx, h->base, debug_mask);
}
@@ -216,6 +254,8 @@ void proto_ctx_update(struct proto_ctx *ctx, enum proto_bases base,
ctx->protocol[base].protos[i].location = *loc;
}
break;
+ case PROTO_BASE_INNER_HDR:
+ break;
default:
BUG("unknown protocol base %d", base);
}
@@ -264,6 +304,8 @@ const struct proto_desc *proto_ctx_find_conflict(struct proto_ctx *ctx,
#define HDR_FIELD(__name, __struct, __member) \
HDR_TEMPLATE(__name, &integer_type, __struct, __member)
+#define HDR_HEX_FIELD(__name, __struct, __member) \
+ HDR_TEMPLATE(__name, &xinteger_type, __struct, __member)
#define HDR_BITFIELD(__name, __dtype, __offset, __len) \
PROTO_HDR_TEMPLATE(__name, __dtype, BYTEORDER_BIG_ENDIAN, \
__offset, __len)
@@ -396,10 +438,10 @@ const struct datatype icmp_type_type = {
.sym_tbl = &icmp_type_tbl,
};
-#define ICMP46HDR_FIELD(__token, __struct, __member, __dep) \
+#define ICMP46HDR_FIELD(__token, __dtype, __struct, __member, __dep) \
{ \
.token = (__token), \
- .dtype = &integer_type, \
+ .dtype = &__dtype, \
.byteorder = BYTEORDER_BIG_ENDIAN, \
.offset = offsetof(__struct, __member) * 8, \
.len = field_sizeof(__struct, __member) * 8, \
@@ -407,7 +449,7 @@ const struct datatype icmp_type_type = {
}
#define ICMPHDR_FIELD(__token, __member, __dep) \
- ICMP46HDR_FIELD(__token, struct icmphdr, __member, __dep)
+ ICMP46HDR_FIELD(__token, integer_type, struct icmphdr, __member, __dep)
#define ICMPHDR_TYPE(__name, __type, __member) \
HDR_TYPE(__name, __type, struct icmphdr, __member)
@@ -501,6 +543,10 @@ const struct proto_desc proto_udp = {
[UDPHDR_LENGTH] = UDPHDR_FIELD("length", len),
[UDPHDR_CHECKSUM] = UDPHDR_FIELD("checksum", check),
},
+ .protocols = {
+ PROTO_LINK(0, &proto_vxlan),
+ PROTO_LINK(0, &proto_geneve),
+ },
};
const struct proto_desc proto_udplite = {
@@ -676,7 +722,9 @@ static const struct symbol_table dscp_type_tbl = {
SYMBOL("cs5", 0x28),
SYMBOL("cs6", 0x30),
SYMBOL("cs7", 0x38),
+ SYMBOL("df", 0x00),
SYMBOL("be", 0x00),
+ SYMBOL("lephb", 0x01),
SYMBOL("af11", 0x0a),
SYMBOL("af12", 0x0c),
SYMBOL("af13", 0x0e),
@@ -689,6 +737,7 @@ static const struct symbol_table dscp_type_tbl = {
SYMBOL("af41", 0x22),
SYMBOL("af42", 0x24),
SYMBOL("af43", 0x26),
+ SYMBOL("va", 0x2c),
SYMBOL("ef", 0x2e),
SYMBOL_LIST_END
},
@@ -727,6 +776,42 @@ const struct datatype ecn_type = {
.sym_tbl = &ecn_type_tbl,
};
+#define GREHDR_TEMPLATE(__name, __dtype, __member) \
+ HDR_TEMPLATE(__name, __dtype, struct grehdr, __member)
+#define GREHDR_TYPE(__name, __member) \
+ GREHDR_TEMPLATE(__name, &ethertype_type, __member)
+
+const struct proto_desc proto_gre = {
+ .name = "gre",
+ .id = PROTO_DESC_GRE,
+ .base = PROTO_BASE_TRANSPORT_HDR,
+ .templates = {
+ [0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8),
+ [GREHDR_FLAGS] = HDR_BITFIELD("flags", &integer_type, 0, 5),
+ [GREHDR_VERSION] = HDR_BITFIELD("version", &integer_type, 13, 3),
+ [GREHDR_PROTOCOL] = HDR_BITFIELD("protocol", &ethertype_type, 16, 16),
+ },
+ .inner = {
+ .hdrsize = sizeof(struct grehdr),
+ .flags = NFT_INNER_NH | NFT_INNER_TH,
+ .type = NFT_INNER_GENEVE + 1,
+ },
+};
+
+const struct proto_desc proto_gretap = {
+ .name = "gretap",
+ .id = PROTO_DESC_GRETAP,
+ .base = PROTO_BASE_TRANSPORT_HDR,
+ .templates = {
+ [0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8),
+ },
+ .inner = {
+ .hdrsize = sizeof(struct grehdr),
+ .flags = NFT_INNER_LL | NFT_INNER_NH | NFT_INNER_TH,
+ .type = NFT_INNER_GENEVE + 2,
+ },
+};
+
#define IPHDR_FIELD(__name, __member) \
HDR_FIELD(__name, struct iphdr, __member)
#define IPHDR_ADDR(__name, __member) \
@@ -750,6 +835,8 @@ const struct proto_desc proto_ip = {
PROTO_LINK(IPPROTO_TCP, &proto_tcp),
PROTO_LINK(IPPROTO_DCCP, &proto_dccp),
PROTO_LINK(IPPROTO_SCTP, &proto_sctp),
+ PROTO_LINK(IPPROTO_GRE, &proto_gre),
+ PROTO_LINK(IPPROTO_GRE, &proto_gretap),
},
.templates = {
[0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8),
@@ -759,7 +846,7 @@ const struct proto_desc proto_ip = {
[IPHDR_ECN] = HDR_BITFIELD("ecn", &ecn_type, 14, 2),
[IPHDR_LENGTH] = IPHDR_FIELD("length", tot_len),
[IPHDR_ID] = IPHDR_FIELD("id", id),
- [IPHDR_FRAG_OFF] = IPHDR_FIELD("frag-off", frag_off),
+ [IPHDR_FRAG_OFF] = HDR_HEX_FIELD("frag-off", struct iphdr, frag_off),
[IPHDR_TTL] = IPHDR_FIELD("ttl", ttl),
[IPHDR_PROTOCOL] = INET_PROTOCOL("protocol", struct iphdr, protocol),
[IPHDR_CHECKSUM] = IPHDR_FIELD("checksum", check),
@@ -826,7 +913,7 @@ const struct datatype icmp6_type_type = {
};
#define ICMP6HDR_FIELD(__token, __member, __dep) \
- ICMP46HDR_FIELD(__token, struct icmp6_hdr, __member, __dep)
+ ICMP46HDR_FIELD(__token, integer_type, struct icmp6_hdr, __member, __dep)
#define ICMP6HDR_TYPE(__name, __type, __member) \
HDR_TYPE(__name, __type, struct icmp6_hdr, __member)
@@ -846,6 +933,12 @@ const struct proto_desc proto_icmp6 = {
[ICMP6HDR_ID] = ICMP6HDR_FIELD("id", icmp6_id, PROTO_ICMP6_ECHO),
[ICMP6HDR_SEQ] = ICMP6HDR_FIELD("sequence", icmp6_seq, PROTO_ICMP6_ECHO),
[ICMP6HDR_MAXDELAY] = ICMP6HDR_FIELD("max-delay", icmp6_maxdelay, PROTO_ICMP6_MGMQ),
+ [ICMP6HDR_TADDR] = ICMP46HDR_FIELD("taddr", ip6addr_type,
+ struct nd_neighbor_solicit, nd_ns_target,
+ PROTO_ICMP6_ADDRESS),
+ [ICMP6HDR_DADDR] = ICMP46HDR_FIELD("daddr", ip6addr_type,
+ struct nd_redirect, nd_rd_dst,
+ PROTO_ICMP6_REDIRECT),
},
};
@@ -876,6 +969,8 @@ const struct proto_desc proto_ip6 = {
PROTO_LINK(IPPROTO_ICMP, &proto_icmp),
PROTO_LINK(IPPROTO_IGMP, &proto_igmp),
PROTO_LINK(IPPROTO_ICMPV6, &proto_icmp6),
+ PROTO_LINK(IPPROTO_GRE, &proto_gre),
+ PROTO_LINK(IPPROTO_GRE, &proto_gretap),
},
.templates = {
[0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8),
@@ -941,6 +1036,8 @@ const struct proto_desc proto_inet_service = {
PROTO_LINK(IPPROTO_ICMP, &proto_icmp),
PROTO_LINK(IPPROTO_IGMP, &proto_igmp),
PROTO_LINK(IPPROTO_ICMPV6, &proto_icmp6),
+ PROTO_LINK(IPPROTO_GRE, &proto_gre),
+ PROTO_LINK(IPPROTO_GRE, &proto_gretap),
},
.templates = {
[0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8),
@@ -1121,6 +1218,57 @@ const struct proto_desc proto_eth = {
};
/*
+ * VXLAN
+ */
+
+const struct proto_desc proto_vxlan = {
+ .name = "vxlan",
+ .id = PROTO_DESC_VXLAN,
+ .base = PROTO_BASE_INNER_HDR,
+ .templates = {
+ [VXLANHDR_FLAGS] = HDR_BITFIELD("flags", &bitmask_type, 0, 8),
+ [VXLANHDR_VNI] = HDR_BITFIELD("vni", &integer_type, (4 * BITS_PER_BYTE), 24),
+ },
+ .protocols = {
+ PROTO_LINK(__constant_htons(ETH_P_IP), &proto_ip),
+ PROTO_LINK(__constant_htons(ETH_P_ARP), &proto_arp),
+ PROTO_LINK(__constant_htons(ETH_P_IPV6), &proto_ip6),
+ PROTO_LINK(__constant_htons(ETH_P_8021Q), &proto_vlan),
+ },
+ .inner = {
+ .hdrsize = sizeof(struct vxlanhdr),
+ .flags = NFT_INNER_HDRSIZE | NFT_INNER_LL | NFT_INNER_NH | NFT_INNER_TH,
+ .type = NFT_INNER_VXLAN,
+ },
+};
+
+/*
+ * GENEVE
+ */
+
+const struct proto_desc proto_geneve = {
+ .name = "geneve",
+ .id = PROTO_DESC_GENEVE,
+ .base = PROTO_BASE_INNER_HDR,
+ .templates = {
+ [GNVHDR_TYPE] = HDR_TYPE("type", &ethertype_type, struct gnvhdr, type),
+ [GNVHDR_VNI] = HDR_BITFIELD("vni", &integer_type, (4 * BITS_PER_BYTE), 24),
+ },
+ .protocols = {
+ PROTO_LINK(__constant_htons(ETH_P_IP), &proto_ip),
+ PROTO_LINK(__constant_htons(ETH_P_ARP), &proto_arp),
+ PROTO_LINK(__constant_htons(ETH_P_IPV6), &proto_ip6),
+ PROTO_LINK(__constant_htons(ETH_P_8021Q), &proto_vlan),
+ },
+ .inner = {
+ .hdrsize = sizeof(struct gnvhdr),
+ .flags = NFT_INNER_HDRSIZE | NFT_INNER_LL | NFT_INNER_NH | NFT_INNER_TH,
+ .type = NFT_INNER_GENEVE,
+ },
+};
+
+
+/*
* Dummy protocol for netdev tables.
*/
const struct proto_desc proto_netdev = {
@@ -1138,7 +1286,7 @@ const struct proto_desc proto_netdev = {
},
};
-static const struct proto_desc *proto_definitions[PROTO_DESC_MAX + 1] = {
+static const struct proto_desc *const proto_definitions[PROTO_DESC_MAX + 1] = {
[PROTO_DESC_AH] = &proto_ah,
[PROTO_DESC_ESP] = &proto_esp,
[PROTO_DESC_COMP] = &proto_comp,
@@ -1156,6 +1304,10 @@ static const struct proto_desc *proto_definitions[PROTO_DESC_MAX + 1] = {
[PROTO_DESC_ARP] = &proto_arp,
[PROTO_DESC_VLAN] = &proto_vlan,
[PROTO_DESC_ETHER] = &proto_eth,
+ [PROTO_DESC_VXLAN] = &proto_vxlan,
+ [PROTO_DESC_GENEVE] = &proto_geneve,
+ [PROTO_DESC_GRE] = &proto_gre,
+ [PROTO_DESC_GRETAP] = &proto_gretap,
};
const struct proto_desc *proto_find_desc(enum proto_desc_id desc_id)
diff --git a/src/rbtree.c b/src/rbtree.c
deleted file mode 100644
index 325c0123..00000000
--- a/src/rbtree.c
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Red Black Trees
- * (C) 1999 Andrea Arcangeli <andrea@suse.de>
- * (C) 2002 David Woodhouse <dwmw2@infradead.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <rbtree.h>
-
-static void __rb_rotate_left(struct rb_node *node, struct rb_root *root)
-{
- struct rb_node *right = node->rb_right;
- struct rb_node *parent = rb_parent(node);
-
- if ((node->rb_right = right->rb_left))
- rb_set_parent(right->rb_left, node);
- right->rb_left = node;
-
- rb_set_parent(right, parent);
-
- if (parent)
- {
- if (node == parent->rb_left)
- parent->rb_left = right;
- else
- parent->rb_right = right;
- }
- else
- root->rb_node = right;
- rb_set_parent(node, right);
-}
-
-static void __rb_rotate_right(struct rb_node *node, struct rb_root *root)
-{
- struct rb_node *left = node->rb_left;
- struct rb_node *parent = rb_parent(node);
-
- if ((node->rb_left = left->rb_right))
- rb_set_parent(left->rb_right, node);
- left->rb_right = node;
-
- rb_set_parent(left, parent);
-
- if (parent)
- {
- if (node == parent->rb_right)
- parent->rb_right = left;
- else
- parent->rb_left = left;
- }
- else
- root->rb_node = left;
- rb_set_parent(node, left);
-}
-
-void rb_insert_color(struct rb_node *node, struct rb_root *root)
-{
- struct rb_node *parent, *gparent;
-
- while ((parent = rb_parent(node)) && rb_is_red(parent))
- {
- gparent = rb_parent(parent);
-
- if (parent == gparent->rb_left)
- {
- {
- register struct rb_node *uncle = gparent->rb_right;
- if (uncle && rb_is_red(uncle))
- {
- rb_set_black(uncle);
- rb_set_black(parent);
- rb_set_red(gparent);
- node = gparent;
- continue;
- }
- }
-
- if (parent->rb_right == node)
- {
- register struct rb_node *tmp;
- __rb_rotate_left(parent, root);
- tmp = parent;
- parent = node;
- node = tmp;
- }
-
- rb_set_black(parent);
- rb_set_red(gparent);
- __rb_rotate_right(gparent, root);
- } else {
- {
- register struct rb_node *uncle = gparent->rb_left;
- if (uncle && rb_is_red(uncle))
- {
- rb_set_black(uncle);
- rb_set_black(parent);
- rb_set_red(gparent);
- node = gparent;
- continue;
- }
- }
-
- if (parent->rb_left == node)
- {
- register struct rb_node *tmp;
- __rb_rotate_right(parent, root);
- tmp = parent;
- parent = node;
- node = tmp;
- }
-
- rb_set_black(parent);
- rb_set_red(gparent);
- __rb_rotate_left(gparent, root);
- }
- }
-
- rb_set_black(root->rb_node);
-}
-
-static void __rb_erase_color(struct rb_node *node, struct rb_node *parent,
- struct rb_root *root)
-{
- struct rb_node *other;
-
- while ((!node || rb_is_black(node)) && node != root->rb_node)
- {
- if (parent->rb_left == node)
- {
- other = parent->rb_right;
- if (rb_is_red(other))
- {
- rb_set_black(other);
- rb_set_red(parent);
- __rb_rotate_left(parent, root);
- other = parent->rb_right;
- }
- if ((!other->rb_left || rb_is_black(other->rb_left)) &&
- (!other->rb_right || rb_is_black(other->rb_right)))
- {
- rb_set_red(other);
- node = parent;
- parent = rb_parent(node);
- }
- else
- {
- if (!other->rb_right || rb_is_black(other->rb_right))
- {
- struct rb_node *o_left;
- if ((o_left = other->rb_left))
- rb_set_black(o_left);
- rb_set_red(other);
- __rb_rotate_right(other, root);
- other = parent->rb_right;
- }
- rb_set_color(other, rb_color(parent));
- rb_set_black(parent);
- if (other->rb_right)
- rb_set_black(other->rb_right);
- __rb_rotate_left(parent, root);
- node = root->rb_node;
- break;
- }
- }
- else
- {
- other = parent->rb_left;
- if (rb_is_red(other))
- {
- rb_set_black(other);
- rb_set_red(parent);
- __rb_rotate_right(parent, root);
- other = parent->rb_left;
- }
- if ((!other->rb_left || rb_is_black(other->rb_left)) &&
- (!other->rb_right || rb_is_black(other->rb_right)))
- {
- rb_set_red(other);
- node = parent;
- parent = rb_parent(node);
- }
- else
- {
- if (!other->rb_left || rb_is_black(other->rb_left))
- {
- register struct rb_node *o_right;
- if ((o_right = other->rb_right))
- rb_set_black(o_right);
- rb_set_red(other);
- __rb_rotate_left(other, root);
- other = parent->rb_left;
- }
- rb_set_color(other, rb_color(parent));
- rb_set_black(parent);
- if (other->rb_left)
- rb_set_black(other->rb_left);
- __rb_rotate_right(parent, root);
- node = root->rb_node;
- break;
- }
- }
- }
- if (node)
- rb_set_black(node);
-}
-
-void rb_erase(struct rb_node *node, struct rb_root *root)
-{
- struct rb_node *child, *parent;
- int color;
-
- if (!node->rb_left)
- child = node->rb_right;
- else if (!node->rb_right)
- child = node->rb_left;
- else
- {
- struct rb_node *old = node, *left;
-
- node = node->rb_right;
- while ((left = node->rb_left) != NULL)
- node = left;
- child = node->rb_right;
- parent = rb_parent(node);
- color = rb_color(node);
-
- if (child)
- rb_set_parent(child, parent);
- if (parent == old) {
- parent->rb_right = child;
- parent = node;
- } else
- parent->rb_left = child;
-
- node->rb_parent_color = old->rb_parent_color;
- node->rb_right = old->rb_right;
- node->rb_left = old->rb_left;
-
- if (rb_parent(old))
- {
- if (rb_parent(old)->rb_left == old)
- rb_parent(old)->rb_left = node;
- else
- rb_parent(old)->rb_right = node;
- } else
- root->rb_node = node;
-
- rb_set_parent(old->rb_left, node);
- if (old->rb_right)
- rb_set_parent(old->rb_right, node);
- goto color;
- }
-
- parent = rb_parent(node);
- color = rb_color(node);
-
- if (child)
- rb_set_parent(child, parent);
- if (parent)
- {
- if (parent->rb_left == node)
- parent->rb_left = child;
- else
- parent->rb_right = child;
- }
- else
- root->rb_node = child;
-
- color:
- if (color == RB_BLACK)
- __rb_erase_color(child, parent, root);
-}
-
-/*
- * This function returns the first node (in sort order) of the tree.
- */
-struct rb_node *rb_first(struct rb_root *root)
-{
- struct rb_node *n;
-
- n = root->rb_node;
- if (!n)
- return NULL;
- while (n->rb_left)
- n = n->rb_left;
- return n;
-}
-
-struct rb_node *rb_last(struct rb_root *root)
-{
- struct rb_node *n;
-
- n = root->rb_node;
- if (!n)
- return NULL;
- while (n->rb_right)
- n = n->rb_right;
- return n;
-}
-
-struct rb_node *rb_next(struct rb_node *node)
-{
- struct rb_node *parent;
-
- if (rb_parent(node) == node)
- return NULL;
-
- /* If we have a right-hand child, go down and then left as far
- as we can. */
- if (node->rb_right) {
- node = node->rb_right;
- while (node->rb_left)
- node=node->rb_left;
- return node;
- }
-
- /* No right-hand children. Everything down and left is
- smaller than us, so any 'next' node must be in the general
- direction of our parent. Go up the tree; any time the
- ancestor is a right-hand child of its parent, keep going
- up. First time it's a left-hand child of its parent, said
- parent is our 'next' node. */
- while ((parent = rb_parent(node)) && node == parent->rb_right)
- node = parent;
-
- return parent;
-}
-
-struct rb_node *rb_prev(struct rb_node *node)
-{
- struct rb_node *parent;
-
- if (rb_parent(node) == node)
- return NULL;
-
- /* If we have a left-hand child, go down and then right as far
- as we can. */
- if (node->rb_left) {
- node = node->rb_left;
- while (node->rb_right)
- node=node->rb_right;
- return node;
- }
-
- /* No left-hand children. Go up till we find an ancestor which
- is a right-hand child of its parent */
- while ((parent = rb_parent(node)) && node == parent->rb_left)
- node = parent;
-
- return parent;
-}
-
-void rb_replace_node(struct rb_node *victim, struct rb_node *new,
- struct rb_root *root)
-{
- struct rb_node *parent = rb_parent(victim);
-
- /* Set the surrounding nodes to point to the replacement */
- if (parent) {
- if (victim == parent->rb_left)
- parent->rb_left = new;
- else
- parent->rb_right = new;
- } else {
- root->rb_node = new;
- }
- if (victim->rb_left)
- rb_set_parent(victim->rb_left, new);
- if (victim->rb_right)
- rb_set_parent(victim->rb_right, new);
-
- /* Copy the pointers/colour from the victim to the replacement */
- *new = *victim;
-}
diff --git a/src/rt.c b/src/rt.c
index d7aa5edd..9320b832 100644
--- a/src/rt.c
+++ b/src/rt.c
@@ -8,12 +8,11 @@
* published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <errno.h>
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
#include <arpa/inet.h>
#include <linux/netfilter.h>
@@ -26,7 +25,7 @@
void realm_table_rt_init(struct nft_ctx *ctx)
{
- ctx->output.tbl.realm = rt_symbol_table_init("/etc/iproute2/rt_realms");
+ ctx->output.tbl.realm = rt_symbol_table_init("rt_realms");
}
void realm_table_rt_exit(struct nft_ctx *ctx)
@@ -46,16 +45,22 @@ static struct error_record *realm_type_parse(struct parse_ctx *ctx,
return symbolic_constant_parse(ctx, sym, ctx->tbl->realm, res);
}
+static void realm_type_describe(struct output_ctx *octx)
+{
+ rt_symbol_table_describe(octx, "rt_realms",
+ octx->tbl.realm, &realm_type);
+}
+
const struct datatype realm_type = {
.type = TYPE_REALM,
.name = "realm",
.desc = "routing realm",
+ .describe = realm_type_describe,
.byteorder = BYTEORDER_HOST_ENDIAN,
.size = 4 * BITS_PER_BYTE,
.basetype = &integer_type,
.print = realm_type_print,
.parse = realm_type_parse,
- .flags = DTYPE_F_PREFIX,
};
const struct rt_template rt_templates[] = {
diff --git a/src/rule.c b/src/rule.c
index 7c048fcc..45289cc0 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -8,11 +8,10 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
#include <inttypes.h>
#include <errno.h>
@@ -26,6 +25,8 @@
#include <json.h>
#include <cache.h>
#include <owner.h>
+#include <intervals.h>
+#include "nftutils.h"
#include <libnftnl/common.h>
#include <libnftnl/ruleset.h>
@@ -72,10 +73,10 @@ static uint32_t tcp_dflt_timeout[] = {
static uint32_t udp_dflt_timeout[] = {
[NFTNL_CTTIMEOUT_UDP_UNREPLIED] = 30,
- [NFTNL_CTTIMEOUT_UDP_REPLIED] = 180,
+ [NFTNL_CTTIMEOUT_UDP_REPLIED] = 120,
};
-struct timeout_protocol timeout_protocol[IPPROTO_MAX] = {
+struct timeout_protocol timeout_protocol[UINT8_MAX + 1] = {
[IPPROTO_TCP] = {
.array_size = NFTNL_CTTIMEOUT_TCP_MAX,
.state_to_name = tcp_state_to_name,
@@ -103,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)
@@ -145,11 +146,12 @@ struct set *set_alloc(const struct location *loc)
{
struct set *set;
+ assert(loc);
+
set = xzalloc(sizeof(*set));
set->refcnt = 1;
set->handle.set_id = ++set_id;
- if (loc != NULL)
- set->location = *loc;
+ set->location = *loc;
init_list_head(&set->stmt_list);
@@ -160,7 +162,7 @@ struct set *set_clone(const struct set *set)
{
struct set *new_set;
- new_set = set_alloc(NULL);
+ new_set = set_alloc(&internal_location);
handle_merge(&new_set->handle, &set->handle);
new_set->flags = set->flags;
new_set->gc_int = set->gc_int;
@@ -189,16 +191,16 @@ void set_free(struct set *set)
if (--set->refcnt > 0)
return;
- if (set->init != NULL)
- expr_free(set->init);
+
+ 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);
expr_free(set->key);
expr_free(set->data);
- xfree(set);
+ free(set);
}
struct set *set_lookup_fuzzy(const char *set_name,
@@ -452,6 +454,8 @@ struct rule *rule_alloc(const struct location *loc, const struct handle *h)
{
struct rule *rule;
+ assert(loc);
+
rule = xzalloc(sizeof(*rule));
rule->location = *loc;
init_list_head(&rule->list);
@@ -475,8 +479,8 @@ void rule_free(struct rule *rule)
return;
stmt_list_free(&rule->stmts);
handle_free(&rule->handle);
- xfree(rule->comment);
- xfree(rule);
+ free_const(rule->comment);
+ free(rule);
}
void rule_print(const struct rule *rule, struct output_ctx *octx)
@@ -553,16 +557,16 @@ 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);
+ 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)
@@ -593,9 +597,9 @@ 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);
+ free(sym);
}
}
@@ -675,6 +679,7 @@ static const char * const chain_hookname_str_array[] = {
"postrouting",
"output",
"ingress",
+ "egress",
NULL,
};
@@ -693,17 +698,16 @@ const char *chain_hookname_lookup(const char *name)
/* internal ID to uniquely identify a set in the batch */
static uint32_t chain_id;
-struct chain *chain_alloc(const char *name)
+struct chain *chain_alloc(void)
{
struct chain *chain;
chain = xzalloc(sizeof(*chain));
+ chain->location = internal_location;
chain->refcnt = 1;
chain->handle.chain_id = ++chain_id;
init_list_head(&chain->rules);
init_list_head(&chain->scope.symbols);
- if (name != NULL)
- chain->handle.chain.name = xstrdup(name);
chain->policy = NULL;
return chain;
@@ -725,16 +729,20 @@ 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);
- 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]);
- xfree(chain->dev_array);
+ free_const(chain->dev_array[i]);
+ free(chain->dev_array);
expr_free(chain->priority.expr);
expr_free(chain->policy);
- xfree(chain->comment);
- xfree(chain);
+ free_const(chain->comment);
+
+ /* MUST be released after all expressions, they could
+ * hold refcounts.
+ */
+ scope_release(&chain->scope);
+ free(chain);
}
struct chain *chain_binding_lookup(const struct table *table,
@@ -757,6 +765,9 @@ struct chain *chain_lookup_fuzzy(const struct handle *h,
struct table *table;
struct chain *chain;
+ if (!h->chain.name)
+ return NULL;
+
string_misspell_init(&st);
list_for_each_entry(table, &cache->table_cache.list, cache.list) {
@@ -832,6 +843,8 @@ const char *hooknum2str(unsigned int family, unsigned int hooknum)
switch (hooknum) {
case NF_NETDEV_INGRESS:
return "ingress";
+ case NF_NETDEV_EGRESS:
+ return "egress";
}
break;
default:
@@ -857,7 +870,7 @@ struct prio_tag {
const char *str;
};
-const static struct prio_tag std_prios[] = {
+static const struct prio_tag std_prios[] = {
{ NF_IP_PRI_RAW, "raw" },
{ NF_IP_PRI_MANGLE, "mangle" },
{ NF_IP_PRI_NAT_DST, "dstnat" },
@@ -866,7 +879,7 @@ const static struct prio_tag std_prios[] = {
{ NF_IP_PRI_NAT_SRC, "srcnat" },
};
-const static struct prio_tag bridge_std_prios[] = {
+static const struct prio_tag bridge_std_prios[] = {
{ NF_BR_PRI_NAT_DST_BRIDGED, "dstnat" },
{ NF_BR_PRI_FILTER_BRIDGED, "filter" },
{ NF_BR_PRI_NAT_DST_OTHER, "out" },
@@ -920,7 +933,8 @@ static bool std_prio_family_hook_compat(int prio, int family, int hook)
case NFPROTO_INET:
case NFPROTO_IPV4:
case NFPROTO_IPV6:
- if (hook == NF_INET_PRE_ROUTING)
+ if (hook == NF_INET_PRE_ROUTING ||
+ hook == NF_INET_LOCAL_OUT)
return true;
}
break;
@@ -929,7 +943,8 @@ static bool std_prio_family_hook_compat(int prio, int family, int hook)
case NFPROTO_INET:
case NFPROTO_IPV4:
case NFPROTO_IPV6:
- if (hook == NF_INET_POST_ROUTING)
+ if (hook == NF_INET_LOCAL_IN ||
+ hook == NF_INET_POST_ROUTING)
return true;
}
}
@@ -962,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) {
@@ -980,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;
}
@@ -1051,13 +1069,19 @@ static void chain_print_declaration(const struct chain *chain,
void chain_rules_print(const struct chain *chain, struct output_ctx *octx,
const char *indent)
{
+ unsigned int flags = octx->flags;
struct rule *rule;
+ if (chain->flags & CHAIN_F_BINDING)
+ octx->flags &= ~NFT_CTX_OUTPUT_HANDLE;
+
list_for_each_entry(rule, &chain->rules, list) {
nft_print(octx, "\t\t%s", indent ? : "");
rule_print(rule, octx);
nft_print(octx, "\n");
}
+
+ octx->flags = flags;
}
static void chain_print(const struct chain *chain, struct output_ctx *octx)
@@ -1078,8 +1102,21 @@ void chain_print_plain(const struct chain *chain, struct output_ctx *octx)
if (chain->flags & CHAIN_F_BASECHAIN) {
mpz_export_data(&policy, chain->policy->value,
BYTEORDER_HOST_ENDIAN, sizeof(int));
- nft_print(octx, " { type %s hook %s priority %s; policy %s; }",
- chain->type.str, chain->hook.name,
+ nft_print(octx, " { type %s hook %s ",
+ chain->type.str, chain->hook.name);
+
+ if (chain->dev_array_len > 0) {
+ int i;
+
+ nft_print(octx, "devices = { ");
+ for (i = 0; i < chain->dev_array_len; i++) {
+ nft_print(octx, "%s", chain->dev_array[i]);
+ if (i + 1 != chain->dev_array_len)
+ nft_print(octx, ", ");
+ }
+ nft_print(octx, " } ");
+ }
+ nft_print(octx, "priority %s; policy %s; }",
prio2str(octx, priobuf, sizeof(priobuf),
chain->handle.family, chain->hook.num,
chain->priority.expr),
@@ -1094,6 +1131,7 @@ struct table *table_alloc(void)
struct table *table;
table = xzalloc(sizeof(*table));
+ table->location = internal_location;
init_list_head(&table->chains);
init_list_head(&table->sets);
init_list_head(&table->objs);
@@ -1120,7 +1158,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)
@@ -1150,7 +1188,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)
@@ -1220,6 +1258,11 @@ static void table_print(const struct table *table, struct output_ctx *octx)
const char *delim = "";
const char *family = family2str(table->handle.family);
+ if (table->has_xt_stmts)
+ fprintf(octx->error_fp,
+ "# Warning: table %s %s is managed by iptables-nft, do not touch!\n",
+ family, table->handle.table.name);
+
nft_print(octx, "table %s %s {", family, table->handle.table.name);
if (nft_output_handle(octx) || table->flags & TABLE_F_OWNER)
nft_print(octx, " #");
@@ -1265,6 +1308,8 @@ struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
{
struct cmd *cmd;
+ assert(loc);
+
cmd = xzalloc(sizeof(*cmd));
init_list_head(&cmd->list);
cmd->op = op;
@@ -1272,104 +1317,12 @@ struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
cmd->handle = *h;
cmd->location = *loc;
cmd->data = data;
- return cmd;
-}
-
-void cmd_add_loc(struct cmd *cmd, uint16_t offset, const struct location *loc)
-{
- if (cmd->num_attrs >= NFT_NLATTR_LOC_MAX)
- return;
-
- cmd->attr[cmd->num_attrs].offset = offset;
- cmd->attr[cmd->num_attrs].location = loc;
- cmd->num_attrs++;
-}
-
-void nft_cmd_expand(struct cmd *cmd)
-{
- struct list_head new_cmds;
- struct flowtable *ft;
- struct table *table;
- struct chain *chain;
- struct rule *rule;
- struct set *set;
- struct obj *obj;
- struct cmd *new;
- struct handle h;
-
- init_list_head(&new_cmds);
-
- switch (cmd->obj) {
- case CMD_OBJ_TABLE:
- table = cmd->table;
- if (!table)
- return;
-
- list_for_each_entry(chain, &table->chains, list) {
- memset(&h, 0, sizeof(h));
- handle_merge(&h, &chain->handle);
- h.chain_id = chain->handle.chain_id;
- new = cmd_alloc(CMD_ADD, CMD_OBJ_CHAIN, &h,
- &chain->location, chain_get(chain));
- list_add_tail(&new->list, &new_cmds);
- }
- list_for_each_entry(obj, &table->objs, list) {
- handle_merge(&obj->handle, &table->handle);
- memset(&h, 0, sizeof(h));
- handle_merge(&h, &obj->handle);
- new = cmd_alloc(CMD_ADD, obj_type_to_cmd(obj->type), &h,
- &obj->location, obj_get(obj));
- list_add_tail(&new->list, &new_cmds);
- }
- list_for_each_entry(set, &table->sets, list) {
- handle_merge(&set->handle, &table->handle);
- memset(&h, 0, sizeof(h));
- handle_merge(&h, &set->handle);
- new = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &h,
- &set->location, set_get(set));
- list_add_tail(&new->list, &new_cmds);
- }
- list_for_each_entry(ft, &table->flowtables, list) {
- handle_merge(&ft->handle, &table->handle);
- memset(&h, 0, sizeof(h));
- handle_merge(&h, &ft->handle);
- new = cmd_alloc(CMD_ADD, CMD_OBJ_FLOWTABLE, &h,
- &ft->location, flowtable_get(ft));
- list_add_tail(&new->list, &new_cmds);
- }
- list_for_each_entry(chain, &table->chains, list) {
- list_for_each_entry(rule, &chain->rules, list) {
- memset(&h, 0, sizeof(h));
- handle_merge(&h, &rule->handle);
- if (chain->flags & CHAIN_F_BINDING) {
- rule->handle.chain_id =
- chain->handle.chain_id;
- rule->handle.chain.location =
- chain->location;
- }
- new = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &h,
- &rule->location,
- rule_get(rule));
- list_add_tail(&new->list, &new_cmds);
- }
- }
- list_splice(&new_cmds, &cmd->list);
- break;
- case CMD_OBJ_SET:
- case CMD_OBJ_MAP:
- set = cmd->set;
- if (!set->init)
- break;
+ cmd->attr = xzalloc_array(NFT_NLATTR_LOC_MAX,
+ sizeof(struct nlerr_loc));
+ cmd->attr_array_len = NFT_NLATTR_LOC_MAX;
+ init_list_head(&cmd->collapse_list);
- memset(&h, 0, sizeof(h));
- handle_merge(&h, &set->handle);
- new = cmd_alloc(CMD_ADD, CMD_OBJ_SETELEMS, &h,
- &set->location, set_get(set));
- list_add(&new->list, &cmd->list);
- break;
- default:
- break;
- }
+ return cmd;
}
struct markup *markup_alloc(uint32_t format)
@@ -1384,7 +1337,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)
@@ -1402,8 +1355,8 @@ struct monitor *monitor_alloc(uint32_t format, uint32_t type, const char *event)
void monitor_free(struct monitor *m)
{
- xfree(m->event);
- xfree(m);
+ free_const(m->event);
+ free(m);
}
void cmd_free(struct cmd *cmd)
@@ -1417,6 +1370,8 @@ void cmd_free(struct cmd *cmd)
set_free(cmd->elem.set);
break;
case CMD_OBJ_SET:
+ case CMD_OBJ_MAP:
+ case CMD_OBJ_METER:
case CMD_OBJ_SETELEMS:
set_free(cmd->set);
break;
@@ -1455,30 +1410,21 @@ void cmd_free(struct cmd *cmd)
BUG("invalid command object type %u\n", cmd->obj);
}
}
- xfree(cmd->arg);
- xfree(cmd);
+ free(cmd->attr);
+ free_const(cmd->arg);
+ free(cmd);
}
#include <netlink.h>
#include <mnl.h>
-static int __do_add_elements(struct netlink_ctx *ctx, struct set *set,
- struct expr *expr, uint32_t flags)
+static int __do_add_elements(struct netlink_ctx *ctx, struct cmd *cmd,
+ struct set *set, struct expr *expr, uint32_t flags)
{
expr->set_flags |= set->flags;
- if (mnl_nft_setelem_add(ctx, set, expr, flags) < 0)
+ if (mnl_nft_setelem_add(ctx, cmd, set, expr, flags) < 0)
return -1;
- if (!set_is_anonymous(set->flags) &&
- set->init != NULL && set->init != expr &&
- set->flags & NFT_SET_INTERVAL &&
- set->desc.field_count <= 1) {
- interval_map_decompose(expr);
- list_splice_tail_init(&expr->expressions, &set->init->expressions);
- set->init->size += expr->size;
- expr->size = 0;
- }
-
return 0;
}
@@ -1489,12 +1435,10 @@ static int do_add_elements(struct netlink_ctx *ctx, struct cmd *cmd,
struct set *set = cmd->elem.set;
if (set_is_non_concat_range(set) &&
- set_to_intervals(ctx->msgs, set, init, true,
- ctx->nft->debug_mask, set->automerge,
- &ctx->nft->output) < 0)
+ set_to_intervals(set, init, true) < 0)
return -1;
- return __do_add_elements(ctx, set, init, flags);
+ return __do_add_elements(ctx, cmd, set, init, flags);
}
static int do_add_setelems(struct netlink_ctx *ctx, struct cmd *cmd,
@@ -1502,7 +1446,7 @@ static int do_add_setelems(struct netlink_ctx *ctx, struct cmd *cmd,
{
struct set *set = cmd->set;
- return __do_add_elements(ctx, set, set->init, flags);
+ return __do_add_elements(ctx, cmd, set, set->init, flags);
}
static int do_add_set(struct netlink_ctx *ctx, struct cmd *cmd,
@@ -1516,13 +1460,17 @@ static int do_add_set(struct netlink_ctx *ctx, struct cmd *cmd,
* comes too late which might result in spurious ENFILE errors.
*/
if (set_is_non_concat_range(set) &&
- set_to_intervals(ctx->msgs, set, set->init, true,
- ctx->nft->debug_mask, set->automerge,
- &ctx->nft->output) < 0)
+ set_to_intervals(set, set->init, true) < 0)
return -1;
}
- return mnl_nft_set_add(ctx, cmd, flags);
+ if (mnl_nft_set_add(ctx, cmd, flags) < 0)
+ return -1;
+
+ if (set_is_anonymous(set->flags))
+ return __do_add_elements(ctx, cmd, set, set->init, flags);
+
+ return 0;
}
static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl)
@@ -1595,12 +1543,10 @@ static int do_delete_setelems(struct netlink_ctx *ctx, struct cmd *cmd)
struct set *set = cmd->elem.set;
if (set_is_non_concat_range(set) &&
- set_to_intervals(ctx->msgs, set, expr, false,
- ctx->nft->debug_mask, set->automerge,
- &ctx->nft->output) < 0)
+ set_to_intervals(set, expr, false) < 0)
return -1;
- if (mnl_nft_setelem_del(ctx, cmd) < 0)
+ if (mnl_nft_setelem_del(ctx, cmd, &cmd->handle, cmd->elem.expr) < 0)
return -1;
return 0;
@@ -1642,8 +1588,7 @@ static int do_command_delete(struct netlink_ctx *ctx, struct cmd *cmd)
}
}
-static int do_list_table(struct netlink_ctx *ctx, struct cmd *cmd,
- struct table *table)
+static int do_list_table(struct netlink_ctx *ctx, struct table *table)
{
table_print(table, &ctx->nft->output);
return 0;
@@ -1651,11 +1596,6 @@ static int do_list_table(struct netlink_ctx *ctx, struct cmd *cmd,
static int do_list_sets(struct netlink_ctx *ctx, struct cmd *cmd)
{
- struct print_fmt_options opts = {
- .tab = "\t",
- .nl = "\n",
- .stmt_separator = "\n",
- };
struct table *table;
struct set *set;
@@ -1673,13 +1613,12 @@ static int do_list_sets(struct netlink_ctx *ctx, struct cmd *cmd)
!set_is_literal(set->flags))
continue;
if (cmd->obj == CMD_OBJ_METERS &&
- !set_is_meter(set->flags))
+ !set_is_meter_compat(set->flags))
continue;
if (cmd->obj == CMD_OBJ_MAPS &&
!map_is_literal(set->flags))
continue;
- set_print_declaration(set, &opts, &ctx->nft->output);
- nft_print(&ctx->nft->output, "%s}%s", opts.tab, opts.nl);
+ set_print(set, &ctx->nft->output);
}
nft_print(&ctx->nft->output, "}\n");
@@ -1691,9 +1630,10 @@ struct obj *obj_alloc(const struct location *loc)
{
struct obj *obj;
+ assert(loc);
+
obj = xzalloc(sizeof(*obj));
- if (loc != NULL)
- obj->location = *loc;
+ obj->location = *loc;
obj->refcnt = 1;
return obj;
@@ -1709,18 +1649,18 @@ 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);
- xfree(ts);
+ free_const(ts->timeout_str);
+ free(ts);
}
}
- xfree(obj);
+ free(obj);
}
struct obj *obj_lookup_fuzzy(const char *obj_name,
@@ -1745,10 +1685,10 @@ struct obj *obj_lookup_fuzzy(const char *obj_name,
static void print_proto_name_proto(uint8_t l4, struct output_ctx *octx)
{
- const struct protoent *p = getprotobynumber(l4);
+ char name[NFT_PROTONAME_MAXSIZE];
- if (p)
- nft_print(octx, "%s", p->p_name);
+ if (nft_getprotobynumber(l4, name, sizeof(name)))
+ nft_print(octx, "%s", name);
else
nft_print(octx, "%d", l4);
}
@@ -1763,11 +1703,14 @@ static void print_proto_timeout_policy(uint8_t l4, const uint32_t *timeout,
nft_print(octx, "%s%spolicy = { ", opts->tab, opts->tab);
for (i = 0; i < timeout_protocol[l4].array_size; i++) {
if (timeout[i] != timeout_protocol[l4].dflt_timeout[i]) {
+ uint64_t timeout_ms;
+
if (comma)
nft_print(octx, ", ");
- nft_print(octx, "%s : %u",
- timeout_protocol[l4].state_to_name[i],
- timeout[i]);
+ timeout_ms = timeout[i] * 1000u;
+ nft_print(octx, "%s : ",
+ timeout_protocol[l4].state_to_name[i]);
+ time_print(timeout_ms, octx);
comma = true;
}
}
@@ -1989,7 +1932,7 @@ static const char * const obj_type_name_array[] = {
[NFT_OBJECT_CT_EXPECT] = "ct expectation",
};
-const char *obj_type_name(enum stmt_types type)
+const char *obj_type_name(unsigned int type)
{
assert(type <= NFT_OBJECT_MAX && obj_type_name_array[type]);
@@ -2007,7 +1950,7 @@ static uint32_t obj_type_cmd_array[NFT_OBJECT_MAX + 1] = {
[NFT_OBJECT_CT_EXPECT] = CMD_OBJ_CT_EXPECT,
};
-uint32_t obj_type_to_cmd(uint32_t type)
+enum cmd_obj obj_type_to_cmd(uint32_t type)
{
assert(type <= NFT_OBJECT_MAX && obj_type_cmd_array[type]);
@@ -2099,9 +2042,10 @@ struct flowtable *flowtable_alloc(const struct location *loc)
{
struct flowtable *flowtable;
+ assert(loc);
+
flowtable = xzalloc(sizeof(*flowtable));
- if (loc != NULL)
- flowtable->location = *loc;
+ flowtable->location = *loc;
flowtable->refcnt = 1;
return flowtable;
@@ -2125,10 +2069,10 @@ 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]);
- xfree(flowtable->dev_array);
+ free_const(flowtable->dev_array[i]);
+ free(flowtable->dev_array);
}
- xfree(flowtable);
+ free(flowtable);
}
static void flowtable_print_declaration(const struct flowtable *flowtable,
@@ -2274,15 +2218,10 @@ static int do_list_ruleset(struct netlink_ctx *ctx, struct cmd *cmd)
table->handle.family != family)
continue;
- cmd->handle.family = table->handle.family;
- cmd->handle.table.name = table->handle.table.name;
-
- if (do_list_table(ctx, cmd, table) < 0)
+ if (do_list_table(ctx, table) < 0)
return -1;
}
- cmd->handle.table.name = NULL;
-
return 0;
}
@@ -2306,9 +2245,14 @@ static int do_list_tables(struct netlink_ctx *ctx, struct cmd *cmd)
static void table_print_declaration(struct table *table,
struct output_ctx *octx)
{
- nft_print(octx, "table %s %s {\n",
- family2str(table->handle.family),
- table->handle.table.name);
+ const char *family = family2str(table->handle.family);
+
+ if (table->has_xt_stmts)
+ fprintf(octx->error_fp,
+ "# Warning: table %s %s is managed by iptables-nft, do not touch!\n",
+ family, table->handle.table.name);
+
+ nft_print(octx, "table %s %s {\n", family, table->handle.table.name);
}
static int do_list_chain(struct netlink_ctx *ctx, struct cmd *cmd,
@@ -2318,13 +2262,9 @@ static int do_list_chain(struct netlink_ctx *ctx, struct cmd *cmd,
table_print_declaration(table, &ctx->nft->output);
- list_for_each_entry(chain, &table->chain_cache.list, cache.list) {
- if (chain->handle.family != cmd->handle.family ||
- strcmp(cmd->handle.chain.name, chain->handle.chain.name) != 0)
- continue;
-
+ chain = chain_cache_find(table, cmd->handle.chain.name);
+ if (chain)
chain_print(chain, &ctx->nft->output);
- }
nft_print(&ctx->nft->output, "}\n");
@@ -2370,11 +2310,13 @@ static void __do_list_set(struct netlink_ctx *ctx, struct cmd *cmd,
static int do_list_set(struct netlink_ctx *ctx, struct cmd *cmd,
struct table *table)
{
- struct set *set;
+ struct set *set = cmd->set;
- set = set_cache_find(table, cmd->handle.set.name);
- if (set == NULL)
- return -1;
+ if (!set) {
+ set = set_cache_find(table, cmd->handle.set.name);
+ if (set == NULL)
+ return -1;
+ }
__do_list_set(ctx, cmd, set);
@@ -2407,7 +2349,7 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_TABLE:
if (!cmd->handle.table.name)
return do_list_tables(ctx, cmd);
- return do_list_table(ctx, cmd, table);
+ return do_list_table(ctx, table);
case CMD_OBJ_CHAIN:
return do_list_chain(ctx, cmd, table);
case CMD_OBJ_CHAINS:
@@ -2417,6 +2359,8 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_SET:
return do_list_set(ctx, cmd, table);
case CMD_OBJ_RULESET:
+ case CMD_OBJ_RULES:
+ case CMD_OBJ_RULE:
return do_list_ruleset(ctx, cmd);
case CMD_OBJ_METERS:
return do_list_sets(ctx, cmd);
@@ -2436,8 +2380,10 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_CT_HELPERS:
return do_list_obj(ctx, cmd, NFT_OBJECT_CT_HELPER);
case CMD_OBJ_CT_TIMEOUT:
+ case CMD_OBJ_CT_TIMEOUTS:
return do_list_obj(ctx, cmd, NFT_OBJECT_CT_TIMEOUT);
case CMD_OBJ_CT_EXPECT:
+ case CMD_OBJ_CT_EXPECTATIONS:
return do_list_obj(ctx, cmd, NFT_OBJECT_CT_EXPECT);
case CMD_OBJ_LIMIT:
case CMD_OBJ_LIMITS:
@@ -2461,7 +2407,7 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
return 0;
}
-static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd)
+static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd, bool reset)
{
struct set *set, *new_set;
struct expr *init;
@@ -2479,7 +2425,7 @@ static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd)
/* Fetch from kernel the elements that have been requested .*/
err = netlink_get_setelem(ctx, &cmd->handle, &cmd->location,
- cmd->elem.set, new_set, init);
+ cmd->elem.set, new_set, init, reset);
if (err >= 0)
__do_list_set(ctx, cmd, new_set);
@@ -2495,7 +2441,7 @@ static int do_command_get(struct netlink_ctx *ctx, struct cmd *cmd)
{
switch (cmd->obj) {
case CMD_OBJ_ELEMENTS:
- return do_get_setelems(ctx, cmd);
+ return do_get_setelems(ctx, cmd, false);
default:
BUG("invalid command object type %u\n", cmd->obj);
}
@@ -2524,6 +2470,23 @@ static int do_command_reset(struct netlink_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_QUOTA:
type = NFT_OBJECT_QUOTA;
break;
+ case CMD_OBJ_RULES:
+ ret = netlink_reset_rules(ctx, cmd, true);
+ if (ret < 0)
+ return ret;
+
+ return do_command_list(ctx, cmd);
+ case CMD_OBJ_RULE:
+ return netlink_reset_rules(ctx, cmd, false);
+ case CMD_OBJ_ELEMENTS:
+ return do_get_setelems(ctx, cmd, true);
+ case CMD_OBJ_SET:
+ case CMD_OBJ_MAP:
+ ret = netlink_list_setelems(ctx, &cmd->handle, cmd->set, true);
+ if (ret < 0)
+ return ret;
+
+ return do_command_list(ctx, cmd);
default:
BUG("invalid command object type %u\n", cmd->obj);
}
@@ -2641,6 +2604,7 @@ int do_command(struct netlink_ctx *ctx, struct cmd *cmd)
case CMD_REPLACE:
return do_command_replace(ctx, cmd);
case CMD_DELETE:
+ case CMD_DESTROY:
return do_command_delete(ctx, cmd);
case CMD_GET:
return do_command_get(ctx, cmd);
@@ -2787,10 +2751,8 @@ static void stmt_reduce(const struct rule *rule)
/* Must not merge across other statements */
if (stmt->ops->type != STMT_EXPRESSION) {
- if (idx < 2)
- continue;
-
- payload_do_merge(sa, idx);
+ if (idx >= 2)
+ payload_do_merge(sa, idx);
idx = 0;
continue;
}
@@ -2804,7 +2766,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;
@@ -2815,7 +2776,8 @@ static void stmt_reduce(const struct rule *rule)
switch (stmt->expr->op) {
case OP_EQ:
case OP_IMPLICIT:
- if (stmt->expr->left->meta.key == NFT_META_PROTOCOL) {
+ if (stmt->expr->left->meta.key == NFT_META_PROTOCOL &&
+ !stmt->expr->left->meta.inner_desc) {
uint16_t protocol;
protocol = mpz_get_uint16(stmt->expr->right->value);
diff --git a/src/scanner.l b/src/scanner.l
index 6cc7778d..e4d20e69 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -10,12 +10,15 @@
%{
+#include <nft.h>
+
#include <limits.h>
#include <glob.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/types.h>
#include <linux/netfilter.h>
+#include <sys/stat.h>
#include <nftables.h>
#include <erec.h>
@@ -118,12 +121,12 @@ digit [0-9]
hexdigit [0-9a-fA-F]
decstring {digit}+
hexstring 0[xX]{hexdigit}+
-numberstring ({decstring}|{hexstring})
letter [a-zA-Z]
string ({letter}|[_.])({letter}|{digit}|[/\-_\.])*
quotedstring \"[^"]*\"
asteriskstring ({string}\*|{string}\\\*|\\\*|{string}\\\*{string})
comment #.*$
+comment_line ^[ \t]*#.*\n
slash \/
timestring ([0-9]+d)?([0-9]+h)?([0-9]+m)?([0-9]+s)?([0-9]+ms)?
@@ -197,27 +200,60 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
%option warn
%option stack
%s SCANSTATE_ARP
+%s SCANSTATE_AT
%s SCANSTATE_CT
%s SCANSTATE_COUNTER
%s SCANSTATE_ETH
+%s SCANSTATE_GRE
+%s SCANSTATE_ICMP
+%s SCANSTATE_IGMP
%s SCANSTATE_IP
%s SCANSTATE_IP6
+%s SCANSTATE_LAST
%s SCANSTATE_LIMIT
+%s SCANSTATE_META
+%s SCANSTATE_POLICY
%s SCANSTATE_QUOTA
%s SCANSTATE_SCTP
%s SCANSTATE_SECMARK
+%s SCANSTATE_TCP
+%s SCANSTATE_TYPE
%s SCANSTATE_VLAN
+%s SCANSTATE_XT
+%s SCANSTATE_CMD_DESTROY
+%s SCANSTATE_CMD_EXPORT
+%s SCANSTATE_CMD_IMPORT
%s SCANSTATE_CMD_LIST
+%s SCANSTATE_CMD_MONITOR
+%s SCANSTATE_CMD_RESET
+%s SCANSTATE_EXPR_AH
+%s SCANSTATE_EXPR_COMP
+%s SCANSTATE_EXPR_DCCP
+%s SCANSTATE_EXPR_DST
+%s SCANSTATE_EXPR_ESP
%s SCANSTATE_EXPR_FIB
+%s SCANSTATE_EXPR_FRAG
%s SCANSTATE_EXPR_HASH
+%s SCANSTATE_EXPR_HBH
%s SCANSTATE_EXPR_IPSEC
+%s SCANSTATE_EXPR_MH
%s SCANSTATE_EXPR_NUMGEN
+%s SCANSTATE_EXPR_OSF
%s SCANSTATE_EXPR_QUEUE
%s SCANSTATE_EXPR_RT
%s SCANSTATE_EXPR_SCTP_CHUNK
%s SCANSTATE_EXPR_SOCKET
+%s SCANSTATE_EXPR_TH
+%s SCANSTATE_EXPR_UDP
+%s SCANSTATE_EXPR_UDPLITE
+%s SCANSTATE_STMT_DUP
+%s SCANSTATE_STMT_FWD
%s SCANSTATE_STMT_LOG
+%s SCANSTATE_STMT_NAT
+%s SCANSTATE_STMT_REJECT
+%s SCANSTATE_STMT_SYNPROXY
+%s SCANSTATE_STMT_TPROXY
%%
@@ -258,7 +294,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"/" { return SLASH; }
"-" { return DASH; }
"*" { return ASTERISK; }
-"@" { return AT; }
+"@" { scanner_push_start_cond(yyscanner, SCANSTATE_AT); return AT; }
"$" { return '$'; }
"=" { return '='; }
"vmap" { return VMAP; }
@@ -272,24 +308,27 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"describe" { return DESCRIBE; }
+<SCANSTATE_CMD_LIST,SCANSTATE_CMD_MONITOR>{
+ "chains" { return CHAINS; }
+ "sets" { return SETS; }
+ "tables" { return TABLES; }
+}
+<SCANSTATE_CMD_MONITOR>{
+ "rules" { return RULES; }
+ "trace" { return TRACE; }
+}
"hook" { return HOOK; }
"device" { return DEVICE; }
"devices" { return DEVICES; }
"table" { return TABLE; }
-"tables" { return TABLES; }
"chain" { return CHAIN; }
-"chains" { return CHAINS; }
"rule" { return RULE; }
-"rules" { return RULES; }
-"sets" { return SETS; }
"set" { return SET; }
"element" { return ELEMENT; }
"map" { return MAP; }
-"maps" { return MAPS; }
"flowtable" { return FLOWTABLE; }
"handle" { return HANDLE; }
"ruleset" { return RULESET; }
-"trace" { return TRACE; }
"socket" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_SOCKET); return SOCKET; }
<SCANSTATE_EXPR_SOCKET>{
@@ -298,7 +337,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"cgroupv2" { return CGROUPV2; }
"level" { return LEVEL; }
}
-"tproxy" { return TPROXY; }
+"tproxy" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_TPROXY); return TPROXY; }
"accept" { return ACCEPT; }
"drop" { return DROP; }
@@ -306,7 +345,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"jump" { return JUMP; }
"goto" { return GOTO; }
"return" { return RETURN; }
-"to" { return TO; }
+<SCANSTATE_EXPR_QUEUE,SCANSTATE_STMT_DUP,SCANSTATE_STMT_FWD,SCANSTATE_STMT_NAT,SCANSTATE_STMT_TPROXY,SCANSTATE_IP,SCANSTATE_IP6>"to" { return TO; } /* XXX: SCANSTATE_IP is a workaround */
"inet" { return INET; }
"netdev" { return NETDEV; }
@@ -319,12 +358,14 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"delete" { return DELETE; }
"get" { return GET; }
"list" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_LIST); return LIST; }
-"reset" { return RESET; }
+"reset" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_RESET); return RESET; }
"flush" { return FLUSH; }
"rename" { return RENAME; }
-"import" { return IMPORT; }
-"export" { return EXPORT; }
-"monitor" { return MONITOR; }
+"import" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_IMPORT); return IMPORT; }
+"export" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_EXPORT); return EXPORT; }
+"monitor" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_MONITOR); return MONITOR; }
+"destroy" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_DESTROY); return DESTROY; }
+
"position" { return POSITION; }
"index" { return INDEX; }
@@ -339,10 +380,12 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"elements" { return ELEMENTS; }
"expires" { return EXPIRES; }
-"policy" { return POLICY; }
+"policy" { scanner_push_start_cond(yyscanner, SCANSTATE_POLICY); return POLICY; }
"size" { return SIZE; }
-"performance" { return PERFORMANCE; }
-"memory" { return MEMORY; }
+<SCANSTATE_POLICY>{
+ "performance" { return PERFORMANCE; }
+ "memory" { return MEMORY; }
+}
"flow" { return FLOW; }
"offload" { return OFFLOAD; }
@@ -352,26 +395,35 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"meters" { return METERS; }
"flowtables" { return FLOWTABLES; }
"limits" { return LIMITS; }
+ "maps" { return MAPS; }
"secmarks" { return SECMARKS; }
"synproxys" { return SYNPROXYS; }
"hooks" { return HOOKS; }
}
"counter" { scanner_push_start_cond(yyscanner, SCANSTATE_COUNTER); return COUNTER; }
-"name" { return NAME; }
+<SCANSTATE_COUNTER,SCANSTATE_LIMIT,SCANSTATE_QUOTA,SCANSTATE_STMT_SYNPROXY,SCANSTATE_EXPR_OSF>"name" { return NAME; }
<SCANSTATE_COUNTER,SCANSTATE_CT,SCANSTATE_LIMIT>"packets" { return PACKETS; }
<SCANSTATE_COUNTER,SCANSTATE_CT,SCANSTATE_LIMIT,SCANSTATE_QUOTA>"bytes" { return BYTES; }
-"counters" { return COUNTERS; }
-"quotas" { return QUOTAS; }
+"last" { scanner_push_start_cond(yyscanner, SCANSTATE_LAST); return LAST; }
+<SCANSTATE_LAST>{
+ "never" { return NEVER; }
+}
+
+<SCANSTATE_CMD_LIST,SCANSTATE_CMD_RESET>{
+ "counters" { return COUNTERS; }
+ "quotas" { return QUOTAS; }
+ "rules" { return RULES; }
+}
"log" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_LOG); return LOG; }
-"prefix" { return PREFIX; }
-"group" { return GROUP; }
+<SCANSTATE_STMT_LOG,SCANSTATE_STMT_NAT,SCANSTATE_IP,SCANSTATE_IP6>"prefix" { return PREFIX; }
<SCANSTATE_STMT_LOG>{
"snaplen" { return SNAPLEN; }
"queue-threshold" { return QUEUE_THRESHOLD; }
"level" { return LEVEL; }
+ "group" { return GROUP; }
}
"queue" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_QUEUE); return QUEUE;}
@@ -384,36 +436,46 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
<SCANSTATE_LIMIT>{
"rate" { return RATE; }
"burst" { return BURST; }
+
+ /* time_unit */
+ "second" { return SECOND; }
+ "minute" { return MINUTE; }
+ "week" { return WEEK; }
}
<SCANSTATE_CT,SCANSTATE_LIMIT,SCANSTATE_QUOTA>"over" { return OVER; }
"quota" { scanner_push_start_cond(yyscanner, SCANSTATE_QUOTA); return QUOTA; }
<SCANSTATE_QUOTA>{
- "used" { return USED; }
"until" { return UNTIL; }
}
-"second" { return SECOND; }
-"minute" { return MINUTE; }
+<SCANSTATE_QUOTA,SCANSTATE_LAST>"used" { return USED; }
+
"hour" { return HOUR; }
"day" { return DAY; }
-"week" { return WEEK; }
-"reject" { return _REJECT; }
-"with" { return WITH; }
-"icmpx" { return ICMPX; }
+"reject" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_REJECT); return _REJECT; }
+<SCANSTATE_STMT_REJECT>{
+ "with" { return WITH; }
+ "icmpx" { return ICMPX; }
+}
-"snat" { return SNAT; }
-"dnat" { return DNAT; }
-"masquerade" { return MASQUERADE; }
-"redirect" { return REDIRECT; }
+"snat" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return SNAT; }
+"dnat" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return DNAT; }
+"masquerade" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return MASQUERADE; }
+"redirect" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return REDIRECT; }
"random" { return RANDOM; }
-"fully-random" { return FULLY_RANDOM; }
-"persistent" { return PERSISTENT; }
+<SCANSTATE_STMT_NAT>{
+ "fully-random" { return FULLY_RANDOM; }
+ "persistent" { return PERSISTENT; }
+ "port" { return PORT; }
+}
-"ll" { return LL_HDR; }
-"nh" { return NETWORK_HDR; }
-"th" { return TRANSPORT_HDR; }
+<SCANSTATE_AT>{
+ "ll" { return LL_HDR; }
+ "nh" { return NETWORK_HDR; }
+}
+"th" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_TH); return TRANSPORT_HDR; }
"bridge" { return BRIDGE; }
@@ -422,11 +484,11 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"saddr" { return SADDR; }
"daddr" { return DADDR; }
}
-"type" { return TYPE; }
+"type" { scanner_push_start_cond(yyscanner, SCANSTATE_TYPE); return TYPE; }
"typeof" { return TYPEOF; }
"vlan" { scanner_push_start_cond(yyscanner, SCANSTATE_VLAN); return VLAN; }
-"id" { return ID; }
+<SCANSTATE_CT,SCANSTATE_EXPR_FRAG,SCANSTATE_VLAN,SCANSTATE_IP,SCANSTATE_ICMP>"id" { return ID; }
<SCANSTATE_VLAN>{
"cfi" { return CFI; }
"dei" { return DEI; }
@@ -445,15 +507,27 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
}
"ip" { scanner_push_start_cond(yyscanner, SCANSTATE_IP); return IP; }
-"version" { return HDRVERSION; }
-"hdrlength" { return HDRLENGTH; }
-"dscp" { return DSCP; }
+<SCANSTATE_IP,SCANSTATE_IP6,SCANSTATE_EXPR_OSF,SCANSTATE_GRE>{
+ "version" { return HDRVERSION; }
+}
+<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_DST,SCANSTATE_EXPR_HBH,SCANSTATE_EXPR_MH,SCANSTATE_EXPR_RT,SCANSTATE_IP>{
+ "hdrlength" { return HDRLENGTH; }
+}
+<SCANSTATE_IP,SCANSTATE_IP6,SCANSTATE_TYPE>{
+ "dscp" { return DSCP; }
+}
"ecn" { return ECN; }
-"length" { return LENGTH; }
-"frag-off" { return FRAG_OFF; }
-"ttl" { return TTL; }
-"protocol" { return PROTOCOL; }
-"checksum" { return CHECKSUM; }
+<SCANSTATE_EXPR_UDP,SCANSTATE_IP,SCANSTATE_IP6,SCANSTATE_META,SCANSTATE_TCP,SCANSTATE_SCTP,SCANSTATE_EXPR_SCTP_CHUNK>"length" { return LENGTH; }
+<SCANSTATE_EXPR_FRAG,SCANSTATE_IP>{
+ "frag-off" { return FRAG_OFF; }
+}
+<SCANSTATE_EXPR_OSF,SCANSTATE_IP>{
+ "ttl" { return TTL; }
+}
+<SCANSTATE_CT,SCANSTATE_IP,SCANSTATE_META,SCANSTATE_TYPE,SCANSTATE_GRE>"protocol" { return PROTOCOL; }
+<SCANSTATE_EXPR_MH,SCANSTATE_EXPR_UDP,SCANSTATE_EXPR_UDPLITE,SCANSTATE_ICMP,SCANSTATE_IGMP,SCANSTATE_IP,SCANSTATE_SCTP,SCANSTATE_TCP>{
+ "checksum" { return CHECKSUM; }
+}
<SCANSTATE_IP>{
"lsrr" { return LSRR; }
@@ -463,39 +537,70 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"ptr" { return PTR; }
"value" { return VALUE; }
+
+ "option" { return OPTION; }
+ "options" { return OPTIONS; }
}
-"echo" { return ECHO; }
-"eol" { return EOL; }
-"maxseg" { return MSS; }
-"mss" { return MSS; }
-"nop" { return NOP; }
-"noop" { return NOP; }
-"sack" { return SACK; }
-"sack0" { return SACK0; }
-"sack1" { return SACK1; }
-"sack2" { return SACK2; }
-"sack3" { return SACK3; }
-"sack-permitted" { return SACK_PERM; }
-"sack-perm" { return SACK_PERM; }
-"timestamp" { return TIMESTAMP; }
+<SCANSTATE_TCP>{
+ /* tcp header fields */
+ "ackseq" { return ACKSEQ; }
+ "doff" { return DOFF; }
+ "window" { return WINDOW; }
+ "urgptr" { return URGPTR; }
+
+ /* tcp option types */
+ "echo" { return ECHO; }
+ "eol" { return EOL; }
+ "maxseg" { return MSS; }
+ "mss" { return MSS; }
+ "nop" { return NOP; }
+ "noop" { return NOP; }
+ "sack" { return SACK; }
+ "sack0" { return SACK0; }
+ "sack1" { return SACK1; }
+ "sack2" { return SACK2; }
+ "sack3" { return SACK3; }
+ "sack-permitted" { return SACK_PERM; }
+ "sack-perm" { return SACK_PERM; }
+ "timestamp" { return TIMESTAMP; }
+ "fastopen" { return FASTOPEN; }
+ "mptcp" { return MPTCP; }
+ "md5sig" { return MD5SIG; }
+
+ /* tcp option fields */
+ "left" { return LEFT; }
+ "right" { return RIGHT; }
+ "count" { return COUNT; }
+ "tsval" { return TSVAL; }
+ "tsecr" { return TSECR; }
+ "subtype" { return SUBTYPE; }
+
+ "options" { return OPTIONS; }
+ "option" { return OPTION; }
+}
"time" { return TIME; }
-"kind" { return KIND; }
-"count" { return COUNT; }
-"left" { return LEFT; }
-"right" { return RIGHT; }
-"tsval" { return TSVAL; }
-"tsecr" { return TSECR; }
-
-"icmp" { return ICMP; }
-"code" { return CODE; }
-"sequence" { return SEQUENCE; }
-"gateway" { return GATEWAY; }
-"mtu" { return MTU; }
+"icmp" { scanner_push_start_cond(yyscanner, SCANSTATE_ICMP); return ICMP; }
+"icmpv6" { scanner_push_start_cond(yyscanner, SCANSTATE_ICMP); return ICMP6; }
+<SCANSTATE_ICMP>{
+ "gateway" { return GATEWAY; }
+ "code" { return CODE; }
+ "param-problem" { return PPTR; }
+ "max-delay" { return MAXDELAY; }
+ "mtu" { return MTU; }
+ "taddr" { return TADDR; }
+ "daddr" { return DADDR; }
+}
+<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_ESP,SCANSTATE_ICMP,SCANSTATE_TCP>{
+ "sequence" { return SEQUENCE; }
+}
-"igmp" { return IGMP; }
-"mrt" { return MRT; }
+"igmp" { scanner_push_start_cond(yyscanner, SCANSTATE_IGMP); return IGMP; }
+<SCANSTATE_IGMP>{
+ "mrt" { return MRT; }
+ "group" { return GROUP; }
+}
"ip6" { scanner_push_start_cond(yyscanner, SCANSTATE_IP6); return IP6; }
"priority" { return PRIORITY; }
@@ -503,36 +608,50 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"flowlabel" { return FLOWLABEL; }
"hoplimit" { return HOPLIMIT; }
}
-"nexthdr" { return NEXTHDR; }
-
-"icmpv6" { return ICMP6; }
-"param-problem" { return PPTR; }
-"max-delay" { return MAXDELAY; }
+<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_COMP,SCANSTATE_EXPR_DST,SCANSTATE_EXPR_FRAG,SCANSTATE_EXPR_HBH,SCANSTATE_EXPR_MH,SCANSTATE_EXPR_RT,SCANSTATE_IP6>{
+ "nexthdr" { return NEXTHDR; }
+}
-"ah" { return AH; }
-"reserved" { return RESERVED; }
-"spi" { return SPI; }
+"ah" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_AH); return AH; }
+<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_FRAG,SCANSTATE_EXPR_MH,SCANSTATE_TCP>{
+ "reserved" { return RESERVED; }
+}
+<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_ESP,SCANSTATE_EXPR_IPSEC>"spi" { return SPI; }
-"esp" { return ESP; }
+"esp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_ESP); return ESP; }
-"comp" { return COMP; }
+"comp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_COMP); return COMP; }
+<SCANSTATE_EXPR_COMP>{
+ "cpi" { return CPI; }
+}
"flags" { return FLAGS; }
-"cpi" { return CPI; }
-"udp" { return UDP; }
-"udplite" { return UDPLITE; }
-"sport" { return SPORT; }
-"dport" { return DPORT; }
-"port" { return PORT; }
+"udp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_UDP); return UDP; }
+"udplite" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_UDPLITE); return UDPLITE; }
+<SCANSTATE_EXPR_UDPLITE>{
+ "csumcov" { return CSUMCOV; }
+}
+<SCANSTATE_EXPR_DCCP,SCANSTATE_SCTP,SCANSTATE_TCP,SCANSTATE_EXPR_TH,SCANSTATE_EXPR_UDP,SCANSTATE_EXPR_UDPLITE>{
+ "sport" { return SPORT; }
+}
+<SCANSTATE_CT,SCANSTATE_EXPR_DCCP,SCANSTATE_SCTP,SCANSTATE_TCP,SCANSTATE_EXPR_TH,SCANSTATE_EXPR_UDP,SCANSTATE_EXPR_UDPLITE>{
+ "dport" { return DPORT; }
+}
+<SCANSTATE_EXPR_DCCP>{
+ "option" { return OPTION; }
+}
+
+"vxlan" { return VXLAN; }
+"vni" { return VNI; }
+
+"geneve" { return GENEVE; }
-"tcp" { return TCP; }
-"ackseq" { return ACKSEQ; }
-"doff" { return DOFF; }
-"window" { return WINDOW; }
-"urgptr" { return URGPTR; }
-"option" { return OPTION; }
+"gre" { scanner_push_start_cond(yyscanner, SCANSTATE_GRE); return GRE; }
+"gretap" { scanner_push_start_cond(yyscanner, SCANSTATE_GRE); return GRETAP; }
-"dccp" { return DCCP; }
+"tcp" { scanner_push_start_cond(yyscanner, SCANSTATE_TCP); return TCP; }
+
+"dccp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_DCCP); return DCCP; }
"sctp" { scanner_push_start_cond(yyscanner, SCANSTATE_SCTP); return SCTP; }
@@ -561,6 +680,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"asconf" { return ASCONF; }
"tsn" { return TSN; }
+ "sack" { return SACK; }
"stream" { return STREAM; }
"ssn" { return SSN; }
"ppid" { return PPID; }
@@ -578,26 +698,24 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
}
"rt" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT; }
-"rt0" { return RT0; }
-"rt2" { return RT2; }
-"srh" { return RT4; }
-"seg-left" { return SEG_LEFT; }
-"addr" { return ADDR; }
-"last-entry" { return LAST_ENT; }
-"tag" { return TAG; }
-"sid" { return SID; }
+"rt0" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT0; }
+"rt2" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT2; }
+"srh" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT4; }
+<SCANSTATE_EXPR_RT,SCANSTATE_STMT_NAT,SCANSTATE_IP,SCANSTATE_IP6>"addr" { return ADDR; }
-"hbh" { return HBH; }
+"hbh" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_HBH); return HBH; }
-"frag" { return FRAG; }
-"reserved2" { return RESERVED2; }
-"more-fragments" { return MORE_FRAGMENTS; }
+"frag" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_FRAG); return FRAG; }
+<SCANSTATE_EXPR_FRAG>{
+ "reserved2" { return RESERVED2; }
+ "more-fragments" { return MORE_FRAGMENTS; }
+}
-"dst" { return DST; }
+"dst" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_DST); return DST; }
-"mh" { return MH; }
+"mh" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_MH); return MH; }
-"meta" { return META; }
+"meta" { scanner_push_start_cond(yyscanner, SCANSTATE_META); return META; }
"mark" { return MARK; }
"iif" { return IIF; }
"iifname" { return IIFNAME; }
@@ -620,8 +738,15 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"cgroup" { return CGROUP; }
<SCANSTATE_EXPR_RT>{
- "classid" { return CLASSID; }
"nexthop" { return NEXTHOP; }
+ "seg-left" { return SEG_LEFT; }
+ "mtu" { return MTU; }
+ "last-entry" { return LAST_ENT; }
+ "tag" { return TAG; }
+ "sid" { return SID; }
+}
+<SCANSTATE_EXPR_RT,SCANSTATE_TYPE>{
+ "classid" { return CLASSID; }
}
"ct" { scanner_push_start_cond(yyscanner, SCANSTATE_CT); return CT; }
@@ -642,6 +767,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"label" { return LABEL; }
"state" { return STATE; }
"status" { return STATUS; }
+ "count" { return COUNT; }
}
"numgen" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_NUMGEN); return NUMGEN; }
@@ -659,24 +785,32 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"mod" { return MOD; }
"offset" { return OFFSET; }
}
-"dup" { return DUP; }
-"fwd" { return FWD; }
+"dup" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_DUP); return DUP; }
+"fwd" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_FWD); return FWD; }
"fib" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_FIB); return FIB; }
-"osf" { return OSF; }
+"osf" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_OSF); return OSF; }
-"synproxy" { return SYNPROXY; }
-"wscale" { return WSCALE; }
+"synproxy" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_SYNPROXY); return SYNPROXY; }
+<SCANSTATE_STMT_SYNPROXY>{
+ "wscale" { return WSCALE; }
+ "maxseg" { return MSS; }
+ "mss" { return MSS; }
+ "timestamp" { return TIMESTAMP; }
+ "sack-permitted" { return SACK_PERM; }
+ "sack-perm" { return SACK_PERM; }
+}
"notrack" { return NOTRACK; }
-"options" { return OPTIONS; }
"all" { return ALL; }
-"xml" { return XML; }
-"json" { return JSON; }
-"vm" { return VM; }
+<SCANSTATE_CMD_EXPORT,SCANSTATE_CMD_IMPORT,SCANSTATE_CMD_MONITOR>{
+ "xml" { return XML; }
+ "json" { return JSON; }
+ "vm" { return VM; }
+}
"exists" { return EXISTS; }
"missing" { return MISSING; }
@@ -694,6 +828,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"secmark" { scanner_push_start_cond(yyscanner, SCANSTATE_SECMARK); return SECMARK; }
+"xt" { scanner_push_start_cond(yyscanner, SCANSTATE_XT); return XT; }
+
{addrstring} {
yylval->string = xstrdup(yytext);
return STRING;
@@ -710,9 +846,9 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
return STRING;
}
-{numberstring} {
+{hexstring} {
errno = 0;
- yylval->val = strtoull(yytext, NULL, 0);
+ yylval->val = strtoull(yytext, NULL, 16);
if (errno != 0) {
yylval->string = xstrdup(yytext);
return STRING;
@@ -720,6 +856,19 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
return NUM;
}
+{decstring} {
+ int base = yytext[0] == '0' ? 8 : 10;
+ char *end;
+
+ errno = 0;
+ yylval->val = strtoull(yytext, &end, base);
+ if (errno != 0 || *end) {
+ yylval->string = xstrdup(yytext);
+ return STRING;
+ }
+ return NUM;
+ }
+
{classid}/[ \t\n:\-},] {
yylval->string = xstrdup(yytext);
return STRING;
@@ -752,6 +901,9 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
{tab}+
{space}+
+{comment_line} {
+ reset_pos(yyget_extra(yyscanner), yylloc);
+ }
{comment}
<<EOF>> {
@@ -823,9 +975,59 @@ static void scanner_push_file(struct nft_ctx *nft, void *scanner,
scanner_push_indesc(state, indesc);
}
+enum nft_include_type {
+ NFT_INCLUDE,
+ NFT_CMDLINE,
+};
+
+static bool __is_useable(unsigned int type, enum nft_include_type t)
+{
+ type &= S_IFMT;
+ switch (type) {
+ case S_IFREG: return true;
+ case S_IFIFO:
+ return t == NFT_CMDLINE; /* disallow include /path/to/fifo */
+ default:
+ break;
+ }
+
+ return false;
+}
+
+/* need to use stat() to, fopen() will block for named fifos */
+static bool filename_is_useable(const char *name)
+{
+ struct stat sb;
+ int err;
+
+ err = stat(name, &sb);
+ if (err)
+ return false;
+
+ return __is_useable(sb.st_mode, NFT_INCLUDE);
+}
+
+static bool fp_is_useable(FILE *fp, enum nft_include_type t)
+{
+ int fd = fileno(fp);
+ struct stat sb;
+ int err;
+
+ if (fd < 0)
+ return false;
+
+ err = fstat(fd, &sb);
+ if (err < 0)
+ return false;
+
+ return __is_useable(sb.st_mode, t);
+}
+
static int include_file(struct nft_ctx *nft, void *scanner,
const char *filename, const struct location *loc,
- const struct input_descriptor *parent_indesc)
+ const struct input_descriptor *parent_indesc,
+ enum nft_include_type includetype)
+
{
struct parser_state *state = yyget_extra(scanner);
struct error_record *erec;
@@ -837,12 +1039,24 @@ static int include_file(struct nft_ctx *nft, void *scanner,
goto err;
}
+ if (includetype == NFT_INCLUDE && !filename_is_useable(filename)) {
+ erec = error(loc, "Not a regular file: \"%s\"\n", filename);
+ goto err;
+ }
+
f = fopen(filename, "r");
if (f == NULL) {
erec = error(loc, "Could not open file \"%s\": %s\n",
filename, strerror(errno));
goto err;
}
+
+ if (!fp_is_useable(f, includetype)) {
+ fclose(f);
+ erec = error(loc, "Not a regular file: \"%s\"\n", filename);
+ goto err;
+ }
+
scanner_push_file(nft, scanner, f, filename, loc, parent_indesc);
return 0;
err:
@@ -903,7 +1117,7 @@ static int include_glob(struct nft_ctx *nft, void *scanner, const char *pattern,
ret = glob(pattern, flags, NULL, &glob_data);
if (ret == 0) {
char *path;
- int len;
+ size_t len;
/* reverse alphabetical order due to stack */
for (i = glob_data.gl_pathc; i > 0; i--) {
@@ -915,7 +1129,7 @@ static int include_glob(struct nft_ctx *nft, void *scanner, const char *pattern,
if (len == 0 || path[len - 1] == '/')
continue;
- ret = include_file(nft, scanner, path, loc, indesc);
+ ret = include_file(nft, scanner, path, loc, indesc, NFT_INCLUDE);
if (ret != 0)
goto err;
}
@@ -952,7 +1166,7 @@ err:
int scanner_read_file(struct nft_ctx *nft, const char *filename,
const struct location *loc)
{
- return include_file(nft, nft->scanner, filename, loc, NULL);
+ return include_file(nft, nft->scanner, filename, loc, NULL, NFT_CMDLINE);
}
static bool search_in_include_path(const char *filename)
@@ -1024,7 +1238,7 @@ void scanner_push_buffer(void *scanner, const struct input_descriptor *indesc,
new_indesc = xzalloc(sizeof(struct input_descriptor));
memcpy(new_indesc, indesc, sizeof(*new_indesc));
new_indesc->data = buffer;
- new_indesc->name = NULL;
+ new_indesc->name = xstrdup(indesc->name);
scanner_push_indesc(state, new_indesc);
b = yy_scan_string(buffer, scanner);
@@ -1039,14 +1253,16 @@ void *scanner_init(struct parser_state *state)
yylex_init_extra(state, &scanner);
yyset_out(NULL, scanner);
+ state->startcond_active = xzalloc_array(__SC_MAX,
+ sizeof(*state->startcond_active));
return scanner;
}
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)
@@ -1068,6 +1284,8 @@ void scanner_destroy(struct nft_ctx *nft)
struct parser_state *state = yyget_extra(nft->scanner);
input_descriptor_list_destroy(state);
+ free(state->startcond_active);
+
yylex_destroy(nft->scanner);
}
@@ -1076,6 +1294,7 @@ static void scanner_push_start_cond(void *scanner, enum startcond_type type)
struct parser_state *state = yyget_extra(scanner);
state->startcond_type = type;
+ state->startcond_active[type]++;
yy_push_state((int)type, scanner);
}
@@ -1084,6 +1303,8 @@ void scanner_pop_start_cond(void *scanner, enum startcond_type t)
{
struct parser_state *state = yyget_extra(scanner);
+ state->startcond_active[t]--;
+
if (state->startcond_type != t) {
state->flex_state_pop++;
return; /* Can't pop just yet! */
@@ -1093,6 +1314,10 @@ void scanner_pop_start_cond(void *scanner, enum startcond_type t)
state->flex_state_pop--;
state->startcond_type = yy_top_state(scanner);
yy_pop_state(scanner);
+
+ t = state->startcond_type;
+ if (state->startcond_active[t])
+ return;
}
state->startcond_type = yy_top_state(scanner);
diff --git a/src/sctp_chunk.c b/src/sctp_chunk.c
index 6e73e72f..24a07e20 100644
--- a/src/sctp_chunk.c
+++ b/src/sctp_chunk.c
@@ -6,10 +6,11 @@
* later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <exthdr.h>
#include <sctp_chunk.h>
-#include <string.h>
#define PHT(__token, __offset, __len) \
PROTO_HDR_TEMPLATE(__token, &integer_type, BYTEORDER_BIG_ENDIAN, \
diff --git a/src/segtree.c b/src/segtree.c
index f721954f..5e6f857f 100644
--- a/src/segtree.c
+++ b/src/segtree.c
@@ -8,8 +8,8 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
-#include <stdlib.h>
-#include <string.h>
+#include <nft.h>
+
#include <inttypes.h>
#include <arpa/inet.h>
@@ -19,573 +19,26 @@
#include <expression.h>
#include <gmputil.h>
#include <utils.h>
-#include <rbtree.h>
-
-/**
- * struct seg_tree - segment tree
- *
- * @root: the rbtree's root
- * @type: the datatype of the dimension
- * @dwidth: width of the dimension
- * @byteorder: byteorder of elements
- * @debug_mask: display debugging information
- */
-struct seg_tree {
- struct rb_root root;
- const struct datatype *keytype;
- unsigned int keylen;
- const struct datatype *datatype;
- unsigned int datalen;
- enum byteorder byteorder;
- unsigned int debug_mask;
-};
-
-enum elementary_interval_flags {
- EI_F_INTERVAL_END = 0x1,
- EI_F_INTERVAL_OPEN = 0x2,
-};
-
-/**
- * struct elementary_interval - elementary interval [left, right]
- *
- * @rb_node: seg_tree rb node
- * @list: list node for linearized tree
- * @left: left endpoint
- * @right: right endpoint
- * @size: interval size (right - left)
- * @flags: flags
- * @expr: associated expression
- */
-struct elementary_interval {
- union {
- struct rb_node rb_node;
- struct list_head list;
- };
-
- mpz_t left;
- mpz_t right;
- mpz_t size;
-
- enum elementary_interval_flags flags;
- struct expr *expr;
-};
-
-static void seg_tree_init(struct seg_tree *tree, const struct set *set,
- struct expr *init, unsigned int debug_mask)
-{
- struct expr *first;
-
- first = list_entry(init->expressions.next, struct expr, list);
- tree->root = RB_ROOT;
- tree->keytype = set->key->dtype;
- tree->keylen = set->key->len;
- tree->datatype = NULL;
- tree->datalen = 0;
- if (set->data) {
- tree->datatype = set->data->dtype;
- tree->datalen = set->data->len;
- }
- tree->byteorder = first->byteorder;
- tree->debug_mask = debug_mask;
-}
-
-static struct elementary_interval *ei_alloc(const mpz_t left, const mpz_t right,
- struct expr *expr,
- enum elementary_interval_flags flags)
-{
- struct elementary_interval *ei;
-
- ei = xzalloc(sizeof(*ei));
- mpz_init_set(ei->left, left);
- mpz_init_set(ei->right, right);
- mpz_init(ei->size);
- mpz_sub(ei->size, right, left);
- if (expr != NULL)
- ei->expr = expr_get(expr);
- ei->flags = flags;
- return ei;
-}
-
-static void ei_destroy(struct elementary_interval *ei)
-{
- mpz_clear(ei->left);
- mpz_clear(ei->right);
- mpz_clear(ei->size);
- if (ei->expr != NULL)
- expr_free(ei->expr);
- xfree(ei);
-}
-
-/**
- * ei_lookup - find elementary interval containing point p
- *
- * @tree: segment tree
- * @p: the point
- */
-static struct elementary_interval *ei_lookup(struct seg_tree *tree, const mpz_t p)
-{
- struct rb_node *n = tree->root.rb_node;
- struct elementary_interval *ei;
-
- while (n != NULL) {
- ei = rb_entry(n, struct elementary_interval, rb_node);
-
- if (mpz_cmp(p, ei->left) >= 0 &&
- mpz_cmp(p, ei->right) <= 0)
- return ei;
- else if (mpz_cmp(p, ei->left) <= 0)
- n = n->rb_left;
- else if (mpz_cmp(p, ei->right) > 0)
- n = n->rb_right;
- }
- return NULL;
-}
-
-static void ei_remove(struct seg_tree *tree, struct elementary_interval *ei)
-{
- rb_erase(&ei->rb_node, &tree->root);
-}
-
-static void __ei_insert(struct seg_tree *tree, struct elementary_interval *new)
-{
- struct rb_node **p = &tree->root.rb_node;
- struct rb_node *parent = NULL;
- struct elementary_interval *ei;
-
- while (*p != NULL) {
- parent = *p;
- ei = rb_entry(parent, struct elementary_interval, rb_node);
-
- if (mpz_cmp(new->left, ei->left) >= 0 &&
- mpz_cmp(new->left, ei->right) <= 0)
- break;
- else if (mpz_cmp(new->left, ei->left) <= 0)
- p = &(*p)->rb_left;
- else if (mpz_cmp(new->left, ei->left) > 0)
- p = &(*p)->rb_right;
- }
-
- rb_link_node(&new->rb_node, parent, p);
- rb_insert_color(&new->rb_node, &tree->root);
-}
-
-static bool segtree_debug(unsigned int debug_mask)
-{
- if (debug_mask & NFT_DEBUG_SEGTREE)
- return true;
-
- return false;
-}
-
-/**
- * ei_insert - insert an elementary interval into the tree
- *
- * @tree: the seg_tree
- * @new: the elementary interval
- *
- * New entries take precedence over existing ones. Insertions are assumed to
- * be ordered by descending interval size, meaning the new interval never
- * extends over more than two existing intervals.
- */
-static int ei_insert(struct list_head *msgs, struct seg_tree *tree,
- struct elementary_interval *new, bool merge)
-{
- struct elementary_interval *lei, *rei, *ei;
- struct expr *new_expr, *expr;
- mpz_t p;
-
- mpz_init2(p, tree->keylen);
-
- /*
- * Lookup the intervals containing the left and right endpoints.
- */
- lei = ei_lookup(tree, new->left);
- rei = ei_lookup(tree, new->right);
-
- if (segtree_debug(tree->debug_mask))
- pr_gmp_debug("insert: [%Zx %Zx]\n", new->left, new->right);
-
- if (lei != NULL && rei != NULL && lei == rei) {
- if (!merge) {
- ei = lei;
- goto err;
- }
- /* single element contained in an existing interval */
- if (mpz_cmp(new->left, new->right) == 0) {
- ei_destroy(new);
- goto out;
- }
-
- /*
- * The new interval is entirely contained in the same interval,
- * split it into two parts:
- *
- * [lei_left, new_left) and (new_right, rei_right]
- */
- if (segtree_debug(tree->debug_mask))
- pr_gmp_debug("split [%Zx %Zx]\n", lei->left, lei->right);
-
- ei_remove(tree, lei);
-
- mpz_sub_ui(p, new->left, 1);
- if (mpz_cmp(lei->left, p) <= 0)
- __ei_insert(tree, ei_alloc(lei->left, p, lei->expr, 0));
-
- mpz_add_ui(p, new->right, 1);
- if (mpz_cmp(p, rei->right) < 0)
- __ei_insert(tree, ei_alloc(p, rei->right, lei->expr, 0));
- ei_destroy(lei);
- } else {
- if (lei != NULL) {
- if (!merge) {
- ei = lei;
- goto err;
- }
- /*
- * Left endpoint is within lei, adjust it so we have:
- *
- * [lei_left, new_left)[new_left, new_right]
- */
- if (segtree_debug(tree->debug_mask)) {
- pr_gmp_debug("adjust left [%Zx %Zx]\n",
- lei->left, lei->right);
- }
-
- mpz_sub_ui(lei->right, new->left, 1);
- mpz_sub(lei->size, lei->right, lei->left);
- if (mpz_sgn(lei->size) < 0) {
- ei_remove(tree, lei);
- ei_destroy(lei);
- }
- }
- if (rei != NULL) {
- if (!merge) {
- ei = rei;
- goto err;
- }
- /*
- * Right endpoint is within rei, adjust it so we have:
- *
- * [new_left, new_right](new_right, rei_right]
- */
- if (segtree_debug(tree->debug_mask)) {
- pr_gmp_debug("adjust right [%Zx %Zx]\n",
- rei->left, rei->right);
- }
-
- mpz_add_ui(rei->left, new->right, 1);
- mpz_sub(rei->size, rei->right, rei->left);
- if (mpz_sgn(rei->size) < 0) {
- ei_remove(tree, rei);
- ei_destroy(rei);
- }
- }
- }
-
- __ei_insert(tree, new);
-out:
- mpz_clear(p);
-
- return 0;
-err:
- mpz_clear(p);
- errno = EEXIST;
- if (new->expr->etype == EXPR_MAPPING) {
- new_expr = new->expr->left;
- expr = ei->expr->left;
- } else {
- new_expr = new->expr;
- expr = ei->expr;
- }
-
- return expr_binary_error(msgs, new_expr, expr,
- "conflicting intervals specified");
-}
-
-/*
- * Sort intervals according to their priority, which is defined inversely to
- * their size.
- *
- * The beginning of the interval is used as secondary sorting criterion. This
- * makes sure that overlapping ranges with equal priority are next to each
- * other, allowing to easily detect unsolvable conflicts during insertion.
- *
- * Note: unsolvable conflicts can only occur when using ranges or two identical
- * prefix specifications.
- */
-static int interval_cmp(const void *p1, const void *p2)
-{
- const struct elementary_interval *e1 = *(void * const *)p1;
- const struct elementary_interval *e2 = *(void * const *)p2;
- mpz_t d;
- int ret;
-
- mpz_init(d);
-
- mpz_sub(d, e2->size, e1->size);
- if (mpz_cmp_ui(d, 0))
- ret = mpz_sgn(d);
- else
- ret = mpz_cmp(e1->left, e2->left);
-
- mpz_clear(d);
- return ret;
-}
-
-static unsigned int expr_to_intervals(const struct expr *set,
- unsigned int keylen,
- struct elementary_interval **intervals)
-{
- struct elementary_interval *ei;
- struct expr *i, *next;
- unsigned int n;
- mpz_t low, high;
-
- mpz_init2(low, keylen);
- mpz_init2(high, keylen);
-
- /*
- * Convert elements to intervals.
- */
- n = 0;
- list_for_each_entry_safe(i, next, &set->expressions, list) {
- range_expr_value_low(low, i);
- range_expr_value_high(high, i);
- ei = ei_alloc(low, high, i, 0);
- intervals[n++] = ei;
- }
- mpz_clear(high);
- mpz_clear(low);
-
- return n;
-}
-
-static bool intervals_match(const struct elementary_interval *e1,
- const struct elementary_interval *e2)
-{
- return mpz_cmp(e1->left, e2->left) == 0 &&
- mpz_cmp(e1->right, e2->right) == 0;
-}
-
-/* This function checks for overlaps in two ways:
- *
- * 1) A new interval end intersects an existing interval.
- * 2) New intervals that are larger than existing ones, that don't intersect
- * at all, but that wrap the existing ones.
- */
-static bool interval_overlap(const struct elementary_interval *e1,
- const struct elementary_interval *e2)
-{
- if (intervals_match(e1, e2))
- return false;
-
- return (mpz_cmp(e1->left, e2->left) >= 0 &&
- mpz_cmp(e1->left, e2->right) <= 0) ||
- (mpz_cmp(e1->right, e2->left) >= 0 &&
- mpz_cmp(e1->right, e2->right) <= 0) ||
- (mpz_cmp(e1->left, e2->left) <= 0 &&
- mpz_cmp(e1->right, e2->right) >= 0);
-}
-
-static int set_overlap(struct list_head *msgs, const struct set *set,
- struct expr *init, unsigned int keylen, bool add)
-{
- struct elementary_interval *new_intervals[init->size + 1];
- struct elementary_interval *intervals[set->init->size + 1];
- unsigned int n, m, i, j;
- int ret = 0;
-
- n = expr_to_intervals(init, keylen, new_intervals);
- m = expr_to_intervals(set->init, keylen, intervals);
-
- for (i = 0; i < n; i++) {
- bool found = false;
-
- for (j = 0; j < m; j++) {
- if (add && interval_overlap(new_intervals[i],
- intervals[j])) {
- expr_error(msgs, new_intervals[i]->expr,
- "interval overlaps with an existing one");
- errno = EEXIST;
- ret = -1;
- goto out;
- } else if (!add && intervals_match(new_intervals[i],
- intervals[j])) {
- found = true;
- break;
- }
- }
- if (!add && !found) {
- expr_error(msgs, new_intervals[i]->expr,
- "interval not found in set");
- errno = ENOENT;
- ret = -1;
- break;
- }
- }
-out:
- for (i = 0; i < n; i++)
- ei_destroy(new_intervals[i]);
- for (i = 0; i < m; i++)
- ei_destroy(intervals[i]);
-
- return ret;
-}
-
-static int set_to_segtree(struct list_head *msgs, struct set *set,
- struct expr *init, struct seg_tree *tree,
- bool add, bool merge)
-{
- struct elementary_interval **intervals;
- struct expr *i, *next;
- unsigned int n, m;
- int err = 0;
-
- /* We are updating an existing set with new elements, check if the new
- * interval overlaps with any of the existing ones.
- */
- if (set->init && set->init != init) {
- err = set_overlap(msgs, set, init, tree->keylen, add);
- if (err < 0)
- return err;
- }
-
- intervals = xmalloc_array(init->size, sizeof(intervals[0]));
- n = expr_to_intervals(init, tree->keylen, intervals);
-
- list_for_each_entry_safe(i, next, &init->expressions, list) {
- list_del(&i->list);
- expr_free(i);
- }
- /*
- * Sort intervals by priority.
- */
- qsort(intervals, n, sizeof(intervals[0]), interval_cmp);
-
- /*
- * Insert elements into tree
- */
- for (n = 0; n < init->size; n++) {
- err = ei_insert(msgs, tree, intervals[n], merge);
- if (err < 0) {
- struct elementary_interval *ei;
- struct rb_node *node, *next;
-
- for (m = n; m < init->size; m++)
- ei_destroy(intervals[m]);
-
- rb_for_each_entry_safe(ei, node, next, &tree->root, rb_node) {
- ei_remove(tree, ei);
- ei_destroy(ei);
- }
- break;
- }
- }
-
- xfree(intervals);
- return err;
-}
-
-static bool segtree_needs_first_segment(const struct set *set,
- const struct expr *init, bool add)
+static enum byteorder get_key_byteorder(const struct expr *e)
{
- if (add && !set->root) {
- /* Add the first segment in four situations:
- *
- * 1) This is an anonymous set.
- * 2) This set exists and it is empty.
- * 3) New empty set and, separately, new elements are added.
- * 4) This set is created with a number of initial elements.
+ enum datatypes basetype = expr_basetype(e)->type;
+
+ switch (basetype) {
+ case TYPE_INTEGER:
+ /* For ranges, integers MUST be in BYTEORDER_BIG_ENDIAN.
+ * If the LHS (lookup key, e.g. 'meta mark', is host endian,
+ * a byteorder expression is injected to convert the register
+ * content before lookup.
*/
- if ((set_is_anonymous(set->flags)) ||
- (set->init && set->init->size == 0) ||
- (set->init == NULL && init) ||
- (set->init == init)) {
- return true;
- }
- }
- /* This is an update for a set that already contains elements, so don't
- * add the first non-matching elements otherwise we hit EEXIST.
- */
- return false;
-}
-
-static void segtree_linearize(struct list_head *list, const struct set *set,
- const struct expr *init, struct seg_tree *tree,
- bool add, bool merge)
-{
- bool needs_first_segment = segtree_needs_first_segment(set, init, add);
- struct elementary_interval *ei, *nei, *prev = NULL;
- struct rb_node *node, *next;
- mpz_t p, q;
-
- mpz_init2(p, tree->keylen);
- mpz_init2(q, tree->keylen);
-
- /*
- * Convert the tree of open intervals to half-closed map expressions.
- */
- rb_for_each_entry_safe(ei, node, next, &tree->root, rb_node) {
- if (segtree_debug(tree->debug_mask))
- pr_gmp_debug("iter: [%Zx %Zx]\n", ei->left, ei->right);
-
- if (prev == NULL) {
- /*
- * If the first segment doesn't begin at zero, insert a
- * non-matching segment to cover [0, first_left).
- */
- if (needs_first_segment && mpz_cmp_ui(ei->left, 0)) {
- mpz_set_ui(p, 0);
- mpz_sub_ui(q, ei->left, 1);
- nei = ei_alloc(p, q, NULL, EI_F_INTERVAL_END);
- list_add_tail(&nei->list, list);
- }
- } else {
- /*
- * If the previous segment doesn't end directly left to
- * this one, insert a non-matching segment to cover
- * (prev_right, ei_left).
- */
- mpz_add_ui(p, prev->right, 1);
- if (mpz_cmp(p, ei->left) < 0 ||
- (!set_is_anonymous(set->flags) && !merge)) {
- mpz_sub_ui(q, ei->left, 1);
- nei = ei_alloc(p, q, NULL, EI_F_INTERVAL_END);
- list_add_tail(&nei->list, list);
- } else if (add && merge &&
- ei->expr->etype != EXPR_MAPPING) {
- /* Merge contiguous segments only in case of
- * new additions.
- */
- mpz_set(prev->right, ei->right);
- ei_remove(tree, ei);
- ei_destroy(ei);
- continue;
- }
- }
-
- ei_remove(tree, ei);
- list_add_tail(&ei->list, list);
-
- prev = ei;
- }
-
- /*
- * If the last segment doesn't end at the right side of the dimension,
- * insert a non-matching segment to cover (last_right, end].
- */
- if (mpz_scan0(prev->right, 0) != tree->keylen) {
- mpz_add_ui(p, prev->right, 1);
- mpz_bitmask(q, tree->keylen);
- nei = ei_alloc(p, q, NULL, EI_F_INTERVAL_END);
- list_add_tail(&nei->list, list);
- } else {
- prev->flags |= EI_F_INTERVAL_OPEN;
+ return BYTEORDER_BIG_ENDIAN;
+ case TYPE_STRING:
+ return BYTEORDER_HOST_ENDIAN;
+ default:
+ break;
}
- mpz_clear(p);
- mpz_clear(q);
+ return BYTEORDER_INVALID;
}
static void interval_expr_copy(struct expr *dst, struct expr *src)
@@ -600,88 +53,6 @@ static void interval_expr_copy(struct expr *dst, struct expr *src)
list_splice_init(&src->stmt_list, &dst->stmt_list);
}
-static void set_insert_interval(struct expr *set, struct seg_tree *tree,
- const struct elementary_interval *ei)
-{
- struct expr *expr;
-
- expr = constant_expr_alloc(&internal_location, tree->keytype,
- tree->byteorder, tree->keylen, NULL);
- mpz_set(expr->value, ei->left);
- expr = set_elem_expr_alloc(&internal_location, expr);
-
- if (ei->expr != NULL) {
- if (ei->expr->etype == EXPR_MAPPING) {
- interval_expr_copy(expr, ei->expr->left);
- expr = mapping_expr_alloc(&ei->expr->location, expr,
- expr_get(ei->expr->right));
- } else {
- interval_expr_copy(expr, ei->expr);
- }
- }
-
- if (ei->flags & EI_F_INTERVAL_END)
- expr->flags |= EXPR_F_INTERVAL_END;
- if (ei->flags & EI_F_INTERVAL_OPEN)
- expr->elem_flags |= NFTNL_SET_ELEM_F_INTERVAL_OPEN;
-
- compound_expr_add(set, expr);
-}
-
-int set_to_intervals(struct list_head *errs, struct set *set,
- struct expr *init, bool add, unsigned int debug_mask,
- bool merge, struct output_ctx *octx)
-{
- struct expr *catchall = NULL, *i, *in, *key;
- struct elementary_interval *ei, *next;
- struct seg_tree tree;
- LIST_HEAD(list);
-
- list_for_each_entry_safe(i, in, &init->expressions, list) {
- if (i->etype == EXPR_MAPPING)
- key = i->left->key;
- else if (i->etype == EXPR_SET_ELEM)
- key = i->key;
- else
- continue;
-
- if (key->etype == EXPR_SET_ELEM_CATCHALL) {
- init->size--;
- catchall = i;
- list_del(&i->list);
- break;
- }
- }
-
- seg_tree_init(&tree, set, init, debug_mask);
- if (set_to_segtree(errs, set, init, &tree, add, merge) < 0)
- return -1;
- segtree_linearize(&list, set, init, &tree, add, merge);
-
- init->size = 0;
- list_for_each_entry_safe(ei, next, &list, list) {
- if (segtree_debug(tree.debug_mask)) {
- pr_gmp_debug("list: [%.*Zx %.*Zx]\n",
- 2 * tree.keylen / BITS_PER_BYTE, ei->left,
- 2 * tree.keylen / BITS_PER_BYTE, ei->right);
- }
- set_insert_interval(init, &tree, ei);
- ei_destroy(ei);
- }
-
- if (segtree_debug(tree.debug_mask)) {
- expr_print(init, octx);
- pr_gmp_debug("\n");
- }
-
- if (catchall) {
- list_add_tail(&catchall->list, &init->expressions);
- init->size++;
- }
-
- return 0;
-}
-
static void set_elem_add(const struct set *set, struct expr *init, mpz_t value,
uint32_t flags, enum byteorder byteorder)
{
@@ -698,6 +69,7 @@ static void set_elem_add(const struct set *set, struct expr *init, mpz_t value,
struct expr *get_set_intervals(const struct set *set, const struct expr *init)
{
+ enum byteorder byteorder = get_key_byteorder(set->key);
struct expr *new_init;
mpz_t low, high;
struct expr *i;
@@ -711,7 +83,7 @@ struct expr *get_set_intervals(const struct set *set, const struct expr *init)
switch (i->key->etype) {
case EXPR_VALUE:
set_elem_add(set, new_init, i->key->value,
- i->flags, i->byteorder);
+ i->flags, byteorder);
break;
case EXPR_CONCAT:
compound_expr_add(new_init, expr_clone(i));
@@ -751,6 +123,11 @@ static struct expr *get_set_interval_find(const struct set *cache_set,
list_for_each_entry(i, &set->init->expressions, list) {
switch (i->key->etype) {
+ case EXPR_VALUE:
+ if (expr_basetype(i->key)->type != TYPE_STRING)
+ break;
+ /* string type, check if its a range (wildcard). */
+ /* fall-through */
case EXPR_PREFIX:
case EXPR_RANGE:
range_expr_value_low(val, i);
@@ -773,6 +150,62 @@ out:
return range;
}
+static struct expr *expr_value(struct expr *expr)
+{
+ switch (expr->etype) {
+ case EXPR_MAPPING:
+ return expr->left->key;
+ case EXPR_SET_ELEM:
+ return expr->key;
+ case EXPR_VALUE:
+ return expr;
+ default:
+ BUG("invalid expression type %s\n", expr_name(expr));
+ }
+}
+
+static struct expr *__expr_to_set_elem(struct expr *low, struct expr *expr)
+{
+ struct expr *elem = set_elem_expr_alloc(&low->location, expr);
+
+ if (low->etype == EXPR_MAPPING) {
+ interval_expr_copy(elem, low->left);
+
+ elem = mapping_expr_alloc(&low->location, elem,
+ expr_clone(low->right));
+ } else {
+ interval_expr_copy(elem, low);
+ }
+ elem->flags |= EXPR_F_KERNEL;
+
+ return elem;
+}
+
+static struct expr *expr_to_set_elem(struct expr *e)
+{
+ unsigned int len = div_round_up(e->len, BITS_PER_BYTE);
+ unsigned int str_len;
+ char data[len + 1];
+ struct expr *expr;
+
+ if (expr_basetype(expr_value(e))->type != TYPE_STRING)
+ return expr_clone(e);
+
+ mpz_export_data(data, expr_value(e)->value, BYTEORDER_BIG_ENDIAN, len);
+
+ str_len = strnlen(data, len);
+ if (str_len >= len || str_len == 0)
+ return expr_clone(e);
+
+ data[str_len] = '*';
+
+ expr = constant_expr_alloc(&e->location, e->dtype,
+ BYTEORDER_HOST_ENDIAN,
+ (str_len + 1) * BITS_PER_BYTE, data);
+
+ return __expr_to_set_elem(e, expr);
+}
+
int get_set_decompose(struct set *cache_set, struct set *set)
{
struct expr *i, *next, *range;
@@ -788,6 +221,8 @@ int get_set_decompose(struct set *cache_set, struct set *set)
mpz_sub_ui(i->key->value, i->key->value, 1);
range = get_set_interval_find(cache_set, left, i);
if (!range) {
+ expr_free(left);
+ expr_free(i);
expr_free(new_init);
errno = ENOENT;
return -1;
@@ -805,7 +240,7 @@ int get_set_decompose(struct set *cache_set, struct set *set)
compound_expr_add(new_init, range);
else
compound_expr_add(new_init,
- expr_clone(left));
+ expr_to_set_elem(left));
}
left = i;
}
@@ -815,7 +250,7 @@ int get_set_decompose(struct set *cache_set, struct set *set)
if (range)
compound_expr_add(new_init, range);
else
- compound_expr_add(new_init, expr_clone(left));
+ compound_expr_add(new_init, expr_to_set_elem(left));
}
expr_free(set->init);
@@ -837,18 +272,6 @@ static bool range_is_prefix(const mpz_t range)
return ret;
}
-static struct expr *expr_value(struct expr *expr)
-{
- switch (expr->etype) {
- case EXPR_MAPPING:
- return expr->left->key;
- case EXPR_SET_ELEM:
- return expr->key;
- default:
- BUG("invalid expression type %s\n", expr_name(expr));
- }
-}
-
static int expr_value_cmp(const void *p1, const void *p2)
{
struct expr *e1 = *(void * const *)p1;
@@ -923,6 +346,8 @@ void concat_range_aggregate(struct expr *set)
list_for_each_entry_safe(r1, r1_next,
&expr_value(start)->expressions,
list) {
+ bool string_type = false;
+
mpz_init(range);
mpz_init(p);
@@ -934,16 +359,48 @@ void concat_range_aggregate(struct expr *set)
goto next;
}
+ if (expr_basetype(r1)->type == TYPE_STRING &&
+ expr_basetype(r2)->type == TYPE_STRING) {
+ string_type = true;
+ mpz_switch_byteorder(r1->value, r1->len / BITS_PER_BYTE);
+ mpz_switch_byteorder(r2->value, r2->len / BITS_PER_BYTE);
+ }
+
mpz_sub(range, r2->value, r1->value);
mpz_sub_ui(range, range, 1);
mpz_and(p, r1->value, range);
/* Check if we are forced, or if it's anyway preferable,
- * to express the range as two points instead of a
- * netmask.
+ * to express the range as a wildcard string, or two points
+ * instead of a netmask.
*/
prefix_len = range_mask_len(r1->value, r2->value,
r1->len);
+ if (string_type) {
+ mpz_switch_byteorder(r1->value, r1->len / BITS_PER_BYTE);
+ mpz_switch_byteorder(r2->value, r2->len / BITS_PER_BYTE);
+ }
+
+ if (prefix_len >= 0 &&
+ (prefix_len % BITS_PER_BYTE) == 0 &&
+ string_type) {
+ unsigned int str_len = prefix_len / BITS_PER_BYTE;
+ char data[str_len + 2];
+
+ mpz_export_data(data, r1->value, BYTEORDER_HOST_ENDIAN, str_len);
+ data[str_len] = '*';
+
+ tmp = constant_expr_alloc(&r1->location, r1->dtype,
+ BYTEORDER_HOST_ENDIAN,
+ (str_len + 1) * BITS_PER_BYTE, data);
+ tmp->len = r2->len;
+ list_replace(&r2->list, &tmp->list);
+ r2_next = tmp->list.next;
+ expr_free(r2);
+ free_r1 = 1;
+ goto next;
+ }
+
if (prefix_len < 0 ||
!(r1->dtype->flags & DTYPE_F_PREFIX)) {
tmp = range_expr_alloc(&r1->location, r1,
@@ -978,12 +435,108 @@ next:
}
}
+static struct expr *interval_to_prefix(struct expr *low, struct expr *i, const mpz_t range)
+{
+ unsigned int prefix_len;
+ struct expr *prefix;
+
+ prefix_len = expr_value(i)->len - mpz_scan0(range, 0);
+ prefix = prefix_expr_alloc(&low->location,
+ expr_clone(expr_value(low)),
+ prefix_len);
+ prefix->len = expr_value(i)->len;
+
+ return __expr_to_set_elem(low, prefix);
+}
+
+static struct expr *interval_to_string(struct expr *low, struct expr *i, const mpz_t range)
+{
+ unsigned int len = div_round_up(i->len, BITS_PER_BYTE);
+ unsigned int prefix_len, str_len;
+ char data[len + 2];
+ struct expr *expr;
+
+ prefix_len = expr_value(i)->len - mpz_scan0(range, 0);
+
+ if (prefix_len > i->len || prefix_len % BITS_PER_BYTE)
+ return interval_to_prefix(low, i, range);
+
+ mpz_export_data(data, expr_value(low)->value, BYTEORDER_BIG_ENDIAN, len);
+
+ str_len = strnlen(data, len);
+ if (str_len >= len || str_len == 0)
+ return interval_to_prefix(low, i, range);
+
+ data[str_len] = '*';
+
+ expr = constant_expr_alloc(&low->location, low->dtype,
+ BYTEORDER_HOST_ENDIAN,
+ (str_len + 1) * BITS_PER_BYTE, data);
+
+ return __expr_to_set_elem(low, expr);
+}
+
+static struct expr *interval_to_range(struct expr *low, struct expr *i, mpz_t range)
+{
+ struct expr *tmp;
+
+ tmp = constant_expr_alloc(&low->location, low->dtype,
+ low->byteorder, expr_value(low)->len,
+ NULL);
+
+ mpz_add(range, range, expr_value(low)->value);
+ mpz_set(tmp->value, range);
+
+ tmp = range_expr_alloc(&low->location,
+ expr_clone(expr_value(low)),
+ tmp);
+
+ return __expr_to_set_elem(low, tmp);
+}
+
+static void
+add_interval(struct expr *set, struct expr *low, struct expr *i)
+{
+ struct expr *expr;
+ mpz_t range, p;
+
+ mpz_init(range);
+ mpz_init(p);
+
+ mpz_sub(range, expr_value(i)->value, expr_value(low)->value);
+ if (i->etype != EXPR_VALUE)
+ mpz_sub_ui(range, range, 1);
+
+ mpz_and(p, expr_value(low)->value, range);
+
+ if (!mpz_cmp_ui(range, 0)) {
+ if (expr_basetype(low)->type == TYPE_STRING)
+ mpz_switch_byteorder(expr_value(low)->value,
+ expr_value(low)->len / BITS_PER_BYTE);
+ low->flags |= EXPR_F_KERNEL;
+ expr = expr_get(low);
+ } else if (range_is_prefix(range) && !mpz_cmp_ui(p, 0)) {
+
+ if (i->dtype->flags & DTYPE_F_PREFIX)
+ expr = interval_to_prefix(low, i, range);
+ else if (expr_basetype(i)->type == TYPE_STRING)
+ expr = interval_to_string(low, i, range);
+ else
+ expr = interval_to_range(low, i, range);
+ } else
+ expr = interval_to_range(low, i, range);
+
+ compound_expr_add(set, expr);
+
+ mpz_clear(range);
+ mpz_clear(p);
+}
+
void interval_map_decompose(struct expr *set)
{
struct expr *i, *next, *low = NULL, *end, *catchall = NULL, *key;
struct expr **elements, **ranges;
unsigned int n, m, size;
- mpz_t range, p;
bool interval;
if (set->size == 0)
@@ -992,9 +545,6 @@ void interval_map_decompose(struct expr *set)
elements = xmalloc_array(set->size, sizeof(struct expr *));
ranges = xmalloc_array(set->size * 2, sizeof(struct expr *));
- mpz_init(range);
- mpz_init(p);
-
/* Sort elements */
n = 0;
list_for_each_entry_safe(i, next, &set->expressions, list) {
@@ -1053,63 +603,7 @@ void interval_map_decompose(struct expr *set)
}
}
- mpz_sub(range, expr_value(i)->value, expr_value(low)->value);
- mpz_sub_ui(range, range, 1);
-
- mpz_and(p, expr_value(low)->value, range);
-
- if (!mpz_cmp_ui(range, 0))
- compound_expr_add(set, expr_get(low));
- else if ((!range_is_prefix(range) ||
- !(i->dtype->flags & DTYPE_F_PREFIX)) ||
- mpz_cmp_ui(p, 0)) {
- struct expr *tmp;
-
- tmp = constant_expr_alloc(&low->location, low->dtype,
- low->byteorder, expr_value(low)->len,
- NULL);
-
- mpz_add(range, range, expr_value(low)->value);
- mpz_set(tmp->value, range);
-
- tmp = range_expr_alloc(&low->location,
- expr_clone(expr_value(low)),
- tmp);
- tmp = set_elem_expr_alloc(&low->location, tmp);
-
- if (low->etype == EXPR_MAPPING) {
- interval_expr_copy(tmp, low->left);
-
- tmp = mapping_expr_alloc(&tmp->location, tmp,
- expr_clone(low->right));
- } else {
- interval_expr_copy(tmp, low);
- }
-
- compound_expr_add(set, tmp);
- } else {
- struct expr *prefix;
- unsigned int prefix_len;
-
- prefix_len = expr_value(i)->len - mpz_scan0(range, 0);
- prefix = prefix_expr_alloc(&low->location,
- expr_clone(expr_value(low)),
- prefix_len);
- prefix->len = expr_value(i)->len;
-
- prefix = set_elem_expr_alloc(&low->location, prefix);
-
- if (low->etype == EXPR_MAPPING) {
- interval_expr_copy(prefix, low->left);
-
- prefix = mapping_expr_alloc(&low->location, prefix,
- expr_clone(low->right));
- } else {
- interval_expr_copy(prefix, low);
- }
-
- compound_expr_add(set, prefix);
- }
+ add_interval(set, low, i);
if (i->flags & EXPR_F_INTERVAL_END) {
expr_free(low);
@@ -1126,30 +620,18 @@ void interval_map_decompose(struct expr *set)
mpz_bitmask(i->value, i->len);
if (!mpz_cmp(i->value, expr_value(low)->value)) {
- expr_free(i);
- i = low;
+ compound_expr_add(set, low);
} else {
- i = range_expr_alloc(&low->location,
- expr_clone(expr_value(low)), i);
- i = set_elem_expr_alloc(&low->location, i);
- if (low->etype == EXPR_MAPPING) {
- i = mapping_expr_alloc(&i->location, i,
- expr_clone(low->right));
- interval_expr_copy(i->left, low->left);
- } else {
- interval_expr_copy(i, low);
- }
+ add_interval(set, low, i);
expr_free(low);
}
- compound_expr_add(set, i);
+ expr_free(i);
+
out:
if (catchall)
compound_expr_add(set, catchall);
- mpz_clear(range);
- mpz_clear(p);
-
- xfree(ranges);
- xfree(elements);
+ free(ranges);
+ free(elements);
}
diff --git a/src/socket.c b/src/socket.c
index eb075153..8a149e63 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -4,10 +4,12 @@
* Copyright (c) 2018 Máté Eckl <ecklm94@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <nftables.h>
#include <expression.h>
#include <socket.h>
diff --git a/src/statement.c b/src/statement.c
index 03c0acf6..ab144d63 100644
--- a/src/statement.c
+++ b/src/statement.c
@@ -8,12 +8,11 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
#include <inttypes.h>
-#include <string.h>
#include <syslog.h>
#include <rule.h>
@@ -23,6 +22,7 @@
#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>
#include <statement.h>
+#include <tcpopt.h>
#include <utils.h>
#include <list.h>
#include <xt.h>
@@ -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)
@@ -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 = {
@@ -248,6 +248,37 @@ struct stmt *counter_stmt_alloc(const struct location *loc)
return stmt;
}
+static void last_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
+{
+ nft_print(octx, "last");
+
+ if (nft_output_stateless(octx))
+ return;
+
+ nft_print(octx, " used ");
+
+ if (stmt->last.set)
+ time_print(stmt->last.used, octx);
+ else
+ nft_print(octx, "never");
+}
+
+static const struct stmt_ops last_stmt_ops = {
+ .type = STMT_LAST,
+ .name = "last",
+ .print = last_stmt_print,
+ .json = last_stmt_json,
+};
+
+struct stmt *last_stmt_alloc(const struct location *loc)
+{
+ struct stmt *stmt;
+
+ stmt = stmt_alloc(loc, &last_stmt_ops);
+ stmt->flags |= STMT_F_STATEFUL;
+ return stmt;
+}
+
static const char *objref_type[NFT_OBJECT_MAX + 1] = {
[NFT_OBJECT_COUNTER] = "counter",
[NFT_OBJECT_QUOTA] = "quota",
@@ -454,9 +485,7 @@ static void limit_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
nft_print(octx, "limit rate %s%" PRIu64 "/%s",
inv ? "over " : "", stmt->limit.rate,
get_unit(stmt->limit.unit));
- if (stmt->limit.burst && stmt->limit.burst != 5)
- nft_print(octx, " burst %u packets",
- stmt->limit.burst);
+ nft_print(octx, " burst %u packets", stmt->limit.burst);
break;
case NFT_LIMIT_PKT_BYTES:
data_unit = get_rate(stmt->limit.rate, &rate);
@@ -464,7 +493,7 @@ static void limit_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
nft_print(octx, "limit rate %s%" PRIu64 " %s/%s",
inv ? "over " : "", rate, data_unit,
get_unit(stmt->limit.unit));
- if (stmt->limit.burst != 5) {
+ if (stmt->limit.burst != 0) {
uint64_t burst;
data_unit = get_rate(stmt->limit.burst, &burst);
@@ -818,6 +847,7 @@ static const struct stmt_ops map_stmt_ops = {
.name = "map",
.print = map_stmt_print,
.destroy = map_stmt_destroy,
+ .json = map_stmt_json,
};
struct stmt *map_stmt_alloc(const struct location *loc)
@@ -909,6 +939,37 @@ struct stmt *fwd_stmt_alloc(const struct location *loc)
return stmt_alloc(loc, &fwd_stmt_ops);
}
+static void optstrip_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
+{
+ const struct expr *expr = stmt->optstrip.expr;
+
+ nft_print(octx, "reset ");
+ expr_print(expr, octx);
+}
+
+static void optstrip_stmt_destroy(struct stmt *stmt)
+{
+ expr_free(stmt->optstrip.expr);
+}
+
+static const struct stmt_ops optstrip_stmt_ops = {
+ .type = STMT_OPTSTRIP,
+ .name = "optstrip",
+ .print = optstrip_stmt_print,
+ .json = optstrip_stmt_json,
+ .destroy = optstrip_stmt_destroy,
+};
+
+struct stmt *optstrip_stmt_alloc(const struct location *loc, struct expr *e)
+{
+ struct stmt *stmt = stmt_alloc(loc, &optstrip_stmt_ops);
+
+ e->exthdr.flags |= NFT_EXTHDR_F_PRESENT;
+ stmt->optstrip.expr = e;
+
+ return stmt;
+}
+
static void tproxy_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
{
nft_print(octx, "tproxy");
@@ -928,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, ":");
@@ -965,6 +1026,7 @@ static const struct stmt_ops xt_stmt_ops = {
.name = "xt",
.print = xt_stmt_print,
.destroy = xt_stmt_destroy,
+ .json = xt_stmt_json,
};
struct stmt *xt_stmt_alloc(const struct location *loc)
diff --git a/src/tcpopt.c b/src/tcpopt.c
index 53fe9bc8..f977e417 100644
--- a/src/tcpopt.c
+++ b/src/tcpopt.c
@@ -1,8 +1,7 @@
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
@@ -91,6 +90,41 @@ static const struct exthdr_desc tcpopt_timestamp = {
},
};
+static const struct exthdr_desc tcpopt_fastopen = {
+ .name = "fastopen",
+ .type = TCPOPT_KIND_FASTOPEN,
+ .templates = {
+ [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_COMMON_LENGTH] = PHT("length", 8, 8),
+ },
+};
+
+static const struct exthdr_desc tcpopt_md5sig = {
+ .name = "md5sig",
+ .type = TCPOPT_KIND_MD5SIG,
+ .templates = {
+ [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_COMMON_LENGTH] = PHT("length", 8, 8),
+ },
+};
+
+
+static const struct exthdr_desc tcpopt_mptcp = {
+ .name = "mptcp",
+ .type = TCPOPT_KIND_MPTCP,
+ .templates = {
+ [TCPOPT_MPTCP_KIND] = PHT("kind", 0, 8),
+ [TCPOPT_MPTCP_LENGTH] = PHT("length", 8, 8),
+ [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[] = {
@@ -101,8 +135,22 @@ const struct exthdr_desc *tcpopt_protocols[] = {
[TCPOPT_KIND_SACK_PERMITTED] = &tcpopt_sack_permitted,
[TCPOPT_KIND_SACK] = &tcpopt_sack,
[TCPOPT_KIND_TIMESTAMP] = &tcpopt_timestamp,
+ [TCPOPT_KIND_MD5SIG] = &tcpopt_md5sig,
+ [TCPOPT_KIND_MPTCP] = &tcpopt_mptcp,
+ [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
*
@@ -152,32 +200,38 @@ 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;
}
tmpl = &desc->templates[field];
- if (!tmpl)
+ if (!tmpl || !tmpl->dtype)
return NULL;
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;
}
@@ -194,6 +248,7 @@ void tcpopt_init_raw(struct expr *expr, uint8_t type, unsigned int off,
expr->exthdr.flags = flags;
expr->exthdr.offset = off;
expr->exthdr.op = NFT_EXTHDR_OP_TCPOPT;
+ expr->exthdr.tmpl = &tcpopt_unknown_template;
if (flags & NFT_EXTHDR_F_PRESENT)
datatype_set(expr, &boolean_type);
@@ -221,14 +276,12 @@ void tcpopt_init_raw(struct expr *expr, uint8_t type, unsigned int off,
}
}
-bool tcpopt_find_template(struct expr *expr, const struct expr *mask,
- unsigned int *shift)
+bool tcpopt_find_template(struct expr *expr, unsigned int offset, unsigned int len)
{
if (expr->exthdr.tmpl != &tcpopt_unknown_template)
return false;
- tcpopt_init_raw(expr, expr->exthdr.desc->type, expr->exthdr.offset,
- expr->len, 0);
+ tcpopt_init_raw(expr, expr->exthdr.desc->type, offset, len, 0);
if (expr->exthdr.tmpl == &tcpopt_unknown_template)
return false;
diff --git a/src/utils.c b/src/utils.c
index 925841c5..2aa1eb4e 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -8,12 +8,12 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <nft.h>
+
#include <stddef.h>
-#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
-#include <string.h>
#include <nftables.h>
#include <utils.h>
@@ -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;
@@ -100,3 +95,8 @@ void xstrunescape(const char *in, char *out)
}
out[k++] = '\0';
}
+
+int round_pow_2(unsigned int n)
+{
+ return 1UL << fls(n - 1);
+}
diff --git a/src/xfrm.c b/src/xfrm.c
index 80f0ea03..b32b2a1d 100644
--- a/src/xfrm.c
+++ b/src/xfrm.c
@@ -1,11 +1,15 @@
/*
* XFRM (ipsec) expression
*
+ * Copyright (c) Red Hat GmbH. Author: Florian Westphal <fw@strlen.de>
+ *
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <nftables.h>
#include <erec.h>
#include <expression.h>
@@ -13,7 +17,6 @@
#include <datatype.h>
#include <gmputil.h>
#include <utils.h>
-#include <string.h>
#include <netinet/ip.h>
#include <linux/netfilter.h>
diff --git a/src/xt.c b/src/xt.c
index 789de992..f7bee216 100644
--- a/src/xt.c
+++ b/src/xt.c
@@ -2,14 +2,14 @@
* Copyright (c) 2013-2015 Pablo Neira Ayuso <pablo@netfilter.org>
* Copyright (c) 2015 Arturo Borrero Gonzalez <arturo@debian.org>
*
- * This program is free software; you can redistribute it and/or modifyi
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * 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 <stdlib.h>
+#include <nft.h>
+
#include <time.h>
-#include <string.h>
#include <net/if.h>
#include <getopt.h>
#include <ctype.h> /* for isspace */
@@ -28,85 +28,108 @@
#ifdef HAVE_LIBXTABLES
#include <xtables.h>
+
+static void *xt_entry_alloc(const struct xt_stmt *xt, uint32_t af);
#endif
void xt_stmt_xlate(const struct stmt *stmt, struct output_ctx *octx)
{
+ static const char *typename[NFT_XT_MAX] = {
+ [NFT_XT_MATCH] = "match",
+ [NFT_XT_TARGET] = "target",
+ [NFT_XT_WATCHER] = "watcher",
+ };
+ int rc = 0;
#ifdef HAVE_LIBXTABLES
struct xt_xlate *xl = xt_xlate_alloc(10240);
+ struct xtables_target *tg;
+ struct xt_entry_target *t;
+ struct xtables_match *mt;
+ struct xt_entry_match *m;
+ size_t size;
+ void *entry;
+
+ xtables_set_nfproto(stmt->xt.family);
+ entry = xt_entry_alloc(&stmt->xt, stmt->xt.family);
switch (stmt->xt.type) {
case NFT_XT_MATCH:
- if (stmt->xt.match->xlate) {
+ mt = xtables_find_match(stmt->xt.name, XTF_TRY_LOAD, NULL);
+ if (!mt) {
+ fprintf(octx->error_fp,
+ "# Warning: XT match %s not found\n",
+ stmt->xt.name);
+ break;
+ }
+ size = XT_ALIGN(sizeof(*m)) + stmt->xt.infolen;
+
+ m = xzalloc(size);
+ memcpy(&m->data, stmt->xt.info, stmt->xt.infolen);
+
+ m->u.match_size = size;
+ m->u.user.revision = stmt->xt.rev;
+
+ if (mt->xlate) {
struct xt_xlate_mt_params params = {
- .ip = stmt->xt.entry,
- .match = stmt->xt.match->m,
- .numeric = 0,
+ .ip = entry,
+ .match = m,
+ .numeric = 1,
};
- stmt->xt.match->xlate(xl, &params);
- nft_print(octx, "%s", xt_xlate_get(xl));
- } else if (stmt->xt.match->print) {
- printf("#");
- stmt->xt.match->print(&stmt->xt.entry,
- stmt->xt.match->m, 0);
+ rc = mt->xlate(xl, &params);
}
+ free(m);
break;
case NFT_XT_WATCHER:
case NFT_XT_TARGET:
- if (stmt->xt.target->xlate) {
+ tg = xtables_find_target(stmt->xt.name, XTF_TRY_LOAD);
+ if (!tg) {
+ fprintf(octx->error_fp,
+ "# Warning: XT target %s not found\n",
+ stmt->xt.name);
+ break;
+ }
+ size = XT_ALIGN(sizeof(*t)) + stmt->xt.infolen;
+
+ t = xzalloc(size);
+ memcpy(&t->data, stmt->xt.info, stmt->xt.infolen);
+
+ t->u.target_size = size;
+ t->u.user.revision = stmt->xt.rev;
+
+ strcpy(t->u.user.name, tg->name);
+
+ if (tg->xlate) {
struct xt_xlate_tg_params params = {
- .ip = stmt->xt.entry,
- .target = stmt->xt.target->t,
- .numeric = 0,
+ .ip = entry,
+ .target = t,
+ .numeric = 1,
};
- stmt->xt.target->xlate(xl, &params);
- nft_print(octx, "%s", xt_xlate_get(xl));
- } else if (stmt->xt.target->print) {
- printf("#");
- stmt->xt.target->print(NULL, stmt->xt.target->t, 0);
+ rc = tg->xlate(xl, &params);
}
- break;
- default:
+ free(t);
break;
}
+ if (rc == 1)
+ nft_print(octx, "%s", xt_xlate_get(xl));
xt_xlate_free(xl);
-#else
- nft_print(octx, "# xt_%s", stmt->xt.name);
+ free(entry);
#endif
+ if (!rc)
+ nft_print(octx, "xt %s \"%s\"",
+ typename[stmt->xt.type], stmt->xt.name);
}
void xt_stmt_destroy(struct stmt *stmt)
{
-#ifdef HAVE_LIBXTABLES
- switch (stmt->xt.type) {
- case NFT_XT_MATCH:
- if (!stmt->xt.match)
- break;
- if (stmt->xt.match->m)
- xfree(stmt->xt.match->m);
- xfree(stmt->xt.match);
- break;
- case NFT_XT_WATCHER:
- case NFT_XT_TARGET:
- if (!stmt->xt.target)
- break;
- if (stmt->xt.target->t)
- xfree(stmt->xt.target->t);
- xfree(stmt->xt.target);
- break;
- default:
- break;
- }
-#endif
- xfree(stmt->xt.entry);
- xfree(stmt->xt.name);
+ free_const(stmt->xt.name);
+ free(stmt->xt.info);
}
#ifdef HAVE_LIBXTABLES
-static void *xt_entry_alloc(struct xt_stmt *xt, uint32_t af)
+static void *xt_entry_alloc(const struct xt_stmt *xt, uint32_t af)
{
union nft_entry {
struct ipt_entry ipt;
@@ -173,24 +196,6 @@ static uint32_t xt_proto(const struct proto_ctx *pctx)
return 0;
}
-
-static struct xtables_target *xt_target_clone(struct xtables_target *t)
-{
- struct xtables_target *clone;
-
- clone = xzalloc(sizeof(struct xtables_target));
- memcpy(clone, t, sizeof(struct xtables_target));
- return clone;
-}
-
-static struct xtables_match *xt_match_clone(struct xtables_match *m)
-{
- struct xtables_match *clone;
-
- clone = xzalloc(sizeof(struct xtables_match));
- memcpy(clone, m, sizeof(struct xtables_match));
- return clone;
-}
#endif
/*
@@ -201,43 +206,23 @@ void netlink_parse_match(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
{
- struct stmt *stmt;
- const char *name;
-#ifdef HAVE_LIBXTABLES
- struct xtables_match *mt;
const char *mtinfo;
- struct xt_entry_match *m;
+ struct stmt *stmt;
uint32_t mt_len;
- xtables_set_nfproto(ctx->table->handle.family);
-
- name = nftnl_expr_get_str(nle, NFTNL_EXPR_MT_NAME);
-
- mt = xtables_find_match(name, XTF_TRY_LOAD, NULL);
- if (!mt) {
- fprintf(stderr, "XT match %s not found\n", name);
- return;
- }
mtinfo = nftnl_expr_get(nle, NFTNL_EXPR_MT_INFO, &mt_len);
- m = xzalloc(sizeof(struct xt_entry_match) + mt_len);
- memcpy(&m->data, mtinfo, mt_len);
-
- m->u.match_size = mt_len + XT_ALIGN(sizeof(struct xt_entry_match));
- m->u.user.revision = nftnl_expr_get_u32(nle, NFTNL_EXPR_MT_REV);
-
stmt = xt_stmt_alloc(loc);
- stmt->xt.name = strdup(name);
+ stmt->xt.name = strdup(nftnl_expr_get_str(nle, NFTNL_EXPR_MT_NAME));
stmt->xt.type = NFT_XT_MATCH;
- stmt->xt.match = xt_match_clone(mt);
- stmt->xt.match->m = m;
-#else
- name = nftnl_expr_get_str(nle, NFTNL_EXPR_MT_NAME);
+ stmt->xt.rev = nftnl_expr_get_u32(nle, NFTNL_EXPR_MT_REV);
+ stmt->xt.family = ctx->table->handle.family;
- stmt = xt_stmt_alloc(loc);
- stmt->xt.name = strdup(name);
- stmt->xt.type = NFT_XT_MATCH;
-#endif
+ stmt->xt.infolen = mt_len;
+ stmt->xt.info = xmalloc(mt_len);
+ memcpy(stmt->xt.info, mtinfo, mt_len);
+
+ ctx->table->has_xt_stmts = true;
rule_stmt_append(ctx->rule, stmt);
}
@@ -245,44 +230,23 @@ void netlink_parse_target(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
{
- struct stmt *stmt;
- const char *name;
-#ifdef HAVE_LIBXTABLES
- struct xtables_target *tg;
const void *tginfo;
- struct xt_entry_target *t;
- size_t size;
+ struct stmt *stmt;
uint32_t tg_len;
- xtables_set_nfproto(ctx->table->handle.family);
-
- name = nftnl_expr_get_str(nle, NFTNL_EXPR_TG_NAME);
- tg = xtables_find_target(name, XTF_TRY_LOAD);
- if (!tg) {
- fprintf(stderr, "XT target %s not found\n", name);
- return;
- }
tginfo = nftnl_expr_get(nle, NFTNL_EXPR_TG_INFO, &tg_len);
- size = XT_ALIGN(sizeof(struct xt_entry_target)) + tg_len;
- t = xzalloc(size);
- memcpy(&t->data, tginfo, tg_len);
- t->u.target_size = size;
- t->u.user.revision = nftnl_expr_get_u32(nle, NFTNL_EXPR_TG_REV);
- strcpy(t->u.user.name, tg->name);
-
stmt = xt_stmt_alloc(loc);
- stmt->xt.name = strdup(name);
+ stmt->xt.name = strdup(nftnl_expr_get_str(nle, NFTNL_EXPR_TG_NAME));
stmt->xt.type = NFT_XT_TARGET;
- stmt->xt.target = xt_target_clone(tg);
- stmt->xt.target->t = t;
-#else
- name = nftnl_expr_get_str(nle, NFTNL_EXPR_TG_NAME);
+ stmt->xt.rev = nftnl_expr_get_u32(nle, NFTNL_EXPR_TG_REV);
+ stmt->xt.family = ctx->table->handle.family;
- stmt = xt_stmt_alloc(loc);
- stmt->xt.name = strdup(name);
- stmt->xt.type = NFT_XT_TARGET;
-#endif
+ stmt->xt.infolen = tg_len;
+ stmt->xt.info = xmalloc(tg_len);
+ memcpy(stmt->xt.info, tginfo, tg_len);
+
+ ctx->table->has_xt_stmts = true;
rule_stmt_append(ctx->rule, stmt);
}
@@ -305,11 +269,12 @@ static bool is_watcher(uint32_t family, struct stmt *stmt)
void stmt_xt_postprocess(struct rule_pp_ctx *rctx, struct stmt *stmt,
struct rule *rule)
{
- if (is_watcher(rctx->pctx.family, stmt))
+ struct dl_proto_ctx *dl = dl_proto_ctx(rctx);
+
+ if (is_watcher(dl->pctx.family, stmt))
stmt->xt.type = NFT_XT_WATCHER;
- stmt->xt.proto = xt_proto(&rctx->pctx);
- stmt->xt.entry = xt_entry_alloc(&stmt->xt, rctx->pctx.family);
+ stmt->xt.proto = xt_proto(&dl->pctx);
}
static int nft_xt_compatible_revision(const char *name, uint8_t rev, int opt)
@@ -383,7 +348,7 @@ err:
}
static struct option original_opts[] = {
- { NULL },
+ { },
};
static struct xtables_globals xt_nft_globals = {
@@ -395,7 +360,18 @@ static struct xtables_globals xt_nft_globals = {
void xt_init(void)
{
- /* Default to IPv4, but this changes in runtime */
- xtables_init_all(&xt_nft_globals, NFPROTO_IPV4);
+ static bool init_once;
+
+ if (!init_once) {
+ /* libxtables is full of global variables and cannot be used
+ * concurrently by multiple threads. Hence, it's fine that the
+ * "init_once" guard is not thread-safe either.
+ * Don't link against xtables if you want thread safety.
+ */
+ init_once = true;
+
+ /* Default to IPv4, but this changes in runtime */
+ xtables_init_all(&xt_nft_globals, NFPROTO_IPV4);
+ }
}
#endif
diff --git a/tests/build/run-tests.sh b/tests/build/run-tests.sh
index 9ce93a8e..916df2e2 100755
--- a/tests/build/run-tests.sh
+++ b/tests/build/run-tests.sh
@@ -1,32 +1,36 @@
#!/bin/bash
-log_file="`pwd`/tests.log"
+log_file="$(pwd)/tests.log"
dir=../..
argument=( --without-cli --with-cli=linenoise --with-cli=editline --enable-debug --with-mini-gmp
--enable-man-doc --with-xtables --with-json)
ok=0
failed=0
-[ -f $log_file ] && rm -rf $log_file
+[ -f "$log_file" ] && rm -rf "$log_file"
tmpdir=$(mktemp -d)
-if [ ! -w $tmpdir ] ; then
+if [ ! -w "$tmpdir" ] ; then
echo "Failed to create tmp file" >&2
exit 0
fi
-git clone $dir $tmpdir >/dev/null 2>>$log_file
-cd $tmpdir
+git clone "$dir" "$tmpdir" &>>"$log_file"
+cd "$tmpdir" || exit
-autoreconf -fi >/dev/null 2>>$log_file
-./configure >/dev/null 2>>$log_file
+if ! autoreconf -fi &>>"$log_file" ; then
+ echo "Something went wrong. Check the log '${log_file}' for details."
+ exit 1
+fi
-echo "Testing build with distcheck"
-make distcheck >/dev/null 2>>$log_file
-rt=$?
+if ! ./configure &>>"$log_file" ; then
+ echo "Something went wrong. Check the log '${log_file}' for details."
+ exit 1
+fi
-if [ $rt != 0 ] ; then
- echo "Something went wrong. Check the log for details."
+echo "Testing build with distcheck"
+if ! make distcheck &>>"$log_file" ; then
+ echo "Something went wrong. Check the log '${log_file}' for details."
exit 1
fi
@@ -35,8 +39,8 @@ echo "Build works. Now, testing compile options"
for var in "${argument[@]}" ; do
echo "[EXECUTING] Testing compile option $var"
- ./configure $var >/dev/null 2>>$log_file
- make -j 8 >/dev/null 2>>$log_file
+ ./configure "$var" &>>"$log_file"
+ make -j 8 &>>"$log_file"
rt=$?
echo -en "\033[1A\033[K" # clean the [EXECUTING] foobar line
@@ -49,7 +53,7 @@ for var in "${argument[@]}" ; do
fi
done
-rm -rf $tmpdir
+rm -rf "$tmpdir"
echo "results: [OK] $ok [FAILED] $failed [TOTAL] $((ok+failed))"
-exit $failed
+[ "$failed" -eq 0 ]
diff --git a/tests/monitor/run-tests.sh b/tests/monitor/run-tests.sh
index ff00450b..f1ac790a 100755
--- a/tests/monitor/run-tests.sh
+++ b/tests/monitor/run-tests.sh
@@ -74,7 +74,7 @@ monitor_run_test() {
echo "command file:"
cat $command_file
}
- $nft -f $command_file || {
+ $nft -f - <$command_file || {
err "nft command failed!"
rc=1
}
@@ -103,7 +103,7 @@ echo_run_test() {
echo "command file:"
cat $command_file
}
- $nft -nn -e -f $command_file >$echo_output || {
+ $nft -nn -e -f - <$command_file >$echo_output || {
err "nft command failed!"
rc=1
}
@@ -161,7 +161,10 @@ for variant in $variants; do
output_append=${variant}_output_append
for testcase in ${testcases:-testcases/*.t}; do
- echo "$variant: running tests from file $(basename $testcase)"
+ filename=$(basename $testcase)
+ echo "$variant: running tests from file $filename"
+ rc_start=$rc
+
# files are like this:
#
# I add table ip t
@@ -199,6 +202,10 @@ for variant in $variants; do
$run_test
let "rc += $?"
}
+
+ let "rc_diff = rc - rc_start"
+ [[ $rc_diff -ne 0 ]] && \
+ echo "$variant: $rc_diff tests from file $filename failed"
done
done
exit $rc
diff --git a/tests/monitor/testcases/map-expr.t b/tests/monitor/testcases/map-expr.t
new file mode 100644
index 00000000..8729c0b4
--- /dev/null
+++ b/tests/monitor/testcases/map-expr.t
@@ -0,0 +1,6 @@
+# first the setup
+I add table ip t
+I add map ip t m { typeof meta day . meta hour : verdict; flags interval; counter; }
+O -
+J {"add": {"table": {"family": "ip", "name": "t", "handle": 0}}}
+J {"add": {"map": {"family": "ip", "name": "m", "table": "t", "type": ["day", "hour"], "handle": 0, "map": "verdict", "flags": ["interval"], "stmt": [{"counter": null}]}}}
diff --git a/tests/monitor/testcases/object.t b/tests/monitor/testcases/object.t
index 2afe33c8..53a9f8c5 100644
--- a/tests/monitor/testcases/object.t
+++ b/tests/monitor/testcases/object.t
@@ -37,7 +37,7 @@ I delete ct helper ip t cth
O -
J {"delete": {"ct helper": {"family": "ip", "name": "cth", "table": "t", "handle": 0, "type": "sip", "protocol": "tcp", "l3proto": "ip"}}}
-I add ct timeout ip t ctt { protocol udp; l3proto ip; policy = { unreplied : 15, replied : 12 }; }
+I add ct timeout ip t ctt { protocol udp; l3proto ip; policy = { unreplied : 15s, replied : 12s }; }
O -
J {"add": {"ct timeout": {"family": "ip", "name": "ctt", "table": "t", "handle": 0, "protocol": "udp", "l3proto": "ip", "policy": {"unreplied": 15, "replied": 12}}}}
diff --git a/tests/monitor/testcases/set-concat-interval.t b/tests/monitor/testcases/set-concat-interval.t
new file mode 100644
index 00000000..763dc319
--- /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 }
diff --git a/tests/monitor/testcases/set-interval.t b/tests/monitor/testcases/set-interval.t
index 1fbcfe22..5053c596 100644
--- a/tests/monitor/testcases/set-interval.t
+++ b/tests/monitor/testcases/set-interval.t
@@ -23,3 +23,8 @@ J {"add": {"rule": {"family": "ip", "table": "t", "chain": "c", "handle": 0, "ex
I add rule ip t c tcp dport { 20, 30-40 }
O -
J {"add": {"rule": {"family": "ip", "table": "t", "chain": "c", "handle": 0, "expr": [{"match": {"op": "==", "left": {"payload": {"protocol": "tcp", "field": "dport"}}, "right": {"set": [20, {"range": [30, 40]}]}}}]}}}
+
+# ... and anon concat range
+I add rule ip t c ether saddr . ip saddr { 08:00:27:40:f7:09 . 192.168.56.10-192.168.56.12 }
+O -
+J {"add": {"rule": {"family": "ip", "table": "t", "chain": "c", "handle": 0, "expr": [{"match": {"op": "==", "left": {"concat": [{"payload": {"protocol": "ether", "field": "saddr"}}, {"payload": {"protocol": "ip", "field": "saddr"}}]}, "right": {"set": [{"concat": ["08:00:27:40:f7:09", {"range": ["192.168.56.10", "192.168.56.12"]}]}]}}}]}}}
diff --git a/tests/monitor/testcases/simple.t b/tests/monitor/testcases/simple.t
index 2d9c92de..67be5c85 100644
--- a/tests/monitor/testcases/simple.t
+++ b/tests/monitor/testcases/simple.t
@@ -15,13 +15,13 @@ J {"add": {"rule": {"family": "ip", "table": "t", "chain": "c", "handle": 0, "ex
I insert rule ip t c counter accept
O insert rule ip t c counter packets 0 bytes 0 accept
-J {"add": {"rule": {"family": "ip", "table": "t", "chain": "c", "handle": 0, "expr": [{"counter": {"packets": 0, "bytes": 0}}, {"accept": null}]}}}
+J {"insert": {"rule": {"family": "ip", "table": "t", "chain": "c", "handle": 0, "expr": [{"counter": {"packets": 0, "bytes": 0}}, {"accept": null}]}}}
I replace rule ip t c handle 2 accept comment "foo bar"
O delete rule ip t c handle 2
O add rule ip t c handle 5 accept comment "foo bar"
-J {"add": {"rule": {"family": "ip", "table": "t", "chain": "c", "handle": 0, "comment": "foo bar", "expr": [{"accept": null}]}}}
J {"delete": {"rule": {"family": "ip", "table": "t", "chain": "c", "handle": 0, "expr": [{"accept": null}]}}}
+J {"add": {"rule": {"family": "ip", "table": "t", "chain": "c", "handle": 0, "comment": "foo bar", "expr": [{"accept": null}]}}}
I add counter ip t cnt
O add counter ip t cnt { packets 0 bytes 0 }
diff --git a/tests/py/README b/tests/py/README
index ed5dc58b..864a966e 100644
--- a/tests/py/README
+++ b/tests/py/README
@@ -163,4 +163,35 @@ G) Acknowledgements
Thanks to the Outreach Program for Women (OPW) for sponsoring this test
infrastructure and my mentor Pablo Neira.
+H) JSON (-j) Mode
+
+This mode is supposed to repeat the same tests using JSON syntax. For each test
+file example.t, there is supposed to be a file example.t.json holding the JSON
+equivalents of each rule in example.t. The file's syntax is similar to payload
+files: An initial comment identifies the rule belonging to the following JSON
+equivalent. Pairs of comment and JSON are separated by a single blank line.
+
+If the example.t.json file does not exist, the test script will warn and create
+(or append to) example.t.json.got. The JSON equivalent written is generated by
+applying the rule in standard syntax and listing the ruleset in JSON format.
+After thorough review, it may be renamed to example.t.json.
+
+One common case for editing the content in example.t.json.got is expected
+differences between input and output. The generated content will match the
+output while it is supposed to match the input.
+
+If a rule is expected to differ in output, the expected output must be recorded
+in example.t.json.output. Its syntax is identical to example.t.json, i.e. pairs
+of comment identifying the rule (in standard syntax) and JSON (output) format
+separated by blank lines. Note: the comment states the rule as in input, not
+output.
+
+If the example.t.json.output file does not exist and output differs from input,
+the file example.t.json.output.got is created with the actual output recorded.
+
+JSON mode will also check the payload created for the rule in JSON syntax by
+comparing it to the recorded one in example.t.payload. Should it differ, it
+will be recorded in example.t.json.payload.got. This is always a bug: A rule's
+JSON equivalent must turn into the same bytecode as the rule itself.
+
-EOF-
diff --git a/tests/py/any/ct.t b/tests/py/any/ct.t
index 8b8e68ab..f73fa4e7 100644
--- a/tests/py/any/ct.t
+++ b/tests/py/any/ct.t
@@ -144,3 +144,6 @@ ct set invalid original 42;fail
ct set invalid 42;fail
notrack;ok
+
+ct count 3;ok
+ct count over 3;ok
diff --git a/tests/py/any/ct.t.json b/tests/py/any/ct.t.json
index 6684963b..a2a06025 100644
--- a/tests/py/any/ct.t.json
+++ b/tests/py/any/ct.t.json
@@ -1502,3 +1502,22 @@
}
]
+# ct count 3
+[
+ {
+ "ct count": {
+ "val": 3
+ }
+ }
+]
+
+# ct count over 3
+[
+ {
+ "ct count": {
+ "inv": true,
+ "val": 3
+ }
+ }
+]
+
diff --git a/tests/py/any/ct.t.payload b/tests/py/any/ct.t.payload
index 733276e1..ed868e53 100644
--- a/tests/py/any/ct.t.payload
+++ b/tests/py/any/ct.t.payload
@@ -508,3 +508,11 @@ ip6
[ bitwise reg 1 = ( reg 1 & 0x00000020 ) ^ 0x00000000 ]
[ cmp eq reg 1 0x00000000 ]
+# ct count 3
+ip test-ip4 output
+ [ connlimit count 3 flags 0 ]
+
+# ct count over 3
+ip test-ip4 output
+ [ connlimit count 3 flags 1 ]
+
diff --git a/tests/py/any/icmpX.t.netdev b/tests/py/any/icmpX.t.netdev
index a327ce6a..cf402428 100644
--- a/tests/py/any/icmpX.t.netdev
+++ b/tests/py/any/icmpX.t.netdev
@@ -1,6 +1,7 @@
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
ip protocol icmp icmp type echo-request;ok;icmp type echo-request
icmp type echo-request;ok
diff --git a/tests/py/any/last.t b/tests/py/any/last.t
new file mode 100644
index 00000000..5c530461
--- /dev/null
+++ b/tests/py/any/last.t
@@ -0,0 +1,13 @@
+:input;type filter hook input priority 0
+:ingress;type filter hook ingress device lo priority 0
+
+*ip;test-ip4;input
+*ip6;test-ip6;input
+*inet;test-inet;input
+*arp;test-arp;input
+*bridge;test-bridge;input
+*netdev;test-netdev;ingress
+
+last;ok
+last used 300s;ok;last
+last used foo;fail
diff --git a/tests/py/any/last.t.json b/tests/py/any/last.t.json
new file mode 100644
index 00000000..2a2b9e72
--- /dev/null
+++ b/tests/py/any/last.t.json
@@ -0,0 +1,16 @@
+# last
+[
+ {
+ "last": null
+ }
+]
+
+# last used 300s
+[
+ {
+ "last": {
+ "used": 300000
+ }
+ }
+]
+
diff --git a/tests/py/any/last.t.json.output b/tests/py/any/last.t.json.output
new file mode 100644
index 00000000..e8ec4f47
--- /dev/null
+++ b/tests/py/any/last.t.json.output
@@ -0,0 +1,7 @@
+# last used 300s
+[
+ {
+ "last": null
+ }
+]
+
diff --git a/tests/py/any/last.t.payload b/tests/py/any/last.t.payload
new file mode 100644
index 00000000..ed47d0f3
--- /dev/null
+++ b/tests/py/any/last.t.payload
@@ -0,0 +1,8 @@
+# last
+ip
+ [ last never ]
+
+# last used 300s
+ip
+ [ last 300000 ]
+
diff --git a/tests/py/any/limit.t b/tests/py/any/limit.t
index ef7f9313..2a84e3f5 100644
--- a/tests/py/any/limit.t
+++ b/tests/py/any/limit.t
@@ -1,18 +1,19 @@
:output;type filter hook output priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*ip;test-ip4;output
*ip6;test-ip6;output
*inet;test-inet;output
*arp;test-arp;output
*bridge;test-bridge;output
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
-limit rate 400/minute;ok
-limit rate 20/second;ok
-limit rate 400/hour;ok
-limit rate 40/day;ok
-limit rate 400/week;ok
+limit rate 400/minute;ok;limit rate 400/minute burst 5 packets
+limit rate 20/second;ok;limit rate 20/second burst 5 packets
+limit rate 400/hour;ok;limit rate 400/hour burst 5 packets
+limit rate 40/day;ok;limit rate 40/day burst 5 packets
+limit rate 400/week;ok;limit rate 400/week burst 5 packets
limit rate 1023/second burst 10 packets;ok
limit rate 1023/second burst 10 bytes;fail
@@ -21,19 +22,22 @@ limit rate 2 kbytes/second;ok
limit rate 1025 kbytes/second;ok
limit rate 1023 mbytes/second;ok
limit rate 10230 mbytes/second;ok
-limit rate 1023000 mbytes/second;ok
limit rate 512 kbytes/second burst 5 packets;fail
+limit rate 1 bytes / second;ok;limit rate 1 bytes/second
+limit rate 1 kbytes / second;ok;limit rate 1 kbytes/second
+limit rate 1 mbytes / second;ok;limit rate 1 mbytes/second
+limit rate 1 gbytes / second;fail
+
limit rate 1025 bytes/second burst 512 bytes;ok
limit rate 1025 kbytes/second burst 1023 kbytes;ok
limit rate 1025 mbytes/second burst 1025 kbytes;ok
-limit rate 1025000 mbytes/second burst 1023 mbytes;ok
-limit rate over 400/minute;ok
-limit rate over 20/second;ok
-limit rate over 400/hour;ok
-limit rate over 40/day;ok
-limit rate over 400/week;ok
+limit rate over 400/minute;ok;limit rate over 400/minute burst 5 packets
+limit rate over 20/second;ok;limit rate over 20/second burst 5 packets
+limit rate over 400/hour;ok;limit rate over 400/hour burst 5 packets
+limit rate over 40/day;ok;limit rate over 40/day burst 5 packets
+limit rate over 400/week;ok;limit rate over 400/week burst 5 packets
limit rate over 1023/second burst 10 packets;ok
limit rate over 1 kbytes/second;ok
@@ -41,9 +45,7 @@ limit rate over 2 kbytes/second;ok
limit rate over 1025 kbytes/second;ok
limit rate over 1023 mbytes/second;ok
limit rate over 10230 mbytes/second;ok
-limit rate over 1023000 mbytes/second;ok
limit rate over 1025 bytes/second burst 512 bytes;ok
limit rate over 1025 kbytes/second burst 1023 kbytes;ok
limit rate over 1025 mbytes/second burst 1025 kbytes;ok
-limit rate over 1025000 mbytes/second burst 1023 mbytes;ok
diff --git a/tests/py/any/limit.t.json b/tests/py/any/limit.t.json
index 8bab7e3d..73160b27 100644
--- a/tests/py/any/limit.t.json
+++ b/tests/py/any/limit.t.json
@@ -114,12 +114,40 @@
}
]
-# limit rate 1023000 mbytes/second
+# limit rate 1 bytes / second
[
{
"limit": {
+ "burst": 0,
+ "burst_unit": "bytes",
+ "per": "second",
+ "rate": 1,
+ "rate_unit": "bytes"
+ }
+ }
+]
+
+# limit rate 1 kbytes / second
+[
+ {
+ "limit": {
+ "burst": 0,
+ "burst_unit": "bytes",
+ "per": "second",
+ "rate": 1,
+ "rate_unit": "kbytes"
+ }
+ }
+]
+
+# limit rate 1 mbytes / second
+[
+ {
+ "limit": {
+ "burst": 0,
+ "burst_unit": "bytes",
"per": "second",
- "rate": 1023000,
+ "rate": 1,
"rate_unit": "mbytes"
}
}
@@ -164,19 +192,6 @@
}
]
-# limit rate 1025000 mbytes/second burst 1023 mbytes
-[
- {
- "limit": {
- "burst": 1023,
- "burst_unit": "mbytes",
- "per": "second",
- "rate": 1025000,
- "rate_unit": "mbytes"
- }
- }
-]
-
# limit rate over 400/minute
[
{
@@ -304,18 +319,6 @@
}
]
-# limit rate over 1023000 mbytes/second
-[
- {
- "limit": {
- "inv": true,
- "per": "second",
- "rate": 1023000,
- "rate_unit": "mbytes"
- }
- }
-]
-
# limit rate over 1025 bytes/second burst 512 bytes
[
{
@@ -357,18 +360,3 @@
}
}
]
-
-# limit rate over 1025000 mbytes/second burst 1023 mbytes
-[
- {
- "limit": {
- "burst": 1023,
- "burst_unit": "mbytes",
- "inv": true,
- "per": "second",
- "rate": 1025000,
- "rate_unit": "mbytes"
- }
- }
-]
-
diff --git a/tests/py/any/limit.t.json.output b/tests/py/any/limit.t.json.output
index e6f26496..2c94d2de 100644
--- a/tests/py/any/limit.t.json.output
+++ b/tests/py/any/limit.t.json.output
@@ -57,7 +57,7 @@
[
{
"limit": {
- "burst": 5,
+ "burst": 0,
"burst_unit": "bytes",
"per": "second",
"rate": 1,
@@ -70,7 +70,7 @@
[
{
"limit": {
- "burst": 5,
+ "burst": 0,
"burst_unit": "bytes",
"per": "second",
"rate": 2,
@@ -83,7 +83,7 @@
[
{
"limit": {
- "burst": 5,
+ "burst": 0,
"burst_unit": "bytes",
"per": "second",
"rate": 1025,
@@ -96,7 +96,7 @@
[
{
"limit": {
- "burst": 5,
+ "burst": 0,
"burst_unit": "bytes",
"per": "second",
"rate": 1023,
@@ -109,7 +109,7 @@
[
{
"limit": {
- "burst": 5,
+ "burst": 0,
"burst_unit": "bytes",
"per": "second",
"rate": 10230,
@@ -118,19 +118,6 @@
}
]
-# limit rate 1023000 mbytes/second
-[
- {
- "limit": {
- "burst": 5,
- "burst_unit": "bytes",
- "per": "second",
- "rate": 1023000,
- "rate_unit": "mbytes"
- }
- }
-]
-
# limit rate over 400/minute
[
{
@@ -195,7 +182,7 @@
[
{
"limit": {
- "burst": 5,
+ "burst": 0,
"burst_unit": "bytes",
"inv": true,
"per": "second",
@@ -209,7 +196,7 @@
[
{
"limit": {
- "burst": 5,
+ "burst": 0,
"burst_unit": "bytes",
"inv": true,
"per": "second",
@@ -223,7 +210,7 @@
[
{
"limit": {
- "burst": 5,
+ "burst": 0,
"burst_unit": "bytes",
"inv": true,
"per": "second",
@@ -237,7 +224,7 @@
[
{
"limit": {
- "burst": 5,
+ "burst": 0,
"burst_unit": "bytes",
"inv": true,
"per": "second",
@@ -251,7 +238,7 @@
[
{
"limit": {
- "burst": 5,
+ "burst": 0,
"burst_unit": "bytes",
"inv": true,
"per": "second",
@@ -260,18 +247,3 @@
}
}
]
-
-# limit rate over 1023000 mbytes/second
-[
- {
- "limit": {
- "burst": 5,
- "burst_unit": "bytes",
- "inv": true,
- "per": "second",
- "rate": 1023000,
- "rate_unit": "mbytes"
- }
- }
-]
-
diff --git a/tests/py/any/limit.t.payload b/tests/py/any/limit.t.payload
index dc6cea9b..dc6701b3 100644
--- a/tests/py/any/limit.t.payload
+++ b/tests/py/any/limit.t.payload
@@ -24,27 +24,36 @@ ip test-ip4 output
# limit rate 1 kbytes/second
ip test-ip4 output
- [ limit rate 1024/second burst 5 type bytes flags 0x0 ]
+ [ limit rate 1024/second burst 0 type bytes flags 0x0 ]
# limit rate 2 kbytes/second
ip test-ip4 output
- [ limit rate 2048/second burst 5 type bytes flags 0x0 ]
+ [ limit rate 2048/second burst 0 type bytes flags 0x0 ]
# limit rate 1025 kbytes/second
ip test-ip4 output
- [ limit rate 1049600/second burst 5 type bytes flags 0x0 ]
+ [ limit rate 1049600/second burst 0 type bytes flags 0x0 ]
# limit rate 1023 mbytes/second
ip test-ip4 output
- [ limit rate 1072693248/second burst 5 type bytes flags 0x0 ]
+ [ limit rate 1072693248/second burst 0 type bytes flags 0x0 ]
# limit rate 10230 mbytes/second
ip test-ip4 output
- [ limit rate 10726932480/second burst 5 type bytes flags 0x0 ]
+ [ limit rate 10726932480/second burst 0 type bytes flags 0x0 ]
+
+# limit rate 1 bytes / second
+ip
+ [ limit rate 1/second burst 0 type bytes flags 0x0 ]
+
+# limit rate 1 kbytes / second
+ip
+ [ limit rate 1024/second burst 0 type bytes flags 0x0 ]
+
+# limit rate 1 mbytes / second
+ip
+ [ limit rate 1048576/second burst 0 type bytes flags 0x0 ]
-# limit rate 1023000 mbytes/second
-ip test-ip4 output
- [ limit rate 1072693248000/second burst 5 type bytes flags 0x0 ]
# limit rate 1025 bytes/second burst 512 bytes
ip test-ip4 output
@@ -58,10 +67,6 @@ ip test-ip4 output
ip test-ip4 output
[ limit rate 1074790400/second burst 1049600 type bytes flags 0x0 ]
-# limit rate 1025000 mbytes/second burst 1023 mbytes
-ip test-ip4 output
- [ limit rate 1074790400000/second burst 1072693248 type bytes flags 0x0 ]
-
# limit rate over 400/minute
ip test-ip4 output
[ limit rate 400/minute burst 5 type packets flags 0x1 ]
@@ -88,27 +93,23 @@ ip test-ip4 output
# limit rate over 1 kbytes/second
ip test-ip4 output
- [ limit rate 1024/second burst 5 type bytes flags 0x1 ]
+ [ limit rate 1024/second burst 0 type bytes flags 0x1 ]
# limit rate over 2 kbytes/second
ip test-ip4 output
- [ limit rate 2048/second burst 5 type bytes flags 0x1 ]
+ [ limit rate 2048/second burst 0 type bytes flags 0x1 ]
# limit rate over 1025 kbytes/second
ip test-ip4 output
- [ limit rate 1049600/second burst 5 type bytes flags 0x1 ]
+ [ limit rate 1049600/second burst 0 type bytes flags 0x1 ]
# limit rate over 1023 mbytes/second
ip test-ip4 output
- [ limit rate 1072693248/second burst 5 type bytes flags 0x1 ]
+ [ limit rate 1072693248/second burst 0 type bytes flags 0x1 ]
# limit rate over 10230 mbytes/second
ip test-ip4 output
- [ limit rate 10726932480/second burst 5 type bytes flags 0x1 ]
-
-# limit rate over 1023000 mbytes/second
-ip test-ip4 output
- [ limit rate 1072693248000/second burst 5 type bytes flags 0x1 ]
+ [ limit rate 10726932480/second burst 0 type bytes flags 0x1 ]
# limit rate over 1025 bytes/second burst 512 bytes
ip test-ip4 output
@@ -121,8 +122,3 @@ ip test-ip4 output
# limit rate over 1025 mbytes/second burst 1025 kbytes
ip test-ip4 output
[ limit rate 1074790400/second burst 1049600 type bytes flags 0x1 ]
-
-# limit rate over 1025000 mbytes/second burst 1023 mbytes
-ip test-ip4 output
- [ limit rate 1074790400000/second burst 1072693248 type bytes flags 0x1 ]
-
diff --git a/tests/py/any/meta.t b/tests/py/any/meta.t
index 6ec4853e..bd10c56d 100644
--- a/tests/py/any/meta.t
+++ b/tests/py/any/meta.t
@@ -1,12 +1,13 @@
:input;type filter hook input priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*ip;test-ip4;input
*ip6;test-ip6;input
*inet;test-inet;input
*arp;test-arp;input
*bridge;test-bridge;input
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
meta length 1000;ok
meta length 22;ok
@@ -55,7 +56,7 @@ meta mark and 0x03 == 0x01;ok;meta mark & 0x00000003 == 0x00000001
meta mark and 0x03 != 0x01;ok;meta mark & 0x00000003 != 0x00000001
meta mark 0x10;ok;meta mark 0x00000010
meta mark != 0x10;ok;meta mark != 0x00000010
-meta mark 0xffffff00/24;ok
+meta mark 0xffffff00/24;ok;meta mark & 0xffffff00 == 0xffffff00
meta mark or 0x03 == 0x01;ok;meta mark | 0x00000003 == 0x00000001
meta mark or 0x03 != 0x01;ok;meta mark | 0x00000003 != 0x00000001
@@ -207,6 +208,8 @@ meta time "2019-06-21 17:00:00" drop;ok
meta time "2019-07-01 00:00:00" drop;ok
meta time "2019-07-01 00:01:00" drop;ok
meta time "2019-07-01 00:00:01" drop;ok
+meta time < "2022-07-01 11:00:00" accept;ok
+meta time > "2022-07-01 11:00:00" accept;ok
meta day "Saturday" drop;ok
meta day 6 drop;ok;meta day "Saturday" drop
meta day "Satturday" drop;fail
@@ -215,7 +218,13 @@ meta hour "17:00:00" drop;ok;meta hour "17:00" drop
meta hour "17:00:01" drop;ok
meta hour "00:00" drop;ok
meta hour "00:01" drop;ok
+time < "2022-07-01 11:00:00" accept;ok;meta time < "2022-07-01 11:00:00" accept
+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.json b/tests/py/any/meta.t.json
index b140aaaa..676affea 100644
--- a/tests/py/any/meta.t.json
+++ b/tests/py/any/meta.t.json
@@ -667,17 +667,17 @@
{
"match": {
"left": {
- "meta": {
- "key": "mark"
- }
+ "&": [
+ {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ 4294967040
+ ]
},
"op": "==",
- "right": {
- "prefix": {
- "addr": 4294967040,
- "len": 24
- }
- }
+ "right": 4294967040
}
}
]
@@ -2561,6 +2561,42 @@
}
]
+# meta time < "2022-07-01 11:00:00" accept
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "time"
+ }
+ },
+ "op": "<",
+ "right": "2022-07-01 11:00:00"
+ }
+ },
+ {
+ "accept": null
+ }
+]
+
+# meta time > "2022-07-01 11:00:00" accept
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "time"
+ }
+ },
+ "op": ">",
+ "right": "2022-07-01 11:00:00"
+ }
+ },
+ {
+ "accept": null
+ }
+]
+
# meta day "Saturday" drop
[
{
@@ -2625,7 +2661,7 @@
}
},
"op": "==",
- "right": "17:00"
+ "right": "17:00:00"
}
},
{
@@ -2686,3 +2722,99 @@
"drop": null
}
]
+
+# time < "2022-07-01 11:00:00" accept
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "time"
+ }
+ },
+ "op": "<",
+ "right": "2022-07-01 11:00:00"
+ }
+ },
+ {
+ "accept": null
+ }
+]
+
+# time > "2022-07-01 11:00:00" accept
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "time"
+ }
+ },
+ "op": ">",
+ "right": "2022-07-01 11:00:00"
+ }
+ },
+ {
+ "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"
+ }
+ }
+ }
+ }
+ }
+ }
+]
+
diff --git a/tests/py/any/meta.t.json.output b/tests/py/any/meta.t.json.output
index 4e9e669f..d46935de 100644
--- a/tests/py/any/meta.t.json.output
+++ b/tests/py/any/meta.t.json.output
@@ -592,24 +592,6 @@
}
]
-# meta time "1970-05-23 21:07:14" drop
-[
- {
- "match": {
- "left": {
- "meta": {
- "key": "time"
- }
- },
- "op": "==",
- "right": "1970-05-23 21:07:14"
- }
- },
- {
- "drop": null
- }
-]
-
# meta time 12341234 drop
[
{
@@ -628,96 +610,6 @@
}
]
-# meta time "2019-06-21 17:00:00" drop
-[
- {
- "match": {
- "left": {
- "meta": {
- "key": "time"
- }
- },
- "op": "==",
- "right": "2019-06-21 17:00:00"
- }
- },
- {
- "drop": null
- }
-]
-
-# meta time "2019-07-01 00:00:00" drop
-[
- {
- "match": {
- "left": {
- "meta": {
- "key": "time"
- }
- },
- "op": "==",
- "right": "2019-07-01 00:00:00"
- }
- },
- {
- "drop": null
- }
-]
-
-# meta time "2019-07-01 00:01:00" drop
-[
- {
- "match": {
- "left": {
- "meta": {
- "key": "time"
- }
- },
- "op": "==",
- "right": "2019-07-01 00:01:00"
- }
- },
- {
- "drop": null
- }
-]
-
-# meta time "2019-07-01 00:00:01" drop
-[
- {
- "match": {
- "left": {
- "meta": {
- "key": "time"
- }
- },
- "op": "==",
- "right": "2019-07-01 00:00:01"
- }
- },
- {
- "drop": null
- }
-]
-
-# meta day "Saturday" drop
-[
- {
- "match": {
- "left": {
- "meta": {
- "key": "day"
- }
- },
- "op": "==",
- "right": "Saturday"
- }
- },
- {
- "drop": null
- }
-]
-
# meta day 6 drop
[
{
@@ -736,24 +628,6 @@
}
]
-# meta hour "17:00" drop
-[
- {
- "match": {
- "left": {
- "meta": {
- "key": "hour"
- }
- },
- "op": "==",
- "right": "17:00"
- }
- },
- {
- "drop": null
- }
-]
-
# meta hour "17:00:00" drop
[
{
@@ -772,57 +646,3 @@
}
]
-# meta hour "17:00:01" drop
-[
- {
- "match": {
- "left": {
- "meta": {
- "key": "hour"
- }
- },
- "op": "==",
- "right": "17:00:01"
- }
- },
- {
- "drop": null
- }
-]
-
-# meta hour "00:00" drop
-[
- {
- "match": {
- "left": {
- "meta": {
- "key": "hour"
- }
- },
- "op": "==",
- "right": "00:00"
- }
- },
- {
- "drop": null
- }
-]
-
-# meta hour "00:01" drop
-[
- {
- "match": {
- "left": {
- "meta": {
- "key": "hour"
- }
- },
- "op": "==",
- "right": "00:01"
- }
- },
- {
- "drop": null
- }
-]
-
diff --git a/tests/py/any/meta.t.payload b/tests/py/any/meta.t.payload
index d8351c27..49dd729b 100644
--- a/tests/py/any/meta.t.payload
+++ b/tests/py/any/meta.t.payload
@@ -1003,6 +1003,20 @@ ip meta-test input
[ cmp eq reg 1 0x22eb8a00 0x15ad18e1 ]
[ immediate reg 0 drop ]
+# meta time < "2022-07-01 11:00:00" accept
+ip test-ip4 input
+ [ meta load time => reg 1 ]
+ [ byteorder reg 1 = hton(reg 1, 8, 8) ]
+ [ cmp lt reg 1 0xf3a8fd16 0x00a07719 ]
+ [ immediate reg 0 accept ]
+
+# meta time > "2022-07-01 11:00:00" accept
+ip test-ip4 input
+ [ meta load time => reg 1 ]
+ [ byteorder reg 1 = hton(reg 1, 8, 8) ]
+ [ cmp gt reg 1 0xf3a8fd16 0x00a07719 ]
+ [ immediate reg 0 accept ]
+
# meta day "Saturday" drop
ip test-ip4 input
[ meta load day => reg 1 ]
@@ -1044,3 +1058,42 @@ ip meta-test input
[ meta load hour => reg 1 ]
[ cmp eq reg 1 0x0001359c ]
[ immediate reg 0 drop ]
+
+# time < "2022-07-01 11:00:00" accept
+ip test-ip4 input
+ [ meta load time => reg 1 ]
+ [ byteorder reg 1 = hton(reg 1, 8, 8) ]
+ [ cmp lt reg 1 0xf3a8fd16 0x00a07719 ]
+ [ immediate reg 0 accept ]
+
+# time > "2022-07-01 11:00:00" accept
+ip test-ip4 input
+ [ meta load time => reg 1 ]
+ [ 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 00000000..5997ccc7
--- /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/any/objects.t b/tests/py/any/objects.t
index 89a9545f..7b51f918 100644
--- a/tests/py/any/objects.t
+++ b/tests/py/any/objects.t
@@ -1,12 +1,13 @@
:output;type filter hook output priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*ip;test-ip4;output
*ip6;test-ip6;output
*inet;test-inet;output
*arp;test-arp;output
*bridge;test-bridge;output
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
%cnt1 type counter;ok
%qt1 type quota 25 mbytes;ok
diff --git a/tests/py/any/queue.t b/tests/py/any/queue.t
index f12acfaf..2e511362 100644
--- a/tests/py/any/queue.t
+++ b/tests/py/any/queue.t
@@ -3,7 +3,6 @@
*ip;test-ip4;output
*ip6;test-ip6;output
*inet;test-inet;output
-*arp;test-arp;output
*bridge;test-bridge;output
queue;ok;queue to 0
diff --git a/tests/py/any/quota.t b/tests/py/any/quota.t
index 9a8db114..79dd7654 100644
--- a/tests/py/any/quota.t
+++ b/tests/py/any/quota.t
@@ -1,12 +1,13 @@
:output;type filter hook output priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*ip;test-ip4;output
*ip6;test-ip6;output
*inet;test-inet;output
*arp;test-arp;output
*bridge;test-bridge;output
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
quota 1025 bytes;ok
quota 1 kbytes;ok
diff --git a/tests/py/any/rawpayload.t b/tests/py/any/rawpayload.t
index c3382a96..5bc9d35f 100644
--- a/tests/py/any/rawpayload.t
+++ b/tests/py/any/rawpayload.t
@@ -1,19 +1,24 @@
:input;type filter hook input priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*inet;test-inet;input
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
meta l4proto { tcp, udp, sctp} @th,16,16 { 22, 23, 80 };ok;meta l4proto { 6, 17, 132} th dport { 22, 23, 80}
meta l4proto tcp @th,16,16 { 22, 23, 80};ok;tcp dport { 22, 23, 80}
-@nh,8,8 255;ok
-@nh,8,16 0;ok
+@nh,8,8 0xff;ok
+@nh,8,16 0x0;ok
# out of range (0-1)
@th,16,1 2;fail
@ll,0,0 2;fail
@ll,0,1;fail
-@ll,0,1 1;ok;@ll,0,8 & 128 == 128
-@ll,0,8 and 0x80 eq 0x80;ok;@ll,0,8 & 128 == 128
-@ll,0,128 0xfedcba987654321001234567890abcde;ok;@ll,0,128 338770000845734292516042252062074518750
+@ll,0,1 1;ok;@ll,0,8 & 0x80 == 0x80
+@ll,0,8 & 0x80 == 0x80;ok
+@ll,0,128 0xfedcba987654321001234567890abcde;ok
+
+meta l4proto 91 @th,400,16 0x0 accept;ok
+
+@ih,32,32 0x14000000;ok
diff --git a/tests/py/any/rawpayload.t.json b/tests/py/any/rawpayload.t.json
index 22028ad8..4cae4d49 100644
--- a/tests/py/any/rawpayload.t.json
+++ b/tests/py/any/rawpayload.t.json
@@ -66,7 +66,7 @@
}
]
-# @nh,8,8 255
+# @nh,8,8 0xff
[
{
"match": {
@@ -78,12 +78,12 @@
}
},
"op": "==",
- "right": 255
+ "right": "0xff"
}
}
]
-# @nh,8,16 0
+# @nh,8,16 0x0
[
{
"match": {
@@ -117,7 +117,7 @@
}
]
-# @ll,0,8 and 0x80 eq 0x80
+# @ll,0,8 & 0x80 == 0x80
[
{
"match": {
@@ -156,3 +156,51 @@
}
]
+# meta l4proto 91 @th,400,16 0x0 accept
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "op": "==",
+ "right": 91
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "base": "th",
+ "len": 16,
+ "offset": 400
+ }
+ },
+ "op": "==",
+ "right": 0
+ }
+ },
+ {
+ "accept": null
+ }
+]
+
+# @ih,32,32 0x14000000
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "base": "ih",
+ "len": 32,
+ "offset": 32
+ }
+ },
+ "op": "==",
+ "right": 335544320
+ }
+ }
+]
+
diff --git a/tests/py/any/rawpayload.t.json.output b/tests/py/any/rawpayload.t.json.output
index ccadbc57..291b237a 100644
--- a/tests/py/any/rawpayload.t.json.output
+++ b/tests/py/any/rawpayload.t.json.output
@@ -79,7 +79,7 @@
}
]
-# @ll,0,8 and 0x80 eq 0x80
+# @ll,0,8 & 0x80 == 0x80
[
{
"match": {
@@ -101,3 +101,19 @@
}
]
+# @nh,8,8 0xff
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "base": "nh",
+ "len": 8,
+ "offset": 8
+ }
+ },
+ "op": "==",
+ "right": 255
+ }
+ }
+]
diff --git a/tests/py/any/rawpayload.t.payload b/tests/py/any/rawpayload.t.payload
index b3ca919f..fe2377e6 100644
--- a/tests/py/any/rawpayload.t.payload
+++ b/tests/py/any/rawpayload.t.payload
@@ -21,12 +21,12 @@ inet test-inet input
[ payload load 2b @ transport header + 2 => reg 1 ]
[ lookup reg 1 set __set%d ]
-# @nh,8,8 255
+# @nh,8,8 0xff
inet test-inet input
[ payload load 1b @ network header + 1 => reg 1 ]
[ cmp eq reg 1 0x000000ff ]
-# @nh,8,16 0
+# @nh,8,16 0x0
inet test-inet input
[ payload load 2b @ network header + 1 => reg 1 ]
[ cmp eq reg 1 0x00000000 ]
@@ -37,7 +37,7 @@ inet test-inet input
[ bitwise reg 1 = ( reg 1 & 0x00000080 ) ^ 0x00000000 ]
[ cmp eq reg 1 0x00000080 ]
-# @ll,0,8 and 0x80 eq 0x80
+# @ll,0,8 & 0x80 == 0x80
inet test-inet input
[ payload load 1b @ link header + 0 => reg 1 ]
[ bitwise reg 1 = ( reg 1 & 0x00000080 ) ^ 0x00000000 ]
@@ -47,3 +47,17 @@ inet test-inet input
inet test-inet input
[ payload load 16b @ link header + 0 => reg 1 ]
[ cmp eq reg 1 0x98badcfe 0x10325476 0x67452301 0xdebc0a89 ]
+
+# meta l4proto 91 @th,400,16 0x0 accept
+inet test-inet input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000005b ]
+ [ payload load 2b @ transport header + 50 => reg 1 ]
+ [ cmp eq reg 1 0x00000000 ]
+ [ immediate reg 0 accept ]
+
+# @ih,32,32 0x14000000
+inet test-inet input
+ [ payload load 4b @ inner header + 4 => reg 1 ]
+ [ cmp eq reg 1 0x00000014 ]
+
diff --git a/tests/py/any/tcpopt.t b/tests/py/any/tcpopt.t
index bcc64eac..177f01c4 100644
--- a/tests/py/any/tcpopt.t
+++ b/tests/py/any/tcpopt.t
@@ -4,17 +4,16 @@
*ip6;test-ip6;input
*inet;test-inet;input
-tcp option eol kind 1;ok
-tcp option nop kind 1;ok
-tcp option maxseg kind 1;ok
+tcp option eol exists;ok
+tcp option nop exists;ok
+tcp option maxseg exists;ok
tcp option maxseg length 1;ok
tcp option maxseg size 1;ok
-tcp option window kind 1;ok
tcp option window length 1;ok
tcp option window count 1;ok
-tcp option sack-perm kind 1;ok
+tcp option sack-perm exists;ok
tcp option sack-perm length 1;ok
-tcp option sack kind 1;ok
+tcp option sack exists;ok
tcp option sack length 1;ok
tcp option sack left 1;ok
tcp option sack0 left 1;ok;tcp option sack left 1
@@ -26,7 +25,7 @@ tcp option sack0 right 1;ok;tcp option sack right 1
tcp option sack1 right 1;ok
tcp option sack2 right 1;ok
tcp option sack3 right 1;ok
-tcp option timestamp kind 1;ok
+tcp option timestamp exists;ok
tcp option timestamp length 1;ok
tcp option timestamp tsval 1;ok
tcp option timestamp tsecr 1;ok
@@ -47,3 +46,17 @@ tcp option window exists;ok
tcp option window missing;ok
tcp option maxseg size set 1360;ok
+
+tcp option md5sig exists;ok
+tcp option fastopen exists;ok
+tcp option mptcp exists;ok
+
+tcp option mptcp subtype 0;ok
+tcp option mptcp subtype 1;ok
+tcp option mptcp subtype { 0, 2};ok
+
+reset tcp option mptcp;ok
+reset tcp option 2;ok;reset tcp option maxseg
+reset tcp option 123;ok
+reset tcp option meh;fail
+reset tcp option 256;fail
diff --git a/tests/py/any/tcpopt.t.json b/tests/py/any/tcpopt.t.json
index a45b4c8b..87074b9d 100644
--- a/tests/py/any/tcpopt.t.json
+++ b/tests/py/any/tcpopt.t.json
@@ -1,47 +1,44 @@
-# tcp option eol kind 1
+# tcp option eol exists
[
{
"match": {
"left": {
"tcp option": {
- "field": "kind",
"name": "eol"
}
},
"op": "==",
- "right": 1
+ "right": true
}
}
]
-# tcp option nop kind 1
+# tcp option nop exists
[
{
"match": {
"left": {
"tcp option": {
- "field": "kind",
"name": "nop"
}
},
"op": "==",
- "right": 1
+ "right": true
}
}
]
-# tcp option maxseg kind 1
+# tcp option maxseg exists
[
{
"match": {
"left": {
"tcp option": {
- "field": "kind",
"name": "maxseg"
}
},
"op": "==",
- "right": 1
+ "right": true
}
}
]
@@ -78,22 +75,6 @@
}
]
-# tcp option window kind 1
-[
- {
- "match": {
- "left": {
- "tcp option": {
- "field": "kind",
- "name": "window"
- }
- },
- "op": "==",
- "right": 1
- }
- }
-]
-
# tcp option window length 1
[
{
@@ -126,18 +107,17 @@
}
]
-# tcp option sack-perm kind 1
+# tcp option sack-perm exists
[
{
"match": {
"left": {
"tcp option": {
- "field": "kind",
"name": "sack-perm"
}
},
"op": "==",
- "right": 1
+ "right": true
}
}
]
@@ -158,18 +138,17 @@
}
]
-# tcp option sack kind 1
+# tcp option sack exists
[
{
"match": {
"left": {
"tcp option": {
- "field": "kind",
"name": "sack"
}
},
"op": "==",
- "right": 1
+ "right": true
}
}
]
@@ -350,18 +329,17 @@
}
]
-# tcp option timestamp kind 1
+# tcp option timestamp exists
[
{
"match": {
"left": {
"tcp option": {
- "field": "kind",
"name": "timestamp"
}
},
"op": "==",
- "right": 1
+ "right": true
}
}
]
@@ -414,36 +392,36 @@
}
]
-# tcp option 6 exists
+# tcp option 255 missing
[
{
"match": {
"left": {
"tcp option": {
- "base": 6,
+ "base": 255,
"len": 8,
"offset": 0
}
},
"op": "==",
- "right": true
+ "right": false
}
}
]
-# tcp option 255 missing
+# tcp option 6 exists
[
{
"match": {
"left": {
"tcp option": {
- "base": 255,
+ "base": 6,
"len": 8,
"offset": 0
}
},
"op": "==",
- "right": false
+ "right": true
}
}
]
@@ -510,3 +488,135 @@
}
]
+# tcp option md5sig exists
+[
+ {
+ "match": {
+ "left": {
+ "tcp option": {
+ "name": "md5sig"
+ }
+ },
+ "op": "==",
+ "right": true
+ }
+ }
+]
+
+# tcp option fastopen exists
+[
+ {
+ "match": {
+ "left": {
+ "tcp option": {
+ "name": "fastopen"
+ }
+ },
+ "op": "==",
+ "right": true
+ }
+ }
+]
+
+# tcp option mptcp exists
+[
+ {
+ "match": {
+ "left": {
+ "tcp option": {
+ "name": "mptcp"
+ }
+ },
+ "op": "==",
+ "right": true
+ }
+ }
+]
+
+# tcp option mptcp subtype 0
+[
+ {
+ "match": {
+ "left": {
+ "tcp option": {
+ "field": "subtype",
+ "name": "mptcp"
+ }
+ },
+ "op": "==",
+ "right": 0
+ }
+ }
+]
+
+# tcp option mptcp subtype 1
+[
+ {
+ "match": {
+ "left": {
+ "tcp option": {
+ "field": "subtype",
+ "name": "mptcp"
+ }
+ },
+ "op": "==",
+ "right": 1
+ }
+ }
+]
+
+# tcp option mptcp subtype { 0, 2}
+[
+ {
+ "match": {
+ "left": {
+ "tcp option": {
+ "field": "subtype",
+ "name": "mptcp"
+ }
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ 0,
+ 2
+ ]
+ }
+ }
+ }
+]
+
+# reset tcp option mptcp
+[
+ {
+ "reset": {
+ "tcp option": {
+ "name": "mptcp"
+ }
+ }
+ }
+]
+
+# reset tcp option 2
+[
+ {
+ "reset": {
+ "tcp option": {
+ "name": "maxseg"
+ }
+ }
+ }
+]
+
+# reset tcp option 123
+[
+ {
+ "reset": {
+ "tcp option": {
+ "base": 123,
+ "len": 0,
+ "offset": 0
+ }
+ }
+ }
+]
diff --git a/tests/py/any/tcpopt.t.payload b/tests/py/any/tcpopt.t.payload
index 51f3a752..99b8985f 100644
--- a/tests/py/any/tcpopt.t.payload
+++ b/tests/py/any/tcpopt.t.payload
@@ -1,16 +1,16 @@
-# tcp option eol kind 1
+# tcp option eol exists
inet
- [ exthdr load tcpopt 1b @ 0 + 0 => reg 1 ]
+ [ exthdr load tcpopt 1b @ 0 + 0 present => reg 1 ]
[ cmp eq reg 1 0x00000001 ]
-# tcp option nop kind 1
+# tcp option nop exists
inet
- [ exthdr load tcpopt 1b @ 1 + 0 => reg 1 ]
+ [ exthdr load tcpopt 1b @ 1 + 0 present => reg 1 ]
[ cmp eq reg 1 0x00000001 ]
-# tcp option maxseg kind 1
+# tcp option maxseg exists
inet
- [ exthdr load tcpopt 1b @ 2 + 0 => reg 1 ]
+ [ exthdr load tcpopt 1b @ 2 + 0 present => reg 1 ]
[ cmp eq reg 1 0x00000001 ]
# tcp option maxseg length 1
@@ -23,11 +23,6 @@ inet
[ exthdr load tcpopt 2b @ 2 + 2 => reg 1 ]
[ cmp eq reg 1 0x00000100 ]
-# tcp option window kind 1
-inet
- [ exthdr load tcpopt 1b @ 3 + 0 => reg 1 ]
- [ cmp eq reg 1 0x00000001 ]
-
# tcp option window length 1
inet
[ exthdr load tcpopt 1b @ 3 + 1 => reg 1 ]
@@ -38,9 +33,9 @@ inet
[ exthdr load tcpopt 1b @ 3 + 2 => reg 1 ]
[ cmp eq reg 1 0x00000001 ]
-# tcp option sack-perm kind 1
+# tcp option sack-perm exists
inet
- [ exthdr load tcpopt 1b @ 4 + 0 => reg 1 ]
+ [ exthdr load tcpopt 1b @ 4 + 0 present => reg 1 ]
[ cmp eq reg 1 0x00000001 ]
# tcp option sack-perm length 1
@@ -48,9 +43,9 @@ inet
[ exthdr load tcpopt 1b @ 4 + 1 => reg 1 ]
[ cmp eq reg 1 0x00000001 ]
-# tcp option sack kind 1
+# tcp option sack exists
inet
- [ exthdr load tcpopt 1b @ 5 + 0 => reg 1 ]
+ [ exthdr load tcpopt 1b @ 5 + 0 present => reg 1 ]
[ cmp eq reg 1 0x00000001 ]
# tcp option sack length 1
@@ -108,9 +103,9 @@ inet
[ exthdr load tcpopt 4b @ 5 + 30 => reg 1 ]
[ cmp eq reg 1 0x01000000 ]
-# tcp option timestamp kind 1
+# tcp option timestamp exists
inet
- [ exthdr load tcpopt 1b @ 8 + 0 => reg 1 ]
+ [ exthdr load tcpopt 1b @ 8 + 0 present => reg 1 ]
[ cmp eq reg 1 0x00000001 ]
# tcp option timestamp length 1
@@ -158,3 +153,50 @@ inet
[ immediate reg 1 0x00005005 ]
[ exthdr write tcpopt reg 1 => 2b @ 2 + 2 ]
+# tcp option md5sig exists
+inet
+ [ exthdr load tcpopt 1b @ 19 + 0 present => reg 1 ]
+ [ cmp eq reg 1 0x00000001 ]
+
+# tcp option fastopen exists
+inet
+ [ exthdr load tcpopt 1b @ 34 + 0 present => reg 1 ]
+ [ cmp eq reg 1 0x00000001 ]
+
+# tcp option mptcp exists
+inet
+ [ exthdr load tcpopt 1b @ 30 + 0 present => reg 1 ]
+ [ cmp eq reg 1 0x00000001 ]
+
+# tcp option mptcp subtype 0
+inet
+ [ exthdr load tcpopt 1b @ 30 + 2 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x000000f0 ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000000 ]
+
+# tcp option mptcp subtype 1
+inet
+ [ exthdr load tcpopt 1b @ 30 + 2 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x000000f0 ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000010 ]
+
+# tcp option mptcp subtype { 0, 2}
+__set%d test-inet 3 size 2
+__set%d test-inet 0
+ element 00000000 : 0 [end] element 00000020 : 0 [end]
+inet
+ [ exthdr load tcpopt 1b @ 30 + 2 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x000000f0 ) ^ 0x00000000 ]
+ [ lookup reg 1 set __set%d ]
+
+# reset tcp option mptcp
+ip test-ip4 input
+ [ exthdr reset tcpopt 30 ]
+
+# reset tcp option 2
+ip test-ip4 input
+ [ exthdr reset tcpopt 2 ]
+
+# reset tcp option 123
+ip test-ip4 input
+ [ exthdr reset tcpopt 123 ]
diff --git a/tests/py/arp/arp.t b/tests/py/arp/arp.t
index 178cf82b..222b91cf 100644
--- a/tests/py/arp/arp.t
+++ b/tests/py/arp/arp.t
@@ -1,9 +1,10 @@
# filter chains available are: input, output, forward
:input;type filter hook input priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*arp;test-arp;input
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
arp htype 1;ok
arp htype != 1;ok
diff --git a/tests/py/bridge/meta.t b/tests/py/bridge/meta.t
index d77ebd89..171aa610 100644
--- a/tests/py/bridge/meta.t
+++ b/tests/py/bridge/meta.t
@@ -9,3 +9,5 @@ meta ibrpvid 100;ok
meta protocol ip udp dport 67;ok
meta protocol ip6 udp dport 67;ok
+
+meta broute set 1;fail
diff --git a/tests/py/bridge/redirect.t b/tests/py/bridge/redirect.t
new file mode 100644
index 00000000..5181e799
--- /dev/null
+++ b/tests/py/bridge/redirect.t
@@ -0,0 +1,5 @@
+:prerouting;type filter hook prerouting priority 0
+
+*bridge;test-bridge;prerouting
+
+meta broute set 1;ok
diff --git a/tests/py/bridge/redirect.t.json b/tests/py/bridge/redirect.t.json
new file mode 100644
index 00000000..7e32b329
--- /dev/null
+++ b/tests/py/bridge/redirect.t.json
@@ -0,0 +1,12 @@
+# meta broute set 1
+[
+ {
+ "mangle": {
+ "key": {
+ "meta": { "key": "broute" }
+ },
+ "value": 1
+ }
+ }
+]
+
diff --git a/tests/py/bridge/redirect.t.payload b/tests/py/bridge/redirect.t.payload
new file mode 100644
index 00000000..1fcfa5f1
--- /dev/null
+++ b/tests/py/bridge/redirect.t.payload
@@ -0,0 +1,4 @@
+# meta broute set 1
+bridge test-bridge prerouting
+ [ immediate reg 1 0x00000001 ]
+ [ meta set broute with reg 1 ]
diff --git a/tests/py/bridge/vlan.t b/tests/py/bridge/vlan.t
index b506ee8d..8fa90dac 100644
--- a/tests/py/bridge/vlan.t
+++ b/tests/py/bridge/vlan.t
@@ -1,8 +1,9 @@
:input;type filter hook input priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*bridge;test-bridge;input
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
vlan id 4094;ok
vlan id 0;ok
@@ -46,3 +47,10 @@ ether type ip vlan id 1 ip saddr 10.0.0.1;fail
# mangling
vlan id 1 vlan id set 2;ok
+
+ether saddr 00:01:02:03:04:05 vlan id 1;ok
+vlan id 2 ether saddr 0:1:2:3:4:6;ok;ether saddr 00:01:02:03:04:06 vlan id 2
+
+ether saddr . vlan id { 0a:0b:0c:0d:0e:0f . 42, 0a:0b:0c:0d:0e:0f . 4095 };ok
+
+ether saddr 00:11:22:33:44:55 counter ether type 8021q;ok
diff --git a/tests/py/bridge/vlan.t.json b/tests/py/bridge/vlan.t.json
index e7640f9a..7dfcdb4b 100644
--- a/tests/py/bridge/vlan.t.json
+++ b/tests/py/bridge/vlan.t.json
@@ -761,3 +761,134 @@
}
}
]
+
+# ether saddr 00:01:02:03:04:05 vlan id 1
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ether"
+ }
+ },
+ "op": "==",
+ "right": "00:01:02:03:04:05"
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "id",
+ "protocol": "vlan"
+ }
+ },
+ "op": "==",
+ "right": 1
+ }
+ }
+]
+
+# vlan id 2 ether saddr 0:1:2:3:4:6
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ether"
+ }
+ },
+ "op": "==",
+ "right": "00:01:02:03:04:06"
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "id",
+ "protocol": "vlan"
+ }
+ },
+ "op": "==",
+ "right": 2
+ }
+ }
+]
+
+# ether saddr . vlan id { 0a:0b:0c:0d:0e:0f . 42, 0a:0b:0c:0d:0e:0f . 4095 }
+[
+ {
+ "match": {
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ether"
+ }
+ },
+ {
+ "payload": {
+ "field": "id",
+ "protocol": "vlan"
+ }
+ }
+ ]
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ {
+ "concat": [
+ "0a:0b:0c:0d:0e:0f",
+ 42
+ ]
+ },
+ {
+ "concat": [
+ "0a:0b:0c:0d:0e:0f",
+ 4095
+ ]
+ }
+ ]
+ }
+ }
+ }
+]
+
+# ether saddr 00:11:22:33:44:55 counter ether type 8021q
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ether"
+ }
+ },
+ "op": "==",
+ "right": "00:11:22:33:44:55"
+ }
+ },
+ {
+ "counter": {
+ "bytes": 0,
+ "packets": 0
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "type",
+ "protocol": "ether"
+ }
+ },
+ "op": "==",
+ "right": "8021q"
+ }
+ }
+]
diff --git a/tests/py/bridge/vlan.t.json.output b/tests/py/bridge/vlan.t.json.output
index 2f90c8ff..eea2d411 100644
--- a/tests/py/bridge/vlan.t.json.output
+++ b/tests/py/bridge/vlan.t.json.output
@@ -202,3 +202,34 @@
}
}
]
+
+# ether saddr 00:11:22:33:44:55 counter ether type 8021q
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ether"
+ }
+ },
+ "op": "==",
+ "right": "00:11:22:33:44:55"
+ }
+ },
+ {
+ "counter": null
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "type",
+ "protocol": "ether"
+ }
+ },
+ "op": "==",
+ "right": "8021q"
+ }
+ }
+]
diff --git a/tests/py/bridge/vlan.t.payload b/tests/py/bridge/vlan.t.payload
index 6c8d595a..2592bb96 100644
--- a/tests/py/bridge/vlan.t.payload
+++ b/tests/py/bridge/vlan.t.payload
@@ -276,3 +276,39 @@ bridge
[ payload load 2b @ link header + 14 => reg 1 ]
[ bitwise reg 1 = ( reg 1 & 0x000000f0 ) ^ 0x00000200 ]
[ payload write reg 1 => 2b @ link header + 14 csum_type 0 csum_off 0 csum_flags 0x0 ]
+
+# ether saddr 00:01:02:03:04:05 vlan id 1
+bridge test-bridge input
+ [ payload load 8b @ link header + 6 => reg 1 ]
+ [ cmp eq reg 1 0x03020100 0x00810504 ]
+ [ payload load 2b @ link header + 14 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000ff0f ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000100 ]
+
+# vlan id 2 ether saddr 0:1:2:3:4:6
+bridge test-bridge input
+ [ payload load 8b @ link header + 6 => reg 1 ]
+ [ cmp eq reg 1 0x03020100 0x00810604 ]
+ [ payload load 2b @ link header + 14 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000ff0f ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000200 ]
+
+# ether saddr . vlan id { 0a:0b:0c:0d:0e:0f . 42, 0a:0b:0c:0d:0e:0f . 4095 }
+__set%d test-bridge 3 size 2
+__set%d test-bridge 0
+ element 0d0c0b0a 00000f0e 00002a00 : 0 [end] element 0d0c0b0a 00000f0e 0000ff0f : 0 [end]
+bridge test-bridge input
+ [ payload load 2b @ link header + 12 => reg 1 ]
+ [ cmp eq reg 1 0x00000081 ]
+ [ payload load 6b @ link header + 6 => reg 1 ]
+ [ payload load 2b @ link header + 14 => reg 10 ]
+ [ bitwise reg 10 = ( reg 10 & 0x0000ff0f ) ^ 0x00000000 ]
+ [ lookup reg 1 set __set%d ]
+
+# ether saddr 00:11:22:33:44:55 counter ether type 8021q
+bridge test-bridge input
+ [ payload load 6b @ link header + 6 => reg 1 ]
+ [ cmp eq reg 1 0x33221100 0x00005544 ]
+ [ counter pkts 0 bytes 0 ]
+ [ payload load 2b @ link header + 12 => reg 1 ]
+ [ cmp eq reg 1 0x00000081 ]
diff --git a/tests/py/bridge/vlan.t.payload.netdev b/tests/py/bridge/vlan.t.payload.netdev
index d2c7d74a..f3341947 100644
--- a/tests/py/bridge/vlan.t.payload.netdev
+++ b/tests/py/bridge/vlan.t.payload.netdev
@@ -322,3 +322,47 @@ netdev
[ payload load 2b @ link header + 14 => reg 1 ]
[ bitwise reg 1 = ( reg 1 & 0x000000f0 ) ^ 0x00000200 ]
[ payload write reg 1 => 2b @ link header + 14 csum_type 0 csum_off 0 csum_flags 0x0 ]
+
+# vlan id 2 ether saddr 0:1:2:3:4:6
+netdev test-netdev ingress
+ [ meta load iiftype => reg 1 ]
+ [ cmp eq reg 1 0x00000001 ]
+ [ payload load 8b @ link header + 6 => reg 1 ]
+ [ cmp eq reg 1 0x03020100 0x00810604 ]
+ [ payload load 2b @ link header + 14 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000ff0f ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000200 ]
+
+# ether saddr 00:01:02:03:04:05 vlan id 1
+netdev test-netdev ingress
+ [ meta load iiftype => reg 1 ]
+ [ cmp eq reg 1 0x00000001 ]
+ [ payload load 8b @ link header + 6 => reg 1 ]
+ [ cmp eq reg 1 0x03020100 0x00810504 ]
+ [ payload load 2b @ link header + 14 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000ff0f ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000100 ]
+
+# ether saddr . vlan id { 0a:0b:0c:0d:0e:0f . 42, 0a:0b:0c:0d:0e:0f . 4095 }
+__set%d test-netdev 3 size 2
+__set%d test-netdev 0
+ element 0d0c0b0a 00000f0e 00002a00 : 0 [end] element 0d0c0b0a 00000f0e 0000ff0f : 0 [end]
+netdev test-netdev ingress
+ [ 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 6b @ link header + 6 => reg 1 ]
+ [ payload load 2b @ link header + 14 => reg 10 ]
+ [ bitwise reg 10 = ( reg 10 & 0x0000ff0f ) ^ 0x00000000 ]
+ [ lookup reg 1 set __set%d ]
+
+# ether saddr 00:11:22:33:44:55 counter ether type 8021q
+bridge test-bridge input
+ [ meta load iiftype => reg 1 ]
+ [ cmp eq reg 1 0x00000001 ]
+ [ payload load 6b @ link header + 6 => reg 1 ]
+ [ cmp eq reg 1 0x33221100 0x00005544 ]
+ [ counter pkts 0 bytes 0 ]
+ [ payload load 2b @ link header + 12 => reg 1 ]
+ [ cmp eq reg 1 0x00000081 ]
diff --git a/tests/py/inet/ah.t b/tests/py/inet/ah.t
index 78c454f7..83b6202b 100644
--- a/tests/py/inet/ah.t
+++ b/tests/py/inet/ah.t
@@ -1,10 +1,11 @@
:input;type filter hook input priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*ip;test-ip4;input
*ip6;test-ip6;input
*inet;test-inet;input
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
- ah nexthdr esp;ok
- ah nexthdr ah;ok
diff --git a/tests/py/inet/comp.t b/tests/py/inet/comp.t
index ec9924ff..2ef53820 100644
--- a/tests/py/inet/comp.t
+++ b/tests/py/inet/comp.t
@@ -1,10 +1,11 @@
:input;type filter hook input priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*ip;test-ip4;input
*ip6;test-ip6;input
*inet;test-inet;input
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
# BUG: nft: payload.c:88: payload_expr_pctx_update: Assertion `left->payload.base + 1 <= (__PROTO_BASE_MAX - 1)' failed.
- comp nexthdr esp;ok;comp nexthdr 50
diff --git a/tests/py/inet/dccp.t b/tests/py/inet/dccp.t
index 2216fa2a..99cddbe7 100644
--- a/tests/py/inet/dccp.t
+++ b/tests/py/inet/dccp.t
@@ -1,10 +1,11 @@
:input;type filter hook input priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*ip;test-ip4;input
*ip6;test-ip6;input
*inet;test-inet;input
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
dccp sport 21-35;ok
dccp sport != 21-35;ok
@@ -22,3 +23,8 @@ dccp type {request, response, data, ack, dataack, closereq, close, reset, sync,
dccp type != {request, response, data, ack, dataack, closereq, close, reset, sync, syncack};ok
dccp type request;ok
dccp type != request;ok
+
+dccp option 0 exists;ok
+dccp option 43 missing;ok
+dccp option 255 exists;ok
+dccp option 256 exists;fail
diff --git a/tests/py/inet/dccp.t.json b/tests/py/inet/dccp.t.json
index 806ef5ee..9f47e97b 100644
--- a/tests/py/inet/dccp.t.json
+++ b/tests/py/inet/dccp.t.json
@@ -230,3 +230,47 @@
}
]
+# dccp option 0 exists
+[
+ {
+ "match": {
+ "left": {
+ "dccp option": {
+ "type": 0
+ }
+ },
+ "op": "==",
+ "right": true
+ }
+ }
+]
+
+# dccp option 43 missing
+[
+ {
+ "match": {
+ "left": {
+ "dccp option": {
+ "type": 43
+ }
+ },
+ "op": "==",
+ "right": false
+ }
+ }
+]
+
+# dccp option 255 exists
+[
+ {
+ "match": {
+ "left": {
+ "dccp option": {
+ "type": 255
+ }
+ },
+ "op": "==",
+ "right": true
+ }
+ }
+]
diff --git a/tests/py/inet/dccp.t.payload b/tests/py/inet/dccp.t.payload
index fbe9dc5b..c0b87be1 100644
--- a/tests/py/inet/dccp.t.payload
+++ b/tests/py/inet/dccp.t.payload
@@ -99,3 +99,17 @@ inet test-inet input
[ bitwise reg 1 = ( reg 1 & 0x0000001e ) ^ 0x00000000 ]
[ cmp neq reg 1 0x00000000 ]
+# dccp option 0 exists
+ip test-inet input
+ [ exthdr load 1b @ 0 + 0 present => reg 1 ]
+ [ cmp eq reg 1 0x00000001 ]
+
+# dccp option 43 missing
+ip test-inet input
+ [ exthdr load 1b @ 43 + 0 present => reg 1 ]
+ [ cmp eq reg 1 0x00000000 ]
+
+# dccp option 255 exists
+ip test-inet input
+ [ exthdr load 1b @ 255 + 0 present => reg 1 ]
+ [ cmp eq reg 1 0x00000001 ]
diff --git a/tests/py/inet/esp.t b/tests/py/inet/esp.t
index 58e9f884..536260cf 100644
--- a/tests/py/inet/esp.t
+++ b/tests/py/inet/esp.t
@@ -1,10 +1,11 @@
:input;type filter hook input priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*ip;test-ip4;input
*ip6;test-ip6;input
*inet;test-inet;input
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
esp spi 100;ok
esp spi != 100;ok
diff --git a/tests/py/inet/ether-ip.t b/tests/py/inet/ether-ip.t
index 0c8c7f9d..759124de 100644
--- a/tests/py/inet/ether-ip.t
+++ b/tests/py/inet/ether-ip.t
@@ -1,8 +1,9 @@
:input;type filter hook input priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*inet;test-inet;input
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
tcp dport 22 iiftype ether ip daddr 1.2.3.4 ether saddr 00:0f:54:0c:11:4 accept;ok;tcp dport 22 ether saddr 00:0f:54:0c:11:04 ip daddr 1.2.3.4 accept
tcp dport 22 ip daddr 1.2.3.4 ether saddr 00:0f:54:0c:11:04;ok
diff --git a/tests/py/inet/ether.t b/tests/py/inet/ether.t
index afdf8b89..8625f70b 100644
--- a/tests/py/inet/ether.t
+++ b/tests/py/inet/ether.t
@@ -1,13 +1,20 @@
:input;type filter hook input priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*ip;test-ip4;input
*ip6;test-ip6;input
*inet;test-inet;input
*bridge;test-bridge;input
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
tcp dport 22 iiftype ether ether saddr 00:0f:54:0c:11:4 accept;ok;tcp dport 22 ether saddr 00:0f:54:0c:11:04 accept
tcp dport 22 ether saddr 00:0f:54:0c:11:04 accept;ok
ether saddr 00:0f:54:0c:11:04 accept;ok
+
+vlan id 1;ok
+ether type vlan vlan id 2;ok;vlan id 2
+
+# invalid dependency
+ether type ip vlan id 1;fail
diff --git a/tests/py/inet/ether.t.json b/tests/py/inet/ether.t.json
index 84b184c7..c7a7f886 100644
--- a/tests/py/inet/ether.t.json
+++ b/tests/py/inet/ether.t.json
@@ -88,3 +88,35 @@
}
]
+# vlan id 1
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "id",
+ "protocol": "vlan"
+ }
+ },
+ "op": "==",
+ "right": 1
+ }
+ }
+]
+
+# ether type vlan vlan id 2
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "id",
+ "protocol": "vlan"
+ }
+ },
+ "op": "==",
+ "right": 2
+ }
+ }
+]
+
diff --git a/tests/py/inet/ether.t.payload b/tests/py/inet/ether.t.payload
index 53648413..8b74a781 100644
--- a/tests/py/inet/ether.t.payload
+++ b/tests/py/inet/ether.t.payload
@@ -30,3 +30,23 @@ inet test-inet input
[ cmp eq reg 1 0x0c540f00 0x00000411 ]
[ immediate reg 0 accept ]
+# vlan id 1
+netdev test-netdev ingress
+ [ 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 ]
+ [ cmp eq reg 1 0x00000100 ]
+
+# ether type vlan vlan id 2
+netdev test-netdev ingress
+ [ 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 ]
+ [ cmp eq reg 1 0x00000200 ]
+
diff --git a/tests/py/inet/ether.t.payload.bridge b/tests/py/inet/ether.t.payload.bridge
index e9208008..0128d5f0 100644
--- a/tests/py/inet/ether.t.payload.bridge
+++ b/tests/py/inet/ether.t.payload.bridge
@@ -26,3 +26,19 @@ bridge test-bridge input
[ cmp eq reg 1 0x0c540f00 0x00000411 ]
[ immediate reg 0 accept ]
+# vlan id 1
+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 ]
+ [ cmp eq reg 1 0x00000100 ]
+
+# ether type vlan vlan id 2
+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 ]
+ [ cmp eq reg 1 0x00000200 ]
+
diff --git a/tests/py/inet/ether.t.payload.ip b/tests/py/inet/ether.t.payload.ip
index a604f603..7c91f412 100644
--- a/tests/py/inet/ether.t.payload.ip
+++ b/tests/py/inet/ether.t.payload.ip
@@ -30,3 +30,23 @@ ip test-ip4 input
[ cmp eq reg 1 0x0c540f00 0x00000411 ]
[ immediate reg 0 accept ]
+# vlan id 1
+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 ]
+ [ cmp eq reg 1 0x00000100 ]
+
+# ether type vlan vlan id 2
+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 ]
+ [ cmp eq reg 1 0x00000200 ]
+
diff --git a/tests/py/inet/geneve.t b/tests/py/inet/geneve.t
new file mode 100644
index 00000000..101f6dfc
--- /dev/null
+++ b/tests/py/inet/geneve.t
@@ -0,0 +1,23 @@
+:input;type filter hook input priority 0
+:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
+
+*ip;test-ip4;input
+*ip6;test-ip6;input
+*inet;test-inet;input
+*netdev;test-netdev;ingress,egress
+
+geneve vni 10;fail
+udp dport 6081 geneve vni 10;ok
+udp dport 6081 geneve ip saddr 10.141.11.2;ok
+udp dport 6081 geneve ip saddr 10.141.11.0/24;ok
+udp dport 6081 geneve ip protocol 1;ok
+udp dport 6081 geneve udp sport 8888;ok
+udp dport 6081 geneve icmp type echo-reply;ok
+udp dport 6081 geneve ether saddr 62:87:4d:d6:19:05;ok
+udp dport 6081 geneve vlan id 10;ok
+udp dport 6081 geneve ip dscp 0x02;ok
+udp dport 6081 geneve ip dscp 0x02;ok
+udp dport 6081 geneve ip saddr . geneve ip daddr { 1.2.3.4 . 4.3.2.1 };ok
+
+udp dport 6081 geneve ip saddr set 1.2.3.4;fail
diff --git a/tests/py/inet/geneve.t.json b/tests/py/inet/geneve.t.json
new file mode 100644
index 00000000..a299fcd2
--- /dev/null
+++ b/tests/py/inet/geneve.t.json
@@ -0,0 +1,344 @@
+# udp dport 6081 geneve vni 10
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 6081
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "vni",
+ "protocol": "geneve",
+ "tunnel": "geneve"
+ }
+ },
+ "op": "==",
+ "right": 10
+ }
+ }
+]
+
+# udp dport 6081 geneve ip saddr 10.141.11.2
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 6081
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip",
+ "tunnel": "geneve"
+ }
+ },
+ "op": "==",
+ "right": "10.141.11.2"
+ }
+ }
+]
+
+# udp dport 6081 geneve ip saddr 10.141.11.0/24
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 6081
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip",
+ "tunnel": "geneve"
+ }
+ },
+ "op": "==",
+ "right": {
+ "prefix": {
+ "addr": "10.141.11.0",
+ "len": 24
+ }
+ }
+ }
+ }
+]
+
+# udp dport 6081 geneve ip protocol 1
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 6081
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "protocol",
+ "protocol": "ip",
+ "tunnel": "geneve"
+ }
+ },
+ "op": "==",
+ "right": 1
+ }
+ }
+]
+
+# udp dport 6081 geneve udp sport 8888
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 6081
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "sport",
+ "protocol": "udp",
+ "tunnel": "geneve"
+ }
+ },
+ "op": "==",
+ "right": 8888
+ }
+ }
+]
+
+# udp dport 6081 geneve icmp type echo-reply
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 6081
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "type",
+ "protocol": "icmp",
+ "tunnel": "geneve"
+ }
+ },
+ "op": "==",
+ "right": "echo-reply"
+ }
+ }
+]
+
+# udp dport 6081 geneve ether saddr 62:87:4d:d6:19:05
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 6081
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ether",
+ "tunnel": "geneve"
+ }
+ },
+ "op": "==",
+ "right": "62:87:4d:d6:19:05"
+ }
+ }
+]
+
+# udp dport 6081 geneve vlan id 10
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 6081
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "id",
+ "protocol": "vlan",
+ "tunnel": "geneve"
+ }
+ },
+ "op": "==",
+ "right": 10
+ }
+ }
+]
+
+# udp dport 6081 geneve ip dscp 0x02
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 6081
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip",
+ "tunnel": "geneve"
+ }
+ },
+ "op": "==",
+ "right": 2
+ }
+ }
+]
+
+# udp dport 6081 geneve ip dscp 0x02
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 6081
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip",
+ "tunnel": "geneve"
+ }
+ },
+ "op": "==",
+ "right": 2
+ }
+ }
+]
+
+# udp dport 6081 geneve ip saddr . geneve ip daddr { 1.2.3.4 . 4.3.2.1 }
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 6081
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip",
+ "tunnel": "geneve"
+ }
+ },
+ {
+ "payload": {
+ "field": "daddr",
+ "protocol": "ip",
+ "tunnel": "geneve"
+ }
+ }
+ ]
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ {
+ "concat": [
+ "1.2.3.4",
+ "4.3.2.1"
+ ]
+ }
+ ]
+ }
+ }
+ }
+]
+
diff --git a/tests/py/inet/geneve.t.payload b/tests/py/inet/geneve.t.payload
new file mode 100644
index 00000000..1ce54de6
--- /dev/null
+++ b/tests/py/inet/geneve.t.payload
@@ -0,0 +1,114 @@
+# udp dport 6081 geneve vni 10
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x0000c117 ]
+ [ inner type 2 hdrsize 8 flags f [ payload load 3b @ unknown header + 4 => reg 1 ] ]
+ [ cmp eq reg 1 0x000a0000 ]
+
+# udp dport 6081 geneve ip saddr 10.141.11.2
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x0000c117 ]
+ [ inner type 2 hdrsize 8 flags f [ meta load protocol => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 2 hdrsize 8 flags f [ payload load 4b @ network header + 12 => reg 1 ] ]
+ [ cmp eq reg 1 0x020b8d0a ]
+
+# udp dport 6081 geneve ip saddr 10.141.11.0/24
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x0000c117 ]
+ [ inner type 2 hdrsize 8 flags f [ meta load protocol => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 2 hdrsize 8 flags f [ payload load 3b @ network header + 12 => reg 1 ] ]
+ [ cmp eq reg 1 0x000b8d0a ]
+
+# udp dport 6081 geneve ip protocol 1
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x0000c117 ]
+ [ inner type 2 hdrsize 8 flags f [ meta load protocol => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 2 hdrsize 8 flags f [ payload load 1b @ network header + 9 => reg 1 ] ]
+ [ cmp eq reg 1 0x00000001 ]
+
+# udp dport 6081 geneve udp sport 8888
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x0000c117 ]
+ [ inner type 2 hdrsize 8 flags f [ meta load l4proto => reg 1 ] ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ inner type 2 hdrsize 8 flags f [ payload load 2b @ transport header + 0 => reg 1 ] ]
+ [ cmp eq reg 1 0x0000b822 ]
+
+# udp dport 6081 geneve icmp type echo-reply
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x0000c117 ]
+ [ inner type 2 hdrsize 8 flags f [ payload load 2b @ link header + 12 => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 2 hdrsize 8 flags f [ meta load l4proto => reg 1 ] ]
+ [ cmp eq reg 1 0x00000001 ]
+ [ inner type 2 hdrsize 8 flags f [ payload load 1b @ transport header + 0 => reg 1 ] ]
+ [ cmp eq reg 1 0x00000000 ]
+
+# udp dport 6081 geneve ether saddr 62:87:4d:d6:19:05
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x0000c117 ]
+ [ inner type 2 hdrsize 8 flags f [ payload load 6b @ link header + 6 => reg 1 ] ]
+ [ cmp eq reg 1 0xd64d8762 0x00000519 ]
+
+# udp dport 6081 geneve vlan id 10
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x0000c117 ]
+ [ inner type 2 hdrsize 8 flags f [ payload load 2b @ link header + 12 => reg 1 ] ]
+ [ cmp eq reg 1 0x00000081 ]
+ [ inner type 2 hdrsize 8 flags f [ payload load 2b @ link header + 14 => reg 1 ] ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000ff0f ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000a00 ]
+
+# udp dport 6081 geneve ip dscp 0x02
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x0000c117 ]
+ [ inner type 2 hdrsize 8 flags f [ meta load protocol => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 2 hdrsize 8 flags f [ payload load 1b @ network header + 1 => reg 1 ] ]
+ [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000008 ]
+
+# udp dport 6081 geneve ip saddr . geneve ip daddr { 1.2.3.4 . 4.3.2.1 }
+__set%d test-ip4 3 size 1
+__set%d test-ip4 0
+ element 04030201 01020304 : 0 [end]
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x0000c117 ]
+ [ inner type 2 hdrsize 8 flags f [ meta load protocol => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 2 hdrsize 8 flags f [ payload load 4b @ network header + 12 => reg 1 ] ]
+ [ inner type 2 hdrsize 8 flags f [ payload load 4b @ network header + 16 => reg 9 ] ]
+ [ lookup reg 1 set __set%d ]
+
diff --git a/tests/py/inet/gre.t b/tests/py/inet/gre.t
new file mode 100644
index 00000000..a3e046a1
--- /dev/null
+++ b/tests/py/inet/gre.t
@@ -0,0 +1,22 @@
+:input;type filter hook input priority 0
+:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
+
+*ip;test-ip4;input
+*ip6;test-ip6;input
+*inet;test-inet;input
+*netdev;test-netdev;ingress,egress
+
+gre version 0;ok
+gre ip saddr 10.141.11.2;ok
+gre ip saddr 10.141.11.0/24;ok
+gre ip protocol 1;ok
+gre udp sport 8888;ok
+gre icmp type echo-reply;ok
+gre ether saddr 62:87:4d:d6:19:05;fail
+gre vlan id 10;fail
+gre ip dscp 0x02;ok
+gre ip dscp 0x02;ok
+gre ip saddr . gre ip daddr { 1.2.3.4 . 4.3.2.1 };ok
+
+gre ip saddr set 1.2.3.4;fail
diff --git a/tests/py/inet/gre.t.json b/tests/py/inet/gre.t.json
new file mode 100644
index 00000000..c4431764
--- /dev/null
+++ b/tests/py/inet/gre.t.json
@@ -0,0 +1,177 @@
+# gre version 0
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "version",
+ "protocol": "gre"
+ }
+ },
+ "op": "==",
+ "right": 0
+ }
+ }
+]
+
+# gre ip saddr 10.141.11.2
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip",
+ "tunnel": "gre"
+ }
+ },
+ "op": "==",
+ "right": "10.141.11.2"
+ }
+ }
+]
+
+# gre ip saddr 10.141.11.0/24
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip",
+ "tunnel": "gre"
+ }
+ },
+ "op": "==",
+ "right": {
+ "prefix": {
+ "addr": "10.141.11.0",
+ "len": 24
+ }
+ }
+ }
+ }
+]
+
+# gre ip protocol 1
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "protocol",
+ "protocol": "ip",
+ "tunnel": "gre"
+ }
+ },
+ "op": "==",
+ "right": 1
+ }
+ }
+]
+
+# gre udp sport 8888
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "sport",
+ "protocol": "udp",
+ "tunnel": "gre"
+ }
+ },
+ "op": "==",
+ "right": 8888
+ }
+ }
+]
+
+# gre icmp type echo-reply
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "type",
+ "protocol": "icmp",
+ "tunnel": "gre"
+ }
+ },
+ "op": "==",
+ "right": "echo-reply"
+ }
+ }
+]
+
+# gre ip dscp 0x02
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip",
+ "tunnel": "gre"
+ }
+ },
+ "op": "==",
+ "right": 2
+ }
+ }
+]
+
+# gre ip dscp 0x02
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip",
+ "tunnel": "gre"
+ }
+ },
+ "op": "==",
+ "right": 2
+ }
+ }
+]
+
+# gre ip saddr . gre ip daddr { 1.2.3.4 . 4.3.2.1 }
+[
+ {
+ "match": {
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip",
+ "tunnel": "gre"
+ }
+ },
+ {
+ "payload": {
+ "field": "daddr",
+ "protocol": "ip",
+ "tunnel": "gre"
+ }
+ }
+ ]
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ {
+ "concat": [
+ "1.2.3.4",
+ "4.3.2.1"
+ ]
+ }
+ ]
+ }
+ }
+ }
+]
+
diff --git a/tests/py/inet/gre.t.payload b/tests/py/inet/gre.t.payload
new file mode 100644
index 00000000..333133ed
--- /dev/null
+++ b/tests/py/inet/gre.t.payload
@@ -0,0 +1,78 @@
+# gre version 0
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000002f ]
+ [ payload load 1b @ transport header + 1 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000007 ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000000 ]
+
+# gre ip saddr 10.141.11.2
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000002f ]
+ [ inner type 3 hdrsize 4 flags c [ meta load protocol => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 3 hdrsize 4 flags c [ payload load 4b @ network header + 12 => reg 1 ] ]
+ [ cmp eq reg 1 0x020b8d0a ]
+
+# gre ip saddr 10.141.11.0/24
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000002f ]
+ [ inner type 3 hdrsize 4 flags c [ meta load protocol => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 3 hdrsize 4 flags c [ payload load 3b @ network header + 12 => reg 1 ] ]
+ [ cmp eq reg 1 0x000b8d0a ]
+
+# gre ip protocol 1
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000002f ]
+ [ inner type 3 hdrsize 4 flags c [ meta load protocol => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 3 hdrsize 4 flags c [ payload load 1b @ network header + 9 => reg 1 ] ]
+ [ cmp eq reg 1 0x00000001 ]
+
+# gre udp sport 8888
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000002f ]
+ [ inner type 3 hdrsize 4 flags c [ meta load l4proto => reg 1 ] ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ inner type 3 hdrsize 4 flags c [ payload load 2b @ transport header + 0 => reg 1 ] ]
+ [ cmp eq reg 1 0x0000b822 ]
+
+# gre icmp type echo-reply
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000002f ]
+ [ inner type 3 hdrsize 4 flags c [ meta load protocol => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 3 hdrsize 4 flags c [ meta load l4proto => reg 1 ] ]
+ [ cmp eq reg 1 0x00000001 ]
+ [ inner type 3 hdrsize 4 flags c [ payload load 1b @ transport header + 0 => reg 1 ] ]
+ [ cmp eq reg 1 0x00000000 ]
+
+# gre ip dscp 0x02
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000002f ]
+ [ inner type 3 hdrsize 4 flags c [ meta load protocol => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 3 hdrsize 4 flags c [ payload load 1b @ network header + 1 => reg 1 ] ]
+ [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000008 ]
+
+# gre ip saddr . gre ip daddr { 1.2.3.4 . 4.3.2.1 }
+__set%d test-ip4 3 size 1
+__set%d test-ip4 0
+ element 04030201 01020304 : 0 [end]
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000002f ]
+ [ inner type 3 hdrsize 4 flags c [ meta load protocol => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 3 hdrsize 4 flags c [ payload load 4b @ network header + 12 => reg 1 ] ]
+ [ inner type 3 hdrsize 4 flags c [ payload load 4b @ network header + 16 => reg 9 ] ]
+ [ lookup reg 1 set __set%d ]
+
diff --git a/tests/py/inet/gretap.t b/tests/py/inet/gretap.t
new file mode 100644
index 00000000..cd7ee215
--- /dev/null
+++ b/tests/py/inet/gretap.t
@@ -0,0 +1,21 @@
+:input;type filter hook input priority 0
+:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
+
+*ip;test-ip4;input
+*ip6;test-ip6;input
+*inet;test-inet;input
+*netdev;test-netdev;ingress,egress
+
+gretap ip saddr 10.141.11.2;ok
+gretap ip saddr 10.141.11.0/24;ok
+gretap ip protocol 1;ok
+gretap udp sport 8888;ok
+gretap icmp type echo-reply;ok
+gretap ether saddr 62:87:4d:d6:19:05;ok
+gretap vlan id 10;ok
+gretap ip dscp 0x02;ok
+gretap ip dscp 0x02;ok
+gretap ip saddr . gretap ip daddr { 1.2.3.4 . 4.3.2.1 };ok
+
+gretap ip saddr set 1.2.3.4;fail
diff --git a/tests/py/inet/gretap.t.json b/tests/py/inet/gretap.t.json
new file mode 100644
index 00000000..36fa9782
--- /dev/null
+++ b/tests/py/inet/gretap.t.json
@@ -0,0 +1,195 @@
+# gretap ip saddr 10.141.11.2
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip",
+ "tunnel": "gretap"
+ }
+ },
+ "op": "==",
+ "right": "10.141.11.2"
+ }
+ }
+]
+
+# gretap ip saddr 10.141.11.0/24
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip",
+ "tunnel": "gretap"
+ }
+ },
+ "op": "==",
+ "right": {
+ "prefix": {
+ "addr": "10.141.11.0",
+ "len": 24
+ }
+ }
+ }
+ }
+]
+
+# gretap ip protocol 1
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "protocol",
+ "protocol": "ip",
+ "tunnel": "gretap"
+ }
+ },
+ "op": "==",
+ "right": 1
+ }
+ }
+]
+
+# gretap udp sport 8888
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "sport",
+ "protocol": "udp",
+ "tunnel": "gretap"
+ }
+ },
+ "op": "==",
+ "right": 8888
+ }
+ }
+]
+
+# gretap icmp type echo-reply
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "type",
+ "protocol": "icmp",
+ "tunnel": "gretap"
+ }
+ },
+ "op": "==",
+ "right": "echo-reply"
+ }
+ }
+]
+
+# gretap ether saddr 62:87:4d:d6:19:05
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ether",
+ "tunnel": "gretap"
+ }
+ },
+ "op": "==",
+ "right": "62:87:4d:d6:19:05"
+ }
+ }
+]
+
+# gretap vlan id 10
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "id",
+ "protocol": "vlan",
+ "tunnel": "gretap"
+ }
+ },
+ "op": "==",
+ "right": 10
+ }
+ }
+]
+
+# gretap ip dscp 0x02
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip",
+ "tunnel": "gretap"
+ }
+ },
+ "op": "==",
+ "right": 2
+ }
+ }
+]
+
+# gretap ip dscp 0x02
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip",
+ "tunnel": "gretap"
+ }
+ },
+ "op": "==",
+ "right": 2
+ }
+ }
+]
+
+# gretap ip saddr . gretap ip daddr { 1.2.3.4 . 4.3.2.1 }
+[
+ {
+ "match": {
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip",
+ "tunnel": "gretap"
+ }
+ },
+ {
+ "payload": {
+ "field": "daddr",
+ "protocol": "ip",
+ "tunnel": "gretap"
+ }
+ }
+ ]
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ {
+ "concat": [
+ "1.2.3.4",
+ "4.3.2.1"
+ ]
+ }
+ ]
+ }
+ }
+ }
+]
+
diff --git a/tests/py/inet/gretap.t.payload b/tests/py/inet/gretap.t.payload
new file mode 100644
index 00000000..654c71e4
--- /dev/null
+++ b/tests/py/inet/gretap.t.payload
@@ -0,0 +1,87 @@
+# gretap ip saddr 10.141.11.2
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000002f ]
+ [ inner type 4 hdrsize 4 flags e [ meta load protocol => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 4 hdrsize 4 flags e [ payload load 4b @ network header + 12 => reg 1 ] ]
+ [ cmp eq reg 1 0x020b8d0a ]
+
+# gretap ip saddr 10.141.11.0/24
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000002f ]
+ [ inner type 4 hdrsize 4 flags e [ meta load protocol => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 4 hdrsize 4 flags e [ payload load 3b @ network header + 12 => reg 1 ] ]
+ [ cmp eq reg 1 0x000b8d0a ]
+
+# gretap ip protocol 1
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000002f ]
+ [ inner type 4 hdrsize 4 flags e [ meta load protocol => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 4 hdrsize 4 flags e [ payload load 1b @ network header + 9 => reg 1 ] ]
+ [ cmp eq reg 1 0x00000001 ]
+
+# gretap udp sport 8888
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000002f ]
+ [ inner type 4 hdrsize 4 flags e [ meta load l4proto => reg 1 ] ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ inner type 4 hdrsize 4 flags e [ payload load 2b @ transport header + 0 => reg 1 ] ]
+ [ cmp eq reg 1 0x0000b822 ]
+
+# gretap icmp type echo-reply
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000002f ]
+ [ inner type 4 hdrsize 4 flags e [ payload load 2b @ link header + 12 => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 4 hdrsize 4 flags e [ meta load l4proto => reg 1 ] ]
+ [ cmp eq reg 1 0x00000001 ]
+ [ inner type 4 hdrsize 4 flags e [ payload load 1b @ transport header + 0 => reg 1 ] ]
+ [ cmp eq reg 1 0x00000000 ]
+
+# gretap ether saddr 62:87:4d:d6:19:05
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000002f ]
+ [ inner type 4 hdrsize 4 flags e [ payload load 6b @ link header + 6 => reg 1 ] ]
+ [ cmp eq reg 1 0xd64d8762 0x00000519 ]
+
+# gretap vlan id 10
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000002f ]
+ [ inner type 4 hdrsize 4 flags e [ payload load 2b @ link header + 12 => reg 1 ] ]
+ [ cmp eq reg 1 0x00000081 ]
+ [ inner type 4 hdrsize 4 flags e [ payload load 2b @ link header + 14 => reg 1 ] ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000ff0f ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000a00 ]
+
+# gretap ip dscp 0x02
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000002f ]
+ [ inner type 4 hdrsize 4 flags e [ meta load protocol => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 4 hdrsize 4 flags e [ payload load 1b @ network header + 1 => reg 1 ] ]
+ [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000008 ]
+
+# gretap ip saddr . gretap ip daddr { 1.2.3.4 . 4.3.2.1 }
+__set%d test-ip4 3 size 1
+__set%d test-ip4 0
+ element 04030201 01020304 : 0 [end]
+ip test-ip4 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000002f ]
+ [ inner type 4 hdrsize 4 flags e [ meta load protocol => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 4 hdrsize 4 flags e [ payload load 4b @ network header + 12 => reg 1 ] ]
+ [ inner type 4 hdrsize 4 flags e [ payload load 4b @ network header + 16 => reg 9 ] ]
+ [ lookup reg 1 set __set%d ]
+
diff --git a/tests/py/inet/icmpX.t b/tests/py/inet/icmpX.t
index 97ff96d0..9430b3d3 100644
--- a/tests/py/inet/icmpX.t
+++ b/tests/py/inet/icmpX.t
@@ -7,4 +7,4 @@ icmp type echo-request;ok
ip6 nexthdr icmpv6 icmpv6 type echo-request;ok;ip6 nexthdr 58 icmpv6 type echo-request
icmpv6 type echo-request;ok
# must not remove 'ip protocol' dependency, this explicitly matches icmpv6-in-ipv4.
-ip protocol ipv6-icmp meta l4proto ipv6-icmp icmpv6 type 1;ok;ip protocol 58 meta l4proto 58 icmpv6 type destination-unreachable
+ip protocol ipv6-icmp meta l4proto ipv6-icmp icmpv6 type 1;ok;ip protocol 58 icmpv6 type destination-unreachable
diff --git a/tests/py/inet/icmpX.t.json.output b/tests/py/inet/icmpX.t.json.output
index 9b0bf9f7..7765cd90 100644
--- a/tests/py/inet/icmpX.t.json.output
+++ b/tests/py/inet/icmpX.t.json.output
@@ -71,15 +71,6 @@
{
"match": {
"left": {
- "meta": { "key": "l4proto" }
- },
- "op": "==",
- "right": 58
- }
- },
- {
- "match": {
- "left": {
"payload": {
"field": "type",
"protocol": "icmpv6"
diff --git a/tests/py/inet/ip.t b/tests/py/inet/ip.t
index ac5b825e..bdb3330c 100644
--- a/tests/py/inet/ip.t
+++ b/tests/py/inet/ip.t
@@ -1,10 +1,11 @@
:input;type filter hook input priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*ip;test-ip4;input
*inet;test-inet;input
*bridge;test-bridge;input
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
ip saddr . ip daddr . ether saddr { 1.1.1.1 . 2.2.2.2 . ca:fe:ca:fe:ca:fe };ok
ip saddr vmap { 10.0.1.0-10.0.1.255 : accept, 10.0.1.1-10.0.2.255 : drop };fail
diff --git a/tests/py/inet/ip.t.payload.bridge b/tests/py/inet/ip.t.payload.bridge
index a422ed76..57dbc9eb 100644
--- a/tests/py/inet/ip.t.payload.bridge
+++ b/tests/py/inet/ip.t.payload.bridge
@@ -3,7 +3,7 @@ __set%d test-bridge 3
__set%d test-bridge 0
element 01010101 02020202 fecafeca 0000feca : 0 [end]
bridge test-bridge input
- [ payload load 2b @ link header + 12 => reg 1 ]
+ [ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ payload load 4b @ network header + 12 => reg 1 ]
[ payload load 4b @ network header + 16 => reg 9 ]
diff --git a/tests/py/inet/ip_tcp.t b/tests/py/inet/ip_tcp.t
index f2a28ebd..03bafc09 100644
--- a/tests/py/inet/ip_tcp.t
+++ b/tests/py/inet/ip_tcp.t
@@ -1,15 +1,16 @@
:input;type filter hook input priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*inet;test-inet;input
*bridge;test-bridge;input
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
# must not remove ip dependency -- ONLY ipv4 packets should be matched
ip protocol tcp tcp dport 22;ok;ip protocol 6 tcp dport 22
-# can remove it here, ip protocol is implied via saddr.
-ip protocol tcp ip saddr 1.2.3.4 tcp dport 22;ok;ip saddr 1.2.3.4 tcp dport 22
+# could in principle remove it here since ipv4 is implied via saddr.
+ip protocol tcp ip saddr 1.2.3.4 tcp dport 22;ok;ip protocol 6 ip saddr 1.2.3.4 tcp dport 22
# but not here.
ip protocol tcp counter ip saddr 1.2.3.4 tcp dport 22;ok;ip protocol 6 counter ip saddr 1.2.3.4 tcp dport 22
diff --git a/tests/py/inet/ip_tcp.t.json.output b/tests/py/inet/ip_tcp.t.json.output
index 4a6a05d7..acad8b1f 100644
--- a/tests/py/inet/ip_tcp.t.json.output
+++ b/tests/py/inet/ip_tcp.t.json.output
@@ -32,6 +32,18 @@
"match": {
"left": {
"payload": {
+ "field": "protocol",
+ "protocol": "ip"
+ }
+ },
+ "op": "==",
+ "right": 6
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
"field": "saddr",
"protocol": "ip"
}
diff --git a/tests/py/inet/ipsec.t b/tests/py/inet/ipsec.t
index e924e9bc..b18df395 100644
--- a/tests/py/inet/ipsec.t
+++ b/tests/py/inet/ipsec.t
@@ -19,3 +19,5 @@ ipsec in ip6 daddr dead::beef;ok
ipsec out ip6 saddr dead::feed;ok
ipsec in spnum 256 reqid 1;fail
+
+counter ipsec out ip daddr 192.168.1.2;ok
diff --git a/tests/py/inet/ipsec.t.json b/tests/py/inet/ipsec.t.json
index d7d3a03c..18a64f35 100644
--- a/tests/py/inet/ipsec.t.json
+++ b/tests/py/inet/ipsec.t.json
@@ -134,3 +134,24 @@
}
}
]
+
+# counter ipsec out ip daddr 192.168.1.2
+[
+ {
+ "counter": null
+ },
+ {
+ "match": {
+ "left": {
+ "ipsec": {
+ "dir": "out",
+ "family": "ip",
+ "key": "daddr",
+ "spnum": 0
+ }
+ },
+ "op": "==",
+ "right": "192.168.1.2"
+ }
+ }
+]
diff --git a/tests/py/inet/ipsec.t.payload b/tests/py/inet/ipsec.t.payload
index c46a2263..9648255d 100644
--- a/tests/py/inet/ipsec.t.payload
+++ b/tests/py/inet/ipsec.t.payload
@@ -37,3 +37,9 @@ ip ipsec-ip4 ipsec-forw
[ xfrm load out 0 saddr6 => reg 1 ]
[ cmp eq reg 1 0x0000adde 0x00000000 0x00000000 0xedfe0000 ]
+# counter ipsec out ip daddr 192.168.1.2
+ip ipsec-ip4 ipsec-forw
+ [ counter pkts 0 bytes 0 ]
+ [ xfrm load out 0 daddr4 => reg 1 ]
+ [ cmp eq reg 1 0x0201a8c0 ]
+
diff --git a/tests/py/inet/map.t b/tests/py/inet/map.t
index e83490a8..5a7161b7 100644
--- a/tests/py/inet/map.t
+++ b/tests/py/inet/map.t
@@ -1,9 +1,10 @@
:input;type filter hook input priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*ip;test-ip4;input
*inet;test-inet;input
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
mark set ip saddr map { 10.2.3.2 : 0x0000002a, 10.2.3.1 : 0x00000017};ok;meta mark set ip saddr map { 10.2.3.1 : 0x00000017, 10.2.3.2 : 0x0000002a}
mark set ip hdrlength map { 5 : 0x00000017, 4 : 0x00000001};ok;meta mark set ip hdrlength map { 4 : 0x00000001, 5 : 0x00000017}
diff --git a/tests/py/inet/meta.t b/tests/py/inet/meta.t
index 423cc5f3..7d2515c9 100644
--- a/tests/py/inet/meta.t
+++ b/tests/py/inet/meta.t
@@ -21,3 +21,13 @@ meta secpath missing;ok;meta ipsec missing
meta ibrname "br0";fail
meta obrname "br0";fail
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
+meta mark set ip6 dscp;ok
+meta mark set ip6 dscp | 0x40;ok
diff --git a/tests/py/inet/meta.t.json b/tests/py/inet/meta.t.json
index 723a36f7..0fee165f 100644
--- a/tests/py/inet/meta.t.json
+++ b/tests/py/inet/meta.t.json
@@ -289,3 +289,281 @@
}
}
]
+
+# meta mark . tcp dport { 0x0000000a-0x00000014 . 80-90, 0x00100000-0x00100123 . 100-120 }
+[
+ {
+ "match": {
+ "left": {
+ "concat": [
+ {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ }
+ ]
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ {
+ "concat": [
+ {
+ "range": [
+ 10,
+ 20
+ ]
+ },
+ {
+ "range": [
+ 80,
+ 90
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "range": [
+ 1048576,
+ 1048867
+ ]
+ },
+ {
+ "range": [
+ 100,
+ 120
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+]
+
+# ip saddr . meta mark { 1.2.3.4 . 0x00000100 , 1.2.3.6-1.2.3.8 . 0x00000200-0x00000300 }
+[
+ {
+ "match": {
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ },
+ {
+ "meta": {
+ "key": "mark"
+ }
+ }
+ ]
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ {
+ "concat": [
+ "1.2.3.4",
+ 256
+ ]
+ },
+ {
+ "concat": [
+ {
+ "range": [
+ "1.2.3.6",
+ "1.2.3.8"
+ ]
+ },
+ {
+ "range": [
+ 512,
+ 768
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+]
+
+# ip saddr . meta mark { 1.2.3.4 . 0x00000100 , 5.6.7.8 . 0x00000200 }
+[
+ {
+ "match": {
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ },
+ {
+ "meta": {
+ "key": "mark"
+ }
+ }
+ ]
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ {
+ "concat": [
+ "1.2.3.4",
+ 256
+ ]
+ },
+ {
+ "concat": [
+ "5.6.7.8",
+ 512
+ ]
+ }
+ ]
+ }
+ }
+ }
+]
+
+# meta mark set ip dscp
+[
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip"
+ }
+ }
+ }
+ }
+]
+
+# meta mark set ip dscp | 0x40
+[
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip"
+ }
+ },
+ 64
+ ]
+ }
+ }
+ }
+]
+
+# meta mark set ip6 dscp
+[
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip6"
+ }
+ }
+ }
+ }
+]
+
+# meta mark set ip6 dscp | 0x40
+[
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip6"
+ }
+ },
+ 64
+ ]
+ }
+ }
+ }
+]
+
+# 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 3e7dd214..8697d5a2 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 fd054549..7184fa0c 100644
--- a/tests/py/inet/meta.t.payload
+++ b/tests/py/inet/meta.t.payload
@@ -97,3 +97,93 @@ inet test-inet input
[ cmp eq reg 1 0x00000011 ]
[ payload load 2b @ transport header + 2 => reg 1 ]
[ cmp eq reg 1 0x00004300 ]
+
+# meta mark . tcp dport { 0x0000000a-0x00000014 . 80-90, 0x00100000-0x00100123 . 100-120 }
+__set%d test-inet 87 size 1
+__set%d test-inet 0
+ element 0a000000 00005000 - 14000000 00005a00 : 0 [end] element 00001000 00006400 - 23011000 00007800 : 0 [end]
+ip test-inet input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ meta load mark => reg 1 ]
+ [ byteorder reg 1 = hton(reg 1, 4, 4) ]
+ [ payload load 2b @ transport header + 2 => reg 9 ]
+ [ lookup reg 1 set __set%d ]
+
+# ip saddr . meta mark { 1.2.3.4 . 0x00000100 , 1.2.3.6-1.2.3.8 . 0x00000200-0x00000300 }
+__set%d test-inet 87 size 2
+__set%d test-inet 0
+ element 04030201 00010000 - 04030201 00010000 : 0 [end] element 06030201 00020000 - 08030201 00030000 : 0 [end]
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x00000002 ]
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ meta load mark => reg 9 ]
+ [ byteorder reg 9 = hton(reg 9, 4, 4) ]
+ [ lookup reg 1 set __set%d ]
+
+# ip saddr . meta mark { 1.2.3.4 . 0x00000100 , 5.6.7.8 . 0x00000200 }
+__set%d test-inet 3 size 2
+__set%d test-inet 0
+ element 04030201 00000100 : 0 [end] element 08070605 00000200 : 0 [end]
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x00000002 ]
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ meta load mark => reg 9 ]
+ [ lookup reg 1 set __set%d ]
+
+# meta mark set ip dscp
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x00000002 ]
+ [ payload load 1b @ network header + 1 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000002 ) ]
+ [ meta set mark with reg 1 ]
+
+# meta mark set ip dscp | 0x40
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x00000002 ]
+ [ payload load 1b @ network header + 1 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000002 ) ]
+ [ bitwise reg 1 = ( reg 1 & 0xffffffbf ) ^ 0x00000040 ]
+ [ meta set mark with reg 1 ]
+
+# meta mark set ip6 dscp
+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 ) ]
+ [ meta set mark with reg 1 ]
+
+# meta mark set ip6 dscp | 0x40
+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 ) ]
+ [ 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 ]
+
diff --git a/tests/py/inet/payloadmerge.t b/tests/py/inet/payloadmerge.t
new file mode 100644
index 00000000..04ba1ce6
--- /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 00000000..e5b66cf9
--- /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 00000000..a0465cdd
--- /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 ]
+
diff --git a/tests/py/inet/reject.t b/tests/py/inet/reject.t
index 1c8aeebe..61a6d556 100644
--- a/tests/py/inet/reject.t
+++ b/tests/py/inet/reject.t
@@ -37,3 +37,5 @@ meta l4proto udp reject with tcp reset;fail
meta nfproto ipv4 reject with icmpx admin-prohibited;ok
meta nfproto ipv6 reject with icmpx admin-prohibited;ok
+
+ether saddr aa:bb:cc:dd:ee:ff ip daddr 192.168.0.1 reject;ok;ether saddr aa:bb:cc:dd:ee:ff ip daddr 192.168.0.1 reject with icmp port-unreachable
diff --git a/tests/py/inet/reject.t.json b/tests/py/inet/reject.t.json
index 76cd1bf5..02ac9007 100644
--- a/tests/py/inet/reject.t.json
+++ b/tests/py/inet/reject.t.json
@@ -295,3 +295,37 @@
}
]
+# ether saddr aa:bb:cc:dd:ee:ff ip daddr 192.168.0.1 reject
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ether"
+ }
+ },
+ "op": "==",
+ "right": "aa:bb:cc:dd:ee:ff"
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "daddr",
+ "protocol": "ip"
+ }
+ },
+ "op": "==",
+ "right": "192.168.0.1"
+ }
+ },
+ {
+ "reject": {
+ "expr": "port-unreachable",
+ "type": "icmp"
+ }
+ }
+]
+
diff --git a/tests/py/inet/reject.t.payload.inet b/tests/py/inet/reject.t.payload.inet
index 62078d91..828cb839 100644
--- a/tests/py/inet/reject.t.payload.inet
+++ b/tests/py/inet/reject.t.payload.inet
@@ -132,3 +132,13 @@ inet test-inet input
[ cmp eq reg 1 0x0000000a ]
[ reject type 2 code 3 ]
+# ether saddr aa:bb:cc:dd:ee:ff ip daddr 192.168.0.1 reject
+inet test-inet input
+ [ meta load iiftype => reg 1 ]
+ [ cmp eq reg 1 0x00000001 ]
+ [ payload load 8b @ link header + 6 => reg 1 ]
+ [ cmp eq reg 1 0xddccbbaa 0x0008ffee ]
+ [ payload load 4b @ network header + 16 => reg 1 ]
+ [ cmp eq reg 1 0x0100a8c0 ]
+ [ reject type 0 code 3 ]
+
diff --git a/tests/py/inet/sctp.t b/tests/py/inet/sctp.t
index 57b9e67b..016173b9 100644
--- a/tests/py/inet/sctp.t
+++ b/tests/py/inet/sctp.t
@@ -1,10 +1,11 @@
:input;type filter hook input priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*ip;test-ip4;input
*ip6;test-ip6;input
*inet;test-inet;input
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
sctp sport 23;ok
sctp sport != 23;ok
diff --git a/tests/py/inet/sets.t b/tests/py/inet/sets.t
index 1c6f3235..5b22e1fe 100644
--- a/tests/py/inet/sets.t
+++ b/tests/py/inet/sets.t
@@ -1,9 +1,10 @@
:input;type filter hook input priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*inet;test-inet;input
*bridge;test-inet;input
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
!set1 type ipv4_addr timeout 60s;ok
?set1 192.168.3.4 timeout 30s, 10.2.1.1;ok
diff --git a/tests/py/inet/sets.t.json b/tests/py/inet/sets.t.json
index ef0cedca..b44ffc20 100644
--- a/tests/py/inet/sets.t.json
+++ b/tests/py/inet/sets.t.json
@@ -76,17 +76,6 @@
{
"match": {
"left": {
- "meta": {
- "key": "nfproto"
- }
- },
- "op": "==",
- "right": "ipv4"
- }
- },
- {
- "match": {
- "left": {
"concat": [
{
"payload": {
diff --git a/tests/py/inet/sets.t.payload.netdev b/tests/py/inet/sets.t.payload.netdev
index 9d6f6bbd..e31aeb92 100644
--- a/tests/py/inet/sets.t.payload.netdev
+++ b/tests/py/inet/sets.t.payload.netdev
@@ -15,9 +15,9 @@ netdev test-netdev ingress
[ immediate reg 0 accept ]
# ip saddr . ip daddr . tcp dport @set3 accept
-inet
- [ meta load nfproto => reg 1 ]
- [ cmp eq reg 1 0x00000002 ]
+netdev
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x00000008 ]
[ meta load l4proto => reg 1 ]
[ cmp eq reg 1 0x00000006 ]
[ payload load 4b @ network header + 12 => reg 1 ]
diff --git a/tests/py/inet/tcp.t b/tests/py/inet/tcp.t
index aa07c3ba..f4bdac17 100644
--- a/tests/py/inet/tcp.t
+++ b/tests/py/inet/tcp.t
@@ -1,10 +1,11 @@
:input;type filter hook input priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*ip;test-ip4;input
*ip6;test-ip6;input
*inet;test-inet;input
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
tcp dport set {1, 2, 3};fail
@@ -67,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
@@ -76,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 8439c2b5..28dd4341 100644
--- a/tests/py/inet/tcp.t.json
+++ b/tests/py/inet/tcp.t.json
@@ -954,12 +954,12 @@
}
},
{
- "|": [ "fin", { "|": [ "syn", { "|": [ "rst", { "|": [ "psh", { "|": [ "ack", { "|": [ "urg", { "|": [ "ecn", "cwr" ] } ] } ] } ] } ] } ] } ]
+ "|": [ "fin", "syn", "rst", "psh", "ack", "urg", "ecn", "cwr" ]
}
]
},
"op": "==",
- "right": { "|": [ "fin", { "|": [ "syn", { "|": [ "rst", { "|": [ "psh", { "|": [ "ack", { "|": [ "urg", { "|": [ "ecn", "cwr" ] } ] } ] } ] } ] } ] } ] }
+ "right": { "|": [ "fin", "syn", "rst", "psh", "ack", "urg", "ecn", "cwr" ] }
}
}
]
@@ -1370,13 +1370,13 @@
"op": "==",
"right": {
"set": [
+ "syn",
{
"|": [
"syn",
"ack"
]
- },
- "syn"
+ }
]
}
}
@@ -1395,56 +1395,16 @@
"protocol": "tcp"
}
},
- {
- "|": [
- {
- "|": [
- {
- "|": [
- {
- "|": [
- {
- "|": [
- "fin",
- "syn"
- ]
- },
- "rst"
- ]
- },
- "psh"
- ]
- },
- "ack"
- ]
- },
- "urg"
- ]
- }
+ { "|": [ "fin", "syn", "rst", "psh", "ack", "urg" ] }
]
},
"op": "==",
"right": {
"set": [
- {
- "|": [
- {
- "|": [
- "fin",
- "psh"
- ]
- },
- "ack"
- ]
- },
"fin",
- {
- "|": [
- "psh",
- "ack"
- ]
- },
- "ack"
+ "ack",
+ { "|": [ "psh", "ack" ] },
+ { "|": [ "fin", "psh", "ack" ] }
]
}
}
@@ -1482,17 +1442,21 @@
"protocol": "tcp"
}
},
- [
- "fin",
- "syn"
- ]
+ {
+ "|": [
+ "fin",
+ "syn"
+ ]
+ }
]
},
"op": "==",
- "right": [
- "fin",
- "syn"
- ]
+ "right": {
+ "|": [
+ "fin",
+ "syn"
+ ]
+ }
}
}
]
@@ -1509,10 +1473,12 @@
"protocol": "tcp"
}
},
- [
- "fin",
- "syn"
- ]
+ {
+ "|": [
+ "fin",
+ "syn"
+ ]
+ }
]
},
"op": "!=",
@@ -1645,12 +1611,14 @@
"protocol": "tcp"
}
},
- [
- "fin",
- "syn",
- "rst",
- "ack"
- ]
+ {
+ "|": [
+ "fin",
+ "syn",
+ "rst",
+ "ack"
+ ]
+ }
]
},
"op": "==",
@@ -1671,12 +1639,14 @@
"protocol": "tcp"
}
},
- [
- "fin",
- "syn",
- "rst",
- "ack"
- ]
+ {
+ "|": [
+ "fin",
+ "syn",
+ "rst",
+ "ack"
+ ]
+ }
]
},
"op": "==",
@@ -1698,12 +1668,14 @@
"protocol": "tcp"
}
},
- [
- "fin",
- "syn",
- "rst",
- "ack"
- ]
+ {
+ "|": [
+ "fin",
+ "syn",
+ "rst",
+ "ack"
+ ]
+ }
]
},
"op": "!=",
@@ -1712,7 +1684,7 @@
}
]
-# tcp flags & (fin | syn | rst | ack) == (syn | ack)
+# tcp flags & (fin | syn | rst | ack) == syn | ack
[
{
"match": {
@@ -1724,24 +1696,28 @@
"protocol": "tcp"
}
},
- [
- "fin",
- "syn",
- "rst",
- "ack"
- ]
+ {
+ "|": [
+ "fin",
+ "syn",
+ "rst",
+ "ack"
+ ]
+ }
]
},
"op": "==",
- "right": [
- "syn",
- "ack"
- ]
+ "right": {
+ "|": [
+ "syn",
+ "ack"
+ ]
+ }
}
}
]
-# tcp flags & (fin | syn | rst | ack) != (syn | ack)
+# tcp flags & (syn | ack) == syn | ack
[
{
"match": {
@@ -1753,24 +1729,26 @@
"protocol": "tcp"
}
},
- [
- "fin",
- "syn",
- "rst",
- "ack"
- ]
+ {
+ "|": [
+ "syn",
+ "ack"
+ ]
+ }
]
},
- "op": "!=",
- "right": [
- "syn",
- "ack"
- ]
+ "op": "==",
+ "right": {
+ "|": [
+ "syn",
+ "ack"
+ ]
+ }
}
}
]
-# tcp flags & (syn | ack) == (syn | ack)
+# tcp flags & (fin | syn | rst | ack) != syn | ack
[
{
"match": {
@@ -1782,17 +1760,16 @@
"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 c471e8d8..d487a8f1 100644
--- a/tests/py/inet/tcp.t.json.output
+++ b/tests/py/inet/tcp.t.json.output
@@ -115,32 +115,6 @@
}
]
-# tcp flags { syn, syn | ack }
-[
- {
- "match": {
- "left": {
- "payload": {
- "field": "flags",
- "protocol": "tcp"
- }
- },
- "op": "==",
- "right": {
- "set": [
- "syn",
- {
- "|": [
- "syn",
- "ack"
- ]
- }
- ]
- }
- }
- }
-]
-
# tcp flags & (fin | syn | rst | psh | ack | urg) == { fin, ack, psh | ack, fin | psh | ack }
[
{
@@ -155,27 +129,11 @@
},
{
"|": [
- {
- "|": [
- {
- "|": [
- {
- "|": [
- {
- "|": [
- "fin",
- "syn"
- ]
- },
- "rst"
- ]
- },
- "psh"
- ]
- },
- "ack"
- ]
- },
+ "fin",
+ "syn",
+ "rst",
+ "psh",
+ "ack",
"urg"
]
}
@@ -187,12 +145,8 @@
"fin",
{
"|": [
- {
- "|": [
- "fin",
- "psh"
- ]
- },
+ "fin",
+ "psh",
"ack"
]
},
diff --git a/tests/py/inet/tcp.t.payload b/tests/py/inet/tcp.t.payload
index 1cfe500b..bc6bb989 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/py/inet/tproxy.t b/tests/py/inet/tproxy.t
index d23bbcb5..9901df75 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 7b3b11c4..71b6fd2f 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 24bf8f60..2f419042 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 ]
+
diff --git a/tests/py/inet/udp.t b/tests/py/inet/udp.t
index c434f2ed..7f21c8ed 100644
--- a/tests/py/inet/udp.t
+++ b/tests/py/inet/udp.t
@@ -1,10 +1,11 @@
:input;type filter hook input priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*ip;test-ip4;input
*ip6;test-ip6;input
*inet;test-inet;input
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
udp sport 80 accept;ok
udp sport != 60 accept;ok
diff --git a/tests/py/inet/udplite.t b/tests/py/inet/udplite.t
index a8fdc8ea..6a54709c 100644
--- a/tests/py/inet/udplite.t
+++ b/tests/py/inet/udplite.t
@@ -1,10 +1,11 @@
:input;type filter hook input priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*ip;test-ip4;input
*ip6;test-ip6;input
*inet;test-inet;input
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
udplite sport 80 accept;ok
udplite sport != 60 accept;ok
diff --git a/tests/py/inet/vmap.t b/tests/py/inet/vmap.t
new file mode 100644
index 00000000..0ac6e561
--- /dev/null
+++ b/tests/py/inet/vmap.t
@@ -0,0 +1,10 @@
+:input;type filter hook input priority 0
+:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
+
+*inet;test-inet;input
+*netdev;test-netdev;ingress,egress
+
+iifname . ip protocol . th dport vmap { "eth0" . tcp . 22 : accept, "eth1" . udp . 67 : drop };ok;iifname . ip protocol . th dport vmap { "eth0" . 6 . 22 : accept, "eth1" . 17 . 67 : drop }
+ip saddr . @ih,32,32 { 1.1.1.1 . 0x14, 2.2.2.2 . 0x1e };ok
+udp length . @th,160,128 vmap { 47-63 . 0xe373135363130333131303735353203 : accept };ok
diff --git a/tests/py/inet/vmap.t.json b/tests/py/inet/vmap.t.json
new file mode 100644
index 00000000..37472cc6
--- /dev/null
+++ b/tests/py/inet/vmap.t.json
@@ -0,0 +1,144 @@
+# iifname . ip protocol . th dport vmap { "eth0" . tcp . 22 : accept, "eth1" . udp . 67 : drop }
+[
+ {
+ "vmap": {
+ "data": {
+ "set": [
+ [
+ {
+ "concat": [
+ "eth0",
+ 6,
+ 22
+ ]
+ },
+ {
+ "accept": null
+ }
+ ],
+ [
+ {
+ "concat": [
+ "eth1",
+ 17,
+ 67
+ ]
+ },
+ {
+ "drop": null
+ }
+ ]
+ ]
+ },
+ "key": {
+ "concat": [
+ {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ {
+ "payload": {
+ "field": "protocol",
+ "protocol": "ip"
+ }
+ },
+ {
+ "payload": {
+ "field": "dport",
+ "protocol": "th"
+ }
+ }
+ ]
+ }
+ }
+ }
+]
+
+# ip saddr . @ih,32,32 { 1.1.1.1 . 0x14, 2.2.2.2 . 0x1e }
+[
+ {
+ "match": {
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ },
+ {
+ "payload": {
+ "base": "ih",
+ "len": 32,
+ "offset": 32
+ }
+ }
+ ]
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ {
+ "concat": [
+ "1.1.1.1",
+ 20
+ ]
+ },
+ {
+ "concat": [
+ "2.2.2.2",
+ 30
+ ]
+ }
+ ]
+ }
+ }
+ }
+]
+
+# udp length . @th,160,128 vmap { 47-63 . 0xe373135363130333131303735353203 : accept }
+[
+ {
+ "vmap": {
+ "data": {
+ "set": [
+ [
+ {
+ "concat": [
+ {
+ "range": [
+ 47,
+ 63
+ ]
+ },
+ "0xe373135363130333131303735353203"
+ ]
+ },
+ {
+ "accept": null
+ }
+ ]
+ ]
+ },
+ "key": {
+ "concat": [
+ {
+ "payload": {
+ "field": "length",
+ "protocol": "udp"
+ }
+ },
+ {
+ "payload": {
+ "base": "th",
+ "len": 128,
+ "offset": 160
+ }
+ }
+ ]
+ }
+ }
+ }
+]
+
diff --git a/tests/py/inet/vmap.t.payload b/tests/py/inet/vmap.t.payload
new file mode 100644
index 00000000..29ec846d
--- /dev/null
+++ b/tests/py/inet/vmap.t.payload
@@ -0,0 +1,34 @@
+# iifname . ip protocol . th dport vmap { "eth0" . tcp . 22 : accept, "eth1" . udp . 67 : drop }
+__map%d test-inet b size 2
+__map%d test-inet 0
+ element 30687465 00000000 00000000 00000000 00000006 00001600 : accept 0 [end] element 31687465 00000000 00000000 00000000 00000011 00004300 : drop 0 [end]
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x00000002 ]
+ [ meta load iifname => reg 1 ]
+ [ payload load 1b @ network header + 9 => reg 2 ]
+ [ payload load 2b @ transport header + 2 => reg 13 ]
+ [ lookup reg 1 set __map%d dreg 0 ]
+
+# ip saddr . @ih,32,32 { 1.1.1.1 . 0x14, 2.2.2.2 . 0x1e }
+__set%d test-inet 3 size 2
+__set%d test-inet 0
+ element 01010101 14000000 : 0 [end] element 02020202 1e000000 : 0 [end]
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x00000002 ]
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ payload load 4b @ inner header + 4 => reg 9 ]
+ [ lookup reg 1 set __set%d ]
+
+# udp length . @th,160,128 vmap { 47-63 . 0xe373135363130333131303735353203 : accept }
+__map%d x 8f size 1
+__map%d x 0
+ element 00002f00 3531370e 33303136 37303131 03323535 - 00003f00 3531370e 33303136 37303131 03323535 : accept 0 [end]
+inet x y
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 4 => reg 1 ]
+ [ payload load 16b @ transport header + 20 => reg 9 ]
+ [ lookup reg 1 set __map%d dreg 0 ]
+
diff --git a/tests/py/inet/vmap.t.payload.netdev b/tests/py/inet/vmap.t.payload.netdev
new file mode 100644
index 00000000..3f51bb33
--- /dev/null
+++ b/tests/py/inet/vmap.t.payload.netdev
@@ -0,0 +1,34 @@
+# iifname . ip protocol . th dport vmap { "eth0" . tcp . 22 : accept, "eth1" . udp . 67 : drop }
+__map%d test-netdev b size 2
+__map%d test-netdev 0
+ element 30687465 00000000 00000000 00000000 00000006 00001600 : accept 0 [end] element 31687465 00000000 00000000 00000000 00000011 00004300 : drop 0 [end]
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ meta load iifname => reg 1 ]
+ [ payload load 1b @ network header + 9 => reg 2 ]
+ [ payload load 2b @ transport header + 2 => reg 13 ]
+ [ lookup reg 1 set __map%d dreg 0 ]
+
+# ip saddr . @ih,32,32 { 1.1.1.1 . 0x14, 2.2.2.2 . 0x1e }
+__set%d test-netdev 3 size 2
+__set%d test-netdev 0
+ element 01010101 14000000 : 0 [end] element 02020202 1e000000 : 0 [end]
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ payload load 4b @ inner header + 4 => reg 9 ]
+ [ lookup reg 1 set __set%d ]
+
+# udp length . @th,160,128 vmap { 47-63 . 0xe373135363130333131303735353203 : accept }
+__map%d test-netdev 8f size 1
+__map%d test-netdev 0
+ element 00002f00 3531370e 33303136 37303131 03323535 - 00003f00 3531370e 33303136 37303131 03323535 : accept 0 [end]
+netdev test-netdev ingress
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 4 => reg 1 ]
+ [ payload load 16b @ transport header + 20 => reg 9 ]
+ [ lookup reg 1 set __map%d dreg 0 ]
+
diff --git a/tests/py/inet/vxlan.t b/tests/py/inet/vxlan.t
new file mode 100644
index 00000000..10cdb7a4
--- /dev/null
+++ b/tests/py/inet/vxlan.t
@@ -0,0 +1,23 @@
+:input;type filter hook input priority 0
+:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
+
+*ip;test-ip4;input
+*ip6;test-ip6;input
+*inet;test-inet;input
+*netdev;test-netdev;ingress,egress
+
+vxlan vni 10;fail
+udp dport 4789 vxlan vni 10;ok
+udp dport 4789 vxlan ip saddr 10.141.11.2;ok
+udp dport 4789 vxlan ip saddr 10.141.11.0/24;ok
+udp dport 4789 vxlan ip protocol 1;ok
+udp dport 4789 vxlan udp sport 8888;ok
+udp dport 4789 vxlan icmp type echo-reply;ok
+udp dport 4789 vxlan ether saddr 62:87:4d:d6:19:05;ok
+udp dport 4789 vxlan vlan id 10;ok
+udp dport 4789 vxlan ip dscp 0x02;ok
+udp dport 4789 vxlan ip dscp 0x02;ok
+udp dport 4789 vxlan ip saddr . vxlan ip daddr { 1.2.3.4 . 4.3.2.1 };ok
+
+udp dport 4789 vxlan ip saddr set 1.2.3.4;fail
diff --git a/tests/py/inet/vxlan.t.json b/tests/py/inet/vxlan.t.json
new file mode 100644
index 00000000..91b3d294
--- /dev/null
+++ b/tests/py/inet/vxlan.t.json
@@ -0,0 +1,344 @@
+# udp dport 4789 vxlan vni 10
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 4789
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "vni",
+ "protocol": "vxlan",
+ "tunnel": "vxlan"
+ }
+ },
+ "op": "==",
+ "right": 10
+ }
+ }
+]
+
+# udp dport 4789 vxlan ip saddr 10.141.11.2
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 4789
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip",
+ "tunnel": "vxlan"
+ }
+ },
+ "op": "==",
+ "right": "10.141.11.2"
+ }
+ }
+]
+
+# udp dport 4789 vxlan ip saddr 10.141.11.0/24
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 4789
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip",
+ "tunnel": "vxlan"
+ }
+ },
+ "op": "==",
+ "right": {
+ "prefix": {
+ "addr": "10.141.11.0",
+ "len": 24
+ }
+ }
+ }
+ }
+]
+
+# udp dport 4789 vxlan ip protocol 1
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 4789
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "protocol",
+ "protocol": "ip",
+ "tunnel": "vxlan"
+ }
+ },
+ "op": "==",
+ "right": 1
+ }
+ }
+]
+
+# udp dport 4789 vxlan udp sport 8888
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 4789
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "sport",
+ "protocol": "udp",
+ "tunnel": "vxlan"
+ }
+ },
+ "op": "==",
+ "right": 8888
+ }
+ }
+]
+
+# udp dport 4789 vxlan icmp type echo-reply
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 4789
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "type",
+ "protocol": "icmp",
+ "tunnel": "vxlan"
+ }
+ },
+ "op": "==",
+ "right": "echo-reply"
+ }
+ }
+]
+
+# udp dport 4789 vxlan ether saddr 62:87:4d:d6:19:05
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 4789
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ether",
+ "tunnel": "vxlan"
+ }
+ },
+ "op": "==",
+ "right": "62:87:4d:d6:19:05"
+ }
+ }
+]
+
+# udp dport 4789 vxlan vlan id 10
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 4789
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "id",
+ "protocol": "vlan",
+ "tunnel": "vxlan"
+ }
+ },
+ "op": "==",
+ "right": 10
+ }
+ }
+]
+
+# udp dport 4789 vxlan ip dscp 0x02
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 4789
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip",
+ "tunnel": "vxlan"
+ }
+ },
+ "op": "==",
+ "right": 2
+ }
+ }
+]
+
+# udp dport 4789 vxlan ip dscp 0x02
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 4789
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip",
+ "tunnel": "vxlan"
+ }
+ },
+ "op": "==",
+ "right": 2
+ }
+ }
+]
+
+# udp dport 4789 vxlan ip saddr . vxlan ip daddr { 1.2.3.4 . 4.3.2.1 }
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 4789
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip",
+ "tunnel": "vxlan"
+ }
+ },
+ {
+ "payload": {
+ "field": "daddr",
+ "protocol": "ip",
+ "tunnel": "vxlan"
+ }
+ }
+ ]
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ {
+ "concat": [
+ "1.2.3.4",
+ "4.3.2.1"
+ ]
+ }
+ ]
+ }
+ }
+ }
+]
+
diff --git a/tests/py/inet/vxlan.t.payload b/tests/py/inet/vxlan.t.payload
new file mode 100644
index 00000000..cde8e56f
--- /dev/null
+++ b/tests/py/inet/vxlan.t.payload
@@ -0,0 +1,114 @@
+# udp dport 4789 vxlan vni 10
+netdev test-netdev ingress
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x0000b512 ]
+ [ inner type 1 hdrsize 8 flags f [ payload load 3b @ unknown header + 4 => reg 1 ] ]
+ [ cmp eq reg 1 0x000a0000 ]
+
+# udp dport 4789 vxlan ip saddr 10.141.11.2
+netdev test-netdev ingress
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x0000b512 ]
+ [ inner type 1 hdrsize 8 flags f [ meta load protocol => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 1 hdrsize 8 flags f [ payload load 4b @ network header + 12 => reg 1 ] ]
+ [ cmp eq reg 1 0x020b8d0a ]
+
+# udp dport 4789 vxlan ip saddr 10.141.11.0/24
+netdev test-netdev ingress
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x0000b512 ]
+ [ inner type 1 hdrsize 8 flags f [ meta load protocol => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 1 hdrsize 8 flags f [ payload load 3b @ network header + 12 => reg 1 ] ]
+ [ cmp eq reg 1 0x000b8d0a ]
+
+# udp dport 4789 vxlan ip protocol 1
+netdev test-netdev ingress
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x0000b512 ]
+ [ inner type 1 hdrsize 8 flags f [ meta load protocol => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 1 hdrsize 8 flags f [ payload load 1b @ network header + 9 => reg 1 ] ]
+ [ cmp eq reg 1 0x00000001 ]
+
+# udp dport 4789 vxlan udp sport 8888
+netdev test-netdev ingress
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x0000b512 ]
+ [ inner type 1 hdrsize 8 flags f [ meta load l4proto => reg 1 ] ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ inner type 1 hdrsize 8 flags f [ payload load 2b @ transport header + 0 => reg 1 ] ]
+ [ cmp eq reg 1 0x0000b822 ]
+
+# udp dport 4789 vxlan icmp type echo-reply
+netdev test-netdev ingress
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x0000b512 ]
+ [ inner type 1 hdrsize 8 flags f [ payload load 2b @ link header + 12 => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 1 hdrsize 8 flags f [ meta load l4proto => reg 1 ] ]
+ [ cmp eq reg 1 0x00000001 ]
+ [ inner type 1 hdrsize 8 flags f [ payload load 1b @ transport header + 0 => reg 1 ] ]
+ [ cmp eq reg 1 0x00000000 ]
+
+# udp dport 4789 vxlan ether saddr 62:87:4d:d6:19:05
+netdev test-netdev ingress
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x0000b512 ]
+ [ inner type 1 hdrsize 8 flags f [ payload load 6b @ link header + 6 => reg 1 ] ]
+ [ cmp eq reg 1 0xd64d8762 0x00000519 ]
+
+# udp dport 4789 vxlan vlan id 10
+netdev test-netdev ingress
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x0000b512 ]
+ [ inner type 1 hdrsize 8 flags f [ payload load 2b @ link header + 12 => reg 1 ] ]
+ [ cmp eq reg 1 0x00000081 ]
+ [ inner type 1 hdrsize 8 flags f [ payload load 2b @ link header + 14 => reg 1 ] ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000ff0f ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000a00 ]
+
+# udp dport 4789 vxlan ip dscp 0x02
+netdev test-netdev ingress
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x0000b512 ]
+ [ inner type 1 hdrsize 8 flags f [ meta load protocol => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 1 hdrsize 8 flags f [ payload load 1b @ network header + 1 => reg 1 ] ]
+ [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000008 ]
+
+# udp dport 4789 vxlan ip saddr . vxlan ip daddr { 1.2.3.4 . 4.3.2.1 }
+__set%d test-netdev 3 size 1
+__set%d test-netdev 0
+ element 04030201 01020304 : 0 [end]
+netdev test-netdev ingress
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000011 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ cmp eq reg 1 0x0000b512 ]
+ [ inner type 1 hdrsize 8 flags f [ meta load protocol => reg 1 ] ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ inner type 1 hdrsize 8 flags f [ payload load 4b @ network header + 12 => reg 1 ] ]
+ [ inner type 1 hdrsize 8 flags f [ payload load 4b @ network header + 16 => reg 9 ] ]
+ [ lookup reg 1 set __set%d ]
+
diff --git a/tests/py/ip/ct.t b/tests/py/ip/ct.t
index a387863e..a0a22289 100644
--- a/tests/py/ip/ct.t
+++ b/tests/py/ip/ct.t
@@ -28,3 +28,9 @@ meta mark set ct original saddr . meta mark map { 1.1.1.1 . 0x00000014 : 0x00000
meta mark set ct original ip saddr . meta mark map { 1.1.1.1 . 0x00000014 : 0x0000001e };ok
ct original saddr . meta mark { 1.1.1.1 . 0x00000014 };fail
ct original ip saddr . meta mark { 1.1.1.1 . 0x00000014 };ok
+ct mark set ip dscp << 2 | 0x10;ok
+ct mark set ip dscp << 26 | 0x10;ok
+ct mark set ip dscp & 0x0f << 1;ok;ct mark set ip dscp & af33
+ct mark set ip dscp & 0x0f << 2;ok;ct mark set ip dscp & 0x3c
+ct mark set ip dscp | 0x04;ok
+ct mark set ip dscp | 1 << 20;ok;ct mark set ip dscp | 0x100000
diff --git a/tests/py/ip/ct.t.json b/tests/py/ip/ct.t.json
index 3288413f..915632ae 100644
--- a/tests/py/ip/ct.t.json
+++ b/tests/py/ip/ct.t.json
@@ -325,3 +325,157 @@
}
}
]
+
+# ct mark set ip dscp << 2 | 0x10
+[
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "<<": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip"
+ }
+ },
+ 2
+ ]
+ },
+ 16
+ ]
+ }
+ }
+ }
+]
+
+# ct mark set ip dscp << 26 | 0x10
+[
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "<<": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip"
+ }
+ },
+ 26
+ ]
+ },
+ 16
+ ]
+ }
+ }
+ }
+]
+
+# ct mark set ip dscp & 0x0f << 1
+[
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "&": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip"
+ }
+ },
+ "af33"
+ ]
+ }
+ }
+ }
+]
+
+# ct mark set ip dscp & 0x0f << 2
+[
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "&": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip"
+ }
+ },
+ 60
+ ]
+ }
+ }
+ }
+]
+
+# ct mark set ip dscp | 0x04
+[
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip"
+ }
+ },
+ 4
+ ]
+ }
+ }
+ }
+]
+
+# ct mark set ip dscp | 1 << 20
+[
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip"
+ }
+ },
+ 1048576
+ ]
+ }
+ }
+ }
+]
diff --git a/tests/py/ip/ct.t.payload b/tests/py/ip/ct.t.payload
index 49f06a84..692011d0 100644
--- a/tests/py/ip/ct.t.payload
+++ b/tests/py/ip/ct.t.payload
@@ -84,3 +84,53 @@ ip
[ ct load src_ip => reg 1 , dir original ]
[ meta load mark => reg 9 ]
[ lookup reg 1 set __set%d ]
+
+# ct mark set ip dscp << 2 | 0x10
+ip test-ip4 output
+ [ payload load 1b @ network header + 1 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000002 ) ]
+ [ bitwise reg 1 = ( reg 1 << 0x00000002 ) ]
+ [ bitwise reg 1 = ( reg 1 & 0xffffffef ) ^ 0x00000010 ]
+ [ ct set mark with reg 1 ]
+
+# ct mark set ip dscp << 26 | 0x10
+ip
+ [ payload load 1b @ network header + 1 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000002 ) ]
+ [ bitwise reg 1 = ( reg 1 << 0x0000001a ) ]
+ [ bitwise reg 1 = ( reg 1 & 0xffffffef ) ^ 0x00000010 ]
+ [ ct set mark with reg 1 ]
+
+# ct mark set ip dscp & 0x0f << 1
+ip test-ip4 output
+ [ payload load 1b @ network header + 1 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000002 ) ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000001e ) ^ 0x00000000 ]
+ [ ct set mark with reg 1 ]
+
+# ct mark set ip dscp & 0x0f << 2
+ip test-ip4 output
+ [ payload load 1b @ network header + 1 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000002 ) ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000003c ) ^ 0x00000000 ]
+ [ ct set mark with reg 1 ]
+
+# ct mark set ip dscp | 0x04
+ip test-ip4 output
+ [ payload load 1b @ network header + 1 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000002 ) ]
+ [ bitwise reg 1 = ( reg 1 & 0xfffffffb ) ^ 0x00000004 ]
+ [ ct set mark with reg 1 ]
+
+# ct mark set ip dscp | 1 << 20
+ip test-ip4 output
+ [ payload load 1b @ network header + 1 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000002 ) ]
+ [ bitwise reg 1 = ( reg 1 & 0xffefffff ) ^ 0x00100000 ]
+ [ ct set mark with reg 1 ]
diff --git a/tests/py/ip/dnat.t b/tests/py/ip/dnat.t
index c5ca4c40..881571db 100644
--- a/tests/py/ip/dnat.t
+++ b/tests/py/ip/dnat.t
@@ -18,3 +18,6 @@ dnat to ct mark . ip daddr map { 0x00000014 . 1.1.1.1 : 1.2.3.4};ok
dnat ip to ip saddr . tcp dport map { 192.168.1.2 . 80 : 10.141.10.0/24 . 8888 - 8999 };ok
dnat ip to ip saddr . tcp dport map { 192.168.1.2 . 80 : 10.141.10.0/24 . 80 };ok
dnat ip to ip saddr . tcp dport map { 192.168.1.2 . 80 : 10.141.10.2 . 8888 - 8999 };ok
+ip daddr 192.168.0.1 dnat ip to tcp dport map { 443 : 10.141.10.4 . 8443, 80 : 10.141.10.4 . 8080 };ok
+meta l4proto 6 dnat ip to iifname . ip saddr map { "enp2s0" . 10.1.1.136 : 1.1.2.69 . 22, "enp2s0" . 10.1.1.1-10.1.1.135 : 1.1.2.66-1.84.236.78 . 22 };ok
+dnat ip to iifname . ip saddr map { "enp2s0" . 10.1.1.136 : 1.1.2.69/32, "enp2s0" . 10.1.1.1-10.1.1.135 : 1.1.2.66-1.84.236.78 };ok
diff --git a/tests/py/ip/dnat.t.json b/tests/py/ip/dnat.t.json
index 0481a368..fe15d072 100644
--- a/tests/py/ip/dnat.t.json
+++ b/tests/py/ip/dnat.t.json
@@ -262,3 +262,482 @@
}
]
+# iifname "eth0" tcp dport 81 dnat to 192.168.3.2:8080-8999
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "op": "==",
+ "right": "eth0"
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ },
+ "op": "==",
+ "right": 81
+ }
+ },
+ {
+ "dnat": {
+ "addr": "192.168.3.2",
+ "port": {
+ "range": [
+ 8080,
+ 8999
+ ]
+ }
+ }
+ }
+]
+
+# iifname "eth0" tcp dport 81 dnat to 192.168.3.2-192.168.3.4:8080-8999
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "op": "==",
+ "right": "eth0"
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ },
+ "op": "==",
+ "right": 81
+ }
+ },
+ {
+ "dnat": {
+ "addr": {
+ "range": [
+ "192.168.3.2",
+ "192.168.3.4"
+ ]
+ },
+ "port": {
+ "range": [
+ 8080,
+ 8999
+ ]
+ }
+ }
+ }
+]
+
+# iifname "eth0" tcp dport 81 dnat to 192.168.3.2-192.168.3.4:8080
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "op": "==",
+ "right": "eth0"
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ },
+ "op": "==",
+ "right": 81
+ }
+ },
+ {
+ "dnat": {
+ "addr": {
+ "range": [
+ "192.168.3.2",
+ "192.168.3.4"
+ ]
+ },
+ "port": 8080
+ }
+ }
+]
+
+# dnat ip to ip saddr . tcp dport map { 192.168.1.2 . 80 : 10.141.10.2 . 8888 - 8999 }
+[
+ {
+ "dnat": {
+ "addr": {
+ "map": {
+ "data": {
+ "set": [
+ [
+ {
+ "concat": [
+ "192.168.1.2",
+ 80
+ ]
+ },
+ {
+ "concat": [
+ "10.141.10.2",
+ {
+ "range": [
+ 8888,
+ 8999
+ ]
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "key": {
+ "concat": [
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ },
+ {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ }
+ ]
+ }
+ }
+ },
+ "family": "ip"
+ }
+ }
+]
+
+# dnat ip to ip saddr . tcp dport map { 192.168.1.2 . 80 : 10.141.10.0/24 . 8888 - 8999 }
+[
+ {
+ "dnat": {
+ "addr": {
+ "map": {
+ "data": {
+ "set": [
+ [
+ {
+ "concat": [
+ "192.168.1.2",
+ 80
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "10.141.10.0",
+ "len": 24
+ }
+ },
+ {
+ "range": [
+ 8888,
+ 8999
+ ]
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "key": {
+ "concat": [
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ },
+ {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ }
+ ]
+ }
+ }
+ },
+ "family": "ip"
+ }
+ }
+]
+
+# dnat ip to ip saddr . tcp dport map { 192.168.1.2 . 80 : 10.141.10.0/24 . 80 }
+[
+ {
+ "dnat": {
+ "addr": {
+ "map": {
+ "data": {
+ "set": [
+ [
+ {
+ "concat": [
+ "192.168.1.2",
+ 80
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "10.141.10.0",
+ "len": 24
+ }
+ },
+ 80
+ ]
+ }
+ ]
+ ]
+ },
+ "key": {
+ "concat": [
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ },
+ {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ }
+ ]
+ }
+ }
+ },
+ "family": "ip"
+ }
+ }
+]
+
+# ip daddr 192.168.0.1 dnat ip to tcp dport map { 443 : 10.141.10.4 . 8443, 80 : 10.141.10.4 . 8080 }
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "daddr",
+ "protocol": "ip"
+ }
+ },
+ "op": "==",
+ "right": "192.168.0.1"
+ }
+ },
+ {
+ "dnat": {
+ "addr": {
+ "map": {
+ "data": {
+ "set": [
+ [
+ 80,
+ {
+ "concat": [
+ "10.141.10.4",
+ 8080
+ ]
+ }
+ ],
+ [
+ 443,
+ {
+ "concat": [
+ "10.141.10.4",
+ 8443
+ ]
+ }
+ ]
+ ]
+ },
+ "key": {
+ "payload": {
+ "field": "dport",
+ "protocol": "tcp"
+ }
+ }
+ }
+ },
+ "family": "ip"
+ }
+ }
+]
+
+# meta l4proto 6 dnat ip to iifname . ip saddr map { "enp2s0" . 10.1.1.136 : 1.1.2.69 . 22, "enp2s0" . 10.1.1.1-10.1.1.135 : 1.1.2.66-1.84.236.78 . 22 }
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "op": "==",
+ "right": 6
+ }
+ },
+ {
+ "dnat": {
+ "addr": {
+ "map": {
+ "data": {
+ "set": [
+ [
+ {
+ "concat": [
+ "enp2s0",
+ "10.1.1.136"
+ ]
+ },
+ {
+ "concat": [
+ "1.1.2.69",
+ 22
+ ]
+ }
+ ],
+ [
+ {
+ "concat": [
+ "enp2s0",
+ {
+ "range": [
+ "10.1.1.1",
+ "10.1.1.135"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "range": [
+ "1.1.2.66",
+ "1.84.236.78"
+ ]
+ },
+ 22
+ ]
+ }
+ ]
+ ]
+ },
+ "key": {
+ "concat": [
+ {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ }
+ ]
+ }
+ }
+ },
+ "family": "ip"
+ }
+ }
+]
+
+# dnat ip to iifname . ip saddr map { "enp2s0" . 10.1.1.136 : 1.1.2.69/32, "enp2s0" . 10.1.1.1-10.1.1.135 : 1.1.2.66-1.84.236.78 }
+[
+ {
+ "dnat": {
+ "addr": {
+ "map": {
+ "data": {
+ "set": [
+ [
+ {
+ "concat": [
+ "enp2s0",
+ "10.1.1.136"
+ ]
+ },
+ {
+ "prefix": {
+ "addr": "1.1.2.69",
+ "len": 32
+ }
+ }
+ ],
+ [
+ {
+ "concat": [
+ "enp2s0",
+ {
+ "range": [
+ "10.1.1.1",
+ "10.1.1.135"
+ ]
+ }
+ ]
+ },
+ {
+ "range": [
+ "1.1.2.66",
+ "1.84.236.78"
+ ]
+ }
+ ]
+ ]
+ },
+ "key": {
+ "concat": [
+ {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ }
+ ]
+ }
+ }
+ },
+ "family": "ip"
+ }
+ }
+]
+
diff --git a/tests/py/ip/dnat.t.payload.ip b/tests/py/ip/dnat.t.payload.ip
index 4872545a..439c6abe 100644
--- a/tests/py/ip/dnat.t.payload.ip
+++ b/tests/py/ip/dnat.t.payload.ip
@@ -167,3 +167,38 @@ ip
[ immediate reg 4 0x00002723 ]
[ nat dnat ip addr_min reg 1 addr_max reg 2 proto_min reg 3 proto_max reg 4 flags 0x2 ]
+# ip daddr 192.168.0.1 dnat ip to tcp dport map { 443 : 10.141.10.4 . 8443, 80 : 10.141.10.4 . 8080 }
+__map%d test-ip4 b size 2
+__map%d test-ip4 0
+ element 0000bb01 : 040a8d0a 0000fb20 0 [end] element 00005000 : 040a8d0a 0000901f 0 [end]
+ip
+ [ payload load 4b @ network header + 16 => reg 1 ]
+ [ cmp eq reg 1 0x0100a8c0 ]
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ lookup reg 1 set __map%d dreg 1 ]
+ [ nat dnat ip addr_min reg 1 proto_min reg 9 ]
+
+# meta l4proto 6 dnat ip to iifname . ip saddr map { "enp2s0" . 10.1.1.136 : 1.1.2.69 . 22, "enp2s0" . 10.1.1.1-10.1.1.135 : 1.1.2.66-1.84.236.78 . 22 }
+__map%d test-ip4 8f size 2
+__map%d test-ip4 0
+ element 32706e65 00003073 00000000 00000000 8801010a - 32706e65 00003073 00000000 00000000 8801010a : 45020101 00001600 45020101 00001600 0 [end] element 32706e65 00003073 00000000 00000000 0101010a - 32706e65 00003073 00000000 00000000 8701010a : 42020101 00001600 4eec5401 00001600 0 [end]
+ip test-ip4 prerouting
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ meta load iifname => reg 1 ]
+ [ payload load 4b @ network header + 12 => reg 2 ]
+ [ lookup reg 1 set __map%d dreg 1 ]
+ [ nat dnat ip addr_min reg 1 addr_max reg 10 proto_min reg 9 proto_max reg 11 ]
+
+# dnat ip to iifname . ip saddr map { "enp2s0" . 10.1.1.136 : 1.1.2.69/32, "enp2s0" . 10.1.1.1-10.1.1.135 : 1.1.2.66-1.84.236.78 }
+__map%d test-ip4 8f size 2
+__map%d test-ip4 0
+ element 32706e65 00003073 00000000 00000000 8801010a - 32706e65 00003073 00000000 00000000 8801010a : 45020101 45020101 0 [end] element 32706e65 00003073 00000000 00000000 0101010a - 32706e65 00003073 00000000 00000000 8701010a : 42020101 4eec5401 0 [end]
+ip test-ip4 prerouting
+ [ meta load iifname => reg 1 ]
+ [ payload load 4b @ network header + 12 => reg 2 ]
+ [ lookup reg 1 set __map%d dreg 1 ]
+ [ nat dnat ip addr_min reg 1 addr_max reg 9 ]
+
diff --git a/tests/py/ip/flowtable.t b/tests/py/ip/flowtable.t
deleted file mode 100644
index 086c6cf6..00000000
--- a/tests/py/ip/flowtable.t
+++ /dev/null
@@ -1,5 +0,0 @@
-:input;type filter hook input priority 0
-
-*ip;test-ip;input
-
-meter xyz size 8192 { ip saddr timeout 30s counter};ok
diff --git a/tests/py/ip/flowtable.t.json b/tests/py/ip/flowtable.t.json
deleted file mode 100644
index a03cc9d7..00000000
--- a/tests/py/ip/flowtable.t.json
+++ /dev/null
@@ -1,24 +0,0 @@
-# meter xyz size 8192 { ip saddr timeout 30s counter}
-[
- {
- "meter": {
- "key": {
- "elem": {
- "timeout": 30,
- "val": {
- "payload": {
- "field": "saddr",
- "protocol": "ip"
- }
- }
- }
- },
- "name": "xyz",
- "size": 8192,
- "stmt": {
- "counter": null
- }
- }
- }
-]
-
diff --git a/tests/py/ip/flowtable.t.payload b/tests/py/ip/flowtable.t.payload
deleted file mode 100644
index c0aad39e..00000000
--- a/tests/py/ip/flowtable.t.payload
+++ /dev/null
@@ -1,7 +0,0 @@
-# meter xyz size 8192 { ip saddr timeout 30s counter}
-xyz test-ip 31
-xyz test-ip 0
-ip test-ip input
- [ payload load 4b @ network header + 12 => reg 1 ]
- [ dynset update reg_key 1 set xyz timeout 30000ms expr [ counter pkts 0 bytes 0 ] ]
-
diff --git a/tests/py/ip/icmp.t b/tests/py/ip/icmp.t
index 7ddf8b38..226c339b 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/ip/icmp.t.json b/tests/py/ip/icmp.t.json
index 4f052509..45e04c78 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 5a075858..52fd6016 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/ip/ip.t b/tests/py/ip/ip.t
index f4a3667c..e6999c29 100644
--- a/tests/py/ip/ip.t
+++ b/tests/py/ip/ip.t
@@ -1,10 +1,11 @@
:input;type filter hook input priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*ip;test-ip4;input
*inet;test-inet;input
*bridge;test-bridge;input
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
- ip version 2;ok
@@ -47,12 +48,15 @@ ip id != 33-45;ok
ip id { 33, 55, 67, 88};ok
ip id != { 33, 55, 67, 88};ok
-ip frag-off 222 accept;ok
-ip frag-off != 233;ok
-ip frag-off 33-45;ok
-ip frag-off != 33-45;ok
-ip frag-off { 33, 55, 67, 88};ok
-ip frag-off != { 33, 55, 67, 88};ok
+ip frag-off 0xde accept;ok
+ip frag-off != 0xe9;ok
+ip frag-off 0x21-0x2d;ok
+ip frag-off != 0x21-0x2d;ok
+ip frag-off { 0x21, 0x37, 0x43, 0x58};ok
+ip frag-off != { 0x21, 0x37, 0x43, 0x58};ok
+ip frag-off & 0x1fff != 0x0;ok
+ip frag-off & 0x2000 != 0x0;ok
+ip frag-off & 0x4000 != 0x0;ok
ip ttl 0 drop;ok
ip ttl 233;ok
@@ -126,3 +130,8 @@ iif "lo" ip dscp set cs0;ok
ip saddr . ip daddr { 192.0.2.1 . 10.0.0.1-10.0.0.2 };ok
ip saddr . ip daddr vmap { 192.168.5.1-192.168.5.128 . 192.168.6.1-192.168.6.128 : accept };ok
+
+ip saddr 1.2.3.4 ip daddr 3.4.5.6;ok
+ip saddr 1.2.3.4 counter ip daddr 3.4.5.6;ok
+
+ip dscp 1/6;ok;ip dscp & 0x3f == lephb
diff --git a/tests/py/ip/ip.t.json b/tests/py/ip/ip.t.json
index b1085035..a170e5c1 100644
--- a/tests/py/ip/ip.t.json
+++ b/tests/py/ip/ip.t.json
@@ -384,7 +384,7 @@
}
]
-# ip frag-off 222 accept
+# ip frag-off 0xde accept
[
{
"match": {
@@ -403,7 +403,7 @@
}
]
-# ip frag-off != 233
+# ip frag-off != 0xe9
[
{
"match": {
@@ -419,7 +419,7 @@
}
]
-# ip frag-off 33-45
+# ip frag-off 0x21-0x2d
[
{
"match": {
@@ -437,7 +437,7 @@
}
]
-# ip frag-off != 33-45
+# ip frag-off != 0x21-0x2d
[
{
"match": {
@@ -455,7 +455,7 @@
}
]
-# ip frag-off { 33, 55, 67, 88}
+# ip frag-off { 0x21, 0x37, 0x43, 0x58}
[
{
"match": {
@@ -478,7 +478,7 @@
}
]
-# ip frag-off != { 33, 55, 67, 88}
+# ip frag-off != { 0x21, 0x37, 0x43, 0x58}
[
{
"match": {
@@ -501,6 +501,69 @@
}
]
+# ip frag-off & 0x1fff != 0x0
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "frag-off",
+ "protocol": "ip"
+ }
+ },
+ 8191
+ ]
+ },
+ "op": "!=",
+ "right": 0
+ }
+ }
+]
+
+# ip frag-off & 0x2000 != 0x0
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "frag-off",
+ "protocol": "ip"
+ }
+ },
+ 8192
+ ]
+ },
+ "op": "!=",
+ "right": 0
+ }
+ }
+]
+
+# ip frag-off & 0x4000 != 0x0
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "frag-off",
+ "protocol": "ip"
+ }
+ },
+ 16384
+ ]
+ },
+ "op": "!=",
+ "right": 0
+ }
+ }
+]
+
# ip ttl 0 drop
[
{
@@ -1685,3 +1748,85 @@
}
]
+# ip saddr 1.2.3.4 ip daddr 3.4.5.6
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ },
+ "op": "==",
+ "right": "1.2.3.4"
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "daddr",
+ "protocol": "ip"
+ }
+ },
+ "op": "==",
+ "right": "3.4.5.6"
+ }
+ }
+]
+
+# ip saddr 1.2.3.4 counter ip daddr 3.4.5.6
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ },
+ "op": "==",
+ "right": "1.2.3.4"
+ }
+ },
+ {
+ "counter": {
+ "bytes": 0,
+ "packets": 0
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "daddr",
+ "protocol": "ip"
+ }
+ },
+ "op": "==",
+ "right": "3.4.5.6"
+ }
+ }
+]
+
+# ip dscp 1/6
+[
+ {
+ "match": {
+ "left": {
+ "&": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip"
+ }
+ },
+ 63
+ ]
+ },
+ "op": "==",
+ "right": "lephb"
+ }
+ }
+]
diff --git a/tests/py/ip/ip.t.json.output b/tests/py/ip/ip.t.json.output
index b201cdaa..351ae935 100644
--- a/tests/py/ip/ip.t.json.output
+++ b/tests/py/ip/ip.t.json.output
@@ -230,3 +230,34 @@
}
]
+# ip saddr 1.2.3.4 counter ip daddr 3.4.5.6
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ },
+ "op": "==",
+ "right": "1.2.3.4"
+ }
+ },
+ {
+ "counter": null
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "daddr",
+ "protocol": "ip"
+ }
+ },
+ "op": "==",
+ "right": "3.4.5.6"
+ }
+ }
+]
+
diff --git a/tests/py/ip/ip.t.payload b/tests/py/ip/ip.t.payload
index 49d1a0fb..d7ddf7be 100644
--- a/tests/py/ip/ip.t.payload
+++ b/tests/py/ip/ip.t.payload
@@ -124,29 +124,29 @@ ip test-ip4 input
[ payload load 2b @ network header + 4 => reg 1 ]
[ lookup reg 1 set __set%d 0x1 ]
-# ip frag-off 222 accept
+# ip frag-off 0xde accept
ip test-ip4 input
[ payload load 2b @ network header + 6 => reg 1 ]
[ cmp eq reg 1 0x0000de00 ]
[ immediate reg 0 accept ]
-# ip frag-off != 233
+# ip frag-off != 0xe9
ip test-ip4 input
[ payload load 2b @ network header + 6 => reg 1 ]
[ cmp neq reg 1 0x0000e900 ]
-# ip frag-off 33-45
+# ip frag-off 0x21-0x2d
ip test-ip4 input
[ payload load 2b @ network header + 6 => reg 1 ]
[ cmp gte reg 1 0x00002100 ]
[ cmp lte reg 1 0x00002d00 ]
-# ip frag-off != 33-45
+# ip frag-off != 0x21-0x2d
ip test-ip4 input
[ payload load 2b @ network header + 6 => reg 1 ]
[ range neq reg 1 0x00002100 0x00002d00 ]
-# ip frag-off { 33, 55, 67, 88}
+# ip frag-off { 0x21, 0x37, 0x43, 0x58}
__set%d test-ip4 3
__set%d test-ip4 0
element 00002100 : 0 [end] element 00003700 : 0 [end] element 00004300 : 0 [end] element 00005800 : 0 [end]
@@ -154,7 +154,7 @@ ip test-ip4 input
[ payload load 2b @ network header + 6 => reg 1 ]
[ lookup reg 1 set __set%d ]
-# ip frag-off != { 33, 55, 67, 88}
+# ip frag-off != { 0x21, 0x37, 0x43, 0x58}
__set%d test-ip4 3
__set%d test-ip4 0
element 00002100 : 0 [end] element 00003700 : 0 [end] element 00004300 : 0 [end] element 00005800 : 0 [end]
@@ -162,6 +162,24 @@ ip test-ip4 input
[ payload load 2b @ network header + 6 => reg 1 ]
[ lookup reg 1 set __set%d 0x1 ]
+# ip frag-off & 0x1fff != 0x0
+ip test-ip4 input
+ [ payload load 2b @ network header + 6 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000ff1f ) ^ 0x00000000 ]
+ [ cmp neq reg 1 0x00000000 ]
+
+# ip frag-off & 0x2000 != 0x0
+ip test-ip4 input
+ [ payload load 2b @ network header + 6 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000020 ) ^ 0x00000000 ]
+ [ cmp neq reg 1 0x00000000 ]
+
+# ip frag-off & 0x4000 != 0x0
+ip test-ip4 input
+ [ payload load 2b @ network header + 6 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000040 ) ^ 0x00000000 ]
+ [ cmp neq reg 1 0x00000000 ]
+
# ip ttl 0 drop
ip test-ip4 input
[ payload load 1b @ network header + 8 => reg 1 ]
@@ -430,7 +448,7 @@ ip test-ip4 input
# ip hdrlength vmap { 0-4 : drop, 5 : accept, 6 : continue } counter
__map%d test-ip4 f size 4
__map%d test-ip4 0
- element 00000000 : drop 0 [end] element 00000005 : accept 0 [end] element 00000006 : continue 0 [end] element 00000007 : drop 1 [end]
+ element 00000000 : drop 0 [end] element 00000005 : accept 0 [end] element 00000006 : continue 0 [end] element 00000007 : 1 [end]
ip test-ip4 input
[ payload load 1b @ network header + 0 => reg 1 ]
[ bitwise reg 1 = ( reg 1 & 0x0000000f ) ^ 0x00000000 ]
@@ -523,3 +541,26 @@ ip
[ payload load 4b @ network header + 12 => reg 1 ]
[ payload load 4b @ network header + 16 => reg 9 ]
[ lookup reg 1 set __map%d dreg 0 ]
+
+# ip saddr 1.2.3.4 ip daddr 3.4.5.6
+ip test-ip4 input
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ cmp eq reg 1 0x04030201 ]
+ [ payload load 4b @ network header + 16 => reg 1 ]
+ [ cmp eq reg 1 0x06050403 ]
+
+# ip saddr 1.2.3.4 counter ip daddr 3.4.5.6
+ip test-ip4 input
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ cmp eq reg 1 0x04030201 ]
+ [ counter pkts 0 bytes 0 ]
+ [ payload load 4b @ network header + 16 => reg 1 ]
+ [ cmp eq reg 1 0x06050403 ]
+
+# ip dscp 1/6
+ip test-ip4 input
+ [ payload load 1b @ network header + 1 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000002 ) ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000003f ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000001 ]
diff --git a/tests/py/ip/ip.t.payload.bridge b/tests/py/ip/ip.t.payload.bridge
index dac86543..53f881d3 100644
--- a/tests/py/ip/ip.t.payload.bridge
+++ b/tests/py/ip/ip.t.payload.bridge
@@ -162,7 +162,7 @@ bridge test-bridge input
[ payload load 2b @ network header + 4 => reg 1 ]
[ lookup reg 1 set __set%d 0x1 ]
-# ip frag-off 222 accept
+# ip frag-off 0xde accept
bridge test-bridge input
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
@@ -170,14 +170,14 @@ bridge test-bridge input
[ cmp eq reg 1 0x0000de00 ]
[ immediate reg 0 accept ]
-# ip frag-off != 233
+# ip frag-off != 0xe9
bridge test-bridge input
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ payload load 2b @ network header + 6 => reg 1 ]
[ cmp neq reg 1 0x0000e900 ]
-# ip frag-off 33-45
+# ip frag-off 0x21-0x2d
bridge test-bridge input
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
@@ -185,14 +185,14 @@ bridge test-bridge input
[ cmp gte reg 1 0x00002100 ]
[ cmp lte reg 1 0x00002d00 ]
-# ip frag-off != 33-45
+# ip frag-off != 0x21-0x2d
bridge test-bridge input
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ payload load 2b @ network header + 6 => reg 1 ]
[ range neq reg 1 0x00002100 0x00002d00 ]
-# ip frag-off { 33, 55, 67, 88}
+# ip frag-off { 0x21, 0x37, 0x43, 0x58}
__set%d test-bridge 3 size 4
__set%d test-bridge 0
element 00002100 : 0 [end] element 00003700 : 0 [end] element 00004300 : 0 [end] element 00005800 : 0 [end]
@@ -202,7 +202,7 @@ bridge test-bridge input
[ payload load 2b @ network header + 6 => reg 1 ]
[ lookup reg 1 set __set%d ]
-# ip frag-off != { 33, 55, 67, 88}
+# ip frag-off != { 0x21, 0x37, 0x43, 0x58}
__set%d test-bridge 3 size 4
__set%d test-bridge 0
element 00002100 : 0 [end] element 00003700 : 0 [end] element 00004300 : 0 [end] element 00005800 : 0 [end]
@@ -212,6 +212,30 @@ bridge test-bridge input
[ payload load 2b @ network header + 6 => reg 1 ]
[ lookup reg 1 set __set%d 0x1 ]
+# ip frag-off & 0x1fff != 0x0
+bridge test-bridge input
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ payload load 2b @ network header + 6 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000ff1f ) ^ 0x00000000 ]
+ [ cmp neq reg 1 0x00000000 ]
+
+# ip frag-off & 0x2000 != 0x0
+bridge test-bridge input
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ payload load 2b @ network header + 6 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000020 ) ^ 0x00000000 ]
+ [ cmp neq reg 1 0x00000000 ]
+
+# ip frag-off & 0x4000 != 0x0
+bridge test-bridge input
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ payload load 2b @ network header + 6 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000040 ) ^ 0x00000000 ]
+ [ cmp neq reg 1 0x00000000 ]
+
# ip ttl 0 drop
bridge test-bridge input
[ meta load protocol => reg 1 ]
@@ -566,7 +590,7 @@ bridge test-bridge input
# ip hdrlength vmap { 0-4 : drop, 5 : accept, 6 : continue } counter
__map%d test-bridge f size 4
__map%d test-bridge 0
- element 00000000 : drop 0 [end] element 00000005 : accept 0 [end] element 00000006 : continue 0 [end] element 00000007 : drop 1 [end]
+ element 00000000 : drop 0 [end] element 00000005 : accept 0 [end] element 00000006 : continue 0 [end] element 00000007 : 1 [end]
bridge test-bridge input
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
@@ -684,3 +708,31 @@ bridge
[ payload load 4b @ network header + 16 => reg 9 ]
[ lookup reg 1 set __map%d dreg 0 ]
+# ip saddr 1.2.3.4 ip daddr 3.4.5.6
+bridge test-bridge input
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ cmp eq reg 1 0x04030201 ]
+ [ payload load 4b @ network header + 16 => reg 1 ]
+ [ cmp eq reg 1 0x06050403 ]
+
+# ip saddr 1.2.3.4 counter ip daddr 3.4.5.6
+bridge test-bridge input
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ cmp eq reg 1 0x04030201 ]
+ [ counter pkts 0 bytes 0 ]
+ [ payload load 4b @ network header + 16 => reg 1 ]
+ [ cmp eq reg 1 0x06050403 ]
+
+# ip dscp 1/6
+bridge test-bridge input
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ payload load 1b @ network header + 1 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000002 ) ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000003f ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000001 ]
diff --git a/tests/py/ip/ip.t.payload.inet b/tests/py/ip/ip.t.payload.inet
index 64371650..08674c98 100644
--- a/tests/py/ip/ip.t.payload.inet
+++ b/tests/py/ip/ip.t.payload.inet
@@ -162,7 +162,7 @@ inet test-inet input
[ payload load 2b @ network header + 4 => reg 1 ]
[ lookup reg 1 set __set%d 0x1 ]
-# ip frag-off 222 accept
+# ip frag-off 0xde accept
inet test-inet input
[ meta load nfproto => reg 1 ]
[ cmp eq reg 1 0x00000002 ]
@@ -170,14 +170,14 @@ inet test-inet input
[ cmp eq reg 1 0x0000de00 ]
[ immediate reg 0 accept ]
-# ip frag-off != 233
+# ip frag-off != 0xe9
inet test-inet input
[ meta load nfproto => reg 1 ]
[ cmp eq reg 1 0x00000002 ]
[ payload load 2b @ network header + 6 => reg 1 ]
[ cmp neq reg 1 0x0000e900 ]
-# ip frag-off 33-45
+# ip frag-off 0x21-0x2d
inet test-inet input
[ meta load nfproto => reg 1 ]
[ cmp eq reg 1 0x00000002 ]
@@ -185,14 +185,14 @@ inet test-inet input
[ cmp gte reg 1 0x00002100 ]
[ cmp lte reg 1 0x00002d00 ]
-# ip frag-off != 33-45
+# ip frag-off != 0x21-0x2d
inet test-inet input
[ meta load nfproto => reg 1 ]
[ cmp eq reg 1 0x00000002 ]
[ payload load 2b @ network header + 6 => reg 1 ]
[ range neq reg 1 0x00002100 0x00002d00 ]
-# ip frag-off { 33, 55, 67, 88}
+# ip frag-off { 0x21, 0x37, 0x43, 0x58}
__set%d test-inet 3
__set%d test-inet 0
element 00002100 : 0 [end] element 00003700 : 0 [end] element 00004300 : 0 [end] element 00005800 : 0 [end]
@@ -202,7 +202,7 @@ inet test-inet input
[ payload load 2b @ network header + 6 => reg 1 ]
[ lookup reg 1 set __set%d ]
-# ip frag-off != { 33, 55, 67, 88}
+# ip frag-off != { 0x21, 0x37, 0x43, 0x58}
__set%d test-inet 3
__set%d test-inet 0
element 00002100 : 0 [end] element 00003700 : 0 [end] element 00004300 : 0 [end] element 00005800 : 0 [end]
@@ -212,6 +212,30 @@ inet test-inet input
[ payload load 2b @ network header + 6 => reg 1 ]
[ lookup reg 1 set __set%d 0x1 ]
+# ip frag-off & 0x1fff != 0x0
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x00000002 ]
+ [ payload load 2b @ network header + 6 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000ff1f ) ^ 0x00000000 ]
+ [ cmp neq reg 1 0x00000000 ]
+
+# ip frag-off & 0x2000 != 0x0
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x00000002 ]
+ [ payload load 2b @ network header + 6 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000020 ) ^ 0x00000000 ]
+ [ cmp neq reg 1 0x00000000 ]
+
+# ip frag-off & 0x4000 != 0x0
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x00000002 ]
+ [ payload load 2b @ network header + 6 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000040 ) ^ 0x00000000 ]
+ [ cmp neq reg 1 0x00000000 ]
+
# ip ttl 0 drop
inet test-inet input
[ meta load nfproto => reg 1 ]
@@ -566,7 +590,7 @@ inet test-inet input
# ip hdrlength vmap { 0-4 : drop, 5 : accept, 6 : continue } counter
__map%d test-inet f size 4
__map%d test-inet 0
- element 00000000 : drop 0 [end] element 00000005 : accept 0 [end] element 00000006 : continue 0 [end] element 00000007 : drop 1 [end]
+ element 00000000 : drop 0 [end] element 00000005 : accept 0 [end] element 00000006 : continue 0 [end] element 00000007 : 1 [end]
inet test-inet input
[ meta load nfproto => reg 1 ]
[ cmp eq reg 1 0x00000002 ]
@@ -684,3 +708,31 @@ inet
[ payload load 4b @ network header + 16 => reg 9 ]
[ lookup reg 1 set __map%d dreg 0 ]
+# ip saddr 1.2.3.4 ip daddr 3.4.5.6
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x00000002 ]
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ cmp eq reg 1 0x04030201 ]
+ [ payload load 4b @ network header + 16 => reg 1 ]
+ [ cmp eq reg 1 0x06050403 ]
+
+# ip saddr 1.2.3.4 counter ip daddr 3.4.5.6
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x00000002 ]
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ cmp eq reg 1 0x04030201 ]
+ [ counter pkts 0 bytes 0 ]
+ [ payload load 4b @ network header + 16 => reg 1 ]
+ [ cmp eq reg 1 0x06050403 ]
+
+# ip dscp 1/6
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x00000002 ]
+ [ payload load 1b @ network header + 1 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000002 ) ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000003f ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000001 ]
diff --git a/tests/py/ip/ip.t.payload.netdev b/tests/py/ip/ip.t.payload.netdev
index 65f8c96a..8220b05d 100644
--- a/tests/py/ip/ip.t.payload.netdev
+++ b/tests/py/ip/ip.t.payload.netdev
@@ -96,7 +96,7 @@ netdev test-netdev ingress
[ payload load 2b @ network header + 4 => reg 1 ]
[ lookup reg 1 set __set%d 0x1 ]
-# ip frag-off 222 accept
+# ip frag-off 0xde accept
netdev test-netdev ingress
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
@@ -104,14 +104,14 @@ netdev test-netdev ingress
[ cmp eq reg 1 0x0000de00 ]
[ immediate reg 0 accept ]
-# ip frag-off != 233
+# ip frag-off != 0xe9
netdev test-netdev ingress
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ payload load 2b @ network header + 6 => reg 1 ]
[ cmp neq reg 1 0x0000e900 ]
-# ip frag-off 33-45
+# ip frag-off 0x21-0x2d
netdev test-netdev ingress
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
@@ -119,14 +119,14 @@ netdev test-netdev ingress
[ cmp gte reg 1 0x00002100 ]
[ cmp lte reg 1 0x00002d00 ]
-# ip frag-off != 33-45
+# ip frag-off != 0x21-0x2d
netdev test-netdev ingress
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
[ payload load 2b @ network header + 6 => reg 1 ]
[ range neq reg 1 0x00002100 0x00002d00 ]
-# ip frag-off { 33, 55, 67, 88}
+# ip frag-off { 0x21, 0x37, 0x43, 0x58}
__set%d test-netdev 3
__set%d test-netdev 0
element 00002100 : 0 [end] element 00003700 : 0 [end] element 00004300 : 0 [end] element 00005800 : 0 [end]
@@ -136,7 +136,7 @@ netdev test-netdev ingress
[ payload load 2b @ network header + 6 => reg 1 ]
[ lookup reg 1 set __set%d ]
-# ip frag-off != { 33, 55, 67, 88}
+# ip frag-off != { 0x21, 0x37, 0x43, 0x58}
__set%d test-netdev 3
__set%d test-netdev 0
element 00002100 : 0 [end] element 00003700 : 0 [end] element 00004300 : 0 [end] element 00005800 : 0 [end]
@@ -146,6 +146,30 @@ netdev test-netdev ingress
[ payload load 2b @ network header + 6 => reg 1 ]
[ lookup reg 1 set __set%d 0x1 ]
+# ip frag-off & 0x1fff != 0x0
+netdev x y
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ payload load 2b @ network header + 6 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000ff1f ) ^ 0x00000000 ]
+ [ cmp neq reg 1 0x00000000 ]
+
+# ip frag-off & 0x2000 != 0x0
+netdev x y
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ payload load 2b @ network header + 6 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000020 ) ^ 0x00000000 ]
+ [ cmp neq reg 1 0x00000000 ]
+
+# ip frag-off & 0x4000 != 0x0
+netdev x y
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ payload load 2b @ network header + 6 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000040 ) ^ 0x00000000 ]
+ [ cmp neq reg 1 0x00000000 ]
+
# ip ttl 0 drop
netdev test-netdev ingress
[ meta load protocol => reg 1 ]
@@ -465,7 +489,7 @@ netdev test-netdev ingress
# ip hdrlength vmap { 0-4 : drop, 5 : accept, 6 : continue } counter
__map%d test-netdev f size 4
__map%d test-netdev 0
- element 00000000 : drop 0 [end] element 00000005 : accept 0 [end] element 00000006 : continue 0 [end] element 00000007 : drop 1 [end]
+ element 00000000 : drop 0 [end] element 00000005 : accept 0 [end] element 00000006 : continue 0 [end] element 00000007 : 1 [end]
netdev test-netdev ingress
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x00000008 ]
@@ -684,3 +708,31 @@ netdev
[ payload load 4b @ network header + 16 => reg 9 ]
[ lookup reg 1 set __map%d dreg 0 ]
+# ip saddr 1.2.3.4 ip daddr 3.4.5.6
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ cmp eq reg 1 0x04030201 ]
+ [ payload load 4b @ network header + 16 => reg 1 ]
+ [ cmp eq reg 1 0x06050403 ]
+
+# ip saddr 1.2.3.4 counter ip daddr 3.4.5.6
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ cmp eq reg 1 0x04030201 ]
+ [ counter pkts 0 bytes 0 ]
+ [ payload load 4b @ network header + 16 => reg 1 ]
+ [ cmp eq reg 1 0x06050403 ]
+
+# ip dscp 1/6
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ payload load 1b @ network header + 1 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000002 ) ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000003f ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000001 ]
diff --git a/tests/py/ip/ip_tcp.t b/tests/py/ip/ip_tcp.t
index 467da3ef..ff398aa6 100644
--- a/tests/py/ip/ip_tcp.t
+++ b/tests/py/ip/ip_tcp.t
@@ -1,5 +1,4 @@
:input;type filter hook input priority 0
-:ingress;type filter hook ingress device lo priority 0
*ip;test-ip;input
diff --git a/tests/py/ip/meta.t b/tests/py/ip/meta.t
index 5a05923a..a88a6145 100644
--- a/tests/py/ip/meta.t
+++ b/tests/py/ip/meta.t
@@ -15,3 +15,8 @@ meta obrname "br0";fail
meta sdif "lo" accept;ok
meta sdifname != "vrf1" accept;ok
+
+meta mark set ip dscp;ok
+
+meta mark set ip dscp << 2 | 0x10;ok
+meta mark set ip dscp << 26 | 0x10;ok
diff --git a/tests/py/ip/meta.t.json b/tests/py/ip/meta.t.json
index 3df31ce3..25936dba 100644
--- a/tests/py/ip/meta.t.json
+++ b/tests/py/ip/meta.t.json
@@ -156,3 +156,81 @@
}
}
]
+
+# meta mark set ip dscp
+[
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip"
+ }
+ }
+ }
+ }
+]
+
+# meta mark set ip dscp << 2 | 0x10
+[
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "<<": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip"
+ }
+ },
+ 2
+ ]
+ },
+ 16
+ ]
+ }
+ }
+ }
+]
+
+
+# meta mark set ip dscp << 26 | 0x10
+[
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "<<": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip"
+ }
+ },
+ 26
+ ]
+ },
+ 16
+ ]
+ }
+ }
+ }
+]
diff --git a/tests/py/ip/meta.t.payload b/tests/py/ip/meta.t.payload
index afde5cc1..880ac5d6 100644
--- a/tests/py/ip/meta.t.payload
+++ b/tests/py/ip/meta.t.payload
@@ -51,3 +51,28 @@ ip test-ip4 input
[ cmp eq reg 1 0x00000011 ]
[ payload load 2b @ transport header + 2 => reg 1 ]
[ cmp eq reg 1 0x00004300 ]
+
+# meta mark set ip dscp
+ip test-ip4 input
+ [ payload load 1b @ network header + 1 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000002 ) ]
+ [ meta set mark with reg 1 ]
+
+# meta mark set ip dscp << 2 | 0x10
+ip test-ip4 input
+ [ payload load 1b @ network header + 1 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000002 ) ]
+ [ bitwise reg 1 = ( reg 1 << 0x00000002 ) ]
+ [ bitwise reg 1 = ( reg 1 & 0xffffffef ) ^ 0x00000010 ]
+ [ meta set mark with reg 1 ]
+
+# meta mark set ip dscp << 26 | 0x10
+ip
+ [ payload load 1b @ network header + 1 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
+ [ bitwise reg 1 = ( reg 1 >> 0x00000002 ) ]
+ [ bitwise reg 1 = ( reg 1 << 0x0000001a ) ]
+ [ bitwise reg 1 = ( reg 1 & 0xffffffef ) ^ 0x00000010 ]
+ [ meta set mark with reg 1 ]
diff --git a/tests/py/ip/numgen.t b/tests/py/ip/numgen.t
index 29a6a105..2a881460 100644
--- a/tests/py/ip/numgen.t
+++ b/tests/py/ip/numgen.t
@@ -5,3 +5,5 @@ ct mark set numgen inc mod 2;ok
ct mark set numgen inc mod 2 offset 100;ok
dnat to numgen inc mod 2 map { 0 : 192.168.10.100, 1 : 192.168.20.200 };ok
dnat to numgen inc mod 10 map { 0-5 : 192.168.10.100, 6-9 : 192.168.20.200};ok
+dnat to numgen inc mod 7 offset 167772161;ok
+dnat to numgen inc mod 255 offset 167772161;ok
diff --git a/tests/py/ip/numgen.t.json b/tests/py/ip/numgen.t.json
index 9902c2cf..6cf66041 100644
--- a/tests/py/ip/numgen.t.json
+++ b/tests/py/ip/numgen.t.json
@@ -97,3 +97,33 @@
}
]
+# dnat to numgen inc mod 7 offset 167772161
+[
+ {
+ "dnat": {
+ "addr": {
+ "numgen": {
+ "mod": 7,
+ "mode": "inc",
+ "offset": 167772161
+ }
+ }
+ }
+ }
+]
+
+# dnat to numgen inc mod 255 offset 167772161
+[
+ {
+ "dnat": {
+ "addr": {
+ "numgen": {
+ "mod": 255,
+ "mode": "inc",
+ "offset": 167772161
+ }
+ }
+ }
+ }
+]
+
diff --git a/tests/py/ip/numgen.t.payload b/tests/py/ip/numgen.t.payload
index 3349c68b..b4eadf85 100644
--- a/tests/py/ip/numgen.t.payload
+++ b/tests/py/ip/numgen.t.payload
@@ -27,3 +27,14 @@ ip test-ip4 pre
[ numgen reg 1 = inc mod 2 offset 100 ]
[ ct set mark with reg 1 ]
+# dnat to numgen inc mod 7 offset 167772161
+ip test-ip4 pre
+ [ numgen reg 1 = inc mod 7 offset 167772161 ]
+ [ byteorder reg 1 = hton(reg 1, 4, 4) ]
+ [ nat dnat ip addr_min reg 1 ]
+
+# dnat to numgen inc mod 255 offset 167772161
+ip test-ip4 pre
+ [ numgen reg 1 = inc mod 255 offset 167772161 ]
+ [ byteorder reg 1 = hton(reg 1, 4, 4) ]
+ [ nat dnat ip addr_min reg 1 ]
diff --git a/tests/py/ip/redirect.t b/tests/py/ip/redirect.t
index d2991ce2..8c2b52f0 100644
--- a/tests/py/ip/redirect.t
+++ b/tests/py/ip/redirect.t
@@ -47,5 +47,5 @@ ip daddr 10.0.0.0-10.2.3.4 udp dport 53 counter redirect;ok
iifname "eth0" ct state established,new tcp dport vmap {22 : drop, 222 : drop } redirect;ok
# redirect with maps
-ip protocol 6 redirect to :tcp dport map { 22 : 8000, 80 : 8080};ok
+redirect to :tcp dport map { 22 : 8000, 80 : 8080};ok
diff --git a/tests/py/ip/redirect.t.json b/tests/py/ip/redirect.t.json
index 3544e7f1..2afdf9b1 100644
--- a/tests/py/ip/redirect.t.json
+++ b/tests/py/ip/redirect.t.json
@@ -593,21 +593,9 @@
}
]
-# ip protocol 6 redirect to :tcp dport map { 22 : 8000, 80 : 8080}
+# redirect to :tcp dport map { 22 : 8000, 80 : 8080}
[
{
- "match": {
- "left": {
- "payload": {
- "field": "protocol",
- "protocol": "ip"
- }
- },
- "op": "==",
- "right": 6
- }
- },
- {
"redirect": {
"port": {
"map": {
diff --git a/tests/py/ip/redirect.t.payload b/tests/py/ip/redirect.t.payload
index 424ad7b4..4bed47c1 100644
--- a/tests/py/ip/redirect.t.payload
+++ b/tests/py/ip/redirect.t.payload
@@ -207,12 +207,12 @@ ip test-ip4 output
[ lookup reg 1 set __map%d dreg 0 ]
[ redir ]
-# ip protocol 6 redirect to :tcp dport map { 22 : 8000, 80 : 8080}
+# redirect to :tcp dport map { 22 : 8000, 80 : 8080}
__map%d test-ip4 b
__map%d test-ip4 0
element 00001600 : 0000401f 0 [end] element 00005000 : 0000901f 0 [end]
ip test-ip4 output
- [ payload load 1b @ network header + 9 => reg 1 ]
+ [ meta load l4proto => reg 1 ]
[ cmp eq reg 1 0x00000006 ]
[ payload load 2b @ transport header + 2 => reg 1 ]
[ lookup reg 1 set __map%d dreg 1 ]
diff --git a/tests/py/ip/sets.t b/tests/py/ip/sets.t
index 7dc884fc..46d9686b 100644
--- a/tests/py/ip/sets.t
+++ b/tests/py/ip/sets.t
@@ -1,9 +1,10 @@
:input;type filter hook input priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*ip;test-ip4;input
*inet;test-inet;input
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
!w type ipv4_addr;ok
!x type inet_proto;ok
@@ -51,6 +52,9 @@ ip saddr != @set33 drop;fail
ip saddr . ip daddr @set5 drop;ok
add @set5 { ip saddr . ip daddr };ok
+!map1 type ipv4_addr . ipv4_addr : mark;ok
+add @map1 { ip saddr . ip daddr : meta mark };ok
+
# test nested anonymous sets
ip saddr { { 1.1.1.0, 3.3.3.0 }, 2.2.2.0 };ok;ip saddr { 1.1.1.0, 2.2.2.0, 3.3.3.0 }
ip saddr { { 1.1.1.0/24, 3.3.3.0/24 }, 2.2.2.0/24 };ok;ip saddr { 1.1.1.0/24, 2.2.2.0/24, 3.3.3.0/24 }
diff --git a/tests/py/ip/sets.t.json b/tests/py/ip/sets.t.json
index d24b3918..44ca1528 100644
--- a/tests/py/ip/sets.t.json
+++ b/tests/py/ip/sets.t.json
@@ -272,3 +272,34 @@
}
]
+# add @map1 { ip saddr . ip daddr : meta mark }
+[
+ {
+ "map": {
+ "data": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "elem": {
+ "concat": [
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ },
+ {
+ "payload": {
+ "field": "daddr",
+ "protocol": "ip"
+ }
+ }
+ ]
+ },
+ "map": "@map1",
+ "op": "add"
+ }
+ }
+]
+
diff --git a/tests/py/ip/sets.t.payload.inet b/tests/py/ip/sets.t.payload.inet
index d7d70b0c..fd6517a5 100644
--- a/tests/py/ip/sets.t.payload.inet
+++ b/tests/py/ip/sets.t.payload.inet
@@ -75,6 +75,15 @@ inet
[ lookup reg 1 set set6 ]
[ immediate reg 0 drop ]
+# add @map1 { ip saddr . ip daddr : meta mark }
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x00000002 ]
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ payload load 4b @ network header + 16 => reg 9 ]
+ [ meta load mark => reg 10 ]
+ [ dynset add reg_key 1 set map1 sreg_data 10 ]
+
# ip saddr vmap { 1.1.1.1 : drop, * : accept }
__map%d test-inet b
__map%d test-inet 0
diff --git a/tests/py/ip/sets.t.payload.ip b/tests/py/ip/sets.t.payload.ip
index 97a96693..d9cc32b6 100644
--- a/tests/py/ip/sets.t.payload.ip
+++ b/tests/py/ip/sets.t.payload.ip
@@ -73,3 +73,11 @@ ip
[ payload load 4b @ network header + 12 => reg 1 ]
[ lookup reg 1 set __map%d dreg 1 ]
[ meta set mark with reg 1 ]
+
+# add @map1 { ip saddr . ip daddr : meta mark }
+ip test-ip4 input
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ payload load 4b @ network header + 16 => reg 9 ]
+ [ meta load mark => reg 10 ]
+ [ dynset add reg_key 1 set map1 sreg_data 10 ]
+
diff --git a/tests/py/ip/sets.t.payload.netdev b/tests/py/ip/sets.t.payload.netdev
index d4317d29..d41b9e8b 100644
--- a/tests/py/ip/sets.t.payload.netdev
+++ b/tests/py/ip/sets.t.payload.netdev
@@ -95,3 +95,13 @@ netdev
[ payload load 4b @ network header + 12 => reg 1 ]
[ lookup reg 1 set __map%d dreg 1 ]
[ meta set mark with reg 1 ]
+
+# add @map1 { ip saddr . ip daddr : meta mark }
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x00000008 ]
+ [ payload load 4b @ network header + 12 => reg 1 ]
+ [ payload load 4b @ network header + 16 => reg 9 ]
+ [ meta load mark => reg 10 ]
+ [ dynset add reg_key 1 set map1 sreg_data 10 ]
+
diff --git a/tests/py/ip/snat.t.json b/tests/py/ip/snat.t.json
index 0813086c..967560e6 100644
--- a/tests/py/ip/snat.t.json
+++ b/tests/py/ip/snat.t.json
@@ -358,3 +358,173 @@
}
]
+# meta l4proto 17 snat ip to ip saddr map { 10.141.11.4 : 192.168.2.3 . 80 }
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "op": "==",
+ "right": "udp"
+ }
+ },
+ {
+ "snat": {
+ "addr": {
+ "map": {
+ "data": {
+ "set": [
+ [
+ "10.141.11.4",
+ {
+ "concat": [
+ "192.168.2.3",
+ 80
+ ]
+ }
+ ]
+ ]
+ },
+ "key": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ }
+ }
+ },
+ "family": "ip"
+ }
+ }
+]
+
+# snat ip to ip saddr map { 10.141.11.4 : 192.168.2.2-192.168.2.4 }
+[
+ {
+ "snat": {
+ "addr": {
+ "map": {
+ "data": {
+ "set": [
+ [
+ "10.141.11.4",
+ {
+ "range": [
+ "192.168.2.2",
+ "192.168.2.4"
+ ]
+ }
+ ]
+ ]
+ },
+ "key": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ }
+ }
+ },
+ "family": "ip"
+ }
+ }
+]
+
+# snat ip to ip saddr map { 10.141.12.14 : 192.168.2.0/24 }
+[
+ {
+ "snat": {
+ "addr": {
+ "map": {
+ "data": {
+ "set": [
+ [
+ "10.141.12.14",
+ {
+ "prefix": {
+ "addr": "192.168.2.0",
+ "len": 24
+ }
+ }
+ ]
+ ]
+ },
+ "key": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ }
+ }
+ },
+ "family": "ip"
+ }
+ }
+]
+
+# meta l4proto { 6, 17} snat ip to ip saddr . th dport map { 10.141.11.4 . 20 : 192.168.2.3 . 80}
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ "tcp",
+ "udp"
+ ]
+ }
+ }
+ },
+ {
+ "snat": {
+ "addr": {
+ "map": {
+ "data": {
+ "set": [
+ [
+ {
+ "concat": [
+ "10.141.11.4",
+ 20
+ ]
+ },
+ {
+ "concat": [
+ "192.168.2.3",
+ 80
+ ]
+ }
+ ]
+ ]
+ },
+ "key": {
+ "concat": [
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ },
+ {
+ "payload": {
+ "field": "dport",
+ "protocol": "th"
+ }
+ }
+ ]
+ }
+ }
+ },
+ "family": "ip"
+ }
+ }
+]
+
diff --git a/tests/py/ip/snat.t.json.output b/tests/py/ip/snat.t.json.output
index 1365316c..2a997801 100644
--- a/tests/py/ip/snat.t.json.output
+++ b/tests/py/ip/snat.t.json.output
@@ -70,3 +70,180 @@
}
]
+# snat ip to ip saddr map { 10.141.11.4 : 192.168.2.3 . 80 }
+[
+ {
+ "snat": {
+ "addr": {
+ "map": {
+ "data": {
+ "set": [
+ [
+ "10.141.11.4",
+ {
+ "concat": [
+ "192.168.2.3",
+ 80
+ ]
+ }
+ ]
+ ]
+ },
+ "key": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ }
+ }
+ },
+ "family": "ip"
+ }
+ }
+]
+
+# meta l4proto 17 snat ip to ip saddr map { 10.141.11.4 : 192.168.2.3 . 80 }
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "op": "==",
+ "right": 17
+ }
+ },
+ {
+ "snat": {
+ "addr": {
+ "map": {
+ "data": {
+ "set": [
+ [
+ "10.141.11.4",
+ {
+ "concat": [
+ "192.168.2.3",
+ 80
+ ]
+ }
+ ]
+ ]
+ },
+ "key": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ }
+ }
+ },
+ "family": "ip"
+ }
+ }
+]
+
+# meta l4proto { 6, 17} snat ip to ip saddr . th dport map { 10.141.11.4 . 20 : 192.168.2.3 . 80}
+[
+ {
+ "match": {
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ 6,
+ 17
+ ]
+ }
+ }
+ },
+ {
+ "snat": {
+ "addr": {
+ "map": {
+ "data": {
+ "set": [
+ [
+ {
+ "concat": [
+ "10.141.11.4",
+ 20
+ ]
+ },
+ {
+ "concat": [
+ "192.168.2.3",
+ 80
+ ]
+ }
+ ]
+ ]
+ },
+ "key": {
+ "concat": [
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ },
+ {
+ "payload": {
+ "field": "dport",
+ "protocol": "th"
+ }
+ }
+ ]
+ }
+ }
+ },
+ "family": "ip"
+ }
+ }
+]
+
+# snat ip prefix to ip saddr map { 10.141.11.0/24 : 192.168.2.0/24 }
+[
+ {
+ "snat": {
+ "addr": {
+ "map": {
+ "data": {
+ "set": [
+ [
+ {
+ "prefix": {
+ "addr": "10.141.11.0",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "192.168.2.0",
+ "len": 24
+ }
+ }
+ ]
+ ]
+ },
+ "key": {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip"
+ }
+ }
+ }
+ },
+ "family": "ip",
+ "flags": "netmap",
+ "type_flags": "prefix"
+ }
+ }
+]
+
diff --git a/tests/py/ip/snat.t.payload b/tests/py/ip/snat.t.payload
index 48ae46b3..71a5e2f1 100644
--- a/tests/py/ip/snat.t.payload
+++ b/tests/py/ip/snat.t.payload
@@ -139,3 +139,16 @@ ip
[ lookup reg 1 set __map%d dreg 1 ]
[ nat snat ip addr_min reg 1 proto_min reg 9 ]
+# ip daddr 192.168.0.1 dnat to tcp dport map { 443 : 10.141.10.4 . 8443, 80 : 10.141.10.4 . 8080 }
+__map%d x b size 2
+__map%d x 0
+ element 0000bb01 : 040a8d0a 0000fb20 0 [end] element 00005000 : 040a8d0a 0000901f 0 [end]
+ip
+ [ payload load 4b @ network header + 16 => reg 1 ]
+ [ cmp eq reg 1 0x0100a8c0 ]
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+ [ payload load 2b @ transport header + 2 => reg 1 ]
+ [ lookup reg 1 set __map%d dreg 1 ]
+ [ nat dnat ip addr_min reg 1 proto_min reg 9 ]
+
diff --git a/tests/py/ip6/ct.t b/tests/py/ip6/ct.t
new file mode 100644
index 00000000..c06fd6a0
--- /dev/null
+++ b/tests/py/ip6/ct.t
@@ -0,0 +1,9 @@
+:output;type filter hook output priority 0
+
+*ip6;test-ip6;output
+
+ct mark set ip6 dscp << 2 | 0x10;ok
+ct mark set ip6 dscp << 26 | 0x10;ok
+ct mark set ip6 dscp | 0x04;ok
+ct mark set ip6 dscp | 0xff000000;ok
+ct mark set ip6 dscp & 0x0f << 2;ok;ct mark set ip6 dscp & 0x3c
diff --git a/tests/py/ip6/ct.t.json b/tests/py/ip6/ct.t.json
new file mode 100644
index 00000000..7d8c88bb
--- /dev/null
+++ b/tests/py/ip6/ct.t.json
@@ -0,0 +1,293 @@
+# ct mark set ip6 dscp lshift 2 or 0x10
+[
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "<<": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip6"
+ }
+ },
+ 2
+ ]
+ },
+ 16
+ ]
+ }
+ }
+ }
+]
+
+# ct mark set ip6 dscp lshift 26 or 0x10
+[
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "<<": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip6"
+ }
+ },
+ 26
+ ]
+ },
+ 16
+ ]
+ }
+ }
+ }
+]
+
+# ct mark set ip6 dscp << 2 | 0x10
+[
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "<<": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip6"
+ }
+ },
+ 2
+ ]
+ },
+ 16
+ ]
+ }
+ }
+ }
+]
+
+# ct mark set ip6 dscp << 26 | 0x10
+[
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "<<": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip6"
+ }
+ },
+ 26
+ ]
+ },
+ 16
+ ]
+ }
+ }
+ }
+]
+
+# ct mark set ip6 dscp | 0x04
+[
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip6"
+ }
+ },
+ 4
+ ]
+ }
+ }
+ }
+]
+
+# ct mark set ip6 dscp | 0xff000000
+[
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip6"
+ }
+ },
+ 4278190080
+ ]
+ }
+ }
+ }
+]
+
+# ct mark set ip6 dscp << 2 | 0x10
+[
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "<<": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip6"
+ }
+ },
+ 2
+ ]
+ },
+ 16
+ ]
+ }
+ }
+ }
+]
+
+# ct mark set ip6 dscp << 26 | 0x10
+[
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "<<": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip6"
+ }
+ },
+ 26
+ ]
+ },
+ 16
+ ]
+ }
+ }
+ }
+]
+
+# ct mark set ip6 dscp | 0x04
+[
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip6"
+ }
+ },
+ 4
+ ]
+ }
+ }
+ }
+]
+
+# ct mark set ip6 dscp | 0xff000000
+[
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip6"
+ }
+ },
+ 4278190080
+ ]
+ }
+ }
+ }
+]
+
+# ct mark set ip6 dscp & 0x0f << 2
+[
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "&": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip6"
+ }
+ },
+ 60
+ ]
+ }
+ }
+ }
+]
diff --git a/tests/py/ip6/ct.t.payload b/tests/py/ip6/ct.t.payload
new file mode 100644
index 00000000..944208f2
--- /dev/null
+++ b/tests/py/ip6/ct.t.payload
@@ -0,0 +1,46 @@
+# ct mark set ip6 dscp << 2 | 0x10
+ip6 test-ip6 output
+ [ 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 ) ]
+ [ bitwise reg 1 = ( reg 1 << 0x00000002 ) ]
+ [ bitwise reg 1 = ( reg 1 & 0xffffffef ) ^ 0x00000010 ]
+ [ ct set mark with reg 1 ]
+
+# ct mark set ip6 dscp << 26 | 0x10
+ip6 test-ip6 output
+ [ 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 ) ]
+ [ bitwise reg 1 = ( reg 1 << 0x0000001a ) ]
+ [ bitwise reg 1 = ( reg 1 & 0xffffffef ) ^ 0x00000010 ]
+ [ ct set mark with reg 1 ]
+
+# ct mark set ip6 dscp | 0x04
+ip6 test-ip6 output
+ [ 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 ) ]
+ [ bitwise reg 1 = ( reg 1 & 0xfffffffb ) ^ 0x00000004 ]
+ [ ct set mark with reg 1 ]
+
+# ct mark set ip6 dscp | 0xff000000
+ip6 test-ip6 output
+ [ 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 ) ]
+ [ bitwise reg 1 = ( reg 1 & 0x00ffffff ) ^ 0xff000000 ]
+ [ ct set mark with reg 1 ]
+
+# ct mark set ip6 dscp & 0x0f << 2
+ip6 test-ip6 output
+ [ 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 ) ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000003c ) ^ 0x00000000 ]
+ [ ct set mark with reg 1 ]
diff --git a/tests/py/ip6/exthdr.t.json.output b/tests/py/ip6/exthdr.t.json.output
index c9f5b49b..813402a2 100644
--- a/tests/py/ip6/exthdr.t.json.output
+++ b/tests/py/ip6/exthdr.t.json.output
@@ -1,33 +1,3 @@
-# exthdr hbh == exists
-[
- {
- "match": {
- "left": {
- "exthdr": {
- "name": "hbh"
- }
- },
- "op": "==",
- "right": true
- }
- }
-]
-
-# exthdr hbh == missing
-[
- {
- "match": {
- "left": {
- "exthdr": {
- "name": "hbh"
- }
- },
- "op": "==",
- "right": false
- }
- }
-]
-
# exthdr hbh 1
[
{
diff --git a/tests/py/ip6/flowtable.t b/tests/py/ip6/flowtable.t
deleted file mode 100644
index e58d51bb..00000000
--- a/tests/py/ip6/flowtable.t
+++ /dev/null
@@ -1,6 +0,0 @@
-:input;type filter hook input priority 0
-
-*ip6;test-ip6;input
-
-meter acct_out size 4096 { meta iif . ip6 saddr timeout 600s counter };ok;meter acct_out size 4096 { iif . ip6 saddr timeout 10m counter }
-meter acct_out size 12345 { ip6 saddr . meta iif timeout 600s counter };ok;meter acct_out size 12345 { ip6 saddr . iif timeout 10m counter }
diff --git a/tests/py/ip6/flowtable.t.json b/tests/py/ip6/flowtable.t.json
deleted file mode 100644
index d0b3a957..00000000
--- a/tests/py/ip6/flowtable.t.json
+++ /dev/null
@@ -1,62 +0,0 @@
-# meter acct_out size 4096 { meta iif . ip6 saddr timeout 600s counter }
-[
- {
- "meter": {
- "key": {
- "elem": {
- "timeout": 600,
- "val": {
- "concat": [
- {
- "meta": { "key": "iif" }
- },
- {
- "payload": {
- "field": "saddr",
- "protocol": "ip6"
- }
- }
- ]
- }
- }
- },
- "name": "acct_out",
- "size": 4096,
- "stmt": {
- "counter": null
- }
- }
- }
-]
-
-# meter acct_out size 12345 { ip6 saddr . meta iif timeout 600s counter }
-[
- {
- "meter": {
- "key": {
- "elem": {
- "timeout": 600,
- "val": {
- "concat": [
- {
- "payload": {
- "field": "saddr",
- "protocol": "ip6"
- }
- },
- {
- "meta": { "key": "iif" }
- }
- ]
- }
- }
- },
- "name": "acct_out",
- "size": 12345,
- "stmt": {
- "counter": null
- }
- }
- }
-]
-
diff --git a/tests/py/ip6/flowtable.t.json.output b/tests/py/ip6/flowtable.t.json.output
deleted file mode 100644
index d0b3a957..00000000
--- a/tests/py/ip6/flowtable.t.json.output
+++ /dev/null
@@ -1,62 +0,0 @@
-# meter acct_out size 4096 { meta iif . ip6 saddr timeout 600s counter }
-[
- {
- "meter": {
- "key": {
- "elem": {
- "timeout": 600,
- "val": {
- "concat": [
- {
- "meta": { "key": "iif" }
- },
- {
- "payload": {
- "field": "saddr",
- "protocol": "ip6"
- }
- }
- ]
- }
- }
- },
- "name": "acct_out",
- "size": 4096,
- "stmt": {
- "counter": null
- }
- }
- }
-]
-
-# meter acct_out size 12345 { ip6 saddr . meta iif timeout 600s counter }
-[
- {
- "meter": {
- "key": {
- "elem": {
- "timeout": 600,
- "val": {
- "concat": [
- {
- "payload": {
- "field": "saddr",
- "protocol": "ip6"
- }
- },
- {
- "meta": { "key": "iif" }
- }
- ]
- }
- }
- },
- "name": "acct_out",
- "size": 12345,
- "stmt": {
- "counter": null
- }
- }
- }
-]
-
diff --git a/tests/py/ip6/flowtable.t.payload b/tests/py/ip6/flowtable.t.payload
deleted file mode 100644
index 559475f6..00000000
--- a/tests/py/ip6/flowtable.t.payload
+++ /dev/null
@@ -1,16 +0,0 @@
-# meter acct_out size 4096 { meta iif . ip6 saddr timeout 600s counter }
-acct_out test-ip6 31
-acct_out test-ip6 0
-ip6 test-ip6 input
- [ meta load iif => reg 1 ]
- [ payload load 16b @ network header + 8 => reg 9 ]
- [ dynset update reg_key 1 set acct_out timeout 600000ms expr [ counter pkts 0 bytes 0 ] ]
-
-# meter acct_out size 12345 { ip6 saddr . meta iif timeout 600s counter }
-acct_out test-ip6 31
-acct_out test-ip6 0
-ip6 test-ip6 input
- [ payload load 16b @ network header + 8 => reg 1 ]
- [ meta load iif => reg 2 ]
- [ dynset update reg_key 1 set acct_out timeout 600000ms expr [ counter pkts 0 bytes 0 ] ]
-
diff --git a/tests/py/ip6/frag.t b/tests/py/ip6/frag.t
index 945398db..6bbd6ac0 100644
--- a/tests/py/ip6/frag.t
+++ b/tests/py/ip6/frag.t
@@ -1,8 +1,10 @@
:output;type filter hook output priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*ip6;test-ip6;output
*inet;test-inet;output
+*netdev;test-netdev;ingress,egress
frag nexthdr tcp;ok;frag nexthdr 6
frag nexthdr != icmp;ok;frag nexthdr != 1
diff --git a/tests/py/ip6/frag.t.payload.netdev b/tests/py/ip6/frag.t.payload.netdev
new file mode 100644
index 00000000..05620754
--- /dev/null
+++ b/tests/py/ip6/frag.t.payload.netdev
@@ -0,0 +1,232 @@
+# frag nexthdr tcp
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 1b @ 44 + 0 => reg 1 ]
+ [ cmp eq reg 1 0x00000006 ]
+
+# frag nexthdr != icmp
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 1b @ 44 + 0 => reg 1 ]
+ [ cmp neq reg 1 0x00000001 ]
+
+# frag nexthdr {esp, ah, comp, udp, udplite, tcp, dccp, sctp}
+__set%d test-netdev 3 size 8
+__set%d test-netdev 0
+ element 00000032 : 0 [end] element 00000033 : 0 [end] element 0000006c : 0 [end] element 00000011 : 0 [end] element 00000088 : 0 [end] element 00000006 : 0 [end] element 00000021 : 0 [end] element 00000084 : 0 [end]
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 1b @ 44 + 0 => reg 1 ]
+ [ lookup reg 1 set __set%d ]
+
+# frag nexthdr != {esp, ah, comp, udp, udplite, tcp, dccp, sctp}
+__set%d test-netdev 3 size 8
+__set%d test-netdev 0
+ element 00000032 : 0 [end] element 00000033 : 0 [end] element 0000006c : 0 [end] element 00000011 : 0 [end] element 00000088 : 0 [end] element 00000006 : 0 [end] element 00000021 : 0 [end] element 00000084 : 0 [end]
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 1b @ 44 + 0 => reg 1 ]
+ [ lookup reg 1 set __set%d 0x1 ]
+
+# frag nexthdr esp
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 1b @ 44 + 0 => reg 1 ]
+ [ cmp eq reg 1 0x00000032 ]
+
+# frag nexthdr ah
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 1b @ 44 + 0 => reg 1 ]
+ [ cmp eq reg 1 0x00000033 ]
+
+# frag reserved 22
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 1b @ 44 + 1 => reg 1 ]
+ [ cmp eq reg 1 0x00000016 ]
+
+# frag reserved != 233
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 1b @ 44 + 1 => reg 1 ]
+ [ cmp neq reg 1 0x000000e9 ]
+
+# frag reserved 33-45
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 1b @ 44 + 1 => reg 1 ]
+ [ cmp gte reg 1 0x00000021 ]
+ [ cmp lte reg 1 0x0000002d ]
+
+# frag reserved != 33-45
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 1b @ 44 + 1 => reg 1 ]
+ [ range neq reg 1 0x00000021 0x0000002d ]
+
+# frag reserved { 33, 55, 67, 88}
+__set%d test-netdev 3 size 4
+__set%d test-netdev 0
+ element 00000021 : 0 [end] element 00000037 : 0 [end] element 00000043 : 0 [end] element 00000058 : 0 [end]
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 1b @ 44 + 1 => reg 1 ]
+ [ lookup reg 1 set __set%d ]
+
+# frag reserved != { 33, 55, 67, 88}
+__set%d test-netdev 3 size 4
+__set%d test-netdev 0
+ element 00000021 : 0 [end] element 00000037 : 0 [end] element 00000043 : 0 [end] element 00000058 : 0 [end]
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 1b @ 44 + 1 => reg 1 ]
+ [ lookup reg 1 set __set%d 0x1 ]
+
+# frag frag-off 22
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 2b @ 44 + 2 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000f8ff ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x0000b000 ]
+
+# frag frag-off != 233
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 2b @ 44 + 2 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000f8ff ) ^ 0x00000000 ]
+ [ cmp neq reg 1 0x00004807 ]
+
+# frag frag-off 33-45
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 2b @ 44 + 2 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000f8ff ) ^ 0x00000000 ]
+ [ cmp gte reg 1 0x00000801 ]
+ [ cmp lte reg 1 0x00006801 ]
+
+# frag frag-off != 33-45
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 2b @ 44 + 2 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000f8ff ) ^ 0x00000000 ]
+ [ range neq reg 1 0x00000801 0x00006801 ]
+
+# frag frag-off { 33, 55, 67, 88}
+__set%d test-netdev 3 size 4
+__set%d test-netdev 0
+ element 00000801 : 0 [end] element 0000b801 : 0 [end] element 00001802 : 0 [end] element 0000c002 : 0 [end]
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 2b @ 44 + 2 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000f8ff ) ^ 0x00000000 ]
+ [ lookup reg 1 set __set%d ]
+
+# frag frag-off != { 33, 55, 67, 88}
+__set%d test-netdev 3 size 4
+__set%d test-netdev 0
+ element 00000801 : 0 [end] element 0000b801 : 0 [end] element 00001802 : 0 [end] element 0000c002 : 0 [end]
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 2b @ 44 + 2 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x0000f8ff ) ^ 0x00000000 ]
+ [ lookup reg 1 set __set%d 0x1 ]
+
+# frag reserved2 1
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 1b @ 44 + 3 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000006 ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000002 ]
+
+# frag more-fragments 0
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 1b @ 44 + 3 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000001 ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000000 ]
+
+# frag more-fragments 1
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 1b @ 44 + 3 => reg 1 ]
+ [ bitwise reg 1 = ( reg 1 & 0x00000001 ) ^ 0x00000000 ]
+ [ cmp eq reg 1 0x00000001 ]
+
+# frag id 1
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 4b @ 44 + 4 => reg 1 ]
+ [ cmp eq reg 1 0x01000000 ]
+
+# frag id 22
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 4b @ 44 + 4 => reg 1 ]
+ [ cmp eq reg 1 0x16000000 ]
+
+# frag id != 33
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 4b @ 44 + 4 => reg 1 ]
+ [ cmp neq reg 1 0x21000000 ]
+
+# frag id 33-45
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 4b @ 44 + 4 => reg 1 ]
+ [ cmp gte reg 1 0x21000000 ]
+ [ cmp lte reg 1 0x2d000000 ]
+
+# frag id != 33-45
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 4b @ 44 + 4 => reg 1 ]
+ [ range neq reg 1 0x21000000 0x2d000000 ]
+
+# frag id { 33, 55, 67, 88}
+__set%d test-netdev 3 size 4
+__set%d test-netdev 0
+ element 21000000 : 0 [end] element 37000000 : 0 [end] element 43000000 : 0 [end] element 58000000 : 0 [end]
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 4b @ 44 + 4 => reg 1 ]
+ [ lookup reg 1 set __set%d ]
+
+# frag id != { 33, 55, 67, 88}
+__set%d test-netdev 3 size 4
+__set%d test-netdev 0
+ element 21000000 : 0 [end] element 37000000 : 0 [end] element 43000000 : 0 [end] element 58000000 : 0 [end]
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ exthdr load ipv6 4b @ 44 + 4 => reg 1 ]
+ [ lookup reg 1 set __set%d 0x1 ]
+
diff --git a/tests/py/ip6/icmpv6.t b/tests/py/ip6/icmpv6.t
index 4de6ee23..7632bfd8 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,4 +84,16 @@ 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
+icmpv6 type nd-neighbor-advert icmpv6 taddr 2001:db8::133;ok
+icmpv6 taddr 2001:db8::133;ok;icmpv6 type { mld-listener-query, mld-listener-report, mld-listener-done, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect} icmpv6 taddr 2001:db8::133
+
+icmpv6 taddr 2001:db8::133;ok;icmpv6 type { mld-listener-query, mld-listener-report, mld-listener-done, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect} icmpv6 taddr 2001:db8::133
+
+icmpv6 type { mld-listener-query, mld-listener-report, mld-listener-done, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect} icmpv6 taddr 2001:db8::133;ok
+icmpv6 type { nd-neighbor-solicit, nd-neighbor-advert } icmpv6 taddr 2001:db8::133;ok
+icmpv6 daddr 2001:db8::133;ok
+icmpv6 type nd-redirect icmpv6 daddr 2001:db8::133;ok;icmpv6 daddr 2001:db8::133
diff --git a/tests/py/ip6/icmpv6.t.json b/tests/py/ip6/icmpv6.t.json
index 2251be82..9df886dd 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,267 @@
}
},
"op": "==",
- "right": "no-route"
+ "right": 0
+ }
+ }
+]
+
+# icmpv6 type mld-listener-query icmpv6 taddr 2001:db8::133
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "type",
+ "protocol": "icmpv6"
+ }
+ },
+ "op": "==",
+ "right": "mld-listener-query"
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "taddr",
+ "protocol": "icmpv6"
+ }
+ },
+ "op": "==",
+ "right": "2001:db8::133"
+ }
+ }
+]
+
+# icmpv6 type nd-neighbor-solicit icmpv6 taddr 2001:db8::133
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "type",
+ "protocol": "icmpv6"
+ }
+ },
+ "op": "==",
+ "right": "nd-neighbor-solicit"
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "taddr",
+ "protocol": "icmpv6"
+ }
+ },
+ "op": "==",
+ "right": "2001:db8::133"
+ }
+ }
+]
+
+# icmpv6 type nd-neighbor-advert icmpv6 taddr 2001:db8::133
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "type",
+ "protocol": "icmpv6"
+ }
+ },
+ "op": "==",
+ "right": "nd-neighbor-advert"
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "taddr",
+ "protocol": "icmpv6"
+ }
+ },
+ "op": "==",
+ "right": "2001:db8::133"
+ }
+ }
+]
+
+# icmpv6 taddr 2001:db8::133
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "type",
+ "protocol": "icmpv6"
+ }
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ "mld-listener-query",
+ "mld-listener-report",
+ "mld-listener-done",
+ "nd-neighbor-solicit",
+ "nd-neighbor-advert",
+ "nd-redirect"
+ ]
+ }
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "taddr",
+ "protocol": "icmpv6"
+ }
+ },
+ "op": "==",
+ "right": "2001:db8::133"
+ }
+ }
+]
+
+# icmpv6 taddr 2001:db8::133
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "type",
+ "protocol": "icmpv6"
+ }
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ "mld-listener-query",
+ "mld-listener-report",
+ "mld-listener-done",
+ "nd-neighbor-solicit",
+ "nd-neighbor-advert",
+ "nd-redirect"
+ ]
+ }
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "taddr",
+ "protocol": "icmpv6"
+ }
+ },
+ "op": "==",
+ "right": "2001:db8::133"
+ }
+ }
+]
+
+# icmpv6 type { mld-listener-query, mld-listener-report, mld-listener-done, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect} icmpv6 taddr 2001:db8::133
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "type",
+ "protocol": "icmpv6"
+ }
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ "mld-listener-query",
+ "mld-listener-report",
+ "mld-listener-done",
+ "nd-neighbor-solicit",
+ "nd-neighbor-advert",
+ "nd-redirect"
+ ]
+ }
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "taddr",
+ "protocol": "icmpv6"
+ }
+ },
+ "op": "==",
+ "right": "2001:db8::133"
+ }
+ }
+]
+
+# icmpv6 type { nd-neighbor-solicit, nd-neighbor-advert } icmpv6 taddr 2001:db8::133
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "type",
+ "protocol": "icmpv6"
+ }
+ },
+ "op": "==",
+ "right": {
+ "set": [
+ "nd-neighbor-solicit",
+ "nd-neighbor-advert"
+ ]
+ }
+ }
+ },
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "taddr",
+ "protocol": "icmpv6"
+ }
+ },
+ "op": "==",
+ "right": "2001:db8::133"
+ }
+ }
+]
+
+# icmpv6 daddr 2001:db8::133
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "daddr",
+ "protocol": "icmpv6"
+ }
+ },
+ "op": "==",
+ "right": "2001:db8::133"
+ }
+ }
+]
+
+# icmpv6 type nd-redirect icmpv6 daddr 2001:db8::133
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "daddr",
+ "protocol": "icmpv6"
+ }
+ },
+ "op": "==",
+ "right": "2001:db8::133"
}
}
]
diff --git a/tests/py/ip6/icmpv6.t.json.output b/tests/py/ip6/icmpv6.t.json.output
index 7b8f5c19..f29b346c 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 0e96be2d..5b6035d1 100644
--- a/tests/py/ip6/icmpv6.t.payload.ip6
+++ b/tests/py/ip6/icmpv6.t.payload.ip6
@@ -554,10 +554,90 @@ 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 ]
[ payload load 2b @ transport header + 0 => reg 1 ]
[ cmp eq reg 1 0x00000004 ]
+# icmpv6 type mld-listener-query icmpv6 taddr 2001:db8::133
+ip6 test-ip6 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000003a ]
+ [ payload load 1b @ transport header + 0 => reg 1 ]
+ [ cmp eq reg 1 0x00000082 ]
+ [ payload load 16b @ transport header + 8 => reg 1 ]
+ [ cmp eq reg 1 0xb80d0120 0x00000000 0x00000000 0x33010000 ]
+
+# icmpv6 type nd-neighbor-solicit icmpv6 taddr 2001:db8::133
+ip6 test-ip6 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000003a ]
+ [ payload load 1b @ transport header + 0 => reg 1 ]
+ [ cmp eq reg 1 0x00000087 ]
+ [ payload load 16b @ transport header + 8 => reg 1 ]
+ [ cmp eq reg 1 0xb80d0120 0x00000000 0x00000000 0x33010000 ]
+
+# icmpv6 type nd-neighbor-advert icmpv6 taddr 2001:db8::133
+ip6 test-ip6 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000003a ]
+ [ payload load 1b @ transport header + 0 => reg 1 ]
+ [ cmp eq reg 1 0x00000088 ]
+ [ payload load 16b @ transport header + 8 => reg 1 ]
+ [ cmp eq reg 1 0xb80d0120 0x00000000 0x00000000 0x33010000 ]
+
+# icmpv6 taddr 2001:db8::133
+__set%d test-ip6 3 size 6
+__set%d test-ip6 0
+ element 00000082 : 0 [end] element 00000083 : 0 [end] element 00000084 : 0 [end] element 00000087 : 0 [end] element 00000088 : 0 [end] element 00000089 : 0 [end]
+ip6 test-ip6 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000003a ]
+ [ payload load 1b @ transport header + 0 => reg 1 ]
+ [ lookup reg 1 set __set%d ]
+ [ payload load 16b @ transport header + 8 => reg 1 ]
+ [ cmp eq reg 1 0xb80d0120 0x00000000 0x00000000 0x33010000 ]
+
+# icmpv6 type { mld-listener-query, mld-listener-report, mld-listener-done, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect} icmpv6 taddr 2001:db8::133
+__set%d test-ip6 3 size 6
+__set%d test-ip6 0
+ element 00000082 : 0 [end] element 00000083 : 0 [end] element 00000084 : 0 [end] element 00000087 : 0 [end] element 00000088 : 0 [end] element 00000089 : 0 [end]
+ip6 test-ip6 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000003a ]
+ [ payload load 1b @ transport header + 0 => reg 1 ]
+ [ lookup reg 1 set __set%d ]
+ [ payload load 16b @ transport header + 8 => reg 1 ]
+ [ cmp eq reg 1 0xb80d0120 0x00000000 0x00000000 0x33010000 ]
+
+# icmpv6 type { nd-neighbor-solicit, nd-neighbor-advert } icmpv6 taddr 2001:db8::133
+__set%d test-ip6 3 size 2
+__set%d test-ip6 0
+ element 00000087 : 0 [end] element 00000088 : 0 [end]
+ip6 test-ip6 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000003a ]
+ [ payload load 1b @ transport header + 0 => reg 1 ]
+ [ lookup reg 1 set __set%d ]
+ [ payload load 16b @ transport header + 8 => reg 1 ]
+ [ cmp eq reg 1 0xb80d0120 0x00000000 0x00000000 0x33010000 ]
+
+# icmpv6 daddr 2001:db8::133
+ip6 test-ip6 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000003a ]
+ [ payload load 1b @ transport header + 0 => reg 1 ]
+ [ cmp eq reg 1 0x00000089 ]
+ [ payload load 16b @ transport header + 24 => reg 1 ]
+ [ cmp eq reg 1 0xb80d0120 0x00000000 0x00000000 0x33010000 ]
+
+# icmpv6 type nd-redirect icmpv6 daddr 2001:db8::133
+ip6 test-ip6 input
+ [ meta load l4proto => reg 1 ]
+ [ cmp eq reg 1 0x0000003a ]
+ [ payload load 1b @ transport header + 0 => reg 1 ]
+ [ cmp eq reg 1 0x00000089 ]
+ [ payload load 16b @ transport header + 24 => reg 1 ]
+ [ cmp eq reg 1 0xb80d0120 0x00000000 0x00000000 0x33010000 ]
diff --git a/tests/py/ip6/ip6.t b/tests/py/ip6/ip6.t
index 2ffe318e..430dd571 100644
--- a/tests/py/ip6/ip6.t
+++ b/tests/py/ip6/ip6.t
@@ -17,6 +17,15 @@ 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
+!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
- ip6 flowlabel 33-45;ok
diff --git a/tests/py/ip6/ip6.t.json b/tests/py/ip6/ip6.t.json
index cf802175..49e5a2dd 100644
--- a/tests/py/ip6/ip6.t.json
+++ b/tests/py/ip6/ip6.t.json
@@ -135,6 +135,106 @@
}
]
+# 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 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 20dfe549..dbb430af 100644
--- a/tests/py/ip6/ip6.t.payload.inet
+++ b/tests/py/ip6/ip6.t.payload.inet
@@ -53,6 +53,50 @@ 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 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 f8e3ca3c..b1289232 100644
--- a/tests/py/ip6/ip6.t.payload.ip6
+++ b/tests/py/ip6/ip6.t.payload.ip6
@@ -41,6 +41,42 @@ 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 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 ]
diff --git a/tests/py/ip6/meta.t b/tests/py/ip6/meta.t
index 471e1481..c177b081 100644
--- a/tests/py/ip6/meta.t
+++ b/tests/py/ip6/meta.t
@@ -14,3 +14,6 @@ meta protocol ip6 udp dport 67;ok;udp dport 67
meta sdif "lo" accept;ok
meta sdifname != "vrf1" accept;ok
+
+meta mark set ip6 dscp << 2 | 0x10;ok
+meta mark set ip6 dscp << 26 | 0x10;ok
diff --git a/tests/py/ip6/meta.t.json b/tests/py/ip6/meta.t.json
index 351320d7..1a2394d8 100644
--- a/tests/py/ip6/meta.t.json
+++ b/tests/py/ip6/meta.t.json
@@ -194,3 +194,120 @@
}
}
]
+
+# meta mark set ip6 dscp lshift 2 or 0x10
+[
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "<<": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip6"
+ }
+ },
+ 2
+ ]
+ },
+ 16
+ ]
+ }
+ }
+ }
+]
+
+# meta mark set ip6 dscp lshift 26 or 0x10
+[
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "<<": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip6"
+ }
+ },
+ 26
+ ]
+ },
+ 16
+ ]
+ }
+ }
+ }
+]
+
+# meta mark set ip6 dscp << 2 | 0x10
+[
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "<<": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip6"
+ }
+ },
+ 2
+ ]
+ },
+ 16
+ ]
+ }
+ }
+ }
+]
+
+# meta mark set ip6 dscp << 26 | 0x10
+[
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "<<": [
+ {
+ "payload": {
+ "field": "dscp",
+ "protocol": "ip6"
+ }
+ },
+ 26
+ ]
+ },
+ 16
+ ]
+ }
+ }
+ }
+]
+
diff --git a/tests/py/ip6/meta.t.json.output b/tests/py/ip6/meta.t.json.output
index dede9b16..61adf184 100644
--- a/tests/py/ip6/meta.t.json.output
+++ b/tests/py/ip6/meta.t.json.output
@@ -46,3 +46,19 @@
}
]
+# meta protocol ip6 udp dport 67
+[
+ {
+ "match": {
+ "left": {
+ "payload": {
+ "field": "dport",
+ "protocol": "udp"
+ }
+ },
+ "op": "==",
+ "right": 67
+ }
+ }
+]
+
diff --git a/tests/py/ip6/meta.t.payload b/tests/py/ip6/meta.t.payload
index 0e3db6ba..6a37f1de 100644
--- a/tests/py/ip6/meta.t.payload
+++ b/tests/py/ip6/meta.t.payload
@@ -60,3 +60,23 @@ ip6 test-ip6 input
[ cmp eq reg 1 0x00000011 ]
[ payload load 2b @ transport header + 2 => reg 1 ]
[ cmp eq reg 1 0x00004300 ]
+
+# meta mark set ip6 dscp << 2 | 0x10
+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 ) ]
+ [ bitwise reg 1 = ( reg 1 << 0x00000002 ) ]
+ [ bitwise reg 1 = ( reg 1 & 0xffffffef ) ^ 0x00000010 ]
+ [ meta set mark with reg 1 ]
+
+# meta mark set ip6 dscp << 26 | 0x10
+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 ) ]
+ [ bitwise reg 1 = ( reg 1 << 0x0000001a ) ]
+ [ bitwise reg 1 = ( reg 1 & 0xffffffef ) ^ 0x00000010 ]
+ [ meta set mark with reg 1 ]
diff --git a/tests/py/ip6/redirect.t b/tests/py/ip6/redirect.t
index 778d53f3..70ef7f9f 100644
--- a/tests/py/ip6/redirect.t
+++ b/tests/py/ip6/redirect.t
@@ -46,4 +46,4 @@ ip6 daddr fe00::1-fe00::200 udp dport 53 counter redirect;ok
iifname "eth0" ct state established,new tcp dport vmap {22 : drop, 222 : drop } redirect;ok
# redirect with maps
-ip6 nexthdr 6 redirect to :tcp dport map { 22 : 8000, 80 : 8080};ok
+redirect to :tcp dport map { 22 : 8000, 80 : 8080};ok
diff --git a/tests/py/ip6/redirect.t.json b/tests/py/ip6/redirect.t.json
index 0059c7ac..c18223fa 100644
--- a/tests/py/ip6/redirect.t.json
+++ b/tests/py/ip6/redirect.t.json
@@ -557,21 +557,9 @@
}
]
-# ip6 nexthdr 6 redirect to :tcp dport map { 22 : 8000, 80 : 8080}
+# redirect to :tcp dport map { 22 : 8000, 80 : 8080}
[
{
- "match": {
- "left": {
- "payload": {
- "field": "nexthdr",
- "protocol": "ip6"
- }
- },
- "op": "==",
- "right": 6
- }
- },
- {
"redirect": {
"port": {
"map": {
diff --git a/tests/py/ip6/redirect.t.payload.ip6 b/tests/py/ip6/redirect.t.payload.ip6
index e9a20316..cfc29013 100644
--- a/tests/py/ip6/redirect.t.payload.ip6
+++ b/tests/py/ip6/redirect.t.payload.ip6
@@ -191,12 +191,12 @@ ip6 test-ip6 output
[ lookup reg 1 set __map%d dreg 0 ]
[ redir ]
-# ip6 nexthdr 6 redirect to :tcp dport map { 22 : 8000, 80 : 8080}
+# redirect to :tcp dport map { 22 : 8000, 80 : 8080}
__map%d test-ip6 b
__map%d test-ip6 0
element 00001600 : 0000401f 0 [end] element 00005000 : 0000901f 0 [end]
ip6 test-ip6 output
- [ payload load 1b @ network header + 6 => reg 1 ]
+ [ meta load l4proto => reg 1 ]
[ cmp eq reg 1 0x00000006 ]
[ payload load 2b @ transport header + 2 => reg 1 ]
[ lookup reg 1 set __map%d dreg 1 ]
diff --git a/tests/py/ip6/sets.t b/tests/py/ip6/sets.t
index add82eb8..17fd62f5 100644
--- a/tests/py/ip6/sets.t
+++ b/tests/py/ip6/sets.t
@@ -1,9 +1,10 @@
:input;type filter hook input priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*ip6;test-ip6;input
*inet;test-inet;input
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
!w type ipv6_addr;ok
!x type inet_proto;ok
@@ -40,4 +41,8 @@ ip6 saddr != @set33 drop;fail
!set5 type ipv6_addr . ipv6_addr;ok
ip6 saddr . ip6 daddr @set5 drop;ok
add @set5 { ip6 saddr . ip6 daddr };ok
+
+!map1 type ipv6_addr . ipv6_addr : mark;ok
+add @map1 { ip6 saddr . ip6 daddr : meta mark };ok
+
delete @set5 { ip6 saddr . ip6 daddr };ok
diff --git a/tests/py/ip6/sets.t.json b/tests/py/ip6/sets.t.json
index 948c1f16..2029d2b5 100644
--- a/tests/py/ip6/sets.t.json
+++ b/tests/py/ip6/sets.t.json
@@ -116,3 +116,35 @@
}
}
]
+
+# add @map1 { ip6 saddr . ip6 daddr : meta mark }
+[
+ {
+ "map": {
+ "data": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "elem": {
+ "concat": [
+ {
+ "payload": {
+ "field": "saddr",
+ "protocol": "ip6"
+ }
+ },
+ {
+ "payload": {
+ "field": "daddr",
+ "protocol": "ip6"
+ }
+ }
+ ]
+ },
+ "map": "@map1",
+ "op": "add"
+ }
+ }
+]
+
diff --git a/tests/py/ip6/sets.t.payload.inet b/tests/py/ip6/sets.t.payload.inet
index 47ad86a2..2bbd5573 100644
--- a/tests/py/ip6/sets.t.payload.inet
+++ b/tests/py/ip6/sets.t.payload.inet
@@ -31,6 +31,15 @@ inet test-inet input
[ payload load 16b @ network header + 24 => reg 2 ]
[ dynset add reg_key 1 set set5 ]
+# add @map1 { ip6 saddr . ip6 daddr : meta mark }
+inet test-inet input
+ [ meta load nfproto => reg 1 ]
+ [ cmp eq reg 1 0x0000000a ]
+ [ payload load 16b @ network header + 8 => reg 1 ]
+ [ payload load 16b @ network header + 24 => reg 2 ]
+ [ meta load mark => reg 3 ]
+ [ dynset add reg_key 1 set map1 sreg_data 3 ]
+
# delete @set5 { ip6 saddr . ip6 daddr }
inet test-inet input
[ meta load nfproto => reg 1 ]
diff --git a/tests/py/ip6/sets.t.payload.ip6 b/tests/py/ip6/sets.t.payload.ip6
index a5febb9f..c59f7b5c 100644
--- a/tests/py/ip6/sets.t.payload.ip6
+++ b/tests/py/ip6/sets.t.payload.ip6
@@ -29,3 +29,10 @@ ip6 test-ip6 input
[ payload load 16b @ network header + 24 => reg 2 ]
[ dynset delete reg_key 1 set set5 ]
+# add @map1 { ip6 saddr . ip6 daddr : meta mark }
+ip6 test-ip6 input
+ [ payload load 16b @ network header + 8 => reg 1 ]
+ [ payload load 16b @ network header + 24 => reg 2 ]
+ [ meta load mark => reg 3 ]
+ [ dynset add reg_key 1 set map1 sreg_data 3 ]
+
diff --git a/tests/py/ip6/sets.t.payload.netdev b/tests/py/ip6/sets.t.payload.netdev
index dab74159..1866d26b 100644
--- a/tests/py/ip6/sets.t.payload.netdev
+++ b/tests/py/ip6/sets.t.payload.netdev
@@ -39,3 +39,12 @@ netdev test-netdev ingress
[ payload load 16b @ network header + 24 => reg 2 ]
[ dynset delete reg_key 1 set set5 ]
+# add @map1 { ip6 saddr . ip6 daddr : meta mark }
+netdev test-netdev ingress
+ [ meta load protocol => reg 1 ]
+ [ cmp eq reg 1 0x0000dd86 ]
+ [ payload load 16b @ network header + 8 => reg 1 ]
+ [ payload load 16b @ network header + 24 => reg 2 ]
+ [ meta load mark => reg 3 ]
+ [ dynset add reg_key 1 set map1 sreg_data 3 ]
+
diff --git a/tests/py/ip6/srh.t.payload b/tests/py/ip6/srh.t.payload
index b6247456..364940a9 100644
--- a/tests/py/ip6/srh.t.payload
+++ b/tests/py/ip6/srh.t.payload
@@ -11,7 +11,7 @@ ip6 test-ip6 input
# srh last-entry { 0, 4-127, 255 }
__set%d test-ip6 7 size 5
__set%d test-ip6 0
- element 00000000 : 0 [end] element 00000001 : 1 [end] element 00000004 : 0 [end] element 00000080 : 1 [end] element 000000ff : 0 [end] userdata = {
+ element 00000000 : 0 [end] element 00000001 : 1 [end] element 00000004 : 0 [end] element 00000080 : 1 [end] element 000000ff : 0 [end] userdata = { \x01\x04\x01\x00\x00\x00 }
ip6 test-ip6 input
[ exthdr load ipv6 1b @ 43 + 4 => reg 1 ]
[ lookup reg 1 set __set%d ]
@@ -29,7 +29,7 @@ ip6 test-ip6 input
# srh flags { 0, 4-127, 255 }
__set%d test-ip6 7 size 5
__set%d test-ip6 0
- element 00000000 : 0 [end] element 00000001 : 1 [end] element 00000004 : 0 [end] element 00000080 : 1 [end] element 000000ff : 0 [end] userdata = {
+ element 00000000 : 0 [end] element 00000001 : 1 [end] element 00000004 : 0 [end] element 00000080 : 1 [end] element 000000ff : 0 [end] userdata = { \x01\x04\x01\x00\x00\x00 }
ip6 test-ip6 input
[ exthdr load ipv6 1b @ 43 + 5 => reg 1 ]
[ lookup reg 1 set __set%d ]
@@ -47,7 +47,7 @@ ip6 test-ip6 input
# srh tag { 0, 4-127, 0xffff }
__set%d test-ip6 7 size 5
__set%d test-ip6 0
- element 00000000 : 0 [end] element 00000100 : 1 [end] element 00000400 : 0 [end] element 00008000 : 1 [end] element 0000ffff : 0 [end] userdata = {
+ element 00000000 : 0 [end] element 00000100 : 1 [end] element 00000400 : 0 [end] element 00008000 : 1 [end] element 0000ffff : 0 [end] userdata = { \x01\x04\x01\x00\x00\x00 }
ip6 test-ip6 input
[ exthdr load ipv6 2b @ 43 + 6 => reg 1 ]
[ lookup reg 1 set __set%d ]
diff --git a/tests/py/ip6/vmap.t b/tests/py/ip6/vmap.t
index 434f5d92..2d54b822 100644
--- a/tests/py/ip6/vmap.t
+++ b/tests/py/ip6/vmap.t
@@ -1,9 +1,10 @@
:input;type filter hook input priority 0
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
*ip6;test-ip6;input
*inet;test-inet;input
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
ip6 saddr vmap { abcd::3 : accept };ok
ip6 saddr 1234:1234:1234:1234:1234:1234:1234:1234:1234;fail
diff --git a/tests/py/ip6/vmap.t.payload.inet b/tests/py/ip6/vmap.t.payload.inet
index 522564a3..931cc6bd 100644
--- a/tests/py/ip6/vmap.t.payload.inet
+++ b/tests/py/ip6/vmap.t.payload.inet
@@ -371,7 +371,7 @@ inet test-inet input
# ip6 saddr vmap { ::/64 : accept}
__map%d test-inet f
__map%d test-inet 0
- element 00000000 00000000 00000000 00000000 : accept 0 [end] element 00000000 01000000 00000000 00000000 : drop 1 [end]
+ element 00000000 00000000 00000000 00000000 : accept 0 [end] element 00000000 01000000 00000000 00000000 : 1 [end]
inet test-inet input
[ meta load nfproto => reg 1 ]
[ cmp eq reg 1 0x0000000a ]
diff --git a/tests/py/ip6/vmap.t.payload.ip6 b/tests/py/ip6/vmap.t.payload.ip6
index 3850a7c5..6e077b27 100644
--- a/tests/py/ip6/vmap.t.payload.ip6
+++ b/tests/py/ip6/vmap.t.payload.ip6
@@ -297,7 +297,7 @@ ip6 test-ip6 input
# ip6 saddr vmap { ::/64 : accept}
__map%d test-ip6 f
__map%d test-ip6 0
- element 00000000 00000000 00000000 00000000 : accept 0 [end] element 00000000 01000000 00000000 00000000 : drop 1 [end]
+ element 00000000 00000000 00000000 00000000 : accept 0 [end] element 00000000 01000000 00000000 00000000 : 1 [end]
ip6 test-ip6 input
[ payload load 16b @ network header + 8 => reg 1 ]
[ lookup reg 1 set __map%d dreg 0 ]
diff --git a/tests/py/ip6/vmap.t.payload.netdev b/tests/py/ip6/vmap.t.payload.netdev
index d9cbad58..45f2c0b0 100644
--- a/tests/py/ip6/vmap.t.payload.netdev
+++ b/tests/py/ip6/vmap.t.payload.netdev
@@ -371,7 +371,7 @@ netdev test-netdev ingress
# ip6 saddr vmap { ::/64 : accept}
__map%d test-netdev f
__map%d test-netdev 0
- element 00000000 00000000 00000000 00000000 : accept 0 [end] element 00000000 01000000 00000000 00000000 : drop 1 [end]
+ element 00000000 00000000 00000000 00000000 : accept 0 [end] element 00000000 01000000 00000000 00000000 : 1 [end]
netdev test-netdev ingress
[ meta load protocol => reg 1 ]
[ cmp eq reg 1 0x0000dd86 ]
diff --git a/tests/py/netdev/dup.t b/tests/py/netdev/dup.t
index 181b4195..56328022 100644
--- a/tests/py/netdev/dup.t
+++ b/tests/py/netdev/dup.t
@@ -1,6 +1,7 @@
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
dup to "lo";ok
dup to meta mark map { 0x00000001 : "lo", 0x00000002 : "lo"};ok
diff --git a/tests/py/netdev/fwd.t b/tests/py/netdev/fwd.t
index 2e34d55a..6051560a 100644
--- a/tests/py/netdev/fwd.t
+++ b/tests/py/netdev/fwd.t
@@ -1,6 +1,7 @@
:ingress;type filter hook ingress device lo priority 0
+:egress;type filter hook egress device lo priority 0
-*netdev;test-netdev;ingress
+*netdev;test-netdev;ingress,egress
fwd to "lo";ok
fwd to meta mark map { 0x00000001 : "lo", 0x00000002 : "lo"};ok
diff --git a/tests/py/nft-test.py b/tests/py/nft-test.py
index f8f9341c..1bc89558 100755
--- a/tests/py/nft-test.py
+++ b/tests/py/nft-test.py
@@ -39,7 +39,7 @@ signal_received = 0
class Colors:
- if sys.stdout.isatty():
+ if sys.stdout.isatty() and sys.stderr.isatty():
HEADER = '\033[95m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
@@ -86,11 +86,12 @@ class Table:
class Set:
"""Class that represents a set"""
- def __init__(self, family, table, name, type, timeout, flags):
+ def __init__(self, family, table, name, type, data, timeout, flags):
self.family = family
self.table = table
self.name = name
self.type = type
+ self.data = data
self.timeout = timeout
self.flags = flags
@@ -366,7 +367,11 @@ def set_add(s, test_result, filename, lineno):
if flags != "":
flags = "flags %s; " % flags
- cmd = "add set %s %s { type %s;%s %s}" % (table, s.name, s.type, s.timeout, flags)
+ if s.data == "":
+ cmd = "add set %s %s { %s;%s %s}" % (table, s.name, s.type, s.timeout, flags)
+ else:
+ 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)
if (ret == 0 and test_result == "fail") or \
@@ -384,6 +389,44 @@ def set_add(s, test_result, filename, lineno):
return 0
+def map_add(s, test_result, filename, lineno):
+ '''
+ Adds a map
+ '''
+ if not table_list:
+ reason = "Missing table to add rule"
+ print_error(reason, filename, lineno)
+ return -1
+
+ for table in table_list:
+ s.table = table.name
+ s.family = table.family
+ if _map_exist(s, filename, lineno):
+ reason = "Map %s already exists in %s" % (s.name, table)
+ print_error(reason, filename, lineno)
+ return -1
+
+ flags = s.flags
+ if flags != "":
+ flags = "flags %s; " % 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)
+
+ if (ret == 0 and test_result == "fail") or \
+ (ret != 0 and test_result == "ok"):
+ reason = "%s: I cannot add the set %s" % (cmd, s.name)
+ print_error(reason, filename, lineno)
+ return -1
+
+ if not _map_exist(s, filename, lineno):
+ reason = "I have just added the set %s to " \
+ "the table %s but it does not exist" % (s.name, table)
+ print_error(reason, filename, lineno)
+ return -1
+
+
def set_add_elements(set_element, set_name, state, filename, lineno):
'''
Adds elements to the set.
@@ -490,6 +533,16 @@ def _set_exist(s, filename, lineno):
return True if (ret == 0) else False
+def _map_exist(s, filename, lineno):
+ '''
+ Check if the map exists.
+ '''
+ cmd = "list map %s %s %s" % (s.family, s.table, s.name)
+ ret = execute_cmd(cmd, filename, lineno)
+
+ return True if (ret == 0) else False
+
+
def set_check_element(rule1, rule2):
'''
Check if element exists in anonymous sets.
@@ -756,6 +809,8 @@ def rule_add(rule, filename, lineno, force_all_family_option, filename_path):
reason = "Invalid JSON syntax in expected output: %s" % json_expected
print_error(reason)
return [-1, warning, error, unit_tests]
+ if json_expected == json_input:
+ print_warning("Recorded JSON output matches input for: %s" % rule[0])
for table in table_list:
if rule[1].strip() == "ok":
@@ -809,17 +864,26 @@ def rule_add(rule, filename, lineno, force_all_family_option, filename_path):
if state == "ok" and not payload_check(table_payload_expected,
payload_log, cmd):
error += 1
- gotf = open("%s.got" % payload_path, 'a')
+
+ try:
+ gotf = open("%s.got" % payload_path)
+ gotf_payload_expected = payload_find_expected(gotf, rule[0])
+ gotf.close()
+ except:
+ gotf_payload_expected = None
payload_log.seek(0, 0)
- gotf.write("# %s\n" % rule[0])
- while True:
- line = payload_log.readline()
- if line == "":
- break
- gotf.write(line)
- gotf.close()
- print_warning("Wrote payload for rule %s" % rule[0],
- gotf.name, 1)
+ if not payload_check(gotf_payload_expected, payload_log, cmd):
+ gotf = open("%s.got" % payload_path, 'a')
+ payload_log.seek(0, 0)
+ gotf.write("# %s\n" % rule[0])
+ while True:
+ line = payload_log.readline()
+ if line == "":
+ break
+ gotf.write(line)
+ gotf.close()
+ print_warning("Wrote payload for rule %s" % rule[0],
+ gotf.name, 1)
# Check for matching ruleset listing
numeric_proto_old = nftables.set_numeric_proto_output(True)
@@ -1082,14 +1146,28 @@ 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
+ while len(tokens) > i and tokens[i] == ":":
+ 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
@@ -1099,9 +1177,13 @@ def set_process(set_line, filename, lineno):
elif len(tokens) != i:
print_error(set_name + " bad flag: " + tokens[i], filename, lineno)
- s = Set("", "", set_name, set_type, timeout, set_flags)
+ s = Set("", "", set_name, set_type, set_data, timeout, set_flags)
+
+ if set_data == "":
+ ret = set_add(s, test_result, filename, lineno)
+ else:
+ ret = map_add(s, test_result, filename, lineno)
- ret = set_add(s, test_result, filename, lineno)
if ret == 0:
all_set[set_name] = set()
diff --git a/tests/shell/README b/tests/shell/README
index e0279bbd..3af17a9e 100644
--- a/tests/shell/README
+++ b/tests/shell/README
@@ -1,16 +1,20 @@
-This test-suite is intended to perform tests of higher level than
-the other regression test-suite.
+This test suite is intended to perform tests on a higher level
+than the other regression test suites.
-It can run arbitrary executables which can perform any test apart of testing
-the nft syntax or netlink code (which is what the regression tests does).
+It can run arbitrary executables which can perform any test, not
+limited to testing the nft syntax or netlink code (which is what
+the regression tests do).
To run the test suite (as root):
$ cd tests/shell
# ./run-tests.sh
-Test files are executables files with the pattern <<name_N>>, where N is the
-expected return code of the executable. Since they are located with `find',
-test-files can be spread in any sub-directories.
+Test files are executable files matching the pattern <<name_N>>,
+where N should be 0 in all new tests. All tests should return 0 on
+success.
+
+Since they are located with `find', test files can be put in any
+subdirectory.
You can turn on a verbose execution by calling:
# ./run-tests.sh -v
@@ -18,11 +22,14 @@ You can turn on a verbose execution by calling:
And generate missing dump files with:
# ./run-tests.sh -g <TESTFILE>
-Before each call to the test-files, `nft flush ruleset' will be called.
-Also, test-files will receive the environment variable $NFT which contains the
-path to the nftables binary being tested.
+Before each test file invocation, `nft flush ruleset' will be called.
+Also, test file process environment will include the variable $NFT
+which contains the nft command being tested.
You can pass an arbitrary $NFT value as well:
# NFT=/usr/local/sbin/nft ./run-tests.sh
-By default the tests are run with the nft binary at '../../src/nft'
+Note that, to support usage such as NFT='valgrind nft', tests must
+invoke $NFT unquoted.
+
+By default, the tests are run with the nft binary at '../../src/nft'
diff --git a/tests/shell/features/bitshift.nft b/tests/shell/features/bitshift.nft
new file mode 100644
index 00000000..7f9ccb64
--- /dev/null
+++ b/tests/shell/features/bitshift.nft
@@ -0,0 +1,7 @@
+# 567d746b55bc ("netfilter: bitwise: add support for shifts.")
+# v5.6-rc1~151^2~73^2
+table ip t {
+ chain c {
+ meta mark set meta mark << 2
+ }
+}
diff --git a/tests/shell/features/catchall_element.nft b/tests/shell/features/catchall_element.nft
new file mode 100644
index 00000000..1a02fd61
--- /dev/null
+++ b/tests/shell/features/catchall_element.nft
@@ -0,0 +1,8 @@
+# aaa31047a6d2 ("netfilter: nftables: add catch-all set element support")
+# v5.13-rc1~94^2~10^2~2
+table t {
+ map m {
+ type inet_service : inet_service
+ elements = { * : 42 }
+ }
+}
diff --git a/tests/shell/features/chain_binding.nft b/tests/shell/features/chain_binding.nft
new file mode 100644
index 00000000..b381ec54
--- /dev/null
+++ b/tests/shell/features/chain_binding.nft
@@ -0,0 +1,7 @@
+# d0e2c7de92c7 ("netfilter: nf_tables: add NFT_CHAIN_BINDING")
+# v5.9-rc1~133^2~302^2~1
+table ip t {
+ chain c {
+ jump { counter; }
+ }
+}
diff --git a/tests/shell/features/comment.sh b/tests/shell/features/comment.sh
new file mode 100755
index 00000000..0ad24d04
--- /dev/null
+++ b/tests/shell/features/comment.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+# 002f21765320 ("netfilter: nf_tables: add userdata attributes to nft_chain")
+# v5.10-rc1~107^2~60^2~5
+
+EXPECTED="table ip x {
+ chain y {
+ comment \"test\"
+ }
+}"
+
+$NFT -f - <<< $EXPECTED
+
+diff -u <($NFT list ruleset) - <<<"$EXPECTED"
diff --git a/tests/shell/features/ctexpect.nft b/tests/shell/features/ctexpect.nft
new file mode 100644
index 00000000..02c3dfd7
--- /dev/null
+++ b/tests/shell/features/ctexpect.nft
@@ -0,0 +1,10 @@
+# 857b46027d6f ("netfilter: nft_ct: add ct expectations support")
+# v5.3-rc1~140^2~153^2~19
+table t {
+ ct expectation ctexpect {
+ protocol tcp
+ dport 5432
+ timeout 1h
+ size 12;
+ }
+}
diff --git a/tests/shell/features/cttimeout.nft b/tests/shell/features/cttimeout.nft
new file mode 100644
index 00000000..4be58cd3
--- /dev/null
+++ b/tests/shell/features/cttimeout.nft
@@ -0,0 +1,8 @@
+# 7e0b2b57f01d ("netfilter: nft_ct: add ct timeout support")
+# v4.19-rc1~140^2~64^2~3
+table t {
+ ct timeout cttime {
+ protocol tcp;
+ policy = {established: 120 }
+ }
+}
diff --git a/tests/shell/features/destroy.nft b/tests/shell/features/destroy.nft
new file mode 100644
index 00000000..b97242e4
--- /dev/null
+++ b/tests/shell/features/destroy.nft
@@ -0,0 +1,3 @@
+# f80a612dd77c ("netfilter: nf_tables: add support to destroy operation")
+# v6.3-rc1~162^2~264^2
+destroy table t
diff --git a/tests/shell/features/dynset_op_delete.nft b/tests/shell/features/dynset_op_delete.nft
new file mode 100644
index 00000000..125b4526
--- /dev/null
+++ b/tests/shell/features/dynset_op_delete.nft
@@ -0,0 +1,12 @@
+# d0a8d877da97 ("netfilter: nft_dynset: support for element deletion")
+# v5.4-rc1~131^2~59^2~4
+table ip x {
+ set s {
+ flags dynamic;
+ type inet_service;
+ }
+
+ chain y {
+ delete @s { tcp dport }
+ }
+}
diff --git a/tests/shell/features/flowtable_counter.sh b/tests/shell/features/flowtable_counter.sh
new file mode 100755
index 00000000..a4c4c621
--- /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/features/flowtable_no_devices.nft b/tests/shell/features/flowtable_no_devices.nft
new file mode 100755
index 00000000..30dd3db8
--- /dev/null
+++ b/tests/shell/features/flowtable_no_devices.nft
@@ -0,0 +1,8 @@
+# 05abe4456fa3 ("netfilter: nf_tables: allow to register flowtable with no devices")
+# v5.8-rc1~165^2~27^2~1
+table ip filter2 {
+ flowtable main_ft2 {
+ hook ingress priority filter
+ counter
+ }
+}
diff --git a/tests/shell/features/inet_ingress.nft b/tests/shell/features/inet_ingress.nft
new file mode 100644
index 00000000..944a5c77
--- /dev/null
+++ b/tests/shell/features/inet_ingress.nft
@@ -0,0 +1,7 @@
+# d3519cb89f6d ("netfilter: nf_tables: add inet ingress support")
+# v5.10-rc1~107^2~17^2~1
+table inet t {
+ chain c {
+ type filter hook ingress device "lo" priority filter; policy accept;
+ }
+}
diff --git a/tests/shell/features/inet_nat.nft b/tests/shell/features/inet_nat.nft
new file mode 100644
index 00000000..189ea1d0
--- /dev/null
+++ b/tests/shell/features/inet_nat.nft
@@ -0,0 +1,7 @@
+# v5.2-rc1~133^2~174^2~15
+# d164385ec572 ("netfilter: nat: add inet family nat support")
+table inet x {
+ chain y {
+ type nat hook prerouting priority dstnat;
+ }
+}
diff --git a/tests/shell/features/inner_matching.nft b/tests/shell/features/inner_matching.nft
new file mode 100644
index 00000000..6c86fd35
--- /dev/null
+++ b/tests/shell/features/inner_matching.nft
@@ -0,0 +1,7 @@
+# 3a07327d10a0 ("netfilter: nft_inner: support for inner tunnel header matching")
+# v6.2-rc1~99^2~350^2~4
+table ip t {
+ chain c {
+ udp dport 4789 vxlan ip saddr 1.2.3.4
+ }
+}
diff --git a/tests/shell/features/json.sh b/tests/shell/features/json.sh
new file mode 100755
index 00000000..d8115702
--- /dev/null
+++ b/tests/shell/features/json.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+# Detect JSON support. Note that $NFT may not be the binary from our build
+# tree, hence we detect it by running the binary (instead of asking the build
+# configuration).
+$NFT -j list ruleset
diff --git a/tests/shell/features/map_lookup.nft b/tests/shell/features/map_lookup.nft
new file mode 100644
index 00000000..06c4c9d9
--- /dev/null
+++ b/tests/shell/features/map_lookup.nft
@@ -0,0 +1,11 @@
+# a4878eeae390 ("netfilter: nf_tables: relax set/map validation checks")
+# v6.5-rc1~163^2~256^2~8
+table ip t {
+ map m {
+ typeof ip daddr : meta mark
+ }
+
+ chain c {
+ ip saddr @m
+ }
+}
diff --git a/tests/shell/features/meta_time.nft b/tests/shell/features/meta_time.nft
new file mode 100644
index 00000000..34550de4
--- /dev/null
+++ b/tests/shell/features/meta_time.nft
@@ -0,0 +1,7 @@
+# 63d10e12b00d ("netfilter: nft_meta: support for time matching")
+# v5.4-rc1~131^2~59^2~6
+table ip t {
+ chain c {
+ meta time "1970-05-23 21:07:14"
+ }
+}
diff --git a/tests/shell/features/netdev_chain_multidevice.sh b/tests/shell/features/netdev_chain_multidevice.sh
new file mode 100755
index 00000000..d2a56d6d
--- /dev/null
+++ b/tests/shell/features/netdev_chain_multidevice.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+# d54725cd11a5 ("netfilter: nf_tables: support for multiple devices per netdev hook")
+# v5.5-rc1~174^2~312^2~4
+
+trap "ip link del d0; ip link del d1" EXIT
+
+ip link add d0 type dummy
+ip link add d1 type dummy
+
+EXPECTED="table netdev filter2 {
+ chain Main_Ingress2 {
+ type filter hook ingress devices = { \"d0\", \"d1\" } priority -500; policy accept;
+ }
+}"
+
+$NFT -f - <<< $EXPECTED
diff --git a/tests/shell/features/netdev_chain_without_device.nft b/tests/shell/features/netdev_chain_without_device.nft
new file mode 100644
index 00000000..25eb200f
--- /dev/null
+++ b/tests/shell/features/netdev_chain_without_device.nft
@@ -0,0 +1,7 @@
+# 207296f1a03b ("netfilter: nf_tables: allow to create netdev chain without device")
+# v6.4-rc1~132^2~14^2
+table netdev t {
+ chain c {
+ type filter hook ingress priority 0; policy accept;
+ }
+}
diff --git a/tests/shell/features/netdev_egress.nft b/tests/shell/features/netdev_egress.nft
new file mode 100644
index 00000000..67d706d8
--- /dev/null
+++ b/tests/shell/features/netdev_egress.nft
@@ -0,0 +1,7 @@
+# 42df6e1d221d ("netfilter: Introduce egress hook")
+# v5.16-rc1~159^2~167^2~10
+table netdev t {
+ chain c {
+ type filter hook egress devices = { lo } priority 0; policy accept;
+ }
+}
diff --git a/tests/shell/features/netmap.nft b/tests/shell/features/netmap.nft
new file mode 100644
index 00000000..2580a8dc
--- /dev/null
+++ b/tests/shell/features/netmap.nft
@@ -0,0 +1,8 @@
+# 3ff7ddb1353d ("netfilter: nft_nat: add netmap support")
+# v5.8-rc1~165^2~393^2
+table ip x {
+ chain y {
+ type nat hook postrouting priority srcnat; policy accept;
+ snat ip prefix to ip saddr map { 10.141.11.0/24 : 192.168.2.0/24 }
+ }
+}
diff --git a/tests/shell/features/osf.nft b/tests/shell/features/osf.nft
new file mode 100644
index 00000000..dbb6b4c3
--- /dev/null
+++ b/tests/shell/features/osf.nft
@@ -0,0 +1,7 @@
+# b96af92d6eaf ("netfilter: nf_tables: implement Passive OS fingerprint module in nft_osf")
+# v4.19-rc1~140^2~135^2~15
+table t {
+ chain c {
+ osf name "Linux"
+ }
+}
diff --git a/tests/shell/features/pipapo.nft b/tests/shell/features/pipapo.nft
new file mode 100644
index 00000000..3557721e
--- /dev/null
+++ b/tests/shell/features/pipapo.nft
@@ -0,0 +1,9 @@
+# 3c4287f62044 ("nf_tables: Add set type for arbitrary concatenation of ranges")
+# v5.6-rc1~151^2~28^2~1
+table t {
+ set s {
+ type ipv4_addr . inet_service
+ flags interval
+ elements = { 1.1.1.1-2.2.2.2 . 80-90 }
+ }
+}
diff --git a/tests/shell/features/prerouting_reject.nft b/tests/shell/features/prerouting_reject.nft
new file mode 100644
index 00000000..3dcfb40e
--- /dev/null
+++ b/tests/shell/features/prerouting_reject.nft
@@ -0,0 +1,8 @@
+# f53b9b0bdc59 netfilter: introduce support for reject at prerouting stage
+# v5.9-rc1~133^2~302^2~11
+table inet t {
+ chain nat_filter {
+ type filter hook prerouting priority 0; policy accept;
+ reject with icmpx type host-unreachable
+ }
+}
diff --git a/tests/shell/features/reset_rule.sh b/tests/shell/features/reset_rule.sh
new file mode 100755
index 00000000..567ee2f1
--- /dev/null
+++ b/tests/shell/features/reset_rule.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+# 8daa8fde3fc3 ("netfilter: nf_tables: Introduce NFT_MSG_GETRULE_RESET")
+# v6.2-rc1~99^2~210^2~2
+
+unshare -n bash -c "$NFT \"add table t; add chain t c ; add rule t c counter packets 1 bytes 42\"; \
+$NFT reset rules chain t c ; \
+$NFT reset rules chain t c |grep counter\ packets\ 0\ bytes\ 0"
diff --git a/tests/shell/features/reset_set.sh b/tests/shell/features/reset_set.sh
new file mode 100755
index 00000000..3d034175
--- /dev/null
+++ b/tests/shell/features/reset_set.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+# 079cd633219d ("netfilter: nf_tables: Introduce NFT_MSG_GETSETELEM_RESET")
+# v6.5-rc1~163^2~9^2~1
+
+unshare -n bash -c "$NFT add table t; \
+ $NFT add set t s { type ipv4_addr\; counter\; elements = { 127.0.0.1 counter packets 1 bytes 2 } } ; \
+ $NFT reset set t s ; \
+ $NFT reset set t s | grep counter\ packets\ 0\ bytes\ 0
+"
diff --git a/tests/shell/features/reset_tcp_options.nft b/tests/shell/features/reset_tcp_options.nft
new file mode 100644
index 00000000..47d1c7b8
--- /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/features/sctp_chunks.nft b/tests/shell/features/sctp_chunks.nft
new file mode 100644
index 00000000..520afd64
--- /dev/null
+++ b/tests/shell/features/sctp_chunks.nft
@@ -0,0 +1,7 @@
+# 133dc203d77d ("netfilter: nft_exthdr: Support SCTP chunks")
+# v5.14-rc1~119^2~373^2~15
+table ip t {
+ chain c {
+ sctp chunk init 0
+ }
+}
diff --git a/tests/shell/features/secmark.nft b/tests/shell/features/secmark.nft
new file mode 100644
index 00000000..ccbb572f
--- /dev/null
+++ b/tests/shell/features/secmark.nft
@@ -0,0 +1,7 @@
+# fb961945457f ("netfilter: nf_tables: add SECMARK support")
+# v4.20-rc1~14^2~125^2~5
+table inet x {
+ secmark ssh_server {
+ "system_u:object_r:ssh_server_packet_t:s0"
+ }
+}
diff --git a/tests/shell/features/set_expr.sh b/tests/shell/features/set_expr.sh
new file mode 100755
index 00000000..fbdfc228
--- /dev/null
+++ b/tests/shell/features/set_expr.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+# 65038428b2c6 ("netfilter: nf_tables: allow to specify stateful expression in set definition")
+# v5.7-rc1~146^2~12^2~25
+
+# NFT_SET_EXPR to detect kernel feature only available since
+# b4e70d8dd9ea ("netfilter: nftables: add set expression flags")
+# v5.11-rc3~39^2^2
+
+EXPECTED="table ip x {
+ set y {
+ typeof ip saddr
+ counter
+ }
+}"
+
+$NFT -f - <<< $EXPECTED
+
+diff -u <($NFT list ruleset) - <<<"$EXPECTED"
diff --git a/tests/shell/features/set_with_two_expressions.nft b/tests/shell/features/set_with_two_expressions.nft
new file mode 100644
index 00000000..97632a7a
--- /dev/null
+++ b/tests/shell/features/set_with_two_expressions.nft
@@ -0,0 +1,9 @@
+# 48b0ae046ee9 ("netfilter: nftables: netlink support for several set element expressions")
+# v5.11-rc1~169^2~25^2
+table x {
+ set y {
+ type ipv4_addr
+ size 65535
+ counter quota 500 bytes
+ }
+}
diff --git a/tests/shell/features/setelem_expiration.sh b/tests/shell/features/setelem_expiration.sh
new file mode 100755
index 00000000..c539ceba
--- /dev/null
+++ b/tests/shell/features/setelem_expiration.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+# v5.3-rc1~140^2~153^2~8
+# 79ebb5bb4e38 ("netfilter: nf_tables: enable set expiration time for set elements")
+
+RULESET="table ip x {
+ set y {
+ type ipv4_addr
+ flags dynamic
+ timeout 1h
+ }
+}"
+
+$NFT -f - <<< $RULESET
+
+$NFT add element ip x y { 1.1.1.1 timeout 1h expires 15m59s }
+
+$NFT list ruleset | grep "expires 15m"
diff --git a/tests/shell/features/stateful_object_update.sh b/tests/shell/features/stateful_object_update.sh
new file mode 100755
index 00000000..62fbf7e3
--- /dev/null
+++ b/tests/shell/features/stateful_object_update.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# d62d0ba97b58 ("netfilter: nf_tables: Introduce stateful object update operation")
+# v5.4-rc1~131^2~59^2~2
+
+set -e
+$NFT add table test-ip
+$NFT add quota test-ip traffic-quota 25 mbytes
+$NFT add quota test-ip traffic-quota 50 mbytes
+
+EXPECTED="table ip test-ip {
+ quota traffic-quota {
+ 50 mbytes
+ }
+}"
+
+GET="$($NFT list ruleset)"
+if [ "$EXPECTED" != "$GET" ] ; then
+ diff -u <(echo "$EXPECTED") <(echo "$GET")
+ exit 1
+fi
diff --git a/tests/shell/features/synproxy.nft b/tests/shell/features/synproxy.nft
new file mode 100644
index 00000000..bea4f920
--- /dev/null
+++ b/tests/shell/features/synproxy.nft
@@ -0,0 +1,9 @@
+# v5.3-rc1~140^2~44^2~10
+# ad49d86e07a4 ("netfilter: nf_tables: Add synproxy support")
+table inet x {
+ synproxy https-synproxy {
+ mss 1460
+ wscale 7
+ timestamp sack-perm
+ }
+}
diff --git a/tests/shell/features/table_flag_owner.nft b/tests/shell/features/table_flag_owner.nft
new file mode 100644
index 00000000..aef122a0
--- /dev/null
+++ b/tests/shell/features/table_flag_owner.nft
@@ -0,0 +1,5 @@
+# 6001a930ce03 ("netfilter: nftables: introduce table ownership")
+# v5.12-rc1~200^2~6^2
+table t {
+ flags owner;
+}
diff --git a/tests/shell/helpers/json-diff-pretty.sh b/tests/shell/helpers/json-diff-pretty.sh
new file mode 100755
index 00000000..bebb7e8e
--- /dev/null
+++ b/tests/shell/helpers/json-diff-pretty.sh
@@ -0,0 +1,17 @@
+#!/bin/bash -e
+
+BASEDIR="$(dirname "$0")"
+
+[ $# -eq 2 ] || (echo "$0: expects two JSON files as arguments" ; exit 1)
+
+FILE1="$1"
+FILE2="$2"
+
+pretty()
+{
+ "$BASEDIR/json-pretty.sh" < "$1" 2>&1 || :
+}
+
+echo "Cmd: \"$0\" \"$FILE1\" \"$FILE2\""
+diff -u "$FILE1" "$FILE2" 2>&1 || :
+diff -u <(pretty "$FILE1") <(pretty "$FILE2") 2>&1 || :
diff --git a/tests/shell/helpers/json-pretty.sh b/tests/shell/helpers/json-pretty.sh
new file mode 100755
index 00000000..5407a842
--- /dev/null
+++ b/tests/shell/helpers/json-pretty.sh
@@ -0,0 +1,30 @@
+#!/bin/bash -e
+
+exec_pretty() {
+ # The output of this command must be stable (and `jq` and python
+ # fallback must generate the same output.
+
+ if command -v jq &>/dev/null ; then
+ # If we have, use `jq`
+ exec jq
+ fi
+
+ # Fallback to python.
+ exec python -c '
+import json
+import sys
+
+parsed = json.load(sys.stdin)
+print(json.dumps(parsed, indent=2))
+'
+}
+
+[ "$#" -le 1 ] || { echo "At most one argument supported" ; exit 1 ; }
+
+if [ "$#" -eq 1 ] ; then
+ # One argument passed. This must be a JSON file.
+ [ -f "$1" ] || { echo "File \"$1\" does not exist" ; exit 1 ; }
+ exec_pretty < "$1"
+fi
+
+exec_pretty
diff --git a/tests/shell/helpers/json-sanitize-ruleset.sh b/tests/shell/helpers/json-sanitize-ruleset.sh
new file mode 100755
index 00000000..31b85cbd
--- /dev/null
+++ b/tests/shell/helpers/json-sanitize-ruleset.sh
@@ -0,0 +1,30 @@
+#!/bin/bash -e
+
+die() {
+ printf "%s\n" "$*"
+ exit 1
+}
+
+do_sed() {
+ # Normalize the "version"/"release_name", otherwise we have to
+ # regenerate the JSON output upon new release.
+ #
+ # Also, "handle" are not stable. Normalize them 0.
+ sed \
+ -e '1s/^\({"nftables": \[{"metainfo": {"version": "\)[0-9.]\+\(", "release_name": "\)[^"]\+\(", "\)/\1VERSION\2RELEASE_NAME\3/' \
+ -e '1s/"handle": [0-9]\+\>/"handle": 0/g' \
+ "$@"
+}
+
+if [ "$#" = 0 ] ; then
+ do_sed
+ exit $?
+fi
+
+for f ; do
+ test -f "$f" || die "$0: file \"$f\" does not exist"
+done
+
+for f ; do
+ do_sed -i "$f" || die "$0: \`sed -i\` failed for \"$f\""
+done
diff --git a/tests/shell/helpers/nft-valgrind-wrapper.sh b/tests/shell/helpers/nft-valgrind-wrapper.sh
new file mode 100755
index 00000000..98bbdf43
--- /dev/null
+++ b/tests/shell/helpers/nft-valgrind-wrapper.sh
@@ -0,0 +1,31 @@
+#!/bin/bash -e
+
+SUFFIX="$(date "+%H%M%S.%6N").$$"
+
+rc=0
+libtool \
+ --mode=execute \
+ valgrind \
+ --log-file="$NFT_TEST_TESTTMPDIR/valgrind.$SUFFIX.%p.log" \
+ --trace-children=yes \
+ --leak-check=full \
+ --show-leak-kinds=all \
+ --num-callers=100 \
+ --error-exitcode=122 \
+ --vgdb-prefix="$_NFT_TEST_VALGRIND_VGDB_PREFIX-$SUFFIX" \
+ $NFT_TEST_VALGRIND_OPTS \
+ "$NFT_REAL" \
+ "$@" \
+ || rc=$?
+
+if [ "$rc" -eq 122 ] ; then
+ shopt -s nullglob
+ FILES=( "$NFT_TEST_TESTTMPDIR/valgrind.$SUFFIX."*".log" )
+ shopt -u nullglob
+ (
+ printf '%s\n' "args: $*"
+ printf '%s\n' "${FILES[*]}"
+ ) >> "$NFT_TEST_TESTTMPDIR/rc-failed-valgrind"
+fi
+
+exit $rc
diff --git a/tests/shell/helpers/random-source.sh b/tests/shell/helpers/random-source.sh
new file mode 100755
index 00000000..91a8248b
--- /dev/null
+++ b/tests/shell/helpers/random-source.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+# Commands like `sort` and `shuf` have a "--random-source" argument, for
+# generating a stable, reproducible output. However, they require an input
+# that provides sufficiently many bytes (depending on the input).
+#
+# This script generates a stream that can be used like
+#
+# shuf --random-source=<($0 "$seed")
+
+seed=""
+for a; do
+ seed="$seed${#a}:$a\n"
+done
+
+if command -v openssl &>/dev/null ; then
+ # We have openssl. Use it.
+ # https://www.gnu.org/software/coreutils/manual/html_node/Random-sources.html#Random-sources
+ #
+ # Note that we don't care that different installations/architectures generate the
+ # same output.
+ openssl enc -aes-256-ctr -pass "pass:$seed" -nosalt </dev/zero 2>/dev/null
+else
+ # Hack something. It's much slower.
+ idx=0
+ while : ; do
+ idx="$((idx++))"
+ seed="$(sha256sum <<<"$idx.$seed")"
+ echo ">>>$seed" >> a
+ seed="${seed%% *}"
+ LANG=C awk -v s="$seed" 'BEGIN{
+ for (i=1; i <= length(s); i+=2) {
+ xchar = substr(s, i, 2);
+ decnum = strtonum("0x"xchar);
+ printf("%c", decnum);
+ }
+ }' || break
+ done
+fi
+exit 0
diff --git a/tests/shell/helpers/test-wrapper.sh b/tests/shell/helpers/test-wrapper.sh
new file mode 100755
index 00000000..c016e0ce
--- /dev/null
+++ b/tests/shell/helpers/test-wrapper.sh
@@ -0,0 +1,328 @@
+#!/bin/bash -e
+
+# This wrapper wraps the invocation of the test. It is called by run-tests.sh,
+# and already in the unshared namespace.
+#
+# For some printf debugging, you can also patch this file.
+
+array_contains() {
+ local needle="$1"
+ local a
+ shift
+ for a; do
+ [ "$a" = "$needle" ] && return 0
+ done
+ return 1
+}
+
+show_file() {
+ local filename="$1"
+ shift
+ local msg="$*"
+
+ printf '%s\n>>>>\n' "$msg"
+ cat "$filename"
+ printf "<<<<\n"
+}
+
+json_pretty() {
+ "$NFT_TEST_BASEDIR/helpers/json-pretty.sh" "$@" 2>&1 || :
+}
+
+TEST="$1"
+TESTBASE="$(basename "$TEST")"
+TESTDIR="$(dirname "$TEST")"
+
+START_TIME="$(cut -d ' ' -f1 /proc/uptime)"
+
+export TMPDIR="$NFT_TEST_TESTTMPDIR"
+
+CLEANUP_UMOUNT_VAR_RUN=n
+
+cleanup() {
+ if [ "$CLEANUP_UMOUNT_VAR_RUN" = y ] ; then
+ umount "/var/run" &>/dev/null || :
+ fi
+}
+
+trap cleanup EXIT
+
+printf '%s\n' "$TEST" > "$NFT_TEST_TESTTMPDIR/name"
+
+read tainted_before < /proc/sys/kernel/tainted
+
+if [ "$NFT_TEST_HAS_UNSHARED_MOUNT" = y ] ; then
+ # We have a private mount namespace. We will mount /var/run/ as a tmpfs.
+ #
+ # The main purpose is so that we can create /var/run/netns, which is
+ # required for `ip netns add` to work. When running as rootless, this
+ # is necessary to get such tests to pass. When running rootful, it's
+ # still useful to not touch the "real" /var/run/netns of the system.
+ #
+ # 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 tmpfs "/var/run" ; then
+ CLEANUP_UMOUNT_VAR_RUN=y
+ fi
+ mkdir -p /var/run/netns
+fi
+
+TEST_TAGS_PARSED=0
+ensure_TEST_TAGS() {
+ if [ "$TEST_TAGS_PARSED" = 0 ] ; then
+ TEST_TAGS_PARSED=1
+ TEST_TAGS=( $(sed -n '1,10 { s/^.*\<\(NFT_TEST_REQUIRES\|NFT_TEST_SKIP\)\>\s*(\s*\(NFT_TEST_SKIP_[a-zA-Z0-9_]\+\|NFT_TEST_HAVE_[a-zA-Z0-9_]\+\)\s*).*$/\1(\2)/p }' "$1" 2>/dev/null || : ) )
+ fi
+}
+
+rc_test=0
+
+if [ "$rc_test" -eq 0 ] ; then
+ for KEY in $(compgen -v | grep '^NFT_TEST_HAVE_') ; do
+ if [ "${!KEY}" != n ]; then
+ continue
+ fi
+ ensure_TEST_TAGS "$TEST"
+ if array_contains "NFT_TEST_REQUIRES($KEY)" "${TEST_TAGS[@]}" ; then
+ echo "Test skipped due to $KEY=n (test has \"NFT_TEST_REQUIRES($KEY)\" tag)" >> "$NFT_TEST_TESTTMPDIR/testout.log"
+ rc_test=77
+ break
+ fi
+ done
+fi
+
+if [ "$rc_test" -eq 0 ] ; then
+ for KEY in $(compgen -v | grep '^NFT_TEST_SKIP_') ; do
+ if [ "${!KEY}" != y ]; then
+ continue
+ fi
+ ensure_TEST_TAGS "$TEST"
+ if array_contains "NFT_TEST_SKIP($KEY)" "${TEST_TAGS[@]}" ; then
+ echo "Test skipped due to $KEY=y (test has \"NFT_TEST_SKIP($KEY)\" tag)" >> "$NFT_TEST_TESTTMPDIR/testout.log"
+ rc_test=77
+ break
+ fi
+ done
+fi
+
+if [ "$rc_test" -eq 0 ] ; then
+ CMD=( "$TEST" )
+ if [ "$NFT_TEST_VERBOSE_TEST" = y ] ; then
+ X="$(sed -n '1 s/^#!\(\/bin\/bash\>.*$\)/\1/p' "$TEST" 2>/dev/null)"
+ if [ -n "$X" ] ; then
+ # Note that kernel parses the shebang differently and does not
+ # word splitting for the arguments. We do split the arguments here
+ # which would matter if there are spaces. For our tests, there
+ # are either no arguments or only one argument without space. So
+ # this is good enough.
+ CMD=( $X -x "$TEST" )
+ fi
+ fi
+ printf "Command: $(printf '%q ' "${CMD[@]}")\n" &>> "$NFT_TEST_TESTTMPDIR/testout.log"
+ "${CMD[@]}" &>> "$NFT_TEST_TESTTMPDIR/testout.log" || rc_test=$?
+fi
+
+rc_chkdump=0
+rc=0
+$NFT list ruleset > "$NFT_TEST_TESTTMPDIR/ruleset-after" 2> "$NFT_TEST_TESTTMPDIR/chkdump" || rc=$?
+if [ "$rc" -ne 0 -o -s "$NFT_TEST_TESTTMPDIR/chkdump" ] ; then
+ show_file "$NFT_TEST_TESTTMPDIR/chkdump" "Command \`$NFT list ruleset\` failed" >> "$NFT_TEST_TESTTMPDIR/rc-failed-chkdump"
+ rc_chkdump=1
+fi
+if [ "$NFT_TEST_HAVE_json" != n ] ; then
+ rc=0
+ $NFT -j list ruleset > "$NFT_TEST_TESTTMPDIR/ruleset-after.json" 2> "$NFT_TEST_TESTTMPDIR/chkdump" || rc=$?
+
+ # Workaround known bug in stmt_print_json(), due to
+ # "chain_stmt_ops.json" being NULL. This spams stderr.
+ sed -i '/^warning: stmt ops chain have no json callback$/d' "$NFT_TEST_TESTTMPDIR/chkdump"
+
+ if [ "$rc" -ne 0 -o -s "$NFT_TEST_TESTTMPDIR/chkdump" ] ; then
+ show_file "$NFT_TEST_TESTTMPDIR/chkdump" "Command \`$NFT -j list ruleset\` failed" >> "$NFT_TEST_TESTTMPDIR/rc-failed-chkdump"
+ rc_chkdump=1
+ fi
+ # JSON output needs normalization/sanitization, otherwise it's not stable.
+ "$NFT_TEST_BASEDIR/helpers/json-sanitize-ruleset.sh" "$NFT_TEST_TESTTMPDIR/ruleset-after.json"
+ json_pretty "$NFT_TEST_TESTTMPDIR/ruleset-after.json" > "$NFT_TEST_TESTTMPDIR/ruleset-after.json-pretty"
+fi
+
+read tainted_after < /proc/sys/kernel/tainted
+
+DUMPPATH="$TESTDIR/dumps"
+DUMPFILE="$DUMPPATH/$TESTBASE.nft"
+JDUMPFILE="$DUMPPATH/$TESTBASE.json-nft"
+NODUMPFILE="$DUMPPATH/$TESTBASE.nodump"
+
+# The caller can request a re-geneating of the .nft, .nodump, .json-nft dump files
+# by setting DUMPGEN=y. In that case, only the existing files will be regenerated
+# (unless all three files are missing, in which case all of them are generated).
+#
+# By setting DUMPGEN=all, all 3 files are always regenerated.
+dump_written=n
+if [ "$rc_test" -eq 0 -a '(' "$DUMPGEN" = all -o "$DUMPGEN" = y ')' ] ; then
+ dump_written=y
+ if [ ! -d "$DUMPPATH" ] ; then
+ mkdir "$DUMPPATH"
+ fi
+ if [ "$DUMPGEN" = all ] ; then
+ gen_nodumpfile=y
+ gen_dumpfile=y
+ gen_jdumpfile=y
+ else
+ # by default, only regenerate the files that we already have on disk.
+ gen_nodumpfile=n
+ gen_dumpfile=n
+ gen_jdumpfile=n
+ test -f "$DUMPFILE" && gen_dumpfile=y
+ test -f "$JDUMPFILE" && gen_jdumpfile=y
+ test -f "$NODUMPFILE" && gen_nodumpfile=y
+ if [ "$gen_dumpfile" != y -a "$gen_jdumpfile" != y -a "$gen_nodumpfile" != y ] ; then
+ # Except, if no files exist. Them generate all files.
+ gen_dumpfile=y
+ gen_jdumpfile=y
+ gen_nodumpfile=y
+ fi
+ fi
+ if [ "$gen_nodumpfile" = y ] ; then
+ : > "$NODUMPFILE"
+ fi
+ if [ "$gen_dumpfile" = y ] ; then
+ cat "$NFT_TEST_TESTTMPDIR/ruleset-after" > "$DUMPFILE"
+ fi
+ if [ "$NFT_TEST_HAVE_json" != n -a "$gen_jdumpfile" = y ] ; then
+ cat "$NFT_TEST_TESTTMPDIR/ruleset-after.json-pretty" > "$JDUMPFILE"
+ fi
+fi
+
+rc_dump=0
+if [ "$rc_test" -ne 77 -a "$dump_written" != y ] ; then
+ if [ -f "$DUMPFILE" ] ; then
+ if ! $DIFF -u "$DUMPFILE" "$NFT_TEST_TESTTMPDIR/ruleset-after" &> "$NFT_TEST_TESTTMPDIR/ruleset-diff" ; then
+ show_file "$NFT_TEST_TESTTMPDIR/ruleset-diff" "Failed \`$DIFF -u \"$DUMPFILE\" \"$NFT_TEST_TESTTMPDIR/ruleset-after\"\`" >> "$NFT_TEST_TESTTMPDIR/rc-failed-dump"
+ rc_dump=1
+ else
+ rm -f "$NFT_TEST_TESTTMPDIR/ruleset-diff"
+ fi
+ fi
+ if [ "$NFT_TEST_HAVE_json" != n -a -f "$JDUMPFILE" ] ; then
+ if ! $DIFF -u "$JDUMPFILE" "$NFT_TEST_TESTTMPDIR/ruleset-after.json-pretty" &> "$NFT_TEST_TESTTMPDIR/ruleset-diff.json" ; then
+ show_file "$NFT_TEST_TESTTMPDIR/ruleset-diff.json" "Failed \`$DIFF -u \"$JDUMPFILE\" \"$NFT_TEST_TESTTMPDIR/ruleset-after.json-pretty\"\`" >> "$NFT_TEST_TESTTMPDIR/rc-failed-dump"
+ rc_dump=1
+ else
+ rm -f "$NFT_TEST_TESTTMPDIR/ruleset-diff.json"
+ fi
+ fi
+fi
+
+# check that a flush after the test succeeds. We anyway need a clean ruleset
+# for the `nft --check` next.
+rc=0
+$NFT flush ruleset &> "$NFT_TEST_TESTTMPDIR/chkdump" || rc=1
+if [ "$rc" = 1 -o -s "$NFT_TEST_TESTTMPDIR/chkdump" ] ; then
+ show_file "$NFT_TEST_TESTTMPDIR/chkdump" "Command \`$NFT flush ruleset\` failed" >> "$NFT_TEST_TESTTMPDIR/rc-failed-chkdump"
+ rc_chkdump=1
+fi
+# Check that `nft [-j] list ruleset | nft [-j] --check -f -` works.
+fail=n
+$NFT --check -f "$NFT_TEST_TESTTMPDIR/ruleset-after" &> "$NFT_TEST_TESTTMPDIR/chkdump" || fail=y
+test -s "$NFT_TEST_TESTTMPDIR/chkdump" && fail=y
+if [ "$fail" = y ] ; then
+ show_file "$NFT_TEST_TESTTMPDIR/chkdump" "Command \`$NFT --check -f \"$NFT_TEST_TESTTMPDIR/ruleset-after\"\` failed" >> "$NFT_TEST_TESTTMPDIR/rc-failed-chkdump"
+ rc_chkdump=1
+fi
+if [ -f "$DUMPFILE" ] && ! cmp "$DUMPFILE" "$NFT_TEST_TESTTMPDIR/ruleset-after" &>/dev/null ; then
+ # Also check the $DUMPFILE to hit possibly new code paths. This
+ # is useful to see crashes and with ASAN/valgrind.
+ $NFT --check -f "$DUMPFILE" &>/dev/null || :
+fi
+if [ "$NFT_TEST_HAVE_json" != n ] ; then
+ if [ ! -f "$JDUMPFILE" ] ; then
+ # Optimally, `nft -j list ruleset | nft -j --check -f -` never
+ # fails. However, there are known issues where this doesn't
+ # work, and we cannot assert hard against that. It's those
+ # tests that don't have a .json-nft file.
+ #
+ # This should be fixed, every test should have a .json-nft
+ # file, and this workaround removed.
+ $NFT -j --check -f "$NFT_TEST_TESTTMPDIR/ruleset-after.json" &>/dev/null || :
+ $NFT -j --check -f "$NFT_TEST_TESTTMPDIR/ruleset-after.json-pretty" &>/dev/null || :
+ else
+ fail=n
+ $NFT -j --check -f "$NFT_TEST_TESTTMPDIR/ruleset-after.json" &> "$NFT_TEST_TESTTMPDIR/chkdump" || fail=y
+ test -s "$NFT_TEST_TESTTMPDIR/chkdump" && fail=y
+ if [ "$fail" = y ] ; then
+ show_file "$NFT_TEST_TESTTMPDIR/chkdump" "Command \`$NFT -j --check -f \"$NFT_TEST_TESTTMPDIR/ruleset-after.json\"\` failed" >> "$NFT_TEST_TESTTMPDIR/rc-failed-chkdump"
+ rc_chkdump=1
+ fi
+ fail=n
+ $NFT -j --check -f "$NFT_TEST_TESTTMPDIR/ruleset-after.json-pretty" &> "$NFT_TEST_TESTTMPDIR/chkdump" || fail=y
+ test -s "$NFT_TEST_TESTTMPDIR/chkdump" && fail=y
+ if [ "$fail" = y ] ; then
+ show_file "$NFT_TEST_TESTTMPDIR/chkdump" "Command \`$NFT -j --check -f \"$NFT_TEST_TESTTMPDIR/ruleset-after.json-pretty\"\` failed" >> "$NFT_TEST_TESTTMPDIR/rc-failed-chkdump"
+ rc_chkdump=1
+ fi
+ fi
+ if [ -f "$JDUMPFILE" ] \
+ && ! cmp "$JDUMPFILE" "$NFT_TEST_TESTTMPDIR/ruleset-after.json" &>/dev/null \
+ && ! cmp "$JDUMPFILE" "$NFT_TEST_TESTTMPDIR/ruleset-after.json-pretty" &>/dev/null ; \
+ then
+ $NFT -j --check -f "$JDUMPFILE" &>/dev/null || :
+ fi
+fi
+rm -f "$NFT_TEST_TESTTMPDIR/chkdump"
+
+rc_valgrind=0
+[ -f "$NFT_TEST_TESTTMPDIR/rc-failed-valgrind" ] && rc_valgrind=1
+
+rc_tainted=0
+if [ "$tainted_before" != "$tainted_after" ] ; then
+ echo "$tainted_after" > "$NFT_TEST_TESTTMPDIR/rc-failed-tainted"
+ rc_tainted=1
+fi
+
+if [ "$rc_valgrind" -ne 0 ] ; then
+ rc_exit=122
+elif [ "$rc_tainted" -ne 0 ] ; then
+ rc_exit=123
+elif [ "$rc_test" -ge 118 -a "$rc_test" -le 124 ] ; then
+ # Special exit codes are reserved. Coerce them.
+ rc_exit=125
+elif [ "$rc_test" -ne 0 ] ; then
+ rc_exit="$rc_test"
+elif [ "$rc_dump" -ne 0 ] ; then
+ rc_exit=124
+elif [ "$rc_chkdump" -ne 0 ] ; then
+ rc_exit=121
+else
+ rc_exit=0
+fi
+
+
+# We always write the real exit code of the test ($rc_test) to one of the files
+# rc-{ok,skipped,failed}, depending on which it is.
+#
+# Note that there might be other rc-failed-{dump,tainted,valgrind} files with
+# additional errors. Note that if such files exist, the overall state will
+# always be failed too (and an "rc-failed" file exists).
+#
+# On failure, we also write the combined "$rc_exit" code from "test-wrapper.sh"
+# to "rc-failed-exit" file.
+#
+# This means, failed tests will have a "rc-failed" file, and additional
+# "rc-failed-*" files exist for further information.
+if [ "$rc_exit" -eq 0 ] ; then
+ RC_FILENAME="rc-ok"
+elif [ "$rc_exit" -eq 77 ] ; then
+ RC_FILENAME="rc-skipped"
+else
+ RC_FILENAME="rc-failed"
+ echo "$rc_exit" > "$NFT_TEST_TESTTMPDIR/rc-failed-exit"
+fi
+echo "$rc_test" > "$NFT_TEST_TESTTMPDIR/$RC_FILENAME"
+
+END_TIME="$(cut -d ' ' -f1 /proc/uptime)"
+WALL_TIME="$(awk -v start="$START_TIME" -v end="$END_TIME" "BEGIN { print(end - start) }")"
+printf "%s\n" "$WALL_TIME" "$START_TIME" "$END_TIME" > "$NFT_TEST_TESTTMPDIR/times"
+
+exit "$rc_exit"
diff --git a/tests/shell/run-tests.sh b/tests/shell/run-tests.sh
index 349ec6cb..6a9b518c 100755
--- a/tests/shell/run-tests.sh
+++ b/tests/shell/run-tests.sh
@@ -1,57 +1,617 @@
#!/bin/bash
-# Configuration
-TESTDIR="./$(dirname $0)/testcases"
-SRC_NFT="$(dirname $0)/../../src/nft"
-DIFF=$(which diff)
+unset LANGUAGE
+export LANG=C
+export LC_ALL=C
+
+GREEN=""
+YELLOW=""
+RED=""
+RESET=""
+if [ -z "$NO_COLOR" ] ; then
+ if [ -n "$CLICOLOR_FORCE" ] || [[ -t 1 ]] ; then
+ # See https://bixense.com/clicolors/ . We only check isatty() on
+ # file descriptor 1, to decide whether colorizing happens (although,
+ # we might also colorize on other places/FDs).
+ GREEN=$'\e[32m'
+ YELLOW=$'\e[33m'
+ RED=$'\e[31m'
+ RESET=$'\e[0m'
+ fi
+fi
+
+array_contains() {
+ local needle="$1"
+ local a
+ shift
+ for a; do
+ [ "$a" = "$needle" ] && return 0
+ done
+ return 1
+}
+
+array_remove_first() {
+ local _varname="$1"
+ local _needle="$2"
+ local _result=()
+ local _a
+
+ eval "local _input=( \"\${$_varname[@]}\" )"
+ for _a in "${_input[@]}" ; do
+ if [ -n "${_needle+x}" -a "$_needle" = "$_a" ] ; then
+ unset _needle
+ else
+ _result+=("$_a")
+ fi
+ done
+ eval "$_varname="'( "${_result[@]}" )'
+}
+
+colorize_keywords() {
+ local out_variable="$1"
+ local color="$2"
+ local val="$3"
+ local val2
+ shift 3
+
+ printf -v val2 '%q' "$val"
+ array_contains "$val" "$@" && val2="$color$val2$RESET"
+ printf -v "$out_variable" '%s' "$val2"
+}
+
+strtonum() {
+ local s="$1"
+ local n
+ local n2
+
+ re='^[[:space:]]*([0-9]+)[[:space:]]*$'
+ if [[ "$s" =~ $re ]] ; then
+ n="${BASH_REMATCH[1]}"
+ if [ "$(( n + 0 ))" = "$n" ] ; then
+ echo "$n"
+ return 0
+ fi
+ fi
+ re='^[[:space:]]*0x([0-9a-fA-F]+)[[:space:]]*$'
+ if [[ "$s" =~ $re ]] ; then
+ n="${BASH_REMATCH[1]}"
+ n2="$(( 16#$n + 0 ))"
+ if [ "$n2" = "$(printf '%d' "0x$n" 2>/dev/null)" ] ; then
+ echo "$n2"
+ return 0
+ fi
+ fi
+ return 1
+}
+
+_msg() {
+ local level="$1"
+ shift
+
+ if [ "$level" = E ] ; then
+ printf '%s\n' "$RED$level$RESET: $*"
+ elif [ "$level" = W ] ; then
+ printf '%s\n' "$YELLOW$level$RESET: $*"
+ else
+ printf '%s\n' "$level: $*"
+ fi
+ if [ "$level" = E ] ; then
+ exit 1
+ fi
+}
msg_error() {
- echo "E: $1 ..." >&2
- exit 1
+ _msg E "$@"
}
msg_warn() {
- echo "W: $1" >&2
+ _msg W "$@"
}
msg_info() {
- echo "I: $1"
+ _msg I "$@"
}
-if [ "$(id -u)" != "0" ] ; then
- msg_error "this requires root!"
+align_text() {
+ local _OUT_VARNAME="$1"
+ local _LEFT_OR_RIGHT="$2"
+ local _INDENT="$3"
+ shift 3
+ local _text="$*"
+ local _text_plain
+ local _text_align
+ local _text_result
+ local _i
+
+ # This function is needed, because "$text" might contain color escape
+ # sequences. A plain `printf '%12s' "$text"` will not align properly.
+
+ # strip escape sequences
+ _text_plain="${_text//$'\e['[0-9]m/}"
+ _text_plain="${_text_plain//$'\e['[0-9][0-9]m/}"
+
+ _text_align=""
+ for (( _i = "${#_text_plain}" ; "$_i" < "$_INDENT" ; _i++ )) ; do
+ _text_align="$_text_align "
+ done
+
+ if [ "$_LEFT_OR_RIGHT" = left ] ; then
+ _text_result="$(printf "%s$_text_align-" "$_text")"
+ else
+ _text_result="$(printf "$_text_align%s-" "$_text")"
+ fi
+ _text_result="${_text_result%-}"
+
+ eval "$_OUT_VARNAME=\"\$_text_result\""
+}
+
+bool_n() {
+ case "$1" in
+ n|N|no|No|NO|0|false|False|FALSE)
+ printf n
+ ;;
+ *)
+ printf y
+ ;;
+ esac
+}
+
+bool_y() {
+ case "$1" in
+ y|Y|yes|Yes|YES|1|true|True|TRUE)
+ printf y
+ ;;
+ *)
+ printf n
+ ;;
+ esac
+}
+
+usage() {
+ echo " $0 [OPTIONS] [TESTS...]"
+ echo
+ echo "OPTIONS:"
+ echo " -h|--help : Print usage."
+ echo " -L|--list-tests : List test names and quit."
+ echo " -v : Sets VERBOSE=y."
+ echo " -g : Sets DUMPGEN=y."
+ echo " -V : Sets VALGRIND=y."
+ echo " -K : Sets KMEMLEAK=y."
+ echo " -R|--without-realroot : Sets NFT_TEST_HAS_REALROOT=n."
+ echo " -U|--no-unshare : Sets NFT_TEST_UNSHARE_CMD=\"\"."
+ echo " -k|--keep-logs : Sets NFT_TEST_KEEP_LOGS=y."
+ echo " -x : Sets NFT_TEST_VERBOSE_TEST=y."
+ echo " -s|--sequential : Sets NFT_TEST_JOBS=0, which also enables global cleanups."
+ echo " Also sets NFT_TEST_SHUFFLE_TESTS=n if left unspecified."
+ echo " -Q|--quick : Sets NFT_TEST_SKIP_slow=y."
+ echo " -S|--setup-host : Modify the host to run as rootless. Otherwise, some tests will be"
+ echo " skipped. Basically, this bumps /proc/sys/net/core/{wmem_max,rmem_max}."
+ echo " Must run as root and this option must be specified alone."
+ echo " -- : Separate options from tests."
+ echo " [TESTS...] : Other options are treated as test names,"
+ echo " that is, executables that are run by the runner."
+ echo
+ echo "ENVIRONMENT VARIABLES:"
+ echo " NFT=<CMD> : Path to nft executable. Will be called as \`\$NFT [...]\` so"
+ echo " it can be a command with parameters. Note that in this mode quoting"
+ echo " does not work, so the usage is limited and the command cannot contain"
+ echo " spaces."
+ echo " NFT_REAL=<CMD> : Real nft comand. Usually this is just the same as \$NFT,"
+ echo " however, you may set NFT='valgrind nft' and NFT_REAL to the real command."
+ echo " VERBOSE=*|y : Enable verbose output."
+ echo " NFT_TEST_VERBOSE_TEST=*|y: if true, enable verbose output for tests. For bash scripts, this means"
+ echo " to pass \"-x\" to the interpreter."
+ echo " DUMPGEN=*|y|all : Regenerate dump files \".{nft,json-nft,nodump}\". \"DUMPGEN=y\" only regenerates existing"
+ echo " files, unless the test has no files (then all three files are generated, and you need to"
+ echo " choose which to keep). With \"DUMPGEN=all\" all 3 files are regenerated, regardless"
+ echo " whether they already exist."
+ echo " VALGRIND=*|y : Run \$NFT in valgrind."
+ echo " KMEMLEAK=*|y : Check for kernel memleaks."
+ echo " NFT_TEST_HAS_REALROOT=*|y : To indicate whether the test has real root permissions."
+ echo " Usually, you don't need this and it gets autodetected."
+ echo " You might want to set it, if you know better than the"
+ echo " \`id -u\` check, whether the user is root in the main namespace."
+ echo " Note that without real root, certain tests may not work,"
+ echo " e.g. due to limited /proc/sys/net/core/{wmem_max,rmem_max}."
+ echo " Checks that cannot pass in such environment should check for"
+ echo " [ \"\$NFT_TEST_HAS_REALROOT\" != y ] and skip gracefully."
+ echo " NFT_TEST_HAS_SOCKET_LIMITS=*|n : some tests will fail if /proc/sys/net/core/{wmem_max,rmem_max} is"
+ echo " too small. When running as real root, then test can override those limits. However,"
+ echo " with rootless the test would fail. Tests will check for [ "\$NFT_TEST_HAS_SOCKET_LIMITS" = y ]"
+ echo " and skip. You may set NFT_TEST_HAS_SOCKET_LIMITS=n if you ensure those limits are"
+ echo " suitable to run the test rootless. Otherwise will be autodetected."
+ echo " Set /proc/sys/net/core/{wmem_max,rmem_max} to at least 4MB to get them to pass automatically."
+ echo " NFT_TEST_UNSHARE_CMD=cmd : when set, this is the command line for an unshare"
+ echo " command, which is used to sandbox each test invocation. By"
+ echo " setting it to empty, no unsharing is done."
+ echo " By default it is unset, in which case it's autodetected as"
+ echo " \`unshare -f -p\` (for root) or as \`unshare -f -p --mount-proc -U --map-root-user -n\`"
+ echo " for non-root."
+ echo " When setting this, you may also want to set NFT_TEST_HAS_UNSHARED=,"
+ echo " NFT_TEST_HAS_REALROOT= and NFT_TEST_HAS_UNSHARED_MOUNT= accordingly."
+ echo " NFT_TEST_HAS_UNSHARED=*|y : To indicate to the test whether the test run will be unshared."
+ echo " Test may consider this."
+ echo " This is only honored when \$NFT_TEST_UNSHARE_CMD= is set. Otherwise it's detected."
+ echo " NFT_TEST_HAS_UNSHARED_MOUNT=*|y : To indicate to the test whether the test run will have a private"
+ echo " mount namespace."
+ echo " This is only honored when \$NFT_TEST_UNSHARE_CMD= is set. Otherwise it's detected."
+ echo " NFT_TEST_KEEP_LOGS=*|y: Keep the temp directory. On success, it will be deleted by default."
+ echo " NFT_TEST_JOBS=<NUM}>: number of jobs for parallel execution. Defaults to \"\$(nproc)*1.5\" for parallel run."
+ echo " Setting this to \"0\" or \"1\", means to run jobs sequentially."
+ echo " Setting this to \"0\" means also to perform global cleanups between tests (remove"
+ echo " kernel modules)."
+ echo " Parallel jobs requires unshare and are disabled with NFT_TEST_UNSHARE_CMD=\"\"."
+ echo " NFT_TEST_FAIL_ON_SKIP=*|y: if any jobs are skipped, exit with error."
+ echo " NFT_TEST_RANDOM_SEED=<SEED>: The test runner will export the environment variable NFT_TEST_RANDOM_SEED"
+ echo " set to a random number. This can be used as a stable seed for tests to randomize behavior."
+ echo " Set this to a fixed value to get reproducible behavior."
+ echo " NFT_TEST_SHUFFLE_TESTS=*|n|y: control whether to randomly shuffle the order of tests. By default, if"
+ echo " tests are specified explicitly, they are not shuffled while they are shuffled when"
+ echo " all tests are run. The shuffling is based on NFT_TEST_RANDOM_SEED."
+ echo " TMPDIR=<PATH> : select a different base directory for the result data."
+ echo
+ echo " NFT_TEST_HAVE_<FEATURE>=*|y: Some tests requires certain features or will be skipped."
+ echo " The features are autodetected, but you can force it by setting the variable."
+ echo " Supported <FEATURE>s are: ${_HAVE_OPTS[@]}."
+ echo " NFT_TEST_SKIP_<OPTION>=*|y: if set, certain tests are skipped."
+ echo " Supported <OPTION>s are: ${_SKIP_OPTS[@]}."
+}
+
+NFT_TEST_BASEDIR="$(dirname "$0")"
+
+# Export the base directory. It may be used by tests.
+export NFT_TEST_BASEDIR
+
+_HAVE_OPTS=()
+shopt -s nullglob
+F=( "$NFT_TEST_BASEDIR/features/"*.nft "$NFT_TEST_BASEDIR/features/"*.sh )
+shopt -u nullglob
+for file in "${F[@]}"; do
+ feat="${file##*/}"
+ feat="${feat%.*}"
+ re="^[a-z_0-9]+$"
+ if [[ "$feat" =~ $re ]] && ! array_contains "$feat" "${_HAVE_OPTS[@]}" && [[ "$file" != *.sh || -x "$file" ]] ; then
+ _HAVE_OPTS+=( "$feat" )
+ else
+ msg_warn "Ignore feature file \"$file\""
+ fi
+done
+_HAVE_OPTS=( $(printf '%s\n' "${_HAVE_OPTS[@]}" | sort) )
+
+for KEY in $(compgen -v | grep '^NFT_TEST_HAVE_' | sort) ; do
+ if ! array_contains "${KEY#NFT_TEST_HAVE_}" "${_HAVE_OPTS[@]}" ; then
+ unset "$KEY"
+ fi
+done
+
+_SKIP_OPTS=( slow )
+for KEY in $(compgen -v | grep '^NFT_TEST_SKIP_' | sort) ; do
+ if ! array_contains "${KEY#NFT_TEST_SKIP_}" "${_SKIP_OPTS[@]}" ; then
+ unset "$KEY"
+ fi
+done
+
+_NFT_TEST_JOBS_DEFAULT="$(nproc)"
+[ "$_NFT_TEST_JOBS_DEFAULT" -gt 0 ] 2>/dev/null || _NFT_TEST_JOBS_DEFAULT=1
+_NFT_TEST_JOBS_DEFAULT="$(( _NFT_TEST_JOBS_DEFAULT + (_NFT_TEST_JOBS_DEFAULT + 1) / 2 ))"
+
+VERBOSE="$(bool_y "$VERBOSE")"
+NFT_TEST_VERBOSE_TEST="$(bool_y "$NFT_TEST_VERBOSE_TEST")"
+if [ "$DUMPGEN" != "all" ] ; then
+ DUMPGEN="$(bool_y "$DUMPGEN")"
fi
+VALGRIND="$(bool_y "$VALGRIND")"
+KMEMLEAK="$(bool_y "$KMEMLEAK")"
+NFT_TEST_KEEP_LOGS="$(bool_y "$NFT_TEST_KEEP_LOGS")"
+NFT_TEST_HAS_REALROOT="$NFT_TEST_HAS_REALROOT"
+NFT_TEST_JOBS="${NFT_TEST_JOBS:-$_NFT_TEST_JOBS_DEFAULT}"
+NFT_TEST_FAIL_ON_SKIP="$(bool_y "$NFT_TEST_FAIL_ON_SKIP")"
+NFT_TEST_RANDOM_SEED="$NFT_TEST_RANDOM_SEED"
+NFT_TEST_SHUFFLE_TESTS="$NFT_TEST_SHUFFLE_TESTS"
+NFT_TEST_SKIP_slow="$(bool_y "$NFT_TEST_SKIP_slow")"
+DO_LIST_TESTS=
-if [ "${1}" != "run" ]; then
- if unshare -f -n true; then
- unshare -n "${0}" run $@
- exit $?
+if [ -z "$NFT_TEST_RANDOM_SEED" ] ; then
+ # Choose a random value.
+ n="$SRANDOM"
+ [ -z "$n" ] && n="$RANDOM"
+else
+ # Parse as number.
+ n="$(strtonum "$NFT_TEST_RANDOM_SEED")"
+ if [ -z "$n" ] ; then
+ # If not a number, pick a hash based on the SHA-sum of the seed.
+ n="$(printf "%d" "0x$(sha256sum <<<"NFT_TEST_RANDOM_SEED:$NFT_TEST_RANDOM_SEED" | sed -n '1 { s/^\(........\).*/\1/p }')")"
fi
- msg_warn "cannot run in own namespace, connectivity might break"
fi
-shift
+# Limit a 31 bit decimal so tests can rely on this being in a certain
+# restricted form.
+NFT_TEST_RANDOM_SEED="$(( $n % 0x80000000 ))"
+export NFT_TEST_RANDOM_SEED
-[ -z "$NFT" ] && NFT=$SRC_NFT
-${NFT} > /dev/null 2>&1
-ret=$?
-if [ ${ret} -eq 126 ] || [ ${ret} -eq 127 ]; then
- msg_error "cannot execute nft command: ${NFT}"
+TESTS=()
+
+SETUP_HOST=
+SETUP_HOST_OTHER=
+
+ARGV_ORIG=( "$@" )
+
+while [ $# -gt 0 ] ; do
+ A="$1"
+ shift
+ case "$A" in
+ -S|--setup-host)
+ ;;
+ *)
+ SETUP_HOST_OTHER=y
+ ;;
+ esac
+ case "$A" in
+ -S|--setup-host)
+ SETUP_HOST="$A"
+ ;;
+ -v)
+ VERBOSE=y
+ ;;
+ -x)
+ NFT_TEST_VERBOSE_TEST=y
+ ;;
+ -g)
+ DUMPGEN=y
+ ;;
+ -V)
+ VALGRIND=y
+ ;;
+ -K)
+ KMEMLEAK=y
+ ;;
+ -h|--help)
+ usage
+ exit 0
+ ;;
+ -k|--keep-logs)
+ NFT_TEST_KEEP_LOGS=y
+ ;;
+ -L|--list-tests)
+ DO_LIST_TESTS=y
+ ;;
+ -R|--without-realroot)
+ NFT_TEST_HAS_REALROOT=n
+ ;;
+ -U|--no-unshare)
+ NFT_TEST_UNSHARE_CMD=
+ ;;
+ -s|--sequential)
+ NFT_TEST_JOBS=0
+ if [ -z "$NFT_TEST_SHUFFLE_TESTS" ] ; then
+ NFT_TEST_SHUFFLE_TESTS=n
+ fi
+ ;;
+ -Q|--quick)
+ NFT_TEST_SKIP_slow=y
+ ;;
+ --)
+ TESTS+=( "$@" )
+ shift $#
+ ;;
+ *)
+ TESTS+=( "$A" )
+ ;;
+ esac
+done
+
+sysctl_bump() {
+ local sysctl="$1"
+ local val="$2"
+ local cur;
+
+ cur="$(cat "$sysctl" 2>/dev/null)" || :
+ if [ -n "$cur" -a "$cur" -ge "$val" ] ; then
+ echo "# Skip: echo $val > $sysctl (current value $cur)"
+ return 0
+ fi
+ echo " echo $val > $sysctl (previous value $cur)"
+ echo "$val" > "$sysctl"
+}
+
+setup_host() {
+ echo "Setting up host for running as rootless (requires root)."
+ sysctl_bump /proc/sys/net/core/rmem_max $((4000*1024)) || return $?
+ sysctl_bump /proc/sys/net/core/wmem_max $((4000*1024)) || return $?
+}
+
+if [ -n "$SETUP_HOST" ] ; then
+ if [ "$SETUP_HOST_OTHER" = y ] ; then
+ msg_error "The $SETUP_HOST option must be specified alone."
+ fi
+ setup_host
+ exit $?
+fi
+
+find_tests() {
+ find "$1" -type f -executable | sort
+}
+
+if [ "${#TESTS[@]}" -eq 0 ] ; then
+ d="$NFT_TEST_BASEDIR/testcases/"
+ d="${d#./}"
+ TESTS=( $(find_tests "$d") )
+ test "${#TESTS[@]}" -gt 0 || msg_error "Could not find tests"
+ if [ -z "$NFT_TEST_SHUFFLE_TESTS" ] ; then
+ NFT_TEST_SHUFFLE_TESTS=y
+ fi
+fi
+
+TESTSOLD=( "${TESTS[@]}" )
+TESTS=()
+for t in "${TESTSOLD[@]}" ; do
+ if [ -f "$t" -a -x "$t" ] ; then
+ TESTS+=( "$t" )
+ elif [ -d "$t" ] ; then
+ TESTS+=( $(find_tests "$t") )
+ else
+ msg_error "Unknown test \"$t\""
+ fi
+done
+
+NFT_TEST_SHUFFLE_TESTS="$(bool_y "$NFT_TEST_SHUFFLE_TESTS")"
+
+if [ "$DO_LIST_TESTS" = y ] ; then
+ printf '%s\n' "${TESTS[@]}"
+ exit 0
+fi
+
+START_TIME="$(cut -d ' ' -f1 /proc/uptime)"
+
+_TMPDIR="${TMPDIR:-/tmp}"
+
+# Export the orignal TMPDIR for the tests. "test-wrapper.sh" sets TMPDIR to
+# NFT_TEST_TESTTMPDIR, so that temporary files are placed along side the
+# test data. In some cases, we may want to know the original TMPDIR.
+export NFT_TEST_TMPDIR_ORIG="$_TMPDIR"
+
+if [ "$NFT_TEST_HAS_REALROOT" = "" ] ; then
+ # The caller didn't set NFT_TEST_HAS_REALROOT and didn't specify
+ # -R/--without-root option. Autodetect it based on `id -u`.
+ export NFT_TEST_HAS_REALROOT="$(test "$(id -u)" = "0" && echo y || echo n)"
else
- msg_info "using nft command: ${NFT}"
+ NFT_TEST_HAS_REALROOT="$(bool_y "$NFT_TEST_HAS_REALROOT")"
fi
+export NFT_TEST_HAS_REALROOT
-if [ ! -d "$TESTDIR" ] ; then
- msg_error "missing testdir $TESTDIR"
+if [ "$NFT_TEST_HAS_SOCKET_LIMITS" = "" ] ; then
+ if [ "$NFT_TEST_HAS_REALROOT" = y ] ; then
+ NFT_TEST_HAS_SOCKET_LIMITS=n
+ elif [ "$(cat /proc/sys/net/core/wmem_max 2>/dev/null)" -ge $((4000*1024)) ] 2>/dev/null && \
+ [ "$(cat /proc/sys/net/core/rmem_max 2>/dev/null)" -ge $((4000*1024)) ] 2>/dev/null ; then
+ NFT_TEST_HAS_SOCKET_LIMITS=n
+ else
+ NFT_TEST_HAS_SOCKET_LIMITS=y
+ fi
+else
+ NFT_TEST_HAS_SOCKET_LIMITS="$(bool_n "$NFT_TEST_HAS_SOCKET_LIMITS")"
fi
+export NFT_TEST_HAS_SOCKET_LIMITS
-FIND="$(which find)"
-if [ ! -x "$FIND" ] ; then
- msg_error "no find binary found"
+detect_unshare() {
+ if ! $1 true &>/dev/null ; then
+ return 1
+ fi
+ NFT_TEST_UNSHARE_CMD="$1"
+ return 0
+}
+
+if [ -n "${NFT_TEST_UNSHARE_CMD+x}" ] ; then
+ # User overrides the unshare command.
+ if ! detect_unshare "$NFT_TEST_UNSHARE_CMD" ; then
+ msg_error "Cannot unshare via NFT_TEST_UNSHARE_CMD=$(printf '%q' "$NFT_TEST_UNSHARE_CMD")"
+ fi
+ if [ -z "${NFT_TEST_HAS_UNSHARED+x}" ] ; then
+ # Autodetect NFT_TEST_HAS_UNSHARED based one whether
+ # $NFT_TEST_UNSHARE_CMD is set.
+ if [ -n "$NFT_TEST_UNSHARE_CMD" ] ; then
+ NFT_TEST_HAS_UNSHARED="y"
+ else
+ NFT_TEST_HAS_UNSHARED="n"
+ fi
+ else
+ NFT_TEST_HAS_UNSHARED="$(bool_y "$NFT_TEST_HAS_UNSHARED")"
+ fi
+ if [ -z "${NFT_TEST_HAS_UNSHARED_MOUNT+x}" ] ; then
+ NFT_TEST_HAS_UNSHARED_MOUNT=n
+ if [ "$NFT_TEST_HAS_UNSHARED" == y ] ; then
+ case "$NFT_TEST_UNSHARE_CMD" in
+ unshare*-m*|unshare*--mount-proc*)
+ NFT_TEST_HAS_UNSHARED_MOUNT=y
+ ;;
+ esac
+ fi
+ else
+ NFT_TEST_HAS_UNSHARED_MOUNT="$(bool_y "$NFT_TEST_HAS_UNSHARED_MOUNT")"
+ fi
+else
+ NFT_TEST_HAS_UNSHARED_MOUNT=n
+ if [ "$NFT_TEST_HAS_REALROOT" = y ] ; then
+ # We appear to have real root. So try to unshare
+ # without a separate USERNS. CLONE_NEWUSER will break
+ # tests that are limited by
+ # /proc/sys/net/core/{wmem_max,rmem_max}. With real
+ # root, we want to test that.
+ if detect_unshare "unshare -f -n -m" ; then
+ NFT_TEST_HAS_UNSHARED_MOUNT=y
+ else
+ detect_unshare "unshare -f -n" ||
+ detect_unshare "unshare -f -p -m --mount-proc -U --map-root-user -n" ||
+ detect_unshare "unshare -f -U --map-root-user -n"
+ fi
+ else
+ if detect_unshare "unshare -f -p -m --mount-proc -U --map-root-user -n" ; then
+ NFT_TEST_HAS_UNSHARED_MOUNT=y
+ else
+ detect_unshare "unshare -f -U --map-root-user -n"
+ fi
+ fi
+ if [ -z "$NFT_TEST_UNSHARE_CMD" ] ; then
+ msg_error "Unshare does not work. Run as root with -U/--no-unshare or set NFT_TEST_UNSHARE_CMD"
+ fi
+ NFT_TEST_HAS_UNSHARED=y
fi
+# If tests wish, they can know whether they are unshared via this variable.
+export NFT_TEST_HAS_UNSHARED
+export NFT_TEST_HAS_UNSHARED_MOUNT
-MODPROBE="$(which modprobe)"
-if [ ! -x "$MODPROBE" ] ; then
- msg_error "no modprobe binary found"
+# normalize the jobs number to be an integer.
+case "$NFT_TEST_JOBS" in
+ ''|*[!0-9]*) NFT_TEST_JOBS=_NFT_TEST_JOBS_DEFAULT ;;
+esac
+if [ -z "$NFT_TEST_UNSHARE_CMD" -a "$NFT_TEST_JOBS" -gt 1 ] ; then
+ NFT_TEST_JOBS=1
+fi
+
+[ -z "$NFT" ] && NFT="$NFT_TEST_BASEDIR/../../src/nft"
+${NFT} > /dev/null 2>&1
+ret=$?
+if [ ${ret} -eq 126 ] || [ ${ret} -eq 127 ]; then
+ msg_error "cannot execute nft command: $NFT"
+fi
+
+NFT_REAL="${NFT_REAL-$NFT}"
+
+feature_probe()
+{
+ local with_path="$NFT_TEST_BASEDIR/features/$1"
+
+ if [ -r "$with_path.nft" ] ; then
+ $NFT_TEST_UNSHARE_CMD "$NFT_REAL" --check -f "$with_path.nft" &>/dev/null
+ return $?
+ fi
+
+ if [ -x "$with_path.sh" ] ; then
+ NFT="$NFT_REAL" $NFT_TEST_UNSHARE_CMD "$with_path.sh" &>/dev/null
+ return $?
+ fi
+
+ return 1
+}
+
+for feat in "${_HAVE_OPTS[@]}" ; do
+ var="NFT_TEST_HAVE_$feat"
+ if [ -z "${!var+x}" ] ; then
+ val='y'
+ feature_probe "$feat" || val='n'
+ else
+ val="$(bool_n "${!var}")"
+ fi
+ eval "export $var=$val"
+ if [ "$NFT_TEST_HAS_UNSHARED" != y ] ; then
+ $NFT flush ruleset
+ fi
+done
+
+if [ "$NFT_TEST_JOBS" -eq 0 ] ; then
+ MODPROBE="$(which modprobe)"
+ if [ ! -x "$MODPROBE" ] ; then
+ msg_error "no modprobe binary found"
+ fi
fi
DIFF="$(which diff)"
@@ -59,23 +619,102 @@ if [ ! -x "$DIFF" ] ; then
DIFF=true
fi
-if [ "$1" == "-v" ] ; then
- VERBOSE=y
- shift
-fi
+JOBS_PIDLIST_ARR=()
+declare -A JOBS_PIDLIST
-if [ "$1" == "-g" ] ; then
- DUMPGEN=y
- shift
-fi
+_NFT_TEST_VALGRIND_VGDB_PREFIX=
+
+cleanup_on_exit() {
+ pids_search=''
+ for pid in "${JOBS_PIDLIST_ARR[@]}" ; do
+ kill -- "-$pid" &>/dev/null
+ pids_search="$pids_search\\|\\<$pid\\>"
+ done
+ if [ -n "$pids_search" ] ; then
+ pids_search="${pids_search:2}"
+ for i in {1..100}; do
+ ps xh -o pgrp | grep -q "$pids_search" || break
+ sleep 0.01
+ done
+ fi
+ if [ "$NFT_TEST_KEEP_LOGS" != y -a -n "$NFT_TEST_TMPDIR" ] ; then
+ rm -rf "$NFT_TEST_TMPDIR"
+ fi
+ if [ -n "$_NFT_TEST_VALGRIND_VGDB_PREFIX" ] ; then
+ rm -rf "$_NFT_TEST_VALGRIND_VGDB_PREFIX"* &>/dev/null
+ fi
+}
+
+trap 'exit 130' SIGINT
+trap 'exit 143' SIGTERM
+trap 'rc=$?; cleanup_on_exit; exit $rc' EXIT
+
+TIMESTAMP=$(date '+%Y%m%d-%H%M%S.%3N')
+NFT_TEST_TMPDIR="$(mktemp --tmpdir="$_TMPDIR" -d "nft-test.$TIMESTAMP$NFT_TEST_TMPDIR_TAG.XXXXXX")" ||
+ msg_error "Failure to create temp directory in \"$_TMPDIR\""
+chmod 755 "$NFT_TEST_TMPDIR"
+
+exec &> >(tee "$NFT_TEST_TMPDIR/test.log")
-for arg in "$@"; do
- SINGLE+=" $arg"
- VERBOSE=y
+msg_info "conf: NFT=$(printf '%q' "$NFT")"
+msg_info "conf: NFT_REAL=$(printf '%q' "$NFT_REAL")"
+msg_info "conf: VERBOSE=$(printf '%q' "$VERBOSE")"
+msg_info "conf: NFT_TEST_VERBOSE_TEST=$(printf '%q' "$NFT_TEST_VERBOSE_TEST")"
+msg_info "conf: DUMPGEN=$(printf '%q' "$DUMPGEN")"
+msg_info "conf: VALGRIND=$(printf '%q' "$VALGRIND")"
+msg_info "conf: KMEMLEAK=$(printf '%q' "$KMEMLEAK")"
+msg_info "conf: NFT_TEST_HAS_REALROOT=$(printf '%q' "$NFT_TEST_HAS_REALROOT")"
+colorize_keywords value "$YELLOW" "$NFT_TEST_HAS_SOCKET_LIMITS" y
+msg_info "conf: NFT_TEST_HAS_SOCKET_LIMITS=$value"
+msg_info "conf: NFT_TEST_UNSHARE_CMD=$(printf '%q' "$NFT_TEST_UNSHARE_CMD")"
+msg_info "conf: NFT_TEST_HAS_UNSHARED=$(printf '%q' "$NFT_TEST_HAS_UNSHARED")"
+msg_info "conf: NFT_TEST_HAS_UNSHARED_MOUNT=$(printf '%q' "$NFT_TEST_HAS_UNSHARED_MOUNT")"
+msg_info "conf: NFT_TEST_KEEP_LOGS=$(printf '%q' "$NFT_TEST_KEEP_LOGS")"
+msg_info "conf: NFT_TEST_JOBS=$NFT_TEST_JOBS"
+msg_info "conf: NFT_TEST_FAIL_ON_SKIP=$NFT_TEST_FAIL_ON_SKIP"
+msg_info "conf: NFT_TEST_RANDOM_SEED=$NFT_TEST_RANDOM_SEED"
+msg_info "conf: NFT_TEST_SHUFFLE_TESTS=$NFT_TEST_SHUFFLE_TESTS"
+msg_info "conf: TMPDIR=$(printf '%q' "$_TMPDIR")"
+echo
+for KEY in $(compgen -v | grep '^NFT_TEST_SKIP_' | sort) ; do
+ colorize_keywords value "$YELLOW" "${!KEY}" y
+ msg_info "conf: $KEY=$value"
+ export "$KEY"
done
+for KEY in $(compgen -v | grep '^NFT_TEST_HAVE_' | sort) ; do
+ colorize_keywords value "$YELLOW" "${!KEY}" n
+ msg_info "conf: $KEY=$value"
+ export "$KEY"
+done
+
+NFT_TEST_LATEST="$_TMPDIR/nft-test.latest.$USER"
+
+ln -snf "$NFT_TEST_TMPDIR" "$NFT_TEST_LATEST"
+
+# export the tmp directory for tests. They may use it, but create distinct
+# files! On success, it will be deleted on EXIT. See also "--keep-logs"
+export NFT_TEST_TMPDIR
+
+echo
+msg_info "info: NFT_TEST_BASEDIR=$(printf '%q' "$NFT_TEST_BASEDIR")"
+msg_info "info: NFT_TEST_TMPDIR=$(printf '%q' "$NFT_TEST_TMPDIR")"
+
+if [ "$VALGRIND" == "y" ]; then
+ NFT="$NFT_TEST_BASEDIR/helpers/nft-valgrind-wrapper.sh"
+ msg_info "info: NFT=$(printf '%q' "$NFT")"
+ _NFT_TEST_VALGRIND_VGDB_PREFIX="$NFT_TEST_TMPDIR_ORIG/vgdb-pipe-nft-test-$TIMESTAMP.$$.$RANDOM"
+ export _NFT_TEST_VALGRIND_VGDB_PREFIX
+fi
kernel_cleanup() {
- $NFT flush ruleset
+ if [ "$NFT_TEST_JOBS" -ne 0 ] ; then
+ # When we run jobs in parallel (even with only one "parallel"
+ # job via `NFT_TEST_JOBS=1`), we skip such global cleanups.
+ return
+ fi
+ if [ "$NFT_TEST_HAS_UNSHARED" != y ] ; then
+ $NFT flush ruleset
+ fi
$MODPROBE -raq \
nft_reject_ipv4 nft_reject_bridge nft_reject_ipv6 nft_reject \
nft_redir_ipv4 nft_redir_ipv6 nft_redir \
@@ -87,6 +726,7 @@ kernel_cleanup() {
nft_fib nft_fib_ipv4 nft_fib_ipv6 nft_fib_inet \
nft_hash nft_ct nft_compat nft_rt nft_objref \
nft_set_hash nft_set_rbtree nft_set_bitmap \
+ nft_synproxy nft_connlimit \
nft_chain_nat \
nft_chain_route_ipv4 nft_chain_route_ipv6 \
nft_dup_netdev nft_fwd_netdev \
@@ -97,67 +737,262 @@ kernel_cleanup() {
nft_xfrm
}
-find_tests() {
- if [ ! -z "$SINGLE" ] ; then
- echo $SINGLE
+echo ""
+ok=0
+skipped=0
+failed=0
+
+kmem_runs=0
+kmemleak_found=0
+
+check_kmemleak_force()
+{
+ test -f /sys/kernel/debug/kmemleak || return 0
+
+ echo scan > /sys/kernel/debug/kmemleak
+
+ lines=$(grep "unreferenced object" /sys/kernel/debug/kmemleak | wc -l)
+ if [ $lines -ne $kmemleak_found ];then
+ msg_warn "[FAILED] kmemleak detected $lines memory leaks"
+ kmemleak_found=$lines
+ fi
+
+ if [ $lines -ne 0 ];then
+ return 1
+ fi
+
+ return 0
+}
+
+check_kmemleak()
+{
+ test -f /sys/kernel/debug/kmemleak || return
+
+ if [ "$KMEMLEAK" == "y" ] ; then
+ check_kmemleak_force
return
fi
- ${FIND} ${TESTDIR} -type f -executable | sort
+
+ kmem_runs=$((kmem_runs + 1))
+ if [ $((kmem_runs % 30)) -eq 0 ]; then
+ # scan slows tests down quite a bit, hence
+ # do this only for every 30th test file by
+ # default.
+ check_kmemleak_force
+ fi
}
-echo ""
-ok=0
-failed=0
-for testfile in $(find_tests)
-do
- kernel_cleanup
+read kernel_tainted < /proc/sys/kernel/tainted
+if [ "$kernel_tainted" -ne 0 ] ; then
+ msg_warn "kernel is tainted"
+ echo
+fi
+
+print_test_header() {
+ local msglevel="$1"
+ local testfile="$2"
+ local testidx_completed="$3"
+ local status="$4"
+ local text
+ local s_idx
+
+ s_idx="${#TESTS[@]}"
+ align_text text right "${#s_idx}" "$testidx_completed"
+ s_idx="$text/${#TESTS[@]}"
+
+ align_text text left 12 "[$status]"
+ _msg "$msglevel" "$text $s_idx $testfile"
+}
+
+print_test_result() {
+ local NFT_TEST_TESTTMPDIR="$1"
+ local testfile="$2"
+ local rc_got="$3"
- msg_info "[EXECUTING] $testfile"
- test_output=$(NFT="$NFT" DIFF=$DIFF ${testfile} 2>&1)
- rc_got=$?
- echo -en "\033[1A\033[K" # clean the [EXECUTING] foobar line
+ local result_msg_level="I"
+ local result_msg_files=( "$NFT_TEST_TESTTMPDIR/testout.log" "$NFT_TEST_TESTTMPDIR/ruleset-diff" )
+ local result_msg_status
if [ "$rc_got" -eq 0 ] ; then
- # check nft dump only for positive tests
- dumppath="$(dirname ${testfile})/dumps"
- dumpfile="${dumppath}/$(basename ${testfile}).nft"
- rc_spec=0
- if [ "$rc_got" -eq 0 ] && [ -f ${dumpfile} ]; then
- test_output=$(${DIFF} -u ${dumpfile} <($NFT list ruleset) 2>&1)
- rc_spec=$?
+ ((ok++))
+ result_msg_status="${GREEN}OK$RESET"
+ elif [ "$rc_got" -eq 77 ] ; then
+ ((skipped++))
+ result_msg_status="${YELLOW}SKIPPED$RESET"
+ else
+ ((failed++))
+ result_msg_level="W"
+ if [ "$rc_got" -eq 121 ] ; then
+ result_msg_status="CHK DUMP"
+ elif [ "$rc_got" -eq 122 ] ; then
+ result_msg_status="VALGRIND"
+ elif [ "$rc_got" -eq 123 ] ; then
+ result_msg_status="TAINTED"
+ elif [ "$rc_got" -eq 124 ] ; then
+ result_msg_status="DUMP FAIL"
+ else
+ result_msg_status="FAILED"
fi
+ result_msg_status="$RED$result_msg_status$RESET"
+ result_msg_files=( "$NFT_TEST_TESTTMPDIR/testout.log" )
+ fi
- if [ "$rc_spec" -eq 0 ]; then
- msg_info "[OK] $testfile"
- [ "$VERBOSE" == "y" ] && [ ! -z "$test_output" ] && echo "$test_output"
- ((ok++))
+ print_test_header "$result_msg_level" "$testfile" "$((ok + skipped + failed))" "$result_msg_status"
- if [ "$DUMPGEN" == "y" ] && [ "$rc_got" == 0 ] && [ ! -f "${dumpfile}" ]; then
- mkdir -p "${dumppath}"
- $NFT list ruleset > "${dumpfile}"
- fi
- else
- ((failed++))
- if [ "$VERBOSE" == "y" ] ; then
- msg_warn "[DUMP FAIL] $testfile: dump diff detected"
- [ ! -z "$test_output" ] && echo "$test_output"
- else
- msg_warn "[DUMP FAIL] $testfile"
+ if [ "$VERBOSE" = "y" ] ; then
+ local f
+
+ for f in "${result_msg_files[@]}"; do
+ if [ -s "$f" ] ; then
+ cat "$f"
fi
+ done
+
+ if [ "$rc_got" -ne 0 ] ; then
+ msg_info "check \"$NFT_TEST_TESTTMPDIR\""
fi
- else
- ((failed++))
- if [ "$VERBOSE" == "y" ] ; then
- msg_warn "[FAILED] $testfile: got $rc_got"
- [ ! -z "$test_output" ] && echo "$test_output"
+ fi
+}
+
+declare -A JOBS_TEMPDIR
+
+job_start() {
+ local testfile="$1"
+ local testidx="$2"
+
+ if [ "$NFT_TEST_JOBS" -le 1 ] && [[ -t 1 ]]; then
+ print_test_header I "$testfile" "$testidx" "EXECUTING"
+ fi
+
+ NFT_TEST_TESTTMPDIR="${JOBS_TEMPDIR["$testfile"]}" \
+ NFT="$NFT" \
+ NFT_REAL="$NFT_REAL" \
+ DIFF="$DIFF" \
+ DUMPGEN="$DUMPGEN" \
+ NFT_TEST_VERBOSE_TEST="$NFT_TEST_VERBOSE_TEST" \
+ $NFT_TEST_UNSHARE_CMD "$NFT_TEST_BASEDIR/helpers/test-wrapper.sh" "$testfile"
+ local rc_got=$?
+
+ if [ "$NFT_TEST_JOBS" -le 1 ] && [[ -t 1 ]]; then
+ echo -en "\033[1A\033[K" # clean the [EXECUTING] foobar line
+ fi
+
+ return "$rc_got"
+}
+
+# `wait -p` is only supported since bash 5.1
+WAIT_SUPPORTS_P=1
+[ "${BASH_VERSINFO[0]}" -le 4 -o \( "${BASH_VERSINFO[0]}" -eq 5 -a "${BASH_VERSINFO[1]}" -eq 0 \) ] && WAIT_SUPPORTS_P=0
+
+job_wait()
+{
+ local num_jobs="$1"
+ local JOBCOMPLETED
+ local rc_got
+
+ while [ "${#JOBS_PIDLIST_ARR[@]}" -gt 0 -a "${#JOBS_PIDLIST_ARR[@]}" -ge "$num_jobs" ] ; do
+ if [ "$WAIT_SUPPORTS_P" = 1 ] ; then
+ wait -n -p JOBCOMPLETED
+ rc_got="$?"
+ array_remove_first JOBS_PIDLIST_ARR "$JOBCOMPLETED"
else
- msg_warn "[FAILED] $testfile"
+ # Without `wait -p` support, we need to explicitly wait
+ # for a PID. That reduces parallelism.
+ JOBCOMPLETED="${JOBS_PIDLIST_ARR[0]}"
+ JOBS_PIDLIST_ARR=( "${JOBS_PIDLIST_ARR[@]:1}" )
+ wait -n "$JOBCOMPLETED"
+ rc_got="$?"
fi
- fi
+
+ local testfile2="${JOBS_PIDLIST[$JOBCOMPLETED]}"
+ unset JOBS_PIDLIST[$JOBCOMPLETED]
+ print_test_result "${JOBS_TEMPDIR["$testfile2"]}" "$testfile2" "$rc_got"
+ check_kmemleak
+ done
+}
+
+if [ "$NFT_TEST_SHUFFLE_TESTS" = y ] ; then
+ TESTS=( $(printf '%s\n' "${TESTS[@]}" | shuf --random-source=<("$NFT_TEST_BASEDIR/helpers/random-source.sh" "nft-test-shuffle-tests" "$NFT_TEST_RANDOM_SEED") ) )
+fi
+
+TESTIDX=0
+for testfile in "${TESTS[@]}" ; do
+ job_wait "$NFT_TEST_JOBS"
+
+ kernel_cleanup
+
+ ((TESTIDX++))
+
+ NFT_TEST_TESTTMPDIR="$NFT_TEST_TMPDIR/test-${testfile//\//-}.$TESTIDX"
+ mkdir "$NFT_TEST_TESTTMPDIR"
+ chmod 755 "$NFT_TEST_TESTTMPDIR"
+ JOBS_TEMPDIR["$testfile"]="$NFT_TEST_TESTTMPDIR"
+
+ [[ -o monitor ]] && set_old_state='set -m' || set_old_state='set +m'
+ set -m
+ ( job_start "$testfile" "$TESTIDX" ) &
+ pid=$!
+ eval "$set_old_state"
+ JOBS_PIDLIST[$pid]="$testfile"
+ JOBS_PIDLIST_ARR+=( "$pid" )
done
+job_wait 0
+
echo ""
-msg_info "results: [OK] $ok [FAILED] $failed [TOTAL] $((ok+failed))"
+
+# kmemleak may report suspected leaks
+# that get free'd after all, so always do
+# a check after all test cases
+# have completed and reset the counter
+# so another warning gets emitted.
+kmemleak_found=0
+check_kmemleak_force
+
+failed_total="$failed"
+if [ "$NFT_TEST_FAIL_ON_SKIP" = y ] ; then
+ failed_total="$((failed_total + skipped))"
+fi
+
+if [ "$failed_total" -gt 0 ] ; then
+ RR="$RED"
+elif [ "$skipped" -gt 0 ] ; then
+ RR="$YELLOW"
+else
+ RR="$GREEN"
+fi
+msg_info "${RR}results$RESET: [OK] $GREEN$ok$RESET [SKIPPED] $YELLOW$skipped$RESET [FAILED] $RED$failed$RESET [TOTAL] $((ok+skipped+failed))"
kernel_cleanup
-exit $failed
+
+# ( \
+# for d in /tmp/nft-test.latest.*/test-*/ ; do \
+# printf '%10.2f %s\n' \
+# "$(sed '1!d' "$d/times")" \
+# "$(cat "$d/name")" ; \
+# done \
+# | sort -n \
+# | awk '{print $0; s+=$1} END{printf("%10.2f\n", s)}' ; \
+# printf '%10.2f wall time\n' "$(sed '1!d' /tmp/nft-test.latest.*/times)" \
+# )
+END_TIME="$(cut -d ' ' -f1 /proc/uptime)"
+WALL_TIME="$(awk -v start="$START_TIME" -v end="$END_TIME" "BEGIN { print(end - start) }")"
+printf "%s\n" "$WALL_TIME" "$START_TIME" "$END_TIME" > "$NFT_TEST_TMPDIR/times"
+
+if [ "$failed_total" -gt 0 -o "$NFT_TEST_KEEP_LOGS" = y ] ; then
+ msg_info "check the temp directory \"$NFT_TEST_TMPDIR\" (\"$NFT_TEST_LATEST\")"
+ msg_info " ls -lad \"$NFT_TEST_LATEST\"/*/*"
+ msg_info " grep -R ^ \"$NFT_TEST_LATEST\"/"
+ NFT_TEST_TMPDIR=
+fi
+
+if [ "$failed" -gt 0 ] ; then
+ exit 1
+elif [ "$NFT_TEST_FAIL_ON_SKIP" = y -a "$skipped" -gt 0 ] ; then
+ msg_info "some tests were skipped. Fail due to NFT_TEST_FAIL_ON_SKIP=y"
+ exit 1
+elif [ "$ok" -eq 0 -a "$skipped" -gt 0 ] ; then
+ exit 77
+else
+ exit 0
+fi
diff --git a/tests/shell/testcases/bitwise/0040mark_binop_0 b/tests/shell/testcases/bitwise/0040mark_binop_0
new file mode 100755
index 00000000..4ecc9d3d
--- /dev/null
+++ b/tests/shell/testcases/bitwise/0040mark_binop_0
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_bitshift)
+
+set -e
+
+RULESET="
+ add table t
+ add chain t c { type filter hook output priority filter; }
+ add rule t c oif lo ct mark set (meta mark | 0x10) << 8
+"
+
+$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/chains/0040mark_shift_1 b/tests/shell/testcases/bitwise/0040mark_binop_1
index b609f5ef..bd9e028d 100755
--- a/tests/shell/testcases/chains/0040mark_shift_1
+++ b/tests/shell/testcases/bitwise/0040mark_binop_1
@@ -1,10 +1,12 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_bitshift)
+
set -e
RULESET="
add table t
- add chain t c { type filter hook input priority mangle; }
+ add chain t c { type filter hook input priority filter; }
add rule t c iif lo ct mark & 0xff 0x10 meta mark set ct mark >> 8
"
diff --git a/tests/shell/testcases/bitwise/0040mark_binop_2 b/tests/shell/testcases/bitwise/0040mark_binop_2
new file mode 100755
index 00000000..5e66a27a
--- /dev/null
+++ b/tests/shell/testcases/bitwise/0040mark_binop_2
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_bitshift)
+
+set -e
+
+RULESET="
+ add table t
+ add chain t c { type filter hook output priority filter; }
+ add rule t c ct mark set ip dscp lshift 2 or 0x10
+"
+
+$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/bitwise/0040mark_binop_3 b/tests/shell/testcases/bitwise/0040mark_binop_3
new file mode 100755
index 00000000..21dda670
--- /dev/null
+++ b/tests/shell/testcases/bitwise/0040mark_binop_3
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_bitshift)
+
+set -e
+
+RULESET="
+ add table t
+ add chain t c { type filter hook input priority filter; }
+ add rule t c meta mark set ip dscp lshift 2 or 0x10
+"
+
+$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/bitwise/0040mark_binop_4 b/tests/shell/testcases/bitwise/0040mark_binop_4
new file mode 100755
index 00000000..e5c8a42a
--- /dev/null
+++ b/tests/shell/testcases/bitwise/0040mark_binop_4
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_bitshift)
+
+set -e
+
+RULESET="
+ add table t
+ add chain t c { type filter hook output priority filter; }
+ add rule t c ct mark set ip dscp lshift 26 or 0x10
+"
+
+$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/bitwise/0040mark_binop_5 b/tests/shell/testcases/bitwise/0040mark_binop_5
new file mode 100755
index 00000000..184fbed0
--- /dev/null
+++ b/tests/shell/testcases/bitwise/0040mark_binop_5
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_bitshift)
+
+set -e
+
+RULESET="
+ add table t
+ add chain t c { type filter hook input priority filter; }
+ add rule t c meta mark set ip dscp lshift 26 or 0x10
+"
+
+$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/bitwise/0040mark_binop_6 b/tests/shell/testcases/bitwise/0040mark_binop_6
new file mode 100755
index 00000000..129dd5c0
--- /dev/null
+++ b/tests/shell/testcases/bitwise/0040mark_binop_6
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_bitshift)
+
+set -e
+
+RULESET="
+ add table ip6 t
+ add chain ip6 t c { type filter hook output priority filter; }
+ add rule ip6 t c ct mark set ip6 dscp lshift 2 or 0x10
+"
+
+$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/bitwise/0040mark_binop_7 b/tests/shell/testcases/bitwise/0040mark_binop_7
new file mode 100755
index 00000000..791a7943
--- /dev/null
+++ b/tests/shell/testcases/bitwise/0040mark_binop_7
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_bitshift)
+
+set -e
+
+RULESET="
+ add table ip6 t
+ add chain ip6 t c { type filter hook input priority filter; }
+ add rule ip6 t c meta mark set ip6 dscp lshift 2 or 0x10
+"
+
+$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/bitwise/0040mark_binop_8 b/tests/shell/testcases/bitwise/0040mark_binop_8
new file mode 100755
index 00000000..5e7bd28d
--- /dev/null
+++ b/tests/shell/testcases/bitwise/0040mark_binop_8
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_bitshift)
+
+set -e
+
+RULESET="
+ add table ip6 t
+ add chain ip6 t c { type filter hook output priority filter; }
+ add rule ip6 t c ct mark set ip6 dscp lshift 26 or 0x10
+"
+
+$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/bitwise/0040mark_binop_9 b/tests/shell/testcases/bitwise/0040mark_binop_9
new file mode 100755
index 00000000..a7b60fb8
--- /dev/null
+++ b/tests/shell/testcases/bitwise/0040mark_binop_9
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_bitshift)
+
+set -e
+
+RULESET="
+ add table ip6 t
+ add chain ip6 t c { type filter hook input priority filter; }
+ add rule ip6 t c meta mark set ip6 dscp lshift 26 or 0x10
+"
+
+$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/bitwise/dumps/0040mark_binop_0.json-nft b/tests/shell/testcases/bitwise/dumps/0040mark_binop_0.json-nft
new file mode 100644
index 00000000..8973de85
--- /dev/null
+++ b/tests/shell/testcases/bitwise/dumps/0040mark_binop_0.json-nft
@@ -0,0 +1,75 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oif"
+ }
+ },
+ "right": "lo"
+ }
+ },
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "<<": [
+ {
+ "|": [
+ {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ 16
+ ]
+ },
+ 8
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0040mark_shift_0.nft b/tests/shell/testcases/bitwise/dumps/0040mark_binop_0.nft
index 52d59d2c..fc0a600a 100644
--- a/tests/shell/testcases/chains/dumps/0040mark_shift_0.nft
+++ b/tests/shell/testcases/bitwise/dumps/0040mark_binop_0.nft
@@ -1,6 +1,6 @@
table ip t {
chain c {
- type filter hook output priority mangle; policy accept;
+ type filter hook output priority filter; policy accept;
oif "lo" ct mark set (meta mark | 0x00000010) << 8
}
}
diff --git a/tests/shell/testcases/bitwise/dumps/0040mark_binop_1.json-nft b/tests/shell/testcases/bitwise/dumps/0040mark_binop_1.json-nft
new file mode 100644
index 00000000..ed8e1a0d
--- /dev/null
+++ b/tests/shell/testcases/bitwise/dumps/0040mark_binop_1.json-nft
@@ -0,0 +1,86 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iif"
+ }
+ },
+ "right": "lo"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "&": [
+ {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ 255
+ ]
+ },
+ "right": 16
+ }
+ },
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ ">>": [
+ {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ 8
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0040mark_shift_1.nft b/tests/shell/testcases/bitwise/dumps/0040mark_binop_1.nft
index 56ec8dc7..dbaacefb 100644
--- a/tests/shell/testcases/chains/dumps/0040mark_shift_1.nft
+++ b/tests/shell/testcases/bitwise/dumps/0040mark_binop_1.nft
@@ -1,6 +1,6 @@
table ip t {
chain c {
- type filter hook input priority mangle; policy accept;
+ type filter hook input priority filter; policy accept;
iif "lo" ct mark & 0x000000ff == 0x00000010 meta mark set ct mark >> 8
}
}
diff --git a/tests/shell/testcases/bitwise/dumps/0040mark_binop_2.json-nft b/tests/shell/testcases/bitwise/dumps/0040mark_binop_2.json-nft
new file mode 100644
index 00000000..3cd9a831
--- /dev/null
+++ b/tests/shell/testcases/bitwise/dumps/0040mark_binop_2.json-nft
@@ -0,0 +1,65 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "<<": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "dscp"
+ }
+ },
+ 2
+ ]
+ },
+ 16
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/bitwise/dumps/0040mark_binop_2.nft b/tests/shell/testcases/bitwise/dumps/0040mark_binop_2.nft
new file mode 100644
index 00000000..2b9be36e
--- /dev/null
+++ b/tests/shell/testcases/bitwise/dumps/0040mark_binop_2.nft
@@ -0,0 +1,6 @@
+table ip t {
+ chain c {
+ type filter hook output priority filter; policy accept;
+ ct mark set ip dscp << 2 | 0x10
+ }
+}
diff --git a/tests/shell/testcases/bitwise/dumps/0040mark_binop_3.json-nft b/tests/shell/testcases/bitwise/dumps/0040mark_binop_3.json-nft
new file mode 100644
index 00000000..00c5b78a
--- /dev/null
+++ b/tests/shell/testcases/bitwise/dumps/0040mark_binop_3.json-nft
@@ -0,0 +1,65 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "<<": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "dscp"
+ }
+ },
+ 2
+ ]
+ },
+ 16
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/bitwise/dumps/0040mark_binop_3.nft b/tests/shell/testcases/bitwise/dumps/0040mark_binop_3.nft
new file mode 100644
index 00000000..8206fec0
--- /dev/null
+++ b/tests/shell/testcases/bitwise/dumps/0040mark_binop_3.nft
@@ -0,0 +1,6 @@
+table ip t {
+ chain c {
+ type filter hook input priority filter; policy accept;
+ meta mark set ip dscp << 2 | 0x10
+ }
+}
diff --git a/tests/shell/testcases/bitwise/dumps/0040mark_binop_4.json-nft b/tests/shell/testcases/bitwise/dumps/0040mark_binop_4.json-nft
new file mode 100644
index 00000000..3aa81605
--- /dev/null
+++ b/tests/shell/testcases/bitwise/dumps/0040mark_binop_4.json-nft
@@ -0,0 +1,65 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "<<": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "dscp"
+ }
+ },
+ 26
+ ]
+ },
+ 16
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/bitwise/dumps/0040mark_binop_4.nft b/tests/shell/testcases/bitwise/dumps/0040mark_binop_4.nft
new file mode 100644
index 00000000..91d9f566
--- /dev/null
+++ b/tests/shell/testcases/bitwise/dumps/0040mark_binop_4.nft
@@ -0,0 +1,6 @@
+table ip t {
+ chain c {
+ type filter hook output priority filter; policy accept;
+ ct mark set ip dscp << 26 | 0x10
+ }
+}
diff --git a/tests/shell/testcases/bitwise/dumps/0040mark_binop_5.json-nft b/tests/shell/testcases/bitwise/dumps/0040mark_binop_5.json-nft
new file mode 100644
index 00000000..a3214973
--- /dev/null
+++ b/tests/shell/testcases/bitwise/dumps/0040mark_binop_5.json-nft
@@ -0,0 +1,65 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "<<": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "dscp"
+ }
+ },
+ 26
+ ]
+ },
+ 16
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/bitwise/dumps/0040mark_binop_5.nft b/tests/shell/testcases/bitwise/dumps/0040mark_binop_5.nft
new file mode 100644
index 00000000..f2b51eb8
--- /dev/null
+++ b/tests/shell/testcases/bitwise/dumps/0040mark_binop_5.nft
@@ -0,0 +1,6 @@
+table ip t {
+ chain c {
+ type filter hook input priority filter; policy accept;
+ meta mark set ip dscp << 26 | 0x10
+ }
+}
diff --git a/tests/shell/testcases/bitwise/dumps/0040mark_binop_6.json-nft b/tests/shell/testcases/bitwise/dumps/0040mark_binop_6.json-nft
new file mode 100644
index 00000000..2de0323d
--- /dev/null
+++ b/tests/shell/testcases/bitwise/dumps/0040mark_binop_6.json-nft
@@ -0,0 +1,65 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "t",
+ "name": "c",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "<<": [
+ {
+ "payload": {
+ "protocol": "ip6",
+ "field": "dscp"
+ }
+ },
+ 2
+ ]
+ },
+ 16
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/bitwise/dumps/0040mark_binop_6.nft b/tests/shell/testcases/bitwise/dumps/0040mark_binop_6.nft
new file mode 100644
index 00000000..cf7be90c
--- /dev/null
+++ b/tests/shell/testcases/bitwise/dumps/0040mark_binop_6.nft
@@ -0,0 +1,6 @@
+table ip6 t {
+ chain c {
+ type filter hook output priority filter; policy accept;
+ ct mark set ip6 dscp << 2 | 0x10
+ }
+}
diff --git a/tests/shell/testcases/bitwise/dumps/0040mark_binop_7.json-nft b/tests/shell/testcases/bitwise/dumps/0040mark_binop_7.json-nft
new file mode 100644
index 00000000..72aee701
--- /dev/null
+++ b/tests/shell/testcases/bitwise/dumps/0040mark_binop_7.json-nft
@@ -0,0 +1,65 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "t",
+ "name": "c",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "<<": [
+ {
+ "payload": {
+ "protocol": "ip6",
+ "field": "dscp"
+ }
+ },
+ 2
+ ]
+ },
+ 16
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/bitwise/dumps/0040mark_binop_7.nft b/tests/shell/testcases/bitwise/dumps/0040mark_binop_7.nft
new file mode 100644
index 00000000..a9663e62
--- /dev/null
+++ b/tests/shell/testcases/bitwise/dumps/0040mark_binop_7.nft
@@ -0,0 +1,6 @@
+table ip6 t {
+ chain c {
+ type filter hook input priority filter; policy accept;
+ meta mark set ip6 dscp << 2 | 0x10
+ }
+}
diff --git a/tests/shell/testcases/bitwise/dumps/0040mark_binop_8.json-nft b/tests/shell/testcases/bitwise/dumps/0040mark_binop_8.json-nft
new file mode 100644
index 00000000..1cf84be5
--- /dev/null
+++ b/tests/shell/testcases/bitwise/dumps/0040mark_binop_8.json-nft
@@ -0,0 +1,65 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "t",
+ "name": "c",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "<<": [
+ {
+ "payload": {
+ "protocol": "ip6",
+ "field": "dscp"
+ }
+ },
+ 26
+ ]
+ },
+ 16
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/bitwise/dumps/0040mark_binop_8.nft b/tests/shell/testcases/bitwise/dumps/0040mark_binop_8.nft
new file mode 100644
index 00000000..04b866ad
--- /dev/null
+++ b/tests/shell/testcases/bitwise/dumps/0040mark_binop_8.nft
@@ -0,0 +1,6 @@
+table ip6 t {
+ chain c {
+ type filter hook output priority filter; policy accept;
+ ct mark set ip6 dscp << 26 | 0x10
+ }
+}
diff --git a/tests/shell/testcases/bitwise/dumps/0040mark_binop_9.json-nft b/tests/shell/testcases/bitwise/dumps/0040mark_binop_9.json-nft
new file mode 100644
index 00000000..6f4494b1
--- /dev/null
+++ b/tests/shell/testcases/bitwise/dumps/0040mark_binop_9.json-nft
@@ -0,0 +1,65 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "t",
+ "name": "c",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "|": [
+ {
+ "<<": [
+ {
+ "payload": {
+ "protocol": "ip6",
+ "field": "dscp"
+ }
+ },
+ 26
+ ]
+ },
+ 16
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/bitwise/dumps/0040mark_binop_9.nft b/tests/shell/testcases/bitwise/dumps/0040mark_binop_9.nft
new file mode 100644
index 00000000..d4745ea4
--- /dev/null
+++ b/tests/shell/testcases/bitwise/dumps/0040mark_binop_9.nft
@@ -0,0 +1,6 @@
+table ip6 t {
+ chain c {
+ type filter hook input priority filter; policy accept;
+ meta mark set ip6 dscp << 26 | 0x10
+ }
+}
diff --git a/tests/shell/testcases/bogons/assert_failures b/tests/shell/testcases/bogons/assert_failures
new file mode 100755
index 00000000..79099427
--- /dev/null
+++ b/tests/shell/testcases/bogons/assert_failures
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+dir=$(dirname $0)/nft-f/
+
+for f in $dir/*; do
+ $NFT --check -f "$f"
+
+ if [ $? -ne 1 ]; then
+ echo "Bogus input file $f did not cause expected error code" 1>&2
+ exit 111
+ fi
+done
diff --git a/tests/shell/testcases/bogons/dumps/assert_failures.json-nft b/tests/shell/testcases/bogons/dumps/assert_failures.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/bogons/dumps/assert_failures.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/bogons/dumps/assert_failures.nft b/tests/shell/testcases/bogons/dumps/assert_failures.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/bogons/dumps/assert_failures.nft
diff --git a/tests/shell/testcases/bogons/nft-f/add_to_a_set_crash b/tests/shell/testcases/bogons/nft-f/add_to_a_set_crash
new file mode 100644
index 00000000..80a01b45
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/add_to_a_set_crash
@@ -0,0 +1,11 @@
+table t {
+ set candidates_ipv4 {
+ type ipv4_addr . inet_service
+ size 65535
+ flags dynamic,timeout
+ }
+
+ chain input {
+ tcp dport 10003 ip saddr . tcp dport @candidates_ipv4 add @candidates_ipv4 { ip saddr . 10 :0004 timeout 1s }
+ }
+}
diff --git a/tests/shell/testcases/bogons/nft-f/binop_with_different_basetype_assert b/tests/shell/testcases/bogons/nft-f/binop_with_different_basetype_assert
new file mode 100644
index 00000000..e8436008
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/binop_with_different_basetype_assert
@@ -0,0 +1,5 @@
+table ip t {
+ chain c {
+ oifname set ip9dscp << 26 | 0x10
+ }
+}
diff --git a/tests/shell/testcases/bogons/nft-f/bitwise_masklen_assert b/tests/shell/testcases/bogons/nft-f/bitwise_masklen_assert
new file mode 100644
index 00000000..0e75e6f1
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/bitwise_masklen_assert
@@ -0,0 +1,5 @@
+table inet t {
+ chain c {
+ udp length . @th,160,138 vmap { 47-63 . 0xe37313536313033&131303735353203 : accept }
+ }
+}
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 00000000..01640528
--- /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
+ }
+}
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 00000000..3a4b981b
--- /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/ct_helper_yystate_underflow b/tests/shell/testcases/bogons/nft-f/ct_helper_yystate_underflow
new file mode 100644
index 00000000..18eb25eb
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/ct_helper_yystate_underflow
@@ -0,0 +1,14 @@
+table inet filter {
+ ct helper sip-5060u {
+ type "sip" protocol udp
+ l3proto ip
+ }5060t {
+ type "sip" protocol tcp
+ l3pownerip
+ }
+
+ chain input {
+ type filtol/dev/stdinok input priority f)lser; policy accept;
+ ct helper set ip protocol . th dport map { udp . 1-20000 : "si60u", tcp . 10000-20000 : "sip-5060t" }
+ }
+}
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 00000000..014525a3
--- /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, }
+ }
+}
diff --git a/tests/shell/testcases/bogons/nft-f/ct_timeout_memleak_objfree b/tests/shell/testcases/bogons/nft-f/ct_timeout_memleak_objfree
new file mode 100644
index 00000000..28b1a211
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/ct_timeout_memleak_objfree
@@ -0,0 +1,5 @@
+table ip filter {
+ ct timeout cttime {
+ protocol tcp
+ l3proto ip
+ policy = { close : 12s }
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 00000000..f1e58b55
--- /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
diff --git a/tests/shell/testcases/bogons/nft-f/double-free-on-binop-dtype_assert b/tests/shell/testcases/bogons/nft-f/double-free-on-binop-dtype_assert
new file mode 100644
index 00000000..b7a9a1cc
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/double-free-on-binop-dtype_assert
@@ -0,0 +1,6 @@
+table inet t {
+ chain c {
+ udp length . @th,160,118 vmap { 47-63 . 0xe3731353631303331313037353532/3 : accept }
+ jump noexist # only here so this fails to load after patch.
+ }
+}
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 00000000..efaff9e5
--- /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/dynamic-stack-buffer-overflow_gen_prefix b/tests/shell/testcases/bogons/nft-f/dynamic-stack-buffer-overflow_gen_prefix
new file mode 100644
index 00000000..23c2dc31
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/dynamic-stack-buffer-overflow_gen_prefix
@@ -0,0 +1,5 @@
+table ip test {
+ chain test {
+ tcp dport set ip daddr map { 192.168.0.1 : 0x000/0001 }
+ }
+}
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 00000000..43d72c4d
--- /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
+ }
+}
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 00000000..e307e7cc
--- /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
diff --git a/tests/shell/testcases/bogons/nft-f/huge_binop_expr_chain_crash b/tests/shell/testcases/bogons/nft-f/huge_binop_expr_chain_crash
new file mode 100644
index 00000000..8d1da726
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/huge_binop_expr_chain_crash
@@ -0,0 +1,5 @@
+table t {
+ chain c {
+ meta oifname^a^b^c^d^e^f^g^h^i^j^k^l^m^n^o^p^q^r^s^t^u^v^w^x^y^z^A^B^C^D^E^F^G^H^I^J^K^L^M^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^0^1^2^3^4^5^6^7^8^9 bar
+ }
+}
diff --git a/tests/shell/testcases/bogons/nft-f/huge_chain_name_assert b/tests/shell/testcases/bogons/nft-f/huge_chain_name_assert
new file mode 100644
index 00000000..161f867d
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/huge_chain_name_assert
@@ -0,0 +1,5 @@
+table inet x {
+ chain c {
+ udp length vmap { 1 : goto rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr }
+ }
+}
diff --git a/tests/shell/testcases/bogons/nft-f/huge_chain_name_define_assert b/tests/shell/testcases/bogons/nft-f/huge_chain_name_define_assert
new file mode 100644
index 00000000..3c2c0d3e
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/huge_chain_name_define_assert
@@ -0,0 +1,7 @@
+define huge = rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr
+
+table t {
+ chain d {
+ jump $huge
+ }
+}
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 00000000..41f8061a
--- /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;
+ }
+}
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 00000000..7599f850
--- /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
+ }
+}
diff --git a/tests/shell/testcases/bogons/nft-f/icmp_reject_type_uint8_assert b/tests/shell/testcases/bogons/nft-f/icmp_reject_type_uint8_assert
new file mode 100644
index 00000000..1fc85b29
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/icmp_reject_type_uint8_assert
@@ -0,0 +1 @@
+rule t c reject with icmp 512
diff --git a/tests/shell/testcases/bogons/nft-f/include-device b/tests/shell/testcases/bogons/nft-f/include-device
new file mode 100644
index 00000000..1eb79773
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/include-device
@@ -0,0 +1 @@
+include "/dev/null"
diff --git a/tests/shell/testcases/bogons/nft-f/invalid_mapping_expr_binop_assert b/tests/shell/testcases/bogons/nft-f/invalid_mapping_expr_binop_assert
new file mode 100644
index 00000000..7205ff4f
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/invalid_mapping_expr_binop_assert
@@ -0,0 +1 @@
+xy mame ip saddr map h& p p
diff --git a/tests/shell/testcases/bogons/nft-f/invalid_range_expr_type_binop b/tests/shell/testcases/bogons/nft-f/invalid_range_expr_type_binop
new file mode 100644
index 00000000..514d6ffe
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/invalid_range_expr_type_binop
@@ -0,0 +1,12 @@
+table ip x {
+ map z {
+ type ipv4_addr : ipv4_addr
+ elements = { 1&.141.0.1 - 192.168.0.2}
+ }
+
+ map z {
+ type ipv4_addr : ipv4_addr
+ flags interval
+ elements = { 10.141.0.0, * : 192.168.0.4 }
+ }
+}
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 00000000..78f16b23
--- /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/mapping_with_invalid_datatype_crash b/tests/shell/testcases/bogons/nft-f/mapping_with_invalid_datatype_crash
new file mode 100644
index 00000000..9f7084c8
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/mapping_with_invalid_datatype_crash
@@ -0,0 +1 @@
+bla to tcp dport map { 80 : 1.1.1.1 . 8001, 81 : 2.2.2.2 . 9001 } bla
diff --git a/tests/shell/testcases/bogons/nft-f/memleak_on_hookspec_error b/tests/shell/testcases/bogons/nft-f/memleak_on_hookspec_error
new file mode 100644
index 00000000..6f52658f
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/memleak_on_hookspec_error
@@ -0,0 +1,21 @@
+table ip filter {
+ ct expectation ctexpect {
+ protocol tcp
+ size 12
+ l3proto ip
+ } . inet_proto : mark
+ flags interval,timeout
+ }
+
+ chain output {
+ type gilter hook output priori
+
+ chain c {
+ cttable inet filter {
+ map test {
+ type mark . inet_service . inet_proto : mark
+ flags interval,timeout
+ }
+
+ chain output {
+ type gilter hook output priority filuer; policy \ No newline at end of file
diff --git a/tests/shell/testcases/bogons/nft-f/memleak_on_meta_set_errpath b/tests/shell/testcases/bogons/nft-f/memleak_on_meta_set_errpath
new file mode 100644
index 00000000..917e8bf8
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/memleak_on_meta_set_errpath
@@ -0,0 +1,5 @@
+table filter {
+ chain y {
+ meta seccark set ct secmark
+ }
+}
diff --git a/tests/shell/testcases/bogons/nft-f/nat_prefix_map_with_set_element_assert b/tests/shell/testcases/bogons/nft-f/nat_prefix_map_with_set_element_assert
new file mode 100644
index 00000000..18c7edd1
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/nat_prefix_map_with_set_element_assert
@@ -0,0 +1,7 @@
+table ip x {
+ chain y {
+ type nat hook postrouting priority srcnat; policy accept;
+ snat ip prefix to ip saddr map { 10.141.11.0/24 : 192.168.2.0/24, 10.141.12.1 }
+ }
+}
+
diff --git a/tests/shell/testcases/bogons/nft-f/nat_stmt_with_set_instead_of_map b/tests/shell/testcases/bogons/nft-f/nat_stmt_with_set_instead_of_map
new file mode 100644
index 00000000..b1302278
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/nat_stmt_with_set_instead_of_map
@@ -0,0 +1,10 @@
+table inet x {
+ set y {
+ type ipv4_addr
+ elements = { 2.2.2.2, 3.3.3.3 }
+ }
+
+ chain y {
+ snat ip to ip saddr map @y
+ }
+}
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 00000000..547b937f
--- /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
+ }
+}
diff --git a/tests/shell/testcases/bogons/nft-f/no_integer_basetype_crash b/tests/shell/testcases/bogons/nft-f/no_integer_basetype_crash
new file mode 100644
index 00000000..16d3e41f
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/no_integer_basetype_crash
@@ -0,0 +1 @@
+cPoR et ip dscp << 2>0 ,xl rt ipsec c0tt in tabl rt ipsec cl
diff --git a/tests/shell/testcases/bogons/nft-f/objmap_to_prefix_assert b/tests/shell/testcases/bogons/nft-f/objmap_to_prefix_assert
new file mode 100644
index 00000000..d880a377
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/objmap_to_prefix_assert
@@ -0,0 +1,6 @@
+table t {
+ chain y {
+ type filter hook input priority filter; policy accept;
+ synproxy name ip saddr map { 192.168.1.0/24 : "x*" }
+ }
+}
diff --git a/tests/shell/testcases/bogons/nft-f/payload_expr_pctx_update_assert b/tests/shell/testcases/bogons/nft-f/payload_expr_pctx_update_assert
new file mode 100644
index 00000000..64bd596a
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/payload_expr_pctx_update_assert
@@ -0,0 +1 @@
+x x comp nexthdr comp
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 00000000..c1358df4
--- /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
diff --git a/tests/shell/testcases/bogons/nft-f/payload_expr_with_0_length_assert b/tests/shell/testcases/bogons/nft-f/payload_expr_with_0_length_assert
new file mode 100644
index 00000000..f85a04e7
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/payload_expr_with_0_length_assert
@@ -0,0 +1 @@
+add rule t c @th,0,0 0
diff --git a/tests/shell/testcases/bogons/nft-f/scope_underflow_assert b/tests/shell/testcases/bogons/nft-f/scope_underflow_assert
new file mode 100644
index 00000000..aee1dcbf
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/scope_underflow_assert
@@ -0,0 +1,6 @@
+table t {
+ chain c {
+ jump{
+ jump {
+ jump
+
diff --git a/tests/shell/testcases/bogons/nft-f/set_definition_with_no_key_assert b/tests/shell/testcases/bogons/nft-f/set_definition_with_no_key_assert
new file mode 100644
index 00000000..59ef1ab3
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/set_definition_with_no_key_assert
@@ -0,0 +1,12 @@
+table inet testifsets {
+ map map_wild { elements = { "abcdex*",
+ "othername",
+ "ppp0" }
+ }
+ map map_wild {
+ type ifname : verdict
+ flags interval
+ elements = { "abcdez*" : jump do_nothing,
+ "eth0" : jump do_nothing }
+ }
+}
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 00000000..f194afbf
--- /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 }
+ }
+}
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 00000000..8b0d2744
--- /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 00000000..66bd6bf8
--- /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
+ }
+}
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 00000000..ea7186bf
--- /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 }
+}
+}
diff --git a/tests/shell/testcases/bogons/nft-f/tcp_option_without_template b/tests/shell/testcases/bogons/nft-f/tcp_option_without_template
new file mode 100644
index 00000000..fd732fd3
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/tcp_option_without_template
@@ -0,0 +1 @@
+add rule f i tcp option nop length . @ih,32,3 1
diff --git a/tests/shell/testcases/bogons/nft-f/tproxy_ranges b/tests/shell/testcases/bogons/nft-f/tproxy_ranges
new file mode 100644
index 00000000..1230860e
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/tproxy_ranges
@@ -0,0 +1,8 @@
+define range = 42-80
+
+table t {
+ chain c {
+ tcp dport 42 tproxy to 192.168.0.1:$range
+ tcp dport 42 tproxy to 192.168.0.0/16
+ }
+}
diff --git a/tests/shell/testcases/bogons/nft-f/unhandled_key_type_13_assert b/tests/shell/testcases/bogons/nft-f/unhandled_key_type_13_assert
new file mode 100644
index 00000000..35eecf60
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/unhandled_key_type_13_assert
@@ -0,0 +1,5 @@
+table ip x {
+ chain y {
+ ip protocol . th dport { tcp / 22, udp . 67 }
+ }
+}
diff --git a/tests/shell/testcases/bogons/nft-f/unhandled_key_type_13_assert_map b/tests/shell/testcases/bogons/nft-f/unhandled_key_type_13_assert_map
new file mode 100644
index 00000000..3da16ce1
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/unhandled_key_type_13_assert_map
@@ -0,0 +1,5 @@
+table ip x {
+ chain y {
+ meta mark set ip protocol . th dport map { tcp / 22 : 1234, udp . 67 : 1234 }
+ }
+}
diff --git a/tests/shell/testcases/bogons/nft-f/unhandled_key_type_13_assert_vmap b/tests/shell/testcases/bogons/nft-f/unhandled_key_type_13_assert_vmap
new file mode 100644
index 00000000..f4dc273f
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/unhandled_key_type_13_assert_vmap
@@ -0,0 +1,5 @@
+table ip x {
+ chain y {
+ ip protocol . th dport vmap { tcp / 22 : accept, udp . 67 : drop }
+ }
+}
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 00000000..e6206736
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/unknown_expr_type_range_assert
@@ -0,0 +1,7 @@
+table ip x {
+ chain k {
+ meta mark set 0x001-3434
+ ct mark set 0x001-3434
+ tcp dport set 1-3
+ }
+}
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 00000000..bb9632b0
--- /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
diff --git a/tests/shell/testcases/bogons/nft-f/zero_length_devicename2_assert b/tests/shell/testcases/bogons/nft-f/zero_length_devicename2_assert
new file mode 100644
index 00000000..fe416f85
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/zero_length_devicename2_assert
@@ -0,0 +1,5 @@
+table netdev x {
+ chain Main_Ingress1 {
+ type filter hook ingress device "" priority -1
+ }
+}
diff --git a/tests/shell/testcases/bogons/nft-f/zero_length_devicename_assert b/tests/shell/testcases/bogons/nft-f/zero_length_devicename_assert
new file mode 100644
index 00000000..84f33073
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/zero_length_devicename_assert
@@ -0,0 +1,5 @@
+table ip x {
+ chain Main_Ingress1 {
+ type filter hook ingress device""lo" priority -1
+ }
+}
diff --git a/tests/shell/testcases/bogons/nft-f/zero_length_devicename_flowtable_assert b/tests/shell/testcases/bogons/nft-f/zero_length_devicename_flowtable_assert
new file mode 100644
index 00000000..2c3e6c3f
--- /dev/null
+++ b/tests/shell/testcases/bogons/nft-f/zero_length_devicename_flowtable_assert
@@ -0,0 +1,5 @@
+table t {
+ flowtable f {
+ devices = { """"lo }
+ }
+}
diff --git a/tests/shell/testcases/cache/0008_delete_by_handle_0 b/tests/shell/testcases/cache/0008_delete_by_handle_0
index 529d6b85..0db4c693 100755
--- a/tests/shell/testcases/cache/0008_delete_by_handle_0
+++ b/tests/shell/testcases/cache/0008_delete_by_handle_0
@@ -16,7 +16,7 @@ $NFT add set t s { type ipv4_addr\; }
HANDLE=`$NFT -a list ruleset | grep "set.*handle" | cut -d' ' -f6`
$NFT delete set t handle $HANDLE
-$NFT add flowtable t f { hook ingress priority 0\; }
+$NFT add flowtable t f { hook ingress priority 0\; devices = { lo } \; }
HANDLE=`$NFT -a list ruleset | grep "flowtable.*handle" | cut -d' ' -f6`
$NFT delete flowtable t handle $HANDLE
diff --git a/tests/shell/testcases/cache/0010_implicit_chain_0 b/tests/shell/testcases/cache/0010_implicit_chain_0
new file mode 100755
index 00000000..834dc6e4
--- /dev/null
+++ b/tests/shell/testcases/cache/0010_implicit_chain_0
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_chain_binding)
+
+set -e
+
+EXPECTED="table ip f {
+ chain c {
+ jump {
+ accept
+ }
+ }
+}"
+
+$NFT 'table ip f { chain c { jump { accept; }; }; }'
+GET="$($NFT list chain ip f c)"
+
+if [ "$EXPECTED" != "$GET" ] ; then
+ $DIFF -u <(echo "$EXPECTED") <(echo "$GET")
+ exit 1
+fi
diff --git a/tests/shell/testcases/cache/0011_index_0 b/tests/shell/testcases/cache/0011_index_0
new file mode 100755
index 00000000..c9eb8683
--- /dev/null
+++ b/tests/shell/testcases/cache/0011_index_0
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+set -e
+
+RULESET="flush ruleset
+add table inet t
+add chain inet t c { type filter hook input priority 0 ; }
+add rule inet t c tcp dport 1234 accept
+add rule inet t c accept
+insert rule inet t c index 1 udp dport 4321 accept"
+
+$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/cache/dumps/0001_cache_handling_0.json-nft b/tests/shell/testcases/cache/dumps/0001_cache_handling_0.json-nft
new file mode 100644
index 00000000..7a2eacdd
--- /dev/null
+++ b/tests/shell/testcases/cache/dumps/0001_cache_handling_0.json-nft
@@ -0,0 +1,142 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "test",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "test",
+ "table": "test",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "elem": [
+ "1.1.1.1",
+ "3.3.3.3"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "right": {
+ "set": [
+ "2.2.2.2",
+ "4.4.4.4"
+ ]
+ }
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "@test"
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "right": {
+ "set": [
+ "2.2.2.2",
+ "4.4.4.4"
+ ]
+ }
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/cache/dumps/0002_interval_0.json-nft b/tests/shell/testcases/cache/dumps/0002_interval_0.json-nft
new file mode 100644
index 00000000..fa15d658
--- /dev/null
+++ b/tests/shell/testcases/cache/dumps/0002_interval_0.json-nft
@@ -0,0 +1,38 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "s",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "prefix": {
+ "addr": "192.168.0.0",
+ "len": 24
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/cache/dumps/0003_cache_update_0.json-nft b/tests/shell/testcases/cache/dumps/0003_cache_update_0.json-nft
new file mode 100644
index 00000000..e09a694c
--- /dev/null
+++ b/tests/shell/testcases/cache/dumps/0003_cache_update_0.json-nft
@@ -0,0 +1,137 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t2",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t2",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t3",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t4",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t4",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t4",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "right": "icmp"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t4",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t4",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "right": "igmp"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t4",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "drop": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/cache/dumps/0003_cache_update_0.nft b/tests/shell/testcases/cache/dumps/0003_cache_update_0.nft
new file mode 100644
index 00000000..43898d33
--- /dev/null
+++ b/tests/shell/testcases/cache/dumps/0003_cache_update_0.nft
@@ -0,0 +1,18 @@
+table ip t {
+ chain c {
+ }
+}
+table ip t2 {
+ chain c {
+ }
+}
+table ip t3 {
+}
+table ip t4 {
+ chain c {
+ meta l4proto icmp accept
+ drop
+ meta l4proto igmp accept
+ drop
+ }
+}
diff --git a/tests/shell/testcases/cache/dumps/0004_cache_update_0.json-nft b/tests/shell/testcases/cache/dumps/0004_cache_update_0.json-nft
new file mode 100644
index 00000000..d1864f00
--- /dev/null
+++ b/tests/shell/testcases/cache/dumps/0004_cache_update_0.json-nft
@@ -0,0 +1,42 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "testfilter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "testfilter",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "testfilter",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/cache/dumps/0004_cache_update_0.nft b/tests/shell/testcases/cache/dumps/0004_cache_update_0.nft
new file mode 100644
index 00000000..4f5761bc
--- /dev/null
+++ b/tests/shell/testcases/cache/dumps/0004_cache_update_0.nft
@@ -0,0 +1,5 @@
+table inet testfilter {
+ chain test {
+ counter packets 0 bytes 0
+ }
+}
diff --git a/tests/shell/testcases/cache/dumps/0005_cache_chain_flush.json-nft b/tests/shell/testcases/cache/dumps/0005_cache_chain_flush.json-nft
new file mode 100644
index 00000000..1c47d3ef
--- /dev/null
+++ b/tests/shell/testcases/cache/dumps/0005_cache_chain_flush.json-nft
@@ -0,0 +1,77 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "z",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "mapping",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "inet_service",
+ "size": 65535,
+ "flags": [
+ "timeout",
+ "dynamic"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "map": {
+ "op": "update",
+ "elem": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "sport"
+ }
+ },
+ "map": "@mapping"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/cache/dumps/0005_cache_chain_flush.nft b/tests/shell/testcases/cache/dumps/0005_cache_chain_flush.nft
new file mode 100644
index 00000000..8ab55a2c
--- /dev/null
+++ b/tests/shell/testcases/cache/dumps/0005_cache_chain_flush.nft
@@ -0,0 +1,14 @@
+table ip x {
+ map mapping {
+ type ipv4_addr : inet_service
+ size 65535
+ flags dynamic,timeout
+ }
+
+ chain y {
+ update @mapping { ip saddr : tcp sport }
+ }
+
+ chain z {
+ }
+}
diff --git a/tests/shell/testcases/cache/dumps/0006_cache_table_flush.json-nft b/tests/shell/testcases/cache/dumps/0006_cache_table_flush.json-nft
new file mode 100644
index 00000000..1c47d3ef
--- /dev/null
+++ b/tests/shell/testcases/cache/dumps/0006_cache_table_flush.json-nft
@@ -0,0 +1,77 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "z",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "mapping",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "inet_service",
+ "size": 65535,
+ "flags": [
+ "timeout",
+ "dynamic"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "map": {
+ "op": "update",
+ "elem": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "sport"
+ }
+ },
+ "map": "@mapping"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/cache/dumps/0006_cache_table_flush.nft b/tests/shell/testcases/cache/dumps/0006_cache_table_flush.nft
new file mode 100644
index 00000000..8ab55a2c
--- /dev/null
+++ b/tests/shell/testcases/cache/dumps/0006_cache_table_flush.nft
@@ -0,0 +1,14 @@
+table ip x {
+ map mapping {
+ type ipv4_addr : inet_service
+ size 65535
+ flags dynamic,timeout
+ }
+
+ chain y {
+ update @mapping { ip saddr : tcp sport }
+ }
+
+ chain z {
+ }
+}
diff --git a/tests/shell/testcases/cache/dumps/0007_echo_cache_init_0.json-nft b/tests/shell/testcases/cache/dumps/0007_echo_cache_init_0.json-nft
new file mode 100644
index 00000000..0968d8a4
--- /dev/null
+++ b/tests/shell/testcases/cache/dumps/0007_echo_cache_init_0.json-nft
@@ -0,0 +1,68 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "comment": "first",
+ "expr": [
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "comment": "second",
+ "expr": [
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "comment": "third",
+ "expr": [
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/cache/dumps/0008_delete_by_handle_0.json-nft b/tests/shell/testcases/cache/dumps/0008_delete_by_handle_0.json-nft
new file mode 100644
index 00000000..e0e56fec
--- /dev/null
+++ b/tests/shell/testcases/cache/dumps/0008_delete_by_handle_0.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/cache/dumps/0008_delete_by_handle_0.nft b/tests/shell/testcases/cache/dumps/0008_delete_by_handle_0.nft
new file mode 100644
index 00000000..985768ba
--- /dev/null
+++ b/tests/shell/testcases/cache/dumps/0008_delete_by_handle_0.nft
@@ -0,0 +1,2 @@
+table ip t {
+}
diff --git a/tests/shell/testcases/cache/dumps/0009_delete_by_handle_incorrect_0.json-nft b/tests/shell/testcases/cache/dumps/0009_delete_by_handle_incorrect_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/cache/dumps/0009_delete_by_handle_incorrect_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/cache/dumps/0009_delete_by_handle_incorrect_0.nft b/tests/shell/testcases/cache/dumps/0009_delete_by_handle_incorrect_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/cache/dumps/0009_delete_by_handle_incorrect_0.nft
diff --git a/tests/shell/testcases/cache/dumps/0010_implicit_chain_0.nft b/tests/shell/testcases/cache/dumps/0010_implicit_chain_0.nft
new file mode 100644
index 00000000..aba92c0e
--- /dev/null
+++ b/tests/shell/testcases/cache/dumps/0010_implicit_chain_0.nft
@@ -0,0 +1,7 @@
+table ip f {
+ chain c {
+ jump {
+ accept
+ }
+ }
+}
diff --git a/tests/shell/testcases/cache/dumps/0011_index_0.json-nft b/tests/shell/testcases/cache/dumps/0011_index_0.json-nft
new file mode 100644
index 00000000..46b2909f
--- /dev/null
+++ b/tests/shell/testcases/cache/dumps/0011_index_0.json-nft
@@ -0,0 +1,93 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "t",
+ "name": "c",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 1234
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 4321
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/cache/dumps/0011_index_0.nft b/tests/shell/testcases/cache/dumps/0011_index_0.nft
new file mode 100644
index 00000000..7e855eb1
--- /dev/null
+++ b/tests/shell/testcases/cache/dumps/0011_index_0.nft
@@ -0,0 +1,8 @@
+table inet t {
+ chain c {
+ type filter hook input priority filter; policy accept;
+ tcp dport 1234 accept
+ udp dport 4321 accept
+ accept
+ }
+}
diff --git a/tests/shell/testcases/chains/0014rename_0 b/tests/shell/testcases/chains/0014rename_0
index bebe48d6..bd84e957 100755
--- a/tests/shell/testcases/chains/0014rename_0
+++ b/tests/shell/testcases/chains/0014rename_0
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
$NFT add table t || exit 1
$NFT add chain t c1 || exit 1
diff --git a/tests/shell/testcases/chains/0021prio_0 b/tests/shell/testcases/chains/0021prio_0
index e7612974..ceda1558 100755
--- a/tests/shell/testcases/chains/0021prio_0
+++ b/tests/shell/testcases/chains/0021prio_0
@@ -69,6 +69,7 @@ done
family=netdev
echo "add table $family x"
gen_chains $family ingress filter lo
+[ "$NFT_TEST_HAVE_netdev_egress" != n ] && gen_chains $family egress filter lo
family=bridge
echo "add table $family x"
@@ -82,3 +83,8 @@ gen_chains $family postrouting srcnat
) >$tmpfile
$NFT -f $tmpfile
+
+if [ "$NFT_TEST_HAVE_netdev_egress" = n ]; then
+ echo "Ran a modified version of the test due to NFT_TEST_HAVE_netdev_egress=n"
+ exit 77
+fi
diff --git a/tests/shell/testcases/chains/0023prio_inet_srcnat_1 b/tests/shell/testcases/chains/0023prio_inet_srcnat_1
index d2b1fa43..e4a668e1 100755
--- a/tests/shell/testcases/chains/0023prio_inet_srcnat_1
+++ b/tests/shell/testcases/chains/0023prio_inet_srcnat_1
@@ -2,7 +2,7 @@
for family in ip ip6 inet
do
- for hook in prerouting input forward output
+ for hook in prerouting forward output
do
$NFT add table $family x
$NFT add chain $family x y "{ type filter hook $hook priority srcnat; }" &> /dev/null
diff --git a/tests/shell/testcases/chains/0024prio_inet_dstnat_1 b/tests/shell/testcases/chains/0024prio_inet_dstnat_1
index d112f2c9..f1b802a0 100755
--- a/tests/shell/testcases/chains/0024prio_inet_dstnat_1
+++ b/tests/shell/testcases/chains/0024prio_inet_dstnat_1
@@ -2,7 +2,7 @@
for family in ip ip6 inet
do
- for hook in input forward output postrouting
+ for hook in input forward postrouting
do
$NFT add table $family x
$NFT add chain $family x y "{ type filter hook $hook priority dstnat; }" &> /dev/null
diff --git a/tests/shell/testcases/chains/0026prio_netdev_1 b/tests/shell/testcases/chains/0026prio_netdev_1
index aa902e9b..b6fa3db5 100755
--- a/tests/shell/testcases/chains/0026prio_netdev_1
+++ b/tests/shell/testcases/chains/0026prio_netdev_1
@@ -1,7 +1,8 @@
#!/bin/bash
family=netdev
- hook=ingress
+ for hook in ingress egress
+ do
for prioname in raw mangle dstnat security srcnat
do
$NFT add table $family x || exit 1
@@ -12,4 +13,5 @@ family=netdev
exit 1
fi
done
+ done
exit 0
diff --git a/tests/shell/testcases/chains/0040mark_shift_0 b/tests/shell/testcases/chains/0040mark_shift_0
deleted file mode 100755
index 55447f0b..00000000
--- a/tests/shell/testcases/chains/0040mark_shift_0
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-
-set -e
-
-RULESET="
- add table t
- add chain t c { type filter hook output priority mangle; }
- add rule t c oif lo ct mark set (meta mark | 0x10) << 8
-"
-
-$NFT --debug=eval -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/chains/0041chain_binding_0 b/tests/shell/testcases/chains/0041chain_binding_0
index 59bdbe9f..141a4b6d 100755
--- a/tests/shell/testcases/chains/0041chain_binding_0
+++ b/tests/shell/testcases/chains/0041chain_binding_0
@@ -1,5 +1,16 @@
#!/bin/bash
+# no table x, caused segfault in earlier nft releases
+$NFT insert rule inet x y handle 107 'goto { log prefix "MOO! "; }'
+if [ $? -ne 1 ]; then
+ exit 1
+fi
+
+if [ $NFT_TEST_HAVE_chain_binding = "n" ] ; then
+ echo "Test partially skipped due to NFT_TEST_HAVE_chain_binding=n"
+ exit 77
+fi
+
set -e
EXPECTED="table inet x {
diff --git a/tests/shell/testcases/chains/0042chain_variable_0 b/tests/shell/testcases/chains/0042chain_variable_0
index 58535f76..c5de495e 100755
--- a/tests/shell/testcases/chains/0042chain_variable_0
+++ b/tests/shell/testcases/chains/0042chain_variable_0
@@ -1,8 +1,11 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_netdev_chain_multidevice)
+
set -e
-ip link add name dummy0 type dummy
+ip link add name d23456789012345 type dummy
+
EXPECTED="define if_main = \"lo\"
@@ -14,24 +17,55 @@ 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" = 1
+cat <<EOF | $DIFF -u <($NFT list ruleset) -
+table netdev filter1 {
+ chain Main_Ingress1 {
+ type filter hook ingress device "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
-EXPECTED="define if_main = { lo, dummy0 }
+
+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, d23456789012345 }
+define lan_interfaces = { lo }
table netdev filter3 {
chain Main_Ingress3 {
type filter hook ingress devices = \$if_main priority -500; policy accept;
}
+ chain Main_Egress3 {
+ type filter hook egress devices = \$lan_interfaces priority -500; policy accept;
+ }
}"
$NFT -f - <<< $EXPECTED
-
diff --git a/tests/shell/testcases/chains/0043chain_ingress_0 b/tests/shell/testcases/chains/0043chain_ingress_0
index bff46468..a6973b99 100755
--- a/tests/shell/testcases/chains/0043chain_ingress_0
+++ b/tests/shell/testcases/chains/0043chain_ingress_0
@@ -1,7 +1,8 @@
#!/bin/bash
-set -e
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_inet_ingress)
+set -e
RULESET="table inet filter {
chain ingress {
type filter hook ingress device \"lo\" priority filter; policy accept;
@@ -14,11 +15,5 @@ RULESET="table inet filter {
}
}"
-# Test auto-removal of chain hook on netns removal
-unshare -n bash -c "ip link add br0 type bridge; \
- $NFT add table netdev test; \
- $NFT add chain netdev test ingress { type filter hook ingress device \"br0\" priority 0\; policy drop\; } ; \
-" || exit 1
-
$NFT -f - <<< "$RULESET" && exit 0
exit 1
diff --git a/tests/shell/testcases/chains/0044chain_destroy_0 b/tests/shell/testcases/chains/0044chain_destroy_0
new file mode 100755
index 00000000..5c5a10a7
--- /dev/null
+++ b/tests/shell/testcases/chains/0044chain_destroy_0
@@ -0,0 +1,12 @@
+#!/bin/bash -e
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_destroy)
+
+$NFT add table t
+
+# pass for non-existent chain
+$NFT destroy chain t c
+
+# successfully delete existing chain
+$NFT add chain t c
+$NFT destroy chain t c
diff --git a/tests/shell/testcases/chains/dumps/0001jumps_0.json-nft b/tests/shell/testcases/chains/dumps/0001jumps_0.json-nft
new file mode 100644
index 00000000..ceef3224
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0001jumps_0.json-nft
@@ -0,0 +1,371 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c1",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c2",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c3",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c4",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c5",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c6",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c7",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c8",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c9",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c10",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c11",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c12",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c13",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c14",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c15",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c16",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c1",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c2"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c2",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c3"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c3",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c4"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c4",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c5"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c5",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c6"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c6",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c7"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c7",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c8"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c8",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c9"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c9",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c10"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c10",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c11"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c11",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c12"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c12",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c13"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c13",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c14"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c14",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c15"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c15",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c16"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0002jumps_1.json-nft b/tests/shell/testcases/chains/dumps/0002jumps_1.json-nft
new file mode 100644
index 00000000..66f921a0
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0002jumps_1.json-nft
@@ -0,0 +1,383 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c1",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c2",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c3",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c4",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c5",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c6",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c7",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c8",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c9",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c10",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c11",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c12",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c13",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c14",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c15",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c16",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c17",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c1",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c2"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c2",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c3"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c3",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c4"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c4",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c5"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c5",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c6"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c6",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c7"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c7",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c8"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c8",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c9"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c9",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c10"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c10",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c11"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c11",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c12"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c12",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c13"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c13",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c14"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c14",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c15"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c15",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c16"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0002jumps_1.nft b/tests/shell/testcases/chains/dumps/0002jumps_1.nft
new file mode 100644
index 00000000..ed37ad0e
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0002jumps_1.nft
@@ -0,0 +1,68 @@
+table ip t {
+ chain c1 {
+ type filter hook input priority filter; policy accept;
+ jump c2
+ }
+
+ chain c2 {
+ jump c3
+ }
+
+ chain c3 {
+ jump c4
+ }
+
+ chain c4 {
+ jump c5
+ }
+
+ chain c5 {
+ jump c6
+ }
+
+ chain c6 {
+ jump c7
+ }
+
+ chain c7 {
+ jump c8
+ }
+
+ chain c8 {
+ jump c9
+ }
+
+ chain c9 {
+ jump c10
+ }
+
+ chain c10 {
+ jump c11
+ }
+
+ chain c11 {
+ jump c12
+ }
+
+ chain c12 {
+ jump c13
+ }
+
+ chain c13 {
+ jump c14
+ }
+
+ chain c14 {
+ jump c15
+ }
+
+ chain c15 {
+ jump c16
+ }
+
+ chain c16 {
+ }
+
+ chain c17 {
+ }
+}
diff --git a/tests/shell/testcases/chains/dumps/0003jump_loop_1.json-nft b/tests/shell/testcases/chains/dumps/0003jump_loop_1.json-nft
new file mode 100644
index 00000000..ceef3224
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0003jump_loop_1.json-nft
@@ -0,0 +1,371 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c1",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c2",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c3",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c4",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c5",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c6",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c7",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c8",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c9",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c10",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c11",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c12",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c13",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c14",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c15",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c16",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c1",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c2"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c2",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c3"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c3",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c4"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c4",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c5"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c5",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c6"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c6",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c7"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c7",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c8"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c8",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c9"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c9",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c10"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c10",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c11"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c11",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c12"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c12",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c13"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c13",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c14"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c14",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c15"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c15",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c16"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0003jump_loop_1.nft b/tests/shell/testcases/chains/dumps/0003jump_loop_1.nft
new file mode 100644
index 00000000..7054cde4
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0003jump_loop_1.nft
@@ -0,0 +1,64 @@
+table ip t {
+ chain c1 {
+ jump c2
+ }
+
+ chain c2 {
+ jump c3
+ }
+
+ chain c3 {
+ jump c4
+ }
+
+ chain c4 {
+ jump c5
+ }
+
+ chain c5 {
+ jump c6
+ }
+
+ chain c6 {
+ jump c7
+ }
+
+ chain c7 {
+ jump c8
+ }
+
+ chain c8 {
+ jump c9
+ }
+
+ chain c9 {
+ jump c10
+ }
+
+ chain c10 {
+ jump c11
+ }
+
+ chain c11 {
+ jump c12
+ }
+
+ chain c12 {
+ jump c13
+ }
+
+ chain c13 {
+ jump c14
+ }
+
+ chain c14 {
+ jump c15
+ }
+
+ chain c15 {
+ jump c16
+ }
+
+ chain c16 {
+ }
+}
diff --git a/tests/shell/testcases/chains/dumps/0004busy_1.json-nft b/tests/shell/testcases/chains/dumps/0004busy_1.json-nft
new file mode 100644
index 00000000..314245ff
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0004busy_1.json-nft
@@ -0,0 +1,49 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c1",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c2",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c1",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c2"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0004busy_1.nft b/tests/shell/testcases/chains/dumps/0004busy_1.nft
new file mode 100644
index 00000000..429dd494
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0004busy_1.nft
@@ -0,0 +1,8 @@
+table ip t {
+ chain c1 {
+ jump c2
+ }
+
+ chain c2 {
+ }
+}
diff --git a/tests/shell/testcases/chains/dumps/0005busy_map_1.json-nft b/tests/shell/testcases/chains/dumps/0005busy_map_1.json-nft
new file mode 100644
index 00000000..ce776822
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0005busy_map_1.json-nft
@@ -0,0 +1,66 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c1",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c2",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c1",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ 1,
+ {
+ "jump": {
+ "target": "c2"
+ }
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0005busy_map_1.nft b/tests/shell/testcases/chains/dumps/0005busy_map_1.nft
new file mode 100644
index 00000000..acf23183
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0005busy_map_1.nft
@@ -0,0 +1,8 @@
+table ip t {
+ chain c1 {
+ tcp dport vmap { 1 : jump c2 }
+ }
+
+ chain c2 {
+ }
+}
diff --git a/tests/shell/testcases/chains/dumps/0006masquerade_0.json-nft b/tests/shell/testcases/chains/dumps/0006masquerade_0.json-nft
new file mode 100644
index 00000000..b6fc221f
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0006masquerade_0.json-nft
@@ -0,0 +1,43 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c1",
+ "handle": 0,
+ "type": "nat",
+ "hook": "postrouting",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c1",
+ "handle": 0,
+ "expr": [
+ {
+ "masquerade": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0007masquerade_1.json-nft b/tests/shell/testcases/chains/dumps/0007masquerade_1.json-nft
new file mode 100644
index 00000000..98b51044
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0007masquerade_1.json-nft
@@ -0,0 +1,30 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c1",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0007masquerade_1.nft b/tests/shell/testcases/chains/dumps/0007masquerade_1.nft
new file mode 100644
index 00000000..b25355f7
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0007masquerade_1.nft
@@ -0,0 +1,5 @@
+table ip t {
+ chain c1 {
+ type filter hook output priority filter; policy accept;
+ }
+}
diff --git a/tests/shell/testcases/chains/dumps/0008masquerade_jump_1.json-nft b/tests/shell/testcases/chains/dumps/0008masquerade_jump_1.json-nft
new file mode 100644
index 00000000..3215496f
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0008masquerade_jump_1.json-nft
@@ -0,0 +1,51 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "output",
+ "handle": 0,
+ "type": "nat",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c1",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c1",
+ "handle": 0,
+ "expr": [
+ {
+ "masquerade": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0008masquerade_jump_1.nft b/tests/shell/testcases/chains/dumps/0008masquerade_jump_1.nft
new file mode 100644
index 00000000..49910711
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0008masquerade_jump_1.nft
@@ -0,0 +1,9 @@
+table ip t {
+ chain output {
+ type nat hook output priority filter; policy accept;
+ }
+
+ chain c1 {
+ masquerade
+ }
+}
diff --git a/tests/shell/testcases/chains/dumps/0009masquerade_jump_1.json-nft b/tests/shell/testcases/chains/dumps/0009masquerade_jump_1.json-nft
new file mode 100644
index 00000000..3215496f
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0009masquerade_jump_1.json-nft
@@ -0,0 +1,51 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "output",
+ "handle": 0,
+ "type": "nat",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c1",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c1",
+ "handle": 0,
+ "expr": [
+ {
+ "masquerade": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0009masquerade_jump_1.nft b/tests/shell/testcases/chains/dumps/0009masquerade_jump_1.nft
new file mode 100644
index 00000000..49910711
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0009masquerade_jump_1.nft
@@ -0,0 +1,9 @@
+table ip t {
+ chain output {
+ type nat hook output priority filter; policy accept;
+ }
+
+ chain c1 {
+ masquerade
+ }
+}
diff --git a/tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.json-nft b/tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.json-nft
new file mode 100644
index 00000000..db64cdbc
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.json-nft
@@ -0,0 +1,26 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.nft b/tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.nft
new file mode 100644
index 00000000..1e0d1d60
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0010endless_jump_loop_1.nft
@@ -0,0 +1,4 @@
+table ip t {
+ chain c {
+ }
+}
diff --git a/tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.json-nft b/tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.json-nft
new file mode 100644
index 00000000..e1a2262f
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.json-nft
@@ -0,0 +1,75 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c1",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c2",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "m",
+ "table": "t",
+ "type": "inet_service",
+ "handle": 0,
+ "map": "verdict",
+ "elem": [
+ [
+ 2,
+ {
+ "jump": {
+ "target": "c2"
+ }
+ }
+ ]
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c1",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "data": "@m"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.nft b/tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.nft
new file mode 100644
index 00000000..ca0a7378
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0011endless_jump_loop_1.nft
@@ -0,0 +1,13 @@
+table ip t {
+ map m {
+ type inet_service : verdict
+ elements = { 2 : jump c2 }
+ }
+
+ chain c1 {
+ tcp dport vmap @m
+ }
+
+ chain c2 {
+ }
+}
diff --git a/tests/shell/testcases/chains/dumps/0013rename_0.json-nft b/tests/shell/testcases/chains/dumps/0013rename_0.json-nft
new file mode 100644
index 00000000..f89c455a
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0013rename_0.json-nft
@@ -0,0 +1,26 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c2",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0014rename_0.json-nft b/tests/shell/testcases/chains/dumps/0014rename_0.json-nft
new file mode 100644
index 00000000..f4c6855e
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0014rename_0.json-nft
@@ -0,0 +1,34 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c1",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c2",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0014rename_0.nft b/tests/shell/testcases/chains/dumps/0014rename_0.nft
new file mode 100644
index 00000000..574c4863
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0014rename_0.nft
@@ -0,0 +1,7 @@
+table ip t {
+ chain c1 {
+ }
+
+ chain c2 {
+ }
+}
diff --git a/tests/shell/testcases/chains/dumps/0015check_jump_loop_1.json-nft b/tests/shell/testcases/chains/dumps/0015check_jump_loop_1.json-nft
new file mode 100644
index 00000000..314245ff
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0015check_jump_loop_1.json-nft
@@ -0,0 +1,49 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c1",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c2",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c1",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c2"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0015check_jump_loop_1.nft b/tests/shell/testcases/chains/dumps/0015check_jump_loop_1.nft
new file mode 100644
index 00000000..429dd494
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0015check_jump_loop_1.nft
@@ -0,0 +1,8 @@
+table ip t {
+ chain c1 {
+ jump c2
+ }
+
+ chain c2 {
+ }
+}
diff --git a/tests/shell/testcases/chains/dumps/0016delete_handle_0.json-nft b/tests/shell/testcases/chains/dumps/0016delete_handle_0.json-nft
new file mode 100644
index 00000000..ca1311db
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0016delete_handle_0.json-nft
@@ -0,0 +1,57 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test-ip",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "test-ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "test-ip",
+ "name": "z",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "test-ip6",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "test-ip6",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "test-ip6",
+ "name": "y",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0017masquerade_jump_1.json-nft b/tests/shell/testcases/chains/dumps/0017masquerade_jump_1.json-nft
new file mode 100644
index 00000000..b368c23a
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0017masquerade_jump_1.json-nft
@@ -0,0 +1,53 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 4,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c1",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "c1"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0017masquerade_jump_1.nft b/tests/shell/testcases/chains/dumps/0017masquerade_jump_1.nft
new file mode 100644
index 00000000..636e8440
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0017masquerade_jump_1.nft
@@ -0,0 +1,9 @@
+table ip t {
+ chain input {
+ type filter hook input priority filter + 4; policy accept;
+ jump c1
+ }
+
+ chain c1 {
+ }
+}
diff --git a/tests/shell/testcases/chains/dumps/0018check_jump_loop_1.json-nft b/tests/shell/testcases/chains/dumps/0018check_jump_loop_1.json-nft
new file mode 100644
index 00000000..7294c841
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0018check_jump_loop_1.json-nft
@@ -0,0 +1,49 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "ap1",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "ap2",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "ap1",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "ap2"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0018check_jump_loop_1.nft b/tests/shell/testcases/chains/dumps/0018check_jump_loop_1.nft
new file mode 100644
index 00000000..437900bc
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0018check_jump_loop_1.nft
@@ -0,0 +1,8 @@
+table ip filter {
+ chain ap1 {
+ jump ap2
+ }
+
+ chain ap2 {
+ }
+}
diff --git a/tests/shell/testcases/chains/dumps/0019masquerade_jump_1.json-nft b/tests/shell/testcases/chains/dumps/0019masquerade_jump_1.json-nft
new file mode 100644
index 00000000..c164ffb8
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0019masquerade_jump_1.json-nft
@@ -0,0 +1,70 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 4,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c1",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ "1.1.1.1",
+ {
+ "jump": {
+ "target": "c1"
+ }
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0019masquerade_jump_1.nft b/tests/shell/testcases/chains/dumps/0019masquerade_jump_1.nft
new file mode 100644
index 00000000..81cf9cc7
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0019masquerade_jump_1.nft
@@ -0,0 +1,9 @@
+table ip t {
+ chain input {
+ type filter hook input priority filter + 4; policy accept;
+ ip saddr vmap { 1.1.1.1 : jump c1 }
+ }
+
+ chain c1 {
+ }
+}
diff --git a/tests/shell/testcases/chains/dumps/0020depth_1.json-nft b/tests/shell/testcases/chains/dumps/0020depth_1.json-nft
new file mode 100644
index 00000000..31bc2b13
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0020depth_1.json-nft
@@ -0,0 +1,475 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "a0",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "a1",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "a2",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "a3",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "a4",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "a5",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "a6",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "a7",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "a8",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "a9",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "a10",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "a11",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "a12",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "a13",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "a14",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "a15",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "a16",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "a17",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "a18",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "a19",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "a1"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "a0",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "a1"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "a1",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "a2"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "a2",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "a3"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "a3",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "a4"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "a4",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "a5"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "a5",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "a6"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "a6",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "a7"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "a7",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "a8"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "a8",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "a9"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "a9",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "a10"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "a11",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "a12"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "a12",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "a13"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "a13",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "a14"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "a14",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "a15"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "a15",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "a16"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "a16",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "a17"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "a17",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "a18"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "a18",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "a19"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0020depth_1.nft b/tests/shell/testcases/chains/dumps/0020depth_1.nft
new file mode 100644
index 00000000..422c3952
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0020depth_1.nft
@@ -0,0 +1,84 @@
+table ip filter {
+ chain input {
+ type filter hook input priority filter; policy accept;
+ jump a1
+ }
+
+ chain a0 {
+ jump a1
+ }
+
+ chain a1 {
+ jump a2
+ }
+
+ chain a2 {
+ jump a3
+ }
+
+ chain a3 {
+ jump a4
+ }
+
+ chain a4 {
+ jump a5
+ }
+
+ chain a5 {
+ jump a6
+ }
+
+ chain a6 {
+ jump a7
+ }
+
+ chain a7 {
+ jump a8
+ }
+
+ chain a8 {
+ jump a9
+ }
+
+ chain a9 {
+ jump a10
+ }
+
+ chain a10 {
+ }
+
+ chain a11 {
+ jump a12
+ }
+
+ chain a12 {
+ jump a13
+ }
+
+ chain a13 {
+ jump a14
+ }
+
+ chain a14 {
+ jump a15
+ }
+
+ chain a15 {
+ jump a16
+ }
+
+ chain a16 {
+ jump a17
+ }
+
+ chain a17 {
+ jump a18
+ }
+
+ chain a18 {
+ jump a19
+ }
+
+ chain a19 {
+ }
+}
diff --git a/tests/shell/testcases/chains/dumps/0021prio_0.json-nft b/tests/shell/testcases/chains/dumps/0021prio_0.json-nft
new file mode 100644
index 00000000..1a3e1161
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0021prio_0.json-nft
@@ -0,0 +1,4743 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingrawm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -311,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingrawm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -310,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingraw",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -300,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingrawp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -290,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingrawp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -289,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingmanglem11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -161,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingmanglem10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -160,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingmangle",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -150,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingmanglep10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -140,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingmanglep11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -139,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingfilterm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingfilterm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingfilter",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingfilterp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingfilterp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingsecuritym11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 39,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingsecuritym10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 40,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingsecurity",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 50,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingsecurityp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 60,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingsecurityp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 61,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "inputrawm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -311,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "inputrawm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -310,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "inputraw",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -300,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "inputrawp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -290,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "inputrawp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -289,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "inputmanglem11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -161,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "inputmanglem10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -160,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "inputmangle",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -150,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "inputmanglep10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -140,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "inputmanglep11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -139,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "inputfilterm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "inputfilterm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "inputfilter",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "inputfilterp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "inputfilterp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "inputsecuritym11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 39,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "inputsecuritym10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 40,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "inputsecurity",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 50,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "inputsecurityp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 60,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "inputsecurityp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 61,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "forwardrawm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -311,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "forwardrawm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -310,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "forwardraw",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -300,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "forwardrawp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -290,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "forwardrawp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -289,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "forwardmanglem11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -161,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "forwardmanglem10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -160,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "forwardmangle",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -150,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "forwardmanglep10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -140,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "forwardmanglep11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -139,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "forwardfilterm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "forwardfilterm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "forwardfilter",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "forwardfilterp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "forwardfilterp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "forwardsecuritym11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 39,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "forwardsecuritym10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 40,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "forwardsecurity",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 50,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "forwardsecurityp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 60,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "forwardsecurityp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 61,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "outputrawm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -311,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "outputrawm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -310,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "outputraw",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -300,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "outputrawp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -290,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "outputrawp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -289,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "outputmanglem11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -161,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "outputmanglem10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -160,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "outputmangle",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -150,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "outputmanglep10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -140,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "outputmanglep11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -139,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "outputfilterm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "outputfilterm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "outputfilter",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "outputfilterp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "outputfilterp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "outputsecuritym11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 39,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "outputsecuritym10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 40,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "outputsecurity",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 50,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "outputsecurityp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 60,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "outputsecurityp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 61,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingrawm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -311,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingrawm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -310,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingraw",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -300,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingrawp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -290,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingrawp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -289,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingmanglem11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -161,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingmanglem10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -160,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingmangle",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -150,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingmanglep10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -140,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingmanglep11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -139,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingfilterm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingfilterm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingfilter",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingfilterp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingfilterp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingsecuritym11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 39,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingsecuritym10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 40,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingsecurity",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 50,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingsecurityp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 60,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingsecurityp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 61,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingdstnatm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -111,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingdstnatm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -110,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingdstnat",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -100,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingdstnatp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -90,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "preroutingdstnatp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -89,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingsrcnatm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 89,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingsrcnatm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 90,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingsrcnat",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 100,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingsrcnatp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 110,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "postroutingsrcnatp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 111,
+ "policy": "accept"
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingrawm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -311,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingrawm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -310,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingraw",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -300,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingrawp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -290,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingrawp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -289,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingmanglem11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -161,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingmanglem10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -160,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingmangle",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -150,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingmanglep10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -140,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingmanglep11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -139,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingfilterm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingfilterm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingfilter",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingfilterp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingfilterp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingsecuritym11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 39,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingsecuritym10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 40,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingsecurity",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 50,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingsecurityp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 60,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingsecurityp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 61,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "inputrawm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -311,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "inputrawm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -310,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "inputraw",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -300,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "inputrawp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -290,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "inputrawp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -289,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "inputmanglem11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -161,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "inputmanglem10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -160,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "inputmangle",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -150,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "inputmanglep10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -140,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "inputmanglep11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -139,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "inputfilterm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "inputfilterm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "inputfilter",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "inputfilterp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "inputfilterp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "inputsecuritym11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 39,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "inputsecuritym10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 40,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "inputsecurity",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 50,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "inputsecurityp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 60,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "inputsecurityp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 61,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "forwardrawm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -311,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "forwardrawm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -310,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "forwardraw",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -300,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "forwardrawp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -290,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "forwardrawp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -289,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "forwardmanglem11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -161,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "forwardmanglem10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -160,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "forwardmangle",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -150,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "forwardmanglep10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -140,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "forwardmanglep11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -139,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "forwardfilterm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "forwardfilterm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "forwardfilter",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "forwardfilterp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "forwardfilterp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "forwardsecuritym11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 39,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "forwardsecuritym10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 40,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "forwardsecurity",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 50,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "forwardsecurityp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 60,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "forwardsecurityp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 61,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "outputrawm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -311,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "outputrawm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -310,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "outputraw",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -300,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "outputrawp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -290,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "outputrawp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -289,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "outputmanglem11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -161,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "outputmanglem10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -160,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "outputmangle",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -150,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "outputmanglep10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -140,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "outputmanglep11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -139,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "outputfilterm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "outputfilterm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "outputfilter",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "outputfilterp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "outputfilterp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "outputsecuritym11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 39,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "outputsecuritym10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 40,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "outputsecurity",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 50,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "outputsecurityp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 60,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "outputsecurityp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 61,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingrawm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -311,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingrawm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -310,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingraw",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -300,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingrawp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -290,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingrawp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -289,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingmanglem11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -161,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingmanglem10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -160,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingmangle",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -150,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingmanglep10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -140,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingmanglep11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -139,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingfilterm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingfilterm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingfilter",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingfilterp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingfilterp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingsecuritym11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 39,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingsecuritym10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 40,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingsecurity",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 50,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingsecurityp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 60,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingsecurityp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 61,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingdstnatm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -111,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingdstnatm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -110,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingdstnat",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -100,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingdstnatp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -90,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "preroutingdstnatp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -89,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingsrcnatm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 89,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingsrcnatm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 90,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingsrcnat",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 100,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingsrcnatp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 110,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "postroutingsrcnatp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 111,
+ "policy": "accept"
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingrawm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -311,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingrawm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -310,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingraw",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -300,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingrawp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -290,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingrawp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -289,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingmanglem11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -161,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingmanglem10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -160,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingmangle",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -150,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingmanglep10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -140,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingmanglep11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -139,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingfilterm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingfilterm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingfilter",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingfilterp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingfilterp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingsecuritym11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 39,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingsecuritym10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 40,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingsecurity",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 50,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingsecurityp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 60,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingsecurityp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 61,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "inputrawm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -311,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "inputrawm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -310,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "inputraw",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -300,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "inputrawp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -290,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "inputrawp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -289,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "inputmanglem11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -161,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "inputmanglem10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -160,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "inputmangle",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -150,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "inputmanglep10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -140,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "inputmanglep11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -139,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "inputfilterm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "inputfilterm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "inputfilter",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "inputfilterp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "inputfilterp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "inputsecuritym11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 39,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "inputsecuritym10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 40,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "inputsecurity",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 50,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "inputsecurityp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 60,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "inputsecurityp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 61,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "forwardrawm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -311,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "forwardrawm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -310,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "forwardraw",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -300,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "forwardrawp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -290,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "forwardrawp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -289,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "forwardmanglem11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -161,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "forwardmanglem10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -160,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "forwardmangle",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -150,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "forwardmanglep10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -140,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "forwardmanglep11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -139,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "forwardfilterm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "forwardfilterm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "forwardfilter",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "forwardfilterp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "forwardfilterp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "forwardsecuritym11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 39,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "forwardsecuritym10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 40,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "forwardsecurity",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 50,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "forwardsecurityp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 60,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "forwardsecurityp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 61,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "outputrawm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -311,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "outputrawm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -310,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "outputraw",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -300,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "outputrawp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -290,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "outputrawp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -289,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "outputmanglem11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -161,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "outputmanglem10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -160,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "outputmangle",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -150,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "outputmanglep10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -140,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "outputmanglep11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -139,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "outputfilterm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "outputfilterm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "outputfilter",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "outputfilterp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "outputfilterp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "outputsecuritym11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 39,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "outputsecuritym10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 40,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "outputsecurity",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 50,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "outputsecurityp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 60,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "outputsecurityp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 61,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingrawm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -311,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingrawm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -310,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingraw",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -300,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingrawp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -290,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingrawp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -289,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingmanglem11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -161,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingmanglem10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -160,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingmangle",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -150,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingmanglep10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -140,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingmanglep11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -139,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingfilterm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingfilterm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingfilter",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingfilterp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingfilterp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingsecuritym11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 39,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingsecuritym10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 40,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingsecurity",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 50,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingsecurityp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 60,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingsecurityp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 61,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingdstnatm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -111,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingdstnatm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -110,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingdstnat",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -100,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingdstnatp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -90,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "preroutingdstnatp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -89,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingsrcnatm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 89,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingsrcnatm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 90,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingsrcnat",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 100,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingsrcnatp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 110,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "postroutingsrcnatp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 111,
+ "policy": "accept"
+ }
+ },
+ {
+ "table": {
+ "family": "arp",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "arp",
+ "table": "x",
+ "name": "inputfilterm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "arp",
+ "table": "x",
+ "name": "inputfilterm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "arp",
+ "table": "x",
+ "name": "inputfilter",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "arp",
+ "table": "x",
+ "name": "inputfilterp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "arp",
+ "table": "x",
+ "name": "inputfilterp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "arp",
+ "table": "x",
+ "name": "outputfilterm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "arp",
+ "table": "x",
+ "name": "outputfilterm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "arp",
+ "table": "x",
+ "name": "outputfilter",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "arp",
+ "table": "x",
+ "name": "outputfilterp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "arp",
+ "table": "x",
+ "name": "outputfilterp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 11,
+ "policy": "accept"
+ }
+ },
+ {
+ "table": {
+ "family": "netdev",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "x",
+ "name": "ingressfilterm11",
+ "handle": 0,
+ "dev": "lo",
+ "type": "filter",
+ "hook": "ingress",
+ "prio": -11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "x",
+ "name": "ingressfilterm10",
+ "handle": 0,
+ "dev": "lo",
+ "type": "filter",
+ "hook": "ingress",
+ "prio": -10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "x",
+ "name": "ingressfilter",
+ "handle": 0,
+ "dev": "lo",
+ "type": "filter",
+ "hook": "ingress",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "x",
+ "name": "ingressfilterp10",
+ "handle": 0,
+ "dev": "lo",
+ "type": "filter",
+ "hook": "ingress",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "x",
+ "name": "ingressfilterp11",
+ "handle": 0,
+ "dev": "lo",
+ "type": "filter",
+ "hook": "ingress",
+ "prio": 11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "x",
+ "name": "egressfilterm11",
+ "handle": 0,
+ "dev": "lo",
+ "type": "filter",
+ "hook": "egress",
+ "prio": -11,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "x",
+ "name": "egressfilterm10",
+ "handle": 0,
+ "dev": "lo",
+ "type": "filter",
+ "hook": "egress",
+ "prio": -10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "x",
+ "name": "egressfilter",
+ "handle": 0,
+ "dev": "lo",
+ "type": "filter",
+ "hook": "egress",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "x",
+ "name": "egressfilterp10",
+ "handle": 0,
+ "dev": "lo",
+ "type": "filter",
+ "hook": "egress",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "x",
+ "name": "egressfilterp11",
+ "handle": 0,
+ "dev": "lo",
+ "type": "filter",
+ "hook": "egress",
+ "prio": 11,
+ "policy": "accept"
+ }
+ },
+ {
+ "table": {
+ "family": "bridge",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "preroutingfilterm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -211,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "preroutingfilterm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -210,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "preroutingfilter",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -200,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "preroutingfilterp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -190,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "preroutingfilterp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -189,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "inputfilterm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -211,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "inputfilterm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -210,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "inputfilter",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -200,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "inputfilterp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -190,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "inputfilterp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -189,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "forwardfilterm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -211,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "forwardfilterm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -210,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "forwardfilter",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -200,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "forwardfilterp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -190,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "forwardfilterp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": -189,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "outputfilterm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -211,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "outputfilterm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -210,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "outputfilter",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -200,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "outputfilterp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -190,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "outputfilterp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": -189,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "postroutingfilterm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -211,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "postroutingfilterm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -210,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "postroutingfilter",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -200,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "postroutingfilterp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -190,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "postroutingfilterp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -189,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "preroutingdstnatm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -311,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "preroutingdstnatm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -310,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "preroutingdstnat",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -300,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "preroutingdstnatp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -290,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "preroutingdstnatp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -289,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "outputoutm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 89,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "outputoutm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 90,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "outputout",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 100,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "outputoutp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 110,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "outputoutp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 111,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "postroutingsrcnatm11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 289,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "postroutingsrcnatm10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 290,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "postroutingsrcnat",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 300,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "postroutingsrcnatp10",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 310,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "x",
+ "name": "postroutingsrcnatp11",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 311,
+ "policy": "accept"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0021prio_0.nft b/tests/shell/testcases/chains/dumps/0021prio_0.nft
index ca94d441..4297d246 100644
--- a/tests/shell/testcases/chains/dumps/0021prio_0.nft
+++ b/tests/shell/testcases/chains/dumps/0021prio_0.nft
@@ -1382,6 +1382,26 @@ table netdev x {
chain ingressfilterp11 {
type filter hook ingress device "lo" priority 11; policy accept;
}
+
+ chain egressfilterm11 {
+ type filter hook egress device "lo" priority -11; policy accept;
+ }
+
+ chain egressfilterm10 {
+ type filter hook egress device "lo" priority filter - 10; policy accept;
+ }
+
+ chain egressfilter {
+ type filter hook egress device "lo" priority filter; policy accept;
+ }
+
+ chain egressfilterp10 {
+ type filter hook egress device "lo" priority filter + 10; policy accept;
+ }
+
+ chain egressfilterp11 {
+ type filter hook egress device "lo" priority 11; policy accept;
+ }
}
table bridge x {
chain preroutingfilterm11 {
diff --git a/tests/shell/testcases/chains/dumps/0022prio_dummy_1.json-nft b/tests/shell/testcases/chains/dumps/0022prio_dummy_1.json-nft
new file mode 100644
index 00000000..15ec0aac
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0022prio_dummy_1.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0022prio_dummy_1.nft b/tests/shell/testcases/chains/dumps/0022prio_dummy_1.nft
new file mode 100644
index 00000000..5d4d2caf
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0022prio_dummy_1.nft
@@ -0,0 +1,2 @@
+table ip x {
+}
diff --git a/tests/shell/testcases/chains/dumps/0023prio_inet_srcnat_1.json-nft b/tests/shell/testcases/chains/dumps/0023prio_inet_srcnat_1.json-nft
new file mode 100644
index 00000000..72e0d438
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0023prio_inet_srcnat_1.json-nft
@@ -0,0 +1,32 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "x",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0023prio_inet_srcnat_1.nft b/tests/shell/testcases/chains/dumps/0023prio_inet_srcnat_1.nft
new file mode 100644
index 00000000..46912eaa
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0023prio_inet_srcnat_1.nft
@@ -0,0 +1,6 @@
+table ip x {
+}
+table ip6 x {
+}
+table inet x {
+}
diff --git a/tests/shell/testcases/chains/dumps/0024prio_inet_dstnat_1.json-nft b/tests/shell/testcases/chains/dumps/0024prio_inet_dstnat_1.json-nft
new file mode 100644
index 00000000..72e0d438
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0024prio_inet_dstnat_1.json-nft
@@ -0,0 +1,32 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "x",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0024prio_inet_dstnat_1.nft b/tests/shell/testcases/chains/dumps/0024prio_inet_dstnat_1.nft
new file mode 100644
index 00000000..46912eaa
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0024prio_inet_dstnat_1.nft
@@ -0,0 +1,6 @@
+table ip x {
+}
+table ip6 x {
+}
+table inet x {
+}
diff --git a/tests/shell/testcases/chains/dumps/0025prio_arp_1.json-nft b/tests/shell/testcases/chains/dumps/0025prio_arp_1.json-nft
new file mode 100644
index 00000000..17410e32
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0025prio_arp_1.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "arp",
+ "name": "x",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0025prio_arp_1.nft b/tests/shell/testcases/chains/dumps/0025prio_arp_1.nft
new file mode 100644
index 00000000..7483cdaa
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0025prio_arp_1.nft
@@ -0,0 +1,2 @@
+table arp x {
+}
diff --git a/tests/shell/testcases/chains/dumps/0026prio_netdev_1.json-nft b/tests/shell/testcases/chains/dumps/0026prio_netdev_1.json-nft
new file mode 100644
index 00000000..7d78bd67
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0026prio_netdev_1.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "netdev",
+ "name": "x",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0026prio_netdev_1.nft b/tests/shell/testcases/chains/dumps/0026prio_netdev_1.nft
new file mode 100644
index 00000000..aa571e00
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0026prio_netdev_1.nft
@@ -0,0 +1,2 @@
+table netdev x {
+}
diff --git a/tests/shell/testcases/chains/dumps/0027prio_bridge_dstnat_1.json-nft b/tests/shell/testcases/chains/dumps/0027prio_bridge_dstnat_1.json-nft
new file mode 100644
index 00000000..af6ff0a4
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0027prio_bridge_dstnat_1.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "bridge",
+ "name": "x",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0027prio_bridge_dstnat_1.nft b/tests/shell/testcases/chains/dumps/0027prio_bridge_dstnat_1.nft
new file mode 100644
index 00000000..d17be818
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0027prio_bridge_dstnat_1.nft
@@ -0,0 +1,2 @@
+table bridge x {
+}
diff --git a/tests/shell/testcases/chains/dumps/0028prio_bridge_out_1.json-nft b/tests/shell/testcases/chains/dumps/0028prio_bridge_out_1.json-nft
new file mode 100644
index 00000000..af6ff0a4
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0028prio_bridge_out_1.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "bridge",
+ "name": "x",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0028prio_bridge_out_1.nft b/tests/shell/testcases/chains/dumps/0028prio_bridge_out_1.nft
new file mode 100644
index 00000000..d17be818
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0028prio_bridge_out_1.nft
@@ -0,0 +1,2 @@
+table bridge x {
+}
diff --git a/tests/shell/testcases/chains/dumps/0029prio_bridge_srcnat_1.json-nft b/tests/shell/testcases/chains/dumps/0029prio_bridge_srcnat_1.json-nft
new file mode 100644
index 00000000..af6ff0a4
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0029prio_bridge_srcnat_1.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "bridge",
+ "name": "x",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0029prio_bridge_srcnat_1.nft b/tests/shell/testcases/chains/dumps/0029prio_bridge_srcnat_1.nft
new file mode 100644
index 00000000..d17be818
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0029prio_bridge_srcnat_1.nft
@@ -0,0 +1,2 @@
+table bridge x {
+}
diff --git a/tests/shell/testcases/chains/dumps/0030create_0.json-nft b/tests/shell/testcases/chains/dumps/0030create_0.json-nft
new file mode 100644
index 00000000..b6088c80
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0030create_0.json-nft
@@ -0,0 +1,26 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0031priority_variable_0.json-nft b/tests/shell/testcases/chains/dumps/0031priority_variable_0.json-nft
new file mode 100644
index 00000000..9572eda3
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0031priority_variable_0.json-nft
@@ -0,0 +1,30 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "global",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "global",
+ "name": "prerouting",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 0,
+ "policy": "accept"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0032priority_variable_0.json-nft b/tests/shell/testcases/chains/dumps/0032priority_variable_0.json-nft
new file mode 100644
index 00000000..3044a668
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0032priority_variable_0.json-nft
@@ -0,0 +1,54 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "global",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "global",
+ "name": "prerouting",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "global",
+ "name": "forward",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -100,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "global",
+ "name": "postrouting",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": -10,
+ "policy": "accept"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0033priority_variable_1.json-nft b/tests/shell/testcases/chains/dumps/0033priority_variable_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0033priority_variable_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0033priority_variable_1.nft b/tests/shell/testcases/chains/dumps/0033priority_variable_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0033priority_variable_1.nft
diff --git a/tests/shell/testcases/chains/dumps/0034priority_variable_1.json-nft b/tests/shell/testcases/chains/dumps/0034priority_variable_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0034priority_variable_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0034priority_variable_1.nft b/tests/shell/testcases/chains/dumps/0034priority_variable_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0034priority_variable_1.nft
diff --git a/tests/shell/testcases/chains/dumps/0035policy_variable_0.json-nft b/tests/shell/testcases/chains/dumps/0035policy_variable_0.json-nft
new file mode 100644
index 00000000..9572eda3
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0035policy_variable_0.json-nft
@@ -0,0 +1,30 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "global",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "global",
+ "name": "prerouting",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 0,
+ "policy": "accept"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0036policy_variable_0.json-nft b/tests/shell/testcases/chains/dumps/0036policy_variable_0.json-nft
new file mode 100644
index 00000000..fc688463
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0036policy_variable_0.json-nft
@@ -0,0 +1,30 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "global",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "global",
+ "name": "prerouting",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 0,
+ "policy": "drop"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0026policy_variable_0.nft b/tests/shell/testcases/chains/dumps/0036policy_variable_0.nft
index d729e1ea..d729e1ea 100644
--- a/tests/shell/testcases/nft-f/dumps/0026policy_variable_0.nft
+++ b/tests/shell/testcases/chains/dumps/0036policy_variable_0.nft
diff --git a/tests/shell/testcases/chains/dumps/0037policy_variable_1.json-nft b/tests/shell/testcases/chains/dumps/0037policy_variable_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0037policy_variable_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0037policy_variable_1.nft b/tests/shell/testcases/chains/dumps/0037policy_variable_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0037policy_variable_1.nft
diff --git a/tests/shell/testcases/chains/dumps/0038policy_variable_1.json-nft b/tests/shell/testcases/chains/dumps/0038policy_variable_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0038policy_variable_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0038policy_variable_1.nft b/tests/shell/testcases/chains/dumps/0038policy_variable_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0038policy_variable_1.nft
diff --git a/tests/shell/testcases/chains/dumps/0039negative_priority_0.json-nft b/tests/shell/testcases/chains/dumps/0039negative_priority_0.json-nft
new file mode 100644
index 00000000..94218a8d
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0039negative_priority_0.json-nft
@@ -0,0 +1,30 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -30,
+ "policy": "accept"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0039negative_priority_0.nft b/tests/shell/testcases/chains/dumps/0039negative_priority_0.nft
new file mode 100644
index 00000000..20f8272a
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0039negative_priority_0.nft
@@ -0,0 +1,5 @@
+table ip t {
+ chain c {
+ type filter hook input priority -30; policy accept;
+ }
+}
diff --git a/tests/shell/testcases/chains/dumps/0042chain_variable_0.json-nft b/tests/shell/testcases/chains/dumps/0042chain_variable_0.json-nft
new file mode 100644
index 00000000..4059e85b
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0042chain_variable_0.json-nft
@@ -0,0 +1,90 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "netdev",
+ "name": "filter1",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "filter1",
+ "name": "Main_Ingress1",
+ "handle": 0,
+ "dev": "lo",
+ "type": "filter",
+ "hook": "ingress",
+ "prio": -500,
+ "policy": "accept"
+ }
+ },
+ {
+ "table": {
+ "family": "netdev",
+ "name": "filter2",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "filter2",
+ "name": "Main_Ingress2",
+ "handle": 0,
+ "dev": [
+ "d23456789012345",
+ "lo"
+ ],
+ "type": "filter",
+ "hook": "ingress",
+ "prio": -500,
+ "policy": "accept"
+ }
+ },
+ {
+ "table": {
+ "family": "netdev",
+ "name": "filter3",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "filter3",
+ "name": "Main_Ingress3",
+ "handle": 0,
+ "dev": [
+ "d23456789012345",
+ "lo"
+ ],
+ "type": "filter",
+ "hook": "ingress",
+ "prio": -500,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "filter3",
+ "name": "Main_Egress3",
+ "handle": 0,
+ "dev": "lo",
+ "type": "filter",
+ "hook": "egress",
+ "prio": -500,
+ "policy": "accept"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0042chain_variable_0.nft b/tests/shell/testcases/chains/dumps/0042chain_variable_0.nft
index 12931aad..84a908d3 100644
--- a/tests/shell/testcases/chains/dumps/0042chain_variable_0.nft
+++ b/tests/shell/testcases/chains/dumps/0042chain_variable_0.nft
@@ -5,11 +5,15 @@ 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 {
+ type filter hook egress device "lo" priority -500; policy accept;
}
}
diff --git a/tests/shell/testcases/chains/dumps/0043chain_ingress_0.json-nft b/tests/shell/testcases/chains/dumps/0043chain_ingress_0.json-nft
new file mode 100644
index 00000000..6753658e
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0043chain_ingress_0.json-nft
@@ -0,0 +1,55 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "ingress",
+ "handle": 0,
+ "dev": "lo",
+ "type": "filter",
+ "hook": "ingress",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "forward",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 0,
+ "policy": "accept"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0043chain_ingress.nft b/tests/shell/testcases/chains/dumps/0043chain_ingress_0.nft
index 74670423..8483b265 100644
--- a/tests/shell/testcases/chains/dumps/0043chain_ingress.nft
+++ b/tests/shell/testcases/chains/dumps/0043chain_ingress_0.nft
@@ -1,10 +1,12 @@
table inet filter {
chain ingress {
- type filter hook ingress device \"lo\" priority filter; policy accept;
+ type filter hook ingress device "lo" priority filter; policy accept;
}
+
chain input {
type filter hook input priority filter; policy accept;
}
+
chain forward {
type filter hook forward priority filter; policy accept;
}
diff --git a/tests/shell/testcases/chains/dumps/0044chain_destroy_0.json-nft b/tests/shell/testcases/chains/dumps/0044chain_destroy_0.json-nft
new file mode 100644
index 00000000..e0e56fec
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0044chain_destroy_0.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/0044chain_destroy_0.nft b/tests/shell/testcases/chains/dumps/0044chain_destroy_0.nft
new file mode 100644
index 00000000..985768ba
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/0044chain_destroy_0.nft
@@ -0,0 +1,2 @@
+table ip t {
+}
diff --git a/tests/shell/testcases/chains/dumps/netdev_chain_0.json-nft b/tests/shell/testcases/chains/dumps/netdev_chain_0.json-nft
new file mode 100644
index 00000000..7d78bd67
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/netdev_chain_0.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "netdev",
+ "name": "x",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/netdev_chain_0.nft b/tests/shell/testcases/chains/dumps/netdev_chain_0.nft
new file mode 100644
index 00000000..aa571e00
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/netdev_chain_0.nft
@@ -0,0 +1,2 @@
+table netdev x {
+}
diff --git a/tests/shell/testcases/chains/dumps/netdev_chain_autoremove.json-nft b/tests/shell/testcases/chains/dumps/netdev_chain_autoremove.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/netdev_chain_autoremove.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/chains/dumps/netdev_chain_autoremove.nft b/tests/shell/testcases/chains/dumps/netdev_chain_autoremove.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/netdev_chain_autoremove.nft
diff --git a/tests/shell/testcases/chains/dumps/netdev_chain_dev_gone.nodump b/tests/shell/testcases/chains/dumps/netdev_chain_dev_gone.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/netdev_chain_dev_gone.nodump
diff --git a/tests/shell/testcases/chains/dumps/netdev_chain_multidev_gone.nodump b/tests/shell/testcases/chains/dumps/netdev_chain_multidev_gone.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/netdev_chain_multidev_gone.nodump
diff --git a/tests/shell/testcases/chains/dumps/netdev_multidev_netns_gone.nodump b/tests/shell/testcases/chains/dumps/netdev_multidev_netns_gone.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/netdev_multidev_netns_gone.nodump
diff --git a/tests/shell/testcases/chains/dumps/netdev_netns_gone.nodump b/tests/shell/testcases/chains/dumps/netdev_netns_gone.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/chains/dumps/netdev_netns_gone.nodump
diff --git a/tests/shell/testcases/chains/netdev_chain_0 b/tests/shell/testcases/chains/netdev_chain_0
new file mode 100755
index 00000000..a323e6ec
--- /dev/null
+++ b/tests/shell/testcases/chains/netdev_chain_0
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_netdev_chain_without_device)
+
+set -e
+
+iface_cleanup() {
+ ip link del d0 &>/dev/null || :
+ ip link del d1 &>/dev/null || :
+ ip link del d2 &>/dev/null || :
+}
+trap 'iface_cleanup' EXIT
+iface_cleanup
+
+ip link add d0 type dummy
+ip link add d1 type dummy
+ip link add d2 type dummy
+
+RULESET="table netdev x {
+ chain y {
+ type filter hook ingress priority 0; policy accept;
+ }
+}"
+
+$NFT -f - <<< "$RULESET"
+
+$NFT add chain netdev x y '{ devices = { d0 }; }'
+$NFT add chain netdev x y '{ devices = { d1, d2, lo }; }'
+$NFT delete chain netdev x y '{ devices = { lo }; }'
diff --git a/tests/shell/testcases/chains/netdev_chain_autoremove b/tests/shell/testcases/chains/netdev_chain_autoremove
new file mode 100755
index 00000000..21f3ad29
--- /dev/null
+++ b/tests/shell/testcases/chains/netdev_chain_autoremove
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+set -e
+
+# Test auto-removal of chain hook on netns removal
+unshare -n bash -e -c "ip link add br0 type bridge; \
+ $NFT add table netdev test; \
+ $NFT add chain netdev test ingress { type filter hook ingress device \"br0\" priority 0\; policy drop\; } ; \
+"
diff --git a/tests/shell/testcases/chains/netdev_chain_dev_gone b/tests/shell/testcases/chains/netdev_chain_dev_gone
new file mode 100755
index 00000000..99933a31
--- /dev/null
+++ b/tests/shell/testcases/chains/netdev_chain_dev_gone
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_inet_ingress)
+
+set -e
+
+iface_cleanup() {
+ ip link del d0 &>/dev/null || :
+}
+trap 'iface_cleanup' EXIT
+
+ip link add d0 type dummy
+
+load_ruleset() {
+ family=$1
+
+ # Test auto-removal of chain hook on device removal
+ RULESET="table $family x {
+ chain x {}
+ chain w {
+ ip daddr 8.7.6.0/24 jump x
+ }
+ chain y {
+ type filter hook ingress device \"d0\" priority 0;
+ ip saddr { 1.2.3.4, 2.3.4.5 } counter
+ ip daddr vmap { 5.4.3.0/24 : jump w, 8.9.0.0/24 : jump x }
+ }
+}"
+ $NFT -c -f - <<< $RULESET
+ $NFT -f - <<< $RULESET
+}
+
+load_ruleset "inet"
+load_ruleset "netdev"
diff --git a/tests/shell/testcases/chains/netdev_chain_multidev_gone b/tests/shell/testcases/chains/netdev_chain_multidev_gone
new file mode 100755
index 00000000..e82698a7
--- /dev/null
+++ b/tests/shell/testcases/chains/netdev_chain_multidev_gone
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_chain_binding)
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_netdev_chain_multidevice)
+
+set -e
+
+iface_cleanup() {
+ ip link del d0 &>/dev/null || :
+ ip link del d1 &>/dev/null || :
+ ip link del d2 &>/dev/null || :
+}
+trap 'iface_cleanup' EXIT
+
+ip link add d0 type dummy
+ip link add d1 type dummy
+ip link add d2 type dummy
+
+load_ruleset() {
+ family=$1
+
+ # Test auto-removal of chain hook on device removal
+ RULESET="table $family x {
+ chain x {}
+ chain w {
+ ip daddr 8.7.6.0/24 jump {
+ ip daddr vmap { 8.7.6.3 : jump x, 8.7.6.4 : jump x }
+ }
+ }
+ chain y {
+ type filter hook ingress devices = { d0, d1, d2 } priority 0;
+ ip saddr { 1.2.3.4, 2.3.4.5 } counter
+ ip daddr vmap { 5.4.3.0/24 : jump w, 8.9.0.0/24 : jump x }
+ }
+}"
+ $NFT -c -f - <<< $RULESET
+ $NFT -f - <<< $RULESET
+}
+
+load_ruleset "inet"
+load_ruleset "netdev"
diff --git a/tests/shell/testcases/chains/netdev_multidev_netns_gone b/tests/shell/testcases/chains/netdev_multidev_netns_gone
new file mode 100755
index 00000000..31ab29bd
--- /dev/null
+++ b/tests/shell/testcases/chains/netdev_multidev_netns_gone
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_chain_binding)
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_netdev_chain_multidevice)
+
+set -e
+
+rnd=$(mktemp -u XXXXXXXX)
+ns1="nft1ns-$rnd"
+
+iface_cleanup() {
+ ip netns del $ns1 &>/dev/null || :
+}
+trap 'iface_cleanup' EXIT
+
+load_ruleset() {
+ family=$1
+
+ ip netns add $ns1
+ ip -net $ns1 link add d0 type dummy
+ ip -net $ns1 link add d1 type dummy
+ ip -net $ns1 link add d2 type dummy
+
+ # Test auto-removal of chain hook on device removal
+ RULESET="table $family x {
+ chain x {}
+ chain w {
+ ip daddr 8.7.6.0/24 jump {
+ ip daddr vmap { 8.7.6.3 : jump x, 8.7.6.4 : jump x }
+ }
+ }
+ chain y {
+ type filter hook ingress devices = { d0, d1, d2 } priority 0;
+ ip saddr { 1.2.3.4, 2.3.4.5 } counter
+ ip daddr vmap { 5.4.3.0/24 : jump w, 8.9.0.0/24 : jump x }
+ }
+}"
+ ip netns exec $ns1 $NFT -f - <<< $RULESET
+ ip netns del $ns1
+}
+
+load_ruleset "inet"
+load_ruleset "netdev"
diff --git a/tests/shell/testcases/chains/netdev_netns_gone b/tests/shell/testcases/chains/netdev_netns_gone
new file mode 100755
index 00000000..3a92c99e
--- /dev/null
+++ b/tests/shell/testcases/chains/netdev_netns_gone
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_inet_ingress)
+
+set -e
+
+rnd=$(mktemp -u XXXXXXXX)
+ns1="nft1ns-$rnd"
+
+iface_cleanup() {
+ ip netns del $ns1 &>/dev/null || :
+}
+trap 'iface_cleanup' EXIT
+
+load_ruleset() {
+ family=$1
+
+ ip netns add $ns1
+ ip -net $ns1 link add d0 type dummy
+
+ RULESET="table $family x {
+ chain x {}
+ chain w {
+ ip daddr 8.7.6.0/24 jump x
+ }
+ chain y {
+ type filter hook ingress device \"d0\" priority 0;
+ ip saddr { 1.2.3.4, 2.3.4.5 } counter
+ ip daddr vmap { 5.4.3.0/24 : jump w, 8.9.0.0/24 : jump x }
+ }
+}"
+ ip netns exec $ns1 $NFT -f - <<< $RULESET
+ ip netns del $ns1
+}
+
+load_ruleset "inet"
+load_ruleset "netdev"
diff --git a/tests/shell/testcases/comments/comments_0 b/tests/shell/testcases/comments/comments_0
new file mode 100755
index 00000000..a50387d6
--- /dev/null
+++ b/tests/shell/testcases/comments/comments_0
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+RULESET="table inet x { # comment
+ # comment 1
+ # comment 2
+ set y { # comment here
+ type ipv4_addr # comment
+ elements = {
+ # 1.1.1.1
+ 2.2.2.2, # comment
+ # more comments
+ 3.3.3.3, # comment
+# comment
+ }
+ # comment
+ }
+
+ # comments are allowed here
+ chain y {
+ # comments are allowed here
+ icmpv6 type {
+ 1, # comments are allowed here
+ 2,
+ } accept
+
+ icmp type {
+# comment
+ 1,
+ # comments also allowed here
+ 2,
+ } accept
+
+ tcp dport {
+ # normal FTP
+ 21,
+ # patched FTP
+ 2121
+ } counter accept
+ }
+}
+"
+
+$NFT -f - <<< "$RULESET"
+
diff --git a/tests/shell/testcases/comments/dumps/comments_0.json-nft b/tests/shell/testcases/comments/dumps/comments_0.json-nft
new file mode 100644
index 00000000..201abd6f
--- /dev/null
+++ b/tests/shell/testcases/comments/dumps/comments_0.json-nft
@@ -0,0 +1,135 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "elem": [
+ "2.2.2.2",
+ "3.3.3.3"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "icmpv6",
+ "field": "type"
+ }
+ },
+ "right": {
+ "set": [
+ "destination-unreachable",
+ "packet-too-big"
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "icmp",
+ "field": "type"
+ }
+ },
+ "right": {
+ "set": [
+ 1,
+ 2
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": {
+ "set": [
+ 21,
+ 2121
+ ]
+ }
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/comments/dumps/comments_0.nft b/tests/shell/testcases/comments/dumps/comments_0.nft
new file mode 100644
index 00000000..82ae510b
--- /dev/null
+++ b/tests/shell/testcases/comments/dumps/comments_0.nft
@@ -0,0 +1,12 @@
+table inet x {
+ set y {
+ type ipv4_addr
+ elements = { 2.2.2.2, 3.3.3.3 }
+ }
+
+ chain y {
+ icmpv6 type { destination-unreachable, packet-too-big } accept
+ icmp type { 1, 2 } accept
+ tcp dport { 21, 2121 } counter packets 0 bytes 0 accept
+ }
+}
diff --git a/tests/shell/testcases/flowtable/0012flowtable_variable_0 b/tests/shell/testcases/flowtable/0012flowtable_variable_0
index 8e334224..9c03820f 100755
--- a/tests/shell/testcases/flowtable/0012flowtable_variable_0
+++ b/tests/shell/testcases/flowtable/0012flowtable_variable_0
@@ -1,7 +1,15 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_flowtable_counter)
+
set -e
+iface_cleanup() {
+ ip link del dummy1 &>/dev/null || :
+}
+trap 'iface_cleanup' EXIT
+iface_cleanup
+
ip link add name dummy1 type dummy
EXPECTED="define if_main = { lo, dummy1 }
diff --git a/tests/shell/testcases/flowtable/0013addafterdelete_0 b/tests/shell/testcases/flowtable/0013addafterdelete_0
index b23ab978..56c9834f 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 6a24c4b9..1ac65104 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/0015destroy_0 b/tests/shell/testcases/flowtable/0015destroy_0
new file mode 100755
index 00000000..cea33524
--- /dev/null
+++ b/tests/shell/testcases/flowtable/0015destroy_0
@@ -0,0 +1,20 @@
+#!/bin/bash -e
+
+# 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
+$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
diff --git a/tests/shell/testcases/flowtable/dumps/0001flowtable_0.json-nft b/tests/shell/testcases/flowtable/dumps/0001flowtable_0.json-nft
new file mode 100644
index 00000000..4d15fe3a
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0001flowtable_0.json-nft
@@ -0,0 +1,53 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "flowtable": {
+ "family": "inet",
+ "name": "f",
+ "table": "t",
+ "handle": 0,
+ "hook": "ingress",
+ "prio": 10,
+ "dev": "lo"
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "flow": {
+ "op": "add",
+ "flowtable": "@f"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0002create_flowtable_0.json-nft b/tests/shell/testcases/flowtable/dumps/0002create_flowtable_0.json-nft
new file mode 100644
index 00000000..0013512b
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0002create_flowtable_0.json-nft
@@ -0,0 +1,29 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "flowtable": {
+ "family": "ip",
+ "name": "f",
+ "table": "t",
+ "handle": 0,
+ "hook": "ingress",
+ "prio": 10,
+ "dev": "lo"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0002create_flowtable_0.nft b/tests/shell/testcases/flowtable/dumps/0002create_flowtable_0.nft
new file mode 100644
index 00000000..aecfb2ab
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0002create_flowtable_0.nft
@@ -0,0 +1,6 @@
+table ip t {
+ flowtable f {
+ hook ingress priority filter + 10
+ devices = { lo }
+ }
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0003add_after_flush_0.json-nft b/tests/shell/testcases/flowtable/dumps/0003add_after_flush_0.json-nft
new file mode 100644
index 00000000..04057f1f
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0003add_after_flush_0.json-nft
@@ -0,0 +1,29 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "flowtable": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "handle": 0,
+ "hook": "ingress",
+ "prio": 0,
+ "dev": "lo"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0003add_after_flush_0.nft b/tests/shell/testcases/flowtable/dumps/0003add_after_flush_0.nft
new file mode 100644
index 00000000..dd904f44
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0003add_after_flush_0.nft
@@ -0,0 +1,6 @@
+table ip x {
+ flowtable y {
+ hook ingress priority filter
+ devices = { lo }
+ }
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0004delete_after_add_0.json-nft b/tests/shell/testcases/flowtable/dumps/0004delete_after_add_0.json-nft
new file mode 100644
index 00000000..15ec0aac
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0004delete_after_add_0.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0004delete_after_add_0.nft b/tests/shell/testcases/flowtable/dumps/0004delete_after_add_0.nft
new file mode 100644
index 00000000..5d4d2caf
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0004delete_after_add_0.nft
@@ -0,0 +1,2 @@
+table ip x {
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0005delete_in_use_1.json-nft b/tests/shell/testcases/flowtable/dumps/0005delete_in_use_1.json-nft
new file mode 100644
index 00000000..302502dc
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0005delete_in_use_1.json-nft
@@ -0,0 +1,53 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "flowtable": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "handle": 0,
+ "hook": "ingress",
+ "prio": 0,
+ "dev": "lo"
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "x",
+ "handle": 0,
+ "expr": [
+ {
+ "flow": {
+ "op": "add",
+ "flowtable": "@y"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0005delete_in_use_1.nft b/tests/shell/testcases/flowtable/dumps/0005delete_in_use_1.nft
new file mode 100644
index 00000000..c1d79e7b
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0005delete_in_use_1.nft
@@ -0,0 +1,10 @@
+table ip x {
+ flowtable y {
+ hook ingress priority filter
+ devices = { lo }
+ }
+
+ chain x {
+ flow add @y
+ }
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0006segfault_0.json-nft b/tests/shell/testcases/flowtable/dumps/0006segfault_0.json-nft
new file mode 100644
index 00000000..e0e56fec
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0006segfault_0.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0006segfault_0.nft b/tests/shell/testcases/flowtable/dumps/0006segfault_0.nft
new file mode 100644
index 00000000..985768ba
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0006segfault_0.nft
@@ -0,0 +1,2 @@
+table ip t {
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0007prio_0.json-nft b/tests/shell/testcases/flowtable/dumps/0007prio_0.json-nft
new file mode 100644
index 00000000..e0e56fec
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0007prio_0.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0007prio_0.nft b/tests/shell/testcases/flowtable/dumps/0007prio_0.nft
new file mode 100644
index 00000000..985768ba
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0007prio_0.nft
@@ -0,0 +1,2 @@
+table ip t {
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0008prio_1.json-nft b/tests/shell/testcases/flowtable/dumps/0008prio_1.json-nft
new file mode 100644
index 00000000..e0e56fec
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0008prio_1.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0008prio_1.nft b/tests/shell/testcases/flowtable/dumps/0008prio_1.nft
new file mode 100644
index 00000000..985768ba
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0008prio_1.nft
@@ -0,0 +1,2 @@
+table ip t {
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0009deleteafterflush_0.json-nft b/tests/shell/testcases/flowtable/dumps/0009deleteafterflush_0.json-nft
new file mode 100644
index 00000000..b6088c80
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0009deleteafterflush_0.json-nft
@@ -0,0 +1,26 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0009deleteafterflush_0.nft b/tests/shell/testcases/flowtable/dumps/0009deleteafterflush_0.nft
new file mode 100644
index 00000000..8e818d2d
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0009deleteafterflush_0.nft
@@ -0,0 +1,4 @@
+table ip x {
+ chain y {
+ }
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0010delete_handle_0.json-nft b/tests/shell/testcases/flowtable/dumps/0010delete_handle_0.json-nft
new file mode 100644
index 00000000..10372b0e
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0010delete_handle_0.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "t",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0010delete_handle_0.nft b/tests/shell/testcases/flowtable/dumps/0010delete_handle_0.nft
new file mode 100644
index 00000000..17838bdf
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0010delete_handle_0.nft
@@ -0,0 +1,2 @@
+table inet t {
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0011deleteafterflush_0.json-nft b/tests/shell/testcases/flowtable/dumps/0011deleteafterflush_0.json-nft
new file mode 100644
index 00000000..b6088c80
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0011deleteafterflush_0.json-nft
@@ -0,0 +1,26 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0011deleteafterflush_0.nft b/tests/shell/testcases/flowtable/dumps/0011deleteafterflush_0.nft
new file mode 100644
index 00000000..8e818d2d
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0011deleteafterflush_0.nft
@@ -0,0 +1,4 @@
+table ip x {
+ chain y {
+ }
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.json-nft b/tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.json-nft
new file mode 100644
index 00000000..10f1df98
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.json-nft
@@ -0,0 +1,47 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "filter1",
+ "handle": 0
+ }
+ },
+ {
+ "flowtable": {
+ "family": "ip",
+ "name": "Main_ft1",
+ "table": "filter1",
+ "handle": 0,
+ "hook": "ingress",
+ "prio": 0,
+ "dev": "lo"
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "filter2",
+ "handle": 0
+ }
+ },
+ {
+ "flowtable": {
+ "family": "ip",
+ "name": "Main_ft2",
+ "table": "filter2",
+ "handle": 0,
+ "hook": "ingress",
+ "prio": 0,
+ "dev": "lo"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.nft b/tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.nft
index 1cbb2f11..df1c51a2 100644
--- a/tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.nft
+++ b/tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.nft
@@ -1,14 +1,14 @@
table ip filter1 {
flowtable Main_ft1 {
hook ingress priority filter
- devices = { dummy1, lo }
+ devices = { lo }
counter
}
}
table ip filter2 {
flowtable Main_ft2 {
hook ingress priority filter
- devices = { dummy1, lo }
+ devices = { lo }
counter
}
}
diff --git a/tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.json-nft b/tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.json-nft
new file mode 100644
index 00000000..85c7b327
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.json-nft
@@ -0,0 +1,29 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "flowtable": {
+ "family": "inet",
+ "name": "f",
+ "table": "filter",
+ "handle": 0,
+ "hook": "ingress",
+ "prio": -1,
+ "dev": "lo"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft b/tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft
new file mode 100644
index 00000000..67db7d02
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft
@@ -0,0 +1,6 @@
+table inet filter {
+ flowtable f {
+ hook ingress priority filter - 1
+ devices = { lo }
+ }
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0014addafterdelete_0.json-nft b/tests/shell/testcases/flowtable/dumps/0014addafterdelete_0.json-nft
new file mode 100644
index 00000000..471ba5be
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0014addafterdelete_0.json-nft
@@ -0,0 +1,63 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "y",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "flowtable": {
+ "family": "inet",
+ "name": "f",
+ "table": "filter",
+ "handle": 0,
+ "hook": "ingress",
+ "prio": -1,
+ "dev": "lo"
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "flow": {
+ "op": "add",
+ "flowtable": "@f"
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0014addafterdelete_0.nft b/tests/shell/testcases/flowtable/dumps/0014addafterdelete_0.nft
new file mode 100644
index 00000000..145aa081
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0014addafterdelete_0.nft
@@ -0,0 +1,12 @@
+table inet filter {
+ flowtable f {
+ hook ingress priority filter - 1
+ devices = { lo }
+ counter
+ }
+
+ chain y {
+ type filter hook forward priority filter; policy accept;
+ flow add @f counter packets 0 bytes 0
+ }
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0015destroy_0.json-nft b/tests/shell/testcases/flowtable/dumps/0015destroy_0.json-nft
new file mode 100644
index 00000000..e0e56fec
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0015destroy_0.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/flowtable/dumps/0015destroy_0.nft b/tests/shell/testcases/flowtable/dumps/0015destroy_0.nft
new file mode 100644
index 00000000..985768ba
--- /dev/null
+++ b/tests/shell/testcases/flowtable/dumps/0015destroy_0.nft
@@ -0,0 +1,2 @@
+table ip t {
+}
diff --git a/tests/shell/testcases/include/0003includepath_0 b/tests/shell/testcases/include/0003includepath_0
index ba722068..20037a8f 100755
--- a/tests/shell/testcases/include/0003includepath_0
+++ b/tests/shell/testcases/include/0003includepath_0
@@ -8,7 +8,7 @@ if [ ! -w $tmpfile1 ] ; then
exit 0
fi
-tmpfile3=$(echo "$tmpfile1" | cut -d'/' -f 3)
+tmpfile3="$(basename "$tmpfile1")"
tmpfile2=$(mktemp)
if [ ! -w $tmpfile2 ] ; then
@@ -24,7 +24,7 @@ RULESET2="include \"$tmpfile3\""
echo "$RULESET1" > $tmpfile1
echo "$RULESET2" > $tmpfile2
-$NFT -I /tmp -f $tmpfile2
+$NFT -I "$(dirname "$tmpfile1")" -f $tmpfile2
if [ $? -ne 0 ] ; then
echo "E: unable to load good ruleset" >&2
exit 1
diff --git a/tests/shell/testcases/include/0020include_chain_0 b/tests/shell/testcases/include/0020include_chain_0
new file mode 100755
index 00000000..49b6f76c
--- /dev/null
+++ b/tests/shell/testcases/include/0020include_chain_0
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+set -e
+
+tmpfile1=$(mktemp -p .)
+if [ ! -w $tmpfile1 ] ; then
+ echo "Failed to create tmp file" >&2
+ exit 0
+fi
+
+trap "rm -rf $tmpfile1" EXIT # cleanup if aborted
+
+RULESET="table inet filter { }
+include \"$tmpfile1\""
+
+RULESET2="chain inet filter input2 {
+ type filter hook input priority filter; policy accept;
+ ip saddr 1.2.3.4 tcp dport { 22, 443, 123 } drop
+}"
+
+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/0001absolute_0.json-nft b/tests/shell/testcases/include/dumps/0001absolute_0.json-nft
new file mode 100644
index 00000000..15ec0aac
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0001absolute_0.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/include/dumps/0002relative_0.json-nft b/tests/shell/testcases/include/dumps/0002relative_0.json-nft
new file mode 100644
index 00000000..15ec0aac
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0002relative_0.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/include/dumps/0003includepath_0.json-nft b/tests/shell/testcases/include/dumps/0003includepath_0.json-nft
new file mode 100644
index 00000000..15ec0aac
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0003includepath_0.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/include/dumps/0004endlessloop_1.json-nft b/tests/shell/testcases/include/dumps/0004endlessloop_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0004endlessloop_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/include/dumps/0004endlessloop_1.nft b/tests/shell/testcases/include/dumps/0004endlessloop_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0004endlessloop_1.nft
diff --git a/tests/shell/testcases/include/dumps/0005glob_empty_0.json-nft b/tests/shell/testcases/include/dumps/0005glob_empty_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0005glob_empty_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/include/dumps/0005glob_empty_0.nft b/tests/shell/testcases/include/dumps/0005glob_empty_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0005glob_empty_0.nft
diff --git a/tests/shell/testcases/include/dumps/0006glob_single_0.json-nft b/tests/shell/testcases/include/dumps/0006glob_single_0.json-nft
new file mode 100644
index 00000000..15ec0aac
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0006glob_single_0.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/include/dumps/0007glob_double_0.json-nft b/tests/shell/testcases/include/dumps/0007glob_double_0.json-nft
new file mode 100644
index 00000000..ea75b43f
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0007glob_double_0.json-nft
@@ -0,0 +1,25 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "y",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/include/dumps/0008glob_nofile_wildcard_0.json-nft b/tests/shell/testcases/include/dumps/0008glob_nofile_wildcard_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0008glob_nofile_wildcard_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/include/dumps/0008glob_nofile_wildcard_0.nft b/tests/shell/testcases/include/dumps/0008glob_nofile_wildcard_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0008glob_nofile_wildcard_0.nft
diff --git a/tests/shell/testcases/include/dumps/0009glob_nofile_1.json-nft b/tests/shell/testcases/include/dumps/0009glob_nofile_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0009glob_nofile_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/include/dumps/0009glob_nofile_1.nft b/tests/shell/testcases/include/dumps/0009glob_nofile_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0009glob_nofile_1.nft
diff --git a/tests/shell/testcases/include/dumps/0010glob_broken_file_1.json-nft b/tests/shell/testcases/include/dumps/0010glob_broken_file_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0010glob_broken_file_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/include/dumps/0010glob_broken_file_1.nft b/tests/shell/testcases/include/dumps/0010glob_broken_file_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0010glob_broken_file_1.nft
diff --git a/tests/shell/testcases/include/dumps/0011glob_dependency_0.json-nft b/tests/shell/testcases/include/dumps/0011glob_dependency_0.json-nft
new file mode 100644
index 00000000..b6088c80
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0011glob_dependency_0.json-nft
@@ -0,0 +1,26 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/include/dumps/0012glob_dependency_1.json-nft b/tests/shell/testcases/include/dumps/0012glob_dependency_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0012glob_dependency_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/include/dumps/0012glob_dependency_1.nft b/tests/shell/testcases/include/dumps/0012glob_dependency_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0012glob_dependency_1.nft
diff --git a/tests/shell/testcases/include/dumps/0013glob_dotfile_0.json-nft b/tests/shell/testcases/include/dumps/0013glob_dotfile_0.json-nft
new file mode 100644
index 00000000..15ec0aac
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0013glob_dotfile_0.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/include/dumps/0013input_descriptors_included_files_0.json-nft b/tests/shell/testcases/include/dumps/0013input_descriptors_included_files_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0013input_descriptors_included_files_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/include/dumps/0013input_descriptors_included_files_0.nft b/tests/shell/testcases/include/dumps/0013input_descriptors_included_files_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0013input_descriptors_included_files_0.nft
diff --git a/tests/shell/testcases/include/dumps/0014glob_directory_0.json-nft b/tests/shell/testcases/include/dumps/0014glob_directory_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0014glob_directory_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/include/dumps/0014glob_directory_0.nft b/tests/shell/testcases/include/dumps/0014glob_directory_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0014glob_directory_0.nft
diff --git a/tests/shell/testcases/include/dumps/0015doubleincludepath_0.json-nft b/tests/shell/testcases/include/dumps/0015doubleincludepath_0.json-nft
new file mode 100644
index 00000000..b6088c80
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0015doubleincludepath_0.json-nft
@@ -0,0 +1,26 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/include/dumps/0016maxdepth_0.json-nft b/tests/shell/testcases/include/dumps/0016maxdepth_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0016maxdepth_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/include/dumps/0016maxdepth_0.nft b/tests/shell/testcases/include/dumps/0016maxdepth_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0016maxdepth_0.nft
diff --git a/tests/shell/testcases/include/dumps/0017glob_more_than_maxdepth_1.json-nft b/tests/shell/testcases/include/dumps/0017glob_more_than_maxdepth_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0017glob_more_than_maxdepth_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/include/dumps/0017glob_more_than_maxdepth_1.nft b/tests/shell/testcases/include/dumps/0017glob_more_than_maxdepth_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0017glob_more_than_maxdepth_1.nft
diff --git a/tests/shell/testcases/include/dumps/0018include_error_0.json-nft b/tests/shell/testcases/include/dumps/0018include_error_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0018include_error_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/include/dumps/0018include_error_0.nft b/tests/shell/testcases/include/dumps/0018include_error_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0018include_error_0.nft
diff --git a/tests/shell/testcases/include/dumps/0019include_error_0.json-nft b/tests/shell/testcases/include/dumps/0019include_error_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0019include_error_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/include/dumps/0019include_error_0.nft b/tests/shell/testcases/include/dumps/0019include_error_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0019include_error_0.nft
diff --git a/tests/shell/testcases/include/dumps/0020include_chain_0.json-nft b/tests/shell/testcases/include/dumps/0020include_chain_0.json-nft
new file mode 100644
index 00000000..e893ccf1
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0020include_chain_0.json-nft
@@ -0,0 +1,128 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "input2",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "output2",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "input2",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "1.2.3.4"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": {
+ "set": [
+ 22,
+ 123,
+ 443
+ ]
+ }
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "output2",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "right": "1.2.3.4"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": {
+ "set": [
+ 22,
+ 123,
+ 443
+ ]
+ }
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/include/dumps/0020include_chain_0.nft b/tests/shell/testcases/include/dumps/0020include_chain_0.nft
new file mode 100644
index 00000000..bf596ffb
--- /dev/null
+++ b/tests/shell/testcases/include/dumps/0020include_chain_0.nft
@@ -0,0 +1,11 @@
+table inet filter {
+ chain input2 {
+ 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/json/0001set_statements_0 b/tests/shell/testcases/json/0001set_statements_0
new file mode 100755
index 00000000..fc4941f4
--- /dev/null
+++ b/tests/shell/testcases/json/0001set_statements_0
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_json)
+
+set -e
+
+$NFT flush ruleset
+
+RULESET='{"nftables": [{"metainfo": {"version": "1.0.5", "release_name": "Lester Gooch #4", "json_schema_version": 1}}, {"table": {"family": "ip", "name": "testt", "handle": 3}}, {"set": {"family": "ip", "name": "ssh_meter", "table": "testt", "type": "ipv4_addr", "handle": 2, "size": 65535}}, {"chain": {"family": "ip", "table": "testt", "name": "testc", "handle": 1, "type": "filter", "hook": "input", "prio": 0, "policy": "accept"}}, {"rule": {"family": "ip", "table": "testt", "chain": "testc", "handle": 3, "expr": [{"match": {"op": "==", "left": {"payload": {"protocol": "tcp", "field": "dport"}}, "right": 22}}, {"match": {"op": "in", "left": {"ct": {"key": "state"}}, "right": "new"}}, {"set": {"op": "add", "elem": {"payload": {"protocol": "ip", "field": "saddr"}}, "stmt": [{"limit": {"rate": 10, "burst": 5, "per": "second"}}], "set": "@ssh_meter"}}, {"accept": null}]}}]}'
+
+$NFT -j -f - <<< $RULESET
diff --git a/tests/shell/testcases/json/0002table_map_0 b/tests/shell/testcases/json/0002table_map_0
new file mode 100755
index 00000000..a1e9f263
--- /dev/null
+++ b/tests/shell/testcases/json/0002table_map_0
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_json)
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_set_expr)
+
+set -e
+
+$NFT flush ruleset
+
+RULESET='{"nftables": [{"metainfo": {"version": "1.0.5", "release_name": "Lester Gooch #4", "json_schema_version": 1}}, {"table": {"family": "ip", "name": "t", "handle": 4}}, {"map": {"family": "ip", "name": "m", "table": "t", "type": "ipv4_addr", "handle": 1, "map": "mark", "stmt": [{"counter": {"packets": 0, "bytes": 0}}]}}]}'
+
+$NFT -j -f - <<< $RULESET
diff --git a/tests/shell/testcases/json/0003json_schema_version_0 b/tests/shell/testcases/json/0003json_schema_version_0
new file mode 100755
index 00000000..43f387a1
--- /dev/null
+++ b/tests/shell/testcases/json/0003json_schema_version_0
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_json)
+
+set -e
+
+$NFT flush ruleset
+
+RULESET='{"nftables": [{"metainfo": {"json_schema_version": 1}}]}'
+
+$NFT -j -f - <<< $RULESET
diff --git a/tests/shell/testcases/json/0004json_schema_version_1 b/tests/shell/testcases/json/0004json_schema_version_1
new file mode 100755
index 00000000..0f8d586f
--- /dev/null
+++ b/tests/shell/testcases/json/0004json_schema_version_1
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_json)
+
+set -e
+
+$NFT flush ruleset
+
+RULESET='{"nftables": [{"metainfo": {"json_schema_version": 999}}]}'
+
+$NFT -j -f - <<< $RULESET && exit 1
+
+exit 0
diff --git a/tests/shell/testcases/json/0005secmark_objref_0 b/tests/shell/testcases/json/0005secmark_objref_0
new file mode 100755
index 00000000..5c44f093
--- /dev/null
+++ b/tests/shell/testcases/json/0005secmark_objref_0
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_json)
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_secmark)
+
+set -e
+
+$NFT flush ruleset
+
+RULESET='{"nftables": [{"metainfo": {"version": "1.0.5", "release_name": "Lester Gooch #4", "json_schema_version": 1}}, {"table": {"family": "inet", "name": "x", "handle": 4}}, {"secmark": {"family": "inet", "name": "ssh_server", "table": "x", "handle": 1, "context": "system_u:object_r:ssh_server_packet_t:s0"}}, {"chain": {"family": "inet", "table": "x", "name": "y", "handle": 2, "type": "filter", "hook": "input", "prio": -225, "policy": "accept"}}, {"chain": {"family": "inet", "table": "x", "name": "z", "handle": 3, "type": "filter", "hook": "output", "prio": 225, "policy": "accept"}}, {"rule": {"family": "inet", "table": "x", "chain": "y", "handle": 4, "expr": [{"match": {"op": "==", "left": {"payload": {"protocol": "tcp", "field": "dport"}}, "right": 2222}}, {"match": {"op": "in", "left": {"ct": {"key": "state"}}, "right": "new"}}, {"secmark": "ssh_server"}]}}, {"rule": {"family": "inet", "table": "x", "chain": "y", "handle": 5, "expr": [{"match": {"op": "in", "left": {"ct": {"key": "state"}}, "right": "new"}}, {"mangle": {"key": {"ct": {"key": "secmark"}}, "value": {"meta": {"key": "secmark"}}}}]}}, {"rule": {"family": "inet", "table": "x", "chain": "y", "handle": 6, "expr": [{"match": {"op": "in", "left": {"ct": {"key": "state"}}, "right": ["established", "related"]}}, {"mangle": {"key": {"meta": {"key": "secmark"}}, "value": {"ct": {"key": "secmark"}}}}]}}, {"rule": {"family": "inet", "table": "x", "chain": "z", "handle": 7, "expr": [{"match": {"op": "in", "left": {"ct": {"key": "state"}}, "right": "new"}}, {"mangle": {"key": {"ct": {"key": "secmark"}}, "value": {"meta": {"key": "secmark"}}}}]}}, {"rule": {"family": "inet", "table": "x", "chain": "z", "handle": 8, "expr": [{"match": {"op": "in", "left": {"ct": {"key": "state"}}, "right": ["established", "related"]}}, {"mangle": {"key": {"meta": {"key": "secmark"}}, "value": {"ct": {"key": "secmark"}}}}]}}]}'
+
+$NFT -j -f - <<< $RULESET
diff --git a/tests/shell/testcases/json/0006obj_comment_0 b/tests/shell/testcases/json/0006obj_comment_0
new file mode 100755
index 00000000..7ce859d2
--- /dev/null
+++ b/tests/shell/testcases/json/0006obj_comment_0
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_json)
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_comment)
+
+set -e
+
+$NFT flush ruleset
+
+RULESET='{"nftables": [{"metainfo": {"version": "1.0.5", "release_name": "Lester Gooch #4", "json_schema_version": 1}}, {"table": {"family": "inet", "name": "t", "handle": 9}}, {"counter": {"family": "inet", "name": "mycounter", "table": "t", "handle": 1, "comment": "my comment in counter", "packets": 0, "bytes": 0}}]}'
+
+$NFT -j -f - <<< $RULESET
diff --git a/tests/shell/testcases/json/dumps/0001set_statements_0.json-nft b/tests/shell/testcases/json/dumps/0001set_statements_0.json-nft
new file mode 100644
index 00000000..91db43e2
--- /dev/null
+++ b/tests/shell/testcases/json/dumps/0001set_statements_0.json-nft
@@ -0,0 +1,100 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "testt",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "testt",
+ "name": "testc",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "ssh_meter",
+ "table": "testt",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 65535,
+ "flags": [
+ "dynamic"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "testt",
+ "chain": "testc",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 22
+ }
+ },
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": "new"
+ }
+ },
+ {
+ "set": {
+ "op": "add",
+ "elem": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "set": "@ssh_meter",
+ "stmt": [
+ {
+ "limit": {
+ "rate": 10,
+ "burst": 5,
+ "per": "second"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/json/dumps/0001set_statements_0.nft b/tests/shell/testcases/json/dumps/0001set_statements_0.nft
new file mode 100644
index 00000000..d80a4321
--- /dev/null
+++ b/tests/shell/testcases/json/dumps/0001set_statements_0.nft
@@ -0,0 +1,12 @@
+table ip testt {
+ set ssh_meter {
+ type ipv4_addr
+ size 65535
+ flags dynamic
+ }
+
+ chain testc {
+ type filter hook input priority filter; policy accept;
+ tcp dport 22 ct state new add @ssh_meter { ip saddr limit rate 10/second burst 5 packets } accept
+ }
+}
diff --git a/tests/shell/testcases/json/dumps/0002table_map_0.json-nft b/tests/shell/testcases/json/dumps/0002table_map_0.json-nft
new file mode 100644
index 00000000..78e3c8ad
--- /dev/null
+++ b/tests/shell/testcases/json/dumps/0002table_map_0.json-nft
@@ -0,0 +1,33 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "m",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "mark",
+ "stmt": [
+ {
+ "counter": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/json/dumps/0002table_map_0.nft b/tests/shell/testcases/json/dumps/0002table_map_0.nft
new file mode 100644
index 00000000..357e92cc
--- /dev/null
+++ b/tests/shell/testcases/json/dumps/0002table_map_0.nft
@@ -0,0 +1,6 @@
+table ip t {
+ map m {
+ type ipv4_addr : mark
+ counter
+ }
+}
diff --git a/tests/shell/testcases/json/dumps/0003json_schema_version_0.json-nft b/tests/shell/testcases/json/dumps/0003json_schema_version_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/json/dumps/0003json_schema_version_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/json/dumps/0003json_schema_version_0.nft b/tests/shell/testcases/json/dumps/0003json_schema_version_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/json/dumps/0003json_schema_version_0.nft
diff --git a/tests/shell/testcases/json/dumps/0004json_schema_version_1.json-nft b/tests/shell/testcases/json/dumps/0004json_schema_version_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/json/dumps/0004json_schema_version_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/json/dumps/0004json_schema_version_1.nft b/tests/shell/testcases/json/dumps/0004json_schema_version_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/json/dumps/0004json_schema_version_1.nft
diff --git a/tests/shell/testcases/json/dumps/0005secmark_objref_0.json-nft b/tests/shell/testcases/json/dumps/0005secmark_objref_0.json-nft
new file mode 100644
index 00000000..3783c6b7
--- /dev/null
+++ b/tests/shell/testcases/json/dumps/0005secmark_objref_0.json-nft
@@ -0,0 +1,233 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "y",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -225,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "z",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 225,
+ "policy": "accept"
+ }
+ },
+ {
+ "secmark": {
+ "family": "inet",
+ "name": "ssh_server",
+ "table": "x",
+ "handle": 0,
+ "context": "system_u:object_r:ssh_server_packet_t:s0"
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 2222
+ }
+ },
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": "new"
+ }
+ },
+ {
+ "secmark": "ssh_server"
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": "new"
+ }
+ },
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "secmark"
+ }
+ },
+ "value": {
+ "meta": {
+ "key": "secmark"
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": [
+ "established",
+ "related"
+ ]
+ }
+ },
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "secmark"
+ }
+ },
+ "value": {
+ "ct": {
+ "key": "secmark"
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "z",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": "new"
+ }
+ },
+ {
+ "mangle": {
+ "key": {
+ "ct": {
+ "key": "secmark"
+ }
+ },
+ "value": {
+ "meta": {
+ "key": "secmark"
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "z",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": [
+ "established",
+ "related"
+ ]
+ }
+ },
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "secmark"
+ }
+ },
+ "value": {
+ "ct": {
+ "key": "secmark"
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/json/dumps/0005secmark_objref_0.nft b/tests/shell/testcases/json/dumps/0005secmark_objref_0.nft
new file mode 100644
index 00000000..4c218e93
--- /dev/null
+++ b/tests/shell/testcases/json/dumps/0005secmark_objref_0.nft
@@ -0,0 +1,18 @@
+table inet x {
+ secmark ssh_server {
+ "system_u:object_r:ssh_server_packet_t:s0"
+ }
+
+ chain y {
+ type filter hook input priority -225; policy accept;
+ tcp dport 2222 ct state new meta secmark set "ssh_server"
+ ct state new ct secmark set meta secmark
+ ct state established,related meta secmark set ct secmark
+ }
+
+ chain z {
+ type filter hook output priority 225; policy accept;
+ ct state new ct secmark set meta secmark
+ ct state established,related meta secmark set ct secmark
+ }
+}
diff --git a/tests/shell/testcases/json/dumps/0006obj_comment_0.json-nft b/tests/shell/testcases/json/dumps/0006obj_comment_0.json-nft
new file mode 100644
index 00000000..208e13ad
--- /dev/null
+++ b/tests/shell/testcases/json/dumps/0006obj_comment_0.json-nft
@@ -0,0 +1,29 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "counter": {
+ "family": "inet",
+ "name": "mycounter",
+ "table": "t",
+ "handle": 0,
+ "comment": "my comment in counter",
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/json/dumps/0006obj_comment_0.nft b/tests/shell/testcases/json/dumps/0006obj_comment_0.nft
new file mode 100644
index 00000000..e52b21b4
--- /dev/null
+++ b/tests/shell/testcases/json/dumps/0006obj_comment_0.nft
@@ -0,0 +1,6 @@
+table inet t {
+ counter mycounter {
+ comment "my comment in counter"
+ packets 0 bytes 0
+ }
+}
diff --git a/tests/shell/testcases/json/dumps/netdev.json-nft b/tests/shell/testcases/json/dumps/netdev.json-nft
new file mode 100644
index 00000000..e0d2bfb4
--- /dev/null
+++ b/tests/shell/testcases/json/dumps/netdev.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "netdev",
+ "name": "test_table",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/json/dumps/netdev.nft b/tests/shell/testcases/json/dumps/netdev.nft
new file mode 100644
index 00000000..3c568ed3
--- /dev/null
+++ b/tests/shell/testcases/json/dumps/netdev.nft
@@ -0,0 +1,2 @@
+table netdev test_table {
+}
diff --git a/tests/shell/testcases/json/netdev b/tests/shell/testcases/json/netdev
new file mode 100755
index 00000000..8c16cf42
--- /dev/null
+++ b/tests/shell/testcases/json/netdev
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+set -e
+
+iface_cleanup() {
+ ip link del d0 &>/dev/null || :
+}
+trap 'iface_cleanup' EXIT
+iface_cleanup
+
+ip link add d0 type dummy
+
+$NFT flush ruleset
+$NFT add table inet test
+$NFT add chain inet test c
+
+$NFT flush ruleset
+
+RULESET='{"nftables":[{"flush":{"ruleset":null}},{"add":{"table":{"family":"netdev","name":"test_table"}}},{"add":{"chain":{"family":"netdev","table":"test_table","name":"test_chain","type":"filter","hook":"ingress","prio":0,"dev":"d0","policy":"accept"}}}]}'
+
+if [ "$NFT_TEST_HAVE_json" != n ]; then
+ $NFT -j -f - <<< $RULESET
+fi
+
+if [ "$NFT_TEST_HAVE_json" = n ]; then
+ echo "Test partially skipped due to missing JSON support."
+ exit 77
+fi
diff --git a/tests/shell/testcases/listing/0013objects_0 b/tests/shell/testcases/listing/0013objects_0
index 4d39143d..c78ada94 100755
--- a/tests/shell/testcases/listing/0013objects_0
+++ b/tests/shell/testcases/listing/0013objects_0
@@ -1,47 +1,23 @@
#!/bin/bash
-# list table with all objects and chains
-
-EXPECTED="table ip test {
- quota https-quota {
- 25 mbytes
- }
-
- ct helper cthelp {
- type \"sip\" protocol tcp
- l3proto ip
- }
-
- ct timeout cttime {
- protocol udp
- l3proto ip
- policy = { unreplied : 15, replied : 12 }
- }
-
- ct expectation ctexpect {
- protocol tcp
- dport 5432
- timeout 1h
- size 12
- l3proto ip
- }
-
- chain input {
- }
-}"
-
set -e
$NFT add table test
$NFT add chain test input
$NFT add quota test https-quota 25 mbytes
$NFT add ct helper test cthelp { type \"sip\" protocol tcp \; }
-$NFT add ct timeout test cttime { protocol udp \; policy = {replied : 12, unreplied : 15 } \; }
-$NFT add ct expectation test ctexpect { protocol tcp \; dport 5432 \; timeout 1h \; size 12 \; }
-$NFT add table test-ip
+if [ "$NFT_TEST_HAVE_cttimeout" != n ] ; then
+ $NFT add ct timeout test cttime { protocol udp \; policy = {replied : 12, unreplied : 15 } \; }
+fi
+if [ "$NFT_TEST_HAVE_ctexpect" != n ] ; then
+ $NFT add ct expectation test ctexpect { protocol tcp \; dport 5432 \; timeout 1h \; size 12 \; }
+fi
-GET="$($NFT list table test)"
-if [ "$EXPECTED" != "$GET" ] ; then
- $DIFF -u <(echo "$EXPECTED") <(echo "$GET")
- exit 1
+if [ "$NFT_TEST_HAVE_cttimeout" = n ] ; then
+ echo "Ran partial test due to NFT_TEST_HAVE_cttimeout=n (skipped)"
+ exit 77
+fi
+if [ "$NFT_TEST_HAVE_ctexpect" = n ] ; then
+ echo "Ran partial test due to NFT_TEST_HAVE_ctexpect=n (skipped)"
+ exit 77
fi
diff --git a/tests/shell/testcases/listing/0020flowtable_0 b/tests/shell/testcases/listing/0020flowtable_0
index 2f0a98d1..0e89f5dd 100755
--- a/tests/shell/testcases/listing/0020flowtable_0
+++ b/tests/shell/testcases/listing/0020flowtable_0
@@ -1,20 +1,65 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_flowtable_no_devices)
+
# list only the flowtable asked for with table
+set -e
+
+FLOWTABLES="flowtable f {
+ hook ingress priority filter
+ devices = { lo }
+}
+flowtable f2 {
+ hook ingress priority filter
+ devices = { d0 }
+}"
+
+RULESET="table inet filter {
+ $FLOWTABLES
+}
+table ip filter {
+ $FLOWTABLES
+}"
+
EXPECTED="table inet filter {
flowtable f {
hook ingress priority filter
devices = { lo }
}
}"
+EXPECTED2="table ip filter {
+ flowtable f2 {
+ hook ingress priority filter
+ devices = { d0 }
+ }
+}"
+EXPECTED3="table ip filter {
+ flowtable f {
+ hook ingress priority filter
+ devices = { lo }
+ }
+ flowtable f2 {
+ hook ingress priority filter
+ devices = { d0 }
+ }
+}"
-set -e
+iface_cleanup() {
+ ip link del d0 &>/dev/null || :
+}
+trap 'iface_cleanup' EXIT
+iface_cleanup
+
+ip link add d0 type dummy
-$NFT -f - <<< "$EXPECTED"
+$NFT -f - <<< "$RULESET"
GET="$($NFT list flowtable inet filter f)"
-if [ "$EXPECTED" != "$GET" ] ; then
- $DIFF -u <(echo "$EXPECTED") <(echo "$GET")
- exit 1
-fi
+$DIFF -u <(echo "$EXPECTED") <(echo "$GET")
+
+GET="$($NFT list flowtable ip filter f2)"
+$DIFF -u <(echo "$EXPECTED2") <(echo "$GET")
+
+GET="$($NFT list flowtables ip)"
+$DIFF -u <(echo "$EXPECTED3") <(echo "$GET")
diff --git a/tests/shell/testcases/listing/0021ruleset_json_terse_0 b/tests/shell/testcases/listing/0021ruleset_json_terse_0
index c739ac3f..98a7ce8a 100755
--- a/tests/shell/testcases/listing/0021ruleset_json_terse_0
+++ b/tests/shell/testcases/listing/0021ruleset_json_terse_0
@@ -6,7 +6,14 @@ $NFT add chain ip test c
$NFT add set ip test s { type ipv4_addr\; }
$NFT add element ip test s { 192.168.3.4, 192.168.3.5 }
-if $NFT -j -t list ruleset | grep '192'
-then
- exit 1
+if [ "$NFT_TEST_HAVE_json" != n ]; then
+ if $NFT -j -t list ruleset | grep '192\.168'
+ then
+ exit 1
+ fi
+fi
+
+if [ "$NFT_TEST_HAVE_json" = n ]; then
+ echo "Test partially skipped due to missing JSON support."
+ exit 77
fi
diff --git a/tests/shell/testcases/listing/0022terse_0 b/tests/shell/testcases/listing/0022terse_0
index 14d31875..4841771c 100755
--- a/tests/shell/testcases/listing/0022terse_0
+++ b/tests/shell/testcases/listing/0022terse_0
@@ -9,7 +9,7 @@ RULESET="table inet filter {
chain input {
type filter hook prerouting priority filter; policy accept;
- ip saddr @example drop
+ ip saddr != { 10.10.10.100, 10.10.10.111 } ip saddr @example drop
}
}"
@@ -31,7 +31,7 @@ EXPECTED="table inet filter {
chain input {
type filter hook prerouting priority filter; policy accept;
- ip saddr @example drop
+ ip saddr != { 10.10.10.100, 10.10.10.111 } ip saddr @example drop
}
}"
diff --git a/tests/shell/testcases/listing/dumps/0001ruleset_0.json-nft b/tests/shell/testcases/listing/dumps/0001ruleset_0.json-nft
new file mode 100644
index 00000000..1bb0e1b8
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0001ruleset_0.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/listing/dumps/0002ruleset_0.json-nft b/tests/shell/testcases/listing/dumps/0002ruleset_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0002ruleset_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/listing/dumps/0002ruleset_0.nft b/tests/shell/testcases/listing/dumps/0002ruleset_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0002ruleset_0.nft
diff --git a/tests/shell/testcases/listing/dumps/0003table_0.json-nft b/tests/shell/testcases/listing/dumps/0003table_0.json-nft
new file mode 100644
index 00000000..1bb0e1b8
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0003table_0.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/listing/dumps/0003table_0.nft b/tests/shell/testcases/listing/dumps/0003table_0.nft
new file mode 100644
index 00000000..1c9f40c5
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0003table_0.nft
@@ -0,0 +1,2 @@
+table ip test {
+}
diff --git a/tests/shell/testcases/listing/dumps/0004table_0.json-nft b/tests/shell/testcases/listing/dumps/0004table_0.json-nft
new file mode 100644
index 00000000..85e9b287
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0004table_0.json-nft
@@ -0,0 +1,25 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test2",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/listing/dumps/0004table_0.nft b/tests/shell/testcases/listing/dumps/0004table_0.nft
new file mode 100644
index 00000000..56d035d1
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0004table_0.nft
@@ -0,0 +1,4 @@
+table ip test {
+}
+table ip test2 {
+}
diff --git a/tests/shell/testcases/listing/dumps/0005ruleset_ip_0.json-nft b/tests/shell/testcases/listing/dumps/0005ruleset_ip_0.json-nft
new file mode 100644
index 00000000..ffd657e5
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0005ruleset_ip_0.json-nft
@@ -0,0 +1,46 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "arp",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "bridge",
+ "name": "test",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/listing/dumps/0005ruleset_ip_0.nft b/tests/shell/testcases/listing/dumps/0005ruleset_ip_0.nft
new file mode 100644
index 00000000..c37261b3
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0005ruleset_ip_0.nft
@@ -0,0 +1,10 @@
+table ip test {
+}
+table ip6 test {
+}
+table inet test {
+}
+table arp test {
+}
+table bridge test {
+}
diff --git a/tests/shell/testcases/listing/dumps/0006ruleset_ip6_0.json-nft b/tests/shell/testcases/listing/dumps/0006ruleset_ip6_0.json-nft
new file mode 100644
index 00000000..ffd657e5
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0006ruleset_ip6_0.json-nft
@@ -0,0 +1,46 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "arp",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "bridge",
+ "name": "test",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/listing/dumps/0006ruleset_ip6_0.nft b/tests/shell/testcases/listing/dumps/0006ruleset_ip6_0.nft
new file mode 100644
index 00000000..c37261b3
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0006ruleset_ip6_0.nft
@@ -0,0 +1,10 @@
+table ip test {
+}
+table ip6 test {
+}
+table inet test {
+}
+table arp test {
+}
+table bridge test {
+}
diff --git a/tests/shell/testcases/listing/dumps/0007ruleset_inet_0.json-nft b/tests/shell/testcases/listing/dumps/0007ruleset_inet_0.json-nft
new file mode 100644
index 00000000..ffd657e5
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0007ruleset_inet_0.json-nft
@@ -0,0 +1,46 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "arp",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "bridge",
+ "name": "test",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/listing/dumps/0007ruleset_inet_0.nft b/tests/shell/testcases/listing/dumps/0007ruleset_inet_0.nft
new file mode 100644
index 00000000..c37261b3
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0007ruleset_inet_0.nft
@@ -0,0 +1,10 @@
+table ip test {
+}
+table ip6 test {
+}
+table inet test {
+}
+table arp test {
+}
+table bridge test {
+}
diff --git a/tests/shell/testcases/listing/dumps/0008ruleset_arp_0.json-nft b/tests/shell/testcases/listing/dumps/0008ruleset_arp_0.json-nft
new file mode 100644
index 00000000..ffd657e5
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0008ruleset_arp_0.json-nft
@@ -0,0 +1,46 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "arp",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "bridge",
+ "name": "test",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/listing/dumps/0008ruleset_arp_0.nft b/tests/shell/testcases/listing/dumps/0008ruleset_arp_0.nft
new file mode 100644
index 00000000..c37261b3
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0008ruleset_arp_0.nft
@@ -0,0 +1,10 @@
+table ip test {
+}
+table ip6 test {
+}
+table inet test {
+}
+table arp test {
+}
+table bridge test {
+}
diff --git a/tests/shell/testcases/listing/dumps/0009ruleset_bridge_0.json-nft b/tests/shell/testcases/listing/dumps/0009ruleset_bridge_0.json-nft
new file mode 100644
index 00000000..ffd657e5
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0009ruleset_bridge_0.json-nft
@@ -0,0 +1,46 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "arp",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "bridge",
+ "name": "test",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/listing/dumps/0009ruleset_bridge_0.nft b/tests/shell/testcases/listing/dumps/0009ruleset_bridge_0.nft
new file mode 100644
index 00000000..c37261b3
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0009ruleset_bridge_0.nft
@@ -0,0 +1,10 @@
+table ip test {
+}
+table ip6 test {
+}
+table inet test {
+}
+table arp test {
+}
+table bridge test {
+}
diff --git a/tests/shell/testcases/listing/dumps/0010sets_0.json-nft b/tests/shell/testcases/listing/dumps/0010sets_0.json-nft
new file mode 100644
index 00000000..efca892e
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0010sets_0.json-nft
@@ -0,0 +1,124 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "nat",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "ssh",
+ "table": "nat",
+ "type": "ipv4_addr",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip6",
+ "name": "testset",
+ "table": "test",
+ "type": "ipv6_addr",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "arp",
+ "name": "test_arp",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "arp",
+ "name": "test_set_arp00",
+ "table": "test_arp",
+ "type": "inet_service",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "arp",
+ "name": "test_set_arp01",
+ "table": "test_arp",
+ "type": "inet_service",
+ "handle": 0,
+ "flags": [
+ "constant"
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "bridge",
+ "name": "test_bridge",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "bridge",
+ "name": "test_set_bridge",
+ "table": "test_bridge",
+ "type": "inet_service",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "set0",
+ "table": "filter",
+ "type": "inet_service",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "set1",
+ "table": "filter",
+ "type": "inet_service",
+ "handle": 0,
+ "flags": [
+ "constant"
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "set2",
+ "table": "filter",
+ "type": "icmpv6_type",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/listing/dumps/0010sets_0.nft b/tests/shell/testcases/listing/dumps/0010sets_0.nft
new file mode 100644
index 00000000..7303c403
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0010sets_0.nft
@@ -0,0 +1,39 @@
+table ip nat {
+ set ssh {
+ type ipv4_addr
+ }
+}
+table ip6 test {
+ set testset {
+ type ipv6_addr
+ }
+}
+table arp test_arp {
+ set test_set_arp00 {
+ type inet_service
+ }
+
+ set test_set_arp01 {
+ type inet_service
+ flags constant
+ }
+}
+table bridge test_bridge {
+ set test_set_bridge {
+ type inet_service
+ }
+}
+table inet filter {
+ set set0 {
+ type inet_service
+ }
+
+ set set1 {
+ type inet_service
+ flags constant
+ }
+
+ set set2 {
+ type icmpv6_type
+ }
+}
diff --git a/tests/shell/testcases/listing/dumps/0011sets_0.json-nft b/tests/shell/testcases/listing/dumps/0011sets_0.json-nft
new file mode 100644
index 00000000..a742fa45
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0011sets_0.json-nft
@@ -0,0 +1,220 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "nat",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "nat",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "nat",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": {
+ "set": [
+ 123,
+ 321
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "test",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "sport"
+ }
+ },
+ "right": {
+ "set": [
+ 123,
+ 321
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "arp",
+ "name": "test_arp",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "arp",
+ "table": "test_arp",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "arp",
+ "table": "test_arp",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "right": {
+ "set": [
+ 123,
+ 321
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "bridge",
+ "name": "test_bridge",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "test_bridge",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "bridge",
+ "table": "test_bridge",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "right": {
+ "set": [
+ "1.1.1.1",
+ "2.2.2.2"
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": {
+ "set": [
+ 80,
+ 443
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/listing/dumps/0011sets_0.nft b/tests/shell/testcases/listing/dumps/0011sets_0.nft
new file mode 100644
index 00000000..4d0aeaf3
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0011sets_0.nft
@@ -0,0 +1,25 @@
+table ip nat {
+ chain test {
+ tcp dport { 123, 321 }
+ }
+}
+table ip6 test {
+ chain test {
+ udp sport { 123, 321 }
+ }
+}
+table arp test_arp {
+ chain test {
+ meta mark { 0x0000007b, 0x00000141 }
+ }
+}
+table bridge test_bridge {
+ chain test {
+ ip daddr { 1.1.1.1, 2.2.2.2 }
+ }
+}
+table inet filter {
+ chain test {
+ tcp dport { 80, 443 }
+ }
+}
diff --git a/tests/shell/testcases/listing/dumps/0012sets_0.json-nft b/tests/shell/testcases/listing/dumps/0012sets_0.json-nft
new file mode 100644
index 00000000..efca892e
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0012sets_0.json-nft
@@ -0,0 +1,124 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "nat",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "ssh",
+ "table": "nat",
+ "type": "ipv4_addr",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip6",
+ "name": "testset",
+ "table": "test",
+ "type": "ipv6_addr",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "arp",
+ "name": "test_arp",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "arp",
+ "name": "test_set_arp00",
+ "table": "test_arp",
+ "type": "inet_service",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "arp",
+ "name": "test_set_arp01",
+ "table": "test_arp",
+ "type": "inet_service",
+ "handle": 0,
+ "flags": [
+ "constant"
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "bridge",
+ "name": "test_bridge",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "bridge",
+ "name": "test_set_bridge",
+ "table": "test_bridge",
+ "type": "inet_service",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "set0",
+ "table": "filter",
+ "type": "inet_service",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "set1",
+ "table": "filter",
+ "type": "inet_service",
+ "handle": 0,
+ "flags": [
+ "constant"
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "set2",
+ "table": "filter",
+ "type": "icmpv6_type",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/listing/dumps/0012sets_0.nft b/tests/shell/testcases/listing/dumps/0012sets_0.nft
new file mode 100644
index 00000000..7303c403
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0012sets_0.nft
@@ -0,0 +1,39 @@
+table ip nat {
+ set ssh {
+ type ipv4_addr
+ }
+}
+table ip6 test {
+ set testset {
+ type ipv6_addr
+ }
+}
+table arp test_arp {
+ set test_set_arp00 {
+ type inet_service
+ }
+
+ set test_set_arp01 {
+ type inet_service
+ flags constant
+ }
+}
+table bridge test_bridge {
+ set test_set_bridge {
+ type inet_service
+ }
+}
+table inet filter {
+ set set0 {
+ type inet_service
+ }
+
+ set set1 {
+ type inet_service
+ flags constant
+ }
+
+ set set2 {
+ type icmpv6_type
+ }
+}
diff --git a/tests/shell/testcases/listing/dumps/0013objects_0.json-nft b/tests/shell/testcases/listing/dumps/0013objects_0.json-nft
new file mode 100644
index 00000000..830aad85
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0013objects_0.json-nft
@@ -0,0 +1,75 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "test",
+ "name": "input",
+ "handle": 0
+ }
+ },
+ {
+ "quota": {
+ "family": "ip",
+ "name": "https-quota",
+ "table": "test",
+ "handle": 0,
+ "bytes": 26214400,
+ "used": 0,
+ "inv": false
+ }
+ },
+ {
+ "ct helper": {
+ "family": "ip",
+ "name": "cthelp",
+ "table": "test",
+ "handle": 0,
+ "type": "sip",
+ "protocol": "tcp",
+ "l3proto": "ip"
+ }
+ },
+ {
+ "ct timeout": {
+ "family": "ip",
+ "name": "cttime",
+ "table": "test",
+ "handle": 0,
+ "protocol": "udp",
+ "l3proto": "ip",
+ "policy": {
+ "unreplied": 15,
+ "replied": 12
+ }
+ }
+ },
+ {
+ "ct expectation": {
+ "family": "ip",
+ "name": "ctexpect",
+ "table": "test",
+ "handle": 0,
+ "protocol": "tcp",
+ "dport": 5432,
+ "timeout": 3600000,
+ "size": 12,
+ "l3proto": "ip"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/listing/dumps/0013objects_0.nft b/tests/shell/testcases/listing/dumps/0013objects_0.nft
new file mode 100644
index 00000000..427db268
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0013objects_0.nft
@@ -0,0 +1,27 @@
+table ip test {
+ quota https-quota {
+ 25 mbytes
+ }
+
+ ct helper cthelp {
+ type "sip" protocol tcp
+ l3proto ip
+ }
+
+ ct timeout cttime {
+ protocol udp
+ l3proto ip
+ policy = { unreplied : 15s, replied : 12s }
+ }
+
+ ct expectation ctexpect {
+ protocol tcp
+ dport 5432
+ timeout 1h
+ size 12
+ l3proto ip
+ }
+
+ chain input {
+ }
+}
diff --git a/tests/shell/testcases/listing/dumps/0014objects_0.json-nft b/tests/shell/testcases/listing/dumps/0014objects_0.json-nft
new file mode 100644
index 00000000..83f72d40
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0014objects_0.json-nft
@@ -0,0 +1,47 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "quota": {
+ "family": "ip",
+ "name": "https-quota",
+ "table": "test",
+ "handle": 0,
+ "bytes": 26214400,
+ "used": 0,
+ "inv": false
+ }
+ },
+ {
+ "ct helper": {
+ "family": "ip",
+ "name": "cthelp",
+ "table": "test",
+ "handle": 0,
+ "type": "sip",
+ "protocol": "tcp",
+ "l3proto": "ip"
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test-ip",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/listing/dumps/0014objects_0.nft b/tests/shell/testcases/listing/dumps/0014objects_0.nft
new file mode 100644
index 00000000..9281a1a0
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0014objects_0.nft
@@ -0,0 +1,12 @@
+table ip test {
+ quota https-quota {
+ 25 mbytes
+ }
+
+ ct helper cthelp {
+ type "sip" protocol tcp
+ l3proto ip
+ }
+}
+table ip test-ip {
+}
diff --git a/tests/shell/testcases/listing/dumps/0015dynamic_0.json-nft b/tests/shell/testcases/listing/dumps/0015dynamic_0.json-nft
new file mode 100644
index 00000000..a94a1b04
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0015dynamic_0.json-nft
@@ -0,0 +1,38 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "test_set",
+ "table": "filter",
+ "type": [
+ "ipv4_addr",
+ "inet_service",
+ "ipv4_addr",
+ "inet_service",
+ "inet_proto"
+ ],
+ "handle": 0,
+ "size": 100000,
+ "flags": [
+ "timeout",
+ "dynamic"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/listing/dumps/0015dynamic_0.nft b/tests/shell/testcases/listing/dumps/0015dynamic_0.nft
new file mode 100644
index 00000000..0f4244bf
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0015dynamic_0.nft
@@ -0,0 +1,7 @@
+table ip filter {
+ set test_set {
+ type ipv4_addr . inet_service . ipv4_addr . inet_service . inet_proto
+ size 100000
+ flags dynamic,timeout
+ }
+}
diff --git a/tests/shell/testcases/listing/dumps/0016anonymous_0.json-nft b/tests/shell/testcases/listing/dumps/0016anonymous_0.json-nft
new file mode 100644
index 00000000..e47ccb8e
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0016anonymous_0.json-nft
@@ -0,0 +1,85 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "1.1.1.1"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ "1.1.1.1",
+ 2
+ ]
+ ]
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/listing/dumps/0016anonymous_0.nft b/tests/shell/testcases/listing/dumps/0016anonymous_0.nft
new file mode 100644
index 00000000..cb089337
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0016anonymous_0.nft
@@ -0,0 +1,6 @@
+table ip x {
+ chain y {
+ ip saddr 1.1.1.1
+ meta mark set ip saddr map { 1.1.1.1 : 0x00000002 }
+ }
+}
diff --git a/tests/shell/testcases/listing/dumps/0017objects_0.json-nft b/tests/shell/testcases/listing/dumps/0017objects_0.json-nft
new file mode 100644
index 00000000..d735f7a1
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0017objects_0.json-nft
@@ -0,0 +1,28 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "inet",
+ "name": "countermap",
+ "table": "filter",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "counter"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/listing/dumps/0017objects_0.nft b/tests/shell/testcases/listing/dumps/0017objects_0.nft
new file mode 100644
index 00000000..e60e3afa
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0017objects_0.nft
@@ -0,0 +1,5 @@
+table inet filter {
+ map countermap {
+ type ipv4_addr : counter
+ }
+}
diff --git a/tests/shell/testcases/listing/dumps/0018data_0.json-nft b/tests/shell/testcases/listing/dumps/0018data_0.json-nft
new file mode 100644
index 00000000..211dcd30
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0018data_0.json-nft
@@ -0,0 +1,28 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "inet",
+ "name": "ipmap",
+ "table": "filter",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "ipv4_addr"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/listing/dumps/0018data_0.nft b/tests/shell/testcases/listing/dumps/0018data_0.nft
new file mode 100644
index 00000000..5d318550
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0018data_0.nft
@@ -0,0 +1,5 @@
+table inet filter {
+ map ipmap {
+ type ipv4_addr : ipv4_addr
+ }
+}
diff --git a/tests/shell/testcases/listing/dumps/0019set_0.json-nft b/tests/shell/testcases/listing/dumps/0019set_0.json-nft
new file mode 100644
index 00000000..3bb7cb8a
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0019set_0.json-nft
@@ -0,0 +1,27 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "ipset",
+ "table": "filter",
+ "type": "ipv4_addr",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/listing/dumps/0019set_0.nft b/tests/shell/testcases/listing/dumps/0019set_0.nft
new file mode 100644
index 00000000..915922ca
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0019set_0.nft
@@ -0,0 +1,5 @@
+table inet filter {
+ set ipset {
+ type ipv4_addr
+ }
+}
diff --git a/tests/shell/testcases/listing/dumps/0020flowtable_0.json-nft b/tests/shell/testcases/listing/dumps/0020flowtable_0.json-nft
new file mode 100644
index 00000000..d511739a
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0020flowtable_0.json-nft
@@ -0,0 +1,67 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "flowtable": {
+ "family": "inet",
+ "name": "f",
+ "table": "filter",
+ "handle": 0,
+ "hook": "ingress",
+ "prio": 0,
+ "dev": "lo"
+ }
+ },
+ {
+ "flowtable": {
+ "family": "inet",
+ "name": "f2",
+ "table": "filter",
+ "handle": 0,
+ "hook": "ingress",
+ "prio": 0
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "flowtable": {
+ "family": "ip",
+ "name": "f",
+ "table": "filter",
+ "handle": 0,
+ "hook": "ingress",
+ "prio": 0,
+ "dev": "lo"
+ }
+ },
+ {
+ "flowtable": {
+ "family": "ip",
+ "name": "f2",
+ "table": "filter",
+ "handle": 0,
+ "hook": "ingress",
+ "prio": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/listing/dumps/0020flowtable_0.nft b/tests/shell/testcases/listing/dumps/0020flowtable_0.nft
new file mode 100644
index 00000000..4a64e531
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0020flowtable_0.nft
@@ -0,0 +1,20 @@
+table inet filter {
+ flowtable f {
+ hook ingress priority filter
+ devices = { lo }
+ }
+
+ flowtable f2 {
+ hook ingress priority filter
+ }
+}
+table ip filter {
+ flowtable f {
+ hook ingress priority filter
+ devices = { lo }
+ }
+
+ flowtable f2 {
+ hook ingress priority filter
+ }
+}
diff --git a/tests/shell/testcases/listing/dumps/0021ruleset_json_terse_0.json-nft b/tests/shell/testcases/listing/dumps/0021ruleset_json_terse_0.json-nft
new file mode 100644
index 00000000..d1131bb4
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0021ruleset_json_terse_0.json-nft
@@ -0,0 +1,39 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "test",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "test",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "elem": [
+ "192.168.3.4",
+ "192.168.3.5"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/listing/dumps/0021ruleset_json_terse_0.nft b/tests/shell/testcases/listing/dumps/0021ruleset_json_terse_0.nft
new file mode 100644
index 00000000..13c8ac63
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0021ruleset_json_terse_0.nft
@@ -0,0 +1,9 @@
+table ip test {
+ set s {
+ type ipv4_addr
+ elements = { 192.168.3.4, 192.168.3.5 }
+ }
+
+ chain c {
+ }
+}
diff --git a/tests/shell/testcases/listing/dumps/0022terse_0.json-nft b/tests/shell/testcases/listing/dumps/0022terse_0.json-nft
new file mode 100644
index 00000000..bd6383da
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0022terse_0.json-nft
@@ -0,0 +1,88 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "example",
+ "table": "filter",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ "10.10.10.10",
+ "10.10.11.11"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "!=",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": {
+ "set": [
+ "10.10.10.100",
+ "10.10.10.111"
+ ]
+ }
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "@example"
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/listing/dumps/0022terse_0.nft b/tests/shell/testcases/listing/dumps/0022terse_0.nft
new file mode 100644
index 00000000..40665cb7
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/0022terse_0.nft
@@ -0,0 +1,12 @@
+table inet filter {
+ set example {
+ type ipv4_addr
+ flags interval
+ elements = { 10.10.10.10, 10.10.11.11 }
+ }
+
+ chain input {
+ type filter hook prerouting priority filter; policy accept;
+ ip saddr != { 10.10.10.100, 10.10.10.111 } ip saddr @example drop
+ }
+}
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 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/listing/dumps/meta_time.nodump
diff --git a/tests/shell/testcases/listing/meta_time b/tests/shell/testcases/listing/meta_time
new file mode 100755
index 00000000..96a9d557
--- /dev/null
+++ b/tests/shell/testcases/listing/meta_time
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_meta_time)
+
+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
+
+$NFT flush chain t c
+TZ=EADT $NFT add rule t c meta hour "03:00"-"14:00"
+TZ=EADT $NFT add rule t c meta hour "04:00"-"15:00"
+TZ=EADT $NFT add rule t c meta hour "05:00"-"16:00"
+TZ=EADT $NFT add rule t c meta hour "06:00"-"17:00"
+
+printf "\t\tmeta hour \"%02d:%02d\"-\"%02d:%02d\"\n" 3 0 14 0 > "$TMP1"
+printf "\t\tmeta hour \"%02d:%02d\"-\"%02d:%02d\"\n" 4 0 15 0 >> "$TMP1"
+printf "\t\tmeta hour \"%02d:%02d\"-\"%02d:%02d\"\n" 5 0 16 0 >> "$TMP1"
+printf "\t\tmeta hour \"%02d:%02d\"-\"%02d:%02d\"\n" 6 0 17 0 >> "$TMP1"
+
+check_decode EADT
diff --git a/tests/shell/testcases/maps/0004interval_map_create_once_0 b/tests/shell/testcases/maps/0004interval_map_create_once_0
index 3de0c9de..64f434ad 100755
--- a/tests/shell/testcases/maps/0004interval_map_create_once_0
+++ b/tests/shell/testcases/maps/0004interval_map_create_once_0
@@ -5,6 +5,10 @@
HOWMANY=63
+if [ "$NFT_TEST_SKIP_slow" = y ] ; then
+ HOWMANY=5
+fi
+
tmpfile=$(mktemp)
if [ ! -w $tmpfile ] ; then
echo "Failed to create tmp file" >&2
@@ -64,3 +68,7 @@ if [ "$EXPECTED" != "$GET" ] ; then
exit 1
fi
+if [ "$HOWMANY" != 63 ] ; then
+ echo "Run a partial test due to NFT_TEST_SKIP_slow=y. Skip"
+ exit 77
+fi
diff --git a/tests/shell/testcases/maps/0009vmap_0 b/tests/shell/testcases/maps/0009vmap_0
index 7627c81d..4e133b72 100755
--- a/tests/shell/testcases/maps/0009vmap_0
+++ b/tests/shell/testcases/maps/0009vmap_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_set_expr)
+
set -e
EXPECTED="table inet filter {
@@ -12,7 +14,7 @@ EXPECTED="table inet filter {
chain prerouting {
type filter hook prerouting priority -300; policy accept;
- iif vmap { "lo" : jump wan_input }
+ iif vmap { "lo" counter : jump wan_input }
}
}"
diff --git a/tests/shell/testcases/maps/0010concat_map_0 b/tests/shell/testcases/maps/0010concat_map_0
index 4848d972..859bbfcf 100755
--- a/tests/shell/testcases/maps/0010concat_map_0
+++ b/tests/shell/testcases/maps/0010concat_map_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_inet_nat)
+
set -e
EXPECTED="table inet x {
diff --git a/tests/shell/testcases/maps/0011vmap_0 b/tests/shell/testcases/maps/0011vmap_0
index 83704d48..3e6fa78d 100755
--- a/tests/shell/testcases/maps/0011vmap_0
+++ b/tests/shell/testcases/maps/0011vmap_0
@@ -22,4 +22,12 @@ EXPECTED="table inet filter {
}"
$NFT -f - <<< "$EXPECTED"
-$NFT 'add element inet filter portmap { 22 : jump ssh_input, * : drop }'
+
+if [ "$NFT_TEST_HAVE_catchall_element" != n ]; then
+ $NFT 'add element inet filter portmap { 22 : jump ssh_input, * : drop }'
+fi
+
+if [ "$NFT_TEST_HAVE_catchall_element" = n ]; then
+ echo "Ran partial tests due to NFT_TEST_HAVE_catchall_element=n (skipped)"
+ exit 77
+fi
diff --git a/tests/shell/testcases/maps/0012map_concat_0 b/tests/shell/testcases/maps/0012map_concat_0
new file mode 100755
index 00000000..d18c7a73
--- /dev/null
+++ b/tests/shell/testcases/maps/0012map_concat_0
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_pipapo)
+
+set -e
+
+EXPECTED="table ip x {
+ map w {
+ typeof ip saddr . meta mark : verdict
+ flags interval
+ counter
+ elements = {
+ 127.0.0.1-127.0.0.4 . 0x123434-0xb00122 : accept,
+ }
+ }
+
+ chain k {
+ type filter hook input priority filter + 1; policy accept;
+ meta mark set 0x123434
+ ip saddr . meta mark vmap @w
+ }
+}"
+
+$NFT -f - <<< "$EXPECTED"
diff --git a/tests/shell/testcases/maps/0013map_0 b/tests/shell/testcases/maps/0013map_0
index 70d7fd3b..c8d20cee 100755
--- a/tests/shell/testcases/maps/0013map_0
+++ b/tests/shell/testcases/maps/0013map_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_pipapo)
+
set -e
RULESET="
diff --git a/tests/shell/testcases/maps/0014destroy_0 b/tests/shell/testcases/maps/0014destroy_0
new file mode 100755
index 00000000..ee81e3cd
--- /dev/null
+++ b/tests/shell/testcases/maps/0014destroy_0
@@ -0,0 +1,12 @@
+#!/bin/bash -e
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_destroy)
+
+$NFT add table x
+
+# pass for non-existent map
+$NFT destroy map x y
+
+# successfully delete existing map
+$NFT add map x y '{ type ipv4_addr : ipv4_addr; }'
+$NFT destroy map x y
diff --git a/tests/shell/testcases/maps/0016map_leak_0 b/tests/shell/testcases/maps/0016map_leak_0
new file mode 100755
index 00000000..e110ee4b
--- /dev/null
+++ b/tests/shell/testcases/maps/0016map_leak_0
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table ip t {
+ map sourcemap {
+ type ipv4_addr : verdict
+ elements = { 100.123.10.2 : jump c }
+ }
+
+ chain c {
+ }
+}"
+
+$NFT -f - <<< "$RULESET"
+# again, since it is addition, not creation, it is successful
+$NFT -f - <<< "$RULESET"
+# flush it to check for refcount leak
+$NFT flush ruleset
+
+#
+# again with stateful objects
+#
+
+RULESET="table ip t {
+ counter c {}
+
+ map sourcemap {
+ type ipv4_addr : counter
+ elements = { 100.123.10.2 : \"c\" }
+ }
+}"
+
+$NFT -f - <<< "$RULESET"
+# again, since it is addition, not creation, it is successful
+$NFT -f - <<< "$RULESET"
+# flush it to check for refcount leak
+$NFT flush ruleset
diff --git a/tests/shell/testcases/maps/0017_map_variable_0 b/tests/shell/testcases/maps/0017_map_variable_0
new file mode 100755
index 00000000..e01adb4c
--- /dev/null
+++ b/tests/shell/testcases/maps/0017_map_variable_0
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+set -e
+
+if [ "$NFT_TEST_HAVE_catchall_element" != n ] ; then
+ CATCHALL="* : 3,"
+else
+ CATCHALL=","
+fi
+
+RULESET="define x = {
+ 1.1.1.1 : 2,
+ $CATCHALL
+}
+
+table ip x {
+ map y {
+ typeof ip saddr : mark
+ elements = \$x
+ }
+ map z {
+ typeof ip saddr : mark
+ elements = \$x
+ }
+}"
+
+$NFT -f - <<< "$RULESET"
+
+if [ "$NFT_TEST_HAVE_catchall_element" = n ] ; then
+ echo "Ran modified version of test due to NFT_TEST_HAVE_catchall_element=n (skipped)"
+ exit 77
+fi
diff --git a/tests/shell/testcases/maps/0018map_leak_timeout_0 b/tests/shell/testcases/maps/0018map_leak_timeout_0
new file mode 100755
index 00000000..09db315a
--- /dev/null
+++ b/tests/shell/testcases/maps/0018map_leak_timeout_0
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+# NFT_TEST_SKIP(NFT_TEST_SKIP_slow)
+
+set -e
+
+RULESET="table ip t {
+ map sourcemap {
+ type ipv4_addr : verdict
+ timeout 3s
+ elements = { 100.123.10.2 : jump c }
+ }
+
+ chain c {
+ }
+}"
+
+$NFT -f - <<< "$RULESET"
+# again, since it is addition, not creation, it is successful
+$NFT -f - <<< "$RULESET"
+
+# wait for elements to expire
+sleep 5
+
+# flush it to check for refcount leak
+$NFT flush ruleset
+
+#
+# again with stateful objects
+#
+
+RULESET="table ip t {
+ counter c {}
+
+ map sourcemap {
+ type ipv4_addr : counter
+ timeout 3s
+ elements = { 100.123.10.2 : \"c\" }
+ }
+}"
+
+$NFT -f - <<< "$RULESET"
+# again, since it is addition, not creation, it is successful
+$NFT -f - <<< "$RULESET"
+# flush it to check for refcount leak
+
+# wait for elements to expire
+sleep 5
+
+$NFT flush ruleset
diff --git a/tests/shell/testcases/sets/0024named_objects_0 b/tests/shell/testcases/maps/0024named_objects_0
index 21200c3c..21200c3c 100755
--- a/tests/shell/testcases/sets/0024named_objects_0
+++ b/tests/shell/testcases/maps/0024named_objects_0
diff --git a/tests/shell/testcases/maps/anon_objmap_concat b/tests/shell/testcases/maps/anon_objmap_concat
new file mode 100755
index 00000000..34465f1d
--- /dev/null
+++ b/tests/shell/testcases/maps/anon_objmap_concat
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_pipapo)
+
+set -e
+dumpfile=$(dirname $0)/dumps/$(basename $0).nft
+
+$NFT -f "$dumpfile"
diff --git a/tests/shell/testcases/maps/dumps/0003map_add_many_elements_0.json-nft b/tests/shell/testcases/maps/dumps/0003map_add_many_elements_0.json-nft
new file mode 100644
index 00000000..1b5c2a23
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0003map_add_many_elements_0.json-nft
@@ -0,0 +1,3874 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "ipv4_addr",
+ "elem": [
+ [
+ "10.0.1.1",
+ "10.0.1.1"
+ ],
+ [
+ "10.0.1.2",
+ "10.0.1.2"
+ ],
+ [
+ "10.0.1.3",
+ "10.0.1.3"
+ ],
+ [
+ "10.0.1.4",
+ "10.0.1.4"
+ ],
+ [
+ "10.0.1.5",
+ "10.0.1.5"
+ ],
+ [
+ "10.0.1.6",
+ "10.0.1.6"
+ ],
+ [
+ "10.0.1.7",
+ "10.0.1.7"
+ ],
+ [
+ "10.0.1.8",
+ "10.0.1.8"
+ ],
+ [
+ "10.0.1.9",
+ "10.0.1.9"
+ ],
+ [
+ "10.0.1.10",
+ "10.0.1.10"
+ ],
+ [
+ "10.0.1.11",
+ "10.0.1.11"
+ ],
+ [
+ "10.0.1.12",
+ "10.0.1.12"
+ ],
+ [
+ "10.0.1.13",
+ "10.0.1.13"
+ ],
+ [
+ "10.0.1.14",
+ "10.0.1.14"
+ ],
+ [
+ "10.0.1.15",
+ "10.0.1.15"
+ ],
+ [
+ "10.0.1.16",
+ "10.0.1.16"
+ ],
+ [
+ "10.0.1.17",
+ "10.0.1.17"
+ ],
+ [
+ "10.0.1.18",
+ "10.0.1.18"
+ ],
+ [
+ "10.0.1.19",
+ "10.0.1.19"
+ ],
+ [
+ "10.0.1.20",
+ "10.0.1.20"
+ ],
+ [
+ "10.0.1.21",
+ "10.0.1.21"
+ ],
+ [
+ "10.0.1.22",
+ "10.0.1.22"
+ ],
+ [
+ "10.0.1.23",
+ "10.0.1.23"
+ ],
+ [
+ "10.0.1.24",
+ "10.0.1.24"
+ ],
+ [
+ "10.0.1.25",
+ "10.0.1.25"
+ ],
+ [
+ "10.0.1.26",
+ "10.0.1.26"
+ ],
+ [
+ "10.0.1.27",
+ "10.0.1.27"
+ ],
+ [
+ "10.0.1.28",
+ "10.0.1.28"
+ ],
+ [
+ "10.0.1.29",
+ "10.0.1.29"
+ ],
+ [
+ "10.0.1.30",
+ "10.0.1.30"
+ ],
+ [
+ "10.0.1.31",
+ "10.0.1.31"
+ ],
+ [
+ "10.0.2.1",
+ "10.0.2.1"
+ ],
+ [
+ "10.0.2.2",
+ "10.0.2.2"
+ ],
+ [
+ "10.0.2.3",
+ "10.0.2.3"
+ ],
+ [
+ "10.0.2.4",
+ "10.0.2.4"
+ ],
+ [
+ "10.0.2.5",
+ "10.0.2.5"
+ ],
+ [
+ "10.0.2.6",
+ "10.0.2.6"
+ ],
+ [
+ "10.0.2.7",
+ "10.0.2.7"
+ ],
+ [
+ "10.0.2.8",
+ "10.0.2.8"
+ ],
+ [
+ "10.0.2.9",
+ "10.0.2.9"
+ ],
+ [
+ "10.0.2.10",
+ "10.0.2.10"
+ ],
+ [
+ "10.0.2.11",
+ "10.0.2.11"
+ ],
+ [
+ "10.0.2.12",
+ "10.0.2.12"
+ ],
+ [
+ "10.0.2.13",
+ "10.0.2.13"
+ ],
+ [
+ "10.0.2.14",
+ "10.0.2.14"
+ ],
+ [
+ "10.0.2.15",
+ "10.0.2.15"
+ ],
+ [
+ "10.0.2.16",
+ "10.0.2.16"
+ ],
+ [
+ "10.0.2.17",
+ "10.0.2.17"
+ ],
+ [
+ "10.0.2.18",
+ "10.0.2.18"
+ ],
+ [
+ "10.0.2.19",
+ "10.0.2.19"
+ ],
+ [
+ "10.0.2.20",
+ "10.0.2.20"
+ ],
+ [
+ "10.0.2.21",
+ "10.0.2.21"
+ ],
+ [
+ "10.0.2.22",
+ "10.0.2.22"
+ ],
+ [
+ "10.0.2.23",
+ "10.0.2.23"
+ ],
+ [
+ "10.0.2.24",
+ "10.0.2.24"
+ ],
+ [
+ "10.0.2.25",
+ "10.0.2.25"
+ ],
+ [
+ "10.0.2.26",
+ "10.0.2.26"
+ ],
+ [
+ "10.0.2.27",
+ "10.0.2.27"
+ ],
+ [
+ "10.0.2.28",
+ "10.0.2.28"
+ ],
+ [
+ "10.0.2.29",
+ "10.0.2.29"
+ ],
+ [
+ "10.0.2.30",
+ "10.0.2.30"
+ ],
+ [
+ "10.0.2.31",
+ "10.0.2.31"
+ ],
+ [
+ "10.0.3.1",
+ "10.0.3.1"
+ ],
+ [
+ "10.0.3.2",
+ "10.0.3.2"
+ ],
+ [
+ "10.0.3.3",
+ "10.0.3.3"
+ ],
+ [
+ "10.0.3.4",
+ "10.0.3.4"
+ ],
+ [
+ "10.0.3.5",
+ "10.0.3.5"
+ ],
+ [
+ "10.0.3.6",
+ "10.0.3.6"
+ ],
+ [
+ "10.0.3.7",
+ "10.0.3.7"
+ ],
+ [
+ "10.0.3.8",
+ "10.0.3.8"
+ ],
+ [
+ "10.0.3.9",
+ "10.0.3.9"
+ ],
+ [
+ "10.0.3.10",
+ "10.0.3.10"
+ ],
+ [
+ "10.0.3.11",
+ "10.0.3.11"
+ ],
+ [
+ "10.0.3.12",
+ "10.0.3.12"
+ ],
+ [
+ "10.0.3.13",
+ "10.0.3.13"
+ ],
+ [
+ "10.0.3.14",
+ "10.0.3.14"
+ ],
+ [
+ "10.0.3.15",
+ "10.0.3.15"
+ ],
+ [
+ "10.0.3.16",
+ "10.0.3.16"
+ ],
+ [
+ "10.0.3.17",
+ "10.0.3.17"
+ ],
+ [
+ "10.0.3.18",
+ "10.0.3.18"
+ ],
+ [
+ "10.0.3.19",
+ "10.0.3.19"
+ ],
+ [
+ "10.0.3.20",
+ "10.0.3.20"
+ ],
+ [
+ "10.0.3.21",
+ "10.0.3.21"
+ ],
+ [
+ "10.0.3.22",
+ "10.0.3.22"
+ ],
+ [
+ "10.0.3.23",
+ "10.0.3.23"
+ ],
+ [
+ "10.0.3.24",
+ "10.0.3.24"
+ ],
+ [
+ "10.0.3.25",
+ "10.0.3.25"
+ ],
+ [
+ "10.0.3.26",
+ "10.0.3.26"
+ ],
+ [
+ "10.0.3.27",
+ "10.0.3.27"
+ ],
+ [
+ "10.0.3.28",
+ "10.0.3.28"
+ ],
+ [
+ "10.0.3.29",
+ "10.0.3.29"
+ ],
+ [
+ "10.0.3.30",
+ "10.0.3.30"
+ ],
+ [
+ "10.0.3.31",
+ "10.0.3.31"
+ ],
+ [
+ "10.0.4.1",
+ "10.0.4.1"
+ ],
+ [
+ "10.0.4.2",
+ "10.0.4.2"
+ ],
+ [
+ "10.0.4.3",
+ "10.0.4.3"
+ ],
+ [
+ "10.0.4.4",
+ "10.0.4.4"
+ ],
+ [
+ "10.0.4.5",
+ "10.0.4.5"
+ ],
+ [
+ "10.0.4.6",
+ "10.0.4.6"
+ ],
+ [
+ "10.0.4.7",
+ "10.0.4.7"
+ ],
+ [
+ "10.0.4.8",
+ "10.0.4.8"
+ ],
+ [
+ "10.0.4.9",
+ "10.0.4.9"
+ ],
+ [
+ "10.0.4.10",
+ "10.0.4.10"
+ ],
+ [
+ "10.0.4.11",
+ "10.0.4.11"
+ ],
+ [
+ "10.0.4.12",
+ "10.0.4.12"
+ ],
+ [
+ "10.0.4.13",
+ "10.0.4.13"
+ ],
+ [
+ "10.0.4.14",
+ "10.0.4.14"
+ ],
+ [
+ "10.0.4.15",
+ "10.0.4.15"
+ ],
+ [
+ "10.0.4.16",
+ "10.0.4.16"
+ ],
+ [
+ "10.0.4.17",
+ "10.0.4.17"
+ ],
+ [
+ "10.0.4.18",
+ "10.0.4.18"
+ ],
+ [
+ "10.0.4.19",
+ "10.0.4.19"
+ ],
+ [
+ "10.0.4.20",
+ "10.0.4.20"
+ ],
+ [
+ "10.0.4.21",
+ "10.0.4.21"
+ ],
+ [
+ "10.0.4.22",
+ "10.0.4.22"
+ ],
+ [
+ "10.0.4.23",
+ "10.0.4.23"
+ ],
+ [
+ "10.0.4.24",
+ "10.0.4.24"
+ ],
+ [
+ "10.0.4.25",
+ "10.0.4.25"
+ ],
+ [
+ "10.0.4.26",
+ "10.0.4.26"
+ ],
+ [
+ "10.0.4.27",
+ "10.0.4.27"
+ ],
+ [
+ "10.0.4.28",
+ "10.0.4.28"
+ ],
+ [
+ "10.0.4.29",
+ "10.0.4.29"
+ ],
+ [
+ "10.0.4.30",
+ "10.0.4.30"
+ ],
+ [
+ "10.0.4.31",
+ "10.0.4.31"
+ ],
+ [
+ "10.0.5.1",
+ "10.0.5.1"
+ ],
+ [
+ "10.0.5.2",
+ "10.0.5.2"
+ ],
+ [
+ "10.0.5.3",
+ "10.0.5.3"
+ ],
+ [
+ "10.0.5.4",
+ "10.0.5.4"
+ ],
+ [
+ "10.0.5.5",
+ "10.0.5.5"
+ ],
+ [
+ "10.0.5.6",
+ "10.0.5.6"
+ ],
+ [
+ "10.0.5.7",
+ "10.0.5.7"
+ ],
+ [
+ "10.0.5.8",
+ "10.0.5.8"
+ ],
+ [
+ "10.0.5.9",
+ "10.0.5.9"
+ ],
+ [
+ "10.0.5.10",
+ "10.0.5.10"
+ ],
+ [
+ "10.0.5.11",
+ "10.0.5.11"
+ ],
+ [
+ "10.0.5.12",
+ "10.0.5.12"
+ ],
+ [
+ "10.0.5.13",
+ "10.0.5.13"
+ ],
+ [
+ "10.0.5.14",
+ "10.0.5.14"
+ ],
+ [
+ "10.0.5.15",
+ "10.0.5.15"
+ ],
+ [
+ "10.0.5.16",
+ "10.0.5.16"
+ ],
+ [
+ "10.0.5.17",
+ "10.0.5.17"
+ ],
+ [
+ "10.0.5.18",
+ "10.0.5.18"
+ ],
+ [
+ "10.0.5.19",
+ "10.0.5.19"
+ ],
+ [
+ "10.0.5.20",
+ "10.0.5.20"
+ ],
+ [
+ "10.0.5.21",
+ "10.0.5.21"
+ ],
+ [
+ "10.0.5.22",
+ "10.0.5.22"
+ ],
+ [
+ "10.0.5.23",
+ "10.0.5.23"
+ ],
+ [
+ "10.0.5.24",
+ "10.0.5.24"
+ ],
+ [
+ "10.0.5.25",
+ "10.0.5.25"
+ ],
+ [
+ "10.0.5.26",
+ "10.0.5.26"
+ ],
+ [
+ "10.0.5.27",
+ "10.0.5.27"
+ ],
+ [
+ "10.0.5.28",
+ "10.0.5.28"
+ ],
+ [
+ "10.0.5.29",
+ "10.0.5.29"
+ ],
+ [
+ "10.0.5.30",
+ "10.0.5.30"
+ ],
+ [
+ "10.0.5.31",
+ "10.0.5.31"
+ ],
+ [
+ "10.0.6.1",
+ "10.0.6.1"
+ ],
+ [
+ "10.0.6.2",
+ "10.0.6.2"
+ ],
+ [
+ "10.0.6.3",
+ "10.0.6.3"
+ ],
+ [
+ "10.0.6.4",
+ "10.0.6.4"
+ ],
+ [
+ "10.0.6.5",
+ "10.0.6.5"
+ ],
+ [
+ "10.0.6.6",
+ "10.0.6.6"
+ ],
+ [
+ "10.0.6.7",
+ "10.0.6.7"
+ ],
+ [
+ "10.0.6.8",
+ "10.0.6.8"
+ ],
+ [
+ "10.0.6.9",
+ "10.0.6.9"
+ ],
+ [
+ "10.0.6.10",
+ "10.0.6.10"
+ ],
+ [
+ "10.0.6.11",
+ "10.0.6.11"
+ ],
+ [
+ "10.0.6.12",
+ "10.0.6.12"
+ ],
+ [
+ "10.0.6.13",
+ "10.0.6.13"
+ ],
+ [
+ "10.0.6.14",
+ "10.0.6.14"
+ ],
+ [
+ "10.0.6.15",
+ "10.0.6.15"
+ ],
+ [
+ "10.0.6.16",
+ "10.0.6.16"
+ ],
+ [
+ "10.0.6.17",
+ "10.0.6.17"
+ ],
+ [
+ "10.0.6.18",
+ "10.0.6.18"
+ ],
+ [
+ "10.0.6.19",
+ "10.0.6.19"
+ ],
+ [
+ "10.0.6.20",
+ "10.0.6.20"
+ ],
+ [
+ "10.0.6.21",
+ "10.0.6.21"
+ ],
+ [
+ "10.0.6.22",
+ "10.0.6.22"
+ ],
+ [
+ "10.0.6.23",
+ "10.0.6.23"
+ ],
+ [
+ "10.0.6.24",
+ "10.0.6.24"
+ ],
+ [
+ "10.0.6.25",
+ "10.0.6.25"
+ ],
+ [
+ "10.0.6.26",
+ "10.0.6.26"
+ ],
+ [
+ "10.0.6.27",
+ "10.0.6.27"
+ ],
+ [
+ "10.0.6.28",
+ "10.0.6.28"
+ ],
+ [
+ "10.0.6.29",
+ "10.0.6.29"
+ ],
+ [
+ "10.0.6.30",
+ "10.0.6.30"
+ ],
+ [
+ "10.0.6.31",
+ "10.0.6.31"
+ ],
+ [
+ "10.0.7.1",
+ "10.0.7.1"
+ ],
+ [
+ "10.0.7.2",
+ "10.0.7.2"
+ ],
+ [
+ "10.0.7.3",
+ "10.0.7.3"
+ ],
+ [
+ "10.0.7.4",
+ "10.0.7.4"
+ ],
+ [
+ "10.0.7.5",
+ "10.0.7.5"
+ ],
+ [
+ "10.0.7.6",
+ "10.0.7.6"
+ ],
+ [
+ "10.0.7.7",
+ "10.0.7.7"
+ ],
+ [
+ "10.0.7.8",
+ "10.0.7.8"
+ ],
+ [
+ "10.0.7.9",
+ "10.0.7.9"
+ ],
+ [
+ "10.0.7.10",
+ "10.0.7.10"
+ ],
+ [
+ "10.0.7.11",
+ "10.0.7.11"
+ ],
+ [
+ "10.0.7.12",
+ "10.0.7.12"
+ ],
+ [
+ "10.0.7.13",
+ "10.0.7.13"
+ ],
+ [
+ "10.0.7.14",
+ "10.0.7.14"
+ ],
+ [
+ "10.0.7.15",
+ "10.0.7.15"
+ ],
+ [
+ "10.0.7.16",
+ "10.0.7.16"
+ ],
+ [
+ "10.0.7.17",
+ "10.0.7.17"
+ ],
+ [
+ "10.0.7.18",
+ "10.0.7.18"
+ ],
+ [
+ "10.0.7.19",
+ "10.0.7.19"
+ ],
+ [
+ "10.0.7.20",
+ "10.0.7.20"
+ ],
+ [
+ "10.0.7.21",
+ "10.0.7.21"
+ ],
+ [
+ "10.0.7.22",
+ "10.0.7.22"
+ ],
+ [
+ "10.0.7.23",
+ "10.0.7.23"
+ ],
+ [
+ "10.0.7.24",
+ "10.0.7.24"
+ ],
+ [
+ "10.0.7.25",
+ "10.0.7.25"
+ ],
+ [
+ "10.0.7.26",
+ "10.0.7.26"
+ ],
+ [
+ "10.0.7.27",
+ "10.0.7.27"
+ ],
+ [
+ "10.0.7.28",
+ "10.0.7.28"
+ ],
+ [
+ "10.0.7.29",
+ "10.0.7.29"
+ ],
+ [
+ "10.0.7.30",
+ "10.0.7.30"
+ ],
+ [
+ "10.0.7.31",
+ "10.0.7.31"
+ ],
+ [
+ "10.0.8.1",
+ "10.0.8.1"
+ ],
+ [
+ "10.0.8.2",
+ "10.0.8.2"
+ ],
+ [
+ "10.0.8.3",
+ "10.0.8.3"
+ ],
+ [
+ "10.0.8.4",
+ "10.0.8.4"
+ ],
+ [
+ "10.0.8.5",
+ "10.0.8.5"
+ ],
+ [
+ "10.0.8.6",
+ "10.0.8.6"
+ ],
+ [
+ "10.0.8.7",
+ "10.0.8.7"
+ ],
+ [
+ "10.0.8.8",
+ "10.0.8.8"
+ ],
+ [
+ "10.0.8.9",
+ "10.0.8.9"
+ ],
+ [
+ "10.0.8.10",
+ "10.0.8.10"
+ ],
+ [
+ "10.0.8.11",
+ "10.0.8.11"
+ ],
+ [
+ "10.0.8.12",
+ "10.0.8.12"
+ ],
+ [
+ "10.0.8.13",
+ "10.0.8.13"
+ ],
+ [
+ "10.0.8.14",
+ "10.0.8.14"
+ ],
+ [
+ "10.0.8.15",
+ "10.0.8.15"
+ ],
+ [
+ "10.0.8.16",
+ "10.0.8.16"
+ ],
+ [
+ "10.0.8.17",
+ "10.0.8.17"
+ ],
+ [
+ "10.0.8.18",
+ "10.0.8.18"
+ ],
+ [
+ "10.0.8.19",
+ "10.0.8.19"
+ ],
+ [
+ "10.0.8.20",
+ "10.0.8.20"
+ ],
+ [
+ "10.0.8.21",
+ "10.0.8.21"
+ ],
+ [
+ "10.0.8.22",
+ "10.0.8.22"
+ ],
+ [
+ "10.0.8.23",
+ "10.0.8.23"
+ ],
+ [
+ "10.0.8.24",
+ "10.0.8.24"
+ ],
+ [
+ "10.0.8.25",
+ "10.0.8.25"
+ ],
+ [
+ "10.0.8.26",
+ "10.0.8.26"
+ ],
+ [
+ "10.0.8.27",
+ "10.0.8.27"
+ ],
+ [
+ "10.0.8.28",
+ "10.0.8.28"
+ ],
+ [
+ "10.0.8.29",
+ "10.0.8.29"
+ ],
+ [
+ "10.0.8.30",
+ "10.0.8.30"
+ ],
+ [
+ "10.0.8.31",
+ "10.0.8.31"
+ ],
+ [
+ "10.0.9.1",
+ "10.0.9.1"
+ ],
+ [
+ "10.0.9.2",
+ "10.0.9.2"
+ ],
+ [
+ "10.0.9.3",
+ "10.0.9.3"
+ ],
+ [
+ "10.0.9.4",
+ "10.0.9.4"
+ ],
+ [
+ "10.0.9.5",
+ "10.0.9.5"
+ ],
+ [
+ "10.0.9.6",
+ "10.0.9.6"
+ ],
+ [
+ "10.0.9.7",
+ "10.0.9.7"
+ ],
+ [
+ "10.0.9.8",
+ "10.0.9.8"
+ ],
+ [
+ "10.0.9.9",
+ "10.0.9.9"
+ ],
+ [
+ "10.0.9.10",
+ "10.0.9.10"
+ ],
+ [
+ "10.0.9.11",
+ "10.0.9.11"
+ ],
+ [
+ "10.0.9.12",
+ "10.0.9.12"
+ ],
+ [
+ "10.0.9.13",
+ "10.0.9.13"
+ ],
+ [
+ "10.0.9.14",
+ "10.0.9.14"
+ ],
+ [
+ "10.0.9.15",
+ "10.0.9.15"
+ ],
+ [
+ "10.0.9.16",
+ "10.0.9.16"
+ ],
+ [
+ "10.0.9.17",
+ "10.0.9.17"
+ ],
+ [
+ "10.0.9.18",
+ "10.0.9.18"
+ ],
+ [
+ "10.0.9.19",
+ "10.0.9.19"
+ ],
+ [
+ "10.0.9.20",
+ "10.0.9.20"
+ ],
+ [
+ "10.0.9.21",
+ "10.0.9.21"
+ ],
+ [
+ "10.0.9.22",
+ "10.0.9.22"
+ ],
+ [
+ "10.0.9.23",
+ "10.0.9.23"
+ ],
+ [
+ "10.0.9.24",
+ "10.0.9.24"
+ ],
+ [
+ "10.0.9.25",
+ "10.0.9.25"
+ ],
+ [
+ "10.0.9.26",
+ "10.0.9.26"
+ ],
+ [
+ "10.0.9.27",
+ "10.0.9.27"
+ ],
+ [
+ "10.0.9.28",
+ "10.0.9.28"
+ ],
+ [
+ "10.0.9.29",
+ "10.0.9.29"
+ ],
+ [
+ "10.0.9.30",
+ "10.0.9.30"
+ ],
+ [
+ "10.0.9.31",
+ "10.0.9.31"
+ ],
+ [
+ "10.0.10.1",
+ "10.0.10.1"
+ ],
+ [
+ "10.0.10.2",
+ "10.0.10.2"
+ ],
+ [
+ "10.0.10.3",
+ "10.0.10.3"
+ ],
+ [
+ "10.0.10.4",
+ "10.0.10.4"
+ ],
+ [
+ "10.0.10.5",
+ "10.0.10.5"
+ ],
+ [
+ "10.0.10.6",
+ "10.0.10.6"
+ ],
+ [
+ "10.0.10.7",
+ "10.0.10.7"
+ ],
+ [
+ "10.0.10.8",
+ "10.0.10.8"
+ ],
+ [
+ "10.0.10.9",
+ "10.0.10.9"
+ ],
+ [
+ "10.0.10.10",
+ "10.0.10.10"
+ ],
+ [
+ "10.0.10.11",
+ "10.0.10.11"
+ ],
+ [
+ "10.0.10.12",
+ "10.0.10.12"
+ ],
+ [
+ "10.0.10.13",
+ "10.0.10.13"
+ ],
+ [
+ "10.0.10.14",
+ "10.0.10.14"
+ ],
+ [
+ "10.0.10.15",
+ "10.0.10.15"
+ ],
+ [
+ "10.0.10.16",
+ "10.0.10.16"
+ ],
+ [
+ "10.0.10.17",
+ "10.0.10.17"
+ ],
+ [
+ "10.0.10.18",
+ "10.0.10.18"
+ ],
+ [
+ "10.0.10.19",
+ "10.0.10.19"
+ ],
+ [
+ "10.0.10.20",
+ "10.0.10.20"
+ ],
+ [
+ "10.0.10.21",
+ "10.0.10.21"
+ ],
+ [
+ "10.0.10.22",
+ "10.0.10.22"
+ ],
+ [
+ "10.0.10.23",
+ "10.0.10.23"
+ ],
+ [
+ "10.0.10.24",
+ "10.0.10.24"
+ ],
+ [
+ "10.0.10.25",
+ "10.0.10.25"
+ ],
+ [
+ "10.0.10.26",
+ "10.0.10.26"
+ ],
+ [
+ "10.0.10.27",
+ "10.0.10.27"
+ ],
+ [
+ "10.0.10.28",
+ "10.0.10.28"
+ ],
+ [
+ "10.0.10.29",
+ "10.0.10.29"
+ ],
+ [
+ "10.0.10.30",
+ "10.0.10.30"
+ ],
+ [
+ "10.0.10.31",
+ "10.0.10.31"
+ ],
+ [
+ "10.0.11.1",
+ "10.0.11.1"
+ ],
+ [
+ "10.0.11.2",
+ "10.0.11.2"
+ ],
+ [
+ "10.0.11.3",
+ "10.0.11.3"
+ ],
+ [
+ "10.0.11.4",
+ "10.0.11.4"
+ ],
+ [
+ "10.0.11.5",
+ "10.0.11.5"
+ ],
+ [
+ "10.0.11.6",
+ "10.0.11.6"
+ ],
+ [
+ "10.0.11.7",
+ "10.0.11.7"
+ ],
+ [
+ "10.0.11.8",
+ "10.0.11.8"
+ ],
+ [
+ "10.0.11.9",
+ "10.0.11.9"
+ ],
+ [
+ "10.0.11.10",
+ "10.0.11.10"
+ ],
+ [
+ "10.0.11.11",
+ "10.0.11.11"
+ ],
+ [
+ "10.0.11.12",
+ "10.0.11.12"
+ ],
+ [
+ "10.0.11.13",
+ "10.0.11.13"
+ ],
+ [
+ "10.0.11.14",
+ "10.0.11.14"
+ ],
+ [
+ "10.0.11.15",
+ "10.0.11.15"
+ ],
+ [
+ "10.0.11.16",
+ "10.0.11.16"
+ ],
+ [
+ "10.0.11.17",
+ "10.0.11.17"
+ ],
+ [
+ "10.0.11.18",
+ "10.0.11.18"
+ ],
+ [
+ "10.0.11.19",
+ "10.0.11.19"
+ ],
+ [
+ "10.0.11.20",
+ "10.0.11.20"
+ ],
+ [
+ "10.0.11.21",
+ "10.0.11.21"
+ ],
+ [
+ "10.0.11.22",
+ "10.0.11.22"
+ ],
+ [
+ "10.0.11.23",
+ "10.0.11.23"
+ ],
+ [
+ "10.0.11.24",
+ "10.0.11.24"
+ ],
+ [
+ "10.0.11.25",
+ "10.0.11.25"
+ ],
+ [
+ "10.0.11.26",
+ "10.0.11.26"
+ ],
+ [
+ "10.0.11.27",
+ "10.0.11.27"
+ ],
+ [
+ "10.0.11.28",
+ "10.0.11.28"
+ ],
+ [
+ "10.0.11.29",
+ "10.0.11.29"
+ ],
+ [
+ "10.0.11.30",
+ "10.0.11.30"
+ ],
+ [
+ "10.0.11.31",
+ "10.0.11.31"
+ ],
+ [
+ "10.0.12.1",
+ "10.0.12.1"
+ ],
+ [
+ "10.0.12.2",
+ "10.0.12.2"
+ ],
+ [
+ "10.0.12.3",
+ "10.0.12.3"
+ ],
+ [
+ "10.0.12.4",
+ "10.0.12.4"
+ ],
+ [
+ "10.0.12.5",
+ "10.0.12.5"
+ ],
+ [
+ "10.0.12.6",
+ "10.0.12.6"
+ ],
+ [
+ "10.0.12.7",
+ "10.0.12.7"
+ ],
+ [
+ "10.0.12.8",
+ "10.0.12.8"
+ ],
+ [
+ "10.0.12.9",
+ "10.0.12.9"
+ ],
+ [
+ "10.0.12.10",
+ "10.0.12.10"
+ ],
+ [
+ "10.0.12.11",
+ "10.0.12.11"
+ ],
+ [
+ "10.0.12.12",
+ "10.0.12.12"
+ ],
+ [
+ "10.0.12.13",
+ "10.0.12.13"
+ ],
+ [
+ "10.0.12.14",
+ "10.0.12.14"
+ ],
+ [
+ "10.0.12.15",
+ "10.0.12.15"
+ ],
+ [
+ "10.0.12.16",
+ "10.0.12.16"
+ ],
+ [
+ "10.0.12.17",
+ "10.0.12.17"
+ ],
+ [
+ "10.0.12.18",
+ "10.0.12.18"
+ ],
+ [
+ "10.0.12.19",
+ "10.0.12.19"
+ ],
+ [
+ "10.0.12.20",
+ "10.0.12.20"
+ ],
+ [
+ "10.0.12.21",
+ "10.0.12.21"
+ ],
+ [
+ "10.0.12.22",
+ "10.0.12.22"
+ ],
+ [
+ "10.0.12.23",
+ "10.0.12.23"
+ ],
+ [
+ "10.0.12.24",
+ "10.0.12.24"
+ ],
+ [
+ "10.0.12.25",
+ "10.0.12.25"
+ ],
+ [
+ "10.0.12.26",
+ "10.0.12.26"
+ ],
+ [
+ "10.0.12.27",
+ "10.0.12.27"
+ ],
+ [
+ "10.0.12.28",
+ "10.0.12.28"
+ ],
+ [
+ "10.0.12.29",
+ "10.0.12.29"
+ ],
+ [
+ "10.0.12.30",
+ "10.0.12.30"
+ ],
+ [
+ "10.0.12.31",
+ "10.0.12.31"
+ ],
+ [
+ "10.0.13.1",
+ "10.0.13.1"
+ ],
+ [
+ "10.0.13.2",
+ "10.0.13.2"
+ ],
+ [
+ "10.0.13.3",
+ "10.0.13.3"
+ ],
+ [
+ "10.0.13.4",
+ "10.0.13.4"
+ ],
+ [
+ "10.0.13.5",
+ "10.0.13.5"
+ ],
+ [
+ "10.0.13.6",
+ "10.0.13.6"
+ ],
+ [
+ "10.0.13.7",
+ "10.0.13.7"
+ ],
+ [
+ "10.0.13.8",
+ "10.0.13.8"
+ ],
+ [
+ "10.0.13.9",
+ "10.0.13.9"
+ ],
+ [
+ "10.0.13.10",
+ "10.0.13.10"
+ ],
+ [
+ "10.0.13.11",
+ "10.0.13.11"
+ ],
+ [
+ "10.0.13.12",
+ "10.0.13.12"
+ ],
+ [
+ "10.0.13.13",
+ "10.0.13.13"
+ ],
+ [
+ "10.0.13.14",
+ "10.0.13.14"
+ ],
+ [
+ "10.0.13.15",
+ "10.0.13.15"
+ ],
+ [
+ "10.0.13.16",
+ "10.0.13.16"
+ ],
+ [
+ "10.0.13.17",
+ "10.0.13.17"
+ ],
+ [
+ "10.0.13.18",
+ "10.0.13.18"
+ ],
+ [
+ "10.0.13.19",
+ "10.0.13.19"
+ ],
+ [
+ "10.0.13.20",
+ "10.0.13.20"
+ ],
+ [
+ "10.0.13.21",
+ "10.0.13.21"
+ ],
+ [
+ "10.0.13.22",
+ "10.0.13.22"
+ ],
+ [
+ "10.0.13.23",
+ "10.0.13.23"
+ ],
+ [
+ "10.0.13.24",
+ "10.0.13.24"
+ ],
+ [
+ "10.0.13.25",
+ "10.0.13.25"
+ ],
+ [
+ "10.0.13.26",
+ "10.0.13.26"
+ ],
+ [
+ "10.0.13.27",
+ "10.0.13.27"
+ ],
+ [
+ "10.0.13.28",
+ "10.0.13.28"
+ ],
+ [
+ "10.0.13.29",
+ "10.0.13.29"
+ ],
+ [
+ "10.0.13.30",
+ "10.0.13.30"
+ ],
+ [
+ "10.0.13.31",
+ "10.0.13.31"
+ ],
+ [
+ "10.0.14.1",
+ "10.0.14.1"
+ ],
+ [
+ "10.0.14.2",
+ "10.0.14.2"
+ ],
+ [
+ "10.0.14.3",
+ "10.0.14.3"
+ ],
+ [
+ "10.0.14.4",
+ "10.0.14.4"
+ ],
+ [
+ "10.0.14.5",
+ "10.0.14.5"
+ ],
+ [
+ "10.0.14.6",
+ "10.0.14.6"
+ ],
+ [
+ "10.0.14.7",
+ "10.0.14.7"
+ ],
+ [
+ "10.0.14.8",
+ "10.0.14.8"
+ ],
+ [
+ "10.0.14.9",
+ "10.0.14.9"
+ ],
+ [
+ "10.0.14.10",
+ "10.0.14.10"
+ ],
+ [
+ "10.0.14.11",
+ "10.0.14.11"
+ ],
+ [
+ "10.0.14.12",
+ "10.0.14.12"
+ ],
+ [
+ "10.0.14.13",
+ "10.0.14.13"
+ ],
+ [
+ "10.0.14.14",
+ "10.0.14.14"
+ ],
+ [
+ "10.0.14.15",
+ "10.0.14.15"
+ ],
+ [
+ "10.0.14.16",
+ "10.0.14.16"
+ ],
+ [
+ "10.0.14.17",
+ "10.0.14.17"
+ ],
+ [
+ "10.0.14.18",
+ "10.0.14.18"
+ ],
+ [
+ "10.0.14.19",
+ "10.0.14.19"
+ ],
+ [
+ "10.0.14.20",
+ "10.0.14.20"
+ ],
+ [
+ "10.0.14.21",
+ "10.0.14.21"
+ ],
+ [
+ "10.0.14.22",
+ "10.0.14.22"
+ ],
+ [
+ "10.0.14.23",
+ "10.0.14.23"
+ ],
+ [
+ "10.0.14.24",
+ "10.0.14.24"
+ ],
+ [
+ "10.0.14.25",
+ "10.0.14.25"
+ ],
+ [
+ "10.0.14.26",
+ "10.0.14.26"
+ ],
+ [
+ "10.0.14.27",
+ "10.0.14.27"
+ ],
+ [
+ "10.0.14.28",
+ "10.0.14.28"
+ ],
+ [
+ "10.0.14.29",
+ "10.0.14.29"
+ ],
+ [
+ "10.0.14.30",
+ "10.0.14.30"
+ ],
+ [
+ "10.0.14.31",
+ "10.0.14.31"
+ ],
+ [
+ "10.0.15.1",
+ "10.0.15.1"
+ ],
+ [
+ "10.0.15.2",
+ "10.0.15.2"
+ ],
+ [
+ "10.0.15.3",
+ "10.0.15.3"
+ ],
+ [
+ "10.0.15.4",
+ "10.0.15.4"
+ ],
+ [
+ "10.0.15.5",
+ "10.0.15.5"
+ ],
+ [
+ "10.0.15.6",
+ "10.0.15.6"
+ ],
+ [
+ "10.0.15.7",
+ "10.0.15.7"
+ ],
+ [
+ "10.0.15.8",
+ "10.0.15.8"
+ ],
+ [
+ "10.0.15.9",
+ "10.0.15.9"
+ ],
+ [
+ "10.0.15.10",
+ "10.0.15.10"
+ ],
+ [
+ "10.0.15.11",
+ "10.0.15.11"
+ ],
+ [
+ "10.0.15.12",
+ "10.0.15.12"
+ ],
+ [
+ "10.0.15.13",
+ "10.0.15.13"
+ ],
+ [
+ "10.0.15.14",
+ "10.0.15.14"
+ ],
+ [
+ "10.0.15.15",
+ "10.0.15.15"
+ ],
+ [
+ "10.0.15.16",
+ "10.0.15.16"
+ ],
+ [
+ "10.0.15.17",
+ "10.0.15.17"
+ ],
+ [
+ "10.0.15.18",
+ "10.0.15.18"
+ ],
+ [
+ "10.0.15.19",
+ "10.0.15.19"
+ ],
+ [
+ "10.0.15.20",
+ "10.0.15.20"
+ ],
+ [
+ "10.0.15.21",
+ "10.0.15.21"
+ ],
+ [
+ "10.0.15.22",
+ "10.0.15.22"
+ ],
+ [
+ "10.0.15.23",
+ "10.0.15.23"
+ ],
+ [
+ "10.0.15.24",
+ "10.0.15.24"
+ ],
+ [
+ "10.0.15.25",
+ "10.0.15.25"
+ ],
+ [
+ "10.0.15.26",
+ "10.0.15.26"
+ ],
+ [
+ "10.0.15.27",
+ "10.0.15.27"
+ ],
+ [
+ "10.0.15.28",
+ "10.0.15.28"
+ ],
+ [
+ "10.0.15.29",
+ "10.0.15.29"
+ ],
+ [
+ "10.0.15.30",
+ "10.0.15.30"
+ ],
+ [
+ "10.0.15.31",
+ "10.0.15.31"
+ ],
+ [
+ "10.0.16.1",
+ "10.0.16.1"
+ ],
+ [
+ "10.0.16.2",
+ "10.0.16.2"
+ ],
+ [
+ "10.0.16.3",
+ "10.0.16.3"
+ ],
+ [
+ "10.0.16.4",
+ "10.0.16.4"
+ ],
+ [
+ "10.0.16.5",
+ "10.0.16.5"
+ ],
+ [
+ "10.0.16.6",
+ "10.0.16.6"
+ ],
+ [
+ "10.0.16.7",
+ "10.0.16.7"
+ ],
+ [
+ "10.0.16.8",
+ "10.0.16.8"
+ ],
+ [
+ "10.0.16.9",
+ "10.0.16.9"
+ ],
+ [
+ "10.0.16.10",
+ "10.0.16.10"
+ ],
+ [
+ "10.0.16.11",
+ "10.0.16.11"
+ ],
+ [
+ "10.0.16.12",
+ "10.0.16.12"
+ ],
+ [
+ "10.0.16.13",
+ "10.0.16.13"
+ ],
+ [
+ "10.0.16.14",
+ "10.0.16.14"
+ ],
+ [
+ "10.0.16.15",
+ "10.0.16.15"
+ ],
+ [
+ "10.0.16.16",
+ "10.0.16.16"
+ ],
+ [
+ "10.0.16.17",
+ "10.0.16.17"
+ ],
+ [
+ "10.0.16.18",
+ "10.0.16.18"
+ ],
+ [
+ "10.0.16.19",
+ "10.0.16.19"
+ ],
+ [
+ "10.0.16.20",
+ "10.0.16.20"
+ ],
+ [
+ "10.0.16.21",
+ "10.0.16.21"
+ ],
+ [
+ "10.0.16.22",
+ "10.0.16.22"
+ ],
+ [
+ "10.0.16.23",
+ "10.0.16.23"
+ ],
+ [
+ "10.0.16.24",
+ "10.0.16.24"
+ ],
+ [
+ "10.0.16.25",
+ "10.0.16.25"
+ ],
+ [
+ "10.0.16.26",
+ "10.0.16.26"
+ ],
+ [
+ "10.0.16.27",
+ "10.0.16.27"
+ ],
+ [
+ "10.0.16.28",
+ "10.0.16.28"
+ ],
+ [
+ "10.0.16.29",
+ "10.0.16.29"
+ ],
+ [
+ "10.0.16.30",
+ "10.0.16.30"
+ ],
+ [
+ "10.0.16.31",
+ "10.0.16.31"
+ ],
+ [
+ "10.0.17.1",
+ "10.0.17.1"
+ ],
+ [
+ "10.0.17.2",
+ "10.0.17.2"
+ ],
+ [
+ "10.0.17.3",
+ "10.0.17.3"
+ ],
+ [
+ "10.0.17.4",
+ "10.0.17.4"
+ ],
+ [
+ "10.0.17.5",
+ "10.0.17.5"
+ ],
+ [
+ "10.0.17.6",
+ "10.0.17.6"
+ ],
+ [
+ "10.0.17.7",
+ "10.0.17.7"
+ ],
+ [
+ "10.0.17.8",
+ "10.0.17.8"
+ ],
+ [
+ "10.0.17.9",
+ "10.0.17.9"
+ ],
+ [
+ "10.0.17.10",
+ "10.0.17.10"
+ ],
+ [
+ "10.0.17.11",
+ "10.0.17.11"
+ ],
+ [
+ "10.0.17.12",
+ "10.0.17.12"
+ ],
+ [
+ "10.0.17.13",
+ "10.0.17.13"
+ ],
+ [
+ "10.0.17.14",
+ "10.0.17.14"
+ ],
+ [
+ "10.0.17.15",
+ "10.0.17.15"
+ ],
+ [
+ "10.0.17.16",
+ "10.0.17.16"
+ ],
+ [
+ "10.0.17.17",
+ "10.0.17.17"
+ ],
+ [
+ "10.0.17.18",
+ "10.0.17.18"
+ ],
+ [
+ "10.0.17.19",
+ "10.0.17.19"
+ ],
+ [
+ "10.0.17.20",
+ "10.0.17.20"
+ ],
+ [
+ "10.0.17.21",
+ "10.0.17.21"
+ ],
+ [
+ "10.0.17.22",
+ "10.0.17.22"
+ ],
+ [
+ "10.0.17.23",
+ "10.0.17.23"
+ ],
+ [
+ "10.0.17.24",
+ "10.0.17.24"
+ ],
+ [
+ "10.0.17.25",
+ "10.0.17.25"
+ ],
+ [
+ "10.0.17.26",
+ "10.0.17.26"
+ ],
+ [
+ "10.0.17.27",
+ "10.0.17.27"
+ ],
+ [
+ "10.0.17.28",
+ "10.0.17.28"
+ ],
+ [
+ "10.0.17.29",
+ "10.0.17.29"
+ ],
+ [
+ "10.0.17.30",
+ "10.0.17.30"
+ ],
+ [
+ "10.0.17.31",
+ "10.0.17.31"
+ ],
+ [
+ "10.0.18.1",
+ "10.0.18.1"
+ ],
+ [
+ "10.0.18.2",
+ "10.0.18.2"
+ ],
+ [
+ "10.0.18.3",
+ "10.0.18.3"
+ ],
+ [
+ "10.0.18.4",
+ "10.0.18.4"
+ ],
+ [
+ "10.0.18.5",
+ "10.0.18.5"
+ ],
+ [
+ "10.0.18.6",
+ "10.0.18.6"
+ ],
+ [
+ "10.0.18.7",
+ "10.0.18.7"
+ ],
+ [
+ "10.0.18.8",
+ "10.0.18.8"
+ ],
+ [
+ "10.0.18.9",
+ "10.0.18.9"
+ ],
+ [
+ "10.0.18.10",
+ "10.0.18.10"
+ ],
+ [
+ "10.0.18.11",
+ "10.0.18.11"
+ ],
+ [
+ "10.0.18.12",
+ "10.0.18.12"
+ ],
+ [
+ "10.0.18.13",
+ "10.0.18.13"
+ ],
+ [
+ "10.0.18.14",
+ "10.0.18.14"
+ ],
+ [
+ "10.0.18.15",
+ "10.0.18.15"
+ ],
+ [
+ "10.0.18.16",
+ "10.0.18.16"
+ ],
+ [
+ "10.0.18.17",
+ "10.0.18.17"
+ ],
+ [
+ "10.0.18.18",
+ "10.0.18.18"
+ ],
+ [
+ "10.0.18.19",
+ "10.0.18.19"
+ ],
+ [
+ "10.0.18.20",
+ "10.0.18.20"
+ ],
+ [
+ "10.0.18.21",
+ "10.0.18.21"
+ ],
+ [
+ "10.0.18.22",
+ "10.0.18.22"
+ ],
+ [
+ "10.0.18.23",
+ "10.0.18.23"
+ ],
+ [
+ "10.0.18.24",
+ "10.0.18.24"
+ ],
+ [
+ "10.0.18.25",
+ "10.0.18.25"
+ ],
+ [
+ "10.0.18.26",
+ "10.0.18.26"
+ ],
+ [
+ "10.0.18.27",
+ "10.0.18.27"
+ ],
+ [
+ "10.0.18.28",
+ "10.0.18.28"
+ ],
+ [
+ "10.0.18.29",
+ "10.0.18.29"
+ ],
+ [
+ "10.0.18.30",
+ "10.0.18.30"
+ ],
+ [
+ "10.0.18.31",
+ "10.0.18.31"
+ ],
+ [
+ "10.0.19.1",
+ "10.0.19.1"
+ ],
+ [
+ "10.0.19.2",
+ "10.0.19.2"
+ ],
+ [
+ "10.0.19.3",
+ "10.0.19.3"
+ ],
+ [
+ "10.0.19.4",
+ "10.0.19.4"
+ ],
+ [
+ "10.0.19.5",
+ "10.0.19.5"
+ ],
+ [
+ "10.0.19.6",
+ "10.0.19.6"
+ ],
+ [
+ "10.0.19.7",
+ "10.0.19.7"
+ ],
+ [
+ "10.0.19.8",
+ "10.0.19.8"
+ ],
+ [
+ "10.0.19.9",
+ "10.0.19.9"
+ ],
+ [
+ "10.0.19.10",
+ "10.0.19.10"
+ ],
+ [
+ "10.0.19.11",
+ "10.0.19.11"
+ ],
+ [
+ "10.0.19.12",
+ "10.0.19.12"
+ ],
+ [
+ "10.0.19.13",
+ "10.0.19.13"
+ ],
+ [
+ "10.0.19.14",
+ "10.0.19.14"
+ ],
+ [
+ "10.0.19.15",
+ "10.0.19.15"
+ ],
+ [
+ "10.0.19.16",
+ "10.0.19.16"
+ ],
+ [
+ "10.0.19.17",
+ "10.0.19.17"
+ ],
+ [
+ "10.0.19.18",
+ "10.0.19.18"
+ ],
+ [
+ "10.0.19.19",
+ "10.0.19.19"
+ ],
+ [
+ "10.0.19.20",
+ "10.0.19.20"
+ ],
+ [
+ "10.0.19.21",
+ "10.0.19.21"
+ ],
+ [
+ "10.0.19.22",
+ "10.0.19.22"
+ ],
+ [
+ "10.0.19.23",
+ "10.0.19.23"
+ ],
+ [
+ "10.0.19.24",
+ "10.0.19.24"
+ ],
+ [
+ "10.0.19.25",
+ "10.0.19.25"
+ ],
+ [
+ "10.0.19.26",
+ "10.0.19.26"
+ ],
+ [
+ "10.0.19.27",
+ "10.0.19.27"
+ ],
+ [
+ "10.0.19.28",
+ "10.0.19.28"
+ ],
+ [
+ "10.0.19.29",
+ "10.0.19.29"
+ ],
+ [
+ "10.0.19.30",
+ "10.0.19.30"
+ ],
+ [
+ "10.0.19.31",
+ "10.0.19.31"
+ ],
+ [
+ "10.0.20.1",
+ "10.0.20.1"
+ ],
+ [
+ "10.0.20.2",
+ "10.0.20.2"
+ ],
+ [
+ "10.0.20.3",
+ "10.0.20.3"
+ ],
+ [
+ "10.0.20.4",
+ "10.0.20.4"
+ ],
+ [
+ "10.0.20.5",
+ "10.0.20.5"
+ ],
+ [
+ "10.0.20.6",
+ "10.0.20.6"
+ ],
+ [
+ "10.0.20.7",
+ "10.0.20.7"
+ ],
+ [
+ "10.0.20.8",
+ "10.0.20.8"
+ ],
+ [
+ "10.0.20.9",
+ "10.0.20.9"
+ ],
+ [
+ "10.0.20.10",
+ "10.0.20.10"
+ ],
+ [
+ "10.0.20.11",
+ "10.0.20.11"
+ ],
+ [
+ "10.0.20.12",
+ "10.0.20.12"
+ ],
+ [
+ "10.0.20.13",
+ "10.0.20.13"
+ ],
+ [
+ "10.0.20.14",
+ "10.0.20.14"
+ ],
+ [
+ "10.0.20.15",
+ "10.0.20.15"
+ ],
+ [
+ "10.0.20.16",
+ "10.0.20.16"
+ ],
+ [
+ "10.0.20.17",
+ "10.0.20.17"
+ ],
+ [
+ "10.0.20.18",
+ "10.0.20.18"
+ ],
+ [
+ "10.0.20.19",
+ "10.0.20.19"
+ ],
+ [
+ "10.0.20.20",
+ "10.0.20.20"
+ ],
+ [
+ "10.0.20.21",
+ "10.0.20.21"
+ ],
+ [
+ "10.0.20.22",
+ "10.0.20.22"
+ ],
+ [
+ "10.0.20.23",
+ "10.0.20.23"
+ ],
+ [
+ "10.0.20.24",
+ "10.0.20.24"
+ ],
+ [
+ "10.0.20.25",
+ "10.0.20.25"
+ ],
+ [
+ "10.0.20.26",
+ "10.0.20.26"
+ ],
+ [
+ "10.0.20.27",
+ "10.0.20.27"
+ ],
+ [
+ "10.0.20.28",
+ "10.0.20.28"
+ ],
+ [
+ "10.0.20.29",
+ "10.0.20.29"
+ ],
+ [
+ "10.0.20.30",
+ "10.0.20.30"
+ ],
+ [
+ "10.0.20.31",
+ "10.0.20.31"
+ ],
+ [
+ "10.0.21.1",
+ "10.0.21.1"
+ ],
+ [
+ "10.0.21.2",
+ "10.0.21.2"
+ ],
+ [
+ "10.0.21.3",
+ "10.0.21.3"
+ ],
+ [
+ "10.0.21.4",
+ "10.0.21.4"
+ ],
+ [
+ "10.0.21.5",
+ "10.0.21.5"
+ ],
+ [
+ "10.0.21.6",
+ "10.0.21.6"
+ ],
+ [
+ "10.0.21.7",
+ "10.0.21.7"
+ ],
+ [
+ "10.0.21.8",
+ "10.0.21.8"
+ ],
+ [
+ "10.0.21.9",
+ "10.0.21.9"
+ ],
+ [
+ "10.0.21.10",
+ "10.0.21.10"
+ ],
+ [
+ "10.0.21.11",
+ "10.0.21.11"
+ ],
+ [
+ "10.0.21.12",
+ "10.0.21.12"
+ ],
+ [
+ "10.0.21.13",
+ "10.0.21.13"
+ ],
+ [
+ "10.0.21.14",
+ "10.0.21.14"
+ ],
+ [
+ "10.0.21.15",
+ "10.0.21.15"
+ ],
+ [
+ "10.0.21.16",
+ "10.0.21.16"
+ ],
+ [
+ "10.0.21.17",
+ "10.0.21.17"
+ ],
+ [
+ "10.0.21.18",
+ "10.0.21.18"
+ ],
+ [
+ "10.0.21.19",
+ "10.0.21.19"
+ ],
+ [
+ "10.0.21.20",
+ "10.0.21.20"
+ ],
+ [
+ "10.0.21.21",
+ "10.0.21.21"
+ ],
+ [
+ "10.0.21.22",
+ "10.0.21.22"
+ ],
+ [
+ "10.0.21.23",
+ "10.0.21.23"
+ ],
+ [
+ "10.0.21.24",
+ "10.0.21.24"
+ ],
+ [
+ "10.0.21.25",
+ "10.0.21.25"
+ ],
+ [
+ "10.0.21.26",
+ "10.0.21.26"
+ ],
+ [
+ "10.0.21.27",
+ "10.0.21.27"
+ ],
+ [
+ "10.0.21.28",
+ "10.0.21.28"
+ ],
+ [
+ "10.0.21.29",
+ "10.0.21.29"
+ ],
+ [
+ "10.0.21.30",
+ "10.0.21.30"
+ ],
+ [
+ "10.0.21.31",
+ "10.0.21.31"
+ ],
+ [
+ "10.0.22.1",
+ "10.0.22.1"
+ ],
+ [
+ "10.0.22.2",
+ "10.0.22.2"
+ ],
+ [
+ "10.0.22.3",
+ "10.0.22.3"
+ ],
+ [
+ "10.0.22.4",
+ "10.0.22.4"
+ ],
+ [
+ "10.0.22.5",
+ "10.0.22.5"
+ ],
+ [
+ "10.0.22.6",
+ "10.0.22.6"
+ ],
+ [
+ "10.0.22.7",
+ "10.0.22.7"
+ ],
+ [
+ "10.0.22.8",
+ "10.0.22.8"
+ ],
+ [
+ "10.0.22.9",
+ "10.0.22.9"
+ ],
+ [
+ "10.0.22.10",
+ "10.0.22.10"
+ ],
+ [
+ "10.0.22.11",
+ "10.0.22.11"
+ ],
+ [
+ "10.0.22.12",
+ "10.0.22.12"
+ ],
+ [
+ "10.0.22.13",
+ "10.0.22.13"
+ ],
+ [
+ "10.0.22.14",
+ "10.0.22.14"
+ ],
+ [
+ "10.0.22.15",
+ "10.0.22.15"
+ ],
+ [
+ "10.0.22.16",
+ "10.0.22.16"
+ ],
+ [
+ "10.0.22.17",
+ "10.0.22.17"
+ ],
+ [
+ "10.0.22.18",
+ "10.0.22.18"
+ ],
+ [
+ "10.0.22.19",
+ "10.0.22.19"
+ ],
+ [
+ "10.0.22.20",
+ "10.0.22.20"
+ ],
+ [
+ "10.0.22.21",
+ "10.0.22.21"
+ ],
+ [
+ "10.0.22.22",
+ "10.0.22.22"
+ ],
+ [
+ "10.0.22.23",
+ "10.0.22.23"
+ ],
+ [
+ "10.0.22.24",
+ "10.0.22.24"
+ ],
+ [
+ "10.0.22.25",
+ "10.0.22.25"
+ ],
+ [
+ "10.0.22.26",
+ "10.0.22.26"
+ ],
+ [
+ "10.0.22.27",
+ "10.0.22.27"
+ ],
+ [
+ "10.0.22.28",
+ "10.0.22.28"
+ ],
+ [
+ "10.0.22.29",
+ "10.0.22.29"
+ ],
+ [
+ "10.0.22.30",
+ "10.0.22.30"
+ ],
+ [
+ "10.0.22.31",
+ "10.0.22.31"
+ ],
+ [
+ "10.0.23.1",
+ "10.0.23.1"
+ ],
+ [
+ "10.0.23.2",
+ "10.0.23.2"
+ ],
+ [
+ "10.0.23.3",
+ "10.0.23.3"
+ ],
+ [
+ "10.0.23.4",
+ "10.0.23.4"
+ ],
+ [
+ "10.0.23.5",
+ "10.0.23.5"
+ ],
+ [
+ "10.0.23.6",
+ "10.0.23.6"
+ ],
+ [
+ "10.0.23.7",
+ "10.0.23.7"
+ ],
+ [
+ "10.0.23.8",
+ "10.0.23.8"
+ ],
+ [
+ "10.0.23.9",
+ "10.0.23.9"
+ ],
+ [
+ "10.0.23.10",
+ "10.0.23.10"
+ ],
+ [
+ "10.0.23.11",
+ "10.0.23.11"
+ ],
+ [
+ "10.0.23.12",
+ "10.0.23.12"
+ ],
+ [
+ "10.0.23.13",
+ "10.0.23.13"
+ ],
+ [
+ "10.0.23.14",
+ "10.0.23.14"
+ ],
+ [
+ "10.0.23.15",
+ "10.0.23.15"
+ ],
+ [
+ "10.0.23.16",
+ "10.0.23.16"
+ ],
+ [
+ "10.0.23.17",
+ "10.0.23.17"
+ ],
+ [
+ "10.0.23.18",
+ "10.0.23.18"
+ ],
+ [
+ "10.0.23.19",
+ "10.0.23.19"
+ ],
+ [
+ "10.0.23.20",
+ "10.0.23.20"
+ ],
+ [
+ "10.0.23.21",
+ "10.0.23.21"
+ ],
+ [
+ "10.0.23.22",
+ "10.0.23.22"
+ ],
+ [
+ "10.0.23.23",
+ "10.0.23.23"
+ ],
+ [
+ "10.0.23.24",
+ "10.0.23.24"
+ ],
+ [
+ "10.0.23.25",
+ "10.0.23.25"
+ ],
+ [
+ "10.0.23.26",
+ "10.0.23.26"
+ ],
+ [
+ "10.0.23.27",
+ "10.0.23.27"
+ ],
+ [
+ "10.0.23.28",
+ "10.0.23.28"
+ ],
+ [
+ "10.0.23.29",
+ "10.0.23.29"
+ ],
+ [
+ "10.0.23.30",
+ "10.0.23.30"
+ ],
+ [
+ "10.0.23.31",
+ "10.0.23.31"
+ ],
+ [
+ "10.0.24.1",
+ "10.0.24.1"
+ ],
+ [
+ "10.0.24.2",
+ "10.0.24.2"
+ ],
+ [
+ "10.0.24.3",
+ "10.0.24.3"
+ ],
+ [
+ "10.0.24.4",
+ "10.0.24.4"
+ ],
+ [
+ "10.0.24.5",
+ "10.0.24.5"
+ ],
+ [
+ "10.0.24.6",
+ "10.0.24.6"
+ ],
+ [
+ "10.0.24.7",
+ "10.0.24.7"
+ ],
+ [
+ "10.0.24.8",
+ "10.0.24.8"
+ ],
+ [
+ "10.0.24.9",
+ "10.0.24.9"
+ ],
+ [
+ "10.0.24.10",
+ "10.0.24.10"
+ ],
+ [
+ "10.0.24.11",
+ "10.0.24.11"
+ ],
+ [
+ "10.0.24.12",
+ "10.0.24.12"
+ ],
+ [
+ "10.0.24.13",
+ "10.0.24.13"
+ ],
+ [
+ "10.0.24.14",
+ "10.0.24.14"
+ ],
+ [
+ "10.0.24.15",
+ "10.0.24.15"
+ ],
+ [
+ "10.0.24.16",
+ "10.0.24.16"
+ ],
+ [
+ "10.0.24.17",
+ "10.0.24.17"
+ ],
+ [
+ "10.0.24.18",
+ "10.0.24.18"
+ ],
+ [
+ "10.0.24.19",
+ "10.0.24.19"
+ ],
+ [
+ "10.0.24.20",
+ "10.0.24.20"
+ ],
+ [
+ "10.0.24.21",
+ "10.0.24.21"
+ ],
+ [
+ "10.0.24.22",
+ "10.0.24.22"
+ ],
+ [
+ "10.0.24.23",
+ "10.0.24.23"
+ ],
+ [
+ "10.0.24.24",
+ "10.0.24.24"
+ ],
+ [
+ "10.0.24.25",
+ "10.0.24.25"
+ ],
+ [
+ "10.0.24.26",
+ "10.0.24.26"
+ ],
+ [
+ "10.0.24.27",
+ "10.0.24.27"
+ ],
+ [
+ "10.0.24.28",
+ "10.0.24.28"
+ ],
+ [
+ "10.0.24.29",
+ "10.0.24.29"
+ ],
+ [
+ "10.0.24.30",
+ "10.0.24.30"
+ ],
+ [
+ "10.0.24.31",
+ "10.0.24.31"
+ ],
+ [
+ "10.0.25.1",
+ "10.0.25.1"
+ ],
+ [
+ "10.0.25.2",
+ "10.0.25.2"
+ ],
+ [
+ "10.0.25.3",
+ "10.0.25.3"
+ ],
+ [
+ "10.0.25.4",
+ "10.0.25.4"
+ ],
+ [
+ "10.0.25.5",
+ "10.0.25.5"
+ ],
+ [
+ "10.0.25.6",
+ "10.0.25.6"
+ ],
+ [
+ "10.0.25.7",
+ "10.0.25.7"
+ ],
+ [
+ "10.0.25.8",
+ "10.0.25.8"
+ ],
+ [
+ "10.0.25.9",
+ "10.0.25.9"
+ ],
+ [
+ "10.0.25.10",
+ "10.0.25.10"
+ ],
+ [
+ "10.0.25.11",
+ "10.0.25.11"
+ ],
+ [
+ "10.0.25.12",
+ "10.0.25.12"
+ ],
+ [
+ "10.0.25.13",
+ "10.0.25.13"
+ ],
+ [
+ "10.0.25.14",
+ "10.0.25.14"
+ ],
+ [
+ "10.0.25.15",
+ "10.0.25.15"
+ ],
+ [
+ "10.0.25.16",
+ "10.0.25.16"
+ ],
+ [
+ "10.0.25.17",
+ "10.0.25.17"
+ ],
+ [
+ "10.0.25.18",
+ "10.0.25.18"
+ ],
+ [
+ "10.0.25.19",
+ "10.0.25.19"
+ ],
+ [
+ "10.0.25.20",
+ "10.0.25.20"
+ ],
+ [
+ "10.0.25.21",
+ "10.0.25.21"
+ ],
+ [
+ "10.0.25.22",
+ "10.0.25.22"
+ ],
+ [
+ "10.0.25.23",
+ "10.0.25.23"
+ ],
+ [
+ "10.0.25.24",
+ "10.0.25.24"
+ ],
+ [
+ "10.0.25.25",
+ "10.0.25.25"
+ ],
+ [
+ "10.0.25.26",
+ "10.0.25.26"
+ ],
+ [
+ "10.0.25.27",
+ "10.0.25.27"
+ ],
+ [
+ "10.0.25.28",
+ "10.0.25.28"
+ ],
+ [
+ "10.0.25.29",
+ "10.0.25.29"
+ ],
+ [
+ "10.0.25.30",
+ "10.0.25.30"
+ ],
+ [
+ "10.0.25.31",
+ "10.0.25.31"
+ ],
+ [
+ "10.0.26.1",
+ "10.0.26.1"
+ ],
+ [
+ "10.0.26.2",
+ "10.0.26.2"
+ ],
+ [
+ "10.0.26.3",
+ "10.0.26.3"
+ ],
+ [
+ "10.0.26.4",
+ "10.0.26.4"
+ ],
+ [
+ "10.0.26.5",
+ "10.0.26.5"
+ ],
+ [
+ "10.0.26.6",
+ "10.0.26.6"
+ ],
+ [
+ "10.0.26.7",
+ "10.0.26.7"
+ ],
+ [
+ "10.0.26.8",
+ "10.0.26.8"
+ ],
+ [
+ "10.0.26.9",
+ "10.0.26.9"
+ ],
+ [
+ "10.0.26.10",
+ "10.0.26.10"
+ ],
+ [
+ "10.0.26.11",
+ "10.0.26.11"
+ ],
+ [
+ "10.0.26.12",
+ "10.0.26.12"
+ ],
+ [
+ "10.0.26.13",
+ "10.0.26.13"
+ ],
+ [
+ "10.0.26.14",
+ "10.0.26.14"
+ ],
+ [
+ "10.0.26.15",
+ "10.0.26.15"
+ ],
+ [
+ "10.0.26.16",
+ "10.0.26.16"
+ ],
+ [
+ "10.0.26.17",
+ "10.0.26.17"
+ ],
+ [
+ "10.0.26.18",
+ "10.0.26.18"
+ ],
+ [
+ "10.0.26.19",
+ "10.0.26.19"
+ ],
+ [
+ "10.0.26.20",
+ "10.0.26.20"
+ ],
+ [
+ "10.0.26.21",
+ "10.0.26.21"
+ ],
+ [
+ "10.0.26.22",
+ "10.0.26.22"
+ ],
+ [
+ "10.0.26.23",
+ "10.0.26.23"
+ ],
+ [
+ "10.0.26.24",
+ "10.0.26.24"
+ ],
+ [
+ "10.0.26.25",
+ "10.0.26.25"
+ ],
+ [
+ "10.0.26.26",
+ "10.0.26.26"
+ ],
+ [
+ "10.0.26.27",
+ "10.0.26.27"
+ ],
+ [
+ "10.0.26.28",
+ "10.0.26.28"
+ ],
+ [
+ "10.0.26.29",
+ "10.0.26.29"
+ ],
+ [
+ "10.0.26.30",
+ "10.0.26.30"
+ ],
+ [
+ "10.0.26.31",
+ "10.0.26.31"
+ ],
+ [
+ "10.0.27.1",
+ "10.0.27.1"
+ ],
+ [
+ "10.0.27.2",
+ "10.0.27.2"
+ ],
+ [
+ "10.0.27.3",
+ "10.0.27.3"
+ ],
+ [
+ "10.0.27.4",
+ "10.0.27.4"
+ ],
+ [
+ "10.0.27.5",
+ "10.0.27.5"
+ ],
+ [
+ "10.0.27.6",
+ "10.0.27.6"
+ ],
+ [
+ "10.0.27.7",
+ "10.0.27.7"
+ ],
+ [
+ "10.0.27.8",
+ "10.0.27.8"
+ ],
+ [
+ "10.0.27.9",
+ "10.0.27.9"
+ ],
+ [
+ "10.0.27.10",
+ "10.0.27.10"
+ ],
+ [
+ "10.0.27.11",
+ "10.0.27.11"
+ ],
+ [
+ "10.0.27.12",
+ "10.0.27.12"
+ ],
+ [
+ "10.0.27.13",
+ "10.0.27.13"
+ ],
+ [
+ "10.0.27.14",
+ "10.0.27.14"
+ ],
+ [
+ "10.0.27.15",
+ "10.0.27.15"
+ ],
+ [
+ "10.0.27.16",
+ "10.0.27.16"
+ ],
+ [
+ "10.0.27.17",
+ "10.0.27.17"
+ ],
+ [
+ "10.0.27.18",
+ "10.0.27.18"
+ ],
+ [
+ "10.0.27.19",
+ "10.0.27.19"
+ ],
+ [
+ "10.0.27.20",
+ "10.0.27.20"
+ ],
+ [
+ "10.0.27.21",
+ "10.0.27.21"
+ ],
+ [
+ "10.0.27.22",
+ "10.0.27.22"
+ ],
+ [
+ "10.0.27.23",
+ "10.0.27.23"
+ ],
+ [
+ "10.0.27.24",
+ "10.0.27.24"
+ ],
+ [
+ "10.0.27.25",
+ "10.0.27.25"
+ ],
+ [
+ "10.0.27.26",
+ "10.0.27.26"
+ ],
+ [
+ "10.0.27.27",
+ "10.0.27.27"
+ ],
+ [
+ "10.0.27.28",
+ "10.0.27.28"
+ ],
+ [
+ "10.0.27.29",
+ "10.0.27.29"
+ ],
+ [
+ "10.0.27.30",
+ "10.0.27.30"
+ ],
+ [
+ "10.0.27.31",
+ "10.0.27.31"
+ ],
+ [
+ "10.0.28.1",
+ "10.0.28.1"
+ ],
+ [
+ "10.0.28.2",
+ "10.0.28.2"
+ ],
+ [
+ "10.0.28.3",
+ "10.0.28.3"
+ ],
+ [
+ "10.0.28.4",
+ "10.0.28.4"
+ ],
+ [
+ "10.0.28.5",
+ "10.0.28.5"
+ ],
+ [
+ "10.0.28.6",
+ "10.0.28.6"
+ ],
+ [
+ "10.0.28.7",
+ "10.0.28.7"
+ ],
+ [
+ "10.0.28.8",
+ "10.0.28.8"
+ ],
+ [
+ "10.0.28.9",
+ "10.0.28.9"
+ ],
+ [
+ "10.0.28.10",
+ "10.0.28.10"
+ ],
+ [
+ "10.0.28.11",
+ "10.0.28.11"
+ ],
+ [
+ "10.0.28.12",
+ "10.0.28.12"
+ ],
+ [
+ "10.0.28.13",
+ "10.0.28.13"
+ ],
+ [
+ "10.0.28.14",
+ "10.0.28.14"
+ ],
+ [
+ "10.0.28.15",
+ "10.0.28.15"
+ ],
+ [
+ "10.0.28.16",
+ "10.0.28.16"
+ ],
+ [
+ "10.0.28.17",
+ "10.0.28.17"
+ ],
+ [
+ "10.0.28.18",
+ "10.0.28.18"
+ ],
+ [
+ "10.0.28.19",
+ "10.0.28.19"
+ ],
+ [
+ "10.0.28.20",
+ "10.0.28.20"
+ ],
+ [
+ "10.0.28.21",
+ "10.0.28.21"
+ ],
+ [
+ "10.0.28.22",
+ "10.0.28.22"
+ ],
+ [
+ "10.0.28.23",
+ "10.0.28.23"
+ ],
+ [
+ "10.0.28.24",
+ "10.0.28.24"
+ ],
+ [
+ "10.0.28.25",
+ "10.0.28.25"
+ ],
+ [
+ "10.0.28.26",
+ "10.0.28.26"
+ ],
+ [
+ "10.0.28.27",
+ "10.0.28.27"
+ ],
+ [
+ "10.0.28.28",
+ "10.0.28.28"
+ ],
+ [
+ "10.0.28.29",
+ "10.0.28.29"
+ ],
+ [
+ "10.0.28.30",
+ "10.0.28.30"
+ ],
+ [
+ "10.0.28.31",
+ "10.0.28.31"
+ ],
+ [
+ "10.0.29.1",
+ "10.0.29.1"
+ ],
+ [
+ "10.0.29.2",
+ "10.0.29.2"
+ ],
+ [
+ "10.0.29.3",
+ "10.0.29.3"
+ ],
+ [
+ "10.0.29.4",
+ "10.0.29.4"
+ ],
+ [
+ "10.0.29.5",
+ "10.0.29.5"
+ ],
+ [
+ "10.0.29.6",
+ "10.0.29.6"
+ ],
+ [
+ "10.0.29.7",
+ "10.0.29.7"
+ ],
+ [
+ "10.0.29.8",
+ "10.0.29.8"
+ ],
+ [
+ "10.0.29.9",
+ "10.0.29.9"
+ ],
+ [
+ "10.0.29.10",
+ "10.0.29.10"
+ ],
+ [
+ "10.0.29.11",
+ "10.0.29.11"
+ ],
+ [
+ "10.0.29.12",
+ "10.0.29.12"
+ ],
+ [
+ "10.0.29.13",
+ "10.0.29.13"
+ ],
+ [
+ "10.0.29.14",
+ "10.0.29.14"
+ ],
+ [
+ "10.0.29.15",
+ "10.0.29.15"
+ ],
+ [
+ "10.0.29.16",
+ "10.0.29.16"
+ ],
+ [
+ "10.0.29.17",
+ "10.0.29.17"
+ ],
+ [
+ "10.0.29.18",
+ "10.0.29.18"
+ ],
+ [
+ "10.0.29.19",
+ "10.0.29.19"
+ ],
+ [
+ "10.0.29.20",
+ "10.0.29.20"
+ ],
+ [
+ "10.0.29.21",
+ "10.0.29.21"
+ ],
+ [
+ "10.0.29.22",
+ "10.0.29.22"
+ ],
+ [
+ "10.0.29.23",
+ "10.0.29.23"
+ ],
+ [
+ "10.0.29.24",
+ "10.0.29.24"
+ ],
+ [
+ "10.0.29.25",
+ "10.0.29.25"
+ ],
+ [
+ "10.0.29.26",
+ "10.0.29.26"
+ ],
+ [
+ "10.0.29.27",
+ "10.0.29.27"
+ ],
+ [
+ "10.0.29.28",
+ "10.0.29.28"
+ ],
+ [
+ "10.0.29.29",
+ "10.0.29.29"
+ ],
+ [
+ "10.0.29.30",
+ "10.0.29.30"
+ ],
+ [
+ "10.0.29.31",
+ "10.0.29.31"
+ ],
+ [
+ "10.0.30.1",
+ "10.0.30.1"
+ ],
+ [
+ "10.0.30.2",
+ "10.0.30.2"
+ ],
+ [
+ "10.0.30.3",
+ "10.0.30.3"
+ ],
+ [
+ "10.0.30.4",
+ "10.0.30.4"
+ ],
+ [
+ "10.0.30.5",
+ "10.0.30.5"
+ ],
+ [
+ "10.0.30.6",
+ "10.0.30.6"
+ ],
+ [
+ "10.0.30.7",
+ "10.0.30.7"
+ ],
+ [
+ "10.0.30.8",
+ "10.0.30.8"
+ ],
+ [
+ "10.0.30.9",
+ "10.0.30.9"
+ ],
+ [
+ "10.0.30.10",
+ "10.0.30.10"
+ ],
+ [
+ "10.0.30.11",
+ "10.0.30.11"
+ ],
+ [
+ "10.0.30.12",
+ "10.0.30.12"
+ ],
+ [
+ "10.0.30.13",
+ "10.0.30.13"
+ ],
+ [
+ "10.0.30.14",
+ "10.0.30.14"
+ ],
+ [
+ "10.0.30.15",
+ "10.0.30.15"
+ ],
+ [
+ "10.0.30.16",
+ "10.0.30.16"
+ ],
+ [
+ "10.0.30.17",
+ "10.0.30.17"
+ ],
+ [
+ "10.0.30.18",
+ "10.0.30.18"
+ ],
+ [
+ "10.0.30.19",
+ "10.0.30.19"
+ ],
+ [
+ "10.0.30.20",
+ "10.0.30.20"
+ ],
+ [
+ "10.0.30.21",
+ "10.0.30.21"
+ ],
+ [
+ "10.0.30.22",
+ "10.0.30.22"
+ ],
+ [
+ "10.0.30.23",
+ "10.0.30.23"
+ ],
+ [
+ "10.0.30.24",
+ "10.0.30.24"
+ ],
+ [
+ "10.0.30.25",
+ "10.0.30.25"
+ ],
+ [
+ "10.0.30.26",
+ "10.0.30.26"
+ ],
+ [
+ "10.0.30.27",
+ "10.0.30.27"
+ ],
+ [
+ "10.0.30.28",
+ "10.0.30.28"
+ ],
+ [
+ "10.0.30.29",
+ "10.0.30.29"
+ ],
+ [
+ "10.0.30.30",
+ "10.0.30.30"
+ ],
+ [
+ "10.0.30.31",
+ "10.0.30.31"
+ ],
+ [
+ "10.0.31.1",
+ "10.0.31.1"
+ ],
+ [
+ "10.0.31.2",
+ "10.0.31.2"
+ ],
+ [
+ "10.0.31.3",
+ "10.0.31.3"
+ ],
+ [
+ "10.0.31.4",
+ "10.0.31.4"
+ ],
+ [
+ "10.0.31.5",
+ "10.0.31.5"
+ ],
+ [
+ "10.0.31.6",
+ "10.0.31.6"
+ ],
+ [
+ "10.0.31.7",
+ "10.0.31.7"
+ ],
+ [
+ "10.0.31.8",
+ "10.0.31.8"
+ ],
+ [
+ "10.0.31.9",
+ "10.0.31.9"
+ ],
+ [
+ "10.0.31.10",
+ "10.0.31.10"
+ ],
+ [
+ "10.0.31.11",
+ "10.0.31.11"
+ ],
+ [
+ "10.0.31.12",
+ "10.0.31.12"
+ ],
+ [
+ "10.0.31.13",
+ "10.0.31.13"
+ ],
+ [
+ "10.0.31.14",
+ "10.0.31.14"
+ ],
+ [
+ "10.0.31.15",
+ "10.0.31.15"
+ ],
+ [
+ "10.0.31.16",
+ "10.0.31.16"
+ ],
+ [
+ "10.0.31.17",
+ "10.0.31.17"
+ ],
+ [
+ "10.0.31.18",
+ "10.0.31.18"
+ ],
+ [
+ "10.0.31.19",
+ "10.0.31.19"
+ ],
+ [
+ "10.0.31.20",
+ "10.0.31.20"
+ ],
+ [
+ "10.0.31.21",
+ "10.0.31.21"
+ ],
+ [
+ "10.0.31.22",
+ "10.0.31.22"
+ ],
+ [
+ "10.0.31.23",
+ "10.0.31.23"
+ ],
+ [
+ "10.0.31.24",
+ "10.0.31.24"
+ ],
+ [
+ "10.0.31.25",
+ "10.0.31.25"
+ ],
+ [
+ "10.0.31.26",
+ "10.0.31.26"
+ ],
+ [
+ "10.0.31.27",
+ "10.0.31.27"
+ ],
+ [
+ "10.0.31.28",
+ "10.0.31.28"
+ ],
+ [
+ "10.0.31.29",
+ "10.0.31.29"
+ ],
+ [
+ "10.0.31.30",
+ "10.0.31.30"
+ ],
+ [
+ "10.0.31.31",
+ "10.0.31.31"
+ ]
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/0003map_add_many_elements_0.nft b/tests/shell/testcases/maps/dumps/0003map_add_many_elements_0.nft
new file mode 100644
index 00000000..c651af06
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0003map_add_many_elements_0.nft
@@ -0,0 +1,486 @@
+table ip x {
+ map y {
+ type ipv4_addr : ipv4_addr
+ elements = { 10.0.1.1 : 10.0.1.1, 10.0.1.2 : 10.0.1.2,
+ 10.0.1.3 : 10.0.1.3, 10.0.1.4 : 10.0.1.4,
+ 10.0.1.5 : 10.0.1.5, 10.0.1.6 : 10.0.1.6,
+ 10.0.1.7 : 10.0.1.7, 10.0.1.8 : 10.0.1.8,
+ 10.0.1.9 : 10.0.1.9, 10.0.1.10 : 10.0.1.10,
+ 10.0.1.11 : 10.0.1.11, 10.0.1.12 : 10.0.1.12,
+ 10.0.1.13 : 10.0.1.13, 10.0.1.14 : 10.0.1.14,
+ 10.0.1.15 : 10.0.1.15, 10.0.1.16 : 10.0.1.16,
+ 10.0.1.17 : 10.0.1.17, 10.0.1.18 : 10.0.1.18,
+ 10.0.1.19 : 10.0.1.19, 10.0.1.20 : 10.0.1.20,
+ 10.0.1.21 : 10.0.1.21, 10.0.1.22 : 10.0.1.22,
+ 10.0.1.23 : 10.0.1.23, 10.0.1.24 : 10.0.1.24,
+ 10.0.1.25 : 10.0.1.25, 10.0.1.26 : 10.0.1.26,
+ 10.0.1.27 : 10.0.1.27, 10.0.1.28 : 10.0.1.28,
+ 10.0.1.29 : 10.0.1.29, 10.0.1.30 : 10.0.1.30,
+ 10.0.1.31 : 10.0.1.31, 10.0.2.1 : 10.0.2.1,
+ 10.0.2.2 : 10.0.2.2, 10.0.2.3 : 10.0.2.3,
+ 10.0.2.4 : 10.0.2.4, 10.0.2.5 : 10.0.2.5,
+ 10.0.2.6 : 10.0.2.6, 10.0.2.7 : 10.0.2.7,
+ 10.0.2.8 : 10.0.2.8, 10.0.2.9 : 10.0.2.9,
+ 10.0.2.10 : 10.0.2.10, 10.0.2.11 : 10.0.2.11,
+ 10.0.2.12 : 10.0.2.12, 10.0.2.13 : 10.0.2.13,
+ 10.0.2.14 : 10.0.2.14, 10.0.2.15 : 10.0.2.15,
+ 10.0.2.16 : 10.0.2.16, 10.0.2.17 : 10.0.2.17,
+ 10.0.2.18 : 10.0.2.18, 10.0.2.19 : 10.0.2.19,
+ 10.0.2.20 : 10.0.2.20, 10.0.2.21 : 10.0.2.21,
+ 10.0.2.22 : 10.0.2.22, 10.0.2.23 : 10.0.2.23,
+ 10.0.2.24 : 10.0.2.24, 10.0.2.25 : 10.0.2.25,
+ 10.0.2.26 : 10.0.2.26, 10.0.2.27 : 10.0.2.27,
+ 10.0.2.28 : 10.0.2.28, 10.0.2.29 : 10.0.2.29,
+ 10.0.2.30 : 10.0.2.30, 10.0.2.31 : 10.0.2.31,
+ 10.0.3.1 : 10.0.3.1, 10.0.3.2 : 10.0.3.2,
+ 10.0.3.3 : 10.0.3.3, 10.0.3.4 : 10.0.3.4,
+ 10.0.3.5 : 10.0.3.5, 10.0.3.6 : 10.0.3.6,
+ 10.0.3.7 : 10.0.3.7, 10.0.3.8 : 10.0.3.8,
+ 10.0.3.9 : 10.0.3.9, 10.0.3.10 : 10.0.3.10,
+ 10.0.3.11 : 10.0.3.11, 10.0.3.12 : 10.0.3.12,
+ 10.0.3.13 : 10.0.3.13, 10.0.3.14 : 10.0.3.14,
+ 10.0.3.15 : 10.0.3.15, 10.0.3.16 : 10.0.3.16,
+ 10.0.3.17 : 10.0.3.17, 10.0.3.18 : 10.0.3.18,
+ 10.0.3.19 : 10.0.3.19, 10.0.3.20 : 10.0.3.20,
+ 10.0.3.21 : 10.0.3.21, 10.0.3.22 : 10.0.3.22,
+ 10.0.3.23 : 10.0.3.23, 10.0.3.24 : 10.0.3.24,
+ 10.0.3.25 : 10.0.3.25, 10.0.3.26 : 10.0.3.26,
+ 10.0.3.27 : 10.0.3.27, 10.0.3.28 : 10.0.3.28,
+ 10.0.3.29 : 10.0.3.29, 10.0.3.30 : 10.0.3.30,
+ 10.0.3.31 : 10.0.3.31, 10.0.4.1 : 10.0.4.1,
+ 10.0.4.2 : 10.0.4.2, 10.0.4.3 : 10.0.4.3,
+ 10.0.4.4 : 10.0.4.4, 10.0.4.5 : 10.0.4.5,
+ 10.0.4.6 : 10.0.4.6, 10.0.4.7 : 10.0.4.7,
+ 10.0.4.8 : 10.0.4.8, 10.0.4.9 : 10.0.4.9,
+ 10.0.4.10 : 10.0.4.10, 10.0.4.11 : 10.0.4.11,
+ 10.0.4.12 : 10.0.4.12, 10.0.4.13 : 10.0.4.13,
+ 10.0.4.14 : 10.0.4.14, 10.0.4.15 : 10.0.4.15,
+ 10.0.4.16 : 10.0.4.16, 10.0.4.17 : 10.0.4.17,
+ 10.0.4.18 : 10.0.4.18, 10.0.4.19 : 10.0.4.19,
+ 10.0.4.20 : 10.0.4.20, 10.0.4.21 : 10.0.4.21,
+ 10.0.4.22 : 10.0.4.22, 10.0.4.23 : 10.0.4.23,
+ 10.0.4.24 : 10.0.4.24, 10.0.4.25 : 10.0.4.25,
+ 10.0.4.26 : 10.0.4.26, 10.0.4.27 : 10.0.4.27,
+ 10.0.4.28 : 10.0.4.28, 10.0.4.29 : 10.0.4.29,
+ 10.0.4.30 : 10.0.4.30, 10.0.4.31 : 10.0.4.31,
+ 10.0.5.1 : 10.0.5.1, 10.0.5.2 : 10.0.5.2,
+ 10.0.5.3 : 10.0.5.3, 10.0.5.4 : 10.0.5.4,
+ 10.0.5.5 : 10.0.5.5, 10.0.5.6 : 10.0.5.6,
+ 10.0.5.7 : 10.0.5.7, 10.0.5.8 : 10.0.5.8,
+ 10.0.5.9 : 10.0.5.9, 10.0.5.10 : 10.0.5.10,
+ 10.0.5.11 : 10.0.5.11, 10.0.5.12 : 10.0.5.12,
+ 10.0.5.13 : 10.0.5.13, 10.0.5.14 : 10.0.5.14,
+ 10.0.5.15 : 10.0.5.15, 10.0.5.16 : 10.0.5.16,
+ 10.0.5.17 : 10.0.5.17, 10.0.5.18 : 10.0.5.18,
+ 10.0.5.19 : 10.0.5.19, 10.0.5.20 : 10.0.5.20,
+ 10.0.5.21 : 10.0.5.21, 10.0.5.22 : 10.0.5.22,
+ 10.0.5.23 : 10.0.5.23, 10.0.5.24 : 10.0.5.24,
+ 10.0.5.25 : 10.0.5.25, 10.0.5.26 : 10.0.5.26,
+ 10.0.5.27 : 10.0.5.27, 10.0.5.28 : 10.0.5.28,
+ 10.0.5.29 : 10.0.5.29, 10.0.5.30 : 10.0.5.30,
+ 10.0.5.31 : 10.0.5.31, 10.0.6.1 : 10.0.6.1,
+ 10.0.6.2 : 10.0.6.2, 10.0.6.3 : 10.0.6.3,
+ 10.0.6.4 : 10.0.6.4, 10.0.6.5 : 10.0.6.5,
+ 10.0.6.6 : 10.0.6.6, 10.0.6.7 : 10.0.6.7,
+ 10.0.6.8 : 10.0.6.8, 10.0.6.9 : 10.0.6.9,
+ 10.0.6.10 : 10.0.6.10, 10.0.6.11 : 10.0.6.11,
+ 10.0.6.12 : 10.0.6.12, 10.0.6.13 : 10.0.6.13,
+ 10.0.6.14 : 10.0.6.14, 10.0.6.15 : 10.0.6.15,
+ 10.0.6.16 : 10.0.6.16, 10.0.6.17 : 10.0.6.17,
+ 10.0.6.18 : 10.0.6.18, 10.0.6.19 : 10.0.6.19,
+ 10.0.6.20 : 10.0.6.20, 10.0.6.21 : 10.0.6.21,
+ 10.0.6.22 : 10.0.6.22, 10.0.6.23 : 10.0.6.23,
+ 10.0.6.24 : 10.0.6.24, 10.0.6.25 : 10.0.6.25,
+ 10.0.6.26 : 10.0.6.26, 10.0.6.27 : 10.0.6.27,
+ 10.0.6.28 : 10.0.6.28, 10.0.6.29 : 10.0.6.29,
+ 10.0.6.30 : 10.0.6.30, 10.0.6.31 : 10.0.6.31,
+ 10.0.7.1 : 10.0.7.1, 10.0.7.2 : 10.0.7.2,
+ 10.0.7.3 : 10.0.7.3, 10.0.7.4 : 10.0.7.4,
+ 10.0.7.5 : 10.0.7.5, 10.0.7.6 : 10.0.7.6,
+ 10.0.7.7 : 10.0.7.7, 10.0.7.8 : 10.0.7.8,
+ 10.0.7.9 : 10.0.7.9, 10.0.7.10 : 10.0.7.10,
+ 10.0.7.11 : 10.0.7.11, 10.0.7.12 : 10.0.7.12,
+ 10.0.7.13 : 10.0.7.13, 10.0.7.14 : 10.0.7.14,
+ 10.0.7.15 : 10.0.7.15, 10.0.7.16 : 10.0.7.16,
+ 10.0.7.17 : 10.0.7.17, 10.0.7.18 : 10.0.7.18,
+ 10.0.7.19 : 10.0.7.19, 10.0.7.20 : 10.0.7.20,
+ 10.0.7.21 : 10.0.7.21, 10.0.7.22 : 10.0.7.22,
+ 10.0.7.23 : 10.0.7.23, 10.0.7.24 : 10.0.7.24,
+ 10.0.7.25 : 10.0.7.25, 10.0.7.26 : 10.0.7.26,
+ 10.0.7.27 : 10.0.7.27, 10.0.7.28 : 10.0.7.28,
+ 10.0.7.29 : 10.0.7.29, 10.0.7.30 : 10.0.7.30,
+ 10.0.7.31 : 10.0.7.31, 10.0.8.1 : 10.0.8.1,
+ 10.0.8.2 : 10.0.8.2, 10.0.8.3 : 10.0.8.3,
+ 10.0.8.4 : 10.0.8.4, 10.0.8.5 : 10.0.8.5,
+ 10.0.8.6 : 10.0.8.6, 10.0.8.7 : 10.0.8.7,
+ 10.0.8.8 : 10.0.8.8, 10.0.8.9 : 10.0.8.9,
+ 10.0.8.10 : 10.0.8.10, 10.0.8.11 : 10.0.8.11,
+ 10.0.8.12 : 10.0.8.12, 10.0.8.13 : 10.0.8.13,
+ 10.0.8.14 : 10.0.8.14, 10.0.8.15 : 10.0.8.15,
+ 10.0.8.16 : 10.0.8.16, 10.0.8.17 : 10.0.8.17,
+ 10.0.8.18 : 10.0.8.18, 10.0.8.19 : 10.0.8.19,
+ 10.0.8.20 : 10.0.8.20, 10.0.8.21 : 10.0.8.21,
+ 10.0.8.22 : 10.0.8.22, 10.0.8.23 : 10.0.8.23,
+ 10.0.8.24 : 10.0.8.24, 10.0.8.25 : 10.0.8.25,
+ 10.0.8.26 : 10.0.8.26, 10.0.8.27 : 10.0.8.27,
+ 10.0.8.28 : 10.0.8.28, 10.0.8.29 : 10.0.8.29,
+ 10.0.8.30 : 10.0.8.30, 10.0.8.31 : 10.0.8.31,
+ 10.0.9.1 : 10.0.9.1, 10.0.9.2 : 10.0.9.2,
+ 10.0.9.3 : 10.0.9.3, 10.0.9.4 : 10.0.9.4,
+ 10.0.9.5 : 10.0.9.5, 10.0.9.6 : 10.0.9.6,
+ 10.0.9.7 : 10.0.9.7, 10.0.9.8 : 10.0.9.8,
+ 10.0.9.9 : 10.0.9.9, 10.0.9.10 : 10.0.9.10,
+ 10.0.9.11 : 10.0.9.11, 10.0.9.12 : 10.0.9.12,
+ 10.0.9.13 : 10.0.9.13, 10.0.9.14 : 10.0.9.14,
+ 10.0.9.15 : 10.0.9.15, 10.0.9.16 : 10.0.9.16,
+ 10.0.9.17 : 10.0.9.17, 10.0.9.18 : 10.0.9.18,
+ 10.0.9.19 : 10.0.9.19, 10.0.9.20 : 10.0.9.20,
+ 10.0.9.21 : 10.0.9.21, 10.0.9.22 : 10.0.9.22,
+ 10.0.9.23 : 10.0.9.23, 10.0.9.24 : 10.0.9.24,
+ 10.0.9.25 : 10.0.9.25, 10.0.9.26 : 10.0.9.26,
+ 10.0.9.27 : 10.0.9.27, 10.0.9.28 : 10.0.9.28,
+ 10.0.9.29 : 10.0.9.29, 10.0.9.30 : 10.0.9.30,
+ 10.0.9.31 : 10.0.9.31, 10.0.10.1 : 10.0.10.1,
+ 10.0.10.2 : 10.0.10.2, 10.0.10.3 : 10.0.10.3,
+ 10.0.10.4 : 10.0.10.4, 10.0.10.5 : 10.0.10.5,
+ 10.0.10.6 : 10.0.10.6, 10.0.10.7 : 10.0.10.7,
+ 10.0.10.8 : 10.0.10.8, 10.0.10.9 : 10.0.10.9,
+ 10.0.10.10 : 10.0.10.10, 10.0.10.11 : 10.0.10.11,
+ 10.0.10.12 : 10.0.10.12, 10.0.10.13 : 10.0.10.13,
+ 10.0.10.14 : 10.0.10.14, 10.0.10.15 : 10.0.10.15,
+ 10.0.10.16 : 10.0.10.16, 10.0.10.17 : 10.0.10.17,
+ 10.0.10.18 : 10.0.10.18, 10.0.10.19 : 10.0.10.19,
+ 10.0.10.20 : 10.0.10.20, 10.0.10.21 : 10.0.10.21,
+ 10.0.10.22 : 10.0.10.22, 10.0.10.23 : 10.0.10.23,
+ 10.0.10.24 : 10.0.10.24, 10.0.10.25 : 10.0.10.25,
+ 10.0.10.26 : 10.0.10.26, 10.0.10.27 : 10.0.10.27,
+ 10.0.10.28 : 10.0.10.28, 10.0.10.29 : 10.0.10.29,
+ 10.0.10.30 : 10.0.10.30, 10.0.10.31 : 10.0.10.31,
+ 10.0.11.1 : 10.0.11.1, 10.0.11.2 : 10.0.11.2,
+ 10.0.11.3 : 10.0.11.3, 10.0.11.4 : 10.0.11.4,
+ 10.0.11.5 : 10.0.11.5, 10.0.11.6 : 10.0.11.6,
+ 10.0.11.7 : 10.0.11.7, 10.0.11.8 : 10.0.11.8,
+ 10.0.11.9 : 10.0.11.9, 10.0.11.10 : 10.0.11.10,
+ 10.0.11.11 : 10.0.11.11, 10.0.11.12 : 10.0.11.12,
+ 10.0.11.13 : 10.0.11.13, 10.0.11.14 : 10.0.11.14,
+ 10.0.11.15 : 10.0.11.15, 10.0.11.16 : 10.0.11.16,
+ 10.0.11.17 : 10.0.11.17, 10.0.11.18 : 10.0.11.18,
+ 10.0.11.19 : 10.0.11.19, 10.0.11.20 : 10.0.11.20,
+ 10.0.11.21 : 10.0.11.21, 10.0.11.22 : 10.0.11.22,
+ 10.0.11.23 : 10.0.11.23, 10.0.11.24 : 10.0.11.24,
+ 10.0.11.25 : 10.0.11.25, 10.0.11.26 : 10.0.11.26,
+ 10.0.11.27 : 10.0.11.27, 10.0.11.28 : 10.0.11.28,
+ 10.0.11.29 : 10.0.11.29, 10.0.11.30 : 10.0.11.30,
+ 10.0.11.31 : 10.0.11.31, 10.0.12.1 : 10.0.12.1,
+ 10.0.12.2 : 10.0.12.2, 10.0.12.3 : 10.0.12.3,
+ 10.0.12.4 : 10.0.12.4, 10.0.12.5 : 10.0.12.5,
+ 10.0.12.6 : 10.0.12.6, 10.0.12.7 : 10.0.12.7,
+ 10.0.12.8 : 10.0.12.8, 10.0.12.9 : 10.0.12.9,
+ 10.0.12.10 : 10.0.12.10, 10.0.12.11 : 10.0.12.11,
+ 10.0.12.12 : 10.0.12.12, 10.0.12.13 : 10.0.12.13,
+ 10.0.12.14 : 10.0.12.14, 10.0.12.15 : 10.0.12.15,
+ 10.0.12.16 : 10.0.12.16, 10.0.12.17 : 10.0.12.17,
+ 10.0.12.18 : 10.0.12.18, 10.0.12.19 : 10.0.12.19,
+ 10.0.12.20 : 10.0.12.20, 10.0.12.21 : 10.0.12.21,
+ 10.0.12.22 : 10.0.12.22, 10.0.12.23 : 10.0.12.23,
+ 10.0.12.24 : 10.0.12.24, 10.0.12.25 : 10.0.12.25,
+ 10.0.12.26 : 10.0.12.26, 10.0.12.27 : 10.0.12.27,
+ 10.0.12.28 : 10.0.12.28, 10.0.12.29 : 10.0.12.29,
+ 10.0.12.30 : 10.0.12.30, 10.0.12.31 : 10.0.12.31,
+ 10.0.13.1 : 10.0.13.1, 10.0.13.2 : 10.0.13.2,
+ 10.0.13.3 : 10.0.13.3, 10.0.13.4 : 10.0.13.4,
+ 10.0.13.5 : 10.0.13.5, 10.0.13.6 : 10.0.13.6,
+ 10.0.13.7 : 10.0.13.7, 10.0.13.8 : 10.0.13.8,
+ 10.0.13.9 : 10.0.13.9, 10.0.13.10 : 10.0.13.10,
+ 10.0.13.11 : 10.0.13.11, 10.0.13.12 : 10.0.13.12,
+ 10.0.13.13 : 10.0.13.13, 10.0.13.14 : 10.0.13.14,
+ 10.0.13.15 : 10.0.13.15, 10.0.13.16 : 10.0.13.16,
+ 10.0.13.17 : 10.0.13.17, 10.0.13.18 : 10.0.13.18,
+ 10.0.13.19 : 10.0.13.19, 10.0.13.20 : 10.0.13.20,
+ 10.0.13.21 : 10.0.13.21, 10.0.13.22 : 10.0.13.22,
+ 10.0.13.23 : 10.0.13.23, 10.0.13.24 : 10.0.13.24,
+ 10.0.13.25 : 10.0.13.25, 10.0.13.26 : 10.0.13.26,
+ 10.0.13.27 : 10.0.13.27, 10.0.13.28 : 10.0.13.28,
+ 10.0.13.29 : 10.0.13.29, 10.0.13.30 : 10.0.13.30,
+ 10.0.13.31 : 10.0.13.31, 10.0.14.1 : 10.0.14.1,
+ 10.0.14.2 : 10.0.14.2, 10.0.14.3 : 10.0.14.3,
+ 10.0.14.4 : 10.0.14.4, 10.0.14.5 : 10.0.14.5,
+ 10.0.14.6 : 10.0.14.6, 10.0.14.7 : 10.0.14.7,
+ 10.0.14.8 : 10.0.14.8, 10.0.14.9 : 10.0.14.9,
+ 10.0.14.10 : 10.0.14.10, 10.0.14.11 : 10.0.14.11,
+ 10.0.14.12 : 10.0.14.12, 10.0.14.13 : 10.0.14.13,
+ 10.0.14.14 : 10.0.14.14, 10.0.14.15 : 10.0.14.15,
+ 10.0.14.16 : 10.0.14.16, 10.0.14.17 : 10.0.14.17,
+ 10.0.14.18 : 10.0.14.18, 10.0.14.19 : 10.0.14.19,
+ 10.0.14.20 : 10.0.14.20, 10.0.14.21 : 10.0.14.21,
+ 10.0.14.22 : 10.0.14.22, 10.0.14.23 : 10.0.14.23,
+ 10.0.14.24 : 10.0.14.24, 10.0.14.25 : 10.0.14.25,
+ 10.0.14.26 : 10.0.14.26, 10.0.14.27 : 10.0.14.27,
+ 10.0.14.28 : 10.0.14.28, 10.0.14.29 : 10.0.14.29,
+ 10.0.14.30 : 10.0.14.30, 10.0.14.31 : 10.0.14.31,
+ 10.0.15.1 : 10.0.15.1, 10.0.15.2 : 10.0.15.2,
+ 10.0.15.3 : 10.0.15.3, 10.0.15.4 : 10.0.15.4,
+ 10.0.15.5 : 10.0.15.5, 10.0.15.6 : 10.0.15.6,
+ 10.0.15.7 : 10.0.15.7, 10.0.15.8 : 10.0.15.8,
+ 10.0.15.9 : 10.0.15.9, 10.0.15.10 : 10.0.15.10,
+ 10.0.15.11 : 10.0.15.11, 10.0.15.12 : 10.0.15.12,
+ 10.0.15.13 : 10.0.15.13, 10.0.15.14 : 10.0.15.14,
+ 10.0.15.15 : 10.0.15.15, 10.0.15.16 : 10.0.15.16,
+ 10.0.15.17 : 10.0.15.17, 10.0.15.18 : 10.0.15.18,
+ 10.0.15.19 : 10.0.15.19, 10.0.15.20 : 10.0.15.20,
+ 10.0.15.21 : 10.0.15.21, 10.0.15.22 : 10.0.15.22,
+ 10.0.15.23 : 10.0.15.23, 10.0.15.24 : 10.0.15.24,
+ 10.0.15.25 : 10.0.15.25, 10.0.15.26 : 10.0.15.26,
+ 10.0.15.27 : 10.0.15.27, 10.0.15.28 : 10.0.15.28,
+ 10.0.15.29 : 10.0.15.29, 10.0.15.30 : 10.0.15.30,
+ 10.0.15.31 : 10.0.15.31, 10.0.16.1 : 10.0.16.1,
+ 10.0.16.2 : 10.0.16.2, 10.0.16.3 : 10.0.16.3,
+ 10.0.16.4 : 10.0.16.4, 10.0.16.5 : 10.0.16.5,
+ 10.0.16.6 : 10.0.16.6, 10.0.16.7 : 10.0.16.7,
+ 10.0.16.8 : 10.0.16.8, 10.0.16.9 : 10.0.16.9,
+ 10.0.16.10 : 10.0.16.10, 10.0.16.11 : 10.0.16.11,
+ 10.0.16.12 : 10.0.16.12, 10.0.16.13 : 10.0.16.13,
+ 10.0.16.14 : 10.0.16.14, 10.0.16.15 : 10.0.16.15,
+ 10.0.16.16 : 10.0.16.16, 10.0.16.17 : 10.0.16.17,
+ 10.0.16.18 : 10.0.16.18, 10.0.16.19 : 10.0.16.19,
+ 10.0.16.20 : 10.0.16.20, 10.0.16.21 : 10.0.16.21,
+ 10.0.16.22 : 10.0.16.22, 10.0.16.23 : 10.0.16.23,
+ 10.0.16.24 : 10.0.16.24, 10.0.16.25 : 10.0.16.25,
+ 10.0.16.26 : 10.0.16.26, 10.0.16.27 : 10.0.16.27,
+ 10.0.16.28 : 10.0.16.28, 10.0.16.29 : 10.0.16.29,
+ 10.0.16.30 : 10.0.16.30, 10.0.16.31 : 10.0.16.31,
+ 10.0.17.1 : 10.0.17.1, 10.0.17.2 : 10.0.17.2,
+ 10.0.17.3 : 10.0.17.3, 10.0.17.4 : 10.0.17.4,
+ 10.0.17.5 : 10.0.17.5, 10.0.17.6 : 10.0.17.6,
+ 10.0.17.7 : 10.0.17.7, 10.0.17.8 : 10.0.17.8,
+ 10.0.17.9 : 10.0.17.9, 10.0.17.10 : 10.0.17.10,
+ 10.0.17.11 : 10.0.17.11, 10.0.17.12 : 10.0.17.12,
+ 10.0.17.13 : 10.0.17.13, 10.0.17.14 : 10.0.17.14,
+ 10.0.17.15 : 10.0.17.15, 10.0.17.16 : 10.0.17.16,
+ 10.0.17.17 : 10.0.17.17, 10.0.17.18 : 10.0.17.18,
+ 10.0.17.19 : 10.0.17.19, 10.0.17.20 : 10.0.17.20,
+ 10.0.17.21 : 10.0.17.21, 10.0.17.22 : 10.0.17.22,
+ 10.0.17.23 : 10.0.17.23, 10.0.17.24 : 10.0.17.24,
+ 10.0.17.25 : 10.0.17.25, 10.0.17.26 : 10.0.17.26,
+ 10.0.17.27 : 10.0.17.27, 10.0.17.28 : 10.0.17.28,
+ 10.0.17.29 : 10.0.17.29, 10.0.17.30 : 10.0.17.30,
+ 10.0.17.31 : 10.0.17.31, 10.0.18.1 : 10.0.18.1,
+ 10.0.18.2 : 10.0.18.2, 10.0.18.3 : 10.0.18.3,
+ 10.0.18.4 : 10.0.18.4, 10.0.18.5 : 10.0.18.5,
+ 10.0.18.6 : 10.0.18.6, 10.0.18.7 : 10.0.18.7,
+ 10.0.18.8 : 10.0.18.8, 10.0.18.9 : 10.0.18.9,
+ 10.0.18.10 : 10.0.18.10, 10.0.18.11 : 10.0.18.11,
+ 10.0.18.12 : 10.0.18.12, 10.0.18.13 : 10.0.18.13,
+ 10.0.18.14 : 10.0.18.14, 10.0.18.15 : 10.0.18.15,
+ 10.0.18.16 : 10.0.18.16, 10.0.18.17 : 10.0.18.17,
+ 10.0.18.18 : 10.0.18.18, 10.0.18.19 : 10.0.18.19,
+ 10.0.18.20 : 10.0.18.20, 10.0.18.21 : 10.0.18.21,
+ 10.0.18.22 : 10.0.18.22, 10.0.18.23 : 10.0.18.23,
+ 10.0.18.24 : 10.0.18.24, 10.0.18.25 : 10.0.18.25,
+ 10.0.18.26 : 10.0.18.26, 10.0.18.27 : 10.0.18.27,
+ 10.0.18.28 : 10.0.18.28, 10.0.18.29 : 10.0.18.29,
+ 10.0.18.30 : 10.0.18.30, 10.0.18.31 : 10.0.18.31,
+ 10.0.19.1 : 10.0.19.1, 10.0.19.2 : 10.0.19.2,
+ 10.0.19.3 : 10.0.19.3, 10.0.19.4 : 10.0.19.4,
+ 10.0.19.5 : 10.0.19.5, 10.0.19.6 : 10.0.19.6,
+ 10.0.19.7 : 10.0.19.7, 10.0.19.8 : 10.0.19.8,
+ 10.0.19.9 : 10.0.19.9, 10.0.19.10 : 10.0.19.10,
+ 10.0.19.11 : 10.0.19.11, 10.0.19.12 : 10.0.19.12,
+ 10.0.19.13 : 10.0.19.13, 10.0.19.14 : 10.0.19.14,
+ 10.0.19.15 : 10.0.19.15, 10.0.19.16 : 10.0.19.16,
+ 10.0.19.17 : 10.0.19.17, 10.0.19.18 : 10.0.19.18,
+ 10.0.19.19 : 10.0.19.19, 10.0.19.20 : 10.0.19.20,
+ 10.0.19.21 : 10.0.19.21, 10.0.19.22 : 10.0.19.22,
+ 10.0.19.23 : 10.0.19.23, 10.0.19.24 : 10.0.19.24,
+ 10.0.19.25 : 10.0.19.25, 10.0.19.26 : 10.0.19.26,
+ 10.0.19.27 : 10.0.19.27, 10.0.19.28 : 10.0.19.28,
+ 10.0.19.29 : 10.0.19.29, 10.0.19.30 : 10.0.19.30,
+ 10.0.19.31 : 10.0.19.31, 10.0.20.1 : 10.0.20.1,
+ 10.0.20.2 : 10.0.20.2, 10.0.20.3 : 10.0.20.3,
+ 10.0.20.4 : 10.0.20.4, 10.0.20.5 : 10.0.20.5,
+ 10.0.20.6 : 10.0.20.6, 10.0.20.7 : 10.0.20.7,
+ 10.0.20.8 : 10.0.20.8, 10.0.20.9 : 10.0.20.9,
+ 10.0.20.10 : 10.0.20.10, 10.0.20.11 : 10.0.20.11,
+ 10.0.20.12 : 10.0.20.12, 10.0.20.13 : 10.0.20.13,
+ 10.0.20.14 : 10.0.20.14, 10.0.20.15 : 10.0.20.15,
+ 10.0.20.16 : 10.0.20.16, 10.0.20.17 : 10.0.20.17,
+ 10.0.20.18 : 10.0.20.18, 10.0.20.19 : 10.0.20.19,
+ 10.0.20.20 : 10.0.20.20, 10.0.20.21 : 10.0.20.21,
+ 10.0.20.22 : 10.0.20.22, 10.0.20.23 : 10.0.20.23,
+ 10.0.20.24 : 10.0.20.24, 10.0.20.25 : 10.0.20.25,
+ 10.0.20.26 : 10.0.20.26, 10.0.20.27 : 10.0.20.27,
+ 10.0.20.28 : 10.0.20.28, 10.0.20.29 : 10.0.20.29,
+ 10.0.20.30 : 10.0.20.30, 10.0.20.31 : 10.0.20.31,
+ 10.0.21.1 : 10.0.21.1, 10.0.21.2 : 10.0.21.2,
+ 10.0.21.3 : 10.0.21.3, 10.0.21.4 : 10.0.21.4,
+ 10.0.21.5 : 10.0.21.5, 10.0.21.6 : 10.0.21.6,
+ 10.0.21.7 : 10.0.21.7, 10.0.21.8 : 10.0.21.8,
+ 10.0.21.9 : 10.0.21.9, 10.0.21.10 : 10.0.21.10,
+ 10.0.21.11 : 10.0.21.11, 10.0.21.12 : 10.0.21.12,
+ 10.0.21.13 : 10.0.21.13, 10.0.21.14 : 10.0.21.14,
+ 10.0.21.15 : 10.0.21.15, 10.0.21.16 : 10.0.21.16,
+ 10.0.21.17 : 10.0.21.17, 10.0.21.18 : 10.0.21.18,
+ 10.0.21.19 : 10.0.21.19, 10.0.21.20 : 10.0.21.20,
+ 10.0.21.21 : 10.0.21.21, 10.0.21.22 : 10.0.21.22,
+ 10.0.21.23 : 10.0.21.23, 10.0.21.24 : 10.0.21.24,
+ 10.0.21.25 : 10.0.21.25, 10.0.21.26 : 10.0.21.26,
+ 10.0.21.27 : 10.0.21.27, 10.0.21.28 : 10.0.21.28,
+ 10.0.21.29 : 10.0.21.29, 10.0.21.30 : 10.0.21.30,
+ 10.0.21.31 : 10.0.21.31, 10.0.22.1 : 10.0.22.1,
+ 10.0.22.2 : 10.0.22.2, 10.0.22.3 : 10.0.22.3,
+ 10.0.22.4 : 10.0.22.4, 10.0.22.5 : 10.0.22.5,
+ 10.0.22.6 : 10.0.22.6, 10.0.22.7 : 10.0.22.7,
+ 10.0.22.8 : 10.0.22.8, 10.0.22.9 : 10.0.22.9,
+ 10.0.22.10 : 10.0.22.10, 10.0.22.11 : 10.0.22.11,
+ 10.0.22.12 : 10.0.22.12, 10.0.22.13 : 10.0.22.13,
+ 10.0.22.14 : 10.0.22.14, 10.0.22.15 : 10.0.22.15,
+ 10.0.22.16 : 10.0.22.16, 10.0.22.17 : 10.0.22.17,
+ 10.0.22.18 : 10.0.22.18, 10.0.22.19 : 10.0.22.19,
+ 10.0.22.20 : 10.0.22.20, 10.0.22.21 : 10.0.22.21,
+ 10.0.22.22 : 10.0.22.22, 10.0.22.23 : 10.0.22.23,
+ 10.0.22.24 : 10.0.22.24, 10.0.22.25 : 10.0.22.25,
+ 10.0.22.26 : 10.0.22.26, 10.0.22.27 : 10.0.22.27,
+ 10.0.22.28 : 10.0.22.28, 10.0.22.29 : 10.0.22.29,
+ 10.0.22.30 : 10.0.22.30, 10.0.22.31 : 10.0.22.31,
+ 10.0.23.1 : 10.0.23.1, 10.0.23.2 : 10.0.23.2,
+ 10.0.23.3 : 10.0.23.3, 10.0.23.4 : 10.0.23.4,
+ 10.0.23.5 : 10.0.23.5, 10.0.23.6 : 10.0.23.6,
+ 10.0.23.7 : 10.0.23.7, 10.0.23.8 : 10.0.23.8,
+ 10.0.23.9 : 10.0.23.9, 10.0.23.10 : 10.0.23.10,
+ 10.0.23.11 : 10.0.23.11, 10.0.23.12 : 10.0.23.12,
+ 10.0.23.13 : 10.0.23.13, 10.0.23.14 : 10.0.23.14,
+ 10.0.23.15 : 10.0.23.15, 10.0.23.16 : 10.0.23.16,
+ 10.0.23.17 : 10.0.23.17, 10.0.23.18 : 10.0.23.18,
+ 10.0.23.19 : 10.0.23.19, 10.0.23.20 : 10.0.23.20,
+ 10.0.23.21 : 10.0.23.21, 10.0.23.22 : 10.0.23.22,
+ 10.0.23.23 : 10.0.23.23, 10.0.23.24 : 10.0.23.24,
+ 10.0.23.25 : 10.0.23.25, 10.0.23.26 : 10.0.23.26,
+ 10.0.23.27 : 10.0.23.27, 10.0.23.28 : 10.0.23.28,
+ 10.0.23.29 : 10.0.23.29, 10.0.23.30 : 10.0.23.30,
+ 10.0.23.31 : 10.0.23.31, 10.0.24.1 : 10.0.24.1,
+ 10.0.24.2 : 10.0.24.2, 10.0.24.3 : 10.0.24.3,
+ 10.0.24.4 : 10.0.24.4, 10.0.24.5 : 10.0.24.5,
+ 10.0.24.6 : 10.0.24.6, 10.0.24.7 : 10.0.24.7,
+ 10.0.24.8 : 10.0.24.8, 10.0.24.9 : 10.0.24.9,
+ 10.0.24.10 : 10.0.24.10, 10.0.24.11 : 10.0.24.11,
+ 10.0.24.12 : 10.0.24.12, 10.0.24.13 : 10.0.24.13,
+ 10.0.24.14 : 10.0.24.14, 10.0.24.15 : 10.0.24.15,
+ 10.0.24.16 : 10.0.24.16, 10.0.24.17 : 10.0.24.17,
+ 10.0.24.18 : 10.0.24.18, 10.0.24.19 : 10.0.24.19,
+ 10.0.24.20 : 10.0.24.20, 10.0.24.21 : 10.0.24.21,
+ 10.0.24.22 : 10.0.24.22, 10.0.24.23 : 10.0.24.23,
+ 10.0.24.24 : 10.0.24.24, 10.0.24.25 : 10.0.24.25,
+ 10.0.24.26 : 10.0.24.26, 10.0.24.27 : 10.0.24.27,
+ 10.0.24.28 : 10.0.24.28, 10.0.24.29 : 10.0.24.29,
+ 10.0.24.30 : 10.0.24.30, 10.0.24.31 : 10.0.24.31,
+ 10.0.25.1 : 10.0.25.1, 10.0.25.2 : 10.0.25.2,
+ 10.0.25.3 : 10.0.25.3, 10.0.25.4 : 10.0.25.4,
+ 10.0.25.5 : 10.0.25.5, 10.0.25.6 : 10.0.25.6,
+ 10.0.25.7 : 10.0.25.7, 10.0.25.8 : 10.0.25.8,
+ 10.0.25.9 : 10.0.25.9, 10.0.25.10 : 10.0.25.10,
+ 10.0.25.11 : 10.0.25.11, 10.0.25.12 : 10.0.25.12,
+ 10.0.25.13 : 10.0.25.13, 10.0.25.14 : 10.0.25.14,
+ 10.0.25.15 : 10.0.25.15, 10.0.25.16 : 10.0.25.16,
+ 10.0.25.17 : 10.0.25.17, 10.0.25.18 : 10.0.25.18,
+ 10.0.25.19 : 10.0.25.19, 10.0.25.20 : 10.0.25.20,
+ 10.0.25.21 : 10.0.25.21, 10.0.25.22 : 10.0.25.22,
+ 10.0.25.23 : 10.0.25.23, 10.0.25.24 : 10.0.25.24,
+ 10.0.25.25 : 10.0.25.25, 10.0.25.26 : 10.0.25.26,
+ 10.0.25.27 : 10.0.25.27, 10.0.25.28 : 10.0.25.28,
+ 10.0.25.29 : 10.0.25.29, 10.0.25.30 : 10.0.25.30,
+ 10.0.25.31 : 10.0.25.31, 10.0.26.1 : 10.0.26.1,
+ 10.0.26.2 : 10.0.26.2, 10.0.26.3 : 10.0.26.3,
+ 10.0.26.4 : 10.0.26.4, 10.0.26.5 : 10.0.26.5,
+ 10.0.26.6 : 10.0.26.6, 10.0.26.7 : 10.0.26.7,
+ 10.0.26.8 : 10.0.26.8, 10.0.26.9 : 10.0.26.9,
+ 10.0.26.10 : 10.0.26.10, 10.0.26.11 : 10.0.26.11,
+ 10.0.26.12 : 10.0.26.12, 10.0.26.13 : 10.0.26.13,
+ 10.0.26.14 : 10.0.26.14, 10.0.26.15 : 10.0.26.15,
+ 10.0.26.16 : 10.0.26.16, 10.0.26.17 : 10.0.26.17,
+ 10.0.26.18 : 10.0.26.18, 10.0.26.19 : 10.0.26.19,
+ 10.0.26.20 : 10.0.26.20, 10.0.26.21 : 10.0.26.21,
+ 10.0.26.22 : 10.0.26.22, 10.0.26.23 : 10.0.26.23,
+ 10.0.26.24 : 10.0.26.24, 10.0.26.25 : 10.0.26.25,
+ 10.0.26.26 : 10.0.26.26, 10.0.26.27 : 10.0.26.27,
+ 10.0.26.28 : 10.0.26.28, 10.0.26.29 : 10.0.26.29,
+ 10.0.26.30 : 10.0.26.30, 10.0.26.31 : 10.0.26.31,
+ 10.0.27.1 : 10.0.27.1, 10.0.27.2 : 10.0.27.2,
+ 10.0.27.3 : 10.0.27.3, 10.0.27.4 : 10.0.27.4,
+ 10.0.27.5 : 10.0.27.5, 10.0.27.6 : 10.0.27.6,
+ 10.0.27.7 : 10.0.27.7, 10.0.27.8 : 10.0.27.8,
+ 10.0.27.9 : 10.0.27.9, 10.0.27.10 : 10.0.27.10,
+ 10.0.27.11 : 10.0.27.11, 10.0.27.12 : 10.0.27.12,
+ 10.0.27.13 : 10.0.27.13, 10.0.27.14 : 10.0.27.14,
+ 10.0.27.15 : 10.0.27.15, 10.0.27.16 : 10.0.27.16,
+ 10.0.27.17 : 10.0.27.17, 10.0.27.18 : 10.0.27.18,
+ 10.0.27.19 : 10.0.27.19, 10.0.27.20 : 10.0.27.20,
+ 10.0.27.21 : 10.0.27.21, 10.0.27.22 : 10.0.27.22,
+ 10.0.27.23 : 10.0.27.23, 10.0.27.24 : 10.0.27.24,
+ 10.0.27.25 : 10.0.27.25, 10.0.27.26 : 10.0.27.26,
+ 10.0.27.27 : 10.0.27.27, 10.0.27.28 : 10.0.27.28,
+ 10.0.27.29 : 10.0.27.29, 10.0.27.30 : 10.0.27.30,
+ 10.0.27.31 : 10.0.27.31, 10.0.28.1 : 10.0.28.1,
+ 10.0.28.2 : 10.0.28.2, 10.0.28.3 : 10.0.28.3,
+ 10.0.28.4 : 10.0.28.4, 10.0.28.5 : 10.0.28.5,
+ 10.0.28.6 : 10.0.28.6, 10.0.28.7 : 10.0.28.7,
+ 10.0.28.8 : 10.0.28.8, 10.0.28.9 : 10.0.28.9,
+ 10.0.28.10 : 10.0.28.10, 10.0.28.11 : 10.0.28.11,
+ 10.0.28.12 : 10.0.28.12, 10.0.28.13 : 10.0.28.13,
+ 10.0.28.14 : 10.0.28.14, 10.0.28.15 : 10.0.28.15,
+ 10.0.28.16 : 10.0.28.16, 10.0.28.17 : 10.0.28.17,
+ 10.0.28.18 : 10.0.28.18, 10.0.28.19 : 10.0.28.19,
+ 10.0.28.20 : 10.0.28.20, 10.0.28.21 : 10.0.28.21,
+ 10.0.28.22 : 10.0.28.22, 10.0.28.23 : 10.0.28.23,
+ 10.0.28.24 : 10.0.28.24, 10.0.28.25 : 10.0.28.25,
+ 10.0.28.26 : 10.0.28.26, 10.0.28.27 : 10.0.28.27,
+ 10.0.28.28 : 10.0.28.28, 10.0.28.29 : 10.0.28.29,
+ 10.0.28.30 : 10.0.28.30, 10.0.28.31 : 10.0.28.31,
+ 10.0.29.1 : 10.0.29.1, 10.0.29.2 : 10.0.29.2,
+ 10.0.29.3 : 10.0.29.3, 10.0.29.4 : 10.0.29.4,
+ 10.0.29.5 : 10.0.29.5, 10.0.29.6 : 10.0.29.6,
+ 10.0.29.7 : 10.0.29.7, 10.0.29.8 : 10.0.29.8,
+ 10.0.29.9 : 10.0.29.9, 10.0.29.10 : 10.0.29.10,
+ 10.0.29.11 : 10.0.29.11, 10.0.29.12 : 10.0.29.12,
+ 10.0.29.13 : 10.0.29.13, 10.0.29.14 : 10.0.29.14,
+ 10.0.29.15 : 10.0.29.15, 10.0.29.16 : 10.0.29.16,
+ 10.0.29.17 : 10.0.29.17, 10.0.29.18 : 10.0.29.18,
+ 10.0.29.19 : 10.0.29.19, 10.0.29.20 : 10.0.29.20,
+ 10.0.29.21 : 10.0.29.21, 10.0.29.22 : 10.0.29.22,
+ 10.0.29.23 : 10.0.29.23, 10.0.29.24 : 10.0.29.24,
+ 10.0.29.25 : 10.0.29.25, 10.0.29.26 : 10.0.29.26,
+ 10.0.29.27 : 10.0.29.27, 10.0.29.28 : 10.0.29.28,
+ 10.0.29.29 : 10.0.29.29, 10.0.29.30 : 10.0.29.30,
+ 10.0.29.31 : 10.0.29.31, 10.0.30.1 : 10.0.30.1,
+ 10.0.30.2 : 10.0.30.2, 10.0.30.3 : 10.0.30.3,
+ 10.0.30.4 : 10.0.30.4, 10.0.30.5 : 10.0.30.5,
+ 10.0.30.6 : 10.0.30.6, 10.0.30.7 : 10.0.30.7,
+ 10.0.30.8 : 10.0.30.8, 10.0.30.9 : 10.0.30.9,
+ 10.0.30.10 : 10.0.30.10, 10.0.30.11 : 10.0.30.11,
+ 10.0.30.12 : 10.0.30.12, 10.0.30.13 : 10.0.30.13,
+ 10.0.30.14 : 10.0.30.14, 10.0.30.15 : 10.0.30.15,
+ 10.0.30.16 : 10.0.30.16, 10.0.30.17 : 10.0.30.17,
+ 10.0.30.18 : 10.0.30.18, 10.0.30.19 : 10.0.30.19,
+ 10.0.30.20 : 10.0.30.20, 10.0.30.21 : 10.0.30.21,
+ 10.0.30.22 : 10.0.30.22, 10.0.30.23 : 10.0.30.23,
+ 10.0.30.24 : 10.0.30.24, 10.0.30.25 : 10.0.30.25,
+ 10.0.30.26 : 10.0.30.26, 10.0.30.27 : 10.0.30.27,
+ 10.0.30.28 : 10.0.30.28, 10.0.30.29 : 10.0.30.29,
+ 10.0.30.30 : 10.0.30.30, 10.0.30.31 : 10.0.30.31,
+ 10.0.31.1 : 10.0.31.1, 10.0.31.2 : 10.0.31.2,
+ 10.0.31.3 : 10.0.31.3, 10.0.31.4 : 10.0.31.4,
+ 10.0.31.5 : 10.0.31.5, 10.0.31.6 : 10.0.31.6,
+ 10.0.31.7 : 10.0.31.7, 10.0.31.8 : 10.0.31.8,
+ 10.0.31.9 : 10.0.31.9, 10.0.31.10 : 10.0.31.10,
+ 10.0.31.11 : 10.0.31.11, 10.0.31.12 : 10.0.31.12,
+ 10.0.31.13 : 10.0.31.13, 10.0.31.14 : 10.0.31.14,
+ 10.0.31.15 : 10.0.31.15, 10.0.31.16 : 10.0.31.16,
+ 10.0.31.17 : 10.0.31.17, 10.0.31.18 : 10.0.31.18,
+ 10.0.31.19 : 10.0.31.19, 10.0.31.20 : 10.0.31.20,
+ 10.0.31.21 : 10.0.31.21, 10.0.31.22 : 10.0.31.22,
+ 10.0.31.23 : 10.0.31.23, 10.0.31.24 : 10.0.31.24,
+ 10.0.31.25 : 10.0.31.25, 10.0.31.26 : 10.0.31.26,
+ 10.0.31.27 : 10.0.31.27, 10.0.31.28 : 10.0.31.28,
+ 10.0.31.29 : 10.0.31.29, 10.0.31.30 : 10.0.31.30,
+ 10.0.31.31 : 10.0.31.31 }
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/0004interval_map_create_once_0.nodump b/tests/shell/testcases/maps/dumps/0004interval_map_create_once_0.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0004interval_map_create_once_0.nodump
diff --git a/tests/shell/testcases/maps/dumps/0005interval_map_add_many_elements_0.json-nft b/tests/shell/testcases/maps/dumps/0005interval_map_add_many_elements_0.json-nft
new file mode 100644
index 00000000..d1a46295
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0005interval_map_add_many_elements_0.json-nft
@@ -0,0 +1,69 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "ipv4_addr",
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ [
+ {
+ "prefix": {
+ "addr": "10.1.1.0",
+ "len": 24
+ }
+ },
+ "10.0.1.1"
+ ],
+ [
+ {
+ "prefix": {
+ "addr": "10.1.2.0",
+ "len": 24
+ }
+ },
+ "10.0.1.2"
+ ],
+ [
+ {
+ "prefix": {
+ "addr": "10.2.1.0",
+ "len": 24
+ }
+ },
+ "10.0.2.1"
+ ],
+ [
+ {
+ "prefix": {
+ "addr": "10.2.2.0",
+ "len": 24
+ }
+ },
+ "10.0.2.2"
+ ]
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/0006interval_map_overlap_0.json-nft b/tests/shell/testcases/maps/dumps/0006interval_map_overlap_0.json-nft
new file mode 100644
index 00000000..1e983219
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0006interval_map_overlap_0.json-nft
@@ -0,0 +1,51 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "ipv4_addr",
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ [
+ {
+ "prefix": {
+ "addr": "10.0.1.0",
+ "len": 24
+ }
+ },
+ "10.0.0.1"
+ ],
+ [
+ {
+ "prefix": {
+ "addr": "10.0.2.0",
+ "len": 24
+ }
+ },
+ "10.0.0.2"
+ ]
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/0007named_ifname_dtype_0.json-nft b/tests/shell/testcases/maps/dumps/0007named_ifname_dtype_0.json-nft
new file mode 100644
index 00000000..ef57a749
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0007named_ifname_dtype_0.json-nft
@@ -0,0 +1,102 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "inet",
+ "name": "m1",
+ "table": "t",
+ "type": "ifname",
+ "handle": 0,
+ "map": "ipv4_addr",
+ "elem": [
+ [
+ "eth0",
+ "1.1.1.1"
+ ]
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "value": {
+ "map": {
+ "key": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "data": "@m1"
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "value": {
+ "map": {
+ "key": {
+ "meta": {
+ "key": "oifname"
+ }
+ },
+ "data": "@m1"
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/0008interval_map_delete_0.json-nft b/tests/shell/testcases/maps/dumps/0008interval_map_delete_0.json-nft
new file mode 100644
index 00000000..bd3c6cc7
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0008interval_map_delete_0.json-nft
@@ -0,0 +1,159 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "m",
+ "table": "filter",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "mark",
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ [
+ "127.0.0.2",
+ 2
+ ],
+ [
+ "127.0.0.3",
+ 3
+ ]
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "data": "@m"
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "right": 2
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "right": 3
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/0008interval_map_delete_0.nft b/tests/shell/testcases/maps/dumps/0008interval_map_delete_0.nft
new file mode 100644
index 00000000..a470a340
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0008interval_map_delete_0.nft
@@ -0,0 +1,15 @@
+table ip filter {
+ map m {
+ type ipv4_addr : mark
+ flags interval
+ elements = { 127.0.0.2 : 0x00000002, 127.0.0.3 : 0x00000003 }
+ }
+
+ chain input {
+ type filter hook input priority filter; policy accept;
+ meta mark set ip daddr map @m
+ meta mark 0x00000002 counter packets 0 bytes 0 accept
+ meta mark 0x00000003 counter packets 0 bytes 0 accept
+ counter packets 0 bytes 0
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/0009vmap_0.json-nft b/tests/shell/testcases/maps/dumps/0009vmap_0.json-nft
new file mode 100644
index 00000000..345a2c74
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0009vmap_0.json-nft
@@ -0,0 +1,117 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "ssh_input",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "wan_input",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "prerouting",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -300,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "wan_input",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ 22,
+ {
+ "jump": {
+ "target": "ssh_input"
+ }
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "prerouting",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "meta": {
+ "key": "iif"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ {
+ "elem": {
+ "val": "lo",
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ },
+ {
+ "jump": {
+ "target": "wan_input"
+ }
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/0009vmap_0.nft b/tests/shell/testcases/maps/dumps/0009vmap_0.nft
index c556fece..c37574ad 100644
--- a/tests/shell/testcases/maps/dumps/0009vmap_0.nft
+++ b/tests/shell/testcases/maps/dumps/0009vmap_0.nft
@@ -8,6 +8,6 @@ table inet filter {
chain prerouting {
type filter hook prerouting priority raw; policy accept;
- iif vmap { "lo" : jump wan_input }
+ iif vmap { "lo" counter packets 0 bytes 0 : jump wan_input }
}
}
diff --git a/tests/shell/testcases/maps/dumps/0010concat_map_0.json-nft b/tests/shell/testcases/maps/dumps/0010concat_map_0.json-nft
new file mode 100644
index 00000000..fcc23bb8
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0010concat_map_0.json-nft
@@ -0,0 +1,106 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "y",
+ "handle": 0,
+ "type": "nat",
+ "hook": "prerouting",
+ "prio": -100,
+ "policy": "accept"
+ }
+ },
+ {
+ "map": {
+ "family": "inet",
+ "name": "z",
+ "table": "x",
+ "type": [
+ "ipv4_addr",
+ "inet_proto",
+ "inet_service"
+ ],
+ "handle": 0,
+ "map": [
+ "ipv4_addr",
+ "inet_service"
+ ],
+ "elem": [
+ [
+ {
+ "concat": [
+ "1.1.1.1",
+ "tcp",
+ 20
+ ]
+ },
+ {
+ "concat": [
+ "2.2.2.2",
+ 30
+ ]
+ }
+ ]
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "dnat": {
+ "family": "ip",
+ "addr": {
+ "map": {
+ "key": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "protocol"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ }
+ ]
+ },
+ "data": "@z"
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/0010concat_map_0.nft b/tests/shell/testcases/maps/dumps/0010concat_map_0.nft
index b6bc338c..2f796b51 100644
--- a/tests/shell/testcases/maps/dumps/0010concat_map_0.nft
+++ b/tests/shell/testcases/maps/dumps/0010concat_map_0.nft
@@ -6,6 +6,6 @@ table inet x {
chain y {
type nat hook prerouting priority dstnat; policy accept;
- meta nfproto ipv4 dnat ip to ip saddr . ip protocol . tcp dport map @z
+ dnat ip to ip saddr . ip protocol . tcp dport map @z
}
}
diff --git a/tests/shell/testcases/maps/dumps/0011vmap_0.json-nft b/tests/shell/testcases/maps/dumps/0011vmap_0.json-nft
new file mode 100644
index 00000000..8f07378a
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0011vmap_0.json-nft
@@ -0,0 +1,145 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "ssh_input",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "wan_input",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "prerouting",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -300,
+ "policy": "accept"
+ }
+ },
+ {
+ "map": {
+ "family": "inet",
+ "name": "portmap",
+ "table": "filter",
+ "type": "inet_service",
+ "handle": 0,
+ "map": "verdict",
+ "elem": [
+ [
+ {
+ "elem": {
+ "val": 22,
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ },
+ {
+ "jump": {
+ "target": "ssh_input"
+ }
+ }
+ ],
+ [
+ {
+ "elem": {
+ "val": "*",
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ ],
+ "stmt": [
+ {
+ "counter": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "wan_input",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "data": "@portmap"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "prerouting",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "meta": {
+ "key": "iif"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ "lo",
+ {
+ "jump": {
+ "target": "wan_input"
+ }
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/0012map_0.json-nft b/tests/shell/testcases/maps/dumps/0012map_0.json-nft
new file mode 100644
index 00000000..2892e11d
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0012map_0.json-nft
@@ -0,0 +1,97 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "z",
+ "table": "x",
+ "type": "ifname",
+ "handle": 0,
+ "map": "verdict",
+ "elem": [
+ [
+ "lo",
+ {
+ "accept": null
+ }
+ ],
+ [
+ "eth0",
+ {
+ "drop": null
+ }
+ ],
+ [
+ "eth1",
+ {
+ "drop": null
+ }
+ ]
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ "lo",
+ {
+ "accept": null
+ }
+ ],
+ [
+ "eth0",
+ {
+ "drop": null
+ }
+ ],
+ [
+ "eth1",
+ {
+ "drop": null
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/0012map_concat_0.json-nft b/tests/shell/testcases/maps/dumps/0012map_concat_0.json-nft
new file mode 100644
index 00000000..00052236
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0012map_concat_0.json-nft
@@ -0,0 +1,132 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "k",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 1,
+ "policy": "accept"
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "w",
+ "table": "x",
+ "type": [
+ "ipv4_addr",
+ "mark"
+ ],
+ "handle": 0,
+ "map": "verdict",
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ [
+ {
+ "elem": {
+ "val": {
+ "concat": [
+ {
+ "range": [
+ "127.0.0.1",
+ "127.0.0.4"
+ ]
+ },
+ {
+ "range": [
+ 1193012,
+ 11534626
+ ]
+ }
+ ]
+ },
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ ],
+ "stmt": [
+ {
+ "counter": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "k",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": 1193012
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "k",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "meta": {
+ "key": "mark"
+ }
+ }
+ ]
+ },
+ "data": "@w"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/0012map_concat_0.nft b/tests/shell/testcases/maps/dumps/0012map_concat_0.nft
new file mode 100644
index 00000000..6649d034
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0012map_concat_0.nft
@@ -0,0 +1,14 @@
+table ip x {
+ map w {
+ typeof ip saddr . meta mark : verdict
+ flags interval
+ counter
+ elements = { 127.0.0.1-127.0.0.4 . 0x00123434-0x00b00122 counter packets 0 bytes 0 : accept }
+ }
+
+ chain k {
+ type filter hook input priority filter + 1; policy accept;
+ meta mark set 0x00123434
+ ip saddr . meta mark vmap @w
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/0013map_0.json-nft b/tests/shell/testcases/maps/dumps/0013map_0.json-nft
new file mode 100644
index 00000000..e91a269d
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0013map_0.json-nft
@@ -0,0 +1,128 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "FORWARD",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 0,
+ "policy": "drop"
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "forwport",
+ "table": "filter",
+ "type": [
+ "ipv4_addr",
+ "inet_proto",
+ "inet_service"
+ ],
+ "handle": 0,
+ "map": "verdict",
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ [
+ {
+ "elem": {
+ "val": {
+ "concat": [
+ "10.133.89.138",
+ "tcp",
+ 8081
+ ]
+ },
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ ],
+ "stmt": [
+ {
+ "counter": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "FORWARD",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "enp0s8"
+ }
+ },
+ {
+ "vmap": {
+ "key": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "protocol"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "th",
+ "field": "dport"
+ }
+ }
+ ]
+ },
+ "data": "@forwport"
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/0014destroy_0.json-nft b/tests/shell/testcases/maps/dumps/0014destroy_0.json-nft
new file mode 100644
index 00000000..15ec0aac
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0014destroy_0.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/0014destroy_0.nft b/tests/shell/testcases/maps/dumps/0014destroy_0.nft
new file mode 100644
index 00000000..5d4d2caf
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0014destroy_0.nft
@@ -0,0 +1,2 @@
+table ip x {
+}
diff --git a/tests/shell/testcases/maps/dumps/0016map_leak_0.json-nft b/tests/shell/testcases/maps/dumps/0016map_leak_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0016map_leak_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/0016map_leak_0.nft b/tests/shell/testcases/maps/dumps/0016map_leak_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0016map_leak_0.nft
diff --git a/tests/shell/testcases/maps/dumps/0017_map_variable_0.json-nft b/tests/shell/testcases/maps/dumps/0017_map_variable_0.json-nft
new file mode 100644
index 00000000..725498cd
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0017_map_variable_0.json-nft
@@ -0,0 +1,58 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "mark",
+ "elem": [
+ [
+ "1.1.1.1",
+ 2
+ ],
+ [
+ "*",
+ 3
+ ]
+ ]
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "z",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "mark",
+ "elem": [
+ [
+ "1.1.1.1",
+ 2
+ ],
+ [
+ "*",
+ 3
+ ]
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/0017_map_variable_0.nft b/tests/shell/testcases/maps/dumps/0017_map_variable_0.nft
new file mode 100644
index 00000000..796dd729
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0017_map_variable_0.nft
@@ -0,0 +1,11 @@
+table ip x {
+ map y {
+ typeof ip saddr : meta mark
+ elements = { 1.1.1.1 : 0x00000002, * : 0x00000003 }
+ }
+
+ map z {
+ typeof ip saddr : meta mark
+ elements = { 1.1.1.1 : 0x00000002, * : 0x00000003 }
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/0018map_leak_timeout_0.json-nft b/tests/shell/testcases/maps/dumps/0018map_leak_timeout_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0018map_leak_timeout_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/0018map_leak_timeout_0.nft b/tests/shell/testcases/maps/dumps/0018map_leak_timeout_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0018map_leak_timeout_0.nft
diff --git a/tests/shell/testcases/maps/dumps/0024named_objects_0.json-nft b/tests/shell/testcases/maps/dumps/0024named_objects_0.json-nft
new file mode 100644
index 00000000..aa2f6f8c
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/0024named_objects_0.json-nft
@@ -0,0 +1,165 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "y",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "counter": {
+ "family": "inet",
+ "name": "user123",
+ "table": "x",
+ "handle": 0,
+ "packets": 12,
+ "bytes": 1433
+ }
+ },
+ {
+ "counter": {
+ "family": "inet",
+ "name": "user321",
+ "table": "x",
+ "handle": 0,
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "quota": {
+ "family": "inet",
+ "name": "user123",
+ "table": "x",
+ "handle": 0,
+ "bytes": 2000,
+ "used": 0,
+ "inv": true
+ }
+ },
+ {
+ "quota": {
+ "family": "inet",
+ "name": "user124",
+ "table": "x",
+ "handle": 0,
+ "bytes": 2000,
+ "used": 0,
+ "inv": true
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "inet",
+ "name": "test",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "quota",
+ "elem": [
+ [
+ "192.168.2.2",
+ "user124"
+ ],
+ [
+ "192.168.2.3",
+ "user124"
+ ]
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "counter": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ "1.1.1.1",
+ "user123"
+ ],
+ [
+ "2.2.2.2",
+ "user123"
+ ],
+ [
+ "192.168.2.2",
+ "user123"
+ ]
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "quota": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": "@test"
+ }
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0024named_objects_0.nft b/tests/shell/testcases/maps/dumps/0024named_objects_0.nft
index 2ffa4f2f..2ffa4f2f 100644
--- a/tests/shell/testcases/sets/dumps/0024named_objects_0.nft
+++ b/tests/shell/testcases/maps/dumps/0024named_objects_0.nft
diff --git a/tests/shell/testcases/maps/dumps/anon_objmap_concat.json-nft b/tests/shell/testcases/maps/dumps/anon_objmap_concat.json-nft
new file mode 100644
index 00000000..64209842
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/anon_objmap_concat.json-nft
@@ -0,0 +1,116 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "ct helper": {
+ "family": "inet",
+ "name": "sip-5060u",
+ "table": "filter",
+ "handle": 0,
+ "type": "sip",
+ "protocol": "udp",
+ "l3proto": "ip"
+ }
+ },
+ {
+ "ct helper": {
+ "family": "inet",
+ "name": "sip-5060t",
+ "table": "filter",
+ "handle": 0,
+ "type": "sip",
+ "protocol": "tcp",
+ "l3proto": "ip"
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "ct helper": {
+ "map": {
+ "key": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "protocol"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "th",
+ "field": "dport"
+ }
+ }
+ ]
+ },
+ "data": {
+ "set": [
+ [
+ {
+ "concat": [
+ "udp",
+ {
+ "range": [
+ 10000,
+ 20000
+ ]
+ }
+ ]
+ },
+ "sip-5060u"
+ ],
+ [
+ {
+ "concat": [
+ "tcp",
+ {
+ "range": [
+ 10000,
+ 20000
+ ]
+ }
+ ]
+ },
+ "sip-5060t"
+ ]
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/anon_objmap_concat.nft b/tests/shell/testcases/maps/dumps/anon_objmap_concat.nft
new file mode 100644
index 00000000..23aca0a2
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/anon_objmap_concat.nft
@@ -0,0 +1,16 @@
+table inet filter {
+ ct helper sip-5060u {
+ type "sip" protocol udp
+ l3proto ip
+ }
+
+ ct helper sip-5060t {
+ type "sip" protocol tcp
+ l3proto ip
+ }
+
+ chain input {
+ type filter hook input priority filter; policy accept;
+ ct helper set ip protocol . th dport map { udp . 10000-20000 : "sip-5060u", tcp . 10000-20000 : "sip-5060t" }
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/anonymous_snat_map_0.json-nft b/tests/shell/testcases/maps/dumps/anonymous_snat_map_0.json-nft
new file mode 100644
index 00000000..f4c55706
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/anonymous_snat_map_0.json-nft
@@ -0,0 +1,58 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "nat",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "nat",
+ "name": "postrouting",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "nat",
+ "chain": "postrouting",
+ "handle": 0,
+ "expr": [
+ {
+ "snat": {
+ "addr": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ "1.1.1.1",
+ "2.2.2.2"
+ ]
+ ]
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/different_map_types_1.json-nft b/tests/shell/testcases/maps/dumps/different_map_types_1.json-nft
new file mode 100644
index 00000000..ed0ce0ed
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/different_map_types_1.json-nft
@@ -0,0 +1,30 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "output",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/different_map_types_1.nft b/tests/shell/testcases/maps/dumps/different_map_types_1.nft
new file mode 100644
index 00000000..3c18b5c7
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/different_map_types_1.nft
@@ -0,0 +1,5 @@
+table ip filter {
+ chain output {
+ type filter hook output priority filter; policy accept;
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/map_catchall_double_deactivate.json-nft b/tests/shell/testcases/maps/dumps/map_catchall_double_deactivate.json-nft
new file mode 100644
index 00000000..49b8bb29
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/map_catchall_double_deactivate.json-nft
@@ -0,0 +1,26 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "test",
+ "name": "testchain",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/map_catchall_double_deactivate.nft b/tests/shell/testcases/maps/dumps/map_catchall_double_deactivate.nft
new file mode 100644
index 00000000..37c48bf3
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/map_catchall_double_deactivate.nft
@@ -0,0 +1,4 @@
+table ip test {
+ chain testchain {
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/map_catchall_double_free.nodump b/tests/shell/testcases/maps/dumps/map_catchall_double_free.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/map_catchall_double_free.nodump
diff --git a/tests/shell/testcases/maps/dumps/map_catchall_double_free_2.json-nft b/tests/shell/testcases/maps/dumps/map_catchall_double_free_2.json-nft
new file mode 100644
index 00000000..a9d4c8e9
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/map_catchall_double_free_2.json-nft
@@ -0,0 +1,46 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "test",
+ "name": "testchain",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "testmap",
+ "table": "test",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "verdict",
+ "elem": [
+ [
+ "*",
+ {
+ "jump": {
+ "target": "testchain"
+ }
+ }
+ ]
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/map_catchall_double_free_2.nft b/tests/shell/testcases/maps/dumps/map_catchall_double_free_2.nft
new file mode 100644
index 00000000..68958c40
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/map_catchall_double_free_2.nft
@@ -0,0 +1,9 @@
+table ip test {
+ map testmap {
+ type ipv4_addr : verdict
+ elements = { * : jump testchain }
+ }
+
+ chain testchain {
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/map_with_flags_0.json-nft b/tests/shell/testcases/maps/dumps/map_with_flags_0.json-nft
new file mode 100644
index 00000000..97b7e94e
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/map_with_flags_0.json-nft
@@ -0,0 +1,31 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "ipv4_addr",
+ "flags": [
+ "timeout"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/named_ct_objects.nft b/tests/shell/testcases/maps/dumps/named_ct_objects.nft
new file mode 100644
index 00000000..59f18932
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/named_ct_objects.nft
@@ -0,0 +1,71 @@
+table inet t {
+ ct expectation exp1 {
+ protocol tcp
+ dport 9876
+ timeout 1m
+ size 12
+ l3proto ip
+ }
+
+ ct expectation exp2 {
+ protocol tcp
+ dport 9876
+ timeout 3s
+ size 13
+ l3proto ip6
+ }
+
+ ct helper myftp {
+ type "ftp" protocol tcp
+ l3proto inet
+ }
+
+ ct timeout dns {
+ protocol tcp
+ l3proto ip
+ policy = { established : 3s, close : 1s }
+ }
+
+ map exp {
+ typeof ip saddr : ct expectation
+ elements = { 192.168.2.2 : "exp1" }
+ }
+
+ map exp6 {
+ typeof ip6 saddr : ct expectation
+ flags interval
+ elements = { dead:beef::/64 : "exp2" }
+ }
+
+ map helpobj {
+ typeof ip6 saddr : ct helper
+ flags interval
+ elements = { dead:beef::/64 : "myftp" }
+ }
+
+ map timeoutmap {
+ typeof ip daddr : ct timeout
+ elements = { 192.168.0.1 : "dns" }
+ }
+
+ set helpname {
+ typeof ct helper
+ elements = { "sip",
+ "ftp" }
+ }
+
+ chain y {
+ ct expectation set ip saddr map @exp
+ ct expectation set ip6 saddr map { dead::beef : "exp2" }
+ ct expectation set ip6 daddr map { dead::beef : "exp2", feed::17 : "exp2" }
+ ct expectation set ip6 daddr . tcp dport map { feed::17 . 512 : "exp2", dead::beef . 123 : "exp2" }
+ ct helper set ip6 saddr map { 1c3::c01d : "myftp", dead::beef : "myftp" }
+ ct helper set ip6 saddr map @helpobj
+ ct timeout set ip daddr map @timeoutmap
+ ct timeout set ip daddr map { 1.2.3.4 : "dns", 5.6.7.8 : "dns", 192.168.8.0/24 : "dns" }
+ ct timeout set ip daddr map { 1.2.3.4-1.2.3.8 : "dns" }
+ ct timeout set ip6 daddr map { 1ce::/64 : "dns", dead::beef : "dns" }
+ ct helper @helpname accept
+ ip saddr 192.168.1.1 ct timeout set "dns"
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/named_limits.json-nft b/tests/shell/testcases/maps/dumps/named_limits.json-nft
new file mode 100644
index 00000000..7fa12981
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/named_limits.json-nft
@@ -0,0 +1,328 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "limit": {
+ "family": "inet",
+ "name": "tarpit-pps",
+ "table": "filter",
+ "handle": 0,
+ "rate": 1,
+ "per": "second",
+ "burst": 5
+ }
+ },
+ {
+ "limit": {
+ "family": "inet",
+ "name": "tarpit-bps",
+ "table": "filter",
+ "handle": 0,
+ "rate": 1,
+ "per": "second",
+ "rate_unit": "kbytes"
+ }
+ },
+ {
+ "limit": {
+ "family": "inet",
+ "name": "http-bulk-rl-1m",
+ "table": "filter",
+ "handle": 0,
+ "rate": 1,
+ "per": "second",
+ "rate_unit": "mbytes"
+ }
+ },
+ {
+ "limit": {
+ "family": "inet",
+ "name": "http-bulk-rl-10m",
+ "table": "filter",
+ "handle": 0,
+ "rate": 10,
+ "per": "second",
+ "rate_unit": "mbytes"
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "tarpit4",
+ "table": "filter",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 10000,
+ "flags": [
+ "timeout",
+ "dynamic"
+ ],
+ "timeout": 60
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "tarpit6",
+ "table": "filter",
+ "type": "ipv6_addr",
+ "handle": 0,
+ "size": 10000,
+ "flags": [
+ "timeout",
+ "dynamic"
+ ],
+ "timeout": 60
+ }
+ },
+ {
+ "map": {
+ "family": "inet",
+ "name": "addr4limit",
+ "table": "filter",
+ "type": [
+ "inet_proto",
+ "ipv4_addr",
+ "inet_service"
+ ],
+ "handle": 0,
+ "map": "limit",
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ [
+ {
+ "concat": [
+ "tcp",
+ {
+ "prefix": {
+ "addr": "192.168.0.0",
+ "len": 16
+ }
+ },
+ {
+ "range": [
+ 1,
+ 65535
+ ]
+ }
+ ]
+ },
+ "tarpit-bps"
+ ],
+ [
+ {
+ "concat": [
+ "udp",
+ {
+ "prefix": {
+ "addr": "192.168.0.0",
+ "len": 16
+ }
+ },
+ {
+ "range": [
+ 1,
+ 65535
+ ]
+ }
+ ]
+ },
+ "tarpit-pps"
+ ],
+ [
+ {
+ "concat": [
+ "tcp",
+ {
+ "range": [
+ "127.0.0.1",
+ "127.1.2.3"
+ ]
+ },
+ {
+ "range": [
+ 1,
+ 1024
+ ]
+ }
+ ]
+ },
+ "tarpit-pps"
+ ],
+ [
+ {
+ "concat": [
+ "tcp",
+ {
+ "range": [
+ "10.0.0.1",
+ "10.0.0.255"
+ ]
+ },
+ 80
+ ]
+ },
+ "http-bulk-rl-1m"
+ ],
+ [
+ {
+ "concat": [
+ "tcp",
+ {
+ "range": [
+ "10.0.0.1",
+ "10.0.0.255"
+ ]
+ },
+ 443
+ ]
+ },
+ "http-bulk-rl-1m"
+ ],
+ [
+ {
+ "concat": [
+ "tcp",
+ {
+ "prefix": {
+ "addr": "10.0.1.0",
+ "len": 24
+ }
+ },
+ {
+ "range": [
+ 1024,
+ 65535
+ ]
+ }
+ ]
+ },
+ "http-bulk-rl-10m"
+ ],
+ [
+ {
+ "concat": [
+ "tcp",
+ "10.0.2.1",
+ 22
+ ]
+ },
+ "http-bulk-rl-10m"
+ ]
+ ]
+ }
+ },
+ {
+ "map": {
+ "family": "inet",
+ "name": "saddr6limit",
+ "table": "filter",
+ "type": "ipv6_addr",
+ "handle": 0,
+ "map": "limit",
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ [
+ {
+ "range": [
+ "dead::beef",
+ "dead::1:aced"
+ ]
+ },
+ "tarpit-pps"
+ ]
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "limit": {
+ "map": {
+ "key": {
+ "concat": [
+ {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "th",
+ "field": "sport"
+ }
+ }
+ ]
+ },
+ "data": "@addr4limit"
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "limit": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "ip6",
+ "field": "saddr"
+ }
+ },
+ "data": "@saddr6limit"
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/named_limits.nft b/tests/shell/testcases/maps/dumps/named_limits.nft
new file mode 100644
index 00000000..214df204
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/named_limits.nft
@@ -0,0 +1,55 @@
+table inet filter {
+ limit tarpit-pps {
+ rate 1/second
+ }
+
+ limit tarpit-bps {
+ rate 1 kbytes/second
+ }
+
+ limit http-bulk-rl-1m {
+ rate 1 mbytes/second
+ }
+
+ limit http-bulk-rl-10m {
+ rate 10 mbytes/second
+ }
+
+ set tarpit4 {
+ typeof ip saddr
+ size 10000
+ flags dynamic,timeout
+ timeout 1m
+ }
+
+ set tarpit6 {
+ typeof ip6 saddr
+ size 10000
+ flags dynamic,timeout
+ timeout 1m
+ }
+
+ map addr4limit {
+ typeof meta l4proto . ip saddr . tcp sport : limit
+ flags interval
+ elements = { tcp . 192.168.0.0/16 . 1-65535 : "tarpit-bps",
+ udp . 192.168.0.0/16 . 1-65535 : "tarpit-pps",
+ tcp . 127.0.0.1-127.1.2.3 . 1-1024 : "tarpit-pps",
+ tcp . 10.0.0.1-10.0.0.255 . 80 : "http-bulk-rl-1m",
+ tcp . 10.0.0.1-10.0.0.255 . 443 : "http-bulk-rl-1m",
+ tcp . 10.0.1.0/24 . 1024-65535 : "http-bulk-rl-10m",
+ tcp . 10.0.2.1 . 22 : "http-bulk-rl-10m" }
+ }
+
+ map saddr6limit {
+ typeof ip6 saddr : limit
+ flags interval
+ elements = { dead::beef-dead::1:aced : "tarpit-pps" }
+ }
+
+ chain input {
+ type filter hook input priority filter; policy accept;
+ limit name meta l4proto . ip saddr . th sport map @addr4limit
+ limit name ip6 saddr map @saddr6limit
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/named_snat_map_0.json-nft b/tests/shell/testcases/maps/dumps/named_snat_map_0.json-nft
new file mode 100644
index 00000000..ad9eb36e
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/named_snat_map_0.json-nft
@@ -0,0 +1,67 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "nat",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "nat",
+ "name": "postrouting",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "m",
+ "table": "nat",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "ipv4_addr",
+ "elem": [
+ [
+ "1.1.1.1",
+ "2.2.2.2"
+ ]
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "nat",
+ "chain": "postrouting",
+ "handle": 0,
+ "expr": [
+ {
+ "snat": {
+ "addr": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": "@m"
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/nat_addr_port.nft b/tests/shell/testcases/maps/dumps/nat_addr_port.nft
index cf6b957f..c8493b3a 100644
--- a/tests/shell/testcases/maps/dumps/nat_addr_port.nft
+++ b/tests/shell/testcases/maps/dumps/nat_addr_port.nft
@@ -114,15 +114,15 @@ table inet inetfoo {
dnat ip to ip daddr map @x4
ip saddr 10.1.1.1 dnat ip to 10.2.3.4
ip saddr 10.1.1.2 tcp dport 42 dnat ip to 10.2.3.4:4242
- meta l4proto tcp meta nfproto ipv4 dnat ip to ip saddr map @y4
- meta nfproto ipv4 dnat ip to ip saddr . tcp dport map @z4
+ meta l4proto tcp dnat ip to ip saddr map @y4
+ dnat ip to ip saddr . tcp dport map @z4
dnat ip to numgen inc mod 2 map @t1v4
meta l4proto tcp dnat ip to numgen inc mod 2 map @t2v4
dnat ip6 to ip6 daddr map @x6
ip6 saddr dead::1 dnat ip6 to feed::1
ip6 saddr dead::2 tcp dport 42 dnat ip6 to [c0::1a]:4242
- meta l4proto tcp meta nfproto ipv6 dnat ip6 to ip6 saddr map @y6
- meta nfproto ipv6 dnat ip6 to ip6 saddr . tcp dport map @z6
+ meta l4proto tcp dnat ip6 to ip6 saddr map @y6
+ dnat ip6 to ip6 saddr . tcp dport map @z6
dnat ip6 to numgen inc mod 2 map @t1v6
meta l4proto tcp dnat ip6 to numgen inc mod 2 map @t2v6
}
diff --git a/tests/shell/testcases/maps/dumps/pipapo_double_flush.json-nft b/tests/shell/testcases/maps/dumps/pipapo_double_flush.json-nft
new file mode 100644
index 00000000..ef8c3930
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/pipapo_double_flush.json-nft
@@ -0,0 +1,42 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "inet",
+ "name": "m",
+ "table": "t",
+ "type": [
+ "ipv4_addr",
+ "ipv4_addr"
+ ],
+ "handle": 0,
+ "map": "verdict",
+ "flags": [
+ "interval"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/pipapo_double_flush.nft b/tests/shell/testcases/maps/dumps/pipapo_double_flush.nft
new file mode 100644
index 00000000..cca569ea
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/pipapo_double_flush.nft
@@ -0,0 +1,9 @@
+table inet t {
+ map m {
+ type ipv4_addr . ipv4_addr : verdict
+ flags interval
+ }
+
+ chain c {
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/typeof_integer_0.nft b/tests/shell/testcases/maps/dumps/typeof_integer_0.nft
new file mode 100644
index 00000000..19c24feb
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/typeof_integer_0.nft
@@ -0,0 +1,20 @@
+table inet t {
+ map m1 {
+ typeof udp length . @ih,32,32 : verdict
+ flags interval
+ elements = { 20-80 . 0x14 : accept,
+ 1-10 . 0xa : drop }
+ }
+
+ map m2 {
+ typeof udp length . @ih,32,32 : verdict
+ elements = { 30 . 0x1e : drop,
+ 20 . 0x24 : accept }
+ }
+
+ chain c {
+ udp length . @nh,32,32 vmap @m1
+ udp length . @nh,32,32 vmap @m2
+ udp length . @th,160,128 vmap { 47-63 . 0xe373135363130333131303735353203 : accept }
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/typeof_maps_0.nft b/tests/shell/testcases/maps/dumps/typeof_maps_0.nft
index 438b9829..a5c0a609 100644
--- a/tests/shell/testcases/maps/dumps/typeof_maps_0.nft
+++ b/tests/shell/testcases/maps/dumps/typeof_maps_0.nft
@@ -17,11 +17,20 @@ table inet t {
map m4 {
typeof iifname . ip protocol . th dport : verdict
+ elements = { "eth0" . tcp . 22 : accept }
+ }
+
+ map m5 {
+ typeof ipsec in reqid . iifname : verdict
+ elements = { 23 . "eth0" : accept }
}
chain c {
ct mark set osf name map @m1
meta mark set vlan id map @m2
meta mark set ip saddr . ip daddr map @m3
+ iifname . ip protocol . th dport vmap @m4
+ iifname . ip protocol . th dport vmap { "eth0" . tcp . 22 : accept, "eth1" . udp . 67 : drop }
+ ipsec in reqid . iifname vmap @m5
}
}
diff --git a/tests/shell/testcases/maps/dumps/typeof_maps_add_delete.json-nft b/tests/shell/testcases/maps/dumps/typeof_maps_add_delete.json-nft
new file mode 100644
index 00000000..8130c46c
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/typeof_maps_add_delete.json-nft
@@ -0,0 +1,283 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "dynset",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "dynset",
+ "name": "test_ping",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "dynset",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "dynmark",
+ "table": "dynset",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "mark",
+ "size": 64,
+ "flags": [
+ "timeout"
+ ],
+ "timeout": 300,
+ "stmt": [
+ {
+ "counter": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "dynset",
+ "chain": "test_ping",
+ "handle": 0,
+ "comment": "should not increment",
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "@dynmark"
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "dynset",
+ "chain": "test_ping",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "!=",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "@dynmark"
+ }
+ },
+ {
+ "map": {
+ "op": "add",
+ "elem": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": 1,
+ "map": "@dynmark"
+ }
+ },
+ {
+ "counter": {
+ "packets": 1,
+ "bytes": 84
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "dynset",
+ "chain": "test_ping",
+ "handle": 0,
+ "comment": "should increment",
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "@dynmark"
+ }
+ },
+ {
+ "counter": {
+ "packets": 1,
+ "bytes": 84
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "dynset",
+ "chain": "test_ping",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "@dynmark"
+ }
+ },
+ {
+ "map": {
+ "op": "delete",
+ "elem": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": 1,
+ "map": "@dynmark"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "dynset",
+ "chain": "test_ping",
+ "handle": 0,
+ "comment": "delete should be instant but might fail under memory pressure",
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "@dynmark"
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "dynset",
+ "chain": "input",
+ "handle": 0,
+ "comment": "also check timeout-gc",
+ "expr": [
+ {
+ "map": {
+ "op": "add",
+ "elem": {
+ "elem": {
+ "val": "10.2.3.4",
+ "timeout": 1
+ }
+ },
+ "data": 2,
+ "map": "@dynmark"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "dynset",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "right": "icmp"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "right": "127.0.0.42"
+ }
+ },
+ {
+ "jump": {
+ "target": "test_ping"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/typeof_maps_add_delete.nft b/tests/shell/testcases/maps/dumps/typeof_maps_add_delete.nft
new file mode 100644
index 00000000..9134673c
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/typeof_maps_add_delete.nft
@@ -0,0 +1,22 @@
+table ip dynset {
+ map dynmark {
+ typeof ip daddr : meta mark
+ size 64
+ counter
+ timeout 5m
+ }
+
+ chain test_ping {
+ ip saddr @dynmark counter packets 0 bytes 0 comment "should not increment"
+ ip saddr != @dynmark add @dynmark { ip saddr : 0x00000001 } counter packets 1 bytes 84
+ ip saddr @dynmark counter packets 1 bytes 84 comment "should increment"
+ ip saddr @dynmark delete @dynmark { ip saddr : 0x00000001 }
+ ip saddr @dynmark counter packets 0 bytes 0 comment "delete should be instant but might fail under memory pressure"
+ }
+
+ chain input {
+ type filter hook input priority filter; policy accept;
+ add @dynmark { 10.2.3.4 timeout 1s : 0x00000002 } comment "also check timeout-gc"
+ meta l4proto icmp ip daddr 127.0.0.42 jump test_ping
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/typeof_maps_concat.nft b/tests/shell/testcases/maps/dumps/typeof_maps_concat.nft
new file mode 100644
index 00000000..1ca98d81
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/typeof_maps_concat.nft
@@ -0,0 +1,11 @@
+table netdev t {
+ map m {
+ typeof ether saddr . vlan id : meta mark
+ size 1234
+ flags dynamic,timeout
+ }
+
+ chain c {
+ ether type != 8021q update @m { ether daddr . 123 timeout 1m : 0x0000002a } counter packets 0 bytes 0 return
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/typeof_maps_concat_update_0.nft b/tests/shell/testcases/maps/dumps/typeof_maps_concat_update_0.nft
new file mode 100644
index 00000000..f8b574f4
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/typeof_maps_concat_update_0.nft
@@ -0,0 +1,13 @@
+table ip foo {
+ map pinned {
+ typeof ip saddr . ct original proto-dst : ip daddr . tcp dport
+ size 65535
+ flags dynamic,timeout
+ timeout 6m
+ }
+
+ chain pr {
+ update @pinned { ip saddr . ct original proto-dst timeout 1m30s : ip daddr . tcp dport }
+ update @pinned { ip saddr . ct original proto-dst timeout 1m30s : ip daddr . tcp dport }
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/typeof_maps_update_0.json-nft b/tests/shell/testcases/maps/dumps/typeof_maps_update_0.json-nft
new file mode 100644
index 00000000..1d50477d
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/typeof_maps_update_0.json-nft
@@ -0,0 +1,110 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "kube-nfproxy-v4",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "kube-nfproxy-v4",
+ "name": "k8s-nfproxy-sep-TMVEFT7EX55F4T62",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "kube-nfproxy-v4",
+ "name": "k8s-nfproxy-sep-GMVEFT7EX55F4T62",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "sticky-set-svc-M53CN2XYVUHRQ7UB",
+ "table": "kube-nfproxy-v4",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "mark",
+ "size": 65535,
+ "flags": [
+ "timeout"
+ ],
+ "timeout": 360
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "sticky-set-svc-153CN2XYVUHRQ7UB",
+ "table": "kube-nfproxy-v4",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "mark",
+ "size": 65535,
+ "flags": [
+ "timeout"
+ ],
+ "timeout": 60
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "kube-nfproxy-v4",
+ "chain": "k8s-nfproxy-sep-TMVEFT7EX55F4T62",
+ "handle": 0,
+ "expr": [
+ {
+ "map": {
+ "op": "update",
+ "elem": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": 2,
+ "map": "@sticky-set-svc-M53CN2XYVUHRQ7UB"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "kube-nfproxy-v4",
+ "chain": "k8s-nfproxy-sep-GMVEFT7EX55F4T62",
+ "handle": 0,
+ "expr": [
+ {
+ "map": {
+ "op": "update",
+ "elem": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": 3,
+ "map": "@sticky-set-svc-153CN2XYVUHRQ7UB"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/typeof_raw_0.nft b/tests/shell/testcases/maps/dumps/typeof_raw_0.nft
new file mode 100644
index 00000000..476169f2
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/typeof_raw_0.nft
@@ -0,0 +1,13 @@
+table ip x {
+ map y {
+ typeof ip saddr . @ih,32,32 : verdict
+ elements = { 1.1.1.1 . 0x14 : accept,
+ 7.7.7.7 . 0x86 : accept,
+ 7.7.7.8 . 0x97 : drop }
+ }
+
+ chain y {
+ ip saddr . @nh,32,32 vmap @y
+ ip saddr . @nh,32,32 vmap { 4.4.4.4 . 0x34 : accept, 5.5.5.5 . 0x45 : drop }
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/vmap_mark_bitwise_0.json-nft b/tests/shell/testcases/maps/dumps/vmap_mark_bitwise_0.json-nft
new file mode 100644
index 00000000..df156411
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/vmap_mark_bitwise_0.json-nft
@@ -0,0 +1,158 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "sctm_o0_0",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "sctm_o0_1",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "SET_ctmark_RPLYroute",
+ "handle": 0
+ }
+ },
+ {
+ "counter": {
+ "family": "ip",
+ "name": "c_o0_0",
+ "table": "x",
+ "handle": 0,
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "sctm_o0",
+ "table": "x",
+ "type": "mark",
+ "handle": 0,
+ "map": "verdict",
+ "elem": [
+ [
+ 0,
+ {
+ "jump": {
+ "target": "sctm_o0_0"
+ }
+ }
+ ],
+ [
+ 1,
+ {
+ "jump": {
+ "target": "sctm_o0_1"
+ }
+ }
+ ]
+ ]
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "sctm_o1",
+ "table": "x",
+ "type": "mark",
+ "handle": 0,
+ "map": "counter",
+ "elem": [
+ [
+ 0,
+ "c_o0_0"
+ ]
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "SET_ctmark_RPLYroute",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "&": [
+ {
+ ">>": [
+ {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ 8
+ ]
+ },
+ 15
+ ]
+ },
+ "data": "@sctm_o0"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "SET_ctmark_RPLYroute",
+ "handle": 0,
+ "expr": [
+ {
+ "counter": {
+ "map": {
+ "key": {
+ "&": [
+ {
+ ">>": [
+ {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ 8
+ ]
+ },
+ 15
+ ]
+ },
+ "data": "@sctm_o1"
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/vmap_mark_bitwise_0.nft b/tests/shell/testcases/maps/dumps/vmap_mark_bitwise_0.nft
new file mode 100644
index 00000000..beb5ffb0
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/vmap_mark_bitwise_0.nft
@@ -0,0 +1,26 @@
+table ip x {
+ counter c_o0_0 {
+ packets 0 bytes 0
+ }
+
+ map sctm_o0 {
+ type mark : verdict
+ elements = { 0x00000000 : jump sctm_o0_0, 0x00000001 : jump sctm_o0_1 }
+ }
+
+ map sctm_o1 {
+ type mark : counter
+ elements = { 0x00000000 : "c_o0_0" }
+ }
+
+ chain sctm_o0_0 {
+ }
+
+ chain sctm_o0_1 {
+ }
+
+ chain SET_ctmark_RPLYroute {
+ meta mark >> 8 & 0xf vmap @sctm_o0
+ counter name meta mark >> 8 & 0xf map @sctm_o1
+ }
+}
diff --git a/tests/shell/testcases/maps/dumps/vmap_timeout.json-nft b/tests/shell/testcases/maps/dumps/vmap_timeout.json-nft
new file mode 100644
index 00000000..1c3aa590
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/vmap_timeout.json-nft
@@ -0,0 +1,229 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "ssh_input",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "log_and_drop",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "other_input",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "wan_input",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "prerouting",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -300,
+ "policy": "accept"
+ }
+ },
+ {
+ "map": {
+ "family": "inet",
+ "name": "portmap",
+ "table": "filter",
+ "type": "inet_service",
+ "handle": 0,
+ "map": "verdict",
+ "flags": [
+ "timeout"
+ ],
+ "gc-interval": 10,
+ "elem": [
+ [
+ 22,
+ {
+ "jump": {
+ "target": "ssh_input"
+ }
+ }
+ ]
+ ]
+ }
+ },
+ {
+ "map": {
+ "family": "inet",
+ "name": "portaddrmap",
+ "table": "filter",
+ "type": [
+ "ipv4_addr",
+ "inet_service"
+ ],
+ "handle": 0,
+ "map": "verdict",
+ "flags": [
+ "timeout"
+ ],
+ "gc-interval": 10,
+ "elem": [
+ [
+ {
+ "concat": [
+ "1.2.3.4",
+ 22
+ ]
+ },
+ {
+ "jump": {
+ "target": "ssh_input"
+ }
+ }
+ ]
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "log_and_drop",
+ "handle": 0,
+ "expr": [
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "other_input",
+ "handle": 0,
+ "expr": [
+ {
+ "goto": {
+ "target": "log_and_drop"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "wan_input",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ }
+ ]
+ },
+ "data": "@portaddrmap"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "wan_input",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "data": "@portmap"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "prerouting",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "meta": {
+ "key": "iif"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ "lo",
+ {
+ "jump": {
+ "target": "wan_input"
+ }
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/maps/dumps/vmap_timeout.nft b/tests/shell/testcases/maps/dumps/vmap_timeout.nft
new file mode 100644
index 00000000..095f894d
--- /dev/null
+++ b/tests/shell/testcases/maps/dumps/vmap_timeout.nft
@@ -0,0 +1,36 @@
+table inet filter {
+ map portmap {
+ type inet_service : verdict
+ flags timeout
+ gc-interval 10s
+ elements = { 22 : jump ssh_input }
+ }
+
+ map portaddrmap {
+ typeof ip daddr . th dport : verdict
+ flags timeout
+ gc-interval 10s
+ elements = { 1.2.3.4 . 22 : jump ssh_input }
+ }
+
+ chain ssh_input {
+ }
+
+ chain log_and_drop {
+ drop
+ }
+
+ chain other_input {
+ goto log_and_drop
+ }
+
+ chain wan_input {
+ ip daddr . tcp dport vmap @portaddrmap
+ tcp dport vmap @portmap
+ }
+
+ chain prerouting {
+ type filter hook prerouting priority raw; policy accept;
+ iif vmap { "lo" : jump wan_input }
+ }
+}
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 00000000..46c538b7
--- /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/map_catchall_double_deactivate b/tests/shell/testcases/maps/map_catchall_double_deactivate
new file mode 100755
index 00000000..651c08a1
--- /dev/null
+++ b/tests/shell/testcases/maps/map_catchall_double_deactivate
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_catchall_element)
+
+$NFT "add table ip test ;
+ add map ip test testmap { type ipv4_addr : verdict; };
+ add chain ip test testchain;
+ add element ip test testmap { * : jump testchain }" || exit 1
+
+$NFT "flush map ip test testmap; delete map ip test testmap; delete map ip test testmap" 2>/dev/null && exit 1
+$NFT "flush map ip test testmap; delete map ip test testmap; delete element ip test testmap { * : jump testchain }" 2>/dev/null && exit 1
+
+$NFT "flush map ip test testmap; delete map ip test testmap" || exit 1
diff --git a/tests/shell/testcases/maps/map_catchall_double_free b/tests/shell/testcases/maps/map_catchall_double_free
new file mode 100755
index 00000000..d101256c
--- /dev/null
+++ b/tests/shell/testcases/maps/map_catchall_double_free
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_catchall_element)
+
+$NFT "add table ip test ;
+ add map ip test testmap { type ipv4_addr . ipv4_addr: verdict; flags interval,timeout; timeout 1s;};
+ add chain ip test testchain;
+ add element ip test testmap { * : jump testchain }" || exit 1
+
+sleep 2
+$NFT "add element ip test testmap { 1.2.3.4 . 5.6.7.8: jump testchain }" || exit 1
+sleep 2
+$NFT "add element ip test testmap { 2.3.4.5 . 6.7.8.9 timeout 1m: jump testchain }" || exit 1
diff --git a/tests/shell/testcases/maps/map_catchall_double_free_2 b/tests/shell/testcases/maps/map_catchall_double_free_2
new file mode 100755
index 00000000..5842fcb5
--- /dev/null
+++ b/tests/shell/testcases/maps/map_catchall_double_free_2
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_catchall_element)
+
+$NFT -f /dev/stdin <<EOF
+table ip test {
+ map testmap {
+ type ipv4_addr : verdict
+ elements = { * : jump testchain }
+ }
+
+ chain testchain { }
+}
+EOF
+
+# second attempt to delete the catchall element
+# musts trigger transaction abort
+$NFT -f /dev/stdin <<EOF
+delete element ip test testmap { * }
+delete element ip test testmap { * }
+EOF
+
+if [ $? -eq 1 ]; then
+ exit 0
+fi
+
+exit 1
diff --git a/tests/shell/testcases/maps/named_ct_objects b/tests/shell/testcases/maps/named_ct_objects
new file mode 100755
index 00000000..61b87c1a
--- /dev/null
+++ b/tests/shell/testcases/maps/named_ct_objects
@@ -0,0 +1,94 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_cttimeout)
+
+$NFT -f /dev/stdin <<EOF || exit 1
+table inet t {
+ ct expectation exp1 {
+ protocol tcp
+ dport 9876
+ timeout 1m
+ size 12
+ l3proto ip
+ }
+
+ ct expectation exp2 {
+ protocol tcp
+ dport 9876
+ timeout 3s
+ size 13
+ l3proto ip6
+ }
+
+ ct helper myftp {
+ type "ftp" protocol tcp
+ }
+
+ ct timeout dns {
+ protocol tcp
+ l3proto ip
+ policy = { established : 3, close : 1 }
+ }
+
+ map exp {
+ typeof ip saddr : ct expectation
+ elements = { 192.168.2.2 : "exp1" }
+ }
+
+ map exp6 {
+ typeof ip6 saddr : ct expectation
+ flags interval
+ elements = { dead:beef::/64 : "exp2" }
+ }
+
+ map helpobj {
+ typeof ip6 saddr : ct helper
+ flags interval
+ elements = { dead:beef::/64 : "myftp" }
+ }
+
+ map timeoutmap {
+ typeof ip daddr : ct timeout
+ elements = { 192.168.0.1 : "dns" }
+ }
+
+ set helpname {
+ typeof ct helper
+ elements = { "ftp", "sip" }
+ }
+
+ chain y {
+ ct expectation set ip saddr map @exp
+ ct expectation set ip6 saddr map { dead::beef : "exp2" }
+ ct expectation set ip6 daddr map { dead::beef : "exp2", feed::17 : "exp2" }
+ ct expectation set ip6 daddr . tcp dport map { dead::beef . 123 : "exp2", feed::17 . 512 : "exp2" }
+ ct helper set ip6 saddr map { dead::beef : "myftp", 1c3::c01d : "myftp" }
+ ct helper set ip6 saddr map @helpobj
+ ct timeout set ip daddr map @timeoutmap
+ ct timeout set ip daddr map { 1.2.3.4 : "dns", 5.6.7.8 : "dns", 192.168.8.0/24 : "dns" }
+ ct timeout set ip daddr map { 1.2.3.4-1.2.3.8 : "dns" }
+ ct timeout set ip6 daddr map { dead::beef : "dns", 1ce::/64 : "dns" }
+ ct helper @helpname accept
+ }
+}
+EOF
+
+must_fail()
+{
+ echo "Command should have failed: $1"
+ exit 111
+}
+
+
+must_work()
+{
+ echo "Command should have succeeded: $1"
+ exit 111
+}
+
+$NFT 'add rule inet t y ip saddr 192.168.1.1 ct timeout set "dns"' || must_work "dns timeout"
+
+$NFT 'add rule inet t y ct helper set ip saddr map @helpobj' && must_fail "helper assignment, map key is ipv6_addr"
+$NFT 'add rule inet t y ct helper set ip6 saddr map @helpname' && must_fail "helper assignment, not a map with objects"
+
+exit 0
diff --git a/tests/shell/testcases/maps/named_limits b/tests/shell/testcases/maps/named_limits
new file mode 100755
index 00000000..ac8e434c
--- /dev/null
+++ b/tests/shell/testcases/maps/named_limits
@@ -0,0 +1,61 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_pipapo)
+
+dumpfile=$(dirname $0)/dumps/$(basename $0).nft
+
+$NFT -f "$dumpfile" || exit 1
+
+add_add_then_create()
+{
+ cmd="$@"
+
+ $NFT "add element inet filter $cmd" || exit 2
+
+ # again, kernel should suppress -EEXIST
+ $NFT "add element inet filter $cmd" || exit 3
+
+ # AGAIN, kernel should report -EEXIST
+ $NFT "create element inet filter $cmd" && echo "$cmd worked" 1>&2 && exit 4
+}
+
+add_create_dupe()
+{
+ cmd="$@"
+
+ $NFT "add element inet filter $cmd" && echo "$cmd worked" 1>&2 && exit 10
+ $NFT "create element inet filter $cmd" && echo "$cmd worked" 1>&2 && exit 11
+}
+
+delete()
+{
+ cmd="$@"
+
+ $NFT "delete element inet filter $cmd" || exit 30
+ $NFT "delete element inet filter $cmd" && echo "$cmd worked" 1>&2 && exit 31
+
+ # destroy should NOT report an error
+# $NFT "destroy element inet filter $cmd" || exit 40
+}
+
+add_add_then_create 'saddr6limit { fee1::dead : "tarpit-pps" }'
+add_add_then_create 'saddr6limit { c01a::/64 : "tarpit-bps" }'
+
+# test same with a diffent set type (concat + interval)
+add_add_then_create 'addr4limit { udp . 1.2.3.4 . 42 : "tarpit-pps", tcp . 1.2.3.4 . 42 : "tarpit-pps" }'
+
+# now test duplicate key with *DIFFERENT* limiter, should fail
+add_create_dupe 'saddr6limit { fee1::dead : "tarpit-bps" }'
+
+add_create_dupe 'addr4limit { udp . 1.2.3.4 . 42 : "tarpit-pps", tcp . 1.2.3.4 . 42 : "http-bulk-rl-10m" }'
+add_create_dupe 'addr4limit { udp . 1.2.3.4 . 43 : "tarpit-pps", tcp . 1.2.3.4 . 42 : "http-bulk-rl-10m" }'
+add_create_dupe 'addr4limit { udp . 1.2.3.5 . 42 : "tarpit-pps", tcp . 1.2.3.4 . 42 : "http-bulk-rl-10m" }'
+add_create_dupe 'addr4limit { udp . 1.2.3.4 . 42 : "tarpit-bps", tcp . 1.2.3.4 . 42 : "tarpit-pps" }'
+
+# delete keys again
+delete 'addr4limit { udp . 1.2.3.4 . 42 : "tarpit-pps", tcp . 1.2.3.4 . 42 :"tarpit-pps" }'
+
+delete 'saddr6limit { fee1::dead : "tarpit-pps" }'
+delete 'saddr6limit { c01a::/64 : "tarpit-bps" }'
+
+exit 0
diff --git a/tests/shell/testcases/maps/pipapo_double_flush b/tests/shell/testcases/maps/pipapo_double_flush
new file mode 100755
index 00000000..35ad0966
--- /dev/null
+++ b/tests/shell/testcases/maps/pipapo_double_flush
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_pipapo)
+
+set -e
+
+$NFT add table inet t
+$NFT add chain inet t c
+$NFT 'add map inet t m { type ipv4_addr . ipv4_addr : verdict; flags interval;}'
+
+for i in $(seq 1 10); do
+ $NFT "add element inet t m { 10.0.0.1 . 1.2.$i.1 - 1.2.$i.10 : jump c }"
+done
+
+$NFT -f /dev/stdin <<EOF
+add element inet t m { 10.1.1.1 . 1.1.1.4 : accept }
+add element inet t m { 10.1.1.6 . 1.1.1.4 : drop }
+add element inet t m { 10.1.1.7 . 1.1.1.4 : jump c }
+flush map inet t m
+add element inet t m { 10.1.1.1 . 1.1.1.4 : accept }
+add element inet t m { 10.1.1.6 . 1.1.1.4 : drop }
+add element inet t m { 10.1.1.7 . 1.1.1.4 : jump c }
+flush map inet t m
+flush map inet t m
+EOF
diff --git a/tests/shell/testcases/maps/typeof_integer_0 b/tests/shell/testcases/maps/typeof_integer_0
new file mode 100755
index 00000000..e93604e8
--- /dev/null
+++ b/tests/shell/testcases/maps/typeof_integer_0
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_pipapo)
+
+EXPECTED="table inet t {
+ map m1 {
+ typeof udp length . @ih,32,32 : verdict
+ flags interval
+ elements = { 20-80 . 0x14 : accept, 1-10 . 0xa : drop }
+ }
+
+ map m2 {
+ typeof udp length . @ih,32,32 : verdict
+ elements = { 20 . 0x24 : accept, 30 . 0x1e : drop }
+ }
+
+ chain c {
+ udp length . @nh,32,32 vmap @m1
+ udp length . @nh,32,32 vmap @m2
+ udp length . @th,160,128 vmap { 47-63 . 0xe373135363130333131303735353203 : accept }
+ }
+}"
+
+$NFT add element inet t m1 { 90-100 . 40 : drop }
+$NFT delete element inet t m2 { 20 . 20 : accept }
+
+set -e
+$NFT -f - <<< $EXPECTED
+
diff --git a/tests/shell/testcases/maps/typeof_maps_0 b/tests/shell/testcases/maps/typeof_maps_0
index f024ebe0..98517fd5 100755
--- a/tests/shell/testcases/maps/typeof_maps_0
+++ b/tests/shell/testcases/maps/typeof_maps_0
@@ -4,10 +4,23 @@
# without typeof, this is 'type string' and 'type integer',
# but neither could be used because it lacks size information.
-EXPECTED="table inet t {
+set -e
+
+die() {
+ printf '%s\n' "$*"
+ exit 1
+}
+
+INPUT_OSF_CT="
+ ct mark set osf name map @m1"
+if [ "$NFT_TEST_HAVE_osf" = n ] ; then
+ INPUT_OSF_CT=
+fi
+
+INPUT="table inet t {
map m1 {
typeof osf name : ct mark
- elements = { "Linux" : 0x00000001 }
+ elements = { Linux : 0x00000001 }
}
map m2 {
@@ -24,15 +37,65 @@ EXPECTED="table inet t {
map m4 {
typeof iifname . ip protocol . th dport : verdict
+ elements = { eth0 . tcp . 22 : accept }
}
- chain c {
- ct mark set osf name map @m1
+ map m5 {
+ typeof ipsec in reqid . meta iifname : verdict
+ elements = { 23 . eth0 : accept }
+ }
+
+ chain c {$INPUT_OSF_CT
ether type vlan meta mark set vlan id map @m2
meta mark set ip saddr . ip daddr map @m3
+ iifname . ip protocol . th dport vmap @m4
+ iifname . ip protocol . th dport vmap { \"eth0\" . tcp . 22 : accept, \"eth1\" . udp . 67 : drop }
+ ipsec in reqid . meta iifname vmap @m5
}
}"
-set -e
-$NFT -f - <<< $EXPECTED
+EXPECTED="table inet t {
+ map m1 {
+ typeof osf name : ct mark
+ elements = { \"Linux\" : 0x00000001 }
+ }
+
+ map m2 {
+ typeof vlan id : meta mark
+ elements = { 1 : 0x00000001, 4095 : 0x00004095 }
+ }
+
+ map m3 {
+ typeof ip saddr . ip daddr : meta mark
+ elements = { 1.2.3.4 . 5.6.7.8 : 0x00000001,
+ 2.3.4.5 . 6.7.8.9 : 0x00000002 }
+ }
+
+ map m4 {
+ typeof iifname . ip protocol . th dport : verdict
+ elements = { \"eth0\" . tcp . 22 : accept }
+ }
+
+ map m5 {
+ typeof ipsec in reqid . iifname : verdict
+ elements = { 23 . \"eth0\" : accept }
+ }
+
+ chain c {$INPUT_OSF_CT
+ meta mark set vlan id map @m2
+ meta mark set ip saddr . ip daddr map @m3
+ iifname . ip protocol . th dport vmap @m4
+ iifname . ip protocol . th dport vmap { \"eth0\" . tcp . 22 : accept, \"eth1\" . udp . 67 : drop }
+ ipsec in reqid . iifname vmap @m5
+ }
+}"
+
+$NFT -f - <<< "$INPUT" || die $'nft command failed to process input:\n'">$INPUT<"
+
+$DIFF -u <($NFT list ruleset) - <<<"$EXPECTED" || die $'diff failed between ruleset and expected data.\nExpected:\n'">$EXPECTED<"
+
+if [ "$NFT_TEST_HAVE_osf" = n ] ; then
+ echo "Partial test due to NFT_TEST_HAVE_osf=n. Skip"
+ exit 77
+fi
diff --git a/tests/shell/testcases/maps/typeof_maps_add_delete b/tests/shell/testcases/maps/typeof_maps_add_delete
new file mode 100755
index 00000000..d2ac9f1c
--- /dev/null
+++ b/tests/shell/testcases/maps/typeof_maps_add_delete
@@ -0,0 +1,56 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_dynset_op_delete)
+
+CONDMATCH="ip saddr @dynmark"
+NCONDMATCH="ip saddr != @dynmark"
+
+# use reduced feature set
+if [ "$NFT_TEST_HAVE_map_lookup" = n ] ; then
+ CONDMATCH=""
+ NCONDMATCH=""
+fi
+
+EXPECTED="table ip dynset {
+ map dynmark {
+ typeof ip daddr : meta mark
+ counter
+ size 64
+ timeout 5m
+ }
+
+ chain test_ping {
+ $CONDMATCH counter comment \"should not increment\"
+ $NCONDMATCH add @dynmark { ip saddr : 0x1 } counter
+ $CONDMATCH counter comment \"should increment\"
+ $CONDMATCH delete @dynmark { ip saddr : 0x1 }
+ $CONDMATCH counter comment \"delete should be instant but might fail under memory pressure\"
+ }
+
+ chain input {
+ type filter hook input priority 0; policy accept;
+
+ add @dynmark { 10.2.3.4 timeout 1s : 0x2 } comment \"also check timeout-gc\"
+ meta l4proto icmp ip daddr 127.0.0.42 jump test_ping
+ }
+}"
+
+set -e
+$NFT -f - <<< $EXPECTED
+$NFT list ruleset
+
+ip link set lo up
+ping -c 1 127.0.0.42
+
+$NFT get element ip dynset dynmark { 10.2.3.4 }
+
+# wait so that 10.2.3.4 times out.
+sleep 2
+
+set +e
+$NFT get element ip dynset dynmark { 10.2.3.4 } && exit 1
+
+if [ "$NFT_TEST_HAVE_map_lookup" = n ] ; then
+ echo "Only tested a subset due to NFT_TEST_HAVE_map_lookup=n. Skipped."
+ exit 77
+fi
diff --git a/tests/shell/testcases/maps/typeof_maps_concat b/tests/shell/testcases/maps/typeof_maps_concat
new file mode 100755
index 00000000..07820b7c
--- /dev/null
+++ b/tests/shell/testcases/maps/typeof_maps_concat
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+set -e
+dumpfile=$(dirname $0)/dumps/$(basename $0).nft
+
+$NFT -f "$dumpfile"
diff --git a/tests/shell/testcases/maps/typeof_maps_concat_update_0 b/tests/shell/testcases/maps/typeof_maps_concat_update_0
new file mode 100755
index 00000000..2a52ea0e
--- /dev/null
+++ b/tests/shell/testcases/maps/typeof_maps_concat_update_0
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+# check update statement does print both concatentations (key and data).
+
+EXPECTED="table ip foo {
+ map pinned {
+ typeof ip saddr . ct original proto-dst : ip daddr . tcp dport
+ size 65535
+ flags dynamic,timeout
+ timeout 6m
+ }
+ chain pr {
+ update @pinned { ip saddr . ct original proto-dst timeout 1m30s : ip daddr . tcp dport }
+ meta l4proto tcp update @pinned { ip saddr . ct original proto-dst timeout 1m30s : ip daddr . tcp dport }
+ }
+}"
+
+set -e
+$NFT -f - <<< $EXPECTED
diff --git a/tests/shell/testcases/maps/typeof_raw_0 b/tests/shell/testcases/maps/typeof_raw_0
new file mode 100755
index 00000000..bcd2c6d8
--- /dev/null
+++ b/tests/shell/testcases/maps/typeof_raw_0
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+EXPECTED="table ip x {
+ map y {
+ typeof ip saddr . @ih,32,32: verdict
+ elements = { 1.1.1.1 . 0x14 : accept, 2.2.2.2 . 0x1e : drop }
+ }
+
+ chain y {
+ ip saddr . @nh,32,32 vmap @y
+ ip saddr . @nh,32,32 vmap { 4.4.4.4 . 0x34 : accept, 5.5.5.5 . 0x45 : drop}
+ }
+}"
+
+set -e
+$NFT -f - <<< $EXPECTED
+$NFT add element ip x y { 7.7.7.7 . 0x86 : accept, 7.7.7.8 . 0x97 : drop }
+$NFT delete element ip x y { 2.2.2.2 . 0x1e : drop }
diff --git a/tests/shell/testcases/maps/vmap_mark_bitwise_0 b/tests/shell/testcases/maps/vmap_mark_bitwise_0
new file mode 100755
index 00000000..2f305b27
--- /dev/null
+++ b/tests/shell/testcases/maps/vmap_mark_bitwise_0
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_bitshift)
+
+set -e
+
+RULESET="table ip x {
+ chain sctm_o0_0 {
+ }
+
+ chain sctm_o0_1 {
+ }
+
+ map sctm_o0 {
+ type mark : verdict
+ elements = {
+ 0x0 : jump sctm_o0_0,
+ 0x1 : jump sctm_o0_1,
+ }
+ }
+
+ counter c_o0_0 {}
+
+ map sctm_o1 {
+ type mark : counter
+ elements = {
+ 0x0 : \"c_o0_0\",
+ }
+ }
+
+ chain SET_ctmark_RPLYroute {
+ meta mark >> 8 & 0xf vmap @sctm_o0
+ }
+
+ chain SET_ctmark_RPLYroute {
+ counter name meta mark >> 8 & 0xf map @sctm_o1
+ }
+}"
+
+$NFT -f - <<< $RULESET
diff --git a/tests/shell/testcases/maps/vmap_timeout b/tests/shell/testcases/maps/vmap_timeout
new file mode 100755
index 00000000..0cd965f7
--- /dev/null
+++ b/tests/shell/testcases/maps/vmap_timeout
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+# NFT_TEST_SKIP(NFT_TEST_SKIP_slow)
+
+set -e
+
+dumpfile=$(dirname $0)/dumps/$(basename $0).nft
+$NFT -f $dumpfile
+
+port=23
+for i in $(seq 1 100) ; do
+ timeout=$((RANDOM%5))
+ timeout=$((timeout+1))
+ j=1
+
+ batched="{ $port timeout 3s : jump other_input "
+ batched_addr="{ 10.0.$((i%256)).$j . $port timeout ${timeout}s : jump other_input "
+ port=$((port + 1))
+ for j in $(seq 2 400); do
+ timeout=$((RANDOM%5))
+ timeout=$((timeout+1))
+
+ batched="$batched, $port timeout ${timeout}s : jump other_input "
+ batched_addr="$batched_addr, 10.0.$((i%256)).$((j%256)) . $port timeout ${timeout}s : jump other_input "
+ port=$((port + 1))
+ done
+
+ fail_addr="$batched_addr, 1.2.3.4 . 23 timeout 5m : jump other_input,
+ 1.2.3.4 . 23 timeout 3m : jump other_input }"
+ fail="$batched, 23 timeout 1m : jump other_input, 23 : jump other_input }"
+
+ batched="$batched }"
+ batched_addr="$batched_addr }"
+
+ if [ $i -gt 90 ]; then
+ # must fail, we create and $fail/$fail_addr contain one element twice.
+ $NFT create element inet filter portmap "$fail" && exit 111
+ $NFT create element inet filter portaddrmap "$fail_addr" && exit 112
+ fi
+
+ $NFT add element inet filter portmap "$batched"
+ $NFT add element inet filter portaddrmap "$batched_addr"
+done
+
+if [ "$NFT_TEST_HAVE_catchall_element" = n ] ; then
+ echo "Partial test due to NFT_TEST_HAVE_catchall_element=n."
+else
+ $NFT add element inet filter portaddrmap { "* timeout 2s : drop" }
+ $NFT add element inet filter portmap { "* timeout 3s : drop" }
+fi
+
+# wait for elements to time out
+sleep 5
diff --git a/tests/shell/testcases/maps/vmap_unary b/tests/shell/testcases/maps/vmap_unary
new file mode 100755
index 00000000..f4e1f012
--- /dev/null
+++ b/tests/shell/testcases/maps/vmap_unary
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_pipapo)
+
+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
diff --git a/tests/shell/testcases/netns/dumps/0001nft-f_0.json-nft b/tests/shell/testcases/netns/dumps/0001nft-f_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/netns/dumps/0001nft-f_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/netns/dumps/0001nft-f_0.nft b/tests/shell/testcases/netns/dumps/0001nft-f_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/netns/dumps/0001nft-f_0.nft
diff --git a/tests/shell/testcases/netns/dumps/0002loosecommands_0.json-nft b/tests/shell/testcases/netns/dumps/0002loosecommands_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/netns/dumps/0002loosecommands_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/netns/dumps/0002loosecommands_0.nft b/tests/shell/testcases/netns/dumps/0002loosecommands_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/netns/dumps/0002loosecommands_0.nft
diff --git a/tests/shell/testcases/netns/dumps/0003many_0.json-nft b/tests/shell/testcases/netns/dumps/0003many_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/netns/dumps/0003many_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/netns/dumps/0003many_0.nft b/tests/shell/testcases/netns/dumps/0003many_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/netns/dumps/0003many_0.nft
diff --git a/tests/shell/testcases/nft-f/0011manydefines_0 b/tests/shell/testcases/nft-f/0011manydefines_0
index 84664f46..aac06706 100755
--- a/tests/shell/testcases/nft-f/0011manydefines_0
+++ b/tests/shell/testcases/nft-f/0011manydefines_0
@@ -4,6 +4,15 @@
HOWMANY=20000
+if [ "$NFT_TEST_HAS_SOCKET_LIMITS" = y ] ; then
+ # The socket limit /proc/sys/net/core/wmem_max may be unsuitable for
+ # the test.
+ #
+ # Run only a subset of the test and mark as skipped at the end.
+ HOWMANY=2000
+fi
+
+
tmpfile=$(mktemp)
if [ ! -w $tmpfile ] ; then
echo "Failed to create tmp file" >&2
@@ -35,3 +44,10 @@ table t {
set -e
$NFT -f $tmpfile
+
+if [ "$HOWMANY" != 20000 ] ; then
+ echo "NFT_TEST_HAS_SOCKET_LIMITS indicates that the socket limit for"
+ echo "/proc/sys/net/core/wmem_max is too small for this test. Mark as SKIPPED"
+ echo "You may bump the limit and rerun with \`NFT_TEST_HAS_SOCKET_LIMITS=n\`."
+ exit 77
+fi
diff --git a/tests/shell/testcases/nft-f/0017ct_timeout_obj_0 b/tests/shell/testcases/nft-f/0017ct_timeout_obj_0
index 4f407793..cfb78950 100755
--- a/tests/shell/testcases/nft-f/0017ct_timeout_obj_0
+++ b/tests/shell/testcases/nft-f/0017ct_timeout_obj_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_cttimeout)
+
EXPECTED='table ip filter {
ct timeout cttime{
protocol tcp
diff --git a/tests/shell/testcases/nft-f/0018ct_expectation_obj_0 b/tests/shell/testcases/nft-f/0018ct_expectation_obj_0
index 4f9872f6..b288457c 100755
--- a/tests/shell/testcases/nft-f/0018ct_expectation_obj_0
+++ b/tests/shell/testcases/nft-f/0018ct_expectation_obj_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_ctexpect)
+
EXPECTED='table ip filter {
ct expectation ctexpect{
protocol tcp
diff --git a/tests/shell/testcases/nft-f/0025empty_dynset_0 b/tests/shell/testcases/nft-f/0025empty_dynset_0
index b66c802f..fbdb5793 100755
--- a/tests/shell/testcases/nft-f/0025empty_dynset_0
+++ b/tests/shell/testcases/nft-f/0025empty_dynset_0
@@ -1,5 +1,7 @@
#!/bin/bash
+set -e
+
RULESET="table ip foo {
set inflows {
type ipv4_addr . inet_service . ifname . ipv4_addr . inet_service
@@ -20,3 +22,9 @@ RULESET="table ip foo {
}"
$NFT -f - <<< "$RULESET"
+
+# inflows_ratelimit will be dumped without 'limit rate .. counter' on old kernels.
+if [ "$NFT_TEST_HAVE_set_with_two_expressions" = n ]; then
+ echo "Partial test due to NFT_TEST_HAVE_set_with_two_expressions=n."
+ exit 77
+fi
diff --git a/tests/shell/testcases/nft-f/0030variable_reuse_0 b/tests/shell/testcases/nft-f/0030variable_reuse_0
new file mode 100755
index 00000000..8afc54aa
--- /dev/null
+++ b/tests/shell/testcases/nft-f/0030variable_reuse_0
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+set -e
+
+RULESET="define test = { 1.1.1.1 }
+
+table ip x {
+ set y {
+ type ipv4_addr
+ elements = { 2.2.2.2, \$test }
+ }
+
+ set z {
+ type ipv4_addr
+ elements = { 3.3.3.3, \$test }
+ }
+}"
+
+$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/nft-f/0031vmap_string_0 b/tests/shell/testcases/nft-f/0031vmap_string_0
new file mode 100755
index 00000000..2af846a4
--- /dev/null
+++ b/tests/shell/testcases/nft-f/0031vmap_string_0
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# Tests parse of corrupted verdicts
+
+set -e
+
+RULESET="
+table ip foo {
+ map bar {
+ type ipv4_addr : verdict
+ elements = {
+ 192.168.0.1 : ber
+ }
+ }
+
+ chain ber {
+ }
+}"
+
+$NFT -f - <<< "$RULESET" && exit 1
+exit 0
diff --git a/tests/shell/testcases/nft-f/0032pknock_0 b/tests/shell/testcases/nft-f/0032pknock_0
new file mode 100755
index 00000000..94fc8407
--- /dev/null
+++ b/tests/shell/testcases/nft-f/0032pknock_0
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+set -e
+
+RULESET="define guarded_ports = {ssh}
+
+table inet portknock {
+ set clients_ipv4 {
+ type ipv4_addr
+ flags timeout
+ }
+
+ set candidates_ipv4 {
+ type ipv4_addr . inet_service
+ flags timeout
+ }
+
+ chain input {
+ type filter hook input priority -10; policy accept;
+
+ tcp dport 10001 add @candidates_ipv4 {ip saddr . 10002 timeout 1s}
+ tcp dport 10002 ip saddr . tcp dport @candidates_ipv4 add @candidates_ipv4 {ip saddr . 10003 timeout 1s}
+ tcp dport 10003 ip saddr . tcp dport @candidates_ipv4 add @candidates_ipv4 {ip saddr . 10004 timeout 1s}
+ tcp dport 10004 ip saddr . tcp dport @candidates_ipv4 add @candidates_ipv4 {ip saddr . 10005 timeout 1s}
+ tcp dport 10005 ip saddr . tcp dport @candidates_ipv4 add @clients_ipv4 {ip saddr timeout 600s} log prefix \"Successful portknock: \"
+
+ tcp dport \$guarded_ports ip saddr @clients_ipv4 counter accept
+ tcp dport \$guarded_ports ct state established,related counter accept
+
+ tcp dport \$guarded_ports reject with tcp reset
+ }
+}"
+
+$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/nft-f/dumps/0001define_slash_0.json-nft b/tests/shell/testcases/nft-f/dumps/0001define_slash_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0001define_slash_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0001define_slash_0.nft b/tests/shell/testcases/nft-f/dumps/0001define_slash_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0001define_slash_0.nft
diff --git a/tests/shell/testcases/nft-f/dumps/0002rollback_rule_0.json-nft b/tests/shell/testcases/nft-f/dumps/0002rollback_rule_0.json-nft
new file mode 100644
index 00000000..99b0b28d
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0002rollback_rule_0.json-nft
@@ -0,0 +1,134 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "other",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "t",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "elem": [
+ "1.1.1.1"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": "new"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": {
+ "set": [
+ 22222,
+ 33333
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "@t"
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "other"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0003rollback_jump_0.json-nft b/tests/shell/testcases/nft-f/dumps/0003rollback_jump_0.json-nft
new file mode 100644
index 00000000..99b0b28d
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0003rollback_jump_0.json-nft
@@ -0,0 +1,134 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "other",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "t",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "elem": [
+ "1.1.1.1"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": "new"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": {
+ "set": [
+ 22222,
+ 33333
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "@t"
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "other"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0004rollback_set_0.json-nft b/tests/shell/testcases/nft-f/dumps/0004rollback_set_0.json-nft
new file mode 100644
index 00000000..99b0b28d
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0004rollback_set_0.json-nft
@@ -0,0 +1,134 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "other",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "t",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "elem": [
+ "1.1.1.1"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": "new"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": {
+ "set": [
+ 22222,
+ 33333
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "@t"
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "other"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0005rollback_map_0.json-nft b/tests/shell/testcases/nft-f/dumps/0005rollback_map_0.json-nft
new file mode 100644
index 00000000..99b0b28d
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0005rollback_map_0.json-nft
@@ -0,0 +1,134 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "other",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "t",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "elem": [
+ "1.1.1.1"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": "new"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": {
+ "set": [
+ 22222,
+ 33333
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "@t"
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "other"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0006action_object_0.json-nft b/tests/shell/testcases/nft-f/dumps/0006action_object_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0006action_object_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0006action_object_0.nft b/tests/shell/testcases/nft-f/dumps/0006action_object_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0006action_object_0.nft
diff --git a/tests/shell/testcases/nft-f/dumps/0007action_object_set_segfault_1.json-nft b/tests/shell/testcases/nft-f/dumps/0007action_object_set_segfault_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0007action_object_set_segfault_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0007action_object_set_segfault_1.nft b/tests/shell/testcases/nft-f/dumps/0007action_object_set_segfault_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0007action_object_set_segfault_1.nft
diff --git a/tests/shell/testcases/nft-f/dumps/0008split_tables_0.json-nft b/tests/shell/testcases/nft-f/dumps/0008split_tables_0.json-nft
new file mode 100644
index 00000000..05ebed5a
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0008split_tables_0.json-nft
@@ -0,0 +1,67 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "ssh",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 1,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "ssh",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 22
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0009variable_0.json-nft b/tests/shell/testcases/nft-f/dumps/0009variable_0.json-nft
new file mode 100644
index 00000000..41236dbe
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0009variable_0.json-nft
@@ -0,0 +1,44 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "forward",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "concat-set-variable",
+ "table": "forward",
+ "type": [
+ "ipv4_addr",
+ "inet_service"
+ ],
+ "handle": 0,
+ "elem": [
+ {
+ "concat": [
+ "10.10.10.10",
+ 25
+ ]
+ },
+ {
+ "concat": [
+ "10.10.10.10",
+ 143
+ ]
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0010variable_0.json-nft b/tests/shell/testcases/nft-f/dumps/0010variable_0.json-nft
new file mode 100644
index 00000000..4b4ec4fb
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0010variable_0.json-nft
@@ -0,0 +1,30 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "whitelist_v4",
+ "table": "filter",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "elem": [
+ "1.1.1.1"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0011manydefines_0.nodump b/tests/shell/testcases/nft-f/dumps/0011manydefines_0.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0011manydefines_0.nodump
diff --git a/tests/shell/testcases/nft-f/dumps/0012different_defines_0.json-nft b/tests/shell/testcases/nft-f/dumps/0012different_defines_0.json-nft
new file mode 100644
index 00000000..1b2e3420
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0012different_defines_0.json-nft
@@ -0,0 +1,778 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "whatever"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oifname"
+ }
+ },
+ "right": "whatever"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iif"
+ }
+ },
+ "right": "lo"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oif"
+ }
+ },
+ "right": "lo"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": {
+ "set": [
+ "whatever"
+ ]
+ }
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iif"
+ }
+ },
+ "right": {
+ "set": [
+ "lo"
+ ]
+ }
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "right": 123
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": [
+ "established",
+ "related",
+ "new"
+ ]
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "!=",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": {
+ "|": [
+ "established",
+ "related",
+ "new"
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "10.0.0.0"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "right": "10.0.0.2"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "10.0.0.0"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip6",
+ "field": "daddr"
+ }
+ },
+ "right": "fe0::1"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip6",
+ "field": "saddr"
+ }
+ },
+ "right": "fe0::2"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ "10.0.0.0",
+ {
+ "drop": null
+ }
+ ],
+ [
+ "10.0.0.2",
+ {
+ "accept": null
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "payload": {
+ "protocol": "ip6",
+ "field": "daddr"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ "fe0::1",
+ {
+ "drop": null
+ }
+ ],
+ [
+ "fe0::2",
+ {
+ "accept": null
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip6",
+ "field": "saddr"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "ip6",
+ "field": "nexthdr"
+ }
+ }
+ ]
+ },
+ "right": {
+ "set": [
+ {
+ "concat": [
+ "fe0::2",
+ "tcp"
+ ]
+ },
+ {
+ "concat": [
+ "fe0::1",
+ "udp"
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ {
+ "meta": {
+ "key": "iif"
+ }
+ }
+ ]
+ },
+ "data": {
+ "set": [
+ [
+ {
+ "concat": [
+ "10.0.0.0",
+ "lo"
+ ]
+ },
+ {
+ "accept": null
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": {
+ "range": [
+ 100,
+ 222
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ {
+ "range": [
+ 100,
+ 222
+ ]
+ },
+ {
+ "accept": null
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "sport"
+ }
+ },
+ "right": 1
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 1
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oifname"
+ }
+ },
+ "right": "foobar"
+ }
+ },
+ {
+ "queue": {
+ "num": 0,
+ "flags": "bypass"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "sport"
+ }
+ },
+ "right": 1
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 1
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oifname"
+ }
+ },
+ "right": "foobar"
+ }
+ },
+ {
+ "queue": {
+ "num": {
+ "range": [
+ 1,
+ 42
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "sport"
+ }
+ },
+ "right": 1
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 1
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oifname"
+ }
+ },
+ "right": "foobar"
+ }
+ },
+ {
+ "queue": {
+ "num": {
+ "range": [
+ 1,
+ 42
+ ]
+ },
+ "flags": [
+ "bypass",
+ "fanout"
+ ]
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "sport"
+ }
+ },
+ "right": 1
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 1
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oifname"
+ }
+ },
+ "right": "foobar"
+ }
+ },
+ {
+ "queue": {
+ "num": {
+ "symhash": {
+ "mod": 2
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "sport"
+ }
+ },
+ "right": 1
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 1
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oifname"
+ }
+ },
+ "right": "foobar"
+ }
+ },
+ {
+ "queue": {
+ "num": {
+ "jhash": {
+ "mod": 4,
+ "expr": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "sport"
+ }
+ }
+ ]
+ }
+ }
+ },
+ "flags": "bypass"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0013defines_1.json-nft b/tests/shell/testcases/nft-f/dumps/0013defines_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0013defines_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0013defines_1.nft b/tests/shell/testcases/nft-f/dumps/0013defines_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0013defines_1.nft
diff --git a/tests/shell/testcases/nft-f/dumps/0014defines_1.json-nft b/tests/shell/testcases/nft-f/dumps/0014defines_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0014defines_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0014defines_1.nft b/tests/shell/testcases/nft-f/dumps/0014defines_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0014defines_1.nft
diff --git a/tests/shell/testcases/nft-f/dumps/0015defines_1.json-nft b/tests/shell/testcases/nft-f/dumps/0015defines_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0015defines_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0015defines_1.nft b/tests/shell/testcases/nft-f/dumps/0015defines_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0015defines_1.nft
diff --git a/tests/shell/testcases/nft-f/dumps/0016redefines_1.json-nft b/tests/shell/testcases/nft-f/dumps/0016redefines_1.json-nft
new file mode 100644
index 00000000..40cdb000
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0016redefines_1.json-nft
@@ -0,0 +1,80 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": {
+ "set": [
+ "1.1.1.1",
+ "2.2.2.2"
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": {
+ "set": [
+ "3.3.3.3",
+ "4.4.4.4"
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0016redefines_1.nft b/tests/shell/testcases/nft-f/dumps/0016redefines_1.nft
new file mode 100644
index 00000000..65b7f491
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0016redefines_1.nft
@@ -0,0 +1,6 @@
+table ip x {
+ chain y {
+ ip saddr { 1.1.1.1, 2.2.2.2 }
+ ip saddr { 3.3.3.3, 4.4.4.4 }
+ }
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0017ct_timeout_obj_0.json-nft b/tests/shell/testcases/nft-f/dumps/0017ct_timeout_obj_0.json-nft
new file mode 100644
index 00000000..b56240ea
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0017ct_timeout_obj_0.json-nft
@@ -0,0 +1,53 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "ct timeout": {
+ "family": "ip",
+ "name": "cttime",
+ "table": "filter",
+ "handle": 0,
+ "protocol": "tcp",
+ "l3proto": "ip",
+ "policy": {
+ "established": 123,
+ "close": 12
+ }
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "ct timeout": "cttime"
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0017ct_timeout_obj_0.nft b/tests/shell/testcases/nft-f/dumps/0017ct_timeout_obj_0.nft
index 7cff1ed5..c5d9649e 100644
--- a/tests/shell/testcases/nft-f/dumps/0017ct_timeout_obj_0.nft
+++ b/tests/shell/testcases/nft-f/dumps/0017ct_timeout_obj_0.nft
@@ -2,7 +2,7 @@ table ip filter {
ct timeout cttime {
protocol tcp
l3proto ip
- policy = { established : 123, close : 12 }
+ policy = { established : 2m3s, close : 12s }
}
chain c {
diff --git a/tests/shell/testcases/nft-f/dumps/0018ct_expectation_obj_0.json-nft b/tests/shell/testcases/nft-f/dumps/0018ct_expectation_obj_0.json-nft
new file mode 100644
index 00000000..21c97970
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0018ct_expectation_obj_0.json-nft
@@ -0,0 +1,52 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "ct expectation": {
+ "family": "ip",
+ "name": "ctexpect",
+ "table": "filter",
+ "handle": 0,
+ "protocol": "tcp",
+ "dport": 9876,
+ "timeout": 60000,
+ "size": 12,
+ "l3proto": "ip"
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "ct expectation": "ctexpect"
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0018ct_expectation_obj_0.nft b/tests/shell/testcases/nft-f/dumps/0018ct_expectation_obj_0.nft
new file mode 100644
index 00000000..396185eb
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0018ct_expectation_obj_0.nft
@@ -0,0 +1,13 @@
+table ip filter {
+ ct expectation ctexpect {
+ protocol tcp
+ dport 9876
+ timeout 1m
+ size 12
+ l3proto ip
+ }
+
+ chain c {
+ ct expectation set "ctexpect"
+ }
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0018jump_variable_0.json-nft b/tests/shell/testcases/nft-f/dumps/0018jump_variable_0.json-nft
new file mode 100644
index 00000000..f62b48a3
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0018jump_variable_0.json-nft
@@ -0,0 +1,49 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "foo",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "foo",
+ "name": "bar",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "foo",
+ "name": "ber",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "ber"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0019jump_variable_1.json-nft b/tests/shell/testcases/nft-f/dumps/0019jump_variable_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0019jump_variable_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0019jump_variable_1.nft b/tests/shell/testcases/nft-f/dumps/0019jump_variable_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0019jump_variable_1.nft
diff --git a/tests/shell/testcases/nft-f/dumps/0020jump_variable_1.json-nft b/tests/shell/testcases/nft-f/dumps/0020jump_variable_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0020jump_variable_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0020jump_variable_1.nft b/tests/shell/testcases/nft-f/dumps/0020jump_variable_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0020jump_variable_1.nft
diff --git a/tests/shell/testcases/nft-f/dumps/0021list_ruleset_0.json-nft b/tests/shell/testcases/nft-f/dumps/0021list_ruleset_0.json-nft
new file mode 100644
index 00000000..f41b1b04
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0021list_ruleset_0.json-nft
@@ -0,0 +1,30 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "prerouting",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -50,
+ "policy": "accept"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0022variables_0.json-nft b/tests/shell/testcases/nft-f/dumps/0022variables_0.json-nft
new file mode 100644
index 00000000..09a4c1e3
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0022variables_0.json-nft
@@ -0,0 +1,115 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "z",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 65535,
+ "flags": [
+ "timeout",
+ "dynamic"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "z",
+ "handle": 0,
+ "expr": [
+ {
+ "set": {
+ "op": "add",
+ "elem": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "set": "@y"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "z",
+ "handle": 0,
+ "expr": [
+ {
+ "set": {
+ "op": "update",
+ "elem": {
+ "elem": {
+ "val": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "timeout": 30
+ }
+ },
+ "set": "@y"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "z",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "@y"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0023check_1.json-nft b/tests/shell/testcases/nft-f/dumps/0023check_1.json-nft
new file mode 100644
index 00000000..ddb2a057
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0023check_1.json-nft
@@ -0,0 +1,30 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "foo",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "foo",
+ "name": "bar",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 0,
+ "policy": "accept"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0023check_1.nft b/tests/shell/testcases/nft-f/dumps/0023check_1.nft
new file mode 100644
index 00000000..04b9e70f
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0023check_1.nft
@@ -0,0 +1,5 @@
+table ip foo {
+ chain bar {
+ type filter hook prerouting priority filter; policy accept;
+ }
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0024priority_0.json-nft b/tests/shell/testcases/nft-f/dumps/0024priority_0.json-nft
new file mode 100644
index 00000000..cdc4b9d9
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0024priority_0.json-nft
@@ -0,0 +1,95 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "statelessnat",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "statelessnat",
+ "name": "prerouting",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -100,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "statelessnat",
+ "name": "postrouting",
+ "handle": 0,
+ "type": "filter",
+ "hook": "postrouting",
+ "prio": 100,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "statelessnat",
+ "chain": "prerouting",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "value": {
+ "map": {
+ "key": {
+ "numgen": {
+ "mode": "inc",
+ "mod": 16,
+ "offset": 0
+ }
+ },
+ "data": {
+ "set": [
+ [
+ {
+ "range": [
+ 0,
+ 7
+ ]
+ },
+ "10.0.1.1"
+ ],
+ [
+ {
+ "range": [
+ 8,
+ 15
+ ]
+ },
+ "10.0.1.2"
+ ]
+ ]
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0025empty_dynset_0.json-nft b/tests/shell/testcases/nft-f/dumps/0025empty_dynset_0.json-nft
new file mode 100644
index 00000000..0cde23b0
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0025empty_dynset_0.json-nft
@@ -0,0 +1,111 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "foo",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "inflows",
+ "table": "foo",
+ "type": [
+ "ipv4_addr",
+ "inet_service",
+ "ifname",
+ "ipv4_addr",
+ "inet_service"
+ ],
+ "handle": 0,
+ "flags": [
+ "dynamic"
+ ],
+ "elem": [
+ {
+ "elem": {
+ "val": {
+ "concat": [
+ "10.1.0.3",
+ 39466,
+ "veth1",
+ "10.3.0.99",
+ 5201
+ ]
+ },
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "inflows6",
+ "table": "foo",
+ "type": [
+ "ipv6_addr",
+ "inet_service",
+ "ifname",
+ "ipv6_addr",
+ "inet_service"
+ ],
+ "handle": 0,
+ "flags": [
+ "dynamic"
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "inflows_ratelimit",
+ "table": "foo",
+ "type": [
+ "ipv4_addr",
+ "inet_service",
+ "ifname",
+ "ipv4_addr",
+ "inet_service"
+ ],
+ "handle": 0,
+ "flags": [
+ "dynamic"
+ ],
+ "elem": [
+ {
+ "elem": {
+ "val": {
+ "concat": [
+ "10.1.0.3",
+ 39466,
+ "veth1",
+ "10.3.0.99",
+ 5201
+ ]
+ },
+ "limit": {
+ "rate": 1,
+ "burst": 5,
+ "per": "second"
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0025empty_dynset_0.nft b/tests/shell/testcases/nft-f/dumps/0025empty_dynset_0.nft
index 2bb35592..33b9e4ff 100644
--- a/tests/shell/testcases/nft-f/dumps/0025empty_dynset_0.nft
+++ b/tests/shell/testcases/nft-f/dumps/0025empty_dynset_0.nft
@@ -13,6 +13,6 @@ table ip foo {
set inflows_ratelimit {
type ipv4_addr . inet_service . ifname . ipv4_addr . inet_service
flags dynamic
- elements = { 10.1.0.3 . 39466 . "veth1" . 10.3.0.99 . 5201 limit rate 1/second counter packets 0 bytes 0 }
+ elements = { 10.1.0.3 . 39466 . "veth1" . 10.3.0.99 . 5201 limit rate 1/second burst 5 packets counter packets 0 bytes 0 }
}
}
diff --git a/tests/shell/testcases/nft-f/dumps/0026listing_0.json-nft b/tests/shell/testcases/nft-f/dumps/0026listing_0.json-nft
new file mode 100644
index 00000000..8acdcdf4
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0026listing_0.json-nft
@@ -0,0 +1,56 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "A",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "A",
+ "name": "B",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "A",
+ "chain": "B",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": {
+ "set": [
+ 1,
+ 2
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0026listing_0.nft b/tests/shell/testcases/nft-f/dumps/0026listing_0.nft
new file mode 100644
index 00000000..fd0bb686
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0026listing_0.nft
@@ -0,0 +1,5 @@
+table ip A {
+ chain B {
+ tcp dport { 1, 2 } accept
+ }
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0027split_chains_0.json-nft b/tests/shell/testcases/nft-f/dumps/0027split_chains_0.json-nft
new file mode 100644
index 00000000..bda8bfc9
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0027split_chains_0.json-nft
@@ -0,0 +1,53 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "x"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0028variable_cmdline_0.json-nft b/tests/shell/testcases/nft-f/dumps/0028variable_cmdline_0.json-nft
new file mode 100644
index 00000000..69d826df
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0028variable_cmdline_0.json-nft
@@ -0,0 +1,34 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "whitelist_v4",
+ "table": "filter",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "elem": [
+ "1.1.1.1",
+ "2.2.2.2",
+ "3.3.3.3",
+ "4.4.4.4",
+ "5.5.5.5"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0029split_file_0.json-nft b/tests/shell/testcases/nft-f/dumps/0029split_file_0.json-nft
new file mode 100644
index 00000000..ab680af8
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0029split_file_0.json-nft
@@ -0,0 +1,61 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "prerouting",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "whitelist_v4",
+ "table": "filter",
+ "type": "ipv4_addr",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "prerouting",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "right": "@whitelist_v4"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0029split_file_0.nft b/tests/shell/testcases/nft-f/dumps/0029split_file_0.nft
new file mode 100644
index 00000000..32d5c0e9
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0029split_file_0.nft
@@ -0,0 +1,10 @@
+table inet filter {
+ set whitelist_v4 {
+ type ipv4_addr
+ }
+
+ chain prerouting {
+ type filter hook prerouting priority filter; policy accept;
+ ip daddr @whitelist_v4
+ }
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0030variable_reuse_0.json-nft b/tests/shell/testcases/nft-f/dumps/0030variable_reuse_0.json-nft
new file mode 100644
index 00000000..e0704b7d
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0030variable_reuse_0.json-nft
@@ -0,0 +1,44 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "elem": [
+ "1.1.1.1",
+ "2.2.2.2"
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "z",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "elem": [
+ "1.1.1.1",
+ "3.3.3.3"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0030variable_reuse_0.nft b/tests/shell/testcases/nft-f/dumps/0030variable_reuse_0.nft
new file mode 100644
index 00000000..635901f4
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0030variable_reuse_0.nft
@@ -0,0 +1,11 @@
+table ip x {
+ set y {
+ type ipv4_addr
+ elements = { 1.1.1.1, 2.2.2.2 }
+ }
+
+ set z {
+ type ipv4_addr
+ elements = { 1.1.1.1, 3.3.3.3 }
+ }
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0031vmap_string_0.json-nft b/tests/shell/testcases/nft-f/dumps/0031vmap_string_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0031vmap_string_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0031vmap_string_0.nft b/tests/shell/testcases/nft-f/dumps/0031vmap_string_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0031vmap_string_0.nft
diff --git a/tests/shell/testcases/nft-f/dumps/0032pknock_0.json-nft b/tests/shell/testcases/nft-f/dumps/0032pknock_0.json-nft
new file mode 100644
index 00000000..4c7d2bbe
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0032pknock_0.json-nft
@@ -0,0 +1,484 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "portknock",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "portknock",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": -10,
+ "policy": "accept"
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "clients_ipv4",
+ "table": "portknock",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 65535,
+ "flags": [
+ "timeout",
+ "dynamic"
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "candidates_ipv4",
+ "table": "portknock",
+ "type": [
+ "ipv4_addr",
+ "inet_service"
+ ],
+ "handle": 0,
+ "size": 65535,
+ "flags": [
+ "timeout",
+ "dynamic"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "portknock",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 10001
+ }
+ },
+ {
+ "set": {
+ "op": "add",
+ "elem": {
+ "elem": {
+ "val": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ 10002
+ ]
+ },
+ "timeout": 1
+ }
+ },
+ "set": "@candidates_ipv4"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "portknock",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 10002
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ }
+ ]
+ },
+ "right": "@candidates_ipv4"
+ }
+ },
+ {
+ "set": {
+ "op": "add",
+ "elem": {
+ "elem": {
+ "val": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ 10003
+ ]
+ },
+ "timeout": 1
+ }
+ },
+ "set": "@candidates_ipv4"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "portknock",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 10003
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ }
+ ]
+ },
+ "right": "@candidates_ipv4"
+ }
+ },
+ {
+ "set": {
+ "op": "add",
+ "elem": {
+ "elem": {
+ "val": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ 10004
+ ]
+ },
+ "timeout": 1
+ }
+ },
+ "set": "@candidates_ipv4"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "portknock",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 10004
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ }
+ ]
+ },
+ "right": "@candidates_ipv4"
+ }
+ },
+ {
+ "set": {
+ "op": "add",
+ "elem": {
+ "elem": {
+ "val": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ 10005
+ ]
+ },
+ "timeout": 1
+ }
+ },
+ "set": "@candidates_ipv4"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "portknock",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 10005
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ }
+ ]
+ },
+ "right": "@candidates_ipv4"
+ }
+ },
+ {
+ "set": {
+ "op": "add",
+ "elem": {
+ "elem": {
+ "val": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "timeout": 600
+ }
+ },
+ "set": "@clients_ipv4"
+ }
+ },
+ {
+ "log": {
+ "prefix": "Successful portknock: "
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "portknock",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 22
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "@clients_ipv4"
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "portknock",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 22
+ }
+ },
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": [
+ "established",
+ "related"
+ ]
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "portknock",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 22
+ }
+ },
+ {
+ "reject": {
+ "type": "tcp reset"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-f/dumps/0032pknock_0.nft b/tests/shell/testcases/nft-f/dumps/0032pknock_0.nft
new file mode 100644
index 00000000..f29dfb28
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/0032pknock_0.nft
@@ -0,0 +1,25 @@
+table inet portknock {
+ set clients_ipv4 {
+ type ipv4_addr
+ size 65535
+ flags dynamic,timeout
+ }
+
+ set candidates_ipv4 {
+ type ipv4_addr . inet_service
+ size 65535
+ flags dynamic,timeout
+ }
+
+ chain input {
+ type filter hook input priority filter - 10; policy accept;
+ tcp dport 10001 add @candidates_ipv4 { ip saddr . 10002 timeout 1s }
+ tcp dport 10002 ip saddr . tcp dport @candidates_ipv4 add @candidates_ipv4 { ip saddr . 10003 timeout 1s }
+ tcp dport 10003 ip saddr . tcp dport @candidates_ipv4 add @candidates_ipv4 { ip saddr . 10004 timeout 1s }
+ tcp dport 10004 ip saddr . tcp dport @candidates_ipv4 add @candidates_ipv4 { ip saddr . 10005 timeout 1s }
+ tcp dport 10005 ip saddr . tcp dport @candidates_ipv4 add @clients_ipv4 { ip saddr timeout 10m } log prefix "Successful portknock: "
+ tcp dport 22 ip saddr @clients_ipv4 counter packets 0 bytes 0 accept
+ tcp dport 22 ct state established,related counter packets 0 bytes 0 accept
+ tcp dport 22 reject with tcp reset
+ }
+}
diff --git a/tests/shell/testcases/nft-f/dumps/sample-ruleset.nft b/tests/shell/testcases/nft-f/dumps/sample-ruleset.nft
new file mode 100644
index 00000000..1a9f4e7a
--- /dev/null
+++ b/tests/shell/testcases/nft-f/dumps/sample-ruleset.nft
@@ -0,0 +1,239 @@
+table inet filter {
+ map if_input {
+ type ifname : verdict
+ elements = { "eth0" : jump public_input,
+ "eth1" : jump home_input,
+ "eth2.10" : jump home_input,
+ "eth2.20" : jump home_input }
+ }
+
+ map if_forward {
+ type ifname : verdict
+ elements = { "eth0" : jump public_forward,
+ "eth1" : jump trusted_forward,
+ "eth2.10" : jump voip_forward,
+ "eth2.20" : jump guest_forward }
+ }
+
+ map if_output {
+ type ifname : verdict
+ elements = { "eth0" : jump public_output,
+ "eth1" : jump home_output,
+ "eth2.10" : jump home_output,
+ "eth2.20" : jump home_output }
+ }
+
+ set ipv4_blacklist {
+ type ipv4_addr
+ flags interval
+ auto-merge
+ }
+
+ set ipv6_blacklist {
+ type ipv6_addr
+ flags interval
+ auto-merge
+ }
+
+ set limit_src_ip {
+ type ipv4_addr
+ size 1024
+ flags dynamic,timeout
+ }
+
+ set limit_src_ip6 {
+ type ipv6_addr
+ size 1024
+ flags dynamic,timeout
+ }
+
+ chain PREROUTING_RAW {
+ type filter hook prerouting priority raw; policy accept;
+ meta l4proto != { icmp, tcp, udp, ipv6-icmp } counter packets 0 bytes 0 drop
+ tcp flags syn jump {
+ tcp option maxseg size 1-500 counter packets 0 bytes 0 drop
+ tcp sport 0 counter packets 0 bytes 0 drop
+ }
+ rt type 0 counter packets 0 bytes 0 drop
+ }
+
+ chain PREROUTING_MANGLE {
+ type filter hook prerouting priority mangle; policy accept;
+ ct state vmap { invalid : jump ct_invalid_pre, related : jump rpfilter, new : jump ct_new_pre, untracked : jump ct_untracked_pre }
+ }
+
+ chain ct_invalid_pre {
+ counter packets 0 bytes 0 drop
+ }
+
+ chain ct_untracked_pre {
+ icmpv6 type { mld-listener-query, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, mld2-listener-report } return
+ counter packets 0 bytes 0 drop
+ }
+
+ chain ct_new_pre {
+ jump rpfilter
+ 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 }
+ }
+
+ chain rpfilter {
+ ip saddr 0.0.0.0 ip daddr 255.255.255.255 udp sport 68 udp dport 67 return
+ ip6 saddr :: ip6 daddr . icmpv6 type { ff02::1:ff00:0/104 . nd-neighbor-solicit, ff02::16 . mld2-listener-report } return
+ fib saddr . iif oif 0 counter packets 0 bytes 0 drop
+ }
+
+ chain blacklist_input_ipv4 {
+ ip saddr { 0.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.0.0.0/24, 192.0.2.0/24, 192.168.0.0/16, 198.18.0.0/15, 198.51.100.0/24, 203.0.113.0/24, 224.0.0.0/3 } counter packets 0 bytes 0 drop
+ ip saddr @ipv4_blacklist counter packets 0 bytes 0 drop
+ }
+
+ chain blacklist_input_ipv6 {
+ icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } ip6 saddr fe80::/64 return
+ udp sport 547 ip6 saddr fe80::/64 return
+ ip6 saddr { ::/3, 2001::/32, 2001:2::/48, 2001:3::/32, 2001:10::-2001:2f:ffff:ffff:ffff:ffff:ffff:ffff, 2001:db8::/32, 2002::/16, 3000::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff } counter packets 0 bytes 0 drop
+ ip6 saddr @ipv6_blacklist counter packets 0 bytes 0 drop
+ }
+
+ chain INPUT {
+ type filter hook input priority filter; policy drop;
+ iif "lo" accept
+ ct state established,related accept
+ iifname vmap @if_input
+ log prefix "NFT REJECT IN " flags ip options flags ether limit rate 5/second burst 10 packets reject
+ }
+
+ chain public_input {
+ icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } ip6 saddr fe80::/64 ip6 hoplimit 255 accept
+ udp sport 547 udp dport 546 ip6 saddr fe80::/64 accept
+ fib daddr type { broadcast, anycast, multicast } counter packets 0 bytes 0 drop
+ counter packets 0 bytes 0 drop
+ }
+
+ chain home_input {
+ icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } ip6 hoplimit 255 accept
+ icmpv6 type { mld-listener-query, mld2-listener-report } ip6 hoplimit 1 accept
+ udp sport 68 udp dport 67 accept
+ udp sport 546 udp dport 547 iifname { "eth1", "eth2.10", "eth2.20" } accept
+ fib daddr type { broadcast, anycast, multicast } counter packets 0 bytes 0 drop
+ icmp type echo-request accept
+ icmpv6 type echo-request accept
+ tcp dport 22 iifname "eth1" accept
+ meta l4proto { tcp, udp } th dport 53 jump {
+ ip6 saddr != { fd00::/8, fe80::/64 } counter packets 0 bytes 0 reject with icmpv6 port-unreachable
+ accept
+ }
+ udp dport 123 accept
+ tcp dport 8443 accept
+ }
+
+ chain FORWARD_MANGLE {
+ 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 | rst) == syn tcp option maxseg size set rt mtu
+ }
+ }
+
+ chain blacklist_output_ipv4 {
+ ip daddr { 0.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.0.0.0/24, 192.0.2.0/24, 192.168.0.0/16, 198.18.0.0/15, 198.51.100.0/24, 203.0.113.0/24, 224.0.0.0/3 } goto log_blacklist
+ ip daddr @ipv4_blacklist goto log_blacklist
+ }
+
+ chain blacklist_output_ipv6 {
+ icmpv6 type . ip6 daddr { nd-router-solicit . ff02::2, nd-neighbor-solicit . ff02::1:ff00:0/104, nd-neighbor-advert . fe80::/64, nd-neighbor-advert . ff02::1, nd-neighbor-advert . ff02::1:ff00:0/104, mld2-listener-report . ff02::16 } return
+ udp dport 547 ip6 daddr ff02::1:2 return
+ ip6 daddr { ::/3, 2001::/32, 2001:2::/48, 2001:3::/32, 2001:10::-2001:2f:ffff:ffff:ffff:ffff:ffff:ffff, 2001:db8::/32, 2002::/16, 3000::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff } goto log_blacklist
+ ip6 daddr @ipv6_blacklist goto log_blacklist
+ }
+
+ chain log_blacklist {
+ log prefix "NFT BLACKLIST " flags ip options flags ether limit rate 5/minute burst 10 packets drop
+ counter packets 0 bytes 0 drop
+ }
+
+ chain FORWARD {
+ type filter hook forward priority filter; policy drop;
+ ct state established,related accept
+ fib daddr type { broadcast, anycast, multicast } counter packets 0 bytes 0 drop
+ iifname vmap @if_forward
+ log prefix "NFT REJECT FWD " flags ip options flags ether limit rate 5/second burst 10 packets reject
+ }
+
+ chain public_forward {
+ udp dport { 5060, 7078-7097 } oifname "eth2.10" jump {
+ ip6 saddr { 2001:db8::1-2001:db8::2 } accept
+ meta nfproto ipv6 log prefix "NFT DROP SIP " flags ip options flags ether limit rate 5/second burst 10 packets drop
+ }
+ counter packets 0 bytes 0 drop
+ }
+
+ chain trusted_forward {
+ oifname "eth0" accept
+ icmp type echo-request accept
+ icmpv6 type echo-request accept
+ ip daddr { 192.168.3.30, 192.168.4.40 } tcp dport vmap { 22 : accept, 80 : drop, 443 : accept }
+ ip daddr 192.168.2.20 jump {
+ tcp dport { 80, 443, 515, 631, 9100 } accept
+ udp dport 161 accept
+ }
+ }
+
+ chain voip_forward {
+ icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request } oifname "eth0" accept
+ ip6 daddr { 2001:db8::1-2001:db8::2 } jump {
+ udp dport { 3478, 5060 } accept
+ udp sport 7078-7097 accept
+ tcp dport 5061 accept
+ }
+ tcp dport 587 ip daddr 10.0.0.1 accept
+ tcp dport 80 oifname "eth0" counter packets 0 bytes 0 reject
+ }
+
+ chain guest_forward {
+ oifname "eth0" accept
+ }
+
+ chain OUTPUT {
+ type filter hook output priority filter; policy drop;
+ oif "lo" accept
+ ct state vmap { invalid : jump ct_invalid_out, established : accept, related : accept, untracked : jump ct_untracked_out }
+ oifname vmap @if_output
+ log prefix "NFT REJECT OUT " flags ip options flags ether limit rate 5/second burst 10 packets reject
+ }
+
+ chain ct_invalid_out {
+ counter packets 0 bytes 0 drop
+ }
+
+ chain ct_untracked_out {
+ icmpv6 type { mld-listener-query, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, mld2-listener-report } return
+ counter packets 0 bytes 0 drop
+ }
+
+ chain public_output {
+ ct state new meta nfproto vmap { ipv4 : jump blacklist_output_ipv4, ipv6 : jump blacklist_output_ipv6 }
+ icmp type { destination-unreachable, echo-request, time-exceeded, parameter-problem } accept
+ icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request } accept
+ icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } ip6 hoplimit 255 accept
+ icmpv6 type { mld-listener-query, mld2-listener-report } ip6 hoplimit 1 accept
+ udp dport 547 ip6 saddr fe80::/64 ip6 daddr ff02::1:2 accept
+ udp dport { 53, 123 } accept
+ tcp dport { 443, 587, 853 } accept
+ }
+
+ chain home_output {
+ icmp type { destination-unreachable, echo-request, time-exceeded, parameter-problem } accept
+ icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request } accept
+ icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } ip6 hoplimit 255 accept
+ icmpv6 type { mld-listener-query, mld2-listener-report } ip6 hoplimit 1 accept
+ udp sport 547 udp dport 546 ip6 saddr fe80::/64 oifname { "eth1", "eth2.10", "eth2.20" } accept
+ udp sport 67 udp dport 68 ip saddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 } accept
+ tcp dport 22 ip daddr 192.168.1.10 accept
+ }
+
+ chain POSTROUTING_SRCNAT {
+ type nat hook postrouting priority srcnat; policy accept;
+ ip saddr { 192.168.1.0-192.168.4.255 } oifname "eth0" masquerade
+ }
+}
diff --git a/tests/shell/testcases/nft-f/sample-ruleset b/tests/shell/testcases/nft-f/sample-ruleset
new file mode 100755
index 00000000..763e41a1
--- /dev/null
+++ b/tests/shell/testcases/nft-f/sample-ruleset
@@ -0,0 +1,262 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_chain_binding)
+
+$NFT -f /dev/stdin <<"EOF"
+define public_if = eth0
+define trusted_if = eth1
+define voip_if = eth2.10
+define guest_if = eth2.20
+define home_if = { $trusted_if, $voip_if, $guest_if }
+define home_ipv6_if = { $trusted_if, $voip_if, $guest_if }
+
+define masq_ip = { 192.168.1.0/24, 192.168.2.0/24, 192.168.3.0/24, 192.168.4.0/24 }
+define masq_if = $public_if
+
+define host1_ip = 192.168.1.10
+define host2_ip = 192.168.2.20
+define host3_ip = 192.168.3.30
+define host4_ip = 192.168.4.40
+
+define proxy_port = 8443
+
+define private_ip = { 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8 }
+define private_ip6 = { fe80::/64, fd00::/8 }
+define bogons_ip = { 0.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.0.0.0/24, 192.0.2.0/24, 192.168.0.0/16, 198.18.0.0/15, 198.51.100.0/24, 203.0.113.0/24, 224.0.0.0/3 }
+define bogons_ip6 = { ::/3, 2001:0002::/48, 2001:0003::/32, 2001:10::/28, 2001:20::/28, 2001::/32, 2001:db8::/32, 2002::/16, 3000::/4, 4000::/2, 8000::/1 }
+
+define sip_whitelist_ip6 = { 2001:db8::1/128, 2001:db8::2/128 }
+define smtps_whitelist_ip = 10.0.0.1
+define protocol_whitelist = { tcp, udp, icmp, ipv6-icmp }
+
+table inet filter {
+ map if_input {
+ type ifname : verdict;
+ elements = { $public_if : jump public_input, $trusted_if : jump home_input, $voip_if : jump home_input, $guest_if : jump home_input }
+ }
+ map if_forward {
+ type ifname : verdict;
+ elements = { $public_if : jump public_forward, $trusted_if : jump trusted_forward, $voip_if : jump voip_forward, $guest_if : jump guest_forward }
+ }
+ map if_output {
+ type ifname : verdict;
+ elements = { $public_if : jump public_output, $trusted_if : jump home_output, $voip_if : jump home_output, $guest_if : jump home_output }
+ }
+
+ set ipv4_blacklist { type ipv4_addr; flags interval; auto-merge; }
+ set ipv6_blacklist { type ipv6_addr; flags interval; auto-merge; }
+ set limit_src_ip { type ipv4_addr; flags dynamic, timeout; size 1024; }
+ set limit_src_ip6 { type ipv6_addr; flags dynamic, timeout; size 1024; }
+
+ chain PREROUTING_RAW {
+ type filter hook prerouting priority raw;
+
+ meta l4proto != $protocol_whitelist counter drop
+ tcp flags syn jump {
+ tcp option maxseg size 1-500 counter drop
+ tcp sport 0 counter drop
+ }
+ rt type 0 counter drop
+ }
+
+ chain PREROUTING_MANGLE {
+ type filter hook prerouting priority mangle;
+
+ ct state vmap { invalid : jump ct_invalid_pre, untracked : jump ct_untracked_pre, new : jump ct_new_pre, related : jump rpfilter }
+ }
+ chain ct_invalid_pre {
+ counter drop
+ }
+ chain ct_untracked_pre {
+ icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, mld-listener-query, mld2-listener-report } return
+ counter drop
+ }
+ chain ct_new_pre {
+ jump rpfilter
+
+ tcp flags & (fin|syn|rst|ack) != syn counter drop
+
+ iifname $public_if meta nfproto vmap { ipv4 : jump blacklist_input_ipv4, ipv6 : jump blacklist_input_ipv6 }
+ }
+ chain rpfilter {
+ ip saddr 0.0.0.0 ip daddr 255.255.255.255 udp sport bootpc udp dport bootps return
+ ip6 saddr ::/128 ip6 daddr . icmpv6 type { ff02::1:ff00:0/104 . nd-neighbor-solicit, ff02::16 . mld2-listener-report } return
+
+ fib saddr . iif oif eq 0 counter drop
+ }
+ chain blacklist_input_ipv4{
+ ip saddr $bogons_ip counter drop
+ ip saddr @ipv4_blacklist counter drop
+ }
+ chain blacklist_input_ipv6{
+ icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } ip6 saddr fe80::/64 return
+ udp sport dhcpv6-server ip6 saddr fe80::/64 return
+
+ ip6 saddr $bogons_ip6 counter drop
+ ip6 saddr @ipv6_blacklist counter drop
+ }
+
+ chain INPUT {
+ type filter hook input priority filter; policy drop;
+
+ iif lo accept
+
+ ct state established,related accept
+
+ iifname vmap @if_input
+
+ log prefix "NFT REJECT IN " flags ether flags ip options limit rate 5/second burst 10 packets reject
+ }
+ chain public_input {
+ icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } ip6 saddr fe80::/64 ip6 hoplimit 255 accept
+
+ udp sport dhcpv6-server udp dport dhcpv6-client ip6 saddr fe80::/64 accept
+ fib daddr type { broadcast, multicast, anycast } counter drop
+
+ counter drop
+ }
+ chain home_input {
+ icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } ip6 hoplimit 255 accept
+ icmpv6 type { mld-listener-query, mld2-listener-report } ip6 hoplimit 1 accept
+
+ udp sport bootpc udp dport bootps accept
+ udp sport dhcpv6-client udp dport dhcpv6-server iifname $home_ipv6_if accept
+
+ fib daddr type { broadcast, multicast, anycast } counter drop
+
+ icmp type echo-request accept
+ icmpv6 type echo-request accept
+
+ tcp dport ssh iifname $trusted_if accept
+
+ meta l4proto { tcp, udp } th dport domain jump {
+ ip6 saddr != $private_ip6 counter reject
+ accept
+ }
+
+ udp dport ntp accept
+
+ tcp dport $proxy_port accept
+ }
+
+ chain FORWARD_MANGLE {
+ type filter hook forward priority mangle;
+
+ oifname $public_if jump {
+ ct state new meta nfproto vmap { ipv4 : jump blacklist_output_ipv4, ipv6 : jump blacklist_output_ipv6 }
+ tcp flags & (syn|rst) == syn tcp option maxseg size set rt mtu
+ }
+ }
+ chain blacklist_output_ipv4 {
+ ip daddr $bogons_ip goto log_blacklist
+ ip daddr @ipv4_blacklist goto log_blacklist
+ }
+ chain blacklist_output_ipv6 {
+ icmpv6 type . ip6 daddr { nd-router-solicit . ff02::2/128, nd-neighbor-solicit . ff02::1:ff00:0/104, nd-neighbor-advert . fe80::/64, nd-neighbor-advert . ff02::1/128, nd-neighbor-advert . ff02::1:ff00:0/104, mld2-listener-report . ff02::16/128 } return
+ udp dport dhcpv6-server ip6 daddr ff02::1:2 return
+
+ ip6 daddr $bogons_ip6 goto log_blacklist
+ ip6 daddr @ipv6_blacklist goto log_blacklist
+ }
+ chain log_blacklist {
+ log prefix "NFT BLACKLIST " flags ether flags ip options limit rate 5/minute burst 10 packets drop
+ counter drop
+ }
+
+ chain FORWARD {
+ type filter hook forward priority filter; policy drop;
+
+ ct state established,related accept
+
+ fib daddr type { broadcast, multicast, anycast } counter drop
+
+ iifname vmap @if_forward
+
+ log prefix "NFT REJECT FWD " flags ether flags ip options limit rate 5/second burst 10 packets reject
+ }
+ chain public_forward {
+ udp dport { 5060, 7078-7097 } oifname $voip_if jump {
+ ip6 saddr $sip_whitelist_ip6 accept
+ meta nfproto ipv6 log prefix "NFT DROP SIP " flags ether flags ip options limit rate 5/second burst 10 packets drop
+ }
+
+ counter drop
+ }
+ chain trusted_forward {
+ oifname $public_if accept
+
+ icmp type echo-request accept
+ icmpv6 type echo-request accept
+
+ ip daddr { $host3_ip, $host4_ip } tcp dport vmap { ssh : accept, https : accept, http : drop }
+
+ ip daddr $host2_ip jump {
+ tcp dport { http, https, printer, ipp, 9100 } accept
+ udp dport snmp accept
+ }
+ }
+ chain voip_forward {
+ icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request } oifname $public_if accept
+
+ ip6 daddr $sip_whitelist_ip6 jump {
+ udp dport { 3478, 5060 } accept
+ udp sport { 7078-7097 } accept
+ tcp dport 5061 accept
+ }
+
+ tcp dport 587 ip daddr $smtps_whitelist_ip accept
+ tcp dport http oifname $public_if counter reject
+ }
+ chain guest_forward {
+ oifname $public_if accept
+ }
+
+ chain OUTPUT {
+ type filter hook output priority filter; policy drop;
+
+ oif lo accept
+
+ ct state vmap { established : accept, related : accept, invalid : jump ct_invalid_out, untracked : jump ct_untracked_out }
+
+ oifname vmap @if_output
+
+ log prefix "NFT REJECT OUT " flags ether flags ip options limit rate 5/second burst 10 packets reject
+ }
+ chain ct_invalid_out {
+ counter drop
+ }
+ chain ct_untracked_out {
+ icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, mld-listener-query, mld2-listener-report } return
+ counter drop
+ }
+ chain public_output {
+ ct state new meta nfproto vmap { ipv4 : jump blacklist_output_ipv4, ipv6 : jump blacklist_output_ipv6 }
+
+ icmp type { destination-unreachable, time-exceeded, parameter-problem, echo-request } accept
+ icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request } accept
+ icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } ip6 hoplimit 255 accept
+ icmpv6 type { mld-listener-query, mld2-listener-report } ip6 hoplimit 1 accept
+
+ udp dport dhcpv6-server ip6 saddr fe80::/64 ip6 daddr ff02::1:2 accept
+
+ udp dport { domain, ntp } accept
+ tcp dport { https, 587, domain-s } accept
+ }
+ chain home_output {
+ icmp type { destination-unreachable, time-exceeded, parameter-problem, echo-request } accept
+ icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request } accept
+ icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } ip6 hoplimit 255 accept
+ icmpv6 type { mld-listener-query, mld2-listener-report } ip6 hoplimit 1 accept
+
+ udp sport dhcpv6-server udp dport dhcpv6-client ip6 saddr fe80::/64 oifname $home_ipv6_if accept
+ udp sport bootps udp dport bootpc ip saddr $private_ip accept
+ tcp dport ssh ip daddr $host1_ip accept
+ }
+
+ chain POSTROUTING_SRCNAT {
+ type nat hook postrouting priority srcnat;
+
+ meta nfproto ipv4 ip saddr $masq_ip oifname $masq_if masquerade
+ }
+}
+EOF
diff --git a/tests/shell/testcases/nft-i/dumps/0001define_0.json-nft b/tests/shell/testcases/nft-i/dumps/0001define_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/nft-i/dumps/0001define_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/nft-i/dumps/0001define_0.nft b/tests/shell/testcases/nft-i/dumps/0001define_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/nft-i/dumps/0001define_0.nft
diff --git a/tests/shell/testcases/optimizations/dumps/dependency_kill.json-nft b/tests/shell/testcases/optimizations/dumps/dependency_kill.json-nft
new file mode 100644
index 00000000..712182e9
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/dependency_kill.json-nft
@@ -0,0 +1,776 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "bridge",
+ "name": "foo",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "bridge",
+ "table": "foo",
+ "name": "bar",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "bridge",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "protocol"
+ }
+ },
+ "right": "ip"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 67
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "bridge",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "protocol"
+ }
+ },
+ "right": "ip6"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 67
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "bridge",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ether",
+ "field": "type"
+ }
+ },
+ "right": "ip"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 67
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "bridge",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ether",
+ "field": "type"
+ }
+ },
+ "right": "ip6"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 67
+ }
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "foo",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "foo",
+ "name": "bar",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 67
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "protocol"
+ }
+ },
+ "right": "ip6"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 67
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 67
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ether",
+ "field": "type"
+ }
+ },
+ "right": "ip6"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 67
+ }
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "foo",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "foo",
+ "name": "bar",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "protocol"
+ }
+ },
+ "right": "ip"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 67
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 67
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ether",
+ "field": "type"
+ }
+ },
+ "right": "ip"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 67
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 67
+ }
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "netdev",
+ "name": "foo",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "foo",
+ "name": "bar",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "netdev",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "protocol"
+ }
+ },
+ "right": "ip"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 67
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "netdev",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "protocol"
+ }
+ },
+ "right": "ip6"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 67
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "netdev",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ether",
+ "field": "type"
+ }
+ },
+ "right": "ip"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 67
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "netdev",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ether",
+ "field": "type"
+ }
+ },
+ "right": "ip6"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 67
+ }
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "foo",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "foo",
+ "name": "bar",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "protocol"
+ }
+ },
+ "right": "ip"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 67
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "protocol"
+ }
+ },
+ "right": "ip6"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 67
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ether",
+ "field": "type"
+ }
+ },
+ "right": "ip"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 67
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ether",
+ "field": "type"
+ }
+ },
+ "right": "ip6"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 67
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "nfproto"
+ }
+ },
+ "right": "ipv4"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 67
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "foo",
+ "chain": "bar",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "nfproto"
+ }
+ },
+ "right": "ipv6"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 67
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optimizations/dumps/merge_nat.json-nft b/tests/shell/testcases/optimizations/dumps/merge_nat.json-nft
new file mode 100644
index 00000000..a6cf1bfc
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_nat.json-nft
@@ -0,0 +1,379 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test1",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "test1",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test1",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oif"
+ }
+ },
+ "right": "lo"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test1",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "dnat": {
+ "addr": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ "4.4.4.4",
+ "1.1.1.1"
+ ],
+ [
+ "5.5.5.5",
+ "2.2.2.2"
+ ]
+ ]
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test2",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "test2",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test2",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oif"
+ }
+ },
+ "right": "lo"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test2",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "dnat": {
+ "family": "ip",
+ "addr": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ 80,
+ {
+ "concat": [
+ "1.1.1.1",
+ 8001
+ ]
+ }
+ ],
+ [
+ 81,
+ {
+ "concat": [
+ "2.2.2.2",
+ 9001
+ ]
+ }
+ ]
+ ]
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test2",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": {
+ "set": [
+ {
+ "prefix": {
+ "addr": "10.141.11.0",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "10.141.13.0",
+ "len": 24
+ }
+ }
+ ]
+ }
+ }
+ },
+ {
+ "masquerade": null
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test4",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "test4",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test4",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oif"
+ }
+ },
+ "right": "lo"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test4",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "dnat": {
+ "family": "ip",
+ "addr": {
+ "map": {
+ "key": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ }
+ ]
+ },
+ "data": {
+ "set": [
+ [
+ {
+ "concat": [
+ "1.1.1.1",
+ 80
+ ]
+ },
+ {
+ "concat": [
+ "4.4.4.4",
+ 8000
+ ]
+ }
+ ],
+ [
+ {
+ "concat": [
+ "2.2.2.2",
+ 81
+ ]
+ },
+ {
+ "concat": [
+ "3.3.3.3",
+ 9000
+ ]
+ }
+ ]
+ ]
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test4",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "redirect": {
+ "port": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ 83,
+ 8083
+ ],
+ [
+ 84,
+ 8084
+ ]
+ ]
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test4",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 85
+ }
+ },
+ {
+ "redirect": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optimizations/dumps/merge_nat.nft b/tests/shell/testcases/optimizations/dumps/merge_nat.nft
new file mode 100644
index 00000000..f6c119ec
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_nat.nft
@@ -0,0 +1,21 @@
+table ip test1 {
+ chain y {
+ oif "lo" accept
+ dnat to ip saddr map { 4.4.4.4 : 1.1.1.1, 5.5.5.5 : 2.2.2.2 }
+ }
+}
+table ip test2 {
+ chain y {
+ oif "lo" accept
+ dnat ip to tcp dport map { 80 : 1.1.1.1 . 8001, 81 : 2.2.2.2 . 9001 }
+ ip saddr { 10.141.11.0/24, 10.141.13.0/24 } masquerade
+ }
+}
+table ip test4 {
+ chain y {
+ oif "lo" accept
+ dnat ip to ip daddr . tcp dport map { 1.1.1.1 . 80 : 4.4.4.4 . 8000, 2.2.2.2 . 81 : 3.3.3.3 . 9000 }
+ redirect to :tcp dport map { 83 : 8083, 84 : 8084 }
+ tcp dport 85 redirect
+ }
+}
diff --git a/tests/shell/testcases/optimizations/dumps/merge_nat_concat.json-nft b/tests/shell/testcases/optimizations/dumps/merge_nat_concat.json-nft
new file mode 100644
index 00000000..dc67feec
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_nat_concat.json-nft
@@ -0,0 +1,200 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test3",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "test3",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test3",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oif"
+ }
+ },
+ "right": "lo"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test3",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "snat": {
+ "addr": {
+ "map": {
+ "key": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "sport"
+ }
+ }
+ ]
+ },
+ "data": {
+ "set": [
+ [
+ {
+ "concat": [
+ "1.1.1.1",
+ {
+ "range": [
+ 1024,
+ 65535
+ ]
+ }
+ ]
+ },
+ "3.3.3.3"
+ ],
+ [
+ {
+ "concat": [
+ "2.2.2.2",
+ {
+ "range": [
+ 1024,
+ 65535
+ ]
+ }
+ ]
+ },
+ "4.4.4.4"
+ ]
+ ]
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test3",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oifname"
+ }
+ },
+ "right": "enp2s0"
+ }
+ },
+ {
+ "snat": {
+ "family": "ip",
+ "addr": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ {
+ "prefix": {
+ "addr": "10.1.1.0",
+ "len": 24
+ }
+ },
+ {
+ "range": [
+ "72.2.3.66",
+ "72.2.3.78"
+ ]
+ }
+ ]
+ ]
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test3",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": {
+ "set": [
+ 8888,
+ 9999
+ ]
+ }
+ }
+ },
+ {
+ "redirect": null
+ }
+ ]
+ }
+ }
+ ]
+}
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 00000000..0faddfd1
--- /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/dumps/merge_nat_inet.json-nft b/tests/shell/testcases/optimizations/dumps/merge_nat_inet.json-nft
new file mode 100644
index 00000000..99930f11
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_nat_inet.json-nft
@@ -0,0 +1,208 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "nat",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "nat",
+ "name": "prerouting",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "nat",
+ "name": "postrouting",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "nat",
+ "chain": "prerouting",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oif"
+ }
+ },
+ "right": "lo"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "nat",
+ "chain": "prerouting",
+ "handle": 0,
+ "expr": [
+ {
+ "dnat": {
+ "family": "ip",
+ "addr": {
+ "map": {
+ "key": {
+ "concat": [
+ {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ }
+ ]
+ },
+ "data": {
+ "set": [
+ [
+ {
+ "concat": [
+ "enp2s0",
+ "72.2.3.70",
+ 80
+ ]
+ },
+ {
+ "concat": [
+ "10.1.1.52",
+ 80
+ ]
+ }
+ ],
+ [
+ {
+ "concat": [
+ "enp2s0",
+ "72.2.3.66",
+ 53122
+ ]
+ },
+ {
+ "concat": [
+ "10.1.1.10",
+ 22
+ ]
+ }
+ ],
+ [
+ {
+ "concat": [
+ "enp2s0",
+ "72.2.3.66",
+ 443
+ ]
+ },
+ {
+ "concat": [
+ "10.1.1.52",
+ 443
+ ]
+ }
+ ]
+ ]
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "nat",
+ "chain": "postrouting",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oif"
+ }
+ },
+ "right": "lo"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "nat",
+ "chain": "postrouting",
+ "handle": 0,
+ "expr": [
+ {
+ "snat": {
+ "family": "ip",
+ "addr": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ "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 00000000..a1a11354
--- /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/dumps/merge_reject.json-nft b/tests/shell/testcases/optimizations/dumps/merge_reject.json-nft
new file mode 100644
index 00000000..46ed0677
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_reject.json-nft
@@ -0,0 +1,320 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "right": "172.30.33.70"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 3306
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ }
+ ]
+ },
+ "right": {
+ "set": [
+ {
+ "concat": [
+ "tcp",
+ "172.30.238.117",
+ 8080
+ ]
+ },
+ {
+ "concat": [
+ "tcp",
+ "172.30.33.71",
+ 3306
+ ]
+ },
+ {
+ "concat": [
+ "tcp",
+ "172.30.254.251",
+ 3306
+ ]
+ }
+ ]
+ }
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "reject": {
+ "type": "icmp",
+ "expr": "port-unreachable"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "right": "172.30.254.252"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 3306
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "reject": {
+ "type": "tcp reset"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "ip6",
+ "field": "daddr"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ }
+ ]
+ },
+ "right": {
+ "set": [
+ {
+ "concat": [
+ "tcp",
+ "aaaa::3",
+ 8080
+ ]
+ },
+ {
+ "concat": [
+ "tcp",
+ "aaaa::2",
+ 3306
+ ]
+ },
+ {
+ "concat": [
+ "tcp",
+ "aaaa::4",
+ 3306
+ ]
+ }
+ ]
+ }
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "reject": {
+ "type": "icmpv6",
+ "expr": "port-unreachable"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip6",
+ "field": "daddr"
+ }
+ },
+ "right": "aaaa::5"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 3306
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "reject": {
+ "type": "tcp reset"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optimizations/dumps/merge_reject.nft b/tests/shell/testcases/optimizations/dumps/merge_reject.nft
new file mode 100644
index 00000000..c29ad6d5
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_reject.nft
@@ -0,0 +1,13 @@
+table ip x {
+ chain y {
+ ip daddr 172.30.33.70 tcp dport 3306 counter packets 0 bytes 0 drop
+ meta l4proto . ip daddr . tcp dport { tcp . 172.30.238.117 . 8080, tcp . 172.30.33.71 . 3306, tcp . 172.30.254.251 . 3306 } counter packets 0 bytes 0 reject
+ ip daddr 172.30.254.252 tcp dport 3306 counter packets 0 bytes 0 reject with tcp reset
+ }
+}
+table ip6 x {
+ chain y {
+ meta l4proto . ip6 daddr . tcp dport { tcp . aaaa::3 . 8080, tcp . aaaa::2 . 3306, tcp . aaaa::4 . 3306 } counter packets 0 bytes 0 reject
+ ip6 daddr aaaa::5 tcp dport 3306 counter packets 0 bytes 0 reject with tcp reset
+ }
+}
diff --git a/tests/shell/testcases/optimizations/dumps/merge_stmts.json-nft b/tests/shell/testcases/optimizations/dumps/merge_stmts.json-nft
new file mode 100644
index 00000000..c392b76a
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_stmts.json-nft
@@ -0,0 +1,63 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "right": {
+ "set": [
+ "192.168.0.1",
+ "192.168.0.2",
+ "192.168.0.3"
+ ]
+ }
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optimizations/dumps/merge_stmts.nft b/tests/shell/testcases/optimizations/dumps/merge_stmts.nft
new file mode 100644
index 00000000..b56ea3ed
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_stmts.nft
@@ -0,0 +1,5 @@
+table ip x {
+ chain y {
+ ip daddr { 192.168.0.1, 192.168.0.2, 192.168.0.3 } counter packets 0 bytes 0 accept
+ }
+}
diff --git a/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.json-nft b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.json-nft
new file mode 100644
index 00000000..267d84ef
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.json-nft
@@ -0,0 +1,374 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "c1",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "c2",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "c3",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ }
+ ]
+ },
+ "right": {
+ "set": [
+ {
+ "concat": [
+ "eth1",
+ "1.1.1.1",
+ "2.2.2.3"
+ ]
+ },
+ {
+ "concat": [
+ "eth1",
+ "1.1.1.2",
+ "2.2.2.4"
+ ]
+ },
+ {
+ "concat": [
+ "eth1",
+ "1.1.1.2",
+ {
+ "prefix": {
+ "addr": "2.2.3.0",
+ "len": 24
+ }
+ }
+ ]
+ },
+ {
+ "concat": [
+ "eth1",
+ "1.1.1.2",
+ {
+ "range": [
+ "2.2.4.0",
+ "2.2.4.10"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ "eth2",
+ "1.1.1.3",
+ "2.2.2.5"
+ ]
+ }
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "protocol"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "th",
+ "field": "dport"
+ }
+ }
+ ]
+ },
+ "right": {
+ "set": [
+ {
+ "concat": [
+ "tcp",
+ 22
+ ]
+ },
+ {
+ "concat": [
+ "udp",
+ 67
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "c1",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ {
+ "meta": {
+ "key": "iifname"
+ }
+ }
+ ]
+ },
+ "right": {
+ "set": [
+ {
+ "concat": [
+ 51820,
+ "foo"
+ ]
+ },
+ {
+ "concat": [
+ 514,
+ "bar"
+ ]
+ },
+ {
+ "concat": [
+ 67,
+ "bar"
+ ]
+ }
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "c2",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ {
+ "meta": {
+ "key": "iifname"
+ }
+ }
+ ]
+ },
+ "right": {
+ "set": [
+ {
+ "concat": [
+ 100,
+ "foo"
+ ]
+ },
+ {
+ "concat": [
+ 51820,
+ "foo"
+ ]
+ },
+ {
+ "concat": [
+ 514,
+ "bar"
+ ]
+ },
+ {
+ "concat": [
+ 67,
+ "bar"
+ ]
+ }
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "c3",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ {
+ "meta": {
+ "key": "iifname"
+ }
+ }
+ ]
+ },
+ "right": {
+ "set": [
+ {
+ "concat": [
+ 100,
+ "foo"
+ ]
+ },
+ {
+ "concat": [
+ 51820,
+ "foo"
+ ]
+ },
+ {
+ "concat": [
+ 514,
+ "bar"
+ ]
+ },
+ {
+ "concat": [
+ 67,
+ "bar"
+ ]
+ },
+ {
+ "concat": [
+ 100,
+ "test"
+ ]
+ },
+ {
+ "concat": [
+ 51820,
+ "test"
+ ]
+ }
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft
new file mode 100644
index 00000000..f56cea1c
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft
@@ -0,0 +1,18 @@
+table ip x {
+ chain y {
+ iifname . ip saddr . ip daddr { "eth1" . 1.1.1.1 . 2.2.2.3, "eth1" . 1.1.1.2 . 2.2.2.4, "eth1" . 1.1.1.2 . 2.2.3.0/24, "eth1" . 1.1.1.2 . 2.2.4.0-2.2.4.10, "eth2" . 1.1.1.3 . 2.2.2.5 } accept
+ ip protocol . th dport { tcp . 22, udp . 67 }
+ }
+
+ chain c1 {
+ udp dport . iifname { 51820 . "foo", 514 . "bar", 67 . "bar" } accept
+ }
+
+ chain c2 {
+ udp dport . iifname { 100 . "foo", 51820 . "foo", 514 . "bar", 67 . "bar" } accept
+ }
+
+ chain c3 {
+ udp dport . iifname { 100 . "foo", 51820 . "foo", 514 . "bar", 67 . "bar", 100 . "test", 51820 . "test" } accept
+ }
+}
diff --git a/tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.json-nft b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.json-nft
new file mode 100644
index 00000000..5dfa40a8
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.json-nft
@@ -0,0 +1,167 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "x",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "concat": [
+ {
+ "meta": {
+ "key": "pkttype"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ }
+ ]
+ },
+ "data": {
+ "set": [
+ [
+ {
+ "concat": [
+ "broadcast",
+ 547
+ ]
+ },
+ {
+ "accept": null
+ }
+ ],
+ [
+ {
+ "concat": [
+ "broadcast",
+ 67
+ ]
+ },
+ {
+ "accept": null
+ }
+ ],
+ [
+ {
+ "concat": [
+ "multicast",
+ 1900
+ ]
+ },
+ {
+ "drop": null
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ }
+ ]
+ },
+ "data": {
+ "set": [
+ [
+ {
+ "concat": [
+ "1.1.1.1",
+ "2.2.2.2"
+ ]
+ },
+ {
+ "accept": null
+ }
+ ],
+ [
+ {
+ "concat": [
+ "2.2.2.2",
+ "3.3.3.3"
+ ]
+ },
+ {
+ "drop": null
+ }
+ ],
+ [
+ {
+ "concat": [
+ "4.4.4.4",
+ "5.5.5.5"
+ ]
+ },
+ {
+ "accept": null
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.nft b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.nft
new file mode 100644
index 00000000..780aa09a
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.nft
@@ -0,0 +1,9 @@
+table ip x {
+ chain x {
+ meta pkttype . udp dport vmap { broadcast . 547 : accept, broadcast . 67 : accept, multicast . 1900 : drop }
+ }
+
+ chain y {
+ ip saddr . ip daddr vmap { 1.1.1.1 . 2.2.2.2 : accept, 2.2.2.2 . 3.3.3.3 : drop, 4.4.4.4 . 5.5.5.5 : accept }
+ }
+}
diff --git a/tests/shell/testcases/optimizations/dumps/merge_stmts_vmap.json-nft b/tests/shell/testcases/optimizations/dumps/merge_stmts_vmap.json-nft
new file mode 100644
index 00000000..17d57b8f
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_stmts_vmap.json-nft
@@ -0,0 +1,182 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "z",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "w",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ "invalid",
+ {
+ "drop": null
+ }
+ ],
+ [
+ "established",
+ {
+ "accept": null
+ }
+ ],
+ [
+ "related",
+ {
+ "accept": null
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "z",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ 1,
+ {
+ "accept": null
+ }
+ ],
+ [
+ {
+ "range": [
+ 2,
+ 3
+ ]
+ },
+ {
+ "drop": null
+ }
+ ],
+ [
+ 4,
+ {
+ "accept": null
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "w",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ {
+ "elem": {
+ "val": "1.1.1.1",
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ],
+ [
+ {
+ "elem": {
+ "val": "1.1.1.2",
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optimizations/dumps/merge_stmts_vmap.nft b/tests/shell/testcases/optimizations/dumps/merge_stmts_vmap.nft
new file mode 100644
index 00000000..8ecbd927
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_stmts_vmap.nft
@@ -0,0 +1,13 @@
+table ip x {
+ chain y {
+ ct state vmap { invalid : drop, established : accept, related : accept }
+ }
+
+ chain z {
+ tcp dport vmap { 1 : accept, 2-3 : drop, 4 : accept }
+ }
+
+ chain w {
+ ip saddr vmap { 1.1.1.1 counter packets 0 bytes 0 : accept, 1.1.1.2 counter packets 0 bytes 0 : drop }
+ }
+}
diff --git a/tests/shell/testcases/optimizations/dumps/merge_vmap_raw.json-nft b/tests/shell/testcases/optimizations/dumps/merge_vmap_raw.json-nft
new file mode 100644
index 00000000..b8ad126c
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_vmap_raw.json-nft
@@ -0,0 +1,438 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "nat_dns_dnstc",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "nat_dns_this_5301",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "nat_dns_saturn_5301",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "nat_dns_saturn_5302",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "nat_dns_saturn_5303",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "nat_dns_acme",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "nat_dns_dnstc",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "right": "udp"
+ }
+ },
+ {
+ "redirect": {
+ "port": 5300
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "nat_dns_dnstc",
+ "handle": 0,
+ "expr": [
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "nat_dns_this_5301",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "right": "udp"
+ }
+ },
+ {
+ "redirect": {
+ "port": 5301
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "nat_dns_this_5301",
+ "handle": 0,
+ "expr": [
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "nat_dns_saturn_5301",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "nfproto"
+ }
+ },
+ "right": "ipv4"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "right": "udp"
+ }
+ },
+ {
+ "dnat": {
+ "family": "ip",
+ "addr": "240.0.1.2",
+ "port": 5301
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "nat_dns_saturn_5301",
+ "handle": 0,
+ "expr": [
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "nat_dns_saturn_5302",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "nfproto"
+ }
+ },
+ "right": "ipv4"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "right": "udp"
+ }
+ },
+ {
+ "dnat": {
+ "family": "ip",
+ "addr": "240.0.1.2",
+ "port": 5302
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "nat_dns_saturn_5302",
+ "handle": 0,
+ "expr": [
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "nat_dns_saturn_5303",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "nfproto"
+ }
+ },
+ "right": "ipv4"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "right": "udp"
+ }
+ },
+ {
+ "dnat": {
+ "family": "ip",
+ "addr": "240.0.1.2",
+ "port": 5303
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "nat_dns_saturn_5303",
+ "handle": 0,
+ "expr": [
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "nat_dns_acme",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "udp",
+ "field": "length"
+ }
+ },
+ {
+ "payload": {
+ "base": "th",
+ "offset": 160,
+ "len": 128
+ }
+ }
+ ]
+ },
+ "data": {
+ "set": [
+ [
+ {
+ "concat": [
+ {
+ "range": [
+ 47,
+ 63
+ ]
+ },
+ "0xe373135363130333131303735353203"
+ ]
+ },
+ {
+ "goto": {
+ "target": "nat_dns_dnstc"
+ }
+ }
+ ],
+ [
+ {
+ "concat": [
+ {
+ "range": [
+ 62,
+ 78
+ ]
+ },
+ "0xe31393032383939353831343037320e"
+ ]
+ },
+ {
+ "goto": {
+ "target": "nat_dns_this_5301"
+ }
+ }
+ ],
+ [
+ {
+ "concat": [
+ {
+ "range": [
+ 62,
+ 78
+ ]
+ },
+ "0xe31363436323733373931323934300e"
+ ]
+ },
+ {
+ "goto": {
+ "target": "nat_dns_saturn_5301"
+ }
+ }
+ ],
+ [
+ {
+ "concat": [
+ {
+ "range": [
+ 62,
+ 78
+ ]
+ },
+ "0xe32393535373539353636383732310e"
+ ]
+ },
+ {
+ "goto": {
+ "target": "nat_dns_saturn_5302"
+ }
+ }
+ ],
+ [
+ {
+ "concat": [
+ {
+ "range": [
+ 62,
+ 78
+ ]
+ },
+ "0xe38353439353637323038363633390e"
+ ]
+ },
+ {
+ "goto": {
+ "target": "nat_dns_saturn_5303"
+ }
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "nat_dns_acme",
+ "handle": 0,
+ "expr": [
+ {
+ "drop": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optimizations/dumps/merge_vmap_raw.nft b/tests/shell/testcases/optimizations/dumps/merge_vmap_raw.nft
new file mode 100644
index 00000000..18847116
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_vmap_raw.nft
@@ -0,0 +1,31 @@
+table inet x {
+ chain nat_dns_dnstc {
+ meta l4proto udp redirect to :5300
+ drop
+ }
+
+ chain nat_dns_this_5301 {
+ meta l4proto udp redirect to :5301
+ drop
+ }
+
+ chain nat_dns_saturn_5301 {
+ meta nfproto ipv4 meta l4proto udp dnat ip to 240.0.1.2:5301
+ drop
+ }
+
+ chain nat_dns_saturn_5302 {
+ meta nfproto ipv4 meta l4proto udp dnat ip to 240.0.1.2:5302
+ drop
+ }
+
+ chain nat_dns_saturn_5303 {
+ meta nfproto ipv4 meta l4proto udp dnat ip to 240.0.1.2:5303
+ drop
+ }
+
+ chain nat_dns_acme {
+ udp length . @th,160,128 vmap { 47-63 . 0xe373135363130333131303735353203 : goto nat_dns_dnstc, 62-78 . 0xe31393032383939353831343037320e : goto nat_dns_this_5301, 62-78 . 0xe31363436323733373931323934300e : goto nat_dns_saturn_5301, 62-78 . 0xe32393535373539353636383732310e : goto nat_dns_saturn_5302, 62-78 . 0xe38353439353637323038363633390e : goto nat_dns_saturn_5303 }
+ drop
+ }
+}
diff --git a/tests/shell/testcases/optimizations/dumps/merge_vmaps.json-nft b/tests/shell/testcases/optimizations/dumps/merge_vmaps.json-nft
new file mode 100644
index 00000000..e87f1c4c
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_vmaps.json-nft
@@ -0,0 +1,205 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "filter_in_tcp",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "filter_in_udp",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 65535,
+ "flags": [
+ "dynamic"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "set": {
+ "op": "update",
+ "elem": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "set": "@s",
+ "stmt": [
+ {
+ "limit": {
+ "rate": 12,
+ "burst": 30,
+ "per": "minute"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ 80,
+ {
+ "accept": null
+ }
+ ],
+ [
+ 81,
+ {
+ "accept": null
+ }
+ ],
+ [
+ 443,
+ {
+ "accept": null
+ }
+ ],
+ [
+ {
+ "range": [
+ 8000,
+ 8100
+ ]
+ },
+ {
+ "accept": null
+ }
+ ],
+ [
+ {
+ "range": [
+ 24000,
+ 25000
+ ]
+ },
+ {
+ "accept": null
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ "tcp",
+ {
+ "goto": {
+ "target": "filter_in_tcp"
+ }
+ }
+ ],
+ [
+ "udp",
+ {
+ "goto": {
+ "target": "filter_in_udp"
+ }
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "log": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optimizations/dumps/merge_vmaps.nft b/tests/shell/testcases/optimizations/dumps/merge_vmaps.nft
new file mode 100644
index 00000000..c981acf0
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/merge_vmaps.nft
@@ -0,0 +1,20 @@
+table ip x {
+ set s {
+ type ipv4_addr
+ size 65535
+ flags dynamic
+ }
+
+ chain filter_in_tcp {
+ }
+
+ chain filter_in_udp {
+ }
+
+ chain y {
+ update @s { ip saddr limit rate 12/minute burst 30 packets } accept
+ tcp dport vmap { 80 : accept, 81 : accept, 443 : accept, 8000-8100 : accept, 24000-25000 : accept }
+ meta l4proto vmap { tcp : goto filter_in_tcp, udp : goto filter_in_udp }
+ log
+ }
+}
diff --git a/tests/shell/testcases/optimizations/dumps/not_mergeable.json-nft b/tests/shell/testcases/optimizations/dumps/not_mergeable.json-nft
new file mode 100644
index 00000000..8e64ba1e
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/not_mergeable.json-nft
@@ -0,0 +1,140 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "t1",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "t2",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "t3",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "t4",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "jump": {
+ "target": "t1"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "jump": {
+ "target": "t2"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "version"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ 4,
+ {
+ "jump": {
+ "target": "t3"
+ }
+ }
+ ],
+ [
+ 6,
+ {
+ "jump": {
+ "target": "t4"
+ }
+ }
+ ]
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optimizations/dumps/not_mergeable.nft b/tests/shell/testcases/optimizations/dumps/not_mergeable.nft
new file mode 100644
index 00000000..02b89207
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/not_mergeable.nft
@@ -0,0 +1,19 @@
+table ip x {
+ chain t1 {
+ }
+
+ chain t2 {
+ }
+
+ chain t3 {
+ }
+
+ chain t4 {
+ }
+
+ chain y {
+ counter packets 0 bytes 0 jump t1
+ counter packets 0 bytes 0 jump t2
+ ip version vmap { 4 : jump t3, 6 : jump t4 }
+ }
+}
diff --git a/tests/shell/testcases/optimizations/dumps/ruleset.json-nft b/tests/shell/testcases/optimizations/dumps/ruleset.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/ruleset.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optimizations/dumps/ruleset.nft b/tests/shell/testcases/optimizations/dumps/ruleset.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/ruleset.nft
diff --git a/tests/shell/testcases/optimizations/dumps/single_anon_set.json-nft b/tests/shell/testcases/optimizations/dumps/single_anon_set.json-nft
new file mode 100644
index 00000000..26634134
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/single_anon_set.json-nft
@@ -0,0 +1,360 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "test",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "127.0.0.1"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iif"
+ }
+ },
+ "right": "lo"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "!=",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 22
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": {
+ "prefix": {
+ "addr": "127.0.0.0",
+ "len": 8
+ }
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": {
+ "range": [
+ "127.0.0.1",
+ "192.168.7.3"
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "sport"
+ }
+ },
+ "right": {
+ "range": [
+ 1,
+ 1023
+ ]
+ }
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "right": {
+ "set": [
+ "192.168.7.1",
+ "192.168.7.5"
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": {
+ "set": [
+ 80,
+ 443
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ }
+ ]
+ },
+ "right": {
+ "set": [
+ {
+ "concat": [
+ "192.168.0.1",
+ 22
+ ]
+ }
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ "192.168.0.1",
+ 1
+ ]
+ ]
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": {
+ "set": [
+ "established",
+ "related"
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optimizations/dumps/single_anon_set.nft.input b/tests/shell/testcases/optimizations/dumps/single_anon_set.nft.input
deleted file mode 100644
index 35b93832..00000000
--- a/tests/shell/testcases/optimizations/dumps/single_anon_set.nft.input
+++ /dev/null
@@ -1,35 +0,0 @@
-table ip test {
- chain test {
- # Test cases where anon set can be removed:
- ip saddr { 127.0.0.1 } accept
- iif { "lo" } accept
-
- # negation, can change to != 22.
- tcp dport != { 22 } drop
-
- # single prefix, can remove anon set.
- ip saddr { 127.0.0.0/8 } accept
-
- # range, can remove anon set.
- ip saddr { 127.0.0.1-192.168.7.3 } accept
- tcp sport { 1-1023 } drop
-
- # Test cases where anon set must be kept.
-
- # 2 elements, cannot remove the anon set.
- ip daddr { 192.168.7.1, 192.168.7.5 } accept
- tcp dport { 80, 443 } accept
-
- # single element, but concatenation which is not
- # supported outside of set/map context at this time.
- ip daddr . tcp dport { 192.168.0.1 . 22 } accept
-
- # single element, but a map.
- meta mark set ip daddr map { 192.168.0.1 : 1 }
-
- # 2 elements. This could be converted because
- # ct state cannot be both established and related
- # at the same time, but this needs extra work.
- ct state { established, related } accept
- }
-}
diff --git a/tests/shell/testcases/optimizations/dumps/single_anon_set_expr.json-nft b/tests/shell/testcases/optimizations/dumps/single_anon_set_expr.json-nft
new file mode 100644
index 00000000..c8adddb1
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/single_anon_set_expr.json-nft
@@ -0,0 +1,59 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "test",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "right": {
+ "set": [
+ {
+ "elem": {
+ "val": 10,
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optimizations/dumps/single_anon_set_expr.nft b/tests/shell/testcases/optimizations/dumps/single_anon_set_expr.nft
new file mode 100644
index 00000000..54880b92
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/single_anon_set_expr.nft
@@ -0,0 +1,5 @@
+table ip test {
+ chain test {
+ meta mark { 0x0000000a counter packets 0 bytes 0 }
+ }
+}
diff --git a/tests/shell/testcases/optimizations/dumps/skip_merge.json-nft b/tests/shell/testcases/optimizations/dumps/skip_merge.json-nft
new file mode 100644
index 00000000..7bb6c656
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/skip_merge.json-nft
@@ -0,0 +1,235 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "udp_input",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "tcp_input",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "udp_accepted",
+ "table": "filter",
+ "type": "inet_service",
+ "handle": 0,
+ "elem": [
+ 500,
+ 4500
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "tcp_accepted",
+ "table": "filter",
+ "type": "inet_service",
+ "handle": 0,
+ "elem": [
+ 80,
+ 443
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "udp_input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": {
+ "range": [
+ 1,
+ 128
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "udp_input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": "@udp_accepted"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "udp_input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 53
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "tcp_input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": {
+ "set": [
+ {
+ "range": [
+ 1,
+ 128
+ ]
+ },
+ {
+ "range": [
+ 8888,
+ 9999
+ ]
+ }
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "tcp_input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": "@tcp_accepted"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "tcp_input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": {
+ "range": [
+ 1024,
+ 65535
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optimizations/dumps/skip_merge.nft b/tests/shell/testcases/optimizations/dumps/skip_merge.nft
new file mode 100644
index 00000000..9c10b74b
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/skip_merge.nft
@@ -0,0 +1,23 @@
+table inet filter {
+ set udp_accepted {
+ type inet_service
+ elements = { 500, 4500 }
+ }
+
+ set tcp_accepted {
+ type inet_service
+ elements = { 80, 443 }
+ }
+
+ chain udp_input {
+ udp dport 1-128 accept
+ udp dport @udp_accepted accept
+ udp dport 53 accept
+ }
+
+ chain tcp_input {
+ tcp dport { 1-128, 8888-9999 } accept
+ tcp dport @tcp_accepted accept
+ tcp dport 1024-65535 accept
+ }
+}
diff --git a/tests/shell/testcases/optimizations/dumps/skip_non_eq.json-nft b/tests/shell/testcases/optimizations/dumps/skip_non_eq.json-nft
new file mode 100644
index 00000000..19296d02
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/skip_non_eq.json-nft
@@ -0,0 +1,108 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "eth0"
+ }
+ },
+ {
+ "match": {
+ "op": "!=",
+ "left": {
+ "meta": {
+ "key": "oifname"
+ }
+ },
+ "right": "eth0"
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "eth0"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oifname"
+ }
+ },
+ "right": "eth0"
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optimizations/dumps/skip_non_eq.nft b/tests/shell/testcases/optimizations/dumps/skip_non_eq.nft
new file mode 100644
index 00000000..6df38655
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/skip_non_eq.nft
@@ -0,0 +1,6 @@
+table inet x {
+ chain y {
+ iifname "eth0" oifname != "eth0" counter packets 0 bytes 0 accept
+ iifname "eth0" oifname "eth0" counter packets 0 bytes 0 accept
+ }
+}
diff --git a/tests/shell/testcases/optimizations/dumps/skip_unsupported.json-nft b/tests/shell/testcases/optimizations/dumps/skip_unsupported.json-nft
new file mode 100644
index 00000000..d6347b1e
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/skip_unsupported.json-nft
@@ -0,0 +1,256 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "GEOIP_CC_wan-lan_120",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "prefix": {
+ "addr": "1.32.128.0",
+ "len": 18
+ }
+ },
+ {
+ "range": [
+ "1.32.200.0",
+ "1.32.204.128"
+ ]
+ },
+ {
+ "prefix": {
+ "addr": "1.32.207.0",
+ "len": 24
+ }
+ },
+ {
+ "range": [
+ "1.32.216.118",
+ "1.32.216.255"
+ ]
+ },
+ {
+ "range": [
+ "1.32.219.0",
+ "1.32.222.255"
+ ]
+ },
+ {
+ "prefix": {
+ "addr": "1.32.226.0",
+ "len": 23
+ }
+ },
+ {
+ "prefix": {
+ "addr": "1.32.231.0",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "1.32.233.0",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "1.32.238.0",
+ "len": 23
+ }
+ },
+ {
+ "prefix": {
+ "addr": "1.32.240.0",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "223.223.220.0",
+ "len": 22
+ }
+ },
+ {
+ "prefix": {
+ "addr": "223.255.254.0",
+ "len": 24
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "1.2.3.4"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 80
+ }
+ },
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": 10
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "1.2.3.4"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 81
+ }
+ },
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": 11
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ }
+ ]
+ },
+ "right": {
+ "set": [
+ {
+ "concat": [
+ "1.2.3.5",
+ 81
+ ]
+ },
+ {
+ "concat": [
+ "1.2.3.5",
+ 82
+ ]
+ }
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optimizations/dumps/skip_unsupported.nft b/tests/shell/testcases/optimizations/dumps/skip_unsupported.nft
new file mode 100644
index 00000000..f24855e7
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/skip_unsupported.nft
@@ -0,0 +1,18 @@
+table inet x {
+ set GEOIP_CC_wan-lan_120 {
+ type ipv4_addr
+ flags interval
+ elements = { 1.32.128.0/18, 1.32.200.0-1.32.204.128,
+ 1.32.207.0/24, 1.32.216.118-1.32.216.255,
+ 1.32.219.0-1.32.222.255, 1.32.226.0/23,
+ 1.32.231.0/24, 1.32.233.0/24,
+ 1.32.238.0/23, 1.32.240.0/24,
+ 223.223.220.0/22, 223.255.254.0/24 }
+ }
+
+ chain y {
+ ip saddr 1.2.3.4 tcp dport 80 meta mark set 0x0000000a accept
+ ip saddr 1.2.3.4 tcp dport 81 meta mark set 0x0000000b accept
+ ip saddr . tcp dport { 1.2.3.5 . 81, 1.2.3.5 . 82 } accept
+ }
+}
diff --git a/tests/shell/testcases/optimizations/dumps/variables.json-nft b/tests/shell/testcases/optimizations/dumps/variables.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/variables.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optimizations/dumps/variables.nft b/tests/shell/testcases/optimizations/dumps/variables.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/optimizations/dumps/variables.nft
diff --git a/tests/shell/testcases/optimizations/merge_nat b/tests/shell/testcases/optimizations/merge_nat
new file mode 100755
index 00000000..3ffcbd57
--- /dev/null
+++ b/tests/shell/testcases/optimizations/merge_nat
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table ip test1 {
+ chain y {
+ oif lo accept
+ ip saddr 4.4.4.4 dnat to 1.1.1.1
+ ip saddr 5.5.5.5 dnat to 2.2.2.2
+ }
+}"
+
+$NFT -o -f - <<< $RULESET
+
+RULESET="table ip test2 {
+ chain y {
+ oif lo accept
+ tcp dport 80 dnat to 1.1.1.1:8001
+ tcp dport 81 dnat to 2.2.2.2:9001
+ ip saddr 10.141.11.0/24 masquerade
+ ip saddr 10.141.13.0/24 masquerade
+ }
+}"
+
+$NFT -o -f - <<< $RULESET
+
+RULESET="table ip test4 {
+ chain y {
+ oif lo accept
+ ip daddr 1.1.1.1 tcp dport 80 dnat to 4.4.4.4:8000
+ ip daddr 2.2.2.2 tcp dport 81 dnat to 3.3.3.3:9000
+ tcp dport 83 redirect to :8083
+ tcp dport 84 redirect to :8084
+ tcp dport 85 redirect
+ }
+}"
+
+$NFT -o -f - <<< $RULESET
diff --git a/tests/shell/testcases/optimizations/merge_nat_concat b/tests/shell/testcases/optimizations/merge_nat_concat
new file mode 100755
index 00000000..2e0a91a3
--- /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
diff --git a/tests/shell/testcases/optimizations/merge_nat_inet b/tests/shell/testcases/optimizations/merge_nat_inet
new file mode 100755
index 00000000..ff1916d3
--- /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
diff --git a/tests/shell/testcases/optimizations/merge_reject b/tests/shell/testcases/optimizations/merge_reject
new file mode 100755
index 00000000..c0ef9cac
--- /dev/null
+++ b/tests/shell/testcases/optimizations/merge_reject
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table ip x {
+ chain y {
+ meta l4proto tcp ip daddr 172.30.33.70 tcp dport 3306 counter packets 0 bytes 0 drop
+ meta l4proto tcp ip daddr 172.30.33.71 tcp dport 3306 counter packets 0 bytes 0 reject
+ meta l4proto tcp ip daddr 172.30.238.117 tcp dport 8080 counter packets 0 bytes 0 reject
+ meta l4proto tcp ip daddr 172.30.254.251 tcp dport 3306 counter packets 0 bytes 0 reject
+ meta l4proto tcp ip daddr 172.30.254.252 tcp dport 3306 counter packets 0 bytes 0 reject with tcp reset
+ }
+}"
+
+$NFT -o -f - <<< $RULESET
+
+RULESET="table ip6 x {
+ chain y {
+ meta l4proto tcp ip6 daddr aaaa::2 tcp dport 3306 counter packets 0 bytes 0 reject
+ meta l4proto tcp ip6 daddr aaaa::3 tcp dport 8080 counter packets 0 bytes 0 reject
+ meta l4proto tcp ip6 daddr aaaa::4 tcp dport 3306 counter packets 0 bytes 0 reject
+ meta l4proto tcp ip6 daddr aaaa::5 tcp dport 3306 counter packets 0 bytes 0 reject with tcp reset
+ }
+}"
+
+$NFT -o -f - <<< $RULESET
diff --git a/tests/shell/testcases/optimizations/merge_stmts b/tests/shell/testcases/optimizations/merge_stmts
new file mode 100755
index 00000000..ec7a9dd6
--- /dev/null
+++ b/tests/shell/testcases/optimizations/merge_stmts
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table ip x {
+ chain y {
+ ip daddr 192.168.0.1 counter accept comment "test1"
+ ip daddr 192.168.0.2 counter accept comment "test2"
+ ip daddr 192.168.0.3 counter accept comment "test3"
+ }
+}"
+
+$NFT -o -f - <<< $RULESET
diff --git a/tests/shell/testcases/optimizations/merge_stmts_concat b/tests/shell/testcases/optimizations/merge_stmts_concat
new file mode 100755
index 00000000..4db4a6f9
--- /dev/null
+++ b/tests/shell/testcases/optimizations/merge_stmts_concat
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_pipapo)
+
+set -e
+
+RULESET="table ip x {
+ chain y {
+ meta iifname eth1 ip saddr 1.1.1.1 ip daddr 2.2.2.3 accept
+ meta iifname eth1 ip saddr 1.1.1.2 ip daddr 2.2.2.4 accept
+ meta iifname eth1 ip saddr 1.1.1.2 ip daddr 2.2.3.0/24 accept
+ meta iifname eth1 ip saddr 1.1.1.2 ip daddr 2.2.4.0-2.2.4.10 accept
+ meta iifname eth2 ip saddr 1.1.1.3 ip daddr 2.2.2.5 accept
+ ip protocol . th dport { tcp . 22, udp . 67 }
+ }
+}"
+
+$NFT -o -f - <<< $RULESET
+
+RULESET="table ip x {
+ chain c1 {
+ udp dport 51820 iifname "foo" accept
+ udp dport { 67, 514 } iifname "bar" accept
+ }
+
+ chain c2 {
+ udp dport { 51820, 100 } iifname "foo" accept
+ udp dport { 67, 514 } iifname "bar" accept
+ }
+
+ chain c3 {
+ udp dport { 51820, 100 } iifname { "foo", "test" } accept
+ udp dport { 67, 514 } iifname "bar" accept
+ }
+}"
+
+$NFT -o -f - <<< $RULESET
diff --git a/tests/shell/testcases/optimizations/merge_stmts_concat_vmap b/tests/shell/testcases/optimizations/merge_stmts_concat_vmap
new file mode 100755
index 00000000..657d0aea
--- /dev/null
+++ b/tests/shell/testcases/optimizations/merge_stmts_concat_vmap
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table ip x {
+ chain x {
+ meta pkttype broadcast udp dport { 67, 547 } accept
+ meta pkttype multicast udp dport 1900 drop
+ }
+ chain y {
+ ip saddr 1.1.1.1 ip daddr 2.2.2.2 accept
+ ip saddr 4.4.4.4 ip daddr 5.5.5.5 accept
+ ip saddr 2.2.2.2 ip daddr 3.3.3.3 drop
+ }
+}"
+
+$NFT -o -f - <<< $RULESET
diff --git a/tests/shell/testcases/optimizations/merge_stmts_vmap b/tests/shell/testcases/optimizations/merge_stmts_vmap
new file mode 100755
index 00000000..e5357c0f
--- /dev/null
+++ b/tests/shell/testcases/optimizations/merge_stmts_vmap
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_set_expr)
+
+set -e
+
+RULESET="table ip x {
+ chain y {
+ ct state invalid drop
+ ct state established,related accept
+ }
+ chain z {
+ tcp dport { 1 } accept
+ tcp dport 2-3 drop
+ tcp dport 4 accept
+ }
+ chain w {
+ ip saddr 1.1.1.1 counter accept
+ ip saddr 1.1.1.2 counter drop
+ }
+}"
+
+$NFT -o -f - <<< $RULESET
diff --git a/tests/shell/testcases/optimizations/merge_vmap_raw b/tests/shell/testcases/optimizations/merge_vmap_raw
new file mode 100755
index 00000000..eb04bec3
--- /dev/null
+++ b/tests/shell/testcases/optimizations/merge_vmap_raw
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_pipapo)
+
+set -e
+
+RULESET="table inet x {
+ chain nat_dns_dnstc { meta l4proto udp redirect to :5300 ; drop ; }
+ chain nat_dns_this_5301 { meta l4proto udp redirect to :5301 ; drop ; }
+ chain nat_dns_saturn_5301 { meta nfproto ipv4 meta l4proto udp dnat to 240.0.1.2:5301 ; drop ; }
+ chain nat_dns_saturn_5302 { meta nfproto ipv4 meta l4proto udp dnat to 240.0.1.2:5302 ; drop ; }
+ chain nat_dns_saturn_5303 { meta nfproto ipv4 meta l4proto udp dnat to 240.0.1.2:5303 ; drop ; }
+
+ chain nat_dns_acme {
+ udp length 47-63 @th,160,128 0x0e373135363130333131303735353203 \
+ goto nat_dns_dnstc
+
+ udp length 62-78 @th,160,128 0x0e31393032383939353831343037320e \
+ goto nat_dns_this_5301
+
+ udp length 62-78 @th,160,128 0x0e31363436323733373931323934300e \
+ goto nat_dns_saturn_5301
+
+ udp length 62-78 @th,160,128 0x0e32393535373539353636383732310e \
+ goto nat_dns_saturn_5302
+
+ udp length 62-78 @th,160,128 0x0e38353439353637323038363633390e \
+ goto nat_dns_saturn_5303
+
+ drop
+ }
+}"
+
+$NFT -o -f - <<< $RULESET
diff --git a/tests/shell/testcases/optimizations/merge_vmaps b/tests/shell/testcases/optimizations/merge_vmaps
new file mode 100755
index 00000000..e2e4be15
--- /dev/null
+++ b/tests/shell/testcases/optimizations/merge_vmaps
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table ip x {
+ set s {
+ type ipv4_addr
+ flags dynamic
+ }
+ chain filter_in_tcp {
+ }
+ chain filter_in_udp {
+ }
+ chain y {
+ update @s { ip saddr limit rate 12/minute burst 30 packets } accept
+ tcp dport vmap {
+ 80 : accept,
+ 81 : accept,
+ 443 : accept,
+ }
+ tcp dport vmap {
+ 8000-8100 : accept,
+ 24000-25000 : accept,
+ }
+ meta l4proto tcp goto filter_in_tcp
+ meta l4proto udp goto filter_in_udp
+ log
+ }
+}"
+
+$NFT -o -f - <<< $RULESET
diff --git a/tests/shell/testcases/optimizations/not_mergeable b/tests/shell/testcases/optimizations/not_mergeable
new file mode 100755
index 00000000..ddb2f0fd
--- /dev/null
+++ b/tests/shell/testcases/optimizations/not_mergeable
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table ip x {
+ chain t1 {
+ }
+ chain t2 {
+ }
+ chain t3 {
+ }
+ chain t4 {
+ }
+ chain y {
+ counter jump t1
+ counter jump t2
+ ip version 4 jump t3
+ ip version 6 jump t4
+ }
+}"
+
+$NFT -o -f - <<< $RULESET
diff --git a/tests/shell/testcases/optimizations/ruleset b/tests/shell/testcases/optimizations/ruleset
new file mode 100755
index 00000000..2b2d80ff
--- /dev/null
+++ b/tests/shell/testcases/optimizations/ruleset
@@ -0,0 +1,170 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_prerouting_reject)
+
+RULESET="table inet uni {
+ chain gtfo {
+ reject with icmpx type host-unreachable
+ drop
+ }
+
+ chain filter_in_tcp {
+ tcp dport vmap {
+ 80 : accept,
+ 81 : accept,
+ 443 : accept,
+ 931 : accept,
+ 5001 : accept,
+ 5201 : accept,
+ }
+ tcp dport vmap {
+ 6800-6999 : accept,
+ 33434-33499 : accept,
+ }
+
+ drop
+ }
+
+ chain filter_in_udp {
+ udp dport vmap {
+ 53 : accept,
+ 123 : accept,
+ 846 : accept,
+ 849 : accept,
+ 5001 : accept,
+ 5201 : accept,
+ }
+ udp dport vmap {
+ 5300-5399 : accept,
+ 6800-6999 : accept,
+ 33434-33499 : accept,
+ }
+
+ drop
+ }
+
+ chain filter_in {
+ type filter hook input priority 0; policy drop;
+
+ ct state vmap {
+ invalid : drop,
+ established : accept,
+ related : accept,
+ untracked : accept,
+ }
+
+ ct status vmap {
+ dnat : accept,
+ snat : accept,
+ }
+
+ iif lo accept
+
+ meta iifgroup {100-199} accept
+
+ meta l4proto tcp goto filter_in_tcp
+ meta l4proto udp goto filter_in_udp
+
+ icmp type vmap {
+ echo-request : accept,
+ }
+ ip6 nexthdr icmpv6 icmpv6 type vmap {
+ echo-request : accept,
+ }
+ }
+
+ chain filter_fwd_ifgroup {
+ meta iifgroup . oifgroup vmap {
+ 100 . 10 : accept,
+ 100 . 100 : accept,
+ 100 . 101 : accept,
+ 101 . 101 : accept,
+ }
+ goto gtfo
+ }
+
+ chain filter_fwd {
+ type filter hook forward priority 0; policy drop;
+
+ fib daddr type broadcast drop
+
+ ct state vmap {
+ invalid : drop,
+ established : accept,
+ related : accept,
+ untracked : accept,
+ }
+
+ ct status vmap {
+ dnat : accept,
+ snat : accept,
+ }
+
+ meta iifgroup {100-199} goto filter_fwd_ifgroup
+ }
+
+ chain nat_fwd_tun {
+ meta l4proto tcp redirect to :15
+ udp dport 53 redirect to :13
+ goto gtfo
+ }
+
+ chain nat_dns_dnstc { meta l4proto udp redirect to :5300 ; drop ; }
+ chain nat_dns_this_5301 { meta l4proto udp redirect to :5301 ; drop ; }
+ chain nat_dns_moon_5301 { meta nfproto ipv4 meta l4proto udp dnat to 240.0.1.2:5301 ; drop ; }
+ chain nat_dns_moon_5302 { meta nfproto ipv4 meta l4proto udp dnat to 240.0.1.2:5302 ; drop ; }
+ chain nat_dns_moon_5303 { meta nfproto ipv4 meta l4proto udp dnat to 240.0.1.2:5303 ; drop ; }
+
+ chain nat_dns_acme {
+ udp length 47-63 @th,160,128 0x0e373135363130333131303735353203 \
+ goto nat_dns_dnstc
+
+ udp length 62-78 @th,160,128 0x0e31393032383939353831343037320e \
+ goto nat_dns_this_5301
+
+ udp length 62-78 @th,160,128 0x0e31363436323733373931323934300e \
+ goto nat_dns_moon_5301
+
+ udp length 62-78 @th,160,128 0x0e32393535373539353636383732310e \
+ goto nat_dns_moon_5302
+
+ udp length 62-78 @th,160,128 0x0e38353439353637323038363633390e \
+ goto nat_dns_moon_5303
+
+ drop
+ }
+
+ chain nat_prerouting {
+ type nat hook prerouting priority -100; policy accept;
+
+ iifgroup 10 udp dport 53 goto nat_dns_acme
+ iifgroup 10 accept
+
+ ip daddr 198.19.0.0/16 goto nat_fwd_tun
+ ip6 daddr fc00::/8 goto nat_fwd_tun
+
+ tcp dport 53 redirect to :25302
+ udp dport 53 redirect to :25302
+ }
+
+ chain nat_output {
+ type nat hook output priority -100; policy accept;
+
+ ip daddr 198.19.0.0/16 goto nat_fwd_tun
+ ip6 daddr fc00::/8 goto nat_fwd_tun
+ }
+
+ chain nat_postrouting {
+ type nat hook postrouting priority 100; policy accept;
+
+ oif != lo masquerade
+ }
+
+ chain mangle_forward {
+ type filter hook forward priority -150; policy accept;
+
+ tcp flags & (syn | rst) == syn tcp option maxseg size set rt mtu
+ }
+}"
+
+$NFT -o -c -f - <<< $RULESET
diff --git a/tests/shell/testcases/optimizations/single_anon_set b/tests/shell/testcases/optimizations/single_anon_set
index 7275e360..632e965f 100755
--- a/tests/shell/testcases/optimizations/single_anon_set
+++ b/tests/shell/testcases/optimizations/single_anon_set
@@ -2,12 +2,52 @@
set -e
+test -d "$NFT_TEST_TESTTMPDIR"
+
# Input file contains rules with anon sets that contain
# one element, plus extra rule with two elements (that should be
# left alone).
# Dump file has the simplified rules where anon sets have been
# replaced by equality tests where possible.
-dumpfile=$(dirname $0)/dumps/$(basename $0).nft
+file_input1="$NFT_TEST_TESTTMPDIR/input1.nft"
+
+cat <<EOF > "$file_input1"
+table ip test {
+ chain test {
+ # Test cases where anon set can be removed:
+ ip saddr { 127.0.0.1 } accept
+ iif { "lo" } accept
+
+ # negation, can change to != 22.
+ tcp dport != { 22 } drop
+
+ # single prefix, can remove anon set.
+ ip saddr { 127.0.0.0/8 } accept
+
+ # range, can remove anon set.
+ ip saddr { 127.0.0.1-192.168.7.3 } accept
+ tcp sport { 1-1023 } drop
+
+ # Test cases where anon set must be kept.
+
+ # 2 elements, cannot remove the anon set.
+ ip daddr { 192.168.7.1, 192.168.7.5 } accept
+ tcp dport { 80, 443 } accept
+
+ # single element, but concatenation which is not
+ # supported outside of set/map context at this time.
+ ip daddr . tcp dport { 192.168.0.1 . 22 } accept
+
+ # single element, but a map.
+ meta mark set ip daddr map { 192.168.0.1 : 1 }
+
+ # 2 elements. This could be converted because
+ # ct state cannot be both established and related
+ # at the same time, but this needs extra work.
+ ct state { established, related } accept
+ }
+}
+EOF
-$NFT -f "$dumpfile".input
+$NFT -f "$file_input1"
diff --git a/tests/shell/testcases/optimizations/single_anon_set_expr b/tests/shell/testcases/optimizations/single_anon_set_expr
new file mode 100755
index 00000000..81b7ceba
--- /dev/null
+++ b/tests/shell/testcases/optimizations/single_anon_set_expr
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_set_expr)
+
+set -e
+
+test -d "$NFT_TEST_TESTTMPDIR"
+
+# Input file contains rules with anon sets that contain
+# one element, plus extra rule with two elements (that should be
+# left alone).
+
+# Dump file has the simplified rules where anon sets have been
+# replaced by equality tests where possible.
+file_input1="$NFT_TEST_TESTTMPDIR/input1.nft"
+
+cat <<EOF > "$file_input1"
+table ip test {
+ chain test {
+ # with stateful statement
+ meta mark { 0x0000000a counter }
+ }
+}
+EOF
+
+$NFT -f "$file_input1"
diff --git a/tests/shell/testcases/optimizations/skip_merge b/tests/shell/testcases/optimizations/skip_merge
new file mode 100755
index 00000000..8af976ca
--- /dev/null
+++ b/tests/shell/testcases/optimizations/skip_merge
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table inet filter {
+ set udp_accepted {
+ type inet_service;
+ elements = {
+ isakmp, ipsec-nat-t
+ }
+ }
+
+ set tcp_accepted {
+ type inet_service;
+ elements = {
+ http, https
+ }
+ }
+
+ chain udp_input {
+ udp dport 1-128 accept
+ udp dport @udp_accepted accept
+ udp dport domain accept
+ }
+
+ chain tcp_input {
+ tcp dport 1-128 accept
+ tcp dport 8888-9999 accept
+ tcp dport @tcp_accepted accept
+ tcp dport 1024-65535 accept
+ }
+}"
+
+$NFT -o -f - <<< $RULESET
diff --git a/tests/shell/testcases/optimizations/skip_non_eq b/tests/shell/testcases/optimizations/skip_non_eq
new file mode 100755
index 00000000..431ed0ad
--- /dev/null
+++ b/tests/shell/testcases/optimizations/skip_non_eq
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table inet x {
+ chain y {
+ iifname "eth0" oifname != "eth0" counter packets 0 bytes 0 accept
+ iifname "eth0" oifname "eth0" counter packets 0 bytes 0 accept
+ }
+}"
+
+$NFT -o -f - <<< $RULESET
diff --git a/tests/shell/testcases/optimizations/skip_unsupported b/tests/shell/testcases/optimizations/skip_unsupported
new file mode 100755
index 00000000..6baa8280
--- /dev/null
+++ b/tests/shell/testcases/optimizations/skip_unsupported
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table inet x {
+ set GEOIP_CC_wan-lan_120 {
+ type ipv4_addr
+ flags interval
+ elements = { 1.32.128.0/18, 1.32.200.0-1.32.204.128,
+ 1.32.207.0/24, 1.32.216.118-1.32.216.255,
+ 1.32.219.0-1.32.222.255, 1.32.226.0/23,
+ 1.32.231.0/24, 1.32.233.0/24,
+ 1.32.238.0/23, 1.32.240.0/24,
+ 223.223.220.0/22, 223.255.254.0/24 }
+ }
+
+ chain y {
+ ip saddr 1.2.3.4 tcp dport 80 meta mark set 10 accept
+ ip saddr 1.2.3.4 tcp dport 81 meta mark set 11 accept
+ ip saddr 1.2.3.5 tcp dport 81 accept comment \"test\"
+ ip saddr 1.2.3.5 tcp dport 82 accept
+ }
+}"
+
+$NFT -o -f - <<< $RULESET
diff --git a/tests/shell/testcases/optimizations/variables b/tests/shell/testcases/optimizations/variables
new file mode 100755
index 00000000..fa986065
--- /dev/null
+++ b/tests/shell/testcases/optimizations/variables
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+set -e
+
+RULESET="define addrv4_vpnnet = 10.1.0.0/16
+
+table ip nat {
+ chain postrouting {
+ type nat hook postrouting priority 0; policy accept;
+
+ ip saddr \$addrv4_vpnnet counter masquerade fully-random comment \"masquerade ipv4\"
+ }
+}"
+
+$NFT -c -o -f - <<< $RULESET
diff --git a/tests/shell/testcases/optionals/comments_chain_0 b/tests/shell/testcases/optionals/comments_chain_0
index fba961c7..1a84cfa6 100755
--- a/tests/shell/testcases/optionals/comments_chain_0
+++ b/tests/shell/testcases/optionals/comments_chain_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_comment)
+
EXPECTED='table ip test_table {
chain test_chain {
comment "test"
diff --git a/tests/shell/testcases/optionals/comments_objects_0 b/tests/shell/testcases/optionals/comments_objects_0
index 7437c77b..28041ebd 100755
--- a/tests/shell/testcases/optionals/comments_objects_0
+++ b/tests/shell/testcases/optionals/comments_objects_0
@@ -1,9 +1,25 @@
#!/bin/bash
-EXPECTED='table ip filter {
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_comment)
+
+set -e
+
+COMMENT128="12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
+
+# test for pass with comment that is 128 bytes long.
+rc=0
+$NFT add table ip filter \{ quota foo1 \{ comment "\"${COMMENT128}\"" \}\; \}\; || rc="$?"
+test "$rc" = 0
+
+# test for failure with comment that is 128+1 bytes long.
+rc=0
+$NFT add table ip filter \{ quota foo2 \{ comment "\"${COMMENT128}x\"" \}\; \}\; || rc="$?"
+test "$rc" = 1
+
+RULESET='table ip filter {
quota q {
over 1200 bytes
- comment "test1"
+ comment "'"$COMMENT128"'"
}
counter c {
@@ -39,6 +55,4 @@ EXPECTED='table ip filter {
}
'
-set -e
-
-$NFT -f - <<< "$EXPECTED"
+$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/optionals/comments_table_0 b/tests/shell/testcases/optionals/comments_table_0
index a0dfd749..56bb206b 100755
--- a/tests/shell/testcases/optionals/comments_table_0
+++ b/tests/shell/testcases/optionals/comments_table_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_comment)
+
# comments are shown
$NFT add table test { comment \"test_comment\"\; }
diff --git a/tests/shell/testcases/optionals/dumps/comments_0.json-nft b/tests/shell/testcases/optionals/dumps/comments_0.json-nft
new file mode 100644
index 00000000..aef4b3e4
--- /dev/null
+++ b/tests/shell/testcases/optionals/dumps/comments_0.json-nft
@@ -0,0 +1,58 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "test",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "comment": "test_comment",
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 22
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optionals/dumps/comments_chain_0.json-nft b/tests/shell/testcases/optionals/dumps/comments_chain_0.json-nft
new file mode 100644
index 00000000..4c752e80
--- /dev/null
+++ b/tests/shell/testcases/optionals/dumps/comments_chain_0.json-nft
@@ -0,0 +1,27 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test_table",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "test_table",
+ "name": "test_chain",
+ "handle": 0,
+ "comment": "test"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optionals/dumps/comments_handles_0.json-nft b/tests/shell/testcases/optionals/dumps/comments_handles_0.json-nft
new file mode 100644
index 00000000..aef4b3e4
--- /dev/null
+++ b/tests/shell/testcases/optionals/dumps/comments_handles_0.json-nft
@@ -0,0 +1,58 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "test",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "comment": "test_comment",
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 22
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optionals/dumps/comments_objects_0.json-nft b/tests/shell/testcases/optionals/dumps/comments_objects_0.json-nft
new file mode 100644
index 00000000..b5359d8b
--- /dev/null
+++ b/tests/shell/testcases/optionals/dumps/comments_objects_0.json-nft
@@ -0,0 +1,102 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "quota": {
+ "family": "ip",
+ "name": "foo1",
+ "table": "filter",
+ "handle": 0,
+ "comment": "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678",
+ "bytes": 0,
+ "used": 0,
+ "inv": false
+ }
+ },
+ {
+ "quota": {
+ "family": "ip",
+ "name": "q",
+ "table": "filter",
+ "handle": 0,
+ "comment": "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678",
+ "bytes": 1200,
+ "used": 0,
+ "inv": true
+ }
+ },
+ {
+ "counter": {
+ "family": "ip",
+ "name": "c",
+ "table": "filter",
+ "handle": 0,
+ "comment": "test2",
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "ct helper": {
+ "family": "ip",
+ "name": "h",
+ "table": "filter",
+ "handle": 0,
+ "comment": "test3",
+ "type": "sip",
+ "protocol": "tcp",
+ "l3proto": "ip"
+ }
+ },
+ {
+ "ct expectation": {
+ "family": "ip",
+ "name": "e",
+ "table": "filter",
+ "handle": 0,
+ "comment": "test4",
+ "protocol": "tcp",
+ "dport": 666,
+ "timeout": 100,
+ "size": 96,
+ "l3proto": "ip"
+ }
+ },
+ {
+ "limit": {
+ "family": "ip",
+ "name": "l",
+ "table": "filter",
+ "handle": 0,
+ "comment": "test5",
+ "rate": 400,
+ "per": "hour",
+ "burst": 5
+ }
+ },
+ {
+ "synproxy": {
+ "family": "ip",
+ "name": "s",
+ "table": "filter",
+ "handle": 0,
+ "comment": "test6",
+ "mss": 1460,
+ "wscale": 2
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optionals/dumps/comments_objects_0.nft b/tests/shell/testcases/optionals/dumps/comments_objects_0.nft
index b760ced6..13822209 100644
--- a/tests/shell/testcases/optionals/dumps/comments_objects_0.nft
+++ b/tests/shell/testcases/optionals/dumps/comments_objects_0.nft
@@ -1,6 +1,11 @@
table ip filter {
+ quota foo1 {
+ comment "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
+ 0 bytes
+ }
+
quota q {
- comment "test1"
+ comment "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
over 1200 bytes
}
diff --git a/tests/shell/testcases/optionals/dumps/comments_objects_dup_0.json-nft b/tests/shell/testcases/optionals/dumps/comments_objects_dup_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/optionals/dumps/comments_objects_dup_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optionals/dumps/comments_objects_dup_0.nft b/tests/shell/testcases/optionals/dumps/comments_objects_dup_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/optionals/dumps/comments_objects_dup_0.nft
diff --git a/tests/shell/testcases/optionals/dumps/comments_table_0.json-nft b/tests/shell/testcases/optionals/dumps/comments_table_0.json-nft
new file mode 100644
index 00000000..8512c7de
--- /dev/null
+++ b/tests/shell/testcases/optionals/dumps/comments_table_0.json-nft
@@ -0,0 +1,19 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0,
+ "comment": "test_comment"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optionals/dumps/delete_object_handles_0.json-nft b/tests/shell/testcases/optionals/dumps/delete_object_handles_0.json-nft
new file mode 100644
index 00000000..583ce528
--- /dev/null
+++ b/tests/shell/testcases/optionals/dumps/delete_object_handles_0.json-nft
@@ -0,0 +1,67 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test-ip",
+ "handle": 0
+ }
+ },
+ {
+ "quota": {
+ "family": "ip",
+ "name": "https-quota",
+ "table": "test-ip",
+ "handle": 0,
+ "bytes": 26214400,
+ "used": 0,
+ "inv": false
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "ports",
+ "table": "test-ip",
+ "type": "inet_service",
+ "handle": 0,
+ "map": "quota"
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "test-ip6",
+ "handle": 0
+ }
+ },
+ {
+ "quota": {
+ "family": "ip6",
+ "name": "http-quota",
+ "table": "test-ip6",
+ "handle": 0,
+ "bytes": 26214400,
+ "used": 0,
+ "inv": true
+ }
+ },
+ {
+ "counter": {
+ "family": "ip6",
+ "name": "http-traffic",
+ "table": "test-ip6",
+ "handle": 0,
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optionals/dumps/delete_object_handles_0.nft b/tests/shell/testcases/optionals/dumps/delete_object_handles_0.nft
new file mode 100644
index 00000000..aac03cc5
--- /dev/null
+++ b/tests/shell/testcases/optionals/dumps/delete_object_handles_0.nft
@@ -0,0 +1,18 @@
+table ip test-ip {
+ quota https-quota {
+ 25 mbytes
+ }
+
+ map ports {
+ type inet_service : quota
+ }
+}
+table ip6 test-ip6 {
+ quota http-quota {
+ over 25 mbytes
+ }
+
+ counter http-traffic {
+ packets 0 bytes 0
+ }
+}
diff --git a/tests/shell/testcases/optionals/dumps/handles_0.json-nft b/tests/shell/testcases/optionals/dumps/handles_0.json-nft
new file mode 100644
index 00000000..ff06af30
--- /dev/null
+++ b/tests/shell/testcases/optionals/dumps/handles_0.json-nft
@@ -0,0 +1,57 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "test",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 22
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optionals/dumps/handles_1.json-nft b/tests/shell/testcases/optionals/dumps/handles_1.json-nft
new file mode 100644
index 00000000..ff06af30
--- /dev/null
+++ b/tests/shell/testcases/optionals/dumps/handles_1.json-nft
@@ -0,0 +1,57 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "test",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 22
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optionals/dumps/handles_1.nft b/tests/shell/testcases/optionals/dumps/handles_1.nft
new file mode 100644
index 00000000..085c6cf1
--- /dev/null
+++ b/tests/shell/testcases/optionals/dumps/handles_1.nft
@@ -0,0 +1,5 @@
+table ip test {
+ chain test {
+ tcp dport 22 counter packets 0 bytes 0 accept
+ }
+}
diff --git a/tests/shell/testcases/optionals/dumps/log_prefix_0.json-nft b/tests/shell/testcases/optionals/dumps/log_prefix_0.json-nft
new file mode 100644
index 00000000..161a58d4
--- /dev/null
+++ b/tests/shell/testcases/optionals/dumps/log_prefix_0.json-nft
@@ -0,0 +1,52 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": "invalid"
+ }
+ },
+ {
+ "log": {
+ "prefix": "invalid state match, logging:"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optionals/dumps/update_object_handles_0.json-nft b/tests/shell/testcases/optionals/dumps/update_object_handles_0.json-nft
new file mode 100644
index 00000000..ba78f8d7
--- /dev/null
+++ b/tests/shell/testcases/optionals/dumps/update_object_handles_0.json-nft
@@ -0,0 +1,39 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test-ip",
+ "handle": 0
+ }
+ },
+ {
+ "counter": {
+ "family": "ip",
+ "name": "traffic-counter",
+ "table": "test-ip",
+ "handle": 0,
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "quota": {
+ "family": "ip",
+ "name": "traffic-quota",
+ "table": "test-ip",
+ "handle": 0,
+ "bytes": 52428800,
+ "used": 0,
+ "inv": false
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/optionals/dumps/update_object_handles_0.nft b/tests/shell/testcases/optionals/dumps/update_object_handles_0.nft
new file mode 100644
index 00000000..f391b631
--- /dev/null
+++ b/tests/shell/testcases/optionals/dumps/update_object_handles_0.nft
@@ -0,0 +1,9 @@
+table ip test-ip {
+ counter traffic-counter {
+ packets 0 bytes 0
+ }
+
+ quota traffic-quota {
+ 50 mbytes
+ }
+}
diff --git a/tests/shell/testcases/optionals/update_object_handles_0 b/tests/shell/testcases/optionals/update_object_handles_0
index 8b12b8c5..ccd96779 100755
--- a/tests/shell/testcases/optionals/update_object_handles_0
+++ b/tests/shell/testcases/optionals/update_object_handles_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_stateful_object_update)
+
set -e
$NFT add table test-ip
$NFT add counter test-ip traffic-counter
diff --git a/tests/shell/testcases/owner/0001-flowtable-uaf b/tests/shell/testcases/owner/0001-flowtable-uaf
new file mode 100755
index 00000000..c07e8d6a
--- /dev/null
+++ b/tests/shell/testcases/owner/0001-flowtable-uaf
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_table_flag_owner)
+
+set -e
+
+$NFT -f - <<EOF
+table t {
+ flags owner
+ flowtable f {
+ hook ingress priority 0
+ devices = { lo }
+ }
+}
+EOF
+
+# trigger uaf.
+$NFT -f - <<EOF
+table t {
+ flags owner
+ flowtable f {
+ hook ingress priority 0
+ devices = { lo }
+ }
+}
+EOF
diff --git a/tests/shell/testcases/owner/dumps/0001-flowtable-uaf.json-nft b/tests/shell/testcases/owner/dumps/0001-flowtable-uaf.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/owner/dumps/0001-flowtable-uaf.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/owner/dumps/0001-flowtable-uaf.nft b/tests/shell/testcases/owner/dumps/0001-flowtable-uaf.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/owner/dumps/0001-flowtable-uaf.nft
diff --git a/tests/shell/testcases/owner/dumps/0002-persist.json-nft b/tests/shell/testcases/owner/dumps/0002-persist.json-nft
new file mode 100644
index 00000000..f0c336a8
--- /dev/null
+++ b/tests/shell/testcases/owner/dumps/0002-persist.json-nft
@@ -0,0 +1,19 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0,
+ "flags": "persist"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/owner/dumps/0002-persist.nft b/tests/shell/testcases/owner/dumps/0002-persist.nft
new file mode 100644
index 00000000..b47027d3
--- /dev/null
+++ b/tests/shell/testcases/owner/dumps/0002-persist.nft
@@ -0,0 +1,3 @@
+table ip t {
+ flags persist
+}
diff --git a/tests/shell/testcases/packetpath/dumps/payload.nodump b/tests/shell/testcases/packetpath/dumps/payload.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/packetpath/dumps/payload.nodump
diff --git a/tests/shell/testcases/packetpath/dumps/set_lookups.json-nft b/tests/shell/testcases/packetpath/dumps/set_lookups.json-nft
new file mode 100644
index 00000000..24363f90
--- /dev/null
+++ b/tests/shell/testcases/packetpath/dumps/set_lookups.json-nft
@@ -0,0 +1,674 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "t",
+ "type": [
+ "ipv4_addr",
+ "iface_index"
+ ],
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "concat": [
+ "127.0.0.1",
+ "lo"
+ ]
+ },
+ {
+ "concat": [
+ "127.0.0.2",
+ "lo"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s2",
+ "table": "t",
+ "type": [
+ "ipv4_addr",
+ "iface_index"
+ ],
+ "handle": 0,
+ "elem": [
+ {
+ "concat": [
+ "127.0.0.1",
+ "lo"
+ ]
+ },
+ {
+ "concat": [
+ "127.0.0.2",
+ "lo"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s3",
+ "table": "t",
+ "type": "iface_index",
+ "handle": 0,
+ "elem": [
+ "lo"
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s4",
+ "table": "t",
+ "type": "iface_index",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ "lo"
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "nomatch",
+ "table": "t",
+ "type": [
+ "ipv4_addr",
+ "iface_index"
+ ],
+ "handle": 0,
+ "elem": [
+ {
+ "concat": [
+ "127.0.0.3",
+ "lo"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "nomatch2",
+ "table": "t",
+ "type": [
+ "ipv4_addr",
+ "iface_index"
+ ],
+ "handle": 0,
+ "elem": [
+ {
+ "concat": [
+ "127.0.0.2",
+ "90000"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "icmp",
+ "field": "type"
+ }
+ },
+ "right": "echo-request"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "meta": {
+ "key": "iif"
+ }
+ }
+ ]
+ },
+ "right": "@s"
+ }
+ },
+ {
+ "counter": {
+ "packets": 1,
+ "bytes": 84
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "icmp",
+ "field": "type"
+ }
+ },
+ "right": "echo-request"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "lo"
+ ]
+ },
+ "right": "@s"
+ }
+ },
+ {
+ "counter": {
+ "packets": 1,
+ "bytes": 84
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "icmp",
+ "field": "type"
+ }
+ },
+ "right": "echo-request"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "lo"
+ ]
+ },
+ "right": "@s"
+ }
+ },
+ {
+ "counter": {
+ "packets": 1,
+ "bytes": 84
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "icmp",
+ "field": "type"
+ }
+ },
+ "right": "echo-request"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "meta": {
+ "key": "iif"
+ }
+ }
+ ]
+ },
+ "right": "@s2"
+ }
+ },
+ {
+ "counter": {
+ "packets": 1,
+ "bytes": 84
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "icmp",
+ "field": "type"
+ }
+ },
+ "right": "echo-request"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "lo"
+ ]
+ },
+ "right": "@s2"
+ }
+ },
+ {
+ "counter": {
+ "packets": 1,
+ "bytes": 84
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "icmp",
+ "field": "type"
+ }
+ },
+ "right": "echo-request"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "lo"
+ ]
+ },
+ "right": "@s2"
+ }
+ },
+ {
+ "counter": {
+ "packets": 1,
+ "bytes": 84
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "icmp",
+ "field": "type"
+ }
+ },
+ "right": "echo-request"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "lo"
+ ]
+ },
+ "right": "@s"
+ }
+ },
+ {
+ "counter": {
+ "packets": 1,
+ "bytes": 84
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "icmp",
+ "field": "type"
+ }
+ },
+ "right": "echo-request"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "lo"
+ ]
+ },
+ "right": "@s2"
+ }
+ },
+ {
+ "counter": {
+ "packets": 1,
+ "bytes": 84
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "icmp",
+ "field": "type"
+ }
+ },
+ "right": "echo-request"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iif"
+ }
+ },
+ "right": "@s3"
+ }
+ },
+ {
+ "counter": {
+ "packets": 1,
+ "bytes": 84
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "icmp",
+ "field": "type"
+ }
+ },
+ "right": "echo-request"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iif"
+ }
+ },
+ "right": "@s4"
+ }
+ },
+ {
+ "counter": {
+ "packets": 1,
+ "bytes": 84
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "lo"
+ ]
+ },
+ "right": "@nomatch"
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ {
+ "meta": {
+ "key": "iif"
+ }
+ }
+ ]
+ },
+ "right": "@nomatch2"
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ }
+ ]
+}
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 00000000..7566f557
--- /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/dumps/tcp_options.nodump b/tests/shell/testcases/packetpath/dumps/tcp_options.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/packetpath/dumps/tcp_options.nodump
diff --git a/tests/shell/testcases/packetpath/dumps/vlan_8021ad_tag.nodump b/tests/shell/testcases/packetpath/dumps/vlan_8021ad_tag.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/packetpath/dumps/vlan_8021ad_tag.nodump
diff --git a/tests/shell/testcases/packetpath/flowtables b/tests/shell/testcases/packetpath/flowtables
new file mode 100755
index 00000000..ec7dfeb7
--- /dev/null
+++ b/tests/shell/testcases/packetpath/flowtables
@@ -0,0 +1,96 @@
+#! /bin/bash -x
+
+# NFT_TEST_SKIP(NFT_TEST_SKIP_slow)
+
+rnd=$(mktemp -u XXXXXXXX)
+R="flowtable-router-$rnd"
+C="flowtable-client-$rnd"
+S="flowtbale-server-$rnd"
+
+cleanup()
+{
+ for i in $R $C $S;do
+ kill $(ip netns pid $i) 2>/dev/null
+ ip netns del $i
+ done
+}
+
+trap cleanup EXIT
+
+ip netns add $R
+ip netns add $S
+ip netns add $C
+
+ip link add s_r netns $S type veth peer name r_s netns $R
+ip netns exec $S ip link set s_r up
+ip netns exec $R ip link set r_s up
+ip link add c_r netns $C type veth peer name r_c netns $R
+ip netns exec $R ip link set r_c up
+ip netns exec $C ip link set c_r up
+
+ip netns exec $S ip -6 addr add 2001:db8:ffff:22::1/64 dev s_r
+ip netns exec $C ip -6 addr add 2001:db8:ffff:21::2/64 dev c_r
+ip netns exec $R ip -6 addr add 2001:db8:ffff:22::fffe/64 dev r_s
+ip netns exec $R ip -6 addr add 2001:db8:ffff:21::fffe/64 dev r_c
+ip netns exec $R sysctl -w net.ipv6.conf.all.forwarding=1
+ip netns exec $C ip route add 2001:db8:ffff:22::/64 via 2001:db8:ffff:21::fffe dev c_r
+ip netns exec $S ip route add 2001:db8:ffff:21::/64 via 2001:db8:ffff:22::fffe dev s_r
+ip netns exec $S ethtool -K s_r tso off
+ip netns exec $C ethtool -K c_r tso off
+
+sleep 3
+ip netns exec $C ping -6 2001:db8:ffff:22::1 -c1 || exit 1
+
+ip netns exec $R nft -f - <<EOF
+table ip6 filter {
+ flowtable f1 {
+ hook ingress priority -100
+ devices = { r_c, r_s }
+ }
+
+ chain forward {
+ type filter hook forward priority filter; policy accept;
+ ip6 nexthdr tcp ct state established,related counter packets 0 bytes 0 flow add @f1 counter packets 0 bytes 0
+ ip6 nexthdr tcp ct state invalid counter packets 0 bytes 0 drop
+ tcp flags fin,rst counter packets 0 bytes 0 accept
+ meta l4proto tcp meta length < 100 counter packets 0 bytes 0 accept
+ ip6 nexthdr tcp counter packets 0 bytes 0 log drop
+ }
+}
+EOF
+
+if [ ! -r /proc/net/nf_conntrack ]
+then
+ echo "E: nf_conntrack unreadable, skipping" >&2
+ exit 77
+fi
+
+ip netns exec $R nft list ruleset
+ip netns exec $R sysctl -w net.netfilter.nf_flowtable_tcp_timeout=5 || {
+ echo "E: set net.netfilter.nf_flowtable_tcp_timeout fail, skipping" >&2
+ exit 77
+}
+ip netns exec $R sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=86400 || {
+ echo "E: set net.netfilter.nf_conntrack_tcp_timeout_established fail, skipping" >&2
+ exit 77
+
+}
+
+# A trick to control the timing to send a packet
+ip netns exec $S socat TCP6-LISTEN:10001 GOPEN:/tmp/pipefile-$rnd,ignoreeof &
+sleep 1
+ip netns exec $C socat -b 2048 PIPE:/tmp/pipefile-$rnd 'TCP:[2001:db8:ffff:22::1]:10001' &
+sleep 1
+ip netns exec $R grep 'OFFLOAD' /proc/net/nf_conntrack || { echo "check [OFFLOAD] tag (failed)"; exit 1; }
+ip netns exec $R cat /proc/net/nf_conntrack
+sleep 6
+ip netns exec $R grep 'OFFLOAD' /proc/net/nf_conntrack && { echo "CT OFFLOAD timeout, fail back to classical path (failed)"; exit 1; }
+ip netns exec $R grep '8639[0-9]' /proc/net/nf_conntrack || { echo "check nf_conntrack_tcp_timeout_established (failed)"; exit 1; }
+ip netns exec $C echo "send sth" >> /tmp/pipefile-$rnd
+ip netns exec $R grep 'OFFLOAD' /proc/net/nf_conntrack || { echo "traffic seen, back to OFFLOAD path (failed)"; exit 1; }
+ip netns exec $C sleep 3
+ip netns exec $C echo "send sth" >> /tmp/pipefile-$rnd
+ip netns exec $C sleep 3
+ip netns exec $R grep 'OFFLOAD' /proc/net/nf_conntrack || { echo "Traffic seen in 5s (nf_flowtable_tcp_timeout), so stay in OFFLOAD (failed)"; exit 1; }
+
+exit 0
diff --git a/tests/shell/testcases/packetpath/payload b/tests/shell/testcases/packetpath/payload
new file mode 100755
index 00000000..4c5c42da
--- /dev/null
+++ b/tests/shell/testcases/packetpath/payload
@@ -0,0 +1,182 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_netdev_egress)
+
+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
diff --git a/tests/shell/testcases/packetpath/set_lookups b/tests/shell/testcases/packetpath/set_lookups
new file mode 100755
index 00000000..85159858
--- /dev/null
+++ b/tests/shell/testcases/packetpath/set_lookups
@@ -0,0 +1,66 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_pipapo)
+
+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/packetpath/tcp_options b/tests/shell/testcases/packetpath/tcp_options
new file mode 100755
index 00000000..57e228c5
--- /dev/null
+++ b/tests/shell/testcases/packetpath/tcp_options
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_reset_tcp_options)
+
+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 & (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
+
+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
diff --git a/tests/shell/testcases/packetpath/vlan_8021ad_tag b/tests/shell/testcases/packetpath/vlan_8021ad_tag
new file mode 100755
index 00000000..379a5710
--- /dev/null
+++ b/tests/shell/testcases/packetpath/vlan_8021ad_tag
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+rnd=$(mktemp -u XXXXXXXX)
+ns1="nft1ifname-$rnd"
+ns2="nft2ifname-$rnd"
+
+cleanup()
+{
+ ip netns del "$ns1"
+ ip netns del "$ns2"
+}
+
+trap cleanup EXIT
+
+set -e
+
+ip netns add "$ns1"
+ip netns add "$ns2"
+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 addr da:d3:00:01:02:03
+
+ip -net "$ns1" link add vlan123 link veth0 type vlan id 123 proto 802.1ad
+ip -net "$ns2" link add vlan123 link veth0 type vlan id 123 proto 802.1ad
+
+
+for dev in veth0 vlan123; do
+ ip -net "$ns1" link set $dev up
+ ip -net "$ns2" link set $dev up
+done
+
+ip -net "$ns1" addr add 10.1.1.1/24 dev vlan123
+ip -net "$ns2" addr add 10.1.1.2/24 dev vlan123
+
+ip netns exec "$ns2" $NFT -f /dev/stdin <<"EOF"
+table netdev t {
+ chain c {
+ type filter hook ingress device veth0 priority filter;
+ ether saddr da:d3:00:01:02:03 ether type 8021ad vlan id 123 ip daddr 10.1.1.2 icmp type echo-request counter
+ }
+}
+EOF
+
+ip netns exec "$ns1" ping -c 1 10.1.1.2
+
+ip netns exec "$ns2" $NFT list ruleset
+ip netns exec "$ns2" $NFT list chain netdev t c | grep 'counter packets 1 bytes 84'
diff --git a/tests/shell/testcases/parsing/describe b/tests/shell/testcases/parsing/describe
new file mode 100755
index 00000000..2ee072e8
--- /dev/null
+++ b/tests/shell/testcases/parsing/describe
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+errmsg='Error: unknown ip option type/field'
+
+str=$($NFT describe ip option rr value 2>&1 | head -n 1)
+
+[ "$str" = "$errmsg" ] && exit 0
diff --git a/tests/shell/testcases/parsing/dumps/describe.json-nft b/tests/shell/testcases/parsing/dumps/describe.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/parsing/dumps/describe.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/parsing/dumps/describe.nft b/tests/shell/testcases/parsing/dumps/describe.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/parsing/dumps/describe.nft
diff --git a/tests/shell/testcases/parsing/dumps/large_rule_pipe.json-nft b/tests/shell/testcases/parsing/dumps/large_rule_pipe.json-nft
new file mode 100644
index 00000000..bf5dc65f
--- /dev/null
+++ b/tests/shell/testcases/parsing/dumps/large_rule_pipe.json-nft
@@ -0,0 +1,4079 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "firewalld",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PREROUTING",
+ "handle": 0,
+ "type": "nat",
+ "hook": "prerouting",
+ "prio": -90,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PREROUTING_ZONES_SOURCE",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PREROUTING_ZONES",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POSTROUTING",
+ "handle": 0,
+ "type": "nat",
+ "hook": "postrouting",
+ "prio": 110,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POSTROUTING_ZONES_SOURCE",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POSTROUTING_ZONES",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_public",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_public_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_public_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_public_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_public",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_public_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_public_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_public_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_home",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_home_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_home_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_home_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_home",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_home_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_home_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_home_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_work",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_work_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_work_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_work_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_work",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_work_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_work_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_work_allow",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PREROUTING",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PREROUTING_ZONES_SOURCE"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PREROUTING",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PREROUTING_ZONES"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PREROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "enp0s25"
+ }
+ },
+ {
+ "goto": {
+ "target": "nat_PRE_home"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PREROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "goto": {
+ "target": "nat_PRE_public"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POSTROUTING",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POSTROUTING_ZONES_SOURCE"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POSTROUTING",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POSTROUTING_ZONES"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POSTROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oifname"
+ }
+ },
+ "right": "enp0s25"
+ }
+ },
+ {
+ "goto": {
+ "target": "nat_POST_home"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POSTROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "goto": {
+ "target": "nat_POST_public"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_public_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_public_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_public_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_public_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_public_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_public_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_home_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_home_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_home_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_home_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_home_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_home_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_work_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_work_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_work_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_work_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_work_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_work_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "firewalld",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PREROUTING",
+ "handle": 0,
+ "type": "nat",
+ "hook": "prerouting",
+ "prio": -90,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PREROUTING_ZONES_SOURCE",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PREROUTING_ZONES",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POSTROUTING",
+ "handle": 0,
+ "type": "nat",
+ "hook": "postrouting",
+ "prio": 110,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POSTROUTING_ZONES_SOURCE",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POSTROUTING_ZONES",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_public",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_public_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_public_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_public_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_public",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_public_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_public_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_public_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_home",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_home_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_home_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_home_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_home",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_home_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_home_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_home_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_work",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_work_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_work_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_work_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_work",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_work_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_work_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_work_allow",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PREROUTING",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PREROUTING_ZONES_SOURCE"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PREROUTING",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PREROUTING_ZONES"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PREROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "enp0s25"
+ }
+ },
+ {
+ "goto": {
+ "target": "nat_PRE_home"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PREROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "goto": {
+ "target": "nat_PRE_public"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POSTROUTING",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POSTROUTING_ZONES_SOURCE"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POSTROUTING",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POSTROUTING_ZONES"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POSTROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oifname"
+ }
+ },
+ "right": "enp0s25"
+ }
+ },
+ {
+ "goto": {
+ "target": "nat_POST_home"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POSTROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "goto": {
+ "target": "nat_POST_public"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_public_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_public_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_public_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_public_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_public_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_public_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_home_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_home_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_home_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_home_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_home_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_home_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_work_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_work_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_work_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_work_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_work_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_work_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "firewalld",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PREROUTING",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -290,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PREROUTING_ZONES_SOURCE",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PREROUTING_ZONES",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PREROUTING",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -140,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PREROUTING_ZONES_SOURCE",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PREROUTING_ZONES",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_INPUT",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FORWARD",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_INPUT_ZONES_SOURCE",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_INPUT_ZONES",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FORWARD_IN_ZONES_SOURCE",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FORWARD_IN_ZONES",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FORWARD_OUT_ZONES_SOURCE",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FORWARD_OUT_ZONES",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_public",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_public_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_public_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_public_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_public",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_public_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_public_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_public_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_public",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_public_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_public_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_public_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_public",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_public_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_public_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_public_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_public",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_public_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_public_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_public_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_home",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_home_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_home_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_home_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_home",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_home_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_home_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_home_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_home",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_home_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_home_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_home_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_home",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_home_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_home_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_home_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_home",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_home_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_home_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_home_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_work",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_work_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_work_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_work_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_work",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_work_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_work_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_work_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_work",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_work_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_work_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_work_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_work",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_work_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_work_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_work_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_work",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_work_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_work_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_work_allow",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PREROUTING",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "icmpv6",
+ "field": "type"
+ }
+ },
+ "right": {
+ "set": [
+ "nd-router-advert",
+ "nd-neighbor-solicit"
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PREROUTING",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "nfproto"
+ }
+ },
+ "right": "ipv6"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "fib": {
+ "result": "oif",
+ "flags": [
+ "saddr",
+ "iif"
+ ]
+ }
+ },
+ "right": false
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PREROUTING",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PREROUTING_ZONES_SOURCE"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PREROUTING",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PREROUTING_ZONES"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PREROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "enp0s25"
+ }
+ },
+ {
+ "goto": {
+ "target": "raw_PRE_home"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PREROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "goto": {
+ "target": "raw_PRE_public"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PREROUTING",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PREROUTING_ZONES_SOURCE"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PREROUTING",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PREROUTING_ZONES"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PREROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "enp0s25"
+ }
+ },
+ {
+ "goto": {
+ "target": "mangle_PRE_home"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PREROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "goto": {
+ "target": "mangle_PRE_public"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_INPUT",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": [
+ "established",
+ "related"
+ ]
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_INPUT",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "lo"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_INPUT",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_INPUT_ZONES_SOURCE"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_INPUT",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_INPUT_ZONES"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_INPUT",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": "invalid"
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_INPUT",
+ "handle": 0,
+ "expr": [
+ {
+ "reject": {
+ "type": "icmpx",
+ "expr": "admin-prohibited"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": [
+ "established",
+ "related"
+ ]
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "lo"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FORWARD_IN_ZONES_SOURCE"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FORWARD_IN_ZONES"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FORWARD_OUT_ZONES_SOURCE"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FORWARD_OUT_ZONES"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": "invalid"
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD",
+ "handle": 0,
+ "expr": [
+ {
+ "reject": {
+ "type": "icmpx",
+ "expr": "admin-prohibited"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_INPUT_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "enp0s25"
+ }
+ },
+ {
+ "goto": {
+ "target": "filter_IN_home"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_INPUT_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "goto": {
+ "target": "filter_IN_public"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD_IN_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "enp0s25"
+ }
+ },
+ {
+ "goto": {
+ "target": "filter_FWDI_home"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD_IN_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "goto": {
+ "target": "filter_FWDI_public"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD_OUT_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oifname"
+ }
+ },
+ "right": "enp0s25"
+ }
+ },
+ {
+ "goto": {
+ "target": "filter_FWDO_home"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD_OUT_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "goto": {
+ "target": "filter_FWDO_public"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_public_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_public_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_public_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_public_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_public_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_public_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_public",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "right": {
+ "set": [
+ "icmp",
+ "ipv6-icmp"
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_public_allow",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 22
+ }
+ },
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": [
+ "new",
+ "untracked"
+ ]
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_public_allow",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip6",
+ "field": "daddr"
+ }
+ },
+ "right": {
+ "prefix": {
+ "addr": "fe80::",
+ "len": 64
+ }
+ }
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 546
+ }
+ },
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": [
+ "new",
+ "untracked"
+ ]
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_public_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_public_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_public_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_public",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "right": {
+ "set": [
+ "icmp",
+ "ipv6-icmp"
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_public_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_public_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_public_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_public_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_public_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_public_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_home_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_home_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_home_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_home_allow",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 137
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "ct": {
+ "key": "helper"
+ }
+ },
+ "right": "netbios-ns"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_home_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_home_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_home_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_home",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "right": {
+ "set": [
+ "icmp",
+ "ipv6-icmp"
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_home_allow",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 22
+ }
+ },
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": [
+ "new",
+ "untracked"
+ ]
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_home_allow",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "right": "224.0.0.251"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 5353
+ }
+ },
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": [
+ "new",
+ "untracked"
+ ]
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_home_allow",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip6",
+ "field": "daddr"
+ }
+ },
+ "right": "ff02::fb"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 5353
+ }
+ },
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": [
+ "new",
+ "untracked"
+ ]
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_home_allow",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": {
+ "range": [
+ 1714,
+ 1764
+ ]
+ }
+ }
+ },
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": [
+ "new",
+ "untracked"
+ ]
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_home_allow",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": {
+ "range": [
+ 1714,
+ 1764
+ ]
+ }
+ }
+ },
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": [
+ "new",
+ "untracked"
+ ]
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_home_allow",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip6",
+ "field": "daddr"
+ }
+ },
+ "right": {
+ "prefix": {
+ "addr": "fe80::",
+ "len": 64
+ }
+ }
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 546
+ }
+ },
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": [
+ "new",
+ "untracked"
+ ]
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_home_allow",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 137
+ }
+ },
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": [
+ "new",
+ "untracked"
+ ]
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_home_allow",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 138
+ }
+ },
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": [
+ "new",
+ "untracked"
+ ]
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_home_allow",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 139
+ }
+ },
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": [
+ "new",
+ "untracked"
+ ]
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_home_allow",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 445
+ }
+ },
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": [
+ "new",
+ "untracked"
+ ]
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_home_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_home_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_home_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_home",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "right": {
+ "set": [
+ "icmp",
+ "ipv6-icmp"
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_home_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_home_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_home_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_home_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_home_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_home",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_home_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_work_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_work_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_work_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_work_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_work_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_work_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_work",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "right": {
+ "set": [
+ "icmp",
+ "ipv6-icmp"
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_work_allow",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 22
+ }
+ },
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": [
+ "new",
+ "untracked"
+ ]
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_work_allow",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip6",
+ "field": "daddr"
+ }
+ },
+ "right": {
+ "prefix": {
+ "addr": "fe80::",
+ "len": 64
+ }
+ }
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 546
+ }
+ },
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": [
+ "new",
+ "untracked"
+ ]
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_work_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_work_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_work_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_work",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "right": {
+ "set": [
+ "icmp",
+ "ipv6-icmp"
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_work_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_work_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_work_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_work_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_work_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_work_allow"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/parsing/dumps/large_rule_pipe.nft b/tests/shell/testcases/parsing/dumps/large_rule_pipe.nft
new file mode 100644
index 00000000..15832752
--- /dev/null
+++ b/tests/shell/testcases/parsing/dumps/large_rule_pipe.nft
@@ -0,0 +1,561 @@
+table ip firewalld {
+ chain nat_PREROUTING {
+ type nat hook prerouting priority dstnat + 10; policy accept;
+ jump nat_PREROUTING_ZONES_SOURCE
+ jump nat_PREROUTING_ZONES
+ }
+
+ chain nat_PREROUTING_ZONES_SOURCE {
+ }
+
+ chain nat_PREROUTING_ZONES {
+ iifname "enp0s25" goto nat_PRE_home
+ goto nat_PRE_public
+ }
+
+ chain nat_POSTROUTING {
+ type nat hook postrouting priority srcnat + 10; policy accept;
+ jump nat_POSTROUTING_ZONES_SOURCE
+ jump nat_POSTROUTING_ZONES
+ }
+
+ chain nat_POSTROUTING_ZONES_SOURCE {
+ }
+
+ chain nat_POSTROUTING_ZONES {
+ oifname "enp0s25" goto nat_POST_home
+ goto nat_POST_public
+ }
+
+ chain nat_PRE_public {
+ jump nat_PRE_public_log
+ jump nat_PRE_public_deny
+ jump nat_PRE_public_allow
+ }
+
+ chain nat_PRE_public_log {
+ }
+
+ chain nat_PRE_public_deny {
+ }
+
+ chain nat_PRE_public_allow {
+ }
+
+ chain nat_POST_public {
+ jump nat_POST_public_log
+ jump nat_POST_public_deny
+ jump nat_POST_public_allow
+ }
+
+ chain nat_POST_public_log {
+ }
+
+ chain nat_POST_public_deny {
+ }
+
+ chain nat_POST_public_allow {
+ }
+
+ chain nat_PRE_home {
+ jump nat_PRE_home_log
+ jump nat_PRE_home_deny
+ jump nat_PRE_home_allow
+ }
+
+ chain nat_PRE_home_log {
+ }
+
+ chain nat_PRE_home_deny {
+ }
+
+ chain nat_PRE_home_allow {
+ }
+
+ chain nat_POST_home {
+ jump nat_POST_home_log
+ jump nat_POST_home_deny
+ jump nat_POST_home_allow
+ }
+
+ chain nat_POST_home_log {
+ }
+
+ chain nat_POST_home_deny {
+ }
+
+ chain nat_POST_home_allow {
+ }
+
+ chain nat_PRE_work {
+ jump nat_PRE_work_log
+ jump nat_PRE_work_deny
+ jump nat_PRE_work_allow
+ }
+
+ chain nat_PRE_work_log {
+ }
+
+ chain nat_PRE_work_deny {
+ }
+
+ chain nat_PRE_work_allow {
+ }
+
+ chain nat_POST_work {
+ jump nat_POST_work_log
+ jump nat_POST_work_deny
+ jump nat_POST_work_allow
+ }
+
+ chain nat_POST_work_log {
+ }
+
+ chain nat_POST_work_deny {
+ }
+
+ chain nat_POST_work_allow {
+ }
+}
+table ip6 firewalld {
+ chain nat_PREROUTING {
+ type nat hook prerouting priority dstnat + 10; policy accept;
+ jump nat_PREROUTING_ZONES_SOURCE
+ jump nat_PREROUTING_ZONES
+ }
+
+ chain nat_PREROUTING_ZONES_SOURCE {
+ }
+
+ chain nat_PREROUTING_ZONES {
+ iifname "enp0s25" goto nat_PRE_home
+ goto nat_PRE_public
+ }
+
+ chain nat_POSTROUTING {
+ type nat hook postrouting priority srcnat + 10; policy accept;
+ jump nat_POSTROUTING_ZONES_SOURCE
+ jump nat_POSTROUTING_ZONES
+ }
+
+ chain nat_POSTROUTING_ZONES_SOURCE {
+ }
+
+ chain nat_POSTROUTING_ZONES {
+ oifname "enp0s25" goto nat_POST_home
+ goto nat_POST_public
+ }
+
+ chain nat_PRE_public {
+ jump nat_PRE_public_log
+ jump nat_PRE_public_deny
+ jump nat_PRE_public_allow
+ }
+
+ chain nat_PRE_public_log {
+ }
+
+ chain nat_PRE_public_deny {
+ }
+
+ chain nat_PRE_public_allow {
+ }
+
+ chain nat_POST_public {
+ jump nat_POST_public_log
+ jump nat_POST_public_deny
+ jump nat_POST_public_allow
+ }
+
+ chain nat_POST_public_log {
+ }
+
+ chain nat_POST_public_deny {
+ }
+
+ chain nat_POST_public_allow {
+ }
+
+ chain nat_PRE_home {
+ jump nat_PRE_home_log
+ jump nat_PRE_home_deny
+ jump nat_PRE_home_allow
+ }
+
+ chain nat_PRE_home_log {
+ }
+
+ chain nat_PRE_home_deny {
+ }
+
+ chain nat_PRE_home_allow {
+ }
+
+ chain nat_POST_home {
+ jump nat_POST_home_log
+ jump nat_POST_home_deny
+ jump nat_POST_home_allow
+ }
+
+ chain nat_POST_home_log {
+ }
+
+ chain nat_POST_home_deny {
+ }
+
+ chain nat_POST_home_allow {
+ }
+
+ chain nat_PRE_work {
+ jump nat_PRE_work_log
+ jump nat_PRE_work_deny
+ jump nat_PRE_work_allow
+ }
+
+ chain nat_PRE_work_log {
+ }
+
+ chain nat_PRE_work_deny {
+ }
+
+ chain nat_PRE_work_allow {
+ }
+
+ chain nat_POST_work {
+ jump nat_POST_work_log
+ jump nat_POST_work_deny
+ jump nat_POST_work_allow
+ }
+
+ chain nat_POST_work_log {
+ }
+
+ chain nat_POST_work_deny {
+ }
+
+ chain nat_POST_work_allow {
+ }
+}
+table inet firewalld {
+ chain raw_PREROUTING {
+ type filter hook prerouting priority raw + 10; policy accept;
+ icmpv6 type { nd-router-advert, nd-neighbor-solicit } accept
+ meta nfproto ipv6 fib saddr . iif oif missing drop
+ jump raw_PREROUTING_ZONES_SOURCE
+ jump raw_PREROUTING_ZONES
+ }
+
+ chain raw_PREROUTING_ZONES_SOURCE {
+ }
+
+ chain raw_PREROUTING_ZONES {
+ iifname "enp0s25" goto raw_PRE_home
+ goto raw_PRE_public
+ }
+
+ chain mangle_PREROUTING {
+ type filter hook prerouting priority mangle + 10; policy accept;
+ jump mangle_PREROUTING_ZONES_SOURCE
+ jump mangle_PREROUTING_ZONES
+ }
+
+ chain mangle_PREROUTING_ZONES_SOURCE {
+ }
+
+ chain mangle_PREROUTING_ZONES {
+ iifname "enp0s25" goto mangle_PRE_home
+ goto mangle_PRE_public
+ }
+
+ chain filter_INPUT {
+ type filter hook input priority filter + 10; policy accept;
+ ct state established,related accept
+ iifname "lo" accept
+ jump filter_INPUT_ZONES_SOURCE
+ jump filter_INPUT_ZONES
+ ct state invalid drop
+ reject with icmpx admin-prohibited
+ }
+
+ chain filter_FORWARD {
+ type filter hook forward priority filter + 10; policy accept;
+ ct state established,related accept
+ iifname "lo" accept
+ jump filter_FORWARD_IN_ZONES_SOURCE
+ jump filter_FORWARD_IN_ZONES
+ jump filter_FORWARD_OUT_ZONES_SOURCE
+ jump filter_FORWARD_OUT_ZONES
+ ct state invalid drop
+ reject with icmpx admin-prohibited
+ }
+
+ chain filter_INPUT_ZONES_SOURCE {
+ }
+
+ chain filter_INPUT_ZONES {
+ iifname "enp0s25" goto filter_IN_home
+ goto filter_IN_public
+ }
+
+ chain filter_FORWARD_IN_ZONES_SOURCE {
+ }
+
+ chain filter_FORWARD_IN_ZONES {
+ iifname "enp0s25" goto filter_FWDI_home
+ goto filter_FWDI_public
+ }
+
+ chain filter_FORWARD_OUT_ZONES_SOURCE {
+ }
+
+ chain filter_FORWARD_OUT_ZONES {
+ oifname "enp0s25" goto filter_FWDO_home
+ goto filter_FWDO_public
+ }
+
+ chain raw_PRE_public {
+ jump raw_PRE_public_log
+ jump raw_PRE_public_deny
+ jump raw_PRE_public_allow
+ }
+
+ chain raw_PRE_public_log {
+ }
+
+ chain raw_PRE_public_deny {
+ }
+
+ chain raw_PRE_public_allow {
+ }
+
+ chain filter_IN_public {
+ jump filter_IN_public_log
+ jump filter_IN_public_deny
+ jump filter_IN_public_allow
+ meta l4proto { icmp, ipv6-icmp } accept
+ }
+
+ chain filter_IN_public_log {
+ }
+
+ chain filter_IN_public_deny {
+ }
+
+ chain filter_IN_public_allow {
+ tcp dport 22 ct state new,untracked accept
+ ip6 daddr fe80::/64 udp dport 546 ct state new,untracked accept
+ }
+
+ chain filter_FWDI_public {
+ jump filter_FWDI_public_log
+ jump filter_FWDI_public_deny
+ jump filter_FWDI_public_allow
+ meta l4proto { icmp, ipv6-icmp } accept
+ }
+
+ chain filter_FWDI_public_log {
+ }
+
+ chain filter_FWDI_public_deny {
+ }
+
+ chain filter_FWDI_public_allow {
+ }
+
+ chain mangle_PRE_public {
+ jump mangle_PRE_public_log
+ jump mangle_PRE_public_deny
+ jump mangle_PRE_public_allow
+ }
+
+ chain mangle_PRE_public_log {
+ }
+
+ chain mangle_PRE_public_deny {
+ }
+
+ chain mangle_PRE_public_allow {
+ }
+
+ chain filter_FWDO_public {
+ jump filter_FWDO_public_log
+ jump filter_FWDO_public_deny
+ jump filter_FWDO_public_allow
+ }
+
+ chain filter_FWDO_public_log {
+ }
+
+ chain filter_FWDO_public_deny {
+ }
+
+ chain filter_FWDO_public_allow {
+ }
+
+ chain raw_PRE_home {
+ jump raw_PRE_home_log
+ jump raw_PRE_home_deny
+ jump raw_PRE_home_allow
+ }
+
+ chain raw_PRE_home_log {
+ }
+
+ chain raw_PRE_home_deny {
+ }
+
+ chain raw_PRE_home_allow {
+ udp dport 137 ct helper "netbios-ns"
+ }
+
+ chain filter_IN_home {
+ jump filter_IN_home_log
+ jump filter_IN_home_deny
+ jump filter_IN_home_allow
+ meta l4proto { icmp, ipv6-icmp } accept
+ }
+
+ chain filter_IN_home_log {
+ }
+
+ chain filter_IN_home_deny {
+ }
+
+ chain filter_IN_home_allow {
+ tcp dport 22 ct state new,untracked accept
+ ip daddr 224.0.0.251 udp dport 5353 ct state new,untracked accept
+ ip6 daddr ff02::fb udp dport 5353 ct state new,untracked accept
+ udp dport 1714-1764 ct state new,untracked accept
+ tcp dport 1714-1764 ct state new,untracked accept
+ ip6 daddr fe80::/64 udp dport 546 ct state new,untracked accept
+ udp dport 137 ct state new,untracked accept
+ udp dport 138 ct state new,untracked accept
+ tcp dport 139 ct state new,untracked accept
+ tcp dport 445 ct state new,untracked accept
+ }
+
+ chain filter_FWDI_home {
+ jump filter_FWDI_home_log
+ jump filter_FWDI_home_deny
+ jump filter_FWDI_home_allow
+ meta l4proto { icmp, ipv6-icmp } accept
+ }
+
+ chain filter_FWDI_home_log {
+ }
+
+ chain filter_FWDI_home_deny {
+ }
+
+ chain filter_FWDI_home_allow {
+ }
+
+ chain mangle_PRE_home {
+ jump mangle_PRE_home_log
+ jump mangle_PRE_home_deny
+ jump mangle_PRE_home_allow
+ }
+
+ chain mangle_PRE_home_log {
+ }
+
+ chain mangle_PRE_home_deny {
+ }
+
+ chain mangle_PRE_home_allow {
+ }
+
+ chain filter_FWDO_home {
+ jump filter_FWDO_home_log
+ jump filter_FWDO_home_deny
+ jump filter_FWDO_home_allow
+ }
+
+ chain filter_FWDO_home_log {
+ }
+
+ chain filter_FWDO_home_deny {
+ }
+
+ chain filter_FWDO_home_allow {
+ }
+
+ chain raw_PRE_work {
+ jump raw_PRE_work_log
+ jump raw_PRE_work_deny
+ jump raw_PRE_work_allow
+ }
+
+ chain raw_PRE_work_log {
+ }
+
+ chain raw_PRE_work_deny {
+ }
+
+ chain raw_PRE_work_allow {
+ }
+
+ chain filter_IN_work {
+ jump filter_IN_work_log
+ jump filter_IN_work_deny
+ jump filter_IN_work_allow
+ meta l4proto { icmp, ipv6-icmp } accept
+ }
+
+ chain filter_IN_work_log {
+ }
+
+ chain filter_IN_work_deny {
+ }
+
+ chain filter_IN_work_allow {
+ tcp dport 22 ct state new,untracked accept
+ ip6 daddr fe80::/64 udp dport 546 ct state new,untracked accept
+ }
+
+ chain filter_FWDI_work {
+ jump filter_FWDI_work_log
+ jump filter_FWDI_work_deny
+ jump filter_FWDI_work_allow
+ meta l4proto { icmp, ipv6-icmp } accept
+ }
+
+ chain filter_FWDI_work_log {
+ }
+
+ chain filter_FWDI_work_deny {
+ }
+
+ chain filter_FWDI_work_allow {
+ }
+
+ chain mangle_PRE_work {
+ jump mangle_PRE_work_log
+ jump mangle_PRE_work_deny
+ jump mangle_PRE_work_allow
+ }
+
+ chain mangle_PRE_work_log {
+ }
+
+ chain mangle_PRE_work_deny {
+ }
+
+ chain mangle_PRE_work_allow {
+ }
+
+ chain filter_FWDO_work {
+ jump filter_FWDO_work_log
+ jump filter_FWDO_work_deny
+ jump filter_FWDO_work_allow
+ }
+
+ chain filter_FWDO_work_log {
+ }
+
+ chain filter_FWDO_work_deny {
+ }
+
+ chain filter_FWDO_work_allow {
+ }
+}
diff --git a/tests/shell/testcases/parsing/dumps/log.json-nft b/tests/shell/testcases/parsing/dumps/log.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/parsing/dumps/log.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/parsing/dumps/log.nft b/tests/shell/testcases/parsing/dumps/log.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/parsing/dumps/log.nft
diff --git a/tests/shell/testcases/parsing/dumps/octal.json-nft b/tests/shell/testcases/parsing/dumps/octal.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/parsing/dumps/octal.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/parsing/dumps/octal.nft b/tests/shell/testcases/parsing/dumps/octal.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/parsing/dumps/octal.nft
diff --git a/tests/shell/testcases/parsing/large_rule_pipe b/tests/shell/testcases/parsing/large_rule_pipe
new file mode 100755
index 00000000..b6760c01
--- /dev/null
+++ b/tests/shell/testcases/parsing/large_rule_pipe
@@ -0,0 +1,571 @@
+#!/bin/bash
+
+set -e
+
+RULESET="#!/sbin/nft -f
+flush ruleset;
+table ip firewalld {
+ chain nat_PREROUTING {
+ type nat hook prerouting priority -90; policy accept;
+ jump nat_PREROUTING_ZONES_SOURCE
+ jump nat_PREROUTING_ZONES
+ }
+
+ chain nat_PREROUTING_ZONES_SOURCE {
+ }
+
+ chain nat_PREROUTING_ZONES {
+ iifname "enp0s25" goto nat_PRE_home
+ goto nat_PRE_public
+ }
+
+ chain nat_POSTROUTING {
+ type nat hook postrouting priority 110; policy accept;
+ jump nat_POSTROUTING_ZONES_SOURCE
+ jump nat_POSTROUTING_ZONES
+ }
+
+ chain nat_POSTROUTING_ZONES_SOURCE {
+ }
+
+ chain nat_POSTROUTING_ZONES {
+ oifname "enp0s25" goto nat_POST_home
+ goto nat_POST_public
+ }
+
+ chain nat_PRE_public {
+ jump nat_PRE_public_log
+ jump nat_PRE_public_deny
+ jump nat_PRE_public_allow
+ }
+
+ chain nat_PRE_public_log {
+ }
+
+ chain nat_PRE_public_deny {
+ }
+
+ chain nat_PRE_public_allow {
+ }
+
+ chain nat_POST_public {
+ jump nat_POST_public_log
+ jump nat_POST_public_deny
+ jump nat_POST_public_allow
+ }
+
+ chain nat_POST_public_log {
+ }
+
+ chain nat_POST_public_deny {
+ }
+
+ chain nat_POST_public_allow {
+ }
+
+ chain nat_PRE_home {
+ jump nat_PRE_home_log
+ jump nat_PRE_home_deny
+ jump nat_PRE_home_allow
+ }
+
+ chain nat_PRE_home_log {
+ }
+
+ chain nat_PRE_home_deny {
+ }
+
+ chain nat_PRE_home_allow {
+ }
+
+ chain nat_POST_home {
+ jump nat_POST_home_log
+ jump nat_POST_home_deny
+ jump nat_POST_home_allow
+ }
+
+ chain nat_POST_home_log {
+ }
+
+ chain nat_POST_home_deny {
+ }
+
+ chain nat_POST_home_allow {
+ }
+
+ chain nat_PRE_work {
+ jump nat_PRE_work_log
+ jump nat_PRE_work_deny
+ jump nat_PRE_work_allow
+ }
+
+ chain nat_PRE_work_log {
+ }
+
+ chain nat_PRE_work_deny {
+ }
+
+ chain nat_PRE_work_allow {
+ }
+
+ chain nat_POST_work {
+ jump nat_POST_work_log
+ jump nat_POST_work_deny
+ jump nat_POST_work_allow
+ }
+
+ chain nat_POST_work_log {
+ }
+
+ chain nat_POST_work_deny {
+ }
+
+ chain nat_POST_work_allow {
+ }
+}
+table ip6 firewalld {
+ chain nat_PREROUTING {
+ type nat hook prerouting priority -90; policy accept;
+ jump nat_PREROUTING_ZONES_SOURCE
+ jump nat_PREROUTING_ZONES
+ }
+
+ chain nat_PREROUTING_ZONES_SOURCE {
+ }
+
+ chain nat_PREROUTING_ZONES {
+ iifname "enp0s25" goto nat_PRE_home
+ goto nat_PRE_public
+ }
+
+ chain nat_POSTROUTING {
+ type nat hook postrouting priority 110; policy accept;
+ jump nat_POSTROUTING_ZONES_SOURCE
+ jump nat_POSTROUTING_ZONES
+ }
+
+ chain nat_POSTROUTING_ZONES_SOURCE {
+ }
+
+ chain nat_POSTROUTING_ZONES {
+ oifname "enp0s25" goto nat_POST_home
+ goto nat_POST_public
+ }
+
+ chain nat_PRE_public {
+ jump nat_PRE_public_log
+ jump nat_PRE_public_deny
+ jump nat_PRE_public_allow
+ }
+
+ chain nat_PRE_public_log {
+ }
+
+ chain nat_PRE_public_deny {
+ }
+
+ chain nat_PRE_public_allow {
+ }
+
+ chain nat_POST_public {
+ jump nat_POST_public_log
+ jump nat_POST_public_deny
+ jump nat_POST_public_allow
+ }
+
+ chain nat_POST_public_log {
+ }
+
+ chain nat_POST_public_deny {
+ }
+
+ chain nat_POST_public_allow {
+ }
+
+ chain nat_PRE_home {
+ jump nat_PRE_home_log
+ jump nat_PRE_home_deny
+ jump nat_PRE_home_allow
+ }
+
+ chain nat_PRE_home_log {
+ }
+
+ chain nat_PRE_home_deny {
+ }
+
+ chain nat_PRE_home_allow {
+ }
+
+ chain nat_POST_home {
+ jump nat_POST_home_log
+ jump nat_POST_home_deny
+ jump nat_POST_home_allow
+ }
+
+ chain nat_POST_home_log {
+ }
+
+ chain nat_POST_home_deny {
+ }
+
+ chain nat_POST_home_allow {
+ }
+
+ chain nat_PRE_work {
+ jump nat_PRE_work_log
+ jump nat_PRE_work_deny
+ jump nat_PRE_work_allow
+ }
+
+ chain nat_PRE_work_log {
+ }
+
+ chain nat_PRE_work_deny {
+ }
+
+ chain nat_PRE_work_allow {
+ }
+
+ chain nat_POST_work {
+ jump nat_POST_work_log
+ jump nat_POST_work_deny
+ jump nat_POST_work_allow
+ }
+
+ chain nat_POST_work_log {
+ }
+
+ chain nat_POST_work_deny {
+ }
+
+ chain nat_POST_work_allow {
+ }
+}
+table inet firewalld {
+ chain raw_PREROUTING {
+ type filter hook prerouting priority -290; policy accept;
+ icmpv6 type { nd-router-advert, nd-neighbor-solicit } accept
+ meta nfproto ipv6 fib saddr . iif oif missing drop
+ jump raw_PREROUTING_ZONES_SOURCE
+ jump raw_PREROUTING_ZONES
+ }
+
+ chain raw_PREROUTING_ZONES_SOURCE {
+ }
+
+ chain raw_PREROUTING_ZONES {
+ iifname "enp0s25" goto raw_PRE_home
+ goto raw_PRE_public
+ }
+
+ chain mangle_PREROUTING {
+ type filter hook prerouting priority -140; policy accept;
+ jump mangle_PREROUTING_ZONES_SOURCE
+ jump mangle_PREROUTING_ZONES
+ }
+
+ chain mangle_PREROUTING_ZONES_SOURCE {
+ }
+
+ chain mangle_PREROUTING_ZONES {
+ iifname "enp0s25" goto mangle_PRE_home
+ goto mangle_PRE_public
+ }
+
+ chain filter_INPUT {
+ type filter hook input priority 10; policy accept;
+ ct state established,related accept
+ iifname "lo" accept
+ jump filter_INPUT_ZONES_SOURCE
+ jump filter_INPUT_ZONES
+ ct state invalid drop
+ reject with icmpx type admin-prohibited
+ }
+
+ chain filter_FORWARD {
+ type filter hook forward priority 10; policy accept;
+ ct state established,related accept
+ iifname "lo" accept
+ jump filter_FORWARD_IN_ZONES_SOURCE
+ jump filter_FORWARD_IN_ZONES
+ jump filter_FORWARD_OUT_ZONES_SOURCE
+ jump filter_FORWARD_OUT_ZONES
+ ct state invalid drop
+ reject with icmpx type admin-prohibited
+ }
+
+ chain filter_INPUT_ZONES_SOURCE {
+ }
+
+ chain filter_INPUT_ZONES {
+ iifname "enp0s25" goto filter_IN_home
+ goto filter_IN_public
+ }
+
+ chain filter_FORWARD_IN_ZONES_SOURCE {
+ }
+
+ chain filter_FORWARD_IN_ZONES {
+ iifname "enp0s25" goto filter_FWDI_home
+ goto filter_FWDI_public
+ }
+
+ chain filter_FORWARD_OUT_ZONES_SOURCE {
+ }
+
+ chain filter_FORWARD_OUT_ZONES {
+ oifname "enp0s25" goto filter_FWDO_home
+ goto filter_FWDO_public
+ }
+
+ chain raw_PRE_public {
+ jump raw_PRE_public_log
+ jump raw_PRE_public_deny
+ jump raw_PRE_public_allow
+ }
+
+ chain raw_PRE_public_log {
+ }
+
+ chain raw_PRE_public_deny {
+ }
+
+ chain raw_PRE_public_allow {
+ }
+
+ chain filter_IN_public {
+ jump filter_IN_public_log
+ jump filter_IN_public_deny
+ jump filter_IN_public_allow
+ meta l4proto { icmp, ipv6-icmp } accept
+ }
+
+ chain filter_IN_public_log {
+ }
+
+ chain filter_IN_public_deny {
+ }
+
+ chain filter_IN_public_allow {
+ tcp dport ssh ct state new,untracked accept
+ ip6 daddr fe80::/64 udp dport dhcpv6-client ct state new,untracked accept
+ }
+
+ chain filter_FWDI_public {
+ jump filter_FWDI_public_log
+ jump filter_FWDI_public_deny
+ jump filter_FWDI_public_allow
+ meta l4proto { icmp, ipv6-icmp } accept
+ }
+
+ chain filter_FWDI_public_log {
+ }
+
+ chain filter_FWDI_public_deny {
+ }
+
+ chain filter_FWDI_public_allow {
+ }
+
+ chain mangle_PRE_public {
+ jump mangle_PRE_public_log
+ jump mangle_PRE_public_deny
+ jump mangle_PRE_public_allow
+ }
+
+ chain mangle_PRE_public_log {
+ }
+
+ chain mangle_PRE_public_deny {
+ }
+
+ chain mangle_PRE_public_allow {
+ }
+
+ chain filter_FWDO_public {
+ jump filter_FWDO_public_log
+ jump filter_FWDO_public_deny
+ jump filter_FWDO_public_allow
+ }
+
+ chain filter_FWDO_public_log {
+ }
+
+ chain filter_FWDO_public_deny {
+ }
+
+ chain filter_FWDO_public_allow {
+ }
+
+ chain raw_PRE_home {
+ jump raw_PRE_home_log
+ jump raw_PRE_home_deny
+ jump raw_PRE_home_allow
+ }
+
+ chain raw_PRE_home_log {
+ }
+
+ chain raw_PRE_home_deny {
+ }
+
+ chain raw_PRE_home_allow {
+ udp dport netbios-ns ct helper "netbios-ns"
+ }
+
+ chain filter_IN_home {
+ jump filter_IN_home_log
+ jump filter_IN_home_deny
+ jump filter_IN_home_allow
+ meta l4proto { icmp, ipv6-icmp } accept
+ }
+
+ chain filter_IN_home_log {
+ }
+
+ chain filter_IN_home_deny {
+ }
+
+ chain filter_IN_home_allow {
+ tcp dport ssh ct state new,untracked accept
+ ip daddr 224.0.0.251 udp dport mdns ct state new,untracked accept
+ ip6 daddr ff02::fb udp dport mdns ct state new,untracked accept
+ udp dport 1714-1764 ct state new,untracked accept
+ tcp dport 1714-1764 ct state new,untracked accept
+ ip6 daddr fe80::/64 udp dport dhcpv6-client ct state new,untracked accept
+ udp dport netbios-ns ct state new,untracked accept
+ udp dport netbios-dgm ct state new,untracked accept
+ tcp dport netbios-ssn ct state new,untracked accept
+ tcp dport microsoft-ds ct state new,untracked accept
+ }
+
+ chain filter_FWDI_home {
+ jump filter_FWDI_home_log
+ jump filter_FWDI_home_deny
+ jump filter_FWDI_home_allow
+ meta l4proto { icmp, ipv6-icmp } accept
+ }
+
+ chain filter_FWDI_home_log {
+ }
+
+ chain filter_FWDI_home_deny {
+ }
+
+ chain filter_FWDI_home_allow {
+ }
+
+ chain mangle_PRE_home {
+ jump mangle_PRE_home_log
+ jump mangle_PRE_home_deny
+ jump mangle_PRE_home_allow
+ }
+
+ chain mangle_PRE_home_log {
+ }
+
+ chain mangle_PRE_home_deny {
+ }
+
+ chain mangle_PRE_home_allow {
+ }
+
+ chain filter_FWDO_home {
+ jump filter_FWDO_home_log
+ jump filter_FWDO_home_deny
+ jump filter_FWDO_home_allow
+ }
+
+ chain filter_FWDO_home_log {
+ }
+
+ chain filter_FWDO_home_deny {
+ }
+
+ chain filter_FWDO_home_allow {
+ }
+
+ chain raw_PRE_work {
+ jump raw_PRE_work_log
+ jump raw_PRE_work_deny
+ jump raw_PRE_work_allow
+ }
+
+ chain raw_PRE_work_log {
+ }
+
+ chain raw_PRE_work_deny {
+ }
+
+ chain raw_PRE_work_allow {
+ }
+
+ chain filter_IN_work {
+ jump filter_IN_work_log
+ jump filter_IN_work_deny
+ jump filter_IN_work_allow
+ meta l4proto { icmp, ipv6-icmp } accept
+ }
+
+ chain filter_IN_work_log {
+ }
+
+ chain filter_IN_work_deny {
+ }
+
+ chain filter_IN_work_allow {
+ tcp dport ssh ct state new,untracked accept
+ ip6 daddr fe80::/64 udp dport dhcpv6-client ct state new,untracked accept
+ }
+
+ chain filter_FWDI_work {
+ jump filter_FWDI_work_log
+ jump filter_FWDI_work_deny
+ jump filter_FWDI_work_allow
+ meta l4proto { icmp, ipv6-icmp } accept
+ }
+
+ chain filter_FWDI_work_log {
+ }
+
+ chain filter_FWDI_work_deny {
+ }
+
+ chain filter_FWDI_work_allow {
+ }
+
+ chain mangle_PRE_work {
+ jump mangle_PRE_work_log
+ jump mangle_PRE_work_deny
+ jump mangle_PRE_work_allow
+ }
+
+ chain mangle_PRE_work_log {
+ }
+
+ chain mangle_PRE_work_deny {
+ }
+
+ chain mangle_PRE_work_allow {
+ }
+
+ chain filter_FWDO_work {
+ jump filter_FWDO_work_log
+ jump filter_FWDO_work_deny
+ jump filter_FWDO_work_allow
+ }
+
+ chain filter_FWDO_work_log {
+ }
+
+ chain filter_FWDO_work_deny {
+ }
+
+ chain filter_FWDO_work_allow {
+ }
+}"
+
+( echo "flush ruleset;"; echo "${RULESET}" ) | $NFT -f -
+
+exit 0
diff --git a/tests/shell/testcases/parsing/log b/tests/shell/testcases/parsing/log
new file mode 100755
index 00000000..0b89d589
--- /dev/null
+++ b/tests/shell/testcases/parsing/log
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+$NFT add table t || exit 1
+$NFT add chain t c || exit 1
+$NFT add rule t c 'iif != lo ip daddr 127.0.0.1/8 counter limit rate 1/second log flags all prefix "nft_lo4 " drop' || exit 1
+$NFT add rule t c 'iif != lo ip daddr 127.0.0.1/8 counter limit rate 1/second log flags all level debug drop' || exit 1
+$NFT delete table t || exit 1
+
+exit 0
+
diff --git a/tests/shell/testcases/parsing/octal b/tests/shell/testcases/parsing/octal
new file mode 100755
index 00000000..09ac26e7
--- /dev/null
+++ b/tests/shell/testcases/parsing/octal
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+$NFT add table t || exit 1
+$NFT add chain t c || exit 1
+$NFT add rule t c 'ip saddr 01 continue comment "0.0.0.1"' || exit 1
+$NFT add rule t c 'ip saddr 08 continue comment "error"' && {
+ echo "'"ip saddr 08"'" not rejected 1>&2
+ exit 1
+}
+$NFT delete table t || exit 1
+
+exit 0
+
diff --git a/tests/shell/testcases/rule_management/0003insert_0 b/tests/shell/testcases/rule_management/0003insert_0
index 329ccc20..c343d579 100755
--- a/tests/shell/testcases/rule_management/0003insert_0
+++ b/tests/shell/testcases/rule_management/0003insert_0
@@ -9,3 +9,7 @@ $NFT add chain t c
$NFT insert rule t c accept
$NFT insert rule t c drop
$NFT insert rule t c masquerade
+
+# check 'evaluate: un-break rule insert with intervals'
+
+$NFT insert rule t c tcp sport { 3478-3497, 16384-16387 }
diff --git a/tests/shell/testcases/rule_management/0010replace_0 b/tests/shell/testcases/rule_management/0010replace_0
index 251cebb2..cd69a89d 100755
--- a/tests/shell/testcases/rule_management/0010replace_0
+++ b/tests/shell/testcases/rule_management/0010replace_0
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
# test for kernel commit ca08987885a147643817d02bf260bc4756ce8cd4
# ("netfilter: nf_tables: deactivate expressions in rule replecement routine")
diff --git a/tests/shell/testcases/rule_management/0011reset_0 b/tests/shell/testcases/rule_management/0011reset_0
new file mode 100755
index 00000000..33eadd9e
--- /dev/null
+++ b/tests/shell/testcases/rule_management/0011reset_0
@@ -0,0 +1,170 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_reset_rule)
+
+set -e
+
+echo "loading ruleset"
+$NFT -f - <<EOF
+table ip t {
+ set s {
+ type ipv4_addr
+ counter
+ elements = { 1.1.1.1 counter packets 1 bytes 11 }
+ }
+ chain c {
+ counter packets 1 bytes 11 update @s { ip saddr } accept
+ counter packets 2 bytes 12 drop
+ }
+
+ chain c2 {
+ counter packets 3 bytes 13 accept
+ counter packets 4 bytes 14 drop
+ }
+}
+table inet t {
+ chain c {
+ counter packets 5 bytes 15 accept
+ counter packets 6 bytes 16 drop
+ }
+}
+table ip t2 {
+ chain c2 {
+ counter packets 7 bytes 17 accept
+ counter packets 8 bytes 18 drop
+ }
+}
+EOF
+
+echo "resetting specific rule"
+handle=$($NFT -a list chain t c | sed -n 's/.*accept # handle \([0-9]*\)$/\1/p')
+$NFT reset rule t c handle $handle
+EXPECT='table ip t {
+ set s {
+ type ipv4_addr
+ size 65535
+ flags dynamic
+ counter
+ elements = { 1.1.1.1 counter packets 1 bytes 11 }
+ }
+
+ chain c {
+ counter packets 0 bytes 0 update @s { ip saddr } accept
+ counter packets 2 bytes 12 drop
+ }
+
+ chain c2 {
+ counter packets 3 bytes 13 accept
+ counter packets 4 bytes 14 drop
+ }
+}
+table inet t {
+ chain c {
+ counter packets 5 bytes 15 accept
+ counter packets 6 bytes 16 drop
+ }
+}
+table ip t2 {
+ chain c2 {
+ counter packets 7 bytes 17 accept
+ counter packets 8 bytes 18 drop
+ }
+}'
+$DIFF -u <(echo "$EXPECT") <($NFT list ruleset)
+
+echo "resetting specific chain"
+EXPECT='table ip t {
+ set s {
+ type ipv4_addr
+ size 65535
+ flags dynamic
+ counter
+ }
+
+ chain c2 {
+ counter packets 3 bytes 13 accept
+ counter packets 4 bytes 14 drop
+ }
+}'
+$DIFF -u <(echo "$EXPECT") <($NFT reset rules chain t c2)
+
+echo "resetting specific table"
+EXPECT='table ip t {
+ set s {
+ type ipv4_addr
+ size 65535
+ flags dynamic
+ counter
+ }
+
+ chain c {
+ counter packets 0 bytes 0 update @s { ip saddr } accept
+ counter packets 2 bytes 12 drop
+ }
+
+ chain c2 {
+ counter packets 0 bytes 0 accept
+ counter packets 0 bytes 0 drop
+ }
+}'
+$DIFF -u <(echo "$EXPECT") <($NFT reset rules table t)
+
+echo "resetting specific family"
+EXPECT='table ip t {
+ set s {
+ type ipv4_addr
+ size 65535
+ flags dynamic
+ counter
+ }
+
+ chain c {
+ counter packets 0 bytes 0 update @s { ip saddr } accept
+ counter packets 0 bytes 0 drop
+ }
+
+ chain c2 {
+ counter packets 0 bytes 0 accept
+ counter packets 0 bytes 0 drop
+ }
+}
+table ip t2 {
+ chain c2 {
+ counter packets 7 bytes 17 accept
+ counter packets 8 bytes 18 drop
+ }
+}'
+$DIFF -u <(echo "$EXPECT") <($NFT reset rules ip)
+
+echo "resetting whole ruleset"
+EXPECT='table ip t {
+ set s {
+ type ipv4_addr
+ size 65535
+ flags dynamic
+ counter
+ }
+
+ chain c {
+ counter packets 0 bytes 0 update @s { ip saddr } accept
+ counter packets 0 bytes 0 drop
+ }
+
+ chain c2 {
+ counter packets 0 bytes 0 accept
+ counter packets 0 bytes 0 drop
+ }
+}
+table inet t {
+ chain c {
+ counter packets 5 bytes 15 accept
+ counter packets 6 bytes 16 drop
+ }
+}
+table ip t2 {
+ chain c2 {
+ counter packets 0 bytes 0 accept
+ counter packets 0 bytes 0 drop
+ }
+}'
+$DIFF -u <(echo "$EXPECT") <($NFT reset rules)
diff --git a/tests/shell/testcases/rule_management/0012destroy_0 b/tests/shell/testcases/rule_management/0012destroy_0
new file mode 100755
index 00000000..a058150f
--- /dev/null
+++ b/tests/shell/testcases/rule_management/0012destroy_0
@@ -0,0 +1,14 @@
+#!/bin/bash -e
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_destroy)
+
+$NFT add table t
+$NFT add chain t c
+
+# pass for non-existent rule
+$NFT destroy rule t c handle 3333
+
+# successfully delete existing rule
+handle=$($NFT -a -e insert rule t c accept | \
+ sed -n 's/.*handle \([0-9]*\)$/\1/p')
+$NFT destroy rule t c handle "$handle"
diff --git a/tests/shell/testcases/rule_management/dumps/0001addinsertposition_0.json-nft b/tests/shell/testcases/rule_management/dumps/0001addinsertposition_0.json-nft
new file mode 100644
index 00000000..83dfee0d
--- /dev/null
+++ b/tests/shell/testcases/rule_management/dumps/0001addinsertposition_0.json-nft
@@ -0,0 +1,65 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/rule_management/dumps/0001addinsertposition_0.nft b/tests/shell/testcases/rule_management/dumps/0001addinsertposition_0.nft
new file mode 100644
index 00000000..527d79d6
--- /dev/null
+++ b/tests/shell/testcases/rule_management/dumps/0001addinsertposition_0.nft
@@ -0,0 +1,7 @@
+table ip t {
+ chain c {
+ drop
+ accept
+ accept
+ }
+}
diff --git a/tests/shell/testcases/rule_management/dumps/0002addinsertlocation_1.json-nft b/tests/shell/testcases/rule_management/dumps/0002addinsertlocation_1.json-nft
new file mode 100644
index 00000000..b3808ce2
--- /dev/null
+++ b/tests/shell/testcases/rule_management/dumps/0002addinsertlocation_1.json-nft
@@ -0,0 +1,52 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/rule_management/dumps/0002addinsertlocation_1.nft b/tests/shell/testcases/rule_management/dumps/0002addinsertlocation_1.nft
new file mode 100644
index 00000000..b76cd930
--- /dev/null
+++ b/tests/shell/testcases/rule_management/dumps/0002addinsertlocation_1.nft
@@ -0,0 +1,6 @@
+table ip t {
+ chain c {
+ accept
+ accept
+ }
+}
diff --git a/tests/shell/testcases/rule_management/dumps/0003insert_0.json-nft b/tests/shell/testcases/rule_management/dumps/0003insert_0.json-nft
new file mode 100644
index 00000000..9216cabf
--- /dev/null
+++ b/tests/shell/testcases/rule_management/dumps/0003insert_0.json-nft
@@ -0,0 +1,102 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "sport"
+ }
+ },
+ "right": {
+ "set": [
+ {
+ "range": [
+ 3478,
+ 3497
+ ]
+ },
+ {
+ "range": [
+ 16384,
+ 16387
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "masquerade": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/rule_management/dumps/0003insert_0.nft b/tests/shell/testcases/rule_management/dumps/0003insert_0.nft
index 9421f4ae..b1875aba 100644
--- a/tests/shell/testcases/rule_management/dumps/0003insert_0.nft
+++ b/tests/shell/testcases/rule_management/dumps/0003insert_0.nft
@@ -1,5 +1,6 @@
table ip t {
chain c {
+ tcp sport { 3478-3497, 16384-16387 }
masquerade
drop
accept
diff --git a/tests/shell/testcases/rule_management/dumps/0004replace_0.json-nft b/tests/shell/testcases/rule_management/dumps/0004replace_0.json-nft
new file mode 100644
index 00000000..5d0b7d06
--- /dev/null
+++ b/tests/shell/testcases/rule_management/dumps/0004replace_0.json-nft
@@ -0,0 +1,39 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "drop": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/rule_management/dumps/0005replace_1.json-nft b/tests/shell/testcases/rule_management/dumps/0005replace_1.json-nft
new file mode 100644
index 00000000..db64cdbc
--- /dev/null
+++ b/tests/shell/testcases/rule_management/dumps/0005replace_1.json-nft
@@ -0,0 +1,26 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/rule_management/dumps/0005replace_1.nft b/tests/shell/testcases/rule_management/dumps/0005replace_1.nft
new file mode 100644
index 00000000..1e0d1d60
--- /dev/null
+++ b/tests/shell/testcases/rule_management/dumps/0005replace_1.nft
@@ -0,0 +1,4 @@
+table ip t {
+ chain c {
+ }
+}
diff --git a/tests/shell/testcases/rule_management/dumps/0006replace_1.json-nft b/tests/shell/testcases/rule_management/dumps/0006replace_1.json-nft
new file mode 100644
index 00000000..db64cdbc
--- /dev/null
+++ b/tests/shell/testcases/rule_management/dumps/0006replace_1.json-nft
@@ -0,0 +1,26 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/rule_management/dumps/0006replace_1.nft b/tests/shell/testcases/rule_management/dumps/0006replace_1.nft
new file mode 100644
index 00000000..1e0d1d60
--- /dev/null
+++ b/tests/shell/testcases/rule_management/dumps/0006replace_1.nft
@@ -0,0 +1,4 @@
+table ip t {
+ chain c {
+ }
+}
diff --git a/tests/shell/testcases/rule_management/dumps/0007delete_0.json-nft b/tests/shell/testcases/rule_management/dumps/0007delete_0.json-nft
new file mode 100644
index 00000000..5d0b7d06
--- /dev/null
+++ b/tests/shell/testcases/rule_management/dumps/0007delete_0.json-nft
@@ -0,0 +1,39 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "drop": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/rule_management/dumps/0008delete_1.json-nft b/tests/shell/testcases/rule_management/dumps/0008delete_1.json-nft
new file mode 100644
index 00000000..db64cdbc
--- /dev/null
+++ b/tests/shell/testcases/rule_management/dumps/0008delete_1.json-nft
@@ -0,0 +1,26 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/rule_management/dumps/0008delete_1.nft b/tests/shell/testcases/rule_management/dumps/0008delete_1.nft
new file mode 100644
index 00000000..1e0d1d60
--- /dev/null
+++ b/tests/shell/testcases/rule_management/dumps/0008delete_1.nft
@@ -0,0 +1,4 @@
+table ip t {
+ chain c {
+ }
+}
diff --git a/tests/shell/testcases/rule_management/dumps/0009delete_1.json-nft b/tests/shell/testcases/rule_management/dumps/0009delete_1.json-nft
new file mode 100644
index 00000000..db64cdbc
--- /dev/null
+++ b/tests/shell/testcases/rule_management/dumps/0009delete_1.json-nft
@@ -0,0 +1,26 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/rule_management/dumps/0009delete_1.nft b/tests/shell/testcases/rule_management/dumps/0009delete_1.nft
new file mode 100644
index 00000000..1e0d1d60
--- /dev/null
+++ b/tests/shell/testcases/rule_management/dumps/0009delete_1.nft
@@ -0,0 +1,4 @@
+table ip t {
+ chain c {
+ }
+}
diff --git a/tests/shell/testcases/rule_management/dumps/0010replace_0.json-nft b/tests/shell/testcases/rule_management/dumps/0010replace_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/rule_management/dumps/0010replace_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/rule_management/dumps/0010replace_0.nft b/tests/shell/testcases/rule_management/dumps/0010replace_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/rule_management/dumps/0010replace_0.nft
diff --git a/tests/shell/testcases/rule_management/dumps/0011reset_0.json-nft b/tests/shell/testcases/rule_management/dumps/0011reset_0.json-nft
new file mode 100644
index 00000000..bc242467
--- /dev/null
+++ b/tests/shell/testcases/rule_management/dumps/0011reset_0.json-nft
@@ -0,0 +1,257 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c2",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 65535,
+ "flags": [
+ "dynamic"
+ ],
+ "elem": [
+ {
+ "elem": {
+ "val": "1.1.1.1",
+ "counter": {
+ "packets": 1,
+ "bytes": 11
+ }
+ }
+ }
+ ],
+ "stmt": [
+ {
+ "counter": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "set": {
+ "op": "update",
+ "elem": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "set": "@s"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c2",
+ "handle": 0,
+ "expr": [
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c2",
+ "handle": 0,
+ "expr": [
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t2",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t2",
+ "name": "c2",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t2",
+ "chain": "c2",
+ "handle": 0,
+ "expr": [
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t2",
+ "chain": "c2",
+ "handle": 0,
+ "expr": [
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/rule_management/dumps/0011reset_0.nft b/tests/shell/testcases/rule_management/dumps/0011reset_0.nft
new file mode 100644
index 00000000..3b4f5a11
--- /dev/null
+++ b/tests/shell/testcases/rule_management/dumps/0011reset_0.nft
@@ -0,0 +1,31 @@
+table ip t {
+ set s {
+ type ipv4_addr
+ size 65535
+ flags dynamic
+ counter
+ elements = { 1.1.1.1 counter packets 1 bytes 11 }
+ }
+
+ chain c {
+ counter packets 0 bytes 0 update @s { ip saddr } accept
+ counter packets 0 bytes 0 drop
+ }
+
+ chain c2 {
+ counter packets 0 bytes 0 accept
+ counter packets 0 bytes 0 drop
+ }
+}
+table inet t {
+ chain c {
+ counter packets 0 bytes 0 accept
+ counter packets 0 bytes 0 drop
+ }
+}
+table ip t2 {
+ chain c2 {
+ counter packets 0 bytes 0 accept
+ counter packets 0 bytes 0 drop
+ }
+}
diff --git a/tests/shell/testcases/rule_management/dumps/0012destroy_0.json-nft b/tests/shell/testcases/rule_management/dumps/0012destroy_0.json-nft
new file mode 100644
index 00000000..db64cdbc
--- /dev/null
+++ b/tests/shell/testcases/rule_management/dumps/0012destroy_0.json-nft
@@ -0,0 +1,26 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/rule_management/dumps/0012destroy_0.nft b/tests/shell/testcases/rule_management/dumps/0012destroy_0.nft
new file mode 100644
index 00000000..1e0d1d60
--- /dev/null
+++ b/tests/shell/testcases/rule_management/dumps/0012destroy_0.nft
@@ -0,0 +1,4 @@
+table ip t {
+ chain c {
+ }
+}
diff --git a/tests/shell/testcases/sets/0011add_many_elements_0 b/tests/shell/testcases/sets/0011add_many_elements_0
index ba23f90f..c37b2f0d 100755
--- a/tests/shell/testcases/sets/0011add_many_elements_0
+++ b/tests/shell/testcases/sets/0011add_many_elements_0
@@ -3,6 +3,14 @@
# test adding many sets elements
HOWMANY=255
+if [ "$NFT_TEST_HAS_SOCKET_LIMITS" = y ] ; then
+ # The socket limit /proc/sys/net/core/wmem_max may be unsuitable for
+ # the test.
+ #
+ # Run only a subset of the test and mark as skipped at the end.
+ HOWMANY=30
+fi
+
tmpfile=$(mktemp)
if [ ! -w $tmpfile ] ; then
@@ -30,3 +38,10 @@ add element x y $(generate)" > $tmpfile
set -e
$NFT -f $tmpfile
+
+if [ "$HOWMANY" != 255 ] ; then
+ echo "NFT_TEST_HAS_SOCKET_LIMITS indicates that the socket limit for"
+ echo "/proc/sys/net/core/wmem_max is too small for this test. Mark as SKIPPED"
+ echo "You may bump the limit and rerun with \`NFT_TEST_HAS_SOCKET_LIMITS=n\`."
+ exit 77
+fi
diff --git a/tests/shell/testcases/sets/0012add_delete_many_elements_0 b/tests/shell/testcases/sets/0012add_delete_many_elements_0
index 7e7beebd..64451604 100755
--- a/tests/shell/testcases/sets/0012add_delete_many_elements_0
+++ b/tests/shell/testcases/sets/0012add_delete_many_elements_0
@@ -3,6 +3,13 @@
# test adding and deleting many sets elements
HOWMANY=255
+if [ "$NFT_TEST_HAS_SOCKET_LIMITS" = y ] ; then
+ # The socket limit /proc/sys/net/core/wmem_max may be unsuitable for
+ # the test.
+ #
+ # Run only a subset of the test and mark as skipped at the end.
+ HOWMANY=30
+fi
tmpfile=$(mktemp)
if [ ! -w $tmpfile ] ; then
@@ -31,3 +38,10 @@ delete element x y $(generate)" > $tmpfile
set -e
$NFT -f $tmpfile
+
+if [ "$HOWMANY" != 255 ] ; then
+ echo "NFT_TEST_HAS_SOCKET_LIMITS indicates that the socket limit for"
+ echo "/proc/sys/net/core/wmem_max is too small for this test. Mark as SKIPPED"
+ echo "You may bump the limit and rerun with \`NFT_TEST_HAS_SOCKET_LIMITS=n\`."
+ exit 77
+fi
diff --git a/tests/shell/testcases/sets/0013add_delete_many_elements_0 b/tests/shell/testcases/sets/0013add_delete_many_elements_0
index 5774317b..c0925dd5 100755
--- a/tests/shell/testcases/sets/0013add_delete_many_elements_0
+++ b/tests/shell/testcases/sets/0013add_delete_many_elements_0
@@ -3,6 +3,13 @@
# test adding and deleting many sets elements in two nft -f runs.
HOWMANY=255
+if [ "$NFT_TEST_HAS_SOCKET_LIMITS" = y ] ; then
+ # The socket limit /proc/sys/net/core/wmem_max may be unsuitable for
+ # the test.
+ #
+ # Run only a subset of the test and mark as skipped at the end.
+ HOWMANY=30
+fi
tmpfile=$(mktemp)
if [ ! -w $tmpfile ] ; then
@@ -32,3 +39,10 @@ add element x y $(generate)" > $tmpfile
$NFT -f $tmpfile
echo "delete element x y $(generate)" > $tmpfile
$NFT -f $tmpfile
+
+if [ "$HOWMANY" != 255 ] ; then
+ echo "NFT_TEST_HAS_SOCKET_LIMITS indicates that the socket limit for"
+ echo "/proc/sys/net/core/wmem_max is too small for this test. Mark as SKIPPED"
+ echo "You may bump the limit and rerun with \`NFT_TEST_HAS_SOCKET_LIMITS=n\`."
+ exit 77
+fi
diff --git a/tests/shell/testcases/sets/0020comments_0 b/tests/shell/testcases/sets/0020comments_0
index 44d451a8..1df38326 100755
--- a/tests/shell/testcases/sets/0020comments_0
+++ b/tests/shell/testcases/sets/0020comments_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_comment)
+
# Test that comments are added to set elements in standard sets.
# Explicitly test bitmap backend set implementation.
diff --git a/tests/shell/testcases/sets/0022type_selective_flush_0 b/tests/shell/testcases/sets/0022type_selective_flush_0
index 6062913b..48f6875b 100755
--- a/tests/shell/testcases/sets/0022type_selective_flush_0
+++ b/tests/shell/testcases/sets/0022type_selective_flush_0
@@ -16,7 +16,7 @@ $NFT -f - <<< "$RULESET"
# Commands that should be invalid
declare -a cmds=(
- "flush set t m" "flush set t f"
+ "flush set t m"
"flush map t s" "flush map t f"
"flush meter t s" "flush meter t m"
)
diff --git a/tests/shell/testcases/sets/0024synproxy_0 b/tests/shell/testcases/sets/0024synproxy_0
new file mode 100755
index 00000000..0c7da572
--- /dev/null
+++ b/tests/shell/testcases/sets/0024synproxy_0
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_synproxy)
+
+# * creating valid named objects
+# * referencing them from a valid rule
+
+RULESET="
+table inet x {
+ synproxy https-synproxy {
+ mss 1460
+ wscale 7
+ timestamp sack-perm
+ }
+ synproxy other-synproxy {
+ mss 1460
+ wscale 5
+ }
+ map test2 {
+ type ipv4_addr : synproxy
+ flags interval
+ elements = { 192.168.1.0/24 : "https-synproxy", 192.168.2.0/24 : "other-synproxy" }
+ }
+ chain y {
+ type filter hook input priority 0; policy accept;
+ synproxy name ip saddr map { 192.168.1.0/24 : "https-synproxy", 192.168.2.0/24 : "other-synproxy" }
+ }
+}"
+
+set -e
+$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/sets/0030add_many_elements_interval_0 b/tests/shell/testcases/sets/0030add_many_elements_interval_0
index 059ade9a..32a705bf 100755
--- a/tests/shell/testcases/sets/0030add_many_elements_interval_0
+++ b/tests/shell/testcases/sets/0030add_many_elements_interval_0
@@ -1,6 +1,13 @@
#!/bin/bash
HOWMANY=255
+if [ "$NFT_TEST_HAS_SOCKET_LIMITS" = y ] ; then
+ # The socket limit /proc/sys/net/core/wmem_max may be unsuitable for
+ # the test.
+ #
+ # Run only a subset of the test and mark as skipped at the end.
+ HOWMANY=30
+fi
tmpfile=$(mktemp)
if [ ! -w $tmpfile ] ; then
@@ -28,3 +35,10 @@ add element x y $(generate)" > $tmpfile
set -e
$NFT -f $tmpfile
+
+if [ "$HOWMANY" != 255 ] ; then
+ echo "NFT_TEST_HAS_SOCKET_LIMITS indicates that the socket limit for"
+ echo "/proc/sys/net/core/wmem_max is too small for this test. Mark as SKIPPED"
+ echo "You may bump the limit and rerun with \`NFT_TEST_HAS_SOCKET_LIMITS=n\`."
+ exit 77
+fi
diff --git a/tests/shell/testcases/sets/0031set_timeout_size_0 b/tests/shell/testcases/sets/0031set_timeout_size_0
index 796640d6..9a4a27f6 100755
--- a/tests/shell/testcases/sets/0031set_timeout_size_0
+++ b/tests/shell/testcases/sets/0031set_timeout_size_0
@@ -8,5 +8,5 @@ add rule x test set update ip daddr timeout 100ms @y"
set -e
$NFT -f - <<< "$RULESET"
-$NFT list chain x test | grep -q 'update @y { ip saddr timeout 1d2h3m4s10ms }'
+$NFT list chain x test | grep -q 'update @y { ip saddr timeout 1d2h3m4s\(10\|8\)ms }'
$NFT list chain x test | grep -q 'update @y { ip daddr timeout 100ms }'
diff --git a/tests/shell/testcases/sets/0034get_element_0 b/tests/shell/testcases/sets/0034get_element_0
index 3343529b..32375b9f 100755
--- a/tests/shell/testcases/sets/0034get_element_0
+++ b/tests/shell/testcases/sets/0034get_element_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_pipapo)
+
RC=0
check() { # (set, elems, expected)
diff --git a/tests/shell/testcases/sets/0036add_set_element_expiration_0 b/tests/shell/testcases/sets/0036add_set_element_expiration_0
index 3097d077..d961ffd4 100755
--- a/tests/shell/testcases/sets/0036add_set_element_expiration_0
+++ b/tests/shell/testcases/sets/0036add_set_element_expiration_0
@@ -1,15 +1,25 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_setelem_expiration)
+
set -e
+drop_seconds() {
+ sed -E 's/m[0-9]*s([0-9]*ms)?/m/g'
+}
+
RULESET="add table ip x
+add set ip x y { type ipv4_addr; flags dynamic,timeout; }
+add element ip x y { 1.1.1.1 timeout 30m expires 15m59s }"
+
+EXPECTED="add table ip x
add set ip x y { type ipv4_addr; flags dynamic,timeout; }
-add element ip x y { 1.1.1.1 timeout 30s expires 15s }"
+add element ip x y { 1.1.1.1 timeout 30m expires 15m }"
-test_output=$($NFT -e -f - <<< "$RULESET" 2>&1 | grep -v '# new generation')
+test_output=$($NFT -e -f - <<< "$RULESET" 2>&1 | grep -v '# new generation' | drop_seconds)
-if [ "$test_output" != "$RULESET" ] ; then
- $DIFF -u <(echo "$test_output") <(echo "$RULESET")
+if [ "$test_output" != "$EXPECTED" ] ; then
+ $DIFF -u <(echo "$test_output") <(echo "$EXPECTED")
exit 1
fi
diff --git a/tests/shell/testcases/sets/0038meter_list_0 b/tests/shell/testcases/sets/0038meter_list_0
index e9e0f6fb..7c37c1d8 100755
--- a/tests/shell/testcases/sets/0038meter_list_0
+++ b/tests/shell/testcases/sets/0038meter_list_0
@@ -14,7 +14,12 @@ RULESET="
"
expected_output="table ip t {
- meter m {
+ set s {
+ type ipv4_addr
+ size 256
+ flags dynamic,timeout
+ }
+ set m {
type ipv4_addr
size 128
flags dynamic
diff --git a/tests/shell/testcases/sets/0043concatenated_ranges_0 b/tests/shell/testcases/sets/0043concatenated_ranges_0
index 11767373..a3dbf5bf 100755
--- a/tests/shell/testcases/sets/0043concatenated_ranges_0
+++ b/tests/shell/testcases/sets/0043concatenated_ranges_0
@@ -1,4 +1,7 @@
-#!/bin/sh -e
+#!/bin/bash -e
+#
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_pipapo)
+# NFT_TEST_SKIP(NFT_TEST_SKIP_slow)
#
# 0043concatenated_ranges_0 - Add, get, list, timeout for concatenated ranges
#
@@ -14,12 +17,7 @@
# - delete them
# - make sure they can't be deleted again
-if [ "$(ps -o comm= $PPID)" = "run-tests.sh" ]; then
- # Skip some permutations on a full test suite run to keep it quick
- TYPES="ipv4_addr ipv6_addr ether_addr inet_service"
-else
- TYPES="ipv4_addr ipv6_addr ether_addr inet_proto inet_service mark"
-fi
+TYPES="ipv4_addr ipv6_addr ether_addr inet_proto inet_service mark"
RULESPEC_ipv4_addr="ip saddr"
ELEMS_ipv4_addr="192.0.2.1 198.51.100.0/25 203.0.113.0-203.0.113.129"
@@ -147,7 +145,7 @@ for ta in ${TYPES}; do
eval add_b=\$ADD_${tb}
eval add_c=\$ADD_${tc}
${NFT} add element inet filter test \
- "{ ${add_a} . ${add_b} . ${add_c} timeout 1s${mapv}}"
+ "{ ${add_a} . ${add_b} . ${add_c} timeout 2m${mapv}}"
[ $(${NFT} list ${setmap} inet filter test | \
grep -c "${add_a} . ${add_b} . ${add_c}") -eq 1 ]
@@ -180,6 +178,10 @@ for ta in ${TYPES}; do
continue
fi
+ ${NFT} delete element inet filter test \
+ "{ ${add_a} . ${add_b} . ${add_c} ${mapv}}"
+ ${NFT} add element inet filter test \
+ "{ ${add_a} . ${add_b} . ${add_c} timeout 1s${mapv}}"
sleep 1
[ $(${NFT} list ${setmap} inet filter test | \
grep -c "${add_a} . ${add_b} . ${add_c} ${mapv}") -eq 0 ]
diff --git a/tests/shell/testcases/sets/0043concatenated_ranges_1 b/tests/shell/testcases/sets/0043concatenated_ranges_1
index bab189c5..bb3bf6b2 100755
--- a/tests/shell/testcases/sets/0043concatenated_ranges_1
+++ b/tests/shell/testcases/sets/0043concatenated_ranges_1
@@ -1,7 +1,9 @@
-#!/bin/sh -e
+#!/bin/bash -e
#
# 0043concatenated_ranges_1 - Insert and list subnets of different sizes
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_pipapo)
+
check() {
$NFT add element "${1}" t s "{ ${2} . ${3} }"
[ "$( $NFT list set "${1}" t s | grep -c "${2} . ${3}" )" = 1 ]
diff --git a/tests/shell/testcases/sets/0044interval_overlap_0 b/tests/shell/testcases/sets/0044interval_overlap_0
index face90f2..b0f51cc8 100755
--- a/tests/shell/testcases/sets/0044interval_overlap_0
+++ b/tests/shell/testcases/sets/0044interval_overlap_0
@@ -1,4 +1,6 @@
-#!/bin/sh -e
+#!/bin/bash -e
+#
+# NFT_TEST_SKIP(NFT_TEST_SKIP_slow)
#
# 0044interval_overlap_0 - Add overlapping and non-overlapping intervals
#
@@ -115,7 +117,11 @@ add_elements() {
IFS='
'
for t in ${intervals_simple} switch ${intervals_concat}; do
+if [ "$NFT_TEST_HAVE_pipapo" = y ] ; then
[ "${t}" = "switch" ] && set="c" && continue
+else
+ break
+fi
[ -z "${pass}" ] && pass="${t}" && continue
[ -z "${interval}" ] && interval="${t}" && continue
unset IFS
@@ -146,7 +152,9 @@ add_elements() {
$NFT add table t
$NFT add set t s '{ type inet_service ; flags interval ; }'
-$NFT add set t c '{ type inet_service . inet_service ; flags interval ; }'
+if [ "$NFT_TEST_HAVE_pipapo" = y ] ; then
+ $NFT add set t c '{ type inet_service . inet_service ; flags interval ; }'
+fi
add_elements
$NFT flush ruleset
@@ -155,7 +163,9 @@ estimate_timeout
$NFT flush ruleset
$NFT add table t
$NFT add set t s "{ type inet_service ; flags interval,timeout; timeout ${timeout}s; gc-interval ${timeout}s; }"
-$NFT add set t c "{ type inet_service . inet_service ; flags interval,timeout ; timeout ${timeout}s; gc-interval ${timeout}s; }"
+if [ "$NFT_TEST_HAVE_pipapo" = y ] ; then
+ $NFT add set t c "{ type inet_service . inet_service ; flags interval,timeout ; timeout ${timeout}s; gc-interval ${timeout}s; }"
+fi
add_elements
sleep $((timeout * 3 / 2))
diff --git a/tests/shell/testcases/sets/0044interval_overlap_1 b/tests/shell/testcases/sets/0044interval_overlap_1
index eeea1943..cdd0c844 100755
--- a/tests/shell/testcases/sets/0044interval_overlap_1
+++ b/tests/shell/testcases/sets/0044interval_overlap_1
@@ -1,4 +1,6 @@
-#!/bin/sh -e
+#!/bin/bash -e
+#
+# NFT_TEST_SKIP(NFT_TEST_SKIP_slow)
#
# 0044interval_overlap_1 - Single-sized intervals can never overlap partially
#
diff --git a/tests/shell/testcases/sets/0046netmap_0 b/tests/shell/testcases/sets/0046netmap_0
index 2804a4a2..7533623e 100755
--- a/tests/shell/testcases/sets/0046netmap_0
+++ b/tests/shell/testcases/sets/0046netmap_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_netmap)
+
EXPECTED="table ip x {
chain y {
type nat hook postrouting priority srcnat; policy accept;
@@ -8,6 +10,12 @@ EXPECTED="table ip x {
10.141.13.0/24 : 192.168.4.0/24 }
}
}
+ table ip6 x {
+ chain y {
+ type nat hook postrouting priority srcnat; policy accept;
+ snat ip6 prefix to ip6 saddr map { 2001:db8:1111::/64 : 2001:db8:2222::/64 }
+ }
+ }
"
set -e
diff --git a/tests/shell/testcases/sets/0047nat_0 b/tests/shell/testcases/sets/0047nat_0
index cb1d4d68..757605ee 100755
--- a/tests/shell/testcases/sets/0047nat_0
+++ b/tests/shell/testcases/sets/0047nat_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_pipapo)
+
EXPECTED="table ip x {
map y {
type ipv4_addr : interval ipv4_addr
@@ -8,6 +10,12 @@ EXPECTED="table ip x {
10.141.11.0/24 : 192.168.4.2-192.168.4.3 }
}
+ chain x {
+ type nat hook prerouting priority dstnat; policy accept;
+ meta l4proto tcp dnat ip to iifname . ip saddr map { enp2s0 . 10.1.1.136 : 1.1.2.69 . 22, enp2s0 . 10.1.1.1-10.1.1.135 : 1.1.2.66-1.84.236.78 . 22 }
+ dnat ip to iifname . ip saddr map { enp2s0 . 10.1.1.136 : 1.1.2.69, enp2s0 . 10.1.1.1-10.1.1.135 : 1.1.2.66-1.84.236.78 }
+ }
+
chain y {
type nat hook postrouting priority srcnat; policy accept;
snat to ip saddr map @y
@@ -18,3 +26,17 @@ EXPECTED="table ip x {
set -e
$NFT -f - <<< $EXPECTED
$NFT add element x y { 10.141.12.0/24 : 192.168.5.10-192.168.5.20 }
+
+EXPECTED="table inet x {
+ chain x {
+ type nat hook prerouting priority dstnat; policy accept;
+ dnat to ip daddr . tcp dport map { 10.141.10.1 . 22 : 192.168.2.2, 10.141.11.2 . 2222 : 192.168.4.2 }
+ }
+
+ chain y {
+ type nat hook postrouting priority srcnat; policy accept;
+ snat to ip saddr map { 10.141.10.0/24 : 192.168.2.2-192.168.2.4, 10.141.11.0/24 : 192.168.4.2-192.168.4.3 }
+ }
+}"
+
+$NFT -f - <<< $EXPECTED
diff --git a/tests/shell/testcases/sets/0048set_counters_0 b/tests/shell/testcases/sets/0048set_counters_0
index e62d25df..95babdc9 100755
--- a/tests/shell/testcases/sets/0048set_counters_0
+++ b/tests/shell/testcases/sets/0048set_counters_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_set_expr)
+
set -e
EXPECTED="table ip x {
diff --git a/tests/shell/testcases/sets/0049set_define_0 b/tests/shell/testcases/sets/0049set_define_0
index 1d512f7b..756afdc1 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/0051set_interval_counter_0 b/tests/shell/testcases/sets/0051set_interval_counter_0
index ea90e264..6e67a43c 100755
--- a/tests/shell/testcases/sets/0051set_interval_counter_0
+++ b/tests/shell/testcases/sets/0051set_interval_counter_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_set_expr)
+
set -e
EXPECTED="table ip x {
diff --git a/tests/shell/testcases/sets/0059set_update_multistmt_0 b/tests/shell/testcases/sets/0059set_update_multistmt_0
index 107bfb87..2aeba2c5 100755
--- a/tests/shell/testcases/sets/0059set_update_multistmt_0
+++ b/tests/shell/testcases/sets/0059set_update_multistmt_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_set_with_two_expressions)
+
RULESET="table x {
set y {
type ipv4_addr
diff --git a/tests/shell/testcases/sets/0060set_multistmt_0 b/tests/shell/testcases/sets/0060set_multistmt_0
index 6bd147c3..8e17444e 100755
--- a/tests/shell/testcases/sets/0060set_multistmt_0
+++ b/tests/shell/testcases/sets/0060set_multistmt_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_set_with_two_expressions)
+
RULESET="table x {
set y {
type ipv4_addr
diff --git a/tests/shell/testcases/sets/0060set_multistmt_1 b/tests/shell/testcases/sets/0060set_multistmt_1
new file mode 100755
index 00000000..04ef047c
--- /dev/null
+++ b/tests/shell/testcases/sets/0060set_multistmt_1
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_set_with_two_expressions)
+
+RULESET="table x {
+ set y {
+ type ipv4_addr
+ size 65535
+ flags dynamic
+ counter quota 500 bytes
+ elements = { 1.2.3.4 counter packets 9 bytes 756 quota 500 bytes used 500 bytes }
+ }
+ chain y {
+ type filter hook output priority filter; policy accept;
+ update @y { ip daddr }
+ }
+}"
+
+$NFT -f - <<< $RULESET
+# should work
+if [ $? -ne 0 ]
+then
+ exit 1
+fi
+
+# should work
+$NFT add element x y { 1.1.1.1 }
+if [ $? -ne 0 ]
+then
+ exit 1
+fi
+
+# should work
+$NFT add element x y { 2.2.2.2 counter quota 1000 bytes }
+if [ $? -ne 0 ]
+then
+ exit 1
+fi
+
+exit 0
diff --git a/tests/shell/testcases/sets/0062set_connlimit_0 b/tests/shell/testcases/sets/0062set_connlimit_0
index 48d589fe..48aa6fce 100755
--- a/tests/shell/testcases/sets/0062set_connlimit_0
+++ b/tests/shell/testcases/sets/0062set_connlimit_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_set_expr)
+
set -e
RULESET="table ip x {
@@ -24,3 +26,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/0063set_catchall_0 b/tests/shell/testcases/sets/0063set_catchall_0
index faca56a1..edd015d0 100755
--- a/tests/shell/testcases/sets/0063set_catchall_0
+++ b/tests/shell/testcases/sets/0063set_catchall_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_catchall_element)
+
set -e
RULESET="table ip x {
diff --git a/tests/shell/testcases/sets/0064map_catchall_0 b/tests/shell/testcases/sets/0064map_catchall_0
index 6f2a7c6f..fd289372 100755
--- a/tests/shell/testcases/sets/0064map_catchall_0
+++ b/tests/shell/testcases/sets/0064map_catchall_0
@@ -1,5 +1,7 @@
#!/bin/bash
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_catchall_element)
+
set -e
RULESET="table ip x {
@@ -17,3 +19,8 @@ RULESET="table ip x {
$NFT -f - <<< $RULESET
$NFT delete element x y { \* : 192.168.0.3 }
$NFT add element x y { \* : 192.168.0.4 }
+
+$NFT add chain x y
+$NFT add rule x y snat to ip saddr map @z
+$NFT 'add rule x y snat to ip saddr map { 10.141.0.0/24 : 192.168.0.2, * : 192.168.0.3 }'
+$NFT 'add rule x y snat to ip saddr . ip daddr map { 10.141.0.0/24 . 10.0.0.0/8 : 192.168.0.2, 192.168.9.0/24 . 192.168.10.0/24 : 192.168.0.4, * : 192.168.0.3 }'
diff --git a/tests/shell/testcases/sets/0067nat_concat_interval_0 b/tests/shell/testcases/sets/0067nat_concat_interval_0
index 530771b0..81621957 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 {
@@ -42,3 +29,30 @@ EXPECTED="table ip nat {
$NFT -f - <<< $EXPECTED
$NFT add rule ip nat prerouting meta l4proto { tcp, udp } dnat to ip daddr . th dport map @fwdtoip_th
+
+EXPECTED="table ip nat {
+ map ipportmap4 {
+ typeof iifname . ip saddr : interval ip daddr
+ flags interval
+ elements = { enp2s0 . 10.1.1.136 : 1.1.2.69, enp2s0 . 10.1.1.1-10.1.1.135 : 1.1.2.66-1.84.236.78 }
+ }
+ chain prerouting {
+ type nat hook prerouting priority dstnat; policy accept;
+ dnat to iifname . ip saddr map @ipportmap4
+ }
+}"
+
+$NFT -f - <<< $EXPECTED
+EXPECTED="table ip nat {
+ map ipportmap5 {
+ typeof iifname . ip saddr : interval ip daddr . tcp dport
+ flags interval
+ elements = { enp2s0 . 10.1.1.136 : 1.1.2.69 . 22, enp2s0 . 10.1.1.1-10.1.1.135 : 1.1.2.66-1.84.236.78 . 22 }
+ }
+ chain prerouting {
+ type nat hook prerouting priority dstnat; policy accept;
+ meta l4proto tcp dnat ip to iifname . ip saddr map @ipportmap5
+ }
+}"
+
+$NFT -f - <<< $EXPECTED
diff --git a/tests/shell/testcases/sets/0067nat_interval_0 b/tests/shell/testcases/sets/0067nat_interval_0
new file mode 100755
index 00000000..c90203d0
--- /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/0068interval_stack_overflow_0 b/tests/shell/testcases/sets/0068interval_stack_overflow_0
index 134282de..e61010c7 100755
--- a/tests/shell/testcases/sets/0068interval_stack_overflow_0
+++ b/tests/shell/testcases/sets/0068interval_stack_overflow_0
@@ -6,10 +6,19 @@ ruleset_file=$(mktemp)
trap 'rm -f "$ruleset_file"' EXIT
+HOWMANY=255
+if [ "$NFT_TEST_HAS_SOCKET_LIMITS" = y ] ; then
+ # The socket limit /proc/sys/net/core/wmem_max may be unsuitable for
+ # the test.
+ #
+ # Run only a subset of the test and mark as skipped at the end.
+ HOWMANY=30
+fi
+
{
echo 'define big_set = {'
- for ((i = 1; i < 255; i++)); do
- for ((j = 1; j < 80; j++)); do
+ for ((i = 1; i < $HOWMANY; i++)); do
+ for ((j = 1; j < 255; j++)); do
echo "10.0.$i.$j,"
done
done
@@ -26,4 +35,11 @@ table inet test68_table {
}
EOF
-( ulimit -s 128 && "$NFT" -f "$ruleset_file" )
+( ulimit -s 400 && $NFT -f "$ruleset_file" )
+
+if [ "$HOWMANY" != 255 ] ; then
+ echo "NFT_TEST_HAS_SOCKET_LIMITS indicates that the socket limit for"
+ echo "/proc/sys/net/core/wmem_max is too small for this test. Mark as SKIPPED"
+ echo "You may bump the limit and rerun with \`NFT_TEST_HAS_SOCKET_LIMITS=n\`."
+ exit 77
+fi
diff --git a/tests/shell/testcases/sets/0069interval_merge_0 b/tests/shell/testcases/sets/0069interval_merge_0
new file mode 100755
index 00000000..edb6422a
--- /dev/null
+++ b/tests/shell/testcases/sets/0069interval_merge_0
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table ip x {
+ set y {
+ type ipv4_addr
+ flags interval
+ auto-merge
+ elements = { 1.2.3.0, 1.2.3.255, 1.2.3.0/24, 3.3.3.3, 4.4.4.4, 4.4.4.4-4.4.4.8, 3.3.3.4, 3.3.3.5 }
+ }
+}"
+
+$NFT -f - <<< $RULESET
+
+RULESET="table ip x {
+ set y {
+ type ipv4_addr
+ flags interval
+ auto-merge
+ elements = { 1.2.4.0, 3.3.3.6, 4.4.4.0/24 }
+ }
+}"
+
+$NFT -f - <<< $RULESET
+
+$NFT add element ip x y { 1.2.3.0-1.2.4.255, 3.3.3.5, 4.4.4.1 }
+$NFT add element ip x y { 1.2.3.0-1.2.4.255, 3.3.3.5, 4.4.5.0 }
diff --git a/tests/shell/testcases/sets/0070stacked_l2_headers b/tests/shell/testcases/sets/0070stacked_l2_headers
new file mode 100755
index 00000000..07820b7c
--- /dev/null
+++ b/tests/shell/testcases/sets/0070stacked_l2_headers
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+set -e
+dumpfile=$(dirname $0)/dumps/$(basename $0).nft
+
+$NFT -f "$dumpfile"
diff --git a/tests/shell/testcases/sets/0071unclosed_prefix_interval_0 b/tests/shell/testcases/sets/0071unclosed_prefix_interval_0
new file mode 100755
index 00000000..79e3ca7d
--- /dev/null
+++ b/tests/shell/testcases/sets/0071unclosed_prefix_interval_0
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+set -e
+
+RULESET="
+table inet t {
+ set s1 {
+ type ipv4_addr
+ flags interval
+ elements = { 192.0.0.0/2, 10.0.0.0/8 }
+ }
+ set s2 {
+ type ipv6_addr
+ flags interval
+ elements = { ff00::/8, fe80::/10 }
+ }
+ chain c {
+ ip saddr @s1 accept
+ ip6 daddr @s2 accept
+ }
+}"
+
+$NFT -f - <<< "$RULESET"
diff --git a/tests/shell/testcases/sets/0072destroy_0 b/tests/shell/testcases/sets/0072destroy_0
new file mode 100755
index 00000000..9886a9b0
--- /dev/null
+++ b/tests/shell/testcases/sets/0072destroy_0
@@ -0,0 +1,12 @@
+#!/bin/bash -e
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_destroy)
+
+$NFT add table x
+
+# pass for non-existent set
+$NFT destroy set x s
+
+# successfully delete existing set
+$NFT add set x s '{type ipv4_addr; size 2;}'
+$NFT destroy set x s
diff --git a/tests/shell/testcases/sets/0073flat_interval_set b/tests/shell/testcases/sets/0073flat_interval_set
new file mode 100755
index 00000000..0630595f
--- /dev/null
+++ b/tests/shell/testcases/sets/0073flat_interval_set
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+set -e
+
+EXPECTED="flush ruleset
+add table inet filter
+add map inet filter testmap { type ipv4_addr : counter; flags interval;}
+add counter inet filter TEST
+add element inet filter testmap { 192.168.0.0/24 : \"TEST\" }"
+
+$NFT -f - <<< "$EXPECTED"
diff --git a/tests/shell/testcases/sets/0074nested_interval_set b/tests/shell/testcases/sets/0074nested_interval_set
new file mode 100755
index 00000000..e7f65fc5
--- /dev/null
+++ b/tests/shell/testcases/sets/0074nested_interval_set
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+set -e
+
+dumpfile=$(dirname $0)/dumps/$(basename $0).nft
+$NFT -f "$dumpfile"
diff --git a/tests/shell/testcases/sets/automerge_0 b/tests/shell/testcases/sets/automerge_0
new file mode 100755
index 00000000..1dbac0b7
--- /dev/null
+++ b/tests/shell/testcases/sets/automerge_0
@@ -0,0 +1,131 @@
+#!/bin/bash
+
+# NFT_TEST_SKIP(NFT_TEST_SKIP_slow)
+
+set -e
+
+RULESET="table inet x {
+ set y {
+ type inet_service
+ flags interval
+ auto-merge
+ }
+}"
+
+HOWMANY=65535
+if [ "$NFT_TEST_HAS_SOCKET_LIMITS" = y ] ; then
+ # The socket limit /proc/sys/net/core/wmem_max may be unsuitable for
+ # the test.
+ #
+ # Run only a subset of the test and mark as skipped at the end.
+ HOWMANY=5000
+fi
+
+$NFT -f - <<< $RULESET
+
+tmpfile=$(mktemp)
+echo -n "add element inet x y { " > $tmpfile
+for ((i=0;i<$HOWMANY;i+=2))
+do
+ echo -n "$i, " >> $tmpfile
+ if [ $i -eq $((HOWMANY-1)) ]
+ then
+ echo -n "$i" >> $tmpfile
+ fi
+done
+echo "}" >> $tmpfile
+
+$NFT -f $tmpfile
+
+tmpfile2=$(mktemp)
+for ((i=1;i<$HOWMANY;i+=2))
+do
+ echo "$i" >> $tmpfile2
+done
+
+tmpfile3=$(mktemp)
+shuf "$tmpfile2" --random-source=<("$NFT_TEST_BASEDIR/helpers/random-source.sh" "automerge-shuf-tmpfile2" "$NFT_TEST_RANDOM_SEED") > "$tmpfile3"
+i=0
+cat $tmpfile3 | while read line && [ $i -lt 10 ]
+do
+ $NFT add element inet x y { $line }
+ if [ $? -ne 0 ]
+ then
+ echo "failed to add $line"
+ exit 1
+ fi
+ i=$((i+1))
+done
+
+for ((i=0;i<10;i++))
+do
+ from=$(($RANDOM%$HOWMANY))
+ to=$(($from+100))
+ $NFT add element inet x y { $from-$to }
+ if [ $? -ne 0 ]
+ then
+ echo "failed to add $from-$to"
+ exit 1
+ fi
+
+ $NFT get element inet x y { $from-$to } 1>/dev/null
+ if [ $? -ne 0 ]
+ then
+ echo "failed to get $from-$to"
+ exit 1
+ fi
+
+ # partial removals in the previous random range
+ from2=$(($from+10))
+ to2=$(($to-10))
+ $NFT delete element inet x y { $from, $to, $from2-$to2 }
+ if [ $? -ne 0 ]
+ then
+ echo "failed to delete $from, $to, $from2-$to2"
+ exit 1
+ fi
+
+ # check deletions are correct
+ from=$(($from+1))
+ $NFT get element inet x y { $from } 1>/dev/null
+ if [ $? -ne 0 ]
+ then
+ echo "failed to get $from"
+ exit 1
+ fi
+
+ to=$(($to-1))
+ $NFT get element inet x y { $to } 1>/dev/null
+ if [ $? -ne 0 ]
+ then
+ echo "failed to get $to"
+ exit 1
+ fi
+
+ from2=$(($from2-1))
+ $NFT get element inet x y { $from2 } 1>/dev/null
+ if [ $? -ne 0 ]
+ then
+ echo "failed to get $from2"
+ exit 1
+ fi
+ to2=$(($to2+1))
+
+ $NFT get element inet x y { $to2 } 1>/dev/null
+ if [ $? -ne 0 ]
+ then
+ echo "failed to get $to2"
+ exit 1
+ fi
+done
+
+rm -f $tmpfile
+rm -f $tmpfile2
+rm -f $tmpfile3
+
+if [ "$HOWMANY" != 65535 ] ; then
+ echo "NFT_TEST_HAS_SOCKET_LIMITS indicates that the socket limit for"
+ echo "/proc/sys/net/core/wmem_max is too small for this test. Mark as SKIPPED"
+ echo "You may bump the limit and rerun with \`NFT_TEST_HAS_SOCKET_LIMITS=n\`."
+ exit 77
+fi
diff --git a/tests/shell/testcases/sets/collapse_elem_0 b/tests/shell/testcases/sets/collapse_elem_0
new file mode 100755
index 00000000..7699e9da
--- /dev/null
+++ b/tests/shell/testcases/sets/collapse_elem_0
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table ip a {
+ set x {
+ type inet_service;
+ }
+}
+table ip6 a {
+ set x {
+ type inet_service;
+ }
+}
+add element ip a x { 1 }
+add element ip a x { 2 }
+add element ip6 a x { 2 }"
+
+$NFT -f - <<< $RULESET
diff --git a/tests/shell/testcases/sets/concat_interval_0 b/tests/shell/testcases/sets/concat_interval_0
new file mode 100755
index 00000000..36138ae0
--- /dev/null
+++ b/tests/shell/testcases/sets/concat_interval_0
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_pipapo)
+
+set -e
+
+RULESET="table ip t {
+ set s {
+ type ipv4_addr . inet_proto . inet_service
+ flags interval
+ counter
+ elements = { 1.0.0.1 . udp . 53 }
+ }
+ set s2 {
+ type ipv4_addr . mark
+ flags interval
+ elements = { 10.10.10.10 . 0x00000100,
+ 20.20.20.20 . 0x00000200 }
+ }
+}"
+
+$NFT -f - <<< $RULESET
+
+$NFT delete element t s { 1.0.0.1 . udp . 53}
+
+exit 0
diff --git a/tests/shell/testcases/sets/dumps/0001named_interval_0.json-nft b/tests/shell/testcases/sets/dumps/0001named_interval_0.json-nft
new file mode 100644
index 00000000..b9c66a21
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0001named_interval_0.json-nft
@@ -0,0 +1,261 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "s1",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "range": [
+ "10.0.0.0",
+ "11.0.0.0"
+ ]
+ },
+ {
+ "prefix": {
+ "addr": "172.16.0.0",
+ "len": 16
+ }
+ }
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "s2",
+ "table": "t",
+ "type": "ipv6_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "prefix": {
+ "addr": "fe00::",
+ "len": 64
+ }
+ },
+ {
+ "range": [
+ "fe11::",
+ "fe22::"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "s3",
+ "table": "t",
+ "type": "inet_proto",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "range": [
+ 10,
+ 20
+ ]
+ },
+ {
+ "range": [
+ 50,
+ 60
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "s4",
+ "table": "t",
+ "type": "inet_service",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "range": [
+ 0,
+ 1024
+ ]
+ },
+ {
+ "range": [
+ 8080,
+ 8082
+ ]
+ },
+ {
+ "range": [
+ 10000,
+ 40000
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "@s1"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip6",
+ "field": "daddr"
+ }
+ },
+ "right": "@s2"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "protocol"
+ }
+ },
+ "right": "@s3"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip6",
+ "field": "nexthdr"
+ }
+ },
+ "right": "@s3"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": "@s4"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0002named_interval_automerging_0.json-nft b/tests/shell/testcases/sets/dumps/0002named_interval_automerging_0.json-nft
new file mode 100644
index 00000000..4c0be670
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0002named_interval_automerging_0.json-nft
@@ -0,0 +1,44 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "prefix": {
+ "addr": "192.168.0.0",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "192.168.1.0",
+ "len": 24
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0003named_interval_missing_flag_0.json-nft b/tests/shell/testcases/sets/dumps/0003named_interval_missing_flag_0.json-nft
new file mode 100644
index 00000000..b6173e9f
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0003named_interval_missing_flag_0.json-nft
@@ -0,0 +1,27 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0004named_interval_shadow_0.json-nft b/tests/shell/testcases/sets/dumps/0004named_interval_shadow_0.json-nft
new file mode 100644
index 00000000..c55858fa
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0004named_interval_shadow_0.json-nft
@@ -0,0 +1,38 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "s",
+ "table": "t",
+ "type": "ipv6_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "prefix": {
+ "addr": "fe00::",
+ "len": 64
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0005named_interval_shadow_0.json-nft b/tests/shell/testcases/sets/dumps/0005named_interval_shadow_0.json-nft
new file mode 100644
index 00000000..a75681f3
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0005named_interval_shadow_0.json-nft
@@ -0,0 +1,38 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "s",
+ "table": "t",
+ "type": "ipv6_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "prefix": {
+ "addr": "fe00::",
+ "len": 48
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0006create_set_0.json-nft b/tests/shell/testcases/sets/dumps/0006create_set_0.json-nft
new file mode 100644
index 00000000..b6173e9f
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0006create_set_0.json-nft
@@ -0,0 +1,27 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0007create_element_0.json-nft b/tests/shell/testcases/sets/dumps/0007create_element_0.json-nft
new file mode 100644
index 00000000..f5a9ac19
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0007create_element_0.json-nft
@@ -0,0 +1,30 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "elem": [
+ "1.1.1.1"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0008comments_interval_0.json-nft b/tests/shell/testcases/sets/dumps/0008comments_interval_0.json-nft
new file mode 100644
index 00000000..c6f5aa68
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0008comments_interval_0.json-nft
@@ -0,0 +1,38 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "elem": {
+ "val": "1.1.1.1",
+ "comment": "test"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0008create_verdict_map_0.json-nft b/tests/shell/testcases/sets/dumps/0008create_verdict_map_0.json-nft
new file mode 100644
index 00000000..fa5dcb25
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0008create_verdict_map_0.json-nft
@@ -0,0 +1,78 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "postrouting",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "sourcemap",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "verdict",
+ "elem": [
+ [
+ "100.123.10.2",
+ {
+ "jump": {
+ "target": "c"
+ }
+ }
+ ]
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "postrouting",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": "@sourcemap"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0009comments_timeout_0.json-nft b/tests/shell/testcases/sets/dumps/0009comments_timeout_0.json-nft
new file mode 100644
index 00000000..2418b39a
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0009comments_timeout_0.json-nft
@@ -0,0 +1,38 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "timeout"
+ ],
+ "elem": [
+ {
+ "elem": {
+ "val": "1.1.1.1",
+ "comment": "test"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0010comments_0.json-nft b/tests/shell/testcases/sets/dumps/0010comments_0.json-nft
new file mode 100644
index 00000000..7ea3c602
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0010comments_0.json-nft
@@ -0,0 +1,35 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "s",
+ "table": "t",
+ "type": "ipv6_addr",
+ "handle": 0,
+ "elem": [
+ {
+ "elem": {
+ "val": "::1",
+ "comment": "test"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0011add_many_elements_0.nodump b/tests/shell/testcases/sets/dumps/0011add_many_elements_0.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0011add_many_elements_0.nodump
diff --git a/tests/shell/testcases/sets/dumps/0012add_delete_many_elements_0.json-nft b/tests/shell/testcases/sets/dumps/0012add_delete_many_elements_0.json-nft
new file mode 100644
index 00000000..c1b7639d
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0012add_delete_many_elements_0.json-nft
@@ -0,0 +1,27 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0013add_delete_many_elements_0.json-nft b/tests/shell/testcases/sets/dumps/0013add_delete_many_elements_0.json-nft
new file mode 100644
index 00000000..c1b7639d
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0013add_delete_many_elements_0.json-nft
@@ -0,0 +1,27 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0014malformed_set_is_not_defined_0.json-nft b/tests/shell/testcases/sets/dumps/0014malformed_set_is_not_defined_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0014malformed_set_is_not_defined_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0014malformed_set_is_not_defined_0.nft b/tests/shell/testcases/sets/dumps/0014malformed_set_is_not_defined_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0014malformed_set_is_not_defined_0.nft
diff --git a/tests/shell/testcases/sets/dumps/0015rulesetflush_0.json-nft b/tests/shell/testcases/sets/dumps/0015rulesetflush_0.json-nft
new file mode 100644
index 00000000..6268e216
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0015rulesetflush_0.json-nft
@@ -0,0 +1,53 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "blacklist_v4",
+ "table": "filter",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "prefix": {
+ "addr": "192.168.0.0",
+ "len": 24
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0016element_leak_0.json-nft b/tests/shell/testcases/sets/dumps/0016element_leak_0.json-nft
new file mode 100644
index 00000000..96b9714a
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0016element_leak_0.json-nft
@@ -0,0 +1,31 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 2,
+ "elem": [
+ "1.1.1.1"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0017add_after_flush_0.json-nft b/tests/shell/testcases/sets/dumps/0017add_after_flush_0.json-nft
new file mode 100644
index 00000000..96b9714a
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0017add_after_flush_0.json-nft
@@ -0,0 +1,31 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 2,
+ "elem": [
+ "1.1.1.1"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0018set_check_size_1.json-nft b/tests/shell/testcases/sets/dumps/0018set_check_size_1.json-nft
new file mode 100644
index 00000000..d226811c
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0018set_check_size_1.json-nft
@@ -0,0 +1,32 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 2,
+ "elem": [
+ "1.1.1.1",
+ "1.1.1.2"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0018set_check_size_1.nft b/tests/shell/testcases/sets/dumps/0018set_check_size_1.nft
new file mode 100644
index 00000000..8cd37076
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0018set_check_size_1.nft
@@ -0,0 +1,7 @@
+table ip x {
+ set s {
+ type ipv4_addr
+ size 2
+ elements = { 1.1.1.1, 1.1.1.2 }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0019set_check_size_0.json-nft b/tests/shell/testcases/sets/dumps/0019set_check_size_0.json-nft
new file mode 100644
index 00000000..d226811c
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0019set_check_size_0.json-nft
@@ -0,0 +1,32 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 2,
+ "elem": [
+ "1.1.1.1",
+ "1.1.1.2"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0020comments_0.json-nft b/tests/shell/testcases/sets/dumps/0020comments_0.json-nft
new file mode 100644
index 00000000..401a8f23
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0020comments_0.json-nft
@@ -0,0 +1,35 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "s",
+ "table": "t",
+ "type": "inet_service",
+ "handle": 0,
+ "elem": [
+ {
+ "elem": {
+ "val": 22,
+ "comment": "test"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0021nesting_0.json-nft b/tests/shell/testcases/sets/dumps/0021nesting_0.json-nft
new file mode 100644
index 00000000..5ed089dc
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0021nesting_0.json-nft
@@ -0,0 +1,69 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": {
+ "set": [
+ {
+ "prefix": {
+ "addr": "1.1.1.0",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "2.2.2.0",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "3.3.3.0",
+ "len": 24
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0022type_selective_flush_0.json-nft b/tests/shell/testcases/sets/dumps/0022type_selective_flush_0.json-nft
new file mode 100644
index 00000000..c6171392
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0022type_selective_flush_0.json-nft
@@ -0,0 +1,101 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "m",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "inet_service"
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "f",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 1024,
+ "flags": [
+ "dynamic"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 80
+ }
+ },
+ {
+ "set": {
+ "op": "add",
+ "elem": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "set": "@f",
+ "stmt": [
+ {
+ "limit": {
+ "rate": 10,
+ "burst": 5,
+ "per": "second"
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0022type_selective_flush_0.nft b/tests/shell/testcases/sets/dumps/0022type_selective_flush_0.nft
index 5a6e3261..38987ded 100644
--- a/tests/shell/testcases/sets/dumps/0022type_selective_flush_0.nft
+++ b/tests/shell/testcases/sets/dumps/0022type_selective_flush_0.nft
@@ -7,7 +7,13 @@ table ip t {
type ipv4_addr : inet_service
}
+ set f {
+ type ipv4_addr
+ size 1024
+ flags dynamic
+ }
+
chain c {
- tcp dport 80 meter f size 1024 { ip saddr limit rate 10/second }
+ tcp dport 80 add @f { ip saddr limit rate 10/second burst 5 packets }
}
}
diff --git a/tests/shell/testcases/sets/dumps/0023incomplete_add_set_command_0.json-nft b/tests/shell/testcases/sets/dumps/0023incomplete_add_set_command_0.json-nft
new file mode 100644
index 00000000..e0e56fec
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0023incomplete_add_set_command_0.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0024named_objects_0.json-nft b/tests/shell/testcases/sets/dumps/0024named_objects_0.json-nft
new file mode 100644
index 00000000..b4521333
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0024named_objects_0.json-nft
@@ -0,0 +1,165 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "counter": {
+ "family": "inet",
+ "name": "user123",
+ "table": "x",
+ "handle": 0,
+ "packets": 12,
+ "bytes": 1433
+ }
+ },
+ {
+ "counter": {
+ "family": "inet",
+ "name": "user321",
+ "table": "x",
+ "handle": 0,
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "quota": {
+ "family": "inet",
+ "name": "user123",
+ "table": "x",
+ "handle": 0,
+ "bytes": 2000,
+ "used": 0,
+ "inv": true
+ }
+ },
+ {
+ "quota": {
+ "family": "inet",
+ "name": "user124",
+ "table": "x",
+ "handle": 0,
+ "bytes": 2000,
+ "used": 0,
+ "inv": true
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "inet",
+ "name": "test",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "quota",
+ "elem": [
+ [
+ "192.168.2.2",
+ "user124"
+ ],
+ [
+ "192.168.2.3",
+ "user124"
+ ]
+ ]
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "y",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "counter": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ "1.1.1.1",
+ "user123"
+ ],
+ [
+ "2.2.2.2",
+ "user123"
+ ],
+ [
+ "192.168.2.2",
+ "user123"
+ ]
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "quota": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": "@test"
+ }
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0024synproxy_0.json-nft b/tests/shell/testcases/sets/dumps/0024synproxy_0.json-nft
new file mode 100644
index 00000000..0af61333
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0024synproxy_0.json-nft
@@ -0,0 +1,131 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "x",
+ "name": "y",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "synproxy": {
+ "family": "inet",
+ "name": "https-synproxy",
+ "table": "x",
+ "handle": 0,
+ "mss": 1460,
+ "wscale": 7,
+ "flags": [
+ "timestamp",
+ "sack-perm"
+ ]
+ }
+ },
+ {
+ "synproxy": {
+ "family": "inet",
+ "name": "other-synproxy",
+ "table": "x",
+ "handle": 0,
+ "mss": 1460,
+ "wscale": 5
+ }
+ },
+ {
+ "map": {
+ "family": "inet",
+ "name": "test2",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "synproxy",
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ [
+ {
+ "prefix": {
+ "addr": "192.168.1.0",
+ "len": 24
+ }
+ },
+ "https-synproxy"
+ ],
+ [
+ {
+ "prefix": {
+ "addr": "192.168.2.0",
+ "len": 24
+ }
+ },
+ "other-synproxy"
+ ]
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "synproxy": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ {
+ "prefix": {
+ "addr": "192.168.1.0",
+ "len": 24
+ }
+ },
+ "https-synproxy"
+ ],
+ [
+ {
+ "prefix": {
+ "addr": "192.168.2.0",
+ "len": 24
+ }
+ },
+ "other-synproxy"
+ ]
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0024synproxy_0.nft b/tests/shell/testcases/sets/dumps/0024synproxy_0.nft
new file mode 100644
index 00000000..e0ee86db
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0024synproxy_0.nft
@@ -0,0 +1,23 @@
+table inet x {
+ synproxy https-synproxy {
+ mss 1460
+ wscale 7
+ timestamp sack-perm
+ }
+
+ synproxy other-synproxy {
+ mss 1460
+ wscale 5
+ }
+
+ map test2 {
+ type ipv4_addr : synproxy
+ flags interval
+ elements = { 192.168.1.0/24 : "https-synproxy", 192.168.2.0/24 : "other-synproxy" }
+ }
+
+ chain y {
+ type filter hook input priority filter; policy accept;
+ synproxy name ip saddr map { 192.168.1.0/24 : "https-synproxy", 192.168.2.0/24 : "other-synproxy" }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0025anonymous_set_0.json-nft b/tests/shell/testcases/sets/dumps/0025anonymous_set_0.json-nft
new file mode 100644
index 00000000..9d56d025
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0025anonymous_set_0.json-nft
@@ -0,0 +1,102 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "right": {
+ "set": [
+ "192.168.0.1",
+ "192.168.0.2",
+ "192.168.0.3"
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oifname"
+ }
+ },
+ "right": "doesntexist"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": {
+ "set": [
+ 22,
+ 23
+ ]
+ }
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0026named_limit_0.json-nft b/tests/shell/testcases/sets/dumps/0026named_limit_0.json-nft
new file mode 100644
index 00000000..5d21f26c
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0026named_limit_0.json-nft
@@ -0,0 +1,75 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "limit": {
+ "family": "ip",
+ "name": "http-traffic",
+ "table": "filter",
+ "handle": 0,
+ "rate": 1,
+ "per": "second",
+ "burst": 5
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "limit": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ 80,
+ "http-traffic"
+ ],
+ [
+ 443,
+ "http-traffic"
+ ]
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0027ipv6_maps_ipv4_0.json-nft b/tests/shell/testcases/sets/dumps/0027ipv6_maps_ipv4_0.json-nft
new file mode 100644
index 00000000..b9251ffa
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0027ipv6_maps_ipv4_0.json-nft
@@ -0,0 +1,38 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "s",
+ "table": "t",
+ "type": "ipv6_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "prefix": {
+ "addr": "::ffff:0.0.0.0",
+ "len": 96
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0028autoselect_0.json-nft b/tests/shell/testcases/sets/dumps/0028autoselect_0.json-nft
new file mode 100644
index 00000000..5968b2e0
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0028autoselect_0.json-nft
@@ -0,0 +1,168 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s1",
+ "table": "t",
+ "type": "inet_proto",
+ "handle": 0,
+ "size": 65535,
+ "flags": [
+ "dynamic"
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s2",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 65535,
+ "flags": [
+ "dynamic"
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s3",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 1024,
+ "flags": [
+ "dynamic"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "foobar"
+ }
+ },
+ {
+ "set": {
+ "op": "add",
+ "elem": {
+ "payload": {
+ "protocol": "ip",
+ "field": "protocol"
+ }
+ },
+ "set": "@s1"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "foobar"
+ }
+ },
+ {
+ "set": {
+ "op": "add",
+ "elem": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "set": "@s2"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "foobar"
+ }
+ },
+ {
+ "set": {
+ "op": "add",
+ "elem": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "set": "@s3"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0028autoselect_0.nft b/tests/shell/testcases/sets/dumps/0028autoselect_0.nft
new file mode 100644
index 00000000..0c604927
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0028autoselect_0.nft
@@ -0,0 +1,26 @@
+table ip t {
+ set s1 {
+ type inet_proto
+ size 65535
+ flags dynamic
+ }
+
+ set s2 {
+ type ipv4_addr
+ size 65535
+ flags dynamic
+ }
+
+ set s3 {
+ type ipv4_addr
+ size 1024
+ flags dynamic
+ }
+
+ chain c {
+ type filter hook input priority filter; policy accept;
+ iifname "foobar" add @s1 { ip protocol }
+ iifname "foobar" add @s2 { ip daddr }
+ iifname "foobar" add @s3 { ip daddr }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0028delete_handle_0.json-nft b/tests/shell/testcases/sets/dumps/0028delete_handle_0.json-nft
new file mode 100644
index 00000000..96314141
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0028delete_handle_0.json-nft
@@ -0,0 +1,53 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test-ip",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "x",
+ "table": "test-ip",
+ "type": "ipv4_addr",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "y",
+ "table": "test-ip",
+ "type": "inet_service",
+ "handle": 0,
+ "flags": [
+ "timeout"
+ ],
+ "timeout": 10845
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "z",
+ "table": "test-ip",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "constant",
+ "interval"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0028delete_handle_0.nft b/tests/shell/testcases/sets/dumps/0028delete_handle_0.nft
new file mode 100644
index 00000000..0f25c763
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0028delete_handle_0.nft
@@ -0,0 +1,15 @@
+table ip test-ip {
+ set x {
+ type ipv4_addr
+ }
+
+ set y {
+ type inet_service
+ timeout 3h45s
+ }
+
+ set z {
+ type ipv4_addr
+ flags constant,interval
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0030add_many_elements_interval_0.nodump b/tests/shell/testcases/sets/dumps/0030add_many_elements_interval_0.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0030add_many_elements_interval_0.nodump
diff --git a/tests/shell/testcases/sets/dumps/0031set_timeout_size_0.nodump b/tests/shell/testcases/sets/dumps/0031set_timeout_size_0.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0031set_timeout_size_0.nodump
diff --git a/tests/shell/testcases/sets/dumps/0032restore_set_simple_0.json-nft b/tests/shell/testcases/sets/dumps/0032restore_set_simple_0.json-nft
new file mode 100644
index 00000000..4d194bff
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0032restore_set_simple_0.json-nft
@@ -0,0 +1,49 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "setA",
+ "table": "filter",
+ "type": [
+ "ipv4_addr",
+ "inet_service",
+ "ipv4_addr"
+ ],
+ "handle": 0,
+ "flags": [
+ "timeout"
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "setB",
+ "table": "filter",
+ "type": [
+ "ipv4_addr",
+ "inet_service"
+ ],
+ "handle": 0,
+ "flags": [
+ "timeout"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0033add_set_simple_flat_0.json-nft b/tests/shell/testcases/sets/dumps/0033add_set_simple_flat_0.json-nft
new file mode 100644
index 00000000..16684438
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0033add_set_simple_flat_0.json-nft
@@ -0,0 +1,49 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "setA",
+ "table": "x",
+ "type": [
+ "ipv4_addr",
+ "inet_service",
+ "ipv4_addr"
+ ],
+ "handle": 0,
+ "flags": [
+ "timeout"
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "setB",
+ "table": "x",
+ "type": [
+ "ipv4_addr",
+ "inet_service"
+ ],
+ "handle": 0,
+ "flags": [
+ "timeout"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0033add_set_simple_flat_0.nft b/tests/shell/testcases/sets/dumps/0033add_set_simple_flat_0.nft
new file mode 100644
index 00000000..d6174c51
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0033add_set_simple_flat_0.nft
@@ -0,0 +1,11 @@
+table ip x {
+ set setA {
+ type ipv4_addr . inet_service . ipv4_addr
+ flags timeout
+ }
+
+ set setB {
+ type ipv4_addr . inet_service
+ flags timeout
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0034get_element_0.json-nft b/tests/shell/testcases/sets/dumps/0034get_element_0.json-nft
new file mode 100644
index 00000000..bfc0e4a0
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0034get_element_0.json-nft
@@ -0,0 +1,140 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "t",
+ "type": "inet_service",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ 10,
+ {
+ "range": [
+ 20,
+ 30
+ ]
+ },
+ 40,
+ {
+ "range": [
+ 50,
+ 60
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "ips",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ "10.0.0.1",
+ {
+ "range": [
+ "10.0.0.5",
+ "10.0.0.8"
+ ]
+ },
+ {
+ "prefix": {
+ "addr": "10.0.0.128",
+ "len": 25
+ }
+ },
+ {
+ "prefix": {
+ "addr": "10.0.1.0",
+ "len": 24
+ }
+ },
+ {
+ "range": [
+ "10.0.2.3",
+ "10.0.2.12"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "cs",
+ "table": "t",
+ "type": [
+ "ipv4_addr",
+ "inet_service"
+ ],
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "concat": [
+ "10.0.0.1",
+ 22
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "10.1.0.0",
+ "len": 16
+ }
+ },
+ {
+ "range": [
+ 1,
+ 1024
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "range": [
+ "10.2.0.1",
+ "10.2.0.8"
+ ]
+ },
+ {
+ "range": [
+ 1024,
+ 65535
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0034get_element_0.nft b/tests/shell/testcases/sets/dumps/0034get_element_0.nft
new file mode 100644
index 00000000..1c1dd977
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0034get_element_0.nft
@@ -0,0 +1,23 @@
+table ip t {
+ set s {
+ type inet_service
+ flags interval
+ elements = { 10, 20-30, 40, 50-60 }
+ }
+
+ set ips {
+ type ipv4_addr
+ flags interval
+ elements = { 10.0.0.1, 10.0.0.5-10.0.0.8,
+ 10.0.0.128/25, 10.0.1.0/24,
+ 10.0.2.3-10.0.2.12 }
+ }
+
+ set cs {
+ type ipv4_addr . inet_service
+ flags interval
+ elements = { 10.0.0.1 . 22,
+ 10.1.0.0/16 . 1-1024,
+ 10.2.0.1-10.2.0.8 . 1024-65535 }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0035add_set_elements_flat_0.json-nft b/tests/shell/testcases/sets/dumps/0035add_set_elements_flat_0.json-nft
new file mode 100644
index 00000000..e4c77147
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0035add_set_elements_flat_0.json-nft
@@ -0,0 +1,30 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0035add_set_elements_flat_0.nft b/tests/shell/testcases/sets/dumps/0035add_set_elements_flat_0.nft
new file mode 100644
index 00000000..ca69cee2
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0035add_set_elements_flat_0.nft
@@ -0,0 +1,6 @@
+table ip x {
+ set y {
+ type ipv4_addr
+ flags interval
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0036add_set_element_expiration_0.nodump b/tests/shell/testcases/sets/dumps/0036add_set_element_expiration_0.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0036add_set_element_expiration_0.nodump
diff --git a/tests/shell/testcases/sets/dumps/0037_set_with_inet_service_0.json-nft b/tests/shell/testcases/sets/dumps/0037_set_with_inet_service_0.json-nft
new file mode 100644
index 00000000..1c3b559d
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0037_set_with_inet_service_0.json-nft
@@ -0,0 +1,159 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "forward",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 0,
+ "policy": "drop"
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "myset",
+ "table": "filter",
+ "type": [
+ "ipv4_addr",
+ "inet_proto",
+ "inet_service"
+ ],
+ "handle": 0,
+ "elem": [
+ {
+ "concat": [
+ "192.168.0.113",
+ "tcp",
+ 22
+ ]
+ },
+ {
+ "concat": [
+ "192.168.0.12",
+ "tcp",
+ 53
+ ]
+ },
+ {
+ "concat": [
+ "192.168.0.12",
+ "udp",
+ 53
+ ]
+ },
+ {
+ "concat": [
+ "192.168.0.12",
+ "tcp",
+ 80
+ ]
+ },
+ {
+ "concat": [
+ "192.168.0.13",
+ "tcp",
+ 80
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "forward",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": [
+ "established",
+ "related"
+ ]
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "forward",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": "new"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "protocol"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "th",
+ "field": "dport"
+ }
+ }
+ ]
+ },
+ "right": "@myset"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0038meter_list_0.json-nft b/tests/shell/testcases/sets/dumps/0038meter_list_0.json-nft
new file mode 100644
index 00000000..5b13f59a
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0038meter_list_0.json-nft
@@ -0,0 +1,96 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 256,
+ "flags": [
+ "timeout",
+ "dynamic"
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "m",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 128,
+ "flags": [
+ "dynamic"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 80
+ }
+ },
+ {
+ "set": {
+ "op": "add",
+ "elem": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "set": "@m",
+ "stmt": [
+ {
+ "limit": {
+ "rate": 10,
+ "burst": 5,
+ "per": "second"
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0038meter_list_0.nft b/tests/shell/testcases/sets/dumps/0038meter_list_0.nft
new file mode 100644
index 00000000..8037dfa5
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0038meter_list_0.nft
@@ -0,0 +1,17 @@
+table ip t {
+ set s {
+ type ipv4_addr
+ size 256
+ flags dynamic,timeout
+ }
+
+ set m {
+ type ipv4_addr
+ size 128
+ flags dynamic
+ }
+
+ chain c {
+ tcp dport 80 add @m { ip saddr limit rate 10/second burst 5 packets }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0039delete_interval_0.json-nft b/tests/shell/testcases/sets/dumps/0039delete_interval_0.json-nft
new file mode 100644
index 00000000..d6e46aad
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0039delete_interval_0.json-nft
@@ -0,0 +1,39 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "range": [
+ "192.168.1.0",
+ "192.168.1.254"
+ ]
+ },
+ "192.168.1.255"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0039delete_interval_0.nft b/tests/shell/testcases/sets/dumps/0039delete_interval_0.nft
new file mode 100644
index 00000000..1fc76572
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0039delete_interval_0.nft
@@ -0,0 +1,7 @@
+table ip t {
+ set s {
+ type ipv4_addr
+ flags interval
+ elements = { 192.168.1.0-192.168.1.254, 192.168.1.255 }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0040get_host_endian_elements_0.json-nft b/tests/shell/testcases/sets/dumps/0040get_host_endian_elements_0.json-nft
new file mode 100644
index 00000000..4b6cf03c
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0040get_host_endian_elements_0.json-nft
@@ -0,0 +1,39 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "t",
+ "type": "mark",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "range": [
+ 35,
+ 66
+ ]
+ },
+ 4919
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0040get_host_endian_elements_0.nft b/tests/shell/testcases/sets/dumps/0040get_host_endian_elements_0.nft
new file mode 100644
index 00000000..f580c381
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0040get_host_endian_elements_0.nft
@@ -0,0 +1,7 @@
+table ip t {
+ set s {
+ type mark
+ flags interval
+ elements = { 0x00000023-0x00000042, 0x00001337 }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0041interval_0.json-nft b/tests/shell/testcases/sets/dumps/0041interval_0.json-nft
new file mode 100644
index 00000000..14a39330
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0041interval_0.json-nft
@@ -0,0 +1,33 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ "192.168.2.196"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0041interval_0.nft b/tests/shell/testcases/sets/dumps/0041interval_0.nft
new file mode 100644
index 00000000..222d4d74
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0041interval_0.nft
@@ -0,0 +1,7 @@
+table ip t {
+ set s {
+ type ipv4_addr
+ flags interval
+ elements = { 192.168.2.196 }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0042update_set_0.json-nft b/tests/shell/testcases/sets/dumps/0042update_set_0.json-nft
new file mode 100644
index 00000000..bc1d4cc2
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0042update_set_0.json-nft
@@ -0,0 +1,87 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "set1",
+ "table": "t",
+ "type": "ether_addr",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "set2",
+ "table": "t",
+ "type": "ether_addr",
+ "handle": 0,
+ "size": 65535,
+ "flags": [
+ "dynamic"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ether",
+ "field": "daddr"
+ }
+ },
+ "right": "@set1"
+ }
+ },
+ {
+ "set": {
+ "op": "add",
+ "elem": {
+ "payload": {
+ "protocol": "ether",
+ "field": "daddr"
+ }
+ },
+ "set": "@set2",
+ "stmt": [
+ {
+ "counter": null
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0042update_set_0.nft b/tests/shell/testcases/sets/dumps/0042update_set_0.nft
new file mode 100644
index 00000000..56cc875e
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0042update_set_0.nft
@@ -0,0 +1,15 @@
+table ip t {
+ set set1 {
+ type ether_addr
+ }
+
+ set set2 {
+ type ether_addr
+ size 65535
+ flags dynamic
+ }
+
+ chain c {
+ ether daddr @set1 add @set2 { ether daddr counter }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0043concatenated_ranges_0.json-nft b/tests/shell/testcases/sets/dumps/0043concatenated_ranges_0.json-nft
new file mode 100644
index 00000000..ffb76e2f
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0043concatenated_ranges_0.json-nft
@@ -0,0 +1,98 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "output",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "map": {
+ "family": "inet",
+ "name": "test",
+ "table": "filter",
+ "type": [
+ "mark",
+ "inet_service",
+ "inet_proto"
+ ],
+ "handle": 0,
+ "map": "mark",
+ "flags": [
+ "interval",
+ "timeout"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "output",
+ "handle": 0,
+ "expr": [
+ {
+ "mangle": {
+ "key": {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ "value": {
+ "map": {
+ "key": {
+ "concat": [
+ {
+ "meta": {
+ "key": "mark"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ {
+ "meta": {
+ "key": "l4proto"
+ }
+ }
+ ]
+ },
+ "data": "@test"
+ }
+ }
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0043concatenated_ranges_0.nft b/tests/shell/testcases/sets/dumps/0043concatenated_ranges_0.nft
new file mode 100644
index 00000000..f2077b91
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0043concatenated_ranges_0.nft
@@ -0,0 +1,11 @@
+table inet filter {
+ map test {
+ type mark . inet_service . inet_proto : mark
+ flags interval,timeout
+ }
+
+ chain output {
+ type filter hook output priority filter; policy accept;
+ meta mark set meta mark . tcp dport . meta l4proto map @test counter packets 0 bytes 0
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0043concatenated_ranges_1.json-nft b/tests/shell/testcases/sets/dumps/0043concatenated_ranges_1.json-nft
new file mode 100644
index 00000000..92b59c86
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0043concatenated_ranges_1.json-nft
@@ -0,0 +1,1723 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip6",
+ "name": "s",
+ "table": "t",
+ "type": [
+ "ipv6_addr",
+ "ipv6_addr"
+ ],
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 32
+ }
+ },
+ {
+ "range": [
+ "2001:db8:20::",
+ "2001:db8:20::20:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 33
+ }
+ },
+ {
+ "range": [
+ "2001:db8:21::",
+ "2001:db8:21::21:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 34
+ }
+ },
+ {
+ "range": [
+ "2001:db8:22::",
+ "2001:db8:22::22:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 35
+ }
+ },
+ {
+ "range": [
+ "2001:db8:23::",
+ "2001:db8:23::23:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 36
+ }
+ },
+ {
+ "range": [
+ "2001:db8:24::",
+ "2001:db8:24::24:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 37
+ }
+ },
+ {
+ "range": [
+ "2001:db8:25::",
+ "2001:db8:25::25:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 38
+ }
+ },
+ {
+ "range": [
+ "2001:db8:26::",
+ "2001:db8:26::26:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 39
+ }
+ },
+ {
+ "range": [
+ "2001:db8:27::",
+ "2001:db8:27::27:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 40
+ }
+ },
+ {
+ "range": [
+ "2001:db8:28::",
+ "2001:db8:28::28:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 41
+ }
+ },
+ {
+ "range": [
+ "2001:db8:29::",
+ "2001:db8:29::29:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 42
+ }
+ },
+ {
+ "range": [
+ "2001:db8:2a::",
+ "2001:db8:2a::2a:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 43
+ }
+ },
+ {
+ "range": [
+ "2001:db8:2b::",
+ "2001:db8:2b::2b:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 44
+ }
+ },
+ {
+ "range": [
+ "2001:db8:2c::",
+ "2001:db8:2c::2c:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 45
+ }
+ },
+ {
+ "range": [
+ "2001:db8:2d::",
+ "2001:db8:2d::2d:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 46
+ }
+ },
+ {
+ "range": [
+ "2001:db8:2e::",
+ "2001:db8:2e::2e:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 47
+ }
+ },
+ {
+ "range": [
+ "2001:db8:2f::",
+ "2001:db8:2f::2f:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 48
+ }
+ },
+ {
+ "range": [
+ "2001:db8:30::",
+ "2001:db8:30::30:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 49
+ }
+ },
+ {
+ "range": [
+ "2001:db8:31::",
+ "2001:db8:31::31:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 50
+ }
+ },
+ {
+ "range": [
+ "2001:db8:32::",
+ "2001:db8:32::32:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 51
+ }
+ },
+ {
+ "range": [
+ "2001:db8:33::",
+ "2001:db8:33::33:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 52
+ }
+ },
+ {
+ "range": [
+ "2001:db8:34::",
+ "2001:db8:34::34:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 53
+ }
+ },
+ {
+ "range": [
+ "2001:db8:35::",
+ "2001:db8:35::35:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 54
+ }
+ },
+ {
+ "range": [
+ "2001:db8:36::",
+ "2001:db8:36::36:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 55
+ }
+ },
+ {
+ "range": [
+ "2001:db8:37::",
+ "2001:db8:37::37:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 56
+ }
+ },
+ {
+ "range": [
+ "2001:db8:38::",
+ "2001:db8:38::38:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 57
+ }
+ },
+ {
+ "range": [
+ "2001:db8:39::",
+ "2001:db8:39::39:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 58
+ }
+ },
+ {
+ "range": [
+ "2001:db8:3a::",
+ "2001:db8:3a::3a:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 59
+ }
+ },
+ {
+ "range": [
+ "2001:db8:3b::",
+ "2001:db8:3b::3b:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 60
+ }
+ },
+ {
+ "range": [
+ "2001:db8:3c::",
+ "2001:db8:3c::3c:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 61
+ }
+ },
+ {
+ "range": [
+ "2001:db8:3d::",
+ "2001:db8:3d::3d:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 62
+ }
+ },
+ {
+ "range": [
+ "2001:db8:3e::",
+ "2001:db8:3e::3e:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 63
+ }
+ },
+ {
+ "range": [
+ "2001:db8:3f::",
+ "2001:db8:3f::3f:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 64
+ }
+ },
+ {
+ "range": [
+ "2001:db8:40::",
+ "2001:db8:40::40:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 65
+ }
+ },
+ {
+ "range": [
+ "2001:db8:41::",
+ "2001:db8:41::41:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 66
+ }
+ },
+ {
+ "range": [
+ "2001:db8:42::",
+ "2001:db8:42::42:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 67
+ }
+ },
+ {
+ "range": [
+ "2001:db8:43::",
+ "2001:db8:43::43:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 68
+ }
+ },
+ {
+ "range": [
+ "2001:db8:44::",
+ "2001:db8:44::44:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 69
+ }
+ },
+ {
+ "range": [
+ "2001:db8:45::",
+ "2001:db8:45::45:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 70
+ }
+ },
+ {
+ "range": [
+ "2001:db8:46::",
+ "2001:db8:46::46:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 71
+ }
+ },
+ {
+ "range": [
+ "2001:db8:47::",
+ "2001:db8:47::47:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 72
+ }
+ },
+ {
+ "range": [
+ "2001:db8:48::",
+ "2001:db8:48::48:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 73
+ }
+ },
+ {
+ "range": [
+ "2001:db8:49::",
+ "2001:db8:49::49:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 74
+ }
+ },
+ {
+ "range": [
+ "2001:db8:4a::",
+ "2001:db8:4a::4a:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 75
+ }
+ },
+ {
+ "range": [
+ "2001:db8:4b::",
+ "2001:db8:4b::4b:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 76
+ }
+ },
+ {
+ "range": [
+ "2001:db8:4c::",
+ "2001:db8:4c::4c:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 77
+ }
+ },
+ {
+ "range": [
+ "2001:db8:4d::",
+ "2001:db8:4d::4d:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 78
+ }
+ },
+ {
+ "range": [
+ "2001:db8:4e::",
+ "2001:db8:4e::4e:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 79
+ }
+ },
+ {
+ "range": [
+ "2001:db8:4f::",
+ "2001:db8:4f::4f:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 80
+ }
+ },
+ {
+ "range": [
+ "2001:db8:50::",
+ "2001:db8:50::50:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 81
+ }
+ },
+ {
+ "range": [
+ "2001:db8:51::",
+ "2001:db8:51::51:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 82
+ }
+ },
+ {
+ "range": [
+ "2001:db8:52::",
+ "2001:db8:52::52:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 83
+ }
+ },
+ {
+ "range": [
+ "2001:db8:53::",
+ "2001:db8:53::53:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 84
+ }
+ },
+ {
+ "range": [
+ "2001:db8:54::",
+ "2001:db8:54::54:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 85
+ }
+ },
+ {
+ "range": [
+ "2001:db8:55::",
+ "2001:db8:55::55:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 86
+ }
+ },
+ {
+ "range": [
+ "2001:db8:56::",
+ "2001:db8:56::56:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 87
+ }
+ },
+ {
+ "range": [
+ "2001:db8:57::",
+ "2001:db8:57::57:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 88
+ }
+ },
+ {
+ "range": [
+ "2001:db8:58::",
+ "2001:db8:58::58:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 89
+ }
+ },
+ {
+ "range": [
+ "2001:db8:59::",
+ "2001:db8:59::59:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 90
+ }
+ },
+ {
+ "range": [
+ "2001:db8:5a::",
+ "2001:db8:5a::5a:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 91
+ }
+ },
+ {
+ "range": [
+ "2001:db8:5b::",
+ "2001:db8:5b::5b:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 92
+ }
+ },
+ {
+ "range": [
+ "2001:db8:5c::",
+ "2001:db8:5c::5c:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 93
+ }
+ },
+ {
+ "range": [
+ "2001:db8:5d::",
+ "2001:db8:5d::5d:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 94
+ }
+ },
+ {
+ "range": [
+ "2001:db8:5e::",
+ "2001:db8:5e::5e:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 95
+ }
+ },
+ {
+ "range": [
+ "2001:db8:5f::",
+ "2001:db8:5f::5f:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 96
+ }
+ },
+ {
+ "range": [
+ "2001:db8:60::",
+ "2001:db8:60::60:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 97
+ }
+ },
+ {
+ "range": [
+ "2001:db8:61::",
+ "2001:db8:61::61:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 98
+ }
+ },
+ {
+ "range": [
+ "2001:db8:62::",
+ "2001:db8:62::62:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 99
+ }
+ },
+ {
+ "range": [
+ "2001:db8:63::",
+ "2001:db8:63::63:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 100
+ }
+ },
+ {
+ "range": [
+ "2001:db8:64::",
+ "2001:db8:64::64:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 101
+ }
+ },
+ {
+ "range": [
+ "2001:db8:65::",
+ "2001:db8:65::65:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 102
+ }
+ },
+ {
+ "range": [
+ "2001:db8:66::",
+ "2001:db8:66::66:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 103
+ }
+ },
+ {
+ "range": [
+ "2001:db8:67::",
+ "2001:db8:67::67:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 104
+ }
+ },
+ {
+ "range": [
+ "2001:db8:68::",
+ "2001:db8:68::68:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 105
+ }
+ },
+ {
+ "range": [
+ "2001:db8:69::",
+ "2001:db8:69::69:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 106
+ }
+ },
+ {
+ "range": [
+ "2001:db8:6a::",
+ "2001:db8:6a::6a:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 107
+ }
+ },
+ {
+ "range": [
+ "2001:db8:6b::",
+ "2001:db8:6b::6b:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 108
+ }
+ },
+ {
+ "range": [
+ "2001:db8:6c::",
+ "2001:db8:6c::6c:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 109
+ }
+ },
+ {
+ "range": [
+ "2001:db8:6d::",
+ "2001:db8:6d::6d:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 110
+ }
+ },
+ {
+ "range": [
+ "2001:db8:6e::",
+ "2001:db8:6e::6e:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 111
+ }
+ },
+ {
+ "range": [
+ "2001:db8:6f::",
+ "2001:db8:6f::6f:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 112
+ }
+ },
+ {
+ "range": [
+ "2001:db8:70::",
+ "2001:db8:70::70:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 113
+ }
+ },
+ {
+ "range": [
+ "2001:db8:71::",
+ "2001:db8:71::71:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 114
+ }
+ },
+ {
+ "range": [
+ "2001:db8:72::",
+ "2001:db8:72::72:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 115
+ }
+ },
+ {
+ "range": [
+ "2001:db8:73::",
+ "2001:db8:73::73:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 116
+ }
+ },
+ {
+ "range": [
+ "2001:db8:74::",
+ "2001:db8:74::74:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 117
+ }
+ },
+ {
+ "range": [
+ "2001:db8:75::",
+ "2001:db8:75::75:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 118
+ }
+ },
+ {
+ "range": [
+ "2001:db8:76::",
+ "2001:db8:76::76:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 119
+ }
+ },
+ {
+ "range": [
+ "2001:db8:77::",
+ "2001:db8:77::77:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 120
+ }
+ },
+ {
+ "range": [
+ "2001:db8:78::",
+ "2001:db8:78::78:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 121
+ }
+ },
+ {
+ "range": [
+ "2001:db8:79::",
+ "2001:db8:79::79:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 122
+ }
+ },
+ {
+ "range": [
+ "2001:db8:7a::",
+ "2001:db8:7a::7a:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 123
+ }
+ },
+ {
+ "range": [
+ "2001:db8:7b::",
+ "2001:db8:7b::7b:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 124
+ }
+ },
+ {
+ "range": [
+ "2001:db8:7c::",
+ "2001:db8:7c::7c:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 125
+ }
+ },
+ {
+ "range": [
+ "2001:db8:7d::",
+ "2001:db8:7d::7d:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 126
+ }
+ },
+ {
+ "range": [
+ "2001:db8:7e::",
+ "2001:db8:7e::7e:1"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "2001:db8::",
+ "len": 127
+ }
+ },
+ {
+ "range": [
+ "2001:db8:7f::",
+ "2001:db8:7f::7f:1"
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "t",
+ "type": [
+ "ipv4_addr",
+ "ipv4_addr"
+ ],
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "192.0.2.0",
+ "len": 24
+ }
+ },
+ {
+ "range": [
+ "192.0.2.72",
+ "192.0.2.74"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "192.0.2.0",
+ "len": 25
+ }
+ },
+ {
+ "range": [
+ "192.0.2.75",
+ "192.0.2.77"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "192.0.2.0",
+ "len": 26
+ }
+ },
+ {
+ "range": [
+ "192.0.2.78",
+ "192.0.2.80"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "192.0.2.0",
+ "len": 27
+ }
+ },
+ {
+ "range": [
+ "192.0.2.81",
+ "192.0.2.83"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "192.0.2.0",
+ "len": 28
+ }
+ },
+ {
+ "range": [
+ "192.0.2.84",
+ "192.0.2.86"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "192.0.2.0",
+ "len": 29
+ }
+ },
+ {
+ "range": [
+ "192.0.2.87",
+ "192.0.2.89"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "192.0.2.0",
+ "len": 30
+ }
+ },
+ {
+ "range": [
+ "192.0.2.90",
+ "192.0.2.92"
+ ]
+ }
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "192.0.2.0",
+ "len": 31
+ }
+ },
+ {
+ "range": [
+ "192.0.2.93",
+ "192.0.2.95"
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0043concatenated_ranges_1.nft b/tests/shell/testcases/sets/dumps/0043concatenated_ranges_1.nft
new file mode 100644
index 00000000..19d08d3d
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0043concatenated_ranges_1.nft
@@ -0,0 +1,116 @@
+table ip6 t {
+ set s {
+ type ipv6_addr . ipv6_addr
+ flags interval
+ elements = { 2001:db8::/32 . 2001:db8:20::-2001:db8:20::20:1,
+ 2001:db8::/33 . 2001:db8:21::-2001:db8:21::21:1,
+ 2001:db8::/34 . 2001:db8:22::-2001:db8:22::22:1,
+ 2001:db8::/35 . 2001:db8:23::-2001:db8:23::23:1,
+ 2001:db8::/36 . 2001:db8:24::-2001:db8:24::24:1,
+ 2001:db8::/37 . 2001:db8:25::-2001:db8:25::25:1,
+ 2001:db8::/38 . 2001:db8:26::-2001:db8:26::26:1,
+ 2001:db8::/39 . 2001:db8:27::-2001:db8:27::27:1,
+ 2001:db8::/40 . 2001:db8:28::-2001:db8:28::28:1,
+ 2001:db8::/41 . 2001:db8:29::-2001:db8:29::29:1,
+ 2001:db8::/42 . 2001:db8:2a::-2001:db8:2a::2a:1,
+ 2001:db8::/43 . 2001:db8:2b::-2001:db8:2b::2b:1,
+ 2001:db8::/44 . 2001:db8:2c::-2001:db8:2c::2c:1,
+ 2001:db8::/45 . 2001:db8:2d::-2001:db8:2d::2d:1,
+ 2001:db8::/46 . 2001:db8:2e::-2001:db8:2e::2e:1,
+ 2001:db8::/47 . 2001:db8:2f::-2001:db8:2f::2f:1,
+ 2001:db8::/48 . 2001:db8:30::-2001:db8:30::30:1,
+ 2001:db8::/49 . 2001:db8:31::-2001:db8:31::31:1,
+ 2001:db8::/50 . 2001:db8:32::-2001:db8:32::32:1,
+ 2001:db8::/51 . 2001:db8:33::-2001:db8:33::33:1,
+ 2001:db8::/52 . 2001:db8:34::-2001:db8:34::34:1,
+ 2001:db8::/53 . 2001:db8:35::-2001:db8:35::35:1,
+ 2001:db8::/54 . 2001:db8:36::-2001:db8:36::36:1,
+ 2001:db8::/55 . 2001:db8:37::-2001:db8:37::37:1,
+ 2001:db8::/56 . 2001:db8:38::-2001:db8:38::38:1,
+ 2001:db8::/57 . 2001:db8:39::-2001:db8:39::39:1,
+ 2001:db8::/58 . 2001:db8:3a::-2001:db8:3a::3a:1,
+ 2001:db8::/59 . 2001:db8:3b::-2001:db8:3b::3b:1,
+ 2001:db8::/60 . 2001:db8:3c::-2001:db8:3c::3c:1,
+ 2001:db8::/61 . 2001:db8:3d::-2001:db8:3d::3d:1,
+ 2001:db8::/62 . 2001:db8:3e::-2001:db8:3e::3e:1,
+ 2001:db8::/63 . 2001:db8:3f::-2001:db8:3f::3f:1,
+ 2001:db8::/64 . 2001:db8:40::-2001:db8:40::40:1,
+ 2001:db8::/65 . 2001:db8:41::-2001:db8:41::41:1,
+ 2001:db8::/66 . 2001:db8:42::-2001:db8:42::42:1,
+ 2001:db8::/67 . 2001:db8:43::-2001:db8:43::43:1,
+ 2001:db8::/68 . 2001:db8:44::-2001:db8:44::44:1,
+ 2001:db8::/69 . 2001:db8:45::-2001:db8:45::45:1,
+ 2001:db8::/70 . 2001:db8:46::-2001:db8:46::46:1,
+ 2001:db8::/71 . 2001:db8:47::-2001:db8:47::47:1,
+ 2001:db8::/72 . 2001:db8:48::-2001:db8:48::48:1,
+ 2001:db8::/73 . 2001:db8:49::-2001:db8:49::49:1,
+ 2001:db8::/74 . 2001:db8:4a::-2001:db8:4a::4a:1,
+ 2001:db8::/75 . 2001:db8:4b::-2001:db8:4b::4b:1,
+ 2001:db8::/76 . 2001:db8:4c::-2001:db8:4c::4c:1,
+ 2001:db8::/77 . 2001:db8:4d::-2001:db8:4d::4d:1,
+ 2001:db8::/78 . 2001:db8:4e::-2001:db8:4e::4e:1,
+ 2001:db8::/79 . 2001:db8:4f::-2001:db8:4f::4f:1,
+ 2001:db8::/80 . 2001:db8:50::-2001:db8:50::50:1,
+ 2001:db8::/81 . 2001:db8:51::-2001:db8:51::51:1,
+ 2001:db8::/82 . 2001:db8:52::-2001:db8:52::52:1,
+ 2001:db8::/83 . 2001:db8:53::-2001:db8:53::53:1,
+ 2001:db8::/84 . 2001:db8:54::-2001:db8:54::54:1,
+ 2001:db8::/85 . 2001:db8:55::-2001:db8:55::55:1,
+ 2001:db8::/86 . 2001:db8:56::-2001:db8:56::56:1,
+ 2001:db8::/87 . 2001:db8:57::-2001:db8:57::57:1,
+ 2001:db8::/88 . 2001:db8:58::-2001:db8:58::58:1,
+ 2001:db8::/89 . 2001:db8:59::-2001:db8:59::59:1,
+ 2001:db8::/90 . 2001:db8:5a::-2001:db8:5a::5a:1,
+ 2001:db8::/91 . 2001:db8:5b::-2001:db8:5b::5b:1,
+ 2001:db8::/92 . 2001:db8:5c::-2001:db8:5c::5c:1,
+ 2001:db8::/93 . 2001:db8:5d::-2001:db8:5d::5d:1,
+ 2001:db8::/94 . 2001:db8:5e::-2001:db8:5e::5e:1,
+ 2001:db8::/95 . 2001:db8:5f::-2001:db8:5f::5f:1,
+ 2001:db8::/96 . 2001:db8:60::-2001:db8:60::60:1,
+ 2001:db8::/97 . 2001:db8:61::-2001:db8:61::61:1,
+ 2001:db8::/98 . 2001:db8:62::-2001:db8:62::62:1,
+ 2001:db8::/99 . 2001:db8:63::-2001:db8:63::63:1,
+ 2001:db8::/100 . 2001:db8:64::-2001:db8:64::64:1,
+ 2001:db8::/101 . 2001:db8:65::-2001:db8:65::65:1,
+ 2001:db8::/102 . 2001:db8:66::-2001:db8:66::66:1,
+ 2001:db8::/103 . 2001:db8:67::-2001:db8:67::67:1,
+ 2001:db8::/104 . 2001:db8:68::-2001:db8:68::68:1,
+ 2001:db8::/105 . 2001:db8:69::-2001:db8:69::69:1,
+ 2001:db8::/106 . 2001:db8:6a::-2001:db8:6a::6a:1,
+ 2001:db8::/107 . 2001:db8:6b::-2001:db8:6b::6b:1,
+ 2001:db8::/108 . 2001:db8:6c::-2001:db8:6c::6c:1,
+ 2001:db8::/109 . 2001:db8:6d::-2001:db8:6d::6d:1,
+ 2001:db8::/110 . 2001:db8:6e::-2001:db8:6e::6e:1,
+ 2001:db8::/111 . 2001:db8:6f::-2001:db8:6f::6f:1,
+ 2001:db8::/112 . 2001:db8:70::-2001:db8:70::70:1,
+ 2001:db8::/113 . 2001:db8:71::-2001:db8:71::71:1,
+ 2001:db8::/114 . 2001:db8:72::-2001:db8:72::72:1,
+ 2001:db8::/115 . 2001:db8:73::-2001:db8:73::73:1,
+ 2001:db8::/116 . 2001:db8:74::-2001:db8:74::74:1,
+ 2001:db8::/117 . 2001:db8:75::-2001:db8:75::75:1,
+ 2001:db8::/118 . 2001:db8:76::-2001:db8:76::76:1,
+ 2001:db8::/119 . 2001:db8:77::-2001:db8:77::77:1,
+ 2001:db8::/120 . 2001:db8:78::-2001:db8:78::78:1,
+ 2001:db8::/121 . 2001:db8:79::-2001:db8:79::79:1,
+ 2001:db8::/122 . 2001:db8:7a::-2001:db8:7a::7a:1,
+ 2001:db8::/123 . 2001:db8:7b::-2001:db8:7b::7b:1,
+ 2001:db8::/124 . 2001:db8:7c::-2001:db8:7c::7c:1,
+ 2001:db8::/125 . 2001:db8:7d::-2001:db8:7d::7d:1,
+ 2001:db8::/126 . 2001:db8:7e::-2001:db8:7e::7e:1,
+ 2001:db8::/127 . 2001:db8:7f::-2001:db8:7f::7f:1 }
+ }
+}
+table ip t {
+ set s {
+ type ipv4_addr . ipv4_addr
+ flags interval
+ elements = { 192.0.2.0/24 . 192.0.2.72-192.0.2.74,
+ 192.0.2.0/25 . 192.0.2.75-192.0.2.77,
+ 192.0.2.0/26 . 192.0.2.78-192.0.2.80,
+ 192.0.2.0/27 . 192.0.2.81-192.0.2.83,
+ 192.0.2.0/28 . 192.0.2.84-192.0.2.86,
+ 192.0.2.0/29 . 192.0.2.87-192.0.2.89,
+ 192.0.2.0/30 . 192.0.2.90-192.0.2.92,
+ 192.0.2.0/31 . 192.0.2.93-192.0.2.95 }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0044interval_overlap_0.nodump b/tests/shell/testcases/sets/dumps/0044interval_overlap_0.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0044interval_overlap_0.nodump
diff --git a/tests/shell/testcases/sets/dumps/0044interval_overlap_1.json-nft b/tests/shell/testcases/sets/dumps/0044interval_overlap_1.json-nft
new file mode 100644
index 00000000..f4aae383
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0044interval_overlap_1.json-nft
@@ -0,0 +1,529 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "t",
+ "type": "inet_service",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ 25,
+ 30,
+ 82,
+ 119,
+ 349,
+ 745,
+ 748,
+ 1165,
+ 1233,
+ 1476,
+ 1550,
+ 1562,
+ 1743,
+ 1745,
+ 1882,
+ 2070,
+ 2194,
+ 2238,
+ 2450,
+ 2455,
+ 2642,
+ 2671,
+ 2906,
+ 3093,
+ 3203,
+ 3287,
+ 3348,
+ 3411,
+ 3540,
+ 3892,
+ 3943,
+ 4133,
+ 4205,
+ 4317,
+ 4733,
+ 5095,
+ 5156,
+ 5223,
+ 5230,
+ 5432,
+ 5826,
+ 5828,
+ 6044,
+ 6377,
+ 6388,
+ 6491,
+ 6952,
+ 6986,
+ 7012,
+ 7187,
+ 7300,
+ 7305,
+ 7549,
+ 7664,
+ 8111,
+ 8206,
+ 8396,
+ 8782,
+ 8920,
+ 8981,
+ 9067,
+ 9216,
+ 9245,
+ 9315,
+ 9432,
+ 9587,
+ 9689,
+ 9844,
+ 9991,
+ 10045,
+ 10252,
+ 10328,
+ 10670,
+ 10907,
+ 11021,
+ 11337,
+ 11427,
+ 11497,
+ 11502,
+ 11523,
+ 11552,
+ 11577,
+ 11721,
+ 11943,
+ 12474,
+ 12718,
+ 12764,
+ 12794,
+ 12922,
+ 13186,
+ 13232,
+ 13383,
+ 13431,
+ 13551,
+ 13676,
+ 13685,
+ 13747,
+ 13925,
+ 13935,
+ 14015,
+ 14090,
+ 14320,
+ 14392,
+ 14515,
+ 14647,
+ 14911,
+ 15096,
+ 15105,
+ 15154,
+ 15440,
+ 15583,
+ 15623,
+ 15677,
+ 15710,
+ 15926,
+ 15934,
+ 15960,
+ 16068,
+ 16166,
+ 16486,
+ 16489,
+ 16528,
+ 16646,
+ 16650,
+ 16770,
+ 16882,
+ 17052,
+ 17237,
+ 17387,
+ 17431,
+ 17886,
+ 17939,
+ 17999,
+ 18092,
+ 18123,
+ 18238,
+ 18562,
+ 18698,
+ 19004,
+ 19229,
+ 19237,
+ 19585,
+ 19879,
+ 19938,
+ 19950,
+ 19958,
+ 20031,
+ 20138,
+ 20157,
+ 20205,
+ 20368,
+ 20682,
+ 20687,
+ 20873,
+ 20910,
+ 20919,
+ 21019,
+ 21068,
+ 21115,
+ 21188,
+ 21236,
+ 21319,
+ 21563,
+ 21734,
+ 21806,
+ 21810,
+ 21959,
+ 21982,
+ 22078,
+ 22181,
+ 22308,
+ 22480,
+ 22643,
+ 22854,
+ 22879,
+ 22961,
+ 23397,
+ 23534,
+ 23845,
+ 23893,
+ 24130,
+ 24406,
+ 24794,
+ 24997,
+ 25019,
+ 25143,
+ 25179,
+ 25439,
+ 25603,
+ 25718,
+ 25859,
+ 25949,
+ 26006,
+ 26022,
+ 26047,
+ 26170,
+ 26193,
+ 26725,
+ 26747,
+ 26924,
+ 27023,
+ 27040,
+ 27233,
+ 27344,
+ 27478,
+ 27593,
+ 27600,
+ 27664,
+ 27678,
+ 27818,
+ 27822,
+ 28003,
+ 28038,
+ 28709,
+ 28808,
+ 29010,
+ 29057,
+ 29228,
+ 29485,
+ 30132,
+ 30160,
+ 30415,
+ 30469,
+ 30673,
+ 30736,
+ 30776,
+ 30780,
+ 31450,
+ 31537,
+ 31669,
+ 31839,
+ 31873,
+ 32019,
+ 32229,
+ 32685,
+ 32879,
+ 33318,
+ 33337,
+ 33404,
+ 33517,
+ 33906,
+ 34214,
+ 34346,
+ 34416,
+ 34727,
+ 34848,
+ 35325,
+ 35400,
+ 35451,
+ 35501,
+ 35637,
+ 35653,
+ 35710,
+ 35761,
+ 35767,
+ 36238,
+ 36258,
+ 36279,
+ 36464,
+ 36586,
+ 36603,
+ 36770,
+ 36774,
+ 36805,
+ 36851,
+ 37079,
+ 37189,
+ 37209,
+ 37565,
+ 37570,
+ 37585,
+ 37832,
+ 37931,
+ 37954,
+ 38006,
+ 38015,
+ 38045,
+ 38109,
+ 38114,
+ 38200,
+ 38209,
+ 38214,
+ 38277,
+ 38306,
+ 38402,
+ 38606,
+ 38697,
+ 38960,
+ 39004,
+ 39006,
+ 39197,
+ 39217,
+ 39265,
+ 39319,
+ 39460,
+ 39550,
+ 39615,
+ 39871,
+ 39886,
+ 40088,
+ 40135,
+ 40244,
+ 40323,
+ 40339,
+ 40355,
+ 40385,
+ 40428,
+ 40538,
+ 40791,
+ 40848,
+ 40959,
+ 41003,
+ 41131,
+ 41349,
+ 41643,
+ 41710,
+ 41826,
+ 41904,
+ 42027,
+ 42148,
+ 42235,
+ 42255,
+ 42498,
+ 42680,
+ 42973,
+ 43118,
+ 43135,
+ 43233,
+ 43349,
+ 43411,
+ 43487,
+ 43840,
+ 43843,
+ 43870,
+ 44040,
+ 44204,
+ 44817,
+ 44883,
+ 44894,
+ 44958,
+ 45201,
+ 45259,
+ 45283,
+ 45357,
+ 45423,
+ 45473,
+ 45498,
+ 45519,
+ 45561,
+ 45611,
+ 45627,
+ 45831,
+ 46043,
+ 46105,
+ 46116,
+ 46147,
+ 46169,
+ 46349,
+ 47147,
+ 47252,
+ 47314,
+ 47335,
+ 47360,
+ 47546,
+ 47617,
+ 47648,
+ 47772,
+ 47793,
+ 47846,
+ 47913,
+ 47952,
+ 48095,
+ 48325,
+ 48334,
+ 48412,
+ 48419,
+ 48540,
+ 48569,
+ 48628,
+ 48751,
+ 48944,
+ 48971,
+ 49008,
+ 49025,
+ 49503,
+ 49505,
+ 49613,
+ 49767,
+ 49839,
+ 49925,
+ 50022,
+ 50028,
+ 50238,
+ 51057,
+ 51477,
+ 51617,
+ 51910,
+ 52044,
+ 52482,
+ 52550,
+ 52643,
+ 52832,
+ 53382,
+ 53690,
+ 53809,
+ 53858,
+ 54001,
+ 54198,
+ 54280,
+ 54327,
+ 54376,
+ 54609,
+ 54776,
+ 54983,
+ 54984,
+ 55019,
+ 55038,
+ 55094,
+ 55368,
+ 55737,
+ 55793,
+ 55904,
+ 55941,
+ 55960,
+ 55978,
+ 56063,
+ 56121,
+ 56314,
+ 56505,
+ 56548,
+ 56568,
+ 56696,
+ 56798,
+ 56855,
+ 57102,
+ 57236,
+ 57333,
+ 57334,
+ 57441,
+ 57574,
+ 57659,
+ 57987,
+ 58325,
+ 58404,
+ 58509,
+ 58782,
+ 58876,
+ 59116,
+ 59544,
+ 59685,
+ 59700,
+ 59750,
+ 59799,
+ 59866,
+ 59870,
+ 59894,
+ 59984,
+ 60343,
+ 60481,
+ 60564,
+ 60731,
+ 61075,
+ 61087,
+ 61148,
+ 61174,
+ 61655,
+ 61679,
+ 61691,
+ 61723,
+ 61730,
+ 61758,
+ 61824,
+ 62035,
+ 62056,
+ 62661,
+ 62768,
+ 62946,
+ 63059,
+ 63116,
+ 63338,
+ 63387,
+ 63672,
+ 63719,
+ 63881,
+ 63995,
+ 64197,
+ 64374,
+ 64377,
+ 64472,
+ 64606,
+ 64662,
+ 64777,
+ 64795,
+ 64906,
+ 65049,
+ 65122,
+ 65318
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0044interval_overlap_1.nft b/tests/shell/testcases/sets/dumps/0044interval_overlap_1.nft
new file mode 100644
index 00000000..5b249a3e
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0044interval_overlap_1.nft
@@ -0,0 +1,106 @@
+table ip t {
+ set s {
+ type inet_service
+ flags interval
+ elements = { 25, 30, 82, 119, 349,
+ 745, 748, 1165, 1233, 1476,
+ 1550, 1562, 1743, 1745, 1882,
+ 2070, 2194, 2238, 2450, 2455,
+ 2642, 2671, 2906, 3093, 3203,
+ 3287, 3348, 3411, 3540, 3892,
+ 3943, 4133, 4205, 4317, 4733,
+ 5095, 5156, 5223, 5230, 5432,
+ 5826, 5828, 6044, 6377, 6388,
+ 6491, 6952, 6986, 7012, 7187,
+ 7300, 7305, 7549, 7664, 8111,
+ 8206, 8396, 8782, 8920, 8981,
+ 9067, 9216, 9245, 9315, 9432,
+ 9587, 9689, 9844, 9991, 10045,
+ 10252, 10328, 10670, 10907, 11021,
+ 11337, 11427, 11497, 11502, 11523,
+ 11552, 11577, 11721, 11943, 12474,
+ 12718, 12764, 12794, 12922, 13186,
+ 13232, 13383, 13431, 13551, 13676,
+ 13685, 13747, 13925, 13935, 14015,
+ 14090, 14320, 14392, 14515, 14647,
+ 14911, 15096, 15105, 15154, 15440,
+ 15583, 15623, 15677, 15710, 15926,
+ 15934, 15960, 16068, 16166, 16486,
+ 16489, 16528, 16646, 16650, 16770,
+ 16882, 17052, 17237, 17387, 17431,
+ 17886, 17939, 17999, 18092, 18123,
+ 18238, 18562, 18698, 19004, 19229,
+ 19237, 19585, 19879, 19938, 19950,
+ 19958, 20031, 20138, 20157, 20205,
+ 20368, 20682, 20687, 20873, 20910,
+ 20919, 21019, 21068, 21115, 21188,
+ 21236, 21319, 21563, 21734, 21806,
+ 21810, 21959, 21982, 22078, 22181,
+ 22308, 22480, 22643, 22854, 22879,
+ 22961, 23397, 23534, 23845, 23893,
+ 24130, 24406, 24794, 24997, 25019,
+ 25143, 25179, 25439, 25603, 25718,
+ 25859, 25949, 26006, 26022, 26047,
+ 26170, 26193, 26725, 26747, 26924,
+ 27023, 27040, 27233, 27344, 27478,
+ 27593, 27600, 27664, 27678, 27818,
+ 27822, 28003, 28038, 28709, 28808,
+ 29010, 29057, 29228, 29485, 30132,
+ 30160, 30415, 30469, 30673, 30736,
+ 30776, 30780, 31450, 31537, 31669,
+ 31839, 31873, 32019, 32229, 32685,
+ 32879, 33318, 33337, 33404, 33517,
+ 33906, 34214, 34346, 34416, 34727,
+ 34848, 35325, 35400, 35451, 35501,
+ 35637, 35653, 35710, 35761, 35767,
+ 36238, 36258, 36279, 36464, 36586,
+ 36603, 36770, 36774, 36805, 36851,
+ 37079, 37189, 37209, 37565, 37570,
+ 37585, 37832, 37931, 37954, 38006,
+ 38015, 38045, 38109, 38114, 38200,
+ 38209, 38214, 38277, 38306, 38402,
+ 38606, 38697, 38960, 39004, 39006,
+ 39197, 39217, 39265, 39319, 39460,
+ 39550, 39615, 39871, 39886, 40088,
+ 40135, 40244, 40323, 40339, 40355,
+ 40385, 40428, 40538, 40791, 40848,
+ 40959, 41003, 41131, 41349, 41643,
+ 41710, 41826, 41904, 42027, 42148,
+ 42235, 42255, 42498, 42680, 42973,
+ 43118, 43135, 43233, 43349, 43411,
+ 43487, 43840, 43843, 43870, 44040,
+ 44204, 44817, 44883, 44894, 44958,
+ 45201, 45259, 45283, 45357, 45423,
+ 45473, 45498, 45519, 45561, 45611,
+ 45627, 45831, 46043, 46105, 46116,
+ 46147, 46169, 46349, 47147, 47252,
+ 47314, 47335, 47360, 47546, 47617,
+ 47648, 47772, 47793, 47846, 47913,
+ 47952, 48095, 48325, 48334, 48412,
+ 48419, 48540, 48569, 48628, 48751,
+ 48944, 48971, 49008, 49025, 49503,
+ 49505, 49613, 49767, 49839, 49925,
+ 50022, 50028, 50238, 51057, 51477,
+ 51617, 51910, 52044, 52482, 52550,
+ 52643, 52832, 53382, 53690, 53809,
+ 53858, 54001, 54198, 54280, 54327,
+ 54376, 54609, 54776, 54983, 54984,
+ 55019, 55038, 55094, 55368, 55737,
+ 55793, 55904, 55941, 55960, 55978,
+ 56063, 56121, 56314, 56505, 56548,
+ 56568, 56696, 56798, 56855, 57102,
+ 57236, 57333, 57334, 57441, 57574,
+ 57659, 57987, 58325, 58404, 58509,
+ 58782, 58876, 59116, 59544, 59685,
+ 59700, 59750, 59799, 59866, 59870,
+ 59894, 59984, 60343, 60481, 60564,
+ 60731, 61075, 61087, 61148, 61174,
+ 61655, 61679, 61691, 61723, 61730,
+ 61758, 61824, 62035, 62056, 62661,
+ 62768, 62946, 63059, 63116, 63338,
+ 63387, 63672, 63719, 63881, 63995,
+ 64197, 64374, 64377, 64472, 64606,
+ 64662, 64777, 64795, 64906, 65049,
+ 65122, 65318 }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0045concat_ipv4_service.json-nft b/tests/shell/testcases/sets/dumps/0045concat_ipv4_service.json-nft
new file mode 100644
index 00000000..8473c333
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0045concat_ipv4_service.json-nft
@@ -0,0 +1,95 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "s",
+ "table": "t",
+ "type": [
+ "ipv4_addr",
+ "inet_service"
+ ],
+ "handle": 0,
+ "size": 65536,
+ "flags": [
+ "timeout",
+ "dynamic"
+ ],
+ "elem": [
+ {
+ "concat": [
+ "192.168.7.1",
+ 22
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 21
+ }
+ },
+ {
+ "set": {
+ "op": "add",
+ "elem": {
+ "elem": {
+ "val": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ 22
+ ]
+ },
+ "timeout": 60
+ }
+ },
+ "set": "@s"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0046netmap_0.json-nft b/tests/shell/testcases/sets/dumps/0046netmap_0.json-nft
new file mode 100644
index 00000000..55f1a2ad
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0046netmap_0.json-nft
@@ -0,0 +1,167 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0,
+ "type": "nat",
+ "hook": "postrouting",
+ "prio": 100,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "snat": {
+ "family": "ip",
+ "addr": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ {
+ "prefix": {
+ "addr": "10.141.11.0",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "192.168.2.0",
+ "len": 24
+ }
+ }
+ ],
+ [
+ {
+ "prefix": {
+ "addr": "10.141.12.0",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "192.168.3.0",
+ "len": 24
+ }
+ }
+ ],
+ [
+ {
+ "prefix": {
+ "addr": "10.141.13.0",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "192.168.4.0",
+ "len": 24
+ }
+ }
+ ]
+ ]
+ }
+ }
+ },
+ "flags": "netmap",
+ "type_flags": "prefix"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "x",
+ "name": "y",
+ "handle": 0,
+ "type": "nat",
+ "hook": "postrouting",
+ "prio": 100,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "snat": {
+ "family": "ip6",
+ "addr": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "ip6",
+ "field": "saddr"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ {
+ "prefix": {
+ "addr": "2001:db8:1111::",
+ "len": 64
+ }
+ },
+ {
+ "prefix": {
+ "addr": "2001:db8:2222::",
+ "len": 64
+ }
+ }
+ ]
+ ]
+ }
+ }
+ },
+ "flags": "netmap",
+ "type_flags": "prefix"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0046netmap_0.nft b/tests/shell/testcases/sets/dumps/0046netmap_0.nft
index e14c3395..5ac6b346 100644
--- a/tests/shell/testcases/sets/dumps/0046netmap_0.nft
+++ b/tests/shell/testcases/sets/dumps/0046netmap_0.nft
@@ -4,3 +4,9 @@ table ip x {
snat ip prefix to ip saddr map { 10.141.11.0/24 : 192.168.2.0/24, 10.141.12.0/24 : 192.168.3.0/24, 10.141.13.0/24 : 192.168.4.0/24 }
}
}
+table ip6 x {
+ chain y {
+ type nat hook postrouting priority srcnat; policy accept;
+ snat ip6 prefix to ip6 saddr map { 2001:db8:1111::/64 : 2001:db8:2222::/64 }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0047nat_0.nft b/tests/shell/testcases/sets/dumps/0047nat_0.nft
index e7968054..9fa9fc74 100644
--- a/tests/shell/testcases/sets/dumps/0047nat_0.nft
+++ b/tests/shell/testcases/sets/dumps/0047nat_0.nft
@@ -6,8 +6,25 @@ table ip x {
10.141.12.0/24 : 192.168.5.10-192.168.5.20 }
}
+ chain x {
+ type nat hook prerouting priority dstnat; policy accept;
+ meta l4proto tcp dnat ip to iifname . ip saddr map { "enp2s0" . 10.1.1.136 : 1.1.2.69 . 22, "enp2s0" . 10.1.1.1-10.1.1.135 : 1.1.2.66-1.84.236.78 . 22 }
+ dnat ip to iifname . ip saddr map { "enp2s0" . 10.1.1.136 : 1.1.2.69/32, "enp2s0" . 10.1.1.1-10.1.1.135 : 1.1.2.66-1.84.236.78 }
+ }
+
chain y {
type nat hook postrouting priority srcnat; policy accept;
snat ip to ip saddr map @y
}
}
+table inet x {
+ chain x {
+ type nat hook prerouting priority dstnat; policy accept;
+ dnat ip to ip daddr . tcp dport map { 10.141.10.1 . 22 : 192.168.2.2, 10.141.11.2 . 2222 : 192.168.4.2 }
+ }
+
+ chain y {
+ type nat hook postrouting priority srcnat; policy accept;
+ snat ip to ip saddr map { 10.141.10.0/24 : 192.168.2.2-192.168.2.4, 10.141.11.0/24 : 192.168.4.2/31 }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0048set_counters_0.json-nft b/tests/shell/testcases/sets/dumps/0048set_counters_0.json-nft
new file mode 100644
index 00000000..62a6a177
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0048set_counters_0.json-nft
@@ -0,0 +1,95 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "z",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "elem": [
+ {
+ "elem": {
+ "val": "192.168.10.35",
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ },
+ {
+ "elem": {
+ "val": "192.168.10.101",
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ },
+ {
+ "elem": {
+ "val": "192.168.10.135",
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ }
+ ],
+ "stmt": [
+ {
+ "counter": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "z",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "right": "@y"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0049set_define_0.json-nft b/tests/shell/testcases/sets/dumps/0049set_define_0.json-nft
new file mode 100644
index 00000000..f8495bab
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0049set_define_0.json-nft
@@ -0,0 +1,94 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "drop"
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "ip-block-4-test",
+ "table": "filter",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "auto-merge": true,
+ "elem": [
+ "1.1.1.1"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": {
+ "set": [
+ 22,
+ 80,
+ 443
+ ]
+ }
+ }
+ },
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": "new"
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0049set_define_0.nft b/tests/shell/testcases/sets/dumps/0049set_define_0.nft
index 998b387a..d654420c 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
diff --git a/tests/shell/testcases/sets/dumps/0050set_define_1.json-nft b/tests/shell/testcases/sets/dumps/0050set_define_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0050set_define_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0050set_define_1.nft b/tests/shell/testcases/sets/dumps/0050set_define_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0050set_define_1.nft
diff --git a/tests/shell/testcases/sets/dumps/0051set_interval_counter_0.json-nft b/tests/shell/testcases/sets/dumps/0051set_interval_counter_0.json-nft
new file mode 100644
index 00000000..b468b5f9
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0051set_interval_counter_0.json-nft
@@ -0,0 +1,85 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "elem": {
+ "val": {
+ "prefix": {
+ "addr": "192.168.2.0",
+ "len": 24
+ }
+ },
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ }
+ ],
+ "stmt": [
+ {
+ "counter": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "right": "@s"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0052overlap_0.json-nft b/tests/shell/testcases/sets/dumps/0052overlap_0.json-nft
new file mode 100644
index 00000000..96d5fbcc
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0052overlap_0.json-nft
@@ -0,0 +1,35 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "w_all",
+ "table": "filter",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "auto-merge": true,
+ "elem": [
+ "10.10.10.10",
+ "10.10.10.253"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0053echo_0.json-nft b/tests/shell/testcases/sets/dumps/0053echo_0.json-nft
new file mode 100644
index 00000000..12a5c4b4
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0053echo_0.json-nft
@@ -0,0 +1,101 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "drop"
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "lo"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": {
+ "prefix": {
+ "addr": "10.0.0.0",
+ "len": 8
+ }
+ }
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "right": "192.168.100.62"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 2001
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0054comments_set_0.json-nft b/tests/shell/testcases/sets/dumps/0054comments_set_0.json-nft
new file mode 100644
index 00000000..3fd6d37e
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0054comments_set_0.json-nft
@@ -0,0 +1,45 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "comment": "test",
+ "flags": [
+ "interval"
+ ]
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "m",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "comment": "another test",
+ "map": "ipv4_addr",
+ "flags": [
+ "interval"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0055tcpflags_0.json-nft b/tests/shell/testcases/sets/dumps/0055tcpflags_0.json-nft
new file mode 100644
index 00000000..e37139f3
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0055tcpflags_0.json-nft
@@ -0,0 +1,138 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "tcp_good_flags",
+ "table": "test",
+ "type": "tcp_flag",
+ "handle": 0,
+ "flags": [
+ "constant"
+ ],
+ "elem": [
+ {
+ "|": [
+ "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"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0055tcpflags_0.nft b/tests/shell/testcases/sets/dumps/0055tcpflags_0.nft
index ffed5426..22bf5c46 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 }
}
}
diff --git a/tests/shell/testcases/sets/dumps/0056dynamic_limit_0.json-nft b/tests/shell/testcases/sets/dumps/0056dynamic_limit_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0056dynamic_limit_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0056dynamic_limit_0.nft b/tests/shell/testcases/sets/dumps/0056dynamic_limit_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0056dynamic_limit_0.nft
diff --git a/tests/shell/testcases/sets/dumps/0057set_create_fails_0.json-nft b/tests/shell/testcases/sets/dumps/0057set_create_fails_0.json-nft
new file mode 100644
index 00000000..79d7257e
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0057set_create_fails_0.json-nft
@@ -0,0 +1,31 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "test",
+ "table": "filter",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 65535,
+ "elem": [
+ "1.1.1.1"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0057set_create_fails_0.nft b/tests/shell/testcases/sets/dumps/0057set_create_fails_0.nft
new file mode 100644
index 00000000..de43d565
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0057set_create_fails_0.nft
@@ -0,0 +1,7 @@
+table inet filter {
+ set test {
+ type ipv4_addr
+ size 65535
+ elements = { 1.1.1.1 }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0058_setupdate_timeout_0.json-nft b/tests/shell/testcases/sets/dumps/0058_setupdate_timeout_0.json-nft
new file mode 100644
index 00000000..ac8d8bef
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0058_setupdate_timeout_0.json-nft
@@ -0,0 +1,68 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "filter",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "ssh_meter",
+ "table": "filter",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 65535,
+ "flags": [
+ "timeout",
+ "dynamic"
+ ],
+ "timeout": 2592000
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "filter",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "set": {
+ "op": "add",
+ "elem": {
+ "elem": {
+ "val": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "timeout": 2592000
+ }
+ },
+ "set": "@ssh_meter"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0059set_update_multistmt_0.json-nft b/tests/shell/testcases/sets/dumps/0059set_update_multistmt_0.json-nft
new file mode 100644
index 00000000..16ecdb2a
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0059set_update_multistmt_0.json-nft
@@ -0,0 +1,79 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "z",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 65535,
+ "flags": [
+ "timeout",
+ "dynamic"
+ ],
+ "timeout": 3600
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "z",
+ "handle": 0,
+ "expr": [
+ {
+ "set": {
+ "op": "update",
+ "elem": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "set": "@y",
+ "stmt": [
+ {
+ "limit": {
+ "rate": 1,
+ "burst": 5,
+ "per": "second"
+ }
+ },
+ {
+ "counter": null
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0059set_update_multistmt_0.nft b/tests/shell/testcases/sets/dumps/0059set_update_multistmt_0.nft
index 1b0ffae4..c1cc3b51 100644
--- a/tests/shell/testcases/sets/dumps/0059set_update_multistmt_0.nft
+++ b/tests/shell/testcases/sets/dumps/0059set_update_multistmt_0.nft
@@ -8,6 +8,6 @@ table ip x {
chain z {
type filter hook output priority filter; policy accept;
- update @y { ip daddr limit rate 1/second counter }
+ update @y { ip daddr limit rate 1/second burst 5 packets counter }
}
}
diff --git a/tests/shell/testcases/sets/dumps/0060set_multistmt_0.json-nft b/tests/shell/testcases/sets/dumps/0060set_multistmt_0.json-nft
new file mode 100644
index 00000000..1aede147
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0060set_multistmt_0.json-nft
@@ -0,0 +1,105 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "elem": [
+ {
+ "elem": {
+ "val": "1.1.1.1",
+ "limit": {
+ "rate": 1,
+ "burst": 5,
+ "per": "second"
+ }
+ }
+ },
+ {
+ "elem": {
+ "val": "4.4.4.4",
+ "limit": {
+ "rate": 1,
+ "burst": 5,
+ "per": "second"
+ }
+ }
+ },
+ {
+ "elem": {
+ "val": "5.5.5.5",
+ "limit": {
+ "rate": 1,
+ "burst": 5,
+ "per": "second"
+ }
+ }
+ }
+ ],
+ "stmt": [
+ {
+ "limit": {
+ "rate": 1,
+ "burst": 5,
+ "per": "second"
+ }
+ },
+ {
+ "counter": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "right": "@y"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0060set_multistmt_0.nft b/tests/shell/testcases/sets/dumps/0060set_multistmt_0.nft
index f23db534..df68fcdf 100644
--- a/tests/shell/testcases/sets/dumps/0060set_multistmt_0.nft
+++ b/tests/shell/testcases/sets/dumps/0060set_multistmt_0.nft
@@ -1,9 +1,9 @@
table ip x {
set y {
type ipv4_addr
- limit rate 1/second counter
- elements = { 1.1.1.1 limit rate 1/second counter packets 0 bytes 0, 4.4.4.4 limit rate 1/second counter packets 0 bytes 0,
- 5.5.5.5 limit rate 1/second counter packets 0 bytes 0 }
+ limit rate 1/second burst 5 packets counter
+ elements = { 1.1.1.1 limit rate 1/second burst 5 packets counter packets 0 bytes 0, 4.4.4.4 limit rate 1/second burst 5 packets counter packets 0 bytes 0,
+ 5.5.5.5 limit rate 1/second burst 5 packets counter packets 0 bytes 0 }
}
chain y {
diff --git a/tests/shell/testcases/sets/dumps/0060set_multistmt_1.json-nft b/tests/shell/testcases/sets/dumps/0060set_multistmt_1.json-nft
new file mode 100644
index 00000000..6098dc56
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0060set_multistmt_1.json-nft
@@ -0,0 +1,105 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 65535,
+ "flags": [
+ "dynamic"
+ ],
+ "elem": [
+ {
+ "elem": {
+ "val": "1.1.1.1",
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ },
+ {
+ "elem": {
+ "val": "1.2.3.4",
+ "counter": {
+ "packets": 9,
+ "bytes": 756
+ }
+ }
+ },
+ {
+ "elem": {
+ "val": "2.2.2.2",
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ }
+ ],
+ "stmt": [
+ {
+ "counter": null
+ },
+ {
+ "quota": {
+ "val": 500,
+ "val_unit": "bytes"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "set": {
+ "op": "update",
+ "elem": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "set": "@y"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0060set_multistmt_1.nft b/tests/shell/testcases/sets/dumps/0060set_multistmt_1.nft
new file mode 100644
index 00000000..ac1bd26b
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0060set_multistmt_1.nft
@@ -0,0 +1,15 @@
+table ip x {
+ set y {
+ type ipv4_addr
+ size 65535
+ flags dynamic
+ counter quota 500 bytes
+ elements = { 1.1.1.1 counter packets 0 bytes 0 quota 500 bytes, 1.2.3.4 counter packets 9 bytes 756 quota 500 bytes used 500 bytes,
+ 2.2.2.2 counter packets 0 bytes 0 quota 1000 bytes }
+ }
+
+ chain y {
+ type filter hook output priority filter; policy accept;
+ update @y { ip daddr }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0061anonymous_automerge_0.json-nft b/tests/shell/testcases/sets/dumps/0061anonymous_automerge_0.json-nft
new file mode 100644
index 00000000..c5591505
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0061anonymous_automerge_0.json-nft
@@ -0,0 +1,57 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": {
+ "set": [
+ {
+ "range": [
+ "1.1.1.1",
+ "1.1.1.2"
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0062set_connlimit_0.json-nft b/tests/shell/testcases/sets/dumps/0062set_connlimit_0.json-nft
new file mode 100644
index 00000000..c5e60e36
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0062set_connlimit_0.json-nft
@@ -0,0 +1,52 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "est-connlimit",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 65535,
+ "flags": [
+ "dynamic"
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "new-connlimit",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 65535,
+ "flags": [
+ "dynamic"
+ ],
+ "stmt": [
+ {
+ "ct count": {
+ "val": 20,
+ "inv": true
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0062set_connlimit_0.nft b/tests/shell/testcases/sets/dumps/0062set_connlimit_0.nft
new file mode 100644
index 00000000..13bbb953
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0062set_connlimit_0.nft
@@ -0,0 +1,14 @@
+table ip x {
+ set est-connlimit {
+ type ipv4_addr
+ size 65535
+ flags dynamic
+ }
+
+ set new-connlimit {
+ type ipv4_addr
+ size 65535
+ flags dynamic
+ ct count over 20
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0063set_catchall_0.json-nft b/tests/shell/testcases/sets/dumps/0063set_catchall_0.json-nft
new file mode 100644
index 00000000..3006f75a
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0063set_catchall_0.json-nft
@@ -0,0 +1,94 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "elem": [
+ {
+ "elem": {
+ "val": "1.1.1.1",
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ },
+ {
+ "elem": {
+ "val": "*",
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ }
+ ],
+ "stmt": [
+ {
+ "counter": null
+ }
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "z",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "elem": {
+ "val": {
+ "prefix": {
+ "addr": "1.1.1.0",
+ "len": 24
+ }
+ },
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ },
+ {
+ "elem": {
+ "val": "*",
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ }
+ ],
+ "stmt": [
+ {
+ "counter": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0064map_catchall_0.json-nft b/tests/shell/testcases/sets/dumps/0064map_catchall_0.json-nft
new file mode 100644
index 00000000..64dd2667
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0064map_catchall_0.json-nft
@@ -0,0 +1,220 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "ipv4_addr",
+ "elem": [
+ [
+ "10.141.0.1",
+ "192.168.0.2"
+ ],
+ [
+ "*",
+ "192.168.0.4"
+ ]
+ ]
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "z",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "ipv4_addr",
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ [
+ {
+ "prefix": {
+ "addr": "10.141.0.0",
+ "len": 24
+ }
+ },
+ "192.168.0.2"
+ ],
+ [
+ "*",
+ "192.168.0.3"
+ ]
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "snat": {
+ "addr": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": "@z"
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "snat": {
+ "addr": {
+ "map": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": {
+ "set": [
+ [
+ {
+ "prefix": {
+ "addr": "10.141.0.0",
+ "len": 24
+ }
+ },
+ "192.168.0.2"
+ ],
+ [
+ "*",
+ "192.168.0.3"
+ ]
+ ]
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "snat": {
+ "addr": {
+ "map": {
+ "key": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ }
+ ]
+ },
+ "data": {
+ "set": [
+ [
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "10.141.0.0",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "10.0.0.0",
+ "len": 8
+ }
+ }
+ ]
+ },
+ "192.168.0.2"
+ ],
+ [
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "192.168.9.0",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "192.168.10.0",
+ "len": 24
+ }
+ }
+ ]
+ },
+ "192.168.0.4"
+ ],
+ [
+ "*",
+ "192.168.0.3"
+ ]
+ ]
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0064map_catchall_0.nft b/tests/shell/testcases/sets/dumps/0064map_catchall_0.nft
index 286683a0..890ed2aa 100644
--- a/tests/shell/testcases/sets/dumps/0064map_catchall_0.nft
+++ b/tests/shell/testcases/sets/dumps/0064map_catchall_0.nft
@@ -9,4 +9,10 @@ table ip x {
flags interval
elements = { 10.141.0.0/24 : 192.168.0.2, * : 192.168.0.3 }
}
+
+ chain y {
+ snat to ip saddr map @z
+ snat to ip saddr map { 10.141.0.0/24 : 192.168.0.2, * : 192.168.0.3 }
+ snat to ip saddr . ip daddr map { 10.141.0.0/24 . 10.0.0.0/8 : 192.168.0.2, 192.168.9.0/24 . 192.168.10.0/24 : 192.168.0.4, * : 192.168.0.3 }
+ }
}
diff --git a/tests/shell/testcases/sets/dumps/0065_icmp_postprocessing.json-nft b/tests/shell/testcases/sets/dumps/0065_icmp_postprocessing.json-nft
new file mode 100644
index 00000000..f470adf3
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0065_icmp_postprocessing.json-nft
@@ -0,0 +1,78 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "foo",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "foo",
+ "handle": 0,
+ "expr": [
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "foo",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "icmp",
+ "field": "type"
+ }
+ },
+ "right": {
+ "set": [
+ "echo-reply",
+ "echo-request"
+ ]
+ }
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "icmp",
+ "field": "id"
+ }
+ },
+ "right": 42
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0065_icmp_postprocessing.nft b/tests/shell/testcases/sets/dumps/0065_icmp_postprocessing.nft
new file mode 100644
index 00000000..461c7a73
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0065_icmp_postprocessing.nft
@@ -0,0 +1,6 @@
+table ip x {
+ chain foo {
+ accept
+ icmp type { echo-reply, echo-request } icmp id 42
+ }
+}
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 3226da15..9ac3774a 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
@@ -17,10 +11,25 @@ table ip nat {
elements = { 1.2.3.4 . 10000-20000 : 192.168.3.4 . 30000-40000 }
}
+ map ipportmap4 {
+ typeof iifname . ip saddr : interval ip daddr
+ flags interval
+ elements = { "enp2s0" . 10.1.1.136 : 1.1.2.69/32,
+ "enp2s0" . 10.1.1.1-10.1.1.135 : 1.1.2.66-1.84.236.78 }
+ }
+
+ map ipportmap5 {
+ typeof iifname . ip saddr : interval ip daddr . tcp dport
+ flags interval
+ elements = { "enp2s0" . 10.1.1.136 : 1.1.2.69 . 22,
+ "enp2s0" . 10.1.1.1-10.1.1.135 : 1.1.2.66-1.84.236.78 . 22 }
+ }
+
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
+ meta l4proto tcp dnat ip to iifname . ip saddr map @ipportmap5
}
}
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 00000000..b6d07fcd
--- /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
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0068interval_stack_overflow_0.nodump b/tests/shell/testcases/sets/dumps/0068interval_stack_overflow_0.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0068interval_stack_overflow_0.nodump
diff --git a/tests/shell/testcases/sets/dumps/0069interval_merge_0.json-nft b/tests/shell/testcases/sets/dumps/0069interval_merge_0.json-nft
new file mode 100644
index 00000000..d7b32f8c
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0069interval_merge_0.json-nft
@@ -0,0 +1,51 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "auto-merge": true,
+ "elem": [
+ {
+ "range": [
+ "1.2.3.0",
+ "1.2.4.255"
+ ]
+ },
+ {
+ "range": [
+ "3.3.3.3",
+ "3.3.3.6"
+ ]
+ },
+ {
+ "range": [
+ "4.4.4.0",
+ "4.4.5.0"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0069interval_merge_0.nft b/tests/shell/testcases/sets/dumps/0069interval_merge_0.nft
new file mode 100644
index 00000000..2d4e1706
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0069interval_merge_0.nft
@@ -0,0 +1,9 @@
+table ip x {
+ set y {
+ type ipv4_addr
+ flags interval
+ auto-merge
+ elements = { 1.2.3.0-1.2.4.255, 3.3.3.3-3.3.3.6,
+ 4.4.4.0-4.4.5.0 }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0070stacked_l2_headers.nft b/tests/shell/testcases/sets/dumps/0070stacked_l2_headers.nft
new file mode 100644
index 00000000..0057e9c6
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0070stacked_l2_headers.nft
@@ -0,0 +1,28 @@
+table netdev nt {
+ set vlanidset {
+ typeof vlan id
+ size 1024
+ flags dynamic,timeout
+ }
+
+ set macset {
+ typeof ether saddr . vlan id
+ size 1024
+ flags dynamic,timeout
+ }
+
+ set ipset {
+ typeof vlan id . ip saddr
+ size 1024
+ flags dynamic,timeout
+ }
+
+ chain nc {
+ update @macset { ether saddr . vlan id timeout 5s } counter packets 0 bytes 0
+ ether saddr . vlan id @macset
+ vlan pcp 1
+ ether saddr 0a:0b:0c:0d:0e:0f vlan id 42
+ update @vlanidset { vlan id timeout 5s } counter packets 0 bytes 0
+ update @ipset { vlan id . ip saddr timeout 5s } counter packets 0 bytes 0
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0071unclosed_prefix_interval_0.json-nft b/tests/shell/testcases/sets/dumps/0071unclosed_prefix_interval_0.json-nft
new file mode 100644
index 00000000..6b579a2e
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0071unclosed_prefix_interval_0.json-nft
@@ -0,0 +1,128 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "t",
+ "name": "c",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "s1",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "prefix": {
+ "addr": "10.0.0.0",
+ "len": 8
+ }
+ },
+ {
+ "prefix": {
+ "addr": "192.0.0.0",
+ "len": 2
+ }
+ }
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "s2",
+ "table": "t",
+ "type": "ipv6_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "prefix": {
+ "addr": "fe80::",
+ "len": 10
+ }
+ },
+ {
+ "prefix": {
+ "addr": "ff00::",
+ "len": 8
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "@s1"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip6",
+ "field": "daddr"
+ }
+ },
+ "right": "@s2"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0071unclosed_prefix_interval_0.nft b/tests/shell/testcases/sets/dumps/0071unclosed_prefix_interval_0.nft
new file mode 100644
index 00000000..4eed94c2
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0071unclosed_prefix_interval_0.nft
@@ -0,0 +1,19 @@
+table inet t {
+ set s1 {
+ type ipv4_addr
+ flags interval
+ elements = { 10.0.0.0/8, 192.0.0.0/2 }
+ }
+
+ set s2 {
+ type ipv6_addr
+ flags interval
+ elements = { fe80::/10,
+ ff00::/8 }
+ }
+
+ chain c {
+ ip saddr @s1 accept
+ ip6 daddr @s2 accept
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0072destroy_0.json-nft b/tests/shell/testcases/sets/dumps/0072destroy_0.json-nft
new file mode 100644
index 00000000..15ec0aac
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0072destroy_0.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0072destroy_0.nft b/tests/shell/testcases/sets/dumps/0072destroy_0.nft
new file mode 100644
index 00000000..5d4d2caf
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0072destroy_0.nft
@@ -0,0 +1,2 @@
+table ip x {
+}
diff --git a/tests/shell/testcases/sets/dumps/0073flat_interval_set.json-nft b/tests/shell/testcases/sets/dumps/0073flat_interval_set.json-nft
new file mode 100644
index 00000000..e2fb6214
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0073flat_interval_set.json-nft
@@ -0,0 +1,52 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "counter": {
+ "family": "inet",
+ "name": "TEST",
+ "table": "filter",
+ "handle": 0,
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "map": {
+ "family": "inet",
+ "name": "testmap",
+ "table": "filter",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "counter",
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ [
+ {
+ "prefix": {
+ "addr": "192.168.0.0",
+ "len": 24
+ }
+ },
+ "TEST"
+ ]
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0073flat_interval_set.nft b/tests/shell/testcases/sets/dumps/0073flat_interval_set.nft
new file mode 100644
index 00000000..20f53741
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0073flat_interval_set.nft
@@ -0,0 +1,11 @@
+table inet filter {
+ counter TEST {
+ packets 0 bytes 0
+ }
+
+ map testmap {
+ type ipv4_addr : counter
+ flags interval
+ elements = { 192.168.0.0/24 : "TEST" }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/0074nested_interval_set.json-nft b/tests/shell/testcases/sets/dumps/0074nested_interval_set.json-nft
new file mode 100644
index 00000000..e2fb6214
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0074nested_interval_set.json-nft
@@ -0,0 +1,52 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "counter": {
+ "family": "inet",
+ "name": "TEST",
+ "table": "filter",
+ "handle": 0,
+ "packets": 0,
+ "bytes": 0
+ }
+ },
+ {
+ "map": {
+ "family": "inet",
+ "name": "testmap",
+ "table": "filter",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "counter",
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ [
+ {
+ "prefix": {
+ "addr": "192.168.0.0",
+ "len": 24
+ }
+ },
+ "TEST"
+ ]
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/0074nested_interval_set.nft b/tests/shell/testcases/sets/dumps/0074nested_interval_set.nft
new file mode 100644
index 00000000..20f53741
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/0074nested_interval_set.nft
@@ -0,0 +1,11 @@
+table inet filter {
+ counter TEST {
+ packets 0 bytes 0
+ }
+
+ map testmap {
+ type ipv4_addr : counter
+ flags interval
+ elements = { 192.168.0.0/24 : "TEST" }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/automerge_0.nodump b/tests/shell/testcases/sets/dumps/automerge_0.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/automerge_0.nodump
diff --git a/tests/shell/testcases/sets/dumps/collapse_elem_0.json-nft b/tests/shell/testcases/sets/dumps/collapse_elem_0.json-nft
new file mode 100644
index 00000000..c713828d
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/collapse_elem_0.json-nft
@@ -0,0 +1,50 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "a",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "x",
+ "table": "a",
+ "type": "inet_service",
+ "handle": 0,
+ "elem": [
+ 1,
+ 2
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "a",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip6",
+ "name": "x",
+ "table": "a",
+ "type": "inet_service",
+ "handle": 0,
+ "elem": [
+ 2
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/collapse_elem_0.nft b/tests/shell/testcases/sets/dumps/collapse_elem_0.nft
new file mode 100644
index 00000000..a3244fc6
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/collapse_elem_0.nft
@@ -0,0 +1,12 @@
+table ip a {
+ set x {
+ type inet_service
+ elements = { 1, 2 }
+ }
+}
+table ip6 a {
+ set x {
+ type inet_service
+ elements = { 2 }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/concat_interval_0.json-nft b/tests/shell/testcases/sets/dumps/concat_interval_0.json-nft
new file mode 100644
index 00000000..d65065e4
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/concat_interval_0.json-nft
@@ -0,0 +1,68 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "t",
+ "type": [
+ "ipv4_addr",
+ "inet_proto",
+ "inet_service"
+ ],
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "stmt": [
+ {
+ "counter": null
+ }
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s2",
+ "table": "t",
+ "type": [
+ "ipv4_addr",
+ "mark"
+ ],
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "concat": [
+ "10.10.10.10",
+ 256
+ ]
+ },
+ {
+ "concat": [
+ "20.20.20.20",
+ 512
+ ]
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/concat_interval_0.nft b/tests/shell/testcases/sets/dumps/concat_interval_0.nft
new file mode 100644
index 00000000..61547c5e
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/concat_interval_0.nft
@@ -0,0 +1,14 @@
+table ip t {
+ set s {
+ type ipv4_addr . inet_proto . inet_service
+ flags interval
+ counter
+ }
+
+ set s2 {
+ type ipv4_addr . mark
+ flags interval
+ elements = { 10.10.10.10 . 0x00000100,
+ 20.20.20.20 . 0x00000200 }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/dynset_missing.json-nft b/tests/shell/testcases/sets/dumps/dynset_missing.json-nft
new file mode 100644
index 00000000..ad8a7cc0
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/dynset_missing.json-nft
@@ -0,0 +1,83 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "test",
+ "name": "output",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "dlist",
+ "table": "test",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 65535,
+ "flags": [
+ "dynamic"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test",
+ "chain": "output",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 1234
+ }
+ },
+ {
+ "set": {
+ "op": "update",
+ "elem": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "set": "@dlist"
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/dynset_missing.nft b/tests/shell/testcases/sets/dumps/dynset_missing.nft
new file mode 100644
index 00000000..6c8ed323
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/dynset_missing.nft
@@ -0,0 +1,12 @@
+table ip test {
+ set dlist {
+ type ipv4_addr
+ size 65535
+ flags dynamic
+ }
+
+ chain output {
+ type filter hook output priority filter; policy accept;
+ udp dport 1234 update @dlist { ip daddr } counter packets 0 bytes 0
+ }
+}
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 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/elem_opts_compat_0.nodump
diff --git a/tests/shell/testcases/sets/dumps/errors_0.json-nft b/tests/shell/testcases/sets/dumps/errors_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/errors_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/errors_0.nft b/tests/shell/testcases/sets/dumps/errors_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/errors_0.nft
diff --git a/tests/shell/testcases/sets/dumps/exact_overlap_0.json-nft b/tests/shell/testcases/sets/dumps/exact_overlap_0.json-nft
new file mode 100644
index 00000000..958d1e5c
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/exact_overlap_0.json-nft
@@ -0,0 +1,110 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "t",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "prefix": {
+ "addr": "1.0.1.0",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "1.0.2.0",
+ "len": 23
+ }
+ },
+ {
+ "prefix": {
+ "addr": "1.0.8.0",
+ "len": 21
+ }
+ },
+ {
+ "prefix": {
+ "addr": "1.0.32.0",
+ "len": 19
+ }
+ },
+ {
+ "prefix": {
+ "addr": "1.1.0.0",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "1.1.2.0",
+ "len": 23
+ }
+ },
+ {
+ "prefix": {
+ "addr": "1.1.4.0",
+ "len": 22
+ }
+ },
+ {
+ "prefix": {
+ "addr": "1.1.8.0",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "1.1.9.0",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "1.1.10.0",
+ "len": 23
+ }
+ },
+ {
+ "prefix": {
+ "addr": "1.1.12.0",
+ "len": 22
+ }
+ },
+ {
+ "prefix": {
+ "addr": "1.1.16.0",
+ "len": 20
+ }
+ },
+ {
+ "prefix": {
+ "addr": "1.1.32.0",
+ "len": 19
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/exact_overlap_0.nft b/tests/shell/testcases/sets/dumps/exact_overlap_0.nft
new file mode 100644
index 00000000..c903e3fc
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/exact_overlap_0.nft
@@ -0,0 +1,13 @@
+table ip t {
+ set s {
+ type ipv4_addr
+ flags interval
+ elements = { 1.0.1.0/24, 1.0.2.0/23,
+ 1.0.8.0/21, 1.0.32.0/19,
+ 1.1.0.0/24, 1.1.2.0/23,
+ 1.1.4.0/22, 1.1.8.0/24,
+ 1.1.9.0/24, 1.1.10.0/23,
+ 1.1.12.0/22, 1.1.16.0/20,
+ 1.1.32.0/19 }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/inner_0.json-nft b/tests/shell/testcases/sets/dumps/inner_0.json-nft
new file mode 100644
index 00000000..8d84e1cc
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/inner_0.json-nft
@@ -0,0 +1,207 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "netdev",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "netdev",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "netdev",
+ "name": "x",
+ "table": "x",
+ "type": [
+ "ipv4_addr",
+ "ipv4_addr"
+ ],
+ "handle": 0,
+ "elem": [
+ {
+ "concat": [
+ "3.3.3.3",
+ "4.4.4.4"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "netdev",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 65535,
+ "flags": [
+ "dynamic"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "netdev",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 4789
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "tunnel": "vxlan",
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "payload": {
+ "tunnel": "vxlan",
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ }
+ ]
+ },
+ "right": {
+ "set": [
+ {
+ "concat": [
+ "1.1.1.1",
+ "2.2.2.2"
+ ]
+ }
+ ]
+ }
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "netdev",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 4789
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "tunnel": "vxlan",
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "payload": {
+ "tunnel": "vxlan",
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ }
+ ]
+ },
+ "right": "@x"
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "netdev",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 4789
+ }
+ },
+ {
+ "set": {
+ "op": "update",
+ "elem": {
+ "payload": {
+ "tunnel": "vxlan",
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "set": "@y"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/inner_0.nft b/tests/shell/testcases/sets/dumps/inner_0.nft
new file mode 100644
index 00000000..925ca777
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/inner_0.nft
@@ -0,0 +1,18 @@
+table netdev x {
+ set x {
+ typeof vxlan ip saddr . vxlan ip daddr
+ elements = { 3.3.3.3 . 4.4.4.4 }
+ }
+
+ set y {
+ typeof vxlan ip saddr
+ size 65535
+ flags dynamic
+ }
+
+ chain y {
+ udp dport 4789 vxlan ip saddr . vxlan ip daddr { 1.1.1.1 . 2.2.2.2 } counter packets 0 bytes 0
+ udp dport 4789 vxlan ip saddr . vxlan ip daddr @x counter packets 0 bytes 0
+ udp dport 4789 update @y { vxlan ip saddr }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/meter_0.json-nft b/tests/shell/testcases/sets/dumps/meter_0.json-nft
new file mode 100644
index 00000000..c318e4f2
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/meter_0.json-nft
@@ -0,0 +1,203 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "test",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip6",
+ "name": "acct_out",
+ "table": "test",
+ "type": [
+ "iface_index",
+ "ipv6_addr"
+ ],
+ "handle": 0,
+ "size": 4096,
+ "flags": [
+ "timeout",
+ "dynamic"
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "ip6",
+ "name": "acct_out2",
+ "table": "test",
+ "type": [
+ "ipv6_addr",
+ "iface_index"
+ ],
+ "handle": 0,
+ "size": 12345,
+ "flags": [
+ "timeout",
+ "dynamic"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "set": {
+ "op": "update",
+ "elem": {
+ "elem": {
+ "val": {
+ "concat": [
+ {
+ "meta": {
+ "key": "iif"
+ }
+ },
+ {
+ "payload": {
+ "protocol": "ip6",
+ "field": "saddr"
+ }
+ }
+ ]
+ },
+ "timeout": 600
+ }
+ },
+ "set": "@acct_out",
+ "stmt": [
+ {
+ "counter": null
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "set": {
+ "op": "update",
+ "elem": {
+ "elem": {
+ "val": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip6",
+ "field": "saddr"
+ }
+ },
+ {
+ "meta": {
+ "key": "iif"
+ }
+ }
+ ]
+ },
+ "timeout": 600
+ }
+ },
+ "set": "@acct_out2",
+ "stmt": [
+ {
+ "counter": null
+ }
+ ]
+ }
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "test",
+ "name": "test",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "xyz",
+ "table": "test",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "size": 8192,
+ "flags": [
+ "timeout",
+ "dynamic"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "test",
+ "chain": "test",
+ "handle": 0,
+ "expr": [
+ {
+ "set": {
+ "op": "update",
+ "elem": {
+ "elem": {
+ "val": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "timeout": 30
+ }
+ },
+ "set": "@xyz",
+ "stmt": [
+ {
+ "counter": null
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/meter_0.nft b/tests/shell/testcases/sets/dumps/meter_0.nft
new file mode 100644
index 00000000..3843f9a9
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/meter_0.nft
@@ -0,0 +1,29 @@
+table ip6 test {
+ set acct_out {
+ type iface_index . ipv6_addr
+ size 4096
+ flags dynamic,timeout
+ }
+
+ set acct_out2 {
+ type ipv6_addr . iface_index
+ size 12345
+ flags dynamic,timeout
+ }
+
+ chain test {
+ update @acct_out { iif . ip6 saddr timeout 10m counter }
+ update @acct_out2 { ip6 saddr . iif timeout 10m counter }
+ }
+}
+table ip test {
+ set xyz {
+ type ipv4_addr
+ size 8192
+ flags dynamic,timeout
+ }
+
+ chain test {
+ update @xyz { ip saddr timeout 30s counter }
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/reset_command_0.nodump b/tests/shell/testcases/sets/dumps/reset_command_0.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/reset_command_0.nodump
diff --git a/tests/shell/testcases/sets/dumps/set_eval_0.json-nft b/tests/shell/testcases/sets/dumps/set_eval_0.json-nft
new file mode 100644
index 00000000..6f692381
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/set_eval_0.json-nft
@@ -0,0 +1,85 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "nat",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "nat",
+ "name": "prerouting",
+ "handle": 0,
+ "type": "nat",
+ "hook": "prerouting",
+ "prio": -100,
+ "policy": "accept"
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "set_with_interval",
+ "table": "nat",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "nat",
+ "chain": "prerouting",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "right": {
+ "set": [
+ "tcp",
+ "udp"
+ ]
+ }
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "th",
+ "field": "dport"
+ }
+ },
+ "right": 443
+ }
+ },
+ {
+ "dnat": {
+ "addr": "10.0.0.1"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/set_eval_0.nft b/tests/shell/testcases/sets/dumps/set_eval_0.nft
new file mode 100644
index 00000000..a45462b8
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/set_eval_0.nft
@@ -0,0 +1,11 @@
+table ip nat {
+ set set_with_interval {
+ type ipv4_addr
+ flags interval
+ }
+
+ chain prerouting {
+ type nat hook prerouting priority dstnat; policy accept;
+ meta l4proto { tcp, udp } th dport 443 dnat to 10.0.0.1
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/sets_with_ifnames.json-nft b/tests/shell/testcases/sets/dumps/sets_with_ifnames.json-nft
new file mode 100644
index 00000000..ac428429
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/sets_with_ifnames.json-nft
@@ -0,0 +1,551 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "testifsets",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "testifsets",
+ "name": "v4icmp",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "testifsets",
+ "name": "v4icmpc",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "testifsets",
+ "name": "input",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "testifsets",
+ "name": "do_nothing",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "simple",
+ "table": "testifsets",
+ "type": "ifname",
+ "handle": 0,
+ "elem": [
+ "abcdef0",
+ "abcdef1",
+ "othername"
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "simple_wild",
+ "table": "testifsets",
+ "type": "ifname",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ "abcdef*",
+ "othername",
+ "ppp0"
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "concat",
+ "table": "testifsets",
+ "type": [
+ "ipv4_addr",
+ "ifname"
+ ],
+ "handle": 0,
+ "elem": [
+ {
+ "concat": [
+ "10.1.2.2",
+ "abcdef0"
+ ]
+ },
+ {
+ "concat": [
+ "10.1.2.2",
+ "abcdef1"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "set": {
+ "family": "inet",
+ "name": "concat_wild",
+ "table": "testifsets",
+ "type": [
+ "ipv4_addr",
+ "ifname"
+ ],
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "concat": [
+ "10.1.2.2",
+ "abcdef*"
+ ]
+ },
+ {
+ "concat": [
+ "10.1.2.1",
+ "bar"
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "1.1.2.0",
+ "len": 24
+ }
+ },
+ "abcdef0"
+ ]
+ },
+ {
+ "concat": [
+ {
+ "prefix": {
+ "addr": "12.2.2.0",
+ "len": 24
+ }
+ },
+ "abcdef*"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "map": {
+ "family": "inet",
+ "name": "map_wild",
+ "table": "testifsets",
+ "type": "ifname",
+ "handle": 0,
+ "map": "verdict",
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ [
+ "abcdef*",
+ {
+ "jump": {
+ "target": "do_nothing"
+ }
+ }
+ ],
+ [
+ "eth0",
+ {
+ "jump": {
+ "target": "do_nothing"
+ }
+ }
+ ]
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "testifsets",
+ "chain": "v4icmp",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "@simple"
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "testifsets",
+ "chain": "v4icmp",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "@simple_wild"
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "testifsets",
+ "chain": "v4icmp",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": {
+ "set": [
+ "eth0",
+ "abcdef0"
+ ]
+ }
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "testifsets",
+ "chain": "v4icmp",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": {
+ "set": [
+ "abcdef*",
+ "eth0"
+ ]
+ }
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "testifsets",
+ "chain": "v4icmp",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "data": "@map_wild"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "testifsets",
+ "chain": "v4icmpc",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "meta": {
+ "key": "iifname"
+ }
+ }
+ ]
+ },
+ "right": "@concat"
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "testifsets",
+ "chain": "v4icmpc",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "meta": {
+ "key": "iifname"
+ }
+ }
+ ]
+ },
+ "right": "@concat_wild"
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "testifsets",
+ "chain": "v4icmpc",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "meta": {
+ "key": "iifname"
+ }
+ }
+ ]
+ },
+ "right": {
+ "set": [
+ {
+ "concat": [
+ "10.1.2.2",
+ "abcdef0"
+ ]
+ }
+ ]
+ }
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "testifsets",
+ "chain": "v4icmpc",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ {
+ "meta": {
+ "key": "iifname"
+ }
+ }
+ ]
+ },
+ "right": {
+ "set": [
+ {
+ "concat": [
+ "10.1.2.2",
+ "abcdef*"
+ ]
+ }
+ ]
+ }
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "testifsets",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "protocol"
+ }
+ },
+ "right": "icmp"
+ }
+ },
+ {
+ "jump": {
+ "target": "v4icmp"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "testifsets",
+ "chain": "input",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "protocol"
+ }
+ },
+ "right": "icmp"
+ }
+ },
+ {
+ "goto": {
+ "target": "v4icmpc"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/sets_with_ifnames.nft b/tests/shell/testcases/sets/dumps/sets_with_ifnames.nft
new file mode 100644
index 00000000..77a8baf5
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/sets_with_ifnames.nft
@@ -0,0 +1,62 @@
+table inet testifsets {
+ set simple {
+ type ifname
+ elements = { "abcdef0",
+ "abcdef1",
+ "othername" }
+ }
+
+ set simple_wild {
+ type ifname
+ flags interval
+ elements = { "abcdef*",
+ "othername",
+ "ppp0" }
+ }
+
+ set concat {
+ type ipv4_addr . ifname
+ elements = { 10.1.2.2 . "abcdef0",
+ 10.1.2.2 . "abcdef1" }
+ }
+
+ set concat_wild {
+ type ipv4_addr . ifname
+ flags interval
+ elements = { 10.1.2.2 . "abcdef*",
+ 10.1.2.1 . "bar",
+ 1.1.2.0/24 . "abcdef0",
+ 12.2.2.0/24 . "abcdef*" }
+ }
+
+ map map_wild {
+ type ifname : verdict
+ flags interval
+ elements = { "abcdef*" : jump do_nothing,
+ "eth0" : jump do_nothing }
+ }
+
+ chain v4icmp {
+ iifname @simple counter packets 0 bytes 0
+ iifname @simple_wild counter packets 0 bytes 0
+ iifname { "eth0", "abcdef0" } counter packets 0 bytes 0
+ iifname { "abcdef*", "eth0" } counter packets 0 bytes 0
+ iifname vmap @map_wild
+ }
+
+ chain v4icmpc {
+ ip saddr . iifname @concat counter packets 0 bytes 0
+ ip saddr . iifname @concat_wild counter packets 0 bytes 0
+ ip saddr . iifname { 10.1.2.2 . "abcdef0" } counter packets 0 bytes 0
+ ip saddr . iifname { 10.1.2.2 . "abcdef*" } counter packets 0 bytes 0
+ }
+
+ chain input {
+ type filter hook input priority filter; policy accept;
+ ip protocol icmp jump v4icmp
+ ip protocol icmp goto v4icmpc
+ }
+
+ chain do_nothing {
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/type_set_symbol.json-nft b/tests/shell/testcases/sets/dumps/type_set_symbol.json-nft
new file mode 100644
index 00000000..e22213ea
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/type_set_symbol.json-nft
@@ -0,0 +1,114 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c1",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c2",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s1",
+ "table": "t",
+ "type": [
+ "ipv4_addr",
+ "ipv4_addr",
+ "inet_service"
+ ],
+ "handle": 0,
+ "size": 65535,
+ "flags": [
+ "timeout",
+ "dynamic"
+ ],
+ "timeout": 10800
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c1",
+ "handle": 0,
+ "expr": [
+ {
+ "set": {
+ "op": "update",
+ "elem": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "10.180.0.4",
+ 80
+ ]
+ },
+ "set": "@s1"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c2",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "concat": [
+ {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "1.2.3.4",
+ 80
+ ]
+ },
+ "right": "@s1"
+ }
+ },
+ {
+ "goto": {
+ "target": "c1"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/sets/dumps/type_set_symbol.nft b/tests/shell/testcases/sets/dumps/type_set_symbol.nft
new file mode 100644
index 00000000..21209f6d
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/type_set_symbol.nft
@@ -0,0 +1,16 @@
+table ip t {
+ set s1 {
+ type ipv4_addr . ipv4_addr . inet_service
+ size 65535
+ flags dynamic,timeout
+ timeout 3h
+ }
+
+ chain c1 {
+ update @s1 { ip saddr . 10.180.0.4 . 80 }
+ }
+
+ chain c2 {
+ ip saddr . 1.2.3.4 . 80 @s1 goto c1
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/typeof_raw_0.nft b/tests/shell/testcases/sets/dumps/typeof_raw_0.nft
new file mode 100644
index 00000000..4d6abaaa
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/typeof_raw_0.nft
@@ -0,0 +1,12 @@
+table inet t {
+ set y {
+ typeof ip daddr . @ih,32,32
+ elements = { 1.1.1.1 . 0x14,
+ 2.2.2.2 . 0x20 }
+ }
+
+ chain y {
+ ip saddr . @nh,32,32 { 1.1.1.1 . 0x14, 2.2.2.2 . 0x1e }
+ ip daddr . @nh,32,32 @y
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/typeof_sets_0.nft b/tests/shell/testcases/sets/dumps/typeof_sets_0.nft
index 565369fb..63fc5b14 100644
--- a/tests/shell/testcases/sets/dumps/typeof_sets_0.nft
+++ b/tests/shell/testcases/sets/dumps/typeof_sets_0.nft
@@ -14,6 +14,52 @@ table inet t {
elements = { 2, 3, 103 }
}
+ set s4 {
+ typeof frag frag-off
+ elements = { 1, 1024 }
+ }
+
+ set s5 {
+ typeof ip option ra value
+ elements = { 1, 1024 }
+ }
+
+ set s6 {
+ typeof tcp option maxseg size
+ elements = { 1, 1024 }
+ }
+
+ set s7 {
+ typeof sctp chunk init num-inbound-streams
+ elements = { 1, 4 }
+ }
+
+ set s8 {
+ typeof ip version
+ elements = { 4, 6 }
+ }
+
+ set s9 {
+ typeof ip hdrlength
+ elements = { 0, 1, 2, 3, 4,
+ 15 }
+ }
+
+ set s10 {
+ typeof iifname . ip saddr . ipsec in reqid
+ elements = { "eth0" . 10.1.1.2 . 42 }
+ }
+
+ set s11 {
+ 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 }
+ }
+
chain c1 {
osf name @s1 accept
}
@@ -21,4 +67,40 @@ table inet t {
chain c2 {
vlan id @s2 accept
}
+
+ chain c4 {
+ frag frag-off @s4 accept
+ }
+
+ chain c5 {
+ ip option ra value @s5 accept
+ }
+
+ chain c6 {
+ tcp option maxseg size @s6 accept
+ }
+
+ chain c7 {
+ sctp chunk init num-inbound-streams @s7 accept
+ }
+
+ chain c8 {
+ ip version @s8 accept
+ }
+
+ chain c9 {
+ ip hdrlength @s9 accept
+ }
+
+ chain c10 {
+ iifname . ip saddr . ipsec in reqid @s10 accept
+ }
+
+ chain c11 {
+ vlan id . ip saddr @s11 accept
+ }
+
+ chain c12 {
+ iifname . ip saddr . meta ipsec @s12 accept
+ }
}
diff --git a/tests/shell/testcases/sets/dumps/typeof_sets_1.nft b/tests/shell/testcases/sets/dumps/typeof_sets_1.nft
new file mode 100644
index 00000000..89cbc835
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/typeof_sets_1.nft
@@ -0,0 +1,15 @@
+table bridge t {
+ set nodhcpvlan {
+ typeof vlan id
+ elements = { 1 }
+ }
+
+ chain c1 {
+ vlan id != @nodhcpvlan vlan type arp counter packets 0 bytes 0 jump c2
+ vlan id != @nodhcpvlan vlan type ip counter packets 0 bytes 0 jump c2
+ vlan id != { 1, 2 } vlan type ip6 counter packets 0 bytes 0 jump c2
+ }
+
+ chain c2 {
+ }
+}
diff --git a/tests/shell/testcases/sets/dumps/typeof_sets_concat.nft b/tests/shell/testcases/sets/dumps/typeof_sets_concat.nft
new file mode 100644
index 00000000..348b5848
--- /dev/null
+++ b/tests/shell/testcases/sets/dumps/typeof_sets_concat.nft
@@ -0,0 +1,23 @@
+table netdev t {
+ set s {
+ typeof ether saddr . vlan id
+ size 2048
+ flags dynamic,timeout
+ }
+
+ chain c {
+ ether type != 8021q add @s { ether saddr . 0 timeout 5s } counter packets 0 bytes 0 return
+ 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
+ }
+}
diff --git a/tests/shell/testcases/sets/dynset_missing b/tests/shell/testcases/sets/dynset_missing
new file mode 100755
index 00000000..fdf5f49e
--- /dev/null
+++ b/tests/shell/testcases/sets/dynset_missing
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+set -e
+
+$NFT -f /dev/stdin <<EOF
+table ip test {
+ chain output { type filter hook output priority 0;
+ }
+}
+EOF
+
+# misses 'flags dynamic'
+$NFT 'add set ip test dlist {type ipv4_addr; }'
+
+# picks rhash backend because 'size' was also missing.
+$NFT 'add rule ip test output udp dport 1234 update @dlist { ip daddr } counter'
+
+tmpfile=$(mktemp)
+
+trap "rm -rf $tmpfile" EXIT
+
+# kernel has forced an 64k upper size, i.e. this restore file
+# has 'size 65536' but no 'flags dynamic'.
+$NFT list ruleset > $tmpfile
+
+# this restore works, because set is still the rhash backend.
+$NFT -f $tmpfile # success
+$NFT flush ruleset
+
+# fails without commit 'attempt to set_eval flag if dynamic updates requested',
+# because set in $tmpfile has 'size x' but no 'flags dynamic'.
+$NFT -f $tmpfile
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 00000000..7563773e
--- /dev/null
+++ b/tests/shell/testcases/sets/elem_opts_compat_0
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_set_expr)
+
+# 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
diff --git a/tests/shell/testcases/sets/errors_0 b/tests/shell/testcases/sets/errors_0
new file mode 100755
index 00000000..27f65df3
--- /dev/null
+++ b/tests/shell/testcases/sets/errors_0
@@ -0,0 +1,69 @@
+#!/bin/bash
+
+RULESET="table ip x {
+ set y {
+ type ipv4_addr
+ flags interval
+ }
+}
+
+delete element ip x y { 2.3.4.5 }"
+
+$NFT -f - <<< $RULESET
+if [ $? -eq 0 ]
+then
+ exit 1
+fi
+
+RULESET="table ip x {
+ set y {
+ type ipv4_addr
+ flags interval
+ }
+}
+
+add element x y { 1.1.1.1/24 }
+delete element x y { 1.1.1.1/24 }
+add element x y { 1.1.1.1/24 }
+delete element x y { 2.2.2.2/24 }"
+
+$NFT -f - <<< $RULESET
+if [ $? -eq 0 ]
+then
+ exit 1
+fi
+
+RULESET="flush ruleset
+create table inet filter
+set inet filter foo {}
+add element inet filter foo { foobar }"
+
+$NFT -f - <<< $RULESET
+if [ $? -eq 0 ]
+then
+ exit 1
+fi
+
+RULESET="table ip x {
+ map x {
+ type ifname . ipv4_addr : verdict
+ elements = { if2 . 10.0.0.2 : jump chain2,
+ if2 . 192.168.0.0/24 : jump chain2 }
+ }
+
+ chain chain2 {}
+}"
+
+$NFT -f - <<< $RULESET
+if [ $? -eq 0 ]
+then
+ exit 1
+fi
+
+RULESET="add set inet filter myset { type ipv4_addr; flags interval; auto-merge }
+add element inet filter myset { 192.168.0.0/24 }
+add element inet filter myset { 192.168.0.2 }
+add element inet filter myset { 192.168.1.0/24 }
+add element inet filter myset { 192.168.1.100 }"
+
+$NFT -f - <<< $RULESET || exit 0
diff --git a/tests/shell/testcases/sets/exact_overlap_0 b/tests/shell/testcases/sets/exact_overlap_0
new file mode 100755
index 00000000..1ce9304a
--- /dev/null
+++ b/tests/shell/testcases/sets/exact_overlap_0
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+RULESET="add table t
+add set t s { type ipv4_addr; flags interval; }
+add element t s { 1.0.1.0/24 }
+add element t s { 1.0.2.0/23 }
+add element t s { 1.0.8.0/21 }
+add element t s { 1.0.32.0/19 }
+add element t s { 1.1.0.0/24 }
+add element t s { 1.1.2.0/23 }
+add element t s { 1.1.4.0/22 }
+add element t s { 1.1.8.0/24 }
+add element t s { 1.1.9.0/24 }
+add element t s { 1.1.10.0/23 }
+add element t s { 1.1.12.0/22 }
+add element t s { 1.1.16.0/20 }
+add element t s { 1.1.32.0/19 }
+add element t s { 1.0.1.0/24 }"
+
+$NFT -f - <<< $RULESET || exit 1
+
+$NFT add element t s { 1.0.1.0/24 }
diff --git a/tests/shell/testcases/sets/inner_0 b/tests/shell/testcases/sets/inner_0
new file mode 100755
index 00000000..39d91bd9
--- /dev/null
+++ b/tests/shell/testcases/sets/inner_0
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_inner_matching)
+
+set -e
+
+RULESET="table netdev x {
+ set x {
+ typeof vxlan ip saddr . vxlan ip daddr
+ elements = {
+ 3.3.3.3 . 4.4.4.4,
+ }
+ }
+
+ set y {
+ typeof vxlan ip saddr
+ flags dynamic
+ }
+
+ chain y {
+ udp dport 4789 vxlan ip saddr . vxlan ip daddr { 1.1.1.1 . 2.2.2.2 } counter
+ udp dport 4789 vxlan ip saddr . vxlan ip daddr @x counter
+ udp dport 4789 update @y { vxlan ip saddr }
+ }
+}"
+
+$NFT -f - <<< $RULESET
diff --git a/tests/shell/testcases/sets/meter_0 b/tests/shell/testcases/sets/meter_0
new file mode 100755
index 00000000..82e6f20a
--- /dev/null
+++ b/tests/shell/testcases/sets/meter_0
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table ip6 test {
+ chain test {
+ meter acct_out size 4096 { meta iif . ip6 saddr timeout 600s counter }
+ meter acct_out2 size 12345 { ip6 saddr . meta iif timeout 600s counter }
+ }
+}
+
+table ip test {
+ chain test {
+ meter xyz size 8192 { ip saddr timeout 30s counter}
+ }
+}"
+
+$NFT -f - <<< $RULESET
diff --git a/tests/shell/testcases/sets/reset_command_0 b/tests/shell/testcases/sets/reset_command_0
new file mode 100755
index 00000000..d38ddb3f
--- /dev/null
+++ b/tests/shell/testcases/sets/reset_command_0
@@ -0,0 +1,87 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_reset_set)
+
+set -e
+
+trap '[[ $? -eq 0 ]] || echo FAIL' EXIT
+
+RULESET="table t {
+ set s {
+ type ipv4_addr . inet_proto . inet_service
+ flags interval, timeout
+ counter
+ timeout 30m
+ elements = {
+ 1.0.0.1 . udp . 53 counter packets 5 bytes 30 expires 20m,
+ 2.0.0.2 . tcp . 22 counter packets 10 bytes 100 timeout 15m expires 10m
+ }
+ }
+ map m {
+ type ipv4_addr : ipv4_addr
+ quota 50 bytes
+ elements = {
+ 1.2.3.4 quota 50 bytes used 10 bytes : 10.2.3.4,
+ 5.6.7.8 quota 100 bytes used 50 bytes : 50.6.7.8
+ }
+ }
+}"
+
+echo -n "applying test ruleset: "
+$NFT -f - <<< "$RULESET"
+echo OK
+
+drop_seconds() {
+ sed 's/[0-9]\+m\?s//g'
+}
+expires_minutes() {
+ sed -n 's/.*expires \([0-9]*\)m.*/\1/p'
+}
+
+echo -n "get set elem matches reset set elem: "
+elem='element t s { 1.0.0.1 . udp . 53 }'
+[[ $($NFT "get $elem ; reset $elem" | \
+ grep 'elements = ' | drop_seconds | uniq | wc -l) == 1 ]]
+echo OK
+
+echo -n "counters are reset, expiry left alone: "
+NEW=$($NFT "get $elem")
+grep -q 'counter packets 0 bytes 0' <<< "$NEW"
+[[ $(expires_minutes <<< "$NEW") -lt 20 ]]
+echo OK
+
+echo -n "get map elem matches reset map elem: "
+elem='element t m { 1.2.3.4 }'
+[[ $($NFT "get $elem ; reset $elem" | \
+ grep 'elements = ' | uniq | wc -l) == 1 ]]
+echo OK
+
+echo -n "quota value is reset: "
+$NFT get element t m '{ 1.2.3.4 }' | grep -q 'quota 50 bytes : 10.2.3.4'
+echo OK
+
+echo -n "other elements remain the same: "
+OUT=$($NFT get element t s '{ 2.0.0.2 . tcp . 22 }')
+grep -q 'counter packets 10 bytes 100 timeout 15m' <<< "$OUT"
+VAL=$(expires_minutes <<< "$OUT")
+[[ $val -lt 10 ]]
+$NFT get element t m '{ 5.6.7.8 }' | grep -q 'quota 100 bytes used 50 bytes'
+echo OK
+
+echo -n "list set matches reset set: "
+EXP=$($NFT list set t s | drop_seconds)
+OUT=$($NFT reset set t s | drop_seconds)
+$DIFF -u <(echo "$EXP") <(echo "$OUT")
+echo OK
+
+echo -n "list map matches reset map: "
+EXP=$($NFT list map t m)
+OUT=$($NFT reset map t m)
+$DIFF -u <(echo "$EXP") <(echo "$OUT")
+echo OK
+
+echo -n "remaining elements are reset: "
+OUT=$($NFT list ruleset)
+grep -q '2.0.0.2 . tcp . 22 counter packets 0 bytes 0' <<< "$OUT"
+grep -q '5.6.7.8 quota 100 bytes : 50.6.7.8' <<< "$OUT"
+echo OK
diff --git a/tests/shell/testcases/sets/set_eval_0 b/tests/shell/testcases/sets/set_eval_0
new file mode 100755
index 00000000..82b6d3bc
--- /dev/null
+++ b/tests/shell/testcases/sets/set_eval_0
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -e
+
+RULESET="table ip nat {
+ set set_with_interval {
+ type ipv4_addr
+ flags interval
+ }
+
+ chain prerouting {
+ type nat hook prerouting priority dstnat; policy accept;
+ meta l4proto { tcp, udp } th dport 443 dnat to 10.0.0.1
+ }
+}"
+
+$NFT -f - <<< $RULESET
diff --git a/tests/shell/testcases/sets/sets_with_ifnames b/tests/shell/testcases/sets/sets_with_ifnames
new file mode 100755
index 00000000..a4bc5072
--- /dev/null
+++ b/tests/shell/testcases/sets/sets_with_ifnames
@@ -0,0 +1,152 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_pipapo)
+
+dumpfile=$(dirname $0)/dumps/$(basename $0).nft
+
+[ -z "$NFT" ] && exit 111
+
+$NFT -f "$dumpfile" || exit 1
+
+rnd=$(mktemp -u XXXXXXXX)
+ns1="nft1ifname-$rnd"
+ns2="nft2ifname-$rnd"
+
+cleanup()
+{
+ ip netns del "$ns1"
+ ip netns del "$ns2"
+}
+
+trap cleanup EXIT
+
+# check a given element is (not) present in the set.
+lookup_elem()
+{
+ local setname=$1
+ local value=$2
+ local fail=$3
+ local expect_result=$4
+ local msg=$5
+
+ result=$(ip netns exec "$ns1" $NFT get element inet testifsets $setname { "$value" } 2>/dev/null | grep "$expect_result" )
+
+ if [ -z "$result" ] && [ $fail -ne 1 ] ; then
+ echo "empty result, expected $expect_result $msg"
+ ip netns exec "$ns1" $NFT get element inet testifsets $setname { "$value" }
+ exit 1
+ fi
+}
+
+check_elem_get()
+{
+ local setname=$1
+ local value=$2
+ local fail=$3
+ local expect_result=$4
+
+ # when query is 'abcde', and set has 'abc*', result is
+ # 'abc*', not 'abcde', so returned element can be different.
+ if [ -z "$expect_result" ]; then
+ expect_result=$ifname
+ fi
+
+ lookup_elem "$setname" "$value" "$fail" "$expect_result" ""
+}
+
+# same, but also delete and re-add the element.
+check_elem()
+{
+ local setname=$1
+ local value=$2
+
+ lookup_elem "$setname" "$value" "0" "$value" "initial check"
+
+ ip netns exec "$ns1" $NFT delete element inet testifsets $setname { "$value" }
+ if [ $? -ne 0 ]; then
+ ip netns exec "$ns1" $NFT list ruleset
+ echo "delete element $setname { $value } failed"
+ exit 1
+ fi
+
+ ip netns exec "$ns1" $NFT add element inet testifsets $setname { "$value" }
+
+ lookup_elem "$setname" "$value" "0" "$value" "check after add/del"
+}
+
+# send pings, check all rules with sets that contain abcdef1 match.
+# there are 4 rules in this chain, 4 should match.
+check_matching_icmp_ppp()
+{
+ pkt=$((RANDOM%10))
+ pkt=$((pkt+1))
+ ip netns exec "$ns1" ping -f -c $pkt 10.1.2.2
+
+ # replies should arrive via 'abcdeg', so, should NOT increment any counters.
+ ip netns exec "$ns1" ping -f -c 100 10.2.2.2
+
+ matches=$(ip netns exec "$ns1" $NFT list chain inet testifsets v4icmp | grep "counter packets $pkt " | wc -l)
+ want=3
+
+ if [ "$matches" -ne $want ] ;then
+ ip netns exec "$ns1" $NFT list ruleset
+ echo "Expected $want matching rules, got $matches, packets $pkt in v4icmp"
+ exit 1
+ fi
+
+ # same, for concat set type.
+
+ matches=$(ip netns exec "$ns1" $NFT list chain inet testifsets v4icmpc | grep "counter packets $pkt " | wc -l)
+
+ if [ "$matches" -ne $want ] ;then
+ ip netns exec "$ns1" $NFT list ruleset
+ echo "Expected $want matching rules, got $matches, packets $pkt in v4icmpc"
+ exit 1
+ fi
+}
+
+ip netns add "$ns1" || exit 111
+ip netns add "$ns2" || exit 111
+ip netns exec "$ns1" $NFT -f "$dumpfile" || exit 3
+
+for n in abcdef0 abcdef1 othername;do
+ check_elem simple $n
+done
+
+check_elem_get simple foo 1
+
+for n in ppp0 othername;do
+ check_elem simple_wild $n
+done
+
+check_elem_get simple_wild enoent 1
+check_elem simple_wild ppp0
+check_elem_get simple_wild abcdefghijk 0 'abcdef\*'
+
+check_elem_get concat '1.2.3.4 . "enoent"' 1
+check_elem_get concat '10.1.2.2 . "abcdef"' 1
+check_elem_get concat '10.1.2.1 . "abcdef1"' 1
+
+check_elem concat '10.1.2.2 . "abcdef0"'
+check_elem concat '10.1.2.2 . "abcdef1"'
+
+set -e
+ip -net "$ns1" link set lo up
+ip -net "$ns2" link set lo up
+ip netns exec "$ns1" ping -f -c 10 127.0.0.1
+
+ip link add abcdef1 netns $ns1 type veth peer name veth0 netns $ns2
+ip link add abcdeg netns $ns1 type veth peer name veth1 netns $ns2
+
+ip -net "$ns1" link set abcdef1 up
+ip -net "$ns2" link set veth0 up
+ip -net "$ns1" link set abcdeg up
+ip -net "$ns2" link set veth1 up
+
+ip -net "$ns1" addr add 10.1.2.1/24 dev abcdef1
+ip -net "$ns1" addr add 10.2.2.1/24 dev abcdeg
+
+ip -net "$ns2" addr add 10.1.2.2/24 dev veth0
+ip -net "$ns2" addr add 10.2.2.2/24 dev veth1
+
+check_matching_icmp_ppp
diff --git a/tests/shell/testcases/sets/type_set_symbol b/tests/shell/testcases/sets/type_set_symbol
new file mode 100755
index 00000000..07820b7c
--- /dev/null
+++ b/tests/shell/testcases/sets/type_set_symbol
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+set -e
+dumpfile=$(dirname $0)/dumps/$(basename $0).nft
+
+$NFT -f "$dumpfile"
diff --git a/tests/shell/testcases/sets/typeof_raw_0 b/tests/shell/testcases/sets/typeof_raw_0
new file mode 100755
index 00000000..66042eb4
--- /dev/null
+++ b/tests/shell/testcases/sets/typeof_raw_0
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+EXPECTED="table inet t {
+ set y {
+ typeof ip daddr . @ih,32,32
+ elements = { 1.1.1.1 . 0x14, 2.2.2.2 . 0x20}
+ }
+
+ chain y {
+ ip saddr . @nh,32,32 { 1.1.1.1 . 0x14, 2.2.2.2 . 0x1e }
+ ip daddr . @nh,32,32 @y
+ }
+}"
+
+set -e
+$NFT -f - <<< $EXPECTED
+
diff --git a/tests/shell/testcases/sets/typeof_sets_0 b/tests/shell/testcases/sets/typeof_sets_0
index 9b2712e5..016227da 100755
--- a/tests/shell/testcases/sets/typeof_sets_0
+++ b/tests/shell/testcases/sets/typeof_sets_0
@@ -4,12 +4,76 @@
# s1 and s2 are identical, they just use different
# ways for declaration.
-EXPECTED="table inet t {
+set -e
+
+die() {
+ printf '%s\n' "$*"
+ exit 1
+}
+
+INPUT_OSF_SET="
set s1 {
typeof osf name
elements = { \"Linux\" }
}
+"
+
+INPUT_FRAG_SET="
+ set s4 {
+ typeof frag frag-off
+ elements = { 1, 1024 }
+ }
+"
+
+INPUT_VERSION_SET="
+ set s8 {
+ typeof ip version
+ elements = { 4, 6 }
+ }
+"
+
+INPUT_OSF_CHAIN="
+ chain c1 {
+ osf name @s1 accept
+ }
+"
+
+INPUT_FRAG_CHAIN="
+ chain c4 {
+ frag frag-off @s4 accept
+ }
+"
+
+INPUT_SCTP_CHAIN="
+ chain c7 {
+ sctp chunk init num-inbound-streams @s7 accept
+ }
+"
+INPUT_VERSION_CHAIN="
+ chain c8 {
+ ip version @s8 accept
+ }
+"
+
+if [ "$NFT_TEST_HAVE_sctp_chunks" = n ] ; then
+ INPUT_SCTP_CHAIN=
+fi
+
+if [ "$NFT_TEST_HAVE_bitshift" = n ] ; then
+ INPUT_FRAG_CHAIN=
+ INPUT_VERSION_CHAIN=
+fi
+if [ "$NFT_TEST_HAVE_osf" = n ] ; then
+ if [ "$((RANDOM % 2))" -eq 1 ] ; then
+ # Regardless of $NFT_TEST_HAVE_osf, we can define the set.
+ # Randomly do so.
+ INPUT_OSF_SET=
+ fi
+ INPUT_OSF_CHAIN=
+fi
+
+INPUT="table inet t {$INPUT_OSF_SET
set s2 {
typeof vlan id
elements = { 2, 3, 103 }
@@ -18,17 +82,162 @@ EXPECTED="table inet t {
set s3 {
typeof meta ibrpvid
elements = { 2, 3, 103 }
+ }$INPUT_FRAG_SET
+
+ set s5 {
+ typeof ip option ra value
+ elements = { 1, 1024 }
}
- chain c1 {
- osf name @s1 accept
+ set s6 {
+ typeof tcp option maxseg size
+ elements = { 1, 1024 }
+ }
+
+ set s7 {
+ typeof sctp chunk init num-inbound-streams
+ elements = { 1, 4 }
+ }$INPUT_VERSION_SET
+
+ set s9 {
+ typeof ip hdrlength
+ elements = { 0, 1, 2, 3, 4, 15 }
}
+ set s10 {
+ typeof meta iifname . ip saddr . ipsec in reqid
+ elements = { \"eth0\" . 10.1.1.2 . 42 }
+ }
+
+ set s11 {
+ 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
}
+$INPUT_FRAG_CHAIN
+ chain c5 {
+ ip option ra value @s5 accept
+ }
+
+ chain c6 {
+ tcp option maxseg size @s6 accept
+ }
+$INPUT_SCTP_CHAIN
+$INPUT_VERSION_CHAIN
+ chain c9 {
+ ip hdrlength @s9 accept
+ }
+
+ chain c10 {
+ meta iifname . ip saddr . ipsec in reqid @s10 accept
+ }
+
+ chain c11 {
+ ether type vlan vlan id . ip saddr @s11 accept
+ }
+
+ chain c12 {
+ meta iifname . ip saddr . meta ipsec @s12 accept
+ }
}"
-set -e
-$NFT -f - <<< $EXPECTED
+EXPECTED="table inet t {$INPUT_OSF_SET
+ set s2 {
+ typeof vlan id
+ elements = { 2, 3, 103 }
+ }
+
+ set s3 {
+ typeof meta ibrpvid
+ elements = { 2, 3, 103 }
+ }
+$INPUT_FRAG_SET
+ set s5 {
+ typeof ip option ra value
+ elements = { 1, 1024 }
+ }
+
+ set s6 {
+ typeof tcp option maxseg size
+ elements = { 1, 1024 }
+ }
+
+ set s7 {
+ typeof sctp chunk init num-inbound-streams
+ elements = { 1, 4 }
+ }
+$INPUT_VERSION_SET
+ set s9 {
+ typeof ip hdrlength
+ elements = { 0, 1, 2, 3, 4,
+ 15 }
+ }
+
+ set s10 {
+ typeof iifname . ip saddr . ipsec in reqid
+ elements = { \"eth0\" . 10.1.1.2 . 42 }
+ }
+
+ set s11 {
+ 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
+ }
+$INPUT_FRAG_CHAIN
+ chain c5 {
+ ip option ra value @s5 accept
+ }
+
+ chain c6 {
+ tcp option maxseg size @s6 accept
+ }
+$INPUT_SCTP_CHAIN$INPUT_VERSION_CHAIN
+ chain c9 {
+ ip hdrlength @s9 accept
+ }
+
+ chain c10 {
+ iifname . ip saddr . ipsec in reqid @s10 accept
+ }
+
+ chain c11 {
+ vlan id . ip saddr @s11 accept
+ }
+
+ chain c12 {
+ iifname . ip saddr . meta ipsec @s12 accept
+ }
+}"
+
+
+$NFT -f - <<< "$INPUT" || die $'nft command failed to process input:\n'">$INPUT<"
+
+$DIFF -u <($NFT list ruleset) - <<<"$EXPECTED" || die $'diff failed between ruleset and expected data.\nExpected:\n'">$EXPECTED<"
+if [ "$NFT_TEST_HAVE_bitshift" = n ] ; then
+ echo "Partial test due to NFT_TEST_HAVE_bitshift=n. Skip"
+ exit 77
+fi
+if [ "$NFT_TEST_HAVE_osf" = n ] ; then
+ echo "Partial test due to NFT_TEST_HAVE_osf=n. Skip"
+ exit 77
+fi
+if [ "$NFT_TEST_HAVE_sctp_chunks" = n ] ; then
+ echo "Partial test due to NFT_TEST_HAVE_sctp_chunks=n. Skip"
+ exit 77
+fi
diff --git a/tests/shell/testcases/sets/typeof_sets_1 b/tests/shell/testcases/sets/typeof_sets_1
new file mode 100755
index 00000000..e520270c
--- /dev/null
+++ b/tests/shell/testcases/sets/typeof_sets_1
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+# regression test for corner case in netlink_delinearize
+
+EXPECTED="table bridge t {
+ set nodhcpvlan {
+ typeof vlan id
+ elements = { 1 }
+ }
+
+ chain c1 {
+ vlan id != @nodhcpvlan vlan type arp counter packets 0 bytes 0 jump c2
+ vlan id != @nodhcpvlan vlan type ip counter packets 0 bytes 0 jump c2
+ vlan id != { 1, 2 } vlan type ip6 counter packets 0 bytes 0 jump c2
+ }
+
+ chain c2 {
+ }
+}"
+
+set -e
+$NFT -f - <<< $EXPECTED
diff --git a/tests/shell/testcases/sets/typeof_sets_concat b/tests/shell/testcases/sets/typeof_sets_concat
new file mode 100755
index 00000000..34465f1d
--- /dev/null
+++ b/tests/shell/testcases/sets/typeof_sets_concat
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_pipapo)
+
+set -e
+dumpfile=$(dirname $0)/dumps/$(basename $0).nft
+
+$NFT -f "$dumpfile"
diff --git a/tests/shell/testcases/transactions/0049huge_0 b/tests/shell/testcases/transactions/0049huge_0
index 684d27a1..f66953c2 100755
--- a/tests/shell/testcases/transactions/0049huge_0
+++ b/tests/shell/testcases/transactions/0049huge_0
@@ -7,6 +7,15 @@ $NFT add table inet test
$NFT add chain inet test c
RULE_COUNT=3000
+
+if [ "$NFT_TEST_HAS_SOCKET_LIMITS" = y ] ; then
+ # The socket limit /proc/sys/net/core/rmem_max may be unsuitable for
+ # the test.
+ #
+ # Run only a subset of the test and mark as skipped at the end.
+ RULE_COUNT=500
+fi
+
RULESET=$(
for ((i = 0; i < ${RULE_COUNT}; i++)); do
echo "add rule inet test c accept comment rule$i"
@@ -28,7 +37,10 @@ done
echo '{"add": {"rule": {"family": "inet", "table": "test", "chain": "c", "expr": [{"accept": null}], "comment": "rule'$((${RULE_COUNT} - 1))'"}}}'
echo ']}'
)
-test $($NFT -j -e -a -f - <<< "$RULESET" |sed 's/\({"add":\)/\n\1/g' |grep '"handle"' |wc -l) -eq ${RULE_COUNT} || exit 1
+
+if [ "$NFT_TEST_HAVE_json" != n ]; then
+ test $($NFT -j -e -a -f - <<< "$RULESET" |sed 's/\({"add":\)/\n\1/g' |grep '"handle"' |wc -l) -eq ${RULE_COUNT} || exit 1
+fi
# Now an example from firewalld's testsuite
#
@@ -38,4 +50,18 @@ RULESET='{"nftables": [{"metainfo": {"json_schema_version": 1}}, {"add": {"table
{"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PREROUTING", "type": "filter", "hook": "prerouting", "prio": -290}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PREROUTING_ZONES"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING", "expr": [{"jump": {"target": "raw_PREROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PREROUTING", "type": "filter", "hook": "prerouting", "prio": -140}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PREROUTING_ZONES"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PREROUTING", "expr": [{"jump": {"target": "mangle_PREROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PREROUTING", "type": "nat", "hook": "prerouting", "prio": -90}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PREROUTING_ZONES"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PREROUTING", "expr": [{"jump": {"target": "nat_PREROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POSTROUTING", "type": "nat", "hook": "postrouting", "prio": 110}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POSTROUTING_ZONES"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POSTROUTING", "expr": [{"jump": {"target": "nat_POSTROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PREROUTING", "type": "nat", "hook": "prerouting", "prio": -90}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PREROUTING_ZONES"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PREROUTING", "expr": [{"jump": {"target": "nat_PREROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POSTROUTING", "type": "nat", "hook": "postrouting", "prio": 110}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POSTROUTING_ZONES"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POSTROUTING", "expr": [{"jump": {"target": "nat_POSTROUTING_ZONES"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_INPUT", "type": "filter", "hook": "input", "prio": 10}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FORWARD", "type": "filter", "hook": "forward", "prio": 10}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_OUTPUT", "type": "filter", "hook": "output", "prio": 10}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_INPUT_ZONES"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["established", "related"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"match": {"left": {"ct": {"key": "status"}}, "op": "in", "right": "dnat"}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "lo"}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"jump": {"target": "filter_INPUT_ZONES"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["invalid"]}}}, {"drop": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT", "expr": [{"reject": {"type": "icmpx", "expr": "admin-prohibited"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FORWARD_IN_ZONES"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FORWARD_OUT_ZONES"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["established", "related"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"match": {"left": {"ct": {"key": "status"}}, "op": "in", "right": "dnat"}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "lo"}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"jump": {"target": "filter_FORWARD_IN_ZONES"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"jump": {"target": "filter_FORWARD_OUT_ZONES"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["invalid"]}}}, {"drop": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "expr": [{"reject": {"type": "icmpx", "expr": "admin-prohibited"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_OUTPUT", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "lo"}}, {"accept": null}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING", "expr": [{"match": {"left": {"meta": {"key": "nfproto"}}, "op": "==", "right": "ipv6"}}, {"match": {"left": {"fib": {"flags": ["saddr", "iif"], "result": "oif"}}, "op": "==", "right": false}}, {"drop": null}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING", "expr": [{"match": {"left": {"payload": {"protocol": "icmpv6", "field": "type"}}, "op": "==", "right": {"set": ["nd-router-advert", "nd-neighbor-solicit"]}}}, {"accept": null}]}}},
{"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_OUTPUT", "index": 0, "expr": [{"match": {"left": {"payload": {"protocol": "ip6", "field": "daddr"}}, "op": "==", "right": {"set": [{"prefix": {"addr": "::0.0.0.0", "len": 96}}, {"prefix": {"addr": "::ffff:0.0.0.0", "len": 96}}, {"prefix": {"addr": "2002:0000::", "len": 24}}, {"prefix": {"addr": "2002:0a00::", "len": 24}}, {"prefix": {"addr": "2002:7f00::", "len": 24}}, {"prefix": {"addr": "2002:ac10::", "len": 28}}, {"prefix": {"addr": "2002:c0a8::", "len": 32}}, {"prefix": {"addr": "2002:a9fe::", "len": 32}}, {"prefix": {"addr": "2002:e000::", "len": 19}}]}}}, {"reject": {"type": "icmpv6", "expr": "addr-unreachable"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD", "index": 2, "expr": [{"match": {"left": {"payload": {"protocol": "ip6", "field": "daddr"}}, "op": "==", "right": {"set": [{"prefix": {"addr": "::0.0.0.0", "len": 96}}, {"prefix": {"addr": "::ffff:0.0.0.0", "len": 96}}, {"prefix": {"addr": "2002:0000::", "len": 24}}, {"prefix": {"addr": "2002:0a00::", "len": 24}}, {"prefix": {"addr": "2002:7f00::", "len": 24}}, {"prefix": {"addr": "2002:ac10::", "len": 28}}, {"prefix": {"addr": "2002:c0a8::", "len": 32}}, {"prefix": {"addr": "2002:a9fe::", "len": 32}}, {"prefix": {"addr": "2002:e000::", "len": 19}}]}}}, {"reject": {"type": "icmpv6", "expr": "addr-unreachable"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_public"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_public_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_public_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_public_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_public_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_public_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_public", "expr": [{"jump": {"target": "raw_PRE_public_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_public", "expr": [{"jump": {"target": "raw_PRE_public_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_public", "expr": [{"jump": {"target": "raw_PRE_public_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_public", "expr": [{"jump": {"target": "raw_PRE_public_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_public", "expr": [{"jump": {"target": "raw_PRE_public_post"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_public"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_public_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_public_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_public_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_public_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_public_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public", "expr": [{"jump": {"target": "filter_IN_public_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public", "expr": [{"jump": {"target": "filter_IN_public_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public", "expr": [{"jump": {"target": "filter_IN_public_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public", "expr": [{"jump": {"target": "filter_IN_public_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public", "expr": [{"jump": {"target": "filter_IN_public_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public_allow", "expr": [{"match": {"left": {"payload": {"protocol": "tcp", "field": "dport"}}, "op": "==", "right": 22}}, {"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["new", "untracked"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public_allow", "expr": [{"match": {"left": {"payload": {"protocol": "ip6", "field": "daddr"}}, "op": "==", "right": {"prefix": {"addr": "fe80::", "len": 64}}}}, {"match": {"left": {"payload": {"protocol": "udp", "field": "dport"}}, "op": "==", "right": 546}}, {"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["new", "untracked"]}}}, {"accept": null}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_public"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_public_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_public_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_public_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_public_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_public_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_public", "expr": [{"jump": {"target": "filter_FWDI_public_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_public", "expr": [{"jump": {"target": "filter_FWDI_public_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_public", "expr": [{"jump": {"target": "filter_FWDI_public_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_public", "expr": [{"jump": {"target": "filter_FWDI_public_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_public", "expr": [{"jump": {"target": "filter_FWDI_public_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_public", "index": 4, "expr": [{"match": {"left": {"meta": {"key": "l4proto"}}, "op": "==", "right": {"set": ["icmp", "icmpv6"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_public", "index": 4, "expr": [{"match": {"left": {"meta": {"key": "l4proto"}}, "op": "==", "right": {"set": ["icmp", "icmpv6"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING_ZONES", "expr": [{"goto": {"target": "raw_PRE_public"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_public"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_public_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_public_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_public_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_public_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_public_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_public", "expr": [{"jump": {"target": "mangle_PRE_public_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_public", "expr": [{"jump": {"target": "mangle_PRE_public_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_public", "expr": [{"jump": {"target": "mangle_PRE_public_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_public", "expr": [{"jump": {"target": "mangle_PRE_public_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_public", "expr": [{"jump": {"target": "mangle_PRE_public_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PREROUTING_ZONES", "expr": [{"goto": {"target": "mangle_PRE_public"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_public"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_public_pre"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_public_log"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_public_deny"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_public_allow"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_public_post"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_pre"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_log"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_deny"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_allow"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_post"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_public"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_public_pre"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_public_log"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_public_deny"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_public_allow"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_public_post"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_pre"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_log"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_deny"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_allow"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_public", "expr": [{"jump": {"target": "nat_PRE_public_post"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PREROUTING_ZONES", "expr": [{"goto": {"target": "nat_PRE_public"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PREROUTING_ZONES", "expr": [{"goto": {"target": "nat_PRE_public"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_public"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_public_pre"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_public_log"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_public_deny"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_public_allow"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_public_post"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_pre"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_log"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_deny"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_allow"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_post"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_public"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_public_pre"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_public_log"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_public_deny"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_public_allow"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_public_post"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_pre"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_log"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_deny"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_allow"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_public", "expr": [{"jump": {"target": "nat_POST_public_post"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POSTROUTING_ZONES", "expr": [{"goto": {"target": "nat_POST_public"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POSTROUTING_ZONES", "expr": [{"goto": {"target": "nat_POST_public"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT_ZONES", "expr": [{"goto": {"target": "filter_IN_public"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD_IN_ZONES", "expr": [{"goto": {"target": "filter_FWDI_public"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_public"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_public_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_public_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_public_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_public_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_public_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_public", "expr": [{"jump": {"target": "filter_FWDO_public_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_public", "expr": [{"jump": {"target": "filter_FWDO_public_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_public", "expr": [{"jump": {"target": "filter_FWDO_public_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_public", "expr": [{"jump": {"target": "filter_FWDO_public_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_public", "expr": [{"jump": {"target": "filter_FWDO_public_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD_OUT_ZONES", "expr": [{"goto": {"target": "filter_FWDO_public"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_trusted"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_trusted_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_trusted_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_trusted_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_trusted_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_trusted_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_trusted", "expr": [{"jump": {"target": "raw_PRE_trusted_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_trusted", "expr": [{"jump": {"target": "raw_PRE_trusted_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_trusted", "expr": [{"jump": {"target": "raw_PRE_trusted_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_trusted", "expr": [{"jump": {"target": "raw_PRE_trusted_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_trusted", "expr": [{"jump": {"target": "raw_PRE_trusted_post"}}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "raw_PRE_trusted"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_trusted"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_trusted_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_trusted_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_trusted_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_trusted_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_trusted_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_trusted", "expr": [{"jump": {"target": "mangle_PRE_trusted_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_trusted", "expr": [{"jump": {"target": "mangle_PRE_trusted_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_trusted", "expr": [{"jump": {"target": "mangle_PRE_trusted_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_trusted", "expr": [{"jump": {"target": "mangle_PRE_trusted_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_trusted", "expr": [{"jump": {"target": "mangle_PRE_trusted_post"}}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "mangle_PRE_trusted"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_trusted"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_trusted_pre"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_trusted_log"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_trusted_deny"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_trusted_allow"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_trusted_post"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_pre"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_log"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_deny"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_allow"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_post"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_trusted"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_trusted_pre"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_trusted_log"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_trusted_deny"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_trusted_allow"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_trusted_post"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_pre"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_log"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_deny"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_allow"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_trusted", "expr": [{"jump": {"target": "nat_PRE_trusted_post"}}]}}}, {"insert": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "nat_PRE_trusted"}}]}}}, {"insert": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "nat_PRE_trusted"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_trusted"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_trusted_pre"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_trusted_log"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_trusted_deny"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_trusted_allow"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_trusted_post"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_pre"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_log"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_deny"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_allow"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_post"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_trusted"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_trusted_pre"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_trusted_log"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_trusted_deny"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_trusted_allow"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_trusted_post"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_pre"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_log"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_deny"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_allow"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_trusted", "expr": [{"jump": {"target": "nat_POST_trusted_post"}}]}}}, {"insert": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POSTROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "nat_POST_trusted"}}]}}}, {"insert": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POSTROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "nat_POST_trusted"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_trusted"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_trusted_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_trusted_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_trusted_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_trusted_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_trusted_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_trusted", "expr": [{"jump": {"target": "filter_IN_trusted_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_trusted", "expr": [{"jump": {"target": "filter_IN_trusted_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_trusted", "expr": [{"jump": {"target": "filter_IN_trusted_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_trusted", "expr": [{"jump": {"target": "filter_IN_trusted_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_trusted", "expr": [{"jump": {"target": "filter_IN_trusted_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_trusted", "expr": [{"accept": null}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "filter_IN_trusted"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_trusted"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_trusted_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_trusted_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_trusted_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_trusted_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_trusted_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_trusted", "expr": [{"jump": {"target": "filter_FWDI_trusted_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_trusted", "expr": [{"jump": {"target": "filter_FWDI_trusted_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_trusted", "expr": [{"jump": {"target": "filter_FWDI_trusted_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_trusted", "expr": [{"jump": {"target": "filter_FWDI_trusted_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_trusted", "expr": [{"jump": {"target": "filter_FWDI_trusted_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_trusted", "expr": [{"accept": null}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD_IN_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "filter_FWDI_trusted"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_trusted"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_trusted_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_trusted_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_trusted_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_trusted_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_trusted_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_trusted", "expr": [{"jump": {"target": "filter_FWDO_trusted_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_trusted", "expr": [{"jump": {"target": "filter_FWDO_trusted_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_trusted", "expr": [{"jump": {"target": "filter_FWDO_trusted_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_trusted", "expr": [{"jump": {"target": "filter_FWDO_trusted_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_trusted", "expr": [{"jump": {"target": "filter_FWDO_trusted_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_trusted", "expr": [{"accept": null}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD_OUT_ZONES", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "perm_dummy2"}}, {"goto": {"target": "filter_FWDO_trusted"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_work"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_work_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_work_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_work_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_work_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "raw_PRE_work_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_work", "expr": [{"jump": {"target": "raw_PRE_work_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_work", "expr": [{"jump": {"target": "raw_PRE_work_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_work", "expr": [{"jump": {"target": "raw_PRE_work_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_work", "expr": [{"jump": {"target": "raw_PRE_work_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PRE_work", "expr": [{"jump": {"target": "raw_PRE_work_post"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_work"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_work_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_work_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_work_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_work_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_IN_work_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work", "expr": [{"jump": {"target": "filter_IN_work_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work", "expr": [{"jump": {"target": "filter_IN_work_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work", "expr": [{"jump": {"target": "filter_IN_work_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work", "expr": [{"jump": {"target": "filter_IN_work_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work", "expr": [{"jump": {"target": "filter_IN_work_post"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work_allow", "expr": [{"match": {"left": {"payload": {"protocol": "tcp", "field": "dport"}}, "op": "==", "right": 22}}, {"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["new", "untracked"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work_allow", "expr": [{"match": {"left": {"payload": {"protocol": "ip6", "field": "daddr"}}, "op": "==", "right": {"prefix": {"addr": "fe80::", "len": 64}}}}, {"match": {"left": {"payload": {"protocol": "udp", "field": "dport"}}, "op": "==", "right": 546}}, {"match": {"left": {"ct": {"key": "state"}}, "op": "in", "right": {"set": ["new", "untracked"]}}}, {"accept": null}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "raw_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "raw_PRE_work"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_work"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_work_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_work_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_work_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_work_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "mangle_PRE_work_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_work", "expr": [{"jump": {"target": "mangle_PRE_work_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_work", "expr": [{"jump": {"target": "mangle_PRE_work_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_work", "expr": [{"jump": {"target": "mangle_PRE_work_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_work", "expr": [{"jump": {"target": "mangle_PRE_work_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PRE_work", "expr": [{"jump": {"target": "mangle_PRE_work_post"}}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "mangle_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "mangle_PRE_work"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_work"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_work_pre"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_work_log"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_work_deny"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_work_allow"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_PRE_work_post"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_pre"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_log"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_deny"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_allow"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_post"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_work"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_work_pre"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_work_log"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_work_deny"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_work_allow"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_PRE_work_post"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_pre"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_log"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_deny"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_allow"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PRE_work", "expr": [{"jump": {"target": "nat_PRE_work_post"}}]}}}, {"insert": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "nat_PRE_work"}}]}}}, {"insert": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_PREROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "nat_PRE_work"}}]}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_work"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_work_pre"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_work_log"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_work_deny"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_work_allow"}}}, {"add": {"chain": {"family": "ip", "table": "firewalld", "name": "nat_POST_work_post"}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_pre"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_log"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_deny"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_allow"}}]}}}, {"add": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_post"}}]}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_work"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_work_pre"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_work_log"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_work_deny"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_work_allow"}}}, {"add": {"chain": {"family": "ip6", "table": "firewalld", "name": "nat_POST_work_post"}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_pre"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_log"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_deny"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_allow"}}]}}}, {"add": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POST_work", "expr": [{"jump": {"target": "nat_POST_work_post"}}]}}}, {"insert": {"rule": {"family": "ip", "table": "firewalld", "chain": "nat_POSTROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "nat_POST_work"}}]}}}, {"insert": {"rule": {"family": "ip6", "table": "firewalld", "chain": "nat_POSTROUTING_ZONES", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "nat_POST_work"}}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_INPUT_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "filter_IN_work"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_work"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_work_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_work_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_work_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_work_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDI_work_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_work", "expr": [{"jump": {"target": "filter_FWDI_work_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_work", "expr": [{"jump": {"target": "filter_FWDI_work_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_work", "expr": [{"jump": {"target": "filter_FWDI_work_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_work", "expr": [{"jump": {"target": "filter_FWDI_work_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_work", "expr": [{"jump": {"target": "filter_FWDI_work_post"}}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD_IN_ZONES", "expr": [{"match": {"left": {"meta": {"key": "iifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "filter_FWDI_work"}}]}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_work"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_work_pre"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_work_log"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_work_deny"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_work_allow"}}}, {"add": {"chain": {"family": "inet", "table": "firewalld", "name": "filter_FWDO_work_post"}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_work", "expr": [{"jump": {"target": "filter_FWDO_work_pre"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_work", "expr": [{"jump": {"target": "filter_FWDO_work_log"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_work", "expr": [{"jump": {"target": "filter_FWDO_work_deny"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_work", "expr": [{"jump": {"target": "filter_FWDO_work_allow"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDO_work", "expr": [{"jump": {"target": "filter_FWDO_work_post"}}]}}}, {"insert": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FORWARD_OUT_ZONES", "expr": [{"match": {"left": {"meta": {"key": "oifname"}}, "op": "==", "right": "perm_dummy"}}, {"goto": {"target": "filter_FWDO_work"}}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_IN_work", "index": 4, "expr": [{"match": {"left": {"meta": {"key": "l4proto"}}, "op": "==", "right": {"set": ["icmp", "icmpv6"]}}}, {"accept": null}]}}}, {"add": {"rule": {"family": "inet", "table": "firewalld", "chain": "filter_FWDI_work", "index": 4, "expr": [{"match": {"left": {"meta": {"key": "l4proto"}}, "op": "==", "right": {"set": ["icmp", "icmpv6"]}}}, {"accept": null}]}}}]}'
-test -z "$($NFT -j -e -a -f - <<< "$RULESET" |sed 's/\({"add":\|{"insert":\)/\n\1/g' |grep '\({"add":\|{"insert":\)' | grep -v '"handle"')"
+if [ "$NFT_TEST_HAVE_json" != n ]; then
+ test -z "$($NFT -j -e -a -f - <<< "$RULESET" |sed 's/\({"add":\|{"insert":\)/\n\1/g' |grep '\({"add":\|{"insert":\)' | grep -v '"handle"')"
+fi
+
+if [ "$NFT_TEST_HAVE_json" = n ]; then
+ echo "Test partially skipped due to missing JSON support."
+ exit 77
+fi
+
+if [ "$RULE_COUNT" != 3000 ] ; then
+ echo "NFT_TEST_HAS_SOCKET_LIMITS indicates that the socket limit for"
+ echo "/proc/sys/net/core/rmem_max is too small for this test. Mark as SKIPPED"
+ echo "You may bump the limit and rerun with \`NFT_TEST_HAS_SOCKET_LIMITS=n\`."
+ exit 77
+fi
diff --git a/tests/shell/testcases/transactions/0051map_0 b/tests/shell/testcases/transactions/0051map_0
new file mode 100755
index 00000000..9ea5cd4c
--- /dev/null
+++ b/tests/shell/testcases/transactions/0051map_0
@@ -0,0 +1,122 @@
+#!/bin/bash
+
+rnd=$(mktemp -u XXXXXXXX)
+ns1="nft1trans-$rnd"
+
+#
+# dependency tracking for implicit set
+#
+RULESET="table ip x {
+ chain w {}
+ chain m {}
+
+ chain y {
+ ip saddr vmap { 1.1.1.1 : jump w, 2.2.2.2 : accept, 3.3.3.3 : goto m }
+ }
+}"
+
+$NFT -c -f - <<< "$RULESET" >/dev/null || exit 0
+$NFT -f - <<< "$RULESET" >/dev/null || exit 0
+ip netns add $ns1
+ip netns exec $ns1 $NFT -f - <<< "$RULESET" >/dev/null || exit 0
+ip netns del $ns1
+
+RULESET="flush chain ip x y
+delete chain ip x w"
+
+$NFT -c -f - <<< "$RULESET" >/dev/null || exit 0
+$NFT -f - <<< "$RULESET" >/dev/null || exit 0
+
+#
+# dependency tracking for map in implicit chain
+#
+RULESET="table ip x {
+ chain w {}
+ chain m {}
+
+ chain y {
+ meta iifname \"eno1\" jump {
+ ip saddr vmap { 1.1.1.1 : jump w, 3.3.3.3 : goto m }
+ }
+ }
+}"
+
+$NFT -c -f - <<< "$RULESET" >/dev/null || exit 0
+$NFT -f - <<< "$RULESET" >/dev/null || exit 0
+ip netns add $ns1
+ip netns exec $ns1 $NFT -f - <<< "$RULESET" >/dev/null || exit 0
+ip netns del $ns1
+
+RULESET="flush chain ip x y
+delete chain ip x w"
+
+$NFT -c -f - <<< "$RULESET" >/dev/null || exit 0
+$NFT -f - <<< "$RULESET" >/dev/null || exit 0
+
+#
+# dependency tracking for explicit map
+#
+RULESET="table ip x {
+ chain w {}
+ chain m {}
+
+ map y {
+ type ipv4_addr : verdict
+ elements = { 1.1.1.1 : jump w, 2.2.2.2 : accept, 3.3.3.3 : goto m }
+ }
+}"
+
+$NFT -c -f - <<< "$RULESET" >/dev/null || exit 0
+$NFT -f - <<< "$RULESET" >/dev/null || exit 0
+ip netns add $ns1
+ip netns exec $ns1 $NFT -f - <<< "$RULESET" >/dev/null || exit 0
+ip netns del $ns1
+
+RULESET="delete set ip x y
+delete chain ip x w"
+
+$NFT -c -f - <<< "$RULESET" >/dev/null || exit 0
+$NFT -f - <<< "$RULESET" >/dev/null || exit 0
+
+#
+# error path for implicit set
+#
+RULESET="table inet filter {
+ chain w {
+ jump z
+ }
+ chain z {
+ jump w
+ }
+
+ chain test {
+ ip protocol { tcp, udp } ip saddr vmap { 1.1.1.1 : jump z } counter flow add @nonexisting
+ ip6 nexthdr { tcp, udp } ct mark and 2 == 2 counter
+ }
+}"
+
+$NFT -c -f - <<< "$RULESET" >/dev/null || exit 0
+$NFT -f - <<< "$RULESET" >/dev/null || exit 0
+
+#
+# error path for implicit set
+#
+RULESET="table inet filter {
+ chain w {
+ jump z
+ }
+ chain z {
+ jump w
+ }
+
+ chain test {
+ ip protocol { tcp, udp } jump {
+ ip saddr vmap { 1.1.1.1 : jump z }
+ }
+ ip6 nexthdr { tcp, udp } ct mark and 2 == 2 counter
+ }
+}"
+
+$NFT -c -f - <<< "$RULESET" >/dev/null || exit 0
+$NFT -f - <<< "$RULESET" >/dev/null || exit 0
+$NFT flush table inet filter || exit 0
diff --git a/tests/shell/testcases/transactions/30s-stress b/tests/shell/testcases/transactions/30s-stress
new file mode 100755
index 00000000..e92b9226
--- /dev/null
+++ b/tests/shell/testcases/transactions/30s-stress
@@ -0,0 +1,683 @@
+#!/bin/bash
+
+# NFT_TEST_SKIP(NFT_TEST_SKIP_slow)
+
+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
+ echo "Invalid runtime $1"
+ exit 1
+ fi
+fi
+
+if [ x = x"$NFT" ] ; then
+ NFT=nft
+fi
+
+if [ "$NFT_TEST_HAS_SOCKET_LIMITS" = y ] ; then
+ # The socket limit /proc/sys/net/core/wmem_max may be unsuitable for
+ # the test.
+ #
+ # Skip it. You may ensure that the limits are suitable and rerun
+ # with NFT_TEST_HAS_SOCKET_LIMITS=n.
+ exit 77
+fi
+
+if [ -z "${NFT_TEST_HAVE_chain_binding+x}" ] ; then
+ NFT_TEST_HAVE_chain_binding=n
+ mydir="$(dirname "$0")"
+ $NFT --check -f "$mydir/../../features/chain_binding.nft"
+ if [ $? -eq 0 ];then
+ NFT_TEST_HAVE_chain_binding=y
+ else
+ echo "Assuming anonymous chains are not supported"
+ fi
+fi
+
+if [ "$NFT_TEST_HAVE_pipapo" != y ] ;then
+ echo "Skipping pipapo set backend, kernel does not support it"
+fi
+
+testns=testns-$(mktemp -u "XXXXXXXX")
+tmp=""
+
+faultname="/proc/self/make-it-fail"
+tables="foo bar"
+
+failslab_defaults() {
+ test -w $faultname || return
+
+ # Disable fault injection unless process has 'make-it-fail' set
+ echo Y > /sys/kernel/debug/failslab/task-filter
+
+ # allow all slabs to fail (if process is tagged).
+ find /sys/kernel/slab/ -wholename '*/kmalloc-[0-9]*/failslab' -type f -exec sh -c 'echo 1 > {}' \;
+
+ # no limit on the number of failures, or clause works around old kernels that reject negative integer.
+ echo -1 > /sys/kernel/debug/failslab/times 2>/dev/null || printf '%#x -1' > /sys/kernel/debug/failslab/times
+
+ # Set to 2 for full dmesg traces for each injected error
+ echo 0 > /sys/kernel/debug/failslab/verbose
+}
+
+failslab_random()
+{
+ r=$((RANDOM%2))
+
+ if [ $r -eq 0 ]; then
+ echo Y > /sys/kernel/debug/failslab/ignore-gfp-wait
+ else
+ echo N > /sys/kernel/debug/failslab/ignore-gfp-wait
+ fi
+
+ r=$((RANDOM%5))
+ echo $r > /sys/kernel/debug/failslab/probability
+ r=$((RANDOM%100))
+ echo $r > /sys/kernel/debug/failslab/interval
+
+ # allow a small initial 'success budget'.
+ # failures only appear after this many allocated bytes.
+ r=$((RANDOM%16384))
+ echo $r > /sys/kernel/debug/$FAILTYPE/space
+}
+
+netns_del() {
+ ip netns pids "$testns" | xargs kill 2>/dev/null
+ ip netns del "$testns"
+}
+
+netns_add()
+{
+ ip netns add "$testns"
+ ip -netns "$testns" link set lo up
+}
+
+cleanup() {
+ [ "$tmp" = "" ] || rm -f "$tmp"
+ netns_del
+}
+
+nft_with_fault_inject()
+{
+ file="$1"
+
+ if [ -w "$faultname" ]; then
+ failslab_random
+
+ ip netns exec "$testns" bash -c "echo 1 > $faultname ; exec $NFT -f $file"
+ fi
+
+ ip netns exec "$testns" $NFT -f "$file"
+}
+
+trap cleanup EXIT
+tmp=$(mktemp)
+
+jump_or_goto()
+{
+ if [ $((RANDOM & 1)) -eq 0 ] ;then
+ echo -n "jump"
+ else
+ echo -n "goto"
+ fi
+}
+
+random_verdict()
+{
+ max="$1"
+
+ if [ $max -eq 0 ]; then
+ max=1
+ fi
+
+ rnd=$((RANDOM%max))
+
+ if [ $rnd -gt 0 ];then
+ jump_or_goto
+ printf " chain%03u" "$((rnd+1))"
+ return
+ fi
+
+ if [ $((RANDOM & 1)) -eq 0 ] ;then
+ echo "accept"
+ else
+ echo "drop"
+ fi
+}
+
+randsleep()
+{
+ local s=$((RANDOM%1))
+ local ms=$((RANDOM%1000))
+ sleep $s.$ms
+}
+
+randlist()
+{
+ while [ -r $tmp ]; do
+ randsleep
+ ip netns exec $testns $NFT list ruleset > /dev/null
+ done
+}
+
+randflush()
+{
+ while [ -r $tmp ]; do
+ randsleep
+ ip netns exec $testns $NFT flush ruleset > /dev/null
+ done
+}
+
+randdeltable()
+{
+ while [ -r $tmp ]; do
+ randsleep
+ for t in $tables; do
+ r=$((RANDOM%10))
+
+ if [ $r -eq 1 ] ;then
+ ip netns exec $testns $NFT delete table inet $t
+ randsleep
+ fi
+ done
+ done
+}
+
+randdelset()
+{
+ while [ -r $tmp ]; do
+ randsleep
+ for t in $tables; do
+ r=$((RANDOM%10))
+ s=$((RANDOM%10))
+
+ case $r in
+ 0)
+ setname=set_$s
+ ;;
+ 1)
+ setname=sett${s}
+ ;;
+ 2)
+ setname=dmap_${s}
+ ;;
+ 3)
+ setname=dmapt${s}
+ ;;
+ 4)
+ setname=vmap_${s}
+ ;;
+ 5)
+ setname=vmapt${s}
+ ;;
+ *)
+ continue
+ ;;
+ esac
+
+ if [ $r -eq 1 ] ;then
+ ip netns exec $testns $NFT delete set inet $t $setname
+ fi
+ done
+ done
+}
+
+randdelchain()
+{
+ while [ -r $tmp ]; do
+ for t in $tables; do
+ local c=$((RANDOM%100))
+ randsleep
+ chain=$(printf "chain%03u" "$c")
+
+ local r=$((RANDOM%10))
+ if [ $r -eq 1 ];then
+ # chain can be invalid/unknown.
+ ip netns exec $testns $NFT delete chain inet $t $chain
+ fi
+ done
+ done
+}
+
+randdisable()
+{
+ while [ -r $tmp ]; do
+ for t in $tables; do
+ randsleep
+ local r=$((RANDOM%10))
+ if [ $r -eq 1 ];then
+ ip netns exec $testns $NFT add table inet $t '{flags dormant; }'
+ randsleep
+ ip netns exec $testns $NFT add table inet $t '{ }'
+ fi
+ done
+ done
+}
+
+randdelns()
+{
+ while [ -r $tmp ]; do
+ randsleep
+ netns_del
+ netns_add
+ randsleep
+ done
+}
+
+available_flags()
+{
+ local -n available_flags=$1
+ selected_key=$2
+ if [ "$selected_key" == "single" ] ;then
+ available_flags+=("interval")
+ elif [ "$selected_key" == "concat" ] ;then
+ if [ "$NFT_TEST_HAVE_pipapo" = y ] ;then
+ available_flags+=("interval")
+ fi
+ fi
+}
+
+random_element_string=""
+
+# create a random element. Could cause any of the following:
+# 1. Invalid set/map
+# 2. Element already exists in set/map w. create
+# 3. Element is new but wants to jump to unknown chain
+# 4. Element already exsists in set/map w. add, but verdict (map data) differs
+# 5. Element is created/added/deleted from 'flags constant' set.
+random_elem()
+{
+ tr=$((RANDOM%2))
+ t=0
+
+ for table in $tables; do
+ if [ $t -ne $tr ]; then
+ t=$((t+1))
+ continue
+ fi
+
+ kr=$((RANDOM%2))
+ k=0
+ cnt=0
+ for key in "single" "concat"; do
+ if [ $k -ne $kr ] ;then
+ cnt=$((cnt+2))
+ k=$((k+1))
+ continue
+ fi
+
+ fr=$((RANDOM%2))
+ f=0
+
+ FLAGS=("")
+ available_flags FLAGS $key
+ for flags in "${FLAGS[@]}" ; do
+ cnt=$((cnt+1))
+ if [ $f -ne fkr ] ;then
+ f=$((f+1))
+ continue
+ fi
+
+ want="${key}${flags}"
+
+ e=$((RANDOM%256))
+ case "$want" in
+ "single") element="10.1.1.$e"
+ ;;
+ "concat") element="10.1.2.$e . $((RANDOM%65536))"
+ ;;
+ "singleinterval") element="10.1.$e.0-10.1.$e.$e"
+ ;;
+ "concatinterval") element="10.1.$e.0-10.1.$e.$e . $((RANDOM%65536))"
+ ;;
+ *) echo "bogus key $want"
+ exit 111
+ ;;
+ esac
+
+ # This may result in invalid jump, but thats what we want.
+ count=$(($RANDOM%100))
+
+ r=$((RANDOM%7))
+ case "$r" in
+ 0)
+ random_element_string=" inet $table set_${cnt} { $element }"
+ ;;
+ 1) random_element_string="inet $table sett${cnt} { $element timeout $((RANDOM%60))s }"
+ ;;
+ 2) random_element_string="inet $table dmap_${cnt} { $element : $RANDOM }"
+ ;;
+ 3) random_element_string="inet $table dmapt${cnt} { $element timeout $((RANDOM%60))s : $RANDOM }"
+ ;;
+ 4) random_element_string="inet $table vmap_${cnt} { $element : `random_verdict $count` }"
+ ;;
+ 5) random_element_string="inet $table vmapt${cnt} { $element timeout $((RANDOM%60))s : `random_verdict $count` }"
+ ;;
+ 6) random_element_string="inet $table setc${cnt} { $element }"
+ ;;
+ esac
+
+ return
+ done
+ done
+ done
+}
+
+randload()
+{
+ while [ -r $tmp ]; do
+ random_element_string=""
+ r=$((RANDOM%10))
+
+ what=""
+ case $r in
+ 1)
+ (echo "flush ruleset"; cat "$tmp"
+ echo "insert rule inet foo INPUT meta nftrace set 1"
+ echo "insert rule inet foo OUTPUT meta nftrace set 1"
+ ) | nft_with_fault_inject "/dev/stdin"
+ ;;
+ 2) what="add"
+ ;;
+ 3) what="create"
+ ;;
+ 4) what="delete"
+ ;;
+ 5) what="destroy"
+ ;;
+ 6) what="get"
+ ;;
+ *)
+ randsleep
+ ;;
+ esac
+
+ if [ x"$what" = "x" ]; then
+ nft_with_fault_inject "$tmp"
+ else
+ # This can trigger abort path, for various reasons:
+ # invalid set name
+ # key mismatches set specification (concat vs. single value)
+ # attempt to delete non-existent key
+ # attempt to create dupliacte key
+ # attempt to add duplicate key with non-matching value (data)
+ # attempt to add new uniqeue key with a jump to an unknown chain
+ random_elem
+ ( cat "$tmp"; echo "$what element $random_element_string") | nft_with_fault_inject "/dev/stdin"
+ fi
+ done
+}
+
+randmonitor()
+{
+ while [ -r $tmp ]; do
+ randsleep
+ timeout=$((RANDOM%16))
+ timeout $((timeout+1)) $NFT monitor > /dev/null
+ done
+}
+
+floodping() {
+ cpunum=$(grep -c processor /proc/cpuinfo)
+ cpunum=$((cpunum+1))
+
+ while [ -r $tmp ]; do
+ spawn=$((RANDOM%$cpunum))
+
+ # spawn at most $cpunum processes. Or maybe none at all.
+ i=0
+ while [ $i -lt $spawn ]; do
+ mask=$(printf 0x%x $((1<<$i)))
+ timeout 3 ip netns exec "$testns" taskset $mask ping -4 -fq 127.0.0.1 > /dev/null &
+ timeout 3 ip netns exec "$testns" taskset $mask ping -6 -fq ::1 > /dev/null &
+ i=$((i+1))
+ done
+
+ wait
+ randsleep
+ done
+}
+
+stress_all()
+{
+ # if fault injection is enabled, first a quick test to trigger
+ # abort paths without any parallel deletes/flushes.
+ if [ -w $faultname ] ;then
+ for i in $(seq 1 10);do
+ nft_with_fault_inject "$tmp"
+ done
+ fi
+
+ randlist &
+ randflush &
+ randdeltable &
+ randdisable &
+ randdelchain &
+ randdelset &
+ randdelns &
+ randload &
+ randmonitor &
+}
+
+gen_anon_chain_jump()
+{
+ echo -n "insert rule inet $@ "
+ jump_or_goto
+
+ if [ "$NFT_TEST_HAVE_chain_binding" = n ] ; then
+ echo " defaultchain"
+ return
+ fi
+
+ echo -n " { "
+ jump_or_goto
+ echo " defaultchain; counter; }"
+}
+
+gen_ruleset() {
+echo > "$tmp"
+for table in $tables; do
+ count=$((RANDOM % 100))
+ if [ $count -lt 1 ];then
+ count=1
+ fi
+
+ echo add table inet "$table" >> "$tmp"
+ echo flush table inet "$table" >> "$tmp"
+
+ echo "add chain inet $table INPUT { type filter hook input priority 0; }" >> "$tmp"
+ echo "add chain inet $table OUTPUT { type filter hook output priority 0; }" >> "$tmp"
+ for c in $(seq 1 $count); do
+ chain=$(printf "chain%03u" "$c")
+ echo "add chain inet $table $chain" >> "$tmp"
+ done
+
+ echo "add chain inet $table defaultchain" >> "$tmp"
+
+ for c in $(seq 1 $count); do
+ chain=$(printf "chain%03u" "$c")
+ for BASE in INPUT OUTPUT; do
+ echo "add rule inet $table $BASE counter jump $chain" >> "$tmp"
+ done
+ if [ $((RANDOM%10)) -eq 1 ];then
+ echo "add rule inet $table $chain counter jump defaultchain" >> "$tmp"
+ else
+ echo "add rule inet $table $chain counter return" >> "$tmp"
+ fi
+ done
+
+ cnt=0
+
+ # add a few anonymous sets. rhashtable is convered by named sets below.
+ c=$((RANDOM%$count))
+ chain=$(printf "chain%03u" "$((c+1))")
+ echo "insert rule inet $table $chain tcp dport 22-26 ip saddr { 1.2.3.4, 5.6.7.8 } counter comment hash_fast" >> "$tmp"
+ echo "insert rule inet $table $chain ip6 saddr { ::1, dead::beef } counter" comment hash >> "$tmp"
+ echo "insert rule inet $table $chain ip saddr { 1.2.3.4 - 5.6.7.8, 127.0.0.1 } comment rbtree" >> "$tmp"
+ # bitmap 1byte, with anon chain jump
+ gen_anon_chain_jump "$table $chain ip protocol { 6, 17 }" >> "$tmp"
+
+ # bitmap 2byte
+ echo "insert rule inet $table $chain tcp dport != { 22, 23, 80 } goto defaultchain" >> "$tmp"
+ echo "insert rule inet $table $chain tcp dport { 1-1024, 8000-8080 } jump defaultchain comment rbtree" >> "$tmp"
+ if [ "$NFT_TEST_HAVE_pipapo" = y ] ;then
+ # pipapo (concat + set), with goto anonymous chain.
+ gen_anon_chain_jump "$table $chain ip saddr . tcp dport { 1.2.3.4 . 1-1024, 1.2.3.6 - 1.2.3.10 . 8000-8080, 1.2.3.4 . 8080, 1.2.3.6 - 1.2.3.10 . 22 }" >> "$tmp"
+ fi
+
+ # add a few anonymous sets. rhashtable is convered by named sets below.
+ c=$((RANDOM%$count))
+ chain=$(printf "chain%03u" "$((c+1))")
+ echo "insert rule inet $table $chain tcp dport 22-26 ip saddr { 1.2.3.4, 5.6.7.8 } counter comment hash_fast" >> "$tmp"
+ echo "insert rule inet $table $chain ip6 saddr { ::1, dead::beef } counter" comment hash >> "$tmp"
+ echo "insert rule inet $table $chain ip saddr { 1.2.3.4 - 5.6.7.8, 127.0.0.1 } comment rbtree" >> "$tmp"
+ # bitmap 1byte, with anon chain jump
+ gen_anon_chain_jump "$table $chain ip protocol { 6, 17 }" >> "$tmp"
+ # bitmap 2byte
+ echo "insert rule inet $table $chain tcp dport != { 22, 23, 80 } goto defaultchain" >> "$tmp"
+ echo "insert rule inet $table $chain tcp dport { 1-1024, 8000-8080 } jump defaultchain comment rbtree" >> "$tmp"
+ if [ "$NFT_TEST_HAVE_pipapo" = y ] ;then
+ # pipapo (concat + set), with goto anonymous chain.
+ gen_anon_chain_jump "$table $chain ip saddr . tcp dport { 1.2.3.4 . 1-1024, 1.2.3.6 - 1.2.3.10 . 8000-8080, 1.2.3.4 . 8080, 1.2.3.6 - 1.2.3.10 . 22 }" >> "$tmp"
+ fi
+
+ # add constant/immutable sets
+ size=$((RANDOM%5120000))
+ size=$((size+2))
+ echo "add set inet $table setc1 { typeof tcp dport; size $size; flags constant; elements = { 22, 44 } }" >> "$tmp"
+ echo "add set inet $table setc2 { typeof ip saddr; size $size; flags constant; elements = { 1.2.3.4, 5.6.7.8 } }" >> "$tmp"
+ echo "add set inet $table setc3 { typeof ip6 daddr; size $size; flags constant; elements = { ::1, dead::1 } }" >> "$tmp"
+ echo "add set inet $table setc4 { typeof tcp dport; size $size; flags interval,constant; elements = { 22-44, 55-66 } }" >> "$tmp"
+ echo "add set inet $table setc5 { typeof ip saddr; size $size; flags interval,constant; elements = { 1.2.3.4-5.6.7.8, 10.1.1.1 } }" >> "$tmp"
+ echo "add set inet $table setc6 { typeof ip6 daddr; size $size; flags interval,constant; elements = { ::1, dead::1-dead::3 } }" >> "$tmp"
+
+ # add named sets with various combinations (plain value, range, concatenated values, concatenated ranges, with timeouts, with data ...)
+ for key in "ip saddr" "ip saddr . tcp dport"; do
+ FLAGS=("")
+ if [ "$key" == "ip saddr" ] ;then
+ FLAGS+=("flags interval;")
+ elif [ "$key" == "ip saddr . tcp dport" ] ;then
+ if [ "$NFT_TEST_HAVE_pipapo" = y ] ;then
+ FLAGS+=("flags interval;")
+ fi
+ fi
+ for ((i = 0; i < ${#FLAGS[@]}; i++)) ; do
+ timeout=$((RANDOM%10))
+ timeout=$((timeout+1))
+ timeout="timeout ${timeout}s"
+
+ cnt=$((cnt+1))
+ flags=${FLAGS[$i]}
+ echo "add set inet $table set_${cnt} { typeof ${key} ; ${flags} }" >> "$tmp"
+ echo "add set inet $table sett${cnt} { typeof ${key} ; $timeout; ${flags} }" >> "$tmp"
+ echo "add map inet $table dmap_${cnt} { typeof ${key} : meta mark ; ${flags} }" >> "$tmp"
+ echo "add map inet $table dmapt${cnt} { typeof ${key} : meta mark ; $timeout ; ${flags} }" >> "$tmp"
+ echo "add map inet $table vmap_${cnt} { typeof ${key} : verdict ; ${flags} }" >> "$tmp"
+ echo "add map inet $table vmapt${cnt} { typeof ${key} : verdict; $timeout ; ${flags} }" >> "$tmp"
+ done
+ done
+
+ cnt=0
+ for key in "single" "concat"; do
+ FLAGS=("")
+ available_flags FLAGS $key
+
+ for ((i = 0; i < ${#FLAGS[@]}; i++)) ; do
+ flags=${FLAGS[$i]}
+ want="${key}${flags}"
+ cnt=$((cnt+1))
+ maxip=$((RANDOM%256))
+
+ if [ $maxip -eq 0 ];then
+ maxip=1
+ fi
+
+ for e in $(seq 1 $maxip);do
+ case "$want" in
+ "single") element="10.1.1.$e"
+ ;;
+ "concat")
+ element="10.1.2.$e . $((RANDOM%65536))"
+ ;;
+ "singleinterval")
+ element="10.1.$e.0-10.1.$e.$e"
+ ;;
+ "concatinterval")
+ element="10.1.$e.0-10.1.$e.$e . $((RANDOM%65536))"
+ ;;
+ *)
+ echo "bogus key $want"
+ exit 111
+ ;;
+ esac
+
+ echo "add element inet $table set_${cnt} { $element }" >> "$tmp"
+ echo "add element inet $table sett${cnt} { $element timeout $((RANDOM%60))s }" >> "$tmp"
+ echo "add element inet $table dmap_${cnt} { $element : $RANDOM }" >> "$tmp"
+ echo "add element inet $table dmapt${cnt} { $element timeout $((RANDOM%60))s : $RANDOM }" >> "$tmp"
+ echo "add element inet $table vmap_${cnt} { $element : `random_verdict $count` }" >> "$tmp"
+ echo "add element inet $table vmapt${cnt} { $element timeout $((RANDOM%60))s : `random_verdict $count` }" >> "$tmp"
+ done
+ done
+ done
+done
+}
+
+run_test()
+{
+ local time_now=$(date +%s)
+ local time_stop=$((time_now + $runtime))
+ local regen=30
+
+ while [ $time_now -lt $time_stop ]; do
+ if [ $regen -gt 0 ];then
+ sleep 1
+ time_now=$(date +%s)
+ regen=$((regen-1))
+ continue
+ fi
+
+ # This clobbers the previously generated ruleset, this is intentional.
+ gen_ruleset
+ regen=$((RANDOM%60))
+ regen=$((regen+2))
+ time_now=$(date +%s)
+ done
+}
+
+netns_add
+
+gen_ruleset
+ip netns exec "$testns" $NFT -f "$tmp" || exit 1
+
+failslab_defaults
+
+stress_all 2>/dev/null &
+
+randsleep
+
+floodping 2> /dev/null &
+
+run_test
+
+# this stops stress_all
+rm -f "$tmp"
+tmp=""
+sleep 4
+
+if [ "$NFT_TEST_HAVE_chain_binding" = n ] ; then
+ echo "Ran a modified version of the test due to NFT_TEST_HAVE_chain_binding=n"
+fi
diff --git a/tests/shell/testcases/transactions/anon_chain_loop b/tests/shell/testcases/transactions/anon_chain_loop
new file mode 100755
index 00000000..2fd61810
--- /dev/null
+++ b/tests/shell/testcases/transactions/anon_chain_loop
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+# anon chains with c1 -> c2 recursive jump, expect failure
+$NFT -f - <<EOF
+table ip t {
+ chain c2 { }
+ chain c1 { }
+}
+
+add t c1 ip saddr 127.0.0.1 jump { jump c2; }
+add t c2 ip saddr 127.0.0.1 jump { jump c1; }
+EOF
+
+if [ $? -eq 0 ] ; then
+ echo "E: able to load bad ruleset" >&2
+ exit 1
+fi
+
+exit 0
diff --git a/tests/shell/testcases/transactions/bad_expression b/tests/shell/testcases/transactions/bad_expression
new file mode 100755
index 00000000..794b6258
--- /dev/null
+++ b/tests/shell/testcases/transactions/bad_expression
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+# table with invalid expression (masquerade called from filter table).
+# nft must return an error. Also catch nfnetlink retry loops that
+# cause nft or kernel to spin.
+timeout 3 $NFT -f - <<EOF
+table ip t0 {
+ chain c { }
+ chain input {
+ type filter hook input priority 0;
+ jump c
+ }
+}
+
+table ip t1 {
+ chain a {
+ masquerade
+ }
+ chain input {
+ type filter hook input priority 1;
+ jump a
+ }
+}
+EOF
+
+rc=$?
+if [ $rc -eq 0 ]; then
+ echo "Ruleset should have failed" 1>&2
+ exit 111
+fi
+
+# 124 means 'command timed out', fail if this
+# happens. Else, pass, failure is wanted here.
+if [ $rc -ne 124 ]; then
+ exit 0
+fi
+
+exit $rc
diff --git a/tests/shell/testcases/transactions/concat_range_abort b/tests/shell/testcases/transactions/concat_range_abort
new file mode 100755
index 00000000..b2bbe37b
--- /dev/null
+++ b/tests/shell/testcases/transactions/concat_range_abort
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_pipapo)
+
+set -e
+
+$NFT -f /dev/stdin <<EOF
+table ip x {
+ map m {
+ typeof ip saddr . meta mark : verdict
+ flags interval
+ counter
+ elements = {
+ 127.0.0.1-127.0.0.4 . 0x123434-0xb00122 : jump foo,
+ }
+ }
+
+ chain foo {
+ accept
+ }
+}
+EOF
+
+$NFT -f /dev/stdin <<EOF
+add chain ip x bar
+add element ip x m { 1.2.3.4 . 42 : jump bar }
+delete set ip x m
+EOF
diff --git a/tests/shell/testcases/transactions/doubled-set b/tests/shell/testcases/transactions/doubled-set
new file mode 100755
index 00000000..50b568eb
--- /dev/null
+++ b/tests/shell/testcases/transactions/doubled-set
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+# NFT_TEST_REQUIRES(NFT_TEST_HAVE_pipapo)
+
+$NFT -f /dev/stdin <<EOF
+table t {
+ set s {
+ type ipv4_addr . ifname
+ flags interval
+ elements = { 1.2.3.4 . "foo" }
+ }
+
+ set s {
+ type ipv4_addr . ifname
+ flags interval
+ elements = { 1.2.3.4 . "foo" }
+
+ }
+}
+EOF
+
+# run-tests.sh will validate dumpfile.
diff --git a/tests/shell/testcases/transactions/dumps/0001table_0.json-nft b/tests/shell/testcases/transactions/dumps/0001table_0.json-nft
new file mode 100644
index 00000000..ea75b43f
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0001table_0.json-nft
@@ -0,0 +1,25 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "y",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0002table_0.json-nft b/tests/shell/testcases/transactions/dumps/0002table_0.json-nft
new file mode 100644
index 00000000..b1fefc31
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0002table_0.json-nft
@@ -0,0 +1,31 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0,
+ "flags": "dormant"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0,
+ "type": "nat",
+ "hook": "prerouting",
+ "prio": 0,
+ "policy": "accept"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0003table_0.json-nft b/tests/shell/testcases/transactions/dumps/0003table_0.json-nft
new file mode 100644
index 00000000..ea75b43f
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0003table_0.json-nft
@@ -0,0 +1,25 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "y",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0003table_0.nft b/tests/shell/testcases/transactions/dumps/0003table_0.nft
new file mode 100644
index 00000000..e4e5f9b1
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0003table_0.nft
@@ -0,0 +1,4 @@
+table ip x {
+}
+table ip y {
+}
diff --git a/tests/shell/testcases/transactions/dumps/0010chain_0.json-nft b/tests/shell/testcases/transactions/dumps/0010chain_0.json-nft
new file mode 100644
index 00000000..85947674
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0010chain_0.json-nft
@@ -0,0 +1,26 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "w",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "w",
+ "name": "y",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0011chain_0.json-nft b/tests/shell/testcases/transactions/dumps/0011chain_0.json-nft
new file mode 100644
index 00000000..12cf0bbf
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0011chain_0.json-nft
@@ -0,0 +1,30 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "drop"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0012chain_0.json-nft b/tests/shell/testcases/transactions/dumps/0012chain_0.json-nft
new file mode 100644
index 00000000..dc5eaa61
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0012chain_0.json-nft
@@ -0,0 +1,30 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "w",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "w",
+ "name": "y",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0013chain_0.json-nft b/tests/shell/testcases/transactions/dumps/0013chain_0.json-nft
new file mode 100644
index 00000000..dc5eaa61
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0013chain_0.json-nft
@@ -0,0 +1,30 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "w",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "w",
+ "name": "y",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 0,
+ "policy": "accept"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0014chain_1.json-nft b/tests/shell/testcases/transactions/dumps/0014chain_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0014chain_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0014chain_1.nft b/tests/shell/testcases/transactions/dumps/0014chain_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0014chain_1.nft
diff --git a/tests/shell/testcases/transactions/dumps/0015chain_0.json-nft b/tests/shell/testcases/transactions/dumps/0015chain_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0015chain_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0015chain_0.nft b/tests/shell/testcases/transactions/dumps/0015chain_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0015chain_0.nft
diff --git a/tests/shell/testcases/transactions/dumps/0020rule_0.json-nft b/tests/shell/testcases/transactions/dumps/0020rule_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0020rule_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0020rule_0.nft b/tests/shell/testcases/transactions/dumps/0020rule_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0020rule_0.nft
diff --git a/tests/shell/testcases/transactions/dumps/0021rule_0.json-nft b/tests/shell/testcases/transactions/dumps/0021rule_0.json-nft
new file mode 100644
index 00000000..4c5500cc
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0021rule_0.json-nft
@@ -0,0 +1,54 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "right": "2.2.2.2"
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0022rule_1.json-nft b/tests/shell/testcases/transactions/dumps/0022rule_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0022rule_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0022rule_1.nft b/tests/shell/testcases/transactions/dumps/0022rule_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0022rule_1.nft
diff --git a/tests/shell/testcases/transactions/dumps/0023rule_1.json-nft b/tests/shell/testcases/transactions/dumps/0023rule_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0023rule_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0023rule_1.nft b/tests/shell/testcases/transactions/dumps/0023rule_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0023rule_1.nft
diff --git a/tests/shell/testcases/transactions/dumps/0024rule_0.json-nft b/tests/shell/testcases/transactions/dumps/0024rule_0.json-nft
new file mode 100644
index 00000000..1e37f7d9
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0024rule_0.json-nft
@@ -0,0 +1,82 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "comment": "rule1",
+ "expr": [
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "comment": "rule2",
+ "expr": [
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "comment": "rule3",
+ "expr": [
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "comment": "rule4",
+ "expr": [
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0025rule_0.json-nft b/tests/shell/testcases/transactions/dumps/0025rule_0.json-nft
new file mode 100644
index 00000000..623d9765
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0025rule_0.json-nft
@@ -0,0 +1,52 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "log": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "handle": 0,
+ "expr": [
+ {
+ "drop": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0030set_0.json-nft b/tests/shell/testcases/transactions/dumps/0030set_0.json-nft
new file mode 100644
index 00000000..15ec0aac
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0030set_0.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0031set_0.json-nft b/tests/shell/testcases/transactions/dumps/0031set_0.json-nft
new file mode 100644
index 00000000..c1b7639d
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0031set_0.json-nft
@@ -0,0 +1,27 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0032set_0.json-nft b/tests/shell/testcases/transactions/dumps/0032set_0.json-nft
new file mode 100644
index 00000000..66bbf0eb
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0032set_0.json-nft
@@ -0,0 +1,27 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "w",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "y",
+ "table": "w",
+ "type": "ipv4_addr",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0033set_0.json-nft b/tests/shell/testcases/transactions/dumps/0033set_0.json-nft
new file mode 100644
index 00000000..15ec0aac
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0033set_0.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0034set_0.json-nft b/tests/shell/testcases/transactions/dumps/0034set_0.json-nft
new file mode 100644
index 00000000..c1b7639d
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0034set_0.json-nft
@@ -0,0 +1,27 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0035set_0.json-nft b/tests/shell/testcases/transactions/dumps/0035set_0.json-nft
new file mode 100644
index 00000000..6b8f671c
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0035set_0.json-nft
@@ -0,0 +1,30 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "elem": [
+ "3.3.3.3"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0036set_1.json-nft b/tests/shell/testcases/transactions/dumps/0036set_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0036set_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0036set_1.nft b/tests/shell/testcases/transactions/dumps/0036set_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0036set_1.nft
diff --git a/tests/shell/testcases/transactions/dumps/0037set_0.json-nft b/tests/shell/testcases/transactions/dumps/0037set_0.json-nft
new file mode 100644
index 00000000..e4c77147
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0037set_0.json-nft
@@ -0,0 +1,30 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0038set_0.json-nft b/tests/shell/testcases/transactions/dumps/0038set_0.json-nft
new file mode 100644
index 00000000..0a36f4a8
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0038set_0.json-nft
@@ -0,0 +1,38 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "prefix": {
+ "addr": "192.168.4.0",
+ "len": 24
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0039set_0.json-nft b/tests/shell/testcases/transactions/dumps/0039set_0.json-nft
new file mode 100644
index 00000000..0a36f4a8
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0039set_0.json-nft
@@ -0,0 +1,38 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "y",
+ "table": "x",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "prefix": {
+ "addr": "192.168.4.0",
+ "len": 24
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0040set_0.json-nft b/tests/shell/testcases/transactions/dumps/0040set_0.json-nft
new file mode 100644
index 00000000..1718a5b9
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0040set_0.json-nft
@@ -0,0 +1,84 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "FORWARD",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "filter",
+ "name": "client_to_any",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "client_to_any",
+ "table": "filter",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "verdict"
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "FORWARD",
+ "handle": 0,
+ "expr": [
+ {
+ "goto": {
+ "target": "client_to_any"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "filter",
+ "chain": "client_to_any",
+ "handle": 0,
+ "expr": [
+ {
+ "vmap": {
+ "key": {
+ "payload": {
+ "protocol": "ip",
+ "field": "saddr"
+ }
+ },
+ "data": "@client_to_any"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0041nat_restore_0.json-nft b/tests/shell/testcases/transactions/dumps/0041nat_restore_0.json-nft
new file mode 100644
index 00000000..32fce943
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0041nat_restore_0.json-nft
@@ -0,0 +1,30 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0,
+ "type": "nat",
+ "hook": "postrouting",
+ "prio": 0,
+ "policy": "accept"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0041nat_restore_0.nft b/tests/shell/testcases/transactions/dumps/0041nat_restore_0.nft
new file mode 100644
index 00000000..b7180012
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0041nat_restore_0.nft
@@ -0,0 +1,5 @@
+table ip t {
+ chain c {
+ type nat hook postrouting priority filter; policy accept;
+ }
+}
diff --git a/tests/shell/testcases/transactions/dumps/0042_stateful_expr_0.json-nft b/tests/shell/testcases/transactions/dumps/0042_stateful_expr_0.json-nft
new file mode 100644
index 00000000..ea3b5d3c
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0042_stateful_expr_0.json-nft
@@ -0,0 +1,28 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "m1",
+ "table": "filter",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "counter"
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0042_stateful_expr_0.nft b/tests/shell/testcases/transactions/dumps/0042_stateful_expr_0.nft
new file mode 100644
index 00000000..e5cc63f2
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0042_stateful_expr_0.nft
@@ -0,0 +1,5 @@
+table ip filter {
+ map m1 {
+ type ipv4_addr : counter
+ }
+}
diff --git a/tests/shell/testcases/transactions/dumps/0043set_1.json-nft b/tests/shell/testcases/transactions/dumps/0043set_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0043set_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0043set_1.nft b/tests/shell/testcases/transactions/dumps/0043set_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0043set_1.nft
diff --git a/tests/shell/testcases/transactions/dumps/0044rule_0.json-nft b/tests/shell/testcases/transactions/dumps/0044rule_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0044rule_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0044rule_0.nft b/tests/shell/testcases/transactions/dumps/0044rule_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0044rule_0.nft
diff --git a/tests/shell/testcases/transactions/dumps/0045anon-unbind_0.json-nft b/tests/shell/testcases/transactions/dumps/0045anon-unbind_0.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0045anon-unbind_0.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0045anon-unbind_0.nft b/tests/shell/testcases/transactions/dumps/0045anon-unbind_0.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0045anon-unbind_0.nft
diff --git a/tests/shell/testcases/transactions/dumps/0046set_0.json-nft b/tests/shell/testcases/transactions/dumps/0046set_0.json-nft
new file mode 100644
index 00000000..f9b488e7
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0046set_0.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "filter",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0046set_0.nft b/tests/shell/testcases/transactions/dumps/0046set_0.nft
new file mode 100644
index 00000000..eb39c44f
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0046set_0.nft
@@ -0,0 +1,2 @@
+table ip filter {
+}
diff --git a/tests/shell/testcases/transactions/dumps/0047set_0.json-nft b/tests/shell/testcases/transactions/dumps/0047set_0.json-nft
new file mode 100644
index 00000000..a7e677b2
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0047set_0.json-nft
@@ -0,0 +1,73 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "filter",
+ "handle": 0
+ }
+ },
+ {
+ "map": {
+ "family": "ip",
+ "name": "group_10060",
+ "table": "filter",
+ "type": "ipv4_addr",
+ "handle": 0,
+ "map": "classid",
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ [
+ "10.1.26.2",
+ "1:bbf8"
+ ],
+ [
+ "10.1.26.3",
+ "1:c1ad"
+ ],
+ [
+ "10.1.26.4",
+ "1:b2d7"
+ ],
+ [
+ "10.1.26.5",
+ "1:f705"
+ ],
+ [
+ "10.1.26.6",
+ "1:b895"
+ ],
+ [
+ "10.1.26.7",
+ "1:ec4c"
+ ],
+ [
+ "10.1.26.8",
+ "1:de78"
+ ],
+ [
+ "10.1.26.9",
+ "1:b4f3"
+ ],
+ [
+ "10.1.26.10",
+ "1:dec6"
+ ],
+ [
+ "10.1.26.11",
+ "1:b4c0"
+ ]
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0047set_0.nft b/tests/shell/testcases/transactions/dumps/0047set_0.nft
new file mode 100644
index 00000000..4da397b2
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0047set_0.nft
@@ -0,0 +1,11 @@
+table ip filter {
+ map group_10060 {
+ type ipv4_addr : classid
+ flags interval
+ elements = { 10.1.26.2 : 1:bbf8, 10.1.26.3 : 1:c1ad,
+ 10.1.26.4 : 1:b2d7, 10.1.26.5 : 1:f705,
+ 10.1.26.6 : 1:b895, 10.1.26.7 : 1:ec4c,
+ 10.1.26.8 : 1:de78, 10.1.26.9 : 1:b4f3,
+ 10.1.26.10 : 1:dec6, 10.1.26.11 : 1:b4c0 }
+ }
+}
diff --git a/tests/shell/testcases/transactions/dumps/0048helpers_0.json-nft b/tests/shell/testcases/transactions/dumps/0048helpers_0.json-nft
new file mode 100644
index 00000000..f9b488e7
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0048helpers_0.json-nft
@@ -0,0 +1,18 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "filter",
+ "handle": 0
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0048helpers_0.nft b/tests/shell/testcases/transactions/dumps/0048helpers_0.nft
new file mode 100644
index 00000000..eb39c44f
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0048helpers_0.nft
@@ -0,0 +1,2 @@
+table ip filter {
+}
diff --git a/tests/shell/testcases/transactions/dumps/0049huge_0.json-nft b/tests/shell/testcases/transactions/dumps/0049huge_0.json-nft
new file mode 100644
index 00000000..456ada94
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0049huge_0.json-nft
@@ -0,0 +1,5121 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "inet",
+ "name": "firewalld",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PREROUTING",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -290,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PREROUTING_ZONES",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PREROUTING",
+ "handle": 0,
+ "type": "filter",
+ "hook": "prerouting",
+ "prio": -140,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PREROUTING_ZONES",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_INPUT",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FORWARD",
+ "handle": 0,
+ "type": "filter",
+ "hook": "forward",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_OUTPUT",
+ "handle": 0,
+ "type": "filter",
+ "hook": "output",
+ "prio": 10,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_INPUT_ZONES",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FORWARD_IN_ZONES",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FORWARD_OUT_ZONES",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_public",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_public_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_public_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_public_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_public_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_public_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_public",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_public_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_public_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_public_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_public_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_public_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_public",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_public_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_public_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_public_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_public_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_public_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_public",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_public_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_public_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_public_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_public_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_public_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_public",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_public_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_public_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_public_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_public_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_public_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_trusted",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_trusted_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_trusted_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_trusted_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_trusted_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_trusted_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_trusted",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_trusted_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_trusted_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_trusted_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_trusted_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_trusted_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_trusted",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_trusted_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_trusted_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_trusted_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_trusted_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_trusted_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_trusted",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_trusted_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_trusted_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_trusted_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_trusted_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_trusted_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_trusted",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_trusted_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_trusted_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_trusted_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_trusted_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_trusted_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_work",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_work_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_work_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_work_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_work_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "raw_PRE_work_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_work",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_work_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_work_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_work_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_work_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_IN_work_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_work",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_work_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_work_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_work_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_work_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "mangle_PRE_work_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_work",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_work_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_work_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_work_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_work_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDI_work_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_work",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_work_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_work_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_work_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_work_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "inet",
+ "table": "firewalld",
+ "name": "filter_FWDO_work_post",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PREROUTING",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "icmpv6",
+ "field": "type"
+ }
+ },
+ "right": {
+ "set": [
+ "nd-router-advert",
+ "nd-neighbor-solicit"
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PREROUTING",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "nfproto"
+ }
+ },
+ "right": "ipv6"
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "fib": {
+ "result": "oif",
+ "flags": [
+ "saddr",
+ "iif"
+ ]
+ }
+ },
+ "right": false
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PREROUTING",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PREROUTING_ZONES"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PREROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "perm_dummy"
+ }
+ },
+ {
+ "goto": {
+ "target": "raw_PRE_work"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PREROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "perm_dummy2"
+ }
+ },
+ {
+ "goto": {
+ "target": "raw_PRE_trusted"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PREROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "goto": {
+ "target": "raw_PRE_public"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PREROUTING",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PREROUTING_ZONES"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PREROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "perm_dummy"
+ }
+ },
+ {
+ "goto": {
+ "target": "mangle_PRE_work"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PREROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "perm_dummy2"
+ }
+ },
+ {
+ "goto": {
+ "target": "mangle_PRE_trusted"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PREROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "goto": {
+ "target": "mangle_PRE_public"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_INPUT",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": {
+ "set": [
+ "established",
+ "related"
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_INPUT",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "status"
+ }
+ },
+ "right": "dnat"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_INPUT",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "lo"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_INPUT",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_INPUT_ZONES"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_INPUT",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": "invalid"
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_INPUT",
+ "handle": 0,
+ "expr": [
+ {
+ "reject": {
+ "type": "icmpx",
+ "expr": "admin-prohibited"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": {
+ "set": [
+ "established",
+ "related"
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "status"
+ }
+ },
+ "right": "dnat"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "lo"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip6",
+ "field": "daddr"
+ }
+ },
+ "right": {
+ "set": [
+ {
+ "prefix": {
+ "addr": "::",
+ "len": 96
+ }
+ },
+ {
+ "prefix": {
+ "addr": "::ffff:0.0.0.0",
+ "len": 96
+ }
+ },
+ {
+ "prefix": {
+ "addr": "2002::",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "2002:a00::",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "2002:7f00::",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "2002:a9fe::",
+ "len": 32
+ }
+ },
+ {
+ "prefix": {
+ "addr": "2002:ac10::",
+ "len": 28
+ }
+ },
+ {
+ "prefix": {
+ "addr": "2002:c0a8::",
+ "len": 32
+ }
+ },
+ {
+ "prefix": {
+ "addr": "2002:e000::",
+ "len": 19
+ }
+ }
+ ]
+ }
+ }
+ },
+ {
+ "reject": {
+ "type": "icmpv6",
+ "expr": "addr-unreachable"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FORWARD_IN_ZONES"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FORWARD_OUT_ZONES"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "in",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": "invalid"
+ }
+ },
+ {
+ "drop": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD",
+ "handle": 0,
+ "expr": [
+ {
+ "reject": {
+ "type": "icmpx",
+ "expr": "admin-prohibited"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_OUTPUT",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oifname"
+ }
+ },
+ "right": "lo"
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_OUTPUT",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip6",
+ "field": "daddr"
+ }
+ },
+ "right": {
+ "set": [
+ {
+ "prefix": {
+ "addr": "::",
+ "len": 96
+ }
+ },
+ {
+ "prefix": {
+ "addr": "::ffff:0.0.0.0",
+ "len": 96
+ }
+ },
+ {
+ "prefix": {
+ "addr": "2002::",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "2002:a00::",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "2002:7f00::",
+ "len": 24
+ }
+ },
+ {
+ "prefix": {
+ "addr": "2002:a9fe::",
+ "len": 32
+ }
+ },
+ {
+ "prefix": {
+ "addr": "2002:ac10::",
+ "len": 28
+ }
+ },
+ {
+ "prefix": {
+ "addr": "2002:c0a8::",
+ "len": 32
+ }
+ },
+ {
+ "prefix": {
+ "addr": "2002:e000::",
+ "len": 19
+ }
+ }
+ ]
+ }
+ }
+ },
+ {
+ "reject": {
+ "type": "icmpv6",
+ "expr": "addr-unreachable"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_INPUT_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "perm_dummy"
+ }
+ },
+ {
+ "goto": {
+ "target": "filter_IN_work"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_INPUT_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "perm_dummy2"
+ }
+ },
+ {
+ "goto": {
+ "target": "filter_IN_trusted"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_INPUT_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "goto": {
+ "target": "filter_IN_public"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD_IN_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "perm_dummy"
+ }
+ },
+ {
+ "goto": {
+ "target": "filter_FWDI_work"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD_IN_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "perm_dummy2"
+ }
+ },
+ {
+ "goto": {
+ "target": "filter_FWDI_trusted"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD_IN_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "goto": {
+ "target": "filter_FWDI_public"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD_OUT_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oifname"
+ }
+ },
+ "right": "perm_dummy"
+ }
+ },
+ {
+ "goto": {
+ "target": "filter_FWDO_work"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD_OUT_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oifname"
+ }
+ },
+ "right": "perm_dummy2"
+ }
+ },
+ {
+ "goto": {
+ "target": "filter_FWDO_trusted"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FORWARD_OUT_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "goto": {
+ "target": "filter_FWDO_public"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_public_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_public_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_public_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_public_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_public_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_public_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_public_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_public_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_public_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_public_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_public",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "right": {
+ "set": [
+ "icmp",
+ "ipv6-icmp"
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_public_allow",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 22
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": {
+ "set": [
+ "new",
+ "untracked"
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_public_allow",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip6",
+ "field": "daddr"
+ }
+ },
+ "right": {
+ "prefix": {
+ "addr": "fe80::",
+ "len": 64
+ }
+ }
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 546
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": {
+ "set": [
+ "new",
+ "untracked"
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_public_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_public_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_public_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_public_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_public_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_public",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "right": {
+ "set": [
+ "icmp",
+ "ipv6-icmp"
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_public_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_public_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_public_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_public_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_public_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_public_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_public_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_public_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_public_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_public_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_trusted_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_trusted_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_trusted_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_trusted_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_trusted_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_trusted_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_trusted_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_trusted_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_trusted_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_trusted_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_trusted_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_trusted_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_trusted_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_trusted_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_trusted_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_trusted_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_trusted_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_trusted_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_trusted_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_trusted_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_trusted_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_trusted_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_trusted_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_trusted_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_trusted_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_work_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_work_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_work_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_work_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "raw_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "raw_PRE_work_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_work_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_work_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_work_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_work_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_IN_work_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_work",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "right": {
+ "set": [
+ "icmp",
+ "ipv6-icmp"
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_work_allow",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "tcp",
+ "field": "dport"
+ }
+ },
+ "right": 22
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": {
+ "set": [
+ "new",
+ "untracked"
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_IN_work_allow",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip6",
+ "field": "daddr"
+ }
+ },
+ "right": {
+ "prefix": {
+ "addr": "fe80::",
+ "len": 64
+ }
+ }
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "udp",
+ "field": "dport"
+ }
+ },
+ "right": 546
+ }
+ },
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "ct": {
+ "key": "state"
+ }
+ },
+ "right": {
+ "set": [
+ "new",
+ "untracked"
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_work_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_work_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_work_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_work_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "mangle_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "mangle_PRE_work_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_work_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_work_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_work_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_work_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDI_work_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDI_work",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "l4proto"
+ }
+ },
+ "right": {
+ "set": [
+ "icmp",
+ "ipv6-icmp"
+ ]
+ }
+ }
+ },
+ {
+ "accept": null
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_work_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_work_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_work_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_work_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "inet",
+ "table": "firewalld",
+ "chain": "filter_FWDO_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "filter_FWDO_work_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "firewalld",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PREROUTING",
+ "handle": 0,
+ "type": "nat",
+ "hook": "prerouting",
+ "prio": -90,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PREROUTING_ZONES",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POSTROUTING",
+ "handle": 0,
+ "type": "nat",
+ "hook": "postrouting",
+ "prio": 110,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POSTROUTING_ZONES",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_public",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_public_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_public_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_public_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_public_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_public_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_public",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_public_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_public_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_public_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_public_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_public_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_trusted",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_trusted_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_trusted_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_trusted_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_trusted_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_trusted_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_trusted",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_trusted_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_trusted_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_trusted_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_trusted_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_trusted_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_work",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_work_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_work_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_work_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_work_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_PRE_work_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_work",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_work_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_work_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_work_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_work_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "firewalld",
+ "name": "nat_POST_work_post",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PREROUTING",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PREROUTING_ZONES"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PREROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "perm_dummy"
+ }
+ },
+ {
+ "goto": {
+ "target": "nat_PRE_work"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PREROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "perm_dummy2"
+ }
+ },
+ {
+ "goto": {
+ "target": "nat_PRE_trusted"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PREROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "goto": {
+ "target": "nat_PRE_public"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POSTROUTING",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POSTROUTING_ZONES"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POSTROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oifname"
+ }
+ },
+ "right": "perm_dummy"
+ }
+ },
+ {
+ "goto": {
+ "target": "nat_POST_work"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POSTROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oifname"
+ }
+ },
+ "right": "perm_dummy2"
+ }
+ },
+ {
+ "goto": {
+ "target": "nat_POST_trusted"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POSTROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "goto": {
+ "target": "nat_POST_public"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_public_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_public_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_public_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_public_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_public_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_public_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_public_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_public_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_public_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_public_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_trusted_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_trusted_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_trusted_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_trusted_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_trusted_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_trusted_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_trusted_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_trusted_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_trusted_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_trusted_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_work_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_work_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_work_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_work_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_work_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_work_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_work_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_work_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_work_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "firewalld",
+ "chain": "nat_POST_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_work_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "table": {
+ "family": "ip6",
+ "name": "firewalld",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PREROUTING",
+ "handle": 0,
+ "type": "nat",
+ "hook": "prerouting",
+ "prio": -90,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PREROUTING_ZONES",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POSTROUTING",
+ "handle": 0,
+ "type": "nat",
+ "hook": "postrouting",
+ "prio": 110,
+ "policy": "accept"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POSTROUTING_ZONES",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_public",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_public_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_public_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_public_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_public_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_public_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_public",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_public_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_public_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_public_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_public_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_public_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_trusted",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_trusted_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_trusted_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_trusted_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_trusted_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_trusted_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_trusted",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_trusted_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_trusted_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_trusted_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_trusted_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_trusted_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_work",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_work_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_work_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_work_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_work_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_PRE_work_post",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_work",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_work_pre",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_work_log",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_work_deny",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_work_allow",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip6",
+ "table": "firewalld",
+ "name": "nat_POST_work_post",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PREROUTING",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PREROUTING_ZONES"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PREROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "perm_dummy"
+ }
+ },
+ {
+ "goto": {
+ "target": "nat_PRE_work"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PREROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "iifname"
+ }
+ },
+ "right": "perm_dummy2"
+ }
+ },
+ {
+ "goto": {
+ "target": "nat_PRE_trusted"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PREROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "goto": {
+ "target": "nat_PRE_public"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POSTROUTING",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POSTROUTING_ZONES"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POSTROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oifname"
+ }
+ },
+ "right": "perm_dummy"
+ }
+ },
+ {
+ "goto": {
+ "target": "nat_POST_work"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POSTROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "meta": {
+ "key": "oifname"
+ }
+ },
+ "right": "perm_dummy2"
+ }
+ },
+ {
+ "goto": {
+ "target": "nat_POST_trusted"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POSTROUTING_ZONES",
+ "handle": 0,
+ "expr": [
+ {
+ "goto": {
+ "target": "nat_POST_public"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_public_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_public_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_public_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_public_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_public_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_public_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_public_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_public_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_public_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_public",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_public_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_trusted_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_trusted_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_trusted_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_trusted_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_trusted_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_trusted_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_trusted_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_trusted_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_trusted_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_trusted",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_trusted_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_work_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_work_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_work_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_work_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_PRE_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_PRE_work_post"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_work_pre"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_work_log"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_work_deny"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_work_allow"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "rule": {
+ "family": "ip6",
+ "table": "firewalld",
+ "chain": "nat_POST_work",
+ "handle": 0,
+ "expr": [
+ {
+ "jump": {
+ "target": "nat_POST_work_post"
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0049huge_0.nft b/tests/shell/testcases/transactions/dumps/0049huge_0.nft
new file mode 100644
index 00000000..96f5a387
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0049huge_0.nft
@@ -0,0 +1,749 @@
+table inet firewalld {
+ chain raw_PREROUTING {
+ type filter hook prerouting priority raw + 10; policy accept;
+ icmpv6 type { nd-router-advert, nd-neighbor-solicit } accept
+ meta nfproto ipv6 fib saddr . iif oif missing drop
+ jump raw_PREROUTING_ZONES
+ }
+
+ chain raw_PREROUTING_ZONES {
+ iifname "perm_dummy" goto raw_PRE_work
+ iifname "perm_dummy2" goto raw_PRE_trusted
+ goto raw_PRE_public
+ }
+
+ chain mangle_PREROUTING {
+ type filter hook prerouting priority mangle + 10; policy accept;
+ jump mangle_PREROUTING_ZONES
+ }
+
+ chain mangle_PREROUTING_ZONES {
+ iifname "perm_dummy" goto mangle_PRE_work
+ iifname "perm_dummy2" goto mangle_PRE_trusted
+ goto mangle_PRE_public
+ }
+
+ chain filter_INPUT {
+ type filter hook input priority filter + 10; policy accept;
+ ct state { established, related } accept
+ ct status dnat accept
+ iifname "lo" accept
+ jump filter_INPUT_ZONES
+ ct state invalid drop
+ reject with icmpx admin-prohibited
+ }
+
+ chain filter_FORWARD {
+ type filter hook forward priority filter + 10; policy accept;
+ ct state { established, related } accept
+ ct status dnat accept
+ iifname "lo" accept
+ ip6 daddr { ::/96, ::ffff:0.0.0.0/96, 2002::/24, 2002:a00::/24, 2002:7f00::/24, 2002:a9fe::/32, 2002:ac10::/28, 2002:c0a8::/32, 2002:e000::/19 } reject with icmpv6 addr-unreachable
+ jump filter_FORWARD_IN_ZONES
+ jump filter_FORWARD_OUT_ZONES
+ ct state invalid drop
+ reject with icmpx admin-prohibited
+ }
+
+ chain filter_OUTPUT {
+ type filter hook output priority filter + 10; policy accept;
+ oifname "lo" accept
+ ip6 daddr { ::/96, ::ffff:0.0.0.0/96, 2002::/24, 2002:a00::/24, 2002:7f00::/24, 2002:a9fe::/32, 2002:ac10::/28, 2002:c0a8::/32, 2002:e000::/19 } reject with icmpv6 addr-unreachable
+ }
+
+ chain filter_INPUT_ZONES {
+ iifname "perm_dummy" goto filter_IN_work
+ iifname "perm_dummy2" goto filter_IN_trusted
+ goto filter_IN_public
+ }
+
+ chain filter_FORWARD_IN_ZONES {
+ iifname "perm_dummy" goto filter_FWDI_work
+ iifname "perm_dummy2" goto filter_FWDI_trusted
+ goto filter_FWDI_public
+ }
+
+ chain filter_FORWARD_OUT_ZONES {
+ oifname "perm_dummy" goto filter_FWDO_work
+ oifname "perm_dummy2" goto filter_FWDO_trusted
+ goto filter_FWDO_public
+ }
+
+ chain raw_PRE_public {
+ jump raw_PRE_public_pre
+ jump raw_PRE_public_log
+ jump raw_PRE_public_deny
+ jump raw_PRE_public_allow
+ jump raw_PRE_public_post
+ }
+
+ chain raw_PRE_public_pre {
+ }
+
+ chain raw_PRE_public_log {
+ }
+
+ chain raw_PRE_public_deny {
+ }
+
+ chain raw_PRE_public_allow {
+ }
+
+ chain raw_PRE_public_post {
+ }
+
+ chain filter_IN_public {
+ jump filter_IN_public_pre
+ jump filter_IN_public_log
+ jump filter_IN_public_deny
+ jump filter_IN_public_allow
+ jump filter_IN_public_post
+ meta l4proto { icmp, ipv6-icmp } accept
+ }
+
+ chain filter_IN_public_pre {
+ }
+
+ chain filter_IN_public_log {
+ }
+
+ chain filter_IN_public_deny {
+ }
+
+ chain filter_IN_public_allow {
+ tcp dport 22 ct state { new, untracked } accept
+ ip6 daddr fe80::/64 udp dport 546 ct state { new, untracked } accept
+ }
+
+ chain filter_IN_public_post {
+ }
+
+ chain filter_FWDI_public {
+ jump filter_FWDI_public_pre
+ jump filter_FWDI_public_log
+ jump filter_FWDI_public_deny
+ jump filter_FWDI_public_allow
+ jump filter_FWDI_public_post
+ meta l4proto { icmp, ipv6-icmp } accept
+ }
+
+ chain filter_FWDI_public_pre {
+ }
+
+ chain filter_FWDI_public_log {
+ }
+
+ chain filter_FWDI_public_deny {
+ }
+
+ chain filter_FWDI_public_allow {
+ }
+
+ chain filter_FWDI_public_post {
+ }
+
+ chain mangle_PRE_public {
+ jump mangle_PRE_public_pre
+ jump mangle_PRE_public_log
+ jump mangle_PRE_public_deny
+ jump mangle_PRE_public_allow
+ jump mangle_PRE_public_post
+ }
+
+ chain mangle_PRE_public_pre {
+ }
+
+ chain mangle_PRE_public_log {
+ }
+
+ chain mangle_PRE_public_deny {
+ }
+
+ chain mangle_PRE_public_allow {
+ }
+
+ chain mangle_PRE_public_post {
+ }
+
+ chain filter_FWDO_public {
+ jump filter_FWDO_public_pre
+ jump filter_FWDO_public_log
+ jump filter_FWDO_public_deny
+ jump filter_FWDO_public_allow
+ jump filter_FWDO_public_post
+ }
+
+ chain filter_FWDO_public_pre {
+ }
+
+ chain filter_FWDO_public_log {
+ }
+
+ chain filter_FWDO_public_deny {
+ }
+
+ chain filter_FWDO_public_allow {
+ }
+
+ chain filter_FWDO_public_post {
+ }
+
+ chain raw_PRE_trusted {
+ jump raw_PRE_trusted_pre
+ jump raw_PRE_trusted_log
+ jump raw_PRE_trusted_deny
+ jump raw_PRE_trusted_allow
+ jump raw_PRE_trusted_post
+ }
+
+ chain raw_PRE_trusted_pre {
+ }
+
+ chain raw_PRE_trusted_log {
+ }
+
+ chain raw_PRE_trusted_deny {
+ }
+
+ chain raw_PRE_trusted_allow {
+ }
+
+ chain raw_PRE_trusted_post {
+ }
+
+ chain mangle_PRE_trusted {
+ jump mangle_PRE_trusted_pre
+ jump mangle_PRE_trusted_log
+ jump mangle_PRE_trusted_deny
+ jump mangle_PRE_trusted_allow
+ jump mangle_PRE_trusted_post
+ }
+
+ chain mangle_PRE_trusted_pre {
+ }
+
+ chain mangle_PRE_trusted_log {
+ }
+
+ chain mangle_PRE_trusted_deny {
+ }
+
+ chain mangle_PRE_trusted_allow {
+ }
+
+ chain mangle_PRE_trusted_post {
+ }
+
+ chain filter_IN_trusted {
+ jump filter_IN_trusted_pre
+ jump filter_IN_trusted_log
+ jump filter_IN_trusted_deny
+ jump filter_IN_trusted_allow
+ jump filter_IN_trusted_post
+ accept
+ }
+
+ chain filter_IN_trusted_pre {
+ }
+
+ chain filter_IN_trusted_log {
+ }
+
+ chain filter_IN_trusted_deny {
+ }
+
+ chain filter_IN_trusted_allow {
+ }
+
+ chain filter_IN_trusted_post {
+ }
+
+ chain filter_FWDI_trusted {
+ jump filter_FWDI_trusted_pre
+ jump filter_FWDI_trusted_log
+ jump filter_FWDI_trusted_deny
+ jump filter_FWDI_trusted_allow
+ jump filter_FWDI_trusted_post
+ accept
+ }
+
+ chain filter_FWDI_trusted_pre {
+ }
+
+ chain filter_FWDI_trusted_log {
+ }
+
+ chain filter_FWDI_trusted_deny {
+ }
+
+ chain filter_FWDI_trusted_allow {
+ }
+
+ chain filter_FWDI_trusted_post {
+ }
+
+ chain filter_FWDO_trusted {
+ jump filter_FWDO_trusted_pre
+ jump filter_FWDO_trusted_log
+ jump filter_FWDO_trusted_deny
+ jump filter_FWDO_trusted_allow
+ jump filter_FWDO_trusted_post
+ accept
+ }
+
+ chain filter_FWDO_trusted_pre {
+ }
+
+ chain filter_FWDO_trusted_log {
+ }
+
+ chain filter_FWDO_trusted_deny {
+ }
+
+ chain filter_FWDO_trusted_allow {
+ }
+
+ chain filter_FWDO_trusted_post {
+ }
+
+ chain raw_PRE_work {
+ jump raw_PRE_work_pre
+ jump raw_PRE_work_log
+ jump raw_PRE_work_deny
+ jump raw_PRE_work_allow
+ jump raw_PRE_work_post
+ }
+
+ chain raw_PRE_work_pre {
+ }
+
+ chain raw_PRE_work_log {
+ }
+
+ chain raw_PRE_work_deny {
+ }
+
+ chain raw_PRE_work_allow {
+ }
+
+ chain raw_PRE_work_post {
+ }
+
+ chain filter_IN_work {
+ jump filter_IN_work_pre
+ jump filter_IN_work_log
+ jump filter_IN_work_deny
+ jump filter_IN_work_allow
+ jump filter_IN_work_post
+ meta l4proto { icmp, ipv6-icmp } accept
+ }
+
+ chain filter_IN_work_pre {
+ }
+
+ chain filter_IN_work_log {
+ }
+
+ chain filter_IN_work_deny {
+ }
+
+ chain filter_IN_work_allow {
+ tcp dport 22 ct state { new, untracked } accept
+ ip6 daddr fe80::/64 udp dport 546 ct state { new, untracked } accept
+ }
+
+ chain filter_IN_work_post {
+ }
+
+ chain mangle_PRE_work {
+ jump mangle_PRE_work_pre
+ jump mangle_PRE_work_log
+ jump mangle_PRE_work_deny
+ jump mangle_PRE_work_allow
+ jump mangle_PRE_work_post
+ }
+
+ chain mangle_PRE_work_pre {
+ }
+
+ chain mangle_PRE_work_log {
+ }
+
+ chain mangle_PRE_work_deny {
+ }
+
+ chain mangle_PRE_work_allow {
+ }
+
+ chain mangle_PRE_work_post {
+ }
+
+ chain filter_FWDI_work {
+ jump filter_FWDI_work_pre
+ jump filter_FWDI_work_log
+ jump filter_FWDI_work_deny
+ jump filter_FWDI_work_allow
+ jump filter_FWDI_work_post
+ meta l4proto { icmp, ipv6-icmp } accept
+ }
+
+ chain filter_FWDI_work_pre {
+ }
+
+ chain filter_FWDI_work_log {
+ }
+
+ chain filter_FWDI_work_deny {
+ }
+
+ chain filter_FWDI_work_allow {
+ }
+
+ chain filter_FWDI_work_post {
+ }
+
+ chain filter_FWDO_work {
+ jump filter_FWDO_work_pre
+ jump filter_FWDO_work_log
+ jump filter_FWDO_work_deny
+ jump filter_FWDO_work_allow
+ jump filter_FWDO_work_post
+ }
+
+ chain filter_FWDO_work_pre {
+ }
+
+ chain filter_FWDO_work_log {
+ }
+
+ chain filter_FWDO_work_deny {
+ }
+
+ chain filter_FWDO_work_allow {
+ }
+
+ chain filter_FWDO_work_post {
+ }
+}
+table ip firewalld {
+ chain nat_PREROUTING {
+ type nat hook prerouting priority dstnat + 10; policy accept;
+ jump nat_PREROUTING_ZONES
+ }
+
+ chain nat_PREROUTING_ZONES {
+ iifname "perm_dummy" goto nat_PRE_work
+ iifname "perm_dummy2" goto nat_PRE_trusted
+ goto nat_PRE_public
+ }
+
+ chain nat_POSTROUTING {
+ type nat hook postrouting priority srcnat + 10; policy accept;
+ jump nat_POSTROUTING_ZONES
+ }
+
+ chain nat_POSTROUTING_ZONES {
+ oifname "perm_dummy" goto nat_POST_work
+ oifname "perm_dummy2" goto nat_POST_trusted
+ goto nat_POST_public
+ }
+
+ chain nat_PRE_public {
+ jump nat_PRE_public_pre
+ jump nat_PRE_public_log
+ jump nat_PRE_public_deny
+ jump nat_PRE_public_allow
+ jump nat_PRE_public_post
+ }
+
+ chain nat_PRE_public_pre {
+ }
+
+ chain nat_PRE_public_log {
+ }
+
+ chain nat_PRE_public_deny {
+ }
+
+ chain nat_PRE_public_allow {
+ }
+
+ chain nat_PRE_public_post {
+ }
+
+ chain nat_POST_public {
+ jump nat_POST_public_pre
+ jump nat_POST_public_log
+ jump nat_POST_public_deny
+ jump nat_POST_public_allow
+ jump nat_POST_public_post
+ }
+
+ chain nat_POST_public_pre {
+ }
+
+ chain nat_POST_public_log {
+ }
+
+ chain nat_POST_public_deny {
+ }
+
+ chain nat_POST_public_allow {
+ }
+
+ chain nat_POST_public_post {
+ }
+
+ chain nat_PRE_trusted {
+ jump nat_PRE_trusted_pre
+ jump nat_PRE_trusted_log
+ jump nat_PRE_trusted_deny
+ jump nat_PRE_trusted_allow
+ jump nat_PRE_trusted_post
+ }
+
+ chain nat_PRE_trusted_pre {
+ }
+
+ chain nat_PRE_trusted_log {
+ }
+
+ chain nat_PRE_trusted_deny {
+ }
+
+ chain nat_PRE_trusted_allow {
+ }
+
+ chain nat_PRE_trusted_post {
+ }
+
+ chain nat_POST_trusted {
+ jump nat_POST_trusted_pre
+ jump nat_POST_trusted_log
+ jump nat_POST_trusted_deny
+ jump nat_POST_trusted_allow
+ jump nat_POST_trusted_post
+ }
+
+ chain nat_POST_trusted_pre {
+ }
+
+ chain nat_POST_trusted_log {
+ }
+
+ chain nat_POST_trusted_deny {
+ }
+
+ chain nat_POST_trusted_allow {
+ }
+
+ chain nat_POST_trusted_post {
+ }
+
+ chain nat_PRE_work {
+ jump nat_PRE_work_pre
+ jump nat_PRE_work_log
+ jump nat_PRE_work_deny
+ jump nat_PRE_work_allow
+ jump nat_PRE_work_post
+ }
+
+ chain nat_PRE_work_pre {
+ }
+
+ chain nat_PRE_work_log {
+ }
+
+ chain nat_PRE_work_deny {
+ }
+
+ chain nat_PRE_work_allow {
+ }
+
+ chain nat_PRE_work_post {
+ }
+
+ chain nat_POST_work {
+ jump nat_POST_work_pre
+ jump nat_POST_work_log
+ jump nat_POST_work_deny
+ jump nat_POST_work_allow
+ jump nat_POST_work_post
+ }
+
+ chain nat_POST_work_pre {
+ }
+
+ chain nat_POST_work_log {
+ }
+
+ chain nat_POST_work_deny {
+ }
+
+ chain nat_POST_work_allow {
+ }
+
+ chain nat_POST_work_post {
+ }
+}
+table ip6 firewalld {
+ chain nat_PREROUTING {
+ type nat hook prerouting priority dstnat + 10; policy accept;
+ jump nat_PREROUTING_ZONES
+ }
+
+ chain nat_PREROUTING_ZONES {
+ iifname "perm_dummy" goto nat_PRE_work
+ iifname "perm_dummy2" goto nat_PRE_trusted
+ goto nat_PRE_public
+ }
+
+ chain nat_POSTROUTING {
+ type nat hook postrouting priority srcnat + 10; policy accept;
+ jump nat_POSTROUTING_ZONES
+ }
+
+ chain nat_POSTROUTING_ZONES {
+ oifname "perm_dummy" goto nat_POST_work
+ oifname "perm_dummy2" goto nat_POST_trusted
+ goto nat_POST_public
+ }
+
+ chain nat_PRE_public {
+ jump nat_PRE_public_pre
+ jump nat_PRE_public_log
+ jump nat_PRE_public_deny
+ jump nat_PRE_public_allow
+ jump nat_PRE_public_post
+ }
+
+ chain nat_PRE_public_pre {
+ }
+
+ chain nat_PRE_public_log {
+ }
+
+ chain nat_PRE_public_deny {
+ }
+
+ chain nat_PRE_public_allow {
+ }
+
+ chain nat_PRE_public_post {
+ }
+
+ chain nat_POST_public {
+ jump nat_POST_public_pre
+ jump nat_POST_public_log
+ jump nat_POST_public_deny
+ jump nat_POST_public_allow
+ jump nat_POST_public_post
+ }
+
+ chain nat_POST_public_pre {
+ }
+
+ chain nat_POST_public_log {
+ }
+
+ chain nat_POST_public_deny {
+ }
+
+ chain nat_POST_public_allow {
+ }
+
+ chain nat_POST_public_post {
+ }
+
+ chain nat_PRE_trusted {
+ jump nat_PRE_trusted_pre
+ jump nat_PRE_trusted_log
+ jump nat_PRE_trusted_deny
+ jump nat_PRE_trusted_allow
+ jump nat_PRE_trusted_post
+ }
+
+ chain nat_PRE_trusted_pre {
+ }
+
+ chain nat_PRE_trusted_log {
+ }
+
+ chain nat_PRE_trusted_deny {
+ }
+
+ chain nat_PRE_trusted_allow {
+ }
+
+ chain nat_PRE_trusted_post {
+ }
+
+ chain nat_POST_trusted {
+ jump nat_POST_trusted_pre
+ jump nat_POST_trusted_log
+ jump nat_POST_trusted_deny
+ jump nat_POST_trusted_allow
+ jump nat_POST_trusted_post
+ }
+
+ chain nat_POST_trusted_pre {
+ }
+
+ chain nat_POST_trusted_log {
+ }
+
+ chain nat_POST_trusted_deny {
+ }
+
+ chain nat_POST_trusted_allow {
+ }
+
+ chain nat_POST_trusted_post {
+ }
+
+ chain nat_PRE_work {
+ jump nat_PRE_work_pre
+ jump nat_PRE_work_log
+ jump nat_PRE_work_deny
+ jump nat_PRE_work_allow
+ jump nat_PRE_work_post
+ }
+
+ chain nat_PRE_work_pre {
+ }
+
+ chain nat_PRE_work_log {
+ }
+
+ chain nat_PRE_work_deny {
+ }
+
+ chain nat_PRE_work_allow {
+ }
+
+ chain nat_PRE_work_post {
+ }
+
+ chain nat_POST_work {
+ jump nat_POST_work_pre
+ jump nat_POST_work_log
+ jump nat_POST_work_deny
+ jump nat_POST_work_allow
+ jump nat_POST_work_post
+ }
+
+ chain nat_POST_work_pre {
+ }
+
+ chain nat_POST_work_log {
+ }
+
+ chain nat_POST_work_deny {
+ }
+
+ chain nat_POST_work_allow {
+ }
+
+ chain nat_POST_work_post {
+ }
+}
diff --git a/tests/shell/testcases/transactions/dumps/0050rule_1.json-nft b/tests/shell/testcases/transactions/dumps/0050rule_1.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0050rule_1.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/0050rule_1.nft b/tests/shell/testcases/transactions/dumps/0050rule_1.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0050rule_1.nft
diff --git a/tests/shell/testcases/transactions/dumps/0051map_0.nodump b/tests/shell/testcases/transactions/dumps/0051map_0.nodump
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/0051map_0.nodump
diff --git a/tests/shell/testcases/transactions/dumps/30s-stress.json-nft b/tests/shell/testcases/transactions/dumps/30s-stress.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/30s-stress.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/30s-stress.nft b/tests/shell/testcases/transactions/dumps/30s-stress.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/30s-stress.nft
diff --git a/tests/shell/testcases/transactions/dumps/anon_chain_loop.json-nft b/tests/shell/testcases/transactions/dumps/anon_chain_loop.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/anon_chain_loop.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/anon_chain_loop.nft b/tests/shell/testcases/transactions/dumps/anon_chain_loop.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/anon_chain_loop.nft
diff --git a/tests/shell/testcases/transactions/dumps/bad_expression.json-nft b/tests/shell/testcases/transactions/dumps/bad_expression.json-nft
new file mode 100644
index 00000000..546cc597
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/bad_expression.json-nft
@@ -0,0 +1,11 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/bad_expression.nft b/tests/shell/testcases/transactions/dumps/bad_expression.nft
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/bad_expression.nft
diff --git a/tests/shell/testcases/transactions/dumps/concat_range_abort.json-nft b/tests/shell/testcases/transactions/dumps/concat_range_abort.json-nft
new file mode 100644
index 00000000..8db71894
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/concat_range_abort.json-nft
@@ -0,0 +1,47 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "foo",
+ "handle": 0
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "bar",
+ "handle": 0
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "foo",
+ "handle": 0,
+ "expr": [
+ {
+ "accept": null
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/concat_range_abort.nft b/tests/shell/testcases/transactions/dumps/concat_range_abort.nft
new file mode 100644
index 00000000..06adca7a
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/concat_range_abort.nft
@@ -0,0 +1,8 @@
+table ip x {
+ chain foo {
+ accept
+ }
+
+ chain bar {
+ }
+}
diff --git a/tests/shell/testcases/transactions/dumps/doubled-set.json-nft b/tests/shell/testcases/transactions/dumps/doubled-set.json-nft
new file mode 100644
index 00000000..2dced124
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/doubled-set.json-nft
@@ -0,0 +1,41 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0
+ }
+ },
+ {
+ "set": {
+ "family": "ip",
+ "name": "s",
+ "table": "t",
+ "type": [
+ "ipv4_addr",
+ "ifname"
+ ],
+ "handle": 0,
+ "flags": [
+ "interval"
+ ],
+ "elem": [
+ {
+ "concat": [
+ "1.2.3.4",
+ "foo"
+ ]
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/doubled-set.nft b/tests/shell/testcases/transactions/dumps/doubled-set.nft
new file mode 100644
index 00000000..48a322eb
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/doubled-set.nft
@@ -0,0 +1,7 @@
+table ip t {
+ set s {
+ type ipv4_addr . ifname
+ flags interval
+ elements = { 1.2.3.4 . "foo" }
+ }
+}
diff --git a/tests/shell/testcases/transactions/dumps/table_onoff.json-nft b/tests/shell/testcases/transactions/dumps/table_onoff.json-nft
new file mode 100644
index 00000000..a7583e8c
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/table_onoff.json-nft
@@ -0,0 +1,59 @@
+{
+ "nftables": [
+ {
+ "metainfo": {
+ "version": "VERSION",
+ "release_name": "RELEASE_NAME",
+ "json_schema_version": 1
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "t",
+ "handle": 0,
+ "flags": "dormant"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "t",
+ "name": "c",
+ "handle": 0,
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "t",
+ "chain": "c",
+ "handle": 0,
+ "expr": [
+ {
+ "match": {
+ "op": "==",
+ "left": {
+ "payload": {
+ "protocol": "ip",
+ "field": "daddr"
+ }
+ },
+ "right": "127.0.0.42"
+ }
+ },
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/shell/testcases/transactions/dumps/table_onoff.nft b/tests/shell/testcases/transactions/dumps/table_onoff.nft
new file mode 100644
index 00000000..038be1c0
--- /dev/null
+++ b/tests/shell/testcases/transactions/dumps/table_onoff.nft
@@ -0,0 +1,8 @@
+table ip t {
+ flags dormant
+
+ chain c {
+ type filter hook input priority filter; policy accept;
+ ip daddr 127.0.0.42 counter packets 0 bytes 0
+ }
+}
diff --git a/tests/shell/testcases/transactions/table_onoff b/tests/shell/testcases/transactions/table_onoff
new file mode 100755
index 00000000..831d4614
--- /dev/null
+++ b/tests/shell/testcases/transactions/table_onoff
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+# attempt to re-awaken a table that is flagged dormant within
+# same transaction
+$NFT -f - <<EOF
+add table ip t
+add table ip t { flags dormant; }
+add chain ip t c { type filter hook input priority 0; }
+add table ip t
+delete table ip t
+EOF
+
+if [ $? -eq 0 ]; then
+ exit 1
+fi
+
+set -e
+
+ip link set lo up
+
+# add a dormant table, then wake it up in same
+# transaction.
+$NFT -f - <<EOF
+add table ip t { flags dormant; }
+add chain ip t c { type filter hook input priority 0; }
+add rule ip t c ip daddr 127.0.0.42 counter
+add table ip t
+EOF
+
+# check table is indeed active.
+ping -c 1 127.0.0.42
+$NFT list chain ip t c | grep "counter packets 1"
+$NFT delete table ip t
+
+# allow to flag table dormant.
+$NFT -f - <<EOF
+add table ip t
+add chain ip t c { type filter hook input priority 0; }
+add rule ip t c ip daddr 127.0.0.42 counter
+add table ip t { flags dormant; }
+EOF
+
+ping -c 1 127.0.0.42
+# expect run-tests.sh to complain if counter isn't 0.
diff --git a/tools/check-tree.sh b/tools/check-tree.sh
new file mode 100755
index 00000000..e358c957
--- /dev/null
+++ b/tools/check-tree.sh
@@ -0,0 +1,134 @@
+#!/bin/bash -e
+
+# Preform various consistency checks of the source tree.
+
+unset LANGUAGE
+export LANG=C
+export LC_ALL=C
+
+die() {
+ printf '%s\n' "$*"
+ exit 1
+}
+
+array_contains() {
+ local needle="$1"
+ local a
+ shift
+ for a; do
+ [ "$a" = "$needle" ] && return 0
+ done
+ return 1
+}
+
+cd "$(dirname "$0")/.."
+
+EXIT_CODE=0
+
+msg_err() {
+ printf "ERR: %s\n" "$*"
+ EXIT_CODE=1
+}
+
+msg_warn() {
+ printf "WARN: %s\n" "$*"
+}
+
+##############################################################################
+
+check_shell_dumps() {
+ local TEST="$1"
+ local base="$(basename "$TEST")"
+ local dir="$(dirname "$TEST")"
+ local has_nft=0
+ local has_jnft=0
+ local has_nodump=0
+ local nft_name
+ local nodump_name
+
+ if [ ! -d "$dir/dumps/" ] ; then
+ msg_err "\"$TEST\" has no \"$dir/dumps/\" directory"
+ return 0
+ fi
+
+ nft_name="$dir/dumps/$base.nft"
+ jnft_name="$dir/dumps/$base.json-nft"
+ nodump_name="$dir/dumps/$base.nodump"
+
+ [ -f "$nft_name" ] && has_nft=1
+ [ -f "$jnft_name" ] && has_jnft=1
+ [ -f "$nodump_name" ] && has_nodump=1
+
+ if [ "$has_nft" != 1 -a "$has_nodump" != 1 ] ; then
+ msg_err "\"$TEST\" has no \"$dir/dumps/$base.{nft,nodump}\" file"
+ elif [ "$has_nft" == 1 -a "$has_nodump" == 1 ] ; then
+ msg_err "\"$TEST\" has both \"$dir/dumps/$base.{nft,nodump}\" files"
+ elif [ "$has_nodump" == 1 -a -s "$nodump_name" ] ; then
+ msg_err "\"$TEST\" has a non-empty \"$dir/dumps/$base.nodump\" file"
+ fi
+ if [ "$has_jnft" = 1 -a "$has_nft" != 1 ] ; then
+ msg_err "\"$TEST\" has a JSON dump file \"$jnft_name\" but lacks a dump \"$nft_name\""
+ fi
+ if [ "$has_nft" = 1 -a "$has_jnft" != 1 ] ; then
+ # it's currently known that `nft -j --check` cannot parse all dumped rulesets.
+ # Accept having no JSON dump file.
+ #
+ # This should be fixed. Currently this is only a warning.
+ msg_warn "\"$TEST\" has a dump file \"$nft_name\" but lacks a JSON dump \"$jnft_name\""
+ fi
+
+ if [ "$has_jnft" = 1 ] && command -v jq &>/dev/null ; then
+ if ! jq empty < "$jnft_name" &>/dev/null ; then
+ msg_err "\"$TEST\" has a JSON dump file \"$jnft_name\" that does not validate with \`jq empty < \"$jnft_name\"\`"
+ fi
+ fi
+}
+
+SHELL_TESTS=( $(find "tests/shell/testcases/" -type f -executable | sort) )
+
+if [ "${#SHELL_TESTS[@]}" -eq 0 ] ; then
+ msg_err "No executable tests under \"tests/shell/testcases/\" found"
+fi
+for t in "${SHELL_TESTS[@]}" ; do
+ check_shell_dumps "$t"
+ if ! ( head -n 1 "$t" | grep -q '^#!/bin/bash\( -e\)\?$' ) ; then
+ # Currently all tests only use bash as shebang. That also
+ # works with `./tests/shell/run-tests.sh -x`.
+ #
+ # We could allow other shebangs, but for now there is no need.
+ # Unless you have a good reason, create a bash script.
+ msg_err "$t should use either \"#!/bin/bash\" or \"#!/bin/bash -e\" as shebang"
+ fi
+done
+
+##############################################################################
+
+SHELL_TESTS2=( $(./tests/shell/run-tests.sh --list-tests) )
+if [ "${SHELL_TESTS[*]}" != "${SHELL_TESTS2[*]}" ] ; then
+ msg_err "\`./tests/shell/run-tests.sh --list-tests\` does not list the expected tests"
+fi
+
+##############################################################################
+#
+F=( $(find tests/shell/testcases/ -type f | grep '^tests/shell/testcases/[^/]\+/dumps/[^/]\+\.\(json-nft\|nft\|nodump\)$' -v | sort) )
+IGNORED_FILES=( tests/shell/testcases/bogons/nft-f/* )
+for f in "${F[@]}" ; do
+ if ! array_contains "$f" "${SHELL_TESTS[@]}" "${IGNORED_FILES[@]}" ; then
+ msg_err "Unexpected file \"$f\""
+ fi
+done
+
+##############################################################################
+
+FILES=( $(find "tests/shell/testcases/" -type f | sed -n 's#\(tests/shell/testcases\(/.*\)\?/\)dumps/\(.*\)\.\(nft\|nodump\)$#\0#p' | sort) )
+
+for f in "${FILES[@]}" ; do
+ f2="$(echo "$f" | sed -n 's#\(tests/shell/testcases\(/.*\)\?/\)dumps/\(.*\)\.\(nft\|nodump\)$#\1\3#p')"
+ if ! array_contains "$f2" "${SHELL_TESTS[@]}" ; then
+ msg_err "\"$f\" has no test \"$f2\""
+ fi
+done
+
+##############################################################################
+
+exit "$EXIT_CODE"