diff options
132 files changed, 5679 insertions, 2197 deletions
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 bb65f749..e3c980a7 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ -AC_INIT([nftables], [1.0.1], [netfilter-devel@vger.kernel.org]) -AC_DEFINE([RELEASE_NAME], ["Fearless Fosdick #3"], [Release name]) +AC_INIT([nftables], [1.0.2], [netfilter-devel@vger.kernel.org]) +AC_DEFINE([RELEASE_NAME], ["Lester Gooch"], [Release name]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) @@ -9,7 +9,7 @@ AM_INIT_AUTOMAKE([-Wall foreign subdir-objects 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"], [ @@ -147,6 +145,7 @@ AC_CONFIG_FILES([ \ files/osf/Makefile \ doc/Makefile \ py/Makefile \ + examples/Makefile \ ]) AC_OUTPUT diff --git a/doc/nft.txt b/doc/nft.txt index e4ed9824..f7a53ac9 100644 --- a/doc/nft.txt +++ b/doc/nft.txt @@ -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 --------------------------------------- diff --git a/doc/payload-expression.txt b/doc/payload-expression.txt index 930a1807..106ff74c 100644 --- a/doc/payload-expression.txt +++ b/doc/payload-expression.txt @@ -614,37 +614,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 +673,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/statements.txt b/doc/statements.txt index 8675892a..6aaf806b 100644 --- a/doc/statements.txt +++ b/doc/statements.txt @@ -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] 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 3a9a5e81..b6c7d48b 100644 --- a/include/cache.h +++ b/include/cache.h @@ -54,6 +54,7 @@ struct nft_cache_filter { const char *table; const char *chain; const char *set; + const char *ft; } list; struct { @@ -133,4 +134,7 @@ struct nft_cache { uint32_t flags; }; +void nft_chain_cache_update(struct netlink_ctx *ctx, struct table *table, + const char *chain); + #endif /* _NFT_CACHE_H_ */ diff --git a/include/datatype.h b/include/datatype.h index f5bb9dc4..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; 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..2c3818e8 100644 --- a/include/expression.h +++ b/include/expression.h @@ -120,6 +120,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 +132,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 +192,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 +201,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> @@ -482,10 +487,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..b0d78eb8 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,7 @@ 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); int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd); @@ -169,6 +171,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 +193,7 @@ STMT_PRINT_STUB(verdict) STMT_PRINT_STUB(connlimit) STMT_PRINT_STUB(tproxy) STMT_PRINT_STUB(synproxy) +STMT_PRINT_STUB(optstrip) #undef STMT_PRINT_STUB #undef EXPR_PRINT_STUB diff --git a/include/mnl.h b/include/mnl.h index 68ec80cd..4c701d4e 100644 --- a/include/mnl.h +++ b/include/mnl.h @@ -33,8 +33,8 @@ 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); int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd, unsigned int flags); @@ -43,25 +43,27 @@ 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_del(struct netlink_ctx *ctx, 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 +78,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 a692edcd..e8e0f68a 100644 --- a/include/netlink.h +++ b/include/netlink.h @@ -39,6 +39,7 @@ struct netlink_parse_ctx { struct stmt *stmt; struct expr *registers[MAX_REGS + 1]; unsigned int debug_mask; + struct netlink_ctx *nlctx; }; struct rule_pp_ctx { @@ -135,7 +136,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); 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..f32154cc 100644 --- a/include/parser.h +++ b/include/parser.h @@ -31,27 +31,57 @@ struct parser_state { enum startcond_type { PARSER_SC_BEGIN, PARSER_SC_ARP, + PARSER_SC_AT, PARSER_SC_CT, PARSER_SC_COUNTER, PARSER_SC_ETH, + PARSER_SC_FLAGS, + 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_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, }; struct mnl_socket; diff --git a/include/payload.h b/include/payload.h index 8bc3fb9a..37869928 100644 --- a/include/payload.h +++ b/include/payload.h @@ -25,16 +25,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 +45,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, 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..e232b97a 100644 --- a/include/rule.h +++ b/include/rule.h @@ -311,7 +311,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 +324,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 +346,7 @@ struct set { struct expr *key; struct expr *data; uint32_t objtype; + struct set *existing_set; struct expr *init; struct expr *rg_cache; uint32_t policy; diff --git a/include/statement.h b/include/statement.h index 06221040..2a2d3001 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; @@ -297,6 +303,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 +333,7 @@ enum stmt_types { STMT_MAP, STMT_SYNPROXY, STMT_CHAIN, + STMT_OPTSTRIP, }; /** @@ -380,6 +388,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/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 6d20716d..fd8df884 100644 --- a/src/cache.c +++ b/src/cache.c @@ -19,6 +19,8 @@ static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags) { + struct set *set; + switch (cmd->obj) { case CMD_OBJ_TABLE: if (!cmd->table) @@ -29,6 +31,10 @@ static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags) NFT_CACHE_SET | NFT_CACHE_OBJECT | NFT_CACHE_FLOWTABLE; + list_for_each_entry(set, &cmd->table->sets, list) { + if (set->automerge) + flags |= NFT_CACHE_SETELEM_MAYBE; + } break; case CMD_OBJ_CHAIN: case CMD_OBJ_SET: @@ -229,6 +235,15 @@ static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd, 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; @@ -342,31 +357,23 @@ struct table *table_cache_find(const struct cache *cache, struct chain_cache_dump_ctx { struct netlink_ctx *nlctx; struct table *table; - const struct nft_cache_filter *filter; }; static int chain_cache_cb(struct nftnl_chain *nlc, void *arg) { struct chain_cache_dump_ctx *ctx = arg; - const struct nft_cache_filter *filter = ctx->filter; const char *chain_name, *table_name; uint32_t hash, family; 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) - return 0; - - if (filter && filter->list.table && filter->list.chain && - (filter->list.family != family || - strcmp(filter->list.table, table_name) || - strcmp(filter->list.chain, chain_name))) + 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); @@ -383,25 +390,33 @@ static int chain_cache_cb(struct nftnl_chain *nlc, void *arg) } static int chain_cache_init(struct netlink_ctx *ctx, struct table *table, - struct nftnl_chain_list *chain_list, - const struct nft_cache_filter *filter) + struct nftnl_chain_list *chain_list) { struct chain_cache_dump_ctx dump_ctx = { .nlctx = ctx, .table = table, - .filter = filter, }; nftnl_chain_list_foreach(chain_list, chain_cache_cb, &dump_ctx); 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; @@ -414,6 +429,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; @@ -441,16 +471,9 @@ struct chain *chain_cache_find(const struct table *table, const char *name) return NULL; } -struct rule_cache_dump_ctx { - struct netlink_ctx *nlctx; - const struct nft_cache_filter *filter; -}; - static int list_rule_cb(struct nftnl_rule *nlr, void *data) { - struct rule_cache_dump_ctx *rule_ctx = data; - const struct nft_cache_filter *filter = rule_ctx->filter; - struct netlink_ctx *ctx = rule_ctx->nlctx; + struct netlink_ctx *ctx = data; const struct handle *h = ctx->data; const char *table, *chain; struct rule *rule; @@ -465,12 +488,6 @@ static int list_rule_cb(struct nftnl_rule *nlr, void *data) (h->chain.name && strcmp(chain, h->chain.name) != 0)) return 0; - if (filter && filter->list.table && filter->list.chain && - (filter->list.family != family || - strcmp(filter->list.table, table) || - strcmp(filter->list.chain, chain))) - return 0; - netlink_dump_rule(nlr, ctx); rule = netlink_delinearize_rule(ctx, nlr); list_add_tail(&rule->list, &ctx->list); @@ -478,16 +495,19 @@ static int list_rule_cb(struct nftnl_rule *nlr, void *data) return 0; } -static int rule_cache_init(struct netlink_ctx *ctx, const struct handle *h, +static int rule_cache_dump(struct netlink_ctx *ctx, const struct handle *h, const struct nft_cache_filter *filter) { - struct rule_cache_dump_ctx rule_ctx = { - .nlctx = ctx, - .filter = filter, - }; struct nftnl_rule_list *rule_cache; + const char *table = NULL; + const char *chain = NULL; - rule_cache = mnl_nft_rule_dump(ctx, h->family); + if (filter) { + table = filter->list.table; + chain = filter->list.chain; + } + + rule_cache = mnl_nft_rule_dump(ctx, h->family, table, chain); if (rule_cache == NULL) { if (errno == EINTR) return -1; @@ -496,7 +516,7 @@ static int rule_cache_init(struct netlink_ctx *ctx, const struct handle *h, } ctx->data = h; - nftnl_rule_list_foreach(rule_cache, list_rule_cb, &rule_ctx); + nftnl_rule_list_foreach(rule_cache, list_rule_cb, ctx); nftnl_rule_list_free(rule_cache); return 0; } @@ -504,57 +524,66 @@ static int rule_cache_init(struct netlink_ctx *ctx, const struct handle *h, struct set_cache_dump_ctx { struct netlink_ctx *nlctx; struct table *table; - const struct nft_cache_filter *filter; }; static int set_cache_cb(struct nftnl_set *nls, void *arg) { struct set_cache_dump_ctx *ctx = arg; + const char *set_table; const char *set_name; + uint32_t set_family; struct set *set; uint32_t hash; + set_table = nftnl_set_get_str(nls, NFTNL_SET_TABLE); + set_family = nftnl_set_get_u32(nls, NFTNL_SET_FAMILY); + + if (set_family != ctx->table->handle.family || + strcmp(set_table, ctx->table->handle.table.name)) + return 0; + set = netlink_delinearize_set(ctx->nlctx, nls); if (!set) return -1; - if (ctx->filter && ctx->filter->list.set && - (ctx->filter->list.family != set->handle.family || - strcmp(ctx->filter->list.table, set->handle.table.name) || - strcmp(ctx->filter->list.set, set->handle.set.name))) { - set_free(set); - return 0; - } - set_name = nftnl_set_get_str(nls, NFTNL_SET_NAME); hash = djb_hash(set_name) % NFT_CACHE_HSIZE; cache_add(&set->cache, &ctx->table->set_cache, hash); + nftnl_set_list_del(nls); + nftnl_set_free(nls); return 0; } static int set_cache_init(struct netlink_ctx *ctx, struct table *table, - struct nftnl_set_list *set_list, - const struct nft_cache_filter *filter) + struct nftnl_set_list *set_list) { struct set_cache_dump_ctx dump_ctx = { .nlctx = ctx, .table = table, - .filter = filter, }; + nftnl_set_list_foreach(set_list, set_cache_cb, &dump_ctx); return 0; } -static struct nftnl_set_list *set_cache_dump(struct netlink_ctx *ctx, - const struct table *table, - int *err) +static struct nftnl_set_list * +set_cache_dump(struct netlink_ctx *ctx, + const struct nft_cache_filter *filter, int *err) { struct nftnl_set_list *set_list; + int family = NFPROTO_UNSPEC; + const char *table = NULL; + const char *set = NULL; + + if (filter) { + family = filter->list.family; + table = filter->list.table; + set = filter->list.set; + } - set_list = mnl_nft_set_dump(ctx, table->handle.family, - table->handle.table.name); + set_list = mnl_nft_set_dump(ctx, family, table, set); if (!set_list) { if (errno == EINTR) { *err = -1; @@ -689,10 +718,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; @@ -701,6 +739,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; } @@ -716,13 +756,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; @@ -772,25 +820,57 @@ static int cache_init_tables(struct netlink_ctx *ctx, struct handle *h, struct table *table, *next; int ret; - ret = netlink_list_tables(ctx, h); + ret = netlink_list_tables(ctx, h, filter); if (ret < 0) return -1; list_for_each_entry_safe(table, next, &ctx->list, list) { list_del(&table->list); - - if (filter && filter->list.table && - (filter->list.family != table->handle.family || - strcmp(filter->list.table, table->handle.table.name))) { - table_free(table); - continue; - } table_cache_add(table, cache); } return 0; } +static int rule_init_cache(struct netlink_ctx *ctx, struct table *table, + const struct nft_cache_filter *filter) +{ + struct rule *rule, *nrule; + struct chain *chain; + int ret; + + ret = rule_cache_dump(ctx, &table->handle, filter); + + 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) + return -1; + + list_move_tail(&rule->list, &chain->rules); + } + + return ret; +} + +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) { @@ -798,33 +878,35 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags, struct nftnl_chain_list *chain_list = NULL; struct nftnl_set_list *set_list = NULL; struct nftnl_obj_list *obj_list; - struct rule *rule, *nrule; struct table *table; - struct chain *chain; struct set *set; int ret = 0; if (flags & NFT_CACHE_CHAIN_BIT) { - chain_list = chain_cache_dump(ctx, &ret); + chain_list = chain_cache_dump(ctx, filter, &ret); if (!chain_list) return -1; } + if (flags & NFT_CACHE_SET_BIT) { + set_list = set_cache_dump(ctx, filter, &ret); + if (!set_list) { + ret = -1; + goto cache_fails; + } + } + if (flags & NFT_CACHE_FLOWTABLE_BIT) { + ft_list = ft_cache_dump(ctx, filter); + if (!ft_list) { + ret = -1; + goto cache_fails; + } + } list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) { if (flags & NFT_CACHE_SET_BIT) { - set_list = set_cache_dump(ctx, table, &ret); - if (!set_list) { - ret = -1; + ret = set_cache_init(ctx, table, set_list); + if (ret < 0) goto cache_fails; - } - ret = set_cache_init(ctx, table, set_list, filter); - - nftnl_set_list_free(set_list); - - if (ret < 0) { - ret = -1; - goto cache_fails; - } } if (flags & NFT_CACHE_SETELEM_BIT) { list_for_each_entry(set, &table->set_cache.list, cache.list) { @@ -836,10 +918,8 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags, 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) { @@ -851,33 +931,19 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags, 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, filter); - if (ret < 0) { - ret = -1; + ret = chain_cache_init(ctx, table, chain_list); + 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); @@ -889,34 +955,29 @@ static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags, nftnl_obj_list_free(obj_list); - if (ret < 0) { - ret = -1; + if (ret < 0) goto cache_fails; - } } if (flags & NFT_CACHE_RULE_BIT) { - ret = rule_cache_init(ctx, &table->handle, filter); - 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); @@ -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) @@ -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; } @@ -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 a06c3996..2e31c858 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -1068,6 +1068,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); @@ -1077,9 +1078,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; } @@ -1088,7 +1090,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, @@ -1134,7 +1136,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; @@ -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: @@ -132,24 +175,8 @@ void erec_print(struct output_ctx *octx, const struct error_record *erec, 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 eebd9921..1447a4c2 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> @@ -138,6 +139,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)); @@ -149,11 +151,19 @@ static int byteorder_conversion(struct eval_ctx *ctx, struct expr **expr, if ((*expr)->etype == EXPR_CONCAT) 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 +339,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; @@ -599,7 +612,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 +624,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) @@ -1016,7 +1029,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); @@ -1246,10 +1259,19 @@ 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; + struct expr *i, *next, *key = NULL; + const struct expr *key_ctx = NULL; + 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); + } list_for_each_entry_safe(i, next, &(*expr)->expressions, list) { - unsigned dsize_bytes; + enum byteorder bo = BYTEORDER_INVALID; + unsigned dsize_bytes, dsize = 0; if (i->etype == EXPR_CT && (i->ct.key == NFT_CT_SRC || @@ -1263,32 +1285,56 @@ 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) { 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) + key = list_next_entry(key, list); } (*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 +1343,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; } @@ -1390,6 +1440,7 @@ static int expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr **expr) 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) @@ -1422,6 +1473,39 @@ 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: + 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: + 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 int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr) { struct expr *set = *expr, *i, *next; @@ -1509,10 +1593,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 +1648,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 +1676,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 +1692,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) @@ -2556,9 +2661,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); @@ -2751,19 +2856,22 @@ static int stmt_evaluate_reject_inet_family(struct eval_ctx *ctx, 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], "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], "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; } @@ -2923,10 +3031,12 @@ static int stmt_evaluate_reject_default(struct eval_ctx *ctx, 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; @@ -3420,6 +3530,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 = {}; @@ -3443,6 +3554,11 @@ 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) { int err; @@ -3616,6 +3732,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); @@ -3645,6 +3762,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; } @@ -3728,10 +3854,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) @@ -3842,6 +3973,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); } @@ -3863,14 +3996,20 @@ 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); + 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)) + return interval_set_eval(ctx, ctx->set, cmd->expr); + + ctx->set = NULL; + return 0; } @@ -3891,8 +4030,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; @@ -3903,32 +4042,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; @@ -3941,7 +4097,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); } @@ -4005,9 +4162,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, @@ -4018,6 +4182,11 @@ 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)) + return interval_set_eval(ctx, ctx->set, set->init); + ctx->set = NULL; return 0; diff --git a/src/expression.c b/src/expression.c index 4c0874fe..deb649e1 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> @@ -135,12 +136,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 +269,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 +302,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) @@ -950,7 +951,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 +992,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 +1000,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 +1188,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 +1465,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/iface.c b/src/iface.c index d0e1834c..c0642e0c 100644 --- a/src/iface.c +++ b/src/iface.c @@ -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..e61adc76 --- /dev/null +++ b/src/intervals.c @@ -0,0 +1,739 @@ +/* + * 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 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 (existing_set) { + if (existing_set->init) { + list_splice_init(&existing_set->init->expressions, + &init->expressions); + } else { + existing_set->init = set_expr_alloc(&internal_location, + set); + } + } + + set_to_range(init); + list_expr_sort(&init->expressions); + + if (set->flags & NFT_SET_MAP) + return 0; + + 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 && + setelem_adjust(set, purge, &prev_range, &range, prev, i) < 0) { + 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); + + set_to_range(existing_set->init); + + 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) + gmp_printf("remove: [%Zx-%Zx]\n", + i->key->left->value, i->key->right->value); + list_for_each_entry(i, &add->expressions, list) + gmp_printf("add: [%Zx-%Zx]\n", + i->key->left->value, i->key->right->value); + list_for_each_entry(i, &existing_set->init->expressions, list) + gmp_printf("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 && + (elem->flags & EXPR_F_KERNEL || prev->flags & EXPR_F_KERNEL)) + 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; + + if (existing_set) { + if (existing_set->init) { + list_splice_init(&existing_set->init->expressions, + &init->expressions); + } else { + existing_set->init = set_expr_alloc(&internal_location, + set); + } + } + + set_to_range(init); + list_expr_sort(&init->expressions); + + 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; @@ -1122,6 +1122,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", @@ -1571,10 +1578,16 @@ 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)); +} + 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 +1617,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; } diff --git a/src/libnftables.c b/src/libnftables.c index 7b9d7efa..6a22ea09 100644 --- a/src/libnftables.c +++ b/src/libnftables.c @@ -128,9 +128,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 +160,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 +391,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 +432,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 +448,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) @@ -460,8 +500,8 @@ 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; unsigned int flags; - struct cmd *cmd; filter = nft_cache_filter_init(); flags = nft_cache_evaluate(nft, cmds, filter); @@ -472,7 +512,7 @@ static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs, nft_cache_filter_fini(filter); - list_for_each_entry(cmd, cmds, list) { + list_for_each_entry_safe(cmd, next, cmds, list) { struct eval_ctx ectx = { .nft = nft, .msgs = msgs, @@ -510,7 +550,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; @@ -578,7 +619,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 @@ -593,8 +634,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; @@ -605,9 +645,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); @@ -616,6 +653,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; @@ -656,5 +696,52 @@ err: json_print_echo(nft); if (rc) nft_cache_release(&nft->cache); + 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; @@ -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])) @@ -484,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..8e6aac5f 100644 --- a/src/mergesort.c +++ b/src/mergesort.c @@ -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: @@ -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, @@ -401,7 +405,7 @@ static void date_type_print(const struct expr *expr, struct output_ctx *octx) 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; @@ -415,7 +419,7 @@ static time_t parse_iso_date(const char *sym) if (strptime(sym, "%F", &tm)) goto success; - return -1; + return false; success: /* @@ -432,7 +436,9 @@ success: return ts; /* 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, @@ -653,20 +653,35 @@ 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) { char buf[MNL_SOCKET_BUFFER_SIZE]; struct nftnl_rule_list *nlr_list; + struct nftnl_rule *nlr = NULL; struct nlmsghdr *nlh; int ret; + 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); + } + 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); + 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) @@ -889,10 +904,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 +917,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; @@ -1016,10 +1046,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 +1059,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 +1093,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); @@ -1252,10 +1296,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 +1310,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 +1327,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; @@ -1677,9 +1728,9 @@ 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, const struct handle *h, + const struct expr *init) { - const struct handle *h = &cmd->handle; struct nftnl_set *nls; int err; @@ -1697,7 +1748,7 @@ int mnl_nft_setelem_del(struct netlink_ctx *ctx, const struct cmd *cmd) netlink_dump_set(nls, ctx); err = mnl_nft_setelem_batch(nls, ctx->batch, NFT_MSG_DELSETELEM, 0, - ctx->seqnum, cmd->expr, ctx); + ctx->seqnum, init, ctx); nftnl_set_free(nls); return err; @@ -1771,11 +1822,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; @@ -1783,10 +1836,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); @@ -1795,7 +1852,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; diff --git a/src/netlink.c b/src/netlink.c index ab90d0c0..89d864ed 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, @@ -135,7 +135,8 @@ struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set, 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_END; __netlink_gen_data(key, &nld, false); key->flags &= ~EXPR_F_INTERVAL_END; @@ -258,6 +259,10 @@ static int netlink_gen_concat_data_expr(int end, const struct expr *i, 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); @@ -664,11 +669,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; @@ -796,7 +809,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; @@ -926,7 +939,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 { @@ -951,7 +964,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; @@ -1096,17 +1109,25 @@ 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(struct expr *key, const struct datatype *dtype, struct expr *data, int *off) { const struct datatype *subtype; + struct expr *expr; - subtype = concat_subtype_lookup(dtype->type, --(*off)); - - expr = constant_expr_splice(data, subtype->size); - expr->dtype = subtype; - expr->byteorder = subtype->byteorder; + if (key) { + (*off)--; + expr = constant_expr_splice(data, key->len); + expr->dtype = datatype_get(key->dtype); + expr->byteorder = key->byteorder; + expr->len = key->len; + } else { + subtype = concat_subtype_lookup(dtype->type, --(*off)); + expr = constant_expr_splice(data, subtype->size); + expr->dtype = subtype; + expr->byteorder = subtype->byteorder; + } if (expr->byteorder == BYTEORDER_HOST_ENDIAN) mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE); @@ -1124,13 +1145,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(n, dtype, data, &off); compound_expr_add(concat, expr); + if (set->key->etype == EXPR_CONCAT) + n = list_next_entry(n, list); } expr_free(data); @@ -1150,7 +1176,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(NULL, dtype, data, &off); list_add_tail(&expr->list, &expressions); } @@ -1162,7 +1188,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(NULL, dtype, data, &off); list_del(&left->list); range = range_expr_alloc(&data->location, left, expr); @@ -1264,6 +1290,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); @@ -1713,7 +1740,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; @@ -1859,7 +1887,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; @@ -1914,6 +1941,8 @@ restart: n = 0; next: list_for_each_entry(stmt, &unordered, list) { + enum proto_bases b = base; + rel = stmt->expr; lhs = rel->left; @@ -1926,18 +1955,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; } diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index 0c2b439e..068c3bba 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -100,7 +100,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]); } @@ -218,6 +218,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); @@ -690,6 +697,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); } } @@ -1867,7 +1878,6 @@ static void payload_match_expand(struct rule_pp_ctx *ctx, struct stmt *nstmt; struct expr *nexpr = NULL; enum proto_bases base = left->payload.base; - bool stacked; payload_expr_expand(&list, left, &ctx->pctx); @@ -1893,22 +1903,17 @@ 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 (payload_is_stacked(ctx->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(&ctx->pdctx, nexpr->left, + ctx->pctx.family); + if (expr->op == OP_EQ && left->flags & EXPR_F_PROTOCOL) + payload_dependency_store(&ctx->pdctx, nstmt, base); } list_del(&ctx->stmt->list); stmt_free(ctx->stmt); @@ -2057,7 +2062,7 @@ static bool __meta_dependency_may_kill(const struct expr *dep, uint8_t *nfproto) } /* We have seen a protocol key expression that restricts matching at the network - * base, leave it in place since this is meaninful in bridge, inet and netdev + * base, leave it in place since this is meaningful in bridge, inet and netdev * families. Exceptions are ICMP and ICMPv6 where this code assumes that can * only happen with IPv4 and IPv6. */ @@ -2066,9 +2071,9 @@ static bool meta_may_dependency_kill(struct payload_dep_ctx *ctx, const struct expr *expr) { uint8_t l4proto, nfproto = NFPROTO_UNSPEC; - struct expr *dep = ctx->pdep->expr; + struct expr *dep = payload_dependency_get(ctx, PROTO_BASE_NETWORK_HDR); - if (ctx->pbase != PROTO_BASE_NETWORK_HDR) + if (!dep) return true; if (__meta_dependency_may_kill(dep, &nfproto)) @@ -2079,14 +2084,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; } @@ -2129,14 +2130,12 @@ static void ct_meta_common_postprocess(struct rule_pp_ctx *ctx, relational_expr_pctx_update(&ctx->pctx, expr); - 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 (base < 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); + payload_dependency_release(&ctx->pdctx, base); + if (left->flags & EXPR_F_PROTOCOL) payload_dependency_store(&ctx->pdctx, ctx->stmt, base); } @@ -2223,8 +2222,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; @@ -2243,7 +2242,7 @@ static void __binop_adjust(const struct expr *binop, struct expr *right, binop_adjust_one(binop, i->key->right, shift); break; case EXPR_SET_ELEM: - __binop_adjust(binop, i->key->key, shift); + binop_adjust(binop, i->key->key, shift); break; default: BUG("unknown expression type %s\n", expr_name(i->key)); @@ -2260,22 +2259,22 @@ 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 **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 *binop = *expr_binop; struct expr *left = binop->left; struct expr *mask = binop->right; unsigned int shift; + assert(binop->etype == EXPR_BINOP); + if ((left->etype == EXPR_PAYLOAD && payload_expr_trim(left, mask, &ctx->pctx, &shift)) || (left->etype == EXPR_EXTHDR && exthdr_find_template(left, mask, &shift))) { + struct expr *right = NULL; + /* mask is implicit, binop needs to be removed. * * Fix all values of the expression according to the mask @@ -2285,48 +2284,74 @@ 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_binop = expr_get(left); expr_free(binop); + 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); } } 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); @@ -2342,8 +2367,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 @@ -2353,7 +2378,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; @@ -2361,7 +2386,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: @@ -2372,9 +2397,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) { @@ -2401,7 +2426,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; + } } } @@ -2422,56 +2461,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) @@ -2649,7 +2665,8 @@ static void stmt_reject_postprocess(struct rule_pp_ctx *rctx) if (stmt->reject.type == NFT_REJECT_TCP_RST && payload_dependency_exists(&rctx->pdctx, PROTO_BASE_TRANSPORT_HDR)) - payload_dependency_release(&rctx->pdctx); + payload_dependency_release(&rctx->pdctx, + PROTO_BASE_TRANSPORT_HDR); break; case NFPROTO_IPV6: stmt->reject.family = rctx->pctx.family; @@ -2657,7 +2674,8 @@ static void stmt_reject_postprocess(struct rule_pp_ctx *rctx) if (stmt->reject.type == NFT_REJECT_TCP_RST && payload_dependency_exists(&rctx->pdctx, PROTO_BASE_TRANSPORT_HDR)) - payload_dependency_release(&rctx->pdctx); + payload_dependency_release(&rctx->pdctx, + PROTO_BASE_TRANSPORT_HDR); break; case NFPROTO_INET: case NFPROTO_BRIDGE: @@ -2691,7 +2709,8 @@ static void stmt_reject_postprocess(struct rule_pp_ctx *rctx) } if (payload_dependency_exists(&rctx->pdctx, PROTO_BASE_NETWORK_HDR)) - payload_dependency_release(&rctx->pdctx); + payload_dependency_release(&rctx->pdctx, + PROTO_BASE_NETWORK_HDR); break; default: break; @@ -2777,7 +2796,7 @@ static void stmt_payload_binop_pp(struct rule_pp_ctx *ctx, struct expr *binop) assert(payload->etype == EXPR_PAYLOAD); if (payload_expr_trim(payload, mask, &ctx->pctx, &shift)) { - __binop_adjust(binop, mask, shift); + binop_adjust(binop, mask, shift); payload_expr_complete(payload, &ctx->pctx); expr_set_type(mask, payload->dtype, payload->byteorder); @@ -2872,7 +2891,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); @@ -3124,6 +3143,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); diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c index 111102fd..c8bbcb74 100644 --- a/src/netlink_linearize.c +++ b/src/netlink_linearize.c @@ -986,7 +986,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)); @@ -1353,6 +1353,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) { @@ -1616,6 +1628,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); } @@ -1673,5 +1687,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/optimize.c b/src/optimize.c new file mode 100644 index 00000000..d6dfffec --- /dev/null +++ b/src/optimize.c @@ -0,0 +1,1033 @@ +/* + * 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.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; + 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 __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b) +{ + 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 (!stmt_expr_supported(expr_a) || + !stmt_expr_supported(expr_b)) + return false; + + return __expr_cmp(expr_a->left, expr_b->left); + case STMT_COUNTER: + case STMT_NOTRACK: + break; + case STMT_VERDICT: + 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_LIMIT: + if (stmt_a->limit.rate != stmt_b->limit.rate || + stmt_a->limit.unit != stmt_b->limit.unit || + stmt_a->limit.burst != stmt_b->limit.burst || + stmt_a->limit.type != stmt_b->limit.type || + stmt_a->limit.flags != stmt_b->limit.flags) + return false; + 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.expr || stmt_b->reject.expr) + return false; + + 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; + 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_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); +} + +static bool stmt_type_find(struct optimize_ctx *ctx, const struct stmt *stmt) +{ + uint32_t i; + + for (i = 0; i < ctx->num_stmts; i++) { + if (__stmt_type_eq(stmt, ctx->stmt[i])) + return true; + } + + return false; +} + +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; + + if (stmt->ops->type == STMT_VERDICT && + stmt->expr->etype == EXPR_MAP) + 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: + case STMT_VERDICT: + clone->expr = expr_get(stmt->expr); + break; + case STMT_COUNTER: + case STMT_NOTRACK: + break; + case STMT_LIMIT: + memcpy(&clone->limit, &stmt->limit, sizeof(clone->limit)); + 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; + default: + stmt_free(clone); + continue; + } + + ctx->stmt[ctx->num_stmts++] = clone; + if (ctx->num_stmts >= MAX_STMTS) + return -1; + } + + return 0; +} + +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])) + return i; + } + /* should not ever happen. */ + return 0; +} + +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); + 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_stmts(const struct optimize_ctx *ctx, + uint32_t from, uint32_t to, + const struct merge *merge) +{ + struct expr *concat, *elem, *set; + struct stmt *stmt, *stmt_a; + 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++) { + concat = concat_expr_alloc(&internal_location); + for (k = 0; k < merge->num_stmts; k++) { + stmt_a = ctx->stmt_matrix[i][merge->stmt[k]]; + compound_expr_add(concat, expr_get(stmt_a->expr->right)); + } + elem = set_elem_expr_alloc(&internal_location, concat); + compound_expr_add(set, elem); + } + 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 from, uint32_t to, + const struct merge *merge) +{ + struct stmt *orig_stmt = ctx->stmt_matrix[from][merge->stmt[0]]; + struct expr *concat_a, *concat_b, *expr, *set; + struct stmt *stmt, *stmt_a, *stmt_b, *verdict; + uint32_t i, j; + 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++) { + concat_b = concat_expr_alloc(&internal_location); + for (j = 0; j < merge->num_stmts; j++) { + stmt_b = ctx->stmt_matrix[i][merge->stmt[j]]; + expr = stmt_b->expr->right; + compound_expr_add(concat_b, expr_get(expr)); + } + verdict = ctx->stmt_matrix[i][k]; + build_verdict_map(concat_b, verdict, set); + expr_free(concat_b); + } + + 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 i; + + for (i = 0; i < ctx->num_stmts; i++) { + switch (ctx->stmt[i]->ops->type) { + case STMT_VERDICT: + case STMT_NAT: + return ctx->stmt[i]->ops->type; + default: + continue; + } + } + + return STMT_INVALID; +} + +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); + + 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); + } + + 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"); +} + +static bool rules_eq(const struct optimize_ctx *ctx, int i, int j) +{ + uint32_t k; + + for (k = 0; k < ctx->num_stmts; k++) { + if (!stmt_type_eq(ctx->stmt_matrix[i][k], ctx->stmt_matrix[j][k])) + 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(struct stmt *) * ctx->num_rules); + for (i = 0; i < ctx->num_rules; i++) + ctx->stmt_matrix[i] = xzalloc(sizeof(struct stmt *) * MAX_STMTS); + + 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; + 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; + + 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/parser_bison.y b/src/parser_bison.y index 81d75ecb..ca5c488c 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -187,6 +187,10 @@ int nft_lex(void *, void *, void *); 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" @@ -404,6 +408,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" @@ -411,13 +416,15 @@ 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" @@ -874,7 +881,13 @@ 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 <stmt> optstrip_stmt +%destructor { stmt_free($$); } optstrip_stmt %type <expr> boolean_expr %destructor { expr_free($$); } boolean_expr @@ -916,28 +929,58 @@ 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_flags : { scanner_pop_start_cond(nft->scanner, PARSER_SC_FLAGS); }; +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_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); } common_block : INCLUDE QUOTED_STRING stmt_separator { @@ -1020,12 +1063,12 @@ 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; } ; @@ -1138,11 +1181,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); } @@ -1239,7 +1282,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); } @@ -1328,7 +1371,7 @@ 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); } @@ -1424,7 +1467,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); } @@ -1636,7 +1679,7 @@ table_block_alloc : /* empty */ } ; -table_options : FLAGS STRING +table_options : FLAGS STRING close_scope_flags { if (strcmp($2, "dormant") == 0) { $<table>0->flags |= TABLE_F_DORMANT; @@ -1780,7 +1823,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; @@ -1892,7 +1935,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; @@ -1903,7 +1946,7 @@ set_block : /* empty */ { $$ = $<set>-1; } datatype_set($1->key, $3->dtype); $$ = $1; } - | set_block FLAGS set_flag_list stmt_separator + | set_block FLAGS set_flag_list stmt_separator close_scope_flags { $1->flags = $3; $$ = $1; @@ -1973,6 +2016,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 { $$ = NFT_OBJECT_SYNPROXY; } ; map_block : /* empty */ { $$ = $<set>-1; } @@ -1985,7 +2029,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; @@ -1995,7 +2039,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; @@ -2029,14 +2073,14 @@ 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; $1->flags |= NFT_SET_OBJECT; $$ = $1; } - | map_block FLAGS set_flag_list stmt_separator + | map_block FLAGS set_flag_list stmt_separator close_scope_flags { $1->flags |= $3; $$ = $1; @@ -2064,7 +2108,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; } @@ -2109,7 +2153,7 @@ flowtable_block : /* empty */ { $$ = $<flowtable>-1; } { $$->flags |= NFT_FLOWTABLE_COUNTER; } - | flowtable_block FLAGS OFFLOAD stmt_separator + | flowtable_block FLAGS OFFLOAD stmt_separator close_scope_flags { $$->flags |= FLOWTABLE_F_HW_OFFLOAD; } @@ -2140,7 +2184,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, @@ -2339,33 +2390,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; } ; @@ -2469,13 +2520,13 @@ dev_spec : DEVICE string | /* empty */ { $$ = NULL; } ; -flags_spec : FLAGS OFFLOAD +flags_spec : FLAGS OFFLOAD close_scope_flags { $<chain>0->flags |= CHAIN_F_HW_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"), @@ -2796,19 +2847,20 @@ 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 ; chain_stmt_type : JUMP { $$ = NFT_JUMP; } @@ -3074,7 +3126,7 @@ log_arg : PREFIX string $<stmt>0->log.level = $2; $<stmt>0->log.flags |= STMT_LOG_LEVEL; } - | FLAGS log_flags + | FLAGS log_flags close_scope_flags { $<stmt>0->log.logflags |= $2; } @@ -3110,7 +3162,7 @@ level_type : string } ; -log_flags : TCP log_flags_tcp +log_flags : TCP log_flags_tcp close_scope_tcp { $$ = $2; } @@ -3321,7 +3373,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; @@ -3335,7 +3387,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; @@ -3349,7 +3401,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; @@ -3361,7 +3413,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; } @@ -3776,13 +3828,13 @@ queue_stmt : queue_stmt_compat close_scope_queue { $$ = queue_stmt_alloc(&@$, $3, 0); } - | QUEUE FLAGS queue_stmt_flags TO queue_stmt_expr close_scope_queue + | QUEUE FLAGS queue_stmt_flags close_scope_flags TO queue_stmt_expr close_scope_queue { - $$ = queue_stmt_alloc(&@$, $5, $3); + $$ = queue_stmt_alloc(&@$, $6, $3); } - | QUEUE FLAGS queue_stmt_flags QUEUENUM queue_stmt_expr_simple close_scope_queue + | QUEUE FLAGS queue_stmt_flags close_scope_flags QUEUENUM queue_stmt_expr_simple close_scope_queue { - $$ = queue_stmt_alloc(&@$, $5, $3); + $$ = queue_stmt_alloc(&@$, $6, $3); } ; @@ -3998,7 +4050,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), @@ -4060,7 +4112,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; } @@ -4077,11 +4129,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); } @@ -4461,11 +4513,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; @@ -4522,7 +4574,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; @@ -4722,10 +4774,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"); } @@ -4735,42 +4787,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, @@ -4784,21 +4836,21 @@ 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 + | 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, @@ -4812,7 +4864,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, @@ -4868,7 +4920,7 @@ chain_expr : variable_expr } ; -meta_expr : META meta_key +meta_expr : META meta_key close_scope_meta { $$ = meta_expr_alloc(&@$, $2); } @@ -4876,7 +4928,7 @@ meta_expr : META meta_key { $$ = meta_expr_alloc(&@$, $1); } - | META STRING + | META STRING close_scope_meta { struct error_record *erec; unsigned int key; @@ -4929,7 +4981,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: @@ -4953,7 +5005,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; @@ -4971,11 +5023,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); } @@ -5242,13 +5294,13 @@ 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 ; -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); @@ -5259,7 +5311,7 @@ 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")) { @@ -5281,7 +5333,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 @@ -5294,7 +5346,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 @@ -5320,11 +5372,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; } ; @@ -5349,20 +5405,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; } @@ -5371,13 +5427,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; } @@ -5399,13 +5455,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; } @@ -5415,7 +5471,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); } @@ -5428,7 +5484,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); } @@ -5438,18 +5494,18 @@ 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); } ; comp_hdr_field : NEXTHDR { $$ = COMPHDR_NEXTHDR; } - | FLAGS { $$ = COMPHDR_FLAGS; } + | FLAGS close_scope_flags { $$ = COMPHDR_FLAGS; } | 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); } @@ -5461,7 +5517,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); } @@ -5477,19 +5533,26 @@ 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); + } + ; + +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)); } ; @@ -5499,25 +5562,63 @@ tcp_hdr_field : SPORT { $$ = TCPHDR_SPORT; } | ACKSEQ { $$ = TCPHDR_ACKSEQ; } | DOFF { $$ = TCPHDR_DOFF; } | RESERVED { $$ = TCPHDR_RESERVED; } - | FLAGS { $$ = TCPHDR_FLAGS; } + | FLAGS close_scope_flags { $$ = TCPHDR_FLAGS; } | WINDOW { $$ = TCPHDR_WINDOW; } | CHECKSUM { $$ = TCPHDR_CHECKSUM; } | 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; @@ -5526,17 +5627,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); } @@ -5544,7 +5652,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; } @@ -5567,8 +5675,8 @@ sctp_chunk_type : DATA { $$ = SCTP_CHUNK_TYPE_DATA; } | ASCONF { $$ = SCTP_CHUNK_TYPE_ASCONF; } ; -sctp_chunk_common_field : TYPE { $$ = SCTP_CHUNK_COMMON_TYPE; } - | FLAGS { $$ = SCTP_CHUNK_COMMON_FLAGS; } +sctp_chunk_common_field : TYPE close_scope_type { $$ = SCTP_CHUNK_COMMON_TYPE; } + | FLAGS close_scope_flags { $$ = SCTP_CHUNK_COMMON_FLAGS; } | LENGTH { $$ = SCTP_CHUNK_COMMON_LENGTH; } ; @@ -5664,7 +5772,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 ($$) @@ -5686,7 +5794,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); } @@ -5704,11 +5812,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); } @@ -5720,7 +5828,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); } @@ -5729,14 +5837,14 @@ 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); } ; rt4_hdr_field : LAST_ENT { $$ = RT4HDR_LASTENT; } - | FLAGS { $$ = RT4HDR_FLAGS; } + | FLAGS close_scope_flags { $$ = RT4HDR_FLAGS; } | TAG { $$ = RT4HDR_TAG; } | SID '[' NUM ']' { @@ -5744,7 +5852,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); } @@ -5758,7 +5866,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); } @@ -5768,7 +5876,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); } @@ -5776,7 +5884,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; } ; @@ -5795,11 +5903,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 7a2d30ff..fb401009 100644 --- a/src/parser_json.c +++ b/src/parser_json.c @@ -689,7 +689,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; @@ -698,7 +698,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, @@ -1903,6 +1903,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) { @@ -2630,6 +2652,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 { @@ -2647,6 +2677,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 }, @@ -2665,6 +2696,7 @@ 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 }, }; const char *type; unsigned int i; @@ -3102,7 +3134,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; } @@ -3118,7 +3152,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; @@ -3131,7 +3167,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}", @@ -3160,14 +3196,15 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx, if (op == CMD_DELETE || op == CMD_LIST) 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}", &devs); + hookstr = chain_hookname_lookup(hook); if (!hookstr) { json_error(ctx, "Invalid flowtable hook '%s'.", hook); @@ -3182,12 +3219,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); } diff --git a/src/payload.c b/src/payload.c index d9e0d425..66418cdd 100644 --- a/src/payload.c +++ b/src/payload.c @@ -124,11 +124,17 @@ static void payload_expr_pctx_update(struct proto_ctx *ctx, #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_MAX 5 static unsigned int expr_payload_type(const struct proto_desc *desc, const struct proto_hdr_template *tmpl) { + if (desc->id == PROTO_DESC_UNKNOWN) + return 0; + return (unsigned int)(tmpl - &desc->templates[0]); } @@ -142,6 +148,15 @@ 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); + return 0; } @@ -159,6 +174,9 @@ 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: if (len != sizeof(uint32_t)) return -1; break; @@ -173,8 +191,10 @@ static int payload_parse_udata(const struct nftnl_udata *attr, void *data) static struct expr *payload_expr_parse_udata(const struct nftnl_udata *attr) { const struct nftnl_udata *ud[NFTNL_UDATA_SET_KEY_PAYLOAD_MAX + 1] = {}; + unsigned int type, base, offset, len = 0; const struct proto_desc *desc; - unsigned int type; + bool is_raw = false; + struct expr *expr; int err; err = nftnl_udata_parse(nftnl_udata_get(attr), nftnl_udata_len(attr), @@ -187,12 +207,40 @@ 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); + return expr; } const struct expr_ops payload_expr_ops = { @@ -610,8 +658,7 @@ void payload_dependency_store(struct payload_dep_ctx *ctx, if (ignore_dep) return; - ctx->pdep = stmt; - ctx->pbase = base + 1; + ctx->pdeps[base + 1] = stmt; } /** @@ -626,20 +673,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) @@ -661,7 +741,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; @@ -678,9 +758,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; @@ -697,7 +779,7 @@ static bool payload_may_dependency_kill_ll(struct payload_dep_ctx *ctx, struct e static bool payload_may_dependency_kill(struct payload_dep_ctx *ctx, unsigned int family, struct expr *expr) { - struct expr *dep = ctx->pdep->expr; + struct expr *dep = payload_dependency_get(ctx, expr->payload.base); /* Protocol key payload expression at network base such as 'ip6 nexthdr' * need to be left in place since it implicitly restricts matching to @@ -733,13 +815,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; } @@ -759,7 +845,7 @@ void payload_dependency_kill(struct payload_dep_ctx *ctx, struct expr *expr, { if (payload_dependency_exists(ctx, expr->payload.base) && payload_may_dependency_kill(ctx, family, expr)) - payload_dependency_release(ctx); + payload_dependency_release(ctx, expr->payload.base); } void exthdr_dependency_kill(struct payload_dep_ctx *ctx, struct expr *expr, @@ -768,15 +854,15 @@ 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; @@ -811,6 +897,9 @@ void payload_expr_complete(struct expr *expr, const struct proto_ctx *ctx) 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; diff --git a/src/proto.c b/src/proto.c index fe58c83a..a013a00d 100644 --- a/src/proto.c +++ b/src/proto.c @@ -60,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; } @@ -78,6 +80,8 @@ 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; } @@ -106,6 +110,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; 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; -} @@ -26,6 +26,7 @@ #include <json.h> #include <cache.h> #include <owner.h> +#include <intervals.h> #include <libnftnl/common.h> #include <libnftnl/ruleset.h> @@ -758,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) { @@ -1472,16 +1476,6 @@ static int __do_add_elements(struct netlink_ctx *ctx, struct set *set, if (mnl_nft_setelem_add(ctx, 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; } @@ -1492,9 +1486,7 @@ 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); @@ -1519,9 +1511,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; } @@ -1598,12 +1588,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->handle, cmd->elem.expr) < 0) return -1; return 0; diff --git a/src/scanner.l b/src/scanner.l index 6cc7778d..2154281e 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -197,27 +197,57 @@ 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_FLAGS +%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_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 +288,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 +302,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 +331,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 +339,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_FLAGS,SCANSTATE_IP,SCANSTATE_IP6>"to" { return TO; } /* XXX: SCANSTATE_FLAGS and SCANSTATE_IP here are workarounds */ "inet" { return INET; } "netdev" { return NETDEV; } @@ -319,30 +352,37 @@ 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; } "position" { return POSITION; } "index" { return INDEX; } "comment" { return COMMENT; } -"constant" { return CONSTANT; } +<SCANSTATE_FLAGS>{ + "constant" { return CONSTANT; } + "dynamic" { return DYNAMIC; } + + /* log flags */ + "all" { return ALL; } +} "interval" { return INTERVAL; } -"dynamic" { return DYNAMIC; } "auto-merge" { return AUTOMERGE; } "timeout" { return TIMEOUT; } "gc-interval" { return GC_INTERVAL; } "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,30 +392,33 @@ 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; } +} "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>"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;} -<SCANSTATE_EXPR_QUEUE>{ +<SCANSTATE_FLAGS,SCANSTATE_EXPR_QUEUE>{ "num" { return QUEUENUM;} "bypass" { return BYPASS;} "fanout" { return FANOUT;} @@ -384,6 +427,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 +441,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 +474,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 +497,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>{ + "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>"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 +527,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 +596,39 @@ 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; } -"flags" { return FLAGS; } -"cpi" { return CPI; } +"comp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_COMP); return COMP; } +<SCANSTATE_EXPR_COMP>{ + "cpi" { return CPI; } +} +"flags" { scanner_push_start_cond(yyscanner, SCANSTATE_FLAGS); return FLAGS; } -"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; } +} -"tcp" { return TCP; } -"ackseq" { return ACKSEQ; } -"doff" { return DOFF; } -"window" { return WINDOW; } -"urgptr" { return URGPTR; } -"option" { return OPTION; } +"tcp" { scanner_push_start_cond(yyscanner, SCANSTATE_TCP); return TCP; } -"dccp" { return DCCP; } +"dccp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_DCCP); return DCCP; } "sctp" { scanner_push_start_cond(yyscanner, SCANSTATE_SCTP); return SCTP; } @@ -561,6 +657,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 +675,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 +715,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 +744,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 +762,30 @@ 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; } @@ -1024,7 +1133,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); diff --git a/src/segtree.c b/src/segtree.c index f721954f..f9cac373 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) +static enum byteorder get_key_byteorder(const struct expr *e) { - 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] + enum datatypes basetype = expr_basetype(e)->type; + + switch (basetype) { + case TYPE_INTEGER: + /* For ranges, integers MUST be in BYTEORDER_BIG_ENDIAN. + * If the LHS (lookup key, e.g. 'meta mark', is host endian, + * a byteorder expression is injected to convert the register + * content before lookup. */ - if (segtree_debug(tree->debug_mask)) - pr_gmp_debug("split [%Zx %Zx]\n", lei->left, lei->right); - - ei_remove(tree, lei); - - mpz_sub_ui(p, new->left, 1); - if (mpz_cmp(lei->left, p) <= 0) - __ei_insert(tree, ei_alloc(lei->left, p, lei->expr, 0)); - - mpz_add_ui(p, new->right, 1); - if (mpz_cmp(p, rei->right) < 0) - __ei_insert(tree, ei_alloc(p, rei->right, lei->expr, 0)); - ei_destroy(lei); - } else { - if (lei != NULL) { - if (!merge) { - ei = lei; - goto err; - } - /* - * Left endpoint is within lei, adjust it so we have: - * - * [lei_left, new_left)[new_left, new_right] - */ - if (segtree_debug(tree->debug_mask)) { - pr_gmp_debug("adjust left [%Zx %Zx]\n", - lei->left, lei->right); - } - - mpz_sub_ui(lei->right, new->left, 1); - mpz_sub(lei->size, lei->right, lei->left); - if (mpz_sgn(lei->size) < 0) { - ei_remove(tree, lei); - ei_destroy(lei); - } - } - if (rei != NULL) { - if (!merge) { - ei = rei; - goto err; - } - /* - * Right endpoint is within rei, adjust it so we have: - * - * [new_left, new_right](new_right, rei_right] - */ - if (segtree_debug(tree->debug_mask)) { - pr_gmp_debug("adjust right [%Zx %Zx]\n", - rei->left, rei->right); - } - - mpz_add_ui(rei->left, new->right, 1); - mpz_sub(rei->size, rei->right, rei->left); - if (mpz_sgn(rei->size) < 0) { - ei_remove(tree, rei); - ei_destroy(rei); - } - } - } - - __ei_insert(tree, new); -out: - mpz_clear(p); - - return 0; -err: - mpz_clear(p); - errno = EEXIST; - if (new->expr->etype == EXPR_MAPPING) { - new_expr = new->expr->left; - expr = ei->expr->left; - } else { - new_expr = new->expr; - expr = ei->expr; - } - - return expr_binary_error(msgs, new_expr, expr, - "conflicting intervals specified"); -} - -/* - * Sort intervals according to their priority, which is defined inversely to - * their size. - * - * The beginning of the interval is used as secondary sorting criterion. This - * makes sure that overlapping ranges with equal priority are next to each - * other, allowing to easily detect unsolvable conflicts during insertion. - * - * Note: unsolvable conflicts can only occur when using ranges or two identical - * prefix specifications. - */ -static int interval_cmp(const void *p1, const void *p2) -{ - const struct elementary_interval *e1 = *(void * const *)p1; - const struct elementary_interval *e2 = *(void * const *)p2; - mpz_t d; - int ret; - - mpz_init(d); - - mpz_sub(d, e2->size, e1->size); - if (mpz_cmp_ui(d, 0)) - ret = mpz_sgn(d); - else - ret = mpz_cmp(e1->left, e2->left); - - mpz_clear(d); - return ret; -} - -static unsigned int expr_to_intervals(const struct expr *set, - unsigned int keylen, - struct elementary_interval **intervals) -{ - struct elementary_interval *ei; - struct expr *i, *next; - unsigned int n; - mpz_t low, high; - - mpz_init2(low, keylen); - mpz_init2(high, keylen); - - /* - * Convert elements to intervals. - */ - n = 0; - list_for_each_entry_safe(i, next, &set->expressions, list) { - range_expr_value_low(low, i); - range_expr_value_high(high, i); - ei = ei_alloc(low, high, i, 0); - intervals[n++] = ei; - } - mpz_clear(high); - mpz_clear(low); - - return n; -} - -static bool intervals_match(const struct elementary_interval *e1, - const struct elementary_interval *e2) -{ - return mpz_cmp(e1->left, e2->left) == 0 && - mpz_cmp(e1->right, e2->right) == 0; -} - -/* This function checks for overlaps in two ways: - * - * 1) A new interval end intersects an existing interval. - * 2) New intervals that are larger than existing ones, that don't intersect - * at all, but that wrap the existing ones. - */ -static bool interval_overlap(const struct elementary_interval *e1, - const struct elementary_interval *e2) -{ - if (intervals_match(e1, e2)) - return false; - - return (mpz_cmp(e1->left, e2->left) >= 0 && - mpz_cmp(e1->left, e2->right) <= 0) || - (mpz_cmp(e1->right, e2->left) >= 0 && - mpz_cmp(e1->right, e2->right) <= 0) || - (mpz_cmp(e1->left, e2->left) <= 0 && - mpz_cmp(e1->right, e2->right) >= 0); -} - -static int set_overlap(struct list_head *msgs, const struct set *set, - struct expr *init, unsigned int keylen, bool add) -{ - struct elementary_interval *new_intervals[init->size + 1]; - struct elementary_interval *intervals[set->init->size + 1]; - unsigned int n, m, i, j; - int ret = 0; - - n = expr_to_intervals(init, keylen, new_intervals); - m = expr_to_intervals(set->init, keylen, intervals); - - for (i = 0; i < n; i++) { - bool found = false; - - for (j = 0; j < m; j++) { - if (add && interval_overlap(new_intervals[i], - intervals[j])) { - expr_error(msgs, new_intervals[i]->expr, - "interval overlaps with an existing one"); - errno = EEXIST; - ret = -1; - goto out; - } else if (!add && intervals_match(new_intervals[i], - intervals[j])) { - found = true; - break; - } - } - if (!add && !found) { - expr_error(msgs, new_intervals[i]->expr, - "interval not found in set"); - errno = ENOENT; - ret = -1; - break; - } - } -out: - for (i = 0; i < n; i++) - ei_destroy(new_intervals[i]); - for (i = 0; i < m; i++) - ei_destroy(intervals[i]); - - return ret; -} - -static int set_to_segtree(struct list_head *msgs, struct set *set, - struct expr *init, struct seg_tree *tree, - bool add, bool merge) -{ - struct elementary_interval **intervals; - struct expr *i, *next; - unsigned int n, m; - int err = 0; - - /* We are updating an existing set with new elements, check if the new - * interval overlaps with any of the existing ones. - */ - if (set->init && set->init != init) { - err = set_overlap(msgs, set, init, tree->keylen, add); - if (err < 0) - return err; - } - - intervals = xmalloc_array(init->size, sizeof(intervals[0])); - n = expr_to_intervals(init, tree->keylen, intervals); - - list_for_each_entry_safe(i, next, &init->expressions, list) { - list_del(&i->list); - expr_free(i); - } - - /* - * Sort intervals by priority. - */ - qsort(intervals, n, sizeof(intervals[0]), interval_cmp); - - /* - * Insert elements into tree - */ - for (n = 0; n < init->size; n++) { - err = ei_insert(msgs, tree, intervals[n], merge); - if (err < 0) { - struct elementary_interval *ei; - struct rb_node *node, *next; - - for (m = n; m < init->size; m++) - ei_destroy(intervals[m]); - - rb_for_each_entry_safe(ei, node, next, &tree->root, rb_node) { - ei_remove(tree, ei); - ei_destroy(ei); - } - break; - } - } - - xfree(intervals); - return err; -} - -static bool segtree_needs_first_segment(const struct set *set, - const struct expr *init, bool add) -{ - 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; -} - -static void segtree_linearize(struct list_head *list, const struct set *set, - const struct expr *init, struct seg_tree *tree, - bool add, bool merge) -{ - bool needs_first_segment = segtree_needs_first_segment(set, init, add); - struct elementary_interval *ei, *nei, *prev = NULL; - struct rb_node *node, *next; - mpz_t p, q; - - mpz_init2(p, tree->keylen); - mpz_init2(q, tree->keylen); - - /* - * Convert the tree of open intervals to half-closed map expressions. - */ - rb_for_each_entry_safe(ei, node, next, &tree->root, rb_node) { - if (segtree_debug(tree->debug_mask)) - pr_gmp_debug("iter: [%Zx %Zx]\n", ei->left, ei->right); - - if (prev == NULL) { - /* - * If the first segment doesn't begin at zero, insert a - * non-matching segment to cover [0, first_left). - */ - if (needs_first_segment && mpz_cmp_ui(ei->left, 0)) { - mpz_set_ui(p, 0); - mpz_sub_ui(q, ei->left, 1); - nei = ei_alloc(p, q, NULL, EI_F_INTERVAL_END); - list_add_tail(&nei->list, list); - } - } else { - /* - * If the previous segment doesn't end directly left to - * this one, insert a non-matching segment to cover - * (prev_right, ei_left). - */ - mpz_add_ui(p, prev->right, 1); - if (mpz_cmp(p, ei->left) < 0 || - (!set_is_anonymous(set->flags) && !merge)) { - mpz_sub_ui(q, ei->left, 1); - nei = ei_alloc(p, q, NULL, EI_F_INTERVAL_END); - list_add_tail(&nei->list, list); - } else if (add && merge && - ei->expr->etype != EXPR_MAPPING) { - /* Merge contiguous segments only in case of - * new additions. - */ - mpz_set(prev->right, ei->right); - ei_remove(tree, ei); - ei_destroy(ei); - continue; - } - } - - ei_remove(tree, ei); - list_add_tail(&ei->list, list); - - prev = ei; - } - - /* - * If the last segment doesn't end at the right side of the dimension, - * insert a non-matching segment to cover (last_right, end]. - */ - if (mpz_scan0(prev->right, 0) != tree->keylen) { - mpz_add_ui(p, prev->right, 1); - mpz_bitmask(q, tree->keylen); - nei = ei_alloc(p, q, NULL, EI_F_INTERVAL_END); - list_add_tail(&nei->list, list); - } else { - prev->flags |= EI_F_INTERVAL_OPEN; + return BYTEORDER_BIG_ENDIAN; + case TYPE_STRING: + return BYTEORDER_HOST_ENDIAN; + default: + break; } - mpz_clear(p); - mpz_clear(q); + return BYTEORDER_INVALID; } static void interval_expr_copy(struct expr *dst, struct expr *src) @@ -600,88 +53,6 @@ static void interval_expr_copy(struct expr *dst, struct expr *src) list_splice_init(&src->stmt_list, &dst->stmt_list); } -static void set_insert_interval(struct expr *set, struct seg_tree *tree, - const struct elementary_interval *ei) -{ - struct expr *expr; - - expr = constant_expr_alloc(&internal_location, tree->keytype, - tree->byteorder, tree->keylen, NULL); - mpz_set(expr->value, ei->left); - expr = set_elem_expr_alloc(&internal_location, expr); - - if (ei->expr != NULL) { - if (ei->expr->etype == EXPR_MAPPING) { - interval_expr_copy(expr, ei->expr->left); - expr = mapping_expr_alloc(&ei->expr->location, expr, - expr_get(ei->expr->right)); - } else { - interval_expr_copy(expr, ei->expr); - } - } - - if (ei->flags & EI_F_INTERVAL_END) - expr->flags |= EXPR_F_INTERVAL_END; - if (ei->flags & EI_F_INTERVAL_OPEN) - expr->elem_flags |= NFTNL_SET_ELEM_F_INTERVAL_OPEN; - - compound_expr_add(set, expr); -} - -int set_to_intervals(struct list_head *errs, struct set *set, - struct expr *init, bool add, unsigned int debug_mask, - bool merge, struct output_ctx *octx) -{ - struct expr *catchall = NULL, *i, *in, *key; - struct elementary_interval *ei, *next; - struct seg_tree tree; - LIST_HEAD(list); - - list_for_each_entry_safe(i, in, &init->expressions, list) { - if (i->etype == EXPR_MAPPING) - key = i->left->key; - else if (i->etype == EXPR_SET_ELEM) - key = i->key; - else - continue; - - if (key->etype == EXPR_SET_ELEM_CATCHALL) { - init->size--; - catchall = i; - list_del(&i->list); - break; - } - } - - seg_tree_init(&tree, set, init, debug_mask); - if (set_to_segtree(errs, set, init, &tree, add, merge) < 0) - return -1; - segtree_linearize(&list, set, init, &tree, add, merge); - - init->size = 0; - list_for_each_entry_safe(ei, next, &list, list) { - if (segtree_debug(tree.debug_mask)) { - pr_gmp_debug("list: [%.*Zx %.*Zx]\n", - 2 * tree.keylen / BITS_PER_BYTE, ei->left, - 2 * tree.keylen / BITS_PER_BYTE, ei->right); - } - set_insert_interval(init, &tree, ei); - ei_destroy(ei); - } - - if (segtree_debug(tree.debug_mask)) { - expr_print(init, octx); - pr_gmp_debug("\n"); - } - - if (catchall) { - list_add_tail(&catchall->list, &init->expressions); - init->size++; - } - - return 0; -} - static void set_elem_add(const struct set *set, struct expr *init, mpz_t value, uint32_t flags, enum byteorder byteorder) { @@ -698,6 +69,7 @@ static void set_elem_add(const struct set *set, struct expr *init, mpz_t value, struct expr *get_set_intervals(const struct set *set, const struct expr *init) { + enum byteorder byteorder = get_key_byteorder(set->key); struct expr *new_init; mpz_t low, high; struct expr *i; @@ -711,7 +83,7 @@ struct expr *get_set_intervals(const struct set *set, const struct expr *init) switch (i->key->etype) { case EXPR_VALUE: set_elem_add(set, new_init, i->key->value, - i->flags, i->byteorder); + i->flags, byteorder); break; case EXPR_CONCAT: compound_expr_add(new_init, expr_clone(i)); @@ -751,6 +123,12 @@ static struct expr *get_set_interval_find(const struct set *cache_set, list_for_each_entry(i, &set->init->expressions, list) { switch (i->key->etype) { + case EXPR_VALUE: + if (expr_basetype(i->key)->type != TYPE_STRING) + break; + /* string type, check if its a range (wildcard), so + * fall through. + */ case EXPR_PREFIX: case EXPR_RANGE: range_expr_value_low(val, i); @@ -773,6 +151,60 @@ out: return range; } +static struct expr *expr_value(struct expr *expr) +{ + switch (expr->etype) { + case EXPR_MAPPING: + return expr->left->key; + case EXPR_SET_ELEM: + return expr->key; + default: + BUG("invalid expression type %s\n", expr_name(expr)); + } +} + +static struct expr *__expr_to_set_elem(struct expr *low, struct expr *expr) +{ + struct expr *elem = set_elem_expr_alloc(&low->location, expr); + + if (low->etype == EXPR_MAPPING) { + interval_expr_copy(elem, low->left); + + elem = mapping_expr_alloc(&low->location, elem, + expr_clone(low->right)); + } else { + interval_expr_copy(elem, low); + } + elem->flags |= EXPR_F_KERNEL; + + return elem; +} + +static struct expr *expr_to_set_elem(struct expr *e) +{ + unsigned int len = div_round_up(e->len, BITS_PER_BYTE); + unsigned int str_len; + char data[len + 1]; + struct expr *expr; + + if (expr_basetype(expr_value(e))->type != TYPE_STRING) + return expr_clone(e); + + mpz_export_data(data, expr_value(e)->value, BYTEORDER_BIG_ENDIAN, len); + + str_len = strnlen(data, len); + if (str_len >= len || str_len == 0) + return expr_clone(e); + + data[str_len] = '*'; + + expr = constant_expr_alloc(&e->location, e->dtype, + BYTEORDER_HOST_ENDIAN, + (str_len + 1) * BITS_PER_BYTE, data); + + return __expr_to_set_elem(e, expr); +} + int get_set_decompose(struct set *cache_set, struct set *set) { struct expr *i, *next, *range; @@ -788,6 +220,8 @@ int get_set_decompose(struct set *cache_set, struct set *set) mpz_sub_ui(i->key->value, i->key->value, 1); range = get_set_interval_find(cache_set, left, i); if (!range) { + expr_free(left); + expr_free(i); expr_free(new_init); errno = ENOENT; return -1; @@ -805,7 +239,7 @@ int get_set_decompose(struct set *cache_set, struct set *set) compound_expr_add(new_init, range); else compound_expr_add(new_init, - expr_clone(left)); + expr_to_set_elem(left)); } left = i; } @@ -815,7 +249,7 @@ int get_set_decompose(struct set *cache_set, struct set *set) if (range) compound_expr_add(new_init, range); else - compound_expr_add(new_init, expr_clone(left)); + compound_expr_add(new_init, expr_to_set_elem(left)); } expr_free(set->init); @@ -837,18 +271,6 @@ static bool range_is_prefix(const mpz_t range) return ret; } -static struct expr *expr_value(struct expr *expr) -{ - switch (expr->etype) { - case EXPR_MAPPING: - return expr->left->key; - case EXPR_SET_ELEM: - return expr->key; - default: - BUG("invalid expression type %s\n", expr_name(expr)); - } -} - static int expr_value_cmp(const void *p1, const void *p2) { struct expr *e1 = *(void * const *)p1; @@ -923,6 +345,8 @@ void concat_range_aggregate(struct expr *set) list_for_each_entry_safe(r1, r1_next, &expr_value(start)->expressions, list) { + bool string_type = false; + mpz_init(range); mpz_init(p); @@ -934,16 +358,48 @@ void concat_range_aggregate(struct expr *set) goto next; } + if (expr_basetype(r1)->type == TYPE_STRING && + expr_basetype(r2)->type == TYPE_STRING) { + string_type = true; + mpz_switch_byteorder(r1->value, r1->len / BITS_PER_BYTE); + mpz_switch_byteorder(r2->value, r2->len / BITS_PER_BYTE); + } + mpz_sub(range, r2->value, r1->value); mpz_sub_ui(range, range, 1); mpz_and(p, r1->value, range); /* Check if we are forced, or if it's anyway preferable, - * to express the range as two points instead of a - * netmask. + * to express the range as a wildcard string, or two points + * instead of a netmask. */ prefix_len = range_mask_len(r1->value, r2->value, r1->len); + if (string_type) { + mpz_switch_byteorder(r1->value, r1->len / BITS_PER_BYTE); + mpz_switch_byteorder(r2->value, r2->len / BITS_PER_BYTE); + } + + if (prefix_len >= 0 && + (prefix_len % BITS_PER_BYTE) == 0 && + string_type) { + unsigned int str_len = prefix_len / BITS_PER_BYTE; + char data[str_len + 2]; + + mpz_export_data(data, r1->value, BYTEORDER_HOST_ENDIAN, str_len); + data[str_len] = '*'; + + tmp = constant_expr_alloc(&r1->location, r1->dtype, + BYTEORDER_HOST_ENDIAN, + (str_len + 1) * BITS_PER_BYTE, data); + tmp->len = r2->len; + list_replace(&r2->list, &tmp->list); + r2_next = tmp->list.next; + expr_free(r2); + free_r1 = 1; + goto next; + } + if (prefix_len < 0 || !(r1->dtype->flags & DTYPE_F_PREFIX)) { tmp = range_expr_alloc(&r1->location, r1, @@ -978,6 +434,65 @@ next: } } +static struct expr *interval_to_prefix(struct expr *low, struct expr *i, const mpz_t range) +{ + unsigned int prefix_len; + struct expr *prefix; + + prefix_len = expr_value(i)->len - mpz_scan0(range, 0); + prefix = prefix_expr_alloc(&low->location, + expr_clone(expr_value(low)), + prefix_len); + prefix->len = expr_value(i)->len; + + return __expr_to_set_elem(low, prefix); +} + +static struct expr *interval_to_string(struct expr *low, struct expr *i, const mpz_t range) +{ + unsigned int len = div_round_up(i->len, BITS_PER_BYTE); + unsigned int prefix_len, str_len; + char data[len + 2]; + struct expr *expr; + + prefix_len = expr_value(i)->len - mpz_scan0(range, 0); + + if (prefix_len > i->len || prefix_len % BITS_PER_BYTE) + return interval_to_prefix(low, i, range); + + mpz_export_data(data, expr_value(low)->value, BYTEORDER_BIG_ENDIAN, len); + + str_len = strnlen(data, len); + if (str_len >= len || str_len == 0) + return interval_to_prefix(low, i, range); + + data[str_len] = '*'; + + expr = constant_expr_alloc(&low->location, low->dtype, + BYTEORDER_HOST_ENDIAN, + (str_len + 1) * BITS_PER_BYTE, data); + + return __expr_to_set_elem(low, expr); +} + +static struct expr *interval_to_range(struct expr *low, struct expr *i, mpz_t range) +{ + struct expr *tmp; + + tmp = constant_expr_alloc(&low->location, low->dtype, + low->byteorder, expr_value(low)->len, + NULL); + + mpz_add(range, range, expr_value(low)->value); + mpz_set(tmp->value, range); + + tmp = range_expr_alloc(&low->location, + expr_clone(expr_value(low)), + tmp); + + return __expr_to_set_elem(low, tmp); +} + void interval_map_decompose(struct expr *set) { struct expr *i, *next, *low = NULL, *end, *catchall = NULL, *key; @@ -1058,57 +573,26 @@ void interval_map_decompose(struct expr *set) mpz_and(p, expr_value(low)->value, range); - if (!mpz_cmp_ui(range, 0)) + if (!mpz_cmp_ui(range, 0)) { + if (expr_basetype(low)->type == TYPE_STRING) + mpz_switch_byteorder(expr_value(low)->value, low->len / BITS_PER_BYTE); + low->flags |= EXPR_F_KERNEL; compound_expr_add(set, expr_get(low)); - else if ((!range_is_prefix(range) || - !(i->dtype->flags & DTYPE_F_PREFIX)) || - mpz_cmp_ui(p, 0)) { - struct expr *tmp; - - tmp = constant_expr_alloc(&low->location, low->dtype, - low->byteorder, expr_value(low)->len, - NULL); + } else if (range_is_prefix(range) && !mpz_cmp_ui(p, 0)) { + struct expr *expr; - mpz_add(range, range, expr_value(low)->value); - mpz_set(tmp->value, range); - - tmp = range_expr_alloc(&low->location, - expr_clone(expr_value(low)), - tmp); - tmp = set_elem_expr_alloc(&low->location, tmp); - - if (low->etype == EXPR_MAPPING) { - interval_expr_copy(tmp, low->left); - - tmp = mapping_expr_alloc(&tmp->location, tmp, - expr_clone(low->right)); - } else { - interval_expr_copy(tmp, low); - } + if (i->dtype->flags & DTYPE_F_PREFIX) + expr = interval_to_prefix(low, i, range); + else if (expr_basetype(i)->type == TYPE_STRING) + expr = interval_to_string(low, i, range); + else + expr = interval_to_range(low, i, range); - compound_expr_add(set, tmp); + compound_expr_add(set, expr); } else { - struct expr *prefix; - unsigned int prefix_len; - - prefix_len = expr_value(i)->len - mpz_scan0(range, 0); - prefix = prefix_expr_alloc(&low->location, - expr_clone(expr_value(low)), - prefix_len); - prefix->len = expr_value(i)->len; - - prefix = set_elem_expr_alloc(&low->location, prefix); - - if (low->etype == EXPR_MAPPING) { - interval_expr_copy(prefix, low->left); + struct expr *expr = interval_to_range(low, i, range); - prefix = mapping_expr_alloc(&low->location, prefix, - expr_clone(low->right)); - } else { - interval_expr_copy(prefix, low); - } - - compound_expr_add(set, prefix); + compound_expr_add(set, expr); } if (i->flags & EXPR_F_INTERVAL_END) { @@ -1139,6 +623,8 @@ void interval_map_decompose(struct expr *set) } else { interval_expr_copy(i, low); } + i->flags |= EXPR_F_KERNEL; + expr_free(low); } diff --git a/src/statement.c b/src/statement.c index 03c0acf6..30caf9c7 100644 --- a/src/statement.c +++ b/src/statement.c @@ -23,6 +23,7 @@ #include <netinet/ip_icmp.h> #include <netinet/icmp6.h> #include <statement.h> +#include <tcpopt.h> #include <utils.h> #include <list.h> #include <xt.h> @@ -909,6 +910,37 @@ struct stmt *fwd_stmt_alloc(const struct location *loc) return stmt_alloc(loc, &fwd_stmt_ops); } +static void optstrip_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + const struct expr *expr = stmt->optstrip.expr; + + nft_print(octx, "reset "); + expr_print(expr, octx); +} + +static void optstrip_stmt_destroy(struct stmt *stmt) +{ + expr_free(stmt->optstrip.expr); +} + +static const struct stmt_ops optstrip_stmt_ops = { + .type = STMT_OPTSTRIP, + .name = "optstrip", + .print = optstrip_stmt_print, + .json = optstrip_stmt_json, + .destroy = optstrip_stmt_destroy, +}; + +struct stmt *optstrip_stmt_alloc(const struct location *loc, struct expr *e) +{ + struct stmt *stmt = stmt_alloc(loc, &optstrip_stmt_ops); + + e->exthdr.flags |= NFT_EXTHDR_F_PRESENT; + stmt->optstrip.expr = e; + + return stmt; +} + static void tproxy_stmt_print(const struct stmt *stmt, struct output_ctx *octx) { nft_print(octx, "tproxy"); diff --git a/src/tcpopt.c b/src/tcpopt.c index 53fe9bc8..c3e07d78 100644 --- a/src/tcpopt.c +++ b/src/tcpopt.c @@ -91,6 +91,34 @@ static const struct exthdr_desc tcpopt_timestamp = { }, }; +static const struct exthdr_desc tcpopt_fastopen = { + .name = "fastopen", + .type = TCPOPT_KIND_FASTOPEN, + .templates = { + [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8), + [TCPOPT_COMMON_LENGTH] = PHT("length", 8, 8), + }, +}; + +static const struct exthdr_desc tcpopt_md5sig = { + .name = "md5sig", + .type = TCPOPT_KIND_MD5SIG, + .templates = { + [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8), + [TCPOPT_COMMON_LENGTH] = PHT("length", 8, 8), + }, +}; + + +static const struct exthdr_desc tcpopt_mptcp = { + .name = "mptcp", + .type = TCPOPT_KIND_MPTCP, + .templates = { + [TCPOPT_MPTCP_KIND] = PHT("kind", 0, 8), + [TCPOPT_MPTCP_LENGTH] = PHT("length", 8, 8), + [TCPOPT_MPTCP_SUBTYPE] = PHT("subtype", 16, 4), + }, +}; #undef PHT const struct exthdr_desc *tcpopt_protocols[] = { @@ -101,6 +129,9 @@ const struct exthdr_desc *tcpopt_protocols[] = { [TCPOPT_KIND_SACK_PERMITTED] = &tcpopt_sack_permitted, [TCPOPT_KIND_SACK] = &tcpopt_sack, [TCPOPT_KIND_TIMESTAMP] = &tcpopt_timestamp, + [TCPOPT_KIND_MD5SIG] = &tcpopt_md5sig, + [TCPOPT_KIND_MPTCP] = &tcpopt_mptcp, + [TCPOPT_KIND_FASTOPEN] = &tcpopt_fastopen, }; /** @@ -194,6 +225,7 @@ void tcpopt_init_raw(struct expr *expr, uint8_t type, unsigned int off, expr->exthdr.flags = flags; expr->exthdr.offset = off; expr->exthdr.op = NFT_EXTHDR_OP_TCPOPT; + expr->exthdr.tmpl = &tcpopt_unknown_template; if (flags & NFT_EXTHDR_F_PRESENT) datatype_set(expr, &boolean_type); @@ -221,14 +253,12 @@ void tcpopt_init_raw(struct expr *expr, uint8_t type, unsigned int off, } } -bool tcpopt_find_template(struct expr *expr, const struct expr *mask, - unsigned int *shift) +bool tcpopt_find_template(struct expr *expr, unsigned int offset, unsigned int len) { if (expr->exthdr.tmpl != &tcpopt_unknown_template) return false; - tcpopt_init_raw(expr, expr->exthdr.desc->type, expr->exthdr.offset, - expr->len, 0); + tcpopt_init_raw(expr, expr->exthdr.desc->type, offset, len, 0); if (expr->exthdr.tmpl == &tcpopt_unknown_template) return false; diff --git a/tests/monitor/run-tests.sh b/tests/monitor/run-tests.sh index ff00450b..b5ca47d9 100755 --- a/tests/monitor/run-tests.sh +++ b/tests/monitor/run-tests.sh @@ -74,7 +74,7 @@ monitor_run_test() { echo "command file:" cat $command_file } - $nft -f $command_file || { + $nft -f - <$command_file || { err "nft command failed!" rc=1 } @@ -103,7 +103,7 @@ echo_run_test() { echo "command file:" cat $command_file } - $nft -nn -e -f $command_file >$echo_output || { + $nft -nn -e -f - <$command_file >$echo_output || { err "nft command failed!" rc=1 } diff --git a/tests/py/any/ct.t b/tests/py/any/ct.t index 8b8e68ab..f73fa4e7 100644 --- a/tests/py/any/ct.t +++ b/tests/py/any/ct.t @@ -144,3 +144,6 @@ ct set invalid original 42;fail ct set invalid 42;fail notrack;ok + +ct count 3;ok +ct count over 3;ok diff --git a/tests/py/any/ct.t.json b/tests/py/any/ct.t.json index 6684963b..a2a06025 100644 --- a/tests/py/any/ct.t.json +++ b/tests/py/any/ct.t.json @@ -1502,3 +1502,22 @@ } ] +# ct count 3 +[ + { + "ct count": { + "val": 3 + } + } +] + +# ct count over 3 +[ + { + "ct count": { + "inv": true, + "val": 3 + } + } +] + diff --git a/tests/py/any/ct.t.payload b/tests/py/any/ct.t.payload index 733276e1..ed868e53 100644 --- a/tests/py/any/ct.t.payload +++ b/tests/py/any/ct.t.payload @@ -508,3 +508,11 @@ ip6 [ bitwise reg 1 = ( reg 1 & 0x00000020 ) ^ 0x00000000 ] [ cmp eq reg 1 0x00000000 ] +# ct count 3 +ip test-ip4 output + [ connlimit count 3 flags 0 ] + +# ct count over 3 +ip test-ip4 output + [ connlimit count 3 flags 1 ] + diff --git a/tests/py/any/meta.t b/tests/py/any/meta.t index fcf4292d..12fabb79 100644 --- a/tests/py/any/meta.t +++ b/tests/py/any/meta.t @@ -208,6 +208,8 @@ meta time "2019-06-21 17:00:00" drop;ok meta time "2019-07-01 00:00:00" drop;ok meta time "2019-07-01 00:01:00" drop;ok meta time "2019-07-01 00:00:01" drop;ok +meta time < "2022-07-01 11:00:00" accept;ok +meta time > "2022-07-01 11:00:00" accept;ok meta day "Saturday" drop;ok meta day 6 drop;ok;meta day "Saturday" drop meta day "Satturday" drop;fail @@ -216,6 +218,8 @@ meta hour "17:00:00" drop;ok;meta hour "17:00" drop meta hour "17:00:01" drop;ok meta hour "00:00" drop;ok meta hour "00:01" drop;ok +time < "2022-07-01 11:00:00" accept;ok;meta time < "2022-07-01 11:00:00" accept +time > "2022-07-01 11:00:00" accept;ok;meta time > "2022-07-01 11:00:00" accept meta time "meh";fail meta hour "24:00" drop;fail diff --git a/tests/py/any/meta.t.json b/tests/py/any/meta.t.json index b140aaaa..4734bbf9 100644 --- a/tests/py/any/meta.t.json +++ b/tests/py/any/meta.t.json @@ -2561,6 +2561,42 @@ } ] +# meta time < "2022-07-01 11:00:00" accept +[ + { + "match": { + "left": { + "meta": { + "key": "time" + } + }, + "op": "<", + "right": "2022-07-01 11:00:00" + } + }, + { + "accept": null + } +] + +# meta time > "2022-07-01 11:00:00" accept +[ + { + "match": { + "left": { + "meta": { + "key": "time" + } + }, + "op": ">", + "right": "2022-07-01 11:00:00" + } + }, + { + "accept": null + } +] + # meta day "Saturday" drop [ { @@ -2686,3 +2722,39 @@ "drop": null } ] + +# time < "2022-07-01 11:00:00" accept +[ + { + "match": { + "left": { + "meta": { + "key": "time" + } + }, + "op": "<", + "right": "2022-07-01 11:00:00" + } + }, + { + "accept": null + } +] + +# time > "2022-07-01 11:00:00" accept +[ + { + "match": { + "left": { + "meta": { + "key": "time" + } + }, + "op": ">", + "right": "2022-07-01 11:00:00" + } + }, + { + "accept": null + } +] diff --git a/tests/py/any/meta.t.payload b/tests/py/any/meta.t.payload index d8351c27..16dc1211 100644 --- a/tests/py/any/meta.t.payload +++ b/tests/py/any/meta.t.payload @@ -1003,6 +1003,20 @@ ip meta-test input [ cmp eq reg 1 0x22eb8a00 0x15ad18e1 ] [ immediate reg 0 drop ] +# meta time < "2022-07-01 11:00:00" accept +ip test-ip4 input + [ meta load time => reg 1 ] + [ byteorder reg 1 = hton(reg 1, 8, 8) ] + [ cmp lt reg 1 0xf3a8fd16 0x00a07719 ] + [ immediate reg 0 accept ] + +# meta time > "2022-07-01 11:00:00" accept +ip test-ip4 input + [ meta load time => reg 1 ] + [ byteorder reg 1 = hton(reg 1, 8, 8) ] + [ cmp gt reg 1 0xf3a8fd16 0x00a07719 ] + [ immediate reg 0 accept ] + # meta day "Saturday" drop ip test-ip4 input [ meta load day => reg 1 ] @@ -1044,3 +1058,17 @@ ip meta-test input [ meta load hour => reg 1 ] [ cmp eq reg 1 0x0001359c ] [ immediate reg 0 drop ] + +# time < "2022-07-01 11:00:00" accept +ip test-ip4 input + [ meta load time => reg 1 ] + [ byteorder reg 1 = hton(reg 1, 8, 8) ] + [ cmp lt reg 1 0xf3a8fd16 0x00a07719 ] + [ immediate reg 0 accept ] + +# time > "2022-07-01 11:00:00" accept +ip test-ip4 input + [ meta load time => reg 1 ] + [ byteorder reg 1 = hton(reg 1, 8, 8) ] + [ cmp gt reg 1 0xf3a8fd16 0x00a07719 ] + [ immediate reg 0 accept ] diff --git a/tests/py/any/tcpopt.t b/tests/py/any/tcpopt.t index bcc64eac..177f01c4 100644 --- a/tests/py/any/tcpopt.t +++ b/tests/py/any/tcpopt.t @@ -4,17 +4,16 @@ *ip6;test-ip6;input *inet;test-inet;input -tcp option eol kind 1;ok -tcp option nop kind 1;ok -tcp option maxseg kind 1;ok +tcp option eol exists;ok +tcp option nop exists;ok +tcp option maxseg exists;ok tcp option maxseg length 1;ok tcp option maxseg size 1;ok -tcp option window kind 1;ok tcp option window length 1;ok tcp option window count 1;ok -tcp option sack-perm kind 1;ok +tcp option sack-perm exists;ok tcp option sack-perm length 1;ok -tcp option sack kind 1;ok +tcp option sack exists;ok tcp option sack length 1;ok tcp option sack left 1;ok tcp option sack0 left 1;ok;tcp option sack left 1 @@ -26,7 +25,7 @@ tcp option sack0 right 1;ok;tcp option sack right 1 tcp option sack1 right 1;ok tcp option sack2 right 1;ok tcp option sack3 right 1;ok -tcp option timestamp kind 1;ok +tcp option timestamp exists;ok tcp option timestamp length 1;ok tcp option timestamp tsval 1;ok tcp option timestamp tsecr 1;ok @@ -47,3 +46,17 @@ tcp option window exists;ok tcp option window missing;ok tcp option maxseg size set 1360;ok + +tcp option md5sig exists;ok +tcp option fastopen exists;ok +tcp option mptcp exists;ok + +tcp option mptcp subtype 0;ok +tcp option mptcp subtype 1;ok +tcp option mptcp subtype { 0, 2};ok + +reset tcp option mptcp;ok +reset tcp option 2;ok;reset tcp option maxseg +reset tcp option 123;ok +reset tcp option meh;fail +reset tcp option 256;fail diff --git a/tests/py/any/tcpopt.t.json b/tests/py/any/tcpopt.t.json index a45b4c8b..4466f14f 100644 --- a/tests/py/any/tcpopt.t.json +++ b/tests/py/any/tcpopt.t.json @@ -1,47 +1,44 @@ -# tcp option eol kind 1 +# tcp option eol exists [ { "match": { "left": { "tcp option": { - "field": "kind", "name": "eol" } }, "op": "==", - "right": 1 + "right": true } } ] -# tcp option nop kind 1 +# tcp option nop exists [ { "match": { "left": { "tcp option": { - "field": "kind", "name": "nop" } }, "op": "==", - "right": 1 + "right": true } } ] -# tcp option maxseg kind 1 +# tcp option maxseg exists [ { "match": { "left": { "tcp option": { - "field": "kind", "name": "maxseg" } }, "op": "==", - "right": 1 + "right": true } } ] @@ -78,22 +75,6 @@ } ] -# tcp option window kind 1 -[ - { - "match": { - "left": { - "tcp option": { - "field": "kind", - "name": "window" - } - }, - "op": "==", - "right": 1 - } - } -] - # tcp option window length 1 [ { @@ -126,18 +107,17 @@ } ] -# tcp option sack-perm kind 1 +# tcp option sack-perm exists [ { "match": { "left": { "tcp option": { - "field": "kind", "name": "sack-perm" } }, "op": "==", - "right": 1 + "right": true } } ] @@ -158,18 +138,17 @@ } ] -# tcp option sack kind 1 +# tcp option sack exists [ { "match": { "left": { "tcp option": { - "field": "kind", "name": "sack" } }, "op": "==", - "right": 1 + "right": true } } ] @@ -213,7 +192,7 @@ "left": { "tcp option": { "field": "left", - "name": "sack0" + "name": "sack" } }, "op": "==", @@ -293,7 +272,7 @@ "left": { "tcp option": { "field": "right", - "name": "sack0" + "name": "sack" } }, "op": "==", @@ -350,18 +329,17 @@ } ] -# tcp option timestamp kind 1 +# tcp option timestamp exists [ { "match": { "left": { "tcp option": { - "field": "kind", "name": "timestamp" } }, "op": "==", - "right": 1 + "right": true } } ] @@ -414,36 +392,36 @@ } ] -# tcp option 6 exists +# tcp option 255 missing [ { "match": { "left": { "tcp option": { - "base": 6, + "base": 255, "len": 8, "offset": 0 } }, "op": "==", - "right": true + "right": false } } ] -# tcp option 255 missing +# tcp option 6 exists [ { "match": { "left": { "tcp option": { - "base": 255, + "base": 6, "len": 8, "offset": 0 } }, "op": "==", - "right": false + "right": true } } ] @@ -510,3 +488,135 @@ } ] +# tcp option md5sig exists +[ + { + "match": { + "left": { + "tcp option": { + "name": "md5sig" + } + }, + "op": "==", + "right": true + } + } +] + +# tcp option fastopen exists +[ + { + "match": { + "left": { + "tcp option": { + "name": "fastopen" + } + }, + "op": "==", + "right": true + } + } +] + +# tcp option mptcp exists +[ + { + "match": { + "left": { + "tcp option": { + "name": "mptcp" + } + }, + "op": "==", + "right": true + } + } +] + +# tcp option mptcp subtype 0 +[ + { + "match": { + "left": { + "tcp option": { + "field": "subtype", + "name": "mptcp" + } + }, + "op": "==", + "right": 0 + } + } +] + +# tcp option mptcp subtype 1 +[ + { + "match": { + "left": { + "tcp option": { + "field": "subtype", + "name": "mptcp" + } + }, + "op": "==", + "right": 1 + } + } +] + +# tcp option mptcp subtype { 0, 2} +[ + { + "match": { + "left": { + "tcp option": { + "field": "subtype", + "name": "mptcp" + } + }, + "op": "==", + "right": { + "set": [ + 0, + 2 + ] + } + } + } +] + +# reset tcp option mptcp +[ + { + "reset": { + "tcp option": { + "name": "mptcp" + } + } + } +] + +# reset tcp option 2 +[ + { + "reset": { + "tcp option": { + "name": "maxseg" + } + } + } +] + +# reset tcp option 123 +[ + { + "reset": { + "tcp option": { + "base": 123, + "len": 0, + "offset": 0 + } + } + } +] diff --git a/tests/py/any/tcpopt.t.payload b/tests/py/any/tcpopt.t.payload index 51f3a752..99b8985f 100644 --- a/tests/py/any/tcpopt.t.payload +++ b/tests/py/any/tcpopt.t.payload @@ -1,16 +1,16 @@ -# tcp option eol kind 1 +# tcp option eol exists inet - [ exthdr load tcpopt 1b @ 0 + 0 => reg 1 ] + [ exthdr load tcpopt 1b @ 0 + 0 present => reg 1 ] [ cmp eq reg 1 0x00000001 ] -# tcp option nop kind 1 +# tcp option nop exists inet - [ exthdr load tcpopt 1b @ 1 + 0 => reg 1 ] + [ exthdr load tcpopt 1b @ 1 + 0 present => reg 1 ] [ cmp eq reg 1 0x00000001 ] -# tcp option maxseg kind 1 +# tcp option maxseg exists inet - [ exthdr load tcpopt 1b @ 2 + 0 => reg 1 ] + [ exthdr load tcpopt 1b @ 2 + 0 present => reg 1 ] [ cmp eq reg 1 0x00000001 ] # tcp option maxseg length 1 @@ -23,11 +23,6 @@ inet [ exthdr load tcpopt 2b @ 2 + 2 => reg 1 ] [ cmp eq reg 1 0x00000100 ] -# tcp option window kind 1 -inet - [ exthdr load tcpopt 1b @ 3 + 0 => reg 1 ] - [ cmp eq reg 1 0x00000001 ] - # tcp option window length 1 inet [ exthdr load tcpopt 1b @ 3 + 1 => reg 1 ] @@ -38,9 +33,9 @@ inet [ exthdr load tcpopt 1b @ 3 + 2 => reg 1 ] [ cmp eq reg 1 0x00000001 ] -# tcp option sack-perm kind 1 +# tcp option sack-perm exists inet - [ exthdr load tcpopt 1b @ 4 + 0 => reg 1 ] + [ exthdr load tcpopt 1b @ 4 + 0 present => reg 1 ] [ cmp eq reg 1 0x00000001 ] # tcp option sack-perm length 1 @@ -48,9 +43,9 @@ inet [ exthdr load tcpopt 1b @ 4 + 1 => reg 1 ] [ cmp eq reg 1 0x00000001 ] -# tcp option sack kind 1 +# tcp option sack exists inet - [ exthdr load tcpopt 1b @ 5 + 0 => reg 1 ] + [ exthdr load tcpopt 1b @ 5 + 0 present => reg 1 ] [ cmp eq reg 1 0x00000001 ] # tcp option sack length 1 @@ -108,9 +103,9 @@ inet [ exthdr load tcpopt 4b @ 5 + 30 => reg 1 ] [ cmp eq reg 1 0x01000000 ] -# tcp option timestamp kind 1 +# tcp option timestamp exists inet - [ exthdr load tcpopt 1b @ 8 + 0 => reg 1 ] + [ exthdr load tcpopt 1b @ 8 + 0 present => reg 1 ] [ cmp eq reg 1 0x00000001 ] # tcp option timestamp length 1 @@ -158,3 +153,50 @@ inet [ immediate reg 1 0x00005005 ] [ exthdr write tcpopt reg 1 => 2b @ 2 + 2 ] +# tcp option md5sig exists +inet + [ exthdr load tcpopt 1b @ 19 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + +# tcp option fastopen exists +inet + [ exthdr load tcpopt 1b @ 34 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + +# tcp option mptcp exists +inet + [ exthdr load tcpopt 1b @ 30 + 0 present => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + +# tcp option mptcp subtype 0 +inet + [ exthdr load tcpopt 1b @ 30 + 2 => reg 1 ] + [ bitwise reg 1 = ( reg 1 & 0x000000f0 ) ^ 0x00000000 ] + [ cmp eq reg 1 0x00000000 ] + +# tcp option mptcp subtype 1 +inet + [ exthdr load tcpopt 1b @ 30 + 2 => reg 1 ] + [ bitwise reg 1 = ( reg 1 & 0x000000f0 ) ^ 0x00000000 ] + [ cmp eq reg 1 0x00000010 ] + +# tcp option mptcp subtype { 0, 2} +__set%d test-inet 3 size 2 +__set%d test-inet 0 + element 00000000 : 0 [end] element 00000020 : 0 [end] +inet + [ exthdr load tcpopt 1b @ 30 + 2 => reg 1 ] + [ bitwise reg 1 = ( reg 1 & 0x000000f0 ) ^ 0x00000000 ] + [ lookup reg 1 set __set%d ] + +# reset tcp option mptcp +ip test-ip4 input + [ exthdr reset tcpopt 30 ] + +# reset tcp option 2 +ip test-ip4 input + [ exthdr reset tcpopt 2 ] + +# reset tcp option 123 +ip test-ip4 input + [ exthdr reset tcpopt 123 ] diff --git a/tests/py/inet/icmpX.t b/tests/py/inet/icmpX.t index 97ff96d0..9430b3d3 100644 --- a/tests/py/inet/icmpX.t +++ b/tests/py/inet/icmpX.t @@ -7,4 +7,4 @@ icmp type echo-request;ok ip6 nexthdr icmpv6 icmpv6 type echo-request;ok;ip6 nexthdr 58 icmpv6 type echo-request icmpv6 type echo-request;ok # must not remove 'ip protocol' dependency, this explicitly matches icmpv6-in-ipv4. -ip protocol ipv6-icmp meta l4proto ipv6-icmp icmpv6 type 1;ok;ip protocol 58 meta l4proto 58 icmpv6 type destination-unreachable +ip protocol ipv6-icmp meta l4proto ipv6-icmp icmpv6 type 1;ok;ip protocol 58 icmpv6 type destination-unreachable diff --git a/tests/py/inet/icmpX.t.json.output b/tests/py/inet/icmpX.t.json.output index 9b0bf9f7..7765cd90 100644 --- a/tests/py/inet/icmpX.t.json.output +++ b/tests/py/inet/icmpX.t.json.output @@ -71,15 +71,6 @@ { "match": { "left": { - "meta": { "key": "l4proto" } - }, - "op": "==", - "right": 58 - } - }, - { - "match": { - "left": { "payload": { "field": "type", "protocol": "icmpv6" diff --git a/tests/py/inet/ip.t.payload.bridge b/tests/py/inet/ip.t.payload.bridge index a422ed76..57dbc9eb 100644 --- a/tests/py/inet/ip.t.payload.bridge +++ b/tests/py/inet/ip.t.payload.bridge @@ -3,7 +3,7 @@ __set%d test-bridge 3 __set%d test-bridge 0 element 01010101 02020202 fecafeca 0000feca : 0 [end] bridge test-bridge input - [ payload load 2b @ link header + 12 => reg 1 ] + [ meta load protocol => reg 1 ] [ cmp eq reg 1 0x00000008 ] [ payload load 4b @ network header + 12 => reg 1 ] [ payload load 4b @ network header + 16 => reg 9 ] diff --git a/tests/py/inet/ip.t.payload.netdev b/tests/py/inet/ip.t.payload.netdev index 38ed0ad3..95be9194 100644 --- a/tests/py/inet/ip.t.payload.netdev +++ b/tests/py/inet/ip.t.payload.netdev @@ -12,17 +12,3 @@ netdev test-netdev ingress [ payload load 6b @ link header + 6 => reg 10 ] [ lookup reg 1 set __set%d ] -# meta protocol ip ip saddr . ip daddr . ether saddr { 1.1.1.1 . 2.2.2.2 . ca:fe:ca:fe:ca:fe } -__set%d test-netdev 3 -__set%d test-netdev 0 - element 01010101 02020202 fecafeca 0000feca : 0 [end] -netdev test-netdev egress - [ meta load protocol => reg 1 ] - [ cmp eq reg 1 0x00000008 ] - [ meta load iiftype => reg 1 ] - [ cmp eq reg 1 0x00000001 ] - [ payload load 4b @ network header + 12 => reg 1 ] - [ payload load 4b @ network header + 16 => reg 9 ] - [ payload load 6b @ link header + 6 => reg 10 ] - [ lookup reg 1 set __set%d ] - diff --git a/tests/py/inet/ip_tcp.t b/tests/py/inet/ip_tcp.t index ab76ffa9..03bafc09 100644 --- a/tests/py/inet/ip_tcp.t +++ b/tests/py/inet/ip_tcp.t @@ -9,8 +9,8 @@ # must not remove ip dependency -- ONLY ipv4 packets should be matched ip protocol tcp tcp dport 22;ok;ip protocol 6 tcp dport 22 -# can remove it here, ip protocol is implied via saddr. -ip protocol tcp ip saddr 1.2.3.4 tcp dport 22;ok;ip saddr 1.2.3.4 tcp dport 22 +# could in principle remove it here since ipv4 is implied via saddr. +ip protocol tcp ip saddr 1.2.3.4 tcp dport 22;ok;ip protocol 6 ip saddr 1.2.3.4 tcp dport 22 # but not here. ip protocol tcp counter ip saddr 1.2.3.4 tcp dport 22;ok;ip protocol 6 counter ip saddr 1.2.3.4 tcp dport 22 diff --git a/tests/py/inet/ip_tcp.t.json.output b/tests/py/inet/ip_tcp.t.json.output index 4a6a05d7..acad8b1f 100644 --- a/tests/py/inet/ip_tcp.t.json.output +++ b/tests/py/inet/ip_tcp.t.json.output @@ -32,6 +32,18 @@ "match": { "left": { "payload": { + "field": "protocol", + "protocol": "ip" + } + }, + "op": "==", + "right": 6 + } + }, + { + "match": { + "left": { + "payload": { "field": "saddr", "protocol": "ip" } diff --git a/tests/py/inet/reject.t b/tests/py/inet/reject.t index 1c8aeebe..61a6d556 100644 --- a/tests/py/inet/reject.t +++ b/tests/py/inet/reject.t @@ -37,3 +37,5 @@ meta l4proto udp reject with tcp reset;fail meta nfproto ipv4 reject with icmpx admin-prohibited;ok meta nfproto ipv6 reject with icmpx admin-prohibited;ok + +ether saddr aa:bb:cc:dd:ee:ff ip daddr 192.168.0.1 reject;ok;ether saddr aa:bb:cc:dd:ee:ff ip daddr 192.168.0.1 reject with icmp port-unreachable diff --git a/tests/py/inet/reject.t.json b/tests/py/inet/reject.t.json index 76cd1bf5..02ac9007 100644 --- a/tests/py/inet/reject.t.json +++ b/tests/py/inet/reject.t.json @@ -295,3 +295,37 @@ } ] +# ether saddr aa:bb:cc:dd:ee:ff ip daddr 192.168.0.1 reject +[ + { + "match": { + "left": { + "payload": { + "field": "saddr", + "protocol": "ether" + } + }, + "op": "==", + "right": "aa:bb:cc:dd:ee:ff" + } + }, + { + "match": { + "left": { + "payload": { + "field": "daddr", + "protocol": "ip" + } + }, + "op": "==", + "right": "192.168.0.1" + } + }, + { + "reject": { + "expr": "port-unreachable", + "type": "icmp" + } + } +] + diff --git a/tests/py/inet/reject.t.payload.inet b/tests/py/inet/reject.t.payload.inet index 62078d91..828cb839 100644 --- a/tests/py/inet/reject.t.payload.inet +++ b/tests/py/inet/reject.t.payload.inet @@ -132,3 +132,13 @@ inet test-inet input [ cmp eq reg 1 0x0000000a ] [ reject type 2 code 3 ] +# ether saddr aa:bb:cc:dd:ee:ff ip daddr 192.168.0.1 reject +inet test-inet input + [ meta load iiftype => reg 1 ] + [ cmp eq reg 1 0x00000001 ] + [ payload load 8b @ link header + 6 => reg 1 ] + [ cmp eq reg 1 0xddccbbaa 0x0008ffee ] + [ payload load 4b @ network header + 16 => reg 1 ] + [ cmp eq reg 1 0x0100a8c0 ] + [ reject type 0 code 3 ] + diff --git a/tests/py/inet/sets.t.json b/tests/py/inet/sets.t.json index ef0cedca..b44ffc20 100644 --- a/tests/py/inet/sets.t.json +++ b/tests/py/inet/sets.t.json @@ -76,17 +76,6 @@ { "match": { "left": { - "meta": { - "key": "nfproto" - } - }, - "op": "==", - "right": "ipv4" - } - }, - { - "match": { - "left": { "concat": [ { "payload": { diff --git a/tests/py/inet/sets.t.payload.netdev b/tests/py/inet/sets.t.payload.netdev index 9d6f6bbd..e31aeb92 100644 --- a/tests/py/inet/sets.t.payload.netdev +++ b/tests/py/inet/sets.t.payload.netdev @@ -15,9 +15,9 @@ netdev test-netdev ingress [ immediate reg 0 accept ] # ip saddr . ip daddr . tcp dport @set3 accept -inet - [ meta load nfproto => reg 1 ] - [ cmp eq reg 1 0x00000002 ] +netdev + [ meta load protocol => reg 1 ] + [ cmp eq reg 1 0x00000008 ] [ meta load l4proto => reg 1 ] [ cmp eq reg 1 0x00000006 ] [ payload load 4b @ network header + 12 => reg 1 ] diff --git a/tests/py/inet/vmap.t b/tests/py/inet/vmap.t new file mode 100644 index 00000000..0ac6e561 --- /dev/null +++ b/tests/py/inet/vmap.t @@ -0,0 +1,10 @@ +:input;type filter hook input priority 0 +:ingress;type filter hook ingress device lo priority 0 +:egress;type filter hook egress device lo priority 0 + +*inet;test-inet;input +*netdev;test-netdev;ingress,egress + +iifname . ip protocol . th dport vmap { "eth0" . tcp . 22 : accept, "eth1" . udp . 67 : drop };ok;iifname . ip protocol . th dport vmap { "eth0" . 6 . 22 : accept, "eth1" . 17 . 67 : drop } +ip saddr . @ih,32,32 { 1.1.1.1 . 0x14, 2.2.2.2 . 0x1e };ok +udp length . @th,160,128 vmap { 47-63 . 0xe373135363130333131303735353203 : accept };ok diff --git a/tests/py/inet/vmap.t.json b/tests/py/inet/vmap.t.json new file mode 100644 index 00000000..37472cc6 --- /dev/null +++ b/tests/py/inet/vmap.t.json @@ -0,0 +1,144 @@ +# iifname . ip protocol . th dport vmap { "eth0" . tcp . 22 : accept, "eth1" . udp . 67 : drop } +[ + { + "vmap": { + "data": { + "set": [ + [ + { + "concat": [ + "eth0", + 6, + 22 + ] + }, + { + "accept": null + } + ], + [ + { + "concat": [ + "eth1", + 17, + 67 + ] + }, + { + "drop": null + } + ] + ] + }, + "key": { + "concat": [ + { + "meta": { + "key": "iifname" + } + }, + { + "payload": { + "field": "protocol", + "protocol": "ip" + } + }, + { + "payload": { + "field": "dport", + "protocol": "th" + } + } + ] + } + } + } +] + +# ip saddr . @ih,32,32 { 1.1.1.1 . 0x14, 2.2.2.2 . 0x1e } +[ + { + "match": { + "left": { + "concat": [ + { + "payload": { + "field": "saddr", + "protocol": "ip" + } + }, + { + "payload": { + "base": "ih", + "len": 32, + "offset": 32 + } + } + ] + }, + "op": "==", + "right": { + "set": [ + { + "concat": [ + "1.1.1.1", + 20 + ] + }, + { + "concat": [ + "2.2.2.2", + 30 + ] + } + ] + } + } + } +] + +# udp length . @th,160,128 vmap { 47-63 . 0xe373135363130333131303735353203 : accept } +[ + { + "vmap": { + "data": { + "set": [ + [ + { + "concat": [ + { + "range": [ + 47, + 63 + ] + }, + "0xe373135363130333131303735353203" + ] + }, + { + "accept": null + } + ] + ] + }, + "key": { + "concat": [ + { + "payload": { + "field": "length", + "protocol": "udp" + } + }, + { + "payload": { + "base": "th", + "len": 128, + "offset": 160 + } + } + ] + } + } + } +] + diff --git a/tests/py/inet/vmap.t.payload b/tests/py/inet/vmap.t.payload new file mode 100644 index 00000000..29ec846d --- /dev/null +++ b/tests/py/inet/vmap.t.payload @@ -0,0 +1,34 @@ +# iifname . ip protocol . th dport vmap { "eth0" . tcp . 22 : accept, "eth1" . udp . 67 : drop } +__map%d test-inet b size 2 +__map%d test-inet 0 + element 30687465 00000000 00000000 00000000 00000006 00001600 : accept 0 [end] element 31687465 00000000 00000000 00000000 00000011 00004300 : drop 0 [end] +inet test-inet input + [ meta load nfproto => reg 1 ] + [ cmp eq reg 1 0x00000002 ] + [ meta load iifname => reg 1 ] + [ payload load 1b @ network header + 9 => reg 2 ] + [ payload load 2b @ transport header + 2 => reg 13 ] + [ lookup reg 1 set __map%d dreg 0 ] + +# ip saddr . @ih,32,32 { 1.1.1.1 . 0x14, 2.2.2.2 . 0x1e } +__set%d test-inet 3 size 2 +__set%d test-inet 0 + element 01010101 14000000 : 0 [end] element 02020202 1e000000 : 0 [end] +inet test-inet input + [ meta load nfproto => reg 1 ] + [ cmp eq reg 1 0x00000002 ] + [ payload load 4b @ network header + 12 => reg 1 ] + [ payload load 4b @ inner header + 4 => reg 9 ] + [ lookup reg 1 set __set%d ] + +# udp length . @th,160,128 vmap { 47-63 . 0xe373135363130333131303735353203 : accept } +__map%d x 8f size 1 +__map%d x 0 + element 00002f00 3531370e 33303136 37303131 03323535 - 00003f00 3531370e 33303136 37303131 03323535 : accept 0 [end] +inet x y + [ meta load l4proto => reg 1 ] + [ cmp eq reg 1 0x00000011 ] + [ payload load 2b @ transport header + 4 => reg 1 ] + [ payload load 16b @ transport header + 20 => reg 9 ] + [ lookup reg 1 set __map%d dreg 0 ] + diff --git a/tests/py/inet/vmap.t.payload.netdev b/tests/py/inet/vmap.t.payload.netdev new file mode 100644 index 00000000..3f51bb33 --- /dev/null +++ b/tests/py/inet/vmap.t.payload.netdev @@ -0,0 +1,34 @@ +# iifname . ip protocol . th dport vmap { "eth0" . tcp . 22 : accept, "eth1" . udp . 67 : drop } +__map%d test-netdev b size 2 +__map%d test-netdev 0 + element 30687465 00000000 00000000 00000000 00000006 00001600 : accept 0 [end] element 31687465 00000000 00000000 00000000 00000011 00004300 : drop 0 [end] +netdev test-netdev ingress + [ meta load protocol => reg 1 ] + [ cmp eq reg 1 0x00000008 ] + [ meta load iifname => reg 1 ] + [ payload load 1b @ network header + 9 => reg 2 ] + [ payload load 2b @ transport header + 2 => reg 13 ] + [ lookup reg 1 set __map%d dreg 0 ] + +# ip saddr . @ih,32,32 { 1.1.1.1 . 0x14, 2.2.2.2 . 0x1e } +__set%d test-netdev 3 size 2 +__set%d test-netdev 0 + element 01010101 14000000 : 0 [end] element 02020202 1e000000 : 0 [end] +netdev test-netdev ingress + [ meta load protocol => reg 1 ] + [ cmp eq reg 1 0x00000008 ] + [ payload load 4b @ network header + 12 => reg 1 ] + [ payload load 4b @ inner header + 4 => reg 9 ] + [ lookup reg 1 set __set%d ] + +# udp length . @th,160,128 vmap { 47-63 . 0xe373135363130333131303735353203 : accept } +__map%d test-netdev 8f size 1 +__map%d test-netdev 0 + element 00002f00 3531370e 33303136 37303131 03323535 - 00003f00 3531370e 33303136 37303131 03323535 : accept 0 [end] +netdev test-netdev ingress + [ meta load l4proto => reg 1 ] + [ cmp eq reg 1 0x00000011 ] + [ payload load 2b @ transport header + 4 => reg 1 ] + [ payload load 16b @ transport header + 20 => reg 9 ] + [ lookup reg 1 set __map%d dreg 0 ] + diff --git a/tests/py/nft-test.py b/tests/py/nft-test.py index f8f9341c..b66a33c2 100755 --- a/tests/py/nft-test.py +++ b/tests/py/nft-test.py @@ -39,7 +39,7 @@ signal_received = 0 class Colors: - if sys.stdout.isatty(): + if sys.stdout.isatty() and sys.stderr.isatty(): HEADER = '\033[95m' GREEN = '\033[92m' YELLOW = '\033[93m' @@ -809,17 +809,26 @@ def rule_add(rule, filename, lineno, force_all_family_option, filename_path): if state == "ok" and not payload_check(table_payload_expected, payload_log, cmd): error += 1 - gotf = open("%s.got" % payload_path, 'a') + + try: + gotf = open("%s.got" % payload_path) + gotf_payload_expected = payload_find_expected(gotf, rule[0]) + gotf.close() + except: + gotf_payload_expected = None payload_log.seek(0, 0) - gotf.write("# %s\n" % rule[0]) - while True: - line = payload_log.readline() - if line == "": - break - gotf.write(line) - gotf.close() - print_warning("Wrote payload for rule %s" % rule[0], - gotf.name, 1) + if not payload_check(gotf_payload_expected, payload_log, cmd): + gotf = open("%s.got" % payload_path, 'a') + payload_log.seek(0, 0) + gotf.write("# %s\n" % rule[0]) + while True: + line = payload_log.readline() + if line == "": + break + gotf.write(line) + gotf.close() + print_warning("Wrote payload for rule %s" % rule[0], + gotf.name, 1) # Check for matching ruleset listing numeric_proto_old = nftables.set_numeric_proto_output(True) diff --git a/tests/shell/testcases/cache/0010_implicit_chain_0 b/tests/shell/testcases/cache/0010_implicit_chain_0 new file mode 100755 index 00000000..0ab0db95 --- /dev/null +++ b/tests/shell/testcases/cache/0010_implicit_chain_0 @@ -0,0 +1,19 @@ +#!/bin/bash + +set -e + +EXPECTED="table ip f { + chain c { + jump { + accept + } + } +}" + +$NFT 'table ip f { chain c { jump { accept; }; }; }' +GET="$($NFT list chain ip f c)" + +if [ "$EXPECTED" != "$GET" ] ; then + $DIFF -u <(echo "$EXPECTED") <(echo "$GET") + exit 1 +fi diff --git a/tests/shell/testcases/chains/0040mark_shift_0 b/tests/shell/testcases/chains/0040mark_shift_0 index 55447f0b..ef3dccfa 100755 --- a/tests/shell/testcases/chains/0040mark_shift_0 +++ b/tests/shell/testcases/chains/0040mark_shift_0 @@ -8,4 +8,4 @@ RULESET=" add rule t c oif lo ct mark set (meta mark | 0x10) << 8 " -$NFT --debug=eval -f - <<< "$RULESET" +$NFT -f - <<< "$RULESET" diff --git a/tests/shell/testcases/chains/0041chain_binding_0 b/tests/shell/testcases/chains/0041chain_binding_0 index 59bdbe9f..4b541bb5 100755 --- a/tests/shell/testcases/chains/0041chain_binding_0 +++ b/tests/shell/testcases/chains/0041chain_binding_0 @@ -1,5 +1,11 @@ #!/bin/bash +# no table x, caused segfault in earlier nft releases +$NFT insert rule inet x y handle 107 'goto { log prefix "MOO! "; }' +if [ $? -ne 1 ]; then + exit 1 +fi + set -e EXPECTED="table inet x { diff --git a/tests/shell/testcases/listing/0020flowtable_0 b/tests/shell/testcases/listing/0020flowtable_0 index 2f0a98d1..47488d8e 100755 --- a/tests/shell/testcases/listing/0020flowtable_0 +++ b/tests/shell/testcases/listing/0020flowtable_0 @@ -2,19 +2,60 @@ # list only the flowtable asked for with table +FLOWTABLES="flowtable f { + hook ingress priority filter + devices = { lo } +} +flowtable f2 { + hook ingress priority filter + devices = { d0 } +}" + +RULESET="table inet filter { + $FLOWTABLES +} +table ip filter { + $FLOWTABLES +}" + EXPECTED="table inet filter { flowtable f { hook ingress priority filter devices = { lo } } }" +EXPECTED2="table ip filter { + flowtable f2 { + hook ingress priority filter + devices = { d0 } + } +}" +EXPECTED3="table ip filter { + flowtable f { + hook ingress priority filter + devices = { lo } + } + flowtable f2 { + hook ingress priority filter + devices = { d0 } + } +}" + +ip link add d0 type dummy || { + echo "Skipping, no dummy interface available" + exit 0 +} +trap "ip link del d0" EXIT set -e -$NFT -f - <<< "$EXPECTED" +$NFT -f - <<< "$RULESET" GET="$($NFT list flowtable inet filter f)" -if [ "$EXPECTED" != "$GET" ] ; then - $DIFF -u <(echo "$EXPECTED") <(echo "$GET") - exit 1 -fi +$DIFF -u <(echo "$EXPECTED") <(echo "$GET") + +GET="$($NFT list flowtable ip filter f2)" +$DIFF -u <(echo "$EXPECTED2") <(echo "$GET") + +GET="$($NFT list flowtables ip)" +$DIFF -u <(echo "$EXPECTED3") <(echo "$GET") diff --git a/tests/shell/testcases/maps/anon_objmap_concat b/tests/shell/testcases/maps/anon_objmap_concat new file mode 100755 index 00000000..07820b7c --- /dev/null +++ b/tests/shell/testcases/maps/anon_objmap_concat @@ -0,0 +1,6 @@ +#!/bin/bash + +set -e +dumpfile=$(dirname $0)/dumps/$(basename $0).nft + +$NFT -f "$dumpfile" diff --git a/tests/shell/testcases/maps/dumps/0010concat_map_0.nft b/tests/shell/testcases/maps/dumps/0010concat_map_0.nft index b6bc338c..2f796b51 100644 --- a/tests/shell/testcases/maps/dumps/0010concat_map_0.nft +++ b/tests/shell/testcases/maps/dumps/0010concat_map_0.nft @@ -6,6 +6,6 @@ table inet x { chain y { type nat hook prerouting priority dstnat; policy accept; - meta nfproto ipv4 dnat ip to ip saddr . ip protocol . tcp dport map @z + dnat ip to ip saddr . ip protocol . tcp dport map @z } } diff --git a/tests/shell/testcases/maps/dumps/anon_objmap_concat.nft b/tests/shell/testcases/maps/dumps/anon_objmap_concat.nft new file mode 100644 index 00000000..23aca0a2 --- /dev/null +++ b/tests/shell/testcases/maps/dumps/anon_objmap_concat.nft @@ -0,0 +1,16 @@ +table inet filter { + ct helper sip-5060u { + type "sip" protocol udp + l3proto ip + } + + ct helper sip-5060t { + type "sip" protocol tcp + l3proto ip + } + + chain input { + type filter hook input priority filter; policy accept; + ct helper set ip protocol . th dport map { udp . 10000-20000 : "sip-5060u", tcp . 10000-20000 : "sip-5060t" } + } +} diff --git a/tests/shell/testcases/maps/dumps/nat_addr_port.nft b/tests/shell/testcases/maps/dumps/nat_addr_port.nft index cf6b957f..c8493b3a 100644 --- a/tests/shell/testcases/maps/dumps/nat_addr_port.nft +++ b/tests/shell/testcases/maps/dumps/nat_addr_port.nft @@ -114,15 +114,15 @@ table inet inetfoo { dnat ip to ip daddr map @x4 ip saddr 10.1.1.1 dnat ip to 10.2.3.4 ip saddr 10.1.1.2 tcp dport 42 dnat ip to 10.2.3.4:4242 - meta l4proto tcp meta nfproto ipv4 dnat ip to ip saddr map @y4 - meta nfproto ipv4 dnat ip to ip saddr . tcp dport map @z4 + meta l4proto tcp dnat ip to ip saddr map @y4 + dnat ip to ip saddr . tcp dport map @z4 dnat ip to numgen inc mod 2 map @t1v4 meta l4proto tcp dnat ip to numgen inc mod 2 map @t2v4 dnat ip6 to ip6 daddr map @x6 ip6 saddr dead::1 dnat ip6 to feed::1 ip6 saddr dead::2 tcp dport 42 dnat ip6 to [c0::1a]:4242 - meta l4proto tcp meta nfproto ipv6 dnat ip6 to ip6 saddr map @y6 - meta nfproto ipv6 dnat ip6 to ip6 saddr . tcp dport map @z6 + meta l4proto tcp dnat ip6 to ip6 saddr map @y6 + dnat ip6 to ip6 saddr . tcp dport map @z6 dnat ip6 to numgen inc mod 2 map @t1v6 meta l4proto tcp dnat ip6 to numgen inc mod 2 map @t2v6 } diff --git a/tests/shell/testcases/maps/dumps/typeof_integer_0.nft b/tests/shell/testcases/maps/dumps/typeof_integer_0.nft new file mode 100644 index 00000000..33041557 --- /dev/null +++ b/tests/shell/testcases/maps/dumps/typeof_integer_0.nft @@ -0,0 +1,20 @@ +table inet t { + map m1 { + typeof udp length . @ih,32,32 : verdict + flags interval + elements = { 20-80 . 0x14 : accept, + 1-10 . 0xa : drop } + } + + map m2 { + typeof udp length . @ih,32,32 : verdict + elements = { 30 . 0x1e : drop, + 20 . 0x24 : accept } + } + + chain c { + udp length . @ih,32,32 vmap @m1 + udp length . @ih,32,32 vmap @m2 + udp length . @th,160,128 vmap { 47-63 . 0xe373135363130333131303735353203 : accept } + } +} diff --git a/tests/shell/testcases/maps/dumps/typeof_maps_0.nft b/tests/shell/testcases/maps/dumps/typeof_maps_0.nft index 438b9829..a5c0a609 100644 --- a/tests/shell/testcases/maps/dumps/typeof_maps_0.nft +++ b/tests/shell/testcases/maps/dumps/typeof_maps_0.nft @@ -17,11 +17,20 @@ table inet t { map m4 { typeof iifname . ip protocol . th dport : verdict + elements = { "eth0" . tcp . 22 : accept } + } + + map m5 { + typeof ipsec in reqid . iifname : verdict + elements = { 23 . "eth0" : accept } } chain c { ct mark set osf name map @m1 meta mark set vlan id map @m2 meta mark set ip saddr . ip daddr map @m3 + iifname . ip protocol . th dport vmap @m4 + iifname . ip protocol . th dport vmap { "eth0" . tcp . 22 : accept, "eth1" . udp . 67 : drop } + ipsec in reqid . iifname vmap @m5 } } diff --git a/tests/shell/testcases/maps/dumps/typeof_raw_0.nft b/tests/shell/testcases/maps/dumps/typeof_raw_0.nft new file mode 100644 index 00000000..e876425b --- /dev/null +++ b/tests/shell/testcases/maps/dumps/typeof_raw_0.nft @@ -0,0 +1,13 @@ +table ip x { + map y { + typeof ip saddr . @ih,32,32 : verdict + elements = { 1.1.1.1 . 0x14 : accept, + 7.7.7.7 . 0x86 : accept, + 7.7.7.8 . 0x97 : drop } + } + + chain y { + ip saddr . @ih,32,32 vmap @y + ip saddr . @ih,32,32 vmap { 4.4.4.4 . 0x34 : accept, 5.5.5.5 . 0x45 : drop } + } +} diff --git a/tests/shell/testcases/maps/typeof_integer_0 b/tests/shell/testcases/maps/typeof_integer_0 new file mode 100755 index 00000000..d51510af --- /dev/null +++ b/tests/shell/testcases/maps/typeof_integer_0 @@ -0,0 +1,27 @@ +#!/bin/bash + +EXPECTED="table inet t { + map m1 { + typeof udp length . @ih,32,32 : verdict + flags interval + elements = { 20-80 . 0x14 : accept, 1-10 . 0xa : drop } + } + + map m2 { + typeof udp length . @ih,32,32 : verdict + elements = { 20 . 0x24 : accept, 30 . 0x1e : drop } + } + + chain c { + udp length . @ih,32,32 vmap @m1 + udp length . @ih,32,32 vmap @m2 + udp length . @th,160,128 vmap { 47-63 . 0xe373135363130333131303735353203 : accept } + } +}" + +$NFT add element inet t m1 { 90-100 . 40 : drop } +$NFT delete element inet t m2 { 20 . 20 : accept } + +set -e +$NFT -f - <<< $EXPECTED + diff --git a/tests/shell/testcases/maps/typeof_maps_0 b/tests/shell/testcases/maps/typeof_maps_0 index f024ebe0..5cf5ddde 100755 --- a/tests/shell/testcases/maps/typeof_maps_0 +++ b/tests/shell/testcases/maps/typeof_maps_0 @@ -24,12 +24,21 @@ EXPECTED="table inet t { map m4 { typeof iifname . ip protocol . th dport : verdict + elements = { eth0 . tcp . 22 : accept } + } + + map m5 { + typeof ipsec in reqid . meta iifname : verdict + elements = { 23 . eth0 : accept } } chain c { ct mark set osf name map @m1 ether type vlan meta mark set vlan id map @m2 meta mark set ip saddr . ip daddr map @m3 + iifname . ip protocol . th dport vmap @m4 + iifname . ip protocol . th dport vmap { \"eth0\" . tcp . 22 : accept, \"eth1\" . udp . 67 : drop } + ipsec in reqid . meta iifname vmap @m5 } }" diff --git a/tests/shell/testcases/maps/typeof_raw_0 b/tests/shell/testcases/maps/typeof_raw_0 new file mode 100755 index 00000000..e3da7825 --- /dev/null +++ b/tests/shell/testcases/maps/typeof_raw_0 @@ -0,0 +1,18 @@ +#!/bin/bash + +EXPECTED="table ip x { + map y { + typeof ip saddr . @ih,32,32: verdict + elements = { 1.1.1.1 . 0x14 : accept, 2.2.2.2 . 0x1e : drop } + } + + chain y { + ip saddr . @ih,32,32 vmap @y + ip saddr . @ih,32,32 vmap { 4.4.4.4 . 0x34 : accept, 5.5.5.5 . 0x45 : drop} + } +}" + +set -e +$NFT -f - <<< $EXPECTED +$NFT add element ip x y { 7.7.7.7 . 0x86 : accept, 7.7.7.8 . 0x97 : drop } +$NFT delete element ip x y { 2.2.2.2 . 0x1e : drop } diff --git a/tests/shell/testcases/optimizations/dumps/merge_nat.nft b/tests/shell/testcases/optimizations/dumps/merge_nat.nft new file mode 100644 index 00000000..7a6ecb76 --- /dev/null +++ b/tests/shell/testcases/optimizations/dumps/merge_nat.nft @@ -0,0 +1,20 @@ +table ip test1 { + chain y { + dnat to ip saddr map { 4.4.4.4 : 1.1.1.1, 5.5.5.5 : 2.2.2.2 } + } +} +table ip test2 { + chain y { + dnat ip to tcp dport map { 80 : 1.1.1.1 . 8001, 81 : 2.2.2.2 . 9001 } + } +} +table ip test3 { + chain y { + snat to ip saddr . tcp sport map { 1.1.1.1 . 1024-65535 : 3.3.3.3, 2.2.2.2 . 1024-65535 : 4.4.4.4 } + } +} +table ip test4 { + chain y { + dnat ip to ip daddr . tcp dport map { 1.1.1.1 . 80 : 4.4.4.4 . 8000, 2.2.2.2 . 81 : 3.3.3.3 . 9000 } + } +} diff --git a/tests/shell/testcases/optimizations/dumps/merge_stmts.nft b/tests/shell/testcases/optimizations/dumps/merge_stmts.nft new file mode 100644 index 00000000..b56ea3ed --- /dev/null +++ b/tests/shell/testcases/optimizations/dumps/merge_stmts.nft @@ -0,0 +1,5 @@ +table ip x { + chain y { + ip daddr { 192.168.0.1, 192.168.0.2, 192.168.0.3 } counter packets 0 bytes 0 accept + } +} diff --git a/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft new file mode 100644 index 00000000..6dbfff2e --- /dev/null +++ b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat.nft @@ -0,0 +1,5 @@ +table ip x { + chain y { + iifname . ip saddr . ip daddr { "eth1" . 1.1.1.1 . 2.2.2.3, "eth1" . 1.1.1.2 . 2.2.2.4, "eth2" . 1.1.1.3 . 2.2.2.5 } accept + } +} diff --git a/tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.nft b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.nft new file mode 100644 index 00000000..c0f9ce0c --- /dev/null +++ b/tests/shell/testcases/optimizations/dumps/merge_stmts_concat_vmap.nft @@ -0,0 +1,5 @@ +table ip x { + chain y { + ip saddr . ip daddr vmap { 1.1.1.1 . 2.2.2.2 : accept, 2.2.2.2 . 3.3.3.3 : drop, 4.4.4.4 . 5.5.5.5 : accept } + } +} diff --git a/tests/shell/testcases/optimizations/dumps/merge_stmts_vmap.nft b/tests/shell/testcases/optimizations/dumps/merge_stmts_vmap.nft new file mode 100644 index 00000000..5a9b3006 --- /dev/null +++ b/tests/shell/testcases/optimizations/dumps/merge_stmts_vmap.nft @@ -0,0 +1,9 @@ +table ip x { + chain y { + ct state vmap { invalid : drop, established : accept, related : accept } + } + + chain z { + tcp dport vmap { 1 : accept, 2-3 : drop, 4 : accept } + } +} diff --git a/tests/shell/testcases/optimizations/dumps/merge_vmap_raw.nft b/tests/shell/testcases/optimizations/dumps/merge_vmap_raw.nft new file mode 100644 index 00000000..18847116 --- /dev/null +++ b/tests/shell/testcases/optimizations/dumps/merge_vmap_raw.nft @@ -0,0 +1,31 @@ +table inet x { + chain nat_dns_dnstc { + meta l4proto udp redirect to :5300 + drop + } + + chain nat_dns_this_5301 { + meta l4proto udp redirect to :5301 + drop + } + + chain nat_dns_saturn_5301 { + meta nfproto ipv4 meta l4proto udp dnat ip to 240.0.1.2:5301 + drop + } + + chain nat_dns_saturn_5302 { + meta nfproto ipv4 meta l4proto udp dnat ip to 240.0.1.2:5302 + drop + } + + chain nat_dns_saturn_5303 { + meta nfproto ipv4 meta l4proto udp dnat ip to 240.0.1.2:5303 + drop + } + + chain nat_dns_acme { + udp length . @th,160,128 vmap { 47-63 . 0xe373135363130333131303735353203 : goto nat_dns_dnstc, 62-78 . 0xe31393032383939353831343037320e : goto nat_dns_this_5301, 62-78 . 0xe31363436323733373931323934300e : goto nat_dns_saturn_5301, 62-78 . 0xe32393535373539353636383732310e : goto nat_dns_saturn_5302, 62-78 . 0xe38353439353637323038363633390e : goto nat_dns_saturn_5303 } + drop + } +} diff --git a/tests/shell/testcases/optimizations/dumps/merge_vmaps.nft b/tests/shell/testcases/optimizations/dumps/merge_vmaps.nft new file mode 100644 index 00000000..05b9e575 --- /dev/null +++ b/tests/shell/testcases/optimizations/dumps/merge_vmaps.nft @@ -0,0 +1,13 @@ +table ip x { + chain filter_in_tcp { + } + + chain filter_in_udp { + } + + chain y { + tcp dport vmap { 80 : accept, 81 : accept, 443 : accept, 8000-8100 : accept, 24000-25000 : accept } + meta l4proto vmap { tcp : goto filter_in_tcp, udp : goto filter_in_udp } + log + } +} diff --git a/tests/shell/testcases/optimizations/merge_nat b/tests/shell/testcases/optimizations/merge_nat new file mode 100755 index 00000000..290cfcfe --- /dev/null +++ b/tests/shell/testcases/optimizations/merge_nat @@ -0,0 +1,39 @@ +#!/bin/bash + +set -e + +RULESET="table ip test1 { + chain y { + ip saddr 4.4.4.4 dnat to 1.1.1.1 + ip saddr 5.5.5.5 dnat to 2.2.2.2 + } +}" + +$NFT -o -f - <<< $RULESET + +RULESET="table ip test2 { + chain y { + tcp dport 80 dnat to 1.1.1.1:8001 + tcp dport 81 dnat to 2.2.2.2:9001 + } +}" + +$NFT -o -f - <<< $RULESET + +RULESET="table ip test3 { + chain y { + ip saddr 1.1.1.1 tcp sport 1024-65535 snat to 3.3.3.3 + ip saddr 2.2.2.2 tcp sport 1024-65535 snat to 4.4.4.4 + } +}" + +$NFT -o -f - <<< $RULESET + +RULESET="table ip test4 { + chain y { + ip daddr 1.1.1.1 tcp dport 80 dnat to 4.4.4.4:8000 + ip daddr 2.2.2.2 tcp dport 81 dnat to 3.3.3.3:9000 + } +}" + +$NFT -o -f - <<< $RULESET diff --git a/tests/shell/testcases/optimizations/merge_stmts b/tests/shell/testcases/optimizations/merge_stmts new file mode 100755 index 00000000..0c35636e --- /dev/null +++ b/tests/shell/testcases/optimizations/merge_stmts @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e + +RULESET="table ip x { + chain y { + ip daddr 192.168.0.1 counter accept + ip daddr 192.168.0.2 counter accept + ip daddr 192.168.0.3 counter accept + } +}" + +$NFT -o -f - <<< $RULESET diff --git a/tests/shell/testcases/optimizations/merge_stmts_concat b/tests/shell/testcases/optimizations/merge_stmts_concat new file mode 100755 index 00000000..941e9a5a --- /dev/null +++ b/tests/shell/testcases/optimizations/merge_stmts_concat @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e + +RULESET="table ip x { + chain y { + meta iifname eth1 ip saddr 1.1.1.1 ip daddr 2.2.2.3 accept + meta iifname eth1 ip saddr 1.1.1.2 ip daddr 2.2.2.4 accept + meta iifname eth2 ip saddr 1.1.1.3 ip daddr 2.2.2.5 accept + } +}" + +$NFT -o -f - <<< $RULESET diff --git a/tests/shell/testcases/optimizations/merge_stmts_concat_vmap b/tests/shell/testcases/optimizations/merge_stmts_concat_vmap new file mode 100755 index 00000000..5c0ae60c --- /dev/null +++ b/tests/shell/testcases/optimizations/merge_stmts_concat_vmap @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e + +RULESET="table ip x { + chain y { + ip saddr 1.1.1.1 ip daddr 2.2.2.2 accept + ip saddr 4.4.4.4 ip daddr 5.5.5.5 accept + ip saddr 2.2.2.2 ip daddr 3.3.3.3 drop + } +}" + +$NFT -o -f - <<< $RULESET diff --git a/tests/shell/testcases/optimizations/merge_stmts_vmap b/tests/shell/testcases/optimizations/merge_stmts_vmap new file mode 100755 index 00000000..79350076 --- /dev/null +++ b/tests/shell/testcases/optimizations/merge_stmts_vmap @@ -0,0 +1,17 @@ +#!/bin/bash + +set -e + +RULESET="table ip x { + chain y { + ct state invalid drop + ct state established,related accept + } + chain z { + tcp dport { 1 } accept + tcp dport 2-3 drop + tcp dport 4 accept + } +}" + +$NFT -o -f - <<< $RULESET diff --git a/tests/shell/testcases/optimizations/merge_vmap_raw b/tests/shell/testcases/optimizations/merge_vmap_raw new file mode 100755 index 00000000..f3dc0721 --- /dev/null +++ b/tests/shell/testcases/optimizations/merge_vmap_raw @@ -0,0 +1,32 @@ +#!/bin/bash + +set -e + +RULESET="table inet x { + chain nat_dns_dnstc { meta l4proto udp redirect to :5300 ; drop ; } + chain nat_dns_this_5301 { meta l4proto udp redirect to :5301 ; drop ; } + chain nat_dns_saturn_5301 { meta nfproto ipv4 meta l4proto udp dnat to 240.0.1.2:5301 ; drop ; } + chain nat_dns_saturn_5302 { meta nfproto ipv4 meta l4proto udp dnat to 240.0.1.2:5302 ; drop ; } + chain nat_dns_saturn_5303 { meta nfproto ipv4 meta l4proto udp dnat to 240.0.1.2:5303 ; drop ; } + + chain nat_dns_acme { + udp length 47-63 @th,160,128 0x0e373135363130333131303735353203 \ + goto nat_dns_dnstc + + udp length 62-78 @th,160,128 0x0e31393032383939353831343037320e \ + goto nat_dns_this_5301 + + udp length 62-78 @th,160,128 0x0e31363436323733373931323934300e \ + goto nat_dns_saturn_5301 + + udp length 62-78 @th,160,128 0x0e32393535373539353636383732310e \ + goto nat_dns_saturn_5302 + + udp length 62-78 @th,160,128 0x0e38353439353637323038363633390e \ + goto nat_dns_saturn_5303 + + drop + } +}" + +$NFT -o -f - <<< $RULESET diff --git a/tests/shell/testcases/optimizations/merge_vmaps b/tests/shell/testcases/optimizations/merge_vmaps new file mode 100755 index 00000000..0922a221 --- /dev/null +++ b/tests/shell/testcases/optimizations/merge_vmaps @@ -0,0 +1,26 @@ +#!/bin/bash + +set -e + +RULESET="table ip x { + chain filter_in_tcp { + } + chain filter_in_udp { + } + chain y { + tcp dport vmap { + 80 : accept, + 81 : accept, + 443 : accept, + } + tcp dport vmap { + 8000-8100 : accept, + 24000-25000 : accept, + } + meta l4proto tcp goto filter_in_tcp + meta l4proto udp goto filter_in_udp + log + } +}" + +$NFT -o -f - <<< $RULESET diff --git a/tests/shell/testcases/owner/0001-flowtable-uaf b/tests/shell/testcases/owner/0001-flowtable-uaf new file mode 100755 index 00000000..4efbe75c --- /dev/null +++ b/tests/shell/testcases/owner/0001-flowtable-uaf @@ -0,0 +1,22 @@ +#!/bin/bash + +set -e + +$NFT -f - <<EOF +table t { + flags owner + flowtable f { + devices = { lo } + } +} +EOF + +# trigger uaf. +$NFT -f - <<EOF +table t { + flags owner + flowtable f { + devices = { lo } + } +} +EOF diff --git a/tests/shell/testcases/parsing/describe b/tests/shell/testcases/parsing/describe new file mode 100755 index 00000000..2ee072e8 --- /dev/null +++ b/tests/shell/testcases/parsing/describe @@ -0,0 +1,7 @@ +#!/bin/bash + +errmsg='Error: unknown ip option type/field' + +str=$($NFT describe ip option rr value 2>&1 | head -n 1) + +[ "$str" = "$errmsg" ] && exit 0 diff --git a/tests/shell/testcases/sets/0024named_objects_0 b/tests/shell/testcases/sets/0024named_objects_0 index 21200c3c..6d21e388 100755 --- a/tests/shell/testcases/sets/0024named_objects_0 +++ b/tests/shell/testcases/sets/0024named_objects_0 @@ -18,6 +18,15 @@ table inet x { quota user124 { over 2000 bytes } + synproxy https-synproxy { + mss 1460 + wscale 7 + timestamp sack-perm + } + synproxy other-synproxy { + mss 1460 + wscale 5 + } set y { type ipv4_addr } @@ -25,9 +34,15 @@ table inet x { type ipv4_addr : quota elements = { 192.168.2.2 : "user124", 192.168.2.3 : "user124"} } + map test2 { + type ipv4_addr : synproxy + flags interval + elements = { 192.168.1.0/24 : "https-synproxy", 192.168.2.0/24 : "other-synproxy" } + } chain y { type filter hook input priority 0; policy accept; counter name ip saddr map { 192.168.2.2 : "user123", 1.1.1.1 : "user123", 2.2.2.2 : "user123"} + synproxy name ip saddr map { 192.168.1.0/24 : "https-synproxy", 192.168.2.0/24 : "other-synproxy" } quota name ip saddr map @test drop } }" diff --git a/tests/shell/testcases/sets/0064map_catchall_0 b/tests/shell/testcases/sets/0064map_catchall_0 index 6f2a7c6f..43685160 100755 --- a/tests/shell/testcases/sets/0064map_catchall_0 +++ b/tests/shell/testcases/sets/0064map_catchall_0 @@ -17,3 +17,8 @@ RULESET="table ip x { $NFT -f - <<< $RULESET $NFT delete element x y { \* : 192.168.0.3 } $NFT add element x y { \* : 192.168.0.4 } + +$NFT add chain x y +$NFT add rule x y snat to ip saddr map @z +$NFT 'add rule x y snat to ip saddr map { 10.141.0.0/24 : 192.168.0.2, * : 192.168.0.3 }' +$NFT 'add rule x y snat to ip saddr . ip daddr map { 10.141.0.0/24 . 10.0.0.0/8 : 192.168.0.2, 192.168.9.0/24 . 192.168.10.0/24 : 192.168.0.4, * : 192.168.0.3 }' diff --git a/tests/shell/testcases/sets/0068interval_stack_overflow_0 b/tests/shell/testcases/sets/0068interval_stack_overflow_0 index 66205724..2cbc9868 100755 --- a/tests/shell/testcases/sets/0068interval_stack_overflow_0 +++ b/tests/shell/testcases/sets/0068interval_stack_overflow_0 @@ -9,7 +9,7 @@ trap 'rm -f "$ruleset_file"' EXIT { echo 'define big_set = {' for ((i = 1; i < 255; i++)); do - for ((j = 1; j < 80; j++)); do + for ((j = 1; j < 255; j++)); do echo "10.0.$i.$j," done done @@ -26,4 +26,4 @@ table inet test68_table { } EOF -( ulimit -s 128 && $NFT -f "$ruleset_file" ) +( ulimit -s 400 && $NFT -f "$ruleset_file" ) diff --git a/tests/shell/testcases/sets/0069interval_merge_0 b/tests/shell/testcases/sets/0069interval_merge_0 new file mode 100755 index 00000000..edb6422a --- /dev/null +++ b/tests/shell/testcases/sets/0069interval_merge_0 @@ -0,0 +1,28 @@ +#!/bin/bash + +set -e + +RULESET="table ip x { + set y { + type ipv4_addr + flags interval + auto-merge + elements = { 1.2.3.0, 1.2.3.255, 1.2.3.0/24, 3.3.3.3, 4.4.4.4, 4.4.4.4-4.4.4.8, 3.3.3.4, 3.3.3.5 } + } +}" + +$NFT -f - <<< $RULESET + +RULESET="table ip x { + set y { + type ipv4_addr + flags interval + auto-merge + elements = { 1.2.4.0, 3.3.3.6, 4.4.4.0/24 } + } +}" + +$NFT -f - <<< $RULESET + +$NFT add element ip x y { 1.2.3.0-1.2.4.255, 3.3.3.5, 4.4.4.1 } +$NFT add element ip x y { 1.2.3.0-1.2.4.255, 3.3.3.5, 4.4.5.0 } diff --git a/tests/shell/testcases/sets/dumps/0024named_objects_0.nft b/tests/shell/testcases/sets/dumps/0024named_objects_0.nft index 2ffa4f2f..52d1bf64 100644 --- a/tests/shell/testcases/sets/dumps/0024named_objects_0.nft +++ b/tests/shell/testcases/sets/dumps/0024named_objects_0.nft @@ -15,6 +15,17 @@ table inet x { over 2000 bytes } + synproxy https-synproxy { + mss 1460 + wscale 7 + timestamp sack-perm + } + + synproxy other-synproxy { + mss 1460 + wscale 5 + } + set y { type ipv4_addr } @@ -24,9 +35,16 @@ table inet x { elements = { 192.168.2.2 : "user124", 192.168.2.3 : "user124" } } + map test2 { + type ipv4_addr : synproxy + flags interval + elements = { 192.168.1.0/24 : "https-synproxy", 192.168.2.0/24 : "other-synproxy" } + } + chain y { type filter hook input priority filter; policy accept; counter name ip saddr map { 1.1.1.1 : "user123", 2.2.2.2 : "user123", 192.168.2.2 : "user123" } + synproxy name ip saddr map { 192.168.1.0/24 : "https-synproxy", 192.168.2.0/24 : "other-synproxy" } quota name ip saddr map @test drop } } diff --git a/tests/shell/testcases/sets/dumps/0064map_catchall_0.nft b/tests/shell/testcases/sets/dumps/0064map_catchall_0.nft index 286683a0..890ed2aa 100644 --- a/tests/shell/testcases/sets/dumps/0064map_catchall_0.nft +++ b/tests/shell/testcases/sets/dumps/0064map_catchall_0.nft @@ -9,4 +9,10 @@ table ip x { flags interval elements = { 10.141.0.0/24 : 192.168.0.2, * : 192.168.0.3 } } + + chain y { + snat to ip saddr map @z + snat to ip saddr map { 10.141.0.0/24 : 192.168.0.2, * : 192.168.0.3 } + snat to ip saddr . ip daddr map { 10.141.0.0/24 . 10.0.0.0/8 : 192.168.0.2, 192.168.9.0/24 . 192.168.10.0/24 : 192.168.0.4, * : 192.168.0.3 } + } } diff --git a/tests/shell/testcases/sets/dumps/0069interval_merge_0.nft b/tests/shell/testcases/sets/dumps/0069interval_merge_0.nft new file mode 100644 index 00000000..2d4e1706 --- /dev/null +++ b/tests/shell/testcases/sets/dumps/0069interval_merge_0.nft @@ -0,0 +1,9 @@ +table ip x { + set y { + type ipv4_addr + flags interval + auto-merge + elements = { 1.2.3.0-1.2.4.255, 3.3.3.3-3.3.3.6, + 4.4.4.0-4.4.5.0 } + } +} diff --git a/tests/shell/testcases/sets/dumps/dynset_missing.nft b/tests/shell/testcases/sets/dumps/dynset_missing.nft new file mode 100644 index 00000000..6c8ed323 --- /dev/null +++ b/tests/shell/testcases/sets/dumps/dynset_missing.nft @@ -0,0 +1,12 @@ +table ip test { + set dlist { + type ipv4_addr + size 65535 + flags dynamic + } + + chain output { + type filter hook output priority filter; policy accept; + udp dport 1234 update @dlist { ip daddr } counter packets 0 bytes 0 + } +} diff --git a/tests/shell/testcases/sets/dumps/sets_with_ifnames.nft b/tests/shell/testcases/sets/dumps/sets_with_ifnames.nft new file mode 100644 index 00000000..6b073ae2 --- /dev/null +++ b/tests/shell/testcases/sets/dumps/sets_with_ifnames.nft @@ -0,0 +1,51 @@ +table inet testifsets { + set simple { + type ifname + elements = { "abcdef0", + "abcdef1", + "othername" } + } + + set simple_wild { + type ifname + flags interval + elements = { "abcdef*", + "othername", + "ppp0" } + } + + set concat { + type ipv4_addr . ifname + elements = { 10.1.2.2 . "abcdef0", + 10.1.2.2 . "abcdef1" } + } + + set concat_wild { + type ipv4_addr . ifname + flags interval + elements = { 10.1.2.2 . "abcdef*", + 10.1.2.1 . "bar", + 1.1.2.0/24 . "abcdef0", + 12.2.2.0/24 . "abcdef*" } + } + + chain v4icmp { + iifname @simple counter packets 0 bytes 0 + iifname @simple_wild counter packets 0 bytes 0 + iifname { "eth0", "abcdef0" } counter packets 0 bytes 0 + iifname { "abcdef*", "eth0" } counter packets 0 bytes 0 + } + + chain v4icmpc { + ip saddr . iifname @concat counter packets 0 bytes 0 + ip saddr . iifname @concat_wild counter packets 0 bytes 0 + ip saddr . iifname { 10.1.2.2 . "abcdef0" } counter packets 0 bytes 0 + ip saddr . iifname { 10.1.2.2 . "abcdef*" } counter packets 0 bytes 0 + } + + chain input { + type filter hook input priority filter; policy accept; + ip protocol icmp jump v4icmp + ip protocol icmp goto v4icmpc + } +} diff --git a/tests/shell/testcases/sets/dumps/typeof_raw_0.nft b/tests/shell/testcases/sets/dumps/typeof_raw_0.nft new file mode 100644 index 00000000..499ff167 --- /dev/null +++ b/tests/shell/testcases/sets/dumps/typeof_raw_0.nft @@ -0,0 +1,12 @@ +table inet t { + set y { + typeof ip daddr . @ih,32,32 + elements = { 1.1.1.1 . 0x14, + 2.2.2.2 . 0x20 } + } + + chain y { + ip saddr . @ih,32,32 { 1.1.1.1 . 0x14, 2.2.2.2 . 0x1e } + ip daddr . @ih,32,32 @y + } +} diff --git a/tests/shell/testcases/sets/dumps/typeof_sets_0.nft b/tests/shell/testcases/sets/dumps/typeof_sets_0.nft index 565369fb..68b4dcc5 100644 --- a/tests/shell/testcases/sets/dumps/typeof_sets_0.nft +++ b/tests/shell/testcases/sets/dumps/typeof_sets_0.nft @@ -14,6 +14,42 @@ table inet t { elements = { 2, 3, 103 } } + set s4 { + typeof frag frag-off + elements = { 1, 1024 } + } + + set s5 { + typeof ip option ra value + elements = { 1, 1024 } + } + + set s6 { + typeof tcp option maxseg size + elements = { 1, 1024 } + } + + set s7 { + typeof sctp chunk init num-inbound-streams + elements = { 1, 4 } + } + + set s8 { + typeof ip version + elements = { 4, 6 } + } + + set s9 { + typeof ip hdrlength + elements = { 0, 1, 2, 3, 4, + 15 } + } + + set s10 { + typeof iifname . ip saddr . ipsec in reqid + elements = { "eth0" . 10.1.1.2 . 42 } + } + chain c1 { osf name @s1 accept } @@ -21,4 +57,32 @@ table inet t { chain c2 { vlan id @s2 accept } + + chain c4 { + frag frag-off @s4 accept + } + + chain c5 { + ip option ra value @s5 accept + } + + chain c6 { + tcp option maxseg size @s6 accept + } + + chain c7 { + sctp chunk init num-inbound-streams @s7 accept + } + + chain c8 { + ip version @s8 accept + } + + chain c9 { + ip hdrlength @s9 accept + } + + chain c10 { + iifname . ip saddr . ipsec in reqid @s10 accept + } } diff --git a/tests/shell/testcases/sets/dynset_missing b/tests/shell/testcases/sets/dynset_missing new file mode 100755 index 00000000..fdf5f49e --- /dev/null +++ b/tests/shell/testcases/sets/dynset_missing @@ -0,0 +1,32 @@ +#!/bin/bash + +set -e + +$NFT -f /dev/stdin <<EOF +table ip test { + chain output { type filter hook output priority 0; + } +} +EOF + +# misses 'flags dynamic' +$NFT 'add set ip test dlist {type ipv4_addr; }' + +# picks rhash backend because 'size' was also missing. +$NFT 'add rule ip test output udp dport 1234 update @dlist { ip daddr } counter' + +tmpfile=$(mktemp) + +trap "rm -rf $tmpfile" EXIT + +# kernel has forced an 64k upper size, i.e. this restore file +# has 'size 65536' but no 'flags dynamic'. +$NFT list ruleset > $tmpfile + +# this restore works, because set is still the rhash backend. +$NFT -f $tmpfile # success +$NFT flush ruleset + +# fails without commit 'attempt to set_eval flag if dynamic updates requested', +# because set in $tmpfile has 'size x' but no 'flags dynamic'. +$NFT -f $tmpfile diff --git a/tests/shell/testcases/sets/sets_with_ifnames b/tests/shell/testcases/sets/sets_with_ifnames new file mode 100755 index 00000000..f4ef4db5 --- /dev/null +++ b/tests/shell/testcases/sets/sets_with_ifnames @@ -0,0 +1,149 @@ +#!/bin/bash + +dumpfile=$(dirname $0)/dumps/$(basename $0).nft + +[ -z "$NFT" ] && exit 111 + +$NFT -f "$dumpfile" || exit 1 + +rnd=$(mktemp -u XXXXXXXX) +ns1="nft1ifname-$rnd" +ns2="nft2ifname-$rnd" + +cleanup() +{ + ip netns del "$ns1" +} + +trap cleanup EXIT + +# check a given element is (not) present in the set. +lookup_elem() +{ + local setname=$1 + local value=$2 + local fail=$3 + local expect_result=$4 + local msg=$5 + + result=$(ip netns exec "$ns1" $NFT get element inet testifsets $setname { "$value" } 2>/dev/null | grep "$expect_result" ) + + if [ -z "$result" ] && [ $fail -ne 1 ] ; then + echo "empty result, expected $expect_result $msg" + ip netns exec "$ns1" $NFT get element inet testifsets $setname { "$value" } + exit 1 + fi +} + +check_elem_get() +{ + local setname=$1 + local value=$2 + local fail=$3 + local expect_result=$4 + + # when query is 'abcde', and set has 'abc*', result is + # 'abc*', not 'abcde', so returned element can be different. + if [ -z "$expect_result" ]; then + expect_result=$ifname + fi + + lookup_elem "$setname" "$value" "$fail" "$expect_result" "" +} + +# same, but also delete and re-add the element. +check_elem() +{ + local setname=$1 + local value=$2 + + lookup_elem "$setname" "$value" "0" "$value" "initial check" + + ip netns exec "$ns1" $NFT delete element inet testifsets $setname { "$value" } + if [ $? -ne 0 ]; then + ip netns exec "$ns1" $NFT list ruleset + echo "delete element $setname { $value } failed" + exit 1 + fi + + ip netns exec "$ns1" $NFT add element inet testifsets $setname { "$value" } + + lookup_elem "$setname" "$value" "0" "$value" "check after add/del" +} + +# send pings, check all rules with sets that contain abcdef1 match. +# there are 4 rules in this chain, 4 should match. +check_matching_icmp_ppp() +{ + pkt=$((RANDOM%10)) + pkt=$((pkt+1)) + ip netns exec "$ns1" ping -f -c $pkt 10.1.2.2 + + # replies should arrive via 'abcdeg', so, should NOT increment any counters. + ip netns exec "$ns1" ping -f -c 100 10.2.2.2 + + matches=$(ip netns exec "$ns1" $NFT list chain inet testifsets v4icmp | grep "counter packets $pkt " | wc -l) + want=3 + + if [ "$matches" -ne $want ] ;then + ip netns exec "$ns1" $NFT list ruleset + echo "Expected $want matching rules, got $matches, packets $pkt in v4icmp" + exit 1 + fi + + # same, for concat set type. + + matches=$(ip netns exec "$ns1" $NFT list chain inet testifsets v4icmpc | grep "counter packets $pkt " | wc -l) + + if [ "$matches" -ne $want ] ;then + ip netns exec "$ns1" $NFT list ruleset + echo "Expected $want matching rules, got $matches, packets $pkt in v4icmpc" + exit 1 + fi +} + +ip netns add "$ns1" || exit 111 +ip netns add "$ns2" || exit 111 +ip netns exec "$ns1" $NFT -f "$dumpfile" || exit 3 + +for n in abcdef0 abcdef1 othername;do + check_elem simple $n +done + +check_elem_get simple foo 1 + +for n in ppp0 othername;do + check_elem simple_wild $n +done + +check_elem_get simple_wild enoent 1 +check_elem simple_wild ppp0 +check_elem_get simple_wild abcdefghijk 0 'abcdef\*' + +check_elem_get concat '1.2.3.4 . "enoent"' 1 +check_elem_get concat '10.1.2.2 . "abcdef"' 1 +check_elem_get concat '10.1.2.1 . "abcdef1"' 1 + +check_elem concat '10.1.2.2 . "abcdef0"' +check_elem concat '10.1.2.2 . "abcdef1"' + +set -e +ip -net "$ns1" link set lo up +ip -net "$ns2" link set lo up +ip netns exec "$ns1" ping -f -c 10 127.0.0.1 + +ip link add abcdef1 netns $ns1 type veth peer name veth0 netns $ns2 +ip link add abcdeg netns $ns1 type veth peer name veth1 netns $ns2 + +ip -net "$ns1" link set abcdef1 up +ip -net "$ns2" link set veth0 up +ip -net "$ns1" link set abcdeg up +ip -net "$ns2" link set veth1 up + +ip -net "$ns1" addr add 10.1.2.1/24 dev abcdef1 +ip -net "$ns1" addr add 10.2.2.1/24 dev abcdeg + +ip -net "$ns2" addr add 10.1.2.2/24 dev veth0 +ip -net "$ns2" addr add 10.2.2.2/24 dev veth1 + +check_matching_icmp_ppp diff --git a/tests/shell/testcases/sets/typeof_raw_0 b/tests/shell/testcases/sets/typeof_raw_0 new file mode 100755 index 00000000..36396b5c --- /dev/null +++ b/tests/shell/testcases/sets/typeof_raw_0 @@ -0,0 +1,17 @@ +#!/bin/bash + +EXPECTED="table inet t { + set y { + typeof ip daddr . @ih,32,32 + elements = { 1.1.1.1 . 0x14, 2.2.2.2 . 0x20} + } + + chain y { + ip saddr . @ih,32,32 { 1.1.1.1 . 0x14, 2.2.2.2 . 0x1e } + ip daddr . @ih,32,32 @y + } +}" + +set -e +$NFT -f - <<< $EXPECTED + diff --git a/tests/shell/testcases/sets/typeof_sets_0 b/tests/shell/testcases/sets/typeof_sets_0 index 9b2712e5..5fc6a121 100755 --- a/tests/shell/testcases/sets/typeof_sets_0 +++ b/tests/shell/testcases/sets/typeof_sets_0 @@ -20,6 +20,41 @@ EXPECTED="table inet t { elements = { 2, 3, 103 } } + set s4 { + typeof frag frag-off + elements = { 1, 1024 } + } + + set s5 { + typeof ip option ra value + elements = { 1, 1024 } + } + + set s6 { + typeof tcp option maxseg size + elements = { 1, 1024 } + } + + set s7 { + typeof sctp chunk init num-inbound-streams + elements = { 1, 4 } + } + + set s8 { + typeof ip version + elements = { 4, 6 } + } + + set s9 { + typeof ip hdrlength + elements = { 0, 1, 2, 3, 4, 15 } + } + + set s10 { + typeof meta iifname . ip saddr . ipsec in reqid + elements = { \"eth0\" . 10.1.1.2 . 42 } + } + chain c1 { osf name @s1 accept } @@ -27,6 +62,34 @@ EXPECTED="table inet t { chain c2 { ether type vlan vlan id @s2 accept } + + chain c4 { + frag frag-off @s4 accept + } + + chain c5 { + ip option ra value @s5 accept + } + + chain c6 { + tcp option maxseg size @s6 accept + } + + chain c7 { + sctp chunk init num-inbound-streams @s7 accept + } + + chain c8 { + ip version @s8 accept + } + + chain c9 { + ip hdrlength @s9 accept + } + + chain c10 { + meta iifname . ip saddr . ipsec in reqid @s10 accept + } }" set -e |