--- /dev/null
+ 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
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <net/if.h>
+#include <sched.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
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[] = {
{
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)
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2022 Masatake YAMATO <yamato@redhat.com>
+#
+# 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