netfilter: nf_tables: do not compare internal table flags on updates
[sfrench/cifs-2.6.git] / net / netfilter / nf_tables_api.c
index 1683dc196b5921da91c8dd81b99ac9106c7493fe..984c1c83ee38c70c708303593a2d471fdfb8a449 100644 (file)
@@ -1195,8 +1195,10 @@ static void nf_tables_table_disable(struct net *net, struct nft_table *table)
 #define __NFT_TABLE_F_INTERNAL         (NFT_TABLE_F_MASK + 1)
 #define __NFT_TABLE_F_WAS_DORMANT      (__NFT_TABLE_F_INTERNAL << 0)
 #define __NFT_TABLE_F_WAS_AWAKEN       (__NFT_TABLE_F_INTERNAL << 1)
+#define __NFT_TABLE_F_WAS_ORPHAN       (__NFT_TABLE_F_INTERNAL << 2)
 #define __NFT_TABLE_F_UPDATE           (__NFT_TABLE_F_WAS_DORMANT | \
-                                        __NFT_TABLE_F_WAS_AWAKEN)
+                                        __NFT_TABLE_F_WAS_AWAKEN | \
+                                        __NFT_TABLE_F_WAS_ORPHAN)
 
 static int nf_tables_updtable(struct nft_ctx *ctx)
 {
@@ -1211,13 +1213,16 @@ static int nf_tables_updtable(struct nft_ctx *ctx)
        if (flags & ~NFT_TABLE_F_MASK)
                return -EOPNOTSUPP;
 
-       if (flags == ctx->table->flags)
+       if (flags == (ctx->table->flags & NFT_TABLE_F_MASK))
                return 0;
 
        if ((nft_table_has_owner(ctx->table) &&
             !(flags & NFT_TABLE_F_OWNER)) ||
-           (!nft_table_has_owner(ctx->table) &&
-            flags & NFT_TABLE_F_OWNER))
+           (flags & NFT_TABLE_F_OWNER &&
+            !nft_table_is_orphan(ctx->table)))
+               return -EOPNOTSUPP;
+
+       if ((flags ^ ctx->table->flags) & NFT_TABLE_F_PERSIST)
                return -EOPNOTSUPP;
 
        /* No dormant off/on/off/on games in single transaction */
@@ -1246,6 +1251,13 @@ static int nf_tables_updtable(struct nft_ctx *ctx)
                }
        }
 
+       if ((flags & NFT_TABLE_F_OWNER) &&
+           !nft_table_has_owner(ctx->table)) {
+               ctx->table->nlpid = ctx->portid;
+               ctx->table->flags |= NFT_TABLE_F_OWNER |
+                                    __NFT_TABLE_F_WAS_ORPHAN;
+       }
+
        nft_trans_table_update(trans) = true;
        nft_trans_commit_list_add_tail(ctx->net, trans);
 
@@ -4238,23 +4250,18 @@ static bool nft_set_ops_candidate(const struct nft_set_type *type, u32 flags)
  * given, in that case the amount of memory per element is used.
  */
 static const struct nft_set_ops *
-nft_select_set_ops(const struct nft_ctx *ctx,
-                  const struct nlattr * const nla[],
+nft_select_set_ops(const struct nft_ctx *ctx, u32 flags,
                   const struct nft_set_desc *desc)
 {
        struct nftables_pernet *nft_net = nft_pernet(ctx->net);
        const struct nft_set_ops *ops, *bops;
        struct nft_set_estimate est, best;
        const struct nft_set_type *type;
-       u32 flags = 0;
        int i;
 
        lockdep_assert_held(&nft_net->commit_mutex);
        lockdep_nfnl_nft_mutex_not_held();
 
-       if (nla[NFTA_SET_FLAGS] != NULL)
-               flags = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
-
        bops        = NULL;
        best.size   = ~0;
        best.lookup = ~0;
@@ -5146,7 +5153,7 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
        if (!(info->nlh->nlmsg_flags & NLM_F_CREATE))
                return -ENOENT;
 
-       ops = nft_select_set_ops(&ctx, nla, &desc);
+       ops = nft_select_set_ops(&ctx, flags, &desc);
        if (IS_ERR(ops))
                return PTR_ERR(ops);
 
@@ -10435,6 +10442,10 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
                                } else if (trans->ctx.table->flags & __NFT_TABLE_F_WAS_AWAKEN) {
                                        trans->ctx.table->flags &= ~NFT_TABLE_F_DORMANT;
                                }
+                               if (trans->ctx.table->flags & __NFT_TABLE_F_WAS_ORPHAN) {
+                                       trans->ctx.table->flags &= ~NFT_TABLE_F_OWNER;
+                                       trans->ctx.table->nlpid = 0;
+                               }
                                trans->ctx.table->flags &= ~__NFT_TABLE_F_UPDATE;
                                nft_trans_destroy(trans);
                        } else {
@@ -11361,6 +11372,10 @@ again:
        list_for_each_entry(table, &nft_net->tables, list) {
                if (nft_table_has_owner(table) &&
                    n->portid == table->nlpid) {
+                       if (table->flags & NFT_TABLE_F_PERSIST) {
+                               table->flags &= ~NFT_TABLE_F_OWNER;
+                               continue;
+                       }
                        __nft_release_hook(net, table);
                        list_del_rcu(&table->list);
                        to_delete[deleted++] = table;