diff options
Diffstat (limited to 'tests/sys')
| -rw-r--r-- | tests/sys/fs/tarfs/tarfs_test.sh | 27 | ||||
| -rw-r--r-- | tests/sys/kern/unix_passfd_test.c | 29 | ||||
| -rw-r--r-- | tests/sys/kern/unix_seqpacket_test.c | 62 | ||||
| -rw-r--r-- | tests/sys/net/bpf/Makefile | 4 | ||||
| -rw-r--r-- | tests/sys/net/bpf/bpf.sh | 61 | ||||
| -rw-r--r-- | tests/sys/net/bpf/pcap-test.c | 268 | ||||
| -rw-r--r-- | tests/sys/netpfil/pf/divert-to.sh | 43 | ||||
| -rw-r--r-- | tests/sys/netpfil/pf/nat.sh | 60 | ||||
| -rw-r--r-- | tests/sys/sound/Makefile | 1 | ||||
| -rw-r--r-- | tests/sys/sound/polling.c | 205 |
10 files changed, 757 insertions, 3 deletions
diff --git a/tests/sys/fs/tarfs/tarfs_test.sh b/tests/sys/fs/tarfs/tarfs_test.sh index d4de71271985..505bfc5325f0 100644 --- a/tests/sys/fs/tarfs/tarfs_test.sh +++ b/tests/sys/fs/tarfs/tarfs_test.sh @@ -396,6 +396,32 @@ tarfs_git_archive_cleanup() { tarfs_cleanup } +atf_test_case tarfs_large cleanup +tarfs_large_head() { + atf_set "descr" "Test support for large files" + atf_set "require.user" "root" + atf_set "require.kmods" "tarfs" + atf_set "timeout" "600" +} +tarfs_large_body() { + tarfs_setup + local tarball="${PWD}/tarfs_test.tar.zst" + local exp off + for exp in 31 32 33 34 35 36 ; do + for off in 1 0 ; do + local size=$(((1<<exp)-off)) + atf_check truncate -s ${size} file + atf_check bsdtar -cf "${tarball}" --no-read-sparse --zstd file + atf_check mount -rt tarfs "${tarball}" "${mnt}" + atf_check -o inline:"${size}\n" stat -f%z "${mnt}"/file + atf_check umount "${mnt}" + done + done +} +tarfs_large_cleanup() { + tarfs_cleanup +} + atf_init_test_cases() { atf_add_test_case tarfs_basic atf_add_test_case tarfs_basic_gnu @@ -414,4 +440,5 @@ atf_init_test_cases() { atf_add_test_case tarfs_long_names atf_add_test_case tarfs_long_paths atf_add_test_case tarfs_git_archive + atf_add_test_case tarfs_large } diff --git a/tests/sys/kern/unix_passfd_test.c b/tests/sys/kern/unix_passfd_test.c index 7dc4541ad402..66bb406ea14e 100644 --- a/tests/sys/kern/unix_passfd_test.c +++ b/tests/sys/kern/unix_passfd_test.c @@ -1189,6 +1189,34 @@ ATF_TC_CLEANUP(cross_jail_dirfd, tc) err(1, "jail_remove"); } +ATF_TC_WITHOUT_HEAD(listening_socket); +ATF_TC_BODY(listening_socket, tc) +{ + struct sockaddr_un sun; + int error, ls, s[2]; + + ls = socket(AF_UNIX, SOCK_STREAM, 0); + ATF_REQUIRE(ls != -1); + + memset(&sun, 0, sizeof(sun)); + sun.sun_len = sizeof(sun); + sun.sun_family = AF_UNIX; + snprintf(sun.sun_path, sizeof(sun.sun_path), "listen.sock"); + error = bind(ls, (struct sockaddr *)&sun, sizeof(sun)); + ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno)); + error = listen(ls, 0); + + error = socketpair(AF_UNIX, SOCK_STREAM, 0, s); + ATF_REQUIRE_MSG(error == 0, "socketpair failed: %s", strerror(errno)); + + sendfd(s[0], ls); + sendfd(s[0], s[0]); + sendfd(s[0], s[1]); + close(ls); + close(s[0]); + close(s[1]); +} + ATF_TP_ADD_TCS(tp) { @@ -1211,6 +1239,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, empty_rights_message); ATF_TP_ADD_TC(tp, control_creates_records); ATF_TP_ADD_TC(tp, cross_jail_dirfd); + ATF_TP_ADD_TC(tp, listening_socket); return (atf_no_error()); } diff --git a/tests/sys/kern/unix_seqpacket_test.c b/tests/sys/kern/unix_seqpacket_test.c index b9a6be015241..27bd430430b4 100644 --- a/tests/sys/kern/unix_seqpacket_test.c +++ b/tests/sys/kern/unix_seqpacket_test.c @@ -1314,6 +1314,67 @@ ATF_TC_BODY(random_eor_and_waitall, tc) free(params.records); } +/* See bug 290658. */ +#define PEEK_RACE_SIZE 10 +#define PEEK_RACE_TRIES 10000 +static void * +peek_race_writer(void *args) +{ + struct timespec ts = {}; + u_short seed[3]; + char buf[PEEK_RACE_SIZE]; + int fd = *(int *)args; + + arc4random_buf(seed, sizeof(seed)); + for (u_int i = 0; i < PEEK_RACE_TRIES; i++) { + ATF_REQUIRE_EQ(PEEK_RACE_SIZE, + send(fd, buf, sizeof(buf), MSG_EOR)); + ts.tv_nsec = nrand48(seed) % 20; + (void)clock_nanosleep(CLOCK_MONOTONIC_FAST, 0, &ts, NULL); + } + + return (NULL); +} + +static void * +peek_race_peeker(void *args) +{ + char buf[PEEK_RACE_SIZE * 10]; + int fd = *(int *)args; + + for (u_int i = 0; i < PEEK_RACE_TRIES; i++) { + ssize_t rcvd; + + while ((rcvd = recv(fd, buf, sizeof(buf), + MSG_PEEK | MSG_DONTWAIT)) == -1) + ATF_REQUIRE(errno == EAGAIN); + ATF_REQUIRE(rcvd == PEEK_RACE_SIZE); + + ATF_REQUIRE_EQ(PEEK_RACE_SIZE, + recv(fd, buf, sizeof(buf), 0)); + } + + return (NULL); +} + +ATF_TC_WITHOUT_HEAD(peek_race); +ATF_TC_BODY(peek_race, tc) +{ + pthread_t peeker, writer; + int sv[2]; + + do_socketpair(sv); + + ATF_REQUIRE_EQ(0, pthread_create(&writer, NULL, peek_race_writer, + &sv[0])); + ATF_REQUIRE_EQ(0, pthread_create(&peeker, NULL, peek_race_peeker, + &sv[1])); + ATF_REQUIRE_EQ(0, pthread_join(writer, NULL)); + ATF_REQUIRE_EQ(0, pthread_join(peeker, NULL)); + close(sv[0]); + close(sv[1]); +} + /* * Main. */ @@ -1370,6 +1431,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, pipe_128k_8k); ATF_TP_ADD_TC(tp, pipe_128k_128k); ATF_TP_ADD_TC(tp, random_eor_and_waitall); + ATF_TP_ADD_TC(tp, peek_race); return atf_no_error(); } diff --git a/tests/sys/net/bpf/Makefile b/tests/sys/net/bpf/Makefile index 9c8a25b15d16..641d1aaef676 100644 --- a/tests/sys/net/bpf/Makefile +++ b/tests/sys/net/bpf/Makefile @@ -7,8 +7,10 @@ BINDIR= ${TESTSDIR} LIBADD+= nv -PROGS= bpf_multi_read +PROGS= bpf_multi_read pcap-test LIBADD.bpf_multi_read+= pcap +CFLAGS.pcap-test.c+= -Wno-cast-align +LIBADD.pcap-test+= pcap ATF_TESTS_SH= bpf diff --git a/tests/sys/net/bpf/bpf.sh b/tests/sys/net/bpf/bpf.sh index 2830c4862de9..f2d647b61de0 100644 --- a/tests/sys/net/bpf/bpf.sh +++ b/tests/sys/net/bpf/bpf.sh @@ -32,7 +32,6 @@ multi_read_head() atf_set descr 'Test multiple readers on /dev/bpf' atf_set require.user root } - multi_read_body() { vnet_init @@ -55,13 +54,71 @@ multi_read_body() # Now let this run for 10 seconds sleep 10 } - multi_read_cleanup() { vnet_cleanup } +atf_test_case "inject" "cleanup" +inject_head() +{ + atf_set descr 'Catch packets, re-inject and check' + atf_set require.user root +} +inject_body() +{ + vnet_init + + epair=$(vnet_mkepair) + ifconfig ${epair}a inet 192.0.2.1/24 up + vnet_mkjail alcatraz ${epair}b + jexec alcatraz ifconfig ${epair}b inet 192.0.2.2/24 up + + in=$(pwd)/$(mktemp in.pcap.XXXXXXXXXX) + in2=$(pwd)/$(mktemp in2.pcap.XXXXXXXXXX) + out=$(pwd)/$(mktemp out.pcap.XXXXXXXXXX) + + # write dump on jail side, with "in" direction + jexec alcatraz $(atf_get_srcdir)/pcap-test \ + capture epair0b $in 3 in > out & pid=$! + while ! jexec alcatraz netstat -B | grep -q epair0b.*pcap-test; do + sleep 0.01; + done + atf_check -s exit:0 -o ignore ping -c 3 -i 0.1 192.0.2.2 + atf_check -s exit:0 sh -c "wait $pid; exit $?" + atf_check -s exit:0 -o empty cat out + + # inject dump on host side, recording on both sides + jexec alcatraz $(atf_get_srcdir)/pcap-test \ + capture epair0b $in2 3 in > jout & jpid=$! + while ! jexec alcatraz netstat -B | grep -q epair0b.*pcap-test; do + sleep 0.01; + done + $(atf_get_srcdir)/pcap-test \ + capture epair0a $out 3 out > hout & hpid=$! + while ! netstat -B | grep -q epair0a.*pcap-test; do + sleep 0.01; + done + atf_check -s exit:0 -o empty -e empty $(atf_get_srcdir)/pcap-test \ + inject epair0a $in 3 + atf_check -s exit:0 sh -c "wait $jpid; exit $?" + atf_check -s exit:0 -o empty cat jout + atf_check -s exit:0 sh -c "wait $hpid; exit $?" + atf_check -s exit:0 -o empty cat hout + + # all 3 dumps should be equal + atf_check -s exit:0 -o empty -e empty $(atf_get_srcdir)/pcap-test \ + compare $in $out + atf_check -s exit:0 -o empty -e empty $(atf_get_srcdir)/pcap-test \ + compare $in $in2 +} +inject_cleanup() +{ + vnet_cleanup +} + atf_init_test_cases() { atf_add_test_case "multi_read" + atf_add_test_case "inject" } diff --git a/tests/sys/net/bpf/pcap-test.c b/tests/sys/net/bpf/pcap-test.c new file mode 100644 index 000000000000..9d01548f7aae --- /dev/null +++ b/tests/sys/net/bpf/pcap-test.c @@ -0,0 +1,268 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 Gleb Smirnoff <glebius@FreeBSD.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/param.h> +#include <sys/queue.h> +#include <netinet/ip.h> +#include <pcap/pcap.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <err.h> + +static int +strtolerr(const char *s) +{ + int rv; + + if ((rv = (int)strtol(s, NULL, 10)) < 1) + errx(1, "bad count %s", s); + return (rv); +} + +static pcap_direction_t +strtodir(const char *s) +{ + static const struct dirstr { + const char *str; + pcap_direction_t dir; + } dirs[] = { + { "in", PCAP_D_IN }, + { "out", PCAP_D_OUT }, + { "both", PCAP_D_INOUT }, + { "inout", PCAP_D_INOUT }, + }; + + for (u_int i = 0; i < nitems(dirs); i++) + if (strcasecmp(s, dirs[i].str) == 0) + return (dirs[i].dir); + errx(1, "bad directions %s", s); +} + +static char errbuf[PCAP_ERRBUF_SIZE]; + +static pcap_t * +pcap_open(const char *name, pcap_direction_t dir) +{ + pcap_t *p; + + if ((p = pcap_create(name, errbuf)) == NULL) + errx(1, "pcap_create: %s", errbuf); + if (pcap_set_timeout(p, 10) != 0) + errx(1, "pcap_set_timeout: %s", pcap_geterr(p)); + if (pcap_activate(p) != 0) + errx(1, "pcap_activate: %s", errbuf); + if (pcap_setdirection(p, dir) != 0) + errx(1, "pcap_setdirection: %s", pcap_geterr(p)); + return (p); +} + +#if 0 +/* + * Deal with the FreeBSD writer only optimization hack in bpf(4). + * Needed only when net.bpf.optimize_writers=1. + */ +static pcap_t * +pcap_rwopen(const char *name, pcap_direction_t dir) +{ + pcap_t *p; + struct bpf_program fp; + + p = pcap_open(name, dir); + if (pcap_compile(p, &fp, "", 0, PCAP_NETMASK_UNKNOWN) != 0) + errx(1, "pcap_compile: %s", pcap_geterr(p)); + if (pcap_setfilter(p, &fp) != 0) + errx(1, "pcap_setfilter: %s", pcap_geterr(p)); + pcap_freecode(&fp); + return (p); +} +#endif + +static void +list(int argc __unused, char *argv[] __unused) +{ + pcap_if_t *all, *p; + + if (pcap_findalldevs(&all, errbuf) != 0) + errx(1, "pcap_findalldevs: %s", errbuf); + for (p = all; p != NULL; p = p->next) + printf("%s ", p->name); + printf("\n"); + pcap_freealldevs(all); +} + +/* args: tap file count direction */ +static void +capture(int argc __unused, char *argv[]) +{ + pcap_t *p; + pcap_dumper_t *d; + pcap_direction_t dir; + int cnt; + + cnt = strtolerr(argv[2]); + dir = strtodir(argv[3]); + p = pcap_open(argv[0], dir); + + if ((d = pcap_dump_open(p, argv[1])) == NULL) + errx(1, "pcap_dump_open: %s", pcap_geterr(p)); + + if (pcap_loop(p, cnt, pcap_dump, (u_char *)d) != 0) + errx(1, "pcap_loop: %s", pcap_geterr(p)); + pcap_dump_close(d); +} + +static void +inject_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes) +{ + pcap_t *p = (pcap_t *)user; + + if (h->caplen != h->len) + errx(1, "incomplete packet %u of %u", h->caplen, h->len); + + if (pcap_inject(p, bytes, h->caplen) != (int)h->caplen) + errx(1, "pcap_inject: %s", errbuf); +} + +/* args: tap file count */ +static void +inject(int argc __unused, char *argv[]) +{ + pcap_t *p, *d; + int cnt; + + cnt = strtolerr(argv[2]); + p = pcap_open(argv[0], PCAP_D_INOUT); + + if ((d = pcap_open_offline(argv[1], errbuf)) == NULL) + errx(1, "pcap_open_offline: %s", errbuf); + if (pcap_loop(d, cnt, inject_packet, (u_char *)p) != 0) + errx(1, "pcap_loop: %s", pcap_geterr(p)); + pcap_close(p); + pcap_close(d); +} + +struct packet { + STAILQ_ENTRY(packet) next; + const void *data; + u_int caplen; + u_int len; +}; +STAILQ_HEAD(plist, packet); + +static void +store_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes) +{ + struct plist *list = (struct plist *)user; + struct packet *p; + + p = malloc(sizeof(*p)); + p->data = bytes; + p->caplen = h->caplen; + p->len = h->len; + STAILQ_INSERT_TAIL(list, p, next); +} + +/* args: file1 file2 */ +static void +compare(int argc __unused, char *argv[]) +{ + pcap_t *f1, *f2; + struct plist + list1 = STAILQ_HEAD_INITIALIZER(list1), + list2 = STAILQ_HEAD_INITIALIZER(list2); + struct packet *p1, *p2; + u_int cnt; + + if ((f1 = pcap_open_offline(argv[0], errbuf)) == NULL) + errx(1, "pcap_open_offline: %s", errbuf); + if (pcap_loop(f1, 0, store_packet, (u_char *)&list1) != 0) + errx(1, "pcap_loop: %s", pcap_geterr(f1)); + if ((f2 = pcap_open_offline(argv[1], errbuf)) == NULL) + errx(1, "pcap_open_offline: %s", errbuf); + if (pcap_loop(f2, 0, store_packet, (u_char *)&list2) != 0) + errx(1, "pcap_loop: %s", pcap_geterr(f2)); + + for (p1 = STAILQ_FIRST(&list1), p2 = STAILQ_FIRST(&list2), cnt = 1; + p1 != NULL && p2 != NULL; + p1 = STAILQ_NEXT(p1, next), p2 = STAILQ_NEXT(p2, next), cnt++) { + if (p1->len != p2->len) + errx(1, "packet #%u length %u != %u", + cnt, p1->len, p2->len); + if (p1->caplen != p2->caplen) + errx(1, "packet #%u capture length %u != %u", + cnt, p1->caplen, p2->caplen); + if (memcmp(p1->data, p2->data, p1->caplen) != 0) + errx(1, "packet #%u payload different", cnt); + } + if (p1 != NULL || p2 != NULL) + errx(1, "packet count different"); + + pcap_close(f1); + pcap_close(f2); +} + +static const struct cmd { + const char *cmd; + void (*func)(int, char **); + u_int argc; +} cmds[] = { + { .cmd = "list", .func = list, .argc = 0 }, + { .cmd = "inject", .func = inject, .argc = 3 }, + { .cmd = "capture", .func = capture,.argc = 4 }, + { .cmd = "compare", .func = compare,.argc = 2 }, +}; + +int +main(int argc, char *argv[]) +{ + + if (argc < 2) { + fprintf(stderr, "Usage: %s ", argv[0]); + for (u_int i = 0; i < nitems(cmds); i++) + fprintf(stderr, "%s%s", cmds[i].cmd, + i != nitems(cmds) - 1 ? "|" : "\n"); + exit(1); + } + + for (u_int i = 0; i < nitems(cmds); i++) + if (strcasecmp(argv[1], cmds[i].cmd) == 0) { + argc -= 2; + argv += 2; + if (argc < (int)cmds[i].argc) + errx(1, "%s takes %u args", + cmds[i].cmd, cmds[i].argc); + cmds[i].func(argc, argv); + return (0); + } + + warnx("Unknown command %s\n", argv[1]); + return (1); +} diff --git a/tests/sys/netpfil/pf/divert-to.sh b/tests/sys/netpfil/pf/divert-to.sh index ae44cd5d51af..153136199311 100644 --- a/tests/sys/netpfil/pf/divert-to.sh +++ b/tests/sys/netpfil/pf/divert-to.sh @@ -372,6 +372,47 @@ in_dn_in_div_in_out_div_out_dn_out_cleanup() pft_cleanup } +atf_test_case "pr260867" "cleanup" +pr260867_head() +{ + atf_set descr 'Test for the loop reported in PR260867' + atf_set require.user root +} + +pr260867_body() +{ + pft_init + divert_init + + epair=$(vnet_mkepair) + + ifconfig ${epair}a 192.0.2.1/24 up + + vnet_mkjail alcatraz ${epair}b + jexec alcatraz ifconfig ${epair}b 192.0.2.2/24 up + + # Sanity check + atf_check -s exit:0 -o ignore ping -c3 192.0.2.2 + + jexec alcatraz /usr/sbin/inetd -p ${PWD}/inetd-echo.pid $(atf_get_srcdir)/echo_inetd.conf + jexec alcatraz $(atf_get_srcdir)/../common/divapp 1001 divert-back & + + jexec alcatraz pfctl -e + pft_set_rules alcatraz \ + "pass in on ${epair}b proto tcp from any to port 7 divert-to 0.0.0.0 port 1001" + + reply=$(echo "foo" | nc -N 192.0.2.2 7) + if ["${reply}" != "foo" ]; + then + atf_fail "Did not receive echo reply" + fi +} + +pr260867_cleanup() +{ + pft_cleanup +} + atf_init_test_cases() { atf_add_test_case "in_div" @@ -383,4 +424,6 @@ atf_init_test_cases() atf_add_test_case "in_div_in_fwd_out_div_out" atf_add_test_case "in_dn_in_div_in_out_div_out_dn_out" + + atf_add_test_case "pr260867" } diff --git a/tests/sys/netpfil/pf/nat.sh b/tests/sys/netpfil/pf/nat.sh index 0824671fa0f1..3d953b495953 100644 --- a/tests/sys/netpfil/pf/nat.sh +++ b/tests/sys/netpfil/pf/nat.sh @@ -260,6 +260,64 @@ endpoint_independent_compat_cleanup() rm -f server2.out } +atf_test_case "endpoint_independent_exhaust" "cleanup" +endpoint_independent_exhaust_head() +{ + atf_set descr 'Test that a client behind NAT gets the same external IP:port for different servers' + atf_set require.user root +} + +endpoint_independent_exhaust_body() +{ + endpoint_independent_setup # Sets ${epair_…} variables + + endpoint_independent_common \ + "nat on ${epair_nat}a inet from ! (${epair_nat}a) to any -> (${epair_nat}a)" \ + "nat on ${epair_nat}a inet from ! (${epair_nat}a) to any -> (${epair_nat}a) port 3000:3001 sticky-address endpoint-independent" + + # Exhaust the available nat ports + for i in $(seq 1 10); do + echo "ping" | jexec client nc -u 198.51.100.32 1234 -w 0 + echo "ping" | jexec client nc -u 198.51.100.22 1234 -w 0 + done +} + +endpoint_independent_exhaust_cleanup() +{ + pft_cleanup + rm -f server1.out + rm -f server2.out +} + +atf_test_case "endpoint_independent_static_port" "cleanup" +endpoint_independent_static_port_head() +{ + atf_set descr 'Test that a client behind NAT gets the same external IP:port for different servers, with static-port' + atf_set require.user root +} + +endpoint_independent_static_port_body() +{ + endpoint_independent_setup # Sets ${epair_…} variables + + endpoint_independent_common \ + "nat on ${epair_nat}a inet from ! (${epair_nat}a) to any -> (${epair_nat}a)" \ + "nat on ${epair_nat}a inet from ! (${epair_nat}a) to any -> (${epair_nat}a) static-port sticky-address endpoint-independent" + + # Exhaust the available nat ports + for i in $(seq 1 10); do + echo "ping" | jexec client nc -u 198.51.100.32 1234 -w 0 + echo "ping" | jexec client nc -u 198.51.100.22 1234 -w 0 + done +} + +endpoint_independent_static_port_cleanup() +{ + pft_cleanup + rm -f server1.out + rm -f server2.out +} + atf_test_case "endpoint_independent_pass" "cleanup" endpoint_independent_pass_head() { @@ -900,6 +958,8 @@ atf_init_test_cases() atf_add_test_case "exhaust" atf_add_test_case "nested_anchor" atf_add_test_case "endpoint_independent_compat" + atf_add_test_case "endpoint_independent_exhaust" + atf_add_test_case "endpoint_independent_static_port" atf_add_test_case "endpoint_independent_pass" atf_add_test_case "nat6_nolinklocal" atf_add_test_case "empty_table_source_hash" diff --git a/tests/sys/sound/Makefile b/tests/sys/sound/Makefile index 74a0765a0540..ab52a7aad386 100644 --- a/tests/sys/sound/Makefile +++ b/tests/sys/sound/Makefile @@ -3,6 +3,7 @@ PACKAGE= tests TESTSDIR= ${TESTSBASE}/sys/sound ATF_TESTS_C+= pcm_read_write +ATF_TESTS_C+= polling ATF_TESTS_C+= sndstat CFLAGS+= -I${SRCTOP}/sys diff --git a/tests/sys/sound/polling.c b/tests/sys/sound/polling.c new file mode 100644 index 000000000000..b7a328d13d4c --- /dev/null +++ b/tests/sys/sound/polling.c @@ -0,0 +1,205 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 The FreeBSD Foundation + * + * This software was developed by Christos Margiolis <christos@FreeBSD.org> + * under sponsorship from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/event.h> +#include <sys/soundcard.h> + +#include <atf-c.h> +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <poll.h> +#include <unistd.h> + +#define FMT_ERR(s) s ": %s", strerror(errno) + +static int +oss_init(void) +{ + int fd, tmp, rc; + + fd = open("/dev/dsp.dummy", O_RDWR); + ATF_REQUIRE_MSG(fd >= 0, FMT_ERR("open")); + + tmp = 2; + rc = ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp); + ATF_REQUIRE_EQ_MSG(rc, 0, FMT_ERR("ioctl")); + + tmp = AFMT_S16_LE; + rc = ioctl(fd, SNDCTL_DSP_SETFMT, &tmp); + ATF_REQUIRE_EQ_MSG(rc, 0, FMT_ERR("ioctl")); + + tmp = 48000; + rc = ioctl(fd, SNDCTL_DSP_SPEED, &tmp); + ATF_REQUIRE_EQ_MSG(rc, 0, FMT_ERR("ioctl")); + + /* + * See http://manuals.opensound.com/developer/SNDCTL_DSP_SETTRIGGER.html + */ + tmp = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT; + rc = ioctl(fd, SNDCTL_DSP_SETTRIGGER, &tmp); + ATF_REQUIRE_EQ_MSG(rc, 0, FMT_ERR("ioctl")); + + return (fd); +} + +ATF_TC(poll_kqueue); +ATF_TC_HEAD(poll_kqueue, tc) +{ + atf_tc_set_md_var(tc, "descr", "kqueue(2) test"); + atf_tc_set_md_var(tc, "require.kmods", "snd_dummy"); +} + +ATF_TC_BODY(poll_kqueue, tc) +{ + struct kevent ev; + int16_t buf[32]; + int fd, kq; + + fd = oss_init(); + + kq = kqueue(); + ATF_REQUIRE_MSG(kq >= 0, FMT_ERR("kqueue")); + EV_SET(&ev, fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, 0); + ATF_REQUIRE_MSG(kevent(kq, &ev, 1, NULL, 0, NULL) == 0, + FMT_ERR("kevent")); + + ATF_REQUIRE_MSG(kevent(kq, NULL, 0, &ev, 1, NULL) == 1, + FMT_ERR("kevent")); + ATF_REQUIRE_MSG((ev.flags & EV_ERROR) == 0, "EV_ERROR is set"); + ATF_REQUIRE_MSG(ev.data != 0, "data is %" PRId64, ev.data); + ATF_REQUIRE_MSG(read(fd, buf, sizeof(buf)) > 0, FMT_ERR("read")); + + EV_SET(&ev, fd, EVFILT_READ, EV_DELETE, 0, 0, 0); + close(kq); + + kq = kqueue(); + ATF_REQUIRE_MSG(kq >= 0, FMT_ERR("kqueue")); + EV_SET(&ev, fd, EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, 0); + ATF_REQUIRE_MSG(kevent(kq, &ev, 1, NULL, 0, NULL) == 0, + FMT_ERR("kevent")); + + ATF_REQUIRE_MSG(kevent(kq, NULL, 0, &ev, 1, NULL) == 1, + FMT_ERR("kevent")); + ATF_REQUIRE_MSG((ev.flags & EV_ERROR) == 0, "EV_ERROR is set"); + ATF_REQUIRE_MSG(ev.data != 0, "data is %" PRId64, ev.data); + ATF_REQUIRE_MSG(write(fd, buf, sizeof(buf)) > 0, FMT_ERR("write")); + + EV_SET(&ev, fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0); + close(kq); + + close(fd); +} + +ATF_TC(poll_poll); +ATF_TC_HEAD(poll_poll, tc) +{ + atf_tc_set_md_var(tc, "descr", "poll(2) test"); + atf_tc_set_md_var(tc, "require.kmods", "snd_dummy"); +} + +ATF_TC_BODY(poll_poll, tc) +{ + struct pollfd pfd[2]; + int16_t buf[32]; + int fd; + bool rd = false; + bool wr = false; + + fd = oss_init(); + + while (!rd || !wr) { + pfd[0].fd = fd; + pfd[0].events = POLLIN; + pfd[1].fd = fd; + pfd[1].events = POLLOUT; + ATF_REQUIRE_MSG(poll(pfd, sizeof(pfd) / sizeof(struct pollfd), + -1) > 0, FMT_ERR("poll")); + + if (pfd[0].revents) { + ATF_REQUIRE_MSG(read(fd, buf, sizeof(buf)) > 0, + FMT_ERR("read")); + rd = true; + } + if (pfd[1].revents) { + ATF_REQUIRE_MSG(write(fd, buf, sizeof(buf)) > 0, + FMT_ERR("write")); + wr = true; + } + } + close(fd); +} + +ATF_TC(poll_select); +ATF_TC_HEAD(poll_select, tc) +{ + atf_tc_set_md_var(tc, "descr", "select(2) test"); + atf_tc_set_md_var(tc, "require.kmods", "snd_dummy"); +} + +ATF_TC_BODY(poll_select, tc) +{ + fd_set fds[2]; + int16_t buf[32]; + int fd; + bool rd = false; + bool wr = false; + + fd = oss_init(); + + while (!rd || !wr) { + FD_ZERO(&fds[0]); + FD_ZERO(&fds[1]); + FD_SET(fd, &fds[0]); + FD_SET(fd, &fds[1]); + ATF_REQUIRE_MSG(select(fd + 2, &fds[0], &fds[1], NULL, NULL) > 0, + FMT_ERR("select")); + if (FD_ISSET(fd, &fds[0])) { + ATF_REQUIRE_MSG(read(fd, buf, sizeof(buf)) > 0, + FMT_ERR("read")); + rd = true; + } + if (FD_ISSET(fd, &fds[1])) { + ATF_REQUIRE_MSG(write(fd, buf, sizeof(buf)) > 0, + FMT_ERR("write")); + wr = true; + } + } + close(fd); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, poll_kqueue); + ATF_TP_ADD_TC(tp, poll_poll); + ATF_TP_ADD_TC(tp, poll_select); + + return (atf_no_error()); +} |
