Skip to content

Commit 3e82acc

Browse files
author
Paolo Abeni
committed
Florian Westphal says: ==================== netfilter: updates for net The following patchset contains Netfilter fixes for *net*: 1) Jozsef Kadlecsik is retiring. Fortunately Jozsef will still keep an eye on ipset patches. 2) remove a bogus direction check from nat core, this caused spurious flakes in the 'reverse clash' selftest, from myself. 3) nf_tables doesn't need to do chain validation on register store, from Pablo Neira Ayuso. 4) nf_tables shouldn't revisit chains during ruleset (graph) validation if possible. Both 3 and 4 were slated for -next initially but there are now two independent reports of people hitting soft lockup errors during ruleset validation, so it makes no sense anymore to route this via -next given this is -stable material. From myself. 5) call cond_resched() in a more frequently visited place during nf_tables chain validation, this wasn't possible earlier due to rcu read lock, but nowadays its not held anymore during set walks. 6) Don't fail conntrack packetdrill test with HZ=100 kernels. netfilter pull request nf-25-12-16 * tag 'nf-25-12-16' of https://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf: selftests: netfilter: packetdrill: avoid failure on HZ=100 kernel netfilter: nf_tables: avoid softlockup warnings in nft_chain_validate netfilter: nf_tables: avoid chain re-validation if possible netfilter: nf_tables: remove redundant chain validation on register store netfilter: nf_nat: remove bogus direction check MAINTAINERS: Remove Jozsef Kadlecsik from MAINTAINERS file ==================== Link: https://patch.msgid.link/20251216190904.14507-1-fw@strlen.de Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2 parents 78a4753 + fec7b07 commit 3e82acc

File tree

8 files changed

+107
-44
lines changed

8 files changed

+107
-44
lines changed

CREDITS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1983,6 +1983,7 @@ D: netfilter: TCP window tracking code
19831983
D: netfilter: raw table
19841984
D: netfilter: iprange match
19851985
D: netfilter: new logging interfaces
1986+
D: netfilter: ipset
19861987
D: netfilter: various other hacks
19871988
S: Tata
19881989
S: Hungary

MAINTAINERS

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17808,7 +17808,6 @@ F: drivers/net/ethernet/neterion/
1780817808

1780917809
NETFILTER
1781017810
M: Pablo Neira Ayuso <pablo@netfilter.org>
17811-
M: Jozsef Kadlecsik <kadlec@netfilter.org>
1781217811
M: Florian Westphal <fw@strlen.de>
1781317812
R: Phil Sutter <phil@nwl.cc>
1781417813
L: netfilter-devel@vger.kernel.org

include/net/netfilter/nf_tables.h

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,6 +1091,29 @@ struct nft_rule_blob {
10911091
__attribute__((aligned(__alignof__(struct nft_rule_dp))));
10921092
};
10931093

1094+
enum nft_chain_types {
1095+
NFT_CHAIN_T_DEFAULT = 0,
1096+
NFT_CHAIN_T_ROUTE,
1097+
NFT_CHAIN_T_NAT,
1098+
NFT_CHAIN_T_MAX
1099+
};
1100+
1101+
/**
1102+
* struct nft_chain_validate_state - validation state
1103+
*
1104+
* If a chain is encountered again during table validation it is
1105+
* possible to avoid revalidation provided the calling context is
1106+
* compatible. This structure stores relevant calling context of
1107+
* previous validations.
1108+
*
1109+
* @hook_mask: the hook numbers and locations the chain is linked to
1110+
* @depth: the deepest call chain level the chain is linked to
1111+
*/
1112+
struct nft_chain_validate_state {
1113+
u8 hook_mask[NFT_CHAIN_T_MAX];
1114+
u8 depth;
1115+
};
1116+
10941117
/**
10951118
* struct nft_chain - nf_tables chain
10961119
*
@@ -1109,6 +1132,7 @@ struct nft_rule_blob {
11091132
* @udlen: user data length
11101133
* @udata: user data in the chain
11111134
* @blob_next: rule blob pointer to the next in the chain
1135+
* @vstate: validation state
11121136
*/
11131137
struct nft_chain {
11141138
struct nft_rule_blob __rcu *blob_gen_0;
@@ -1128,23 +1152,17 @@ struct nft_chain {
11281152

11291153
/* Only used during control plane commit phase: */
11301154
struct nft_rule_blob *blob_next;
1155+
struct nft_chain_validate_state vstate;
11311156
};
11321157

1133-
int nft_chain_validate(const struct nft_ctx *ctx, const struct nft_chain *chain);
1158+
int nft_chain_validate(const struct nft_ctx *ctx, struct nft_chain *chain);
11341159
int nft_setelem_validate(const struct nft_ctx *ctx, struct nft_set *set,
11351160
const struct nft_set_iter *iter,
11361161
struct nft_elem_priv *elem_priv);
11371162
int nft_set_catchall_validate(const struct nft_ctx *ctx, struct nft_set *set);
11381163
int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain);
11391164
void nf_tables_unbind_chain(const struct nft_ctx *ctx, struct nft_chain *chain);
11401165

1141-
enum nft_chain_types {
1142-
NFT_CHAIN_T_DEFAULT = 0,
1143-
NFT_CHAIN_T_ROUTE,
1144-
NFT_CHAIN_T_NAT,
1145-
NFT_CHAIN_T_MAX
1146-
};
1147-
11481166
/**
11491167
* struct nft_chain_type - nf_tables chain type info
11501168
*

net/netfilter/nf_nat_core.c

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -294,25 +294,13 @@ nf_nat_used_tuple_new(const struct nf_conntrack_tuple *tuple,
294294

295295
ct = nf_ct_tuplehash_to_ctrack(thash);
296296

297-
/* NB: IP_CT_DIR_ORIGINAL should be impossible because
298-
* nf_nat_used_tuple() handles origin collisions.
299-
*
300-
* Handle remote chance other CPU confirmed its ct right after.
301-
*/
302-
if (thash->tuple.dst.dir != IP_CT_DIR_REPLY)
303-
goto out;
304-
305297
/* clashing connection subject to NAT? Retry with new tuple. */
306298
if (READ_ONCE(ct->status) & uses_nat)
307299
goto out;
308300

309301
if (nf_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
310-
&ignored_ct->tuplehash[IP_CT_DIR_REPLY].tuple) &&
311-
nf_ct_tuple_equal(&ct->tuplehash[IP_CT_DIR_REPLY].tuple,
312-
&ignored_ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple)) {
302+
&ignored_ct->tuplehash[IP_CT_DIR_REPLY].tuple))
313303
taken = false;
314-
goto out;
315-
}
316304
out:
317305
nf_ct_put(ct);
318306
return taken;

net/netfilter/nf_tables_api.c

Lines changed: 67 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,29 @@ static void nft_validate_state_update(struct nft_table *table, u8 new_validate_s
123123

124124
table->validate_state = new_validate_state;
125125
}
126+
127+
static bool nft_chain_vstate_valid(const struct nft_ctx *ctx,
128+
const struct nft_chain *chain)
129+
{
130+
const struct nft_base_chain *base_chain;
131+
enum nft_chain_types type;
132+
u8 hooknum;
133+
134+
if (WARN_ON_ONCE(!nft_is_base_chain(ctx->chain)))
135+
return false;
136+
137+
base_chain = nft_base_chain(ctx->chain);
138+
hooknum = base_chain->ops.hooknum;
139+
type = base_chain->type->type;
140+
141+
/* chain is already validated for this call depth */
142+
if (chain->vstate.depth >= ctx->level &&
143+
chain->vstate.hook_mask[type] & BIT(hooknum))
144+
return true;
145+
146+
return false;
147+
}
148+
126149
static void nf_tables_trans_destroy_work(struct work_struct *w);
127150

128151
static void nft_trans_gc_work(struct work_struct *work);
@@ -4079,6 +4102,29 @@ static void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *r
40794102
nf_tables_rule_destroy(ctx, rule);
40804103
}
40814104

4105+
static void nft_chain_vstate_update(const struct nft_ctx *ctx, struct nft_chain *chain)
4106+
{
4107+
const struct nft_base_chain *base_chain;
4108+
enum nft_chain_types type;
4109+
u8 hooknum;
4110+
4111+
/* ctx->chain must hold the calling base chain. */
4112+
if (WARN_ON_ONCE(!nft_is_base_chain(ctx->chain))) {
4113+
memset(&chain->vstate, 0, sizeof(chain->vstate));
4114+
return;
4115+
}
4116+
4117+
base_chain = nft_base_chain(ctx->chain);
4118+
hooknum = base_chain->ops.hooknum;
4119+
type = base_chain->type->type;
4120+
4121+
BUILD_BUG_ON(BIT(NF_INET_NUMHOOKS) > U8_MAX);
4122+
4123+
chain->vstate.hook_mask[type] |= BIT(hooknum);
4124+
if (chain->vstate.depth < ctx->level)
4125+
chain->vstate.depth = ctx->level;
4126+
}
4127+
40824128
/** nft_chain_validate - loop detection and hook validation
40834129
*
40844130
* @ctx: context containing call depth and base chain
@@ -4088,15 +4134,25 @@ static void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *r
40884134
* and set lookups until either the jump limit is hit or all reachable
40894135
* chains have been validated.
40904136
*/
4091-
int nft_chain_validate(const struct nft_ctx *ctx, const struct nft_chain *chain)
4137+
int nft_chain_validate(const struct nft_ctx *ctx, struct nft_chain *chain)
40924138
{
40934139
struct nft_expr *expr, *last;
40944140
struct nft_rule *rule;
40954141
int err;
40964142

4143+
BUILD_BUG_ON(NFT_JUMP_STACK_SIZE > 255);
40974144
if (ctx->level == NFT_JUMP_STACK_SIZE)
40984145
return -EMLINK;
40994146

4147+
if (ctx->level > 0) {
4148+
/* jumps to base chains are not allowed. */
4149+
if (nft_is_base_chain(chain))
4150+
return -ELOOP;
4151+
4152+
if (nft_chain_vstate_valid(ctx, chain))
4153+
return 0;
4154+
}
4155+
41004156
list_for_each_entry(rule, &chain->rules, list) {
41014157
if (fatal_signal_pending(current))
41024158
return -EINTR;
@@ -4115,8 +4171,11 @@ int nft_chain_validate(const struct nft_ctx *ctx, const struct nft_chain *chain)
41154171
if (err < 0)
41164172
return err;
41174173
}
4174+
4175+
cond_resched();
41184176
}
41194177

4178+
nft_chain_vstate_update(ctx, chain);
41204179
return 0;
41214180
}
41224181
EXPORT_SYMBOL_GPL(nft_chain_validate);
@@ -4128,7 +4187,7 @@ static int nft_table_validate(struct net *net, const struct nft_table *table)
41284187
.net = net,
41294188
.family = table->family,
41304189
};
4131-
int err;
4190+
int err = 0;
41324191

41334192
list_for_each_entry(chain, &table->chains, list) {
41344193
if (!nft_is_base_chain(chain))
@@ -4137,12 +4196,14 @@ static int nft_table_validate(struct net *net, const struct nft_table *table)
41374196
ctx.chain = chain;
41384197
err = nft_chain_validate(&ctx, chain);
41394198
if (err < 0)
4140-
return err;
4141-
4142-
cond_resched();
4199+
goto err;
41434200
}
41444201

4145-
return 0;
4202+
err:
4203+
list_for_each_entry(chain, &table->chains, list)
4204+
memset(&chain->vstate, 0, sizeof(chain->vstate));
4205+
4206+
return err;
41464207
}
41474208

41484209
int nft_setelem_validate(const struct nft_ctx *ctx, struct nft_set *set,
@@ -11676,21 +11737,10 @@ static int nft_validate_register_store(const struct nft_ctx *ctx,
1167611737
enum nft_data_types type,
1167711738
unsigned int len)
1167811739
{
11679-
int err;
11680-
1168111740
switch (reg) {
1168211741
case NFT_REG_VERDICT:
1168311742
if (type != NFT_DATA_VERDICT)
1168411743
return -EINVAL;
11685-
11686-
if (data != NULL &&
11687-
(data->verdict.code == NFT_GOTO ||
11688-
data->verdict.code == NFT_JUMP)) {
11689-
err = nft_chain_validate(ctx, data->verdict.chain);
11690-
if (err < 0)
11691-
return err;
11692-
}
11693-
1169411744
break;
1169511745
default:
1169611746
if (type != NFT_DATA_VALUE)

tools/testing/selftests/net/netfilter/conntrack_reverse_clash.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,14 @@ static void die(const char *e)
3333
exit(111);
3434
}
3535

36-
static void die_port(uint16_t got, uint16_t want)
36+
static void die_port(const struct sockaddr_in *sin, uint16_t want)
3737
{
38-
fprintf(stderr, "Port number changed, wanted %d got %d\n", want, ntohs(got));
38+
uint16_t got = ntohs(sin->sin_port);
39+
char str[INET_ADDRSTRLEN];
40+
41+
inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str));
42+
43+
fprintf(stderr, "Port number changed, wanted %d got %d from %s\n", want, got, str);
3944
exit(1);
4045
}
4146

@@ -100,7 +105,7 @@ int main(int argc, char *argv[])
100105
die("child recvfrom");
101106

102107
if (peer.sin_port != htons(PORT))
103-
die_port(peer.sin_port, PORT);
108+
die_port(&peer, PORT);
104109
} else {
105110
if (sendto(s2, buf, LEN, 0, (struct sockaddr *)&sa1, sizeof(sa1)) != LEN)
106111
continue;
@@ -109,7 +114,7 @@ int main(int argc, char *argv[])
109114
die("parent recvfrom");
110115

111116
if (peer.sin_port != htons((PORT + 1)))
112-
die_port(peer.sin_port, PORT + 1);
117+
die_port(&peer, PORT + 1);
113118
}
114119
}
115120

tools/testing/selftests/net/netfilter/conntrack_reverse_clash.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ if ip netns exec "$ns0" ./conntrack_reverse_clash; then
4545
echo "PASS: No SNAT performed for null bindings"
4646
else
4747
echo "ERROR: SNAT performed without any matching snat rule"
48+
ip netns exec "$ns0" conntrack -L
49+
ip netns exec "$ns0" conntrack -S
4850
exit 1
4951
fi
5052

tools/testing/selftests/net/netfilter/packetdrill/conntrack_syn_challenge_ack.pkt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
+0.01 > R 643160523:643160523(0) win 0
2828

29-
+0.01 `conntrack -f $NFCT_IP_VERSION -L -p tcp --dport 8080 2>/dev/null | grep UNREPLIED | grep -q SYN_SENT`
29+
+0.1 `conntrack -f $NFCT_IP_VERSION -L -p tcp --dport 8080 2>/dev/null | grep UNREPLIED | grep -q SYN_SENT`
3030

3131
// Must go through.
3232
+0.01 > S 0:0(0) win 65535 <mss 1460,sackOK,TS val 1 ecr 0,nop,wscale 8>

0 commit comments

Comments
 (0)