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.am415
-rw-r--r--configure.ac56
-rw-r--r--doc/Makefile.am30
-rw-r--r--doc/libnftables-json.adoc33
-rw-r--r--doc/libnftables.adoc49
-rw-r--r--doc/nft.txt87
-rw-r--r--doc/payload-expression.txt194
-rw-r--r--doc/primary-expression.txt4
-rw-r--r--doc/stateful-objects.txt2
-rw-r--r--doc/statements.txt113
-rw-r--r--examples/Makefile.am6
-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.h18
-rw-r--r--include/cli.h3
-rw-r--r--include/cmd.h6
-rw-r--r--include/ct.h2
-rw-r--r--include/datatype.h12
-rw-r--r--include/dccpopt.h41
-rw-r--r--include/expression.h16
-rw-r--r--include/exthdr.h1
-rw-r--r--include/gmputil.h4
-rw-r--r--include/headers.h2
-rw-r--r--include/json.h6
-rw-r--r--include/linux/Makefile.am12
-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/mnl.h10
-rw-r--r--include/netlink.h20
-rw-r--r--include/nft.h18
-rw-r--r--include/nftables.h20
-rw-r--r--include/nftables/Makefile.am1
-rw-r--r--include/nftables/libnftables.h9
-rw-r--r--include/parser.h5
-rw-r--r--include/payload.h4
-rw-r--r--include/proto.h62
-rw-r--r--include/rule.h23
-rw-r--r--include/statement.h22
-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.am119
-rw-r--r--src/cache.c77
-rw-r--r--src/cli.c68
-rw-r--r--src/cmd.c239
-rw-r--r--src/ct.c14
-rw-r--r--src/datatype.c269
-rw-r--r--src/dccpopt.c277
-rw-r--r--src/erec.c14
-rw-r--r--src/evaluate.c1184
-rw-r--r--src/expression.c52
-rw-r--r--src/exthdr.c20
-rw-r--r--src/fib.c7
-rw-r--r--src/gmputil.c27
-rw-r--r--src/hash.c6
-rw-r--r--src/iface.c8
-rw-r--r--src/intervals.c17
-rw-r--r--src/ipopt.c3
-rw-r--r--src/json.c241
-rw-r--r--src/libnftables.c142
-rw-r--r--src/libnftables.map5
-rw-r--r--src/main.c82
-rw-r--r--src/mergesort.c38
-rw-r--r--src/meta.c139
-rw-r--r--src/mini-gmp.c4
-rw-r--r--src/misspell.c14
-rw-r--r--src/mnl.c443
-rw-r--r--src/monitor.c37
-rw-r--r--src/netlink.c270
-rw-r--r--src/netlink_delinearize.c447
-rw-r--r--src/netlink_linearize.c113
-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.c404
-rw-r--r--src/osf.c11
-rw-r--r--src/owner.c13
-rw-r--r--src/parser_bison.y637
-rw-r--r--src/parser_json.c518
-rw-r--r--src/payload.c267
-rw-r--r--src/print.c7
-rw-r--r--src/proto.c159
-rw-r--r--src/rt.c5
-rw-r--r--src/rule.c426
-rw-r--r--src/scanner.l135
-rw-r--r--src/sctp_chunk.c3
-rw-r--r--src/segtree.c106
-rw-r--r--src/socket.c6
-rw-r--r--src/statement.c50
-rw-r--r--src/tcpopt.c49
-rw-r--r--src/utils.c14
-rw-r--r--src/xfrm.c9
-rw-r--r--src/xt.c246
-rwxr-xr-xtests/build/run-tests.sh34
-rwxr-xr-xtests/monitor/run-tests.sh9
-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.t2
-rw-r--r--tests/monitor/testcases/simple.t4
-rw-r--r--tests/py/README31
-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.output14
-rw-r--r--tests/py/any/last.t.payload8
-rw-r--r--tests/py/any/limit.t20
-rw-r--r--tests/py/any/limit.t.json6
-rw-r--r--tests/py/any/limit.t.json.output24
-rw-r--r--tests/py/any/limit.t.payload30
-rw-r--r--tests/py/any/meta.t4
-rw-r--r--tests/py/any/meta.t.payload25
-rw-r--r--tests/py/any/meta.t.payload.bridge20
-rw-r--r--tests/py/any/rawpayload.t2
-rw-r--r--tests/py/any/rawpayload.t.json31
-rw-r--r--tests/py/any/rawpayload.t.payload8
-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.t2
-rw-r--r--tests/py/bridge/vlan.t.json34
-rw-r--r--tests/py/bridge/vlan.t.payload8
-rw-r--r--tests/py/bridge/vlan.t.payload.netdev10
-rw-r--r--tests/py/inet/dccp.t5
-rw-r--r--tests/py/inet/dccp.t.json44
-rw-r--r--tests/py/inet/dccp.t.payload14
-rw-r--r--tests/py/inet/ether.t6
-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/meta.t9
-rw-r--r--tests/py/inet/meta.t.json237
-rw-r--r--tests/py/inet/meta.t.payload76
-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/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.t2
-rw-r--r--tests/py/ip/dnat.t.json146
-rw-r--r--tests/py/ip/dnat.t.payload.ip22
-rw-r--r--tests/py/ip/ip.t18
-rw-r--r--tests/py/ip/ip.t.json136
-rw-r--r--tests/py/ip/ip.t.json.output31
-rw-r--r--tests/py/ip/ip.t.payload45
-rw-r--r--tests/py/ip/ip.t.payload.bridge54
-rw-r--r--tests/py/ip/ip.t.payload.inet54
-rw-r--r--tests/py/ip/ip.t.payload.netdev54
-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.json.output30
-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.t3
-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/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/frag.t.payload.netdev2026
-rw-r--r--tests/py/ip6/icmpv6.t12
-rw-r--r--tests/py/ip6/icmpv6.t.json260
-rw-r--r--tests/py/ip6/icmpv6.t.payload.ip680
-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.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.t4
-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
-rwxr-xr-xtests/py/nft-test.py81
-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/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.sh17
-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.sh316
-rwxr-xr-xtests/shell/run-tests.sh1012
-rwxr-xr-xtests/shell/testcases/bitwise/0040mark_binop_0 (renamed from tests/shell/testcases/chains/0040mark_shift_0)4
-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-nft1
-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-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_2.nft6
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_3.json-nft1
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_3.nft6
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_4.json-nft1
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_4.nft6
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_5.json-nft1
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_5.nft6
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_6.json-nft1
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_6.nft6
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_7.json-nft1
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_7.nft6
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_8.json-nft1
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_8.nft6
-rw-r--r--tests/shell/testcases/bitwise/dumps/0040mark_binop_9.json-nft1
-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-nft1
-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/double-free-on-binop-dtype_assert6
-rw-r--r--tests/shell/testcases/bogons/nft-f/huge_shift_assert5
-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/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/nat_prefix_map_with_set_element_assert7
-rw-r--r--tests/shell/testcases/bogons/nft-f/no_integer_basetype_crash1
-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/unknown_expr_type_range_assert5
-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_02
-rwxr-xr-xtests/shell/testcases/cache/0011_index_012
-rw-r--r--tests/shell/testcases/cache/dumps/0001_cache_handling_0.json-nft1
-rw-r--r--tests/shell/testcases/cache/dumps/0002_interval_0.json-nft1
-rw-r--r--tests/shell/testcases/cache/dumps/0003_cache_update_0.json-nft1
-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-nft1
-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-nft1
-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-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/cache/dumps/0008_delete_by_handle_0.json-nft1
-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-nft1
-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-nft1
-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_07
-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/0041chain_binding_05
-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-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0002jumps_1.json-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0002jumps_1.nft68
-rw-r--r--tests/shell/testcases/chains/dumps/0003jump_loop_1.json-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0003jump_loop_1.nft64
-rw-r--r--tests/shell/testcases/chains/dumps/0004busy_1.json-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0004busy_1.nft8
-rw-r--r--tests/shell/testcases/chains/dumps/0005busy_map_1.json-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0005busy_map_1.nft8
-rw-r--r--tests/shell/testcases/chains/dumps/0006masquerade_0.json-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0007masquerade_1.json-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0007masquerade_1.nft5
-rw-r--r--tests/shell/testcases/chains/dumps/0008masquerade_jump_1.json-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0008masquerade_jump_1.nft9
-rw-r--r--tests/shell/testcases/chains/dumps/0009masquerade_jump_1.json-nft1
-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-nft1
-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.nft13
-rw-r--r--tests/shell/testcases/chains/dumps/0013rename_0.json-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0014rename_0.json-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0014rename_0.nft7
-rw-r--r--tests/shell/testcases/chains/dumps/0015check_jump_loop_1.json-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0017masquerade_jump_1.json-nft1
-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-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0019masquerade_jump_1.nft9
-rw-r--r--tests/shell/testcases/chains/dumps/0020depth_1.json-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0020depth_1.nft84
-rw-r--r--tests/shell/testcases/chains/dumps/0021prio_0.json-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0022prio_dummy_1.json-nft1
-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-nft1
-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-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0025prio_arp_1.nft2
-rw-r--r--tests/shell/testcases/chains/dumps/0026prio_netdev_1.json-nft1
-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-nft1
-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-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0029prio_bridge_srcnat_1.nft2
-rw-r--r--tests/shell/testcases/chains/dumps/0030create_0.json-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0031priority_variable_0.json-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0032priority_variable_0.json-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0033priority_variable_1.json-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0033priority_variable_1.nft0
-rw-r--r--tests/shell/testcases/chains/dumps/0034priority_variable_1.json-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0034priority_variable_1.nft0
-rw-r--r--tests/shell/testcases/chains/dumps/0035policy_variable_0.json-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0036policy_variable_0.json-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0037policy_variable_1.nft0
-rw-r--r--tests/shell/testcases/chains/dumps/0038policy_variable_1.json-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0038policy_variable_1.nft0
-rw-r--r--tests/shell/testcases/chains/dumps/0039negative_priority_0.json-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0039negative_priority_0.nft5
-rw-r--r--tests/shell/testcases/chains/dumps/0042chain_variable_0.json-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0042chain_variable_0.nft8
-rw-r--r--tests/shell/testcases/chains/dumps/0043chain_ingress_0.json-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/0044chain_destroy_0.nft2
-rw-r--r--tests/shell/testcases/chains/dumps/netdev_chain_0.json-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/netdev_chain_0.nft2
-rw-r--r--tests/shell/testcases/chains/dumps/netdev_chain_autoremove.json-nft1
-rw-r--r--tests/shell/testcases/chains/dumps/netdev_chain_autoremove.nft0
-rwxr-xr-xtests/shell/testcases/chains/netdev_chain_029
-rwxr-xr-xtests/shell/testcases/chains/netdev_chain_autoremove9
-rwxr-xr-xtests/shell/testcases/comments/comments_044
-rw-r--r--tests/shell/testcases/comments/dumps/comments_0.json-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/flowtable/dumps/0002create_flowtable_0.json-nft1
-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-nft1
-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-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/flowtable/dumps/0005delete_in_use_1.nft10
-rw-r--r--tests/shell/testcases/flowtable/dumps/0006segfault_0.json-nft1
-rw-r--r--tests/shell/testcases/flowtable/dumps/0006segfault_0.nft2
-rw-r--r--tests/shell/testcases/flowtable/dumps/0007prio_0.json-nft1
-rw-r--r--tests/shell/testcases/flowtable/dumps/0007prio_0.nft2
-rw-r--r--tests/shell/testcases/flowtable/dumps/0008prio_1.json-nft1
-rw-r--r--tests/shell/testcases/flowtable/dumps/0008prio_1.nft2
-rw-r--r--tests/shell/testcases/flowtable/dumps/0009deleteafterflush_0.json-nft1
-rw-r--r--tests/shell/testcases/flowtable/dumps/0009deleteafterflush_0.nft4
-rw-r--r--tests/shell/testcases/flowtable/dumps/0010delete_handle_0.json-nft1
-rw-r--r--tests/shell/testcases/flowtable/dumps/0010delete_handle_0.nft2
-rw-r--r--tests/shell/testcases/flowtable/dumps/0011deleteafterflush_0.json-nft1
-rw-r--r--tests/shell/testcases/flowtable/dumps/0011deleteafterflush_0.nft4
-rw-r--r--tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.json-nft1
-rw-r--r--tests/shell/testcases/flowtable/dumps/0012flowtable_variable_0.nft4
-rw-r--r--tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.json-nft1
-rw-r--r--tests/shell/testcases/flowtable/dumps/0013addafterdelete_0.nft6
-rw-r--r--tests/shell/testcases/flowtable/dumps/0014addafterdelete_0.json-nft1
-rw-r--r--tests/shell/testcases/flowtable/dumps/0014addafterdelete_0.nft12
-rw-r--r--tests/shell/testcases/flowtable/dumps/0015destroy_0.json-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/include/dumps/0002relative_0.json-nft1
-rw-r--r--tests/shell/testcases/include/dumps/0003includepath_0.json-nft1
-rw-r--r--tests/shell/testcases/include/dumps/0004endlessloop_1.json-nft1
-rw-r--r--tests/shell/testcases/include/dumps/0004endlessloop_1.nft0
-rw-r--r--tests/shell/testcases/include/dumps/0005glob_empty_0.json-nft1
-rw-r--r--tests/shell/testcases/include/dumps/0005glob_empty_0.nft0
-rw-r--r--tests/shell/testcases/include/dumps/0006glob_single_0.json-nft1
-rw-r--r--tests/shell/testcases/include/dumps/0007glob_double_0.json-nft1
-rw-r--r--tests/shell/testcases/include/dumps/0008glob_nofile_wildcard_0.json-nft1
-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-nft1
-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-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/include/dumps/0012glob_dependency_1.json-nft1
-rw-r--r--tests/shell/testcases/include/dumps/0012glob_dependency_1.nft0
-rw-r--r--tests/shell/testcases/include/dumps/0013glob_dotfile_0.json-nft1
-rw-r--r--tests/shell/testcases/include/dumps/0013input_descriptors_included_files_0.json-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/include/dumps/0014glob_directory_0.nft0
-rw-r--r--tests/shell/testcases/include/dumps/0015doubleincludepath_0.json-nft1
-rw-r--r--tests/shell/testcases/include/dumps/0016maxdepth_0.json-nft1
-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-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/include/dumps/0018include_error_0.nft0
-rw-r--r--tests/shell/testcases/include/dumps/0019include_error_0.json-nft1
-rw-r--r--tests/shell/testcases/include/dumps/0019include_error_0.nft0
-rw-r--r--tests/shell/testcases/include/dumps/0020include_chain_0.json-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/json/dumps/0001set_statements_0.nft12
-rw-r--r--tests/shell/testcases/json/dumps/0002table_map_0.json-nft1
-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-nft1
-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-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/json/dumps/0005secmark_objref_0.nft18
-rw-r--r--tests/shell/testcases/json/dumps/0006obj_comment_0.json-nft1
-rw-r--r--tests/shell/testcases/json/dumps/0006obj_comment_0.nft6
-rw-r--r--tests/shell/testcases/json/dumps/netdev.json-nft1
-rw-r--r--tests/shell/testcases/json/dumps/netdev.nft2
-rwxr-xr-xtests/shell/testcases/json/netdev21
-rwxr-xr-xtests/shell/testcases/listing/0013objects_050
-rwxr-xr-xtests/shell/testcases/listing/0020flowtable_014
-rwxr-xr-xtests/shell/testcases/listing/0021ruleset_json_terse_013
-rw-r--r--tests/shell/testcases/listing/dumps/0001ruleset_0.json-nft1
-rw-r--r--tests/shell/testcases/listing/dumps/0002ruleset_0.json-nft1
-rw-r--r--tests/shell/testcases/listing/dumps/0002ruleset_0.nft0
-rw-r--r--tests/shell/testcases/listing/dumps/0003table_0.json-nft1
-rw-r--r--tests/shell/testcases/listing/dumps/0003table_0.nft2
-rw-r--r--tests/shell/testcases/listing/dumps/0004table_0.json-nft1
-rw-r--r--tests/shell/testcases/listing/dumps/0004table_0.nft4
-rw-r--r--tests/shell/testcases/listing/dumps/0005ruleset_ip_0.json-nft1
-rw-r--r--tests/shell/testcases/listing/dumps/0005ruleset_ip_0.nft10
-rw-r--r--tests/shell/testcases/listing/dumps/0006ruleset_ip6_0.json-nft1
-rw-r--r--tests/shell/testcases/listing/dumps/0006ruleset_ip6_0.nft10
-rw-r--r--tests/shell/testcases/listing/dumps/0007ruleset_inet_0.json-nft1
-rw-r--r--tests/shell/testcases/listing/dumps/0007ruleset_inet_0.nft10
-rw-r--r--tests/shell/testcases/listing/dumps/0008ruleset_arp_0.json-nft1
-rw-r--r--tests/shell/testcases/listing/dumps/0008ruleset_arp_0.nft10
-rw-r--r--tests/shell/testcases/listing/dumps/0009ruleset_bridge_0.json-nft1
-rw-r--r--tests/shell/testcases/listing/dumps/0009ruleset_bridge_0.nft10
-rw-r--r--tests/shell/testcases/listing/dumps/0010sets_0.json-nft1
-rw-r--r--tests/shell/testcases/listing/dumps/0010sets_0.nft39
-rw-r--r--tests/shell/testcases/listing/dumps/0011sets_0.json-nft1
-rw-r--r--tests/shell/testcases/listing/dumps/0011sets_0.nft25
-rw-r--r--tests/shell/testcases/listing/dumps/0012sets_0.json-nft1
-rw-r--r--tests/shell/testcases/listing/dumps/0012sets_0.nft39
-rw-r--r--tests/shell/testcases/listing/dumps/0013objects_0.nft27
-rw-r--r--tests/shell/testcases/listing/dumps/0014objects_0.json-nft1
-rw-r--r--tests/shell/testcases/listing/dumps/0014objects_0.nft12
-rw-r--r--tests/shell/testcases/listing/dumps/0015dynamic_0.json-nft1
-rw-r--r--tests/shell/testcases/listing/dumps/0015dynamic_0.nft7
-rw-r--r--tests/shell/testcases/listing/dumps/0016anonymous_0.json-nft1
-rw-r--r--tests/shell/testcases/listing/dumps/0016anonymous_0.nft6
-rw-r--r--tests/shell/testcases/listing/dumps/0017objects_0.json-nft1
-rw-r--r--tests/shell/testcases/listing/dumps/0017objects_0.nft5
-rw-r--r--tests/shell/testcases/listing/dumps/0018data_0.json-nft1
-rw-r--r--tests/shell/testcases/listing/dumps/0018data_0.nft5
-rw-r--r--tests/shell/testcases/listing/dumps/0019set_0.json-nft1
-rw-r--r--tests/shell/testcases/listing/dumps/0019set_0.nft5
-rw-r--r--tests/shell/testcases/listing/dumps/0020flowtable_0.json-nft1
-rw-r--r--tests/shell/testcases/listing/dumps/0020flowtable_0.nft20
-rw-r--r--tests/shell/testcases/listing/dumps/0021ruleset_json_terse_0.json-nft1
-rw-r--r--tests/shell/testcases/listing/dumps/0021ruleset_json_terse_0.nft9
-rw-r--r--tests/shell/testcases/listing/dumps/0022terse_0.json-nft1
-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_time54
-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/anon_objmap_concat2
-rw-r--r--tests/shell/testcases/maps/dumps/0003map_add_many_elements_0.json-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/maps/dumps/0006interval_map_overlap_0.json-nft1
-rw-r--r--tests/shell/testcases/maps/dumps/0007named_ifname_dtype_0.json-nft1
-rw-r--r--tests/shell/testcases/maps/dumps/0008interval_map_delete_0.json-nft1
-rw-r--r--tests/shell/testcases/maps/dumps/0008interval_map_delete_0.nft15
-rw-r--r--tests/shell/testcases/maps/dumps/0009vmap_0.json-nft1
-rw-r--r--tests/shell/testcases/maps/dumps/0009vmap_0.nft2
-rw-r--r--tests/shell/testcases/maps/dumps/0012map_concat_0.json-nft1
-rw-r--r--tests/shell/testcases/maps/dumps/0012map_concat_0.nft14
-rw-r--r--tests/shell/testcases/maps/dumps/0013map_0.json-nft1
-rw-r--r--tests/shell/testcases/maps/dumps/0014destroy_0.json-nft1
-rw-r--r--tests/shell/testcases/maps/dumps/0014destroy_0.nft2
-rw-r--r--tests/shell/testcases/maps/dumps/0016map_leak_0.json-nft1
-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-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/maps/dumps/0018map_leak_timeout_0.nft0
-rw-r--r--tests/shell/testcases/maps/dumps/anon_objmap_concat.json-nft1
-rw-r--r--tests/shell/testcases/maps/dumps/anonymous_snat_map_0.json-nft1
-rw-r--r--tests/shell/testcases/maps/dumps/different_map_types_1.json-nft1
-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-nft1
-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_with_flags_0.json-nft1
-rw-r--r--tests/shell/testcases/maps/dumps/named_snat_map_0.json-nft1
-rw-r--r--tests/shell/testcases/maps/dumps/typeof_integer_0.nft4
-rw-r--r--tests/shell/testcases/maps/dumps/typeof_maps_add_delete.json-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/maps/dumps/typeof_raw_0.nft4
-rw-r--r--tests/shell/testcases/maps/dumps/vmap_mark_bitwise_0.nft26
-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/typeof_integer_06
-rwxr-xr-xtests/shell/testcases/maps/typeof_maps_066
-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_04
-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_unary17
-rw-r--r--tests/shell/testcases/netns/dumps/0001nft-f_0.json-nft1
-rw-r--r--tests/shell/testcases/netns/dumps/0001nft-f_0.nft0
-rw-r--r--tests/shell/testcases/netns/dumps/0002loosecommands_0.json-nft1
-rw-r--r--tests/shell/testcases/netns/dumps/0002loosecommands_0.nft0
-rw-r--r--tests/shell/testcases/netns/dumps/0003many_0.json-nft1
-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/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-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/nft-f/dumps/0003rollback_jump_0.json-nft1
-rw-r--r--tests/shell/testcases/nft-f/dumps/0004rollback_set_0.json-nft1
-rw-r--r--tests/shell/testcases/nft-f/dumps/0005rollback_map_0.json-nft1
-rw-r--r--tests/shell/testcases/nft-f/dumps/0006action_object_0.json-nft1
-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-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/nft-f/dumps/0009variable_0.json-nft1
-rw-r--r--tests/shell/testcases/nft-f/dumps/0010variable_0.json-nft1
-rw-r--r--tests/shell/testcases/nft-f/dumps/0011manydefines_0.nodump0
-rw-r--r--tests/shell/testcases/nft-f/dumps/0013defines_1.json-nft1
-rw-r--r--tests/shell/testcases/nft-f/dumps/0013defines_1.nft0
-rw-r--r--tests/shell/testcases/nft-f/dumps/0014defines_1.json-nft1
-rw-r--r--tests/shell/testcases/nft-f/dumps/0014defines_1.nft0
-rw-r--r--tests/shell/testcases/nft-f/dumps/0015defines_1.json-nft1
-rw-r--r--tests/shell/testcases/nft-f/dumps/0015defines_1.nft0
-rw-r--r--tests/shell/testcases/nft-f/dumps/0016redefines_1.json-nft1
-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.nft2
-rw-r--r--tests/shell/testcases/nft-f/dumps/0018ct_expectation_obj_0.json-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/nft-f/dumps/0019jump_variable_1.json-nft1
-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-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/nft-f/dumps/0022variables_0.json-nft1
-rw-r--r--tests/shell/testcases/nft-f/dumps/0023check_1.json-nft1
-rw-r--r--tests/shell/testcases/nft-f/dumps/0023check_1.nft5
-rw-r--r--tests/shell/testcases/nft-f/dumps/0025empty_dynset_0.json-nft1
-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-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/nft-f/dumps/0028variable_cmdline_0.json-nft1
-rw-r--r--tests/shell/testcases/nft-f/dumps/0029split_file_0.json-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/nft-f/dumps/0031vmap_string_0.json-nft1
-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-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/nft-i/dumps/0001define_0.nft0
-rw-r--r--tests/shell/testcases/optimizations/dumps/dependency_kill.json-nft1
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_nat.json-nft1
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_nat.nft11
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_nat_concat.json-nft1
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_nat_concat.nft8
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_nat_inet.nft11
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_reject.json-nft1
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_stmts.json-nft1
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_stmts_concat.json-nft1
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft14
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.json-nft1
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.nft4
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_stmts_vmap.json-nft1
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_stmts_vmap.nft4
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_vmap_raw.json-nft1
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_vmaps.json-nft1
-rw-r--r--tests/shell/testcases/optimizations/dumps/not_mergeable.json-nft1
-rw-r--r--tests/shell/testcases/optimizations/dumps/not_mergeable.nft19
-rw-r--r--tests/shell/testcases/optimizations/dumps/ruleset.json-nft1
-rw-r--r--tests/shell/testcases/optimizations/dumps/ruleset.nft0
-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-nft1
-rw-r--r--tests/shell/testcases/optimizations/dumps/single_anon_set_expr.nft5
-rw-r--r--tests/shell/testcases/optimizations/dumps/skip_merge.json-nft1
-rw-r--r--tests/shell/testcases/optimizations/dumps/skip_non_eq.json-nft1
-rw-r--r--tests/shell/testcases/optimizations/dumps/skip_unsupported.json-nft1
-rw-r--r--tests/shell/testcases/optimizations/dumps/skip_unsupported.nft11
-rw-r--r--tests/shell/testcases/optimizations/dumps/variables.json-nft1
-rw-r--r--tests/shell/testcases/optimizations/dumps/variables.nft0
-rwxr-xr-xtests/shell/testcases/optimizations/merge_nat17
-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_stmts_concat23
-rwxr-xr-xtests/shell/testcases/optimizations/merge_stmts_concat_vmap4
-rwxr-xr-xtests/shell/testcases/optimizations/merge_stmts_vmap6
-rwxr-xr-xtests/shell/testcases/optimizations/merge_vmap_raw2
-rwxr-xr-xtests/shell/testcases/optimizations/not_mergeable22
-rwxr-xr-xtests/shell/testcases/optimizations/ruleset2
-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_unsupported11
-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-nft1
-rw-r--r--tests/shell/testcases/optionals/dumps/comments_chain_0.json-nft1
-rw-r--r--tests/shell/testcases/optionals/dumps/comments_handles_0.json-nft1
-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-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/optionals/dumps/delete_object_handles_0.json-nft1
-rw-r--r--tests/shell/testcases/optionals/dumps/delete_object_handles_0.nft18
-rw-r--r--tests/shell/testcases/optionals/dumps/handles_0.json-nft1
-rw-r--r--tests/shell/testcases/optionals/dumps/handles_1.json-nft1
-rw-r--r--tests/shell/testcases/optionals/dumps/handles_1.nft5
-rw-r--r--tests/shell/testcases/optionals/dumps/log_prefix_0.json-nft1
-rw-r--r--tests/shell/testcases/optionals/dumps/update_object_handles_0.json-nft1
-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-uaf4
-rw-r--r--tests/shell/testcases/owner/dumps/0001-flowtable-uaf.json-nft1
-rw-r--r--tests/shell/testcases/owner/dumps/0001-flowtable-uaf.nft0
-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/tcp_options55
-rwxr-xr-xtests/shell/testcases/packetpath/vlan_8021ad_tag50
-rw-r--r--tests/shell/testcases/parsing/dumps/describe.json-nft1
-rw-r--r--tests/shell/testcases/parsing/dumps/describe.nft0
-rw-r--r--tests/shell/testcases/parsing/dumps/large_rule_pipe.json-nft1
-rw-r--r--tests/shell/testcases/parsing/dumps/large_rule_pipe.nft561
-rw-r--r--tests/shell/testcases/parsing/dumps/log.json-nft1
-rw-r--r--tests/shell/testcases/parsing/dumps/log.nft0
-rw-r--r--tests/shell/testcases/parsing/dumps/octal.json-nft1
-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/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-nft1
-rw-r--r--tests/shell/testcases/rule_management/dumps/0001addinsertposition_0.nft7
-rw-r--r--tests/shell/testcases/rule_management/dumps/0002addinsertlocation_1.json-nft1
-rw-r--r--tests/shell/testcases/rule_management/dumps/0002addinsertlocation_1.nft6
-rw-r--r--tests/shell/testcases/rule_management/dumps/0003insert_0.json-nft1
-rw-r--r--tests/shell/testcases/rule_management/dumps/0003insert_0.nft1
-rw-r--r--tests/shell/testcases/rule_management/dumps/0004replace_0.json-nft1
-rw-r--r--tests/shell/testcases/rule_management/dumps/0005replace_1.json-nft1
-rw-r--r--tests/shell/testcases/rule_management/dumps/0005replace_1.nft4
-rw-r--r--tests/shell/testcases/rule_management/dumps/0006replace_1.json-nft1
-rw-r--r--tests/shell/testcases/rule_management/dumps/0006replace_1.nft4
-rw-r--r--tests/shell/testcases/rule_management/dumps/0007delete_0.json-nft1
-rw-r--r--tests/shell/testcases/rule_management/dumps/0008delete_1.json-nft1
-rw-r--r--tests/shell/testcases/rule_management/dumps/0008delete_1.nft4
-rw-r--r--tests/shell/testcases/rule_management/dumps/0009delete_1.json-nft1
-rw-r--r--tests/shell/testcases/rule_management/dumps/0009delete_1.nft4
-rw-r--r--tests/shell/testcases/rule_management/dumps/0010replace_0.json-nft1
-rw-r--r--tests/shell/testcases/rule_management/dumps/0010replace_0.nft0
-rw-r--r--tests/shell/testcases/rule_management/dumps/0011reset_0.json-nft1
-rw-r--r--tests/shell/testcases/rule_management/dumps/0011reset_0.nft31
-rw-r--r--tests/shell/testcases/rule_management/dumps/0012destroy_0.json-nft1
-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/0024named_objects_015
-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/0034get_element_02
-rwxr-xr-xtests/shell/testcases/sets/0036add_set_element_expiration_018
-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_02
-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_02
-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_018
-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_079
-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-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0002named_interval_automerging_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0003named_interval_missing_flag_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0004named_interval_shadow_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0005named_interval_shadow_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0006create_set_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0007create_element_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0008comments_interval_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0009comments_timeout_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0010comments_0.json-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0013add_delete_many_elements_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0014malformed_set_is_not_defined_0.json-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0016element_leak_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0017add_after_flush_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0018set_check_size_1.json-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0020comments_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0021nesting_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0022type_selective_flush_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0022type_selective_flush_0.nft2
-rw-r--r--tests/shell/testcases/sets/dumps/0023incomplete_add_set_command_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0024named_objects_0.nft18
-rw-r--r--tests/shell/testcases/sets/dumps/0024synproxy_0.nft23
-rw-r--r--tests/shell/testcases/sets/dumps/0025anonymous_set_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0026named_limit_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0027ipv6_maps_ipv4_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0028autoselect_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0028autoselect_0.nft26
-rw-r--r--tests/shell/testcases/sets/dumps/0028delete_handle_0.json-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0033add_set_simple_flat_0.json-nft1
-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-nft1
-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-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0038meter_list_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0038meter_list_0.nft11
-rw-r--r--tests/shell/testcases/sets/dumps/0039delete_interval_0.json-nft1
-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-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0041interval_0.nft7
-rw-r--r--tests/shell/testcases/sets/dumps/0042update_set_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0042update_set_0.nft15
-rw-r--r--tests/shell/testcases/sets/dumps/0043concatenated_ranges_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0043concatenated_ranges_0.nft11
-rw-r--r--tests/shell/testcases/sets/dumps/0043concatenated_ranges_1.json-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0044interval_overlap_1.nft106
-rw-r--r--tests/shell/testcases/sets/dumps/0045concat_ipv4_service.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0046netmap_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0047nat_0.nft17
-rw-r--r--tests/shell/testcases/sets/dumps/0048set_counters_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0049set_define_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0049set_define_0.nft7
-rw-r--r--tests/shell/testcases/sets/dumps/0050set_define_1.json-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0052overlap_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0053echo_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0054comments_set_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0055tcpflags_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0056dynamic_limit_0.json-nft1
-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-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0059set_update_multistmt_0.json-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0060set_multistmt_0.nft6
-rw-r--r--tests/shell/testcases/sets/dumps/0060set_multistmt_1.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0060set_multistmt_1.nft15
-rw-r--r--tests/shell/testcases/sets/dumps/0061anonymous_automerge_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0062set_connlimit_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0062set_connlimit_0.nft14
-rw-r--r--tests/shell/testcases/sets/dumps/0063set_catchall_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0064map_catchall_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0065_icmp_postprocessing.json-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0071unclosed_prefix_interval_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0071unclosed_prefix_interval_0.nft19
-rw-r--r--tests/shell/testcases/sets/dumps/0072destroy_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0072destroy_0.nft2
-rw-r--r--tests/shell/testcases/sets/dumps/0073flat_interval_set.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/0073flat_interval_set.nft11
-rw-r--r--tests/shell/testcases/sets/dumps/0074nested_interval_set.json-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/collapse_elem_0.nft12
-rw-r--r--tests/shell/testcases/sets/dumps/concat_interval_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/concat_interval_0.nft14
-rw-r--r--tests/shell/testcases/sets/dumps/dynset_missing.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/elem_opts_compat_0.nodump0
-rw-r--r--tests/shell/testcases/sets/dumps/errors_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/errors_0.nft0
-rw-r--r--tests/shell/testcases/sets/dumps/exact_overlap_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/exact_overlap_0.nft13
-rw-r--r--tests/shell/testcases/sets/dumps/inner_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/inner_0.nft18
-rw-r--r--tests/shell/testcases/sets/dumps/reset_command_0.nodump0
-rw-r--r--tests/shell/testcases/sets/dumps/set_eval_0.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/type_set_symbol.json-nft1
-rw-r--r--tests/shell/testcases/sets/dumps/type_set_symbol.nft16
-rw-r--r--tests/shell/testcases/sets/dumps/typeof_raw_0.nft4
-rw-r--r--tests/shell/testcases/sets/dumps/typeof_sets_1.nft15
-rw-r--r--tests/shell/testcases/sets/dumps/typeof_sets_concat.nft12
-rwxr-xr-xtests/shell/testcases/sets/elem_opts_compat_031
-rwxr-xr-xtests/shell/testcases/sets/inner_027
-rwxr-xr-xtests/shell/testcases/sets/reset_command_087
-rwxr-xr-xtests/shell/testcases/sets/sets_with_ifnames2
-rwxr-xr-xtests/shell/testcases/sets/type_set_symbol6
-rwxr-xr-xtests/shell/testcases/sets/typeof_raw_04
-rwxr-xr-xtests/shell/testcases/sets/typeof_sets_0182
-rwxr-xr-xtests/shell/testcases/sets/typeof_sets_122
-rwxr-xr-xtests/shell/testcases/sets/typeof_sets_concat6
-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
-rw-r--r--tests/shell/testcases/transactions/dumps/0001table_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0002table_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0003table_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0003table_0.nft4
-rw-r--r--tests/shell/testcases/transactions/dumps/0010chain_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0011chain_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0012chain_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0013chain_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0014chain_1.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0014chain_1.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/0015chain_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0015chain_0.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/0020rule_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0020rule_0.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/0021rule_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0022rule_1.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0022rule_1.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/0023rule_1.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0023rule_1.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/0024rule_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0025rule_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0030set_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0031set_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0032set_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0033set_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0034set_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0035set_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0036set_1.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0036set_1.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/0037set_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0038set_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0039set_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0040set_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0041nat_restore_0.json-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0042_stateful_expr_0.nft5
-rw-r--r--tests/shell/testcases/transactions/dumps/0043set_1.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0043set_1.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/0044rule_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0044rule_0.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/0045anon-unbind_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0045anon-unbind_0.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/0046set_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0046set_0.nft2
-rw-r--r--tests/shell/testcases/transactions/dumps/0047set_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0047set_0.nft11
-rw-r--r--tests/shell/testcases/transactions/dumps/0048helpers_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0048helpers_0.nft2
-rw-r--r--tests/shell/testcases/transactions/dumps/0049huge_0.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/0049huge_0.nft749
-rw-r--r--tests/shell/testcases/transactions/dumps/0050rule_1.json-nft1
-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-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/30s-stress.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/anon_chain_loop.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/anon_chain_loop.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/bad_expression.json-nft1
-rw-r--r--tests/shell/testcases/transactions/dumps/bad_expression.nft0
-rw-r--r--tests/shell/testcases/transactions/dumps/table_onoff.json-nft1
-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
1023 files changed, 22011 insertions, 4958 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 72fb4e88..0ed831a1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,16 +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 \
- examples
-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-implicit-function-declaration \
+ -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)
+ $(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 eb1882dd..724a4ae7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,10 +1,10 @@
-AC_INIT([nftables], [1.0.5], [netfilter-devel@vger.kernel.org])
-AC_DEFINE([RELEASE_NAME], ["Lester Gooch #4"], [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])])
@@ -23,6 +23,9 @@ 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
@@ -44,7 +47,6 @@ fi
AM_PROG_AR
LT_INIT([disable-static])
-AM_PROG_CC_C_O
AC_EXEEXT
CHECK_GCC_FVISIBILITY
@@ -55,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.3])
+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)])],
@@ -108,44 +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 \
- examples/Makefile \
])
AC_OUTPUT
@@ -157,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/libnftables-json.adoc b/doc/libnftables-json.adoc
index 9cc17ff2..3e6e1db3 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.
@@ -1059,10 +1059,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 +1184,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 +1226,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]
____
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 f7a53ac9..b08e32fa 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*
@@ -322,9 +322,10 @@ TABLES
------
[verse]
{*add* | *create*} *table* ['family'] 'table' [ {*comment* 'comment' *;*'} *{ flags* 'flags' *; }*]
-{*delete* | *list* | *flush*} *table* ['family'] 'table'
+{*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*,
@@ -368,6 +369,7 @@ 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.
@@ -375,9 +377,10 @@ 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'
+{*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
@@ -390,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.
@@ -411,7 +415,7 @@ 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
@@ -430,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.
@@ -472,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*.
@@ -481,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
@@ -509,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*
-------------
@@ -559,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
@@ -571,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"]
@@ -585,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
@@ -612,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.
@@ -620,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"]
@@ -637,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
@@ -649,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
@@ -674,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"]
|=================
@@ -692,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
@@ -716,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
@@ -732,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
@@ -745,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 106ff74c..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"]
@@ -691,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 4d6b0878..e13970cf 100644
--- a/doc/primary-expression.txt
+++ b/doc/primary-expression.txt
@@ -442,7 +442,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:
@@ -472,7 +472,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 e3c79220..00d3c5f1 100644
--- a/doc/stateful-objects.txt
+++ b/doc/stateful-objects.txt
@@ -94,7 +94,7 @@ 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 6aaf806b..19672805 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.
@@ -278,7 +278,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]
@@ -296,7 +296,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. +
@@ -316,6 +316,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
@@ -332,8 +335,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"]
@@ -351,8 +359,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']
@@ -390,6 +398,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"]
|==================
@@ -400,7 +411,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)
|===============================
@@ -449,6 +460,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
@@ -608,7 +665,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
@@ -678,7 +735,24 @@ The fwd statement is used to redirect a raw packet to another interface. It is
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
~~~~~~~~~~~~~
@@ -694,6 +768,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'] *}*
@@ -778,3 +856,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/Makefile.am b/examples/Makefile.am
deleted file mode 100644
index 3b8b0b67..00000000
--- a/examples/Makefile.am
+++ /dev/null
@@ -1,6 +0,0 @@
-check_PROGRAMS = nft-buffer \
- nft-json-file
-
-AM_CPPFLAGS = -I$(top_srcdir)/include
-
-LDADD = $(top_builddir)/src/libnftables.la
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 ff56dfe4..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 \
- intervals.h \
- ipopt.h \
- json.h \
- mini-gmp.h \
- gmputil.h \
- iface.h \
- mnl.h \
- nftables.h \
- payload.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 575381ef..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),
@@ -55,6 +57,7 @@ struct nft_cache_filter {
const char *chain;
const char *set;
const char *ft;
+ uint64_t rule_handle;
} list;
struct {
@@ -63,12 +66,13 @@ struct nft_cache_filter {
};
struct nft_cache;
+struct nft_ctx;
enum cmd_ops;
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, enum cmd_ops cmd,
+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);
@@ -94,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);
@@ -118,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,
@@ -135,7 +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 73f38f66..09a78945 100644
--- a/include/datatype.h
+++ b/include/datatype.h
@@ -1,7 +1,6 @@
#ifndef NFTABLES_DATATYPE_H
#define NFTABLES_DATATYPE_H
-#include <stdbool.h>
#include <json.h>
/**
@@ -23,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)
@@ -167,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;
@@ -174,13 +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 *dtype_clone(const struct datatype *orig_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,
@@ -299,7 +301,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,
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/expression.h b/include/expression.h
index cf7319b6..aede223d 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>
@@ -41,6 +40,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 +76,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,7 +120,11 @@ 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;
@@ -181,7 +189,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
@@ -311,6 +319,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;
@@ -329,6 +338,7 @@ struct expr {
/* EXPR_META */
enum nft_meta_keys key;
enum proto_bases base;
+ const struct proto_desc *inner_desc;
} meta;
struct {
/* SOCKET */
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/json.h b/include/json.h
index b0d78eb8..39be8928 100644
--- a/include/json.h
+++ b/include/json.h
@@ -74,6 +74,7 @@ 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);
@@ -83,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);
@@ -92,6 +94,7 @@ 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);
@@ -176,6 +179,7 @@ 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)
@@ -185,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)
@@ -194,6 +199,7 @@ 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/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/mnl.h b/include/mnl.h
index 8e0a7e3f..cd5a2053 100644
--- a/include/mnl.h
+++ b/include/mnl.h
@@ -34,7 +34,9 @@ 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,
- const char *table, const char *chain);
+ 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);
@@ -66,9 +68,11 @@ int mnl_nft_setelem_add(struct netlink_ctx *ctx, struct cmd *cmd,
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,
diff --git a/include/netlink.h b/include/netlink.h
index 63d07edf..6766d7e8 100644
--- a/include/netlink.h
+++ b/include/netlink.h
@@ -40,6 +40,8 @@ struct netlink_parse_ctx {
struct expr *registers[MAX_REGS + 1];
unsigned int debug_mask;
struct netlink_ctx *nlctx;
+ bool inner;
+ uint8_t inner_reg;
};
@@ -49,9 +51,14 @@ struct netlink_parse_ctx {
#define RULE_PP_REMOVE_OP_AND (RULE_PP_IN_CONCATENATION | \
RULE_PP_IN_SET_ELEM)
-struct rule_pp_ctx {
+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;
};
@@ -77,6 +84,7 @@ 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);
@@ -157,10 +165,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);
@@ -176,6 +185,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,
@@ -246,4 +258,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 d49eb579..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,6 +132,7 @@ 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;
@@ -210,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);
@@ -222,8 +235,7 @@ 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);
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 85e08c9b..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>
@@ -49,6 +48,14 @@ 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),
NFT_CTX_OUTPUT_STATELESS = (1 << 2),
diff --git a/include/parser.h b/include/parser.h
index 2fb037cb..f79a22f3 100644
--- a/include/parser.h
+++ b/include/parser.h
@@ -22,6 +22,7 @@ struct parser_state {
struct scope *scopes[SCOPE_NEST_MAX];
unsigned int scope;
+ bool scope_err;
unsigned int flex_state_pop;
unsigned int startcond_type;
@@ -36,10 +37,12 @@ enum startcond_type {
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,
@@ -49,6 +52,8 @@ enum startcond_type {
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,
diff --git a/include/payload.h b/include/payload.h
index 37869928..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);
@@ -67,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 35e760c7..9c98a0b7 100644
--- a/include/proto.h
+++ b/include/proto.h
@@ -35,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,
};
/**
@@ -96,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)
@@ -131,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), }
@@ -185,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;
@@ -204,7 +215,7 @@ struct proto_ctx {
};
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);
@@ -216,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);
@@ -263,6 +276,7 @@ enum ip_hdr_fields {
IPHDR_SADDR,
IPHDR_DADDR,
};
+#define IPHDR_MAX IPHDR_DADDR
enum icmp_hdr_fields {
ICMPHDR_INVALID,
@@ -293,6 +307,8 @@ enum icmp6_hdr_fields {
ICMP6HDR_ID,
ICMP6HDR_SEQ,
ICMP6HDR_MAXDELAY,
+ ICMP6HDR_TADDR,
+ ICMP6HDR_DADDR,
};
enum ip6_hdr_fields {
@@ -376,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;
@@ -413,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/rule.h b/include/rule.h
index ad9f9127..6236d292 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,
@@ -518,7 +517,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;
@@ -562,6 +561,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,
@@ -579,6 +579,7 @@ enum cmd_ops {
CMD_EXPORT,
CMD_MONITOR,
CMD_DESCRIBE,
+ CMD_DESTROY,
};
/**
@@ -619,6 +620,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,
@@ -641,9 +643,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,
@@ -731,16 +735,11 @@ struct cmd {
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 bool nft_cmd_collapse(struct list_head *cmds);
-extern void nft_cmd_uncollapse(struct list_head *cmds);
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>
@@ -767,8 +766,10 @@ struct eval_ctx {
struct rule *rule;
struct set *set;
struct stmt *stmt;
+ uint32_t stmt_len;
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);
@@ -784,7 +785,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 2a2d3001..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;
@@ -255,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;
@@ -264,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);
@@ -304,6 +310,7 @@ extern struct stmt *xt_stmt_alloc(const struct location *loc);
* @STMT_SYNPROXY: synproxy statement
* @STMT_CHAIN: chain statement
* @STMT_OPTSTRIP: optstrip statement
+ * @STMT_LAST: last statement
*/
enum stmt_types {
STMT_INVALID,
@@ -334,6 +341,7 @@ enum stmt_types {
STMT_SYNPROXY,
STMT_CHAIN,
STMT_OPTSTRIP,
+ STMT_LAST,
};
/**
@@ -383,6 +391,7 @@ 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;
@@ -407,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/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 264d981e..00000000
--- a/src/Makefile.am
+++ /dev/null
@@ -1,119 +0,0 @@
-include $(top_srcdir)/Make_global.am
-
-sbin_PROGRAMS = nft
-
-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 \
- intervals.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 \
- gmputil.c \
- utils.c \
- erec.c \
- mnl.c \
- iface.c \
- mergesort.c \
- optimize.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} \
- -Wl,--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 f790f995..b7f46c00 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>
@@ -124,9 +126,9 @@ void nft_cache_filter_fini(struct nft_cache_filter *filter)
struct nft_filter_obj *obj, *next;
list_for_each_entry_safe(obj, next, &filter->obj[i].head, list)
- xfree(obj);
+ free(obj);
}
- xfree(filter);
+ free(filter);
}
static void cache_filter_add(struct nft_cache_filter *filter,
@@ -212,6 +214,10 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
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;
@@ -235,6 +241,8 @@ 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 &&
@@ -263,6 +271,36 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
return flags;
}
+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;
@@ -277,6 +315,7 @@ static int nft_handle_validate(const struct cmd *cmd, struct list_head *msgs)
}
break;
case CMD_OBJ_RULE:
+ case CMD_OBJ_RULES:
case CMD_OBJ_CHAIN:
case CMD_OBJ_CHAINS:
if (h->table.name &&
@@ -342,7 +381,9 @@ static int nft_handle_validate(const struct cmd *cmd, struct list_head *msgs)
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;
@@ -391,6 +432,7 @@ 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 |
@@ -403,7 +445,7 @@ 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);
@@ -591,31 +633,36 @@ static int list_rule_cb(struct nftnl_rule *nlr, void *data)
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 ||
+ 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;
}
-static int rule_cache_dump(struct netlink_ctx *ctx, const struct handle *h,
- const struct nft_cache_filter *filter)
+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_cache = mnl_nft_rule_dump(ctx, h->family,
+ table, chain, rule_handle, dump, reset);
if (rule_cache == NULL) {
if (errno == EINTR)
return -1;
@@ -947,7 +994,7 @@ static int rule_init_cache(struct netlink_ctx *ctx, struct table *table,
struct chain *chain;
int ret;
- ret = rule_cache_dump(ctx, &table->handle, filter);
+ 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);
@@ -980,8 +1027,10 @@ static int implicit_chain_cache(struct netlink_ctx *ctx, struct table *table,
int ret = 0;
list_for_each_entry(chain, &table->chain_bindings, cache.list) {
- filter.list.table = table->handle.table.name;
- filter.list.chain = chain->handle.chain.name;
+ filter.list = (typeof(filter.list)) {
+ .table = table->handle.table.name,
+ .chain = chain->handle.chain.name,
+ };
ret = rule_init_cache(ctx, table, &filter);
}
@@ -1034,7 +1083,7 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
continue;
ret = netlink_list_setelems(ctx, &set->handle,
- set);
+ set, false);
if (ret < 0)
goto cache_fails;
}
@@ -1047,7 +1096,7 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
continue;
ret = netlink_list_setelems(ctx, &set->handle,
- set);
+ set, false);
if (ret < 0)
goto cache_fails;
}
@@ -1230,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 11fc85ab..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
@@ -37,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
@@ -100,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);
@@ -125,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);
@@ -139,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);
@@ -176,16 +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(void)
+void cli_exit(int rc)
{
rl_callback_handler_remove();
rl_deprep_terminal();
write_history(histfile);
+
+ __cli_exit(rc);
}
#elif defined(HAVE_LIBEDIT)
@@ -205,51 +213,63 @@ 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;
cli_complete(line);
}
- cli_exit();
- return 0;
+ return cli_rc;
}
-void cli_exit(void)
+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 63692422..68c476c6 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)
@@ -233,6 +253,33 @@ 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)
{
@@ -255,6 +302,194 @@ 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;
+ }
+
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 e246d303..ebfd90a1 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",
@@ -216,7 +216,7 @@ static struct error_record *ct_label_type_parse(struct parse_ctx *ctx,
return NULL;
}
-static const struct datatype ct_label_type = {
+const struct datatype ct_label_type = {
.type = TYPE_CT_LABEL,
.name = "ct_label",
.desc = "conntrack label",
@@ -271,10 +271,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,
@@ -570,7 +570,7 @@ static void flow_offload_stmt_print(const struct stmt *stmt,
static void flow_offload_stmt_destroy(struct stmt *stmt)
{
- xfree(stmt->flow.table_name);
+ free_const(stmt->flow.table_name);
}
static const struct stmt_ops flow_offload_stmt_ops = {
diff --git a/src/datatype.c b/src/datatype.c
index 2e31c858..86d55a52 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 = {
@@ -528,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;
}
@@ -587,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;
- err = getaddrinfo(sym->identifier, NULL, &hints, &ai);
- if (err != 0)
- return error(&sym->location, "Could not resolve hostname: %s",
- gai_strerror(err));
+ 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));
- 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_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;
}
@@ -626,12 +715,11 @@ 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)) {
- p = getprotobynumber(mpz_get_uint8(expr->value));
- if (p != NULL) {
- nft_print(octx, "%s", p->p_name);
+ char name[NFT_PROTONAME_MAXSIZE];
+
+ if (nft_getprotobynumber(mpz_get_uint8(expr->value), name, sizeof(name))) {
+ nft_print(octx, "%s", name);
return;
}
}
@@ -640,15 +728,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);
}
}
@@ -656,7 +744,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;
@@ -669,11 +756,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,
@@ -697,12 +786,12 @@ 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)
@@ -737,7 +826,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);
}
@@ -814,8 +908,8 @@ void rt_symbol_table_free(const struct symbol_table *tbl)
const struct symbolic_constant *s;
for (s = tbl->symbols; s->identifier != NULL; s++)
- xfree(s->identifier);
- xfree(tbl);
+ free_const(s->identifier);
+ free_const(tbl);
}
void mark_table_init(struct nft_ctx *ctx)
@@ -1105,17 +1199,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;
@@ -1128,24 +1223,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));
}
-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;
}
@@ -1158,12 +1260,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)
@@ -1192,7 +1297,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;
@@ -1204,15 +1309,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;
@@ -1360,10 +1465,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;
@@ -1401,7 +1506,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)
@@ -1410,7 +1515,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 a4b93fb0..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)))
@@ -170,6 +168,8 @@ 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;
@@ -203,7 +203,7 @@ void erec_print(struct output_ctx *octx, const struct error_record *erec,
}
pbuf[end] = '\0';
fprintf(f, "%s", pbuf);
- xfree(pbuf);
+ free(pbuf);
}
fprintf(f, "\n");
}
diff --git a/src/evaluate.c b/src/evaluate.c
index 919c38c5..a62a2346 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>
@@ -39,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[] = {
@@ -75,7 +81,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);
@@ -148,8 +154,29 @@ 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);
+
+ 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;
+ }
basetype = expr_basetype(*expr)->type;
switch (basetype) {
@@ -165,7 +192,7 @@ static int byteorder_conversion(struct eval_ctx *ctx, struct expr **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);
@@ -249,7 +276,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;
@@ -361,6 +391,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 &&
@@ -369,24 +400,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;
}
@@ -418,6 +454,18 @@ static int expr_evaluate_primary(struct eval_ctx *ctx, struct expr **expr)
return 0;
}
+int stmt_dependency_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ uint32_t stmt_len = ctx->stmt_len;
+
+ if (stmt_evaluate(ctx, stmt) < 0)
+ return stmt_error(ctx, stmt, "dependency statement is invalid");
+
+ ctx->stmt_len = stmt_len;
+
+ return 0;
+}
+
static int
conflict_resolution_gen_dependency(struct eval_ctx *ctx, int protocol,
const struct expr *expr,
@@ -427,11 +475,13 @@ conflict_resolution_gen_dependency(struct eval_ctx *ctx, int protocol,
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);
@@ -441,10 +491,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;
}
@@ -465,10 +518,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;
@@ -486,7 +540,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);
@@ -503,6 +560,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,
@@ -510,7 +577,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;
@@ -519,10 +586,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)
@@ -531,9 +601,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: {
@@ -577,11 +653,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;
@@ -594,7 +672,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);
@@ -638,9 +717,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;
@@ -657,21 +738,29 @@ static int resolve_protocol_conflict(struct eval_ctx *ctx,
{
enum proto_bases base = payload->payload.base;
struct stmt *nstmt = NULL;
+ struct proto_ctx *pctx;
int link, err;
+ pctx = eval_proto_ctx(ctx);
+
if (payload->payload.base == PROTO_BASE_LL_HDR) {
if (proto_is_dummy(desc)) {
- err = meta_iiftype_gen_dependency(ctx, payload, &nstmt);
- if (err < 0)
- return err;
-
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+ 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 < ctx->pctx.stacked_ll_count; i++) {
- if (ctx->pctx.stacked_ll[i] == payload->payload.desc)
+ for (i = 0; i < pctx->stacked_ll_count; i++) {
+ if (pctx->stacked_ll[i] == payload->payload.desc)
return 0;
}
}
@@ -679,7 +768,7 @@ static int resolve_protocol_conflict(struct eval_ctx *ctx,
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);
@@ -690,8 +779,8 @@ static int resolve_protocol_conflict(struct eval_ctx *ctx,
if (base == PROTO_BASE_LL_HDR) {
unsigned int i;
- for (i = 0; i < ctx->pctx.stacked_ll_count; i++)
- payload->payload.offset += ctx->pctx.stacked_ll[i]->length;
+ for (i = 0; i < pctx->stacked_ll_count; i++)
+ payload->payload.offset += pctx->stacked_ll[i]->length;
}
rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
@@ -709,25 +798,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 ||
+ 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,
@@ -745,8 +857,8 @@ static int __expr_evaluate_payload(struct eval_ctx *ctx, struct expr *expr)
if (desc->base == PROTO_BASE_LL_HDR) {
unsigned int i;
- for (i = 0; i < ctx->pctx.stacked_ll_count; i++)
- payload->payload.offset += ctx->pctx.stacked_ll[i]->length;
+ 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)
@@ -773,13 +885,13 @@ check_icmp:
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,
+ pctx->protocol[base].desc->name,
payload->payload.desc->name);
}
@@ -791,6 +903,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)
@@ -802,14 +915,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.
*/
@@ -817,20 +997,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:
@@ -845,8 +1027,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:
@@ -856,7 +1040,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)
@@ -878,8 +1062,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;
@@ -892,7 +1076,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);
@@ -908,8 +1092,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:
@@ -934,13 +1120,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);
}
@@ -987,7 +1173,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;
}
@@ -998,7 +1183,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;
@@ -1063,12 +1248,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);
@@ -1087,7 +1270,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;
@@ -1154,14 +1337,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)
@@ -1170,9 +1363,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);
@@ -1182,13 +1375,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 (byteorder_conversion(ctx, &op->right, left->byteorder) < 0)
+ 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;
+
+ 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);
@@ -1205,14 +1417,20 @@ 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;
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;
@@ -1245,8 +1463,11 @@ 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:
@@ -1279,20 +1500,28 @@ 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;
+ 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) {
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 ||
i->ct.key == NFT_CT_DST))
@@ -1310,7 +1539,7 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
dsize = key->len;
bo = key->byteorder;
off--;
- } else if (dtype == NULL) {
+ } else if (dtype == NULL || off == 0) {
tmp = datatype_lookup(TYPE_INVALID);
} else {
tmp = concat_subtype_lookup(type, --off);
@@ -1319,19 +1548,25 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
}
__expr_set_context(&ctx->ectx, tmp, bo, dsize, 0);
+ ctx->ectx.key = i;
if (list_member_evaluate(ctx, &i) < 0)
return -1;
+
+ if (i->etype == EXPR_SET)
+ return expr_error(ctx->msgs, i,
+ "cannot use %s in concatenation",
+ expr_name(i));
+
flags &= i->flags;
if (!key && i->dtype->type == TYPE_INTEGER) {
struct datatype *clone;
- clone = dtype_clone(i->dtype);
+ clone = datatype_clone(i->dtype);
clone->size = i->len;
clone->byteorder = i->byteorder;
- clone->refcnt = 1;
- i->dtype = clone;
+ __datatype_set(i, clone);
}
if (dtype == NULL && i->dtype->size == 0)
@@ -1348,12 +1583,18 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
dsize_bytes = div_round_up(dsize, BITS_PER_BYTE);
(*expr)->field_len[(*expr)->field_count++] = dsize_bytes;
size += netlink_padded_len(dsize);
- if (key)
+ if (key && expressions) {
+ if (list_is_last(&key->list, expressions))
+ runaway = true;
+
key = list_next_entry(key, list);
+ }
+
+ ctx->inner_desc = NULL;
}
(*expr)->flags |= flags;
- datatype_set(*expr, concat_type_alloc(ntype));
+ __datatype_set(*expr, concat_type_alloc(ntype));
(*expr)->len = size;
if (off > 0)
@@ -1384,7 +1625,7 @@ static int expr_evaluate_list(struct eval_ctx *ctx, struct expr **expr)
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)
return expr_error(ctx->msgs, i,
"Basetype of type %s is not bitmask",
i->dtype->desc);
@@ -1421,7 +1662,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",
@@ -1520,6 +1762,8 @@ static int interval_set_eval(struct eval_ctx *ctx, struct set *set,
switch (ctx->cmd->op) {
case CMD_CREATE:
case CMD_ADD:
+ case CMD_REPLACE:
+ case CMD_INSERT:
if (set->automerge) {
ret = set_automerge(ctx->msgs, ctx->cmd, set, init,
ctx->nft->debug_mask);
@@ -1528,6 +1772,7 @@ static int interval_set_eval(struct eval_ctx *ctx, struct set *set,
}
break;
case CMD_DELETE:
+ case CMD_DESTROY:
ret = set_delete(ctx->msgs, ctx->cmd, set, init,
ctx->nft->debug_mask);
break;
@@ -1541,6 +1786,14 @@ static int interval_set_eval(struct eval_ctx *ctx, struct set *set,
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;
@@ -1606,21 +1859,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;
@@ -1647,11 +1885,61 @@ static void map_set_concat_info(struct expr *map)
}
}
+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 &&
@@ -1678,6 +1966,7 @@ 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;
@@ -1692,12 +1981,20 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
ctx->ectx.len, NULL);
}
- dtype = set_datatype_alloc(ectx.dtype, ectx.byteorder);
- if (dtype->type == TYPE_VERDICT)
+ if (!ectx.dtype)
+ 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
+ } 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,
@@ -1721,9 +2018,13 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
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;
@@ -1742,11 +2043,11 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
/* symbol has been already evaluated to set reference */
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",
@@ -1804,17 +2105,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;
@@ -1826,6 +2124,10 @@ 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,
@@ -2091,16 +2393,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)
@@ -2164,11 +2504,22 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
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:
/*
* 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:
@@ -2181,7 +2532,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) {
@@ -2280,11 +2631,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");
@@ -2351,9 +2703,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:
@@ -2403,6 +2756,7 @@ 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);
@@ -2419,7 +2773,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:
@@ -2526,13 +2880,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:
@@ -2547,6 +2906,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;
}
@@ -2565,6 +2928,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)
@@ -2575,7 +2957,8 @@ 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,
@@ -2626,6 +3009,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;
@@ -2733,6 +3121,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)
@@ -2768,20 +3174,43 @@ 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;
+
+ switch (stmt->meta.expr->etype) {
+ case EXPR_RANGE:
+ ret = expr_error(ctx->msgs, stmt->meta.expr,
+ "Meta expression cannot be a range");
+ break;
+ default:
+ break;
+
+ }
+
+ return ret;
}
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))
@@ -2795,9 +3224,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,
@@ -2809,9 +3239,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;
@@ -2870,7 +3301,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;
}
@@ -2878,6 +3309,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;
@@ -2887,7 +3319,7 @@ 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:
@@ -2895,14 +3327,14 @@ static int stmt_evaluate_reject_inet_family(struct eval_ctx *ctx,
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:
return stmt_error(ctx, stmt,
@@ -2917,9 +3349,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;
@@ -2934,13 +3367,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):
@@ -2948,29 +3382,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;
@@ -2982,14 +3416,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;
@@ -3003,7 +3438,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:
@@ -3017,7 +3454,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");
@@ -3041,28 +3478,29 @@ 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:
@@ -3079,14 +3517,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):
@@ -3105,7 +3543,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;
@@ -3122,9 +3563,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)
@@ -3141,7 +3582,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;
@@ -3169,13 +3610,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) {
@@ -3185,7 +3627,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;
@@ -3214,7 +3656,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);
@@ -3267,7 +3709,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))
@@ -3275,41 +3724,97 @@ 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 const char *stmt_name(const struct stmt *stmt)
+{
+ switch (stmt->ops->type) {
+ case STMT_NAT:
+ switch (stmt->nat.type) {
+ case NFT_NAT_SNAT:
+ return "snat";
+ case NFT_NAT_DNAT:
+ return "dnat";
+ case NFT_NAT_REDIR:
+ return "redirect";
+ case NFT_NAT_MASQ:
+ return "masquerade";
+ }
+ break;
+ default:
+ break;
+ }
+
+ return stmt->ops->name;
}
static int stmt_evaluate_l3proto(struct eval_ctx *ctx,
struct stmt *stmt, uint8_t family)
{
+ 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);
@@ -3322,10 +3827,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 (stmt->nat.family == NFPROTO_INET)
+ expr_family_infer(pctx, stmt->nat.addr, &stmt->nat.family);
switch (stmt->nat.family) {
case NFPROTO_IPV4:
@@ -3335,23 +3845,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 (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,
+ 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)
- return 0;
+ if (stmt->nat.addr->etype != EXPR_MAP) {
+ err = 0;
+ goto out;
+ }
data = stmt->nat.addr->mappings->set->data;
if (data->flags & EXPR_F_INTERVAL)
@@ -3359,36 +3876,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;
}
@@ -3451,7 +3974,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;
@@ -3471,13 +3994,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;
@@ -3486,7 +4010,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");
@@ -3501,7 +4025,7 @@ static int stmt_evaluate_tproxy(struct eval_ctx *ctx, struct stmt *stmt)
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)
@@ -3550,7 +4074,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;
@@ -3570,9 +4094,9 @@ static int stmt_evaluate_chain(struct eval_ctx *ctx, struct stmt *stmt)
struct handle h2 = {};
handle_merge(&rule->handle, &ctx->rule->handle);
- xfree(rule->handle.table.name);
+ free_const(rule->handle.table.name);
rule->handle.table.name = xstrdup(ctx->rule->handle.table.name);
- xfree(rule->handle.chain.name);
+ free_const(rule->handle.chain.name);
rule->handle.chain.name = NULL;
rule->handle.chain_id = chain->handle.chain_id;
if (rule_evaluate(&rule_ctx, rule, CMD_INVALID) < 0)
@@ -3596,9 +4120,10 @@ static int stmt_evaluate_optstrip(struct eval_ctx *ctx, struct stmt *stmt)
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)
@@ -3638,10 +4163,11 @@ static int stmt_evaluate_dup(struct eval_ctx *ctx, struct stmt *stmt)
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,
@@ -3704,15 +4230,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);
@@ -3723,13 +4259,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,
@@ -3777,7 +4313,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,
@@ -3820,7 +4356,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,
@@ -3845,6 +4385,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)
@@ -3914,7 +4457,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",
@@ -3962,9 +4505,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:
@@ -4041,11 +4587,14 @@ static int setelem_evaluate(struct eval_ctx *ctx, struct cmd *cmd)
return -1;
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;
- if (set_is_interval(ctx->set->flags) &&
- !(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;
@@ -4085,11 +4634,10 @@ static int set_expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
i->dtype->type == TYPE_INTEGER) {
struct datatype *dtype;
- dtype = dtype_clone(i->dtype);
+ dtype = datatype_clone(i->dtype);
dtype->size = i->len;
dtype->byteorder = i->byteorder;
- dtype->refcnt = 1;
- i->dtype = dtype;
+ __datatype_set(i, dtype);
}
if (i->dtype->size == 0 && i->len == 0)
@@ -4112,7 +4660,7 @@ static int set_expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
}
(*expr)->flags |= flags;
- datatype_set(*expr, concat_type_alloc(ntype));
+ __datatype_set(*expr, concat_type_alloc(ntype));
(*expr)->len = size;
expr_set_context(&ctx->ectx, (*expr)->dtype, (*expr)->len);
@@ -4121,6 +4669,32 @@ static int set_expr_evaluate_concat(struct eval_ctx *ctx, struct expr **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)
+ 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;
@@ -4129,6 +4703,12 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
struct stmt *stmt;
const char *type;
+ type = set_is_map(set->flags) ? "map" : "set";
+
+ if (set->key == NULL)
+ return set_error(ctx, set, "%s definition does not specify key",
+ type);
+
if (!set_is_anonymous(set->flags)) {
table = table_cache_find(&ctx->nft->cache.table_cache,
set->handle.table.name,
@@ -4139,13 +4719,19 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
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);
@@ -4211,23 +4797,6 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
}
set->existing_set = existing_set;
- 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)
- 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;
}
@@ -4333,7 +4902,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;
}
@@ -4356,8 +4925,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);
@@ -4467,7 +5040,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;
@@ -4482,6 +5057,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);
@@ -4545,7 +5122,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,
@@ -4555,7 +5131,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);
}
@@ -4585,15 +5161,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,
@@ -4601,11 +5179,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;
}
@@ -4639,8 +5212,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;
@@ -4673,11 +5246,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)) {
@@ -4690,34 +5258,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;
}
@@ -4729,6 +5269,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);
@@ -4974,38 +5516,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);
@@ -5016,10 +5528,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(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,
@@ -5069,6 +5584,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,
@@ -5104,6 +5621,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,
@@ -5112,6 +5631,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);
}
@@ -5348,6 +5872,7 @@ 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)
@@ -5380,6 +5905,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 7390089c..dde48b6a 100644
--- a/src/expression.c
+++ b/src/expression.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 <limits.h>
#include <expression.h>
@@ -29,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;
@@ -94,7 +94,7 @@ void expr_free(struct expr *expr)
*/
if (expr->etype != EXPR_INVALID)
expr_destroy(expr);
- xfree(expr);
+ free(expr);
}
void expr_print(const struct expr *expr, struct output_ctx *octx)
@@ -314,7 +314,7 @@ static void symbol_expr_clone(struct expr *new, const struct expr *expr)
static void symbol_expr_destroy(struct expr *expr)
{
- xfree(expr->identifier);
+ free_const(expr->identifier);
}
static const struct expr_ops symbol_expr_ops = {
@@ -994,7 +994,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;
@@ -1012,7 +1012,7 @@ static struct expr *concat_expr_parse_udata(const struct nftnl_udata *attr)
if (!dtype)
goto err_free;
- concat_expr->dtype = datatype_get(dtype);
+ __datatype_set(concat_expr, dtype);
concat_expr->len = len;
return concat_expr;
@@ -1335,15 +1335,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)
@@ -1351,6 +1350,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",
@@ -1378,11 +1383,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)
@@ -1497,9 +1508,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;
@@ -1531,20 +1540,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 3e5f5cd8..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>
@@ -84,6 +83,9 @@ 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", name);
@@ -177,6 +179,8 @@ static struct expr *exthdr_expr_parse_udata(const struct nftnl_udata *attr)
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;
}
@@ -206,6 +210,7 @@ static int exthdr_expr_build_udata(struct nftnl_udata_buf *udbuf,
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;
@@ -283,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,
@@ -332,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;
@@ -398,7 +404,7 @@ bool exthdr_find_template(struct expr *expr, const struct expr *mask, unsigned i
found = tcpopt_find_template(expr, off, mask_len - mask_offset);
break;
case NFT_EXTHDR_OP_IPV6:
- exthdr_init_raw(expr, expr->exthdr.desc->type,
+ exthdr_init_raw(expr, expr->exthdr.raw_type,
off, mask_len - mask_offset, expr->exthdr.op, 0);
/* still failed to find a template... Bug. */
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 c0642e0c..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>
diff --git a/src/intervals.c b/src/intervals.c
index 13009ca1..85de0199 100644
--- a/src/intervals.c
+++ b/src/intervals.c
@@ -6,6 +6,8 @@
* later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <nftables.h>
#include <expression.h>
#include <intervals.h>
@@ -416,11 +418,12 @@ static int setelem_delete(struct list_head *msgs, struct set *set,
list_del(&i->list);
expr_free(i);
}
- } else if (set->automerge &&
- setelem_adjust(set, purge, &prev_range, &range, prev, i) < 0) {
- expr_error(msgs, i, "element does not exist");
- err = -1;
- goto err;
+ } 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;
@@ -708,9 +711,9 @@ int set_to_intervals(const struct set *set, struct expr *init, bool add)
if (set->key->byteorder == BYTEORDER_HOST_ENDIAN)
mpz_switch_byteorder(expr->value, set->key->len / BITS_PER_BYTE);
- newelem = set_elem_expr_alloc(&internal_location, expr);
+ newelem = set_elem_expr_alloc(&expr->location, expr);
if (i->etype == EXPR_MAPPING) {
- newelem = mapping_expr_alloc(&internal_location,
+ newelem = mapping_expr_alloc(&expr->location,
newelem,
expr_get(i->right));
}
diff --git a/src/ipopt.c b/src/ipopt.c
index 67e904ff..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>
diff --git a/src/json.c b/src/json.c
index a525fd1b..6809cd50 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,9 +81,50 @@ 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 *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;
+ 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;
}
@@ -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);
@@ -152,6 +205,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 +226,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 +257,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 +266,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 +280,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 +302,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 +339,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 +497,7 @@ static json_t *table_flags_json(const struct table *table)
json_decref(root);
return NULL;
case 1:
- json_unpack(root, "[o]", &tmp);
+ json_unpack(root, "[O]", &tmp);
json_decref(root);
root = tmp;
break;
@@ -478,6 +518,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);
}
@@ -535,15 +578,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 +764,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 +1098,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 +1110,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)
@@ -1439,12 +1494,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)
@@ -1584,6 +1676,19 @@ json_t *optstrip_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
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)
{
@@ -1717,10 +1822,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));
}
@@ -1856,6 +1964,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 a376825d..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;
@@ -147,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);
@@ -175,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);
@@ -213,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;
}
@@ -338,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);
@@ -348,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);
@@ -403,6 +398,22 @@ 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)
{
@@ -520,6 +531,14 @@ static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs,
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,
@@ -539,13 +558,6 @@ static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs,
if (err < 0 || nft->state->nerrs)
return -1;
- list_for_each_entry(cmd, cmds, list) {
- if (cmd->op != CMD_ADD)
- continue;
-
- nft_cmd_expand(cmd);
- }
-
return 0;
}
@@ -561,7 +573,7 @@ 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,
@@ -583,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);
@@ -602,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;
}
@@ -648,19 +662,52 @@ retry:
return rc;
}
+/* 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;
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);
@@ -679,7 +726,7 @@ static 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);
@@ -697,18 +744,19 @@ 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]);
@@ -752,12 +800,12 @@ int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
if (nft->optimize_flags) {
ret = nft_run_optimized_file(nft, filename);
- xfree(nft->stdin_buf);
+ free_const(nft->stdin_buf);
return ret;
}
ret = __nft_run_cmd_from_filename(nft, filename);
- xfree(nft->stdin_buf);
+ free_const(nft->stdin_buf);
return ret;
}
diff --git a/src/libnftables.map b/src/libnftables.map
index a46a3ad5..9369f44f 100644
--- a/src/libnftables.map
+++ b/src/libnftables.map
@@ -33,3 +33,8 @@ 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 9bd25db8..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>
@@ -325,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;
@@ -333,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;
+ }
}
}
@@ -361,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())
@@ -384,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;
@@ -405,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:
@@ -415,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:
@@ -450,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)
@@ -470,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:
@@ -492,18 +502,20 @@ int main(int argc, char * const *argv)
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(" ");
@@ -511,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]);
@@ -519,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 dca71422..4d0e280f 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,20 +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:
case EXPR_RANGE:
- expr_msort_value(expr->left, value);
- break;
+ 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;
@@ -52,20 +48,24 @@ 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;
}
diff --git a/src/meta.c b/src/meta.c
index 257bbc9f..d7f810ce 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)
{
@@ -103,13 +99,13 @@ static struct error_record *tchandle_type_parse(struct parse_ctx *ctx,
handle = strtoull(sym->identifier, NULL, 0);
}
out:
- xfree(str);
+ free(str);
*res = constant_expr_alloc(&sym->location, sym->dtype,
BYTEORDER_HOST_ENDIAN,
sizeof(handle) * BITS_PER_BYTE, &handle);
return NULL;
err:
- xfree(str);
+ free(str);
return error(&sym->location, "Could not parse %s", sym->dtype->desc);
}
@@ -385,21 +381,22 @@ 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);
+ tstamp = tstamp64;
+ if (localtime_r(&tstamp, &tm))
+ tstamp64 += tm.tm_gmtoff;
- if (cur_tm)
- tstamp += cur_tm->tm_gmtoff;
-
- if ((tm = gmtime((time_t *) &tstamp)) != NULL &&
- strftime(timestr, sizeof(timestr) - 1, "%Y-%m-%d %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");
@@ -407,7 +404,8 @@ static void date_type_print(const struct expr *expr, struct output_ctx *octx)
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));
@@ -429,14 +427,15 @@ 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 */
- *tstamp = ts - cur_tm->tm_gmtoff;
+ *tstamp = ts - cur_tm.tm_gmtoff;
return true;
}
@@ -491,16 +490,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;
@@ -517,10 +521,12 @@ static struct error_record *hour_type_parse(struct parse_ctx *ctx,
struct expr **res)
{
struct error_record *er;
- struct tm tm, *cur_tm;
+ 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));
@@ -534,7 +540,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')
@@ -698,6 +707,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)
@@ -717,12 +728,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)
@@ -734,6 +749,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;
}
/**
@@ -768,6 +784,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;
@@ -807,13 +828,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;
}
@@ -825,6 +852,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;
@@ -839,6 +867,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;
@@ -852,7 +882,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 = {
@@ -901,12 +938,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);
}
@@ -931,8 +972,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;
}
@@ -960,11 +1004,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))
@@ -987,9 +1031,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;
@@ -997,8 +1042,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 e87b0338..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;
@@ -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);
@@ -654,13 +666,21 @@ err_free:
}
struct nftnl_rule_list *mnl_nft_rule_dump(struct netlink_ctx *ctx, int family,
- const char *table, const char *chain)
+ 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();
@@ -670,14 +690,16 @@ struct nftnl_rule_list *mnl_nft_rule_dump(struct netlink_ctx *ctx, int family,
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);
@@ -696,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)
@@ -720,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)
@@ -794,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));
@@ -807,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);
@@ -847,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;
@@ -856,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);
@@ -872,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);
@@ -992,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;
@@ -1001,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);
@@ -1238,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;
@@ -1248,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);
@@ -1453,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;
@@ -1463,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);
@@ -1737,6 +1842,7 @@ int mnl_nft_setelem_flush(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)
{
+ enum nf_tables_msg_types msg_type = NFT_MSG_DELSETELEM;
struct nftnl_set *nls;
int err;
@@ -1753,7 +1859,10 @@ int mnl_nft_setelem_del(struct netlink_ctx *ctx, struct cmd *cmd,
netlink_dump_set(nls, ctx);
- err = mnl_nft_setelem_batch(nls, cmd, ctx->batch, NFT_MSG_DELSETELEM, 0,
+ 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);
@@ -1761,14 +1870,21 @@ int mnl_nft_setelem_del(struct netlink_ctx *ctx, struct cmd *cmd,
}
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);
@@ -1791,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);
@@ -1867,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();
@@ -1918,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);
@@ -1951,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);
@@ -1960,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)
@@ -1971,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);
@@ -2000,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);
@@ -2071,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)
@@ -2183,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;
@@ -2217,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]);
@@ -2235,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])
@@ -2356,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 7fa92ebf..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