netfilter: nf_tables: audit log object reset once per table
authorPhil Sutter <phil@nwl.cc>
Wed, 11 Oct 2023 15:06:59 +0000 (17:06 +0200)
committerFlorian Westphal <fw@strlen.de>
Wed, 18 Oct 2023 11:43:40 +0000 (13:43 +0200)
When resetting multiple objects at once (via dump request), emit a log
message per table (or filled skb) and resurrect the 'entries' parameter
to contain the number of objects being logged for.

To test the skb exhaustion path, perform some bulk counter and quota
adds in the kselftest.

Signed-off-by: Phil Sutter <phil@nwl.cc>
Reviewed-by: Richard Guy Briggs <rgb@redhat.com>
Acked-by: Paul Moore <paul@paul-moore.com> (Audit)
Signed-off-by: Florian Westphal <fw@strlen.de>
net/netfilter/nf_tables_api.c
tools/testing/selftests/netfilter/nft_audit.sh

index a623d31b6518fd2845f829fc050d06daac633f7e..7b77ff5985f68f11feeee913bd31121b5d1f4dfb 100644 (file)
@@ -7607,6 +7607,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;
@@ -7621,8 +7631,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;
@@ -7635,6 +7647,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;
@@ -7650,34 +7663,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;
@@ -7782,7 +7788,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);
index bb34329e02a7f99b0b60bef66f9f1a2441b04c16..e94a80859bbdb4e88061182312f8052c24ad2cf5 100755 (executable)
@@ -93,6 +93,12 @@ do_test 'nft add counter t1 c1' \
 do_test 'nft add counter t2 c1; add counter t2 c2' \
 'table=t2 family=2 entries=2 op=nft_register_obj'
 
+for ((i = 3; i <= 500; i++)); do
+       echo "add counter t2 c$i"
+done >$rulefile
+do_test "nft -f $rulefile" \
+'table=t2 family=2 entries=498 op=nft_register_obj'
+
 # adding/updating quotas
 
 do_test 'nft add quota t1 q1 { 10 bytes }' \
@@ -101,6 +107,12 @@ do_test 'nft add quota t1 q1 { 10 bytes }' \
 do_test 'nft add quota t2 q1 { 10 bytes }; add quota t2 q2 { 10 bytes }' \
 'table=t2 family=2 entries=2 op=nft_register_obj'
 
+for ((i = 3; i <= 500; i++)); do
+       echo "add quota t2 q$i { 10 bytes }"
+done >$rulefile
+do_test "nft -f $rulefile" \
+'table=t2 family=2 entries=498 op=nft_register_obj'
+
 # changing the quota value triggers obj update path
 do_test 'nft add quota t1 q1 { 20 bytes }' \
 'table=t1 family=2 entries=1 op=nft_register_obj'
@@ -150,6 +162,40 @@ done
 do_test 'nft reset set t1 s' \
 'table=t1 family=2 entries=3 op=nft_reset_setelem'
 
+# resetting counters
+
+do_test 'nft reset counter t1 c1' \
+'table=t1 family=2 entries=1 op=nft_reset_obj'
+
+do_test 'nft reset counters t1' \
+'table=t1 family=2 entries=1 op=nft_reset_obj'
+
+do_test 'nft reset counters t2' \
+'table=t2 family=2 entries=342 op=nft_reset_obj
+table=t2 family=2 entries=158 op=nft_reset_obj'
+
+do_test 'nft reset counters' \
+'table=t1 family=2 entries=1 op=nft_reset_obj
+table=t2 family=2 entries=341 op=nft_reset_obj
+table=t2 family=2 entries=159 op=nft_reset_obj'
+
+# resetting quotas
+
+do_test 'nft reset quota t1 q1' \
+'table=t1 family=2 entries=1 op=nft_reset_obj'
+
+do_test 'nft reset quotas t1' \
+'table=t1 family=2 entries=1 op=nft_reset_obj'
+
+do_test 'nft reset quotas t2' \
+'table=t2 family=2 entries=315 op=nft_reset_obj
+table=t2 family=2 entries=185 op=nft_reset_obj'
+
+do_test 'nft reset quotas' \
+'table=t1 family=2 entries=1 op=nft_reset_obj
+table=t2 family=2 entries=314 op=nft_reset_obj
+table=t2 family=2 entries=186 op=nft_reset_obj'
+
 # deleting rules
 
 readarray -t handles < <(nft -a list chain t1 c1 | \