Commit 1e1fb6f0 authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso

netfilter: nf_tables: reject table flag and netdev basechain updates

netdev basechain updates are stored in the transaction object hook list.
When setting on the table dormant flag, it iterates over the existing
hooks in the basechain. Thus, skipping the hooks that are being
added/deleted in this transaction, which leaves hook registration in
inconsistent state.

Reject table flag updates in combination with netdev basechain updates
in the same batch:

- Update table flags and add/delete basechain: Check from basechain update
  path if there are pending flag updates for this table.
- add/delete basechain and update table flags: Iterate over the transaction
  list to search for basechain updates from the table update path.

In both cases, the batch is rejected. Based on suggestion from Florian Westphal.

Fixes: b9703ed4 ("netfilter: nf_tables: support for adding new devices to an existing netdev chain")
Fixes: 7d937b10 ("netfilter: nf_tables: support for deleting devices in an existing netdev chain")
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent b32ca27f
...@@ -1200,6 +1200,25 @@ static void nf_tables_table_disable(struct net *net, struct nft_table *table) ...@@ -1200,6 +1200,25 @@ static void nf_tables_table_disable(struct net *net, struct nft_table *table)
__NFT_TABLE_F_WAS_AWAKEN | \ __NFT_TABLE_F_WAS_AWAKEN | \
__NFT_TABLE_F_WAS_ORPHAN) __NFT_TABLE_F_WAS_ORPHAN)
static bool nft_table_pending_update(const struct nft_ctx *ctx)
{
struct nftables_pernet *nft_net = nft_pernet(ctx->net);
struct nft_trans *trans;
if (ctx->table->flags & __NFT_TABLE_F_UPDATE)
return true;
list_for_each_entry(trans, &nft_net->commit_list, list) {
if ((trans->msg_type == NFT_MSG_NEWCHAIN ||
trans->msg_type == NFT_MSG_DELCHAIN) &&
trans->ctx.table == ctx->table &&
nft_trans_chain_update(trans))
return true;
}
return false;
}
static int nf_tables_updtable(struct nft_ctx *ctx) static int nf_tables_updtable(struct nft_ctx *ctx)
{ {
struct nft_trans *trans; struct nft_trans *trans;
...@@ -1226,7 +1245,7 @@ static int nf_tables_updtable(struct nft_ctx *ctx) ...@@ -1226,7 +1245,7 @@ static int nf_tables_updtable(struct nft_ctx *ctx)
return -EOPNOTSUPP; return -EOPNOTSUPP;
/* No dormant off/on/off/on games in single transaction */ /* No dormant off/on/off/on games in single transaction */
if (ctx->table->flags & __NFT_TABLE_F_UPDATE) if (nft_table_pending_update(ctx))
return -EINVAL; return -EINVAL;
trans = nft_trans_alloc(ctx, NFT_MSG_NEWTABLE, trans = nft_trans_alloc(ctx, NFT_MSG_NEWTABLE,
...@@ -2631,6 +2650,13 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy, ...@@ -2631,6 +2650,13 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
} }
} }
if (table->flags & __NFT_TABLE_F_UPDATE &&
!list_empty(&hook.list)) {
NL_SET_BAD_ATTR(extack, attr);
err = -EOPNOTSUPP;
goto err_hooks;
}
if (!(table->flags & NFT_TABLE_F_DORMANT) && if (!(table->flags & NFT_TABLE_F_DORMANT) &&
nft_is_base_chain(chain) && nft_is_base_chain(chain) &&
!list_empty(&hook.list)) { !list_empty(&hook.list)) {
...@@ -2860,6 +2886,9 @@ static int nft_delchain_hook(struct nft_ctx *ctx, ...@@ -2860,6 +2886,9 @@ static int nft_delchain_hook(struct nft_ctx *ctx,
struct nft_trans *trans; struct nft_trans *trans;
int err; int err;
if (ctx->table->flags & __NFT_TABLE_F_UPDATE)
return -EOPNOTSUPP;
err = nft_chain_parse_hook(ctx->net, basechain, nla, &chain_hook, err = nft_chain_parse_hook(ctx->net, basechain, nla, &chain_hook,
ctx->family, chain->flags, extack); ctx->family, chain->flags, extack);
if (err < 0) if (err < 0)
......
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