|
@@ -3367,35 +3367,50 @@ static int nf_tables_dump_setelem(const struct nft_ctx *ctx,
|
|
return nf_tables_fill_setelem(args->skb, set, elem);
|
|
return nf_tables_fill_setelem(args->skb, set, elem);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+struct nft_set_dump_ctx {
|
|
|
|
+ const struct nft_set *set;
|
|
|
|
+ struct nft_ctx ctx;
|
|
|
|
+};
|
|
|
|
+
|
|
static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
|
|
static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
|
|
{
|
|
{
|
|
|
|
+ struct nft_set_dump_ctx *dump_ctx = cb->data;
|
|
struct net *net = sock_net(skb->sk);
|
|
struct net *net = sock_net(skb->sk);
|
|
- u8 genmask = nft_genmask_cur(net);
|
|
|
|
|
|
+ struct nft_af_info *afi;
|
|
|
|
+ struct nft_table *table;
|
|
struct nft_set *set;
|
|
struct nft_set *set;
|
|
struct nft_set_dump_args args;
|
|
struct nft_set_dump_args args;
|
|
- struct nft_ctx ctx;
|
|
|
|
- struct nlattr *nla[NFTA_SET_ELEM_LIST_MAX + 1];
|
|
|
|
|
|
+ bool set_found = false;
|
|
struct nfgenmsg *nfmsg;
|
|
struct nfgenmsg *nfmsg;
|
|
struct nlmsghdr *nlh;
|
|
struct nlmsghdr *nlh;
|
|
struct nlattr *nest;
|
|
struct nlattr *nest;
|
|
u32 portid, seq;
|
|
u32 portid, seq;
|
|
- int event, err;
|
|
|
|
|
|
+ int event;
|
|
|
|
|
|
- err = nlmsg_parse(cb->nlh, sizeof(struct nfgenmsg), nla,
|
|
|
|
- NFTA_SET_ELEM_LIST_MAX, nft_set_elem_list_policy,
|
|
|
|
- NULL);
|
|
|
|
- if (err < 0)
|
|
|
|
- return err;
|
|
|
|
|
|
+ rcu_read_lock();
|
|
|
|
+ list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
|
|
|
|
+ if (afi != dump_ctx->ctx.afi)
|
|
|
|
+ continue;
|
|
|
|
|
|
- err = nft_ctx_init_from_elemattr(&ctx, net, cb->skb, cb->nlh,
|
|
|
|
- (void *)nla, genmask);
|
|
|
|
- if (err < 0)
|
|
|
|
- return err;
|
|
|
|
|
|
+ list_for_each_entry_rcu(table, &afi->tables, list) {
|
|
|
|
+ if (table != dump_ctx->ctx.table)
|
|
|
|
+ continue;
|
|
|
|
|
|
- set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET],
|
|
|
|
- genmask);
|
|
|
|
- if (IS_ERR(set))
|
|
|
|
- return PTR_ERR(set);
|
|
|
|
|
|
+ list_for_each_entry_rcu(set, &table->sets, list) {
|
|
|
|
+ if (set == dump_ctx->set) {
|
|
|
|
+ set_found = true;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!set_found) {
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
+ return -ENOENT;
|
|
|
|
+ }
|
|
|
|
|
|
event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWSETELEM);
|
|
event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWSETELEM);
|
|
portid = NETLINK_CB(cb->skb).portid;
|
|
portid = NETLINK_CB(cb->skb).portid;
|
|
@@ -3407,11 +3422,11 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
|
|
|
|
nfmsg = nlmsg_data(nlh);
|
|
nfmsg = nlmsg_data(nlh);
|
|
- nfmsg->nfgen_family = ctx.afi->family;
|
|
|
|
|
|
+ nfmsg->nfgen_family = afi->family;
|
|
nfmsg->version = NFNETLINK_V0;
|
|
nfmsg->version = NFNETLINK_V0;
|
|
- nfmsg->res_id = htons(ctx.net->nft.base_seq & 0xffff);
|
|
|
|
|
|
+ nfmsg->res_id = htons(net->nft.base_seq & 0xffff);
|
|
|
|
|
|
- if (nla_put_string(skb, NFTA_SET_ELEM_LIST_TABLE, ctx.table->name))
|
|
|
|
|
|
+ if (nla_put_string(skb, NFTA_SET_ELEM_LIST_TABLE, table->name))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
if (nla_put_string(skb, NFTA_SET_ELEM_LIST_SET, set->name))
|
|
if (nla_put_string(skb, NFTA_SET_ELEM_LIST_SET, set->name))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
@@ -3422,12 +3437,13 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
|
|
|
|
|
|
args.cb = cb;
|
|
args.cb = cb;
|
|
args.skb = skb;
|
|
args.skb = skb;
|
|
- args.iter.genmask = nft_genmask_cur(ctx.net);
|
|
|
|
|
|
+ args.iter.genmask = nft_genmask_cur(net);
|
|
args.iter.skip = cb->args[0];
|
|
args.iter.skip = cb->args[0];
|
|
args.iter.count = 0;
|
|
args.iter.count = 0;
|
|
args.iter.err = 0;
|
|
args.iter.err = 0;
|
|
args.iter.fn = nf_tables_dump_setelem;
|
|
args.iter.fn = nf_tables_dump_setelem;
|
|
- set->ops->walk(&ctx, set, &args.iter);
|
|
|
|
|
|
+ set->ops->walk(&dump_ctx->ctx, set, &args.iter);
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
|
|
nla_nest_end(skb, nest);
|
|
nla_nest_end(skb, nest);
|
|
nlmsg_end(skb, nlh);
|
|
nlmsg_end(skb, nlh);
|
|
@@ -3441,9 +3457,16 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
|
|
return skb->len;
|
|
return skb->len;
|
|
|
|
|
|
nla_put_failure:
|
|
nla_put_failure:
|
|
|
|
+ rcu_read_unlock();
|
|
return -ENOSPC;
|
|
return -ENOSPC;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int nf_tables_dump_set_done(struct netlink_callback *cb)
|
|
|
|
+{
|
|
|
|
+ kfree(cb->data);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
static int nf_tables_getsetelem(struct net *net, struct sock *nlsk,
|
|
static int nf_tables_getsetelem(struct net *net, struct sock *nlsk,
|
|
struct sk_buff *skb, const struct nlmsghdr *nlh,
|
|
struct sk_buff *skb, const struct nlmsghdr *nlh,
|
|
const struct nlattr * const nla[])
|
|
const struct nlattr * const nla[])
|
|
@@ -3465,7 +3488,18 @@ static int nf_tables_getsetelem(struct net *net, struct sock *nlsk,
|
|
if (nlh->nlmsg_flags & NLM_F_DUMP) {
|
|
if (nlh->nlmsg_flags & NLM_F_DUMP) {
|
|
struct netlink_dump_control c = {
|
|
struct netlink_dump_control c = {
|
|
.dump = nf_tables_dump_set,
|
|
.dump = nf_tables_dump_set,
|
|
|
|
+ .done = nf_tables_dump_set_done,
|
|
};
|
|
};
|
|
|
|
+ struct nft_set_dump_ctx *dump_ctx;
|
|
|
|
+
|
|
|
|
+ dump_ctx = kmalloc(sizeof(*dump_ctx), GFP_KERNEL);
|
|
|
|
+ if (!dump_ctx)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ dump_ctx->set = set;
|
|
|
|
+ dump_ctx->ctx = ctx;
|
|
|
|
+
|
|
|
|
+ c.data = dump_ctx;
|
|
return netlink_dump_start(nlsk, skb, nlh, &c);
|
|
return netlink_dump_start(nlsk, skb, nlh, &c);
|
|
}
|
|
}
|
|
return -EOPNOTSUPP;
|
|
return -EOPNOTSUPP;
|
|
@@ -3593,9 +3627,9 @@ void nft_set_elem_destroy(const struct nft_set *set, void *elem,
|
|
{
|
|
{
|
|
struct nft_set_ext *ext = nft_set_elem_ext(set, elem);
|
|
struct nft_set_ext *ext = nft_set_elem_ext(set, elem);
|
|
|
|
|
|
- nft_data_uninit(nft_set_ext_key(ext), NFT_DATA_VALUE);
|
|
|
|
|
|
+ nft_data_release(nft_set_ext_key(ext), NFT_DATA_VALUE);
|
|
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
|
|
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
|
|
- nft_data_uninit(nft_set_ext_data(ext), set->dtype);
|
|
|
|
|
|
+ nft_data_release(nft_set_ext_data(ext), set->dtype);
|
|
if (destroy_expr && nft_set_ext_exists(ext, NFT_SET_EXT_EXPR))
|
|
if (destroy_expr && nft_set_ext_exists(ext, NFT_SET_EXT_EXPR))
|
|
nf_tables_expr_destroy(NULL, nft_set_ext_expr(ext));
|
|
nf_tables_expr_destroy(NULL, nft_set_ext_expr(ext));
|
|
if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF))
|
|
if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF))
|
|
@@ -3604,6 +3638,18 @@ void nft_set_elem_destroy(const struct nft_set *set, void *elem,
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(nft_set_elem_destroy);
|
|
EXPORT_SYMBOL_GPL(nft_set_elem_destroy);
|
|
|
|
|
|
|
|
+/* Only called from commit path, nft_set_elem_deactivate() already deals with
|
|
|
|
+ * the refcounting from the preparation phase.
|
|
|
|
+ */
|
|
|
|
+static void nf_tables_set_elem_destroy(const struct nft_set *set, void *elem)
|
|
|
|
+{
|
|
|
|
+ struct nft_set_ext *ext = nft_set_elem_ext(set, elem);
|
|
|
|
+
|
|
|
|
+ if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPR))
|
|
|
|
+ nf_tables_expr_destroy(NULL, nft_set_ext_expr(ext));
|
|
|
|
+ kfree(elem);
|
|
|
|
+}
|
|
|
|
+
|
|
static int nft_setelem_parse_flags(const struct nft_set *set,
|
|
static int nft_setelem_parse_flags(const struct nft_set *set,
|
|
const struct nlattr *attr, u32 *flags)
|
|
const struct nlattr *attr, u32 *flags)
|
|
{
|
|
{
|
|
@@ -3815,9 +3861,9 @@ err4:
|
|
kfree(elem.priv);
|
|
kfree(elem.priv);
|
|
err3:
|
|
err3:
|
|
if (nla[NFTA_SET_ELEM_DATA] != NULL)
|
|
if (nla[NFTA_SET_ELEM_DATA] != NULL)
|
|
- nft_data_uninit(&data, d2.type);
|
|
|
|
|
|
+ nft_data_release(&data, d2.type);
|
|
err2:
|
|
err2:
|
|
- nft_data_uninit(&elem.key.val, d1.type);
|
|
|
|
|
|
+ nft_data_release(&elem.key.val, d1.type);
|
|
err1:
|
|
err1:
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
@@ -3862,6 +3908,53 @@ static int nf_tables_newsetelem(struct net *net, struct sock *nlsk,
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * nft_data_hold - hold a nft_data item
|
|
|
|
+ *
|
|
|
|
+ * @data: struct nft_data to release
|
|
|
|
+ * @type: type of data
|
|
|
|
+ *
|
|
|
|
+ * Hold a nft_data item. NFT_DATA_VALUE types can be silently discarded,
|
|
|
|
+ * NFT_DATA_VERDICT bumps the reference to chains in case of NFT_JUMP and
|
|
|
|
+ * NFT_GOTO verdicts. This function must be called on active data objects
|
|
|
|
+ * from the second phase of the commit protocol.
|
|
|
|
+ */
|
|
|
|
+static void nft_data_hold(const struct nft_data *data, enum nft_data_types type)
|
|
|
|
+{
|
|
|
|
+ if (type == NFT_DATA_VERDICT) {
|
|
|
|
+ switch (data->verdict.code) {
|
|
|
|
+ case NFT_JUMP:
|
|
|
|
+ case NFT_GOTO:
|
|
|
|
+ data->verdict.chain->use++;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void nft_set_elem_activate(const struct net *net,
|
|
|
|
+ const struct nft_set *set,
|
|
|
|
+ struct nft_set_elem *elem)
|
|
|
|
+{
|
|
|
|
+ const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
|
|
|
|
+
|
|
|
|
+ if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
|
|
|
|
+ nft_data_hold(nft_set_ext_data(ext), set->dtype);
|
|
|
|
+ if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF))
|
|
|
|
+ (*nft_set_ext_obj(ext))->use++;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void nft_set_elem_deactivate(const struct net *net,
|
|
|
|
+ const struct nft_set *set,
|
|
|
|
+ struct nft_set_elem *elem)
|
|
|
|
+{
|
|
|
|
+ const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
|
|
|
|
+
|
|
|
|
+ if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
|
|
|
|
+ nft_data_release(nft_set_ext_data(ext), set->dtype);
|
|
|
|
+ if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF))
|
|
|
|
+ (*nft_set_ext_obj(ext))->use--;
|
|
|
|
+}
|
|
|
|
+
|
|
static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
|
|
static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
|
|
const struct nlattr *attr)
|
|
const struct nlattr *attr)
|
|
{
|
|
{
|
|
@@ -3927,6 +4020,8 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
|
|
kfree(elem.priv);
|
|
kfree(elem.priv);
|
|
elem.priv = priv;
|
|
elem.priv = priv;
|
|
|
|
|
|
|
|
+ nft_set_elem_deactivate(ctx->net, set, &elem);
|
|
|
|
+
|
|
nft_trans_elem(trans) = elem;
|
|
nft_trans_elem(trans) = elem;
|
|
list_add_tail(&trans->list, &ctx->net->nft.commit_list);
|
|
list_add_tail(&trans->list, &ctx->net->nft.commit_list);
|
|
return 0;
|
|
return 0;
|
|
@@ -3936,7 +4031,7 @@ err4:
|
|
err3:
|
|
err3:
|
|
kfree(elem.priv);
|
|
kfree(elem.priv);
|
|
err2:
|
|
err2:
|
|
- nft_data_uninit(&elem.key.val, desc.type);
|
|
|
|
|
|
+ nft_data_release(&elem.key.val, desc.type);
|
|
err1:
|
|
err1:
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
@@ -4743,8 +4838,8 @@ static void nf_tables_commit_release(struct nft_trans *trans)
|
|
nft_set_destroy(nft_trans_set(trans));
|
|
nft_set_destroy(nft_trans_set(trans));
|
|
break;
|
|
break;
|
|
case NFT_MSG_DELSETELEM:
|
|
case NFT_MSG_DELSETELEM:
|
|
- nft_set_elem_destroy(nft_trans_elem_set(trans),
|
|
|
|
- nft_trans_elem(trans).priv, true);
|
|
|
|
|
|
+ nf_tables_set_elem_destroy(nft_trans_elem_set(trans),
|
|
|
|
+ nft_trans_elem(trans).priv);
|
|
break;
|
|
break;
|
|
case NFT_MSG_DELOBJ:
|
|
case NFT_MSG_DELOBJ:
|
|
nft_obj_destroy(nft_trans_obj(trans));
|
|
nft_obj_destroy(nft_trans_obj(trans));
|
|
@@ -4979,6 +5074,7 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb)
|
|
case NFT_MSG_DELSETELEM:
|
|
case NFT_MSG_DELSETELEM:
|
|
te = (struct nft_trans_elem *)trans->data;
|
|
te = (struct nft_trans_elem *)trans->data;
|
|
|
|
|
|
|
|
+ nft_set_elem_activate(net, te->set, &te->elem);
|
|
te->set->ops->activate(net, te->set, &te->elem);
|
|
te->set->ops->activate(net, te->set, &te->elem);
|
|
te->set->ndeact--;
|
|
te->set->ndeact--;
|
|
|
|
|
|
@@ -5464,7 +5560,7 @@ int nft_data_init(const struct nft_ctx *ctx,
|
|
EXPORT_SYMBOL_GPL(nft_data_init);
|
|
EXPORT_SYMBOL_GPL(nft_data_init);
|
|
|
|
|
|
/**
|
|
/**
|
|
- * nft_data_uninit - release a nft_data item
|
|
|
|
|
|
+ * nft_data_release - release a nft_data item
|
|
*
|
|
*
|
|
* @data: struct nft_data to release
|
|
* @data: struct nft_data to release
|
|
* @type: type of data
|
|
* @type: type of data
|
|
@@ -5472,7 +5568,7 @@ EXPORT_SYMBOL_GPL(nft_data_init);
|
|
* Release a nft_data item. NFT_DATA_VALUE types can be silently discarded,
|
|
* Release a nft_data item. NFT_DATA_VALUE types can be silently discarded,
|
|
* all others need to be released by calling this function.
|
|
* all others need to be released by calling this function.
|
|
*/
|
|
*/
|
|
-void nft_data_uninit(const struct nft_data *data, enum nft_data_types type)
|
|
|
|
|
|
+void nft_data_release(const struct nft_data *data, enum nft_data_types type)
|
|
{
|
|
{
|
|
if (type < NFT_DATA_VERDICT)
|
|
if (type < NFT_DATA_VERDICT)
|
|
return;
|
|
return;
|
|
@@ -5483,7 +5579,7 @@ void nft_data_uninit(const struct nft_data *data, enum nft_data_types type)
|
|
WARN_ON(1);
|
|
WARN_ON(1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-EXPORT_SYMBOL_GPL(nft_data_uninit);
|
|
|
|
|
|
+EXPORT_SYMBOL_GPL(nft_data_release);
|
|
|
|
|
|
int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data,
|
|
int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data,
|
|
enum nft_data_types type, unsigned int len)
|
|
enum nft_data_types type, unsigned int len)
|