From 55529a2620d5fd3cc549c38bf50455748da9020f Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Sun, 25 Sep 2022 02:17:59 +0900 Subject: [PATCH] tests: (lsfd) add a case for testing SOCKNETNS column Signed-off-by: Masatake YAMATO --- tests/expected/lsfd/mkfds-unix-in-netns | 18 +++ tests/helpers/test_mkfds.c | 199 ++++++++++++++++++++++++ tests/ts/lsfd/mkfds-unix-in-netns | 91 +++++++++++ 3 files changed, 308 insertions(+) create mode 100644 tests/expected/lsfd/mkfds-unix-in-netns create mode 100755 tests/ts/lsfd/mkfds-unix-in-netns diff --git a/tests/expected/lsfd/mkfds-unix-in-netns b/tests/expected/lsfd/mkfds-unix-in-netns new file mode 100644 index 0000000000..46b6f200d8 --- /dev/null +++ b/tests/expected/lsfd/mkfds-unix-in-netns @@ -0,0 +1,18 @@ + 5 SOCK state=listen path=test_mkfds-unix-stream-ns listen stream test_mkfds-unix-stream-ns +ASSOC,STTYPE,NAME,SOCKSTATE,SOCKTYPE,UNIX.PATH: 0 +the netns for the stream socket is extracted as expectedly + 5 SOCK state=listen path=@test_mkfds-unix-stream-ns listen stream @test_mkfds-unix-stream-ns +ASSOC,STTYPE,NAME,SOCKSTATE,SOCKTYPE,UNIX.PATH: 0 +the netns for the abstract stream socket is extracted as expectedly + 5 SOCK state=unconnected path=test_mkfds-unix-dgram-ns type=dgram unconnected dgram test_mkfds-unix-dgram-ns +ASSOC,STTYPE,NAME,SOCKSTATE,SOCKTYPE,UNIX.PATH: 0 +the netns for the dgram socket is extracted as expectedly + 5 SOCK state=unconnected path=@test_mkfds-unix-dgram-ns type=dgram unconnected dgram @test_mkfds-unix-dgram-ns +ASSOC,STTYPE,NAME,SOCKSTATE,SOCKTYPE,UNIX.PATH: 0 +the netns for the abstract dgram socket is extracted as expectedly + 5 SOCK state=listen path=test_mkfds-unix-seqpacket-ns type=seqpacket listen seqpacket test_mkfds-unix-seqpacket-ns +ASSOC,STTYPE,NAME,SOCKSTATE,SOCKTYPE,UNIX.PATH: 0 +the netns for the seqpacket socket is extracted as expectedly + 5 SOCK state=listen path=@test_mkfds-unix-seqpacket-ns type=seqpacket listen seqpacket @test_mkfds-unix-seqpacket-ns +ASSOC,STTYPE,NAME,SOCKSTATE,SOCKTYPE,UNIX.PATH: 0 +the netns for the abstract seqpacket socket is extracted as expectedly diff --git a/tests/helpers/test_mkfds.c b/tests/helpers/test_mkfds.c index 86e2d2ba46..05b6db1e79 100644 --- a/tests/helpers/test_mkfds.c +++ b/tests/helpers/test_mkfds.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -1053,6 +1054,175 @@ static void *make_unix_dgram(const struct factory *factory, struct fdesc fdescs[ return NULL; } +static void *make_unix_in_new_netns(const struct factory *factory, struct fdesc fdescs[], + int argc, char ** argv) +{ + struct arg type = decode_arg("type", factory->params, argc, argv); + const char *stype = ARG_STRING(type); + + struct arg path = decode_arg("path", factory->params, argc, argv); + const char *spath = ARG_STRING(path); + + struct arg abstract = decode_arg("abstract", factory->params, argc, argv); + bool babstract = ARG_BOOLEAN(abstract); + + int typesym; + const char *typestr; + + struct sockaddr_un un; + size_t un_len = sizeof(un); + + int self_netns, tmp_netns, sd; + + if (strcmp(stype, "stream") == 0) { + typesym = SOCK_STREAM; + typestr = "STREAM"; + } else if (strcmp(stype, "seqpacket") == 0) { + typesym = SOCK_SEQPACKET; + typestr = "SEQPACKET"; + } else if (strcmp(stype, "dgram") == 0) { + typesym = SOCK_DGRAM; + typestr = "DGRAM"; + } else { + free_arg(&abstract); + free_arg(&path); + free_arg(&type); + errx(EXIT_FAILURE, _("unknown unix socket type: %s"), stype); + } + + memset(&un, 0, sizeof(un)); + un.sun_family = AF_UNIX; + if (babstract) { + strncpy(un.sun_path + 1, spath, sizeof(un.sun_path) - 1 - 1); + size_t pathlen = strlen(spath); + if (sizeof(un.sun_path) - 1 > pathlen) + un_len = sizeof(un) - sizeof(un.sun_path) + 1 + pathlen; + } else + strncpy(un.sun_path, spath, sizeof(un.sun_path) - 1 ); + + free_arg(&abstract); + free_arg(&path); + free_arg(&type); + + self_netns = open("/proc/self/ns/net", O_RDONLY); + if (self_netns < 0) + err(EXIT_FAILURE, _("failed to open /proc/self/ns/net")); + if (self_netns != fdescs[0].fd) { + if (dup2(self_netns, fdescs[0].fd) < 0) { + int e = errno; + close(self_netns); + errno = e; + err(EXIT_FAILURE, "failed to dup %d -> %d", self_netns, fdescs[0].fd); + } + close(self_netns); + self_netns = fdescs[0].fd; + } + + fdescs[0] = (struct fdesc){ + .fd = fdescs[0].fd, + .close = close_fdesc, + .data = NULL, + }; + + if (unshare(CLONE_NEWNET) < 0) { + int e = errno; + close_fdesc(self_netns, NULL); + errno = e; + err(EXIT_FAILURE, "failed in unshare"); + } + + tmp_netns = open("/proc/self/ns/net", O_RDONLY); + if (tmp_netns < 0) { + int e = errno; + close_fdesc(self_netns, NULL); + errno = e; + err(EXIT_FAILURE, _("failed to open /proc/self/ns/net for the new netns")); + } + if (tmp_netns != fdescs[1].fd) { + if (dup2(tmp_netns, fdescs[1].fd) < 0) { + int e = errno; + close_fdesc(self_netns, NULL); + close(tmp_netns); + errno = e; + err(EXIT_FAILURE, "failed to dup %d -> %d", tmp_netns, fdescs[1].fd); + } + close(tmp_netns); + tmp_netns = fdescs[1].fd; + } + + fdescs[1] = (struct fdesc){ + .fd = fdescs[1].fd, + .close = close_fdesc, + .data = NULL, + }; + + sd = socket(AF_UNIX, typesym, 0); + if (sd < 0) { + int e = errno; + close_fdesc(self_netns, NULL); + close_fdesc(tmp_netns, NULL); + errno = e; + err(EXIT_FAILURE, + _("failed to make a socket with AF_UNIX + SOCK_%s"), + typestr); + } + + if (sd != fdescs[2].fd) { + if (dup2(sd, fdescs[2].fd) < 0) { + int e = errno; + close_fdesc(self_netns, NULL); + close_fdesc(tmp_netns, NULL); + close(sd); + errno = e; + err(EXIT_FAILURE, "failed to dup %d -> %d", sd, fdescs[2].fd); + } + close(sd); + sd = fdescs[2].fd; + } + + fdescs[2] = (struct fdesc){ + .fd = fdescs[2].fd, + .close = close_unix_socket, + .data = NULL, + }; + + if (!babstract) + unlink(un.sun_path); + if (bind(sd, (const struct sockaddr *)&un, un_len) < 0) { + int e = errno; + close_fdesc(self_netns, NULL); + close_fdesc(tmp_netns, NULL); + close_unix_socket(sd, NULL); + errno = e; + err(EXIT_FAILURE, "failed to bind a socket"); + } + + if (!babstract) + fdescs[2].data = xstrdup(un.sun_path); + + if (typesym != SOCK_DGRAM) { + if (listen(sd, 1) < 0) { + int e = errno; + close_fdesc(self_netns, NULL); + close_fdesc(tmp_netns, NULL); + close_unix_socket(sd, fdescs[2].data); + errno = e; + err(EXIT_FAILURE, "failed to listen a socket"); + } + } + + if (setns(self_netns, CLONE_NEWNET) < 0) { + int e = errno; + close_fdesc(self_netns, NULL); + close_fdesc(tmp_netns, NULL); + close_unix_socket(sd, fdescs[2].data); + errno = e; + err(EXIT_FAILURE, "failed to swich back to the original net namespace"); + } + + return NULL; +} + #define PARAM_END { .name = NULL, } static const struct factory factories[] = { { @@ -1319,6 +1489,35 @@ static const struct factory factories[] = { PARAM_END }, }, + { + .name = "unix-in-netns", + .desc = "make a unix socket in a new network namespace", + .priv = true, + .N = 3, + .EX_N = 0, + .make = make_unix_in_new_netns, + .params = (struct parameter []) { + { + .name = "type", + .type = PTYPE_STRING, + .desc = "dgram, stream, or seqpacket", + .defv.string = "stream", + }, + { + .name = "path", + .type = PTYPE_STRING, + .desc = "path for unix non-stream bound to", + .defv.string = "/tmp/test_mkfds-unix-in-netns", + }, + { + .name = "abstract", + .type = PTYPE_BOOLEAN, + .desc = "use PATH as an abstract socket address", + .defv.boolean = false, + }, + PARAM_END + }, + }, }; static int count_parameters(const struct factory *factory) diff --git a/tests/ts/lsfd/mkfds-unix-in-netns b/tests/ts/lsfd/mkfds-unix-in-netns new file mode 100755 index 0000000000..d57edc6f03 --- /dev/null +++ b/tests/ts/lsfd/mkfds-unix-in-netns @@ -0,0 +1,91 @@ +#!/bin/bash +# +# Copyright (C) 2022 Masatake YAMATO +# +# This file is part of util-linux. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This file is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +TS_TOPDIR="${0%/*}/../.." +TS_DESC="UNIX sockets made in a differenct net namespace" + +. "$TS_TOPDIR"/functions.sh +ts_init "$*" +ts_skip_nonroot + +ts_check_test_command "$TS_CMD_LSFD" + +ts_check_test_command "$TS_HELPER_MKFDS" + +ts_cd "$TS_OUTDIR" + +PID= +FDSELFNS=3 +FDALTNS=4 +FDSOCK=5 + +EXPR='((TYPE == "UNIX") or (TYPE == "UNIX-STREAM")) and (FD == 5)' + +compare_net_namespaces() +{ + local type=$1 + local pid=$2 + local altns_inode + local sock_netns + + altns_inode=$(${TS_CMD_LSFD} -n -o INODE -p "${pid}" -Q '(FD == 4)') + sock_netns=$(${TS_CMD_LSFD} -n -o SOCKNETNS -p "${pid}" -Q '(FD == 5)') + + if [[ "${altns_inode}" == "${sock_netns}" ]]; then + echo "the netns for the $type socket is extracted as expectedly" + else + echo "the netns for the $type socket is not extracted well" + echo "altns_inode=${altns_inode}" + echo "sock_netns=${sock_netns}" + fi +} + +{ + for t in stream dgram seqpacket; do + coproc MKFDS { "$TS_HELPER_MKFDS" unix-in-netns $FDSELFNS $FDALTNS $FDSOCK \ + path=test_mkfds-unix-$t-ns \ + type=$t ; } + if read -r -u "${MKFDS[0]}" PID; then + ${TS_CMD_LSFD} -n \ + -o ASSOC,STTYPE,NAME,SOCKSTATE,SOCKTYPE,UNIX.PATH \ + -p "${PID}" -Q "${EXPR}" + echo 'ASSOC,STTYPE,NAME,SOCKSTATE,SOCKTYPE,UNIX.PATH': $? + + compare_net_namespaces "$t" "${PID}" + + kill -CONT "${PID}" + wait "${MKFDS_PID}" + fi + + coproc MKFDS { "$TS_HELPER_MKFDS" unix-in-netns $FDSELFNS $FDALTNS $FDSOCK \ + path=test_mkfds-unix-$t-ns \ + abstract=true \ + type=$t ; } + if read -r -u "${MKFDS[0]}" PID; then + ${TS_CMD_LSFD} -n \ + -o ASSOC,STTYPE,NAME,SOCKSTATE,SOCKTYPE,UNIX.PATH \ + -p "${PID}" -Q "${EXPR}" + echo 'ASSOC,STTYPE,NAME,SOCKSTATE,SOCKTYPE,UNIX.PATH': $? + + compare_net_namespaces "abstract $t" "${PID}" + + kill -CONT "${PID}" + wait "${MKFDS_PID}" + fi + done +} > "$TS_OUTPUT" 2>&1 + +ts_finalize -- 2.47.2