Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1081d11b authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso
Browse files

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: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 7c95f6d8
Loading
Loading
Loading
Loading
+11 −4
Original line number Diff line number Diff line
@@ -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
 *	@ctx: rule context
 *	@rule: rule that needs to be updated
 *	@ctx: transaction context
 *	@data: internal information related to the transaction
 */
struct nft_rule_trans {
struct nft_trans {
	struct list_head		list;
	struct nft_ctx			ctx;
	char				data[0];
};

struct nft_trans_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)
{
	return (struct nft_expr *)&rule->data[0];
+69 −54
Original line number Diff line number Diff line
@@ -105,6 +105,25 @@ static void nft_ctx_init(struct nft_ctx *ctx,
	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
 */
@@ -1557,26 +1576,25 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
	kfree(rule);
}

#define NFT_RULE_MAXEXPRS	128

static struct nft_expr_info *info;

static struct nft_rule_trans *
nf_tables_trans_add(struct nft_ctx *ctx, struct nft_rule *rule)
static struct nft_trans *nft_trans_rule_add(struct nft_ctx *ctx,
					    struct nft_rule *rule)
{
	struct nft_rule_trans *rupd;
	struct nft_trans *trans;

	rupd = kmalloc(sizeof(struct nft_rule_trans), GFP_KERNEL);
	if (rupd == NULL)
	trans = nft_trans_alloc(ctx, sizeof(struct nft_trans_rule));
	if (trans == NULL)
		return NULL;

	rupd->ctx = *ctx;
	rupd->rule = rule;
	list_add_tail(&rupd->list, &ctx->net->nft.commit_list);
	nft_trans_rule(trans) = rule;
	list_add_tail(&trans->list, &ctx->net->nft.commit_list);

	return rupd;
	return trans;
}

#define NFT_RULE_MAXEXPRS	128

static struct nft_expr_info *info;

static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
			     const struct nlmsghdr *nlh,
			     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_chain *chain;
	struct nft_rule *rule, *old_rule = NULL;
	struct nft_rule_trans *repl = NULL;
	struct nft_trans *trans = NULL;
	struct nft_expr *expr;
	struct nft_ctx ctx;
	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 (nft_rule_is_active_next(net, old_rule)) {
			repl = nf_tables_trans_add(&ctx, old_rule);
			if (repl == NULL) {
			trans = nft_trans_rule_add(&ctx, old_rule);
			if (trans == NULL) {
				err = -ENOMEM;
				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);
	}

	if (nf_tables_trans_add(&ctx, rule) == NULL) {
	if (nft_trans_rule_add(&ctx, rule) == NULL) {
		err = -ENOMEM;
		goto err3;
	}
@@ -1716,11 +1734,10 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,

err3:
	list_del_rcu(&rule->list);
	if (repl) {
		list_del_rcu(&repl->rule->list);
		list_del(&repl->list);
		nft_rule_clear(net, repl->rule);
		kfree(repl);
	if (trans) {
		list_del_rcu(&nft_trans_rule(trans)->list);
		nft_rule_clear(net, nft_trans_rule(trans));
		nft_trans_destroy(trans);
	}
err2:
	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 */
	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;
		nft_rule_disactivate_next(ctx->net, rule);
		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)
{
	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 */
	net->nft.genctr++;
@@ -1826,38 +1843,38 @@ static int nf_tables_commit(struct sk_buff *skb)
	 */
	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.
		 * Clear the next bit of the genmask since its meaning has
		 * changed, now it is the future.
		 */
		if (nft_rule_is_active(net, rupd->rule)) {
			nft_rule_clear(net, rupd->rule);
			nf_tables_rule_notify(skb, rupd->ctx.nlh,
					      rupd->ctx.table, rupd->ctx.chain,
					      rupd->rule, NFT_MSG_NEWRULE, 0,
					      rupd->ctx.afi->family);
			list_del(&rupd->list);
			kfree(rupd);
		if (nft_rule_is_active(net, nft_trans_rule(trans))) {
			nft_rule_clear(net, nft_trans_rule(trans));
			nf_tables_rule_notify(skb, trans->ctx.nlh,
					      trans->ctx.table,
					      trans->ctx.chain,
					      nft_trans_rule(trans),
					      NFT_MSG_NEWRULE, 0,
					      trans->ctx.afi->family);
			nft_trans_destroy(trans);
			continue;
		}

		/* This rule is in the past, get rid of it */
		list_del_rcu(&rupd->rule->list);
		nf_tables_rule_notify(skb, rupd->ctx.nlh,
				      rupd->ctx.table, rupd->ctx.chain,
				      rupd->rule, NFT_MSG_DELRULE, 0,
				      rupd->ctx.afi->family);
		list_del_rcu(&nft_trans_rule(trans)->list);
		nf_tables_rule_notify(skb, trans->ctx.nlh,
				      trans->ctx.table, trans->ctx.chain,
				      nft_trans_rule(trans), NFT_MSG_DELRULE,
				      0, trans->ctx.afi->family);
	}

	/* Make sure we don't see any packet traversing old rules */
	synchronize_rcu();

	/* Now we can safely release unused old rules */
	list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
		nf_tables_rule_destroy(&rupd->ctx, rupd->rule);
		list_del(&rupd->list);
		kfree(rupd);
	list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
		nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
		nft_trans_destroy(trans);
	}

	return 0;
@@ -1866,27 +1883,25 @@ static int nf_tables_commit(struct sk_buff *skb)
static int nf_tables_abort(struct sk_buff *skb)
{
	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) {
		if (!nft_rule_is_active_next(net, rupd->rule)) {
			nft_rule_clear(net, rupd->rule);
			list_del(&rupd->list);
			kfree(rupd);
	list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
		if (!nft_rule_is_active_next(net, nft_trans_rule(trans))) {
			nft_rule_clear(net, nft_trans_rule(trans));
			nft_trans_destroy(trans);
			continue;
		}

		/* 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 */
	synchronize_rcu();

	list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
		nf_tables_rule_destroy(&rupd->ctx, rupd->rule);
		list_del(&rupd->list);
		kfree(rupd);
	list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
		nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
		nft_trans_destroy(trans);
	}

	return 0;