Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <phil@nwl.cc>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Phil Sutter and committed by
Pablo Neira Ayuso
e8dbde59 7fb818f2

+277 -2
+1
tools/testing/selftests/netfilter/.gitignore
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 nf-queue 3 3 connect_close 4 + audit_logread
+2 -2
tools/testing/selftests/netfilter/Makefile
··· 6 6 nft_concat_range.sh nft_conntrack_helper.sh \ 7 7 nft_queue.sh nft_meta.sh nf_nat_edemux.sh \ 8 8 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 10 10 11 11 HOSTPKG_CONFIG := pkg-config 12 12 13 13 CFLAGS += $(shell $(HOSTPKG_CONFIG) --cflags libmnl 2>/dev/null) 14 14 LDLIBS += $(shell $(HOSTPKG_CONFIG) --libs libmnl 2>/dev/null || echo -lmnl) 15 15 16 - TEST_GEN_FILES = nf-queue connect_close 16 + TEST_GEN_FILES = nf-queue connect_close audit_logread 17 17 18 18 include ../lib.mk
+165
tools/testing/selftests/netfilter/audit_logread.c
··· 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 + }
+1
tools/testing/selftests/netfilter/config
··· 6 6 CONFIG_NFT_MASQ=m 7 7 CONFIG_NFT_FLOW_OFFLOAD=m 8 8 CONFIG_NF_CT_NETLINK=m 9 + CONFIG_AUDIT=y
+108
tools/testing/selftests/netfilter/nft_audit.sh
··· 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