summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--COPYING8
-rw-r--r--Makefile.am3
-rw-r--r--configure.ac19
-rw-r--r--doc/libnftables-json.adoc27
-rw-r--r--doc/libnftables.adoc19
-rw-r--r--doc/nft.txt98
-rw-r--r--doc/payload-expression.txt193
-rw-r--r--doc/primary-expression.txt8
-rw-r--r--doc/stateful-objects.txt11
-rw-r--r--doc/statements.txt68
-rw-r--r--examples/.gitignore5
-rw-r--r--examples/Makefile.am6
-rw-r--r--examples/json-ruleset.nft43
-rw-r--r--examples/nft-buffer.c34
-rw-r--r--examples/nft-json-file.c30
-rw-r--r--include/Makefile.am2
-rw-r--r--include/cache.h44
-rw-r--r--include/datatype.h7
-rw-r--r--include/erec.h5
-rw-r--r--include/expression.h20
-rw-r--r--include/intervals.h12
-rw-r--r--include/ipopt.h2
-rw-r--r--include/json.h6
-rw-r--r--include/linux/netfilter.h1
-rw-r--r--include/linux/netfilter/nf_tables.h71
-rw-r--r--include/mnl.h24
-rw-r--r--include/netlink.h30
-rw-r--r--include/nftables.h5
-rw-r--r--include/nftables/libnftables.h7
-rw-r--r--include/parser.h35
-rw-r--r--include/payload.h19
-rw-r--r--include/proto.h62
-rw-r--r--include/rbtree.h98
-rw-r--r--include/rule.h26
-rw-r--r--include/statement.h20
-rw-r--r--include/tcpopt.h13
-rw-r--r--py/nftables.py99
-rw-r--r--src/Makefile.am7
-rw-r--r--src/cache.c614
-rw-r--r--src/cli.c22
-rw-r--r--src/cmd.c12
-rw-r--r--src/ct.c10
-rw-r--r--src/datatype.c36
-rw-r--r--src/erec.c89
-rw-r--r--src/evaluate.c715
-rw-r--r--src/expression.c67
-rw-r--r--src/exthdr.c109
-rw-r--r--src/fib.c4
-rw-r--r--src/hash.c4
-rw-r--r--src/iface.c54
-rw-r--r--src/intervals.c748
-rw-r--r--src/ipopt.c26
-rw-r--r--src/json.c134
-rw-r--r--src/libnftables.c158
-rw-r--r--src/libnftables.map5
-rw-r--r--src/main.c13
-rw-r--r--src/mergesort.c7
-rw-r--r--src/meta.c68
-rw-r--r--src/misspell.c8
-rw-r--r--src/mnl.c304
-rw-r--r--src/monitor.c69
-rw-r--r--src/netlink.c323
-rw-r--r--src/netlink_delinearize.c642
-rw-r--r--src/netlink_linearize.c118
-rw-r--r--src/numgen.c4
-rw-r--r--src/optimize.c1252
-rw-r--r--src/osf.c8
-rw-r--r--src/owner.c10
-rw-r--r--src/parser_bison.y777
-rw-r--r--src/parser_json.c211
-rw-r--r--src/payload.c366
-rw-r--r--src/print.c5
-rw-r--r--src/proto.c152
-rw-r--r--src/rbtree.c388
-rw-r--r--src/rule.c198
-rw-r--r--src/scanner.l398
-rw-r--r--src/segtree.c959
-rw-r--r--src/socket.c4
-rw-r--r--src/statement.c35
-rw-r--r--src/tcpopt.c38
-rw-r--r--src/xfrm.c6
-rw-r--r--src/xt.c221
-rwxr-xr-xtests/build/run-tests.sh2
-rwxr-xr-xtests/monitor/run-tests.sh4
-rw-r--r--tests/monitor/testcases/map-expr.t6
-rw-r--r--tests/monitor/testcases/set-interval.t5
-rw-r--r--tests/monitor/testcases/simple.t4
-rw-r--r--tests/py/any/ct.t3
-rw-r--r--tests/py/any/ct.t.json19
-rw-r--r--tests/py/any/ct.t.payload8
-rw-r--r--tests/py/any/icmpX.t.netdev3
-rw-r--r--tests/py/any/limit.t8
-rw-r--r--tests/py/any/limit.t.json39
-rw-r--r--tests/py/any/limit.t.json.output24
-rw-r--r--tests/py/any/limit.t.payload37
-rw-r--r--tests/py/any/meta.t7
-rw-r--r--tests/py/any/meta.t.json72
-rw-r--r--tests/py/any/meta.t.payload28
-rw-r--r--tests/py/any/objects.t3
-rw-r--r--tests/py/any/queue.t1
-rw-r--r--tests/py/any/quota.t3
-rw-r--r--tests/py/any/rawpayload.t17
-rw-r--r--tests/py/any/rawpayload.t.json56
-rw-r--r--tests/py/any/rawpayload.t.json.output18
-rw-r--r--tests/py/any/rawpayload.t.payload20
-rw-r--r--tests/py/any/tcpopt.t27
-rw-r--r--tests/py/any/tcpopt.t.json194
-rw-r--r--tests/py/any/tcpopt.t.payload76
-rw-r--r--tests/py/arp/arp.t3
-rw-r--r--tests/py/bridge/vlan.t11
-rw-r--r--tests/py/bridge/vlan.t.json124
-rw-r--r--tests/py/bridge/vlan.t.payload39
-rw-r--r--tests/py/bridge/vlan.t.payload.netdev47
-rw-r--r--tests/py/inet/ah.t3
-rw-r--r--tests/py/inet/comp.t3
-rw-r--r--tests/py/inet/dccp.t3
-rw-r--r--tests/py/inet/esp.t3
-rw-r--r--tests/py/inet/ether-ip.t3
-rw-r--r--tests/py/inet/ether.t9
-rw-r--r--tests/py/inet/ether.t.json32
-rw-r--r--tests/py/inet/ether.t.payload20
-rw-r--r--tests/py/inet/ether.t.payload.bridge16
-rw-r--r--tests/py/inet/ether.t.payload.ip20
-rw-r--r--tests/py/inet/geneve.t23
-rw-r--r--tests/py/inet/geneve.t.payload114
-rw-r--r--tests/py/inet/gre.t22
-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.payload87
-rw-r--r--tests/py/inet/icmpX.t2
-rw-r--r--tests/py/inet/icmpX.t.json.output9
-rw-r--r--tests/py/inet/ip.t3
-rw-r--r--tests/py/inet/ip.t.payload.bridge2
-rw-r--r--tests/py/inet/ip_tcp.t7
-rw-r--r--tests/py/inet/ip_tcp.t.json.output12
-rw-r--r--tests/py/inet/ipsec.t2
-rw-r--r--tests/py/inet/ipsec.t.json21
-rw-r--r--tests/py/inet/ipsec.t.payload6
-rw-r--r--tests/py/inet/map.t3
-rw-r--r--tests/py/inet/meta.t4
-rw-r--r--tests/py/inet/meta.t.json151
-rw-r--r--tests/py/inet/meta.t.payload36
-rw-r--r--tests/py/inet/reject.t2
-rw-r--r--tests/py/inet/reject.t.json34
-rw-r--r--tests/py/inet/reject.t.payload.inet10
-rw-r--r--tests/py/inet/sctp.t3
-rw-r--r--tests/py/inet/sets.t3
-rw-r--r--tests/py/inet/sets.t.json11
-rw-r--r--tests/py/inet/sets.t.payload.netdev6
-rw-r--r--tests/py/inet/tcp.t3
-rw-r--r--tests/py/inet/udp.t3
-rw-r--r--tests/py/inet/udplite.t3
-rw-r--r--tests/py/inet/vmap.t10
-rw-r--r--tests/py/inet/vmap.t.json144
-rw-r--r--tests/py/inet/vmap.t.payload34
-rw-r--r--tests/py/inet/vmap.t.payload.netdev34
-rw-r--r--tests/py/inet/vxlan.t23
-rw-r--r--tests/py/inet/vxlan.t.payload114
-rw-r--r--tests/py/ip/dnat.t1
-rw-r--r--tests/py/ip/dnat.t.json333
-rw-r--r--tests/py/ip/dnat.t.payload.ip13
-rw-r--r--tests/py/ip/ip.t3
-rw-r--r--tests/py/ip/ip.t.payload2
-rw-r--r--tests/py/ip/ip.t.payload.bridge2
-rw-r--r--tests/py/ip/ip.t.payload.inet2
-rw-r--r--tests/py/ip/ip.t.payload.netdev2
-rw-r--r--tests/py/ip/ip_tcp.t1
-rw-r--r--tests/py/ip/sets.t3
-rw-r--r--tests/py/ip/snat.t6
-rw-r--r--tests/py/ip/snat.t.json170
-rw-r--r--tests/py/ip/snat.t.json.output177
-rw-r--r--tests/py/ip/snat.t.payload35
-rw-r--r--tests/py/ip6/frag.t2
-rw-r--r--tests/py/ip6/frag.t.payload.netdev2186
-rw-r--r--tests/py/ip6/meta.t.json.output16
-rw-r--r--tests/py/ip6/sets.t3
-rw-r--r--tests/py/ip6/srh.t.payload6
-rw-r--r--tests/py/ip6/vmap.t3
-rw-r--r--tests/py/ip6/vmap.t.payload.inet2
-rw-r--r--tests/py/ip6/vmap.t.payload.ip62
-rw-r--r--tests/py/ip6/vmap.t.payload.netdev2
-rw-r--r--tests/py/netdev/dup.t (renamed from tests/py/any/dup.t)3
-rw-r--r--tests/py/netdev/dup.t.json (renamed from tests/py/any/dup.t.json)0
-rw-r--r--tests/py/netdev/dup.t.payload (renamed from tests/py/any/dup.t.payload)0
-rw-r--r--tests/py/netdev/fwd.t (renamed from tests/py/any/fwd.t)3
-rw-r--r--tests/py/netdev/fwd.t.json (renamed from tests/py/any/fwd.t.json)0
-rw-r--r--tests/py/netdev/fwd.t.json.output (renamed from tests/py/any/fwd.t.json.output)0
-rw-r--r--tests/py/netdev/fwd.t.payload (renamed from tests/py/any/fwd.t.payload)0
-rwxr-xr-xtests/py/nft-test.py31
-rw-r--r--tests/shell/README29
-rwxr-xr-xtests/shell/run-tests.sh19
-rwxr-xr-xtests/shell/testcases/cache/0010_implicit_chain_019
-rwxr-xr-xtests/shell/testcases/chains/0021prio_01
-rwxr-xr-xtests/shell/testcases/chains/0026prio_netdev_14
-rwxr-xr-xtests/shell/testcases/chains/0040mark_shift_02
-rwxr-xr-xtests/shell/testcases/chains/0041chain_binding_06
-rwxr-xr-xtests/shell/testcases/chains/0043chain_ingress_06
-rw-r--r--tests/shell/testcases/chains/dumps/0021prio_0.nft20
-rwxr-xr-xtests/shell/testcases/comments/comments_044
-rw-r--r--tests/shell/testcases/comments/dumps/comments_0.nft12
-rwxr-xr-xtests/shell/testcases/json/0001set_statements_09
-rwxr-xr-xtests/shell/testcases/json/0002table_map_09
-rwxr-xr-xtests/shell/testcases/json/0003json_schema_version_09
-rwxr-xr-xtests/shell/testcases/json/0004json_schema_version_111
-rwxr-xr-xtests/shell/testcases/json/0005secmark_objref_09
-rwxr-xr-xtests/shell/testcases/json/0006obj_comment_09
-rw-r--r--tests/shell/testcases/json/dumps/0001set_statements_0.nft12
-rw-r--r--tests/shell/testcases/json/dumps/0002table_map_0.nft6
-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.nft0
-rw-r--r--tests/shell/testcases/json/dumps/0005secmark_objref_0.nft18
-rw-r--r--tests/shell/testcases/json/dumps/0006obj_comment_0.nft6
-rwxr-xr-xtests/shell/testcases/json/netdev19
-rwxr-xr-xtests/shell/testcases/listing/0020flowtable_051
-rwxr-xr-xtests/shell/testcases/listing/0022terse_069
-rwxr-xr-xtests/shell/testcases/maps/0012map_019
-rwxr-xr-xtests/shell/testcases/maps/0013map_014
-rwxr-xr-xtests/shell/testcases/maps/anon_objmap_concat6
-rw-r--r--tests/shell/testcases/maps/dumps/0010concat_map_0.nft2
-rw-r--r--tests/shell/testcases/maps/dumps/0012map_0.nft13
-rw-r--r--tests/shell/testcases/maps/dumps/0013map_0.nft13
-rw-r--r--tests/shell/testcases/maps/dumps/anon_objmap_concat.nft16
-rw-r--r--tests/shell/testcases/maps/dumps/nat_addr_port.nft8
-rw-r--r--tests/shell/testcases/maps/dumps/typeof_integer_0.nft20
-rw-r--r--tests/shell/testcases/maps/dumps/typeof_maps_0.nft9
-rw-r--r--tests/shell/testcases/maps/dumps/typeof_maps_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_raw_0.nft13
-rwxr-xr-xtests/shell/testcases/maps/typeof_integer_027
-rwxr-xr-xtests/shell/testcases/maps/typeof_maps_09
-rwxr-xr-xtests/shell/testcases/maps/typeof_maps_concat6
-rwxr-xr-xtests/shell/testcases/maps/typeof_maps_concat_update_019
-rwxr-xr-xtests/shell/testcases/maps/typeof_raw_018
-rwxr-xr-xtests/shell/testcases/nft-f/0029split_file_025
-rwxr-xr-xtests/shell/testcases/nft-f/0030variable_reuse_019
-rwxr-xr-xtests/shell/testcases/nft-f/0031vmap_string_021
-rw-r--r--tests/shell/testcases/nft-f/dumps/0030variable_reuse_0.nft11
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_nat.nft24
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_reject.nft13
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_stmts.nft5
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft18
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.nft9
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_stmts_vmap.nft9
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_vmap_raw.nft31
-rw-r--r--tests/shell/testcases/optimizations/dumps/merge_vmaps.nft20
-rw-r--r--tests/shell/testcases/optimizations/dumps/not_mergeable.nft19
-rw-r--r--tests/shell/testcases/optimizations/dumps/skip_merge.nft23
-rw-r--r--tests/shell/testcases/optimizations/dumps/skip_non_eq.nft6
-rw-r--r--tests/shell/testcases/optimizations/dumps/skip_unsupported.nft7
-rwxr-xr-xtests/shell/testcases/optimizations/merge_nat43
-rwxr-xr-xtests/shell/testcases/optimizations/merge_reject26
-rwxr-xr-xtests/shell/testcases/optimizations/merge_stmts13
-rwxr-xr-xtests/shell/testcases/optimizations/merge_stmts_concat35
-rwxr-xr-xtests/shell/testcases/optimizations/merge_stmts_concat_vmap17
-rwxr-xr-xtests/shell/testcases/optimizations/merge_stmts_vmap17
-rwxr-xr-xtests/shell/testcases/optimizations/merge_vmap_raw32
-rwxr-xr-xtests/shell/testcases/optimizations/merge_vmaps31
-rwxr-xr-xtests/shell/testcases/optimizations/not_mergeable22
-rwxr-xr-xtests/shell/testcases/optimizations/ruleset168
-rwxr-xr-xtests/shell/testcases/optimizations/skip_merge34
-rwxr-xr-xtests/shell/testcases/optimizations/skip_non_eq12
-rwxr-xr-xtests/shell/testcases/optimizations/skip_unsupported14
-rwxr-xr-xtests/shell/testcases/optimizations/variables15
-rwxr-xr-xtests/shell/testcases/owner/0001-flowtable-uaf22
-rwxr-xr-xtests/shell/testcases/parsing/describe7
-rwxr-xr-xtests/shell/testcases/parsing/log10
-rwxr-xr-xtests/shell/testcases/parsing/octal13
-rwxr-xr-xtests/shell/testcases/rule_management/0003insert_04
-rwxr-xr-xtests/shell/testcases/rule_management/0011destroy_08
-rwxr-xr-xtests/shell/testcases/rule_management/0011reset_0168
-rwxr-xr-xtests/shell/testcases/rule_management/0012destroy_07
-rw-r--r--tests/shell/testcases/rule_management/dumps/0003insert_0.nft1
-rw-r--r--tests/shell/testcases/rule_management/dumps/0011destroy_0.nft4
-rw-r--r--tests/shell/testcases/rule_management/dumps/0011reset_0.nft31
-rw-r--r--tests/shell/testcases/rule_management/dumps/0012destroy_0.nft4
-rwxr-xr-xtests/shell/testcases/sets/0024named_objects_015
-rwxr-xr-xtests/shell/testcases/sets/0031set_timeout_size_02
-rwxr-xr-xtests/shell/testcases/sets/0045concat_ipv4_service16
-rwxr-xr-xtests/shell/testcases/sets/0046netmap_06
-rwxr-xr-xtests/shell/testcases/sets/0064map_catchall_05
-rwxr-xr-xtests/shell/testcases/sets/0067nat_concat_interval_011
-rwxr-xr-xtests/shell/testcases/sets/0068interval_stack_overflow_029
-rwxr-xr-xtests/shell/testcases/sets/0069interval_merge_028
-rwxr-xr-xtests/shell/testcases/sets/0070stacked_l2_headers6
-rwxr-xr-xtests/shell/testcases/sets/0071unclosed_prefix_interval_023
-rwxr-xr-xtests/shell/testcases/sets/automerge_0113
-rwxr-xr-xtests/shell/testcases/sets/collapse_elem_019
-rwxr-xr-xtests/shell/testcases/sets/concat_interval_024
-rw-r--r--tests/shell/testcases/sets/dumps/0024named_objects_0.nft18
-rw-r--r--tests/shell/testcases/sets/dumps/0045concat_ipv4_service.nft12
-rw-r--r--tests/shell/testcases/sets/dumps/0046netmap_0.nft6
-rw-r--r--tests/shell/testcases/sets/dumps/0064map_catchall_0.nft6
-rw-r--r--tests/shell/testcases/sets/dumps/0067nat_concat_interval_0.nft7
-rw-r--r--tests/shell/testcases/sets/dumps/0069interval_merge_0.nft9
-rw-r--r--tests/shell/testcases/sets/dumps/0070stacked_l2_headers.nft28
-rw-r--r--tests/shell/testcases/sets/dumps/0071unclosed_prefix_interval_0.nft19
-rw-r--r--tests/shell/testcases/sets/dumps/collapse_elem_0.nft12
-rw-r--r--tests/shell/testcases/sets/dumps/concat_interval_0.nft14
-rw-r--r--tests/shell/testcases/sets/dumps/dynset_missing.nft12
-rw-r--r--tests/shell/testcases/sets/dumps/inner_0.nft18
-rw-r--r--tests/shell/testcases/sets/dumps/set_eval_0.nft11
-rw-r--r--tests/shell/testcases/sets/dumps/sets_with_ifnames.nft62
-rw-r--r--tests/shell/testcases/sets/dumps/typeof_raw_0.nft12
-rw-r--r--tests/shell/testcases/sets/dumps/typeof_sets_0.nft73
-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/dynset_missing32
-rwxr-xr-xtests/shell/testcases/sets/errors_069
-rwxr-xr-xtests/shell/testcases/sets/exact_overlap_022
-rwxr-xr-xtests/shell/testcases/sets/inner_025
-rwxr-xr-xtests/shell/testcases/sets/set_eval_017
-rwxr-xr-xtests/shell/testcases/sets/sets_with_ifnames150
-rwxr-xr-xtests/shell/testcases/sets/typeof_raw_017
-rwxr-xr-xtests/shell/testcases/sets/typeof_sets_072
-rwxr-xr-xtests/shell/testcases/sets/typeof_sets_122
-rwxr-xr-xtests/shell/testcases/sets/typeof_sets_concat6
316 files changed, 15383 insertions, 2992 deletions
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/Makefile.am b/Makefile.am
index 4a17424d..72fb4e88 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3,7 +3,8 @@ ACLOCAL_AMFLAGS = -I m4
SUBDIRS = src \
include \
files \
- doc
+ doc \
+ examples
if HAVE_PYTHON
SUBDIRS += py
endif
diff --git a/configure.ac b/configure.ac
index 6069b871..a9c4b78f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,15 +1,15 @@
-AC_INIT([nftables], [1.0.0], [netfilter-devel@vger.kernel.org])
-AC_DEFINE([RELEASE_NAME], ["Fearless Fosdick #2"], [Release name])
+AC_INIT([nftables], [1.0.6], [netfilter-devel@vger.kernel.org])
+AC_DEFINE([RELEASE_NAME], ["Lester Gooch #5"], [Release name])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([-Wall foreign subdir-objects
- tar-pax no-dist-gzip dist-bzip2 1.6])
+ tar-pax no-dist-gzip dist-xz 1.6])
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
-AC_CONFIG_HEADER([config.h])
+AC_CONFIG_HEADERS([config.h])
AC_ARG_ENABLE([debug],
AS_HELP_STRING([--disable-debug], [Disable debugging symbols]),
@@ -26,7 +26,7 @@ AC_PROG_CC
AC_PROG_MKDIR_P
AC_PROG_INSTALL
AC_PROG_SED
-AM_PROG_LEX
+AC_PROG_LEX([noyywrap])
AC_PROG_YACC
if test -z "$ac_cv_prog_YACC" -a ! -f "${srcdir}/src/parser_bison.c"
@@ -43,11 +43,9 @@ then
fi
AM_PROG_AR
-AM_PROG_LIBTOOL
-LT_INIT
+LT_INIT([disable-static])
AM_PROG_CC_C_O
AC_EXEEXT
-AC_DISABLE_STATIC
CHECK_GCC_FVISIBILITY
AS_IF([test "x$enable_man_doc" = "xyes"], [
@@ -57,7 +55,7 @@ AS_IF([test "x$enable_man_doc" = "xyes"], [
])
PKG_CHECK_MODULES([LIBMNL], [libmnl >= 1.0.4])
-PKG_CHECK_MODULES([LIBNFTNL], [libnftnl >= 1.2.0])
+PKG_CHECK_MODULES([LIBNFTNL], [libnftnl >= 1.2.4])
AC_ARG_WITH([mini-gmp], [AS_HELP_STRING([--with-mini-gmp],
[Use builtin mini-gmp (for embedded builds)])],
@@ -69,7 +67,7 @@ AM_CONDITIONAL([BUILD_MINIGMP], [test "x$with_mini_gmp" = xyes])
AC_ARG_WITH([cli], [AS_HELP_STRING([--without-cli],
[disable interactive CLI (libreadline, editline or linenoise support)])],
- [], [with_cli=readline])
+ [], [with_cli=editline])
AS_IF([test "x$with_cli" = xreadline], [
AC_CHECK_LIB([readline], [readline], ,
@@ -147,6 +145,7 @@ AC_CONFIG_FILES([ \
files/osf/Makefile \
doc/Makefile \
py/Makefile \
+ examples/Makefile \
])
AC_OUTPUT
diff --git a/doc/libnftables-json.adoc b/doc/libnftables-json.adoc
index c152dc05..f4aea36e 100644
--- a/doc/libnftables-json.adoc
+++ b/doc/libnftables-json.adoc
@@ -91,14 +91,15 @@ translates into JSON as such:
{ "add": { "chain": {
"family": "inet",
"table": "mytable",
- "chain": "mychain"
- }}}
+ "name": "mychain"
+ }}},
{ "add": { "rule": {
"family": "inet",
"table": "mytable",
"chain": "mychain",
"expr": [
{ "match": {
+ "op": "==",
"left": { "payload": {
"protocol": "tcp",
"field": "dport"
@@ -174,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'
____
Reset state in suitable objects, i.e. zero their internal counter.
@@ -1058,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
@@ -1171,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
diff --git a/doc/libnftables.adoc b/doc/libnftables.adoc
index 3abb9595..7ea0d56e 100644
--- a/doc/libnftables.adoc
+++ b/doc/libnftables.adoc
@@ -37,6 +37,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,7 +71,7 @@ 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'.
@@ -118,7 +121,7 @@ NFT_CTX_OUTPUT_JSON::
This flag controls JSON output format, input is auto-detected.
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 +199,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 +209,14 @@ The function returns zero on success or non-zero if memory allocation failed.
The *nft_ctx_clear_include_paths*() function removes all include paths, even the built-in default one.
+=== nft_ctx_add_var() and nft_ctx_clear_vars()
+The *define* command in nftables ruleset allows one to define variables.
+
+The *nft_ctx_add_var*() function extends the list of variables in 'ctx'. The variable must be given in the format 'key=value'.
+The function returns zero on success or non-zero if the variable is malformed.
+
+The *nft_ctx_clear_vars*() function removes all variables.
+
=== nft_run_cmd_from_buffer() and nft_run_cmd_from_filename()
These functions perform the actual work of parsing user input into nftables commands and executing them.
diff --git a/doc/nft.txt b/doc/nft.txt
index c9bb901b..7de4935b 100644
--- a/doc/nft.txt
+++ b/doc/nft.txt
@@ -9,7 +9,7 @@ nft - Administration tool of the nftables framework for packet filtering and cla
SYNOPSIS
--------
[verse]
-*nft* [ *-nNscaeSupyjt* ] [ *-I* 'directory' ] [ *-f* 'filename' | *-i* | 'cmd' ...]
+*nft* [ *-nNscaeSupyjtT* ] [ *-I* 'directory' ] [ *-f* 'filename' | *-i* | 'cmd' ...]
*nft* *-h*
*nft* *-v*
@@ -62,6 +62,11 @@ understanding of their meaning. You can get information about options by running
*--check*::
Check commands validity without actually applying the changes.
+*-o*::
+*--optimize*::
+ Optimize your ruleset. You can combine this option with '-c' to inspect
+ the proposed optimizations.
+
.Ruleset list output formatting that modify the output of the list ruleset command:
*-a*::
@@ -165,17 +170,23 @@ SYMBOLIC VARIABLES
~~~~~~~~~~~~~~~~~~
[verse]
*define* 'variable' *=* 'expr'
+*undefine* 'variable'
+*redefine* 'variable' *=* 'expr'
*$variable*
Symbolic variables can be defined using the *define* statement. Variable
references are expressions and can be used to initialize other variables. The scope
of a definition is the current block and all blocks contained within.
+Symbolic variables can be undefined using the *undefine* statement, and modified
+using the *redefine* statement.
.Using symbolic variables
---------------------------------------
define int_if1 = eth0
define int_if2 = eth1
define int_ifs = { $int_if1, $int_if2 }
+redefine int_if2 = wlan0
+undefine int_if2
filter input iif $int_ifs accept
---------------------------------------
@@ -193,7 +204,7 @@ packet processing paths, which invoke nftables if rules for these hooks exist.
*inet*:: Internet (IPv4/IPv6) address family.
*arp*:: ARP address family, handling IPv4 ARP packets.
*bridge*:: Bridge address family, handling packets which traverse a bridge device.
-*netdev*:: Netdev address family, handling packets from ingress.
+*netdev*:: Netdev address family, handling packets on ingress and egress.
All nftables objects exist in address family specific namespaces, therefore all
identifiers include an address family. If an identifier is specified without an
@@ -251,9 +262,9 @@ The list of supported hooks is identical to IPv4/IPv6/Inet address families abov
NETDEV ADDRESS FAMILY
~~~~~~~~~~~~~~~~~~~~
-The Netdev address family handles packets from the device ingress path. This
-family allows you to filter packets of any ethertype such as ARP, VLAN 802.1q,
-VLAN 802.1ad (Q-in-Q) as well as IPv4 and IPv6 packets.
+The Netdev address family handles packets from the device ingress and egress
+path. This family allows you to filter packets of any ethertype such as ARP,
+VLAN 802.1q, VLAN 802.1ad (Q-in-Q) as well as IPv4 and IPv6 packets.
.Netdev address family hooks
[options="header"]
@@ -263,8 +274,27 @@ VLAN 802.1ad (Q-in-Q) as well as IPv4 and IPv6 packets.
All packets entering the system are processed by this hook. It is invoked after
the network taps (ie. *tcpdump*), right after *tc* ingress and before layer 3
protocol handlers, it can be used for early filtering and policing.
+|egress |
+All packets leaving the system are processed by this hook. It is invoked after
+layer 3 protocol handlers and before *tc* egress. It can be used for late
+filtering and policing.
|=================
+Tunneled packets (such as *vxlan*) are processed by netdev family hooks both
+in decapsulated and encapsulated (tunneled) form. So a packet can be filtered
+on the overlay network as well as on the underlying network.
+
+Note that the order of netfilter and *tc* is mirrored on ingress versus egress.
+This ensures symmetry for NAT and other packet mangling.
+
+Ingress packets which are redirected out some other interface are only
+processed by netfilter on egress if they have passed through netfilter ingress
+processing before. Thus, ingress packets which are redirected by *tc* are not
+subjected to netfilter. But they are if they are redirected by *netfilter* on
+ingress. Conceptually, tc and netfilter can be thought of as layers, with
+netfilter layered above tc: If the packet hasn't been passed up from the
+tc layer to the netfilter layer, it's not subjected to netfilter on egress.
+
RULESET
-------
[verse]
@@ -292,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*,
@@ -338,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.
@@ -345,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
@@ -360,6 +393,7 @@ organization.
are specified, the chain is created as a base chain and hooked up to the networking stack.
*create*:: Similar to the *add* command, but returns an error if the chain already exists.
*delete*:: Delete the specified chain. The chain must not contain any rules or be used as jump target.
+*destroy*:: Delete the specified chain, it does not fail if it does not exist. The chain must not contain any rules or be used as jump target.
*rename*:: Rename the specified chain.
*list*:: List all rules of the specified chain.
*flush*:: Flush all rules of the specified chain.
@@ -381,16 +415,17 @@ statements for instance).
|route | ip, ip6 | output |
If a packet has traversed a chain of this type and is about to be accepted, a
new route lookup is performed if relevant parts of the IP header have changed.
-This allows to e.g. implement policy routing selectors in nftables.
+This allows one to e.g. implement policy routing selectors in nftables.
|=================
Apart from the special cases illustrated above (e.g. *nat* type not supporting
*forward* hook or *route* type only supporting *output* hook), there are three
further quirks worth noticing:
-* The netdev family supports merely a single combination, namely *filter* type and
- *ingress* hook. Base chains in this family also require the *device* parameter
- to be present since they exist per incoming interface only.
+* The netdev family supports merely two combinations, namely *filter* type with
+ *ingress* hook and *filter* type with *egress* hook. Base chains in this
+ family also require the *device* parameter to be present since they exist per
+ interface only.
* The arp family supports only the *input* and *output* hooks, both in chains of type
*filter*.
* The inet family also supports the *ingress* hook (since Linux kernel 5.10),
@@ -441,7 +476,7 @@ with these standard names to ease relative prioritizing, e.g. *mangle - 5* stand
for *-155*. Values will also be printed like this until the value is not
further than 10 from the standard value.
-Base chains also allow to set the chain's *policy*, i.e. what happens to
+Base chains also allow one to set the chain's *policy*, i.e. what happens to
packets not explicitly accepted or refused in contained rules. Supported policy
values are *accept* (which is the default) or *drop*.
@@ -450,7 +485,11 @@ 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']
+*reset rules* *table* ['family'] 'table'
+*reset rules* *chain* ['family'] 'table' ['chain']
Rules are added to chains in the given table. If the family is not specified, the
ip family is used. Rules are constructed from two kinds of components according
@@ -478,6 +517,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, i.e. counter and quota statement values.
.*add a rule to ip table output chain*
-------------
@@ -528,10 +569,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*} *set* ['family'] 'table' 'set'
*list sets* ['family']
*delete set* ['family'] 'table' *handle* 'handle'
-{*add* | *delete*} *element* ['family'] 'table' 'set' *{* 'element'[*,* ...] *}*
+{*add* | *delete* | *destroy* } *element* ['family'] 'table' 'set' *{* 'element'[*,* ...] *}*
Sets are element containers of a user-defined data type, they are uniquely
identified by a user-defined name and attached to tables. Their behaviour can
@@ -540,6 +581,7 @@ 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.
@@ -581,7 +623,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*} *map* ['family'] 'table' 'map'
*list maps* ['family']
Maps store data based on some specific key used as input. They are uniquely identified by a user-defined name and attached to tables.
@@ -589,6 +631,7 @@ 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.
@@ -623,13 +666,13 @@ ELEMENTS
--------
[verse]
____
-{*add* | *create* | *delete* | *get* } *element* ['family'] 'table' 'set' *{* 'ELEMENT'[*,* ...] *}*
+{*add* | *create* | *delete* | *destroy* | *get* } *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
@@ -661,7 +704,7 @@ FLOWTABLES
[verse]
{*add* | *create*} *flowtable* ['family'] 'table' 'flowtable' *{ hook* 'hook' *priority* 'priority' *; devices = {* 'device'[*,* ...] *} ; }*
*list flowtables* ['family']
-{*delete* | *list*} *flowtable* ['family'] 'table' 'flowtable'
+{*delete* | *destroy* | *list*} *flowtable* ['family'] 'table' 'flowtable'
*delete* *flowtable* ['family'] 'table' *handle* 'handle'
Flowtables allow you to accelerate packet forwarding in software. Flowtables
@@ -685,6 +728,7 @@ and subtraction can be used to set relative priority, e.g. filter + 5 equals to
[horizontal]
*add*:: Add a new flowtable for the given family with the given name.
*delete*:: Delete the specified flowtable.
+*destroy*:: Delete the specified flowtable, it does not fail if it does not exist.
*list*:: List all flowtables.
LISTING
@@ -701,11 +745,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* 'table'
+*reset quotas* ['family'] *table* 'table'
Stateful objects are attached to tables and are identified by a unique name.
They group stateful information from rules, to reference them in rules the
@@ -714,6 +769,7 @@ keywords "type name" are used e.g. "counter name".
[horizontal]
*add*:: Add a new stateful object in the specified table.
*delete*:: Delete the specified object.
+*destroy*:: Delete the specified object, it does not fail if it does not exist.
*list*:: Display stateful information the object holds.
*reset*:: List-and-reset stateful object.
diff --git a/doc/payload-expression.txt b/doc/payload-expression.txt
index 930a1807..f1de3447 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"]
|==================
@@ -524,6 +532,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 +710,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
@@ -614,37 +778,37 @@ Segment Routing Header
|Keyword| Description | TCP option fields
|eol|
End if option list|
-kind
+-
|nop|
1 Byte TCP Nop padding option |
-kind
+-
|maxseg|
TCP Maximum Segment Size|
-kind, length, size
+length, size
|window|
TCP Window Scaling |
-kind, length, count
+length, count
|sack-perm |
TCP SACK permitted |
-kind, length
+length
|sack|
TCP Selective Acknowledgement (alias of block 0) |
-kind, length, left, right
+length, left, right
|sack0|
TCP Selective Acknowledgement (block 0) |
-kind, length, left, right
+length, left, right
|sack1|
TCP Selective Acknowledgement (block 1) |
-kind, length, left, right
+length, left, right
|sack2|
TCP Selective Acknowledgement (block 2) |
-kind, length, left, right
+length, left, right
|sack3|
TCP Selective Acknowledgement (block 3) |
-kind, length, left, right
+length, left, right
|timestamp|
TCP Timestamps |
-kind, length, tsval, tsecr
+length, tsval, tsecr
|============================
TCP option matching also supports raw expression syntax to access arbitrary options:
@@ -673,7 +837,12 @@ type, length, ptr, addr
.finding TCP options
--------------------
-filter input tcp option sack-perm kind 1 counter
+filter input tcp option sack-perm exists counter
+--------------------
+
+.matching TCP options
+--------------------
+filter input tcp option maxseg size lt 536
--------------------
.matching IPv6 exthdr
diff --git a/doc/primary-expression.txt b/doc/primary-expression.txt
index f97778b9..e13970cf 100644
--- a/doc/primary-expression.txt
+++ b/doc/primary-expression.txt
@@ -428,6 +428,10 @@ Destination address of the tunnel|
ipv4_addr/ipv6_addr
|=================================
+*Note:* When using xfrm_interface, this expression is not useable in output
+hook as the plain packet does not traverse it with IPsec info attached - use a
+chain in postrouting hook instead.
+
NUMGEN EXPRESSION
~~~~~~~~~~~~~~~~~
@@ -438,7 +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:
@@ -468,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 4972969e..e3c79220 100644
--- a/doc/stateful-objects.txt
+++ b/doc/stateful-objects.txt
@@ -77,6 +77,17 @@ per ct timeout comment field |
string
|=================
+tcp connection state names that can have a specific timeout value are:
+
+'close', 'close_wait', 'established', 'fin_wait', 'last_ack', 'retrans', 'syn_recv', 'syn_sent', 'time_wait' and 'unack'.
+
+You can use 'sysctl -a |grep net.netfilter.nf_conntrack_tcp_timeout_' to view and change the system-wide defaults.
+'ct timeout' allows for flow-specific settings, without changing the global timeouts.
+
+For example, tcp port 53 could have much lower settings than other traffic.
+
+udp state names that can have a specific timeout value are 'replied' and 'unreplied'.
+
.defining and assigning ct timeout policy
----------------------------------
table ip filter {
diff --git a/doc/statements.txt b/doc/statements.txt
index d402da70..0532b2b1 100644
--- a/doc/statements.txt
+++ b/doc/statements.txt
@@ -11,7 +11,7 @@ The verdict statement alters control flow in the ruleset and issues policy decis
[horizontal]
*accept*:: Terminate ruleset evaluation and accept the packet.
The packet can still be dropped later by another hook, for instance accept
-in the forward hook still allows to drop the packet later in the postrouting hook,
+in the forward hook still allows one to drop the packet later in the postrouting hook,
or another forward base chain that has a higher priority number and is evaluated
afterwards in the processing pipeline.
*drop*:: Terminate ruleset evaluation and drop the packet.
@@ -71,7 +71,7 @@ EXTENSION HEADER STATEMENT
The extension header statement alters packet content in variable-sized headers.
This can currently be used to alter the TCP Maximum segment size of packets,
-similar to TCPMSS.
+similar to the TCPMSS target in iptables.
.change tcp mss
---------------
@@ -80,6 +80,13 @@ tcp flags syn tcp option maxseg size set 1360
tcp flags syn tcp option maxseg size set rt mtu
---------------
+You can also remove tcp options via reset keyword.
+
+.remove tcp option
+---------------
+tcp flags syn reset tcp option sack-perm
+---------------
+
LOG STATEMENT
~~~~~~~~~~~~~
[verse]
@@ -271,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]
@@ -325,8 +332,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"]
@@ -601,7 +613,7 @@ ____
QUEUE_EXPRESSION can be used to compute a queue number
at run-time with the hash or numgen expressions. It also
-allows to use the map statement to assign fixed queue numbers
+allows one to use the map statement to assign fixed queue numbers
based on external inputs such as the source ip address or interface names.
.queue statement values
@@ -658,7 +670,7 @@ string
ip filter forward dup to 10.2.3.4 device "eth0"
# copy raw frame to another interface
-netdetv ingress dup to "eth0"
+netdev ingress dup to "eth0"
dup to "eth0"
# combine with map dst addr to gateways
@@ -668,10 +680,27 @@ dup to ip daddr map { 192.168.7.1 : "eth0", 192.168.7.2 : "eth1" }
FWD STATEMENT
~~~~~~~~~~~~~
The fwd statement is used to redirect a raw packet to another interface. It is
-only available in the netdev family ingress hook. It is similar to the dup
-statement except that no copy is made.
+only available in the netdev family ingress and egress hooks. It is similar to
+the dup statement except that no copy is made.
+You can also specify the address of the next hop and the device to forward the
+packet to. This updates the source and destination MAC address of the packet by
+transmitting it through the neighboring layer. This also decrements the ttl
+field of the IP packet. This provides a way to effectively bypass the classical
+forwarding path, thus skipping the fib (forwarding information base) lookup.
+
+[verse]
*fwd to* 'device'
+*fwd* [*ip* | *ip6*] *to* 'address' *device* 'device'
+
+.Using the fwd statement
+------------------------
+# redirect raw packet to device
+netdev ingress fwd to "eth0"
+
+# forward packet to next hop 192.168.200.1 via eth0 device
+netdev ingress ether saddr set fwd ip to 192.168.200.1 device "eth0"
+-----------------------------------
SET STATEMENT
~~~~~~~~~~~~~
@@ -687,6 +716,10 @@ will not grow indefinitely) either from the set definition or from the statement
that adds or updates them. The set statement can be used to e.g. create dynamic
blacklists.
+Dynamic updates are also supported with maps. In this case, the *add* or
+*update* rule needs to provide both the key and the data element (value),
+separated via ':'.
+
[verse]
{*add* | *update*} *@*'setname' *{* 'expression' [*timeout* 'timeout'] [*comment* 'string'] *}*
@@ -771,3 +804,20 @@ ____
# jump to different chains depending on layer 4 protocol type:
nft add rule ip filter input ip protocol vmap { tcp : jump tcp-chain, udp : jump udp-chain , icmp : jump icmp-chain }
------------------------
+
+XT STATEMENT
+~~~~~~~~~~~~
+This represents an xt statement from xtables compat interface. It is a
+fallback if translation is not available or not complete.
+
+[verse]
+____
+*xt* 'TYPE' 'NAME'
+
+'TYPE' := *match* | *target* | *watcher*
+____
+
+Seeing this means the ruleset (or parts of it) were created by *iptables-nft*
+and one should use that to manage it.
+
+*BEWARE:* nftables won't restore these statements.
diff --git a/examples/.gitignore b/examples/.gitignore
new file mode 100644
index 00000000..7b1a583c
--- /dev/null
+++ b/examples/.gitignore
@@ -0,0 +1,5 @@
+/.deps/
+/.libs/
+/nft-buffer
+/nft-json-file
+/*.o
diff --git a/examples/Makefile.am b/examples/Makefile.am
new file mode 100644
index 00000000..3b8b0b67
--- /dev/null
+++ b/examples/Makefile.am
@@ -0,0 +1,6 @@
+check_PROGRAMS = nft-buffer \
+ nft-json-file
+
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+LDADD = $(top_builddir)/src/libnftables.la
diff --git a/examples/json-ruleset.nft b/examples/json-ruleset.nft
new file mode 100644
index 00000000..acea1786
--- /dev/null
+++ b/examples/json-ruleset.nft
@@ -0,0 +1,43 @@
+{
+ "nftables": [
+ {
+ "flush": {
+ "ruleset": {
+ "family": "ip"
+ }
+ }
+ },
+ {
+ "table": {
+ "family": "ip",
+ "name": "x"
+ }
+ },
+ {
+ "chain": {
+ "family": "ip",
+ "table": "x",
+ "name": "y",
+ "type": "filter",
+ "hook": "input",
+ "prio": 0,
+ "policy": "accept"
+ }
+ },
+ {
+ "rule": {
+ "family": "ip",
+ "table": "x",
+ "chain": "y",
+ "expr": [
+ {
+ "counter": {
+ "packets": 0,
+ "bytes": 0
+ }
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/examples/nft-buffer.c b/examples/nft-buffer.c
new file mode 100644
index 00000000..1c4b1e04
--- /dev/null
+++ b/examples/nft-buffer.c
@@ -0,0 +1,34 @@
+/* gcc nft-buffer.c -o nft-buffer -lnftables */
+#include <stdlib.h>
+#include <nftables/libnftables.h>
+
+const char ruleset[] =
+ "flush ruleset;"
+ "add table x;"
+ "add chain x y { type filter hook input priority 0; };"
+ "add rule x y counter;";
+
+int main(void)
+{
+ struct nft_ctx *ctx;
+ int err;
+
+ ctx = nft_ctx_new(0);
+ if (!ctx) {
+ perror("cannot allocate nft context");
+ return EXIT_FAILURE;
+ }
+
+ /* create ruleset: all commands in the buffer are atomically applied */
+ err = nft_run_cmd_from_buffer(ctx, ruleset);
+ if (err < 0)
+ fprintf(stderr, "failed to run nftables command\n");
+
+ err = nft_run_cmd_from_buffer(ctx, "list ruleset");
+ if (err < 0)
+ fprintf(stderr, "failed to run nftables command\n");
+
+ nft_ctx_free(ctx);
+
+ return EXIT_SUCCESS;
+}
diff --git a/examples/nft-json-file.c b/examples/nft-json-file.c
new file mode 100644
index 00000000..0c832f22
--- /dev/null
+++ b/examples/nft-json-file.c
@@ -0,0 +1,30 @@
+/* gcc nft-json-file.c -o nft-json-file -lnftables */
+#include <stdlib.h>
+#include <nftables/libnftables.h>
+
+int main(void)
+{
+ struct nft_ctx *ctx;
+ int err;
+
+ ctx = nft_ctx_new(0);
+ if (!ctx) {
+ perror("cannot allocate nft context");
+ return EXIT_FAILURE;
+ }
+
+ nft_ctx_output_set_flags(ctx, NFT_CTX_OUTPUT_JSON);
+
+ /* create ruleset: all commands in the buffer are atomically applied */
+ err = nft_run_cmd_from_filename(ctx, "json-ruleset.nft");
+ if (err < 0)
+ fprintf(stderr, "failed to run nftables command\n");
+
+ err = nft_run_cmd_from_buffer(ctx, "list ruleset");
+ if (err < 0)
+ fprintf(stderr, "failed to run nftables command\n");
+
+ nft_ctx_free(ctx);
+
+ return EXIT_SUCCESS;
+}
diff --git a/include/Makefile.am b/include/Makefile.am
index b997f46b..ff56dfe4 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -8,6 +8,7 @@ noinst_HEADERS = cli.h \
expression.h \
fib.h \
hash.h \
+ intervals.h \
ipopt.h \
json.h \
mini-gmp.h \
@@ -16,7 +17,6 @@ noinst_HEADERS = cli.h \
mnl.h \
nftables.h \
payload.h \
- rbtree.h \
tcpopt.h \
statement.h \
ct.h \
diff --git a/include/cache.h b/include/cache.h
index 70aaf735..5bf78fe0 100644
--- a/include/cache.h
+++ b/include/cache.h
@@ -3,6 +3,8 @@
#include <string.h>
+struct handle;
+
enum cache_level_bits {
NFT_CACHE_TABLE_BIT = (1 << 0),
NFT_CACHE_CHAIN_BIT = (1 << 1),
@@ -32,18 +34,46 @@ enum cache_level_flags {
NFT_CACHE_CHAIN_BIT |
NFT_CACHE_RULE_BIT,
NFT_CACHE_FULL = __NFT_CACHE_MAX_BIT - 1,
+ NFT_CACHE_TERSE = (1 << 27),
NFT_CACHE_SETELEM_MAYBE = (1 << 28),
NFT_CACHE_REFRESH = (1 << 29),
NFT_CACHE_UPDATE = (1 << 30),
NFT_CACHE_FLUSHED = (1 << 31),
};
+struct nft_filter_obj {
+ struct list_head list;
+ uint32_t family;
+ const char *table;
+ const char *set;
+};
+
+#define NFT_CACHE_HSIZE 8192
+
+struct nft_cache_filter {
+ struct {
+ uint32_t family;
+ const char *table;
+ const char *chain;
+ const char *set;
+ const char *ft;
+ uint64_t rule_handle;
+ } list;
+
+ struct {
+ struct list_head head;
+ } obj[NFT_CACHE_HSIZE];
+};
+
struct nft_cache;
enum cmd_ops;
-unsigned int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds);
+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,
- struct list_head *msgs);
+ struct list_head *msgs,
+ const struct nft_cache_filter *filter);
bool nft_cache_needs_update(struct nft_cache *cache);
void nft_cache_release(struct nft_cache *cache);
@@ -57,7 +87,8 @@ static inline uint32_t djb_hash(const char *key)
return hash;
}
-#define NFT_CACHE_HSIZE 8192
+struct nft_cache_filter *nft_cache_filter_init(void);
+void nft_cache_filter_fini(struct nft_cache_filter *filter);
struct table;
struct chain;
@@ -107,4 +138,11 @@ struct nft_cache {
uint32_t flags;
};
+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/datatype.h b/include/datatype.h
index 448be57f..73f38f66 100644
--- a/include/datatype.h
+++ b/include/datatype.h
@@ -1,6 +1,7 @@
#ifndef NFTABLES_DATATYPE_H
#define NFTABLES_DATATYPE_H
+#include <stdbool.h>
#include <json.h>
/**
@@ -176,6 +177,7 @@ extern const struct datatype *datatype_lookup_byname(const char *name);
extern struct datatype *datatype_get(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 parse_ctx {
struct symbol_tables *tbl;
@@ -254,6 +256,7 @@ extern const struct datatype verdict_type;
extern const struct datatype nfproto_type;
extern const struct datatype bitmask_type;
extern const struct datatype integer_type;
+extern const struct datatype xinteger_type;
extern const struct datatype string_type;
extern const struct datatype lladdr_type;
extern const struct datatype ipaddr_type;
@@ -309,6 +312,10 @@ extern struct error_record *rate_parse(const struct location *loc,
extern struct error_record *data_unit_parse(const struct location *loc,
const char *str, uint64_t *rate);
+struct limit_rate {
+ uint64_t rate, unit;
+};
+
extern void expr_chain_export(const struct expr *e, char *chain);
#endif /* NFTABLES_DATATYPE_H */
diff --git a/include/erec.h b/include/erec.h
index 79a16290..c17f5def 100644
--- a/include/erec.h
+++ b/include/erec.h
@@ -76,4 +76,9 @@ extern int __fmtstring(4, 5) __stmt_binary_error(struct eval_ctx *ctx,
#define stmt_binary_error(ctx, s1, s2, fmt, args...) \
__stmt_binary_error(ctx, &(s1)->location, &(s2)->location, fmt, ## args)
+void print_location(FILE *f, const struct input_descriptor *indesc,
+ const struct location *loc);
+const char *line_location(const struct input_descriptor *indesc,
+ const struct location *loc, char *buf, size_t bufsiz);
+
#endif /* NFTABLES_EREC_H */
diff --git a/include/expression.h b/include/expression.h
index 742fcdd7..1f58a68c 100644
--- a/include/expression.h
+++ b/include/expression.h
@@ -41,6 +41,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 +77,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,
@@ -120,6 +125,7 @@ struct expr_ctx {
enum byteorder byteorder;
unsigned int len;
unsigned int maxval;
+ const struct expr *key;
};
static inline void __expr_set_context(struct expr_ctx *ctx,
@@ -131,6 +137,7 @@ static inline void __expr_set_context(struct expr_ctx *ctx,
ctx->byteorder = byteorder;
ctx->len = len;
ctx->maxval = maxval;
+ ctx->key = NULL;
}
static inline void expr_set_context(struct expr_ctx *ctx,
@@ -190,6 +197,7 @@ const struct expr_ops *expr_ops_by_type(enum expr_types etype);
* @EXPR_F_INTERVAL_END: set member ends an open interval
* @EXPR_F_BOOLEAN: expression is boolean (set by relational expr on LHS)
* @EXPR_F_INTERVAL: expression describes a interval
+ * @EXPR_F_KERNEL: expression resides in the kernel
*/
enum expr_flags {
EXPR_F_CONSTANT = 0x1,
@@ -198,6 +206,8 @@ enum expr_flags {
EXPR_F_INTERVAL_END = 0x8,
EXPR_F_BOOLEAN = 0x10,
EXPR_F_INTERVAL = 0x20,
+ EXPR_F_KERNEL = 0x40,
+ EXPR_F_REMOVE = 0x80,
};
#include <payload.h>
@@ -238,6 +248,7 @@ struct expr {
enum expr_types etype:8;
enum ops op:8;
unsigned int len;
+ struct cmd *cmd;
union {
struct {
@@ -305,6 +316,7 @@ struct expr {
/* EXPR_PAYLOAD */
const struct proto_desc *desc;
const struct proto_hdr_template *tmpl;
+ const struct proto_desc *inner_desc;
enum proto_bases base;
unsigned int offset;
bool is_raw;
@@ -323,6 +335,7 @@ struct expr {
/* EXPR_META */
enum nft_meta_keys key;
enum proto_bases base;
+ const struct proto_desc *inner_desc;
} meta;
struct {
/* SOCKET */
@@ -475,6 +488,7 @@ extern struct expr *compound_expr_alloc(const struct location *loc,
extern void compound_expr_add(struct expr *compound, struct expr *expr);
extern void compound_expr_remove(struct expr *compound, struct expr *expr);
extern void list_expr_sort(struct list_head *head);
+extern void list_splice_sorted(struct list_head *list, struct list_head *head);
extern struct expr *concat_expr_alloc(const struct location *loc);
@@ -482,10 +496,6 @@ extern struct expr *list_expr_alloc(const struct location *loc);
extern struct expr *set_expr_alloc(const struct location *loc,
const struct set *set);
-extern int set_to_intervals(struct list_head *msgs, struct set *set,
- struct expr *init, bool add,
- unsigned int debug_mask, bool merge,
- struct output_ctx *octx);
extern void concat_range_aggregate(struct expr *set);
extern void interval_map_decompose(struct expr *set);
diff --git a/include/intervals.h b/include/intervals.h
new file mode 100644
index 00000000..964804b1
--- /dev/null
+++ b/include/intervals.h
@@ -0,0 +1,12 @@
+#ifndef NFTABLES_INTERVALS_H
+#define NFTABLES_INTERVALS_H
+
+void set_to_range(struct expr *init);
+int set_automerge(struct list_head *msgs, struct cmd *cmd, struct set *set,
+ struct expr *init, unsigned int debug_mask);
+int set_delete(struct list_head *msgs, struct cmd *cmd, struct set *set,
+ struct expr *init, unsigned int debug_mask);
+int set_overlap(struct list_head *msgs, struct set *set, struct expr *init);
+int set_to_intervals(const struct set *set, struct expr *init, bool add);
+
+#endif
diff --git a/include/ipopt.h b/include/ipopt.h
index d8d48066..03420dc6 100644
--- a/include/ipopt.h
+++ b/include/ipopt.h
@@ -6,7 +6,7 @@
#include <statement.h>
extern struct expr *ipopt_expr_alloc(const struct location *loc,
- uint8_t type, uint8_t field, uint8_t ptr);
+ uint8_t type, uint8_t field);
extern void ipopt_init_raw(struct expr *expr, uint8_t type,
unsigned int offset, unsigned int len,
diff --git a/include/json.h b/include/json.h
index 3db9f278..f691678d 100644
--- a/include/json.h
+++ b/include/json.h
@@ -69,6 +69,7 @@ json_t *uid_type_json(const struct expr *expr, struct output_ctx *octx);
json_t *gid_type_json(const struct expr *expr, struct output_ctx *octx);
json_t *expr_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
+json_t *flow_offload_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *payload_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *exthdr_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *quota_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
@@ -90,6 +91,8 @@ json_t *verdict_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *connlimit_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *tproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
json_t *synproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
+json_t *optstrip_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
+json_t *xt_stmt_json(const struct stmt *stmt, struct output_ctx *octx);
int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd);
@@ -169,6 +172,7 @@ EXPR_PRINT_STUB(uid_type)
EXPR_PRINT_STUB(gid_type)
STMT_PRINT_STUB(expr)
+STMT_PRINT_STUB(flow_offload)
STMT_PRINT_STUB(payload)
STMT_PRINT_STUB(exthdr)
STMT_PRINT_STUB(quota)
@@ -190,6 +194,8 @@ STMT_PRINT_STUB(verdict)
STMT_PRINT_STUB(connlimit)
STMT_PRINT_STUB(tproxy)
STMT_PRINT_STUB(synproxy)
+STMT_PRINT_STUB(optstrip)
+STMT_PRINT_STUB(xt)
#undef STMT_PRINT_STUB
#undef EXPR_PRINT_STUB
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
index feb6287c..9e078880 100644
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -54,6 +54,7 @@ enum nf_inet_hooks {
enum nf_dev_hooks {
NF_NETDEV_INGRESS,
+ NF_NETDEV_EGRESS,
NF_NETDEV_NUMHOOKS
};
diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
index 75df968d..ff677f3a 100644
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/linux/netfilter/nf_tables.h
@@ -97,6 +97,14 @@ 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)
*/
enum nf_tables_msg_types {
NFT_MSG_NEWTABLE,
@@ -124,6 +132,14 @@ 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_MAX,
};
@@ -164,7 +180,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 +192,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 +202,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 +769,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 +796,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
*
@@ -891,7 +941,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 +969,7 @@ enum nft_meta_keys {
NFT_META_TIME_HOUR,
NFT_META_SDIF,
NFT_META_SDIFNAME,
+ __NFT_META_IIFTYPE,
};
/**
@@ -1013,6 +1065,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 +1082,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 +1243,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/mnl.h b/include/mnl.h
index 68ec80cd..c0676691 100644
--- a/include/mnl.h
+++ b/include/mnl.h
@@ -33,8 +33,10 @@ int mnl_nft_rule_add(struct netlink_ctx *ctx, struct cmd *cmd,
int mnl_nft_rule_del(struct netlink_ctx *ctx, struct cmd *cmd);
int mnl_nft_rule_replace(struct netlink_ctx *ctx, struct cmd *cmd);
-struct nftnl_rule_list *mnl_nft_rule_dump(struct netlink_ctx *ctx,
- int family);
+struct nftnl_rule_list *mnl_nft_rule_dump(struct netlink_ctx *ctx, int family,
+ const char *table, const char *chain,
+ uint64_t rule_handle,
+ bool dump, bool reset);
int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags);
@@ -43,25 +45,28 @@ int mnl_nft_chain_rename(struct netlink_ctx *ctx, const struct cmd *cmd,
const struct chain *chain);
struct nftnl_chain_list *mnl_nft_chain_dump(struct netlink_ctx *ctx,
- int family);
+ int family, const char *table,
+ const char *chain);
int mnl_nft_table_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags);
int mnl_nft_table_del(struct netlink_ctx *ctx, struct cmd *cmd);
struct nftnl_table_list *mnl_nft_table_dump(struct netlink_ctx *ctx,
- int family);
+ int family, const char *table);
int mnl_nft_set_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags);
int mnl_nft_set_del(struct netlink_ctx *ctx, struct cmd *cmd);
struct nftnl_set_list *mnl_nft_set_dump(struct netlink_ctx *ctx, int family,
- const char *table);
+ const char *table, const char *set);
-int mnl_nft_setelem_add(struct netlink_ctx *ctx, const struct set *set,
- const struct expr *expr, unsigned int flags);
-int mnl_nft_setelem_del(struct netlink_ctx *ctx, const struct cmd *cmd);
+int mnl_nft_setelem_add(struct netlink_ctx *ctx, struct cmd *cmd,
+ const struct set *set, const struct expr *expr,
+ unsigned int flags);
+int mnl_nft_setelem_del(struct netlink_ctx *ctx, struct cmd *cmd,
+ const struct handle *h, const struct expr *init);
int mnl_nft_setelem_flush(struct netlink_ctx *ctx, const struct cmd *cmd);
int mnl_nft_setelem_get(struct netlink_ctx *ctx, struct nftnl_set *nls);
struct nftnl_set *mnl_nft_setelem_get_one(struct netlink_ctx *ctx,
@@ -76,7 +81,8 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, struct cmd *cmd,
int mnl_nft_obj_del(struct netlink_ctx *ctx, struct cmd *cmd, int type);
struct nftnl_flowtable_list *
-mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family, const char *table);
+mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family,
+ const char *table, const char *ft);
int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd,
unsigned int flags);
diff --git a/include/netlink.h b/include/netlink.h
index 2467ff82..0d97f71c 100644
--- a/include/netlink.h
+++ b/include/netlink.h
@@ -39,12 +39,28 @@ struct netlink_parse_ctx {
struct stmt *stmt;
struct expr *registers[MAX_REGS + 1];
unsigned int debug_mask;
+ struct netlink_ctx *nlctx;
+ bool inner;
+ uint8_t inner_reg;
};
-struct rule_pp_ctx {
+
+#define RULE_PP_IN_CONCATENATION (1 << 0)
+#define RULE_PP_IN_SET_ELEM (1 << 1)
+
+#define RULE_PP_REMOVE_OP_AND (RULE_PP_IN_CONCATENATION | \
+ RULE_PP_IN_SET_ELEM)
+
+struct dl_proto_ctx {
struct proto_ctx pctx;
struct payload_dep_ctx pdctx;
+};
+
+struct rule_pp_ctx {
+ struct dl_proto_ctx _dl[2];
+ struct dl_proto_ctx *dl;
struct stmt *stmt;
+ unsigned int flags;
};
extern const struct input_descriptor indesc_netlink;
@@ -72,6 +88,8 @@ struct netlink_ctx {
extern struct nftnl_expr *alloc_nft_expr(const char *name);
extern void alloc_setelem_cache(const struct expr *set, struct nftnl_set *nls);
+struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
+ const struct expr *expr);
extern struct nftnl_table *netlink_table_alloc(const struct nlmsghdr *nlh);
extern struct nftnl_chain *netlink_chain_alloc(const struct nlmsghdr *nlh);
@@ -122,8 +140,6 @@ extern struct expr *netlink_alloc_data(const struct location *loc,
const struct nft_data_delinearize *nld,
enum nft_registers dreg);
-extern int netlink_list_rules(struct netlink_ctx *ctx, const struct handle *h);
-
struct netlink_linearize_ctx;
extern void netlink_linearize_rule(struct netlink_ctx *ctx,
const struct rule *rule,
@@ -135,7 +151,8 @@ extern int netlink_list_chains(struct netlink_ctx *ctx, const struct handle *h);
extern struct chain *netlink_delinearize_chain(struct netlink_ctx *ctx,
const struct nftnl_chain *nlc);
-extern int netlink_list_tables(struct netlink_ctx *ctx, const struct handle *h);
+extern int netlink_list_tables(struct netlink_ctx *ctx, const struct handle *h,
+ const struct nft_cache_filter *filter);
extern struct table *netlink_delinearize_table(struct netlink_ctx *ctx,
const struct nftnl_table *nlt);
@@ -166,6 +183,9 @@ extern int netlink_list_flowtables(struct netlink_ctx *ctx,
extern struct flowtable *netlink_delinearize_flowtable(struct netlink_ctx *ctx,
struct nftnl_flowtable *nlo);
+extern int netlink_reset_rules(struct netlink_ctx *ctx, const struct cmd *cmd,
+ bool dump);
+
extern void netlink_dump_chain(const struct nftnl_chain *nlc,
struct netlink_ctx *ctx);
extern void netlink_dump_rule(const struct nftnl_rule *nlr,
@@ -236,4 +256,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/nftables.h b/include/nftables.h
index 7b633905..d49eb579 100644
--- a/include/nftables.h
+++ b/include/nftables.h
@@ -123,11 +123,13 @@ struct nft_ctx {
bool check;
struct nft_cache cache;
uint32_t flags;
+ uint32_t optimize_flags;
struct parser_state *state;
void *scanner;
struct scope *top_scope;
void *json_root;
json_t *json_echo;
+ const char *stdin_buf;
};
enum nftables_exit_codes {
@@ -175,6 +177,7 @@ enum input_descriptor_types {
INDESC_FILE,
INDESC_CLI,
INDESC_NETLINK,
+ INDESC_STDIN,
};
/**
@@ -222,6 +225,8 @@ int nft_print(struct output_ctx *octx, const char *fmt, ...)
int nft_gmp_print(struct output_ctx *octx, const char *fmt, ...)
__attribute__((format(printf, 2, 0)));
+int nft_optimize(struct nft_ctx *nft, struct list_head *cmds);
+
#define __NFT_OUTPUT_NOTSUPP UINT_MAX
#endif /* NFTABLES_NFTABLES_H */
diff --git a/include/nftables/libnftables.h b/include/nftables/libnftables.h
index 957b5fbe..85e08c9b 100644
--- a/include/nftables/libnftables.h
+++ b/include/nftables/libnftables.h
@@ -41,6 +41,13 @@ void nft_ctx_free(struct nft_ctx *ctx);
bool nft_ctx_get_dry_run(struct nft_ctx *ctx);
void nft_ctx_set_dry_run(struct nft_ctx *ctx, bool dry);
+enum nft_optimize_flags {
+ NFT_OPTIMIZE_ENABLED = 0x1,
+};
+
+uint32_t nft_ctx_get_optimize(struct nft_ctx *ctx);
+void nft_ctx_set_optimize(struct nft_ctx *ctx, uint32_t flags);
+
enum {
NFT_CTX_OUTPUT_REVERSEDNS = (1 << 0),
NFT_CTX_OUTPUT_SERVICE = (1 << 1),
diff --git a/include/parser.h b/include/parser.h
index e8635b4c..1bd490f0 100644
--- a/include/parser.h
+++ b/include/parser.h
@@ -22,36 +22,71 @@ struct parser_state {
struct scope *scopes[SCOPE_NEST_MAX];
unsigned int scope;
+ bool scope_err;
unsigned int flex_state_pop;
unsigned int startcond_type;
struct list_head *cmds;
+ unsigned int *startcond_active;
};
enum startcond_type {
PARSER_SC_BEGIN,
PARSER_SC_ARP,
+ PARSER_SC_AT,
PARSER_SC_CT,
PARSER_SC_COUNTER,
PARSER_SC_ETH,
+ PARSER_SC_GRE,
+ PARSER_SC_ICMP,
+ PARSER_SC_IGMP,
PARSER_SC_IP,
PARSER_SC_IP6,
PARSER_SC_LIMIT,
+ PARSER_SC_META,
+ PARSER_SC_POLICY,
PARSER_SC_QUOTA,
PARSER_SC_SCTP,
PARSER_SC_SECMARK,
+ PARSER_SC_TCP,
+ PARSER_SC_TYPE,
PARSER_SC_VLAN,
+ PARSER_SC_XT,
+ PARSER_SC_CMD_EXPORT,
+ PARSER_SC_CMD_IMPORT,
PARSER_SC_CMD_LIST,
+ PARSER_SC_CMD_MONITOR,
+ PARSER_SC_CMD_RESET,
+ PARSER_SC_EXPR_AH,
+ PARSER_SC_EXPR_COMP,
+ PARSER_SC_EXPR_DCCP,
+ PARSER_SC_EXPR_DST,
+ PARSER_SC_EXPR_ESP,
PARSER_SC_EXPR_FIB,
+ PARSER_SC_EXPR_FRAG,
PARSER_SC_EXPR_HASH,
+ PARSER_SC_EXPR_HBH,
PARSER_SC_EXPR_IPSEC,
+ PARSER_SC_EXPR_MH,
PARSER_SC_EXPR_NUMGEN,
+ PARSER_SC_EXPR_OSF,
PARSER_SC_EXPR_QUEUE,
PARSER_SC_EXPR_RT,
PARSER_SC_EXPR_SCTP_CHUNK,
PARSER_SC_EXPR_SOCKET,
+ PARSER_SC_EXPR_TH,
+ PARSER_SC_EXPR_UDP,
+ PARSER_SC_EXPR_UDPLITE,
+ PARSER_SC_STMT_DUP,
+ PARSER_SC_STMT_FWD,
PARSER_SC_STMT_LOG,
+ PARSER_SC_STMT_NAT,
+ PARSER_SC_STMT_REJECT,
+ PARSER_SC_STMT_SYNPROXY,
+ PARSER_SC_STMT_TPROXY,
+
+ __SC_MAX
};
struct mnl_socket;
diff --git a/include/payload.h b/include/payload.h
index 8bc3fb9a..08e45f7f 100644
--- a/include/payload.h
+++ b/include/payload.h
@@ -15,6 +15,8 @@ struct eval_ctx;
struct stmt;
extern int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
struct stmt **res);
+int payload_gen_inner_dependency(struct eval_ctx *ctx, const struct expr *expr,
+ struct stmt **res);
extern int payload_gen_icmp_dependency(struct eval_ctx *ctx,
const struct expr *expr,
struct stmt **res);
@@ -25,16 +27,14 @@ extern int exthdr_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
/**
* struct payload_dep_ctx - payload protocol dependency tracking
*
- * @pbase: protocol base of last dependency match
* @icmp_type: extra info for icmp(6) decoding
- * @pdep: last dependency match
* @prev: previous statement
+ * @pdeps: last dependency match per protocol layer
*/
struct payload_dep_ctx {
- enum proto_bases pbase:8;
- uint8_t icmp_type;
- struct stmt *pdep;
- struct stmt *prev;
+ uint8_t icmp_type;
+ struct stmt *prev;
+ struct stmt *pdeps[PROTO_BASE_MAX + 1];
};
extern bool payload_is_known(const struct expr *expr);
@@ -47,7 +47,10 @@ extern void payload_dependency_store(struct payload_dep_ctx *ctx,
enum proto_bases base);
extern bool payload_dependency_exists(const struct payload_dep_ctx *ctx,
enum proto_bases base);
-extern void payload_dependency_release(struct payload_dep_ctx *ctx);
+extern struct expr *payload_dependency_get(struct payload_dep_ctx *ctx,
+ enum proto_bases base);
+extern void payload_dependency_release(struct payload_dep_ctx *ctx,
+ enum proto_bases base);
extern void payload_dependency_kill(struct payload_dep_ctx *ctx,
struct expr *expr, unsigned int family);
extern void exthdr_dependency_kill(struct payload_dep_ctx *ctx,
@@ -66,4 +69,6 @@ extern void payload_expr_complete(struct expr *expr,
bool payload_expr_cmp(const struct expr *e1, const struct expr *e2);
+const struct proto_desc *find_proto_desc(const struct nftnl_udata *ud);
+
#endif /* NFTABLES_PAYLOAD_H */
diff --git a/include/proto.h b/include/proto.h
index 580e4090..3a20ff8c 100644
--- a/include/proto.h
+++ b/include/proto.h
@@ -18,6 +18,7 @@ enum proto_bases {
PROTO_BASE_LL_HDR,
PROTO_BASE_NETWORK_HDR,
PROTO_BASE_TRANSPORT_HDR,
+ PROTO_BASE_INNER_HDR,
__PROTO_BASE_MAX
};
#define PROTO_BASE_MAX (__PROTO_BASE_MAX - 1)
@@ -95,6 +96,10 @@ enum proto_desc_id {
PROTO_DESC_ARP,
PROTO_DESC_VLAN,
PROTO_DESC_ETHER,
+ PROTO_DESC_VXLAN,
+ PROTO_DESC_GENEVE,
+ PROTO_DESC_GRE,
+ PROTO_DESC_GRETAP,
__PROTO_DESC_MAX
};
#define PROTO_DESC_MAX (__PROTO_DESC_MAX - 1)
@@ -130,7 +135,11 @@ struct proto_desc {
uint32_t filter;
} format;
unsigned int pseudohdr[PROTO_HDRS_MAX];
-
+ struct {
+ uint32_t hdrsize;
+ uint32_t flags;
+ enum nft_inner_type type;
+ } inner;
};
#define PROTO_LINK(__num, __desc) { .num = (__num), .desc = (__desc), }
@@ -184,6 +193,7 @@ extern const struct proto_desc *proto_dev_desc(uint16_t type);
struct proto_ctx {
unsigned int debug_mask;
uint8_t family;
+ bool inner;
union {
struct {
uint8_t type;
@@ -192,17 +202,18 @@ struct proto_ctx {
struct {
struct location location;
const struct proto_desc *desc;
- unsigned int offset;
struct {
struct location location;
const struct proto_desc *desc;
} protos[PROTO_CTX_NUM_PROTOS];
unsigned int num_protos;
} protocol[PROTO_BASE_MAX + 1];
+ const struct proto_desc *stacked_ll[PROTO_CTX_NUM_PROTOS];
+ uint8_t stacked_ll_count;
};
extern void proto_ctx_init(struct proto_ctx *ctx, unsigned int family,
- unsigned int debug_mask);
+ unsigned int debug_mask, bool inner);
extern void proto_ctx_update(struct proto_ctx *ctx, enum proto_bases base,
const struct location *loc,
const struct proto_desc *desc);
@@ -214,6 +225,8 @@ extern const struct proto_desc *proto_find_upper(const struct proto_desc *base,
unsigned int num);
extern int proto_find_num(const struct proto_desc *base,
const struct proto_desc *desc);
+const struct proto_desc *proto_find_inner(uint32_t type, uint32_t hdrsize,
+ uint32_t flags);
extern const struct proto_desc *proto_find_desc(enum proto_desc_id desc_id);
@@ -261,6 +274,7 @@ enum ip_hdr_fields {
IPHDR_SADDR,
IPHDR_DADDR,
};
+#define IPHDR_MAX IPHDR_DADDR
enum icmp_hdr_fields {
ICMPHDR_INVALID,
@@ -374,6 +388,45 @@ enum th_hdr_fields {
THDR_DPORT,
};
+struct vxlanhdr {
+ uint32_t vx_flags;
+ uint32_t vx_vni;
+};
+
+enum vxlan_hdr_fields {
+ VXLANHDR_INVALID,
+ VXLANHDR_VNI,
+ VXLANHDR_FLAGS,
+};
+
+struct gnvhdr {
+ uint16_t flags;
+ uint16_t type;
+ uint32_t vni;
+};
+enum geneve_hdr_fields {
+ GNVHDR_INVALID,
+ GNVHDR_VNI,
+ GNVHDR_TYPE,
+};
+
+struct grehdr {
+ uint16_t flags;
+ uint16_t protocol;
+};
+
+enum gre_hdr_fields {
+ GREHDR_INVALID,
+ GREHDR_VERSION,
+ GREHDR_FLAGS,
+ GREHDR_PROTOCOL,
+};
+
+extern const struct proto_desc proto_vxlan;
+extern const struct proto_desc proto_geneve;
+extern const struct proto_desc proto_gre;
+extern const struct proto_desc proto_gretap;
+
extern const struct proto_desc proto_icmp;
extern const struct proto_desc proto_igmp;
extern const struct proto_desc proto_ah;
@@ -411,4 +464,7 @@ extern const struct datatype icmp6_type_type;
extern const struct datatype dscp_type;
extern const struct datatype ecn_type;
+struct eval_ctx;
+struct proto_ctx *eval_proto_ctx(struct eval_ctx *ctx);
+
#endif /* NFTABLES_PROTO_H */
diff --git a/include/rbtree.h b/include/rbtree.h
deleted file mode 100644
index ac65283f..00000000
--- a/include/rbtree.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Red Black Trees
- * (C) 1999 Andrea Arcangeli <andrea@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#ifndef NFTABLES_RBTREE_H
-#define NFTABLES_RBTREE_H
-
-#include <stddef.h>
-
-struct rb_node
-{
- unsigned long rb_parent_color;
-#define RB_RED 0
-#define RB_BLACK 1
- struct rb_node *rb_right;
- struct rb_node *rb_left;
-};
-
-struct rb_root
-{
- struct rb_node *rb_node;
-};
-
-#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3))
-#define rb_color(r) ((r)->rb_parent_color & 1)
-#define rb_is_red(r) (!rb_color(r))
-#define rb_is_black(r) rb_color(r)
-#define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0)
-#define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0)
-
-static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p)
-{
- rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p;
-}
-static inline void rb_set_color(struct rb_node *rb, int color)
-{
- rb->rb_parent_color = (rb->rb_parent_color & ~1) | color;
-}
-
-#define RB_ROOT (struct rb_root) { NULL, }
-#define rb_entry(ptr, type, member) container_of(ptr, type, member)
-
-#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL)
-#define RB_EMPTY_NODE(node) (rb_parent(node) == node)
-#define RB_CLEAR_NODE(node) (rb_set_parent(node, node))
-
-extern void rb_insert_color(struct rb_node *, struct rb_root *);
-extern void rb_erase(struct rb_node *, struct rb_root *);
-
-/* Find logical next and previous nodes in a tree */
-extern struct rb_node *rb_next(struct rb_node *);
-extern struct rb_node *rb_prev(struct rb_node *);
-extern struct rb_node *rb_first(struct rb_root *);
-extern struct rb_node *rb_last(struct rb_root *);
-
-/* Fast replacement of a single node without remove/rebalance/add/rebalance */
-extern void rb_replace_node(struct rb_node *victim, struct rb_node *new,
- struct rb_root *root);
-
-static inline void rb_link_node(struct rb_node * node, struct rb_node * parent,
- struct rb_node ** rb_link)
-{
- node->rb_parent_color = (unsigned long )parent;
- node->rb_left = node->rb_right = NULL;
-
- *rb_link = node;
-}
-
-#define rb_for_each_entry(pos, root, member) \
- for ((pos) = (root)->rb_node ? \
- rb_entry(rb_first(root), typeof(*pos), member) : NULL; \
- (pos) != NULL; \
- (pos) = rb_entry(rb_next(&(pos)->member), typeof(*pos), member))
-
-#define rb_for_each_entry_safe(pos, node, next, root, member) \
- for ((node) = rb_first(root); \
- (pos) = (node) ? rb_entry((node), typeof(*pos), member) : NULL, \
- (next) = (node) ? rb_next(node) : NULL, \
- (pos) != NULL; \
- (node) = (next))
-
-#endif /* NFTABLES_RBTREE_H */
diff --git a/include/rule.h b/include/rule.h
index be316956..0828d161 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -169,6 +169,7 @@ struct table {
unsigned int refcnt;
uint32_t owner;
const char *comment;
+ bool has_xt_stmts;
};
extern struct table *table_alloc(void);
@@ -311,7 +312,6 @@ void rule_stmt_append(struct rule *rule, struct stmt *stmt);
void rule_stmt_insert_at(struct rule *rule, struct stmt *nstmt,
struct stmt *stmt);
-
/**
* struct set - nftables set
*
@@ -325,6 +325,7 @@ void rule_stmt_insert_at(struct rule *rule, struct stmt *nstmt,
* @key: key expression (data type, length))
* @data: mapping data expression
* @objtype: mapping object type
+ * @existing_set: reference to existing set in the kernel
* @init: initializer
* @rg_cache: cached range element (left)
* @policy: set mechanism policy
@@ -346,6 +347,7 @@ struct set {
struct expr *key;
struct expr *data;
uint32_t objtype;
+ struct set *existing_set;
struct expr *init;
struct expr *rg_cache;
uint32_t policy;
@@ -561,6 +563,7 @@ void flowtable_print(const struct flowtable *n, struct output_ctx *octx);
* @CMD_EXPORT: export the ruleset in a given format
* @CMD_MONITOR: event listener
* @CMD_DESCRIBE: describe an expression
+ * @CMD_DESTROY: destroy object
*/
enum cmd_ops {
CMD_INVALID,
@@ -578,6 +581,7 @@ enum cmd_ops {
CMD_EXPORT,
CMD_MONITOR,
CMD_DESCRIBE,
+ CMD_DESTROY,
};
/**
@@ -618,6 +622,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,
@@ -680,6 +685,11 @@ void monitor_free(struct monitor *m);
#define NFT_NLATTR_LOC_MAX 32
+struct nlerr_loc {
+ uint16_t offset;
+ const struct location *location;
+};
+
/**
* struct cmd - command statement
*
@@ -699,6 +709,7 @@ struct cmd {
enum cmd_obj obj;
struct handle handle;
uint32_t seqnum;
+ struct list_head collapse_list;
union {
void *data;
struct expr *expr;
@@ -715,11 +726,9 @@ struct cmd {
struct markup *markup;
struct obj *object;
};
- struct {
- uint16_t offset;
- const struct location *location;
- } attr[NFT_NLATTR_LOC_MAX];
- int num_attrs;
+ struct nlerr_loc *attr;
+ uint32_t attr_array_len;
+ uint32_t num_attrs;
const void *arg;
};
@@ -727,6 +736,8 @@ 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);
@@ -761,7 +772,8 @@ struct eval_ctx {
struct set *set;
struct stmt *stmt;
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);
diff --git a/include/statement.h b/include/statement.h
index 06221040..e648fb13 100644
--- a/include/statement.h
+++ b/include/statement.h
@@ -145,6 +145,12 @@ struct nat_stmt {
extern struct stmt *nat_stmt_alloc(const struct location *loc,
enum nft_nat_etypes type);
+struct optstrip_stmt {
+ struct expr *expr;
+};
+
+extern struct stmt *optstrip_stmt_alloc(const struct location *loc, struct expr *e);
+
struct tproxy_stmt {
struct expr *addr;
struct expr *port;
@@ -249,8 +255,8 @@ enum nft_xt_type {
NFT_XT_MATCH = 0,
NFT_XT_TARGET,
NFT_XT_WATCHER,
- NFT_XT_MAX
};
+#define NFT_XT_MAX (NFT_XT_WATCHER + 1)
struct xtables_match;
struct xtables_target;
@@ -258,12 +264,11 @@ struct xtables_target;
struct xt_stmt {
const char *name;
enum nft_xt_type type;
+ uint32_t rev;
+ uint32_t family;
+ size_t infolen;
+ void *info;
uint32_t proto;
- union {
- struct xtables_match *match;
- struct xtables_target *target;
- };
- void *entry;
};
extern struct stmt *xt_stmt_alloc(const struct location *loc);
@@ -297,6 +302,7 @@ extern struct stmt *xt_stmt_alloc(const struct location *loc);
* @STMT_MAP: map statement
* @STMT_SYNPROXY: synproxy statement
* @STMT_CHAIN: chain statement
+ * @STMT_OPTSTRIP: optstrip statement
*/
enum stmt_types {
STMT_INVALID,
@@ -326,6 +332,7 @@ enum stmt_types {
STMT_MAP,
STMT_SYNPROXY,
STMT_CHAIN,
+ STMT_OPTSTRIP,
};
/**
@@ -380,6 +387,7 @@ struct stmt {
struct reject_stmt reject;
struct nat_stmt nat;
struct tproxy_stmt tproxy;
+ struct optstrip_stmt optstrip;
struct queue_stmt queue;
struct quota_stmt quota;
struct ct_stmt ct;
diff --git a/include/tcpopt.h b/include/tcpopt.h
index 667c8a77..3a0b8424 100644
--- a/include/tcpopt.h
+++ b/include/tcpopt.h
@@ -12,8 +12,8 @@ extern void tcpopt_init_raw(struct expr *expr, uint8_t type,
unsigned int offset, unsigned int len,
uint32_t flags);
-extern bool tcpopt_find_template(struct expr *expr, const struct expr *mask,
- unsigned int *shift);
+extern bool tcpopt_find_template(struct expr *expr, unsigned int offset,
+ unsigned int len);
/* TCP option numbers used on wire */
enum tcpopt_kind {
@@ -25,6 +25,9 @@ enum tcpopt_kind {
TCPOPT_KIND_SACK = 5,
TCPOPT_KIND_TIMESTAMP = 8,
TCPOPT_KIND_ECHO = 8,
+ TCPOPT_KIND_MD5SIG = 19,
+ TCPOPT_KIND_MPTCP = 30,
+ TCPOPT_KIND_FASTOPEN = 34,
__TCPOPT_KIND_MAX,
/* extra oob info, internal to nft */
@@ -71,6 +74,12 @@ enum tcpopt_hdr_field_sack {
TCPOPT_SACK_RIGHT3,
};
+enum tcpopt_hdr_mptcp_common {
+ TCPOPT_MPTCP_KIND,
+ TCPOPT_MPTCP_LENGTH,
+ TCPOPT_MPTCP_SUBTYPE,
+};
+
extern const struct exthdr_desc *tcpopt_protocols[__TCPOPT_KIND_MAX];
#endif /* NFTABLES_TCPOPT_H */
diff --git a/py/nftables.py b/py/nftables.py
index 2a0a1e89..6daeafc2 100644
--- a/py/nftables.py
+++ b/py/nftables.py
@@ -116,6 +116,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]
@@ -446,3 +471,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/src/Makefile.am b/src/Makefile.am
index 01c12c81..264d981e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -2,8 +2,6 @@ include $(top_srcdir)/Make_global.am
sbin_PROGRAMS = nft
-CLEANFILES = scanner.c parser_bison.c
-
AM_CPPFLAGS = -I$(top_srcdir)/include
AM_CPPFLAGS += -DDEFAULT_INCLUDE_PATH="\"${sysconfdir}\"" \
${LIBMNL_CFLAGS} ${LIBNFTNL_CFLAGS}
@@ -50,6 +48,7 @@ libnftables_la_SOURCES = \
exthdr.c \
fib.c \
hash.c \
+ intervals.c \
ipopt.c \
meta.c \
rt.c \
@@ -63,13 +62,13 @@ libnftables_la_SOURCES = \
monitor.c \
owner.c \
segtree.c \
- rbtree.c \
gmputil.c \
utils.c \
erec.c \
mnl.c \
iface.c \
mergesort.c \
+ optimize.c \
osf.c \
nfnl_osf.c \
tcpopt.c \
@@ -92,7 +91,7 @@ libparser_la_CFLAGS = ${AM_CFLAGS} \
libnftables_la_LIBADD = ${LIBMNL_LIBS} ${LIBNFTNL_LIBS} libparser.la
libnftables_la_LDFLAGS = -version-info ${libnftables_LIBVERSION} \
- --version-script=$(srcdir)/libnftables.map
+ -Wl,--version-script=$(srcdir)/libnftables.map
if BUILD_MINIGMP
noinst_LTLIBRARIES += libminigmp.la
diff --git a/src/cache.c b/src/cache.c
index 42e6b65c..38bc219a 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -16,10 +16,27 @@
#include <mnl.h>
#include <libnftnl/chain.h>
#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags)
{
+ struct set *set;
+
switch (cmd->obj) {
+ case CMD_OBJ_TABLE:
+ if (!cmd->table)
+ break;
+
+ flags |= NFT_CACHE_TABLE |
+ NFT_CACHE_CHAIN |
+ NFT_CACHE_SET |
+ NFT_CACHE_OBJECT |
+ NFT_CACHE_FLOWTABLE;
+ list_for_each_entry(set, &cmd->table->sets, list) {
+ if (set->automerge)
+ flags |= NFT_CACHE_SETELEM_MAYBE;
+ }
+ break;
case CMD_OBJ_CHAIN:
case CMD_OBJ_SET:
case CMD_OBJ_COUNTER:
@@ -86,13 +103,74 @@ static unsigned int evaluate_cache_get(struct cmd *cmd, unsigned int flags)
return flags;
}
-static unsigned int evaluate_cache_flush(struct cmd *cmd, unsigned int flags)
+struct nft_cache_filter *nft_cache_filter_init(void)
+{
+ struct nft_cache_filter *filter;
+ int i;
+
+ filter = xzalloc(sizeof(struct nft_cache_filter));
+ memset(&filter->list, 0, sizeof(filter->list));
+ for (i = 0; i < NFT_CACHE_HSIZE; i++)
+ init_list_head(&filter->obj[i].head);
+
+ return filter;
+}
+
+void nft_cache_filter_fini(struct nft_cache_filter *filter)
+{
+ int i;
+
+ for (i = 0; i < NFT_CACHE_HSIZE; i++) {
+ struct nft_filter_obj *obj, *next;
+
+ list_for_each_entry_safe(obj, next, &filter->obj[i].head, list)
+ xfree(obj);
+ }
+ xfree(filter);
+}
+
+static void cache_filter_add(struct nft_cache_filter *filter,
+ const struct cmd *cmd)
+{
+ struct nft_filter_obj *obj;
+ uint32_t hash;
+
+ obj = xmalloc(sizeof(struct nft_filter_obj));
+ obj->family = cmd->handle.family;
+ obj->table = cmd->handle.table.name;
+ obj->set = cmd->handle.set.name;
+
+ hash = djb_hash(cmd->handle.set.name) % NFT_CACHE_HSIZE;
+ list_add_tail(&obj->list, &filter->obj[hash].head);
+}
+
+static bool cache_filter_find(const struct nft_cache_filter *filter,
+ const struct handle *handle)
+{
+ struct nft_filter_obj *obj;
+ uint32_t hash;
+
+ hash = djb_hash(handle->set.name) % NFT_CACHE_HSIZE;
+
+ list_for_each_entry(obj, &filter->obj[hash].head, list) {
+ if (obj->family == handle->family &&
+ !strcmp(obj->table, handle->table.name) &&
+ !strcmp(obj->set, handle->set.name))
+ return true;
+ }
+
+ return false;
+}
+
+static unsigned int evaluate_cache_flush(struct cmd *cmd, unsigned int flags,
+ struct nft_cache_filter *filter)
{
switch (cmd->obj) {
case CMD_OBJ_SET:
case CMD_OBJ_MAP:
case CMD_OBJ_METER:
flags |= NFT_CACHE_SET;
+ cache_filter_add(filter, cmd);
break;
case CMD_OBJ_RULESET:
flags |= NFT_CACHE_FLUSHED;
@@ -117,12 +195,215 @@ static unsigned int evaluate_cache_rename(struct cmd *cmd, unsigned int flags)
return flags;
}
-unsigned int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds)
+static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd,
+ unsigned int flags,
+ struct nft_cache_filter *filter)
+{
+ switch (cmd->obj) {
+ case CMD_OBJ_TABLE:
+ if (filter && cmd->handle.table.name) {
+ filter->list.family = cmd->handle.family;
+ filter->list.table = cmd->handle.table.name;
+ }
+ flags |= NFT_CACHE_FULL;
+ break;
+ case CMD_OBJ_CHAIN:
+ if (filter && cmd->handle.chain.name) {
+ filter->list.family = cmd->handle.family;
+ filter->list.table = cmd->handle.table.name;
+ filter->list.chain = cmd->handle.chain.name;
+ }
+ flags |= NFT_CACHE_FULL;
+ break;
+ case CMD_OBJ_SET:
+ case CMD_OBJ_MAP:
+ if (filter && cmd->handle.table.name && cmd->handle.set.name) {
+ filter->list.family = cmd->handle.family;
+ filter->list.table = cmd->handle.table.name;
+ filter->list.set = cmd->handle.set.name;
+ }
+ if (filter->list.table && filter->list.set)
+ flags |= NFT_CACHE_TABLE | NFT_CACHE_SET | NFT_CACHE_SETELEM;
+ else if (nft_output_terse(&nft->output))
+ flags |= NFT_CACHE_FULL | NFT_CACHE_TERSE;
+ else
+ flags |= NFT_CACHE_FULL;
+ break;
+ case CMD_OBJ_CHAINS:
+ flags |= NFT_CACHE_TABLE | NFT_CACHE_CHAIN;
+ break;
+ case CMD_OBJ_SETS:
+ case CMD_OBJ_MAPS:
+ flags |= NFT_CACHE_TABLE | NFT_CACHE_SET;
+ break;
+ case CMD_OBJ_FLOWTABLE:
+ if (filter &&
+ cmd->handle.table.name &&
+ cmd->handle.flowtable.name) {
+ filter->list.family = cmd->handle.family;
+ filter->list.table = cmd->handle.table.name;
+ filter->list.ft = cmd->handle.flowtable.name;
+ }
+ /* fall through */
+ case CMD_OBJ_FLOWTABLES:
+ flags |= NFT_CACHE_TABLE | NFT_CACHE_FLOWTABLE;
+ break;
+ case CMD_OBJ_RULESET:
+ if (nft_output_terse(&nft->output))
+ flags |= NFT_CACHE_FULL | NFT_CACHE_TERSE;
+ else
+ flags |= NFT_CACHE_FULL;
+ break;
+ default:
+ flags |= NFT_CACHE_FULL;
+ break;
+ }
+ flags |= NFT_CACHE_REFRESH;
+
+ 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:
+ 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;
+ default:
+ flags |= NFT_CACHE_TABLE;
+ break;
+ }
+
+ return flags;
+}
+
+static int nft_handle_validate(const struct cmd *cmd, struct list_head *msgs)
+{
+ const struct handle *h = &cmd->handle;
+ const struct location *loc;
+
+ switch (cmd->obj) {
+ case CMD_OBJ_TABLE:
+ if (h->table.name &&
+ strlen(h->table.name) > NFT_NAME_MAXLEN) {
+ loc = &h->table.location;
+ goto err_name_too_long;
+ }
+ break;
+ case CMD_OBJ_RULE:
+ case CMD_OBJ_RULES:
+ case CMD_OBJ_CHAIN:
+ case CMD_OBJ_CHAINS:
+ if (h->table.name &&
+ strlen(h->table.name) > NFT_NAME_MAXLEN) {
+ loc = &h->table.location;
+ goto err_name_too_long;
+ }
+ if (h->chain.name &&
+ strlen(h->chain.name) > NFT_NAME_MAXLEN) {
+ loc = &h->chain.location;
+ goto err_name_too_long;
+ }
+ break;
+ case CMD_OBJ_ELEMENTS:
+ case CMD_OBJ_SET:
+ case CMD_OBJ_SETS:
+ case CMD_OBJ_MAP:
+ case CMD_OBJ_MAPS:
+ case CMD_OBJ_METER:
+ case CMD_OBJ_METERS:
+ if (h->table.name &&
+ strlen(h->table.name) > NFT_NAME_MAXLEN) {
+ loc = &h->table.location;
+ goto err_name_too_long;
+ }
+ if (h->set.name &&
+ strlen(h->set.name) > NFT_NAME_MAXLEN) {
+ loc = &h->set.location;
+ goto err_name_too_long;
+ }
+ break;
+ case CMD_OBJ_FLOWTABLE:
+ case CMD_OBJ_FLOWTABLES:
+ if (h->table.name &&
+ strlen(h->table.name) > NFT_NAME_MAXLEN) {
+ loc = &h->table.location;
+ goto err_name_too_long;
+ }
+ if (h->flowtable.name &&
+ strlen(h->flowtable.name) > NFT_NAME_MAXLEN) {
+ loc = &h->flowtable.location;
+ goto err_name_too_long;
+ }
+ break;
+ case CMD_OBJ_INVALID:
+ case CMD_OBJ_EXPR:
+ case CMD_OBJ_RULESET:
+ case CMD_OBJ_MARKUP:
+ case CMD_OBJ_MONITOR:
+ case CMD_OBJ_SETELEMS:
+ case CMD_OBJ_HOOKS:
+ break;
+ case CMD_OBJ_COUNTER:
+ case CMD_OBJ_COUNTERS:
+ case CMD_OBJ_QUOTA:
+ case CMD_OBJ_QUOTAS:
+ case CMD_OBJ_LIMIT:
+ case CMD_OBJ_LIMITS:
+ case CMD_OBJ_SECMARK:
+ case CMD_OBJ_SECMARKS:
+ case CMD_OBJ_SYNPROXY:
+ case CMD_OBJ_SYNPROXYS:
+ case CMD_OBJ_CT_HELPER:
+ case CMD_OBJ_CT_HELPERS:
+ case CMD_OBJ_CT_TIMEOUT:
+ case CMD_OBJ_CT_EXPECT:
+ if (h->table.name &&
+ strlen(h->table.name) > NFT_NAME_MAXLEN) {
+ loc = &h->table.location;
+ goto err_name_too_long;
+ }
+ if (h->obj.name &&
+ strlen(h->obj.name) > NFT_NAME_MAXLEN) {
+ loc = &h->obj.location;
+ goto err_name_too_long;
+ }
+ break;
+ }
+
+ return 0;
+
+err_name_too_long:
+ erec_queue(error(loc, "name too long, %d characters maximum allowed",
+ NFT_NAME_MAXLEN),
+ msgs);
+ return -1;
+}
+
+int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds,
+ struct list_head *msgs, struct nft_cache_filter *filter,
+ unsigned int *pflags)
{
unsigned int flags = NFT_CACHE_EMPTY;
struct cmd *cmd;
list_for_each_entry(cmd, cmds, list) {
+ if (nft_handle_validate(cmd, msgs) < 0)
+ return -1;
+
+ if (filter->list.table && cmd->op != CMD_LIST)
+ memset(&filter->list, 0, sizeof(filter->list));
+
switch (cmd->op) {
case CMD_ADD:
case CMD_INSERT:
@@ -135,6 +416,7 @@ unsigned int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds)
flags = NFT_CACHE_FULL;
break;
case CMD_DELETE:
+ case CMD_DESTROY:
flags |= NFT_CACHE_TABLE |
NFT_CACHE_CHAIN |
NFT_CACHE_SET |
@@ -147,30 +429,31 @@ unsigned int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds)
flags = evaluate_cache_get(cmd, flags);
break;
case CMD_RESET:
- flags |= NFT_CACHE_TABLE;
+ flags |= evaluate_cache_reset(cmd, flags, filter);
break;
case CMD_LIST:
- case CMD_EXPORT:
- flags |= NFT_CACHE_FULL | NFT_CACHE_REFRESH;
+ flags |= evaluate_cache_list(nft, cmd, flags, filter);
break;
case CMD_MONITOR:
flags |= NFT_CACHE_FULL;
break;
case CMD_FLUSH:
- flags = evaluate_cache_flush(cmd, flags);
+ flags = evaluate_cache_flush(cmd, flags, filter);
break;
case CMD_RENAME:
flags = evaluate_cache_rename(cmd, flags);
break;
case CMD_DESCRIBE:
case CMD_IMPORT:
+ case CMD_EXPORT:
break;
default:
break;
}
}
+ *pflags = flags;
- return flags;
+ return 0;
}
void table_cache_add(struct table *table, struct nft_cache *cache)
@@ -218,13 +501,13 @@ static int chain_cache_cb(struct nftnl_chain *nlc, void *arg)
struct chain *chain;
table_name = nftnl_chain_get_str(nlc, NFTNL_CHAIN_TABLE);
- chain_name = nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME);
family = nftnl_chain_get_u32(nlc, NFTNL_CHAIN_FAMILY);
- if (strcmp(table_name, ctx->table->handle.table.name) ||
- family != ctx->table->handle.family)
+ if (family != ctx->table->handle.family ||
+ strcmp(table_name, ctx->table->handle.table.name))
return 0;
+ chain_name = nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME);
hash = djb_hash(chain_name) % NFT_CACHE_HSIZE;
chain = netlink_delinearize_chain(ctx->nlctx, nlc);
@@ -252,12 +535,22 @@ static int chain_cache_init(struct netlink_ctx *ctx, struct table *table,
return 0;
}
-static struct nftnl_chain_list *chain_cache_dump(struct netlink_ctx *ctx,
- int *err)
+static struct nftnl_chain_list *
+chain_cache_dump(struct netlink_ctx *ctx,
+ const struct nft_cache_filter *filter, int *err)
{
struct nftnl_chain_list *chain_list;
+ const char *table = NULL;
+ const char *chain = NULL;
+ int family = NFPROTO_UNSPEC;
+
+ if (filter && filter->list.table && filter->list.chain) {
+ family = filter->list.family;
+ table = filter->list.table;
+ chain = filter->list.chain;
+ }
- chain_list = mnl_nft_chain_dump(ctx, AF_UNSPEC);
+ chain_list = mnl_nft_chain_dump(ctx, family, table, chain);
if (chain_list == NULL) {
if (errno == EINTR) {
*err = -1;
@@ -270,6 +563,21 @@ static struct nftnl_chain_list *chain_cache_dump(struct netlink_ctx *ctx,
return chain_list;
}
+void nft_chain_cache_update(struct netlink_ctx *ctx, struct table *table,
+ const char *chain)
+{
+ struct nftnl_chain_list *chain_list;
+
+ chain_list = mnl_nft_chain_dump(ctx, table->handle.family,
+ table->handle.table.name, chain);
+ if (!chain_list)
+ return;
+
+ chain_cache_init(ctx, table, chain_list);
+
+ nftnl_chain_list_free(chain_list);
+}
+
void chain_cache_add(struct chain *chain, struct table *table)
{
uint32_t hash;
@@ -297,6 +605,61 @@ struct chain *chain_cache_find(const struct table *table, const char *name)
return NULL;
}
+static int list_rule_cb(struct nftnl_rule *nlr, void *data)
+{
+ struct netlink_ctx *ctx = data;
+ const struct handle *h = ctx->data;
+ const char *table, *chain;
+ struct rule *rule;
+ uint32_t family;
+
+ family = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY);
+ table = nftnl_rule_get_str(nlr, NFTNL_RULE_TABLE);
+ chain = nftnl_rule_get_str(nlr, NFTNL_RULE_CHAIN);
+
+ if ((h->family != NFPROTO_UNSPEC && h->family != family) ||
+ (h->table.name && strcmp(table, h->table.name) != 0) ||
+ (h->chain.name && strcmp(chain, h->chain.name) != 0))
+ return 0;
+
+ netlink_dump_rule(nlr, ctx);
+ rule = netlink_delinearize_rule(ctx, nlr);
+ assert(rule);
+ list_add_tail(&rule->list, &ctx->list);
+
+ return 0;
+}
+
+int rule_cache_dump(struct netlink_ctx *ctx, const struct handle *h,
+ const struct nft_cache_filter *filter,
+ bool dump, bool reset)
+{
+ struct nftnl_rule_list *rule_cache;
+ const char *table = NULL;
+ const char *chain = NULL;
+ uint64_t rule_handle = 0;
+
+ if (filter) {
+ table = filter->list.table;
+ chain = filter->list.chain;
+ rule_handle = filter->list.rule_handle;
+ }
+
+ rule_cache = mnl_nft_rule_dump(ctx, h->family,
+ table, chain, rule_handle, dump, reset);
+ if (rule_cache == NULL) {
+ if (errno == EINTR)
+ return -1;
+
+ return 0;
+ }
+
+ ctx->data = h;
+ nftnl_rule_list_foreach(rule_cache, list_rule_cb, ctx);
+ nftnl_rule_list_free(rule_cache);
+ return 0;
+}
+
struct set_cache_dump_ctx {
struct netlink_ctx *nlctx;
struct table *table;
@@ -305,10 +668,19 @@ struct set_cache_dump_ctx {
static int set_cache_cb(struct nftnl_set *nls, void *arg)
{
struct set_cache_dump_ctx *ctx = arg;
+ const char *set_table;
const char *set_name;
+ uint32_t set_family;
struct set *set;
uint32_t hash;
+ set_table = nftnl_set_get_str(nls, NFTNL_SET_TABLE);
+ set_family = nftnl_set_get_u32(nls, NFTNL_SET_FAMILY);
+
+ if (set_family != ctx->table->handle.family ||
+ strcmp(set_table, ctx->table->handle.table.name))
+ return 0;
+
set = netlink_delinearize_set(ctx->nlctx, nls);
if (!set)
return -1;
@@ -317,6 +689,8 @@ static int set_cache_cb(struct nftnl_set *nls, void *arg)
hash = djb_hash(set_name) % NFT_CACHE_HSIZE;
cache_add(&set->cache, &ctx->table->set_cache, hash);
+ nftnl_set_list_del(nls);
+ nftnl_set_free(nls);
return 0;
}
@@ -327,19 +701,28 @@ static int set_cache_init(struct netlink_ctx *ctx, struct table *table,
.nlctx = ctx,
.table = table,
};
+
nftnl_set_list_foreach(set_list, set_cache_cb, &dump_ctx);
return 0;
}
-static struct nftnl_set_list *set_cache_dump(struct netlink_ctx *ctx,
- const struct table *table,
- int *err)
+static struct nftnl_set_list *
+set_cache_dump(struct netlink_ctx *ctx,
+ const struct nft_cache_filter *filter, int *err)
{
struct nftnl_set_list *set_list;
+ int family = NFPROTO_UNSPEC;
+ const char *table = NULL;
+ const char *set = NULL;
+
+ if (filter) {
+ family = filter->list.family;
+ table = filter->list.table;
+ set = filter->list.set;
+ }
- set_list = mnl_nft_set_dump(ctx, table->handle.family,
- table->handle.table.name);
+ set_list = mnl_nft_set_dump(ctx, family, table, set);
if (!set_list) {
if (errno == EINTR) {
*err = -1;
@@ -474,10 +857,19 @@ struct ft_cache_dump_ctx {
static int ft_cache_cb(struct nftnl_flowtable *nlf, void *arg)
{
struct ft_cache_dump_ctx *ctx = arg;
- const char *ft_name;
struct flowtable *ft;
+ const char *ft_table;
+ const char *ft_name;
+ uint32_t ft_family;
uint32_t hash;
+ ft_family = nftnl_flowtable_get_u32(nlf, NFTNL_FLOWTABLE_FAMILY);
+ ft_table = nftnl_flowtable_get_str(nlf, NFTNL_FLOWTABLE_TABLE);
+
+ if (ft_family != ctx->table->handle.family ||
+ strcmp(ft_table, ctx->table->handle.table.name))
+ return 0;
+
ft = netlink_delinearize_flowtable(ctx->nlctx, nlf);
if (!ft)
return -1;
@@ -486,6 +878,8 @@ static int ft_cache_cb(struct nftnl_flowtable *nlf, void *arg)
hash = djb_hash(ft_name) % NFT_CACHE_HSIZE;
cache_add(&ft->cache, &ctx->table->ft_cache, hash);
+ nftnl_flowtable_list_del(nlf);
+ nftnl_flowtable_free(nlf);
return 0;
}
@@ -501,13 +895,21 @@ static int ft_cache_init(struct netlink_ctx *ctx, struct table *table,
return 0;
}
-static struct nftnl_flowtable_list *ft_cache_dump(struct netlink_ctx *ctx,
- const struct table *table)
+static struct nftnl_flowtable_list *
+ft_cache_dump(struct netlink_ctx *ctx, const struct nft_cache_filter *filter)
{
struct nftnl_flowtable_list *ft_list;
+ int family = NFPROTO_UNSPEC;
+ const char *table = NULL;
+ const char *ft = NULL;
+
+ if (filter) {
+ family = filter->list.family;
+ table = filter->list.table;
+ ft = filter->list.ft;
+ }
- ft_list = mnl_nft_flowtable_dump(ctx, table->handle.family,
- table->handle.table.name);
+ ft_list = mnl_nft_flowtable_dump(ctx, family, table, ft);
if (!ft_list) {
if (errno == EINTR)
return NULL;
@@ -551,12 +953,13 @@ struct flowtable *ft_cache_find(const struct table *table, const char *name)
}
static int cache_init_tables(struct netlink_ctx *ctx, struct handle *h,
- struct nft_cache *cache)
+ struct nft_cache *cache,
+ const struct nft_cache_filter *filter)
{
struct table *table, *next;
int ret;
- ret = netlink_list_tables(ctx, h);
+ ret = netlink_list_tables(ctx, h, filter);
if (ret < 0)
return -1;
@@ -568,83 +971,127 @@ static int cache_init_tables(struct netlink_ctx *ctx, struct handle *h,
return 0;
}
-static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags)
+static int rule_init_cache(struct netlink_ctx *ctx, struct table *table,
+ const struct nft_cache_filter *filter)
+{
+ struct rule *rule, *nrule;
+ struct chain *chain;
+ int ret;
+
+ ret = rule_cache_dump(ctx, &table->handle, filter, true, false);
+
+ list_for_each_entry_safe(rule, nrule, &ctx->list, list) {
+ chain = chain_cache_find(table, rule->handle.chain.name);
+ if (!chain)
+ chain = chain_binding_lookup(table,
+ rule->handle.chain.name);
+ if (!chain)
+ goto err_ctx_list;
+
+ list_move_tail(&rule->list, &chain->rules);
+ }
+
+ return ret;
+
+err_ctx_list:
+ list_for_each_entry_safe(rule, nrule, &ctx->list, list) {
+ list_del(&rule->list);
+ rule_free(rule);
+ }
+ errno = EINTR;
+
+ return -1;
+}
+
+static int implicit_chain_cache(struct netlink_ctx *ctx, struct table *table,
+ const char *chain_name)
+{
+ struct nft_cache_filter filter;
+ struct chain *chain;
+ int ret = 0;
+
+ list_for_each_entry(chain, &table->chain_bindings, cache.list) {
+ filter.list.table = table->handle.table.name;
+ filter.list.chain = chain->handle.chain.name;
+ ret = rule_init_cache(ctx, table, &filter);
+ }
+
+ return ret;
+}
+
+static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags,
+ const struct nft_cache_filter *filter)
{
struct nftnl_flowtable_list *ft_list = NULL;
struct nftnl_chain_list *chain_list = NULL;
struct nftnl_set_list *set_list = NULL;
struct nftnl_obj_list *obj_list;
- struct rule *rule, *nrule;
struct table *table;
- struct chain *chain;
struct set *set;
int ret = 0;
if (flags & NFT_CACHE_CHAIN_BIT) {
- chain_list = chain_cache_dump(ctx, &ret);
+ chain_list = chain_cache_dump(ctx, filter, &ret);
if (!chain_list)
return -1;
}
+ if (flags & NFT_CACHE_SET_BIT) {
+ set_list = set_cache_dump(ctx, filter, &ret);
+ if (!set_list) {
+ ret = -1;
+ goto cache_fails;
+ }
+ }
+ if (flags & NFT_CACHE_FLOWTABLE_BIT) {
+ ft_list = ft_cache_dump(ctx, filter);
+ if (!ft_list) {
+ ret = -1;
+ goto cache_fails;
+ }
+ }
list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) {
if (flags & NFT_CACHE_SET_BIT) {
- set_list = set_cache_dump(ctx, table, &ret);
- if (!set_list) {
- ret = -1;
- goto cache_fails;
- }
ret = set_cache_init(ctx, table, set_list);
-
- nftnl_set_list_free(set_list);
-
- if (ret < 0) {
- ret = -1;
+ if (ret < 0)
goto cache_fails;
- }
}
if (flags & NFT_CACHE_SETELEM_BIT) {
list_for_each_entry(set, &table->set_cache.list, cache.list) {
+ if (cache_filter_find(filter, &set->handle))
+ continue;
+ if (!set_is_anonymous(set->flags) &&
+ flags & NFT_CACHE_TERSE)
+ continue;
+
ret = netlink_list_setelems(ctx, &set->handle,
set);
- if (ret < 0) {
- ret = -1;
+ if (ret < 0)
goto cache_fails;
- }
}
} else if (flags & NFT_CACHE_SETELEM_MAYBE) {
list_for_each_entry(set, &table->set_cache.list, cache.list) {
+ if (cache_filter_find(filter, &set->handle))
+ continue;
+
if (!set_is_non_concat_range(set))
continue;
ret = netlink_list_setelems(ctx, &set->handle,
set);
- if (ret < 0) {
- ret = -1;
+ if (ret < 0)
goto cache_fails;
- }
}
}
if (flags & NFT_CACHE_CHAIN_BIT) {
ret = chain_cache_init(ctx, table, chain_list);
- if (ret < 0) {
- ret = -1;
+ if (ret < 0)
goto cache_fails;
- }
}
if (flags & NFT_CACHE_FLOWTABLE_BIT) {
- ft_list = ft_cache_dump(ctx, table);
- if (!ft_list) {
- ret = -1;
- goto cache_fails;
- }
ret = ft_cache_init(ctx, table, ft_list);
-
- nftnl_flowtable_list_free(ft_list);
-
- if (ret < 0) {
- ret = -1;
+ if (ret < 0)
goto cache_fails;
- }
}
if (flags & NFT_CACHE_OBJECT_BIT) {
obj_list = obj_cache_dump(ctx, table);
@@ -656,41 +1103,37 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags)
nftnl_obj_list_free(obj_list);
- if (ret < 0) {
- ret = -1;
+ if (ret < 0)
goto cache_fails;
- }
}
if (flags & NFT_CACHE_RULE_BIT) {
- ret = netlink_list_rules(ctx, &table->handle);
- list_for_each_entry_safe(rule, nrule, &ctx->list, list) {
- chain = chain_cache_find(table, rule->handle.chain.name);
- if (!chain)
- chain = chain_binding_lookup(table,
- rule->handle.chain.name);
- if (!chain) {
- ret = -1;
- goto cache_fails;
- }
-
- list_move_tail(&rule->list, &chain->rules);
- }
- if (ret < 0) {
- ret = -1;
+ ret = rule_init_cache(ctx, table, filter);
+ if (ret < 0)
goto cache_fails;
+
+ if (filter && filter->list.table && filter->list.chain) {
+ ret = implicit_chain_cache(ctx, table, filter->list.chain);
+ if (ret < 0)
+ goto cache_fails;
}
}
}
cache_fails:
+ if (set_list)
+ nftnl_set_list_free(set_list);
+ if (ft_list)
+ nftnl_flowtable_list_free(ft_list);
+
if (flags & NFT_CACHE_CHAIN_BIT)
nftnl_chain_list_free(chain_list);
return ret;
}
-static int nft_cache_init(struct netlink_ctx *ctx, unsigned int flags)
+static int nft_cache_init(struct netlink_ctx *ctx, unsigned int flags,
+ const struct nft_cache_filter *filter)
{
struct handle handle = {
.family = NFPROTO_UNSPEC,
@@ -701,10 +1144,10 @@ static int nft_cache_init(struct netlink_ctx *ctx, unsigned int flags)
return 0;
/* assume NFT_CACHE_TABLE is always set. */
- ret = cache_init_tables(ctx, &handle, &ctx->nft->cache);
+ ret = cache_init_tables(ctx, &handle, &ctx->nft->cache, filter);
if (ret < 0)
return ret;
- ret = cache_init_objects(ctx, flags);
+ ret = cache_init_objects(ctx, flags, filter);
if (ret < 0)
return ret;
@@ -732,7 +1175,8 @@ bool nft_cache_needs_update(struct nft_cache *cache)
}
int nft_cache_update(struct nft_ctx *nft, unsigned int flags,
- struct list_head *msgs)
+ struct list_head *msgs,
+ const struct nft_cache_filter *filter)
{
struct netlink_ctx ctx = {
.list = LIST_HEAD_INIT(ctx.list),
@@ -761,14 +1205,18 @@ replay:
goto skip;
}
- ret = nft_cache_init(&ctx, flags);
+ ret = nft_cache_init(&ctx, flags, filter);
if (ret < 0) {
if (errno == EINTR) {
nft_cache_release(cache);
goto replay;
}
+ erec_queue(error(&netlink_location, "cache initialization failed: %s",
+ strerror(errno)),
+ msgs);
nft_cache_release(cache);
+
return -1;
}
diff --git a/src/cli.c b/src/cli.c
index 4845e5cf..11fc85ab 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -26,7 +26,6 @@
#include <readline/history.h>
#elif defined(HAVE_LIBEDIT)
#include <editline/readline.h>
-#include <editline/history.h>
#else
#include <linenoise.h>
#endif
@@ -153,13 +152,6 @@ static void cli_complete(char *line)
nft_run_cmd_from_buffer(cli_nft, line);
free(line);
}
-
-void cli_exit(void)
-{
- rl_callback_handler_remove();
- rl_deprep_terminal();
- write_history(histfile);
-}
#endif
#if defined(HAVE_LIBREADLINE)
@@ -189,6 +181,13 @@ int cli_init(struct nft_ctx *nft)
return 0;
}
+void cli_exit(void)
+{
+ rl_callback_handler_remove();
+ rl_deprep_terminal();
+ write_history(histfile);
+}
+
#elif defined(HAVE_LIBEDIT)
int cli_init(struct nft_ctx *nft)
@@ -213,10 +212,17 @@ int cli_init(struct nft_ctx *nft)
cli_complete(line);
}
+ cli_exit();
return 0;
}
+void cli_exit(void)
+{
+ rl_deprep_terminal();
+ write_history(histfile);
+}
+
#else /* HAVE_LINENOISE */
int cli_init(struct nft_ctx *nft)
diff --git a/src/cmd.c b/src/cmd.c
index d956919b..9e375078 100644
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -1,3 +1,11 @@
+/*
+ * 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 <erec.h>
#include <mnl.h>
#include <cmd.h>
@@ -81,7 +89,7 @@ static int nft_cmd_enoent_rule(struct netlink_ctx *ctx, const struct cmd *cmd,
const struct table *table = NULL;
struct chain *chain;
- if (nft_cache_update(ctx->nft, flags, ctx->msgs) < 0)
+ if (nft_cache_update(ctx->nft, flags, ctx->msgs, NULL) < 0)
return 0;
chain = chain_lookup_fuzzy(&cmd->handle, &ctx->nft->cache, &table);
@@ -237,7 +245,7 @@ void nft_cmd_error(struct netlink_ctx *ctx, struct cmd *cmd,
struct mnl_err *err)
{
const struct location *loc = NULL;
- int i;
+ uint32_t i;
for (i = 0; i < cmd->num_attrs; i++) {
if (!cmd->attr[i].offset)
diff --git a/src/ct.c b/src/ct.c
index 2218ecc7..64327561 100644
--- a/src/ct.c
+++ b/src/ct.c
@@ -176,7 +176,7 @@ static struct error_record *ct_label_type_parse(struct parse_ctx *ctx,
{
const struct symbolic_constant *s;
const struct datatype *dtype;
- uint8_t data[CT_LABEL_BIT_SIZE];
+ uint8_t data[CT_LABEL_BIT_SIZE / BITS_PER_BYTE];
uint64_t bit;
mpz_t value;
@@ -211,8 +211,7 @@ static struct error_record *ct_label_type_parse(struct parse_ctx *ctx,
mpz_export_data(data, value, BYTEORDER_HOST_ENDIAN, sizeof(data));
*res = constant_expr_alloc(&sym->location, dtype,
- dtype->byteorder, sizeof(data),
- data);
+ dtype->byteorder, CT_LABEL_BIT_SIZE, data);
mpz_clear(value);
return NULL;
}
@@ -272,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,
@@ -579,6 +578,7 @@ static const struct stmt_ops flow_offload_stmt_ops = {
.name = "flow_offload",
.print = flow_offload_stmt_print,
.destroy = flow_offload_stmt_destroy,
+ .json = flow_offload_stmt_json,
};
struct stmt *flow_offload_stmt_alloc(const struct location *loc,
diff --git a/src/datatype.c b/src/datatype.c
index b849f708..002ed46a 100644
--- a/src/datatype.c
+++ b/src/datatype.c
@@ -321,23 +321,11 @@ 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)
-{
- *res = constant_expr_alloc(&sym->location, &string_type,
- BYTEORDER_HOST_ENDIAN,
- (strlen(sym->identifier) + 1) * BITS_PER_BYTE,
- sym->identifier);
- return NULL;
-}
-
const struct datatype verdict_type = {
.type = TYPE_VERDICT,
.name = "verdict",
.desc = "netfilter verdict",
.print = verdict_type_print,
- .parse = verdict_type_parse,
};
static const struct symbol_table nfproto_tbl = {
@@ -410,6 +398,22 @@ const struct datatype integer_type = {
.parse = integer_type_parse,
};
+static void xinteger_type_print(const struct expr *expr, struct output_ctx *octx)
+{
+ nft_gmp_print(octx, "0x%Zx", expr->value);
+}
+
+/* Alias of integer_type to print raw payload expressions in hexadecimal. */
+const struct datatype xinteger_type = {
+ .type = TYPE_INTEGER,
+ .name = "integer",
+ .desc = "integer",
+ .basetype = &integer_type,
+ .print = xinteger_type_print,
+ .json = integer_type_json,
+ .parse = integer_type_parse,
+};
+
static void string_type_print(const struct expr *expr, struct output_ctx *octx)
{
unsigned int len = div_round_up(expr->len, BITS_PER_BYTE);
@@ -1052,6 +1056,7 @@ static struct error_record *time_type_parse(struct parse_ctx *ctx,
struct expr **res)
{
struct error_record *erec;
+ uint32_t s32;
uint64_t s;
erec = time_parse(&sym->location, sym->identifier, &s);
@@ -1061,9 +1066,10 @@ static struct error_record *time_type_parse(struct parse_ctx *ctx,
if (s > UINT32_MAX)
return error(&sym->location, "value too large");
+ s32 = s;
*res = constant_expr_alloc(&sym->location, &time_type,
BYTEORDER_HOST_ENDIAN,
- sizeof(uint32_t) * BITS_PER_BYTE, &s);
+ sizeof(uint32_t) * BITS_PER_BYTE, &s32);
return NULL;
}
@@ -1072,7 +1078,7 @@ const struct datatype time_type = {
.name = "time",
.desc = "relative time",
.byteorder = BYTEORDER_HOST_ENDIAN,
- .size = 8 * BITS_PER_BYTE,
+ .size = 4 * BITS_PER_BYTE,
.basetype = &integer_type,
.print = time_type_print,
.json = time_type_json,
@@ -1118,7 +1124,7 @@ void datatype_set(struct expr *expr, const struct datatype *dtype)
expr->dtype = datatype_get(dtype);
}
-static struct datatype *dtype_clone(const struct datatype *orig_dtype)
+struct datatype *dtype_clone(const struct datatype *orig_dtype)
{
struct datatype *dtype;
diff --git a/src/erec.c b/src/erec.c
index 5c3351a5..aebb8632 100644
--- a/src/erec.c
+++ b/src/erec.c
@@ -81,11 +81,58 @@ struct error_record *erec_create(enum error_record_types type,
return erec;
}
+void print_location(FILE *f, const struct input_descriptor *indesc,
+ const struct location *loc)
+{
+ const struct input_descriptor *tmp;
+ const struct location *iloc;
+
+ if (indesc->location.indesc != NULL) {
+ const char *prefix = "In file included from";
+ iloc = &indesc->location;
+ for (tmp = iloc->indesc;
+ tmp != NULL && tmp->type != INDESC_INTERNAL;
+ tmp = iloc->indesc) {
+ fprintf(f, "%s %s:%u:%u-%u:\n", prefix,
+ tmp->name,
+ iloc->first_line, iloc->first_column,
+ iloc->last_column);
+ prefix = " from";
+ iloc = &tmp->location;
+ }
+ }
+ if (indesc->type != INDESC_BUFFER && indesc->name) {
+ fprintf(f, "%s:%u:%u-%u: ", indesc->name,
+ loc->first_line, loc->first_column,
+ loc->last_column);
+ }
+}
+
+const char *line_location(const struct input_descriptor *indesc,
+ const struct location *loc, char *buf, size_t bufsiz)
+{
+ const char *line = NULL;
+ FILE *f;
+
+ f = fopen(indesc->name, "r");
+ if (!f)
+ return NULL;
+
+ if (!fseek(f, loc->line_offset, SEEK_SET) &&
+ fread(buf, 1, bufsiz - 1, f) > 0) {
+ *strchrnul(buf, '\n') = '\0';
+ line = buf;
+ }
+ fclose(f);
+
+ return line;
+}
+
void erec_print(struct output_ctx *octx, const struct error_record *erec,
unsigned int debug_mask)
{
- const struct location *loc = erec->locations, *iloc;
- const struct input_descriptor *indesc = loc->indesc, *tmp;
+ const struct location *loc = erec->locations;
+ const struct input_descriptor *indesc = loc->indesc;
const char *line = NULL;
char buf[1024] = {};
char *pbuf = NULL;
@@ -99,17 +146,13 @@ void erec_print(struct output_ctx *octx, const struct error_record *erec,
line = indesc->data;
*strchrnul(line, '\n') = '\0';
break;
+ case INDESC_STDIN:
+ line = indesc->data;
+ line += loc->line_offset;
+ *strchrnul(line, '\n') = '\0';
+ break;
case INDESC_FILE:
- f = fopen(indesc->name, "r");
- if (!f)
- break;
-
- if (!fseek(f, loc->line_offset, SEEK_SET) &&
- fread(buf, 1, sizeof(buf) - 1, f) > 0) {
- *strchrnul(buf, '\n') = '\0';
- line = buf;
- }
- fclose(f);
+ line = line_location(indesc, loc, buf, sizeof(buf));
break;
case INDESC_INTERNAL:
case INDESC_NETLINK:
@@ -127,29 +170,15 @@ void erec_print(struct output_ctx *octx, const struct error_record *erec,
fprintf(f, "%s\n", erec->msg);
for (l = 0; l < (int)erec->num_locations; l++) {
loc = &erec->locations[l];
+ if (!loc->nle)
+ continue;
netlink_dump_expr(loc->nle, f, debug_mask);
}
return;
}
- if (indesc->location.indesc != NULL) {
- const char *prefix = "In file included from";
- iloc = &indesc->location;
- for (tmp = iloc->indesc;
- tmp != NULL && tmp->type != INDESC_INTERNAL;
- tmp = iloc->indesc) {
- fprintf(f, "%s %s:%u:%u-%u:\n", prefix,
- tmp->name,
- iloc->first_line, iloc->first_column,
- iloc->last_column);
- prefix = " from";
- iloc = &tmp->location;
- }
- }
- if (indesc->name != NULL)
- fprintf(f, "%s:%u:%u-%u: ", indesc->name,
- loc->first_line, loc->first_column,
- loc->last_column);
+ print_location(f, indesc, loc);
+
if (error_record_names[erec->type])
fprintf(f, "%s: ", error_record_names[erec->type]);
fprintf(f, "%s\n", erec->msg);
diff --git a/src/evaluate.c b/src/evaluate.c
index 8ebc7561..128b7504 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -29,6 +29,7 @@
#include <expression.h>
#include <statement.h>
+#include <intervals.h>
#include <netlink.h>
#include <time.h>
#include <rule.h>
@@ -38,6 +39,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[] = {
@@ -138,6 +146,7 @@ static enum ops byteorder_conversion_op(struct expr *expr,
static int byteorder_conversion(struct eval_ctx *ctx, struct expr **expr,
enum byteorder byteorder)
{
+ enum datatypes basetype;
enum ops op;
assert(!expr_is_constant(*expr) || expr_is_singleton(*expr));
@@ -146,14 +155,43 @@ 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;
+ }
- if (expr_basetype(*expr)->type != TYPE_INTEGER)
+ basetype = expr_basetype(*expr)->type;
+ switch (basetype) {
+ case TYPE_INTEGER:
+ break;
+ case TYPE_STRING:
+ return 0;
+ default:
return expr_error(ctx->msgs, *expr,
- "Byteorder mismatch: expected %s, got %s",
+ "Byteorder mismatch: %s expected %s, %s got %s",
byteorder_names[byteorder],
+ expr_name(*expr),
byteorder_names[(*expr)->byteorder]);
+ }
if (expr_is_constant(*expr) || (*expr)->len / BITS_PER_BYTE < 2)
(*expr)->byteorder = byteorder;
@@ -329,15 +367,18 @@ static int expr_evaluate_string(struct eval_ctx *ctx, struct expr **exprp)
*exprp = value;
return 0;
}
+
+ data[datalen] = 0;
value = constant_expr_alloc(&expr->location, ctx->ectx.dtype,
BYTEORDER_HOST_ENDIAN,
- datalen * BITS_PER_BYTE, data);
+ expr->len, data);
prefix = prefix_expr_alloc(&expr->location, value,
datalen * BITS_PER_BYTE);
datatype_set(prefix, ctx->ectx.dtype);
prefix->flags |= EXPR_F_CONSTANT;
prefix->byteorder = BYTEORDER_HOST_ENDIAN;
+ prefix->len = expr->len;
expr_free(expr);
*exprp = prefix;
@@ -414,11 +455,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);
@@ -432,6 +475,9 @@ conflict_resolution_gen_dependency(struct eval_ctx *ctx, int protocol,
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;
}
@@ -564,6 +610,7 @@ 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) {
@@ -581,7 +628,8 @@ static int expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
assert(dependency);
- base = ctx->pctx.protocol[pb].desc;
+ pctx = eval_proto_ctx(ctx);
+ base = pctx->protocol[pb].desc;
if (base == dependency)
return __expr_evaluate_exthdr(ctx, exprp);
@@ -599,7 +647,7 @@ static int expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
/* dependency supersede.
*
- * 'inet' is a 'phony' l2 dependency used by NFPROTO_INET to fulfill network
+ * 'inet' is a 'phony' l2 dependency used by NFPROTO_INET to fulfil network
* header dependency, i.e. ensure that 'ip saddr 1.2.3.4' only sees ip headers.
*
* If a match expression that depends on a particular L2 header, e.g. ethernet,
@@ -611,7 +659,7 @@ static int expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp)
* Example: inet filter in ip saddr 1.2.3.4 ether saddr a:b:c:d:e:f
*
* ip saddr adds meta dependency on ipv4 packets
- * ether saddr adds another dependeny on ethernet frames.
+ * ether saddr adds another dependency on ethernet frames.
*/
static int meta_iiftype_gen_dependency(struct eval_ctx *ctx,
struct expr *payload, struct stmt **res)
@@ -629,6 +677,9 @@ static int meta_iiftype_gen_dependency(struct eval_ctx *ctx,
return expr_error(ctx->msgs, payload,
"dependency statement is invalid");
+ if (ctx->inner_desc)
+ nstmt->expr->left->meta.inner_desc = ctx->inner_desc;
+
*res = nstmt;
return 0;
}
@@ -644,20 +695,37 @@ 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;
- if (payload->payload.base == PROTO_BASE_LL_HDR &&
- proto_is_dummy(desc)) {
- err = meta_iiftype_gen_dependency(ctx, payload, &nstmt);
- if (err < 0)
- return err;
+ pctx = eval_proto_ctx(ctx);
- rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+ if (payload->payload.base == PROTO_BASE_LL_HDR) {
+ if (proto_is_dummy(desc)) {
+ if (ctx->inner_desc) {
+ proto_ctx_update(pctx, PROTO_BASE_LL_HDR, &payload->location, &proto_eth);
+ } else {
+ err = meta_iiftype_gen_dependency(ctx, payload, &nstmt);
+ if (err < 0)
+ return err;
+
+ desc = payload->payload.desc;
+ rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+ }
+ } else {
+ unsigned int i;
+
+ /* payload desc stored in the L2 header stack? No conflict. */
+ for (i = 0; i < pctx->stacked_ll_count; i++) {
+ if (pctx->stacked_ll[i] == payload->payload.desc)
+ return 0;
+ }
+ }
}
assert(base <= PROTO_BASE_MAX);
/* This payload and the existing context don't match, conflict. */
- if (ctx->pctx.protocol[base + 1].desc != NULL)
+ if (pctx->protocol[base + 1].desc != NULL)
return 1;
link = proto_find_num(desc, payload->payload.desc);
@@ -665,7 +733,13 @@ static int resolve_protocol_conflict(struct eval_ctx *ctx,
conflict_resolution_gen_dependency(ctx, link, payload, &nstmt) < 0)
return 1;
- payload->payload.offset += ctx->pctx.protocol[base].offset;
+ if (base == PROTO_BASE_LL_HDR) {
+ unsigned int i;
+
+ for (i = 0; i < pctx->stacked_ll_count; i++)
+ payload->payload.offset += pctx->stacked_ll[i]->length;
+ }
+
rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
return 0;
@@ -681,25 +755,47 @@ 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);
+
+ payload->payload.offset += pctx->stacked_ll[0]->length;
+ rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+ return 1;
+ }
goto check_icmp;
}
if (payload->payload.base == desc->base &&
- proto_ctx_is_ambiguous(&ctx->pctx, base)) {
- desc = proto_ctx_find_conflict(&ctx->pctx, base, payload->payload.desc);
+ proto_ctx_is_ambiguous(pctx, base)) {
+ desc = proto_ctx_find_conflict(pctx, base, payload->payload.desc);
assert(desc);
return expr_error(ctx->msgs, payload,
@@ -714,7 +810,12 @@ static int __expr_evaluate_payload(struct eval_ctx *ctx, struct expr *expr)
if (desc == payload->payload.desc) {
const struct proto_hdr_template *tmpl;
- payload->payload.offset += ctx->pctx.protocol[base].offset;
+ if (desc->base == PROTO_BASE_LL_HDR) {
+ unsigned int i;
+
+ for (i = 0; i < pctx->stacked_ll_count; i++)
+ payload->payload.offset += pctx->stacked_ll[i]->length;
+ }
check_icmp:
if (desc != &proto_icmp && desc != &proto_icmp6)
return 0;
@@ -740,13 +841,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);
}
@@ -777,6 +878,67 @@ static int expr_evaluate_payload(struct eval_ctx *ctx, struct expr **exprp)
return 0;
}
+static int expr_evaluate_inner(struct eval_ctx *ctx, struct expr **exprp)
+{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
+ const struct proto_desc *desc = NULL;
+ struct expr *expr = *exprp;
+ int ret;
+
+ assert(expr->etype == EXPR_PAYLOAD);
+
+ pctx = eval_proto_ctx(ctx);
+ desc = pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc;
+
+ if (desc == NULL &&
+ expr->payload.inner_desc->base < PROTO_BASE_INNER_HDR) {
+ struct stmt *nstmt;
+
+ if (payload_gen_inner_dependency(ctx, expr, &nstmt) < 0)
+ return -1;
+
+ rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
+
+ proto_ctx_update(pctx, PROTO_BASE_TRANSPORT_HDR, &expr->location, expr->payload.inner_desc);
+ }
+
+ if (expr->payload.inner_desc->base == PROTO_BASE_INNER_HDR) {
+ desc = pctx->protocol[expr->payload.inner_desc->base - 1].desc;
+ if (!desc) {
+ return expr_error(ctx->msgs, expr,
+ "no transport protocol specified");
+ }
+
+ if (proto_find_num(desc, expr->payload.inner_desc) < 0) {
+ return expr_error(ctx->msgs, expr,
+ "unexpected transport protocol %s",
+ desc->name);
+ }
+
+ proto_ctx_update(pctx, expr->payload.inner_desc->base, &expr->location,
+ expr->payload.inner_desc);
+ }
+
+ if (expr->payload.base != PROTO_BASE_INNER_HDR)
+ ctx->inner_desc = expr->payload.inner_desc;
+
+ ret = expr_evaluate_payload(ctx, exprp);
+
+ return ret;
+}
+
+static int expr_evaluate_payload_inner(struct eval_ctx *ctx, struct expr **exprp)
+{
+ int ret;
+
+ if ((*exprp)->payload.inner_desc)
+ ret = expr_evaluate_inner(ctx, exprp);
+ else
+ ret = expr_evaluate_payload(ctx, exprp);
+
+ return ret;
+}
+
/*
* RT expression: validate protocol dependencies.
*/
@@ -784,20 +946,22 @@ static int expr_evaluate_rt(struct eval_ctx *ctx, struct expr **expr)
{
static const char emsg[] = "cannot determine ip protocol version, use \"ip nexthop\" or \"ip6 nexthop\" instead";
struct expr *rt = *expr;
+ struct proto_ctx *pctx;
- rt_expr_update_type(&ctx->pctx, rt);
+ pctx = eval_proto_ctx(ctx);
+ rt_expr_update_type(pctx, rt);
switch (rt->rt.key) {
case NFT_RT_NEXTHOP4:
if (rt->dtype != &ipaddr_type)
return expr_error(ctx->msgs, rt, "%s", emsg);
- if (ctx->pctx.family == NFPROTO_IPV6)
+ if (pctx->family == NFPROTO_IPV6)
return expr_error(ctx->msgs, rt, "%s nexthop will not match", "ip");
break;
case NFT_RT_NEXTHOP6:
if (rt->dtype != &ip6addr_type)
return expr_error(ctx->msgs, rt, "%s", emsg);
- if (ctx->pctx.family == NFPROTO_IPV4)
+ if (pctx->family == NFPROTO_IPV4)
return expr_error(ctx->msgs, rt, "%s nexthop will not match", "ip6");
break;
default:
@@ -812,8 +976,10 @@ static int ct_gen_nh_dependency(struct eval_ctx *ctx, struct expr *ct)
const struct proto_desc *base, *base_now;
struct expr *left, *right, *dep;
struct stmt *nstmt = NULL;
+ struct proto_ctx *pctx;
- base_now = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ pctx = eval_proto_ctx(ctx);
+ base_now = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
switch (ct->ct.nfproto) {
case NFPROTO_IPV4:
@@ -823,7 +989,7 @@ static int ct_gen_nh_dependency(struct eval_ctx *ctx, struct expr *ct)
base = &proto_ip6;
break;
default:
- base = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ base = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if (base == &proto_ip)
ct->ct.nfproto = NFPROTO_IPV4;
else if (base == &proto_ip)
@@ -845,8 +1011,8 @@ static int ct_gen_nh_dependency(struct eval_ctx *ctx, struct expr *ct)
return expr_error(ctx->msgs, ct,
"conflicting dependencies: %s vs. %s\n",
base->name,
- ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc->name);
- switch (ctx->pctx.family) {
+ pctx->protocol[PROTO_BASE_NETWORK_HDR].desc->name);
+ switch (pctx->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
return 0;
@@ -859,7 +1025,7 @@ static int ct_gen_nh_dependency(struct eval_ctx *ctx, struct expr *ct)
constant_data_ptr(ct->ct.nfproto, left->len));
dep = relational_expr_alloc(&ct->location, OP_EQ, left, right);
- relational_expr_pctx_update(&ctx->pctx, dep);
+ relational_expr_pctx_update(pctx, dep);
nstmt = expr_stmt_alloc(&dep->location, dep);
rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt);
@@ -875,8 +1041,10 @@ static int expr_evaluate_ct(struct eval_ctx *ctx, struct expr **expr)
{
const struct proto_desc *base, *error;
struct expr *ct = *expr;
+ struct proto_ctx *pctx;
- base = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ pctx = eval_proto_ctx(ctx);
+ base = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
switch (ct->ct.key) {
case NFT_CT_SRC:
@@ -901,13 +1069,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);
}
@@ -1016,7 +1184,7 @@ static int expr_evaluate_range(struct eval_ctx *ctx, struct expr **expr)
left = range->left;
right = range->right;
- if (mpz_cmp(left->value, right->value) >= 0)
+ if (mpz_cmp(left->value, right->value) > 0)
return expr_error(ctx->msgs, range,
"Range has zero or negative size");
datatype_set(range, left->dtype);
@@ -1137,7 +1305,6 @@ 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;
op->byteorder = BYTEORDER_HOST_ENDIAN;
op->len = left->len;
@@ -1213,7 +1380,7 @@ static int expr_evaluate_binop(struct eval_ctx *ctx, struct expr **expr)
sym, expr_name(right));
/* The grammar guarantees this */
- assert(expr_basetype(left) == expr_basetype(right));
+ assert(datatype_equal(expr_basetype(left), expr_basetype(right)));
switch (op->op) {
case OP_LSHIFT:
@@ -1246,10 +1413,27 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
uint32_t type = dtype ? dtype->type : 0, ntype = 0;
int off = dtype ? dtype->subtypes : 0;
unsigned int flags = EXPR_F_CONSTANT | EXPR_F_SINGLETON;
- struct expr *i, *next;
+ const struct list_head *expressions = NULL;
+ struct expr *i, *next, *key = NULL;
+ const struct expr *key_ctx = NULL;
+ bool runaway = false;
+ uint32_t size = 0;
+
+ if (ctx->ectx.key && ctx->ectx.key->etype == EXPR_CONCAT) {
+ key_ctx = ctx->ectx.key;
+ assert(!list_empty(&ctx->ectx.key->expressions));
+ key = list_first_entry(&ctx->ectx.key->expressions, struct expr, list);
+ expressions = &ctx->ectx.key->expressions;
+ }
list_for_each_entry_safe(i, next, &(*expr)->expressions, list) {
- unsigned dsize_bytes;
+ enum byteorder bo = BYTEORDER_INVALID;
+ unsigned dsize_bytes, dsize = 0;
+
+ if (runaway) {
+ return expr_binary_error(ctx->msgs, *expr, key_ctx,
+ "too many concatenation components");
+ }
if (i->etype == EXPR_CT &&
(i->ct.key == NFT_CT_SRC ||
@@ -1263,32 +1447,62 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
"expecting %s",
dtype->desc);
- if (dtype == NULL)
+ if (key) {
+ tmp = key->dtype;
+ dsize = key->len;
+ bo = key->byteorder;
+ off--;
+ } else if (dtype == NULL || off == 0) {
tmp = datatype_lookup(TYPE_INVALID);
- else
+ } else {
tmp = concat_subtype_lookup(type, --off);
- expr_set_context(&ctx->ectx, tmp, tmp->size);
+ dsize = tmp->size;
+ bo = tmp->byteorder;
+ }
+
+ __expr_set_context(&ctx->ectx, tmp, bo, dsize, 0);
if (list_member_evaluate(ctx, &i) < 0)
return -1;
flags &= i->flags;
+ if (!key && i->dtype->type == TYPE_INTEGER) {
+ struct datatype *clone;
+
+ clone = dtype_clone(i->dtype);
+ clone->size = i->len;
+ clone->byteorder = i->byteorder;
+ clone->refcnt = 1;
+ i->dtype = clone;
+ }
+
if (dtype == NULL && i->dtype->size == 0)
return expr_binary_error(ctx->msgs, i, *expr,
"can not use variable sized "
"data types (%s) in concat "
"expressions",
i->dtype->name);
+ if (dsize == 0) /* reload after evaluation or clone above */
+ dsize = i->dtype->size;
ntype = concat_subtype_add(ntype, i->dtype->type);
- dsize_bytes = div_round_up(i->dtype->size, BITS_PER_BYTE);
+ dsize_bytes = div_round_up(dsize, BITS_PER_BYTE);
(*expr)->field_len[(*expr)->field_count++] = dsize_bytes;
+ size += netlink_padded_len(dsize);
+ if (key && expressions) {
+ if (list_is_last(&key->list, expressions))
+ runaway = true;
+
+ key = list_next_entry(key, list);
+ }
+
+ ctx->inner_desc = NULL;
}
(*expr)->flags |= flags;
datatype_set(*expr, concat_type_alloc(ntype));
- (*expr)->len = (*expr)->dtype->size;
+ (*expr)->len = size;
if (off > 0)
return expr_error(ctx->msgs, *expr,
@@ -1297,6 +1511,10 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
dtype->desc, (*expr)->dtype->desc);
expr_set_context(&ctx->ectx, (*expr)->dtype, (*expr)->len);
+ if (!key_ctx)
+ ctx->ectx.key = *expr;
+ else
+ ctx->ectx.key = key_ctx;
return 0;
}
@@ -1381,15 +1599,15 @@ static int __expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr *elem)
static int expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr **expr)
{
struct expr *elem = *expr;
+ const struct expr *key;
if (ctx->set) {
- const struct expr *key;
-
if (__expr_evaluate_set_elem(ctx, elem) < 0)
return -1;
key = ctx->set->key;
__expr_set_context(&ctx->ectx, key->dtype, key->byteorder, key->len, 0);
+ ctx->ectx.key = key;
}
if (expr_evaluate(ctx, &elem->key) < 0)
@@ -1400,9 +1618,19 @@ static int expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr **expr)
switch (elem->key->etype) {
case EXPR_PREFIX:
case EXPR_RANGE:
- return expr_error(ctx->msgs, elem,
- "You must add 'flags interval' to your %s declaration if you want to add %s elements",
- set_is_map(ctx->set->flags) ? "map" : "set", expr_name(elem->key));
+ key = elem->key;
+ goto err_missing_flag;
+ case EXPR_CONCAT:
+ list_for_each_entry(key, &elem->key->expressions, list) {
+ switch (key->etype) {
+ case EXPR_PREFIX:
+ case EXPR_RANGE:
+ goto err_missing_flag;
+ default:
+ break;
+ }
+ }
+ break;
default:
break;
}
@@ -1411,7 +1639,13 @@ static int expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr **expr)
datatype_set(elem, elem->key->dtype);
elem->len = elem->key->len;
elem->flags = elem->key->flags;
+
return 0;
+
+err_missing_flag:
+ return expr_error(ctx->msgs, key,
+ "You must add 'flags interval' to your %s declaration if you want to add %s elements",
+ set_is_map(ctx->set->flags) ? "map" : "set", expr_name(key));
}
static const struct expr *expr_set_elem(const struct expr *expr)
@@ -1422,6 +1656,49 @@ static const struct expr *expr_set_elem(const struct expr *expr)
return expr;
}
+static int interval_set_eval(struct eval_ctx *ctx, struct set *set,
+ struct expr *init)
+{
+ int ret;
+
+ if (!init)
+ return 0;
+
+ ret = 0;
+ switch (ctx->cmd->op) {
+ case CMD_CREATE:
+ case CMD_ADD:
+ case CMD_INSERT:
+ if (set->automerge) {
+ ret = set_automerge(ctx->msgs, ctx->cmd, set, init,
+ ctx->nft->debug_mask);
+ } else {
+ ret = set_overlap(ctx->msgs, set, init);
+ }
+ break;
+ case CMD_DELETE:
+ case CMD_DESTROY:
+ ret = set_delete(ctx->msgs, ctx->cmd, set, init,
+ ctx->nft->debug_mask);
+ break;
+ case CMD_GET:
+ break;
+ default:
+ BUG("unhandled op %d\n", ctx->cmd->op);
+ break;
+ }
+
+ return ret;
+}
+
+static void expr_evaluate_set_ref(struct eval_ctx *ctx, struct expr *expr)
+{
+ struct set *set = expr->set;
+
+ expr_set_context(&ctx->ectx, set->key->dtype, set->key->len);
+ ctx->ectx.key = set->key;
+}
+
static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr)
{
struct expr *set = *expr, *i, *next;
@@ -1439,7 +1716,7 @@ static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr)
list_for_each_entry(j, &i->left->key->expressions, list) {
new = mapping_expr_alloc(&i->location,
expr_get(j),
- expr_clone(i->right));
+ expr_get(i->right));
list_add_tail(&new->list, &set->expressions);
set->size++;
}
@@ -1457,7 +1734,7 @@ static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr)
if (elem->etype == EXPR_SET_ELEM &&
elem->key->etype == EXPR_SET) {
- struct expr *new = expr_clone(elem->key);
+ struct expr *new = expr_get(elem->key);
set->set_flags |= elem->key->set_flags;
list_replace(&i->list, &new->list);
@@ -1509,10 +1786,25 @@ static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr)
datatype_set(set, ctx->ectx.dtype);
set->len = ctx->ectx.len;
set->flags |= EXPR_F_CONSTANT;
+
return 0;
}
static int binop_transfer(struct eval_ctx *ctx, struct expr **expr);
+
+static void map_set_concat_info(struct expr *map)
+{
+ map->mappings->set->flags |= map->mappings->set->init->set_flags;
+
+ if (map->mappings->set->flags & NFT_SET_INTERVAL &&
+ map->map->etype == EXPR_CONCAT) {
+ memcpy(&map->mappings->set->desc.field_len, &map->map->field_len,
+ sizeof(map->mappings->set->desc.field_len));
+ map->mappings->set->desc.field_count = map->map->field_count;
+ map->mappings->flags |= NFT_SET_CONCAT;
+ }
+}
+
static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
{
struct expr_ctx ectx = ctx->ectx;
@@ -1549,14 +1841,21 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
switch (map->mappings->etype) {
case EXPR_SET:
- key = constant_expr_alloc(&map->location,
- ctx->ectx.dtype,
- ctx->ectx.byteorder,
- ctx->ectx.len, NULL);
+ if (ctx->ectx.key && ctx->ectx.key->etype == EXPR_CONCAT) {
+ key = expr_clone(ctx->ectx.key);
+ } else {
+ key = constant_expr_alloc(&map->location,
+ ctx->ectx.dtype,
+ ctx->ectx.byteorder,
+ ctx->ectx.len, NULL);
+ }
dtype = set_datatype_alloc(ectx.dtype, ectx.byteorder);
- data = constant_expr_alloc(&netlink_location, dtype,
- dtype->byteorder, ectx.len, NULL);
+ if (dtype->type == TYPE_VERDICT)
+ data = verdict_expr_alloc(&netlink_location, 0, NULL);
+ else
+ data = constant_expr_alloc(&netlink_location, dtype,
+ dtype->byteorder, ectx.len, NULL);
mappings = implicit_set_declaration(ctx, "__map%d",
key, data,
@@ -1570,6 +1869,12 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
ctx->set = mappings->set;
if (expr_evaluate(ctx, &map->mappings->set->init) < 0)
return -1;
+
+ if (set_is_interval(map->mappings->set->init->set_flags) &&
+ !(map->mappings->set->init->set_flags & NFT_SET_CONCAT) &&
+ interval_set_eval(ctx, ctx->set, map->mappings->set->init) < 0)
+ return -1;
+
expr_set_context(&ctx->ectx, ctx->set->key->dtype, ctx->set->key->len);
if (binop_transfer(ctx, expr) < 0)
return -1;
@@ -1580,15 +1885,8 @@ static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
ctx->set->key->len = ctx->ectx.len;
ctx->set = NULL;
map = *expr;
- map->mappings->set->flags |= map->mappings->set->init->set_flags;
-
- if (map->mappings->set->flags & NFT_SET_INTERVAL &&
- map->map->etype == EXPR_CONCAT) {
- memcpy(&map->mappings->set->desc.field_len, &map->map->field_len,
- sizeof(map->mappings->set->desc.field_len));
- map->mappings->set->desc.field_count = map->map->field_count;
- map->mappings->flags |= NFT_SET_CONCAT;
- }
+
+ map_set_concat_info(map);
break;
case EXPR_SYMBOL:
if (expr_evaluate(ctx, &map->mappings) < 0)
@@ -1954,6 +2252,7 @@ static bool range_needs_swap(const struct expr *range)
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;
@@ -1961,6 +2260,8 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
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)
@@ -2028,7 +2329,7 @@ static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
* Update protocol context for payload and meta iiftype
* equality expressions.
*/
- relational_expr_pctx_update(&ctx->pctx, rel);
+ relational_expr_pctx_update(pctx, rel);
/* fall through */
case OP_NEQ:
@@ -2140,11 +2441,12 @@ static int expr_evaluate_fib(struct eval_ctx *ctx, struct expr **exprp)
static int expr_evaluate_meta(struct eval_ctx *ctx, struct expr **exprp)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
struct expr *meta = *exprp;
switch (meta->meta.key) {
case NFT_META_NFPROTO:
- if (ctx->pctx.family != NFPROTO_INET &&
+ if (pctx->family != NFPROTO_INET &&
meta->flags & EXPR_F_PROTOCOL)
return expr_error(ctx->msgs, meta,
"meta nfproto is only useful in the inet family");
@@ -2187,7 +2489,16 @@ static int expr_evaluate_osf(struct eval_ctx *ctx, struct expr **expr)
static int expr_evaluate_variable(struct eval_ctx *ctx, struct expr **exprp)
{
- struct expr *new = expr_clone((*exprp)->sym->expr);
+ struct symbol *sym = (*exprp)->sym;
+ struct expr *new;
+
+ /* If variable is reused from different locations in the ruleset, then
+ * clone expression.
+ */
+ if (sym->refcnt > 2)
+ new = expr_clone(sym->expr);
+ else
+ new = expr_get(sym->expr);
if (expr_evaluate(ctx, &new) < 0) {
expr_free(new);
@@ -2202,9 +2513,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:
@@ -2254,6 +2566,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);
@@ -2270,7 +2583,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:
@@ -2416,6 +2729,25 @@ static int stmt_evaluate_arg(struct eval_ctx *ctx, struct stmt *stmt,
return __stmt_evaluate_arg(ctx, stmt, dtype, len, byteorder, expr);
}
+/* like stmt_evaluate_arg, but keep existing context created
+ * by previous expr_evaluate().
+ *
+ * This is needed for add/update statements:
+ * ctx->ectx.key has the set key, which may be needed for 'typeof'
+ * sets: the 'add/update' expression might contain integer data types.
+ *
+ * Without the key we cannot derive the element size.
+ */
+static int stmt_evaluate_key(struct eval_ctx *ctx, struct stmt *stmt,
+ const struct datatype *dtype, unsigned int len,
+ enum byteorder byteorder, struct expr **expr)
+{
+ if (expr_evaluate(ctx, expr) < 0)
+ return -1;
+
+ return __stmt_evaluate_arg(ctx, stmt, dtype, len, byteorder, expr);
+}
+
static int stmt_evaluate_verdict(struct eval_ctx *ctx, struct stmt *stmt)
{
if (stmt_evaluate_arg(ctx, stmt, &verdict_type, 0, 0, &stmt->expr) < 0)
@@ -2426,7 +2758,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,
@@ -2446,6 +2779,9 @@ static bool stmt_evaluate_payload_need_csum(const struct expr *payload)
{
const struct proto_desc *desc;
+ if (payload->payload.base == PROTO_BASE_INNER_HDR)
+ return true;
+
desc = payload->payload.desc;
return desc && desc->checksum_key;
@@ -2474,6 +2810,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;
@@ -2544,9 +2885,9 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
mpz_clear(ff);
assert(sizeof(data) * BITS_PER_BYTE >= masklen);
- mpz_export_data(data, bitmask, BYTEORDER_HOST_ENDIAN, sizeof(data));
+ mpz_export_data(data, bitmask, payload->byteorder, payload_byte_size);
mask = constant_expr_alloc(&payload->location, expr_basetype(payload),
- BYTEORDER_HOST_ENDIAN, masklen, data);
+ payload->byteorder, masklen, data);
mpz_clear(bitmask);
payload_bytes = payload_expr_alloc(&payload->location, NULL, 0);
@@ -2554,6 +2895,7 @@ static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt)
payload_byte_offset * BITS_PER_BYTE,
payload_byte_size * BITS_PER_BYTE);
+ payload_bytes->payload.is_raw = 1;
payload_bytes->payload.desc = payload->payload.desc;
payload_bytes->byteorder = payload->byteorder;
@@ -2642,9 +2984,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,
@@ -2656,9 +2999,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;
@@ -2725,6 +3069,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;
@@ -2734,23 +3079,26 @@ static int stmt_evaluate_reject_inet_family(struct eval_ctx *ctx,
case NFT_REJECT_ICMPX_UNREACH:
break;
case NFT_REJECT_ICMP_UNREACH:
- base = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ base = pctx->protocol[PROTO_BASE_LL_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
case NFPROTO_IPV4:
+ case __constant_htons(ETH_P_IP):
if (stmt->reject.family == NFPROTO_IPV4)
break;
return stmt_binary_error(ctx, stmt->reject.expr,
- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+ &pctx->protocol[PROTO_BASE_NETWORK_HDR],
"conflicting protocols specified: ip vs ip6");
case NFPROTO_IPV6:
+ case __constant_htons(ETH_P_IPV6):
if (stmt->reject.family == NFPROTO_IPV6)
break;
return stmt_binary_error(ctx, stmt->reject.expr,
- &ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR],
+ &pctx->protocol[PROTO_BASE_NETWORK_HDR],
"conflicting protocols specified: ip vs ip6");
default:
- BUG("unsupported family");
+ return stmt_error(ctx, stmt,
+ "cannot infer ICMP reject variant to use: explicit value required.\n");
}
break;
}
@@ -2761,9 +3109,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;
@@ -2778,13 +3127,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):
@@ -2792,29 +3142,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;
@@ -2826,14 +3176,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;
@@ -2847,7 +3198,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:
@@ -2861,7 +3214,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");
@@ -2885,35 +3238,38 @@ static int stmt_evaluate_reject_family(struct eval_ctx *ctx, struct stmt *stmt,
static int stmt_evaluate_reject_default(struct eval_ctx *ctx,
struct stmt *stmt)
{
- int protocol;
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_desc *desc, *base;
+ int protocol;
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
stmt->reject.type = NFT_REJECT_ICMP_UNREACH;
- stmt->reject.family = ctx->pctx.family;
- if (ctx->pctx.family == NFPROTO_IPV4)
+ stmt->reject.family = pctx->family;
+ if (pctx->family == NFPROTO_IPV4)
stmt->reject.icmp_code = ICMP_PORT_UNREACH;
else
stmt->reject.icmp_code = ICMP6_DST_UNREACH_NOPORT;
break;
case NFPROTO_INET:
- desc = ctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ desc = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc;
if (desc == NULL) {
stmt->reject.type = NFT_REJECT_ICMPX_UNREACH;
stmt->reject.icmp_code = NFT_REJECT_ICMPX_PORT_UNREACH;
break;
}
stmt->reject.type = NFT_REJECT_ICMP_UNREACH;
- base = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ base = pctx->protocol[PROTO_BASE_LL_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
case NFPROTO_IPV4:
+ case __constant_htons(ETH_P_IP):
stmt->reject.family = NFPROTO_IPV4;
stmt->reject.icmp_code = ICMP_PORT_UNREACH;
break;
case NFPROTO_IPV6:
+ case __constant_htons(ETH_P_IPV6):
stmt->reject.family = NFPROTO_IPV6;
stmt->reject.icmp_code = ICMP6_DST_UNREACH_NOPORT;
break;
@@ -2921,14 +3277,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):
@@ -2964,9 +3320,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)
@@ -2983,7 +3339,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;
@@ -3011,13 +3367,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) {
@@ -3027,7 +3384,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;
@@ -3056,7 +3413,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);
@@ -3080,6 +3437,11 @@ static bool nat_evaluate_addr_has_th_expr(const struct expr *map)
list_for_each_entry(i, &concat->expressions, list) {
enum proto_bases base;
+ if (i->etype == EXPR_PAYLOAD &&
+ i->payload.base == PROTO_BASE_TRANSPORT_HDR &&
+ i->payload.desc != &proto_th)
+ return true;
+
if ((i->flags & EXPR_F_PROTOCOL) == 0)
continue;
@@ -3104,7 +3466,7 @@ 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);
if (pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc == NULL &&
!nat_evaluate_addr_has_th_expr(stmt->nat.addr))
@@ -3120,16 +3482,17 @@ static int nat_evaluate_transport(struct eval_ctx *ctx, struct stmt *stmt,
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;
@@ -3139,10 +3502,11 @@ static int stmt_evaluate_addr(struct eval_ctx *ctx, struct stmt *stmt,
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) {
+ if (pctx->family == NFPROTO_INET) {
dtype = get_addr_dtype(family);
if (dtype->size == 0)
return stmt_error(ctx, stmt,
@@ -3159,6 +3523,7 @@ 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 = eval_proto_ctx(ctx);
struct expr *one, *two, *data, *tmp;
const struct datatype *dtype;
int addr_type, err;
@@ -3179,6 +3544,13 @@ static int stmt_evaluate_nat_map(struct eval_ctx *ctx, struct stmt *stmt)
if (expr_evaluate(ctx, &stmt->nat.addr))
return -1;
+ if (pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc == NULL &&
+ !nat_evaluate_addr_has_th_expr(stmt->nat.addr)) {
+ return stmt_binary_error(ctx, stmt->nat.addr, stmt,
+ "transport protocol mapping is only "
+ "valid after transport protocol match");
+ }
+
if (stmt->nat.addr->etype != EXPR_MAP)
return 0;
@@ -3243,7 +3615,8 @@ static bool nat_concat_map(struct eval_ctx *ctx, struct stmt *stmt)
if (expr_evaluate(ctx, &stmt->nat.addr->mappings))
return false;
- if (stmt->nat.addr->mappings->set->data->etype == EXPR_CONCAT) {
+ if (stmt->nat.addr->mappings->set->data->etype == EXPR_CONCAT ||
+ stmt->nat.addr->mappings->set->data->dtype->subtypes) {
stmt->nat.type_flags |= STMT_NAT_F_CONCAT;
return true;
}
@@ -3299,13 +3672,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;
@@ -3314,7 +3688,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");
@@ -3393,6 +3767,7 @@ static int stmt_evaluate_chain(struct eval_ctx *ctx, struct stmt *stmt)
struct eval_ctx rule_ctx = {
.nft = ctx->nft,
.msgs = ctx->msgs,
+ .cmd = ctx->cmd,
};
struct handle h2 = {};
@@ -3416,11 +3791,17 @@ static int stmt_evaluate_chain(struct eval_ctx *ctx, struct stmt *stmt)
return 0;
}
+static int stmt_evaluate_optstrip(struct eval_ctx *ctx, struct stmt *stmt)
+{
+ return expr_evaluate(ctx, &stmt->optstrip.expr);
+}
+
static int stmt_evaluate_dup(struct eval_ctx *ctx, struct stmt *stmt)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
int err;
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_IPV4:
case NFPROTO_IPV6:
if (stmt->dup.to == NULL)
@@ -3460,10 +3841,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,
@@ -3545,7 +3927,7 @@ 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);
@@ -3589,6 +3971,7 @@ static int stmt_evaluate_log(struct eval_ctx *ctx, struct stmt *stmt)
static int stmt_evaluate_set(struct eval_ctx *ctx, struct stmt *stmt)
{
+ struct set *this_set;
struct stmt *this;
expr_set_context(&ctx->ectx, NULL, 0);
@@ -3598,7 +3981,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,
@@ -3618,6 +4001,15 @@ static int stmt_evaluate_set(struct eval_ctx *ctx, struct stmt *stmt)
"statement must be stateful");
}
+ this_set = stmt->set.set->set;
+
+ /* Make sure EVAL flag is set on set definition so that kernel
+ * picks a set that allows updates from the packet path.
+ *
+ * Alternatively we could error out in case 'flags dynamic' was
+ * not given, but we can repair this here.
+ */
+ this_set->flags |= NFT_SET_EVAL;
return 0;
}
@@ -3632,7 +4024,7 @@ 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 (stmt_evaluate_key(ctx, stmt,
stmt->map.set->set->key->dtype,
stmt->map.set->set->key->len,
stmt->map.set->set->key->byteorder,
@@ -3657,6 +4049,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)
@@ -3701,10 +4096,15 @@ static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt)
ctx->set = mappings->set;
if (expr_evaluate(ctx, &map->mappings->set->init) < 0)
return -1;
+
+ if (set_is_interval(map->mappings->set->init->set_flags) &&
+ !(map->mappings->set->init->set_flags & NFT_SET_CONCAT) &&
+ interval_set_eval(ctx, ctx->set, map->mappings->set->init) < 0)
+ return -1;
+
ctx->set = NULL;
- map->mappings->set->flags |=
- map->mappings->set->init->set_flags;
+ map_set_concat_info(map);
/* fall through */
case EXPR_SYMBOL:
if (expr_evaluate(ctx, &map->mappings) < 0)
@@ -3815,6 +4215,8 @@ int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
return stmt_evaluate_synproxy(ctx, stmt);
case STMT_CHAIN:
return stmt_evaluate_chain(ctx, stmt);
+ case STMT_OPTSTRIP:
+ return stmt_evaluate_optstrip(ctx, stmt);
default:
BUG("unknown statement type %s\n", stmt->ops->name);
}
@@ -3836,14 +4238,24 @@ static int setelem_evaluate(struct eval_ctx *ctx, struct cmd *cmd)
return set_not_found(ctx, &ctx->cmd->handle.set.location,
ctx->cmd->handle.set.name);
+ if (set->key == NULL)
+ return -1;
+
+ set->existing_set = set;
ctx->set = set;
expr_set_context(&ctx->ectx, set->key->dtype, set->key->len);
if (expr_evaluate(ctx, &cmd->expr) < 0)
return -1;
- ctx->set = NULL;
cmd->elem.set = set_get(set);
+ if (set_is_interval(ctx->set->flags) &&
+ !(set->flags & NFT_SET_CONCAT) &&
+ interval_set_eval(ctx, ctx->set, cmd->expr) < 0)
+ return -1;
+
+ ctx->set = NULL;
+
return 0;
}
@@ -3864,8 +4276,8 @@ static int set_key_data_error(struct eval_ctx *ctx, const struct set *set,
static int set_expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
{
unsigned int flags = EXPR_F_CONSTANT | EXPR_F_SINGLETON;
+ uint32_t ntype = 0, size = 0;
struct expr *i, *next;
- uint32_t ntype = 0;
list_for_each_entry_safe(i, next, &(*expr)->expressions, list) {
unsigned dsize_bytes;
@@ -3876,32 +4288,49 @@ static int set_expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
return expr_error(ctx->msgs, i,
"specify either ip or ip6 for address matching");
- if (i->dtype->size == 0)
+ if (i->etype == EXPR_PAYLOAD &&
+ i->dtype->type == TYPE_INTEGER) {
+ struct datatype *dtype;
+
+ dtype = dtype_clone(i->dtype);
+ dtype->size = i->len;
+ dtype->byteorder = i->byteorder;
+ dtype->refcnt = 1;
+ i->dtype = dtype;
+ }
+
+ if (i->dtype->size == 0 && i->len == 0)
return expr_binary_error(ctx->msgs, i, *expr,
"can not use variable sized "
"data types (%s) in concat "
"expressions",
i->dtype->name);
+ if (i->dtype->size)
+ assert(i->len == i->dtype->size);
+
flags &= i->flags;
ntype = concat_subtype_add(ntype, i->dtype->type);
- dsize_bytes = div_round_up(i->dtype->size, BITS_PER_BYTE);
+ dsize_bytes = div_round_up(i->len, BITS_PER_BYTE);
(*expr)->field_len[(*expr)->field_count++] = dsize_bytes;
+ size += netlink_padded_len(i->len);
}
(*expr)->flags |= flags;
datatype_set(*expr, concat_type_alloc(ntype));
- (*expr)->len = (*expr)->dtype->size;
+ (*expr)->len = size;
expr_set_context(&ctx->ectx, (*expr)->dtype, (*expr)->len);
+ ctx->ectx.key = *expr;
return 0;
}
static int set_evaluate(struct eval_ctx *ctx, struct set *set)
{
+ struct set *existing_set = NULL;
unsigned int num_stmts = 0;
struct table *table;
struct stmt *stmt;
@@ -3914,7 +4343,8 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
if (table == NULL)
return table_not_found(ctx);
- if (!set_cache_find(table, set->handle.set.name))
+ existing_set = set_cache_find(table, set->handle.set.name);
+ if (!existing_set)
set_cache_add(set_get(set), table);
}
@@ -3978,9 +4408,16 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set)
if (num_stmts > 1)
set->flags |= NFT_SET_EXPR;
- if (set_is_anonymous(set->flags))
+ if (set_is_anonymous(set->flags)) {
+ if (set_is_interval(set->init->set_flags) &&
+ !(set->init->set_flags & NFT_SET_CONCAT) &&
+ interval_set_eval(ctx, set, set->init) < 0)
+ return -1;
+
return 0;
+ }
+ set->existing_set = existing_set;
ctx->set = set;
if (set->init != NULL) {
__expr_set_context(&ctx->ectx, set->key->dtype,
@@ -3991,6 +4428,12 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *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;
@@ -4097,7 +4540,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;
}
@@ -4231,7 +4674,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;
@@ -4246,6 +4691,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);
@@ -4296,6 +4743,8 @@ static uint32_t str2hooknum(uint32_t family, const char *hook)
case NFPROTO_NETDEV:
if (!strcmp(hook, "ingress"))
return NF_NETDEV_INGRESS;
+ else if (!strcmp(hook, "egress"))
+ return NF_NETDEV_EGRESS;
break;
default:
break;
@@ -4866,6 +5315,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,
@@ -5110,6 +5561,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)
@@ -5142,6 +5594,7 @@ int cmd_evaluate(struct eval_ctx *ctx, struct cmd *cmd)
case CMD_INSERT:
return cmd_evaluate_add(ctx, cmd);
case CMD_DELETE:
+ case CMD_DESTROY:
return cmd_evaluate_delete(ctx, cmd);
case CMD_GET:
return cmd_evaluate_get(ctx, cmd);
diff --git a/src/expression.c b/src/expression.c
index 4c0874fe..ce7fef09 100644
--- a/src/expression.c
+++ b/src/expression.c
@@ -18,6 +18,7 @@
#include <expression.h>
#include <statement.h>
#include <datatype.h>
+#include <netlink.h>
#include <rule.h>
#include <gmputil.h>
#include <utils.h>
@@ -28,6 +29,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;
@@ -135,12 +137,12 @@ void expr_describe(const struct expr *expr, struct output_ctx *octx)
nft_print(octx, "datatype %s (%s)",
dtype->name, dtype->desc);
len = dtype->size;
- } else if (dtype != &invalid_type) {
+ } else {
nft_print(octx, "%s expression, datatype %s (%s)",
expr_name(expr), dtype->name, dtype->desc);
- } else {
- nft_print(octx, "datatype %s is invalid\n", expr->identifier);
- return;
+
+ if (dtype == &invalid_type)
+ return;
}
if (dtype->basetype != NULL) {
@@ -268,6 +270,7 @@ static struct expr *verdict_expr_parse_udata(const struct nftnl_udata *attr)
struct expr *e;
e = symbol_expr_alloc(&internal_location, SYMBOL_VALUE, NULL, "verdict");
+ e->dtype = &verdict_type;
e->len = NFT_REG_SIZE * BITS_PER_BYTE;
return e;
}
@@ -300,8 +303,7 @@ struct expr *verdict_expr_alloc(const struct location *loc,
static void symbol_expr_print(const struct expr *expr, struct output_ctx *octx)
{
- nft_print(octx, "%s%s", expr->scope != NULL ? "$" : "",
- expr->identifier);
+ nft_print(octx, "%s", expr->identifier);
}
static void symbol_expr_clone(struct expr *new, const struct expr *expr)
@@ -878,17 +880,30 @@ static void concat_expr_print(const struct expr *expr, struct output_ctx *octx)
#define NFTNL_UDATA_SET_KEY_CONCAT_SUB_DATA 1
#define NFTNL_UDATA_SET_KEY_CONCAT_SUB_MAX 2
+static struct expr *expr_build_udata_recurse(struct expr *e)
+{
+ switch (e->etype) {
+ case EXPR_BINOP:
+ return e->left;
+ default:
+ break;
+ }
+
+ return e;
+}
+
static int concat_expr_build_udata(struct nftnl_udata_buf *udbuf,
const struct expr *concat_expr)
{
struct nftnl_udata *nest;
+ struct expr *expr, *tmp;
unsigned int i = 0;
- struct expr *expr;
- list_for_each_entry(expr, &concat_expr->expressions, list) {
+ list_for_each_entry_safe(expr, tmp, &concat_expr->expressions, list) {
struct nftnl_udata *nest_expr;
int err;
+ expr = expr_build_udata_recurse(expr);
if (!expr_ops(expr)->build_udata || i >= NFT_REG32_SIZE)
return -1;
@@ -950,7 +965,7 @@ static struct expr *concat_expr_parse_udata(const struct nftnl_udata *attr)
const struct nftnl_udata *ud[NFTNL_UDATA_SET_KEY_CONCAT_NEST_MAX] = {};
const struct datatype *dtype;
struct expr *concat_expr;
- uint32_t dt = 0;
+ uint32_t dt = 0, len = 0;
unsigned int i;
int err;
@@ -991,6 +1006,7 @@ static struct expr *concat_expr_parse_udata(const struct nftnl_udata *attr)
dt = concat_subtype_add(dt, expr->dtype->type);
compound_expr_add(concat_expr, expr);
+ len += netlink_padded_len(expr->len);
}
dtype = concat_type_alloc(dt);
@@ -998,7 +1014,7 @@ static struct expr *concat_expr_parse_udata(const struct nftnl_udata *attr)
goto err_free;
concat_expr->dtype = datatype_get(dtype);
- concat_expr->len = dtype->size;
+ concat_expr->len = len;
return concat_expr;
@@ -1186,14 +1202,40 @@ struct expr *mapping_expr_alloc(const struct location *loc,
return expr;
}
+static bool __set_expr_is_vmap(const struct expr *mappings)
+{
+ const struct expr *mapping;
+
+ if (list_empty(&mappings->expressions))
+ return false;
+
+ mapping = list_first_entry(&mappings->expressions, struct expr, list);
+ if (mapping->etype == EXPR_MAPPING &&
+ mapping->right->etype == EXPR_VERDICT)
+ return true;
+
+ return false;
+}
+
+static bool set_expr_is_vmap(const struct expr *expr)
+{
+
+ if (expr->mappings->etype == EXPR_SET)
+ return __set_expr_is_vmap(expr->mappings);
+
+ return false;
+}
+
static void map_expr_print(const struct expr *expr, struct output_ctx *octx)
{
expr_print(expr->map, octx);
- if (expr->mappings->etype == EXPR_SET_REF &&
- expr->mappings->set->data->dtype->type == TYPE_VERDICT)
+ if ((expr->mappings->etype == EXPR_SET_REF &&
+ expr->mappings->set->data->dtype->type == TYPE_VERDICT) ||
+ set_expr_is_vmap(expr))
nft_print(octx, " vmap ");
else
nft_print(octx, " map ");
+
expr_print(expr->mappings, octx);
}
@@ -1437,6 +1479,7 @@ void range_expr_value_high(mpz_t rop, const struct expr *expr)
return mpz_set(rop, expr->value);
case EXPR_PREFIX:
range_expr_value_low(rop, expr->prefix);
+ assert(expr->len >= expr->prefix_len);
mpz_init_bitmask(tmp, expr->len - expr->prefix_len);
mpz_add(rop, rop, tmp);
mpz_clear(tmp);
diff --git a/src/exthdr.c b/src/exthdr.c
index 22a08b0c..3e5f5cd8 100644
--- a/src/exthdr.c
+++ b/src/exthdr.c
@@ -46,6 +46,9 @@ static const struct exthdr_desc *exthdr_find_desc(enum exthdr_desc_id desc_id)
static void exthdr_expr_print(const struct expr *expr, struct output_ctx *octx)
{
+ const char *name = expr->exthdr.desc ?
+ expr->exthdr.desc->name : "unknown-exthdr";
+
if (expr->exthdr.op == NFT_EXTHDR_OP_TCPOPT) {
/* Offset calculation is a bit hacky at this point.
* There might be a tcp option one day with another
@@ -65,14 +68,14 @@ static void exthdr_expr_print(const struct expr *expr, struct output_ctx *octx)
return;
}
- nft_print(octx, "tcp option %s", expr->exthdr.desc->name);
+ nft_print(octx, "tcp option %s", name);
if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
return;
if (offset)
nft_print(octx, "%d", offset);
nft_print(octx, " %s", expr->exthdr.tmpl->token);
} else if (expr->exthdr.op == NFT_EXTHDR_OP_IPV4) {
- nft_print(octx, "ip option %s", expr->exthdr.desc->name);
+ nft_print(octx, "ip option %s", name);
if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
return;
nft_print(octx, " %s", expr->exthdr.tmpl->token);
@@ -83,10 +86,9 @@ static void exthdr_expr_print(const struct expr *expr, struct output_ctx *octx)
nft_print(octx, " %s", expr->exthdr.tmpl->token);
} else {
if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
- nft_print(octx, "exthdr %s", expr->exthdr.desc->name);
+ nft_print(octx, "exthdr %s", name);
else {
- nft_print(octx, "%s %s",
- expr->exthdr.desc ? expr->exthdr.desc->name : "unknown-exthdr",
+ nft_print(octx, "%s %s", name,
expr->exthdr.tmpl->token);
}
}
@@ -113,7 +115,8 @@ static void exthdr_expr_clone(struct expr *new, const struct expr *expr)
#define NFTNL_UDATA_EXTHDR_DESC 0
#define NFTNL_UDATA_EXTHDR_TYPE 1
-#define NFTNL_UDATA_EXTHDR_MAX 2
+#define NFTNL_UDATA_EXTHDR_OP 2
+#define NFTNL_UDATA_EXTHDR_MAX 3
static int exthdr_parse_udata(const struct nftnl_udata *attr, void *data)
{
@@ -124,6 +127,7 @@ static int exthdr_parse_udata(const struct nftnl_udata *attr, void *data)
switch (type) {
case NFTNL_UDATA_EXTHDR_DESC:
case NFTNL_UDATA_EXTHDR_TYPE:
+ case NFTNL_UDATA_EXTHDR_OP:
if (len != sizeof(uint32_t))
return -1;
break;
@@ -138,6 +142,7 @@ static int exthdr_parse_udata(const struct nftnl_udata *attr, void *data)
static struct expr *exthdr_expr_parse_udata(const struct nftnl_udata *attr)
{
const struct nftnl_udata *ud[NFTNL_UDATA_EXTHDR_MAX + 1] = {};
+ enum nft_exthdr_op op = NFT_EXTHDR_OP_IPV6;
const struct exthdr_desc *desc;
unsigned int type;
uint32_t desc_id;
@@ -152,22 +157,37 @@ static struct expr *exthdr_expr_parse_udata(const struct nftnl_udata *attr)
!ud[NFTNL_UDATA_EXTHDR_TYPE])
return NULL;
- desc_id = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_DESC]);
- desc = exthdr_find_desc(desc_id);
- if (!desc)
- return NULL;
+ if (ud[NFTNL_UDATA_EXTHDR_OP])
+ op = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_OP]);
+ desc_id = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_DESC]);
type = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_TYPE]);
- return exthdr_expr_alloc(&internal_location, desc, type);
+ switch (op) {
+ case NFT_EXTHDR_OP_IPV6:
+ desc = exthdr_find_desc(desc_id);
+
+ return exthdr_expr_alloc(&internal_location, desc, type);
+ case NFT_EXTHDR_OP_TCPOPT:
+ return tcpopt_expr_alloc(&internal_location,
+ desc_id, type);
+ case NFT_EXTHDR_OP_IPV4:
+ return ipopt_expr_alloc(&internal_location,
+ desc_id, type);
+ case NFT_EXTHDR_OP_SCTP:
+ return sctp_chunk_expr_alloc(&internal_location,
+ desc_id, type);
+ case __NFT_EXTHDR_OP_MAX:
+ return NULL;
+ }
+
+ return NULL;
}
static unsigned int expr_exthdr_type(const struct exthdr_desc *desc,
const struct proto_hdr_template *tmpl)
{
- unsigned int offset = (unsigned int)(tmpl - &desc->templates[0]);
-
- return offset / sizeof(*tmpl);
+ return (unsigned int)(tmpl - &desc->templates[0]);
}
static int exthdr_expr_build_udata(struct nftnl_udata_buf *udbuf,
@@ -176,9 +196,22 @@ static int exthdr_expr_build_udata(struct nftnl_udata_buf *udbuf,
const struct proto_hdr_template *tmpl = expr->exthdr.tmpl;
const struct exthdr_desc *desc = expr->exthdr.desc;
unsigned int type = expr_exthdr_type(desc, tmpl);
+ enum nft_exthdr_op op = expr->exthdr.op;
- nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_DESC, desc->id);
nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_TYPE, type);
+ switch (op) {
+ case NFT_EXTHDR_OP_IPV6:
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_DESC, desc->id);
+ break;
+ case NFT_EXTHDR_OP_TCPOPT:
+ case NFT_EXTHDR_OP_IPV4:
+ case NFT_EXTHDR_OP_SCTP:
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_OP, op);
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_DESC, expr->exthdr.raw_type);
+ break;
+ default:
+ return -1;
+ }
return 0;
}
@@ -348,16 +381,7 @@ static unsigned int mask_length(const struct expr *mask)
bool exthdr_find_template(struct expr *expr, const struct expr *mask, unsigned int *shift)
{
unsigned int off, mask_offset, mask_len;
-
- if (expr->exthdr.op != NFT_EXTHDR_OP_IPV4 &&
- expr->exthdr.tmpl != &exthdr_unknown_template)
- return false;
-
- /* In case we are handling tcp options instead of the default ipv6
- * extension headers.
- */
- if (expr->exthdr.op == NFT_EXTHDR_OP_TCPOPT)
- return tcpopt_find_template(expr, mask, shift);
+ bool found;
mask_offset = mpz_scan1(mask->value, 0);
mask_len = mask_length(mask);
@@ -366,24 +390,31 @@ bool exthdr_find_template(struct expr *expr, const struct expr *mask, unsigned i
off += round_up(mask->len, BITS_PER_BYTE) - mask_len;
/* Handle ip options after the offset and mask have been calculated. */
- if (expr->exthdr.op == NFT_EXTHDR_OP_IPV4) {
- if (ipopt_find_template(expr, off, mask_len - mask_offset)) {
- *shift = mask_offset;
- return true;
- } else {
+ switch (expr->exthdr.op) {
+ case NFT_EXTHDR_OP_IPV4:
+ found = ipopt_find_template(expr, off, mask_len - mask_offset);
+ break;
+ case NFT_EXTHDR_OP_TCPOPT:
+ found = tcpopt_find_template(expr, off, mask_len - mask_offset);
+ break;
+ case NFT_EXTHDR_OP_IPV6:
+ exthdr_init_raw(expr, expr->exthdr.desc->type,
+ off, mask_len - mask_offset, expr->exthdr.op, 0);
+
+ /* still failed to find a template... Bug. */
+ if (expr->exthdr.tmpl == &exthdr_unknown_template)
return false;
- }
+ found = true;
+ break;
+ default:
+ found = false;
+ break;
}
- exthdr_init_raw(expr, expr->exthdr.desc->type,
- off, mask_len - mask_offset, expr->exthdr.op, 0);
-
- /* still failed to find a template... Bug. */
- if (expr->exthdr.tmpl == &exthdr_unknown_template)
- return false;
+ if (found)
+ *shift = mask_offset;
- *shift = mask_offset;
- return true;
+ return found;
}
#define HDR_TEMPLATE(__name, __dtype, __type, __member) \
diff --git a/src/fib.c b/src/fib.c
index c6ad0f9c..98c57868 100644
--- a/src/fib.c
+++ b/src/fib.c
@@ -4,8 +4,8 @@
* 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 <nftables.h>
diff --git a/src/hash.c b/src/hash.c
index 42c50407..a3fd0872 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -4,8 +4,8 @@
* 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 <nftables.h>
diff --git a/src/iface.c b/src/iface.c
index d0e1834c..3647778c 100644
--- a/src/iface.c
+++ b/src/iface.c
@@ -2,8 +2,8 @@
* 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 <stdio.h>
@@ -59,13 +59,13 @@ static int data_cb(const struct nlmsghdr *nlh, void *data)
return MNL_CB_OK;
}
-void iface_cache_update(void)
+static int iface_mnl_talk(struct mnl_socket *nl, uint32_t portid)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
- struct mnl_socket *nl;
struct nlmsghdr *nlh;
struct rtgenmsg *rt;
- uint32_t seq, portid;
+ bool eintr = false;
+ uint32_t seq;
int ret;
nlh = mnl_nlmsg_put_header(buf);
@@ -75,6 +75,38 @@ void iface_cache_update(void)
rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
rt->rtgen_family = AF_PACKET;
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
+ return -1;
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL);
+ if (ret == 0)
+ break;
+ if (ret < 0) {
+ if (errno != EINTR)
+ return ret;
+
+ /* process all pending messages before reporting EINTR */
+ eintr = true;
+ }
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+
+ if (eintr) {
+ ret = -1;
+ errno = EINTR;
+ }
+
+ return ret;
+}
+
+void iface_cache_update(void)
+{
+ struct mnl_socket *nl;
+ uint32_t portid;
+ int ret;
+
nl = mnl_socket_open(NETLINK_ROUTE);
if (nl == NULL)
netlink_init_error();
@@ -84,16 +116,10 @@ void iface_cache_update(void)
portid = mnl_socket_get_portid(nl);
- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
- netlink_init_error();
+ do {
+ ret = iface_mnl_talk(nl, portid);
+ } while (ret < 0 && errno == EINTR);
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- while (ret > 0) {
- ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL);
- if (ret <= MNL_CB_STOP)
- break;
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- }
if (ret == -1)
netlink_init_error();
diff --git a/src/intervals.c b/src/intervals.c
new file mode 100644
index 00000000..95e25cf0
--- /dev/null
+++ b/src/intervals.c
@@ -0,0 +1,748 @@
+/*
+ * Copyright (c) 2022 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
+ */
+
+#include <nftables.h>
+#include <expression.h>
+#include <intervals.h>
+#include <rule.h>
+
+static void setelem_expr_to_range(struct expr *expr)
+{
+ unsigned char data[sizeof(struct in6_addr) * BITS_PER_BYTE];
+ struct expr *key, *value;
+ mpz_t rop;
+
+ assert(expr->etype == EXPR_SET_ELEM);
+
+ switch (expr->key->etype) {
+ case EXPR_SET_ELEM_CATCHALL:
+ case EXPR_RANGE:
+ break;
+ case EXPR_PREFIX:
+ mpz_init(rop);
+ mpz_bitmask(rop, expr->key->len - expr->key->prefix_len);
+ if (expr_basetype(expr)->type == TYPE_STRING)
+ mpz_switch_byteorder(expr->key->prefix->value, expr->len / BITS_PER_BYTE);
+
+ mpz_ior(rop, rop, expr->key->prefix->value);
+ mpz_export_data(data, rop, expr->key->prefix->byteorder,
+ expr->key->prefix->len / BITS_PER_BYTE);
+ mpz_clear(rop);
+ value = constant_expr_alloc(&expr->location,
+ expr->key->prefix->dtype,
+ expr->key->prefix->byteorder,
+ expr->key->prefix->len, data);
+ key = range_expr_alloc(&expr->location,
+ expr_get(expr->key->prefix),
+ value);
+ expr_free(expr->key);
+ expr->key = key;
+ break;
+ case EXPR_VALUE:
+ if (expr_basetype(expr)->type == TYPE_STRING)
+ mpz_switch_byteorder(expr->key->value, expr->len / BITS_PER_BYTE);
+
+ key = range_expr_alloc(&expr->location,
+ expr_clone(expr->key),
+ expr_get(expr->key));
+ expr_free(expr->key);
+ expr->key = key;
+ break;
+ default:
+ BUG("unhandled key type %d\n", expr->key->etype);
+ }
+}
+
+struct set_automerge_ctx {
+ struct set *set;
+ struct expr *init;
+ struct expr *purge;
+ unsigned int debug_mask;
+};
+
+static void purge_elem(struct set_automerge_ctx *ctx, struct expr *i)
+{
+ if (ctx->debug_mask & NFT_DEBUG_SEGTREE) {
+ pr_gmp_debug("remove: [%Zx-%Zx]\n",
+ i->key->left->value,
+ i->key->right->value);
+ }
+ list_move_tail(&i->list, &ctx->purge->expressions);
+}
+
+static void remove_overlapping_range(struct set_automerge_ctx *ctx,
+ struct expr *prev, struct expr *i)
+{
+ if (i->flags & EXPR_F_KERNEL) {
+ purge_elem(ctx, i);
+ return;
+ }
+ list_del(&i->list);
+ expr_free(i);
+ ctx->init->size--;
+}
+
+struct range {
+ mpz_t low;
+ mpz_t high;
+};
+
+static bool merge_ranges(struct set_automerge_ctx *ctx,
+ struct expr *prev, struct expr *i,
+ struct range *prev_range, struct range *range)
+{
+ if (prev->flags & EXPR_F_KERNEL) {
+ purge_elem(ctx, prev);
+ expr_free(i->key->left);
+ i->key->left = expr_get(prev->key->left);
+ mpz_set(prev_range->high, range->high);
+ return true;
+ } else if (i->flags & EXPR_F_KERNEL) {
+ purge_elem(ctx, i);
+ expr_free(prev->key->right);
+ prev->key->right = expr_get(i->key->right);
+ mpz_set(prev_range->high, range->high);
+ } else {
+ expr_free(prev->key->right);
+ prev->key->right = expr_get(i->key->right);
+ mpz_set(prev_range->high, range->high);
+ list_del(&i->list);
+ expr_free(i);
+ ctx->init->size--;
+ }
+ return false;
+}
+
+static void set_sort_splice(struct expr *init, struct set *set)
+{
+ struct set *existing_set = set->existing_set;
+
+ set_to_range(init);
+ list_expr_sort(&init->expressions);
+
+ if (!existing_set)
+ return;
+
+ if (existing_set->init) {
+ set_to_range(existing_set->init);
+ list_splice_sorted(&existing_set->init->expressions,
+ &init->expressions);
+ init_list_head(&existing_set->init->expressions);
+ } else {
+ existing_set->init = set_expr_alloc(&internal_location, set);
+ }
+}
+
+static void setelem_automerge(struct set_automerge_ctx *ctx)
+{
+ struct expr *i, *next, *prev = NULL;
+ struct range range, prev_range;
+ mpz_t rop;
+
+ mpz_init(prev_range.low);
+ mpz_init(prev_range.high);
+ mpz_init(range.low);
+ mpz_init(range.high);
+ mpz_init(rop);
+
+ list_for_each_entry_safe(i, next, &ctx->init->expressions, list) {
+ if (i->key->etype == EXPR_SET_ELEM_CATCHALL)
+ continue;
+
+ range_expr_value_low(range.low, i);
+ range_expr_value_high(range.high, i);
+
+ if (!prev) {
+ prev = i;
+ mpz_set(prev_range.low, range.low);
+ mpz_set(prev_range.high, range.high);
+ continue;
+ }
+
+ if (mpz_cmp(prev_range.low, range.low) <= 0 &&
+ mpz_cmp(prev_range.high, range.high) >= 0) {
+ remove_overlapping_range(ctx, prev, i);
+ continue;
+ } else if (mpz_cmp(range.low, prev_range.high) <= 0) {
+ if (merge_ranges(ctx, prev, i, &prev_range, &range))
+ prev = i;
+ continue;
+ } else if (ctx->set->automerge) {
+ mpz_sub(rop, range.low, prev_range.high);
+ /* two contiguous ranges */
+ if (mpz_cmp_ui(rop, 1) == 0) {
+ if (merge_ranges(ctx, prev, i, &prev_range, &range))
+ prev = i;
+ continue;
+ }
+ }
+
+ prev = i;
+ mpz_set(prev_range.low, range.low);
+ mpz_set(prev_range.high, range.high);
+ }
+
+ mpz_clear(prev_range.low);
+ mpz_clear(prev_range.high);
+ mpz_clear(range.low);
+ mpz_clear(range.high);
+ mpz_clear(rop);
+}
+
+static struct expr *interval_expr_key(struct expr *i)
+{
+ struct expr *elem;
+
+ switch (i->etype) {
+ case EXPR_MAPPING:
+ elem = i->left;
+ break;
+ case EXPR_SET_ELEM:
+ elem = i;
+ break;
+ default:
+ BUG("unhandled expression type %d\n", i->etype);
+ return NULL;
+ }
+
+ return elem;
+}
+
+void set_to_range(struct expr *init)
+{
+ struct expr *i, *elem;
+
+ list_for_each_entry(i, &init->expressions, list) {
+ elem = interval_expr_key(i);
+ setelem_expr_to_range(elem);
+ }
+}
+
+int set_automerge(struct list_head *msgs, struct cmd *cmd, struct set *set,
+ struct expr *init, unsigned int debug_mask)
+{
+ struct set *existing_set = set->existing_set;
+ struct set_automerge_ctx ctx = {
+ .set = set,
+ .init = init,
+ .debug_mask = debug_mask,
+ };
+ struct expr *i, *next, *clone;
+ struct cmd *purge_cmd;
+ struct handle h = {};
+
+ if (set->flags & NFT_SET_MAP) {
+ set_to_range(init);
+ list_expr_sort(&init->expressions);
+ return 0;
+ }
+
+ set_sort_splice(init, set);
+
+ ctx.purge = set_expr_alloc(&internal_location, set);
+
+ setelem_automerge(&ctx);
+
+ list_for_each_entry_safe(i, next, &init->expressions, list) {
+ if (i->flags & EXPR_F_KERNEL) {
+ list_move_tail(&i->list, &existing_set->init->expressions);
+ } else if (existing_set) {
+ if (debug_mask & NFT_DEBUG_SEGTREE) {
+ pr_gmp_debug("add: [%Zx-%Zx]\n",
+ i->key->left->value, i->key->right->value);
+ }
+ clone = expr_clone(i);
+ clone->flags |= EXPR_F_KERNEL;
+ list_add_tail(&clone->list, &existing_set->init->expressions);
+ }
+ }
+
+ if (list_empty(&ctx.purge->expressions)) {
+ expr_free(ctx.purge);
+ return 0;
+ }
+
+ handle_merge(&h, &set->handle);
+ purge_cmd = cmd_alloc(CMD_DELETE, CMD_OBJ_ELEMENTS, &h, &init->location, ctx.purge);
+ purge_cmd->elem.set = set_get(set);
+ list_add_tail(&purge_cmd->list, &cmd->list);
+
+ return 0;
+}
+
+static void remove_elem(struct expr *prev, struct set *set, struct expr *purge)
+{
+ struct expr *clone;
+
+ if (prev->flags & EXPR_F_KERNEL) {
+ clone = expr_clone(prev);
+ list_move_tail(&clone->list, &purge->expressions);
+ }
+}
+
+static void __adjust_elem_left(struct set *set, struct expr *prev, struct expr *i)
+{
+ prev->flags &= ~EXPR_F_KERNEL;
+ expr_free(prev->key->left);
+ prev->key->left = expr_get(i->key->right);
+ mpz_add_ui(prev->key->left->value, prev->key->left->value, 1);
+ list_move(&prev->list, &set->existing_set->init->expressions);
+}
+
+static void adjust_elem_left(struct set *set, struct expr *prev, struct expr *i,
+ struct expr *purge)
+{
+ remove_elem(prev, set, purge);
+ __adjust_elem_left(set, prev, i);
+
+ list_del(&i->list);
+ expr_free(i);
+}
+
+static void __adjust_elem_right(struct set *set, struct expr *prev, struct expr *i)
+{
+ prev->flags &= ~EXPR_F_KERNEL;
+ expr_free(prev->key->right);
+ prev->key->right = expr_get(i->key->left);
+ mpz_sub_ui(prev->key->right->value, prev->key->right->value, 1);
+ list_move(&prev->list, &set->existing_set->init->expressions);
+}
+
+static void adjust_elem_right(struct set *set, struct expr *prev, struct expr *i,
+ struct expr *purge)
+{
+ remove_elem(prev, set, purge);
+ __adjust_elem_right(set, prev, i);
+
+ list_del(&i->list);
+ expr_free(i);
+}
+
+static void split_range(struct set *set, struct expr *prev, struct expr *i,
+ struct expr *purge)
+{
+ struct expr *clone;
+
+ if (prev->flags & EXPR_F_KERNEL) {
+ clone = expr_clone(prev);
+ list_move_tail(&clone->list, &purge->expressions);
+ }
+
+ prev->flags &= ~EXPR_F_KERNEL;
+ clone = expr_clone(prev);
+ expr_free(clone->key->left);
+ clone->key->left = expr_get(i->key->right);
+ mpz_add_ui(clone->key->left->value, i->key->right->value, 1);
+ list_add_tail(&clone->list, &set->existing_set->init->expressions);
+
+ expr_free(prev->key->right);
+ prev->key->right = expr_get(i->key->left);
+ mpz_sub_ui(prev->key->right->value, i->key->left->value, 1);
+ list_move(&prev->list, &set->existing_set->init->expressions);
+
+ list_del(&i->list);
+ expr_free(i);
+}
+
+static int setelem_adjust(struct set *set, struct expr *purge,
+ struct range *prev_range, struct range *range,
+ struct expr *prev, struct expr *i)
+{
+ if (mpz_cmp(prev_range->low, range->low) == 0 &&
+ mpz_cmp(prev_range->high, range->high) > 0) {
+ if (i->flags & EXPR_F_REMOVE)
+ adjust_elem_left(set, prev, i, purge);
+ } else if (mpz_cmp(prev_range->low, range->low) < 0 &&
+ mpz_cmp(prev_range->high, range->high) == 0) {
+ if (i->flags & EXPR_F_REMOVE)
+ adjust_elem_right(set, prev, i, purge);
+ } else if (mpz_cmp(prev_range->low, range->low) < 0 &&
+ mpz_cmp(prev_range->high, range->high) > 0) {
+ if (i->flags & EXPR_F_REMOVE)
+ split_range(set, prev, i, purge);
+ } else {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int setelem_delete(struct list_head *msgs, struct set *set,
+ struct expr *purge, struct expr *elems,
+ unsigned int debug_mask)
+{
+ struct expr *i, *next, *prev = NULL;
+ struct range range, prev_range;
+ int err = 0;
+ mpz_t rop;
+
+ mpz_init(prev_range.low);
+ mpz_init(prev_range.high);
+ mpz_init(range.low);
+ mpz_init(range.high);
+ mpz_init(rop);
+
+ list_for_each_entry_safe(i, next, &elems->expressions, list) {
+ if (i->key->etype == EXPR_SET_ELEM_CATCHALL)
+ continue;
+
+ range_expr_value_low(range.low, i);
+ range_expr_value_high(range.high, i);
+
+ if (!prev && i->flags & EXPR_F_REMOVE) {
+ expr_error(msgs, i, "element does not exist");
+ err = -1;
+ goto err;
+ }
+
+ if (!(i->flags & EXPR_F_REMOVE)) {
+ prev = i;
+ mpz_set(prev_range.low, range.low);
+ mpz_set(prev_range.high, range.high);
+ continue;
+ }
+
+ if (mpz_cmp(prev_range.low, range.low) == 0 &&
+ mpz_cmp(prev_range.high, range.high) == 0) {
+ if (i->flags & EXPR_F_REMOVE) {
+ if (prev->flags & EXPR_F_KERNEL)
+ list_move_tail(&prev->list, &purge->expressions);
+
+ list_del(&i->list);
+ expr_free(i);
+ }
+ } else if (set->automerge) {
+ if (setelem_adjust(set, purge, &prev_range, &range, prev, i) < 0) {
+ expr_error(msgs, i, "element does not exist");
+ err = -1;
+ goto err;
+ }
+ } else if (i->flags & EXPR_F_REMOVE) {
+ expr_error(msgs, i, "element does not exist");
+ err = -1;
+ goto err;
+ }
+ prev = NULL;
+ }
+err:
+ mpz_clear(prev_range.low);
+ mpz_clear(prev_range.high);
+ mpz_clear(range.low);
+ mpz_clear(range.high);
+ mpz_clear(rop);
+
+ return err;
+}
+
+static void automerge_delete(struct list_head *msgs, struct set *set,
+ struct expr *init, unsigned int debug_mask)
+{
+ struct set_automerge_ctx ctx = {
+ .set = set,
+ .init = init,
+ .debug_mask = debug_mask,
+ };
+
+ ctx.purge = set_expr_alloc(&internal_location, set);
+ list_expr_sort(&init->expressions);
+ setelem_automerge(&ctx);
+ expr_free(ctx.purge);
+}
+
+static int __set_delete(struct list_head *msgs, struct expr *i, struct set *set,
+ struct expr *init, struct set *existing_set,
+ unsigned int debug_mask)
+{
+ i->flags |= EXPR_F_REMOVE;
+ list_move(&i->list, &existing_set->init->expressions);
+ list_expr_sort(&existing_set->init->expressions);
+
+ return setelem_delete(msgs, set, init, existing_set->init, debug_mask);
+}
+
+/* detection for unexisting intervals already exists in Linux kernels >= 5.7. */
+int set_delete(struct list_head *msgs, struct cmd *cmd, struct set *set,
+ struct expr *init, unsigned int debug_mask)
+{
+ struct set *existing_set = set->existing_set;
+ struct expr *i, *next, *add, *clone;
+ struct handle h = {};
+ struct cmd *add_cmd;
+ LIST_HEAD(del_list);
+ int err;
+
+ set_to_range(init);
+ if (set->automerge)
+ automerge_delete(msgs, set, init, debug_mask);
+
+ if (existing_set->init) {
+ set_to_range(existing_set->init);
+ } else {
+ existing_set->init = set_expr_alloc(&internal_location, set);
+ }
+
+ list_splice_init(&init->expressions, &del_list);
+
+ list_for_each_entry_safe(i, next, &del_list, list) {
+ err = __set_delete(msgs, i, set, init, existing_set, debug_mask);
+ if (err < 0) {
+ list_splice(&del_list, &init->expressions);
+ return err;
+ }
+ }
+
+ add = set_expr_alloc(&internal_location, set);
+ list_for_each_entry(i, &existing_set->init->expressions, list) {
+ if (!(i->flags & EXPR_F_KERNEL)) {
+ clone = expr_clone(i);
+ list_add_tail(&clone->list, &add->expressions);
+ i->flags |= EXPR_F_KERNEL;
+ }
+ }
+
+ if (debug_mask & NFT_DEBUG_SEGTREE) {
+ list_for_each_entry(i, &init->expressions, list)
+ pr_gmp_debug("remove: [%Zx-%Zx]\n",
+ i->key->left->value, i->key->right->value);
+ list_for_each_entry(i, &add->expressions, list)
+ pr_gmp_debug("add: [%Zx-%Zx]\n",
+ i->key->left->value, i->key->right->value);
+ list_for_each_entry(i, &existing_set->init->expressions, list)
+ pr_gmp_debug("existing: [%Zx-%Zx]\n",
+ i->key->left->value, i->key->right->value);
+ }
+
+ if (list_empty(&add->expressions)) {
+ expr_free(add);
+ return 0;
+ }
+
+ handle_merge(&h, &cmd->handle);
+ add_cmd = cmd_alloc(CMD_ADD, CMD_OBJ_ELEMENTS, &h, &cmd->location, add);
+ add_cmd->elem.set = set_get(set);
+ list_add(&add_cmd->list, &cmd->list);
+
+ return 0;
+}
+
+static int setelem_overlap(struct list_head *msgs, struct set *set,
+ struct expr *init)
+{
+ struct expr *i, *next, *elem, *prev = NULL;
+ struct range range, prev_range;
+ int err = 0;
+ mpz_t rop;
+
+ mpz_init(prev_range.low);
+ mpz_init(prev_range.high);
+ mpz_init(range.low);
+ mpz_init(range.high);
+ mpz_init(rop);
+
+ list_for_each_entry_safe(elem, next, &init->expressions, list) {
+ i = interval_expr_key(elem);
+
+ if (i->key->etype == EXPR_SET_ELEM_CATCHALL)
+ continue;
+
+ range_expr_value_low(range.low, i);
+ range_expr_value_high(range.high, i);
+
+ if (!prev) {
+ prev = elem;
+ mpz_set(prev_range.low, range.low);
+ mpz_set(prev_range.high, range.high);
+ continue;
+ }
+
+ if (mpz_cmp(prev_range.low, range.low) == 0 &&
+ mpz_cmp(prev_range.high, range.high) == 0)
+ goto next;
+
+ if (mpz_cmp(prev_range.low, range.low) <= 0 &&
+ mpz_cmp(prev_range.high, range.high) >= 0) {
+ if (prev->flags & EXPR_F_KERNEL)
+ expr_error(msgs, i, "interval overlaps with an existing one");
+ else if (elem->flags & EXPR_F_KERNEL)
+ expr_error(msgs, prev, "interval overlaps with an existing one");
+ else
+ expr_binary_error(msgs, i, prev,
+ "conflicting intervals specified");
+ err = -1;
+ goto err_out;
+ } else if (mpz_cmp(range.low, prev_range.high) <= 0) {
+ if (prev->flags & EXPR_F_KERNEL)
+ expr_error(msgs, i, "interval overlaps with an existing one");
+ else if (elem->flags & EXPR_F_KERNEL)
+ expr_error(msgs, prev, "interval overlaps with an existing one");
+ else
+ expr_binary_error(msgs, i, prev,
+ "conflicting intervals specified");
+ err = -1;
+ goto err_out;
+ }
+next:
+ prev = elem;
+ mpz_set(prev_range.low, range.low);
+ mpz_set(prev_range.high, range.high);
+ }
+
+err_out:
+ mpz_clear(prev_range.low);
+ mpz_clear(prev_range.high);
+ mpz_clear(range.low);
+ mpz_clear(range.high);
+ mpz_clear(rop);
+
+ return err;
+}
+
+/* overlap detection for intervals already exists in Linux kernels >= 5.7. */
+int set_overlap(struct list_head *msgs, struct set *set, struct expr *init)
+{
+ struct set *existing_set = set->existing_set;
+ struct expr *i, *n, *clone;
+ int err;
+
+ set_sort_splice(init, set);
+
+ err = setelem_overlap(msgs, set, init);
+
+ list_for_each_entry_safe(i, n, &init->expressions, list) {
+ if (i->flags & EXPR_F_KERNEL)
+ list_move_tail(&i->list, &existing_set->init->expressions);
+ else if (existing_set) {
+ clone = expr_clone(i);
+ clone->flags |= EXPR_F_KERNEL;
+ list_add_tail(&clone->list, &existing_set->init->expressions);
+ }
+ }
+
+ return err;
+}
+
+static bool segtree_needs_first_segment(const struct set *set,
+ const struct expr *init, bool add)
+{
+ if (add && !set->root) {
+ /* Add the first segment in four situations:
+ *
+ * 1) This is an anonymous set.
+ * 2) This set exists and it is empty.
+ * 3) New empty set and, separately, new elements are added.
+ * 4) This set is created with a number of initial elements.
+ */
+ if ((set_is_anonymous(set->flags)) ||
+ (set->init && set->init->size == 0) ||
+ (set->init == NULL && init) ||
+ (set->init == init)) {
+ return true;
+ }
+ }
+ /* This is an update for a set that already contains elements, so don't
+ * add the first non-matching elements otherwise we hit EEXIST.
+ */
+ return false;
+}
+
+int set_to_intervals(const struct set *set, struct expr *init, bool add)
+{
+ struct expr *i, *n, *prev = NULL, *elem, *newelem = NULL, *root, *expr;
+ LIST_HEAD(intervals);
+ uint32_t flags;
+ mpz_t p, q;
+
+ mpz_init2(p, set->key->len);
+ mpz_init2(q, set->key->len);
+
+ list_for_each_entry_safe(i, n, &init->expressions, list) {
+ flags = 0;
+
+ elem = interval_expr_key(i);
+
+ if (elem->key->etype == EXPR_SET_ELEM_CATCHALL)
+ continue;
+
+ if (!prev && segtree_needs_first_segment(set, init, add) &&
+ mpz_cmp_ui(elem->key->left->value, 0)) {
+ mpz_set_ui(p, 0);
+ expr = constant_expr_alloc(&internal_location,
+ set->key->dtype,
+ set->key->byteorder,
+ set->key->len, NULL);
+ mpz_set(expr->value, p);
+ root = set_elem_expr_alloc(&internal_location, expr);
+ if (i->etype == EXPR_MAPPING) {
+ root = mapping_expr_alloc(&internal_location,
+ root,
+ expr_get(i->right));
+ }
+ root->flags |= EXPR_F_INTERVAL_END;
+ list_add(&root->list, &intervals);
+ init->size++;
+ }
+
+ if (newelem) {
+ mpz_set(p, interval_expr_key(newelem)->key->value);
+ if (set->key->byteorder == BYTEORDER_HOST_ENDIAN)
+ mpz_switch_byteorder(p, set->key->len / BITS_PER_BYTE);
+
+ if (!(set->flags & NFT_SET_ANONYMOUS) ||
+ mpz_cmp(p, elem->key->left->value) != 0)
+ list_add_tail(&newelem->list, &intervals);
+ else
+ expr_free(newelem);
+ }
+ newelem = NULL;
+
+ if (mpz_scan0(elem->key->right->value, 0) != set->key->len) {
+ mpz_add_ui(p, elem->key->right->value, 1);
+ expr = constant_expr_alloc(&elem->key->location, set->key->dtype,
+ set->key->byteorder, set->key->len,
+ NULL);
+ mpz_set(expr->value, p);
+ if (set->key->byteorder == BYTEORDER_HOST_ENDIAN)
+ mpz_switch_byteorder(expr->value, set->key->len / BITS_PER_BYTE);
+
+ newelem = set_elem_expr_alloc(&internal_location, expr);
+ if (i->etype == EXPR_MAPPING) {
+ newelem = mapping_expr_alloc(&internal_location,
+ newelem,
+ expr_get(i->right));
+ }
+ newelem->flags |= EXPR_F_INTERVAL_END;
+ } else {
+ flags = NFTNL_SET_ELEM_F_INTERVAL_OPEN;
+ }
+
+ expr = constant_expr_alloc(&elem->key->location, set->key->dtype,
+ set->key->byteorder, set->key->len, NULL);
+
+ mpz_set(expr->value, elem->key->left->value);
+ if (set->key->byteorder == BYTEORDER_HOST_ENDIAN)
+ mpz_switch_byteorder(expr->value, set->key->len / BITS_PER_BYTE);
+
+ expr_free(elem->key);
+ elem->key = expr;
+ i->elem_flags |= flags;
+ init->size++;
+ list_move_tail(&i->list, &intervals);
+
+ prev = i;
+ }
+
+ if (newelem)
+ list_add_tail(&newelem->list, &intervals);
+
+ list_splice_init(&intervals, &init->expressions);
+
+ mpz_clear(p);
+ mpz_clear(q);
+
+ return 0;
+}
diff --git a/src/ipopt.c b/src/ipopt.c
index 5f9f908c..67e904ff 100644
--- a/src/ipopt.c
+++ b/src/ipopt.c
@@ -66,27 +66,8 @@ const struct exthdr_desc *ipopt_protocols[UINT8_MAX] = {
[IPOPT_RA] = &ipopt_ra,
};
-static unsigned int calc_offset(const struct exthdr_desc *desc,
- const struct proto_hdr_template *tmpl,
- unsigned int arg)
-{
- if (!desc || tmpl == &ipopt_unknown_template)
- return 0;
-
- switch (desc->type) {
- case IPOPT_RR:
- case IPOPT_LSRR:
- case IPOPT_SSRR:
- if (tmpl == &desc->templates[IPOPT_FIELD_ADDR_0])
- return (tmpl->offset < 24) ? 0 : arg;
- return 0;
- default:
- return 0;
- }
-}
-
struct expr *ipopt_expr_alloc(const struct location *loc, uint8_t type,
- uint8_t field, uint8_t ptr)
+ uint8_t field)
{
const struct proto_hdr_template *tmpl;
const struct exthdr_desc *desc;
@@ -97,12 +78,15 @@ struct expr *ipopt_expr_alloc(const struct location *loc, uint8_t type,
if (!tmpl)
return NULL;
+ if (!tmpl->len)
+ return NULL;
+
expr = expr_alloc(loc, EXPR_EXTHDR, tmpl->dtype,
BYTEORDER_BIG_ENDIAN, tmpl->len);
expr->exthdr.desc = desc;
expr->exthdr.tmpl = tmpl;
expr->exthdr.op = NFT_EXTHDR_OP_IPV4;
- expr->exthdr.offset = tmpl->offset + calc_offset(desc, tmpl, ptr);
+ expr->exthdr.offset = tmpl->offset;
expr->exthdr.raw_type = desc->type;
return expr;
diff --git a/src/json.c b/src/json.c
index 63b325af..f15461d3 100644
--- a/src/json.c
+++ b/src/json.c
@@ -1,3 +1,11 @@
+/*
+ * 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.
+ */
+
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
@@ -77,6 +85,47 @@ static json_t *set_dtype_json(const struct expr *key)
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;
+}
+
static json_t *set_print_json(struct output_ctx *octx, const struct set *set)
{
json_t *root, *tmp;
@@ -152,6 +201,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 +222,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)
{
@@ -305,6 +331,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}",
@@ -1122,6 +1154,13 @@ json_t *expr_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
return expr_print_json(stmt->expr, octx);
}
+json_t *flow_offload_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
+{
+ return json_pack("{s:{s:s, s:s+}}", "flow",
+ "op", "add", "flowtable",
+ "@", stmt->flow.table_name);
+}
+
json_t *payload_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
{
return json_pack("{s: {s:o, s:o}}", "mangle",
@@ -1434,10 +1473,20 @@ 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)
{
- 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 *objref_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
@@ -1571,10 +1620,29 @@ json_t *synproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
return json_pack("{s:o}", "synproxy", root);
}
+json_t *optstrip_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
+{
+ return json_pack("{s:o}", "reset",
+ expr_print_json(stmt->optstrip.expr, octx));
+}
+
+json_t *xt_stmt_json(const struct stmt *stmt, struct output_ctx *octx)
+{
+ static const char *xt_typename[NFT_XT_MAX] = {
+ [NFT_XT_MATCH] = "match",
+ [NFT_XT_TARGET] = "target",
+ [NFT_XT_WATCHER] = "watcher",
+ };
+
+ return json_pack("{s:{s:s, s:s}}", "xt",
+ "type", xt_typename[stmt->xt.type],
+ "name", stmt->xt.name);
+}
+
static json_t *table_print_json_full(struct netlink_ctx *ctx,
struct table *table)
{
- json_t *root = json_array(), *tmp;
+ json_t *root = json_array(), *rules = json_array(), *tmp;
struct flowtable *flowtable;
struct chain *chain;
struct rule *rule;
@@ -1604,10 +1672,13 @@ static json_t *table_print_json_full(struct netlink_ctx *ctx,
list_for_each_entry(rule, &chain->rules, list) {
tmp = rule_print_json(&ctx->nft->output, rule);
- json_array_append_new(root, tmp);
+ json_array_append_new(rules, tmp);
}
}
+ json_array_extend(root, rules);
+ json_decref(rules);
+
return root;
}
@@ -1840,6 +1911,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 fc52fbc3..ec01a427 100644
--- a/src/libnftables.c
+++ b/src/libnftables.c
@@ -2,9 +2,8 @@
* 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 <nftables/libnftables.h>
#include <erec.h>
@@ -128,9 +127,7 @@ int nft_ctx_add_var(struct nft_ctx *ctx, const char *var)
if (!separator)
return -1;
- tmp = realloc(ctx->vars, (pcount + 1) * sizeof(struct nft_vars));
- if (!tmp)
- return -1;
+ tmp = xrealloc(ctx->vars, (pcount + 1) * sizeof(struct nft_vars));
*separator = '\0';
value = separator + 1;
@@ -162,9 +159,7 @@ int nft_ctx_add_include_path(struct nft_ctx *ctx, const char *path)
char **tmp;
int pcount = ctx->num_include_paths;
- tmp = realloc(ctx->include_paths, (pcount + 1) * sizeof(char *));
- if (!tmp)
- return -1;
+ tmp = xrealloc(ctx->include_paths, (pcount + 1) * sizeof(char *));
ctx->include_paths = tmp;
@@ -395,6 +390,18 @@ void nft_ctx_set_dry_run(struct nft_ctx *ctx, bool dry)
ctx->check = dry;
}
+EXPORT_SYMBOL(nft_ctx_get_optimize);
+uint32_t nft_ctx_get_optimize(struct nft_ctx *ctx)
+{
+ return ctx->optimize_flags;
+}
+
+EXPORT_SYMBOL(nft_ctx_set_optimize);
+void nft_ctx_set_optimize(struct nft_ctx *ctx, uint32_t flags)
+{
+ ctx->optimize_flags = flags;
+}
+
EXPORT_SYMBOL(nft_ctx_output_get_flags);
unsigned int nft_ctx_output_get_flags(struct nft_ctx *ctx)
{
@@ -424,13 +431,14 @@ static const struct input_descriptor indesc_cmdline = {
};
static int nft_parse_bison_buffer(struct nft_ctx *nft, const char *buf,
- struct list_head *msgs, struct list_head *cmds)
+ struct list_head *msgs, struct list_head *cmds,
+ const struct input_descriptor *indesc)
{
int ret;
parser_init(nft, nft->state, msgs, cmds, nft->top_scope);
nft->scanner = scanner_init(nft->state);
- scanner_push_buffer(nft->scanner, &indesc_cmdline, buf);
+ scanner_push_buffer(nft->scanner, indesc, buf);
ret = nft_parse(nft, nft->scanner, nft->state);
if (ret != 0 || nft->state->nerrs > 0)
@@ -439,11 +447,42 @@ static int nft_parse_bison_buffer(struct nft_ctx *nft, const char *buf,
return 0;
}
+static char *stdin_to_buffer(void)
+{
+ unsigned int bufsiz = 16384, consumed = 0;
+ int numbytes;
+ char *buf;
+
+ buf = xmalloc(bufsiz);
+
+ numbytes = read(STDIN_FILENO, buf, bufsiz);
+ while (numbytes > 0) {
+ consumed += numbytes;
+ if (consumed == bufsiz) {
+ bufsiz *= 2;
+ buf = xrealloc(buf, bufsiz);
+ }
+ numbytes = read(STDIN_FILENO, buf + consumed, bufsiz - consumed);
+ }
+ buf[consumed] = '\0';
+
+ return buf;
+}
+
+static const struct input_descriptor indesc_stdin = {
+ .type = INDESC_STDIN,
+ .name = "/dev/stdin",
+};
+
static int nft_parse_bison_filename(struct nft_ctx *nft, const char *filename,
struct list_head *msgs, struct list_head *cmds)
{
int ret;
+ if (nft->stdin_buf)
+ return nft_parse_bison_buffer(nft, nft->stdin_buf, msgs, cmds,
+ &indesc_stdin);
+
parser_init(nft, nft->state, msgs, cmds, nft->top_scope);
nft->scanner = scanner_init(nft->state);
if (scanner_read_file(nft, filename, &internal_location) < 0)
@@ -459,24 +498,44 @@ static int nft_parse_bison_filename(struct nft_ctx *nft, const char *filename,
static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs,
struct list_head *cmds)
{
+ struct nft_cache_filter *filter;
+ struct cmd *cmd, *next;
+ bool collapsed = false;
unsigned int flags;
- struct cmd *cmd;
+ int err = 0;
- flags = nft_cache_evaluate(nft, cmds);
- if (nft_cache_update(nft, flags, msgs) < 0)
+ filter = nft_cache_filter_init();
+ if (nft_cache_evaluate(nft, cmds, msgs, filter, &flags) < 0) {
+ nft_cache_filter_fini(filter);
+ return -1;
+ }
+ if (nft_cache_update(nft, flags, msgs, filter) < 0) {
+ nft_cache_filter_fini(filter);
return -1;
+ }
- list_for_each_entry(cmd, cmds, list) {
+ nft_cache_filter_fini(filter);
+
+ if (nft_cmd_collapse(cmds))
+ collapsed = true;
+
+ list_for_each_entry_safe(cmd, next, cmds, list) {
struct eval_ctx ectx = {
.nft = nft,
.msgs = msgs,
};
+
if (cmd_evaluate(&ectx, cmd) < 0 &&
- ++nft->state->nerrs == nft->parser_max_errors)
- return -1;
+ ++nft->state->nerrs == nft->parser_max_errors) {
+ err = -1;
+ break;
+ }
}
- if (nft->state->nerrs)
+ if (collapsed)
+ nft_cmd_uncollapse(cmds);
+
+ if (err < 0 || nft->state->nerrs)
return -1;
list_for_each_entry(cmd, cmds, list) {
@@ -504,7 +563,8 @@ int nft_run_cmd_from_buffer(struct nft_ctx *nft, const char *buf)
if (nft_output_json(&nft->output))
rc = nft_parse_json_buffer(nft, nlbuf, &msgs, &cmds);
if (rc == -EINVAL)
- rc = nft_parse_bison_buffer(nft, nlbuf, &msgs, &cmds);
+ rc = nft_parse_bison_buffer(nft, nlbuf, &msgs, &cmds,
+ &indesc_cmdline);
parser_rc = rc;
@@ -572,7 +632,7 @@ retry:
}
snprintf(buf + offset, bufsize - offset, "\n");
- rc = nft_parse_bison_buffer(ctx, buf, msgs, &cmds);
+ rc = nft_parse_bison_buffer(ctx, buf, msgs, &cmds, &indesc_cmdline);
assert(list_empty(&cmds));
/* Stash the buffer that contains the variable definitions and zap the
@@ -587,8 +647,7 @@ retry:
return rc;
}
-EXPORT_SYMBOL(nft_run_cmd_from_filename);
-int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
+static int __nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
{
struct cmd *cmd, *next;
int rc, parser_rc;
@@ -599,9 +658,6 @@ int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
if (rc < 0)
goto err;
- if (!strcmp(filename, "-"))
- filename = "/dev/stdin";
-
rc = -EINVAL;
if (nft_output_json(&nft->output))
rc = nft_parse_json_filename(nft, filename, &msgs, &cmds);
@@ -610,6 +666,9 @@ int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
parser_rc = rc;
+ if (nft->optimize_flags)
+ nft_optimize(nft, &cmds);
+
rc = nft_evaluate(nft, &msgs, &cmds);
if (rc < 0)
goto err;
@@ -650,5 +709,54 @@ err:
json_print_echo(nft);
if (rc)
nft_cache_release(&nft->cache);
+
+ scope_release(nft->state->scopes[0]);
+
return rc;
}
+
+static int nft_run_optimized_file(struct nft_ctx *nft, const char *filename)
+{
+ uint32_t optimize_flags;
+ bool check;
+ int ret;
+
+ check = nft->check;
+ nft->check = true;
+ optimize_flags = nft->optimize_flags;
+ nft->optimize_flags = 0;
+
+ /* First check the original ruleset loads fine as is. */
+ ret = __nft_run_cmd_from_filename(nft, filename);
+ if (ret < 0)
+ return ret;
+
+ nft->check = check;
+ nft->optimize_flags = optimize_flags;
+
+ return __nft_run_cmd_from_filename(nft, filename);
+}
+
+EXPORT_SYMBOL(nft_run_cmd_from_filename);
+int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)
+{
+ int ret;
+
+ if (!strcmp(filename, "-"))
+ filename = "/dev/stdin";
+
+ if (!strcmp(filename, "/dev/stdin") &&
+ !nft_output_json(&nft->output))
+ nft->stdin_buf = stdin_to_buffer();
+
+ if (nft->optimize_flags) {
+ ret = nft_run_optimized_file(nft, filename);
+ xfree(nft->stdin_buf);
+ return ret;
+ }
+
+ ret = __nft_run_cmd_from_filename(nft, filename);
+ xfree(nft->stdin_buf);
+
+ return ret;
+}
diff --git a/src/libnftables.map b/src/libnftables.map
index d3a795ce..a46a3ad5 100644
--- a/src/libnftables.map
+++ b/src/libnftables.map
@@ -28,3 +28,8 @@ LIBNFTABLES_2 {
nft_ctx_add_var;
nft_ctx_clear_vars;
} LIBNFTABLES_1;
+
+LIBNFTABLES_3 {
+ nft_ctx_set_optimize;
+ nft_ctx_get_optimize;
+} LIBNFTABLES_2;
diff --git a/src/main.c b/src/main.c
index 21096fc7..9bd25db8 100644
--- a/src/main.c
+++ b/src/main.c
@@ -36,7 +36,8 @@ enum opt_indices {
IDX_INTERACTIVE,
IDX_INCLUDEPATH,
IDX_CHECK,
-#define IDX_RULESET_INPUT_END IDX_CHECK
+ IDX_OPTIMIZE,
+#define IDX_RULESET_INPUT_END IDX_OPTIMIZE
/* Ruleset list formatting */
IDX_HANDLE,
#define IDX_RULESET_LIST_START IDX_HANDLE
@@ -80,6 +81,7 @@ enum opt_vals {
OPT_NUMERIC_PROTO = 'p',
OPT_NUMERIC_TIME = 'T',
OPT_TERSE = 't',
+ OPT_OPTIMIZE = 'o',
OPT_INVALID = '?',
};
@@ -136,6 +138,8 @@ static const struct nft_opt nft_options[] = {
"Format output in JSON"),
[IDX_DEBUG] = NFT_OPT("debug", OPT_DEBUG, "<level [,level...]>",
"Specify debugging level (scanner, parser, eval, netlink, mnl, proto-ctx, segtree, all)"),
+ [IDX_OPTIMIZE] = NFT_OPT("optimize", OPT_OPTIMIZE, NULL,
+ "Optimize ruleset"),
};
#define NR_NFT_OPTIONS (sizeof(nft_options) / sizeof(nft_options[0]))
@@ -363,6 +367,10 @@ int main(int argc, char * const *argv)
unsigned int len;
int i, val, rc;
+ /* nftables cannot be used with setuid in a safe way. */
+ if (getuid() != geteuid())
+ _exit(111);
+
if (!nft_options_check(argc, argv))
exit(EXIT_FAILURE);
@@ -480,6 +488,9 @@ int main(int argc, char * const *argv)
case OPT_TERSE:
output_flags |= NFT_CTX_OUTPUT_TERSE;
break;
+ case OPT_OPTIMIZE:
+ nft_ctx_set_optimize(nft, 0x1);
+ break;
case OPT_INVALID:
exit(EXIT_FAILURE);
}
diff --git a/src/mergesort.c b/src/mergesort.c
index 152b0556..a3a9d605 100644
--- a/src/mergesort.c
+++ b/src/mergesort.c
@@ -2,8 +2,8 @@
* 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>
@@ -36,6 +36,7 @@ static void expr_msort_value(const struct expr *expr, mpz_t value)
break;
case EXPR_BINOP:
case EXPR_MAPPING:
+ case EXPR_RANGE:
expr_msort_value(expr->left, value);
break;
case EXPR_VALUE:
@@ -69,7 +70,7 @@ static int expr_msort_cmp(const struct expr *e1, const struct expr *e2)
return ret;
}
-static void list_splice_sorted(struct list_head *list, struct list_head *head)
+void list_splice_sorted(struct list_head *list, struct list_head *head)
{
struct list_head *h = head->next;
struct list_head *l = list->next;
diff --git a/src/meta.c b/src/meta.c
index bdd10269..013e8cba 100644
--- a/src/meta.c
+++ b/src/meta.c
@@ -220,18 +220,20 @@ static struct error_record *uid_type_parse(struct parse_ctx *ctx,
struct expr **res)
{
struct passwd *pw;
- uint64_t uid;
+ uid_t uid;
char *endptr = NULL;
pw = getpwnam(sym->identifier);
if (pw != NULL)
uid = pw->pw_uid;
else {
- uid = strtoull(sym->identifier, &endptr, 10);
- if (uid > UINT32_MAX)
+ uint64_t _uid = strtoull(sym->identifier, &endptr, 10);
+
+ if (_uid > UINT32_MAX)
return error(&sym->location, "Value too large");
else if (*endptr)
return error(&sym->location, "User does not exist");
+ uid = _uid;
}
*res = constant_expr_alloc(&sym->location, sym->dtype,
@@ -274,18 +276,20 @@ static struct error_record *gid_type_parse(struct parse_ctx *ctx,
struct expr **res)
{
struct group *gr;
- uint64_t gid;
+ gid_t gid;
char *endptr = NULL;
gr = getgrnam(sym->identifier);
if (gr != NULL)
gid = gr->gr_gid;
else {
- gid = strtoull(sym->identifier, &endptr, 0);
- if (gid > UINT32_MAX)
+ uint64_t _gid = strtoull(sym->identifier, &endptr, 0);
+
+ if (_gid > UINT32_MAX)
return error(&sym->location, "Value too large");
else if (*endptr)
return error(&sym->location, "Group does not exist");
+ gid = _gid;
}
*res = constant_expr_alloc(&sym->location, sym->dtype,
@@ -395,27 +399,27 @@ static void date_type_print(const struct expr *expr, struct output_ctx *octx)
tstamp += cur_tm->tm_gmtoff;
if ((tm = gmtime((time_t *) &tstamp)) != NULL &&
- strftime(timestr, sizeof(timestr) - 1, "%F %T", tm))
+ strftime(timestr, sizeof(timestr) - 1, "%Y-%m-%d %T", tm))
nft_print(octx, "\"%s\"", timestr);
else
nft_print(octx, "Error converting timestamp to printed time");
}
-static time_t parse_iso_date(const char *sym)
+static bool parse_iso_date(uint64_t *tstamp, const char *sym)
{
struct tm tm, *cur_tm;
time_t ts;
memset(&tm, 0, sizeof(struct tm));
- if (strptime(sym, "%F %T", &tm))
+ if (strptime(sym, "%Y-%m-%d %T", &tm))
goto success;
- if (strptime(sym, "%F %R", &tm))
+ if (strptime(sym, "%Y-%m-%d %R", &tm))
goto success;
- if (strptime(sym, "%F", &tm))
+ if (strptime(sym, "%Y-%m-%d", &tm))
goto success;
- return -1;
+ return false;
success:
/*
@@ -429,10 +433,12 @@ success:
cur_tm = localtime(&ts);
if (ts == (time_t) -1 || cur_tm == NULL)
- return ts;
+ return false;
/* Substract tm_gmtoff to get the current time */
- return ts - cur_tm->tm_gmtoff;
+ *tstamp = ts - cur_tm->tm_gmtoff;
+
+ return true;
}
static struct error_record *date_type_parse(struct parse_ctx *ctx,
@@ -440,9 +446,9 @@ static struct error_record *date_type_parse(struct parse_ctx *ctx,
struct expr **res)
{
const char *endptr = sym->identifier;
- time_t tstamp;
+ uint64_t tstamp;
- if ((tstamp = parse_iso_date(sym->identifier)) != -1)
+ if (parse_iso_date(&tstamp, sym->identifier))
goto success;
tstamp = strtoul(sym->identifier, (char **) &endptr, 10);
@@ -512,7 +518,8 @@ static struct error_record *hour_type_parse(struct parse_ctx *ctx,
{
struct error_record *er;
struct tm tm, *cur_tm;
- uint64_t result = 0;
+ uint32_t result;
+ uint64_t tmp;
char *endptr;
time_t ts;
@@ -540,8 +547,8 @@ static struct error_record *hour_type_parse(struct parse_ctx *ctx,
if (endptr && *endptr)
return error(&sym->location, "Can't parse trailing input: \"%s\"\n", endptr);
- if ((er = time_parse(&sym->location, sym->identifier, &result)) == NULL) {
- result /= 1000;
+ if ((er = time_parse(&sym->location, sym->identifier, &tmp)) == NULL) {
+ result = tmp / 1000;
goto convert;
}
@@ -595,7 +602,7 @@ const struct datatype hour_type = {
.name = "hour",
.desc = "Hour of day of packet reception",
.byteorder = BYTEORDER_HOST_ENDIAN,
- .size = sizeof(uint64_t) * BITS_PER_BYTE,
+ .size = sizeof(uint32_t) * BITS_PER_BYTE,
.basetype = &integer_type,
.print = hour_type_print,
.parse = hour_type_parse,
@@ -727,6 +734,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;
}
/**
@@ -800,13 +808,19 @@ static void meta_expr_pctx_update(struct proto_ctx *ctx,
}
#define NFTNL_UDATA_META_KEY 0
-#define NFTNL_UDATA_META_MAX 1
+#define NFTNL_UDATA_META_INNER_DESC 1
+#define NFTNL_UDATA_META_MAX 2
static int meta_expr_build_udata(struct nftnl_udata_buf *udbuf,
const struct expr *expr)
{
nftnl_udata_put_u32(udbuf, NFTNL_UDATA_META_KEY, expr->meta.key);
+ if (expr->meta.inner_desc) {
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_META_INNER_DESC,
+ expr->meta.inner_desc->id);
+ }
+
return 0;
}
@@ -818,6 +832,7 @@ static int meta_parse_udata(const struct nftnl_udata *attr, void *data)
switch (type) {
case NFTNL_UDATA_META_KEY:
+ case NFTNL_UDATA_META_INNER_DESC:
if (len != sizeof(uint32_t))
return -1;
break;
@@ -832,6 +847,8 @@ static int meta_parse_udata(const struct nftnl_udata *attr, void *data)
static struct expr *meta_expr_parse_udata(const struct nftnl_udata *attr)
{
const struct nftnl_udata *ud[NFTNL_UDATA_META_MAX + 1] = {};
+ const struct proto_desc *desc;
+ struct expr *expr;
uint32_t key;
int err;
@@ -845,7 +862,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 = {
diff --git a/src/misspell.c b/src/misspell.c
index 6536d755..8992c75e 100644
--- a/src/misspell.c
+++ b/src/misspell.c
@@ -1,3 +1,11 @@
+/*
+ * 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 <stdlib.h>
#include <string.h>
#include <limits.h>
diff --git a/src/mnl.c b/src/mnl.c
index 2d5afdfe..d5fd58c3 100644
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -2,8 +2,8 @@
* 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/)
*/
@@ -377,7 +377,7 @@ static int mnl_batch_extack_cb(const struct nlmsghdr *nlh, void *data)
return MNL_CB_ERROR;
}
-#define NFT_MNL_ECHO_RCVBUFF_DEFAULT (MNL_SOCKET_BUFFER_SIZE * 1024)
+#define NFT_MNL_ECHO_RCVBUFF_DEFAULT (MNL_SOCKET_BUFFER_SIZE * 1024U)
#define NFT_MNL_ACK_MAXSIZE ((sizeof(struct nlmsghdr) + \
sizeof(struct nfgenmsg) + (1 << 16)) + \
MNL_SOCKET_BUFFER_SIZE)
@@ -592,6 +592,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 +603,11 @@ int mnl_nft_rule_del(struct netlink_ctx *ctx, struct cmd *cmd)
nftnl_rule_set_u32(nlr, NFTNL_RULE_FAMILY, h->family);
+ if (cmd->op == CMD_DESTROY)
+ msg_type = NFT_MSG_DESTROYRULE;
+
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
- NFT_MSG_DELRULE,
+ msg_type,
nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY),
0, ctx->seqnum);
@@ -653,20 +657,45 @@ err_free:
return MNL_CB_OK;
}
-struct nftnl_rule_list *mnl_nft_rule_dump(struct netlink_ctx *ctx,
- int family)
+struct nftnl_rule_list *mnl_nft_rule_dump(struct netlink_ctx *ctx, int family,
+ const char *table, const char *chain,
+ uint64_t rule_handle,
+ bool dump, bool reset)
{
+ uint16_t nl_flags = dump ? NLM_F_DUMP : NLM_F_ACK;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nftnl_rule_list *nlr_list;
+ struct nftnl_rule *nlr = NULL;
struct nlmsghdr *nlh;
- int ret;
+ int msg_type, ret;
+
+ if (reset)
+ msg_type = NFT_MSG_GETRULE_RESET;
+ else
+ msg_type = NFT_MSG_GETRULE;
+
+ if (table) {
+ nlr = nftnl_rule_alloc();
+ if (!nlr)
+ memory_allocation_error();
+
+ nftnl_rule_set_str(nlr, NFTNL_RULE_TABLE, table);
+ if (chain)
+ nftnl_rule_set_str(nlr, NFTNL_RULE_CHAIN, chain);
+ if (rule_handle)
+ nftnl_rule_set_u64(nlr, NFTNL_RULE_HANDLE, rule_handle);
+ }
nlr_list = nftnl_rule_list_alloc();
if (nlr_list == NULL)
memory_allocation_error();
- nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, family,
- NLM_F_DUMP, ctx->seqnum);
+ nlh = nftnl_nlmsg_build_hdr(buf, msg_type, family,
+ nl_flags, ctx->seqnum);
+ if (nlr) {
+ nftnl_rule_nlmsg_build_payload(nlh, nlr);
+ nftnl_rule_free(nlr);
+ }
ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, rule_cb, nlr_list);
if (ret < 0)
@@ -832,6 +861,7 @@ int mnl_nft_chain_rename(struct netlink_ctx *ctx, const struct cmd *cmd,
int mnl_nft_chain_del(struct netlink_ctx *ctx, struct cmd *cmd)
{
+ enum nf_tables_msg_types msg_type = NFT_MSG_DELCHAIN;
struct nftnl_chain *nlc;
struct nlmsghdr *nlh;
@@ -841,8 +871,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);
@@ -889,10 +922,12 @@ err_free:
}
struct nftnl_chain_list *mnl_nft_chain_dump(struct netlink_ctx *ctx,
- int family)
+ int family, const char *table,
+ const char *chain)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nftnl_chain_list *nlc_list;
+ struct nftnl_chain *nlc = NULL;
struct nlmsghdr *nlh;
int ret;
@@ -900,11 +935,24 @@ struct nftnl_chain_list *mnl_nft_chain_dump(struct netlink_ctx *ctx,
if (nlc_list == NULL)
memory_allocation_error();
+ if (table && chain) {
+ nlc = nftnl_chain_alloc();
+ if (!nlc)
+ memory_allocation_error();
+
+ nftnl_chain_set_str(nlc, NFTNL_CHAIN_TABLE, table);
+ nftnl_chain_set_str(nlc, NFTNL_CHAIN_NAME, chain);
+ }
+
nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, family,
- NLM_F_DUMP, ctx->seqnum);
+ nlc ? NLM_F_ACK : NLM_F_DUMP, ctx->seqnum);
+ if (nlc) {
+ nftnl_chain_nlmsg_build_payload(nlh, nlc);
+ nftnl_chain_free(nlc);
+ }
ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, chain_cb, nlc_list);
- if (ret < 0)
+ if (ret < 0 && errno != ENOENT)
goto err;
return nlc_list;
@@ -962,6 +1010,7 @@ int mnl_nft_table_add(struct netlink_ctx *ctx, struct cmd *cmd,
int mnl_nft_table_del(struct netlink_ctx *ctx, struct cmd *cmd)
{
+ enum nf_tables_msg_types msg_type = NFT_MSG_DELTABLE;
struct nftnl_table *nlt;
struct nlmsghdr *nlh;
@@ -971,10 +1020,11 @@ int mnl_nft_table_del(struct netlink_ctx *ctx, struct cmd *cmd)
nftnl_table_set_u32(nlt, NFTNL_TABLE_FAMILY, cmd->handle.family);
- nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
- NFT_MSG_DELTABLE,
- cmd->handle.family,
- 0, ctx->seqnum);
+ if (cmd->op == CMD_DESTROY)
+ msg_type = NFT_MSG_DESTROYTABLE;
+
+ nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch), msg_type,
+ cmd->handle.family, 0, ctx->seqnum);
if (cmd->handle.table.name) {
cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
@@ -1016,10 +1066,12 @@ err_free:
}
struct nftnl_table_list *mnl_nft_table_dump(struct netlink_ctx *ctx,
- int family)
+ int family, const char *table)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nftnl_table_list *nlt_list;
+ struct nftnl_table *nlt = NULL;
+ int flags = NLM_F_DUMP;
struct nlmsghdr *nlh;
int ret;
@@ -1027,11 +1079,25 @@ struct nftnl_table_list *mnl_nft_table_dump(struct netlink_ctx *ctx,
if (nlt_list == NULL)
return NULL;
+ if (table) {
+ nlt = nftnl_table_alloc();
+ if (!nlt)
+ memory_allocation_error();
+
+ nftnl_table_set_u32(nlt, NFTNL_TABLE_FAMILY, family);
+ nftnl_table_set_str(nlt, NFTNL_TABLE_NAME, table);
+ flags = NLM_F_ACK;
+ }
+
nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, family,
- NLM_F_DUMP, ctx->seqnum);
+ flags, ctx->seqnum);
+ if (nlt) {
+ nftnl_table_nlmsg_build_payload(nlh, nlt);
+ nftnl_table_free(nlt);
+ }
ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, table_cb, nlt_list);
- if (ret < 0)
+ if (ret < 0 && errno != ENOENT)
goto err;
return nlt_list;
@@ -1047,9 +1113,7 @@ static void set_key_expression(struct netlink_ctx *ctx,
{
struct nftnl_udata *nest1, *nest2;
- if (expr->flags & EXPR_F_CONSTANT ||
- set_is_anonymous(set_flags) ||
- !expr_ops(expr)->build_udata)
+ if (!expr_ops(expr)->build_udata)
return;
nest1 = nftnl_udata_nest_start(udbuf, type);
@@ -1194,6 +1258,7 @@ int mnl_nft_set_add(struct netlink_ctx *ctx, struct cmd *cmd,
int mnl_nft_set_del(struct netlink_ctx *ctx, struct cmd *cmd)
{
+ enum nf_tables_msg_types msg_type = NFT_MSG_DELSET;
const struct handle *h = &cmd->handle;
struct nftnl_set *nls;
struct nlmsghdr *nlh;
@@ -1204,8 +1269,11 @@ int mnl_nft_set_del(struct netlink_ctx *ctx, struct cmd *cmd)
nftnl_set_set_u32(nls, NFTNL_SET_FAMILY, h->family);
+ if (cmd->op == CMD_DESTROY)
+ msg_type = NFT_MSG_DESTROYSET;
+
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
- NFT_MSG_DELSET,
+ msg_type,
h->family,
0, ctx->seqnum);
@@ -1252,10 +1320,12 @@ err_free:
}
struct nftnl_set_list *
-mnl_nft_set_dump(struct netlink_ctx *ctx, int family, const char *table)
+mnl_nft_set_dump(struct netlink_ctx *ctx, int family,
+ const char *table, const char *set)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nftnl_set_list *nls_list;
+ int flags = NLM_F_DUMP;
struct nlmsghdr *nlh;
struct nftnl_set *s;
int ret;
@@ -1264,10 +1334,15 @@ mnl_nft_set_dump(struct netlink_ctx *ctx, int family, const char *table)
if (s == NULL)
memory_allocation_error();
- nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETSET, family,
- NLM_F_DUMP, ctx->seqnum);
if (table != NULL)
nftnl_set_set_str(s, NFTNL_SET_TABLE, table);
+ if (set) {
+ nftnl_set_set_str(s, NFTNL_SET_NAME, set);
+ flags = NLM_F_ACK;
+ }
+
+ nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETSET, family,
+ flags, ctx->seqnum);
nftnl_set_nlmsg_build_payload(nlh, s);
nftnl_set_free(s);
@@ -1276,7 +1351,7 @@ mnl_nft_set_dump(struct netlink_ctx *ctx, int family, const char *table)
memory_allocation_error();
ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, set_cb, nls_list);
- if (ret < 0)
+ if (ret < 0 && errno != ENOENT)
goto err;
return nls_list;
@@ -1402,6 +1477,7 @@ int mnl_nft_obj_add(struct netlink_ctx *ctx, struct cmd *cmd,
int mnl_nft_obj_del(struct netlink_ctx *ctx, struct cmd *cmd, int type)
{
+ enum nf_tables_msg_types msg_type = NFT_MSG_DELOBJ;
struct nftnl_obj *nlo;
struct nlmsghdr *nlh;
@@ -1412,8 +1488,11 @@ int mnl_nft_obj_del(struct netlink_ctx *ctx, struct cmd *cmd, int type)
nftnl_obj_set_u32(nlo, NFTNL_OBJ_FAMILY, cmd->handle.family);
nftnl_obj_set_u32(nlo, NFTNL_OBJ_TYPE, type);
+ if (cmd->op == CMD_DESTROY)
+ msg_type = NFT_MSG_DESTROYOBJ;
+
nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch),
- NFT_MSG_DELOBJ, cmd->handle.family,
+ msg_type, cmd->handle.family,
0, ctx->seqnum);
cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location);
@@ -1518,39 +1597,114 @@ static int set_elem_cb(const struct nlmsghdr *nlh, void *data)
return MNL_CB_OK;
}
-static int mnl_nft_setelem_batch(struct nftnl_set *nls,
+static bool mnl_nft_attr_nest_overflow(struct nlmsghdr *nlh,
+ const struct nlattr *from,
+ const struct nlattr *to)
+{
+ int len = (void *)to + to->nla_len - (void *)from;
+
+ /* The attribute length field is 16 bits long, thus the maximum payload
+ * that an attribute can convey is UINT16_MAX. In case of overflow,
+ * discard the last attribute that did not fit into the nest.
+ */
+ if (len > UINT16_MAX) {
+ nlh->nlmsg_len -= to->nla_len;
+ return true;
+ }
+ return false;
+}
+
+static void netlink_dump_setelem(const struct nftnl_set_elem *nlse,
+ struct netlink_ctx *ctx)
+{
+ FILE *fp = ctx->nft->output.output_fp;
+ char buf[4096];
+
+ if (!(ctx->nft->debug_mask & NFT_DEBUG_NETLINK) || !fp)
+ return;
+
+ nftnl_set_elem_snprintf(buf, sizeof(buf), nlse, NFTNL_OUTPUT_DEFAULT, 0);
+ fprintf(fp, "\t%s", buf);
+}
+
+static void netlink_dump_setelem_done(struct netlink_ctx *ctx)
+{
+ FILE *fp = ctx->nft->output.output_fp;
+
+ if (!(ctx->nft->debug_mask & NFT_DEBUG_NETLINK) || !fp)
+ return;
+
+ fprintf(fp, "\n");
+}
+
+static int mnl_nft_setelem_batch(const struct nftnl_set *nls, struct cmd *cmd,
struct nftnl_batch *batch,
- enum nf_tables_msg_types cmd,
- unsigned int flags, uint32_t seqnum)
+ enum nf_tables_msg_types msg_type,
+ unsigned int flags, uint32_t seqnum,
+ const struct expr *set,
+ struct netlink_ctx *ctx)
{
+ struct nlattr *nest1, *nest2;
+ struct nftnl_set_elem *nlse;
struct nlmsghdr *nlh;
- struct nftnl_set_elems_iter *iter;
- int ret;
-
- iter = nftnl_set_elems_iter_create(nls);
- if (iter == NULL)
- memory_allocation_error();
+ struct expr *expr = NULL;
+ int i = 0;
- if (cmd == NFT_MSG_NEWSETELEM)
+ if (msg_type == NFT_MSG_NEWSETELEM)
flags |= NLM_F_CREATE;
- while (nftnl_set_elems_iter_cur(iter)) {
- nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(batch), cmd,
- nftnl_set_get_u32(nls, NFTNL_SET_FAMILY),
- flags, seqnum);
- ret = nftnl_set_elems_nlmsg_build_payload_iter(nlh, iter);
- mnl_nft_batch_continue(batch);
- if (ret <= 0)
- break;
+ if (set)
+ expr = list_first_entry(&set->expressions, struct expr, list);
+
+next:
+ nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(batch), msg_type,
+ nftnl_set_get_u32(nls, NFTNL_SET_FAMILY),
+ flags, seqnum);
+
+ if (nftnl_set_is_set(nls, NFTNL_SET_TABLE)) {
+ mnl_attr_put_strz(nlh, NFTA_SET_ELEM_LIST_TABLE,
+ nftnl_set_get_str(nls, NFTNL_SET_TABLE));
+ }
+ if (nftnl_set_is_set(nls, NFTNL_SET_NAME)) {
+ mnl_attr_put_strz(nlh, NFTA_SET_ELEM_LIST_SET,
+ nftnl_set_get_str(nls, NFTNL_SET_NAME));
+ }
+ if (nftnl_set_is_set(nls, NFTNL_SET_ID)) {
+ mnl_attr_put_u32(nlh, NFTA_SET_ELEM_LIST_SET_ID,
+ htonl(nftnl_set_get_u32(nls, NFTNL_SET_ID)));
}
- nftnl_set_elems_iter_destroy(iter);
+ if (!set || list_empty(&set->expressions))
+ return 0;
+
+ assert(expr);
+ nest1 = mnl_attr_nest_start(nlh, NFTA_SET_ELEM_LIST_ELEMENTS);
+ list_for_each_entry_from(expr, &set->expressions, list) {
+ nlse = alloc_nftnl_setelem(set, expr);
+
+ cmd_add_loc(cmd, nlh->nlmsg_len, &expr->location);
+ nest2 = mnl_attr_nest_start(nlh, ++i);
+ nftnl_set_elem_nlmsg_build_payload(nlh, nlse);
+ mnl_attr_nest_end(nlh, nest2);
+
+ netlink_dump_setelem(nlse, ctx);
+ nftnl_set_elem_free(nlse);
+ if (mnl_nft_attr_nest_overflow(nlh, nest1, nest2)) {
+ mnl_attr_nest_end(nlh, nest1);
+ mnl_nft_batch_continue(batch);
+ goto next;
+ }
+ }
+ mnl_attr_nest_end(nlh, nest1);
+ mnl_nft_batch_continue(batch);
+ netlink_dump_setelem_done(ctx);
return 0;
}
-int mnl_nft_setelem_add(struct netlink_ctx *ctx, const struct set *set,
- const struct expr *expr, unsigned int flags)
+int mnl_nft_setelem_add(struct netlink_ctx *ctx, struct cmd *cmd,
+ const struct set *set, const struct expr *expr,
+ unsigned int flags)
{
const struct handle *h = &set->handle;
struct nftnl_set *nls;
@@ -1569,11 +1723,10 @@ int mnl_nft_setelem_add(struct netlink_ctx *ctx, const struct set *set,
nftnl_set_set_u32(nls, NFTNL_SET_DATA_TYPE,
dtype_map_to_kernel(set->data->dtype));
- alloc_setelem_cache(expr, nls);
netlink_dump_set(nls, ctx);
- err = mnl_nft_setelem_batch(nls, ctx->batch, NFT_MSG_NEWSETELEM, flags,
- ctx->seqnum);
+ err = mnl_nft_setelem_batch(nls, cmd, ctx->batch, NFT_MSG_NEWSETELEM,
+ flags, ctx->seqnum, expr, ctx);
nftnl_set_free(nls);
return err;
@@ -1609,9 +1762,10 @@ int mnl_nft_setelem_flush(struct netlink_ctx *ctx, const struct cmd *cmd)
return 0;
}
-int mnl_nft_setelem_del(struct netlink_ctx *ctx, const struct cmd *cmd)
+int mnl_nft_setelem_del(struct netlink_ctx *ctx, struct cmd *cmd,
+ const struct handle *h, const struct expr *init)
{
- const struct handle *h = &cmd->handle;
+ enum nf_tables_msg_types msg_type = NFT_MSG_DELSETELEM;
struct nftnl_set *nls;
int err;
@@ -1626,12 +1780,13 @@ int mnl_nft_setelem_del(struct netlink_ctx *ctx, const struct cmd *cmd)
else if (h->handle.id)
nftnl_set_set_u64(nls, NFTNL_SET_HANDLE, h->handle.id);
- if (cmd->expr)
- alloc_setelem_cache(cmd->expr, nls);
netlink_dump_set(nls, ctx);
- err = mnl_nft_setelem_batch(nls, ctx->batch, NFT_MSG_DELSETELEM, 0,
- ctx->seqnum);
+ if (cmd->op == CMD_DESTROY)
+ msg_type = NFT_MSG_DESTROYSETELEM;
+
+ err = mnl_nft_setelem_batch(nls, cmd, ctx->batch, msg_type, 0,
+ ctx->seqnum, init, ctx);
nftnl_set_free(nls);
return err;
@@ -1705,11 +1860,13 @@ err_free:
}
struct nftnl_flowtable_list *
-mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family, const char *table)
+mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family,
+ const char *table, const char *ft)
{
struct nftnl_flowtable_list *nln_list;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nftnl_flowtable *n;
+ int flags = NLM_F_DUMP;
struct nlmsghdr *nlh;
int ret;
@@ -1717,10 +1874,14 @@ mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family, const char *table)
if (n == NULL)
memory_allocation_error();
- nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETFLOWTABLE, family,
- NLM_F_DUMP, ctx->seqnum);
if (table != NULL)
nftnl_flowtable_set_str(n, NFTNL_FLOWTABLE_TABLE, table);
+ if (ft) {
+ nftnl_flowtable_set_str(n, NFTNL_FLOWTABLE_NAME, ft);
+ flags = NLM_F_ACK;
+ }
+ nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETFLOWTABLE, family,
+ flags, ctx->seqnum);
nftnl_flowtable_nlmsg_build_payload(nlh, n);
nftnl_flowtable_free(n);
@@ -1729,7 +1890,7 @@ mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family, const char *table)
memory_allocation_error();
ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, flowtable_cb, nln_list);
- if (ret < 0)
+ if (ret < 0 && errno != ENOENT)
goto err;
return nln_list;
@@ -1831,6 +1992,7 @@ 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;
@@ -1852,8 +2014,11 @@ int mnl_nft_flowtable_del(struct netlink_ctx *ctx, struct cmd *cmd)
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);
@@ -2088,16 +2253,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]);
diff --git a/src/monitor.c b/src/monitor.c
index 144fe96c..9692b859 100644
--- a/src/monitor.c
+++ b/src/monitor.c
@@ -2,8 +2,8 @@
* 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>
@@ -40,6 +40,13 @@
#include <iface.h>
#include <json.h>
+enum {
+ NFT_OF_EVENT_ADD,
+ NFT_OF_EVENT_INSERT,
+ NFT_OF_EVENT_DEL,
+ NFT_OF_EVENT_CREATE,
+};
+
#define nft_mon_print(monh, ...) nft_print(&monh->ctx->nft->output, __VA_ARGS__)
struct nftnl_table *netlink_table_alloc(const struct nlmsghdr *nlh)
@@ -120,17 +127,24 @@ struct nftnl_obj *netlink_obj_alloc(const struct nlmsghdr *nlh)
return nlo;
}
-static uint32_t netlink_msg2nftnl_of(uint32_t msg)
+static uint32_t netlink_msg2nftnl_of(uint32_t type, uint16_t flags)
{
- switch (msg) {
+ switch (type) {
+ case NFT_MSG_NEWRULE:
+ if (flags & NLM_F_APPEND)
+ return NFT_OF_EVENT_ADD;
+ else
+ return NFT_OF_EVENT_INSERT;
case NFT_MSG_NEWTABLE:
case NFT_MSG_NEWCHAIN:
case NFT_MSG_NEWSET:
case NFT_MSG_NEWSETELEM:
- case NFT_MSG_NEWRULE:
case NFT_MSG_NEWOBJ:
case NFT_MSG_NEWFLOWTABLE:
- return NFTNL_OF_EVENT_NEW;
+ if (flags & NLM_F_EXCL)
+ return NFT_OF_EVENT_CREATE;
+ else
+ return NFT_OF_EVENT_ADD;
case NFT_MSG_DELTABLE:
case NFT_MSG_DELCHAIN:
case NFT_MSG_DELSET:
@@ -147,18 +161,22 @@ static uint32_t netlink_msg2nftnl_of(uint32_t msg)
static const char *nftnl_of2cmd(uint32_t of)
{
switch (of) {
- case NFTNL_OF_EVENT_NEW:
+ case NFT_OF_EVENT_ADD:
return "add";
- case NFTNL_OF_EVENT_DEL:
+ case NFT_OF_EVENT_CREATE:
+ return "create";
+ case NFT_OF_EVENT_INSERT:
+ return "insert";
+ case NFT_OF_EVENT_DEL:
return "delete";
default:
return "???";
}
}
-static const char *netlink_msg2cmd(uint32_t msg)
+static const char *netlink_msg2cmd(uint32_t type, uint16_t flags)
{
- return nftnl_of2cmd(netlink_msg2nftnl_of(msg));
+ return nftnl_of2cmd(netlink_msg2nftnl_of(type, flags));
}
static void nlr_for_each_set(struct nftnl_rule *nlr,
@@ -206,7 +224,7 @@ static int netlink_events_table_cb(const struct nlmsghdr *nlh, int type,
nlt = netlink_table_alloc(nlh);
t = netlink_delinearize_table(monh->ctx, nlt);
- cmd = netlink_msg2cmd(type);
+ cmd = netlink_msg2cmd(type, nlh->nlmsg_flags);
switch (monh->format) {
case NFTNL_OUTPUT_DEFAULT:
@@ -243,7 +261,7 @@ static int netlink_events_chain_cb(const struct nlmsghdr *nlh, int type,
nlc = netlink_chain_alloc(nlh);
c = netlink_delinearize_chain(monh->ctx, nlc);
- cmd = netlink_msg2cmd(type);
+ cmd = netlink_msg2cmd(type, nlh->nlmsg_flags);
switch (monh->format) {
case NFTNL_OUTPUT_DEFAULT:
@@ -292,7 +310,7 @@ static int netlink_events_set_cb(const struct nlmsghdr *nlh, int type,
return MNL_CB_ERROR;
}
family = family2str(set->handle.family);
- cmd = netlink_msg2cmd(type);
+ cmd = netlink_msg2cmd(type, nlh->nlmsg_flags);
switch (monh->format) {
case NFTNL_OUTPUT_DEFAULT:
@@ -394,7 +412,7 @@ static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type,
table = nftnl_set_get_str(nls, NFTNL_SET_TABLE);
setname = nftnl_set_get_str(nls, NFTNL_SET_NAME);
family = nftnl_set_get_u32(nls, NFTNL_SET_FAMILY);
- cmd = netlink_msg2cmd(type);
+ cmd = netlink_msg2cmd(type, nlh->nlmsg_flags);
set = set_lookup_global(family, table, setname, &monh->ctx->nft->cache);
if (set == NULL) {
@@ -410,6 +428,7 @@ static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type,
* used by named sets, so use a dummy set.
*/
dummyset = set_alloc(monh->loc);
+ handle_merge(&dummyset->handle, &set->handle);
dummyset->key = expr_clone(set->key);
if (set->data)
dummyset->data = expr_clone(set->data);
@@ -482,7 +501,7 @@ static int netlink_events_obj_cb(const struct nlmsghdr *nlh, int type,
return MNL_CB_ERROR;
}
family = family2str(obj->handle.family);
- cmd = netlink_msg2cmd(type);
+ cmd = netlink_msg2cmd(type, nlh->nlmsg_flags);
switch (monh->format) {
case NFTNL_OUTPUT_DEFAULT:
@@ -515,8 +534,13 @@ static int netlink_events_obj_cb(const struct nlmsghdr *nlh, int type,
static void rule_map_decompose_cb(struct set *s, void *data)
{
- if (set_is_interval(s->flags) && set_is_anonymous(s->flags))
+ if (!set_is_anonymous(s->flags))
+ return;
+
+ if (set_is_non_concat_range(s))
interval_map_decompose(s->init);
+ else if (set_is_interval(s->flags))
+ concat_range_aggregate(s->init);
}
static int netlink_events_rule_cb(const struct nlmsghdr *nlh, int type,
@@ -528,9 +552,13 @@ static int netlink_events_rule_cb(const struct nlmsghdr *nlh, int type,
nlr = netlink_rule_alloc(nlh);
r = netlink_delinearize_rule(monh->ctx, nlr);
+ if (!r) {
+ fprintf(stderr, "W: Received event for an unknown table.\n");
+ goto out_free_nlr;
+ }
nlr_for_each_set(nlr, rule_map_decompose_cb, NULL,
&monh->ctx->nft->cache);
- cmd = netlink_msg2cmd(type);
+ cmd = netlink_msg2cmd(type, nlh->nlmsg_flags);
switch (monh->format) {
case NFTNL_OUTPUT_DEFAULT:
@@ -541,7 +569,10 @@ static int netlink_events_rule_cb(const struct nlmsghdr *nlh, int type,
family,
r->handle.table.name,
r->handle.chain.name);
-
+ if (r->handle.position.id) {
+ nft_mon_print(monh, "handle %" PRIu64" ",
+ r->handle.position.id);
+ }
switch (type) {
case NFT_MSG_NEWRULE:
rule_print(r, &monh->ctx->nft->output);
@@ -561,6 +592,7 @@ static int netlink_events_rule_cb(const struct nlmsghdr *nlh, int type,
break;
}
rule_free(r);
+out_free_nlr:
nftnl_rule_free(nlr);
return MNL_CB_OK;
}
@@ -612,6 +644,7 @@ static void netlink_events_cache_addset(struct netlink_mon_handler *monh,
memset(&set_tmpctx, 0, sizeof(set_tmpctx));
init_list_head(&set_tmpctx.list);
init_list_head(&msgs);
+ set_tmpctx.nft = monh->ctx->nft;
set_tmpctx.msgs = &msgs;
nls = netlink_set_alloc(nlh);
diff --git a/src/netlink.c b/src/netlink.c
index 9a0d96f0..f1452d48 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -59,7 +59,7 @@ void __noreturn __netlink_abi_error(const char *file, int line,
{
fprintf(stderr, "E: Contact urgently your Linux kernel vendor. "
"Netlink ABI is broken: %s:%d %s\n", file, line, reason);
- exit(NFT_EXIT_FAILURE);
+ abort();
}
int netlink_io_error(struct netlink_ctx *ctx, const struct location *loc,
@@ -96,12 +96,13 @@ struct nftnl_expr *alloc_nft_expr(const char *name)
return nle;
}
+static void netlink_gen_key(const struct expr *expr,
+ struct nft_data_linearize *data);
+static void __netlink_gen_data(const struct expr *expr,
+ struct nft_data_linearize *data, bool expand);
-void __netlink_gen_data(const struct expr *expr,
- struct nft_data_linearize *data, bool expand);
-
-static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
- const struct expr *expr)
+struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
+ const struct expr *expr)
{
const struct expr *elem, *data;
struct nftnl_set_elem *nlse;
@@ -133,15 +134,23 @@ static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
case EXPR_SET_ELEM_CATCHALL:
break;
default:
- __netlink_gen_data(key, &nld, false);
- nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY, &nld.value, nld.len);
- if (set->set_flags & NFT_SET_INTERVAL && key->field_count > 1) {
+ if (set->set_flags & NFT_SET_INTERVAL &&
+ key->etype == EXPR_CONCAT && key->field_count > 1) {
+ key->flags |= EXPR_F_INTERVAL;
+ netlink_gen_key(key, &nld);
+ key->flags &= ~EXPR_F_INTERVAL;
+
+ nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY, &nld.value, nld.len);
+
key->flags |= EXPR_F_INTERVAL_END;
- __netlink_gen_data(key, &nld, false);
+ netlink_gen_key(key, &nld);
key->flags &= ~EXPR_F_INTERVAL_END;
nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY_END,
&nld.value, nld.len);
+ } else {
+ netlink_gen_key(key, &nld);
+ nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY, &nld.value, nld.len);
}
break;
}
@@ -245,19 +254,34 @@ static int netlink_export_pad(unsigned char *data, const mpz_t v,
return netlink_padded_len(i->len) / BITS_PER_BYTE;
}
-static int netlink_gen_concat_data_expr(int end, const struct expr *i,
- unsigned char *data)
+static int __netlink_gen_concat_key(uint32_t flags, const struct expr *i,
+ unsigned char *data)
{
+ struct expr *expr;
+
switch (i->etype) {
case EXPR_RANGE:
- i = end ? i->right : i->left;
+ if (flags & EXPR_F_INTERVAL_END)
+ expr = i->right;
+ else
+ expr = i->left;
+
+ if (expr_basetype(expr)->type == TYPE_INTEGER &&
+ expr->byteorder == BYTEORDER_HOST_ENDIAN)
+ mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE);
+
+ i = expr;
break;
case EXPR_PREFIX:
- if (end) {
+ if (flags & EXPR_F_INTERVAL_END) {
int count;
mpz_t v;
mpz_init_bitmask(v, i->len - i->prefix_len);
+
+ if (i->byteorder == BYTEORDER_HOST_ENDIAN)
+ mpz_switch_byteorder(v, i->len / BITS_PER_BYTE);
+
mpz_add(v, i->prefix->value, v);
count = netlink_export_pad(data, v, i);
mpz_clear(v);
@@ -265,6 +289,16 @@ static int netlink_gen_concat_data_expr(int end, const struct expr *i,
}
return netlink_export_pad(data, i->prefix->value, i);
case EXPR_VALUE:
+ /* Switch byteorder only once for singleton values when the set
+ * contains concatenation of intervals.
+ */
+ if (!(flags & EXPR_F_INTERVAL))
+ break;
+
+ expr = (struct expr *)i;
+ if (expr_basetype(expr)->type == TYPE_INTEGER &&
+ expr->byteorder == BYTEORDER_HOST_ENDIAN)
+ mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE);
break;
default:
BUG("invalid expression type '%s' in set", expr_ops(i)->name);
@@ -273,23 +307,50 @@ static int netlink_gen_concat_data_expr(int end, const struct expr *i,
return netlink_export_pad(data, i->value, i);
}
-static void __netlink_gen_concat(const struct expr *expr,
- struct nft_data_linearize *nld)
+static void netlink_gen_concat_key(const struct expr *expr,
+ struct nft_data_linearize *nld)
{
unsigned int len = expr->len / BITS_PER_BYTE, offset = 0;
- int end = expr->flags & EXPR_F_INTERVAL_END;
unsigned char data[len];
const struct expr *i;
memset(data, 0, len);
list_for_each_entry(i, &expr->expressions, list)
- offset += netlink_gen_concat_data_expr(end, i, data + offset);
+ offset += __netlink_gen_concat_key(expr->flags, i, data + offset);
memcpy(nld->value, data, len);
nld->len = len;
}
+static int __netlink_gen_concat_data(int end, const struct expr *i,
+ unsigned char *data)
+{
+ switch (i->etype) {
+ case EXPR_RANGE:
+ i = end ? i->right : i->left;
+ break;
+ case EXPR_PREFIX:
+ if (end) {
+ int count;
+ mpz_t v;
+
+ mpz_init_bitmask(v, i->len - i->prefix_len);
+ mpz_add(v, i->prefix->value, v);
+ count = netlink_export_pad(data, v, i);
+ mpz_clear(v);
+ return count;
+ }
+ return netlink_export_pad(data, i->prefix->value, i);
+ case EXPR_VALUE:
+ break;
+ default:
+ BUG("invalid expression type '%s' in set", expr_ops(i)->name);
+ }
+
+ return netlink_export_pad(data, i->value, i);
+}
+
static void __netlink_gen_concat_expand(const struct expr *expr,
struct nft_data_linearize *nld)
{
@@ -300,18 +361,33 @@ static void __netlink_gen_concat_expand(const struct expr *expr,
memset(data, 0, len);
list_for_each_entry(i, &expr->expressions, list)
- offset += netlink_gen_concat_data_expr(false, i, data + offset);
+ offset += __netlink_gen_concat_data(false, i, data + offset);
list_for_each_entry(i, &expr->expressions, list)
- offset += netlink_gen_concat_data_expr(true, i, data + offset);
+ offset += __netlink_gen_concat_data(true, i, data + offset);
+
+ memcpy(nld->value, data, len);
+ nld->len = len;
+}
+
+static void __netlink_gen_concat(const struct expr *expr,
+ struct nft_data_linearize *nld)
+{
+ unsigned int len = expr->len / BITS_PER_BYTE, offset = 0;
+ unsigned char data[len];
+ const struct expr *i;
+
+ memset(data, 0, len);
+
+ list_for_each_entry(i, &expr->expressions, list)
+ offset += __netlink_gen_concat_data(expr->flags, i, data + offset);
memcpy(nld->value, data, len);
nld->len = len;
}
static void netlink_gen_concat_data(const struct expr *expr,
- struct nft_data_linearize *nld,
- bool expand)
+ struct nft_data_linearize *nld, bool expand)
{
if (expand)
__netlink_gen_concat_expand(expr, nld);
@@ -398,8 +474,25 @@ static void netlink_gen_prefix(const struct expr *expr,
nld->len = len;
}
-void __netlink_gen_data(const struct expr *expr,
- struct nft_data_linearize *data, bool expand)
+static void netlink_gen_key(const struct expr *expr,
+ struct nft_data_linearize *data)
+{
+ switch (expr->etype) {
+ case EXPR_VALUE:
+ return netlink_gen_constant_data(expr, data);
+ case EXPR_CONCAT:
+ return netlink_gen_concat_key(expr, data);
+ case EXPR_RANGE:
+ return netlink_gen_range(expr, data);
+ case EXPR_PREFIX:
+ return netlink_gen_prefix(expr, data);
+ default:
+ BUG("invalid data expression type %s\n", expr_name(expr));
+ }
+}
+
+static void __netlink_gen_data(const struct expr *expr,
+ struct nft_data_linearize *data, bool expand)
{
switch (expr->etype) {
case EXPR_VALUE:
@@ -483,48 +576,6 @@ void netlink_dump_expr(const struct nftnl_expr *nle,
fprintf(fp, "\n");
}
-static int list_rule_cb(struct nftnl_rule *nlr, void *arg)
-{
- struct netlink_ctx *ctx = arg;
- const struct handle *h = ctx->data;
- struct rule *rule;
- const char *table, *chain;
- uint32_t family;
-
- family = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY);
- table = nftnl_rule_get_str(nlr, NFTNL_RULE_TABLE);
- chain = nftnl_rule_get_str(nlr, NFTNL_RULE_CHAIN);
-
- if (h->family != family ||
- strcmp(table, h->table.name) != 0 ||
- (h->chain.name && strcmp(chain, h->chain.name) != 0))
- return 0;
-
- netlink_dump_rule(nlr, ctx);
- rule = netlink_delinearize_rule(ctx, nlr);
- list_add_tail(&rule->list, &ctx->list);
-
- return 0;
-}
-
-int netlink_list_rules(struct netlink_ctx *ctx, const struct handle *h)
-{
- struct nftnl_rule_list *rule_cache;
-
- rule_cache = mnl_nft_rule_dump(ctx, h->family);
- if (rule_cache == NULL) {
- if (errno == EINTR)
- return -1;
-
- return 0;
- }
-
- ctx->data = h;
- nftnl_rule_list_foreach(rule_cache, list_rule_cb, ctx);
- nftnl_rule_list_free(rule_cache);
- return 0;
-}
-
void netlink_dump_chain(const struct nftnl_chain *nlc, struct netlink_ctx *ctx)
{
FILE *fp = ctx->nft->output.output_fp;
@@ -706,11 +757,19 @@ static int list_table_cb(struct nftnl_table *nlt, void *arg)
return 0;
}
-int netlink_list_tables(struct netlink_ctx *ctx, const struct handle *h)
+int netlink_list_tables(struct netlink_ctx *ctx, const struct handle *h,
+ const struct nft_cache_filter *filter)
{
struct nftnl_table_list *table_cache;
+ uint32_t family = h->family;
+ const char *table = NULL;
+
+ if (filter) {
+ family = filter->list.family;
+ table = filter->list.table;
+ }
- table_cache = mnl_nft_table_dump(ctx, h->family);
+ table_cache = mnl_nft_table_dump(ctx, family, table);
if (table_cache == NULL) {
if (errno == EINTR)
return -1;
@@ -838,7 +897,7 @@ static struct expr *set_make_key(const struct nftnl_udata *attr)
return expr;
}
-static bool set_udata_key_valid(const struct expr *e, const struct datatype *d, uint32_t len)
+static bool set_udata_key_valid(const struct expr *e, uint32_t len)
{
if (!e)
return false;
@@ -968,7 +1027,7 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
dtype = set_datatype_alloc(datatype, databyteorder);
klen = nftnl_set_get_u32(nls, NFTNL_SET_DATA_LEN) * BITS_PER_BYTE;
- if (set_udata_key_valid(typeof_expr_data, dtype, klen)) {
+ if (set_udata_key_valid(typeof_expr_data, klen)) {
datatype_free(datatype_get(dtype));
set->data = typeof_expr_data;
} else {
@@ -993,7 +1052,7 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
dtype = set_datatype_alloc(keytype, keybyteorder);
klen = nftnl_set_get_u32(nls, NFTNL_SET_KEY_LEN) * BITS_PER_BYTE;
- if (set_udata_key_valid(typeof_expr_key, dtype, klen)) {
+ if (set_udata_key_valid(typeof_expr_key, klen)) {
datatype_free(datatype_get(dtype));
set->key = typeof_expr_key;
set->key_typeof_valid = true;
@@ -1138,26 +1197,40 @@ static struct expr *netlink_parse_interval_elem(const struct set *set,
return range_expr_to_prefix(range);
}
-static struct expr *concat_elem_expr(struct expr *expr,
+static struct expr *concat_elem_expr(const struct set *set, struct expr *key,
const struct datatype *dtype,
struct expr *data, int *off)
{
const struct datatype *subtype;
+ unsigned int sub_length;
+ struct expr *expr;
- subtype = concat_subtype_lookup(dtype->type, --(*off));
+ if (key) {
+ (*off)--;
+ sub_length = round_up(key->len, BITS_PER_BYTE);
- expr = constant_expr_splice(data, subtype->size);
- expr->dtype = subtype;
- expr->byteorder = subtype->byteorder;
+ expr = constant_expr_splice(data, sub_length);
+ expr->dtype = datatype_get(key->dtype);
+ expr->byteorder = key->byteorder;
+ expr->len = key->len;
+ } else {
+ subtype = concat_subtype_lookup(dtype->type, --(*off));
+ sub_length = round_up(subtype->size, BITS_PER_BYTE);
+ expr = constant_expr_splice(data, sub_length);
+ expr->dtype = subtype;
+ expr->byteorder = subtype->byteorder;
+ }
- if (expr->byteorder == BYTEORDER_HOST_ENDIAN)
+ if (expr_basetype(expr)->type == TYPE_STRING ||
+ (!(set->flags & NFT_SET_INTERVAL) &&
+ expr->byteorder == BYTEORDER_HOST_ENDIAN))
mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE);
if (expr->dtype->basetype != NULL &&
expr->dtype->basetype->type == TYPE_BITMASK)
expr = bitmask_expr_to_binops(expr);
- data->len -= netlink_padding_len(expr->len);
+ data->len -= netlink_padding_len(sub_length);
return expr;
}
@@ -1166,13 +1239,18 @@ static struct expr *netlink_parse_concat_elem_key(const struct set *set,
struct expr *data)
{
const struct datatype *dtype = set->key->dtype;
- struct expr *concat, *expr;
+ struct expr *concat, *expr, *n = NULL;
int off = dtype->subtypes;
+ if (set->key->etype == EXPR_CONCAT)
+ n = list_first_entry(&set->key->expressions, struct expr, list);
+
concat = concat_expr_alloc(&data->location);
while (off > 0) {
- expr = concat_elem_expr(expr, dtype, data, &off);
+ expr = concat_elem_expr(set, n, dtype, data, &off);
compound_expr_add(concat, expr);
+ if (set->key->etype == EXPR_CONCAT)
+ n = list_next_entry(n, list);
}
expr_free(data);
@@ -1192,7 +1270,7 @@ static struct expr *netlink_parse_concat_elem(const struct set *set,
concat = concat_expr_alloc(&data->location);
while (off > 0) {
- expr = concat_elem_expr(expr, dtype, data, &off);
+ expr = concat_elem_expr(set, NULL, dtype, data, &off);
list_add_tail(&expr->list, &expressions);
}
@@ -1204,7 +1282,7 @@ static struct expr *netlink_parse_concat_elem(const struct set *set,
while (off > 0) {
left = list_first_entry(&expressions, struct expr, list);
- expr = concat_elem_expr(expr, dtype, data, &off);
+ expr = concat_elem_expr(set, NULL, dtype, data, &off);
list_del(&left->list);
range = range_expr_alloc(&data->location, left, expr);
@@ -1306,6 +1384,7 @@ key_end:
}
expr = set_elem_expr_alloc(&netlink_location, key);
+ expr->flags |= EXPR_F_KERNEL;
if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_TIMEOUT))
expr->timeout = nftnl_set_elem_get_u64(nlse, NFTNL_SET_ELEM_TIMEOUT);
@@ -1324,7 +1403,7 @@ key_end:
nftnl_set_elem_expr_foreach(nlse, set_elem_parse_expressions,
&setelem_parse_ctx);
}
- list_splice_tail(&setelem_parse_ctx.stmt_list, &expr->stmt_list);
+ list_splice_tail_init(&setelem_parse_ctx.stmt_list, &expr->stmt_list);
if (flags & NFT_SET_ELEM_INTERVAL_END) {
expr->flags |= EXPR_F_INTERVAL_END;
@@ -1688,6 +1767,55 @@ int netlink_reset_objs(struct netlink_ctx *ctx, const struct cmd *cmd,
return err;
}
+int netlink_reset_rules(struct netlink_ctx *ctx, const struct cmd *cmd,
+ bool dump)
+{
+ const struct handle *h = &cmd->handle;
+ struct nft_cache_filter f = {
+ .list.table = h->table.name,
+ .list.chain = h->chain.name,
+ .list.rule_handle = h->handle.id,
+ };
+ struct rule *rule, *next, *crule, *cnext;
+ struct table *table;
+ struct chain *chain;
+ int ret;
+
+ ret = rule_cache_dump(ctx, h, &f, dump, true);
+
+ list_for_each_entry_safe(rule, next, &ctx->list, list) {
+ table = table_cache_find(&ctx->nft->cache.table_cache,
+ rule->handle.table.name,
+ rule->handle.family);
+ if (!table)
+ continue;
+
+ chain = chain_cache_find(table, rule->handle.chain.name);
+ if (!chain)
+ continue;
+
+ list_del(&rule->list);
+ list_for_each_entry_safe(crule, cnext, &chain->rules, list) {
+ if (crule->handle.handle.id != rule->handle.handle.id)
+ continue;
+
+ list_replace(&crule->list, &rule->list);
+ rule_free(crule);
+ rule = NULL;
+ break;
+ }
+ if (rule) {
+ list_add_tail(&rule->list, &chain->rules);
+ }
+ }
+ list_for_each_entry_safe(rule, next, &ctx->list, list) {
+ list_del(&rule->list);
+ rule_free(rule);
+ }
+
+ return ret;
+}
+
struct flowtable *
netlink_delinearize_flowtable(struct netlink_ctx *ctx,
struct nftnl_flowtable *nlo)
@@ -1711,7 +1839,8 @@ netlink_delinearize_flowtable(struct netlink_ctx *ctx,
while (dev_array[len])
len++;
- flowtable->dev_array = calloc(1, len * sizeof(char *));
+ if (len)
+ flowtable->dev_array = xmalloc(len * sizeof(char *));
for (i = 0; i < len; i++)
flowtable->dev_array[i] = xstrdup(dev_array[i]);
@@ -1755,7 +1884,8 @@ int netlink_list_flowtables(struct netlink_ctx *ctx, const struct handle *h)
struct nftnl_flowtable_list *flowtable_cache;
int err;
- flowtable_cache = mnl_nft_flowtable_dump(ctx, h->family, h->table.name);
+ flowtable_cache = mnl_nft_flowtable_dump(ctx, h->family,
+ h->table.name, NULL);
if (flowtable_cache == NULL) {
if (errno == EINTR)
return -1;
@@ -1901,7 +2031,6 @@ static void trace_gen_stmts(struct list_head *stmts,
const void *hdr;
uint32_t hlen;
unsigned int n;
- bool stacked;
if (!nftnl_trace_is_set(nlt, attr))
return;
@@ -1956,6 +2085,8 @@ restart:
n = 0;
next:
list_for_each_entry(stmt, &unordered, list) {
+ enum proto_bases b = base;
+
rel = stmt->expr;
lhs = rel->left;
@@ -1968,18 +2099,14 @@ next:
list_move_tail(&stmt->list, stmts);
n++;
- stacked = payload_is_stacked(desc, rel);
+ if (payload_is_stacked(desc, rel))
+ b--;
- if (lhs->flags & EXPR_F_PROTOCOL &&
- pctx->pbase == PROTO_BASE_INVALID) {
- payload_dependency_store(pctx, stmt, base - stacked);
- } else {
- /* Don't strip 'icmp type' from payload dump. */
- if (pctx->icmp_type == 0)
- payload_dependency_kill(pctx, lhs, ctx->family);
- if (lhs->flags & EXPR_F_PROTOCOL)
- payload_dependency_store(pctx, stmt, base - stacked);
- }
+ /* Don't strip 'icmp type' from payload dump. */
+ if (pctx->icmp_type == 0)
+ payload_dependency_kill(pctx, lhs, ctx->family);
+ if (lhs->flags & EXPR_F_PROTOCOL)
+ payload_dependency_store(pctx, stmt, b);
goto next;
}
@@ -2008,7 +2135,7 @@ static void trace_print_packet(const struct nftnl_trace *nlt,
meta_expr_alloc(&netlink_location,
NFT_META_OIF), octx);
- proto_ctx_init(&ctx, nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY), 0);
+ proto_ctx_init(&ctx, nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY), 0, false);
ll_desc = ctx.protocol[PROTO_BASE_LL_HDR].desc;
if ((ll_desc == &proto_inet || ll_desc == &proto_netdev) &&
nftnl_trace_is_set(nlt, NFTNL_TRACE_NFPROTO)) {
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index bd75ad5c..4cd6cc3a 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -30,6 +30,16 @@
#include <cache.h>
#include <xt.h>
+struct dl_proto_ctx *dl_proto_ctx(struct rule_pp_ctx *ctx)
+{
+ return ctx->dl;
+}
+
+static struct dl_proto_ctx *dl_proto_ctx_outer(struct rule_pp_ctx *ctx)
+{
+ return &ctx->_dl[0];
+}
+
static int netlink_parse_expr(const struct nftnl_expr *nle,
struct netlink_parse_ctx *ctx);
@@ -100,7 +110,7 @@ static void netlink_release_registers(struct netlink_parse_ctx *ctx)
{
int i;
- for (i = 0; i < MAX_REGS; i++)
+ for (i = 0; i <= MAX_REGS; i++)
expr_free(ctx->registers[i]);
}
@@ -134,6 +144,50 @@ err:
return NULL;
}
+static struct expr *netlink_parse_concat_key(struct netlink_parse_ctx *ctx,
+ const struct location *loc,
+ unsigned int reg,
+ const struct expr *key)
+{
+ uint32_t type = key->dtype->type;
+ unsigned int n, len = key->len;
+ struct expr *concat, *expr;
+ unsigned int consumed;
+
+ concat = concat_expr_alloc(loc);
+ n = div_round_up(fls(type), TYPE_BITS);
+
+ while (len > 0) {
+ const struct datatype *i;
+
+ expr = netlink_get_register(ctx, loc, reg);
+ if (expr == NULL) {
+ netlink_error(ctx, loc,
+ "Concat expression size mismatch");
+ goto err;
+ }
+
+ if (n > 0 && concat_subtype_id(type, --n)) {
+ i = concat_subtype_lookup(type, n);
+
+ expr_set_type(expr, i, i->byteorder);
+ }
+
+ compound_expr_add(concat, expr);
+
+ consumed = netlink_padded_len(expr->len);
+ assert(consumed > 0);
+ len -= consumed;
+ reg += netlink_register_space(expr->len);
+ }
+
+ return concat;
+
+err:
+ expr_free(concat);
+ return NULL;
+}
+
static struct expr *netlink_parse_concat_data(struct netlink_parse_ctx *ctx,
const struct location *loc,
unsigned int reg,
@@ -156,6 +210,7 @@ static struct expr *netlink_parse_concat_data(struct netlink_parse_ctx *ctx,
len -= netlink_padded_len(expr->len);
reg += netlink_register_space(expr->len);
+ expr_free(expr);
}
return concat;
@@ -174,6 +229,13 @@ static void netlink_parse_chain_verdict(struct netlink_parse_ctx *ctx,
expr_chain_export(expr->chain, chain_name);
chain = chain_binding_lookup(ctx->table, chain_name);
+
+ /* Special case: 'nft list chain x y' needs to pull in implicit chains */
+ if (!chain && !strncmp(chain_name, "__chain", strlen("__chain"))) {
+ nft_chain_cache_update(ctx->nlctx, ctx->table, chain_name);
+ chain = chain_binding_lookup(ctx->table, chain_name);
+ }
+
if (chain) {
ctx->stmt = chain_stmt_alloc(loc, chain, verdict);
expr_free(expr);
@@ -561,6 +623,10 @@ static void netlink_parse_payload_expr(struct netlink_parse_ctx *ctx,
struct expr *expr;
base = nftnl_expr_get_u32(nle, NFTNL_EXPR_PAYLOAD_BASE) + 1;
+
+ if (base == NFT_PAYLOAD_TUN_HEADER + 1)
+ base = NFT_PAYLOAD_INNER_HEADER + 1;
+
offset = nftnl_expr_get_u32(nle, NFTNL_EXPR_PAYLOAD_OFFSET) * BITS_PER_BYTE;
len = nftnl_expr_get_u32(nle, NFTNL_EXPR_PAYLOAD_LEN) * BITS_PER_BYTE;
@@ -568,9 +634,80 @@ static void netlink_parse_payload_expr(struct netlink_parse_ctx *ctx,
payload_init_raw(expr, base, offset, len);
dreg = netlink_parse_register(nle, NFTNL_EXPR_PAYLOAD_DREG);
+
+ if (ctx->inner)
+ ctx->inner_reg = dreg;
+
netlink_set_register(ctx, dreg, expr);
}
+static void netlink_parse_inner(struct netlink_parse_ctx *ctx,
+ const struct location *loc,
+ const struct nftnl_expr *nle)
+{
+ const struct proto_desc *inner_desc;
+ const struct nftnl_expr *inner_nle;
+ uint32_t hdrsize, flags, type;
+ struct expr *expr;
+
+ hdrsize = nftnl_expr_get_u32(nle, NFTNL_EXPR_INNER_HDRSIZE);
+ type = nftnl_expr_get_u32(nle, NFTNL_EXPR_INNER_TYPE);
+ flags = nftnl_expr_get_u32(nle, NFTNL_EXPR_INNER_FLAGS);
+
+ inner_nle = nftnl_expr_get(nle, NFTNL_EXPR_INNER_EXPR, NULL);
+ if (!inner_nle) {
+ netlink_error(ctx, loc, "Could not parse inner expression");
+ return;
+ }
+
+ inner_desc = proto_find_inner(type, hdrsize, flags);
+
+ ctx->inner = true;
+ if (netlink_parse_expr(inner_nle, ctx) < 0) {
+ ctx->inner = false;
+ return;
+ }
+ ctx->inner = false;
+
+ expr = netlink_get_register(ctx, loc, ctx->inner_reg);
+ assert(expr);
+
+ if (expr->etype == EXPR_PAYLOAD &&
+ expr->payload.base == PROTO_BASE_INNER_HDR) {
+ const struct proto_hdr_template *tmpl;
+ unsigned int i;
+
+ for (i = 1; i < array_size(inner_desc->templates); i++) {
+ tmpl = &inner_desc->templates[i];
+
+ if (tmpl->len == 0)
+ return;
+
+ if (tmpl->offset != expr->payload.offset ||
+ tmpl->len != expr->len)
+ continue;
+
+ expr->payload.desc = inner_desc;
+ expr->payload.tmpl = tmpl;
+ break;
+ }
+ }
+
+ switch (expr->etype) {
+ case EXPR_PAYLOAD:
+ expr->payload.inner_desc = inner_desc;
+ break;
+ case EXPR_META:
+ expr->meta.inner_desc = inner_desc;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ netlink_set_register(ctx, ctx->inner_reg, expr);
+}
+
static void netlink_parse_payload_stmt(struct netlink_parse_ctx *ctx,
const struct location *loc,
const struct nftnl_expr *nle)
@@ -646,6 +783,10 @@ static void netlink_parse_exthdr(struct netlink_parse_ctx *ctx,
stmt = exthdr_stmt_alloc(loc, expr, val);
rule_stmt_append(ctx->rule, stmt);
+ } else {
+ struct stmt *stmt = optstrip_stmt_alloc(loc, expr);
+
+ rule_stmt_append(ctx->rule, stmt);
}
}
@@ -723,6 +864,9 @@ static void netlink_parse_meta_expr(struct netlink_parse_ctx *ctx,
expr = meta_expr_alloc(loc, key);
dreg = netlink_parse_register(nle, NFTNL_EXPR_META_DREG);
+ if (ctx->inner)
+ ctx->inner_reg = dreg;
+
netlink_set_register(ctx, dreg, expr);
}
@@ -1572,7 +1716,7 @@ static void netlink_parse_dynset(struct netlink_parse_ctx *ctx,
if (expr->len < set->key->len) {
expr_free(expr);
- expr = netlink_parse_concat_expr(ctx, loc, sreg, set->key->len);
+ expr = netlink_parse_concat_key(ctx, loc, sreg, set->key);
if (expr == NULL)
return;
}
@@ -1604,6 +1748,14 @@ static void netlink_parse_dynset(struct netlink_parse_ctx *ctx,
if (nftnl_expr_is_set(nle, NFTNL_EXPR_DYNSET_SREG_DATA)) {
sreg_data = netlink_parse_register(nle, NFTNL_EXPR_DYNSET_SREG_DATA);
expr_data = netlink_get_register(ctx, loc, sreg_data);
+
+ if (expr_data->len < set->data->len) {
+ expr_free(expr_data);
+ expr_data = netlink_parse_concat_expr(ctx, loc, sreg_data, set->data->len);
+ if (expr_data == NULL)
+ netlink_error(ctx, loc,
+ "Could not parse dynset map data expressions");
+ }
}
if (expr_data != NULL) {
@@ -1716,6 +1868,7 @@ static const struct expr_handler netlink_parsers[] = {
{ .name = "bitwise", .parse = netlink_parse_bitwise },
{ .name = "byteorder", .parse = netlink_parse_byteorder },
{ .name = "payload", .parse = netlink_parse_payload },
+ { .name = "inner", .parse = netlink_parse_inner },
{ .name = "exthdr", .parse = netlink_parse_exthdr },
{ .name = "meta", .parse = netlink_parse_meta },
{ .name = "socket", .parse = netlink_parse_socket },
@@ -1812,6 +1965,37 @@ struct stmt *netlink_parse_set_expr(const struct set *set,
return pctx->stmt;
}
+static bool meta_outer_may_dependency_kill(struct rule_pp_ctx *ctx,
+ const struct expr *expr)
+{
+ struct dl_proto_ctx *dl_outer = dl_proto_ctx_outer(ctx);
+ struct stmt *stmt = dl_outer->pdctx.pdeps[expr->payload.inner_desc->base];
+ struct expr *dep;
+ uint8_t l4proto;
+
+ if (!stmt)
+ return false;
+
+ dep = stmt->expr;
+
+ if (dep->left->meta.key != NFT_META_L4PROTO)
+ return false;
+
+ l4proto = mpz_get_uint8(dep->right->value);
+
+ switch (l4proto) {
+ case IPPROTO_GRE:
+ if (expr->payload.inner_desc == &proto_gre ||
+ expr->payload.inner_desc == &proto_gretap)
+ return true;
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
+
static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp);
static void payload_match_expand(struct rule_pp_ctx *ctx,
@@ -1820,12 +2004,12 @@ static void payload_match_expand(struct rule_pp_ctx *ctx,
{
struct expr *left = payload, *right = expr->right, *tmp;
struct list_head list = LIST_HEAD_INIT(list);
- struct stmt *nstmt;
- struct expr *nexpr = NULL;
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
enum proto_bases base = left->payload.base;
- bool stacked;
+ struct expr *nexpr = NULL;
+ struct stmt *nstmt;
- payload_expr_expand(&list, left, &ctx->pctx);
+ payload_expr_expand(&list, left, &dl->pctx);
list_for_each_entry(left, &list, list) {
tmp = constant_expr_splice(right, left->len);
@@ -1840,7 +2024,7 @@ static void payload_match_expand(struct rule_pp_ctx *ctx,
nexpr = relational_expr_alloc(&expr->location, expr->op,
left, tmp);
if (expr->op == OP_EQ)
- relational_expr_pctx_update(&ctx->pctx, nexpr);
+ relational_expr_pctx_update(&dl->pctx, nexpr);
nstmt = expr_stmt_alloc(&ctx->stmt->location, nexpr);
list_add_tail(&nstmt->list, &ctx->stmt->list);
@@ -1849,22 +2033,31 @@ static void payload_match_expand(struct rule_pp_ctx *ctx,
assert(left->payload.base);
assert(base == left->payload.base);
- stacked = payload_is_stacked(ctx->pctx.protocol[base].desc, nexpr);
+ if (expr->left->payload.inner_desc) {
+ if (expr->left->payload.inner_desc == expr->left->payload.desc) {
+ nexpr->left->payload.desc = expr->left->payload.desc;
+ nexpr->left->payload.tmpl = expr->left->payload.tmpl;
+ }
+ nexpr->left->payload.inner_desc = expr->left->payload.inner_desc;
+
+ if (meta_outer_may_dependency_kill(ctx, expr->left)) {
+ struct dl_proto_ctx *dl_outer = dl_proto_ctx_outer(ctx);
+
+ payload_dependency_release(&dl_outer->pdctx, expr->left->payload.inner_desc->base);
+ }
+ }
+
+ if (payload_is_stacked(dl->pctx.protocol[base].desc, nexpr))
+ base--;
/* Remember the first payload protocol expression to
* kill it later on if made redundant by a higher layer
* payload expression.
*/
- if (ctx->pdctx.pbase == PROTO_BASE_INVALID &&
- expr->op == OP_EQ &&
- left->flags & EXPR_F_PROTOCOL) {
- payload_dependency_store(&ctx->pdctx, nstmt, base - stacked);
- } else {
- payload_dependency_kill(&ctx->pdctx, nexpr->left,
- ctx->pctx.family);
- if (expr->op == OP_EQ && left->flags & EXPR_F_PROTOCOL)
- payload_dependency_store(&ctx->pdctx, nstmt, base - stacked);
- }
+ payload_dependency_kill(&dl->pdctx, nexpr->left,
+ dl->pctx.family);
+ if (expr->op == OP_EQ && left->flags & EXPR_F_PROTOCOL)
+ payload_dependency_store(&dl->pdctx, nstmt, base);
}
list_del(&ctx->stmt->list);
stmt_free(ctx->stmt);
@@ -1873,6 +2066,7 @@ static void payload_match_expand(struct rule_pp_ctx *ctx,
static void payload_icmp_check(struct rule_pp_ctx *rctx, struct expr *expr, const struct expr *value)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(rctx);
const struct proto_hdr_template *tmpl;
const struct proto_desc *desc;
uint8_t icmp_type;
@@ -1887,10 +2081,10 @@ static void payload_icmp_check(struct rule_pp_ctx *rctx, struct expr *expr, cons
/* icmp(v6) type is 8 bit, if value is smaller or larger, this is not
* a protocol dependency.
*/
- if (expr->len != 8 || value->len != 8 || rctx->pctx.th_dep.icmp.type)
+ if (expr->len != 8 || value->len != 8 || dl->pctx.th_dep.icmp.type)
return;
- desc = rctx->pctx.protocol[expr->payload.base].desc;
+ desc = dl->pctx.protocol[expr->payload.base].desc;
if (desc == NULL)
return;
@@ -1918,7 +2112,7 @@ static void payload_icmp_check(struct rule_pp_ctx *rctx, struct expr *expr, cons
expr->payload.desc = desc;
expr->payload.tmpl = tmpl;
- rctx->pctx.th_dep.icmp.type = icmp_type;
+ dl->pctx.th_dep.icmp.type = icmp_type;
return;
}
}
@@ -1927,10 +2121,7 @@ static void payload_match_postprocess(struct rule_pp_ctx *ctx,
struct expr *expr,
struct expr *payload)
{
- enum proto_bases base = payload->payload.base;
-
- assert(payload->payload.offset >= ctx->pctx.protocol[base].offset);
- payload->payload.offset -= ctx->pctx.protocol[base].offset;
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
switch (expr->op) {
case OP_EQ:
@@ -1955,10 +2146,10 @@ static void payload_match_postprocess(struct rule_pp_ctx *ctx,
}
/* Fall through */
default:
- payload_expr_complete(payload, &ctx->pctx);
+ payload_expr_complete(payload, &dl->pctx);
expr_set_type(expr->right, payload->dtype,
payload->byteorder);
- payload_dependency_kill(&ctx->pdctx, payload, ctx->pctx.family);
+ payload_dependency_kill(&dl->pdctx, payload, dl->pctx.family);
break;
}
}
@@ -2013,7 +2204,7 @@ static bool __meta_dependency_may_kill(const struct expr *dep, uint8_t *nfproto)
}
/* We have seen a protocol key expression that restricts matching at the network
- * base, leave it in place since this is meaninful in bridge, inet and netdev
+ * base, leave it in place since this is meaningful in bridge, inet and netdev
* families. Exceptions are ICMP and ICMPv6 where this code assumes that can
* only happen with IPv4 and IPv6.
*/
@@ -2022,9 +2213,9 @@ static bool meta_may_dependency_kill(struct payload_dep_ctx *ctx,
const struct expr *expr)
{
uint8_t l4proto, nfproto = NFPROTO_UNSPEC;
- struct expr *dep = ctx->pdep->expr;
+ struct expr *dep = payload_dependency_get(ctx, PROTO_BASE_NETWORK_HDR);
- if (ctx->pbase != PROTO_BASE_NETWORK_HDR)
+ if (!dep)
return true;
if (__meta_dependency_may_kill(dep, &nfproto))
@@ -2035,14 +2226,10 @@ static bool meta_may_dependency_kill(struct payload_dep_ctx *ctx,
case NFPROTO_NETDEV:
case NFPROTO_BRIDGE:
break;
+ case NFPROTO_IPV4:
+ case NFPROTO_IPV6:
+ return family == nfproto;
default:
- if (family == NFPROTO_IPV4 &&
- nfproto != NFPROTO_IPV4)
- return false;
- else if (family == NFPROTO_IPV6 &&
- nfproto != NFPROTO_IPV6)
- return false;
-
return true;
}
@@ -2070,6 +2257,7 @@ static void ct_meta_common_postprocess(struct rule_pp_ctx *ctx,
const struct expr *expr,
enum proto_bases base)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
const struct expr *left = expr->left;
struct expr *right = expr->right;
@@ -2083,18 +2271,16 @@ static void ct_meta_common_postprocess(struct rule_pp_ctx *ctx,
expr->right->etype == EXPR_SET_REF)
break;
- relational_expr_pctx_update(&ctx->pctx, expr);
+ relational_expr_pctx_update(&dl->pctx, expr);
+
+ if (base < PROTO_BASE_TRANSPORT_HDR) {
+ if (payload_dependency_exists(&dl->pdctx, base) &&
+ meta_may_dependency_kill(&dl->pdctx,
+ dl->pctx.family, expr))
+ payload_dependency_release(&dl->pdctx, base);
- if (ctx->pdctx.pbase == PROTO_BASE_INVALID &&
- left->flags & EXPR_F_PROTOCOL) {
- payload_dependency_store(&ctx->pdctx, ctx->stmt, base);
- } else if (ctx->pdctx.pbase < PROTO_BASE_TRANSPORT_HDR) {
- if (payload_dependency_exists(&ctx->pdctx, base) &&
- meta_may_dependency_kill(&ctx->pdctx,
- ctx->pctx.family, expr))
- payload_dependency_release(&ctx->pdctx);
if (left->flags & EXPR_F_PROTOCOL)
- payload_dependency_store(&ctx->pdctx, ctx->stmt, base);
+ payload_dependency_store(&dl->pdctx, ctx->stmt, base);
}
break;
default:
@@ -2179,8 +2365,8 @@ static void binop_adjust_one(const struct expr *binop, struct expr *value,
}
}
-static void __binop_adjust(const struct expr *binop, struct expr *right,
- unsigned int shift)
+static void binop_adjust(const struct expr *binop, struct expr *right,
+ unsigned int shift)
{
struct expr *i;
@@ -2189,6 +2375,9 @@ static void __binop_adjust(const struct expr *binop, struct expr *right,
binop_adjust_one(binop, right, shift);
break;
case EXPR_SET_REF:
+ if (!set_is_anonymous(right->set->flags))
+ break;
+
list_for_each_entry(i, &right->set->init->expressions, list) {
switch (i->key->etype) {
case EXPR_VALUE:
@@ -2199,7 +2388,7 @@ static void __binop_adjust(const struct expr *binop, struct expr *right,
binop_adjust_one(binop, i->key->right, shift);
break;
case EXPR_SET_ELEM:
- __binop_adjust(binop, i->key->key, shift);
+ binop_adjust(binop, i->key->key, shift);
break;
default:
BUG("unknown expression type %s\n", expr_name(i->key));
@@ -2216,22 +2405,24 @@ static void __binop_adjust(const struct expr *binop, struct expr *right,
}
}
-static void binop_adjust(struct expr *expr, unsigned int shift)
+static void __binop_postprocess(struct rule_pp_ctx *ctx,
+ struct expr *expr,
+ struct expr *left,
+ struct expr *mask,
+ struct expr **expr_binop)
{
- __binop_adjust(expr->left, expr->right, shift);
-}
-
-static void binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr)
-{
- struct expr *binop = expr->left;
- struct expr *left = binop->left;
- struct expr *mask = binop->right;
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
+ struct expr *binop = *expr_binop;
unsigned int shift;
+ assert(binop->etype == EXPR_BINOP);
+
if ((left->etype == EXPR_PAYLOAD &&
- payload_expr_trim(left, mask, &ctx->pctx, &shift)) ||
+ payload_expr_trim(left, mask, &dl->pctx, &shift)) ||
(left->etype == EXPR_EXTHDR &&
exthdr_find_template(left, mask, &shift))) {
+ struct expr *right = NULL;
+
/* mask is implicit, binop needs to be removed.
*
* Fix all values of the expression according to the mask
@@ -2241,48 +2432,85 @@ static void binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr)
* Finally, convert the expression to 1) by replacing
* the binop with the binop payload/exthdr expression.
*/
- binop_adjust(expr, shift);
+ switch (expr->etype) {
+ case EXPR_BINOP:
+ case EXPR_RELATIONAL:
+ right = expr->right;
+ binop_adjust(binop, right, shift);
+ break;
+ case EXPR_MAP:
+ right = expr->mappings;
+ binop_adjust(binop, right, shift);
+ break;
+ default:
+ break;
+ }
- assert(expr->left->etype == EXPR_BINOP);
assert(binop->left == left);
- expr->left = expr_get(left);
- expr_free(binop);
+ *expr_binop = expr_get(left);
+
if (left->etype == EXPR_PAYLOAD)
payload_match_postprocess(ctx, expr, left);
- else if (left->etype == EXPR_EXTHDR)
- expr_set_type(expr->right, left->dtype, left->byteorder);
+ else if (left->etype == EXPR_EXTHDR && right)
+ expr_set_type(right, left->dtype, left->byteorder);
+
+ expr_free(binop);
}
}
+static void binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr,
+ struct expr **expr_binop)
+{
+ struct expr *binop = *expr_binop;
+ struct expr *left = binop->left;
+ struct expr *mask = binop->right;
+
+ __binop_postprocess(ctx, expr, left, mask, expr_binop);
+}
+
static void map_binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr)
{
- struct expr *binop = expr->left;
+ struct expr *binop = expr->map;
if (binop->op != OP_AND)
return;
if (binop->left->etype == EXPR_PAYLOAD &&
binop->right->etype == EXPR_VALUE)
- binop_postprocess(ctx, expr);
+ binop_postprocess(ctx, expr, &expr->map);
+}
+
+static bool is_shift_by_zero(const struct expr *binop)
+{
+ struct expr *rhs;
+
+ if (binop->op != OP_RSHIFT && binop->op != OP_LSHIFT)
+ return false;
+
+ rhs = binop->right;
+ if (rhs->etype != EXPR_VALUE || rhs->len > 64)
+ return false;
+
+ return mpz_get_uint64(rhs->value) == 0;
}
static void relational_binop_postprocess(struct rule_pp_ctx *ctx,
struct expr **exprp)
{
- struct expr *expr = *exprp, *binop = expr->left, *value = expr->right;
+ struct expr *expr = *exprp, *binop = expr->left, *right = expr->right;
if (binop->op == OP_AND && (expr->op == OP_NEQ || expr->op == OP_EQ) &&
- value->dtype->basetype &&
- value->dtype->basetype->type == TYPE_BITMASK) {
- switch (value->etype) {
+ right->dtype->basetype &&
+ right->dtype->basetype->type == TYPE_BITMASK) {
+ switch (right->etype) {
case EXPR_VALUE:
- if (!mpz_cmp_ui(value->value, 0)) {
+ if (!mpz_cmp_ui(right->value, 0)) {
/* Flag comparison: data & flags != 0
*
* Split the flags into a list of flag values and convert the
* op to OP_EQ.
*/
- expr_free(value);
+ expr_free(right);
expr->left = expr_get(binop->left);
expr->right = binop_tree_to_list(NULL, binop->right);
@@ -2298,8 +2526,8 @@ static void relational_binop_postprocess(struct rule_pp_ctx *ctx,
}
expr_free(binop);
} else if (binop->right->etype == EXPR_VALUE &&
- value->etype == EXPR_VALUE &&
- !mpz_cmp(value->value, binop->right->value)) {
+ right->etype == EXPR_VALUE &&
+ !mpz_cmp(right->value, binop->right->value)) {
/* Skip flag / flag representation for:
* data & flag == flag
* data & flag != flag
@@ -2309,7 +2537,7 @@ static void relational_binop_postprocess(struct rule_pp_ctx *ctx,
*exprp = flagcmp_expr_alloc(&expr->location, expr->op,
expr_get(binop->left),
binop_tree_to_list(NULL, binop->right),
- expr_get(value));
+ expr_get(right));
expr_free(expr);
}
break;
@@ -2317,7 +2545,7 @@ static void relational_binop_postprocess(struct rule_pp_ctx *ctx,
*exprp = flagcmp_expr_alloc(&expr->location, expr->op,
expr_get(binop->left),
binop_tree_to_list(NULL, binop->right),
- binop_tree_to_list(NULL, value));
+ binop_tree_to_list(NULL, right));
expr_free(expr);
break;
default:
@@ -2328,9 +2556,9 @@ static void relational_binop_postprocess(struct rule_pp_ctx *ctx,
expr_mask_is_prefix(binop->right)) {
expr->left = expr_get(binop->left);
expr->right = prefix_expr_alloc(&expr->location,
- expr_get(value),
+ expr_get(right),
expr_mask_to_prefix(binop->right));
- expr_free(value);
+ expr_free(right);
expr_free(binop);
} else if (binop->op == OP_AND &&
binop->right->etype == EXPR_VALUE) {
@@ -2357,7 +2585,21 @@ static void relational_binop_postprocess(struct rule_pp_ctx *ctx,
* payload_expr_trim will figure out if the mask is needed to match
* templates.
*/
- binop_postprocess(ctx, expr);
+ binop_postprocess(ctx, expr, &expr->left);
+ } else if (binop->op == OP_RSHIFT && binop->left->op == OP_AND &&
+ binop->right->etype == EXPR_VALUE && binop->left->right->etype == EXPR_VALUE) {
+ /* Handle 'ip version @s4' and similar, i.e. set lookups where the lhs needs
+ * fixups to mask out unwanted bits AND a shift.
+ */
+
+ binop_postprocess(ctx, binop, &binop->left);
+ if (is_shift_by_zero(binop)) {
+ struct expr *lhs = binop->left;
+
+ expr_get(lhs);
+ expr_free(binop);
+ expr->left = lhs;
+ }
}
}
@@ -2378,56 +2620,33 @@ static struct expr *string_wildcard_expr_alloc(struct location *loc,
expr->len + BITS_PER_BYTE, data);
}
-static void escaped_string_wildcard_expr_alloc(struct expr **exprp,
- unsigned int len)
-{
- struct expr *expr = *exprp, *tmp;
- char data[len + 3];
- int pos;
-
- mpz_export_data(data, expr->value, BYTEORDER_HOST_ENDIAN, len);
- pos = div_round_up(len, BITS_PER_BYTE);
- data[pos - 1] = '\\';
- data[pos] = '*';
-
- tmp = constant_expr_alloc(&expr->location, expr->dtype,
- BYTEORDER_HOST_ENDIAN,
- expr->len + BITS_PER_BYTE, data);
- expr_free(expr);
- *exprp = tmp;
-}
-
/* This calculates the string length and checks if it is nul-terminated, this
* function is quite a hack :)
*/
static bool __expr_postprocess_string(struct expr **exprp)
{
struct expr *expr = *exprp;
- unsigned int len = expr->len;
- bool nulterminated = false;
- mpz_t tmp;
-
- mpz_init(tmp);
- while (len >= BITS_PER_BYTE) {
- mpz_bitmask(tmp, BITS_PER_BYTE);
- mpz_lshift_ui(tmp, len - BITS_PER_BYTE);
- mpz_and(tmp, tmp, expr->value);
- if (mpz_cmp_ui(tmp, 0))
- break;
- else
- nulterminated = true;
- len -= BITS_PER_BYTE;
- }
+ unsigned int len = div_round_up(expr->len, BITS_PER_BYTE);
+ char data[len + 1];
- mpz_rshift_ui(tmp, len - BITS_PER_BYTE);
+ mpz_export_data(data, expr->value, BYTEORDER_HOST_ENDIAN, len);
- if (nulterminated &&
- mpz_cmp_ui(tmp, '*') == 0)
- escaped_string_wildcard_expr_alloc(exprp, len);
+ if (data[len - 1] != '\0')
+ return false;
- mpz_clear(tmp);
+ len = strlen(data);
+ if (len && data[len - 1] == '*') {
+ data[len - 1] = '\\';
+ data[len] = '*';
+ data[len + 1] = '\0';
+ expr = constant_expr_alloc(&expr->location, expr->dtype,
+ BYTEORDER_HOST_ENDIAN,
+ (len + 2) * BITS_PER_BYTE, data);
+ expr_free(*exprp);
+ *exprp = expr;
+ }
- return nulterminated;
+ return true;
}
static struct expr *expr_postprocess_string(struct expr *expr)
@@ -2451,6 +2670,7 @@ static struct expr *expr_postprocess_string(struct expr *expr)
static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
struct expr *expr = *exprp, *i;
switch (expr->etype) {
@@ -2478,16 +2698,23 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
unsigned int type = expr->dtype->type, ntype = 0;
int off = expr->dtype->subtypes;
const struct datatype *dtype;
+ LIST_HEAD(tmp);
+ struct expr *n;
- list_for_each_entry(i, &expr->expressions, list) {
+ ctx->flags |= RULE_PP_IN_CONCATENATION;
+ list_for_each_entry_safe(i, n, &expr->expressions, list) {
if (type) {
dtype = concat_subtype_lookup(type, --off);
expr_set_type(i, dtype, dtype->byteorder);
}
+ list_del(&i->list);
expr_postprocess(ctx, &i);
+ list_add_tail(&i->list, &tmp);
ntype = concat_subtype_add(ntype, i->dtype->type);
}
+ ctx->flags &= ~RULE_PP_IN_CONCATENATION;
+ list_splice(&tmp, &expr->expressions);
datatype_set(expr, concat_type_alloc(ntype));
break;
}
@@ -2503,6 +2730,27 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
expr_set_type(expr->right, &integer_type,
BYTEORDER_HOST_ENDIAN);
break;
+ case OP_AND:
+ expr_set_type(expr->right, expr->left->dtype,
+ expr->left->byteorder);
+
+ /* Do not process OP_AND in ordinary rule context.
+ *
+ * Removal needs to be performed as part of the relational
+ * operation because the RHS constant might need to be adjusted
+ * (shifted).
+ *
+ * This is different in set element context or concatenations:
+ * There is no relational operation (eq, neq and so on), thus
+ * it needs to be processed right away.
+ */
+ if ((ctx->flags & RULE_PP_REMOVE_OP_AND) &&
+ expr->left->etype == EXPR_PAYLOAD &&
+ expr->right->etype == EXPR_VALUE) {
+ __binop_postprocess(ctx, expr, expr->left, expr->right, exprp);
+ return;
+ }
+ break;
default:
expr_set_type(expr->right, expr->left->dtype,
expr->left->byteorder);
@@ -2540,8 +2788,15 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
}
break;
case EXPR_PAYLOAD:
- payload_expr_complete(expr, &ctx->pctx);
- payload_dependency_kill(&ctx->pdctx, expr, ctx->pctx.family);
+ payload_expr_complete(expr, &dl->pctx);
+ if (expr->payload.inner_desc) {
+ if (meta_outer_may_dependency_kill(ctx, expr)) {
+ struct dl_proto_ctx *dl_outer = dl_proto_ctx_outer(ctx);
+
+ payload_dependency_release(&dl_outer->pdctx, expr->payload.inner_desc->base);
+ }
+ }
+ payload_dependency_kill(&dl->pdctx, expr, dl->pctx.family);
break;
case EXPR_VALUE:
// FIXME
@@ -2565,10 +2820,12 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
expr_postprocess(ctx, &expr->prefix);
break;
case EXPR_SET_ELEM:
+ ctx->flags |= RULE_PP_IN_SET_ELEM;
expr_postprocess(ctx, &expr->key);
+ ctx->flags &= ~RULE_PP_IN_SET_ELEM;
break;
case EXPR_EXTHDR:
- exthdr_dependency_kill(&ctx->pdctx, expr, ctx->pctx.family);
+ exthdr_dependency_kill(&dl->pdctx, expr, dl->pctx.family);
break;
case EXPR_SET_REF:
case EXPR_META:
@@ -2585,7 +2842,7 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
expr_postprocess(ctx, &expr->hash.expr);
break;
case EXPR_CT:
- ct_expr_update_type(&ctx->pctx, expr);
+ ct_expr_update_type(&dl->pctx, expr);
break;
default:
BUG("unknown expression type %s\n", expr_name(expr));
@@ -2594,26 +2851,29 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
static void stmt_reject_postprocess(struct rule_pp_ctx *rctx)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(rctx);
const struct proto_desc *desc, *base;
struct stmt *stmt = rctx->stmt;
int protocol;
- switch (rctx->pctx.family) {
+ switch (dl->pctx.family) {
case NFPROTO_IPV4:
- stmt->reject.family = rctx->pctx.family;
+ stmt->reject.family = dl->pctx.family;
datatype_set(stmt->reject.expr, &icmp_code_type);
if (stmt->reject.type == NFT_REJECT_TCP_RST &&
- payload_dependency_exists(&rctx->pdctx,
+ payload_dependency_exists(&dl->pdctx,
PROTO_BASE_TRANSPORT_HDR))
- payload_dependency_release(&rctx->pdctx);
+ payload_dependency_release(&dl->pdctx,
+ PROTO_BASE_TRANSPORT_HDR);
break;
case NFPROTO_IPV6:
- stmt->reject.family = rctx->pctx.family;
+ stmt->reject.family = dl->pctx.family;
datatype_set(stmt->reject.expr, &icmpv6_code_type);
if (stmt->reject.type == NFT_REJECT_TCP_RST &&
- payload_dependency_exists(&rctx->pdctx,
+ payload_dependency_exists(&dl->pdctx,
PROTO_BASE_TRANSPORT_HDR))
- payload_dependency_release(&rctx->pdctx);
+ payload_dependency_release(&dl->pdctx,
+ PROTO_BASE_TRANSPORT_HDR);
break;
case NFPROTO_INET:
case NFPROTO_BRIDGE:
@@ -2628,8 +2888,8 @@ static void stmt_reject_postprocess(struct rule_pp_ctx *rctx)
*/
stmt->reject.verbose_print = 1;
- base = rctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
- desc = rctx->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
+ base = dl->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ desc = dl->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc;
protocol = proto_find_num(base, desc);
switch (protocol) {
case NFPROTO_IPV4: /* INET */
@@ -2646,8 +2906,9 @@ static void stmt_reject_postprocess(struct rule_pp_ctx *rctx)
break;
}
- if (payload_dependency_exists(&rctx->pdctx, PROTO_BASE_NETWORK_HDR))
- payload_dependency_release(&rctx->pdctx);
+ if (payload_dependency_exists(&dl->pdctx, PROTO_BASE_NETWORK_HDR))
+ payload_dependency_release(&dl->pdctx,
+ PROTO_BASE_NETWORK_HDR);
break;
default:
break;
@@ -2690,23 +2951,24 @@ static bool expr_may_merge_range(struct expr *expr, struct expr *prev,
static void expr_postprocess_range(struct rule_pp_ctx *ctx, enum ops op)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
struct stmt *nstmt, *stmt = ctx->stmt;
struct expr *nexpr, *rel;
- nexpr = range_expr_alloc(&ctx->pdctx.prev->location,
- expr_clone(ctx->pdctx.prev->expr->right),
+ nexpr = range_expr_alloc(&dl->pdctx.prev->location,
+ expr_clone(dl->pdctx.prev->expr->right),
expr_clone(stmt->expr->right));
expr_set_type(nexpr, stmt->expr->right->dtype,
stmt->expr->right->byteorder);
- rel = relational_expr_alloc(&ctx->pdctx.prev->location, op,
+ rel = relational_expr_alloc(&dl->pdctx.prev->location, op,
expr_clone(stmt->expr->left), nexpr);
nstmt = expr_stmt_alloc(&stmt->location, rel);
list_add_tail(&nstmt->list, &stmt->list);
- list_del(&ctx->pdctx.prev->list);
- stmt_free(ctx->pdctx.prev);
+ list_del(&dl->pdctx.prev->list);
+ stmt_free(dl->pdctx.prev);
list_del(&stmt->list);
stmt_free(stmt);
@@ -2715,26 +2977,28 @@ static void expr_postprocess_range(struct rule_pp_ctx *ctx, enum ops op)
static void stmt_expr_postprocess(struct rule_pp_ctx *ctx)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
enum ops op;
expr_postprocess(ctx, &ctx->stmt->expr);
- if (ctx->pdctx.prev && ctx->stmt &&
- ctx->stmt->ops->type == ctx->pdctx.prev->ops->type &&
- expr_may_merge_range(ctx->stmt->expr, ctx->pdctx.prev->expr, &op))
+ if (dl->pdctx.prev && ctx->stmt &&
+ ctx->stmt->ops->type == dl->pdctx.prev->ops->type &&
+ expr_may_merge_range(ctx->stmt->expr, dl->pdctx.prev->expr, &op))
expr_postprocess_range(ctx, op);
}
static void stmt_payload_binop_pp(struct rule_pp_ctx *ctx, struct expr *binop)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
struct expr *payload = binop->left;
struct expr *mask = binop->right;
unsigned int shift;
assert(payload->etype == EXPR_PAYLOAD);
- if (payload_expr_trim(payload, mask, &ctx->pctx, &shift)) {
- __binop_adjust(binop, mask, shift);
- payload_expr_complete(payload, &ctx->pctx);
+ if (payload_expr_trim(payload, mask, &dl->pctx, &shift)) {
+ binop_adjust(binop, mask, shift);
+ payload_expr_complete(payload, &dl->pctx);
expr_set_type(mask, payload->dtype,
payload->byteorder);
}
@@ -2828,7 +3092,7 @@ static void stmt_payload_binop_postprocess(struct rule_pp_ctx *ctx)
mpz_set(mask->value, bitmask);
mpz_clear(bitmask);
- binop_postprocess(ctx, expr);
+ binop_postprocess(ctx, expr, &expr->left);
if (!payload_is_known(payload)) {
mpz_set(mask->value, tmp);
mpz_clear(tmp);
@@ -2889,17 +3153,19 @@ static void stmt_payload_binop_postprocess(struct rule_pp_ctx *ctx)
static void stmt_payload_postprocess(struct rule_pp_ctx *ctx)
{
+ struct dl_proto_ctx *dl = dl_proto_ctx(ctx);
struct stmt *stmt = ctx->stmt;
+ payload_expr_complete(stmt->payload.expr, &dl->pctx);
+ if (!payload_is_known(stmt->payload.expr))
+ stmt_payload_binop_postprocess(ctx);
+
expr_postprocess(ctx, &stmt->payload.expr);
expr_set_type(stmt->payload.val,
stmt->payload.expr->dtype,
stmt->payload.expr->byteorder);
- if (!payload_is_known(stmt->payload.expr))
- stmt_payload_binop_postprocess(ctx);
-
expr_postprocess(ctx, &stmt->payload.val);
}
@@ -2936,19 +3202,75 @@ rule_maybe_reset_payload_deps(struct payload_dep_ctx *pdctx, enum stmt_types t)
payload_dependency_reset(pdctx);
}
+static bool has_inner_desc(const struct expr *expr)
+{
+ struct expr *i;
+
+ switch (expr->etype) {
+ case EXPR_BINOP:
+ return has_inner_desc(expr->left);
+ case EXPR_CONCAT:
+ list_for_each_entry(i, &expr->expressions, list) {
+ if (has_inner_desc(i))
+ return true;
+ }
+ break;
+ case EXPR_META:
+ return expr->meta.inner_desc;
+ case EXPR_PAYLOAD:
+ return expr->payload.inner_desc;
+ case EXPR_SET_ELEM:
+ return has_inner_desc(expr->key);
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static struct dl_proto_ctx *rule_update_dl_proto_ctx(struct rule_pp_ctx *rctx)
+{
+ const struct stmt *stmt = rctx->stmt;
+ bool inner = false;
+
+ switch (stmt->ops->type) {
+ case STMT_EXPRESSION:
+ if (has_inner_desc(stmt->expr->left))
+ inner = true;
+ break;
+ case STMT_SET:
+ if (has_inner_desc(stmt->set.key))
+ inner = true;
+ break;
+ default:
+ break;
+ }
+
+ if (inner)
+ rctx->dl = &rctx->_dl[1];
+ else
+ rctx->dl = &rctx->_dl[0];
+
+ return rctx->dl;
+}
+
static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *rule)
{
struct stmt *stmt, *next;
+ struct dl_proto_ctx *dl;
struct rule_pp_ctx rctx;
struct expr *expr;
memset(&rctx, 0, sizeof(rctx));
- proto_ctx_init(&rctx.pctx, rule->handle.family, ctx->debug_mask);
+ proto_ctx_init(&rctx._dl[0].pctx, rule->handle.family, ctx->debug_mask, false);
+ /* use NFPROTO_BRIDGE to set up proto_eth as base protocol. */
+ proto_ctx_init(&rctx._dl[1].pctx, NFPROTO_BRIDGE, ctx->debug_mask, true);
list_for_each_entry_safe(stmt, next, &rule->stmts, list) {
enum stmt_types type = stmt->ops->type;
rctx.stmt = stmt;
+ dl = rule_update_dl_proto_ctx(&rctx);
switch (type) {
case STMT_EXPRESSION:
@@ -2980,7 +3302,7 @@ static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *r
if (stmt->nat.addr != NULL)
expr_postprocess(&rctx, &stmt->nat.addr);
if (stmt->nat.proto != NULL) {
- payload_dependency_reset(&rctx.pdctx);
+ payload_dependency_reset(&dl->pdctx);
expr_postprocess(&rctx, &stmt->nat.proto);
}
break;
@@ -2988,7 +3310,7 @@ static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *r
if (stmt->tproxy.addr)
expr_postprocess(&rctx, &stmt->tproxy.addr);
if (stmt->tproxy.port) {
- payload_dependency_reset(&rctx.pdctx);
+ payload_dependency_reset(&dl->pdctx);
expr_postprocess(&rctx, &stmt->tproxy.port);
}
break;
@@ -3026,9 +3348,9 @@ static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *r
break;
}
- rctx.pdctx.prev = rctx.stmt;
+ dl->pdctx.prev = rctx.stmt;
- rule_maybe_reset_payload_deps(&rctx.pdctx, type);
+ rule_maybe_reset_payload_deps(&dl->pdctx, type);
}
}
@@ -3080,6 +3402,7 @@ struct rule *netlink_delinearize_rule(struct netlink_ctx *ctx,
memset(&_ctx, 0, sizeof(_ctx));
_ctx.msgs = ctx->msgs;
_ctx.debug_mask = ctx->nft->debug_mask;
+ _ctx.nlctx = ctx;
memset(&h, 0, sizeof(h));
h.family = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY);
@@ -3093,7 +3416,10 @@ struct rule *netlink_delinearize_rule(struct netlink_ctx *ctx,
pctx->rule = rule_alloc(&netlink_location, &h);
pctx->table = table_cache_find(&ctx->nft->cache.table_cache,
h.table.name, h.family);
- assert(pctx->table != NULL);
+ if (!pctx->table) {
+ errno = ENOENT;
+ return NULL;
+ }
pctx->rule->comment = nftnl_rule_get_comment(nlr);
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index 454b9ba3..3da72f50 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -178,9 +178,8 @@ static void netlink_gen_hash(struct netlink_linearize_ctx *ctx,
nft_rule_add_expr(ctx, nle, &expr->location);
}
-static void netlink_gen_payload(struct netlink_linearize_ctx *ctx,
- const struct expr *expr,
- enum nft_registers dreg)
+static struct nftnl_expr *
+__netlink_gen_payload(const struct expr *expr, enum nft_registers dreg)
{
struct nftnl_expr *nle;
@@ -193,6 +192,72 @@ static void netlink_gen_payload(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_LEN,
div_round_up(expr->len, BITS_PER_BYTE));
+ return nle;
+}
+
+static struct nftnl_expr *
+__netlink_gen_meta(const struct expr *expr, enum nft_registers dreg)
+{
+ struct nftnl_expr *nle;
+
+ nle = alloc_nft_expr("meta");
+ netlink_put_register(nle, NFTNL_EXPR_META_DREG, dreg);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_META_KEY, expr->meta.key);
+
+ return nle;
+}
+
+static struct nftnl_expr *netlink_gen_inner_expr(const struct expr *expr,
+ enum nft_registers dreg)
+{
+ struct expr *_expr = (struct expr *)expr;
+ struct nftnl_expr *nle;
+
+ switch (expr->etype) {
+ case EXPR_PAYLOAD:
+ if (expr->payload.base == NFT_PAYLOAD_INNER_HEADER + 1)
+ _expr->payload.base = NFT_PAYLOAD_TUN_HEADER + 1;
+
+ nle = __netlink_gen_payload(expr, dreg);
+ break;
+ case EXPR_META:
+ nle = __netlink_gen_meta(expr, dreg);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ return nle;
+}
+
+static void netlink_gen_inner(struct netlink_linearize_ctx *ctx,
+ const struct expr *expr,
+ enum nft_registers dreg,
+ const struct proto_desc *desc)
+{
+ struct nftnl_expr *nle;
+
+ nle = alloc_nft_expr("inner");
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_INNER_HDRSIZE, desc->inner.hdrsize);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_INNER_FLAGS, desc->inner.flags);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_INNER_TYPE, desc->inner.type);
+ nftnl_expr_set(nle, NFTNL_EXPR_INNER_EXPR, netlink_gen_inner_expr(expr, dreg), 0);
+ nft_rule_add_expr(ctx, nle, &expr->location);
+}
+
+static void netlink_gen_payload(struct netlink_linearize_ctx *ctx,
+ const struct expr *expr,
+ enum nft_registers dreg)
+{
+ struct nftnl_expr *nle;
+
+ if (expr->payload.inner_desc) {
+ netlink_gen_inner(ctx, expr, dreg, expr->payload.inner_desc);
+ return;
+ }
+
+ nle = __netlink_gen_payload(expr, dreg);
nft_rule_add_expr(ctx, nle, &expr->location);
}
@@ -221,9 +286,12 @@ static void netlink_gen_meta(struct netlink_linearize_ctx *ctx,
{
struct nftnl_expr *nle;
- nle = alloc_nft_expr("meta");
- netlink_put_register(nle, NFTNL_EXPR_META_DREG, dreg);
- nftnl_expr_set_u32(nle, NFTNL_EXPR_META_KEY, expr->meta.key);
+ if (expr->meta.inner_desc) {
+ netlink_gen_inner(ctx, expr, dreg, expr->meta.inner_desc);
+ return;
+ }
+
+ nle = __netlink_gen_meta(expr, dreg);
nft_rule_add_expr(ctx, nle, &expr->location);
}
@@ -986,7 +1054,7 @@ static void netlink_gen_exthdr_stmt(struct netlink_linearize_ctx *ctx,
nle = alloc_nft_expr("exthdr");
netlink_put_register(nle, NFTNL_EXPR_EXTHDR_SREG, sreg);
nftnl_expr_set_u8(nle, NFTNL_EXPR_EXTHDR_TYPE,
- expr->exthdr.desc->type);
+ expr->exthdr.raw_type);
nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_OFFSET, offset / BITS_PER_BYTE);
nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_LEN,
div_round_up(expr->len, BITS_PER_BYTE));
@@ -1028,8 +1096,9 @@ static void netlink_gen_payload_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_CSUM_OFFSET,
csum_off / BITS_PER_BYTE);
}
- if (expr->payload.base == PROTO_BASE_NETWORK_HDR && desc &&
- payload_needs_l4csum_update_pseudohdr(expr, desc))
+ if ((expr->payload.base == PROTO_BASE_NETWORK_HDR && desc &&
+ payload_needs_l4csum_update_pseudohdr(expr, desc)) ||
+ expr->payload.base == PROTO_BASE_INNER_HDR)
nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_FLAGS,
NFT_PAYLOAD_L4CSUM_PSEUDOHDR);
@@ -1352,6 +1421,18 @@ static void netlink_gen_fwd_stmt(struct netlink_linearize_ctx *ctx,
nft_rule_add_expr(ctx, nle, &stmt->location);
}
+static void netlink_gen_optstrip_stmt(struct netlink_linearize_ctx *ctx,
+ const struct stmt *stmt)
+{
+ struct nftnl_expr *nle = alloc_nft_expr("exthdr");
+ struct expr *expr = stmt->optstrip.expr;
+
+ nftnl_expr_set_u8(nle, NFTNL_EXPR_EXTHDR_TYPE,
+ expr->exthdr.raw_type);
+ nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_OP, expr->exthdr.op);
+ nft_rule_add_expr(ctx, nle, &expr->location);
+}
+
static void netlink_gen_queue_stmt(struct netlink_linearize_ctx *ctx,
const struct stmt *stmt)
{
@@ -1507,6 +1588,10 @@ static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx,
nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_SET_ID, set->handle.set_id);
nft_rule_add_expr(ctx, nle, &stmt->location);
+ if (stmt->map.key->timeout > 0)
+ nftnl_expr_set_u64(nle, NFTNL_EXPR_DYNSET_TIMEOUT,
+ stmt->map.key->timeout);
+
list_for_each_entry(this, &stmt->map.stmt_list, list)
num_stmts++;
@@ -1615,6 +1700,8 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
return netlink_gen_map_stmt(ctx, stmt);
case STMT_CHAIN:
return netlink_gen_chain_stmt(ctx, stmt);
+ case STMT_OPTSTRIP:
+ return netlink_gen_optstrip_stmt(ctx, stmt);
default:
BUG("unknown statement type %s\n", stmt->ops->name);
}
@@ -1672,5 +1759,16 @@ void netlink_linearize_rule(struct netlink_ctx *ctx,
nftnl_udata_buf_free(udata);
}
- netlink_dump_rule(lctx->nlr, ctx);
+ if (ctx->nft->debug_mask & NFT_DEBUG_NETLINK) {
+ nftnl_rule_set_str(lctx->nlr, NFTNL_RULE_TABLE,
+ rule->handle.table.name);
+ if (rule->handle.chain.name)
+ nftnl_rule_set_str(lctx->nlr, NFTNL_RULE_CHAIN,
+ rule->handle.chain.name);
+
+ netlink_dump_rule(lctx->nlr, ctx);
+
+ nftnl_rule_unset(lctx->nlr, NFTNL_RULE_CHAIN);
+ nftnl_rule_unset(lctx->nlr, NFTNL_RULE_TABLE);
+ }
}
diff --git a/src/numgen.c b/src/numgen.c
index ea2b2626..256514d1 100644
--- a/src/numgen.c
+++ b/src/numgen.c
@@ -4,8 +4,8 @@
* 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 <nftables.h>
diff --git a/src/optimize.c b/src/optimize.c
new file mode 100644
index 00000000..ff4f2627
--- /dev/null
+++ b/src/optimize.c
@@ -0,0 +1,1252 @@
+/*
+ * Copyright (c) 2021 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
+ */
+
+/* Funded through the NGI0 PET Fund established by NLnet (https://nlnet.nl)
+ * with support from the European Commission's Next Generation Internet
+ * programme.
+ */
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <nftables.h>
+#include <parser.h>
+#include <expression.h>
+#include <statement.h>
+#include <utils.h>
+#include <erec.h>
+
+#define MAX_STMTS 32
+
+struct optimize_ctx {
+ struct stmt *stmt[MAX_STMTS];
+ uint32_t num_stmts;
+
+ struct stmt ***stmt_matrix;
+ struct rule **rule;
+ uint32_t num_rules;
+};
+
+static bool __expr_cmp(const struct expr *expr_a, const struct expr *expr_b)
+{
+ if (expr_a->etype != expr_b->etype)
+ return false;
+
+ switch (expr_a->etype) {
+ case EXPR_PAYLOAD:
+ if (expr_a->payload.base != expr_b->payload.base)
+ return false;
+ if (expr_a->payload.offset != expr_b->payload.offset)
+ return false;
+ if (expr_a->payload.desc != expr_b->payload.desc)
+ return false;
+ if (expr_a->payload.inner_desc != expr_b->payload.inner_desc)
+ return false;
+ if (expr_a->payload.tmpl != expr_b->payload.tmpl)
+ return false;
+ break;
+ case EXPR_EXTHDR:
+ if (expr_a->exthdr.desc != expr_b->exthdr.desc)
+ return false;
+ if (expr_a->exthdr.tmpl != expr_b->exthdr.tmpl)
+ return false;
+ break;
+ case EXPR_META:
+ if (expr_a->meta.key != expr_b->meta.key)
+ return false;
+ if (expr_a->meta.base != expr_b->meta.base)
+ return false;
+ break;
+ case EXPR_CT:
+ if (expr_a->ct.key != expr_b->ct.key)
+ return false;
+ if (expr_a->ct.base != expr_b->ct.base)
+ return false;
+ if (expr_a->ct.direction != expr_b->ct.direction)
+ return false;
+ if (expr_a->ct.nfproto != expr_b->ct.nfproto)
+ return false;
+ break;
+ case EXPR_RT:
+ if (expr_a->rt.key != expr_b->rt.key)
+ return false;
+ break;
+ case EXPR_SOCKET:
+ if (expr_a->socket.key != expr_b->socket.key)
+ return false;
+ if (expr_a->socket.level != expr_b->socket.level)
+ return false;
+ break;
+ case EXPR_OSF:
+ if (expr_a->osf.ttl != expr_b->osf.ttl)
+ return false;
+ if (expr_a->osf.flags != expr_b->osf.flags)
+ return false;
+ break;
+ case EXPR_XFRM:
+ if (expr_a->xfrm.key != expr_b->xfrm.key)
+ return false;
+ if (expr_a->xfrm.direction != expr_b->xfrm.direction)
+ return false;
+ break;
+ case EXPR_FIB:
+ if (expr_a->fib.flags != expr_b->fib.flags)
+ return false;
+ if (expr_a->fib.result != expr_b->fib.result)
+ return false;
+ break;
+ case EXPR_NUMGEN:
+ if (expr_a->numgen.type != expr_b->numgen.type)
+ return false;
+ if (expr_a->numgen.mod != expr_b->numgen.mod)
+ return false;
+ if (expr_a->numgen.offset != expr_b->numgen.offset)
+ return false;
+ break;
+ case EXPR_HASH:
+ if (expr_a->hash.mod != expr_b->hash.mod)
+ return false;
+ if (expr_a->hash.seed_set != expr_b->hash.seed_set)
+ return false;
+ if (expr_a->hash.seed != expr_b->hash.seed)
+ return false;
+ if (expr_a->hash.offset != expr_b->hash.offset)
+ return false;
+ if (expr_a->hash.type != expr_b->hash.type)
+ return false;
+ break;
+ case EXPR_BINOP:
+ return __expr_cmp(expr_a->left, expr_b->left);
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+static bool stmt_expr_supported(const struct expr *expr)
+{
+ switch (expr->right->etype) {
+ case EXPR_SYMBOL:
+ case EXPR_RANGE:
+ case EXPR_PREFIX:
+ case EXPR_SET:
+ case EXPR_LIST:
+ case EXPR_VALUE:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static bool expr_symbol_set(const struct expr *expr)
+{
+ return expr->right->etype == EXPR_SYMBOL &&
+ expr->right->symtype == SYMBOL_SET;
+}
+
+static bool __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b,
+ bool fully_compare)
+{
+ struct expr *expr_a, *expr_b;
+
+ if (stmt_a->ops->type != stmt_b->ops->type)
+ return false;
+
+ switch (stmt_a->ops->type) {
+ case STMT_EXPRESSION:
+ expr_a = stmt_a->expr;
+ expr_b = stmt_b->expr;
+
+ if (expr_a->op != expr_b->op)
+ return false;
+ if (expr_a->op != OP_IMPLICIT && expr_a->op != OP_EQ)
+ return false;
+
+ if (fully_compare) {
+ if (!stmt_expr_supported(expr_a) ||
+ !stmt_expr_supported(expr_b))
+ return false;
+
+ if (expr_symbol_set(expr_a) ||
+ expr_symbol_set(expr_b))
+ return false;
+ }
+
+ return __expr_cmp(expr_a->left, expr_b->left);
+ case STMT_COUNTER:
+ case STMT_NOTRACK:
+ break;
+ case STMT_VERDICT:
+ if (!fully_compare)
+ break;
+
+ expr_a = stmt_a->expr;
+ expr_b = stmt_b->expr;
+
+ if (expr_a->etype != expr_b->etype)
+ return false;
+
+ if (expr_a->etype == EXPR_MAP &&
+ expr_b->etype == EXPR_MAP)
+ return __expr_cmp(expr_a->map, expr_b->map);
+ break;
+ case STMT_LOG:
+ if (stmt_a->log.snaplen != stmt_b->log.snaplen ||
+ stmt_a->log.group != stmt_b->log.group ||
+ stmt_a->log.qthreshold != stmt_b->log.qthreshold ||
+ stmt_a->log.level != stmt_b->log.level ||
+ stmt_a->log.logflags != stmt_b->log.logflags ||
+ stmt_a->log.flags != stmt_b->log.flags)
+ return false;
+
+ if (!!stmt_a->log.prefix ^ !!stmt_b->log.prefix)
+ return false;
+
+ if (!stmt_a->log.prefix)
+ return true;
+
+ if (stmt_a->log.prefix->etype != EXPR_VALUE ||
+ stmt_b->log.prefix->etype != EXPR_VALUE ||
+ mpz_cmp(stmt_a->log.prefix->value, stmt_b->log.prefix->value))
+ return false;
+ break;
+ case STMT_REJECT:
+ if (stmt_a->reject.family != stmt_b->reject.family ||
+ stmt_a->reject.type != stmt_b->reject.type ||
+ stmt_a->reject.icmp_code != stmt_b->reject.icmp_code)
+ return false;
+
+ if (!!stmt_a->reject.expr ^ !!stmt_b->reject.expr)
+ return false;
+
+ if (!stmt_a->reject.expr)
+ return true;
+
+ if (__expr_cmp(stmt_a->reject.expr, stmt_b->reject.expr))
+ return false;
+ break;
+ case STMT_NAT:
+ if (stmt_a->nat.type != stmt_b->nat.type ||
+ stmt_a->nat.flags != stmt_b->nat.flags ||
+ stmt_a->nat.family != stmt_b->nat.family ||
+ stmt_a->nat.type_flags != stmt_b->nat.type_flags ||
+ (stmt_a->nat.addr &&
+ stmt_a->nat.addr->etype != EXPR_SYMBOL &&
+ stmt_a->nat.addr->etype != EXPR_RANGE) ||
+ (stmt_b->nat.addr &&
+ stmt_b->nat.addr->etype != EXPR_SYMBOL &&
+ stmt_b->nat.addr->etype != EXPR_RANGE) ||
+ (stmt_a->nat.proto &&
+ stmt_a->nat.proto->etype != EXPR_SYMBOL &&
+ stmt_a->nat.proto->etype != EXPR_RANGE) ||
+ (stmt_b->nat.proto &&
+ stmt_b->nat.proto->etype != EXPR_SYMBOL &&
+ stmt_b->nat.proto->etype != EXPR_RANGE))
+ return false;
+
+ return true;
+ default:
+ /* ... Merging anything else is yet unsupported. */
+ return false;
+ }
+
+ return true;
+}
+
+static bool expr_verdict_eq(const struct expr *expr_a, const struct expr *expr_b)
+{
+ if (expr_a->verdict != expr_b->verdict)
+ return false;
+ if (expr_a->chain && expr_b->chain) {
+ if (expr_a->chain->etype != expr_b->chain->etype)
+ return false;
+ if (expr_a->chain->etype == EXPR_VALUE &&
+ strcmp(expr_a->chain->identifier, expr_b->chain->identifier))
+ return false;
+ } else if (expr_a->chain || expr_b->chain) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool stmt_verdict_eq(const struct stmt *stmt_a, const struct stmt *stmt_b)
+{
+ struct expr *expr_a, *expr_b;
+
+ assert (stmt_a->ops->type == STMT_VERDICT);
+
+ expr_a = stmt_a->expr;
+ expr_b = stmt_b->expr;
+ if (expr_a->etype == EXPR_VERDICT &&
+ expr_b->etype == EXPR_VERDICT)
+ return expr_verdict_eq(expr_a, expr_b);
+
+ if (expr_a->etype == EXPR_MAP &&
+ expr_b->etype == EXPR_MAP)
+ return __expr_cmp(expr_a->map, expr_b->map);
+
+ return false;
+}
+
+static bool stmt_type_find(struct optimize_ctx *ctx, const struct stmt *stmt)
+{
+ bool unsupported_exists = false;
+ uint32_t i;
+
+ for (i = 0; i < ctx->num_stmts; i++) {
+ if (ctx->stmt[i]->ops->type == STMT_INVALID)
+ unsupported_exists = true;
+
+ if (__stmt_type_eq(stmt, ctx->stmt[i], false))
+ return true;
+ }
+
+ switch (stmt->ops->type) {
+ case STMT_EXPRESSION:
+ case STMT_VERDICT:
+ case STMT_COUNTER:
+ case STMT_NOTRACK:
+ case STMT_LOG:
+ case STMT_NAT:
+ case STMT_REJECT:
+ break;
+ default:
+ /* add unsupported statement only once to statement matrix. */
+ if (unsupported_exists)
+ return true;
+ break;
+ }
+
+ return false;
+}
+
+static struct stmt_ops unsupported_stmt_ops = {
+ .type = STMT_INVALID,
+ .name = "unsupported",
+};
+
+static int rule_collect_stmts(struct optimize_ctx *ctx, struct rule *rule)
+{
+ struct stmt *stmt, *clone;
+
+ list_for_each_entry(stmt, &rule->stmts, list) {
+ if (stmt_type_find(ctx, stmt))
+ continue;
+
+ /* No refcounter available in statement objects, clone it to
+ * to store in the array of selectors.
+ */
+ clone = stmt_alloc(&internal_location, stmt->ops);
+ switch (stmt->ops->type) {
+ case STMT_EXPRESSION:
+ if (stmt->expr->op != OP_IMPLICIT &&
+ stmt->expr->op != OP_EQ) {
+ clone->ops = &unsupported_stmt_ops;
+ break;
+ }
+ if (stmt->expr->left->etype == EXPR_CONCAT) {
+ clone->ops = &unsupported_stmt_ops;
+ break;
+ }
+ case STMT_VERDICT:
+ clone->expr = expr_get(stmt->expr);
+ break;
+ case STMT_COUNTER:
+ case STMT_NOTRACK:
+ break;
+ case STMT_LOG:
+ memcpy(&clone->log, &stmt->log, sizeof(clone->log));
+ if (stmt->log.prefix)
+ clone->log.prefix = expr_get(stmt->log.prefix);
+ break;
+ case STMT_NAT:
+ clone->nat.type = stmt->nat.type;
+ clone->nat.family = stmt->nat.family;
+ if (stmt->nat.addr)
+ clone->nat.addr = expr_clone(stmt->nat.addr);
+ if (stmt->nat.proto)
+ clone->nat.proto = expr_clone(stmt->nat.proto);
+ clone->nat.flags = stmt->nat.flags;
+ clone->nat.type_flags = stmt->nat.type_flags;
+ break;
+ case STMT_REJECT:
+ if (stmt->reject.expr)
+ clone->reject.expr = expr_get(stmt->reject.expr);
+ clone->reject.type = stmt->reject.type;
+ clone->reject.icmp_code = stmt->reject.icmp_code;
+ clone->reject.family = stmt->reject.family;
+ break;
+ default:
+ clone->ops = &unsupported_stmt_ops;
+ break;
+ }
+
+ ctx->stmt[ctx->num_stmts++] = clone;
+ if (ctx->num_stmts >= MAX_STMTS)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int unsupported_in_stmt_matrix(const struct optimize_ctx *ctx)
+{
+ uint32_t i;
+
+ for (i = 0; i < ctx->num_stmts; i++) {
+ if (ctx->stmt[i]->ops->type == STMT_INVALID)
+ return i;
+ }
+ /* this should not happen. */
+ return -1;
+}
+
+static int cmd_stmt_find_in_stmt_matrix(struct optimize_ctx *ctx, struct stmt *stmt)
+{
+ uint32_t i;
+
+ for (i = 0; i < ctx->num_stmts; i++) {
+ if (__stmt_type_eq(stmt, ctx->stmt[i], false))
+ return i;
+ }
+
+ return -1;
+}
+
+static struct stmt unsupported_stmt = {
+ .ops = &unsupported_stmt_ops,
+};
+
+static void rule_build_stmt_matrix_stmts(struct optimize_ctx *ctx,
+ struct rule *rule, uint32_t *i)
+{
+ struct stmt *stmt;
+ int k;
+
+ list_for_each_entry(stmt, &rule->stmts, list) {
+ k = cmd_stmt_find_in_stmt_matrix(ctx, stmt);
+ if (k < 0) {
+ k = unsupported_in_stmt_matrix(ctx);
+ assert(k >= 0);
+ ctx->stmt_matrix[*i][k] = &unsupported_stmt;
+ continue;
+ }
+ ctx->stmt_matrix[*i][k] = stmt;
+ }
+ ctx->rule[(*i)++] = rule;
+}
+
+static int stmt_verdict_find(const struct optimize_ctx *ctx)
+{
+ uint32_t i;
+
+ for (i = 0; i < ctx->num_stmts; i++) {
+ if (ctx->stmt[i]->ops->type != STMT_VERDICT)
+ continue;
+
+ return i;
+ }
+
+ return -1;
+}
+
+struct merge {
+ /* interval of rules to be merged */
+ uint32_t rule_from;
+ uint32_t num_rules;
+ /* statements to be merged (index relative to statement matrix) */
+ uint32_t stmt[MAX_STMTS];
+ uint32_t num_stmts;
+};
+
+static void merge_expr_stmts(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to,
+ const struct merge *merge,
+ struct stmt *stmt_a)
+{
+ struct expr *expr_a, *expr_b, *set, *elem;
+ struct stmt *stmt_b;
+ uint32_t i;
+
+ set = set_expr_alloc(&internal_location, NULL);
+ set->set_flags |= NFT_SET_ANONYMOUS;
+
+ expr_a = stmt_a->expr->right;
+ elem = set_elem_expr_alloc(&internal_location, expr_get(expr_a));
+ compound_expr_add(set, elem);
+
+ for (i = from + 1; i <= to; i++) {
+ stmt_b = ctx->stmt_matrix[i][merge->stmt[0]];
+ expr_b = stmt_b->expr->right;
+ elem = set_elem_expr_alloc(&internal_location, expr_get(expr_b));
+ compound_expr_add(set, elem);
+ }
+
+ expr_free(stmt_a->expr->right);
+ stmt_a->expr->right = set;
+}
+
+static void merge_vmap(const struct optimize_ctx *ctx,
+ struct stmt *stmt_a, const struct stmt *stmt_b)
+{
+ struct expr *mappings, *mapping, *expr;
+
+ mappings = stmt_b->expr->mappings;
+ list_for_each_entry(expr, &mappings->expressions, list) {
+ mapping = expr_clone(expr);
+ compound_expr_add(stmt_a->expr->mappings, mapping);
+ }
+}
+
+static void merge_verdict_stmts(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to,
+ const struct merge *merge,
+ struct stmt *stmt_a)
+{
+ struct stmt *stmt_b;
+ uint32_t i;
+
+ for (i = from + 1; i <= to; i++) {
+ stmt_b = ctx->stmt_matrix[i][merge->stmt[0]];
+ switch (stmt_b->ops->type) {
+ case STMT_VERDICT:
+ switch (stmt_b->expr->etype) {
+ case EXPR_MAP:
+ merge_vmap(ctx, stmt_a, stmt_b);
+ break;
+ default:
+ assert(0);
+ }
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+}
+
+static void merge_stmts(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to, const struct merge *merge)
+{
+ struct stmt *stmt_a = ctx->stmt_matrix[from][merge->stmt[0]];
+
+ switch (stmt_a->ops->type) {
+ case STMT_EXPRESSION:
+ merge_expr_stmts(ctx, from, to, merge, stmt_a);
+ break;
+ case STMT_VERDICT:
+ merge_verdict_stmts(ctx, from, to, merge, stmt_a);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+static void __merge_concat(const struct optimize_ctx *ctx, uint32_t i,
+ const struct merge *merge, struct list_head *concat_list)
+{
+ struct expr *concat, *next, *expr, *concat_clone, *clone;
+ LIST_HEAD(pending_list);
+ struct stmt *stmt_a;
+ uint32_t k;
+
+ concat = concat_expr_alloc(&internal_location);
+ list_add(&concat->list, concat_list);
+
+ for (k = 0; k < merge->num_stmts; k++) {
+ list_for_each_entry_safe(concat, next, concat_list, list) {
+ stmt_a = ctx->stmt_matrix[i][merge->stmt[k]];
+ switch (stmt_a->expr->right->etype) {
+ case EXPR_SET:
+ list_for_each_entry(expr, &stmt_a->expr->right->expressions, list) {
+ concat_clone = expr_clone(concat);
+ clone = expr_clone(expr->key);
+ compound_expr_add(concat_clone, clone);
+ list_add_tail(&concat_clone->list, &pending_list);
+ }
+ list_del(&concat->list);
+ expr_free(concat);
+ break;
+ case EXPR_SYMBOL:
+ case EXPR_VALUE:
+ case EXPR_PREFIX:
+ case EXPR_RANGE:
+ clone = expr_clone(stmt_a->expr->right);
+ compound_expr_add(concat, clone);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+ list_splice_init(&pending_list, concat_list);
+ }
+}
+
+static void __merge_concat_stmts(const struct optimize_ctx *ctx, uint32_t i,
+ const struct merge *merge, struct expr *set)
+{
+ struct expr *concat, *next, *elem;
+ LIST_HEAD(concat_list);
+
+ __merge_concat(ctx, i, merge, &concat_list);
+
+ list_for_each_entry_safe(concat, next, &concat_list, list) {
+ list_del(&concat->list);
+ elem = set_elem_expr_alloc(&internal_location, concat);
+ compound_expr_add(set, elem);
+ }
+}
+
+static void merge_concat_stmts(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to,
+ const struct merge *merge)
+{
+ struct stmt *stmt, *stmt_a;
+ struct expr *concat, *set;
+ uint32_t i, k;
+
+ stmt = ctx->stmt_matrix[from][merge->stmt[0]];
+ /* build concatenation of selectors, eg. ifname . ip daddr . tcp dport */
+ concat = concat_expr_alloc(&internal_location);
+
+ for (k = 0; k < merge->num_stmts; k++) {
+ stmt_a = ctx->stmt_matrix[from][merge->stmt[k]];
+ compound_expr_add(concat, expr_get(stmt_a->expr->left));
+ }
+ expr_free(stmt->expr->left);
+ stmt->expr->left = concat;
+
+ /* build set data contenation, eg. { eth0 . 1.1.1.1 . 22 } */
+ set = set_expr_alloc(&internal_location, NULL);
+ set->set_flags |= NFT_SET_ANONYMOUS;
+
+ for (i = from; i <= to; i++)
+ __merge_concat_stmts(ctx, i, merge, set);
+
+ expr_free(stmt->expr->right);
+ stmt->expr->right = set;
+
+ for (k = 1; k < merge->num_stmts; k++) {
+ stmt_a = ctx->stmt_matrix[from][merge->stmt[k]];
+ list_del(&stmt_a->list);
+ stmt_free(stmt_a);
+ }
+}
+
+static void build_verdict_map(struct expr *expr, struct stmt *verdict, struct expr *set)
+{
+ struct expr *item, *elem, *mapping;
+
+ switch (expr->etype) {
+ case EXPR_LIST:
+ list_for_each_entry(item, &expr->expressions, list) {
+ elem = set_elem_expr_alloc(&internal_location, expr_get(item));
+ mapping = mapping_expr_alloc(&internal_location, elem,
+ expr_get(verdict->expr));
+ compound_expr_add(set, mapping);
+ }
+ break;
+ case EXPR_SET:
+ list_for_each_entry(item, &expr->expressions, list) {
+ elem = set_elem_expr_alloc(&internal_location, expr_get(item->key));
+ mapping = mapping_expr_alloc(&internal_location, elem,
+ expr_get(verdict->expr));
+ compound_expr_add(set, mapping);
+ }
+ break;
+ case EXPR_PREFIX:
+ case EXPR_RANGE:
+ case EXPR_VALUE:
+ case EXPR_SYMBOL:
+ case EXPR_CONCAT:
+ elem = set_elem_expr_alloc(&internal_location, expr_get(expr));
+ mapping = mapping_expr_alloc(&internal_location, elem,
+ expr_get(verdict->expr));
+ compound_expr_add(set, mapping);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+}
+
+static void remove_counter(const struct optimize_ctx *ctx, uint32_t from)
+{
+ struct stmt *stmt;
+ uint32_t i;
+
+ /* remove counter statement */
+ for (i = 0; i < ctx->num_stmts; i++) {
+ stmt = ctx->stmt_matrix[from][i];
+ if (!stmt)
+ continue;
+
+ if (stmt->ops->type == STMT_COUNTER) {
+ list_del(&stmt->list);
+ stmt_free(stmt);
+ }
+ }
+}
+
+static void merge_stmts_vmap(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to,
+ const struct merge *merge)
+{
+ struct stmt *stmt_a = ctx->stmt_matrix[from][merge->stmt[0]];
+ struct stmt *stmt_b, *verdict_a, *verdict_b, *stmt;
+ struct expr *expr_a, *expr_b, *expr, *left, *set;
+ uint32_t i;
+ int k;
+
+ k = stmt_verdict_find(ctx);
+ assert(k >= 0);
+
+ verdict_a = ctx->stmt_matrix[from][k];
+ set = set_expr_alloc(&internal_location, NULL);
+ set->set_flags |= NFT_SET_ANONYMOUS;
+
+ expr_a = stmt_a->expr->right;
+ build_verdict_map(expr_a, verdict_a, set);
+ for (i = from + 1; i <= to; i++) {
+ stmt_b = ctx->stmt_matrix[i][merge->stmt[0]];
+ expr_b = stmt_b->expr->right;
+ verdict_b = ctx->stmt_matrix[i][k];
+
+ build_verdict_map(expr_b, verdict_b, set);
+ }
+
+ left = expr_get(stmt_a->expr->left);
+ expr = map_expr_alloc(&internal_location, left, set);
+ stmt = verdict_stmt_alloc(&internal_location, expr);
+
+ remove_counter(ctx, from);
+ list_add(&stmt->list, &stmt_a->list);
+ list_del(&stmt_a->list);
+ stmt_free(stmt_a);
+ list_del(&verdict_a->list);
+ stmt_free(verdict_a);
+}
+
+static void __merge_concat_stmts_vmap(const struct optimize_ctx *ctx,
+ uint32_t i, const struct merge *merge,
+ struct expr *set, struct stmt *verdict)
+{
+ struct expr *concat, *next, *elem, *mapping;
+ LIST_HEAD(concat_list);
+
+ __merge_concat(ctx, i, merge, &concat_list);
+
+ list_for_each_entry_safe(concat, next, &concat_list, list) {
+ list_del(&concat->list);
+ elem = set_elem_expr_alloc(&internal_location, concat);
+ mapping = mapping_expr_alloc(&internal_location, elem,
+ expr_get(verdict->expr));
+ compound_expr_add(set, mapping);
+ }
+}
+
+static void merge_concat_stmts_vmap(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to,
+ const struct merge *merge)
+{
+ struct stmt *orig_stmt = ctx->stmt_matrix[from][merge->stmt[0]];
+ struct stmt *stmt, *stmt_a, *verdict;
+ struct expr *concat_a, *expr, *set;
+ uint32_t i;
+ int k;
+
+ k = stmt_verdict_find(ctx);
+ assert(k >= 0);
+
+ /* build concatenation of selectors, eg. ifname . ip daddr . tcp dport */
+ concat_a = concat_expr_alloc(&internal_location);
+ for (i = 0; i < merge->num_stmts; i++) {
+ stmt_a = ctx->stmt_matrix[from][merge->stmt[i]];
+ compound_expr_add(concat_a, expr_get(stmt_a->expr->left));
+ }
+
+ /* build set data contenation, eg. { eth0 . 1.1.1.1 . 22 : accept } */
+ set = set_expr_alloc(&internal_location, NULL);
+ set->set_flags |= NFT_SET_ANONYMOUS;
+
+ for (i = from; i <= to; i++) {
+ verdict = ctx->stmt_matrix[i][k];
+ __merge_concat_stmts_vmap(ctx, i, merge, set, verdict);
+ }
+
+ expr = map_expr_alloc(&internal_location, concat_a, set);
+ stmt = verdict_stmt_alloc(&internal_location, expr);
+
+ remove_counter(ctx, from);
+ list_add(&stmt->list, &orig_stmt->list);
+ list_del(&orig_stmt->list);
+ stmt_free(orig_stmt);
+
+ for (i = 1; i < merge->num_stmts; i++) {
+ stmt_a = ctx->stmt_matrix[from][merge->stmt[i]];
+ list_del(&stmt_a->list);
+ stmt_free(stmt_a);
+ }
+
+ verdict = ctx->stmt_matrix[from][k];
+ list_del(&verdict->list);
+ stmt_free(verdict);
+}
+
+static bool stmt_verdict_cmp(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to)
+{
+ struct stmt *stmt_a, *stmt_b;
+ uint32_t i;
+ int k;
+
+ k = stmt_verdict_find(ctx);
+ if (k < 0)
+ return true;
+
+ for (i = from; i + 1 <= to; i++) {
+ stmt_a = ctx->stmt_matrix[i][k];
+ stmt_b = ctx->stmt_matrix[i + 1][k];
+ if (!stmt_a && !stmt_b)
+ continue;
+ if (!stmt_a || !stmt_b)
+ return false;
+ if (!stmt_verdict_eq(stmt_a, stmt_b))
+ return false;
+ }
+
+ return true;
+}
+
+static int stmt_nat_find(const struct optimize_ctx *ctx)
+{
+ uint32_t i;
+
+ for (i = 0; i < ctx->num_stmts; i++) {
+ if (ctx->stmt[i]->ops->type != STMT_NAT)
+ continue;
+
+ return i;
+ }
+
+ return -1;
+}
+
+static struct expr *stmt_nat_expr(struct stmt *nat_stmt)
+{
+ struct expr *nat_expr;
+
+ if (nat_stmt->nat.proto) {
+ nat_expr = concat_expr_alloc(&internal_location);
+ compound_expr_add(nat_expr, expr_get(nat_stmt->nat.addr));
+ compound_expr_add(nat_expr, expr_get(nat_stmt->nat.proto));
+ expr_free(nat_stmt->nat.proto);
+ nat_stmt->nat.proto = NULL;
+ } else {
+ nat_expr = expr_get(nat_stmt->nat.addr);
+ }
+
+ return nat_expr;
+}
+
+static void merge_nat(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to,
+ const struct merge *merge)
+{
+ struct expr *expr, *set, *elem, *nat_expr, *mapping, *left;
+ struct stmt *stmt, *nat_stmt;
+ uint32_t i;
+ int k;
+
+ k = stmt_nat_find(ctx);
+ assert(k >= 0);
+
+ set = set_expr_alloc(&internal_location, NULL);
+ set->set_flags |= NFT_SET_ANONYMOUS;
+
+ for (i = from; i <= to; i++) {
+ stmt = ctx->stmt_matrix[i][merge->stmt[0]];
+ expr = stmt->expr->right;
+
+ nat_stmt = ctx->stmt_matrix[i][k];
+ nat_expr = stmt_nat_expr(nat_stmt);
+
+ elem = set_elem_expr_alloc(&internal_location, expr_get(expr));
+ mapping = mapping_expr_alloc(&internal_location, elem, nat_expr);
+ compound_expr_add(set, mapping);
+ }
+
+ stmt = ctx->stmt_matrix[from][merge->stmt[0]];
+ left = expr_get(stmt->expr->left);
+ expr = map_expr_alloc(&internal_location, left, set);
+
+ nat_stmt = ctx->stmt_matrix[from][k];
+ expr_free(nat_stmt->nat.addr);
+ nat_stmt->nat.addr = expr;
+
+ remove_counter(ctx, from);
+ list_del(&stmt->list);
+ stmt_free(stmt);
+}
+
+static void merge_concat_nat(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to,
+ const struct merge *merge)
+{
+ struct expr *expr, *set, *elem, *nat_expr, *mapping, *left, *concat;
+ struct stmt *stmt, *nat_stmt;
+ uint32_t i, j;
+ int k;
+
+ k = stmt_nat_find(ctx);
+ assert(k >= 0);
+
+ set = set_expr_alloc(&internal_location, NULL);
+ set->set_flags |= NFT_SET_ANONYMOUS;
+
+ for (i = from; i <= to; i++) {
+
+ concat = concat_expr_alloc(&internal_location);
+ for (j = 0; j < merge->num_stmts; j++) {
+ stmt = ctx->stmt_matrix[i][merge->stmt[j]];
+ expr = stmt->expr->right;
+ compound_expr_add(concat, expr_get(expr));
+ }
+
+ nat_stmt = ctx->stmt_matrix[i][k];
+ nat_expr = stmt_nat_expr(nat_stmt);
+
+ elem = set_elem_expr_alloc(&internal_location, concat);
+ mapping = mapping_expr_alloc(&internal_location, elem, nat_expr);
+ compound_expr_add(set, mapping);
+ }
+
+ concat = concat_expr_alloc(&internal_location);
+ for (j = 0; j < merge->num_stmts; j++) {
+ stmt = ctx->stmt_matrix[from][merge->stmt[j]];
+ left = stmt->expr->left;
+ compound_expr_add(concat, expr_get(left));
+ }
+ expr = map_expr_alloc(&internal_location, concat, set);
+
+ nat_stmt = ctx->stmt_matrix[from][k];
+ expr_free(nat_stmt->nat.addr);
+ nat_stmt->nat.addr = expr;
+
+ remove_counter(ctx, from);
+ for (j = 0; j < merge->num_stmts; j++) {
+ stmt = ctx->stmt_matrix[from][merge->stmt[j]];
+ list_del(&stmt->list);
+ stmt_free(stmt);
+ }
+}
+
+static void rule_optimize_print(struct output_ctx *octx,
+ const struct rule *rule)
+{
+ const struct location *loc = &rule->location;
+ const struct input_descriptor *indesc = loc->indesc;
+ const char *line = "";
+ char buf[1024];
+
+ switch (indesc->type) {
+ case INDESC_BUFFER:
+ case INDESC_CLI:
+ line = indesc->data;
+ *strchrnul(line, '\n') = '\0';
+ break;
+ case INDESC_STDIN:
+ line = indesc->data;
+ line += loc->line_offset;
+ *strchrnul(line, '\n') = '\0';
+ break;
+ case INDESC_FILE:
+ line = line_location(indesc, loc, buf, sizeof(buf));
+ break;
+ case INDESC_INTERNAL:
+ case INDESC_NETLINK:
+ break;
+ default:
+ BUG("invalid input descriptor type %u\n", indesc->type);
+ }
+
+ print_location(octx->error_fp, indesc, loc);
+ fprintf(octx->error_fp, "%s\n", line);
+}
+
+static enum stmt_types merge_stmt_type(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to)
+{
+ uint32_t i, j;
+
+ for (i = from; i <= to; i++) {
+ for (j = 0; j < ctx->num_stmts; j++) {
+ if (!ctx->stmt_matrix[i][j])
+ continue;
+ if (ctx->stmt_matrix[i][j]->ops->type == STMT_NAT)
+ return STMT_NAT;
+ }
+ }
+
+ /* merge by verdict, even if no verdict is specified. */
+ return STMT_VERDICT;
+}
+
+static void merge_rules(const struct optimize_ctx *ctx,
+ uint32_t from, uint32_t to,
+ const struct merge *merge,
+ struct output_ctx *octx)
+{
+ enum stmt_types stmt_type;
+ bool same_verdict;
+ uint32_t i;
+
+ stmt_type = merge_stmt_type(ctx, from, to);
+
+ switch (stmt_type) {
+ case STMT_VERDICT:
+ same_verdict = stmt_verdict_cmp(ctx, from, to);
+ if (merge->num_stmts > 1) {
+ if (same_verdict)
+ merge_concat_stmts(ctx, from, to, merge);
+ else
+ merge_concat_stmts_vmap(ctx, from, to, merge);
+ } else {
+ if (same_verdict)
+ merge_stmts(ctx, from, to, merge);
+ else
+ merge_stmts_vmap(ctx, from, to, merge);
+ }
+ break;
+ case STMT_NAT:
+ if (merge->num_stmts > 1)
+ merge_concat_nat(ctx, from, to, merge);
+ else
+ merge_nat(ctx, from, to, merge);
+ break;
+ default:
+ assert(0);
+ }
+
+ if (ctx->rule[from]->comment) {
+ xfree(ctx->rule[from]->comment);
+ ctx->rule[from]->comment = NULL;
+ }
+
+ octx->flags |= NFT_CTX_OUTPUT_STATELESS;
+
+ fprintf(octx->error_fp, "Merging:\n");
+ rule_optimize_print(octx, ctx->rule[from]);
+
+ for (i = from + 1; i <= to; i++) {
+ rule_optimize_print(octx, ctx->rule[i]);
+ list_del(&ctx->rule[i]->list);
+ rule_free(ctx->rule[i]);
+ }
+
+ fprintf(octx->error_fp, "into:\n\t");
+ rule_print(ctx->rule[from], octx);
+ fprintf(octx->error_fp, "\n");
+
+ octx->flags &= ~NFT_CTX_OUTPUT_STATELESS;
+}
+
+static bool stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b)
+{
+ if (!stmt_a && !stmt_b)
+ return true;
+ else if (!stmt_a)
+ return false;
+ else if (!stmt_b)
+ return false;
+
+ return __stmt_type_eq(stmt_a, stmt_b, true);
+}
+
+static bool stmt_is_mergeable(const struct stmt *stmt)
+{
+ if (!stmt)
+ return false;
+
+ switch (stmt->ops->type) {
+ case STMT_VERDICT:
+ if (stmt->expr->etype == EXPR_MAP)
+ return true;
+ break;
+ case STMT_EXPRESSION:
+ case STMT_NAT:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static bool rules_eq(const struct optimize_ctx *ctx, int i, int j)
+{
+ uint32_t k, mergeable = 0;
+
+ for (k = 0; k < ctx->num_stmts; k++) {
+ if (stmt_is_mergeable(ctx->stmt_matrix[i][k]))
+ mergeable++;
+
+ if (!stmt_type_eq(ctx->stmt_matrix[i][k], ctx->stmt_matrix[j][k]))
+ return false;
+ }
+
+ if (mergeable == 0)
+ return false;
+
+ return true;
+}
+
+static int chain_optimize(struct nft_ctx *nft, struct list_head *rules)
+{
+ struct optimize_ctx *ctx;
+ uint32_t num_merges = 0;
+ struct merge *merge;
+ uint32_t i, j, m, k;
+ struct rule *rule;
+ int ret;
+
+ ctx = xzalloc(sizeof(*ctx));
+
+ /* Step 1: collect statements in rules */
+ list_for_each_entry(rule, rules, list) {
+ ret = rule_collect_stmts(ctx, rule);
+ if (ret < 0)
+ goto err;
+
+ ctx->num_rules++;
+ }
+
+ ctx->rule = xzalloc(sizeof(*ctx->rule) * ctx->num_rules);
+ ctx->stmt_matrix = xzalloc(sizeof(*ctx->stmt_matrix) * ctx->num_rules);
+ for (i = 0; i < ctx->num_rules; i++)
+ ctx->stmt_matrix[i] = xzalloc_array(MAX_STMTS,
+ sizeof(**ctx->stmt_matrix));
+
+ merge = xzalloc(sizeof(*merge) * ctx->num_rules);
+
+ /* Step 2: Build matrix of statements */
+ i = 0;
+ list_for_each_entry(rule, rules, list)
+ rule_build_stmt_matrix_stmts(ctx, rule, &i);
+
+ /* Step 3: Look for common selectors for possible rule mergers */
+ for (i = 0; i < ctx->num_rules; i++) {
+ for (j = i + 1; j < ctx->num_rules; j++) {
+ if (!rules_eq(ctx, i, j)) {
+ if (merge[num_merges].num_rules > 0)
+ num_merges++;
+
+ i = j - 1;
+ break;
+ }
+ if (merge[num_merges].num_rules > 0) {
+ merge[num_merges].num_rules++;
+ } else {
+ merge[num_merges].rule_from = i;
+ merge[num_merges].num_rules = 2;
+ }
+ }
+ if (j == ctx->num_rules && merge[num_merges].num_rules > 0) {
+ num_merges++;
+ break;
+ }
+ }
+
+ /* Step 4: Infer how to merge the candidate rules */
+ for (k = 0; k < num_merges; k++) {
+ i = merge[k].rule_from;
+
+ for (m = 0; m < ctx->num_stmts; m++) {
+ if (!ctx->stmt_matrix[i][m])
+ continue;
+ switch (ctx->stmt_matrix[i][m]->ops->type) {
+ case STMT_EXPRESSION:
+ merge[k].stmt[merge[k].num_stmts++] = m;
+ break;
+ case STMT_VERDICT:
+ if (ctx->stmt_matrix[i][m]->expr->etype == EXPR_MAP)
+ merge[k].stmt[merge[k].num_stmts++] = m;
+ break;
+ default:
+ break;
+ }
+ }
+
+ j = merge[k].num_rules - 1;
+ merge_rules(ctx, i, i + j, &merge[k], &nft->output);
+ }
+ ret = 0;
+ for (i = 0; i < ctx->num_rules; i++)
+ xfree(ctx->stmt_matrix[i]);
+
+ xfree(ctx->stmt_matrix);
+ xfree(merge);
+err:
+ for (i = 0; i < ctx->num_stmts; i++)
+ stmt_free(ctx->stmt[i]);
+
+ xfree(ctx->rule);
+ xfree(ctx);
+
+ return ret;
+}
+
+static int cmd_optimize(struct nft_ctx *nft, struct cmd *cmd)
+{
+ struct table *table;
+ struct chain *chain;
+ int ret = 0;
+
+ switch (cmd->obj) {
+ case CMD_OBJ_TABLE:
+ table = cmd->table;
+ if (!table)
+ break;
+
+ list_for_each_entry(chain, &table->chains, list) {
+ if (chain->flags & CHAIN_F_HW_OFFLOAD)
+ continue;
+
+ chain_optimize(nft, &chain->rules);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+int nft_optimize(struct nft_ctx *nft, struct list_head *cmds)
+{
+ struct cmd *cmd;
+ int ret = 0;
+
+ list_for_each_entry(cmd, cmds, list) {
+ switch (cmd->op) {
+ case CMD_ADD:
+ ret = cmd_optimize(nft, cmd);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return ret;
+}
diff --git a/src/osf.c b/src/osf.c
index cb58315d..c611b542 100644
--- a/src/osf.c
+++ b/src/osf.c
@@ -1,3 +1,11 @@
+/*
+ * Copyright (c) 2018 Fernando Fernandez Mancera <ffmancera@riseup.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
+ */
+
#include <nftables.h>
#include <expression.h>
#include <utils.h>
diff --git a/src/owner.c b/src/owner.c
index 2d98a2e9..c34b0c15 100644
--- a/src/owner.c
+++ b/src/owner.c
@@ -1,3 +1,11 @@
+/*
+ * Copyright (c) 2021 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
+ */
+
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
@@ -66,7 +74,7 @@ static char *portid2name(pid_t pid, uint32_t portid, unsigned long inode)
continue;
rl = readlink(procname, tmp, sizeof(tmp));
- if (rl <= 0 || rl > (ssize_t)sizeof(tmp))
+ if (rl <= 0 || rl >= (ssize_t)sizeof(tmp))
continue;
tmp[rl] = 0;
diff --git a/src/parser_bison.y b/src/parser_bison.y
index c25af6ba..b229de7a 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -65,15 +65,26 @@ static struct scope *current_scope(const struct parser_state *state)
return state->scopes[state->scope];
}
-static void open_scope(struct parser_state *state, struct scope *scope)
+static int open_scope(struct parser_state *state, struct scope *scope)
{
- assert(state->scope < array_size(state->scopes) - 1);
+ if (state->scope >= array_size(state->scopes) - 1) {
+ state->scope_err = true;
+ return -1;
+ }
+
scope_init(scope, current_scope(state));
state->scopes[++state->scope] = scope;
+
+ return 0;
}
static void close_scope(struct parser_state *state)
{
+ if (state->scope_err) {
+ state->scope_err = false;
+ return;
+ }
+
assert(state->scope > 0);
state->scope--;
}
@@ -186,6 +197,11 @@ int nft_lex(void *, void *, void *);
struct handle_spec handle_spec;
struct position_spec position_spec;
struct prio_spec prio_spec;
+ struct limit_rate limit_rate;
+ struct tcp_kind_field {
+ uint16_t kind; /* must allow > 255 for SACK1, 2.. hack */
+ uint8_t field;
+ } tcp_kind_field;
}
%token TOKEN_EOF 0 "end of file"
@@ -275,6 +291,8 @@ int nft_lex(void *, void *, void *);
%token DESCRIBE "describe"
%token IMPORT "import"
%token EXPORT "export"
+%token DESTROY "destroy"
+
%token MONITOR "monitor"
%token ALL "all"
@@ -403,6 +421,7 @@ int nft_lex(void *, void *, void *);
%token OPTION "option"
%token ECHO "echo"
%token EOL "eol"
+%token MPTCP "mptcp"
%token NOP "nop"
%token SACK "sack"
%token SACK0 "sack0"
@@ -410,16 +429,26 @@ int nft_lex(void *, void *, void *);
%token SACK2 "sack2"
%token SACK3 "sack3"
%token SACK_PERM "sack-permitted"
+%token FASTOPEN "fastopen"
+%token MD5SIG "md5sig"
%token TIMESTAMP "timestamp"
-%token KIND "kind"
%token COUNT "count"
%token LEFT "left"
%token RIGHT "right"
%token TSVAL "tsval"
%token TSECR "tsecr"
+%token SUBTYPE "subtype"
%token DCCP "dccp"
+%token VXLAN "vxlan"
+%token VNI "vni"
+
+%token GRE "gre"
+%token GRETAP "gretap"
+
+%token GENEVE "geneve"
+
%token SCTP "sctp"
%token CHUNK "chunk"
%token DATA "data"
@@ -607,6 +636,11 @@ int nft_lex(void *, void *, void *);
%token IN "in"
%token OUT "out"
+%token XT "xt"
+
+%type <limit_rate> limit_rate_pkts
+%type <limit_rate> limit_rate_bytes
+
%type <string> identifier type_identifier string comment_spec
%destructor { xfree($$); } identifier type_identifier string comment_spec
@@ -618,8 +652,8 @@ int nft_lex(void *, void *, void *);
%type <cmd> line
%destructor { cmd_free($$); } line
-%type <cmd> base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd get_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd
-%destructor { cmd_free($$); } base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd get_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd
+%type <cmd> base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd get_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd destroy_cmd
+%destructor { cmd_free($$); } base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd get_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd destroy_cmd
%type <handle> table_spec tableid_spec table_or_id_spec
%destructor { handle_free(&$$); } table_spec tableid_spec table_or_id_spec
@@ -689,7 +723,7 @@ int nft_lex(void *, void *, void *);
%type <val> level_type log_flags log_flags_tcp log_flag_tcp
%type <stmt> limit_stmt quota_stmt connlimit_stmt
%destructor { stmt_free($$); } limit_stmt quota_stmt connlimit_stmt
-%type <val> limit_burst_pkts limit_burst_bytes limit_mode time_unit quota_mode
+%type <val> limit_burst_pkts limit_burst_bytes limit_mode limit_bytes time_unit quota_mode
%type <stmt> reject_stmt reject_stmt_alloc
%destructor { stmt_free($$); } reject_stmt reject_stmt_alloc
%type <stmt> nat_stmt nat_stmt_alloc masq_stmt masq_stmt_alloc redir_stmt redir_stmt_alloc
@@ -870,7 +904,23 @@ int nft_lex(void *, void *, void *);
%type <expr> tcp_hdr_expr
%destructor { expr_free($$); } tcp_hdr_expr
%type <val> tcp_hdr_field
-%type <val> tcp_hdr_option_type tcp_hdr_option_field
+%type <val> tcp_hdr_option_type
+%type <val> tcp_hdr_option_sack
+%type <val> tcpopt_field_maxseg tcpopt_field_mptcp tcpopt_field_sack tcpopt_field_tsopt tcpopt_field_window
+%type <tcp_kind_field> tcp_hdr_option_kind_and_field
+
+%type <expr> inner_eth_expr inner_inet_expr inner_expr
+%destructor { expr_free($$); } inner_eth_expr inner_inet_expr inner_expr
+
+%type <expr> vxlan_hdr_expr geneve_hdr_expr gre_hdr_expr gretap_hdr_expr
+%destructor { expr_free($$); } vxlan_hdr_expr geneve_hdr_expr gre_hdr_expr gretap_hdr_expr
+%type <val> vxlan_hdr_field geneve_hdr_field gre_hdr_field
+
+%type <stmt> optstrip_stmt
+%destructor { stmt_free($$); } optstrip_stmt
+
+%type <stmt> xt_stmt
+%destructor { stmt_free($$); } xt_stmt
%type <expr> boolean_expr
%destructor { expr_free($$); } boolean_expr
@@ -912,28 +962,59 @@ opt_newline : NEWLINE
| /* empty */
;
+close_scope_ah : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_AH); };
close_scope_arp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_ARP); };
+close_scope_at : { scanner_pop_start_cond(nft->scanner, PARSER_SC_AT); };
+close_scope_comp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_COMP); };
close_scope_ct : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CT); };
close_scope_counter : { scanner_pop_start_cond(nft->scanner, PARSER_SC_COUNTER); };
+close_scope_dccp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_DCCP); };
+close_scope_dst : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_DST); };
+close_scope_dup : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_DUP); };
+close_scope_esp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_ESP); };
close_scope_eth : { scanner_pop_start_cond(nft->scanner, PARSER_SC_ETH); };
+close_scope_export : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_EXPORT); };
close_scope_fib : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_FIB); };
+close_scope_frag : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_FRAG); };
+close_scope_fwd : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_FWD); };
+close_scope_gre : { scanner_pop_start_cond(nft->scanner, PARSER_SC_GRE); };
close_scope_hash : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_HASH); };
+close_scope_hbh : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_HBH); };
close_scope_ip : { scanner_pop_start_cond(nft->scanner, PARSER_SC_IP); };
close_scope_ip6 : { scanner_pop_start_cond(nft->scanner, PARSER_SC_IP6); };
close_scope_vlan : { scanner_pop_start_cond(nft->scanner, PARSER_SC_VLAN); };
+close_scope_icmp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_ICMP); };
+close_scope_igmp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_IGMP); };
+close_scope_import : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_IMPORT); };
close_scope_ipsec : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_IPSEC); };
close_scope_list : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_LIST); };
close_scope_limit : { scanner_pop_start_cond(nft->scanner, PARSER_SC_LIMIT); };
+close_scope_meta : { scanner_pop_start_cond(nft->scanner, PARSER_SC_META); };
+close_scope_mh : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_MH); };
+close_scope_monitor : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_MONITOR); };
+close_scope_nat : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_NAT); };
close_scope_numgen : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_NUMGEN); };
+close_scope_osf : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_OSF); };
+close_scope_policy : { scanner_pop_start_cond(nft->scanner, PARSER_SC_POLICY); };
close_scope_quota : { scanner_pop_start_cond(nft->scanner, PARSER_SC_QUOTA); };
close_scope_queue : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_QUEUE); };
+close_scope_reject : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_REJECT); };
+close_scope_reset : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_RESET); };
close_scope_rt : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_RT); };
close_scope_sctp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_SCTP); };
close_scope_sctp_chunk : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_SCTP_CHUNK); };
close_scope_secmark : { scanner_pop_start_cond(nft->scanner, PARSER_SC_SECMARK); };
close_scope_socket : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_SOCKET); }
+close_scope_tcp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_TCP); };
+close_scope_tproxy : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_TPROXY); };
+close_scope_type : { scanner_pop_start_cond(nft->scanner, PARSER_SC_TYPE); };
+close_scope_th : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_TH); };
+close_scope_udp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_UDP); };
+close_scope_udplite : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_UDPLITE); };
close_scope_log : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_LOG); }
+close_scope_synproxy : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_SYNPROXY); }
+close_scope_xt : { scanner_pop_start_cond(nft->scanner, PARSER_SC_XT); }
common_block : INCLUDE QUOTED_STRING stmt_separator
{
@@ -1016,13 +1097,14 @@ base_cmd : /* empty */ add_cmd { $$ = $1; }
| DELETE delete_cmd { $$ = $2; }
| GET get_cmd { $$ = $2; }
| LIST list_cmd close_scope_list { $$ = $2; }
- | RESET reset_cmd { $$ = $2; }
+ | RESET reset_cmd close_scope_reset { $$ = $2; }
| FLUSH flush_cmd { $$ = $2; }
| RENAME rename_cmd { $$ = $2; }
- | IMPORT import_cmd { $$ = $2; }
- | EXPORT export_cmd { $$ = $2; }
- | MONITOR monitor_cmd { $$ = $2; }
+ | IMPORT import_cmd close_scope_import { $$ = $2; }
+ | EXPORT export_cmd close_scope_export { $$ = $2; }
+ | MONITOR monitor_cmd close_scope_monitor { $$ = $2; }
| DESCRIBE describe_cmd { $$ = $2; }
+ | DESTROY destroy_cmd { $$ = $2; }
;
add_cmd : TABLE table_spec
@@ -1134,11 +1216,11 @@ add_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_ADD, CMD_OBJ_SECMARK, &$2, &@$, $3);
}
- | SYNPROXY obj_spec synproxy_obj synproxy_config
+ | SYNPROXY obj_spec synproxy_obj synproxy_config close_scope_synproxy
{
$$ = cmd_alloc(CMD_ADD, CMD_OBJ_SYNPROXY, &$2, &@$, $3);
}
- | SYNPROXY obj_spec synproxy_obj '{' synproxy_block '}'
+ | SYNPROXY obj_spec synproxy_obj '{' synproxy_block '}' close_scope_synproxy
{
$$ = cmd_alloc(CMD_ADD, CMD_OBJ_SYNPROXY, &$2, &@$, $3);
}
@@ -1235,7 +1317,7 @@ create_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SECMARK, &$2, &@$, $3);
}
- | SYNPROXY obj_spec synproxy_obj synproxy_config
+ | SYNPROXY obj_spec synproxy_obj synproxy_config close_scope_synproxy
{
$$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SYNPROXY, &$2, &@$, $3);
}
@@ -1324,12 +1406,80 @@ delete_cmd : TABLE table_or_id_spec
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SECMARK, &$2, &@$, NULL);
}
- | SYNPROXY obj_or_id_spec
+ | SYNPROXY obj_or_id_spec close_scope_synproxy
{
$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
}
;
+destroy_cmd : TABLE table_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_TABLE, &$2, &@$, NULL);
+ }
+ | CHAIN chain_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_CHAIN, &$2, &@$, NULL);
+ }
+ | RULE ruleid_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_RULE, &$2, &@$, NULL);
+ }
+ | SET set_or_id_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SET, &$2, &@$, NULL);
+ }
+ | MAP set_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SET, &$2, &@$, NULL);
+ }
+ | ELEMENT set_spec set_block_expr
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
+ }
+ | FLOWTABLE flowtable_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL);
+ }
+ | FLOWTABLE flowtableid_spec
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL);
+ }
+ | FLOWTABLE flowtable_spec flowtable_block_alloc
+ '{' flowtable_block '}'
+ {
+ $5->location = @5;
+ handle_merge(&$3->handle, &$2);
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_FLOWTABLE, &$2, &@$, $5);
+ }
+ | COUNTER obj_or_id_spec close_scope_counter
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_COUNTER, &$2, &@$, NULL);
+ }
+ | QUOTA obj_or_id_spec close_scope_quota
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_QUOTA, &$2, &@$, NULL);
+ }
+ | CT ct_obj_type obj_spec ct_obj_alloc close_scope_ct
+ {
+ $$ = cmd_alloc_obj_ct(CMD_DESTROY, $2, &$3, &@$, $4);
+ if ($2 == NFT_OBJECT_CT_TIMEOUT)
+ init_list_head(&$4->ct_timeout.timeout_list);
+ }
+ | LIMIT obj_or_id_spec close_scope_limit
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_LIMIT, &$2, &@$, NULL);
+ }
+ | SECMARK obj_or_id_spec close_scope_secmark
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SECMARK, &$2, &@$, NULL);
+ }
+ | SYNPROXY obj_or_id_spec close_scope_synproxy
+ {
+ $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
+ }
+ ;
+
+
get_cmd : ELEMENT set_spec set_block_expr
{
$$ = cmd_alloc(CMD_GET, CMD_OBJ_ELEMENTS, &$2, &@$, $3);
@@ -1420,7 +1570,7 @@ list_cmd : TABLE table_spec
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXYS, &$3, &@$, NULL);
}
- | SYNPROXY obj_spec
+ | SYNPROXY obj_spec close_scope_synproxy
{
$$ = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXY, &$2, &@$, NULL);
}
@@ -1518,6 +1668,22 @@ reset_cmd : COUNTERS ruleset_spec
{
$$ = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTA, &$2, &@$, NULL);
}
+ | RULES ruleset_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$2, &@$, NULL);
+ }
+ | RULES TABLE table_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$3, &@$, NULL);
+ }
+ | RULES CHAIN chain_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$3, &@$, NULL);
+ }
+ | RULE ruleid_spec
+ {
+ $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULE, &$2, &@$, NULL);
+ }
;
flush_cmd : TABLE table_spec
@@ -1628,7 +1794,11 @@ describe_cmd : primary_expr
table_block_alloc : /* empty */
{
$$ = table_alloc();
- open_scope(state, &$$->scope);
+ if (open_scope(state, &$$->scope) < 0) {
+ erec_queue(error(&@$, "too many levels of nesting"),
+ state->msgs);
+ state->nerrs++;
+ }
}
;
@@ -1776,7 +1946,7 @@ table_block : /* empty */ { $$ = $<table>-1; }
}
| table_block SYNPROXY obj_identifier
obj_block_alloc '{' synproxy_block '}'
- stmt_separator
+ stmt_separator close_scope_synproxy
{
$4->location = @3;
$4->type = NFT_OBJECT_SYNPROXY;
@@ -1790,7 +1960,11 @@ table_block : /* empty */ { $$ = $<table>-1; }
chain_block_alloc : /* empty */
{
$$ = chain_alloc(NULL);
- open_scope(state, &$$->scope);
+ if (open_scope(state, &$$->scope) < 0) {
+ erec_queue(error(&@$, "too many levels of nesting"),
+ state->msgs);
+ state->nerrs++;
+ }
}
;
@@ -1888,7 +2062,7 @@ set_block_alloc : /* empty */
set_block : /* empty */ { $$ = $<set>-1; }
| set_block common_block
| set_block stmt_separator
- | set_block TYPE data_type_expr stmt_separator
+ | set_block TYPE data_type_expr stmt_separator close_scope_type
{
$1->key = $3;
$$ = $1;
@@ -1969,6 +2143,7 @@ map_block_obj_type : COUNTER close_scope_counter { $$ = NFT_OBJECT_COUNTER; }
| QUOTA close_scope_quota { $$ = NFT_OBJECT_QUOTA; }
| LIMIT close_scope_limit { $$ = NFT_OBJECT_LIMIT; }
| SECMARK close_scope_secmark { $$ = NFT_OBJECT_SECMARK; }
+ | SYNPROXY close_scope_synproxy { $$ = NFT_OBJECT_SYNPROXY; }
;
map_block : /* empty */ { $$ = $<set>-1; }
@@ -1981,7 +2156,7 @@ map_block : /* empty */ { $$ = $<set>-1; }
}
| map_block TYPE
data_type_expr COLON data_type_expr
- stmt_separator
+ stmt_separator close_scope_type
{
$1->key = $3;
$1->data = $5;
@@ -1991,7 +2166,7 @@ map_block : /* empty */ { $$ = $<set>-1; }
}
| map_block TYPE
data_type_expr COLON INTERVAL data_type_expr
- stmt_separator
+ stmt_separator close_scope_type
{
$1->key = $3;
$1->data = $6;
@@ -2025,7 +2200,7 @@ map_block : /* empty */ { $$ = $<set>-1; }
}
| map_block TYPE
data_type_expr COLON map_block_obj_type
- stmt_separator
+ stmt_separator close_scope_type
{
$1->key = $3;
$1->objtype = $5;
@@ -2060,7 +2235,7 @@ map_block : /* empty */ { $$ = $<set>-1; }
| map_block set_mechanism stmt_separator
;
-set_mechanism : POLICY set_policy_spec
+set_mechanism : POLICY set_policy_spec close_scope_policy
{
$<set>0->policy = $2;
}
@@ -2136,7 +2311,14 @@ flowtable_list_expr : flowtable_expr_member
| flowtable_list_expr COMMA opt_newline
;
-flowtable_expr_member : STRING
+flowtable_expr_member : QUOTED_STRING
+ {
+ $$ = constant_expr_alloc(&@$, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ strlen($1) * BITS_PER_BYTE, $1);
+ xfree($1);
+ }
+ | STRING
{
$$ = constant_expr_alloc(&@$, &string_type,
BYTEORDER_HOST_ENDIAN,
@@ -2335,33 +2517,33 @@ type_identifier : STRING { $$ = $1; }
| CLASSID { $$ = xstrdup("classid"); }
;
-hook_spec : TYPE STRING HOOK STRING dev_spec prio_spec
+hook_spec : TYPE close_scope_type STRING HOOK STRING dev_spec prio_spec
{
- const char *chain_type = chain_type_name_lookup($2);
+ const char *chain_type = chain_type_name_lookup($3);
if (chain_type == NULL) {
- erec_queue(error(&@2, "unknown chain type"),
+ erec_queue(error(&@3, "unknown chain type"),
state->msgs);
- xfree($2);
+ xfree($3);
YYERROR;
}
- $<chain>0->type.loc = @2;
+ $<chain>0->type.loc = @3;
$<chain>0->type.str = xstrdup(chain_type);
- xfree($2);
+ xfree($3);
$<chain>0->loc = @$;
- $<chain>0->hook.loc = @4;
- $<chain>0->hook.name = chain_hookname_lookup($4);
+ $<chain>0->hook.loc = @5;
+ $<chain>0->hook.name = chain_hookname_lookup($5);
if ($<chain>0->hook.name == NULL) {
- erec_queue(error(&@4, "unknown chain hook"),
+ erec_queue(error(&@5, "unknown chain hook"),
state->msgs);
- xfree($4);
+ xfree($5);
YYERROR;
}
- xfree($4);
+ xfree($5);
- $<chain>0->dev_expr = $5;
- $<chain>0->priority = $6;
+ $<chain>0->dev_expr = $6;
+ $<chain>0->priority = $7;
$<chain>0->flags |= CHAIN_F_BASECHAIN;
}
;
@@ -2471,7 +2653,7 @@ flags_spec : FLAGS OFFLOAD
}
;
-policy_spec : POLICY policy_expr
+policy_spec : POLICY policy_expr close_scope_policy
{
if ($<chain>0->policy) {
erec_queue(error(&@$, "you cannot set chain policy twice"),
@@ -2792,19 +2974,32 @@ stmt : verdict_stmt
| stateful_stmt
| meta_stmt
| log_stmt close_scope_log
- | reject_stmt
- | nat_stmt
- | tproxy_stmt
+ | reject_stmt close_scope_reject
+ | nat_stmt close_scope_nat
+ | tproxy_stmt close_scope_tproxy
| queue_stmt
| ct_stmt
- | masq_stmt
- | redir_stmt
- | dup_stmt
- | fwd_stmt
+ | masq_stmt close_scope_nat
+ | redir_stmt close_scope_nat
+ | dup_stmt close_scope_dup
+ | fwd_stmt close_scope_fwd
| set_stmt
| map_stmt
- | synproxy_stmt
+ | synproxy_stmt close_scope_synproxy
| chain_stmt
+ | optstrip_stmt
+ | xt_stmt close_scope_xt
+ ;
+
+xt_stmt : XT STRING STRING
+ {
+ $$ = NULL;
+ xfree($2);
+ xfree($3);
+ erec_queue(error(&@$, "unsupported xtables compat expression, use iptables-nft with this ruleset"),
+ state->msgs);
+ YYERROR;
+ }
;
chain_stmt_type : JUMP { $$ = NFT_JUMP; }
@@ -2859,7 +3054,7 @@ verdict_map_list_expr : verdict_map_list_member_expr
verdict_map_list_member_expr: opt_newline set_elem_expr COLON verdict_expr opt_newline
{
- $$ = mapping_expr_alloc(&@$, $2, $4);
+ $$ = mapping_expr_alloc(&@2, $2, $4);
}
;
@@ -3106,7 +3301,7 @@ level_type : string
}
;
-log_flags : TCP log_flags_tcp
+log_flags : TCP log_flags_tcp close_scope_tcp
{
$$ = $2;
}
@@ -3145,42 +3340,26 @@ log_flag_tcp : SEQUENCE
}
;
-limit_stmt : LIMIT RATE limit_mode NUM SLASH time_unit limit_burst_pkts close_scope_limit
+limit_stmt : LIMIT RATE limit_mode limit_rate_pkts limit_burst_pkts close_scope_limit
{
- if ($7 == 0) {
- erec_queue(error(&@7, "limit burst must be > 0"),
+ if ($5 == 0) {
+ erec_queue(error(&@5, "packet limit burst must be > 0"),
state->msgs);
YYERROR;
}
$$ = limit_stmt_alloc(&@$);
- $$->limit.rate = $4;
- $$->limit.unit = $6;
- $$->limit.burst = $7;
+ $$->limit.rate = $4.rate;
+ $$->limit.unit = $4.unit;
+ $$->limit.burst = $5;
$$->limit.type = NFT_LIMIT_PKTS;
$$->limit.flags = $3;
}
- | LIMIT RATE limit_mode NUM STRING limit_burst_bytes close_scope_limit
+ | LIMIT RATE limit_mode limit_rate_bytes limit_burst_bytes close_scope_limit
{
- struct error_record *erec;
- uint64_t rate, unit;
-
- if ($6 == 0) {
- erec_queue(error(&@6, "limit burst must be > 0"),
- state->msgs);
- YYERROR;
- }
-
- erec = rate_parse(&@$, $5, &rate, &unit);
- xfree($5);
- if (erec != NULL) {
- erec_queue(erec, state->msgs);
- YYERROR;
- }
-
$$ = limit_stmt_alloc(&@$);
- $$->limit.rate = rate * $4;
- $$->limit.unit = unit;
- $$->limit.burst = $6;
+ $$->limit.rate = $4.rate;
+ $$->limit.unit = $4.unit;
+ $$->limit.burst = $5;
$$->limit.type = NFT_LIMIT_PKT_BYTES;
$$->limit.flags = $3;
}
@@ -3250,20 +3429,51 @@ limit_burst_pkts : /* empty */ { $$ = 5; }
| BURST NUM PACKETS { $$ = $2; }
;
-limit_burst_bytes : /* empty */ { $$ = 5; }
- | BURST NUM BYTES { $$ = $2; }
- | BURST NUM STRING
+limit_rate_pkts : NUM SLASH time_unit
+ {
+ $$.rate = $1;
+ $$.unit = $3;
+ }
+ ;
+
+limit_burst_bytes : /* empty */ { $$ = 0; }
+ | BURST limit_bytes { $$ = $2; }
+ ;
+
+limit_rate_bytes : NUM STRING
+ {
+ struct error_record *erec;
+ uint64_t rate, unit;
+
+ erec = rate_parse(&@$, $2, &rate, &unit);
+ xfree($2);
+ if (erec != NULL) {
+ erec_queue(erec, state->msgs);
+ YYERROR;
+ }
+ $$.rate = rate * $1;
+ $$.unit = unit;
+ }
+ | limit_bytes SLASH time_unit
+ {
+ $$.rate = $1;
+ $$.unit = $3;
+ }
+ ;
+
+limit_bytes : NUM BYTES { $$ = $1; }
+ | NUM STRING
{
struct error_record *erec;
uint64_t rate;
- erec = data_unit_parse(&@$, $3, &rate);
- xfree($3);
+ erec = data_unit_parse(&@$, $2, &rate);
+ xfree($2);
if (erec != NULL) {
erec_queue(erec, state->msgs);
YYERROR;
}
- $$ = $2 * rate;
+ $$ = $1 * rate;
}
;
@@ -3297,7 +3507,7 @@ reject_opts : /* empty */
$<stmt>0->reject.type = -1;
$<stmt>0->reject.icmp_code = -1;
}
- | WITH ICMP TYPE reject_with_expr
+ | WITH ICMP TYPE reject_with_expr close_scope_type close_scope_icmp
{
$<stmt>0->reject.family = NFPROTO_IPV4;
$<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
@@ -3311,7 +3521,7 @@ reject_opts : /* empty */
$<stmt>0->reject.expr = $3;
datatype_set($<stmt>0->reject.expr, &icmp_code_type);
}
- | WITH ICMP6 TYPE reject_with_expr
+ | WITH ICMP6 TYPE reject_with_expr close_scope_type close_scope_icmp
{
$<stmt>0->reject.family = NFPROTO_IPV6;
$<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH;
@@ -3325,7 +3535,7 @@ reject_opts : /* empty */
$<stmt>0->reject.expr = $3;
datatype_set($<stmt>0->reject.expr, &icmpv6_code_type);
}
- | WITH ICMPX TYPE reject_with_expr
+ | WITH ICMPX TYPE reject_with_expr close_scope_type
{
$<stmt>0->reject.type = NFT_REJECT_ICMPX_UNREACH;
$<stmt>0->reject.expr = $4;
@@ -3337,7 +3547,7 @@ reject_opts : /* empty */
$<stmt>0->reject.expr = $3;
datatype_set($<stmt>0->reject.expr, &icmpx_code_type);
}
- | WITH TCP RESET
+ | WITH TCP close_scope_tcp RESET close_scope_reset
{
$<stmt>0->reject.type = NFT_REJECT_TCP_RST;
}
@@ -3974,7 +4184,7 @@ set_ref_expr : set_ref_symbol_expr
| variable_expr
;
-set_ref_symbol_expr : AT identifier
+set_ref_symbol_expr : AT identifier close_scope_at
{
$$ = symbol_expr_alloc(&@$, SYMBOL_SET,
current_scope(state),
@@ -4036,7 +4246,7 @@ fib_expr : FIB fib_tuple fib_result close_scope_fib
fib_result : OIF { $$ =NFT_FIB_RESULT_OIF; }
| OIFNAME { $$ =NFT_FIB_RESULT_OIFNAME; }
- | TYPE { $$ =NFT_FIB_RESULT_ADDRTYPE; }
+ | TYPE close_scope_type { $$ =NFT_FIB_RESULT_ADDRTYPE; }
;
fib_flag : SADDR { $$ = NFTA_FIB_F_SADDR; }
@@ -4053,11 +4263,11 @@ fib_tuple : fib_flag DOT fib_tuple
| fib_flag
;
-osf_expr : OSF osf_ttl HDRVERSION
+osf_expr : OSF osf_ttl HDRVERSION close_scope_osf
{
$$ = osf_expr_alloc(&@$, $2, NFT_OSF_F_VERSION);
}
- | OSF osf_ttl NAME
+ | OSF osf_ttl NAME close_scope_osf
{
$$ = osf_expr_alloc(&@$, $2, 0);
}
@@ -4187,7 +4397,7 @@ set_list_member_expr : opt_newline set_expr opt_newline
}
| opt_newline set_elem_expr COLON set_rhs_expr opt_newline
{
- $$ = mapping_expr_alloc(&@$, $2, $4);
+ $$ = mapping_expr_alloc(&@2, $2, $4);
}
;
@@ -4280,44 +4490,34 @@ set_elem_stmt : COUNTER close_scope_counter
$$->counter.packets = $3;
$$->counter.bytes = $5;
}
- | LIMIT RATE limit_mode NUM SLASH time_unit limit_burst_pkts close_scope_limit
+ | LIMIT RATE limit_mode limit_rate_pkts limit_burst_pkts close_scope_limit
{
- if ($7 == 0) {
- erec_queue(error(&@7, "limit burst must be > 0"),
+ if ($5 == 0) {
+ erec_queue(error(&@5, "limit burst must be > 0"),
state->msgs);
YYERROR;
}
$$ = limit_stmt_alloc(&@$);
- $$->limit.rate = $4;
- $$->limit.unit = $6;
- $$->limit.burst = $7;
+ $$->limit.rate = $4.rate;
+ $$->limit.unit = $4.unit;
+ $$->limit.burst = $5;
$$->limit.type = NFT_LIMIT_PKTS;
$$->limit.flags = $3;
}
- | LIMIT RATE limit_mode NUM STRING limit_burst_bytes close_scope_limit
+ | LIMIT RATE limit_mode limit_rate_bytes limit_burst_bytes close_scope_limit
{
- struct error_record *erec;
- uint64_t rate, unit;
-
- if ($6 == 0) {
+ if ($5 == 0) {
erec_queue(error(&@6, "limit burst must be > 0"),
state->msgs);
YYERROR;
}
- erec = rate_parse(&@$, $5, &rate, &unit);
- xfree($5);
- if (erec != NULL) {
- erec_queue(erec, state->msgs);
- YYERROR;
- }
-
$$ = limit_stmt_alloc(&@$);
- $$->limit.rate = rate * $4;
- $$->limit.unit = unit;
- $$->limit.burst = $6;
+ $$->limit.rate = $4.rate;
+ $$->limit.unit = $4.unit;
+ $$->limit.burst = $5;
$$->limit.type = NFT_LIMIT_PKT_BYTES;
$$->limit.flags = $3;
- }
+ }
| CT COUNT NUM close_scope_ct
{
$$ = connlimit_stmt_alloc(&@$);
@@ -4447,11 +4647,11 @@ ct_cmd_type : HELPERS { $$ = CMD_OBJ_CT_HELPERS; }
| EXPECTATION { $$ = CMD_OBJ_CT_EXPECT; }
;
-ct_l4protoname : TCP { $$ = IPPROTO_TCP; }
- | UDP { $$ = IPPROTO_UDP; }
+ct_l4protoname : TCP close_scope_tcp { $$ = IPPROTO_TCP; }
+ | UDP close_scope_udp { $$ = IPPROTO_UDP; }
;
-ct_helper_config : TYPE QUOTED_STRING PROTOCOL ct_l4protoname stmt_separator
+ct_helper_config : TYPE QUOTED_STRING PROTOCOL ct_l4protoname stmt_separator close_scope_type
{
struct ct_helper *ct;
int ret;
@@ -4508,7 +4708,7 @@ ct_timeout_config : PROTOCOL ct_l4protoname stmt_separator
ct = &$<obj>0->ct_timeout;
ct->l4proto = l4proto;
}
- | POLICY '=' '{' timeout_states '}' stmt_separator
+ | POLICY '=' '{' timeout_states '}' stmt_separator close_scope_policy
{
struct ct_timeout *ct;
@@ -4550,34 +4750,25 @@ ct_obj_alloc : /* empty */
}
;
-limit_config : RATE limit_mode NUM SLASH time_unit limit_burst_pkts
+limit_config : RATE limit_mode limit_rate_pkts limit_burst_pkts
{
struct limit *limit;
limit = &$<obj>0->limit;
- limit->rate = $3;
- limit->unit = $5;
- limit->burst = $6;
+ limit->rate = $3.rate;
+ limit->unit = $3.unit;
+ limit->burst = $4;
limit->type = NFT_LIMIT_PKTS;
limit->flags = $2;
}
- | RATE limit_mode NUM STRING limit_burst_bytes
+ | RATE limit_mode limit_rate_bytes limit_burst_bytes
{
struct limit *limit;
- struct error_record *erec;
- uint64_t rate, unit;
-
- erec = rate_parse(&@$, $4, &rate, &unit);
- xfree($4);
- if (erec != NULL) {
- erec_queue(erec, state->msgs);
- YYERROR;
- }
limit = &$<obj>0->limit;
- limit->rate = rate * $3;
- limit->unit = unit;
- limit->burst = $5;
+ limit->rate = $3.rate;
+ limit->unit = $3.unit;
+ limit->burst = $4;
limit->type = NFT_LIMIT_PKT_BYTES;
limit->flags = $2;
}
@@ -4717,10 +4908,10 @@ keyword_expr : ETHER close_scope_eth { $$ = symbol_value(&@$, "ether"); }
| IP6 close_scope_ip6 { $$ = symbol_value(&@$, "ip6"); }
| VLAN close_scope_vlan { $$ = symbol_value(&@$, "vlan"); }
| ARP close_scope_arp { $$ = symbol_value(&@$, "arp"); }
- | DNAT { $$ = symbol_value(&@$, "dnat"); }
- | SNAT { $$ = symbol_value(&@$, "snat"); }
+ | DNAT close_scope_nat { $$ = symbol_value(&@$, "dnat"); }
+ | SNAT close_scope_nat { $$ = symbol_value(&@$, "snat"); }
| ECN { $$ = symbol_value(&@$, "ecn"); }
- | RESET { $$ = symbol_value(&@$, "reset"); }
+ | RESET close_scope_reset { $$ = symbol_value(&@$, "reset"); }
| ORIGINAL { $$ = symbol_value(&@$, "original"); }
| REPLY { $$ = symbol_value(&@$, "reply"); }
| LABEL { $$ = symbol_value(&@$, "label"); }
@@ -4730,42 +4921,42 @@ primary_rhs_expr : symbol_expr { $$ = $1; }
| integer_expr { $$ = $1; }
| boolean_expr { $$ = $1; }
| keyword_expr { $$ = $1; }
- | TCP
+ | TCP close_scope_tcp
{
uint8_t data = IPPROTO_TCP;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | UDP
+ | UDP close_scope_udp
{
uint8_t data = IPPROTO_UDP;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | UDPLITE
+ | UDPLITE close_scope_udplite
{
uint8_t data = IPPROTO_UDPLITE;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | ESP
+ | ESP close_scope_esp
{
uint8_t data = IPPROTO_ESP;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | AH
+ | AH close_scope_ah
{
uint8_t data = IPPROTO_AH;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | ICMP
+ | ICMP close_scope_icmp
{
uint8_t data = IPPROTO_ICMP;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
@@ -4779,21 +4970,28 @@ primary_rhs_expr : symbol_expr { $$ = $1; }
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | ICMP6
+ | ICMP6 close_scope_icmp
{
uint8_t data = IPPROTO_ICMPV6;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | COMP
+ | GRE close_scope_gre
+ {
+ uint8_t data = IPPROTO_GRE;
+ $$ = constant_expr_alloc(&@$, &inet_protocol_type,
+ BYTEORDER_HOST_ENDIAN,
+ sizeof(data) * BITS_PER_BYTE, &data);
+ }
+ | COMP close_scope_comp
{
uint8_t data = IPPROTO_COMP;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | DCCP
+ | DCCP close_scope_dccp
{
uint8_t data = IPPROTO_DCCP;
$$ = constant_expr_alloc(&@$, &inet_protocol_type,
@@ -4807,7 +5005,7 @@ primary_rhs_expr : symbol_expr { $$ = $1; }
BYTEORDER_HOST_ENDIAN,
sizeof(data) * BITS_PER_BYTE, &data);
}
- | REDIRECT
+ | REDIRECT close_scope_nat
{
uint8_t data = ICMP_REDIRECT;
$$ = constant_expr_alloc(&@$, &icmp_type_type,
@@ -4863,7 +5061,7 @@ chain_expr : variable_expr
}
;
-meta_expr : META meta_key
+meta_expr : META meta_key close_scope_meta
{
$$ = meta_expr_alloc(&@$, $2);
}
@@ -4871,7 +5069,7 @@ meta_expr : META meta_key
{
$$ = meta_expr_alloc(&@$, $1);
}
- | META STRING
+ | META STRING close_scope_meta
{
struct error_record *erec;
unsigned int key;
@@ -4924,7 +5122,7 @@ meta_key_unqualified : MARK { $$ = NFT_META_MARK; }
| HOUR { $$ = NFT_META_TIME_HOUR; }
;
-meta_stmt : META meta_key SET stmt_expr
+meta_stmt : META meta_key SET stmt_expr close_scope_meta
{
switch ($2) {
case NFT_META_SECMARK:
@@ -4948,7 +5146,7 @@ meta_stmt : META meta_key SET stmt_expr
{
$$ = meta_stmt_alloc(&@$, $1, $3);
}
- | META STRING SET stmt_expr
+ | META STRING SET stmt_expr close_scope_meta
{
struct error_record *erec;
unsigned int key;
@@ -4966,11 +5164,11 @@ meta_stmt : META meta_key SET stmt_expr
{
$$ = notrack_stmt_alloc(&@$);
}
- | FLOW OFFLOAD AT string
+ | FLOW OFFLOAD AT string close_scope_at
{
$$ = flow_offload_stmt_alloc(&@$, $4);
}
- | FLOW ADD AT string
+ | FLOW ADD AT string close_scope_at
{
$$ = flow_offload_stmt_alloc(&@$, $4);
}
@@ -5237,13 +5435,17 @@ payload_expr : payload_raw_expr
| comp_hdr_expr
| udp_hdr_expr
| udplite_hdr_expr
- | tcp_hdr_expr
+ | tcp_hdr_expr close_scope_tcp
| dccp_hdr_expr
| sctp_hdr_expr
| th_hdr_expr
+ | vxlan_hdr_expr
+ | geneve_hdr_expr
+ | gre_hdr_expr
+ | gretap_hdr_expr
;
-payload_raw_expr : AT payload_base_spec COMMA NUM COMMA NUM
+payload_raw_expr : AT payload_base_spec COMMA NUM COMMA NUM close_scope_at
{
$$ = payload_expr_alloc(&@$, NULL, 0);
payload_init_raw($$, $2, $4, $6);
@@ -5254,7 +5456,18 @@ payload_raw_expr : AT payload_base_spec COMMA NUM COMMA NUM
payload_base_spec : LL_HDR { $$ = PROTO_BASE_LL_HDR; }
| NETWORK_HDR { $$ = PROTO_BASE_NETWORK_HDR; }
- | TRANSPORT_HDR { $$ = PROTO_BASE_TRANSPORT_HDR; }
+ | TRANSPORT_HDR close_scope_th { $$ = PROTO_BASE_TRANSPORT_HDR; }
+ | STRING
+ {
+ if (!strcmp($1, "ih")) {
+ $$ = PROTO_BASE_INNER_HDR;
+ } else {
+ erec_queue(error(&@1, "unknown raw payload base"), state->msgs);
+ xfree($1);
+ YYERROR;
+ }
+ xfree($1);
+ }
;
eth_hdr_expr : ETHER eth_hdr_field close_scope_eth
@@ -5265,7 +5478,7 @@ eth_hdr_expr : ETHER eth_hdr_field close_scope_eth
eth_hdr_field : SADDR { $$ = ETHHDR_SADDR; }
| DADDR { $$ = ETHHDR_DADDR; }
- | TYPE { $$ = ETHHDR_TYPE; }
+ | TYPE close_scope_type { $$ = ETHHDR_TYPE; }
;
vlan_hdr_expr : VLAN vlan_hdr_field close_scope_vlan
@@ -5278,7 +5491,7 @@ vlan_hdr_field : ID { $$ = VLANHDR_VID; }
| CFI { $$ = VLANHDR_CFI; }
| DEI { $$ = VLANHDR_DEI; }
| PCP { $$ = VLANHDR_PCP; }
- | TYPE { $$ = VLANHDR_TYPE; }
+ | TYPE close_scope_type { $$ = VLANHDR_TYPE; }
;
arp_hdr_expr : ARP arp_hdr_field close_scope_arp
@@ -5304,11 +5517,15 @@ ip_hdr_expr : IP ip_hdr_field close_scope_ip
}
| IP OPTION ip_option_type ip_option_field close_scope_ip
{
- $$ = ipopt_expr_alloc(&@$, $3, $4, 0);
+ $$ = ipopt_expr_alloc(&@$, $3, $4);
+ if (!$$) {
+ erec_queue(error(&@1, "unknown ip option type/field"), state->msgs);
+ YYERROR;
+ }
}
| IP OPTION ip_option_type close_scope_ip
{
- $$ = ipopt_expr_alloc(&@$, $3, IPOPT_FIELD_TYPE, 0);
+ $$ = ipopt_expr_alloc(&@$, $3, IPOPT_FIELD_TYPE);
$$->exthdr.flags = NFT_EXTHDR_F_PRESENT;
}
;
@@ -5333,20 +5550,20 @@ ip_option_type : LSRR { $$ = IPOPT_LSRR; }
| RA { $$ = IPOPT_RA; }
;
-ip_option_field : TYPE { $$ = IPOPT_FIELD_TYPE; }
+ip_option_field : TYPE close_scope_type { $$ = IPOPT_FIELD_TYPE; }
| LENGTH { $$ = IPOPT_FIELD_LENGTH; }
| VALUE { $$ = IPOPT_FIELD_VALUE; }
| PTR { $$ = IPOPT_FIELD_PTR; }
| ADDR { $$ = IPOPT_FIELD_ADDR_0; }
;
-icmp_hdr_expr : ICMP icmp_hdr_field
+icmp_hdr_expr : ICMP icmp_hdr_field close_scope_icmp
{
$$ = payload_expr_alloc(&@$, &proto_icmp, $2);
}
;
-icmp_hdr_field : TYPE { $$ = ICMPHDR_TYPE; }
+icmp_hdr_field : TYPE close_scope_type { $$ = ICMPHDR_TYPE; }
| CODE { $$ = ICMPHDR_CODE; }
| CHECKSUM { $$ = ICMPHDR_CHECKSUM; }
| ID { $$ = ICMPHDR_ID; }
@@ -5355,13 +5572,13 @@ icmp_hdr_field : TYPE { $$ = ICMPHDR_TYPE; }
| MTU { $$ = ICMPHDR_MTU; }
;
-igmp_hdr_expr : IGMP igmp_hdr_field
+igmp_hdr_expr : IGMP igmp_hdr_field close_scope_igmp
{
$$ = payload_expr_alloc(&@$, &proto_igmp, $2);
}
;
-igmp_hdr_field : TYPE { $$ = IGMPHDR_TYPE; }
+igmp_hdr_field : TYPE close_scope_type { $$ = IGMPHDR_TYPE; }
| CHECKSUM { $$ = IGMPHDR_CHECKSUM; }
| MRT { $$ = IGMPHDR_MRT; }
| GROUP { $$ = IGMPHDR_GROUP; }
@@ -5383,13 +5600,13 @@ ip6_hdr_field : HDRVERSION { $$ = IP6HDR_VERSION; }
| SADDR { $$ = IP6HDR_SADDR; }
| DADDR { $$ = IP6HDR_DADDR; }
;
-icmp6_hdr_expr : ICMP6 icmp6_hdr_field
+icmp6_hdr_expr : ICMP6 icmp6_hdr_field close_scope_icmp
{
$$ = payload_expr_alloc(&@$, &proto_icmp6, $2);
}
;
-icmp6_hdr_field : TYPE { $$ = ICMP6HDR_TYPE; }
+icmp6_hdr_field : TYPE close_scope_type { $$ = ICMP6HDR_TYPE; }
| CODE { $$ = ICMP6HDR_CODE; }
| CHECKSUM { $$ = ICMP6HDR_CHECKSUM; }
| PPTR { $$ = ICMP6HDR_PPTR; }
@@ -5399,7 +5616,7 @@ icmp6_hdr_field : TYPE { $$ = ICMP6HDR_TYPE; }
| MAXDELAY { $$ = ICMP6HDR_MAXDELAY; }
;
-auth_hdr_expr : AH auth_hdr_field
+auth_hdr_expr : AH auth_hdr_field close_scope_ah
{
$$ = payload_expr_alloc(&@$, &proto_ah, $2);
}
@@ -5412,7 +5629,7 @@ auth_hdr_field : NEXTHDR { $$ = AHHDR_NEXTHDR; }
| SEQUENCE { $$ = AHHDR_SEQUENCE; }
;
-esp_hdr_expr : ESP esp_hdr_field
+esp_hdr_expr : ESP esp_hdr_field close_scope_esp
{
$$ = payload_expr_alloc(&@$, &proto_esp, $2);
}
@@ -5422,7 +5639,7 @@ esp_hdr_field : SPI { $$ = ESPHDR_SPI; }
| SEQUENCE { $$ = ESPHDR_SEQUENCE; }
;
-comp_hdr_expr : COMP comp_hdr_field
+comp_hdr_expr : COMP comp_hdr_field close_scope_comp
{
$$ = payload_expr_alloc(&@$, &proto_comp, $2);
}
@@ -5433,7 +5650,7 @@ comp_hdr_field : NEXTHDR { $$ = COMPHDR_NEXTHDR; }
| CPI { $$ = COMPHDR_CPI; }
;
-udp_hdr_expr : UDP udp_hdr_field
+udp_hdr_expr : UDP udp_hdr_field close_scope_udp
{
$$ = payload_expr_alloc(&@$, &proto_udp, $2);
}
@@ -5445,7 +5662,7 @@ udp_hdr_field : SPORT { $$ = UDPHDR_SPORT; }
| CHECKSUM { $$ = UDPHDR_CHECKSUM; }
;
-udplite_hdr_expr : UDPLITE udplite_hdr_field
+udplite_hdr_expr : UDPLITE udplite_hdr_field close_scope_udplite
{
$$ = payload_expr_alloc(&@$, &proto_udplite, $2);
}
@@ -5461,19 +5678,114 @@ tcp_hdr_expr : TCP tcp_hdr_field
{
$$ = payload_expr_alloc(&@$, &proto_tcp, $2);
}
- | TCP OPTION tcp_hdr_option_type tcp_hdr_option_field
- {
- $$ = tcpopt_expr_alloc(&@$, $3, $4);
- }
| TCP OPTION tcp_hdr_option_type
{
$$ = tcpopt_expr_alloc(&@$, $3, TCPOPT_COMMON_KIND);
$$->exthdr.flags = NFT_EXTHDR_F_PRESENT;
}
- | TCP OPTION AT tcp_hdr_option_type COMMA NUM COMMA NUM
+ | TCP OPTION tcp_hdr_option_kind_and_field
+ {
+ $$ = tcpopt_expr_alloc(&@$, $3.kind, $3.field);
+ }
+ | TCP OPTION AT close_scope_at tcp_hdr_option_type COMMA NUM COMMA NUM
+ {
+ $$ = tcpopt_expr_alloc(&@$, $5, 0);
+ tcpopt_init_raw($$, $5, $7, $9, 0);
+ }
+ ;
+
+inner_inet_expr : ip_hdr_expr
+ | icmp_hdr_expr
+ | igmp_hdr_expr
+ | ip6_hdr_expr
+ | icmp6_hdr_expr
+ | auth_hdr_expr
+ | esp_hdr_expr
+ | comp_hdr_expr
+ | udp_hdr_expr
+ | udplite_hdr_expr
+ | tcp_hdr_expr close_scope_tcp
+ | dccp_hdr_expr
+ | sctp_hdr_expr
+ | th_hdr_expr
+ ;
+
+inner_eth_expr : eth_hdr_expr
+ | vlan_hdr_expr
+ | arp_hdr_expr
+ ;
+
+inner_expr : inner_eth_expr
+ | inner_inet_expr
+ ;
+
+vxlan_hdr_expr : VXLAN vxlan_hdr_field
+ {
+ struct expr *expr;
+
+ expr = payload_expr_alloc(&@$, &proto_vxlan, $2);
+ expr->payload.inner_desc = &proto_vxlan;
+ $$ = expr;
+ }
+ | VXLAN inner_expr
+ {
+ $$ = $2;
+ $$->location = @$;
+ $$->payload.inner_desc = &proto_vxlan;
+ }
+ ;
+
+vxlan_hdr_field : VNI { $$ = VXLANHDR_VNI; }
+ | FLAGS { $$ = VXLANHDR_FLAGS; }
+ ;
+
+geneve_hdr_expr : GENEVE geneve_hdr_field
+ {
+ struct expr *expr;
+
+ expr = payload_expr_alloc(&@$, &proto_geneve, $2);
+ expr->payload.inner_desc = &proto_geneve;
+ $$ = expr;
+ }
+ | GENEVE inner_expr
+ {
+ $$ = $2;
+ $$->location = @$;
+ $$->payload.inner_desc = &proto_geneve;
+ }
+ ;
+
+geneve_hdr_field : VNI { $$ = GNVHDR_VNI; }
+ | TYPE { $$ = GNVHDR_TYPE; }
+ ;
+
+gre_hdr_expr : GRE gre_hdr_field close_scope_gre
+ {
+ $$ = payload_expr_alloc(&@$, &proto_gre, $2);
+ }
+ | GRE close_scope_gre inner_inet_expr
+ {
+ $$ = $3;
+ $$->payload.inner_desc = &proto_gre;
+ }
+ ;
+
+gre_hdr_field : HDRVERSION { $$ = GREHDR_VERSION; }
+ | FLAGS { $$ = GREHDR_FLAGS; }
+ | PROTOCOL { $$ = GREHDR_PROTOCOL; }
+ ;
+
+gretap_hdr_expr : GRETAP close_scope_gre inner_expr
+ {
+ $$ = $3;
+ $$->payload.inner_desc = &proto_gretap;
+ }
+ ;
+
+optstrip_stmt : RESET TCP OPTION tcp_hdr_option_type close_scope_tcp
{
- $$ = tcpopt_expr_alloc(&@$, $4, 0);
- tcpopt_init_raw($$, $4, $6, $8, 0);
+ $$ = optstrip_stmt_alloc(&@$, tcpopt_expr_alloc(&@$,
+ $4, TCPOPT_COMMON_KIND));
}
;
@@ -5489,19 +5801,57 @@ tcp_hdr_field : SPORT { $$ = TCPHDR_SPORT; }
| URGPTR { $$ = TCPHDR_URGPTR; }
;
-tcp_hdr_option_type : EOL { $$ = TCPOPT_KIND_EOL; }
- | NOP { $$ = TCPOPT_KIND_NOP; }
- | MSS { $$ = TCPOPT_KIND_MAXSEG; }
- | WINDOW { $$ = TCPOPT_KIND_WINDOW; }
- | SACK_PERM { $$ = TCPOPT_KIND_SACK_PERMITTED; }
- | SACK { $$ = TCPOPT_KIND_SACK; }
+tcp_hdr_option_kind_and_field : MSS tcpopt_field_maxseg
+ {
+ struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_MAXSEG, .field = $2 };
+ $$ = kind_field;
+ }
+ | tcp_hdr_option_sack tcpopt_field_sack
+ {
+ struct tcp_kind_field kind_field = { .kind = $1, .field = $2 };
+ $$ = kind_field;
+ }
+ | WINDOW tcpopt_field_window
+ {
+ struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_WINDOW, .field = $2 };
+ $$ = kind_field;
+ }
+ | TIMESTAMP tcpopt_field_tsopt
+ {
+ struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_TIMESTAMP, .field = $2 };
+ $$ = kind_field;
+ }
+ | tcp_hdr_option_type LENGTH
+ {
+ struct tcp_kind_field kind_field = { .kind = $1, .field = TCPOPT_COMMON_LENGTH };
+ $$ = kind_field;
+ }
+ | MPTCP tcpopt_field_mptcp
+ {
+ struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_MPTCP, .field = $2 };
+ $$ = kind_field;
+ }
+ ;
+
+tcp_hdr_option_sack : SACK { $$ = TCPOPT_KIND_SACK; }
| SACK0 { $$ = TCPOPT_KIND_SACK; }
| SACK1 { $$ = TCPOPT_KIND_SACK1; }
| SACK2 { $$ = TCPOPT_KIND_SACK2; }
| SACK3 { $$ = TCPOPT_KIND_SACK3; }
- | ECHO { $$ = TCPOPT_KIND_ECHO; }
- | TIMESTAMP { $$ = TCPOPT_KIND_TIMESTAMP; }
- | NUM {
+ ;
+
+tcp_hdr_option_type : ECHO { $$ = TCPOPT_KIND_ECHO; }
+ | EOL { $$ = TCPOPT_KIND_EOL; }
+ | FASTOPEN { $$ = TCPOPT_KIND_FASTOPEN; }
+ | MD5SIG { $$ = TCPOPT_KIND_MD5SIG; }
+ | MPTCP { $$ = TCPOPT_KIND_MPTCP; }
+ | MSS { $$ = TCPOPT_KIND_MAXSEG; }
+ | NOP { $$ = TCPOPT_KIND_NOP; }
+ | SACK_PERM { $$ = TCPOPT_KIND_SACK_PERMITTED; }
+ | TIMESTAMP { $$ = TCPOPT_KIND_TIMESTAMP; }
+ | WINDOW { $$ = TCPOPT_KIND_WINDOW; }
+ | tcp_hdr_option_sack { $$ = $1; }
+ | NUM {
if ($1 > 255) {
erec_queue(error(&@1, "value too large"), state->msgs);
YYERROR;
@@ -5510,17 +5860,24 @@ tcp_hdr_option_type : EOL { $$ = TCPOPT_KIND_EOL; }
}
;
-tcp_hdr_option_field : KIND { $$ = TCPOPT_COMMON_KIND; }
- | LENGTH { $$ = TCPOPT_COMMON_LENGTH; }
- | SIZE { $$ = TCPOPT_MAXSEG_SIZE; }
- | COUNT { $$ = TCPOPT_WINDOW_COUNT; }
- | LEFT { $$ = TCPOPT_SACK_LEFT; }
+tcpopt_field_sack : LEFT { $$ = TCPOPT_SACK_LEFT; }
| RIGHT { $$ = TCPOPT_SACK_RIGHT; }
- | TSVAL { $$ = TCPOPT_TS_TSVAL; }
+ ;
+
+tcpopt_field_window : COUNT { $$ = TCPOPT_WINDOW_COUNT; }
+ ;
+
+tcpopt_field_tsopt : TSVAL { $$ = TCPOPT_TS_TSVAL; }
| TSECR { $$ = TCPOPT_TS_TSECR; }
;
-dccp_hdr_expr : DCCP dccp_hdr_field
+tcpopt_field_maxseg : SIZE { $$ = TCPOPT_MAXSEG_SIZE; }
+ ;
+
+tcpopt_field_mptcp : SUBTYPE { $$ = TCPOPT_MPTCP_SUBTYPE; }
+ ;
+
+dccp_hdr_expr : DCCP dccp_hdr_field close_scope_dccp
{
$$ = payload_expr_alloc(&@$, &proto_dccp, $2);
}
@@ -5528,7 +5885,7 @@ dccp_hdr_expr : DCCP dccp_hdr_field
dccp_hdr_field : SPORT { $$ = DCCPHDR_SPORT; }
| DPORT { $$ = DCCPHDR_DPORT; }
- | TYPE { $$ = DCCPHDR_TYPE; }
+ | TYPE close_scope_type { $$ = DCCPHDR_TYPE; }
;
sctp_chunk_type : DATA { $$ = SCTP_CHUNK_TYPE_DATA; }
@@ -5551,7 +5908,7 @@ sctp_chunk_type : DATA { $$ = SCTP_CHUNK_TYPE_DATA; }
| ASCONF { $$ = SCTP_CHUNK_TYPE_ASCONF; }
;
-sctp_chunk_common_field : TYPE { $$ = SCTP_CHUNK_COMMON_TYPE; }
+sctp_chunk_common_field : TYPE close_scope_type { $$ = SCTP_CHUNK_COMMON_TYPE; }
| FLAGS { $$ = SCTP_CHUNK_COMMON_FLAGS; }
| LENGTH { $$ = SCTP_CHUNK_COMMON_LENGTH; }
;
@@ -5648,7 +6005,7 @@ sctp_hdr_field : SPORT { $$ = SCTPHDR_SPORT; }
| CHECKSUM { $$ = SCTPHDR_CHECKSUM; }
;
-th_hdr_expr : TRANSPORT_HDR th_hdr_field
+th_hdr_expr : TRANSPORT_HDR th_hdr_field close_scope_th
{
$$ = payload_expr_alloc(&@$, &proto_th, $2);
if ($$)
@@ -5670,7 +6027,7 @@ exthdr_expr : hbh_hdr_expr
| mh_hdr_expr
;
-hbh_hdr_expr : HBH hbh_hdr_field
+hbh_hdr_expr : HBH hbh_hdr_field close_scope_hbh
{
$$ = exthdr_expr_alloc(&@$, &exthdr_hbh, $2);
}
@@ -5688,11 +6045,11 @@ rt_hdr_expr : RT rt_hdr_field close_scope_rt
rt_hdr_field : NEXTHDR { $$ = RTHDR_NEXTHDR; }
| HDRLENGTH { $$ = RTHDR_HDRLENGTH; }
- | TYPE { $$ = RTHDR_TYPE; }
+ | TYPE close_scope_type { $$ = RTHDR_TYPE; }
| SEG_LEFT { $$ = RTHDR_SEG_LEFT; }
;
-rt0_hdr_expr : RT0 rt0_hdr_field
+rt0_hdr_expr : RT0 rt0_hdr_field close_scope_rt
{
$$ = exthdr_expr_alloc(&@$, &exthdr_rt0, $2);
}
@@ -5704,7 +6061,7 @@ rt0_hdr_field : ADDR '[' NUM ']'
}
;
-rt2_hdr_expr : RT2 rt2_hdr_field
+rt2_hdr_expr : RT2 rt2_hdr_field close_scope_rt
{
$$ = exthdr_expr_alloc(&@$, &exthdr_rt2, $2);
}
@@ -5713,7 +6070,7 @@ rt2_hdr_expr : RT2 rt2_hdr_field
rt2_hdr_field : ADDR { $$ = RT2HDR_ADDR; }
;
-rt4_hdr_expr : RT4 rt4_hdr_field
+rt4_hdr_expr : RT4 rt4_hdr_field close_scope_rt
{
$$ = exthdr_expr_alloc(&@$, &exthdr_rt4, $2);
}
@@ -5728,7 +6085,7 @@ rt4_hdr_field : LAST_ENT { $$ = RT4HDR_LASTENT; }
}
;
-frag_hdr_expr : FRAG frag_hdr_field
+frag_hdr_expr : FRAG frag_hdr_field close_scope_frag
{
$$ = exthdr_expr_alloc(&@$, &exthdr_frag, $2);
}
@@ -5742,7 +6099,7 @@ frag_hdr_field : NEXTHDR { $$ = FRAGHDR_NEXTHDR; }
| ID { $$ = FRAGHDR_ID; }
;
-dst_hdr_expr : DST dst_hdr_field
+dst_hdr_expr : DST dst_hdr_field close_scope_dst
{
$$ = exthdr_expr_alloc(&@$, &exthdr_dst, $2);
}
@@ -5752,7 +6109,7 @@ dst_hdr_field : NEXTHDR { $$ = DSTHDR_NEXTHDR; }
| HDRLENGTH { $$ = DSTHDR_HDRLENGTH; }
;
-mh_hdr_expr : MH mh_hdr_field
+mh_hdr_expr : MH mh_hdr_field close_scope_mh
{
$$ = exthdr_expr_alloc(&@$, &exthdr_mh, $2);
}
@@ -5760,7 +6117,7 @@ mh_hdr_expr : MH mh_hdr_field
mh_hdr_field : NEXTHDR { $$ = MHHDR_NEXTHDR; }
| HDRLENGTH { $$ = MHHDR_HDRLENGTH; }
- | TYPE { $$ = MHHDR_TYPE; }
+ | TYPE close_scope_type { $$ = MHHDR_TYPE; }
| RESERVED { $$ = MHHDR_RESERVED; }
| CHECKSUM { $$ = MHHDR_CHECKSUM; }
;
@@ -5779,11 +6136,11 @@ exthdr_exists_expr : EXTHDR exthdr_key
}
;
-exthdr_key : HBH { $$ = IPPROTO_HOPOPTS; }
+exthdr_key : HBH close_scope_hbh { $$ = IPPROTO_HOPOPTS; }
| RT close_scope_rt { $$ = IPPROTO_ROUTING; }
- | FRAG { $$ = IPPROTO_FRAGMENT; }
- | DST { $$ = IPPROTO_DSTOPTS; }
- | MH { $$ = IPPROTO_MH; }
+ | FRAG close_scope_frag { $$ = IPPROTO_FRAGMENT; }
+ | DST close_scope_dst { $$ = IPPROTO_DSTOPTS; }
+ | MH close_scope_mh { $$ = IPPROTO_MH; }
;
%%
diff --git a/src/parser_json.c b/src/parser_json.c
index 3cd21175..ec0c02a0 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -1,3 +1,11 @@
+/*
+ * 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.
+ */
+
#define _GNU_SOURCE
#include <errno.h>
#include <stdint.h> /* needed by gmputil.h */
@@ -558,6 +566,8 @@ static struct expr *json_parse_payload_expr(struct json_ctx *ctx,
val = PROTO_BASE_NETWORK_HDR;
} else if (!strcmp(base, "th")) {
val = PROTO_BASE_TRANSPORT_HDR;
+ } else if (!strcmp(base, "ih")) {
+ val = PROTO_BASE_INNER_HDR;
} else {
json_error(ctx, "Invalid payload base '%s'.", base);
return NULL;
@@ -687,7 +697,7 @@ static struct expr *json_parse_ip_option_expr(struct json_ctx *ctx,
if (json_unpack(root, "{s:s}", "field", &field)) {
expr = ipopt_expr_alloc(int_loc, descval,
- IPOPT_FIELD_TYPE, 0);
+ IPOPT_FIELD_TYPE);
expr->exthdr.flags = NFT_EXTHDR_F_PRESENT;
return expr;
@@ -696,7 +706,7 @@ static struct expr *json_parse_ip_option_expr(struct json_ctx *ctx,
json_error(ctx, "Unknown ip option field '%s'.", field);
return NULL;
}
- return ipopt_expr_alloc(int_loc, descval, fieldval, 0);
+ return ipopt_expr_alloc(int_loc, descval, fieldval);
}
static int json_parse_sctp_chunk_field(const struct exthdr_desc *desc,
@@ -1824,7 +1834,7 @@ static struct stmt *json_parse_limit_stmt(struct json_ctx *ctx,
const char *key, json_t *value)
{
struct stmt *stmt;
- uint64_t rate, burst = 5;
+ uint64_t rate, burst = 0;
const char *rate_unit = "packets", *time, *burst_unit = "bytes";
int inv = 0;
@@ -1838,6 +1848,9 @@ static struct stmt *json_parse_limit_stmt(struct json_ctx *ctx,
stmt = limit_stmt_alloc(int_loc);
if (!strcmp(rate_unit, "packets")) {
+ if (burst == 0)
+ burst = 5;
+
stmt->limit.type = NFT_LIMIT_PKTS;
stmt->limit.rate = rate;
stmt->limit.burst = burst;
@@ -1901,6 +1914,28 @@ out_err:
return NULL;
}
+static struct stmt *json_parse_flow_offload_stmt(struct json_ctx *ctx,
+ const char *key, json_t *value)
+{
+ const char *opstr, *flowtable;
+
+ if (json_unpack_err(ctx, value, "{s:s, s:s}",
+ "op", &opstr, "flowtable", &flowtable))
+ return NULL;
+
+ if (strcmp(opstr, "add")) {
+ json_error(ctx, "Unknown flow offload statement op '%s'.", opstr);
+ return NULL;
+ }
+
+ if (flowtable[0] != '@') {
+ json_error(ctx, "Illegal flowtable reference in flow offload statement.");
+ return NULL;
+ }
+
+ return flow_offload_stmt_alloc(int_loc, xstrdup(flowtable + 1));
+}
+
static struct stmt *json_parse_notrack_stmt(struct json_ctx *ctx,
const char *key, json_t *value)
{
@@ -1939,6 +1974,23 @@ static struct stmt *json_parse_dup_stmt(struct json_ctx *ctx,
return stmt;
}
+static struct stmt *json_parse_secmark_stmt(struct json_ctx *ctx,
+ const char *key, json_t *value)
+{
+ struct stmt *stmt;
+
+ stmt = objref_stmt_alloc(int_loc);
+ stmt->objref.type = NFT_OBJECT_SECMARK;
+ stmt->objref.expr = json_parse_stmt_expr(ctx, value);
+ if (!stmt->objref.expr) {
+ json_error(ctx, "Invalid secmark reference.");
+ stmt_free(stmt);
+ return NULL;
+ }
+
+ return stmt;
+}
+
static int json_parse_nat_flag(struct json_ctx *ctx,
json_t *root, int *flags)
{
@@ -2203,13 +2255,36 @@ static struct stmt *json_parse_reject_stmt(struct json_ctx *ctx,
return stmt;
}
+static void json_parse_set_stmt_list(struct json_ctx *ctx,
+ struct list_head *stmt_list,
+ json_t *stmt_json)
+{
+ struct list_head *head;
+ struct stmt *tmp;
+ json_t *value;
+ size_t index;
+
+ if (!stmt_json)
+ return;
+
+ if (!json_is_array(stmt_json))
+ json_error(ctx, "Unexpected object type in stmt");
+
+ head = stmt_list;
+ json_array_foreach(stmt_json, index, value) {
+ tmp = json_parse_stmt(ctx, value);
+ list_add(&tmp->list, head);
+ head = &tmp->list;
+ }
+}
+
static struct stmt *json_parse_set_stmt(struct json_ctx *ctx,
const char *key, json_t *value)
{
const char *opstr, *set;
struct expr *expr, *expr2;
+ json_t *elem, *stmt_json;
struct stmt *stmt;
- json_t *elem;
int op;
if (json_unpack_err(ctx, value, "{s:s, s:o, s:s}",
@@ -2244,6 +2319,10 @@ static struct stmt *json_parse_set_stmt(struct json_ctx *ctx,
stmt->set.op = op;
stmt->set.key = expr;
stmt->set.set = expr2;
+
+ if (!json_unpack(value, "{s:o}", "stmt", &stmt_json))
+ json_parse_set_stmt_list(ctx, &stmt->set.stmt_list, stmt_json);
+
return stmt;
}
@@ -2628,6 +2707,14 @@ static struct stmt *json_parse_connlimit_stmt(struct json_ctx *ctx,
return stmt;
}
+static struct stmt *json_parse_optstrip_stmt(struct json_ctx *ctx,
+ const char *key, json_t *value)
+{
+ struct expr *expr = json_parse_expr(ctx, value);
+
+ return expr ? optstrip_stmt_alloc(int_loc, expr) : NULL;
+}
+
static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
{
struct {
@@ -2645,6 +2732,7 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
{ "mangle", json_parse_mangle_stmt },
{ "quota", json_parse_quota_stmt },
{ "limit", json_parse_limit_stmt },
+ { "flow", json_parse_flow_offload_stmt },
{ "fwd", json_parse_fwd_stmt },
{ "notrack", json_parse_notrack_stmt },
{ "dup", json_parse_dup_stmt },
@@ -2663,6 +2751,8 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
{ "ct count", json_parse_connlimit_stmt },
{ "tproxy", json_parse_tproxy_stmt },
{ "synproxy", json_parse_synproxy_stmt },
+ { "reset", json_parse_optstrip_stmt },
+ { "secmark", json_parse_secmark_stmt },
};
const char *type;
unsigned int i;
@@ -2682,6 +2772,11 @@ static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root)
return verdict_stmt_alloc(int_loc, expr);
}
+ if (!strcmp(type, "xt")) {
+ json_error(ctx, "unsupported xtables compat expression, use iptables-nft with this ruleset");
+ return NULL;
+ }
+
for (i = 0; i < array_size(stmt_parser_tbl); i++) {
if (!strcmp(type, stmt_parser_tbl[i].key))
return stmt_parser_tbl[i].cb(ctx, stmt_parser_tbl[i].key, tmp);
@@ -2746,8 +2841,7 @@ static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root,
struct handle h = {
.table.location = *int_loc,
};
- const char *family = "", *policy = "", *type, *hookstr;
- const char name[IFNAMSIZ];
+ const char *family = "", *policy = "", *type, *hookstr, *name;
struct chain *chain;
int prio;
@@ -2842,7 +2936,7 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root,
if (op != CMD_DELETE &&
json_unpack_err(ctx, root, "{s:o}", "expr", &tmp))
return NULL;
- else if (op == CMD_DELETE &&
+ else if ((op == CMD_DELETE || op == CMD_DESTROY) &&
json_unpack_err(ctx, root, "{s:I}", "handle", &h.handle.id))
return NULL;
@@ -2853,7 +2947,7 @@ static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root,
h.table.name = xstrdup(h.table.name);
h.chain.name = xstrdup(h.chain.name);
- if (op == CMD_DELETE)
+ if (op == CMD_DELETE || op == CMD_DESTROY)
return cmd_alloc(op, obj, &h, int_loc, NULL);
if (!json_is_array(tmp)) {
@@ -2939,8 +3033,8 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root,
{
struct handle h = { 0 };
const char *family = "", *policy, *dtype_ext = NULL;
+ json_t *tmp, *stmt_json;
struct set *set;
- json_t *tmp;
if (json_unpack_err(ctx, root, "{s:s, s:s}",
"family", &family,
@@ -2949,7 +3043,7 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root,
if (op != CMD_DELETE &&
json_unpack_err(ctx, root, "{s:s}", "name", &h.set.name)) {
return NULL;
- } else if (op == CMD_DELETE &&
+ } else if ((op == CMD_DELETE || op == CMD_DESTROY) &&
json_unpack(root, "{s:s}", "name", &h.set.name) &&
json_unpack(root, "{s:I}", "handle", &h.handle.id)) {
json_error(ctx, "Either name or handle required to delete a set.");
@@ -2966,6 +3060,7 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root,
switch (op) {
case CMD_DELETE:
+ case CMD_DESTROY:
case CMD_LIST:
case CMD_FLUSH:
return cmd_alloc(op, obj, &h, int_loc, NULL);
@@ -3051,6 +3146,9 @@ static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root,
set->gc_int *= 1000;
json_unpack(root, "{s:i}", "size", &set->desc.size);
+ if (!json_unpack(root, "{s:o}", "stmt", &stmt_json))
+ json_parse_set_stmt_list(ctx, &set->stmt_list, stmt_json);
+
handle_merge(&set->handle, &h);
if (op == CMD_ADD)
@@ -3100,7 +3198,9 @@ static struct expr *json_parse_flowtable_devs(struct json_ctx *ctx,
size_t index;
if (!json_unpack(root, "s", &dev)) {
- tmp = symbol_expr_alloc(int_loc, SYMBOL_VALUE, NULL, dev);
+ tmp = constant_expr_alloc(int_loc, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ strlen(dev) * BITS_PER_BYTE, dev);
compound_expr_add(expr, tmp);
return expr;
}
@@ -3116,7 +3216,9 @@ static struct expr *json_parse_flowtable_devs(struct json_ctx *ctx,
expr_free(expr);
return NULL;
}
- tmp = symbol_expr_alloc(int_loc, SYMBOL_VALUE, NULL, dev);
+ tmp = constant_expr_alloc(int_loc, &string_type,
+ BYTEORDER_HOST_ENDIAN,
+ strlen(dev) * BITS_PER_BYTE, dev);
compound_expr_add(expr, tmp);
}
return expr;
@@ -3129,7 +3231,7 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx,
const char *family, *hook, *hookstr;
struct flowtable *flowtable;
struct handle h = { 0 };
- json_t *devs;
+ json_t *devs = NULL;
int prio;
if (json_unpack_err(ctx, root, "{s:s, s:s}",
@@ -3140,7 +3242,7 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx,
if (op != CMD_DELETE &&
json_unpack_err(ctx, root, "{s:s}", "name", &h.flowtable.name)) {
return NULL;
- } else if (op == CMD_DELETE &&
+ } else if ((op == CMD_DELETE || op == CMD_DESTROY) &&
json_unpack(root, "{s:s}", "name", &h.flowtable.name) &&
json_unpack(root, "{s:I}", "handle", &h.handle.id)) {
json_error(ctx, "Either name or handle required to delete a flowtable.");
@@ -3155,17 +3257,18 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx,
if (h.flowtable.name)
h.flowtable.name = xstrdup(h.flowtable.name);
- if (op == CMD_DELETE || op == CMD_LIST)
+ if (op == CMD_DELETE || op == CMD_LIST || op == CMD_DESTROY)
return cmd_alloc(op, cmd_obj, &h, int_loc, NULL);
- if (json_unpack_err(ctx, root, "{s:s, s:I, s:o}",
+ if (json_unpack_err(ctx, root, "{s:s, s:I}",
"hook", &hook,
- "prio", &prio,
- "dev", &devs)) {
+ "prio", &prio)) {
handle_free(&h);
return NULL;
}
+ json_unpack(root, "{s:o}", "dev", &devs);
+
hookstr = chain_hookname_lookup(hook);
if (!hookstr) {
json_error(ctx, "Invalid flowtable hook '%s'.", hook);
@@ -3180,12 +3283,14 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx,
BYTEORDER_HOST_ENDIAN,
sizeof(int) * BITS_PER_BYTE, &prio);
- flowtable->dev_expr = json_parse_flowtable_devs(ctx, devs);
- if (!flowtable->dev_expr) {
- json_error(ctx, "Invalid flowtable dev.");
- flowtable_free(flowtable);
- handle_free(&h);
- return NULL;
+ if (devs) {
+ flowtable->dev_expr = json_parse_flowtable_devs(ctx, devs);
+ if (!flowtable->dev_expr) {
+ json_error(ctx, "Invalid flowtable dev.");
+ flowtable_free(flowtable);
+ handle_free(&h);
+ return NULL;
+ }
}
return cmd_alloc(op, cmd_obj, &h, int_loc, flowtable);
}
@@ -3241,7 +3346,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
cmd_obj == NFT_OBJECT_CT_HELPER) &&
json_unpack_err(ctx, root, "{s:s}", "name", &h.obj.name)) {
return NULL;
- } else if (op == CMD_DELETE &&
+ } else if ((op == CMD_DELETE || op == CMD_DESTROY) &&
cmd_obj != NFT_OBJECT_CT_HELPER &&
json_unpack(root, "{s:s}", "name", &h.obj.name) &&
json_unpack(root, "{s:I}", "handle", &h.handle.id)) {
@@ -3257,7 +3362,7 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
if (h.obj.name)
h.obj.name = xstrdup(h.obj.name);
- if (op == CMD_DELETE || op == CMD_LIST) {
+ if (op == CMD_DELETE || op == CMD_LIST || op == CMD_DESTROY) {
if (cmd_obj == NFT_OBJECT_CT_HELPER)
return cmd_alloc_obj_ct(op, NFT_OBJECT_CT_HELPER,
&h, int_loc, obj_alloc(int_loc));
@@ -3266,6 +3371,9 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx,
obj = obj_alloc(int_loc);
+ if (!json_unpack(root, "{s:s}", "comment", &obj->comment))
+ obj->comment = xstrdup(obj->comment);
+
switch (cmd_obj) {
case CMD_OBJ_COUNTER:
obj->type = NFT_OBJECT_COUNTER;
@@ -3656,6 +3764,39 @@ static struct cmd *json_parse_cmd_list(struct json_ctx *ctx,
return NULL;
}
+static struct cmd *json_parse_cmd_reset_rule(struct json_ctx *ctx,
+ json_t *root, enum cmd_ops op,
+ enum cmd_obj obj)
+{
+ struct handle h = {
+ .family = NFPROTO_UNSPEC,
+ };
+ const char *family = NULL, *table = NULL, *chain = NULL;
+
+
+ if (obj == CMD_OBJ_RULE &&
+ json_unpack_err(ctx, root, "{s:s, s:s, s:s, s:I}",
+ "family", &family, "table", &table,
+ "chain", &chain, "handle", &h.handle.id))
+ return NULL;
+ else if (obj == CMD_OBJ_RULES) {
+ json_unpack(root, "{s:s}", "family", &family);
+ json_unpack(root, "{s:s}", "table", &table);
+ json_unpack(root, "{s:s}", "chain", &chain);
+ }
+
+ if (family && parse_family(family, &h.family)) {
+ json_error(ctx, "Unknown family '%s'.", family);
+ return NULL;
+ }
+ if (table) {
+ h.table.name = xstrdup(table);
+ if (chain)
+ h.chain.name = xstrdup(chain);
+ }
+ return cmd_alloc(op, obj, &h, int_loc, NULL);
+}
+
static struct cmd *json_parse_cmd_reset(struct json_ctx *ctx,
json_t *root, enum cmd_ops op)
{
@@ -3669,6 +3810,8 @@ static struct cmd *json_parse_cmd_reset(struct json_ctx *ctx,
{ "counters", CMD_OBJ_COUNTERS, json_parse_cmd_list_multiple },
{ "quota", CMD_OBJ_QUOTA, json_parse_cmd_add_object },
{ "quotas", CMD_OBJ_QUOTAS, json_parse_cmd_list_multiple },
+ { "rule", CMD_OBJ_RULE, json_parse_cmd_reset_rule },
+ { "rules", CMD_OBJ_RULES, json_parse_cmd_reset_rule },
};
unsigned int i;
json_t *tmp;
@@ -3767,6 +3910,7 @@ static struct cmd *json_parse_cmd(struct json_ctx *ctx, json_t *root)
{ "reset", CMD_RESET, json_parse_cmd_reset },
{ "flush", CMD_FLUSH, json_parse_cmd_flush },
{ "rename", CMD_RENAME, json_parse_cmd_rename },
+ { "destroy", CMD_DESTROY, json_parse_cmd_add },
//{ "export", CMD_EXPORT, json_parse_cmd_export },
//{ "monitor", CMD_MONITOR, json_parse_cmd_monitor },
//{ "describe", CMD_DESCRIBE, json_parse_cmd_describe }
@@ -3789,13 +3933,14 @@ static int json_verify_metainfo(struct json_ctx *ctx, json_t *root)
{
int schema_version;
- if (!json_unpack(root, "{s:i}", "json_schema_version", &schema_version))
- return 0;
-
- if (schema_version > JSON_SCHEMA_VERSION) {
- json_error(ctx, "Schema version %d not supported, maximum supported version is %d\n",
- schema_version, JSON_SCHEMA_VERSION);
- return 1;
+ if (!json_unpack(root, "{s:i}", "json_schema_version", &schema_version)) {
+ if (schema_version > JSON_SCHEMA_VERSION) {
+ json_error(ctx,
+ "Schema version %d not supported, maximum"
+ " supported version is %d\n",
+ schema_version, JSON_SCHEMA_VERSION);
+ return 1;
+ }
}
return 0;
diff --git a/src/payload.c b/src/payload.c
index 97b60713..ed76623c 100644
--- a/src/payload.c
+++ b/src/payload.c
@@ -47,6 +47,10 @@ static void payload_expr_print(const struct expr *expr, struct output_ctx *octx)
const struct proto_desc *desc;
const struct proto_hdr_template *tmpl;
+ if (expr->payload.inner_desc &&
+ expr->payload.inner_desc != expr->payload.desc)
+ nft_print(octx, "%s ", expr->payload.inner_desc->name);
+
desc = expr->payload.desc;
tmpl = expr->payload.tmpl;
if (payload_is_known(expr))
@@ -67,6 +71,7 @@ bool payload_expr_cmp(const struct expr *e1, const struct expr *e2)
static void payload_expr_clone(struct expr *new, const struct expr *expr)
{
+ new->payload.inner_desc = expr->payload.inner_desc;
new->payload.desc = expr->payload.desc;
new->payload.tmpl = expr->payload.tmpl;
new->payload.base = expr->payload.base;
@@ -115,18 +120,32 @@ static void payload_expr_pctx_update(struct proto_ctx *ctx,
assert(desc->base <= PROTO_BASE_MAX);
if (desc->base == base->base) {
assert(base->length > 0);
- ctx->protocol[base->base].offset += base->length;
+
+ if (!left->payload.is_raw) {
+ if (desc->base == PROTO_BASE_LL_HDR &&
+ ctx->stacked_ll_count < PROTO_CTX_NUM_PROTOS) {
+ ctx->stacked_ll[ctx->stacked_ll_count] = base;
+ ctx->stacked_ll_count++;
+ }
+ }
}
proto_ctx_update(ctx, desc->base, loc, desc);
}
#define NFTNL_UDATA_SET_KEY_PAYLOAD_DESC 0
#define NFTNL_UDATA_SET_KEY_PAYLOAD_TYPE 1
-#define NFTNL_UDATA_SET_KEY_PAYLOAD_MAX 2
+#define NFTNL_UDATA_SET_KEY_PAYLOAD_BASE 2
+#define NFTNL_UDATA_SET_KEY_PAYLOAD_OFFSET 3
+#define NFTNL_UDATA_SET_KEY_PAYLOAD_LEN 4
+#define NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC 5
+#define NFTNL_UDATA_SET_KEY_PAYLOAD_MAX 6
static unsigned int expr_payload_type(const struct proto_desc *desc,
const struct proto_hdr_template *tmpl)
{
+ if (desc->id == PROTO_DESC_UNKNOWN)
+ return 0;
+
return (unsigned int)(tmpl - &desc->templates[0]);
}
@@ -140,10 +159,24 @@ static int payload_expr_build_udata(struct nftnl_udata_buf *udbuf,
nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_DESC, desc->id);
nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_TYPE, type);
+ if (desc->id == 0) {
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_BASE,
+ expr->payload.base);
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_OFFSET,
+ expr->payload.offset);
+ }
+ if (expr->dtype->type == TYPE_INTEGER)
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_LEN, expr->len);
+
+ if (expr->payload.inner_desc) {
+ nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC,
+ expr->payload.inner_desc->id);
+ }
+
return 0;
}
-static const struct proto_desc *find_proto_desc(const struct nftnl_udata *ud)
+const struct proto_desc *find_proto_desc(const struct nftnl_udata *ud)
{
return proto_find_desc(nftnl_udata_get_u32(ud));
}
@@ -157,6 +190,10 @@ static int payload_parse_udata(const struct nftnl_udata *attr, void *data)
switch (type) {
case NFTNL_UDATA_SET_KEY_PAYLOAD_DESC:
case NFTNL_UDATA_SET_KEY_PAYLOAD_TYPE:
+ case NFTNL_UDATA_SET_KEY_PAYLOAD_BASE:
+ case NFTNL_UDATA_SET_KEY_PAYLOAD_OFFSET:
+ case NFTNL_UDATA_SET_KEY_PAYLOAD_LEN:
+ case NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC:
if (len != sizeof(uint32_t))
return -1;
break;
@@ -171,8 +208,10 @@ static int payload_parse_udata(const struct nftnl_udata *attr, void *data)
static struct expr *payload_expr_parse_udata(const struct nftnl_udata *attr)
{
const struct nftnl_udata *ud[NFTNL_UDATA_SET_KEY_PAYLOAD_MAX + 1] = {};
+ unsigned int type, base, offset, len = 0;
const struct proto_desc *desc;
- unsigned int type;
+ bool is_raw = false;
+ struct expr *expr;
int err;
err = nftnl_udata_parse(nftnl_udata_get(attr), nftnl_udata_len(attr),
@@ -185,12 +224,45 @@ static struct expr *payload_expr_parse_udata(const struct nftnl_udata *attr)
return NULL;
desc = find_proto_desc(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_DESC]);
- if (!desc)
- return NULL;
+ if (!desc) {
+ if (!ud[NFTNL_UDATA_SET_KEY_PAYLOAD_BASE] ||
+ !ud[NFTNL_UDATA_SET_KEY_PAYLOAD_OFFSET])
+ return NULL;
+
+ base = nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_BASE]);
+ offset = nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_OFFSET]);
+ is_raw = true;
+ }
type = nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_TYPE]);
+ if (ud[NFTNL_UDATA_SET_KEY_PAYLOAD_LEN])
+ len = nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_LEN]);
+
+ expr = payload_expr_alloc(&internal_location, desc, type);
+
+ if (len)
+ expr->len = len;
+
+ if (is_raw) {
+ struct datatype *dtype;
+
+ expr->payload.base = base;
+ expr->payload.offset = offset;
+ expr->payload.is_raw = true;
+ expr->len = len;
+ dtype = dtype_clone(&xinteger_type);
+ dtype->size = len;
+ dtype->byteorder = BYTEORDER_BIG_ENDIAN;
+ dtype->refcnt = 1;
+ expr->dtype = dtype;
+ }
- return payload_expr_alloc(&internal_location, desc, type);
+ if (ud[NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC]) {
+ desc = find_proto_desc(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC]);
+ expr->payload.inner_desc = desc;
+ }
+
+ return expr;
}
const struct expr_ops payload_expr_ops = {
@@ -267,7 +339,7 @@ void payload_init_raw(struct expr *expr, enum proto_bases base,
expr->payload.base = base;
expr->payload.offset = offset;
expr->len = len;
- expr->dtype = &integer_type;
+ expr->dtype = &xinteger_type;
if (base != PROTO_BASE_TRANSPORT_HDR)
return;
@@ -336,9 +408,11 @@ static int payload_add_dependency(struct eval_ctx *ctx,
{
const struct proto_hdr_template *tmpl;
struct expr *dep, *left, *right;
+ struct proto_ctx *pctx;
struct stmt *stmt;
- int protocol = proto_find_num(desc, upper);
+ int protocol;
+ protocol = proto_find_num(desc, upper);
if (protocol < 0)
return expr_error(ctx->msgs, expr,
"conflicting protocols specified: %s vs. %s",
@@ -360,15 +434,24 @@ static int payload_add_dependency(struct eval_ctx *ctx,
return expr_error(ctx->msgs, expr,
"dependency statement is invalid");
}
- relational_expr_pctx_update(&ctx->pctx, dep);
+
+ if (ctx->inner_desc) {
+ if (tmpl->meta_key)
+ left->meta.inner_desc = ctx->inner_desc;
+ else
+ left->payload.inner_desc = ctx->inner_desc;
+ }
+
+ pctx = eval_proto_ctx(ctx);
+ relational_expr_pctx_update(pctx, dep);
*res = stmt;
return 0;
}
static const struct proto_desc *
-payload_get_get_ll_hdr(const struct eval_ctx *ctx)
+payload_get_get_ll_hdr(const struct proto_ctx *pctx)
{
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_INET:
return &proto_inet;
case NFPROTO_BRIDGE:
@@ -385,9 +468,11 @@ payload_get_get_ll_hdr(const struct eval_ctx *ctx)
static const struct proto_desc *
payload_gen_special_dependency(struct eval_ctx *ctx, const struct expr *expr)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
+
switch (expr->payload.base) {
case PROTO_BASE_LL_HDR:
- return payload_get_get_ll_hdr(ctx);
+ return payload_get_get_ll_hdr(pctx);
case PROTO_BASE_TRANSPORT_HDR:
if (expr->payload.desc == &proto_icmp ||
expr->payload.desc == &proto_icmp6 ||
@@ -395,13 +480,21 @@ payload_gen_special_dependency(struct eval_ctx *ctx, const struct expr *expr)
const struct proto_desc *desc, *desc_upper;
struct stmt *nstmt;
- desc = ctx->pctx.protocol[PROTO_BASE_LL_HDR].desc;
+ desc = pctx->protocol[PROTO_BASE_LL_HDR].desc;
if (!desc) {
- desc = payload_get_get_ll_hdr(ctx);
+ desc = payload_get_get_ll_hdr(pctx);
if (!desc)
break;
}
+ /* this tunnel protocol does not encapsulate an inner
+ * link layer, use proto_netdev which relies on
+ * NFT_META_PROTOCOL for dependencies.
+ */
+ if (expr->payload.inner_desc &&
+ !(expr->payload.inner_desc->inner.flags & NFT_INNER_LL))
+ desc = &proto_netdev;
+
desc_upper = &proto_ip6;
if (expr->payload.desc == &proto_icmp ||
expr->payload.desc == &proto_igmp)
@@ -447,11 +540,14 @@ payload_gen_special_dependency(struct eval_ctx *ctx, const struct expr *expr)
int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
struct stmt **res)
{
- const struct hook_proto_desc *h = &hook_proto_desc[ctx->pctx.family];
+ const struct hook_proto_desc *h;
const struct proto_desc *desc;
+ struct proto_ctx *pctx;
struct stmt *stmt;
uint16_t type;
+ pctx = eval_proto_ctx(ctx);
+ h = &hook_proto_desc[pctx->family];
if (expr->payload.base < h->base) {
if (expr->payload.base < h->base - 1)
return expr_error(ctx->msgs, expr,
@@ -472,7 +568,7 @@ int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
return 0;
}
- desc = ctx->pctx.protocol[expr->payload.base - 1].desc;
+ desc = pctx->protocol[expr->payload.base - 1].desc;
/* Special case for mixed IPv4/IPv6 and bridge tables */
if (desc == NULL)
desc = payload_gen_special_dependency(ctx, expr);
@@ -483,7 +579,7 @@ int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
"no %s protocol specified",
proto_base_names[expr->payload.base - 1]);
- if (ctx->pctx.family == NFPROTO_BRIDGE && desc == &proto_eth) {
+ if (pctx->family == NFPROTO_BRIDGE && desc == &proto_eth) {
/* prefer netdev proto, which adds dependencies based
* on skb->protocol.
*
@@ -508,11 +604,13 @@ int exthdr_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
enum proto_bases pb, struct stmt **res)
{
const struct proto_desc *desc;
+ struct proto_ctx *pctx;
- desc = ctx->pctx.protocol[pb].desc;
+ pctx = eval_proto_ctx(ctx);
+ desc = pctx->protocol[pb].desc;
if (desc == NULL) {
if (expr->exthdr.op == NFT_EXTHDR_OP_TCPOPT) {
- switch (ctx->pctx.family) {
+ switch (pctx->family) {
case NFPROTO_NETDEV:
case NFPROTO_BRIDGE:
case NFPROTO_INET:
@@ -608,8 +706,7 @@ void payload_dependency_store(struct payload_dep_ctx *ctx,
if (ignore_dep)
return;
- ctx->pdep = stmt;
- ctx->pbase = base + 1;
+ ctx->pdeps[base + 1] = stmt;
}
/**
@@ -624,20 +721,53 @@ void payload_dependency_store(struct payload_dep_ctx *ctx,
bool payload_dependency_exists(const struct payload_dep_ctx *ctx,
enum proto_bases base)
{
- return ctx->pbase != PROTO_BASE_INVALID &&
- ctx->pdep != NULL &&
- (ctx->pbase == base || (base == PROTO_BASE_TRANSPORT_HDR && ctx->pbase == base + 1));
+ if (ctx->pdeps[base])
+ return true;
+
+ return base == PROTO_BASE_TRANSPORT_HDR &&
+ ctx->pdeps[PROTO_BASE_INNER_HDR];
+}
+
+/**
+ * payload_dependency_get - return a payload dependency if available
+ * @ctx: payload dependency context
+ * @base: payload protocol base
+ *
+ * If we have seen a protocol key payload expression for this base, we return
+ * it.
+ */
+struct expr *payload_dependency_get(struct payload_dep_ctx *ctx,
+ enum proto_bases base)
+{
+ if (ctx->pdeps[base])
+ return ctx->pdeps[base]->expr;
+
+ if (base == PROTO_BASE_TRANSPORT_HDR &&
+ ctx->pdeps[PROTO_BASE_INNER_HDR])
+ return ctx->pdeps[PROTO_BASE_INNER_HDR]->expr;
+
+ return NULL;
}
-void payload_dependency_release(struct payload_dep_ctx *ctx)
+static void __payload_dependency_release(struct payload_dep_ctx *ctx,
+ enum proto_bases base)
{
- list_del(&ctx->pdep->list);
- stmt_free(ctx->pdep);
+ list_del(&ctx->pdeps[base]->list);
+ stmt_free(ctx->pdeps[base]);
- ctx->pbase = PROTO_BASE_INVALID;
- if (ctx->pdep == ctx->prev)
+ if (ctx->pdeps[base] == ctx->prev)
ctx->prev = NULL;
- ctx->pdep = NULL;
+ ctx->pdeps[base] = NULL;
+}
+
+void payload_dependency_release(struct payload_dep_ctx *ctx,
+ enum proto_bases base)
+{
+ if (ctx->pdeps[base])
+ __payload_dependency_release(ctx, base);
+ else if (base == PROTO_BASE_TRANSPORT_HDR &&
+ ctx->pdeps[PROTO_BASE_INNER_HDR])
+ __payload_dependency_release(ctx, PROTO_BASE_INNER_HDR);
}
static uint8_t icmp_dep_to_type(enum icmp_hdr_field_type t)
@@ -659,7 +789,7 @@ static uint8_t icmp_dep_to_type(enum icmp_hdr_field_type t)
static bool payload_may_dependency_kill_icmp(struct payload_dep_ctx *ctx, struct expr *expr)
{
- const struct expr *dep = ctx->pdep->expr;
+ const struct expr *dep = payload_dependency_get(ctx, expr->payload.base);
uint8_t icmp_type;
icmp_type = expr->payload.tmpl->icmp_dep;
@@ -676,9 +806,11 @@ static bool payload_may_dependency_kill_icmp(struct payload_dep_ctx *ctx, struct
static bool payload_may_dependency_kill_ll(struct payload_dep_ctx *ctx, struct expr *expr)
{
- const struct expr *dep = ctx->pdep->expr;
+ const struct expr *dep = payload_dependency_get(ctx, expr->payload.base);
- /* Never remove a 'vlan type 0x...' expression, they are never added implicitly */
+ /* Never remove a 'vlan type 0x...' expression, they are never added
+ * implicitly
+ */
if (dep->left->payload.desc == &proto_vlan)
return false;
@@ -695,7 +827,7 @@ static bool payload_may_dependency_kill_ll(struct payload_dep_ctx *ctx, struct e
static bool payload_may_dependency_kill(struct payload_dep_ctx *ctx,
unsigned int family, struct expr *expr)
{
- struct expr *dep = ctx->pdep->expr;
+ struct expr *dep = payload_dependency_get(ctx, expr->payload.base);
/* Protocol key payload expression at network base such as 'ip6 nexthdr'
* need to be left in place since it implicitly restricts matching to
@@ -731,13 +863,17 @@ static bool payload_may_dependency_kill(struct payload_dep_ctx *ctx,
break;
}
- if (expr->payload.base == PROTO_BASE_TRANSPORT_HDR &&
- dep->left->payload.base == PROTO_BASE_TRANSPORT_HDR) {
- if (dep->left->payload.desc == &proto_icmp)
- return payload_may_dependency_kill_icmp(ctx, expr);
- if (dep->left->payload.desc == &proto_icmp6)
- return payload_may_dependency_kill_icmp(ctx, expr);
- }
+ if (expr->payload.base != PROTO_BASE_TRANSPORT_HDR)
+ return true;
+
+ if (dep->left->payload.base != PROTO_BASE_TRANSPORT_HDR)
+ return true;
+
+ if (dep->left->payload.desc == &proto_icmp)
+ return payload_may_dependency_kill_icmp(ctx, expr);
+
+ if (dep->left->payload.desc == &proto_icmp6)
+ return payload_may_dependency_kill_icmp(ctx, expr);
return true;
}
@@ -755,9 +891,10 @@ static bool payload_may_dependency_kill(struct payload_dep_ctx *ctx,
void payload_dependency_kill(struct payload_dep_ctx *ctx, struct expr *expr,
unsigned int family)
{
- if (payload_dependency_exists(ctx, expr->payload.base) &&
+ if (expr->payload.desc != &proto_unknown &&
+ payload_dependency_exists(ctx, expr->payload.base) &&
payload_may_dependency_kill(ctx, family, expr))
- payload_dependency_release(ctx);
+ payload_dependency_release(ctx, expr->payload.base);
}
void exthdr_dependency_kill(struct payload_dep_ctx *ctx, struct expr *expr,
@@ -766,21 +903,53 @@ void exthdr_dependency_kill(struct payload_dep_ctx *ctx, struct expr *expr,
switch (expr->exthdr.op) {
case NFT_EXTHDR_OP_TCPOPT:
if (payload_dependency_exists(ctx, PROTO_BASE_TRANSPORT_HDR))
- payload_dependency_release(ctx);
+ payload_dependency_release(ctx, PROTO_BASE_TRANSPORT_HDR);
break;
case NFT_EXTHDR_OP_IPV6:
if (payload_dependency_exists(ctx, PROTO_BASE_NETWORK_HDR))
- payload_dependency_release(ctx);
+ payload_dependency_release(ctx, PROTO_BASE_NETWORK_HDR);
break;
case NFT_EXTHDR_OP_IPV4:
if (payload_dependency_exists(ctx, PROTO_BASE_NETWORK_HDR))
- payload_dependency_release(ctx);
+ payload_dependency_release(ctx, PROTO_BASE_NETWORK_HDR);
break;
default:
break;
}
}
+static const struct proto_desc *get_stacked_desc(const struct proto_ctx *ctx,
+ const struct proto_desc *top,
+ const struct expr *e,
+ unsigned int *skip)
+{
+ unsigned int i, total, payload_offset = e->payload.offset;
+
+ assert(e->etype == EXPR_PAYLOAD);
+
+ if (e->payload.base != PROTO_BASE_LL_HDR ||
+ payload_offset < top->length) {
+ *skip = 0;
+ return top;
+ }
+
+ for (i = 0, total = 0; i < ctx->stacked_ll_count; i++) {
+ const struct proto_desc *stacked;
+
+ stacked = ctx->stacked_ll[i];
+ if (payload_offset < stacked->length) {
+ *skip = total;
+ return stacked;
+ }
+
+ payload_offset -= stacked->length;
+ total += stacked->length;
+ }
+
+ *skip = total;
+ return top;
+}
+
/**
* payload_expr_complete - fill in type information of a raw payload expr
*
@@ -792,9 +961,10 @@ void exthdr_dependency_kill(struct payload_dep_ctx *ctx, struct expr *expr,
*/
void payload_expr_complete(struct expr *expr, const struct proto_ctx *ctx)
{
+ unsigned int payload_offset = expr->payload.offset;
const struct proto_desc *desc;
const struct proto_hdr_template *tmpl;
- unsigned int i;
+ unsigned int i, total;
assert(expr->etype == EXPR_PAYLOAD);
@@ -803,12 +973,18 @@ void payload_expr_complete(struct expr *expr, const struct proto_ctx *ctx)
return;
assert(desc->base == expr->payload.base);
+ desc = get_stacked_desc(ctx, desc, expr, &total);
+ payload_offset -= total;
+
for (i = 0; i < array_size(desc->templates); i++) {
tmpl = &desc->templates[i];
- if (tmpl->offset != expr->payload.offset ||
+ if (tmpl->offset != payload_offset ||
tmpl->len != expr->len)
continue;
+ if (tmpl->meta_key && i == 0)
+ continue;
+
if (tmpl->icmp_dep && ctx->th_dep.icmp.type &&
ctx->th_dep.icmp.type != icmp_dep_to_type(tmpl->icmp_dep))
continue;
@@ -859,6 +1035,7 @@ bool payload_expr_trim(struct expr *expr, struct expr *mask,
unsigned int payload_len = expr->len;
const struct proto_desc *desc;
unsigned int off, i, len = 0;
+ unsigned int total;
assert(expr->etype == EXPR_PAYLOAD);
@@ -868,10 +1045,8 @@ bool payload_expr_trim(struct expr *expr, struct expr *mask,
assert(desc->base == expr->payload.base);
- if (ctx->protocol[expr->payload.base].offset) {
- assert(payload_offset >= ctx->protocol[expr->payload.base].offset);
- payload_offset -= ctx->protocol[expr->payload.base].offset;
- }
+ desc = get_stacked_desc(ctx, desc, expr, &total);
+ payload_offset -= total;
off = round_up(mask->len, BITS_PER_BYTE) - mask_len;
payload_offset += off;
@@ -918,25 +1093,30 @@ bool payload_expr_trim(struct expr *expr, struct expr *mask,
void payload_expr_expand(struct list_head *list, struct expr *expr,
const struct proto_ctx *ctx)
{
+ unsigned int payload_offset = expr->payload.offset;
const struct proto_hdr_template *tmpl;
const struct proto_desc *desc;
+ unsigned int i, total;
struct expr *new;
- unsigned int i;
assert(expr->etype == EXPR_PAYLOAD);
desc = ctx->protocol[expr->payload.base].desc;
- if (desc == NULL)
+ if (desc == NULL || desc == &proto_unknown)
goto raw;
+
assert(desc->base == expr->payload.base);
+ desc = get_stacked_desc(ctx, desc, expr, &total);
+ payload_offset -= total;
+
for (i = 1; i < array_size(desc->templates); i++) {
tmpl = &desc->templates[i];
if (tmpl->len == 0)
break;
- if (tmpl->offset != expr->payload.offset)
+ if (tmpl->offset != payload_offset)
continue;
if (tmpl->icmp_dep && ctx->th_dep.icmp.type &&
@@ -948,6 +1128,7 @@ void payload_expr_expand(struct list_head *list, struct expr *expr,
list_add_tail(&new->list, list);
expr->len -= tmpl->len;
expr->payload.offset += tmpl->len;
+ payload_offset += tmpl->len;
if (expr->len == 0)
return;
} else if (expr->len > 0) {
@@ -960,8 +1141,12 @@ void payload_expr_expand(struct list_head *list, struct expr *expr,
}
raw:
new = payload_expr_alloc(&expr->location, NULL, 0);
- payload_init_raw(new, expr->payload.base, expr->payload.offset,
+ payload_init_raw(new, expr->payload.base, payload_offset,
expr->len);
+
+ if (expr->payload.inner_desc)
+ new->dtype = &integer_type;
+
list_add_tail(&new->list, list);
}
@@ -983,6 +1168,9 @@ bool payload_can_merge(const struct expr *e1, const struct expr *e2)
{
unsigned int total;
+ if (e1->payload.inner_desc != e2->payload.inner_desc)
+ return false;
+
if (!payload_is_adjacent(e1, e2))
return false;
@@ -1039,6 +1227,8 @@ struct expr *payload_expr_join(const struct expr *e1, const struct expr *e2)
expr->payload.base = e1->payload.base;
expr->payload.offset = e1->payload.offset;
expr->len = e1->len + e2->len;
+ expr->payload.inner_desc = e1->payload.inner_desc;
+
return expr;
}
@@ -1090,6 +1280,7 @@ __payload_gen_icmp_echo_dependency(struct eval_ctx *ctx, const struct expr *expr
int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr,
struct stmt **res)
{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
const struct proto_hdr_template *tmpl;
const struct proto_desc *desc;
struct stmt *stmt = NULL;
@@ -1106,11 +1297,11 @@ int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr,
break;
case PROTO_ICMP_ECHO:
/* do not test ICMP_ECHOREPLY here: its 0 */
- if (ctx->pctx.th_dep.icmp.type == ICMP_ECHO)
+ if (pctx->th_dep.icmp.type == ICMP_ECHO)
goto done;
type = ICMP_ECHO;
- if (ctx->pctx.th_dep.icmp.type)
+ if (pctx->th_dep.icmp.type)
goto bad_proto;
stmt = __payload_gen_icmp_echo_dependency(ctx, expr,
@@ -1121,21 +1312,21 @@ int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr,
case PROTO_ICMP_MTU:
case PROTO_ICMP_ADDRESS:
type = icmp_dep_to_type(tmpl->icmp_dep);
- if (ctx->pctx.th_dep.icmp.type == type)
+ if (pctx->th_dep.icmp.type == type)
goto done;
- if (ctx->pctx.th_dep.icmp.type)
+ if (pctx->th_dep.icmp.type)
goto bad_proto;
stmt = __payload_gen_icmp_simple_dependency(ctx, expr,
&icmp_type_type,
desc, type);
break;
case PROTO_ICMP6_ECHO:
- if (ctx->pctx.th_dep.icmp.type == ICMP6_ECHO_REQUEST ||
- ctx->pctx.th_dep.icmp.type == ICMP6_ECHO_REPLY)
+ if (pctx->th_dep.icmp.type == ICMP6_ECHO_REQUEST ||
+ pctx->th_dep.icmp.type == ICMP6_ECHO_REPLY)
goto done;
type = ICMP6_ECHO_REQUEST;
- if (ctx->pctx.th_dep.icmp.type)
+ if (pctx->th_dep.icmp.type)
goto bad_proto;
stmt = __payload_gen_icmp_echo_dependency(ctx, expr,
@@ -1148,9 +1339,9 @@ int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr,
case PROTO_ICMP6_MGMQ:
case PROTO_ICMP6_PPTR:
type = icmp_dep_to_type(tmpl->icmp_dep);
- if (ctx->pctx.th_dep.icmp.type == type)
+ if (pctx->th_dep.icmp.type == type)
goto done;
- if (ctx->pctx.th_dep.icmp.type)
+ if (pctx->th_dep.icmp.type)
goto bad_proto;
stmt = __payload_gen_icmp_simple_dependency(ctx, expr,
&icmp6_type_type,
@@ -1161,7 +1352,7 @@ int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr,
BUG("Unhandled icmp dependency code");
}
- ctx->pctx.th_dep.icmp.type = type;
+ pctx->th_dep.icmp.type = type;
if (stmt_evaluate(ctx, stmt) < 0)
return expr_error(ctx->msgs, expr,
@@ -1172,5 +1363,44 @@ done:
bad_proto:
return expr_error(ctx->msgs, expr, "incompatible icmp match: rule has %d, need %u",
- ctx->pctx.th_dep.icmp.type, type);
+ pctx->th_dep.icmp.type, type);
+}
+
+int payload_gen_inner_dependency(struct eval_ctx *ctx, const struct expr *expr,
+ struct stmt **res)
+{
+ struct proto_ctx *pctx = eval_proto_ctx(ctx);
+ const struct proto_hdr_template *tmpl;
+ const struct proto_desc *desc, *inner_desc;
+ struct expr *left, *right, *dep;
+ struct stmt *stmt = NULL;
+ int protocol;
+
+ assert(expr->etype == EXPR_PAYLOAD);
+
+ inner_desc = expr->payload.inner_desc;
+ desc = pctx->protocol[inner_desc->base - 1].desc;
+ if (desc == NULL)
+ desc = &proto_ip;
+
+ tmpl = &inner_desc->templates[0];
+ assert(tmpl);
+
+ protocol = proto_find_num(desc, inner_desc);
+ if (protocol < 0)
+ return expr_error(ctx->msgs, expr,
+ "conflicting protocols specified: %s vs. %s",
+ desc->name, inner_desc->name);
+
+ left = meta_expr_alloc(&expr->location, tmpl->meta_key);
+
+ right = constant_expr_alloc(&expr->location, tmpl->dtype,
+ tmpl->dtype->byteorder, tmpl->len,
+ constant_data_ptr(protocol, tmpl->len));
+
+ dep = relational_expr_alloc(&expr->location, OP_EQ, left, right);
+ stmt = expr_stmt_alloc(&dep->location, dep);
+
+ *res = stmt;
+ return 0;
}
diff --git a/src/print.c b/src/print.c
index d1b25e8b..4896e13c 100644
--- a/src/print.c
+++ b/src/print.c
@@ -2,9 +2,8 @@
* Copyright (c) 2017 Phil Sutter <phil@nwl.cc>
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
+ * it under the terms of the GNU General Public License version 2 (or any
+ * later) as published by the Free Software Foundation.
*/
#include <stdarg.h>
diff --git a/src/proto.c b/src/proto.c
index 2b61e0ba..edf99e84 100644
--- a/src/proto.c
+++ b/src/proto.c
@@ -28,6 +28,7 @@ const char *proto_base_names[] = {
[PROTO_BASE_LL_HDR] = "link layer",
[PROTO_BASE_NETWORK_HDR] = "network layer",
[PROTO_BASE_TRANSPORT_HDR] = "transport layer",
+ [PROTO_BASE_INNER_HDR] = "payload data",
};
const char *proto_base_tokens[] = {
@@ -35,6 +36,7 @@ const char *proto_base_tokens[] = {
[PROTO_BASE_LL_HDR] = "ll",
[PROTO_BASE_NETWORK_HDR] = "nh",
[PROTO_BASE_TRANSPORT_HDR] = "th",
+ [PROTO_BASE_INNER_HDR] = "ih",
};
const struct proto_hdr_template proto_unknown_template =
@@ -58,6 +60,8 @@ proto_find_upper(const struct proto_desc *base, unsigned int num)
unsigned int i;
for (i = 0; i < array_size(base->protocols); i++) {
+ if (!base->protocols[i].desc)
+ break;
if (base->protocols[i].num == num)
return base->protocols[i].desc;
}
@@ -76,12 +80,38 @@ int proto_find_num(const struct proto_desc *base,
unsigned int i;
for (i = 0; i < array_size(base->protocols); i++) {
+ if (!base->protocols[i].desc)
+ break;
if (base->protocols[i].desc == desc)
return base->protocols[i].num;
}
return -1;
}
+static const struct proto_desc *inner_protocols[] = {
+ &proto_vxlan,
+ &proto_geneve,
+ &proto_gre,
+ &proto_gretap,
+};
+
+const struct proto_desc *proto_find_inner(uint32_t type, uint32_t hdrsize,
+ uint32_t flags)
+{
+ const struct proto_desc *desc;
+ unsigned int i;
+
+ for (i = 0; i < array_size(inner_protocols); i++) {
+ desc = inner_protocols[i];
+ if (desc->inner.type == type &&
+ desc->inner.hdrsize == hdrsize &&
+ desc->inner.flags == flags)
+ return inner_protocols[i];
+ }
+
+ return &proto_unknown;
+}
+
static const struct dev_proto_desc dev_proto_desc[] = {
DEV_PROTO_DESC(ARPHRD_ETHER, &proto_eth),
};
@@ -104,6 +134,8 @@ int proto_dev_type(const struct proto_desc *desc, uint16_t *res)
return 0;
}
for (j = 0; j < array_size(base->protocols); j++) {
+ if (!base->protocols[j].desc)
+ break;
if (base->protocols[j].desc == desc) {
*res = dev_proto_desc[i].type;
return 0;
@@ -146,14 +178,20 @@ static void proto_ctx_debug(const struct proto_ctx *ctx, enum proto_bases base,
if (!(debug_mask & NFT_DEBUG_PROTO_CTX))
return;
- pr_debug("update %s protocol context:\n", proto_base_names[base]);
+ if (base == PROTO_BASE_LL_HDR && ctx->stacked_ll_count) {
+ pr_debug(" saved ll headers:");
+ for (i = 0; i < ctx->stacked_ll_count; i++)
+ pr_debug(" %s", ctx->stacked_ll[i]->name);
+ }
+
+ pr_debug("update %s protocol context%s:\n",
+ proto_base_names[base], ctx->inner ? " (inner)" : "");
+
for (i = PROTO_BASE_LL_HDR; i <= PROTO_BASE_MAX; i++) {
pr_debug(" %-20s: %s",
proto_base_names[i],
ctx->protocol[i].desc ? ctx->protocol[i].desc->name :
"none");
- if (ctx->protocol[i].offset)
- pr_debug(" (offset: %u)", ctx->protocol[i].offset);
if (i == base)
pr_debug(" <-");
pr_debug("\n");
@@ -169,7 +207,7 @@ static void proto_ctx_debug(const struct proto_ctx *ctx, enum proto_bases base,
* @debug_mask: display debugging information
*/
void proto_ctx_init(struct proto_ctx *ctx, unsigned int family,
- unsigned int debug_mask)
+ unsigned int debug_mask, bool inner)
{
const struct hook_proto_desc *h = &hook_proto_desc[family];
@@ -177,6 +215,7 @@ void proto_ctx_init(struct proto_ctx *ctx, unsigned int family,
ctx->family = family;
ctx->protocol[h->base].desc = h->desc;
ctx->debug_mask = debug_mask;
+ ctx->inner = inner;
proto_ctx_debug(ctx, h->base, debug_mask);
}
@@ -216,6 +255,8 @@ void proto_ctx_update(struct proto_ctx *ctx, enum proto_bases base,
ctx->protocol[base].protos[i].location = *loc;
}
break;
+ case PROTO_BASE_INNER_HDR:
+ break;
default:
BUG("unknown protocol base %d", base);
}
@@ -501,6 +542,10 @@ const struct proto_desc proto_udp = {
[UDPHDR_LENGTH] = UDPHDR_FIELD("length", len),
[UDPHDR_CHECKSUM] = UDPHDR_FIELD("checksum", check),
},
+ .protocols = {
+ PROTO_LINK(0, &proto_vxlan),
+ PROTO_LINK(0, &proto_geneve),
+ },
};
const struct proto_desc proto_udplite = {
@@ -676,7 +721,9 @@ static const struct symbol_table dscp_type_tbl = {
SYMBOL("cs5", 0x28),
SYMBOL("cs6", 0x30),
SYMBOL("cs7", 0x38),
+ SYMBOL("df", 0x00),
SYMBOL("be", 0x00),
+ SYMBOL("lephb", 0x01),
SYMBOL("af11", 0x0a),
SYMBOL("af12", 0x0c),
SYMBOL("af13", 0x0e),
@@ -689,6 +736,7 @@ static const struct symbol_table dscp_type_tbl = {
SYMBOL("af41", 0x22),
SYMBOL("af42", 0x24),
SYMBOL("af43", 0x26),
+ SYMBOL("va", 0x2c),
SYMBOL("ef", 0x2e),
SYMBOL_LIST_END
},
@@ -727,6 +775,42 @@ const struct datatype ecn_type = {
.sym_tbl = &ecn_type_tbl,
};
+#define GREHDR_TEMPLATE(__name, __dtype, __member) \
+ HDR_TEMPLATE(__name, __dtype, struct grehdr, __member)
+#define GREHDR_TYPE(__name, __member) \
+ GREHDR_TEMPLATE(__name, &ethertype_type, __member)
+
+const struct proto_desc proto_gre = {
+ .name = "gre",
+ .id = PROTO_DESC_GRE,
+ .base = PROTO_BASE_TRANSPORT_HDR,
+ .templates = {
+ [0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8),
+ [GREHDR_FLAGS] = HDR_BITFIELD("flags", &integer_type, 0, 5),
+ [GREHDR_VERSION] = HDR_BITFIELD("version", &integer_type, 13, 3),
+ [GREHDR_PROTOCOL] = HDR_BITFIELD("protocol", &ethertype_type, 16, 16),
+ },
+ .inner = {
+ .hdrsize = sizeof(struct grehdr),
+ .flags = NFT_INNER_NH | NFT_INNER_TH,
+ .type = NFT_INNER_GENEVE + 1,
+ },
+};
+
+const struct proto_desc proto_gretap = {
+ .name = "gretap",
+ .id = PROTO_DESC_GRETAP,
+ .base = PROTO_BASE_TRANSPORT_HDR,
+ .templates = {
+ [0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8),
+ },
+ .inner = {
+ .hdrsize = sizeof(struct grehdr),
+ .flags = NFT_INNER_LL | NFT_INNER_NH | NFT_INNER_TH,
+ .type = NFT_INNER_GENEVE + 2,
+ },
+};
+
#define IPHDR_FIELD(__name, __member) \
HDR_FIELD(__name, struct iphdr, __member)
#define IPHDR_ADDR(__name, __member) \
@@ -750,6 +834,8 @@ const struct proto_desc proto_ip = {
PROTO_LINK(IPPROTO_TCP, &proto_tcp),
PROTO_LINK(IPPROTO_DCCP, &proto_dccp),
PROTO_LINK(IPPROTO_SCTP, &proto_sctp),
+ PROTO_LINK(IPPROTO_GRE, &proto_gre),
+ PROTO_LINK(IPPROTO_GRE, &proto_gretap),
},
.templates = {
[0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8),
@@ -876,6 +962,8 @@ const struct proto_desc proto_ip6 = {
PROTO_LINK(IPPROTO_ICMP, &proto_icmp),
PROTO_LINK(IPPROTO_IGMP, &proto_igmp),
PROTO_LINK(IPPROTO_ICMPV6, &proto_icmp6),
+ PROTO_LINK(IPPROTO_GRE, &proto_gre),
+ PROTO_LINK(IPPROTO_GRE, &proto_gretap),
},
.templates = {
[0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8),
@@ -941,6 +1029,8 @@ const struct proto_desc proto_inet_service = {
PROTO_LINK(IPPROTO_ICMP, &proto_icmp),
PROTO_LINK(IPPROTO_IGMP, &proto_igmp),
PROTO_LINK(IPPROTO_ICMPV6, &proto_icmp6),
+ PROTO_LINK(IPPROTO_GRE, &proto_gre),
+ PROTO_LINK(IPPROTO_GRE, &proto_gretap),
},
.templates = {
[0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8),
@@ -1121,6 +1211,57 @@ const struct proto_desc proto_eth = {
};
/*
+ * VXLAN
+ */
+
+const struct proto_desc proto_vxlan = {
+ .name = "vxlan",
+ .id = PROTO_DESC_VXLAN,
+ .base = PROTO_BASE_INNER_HDR,
+ .templates = {
+ [VXLANHDR_FLAGS] = HDR_BITFIELD("flags", &bitmask_type, 0, 8),
+ [VXLANHDR_VNI] = HDR_BITFIELD("vni", &integer_type, (4 * BITS_PER_BYTE), 24),
+ },
+ .protocols = {
+ PROTO_LINK(__constant_htons(ETH_P_IP), &proto_ip),
+ PROTO_LINK(__constant_htons(ETH_P_ARP), &proto_arp),
+ PROTO_LINK(__constant_htons(ETH_P_IPV6), &proto_ip6),
+ PROTO_LINK(__constant_htons(ETH_P_8021Q), &proto_vlan),
+ },
+ .inner = {
+ .hdrsize = sizeof(struct vxlanhdr),
+ .flags = NFT_INNER_HDRSIZE | NFT_INNER_LL | NFT_INNER_NH | NFT_INNER_TH,
+ .type = NFT_INNER_VXLAN,
+ },
+};
+
+/*
+ * GENEVE
+ */
+
+const struct proto_desc proto_geneve = {
+ .name = "geneve",
+ .id = PROTO_DESC_GENEVE,
+ .base = PROTO_BASE_INNER_HDR,
+ .templates = {
+ [GNVHDR_TYPE] = HDR_TYPE("type", &ethertype_type, struct gnvhdr, type),
+ [GNVHDR_VNI] = HDR_BITFIELD("vni", &integer_type, (4 * BITS_PER_BYTE), 24),
+ },
+ .protocols = {
+ PROTO_LINK(__constant_htons(ETH_P_IP), &proto_ip),
+ PROTO_LINK(__constant_htons(ETH_P_ARP), &proto_arp),
+ PROTO_LINK(__constant_htons(ETH_P_IPV6), &proto_ip6),
+ PROTO_LINK(__constant_htons(ETH_P_8021Q), &proto_vlan),
+ },
+ .inner = {
+ .hdrsize = sizeof(struct gnvhdr),
+ .flags = NFT_INNER_HDRSIZE | NFT_INNER_LL | NFT_INNER_NH | NFT_INNER_TH,
+ .type = NFT_INNER_GENEVE,
+ },
+};
+
+
+/*
* Dummy protocol for netdev tables.
*/
const struct proto_desc proto_netdev = {
@@ -1156,6 +1297,9 @@ static const struct proto_desc *proto_definitions[PROTO_DESC_MAX + 1] = {
[PROTO_DESC_ARP] = &proto_arp,
[PROTO_DESC_VLAN] = &proto_vlan,
[PROTO_DESC_ETHER] = &proto_eth,
+ [PROTO_DESC_VXLAN] = &proto_vxlan,
+ [PROTO_DESC_GRE] = &proto_gre,
+ [PROTO_DESC_GRETAP] = &proto_gretap,
};
const struct proto_desc *proto_find_desc(enum proto_desc_id desc_id)
diff --git a/src/rbtree.c b/src/rbtree.c
deleted file mode 100644
index 325c0123..00000000
--- a/src/rbtree.c
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Red Black Trees
- * (C) 1999 Andrea Arcangeli <andrea@suse.de>
- * (C) 2002 David Woodhouse <dwmw2@infradead.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <rbtree.h>
-
-static void __rb_rotate_left(struct rb_node *node, struct rb_root *root)
-{
- struct rb_node *right = node->rb_right;
- struct rb_node *parent = rb_parent(node);
-
- if ((node->rb_right = right->rb_left))
- rb_set_parent(right->rb_left, node);
- right->rb_left = node;
-
- rb_set_parent(right, parent);
-
- if (parent)
- {
- if (node == parent->rb_left)
- parent->rb_left = right;
- else
- parent->rb_right = right;
- }
- else
- root->rb_node = right;
- rb_set_parent(node, right);
-}
-
-static void __rb_rotate_right(struct rb_node *node, struct rb_root *root)
-{
- struct rb_node *left = node->rb_left;
- struct rb_node *parent = rb_parent(node);
-
- if ((node->rb_left = left->rb_right))
- rb_set_parent(left->rb_right, node);
- left->rb_right = node;
-
- rb_set_parent(left, parent);
-
- if (parent)
- {
- if (node == parent->rb_right)
- parent->rb_right = left;
- else
- parent->rb_left = left;
- }
- else
- root->rb_node = left;
- rb_set_parent(node, left);
-}
-
-void rb_insert_color(struct rb_node *node, struct rb_root *root)
-{
- struct rb_node *parent, *gparent;
-
- while ((parent = rb_parent(node)) && rb_is_red(parent))
- {
- gparent = rb_parent(parent);
-
- if (parent == gparent->rb_left)
- {
- {
- register struct rb_node *uncle = gparent->rb_right;
- if (uncle && rb_is_red(uncle))
- {
- rb_set_black(uncle);
- rb_set_black(parent);
- rb_set_red(gparent);
- node = gparent;
- continue;
- }
- }
-
- if (parent->rb_right == node)
- {
- register struct rb_node *tmp;
- __rb_rotate_left(parent, root);
- tmp = parent;
- parent = node;
- node = tmp;
- }
-
- rb_set_black(parent);
- rb_set_red(gparent);
- __rb_rotate_right(gparent, root);
- } else {
- {
- register struct rb_node *uncle = gparent->rb_left;
- if (uncle && rb_is_red(uncle))
- {
- rb_set_black(uncle);
- rb_set_black(parent);
- rb_set_red(gparent);
- node = gparent;
- continue;
- }
- }
-
- if (parent->rb_left == node)
- {
- register struct rb_node *tmp;
- __rb_rotate_right(parent, root);
- tmp = parent;
- parent = node;
- node = tmp;
- }
-
- rb_set_black(parent);
- rb_set_red(gparent);
- __rb_rotate_left(gparent, root);
- }
- }
-
- rb_set_black(root->rb_node);
-}
-
-static void __rb_erase_color(struct rb_node *node, struct rb_node *parent,
- struct rb_root *root)
-{
- struct rb_node *other;
-
- while ((!node || rb_is_black(node)) && node != root->rb_node)
- {
- if (parent->rb_left == node)
- {
- other = parent->rb_right;
- if (rb_is_red(other))
- {
- rb_set_black(other);
- rb_set_red(parent);
- __rb_rotate_left(parent, root);
- other = parent->rb_right;
- }
- if ((!other->rb_left || rb_is_black(other->rb_left)) &&
- (!other->rb_right || rb_is_black(other->rb_right)))
- {
- rb_set_red(other);
- node = parent;
- parent = rb_parent(node);
- }
- else
- {
- if (!other->rb_right || rb_is_black(other->rb_right))
- {
- struct rb_node *o_left;
- if ((o_left = other->rb_left))
- rb_set_black(o_left);
- rb_set_red(other);
- __rb_rotate_right(other, root);
- other = parent->rb_right;
- }
- rb_set_color(other, rb_color(parent));
- rb_set_black(parent);
- if (other->rb_right)
- rb_set_black(other->rb_right);
- __rb_rotate_left(parent, root);
- node = root->rb_node;
- break;
- }
- }
- else
- {
- other = parent->rb_left;
- if (rb_is_red(other))
- {
- rb_set_black(other);
- rb_set_red(parent);
- __rb_rotate_right(parent, root);
- other = parent->rb_left;
- }
- if ((!other->rb_left || rb_is_black(other->rb_left)) &&
- (!other->rb_right || rb_is_black(other->rb_right)))
- {
- rb_set_red(other);
- node = parent;
- parent = rb_parent(node);
- }
- else
- {
- if (!other->rb_left || rb_is_black(other->rb_left))
- {
- register struct rb_node *o_right;
- if ((o_right = other->rb_right))
- rb_set_black(o_right);
- rb_set_red(other);
- __rb_rotate_left(other, root);
- other = parent->rb_left;
- }
- rb_set_color(other, rb_color(parent));
- rb_set_black(parent);
- if (other->rb_left)
- rb_set_black(other->rb_left);
- __rb_rotate_right(parent, root);
- node = root->rb_node;
- break;
- }
- }
- }
- if (node)
- rb_set_black(node);
-}
-
-void rb_erase(struct rb_node *node, struct rb_root *root)
-{
- struct rb_node *child, *parent;
- int color;
-
- if (!node->rb_left)
- child = node->rb_right;
- else if (!node->rb_right)
- child = node->rb_left;
- else
- {
- struct rb_node *old = node, *left;
-
- node = node->rb_right;
- while ((left = node->rb_left) != NULL)
- node = left;
- child = node->rb_right;
- parent = rb_parent(node);
- color = rb_color(node);
-
- if (child)
- rb_set_parent(child, parent);
- if (parent == old) {
- parent->rb_right = child;
- parent = node;
- } else
- parent->rb_left = child;
-
- node->rb_parent_color = old->rb_parent_color;
- node->rb_right = old->rb_right;
- node->rb_left = old->rb_left;
-
- if (rb_parent(old))
- {
- if (rb_parent(old)->rb_left == old)
- rb_parent(old)->rb_left = node;
- else
- rb_parent(old)->rb_right = node;
- } else
- root->rb_node = node;
-
- rb_set_parent(old->rb_left, node);
- if (old->rb_right)
- rb_set_parent(old->rb_right, node);
- goto color;
- }
-
- parent = rb_parent(node);
- color = rb_color(node);
-
- if (child)
- rb_set_parent(child, parent);
- if (parent)
- {
- if (parent->rb_left == node)
- parent->rb_left = child;
- else
- parent->rb_right = child;
- }
- else
- root->rb_node = child;
-
- color:
- if (color == RB_BLACK)
- __rb_erase_color(child, parent, root);
-}
-
-/*
- * This function returns the first node (in sort order) of the tree.
- */
-struct rb_node *rb_first(struct rb_root *root)
-{
- struct rb_node *n;
-
- n = root->rb_node;
- if (!n)
- return NULL;
- while (n->rb_left)
- n = n->rb_left;
- return n;
-}
-
-struct rb_node *rb_last(struct rb_root *root)
-{
- struct rb_node *n;
-
- n = root->rb_node;
- if (!n)
- return NULL;
- while (n->rb_right)
- n = n->rb_right;
- return n;
-}
-
-struct rb_node *rb_next(struct rb_node *node)
-{
- struct rb_node *parent;
-
- if (rb_parent(node) == node)
- return NULL;
-
- /* If we have a right-hand child, go down and then left as far
- as we can. */
- if (node->rb_right) {
- node = node->rb_right;
- while (node->rb_left)
- node=node->rb_left;
- return node;
- }
-
- /* No right-hand children. Everything down and left is
- smaller than us, so any 'next' node must be in the general
- direction of our parent. Go up the tree; any time the
- ancestor is a right-hand child of its parent, keep going
- up. First time it's a left-hand child of its parent, said
- parent is our 'next' node. */
- while ((parent = rb_parent(node)) && node == parent->rb_right)
- node = parent;
-
- return parent;
-}
-
-struct rb_node *rb_prev(struct rb_node *node)
-{
- struct rb_node *parent;
-
- if (rb_parent(node) == node)
- return NULL;
-
- /* If we have a left-hand child, go down and then right as far
- as we can. */
- if (node->rb_left) {
- node = node->rb_left;
- while (node->rb_right)
- node=node->rb_right;
- return node;
- }
-
- /* No left-hand children. Go up till we find an ancestor which
- is a right-hand child of its parent */
- while ((parent = rb_parent(node)) && node == parent->rb_left)
- node = parent;
-
- return parent;
-}
-
-void rb_replace_node(struct rb_node *victim, struct rb_node *new,
- struct rb_root *root)
-{
- struct rb_node *parent = rb_parent(victim);
-
- /* Set the surrounding nodes to point to the replacement */
- if (parent) {
- if (victim == parent->rb_left)
- parent->rb_left = new;
- else
- parent->rb_right = new;
- } else {
- root->rb_node = new;
- }
- if (victim->rb_left)
- rb_set_parent(victim->rb_left, new);
- if (victim->rb_right)
- rb_set_parent(victim->rb_right, new);
-
- /* Copy the pointers/colour from the victim to the replacement */
- *new = *victim;
-}
diff --git a/src/rule.c b/src/rule.c
index 6091067f..a58fd1f2 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -26,6 +26,7 @@
#include <json.h>
#include <cache.h>
#include <owner.h>
+#include <intervals.h>
#include <libnftnl/common.h>
#include <libnftnl/ruleset.h>
@@ -72,7 +73,7 @@ static uint32_t tcp_dflt_timeout[] = {
static uint32_t udp_dflt_timeout[] = {
[NFTNL_CTTIMEOUT_UDP_UNREPLIED] = 30,
- [NFTNL_CTTIMEOUT_UDP_REPLIED] = 180,
+ [NFTNL_CTTIMEOUT_UDP_REPLIED] = 120,
};
struct timeout_protocol timeout_protocol[IPPROTO_MAX] = {
@@ -366,21 +367,21 @@ static void set_print_declaration(const struct set *set,
nft_print(octx, "%s", opts->stmt_separator);
}
- if (!list_empty(&set->stmt_list))
+ if (!list_empty(&set->stmt_list)) {
+ unsigned int flags = octx->flags;
+
nft_print(octx, "%s%s", opts->tab, opts->tab);
- if (!list_empty(&set->stmt_list)) {
octx->flags |= NFT_CTX_OUTPUT_STATELESS;
list_for_each_entry(stmt, &set->stmt_list, list) {
stmt_print(stmt, octx);
if (!list_is_last(&stmt->list, &set->stmt_list))
nft_print(octx, " ");
}
- octx->flags &= ~NFT_CTX_OUTPUT_STATELESS;
- }
+ octx->flags = flags;
- if (!list_empty(&set->stmt_list))
nft_print(octx, "%s", opts->stmt_separator);
+ }
if (set->automerge)
nft_print(octx, "%s%sauto-merge%s", opts->tab, opts->tab,
@@ -675,6 +676,7 @@ static const char * const chain_hookname_str_array[] = {
"postrouting",
"output",
"ingress",
+ "egress",
NULL,
};
@@ -757,6 +759,9 @@ struct chain *chain_lookup_fuzzy(const struct handle *h,
struct table *table;
struct chain *chain;
+ if (!h->chain.name)
+ return NULL;
+
string_misspell_init(&st);
list_for_each_entry(table, &cache->table_cache.list, cache.list) {
@@ -832,6 +837,8 @@ const char *hooknum2str(unsigned int family, unsigned int hooknum)
switch (hooknum) {
case NF_NETDEV_INGRESS:
return "ingress";
+ case NF_NETDEV_EGRESS:
+ return "egress";
}
break;
default:
@@ -1051,13 +1058,19 @@ static void chain_print_declaration(const struct chain *chain,
void chain_rules_print(const struct chain *chain, struct output_ctx *octx,
const char *indent)
{
+ unsigned int flags = octx->flags;
struct rule *rule;
+ if (chain->flags & CHAIN_F_BINDING)
+ octx->flags &= ~NFT_CTX_OUTPUT_HANDLE;
+
list_for_each_entry(rule, &chain->rules, list) {
nft_print(octx, "\t\t%s", indent ? : "");
rule_print(rule, octx);
nft_print(octx, "\n");
}
+
+ octx->flags = flags;
}
static void chain_print(const struct chain *chain, struct output_ctx *octx)
@@ -1220,6 +1233,11 @@ static void table_print(const struct table *table, struct output_ctx *octx)
const char *delim = "";
const char *family = family2str(table->handle.family);
+ if (table->has_xt_stmts)
+ fprintf(octx->error_fp,
+ "# Warning: table %s %s is managed by iptables-nft, do not touch!\n",
+ family, table->handle.table.name);
+
nft_print(octx, "table %s %s {", family, table->handle.table.name);
if (nft_output_handle(octx) || table->flags & TABLE_F_OWNER)
nft_print(octx, " #");
@@ -1272,13 +1290,20 @@ struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
cmd->handle = *h;
cmd->location = *loc;
cmd->data = data;
+ cmd->attr = xzalloc_array(NFT_NLATTR_LOC_MAX,
+ sizeof(struct nlerr_loc));
+ cmd->attr_array_len = NFT_NLATTR_LOC_MAX;
+ init_list_head(&cmd->collapse_list);
+
return cmd;
}
void cmd_add_loc(struct cmd *cmd, uint16_t offset, const struct location *loc)
{
- if (cmd->num_attrs >= NFT_NLATTR_LOC_MAX)
- return;
+ 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;
@@ -1372,6 +1397,82 @@ void nft_cmd_expand(struct cmd *cmd)
}
}
+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);
+ }
+ }
+}
+
struct markup *markup_alloc(uint32_t format)
{
struct markup *markup;
@@ -1455,6 +1556,7 @@ void cmd_free(struct cmd *cmd)
BUG("invalid command object type %u\n", cmd->obj);
}
}
+ xfree(cmd->attr);
xfree(cmd->arg);
xfree(cmd);
}
@@ -1462,23 +1564,13 @@ void cmd_free(struct cmd *cmd)
#include <netlink.h>
#include <mnl.h>
-static int __do_add_elements(struct netlink_ctx *ctx, struct set *set,
- struct expr *expr, uint32_t flags)
+static int __do_add_elements(struct netlink_ctx *ctx, struct cmd *cmd,
+ struct set *set, struct expr *expr, uint32_t flags)
{
expr->set_flags |= set->flags;
- if (mnl_nft_setelem_add(ctx, set, expr, flags) < 0)
+ if (mnl_nft_setelem_add(ctx, cmd, set, expr, flags) < 0)
return -1;
- if (!set_is_anonymous(set->flags) &&
- set->init != NULL && set->init != expr &&
- set->flags & NFT_SET_INTERVAL &&
- set->desc.field_count <= 1) {
- interval_map_decompose(expr);
- list_splice_tail_init(&expr->expressions, &set->init->expressions);
- set->init->size += expr->size;
- expr->size = 0;
- }
-
return 0;
}
@@ -1489,12 +1581,10 @@ static int do_add_elements(struct netlink_ctx *ctx, struct cmd *cmd,
struct set *set = cmd->elem.set;
if (set_is_non_concat_range(set) &&
- set_to_intervals(ctx->msgs, set, init, true,
- ctx->nft->debug_mask, set->automerge,
- &ctx->nft->output) < 0)
+ set_to_intervals(set, init, true) < 0)
return -1;
- return __do_add_elements(ctx, set, init, flags);
+ return __do_add_elements(ctx, cmd, set, init, flags);
}
static int do_add_setelems(struct netlink_ctx *ctx, struct cmd *cmd,
@@ -1502,7 +1592,7 @@ static int do_add_setelems(struct netlink_ctx *ctx, struct cmd *cmd,
{
struct set *set = cmd->set;
- return __do_add_elements(ctx, set, set->init, flags);
+ return __do_add_elements(ctx, cmd, set, set->init, flags);
}
static int do_add_set(struct netlink_ctx *ctx, struct cmd *cmd,
@@ -1516,9 +1606,7 @@ static int do_add_set(struct netlink_ctx *ctx, struct cmd *cmd,
* comes too late which might result in spurious ENFILE errors.
*/
if (set_is_non_concat_range(set) &&
- set_to_intervals(ctx->msgs, set, set->init, true,
- ctx->nft->debug_mask, set->automerge,
- &ctx->nft->output) < 0)
+ set_to_intervals(set, set->init, true) < 0)
return -1;
}
@@ -1595,12 +1683,10 @@ static int do_delete_setelems(struct netlink_ctx *ctx, struct cmd *cmd)
struct set *set = cmd->elem.set;
if (set_is_non_concat_range(set) &&
- set_to_intervals(ctx->msgs, set, expr, false,
- ctx->nft->debug_mask, set->automerge,
- &ctx->nft->output) < 0)
+ set_to_intervals(set, expr, false) < 0)
return -1;
- if (mnl_nft_setelem_del(ctx, cmd) < 0)
+ if (mnl_nft_setelem_del(ctx, cmd, &cmd->handle, cmd->elem.expr) < 0)
return -1;
return 0;
@@ -1811,13 +1897,12 @@ static void obj_print_data(const struct obj *obj,
nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id);
obj_print_comment(obj, opts, octx);
- nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab);
- if (nft_output_stateless(octx)) {
- nft_print(octx, "packets 0 bytes 0");
- break;
- }
- nft_print(octx, "packets %" PRIu64 " bytes %" PRIu64 "%s",
- obj->counter.packets, obj->counter.bytes, opts->nl);
+ if (nft_output_stateless(octx))
+ nft_print(octx, "%s", opts->nl);
+ else
+ nft_print(octx, "%s%s%spackets %" PRIu64 " bytes %" PRIu64 "%s",
+ opts->nl, opts->tab, opts->tab,
+ obj->counter.packets, obj->counter.bytes, opts->nl);
break;
case NFT_OBJECT_QUOTA: {
const char *data_unit;
@@ -2307,9 +2392,14 @@ static int do_list_tables(struct netlink_ctx *ctx, struct cmd *cmd)
static void table_print_declaration(struct table *table,
struct output_ctx *octx)
{
- nft_print(octx, "table %s %s {\n",
- family2str(table->handle.family),
- table->handle.table.name);
+ const char *family = family2str(table->handle.family);
+
+ if (table->has_xt_stmts)
+ fprintf(octx->error_fp,
+ "# Warning: table %s %s is managed by iptables-nft, do not touch!\n",
+ family, table->handle.table.name);
+
+ nft_print(octx, "table %s %s {\n", family, table->handle.table.name);
}
static int do_list_chain(struct netlink_ctx *ctx, struct cmd *cmd,
@@ -2319,13 +2409,9 @@ static int do_list_chain(struct netlink_ctx *ctx, struct cmd *cmd,
table_print_declaration(table, &ctx->nft->output);
- list_for_each_entry(chain, &table->chain_cache.list, cache.list) {
- if (chain->handle.family != cmd->handle.family ||
- strcmp(cmd->handle.chain.name, chain->handle.chain.name) != 0)
- continue;
-
+ chain = chain_cache_find(table, cmd->handle.chain.name);
+ if (chain)
chain_print(chain, &ctx->nft->output);
- }
nft_print(&ctx->nft->output, "}\n");
@@ -2418,6 +2504,8 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_SET:
return do_list_set(ctx, cmd, table);
case CMD_OBJ_RULESET:
+ case CMD_OBJ_RULES:
+ case CMD_OBJ_RULE:
return do_list_ruleset(ctx, cmd);
case CMD_OBJ_METERS:
return do_list_sets(ctx, cmd);
@@ -2525,6 +2613,14 @@ static int do_command_reset(struct netlink_ctx *ctx, struct cmd *cmd)
case CMD_OBJ_QUOTA:
type = NFT_OBJECT_QUOTA;
break;
+ case CMD_OBJ_RULES:
+ ret = netlink_reset_rules(ctx, cmd, true);
+ if (ret < 0)
+ return ret;
+
+ return do_command_list(ctx, cmd);
+ case CMD_OBJ_RULE:
+ return netlink_reset_rules(ctx, cmd, false);
default:
BUG("invalid command object type %u\n", cmd->obj);
}
@@ -2642,6 +2738,7 @@ int do_command(struct netlink_ctx *ctx, struct cmd *cmd)
case CMD_REPLACE:
return do_command_replace(ctx, cmd);
case CMD_DELETE:
+ case CMD_DESTROY:
return do_command_delete(ctx, cmd);
case CMD_GET:
return do_command_get(ctx, cmd);
@@ -2816,7 +2913,8 @@ static void stmt_reduce(const struct rule *rule)
switch (stmt->expr->op) {
case OP_EQ:
case OP_IMPLICIT:
- if (stmt->expr->left->meta.key == NFT_META_PROTOCOL) {
+ if (stmt->expr->left->meta.key == NFT_META_PROTOCOL &&
+ !stmt->expr->left->meta.inner_desc) {
uint16_t protocol;
protocol = mpz_get_uint16(stmt->expr->right->value);
diff --git a/src/scanner.l b/src/scanner.l
index 6cc7778d..c0c49b97 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -118,12 +118,12 @@ digit [0-9]
hexdigit [0-9a-fA-F]
decstring {digit}+
hexstring 0[xX]{hexdigit}+
-numberstring ({decstring}|{hexstring})
letter [a-zA-Z]
string ({letter}|[_.])({letter}|{digit}|[/\-_\.])*
quotedstring \"[^"]*\"
asteriskstring ({string}\*|{string}\\\*|\\\*|{string}\\\*{string})
comment #.*$
+comment_line ^[ \t]*#.*\n
slash \/
timestring ([0-9]+d)?([0-9]+h)?([0-9]+m)?([0-9]+s)?([0-9]+ms)?
@@ -197,27 +197,58 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
%option warn
%option stack
%s SCANSTATE_ARP
+%s SCANSTATE_AT
%s SCANSTATE_CT
%s SCANSTATE_COUNTER
%s SCANSTATE_ETH
+%s SCANSTATE_GRE
+%s SCANSTATE_ICMP
+%s SCANSTATE_IGMP
%s SCANSTATE_IP
%s SCANSTATE_IP6
%s SCANSTATE_LIMIT
+%s SCANSTATE_META
+%s SCANSTATE_POLICY
%s SCANSTATE_QUOTA
%s SCANSTATE_SCTP
%s SCANSTATE_SECMARK
+%s SCANSTATE_TCP
+%s SCANSTATE_TYPE
%s SCANSTATE_VLAN
+%s SCANSTATE_XT
+%s SCANSTATE_CMD_EXPORT
+%s SCANSTATE_CMD_IMPORT
%s SCANSTATE_CMD_LIST
+%s SCANSTATE_CMD_MONITOR
+%s SCANSTATE_CMD_RESET
+%s SCANSTATE_EXPR_AH
+%s SCANSTATE_EXPR_COMP
+%s SCANSTATE_EXPR_DCCP
+%s SCANSTATE_EXPR_DST
+%s SCANSTATE_EXPR_ESP
%s SCANSTATE_EXPR_FIB
+%s SCANSTATE_EXPR_FRAG
%s SCANSTATE_EXPR_HASH
+%s SCANSTATE_EXPR_HBH
%s SCANSTATE_EXPR_IPSEC
+%s SCANSTATE_EXPR_MH
%s SCANSTATE_EXPR_NUMGEN
+%s SCANSTATE_EXPR_OSF
%s SCANSTATE_EXPR_QUEUE
%s SCANSTATE_EXPR_RT
%s SCANSTATE_EXPR_SCTP_CHUNK
%s SCANSTATE_EXPR_SOCKET
+%s SCANSTATE_EXPR_TH
+%s SCANSTATE_EXPR_UDP
+%s SCANSTATE_EXPR_UDPLITE
+%s SCANSTATE_STMT_DUP
+%s SCANSTATE_STMT_FWD
%s SCANSTATE_STMT_LOG
+%s SCANSTATE_STMT_NAT
+%s SCANSTATE_STMT_REJECT
+%s SCANSTATE_STMT_SYNPROXY
+%s SCANSTATE_STMT_TPROXY
%%
@@ -258,7 +289,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"/" { return SLASH; }
"-" { return DASH; }
"*" { return ASTERISK; }
-"@" { return AT; }
+"@" { scanner_push_start_cond(yyscanner, SCANSTATE_AT); return AT; }
"$" { return '$'; }
"=" { return '='; }
"vmap" { return VMAP; }
@@ -272,24 +303,27 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"describe" { return DESCRIBE; }
+<SCANSTATE_CMD_LIST,SCANSTATE_CMD_MONITOR>{
+ "chains" { return CHAINS; }
+ "sets" { return SETS; }
+ "tables" { return TABLES; }
+}
+<SCANSTATE_CMD_MONITOR>{
+ "rules" { return RULES; }
+ "trace" { return TRACE; }
+}
"hook" { return HOOK; }
"device" { return DEVICE; }
"devices" { return DEVICES; }
"table" { return TABLE; }
-"tables" { return TABLES; }
"chain" { return CHAIN; }
-"chains" { return CHAINS; }
"rule" { return RULE; }
-"rules" { return RULES; }
-"sets" { return SETS; }
"set" { return SET; }
"element" { return ELEMENT; }
"map" { return MAP; }
-"maps" { return MAPS; }
"flowtable" { return FLOWTABLE; }
"handle" { return HANDLE; }
"ruleset" { return RULESET; }
-"trace" { return TRACE; }
"socket" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_SOCKET); return SOCKET; }
<SCANSTATE_EXPR_SOCKET>{
@@ -298,7 +332,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"cgroupv2" { return CGROUPV2; }
"level" { return LEVEL; }
}
-"tproxy" { return TPROXY; }
+"tproxy" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_TPROXY); return TPROXY; }
"accept" { return ACCEPT; }
"drop" { return DROP; }
@@ -306,7 +340,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"jump" { return JUMP; }
"goto" { return GOTO; }
"return" { return RETURN; }
-"to" { return TO; }
+<SCANSTATE_EXPR_QUEUE,SCANSTATE_STMT_DUP,SCANSTATE_STMT_FWD,SCANSTATE_STMT_NAT,SCANSTATE_STMT_TPROXY,SCANSTATE_IP,SCANSTATE_IP6>"to" { return TO; } /* XXX: SCANSTATE_IP is a workaround */
"inet" { return INET; }
"netdev" { return NETDEV; }
@@ -319,12 +353,13 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"delete" { return DELETE; }
"get" { return GET; }
"list" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_LIST); return LIST; }
-"reset" { return RESET; }
+"reset" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_RESET); return RESET; }
"flush" { return FLUSH; }
"rename" { return RENAME; }
-"import" { return IMPORT; }
-"export" { return EXPORT; }
-"monitor" { return MONITOR; }
+"import" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_IMPORT); return IMPORT; }
+"export" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_EXPORT); return EXPORT; }
+"monitor" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_MONITOR); return MONITOR; }
+"destroy" { return DESTROY; }
"position" { return POSITION; }
"index" { return INDEX; }
@@ -339,10 +374,12 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"elements" { return ELEMENTS; }
"expires" { return EXPIRES; }
-"policy" { return POLICY; }
+"policy" { scanner_push_start_cond(yyscanner, SCANSTATE_POLICY); return POLICY; }
"size" { return SIZE; }
-"performance" { return PERFORMANCE; }
-"memory" { return MEMORY; }
+<SCANSTATE_POLICY>{
+ "performance" { return PERFORMANCE; }
+ "memory" { return MEMORY; }
+}
"flow" { return FLOW; }
"offload" { return OFFLOAD; }
@@ -352,26 +389,30 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"meters" { return METERS; }
"flowtables" { return FLOWTABLES; }
"limits" { return LIMITS; }
+ "maps" { return MAPS; }
"secmarks" { return SECMARKS; }
"synproxys" { return SYNPROXYS; }
"hooks" { return HOOKS; }
}
"counter" { scanner_push_start_cond(yyscanner, SCANSTATE_COUNTER); return COUNTER; }
-"name" { return NAME; }
+<SCANSTATE_COUNTER,SCANSTATE_LIMIT,SCANSTATE_QUOTA,SCANSTATE_STMT_SYNPROXY,SCANSTATE_EXPR_OSF>"name" { return NAME; }
<SCANSTATE_COUNTER,SCANSTATE_CT,SCANSTATE_LIMIT>"packets" { return PACKETS; }
<SCANSTATE_COUNTER,SCANSTATE_CT,SCANSTATE_LIMIT,SCANSTATE_QUOTA>"bytes" { return BYTES; }
-"counters" { return COUNTERS; }
-"quotas" { return QUOTAS; }
+<SCANSTATE_CMD_LIST,SCANSTATE_CMD_RESET>{
+ "counters" { return COUNTERS; }
+ "quotas" { return QUOTAS; }
+ "rules" { return RULES; }
+}
"log" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_LOG); return LOG; }
-"prefix" { return PREFIX; }
-"group" { return GROUP; }
+<SCANSTATE_STMT_LOG,SCANSTATE_STMT_NAT,SCANSTATE_IP,SCANSTATE_IP6>"prefix" { return PREFIX; }
<SCANSTATE_STMT_LOG>{
"snaplen" { return SNAPLEN; }
"queue-threshold" { return QUEUE_THRESHOLD; }
"level" { return LEVEL; }
+ "group" { return GROUP; }
}
"queue" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_QUEUE); return QUEUE;}
@@ -384,6 +425,11 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
<SCANSTATE_LIMIT>{
"rate" { return RATE; }
"burst" { return BURST; }
+
+ /* time_unit */
+ "second" { return SECOND; }
+ "minute" { return MINUTE; }
+ "week" { return WEEK; }
}
<SCANSTATE_CT,SCANSTATE_LIMIT,SCANSTATE_QUOTA>"over" { return OVER; }
@@ -393,27 +439,31 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"until" { return UNTIL; }
}
-"second" { return SECOND; }
-"minute" { return MINUTE; }
"hour" { return HOUR; }
"day" { return DAY; }
-"week" { return WEEK; }
-"reject" { return _REJECT; }
-"with" { return WITH; }
-"icmpx" { return ICMPX; }
+"reject" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_REJECT); return _REJECT; }
+<SCANSTATE_STMT_REJECT>{
+ "with" { return WITH; }
+ "icmpx" { return ICMPX; }
+}
-"snat" { return SNAT; }
-"dnat" { return DNAT; }
-"masquerade" { return MASQUERADE; }
-"redirect" { return REDIRECT; }
+"snat" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return SNAT; }
+"dnat" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return DNAT; }
+"masquerade" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return MASQUERADE; }
+"redirect" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return REDIRECT; }
"random" { return RANDOM; }
-"fully-random" { return FULLY_RANDOM; }
-"persistent" { return PERSISTENT; }
+<SCANSTATE_STMT_NAT>{
+ "fully-random" { return FULLY_RANDOM; }
+ "persistent" { return PERSISTENT; }
+ "port" { return PORT; }
+}
-"ll" { return LL_HDR; }
-"nh" { return NETWORK_HDR; }
-"th" { return TRANSPORT_HDR; }
+<SCANSTATE_AT>{
+ "ll" { return LL_HDR; }
+ "nh" { return NETWORK_HDR; }
+}
+"th" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_TH); return TRANSPORT_HDR; }
"bridge" { return BRIDGE; }
@@ -422,11 +472,11 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"saddr" { return SADDR; }
"daddr" { return DADDR; }
}
-"type" { return TYPE; }
+"type" { scanner_push_start_cond(yyscanner, SCANSTATE_TYPE); return TYPE; }
"typeof" { return TYPEOF; }
"vlan" { scanner_push_start_cond(yyscanner, SCANSTATE_VLAN); return VLAN; }
-"id" { return ID; }
+<SCANSTATE_CT,SCANSTATE_EXPR_FRAG,SCANSTATE_VLAN,SCANSTATE_IP,SCANSTATE_ICMP>"id" { return ID; }
<SCANSTATE_VLAN>{
"cfi" { return CFI; }
"dei" { return DEI; }
@@ -445,15 +495,27 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
}
"ip" { scanner_push_start_cond(yyscanner, SCANSTATE_IP); return IP; }
-"version" { return HDRVERSION; }
-"hdrlength" { return HDRLENGTH; }
-"dscp" { return DSCP; }
+<SCANSTATE_IP,SCANSTATE_IP6,SCANSTATE_EXPR_OSF,SCANSTATE_GRE>{
+ "version" { return HDRVERSION; }
+}
+<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_DST,SCANSTATE_EXPR_HBH,SCANSTATE_EXPR_MH,SCANSTATE_EXPR_RT,SCANSTATE_IP>{
+ "hdrlength" { return HDRLENGTH; }
+}
+<SCANSTATE_IP,SCANSTATE_IP6,SCANSTATE_TYPE>{
+ "dscp" { return DSCP; }
+}
"ecn" { return ECN; }
-"length" { return LENGTH; }
-"frag-off" { return FRAG_OFF; }
-"ttl" { return TTL; }
-"protocol" { return PROTOCOL; }
-"checksum" { return CHECKSUM; }
+<SCANSTATE_EXPR_UDP,SCANSTATE_IP,SCANSTATE_IP6,SCANSTATE_META,SCANSTATE_TCP,SCANSTATE_SCTP,SCANSTATE_EXPR_SCTP_CHUNK>"length" { return LENGTH; }
+<SCANSTATE_EXPR_FRAG,SCANSTATE_IP>{
+ "frag-off" { return FRAG_OFF; }
+}
+<SCANSTATE_EXPR_OSF,SCANSTATE_IP>{
+ "ttl" { return TTL; }
+}
+<SCANSTATE_CT,SCANSTATE_IP,SCANSTATE_META,SCANSTATE_TYPE,SCANSTATE_GRE>"protocol" { return PROTOCOL; }
+<SCANSTATE_EXPR_MH,SCANSTATE_EXPR_UDP,SCANSTATE_EXPR_UDPLITE,SCANSTATE_ICMP,SCANSTATE_IGMP,SCANSTATE_IP,SCANSTATE_SCTP,SCANSTATE_TCP>{
+ "checksum" { return CHECKSUM; }
+}
<SCANSTATE_IP>{
"lsrr" { return LSRR; }
@@ -463,39 +525,68 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"ptr" { return PTR; }
"value" { return VALUE; }
+
+ "option" { return OPTION; }
+ "options" { return OPTIONS; }
}
-"echo" { return ECHO; }
-"eol" { return EOL; }
-"maxseg" { return MSS; }
-"mss" { return MSS; }
-"nop" { return NOP; }
-"noop" { return NOP; }
-"sack" { return SACK; }
-"sack0" { return SACK0; }
-"sack1" { return SACK1; }
-"sack2" { return SACK2; }
-"sack3" { return SACK3; }
-"sack-permitted" { return SACK_PERM; }
-"sack-perm" { return SACK_PERM; }
-"timestamp" { return TIMESTAMP; }
+<SCANSTATE_TCP>{
+ /* tcp header fields */
+ "ackseq" { return ACKSEQ; }
+ "doff" { return DOFF; }
+ "window" { return WINDOW; }
+ "urgptr" { return URGPTR; }
+
+ /* tcp option types */
+ "echo" { return ECHO; }
+ "eol" { return EOL; }
+ "maxseg" { return MSS; }
+ "mss" { return MSS; }
+ "nop" { return NOP; }
+ "noop" { return NOP; }
+ "sack" { return SACK; }
+ "sack0" { return SACK0; }
+ "sack1" { return SACK1; }
+ "sack2" { return SACK2; }
+ "sack3" { return SACK3; }
+ "sack-permitted" { return SACK_PERM; }
+ "sack-perm" { return SACK_PERM; }
+ "timestamp" { return TIMESTAMP; }
+ "fastopen" { return FASTOPEN; }
+ "mptcp" { return MPTCP; }
+ "md5sig" { return MD5SIG; }
+
+ /* tcp option fields */
+ "left" { return LEFT; }
+ "right" { return RIGHT; }
+ "count" { return COUNT; }
+ "tsval" { return TSVAL; }
+ "tsecr" { return TSECR; }
+ "subtype" { return SUBTYPE; }
+
+ "options" { return OPTIONS; }
+ "option" { return OPTION; }
+}
"time" { return TIME; }
-"kind" { return KIND; }
-"count" { return COUNT; }
-"left" { return LEFT; }
-"right" { return RIGHT; }
-"tsval" { return TSVAL; }
-"tsecr" { return TSECR; }
-
-"icmp" { return ICMP; }
-"code" { return CODE; }
-"sequence" { return SEQUENCE; }
-"gateway" { return GATEWAY; }
-"mtu" { return MTU; }
+"icmp" { scanner_push_start_cond(yyscanner, SCANSTATE_ICMP); return ICMP; }
+"icmpv6" { scanner_push_start_cond(yyscanner, SCANSTATE_ICMP); return ICMP6; }
+<SCANSTATE_ICMP>{
+ "gateway" { return GATEWAY; }
+ "code" { return CODE; }
+ "param-problem" { return PPTR; }
+ "max-delay" { return MAXDELAY; }
+ "mtu" { return MTU; }
+}
+<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_ESP,SCANSTATE_ICMP,SCANSTATE_TCP>{
+ "sequence" { return SEQUENCE; }
+}
-"igmp" { return IGMP; }
-"mrt" { return MRT; }
+"igmp" { scanner_push_start_cond(yyscanner, SCANSTATE_IGMP); return IGMP; }
+<SCANSTATE_IGMP>{
+ "mrt" { return MRT; }
+ "group" { return GROUP; }
+}
"ip6" { scanner_push_start_cond(yyscanner, SCANSTATE_IP6); return IP6; }
"priority" { return PRIORITY; }
@@ -503,36 +594,47 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"flowlabel" { return FLOWLABEL; }
"hoplimit" { return HOPLIMIT; }
}
-"nexthdr" { return NEXTHDR; }
-
-"icmpv6" { return ICMP6; }
-"param-problem" { return PPTR; }
-"max-delay" { return MAXDELAY; }
+<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_COMP,SCANSTATE_EXPR_DST,SCANSTATE_EXPR_FRAG,SCANSTATE_EXPR_HBH,SCANSTATE_EXPR_MH,SCANSTATE_EXPR_RT,SCANSTATE_IP6>{
+ "nexthdr" { return NEXTHDR; }
+}
-"ah" { return AH; }
-"reserved" { return RESERVED; }
-"spi" { return SPI; }
+"ah" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_AH); return AH; }
+<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_FRAG,SCANSTATE_EXPR_MH,SCANSTATE_TCP>{
+ "reserved" { return RESERVED; }
+}
+<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_ESP,SCANSTATE_EXPR_IPSEC>"spi" { return SPI; }
-"esp" { return ESP; }
+"esp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_ESP); return ESP; }
-"comp" { return COMP; }
+"comp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_COMP); return COMP; }
+<SCANSTATE_EXPR_COMP>{
+ "cpi" { return CPI; }
+}
"flags" { return FLAGS; }
-"cpi" { return CPI; }
-"udp" { return UDP; }
-"udplite" { return UDPLITE; }
-"sport" { return SPORT; }
-"dport" { return DPORT; }
-"port" { return PORT; }
+"udp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_UDP); return UDP; }
+"udplite" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_UDPLITE); return UDPLITE; }
+<SCANSTATE_EXPR_UDPLITE>{
+ "csumcov" { return CSUMCOV; }
+}
+<SCANSTATE_EXPR_DCCP,SCANSTATE_SCTP,SCANSTATE_TCP,SCANSTATE_EXPR_TH,SCANSTATE_EXPR_UDP,SCANSTATE_EXPR_UDPLITE>{
+ "sport" { return SPORT; }
+}
+<SCANSTATE_CT,SCANSTATE_EXPR_DCCP,SCANSTATE_SCTP,SCANSTATE_TCP,SCANSTATE_EXPR_TH,SCANSTATE_EXPR_UDP,SCANSTATE_EXPR_UDPLITE>{
+ "dport" { return DPORT; }
+}
+
+"vxlan" { return VXLAN; }
+"vni" { return VNI; }
+
+"geneve" { return GENEVE; }
-"tcp" { return TCP; }
-"ackseq" { return ACKSEQ; }
-"doff" { return DOFF; }
-"window" { return WINDOW; }
-"urgptr" { return URGPTR; }
-"option" { return OPTION; }
+"gre" { scanner_push_start_cond(yyscanner, SCANSTATE_GRE); return GRE; }
+"gretap" { scanner_push_start_cond(yyscanner, SCANSTATE_GRE); return GRETAP; }
-"dccp" { return DCCP; }
+"tcp" { scanner_push_start_cond(yyscanner, SCANSTATE_TCP); return TCP; }
+
+"dccp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_DCCP); return DCCP; }
"sctp" { scanner_push_start_cond(yyscanner, SCANSTATE_SCTP); return SCTP; }
@@ -561,6 +663,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"asconf" { return ASCONF; }
"tsn" { return TSN; }
+ "sack" { return SACK; }
"stream" { return STREAM; }
"ssn" { return SSN; }
"ppid" { return PPID; }
@@ -578,26 +681,24 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
}
"rt" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT; }
-"rt0" { return RT0; }
-"rt2" { return RT2; }
-"srh" { return RT4; }
-"seg-left" { return SEG_LEFT; }
-"addr" { return ADDR; }
-"last-entry" { return LAST_ENT; }
-"tag" { return TAG; }
-"sid" { return SID; }
+"rt0" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT0; }
+"rt2" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT2; }
+"srh" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT4; }
+<SCANSTATE_EXPR_RT,SCANSTATE_STMT_NAT,SCANSTATE_IP,SCANSTATE_IP6>"addr" { return ADDR; }
-"hbh" { return HBH; }
+"hbh" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_HBH); return HBH; }
-"frag" { return FRAG; }
-"reserved2" { return RESERVED2; }
-"more-fragments" { return MORE_FRAGMENTS; }
+"frag" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_FRAG); return FRAG; }
+<SCANSTATE_EXPR_FRAG>{
+ "reserved2" { return RESERVED2; }
+ "more-fragments" { return MORE_FRAGMENTS; }
+}
-"dst" { return DST; }
+"dst" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_DST); return DST; }
-"mh" { return MH; }
+"mh" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_MH); return MH; }
-"meta" { return META; }
+"meta" { scanner_push_start_cond(yyscanner, SCANSTATE_META); return META; }
"mark" { return MARK; }
"iif" { return IIF; }
"iifname" { return IIFNAME; }
@@ -620,8 +721,15 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"cgroup" { return CGROUP; }
<SCANSTATE_EXPR_RT>{
- "classid" { return CLASSID; }
"nexthop" { return NEXTHOP; }
+ "seg-left" { return SEG_LEFT; }
+ "mtu" { return MTU; }
+ "last-entry" { return LAST_ENT; }
+ "tag" { return TAG; }
+ "sid" { return SID; }
+}
+<SCANSTATE_EXPR_RT,SCANSTATE_TYPE>{
+ "classid" { return CLASSID; }
}
"ct" { scanner_push_start_cond(yyscanner, SCANSTATE_CT); return CT; }
@@ -642,6 +750,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"label" { return LABEL; }
"state" { return STATE; }
"status" { return STATUS; }
+ "count" { return COUNT; }
}
"numgen" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_NUMGEN); return NUMGEN; }
@@ -659,24 +768,32 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"mod" { return MOD; }
"offset" { return OFFSET; }
}
-"dup" { return DUP; }
-"fwd" { return FWD; }
+"dup" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_DUP); return DUP; }
+"fwd" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_FWD); return FWD; }
"fib" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_FIB); return FIB; }
-"osf" { return OSF; }
+"osf" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_OSF); return OSF; }
-"synproxy" { return SYNPROXY; }
-"wscale" { return WSCALE; }
+"synproxy" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_SYNPROXY); return SYNPROXY; }
+<SCANSTATE_STMT_SYNPROXY>{
+ "wscale" { return WSCALE; }
+ "maxseg" { return MSS; }
+ "mss" { return MSS; }
+ "timestamp" { return TIMESTAMP; }
+ "sack-permitted" { return SACK_PERM; }
+ "sack-perm" { return SACK_PERM; }
+}
"notrack" { return NOTRACK; }
-"options" { return OPTIONS; }
"all" { return ALL; }
-"xml" { return XML; }
-"json" { return JSON; }
-"vm" { return VM; }
+<SCANSTATE_CMD_EXPORT,SCANSTATE_CMD_IMPORT,SCANSTATE_CMD_MONITOR>{
+ "xml" { return XML; }
+ "json" { return JSON; }
+ "vm" { return VM; }
+}
"exists" { return EXISTS; }
"missing" { return MISSING; }
@@ -694,6 +811,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
"secmark" { scanner_push_start_cond(yyscanner, SCANSTATE_SECMARK); return SECMARK; }
+"xt" { scanner_push_start_cond(yyscanner, SCANSTATE_XT); return XT; }
+
{addrstring} {
yylval->string = xstrdup(yytext);
return STRING;
@@ -710,9 +829,9 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
return STRING;
}
-{numberstring} {
+{hexstring} {
errno = 0;
- yylval->val = strtoull(yytext, NULL, 0);
+ yylval->val = strtoull(yytext, NULL, 16);
if (errno != 0) {
yylval->string = xstrdup(yytext);
return STRING;
@@ -720,6 +839,19 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
return NUM;
}
+{decstring} {
+ int base = yytext[0] == '0' ? 8 : 10;
+ char *end;
+
+ errno = 0;
+ yylval->val = strtoull(yytext, &end, base);
+ if (errno != 0 || *end) {
+ yylval->string = xstrdup(yytext);
+ return STRING;
+ }
+ return NUM;
+ }
+
{classid}/[ \t\n:\-},] {
yylval->string = xstrdup(yytext);
return STRING;
@@ -752,6 +884,9 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr})
{tab}+
{space}+
+{comment_line} {
+ reset_pos(yyget_extra(yyscanner), yylloc);
+ }
{comment}
<<EOF>> {
@@ -1024,7 +1159,7 @@ void scanner_push_buffer(void *scanner, const struct input_descriptor *indesc,
new_indesc = xzalloc(sizeof(struct input_descriptor));
memcpy(new_indesc, indesc, sizeof(*new_indesc));
new_indesc->data = buffer;
- new_indesc->name = NULL;
+ new_indesc->name = xstrdup(indesc->name);
scanner_push_indesc(state, new_indesc);
b = yy_scan_string(buffer, scanner);
@@ -1039,6 +1174,8 @@ void *scanner_init(struct parser_state *state)
yylex_init_extra(state, &scanner);
yyset_out(NULL, scanner);
+ state->startcond_active = xzalloc_array(__SC_MAX,
+ sizeof(*state->startcond_active));
return scanner;
}
@@ -1068,6 +1205,8 @@ void scanner_destroy(struct nft_ctx *nft)
struct parser_state *state = yyget_extra(nft->scanner);
input_descriptor_list_destroy(state);
+ xfree(state->startcond_active);
+
yylex_destroy(nft->scanner);
}
@@ -1076,6 +1215,7 @@ static void scanner_push_start_cond(void *scanner, enum startcond_type type)
struct parser_state *state = yyget_extra(scanner);
state->startcond_type = type;
+ state->startcond_active[type]++;
yy_push_state((int)type, scanner);
}
@@ -1084,6 +1224,8 @@ void scanner_pop_start_cond(void *scanner, enum startcond_type t)
{
struct parser_state *state = yyget_extra(scanner);
+ state->startcond_active[t]--;
+
if (state->startcond_type != t) {
state->flex_state_pop++;
return; /* Can't pop just yet! */
@@ -1093,6 +1235,10 @@ void scanner_pop_start_cond(void *scanner, enum startcond_type t)
state->flex_state_pop--;
state->startcond_type = yy_top_state(scanner);
yy_pop_state(scanner);
+
+ t = state->startcond_type;
+ if (state->startcond_active[t])
+ return;
}
state->startcond_type = yy_top_state(scanner);
diff --git a/src/segtree.c b/src/segtree.c
index f721954f..0e3d111f 100644
--- a/src/segtree.c
+++ b/src/segtree.c
@@ -19,573 +19,26 @@
#include <expression.h>
#include <gmputil.h>
#include <utils.h>
-#include <rbtree.h>
-/**
- * struct seg_tree - segment tree
- *
- * @root: the rbtree's root
- * @type: the datatype of the dimension
- * @dwidth: width of the dimension
- * @byteorder: byteorder of elements
- * @debug_mask: display debugging information
- */
-struct seg_tree {
- struct rb_root root;
- const struct datatype *keytype;
- unsigned int keylen;
- const struct datatype *datatype;
- unsigned int datalen;
- enum byteorder byteorder;
- unsigned int debug_mask;
-};
-
-enum elementary_interval_flags {
- EI_F_INTERVAL_END = 0x1,
- EI_F_INTERVAL_OPEN = 0x2,
-};
-
-/**
- * struct elementary_interval - elementary interval [left, right]
- *
- * @rb_node: seg_tree rb node
- * @list: list node for linearized tree
- * @left: left endpoint
- * @right: right endpoint
- * @size: interval size (right - left)
- * @flags: flags
- * @expr: associated expression
- */
-struct elementary_interval {
- union {
- struct rb_node rb_node;
- struct list_head list;
- };
-
- mpz_t left;
- mpz_t right;
- mpz_t size;
-
- enum elementary_interval_flags flags;
- struct expr *expr;
-};
-
-static void seg_tree_init(struct seg_tree *tree, const struct set *set,
- struct expr *init, unsigned int debug_mask)
-{
- struct expr *first;
-
- first = list_entry(init->expressions.next, struct expr, list);
- tree->root = RB_ROOT;
- tree->keytype = set->key->dtype;
- tree->keylen = set->key->len;
- tree->datatype = NULL;
- tree->datalen = 0;
- if (set->data) {
- tree->datatype = set->data->dtype;
- tree->datalen = set->data->len;
- }
- tree->byteorder = first->byteorder;
- tree->debug_mask = debug_mask;
-}
-
-static struct elementary_interval *ei_alloc(const mpz_t left, const mpz_t right,
- struct expr *expr,
- enum elementary_interval_flags flags)
-{
- struct elementary_interval *ei;
-
- ei = xzalloc(sizeof(*ei));
- mpz_init_set(ei->left, left);
- mpz_init_set(ei->right, right);
- mpz_init(ei->size);
- mpz_sub(ei->size, right, left);
- if (expr != NULL)
- ei->expr = expr_get(expr);
- ei->flags = flags;
- return ei;
-}
-
-static void ei_destroy(struct elementary_interval *ei)
-{
- mpz_clear(ei->left);
- mpz_clear(ei->right);
- mpz_clear(ei->size);
- if (ei->expr != NULL)
- expr_free(ei->expr);
- xfree(ei);
-}
-
-/**
- * ei_lookup - find elementary interval containing point p
- *
- * @tree: segment tree
- * @p: the point
- */
-static struct elementary_interval *ei_lookup(struct seg_tree *tree, const mpz_t p)
-{
- struct rb_node *n = tree->root.rb_node;
- struct elementary_interval *ei;
-
- while (n != NULL) {
- ei = rb_entry(n, struct elementary_interval, rb_node);
-
- if (mpz_cmp(p, ei->left) >= 0 &&
- mpz_cmp(p, ei->right) <= 0)
- return ei;
- else if (mpz_cmp(p, ei->left) <= 0)
- n = n->rb_left;
- else if (mpz_cmp(p, ei->right) > 0)
- n = n->rb_right;
- }
- return NULL;
-}
-
-static void ei_remove(struct seg_tree *tree, struct elementary_interval *ei)
-{
- rb_erase(&ei->rb_node, &tree->root);
-}
-
-static void __ei_insert(struct seg_tree *tree, struct elementary_interval *new)
-{
- struct rb_node **p = &tree->root.rb_node;
- struct rb_node *parent = NULL;
- struct elementary_interval *ei;
-
- while (*p != NULL) {
- parent = *p;
- ei = rb_entry(parent, struct elementary_interval, rb_node);
-
- if (mpz_cmp(new->left, ei->left) >= 0 &&
- mpz_cmp(new->left, ei->right) <= 0)
- break;
- else if (mpz_cmp(new->left, ei->left) <= 0)
- p = &(*p)->rb_left;
- else if (mpz_cmp(new->left, ei->left) > 0)
- p = &(*p)->rb_right;
- }
-
- rb_link_node(&new->rb_node, parent, p);
- rb_insert_color(&new->rb_node, &tree->root);
-}
-
-static bool segtree_debug(unsigned int debug_mask)
-{
- if (debug_mask & NFT_DEBUG_SEGTREE)
- return true;
-
- return false;
-}
-
-/**
- * ei_insert - insert an elementary interval into the tree
- *
- * @tree: the seg_tree
- * @new: the elementary interval
- *
- * New entries take precedence over existing ones. Insertions are assumed to
- * be ordered by descending interval size, meaning the new interval never
- * extends over more than two existing intervals.
- */
-static int ei_insert(struct list_head *msgs, struct seg_tree *tree,
- struct elementary_interval *new, bool merge)
-{
- struct elementary_interval *lei, *rei, *ei;
- struct expr *new_expr, *expr;
- mpz_t p;
-
- mpz_init2(p, tree->keylen);
-
- /*
- * Lookup the intervals containing the left and right endpoints.
- */
- lei = ei_lookup(tree, new->left);
- rei = ei_lookup(tree, new->right);
-
- if (segtree_debug(tree->debug_mask))
- pr_gmp_debug("insert: [%Zx %Zx]\n", new->left, new->right);
-
- if (lei != NULL && rei != NULL && lei == rei) {
- if (!merge) {
- ei = lei;
- goto err;
- }
- /* single element contained in an existing interval */
- if (mpz_cmp(new->left, new->right) == 0) {
- ei_destroy(new);
- goto out;
- }
-
- /*
- * The new interval is entirely contained in the same interval,
- * split it into two parts:
- *
- * [lei_left, new_left) and (new_right, rei_right]
- */
- if (segtree_debug(tree->debug_mask))
- pr_gmp_debug("split [%Zx %Zx]\n", lei->left, lei->right);
-
- ei_remove(tree, lei);
-
- mpz_sub_ui(p, new->left, 1);
- if (mpz_cmp(lei->left, p) <= 0)
- __ei_insert(tree, ei_alloc(lei->left, p, lei->expr, 0));
-
- mpz_add_ui(p, new->right, 1);
- if (mpz_cmp(p, rei->right) < 0)
- __ei_insert(tree, ei_alloc(p, rei->right, lei->expr, 0));
- ei_destroy(lei);
- } else {
- if (lei != NULL) {
- if (!merge) {
- ei = lei;
- goto err;
- }
- /*
- * Left endpoint is within lei, adjust it so we have:
- *
- * [lei_left, new_left)[new_left, new_right]
- */
- if (segtree_debug(tree->debug_mask)) {
- pr_gmp_debug("adjust left [%Zx %Zx]\n",
- lei->left, lei->right);
- }
-
- mpz_sub_ui(lei->right, new->left, 1);
- mpz_sub(lei->size, lei->right, lei->left);
- if (mpz_sgn(lei->size) < 0) {
- ei_remove(tree, lei);
- ei_destroy(lei);
- }
- }
- if (rei != NULL) {
- if (!merge) {
- ei = rei;
- goto err;
- }
- /*
- * Right endpoint is within rei, adjust it so we have:
- *
- * [new_left, new_right](new_right, rei_right]
- */
- if (segtree_debug(tree->debug_mask)) {
- pr_gmp_debug("adjust right [%Zx %Zx]\n",
- rei->left, rei->right);
- }
-
- mpz_add_ui(rei->left, new->right, 1);
- mpz_sub(rei->size, rei->right, rei->left);
- if (mpz_sgn(rei->size) < 0) {
- ei_remove(tree, rei);
- ei_destroy(rei);
- }
- }
- }
-
- __ei_insert(tree, new);
-out:
- mpz_clear(p);
-
- return 0;
-err:
- mpz_clear(p);
- errno = EEXIST;
- if (new->expr->etype == EXPR_MAPPING) {
- new_expr = new->expr->left;
- expr = ei->expr->left;
- } else {
- new_expr = new->expr;
- expr = ei->expr;
- }
-
- return expr_binary_error(msgs, new_expr, expr,
- "conflicting intervals specified");
-}
-
-/*
- * Sort intervals according to their priority, which is defined inversely to
- * their size.
- *
- * The beginning of the interval is used as secondary sorting criterion. This
- * makes sure that overlapping ranges with equal priority are next to each
- * other, allowing to easily detect unsolvable conflicts during insertion.
- *
- * Note: unsolvable conflicts can only occur when using ranges or two identical
- * prefix specifications.
- */
-static int interval_cmp(const void *p1, const void *p2)
-{
- const struct elementary_interval *e1 = *(void * const *)p1;
- const struct elementary_interval *e2 = *(void * const *)p2;
- mpz_t d;
- int ret;
-
- mpz_init(d);
-
- mpz_sub(d, e2->size, e1->size);
- if (mpz_cmp_ui(d, 0))
- ret = mpz_sgn(d);
- else
- ret = mpz_cmp(e1->left, e2->left);
-
- mpz_clear(d);
- return ret;
-}
-
-static unsigned int expr_to_intervals(const struct expr *set,
- unsigned int keylen,
- struct elementary_interval **intervals)
-{
- struct elementary_interval *ei;
- struct expr *i, *next;
- unsigned int n;
- mpz_t low, high;
-
- mpz_init2(low, keylen);
- mpz_init2(high, keylen);
-
- /*
- * Convert elements to intervals.
- */
- n = 0;
- list_for_each_entry_safe(i, next, &set->expressions, list) {
- range_expr_value_low(low, i);
- range_expr_value_high(high, i);
- ei = ei_alloc(low, high, i, 0);
- intervals[n++] = ei;
- }
- mpz_clear(high);
- mpz_clear(low);
-
- return n;
-}
-
-static bool intervals_match(const struct elementary_interval *e1,
- const struct elementary_interval *e2)
-{
- return mpz_cmp(e1->left, e2->left) == 0 &&
- mpz_cmp(e1->right, e2->right) == 0;
-}
-
-/* This function checks for overlaps in two ways:
- *
- * 1) A new interval end intersects an existing interval.
- * 2) New intervals that are larger than existing ones, that don't intersect
- * at all, but that wrap the existing ones.
- */
-static bool interval_overlap(const struct elementary_interval *e1,
- const struct elementary_interval *e2)
-{
- if (intervals_match(e1, e2))
- return false;
-
- return (mpz_cmp(e1->left, e2->left) >= 0 &&
- mpz_cmp(e1->left, e2->right) <= 0) ||
- (mpz_cmp(e1->right, e2->left) >= 0 &&
- mpz_cmp(e1->right, e2->right) <= 0) ||
- (mpz_cmp(e1->left, e2->left) <= 0 &&
- mpz_cmp(e1->right, e2->right) >= 0);
-}
-
-static int set_overlap(struct list_head *msgs, const struct set *set,
- struct expr *init, unsigned int keylen, bool add)
-{
- struct elementary_interval *new_intervals[init->size + 1];
- struct elementary_interval *intervals[set->init->size + 1];
- unsigned int n, m, i, j;
- int ret = 0;
-
- n = expr_to_intervals(init, keylen, new_intervals);
- m = expr_to_intervals(set->init, keylen, intervals);
-
- for (i = 0; i < n; i++) {
- bool found = false;
-
- for (j = 0; j < m; j++) {
- if (add && interval_overlap(new_intervals[i],
- intervals[j])) {
- expr_error(msgs, new_intervals[i]->expr,
- "interval overlaps with an existing one");
- errno = EEXIST;
- ret = -1;
- goto out;
- } else if (!add && intervals_match(new_intervals[i],
- intervals[j])) {
- found = true;
- break;
- }
- }
- if (!add && !found) {
- expr_error(msgs, new_intervals[i]->expr,
- "interval not found in set");
- errno = ENOENT;
- ret = -1;
- break;
- }
- }
-out:
- for (i = 0; i < n; i++)
- ei_destroy(new_intervals[i]);
- for (i = 0; i < m; i++)
- ei_destroy(intervals[i]);
-
- return ret;
-}
-
-static int set_to_segtree(struct list_head *msgs, struct set *set,
- struct expr *init, struct seg_tree *tree,
- bool add, bool merge)
-{
- struct elementary_interval **intervals;
- struct expr *i, *next;
- unsigned int n, m;
- int err = 0;
-
- /* We are updating an existing set with new elements, check if the new
- * interval overlaps with any of the existing ones.
- */
- if (set->init && set->init != init) {
- err = set_overlap(msgs, set, init, tree->keylen, add);
- if (err < 0)
- return err;
- }
-
- intervals = xmalloc_array(init->size, sizeof(intervals[0]));
- n = expr_to_intervals(init, tree->keylen, intervals);
-
- list_for_each_entry_safe(i, next, &init->expressions, list) {
- list_del(&i->list);
- expr_free(i);
- }
-
- /*
- * Sort intervals by priority.
- */
- qsort(intervals, n, sizeof