diff options
Diffstat (limited to 'tests/sys')
-rw-r--r-- | tests/sys/aio/aio_test.c | 336 | ||||
-rwxr-xr-x | tests/sys/geom/class/gate/ggate_test.sh | 62 | ||||
-rw-r--r-- | tests/sys/kern/Makefile | 8 | ||||
-rw-r--r-- | tests/sys/kern/libkern_crc32.c | 132 | ||||
-rwxr-xr-x | tests/sys/netinet/fibs_test.sh | 252 |
5 files changed, 643 insertions, 147 deletions
diff --git a/tests/sys/aio/aio_test.c b/tests/sys/aio/aio_test.c index 33ccd7d3ab7b..ca8b41eec242 100644 --- a/tests/sys/aio/aio_test.c +++ b/tests/sys/aio/aio_test.c @@ -33,7 +33,7 @@ * reading it from a second descriptor using AIO. For some targets, the same * fd is used for write and read (i.e., file, md device), but for others the * operation is performed on a peer (pty, socket, fifo, etc). A timeout is - * initiated to detect undo blocking. This test does not attempt to exercise + * initiated to detect undue blocking. This test does not attempt to exercise * error cases or more subtle asynchronous behavior, just make sure that the * basic operations work on some basic object types. */ @@ -74,6 +74,13 @@ #define GLOBAL_MAX 16384 #define BUFFER_MAX GLOBAL_MAX + +/* + * A completion function will block until the aio has completed, then return + * the result of the aio. errno will be set appropriately. + */ +typedef ssize_t (*completion)(struct aiocb*); + struct aio_context { int ac_read_fd, ac_write_fd; long ac_seed; @@ -179,6 +186,45 @@ aio_context_init(struct aio_context *ac, int read_fd, ac->ac_cleanup_arg = cleanup_arg; } +static ssize_t +poll(struct aiocb *aio) { + int error; + + while ((error = aio_error(aio)) == EINPROGRESS && !aio_timedout) + usleep(25000); + switch (error) { + case EINPROGRESS: + errno = EINTR; + return (-1); + case 0: + return (aio_return(aio)); + default: + return (error); + } +} + +static ssize_t +suspend(struct aiocb *aio) { + const struct aiocb *const iocbs[] = {aio}; + int error; + + error = aio_suspend(iocbs, 1, NULL); + if (error == 0) + return (aio_return(aio)); + else + return (error); +} + +static ssize_t +waitcomplete(struct aiocb *aio) { + struct aiocb *aiop; + ssize_t ret; + + ret = aio_waitcomplete(&aiop, NULL); + ATF_REQUIRE_EQ(aio, aiop); + return (ret); +} + /* * Each tester can register a callback to clean up in the event the test * fails. Preserve the value of errno so that subsequent calls to errx() @@ -201,13 +247,11 @@ aio_cleanup(struct aio_context *ac) * file descriptor. */ static void -aio_write_test(struct aio_context *ac) +aio_write_test(struct aio_context *ac, completion comp) { - struct aiocb aio, *aiop; + struct aiocb aio; ssize_t len; - ATF_REQUIRE_KERNEL_MODULE("aio"); - bzero(&aio, sizeof(aio)); aio.aio_buf = ac->ac_buffer; aio.aio_nbytes = ac->ac_buflen; @@ -227,24 +271,23 @@ aio_write_test(struct aio_context *ac) atf_tc_fail("aio_write failed: %s", strerror(errno)); } - len = aio_waitcomplete(&aiop, NULL); + len = comp(&aio); if (len < 0) { if (errno == EINTR) { if (aio_timedout) { aio_cleanup(ac); - atf_tc_fail("aio_waitcomplete timed out"); + atf_tc_fail("aio timed out"); } } aio_cleanup(ac); - atf_tc_fail("aio_waitcomplete failed: %s", strerror(errno)); + atf_tc_fail("aio failed: %s", strerror(errno)); } aio_timeout_stop(); if (len != ac->ac_buflen) { aio_cleanup(ac); - atf_tc_fail("aio_waitcomplete short write (%jd)", - (intmax_t)len); + atf_tc_fail("aio short write (%jd)", (intmax_t)len); } } @@ -253,13 +296,11 @@ aio_write_test(struct aio_context *ac) * provided file descriptor. */ static void -aio_read_test(struct aio_context *ac) +aio_read_test(struct aio_context *ac, completion comp) { - struct aiocb aio, *aiop; + struct aiocb aio; ssize_t len; - ATF_REQUIRE_KERNEL_MODULE("aio"); - bzero(ac->ac_buffer, ac->ac_buflen); bzero(&aio, sizeof(aio)); aio.aio_buf = ac->ac_buffer; @@ -273,30 +314,30 @@ aio_read_test(struct aio_context *ac) if (errno == EINTR) { if (aio_timedout) { aio_cleanup(ac); - atf_tc_fail("aio_write timed out"); + atf_tc_fail("aio_read timed out"); } } aio_cleanup(ac); atf_tc_fail("aio_read failed: %s", strerror(errno)); } - len = aio_waitcomplete(&aiop, NULL); + len = comp(&aio); if (len < 0) { if (errno == EINTR) { if (aio_timedout) { aio_cleanup(ac); - atf_tc_fail("aio_waitcomplete timed out"); + atf_tc_fail("aio timed out"); } } aio_cleanup(ac); - atf_tc_fail("aio_waitcomplete failed: %s", strerror(errno)); + atf_tc_fail("aio failed: %s", strerror(errno)); } aio_timeout_stop(); if (len != ac->ac_buflen) { aio_cleanup(ac); - atf_tc_fail("aio_waitcomplete short read (%jd)", + atf_tc_fail("aio short read (%jd)", (intmax_t)len); } @@ -316,9 +357,11 @@ aio_read_test(struct aio_context *ac) * Test with a classic file. Assumes we can create a moderate size temporary * file. */ +#define FILE_LEN GLOBAL_MAX +#define FILE_PATHNAME "testfile" +#define FILE_TIMEOUT 30 struct aio_file_arg { int afa_fd; - char *afa_pathname; }; static void @@ -328,15 +371,12 @@ aio_file_cleanup(void *arg) afa = arg; close(afa->afa_fd); - unlink(afa->afa_pathname); + unlink(FILE_PATHNAME); } -#define FILE_LEN GLOBAL_MAX -#define FILE_TIMEOUT 30 -ATF_TC_WITHOUT_HEAD(aio_file_test); -ATF_TC_BODY(aio_file_test, tc) +static void +aio_file_test(completion comp) { - char pathname[PATH_MAX]; struct aio_file_arg arg; struct aio_context ac; int fd; @@ -344,25 +384,43 @@ ATF_TC_BODY(aio_file_test, tc) ATF_REQUIRE_KERNEL_MODULE("aio"); ATF_REQUIRE_UNSAFE_AIO(); - strcpy(pathname, PATH_TEMPLATE); - fd = mkstemp(pathname); - ATF_REQUIRE_MSG(fd != -1, "mkstemp failed: %s", strerror(errno)); + fd = open(FILE_PATHNAME, O_RDWR | O_CREAT); + ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); arg.afa_fd = fd; - arg.afa_pathname = pathname; aio_context_init(&ac, fd, fd, FILE_LEN, FILE_TIMEOUT, aio_file_cleanup, &arg); - aio_write_test(&ac); - aio_read_test(&ac); + aio_write_test(&ac, comp); + aio_read_test(&ac, comp); aio_file_cleanup(&arg); } +ATF_TC_WITHOUT_HEAD(file_poll); +ATF_TC_BODY(file_poll, tc) +{ + aio_file_test(poll); +} + +ATF_TC_WITHOUT_HEAD(file_suspend); +ATF_TC_BODY(file_suspend, tc) +{ + aio_file_test(suspend); +} + +ATF_TC_WITHOUT_HEAD(file_waitcomplete); +ATF_TC_BODY(file_waitcomplete, tc) +{ + aio_file_test(waitcomplete); +} + +#define FIFO_LEN 256 +#define FIFO_PATHNAME "testfifo" +#define FIFO_TIMEOUT 30 struct aio_fifo_arg { int afa_read_fd; int afa_write_fd; - char *afa_pathname; }; static void @@ -375,39 +433,25 @@ aio_fifo_cleanup(void *arg) close(afa->afa_read_fd); if (afa->afa_write_fd != -1) close(afa->afa_write_fd); - unlink(afa->afa_pathname); + unlink(FIFO_PATHNAME); } -#define FIFO_LEN 256 -#define FIFO_TIMEOUT 30 -ATF_TC_WITHOUT_HEAD(aio_fifo_test); -ATF_TC_BODY(aio_fifo_test, tc) +static void +aio_fifo_test(completion comp) { int error, read_fd = -1, write_fd = -1; struct aio_fifo_arg arg; - char pathname[PATH_MAX]; struct aio_context ac; ATF_REQUIRE_KERNEL_MODULE("aio"); ATF_REQUIRE_UNSAFE_AIO(); - /* - * In theory, mkstemp() can return a name that is then collided with. - * Because this is a regression test, we treat that as a test failure - * rather than retrying. - */ - strcpy(pathname, PATH_TEMPLATE); - ATF_REQUIRE_MSG(mkstemp(pathname) != -1, - "mkstemp failed: %s", strerror(errno)); - ATF_REQUIRE_MSG(unlink(pathname) == 0, - "unlink failed: %s", strerror(errno)); - ATF_REQUIRE_MSG(mkfifo(pathname, 0600) != -1, + ATF_REQUIRE_MSG(mkfifo(FIFO_PATHNAME, 0600) != -1, "mkfifo failed: %s", strerror(errno)); - arg.afa_pathname = pathname; arg.afa_read_fd = -1; arg.afa_write_fd = -1; - read_fd = open(pathname, O_RDONLY | O_NONBLOCK); + read_fd = open(FIFO_PATHNAME, O_RDONLY | O_NONBLOCK); if (read_fd == -1) { error = errno; aio_fifo_cleanup(&arg); @@ -417,7 +461,7 @@ ATF_TC_BODY(aio_fifo_test, tc) } arg.afa_read_fd = read_fd; - write_fd = open(pathname, O_WRONLY); + write_fd = open(FIFO_PATHNAME, O_WRONLY); if (write_fd == -1) { error = errno; aio_fifo_cleanup(&arg); @@ -429,12 +473,30 @@ ATF_TC_BODY(aio_fifo_test, tc) aio_context_init(&ac, read_fd, write_fd, FIFO_LEN, FIFO_TIMEOUT, aio_fifo_cleanup, &arg); - aio_write_test(&ac); - aio_read_test(&ac); + aio_write_test(&ac, comp); + aio_read_test(&ac, comp); aio_fifo_cleanup(&arg); } +ATF_TC_WITHOUT_HEAD(fifo_poll); +ATF_TC_BODY(fifo_poll, tc) +{ + aio_fifo_test(poll); +} + +ATF_TC_WITHOUT_HEAD(fifo_suspend); +ATF_TC_BODY(fifo_suspend, tc) +{ + aio_fifo_test(waitcomplete); +} + +ATF_TC_WITHOUT_HEAD(fifo_waitcomplete); +ATF_TC_BODY(fifo_waitcomplete, tc) +{ + aio_fifo_test(waitcomplete); +} + struct aio_unix_socketpair_arg { int asa_sockets[2]; }; @@ -451,8 +513,8 @@ aio_unix_socketpair_cleanup(void *arg) #define UNIX_SOCKETPAIR_LEN 256 #define UNIX_SOCKETPAIR_TIMEOUT 30 -ATF_TC_WITHOUT_HEAD(aio_unix_socketpair_test); -ATF_TC_BODY(aio_unix_socketpair_test, tc) +static void +aio_unix_socketpair_test(completion comp) { struct aio_unix_socketpair_arg arg; struct aio_context ac; @@ -471,12 +533,12 @@ ATF_TC_BODY(aio_unix_socketpair_test, tc) aio_unix_socketpair_cleanup, &arg); ATF_REQUIRE_MSG(getrusage(RUSAGE_SELF, &ru_before) != -1, "getrusage failed: %s", strerror(errno)); - aio_write_test(&ac); + aio_write_test(&ac, comp); ATF_REQUIRE_MSG(getrusage(RUSAGE_SELF, &ru_after) != -1, "getrusage failed: %s", strerror(errno)); ATF_REQUIRE(ru_after.ru_msgsnd == ru_before.ru_msgsnd + 1); ru_before = ru_after; - aio_read_test(&ac); + aio_read_test(&ac, comp); ATF_REQUIRE_MSG(getrusage(RUSAGE_SELF, &ru_after) != -1, "getrusage failed: %s", strerror(errno)); ATF_REQUIRE(ru_after.ru_msgrcv == ru_before.ru_msgrcv + 1); @@ -484,6 +546,24 @@ ATF_TC_BODY(aio_unix_socketpair_test, tc) aio_unix_socketpair_cleanup(&arg); } +ATF_TC_WITHOUT_HEAD(socket_poll); +ATF_TC_BODY(socket_poll, tc) +{ + aio_unix_socketpair_test(poll); +} + +ATF_TC_WITHOUT_HEAD(socket_suspend); +ATF_TC_BODY(socket_suspend, tc) +{ + aio_unix_socketpair_test(suspend); +} + +ATF_TC_WITHOUT_HEAD(socket_waitcomplete); +ATF_TC_BODY(socket_waitcomplete, tc) +{ + aio_unix_socketpair_test(waitcomplete); +} + struct aio_pty_arg { int apa_read_fd; int apa_write_fd; @@ -501,8 +581,8 @@ aio_pty_cleanup(void *arg) #define PTY_LEN 256 #define PTY_TIMEOUT 30 -ATF_TC_WITHOUT_HEAD(aio_pty_test); -ATF_TC_BODY(aio_pty_test, tc) +static void +aio_pty_test(completion comp) { struct aio_pty_arg arg; struct aio_context ac; @@ -535,12 +615,30 @@ ATF_TC_BODY(aio_pty_test, tc) aio_context_init(&ac, read_fd, write_fd, PTY_LEN, PTY_TIMEOUT, aio_pty_cleanup, &arg); - aio_write_test(&ac); - aio_read_test(&ac); + aio_write_test(&ac, comp); + aio_read_test(&ac, comp); aio_pty_cleanup(&arg); } +ATF_TC_WITHOUT_HEAD(pty_poll); +ATF_TC_BODY(pty_poll, tc) +{ + aio_pty_test(poll); +} + +ATF_TC_WITHOUT_HEAD(pty_suspend); +ATF_TC_BODY(pty_suspend, tc) +{ + aio_pty_test(suspend); +} + +ATF_TC_WITHOUT_HEAD(pty_waitcomplete); +ATF_TC_BODY(pty_waitcomplete, tc) +{ + aio_pty_test(waitcomplete); +} + static void aio_pipe_cleanup(void *arg) { @@ -552,8 +650,8 @@ aio_pipe_cleanup(void *arg) #define PIPE_LEN 256 #define PIPE_TIMEOUT 30 -ATF_TC_WITHOUT_HEAD(aio_pipe_test); -ATF_TC_BODY(aio_pipe_test, tc) +static void +aio_pipe_test(completion comp) { struct aio_context ac; int pipes[2]; @@ -566,12 +664,30 @@ ATF_TC_BODY(aio_pipe_test, tc) aio_context_init(&ac, pipes[0], pipes[1], PIPE_LEN, PIPE_TIMEOUT, aio_pipe_cleanup, pipes); - aio_write_test(&ac); - aio_read_test(&ac); + aio_write_test(&ac, comp); + aio_read_test(&ac, comp); aio_pipe_cleanup(pipes); } +ATF_TC_WITHOUT_HEAD(pipe_poll); +ATF_TC_BODY(pipe_poll, tc) +{ + aio_pipe_test(poll); +} + +ATF_TC_WITHOUT_HEAD(pipe_suspend); +ATF_TC_BODY(pipe_suspend, tc) +{ + aio_pipe_test(suspend); +} + +ATF_TC_WITHOUT_HEAD(pipe_waitcomplete); +ATF_TC_BODY(pipe_waitcomplete, tc) +{ + aio_pipe_test(waitcomplete); +} + struct aio_md_arg { int ama_mdctl_fd; int ama_unit; @@ -608,13 +724,8 @@ aio_md_cleanup(void *arg) #define MD_LEN GLOBAL_MAX #define MD_TIMEOUT 30 -ATF_TC(aio_md_test); -ATF_TC_HEAD(aio_md_test, tc) -{ - - atf_tc_set_md_var(tc, "require.user", "root"); -} -ATF_TC_BODY(aio_md_test, tc) +static void +aio_md_test(completion comp) { int error, fd, mdctl_fd, unit; char pathname[PATH_MAX]; @@ -654,16 +765,48 @@ ATF_TC_BODY(aio_md_test, tc) aio_context_init(&ac, fd, fd, MD_LEN, MD_TIMEOUT, aio_md_cleanup, &arg); - aio_write_test(&ac); - aio_read_test(&ac); + aio_write_test(&ac, comp); + aio_read_test(&ac, comp); aio_md_cleanup(&arg); } +ATF_TC(md_poll); +ATF_TC_HEAD(md_poll, tc) +{ + + atf_tc_set_md_var(tc, "require.user", "root"); +} +ATF_TC_BODY(md_poll, tc) +{ + aio_md_test(poll); +} + +ATF_TC(md_suspend); +ATF_TC_HEAD(md_suspend, tc) +{ + + atf_tc_set_md_var(tc, "require.user", "root"); +} +ATF_TC_BODY(md_suspend, tc) +{ + aio_md_test(suspend); +} + +ATF_TC(md_waitcomplete); +ATF_TC_HEAD(md_waitcomplete, tc) +{ + + atf_tc_set_md_var(tc, "require.user", "root"); +} +ATF_TC_BODY(md_waitcomplete, tc) +{ + aio_md_test(waitcomplete); +} + ATF_TC_WITHOUT_HEAD(aio_large_read_test); ATF_TC_BODY(aio_large_read_test, tc) { - char pathname[PATH_MAX]; struct aiocb cb, *cbp; ssize_t nread; size_t len; @@ -689,11 +832,10 @@ ATF_TC_BODY(aio_large_read_test, tc) len = INT_MAX; #endif - strcpy(pathname, PATH_TEMPLATE); - fd = mkstemp(pathname); - ATF_REQUIRE_MSG(fd != -1, "mkstemp failed: %s", strerror(errno)); + fd = open(FILE_PATHNAME, O_RDWR | O_CREAT); + ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); - unlink(pathname); + unlink(FILE_PATHNAME); memset(&cb, 0, sizeof(cb)); cb.aio_nbytes = len; @@ -937,7 +1079,6 @@ ATF_TC_BODY(aio_fsync_test, tc) char *buffer; } buffers[16]; struct stat sb; - char pathname[PATH_MAX]; ssize_t rval; unsigned i; int fd; @@ -945,10 +1086,9 @@ ATF_TC_BODY(aio_fsync_test, tc) ATF_REQUIRE_KERNEL_MODULE("aio"); ATF_REQUIRE_UNSAFE_AIO(); - strcpy(pathname, PATH_TEMPLATE); - fd = mkstemp(pathname); - ATF_REQUIRE_MSG(fd != -1, "mkstemp failed: %s", strerror(errno)); - unlink(pathname); + fd = open(FILE_PATHNAME, O_RDWR | O_CREAT); + ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno)); + unlink(FILE_PATHNAME); ATF_REQUIRE(fstat(fd, &sb) == 0); ATF_REQUIRE(sb.st_blksize != 0); @@ -1009,12 +1149,24 @@ ATF_TC_BODY(aio_fsync_test, tc) ATF_TP_ADD_TCS(tp) { - ATF_TP_ADD_TC(tp, aio_file_test); - ATF_TP_ADD_TC(tp, aio_fifo_test); - ATF_TP_ADD_TC(tp, aio_unix_socketpair_test); - ATF_TP_ADD_TC(tp, aio_pty_test); - ATF_TP_ADD_TC(tp, aio_pipe_test); - ATF_TP_ADD_TC(tp, aio_md_test); + ATF_TP_ADD_TC(tp, file_poll); + ATF_TP_ADD_TC(tp, file_suspend); + ATF_TP_ADD_TC(tp, file_waitcomplete); + ATF_TP_ADD_TC(tp, fifo_poll); + ATF_TP_ADD_TC(tp, fifo_suspend); + ATF_TP_ADD_TC(tp, fifo_waitcomplete); + ATF_TP_ADD_TC(tp, socket_poll); + ATF_TP_ADD_TC(tp, socket_suspend); + ATF_TP_ADD_TC(tp, socket_waitcomplete); + ATF_TP_ADD_TC(tp, pty_poll); + ATF_TP_ADD_TC(tp, pty_suspend); + ATF_TP_ADD_TC(tp, pty_waitcomplete); + ATF_TP_ADD_TC(tp, pipe_poll); + ATF_TP_ADD_TC(tp, pipe_suspend); + ATF_TP_ADD_TC(tp, pipe_waitcomplete); + ATF_TP_ADD_TC(tp, md_poll); + ATF_TP_ADD_TC(tp, md_suspend); + ATF_TP_ADD_TC(tp, md_waitcomplete); ATF_TP_ADD_TC(tp, aio_large_read_test); ATF_TP_ADD_TC(tp, aio_socket_two_reads); ATF_TP_ADD_TC(tp, aio_socket_blocking_short_write); diff --git a/tests/sys/geom/class/gate/ggate_test.sh b/tests/sys/geom/class/gate/ggate_test.sh index ffdb341a4b28..162744d818df 100755 --- a/tests/sys/geom/class/gate/ggate_test.sh +++ b/tests/sys/geom/class/gate/ggate_test.sh @@ -4,7 +4,6 @@ PIDFILE=ggated.pid PLAINFILES=plainfiles PORT=33080 CONF=gg.exports -RETRIES=16 atf_test_case ggated cleanup ggated_head() @@ -21,31 +20,23 @@ ggated_body() work=$(alloc_md) src=$(alloc_md) - dd if=/dev/random of=/dev/$work bs=1m count=1 conv=notrunc - dd if=/dev/random of=/dev/$src bs=1m count=1 conv=notrunc + atf_check -e ignore -o ignore \ + dd if=/dev/random of=/dev/$work bs=1m count=1 conv=notrunc + atf_check -e ignore -o ignore \ + dd if=/dev/random of=/dev/$src bs=1m count=1 conv=notrunc echo $CONF >> $PLAINFILES echo "127.0.0.1 RW /dev/$work" > $CONF atf_check ggated -p $PORT -F $PIDFILE $CONF - for try in `jot $RETRIES`; do - ggatec create -p $PORT -u $us 127.0.0.1 /dev/$work && break - # wait for ggated to be ready - sleep 0.25 - done - if [ "$try" -eq "$RETRIES" ]; then - atf_fail "ggatec create failed" - fi + atf_check ggatec create -p $PORT -u $us 127.0.0.1 /dev/$work - for try in `jot $RETRIES`; do - dd if=/dev/${src} of=/dev/ggate${us} bs=1m count=1 conv=notrunc\ - && break - # Wait for /dev/ggate${us} to be ready - sleep 0.25 - done - if [ "$try" -eq "$RETRIES" ]; then - atf_fail "dd failed; /dev/ggate${us} isn't working" - fi + ggate_dev=/dev/ggate${us} + + wait_for_ggate_device ${ggate_dev} + + atf_check -e ignore -o ignore \ + dd if=/dev/${src} of=${ggate_dev} bs=1m count=1 conv=notrunc checksum /dev/$src /dev/$work } @@ -74,7 +65,12 @@ ggatel_file_body() atf_check ggatel create -u $us work - dd if=src of=/dev/ggate${us} bs=1m count=1 conv=notrunc + ggate_dev=/dev/ggate${us} + + wait_for_ggate_device ${ggate_dev} + + atf_check -e ignore -o ignore \ + dd if=src of=${ggate_dev} bs=1m count=1 conv=notrunc checksum src work } @@ -99,12 +95,19 @@ ggatel_md_body() work=$(alloc_md) src=$(alloc_md) - dd if=/dev/random of=$work bs=1m count=1 conv=notrunc - dd if=/dev/random of=$src bs=1m count=1 conv=notrunc + atf_check -e ignore -o ignore \ + dd if=/dev/random of=$work bs=1m count=1 conv=notrunc + atf_check -e ignore -o ignore \ + dd if=/dev/random of=$src bs=1m count=1 conv=notrunc atf_check ggatel create -u $us /dev/$work - dd if=/dev/$src of=/dev/ggate${us} bs=1m count=1 conv=notrunc + ggate_dev=/dev/ggate${us} + + wait_for_ggate_device ${ggate_dev} + + atf_check -e ignore -o ignore \ + dd if=/dev/$src of=${ggate_dev} bs=1m count=1 conv=notrunc checksum /dev/$src /dev/$work } @@ -191,3 +194,14 @@ common_cleanup() fi true } + +# Bug 204616: ggatel(8) creates /dev/ggate* asynchronously if `ggatel create` +# isn't called with `-v`. +wait_for_ggate_device() +{ + ggate_device=$1 + + while [ ! -c $ggate_device ]; do + sleep 0.5 + done +} diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile index 61b315f18d12..795752cd7779 100644 --- a/tests/sys/kern/Makefile +++ b/tests/sys/kern/Makefile @@ -29,13 +29,19 @@ NETBSD_ATF_TESTS_C+= mqueue_test CFLAGS.mqueue_test+= -I${SRCTOP}/tests LIBADD.mqueue_test+= rt +.if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "i386" +ATF_TESTS_C+= libkern_crc32 +CFLAGS.libkern_crc32+= -msse4 -DUSERSPACE_TESTING +LDADD.libkern_crc32+= ${SRCTOP}/sys/libkern/x86/crc32_sse42.c +.endif + # subr_unit.c contains functions whose prototypes lie in headers that cannot be # included in userland. But as far as subr_unit_test goes, they're effectively # static. So it's ok to disable -Wmissing-prototypes for this program. CFLAGS.subr_unit.c+= -Wno-missing-prototypes SRCS.subr_unit_test+= subr_unit.c -WARNS?= 5 +WARNS?= 3 TESTS_SUBDIRS+= acct TESTS_SUBDIRS+= execve diff --git a/tests/sys/kern/libkern_crc32.c b/tests/sys/kern/libkern_crc32.c new file mode 100644 index 000000000000..5eef045c4573 --- /dev/null +++ b/tests/sys/kern/libkern_crc32.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2017 Conrad Meyer <cem@FreeBSD.org> + * All rights reserved. + * + * 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. + * + * $FreeBSD$ + */ + +#include <sys/param.h> + +#include <stdint.h> + +#include <atf-c.h> + +extern uint32_t sse42_crc32c(uint32_t, const unsigned char *, unsigned); + +ATF_TC_WITHOUT_HEAD(crc32c_basic_correctness); +ATF_TC_BODY(crc32c_basic_correctness, tc) +{ + const uint64_t inputs[] = { + 0xf408c634b3a9142, + 0x80539e8c7c352e2b, + 0x62e9121db6e4d649, + 0x899345850ed0a286, + 0x2302df11b4a43b15, + 0xe943de7b3d35d70, + 0xdf1ff2bf41abf56b, + 0x9bc138abae315de2, + 0x31cc82e56234f0ff, + 0xce63c0cd6988e847, + 0x3e42f6b78ee352fa, + 0xfa4085436078cfa6, + 0x53349558bf670a4b, + 0x2714e10e7d722c61, + 0xc0d3261addfc6908, + 0xd1567c3181d3a1bf, + }; + const uint32_t results[] = { + 0x2ce33ede, + 0xc49cc573, + 0xb8683c96, + 0x6918660d, + 0xa904e522, + 0x52dbc42c, + 0x98863c22, + 0x894d5d2c, + 0xb003745d, + 0xfc496dbd, + 0x97d2fbb5, + 0x3c062ef1, + 0xcc2eff18, + 0x6a9b09f6, + 0x420242c1, + 0xfd562dc3, + }; + size_t i; + uint32_t act; + + ATF_REQUIRE(nitems(inputs) == nitems(results)); + + for (i = 0; i < nitems(inputs); i++) { + act = sse42_crc32c(~0, (const void *)&inputs[i], + sizeof(inputs[0])); + ATF_REQUIRE_MSG(act == results[i], + "crc32c(0x%jx) = 0x%08x, got 0x%08x", (uintmax_t)inputs[i], + results[i], act); + } +} + +ATF_TC_WITHOUT_HEAD(crc32c_alignment); +ATF_TC_BODY(crc32c_alignment, tc) +{ + const uint64_t input = 0xf408c634b3a9142; + const uint32_t result = 0x2ce33ede; + unsigned char buf[15]; + size_t i; + uint32_t act; + + + for (i = 1; i < 8; i++) { + memcpy(&buf[i], &input, sizeof(input)); + + act = sse42_crc32c(~0, (const void *)&buf[i], sizeof(input)); + ATF_REQUIRE_MSG(act == result, + "crc32c(0x%jx) = 0x%08x, got 0x%08x", (uintmax_t)input, + result, act); + } +} + +ATF_TC_WITHOUT_HEAD(crc32c_trailing_bytes); +ATF_TC_BODY(crc32c_trailing_bytes, tc) +{ + const unsigned char input[] = { + 0x87, 0x54, 0x74, 0xd2, 0xb, 0x9b, 0xdd, 0xf6, 0x68, 0x37, + 0xd4, 0x4, 0x5e, 0xa9, 0xb3 + }; + const uint32_t result = 0xec638d62; + uint32_t act; + + act = sse42_crc32c(~0, input, sizeof(input)); + ATF_REQUIRE_MSG(act == result, "expected 0x%08x, got 0x%08x", result, + act); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, crc32c_basic_correctness); + ATF_TP_ADD_TC(tp, crc32c_alignment); + ATF_TP_ADD_TC(tp, crc32c_trailing_bytes); + return (atf_no_error()); +} diff --git a/tests/sys/netinet/fibs_test.sh b/tests/sys/netinet/fibs_test.sh index 70b53e81e8da..bf5a54b451a2 100755 --- a/tests/sys/netinet/fibs_test.sh +++ b/tests/sys/netinet/fibs_test.sh @@ -75,9 +75,9 @@ arpresolve_checks_interface_fib_body() get_fibs 2 # Configure TAP interfaces - setup_tap "$FIB0" ${ADDR0} ${MASK0} + setup_tap "$FIB0" inet ${ADDR0} ${MASK0} TAP0=$TAP - setup_tap "$FIB1" ${ADDR1} ${MASK1} + setup_tap "$FIB1" inet ${ADDR1} ${MASK1} TAP1=$TAP # Simulate a crossover cable @@ -112,7 +112,7 @@ arpresolve_checks_interface_fib_cleanup() atf_test_case loopback_and_network_routes_on_nondefault_fib cleanup loopback_and_network_routes_on_nondefault_fib_head() { - atf_set "descr" "When creating and deleting loopback routes, use the interface's fib" + atf_set "descr" "When creating and deleting loopback IPv4 routes, use the interface's fib" atf_set "require.user" "root" atf_set "require.config" "fibs" } @@ -132,7 +132,7 @@ loopback_and_network_routes_on_nondefault_fib_body() get_fibs 1 # Configure a TAP interface - setup_tap ${FIB0} ${ADDR} ${MASK} + setup_tap ${FIB0} inet ${ADDR} ${MASK} # Check whether the host route exists in only the correct FIB setfib ${FIB0} netstat -rn -f inet | grep -q "^${ADDR}.*UHS.*lo0" @@ -156,7 +156,7 @@ loopback_and_network_routes_on_nondefault_fib_body() setfib 0 netstat -rn -f inet | \ grep -q "^${SUBNET}/${MASK}.*${TAPD}" if [ 0 -eq $? ]; then - setfib ${FIB0} netstat -rn -f inet + setfib 0 netstat -rn -f inet atf_fail "Network route appeared in the wrong FIB" fi } @@ -166,12 +166,70 @@ loopback_and_network_routes_on_nondefault_fib_cleanup() cleanup_tap } +atf_test_case loopback_and_network_routes_on_nondefault_fib_inet6 cleanup +loopback_and_network_routes_on_nondefault_fib_inet6_head() +{ + atf_set "descr" "When creating and deleting loopback IPv6 routes, use the interface's fib" + atf_set "require.user" "root" + atf_set "require.config" "fibs" +} + +loopback_and_network_routes_on_nondefault_fib_inet6_body() +{ + atf_expect_fail "PR196361 IPv6 network routes don't respect net.add_addr_allfibs=0" + # Configure the TAP interface to use a nonrouteable RFC3849 + # address and a non-default fib + ADDR="2001:db8::2" + SUBNET="2001:db8::" + MASK="64" + + # Check system configuration + if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then + atf_skip "This test requires net.add_addr_allfibs=0" + fi + get_fibs 1 + + # Configure a TAP interface + setup_tap ${FIB0} inet6 ${ADDR} ${MASK} + + # Check whether the host route exists in only the correct FIB + setfib ${FIB0} netstat -rn -f inet6 | grep -q "^${ADDR}.*UHS.*lo0" + if [ 0 -ne $? ]; then + setfib ${FIB0} netstat -rn -f inet6 + atf_fail "Host route did not appear in the correct FIB" + fi + setfib 0 netstat -rn -f inet6 | grep -q "^${ADDR}.*UHS.*lo0" + if [ 0 -eq $? ]; then + setfib 0 netstat -rn -f inet6 + atf_fail "Host route appeared in the wrong FIB" + fi + + # Check whether the network route exists in only the correct FIB + setfib ${FIB0} netstat -rn -f inet6 | \ + grep -q "^${SUBNET}/${MASK}.*${TAPD}" + if [ 0 -ne $? ]; then + setfib ${FIB0} netstat -rn -f inet6 + atf_fail "Network route did not appear in the correct FIB" + fi + setfib 0 netstat -rn -f inet6 | \ + grep -q "^${SUBNET}/${MASK}.*${TAPD}" + if [ 0 -eq $? ]; then + setfib 0 netstat -rn -f inet6 + atf_fail "Network route appeared in the wrong FIB" + fi +} + +loopback_and_network_routes_on_nondefault_fib_inet6_cleanup() +{ + cleanup_tap +} + # Regression test for kern/187552 atf_test_case default_route_with_multiple_fibs_on_same_subnet cleanup default_route_with_multiple_fibs_on_same_subnet_head() { - atf_set "descr" "Multiple interfaces on the same subnet but with different fibs can both have default routes" + atf_set "descr" "Multiple interfaces on the same subnet but with different fibs can both have default IPv4 routes" atf_set "require.user" "root" atf_set "require.config" "fibs" } @@ -193,9 +251,9 @@ default_route_with_multiple_fibs_on_same_subnet_body() get_fibs 2 # Configure TAP interfaces - setup_tap "$FIB0" ${ADDR0} ${MASK} + setup_tap "$FIB0" inet ${ADDR0} ${MASK} TAP0=$TAP - setup_tap "$FIB1" ${ADDR1} ${MASK} + setup_tap "$FIB1" inet ${ADDR1} ${MASK} TAP1=$TAP # Attempt to add default routes @@ -215,6 +273,53 @@ default_route_with_multiple_fibs_on_same_subnet_cleanup() cleanup_tap } +atf_test_case default_route_with_multiple_fibs_on_same_subnet_inet6 cleanup +default_route_with_multiple_fibs_on_same_subnet_inet6_head() +{ + atf_set "descr" "Multiple interfaces on the same subnet but with different fibs can both have default IPv6 routes" + atf_set "require.user" "root" + atf_set "require.config" "fibs" +} + +default_route_with_multiple_fibs_on_same_subnet_inet6_body() +{ + # Configure the TAP interfaces to use nonrouteable RFC3849 + # addresses and non-default FIBs + ADDR0="2001:db8::2" + ADDR1="2001:db8::3" + GATEWAY="2001:db8::1" + SUBNET="2001:db8::" + MASK="64" + + # Check system configuration + if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then + atf_skip "This test requires net.add_addr_allfibs=0" + fi + get_fibs 2 + + # Configure TAP interfaces + setup_tap "$FIB0" inet6 ${ADDR0} ${MASK} + TAP0=$TAP + setup_tap "$FIB1" inet6 ${ADDR1} ${MASK} + TAP1=$TAP + + # Attempt to add default routes + setfib ${FIB0} route -6 add default ${GATEWAY} + setfib ${FIB1} route -6 add default ${GATEWAY} + + # Verify that the default route exists for both fibs, with their + # respective interfaces. + atf_check -o match:"^default.*${TAP0}$" \ + setfib ${FIB0} netstat -rn -f inet6 + atf_check -o match:"^default.*${TAP1}$" \ + setfib ${FIB1} netstat -rn -f inet6 +} + +default_route_with_multiple_fibs_on_same_subnet_inet6_cleanup() +{ + cleanup_tap +} + # Regression test for PR kern/189089 # Create two tap interfaces and assign them both the same IP address but with @@ -223,7 +328,7 @@ default_route_with_multiple_fibs_on_same_subnet_cleanup() atf_test_case same_ip_multiple_ifaces_fib0 cleanup same_ip_multiple_ifaces_fib0_head() { - atf_set "descr" "Can remove an IP alias from an interface when the same IP is also assigned to another interface." + atf_set "descr" "Can remove an IPv4 alias from an interface when the same IPv4 is also assigned to another interface." atf_set "require.user" "root" atf_set "require.config" "fibs" } @@ -237,16 +342,16 @@ same_ip_multiple_ifaces_fib0_body() # of net.add_addr_allfibs # Setup the interfaces, then remove one alias. It should not panic. - setup_tap 0 ${ADDR} ${MASK0} + setup_tap 0 inet ${ADDR} ${MASK0} TAP0=${TAP} - setup_tap 0 ${ADDR} ${MASK1} + setup_tap 0 inet ${ADDR} ${MASK1} TAP1=${TAP} ifconfig ${TAP1} -alias ${ADDR} # Do it again, in the opposite order. It should not panic. - setup_tap 0 ${ADDR} ${MASK0} + setup_tap 0 inet ${ADDR} ${MASK0} TAP0=${TAP} - setup_tap 0 ${ADDR} ${MASK1} + setup_tap 0 inet ${ADDR} ${MASK1} TAP1=${TAP} ifconfig ${TAP0} -alias ${ADDR} } @@ -266,7 +371,7 @@ same_ip_multiple_ifaces_fib0_cleanup() atf_test_case same_ip_multiple_ifaces cleanup same_ip_multiple_ifaces_head() { - atf_set "descr" "Can remove an IP alias from an interface when the same IP is also assigned to another interface, on non-default FIBs." + atf_set "descr" "Can remove an IPv4 alias from an interface when the same address is also assigned to another interface, on non-default FIBs." atf_set "require.user" "root" atf_set "require.config" "fibs" } @@ -282,18 +387,18 @@ same_ip_multiple_ifaces_body() get_fibs 2 # Setup the interfaces, then remove one alias. It should not panic. - setup_tap ${FIB0} ${ADDR} ${MASK0} + setup_tap ${FIB0} inet ${ADDR} ${MASK0} TAP0=${TAP} - setup_tap ${FIB1} ${ADDR} ${MASK1} + setup_tap ${FIB1} inet ${ADDR} ${MASK1} TAP1=${TAP} ifconfig ${TAP1} -alias ${ADDR} atf_check -o not-match:"^${ADDR}[[:space:]]" \ setfib ${FIB1} netstat -rn -f inet # Do it again, in the opposite order. It should not panic. - setup_tap ${FIB0} ${ADDR} ${MASK0} + setup_tap ${FIB0} inet ${ADDR} ${MASK0} TAP0=${TAP} - setup_tap ${FIB1} ${ADDR} ${MASK1} + setup_tap ${FIB1} inet ${ADDR} ${MASK1} TAP1=${TAP} ifconfig ${TAP0} -alias ${ADDR} atf_check -o not-match:"^${ADDR}[[:space:]]" \ @@ -304,15 +409,58 @@ same_ip_multiple_ifaces_cleanup() # Due to PR kern/189088, we must destroy the interfaces in LIFO order # in order for the routes to be correctly cleaned up. for TAPD in `tail -r "tap_devices_to_cleanup"`; do + echo ifconfig ${TAPD} destroy ifconfig ${TAPD} destroy done } +atf_test_case same_ip_multiple_ifaces_inet6 cleanup +same_ip_multiple_ifaces_inet6_head() +{ + atf_set "descr" "Can remove an IPv6 alias from an interface when the same address is also assigned to another interface, on non-default FIBs." + atf_set "require.user" "root" + atf_set "require.config" "fibs" +} +same_ip_multiple_ifaces_inet6_body() +{ + ADDR="2001:db8::2" + MASK0="64" + MASK1="128" + + # Unlike most of the tests in this file, this is applicable regardless + # of net.add_addr_allfibs + get_fibs 2 + + # Setup the interfaces, then remove one alias. It should not panic. + setup_tap ${FIB0} inet6 ${ADDR} ${MASK0} + TAP0=${TAP} + setup_tap ${FIB1} inet6 ${ADDR} ${MASK1} + TAP1=${TAP} + atf_check -s exit:0 ifconfig ${TAP1} inet6 ${ADDR} -alias + atf_check -o not-match:"^${ADDR}[[:space:]]" \ + setfib ${FIB1} netstat -rn -f inet6 + ifconfig ${TAP1} destroy + ifconfig ${TAP0} destroy + + # Do it again, in the opposite order. It should not panic. + setup_tap ${FIB0} inet6 ${ADDR} ${MASK0} + TAP0=${TAP} + setup_tap ${FIB1} inet6 ${ADDR} ${MASK1} + TAP1=${TAP} + atf_check -s exit:0 ifconfig ${TAP0} inet6 ${ADDR} -alias + atf_check -o not-match:"^${ADDR}[[:space:]]" \ + setfib ${FIB0} netstat -rn -f inet6 +} +same_ip_multiple_ifaces_inet6_cleanup() +{ + cleanup_tap +} + # Regression test for kern/187550 atf_test_case subnet_route_with_multiple_fibs_on_same_subnet cleanup subnet_route_with_multiple_fibs_on_same_subnet_head() { - atf_set "descr" "Multiple FIBs can have subnet routes for the same subnet" + atf_set "descr" "Multiple FIBs can have IPv4 subnet routes for the same subnet" atf_set "require.user" "root" atf_set "require.config" "fibs" } @@ -333,8 +481,8 @@ subnet_route_with_multiple_fibs_on_same_subnet_body() get_fibs 2 # Configure TAP interfaces - setup_tap "$FIB0" ${ADDR0} ${MASK} - setup_tap "$FIB1" ${ADDR1} ${MASK} + setup_tap "$FIB0" inet ${ADDR0} ${MASK} + setup_tap "$FIB1" inet ${ADDR1} ${MASK} # Check that a subnet route exists on both fibs atf_check -o ignore setfib "$FIB0" route get $ADDR1 @@ -346,6 +494,43 @@ subnet_route_with_multiple_fibs_on_same_subnet_cleanup() cleanup_tap } +atf_test_case subnet_route_with_multiple_fibs_on_same_subnet_inet6 cleanup +subnet_route_with_multiple_fibs_on_same_subnet_inet6_head() +{ + atf_set "descr" "Multiple FIBs can have IPv6 subnet routes for the same subnet" + atf_set "require.user" "root" + atf_set "require.config" "fibs" +} + +subnet_route_with_multiple_fibs_on_same_subnet_inet6_body() +{ + # Configure the TAP interfaces to use a RFC3849 nonrouteable addresses + # and a non-default fib + ADDR0="2001:db8::2" + ADDR1="2001:db8::3" + SUBNET="2001:db8::" + MASK="64" + + # Check system configuration + if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then + atf_skip "This test requires net.add_addr_allfibs=0" + fi + get_fibs 2 + + # Configure TAP interfaces + setup_tap "$FIB0" inet6 ${ADDR0} ${MASK} + setup_tap "$FIB1" inet6 ${ADDR1} ${MASK} + + # Check that a subnet route exists on both fibs + atf_check -o ignore setfib "$FIB0" route -6 get $ADDR1 + atf_check -o ignore setfib "$FIB1" route -6 get $ADDR0 +} + +subnet_route_with_multiple_fibs_on_same_subnet_inet6_cleanup() +{ + cleanup_tap +} + # Test that source address selection works correctly for UDP packets with # SO_DONTROUTE set that are sent on non-default FIBs. # This bug was discovered with "setfib 1 netperf -t UDP_STREAM -H some_host" @@ -386,9 +571,9 @@ udp_dontroute_body() get_fibs 2 # Configure the TAP interfaces - setup_tap ${FIB0} ${ADDR0} ${MASK} + setup_tap ${FIB0} inet ${ADDR0} ${MASK} TARGET_TAP=${TAP} - setup_tap ${FIB1} ${ADDR1} ${MASK} + setup_tap ${FIB1} inet ${ADDR1} ${MASK} # Send a UDP packet with SO_DONTROUTE. In the failure case, it will # return ENETUNREACH, or send the packet to the wrong tap @@ -397,8 +582,8 @@ udp_dontroute_body() cleanup_tap # Repeat, but this time target the other tap - setup_tap ${FIB0} ${ADDR0} ${MASK} - setup_tap ${FIB1} ${ADDR1} ${MASK} + setup_tap ${FIB0} inet ${ADDR0} ${MASK} + setup_tap ${FIB1} inet ${ADDR1} ${MASK} TARGET_TAP=${TAP} atf_check -o ignore setfib ${FIB1} \ @@ -415,10 +600,14 @@ atf_init_test_cases() { atf_add_test_case arpresolve_checks_interface_fib atf_add_test_case loopback_and_network_routes_on_nondefault_fib + atf_add_test_case loopback_and_network_routes_on_nondefault_fib_inet6 atf_add_test_case default_route_with_multiple_fibs_on_same_subnet + atf_add_test_case default_route_with_multiple_fibs_on_same_subnet_inet6 atf_add_test_case same_ip_multiple_ifaces_fib0 atf_add_test_case same_ip_multiple_ifaces + atf_add_test_case same_ip_multiple_ifaces_inet6 atf_add_test_case subnet_route_with_multiple_fibs_on_same_subnet + atf_add_test_case subnet_route_with_multiple_fibs_on_same_subnet_inet6 atf_add_test_case udp_dontroute } @@ -464,24 +653,27 @@ get_tap() # Create a tap(4) interface, configure it, and register it for cleanup. # parameters: # fib +# Protocol (inet or inet6) # IP address # Netmask in number of bits (eg 24 or 8) # Return: the tap interface name as the env variable TAP setup_tap() { local FIB=$1 - local ADDR=$2 - local MASK=$3 + local PROTO=$2 + local ADDR=$3 + local MASK=$4 get_tap - echo setfib ${FIB} ifconfig $TAP ${ADDR}/${MASK} fib $FIB - setfib ${FIB} ifconfig $TAP ${ADDR}/${MASK} fib $FIB + echo setfib ${FIB} ifconfig $TAP ${PROTO} ${ADDR}/${MASK} fib $FIB + setfib ${FIB} ifconfig $TAP ${PROTO} ${ADDR}/${MASK} fib $FIB } cleanup_tap() { if [ -f tap_devices_to_cleanup ]; then for tap_device in $(cat tap_devices_to_cleanup); do - ifconfig "${tap_device}" destroy + echo ifconfig "${tap_device}" destroy + ifconfig "${tap_device}" destroy 2>/dev/null || true done rm -f tap_devices_to_cleanup fi |