Skip to content

Commit 91ffb08

Browse files
committed
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf
Pablo Neira Ayuso says: ==================== Netfilter fixes for net 1) Fix NAT support for NFPROTO_INET without layer 3 address, from Florian Westphal. 2) Use kfree_rcu(ptr, rcu) variant in nf_tables clean_net path. 3) Use list to collect flowtable hooks to be deleted. 4) Initialize list of hook field in flowtable transaction. 5) Release hooks on error for flowtable updates. 6) Memleak in hardware offload rule commit and abort paths. 7) Early bail out in case device does not support for hardware offload. This adds a new interface to net/core/flow_offload.c to check if the flow indirect block list is empty. * git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf: netfilter: nf_tables: bail out early if hardware offload is not supported netfilter: nf_tables: memleak flow rule from commit path netfilter: nf_tables: release new hooks on unsupported flowtable flags netfilter: nf_tables: always initialize flowtable hook list in transaction netfilter: nf_tables: delete flowtable hooks via transaction list netfilter: nf_tables: use kfree_rcu(ptr, rcu) to release hooks in clean_net path netfilter: nat: really support inet nat without l3 address ==================== Link: https://lore.kernel.org/r/20220606212055.98300-1-pablo@netfilter.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2 parents cf67838 + 3a41c64 commit 91ffb08

8 files changed

Lines changed: 98 additions & 35 deletions

File tree

include/net/flow_offload.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,5 +612,6 @@ int flow_indr_dev_setup_offload(struct net_device *dev, struct Qdisc *sch,
612612
enum tc_setup_type type, void *data,
613613
struct flow_block_offload *bo,
614614
void (*cleanup)(struct flow_block_cb *block_cb));
615+
bool flow_indr_dev_exists(void);
615616

616617
#endif /* _NET_FLOW_OFFLOAD_H */

include/net/netfilter/nf_tables.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1090,7 +1090,6 @@ struct nft_stats {
10901090

10911091
struct nft_hook {
10921092
struct list_head list;
1093-
bool inactive;
10941093
struct nf_hook_ops ops;
10951094
struct rcu_head rcu;
10961095
};

include/net/netfilter/nf_tables_offload.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ int nft_flow_rule_offload_commit(struct net *net);
9292
NFT_OFFLOAD_MATCH(__key, __base, __field, __len, __reg) \
9393
memset(&(__reg)->mask, 0xff, (__reg)->len);
9494

95-
int nft_chain_offload_priority(struct nft_base_chain *basechain);
95+
bool nft_chain_offload_support(const struct nft_base_chain *basechain);
9696

9797
int nft_offload_init(void);
9898
void nft_offload_exit(void);

net/core/flow_offload.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,3 +595,9 @@ int flow_indr_dev_setup_offload(struct net_device *dev, struct Qdisc *sch,
595595
return (bo && list_empty(&bo->cb_list)) ? -EOPNOTSUPP : count;
596596
}
597597
EXPORT_SYMBOL(flow_indr_dev_setup_offload);
598+
599+
bool flow_indr_dev_exists(void)
600+
{
601+
return !list_empty(&flow_block_indr_dev_list);
602+
}
603+
EXPORT_SYMBOL(flow_indr_dev_exists);

net/netfilter/nf_tables_api.c

Lines changed: 23 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,7 @@ static int nft_trans_flowtable_add(struct nft_ctx *ctx, int msg_type,
544544
if (msg_type == NFT_MSG_NEWFLOWTABLE)
545545
nft_activate_next(ctx->net, flowtable);
546546

547+
INIT_LIST_HEAD(&nft_trans_flowtable_hooks(trans));
547548
nft_trans_flowtable(trans) = flowtable;
548549
nft_trans_commit_list_add_tail(ctx->net, trans);
549550

@@ -1914,7 +1915,6 @@ static struct nft_hook *nft_netdev_hook_alloc(struct net *net,
19141915
goto err_hook_dev;
19151916
}
19161917
hook->ops.dev = dev;
1917-
hook->inactive = false;
19181918

19191919
return hook;
19201920

@@ -2166,7 +2166,7 @@ static int nft_basechain_init(struct nft_base_chain *basechain, u8 family,
21662166
chain->flags |= NFT_CHAIN_BASE | flags;
21672167
basechain->policy = NF_ACCEPT;
21682168
if (chain->flags & NFT_CHAIN_HW_OFFLOAD &&
2169-
nft_chain_offload_priority(basechain) < 0)
2169+
!nft_chain_offload_support(basechain))
21702170
return -EOPNOTSUPP;
21712171

21722172
flow_block_init(&basechain->flow_block);
@@ -7332,7 +7332,7 @@ static void __nft_unregister_flowtable_net_hooks(struct net *net,
73327332
nf_unregister_net_hook(net, &hook->ops);
73337333
if (release_netdev) {
73347334
list_del(&hook->list);
7335-
kfree_rcu(hook);
7335+
kfree_rcu(hook, rcu);
73367336
}
73377337
}
73387338
}
@@ -7433,11 +7433,15 @@ static int nft_flowtable_update(struct nft_ctx *ctx, const struct nlmsghdr *nlh,
74337433

74347434
if (nla[NFTA_FLOWTABLE_FLAGS]) {
74357435
flags = ntohl(nla_get_be32(nla[NFTA_FLOWTABLE_FLAGS]));
7436-
if (flags & ~NFT_FLOWTABLE_MASK)
7437-
return -EOPNOTSUPP;
7436+
if (flags & ~NFT_FLOWTABLE_MASK) {
7437+
err = -EOPNOTSUPP;
7438+
goto err_flowtable_update_hook;
7439+
}
74387440
if ((flowtable->data.flags & NFT_FLOWTABLE_HW_OFFLOAD) ^
7439-
(flags & NFT_FLOWTABLE_HW_OFFLOAD))
7440-
return -EOPNOTSUPP;
7441+
(flags & NFT_FLOWTABLE_HW_OFFLOAD)) {
7442+
err = -EOPNOTSUPP;
7443+
goto err_flowtable_update_hook;
7444+
}
74417445
} else {
74427446
flags = flowtable->data.flags;
74437447
}
@@ -7618,6 +7622,7 @@ static int nft_delflowtable_hook(struct nft_ctx *ctx,
76187622
{
76197623
const struct nlattr * const *nla = ctx->nla;
76207624
struct nft_flowtable_hook flowtable_hook;
7625+
LIST_HEAD(flowtable_del_list);
76217626
struct nft_hook *this, *hook;
76227627
struct nft_trans *trans;
76237628
int err;
@@ -7633,7 +7638,7 @@ static int nft_delflowtable_hook(struct nft_ctx *ctx,
76337638
err = -ENOENT;
76347639
goto err_flowtable_del_hook;
76357640
}
7636-
hook->inactive = true;
7641+
list_move(&hook->list, &flowtable_del_list);
76377642
}
76387643

76397644
trans = nft_trans_alloc(ctx, NFT_MSG_DELFLOWTABLE,
@@ -7646,20 +7651,15 @@ static int nft_delflowtable_hook(struct nft_ctx *ctx,
76467651
nft_trans_flowtable(trans) = flowtable;
76477652
nft_trans_flowtable_update(trans) = true;
76487653
INIT_LIST_HEAD(&nft_trans_flowtable_hooks(trans));
7654+
list_splice(&flowtable_del_list, &nft_trans_flowtable_hooks(trans));
76497655
nft_flowtable_hook_release(&flowtable_hook);
76507656

76517657
nft_trans_commit_list_add_tail(ctx->net, trans);
76527658

76537659
return 0;
76547660

76557661
err_flowtable_del_hook:
7656-
list_for_each_entry(this, &flowtable_hook.list, list) {
7657-
hook = nft_hook_list_find(&flowtable->hook_list, this);
7658-
if (!hook)
7659-
break;
7660-
7661-
hook->inactive = false;
7662-
}
7662+
list_splice(&flowtable_del_list, &flowtable->hook_list);
76637663
nft_flowtable_hook_release(&flowtable_hook);
76647664

76657665
return err;
@@ -8329,6 +8329,9 @@ static void nft_commit_release(struct nft_trans *trans)
83298329
nf_tables_chain_destroy(&trans->ctx);
83308330
break;
83318331
case NFT_MSG_DELRULE:
8332+
if (trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD)
8333+
nft_flow_rule_destroy(nft_trans_flow_rule(trans));
8334+
83328335
nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
83338336
break;
83348337
case NFT_MSG_DELSET:
@@ -8563,17 +8566,6 @@ void nft_chain_del(struct nft_chain *chain)
85638566
list_del_rcu(&chain->list);
85648567
}
85658568

8566-
static void nft_flowtable_hooks_del(struct nft_flowtable *flowtable,
8567-
struct list_head *hook_list)
8568-
{
8569-
struct nft_hook *hook, *next;
8570-
8571-
list_for_each_entry_safe(hook, next, &flowtable->hook_list, list) {
8572-
if (hook->inactive)
8573-
list_move(&hook->list, hook_list);
8574-
}
8575-
}
8576-
85778569
static void nf_tables_module_autoload_cleanup(struct net *net)
85788570
{
85798571
struct nftables_pernet *nft_net = nft_pernet(net);
@@ -8828,6 +8820,9 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
88288820
nf_tables_rule_notify(&trans->ctx,
88298821
nft_trans_rule(trans),
88308822
NFT_MSG_NEWRULE);
8823+
if (trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD)
8824+
nft_flow_rule_destroy(nft_trans_flow_rule(trans));
8825+
88318826
nft_trans_destroy(trans);
88328827
break;
88338828
case NFT_MSG_DELRULE:
@@ -8918,8 +8913,6 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
89188913
break;
89198914
case NFT_MSG_DELFLOWTABLE:
89208915
if (nft_trans_flowtable_update(trans)) {
8921-
nft_flowtable_hooks_del(nft_trans_flowtable(trans),
8922-
&nft_trans_flowtable_hooks(trans));
89238916
nf_tables_flowtable_notify(&trans->ctx,
89248917
nft_trans_flowtable(trans),
89258918
&nft_trans_flowtable_hooks(trans),
@@ -9000,7 +8993,6 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
90008993
struct nftables_pernet *nft_net = nft_pernet(net);
90018994
struct nft_trans *trans, *next;
90028995
struct nft_trans_elem *te;
9003-
struct nft_hook *hook;
90048996

90058997
if (action == NFNL_ABORT_VALIDATE &&
90068998
nf_tables_validate(net) < 0)
@@ -9131,8 +9123,8 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
91319123
break;
91329124
case NFT_MSG_DELFLOWTABLE:
91339125
if (nft_trans_flowtable_update(trans)) {
9134-
list_for_each_entry(hook, &nft_trans_flowtable(trans)->hook_list, list)
9135-
hook->inactive = false;
9126+
list_splice(&nft_trans_flowtable_hooks(trans),
9127+
&nft_trans_flowtable(trans)->hook_list);
91369128
} else {
91379129
trans->ctx.table->use++;
91389130
nft_clear(trans->ctx.net, nft_trans_flowtable(trans));

net/netfilter/nf_tables_offload.c

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ static int nft_setup_cb_call(enum tc_setup_type type, void *type_data,
208208
return 0;
209209
}
210210

211-
int nft_chain_offload_priority(struct nft_base_chain *basechain)
211+
static int nft_chain_offload_priority(const struct nft_base_chain *basechain)
212212
{
213213
if (basechain->ops.priority <= 0 ||
214214
basechain->ops.priority > USHRT_MAX)
@@ -217,6 +217,27 @@ int nft_chain_offload_priority(struct nft_base_chain *basechain)
217217
return 0;
218218
}
219219

220+
bool nft_chain_offload_support(const struct nft_base_chain *basechain)
221+
{
222+
struct net_device *dev;
223+
struct nft_hook *hook;
224+
225+
if (nft_chain_offload_priority(basechain) < 0)
226+
return false;
227+
228+
list_for_each_entry(hook, &basechain->hook_list, list) {
229+
if (hook->ops.pf != NFPROTO_NETDEV ||
230+
hook->ops.hooknum != NF_NETDEV_INGRESS)
231+
return false;
232+
233+
dev = hook->ops.dev;
234+
if (!dev->netdev_ops->ndo_setup_tc && !flow_indr_dev_exists())
235+
return false;
236+
}
237+
238+
return true;
239+
}
240+
220241
static void nft_flow_cls_offload_setup(struct flow_cls_offload *cls_flow,
221242
const struct nft_base_chain *basechain,
222243
const struct nft_rule *rule,

net/netfilter/nft_nat.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,8 @@ static void nft_nat_inet_eval(const struct nft_expr *expr,
335335
{
336336
const struct nft_nat *priv = nft_expr_priv(expr);
337337

338-
if (priv->family == nft_pf(pkt))
338+
if (priv->family == nft_pf(pkt) ||
339+
priv->family == NFPROTO_INET)
339340
nft_nat_eval(expr, regs, pkt);
340341
}
341342

tools/testing/selftests/netfilter/nft_nat.sh

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,45 @@ EOF
374374
return $lret
375375
}
376376

377+
test_local_dnat_portonly()
378+
{
379+
local family=$1
380+
local daddr=$2
381+
local lret=0
382+
local sr_s
383+
local sr_r
384+
385+
ip netns exec "$ns0" nft -f /dev/stdin <<EOF
386+
table $family nat {
387+
chain output {
388+
type nat hook output priority 0; policy accept;
389+
meta l4proto tcp dnat to :2000
390+
391+
}
392+
}
393+
EOF
394+
if [ $? -ne 0 ]; then
395+
if [ $family = "inet" ];then
396+
echo "SKIP: inet port test"
397+
test_inet_nat=false
398+
return
399+
fi
400+
echo "SKIP: Could not add $family dnat hook"
401+
return
402+
fi
403+
404+
echo SERVER-$family | ip netns exec "$ns1" timeout 5 socat -u STDIN TCP-LISTEN:2000 &
405+
sc_s=$!
406+
407+
result=$(ip netns exec "$ns0" timeout 1 socat TCP:$daddr:2000 STDOUT)
408+
409+
if [ "$result" = "SERVER-inet" ];then
410+
echo "PASS: inet port rewrite without l3 address"
411+
else
412+
echo "ERROR: inet port rewrite"
413+
ret=1
414+
fi
415+
}
377416

378417
test_masquerade6()
379418
{
@@ -1148,6 +1187,10 @@ fi
11481187
reset_counters
11491188
test_local_dnat ip
11501189
test_local_dnat6 ip6
1190+
1191+
reset_counters
1192+
test_local_dnat_portonly inet 10.0.1.99
1193+
11511194
reset_counters
11521195
$test_inet_nat && test_local_dnat inet
11531196
$test_inet_nat && test_local_dnat6 inet

0 commit comments

Comments
 (0)