Commit 109bf4cf authored by David S. Miller's avatar David S. Miller

Merge tag 'nf-next-23-12-22' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next

Pablo Neira Ayuso says:

====================
netfilter pull request 23-12-22

The following patchset contains Netfilter updates for net-next:

1) Add locking for NFT_MSG_GETSETELEM_RESET requests, to address a
   race scenario with two concurrent processes running a dump-and-reset
   which exposes negative counters to userspace, from Phil Sutter.

2) Use GFP_KERNEL in pipapo GC, from Florian Westphal.

3) Reorder nf_flowtable struct members, place the read-mostly parts
   accessed by the datapath first. From Florian Westphal.

4) Set on dead flag for NFT_MSG_NEWSET in abort path,
   from Florian Westphal.

5) Support filtering zone in ctnetlink, from Felix Huettner.

6) Bail out if user tries to redefine an existing chain with different
   type in nf_tables.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 240436c0 aaba7ddc
...@@ -74,12 +74,13 @@ enum nf_flowtable_flags { ...@@ -74,12 +74,13 @@ enum nf_flowtable_flags {
}; };
struct nf_flowtable { struct nf_flowtable {
struct list_head list; unsigned int flags; /* readonly in datapath */
struct rhashtable rhashtable; int priority; /* control path (padding hole) */
int priority; struct rhashtable rhashtable; /* datapath, read-mostly members come first */
struct list_head list; /* slowpath parts */
const struct nf_flowtable_type *type; const struct nf_flowtable_type *type;
struct delayed_work gc_work; struct delayed_work gc_work;
unsigned int flags;
struct flow_block flow_block; struct flow_block flow_block;
struct rw_semaphore flow_block_lock; /* Guards flow_block */ struct rw_semaphore flow_block_lock; /* Guards flow_block */
possible_net_t net; possible_net_t net;
......
...@@ -992,13 +992,13 @@ ctnetlink_alloc_filter(const struct nlattr * const cda[], u8 family) ...@@ -992,13 +992,13 @@ ctnetlink_alloc_filter(const struct nlattr * const cda[], u8 family)
if (err) if (err)
goto err_filter; goto err_filter;
if (!cda[CTA_FILTER])
return filter;
err = ctnetlink_parse_zone(cda[CTA_ZONE], &filter->zone); err = ctnetlink_parse_zone(cda[CTA_ZONE], &filter->zone);
if (err < 0) if (err < 0)
goto err_filter; goto err_filter;
if (!cda[CTA_FILTER])
return filter;
err = ctnetlink_parse_filter(cda[CTA_FILTER], filter); err = ctnetlink_parse_filter(cda[CTA_FILTER], filter);
if (err < 0) if (err < 0)
goto err_filter; goto err_filter;
...@@ -1043,7 +1043,7 @@ ctnetlink_alloc_filter(const struct nlattr * const cda[], u8 family) ...@@ -1043,7 +1043,7 @@ ctnetlink_alloc_filter(const struct nlattr * const cda[], u8 family)
static bool ctnetlink_needs_filter(u8 family, const struct nlattr * const *cda) static bool ctnetlink_needs_filter(u8 family, const struct nlattr * const *cda)
{ {
return family || cda[CTA_MARK] || cda[CTA_FILTER] || cda[CTA_STATUS]; return family || cda[CTA_MARK] || cda[CTA_FILTER] || cda[CTA_STATUS] || cda[CTA_ZONE];
} }
static int ctnetlink_start(struct netlink_callback *cb) static int ctnetlink_start(struct netlink_callback *cb)
...@@ -1148,6 +1148,10 @@ static int ctnetlink_filter_match(struct nf_conn *ct, void *data) ...@@ -1148,6 +1148,10 @@ static int ctnetlink_filter_match(struct nf_conn *ct, void *data)
if (filter->family && nf_ct_l3num(ct) != filter->family) if (filter->family && nf_ct_l3num(ct) != filter->family)
goto ignore_entry; goto ignore_entry;
if (filter->zone.id != NF_CT_DEFAULT_ZONE_ID &&
!nf_ct_zone_equal_any(ct, &filter->zone))
goto ignore_entry;
if (filter->orig_flags) { if (filter->orig_flags) {
tuple = nf_ct_tuple(ct, IP_CT_DIR_ORIGINAL); tuple = nf_ct_tuple(ct, IP_CT_DIR_ORIGINAL);
if (!ctnetlink_filter_match_tuple(&filter->orig, tuple, if (!ctnetlink_filter_match_tuple(&filter->orig, tuple,
......
...@@ -2261,7 +2261,16 @@ static int nft_chain_parse_hook(struct net *net, ...@@ -2261,7 +2261,16 @@ static int nft_chain_parse_hook(struct net *net,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
type = basechain->type; if (nla[NFTA_CHAIN_TYPE]) {
type = __nf_tables_chain_type_lookup(nla[NFTA_CHAIN_TYPE],
family);
if (!type) {
NL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_TYPE]);
return -ENOENT;
}
} else {
type = basechain->type;
}
} }
if (!try_module_get(type->owner)) { if (!try_module_get(type->owner)) {
...@@ -5817,10 +5826,6 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -5817,10 +5826,6 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
nla_nest_end(skb, nest); nla_nest_end(skb, nest);
nlmsg_end(skb, nlh); nlmsg_end(skb, nlh);
if (dump_ctx->reset && args.iter.count > args.iter.skip)
audit_log_nft_set_reset(table, cb->seq,
args.iter.count - args.iter.skip);
rcu_read_unlock(); rcu_read_unlock();
if (args.iter.err && args.iter.err != -EMSGSIZE) if (args.iter.err && args.iter.err != -EMSGSIZE)
...@@ -5836,6 +5841,26 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -5836,6 +5841,26 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
return -ENOSPC; return -ENOSPC;
} }
static int nf_tables_dumpreset_set(struct sk_buff *skb,
struct netlink_callback *cb)
{
struct nftables_pernet *nft_net = nft_pernet(sock_net(skb->sk));
struct nft_set_dump_ctx *dump_ctx = cb->data;
int ret, skip = cb->args[0];
mutex_lock(&nft_net->commit_mutex);
ret = nf_tables_dump_set(skb, cb);
if (cb->args[0] > skip)
audit_log_nft_set_reset(dump_ctx->ctx.table, cb->seq,
cb->args[0] - skip);
mutex_unlock(&nft_net->commit_mutex);
return ret;
}
static int nf_tables_dump_set_start(struct netlink_callback *cb) static int nf_tables_dump_set_start(struct netlink_callback *cb)
{ {
struct nft_set_dump_ctx *dump_ctx = cb->data; struct nft_set_dump_ctx *dump_ctx = cb->data;
...@@ -5910,7 +5935,7 @@ static int nft_setelem_parse_flags(const struct nft_set *set, ...@@ -5910,7 +5935,7 @@ static int nft_setelem_parse_flags(const struct nft_set *set,
return 0; return 0;
} }
static int nft_setelem_parse_key(struct nft_ctx *ctx, struct nft_set *set, static int nft_setelem_parse_key(struct nft_ctx *ctx, const struct nft_set *set,
struct nft_data *key, struct nlattr *attr) struct nft_data *key, struct nlattr *attr)
{ {
struct nft_data_desc desc = { struct nft_data_desc desc = {
...@@ -5963,7 +5988,7 @@ static void *nft_setelem_catchall_get(const struct net *net, ...@@ -5963,7 +5988,7 @@ static void *nft_setelem_catchall_get(const struct net *net,
return priv; return priv;
} }
static int nft_setelem_get(struct nft_ctx *ctx, struct nft_set *set, static int nft_setelem_get(struct nft_ctx *ctx, const struct nft_set *set,
struct nft_set_elem *elem, u32 flags) struct nft_set_elem *elem, u32 flags)
{ {
void *priv; void *priv;
...@@ -5982,7 +6007,7 @@ static int nft_setelem_get(struct nft_ctx *ctx, struct nft_set *set, ...@@ -5982,7 +6007,7 @@ static int nft_setelem_get(struct nft_ctx *ctx, struct nft_set *set,
return 0; return 0;
} }
static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set, static int nft_get_set_elem(struct nft_ctx *ctx, const struct nft_set *set,
const struct nlattr *attr, bool reset) const struct nlattr *attr, bool reset)
{ {
struct nlattr *nla[NFTA_SET_ELEM_MAX + 1]; struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
...@@ -6039,21 +6064,18 @@ static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -6039,21 +6064,18 @@ static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set,
return err; return err;
} }
/* called with rcu_read_lock held */ static int nft_set_dump_ctx_init(struct nft_set_dump_ctx *dump_ctx,
static int nf_tables_getsetelem(struct sk_buff *skb, const struct sk_buff *skb,
const struct nfnl_info *info, const struct nfnl_info *info,
const struct nlattr * const nla[]) const struct nlattr * const nla[],
bool reset)
{ {
struct netlink_ext_ack *extack = info->extack; struct netlink_ext_ack *extack = info->extack;
u8 genmask = nft_genmask_cur(info->net); u8 genmask = nft_genmask_cur(info->net);
u8 family = info->nfmsg->nfgen_family; u8 family = info->nfmsg->nfgen_family;
int rem, err = 0, nelems = 0;
struct net *net = info->net; struct net *net = info->net;
struct nft_table *table; struct nft_table *table;
struct nft_set *set; struct nft_set *set;
struct nlattr *attr;
struct nft_ctx ctx;
bool reset = false;
table = nft_table_lookup(net, nla[NFTA_SET_ELEM_LIST_TABLE], family, table = nft_table_lookup(net, nla[NFTA_SET_ELEM_LIST_TABLE], family,
genmask, 0); genmask, 0);
...@@ -6068,10 +6090,22 @@ static int nf_tables_getsetelem(struct sk_buff *skb, ...@@ -6068,10 +6090,22 @@ static int nf_tables_getsetelem(struct sk_buff *skb,
return PTR_ERR(set); return PTR_ERR(set);
} }
nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla); nft_ctx_init(&dump_ctx->ctx, net, skb,
info->nlh, family, table, NULL, nla);
dump_ctx->set = set;
dump_ctx->reset = reset;
return 0;
}
if (NFNL_MSG_TYPE(info->nlh->nlmsg_type) == NFT_MSG_GETSETELEM_RESET) /* called with rcu_read_lock held */
reset = true; static int nf_tables_getsetelem(struct sk_buff *skb,
const struct nfnl_info *info,
const struct nlattr * const nla[])
{
struct netlink_ext_ack *extack = info->extack;
struct nft_set_dump_ctx dump_ctx;
struct nlattr *attr;
int rem, err = 0;
if (info->nlh->nlmsg_flags & NLM_F_DUMP) { if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = { struct netlink_dump_control c = {
...@@ -6080,12 +6114,55 @@ static int nf_tables_getsetelem(struct sk_buff *skb, ...@@ -6080,12 +6114,55 @@ static int nf_tables_getsetelem(struct sk_buff *skb,
.done = nf_tables_dump_set_done, .done = nf_tables_dump_set_done,
.module = THIS_MODULE, .module = THIS_MODULE,
}; };
struct nft_set_dump_ctx dump_ctx = {
.set = set, err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, false);
.ctx = ctx, if (err)
.reset = reset, return err;
c.data = &dump_ctx;
return nft_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
}
if (!nla[NFTA_SET_ELEM_LIST_ELEMENTS])
return -EINVAL;
err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, false);
if (err)
return err;
nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
err = nft_get_set_elem(&dump_ctx.ctx, dump_ctx.set, attr, false);
if (err < 0) {
NL_SET_BAD_ATTR(extack, attr);
break;
}
}
return err;
}
static int nf_tables_getsetelem_reset(struct sk_buff *skb,
const struct nfnl_info *info,
const struct nlattr * const nla[])
{
struct nftables_pernet *nft_net = nft_pernet(info->net);
struct netlink_ext_ack *extack = info->extack;
struct nft_set_dump_ctx dump_ctx;
int rem, err = 0, nelems = 0;
struct nlattr *attr;
if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = {
.start = nf_tables_dump_set_start,
.dump = nf_tables_dumpreset_set,
.done = nf_tables_dump_set_done,
.module = THIS_MODULE,
}; };
err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, true);
if (err)
return err;
c.data = &dump_ctx; c.data = &dump_ctx;
return nft_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c); return nft_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
} }
...@@ -6093,18 +6170,31 @@ static int nf_tables_getsetelem(struct sk_buff *skb, ...@@ -6093,18 +6170,31 @@ static int nf_tables_getsetelem(struct sk_buff *skb,
if (!nla[NFTA_SET_ELEM_LIST_ELEMENTS]) if (!nla[NFTA_SET_ELEM_LIST_ELEMENTS])
return -EINVAL; return -EINVAL;
if (!try_module_get(THIS_MODULE))
return -EINVAL;
rcu_read_unlock();
mutex_lock(&nft_net->commit_mutex);
rcu_read_lock();
err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, true);
if (err)
goto out_unlock;
nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) { nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
err = nft_get_set_elem(&ctx, set, attr, reset); err = nft_get_set_elem(&dump_ctx.ctx, dump_ctx.set, attr, true);
if (err < 0) { if (err < 0) {
NL_SET_BAD_ATTR(extack, attr); NL_SET_BAD_ATTR(extack, attr);
break; break;
} }
nelems++; nelems++;
} }
audit_log_nft_set_reset(dump_ctx.ctx.table, nft_net->base_seq, nelems);
if (reset) out_unlock:
audit_log_nft_set_reset(table, nft_pernet(net)->base_seq, rcu_read_unlock();
nelems); mutex_unlock(&nft_net->commit_mutex);
rcu_read_lock();
module_put(THIS_MODULE);
return err; return err;
} }
...@@ -9078,7 +9168,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { ...@@ -9078,7 +9168,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
.policy = nft_set_elem_list_policy, .policy = nft_set_elem_list_policy,
}, },
[NFT_MSG_GETSETELEM_RESET] = { [NFT_MSG_GETSETELEM_RESET] = {
.call = nf_tables_getsetelem, .call = nf_tables_getsetelem_reset,
.type = NFNL_CB_RCU, .type = NFNL_CB_RCU,
.attr_count = NFTA_SET_ELEM_LIST_MAX, .attr_count = NFTA_SET_ELEM_LIST_MAX,
.policy = nft_set_elem_list_policy, .policy = nft_set_elem_list_policy,
...@@ -10383,6 +10473,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) ...@@ -10383,6 +10473,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
nft_trans_destroy(trans); nft_trans_destroy(trans);
break; break;
} }
nft_trans_set(trans)->dead = 1;
list_del_rcu(&nft_trans_set(trans)->list); list_del_rcu(&nft_trans_set(trans)->list);
break; break;
case NFT_MSG_DELSET: case NFT_MSG_DELSET:
......
...@@ -1597,7 +1597,7 @@ static void pipapo_gc(struct nft_set *set, struct nft_pipapo_match *m) ...@@ -1597,7 +1597,7 @@ static void pipapo_gc(struct nft_set *set, struct nft_pipapo_match *m)
if (nft_set_elem_expired(&e->ext)) { if (nft_set_elem_expired(&e->ext)) {
priv->dirty = true; priv->dirty = true;
gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC); gc = nft_trans_gc_queue_sync(gc, GFP_KERNEL);
if (!gc) if (!gc)
return; return;
......
...@@ -2,3 +2,5 @@ ...@@ -2,3 +2,5 @@
nf-queue nf-queue
connect_close connect_close
audit_logread audit_logread
conntrack_dump_flush
sctp_collision
...@@ -14,6 +14,7 @@ HOSTPKG_CONFIG := pkg-config ...@@ -14,6 +14,7 @@ HOSTPKG_CONFIG := pkg-config
CFLAGS += $(shell $(HOSTPKG_CONFIG) --cflags libmnl 2>/dev/null) CFLAGS += $(shell $(HOSTPKG_CONFIG) --cflags libmnl 2>/dev/null)
LDLIBS += $(shell $(HOSTPKG_CONFIG) --libs libmnl 2>/dev/null || echo -lmnl) LDLIBS += $(shell $(HOSTPKG_CONFIG) --libs libmnl 2>/dev/null || echo -lmnl)
TEST_GEN_FILES = nf-queue connect_close audit_logread sctp_collision TEST_GEN_FILES = nf-queue connect_close audit_logread sctp_collision \
conntrack_dump_flush
include ../lib.mk include ../lib.mk
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment