netfilter: nf_tables: skip dead set elements in netlink dump
[platform/kernel/linux-starfive.git] / net / netfilter / nf_tables_api.c
index a72b6ae..1f6d5ff 100644 (file)
@@ -805,7 +805,7 @@ static struct nft_table *nft_table_lookup(const struct net *net,
 
 static struct nft_table *nft_table_lookup_byhandle(const struct net *net,
                                                   const struct nlattr *nla,
-                                                  u8 genmask, u32 nlpid)
+                                                  int family, u8 genmask, u32 nlpid)
 {
        struct nftables_pernet *nft_net;
        struct nft_table *table;
@@ -813,6 +813,7 @@ static struct nft_table *nft_table_lookup_byhandle(const struct net *net,
        nft_net = nft_pernet(net);
        list_for_each_entry(table, &nft_net->tables, list) {
                if (be64_to_cpu(nla_get_be64(nla)) == table->handle &&
+                   table->family == family &&
                    nft_active_genmask(table, genmask)) {
                        if (nft_table_has_owner(table) &&
                            nlpid && table->nlpid != nlpid)
@@ -1546,7 +1547,7 @@ static int nf_tables_deltable(struct sk_buff *skb, const struct nfnl_info *info,
 
        if (nla[NFTA_TABLE_HANDLE]) {
                attr = nla[NFTA_TABLE_HANDLE];
-               table = nft_table_lookup_byhandle(net, attr, genmask,
+               table = nft_table_lookup_byhandle(net, attr, family, genmask,
                                                  NETLINK_CB(skb).portid);
        } else {
                attr = nla[NFTA_TABLE_NAME];
@@ -2262,7 +2263,16 @@ static int nft_chain_parse_hook(struct net *net,
                                return -EOPNOTSUPP;
                }
 
-               type = basechain->type;
+               if (nla[NFTA_CHAIN_TYPE]) {
+                       type = __nf_tables_chain_type_lookup(nla[NFTA_CHAIN_TYPE],
+                                                            family);
+                       if (!type) {
+                               NL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_TYPE]);
+                               return -ENOENT;
+                       }
+               } else {
+                       type = basechain->type;
+               }
        }
 
        if (!try_module_get(type->owner)) {
@@ -3166,7 +3176,7 @@ int nft_expr_inner_parse(const struct nft_ctx *ctx, const struct nlattr *nla,
        if (err < 0)
                return err;
 
-       if (!tb[NFTA_EXPR_DATA])
+       if (!tb[NFTA_EXPR_DATA] || !tb[NFTA_EXPR_NAME])
                return -EINVAL;
 
        type = __nft_expr_type_get(ctx->family, tb[NFTA_EXPR_NAME]);
@@ -3465,10 +3475,6 @@ static int __nf_tables_dump_rules(struct sk_buff *skb,
                        goto cont_skip;
                if (*idx < s_idx)
                        goto cont;
-               if (*idx > s_idx) {
-                       memset(&cb->args[1], 0,
-                                       sizeof(cb->args) - sizeof(cb->args[0]));
-               }
                if (prule)
                        handle = prule->handle;
                else
@@ -4747,8 +4753,8 @@ static int nft_set_desc_concat_parse(const struct nlattr *attr,
 static int nft_set_desc_concat(struct nft_set_desc *desc,
                               const struct nlattr *nla)
 {
+       u32 num_regs = 0, key_num_regs = 0;
        struct nlattr *attr;
-       u32 num_regs = 0;
        int rem, err, i;
 
        nla_for_each_nested(attr, nla, rem) {
@@ -4763,6 +4769,10 @@ static int nft_set_desc_concat(struct nft_set_desc *desc,
        for (i = 0; i < desc->field_count; i++)
                num_regs += DIV_ROUND_UP(desc->field_len[i], sizeof(u32));
 
+       key_num_regs = DIV_ROUND_UP(desc->klen, sizeof(u32));
+       if (key_num_regs != num_regs)
+               return -EINVAL;
+
        if (num_regs > NFT_REG32_COUNT)
                return -E2BIG;
 
@@ -4984,8 +4994,16 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
        }
 
        desc.policy = NFT_SET_POL_PERFORMANCE;
-       if (nla[NFTA_SET_POLICY] != NULL)
+       if (nla[NFTA_SET_POLICY] != NULL) {
                desc.policy = ntohl(nla_get_be32(nla[NFTA_SET_POLICY]));
+               switch (desc.policy) {
+               case NFT_SET_POL_PERFORMANCE:
+               case NFT_SET_POL_MEMORY:
+                       break;
+               default:
+                       return -EOPNOTSUPP;
+               }
+       }
 
        if (nla[NFTA_SET_DESC] != NULL) {
                err = nf_tables_set_desc_parse(&desc, nla[NFTA_SET_DESC]);
@@ -5556,7 +5574,6 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
        const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
        unsigned char *b = skb_tail_pointer(skb);
        struct nlattr *nest;
-       u64 timeout = 0;
 
        nest = nla_nest_start_noflag(skb, NFTA_LIST_ELEM);
        if (nest == NULL)
@@ -5592,15 +5609,11 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
                         htonl(*nft_set_ext_flags(ext))))
                goto nla_put_failure;
 
-       if (nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT)) {
-               timeout = *nft_set_ext_timeout(ext);
-               if (nla_put_be64(skb, NFTA_SET_ELEM_TIMEOUT,
-                                nf_jiffies64_to_msecs(timeout),
-                                NFTA_SET_ELEM_PAD))
-                       goto nla_put_failure;
-       } else if (set->flags & NFT_SET_TIMEOUT) {
-               timeout = READ_ONCE(set->timeout);
-       }
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT) &&
+           nla_put_be64(skb, NFTA_SET_ELEM_TIMEOUT,
+                        nf_jiffies64_to_msecs(*nft_set_ext_timeout(ext)),
+                        NFTA_SET_ELEM_PAD))
+               goto nla_put_failure;
 
        if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
                u64 expires, now = get_jiffies_64();
@@ -5615,9 +5628,6 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
                                 nf_jiffies64_to_msecs(expires),
                                 NFTA_SET_ELEM_PAD))
                        goto nla_put_failure;
-
-               if (reset)
-                       *nft_set_ext_expiration(ext) = now + timeout;
        }
 
        if (nft_set_ext_exists(ext, NFT_SET_EXT_USERDATA)) {
@@ -5652,7 +5662,7 @@ static int nf_tables_dump_setelem(const struct nft_ctx *ctx,
        const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
        struct nft_set_dump_args *args;
 
-       if (nft_set_elem_expired(ext))
+       if (nft_set_elem_expired(ext) || nft_set_elem_is_dead(ext))
                return 0;
 
        args = container_of(iter, struct nft_set_dump_args, iter);
@@ -6433,7 +6443,7 @@ static int nft_setelem_catchall_deactivate(const struct net *net,
 
        list_for_each_entry(catchall, &set->catchall_list, list) {
                ext = nft_set_elem_ext(set, catchall->elem);
-               if (!nft_is_active(net, ext))
+               if (!nft_is_active_next(net, ext))
                        continue;
 
                kfree(elem->priv);
@@ -6476,6 +6486,12 @@ static int nft_setelem_deactivate(const struct net *net,
        return ret;
 }
 
+static void nft_setelem_catchall_destroy(struct nft_set_elem_catchall *catchall)
+{
+       list_del_rcu(&catchall->list);
+       kfree_rcu(catchall, rcu);
+}
+
 static void nft_setelem_catchall_remove(const struct net *net,
                                        const struct nft_set *set,
                                        const struct nft_set_elem *elem)
@@ -6484,8 +6500,7 @@ static void nft_setelem_catchall_remove(const struct net *net,
 
        list_for_each_entry_safe(catchall, next, &set->catchall_list, list) {
                if (catchall->elem == elem->priv) {
-                       list_del_rcu(&catchall->list);
-                       kfree_rcu(catchall, rcu);
+                       nft_setelem_catchall_destroy(catchall);
                        break;
                }
        }
@@ -7217,10 +7232,11 @@ static int nf_tables_delsetelem(struct sk_buff *skb,
 
                if (err < 0) {
                        NL_SET_BAD_ATTR(extack, attr);
-                       break;
+                       return err;
                }
        }
-       return err;
+
+       return 0;
 }
 
 /*
@@ -7615,6 +7631,16 @@ nla_put_failure:
        return -1;
 }
 
+static void audit_log_obj_reset(const struct nft_table *table,
+                               unsigned int base_seq, unsigned int nentries)
+{
+       char *buf = kasprintf(GFP_ATOMIC, "%s:%u", table->name, base_seq);
+
+       audit_log_nfcfg(buf, table->family, nentries,
+                       AUDIT_NFT_OP_OBJ_RESET, GFP_ATOMIC);
+       kfree(buf);
+}
+
 struct nft_obj_filter {
        char            *table;
        u32             type;
@@ -7629,8 +7655,10 @@ static int nf_tables_dump_obj(struct sk_buff *skb, struct netlink_callback *cb)
        struct net *net = sock_net(skb->sk);
        int family = nfmsg->nfgen_family;
        struct nftables_pernet *nft_net;
+       unsigned int entries = 0;
        struct nft_object *obj;
        bool reset = false;
+       int rc = 0;
 
        if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) == NFT_MSG_GETOBJ_RESET)
                reset = true;
@@ -7643,6 +7671,7 @@ static int nf_tables_dump_obj(struct sk_buff *skb, struct netlink_callback *cb)
                if (family != NFPROTO_UNSPEC && family != table->family)
                        continue;
 
+               entries = 0;
                list_for_each_entry_rcu(obj, &table->objects, list) {
                        if (!nft_is_active(net, obj))
                                goto cont;
@@ -7658,34 +7687,27 @@ static int nf_tables_dump_obj(struct sk_buff *skb, struct netlink_callback *cb)
                            filter->type != NFT_OBJECT_UNSPEC &&
                            obj->ops->type->type != filter->type)
                                goto cont;
-                       if (reset) {
-                               char *buf = kasprintf(GFP_ATOMIC,
-                                                     "%s:%u",
-                                                     table->name,
-                                                     nft_net->base_seq);
-
-                               audit_log_nfcfg(buf,
-                                               family,
-                                               obj->handle,
-                                               AUDIT_NFT_OP_OBJ_RESET,
-                                               GFP_ATOMIC);
-                               kfree(buf);
-                       }
 
-                       if (nf_tables_fill_obj_info(skb, net, NETLINK_CB(cb->skb).portid,
-                                                   cb->nlh->nlmsg_seq,
-                                                   NFT_MSG_NEWOBJ,
-                                                   NLM_F_MULTI | NLM_F_APPEND,
-                                                   table->family, table,
-                                                   obj, reset) < 0)
-                               goto done;
+                       rc = nf_tables_fill_obj_info(skb, net,
+                                                    NETLINK_CB(cb->skb).portid,
+                                                    cb->nlh->nlmsg_seq,
+                                                    NFT_MSG_NEWOBJ,
+                                                    NLM_F_MULTI | NLM_F_APPEND,
+                                                    table->family, table,
+                                                    obj, reset);
+                       if (rc < 0)
+                               break;
 
+                       entries++;
                        nl_dump_check_consistent(cb, nlmsg_hdr(skb));
 cont:
                        idx++;
                }
+               if (reset && entries)
+                       audit_log_obj_reset(table, nft_net->base_seq, entries);
+               if (rc < 0)
+                       break;
        }
-done:
        rcu_read_unlock();
 
        cb->args[0] = idx;
@@ -7790,7 +7812,7 @@ static int nf_tables_getobj(struct sk_buff *skb, const struct nfnl_info *info,
 
                audit_log_nfcfg(buf,
                                family,
-                               obj->handle,
+                               1,
                                AUDIT_NFT_OP_OBJ_RESET,
                                GFP_ATOMIC);
                kfree(buf);
@@ -9640,9 +9662,8 @@ void nft_trans_gc_queue_sync_done(struct nft_trans_gc *trans)
        call_rcu(&trans->rcu, nft_trans_gc_trans_free);
 }
 
-static struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc,
-                                                 unsigned int gc_seq,
-                                                 bool sync)
+struct nft_trans_gc *nft_trans_gc_catchall_async(struct nft_trans_gc *gc,
+                                                unsigned int gc_seq)
 {
        struct nft_set_elem_catchall *catchall;
        const struct nft_set *set = gc->set;
@@ -9658,11 +9679,7 @@ static struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc,
 
                nft_set_elem_dead(ext);
 dead_elem:
-               if (sync)
-                       gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC);
-               else
-                       gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC);
-
+               gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC);
                if (!gc)
                        return NULL;
 
@@ -9672,15 +9689,34 @@ dead_elem:
        return gc;
 }
 
-struct nft_trans_gc *nft_trans_gc_catchall_async(struct nft_trans_gc *gc,
-                                                unsigned int gc_seq)
-{
-       return nft_trans_gc_catchall(gc, gc_seq, false);
-}
-
 struct nft_trans_gc *nft_trans_gc_catchall_sync(struct nft_trans_gc *gc)
 {
-       return nft_trans_gc_catchall(gc, 0, true);
+       struct nft_set_elem_catchall *catchall, *next;
+       const struct nft_set *set = gc->set;
+       struct nft_set_elem elem;
+       struct nft_set_ext *ext;
+
+       WARN_ON_ONCE(!lockdep_commit_lock_is_held(gc->net));
+
+       list_for_each_entry_safe(catchall, next, &set->catchall_list, list) {
+               ext = nft_set_elem_ext(set, catchall->elem);
+
+               if (!nft_set_elem_expired(ext))
+                       continue;
+
+               gc = nft_trans_gc_queue_sync(gc, GFP_KERNEL);
+               if (!gc)
+                       return NULL;
+
+               memset(&elem, 0, sizeof(elem));
+               elem.priv = catchall->elem;
+
+               nft_setelem_data_deactivate(gc->net, gc->set, &elem);
+               nft_setelem_catchall_destroy(catchall);
+               nft_trans_gc_elem_add(gc, elem.priv);
+       }
+
+       return gc;
 }
 
 static void nf_tables_module_autoload_cleanup(struct net *net)
@@ -9834,7 +9870,7 @@ static void nft_set_commit_update(struct list_head *set_update_list)
        list_for_each_entry_safe(set, next, set_update_list, pending_update) {
                list_del_init(&set->pending_update);
 
-               if (!set->ops->commit)
+               if (!set->ops->commit || set->dead)
                        continue;
 
                set->ops->commit(set);
@@ -10330,6 +10366,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
                                nft_trans_destroy(trans);
                                break;
                        }
+                       nft_trans_set(trans)->dead = 1;
                        list_del_rcu(&nft_trans_set(trans)->list);
                        break;
                case NFT_MSG_DELSET: