Skip to content

Commit

Permalink
netfilter: nf_tables: audit log object reset once per table
Browse files Browse the repository at this point in the history
[ Upstream commit 1baf015 ]

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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
Phil Sutter authored and gregkh committed Nov 8, 2023
1 parent ec80ad4 commit 0f2840d
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 22 deletions.
50 changes: 28 additions & 22 deletions net/netfilter/nf_tables_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -7324,6 +7324,16 @@ static int nf_tables_fill_obj_info(struct sk_buff *skb, struct net *net,
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;
Expand All @@ -7338,8 +7348,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;
Expand All @@ -7352,6 +7364,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;
Expand All @@ -7367,34 +7380,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;
Expand Down Expand Up @@ -7499,7 +7505,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);
Expand Down
46 changes: 46 additions & 0 deletions tools/testing/selftests/netfilter/nft_audit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,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 }' \
Expand All @@ -107,6 +113,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'
Expand Down Expand Up @@ -156,6 +168,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 | \
Expand Down

0 comments on commit 0f2840d

Please sign in to comment.