netfilter: nf_tables: generalise transaction infrastructure
This patch generalises the existing rule transaction infrastructure so it can be used to handle set, table and chain object transactions as well. The transaction provides a data area that stores private information depending on the transaction type. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
Родитель
7c95f6d866
Коммит
1081d11b08
|
@ -387,18 +387,25 @@ struct nft_rule {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct nft_rule_trans - nf_tables rule update in transaction
|
* struct nft_trans - nf_tables object update in transaction
|
||||||
*
|
*
|
||||||
* @list: used internally
|
* @list: used internally
|
||||||
* @ctx: rule context
|
* @ctx: transaction context
|
||||||
* @rule: rule that needs to be updated
|
* @data: internal information related to the transaction
|
||||||
*/
|
*/
|
||||||
struct nft_rule_trans {
|
struct nft_trans {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct nft_ctx ctx;
|
struct nft_ctx ctx;
|
||||||
|
char data[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nft_trans_rule {
|
||||||
struct nft_rule *rule;
|
struct nft_rule *rule;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define nft_trans_rule(trans) \
|
||||||
|
(((struct nft_trans_rule *)trans->data)->rule)
|
||||||
|
|
||||||
static inline struct nft_expr *nft_expr_first(const struct nft_rule *rule)
|
static inline struct nft_expr *nft_expr_first(const struct nft_rule *rule)
|
||||||
{
|
{
|
||||||
return (struct nft_expr *)&rule->data[0];
|
return (struct nft_expr *)&rule->data[0];
|
||||||
|
|
|
@ -105,6 +105,25 @@ static void nft_ctx_init(struct nft_ctx *ctx,
|
||||||
ctx->nla = nla;
|
ctx->nla = nla;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct nft_trans *nft_trans_alloc(struct nft_ctx *ctx, u32 size)
|
||||||
|
{
|
||||||
|
struct nft_trans *trans;
|
||||||
|
|
||||||
|
trans = kzalloc(sizeof(struct nft_trans) + size, GFP_KERNEL);
|
||||||
|
if (trans == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
trans->ctx = *ctx;
|
||||||
|
|
||||||
|
return trans;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nft_trans_destroy(struct nft_trans *trans)
|
||||||
|
{
|
||||||
|
list_del(&trans->list);
|
||||||
|
kfree(trans);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tables
|
* Tables
|
||||||
*/
|
*/
|
||||||
|
@ -1557,26 +1576,25 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
|
||||||
kfree(rule);
|
kfree(rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct nft_trans *nft_trans_rule_add(struct nft_ctx *ctx,
|
||||||
|
struct nft_rule *rule)
|
||||||
|
{
|
||||||
|
struct nft_trans *trans;
|
||||||
|
|
||||||
|
trans = nft_trans_alloc(ctx, sizeof(struct nft_trans_rule));
|
||||||
|
if (trans == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
nft_trans_rule(trans) = rule;
|
||||||
|
list_add_tail(&trans->list, &ctx->net->nft.commit_list);
|
||||||
|
|
||||||
|
return trans;
|
||||||
|
}
|
||||||
|
|
||||||
#define NFT_RULE_MAXEXPRS 128
|
#define NFT_RULE_MAXEXPRS 128
|
||||||
|
|
||||||
static struct nft_expr_info *info;
|
static struct nft_expr_info *info;
|
||||||
|
|
||||||
static struct nft_rule_trans *
|
|
||||||
nf_tables_trans_add(struct nft_ctx *ctx, struct nft_rule *rule)
|
|
||||||
{
|
|
||||||
struct nft_rule_trans *rupd;
|
|
||||||
|
|
||||||
rupd = kmalloc(sizeof(struct nft_rule_trans), GFP_KERNEL);
|
|
||||||
if (rupd == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
rupd->ctx = *ctx;
|
|
||||||
rupd->rule = rule;
|
|
||||||
list_add_tail(&rupd->list, &ctx->net->nft.commit_list);
|
|
||||||
|
|
||||||
return rupd;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
|
static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
|
||||||
const struct nlmsghdr *nlh,
|
const struct nlmsghdr *nlh,
|
||||||
const struct nlattr * const nla[])
|
const struct nlattr * const nla[])
|
||||||
|
@ -1587,7 +1605,7 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
|
||||||
struct nft_table *table;
|
struct nft_table *table;
|
||||||
struct nft_chain *chain;
|
struct nft_chain *chain;
|
||||||
struct nft_rule *rule, *old_rule = NULL;
|
struct nft_rule *rule, *old_rule = NULL;
|
||||||
struct nft_rule_trans *repl = NULL;
|
struct nft_trans *trans = NULL;
|
||||||
struct nft_expr *expr;
|
struct nft_expr *expr;
|
||||||
struct nft_ctx ctx;
|
struct nft_ctx ctx;
|
||||||
struct nlattr *tmp;
|
struct nlattr *tmp;
|
||||||
|
@ -1685,8 +1703,8 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
|
||||||
|
|
||||||
if (nlh->nlmsg_flags & NLM_F_REPLACE) {
|
if (nlh->nlmsg_flags & NLM_F_REPLACE) {
|
||||||
if (nft_rule_is_active_next(net, old_rule)) {
|
if (nft_rule_is_active_next(net, old_rule)) {
|
||||||
repl = nf_tables_trans_add(&ctx, old_rule);
|
trans = nft_trans_rule_add(&ctx, old_rule);
|
||||||
if (repl == NULL) {
|
if (trans == NULL) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto err2;
|
goto err2;
|
||||||
}
|
}
|
||||||
|
@ -1708,7 +1726,7 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
|
||||||
list_add_rcu(&rule->list, &chain->rules);
|
list_add_rcu(&rule->list, &chain->rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nf_tables_trans_add(&ctx, rule) == NULL) {
|
if (nft_trans_rule_add(&ctx, rule) == NULL) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto err3;
|
goto err3;
|
||||||
}
|
}
|
||||||
|
@ -1716,11 +1734,10 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
|
||||||
|
|
||||||
err3:
|
err3:
|
||||||
list_del_rcu(&rule->list);
|
list_del_rcu(&rule->list);
|
||||||
if (repl) {
|
if (trans) {
|
||||||
list_del_rcu(&repl->rule->list);
|
list_del_rcu(&nft_trans_rule(trans)->list);
|
||||||
list_del(&repl->list);
|
nft_rule_clear(net, nft_trans_rule(trans));
|
||||||
nft_rule_clear(net, repl->rule);
|
nft_trans_destroy(trans);
|
||||||
kfree(repl);
|
|
||||||
}
|
}
|
||||||
err2:
|
err2:
|
||||||
nf_tables_rule_destroy(&ctx, rule);
|
nf_tables_rule_destroy(&ctx, rule);
|
||||||
|
@ -1737,7 +1754,7 @@ nf_tables_delrule_one(struct nft_ctx *ctx, struct nft_rule *rule)
|
||||||
{
|
{
|
||||||
/* You cannot delete the same rule twice */
|
/* You cannot delete the same rule twice */
|
||||||
if (nft_rule_is_active_next(ctx->net, rule)) {
|
if (nft_rule_is_active_next(ctx->net, rule)) {
|
||||||
if (nf_tables_trans_add(ctx, rule) == NULL)
|
if (nft_trans_rule_add(ctx, rule) == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
nft_rule_disactivate_next(ctx->net, rule);
|
nft_rule_disactivate_next(ctx->net, rule);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1813,7 +1830,7 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
|
||||||
static int nf_tables_commit(struct sk_buff *skb)
|
static int nf_tables_commit(struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct net *net = sock_net(skb->sk);
|
struct net *net = sock_net(skb->sk);
|
||||||
struct nft_rule_trans *rupd, *tmp;
|
struct nft_trans *trans, *next;
|
||||||
|
|
||||||
/* Bump generation counter, invalidate any dump in progress */
|
/* Bump generation counter, invalidate any dump in progress */
|
||||||
net->nft.genctr++;
|
net->nft.genctr++;
|
||||||
|
@ -1826,38 +1843,38 @@ static int nf_tables_commit(struct sk_buff *skb)
|
||||||
*/
|
*/
|
||||||
synchronize_rcu();
|
synchronize_rcu();
|
||||||
|
|
||||||
list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
|
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
|
||||||
/* This rule was inactive in the past and just became active.
|
/* This rule was inactive in the past and just became active.
|
||||||
* Clear the next bit of the genmask since its meaning has
|
* Clear the next bit of the genmask since its meaning has
|
||||||
* changed, now it is the future.
|
* changed, now it is the future.
|
||||||
*/
|
*/
|
||||||
if (nft_rule_is_active(net, rupd->rule)) {
|
if (nft_rule_is_active(net, nft_trans_rule(trans))) {
|
||||||
nft_rule_clear(net, rupd->rule);
|
nft_rule_clear(net, nft_trans_rule(trans));
|
||||||
nf_tables_rule_notify(skb, rupd->ctx.nlh,
|
nf_tables_rule_notify(skb, trans->ctx.nlh,
|
||||||
rupd->ctx.table, rupd->ctx.chain,
|
trans->ctx.table,
|
||||||
rupd->rule, NFT_MSG_NEWRULE, 0,
|
trans->ctx.chain,
|
||||||
rupd->ctx.afi->family);
|
nft_trans_rule(trans),
|
||||||
list_del(&rupd->list);
|
NFT_MSG_NEWRULE, 0,
|
||||||
kfree(rupd);
|
trans->ctx.afi->family);
|
||||||
|
nft_trans_destroy(trans);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This rule is in the past, get rid of it */
|
/* This rule is in the past, get rid of it */
|
||||||
list_del_rcu(&rupd->rule->list);
|
list_del_rcu(&nft_trans_rule(trans)->list);
|
||||||
nf_tables_rule_notify(skb, rupd->ctx.nlh,
|
nf_tables_rule_notify(skb, trans->ctx.nlh,
|
||||||
rupd->ctx.table, rupd->ctx.chain,
|
trans->ctx.table, trans->ctx.chain,
|
||||||
rupd->rule, NFT_MSG_DELRULE, 0,
|
nft_trans_rule(trans), NFT_MSG_DELRULE,
|
||||||
rupd->ctx.afi->family);
|
0, trans->ctx.afi->family);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure we don't see any packet traversing old rules */
|
/* Make sure we don't see any packet traversing old rules */
|
||||||
synchronize_rcu();
|
synchronize_rcu();
|
||||||
|
|
||||||
/* Now we can safely release unused old rules */
|
/* Now we can safely release unused old rules */
|
||||||
list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
|
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
|
||||||
nf_tables_rule_destroy(&rupd->ctx, rupd->rule);
|
nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
|
||||||
list_del(&rupd->list);
|
nft_trans_destroy(trans);
|
||||||
kfree(rupd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1866,27 +1883,25 @@ static int nf_tables_commit(struct sk_buff *skb)
|
||||||
static int nf_tables_abort(struct sk_buff *skb)
|
static int nf_tables_abort(struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct net *net = sock_net(skb->sk);
|
struct net *net = sock_net(skb->sk);
|
||||||
struct nft_rule_trans *rupd, *tmp;
|
struct nft_trans *trans, *next;
|
||||||
|
|
||||||
list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
|
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
|
||||||
if (!nft_rule_is_active_next(net, rupd->rule)) {
|
if (!nft_rule_is_active_next(net, nft_trans_rule(trans))) {
|
||||||
nft_rule_clear(net, rupd->rule);
|
nft_rule_clear(net, nft_trans_rule(trans));
|
||||||
list_del(&rupd->list);
|
nft_trans_destroy(trans);
|
||||||
kfree(rupd);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This rule is inactive, get rid of it */
|
/* This rule is inactive, get rid of it */
|
||||||
list_del_rcu(&rupd->rule->list);
|
list_del_rcu(&nft_trans_rule(trans)->list);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure we don't see any packet accessing aborted rules */
|
/* Make sure we don't see any packet accessing aborted rules */
|
||||||
synchronize_rcu();
|
synchronize_rcu();
|
||||||
|
|
||||||
list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
|
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
|
||||||
nf_tables_rule_destroy(&rupd->ctx, rupd->rule);
|
nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
|
||||||
list_del(&rupd->list);
|
nft_trans_destroy(trans);
|
||||||
kfree(rupd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче