Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf

Pablo Neira Ayuso says:

====================
Netfilter fixes for net

The following patchset contains Netfilter fixes for net:

1) Flush the cleanup xtables worker to make sure destructors
   have completed, from Florian Westphal.

2) iifgroup is matching erroneously, also from Florian.

3) Add selftest for meta interface matching, from Florian Westphal.

4) Move nf_ct_offload_timeout() to header, from Roi Dayan.

5) Call nf_ct_offload_timeout() from flow_offload_add() to
   make sure garbage collection does not evict offloaded flow,
   from Roi Dayan.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2020-08-04 13:32:39 -07:00
commit ee895a30ef
9 changed files with 182 additions and 20 deletions

View File

@ -279,6 +279,18 @@ static inline bool nf_ct_should_gc(const struct nf_conn *ct)
!nf_ct_is_dying(ct);
}
#define NF_CT_DAY (86400 * HZ)
/* Set an arbitrary timeout large enough not to ever expire, this save
* us a check for the IPS_OFFLOAD_BIT from the packet path via
* nf_ct_is_expired().
*/
static inline void nf_ct_offload_timeout(struct nf_conn *ct)
{
if (nf_ct_expires(ct) < NF_CT_DAY / 2)
ct->timeout = nfct_time_stamp + NF_CT_DAY;
}
struct kernel_param;
int nf_conntrack_set_hashsize(const char *val, const struct kernel_param *kp);

View File

@ -1507,4 +1507,6 @@ void nft_chain_filter_fini(void);
void __init nft_chain_route_init(void);
void nft_chain_route_fini(void);
void nf_tables_trans_destroy_flush_work(void);
#endif /* _NET_NF_TABLES_H */

View File

@ -1344,18 +1344,6 @@ static bool gc_worker_can_early_drop(const struct nf_conn *ct)
return false;
}
#define DAY (86400 * HZ)
/* Set an arbitrary timeout large enough not to ever expire, this save
* us a check for the IPS_OFFLOAD_BIT from the packet path via
* nf_ct_is_expired().
*/
static void nf_ct_offload_timeout(struct nf_conn *ct)
{
if (nf_ct_expires(ct) < DAY / 2)
ct->timeout = nfct_time_stamp + DAY;
}
static void gc_worker(struct work_struct *work)
{
unsigned int min_interval = max(HZ / GC_MAX_BUCKETS_DIV, 1u);

View File

@ -243,6 +243,8 @@ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow)
return err;
}
nf_ct_offload_timeout(flow->ct);
if (nf_flowtable_hw_offload(flow_table)) {
__set_bit(NF_FLOW_HW, &flow->flags);
nf_flow_offload_add(flow_table, flow);

View File

@ -7390,6 +7390,12 @@ static void nf_tables_trans_destroy_work(struct work_struct *w)
}
}
void nf_tables_trans_destroy_flush_work(void)
{
flush_work(&trans_destroy_work);
}
EXPORT_SYMBOL_GPL(nf_tables_trans_destroy_flush_work);
static int nf_tables_commit_chain_prepare(struct net *net, struct nft_chain *chain)
{
struct nft_rule *rule;
@ -7572,9 +7578,9 @@ static void nf_tables_commit_release(struct net *net)
spin_unlock(&nf_tables_destroy_list_lock);
nf_tables_module_autoload_cleanup(net);
mutex_unlock(&net->nft.commit_mutex);
schedule_work(&trans_destroy_work);
mutex_unlock(&net->nft.commit_mutex);
}
static int nf_tables_commit(struct net *net, struct sk_buff *skb)

View File

@ -27,6 +27,8 @@ struct nft_xt_match_priv {
void *info;
};
static refcount_t nft_compat_pending_destroy = REFCOUNT_INIT(1);
static int nft_compat_chain_validate_dependency(const struct nft_ctx *ctx,
const char *tablename)
{
@ -236,6 +238,15 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv);
/* xtables matches or targets can have side effects, e.g.
* creation/destruction of /proc files.
* The xt ->destroy functions are run asynchronously from
* work queue. If we have pending invocations we thus
* need to wait for those to finish.
*/
if (refcount_read(&nft_compat_pending_destroy) > 1)
nf_tables_trans_destroy_flush_work();
ret = xt_check_target(&par, size, proto, inv);
if (ret < 0)
return ret;
@ -247,6 +258,13 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
return 0;
}
static void __nft_mt_tg_destroy(struct module *me, const struct nft_expr *expr)
{
refcount_dec(&nft_compat_pending_destroy);
module_put(me);
kfree(expr->ops);
}
static void
nft_target_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
{
@ -262,8 +280,7 @@ nft_target_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
if (par.target->destroy != NULL)
par.target->destroy(&par);
module_put(me);
kfree(expr->ops);
__nft_mt_tg_destroy(me, expr);
}
static int nft_extension_dump_info(struct sk_buff *skb, int attr,
@ -494,8 +511,7 @@ __nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr,
if (par.match->destroy != NULL)
par.match->destroy(&par);
module_put(me);
kfree(expr->ops);
__nft_mt_tg_destroy(me, expr);
}
static void
@ -700,6 +716,14 @@ static const struct nfnetlink_subsystem nfnl_compat_subsys = {
static struct nft_expr_type nft_match_type;
static void nft_mt_tg_deactivate(const struct nft_ctx *ctx,
const struct nft_expr *expr,
enum nft_trans_phase phase)
{
if (phase == NFT_TRANS_COMMIT)
refcount_inc(&nft_compat_pending_destroy);
}
static const struct nft_expr_ops *
nft_match_select_ops(const struct nft_ctx *ctx,
const struct nlattr * const tb[])
@ -738,6 +762,7 @@ nft_match_select_ops(const struct nft_ctx *ctx,
ops->type = &nft_match_type;
ops->eval = nft_match_eval;
ops->init = nft_match_init;
ops->deactivate = nft_mt_tg_deactivate,
ops->destroy = nft_match_destroy;
ops->dump = nft_match_dump;
ops->validate = nft_match_validate;
@ -828,6 +853,7 @@ nft_target_select_ops(const struct nft_ctx *ctx,
ops->size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize));
ops->init = nft_target_init;
ops->destroy = nft_target_destroy;
ops->deactivate = nft_mt_tg_deactivate,
ops->dump = nft_target_dump;
ops->validate = nft_target_validate;
ops->data = target;
@ -891,6 +917,8 @@ static void __exit nft_compat_module_exit(void)
nfnetlink_subsys_unregister(&nfnl_compat_subsys);
nft_unregister_expr(&nft_target_type);
nft_unregister_expr(&nft_match_type);
WARN_ON_ONCE(refcount_read(&nft_compat_pending_destroy) != 1);
}
MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_NFT_COMPAT);

View File

@ -253,7 +253,7 @@ static bool nft_meta_get_eval_ifname(enum nft_meta_keys key, u32 *dest,
return false;
break;
case NFT_META_IIFGROUP:
if (!nft_meta_store_ifgroup(dest, nft_out(pkt)))
if (!nft_meta_store_ifgroup(dest, nft_in(pkt)))
return false;
break;
case NFT_META_OIFGROUP:

View File

@ -4,7 +4,7 @@
TEST_PROGS := nft_trans_stress.sh nft_nat.sh bridge_brouter.sh \
conntrack_icmp_related.sh nft_flowtable.sh ipvs.sh \
nft_concat_range.sh nft_conntrack_helper.sh \
nft_queue.sh
nft_queue.sh nft_meta.sh
LDLIBS = -lmnl
TEST_GEN_FILES = nf-queue

View File

@ -0,0 +1,124 @@
#!/bin/bash
# check iif/iifname/oifgroup/iiftype match.
# Kselftest framework requirement - SKIP code is 4.
ksft_skip=4
sfx=$(mktemp -u "XXXXXXXX")
ns0="ns0-$sfx"
nft --version > /dev/null 2>&1
if [ $? -ne 0 ];then
echo "SKIP: Could not run test without nft tool"
exit $ksft_skip
fi
cleanup()
{
ip netns del "$ns0"
}
ip netns add "$ns0"
ip -net "$ns0" link set lo up
ip -net "$ns0" addr add 127.0.0.1 dev lo
trap cleanup EXIT
ip netns exec "$ns0" nft -f /dev/stdin <<EOF
table inet filter {
counter iifcount {}
counter iifnamecount {}
counter iifgroupcount {}
counter iiftypecount {}
counter infproto4count {}
counter il4protocounter {}
counter imarkcounter {}
counter oifcount {}
counter oifnamecount {}
counter oifgroupcount {}
counter oiftypecount {}
counter onfproto4count {}
counter ol4protocounter {}
counter oskuidcounter {}
counter oskgidcounter {}
counter omarkcounter {}
chain input {
type filter hook input priority 0; policy accept;
meta iif lo counter name "iifcount"
meta iifname "lo" counter name "iifnamecount"
meta iifgroup "default" counter name "iifgroupcount"
meta iiftype "loopback" counter name "iiftypecount"
meta nfproto ipv4 counter name "infproto4count"
meta l4proto icmp counter name "il4protocounter"
meta mark 42 counter name "imarkcounter"
}
chain output {
type filter hook output priority 0; policy accept;
meta oif lo counter name "oifcount" counter
meta oifname "lo" counter name "oifnamecount"
meta oifgroup "default" counter name "oifgroupcount"
meta oiftype "loopback" counter name "oiftypecount"
meta nfproto ipv4 counter name "onfproto4count"
meta l4proto icmp counter name "ol4protocounter"
meta skuid 0 counter name "oskuidcounter"
meta skgid 0 counter name "oskgidcounter"
meta mark 42 counter name "omarkcounter"
}
}
EOF
if [ $? -ne 0 ]; then
echo "SKIP: Could not add test ruleset"
exit $ksft_skip
fi
ret=0
check_one_counter()
{
local cname="$1"
local want="packets $2"
local verbose="$3"
cnt=$(ip netns exec "$ns0" nft list counter inet filter $cname | grep -q "$want")
if [ $? -ne 0 ];then
echo "FAIL: $cname, want \"$want\", got"
ret=1
ip netns exec "$ns0" nft list counter inet filter $counter
fi
}
check_lo_counters()
{
local want="$1"
local verbose="$2"
local counter
for counter in iifcount iifnamecount iifgroupcount iiftypecount infproto4count \
oifcount oifnamecount oifgroupcount oiftypecount onfproto4count \
il4protocounter \
ol4protocounter \
; do
check_one_counter "$counter" "$want" "$verbose"
done
}
check_lo_counters "0" false
ip netns exec "$ns0" ping -q -c 1 127.0.0.1 -m 42 > /dev/null
check_lo_counters "2" true
check_one_counter oskuidcounter "1" true
check_one_counter oskgidcounter "1" true
check_one_counter imarkcounter "1" true
check_one_counter omarkcounter "1" true
if [ $ret -eq 0 ];then
echo "OK: nftables meta iif/oif counters at expected values"
fi
exit $ret