From 87db664458518f5e804669dd3f422bfc09baa1bb Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Mar 30 2021 15:35:25 +0000 Subject: import iptables-1.8.4-17.el8 --- diff --git a/SOURCES/0026-nft-Make-batch_add_chain-return-the-added-batch-obje.patch b/SOURCES/0026-nft-Make-batch_add_chain-return-the-added-batch-obje.patch new file mode 100644 index 0000000..4e077ad --- /dev/null +++ b/SOURCES/0026-nft-Make-batch_add_chain-return-the-added-batch-obje.patch @@ -0,0 +1,167 @@ +From d1b516014e4883f30ee2faf264dd89a6d7940e2c Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Sat, 3 Oct 2020 17:46:09 +0200 +Subject: [PATCH] nft: Make batch_add_chain() return the added batch object + +Do this so in a later patch the 'skip' field can be adjusted. + +While being at it, simplify a few callers and eliminate the need for a +'ret' variable. + +Signed-off-by: Phil Sutter +Reviewed-by: Florian Westphal +(cherry picked from commit 0d77e64e8d9b8a3984b01a4951524dc40f61f4b6) + +Conflicts: + iptables/nft.c +-> Upstream changed good/bad return codes of nft_chain_restore() + function. + +Signed-off-by: Phil Sutter +--- + iptables/nft.c | 35 +++++++++++++++++------------------ + 1 file changed, 17 insertions(+), 18 deletions(-) + +diff --git a/iptables/nft.c b/iptables/nft.c +index e95e99f1d8d71..0efd18d57320f 100644 +--- a/iptables/nft.c ++++ b/iptables/nft.c +@@ -398,10 +398,11 @@ batch_set_add(struct nft_handle *h, enum obj_update_type type, + return batch_add(h, type, s); + } + +-static int batch_chain_add(struct nft_handle *h, enum obj_update_type type, ++static struct obj_update * ++batch_chain_add(struct nft_handle *h, enum obj_update_type type, + struct nftnl_chain *c) + { +- return batch_add(h, type, c) ? 0 : -1; ++ return batch_add(h, type, c); + } + + static struct obj_update * +@@ -910,7 +911,6 @@ int nft_chain_set(struct nft_handle *h, const char *table, + const struct xt_counters *counters) + { + struct nftnl_chain *c = NULL; +- int ret; + + nft_fn = nft_chain_set; + +@@ -924,10 +924,11 @@ int nft_chain_set(struct nft_handle *h, const char *table, + if (c == NULL) + return 0; + +- ret = batch_chain_add(h, NFT_COMPAT_CHAIN_UPDATE, c); ++ if (!batch_chain_add(h, NFT_COMPAT_CHAIN_UPDATE, c)) ++ return 0; + + /* the core expects 1 for success and 0 for error */ +- return ret == 0 ? 1 : 0; ++ return 1; + } + + static int __add_match(struct nftnl_expr *e, struct xt_entry_match *m) +@@ -1734,7 +1735,6 @@ int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *tabl + { + struct nftnl_chain_list *list; + struct nftnl_chain *c; +- int ret; + + nft_fn = nft_chain_user_add; + +@@ -1754,14 +1754,15 @@ int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *tabl + if (h->family == NFPROTO_BRIDGE) + nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, NF_ACCEPT); + +- ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c); ++ if (!batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c)) ++ return 0; + + list = nft_chain_list_get(h, table, chain); + if (list) + nftnl_chain_list_add(c, list); + + /* the core expects 1 for success and 0 for error */ +- return ret == 0 ? 1 : 0; ++ return 1; + } + + int nft_chain_restore(struct nft_handle *h, const char *chain, const char *table) +@@ -1769,7 +1770,6 @@ int nft_chain_restore(struct nft_handle *h, const char *chain, const char *table + struct nftnl_chain_list *list; + struct nftnl_chain *c; + bool created = false; +- int ret; + + c = nft_chain_find(h, table, chain); + if (c) { +@@ -1794,13 +1794,14 @@ int nft_chain_restore(struct nft_handle *h, const char *chain, const char *table + if (!created) + return 0; + +- ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c); ++ if (!batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c)) ++ return -1; + + list = nft_chain_list_get(h, table, chain); + if (list) + nftnl_chain_list_add(c, list); + +- return ret; ++ return 0; + } + + /* From linux/netlink.h */ +@@ -1818,7 +1819,6 @@ static int __nft_chain_user_del(struct nftnl_chain *c, void *data) + { + struct chain_user_del_data *d = data; + struct nft_handle *h = d->handle; +- int ret; + + /* don't delete built-in chain */ + if (nft_chain_builtin(c)) +@@ -1834,8 +1834,7 @@ static int __nft_chain_user_del(struct nftnl_chain *c, void *data) + + /* XXX This triggers a fast lookup from the kernel. */ + nftnl_chain_unset(c, NFTNL_CHAIN_HANDLE); +- ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_DEL, c); +- if (ret) ++ if (!batch_chain_add(h, NFT_COMPAT_CHAIN_USER_DEL, c)) + return -1; + + nftnl_chain_list_del(c); +@@ -1910,7 +1909,6 @@ int nft_chain_user_rename(struct nft_handle *h,const char *chain, + { + struct nftnl_chain *c; + uint64_t handle; +- int ret; + + nft_fn = nft_chain_user_rename; + +@@ -1941,10 +1939,11 @@ int nft_chain_user_rename(struct nft_handle *h,const char *chain, + nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, newname); + nftnl_chain_set_u64(c, NFTNL_CHAIN_HANDLE, handle); + +- ret = batch_chain_add(h, NFT_COMPAT_CHAIN_RENAME, c); ++ if (!batch_chain_add(h, NFT_COMPAT_CHAIN_RENAME, c)) ++ return 0; + + /* the core expects 1 for success and 0 for error */ +- return ret == 0 ? 1 : 0; ++ return 1; + } + + bool nft_table_find(struct nft_handle *h, const char *tablename) +@@ -3217,7 +3216,7 @@ static int __nft_chain_zero_counters(struct nftnl_chain *c, void *data) + nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS, 0); + nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES, 0); + nftnl_chain_unset(c, NFTNL_CHAIN_HANDLE); +- if (batch_chain_add(h, NFT_COMPAT_CHAIN_ZERO, c)) ++ if (!batch_chain_add(h, NFT_COMPAT_CHAIN_ZERO, c)) + return -1; + } + +-- +2.28.0 + diff --git a/SOURCES/0027-nft-Fix-error-reporting-for-refreshed-transactions.patch b/SOURCES/0027-nft-Fix-error-reporting-for-refreshed-transactions.patch new file mode 100644 index 0000000..c0a8f40 --- /dev/null +++ b/SOURCES/0027-nft-Fix-error-reporting-for-refreshed-transactions.patch @@ -0,0 +1,42 @@ +From 2dff9a669400644ec1e66d394b03d743eec2cd55 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Mon, 5 Oct 2020 15:54:35 +0200 +Subject: [PATCH] nft: Fix error reporting for refreshed transactions + +When preparing a batch from the list of batch objects in nft_action(), +the sequence number used for each object is stored within that object +for later matching against returned error messages. Though if the +transaction has to be refreshed, some of those objects may be skipped, +other objects take over their sequence number and errors are matched to +skipped objects. Avoid this by resetting the skipped object's sequence +number to zero. + +Fixes: 58d7de0181f61 ("xtables: handle concurrent ruleset modifications") +Signed-off-by: Phil Sutter +Reviewed-by: Florian Westphal +(cherry picked from commit e98b825a037807bf6c918eb66ee9682cc4c46183) +Signed-off-by: Phil Sutter +--- + iptables/nft.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/iptables/nft.c b/iptables/nft.c +index 0efd18d57320f..d661ac2cafda6 100644 +--- a/iptables/nft.c ++++ b/iptables/nft.c +@@ -2767,9 +2767,10 @@ retry: + h->nft_genid++; + + list_for_each_entry(n, &h->obj_list, head) { +- +- if (n->skip) ++ if (n->skip) { ++ n->seq = 0; + continue; ++ } + + n->seq = seq++; + switch (n->type) { +-- +2.28.0 + diff --git a/SOURCES/0028-nft-Fix-for-concurrent-noflush-restore-calls.patch b/SOURCES/0028-nft-Fix-for-concurrent-noflush-restore-calls.patch new file mode 100644 index 0000000..98a7ebb --- /dev/null +++ b/SOURCES/0028-nft-Fix-for-concurrent-noflush-restore-calls.patch @@ -0,0 +1,242 @@ +From 575a1e5589f813af7e838c045863b510b4740353 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Mon, 5 Oct 2020 16:06:49 +0200 +Subject: [PATCH] nft: Fix for concurrent noflush restore calls + +Transaction refresh was broken with regards to nft_chain_restore(): It +created a rule flush batch object only if the chain was found in cache +and a chain add object only if the chain was not found. Yet with +concurrent ruleset updates, one has to expect both situations: + +* If a chain vanishes, the rule flush job must be skipped and instead + the chain add job become active. + +* If a chain appears, the chain add job must be skipped and instead + rules flushed. + +Change the code accordingly: Create both batch objects and set their +'skip' field depending on the situation in cache and adjust both in +nft_refresh_transaction(). + +As a side-effect, the implicit rule flush becomes explicit and all +handling of implicit batch jobs is dropped along with the related field +indicating such. + +Reuse the 'implicit' parameter of __nft_rule_flush() to control the +initial 'skip' field value instead. + +A subtle caveat is vanishing of existing chains: Creating the chain add +job based on the chain in cache causes a netlink message containing that +chain's handle which the kernel dislikes. Therefore unset the chain's +handle in that case. + +Fixes: 58d7de0181f61 ("xtables: handle concurrent ruleset modifications") +Signed-off-by: Phil Sutter +(cherry picked from commit dac904bdcd9a18aabafee7275ccf0c2bd53800f3) + +Conflicts: + iptables/nft.c +-> Upstream changed good/bad return codes of nft_chain_restore() + function. + +Signed-off-by: Phil Sutter +--- + iptables/nft.c | 58 ++++++++++--------- + .../ipt-restore/0016-concurrent-restores_0 | 53 +++++++++++++++++ + 2 files changed, 83 insertions(+), 28 deletions(-) + create mode 100755 iptables/tests/shell/testcases/ipt-restore/0016-concurrent-restores_0 + +diff --git a/iptables/nft.c b/iptables/nft.c +index d661ac2cafda6..dc5490c085364 100644 +--- a/iptables/nft.c ++++ b/iptables/nft.c +@@ -283,7 +283,6 @@ struct obj_update { + struct list_head head; + enum obj_update_type type:8; + uint8_t skip:1; +- uint8_t implicit:1; + unsigned int seq; + union { + struct nftnl_table *table; +@@ -1650,7 +1649,7 @@ int nft_rule_save(struct nft_handle *h, const char *table, unsigned int format) + + static void + __nft_rule_flush(struct nft_handle *h, const char *table, +- const char *chain, bool verbose, bool implicit) ++ const char *chain, bool verbose, bool skip) + { + struct obj_update *obj; + struct nftnl_rule *r; +@@ -1672,7 +1671,7 @@ __nft_rule_flush(struct nft_handle *h, const char *table, + return; + } + +- obj->implicit = implicit; ++ obj->skip = skip; + } + + int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table, +@@ -1768,17 +1767,12 @@ int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *tabl + int nft_chain_restore(struct nft_handle *h, const char *chain, const char *table) + { + struct nftnl_chain_list *list; ++ struct obj_update *obj; + struct nftnl_chain *c; + bool created = false; + + c = nft_chain_find(h, table, chain); +- if (c) { +- /* Apparently -n still flushes existing user defined +- * chains that are redefined. +- */ +- if (h->noflush) +- __nft_rule_flush(h, table, chain, false, true); +- } else { ++ if (!c) { + c = nftnl_chain_alloc(); + if (!c) + return -1; +@@ -1786,20 +1780,26 @@ int nft_chain_restore(struct nft_handle *h, const char *chain, const char *table + nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, table); + nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, chain); + created = true; +- } + +- if (h->family == NFPROTO_BRIDGE) +- nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, NF_ACCEPT); ++ list = nft_chain_list_get(h, table, chain); ++ if (list) ++ nftnl_chain_list_add(c, list); ++ } else { ++ /* If the chain should vanish meanwhile, kernel genid changes ++ * and the transaction is refreshed enabling the chain add ++ * object. With the handle still set, kernel interprets it as a ++ * chain replace job and errors since it is not found anymore. ++ */ ++ nftnl_chain_unset(c, NFTNL_CHAIN_HANDLE); ++ } + +- if (!created) +- return 0; ++ __nft_rule_flush(h, table, chain, false, created); + +- if (!batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c)) ++ obj = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c); ++ if (!obj) + return -1; + +- list = nft_chain_list_get(h, table, chain); +- if (list) +- nftnl_chain_list_add(c, list); ++ obj->skip = !created; + + return 0; + } +@@ -2693,11 +2693,6 @@ static void nft_refresh_transaction(struct nft_handle *h) + h->error.lineno = 0; + + list_for_each_entry_safe(n, tmp, &h->obj_list, head) { +- if (n->implicit) { +- batch_obj_del(h, n); +- continue; +- } +- + switch (n->type) { + case NFT_COMPAT_TABLE_FLUSH: + tablename = nftnl_table_get_str(n->table, NFTNL_TABLE_NAME); +@@ -2723,14 +2718,22 @@ static void nft_refresh_transaction(struct nft_handle *h) + + c = nft_chain_find(h, tablename, chainname); + if (c) { +- /* -restore -n flushes existing rules from redefined user-chain */ +- __nft_rule_flush(h, tablename, +- chainname, false, true); + n->skip = 1; + } else if (!c) { + n->skip = 0; + } + break; ++ case NFT_COMPAT_RULE_FLUSH: ++ tablename = nftnl_rule_get_str(n->rule, NFTNL_RULE_TABLE); ++ if (!tablename) ++ continue; ++ ++ chainname = nftnl_rule_get_str(n->rule, NFTNL_RULE_CHAIN); ++ if (!chainname) ++ continue; ++ ++ n->skip = !nft_chain_find(h, tablename, chainname); ++ break; + case NFT_COMPAT_TABLE_ADD: + case NFT_COMPAT_CHAIN_ADD: + case NFT_COMPAT_CHAIN_ZERO: +@@ -2742,7 +2745,6 @@ static void nft_refresh_transaction(struct nft_handle *h) + case NFT_COMPAT_RULE_INSERT: + case NFT_COMPAT_RULE_REPLACE: + case NFT_COMPAT_RULE_DELETE: +- case NFT_COMPAT_RULE_FLUSH: + case NFT_COMPAT_SET_ADD: + break; + } +diff --git a/iptables/tests/shell/testcases/ipt-restore/0016-concurrent-restores_0 b/iptables/tests/shell/testcases/ipt-restore/0016-concurrent-restores_0 +new file mode 100755 +index 0000000000000..53ec12fa368af +--- /dev/null ++++ b/iptables/tests/shell/testcases/ipt-restore/0016-concurrent-restores_0 +@@ -0,0 +1,53 @@ ++#!/bin/bash ++ ++set -e ++ ++RS="*filter ++:INPUT ACCEPT [12024:3123388] ++:FORWARD ACCEPT [0:0] ++:OUTPUT ACCEPT [12840:2144421] ++:FOO - [0:0] ++:BAR0 - [0:0] ++:BAR1 - [0:0] ++:BAR2 - [0:0] ++:BAR3 - [0:0] ++:BAR4 - [0:0] ++:BAR5 - [0:0] ++:BAR6 - [0:0] ++:BAR7 - [0:0] ++:BAR8 - [0:0] ++:BAR9 - [0:0] ++" ++ ++RS1="$RS ++-X BAR3 ++-X BAR6 ++-X BAR9 ++-A FOO -s 9.9.0.1/32 -j BAR1 ++-A FOO -s 9.9.0.2/32 -j BAR2 ++-A FOO -s 9.9.0.4/32 -j BAR4 ++-A FOO -s 9.9.0.5/32 -j BAR5 ++-A FOO -s 9.9.0.7/32 -j BAR7 ++-A FOO -s 9.9.0.8/32 -j BAR8 ++COMMIT ++" ++ ++RS2="$RS ++-X BAR2 ++-X BAR5 ++-X BAR7 ++-A FOO -s 9.9.0.1/32 -j BAR1 ++-A FOO -s 9.9.0.3/32 -j BAR3 ++-A FOO -s 9.9.0.4/32 -j BAR4 ++-A FOO -s 9.9.0.6/32 -j BAR6 ++-A FOO -s 9.9.0.8/32 -j BAR8 ++-A FOO -s 9.9.0.9/32 -j BAR9 ++COMMIT ++" ++ ++for n in $(seq 1 10); do ++ $XT_MULTI iptables-restore --noflush -w <<< "$RS1" & ++ $XT_MULTI iptables-restore --noflush -w <<< "$RS2" & ++ wait -n ++ wait -n ++done +-- +2.28.0 + diff --git a/SOURCES/0029-tests-shell-Improve-concurrent-noflush-restore-test-.patch b/SOURCES/0029-tests-shell-Improve-concurrent-noflush-restore-test-.patch new file mode 100644 index 0000000..6d5d040 --- /dev/null +++ b/SOURCES/0029-tests-shell-Improve-concurrent-noflush-restore-test-.patch @@ -0,0 +1,55 @@ +From 674cce10a34e90f2791a3d58789793eef29e8f8b Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Mon, 26 Oct 2020 17:25:03 +0100 +Subject: [PATCH] tests: shell: Improve concurrent noflush restore test a bit + +The described issue happens only if chain FOO does not exist at program +start so flush the ruleset after each iteration to make sure this is the +case. Sadly the bug is still not 100% reproducible on my testing VM. + +While being at it, add a paragraph describing what exact situation the +test is trying to provoke. + +Fixes: dac904bdcd9a1 ("nft: Fix for concurrent noflush restore calls") +Signed-off-by: Phil Sutter +(cherry picked from commit ed8c8b9316451a4499eeb592d2cf7d782bbe4e9a) +Signed-off-by: Phil Sutter +--- + .../ipt-restore/0016-concurrent-restores_0 | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/iptables/tests/shell/testcases/ipt-restore/0016-concurrent-restores_0 b/iptables/tests/shell/testcases/ipt-restore/0016-concurrent-restores_0 +index 53ec12fa368af..aa746ab458a3c 100755 +--- a/iptables/tests/shell/testcases/ipt-restore/0016-concurrent-restores_0 ++++ b/iptables/tests/shell/testcases/ipt-restore/0016-concurrent-restores_0 +@@ -1,5 +1,14 @@ + #!/bin/bash + ++# test for iptables-restore --noflush skipping an explicitly requested chain ++# flush because the chain did not exist when cache was fetched. In order to ++# expect for that chain to appear when refreshing the transaction (due to a ++# concurrent ruleset change), the chain flush job has to be present in batch ++# job list (although disabled at first). ++# The input line requesting chain flush is ':FOO - [0:0]'. RS1 and RS2 contents ++# are crafted to cause EBUSY when deleting the BAR* chains if FOO is not ++# flushed in the same transaction. ++ + set -e + + RS="*filter +@@ -45,7 +54,12 @@ RS2="$RS + COMMIT + " + ++NORS="*filter ++COMMIT ++" ++ + for n in $(seq 1 10); do ++ $XT_MULTI iptables-restore <<< "$NORS" + $XT_MULTI iptables-restore --noflush -w <<< "$RS1" & + $XT_MULTI iptables-restore --noflush -w <<< "$RS2" & + wait -n +-- +2.28.0 + diff --git a/SOURCES/0030-nft-cache-Make-nft_rebuild_cache-respect-fake-cache.patch b/SOURCES/0030-nft-cache-Make-nft_rebuild_cache-respect-fake-cache.patch new file mode 100644 index 0000000..e802197 --- /dev/null +++ b/SOURCES/0030-nft-cache-Make-nft_rebuild_cache-respect-fake-cache.patch @@ -0,0 +1,80 @@ +From 2c183a2457d8640aaee3a98fc8fea70bf64d46f2 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Sat, 29 Feb 2020 02:08:26 +0100 +Subject: [PATCH] nft: cache: Make nft_rebuild_cache() respect fake cache + +If transaction needed a refresh in nft_action(), restore with flush +would fetch a full cache instead of merely refreshing table list +contained in "fake" cache. + +To fix this, nft_rebuild_cache() must distinguish between fake cache and +full rule cache. Therefore introduce NFT_CL_FAKE to be distinguished +from NFT_CL_RULES. + +Signed-off-by: Phil Sutter +(cherry picked from commit 40ad7793d1884f28767cf58c96e9d76ae0a18db1) + +RHEL-only fix: Make nft_rebuild_cache() check 'level' instead of +'h->cache_level' as the latter may be reset by __nft_flush_cache(). + +Signed-off-by: Phil Sutter +--- + iptables/nft-cache.c | 13 +++++++++---- + iptables/nft.h | 3 ++- + 2 files changed, 11 insertions(+), 5 deletions(-) + +diff --git a/iptables/nft-cache.c b/iptables/nft-cache.c +index bc6e7f7eaebfb..9623b463f0dd5 100644 +--- a/iptables/nft-cache.c ++++ b/iptables/nft-cache.c +@@ -480,6 +480,7 @@ __nft_build_cache(struct nft_handle *h, enum nft_cache_level level, + break; + /* fall through */ + case NFT_CL_RULES: ++ case NFT_CL_FAKE: + break; + } + +@@ -516,7 +517,7 @@ void nft_fake_cache(struct nft_handle *h) + + h->cache->table[type].chains = nftnl_chain_list_alloc(); + } +- h->cache_level = NFT_CL_RULES; ++ h->cache_level = NFT_CL_FAKE; + mnl_genid_get(h, &h->nft_genid); + } + +@@ -629,9 +630,13 @@ void nft_rebuild_cache(struct nft_handle *h) + if (h->cache_level) + __nft_flush_cache(h); + +- h->nft_genid = 0; +- h->cache_level = NFT_CL_NONE; +- __nft_build_cache(h, level, NULL, NULL, NULL); ++ if (level == NFT_CL_FAKE) { ++ nft_fake_cache(h); ++ } else { ++ h->nft_genid = 0; ++ h->cache_level = NFT_CL_NONE; ++ __nft_build_cache(h, level, NULL, NULL, NULL); ++ } + } + + void nft_release_cache(struct nft_handle *h) +diff --git a/iptables/nft.h b/iptables/nft.h +index 5cf260a6d2cd3..2094b01455194 100644 +--- a/iptables/nft.h ++++ b/iptables/nft.h +@@ -32,7 +32,8 @@ enum nft_cache_level { + NFT_CL_TABLES, + NFT_CL_CHAINS, + NFT_CL_SETS, +- NFT_CL_RULES ++ NFT_CL_RULES, ++ NFT_CL_FAKE /* must be last entry */ + }; + + struct nft_cache { +-- +2.28.0 + diff --git a/SOURCES/0031-nft-Fix-for-broken-address-mask-match-detection.patch b/SOURCES/0031-nft-Fix-for-broken-address-mask-match-detection.patch new file mode 100644 index 0000000..3f69e13 --- /dev/null +++ b/SOURCES/0031-nft-Fix-for-broken-address-mask-match-detection.patch @@ -0,0 +1,60 @@ +From 74a62264d4615ae7f76454e7ca406c46a62c7999 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Mon, 28 Sep 2020 18:57:18 +0200 +Subject: [PATCH] nft: Fix for broken address mask match detection + +Trying to decide whether a bitwise expression is needed to match parts +of a source or destination address only, add_addr() checks if all bytes +in 'mask' are 0xff or not. The check is apparently broken though as each +byte in 'mask' is cast to a signed char before comparing against 0xff, +therefore the bitwise is always added: + +| # ./bad/iptables-nft -A foo -s 10.0.0.1 -j ACCEPT +| # ./good/iptables-nft -A foo -s 10.0.0.2 -j ACCEPT +| # nft --debug=netlink list chain ip filter foo +| ip filter foo 5 +| [ payload load 4b @ network header + 12 => reg 1 ] +| [ bitwise reg 1 = (reg=1 & 0xffffffff ) ^ 0x00000000 ] +| [ cmp eq reg 1 0x0100000a ] +| [ counter pkts 0 bytes 0 ] +| [ immediate reg 0 accept ] +| +| ip filter foo 6 5 +| [ payload load 4b @ network header + 12 => reg 1 ] +| [ cmp eq reg 1 0x0200000a ] +| [ counter pkts 0 bytes 0 ] +| [ immediate reg 0 accept ] +| +| table ip filter { +| chain foo { +| ip saddr 10.0.0.1 counter packets 0 bytes 0 accept +| ip saddr 10.0.0.2 counter packets 0 bytes 0 accept +| } +| } + +Fix the cast, safe an extra op and gain 100% performance in ideal cases. + +Fixes: 56859380eb328 ("xtables-compat: avoid unneeded bitwise ops") +Signed-off-by: Phil Sutter +(cherry picked from commit 72ed608bf1ea550ac13b5b880afc7ad3ffa0afd0) +Signed-off-by: Phil Sutter +--- + iptables/nft-shared.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c +index 78e422781723f..f60f5df97fb86 100644 +--- a/iptables/nft-shared.c ++++ b/iptables/nft-shared.c +@@ -165,7 +165,7 @@ void add_outiface(struct nftnl_rule *r, char *iface, uint32_t op) + void add_addr(struct nftnl_rule *r, int offset, + void *data, void *mask, size_t len, uint32_t op) + { +- const char *m = mask; ++ const unsigned char *m = mask; + int i; + + add_payload(r, offset, len, NFT_PAYLOAD_NETWORK_HEADER); +-- +2.28.0 + diff --git a/SOURCES/0032-nft-Optimize-class-based-IP-prefix-matches.patch b/SOURCES/0032-nft-Optimize-class-based-IP-prefix-matches.patch new file mode 100644 index 0000000..80f79a2 --- /dev/null +++ b/SOURCES/0032-nft-Optimize-class-based-IP-prefix-matches.patch @@ -0,0 +1,150 @@ +From 87a2128fcfd4c5b0847a8c611652ade8c54d8185 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Fri, 2 Oct 2020 09:44:38 +0200 +Subject: [PATCH] nft: Optimize class-based IP prefix matches + +Payload expression works on byte-boundaries, leverage this with suitable +prefix lengths. + +Signed-off-by: Phil Sutter +(cherry picked from commit 323259001d617ae359430a03ee3d3e7f107684e0) +Signed-off-by: Phil Sutter +--- + iptables/nft-arp.c | 11 ++++++++--- + iptables/nft-ipv4.c | 6 ++++-- + iptables/nft-ipv6.c | 6 ++++-- + iptables/nft-shared.c | 14 ++++++++++---- + iptables/nft-shared.h | 4 ++++ + 5 files changed, 30 insertions(+), 11 deletions(-) + +diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c +index d4a86610ec217..ac400e484a4fa 100644 +--- a/iptables/nft-arp.c ++++ b/iptables/nft-arp.c +@@ -303,7 +303,8 @@ static bool nft_arp_parse_devaddr(struct nft_xt_ctx *ctx, + memcpy(info->mask, ctx->bitwise.mask, ETH_ALEN); + ctx->flags &= ~NFT_XT_CTX_BITWISE; + } else { +- memset(info->mask, 0xff, ETH_ALEN); ++ memset(info->mask, 0xff, ++ min(ctx->payload.len, ETH_ALEN)); + } + + return inv; +@@ -360,7 +361,9 @@ static void nft_arp_parse_payload(struct nft_xt_ctx *ctx, + parse_mask_ipv4(ctx, &fw->arp.smsk); + ctx->flags &= ~NFT_XT_CTX_BITWISE; + } else { +- fw->arp.smsk.s_addr = 0xffffffff; ++ memset(&fw->arp.smsk, 0xff, ++ min(ctx->payload.len, ++ sizeof(struct in_addr))); + } + + if (inv) +@@ -380,7 +383,9 @@ static void nft_arp_parse_payload(struct nft_xt_ctx *ctx, + parse_mask_ipv4(ctx, &fw->arp.tmsk); + ctx->flags &= ~NFT_XT_CTX_BITWISE; + } else { +- fw->arp.tmsk.s_addr = 0xffffffff; ++ memset(&fw->arp.tmsk, 0xff, ++ min(ctx->payload.len, ++ sizeof(struct in_addr))); + } + + if (inv) +diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c +index 70634f8fad84d..c84af2df90da7 100644 +--- a/iptables/nft-ipv4.c ++++ b/iptables/nft-ipv4.c +@@ -199,7 +199,8 @@ static void nft_ipv4_parse_payload(struct nft_xt_ctx *ctx, + parse_mask_ipv4(ctx, &cs->fw.ip.smsk); + ctx->flags &= ~NFT_XT_CTX_BITWISE; + } else { +- cs->fw.ip.smsk.s_addr = 0xffffffff; ++ memset(&cs->fw.ip.smsk, 0xff, ++ min(ctx->payload.len, sizeof(struct in_addr))); + } + + if (inv) +@@ -212,7 +213,8 @@ static void nft_ipv4_parse_payload(struct nft_xt_ctx *ctx, + parse_mask_ipv4(ctx, &cs->fw.ip.dmsk); + ctx->flags &= ~NFT_XT_CTX_BITWISE; + } else { +- cs->fw.ip.dmsk.s_addr = 0xffffffff; ++ memset(&cs->fw.ip.dmsk, 0xff, ++ min(ctx->payload.len, sizeof(struct in_addr))); + } + + if (inv) +diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c +index d01491bfdb689..cfced245a781c 100644 +--- a/iptables/nft-ipv6.c ++++ b/iptables/nft-ipv6.c +@@ -146,7 +146,8 @@ static void nft_ipv6_parse_payload(struct nft_xt_ctx *ctx, + parse_mask_ipv6(ctx, &cs->fw6.ipv6.smsk); + ctx->flags &= ~NFT_XT_CTX_BITWISE; + } else { +- memset(&cs->fw6.ipv6.smsk, 0xff, sizeof(struct in6_addr)); ++ memset(&cs->fw6.ipv6.smsk, 0xff, ++ min(ctx->payload.len, sizeof(struct in6_addr))); + } + + if (inv) +@@ -159,7 +160,8 @@ static void nft_ipv6_parse_payload(struct nft_xt_ctx *ctx, + parse_mask_ipv6(ctx, &cs->fw6.ipv6.dmsk); + ctx->flags &= ~NFT_XT_CTX_BITWISE; + } else { +- memset(&cs->fw6.ipv6.dmsk, 0xff, sizeof(struct in6_addr)); ++ memset(&cs->fw6.ipv6.dmsk, 0xff, ++ min(ctx->payload.len, sizeof(struct in6_addr))); + } + + if (inv) +diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c +index f60f5df97fb86..b1237049d0a34 100644 +--- a/iptables/nft-shared.c ++++ b/iptables/nft-shared.c +@@ -166,16 +166,22 @@ void add_addr(struct nftnl_rule *r, int offset, + void *data, void *mask, size_t len, uint32_t op) + { + const unsigned char *m = mask; ++ bool bitwise = false; + int i; + +- add_payload(r, offset, len, NFT_PAYLOAD_NETWORK_HEADER); +- + for (i = 0; i < len; i++) { +- if (m[i] != 0xff) ++ if (m[i] != 0xff) { ++ bitwise = m[i] != 0; + break; ++ } + } + +- if (i != len) ++ if (!bitwise) ++ len = i; ++ ++ add_payload(r, offset, len, NFT_PAYLOAD_NETWORK_HEADER); ++ ++ if (bitwise) + add_bitwise(r, mask, len); + + add_cmp_ptr(r, op, data, len); +diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h +index bee99a7dd0c93..c7f1e366b75ee 100644 +--- a/iptables/nft-shared.h ++++ b/iptables/nft-shared.h +@@ -252,4 +252,8 @@ void xtables_restore_parse(struct nft_handle *h, + const struct nft_xt_restore_parse *p); + + void nft_check_xt_legacy(int family, bool is_ipt_save); ++ ++#define min(x, y) ((x) < (y) ? (x) : (y)) ++#define max(x, y) ((x) > (y) ? (x) : (y)) ++ + #endif +-- +2.28.0 + diff --git a/SOURCES/0033-ebtables-Optimize-masked-MAC-address-matches.patch b/SOURCES/0033-ebtables-Optimize-masked-MAC-address-matches.patch new file mode 100644 index 0000000..f70a4fa --- /dev/null +++ b/SOURCES/0033-ebtables-Optimize-masked-MAC-address-matches.patch @@ -0,0 +1,218 @@ +From 03a484b63c5a61678555bcaca68fa36dc81468c1 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Fri, 30 Oct 2020 14:08:33 +0100 +Subject: [PATCH] ebtables: Optimize masked MAC address matches + +Just like with class-based prefix matches in iptables-nft, optimize +masked MAC address matches if the mask is on a byte-boundary. + +To reuse the logic in add_addr(), extend it to accept the payload base +value via parameter. + +Signed-off-by: Phil Sutter +(cherry picked from commit 274cb05edc58d6fa982a34c84b2f4cf6acc3e335) +Signed-off-by: Phil Sutter +--- + iptables/nft-arp.c | 12 ++++++++---- + iptables/nft-bridge.c | 22 ++++++++++------------ + iptables/nft-ipv4.c | 6 ++++-- + iptables/nft-ipv6.c | 6 ++++-- + iptables/nft-shared.c | 5 ++--- + iptables/nft-shared.h | 3 ++- + 6 files changed, 30 insertions(+), 24 deletions(-) + +diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c +index ac400e484a4fa..776b55949472b 100644 +--- a/iptables/nft-arp.c ++++ b/iptables/nft-arp.c +@@ -178,7 +178,8 @@ static int nft_arp_add(struct nft_handle *h, struct nftnl_rule *r, void *data) + + if (need_devaddr(&fw->arp.src_devaddr)) { + op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_SRCDEVADDR); +- add_addr(r, sizeof(struct arphdr), ++ add_addr(r, NFT_PAYLOAD_NETWORK_HEADER, ++ sizeof(struct arphdr), + &fw->arp.src_devaddr.addr, + &fw->arp.src_devaddr.mask, + fw->arp.arhln, op); +@@ -189,7 +190,8 @@ static int nft_arp_add(struct nft_handle *h, struct nftnl_rule *r, void *data) + fw->arp.smsk.s_addr != 0 || + fw->arp.invflags & ARPT_INV_SRCIP) { + op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_SRCIP); +- add_addr(r, sizeof(struct arphdr) + fw->arp.arhln, ++ add_addr(r, NFT_PAYLOAD_NETWORK_HEADER, ++ sizeof(struct arphdr) + fw->arp.arhln, + &fw->arp.src.s_addr, &fw->arp.smsk.s_addr, + sizeof(struct in_addr), op); + } +@@ -197,7 +199,8 @@ static int nft_arp_add(struct nft_handle *h, struct nftnl_rule *r, void *data) + + if (need_devaddr(&fw->arp.tgt_devaddr)) { + op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_TGTDEVADDR); +- add_addr(r, sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr), ++ add_addr(r, NFT_PAYLOAD_NETWORK_HEADER, ++ sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr), + &fw->arp.tgt_devaddr.addr, + &fw->arp.tgt_devaddr.mask, + fw->arp.arhln, op); +@@ -207,7 +210,8 @@ static int nft_arp_add(struct nft_handle *h, struct nftnl_rule *r, void *data) + fw->arp.tmsk.s_addr != 0 || + fw->arp.invflags & ARPT_INV_TGTIP) { + op = nft_invflags2cmp(fw->arp.invflags, ARPT_INV_TGTIP); +- add_addr(r, sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr) + fw->arp.arhln, ++ add_addr(r, NFT_PAYLOAD_NETWORK_HEADER, ++ sizeof(struct arphdr) + fw->arp.arhln + sizeof(struct in_addr) + fw->arp.arhln, + &fw->arp.tgt.s_addr, &fw->arp.tmsk.s_addr, + sizeof(struct in_addr), op); + } +diff --git a/iptables/nft-bridge.c b/iptables/nft-bridge.c +index 3f85cbbf5e4cf..2aa15e2d1e69d 100644 +--- a/iptables/nft-bridge.c ++++ b/iptables/nft-bridge.c +@@ -159,20 +159,16 @@ static int nft_bridge_add(struct nft_handle *h, + + if (fw->bitmask & EBT_ISOURCE) { + op = nft_invflags2cmp(fw->invflags, EBT_ISOURCE); +- add_payload(r, offsetof(struct ethhdr, h_source), 6, +- NFT_PAYLOAD_LL_HEADER); +- if (!mac_all_ones(fw->sourcemsk)) +- add_bitwise(r, fw->sourcemsk, 6); +- add_cmp_ptr(r, op, fw->sourcemac, 6); ++ add_addr(r, NFT_PAYLOAD_LL_HEADER, ++ offsetof(struct ethhdr, h_source), ++ fw->sourcemac, fw->sourcemsk, ETH_ALEN, op); + } + + if (fw->bitmask & EBT_IDEST) { + op = nft_invflags2cmp(fw->invflags, EBT_IDEST); +- add_payload(r, offsetof(struct ethhdr, h_dest), 6, +- NFT_PAYLOAD_LL_HEADER); +- if (!mac_all_ones(fw->destmsk)) +- add_bitwise(r, fw->destmsk, 6); +- add_cmp_ptr(r, op, fw->destmac, 6); ++ add_addr(r, NFT_PAYLOAD_LL_HEADER, ++ offsetof(struct ethhdr, h_dest), ++ fw->destmac, fw->destmsk, ETH_ALEN, op); + } + + if ((fw->bitmask & EBT_NOPROTO) == 0) { +@@ -258,7 +254,8 @@ static void nft_bridge_parse_payload(struct nft_xt_ctx *ctx, + memcpy(fw->destmsk, ctx->bitwise.mask, ETH_ALEN); + ctx->flags &= ~NFT_XT_CTX_BITWISE; + } else { +- memset(&fw->destmsk, 0xff, ETH_ALEN); ++ memset(&fw->destmsk, 0xff, ++ min(ctx->payload.len, ETH_ALEN)); + } + fw->bitmask |= EBT_IDEST; + break; +@@ -272,7 +269,8 @@ static void nft_bridge_parse_payload(struct nft_xt_ctx *ctx, + memcpy(fw->sourcemsk, ctx->bitwise.mask, ETH_ALEN); + ctx->flags &= ~NFT_XT_CTX_BITWISE; + } else { +- memset(&fw->sourcemsk, 0xff, ETH_ALEN); ++ memset(&fw->sourcemsk, 0xff, ++ min(ctx->payload.len, ETH_ALEN)); + } + fw->bitmask |= EBT_ISOURCE; + break; +diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c +index c84af2df90da7..5bd0710781533 100644 +--- a/iptables/nft-ipv4.c ++++ b/iptables/nft-ipv4.c +@@ -50,13 +50,15 @@ static int nft_ipv4_add(struct nft_handle *h, struct nftnl_rule *r, void *data) + + if (cs->fw.ip.src.s_addr || cs->fw.ip.smsk.s_addr || cs->fw.ip.invflags & IPT_INV_SRCIP) { + op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_SRCIP); +- add_addr(r, offsetof(struct iphdr, saddr), ++ add_addr(r, NFT_PAYLOAD_NETWORK_HEADER, ++ offsetof(struct iphdr, saddr), + &cs->fw.ip.src.s_addr, &cs->fw.ip.smsk.s_addr, + sizeof(struct in_addr), op); + } + if (cs->fw.ip.dst.s_addr || cs->fw.ip.dmsk.s_addr || cs->fw.ip.invflags & IPT_INV_DSTIP) { + op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_DSTIP); +- add_addr(r, offsetof(struct iphdr, daddr), ++ add_addr(r, NFT_PAYLOAD_NETWORK_HEADER, ++ offsetof(struct iphdr, daddr), + &cs->fw.ip.dst.s_addr, &cs->fw.ip.dmsk.s_addr, + sizeof(struct in_addr), op); + } +diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c +index cfced245a781c..6ece631d85f59 100644 +--- a/iptables/nft-ipv6.c ++++ b/iptables/nft-ipv6.c +@@ -51,7 +51,8 @@ static int nft_ipv6_add(struct nft_handle *h, struct nftnl_rule *r, void *data) + !IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.smsk) || + (cs->fw6.ipv6.invflags & IPT_INV_SRCIP)) { + op = nft_invflags2cmp(cs->fw6.ipv6.invflags, IPT_INV_SRCIP); +- add_addr(r, offsetof(struct ip6_hdr, ip6_src), ++ add_addr(r, NFT_PAYLOAD_NETWORK_HEADER, ++ offsetof(struct ip6_hdr, ip6_src), + &cs->fw6.ipv6.src, &cs->fw6.ipv6.smsk, + sizeof(struct in6_addr), op); + } +@@ -59,7 +60,8 @@ static int nft_ipv6_add(struct nft_handle *h, struct nftnl_rule *r, void *data) + !IN6_IS_ADDR_UNSPECIFIED(&cs->fw6.ipv6.dmsk) || + (cs->fw6.ipv6.invflags & IPT_INV_DSTIP)) { + op = nft_invflags2cmp(cs->fw6.ipv6.invflags, IPT_INV_DSTIP); +- add_addr(r, offsetof(struct ip6_hdr, ip6_dst), ++ add_addr(r, NFT_PAYLOAD_NETWORK_HEADER, ++ offsetof(struct ip6_hdr, ip6_dst), + &cs->fw6.ipv6.dst, &cs->fw6.ipv6.dmsk, + sizeof(struct in6_addr), op); + } +diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c +index b1237049d0a34..2aae0a3a49dfe 100644 +--- a/iptables/nft-shared.c ++++ b/iptables/nft-shared.c +@@ -20,7 +20,6 @@ + + #include + +-#include + #include + #include + +@@ -162,7 +161,7 @@ void add_outiface(struct nftnl_rule *r, char *iface, uint32_t op) + add_cmp_ptr(r, op, iface, iface_len + 1); + } + +-void add_addr(struct nftnl_rule *r, int offset, ++void add_addr(struct nftnl_rule *r, enum nft_payload_bases base, int offset, + void *data, void *mask, size_t len, uint32_t op) + { + const unsigned char *m = mask; +@@ -179,7 +178,7 @@ void add_addr(struct nftnl_rule *r, int offset, + if (!bitwise) + len = i; + +- add_payload(r, offset, len, NFT_PAYLOAD_NETWORK_HEADER); ++ add_payload(r, offset, len, base); + + if (bitwise) + add_bitwise(r, mask, len); +diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h +index c7f1e366b75ee..520a296fb530c 100644 +--- a/iptables/nft-shared.h ++++ b/iptables/nft-shared.h +@@ -8,6 +8,7 @@ + #include + + #include ++#include + + #include "xshared.h" + +@@ -124,7 +125,7 @@ void add_cmp_u16(struct nftnl_rule *r, uint16_t val, uint32_t op); + void add_cmp_u32(struct nftnl_rule *r, uint32_t val, uint32_t op); + void add_iniface(struct nftnl_rule *r, char *iface, uint32_t op); + void add_outiface(struct nftnl_rule *r, char *iface, uint32_t op); +-void add_addr(struct nftnl_rule *r, int offset, ++void add_addr(struct nftnl_rule *r, enum nft_payload_bases base, int offset, + void *data, void *mask, size_t len, uint32_t op); + void add_proto(struct nftnl_rule *r, int offset, size_t len, + uint8_t proto, uint32_t op); +-- +2.28.0 + diff --git a/SOURCES/0034-tests-shell-Add-test-for-bitwise-avoidance-fixes.patch b/SOURCES/0034-tests-shell-Add-test-for-bitwise-avoidance-fixes.patch new file mode 100644 index 0000000..281a9d4 --- /dev/null +++ b/SOURCES/0034-tests-shell-Add-test-for-bitwise-avoidance-fixes.patch @@ -0,0 +1,366 @@ +From 6aef90100bebe2b00d4edffe59fb9c43643816de Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 10 Nov 2020 14:50:46 +0100 +Subject: [PATCH] tests/shell: Add test for bitwise avoidance fixes + +Masked address matching was recently improved to avoid bitwise +expression if the given mask covers full bytes. Make use of nft netlink +debug output to assert iptables-nft generates the right bytecode for +each situation. + +Signed-off-by: Phil Sutter +(cherry picked from commit 81a2e128512837b53e5b9ea501b6c8dc64eeca78) +Signed-off-by: Phil Sutter +--- + .../nft-only/0009-needless-bitwise_0 | 339 ++++++++++++++++++ + 1 file changed, 339 insertions(+) + create mode 100755 iptables/tests/shell/testcases/nft-only/0009-needless-bitwise_0 + +diff --git a/iptables/tests/shell/testcases/nft-only/0009-needless-bitwise_0 b/iptables/tests/shell/testcases/nft-only/0009-needless-bitwise_0 +new file mode 100755 +index 0000000000000..c5c6e706a1029 +--- /dev/null ++++ b/iptables/tests/shell/testcases/nft-only/0009-needless-bitwise_0 +@@ -0,0 +1,339 @@ ++#!/bin/bash -x ++ ++[[ $XT_MULTI == *xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; } ++set -e ++ ++nft flush ruleset ++ ++( ++ echo "*filter" ++ for plen in "" 32 30 24 16 8 0; do ++ addr="10.1.2.3${plen:+/}$plen" ++ echo "-A OUTPUT -d $addr" ++ done ++ echo "COMMIT" ++) | $XT_MULTI iptables-restore ++ ++( ++ echo "*filter" ++ for plen in "" 128 124 120 112 88 80 64 48 16 8 0; do ++ addr="feed:c0ff:ee00:0102:0304:0506:0708:090A${plen:+/}$plen" ++ echo "-A OUTPUT -d $addr" ++ done ++ echo "COMMIT" ++) | $XT_MULTI ip6tables-restore ++ ++masks=" ++ff:ff:ff:ff:ff:ff ++ff:ff:ff:ff:ff:f0 ++ff:ff:ff:ff:ff:00 ++ff:ff:ff:ff:00:00 ++ff:ff:ff:00:00:00 ++ff:ff:00:00:00:00 ++ff:00:00:00:00:00 ++" ++( ++ echo "*filter" ++ for plen in "" 32 30 24 16 8 0; do ++ addr="10.1.2.3${plen:+/}$plen" ++ echo "-A OUTPUT -d $addr" ++ done ++ for mask in $masks; do ++ echo "-A OUTPUT --destination-mac fe:ed:00:c0:ff:ee/$mask" ++ done ++ echo "COMMIT" ++) | $XT_MULTI arptables-restore ++ ++( ++ echo "*filter" ++ for mask in $masks; do ++ echo "-A OUTPUT -d fe:ed:00:c0:ff:ee/$mask" ++ done ++ echo "COMMIT" ++) | $XT_MULTI ebtables-restore ++ ++EXPECT="ip filter OUTPUT 4 ++ [ payload load 4b @ network header + 16 => reg 1 ] ++ [ cmp eq reg 1 0x0302010a ] ++ [ counter pkts 0 bytes 0 ] ++ ++ip filter OUTPUT 5 4 ++ [ payload load 4b @ network header + 16 => reg 1 ] ++ [ cmp eq reg 1 0x0302010a ] ++ [ counter pkts 0 bytes 0 ] ++ ++ip filter OUTPUT 6 5 ++ [ payload load 4b @ network header + 16 => reg 1 ] ++ [ bitwise reg 1 = (reg=1 & 0xfcffffff ) ^ 0x00000000 ] ++ [ cmp eq reg 1 0x0002010a ] ++ [ counter pkts 0 bytes 0 ] ++ ++ip filter OUTPUT 7 6 ++ [ payload load 3b @ network header + 16 => reg 1 ] ++ [ cmp eq reg 1 0x0002010a ] ++ [ counter pkts 0 bytes 0 ] ++ ++ip filter OUTPUT 8 7 ++ [ payload load 2b @ network header + 16 => reg 1 ] ++ [ cmp eq reg 1 0x0000010a ] ++ [ counter pkts 0 bytes 0 ] ++ ++ip filter OUTPUT 9 8 ++ [ payload load 1b @ network header + 16 => reg 1 ] ++ [ cmp eq reg 1 0x0000000a ] ++ [ counter pkts 0 bytes 0 ] ++ ++ip filter OUTPUT 10 9 ++ [ counter pkts 0 bytes 0 ] ++ ++ip6 filter OUTPUT 4 ++ [ payload load 16b @ network header + 24 => reg 1 ] ++ [ cmp eq reg 1 0xffc0edfe 0x020100ee 0x06050403 0x0a090807 ] ++ [ counter pkts 0 bytes 0 ] ++ ++ip6 filter OUTPUT 5 4 ++ [ payload load 16b @ network header + 24 => reg 1 ] ++ [ cmp eq reg 1 0xffc0edfe 0x020100ee 0x06050403 0x0a090807 ] ++ [ counter pkts 0 bytes 0 ] ++ ++ip6 filter OUTPUT 6 5 ++ [ payload load 16b @ network header + 24 => reg 1 ] ++ [ bitwise reg 1 = (reg=1 & 0xffffffff 0xffffffff 0xffffffff 0xf0ffffff ) ^ 0x00000000 0x00000000 0x00000000 0x00000000 ] ++ [ cmp eq reg 1 0xffc0edfe 0x020100ee 0x06050403 0x00090807 ] ++ [ counter pkts 0 bytes 0 ] ++ ++ip6 filter OUTPUT 7 6 ++ [ payload load 15b @ network header + 24 => reg 1 ] ++ [ cmp eq reg 1 0xffc0edfe 0x020100ee 0x06050403 0x00090807 ] ++ [ counter pkts 0 bytes 0 ] ++ ++ip6 filter OUTPUT 8 7 ++ [ payload load 14b @ network header + 24 => reg 1 ] ++ [ cmp eq reg 1 0xffc0edfe 0x020100ee 0x06050403 0x00000807 ] ++ [ counter pkts 0 bytes 0 ] ++ ++ip6 filter OUTPUT 9 8 ++ [ payload load 11b @ network header + 24 => reg 1 ] ++ [ cmp eq reg 1 0xffc0edfe 0x020100ee 0x00050403 ] ++ [ counter pkts 0 bytes 0 ] ++ ++ip6 filter OUTPUT 10 9 ++ [ payload load 10b @ network header + 24 => reg 1 ] ++ [ cmp eq reg 1 0xffc0edfe 0x020100ee 0x00000403 ] ++ [ counter pkts 0 bytes 0 ] ++ ++ip6 filter OUTPUT 11 10 ++ [ payload load 8b @ network header + 24 => reg 1 ] ++ [ cmp eq reg 1 0xffc0edfe 0x020100ee ] ++ [ counter pkts 0 bytes 0 ] ++ ++ip6 filter OUTPUT 12 11 ++ [ payload load 6b @ network header + 24 => reg 1 ] ++ [ cmp eq reg 1 0xffc0edfe 0x000000ee ] ++ [ counter pkts 0 bytes 0 ] ++ ++ip6 filter OUTPUT 13 12 ++ [ payload load 2b @ network header + 24 => reg 1 ] ++ [ cmp eq reg 1 0x0000edfe ] ++ [ counter pkts 0 bytes 0 ] ++ ++ip6 filter OUTPUT 14 13 ++ [ payload load 1b @ network header + 24 => reg 1 ] ++ [ cmp eq reg 1 0x000000fe ] ++ [ counter pkts 0 bytes 0 ] ++ ++ip6 filter OUTPUT 15 14 ++ [ counter pkts 0 bytes 0 ] ++ ++arp filter OUTPUT 3 ++ [ payload load 2b @ network header + 0 => reg 1 ] ++ [ cmp eq reg 1 0x00000100 ] ++ [ payload load 1b @ network header + 4 => reg 1 ] ++ [ cmp eq reg 1 0x00000006 ] ++ [ payload load 1b @ network header + 5 => reg 1 ] ++ [ cmp eq reg 1 0x00000004 ] ++ [ payload load 4b @ network header + 24 => reg 1 ] ++ [ cmp eq reg 1 0x0302010a ] ++ [ counter pkts 0 bytes 0 ] ++ ++arp filter OUTPUT 4 3 ++ [ payload load 2b @ network header + 0 => reg 1 ] ++ [ cmp eq reg 1 0x00000100 ] ++ [ payload load 1b @ network header + 4 => reg 1 ] ++ [ cmp eq reg 1 0x00000006 ] ++ [ payload load 1b @ network header + 5 => reg 1 ] ++ [ cmp eq reg 1 0x00000004 ] ++ [ payload load 4b @ network header + 24 => reg 1 ] ++ [ cmp eq reg 1 0x0302010a ] ++ [ counter pkts 0 bytes 0 ] ++ ++arp filter OUTPUT 5 4 ++ [ payload load 2b @ network header + 0 => reg 1 ] ++ [ cmp eq reg 1 0x00000100 ] ++ [ payload load 1b @ network header + 4 => reg 1 ] ++ [ cmp eq reg 1 0x00000006 ] ++ [ payload load 1b @ network header + 5 => reg 1 ] ++ [ cmp eq reg 1 0x00000004 ] ++ [ payload load 4b @ network header + 24 => reg 1 ] ++ [ bitwise reg 1 = (reg=1 & 0xfcffffff ) ^ 0x00000000 ] ++ [ cmp eq reg 1 0x0002010a ] ++ [ counter pkts 0 bytes 0 ] ++ ++arp filter OUTPUT 6 5 ++ [ payload load 2b @ network header + 0 => reg 1 ] ++ [ cmp eq reg 1 0x00000100 ] ++ [ payload load 1b @ network header + 4 => reg 1 ] ++ [ cmp eq reg 1 0x00000006 ] ++ [ payload load 1b @ network header + 5 => reg 1 ] ++ [ cmp eq reg 1 0x00000004 ] ++ [ payload load 3b @ network header + 24 => reg 1 ] ++ [ cmp eq reg 1 0x0002010a ] ++ [ counter pkts 0 bytes 0 ] ++ ++arp filter OUTPUT 7 6 ++ [ payload load 2b @ network header + 0 => reg 1 ] ++ [ cmp eq reg 1 0x00000100 ] ++ [ payload load 1b @ network header + 4 => reg 1 ] ++ [ cmp eq reg 1 0x00000006 ] ++ [ payload load 1b @ network header + 5 => reg 1 ] ++ [ cmp eq reg 1 0x00000004 ] ++ [ payload load 2b @ network header + 24 => reg 1 ] ++ [ cmp eq reg 1 0x0000010a ] ++ [ counter pkts 0 bytes 0 ] ++ ++arp filter OUTPUT 8 7 ++ [ payload load 2b @ network header + 0 => reg 1 ] ++ [ cmp eq reg 1 0x00000100 ] ++ [ payload load 1b @ network header + 4 => reg 1 ] ++ [ cmp eq reg 1 0x00000006 ] ++ [ payload load 1b @ network header + 5 => reg 1 ] ++ [ cmp eq reg 1 0x00000004 ] ++ [ payload load 1b @ network header + 24 => reg 1 ] ++ [ cmp eq reg 1 0x0000000a ] ++ [ counter pkts 0 bytes 0 ] ++ ++arp filter OUTPUT 9 8 ++ [ payload load 2b @ network header + 0 => reg 1 ] ++ [ cmp eq reg 1 0x00000100 ] ++ [ payload load 1b @ network header + 4 => reg 1 ] ++ [ cmp eq reg 1 0x00000006 ] ++ [ payload load 1b @ network header + 5 => reg 1 ] ++ [ cmp eq reg 1 0x00000004 ] ++ [ counter pkts 0 bytes 0 ] ++ ++arp filter OUTPUT 10 9 ++ [ payload load 2b @ network header + 0 => reg 1 ] ++ [ cmp eq reg 1 0x00000100 ] ++ [ payload load 1b @ network header + 4 => reg 1 ] ++ [ cmp eq reg 1 0x00000006 ] ++ [ payload load 1b @ network header + 5 => reg 1 ] ++ [ cmp eq reg 1 0x00000004 ] ++ [ payload load 6b @ network header + 18 => reg 1 ] ++ [ cmp eq reg 1 0xc000edfe 0x0000eeff ] ++ [ counter pkts 0 bytes 0 ] ++ ++arp filter OUTPUT 11 10 ++ [ payload load 2b @ network header + 0 => reg 1 ] ++ [ cmp eq reg 1 0x00000100 ] ++ [ payload load 1b @ network header + 4 => reg 1 ] ++ [ cmp eq reg 1 0x00000006 ] ++ [ payload load 1b @ network header + 5 => reg 1 ] ++ [ cmp eq reg 1 0x00000004 ] ++ [ payload load 6b @ network header + 18 => reg 1 ] ++ [ bitwise reg 1 = (reg=1 & 0xffffffff 0x0000f0ff ) ^ 0x00000000 0x00000000 ] ++ [ cmp eq reg 1 0xc000edfe 0x0000e0ff ] ++ [ counter pkts 0 bytes 0 ] ++ ++arp filter OUTPUT 12 11 ++ [ payload load 2b @ network header + 0 => reg 1 ] ++ [ cmp eq reg 1 0x00000100 ] ++ [ payload load 1b @ network header + 4 => reg 1 ] ++ [ cmp eq reg 1 0x00000006 ] ++ [ payload load 1b @ network header + 5 => reg 1 ] ++ [ cmp eq reg 1 0x00000004 ] ++ [ payload load 5b @ network header + 18 => reg 1 ] ++ [ cmp eq reg 1 0xc000edfe 0x000000ff ] ++ [ counter pkts 0 bytes 0 ] ++ ++arp filter OUTPUT 13 12 ++ [ payload load 2b @ network header + 0 => reg 1 ] ++ [ cmp eq reg 1 0x00000100 ] ++ [ payload load 1b @ network header + 4 => reg 1 ] ++ [ cmp eq reg 1 0x00000006 ] ++ [ payload load 1b @ network header + 5 => reg 1 ] ++ [ cmp eq reg 1 0x00000004 ] ++ [ payload load 4b @ network header + 18 => reg 1 ] ++ [ cmp eq reg 1 0xc000edfe ] ++ [ counter pkts 0 bytes 0 ] ++ ++arp filter OUTPUT 14 13 ++ [ payload load 2b @ network header + 0 => reg 1 ] ++ [ cmp eq reg 1 0x00000100 ] ++ [ payload load 1b @ network header + 4 => reg 1 ] ++ [ cmp eq reg 1 0x00000006 ] ++ [ payload load 1b @ network header + 5 => reg 1 ] ++ [ cmp eq reg 1 0x00000004 ] ++ [ payload load 3b @ network header + 18 => reg 1 ] ++ [ cmp eq reg 1 0x0000edfe ] ++ [ counter pkts 0 bytes 0 ] ++ ++arp filter OUTPUT 15 14 ++ [ payload load 2b @ network header + 0 => reg 1 ] ++ [ cmp eq reg 1 0x00000100 ] ++ [ payload load 1b @ network header + 4 => reg 1 ] ++ [ cmp eq reg 1 0x00000006 ] ++ [ payload load 1b @ network header + 5 => reg 1 ] ++ [ cmp eq reg 1 0x00000004 ] ++ [ payload load 2b @ network header + 18 => reg 1 ] ++ [ cmp eq reg 1 0x0000edfe ] ++ [ counter pkts 0 bytes 0 ] ++ ++arp filter OUTPUT 16 15 ++ [ payload load 2b @ network header + 0 => reg 1 ] ++ [ cmp eq reg 1 0x00000100 ] ++ [ payload load 1b @ network header + 4 => reg 1 ] ++ [ cmp eq reg 1 0x00000006 ] ++ [ payload load 1b @ network header + 5 => reg 1 ] ++ [ cmp eq reg 1 0x00000004 ] ++ [ payload load 1b @ network header + 18 => reg 1 ] ++ [ cmp eq reg 1 0x000000fe ] ++ [ counter pkts 0 bytes 0 ] ++ ++bridge filter OUTPUT 4 ++ [ payload load 6b @ link header + 0 => reg 1 ] ++ [ cmp eq reg 1 0xc000edfe 0x0000eeff ] ++ [ counter pkts 0 bytes 0 ] ++ ++bridge filter OUTPUT 5 4 ++ [ payload load 6b @ link header + 0 => reg 1 ] ++ [ bitwise reg 1 = (reg=1 & 0xffffffff 0x0000f0ff ) ^ 0x00000000 0x00000000 ] ++ [ cmp eq reg 1 0xc000edfe 0x0000e0ff ] ++ [ counter pkts 0 bytes 0 ] ++ ++bridge filter OUTPUT 6 5 ++ [ payload load 5b @ link header + 0 => reg 1 ] ++ [ cmp eq reg 1 0xc000edfe 0x000000ff ] ++ [ counter pkts 0 bytes 0 ] ++ ++bridge filter OUTPUT 7 6 ++ [ payload load 4b @ link header + 0 => reg 1 ] ++ [ cmp eq reg 1 0xc000edfe ] ++ [ counter pkts 0 bytes 0 ] ++ ++bridge filter OUTPUT 8 7 ++ [ payload load 3b @ link header + 0 => reg 1 ] ++ [ cmp eq reg 1 0x0000edfe ] ++ [ counter pkts 0 bytes 0 ] ++ ++bridge filter OUTPUT 9 8 ++ [ payload load 2b @ link header + 0 => reg 1 ] ++ [ cmp eq reg 1 0x0000edfe ] ++ [ counter pkts 0 bytes 0 ] ++ ++bridge filter OUTPUT 10 9 ++ [ payload load 1b @ link header + 0 => reg 1 ] ++ [ cmp eq reg 1 0x000000fe ] ++ [ counter pkts 0 bytes 0 ] ++" ++ ++diff -u -Z <(echo "$EXPECT") <(nft --debug=netlink list ruleset | awk '/^table/{exit} {print}') +-- +2.28.0 + diff --git a/SOURCES/0035-libxtables-Make-sure-extensions-register-in-revision.patch b/SOURCES/0035-libxtables-Make-sure-extensions-register-in-revision.patch new file mode 100644 index 0000000..9ab85d2 --- /dev/null +++ b/SOURCES/0035-libxtables-Make-sure-extensions-register-in-revision.patch @@ -0,0 +1,128 @@ +From e6eede725bbd395fb8b385aec4d0a32ce99e842c Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Mon, 21 Sep 2020 13:42:06 +0200 +Subject: [PATCH] libxtables: Make sure extensions register in revision order + +Insert extensions into pending lists in ordered fashion: Group by +extension name (and, for matches, family) and order groups by descending +revision number. + +This allows to simplify the later full registration considerably. Since +that involves kernel compatibility checks, the extra cycles here pay off +eventually. + +Signed-off-by: Phil Sutter +(cherry picked from commit b3ac87038f4e45141831d9ab485a2f627daba3f1) +Signed-off-by: Phil Sutter +--- + libxtables/xtables.c | 71 +++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 64 insertions(+), 7 deletions(-) + +diff --git a/libxtables/xtables.c b/libxtables/xtables.c +index 777c2b08e9896..13139d7f8ad62 100644 +--- a/libxtables/xtables.c ++++ b/libxtables/xtables.c +@@ -902,8 +902,14 @@ static void xtables_check_options(const char *name, const struct option *opt) + } + } + ++static int xtables_match_prefer(const struct xtables_match *a, ++ const struct xtables_match *b); ++ + void xtables_register_match(struct xtables_match *me) + { ++ struct xtables_match **pos; ++ bool seen_myself = false; ++ + if (me->next) { + fprintf(stderr, "%s: match \"%s\" already registered\n", + xt_params->program_name, me->name); +@@ -955,10 +961,34 @@ void xtables_register_match(struct xtables_match *me) + if (me->extra_opts != NULL) + xtables_check_options(me->name, me->extra_opts); + +- +- /* place on linked list of matches pending full registration */ +- me->next = xtables_pending_matches; +- xtables_pending_matches = me; ++ /* order into linked list of matches pending full registration */ ++ for (pos = &xtables_pending_matches; *pos; pos = &(*pos)->next) { ++ /* group by name and family */ ++ if (strcmp(me->name, (*pos)->name) || ++ me->family != (*pos)->family) { ++ if (seen_myself) ++ break; /* end of own group, append to it */ ++ continue; ++ } ++ /* found own group */ ++ seen_myself = true; ++ if (xtables_match_prefer(me, *pos) >= 0) ++ break; /* put preferred items first in group */ ++ } ++ /* if own group was not found, prepend item */ ++ if (!*pos && !seen_myself) ++ pos = &xtables_pending_matches; ++ ++ me->next = *pos; ++ *pos = me; ++#ifdef DEBUG ++ printf("%s: inserted match %s (family %d, revision %d):\n", ++ __func__, me->name, me->family, me->revision); ++ for (pos = &xtables_pending_matches; *pos; pos = &(*pos)->next) { ++ printf("%s:\tmatch %s (family %d, revision %d)\n", __func__, ++ (*pos)->name, (*pos)->family, (*pos)->revision); ++ } ++#endif + } + + /** +@@ -1097,6 +1127,9 @@ void xtables_register_matches(struct xtables_match *match, unsigned int n) + + void xtables_register_target(struct xtables_target *me) + { ++ struct xtables_target **pos; ++ bool seen_myself = false; ++ + if (me->next) { + fprintf(stderr, "%s: target \"%s\" already registered\n", + xt_params->program_name, me->name); +@@ -1152,9 +1185,33 @@ void xtables_register_target(struct xtables_target *me) + if (me->family != afinfo->family && me->family != AF_UNSPEC) + return; + +- /* place on linked list of targets pending full registration */ +- me->next = xtables_pending_targets; +- xtables_pending_targets = me; ++ /* order into linked list of targets pending full registration */ ++ for (pos = &xtables_pending_targets; *pos; pos = &(*pos)->next) { ++ /* group by name */ ++ if (!extension_cmp(me->name, (*pos)->name, (*pos)->family)) { ++ if (seen_myself) ++ break; /* end of own group, append to it */ ++ continue; ++ } ++ /* found own group */ ++ seen_myself = true; ++ if (xtables_target_prefer(me, *pos) >= 0) ++ break; /* put preferred items first in group */ ++ } ++ /* if own group was not found, prepend item */ ++ if (!*pos && !seen_myself) ++ pos = &xtables_pending_targets; ++ ++ me->next = *pos; ++ *pos = me; ++#ifdef DEBUG ++ printf("%s: inserted target %s (family %d, revision %d):\n", ++ __func__, me->name, me->family, me->revision); ++ for (pos = &xtables_pending_targets; *pos; pos = &(*pos)->next) { ++ printf("%s:\ttarget %s (family %d, revision %d)\n", __func__, ++ (*pos)->name, (*pos)->family, (*pos)->revision); ++ } ++#endif + } + + static bool xtables_fully_register_pending_target(struct xtables_target *me) +-- +2.28.0 + diff --git a/SOURCES/0036-libxtables-Simplify-pending-extension-registration.patch b/SOURCES/0036-libxtables-Simplify-pending-extension-registration.patch new file mode 100644 index 0000000..9b527a5 --- /dev/null +++ b/SOURCES/0036-libxtables-Simplify-pending-extension-registration.patch @@ -0,0 +1,241 @@ +From 1a842fb1cfb3b36f3081aee37c5fdd4a897d77d5 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Fri, 18 Sep 2020 18:48:14 +0200 +Subject: [PATCH] libxtables: Simplify pending extension registration + +Assuming that pending extensions are sorted by first name and family, +then descending revision, the decision where to insert a newly +registered extension may be simplified by memorizing the previous +registration (which obviously is of same name and family and higher +revision). + +As a side-effect, fix for unsupported old extension revisions lingering +in pending extension list forever and being retried with every use of +the given extension. Any revision being rejected by the kernel may +safely be dropped iff a previous (read: higher) revision was accepted +already. + +Yet another side-effect of this change is the removal of an unwanted +recursion by xtables_fully_register_pending_*() into itself via +xtables_find_*(). + +Signed-off-by: Phil Sutter +(cherry picked from commit a1eaaceb0460b338294e40bdd5bc5186320a478c) +Signed-off-by: Phil Sutter +--- + libxtables/xtables.c | 128 +++++++++++-------------------------------- + 1 file changed, 33 insertions(+), 95 deletions(-) + +diff --git a/libxtables/xtables.c b/libxtables/xtables.c +index 13139d7f8ad62..409128333e0e6 100644 +--- a/libxtables/xtables.c ++++ b/libxtables/xtables.c +@@ -203,8 +203,10 @@ struct xtables_match *xtables_matches; + struct xtables_target *xtables_targets; + + /* Fully register a match/target which was previously partially registered. */ +-static bool xtables_fully_register_pending_match(struct xtables_match *me); +-static bool xtables_fully_register_pending_target(struct xtables_target *me); ++static bool xtables_fully_register_pending_match(struct xtables_match *me, ++ struct xtables_match *prev); ++static bool xtables_fully_register_pending_target(struct xtables_target *me, ++ struct xtables_target *prev); + + void xtables_init(void) + { +@@ -616,6 +618,7 @@ struct xtables_match * + xtables_find_match(const char *name, enum xtables_tryload tryload, + struct xtables_rule_match **matches) + { ++ struct xtables_match *prev = NULL; + struct xtables_match **dptr; + struct xtables_match *ptr; + const char *icmp6 = "icmp6"; +@@ -637,8 +640,12 @@ 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)) ++ if (xtables_fully_register_pending_match(ptr, prev)) { ++ prev = ptr; + continue; ++ } else if (prev) { ++ continue; ++ } + *dptr = ptr; + } + dptr = &((*dptr)->next); +@@ -732,6 +739,7 @@ xtables_find_match_revision(const char *name, enum xtables_tryload tryload, + struct xtables_target * + xtables_find_target(const char *name, enum xtables_tryload tryload) + { ++ struct xtables_target *prev = NULL; + struct xtables_target **dptr; + struct xtables_target *ptr; + +@@ -748,8 +756,12 @@ 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)) ++ if (xtables_fully_register_pending_target(ptr, prev)) { ++ prev = ptr; + continue; ++ } else if (prev) { ++ continue; ++ } + *dptr = ptr; + } + dptr = &((*dptr)->next); +@@ -1052,64 +1064,27 @@ static int xtables_target_prefer(const struct xtables_target *a, + b->revision, b->family); + } + +-static bool xtables_fully_register_pending_match(struct xtables_match *me) ++static bool xtables_fully_register_pending_match(struct xtables_match *me, ++ struct xtables_match *prev) + { +- struct xtables_match **i, *old, *pos = NULL; ++ struct xtables_match **i; + const char *rn; +- int compare; + + /* See if new match can be used. */ + rn = (me->real_name != NULL) ? me->real_name : me->name; + if (!compatible_match_revision(rn, me->revision)) + return false; + +- old = xtables_find_match(me->name, XTF_DURING_LOAD, NULL); +- while (old) { +- compare = xtables_match_prefer(old, me); +- if (compare == 0) { +- fprintf(stderr, +- "%s: match `%s' already registered.\n", +- xt_params->program_name, me->name); +- exit(1); +- } +- +- /* Now we have two (or more) options, check compatibility. */ +- rn = (old->real_name != NULL) ? old->real_name : old->name; +- if (compare > 0) { +- /* Kernel tells old isn't compatible anymore??? */ +- if (!compatible_match_revision(rn, old->revision)) { +- /* Delete old one. */ +- for (i = &xtables_matches; *i != old;) +- i = &(*i)->next; +- *i = old->next; +- } +- pos = old; +- old = old->next; +- if (!old) +- break; +- if (!extension_cmp(me->name, old->name, old->family)) +- break; +- continue; +- } +- +- /* Found right old */ +- pos = old; +- break; +- } +- +- if (!pos) { ++ if (!prev) { + /* Append to list. */ + for (i = &xtables_matches; *i; i = &(*i)->next); +- } else if (compare < 0) { +- /* Prepend it */ +- for (i = &xtables_matches; *i != pos; i = &(*i)->next); +- } else if (compare > 0) { ++ } else { + /* Append it */ +- i = &pos->next; +- pos = pos->next; ++ i = &prev->next; ++ prev = prev->next; + } + +- me->next = pos; ++ me->next = prev; + *i = me; + + me->m = NULL; +@@ -1214,11 +1189,11 @@ void xtables_register_target(struct xtables_target *me) + #endif + } + +-static bool xtables_fully_register_pending_target(struct xtables_target *me) ++static bool xtables_fully_register_pending_target(struct xtables_target *me, ++ struct xtables_target *prev) + { +- struct xtables_target **i, *old, *pos = NULL; ++ struct xtables_target **i; + const char *rn; +- int compare; + + if (strcmp(me->name, "standard") != 0) { + /* See if new target can be used. */ +@@ -1227,54 +1202,17 @@ static bool xtables_fully_register_pending_target(struct xtables_target *me) + return false; + } + +- old = xtables_find_target(me->name, XTF_DURING_LOAD); +- while (old) { +- compare = xtables_target_prefer(old, me); +- if (compare == 0) { +- fprintf(stderr, +- "%s: target `%s' already registered.\n", +- xt_params->program_name, me->name); +- exit(1); +- } +- +- /* Now we have two (or more) options, check compatibility. */ +- rn = (old->real_name != NULL) ? old->real_name : old->name; +- if (compare > 0) { +- /* Kernel tells old isn't compatible anymore??? */ +- if (!compatible_target_revision(rn, old->revision)) { +- /* Delete old one. */ +- for (i = &xtables_targets; *i != old;) +- i = &(*i)->next; +- *i = old->next; +- } +- pos = old; +- old = old->next; +- if (!old) +- break; +- if (!extension_cmp(me->name, old->name, old->family)) +- break; +- continue; +- } +- +- /* Found right old */ +- pos = old; +- break; +- } +- +- if (!pos) { ++ if (!prev) { + /* Prepend to list. */ + i = &xtables_targets; +- pos = xtables_targets; +- } else if (compare < 0) { +- /* Prepend it */ +- for (i = &xtables_targets; *i != pos; i = &(*i)->next); +- } else if (compare > 0) { ++ prev = xtables_targets; ++ } else { + /* Append it */ +- i = &pos->next; +- pos = pos->next; ++ i = &prev->next; ++ prev = prev->next; + } + +- me->next = pos; ++ me->next = prev; + *i = me; + + me->t = NULL; +-- +2.28.0 + diff --git a/SOURCES/0037-libxtables-Register-multiple-extensions-in-ascending.patch b/SOURCES/0037-libxtables-Register-multiple-extensions-in-ascending.patch new file mode 100644 index 0000000..1146305 --- /dev/null +++ b/SOURCES/0037-libxtables-Register-multiple-extensions-in-ascending.patch @@ -0,0 +1,52 @@ +From cfcafd3638cdc06a8b4a1d267e58b5ad1e35922c Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 22 Sep 2020 20:01:15 +0200 +Subject: [PATCH] libxtables: Register multiple extensions in ascending order + +The newly introduced ordered insert algorithm in +xtables_register_{match,target}() works best if extensions of same name +are passed in ascending revisions. Since this is the case in about all +extensions' arrays, iterate over them from beginning to end. + +Signed-off-by: Phil Sutter +(cherry picked from commit b5f1a3beac1d1f2b96c8be8ebec450f5ea758090) +Signed-off-by: Phil Sutter +--- + libxtables/xtables.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/libxtables/xtables.c b/libxtables/xtables.c +index 409128333e0e6..28ffffedd8147 100644 +--- a/libxtables/xtables.c ++++ b/libxtables/xtables.c +@@ -1095,9 +1095,10 @@ static bool xtables_fully_register_pending_match(struct xtables_match *me, + + void xtables_register_matches(struct xtables_match *match, unsigned int n) + { +- do { +- xtables_register_match(&match[--n]); +- } while (n > 0); ++ int i; ++ ++ for (i = 0; i < n; i++) ++ xtables_register_match(&match[i]); + } + + void xtables_register_target(struct xtables_target *me) +@@ -1223,9 +1224,10 @@ static bool xtables_fully_register_pending_target(struct xtables_target *me, + + void xtables_register_targets(struct xtables_target *target, unsigned int n) + { +- do { +- xtables_register_target(&target[--n]); +- } while (n > 0); ++ int i; ++ ++ for (i = 0; i < n; i++) ++ xtables_register_target(&target[i]); + } + + /* receives a list of xtables_rule_match, release them */ +-- +2.28.0 + diff --git a/SOURCES/0038-tests-shell-Test-for-fixed-extension-registration.patch b/SOURCES/0038-tests-shell-Test-for-fixed-extension-registration.patch new file mode 100644 index 0000000..7a5cca6 --- /dev/null +++ b/SOURCES/0038-tests-shell-Test-for-fixed-extension-registration.patch @@ -0,0 +1,53 @@ +From f5185f4efad409fc7ec4ae05267b642ee4103a6c Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Fri, 4 Dec 2020 17:44:51 +0100 +Subject: [PATCH] tests/shell: Test for fixed extension registration + +Use strace to look at iptables-restore behaviour with typically +problematic input (conntrack revision 0 is no longer supported by +current kernels) to make sure the fix in commit a1eaaceb0460b +("libxtables: Simplify pending extension registration") is still +effective. + +Signed-off-by: Phil Sutter +(cherry picked from commit 93d0c97e8b6713f51ba679e01a1338d4f9076e7c) +Signed-off-by: Phil Sutter +--- + .../0017-pointless-compat-checks_0 | 25 +++++++++++++++++++ + 1 file changed, 25 insertions(+) + create mode 100755 iptables/tests/shell/testcases/ipt-restore/0017-pointless-compat-checks_0 + +diff --git a/iptables/tests/shell/testcases/ipt-restore/0017-pointless-compat-checks_0 b/iptables/tests/shell/testcases/ipt-restore/0017-pointless-compat-checks_0 +new file mode 100755 +index 0000000000000..cf73de32df409 +--- /dev/null ++++ b/iptables/tests/shell/testcases/ipt-restore/0017-pointless-compat-checks_0 +@@ -0,0 +1,25 @@ ++#!/bin/bash ++ ++# A bug in extension registration would leave unsupported older extension ++# revisions in pending list and get compatibility checked again for each rule ++# using them. With SELinux enabled, the resulting socket() call per rule leads ++# to significant slowdown (~50% performance in worst cases). ++ ++set -e ++ ++strace --version >/dev/null || { echo "skip for missing strace"; exit 0; } ++ ++RULESET="$( ++ echo "*filter" ++ for ((i = 0; i < 100; i++)); do ++ echo "-A FORWARD -m conntrack --ctstate NEW" ++ done ++ echo "COMMIT" ++)" ++ ++cmd="$XT_MULTI iptables-restore" ++socketcount=$(strace -esocket $cmd <<< "$RULESET" 2>&1 | wc -l) ++ ++# unpatched iptables-restore would open 111 sockets, ++# patched only 12 but keep a certain margin for future changes ++[[ $socketcount -lt 20 ]] +-- +2.28.0 + diff --git a/SOURCES/0039-extensions-libipt_icmp-Fix-translation-of-type-any.patch b/SOURCES/0039-extensions-libipt_icmp-Fix-translation-of-type-any.patch new file mode 100644 index 0000000..c1c430d --- /dev/null +++ b/SOURCES/0039-extensions-libipt_icmp-Fix-translation-of-type-any.patch @@ -0,0 +1,52 @@ +From 6adcdca2aaf8cba6ee452c88f41ad8695bebdcfc Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Tue, 6 Oct 2020 19:07:19 +0200 +Subject: [PATCH] extensions: libipt_icmp: Fix translation of type 'any' + +By itself, '-m icmp --icmp-type any' is a noop, it matches any icmp +types. Yet nft_ipv4_xlate() does not emit an 'ip protocol' match if +there's an extension with same name present in the rule. Luckily, legacy +iptables demands icmp match to be prepended by '-p icmp', so we can +assume this is present and just emit the 'ip protocol' match from icmp +xlate callback. + +Fixes: aa158ca0fda65 ("extensions: libipt_icmp: Add translation to nft") +Signed-off-by: Phil Sutter +Reviewed-by: Florian Westphal +(cherry picked from commit ad4b17b98bbedf93d2182a4dc9a37e9cf3adfe1b) +Signed-off-by: Phil Sutter +--- + extensions/libipt_icmp.c | 5 +++++ + extensions/libipt_icmp.txlate | 3 +++ + 2 files changed, 8 insertions(+) + +diff --git a/extensions/libipt_icmp.c b/extensions/libipt_icmp.c +index e76257c54708c..e5e236613f39f 100644 +--- a/extensions/libipt_icmp.c ++++ b/extensions/libipt_icmp.c +@@ -256,6 +256,11 @@ static int icmp_xlate(struct xt_xlate *xl, + if (!type_xlate_print(xl, info->type, info->code[0], + info->code[1])) + return 0; ++ } else { ++ /* '-m icmp --icmp-type any' is a noop by itself, ++ * but it eats a (mandatory) previous '-p icmp' so ++ * emit it here */ ++ xt_xlate_add(xl, "ip protocol icmp"); + } + return 1; + } +diff --git a/extensions/libipt_icmp.txlate b/extensions/libipt_icmp.txlate +index 434f8cc4eb1ae..a2aec8e26df75 100644 +--- a/extensions/libipt_icmp.txlate ++++ b/extensions/libipt_icmp.txlate +@@ -6,3 +6,6 @@ nft add rule ip filter INPUT icmp type destination-unreachable counter accept + + iptables-translate -t filter -A INPUT -m icmp ! --icmp-type 3 -j ACCEPT + nft add rule ip filter INPUT icmp type != destination-unreachable counter accept ++ ++iptables-translate -t filter -A INPUT -m icmp --icmp-type any -j ACCEPT ++nft add rule ip filter INPUT ip protocol icmp counter accept +-- +2.28.0 + diff --git a/SOURCES/0040-extensions-libxt_CT-add-translation-for-NOTRACK.patch b/SOURCES/0040-extensions-libxt_CT-add-translation-for-NOTRACK.patch new file mode 100644 index 0000000..ca1316b --- /dev/null +++ b/SOURCES/0040-extensions-libxt_CT-add-translation-for-NOTRACK.patch @@ -0,0 +1,66 @@ +From 1695f552d3947299e54978bcd5bc1cdc3a5c14f7 Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Wed, 15 Apr 2020 18:16:41 +0200 +Subject: [PATCH] extensions: libxt_CT: add translation for NOTRACK + +Signed-off-by: Pablo Neira Ayuso +(cherry picked from commit f3d4a3ddbcfca15a00dd9758f481420038f6de10) +Signed-off-by: Phil Sutter +--- + extensions/libxt_CT.c | 16 ++++++++++++++++ + extensions/libxt_NOTRACK.txlate | 2 ++ + 2 files changed, 18 insertions(+) + create mode 100644 extensions/libxt_NOTRACK.txlate + +diff --git a/extensions/libxt_CT.c b/extensions/libxt_CT.c +index 371b21766c56c..fbbbe2660e9fc 100644 +--- a/extensions/libxt_CT.c ++++ b/extensions/libxt_CT.c +@@ -348,6 +348,20 @@ static void notrack_ct2_tg_init(struct xt_entry_target *target) + info->flags = XT_CT_NOTRACK | XT_CT_NOTRACK_ALIAS; + } + ++static int xlate_ct1_tg(struct xt_xlate *xl, ++ const struct xt_xlate_tg_params *params) ++{ ++ struct xt_ct_target_info_v1 *info = ++ (struct xt_ct_target_info_v1 *)params->target->data; ++ ++ if (info->flags & XT_CT_NOTRACK) ++ xt_xlate_add(xl, "notrack"); ++ else ++ return 0; ++ ++ return 1; ++} ++ + static struct xtables_target ct_target_reg[] = { + { + .family = NFPROTO_UNSPEC, +@@ -387,6 +401,7 @@ static struct xtables_target ct_target_reg[] = { + .alias = ct_print_name_alias, + .x6_parse = ct_parse_v1, + .x6_options = ct_opts_v1, ++ .xlate = xlate_ct1_tg, + }, + { + .family = NFPROTO_UNSPEC, +@@ -418,6 +433,7 @@ static struct xtables_target ct_target_reg[] = { + .size = XT_ALIGN(sizeof(struct xt_ct_target_info_v1)), + .userspacesize = offsetof(struct xt_ct_target_info_v1, ct), + .init = notrack_ct2_tg_init, ++ .xlate = xlate_ct1_tg, + }, + { + .family = NFPROTO_UNSPEC, +diff --git a/extensions/libxt_NOTRACK.txlate b/extensions/libxt_NOTRACK.txlate +new file mode 100644 +index 0000000000000..9d35619d3dbdd +--- /dev/null ++++ b/extensions/libxt_NOTRACK.txlate +@@ -0,0 +1,2 @@ ++iptables-translate -A PREROUTING -t raw -j NOTRACK ++nft add rule ip raw PREROUTING counter notrack +-- +2.28.0 + diff --git a/SOURCES/0041-nft-Fix-command-name-in-ip6tables-error-message.patch b/SOURCES/0041-nft-Fix-command-name-in-ip6tables-error-message.patch new file mode 100644 index 0000000..3a57697 --- /dev/null +++ b/SOURCES/0041-nft-Fix-command-name-in-ip6tables-error-message.patch @@ -0,0 +1,45 @@ +From 6d8a390bd13bac294ff9de225a49fe9e4de2d6e5 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Fri, 7 Aug 2020 13:48:28 +0200 +Subject: [PATCH] nft: Fix command name in ip6tables error message + +Upon errors, ip6tables-nft would prefix its error messages with +'iptables:' instead of 'ip6tables:'. Turns out the command name was +hard-coded, use 'progname' variable instead. +While being at it, merge the two mostly identical fprintf() calls into +one. + +Signed-off-by: Phil Sutter +Acked-by: Pablo Neira Ayuso +(cherry picked from commit 3be40dcfb5af1438b6abdbda45a1e3b59c104e13) +Signed-off-by: Phil Sutter +--- + iptables/xtables-standalone.c | 12 ++++-------- + 1 file changed, 4 insertions(+), 8 deletions(-) + +diff --git a/iptables/xtables-standalone.c b/iptables/xtables-standalone.c +index 022d5dd44abbf..b5b7ccaf4e660 100644 +--- a/iptables/xtables-standalone.c ++++ b/iptables/xtables-standalone.c +@@ -74,14 +74,10 @@ xtables_main(int family, const char *progname, int argc, char *argv[]) + nft_fini(&h); + + if (!ret) { +- if (errno == EINVAL) { +- fprintf(stderr, "iptables: %s. " +- "Run `dmesg' for more information.\n", +- nft_strerror(errno)); +- } else { +- fprintf(stderr, "iptables: %s.\n", +- nft_strerror(errno)); +- } ++ fprintf(stderr, "%s: %s.%s\n", progname, nft_strerror(errno), ++ (errno == EINVAL ? ++ " Run `dmesg' for more information." : "")); ++ + if (errno == EAGAIN) + exit(RESOURCE_PROBLEM); + } +-- +2.28.0 + diff --git a/SOURCES/0042-tests-shell-Merge-and-extend-return-codes-test.patch b/SOURCES/0042-tests-shell-Merge-and-extend-return-codes-test.patch new file mode 100644 index 0000000..93ce99a --- /dev/null +++ b/SOURCES/0042-tests-shell-Merge-and-extend-return-codes-test.patch @@ -0,0 +1,206 @@ +From c5f07a7d718f812f916686926567adbac6c1b125 Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Thu, 6 Aug 2020 18:52:34 +0200 +Subject: [PATCH] tests: shell: Merge and extend return codes test + +Merge scripts for iptables and ip6tables, they were widely identical. +Also extend the test by one check (removing a non-existent rule with +valid chain and target) and quote the error messages where differences +are deliberately ignored. + +Signed-off-by: Phil Sutter +Acked-by: Pablo Neira Ayuso +(cherry picked from commit cd3e83d1b04fd2683f0fb06e496ee5be08a96b4f) + +Conflicts: + iptables/tests/shell/testcases/ip6tables/0004-return-codes_0 + iptables/tests/shell/testcases/iptables/0004-return-codes_0 +-> Missing upstream commit a7f1e208cdf9c ("nft: split parsing from + netlink commands") which added a few tests to both files. + +Signed-off-by: Phil Sutter +--- + .../testcases/ip6tables/0004-return-codes_0 | 38 ------- + .../testcases/iptables/0004-return-codes_0 | 104 ++++++++++-------- + 2 files changed, 58 insertions(+), 84 deletions(-) + delete mode 100755 iptables/tests/shell/testcases/ip6tables/0004-return-codes_0 + +diff --git a/iptables/tests/shell/testcases/ip6tables/0004-return-codes_0 b/iptables/tests/shell/testcases/ip6tables/0004-return-codes_0 +deleted file mode 100755 +index f023b7915498e..0000000000000 +--- a/iptables/tests/shell/testcases/ip6tables/0004-return-codes_0 ++++ /dev/null +@@ -1,38 +0,0 @@ +-#!/bin/sh +- +-# make sure error return codes are as expected useful cases +-# (e.g. commands to check ruleset state) +- +-global_rc=0 +- +-cmd() { # (rc, cmd, [args ...]) +- rc_exp=$1; shift +- +- $XT_MULTI "$@" +- rc=$? +- +- [ $rc -eq $rc_exp ] || { +- echo "---> expected $rc_exp, got $rc for command '$@'" +- global_rc=1 +- } +-} +- +-# test chain creation +-cmd 0 ip6tables -N foo +-cmd 1 ip6tables -N foo +-# iptables-nft allows this - bug or feature? +-#cmd 2 ip6tables -N "invalid name" +- +-# test rule adding +-cmd 0 ip6tables -A INPUT -j ACCEPT +-cmd 1 ip6tables -A noexist -j ACCEPT +- +-# test rule checking +-cmd 0 ip6tables -C INPUT -j ACCEPT +-cmd 1 ip6tables -C FORWARD -j ACCEPT +-cmd 1 ip6tables -C nonexist -j ACCEPT +-cmd 2 ip6tables -C INPUT -j foobar +-cmd 2 ip6tables -C INPUT -m foobar -j ACCEPT +-cmd 3 ip6tables -t foobar -C INPUT -j ACCEPT +- +-exit $global_rc +diff --git a/iptables/tests/shell/testcases/iptables/0004-return-codes_0 b/iptables/tests/shell/testcases/iptables/0004-return-codes_0 +index ce02e0bcb128b..67f1698945753 100755 +--- a/iptables/tests/shell/testcases/iptables/0004-return-codes_0 ++++ b/iptables/tests/shell/testcases/iptables/0004-return-codes_0 +@@ -13,69 +13,81 @@ cmd() { # (rc, msg, cmd, [args ...]) + msg_exp="$1"; shift + } + +- msg="$($XT_MULTI "$@" 2>&1 >/dev/null)" +- rc=$? ++ for ipt in iptables ip6tables; do ++ msg="$($XT_MULTI $ipt "$@" 2>&1 >/dev/null)" ++ rc=$? + +- [ $rc -eq $rc_exp ] || { +- echo "---> expected return code $rc_exp, got $rc for command '$@'" +- global_rc=1 +- } ++ [ $rc -eq $rc_exp ] || { ++ echo "---> expected return code $rc_exp, got $rc for command '$ipt $@'" ++ global_rc=1 ++ } + +- [ -n "$msg_exp" ] || return +- grep -q "$msg_exp" <<< $msg || { +- echo "---> expected error message '$msg_exp', got '$msg' for command '$@'" +- global_rc=1 +- } ++ [ -n "$msg_exp" ] || continue ++ msg_exp_full="${ipt}$msg_exp" ++ grep -q "$msg_exp_full" <<< $msg || { ++ echo "---> expected error message '$msg_exp_full', got '$msg' for command '$ipt $@'" ++ global_rc=1 ++ } ++ done + } + +-EEXIST_F="File exists." +-EEXIST="Chain already exists." +-ENOENT="No chain/target/match by that name." +-E2BIG_I="Index of insertion too big." +-E2BIG_D="Index of deletion too big." +-E2BIG_R="Index of replacement too big." +-EBADRULE="Bad rule (does a matching rule exist in that chain?)." +-ENOTGT="Couldn't load target \`foobar':No such file or directory" +-ENOMTH="Couldn't load match \`foobar':No such file or directory" +-ENOTBL="can't initialize iptables table \`foobar': Table does not exist" ++EEXIST_F=": File exists." ++EEXIST=": Chain already exists." ++ENOENT=": No chain/target/match by that name." ++E2BIG_I=": Index of insertion too big." ++E2BIG_D=": Index of deletion too big." ++E2BIG_R=": Index of replacement too big." ++EBADRULE=": Bad rule (does a matching rule exist in that chain?)." ++#ENOTGT=" v[0-9\.]* [^ ]*: Couldn't load target \`foobar':No such file or directory" ++ENOMTH=" v[0-9\.]* [^ ]*: Couldn't load match \`foobar':No such file or directory" ++ENOTBL=": can't initialize iptables table \`foobar': Table does not exist" + + # test chain creation +-cmd 0 iptables -N foo +-cmd 1 "$EEXIST" iptables -N foo ++cmd 0 -N foo ++cmd 1 "$EEXIST" -N foo + # iptables-nft allows this - bug or feature? +-#cmd 2 iptables -N "invalid name" ++#cmd 2 -N "invalid name" + + # test chain flushing/zeroing +-cmd 0 iptables -F foo +-cmd 0 iptables -Z foo +-cmd 1 "$ENOENT" iptables -F bar +-cmd 1 "$ENOENT" iptables -Z bar ++cmd 0 -F foo ++cmd 0 -Z foo ++cmd 1 "$ENOENT" -F bar ++cmd 1 "$ENOENT" -Z bar + + # test chain rename +-cmd 0 iptables -E foo bar +-cmd 1 "$EEXIST_F" iptables -E foo bar ++cmd 0 -E foo bar ++cmd 1 "$EEXIST_F" -E foo bar + + # test rule adding +-cmd 0 iptables -A INPUT -j ACCEPT +-cmd 1 "$ENOENT" iptables -A noexist -j ACCEPT ++cmd 0 -A INPUT -j ACCEPT ++cmd 1 "$ENOENT" -A noexist -j ACCEPT ++# next three differ: ++# legacy: Couldn't load target `foobar':No such file or directory ++# nft: Chain 'foobar' does not exist ++cmd 2 "" -I INPUT -j foobar ++cmd 2 "" -R INPUT 1 -j foobar ++cmd 2 "" -D INPUT -j foobar ++cmd 1 "$EBADRULE" -D INPUT -p tcp --dport 22 -j ACCEPT + + # test rulenum commands +-cmd 1 "$E2BIG_I" iptables -I INPUT 23 -j ACCEPT +-cmd 1 "$E2BIG_D" iptables -D INPUT 23 +-cmd 1 "$E2BIG_R" iptables -R INPUT 23 -j ACCEPT +-cmd 1 "$ENOENT" iptables -I nonexist 23 -j ACCEPT +-cmd 1 "$ENOENT" iptables -D nonexist 23 +-cmd 1 "$ENOENT" iptables -R nonexist 23 -j ACCEPT ++cmd 1 "$E2BIG_I" -I INPUT 23 -j ACCEPT ++cmd 1 "$E2BIG_D" -D INPUT 23 ++cmd 1 "$E2BIG_R" -R INPUT 23 -j ACCEPT ++cmd 1 "$ENOENT" -I nonexist 23 -j ACCEPT ++cmd 1 "$ENOENT" -D nonexist 23 ++cmd 1 "$ENOENT" -R nonexist 23 -j ACCEPT + + # test rule checking +-cmd 0 iptables -C INPUT -j ACCEPT +-cmd 1 "$EBADRULE" iptables -C FORWARD -j ACCEPT +-cmd 1 "$BADRULE" iptables -C nonexist -j ACCEPT +-cmd 2 "$ENOMTH" iptables -C INPUT -m foobar -j ACCEPT ++cmd 0 -C INPUT -j ACCEPT ++cmd 1 "$EBADRULE" -C FORWARD -j ACCEPT ++cmd 1 "$BADRULE" -C nonexist -j ACCEPT ++cmd 2 "$ENOMTH" -C INPUT -m foobar -j ACCEPT + # messages of those don't match, but iptables-nft ones are actually nicer. +-#cmd 2 "$ENOTGT" iptables -C INPUT -j foobar +-#cmd 3 "$ENOTBL" iptables -t foobar -C INPUT -j ACCEPT +-cmd 2 "" iptables -C INPUT -j foobar +-cmd 3 "" iptables -t foobar -C INPUT -j ACCEPT ++# legacy: Couldn't load target `foobar':No such file or directory ++# nft: Chain 'foobar' does not exist ++cmd 2 "" -C INPUT -j foobar ++# legacy: can't initialize ip6tables table `foobar': Table does not exist (do you need to insmod?) ++# nft: table 'foobar' does not exist ++cmd 3 "" -t foobar -C INPUT -j ACCEPT + + exit $global_rc +-- +2.28.0 + diff --git a/SOURCES/0043-extensions-dccp-Fix-for-DCCP-type-INVALID.patch b/SOURCES/0043-extensions-dccp-Fix-for-DCCP-type-INVALID.patch new file mode 100644 index 0000000..bf463a2 --- /dev/null +++ b/SOURCES/0043-extensions-dccp-Fix-for-DCCP-type-INVALID.patch @@ -0,0 +1,177 @@ +From 98794894774a39927bc975921fc9e40f00db937b Mon Sep 17 00:00:00 2001 +From: Phil Sutter +Date: Wed, 2 Dec 2020 13:37:06 +0100 +Subject: [PATCH] extensions: dccp: Fix for DCCP type 'INVALID' + +Support for matching on invalid DCCP type field values was pretty +broken: While RFC4340 declares any type value from 10 to 15 invalid, the +extension's type name 'INVALID' mapped to type value 10 only. Fix this +by introduction of INVALID_OTHER_TYPE_MASK which has the remaining +invalid type's bits set and apply it if bit 10 is set after parsing the +type list. When printing, stop searching type names after printing +'INVALID' - unless numeric output was requested. The latter prints all +actual type values. Since parsing types in numeric form is not +supported, changing the output should not break existing scripts. + +When translating into nftables syntax, the code returned prematurely if +'INVALID' was among the list of types - thereby emitting invalid syntax. +Instead print a real match for invalid types by use of a range +expression. + +While being at it, fix syntax of translator output: If only +'--dccp-types' was translated, the output contained an extra 'dccp'. On +the other hand, if '--sport' and '--dport' was present, a required +'dccp' between the translations of both was missing. + +Fixes: e40b11d7ef827 ("add support for new 'dccp' protocol match") +Fixes: c94a998724143 ("extensions: libxt_dccp: Add translation to nft") +Signed-off-by: Phil Sutter +(cherry picked from commit 4bcbc8e11a2764f4537dc405962f83cd072cccfe) +Signed-off-by: Phil Sutter +--- + extensions/libxt_dccp.c | 58 ++++++++++++++++++++++-------------- + extensions/libxt_dccp.txlate | 12 ++++++-- + 2 files changed, 45 insertions(+), 25 deletions(-) + +diff --git a/extensions/libxt_dccp.c b/extensions/libxt_dccp.c +index 5e67c264db2a9..aea3e20be4818 100644 +--- a/extensions/libxt_dccp.c ++++ b/extensions/libxt_dccp.c +@@ -76,6 +76,9 @@ static const char *const dccp_pkt_types[] = { + [DCCP_PKT_INVALID] = "INVALID", + }; + ++/* Bits for type values 11-15 */ ++#define INVALID_OTHER_TYPE_MASK 0xf800 ++ + static uint16_t + parse_dccp_types(const char *typestring) + { +@@ -95,6 +98,9 @@ parse_dccp_types(const char *typestring) + xtables_error(PARAMETER_PROBLEM, + "Unknown DCCP type `%s'", ptr); + } ++ if (typemask & (1 << DCCP_PKT_INVALID)) ++ typemask |= INVALID_OTHER_TYPE_MASK; ++ + + free(buffer); + return typemask; +@@ -193,9 +199,13 @@ print_types(uint16_t types, int inverted, int numeric) + + if (numeric) + printf("%u", i); +- else ++ else { + printf("%s", dccp_pkt_types[i]); + ++ if (i == DCCP_PKT_INVALID) ++ break; ++ } ++ + types &= ~(1 << i); + } + } +@@ -288,6 +298,7 @@ static const char *const dccp_pkt_types_xlate[] = { + [DCCP_PKT_RESET] = "reset", + [DCCP_PKT_SYNC] = "sync", + [DCCP_PKT_SYNCACK] = "syncack", ++ [DCCP_PKT_INVALID] = "10-15", + }; + + static int dccp_type_xlate(const struct xt_dccp_info *einfo, +@@ -296,10 +307,10 @@ static int dccp_type_xlate(const struct xt_dccp_info *einfo, + bool have_type = false, set_need = false; + uint16_t types = einfo->typemask; + +- if (types & (1 << DCCP_PKT_INVALID)) +- return 0; +- +- xt_xlate_add(xl, " dccp type%s ", einfo->invflags ? " !=" : ""); ++ if (types & INVALID_OTHER_TYPE_MASK) { ++ types &= ~INVALID_OTHER_TYPE_MASK; ++ types |= 1 << DCCP_PKT_INVALID; ++ } + + if ((types != 0) && !(types == (types & -types))) { + xt_xlate_add(xl, "{"); +@@ -335,34 +346,37 @@ static int dccp_xlate(struct xt_xlate *xl, + char *space = ""; + int ret = 1; + +- xt_xlate_add(xl, "dccp "); +- + if (einfo->flags & XT_DCCP_SRC_PORTS) { ++ xt_xlate_add(xl, "dccp sport%s %u", ++ einfo->invflags & XT_DCCP_SRC_PORTS ? " !=" : "", ++ einfo->spts[0]); ++ + if (einfo->spts[0] != einfo->spts[1]) +- xt_xlate_add(xl, "sport%s %u-%u", +- einfo->invflags & XT_DCCP_SRC_PORTS ? " !=" : "", +- einfo->spts[0], einfo->spts[1]); +- else +- xt_xlate_add(xl, "sport%s %u", +- einfo->invflags & XT_DCCP_SRC_PORTS ? " !=" : "", +- einfo->spts[0]); ++ xt_xlate_add(xl, "-%u", einfo->spts[1]); ++ + space = " "; + } + + if (einfo->flags & XT_DCCP_DEST_PORTS) { ++ xt_xlate_add(xl, "%sdccp dport%s %u", space, ++ einfo->invflags & XT_DCCP_DEST_PORTS ? " !=" : "", ++ einfo->dpts[0]); ++ + if (einfo->dpts[0] != einfo->dpts[1]) +- xt_xlate_add(xl, "%sdport%s %u-%u", space, +- einfo->invflags & XT_DCCP_DEST_PORTS ? " !=" : "", +- einfo->dpts[0], einfo->dpts[1]); +- else +- xt_xlate_add(xl, "%sdport%s %u", space, +- einfo->invflags & XT_DCCP_DEST_PORTS ? " !=" : "", +- einfo->dpts[0]); ++ xt_xlate_add(xl, "-%u", einfo->dpts[1]); ++ ++ space = " "; + } + +- if (einfo->flags & XT_DCCP_TYPE) ++ if (einfo->flags & XT_DCCP_TYPE && einfo->typemask) { ++ xt_xlate_add(xl, "%sdccp type%s ", space, ++ einfo->invflags & XT_DCCP_TYPE ? " !=" : ""); + ret = dccp_type_xlate(einfo, xl); + ++ space = " "; ++ } ++ ++ /* FIXME: no dccp option support in nftables yet */ + if (einfo->flags & XT_DCCP_OPTION) + ret = 0; + +diff --git a/extensions/libxt_dccp.txlate b/extensions/libxt_dccp.txlate +index b47dc65f5bc4f..ea853f6acf627 100644 +--- a/extensions/libxt_dccp.txlate ++++ b/extensions/libxt_dccp.txlate +@@ -7,8 +7,14 @@ nft add rule ip filter INPUT dccp dport 100-200 counter + iptables-translate -A INPUT -p dccp -m dccp ! --dport 100 + nft add rule ip filter INPUT dccp dport != 100 counter + +-iptables-translate -A INPUT -p dccp -m dccp --dport 100 --dccp-types REQUEST,RESPONSE,DATA,ACK,DATAACK,CLOSEREQ,CLOSE,SYNC,SYNCACK +-nft add rule ip filter INPUT dccp dport 100 dccp type {request, response, data, ack, dataack, closereq, close, sync, syncack} counter ++iptables-translate -A INPUT -p dccp -m dccp --dccp-types CLOSE ++nft add rule ip filter INPUT dccp type close counter ++ ++iptables-translate -A INPUT -p dccp -m dccp --dccp-types INVALID ++nft add rule ip filter INPUT dccp type 10-15 counter ++ ++iptables-translate -A INPUT -p dccp -m dccp --dport 100 --dccp-types REQUEST,RESPONSE,DATA,ACK,DATAACK,CLOSEREQ,CLOSE,SYNC,SYNCACK,INVALID ++nft add rule ip filter INPUT dccp dport 100 dccp type {request, response, data, ack, dataack, closereq, close, sync, syncack, 10-15} counter + + iptables-translate -A INPUT -p dccp -m dccp --sport 200 --dport 100 +-nft add rule ip filter INPUT dccp sport 200 dport 100 counter ++nft add rule ip filter INPUT dccp sport 200 dccp dport 100 counter +-- +2.28.0 + diff --git a/SOURCES/iptables.init b/SOURCES/iptables.init index 6b391fd..52fa91a 100755 --- a/SOURCES/iptables.init +++ b/SOURCES/iptables.init @@ -134,7 +134,7 @@ load_sysctl() { echo -n $"Loading sysctl settings: " ret=0 for item in $IPTABLES_SYSCTL_LOAD_LIST; do - fgrep -hs $item /etc/sysctl.d/* | sysctl -p - >/dev/null + fgrep -hs $item /etc/sysctl.d/*.conf | sysctl -p - >/dev/null let ret+=$?; done [ $ret -eq 0 ] && success || failure diff --git a/SPECS/iptables.spec b/SPECS/iptables.spec index 4a37e0a..87f721b 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: 14%{?dist} +Release: 17%{?dist} Source: %{url}/files/%{name}-%{version}.tar.bz2 Source1: iptables.init Source2: iptables-config @@ -59,6 +59,24 @@ Patch22: 0022-nfnl_osf-Improve-error-handling.patch Patch23: 0023-nft-cache-Reset-genid-when-rebuilding-cache.patch Patch24: 0024-nft-Fix-for-F-in-iptables-dumps.patch Patch25: 0025-tests-shell-Test-F-in-dump-files.patch +Patch26: 0026-nft-Make-batch_add_chain-return-the-added-batch-obje.patch +Patch27: 0027-nft-Fix-error-reporting-for-refreshed-transactions.patch +Patch28: 0028-nft-Fix-for-concurrent-noflush-restore-calls.patch +Patch29: 0029-tests-shell-Improve-concurrent-noflush-restore-test-.patch +Patch30: 0030-nft-cache-Make-nft_rebuild_cache-respect-fake-cache.patch +Patch31: 0031-nft-Fix-for-broken-address-mask-match-detection.patch +Patch32: 0032-nft-Optimize-class-based-IP-prefix-matches.patch +Patch33: 0033-ebtables-Optimize-masked-MAC-address-matches.patch +Patch34: 0034-tests-shell-Add-test-for-bitwise-avoidance-fixes.patch +Patch35: 0035-libxtables-Make-sure-extensions-register-in-revision.patch +Patch36: 0036-libxtables-Simplify-pending-extension-registration.patch +Patch37: 0037-libxtables-Register-multiple-extensions-in-ascending.patch +Patch38: 0038-tests-shell-Test-for-fixed-extension-registration.patch +Patch39: 0039-extensions-libipt_icmp-Fix-translation-of-type-any.patch +Patch40: 0040-extensions-libxt_CT-add-translation-for-NOTRACK.patch +Patch41: 0041-nft-Fix-command-name-in-ip6tables-error-message.patch +Patch42: 0042-tests-shell-Merge-and-extend-return-codes-test.patch +Patch43: 0043-extensions-dccp-Fix-for-DCCP-type-INVALID.patch # pf.os: ISC license # iptables-apply: Artistic Licence 2.0 @@ -467,6 +485,31 @@ done %doc %{_mandir}/man8/ebtables*.8* %changelog +* Thu Dec 10 2020 Phil Sutter - 1.8.4-17 +- extensions: dccp: Fix for DCCP type 'INVALID' +- tests: shell: Merge and extend return codes test +- nft: Fix command name in ip6tables error message +- extensions: libxt_CT: add translation for NOTRACK +- extensions: libipt_icmp: Fix translation of type 'any' +- tests/shell: Test for fixed extension registration +- libxtables: Register multiple extensions in ascending order +- libxtables: Simplify pending extension registration +- libxtables: Make sure extensions register in revision order + +* Wed Oct 28 2020 Phil Sutter - 1.8.4-16 +- tests/shell: Add test for bitwise avoidance fixes +- ebtables: Optimize masked MAC address matches +- nft: Optimize class-based IP prefix matches +- nft: Fix for broken address mask match detection +- nft: cache: Make nft_rebuild_cache() respect fake cache +- tests: shell: Improve concurrent noflush restore test a bit +- nft: Fix for concurrent noflush restore calls +- nft: Fix error reporting for refreshed transactions +- nft: Make batch_add_chain() return the added batch object + +* Sat Aug 15 2020 Phil Sutter - 1.8.4-15 +- Ignore sysctl files not suffixed '.conf' + * Wed Jun 24 2020 Phil Sutter - 1.8.4-14 - nft: Fix for '-F' in iptables dumps - tests: shell: Test -F in dump files