netfilter: nf_tables: Introduce stateful object update operation

This patch adds the infrastructure needed for the stateful object update
support.

Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
Fernando Fernandez Mancera 2019-08-26 13:40:52 +02:00 committed by Pablo Neira Ayuso
parent 44b63b0a71
commit d62d0ba97b
2 changed files with 80 additions and 7 deletions

View File

@ -1127,6 +1127,7 @@ struct nft_object_type {
* @init: initialize object from netlink attributes * @init: initialize object from netlink attributes
* @destroy: release existing stateful object * @destroy: release existing stateful object
* @dump: netlink dump stateful object * @dump: netlink dump stateful object
* @update: update stateful object
*/ */
struct nft_object_ops { struct nft_object_ops {
void (*eval)(struct nft_object *obj, void (*eval)(struct nft_object *obj,
@ -1141,6 +1142,8 @@ struct nft_object_ops {
int (*dump)(struct sk_buff *skb, int (*dump)(struct sk_buff *skb,
struct nft_object *obj, struct nft_object *obj,
bool reset); bool reset);
void (*update)(struct nft_object *obj,
struct nft_object *newobj);
const struct nft_object_type *type; const struct nft_object_type *type;
}; };
@ -1429,10 +1432,16 @@ struct nft_trans_elem {
struct nft_trans_obj { struct nft_trans_obj {
struct nft_object *obj; struct nft_object *obj;
struct nft_object *newobj;
bool update;
}; };
#define nft_trans_obj(trans) \ #define nft_trans_obj(trans) \
(((struct nft_trans_obj *)trans->data)->obj) (((struct nft_trans_obj *)trans->data)->obj)
#define nft_trans_obj_newobj(trans) \
(((struct nft_trans_obj *)trans->data)->newobj)
#define nft_trans_obj_update(trans) \
(((struct nft_trans_obj *)trans->data)->update)
struct nft_trans_flowtable { struct nft_trans_flowtable {
struct nft_flowtable *flowtable; struct nft_flowtable *flowtable;

View File

@ -5131,6 +5131,38 @@ nft_obj_type_get(struct net *net, u32 objtype)
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);
} }
static int nf_tables_updobj(const struct nft_ctx *ctx,
const struct nft_object_type *type,
const struct nlattr *attr,
struct nft_object *obj)
{
struct nft_object *newobj;
struct nft_trans *trans;
int err;
trans = nft_trans_alloc(ctx, NFT_MSG_NEWOBJ,
sizeof(struct nft_trans_obj));
if (!trans)
return -ENOMEM;
newobj = nft_obj_init(ctx, type, attr);
if (IS_ERR(newobj)) {
err = PTR_ERR(newobj);
goto err1;
}
nft_trans_obj(trans) = obj;
nft_trans_obj_update(trans) = true;
nft_trans_obj_newobj(trans) = newobj;
list_add_tail(&trans->list, &ctx->net->nft.commit_list);
return 0;
err1:
kfree(trans);
kfree(newobj);
return err;
}
static int nf_tables_newobj(struct net *net, struct sock *nlsk, static int nf_tables_newobj(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[],
@ -5170,7 +5202,13 @@ static int nf_tables_newobj(struct net *net, struct sock *nlsk,
NL_SET_BAD_ATTR(extack, nla[NFTA_OBJ_NAME]); NL_SET_BAD_ATTR(extack, nla[NFTA_OBJ_NAME]);
return -EEXIST; return -EEXIST;
} }
return 0; if (nlh->nlmsg_flags & NLM_F_REPLACE)
return -EOPNOTSUPP;
type = nft_obj_type_get(net, objtype);
nft_ctx_init(&ctx, net, skb, nlh, family, table, NULL, nla);
return nf_tables_updobj(&ctx, type, nla[NFTA_OBJ_DATA], obj);
} }
nft_ctx_init(&ctx, net, skb, nlh, family, table, NULL, nla); nft_ctx_init(&ctx, net, skb, nlh, family, table, NULL, nla);
@ -6431,6 +6469,19 @@ static void nft_chain_commit_update(struct nft_trans *trans)
} }
} }
static void nft_obj_commit_update(struct nft_trans *trans)
{
struct nft_object *newobj;
struct nft_object *obj;
obj = nft_trans_obj(trans);
newobj = nft_trans_obj_newobj(trans);
obj->ops->update(obj, newobj);
kfree(newobj);
}
static void nft_commit_release(struct nft_trans *trans) static void nft_commit_release(struct nft_trans *trans)
{ {
switch (trans->msg_type) { switch (trans->msg_type) {
@ -6795,10 +6846,18 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
te->set->ndeact--; te->set->ndeact--;
break; break;
case NFT_MSG_NEWOBJ: case NFT_MSG_NEWOBJ:
nft_clear(net, nft_trans_obj(trans)); if (nft_trans_obj_update(trans)) {
nf_tables_obj_notify(&trans->ctx, nft_trans_obj(trans), nft_obj_commit_update(trans);
NFT_MSG_NEWOBJ); nf_tables_obj_notify(&trans->ctx,
nft_trans_destroy(trans); nft_trans_obj(trans),
NFT_MSG_NEWOBJ);
} else {
nft_clear(net, nft_trans_obj(trans));
nf_tables_obj_notify(&trans->ctx,
nft_trans_obj(trans),
NFT_MSG_NEWOBJ);
nft_trans_destroy(trans);
}
break; break;
case NFT_MSG_DELOBJ: case NFT_MSG_DELOBJ:
nft_obj_del(nft_trans_obj(trans)); nft_obj_del(nft_trans_obj(trans));
@ -6945,8 +7004,13 @@ static int __nf_tables_abort(struct net *net)
nft_trans_destroy(trans); nft_trans_destroy(trans);
break; break;
case NFT_MSG_NEWOBJ: case NFT_MSG_NEWOBJ:
trans->ctx.table->use--; if (nft_trans_obj_update(trans)) {
nft_obj_del(nft_trans_obj(trans)); kfree(nft_trans_obj_newobj(trans));
nft_trans_destroy(trans);
} else {
trans->ctx.table->use--;
nft_obj_del(nft_trans_obj(trans));
}
break; break;
case NFT_MSG_DELOBJ: case NFT_MSG_DELOBJ:
trans->ctx.table->use++; trans->ctx.table->use++;