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/SPECS/iptables.spec b/SPECS/iptables.spec index a631e1c..50145cc 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: 15%{?dist} +Release: 16%{?dist} Source: %{url}/files/%{name}-%{version}.tar.bz2 Source1: iptables.init Source2: iptables-config @@ -59,6 +59,15 @@ 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 # pf.os: ISC license # iptables-apply: Artistic Licence 2.0 @@ -467,6 +476,17 @@ done %doc %{_mandir}/man8/ebtables*.8* %changelog +* 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'