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 {
};
struct nf_flowtable {
struct list_head list;
struct rhashtable rhashtable;
int priority;
unsigned int flags; /* readonly in datapath */
int priority; /* control path (padding hole) */
struct rhashtable rhashtable; /* datapath, read-mostly members come first */
struct list_head list; /* slowpath parts */
const struct nf_flowtable_type *type;
struct delayed_work gc_work;
unsigned int flags;
struct flow_block flow_block;
struct rw_semaphore flow_block_lock; /* Guards flow_block */
possible_net_t net;
......
......@@ -992,13 +992,13 @@ ctnetlink_alloc_filter(const struct nlattr * const cda[], u8 family)
if (err)
goto err_filter;
if (!cda[CTA_FILTER])
return filter;
err = ctnetlink_parse_zone(cda[CTA_ZONE], &filter->zone);
if (err < 0)
goto err_filter;
if (!cda[CTA_FILTER])
return filter;
err = ctnetlink_parse_filter(cda[CTA_FILTER], filter);
if (err < 0)
goto err_filter;
......@@ -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)
{
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)
......@@ -1148,6 +1148,10 @@ static int ctnetlink_filter_match(struct nf_conn *ct, void *data)
if (filter->family && nf_ct_l3num(ct) != filter->family)
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) {
tuple = nf_ct_tuple(ct, IP_CT_DIR_ORIGINAL);
if (!ctnetlink_filter_match_tuple(&filter->orig, tuple,
......
......@@ -2261,8 +2261,17 @@ static int nft_chain_parse_hook(struct net *net,
return -EOPNOTSUPP;
}
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 (nla[NFTA_CHAIN_TYPE])
......@@ -5817,10 +5826,6 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
nla_nest_end(skb, nest);
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();
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)
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)
{
struct nft_set_dump_ctx *dump_ctx = cb->data;
......@@ -5910,7 +5935,7 @@ static int nft_setelem_parse_flags(const struct nft_set *set,
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_desc desc = {
......@@ -5963,7 +5988,7 @@ static void *nft_setelem_catchall_get(const struct net *net,
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)
{
void *priv;
......@@ -5982,7 +6007,7 @@ static int nft_setelem_get(struct nft_ctx *ctx, struct nft_set *set,
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)
{
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,
return err;
}
/* called with rcu_read_lock held */
static int nf_tables_getsetelem(struct sk_buff *skb,
static int nft_set_dump_ctx_init(struct nft_set_dump_ctx *dump_ctx,
const struct sk_buff *skb,
const struct nfnl_info *info,
const struct nlattr * const nla[])
const struct nlattr * const nla[],
bool reset)
{
struct netlink_ext_ack *extack = info->extack;
u8 genmask = nft_genmask_cur(info->net);
u8 family = info->nfmsg->nfgen_family;
int rem, err = 0, nelems = 0;
struct net *net = info->net;
struct nft_table *table;
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,
genmask, 0);
......@@ -6068,10 +6090,22 @@ static int nf_tables_getsetelem(struct sk_buff *skb,
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)
reset = true;
/* called with rcu_read_lock held */
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) {
struct netlink_dump_control c = {
......@@ -6080,12 +6114,55 @@ static int nf_tables_getsetelem(struct sk_buff *skb,
.done = nf_tables_dump_set_done,
.module = THIS_MODULE,
};
struct nft_set_dump_ctx dump_ctx = {
.set = set,
.ctx = ctx,
.reset = reset,
err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, false);
if (err)
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;
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,
if (!nla[NFTA_SET_ELEM_LIST_ELEMENTS])
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) {
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) {
NL_SET_BAD_ATTR(extack, attr);
break;
}
nelems++;
}
audit_log_nft_set_reset(dump_ctx.ctx.table, nft_net->base_seq, nelems);
if (reset)
audit_log_nft_set_reset(table, nft_pernet(net)->base_seq,
nelems);
out_unlock:
rcu_read_unlock();
mutex_unlock(&nft_net->commit_mutex);
rcu_read_lock();
module_put(THIS_MODULE);
return err;
}
......@@ -9078,7 +9168,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
.policy = nft_set_elem_list_policy,
},
[NFT_MSG_GETSETELEM_RESET] = {
.call = nf_tables_getsetelem,
.call = nf_tables_getsetelem_reset,
.type = NFNL_CB_RCU,
.attr_count = NFTA_SET_ELEM_LIST_MAX,
.policy = nft_set_elem_list_policy,
......@@ -10383,6 +10473,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
nft_trans_destroy(trans);
break;
}
nft_trans_set(trans)->dead = 1;
list_del_rcu(&nft_trans_set(trans)->list);
break;
case NFT_MSG_DELSET:
......
......@@ -1597,7 +1597,7 @@ static void pipapo_gc(struct nft_set *set, struct nft_pipapo_match *m)
if (nft_set_elem_expired(&e->ext)) {
priv->dirty = true;
gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC);
gc = nft_trans_gc_queue_sync(gc, GFP_KERNEL);
if (!gc)
return;
......
......@@ -2,3 +2,5 @@
nf-queue
connect_close
audit_logread
conntrack_dump_flush
sctp_collision
......@@ -14,6 +14,7 @@ HOSTPKG_CONFIG := pkg-config
CFLAGS += $(shell $(HOSTPKG_CONFIG) --cflags libmnl 2>/dev/null)
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
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