diff --git a/SOURCES/0063-nft-Simplify-immediate-parsing.patch b/SOURCES/0063-nft-Simplify-immediate-parsing.patch new file mode 100644 index 0000000..4123f4b --- /dev/null +++ b/SOURCES/0063-nft-Simplify-immediate-parsing.patch @@ -0,0 +1,199 @@ +From c65bd8b3c23f0fe5f824274467740a2d350dcb9c Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 1 Mar 2022 18:59:31 +0100 +Subject: [PATCH] nft: Simplify immediate parsing + +Implementations of parse_immediate callback are mostly trivial, the only +relevant part is access to family-specific parts of struct +iptables_command_state when setting goto flag for iptables and +ip6tables. Refactor them into simple set_goto_flag callbacks. + +Signed-off-by: Phil Sutter +Acked-by: Florian Westphal +(cherry picked from commit b5f2faea325a315bfb932ebc634f3298d4824cae) +--- + iptables/nft-arp.c | 9 --------- + iptables/nft-bridge.c | 9 --------- + iptables/nft-ipv4.c | 12 +++--------- + iptables/nft-ipv6.c | 12 +++--------- + iptables/nft-shared.c | 17 +++++++---------- + iptables/nft-shared.h | 2 +- + 6 files changed, 14 insertions(+), 47 deletions(-) + +diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c +index 7c61c31a13c40..0c37a762cd418 100644 +--- a/iptables/nft-arp.c ++++ b/iptables/nft-arp.c +@@ -182,14 +182,6 @@ static void nft_arp_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e, + fw->arp.invflags |= flags; + } + +-static void nft_arp_parse_immediate(const char *jumpto, bool nft_goto, +- void *data) +-{ +- struct iptables_command_state *cs = data; +- +- cs->jumpto = jumpto; +-} +- + static void parse_mask_ipv4(struct nft_xt_ctx *ctx, struct in_addr *mask) + { + mask->s_addr = ctx->bitwise.mask[0]; +@@ -575,7 +567,6 @@ struct nft_family_ops nft_family_ops_arp = { + .print_payload = NULL, + .parse_meta = nft_arp_parse_meta, + .parse_payload = nft_arp_parse_payload, +- .parse_immediate = nft_arp_parse_immediate, + .print_header = nft_arp_print_header, + .print_rule = nft_arp_print_rule, + .save_rule = nft_arp_save_rule, +diff --git a/iptables/nft-bridge.c b/iptables/nft-bridge.c +index 2aa15e2d1e69d..e00a19e843d93 100644 +--- a/iptables/nft-bridge.c ++++ b/iptables/nft-bridge.c +@@ -284,14 +284,6 @@ static void nft_bridge_parse_payload(struct nft_xt_ctx *ctx, + } + } + +-static void nft_bridge_parse_immediate(const char *jumpto, bool nft_goto, +- void *data) +-{ +- struct iptables_command_state *cs = data; +- +- cs->jumpto = jumpto; +-} +- + /* return 0 if saddr, 1 if daddr, -1 on error */ + static int + lookup_check_ether_payload(uint32_t base, uint32_t offset, uint32_t len) +@@ -948,7 +940,6 @@ struct nft_family_ops nft_family_ops_bridge = { + .print_payload = NULL, + .parse_meta = nft_bridge_parse_meta, + .parse_payload = nft_bridge_parse_payload, +- .parse_immediate = nft_bridge_parse_immediate, + .parse_lookup = nft_bridge_parse_lookup, + .parse_match = nft_bridge_parse_match, + .parse_target = nft_bridge_parse_target, +diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c +index d8c48ce8817b6..c826ac153139f 100644 +--- a/iptables/nft-ipv4.c ++++ b/iptables/nft-ipv4.c +@@ -241,15 +241,9 @@ static void nft_ipv4_parse_payload(struct nft_xt_ctx *ctx, + } + } + +-static void nft_ipv4_parse_immediate(const char *jumpto, bool nft_goto, +- void *data) ++static void nft_ipv4_set_goto_flag(struct iptables_command_state *cs) + { +- struct iptables_command_state *cs = data; +- +- cs->jumpto = jumpto; +- +- if (nft_goto) +- cs->fw.ip.flags |= IPT_F_GOTO; ++ cs->fw.ip.flags |= IPT_F_GOTO; + } + + static void print_fragment(unsigned int flags, unsigned int invflags, +@@ -473,7 +467,7 @@ struct nft_family_ops nft_family_ops_ipv4 = { + .is_same = nft_ipv4_is_same, + .parse_meta = nft_ipv4_parse_meta, + .parse_payload = nft_ipv4_parse_payload, +- .parse_immediate = nft_ipv4_parse_immediate, ++ .set_goto_flag = nft_ipv4_set_goto_flag, + .print_header = print_header, + .print_rule = nft_ipv4_print_rule, + .save_rule = nft_ipv4_save_rule, +diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c +index a5481b3f77ac5..127bc96379968 100644 +--- a/iptables/nft-ipv6.c ++++ b/iptables/nft-ipv6.c +@@ -180,15 +180,9 @@ static void nft_ipv6_parse_payload(struct nft_xt_ctx *ctx, + } + } + +-static void nft_ipv6_parse_immediate(const char *jumpto, bool nft_goto, +- void *data) ++static void nft_ipv6_set_goto_flag(struct iptables_command_state *cs) + { +- struct iptables_command_state *cs = data; +- +- cs->jumpto = jumpto; +- +- if (nft_goto) +- cs->fw6.ipv6.flags |= IP6T_F_GOTO; ++ cs->fw6.ipv6.flags |= IP6T_F_GOTO; + } + + static void nft_ipv6_print_rule(struct nft_handle *h, struct nftnl_rule *r, +@@ -415,7 +409,7 @@ struct nft_family_ops nft_family_ops_ipv6 = { + .is_same = nft_ipv6_is_same, + .parse_meta = nft_ipv6_parse_meta, + .parse_payload = nft_ipv6_parse_payload, +- .parse_immediate = nft_ipv6_parse_immediate, ++ .set_goto_flag = nft_ipv6_set_goto_flag, + .print_header = print_header, + .print_rule = nft_ipv6_print_rule, + .save_rule = nft_ipv6_save_rule, +diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c +index 7f757d38ecaec..172cf2054a33c 100644 +--- a/iptables/nft-shared.c ++++ b/iptables/nft-shared.c +@@ -510,9 +510,7 @@ static void nft_parse_counter(struct nftnl_expr *e, struct xt_counters *counters + static void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e) + { + const char *chain = nftnl_expr_get_str(e, NFTNL_EXPR_IMM_CHAIN); +- const char *jumpto = NULL; +- bool nft_goto = false; +- void *data = ctx->cs; ++ struct iptables_command_state *cs = ctx->cs; + int verdict; + + if (nftnl_expr_is_set(e, NFTNL_EXPR_IMM_DATA)) { +@@ -535,23 +533,22 @@ static void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e) + /* Standard target? */ + switch(verdict) { + case NF_ACCEPT: +- jumpto = "ACCEPT"; ++ cs->jumpto = "ACCEPT"; + break; + case NF_DROP: +- jumpto = "DROP"; ++ cs->jumpto = "DROP"; + break; + case NFT_RETURN: +- jumpto = "RETURN"; ++ cs->jumpto = "RETURN"; + break;; + case NFT_GOTO: +- nft_goto = true; ++ if (ctx->h->ops->set_goto_flag) ++ ctx->h->ops->set_goto_flag(cs); + /* fall through */ + case NFT_JUMP: +- jumpto = chain; ++ cs->jumpto = chain; + break; + } +- +- ctx->h->ops->parse_immediate(jumpto, nft_goto, data); + } + + static void nft_parse_limit(struct nft_xt_ctx *ctx, struct nftnl_expr *e) +diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h +index 520a296fb530c..29f7056714235 100644 +--- a/iptables/nft-shared.h ++++ b/iptables/nft-shared.h +@@ -89,7 +89,7 @@ struct nft_family_ops { + void *data); + void (*parse_lookup)(struct nft_xt_ctx *ctx, struct nftnl_expr *e, + void *data); +- void (*parse_immediate)(const char *jumpto, bool nft_goto, void *data); ++ void (*set_goto_flag)(struct iptables_command_state *cs); + + void (*print_table_header)(const char *tablename); + void (*print_header)(unsigned int format, const char *chain, +-- +2.34.1 + diff --git a/SOURCES/0064-nft-Speed-up-immediate-parsing.patch b/SOURCES/0064-nft-Speed-up-immediate-parsing.patch new file mode 100644 index 0000000..8eba9ef --- /dev/null +++ b/SOURCES/0064-nft-Speed-up-immediate-parsing.patch @@ -0,0 +1,119 @@ +From 5d6c1effe324d1a2401a4315895fe72c7255a14d Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 1 Mar 2022 19:46:21 +0100 +Subject: [PATCH] nft: Speed up immediate parsing + +Parsing of rules which jump to a chain pointlessly causes a call to +xtables_find_target() despite the code already knowing the outcome. + +Avoid the significant delay for rulesets with many chain jumps by +performing the (standard) target lookup only for accept/drop/return +verdicts. + +From a biased test-case on my VM: + +| # iptables-nft-save | grep -c -- '-j' +| 133943 +| # time ./old/iptables-nft-save >/dev/null +| real 0m45.566s +| user 0m1.308s +| sys 0m8.430s +| # time ./new/iptables-nft-save >/dev/null +| real 0m3.547s +| user 0m0.762s +| sys 0m2.476s + +Signed-off-by: Phil Sutter +Acked-by: Florian Westphal +(cherry picked from commit 07ee529f5a62838d68be59683be99bf6a7cda0f2) +--- + iptables/nft-bridge.c | 1 + + iptables/nft-shared.c | 37 ++++++++++++++++++------------------- + 2 files changed, 19 insertions(+), 19 deletions(-) + +diff --git a/iptables/nft-bridge.c b/iptables/nft-bridge.c +index e00a19e843d93..3fd03fb8de4ff 100644 +--- a/iptables/nft-bridge.c ++++ b/iptables/nft-bridge.c +@@ -530,6 +530,7 @@ static void nft_bridge_parse_target(struct xtables_target *t, void *data) + } + + cs->target = t; ++ cs->jumpto = t->name; + } + + static void nft_rule_to_ebtables_command_state(struct nft_handle *h, +diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c +index 172cf2054a33c..d73d0b6159be6 100644 +--- a/iptables/nft-shared.c ++++ b/iptables/nft-shared.c +@@ -511,6 +511,8 @@ static void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e) + { + const char *chain = nftnl_expr_get_str(e, NFTNL_EXPR_IMM_CHAIN); + struct iptables_command_state *cs = ctx->cs; ++ struct xt_entry_target *t; ++ uint32_t size; + int verdict; + + if (nftnl_expr_is_set(e, NFTNL_EXPR_IMM_DATA)) { +@@ -547,8 +549,21 @@ static void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e) + /* fall through */ + case NFT_JUMP: + cs->jumpto = chain; +- break; ++ /* fall through */ ++ default: ++ return; + } ++ ++ cs->target = xtables_find_target(cs->jumpto, XTF_TRY_LOAD); ++ if (!cs->target) ++ return; ++ ++ size = XT_ALIGN(sizeof(struct xt_entry_target)) + cs->target->size; ++ t = xtables_calloc(1, size); ++ t->u.target_size = size; ++ t->u.user.revision = cs->target->revision; ++ strcpy(t->u.user.name, cs->jumpto); ++ cs->target->t = t; + } + + static void nft_parse_limit(struct nft_xt_ctx *ctx, struct nftnl_expr *e) +@@ -676,25 +691,8 @@ void nft_rule_to_iptables_command_state(struct nft_handle *h, + } + } + +- if (cs->target != NULL) { +- cs->jumpto = cs->target->name; +- } else if (cs->jumpto != NULL) { +- struct xt_entry_target *t; +- uint32_t size; +- +- cs->target = xtables_find_target(cs->jumpto, XTF_TRY_LOAD); +- if (!cs->target) +- return; +- +- size = XT_ALIGN(sizeof(struct xt_entry_target)) + cs->target->size; +- t = xtables_calloc(1, size); +- t->u.target_size = size; +- t->u.user.revision = cs->target->revision; +- strcpy(t->u.user.name, cs->jumpto); +- cs->target->t = t; +- } else { ++ if (!cs->jumpto) + cs->jumpto = ""; +- } + } + + void nft_clear_iptables_command_state(struct iptables_command_state *cs) +@@ -991,6 +989,7 @@ void nft_ipv46_parse_target(struct xtables_target *t, void *data) + struct iptables_command_state *cs = data; + + cs->target = t; ++ cs->jumpto = t->name; + } + + bool nft_ipv46_rule_find(struct nft_handle *h, struct nftnl_rule *r, void *data) +-- +2.34.1 + diff --git a/SOURCES/0065-xshared-Prefer-xtables_chain_protos-lookup-over-getp.patch b/SOURCES/0065-xshared-Prefer-xtables_chain_protos-lookup-over-getp.patch new file mode 100644 index 0000000..9d2bd59 --- /dev/null +++ b/SOURCES/0065-xshared-Prefer-xtables_chain_protos-lookup-over-getp.patch @@ -0,0 +1,104 @@ +From daca1bc21c6fca067d861792c97357d7561a0564 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 1 Mar 2022 23:05:29 +0100 +Subject: [PATCH] xshared: Prefer xtables_chain_protos lookup over getprotoent + +When dumping a large ruleset, common protocol matches such as for TCP +port number significantly slow down rule printing due to repeated calls +for getprotobynumber(). The latter does not involve any caching, so +/etc/protocols is consulted over and over again. + +As a simple countermeasure, make functions converting between proto +number and name prefer the built-in list of "well-known" protocols. This +is not a perfect solution, repeated rules for protocol names libxtables +does not cache (e.g. igmp or dccp) will still be slow. Implementing +getprotoent() result caching could solve this. + +As a side-effect, explicit check for pseudo-protocol "all" may be +dropped as it is contained in the built-in list and therefore immutable. + +Also update xtables_chain_protos entries a bit to align with typical +/etc/protocols contents. The testsuite assumes those names, so the +preferred ones prior to this patch are indeed uncommon nowadays. + +Signed-off-by: Phil Sutter +Acked-by: Florian Westphal +(cherry picked from commit b6196c7504d4d41827cea86c167926125cdbf1f3) +--- + iptables/xshared.c | 8 ++++---- + libxtables/xtables.c | 19 ++++++------------- + 2 files changed, 10 insertions(+), 17 deletions(-) + +diff --git a/iptables/xshared.c b/iptables/xshared.c +index e3c8072b5ca96..dcc995a9cabe6 100644 +--- a/iptables/xshared.c ++++ b/iptables/xshared.c +@@ -52,16 +52,16 @@ proto_to_name(uint8_t proto, int nolookup) + { + unsigned int i; + ++ for (i = 0; xtables_chain_protos[i].name != NULL; ++i) ++ if (xtables_chain_protos[i].num == proto) ++ return xtables_chain_protos[i].name; ++ + if (proto && !nolookup) { + struct protoent *pent = getprotobynumber(proto); + if (pent) + return pent->p_name; + } + +- for (i = 0; xtables_chain_protos[i].name != NULL; ++i) +- if (xtables_chain_protos[i].num == proto) +- return xtables_chain_protos[i].name; +- + return NULL; + } + +diff --git a/libxtables/xtables.c b/libxtables/xtables.c +index 28ffffedd8147..58dd69440253d 100644 +--- a/libxtables/xtables.c ++++ b/libxtables/xtables.c +@@ -2021,10 +2021,11 @@ const struct xtables_pprot xtables_chain_protos[] = { + {"udp", IPPROTO_UDP}, + {"udplite", IPPROTO_UDPLITE}, + {"icmp", IPPROTO_ICMP}, +- {"icmpv6", IPPROTO_ICMPV6}, + {"ipv6-icmp", IPPROTO_ICMPV6}, ++ {"icmpv6", IPPROTO_ICMPV6}, + {"esp", IPPROTO_ESP}, + {"ah", IPPROTO_AH}, ++ {"mobility-header", IPPROTO_MH}, + {"ipv6-mh", IPPROTO_MH}, + {"mh", IPPROTO_MH}, + {"all", 0}, +@@ -2040,23 +2041,15 @@ xtables_parse_protocol(const char *s) + if (xtables_strtoui(s, NULL, &proto, 0, UINT8_MAX)) + return proto; + +- /* first deal with the special case of 'all' to prevent +- * people from being able to redefine 'all' in nsswitch +- * and/or provoke expensive [not working] ldap/nis/... +- * lookups */ +- if (strcmp(s, "all") == 0) +- return 0; ++ for (i = 0; xtables_chain_protos[i].name != NULL; ++i) { ++ if (strcmp(s, xtables_chain_protos[i].name) == 0) ++ return xtables_chain_protos[i].num; ++ } + + pent = getprotobyname(s); + if (pent != NULL) + return pent->p_proto; + +- for (i = 0; i < ARRAY_SIZE(xtables_chain_protos); ++i) { +- if (xtables_chain_protos[i].name == NULL) +- continue; +- if (strcmp(s, xtables_chain_protos[i].name) == 0) +- return xtables_chain_protos[i].num; +- } + xt_params->exit_err(PARAMETER_PROBLEM, + "unknown protocol \"%s\" specified", s); + return -1; +-- +2.34.1 + diff --git a/SOURCES/0066-xshared-Merge-and-share-parse_chain.patch b/SOURCES/0066-xshared-Merge-and-share-parse_chain.patch new file mode 100644 index 0000000..2d6b216 --- /dev/null +++ b/SOURCES/0066-xshared-Merge-and-share-parse_chain.patch @@ -0,0 +1,163 @@ +From 767c668628296fb3236aeeea1699ce06e66e5270 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Fri, 5 Apr 2019 13:21:19 +0200 +Subject: [PATCH] xshared: Merge and share parse_chain() + +Have a common routine to perform chain name checks, combining all +variants' requirements. + +Signed-off-by: Phil Sutter +(cherry picked from commit 1189d830ea4fd269da87761d400ebabca02e1ef3) + +Conflicts: + iptables/ip6tables.c + iptables/xshared.c +-> Context changes due to missing commit 9dc50b5b8e441 + ("xshared: Merge invflags handling code") +--- + iptables/ip6tables.c | 26 -------------------------- + iptables/iptables.c | 25 ------------------------- + iptables/xshared.c | 24 ++++++++++++++++++++++++ + iptables/xshared.h | 1 + + iptables/xtables.c | 9 +-------- + 5 files changed, 26 insertions(+), 59 deletions(-) + +diff --git a/iptables/ip6tables.c b/iptables/ip6tables.c +index 576c2cf8b0d9f..614d1e249c06d 100644 +--- a/iptables/ip6tables.c ++++ b/iptables/ip6tables.c +@@ -327,32 +327,6 @@ static int is_exthdr(uint16_t proto) + proto == IPPROTO_DSTOPTS); + } + +-static void +-parse_chain(const char *chainname) +-{ +- const char *ptr; +- +- if (strlen(chainname) >= XT_EXTENSION_MAXNAMELEN) +- xtables_error(PARAMETER_PROBLEM, +- "chain name `%s' too long (must be under %u chars)", +- chainname, XT_EXTENSION_MAXNAMELEN); +- +- if (*chainname == '-' || *chainname == '!') +- xtables_error(PARAMETER_PROBLEM, +- "chain name not allowed to start " +- "with `%c'\n", *chainname); +- +- if (xtables_find_target(chainname, XTF_TRY_LOAD)) +- xtables_error(PARAMETER_PROBLEM, +- "chain name may not clash " +- "with target name\n"); +- +- for (ptr = chainname; *ptr; ptr++) +- if (isspace(*ptr)) +- xtables_error(PARAMETER_PROBLEM, +- "Invalid chain name `%s'", chainname); +-} +- + static void + set_option(unsigned int *options, unsigned int option, uint8_t *invflg, + int invert) +diff --git a/iptables/iptables.c b/iptables/iptables.c +index 88ef6cf666d4b..3b395981cc8ea 100644 +--- a/iptables/iptables.c ++++ b/iptables/iptables.c +@@ -319,31 +319,6 @@ opt2char(int option) + + /* Christophe Burki wants `-p 6' to imply `-m tcp'. */ + +-static void +-parse_chain(const char *chainname) +-{ +- const char *ptr; +- +- if (strlen(chainname) >= XT_EXTENSION_MAXNAMELEN) +- xtables_error(PARAMETER_PROBLEM, +- "chain name `%s' too long (must be under %u chars)", +- chainname, XT_EXTENSION_MAXNAMELEN); +- +- if (*chainname == '-' || *chainname == '!') +- xtables_error(PARAMETER_PROBLEM, +- "chain name not allowed to start " +- "with `%c'\n", *chainname); +- +- if (xtables_find_target(chainname, XTF_TRY_LOAD)) +- xtables_error(PARAMETER_PROBLEM, +- "chain name may not clash " +- "with target name\n"); +- +- for (ptr = chainname; *ptr; ptr++) +- if (isspace(*ptr)) +- xtables_error(PARAMETER_PROBLEM, +- "Invalid chain name `%s'", chainname); +-} + + static void + set_option(unsigned int *options, unsigned int option, uint8_t *invflg, +diff --git a/iptables/xshared.c b/iptables/xshared.c +index dcc995a9cabe6..de8326b6c7b05 100644 +--- a/iptables/xshared.c ++++ b/iptables/xshared.c +@@ -775,3 +775,27 @@ int parse_rulenumber(const char *rule) + + return rulenum; + } ++ ++void parse_chain(const char *chainname) ++{ ++ const char *ptr; ++ ++ if (strlen(chainname) >= XT_EXTENSION_MAXNAMELEN) ++ xtables_error(PARAMETER_PROBLEM, ++ "chain name `%s' too long (must be under %u chars)", ++ chainname, XT_EXTENSION_MAXNAMELEN); ++ ++ if (*chainname == '-' || *chainname == '!') ++ xtables_error(PARAMETER_PROBLEM, ++ "chain name not allowed to start with `%c'\n", ++ *chainname); ++ ++ if (xtables_find_target(chainname, XTF_TRY_LOAD)) ++ xtables_error(PARAMETER_PROBLEM, ++ "chain name may not clash with target name\n"); ++ ++ for (ptr = chainname; *ptr; ptr++) ++ if (isspace(*ptr)) ++ xtables_error(PARAMETER_PROBLEM, ++ "Invalid chain name `%s'", chainname); ++} +diff --git a/iptables/xshared.h b/iptables/xshared.h +index e4015c00e2a35..f5d2f8d0a2bc5 100644 +--- a/iptables/xshared.h ++++ b/iptables/xshared.h +@@ -217,5 +217,6 @@ char cmd2char(int option); + void add_command(unsigned int *cmd, const int newcmd, + const int othercmds, int invert); + int parse_rulenumber(const char *rule); ++void parse_chain(const char *chainname); + + #endif /* IPTABLES_XSHARED_H */ +diff --git a/iptables/xtables.c b/iptables/xtables.c +index 8c2d21d42b7d2..3ea293ee7c411 100644 +--- a/iptables/xtables.c ++++ b/iptables/xtables.c +@@ -668,14 +668,7 @@ void do_parse(struct nft_handle *h, int argc, char *argv[], + break; + + case 'N': +- if (optarg && (*optarg == '-' || *optarg == '!')) +- xtables_error(PARAMETER_PROBLEM, +- "chain name not allowed to start " +- "with `%c'\n", *optarg); +- if (xtables_find_target(optarg, XTF_TRY_LOAD)) +- xtables_error(PARAMETER_PROBLEM, +- "chain name may not clash " +- "with target name\n"); ++ parse_chain(optarg); + add_command(&p->command, CMD_NEW_CHAIN, CMD_NONE, + cs->invert); + p->chain = optarg; +-- +2.34.1 + diff --git a/SOURCES/0067-nft-Reject-standard-targets-as-chain-names-when-rest.patch b/SOURCES/0067-nft-Reject-standard-targets-as-chain-names-when-rest.patch new file mode 100644 index 0000000..a26a9c0 --- /dev/null +++ b/SOURCES/0067-nft-Reject-standard-targets-as-chain-names-when-rest.patch @@ -0,0 +1,112 @@ +From 257c18bf4fbcc7e5f4fb3c9cadab699986a9bd41 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Wed, 16 Mar 2022 17:14:07 +0100 +Subject: [PATCH] nft: Reject standard targets as chain names when restoring + +Reuse parse_chain() called from do_parse() for '-N' and rename it for a +better description of what it does. + +Note that by itself, this patch will likely kill iptables-restore +performance for big rulesets due to the extra extension lookup for chain +lines. A following patch announcing those chains to libxtables will +alleviate that. + +Signed-off-by: Phil Sutter +Reviewed-by: Florian Westphal +(cherry picked from commit b1aee6b2238794446feba41778f88703784560f7) + +Conflicts: + iptables/xshared.c +-> Parts manually applied due to unmerged do_parse() function. +--- + iptables/ip6tables.c | 2 +- + iptables/iptables.c | 2 +- + iptables/xshared.c | 2 +- + iptables/xshared.h | 2 +- + iptables/xtables-restore.c | 5 +---- + iptables/xtables.c | 2 +- + 6 files changed, 6 insertions(+), 9 deletions(-) + +diff --git a/iptables/ip6tables.c b/iptables/ip6tables.c +index 614d1e249c06d..b96dc033e7ebb 100644 +--- a/iptables/ip6tables.c ++++ b/iptables/ip6tables.c +@@ -1247,7 +1247,7 @@ int do_command6(int argc, char *argv[], char **table, + break; + + case 'N': +- parse_chain(optarg); ++ assert_valid_chain_name(optarg); + add_command(&command, CMD_NEW_CHAIN, CMD_NONE, + cs.invert); + chain = optarg; +diff --git a/iptables/iptables.c b/iptables/iptables.c +index 3b395981cc8ea..6e2946f5660de 100644 +--- a/iptables/iptables.c ++++ b/iptables/iptables.c +@@ -1243,7 +1243,7 @@ int do_command4(int argc, char *argv[], char **table, + break; + + case 'N': +- parse_chain(optarg); ++ assert_valid_chain_name(optarg); + add_command(&command, CMD_NEW_CHAIN, CMD_NONE, + cs.invert); + chain = optarg; +diff --git a/iptables/xshared.c b/iptables/xshared.c +index de8326b6c7b05..0c232ca2ae8d5 100644 +--- a/iptables/xshared.c ++++ b/iptables/xshared.c +@@ -776,7 +776,7 @@ int parse_rulenumber(const char *rule) + return rulenum; + } + +-void parse_chain(const char *chainname) ++void assert_valid_chain_name(const char *chainname) + { + const char *ptr; + +diff --git a/iptables/xshared.h b/iptables/xshared.h +index f5d2f8d0a2bc5..095a574d85879 100644 +--- a/iptables/xshared.h ++++ b/iptables/xshared.h +@@ -217,6 +217,6 @@ char cmd2char(int option); + void add_command(unsigned int *cmd, const int newcmd, + const int othercmds, int invert); + int parse_rulenumber(const char *rule); +-void parse_chain(const char *chainname); ++void assert_valid_chain_name(const char *chainname); + + #endif /* IPTABLES_XSHARED_H */ +diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c +index c472ac9bf651b..a078da32045dc 100644 +--- a/iptables/xtables-restore.c ++++ b/iptables/xtables-restore.c +@@ -150,10 +150,7 @@ static void xtables_restore_parse_line(struct nft_handle *h, + "%s: line %u chain name invalid\n", + xt_params->program_name, line); + +- if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN) +- xtables_error(PARAMETER_PROBLEM, +- "Invalid chain name `%s' (%u chars max)", +- chain, XT_EXTENSION_MAXNAMELEN - 1); ++ assert_valid_chain_name(chain); + + policy = strtok(NULL, " \t\n"); + DEBUGP("line %u, policy '%s'\n", line, policy); +diff --git a/iptables/xtables.c b/iptables/xtables.c +index 3ea293ee7c411..9006962472c58 100644 +--- a/iptables/xtables.c ++++ b/iptables/xtables.c +@@ -668,7 +668,7 @@ void do_parse(struct nft_handle *h, int argc, char *argv[], + break; + + case 'N': +- parse_chain(optarg); ++ assert_valid_chain_name(optarg); + add_command(&p->command, CMD_NEW_CHAIN, CMD_NONE, + cs->invert); + p->chain = optarg; +-- +2.34.1 + diff --git a/SOURCES/0068-libxtables-Implement-notargets-hash-table.patch b/SOURCES/0068-libxtables-Implement-notargets-hash-table.patch new file mode 100644 index 0000000..082886c --- /dev/null +++ b/SOURCES/0068-libxtables-Implement-notargets-hash-table.patch @@ -0,0 +1,142 @@ +From 3e8e2f0a6590a3b1eeb989e364fe4b5638be108f Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 15 Dec 2020 15:40:56 +0100 +Subject: [PATCH] libxtables: Implement notargets hash table + +Target lookup is relatively costly due to the filesystem access. Avoid +this overhead in huge rulesets which contain many chain jumps by caching +the failed lookups into a hashtable for later. + +Signed-off-by: Phil Sutter +Acked-by: Florian Westphal +(cherry picked from commit f58b0d7406451afbb4b9b6c7888990c964fa7c79) + +Conflicts: + libxtables/xtables.c +-> Context changes and missing xtables_fini() due to missing commit + 7db4333dc0b6c ("libxtables: Introduce xtables_fini()") +--- + libxtables/xtables.c | 79 ++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 79 insertions(+) + +diff --git a/libxtables/xtables.c b/libxtables/xtables.c +index 58dd69440253d..1e1c218df7441 100644 +--- a/libxtables/xtables.c ++++ b/libxtables/xtables.c +@@ -48,6 +48,7 @@ + #include + #include + #include ++#include + + #ifndef NO_SHARED_LIBS + #include +@@ -208,6 +209,71 @@ static bool xtables_fully_register_pending_match(struct xtables_match *me, + static bool xtables_fully_register_pending_target(struct xtables_target *me, + struct xtables_target *prev); + ++struct notarget { ++ struct hlist_node node; ++ char name[]; ++}; ++ ++#define NOTARGET_HSIZE 512 ++static struct hlist_head notargets[NOTARGET_HSIZE]; ++ ++static void notargets_hlist_init(void) ++{ ++ int i; ++ ++ for (i = 0; i < NOTARGET_HSIZE; i++) ++ INIT_HLIST_HEAD(¬argets[i]); ++} ++ ++static void notargets_hlist_free(void) ++{ ++ struct hlist_node *pos, *n; ++ struct notarget *cur; ++ int i; ++ ++ for (i = 0; i < NOTARGET_HSIZE; i++) { ++ hlist_for_each_entry_safe(cur, pos, n, ¬argets[i], node) { ++ hlist_del(&cur->node); ++ free(cur); ++ } ++ } ++} ++ ++static uint32_t djb_hash(const char *key) ++{ ++ uint32_t i, hash = 5381; ++ ++ for (i = 0; i < strlen(key); i++) ++ hash = ((hash << 5) + hash) + key[i]; ++ ++ return hash; ++} ++ ++static struct notarget *notargets_hlist_lookup(const char *name) ++{ ++ uint32_t key = djb_hash(name) % NOTARGET_HSIZE; ++ struct hlist_node *node; ++ struct notarget *cur; ++ ++ hlist_for_each_entry(cur, node, ¬argets[key], node) { ++ if (!strcmp(name, cur->name)) ++ return cur; ++ } ++ return NULL; ++} ++ ++static void notargets_hlist_insert(const char *name) ++{ ++ struct notarget *cur; ++ ++ if (!name) ++ return; ++ ++ cur = xtables_malloc(sizeof(*cur) + strlen(name) + 1); ++ strcpy(cur->name, name); ++ hlist_add_head(&cur->node, ¬argets[djb_hash(name) % NOTARGET_HSIZE]); ++} ++ + void xtables_init(void) + { + xtables_libdir = getenv("XTABLES_LIBDIR"); +@@ -233,6 +299,13 @@ void xtables_init(void) + return; + } + xtables_libdir = XTABLES_LIBDIR; ++ ++ notargets_hlist_init(); ++} ++ ++void xtables_fini(void) ++{ ++ notargets_hlist_free(); + } + + void xtables_set_nfproto(uint8_t nfproto) +@@ -750,6 +823,10 @@ xtables_find_target(const char *name, enum xtables_tryload tryload) + || strcmp(name, XTC_LABEL_QUEUE) == 0 + || strcmp(name, XTC_LABEL_RETURN) == 0) + name = "standard"; ++ /* known non-target? */ ++ else if (notargets_hlist_lookup(name) && ++ tryload != XTF_LOAD_MUST_SUCCEED) ++ return NULL; + + /* Trigger delayed initialization */ + for (dptr = &xtables_pending_targets; *dptr; ) { +@@ -813,6 +890,8 @@ xtables_find_target(const char *name, enum xtables_tryload tryload) + + if (ptr) + ptr->used = 1; ++ else ++ notargets_hlist_insert(name); + + return ptr; + } +-- +2.34.1 + diff --git a/SOURCES/0069-libxtables-Boost-rule-target-checks-by-announcing-ch.patch b/SOURCES/0069-libxtables-Boost-rule-target-checks-by-announcing-ch.patch new file mode 100644 index 0000000..7116ad2 --- /dev/null +++ b/SOURCES/0069-libxtables-Boost-rule-target-checks-by-announcing-ch.patch @@ -0,0 +1,86 @@ +From 86bf4207cb744c38807fb5c42c5921fc9964a2af Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Fri, 4 Mar 2022 12:50:01 +0100 +Subject: [PATCH] libxtables: Boost rule target checks by announcing chain + names + +When restoring a ruleset, feed libxtables with chain names from +respective lines to avoid an extension search. + +While the user's intention is clear, this effectively disables the +sanity check for clashes with target extensions. But: + +* The check yielded only a warning and the clashing chain was finally + accepted. + +* Users crafting iptables dumps for feeding into iptables-restore likely + know what they're doing. + +Signed-off-by: Phil Sutter +Acked-by: Florian Westphal +(cherry picked from commit ac4c84cc63d3cc021ca532692885a644fcde4518) +--- + include/xtables.h | 3 +++ + iptables/iptables-restore.c | 1 + + iptables/xtables-restore.c | 1 + + libxtables/xtables.c | 6 ++++++ + 4 files changed, 11 insertions(+) + +diff --git a/include/xtables.h b/include/xtables.h +index 4aa084a1a2a30..d77a73a4303a7 100644 +--- a/include/xtables.h ++++ b/include/xtables.h +@@ -632,6 +632,9 @@ void xt_xlate_add_comment(struct xt_xlate *xl, const char *comment); + const char *xt_xlate_get_comment(struct xt_xlate *xl); + const char *xt_xlate_get(struct xt_xlate *xl); + ++/* informed target lookups */ ++void xtables_announce_chain(const char *name); ++ + #ifdef XTABLES_INTERNAL + + /* Shipped modules rely on this... */ +diff --git a/iptables/iptables-restore.c b/iptables/iptables-restore.c +index b0a51d491c508..339abaa32a055 100644 +--- a/iptables/iptables-restore.c ++++ b/iptables/iptables-restore.c +@@ -309,6 +309,7 @@ ip46tables_restore_main(const struct iptables_restore_cb *cb, + cb->ops->strerror(errno)); + } + ++ xtables_announce_chain(chain); + ret = 1; + + } else if (in_table) { +diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c +index a078da32045dc..41e7cb7661464 100644 +--- a/iptables/xtables-restore.c ++++ b/iptables/xtables-restore.c +@@ -150,6 +150,7 @@ static void xtables_restore_parse_line(struct nft_handle *h, + "%s: line %u chain name invalid\n", + xt_params->program_name, line); + ++ xtables_announce_chain(chain); + assert_valid_chain_name(chain); + + policy = strtok(NULL, " \t\n"); +diff --git a/libxtables/xtables.c b/libxtables/xtables.c +index 1e1c218df7441..4aee74acb6816 100644 +--- a/libxtables/xtables.c ++++ b/libxtables/xtables.c +@@ -274,6 +274,12 @@ static void notargets_hlist_insert(const char *name) + hlist_add_head(&cur->node, ¬argets[djb_hash(name) % NOTARGET_HSIZE]); + } + ++void xtables_announce_chain(const char *name) ++{ ++ if (!notargets_hlist_lookup(name)) ++ notargets_hlist_insert(name); ++} ++ + void xtables_init(void) + { + xtables_libdir = getenv("XTABLES_LIBDIR"); +-- +2.34.1 + diff --git a/SOURCES/0070-Use-proto_to_name-from-xshared-in-more-places.patch b/SOURCES/0070-Use-proto_to_name-from-xshared-in-more-places.patch new file mode 100644 index 0000000..31e0d66 --- /dev/null +++ b/SOURCES/0070-Use-proto_to_name-from-xshared-in-more-places.patch @@ -0,0 +1,156 @@ +From f8839b3651e0ffbb93b6ce4675809d60782a4396 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 17 Nov 2020 00:57:10 +0100 +Subject: [PATCH] Use proto_to_name() from xshared in more places + +Share the common proto name lookup code. While being at it, make proto +number variable 16bit, values may exceed 256. + +This aligns iptables-nft '-p' argument printing with legacy iptables. In +practice, this should make a difference only in corner cases. + +Signed-off-by: Phil Sutter +(cherry picked from commit 556f704458cdb509d395ddb7d2629987d60e762e) +--- + include/xtables.h | 2 +- + iptables/ip6tables.c | 22 +++++----------------- + iptables/iptables.c | 20 +++++--------------- + iptables/nft-shared.c | 6 +++--- + iptables/xshared.c | 2 +- + iptables/xshared.h | 2 +- + 6 files changed, 16 insertions(+), 38 deletions(-) + +diff --git a/include/xtables.h b/include/xtables.h +index d77a73a4303a7..06982e720cbb8 100644 +--- a/include/xtables.h ++++ b/include/xtables.h +@@ -395,7 +395,7 @@ struct xtables_rule_match { + */ + struct xtables_pprot { + const char *name; +- uint8_t num; ++ uint16_t num; + }; + + enum xtables_tryload { +diff --git a/iptables/ip6tables.c b/iptables/ip6tables.c +index b96dc033e7ebb..4860682001360 100644 +--- a/iptables/ip6tables.c ++++ b/iptables/ip6tables.c +@@ -849,28 +849,16 @@ print_iface(char letter, const char *iface, const unsigned char *mask, + } + } + +-/* The ip6tables looks up the /etc/protocols. */ + static void print_proto(uint16_t proto, int invert) + { + if (proto) { +- unsigned int i; ++ const char *pname = proto_to_name(proto, 0); + const char *invertstr = invert ? " !" : ""; + +- const struct protoent *pent = getprotobynumber(proto); +- if (pent) { +- printf("%s -p %s", +- invertstr, pent->p_name); +- return; +- } +- +- for (i = 0; xtables_chain_protos[i].name != NULL; ++i) +- if (xtables_chain_protos[i].num == proto) { +- printf("%s -p %s", +- invertstr, xtables_chain_protos[i].name); +- return; +- } +- +- printf("%s -p %u", invertstr, proto); ++ if (pname) ++ printf("%s -p %s", invertstr, pname); ++ else ++ printf("%s -p %u", invertstr, proto); + } + } + +diff --git a/iptables/iptables.c b/iptables/iptables.c +index 6e2946f5660de..620429b5d4817 100644 +--- a/iptables/iptables.c ++++ b/iptables/iptables.c +@@ -819,23 +819,13 @@ list_entries(const xt_chainlabel chain, int rulenum, int verbose, int numeric, + static void print_proto(uint16_t proto, int invert) + { + if (proto) { +- unsigned int i; ++ const char *pname = proto_to_name(proto, 0); + const char *invertstr = invert ? " !" : ""; + +- const struct protoent *pent = getprotobynumber(proto); +- if (pent) { +- printf("%s -p %s", invertstr, pent->p_name); +- return; +- } +- +- for (i = 0; xtables_chain_protos[i].name != NULL; ++i) +- if (xtables_chain_protos[i].num == proto) { +- printf("%s -p %s", +- invertstr, xtables_chain_protos[i].name); +- return; +- } +- +- printf("%s -p %u", invertstr, proto); ++ if (pname) ++ printf("%s -p %s", invertstr, pname); ++ else ++ printf("%s -p %u", invertstr, proto); + } + } + +diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c +index d73d0b6159be6..e3ba4ac34146f 100644 +--- a/iptables/nft-shared.c ++++ b/iptables/nft-shared.c +@@ -821,13 +821,13 @@ void save_rule_details(const struct iptables_command_state *cs, + } + + if (proto > 0) { +- const struct protoent *pent = getprotobynumber(proto); ++ const char *pname = proto_to_name(proto, 0); + + if (invflags & XT_INV_PROTO) + printf("! "); + +- if (pent) +- printf("-p %s ", pent->p_name); ++ if (pname) ++ printf("-p %s ", pname); + else + printf("-p %u ", proto); + } +diff --git a/iptables/xshared.c b/iptables/xshared.c +index 0c232ca2ae8d5..7a55ed5d15715 100644 +--- a/iptables/xshared.c ++++ b/iptables/xshared.c +@@ -48,7 +48,7 @@ void print_extension_helps(const struct xtables_target *t, + } + + const char * +-proto_to_name(uint8_t proto, int nolookup) ++proto_to_name(uint16_t proto, int nolookup) + { + unsigned int i; + +diff --git a/iptables/xshared.h b/iptables/xshared.h +index 095a574d85879..f3c7f28806619 100644 +--- a/iptables/xshared.h ++++ b/iptables/xshared.h +@@ -146,7 +146,7 @@ enum { + + extern void print_extension_helps(const struct xtables_target *, + const struct xtables_rule_match *); +-extern const char *proto_to_name(uint8_t, int); ++extern const char *proto_to_name(uint16_t, int); + extern int command_default(struct iptables_command_state *, + struct xtables_globals *); + extern struct xtables_match *load_proto(struct iptables_command_state *); +-- +2.34.1 + diff --git a/SOURCES/0071-libxtables-Register-only-the-highest-revision-extens.patch b/SOURCES/0071-libxtables-Register-only-the-highest-revision-extens.patch new file mode 100644 index 0000000..cf381ec --- /dev/null +++ b/SOURCES/0071-libxtables-Register-only-the-highest-revision-extens.patch @@ -0,0 +1,64 @@ +From afcbce6924dfe05af4b41bf46b21794f4a4d8302 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Fri, 11 Feb 2022 17:39:24 +0100 +Subject: [PATCH] libxtables: Register only the highest revision extension + +When fully registering extensions, ignore all consecutive ones with same +name and family value. Since commit b3ac87038f4e4 ("libxtables: Make +sure extensions register in revision order"), one may safely assume the +list of pending extensions has highest revision numbers first. Since +iptables is only interested in the highest revision the kernel supports, +registration and compatibility checks may be skipped once the first +matching extension in pending list has validated. + +Signed-off-by: Phil Sutter +(cherry picked from commit 2dbb49d15fb44ddd521a734eca3be3f940b7c1ba) +--- + libxtables/xtables.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/libxtables/xtables.c b/libxtables/xtables.c +index 4aee74acb6816..57ad0330a454c 100644 +--- a/libxtables/xtables.c ++++ b/libxtables/xtables.c +@@ -701,6 +701,7 @@ xtables_find_match(const char *name, enum xtables_tryload tryload, + struct xtables_match **dptr; + struct xtables_match *ptr; + const char *icmp6 = "icmp6"; ++ bool found = false; + + if (strlen(name) >= XT_EXTENSION_MAXNAMELEN) + xtables_error(PARAMETER_PROBLEM, +@@ -719,7 +720,9 @@ xtables_find_match(const char *name, enum xtables_tryload tryload, + if (extension_cmp(name, (*dptr)->name, (*dptr)->family)) { + ptr = *dptr; + *dptr = (*dptr)->next; +- if (xtables_fully_register_pending_match(ptr, prev)) { ++ if (!found && ++ xtables_fully_register_pending_match(ptr, prev)) { ++ found = true; + prev = ptr; + continue; + } else if (prev) { +@@ -821,6 +824,7 @@ xtables_find_target(const char *name, enum xtables_tryload tryload) + struct xtables_target *prev = NULL; + struct xtables_target **dptr; + struct xtables_target *ptr; ++ bool found = false; + + /* Standard target? */ + if (strcmp(name, "") == 0 +@@ -839,7 +843,9 @@ xtables_find_target(const char *name, enum xtables_tryload tryload) + if (extension_cmp(name, (*dptr)->name, (*dptr)->family)) { + ptr = *dptr; + *dptr = (*dptr)->next; +- if (xtables_fully_register_pending_target(ptr, prev)) { ++ if (!found && ++ xtables_fully_register_pending_target(ptr, prev)) { ++ found = true; + prev = ptr; + continue; + } else if (prev) { +-- +2.34.1 + diff --git a/SOURCES/0072-xshared-Fix-response-to-unprivileged-users.patch b/SOURCES/0072-xshared-Fix-response-to-unprivileged-users.patch new file mode 100644 index 0000000..abe0780 --- /dev/null +++ b/SOURCES/0072-xshared-Fix-response-to-unprivileged-users.patch @@ -0,0 +1,119 @@ +From 6d1b02218a591ff95053b22c1ed802355e44878d Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 18 Jan 2022 22:39:08 +0100 +Subject: [PATCH] xshared: Fix response to unprivileged users + +Expected behaviour in both variants is: + +* Print help without error, append extension help if -m and/or -j + options are present +* Indicate lack of permissions in an error message for anything else + +With iptables-nft, this was broken basically from day 1. Shared use of +do_parse() then somewhat broke legacy: it started complaining about +inability to create a lock file. + +Fix this by making iptables-nft assume extension revision 0 is present +if permissions don't allow to verify. This is consistent with legacy. + +Second part is to exit directly after printing help - this avoids having +to make the following code "nop-aware" to prevent privileged actions. + +Signed-off-by: Phil Sutter +Reviewed-by: Florian Westphal +(cherry picked from commit 26ecdf53960658771c0fc582f72a4025e2887f75) + +Conflicts: + iptables/xshared.c +-> Some chunks not applied as not necessary in RHEL8. +--- + iptables/nft.c | 5 ++ + .../testcases/iptables/0008-unprivileged_0 | 60 +++++++++++++++++++ + 2 files changed, 65 insertions(+) + create mode 100755 iptables/tests/shell/testcases/iptables/0008-unprivileged_0 + +diff --git a/iptables/nft.c b/iptables/nft.c +index dc5490c085364..c5cc6f83bf573 100644 +--- a/iptables/nft.c ++++ b/iptables/nft.c +@@ -3110,6 +3110,11 @@ int nft_compatible_revision(const char *name, uint8_t rev, int opt) + err: + mnl_socket_close(nl); + ++ /* pretend revision 0 is valid if not permitted to check - ++ * this is required for printing extension help texts as user */ ++ if (ret < 0 && errno == EPERM && rev == 0) ++ return 1; ++ + return ret < 0 ? 0 : 1; + } + +diff --git a/iptables/tests/shell/testcases/iptables/0008-unprivileged_0 b/iptables/tests/shell/testcases/iptables/0008-unprivileged_0 +new file mode 100755 +index 0000000000000..43e3bc8721dbd +--- /dev/null ++++ b/iptables/tests/shell/testcases/iptables/0008-unprivileged_0 +@@ -0,0 +1,60 @@ ++#!/bin/bash ++ ++# iptables may print match/target specific help texts ++# help output should work for unprivileged users ++ ++run() { ++ echo "running: $*" >&2 ++ runuser -u nobody -- "$@" ++} ++ ++grep_or_rc() { ++ declare -g rc ++ grep -q "$*" && return 0 ++ echo "missing in output: $*" >&2 ++ return 1 ++} ++ ++out=$(run $XT_MULTI iptables --help) ++let "rc+=$?" ++grep_or_rc "iptables -h (print this help information)" <<< "$out" ++let "rc+=$?" ++ ++out=$(run $XT_MULTI iptables -m limit --help) ++let "rc+=$?" ++grep_or_rc "limit match options:" <<< "$out" ++let "rc+=$?" ++ ++out=$(run $XT_MULTI iptables -p tcp --help) ++let "rc+=$?" ++grep_or_rc "tcp match options:" <<< "$out" ++let "rc+=$?" ++ ++out=$(run $XT_MULTI iptables -j DNAT --help) ++let "rc+=$?" ++grep_or_rc "DNAT target options:" <<< "$out" ++let "rc+=$?" ++ ++out=$(run $XT_MULTI iptables -p tcp -j DNAT --help) ++let "rc+=$?" ++grep_or_rc "tcp match options:" <<< "$out" ++let "rc+=$?" ++out=$(run $XT_MULTI iptables -p tcp -j DNAT --help) ++let "rc+=$?" ++grep_or_rc "DNAT target options:" <<< "$out" ++let "rc+=$?" ++ ++ ++run $XT_MULTI iptables -L 2>&1 | \ ++ grep_or_rc "Permission denied" ++let "rc+=$?" ++ ++run $XT_MULTI iptables -A FORWARD -p tcp --dport 123 2>&1 | \ ++ grep_or_rc "Permission denied" ++let "rc+=$?" ++ ++run $XT_MULTI iptables -A FORWARD -j DNAT --to-destination 1.2.3.4 2>&1 | \ ++ grep_or_rc "Permission denied" ++let "rc+=$?" ++ ++exit $rc +-- +2.34.1 + diff --git a/SOURCES/0073-Improve-error-messages-for-unsupported-extensions.patch b/SOURCES/0073-Improve-error-messages-for-unsupported-extensions.patch new file mode 100644 index 0000000..0fc7856 --- /dev/null +++ b/SOURCES/0073-Improve-error-messages-for-unsupported-extensions.patch @@ -0,0 +1,84 @@ +From 4243bd97f3c703a75e795fdc6dd2273a7c74e85c Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Fri, 11 Feb 2022 17:47:22 +0100 +Subject: [PATCH] Improve error messages for unsupported extensions + +If a given extension was not supported by the kernel, iptables would +print a rather confusing error message if extension parameters were +given: + +| # rm /lib/modules/$(uname -r)/kernel/net/netfilter/xt_LOG.ko +| # iptables -A FORWARD -j LOG --log-prefix foo +| iptables v1.8.7 (legacy): unknown option "--log-prefix" + +Avoid this by pretending extension revision 0 is always supported. It is +the same hack as used to successfully print extension help texts as +unprivileged user, extended to all error codes to serve privileged ones +as well. + +In addition, print a warning if kernel rejected revision 0 and it's not +a permissions problem. This helps users find out which extension in a +rule the kernel didn't like. + +Finally, the above commands result in these messages: + +| Warning: Extension LOG revision 0 not supported, missing kernel module? +| iptables: No chain/target/match by that name. + +Or, for iptables-nft: + +| Warning: Extension LOG revision 0 not supported, missing kernel module? +| iptables v1.8.7 (nf_tables): RULE_APPEND failed (No such file or directory): rule in chain FORWARD + +Signed-off-by: Phil Sutter +(cherry picked from commit 17534cb18ed0a5052dc45c117401251359dba6aa) +--- + iptables/nft.c | 12 +++++++++--- + libxtables/xtables.c | 7 ++++++- + 2 files changed, 15 insertions(+), 4 deletions(-) + +diff --git a/iptables/nft.c b/iptables/nft.c +index c5cc6f83bf573..9643abf2d0085 100644 +--- a/iptables/nft.c ++++ b/iptables/nft.c +@@ -3110,10 +3110,16 @@ int nft_compatible_revision(const char *name, uint8_t rev, int opt) + err: + mnl_socket_close(nl); + +- /* pretend revision 0 is valid if not permitted to check - +- * this is required for printing extension help texts as user */ +- if (ret < 0 && errno == EPERM && rev == 0) ++ /* pretend revision 0 is valid - ++ * this is required for printing extension help texts as user, also ++ * helps error messaging on unavailable kernel extension */ ++ if (ret < 0 && rev == 0) { ++ if (errno != EPERM) ++ fprintf(stderr, ++ "Warning: Extension %s revision 0 not supported, missing kernel module?\n", ++ name); + return 1; ++ } + + return ret < 0 ? 0 : 1; + } +diff --git a/libxtables/xtables.c b/libxtables/xtables.c +index 57ad0330a454c..a5c8d7e2c17ef 100644 +--- a/libxtables/xtables.c ++++ b/libxtables/xtables.c +@@ -968,7 +968,12 @@ int xtables_compatible_revision(const char *name, uint8_t revision, int opt) + /* Definitely don't support this? */ + if (errno == ENOENT || errno == EPROTONOSUPPORT) { + close(sockfd); +- return 0; ++ /* Pretend revision 0 support for better error messaging */ ++ if (revision == 0) ++ fprintf(stderr, ++ "Warning: Extension %s revision 0 not supported, missing kernel module?\n", ++ name); ++ return (revision == 0); + } else if (errno == ENOPROTOOPT) { + close(sockfd); + /* Assume only revision 0 support (old kernel) */ +-- +2.34.1 + diff --git a/SOURCES/0074-nft-Fix-EPERM-handling-for-extensions-without-rev-0.patch b/SOURCES/0074-nft-Fix-EPERM-handling-for-extensions-without-rev-0.patch new file mode 100644 index 0000000..9551eaf --- /dev/null +++ b/SOURCES/0074-nft-Fix-EPERM-handling-for-extensions-without-rev-0.patch @@ -0,0 +1,65 @@ +From 3696c4af80496a0dd5a3637e4be11754e2cdc99e Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Wed, 4 May 2022 11:19:16 +0200 +Subject: [PATCH] nft: Fix EPERM handling for extensions without rev 0 + +Treating revision 0 as compatible in EPERM case works fine as long as +there is a revision 0 of that extension defined in DSO. Fix the code for +others: Extend the EPERM handling to all revisions and keep the existing +warning for revision 0. + +Fixes: 17534cb18ed0a ("Improve error messages for unsupported extensions") +Signed-off-by: Phil Sutter +(cherry picked from commit 8468fd4f7c85c21ab375402bc80d0188412b6cbf) +--- + iptables/nft.c | 11 +++++++---- + .../shell/testcases/iptables/0008-unprivileged_0 | 6 ++++++ + 2 files changed, 13 insertions(+), 4 deletions(-) + +diff --git a/iptables/nft.c b/iptables/nft.c +index 9643abf2d0085..9839e8c683708 100644 +--- a/iptables/nft.c ++++ b/iptables/nft.c +@@ -3110,15 +3110,18 @@ int nft_compatible_revision(const char *name, uint8_t rev, int opt) + err: + mnl_socket_close(nl); + +- /* pretend revision 0 is valid - ++ /* ignore EPERM and errors for revision 0 - + * this is required for printing extension help texts as user, also + * helps error messaging on unavailable kernel extension */ +- if (ret < 0 && rev == 0) { +- if (errno != EPERM) ++ if (ret < 0) { ++ if (errno == EPERM) ++ return 1; ++ if (rev == 0) { + fprintf(stderr, + "Warning: Extension %s revision 0 not supported, missing kernel module?\n", + name); +- return 1; ++ return 1; ++ } + } + + return ret < 0 ? 0 : 1; +diff --git a/iptables/tests/shell/testcases/iptables/0008-unprivileged_0 b/iptables/tests/shell/testcases/iptables/0008-unprivileged_0 +index 43e3bc8721dbd..983531fef4720 100755 +--- a/iptables/tests/shell/testcases/iptables/0008-unprivileged_0 ++++ b/iptables/tests/shell/testcases/iptables/0008-unprivileged_0 +@@ -35,6 +35,12 @@ let "rc+=$?" + grep_or_rc "DNAT target options:" <<< "$out" + let "rc+=$?" + ++# TEE has no revision 0 ++out=$(run $XT_MULTI iptables -j TEE --help) ++let "rc+=$?" ++grep_or_rc "TEE target options:" <<< "$out" ++let "rc+=$?" ++ + out=$(run $XT_MULTI iptables -p tcp -j DNAT --help) + let "rc+=$?" + grep_or_rc "tcp match options:" <<< "$out" +-- +2.34.1 + diff --git a/SOURCES/0075-tests-shell-Check-overhead-in-iptables-save-and-rest.patch b/SOURCES/0075-tests-shell-Check-overhead-in-iptables-save-and-rest.patch new file mode 100644 index 0000000..8f44318 --- /dev/null +++ b/SOURCES/0075-tests-shell-Check-overhead-in-iptables-save-and-rest.patch @@ -0,0 +1,61 @@ +From 4d61a3ea7bc6cbef8d4ea021e4998137cfca1453 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Thu, 2 Jun 2022 13:44:45 +0200 +Subject: [PATCH] tests: shell: Check overhead in iptables-save and -restore + +Some repeated calls have been reduced recently, assert this in a test +evaluating strace output. + +Signed-off-by: Phil Sutter +(cherry picked from commit 0416ae5dea134b33e22c97e68b64010d679debe1) +--- + .../shell/testcases/ipt-save/0007-overhead_0 | 37 +++++++++++++++++++ + 1 file changed, 37 insertions(+) + create mode 100755 iptables/tests/shell/testcases/ipt-save/0007-overhead_0 + +diff --git a/iptables/tests/shell/testcases/ipt-save/0007-overhead_0 b/iptables/tests/shell/testcases/ipt-save/0007-overhead_0 +new file mode 100755 +index 0000000000000..b86d71f209471 +--- /dev/null ++++ b/iptables/tests/shell/testcases/ipt-save/0007-overhead_0 +@@ -0,0 +1,37 @@ ++#!/bin/bash ++ ++# Test recent performance improvements in iptables-save due to reduced ++# overhead. ++ ++strace --version >/dev/null || { echo "skip for missing strace"; exit 0; } ++ ++RULESET=$( ++ echo "*filter" ++ for ((i = 0; i < 100; i++)); do ++ echo ":mychain$i -" ++ echo "-A FORWARD -p tcp --dport 22 -j mychain$i" ++ done ++ echo "COMMIT" ++) ++ ++RESTORE_STRACE=$(strace $XT_MULTI iptables-restore <<< "$RULESET" 2>&1 >/dev/null) ++SAVE_STRACE=$(strace $XT_MULTI iptables-save 2>&1 >/dev/null) ++ ++do_grep() { # (name, threshold, pattern) ++ local cnt=$(grep -c "$3") ++ [[ $cnt -le $2 ]] && return 0 ++ echo "ERROR: Too many $3 lookups for $1: $cnt > $2" ++ exit 1 ++} ++ ++# iptables prefers hard-coded protocol names instead of looking them up first ++ ++do_grep "$XT_MULTI iptables-restore" 0 /etc/protocols <<< "$RESTORE_STRACE" ++do_grep "$XT_MULTI iptables-save" 0 /etc/protocols <<< "$SAVE_STRACE" ++ ++# iptables-nft-save pointlessly checked whether chain jumps are targets ++ ++do_grep "$XT_MULTI iptables-restore" 10 libxt_ <<< "$RESTORE_STRACE" ++do_grep "$XT_MULTI iptables-save" 10 libxt_ <<< "$SAVE_STRACE" ++ ++exit 0 +-- +2.34.1 + diff --git a/SOURCES/0076-libxtables-Fix-unsupported-extension-warning-corner-.patch b/SOURCES/0076-libxtables-Fix-unsupported-extension-warning-corner-.patch new file mode 100644 index 0000000..a70af46 --- /dev/null +++ b/SOURCES/0076-libxtables-Fix-unsupported-extension-warning-corner-.patch @@ -0,0 +1,91 @@ +From f70e667bbc14c1dbf96b8732704aea294e4dcaa7 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Thu, 30 Jun 2022 18:04:39 +0200 +Subject: [PATCH] libxtables: Fix unsupported extension warning corner case + +Some extensions are not supported in revision 0 by user space anymore, +for those the warning in xtables_compatible_revision() does not print as +no revision 0 is tried. + +To fix this, one has to track if none of the user space supported +revisions were accepted by the kernel. Therefore add respective logic to +xtables_find_{target,match}(). + +Note that this does not lead to duplicated warnings for unsupported +extensions that have a revision 0 because xtables_compatible_revision() +returns true for them to allow for extension's help output. + +For the record, these ip6tables extensions are affected: set/SET, +socket, tos/TOS, TPROXY and SNAT. In addition to that, TEE is affected +for both families. + +Fixes: 17534cb18ed0a ("Improve error messages for unsupported extensions") +Signed-off-by: Phil Sutter +(cherry picked from commit 552c4a2f9e5706fef5f7abb27d1492a78bbb2a37) +--- + libxtables/xtables.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/libxtables/xtables.c b/libxtables/xtables.c +index a5c8d7e2c17ef..89547fb3ab947 100644 +--- a/libxtables/xtables.c ++++ b/libxtables/xtables.c +@@ -702,6 +702,7 @@ xtables_find_match(const char *name, enum xtables_tryload tryload, + struct xtables_match *ptr; + const char *icmp6 = "icmp6"; + bool found = false; ++ bool seen = false; + + if (strlen(name) >= XT_EXTENSION_MAXNAMELEN) + xtables_error(PARAMETER_PROBLEM, +@@ -720,6 +721,7 @@ xtables_find_match(const char *name, enum xtables_tryload tryload, + if (extension_cmp(name, (*dptr)->name, (*dptr)->family)) { + ptr = *dptr; + *dptr = (*dptr)->next; ++ seen = true; + if (!found && + xtables_fully_register_pending_match(ptr, prev)) { + found = true; +@@ -733,6 +735,11 @@ xtables_find_match(const char *name, enum xtables_tryload tryload, + dptr = &((*dptr)->next); + } + ++ if (seen && !found) ++ fprintf(stderr, ++ "Warning: Extension %s is not supported, missing kernel module?\n", ++ name); ++ + for (ptr = xtables_matches; ptr; ptr = ptr->next) { + if (extension_cmp(name, ptr->name, ptr->family)) { + struct xtables_match *clone; +@@ -825,6 +832,7 @@ xtables_find_target(const char *name, enum xtables_tryload tryload) + struct xtables_target **dptr; + struct xtables_target *ptr; + bool found = false; ++ bool seen = false; + + /* Standard target? */ + if (strcmp(name, "") == 0 +@@ -843,6 +851,7 @@ xtables_find_target(const char *name, enum xtables_tryload tryload) + if (extension_cmp(name, (*dptr)->name, (*dptr)->family)) { + ptr = *dptr; + *dptr = (*dptr)->next; ++ seen = true; + if (!found && + xtables_fully_register_pending_target(ptr, prev)) { + found = true; +@@ -856,6 +865,11 @@ xtables_find_target(const char *name, enum xtables_tryload tryload) + dptr = &((*dptr)->next); + } + ++ if (seen && !found) ++ fprintf(stderr, ++ "Warning: Extension %s is not supported, missing kernel module?\n", ++ name); ++ + for (ptr = xtables_targets; ptr; ptr = ptr->next) { + if (extension_cmp(name, ptr->name, ptr->family)) { + struct xtables_target *clone; +-- +2.34.1 + diff --git a/SPECS/iptables.spec b/SPECS/iptables.spec index 85be0bd..975d753 100644 --- a/SPECS/iptables.spec +++ b/SPECS/iptables.spec @@ -17,7 +17,7 @@ Name: iptables Summary: Tools for managing Linux kernel packet filtering capabilities URL: http://www.netfilter.org/projects/iptables Version: 1.8.4 -Release: 22%{?dist} +Release: 23%{?dist} Source: %{url}/files/%{name}-%{version}.tar.bz2 Source1: iptables.init Source2: iptables-config @@ -96,6 +96,20 @@ Patch59: 0059-doc-ebtables-nft.8-Adjust-for-missing-atomic-options.patch Patch60: 0060-ebtables-Dump-atomic-waste.patch Patch61: 0061-extensions-hashlimit-Fix-tests-with-HZ-100.patch Patch62: 0062-extensions-hashlimit-Fix-tests-with-HZ-1000.patch +Patch63: 0063-nft-Simplify-immediate-parsing.patch +Patch64: 0064-nft-Speed-up-immediate-parsing.patch +Patch65: 0065-xshared-Prefer-xtables_chain_protos-lookup-over-getp.patch +Patch66: 0066-xshared-Merge-and-share-parse_chain.patch +Patch67: 0067-nft-Reject-standard-targets-as-chain-names-when-rest.patch +Patch68: 0068-libxtables-Implement-notargets-hash-table.patch +Patch69: 0069-libxtables-Boost-rule-target-checks-by-announcing-ch.patch +Patch70: 0070-Use-proto_to_name-from-xshared-in-more-places.patch +Patch71: 0071-libxtables-Register-only-the-highest-revision-extens.patch +Patch72: 0072-xshared-Fix-response-to-unprivileged-users.patch +Patch73: 0073-Improve-error-messages-for-unsupported-extensions.patch +Patch74: 0074-nft-Fix-EPERM-handling-for-extensions-without-rev-0.patch +Patch75: 0075-tests-shell-Check-overhead-in-iptables-save-and-rest.patch +Patch76: 0076-libxtables-Fix-unsupported-extension-warning-corner-.patch # pf.os: ISC license # iptables-apply: Artistic Licence 2.0 @@ -504,6 +518,22 @@ done %doc %{_mandir}/man8/ebtables*.8* %changelog +* Fri Jul 01 2022 Phil Sutter - 1.8.4-23 +- libxtables: Fix unsupported extension warning corner case +- tests: shell: Check overhead in iptables-save and -restore +- nft: Fix EPERM handling for extensions without rev 0 +- Improve error messages for unsupported extensions +- xshared: Fix response to unprivileged users +- libxtables: Register only the highest revision extension +- Use proto_to_name() from xshared in more places +- libxtables: Boost rule target checks by announcing chain names +- libxtables: Implement notargets hash table +- nft: Reject standard targets as chain names when restoring +- xshared: Merge and share parse_chain() +- xshared: Prefer xtables_chain_protos lookup over getprotoent +- nft: Speed up immediate parsing +- nft: Simplify immediate parsing + * Mon Nov 29 2021 Phil Sutter - 1.8.4-22 - extensions: hashlimit: Fix tests with HZ=1000