Skip to content

Commit e8dbde5

Browse files
Phil Sutterummakynes
authored andcommitted
selftests: netfilter: Test nf_tables audit logging
Compare NETFILTER_CFG type audit logs emitted from kernel upon ruleset modifications against expected output. Signed-off-by: Phil Sutter <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 7fb818f commit e8dbde5

File tree

5 files changed

+277
-2
lines changed

5 files changed

+277
-2
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
# SPDX-License-Identifier: GPL-2.0-only
22
nf-queue
33
connect_close
4+
audit_logread

tools/testing/selftests/netfilter/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ TEST_PROGS := nft_trans_stress.sh nft_fib.sh nft_nat.sh bridge_brouter.sh \
66
nft_concat_range.sh nft_conntrack_helper.sh \
77
nft_queue.sh nft_meta.sh nf_nat_edemux.sh \
88
ipip-conntrack-mtu.sh conntrack_tcp_unreplied.sh \
9-
conntrack_vrf.sh nft_synproxy.sh rpath.sh
9+
conntrack_vrf.sh nft_synproxy.sh rpath.sh nft_audit.sh
1010

1111
HOSTPKG_CONFIG := pkg-config
1212

1313
CFLAGS += $(shell $(HOSTPKG_CONFIG) --cflags libmnl 2>/dev/null)
1414
LDLIBS += $(shell $(HOSTPKG_CONFIG) --libs libmnl 2>/dev/null || echo -lmnl)
1515

16-
TEST_GEN_FILES = nf-queue connect_close
16+
TEST_GEN_FILES = nf-queue connect_close audit_logread
1717

1818
include ../lib.mk
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#define _GNU_SOURCE
4+
#include <errno.h>
5+
#include <fcntl.h>
6+
#include <poll.h>
7+
#include <signal.h>
8+
#include <stdint.h>
9+
#include <stdio.h>
10+
#include <stdlib.h>
11+
#include <string.h>
12+
#include <sys/socket.h>
13+
#include <unistd.h>
14+
#include <linux/audit.h>
15+
#include <linux/netlink.h>
16+
17+
static int fd;
18+
19+
#define MAX_AUDIT_MESSAGE_LENGTH 8970
20+
struct audit_message {
21+
struct nlmsghdr nlh;
22+
union {
23+
struct audit_status s;
24+
char data[MAX_AUDIT_MESSAGE_LENGTH];
25+
} u;
26+
};
27+
28+
int audit_recv(int fd, struct audit_message *rep)
29+
{
30+
struct sockaddr_nl addr;
31+
socklen_t addrlen = sizeof(addr);
32+
int ret;
33+
34+
do {
35+
ret = recvfrom(fd, rep, sizeof(*rep), 0,
36+
(struct sockaddr *)&addr, &addrlen);
37+
} while (ret < 0 && errno == EINTR);
38+
39+
if (ret < 0 ||
40+
addrlen != sizeof(addr) ||
41+
addr.nl_pid != 0 ||
42+
rep->nlh.nlmsg_type == NLMSG_ERROR) /* short-cut for now */
43+
return -1;
44+
45+
return ret;
46+
}
47+
48+
int audit_send(int fd, uint16_t type, uint32_t key, uint32_t val)
49+
{
50+
static int seq = 0;
51+
struct audit_message msg = {
52+
.nlh = {
53+
.nlmsg_len = NLMSG_SPACE(sizeof(msg.u.s)),
54+
.nlmsg_type = type,
55+
.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
56+
.nlmsg_seq = ++seq,
57+
},
58+
.u.s = {
59+
.mask = key,
60+
.enabled = key == AUDIT_STATUS_ENABLED ? val : 0,
61+
.pid = key == AUDIT_STATUS_PID ? val : 0,
62+
}
63+
};
64+
struct sockaddr_nl addr = {
65+
.nl_family = AF_NETLINK,
66+
};
67+
int ret;
68+
69+
do {
70+
ret = sendto(fd, &msg, msg.nlh.nlmsg_len, 0,
71+
(struct sockaddr *)&addr, sizeof(addr));
72+
} while (ret < 0 && errno == EINTR);
73+
74+
if (ret != (int)msg.nlh.nlmsg_len)
75+
return -1;
76+
return 0;
77+
}
78+
79+
int audit_set(int fd, uint32_t key, uint32_t val)
80+
{
81+
struct audit_message rep = { 0 };
82+
int ret;
83+
84+
ret = audit_send(fd, AUDIT_SET, key, val);
85+
if (ret)
86+
return ret;
87+
88+
ret = audit_recv(fd, &rep);
89+
if (ret < 0)
90+
return ret;
91+
return 0;
92+
}
93+
94+
int readlog(int fd)
95+
{
96+
struct audit_message rep = { 0 };
97+
int ret = audit_recv(fd, &rep);
98+
const char *sep = "";
99+
char *k, *v;
100+
101+
if (ret < 0)
102+
return ret;
103+
104+
if (rep.nlh.nlmsg_type != AUDIT_NETFILTER_CFG)
105+
return 0;
106+
107+
/* skip the initial "audit(...): " part */
108+
strtok(rep.u.data, " ");
109+
110+
while ((k = strtok(NULL, "="))) {
111+
v = strtok(NULL, " ");
112+
113+
/* these vary and/or are uninteresting, ignore */
114+
if (!strcmp(k, "pid") ||
115+
!strcmp(k, "comm") ||
116+
!strcmp(k, "subj"))
117+
continue;
118+
119+
/* strip the varying sequence number */
120+
if (!strcmp(k, "table"))
121+
*strchrnul(v, ':') = '\0';
122+
123+
printf("%s%s=%s", sep, k, v);
124+
sep = " ";
125+
}
126+
if (*sep) {
127+
printf("\n");
128+
fflush(stdout);
129+
}
130+
return 0;
131+
}
132+
133+
void cleanup(int sig)
134+
{
135+
audit_set(fd, AUDIT_STATUS_ENABLED, 0);
136+
close(fd);
137+
if (sig)
138+
exit(0);
139+
}
140+
141+
int main(int argc, char **argv)
142+
{
143+
struct sigaction act = {
144+
.sa_handler = cleanup,
145+
};
146+
147+
fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_AUDIT);
148+
if (fd < 0) {
149+
perror("Can't open netlink socket");
150+
return -1;
151+
}
152+
153+
if (sigaction(SIGTERM, &act, NULL) < 0 ||
154+
sigaction(SIGINT, &act, NULL) < 0) {
155+
perror("Can't set signal handler");
156+
close(fd);
157+
return -1;
158+
}
159+
160+
audit_set(fd, AUDIT_STATUS_ENABLED, 1);
161+
audit_set(fd, AUDIT_STATUS_PID, getpid());
162+
163+
while (1)
164+
readlog(fd);
165+
}

tools/testing/selftests/netfilter/config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ CONFIG_NFT_REDIR=m
66
CONFIG_NFT_MASQ=m
77
CONFIG_NFT_FLOW_OFFLOAD=m
88
CONFIG_NF_CT_NETLINK=m
9+
CONFIG_AUDIT=y
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#!/bin/bash
2+
# SPDX-License-Identifier: GPL-2.0
3+
#
4+
# Check that audit logs generated for nft commands are as expected.
5+
6+
SKIP_RC=4
7+
RC=0
8+
9+
nft --version >/dev/null 2>&1 || {
10+
echo "SKIP: missing nft tool"
11+
exit $SKIP_RC
12+
}
13+
14+
logfile=$(mktemp)
15+
echo "logging into $logfile"
16+
./audit_logread >"$logfile" &
17+
logread_pid=$!
18+
trap 'kill $logread_pid; rm -f $logfile' EXIT
19+
exec 3<"$logfile"
20+
21+
do_test() { # (cmd, log)
22+
echo -n "testing for cmd: $1 ... "
23+
cat <&3 >/dev/null
24+
$1 >/dev/null || exit 1
25+
sleep 0.1
26+
res=$(diff -a -u <(echo "$2") - <&3)
27+
[ $? -eq 0 ] && { echo "OK"; return; }
28+
echo "FAIL"
29+
echo "$res"
30+
((RC++))
31+
}
32+
33+
nft flush ruleset
34+
35+
for table in t1 t2; do
36+
do_test "nft add table $table" \
37+
"table=$table family=2 entries=1 op=nft_register_table"
38+
39+
do_test "nft add chain $table c1" \
40+
"table=$table family=2 entries=1 op=nft_register_chain"
41+
42+
do_test "nft add chain $table c2; add chain $table c3" \
43+
"table=$table family=2 entries=2 op=nft_register_chain"
44+
45+
cmd="add rule $table c1 counter"
46+
47+
do_test "nft $cmd" \
48+
"table=$table family=2 entries=1 op=nft_register_rule"
49+
50+
do_test "nft $cmd; $cmd" \
51+
"table=$table family=2 entries=2 op=nft_register_rule"
52+
53+
cmd=""
54+
sep=""
55+
for chain in c2 c3; do
56+
for i in {1..3}; do
57+
cmd+="$sep add rule $table $chain counter"
58+
sep=";"
59+
done
60+
done
61+
do_test "nft $cmd" \
62+
"table=$table family=2 entries=6 op=nft_register_rule"
63+
done
64+
65+
do_test 'nft reset rules t1 c2' \
66+
'table=t1 family=2 entries=3 op=nft_reset_rule'
67+
68+
do_test 'nft reset rules table t1' \
69+
'table=t1 family=2 entries=3 op=nft_reset_rule
70+
table=t1 family=2 entries=3 op=nft_reset_rule
71+
table=t1 family=2 entries=3 op=nft_reset_rule'
72+
73+
do_test 'nft reset rules' \
74+
'table=t1 family=2 entries=3 op=nft_reset_rule
75+
table=t1 family=2 entries=3 op=nft_reset_rule
76+
table=t1 family=2 entries=3 op=nft_reset_rule
77+
table=t2 family=2 entries=3 op=nft_reset_rule
78+
table=t2 family=2 entries=3 op=nft_reset_rule
79+
table=t2 family=2 entries=3 op=nft_reset_rule'
80+
81+
for ((i = 0; i < 500; i++)); do
82+
echo "add rule t2 c3 counter accept comment \"rule $i\""
83+
done | do_test 'nft -f -' \
84+
'table=t2 family=2 entries=500 op=nft_register_rule'
85+
86+
do_test 'nft reset rules t2 c3' \
87+
'table=t2 family=2 entries=189 op=nft_reset_rule
88+
table=t2 family=2 entries=188 op=nft_reset_rule
89+
table=t2 family=2 entries=126 op=nft_reset_rule'
90+
91+
do_test 'nft reset rules t2' \
92+
'table=t2 family=2 entries=3 op=nft_reset_rule
93+
table=t2 family=2 entries=3 op=nft_reset_rule
94+
table=t2 family=2 entries=186 op=nft_reset_rule
95+
table=t2 family=2 entries=188 op=nft_reset_rule
96+
table=t2 family=2 entries=129 op=nft_reset_rule'
97+
98+
do_test 'nft reset rules' \
99+
'table=t1 family=2 entries=3 op=nft_reset_rule
100+
table=t1 family=2 entries=3 op=nft_reset_rule
101+
table=t1 family=2 entries=3 op=nft_reset_rule
102+
table=t2 family=2 entries=3 op=nft_reset_rule
103+
table=t2 family=2 entries=3 op=nft_reset_rule
104+
table=t2 family=2 entries=180 op=nft_reset_rule
105+
table=t2 family=2 entries=188 op=nft_reset_rule
106+
table=t2 family=2 entries=135 op=nft_reset_rule'
107+
108+
exit $RC

0 commit comments

Comments
 (0)