summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author5eraph <5eraph@protonmail.com>2020-04-29 20:15:33 +0200
committer5eraph <5eraph@protonmail.com>2020-05-03 15:32:10 +0200
commit70af0e81e6c580a8cc75ce7dc267aaff22c97b1c (patch)
tree55d476d95e9e21861fd27b5284df753b566fc563
parent4367de7c3361c344155220a4e999ffd7432dad81 (diff)
support slirp configuration v2 and v3
Signed-off-by: Bohumil Cervenka <5eraph@protonmail.com>
-rw-r--r--Makefile.am2
-rw-r--r--main.c174
-rw-r--r--slirp4netns.1.md29
-rw-r--r--slirp4netns.c18
-rw-r--r--slirp4netns.h37
-rwxr-xr-xtests/common.sh12
-rwxr-xr-xtests/test-slirp4netns-disable-dns.sh35
-rwxr-xr-xtests/test-slirp4netns-outbound-addr.sh53
8 files changed, 339 insertions, 21 deletions
diff --git a/Makefile.am b/Makefile.am
index 0b8e2e8..a8247f7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5,7 +5,7 @@ AM_CFLAGS = @GLIB_CFLAGS@ @SLIRP_CFLAGS@ @LIBCAP_CFLAGS@ @LIBSECCOMP_CFLAGS@
noinst_LIBRARIES = libparson.a
AM_TESTS_ENVIRONMENT = PATH="$(abs_top_builddir):$(PATH)"
-TESTS = tests/test-slirp4netns.sh tests/test-slirp4netns-configure.sh tests/test-slirp4netns-exit-fd.sh tests/test-slirp4netns-ready-fd.sh tests/test-slirp4netns-api-socket.sh tests/test-slirp4netns-disable-host-loopback.sh tests/test-slirp4netns-cidr.sh
+TESTS = tests/test-slirp4netns.sh tests/test-slirp4netns-configure.sh tests/test-slirp4netns-exit-fd.sh tests/test-slirp4netns-ready-fd.sh tests/test-slirp4netns-api-socket.sh tests/test-slirp4netns-disable-host-loopback.sh tests/test-slirp4netns-cidr.sh tests/test-slirp4netns-outbound-addr.sh tests/test-slirp4netns-disable-dns.sh
EXTRA_DIST = \
slirp4netns.1.md \
diff --git a/main.c b/main.c
index 2272368..591dfbf 100644
--- a/main.c
+++ b/main.c
@@ -21,6 +21,7 @@
#include <regex.h>
#include <libslirp.h>
#include "slirp4netns.h"
+#include <ifaddrs.h>
#define DEFAULT_MTU (1500)
#define DEFAULT_CIDR ("10.0.2.0/24")
@@ -271,6 +272,19 @@ static int parent(int sock, int ready_fd, int exit_fd, const char *api_socket,
if (api_socket != NULL) {
printf("* API Socket: %s\n", api_socket);
}
+#if SLIRP_CONFIG_VERSION_MAX >= 2
+ if (cfg->enable_outbound_addr) {
+ printf("* Outbound IPv4: %s\n",
+ inet_ntoa(cfg->outbound_addr.sin_addr));
+ }
+ if (cfg->enable_outbound_addr6) {
+ char str[INET6_ADDRSTRLEN];
+ if (inet_ntop(AF_INET6, &cfg->outbound_addr6.sin6_addr, str,
+ INET6_ADDRSTRLEN) != NULL) {
+ printf("* Outbound IPv6: %s\n", str);
+ }
+ }
+#endif
if (!cfg->disable_host_loopback) {
printf(
"WARNING: 127.0.0.1:* on the host is accessible as %s (set "
@@ -332,6 +346,17 @@ static void usage(const char *argv0)
"caps except CAP_NET_BIND_SERVICE if running as the root)\n");
printf("--enable-seccomp enable seccomp to limit syscalls "
"(experimental)\n");
+ /* v1.1.0 */
+#if SLIRP_CONFIG_VERSION_MAX >= 2
+ printf("--outbound-addr=IPv4 sets outbound ipv4 address to bound to "
+ "(experimental)\n");
+ printf("--outbound-addr6=IPv6 sets outbound ipv6 address to bound to "
+ "(experimental)\n");
+#endif
+#if SLIRP_CONFIG_VERSION_MAX >= 3
+ printf("--disable-dns disables 10.0.2.3 (or configured internal "
+ "ip) to host dns redirect (experimental)\n");
+#endif
/* others */
printf("-h, --help show this help and exit\n");
printf("-v, --version show version and exit\n");
@@ -345,24 +370,28 @@ static void version()
printf("commit: %s\n", COMMIT);
#endif
printf("libslirp: %s\n", slirp_version_string());
+ printf("SLIRP_CONFIG_VERSION_MAX: %d\n", SLIRP_CONFIG_VERSION_MAX);
}
struct options {
- pid_t target_pid; // argv[1]
char *tapname; // argv[2]
- bool do_config_network; // -c
- int exit_fd; // -e
- int ready_fd; // -r
- unsigned int mtu; // -m
- bool disable_host_loopback; // --disable-host-loopback
char *cidr; // --cidr
- bool enable_ipv6; // -6
char *api_socket; // -a
char *netns_type; // argv[1]
char *netns_path; // --netns-path
char *userns_path; // --userns-path
+ char *outbound_addr; // --outbound-addr
+ char *outbound_addr6; // --outbound-addr6
+ pid_t target_pid; // argv[1]
+ int exit_fd; // -e
+ int ready_fd; // -r
+ unsigned int mtu; // -m
+ bool do_config_network; // -c
+ bool disable_host_loopback; // --disable-host-loopback
+ bool enable_ipv6; // -6
bool enable_sandbox; // --enable-sandbox
bool enable_seccomp; // --enable-seccomp
+ bool disable_dns; // --disable-dns
};
static void options_init(struct options *options)
@@ -398,6 +427,14 @@ static void options_destroy(struct options *options)
free(options->userns_path);
options->userns_path = NULL;
}
+ if (options->outbound_addr != NULL) {
+ free(options->outbound_addr);
+ options->outbound_addr = NULL;
+ }
+ if (options->outbound_addr6 != NULL) {
+ free(options->outbound_addr6);
+ options->outbound_addr6 = NULL;
+ }
}
// * caller does not need to call options_init()
@@ -411,12 +448,17 @@ static void parse_args(int argc, char *const argv[], struct options *options)
char *optarg_netns_type = NULL;
char *optarg_userns_path = NULL;
char *optarg_api_socket = NULL;
+ char *optarg_outbound_addr = NULL;
+ char *optarg_outbound_addr6 = NULL;
#define CIDR -42
#define DISABLE_HOST_LOOPBACK -43
#define NETNS_TYPE -44
#define USERNS_PATH -45
#define ENABLE_SANDBOX -46
#define ENABLE_SECCOMP -47
+#define OUTBOUND_ADDR -48
+#define OUTBOUND_ADDR6 -49
+#define DISABLE_DNS -50
#define _DEPRECATED_NO_HOST_LOOPBACK \
-10043 // deprecated in favor of disable-host-loopback
#define _DEPRECATED_CREATE_SANDBOX \
@@ -438,6 +480,9 @@ static void parse_args(int argc, char *const argv[], struct options *options)
{ "enable-seccomp", no_argument, NULL, ENABLE_SECCOMP },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'v' },
+ { "outbound-addr", required_argument, NULL, OUTBOUND_ADDR },
+ { "outbound-addr6", required_argument, NULL, OUTBOUND_ADDR6 },
+ { "disable-dns", no_argument, NULL, DISABLE_DNS },
{ 0, 0, 0, 0 },
};
options_init(options);
@@ -526,6 +571,17 @@ static void parse_args(int argc, char *const argv[], struct options *options)
version();
exit(EXIT_SUCCESS);
break;
+ case OUTBOUND_ADDR:
+ printf("WARNING: Support for --outbount-addr is experimental\n");
+ optarg_outbound_addr = optarg;
+ break;
+ case OUTBOUND_ADDR6:
+ printf("WARNING: Support for --outbount-addr6 is experimental\n");
+ optarg_outbound_addr6 = optarg;
+ break;
+ case DISABLE_DNS:
+ options->disable_dns = true;
+ break;
default:
goto error;
break;
@@ -543,6 +599,12 @@ static void parse_args(int argc, char *const argv[], struct options *options)
if (optarg_api_socket != NULL) {
options->api_socket = strdup(optarg_api_socket);
}
+ if (optarg_outbound_addr != NULL) {
+ options->outbound_addr = strdup(optarg_outbound_addr);
+ }
+ if (optarg_outbound_addr6 != NULL) {
+ options->outbound_addr6 = strdup(optarg_outbound_addr6);
+ }
#undef CIDR
#undef DISABLE_HOST_LOOPBACK
#undef NETNS_TYPE
@@ -550,6 +612,9 @@ static void parse_args(int argc, char *const argv[], struct options *options)
#undef _DEPRECATED_NO_HOST_LOOPBACK
#undef ENABLE_SANDBOX
#undef ENABLE_SECCOMP
+#undef OUTBOUND_ADDR
+#undef OUTBOUND_ADDR6
+#undef DISABLE_DNS
if (argc - optind < 2) {
goto error;
}
@@ -670,6 +735,36 @@ finish:
return rc;
}
+static int get_interface_addr(const char *interface, int af, void *addr)
+{
+ struct ifaddrs *ifaddr, *ifa;
+ if (interface == NULL)
+ return -1;
+
+ if (getifaddrs(&ifaddr) == -1) {
+ fprintf(stderr, "getifaddrs failed to obtain interface addresses");
+ return -1;
+ }
+
+ for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr == NULL || ifa->ifa_name == NULL)
+ continue;
+ if (ifa->ifa_addr->sa_family == af) {
+ if (strcmp(ifa->ifa_name, interface) == 0) {
+ if (af == AF_INET) {
+ *(struct in_addr *)addr =
+ ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
+ } else {
+ *(struct in6_addr *)addr =
+ ((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
+ }
+ return 0;
+ }
+ }
+ }
+ return -1;
+}
+
static int slirp4netns_config_from_options(struct slirp4netns_config *cfg,
struct options *opt)
{
@@ -684,6 +779,71 @@ static int slirp4netns_config_from_options(struct slirp4netns_config *cfg,
cfg->disable_host_loopback = opt->disable_host_loopback;
cfg->enable_sandbox = opt->enable_sandbox;
cfg->enable_seccomp = opt->enable_seccomp;
+
+#if SLIRP_CONFIG_VERSION_MAX >= 2
+ cfg->enable_outbound_addr = false;
+ cfg->enable_outbound_addr6 = false;
+#endif
+
+ if (opt->outbound_addr != NULL) {
+#if SLIRP_CONFIG_VERSION_MAX >= 2
+ cfg->outbound_addr.sin_family = AF_INET;
+ cfg->outbound_addr.sin_port = 0; // Any local port will do
+ if (inet_pton(AF_INET, opt->outbound_addr,
+ &cfg->outbound_addr.sin_addr) == 1) {
+ cfg->enable_outbound_addr = true;
+ } else {
+ if (get_interface_addr(opt->outbound_addr, AF_INET,
+ &cfg->outbound_addr.sin_addr) != 0) {
+ fprintf(stderr, "outbound-addr has to be valid ipv4 address or "
+ "interface name.");
+ rc = -1;
+ goto finish;
+ }
+ cfg->enable_outbound_addr = true;
+ }
+#else
+ fprintf(stderr, "slirp4netns has to be compiled against libslrip 4.2.0 "
+ "or newer for --outbound-addr support.");
+ rc = -1;
+ goto finish;
+#endif
+ }
+ if (opt->outbound_addr6 != NULL) {
+#if SLIRP_CONFIG_VERSION_MAX >= 2
+ cfg->outbound_addr6.sin6_family = AF_INET6;
+ cfg->outbound_addr6.sin6_port = 0; // Any local port will do
+ if (inet_pton(AF_INET6, opt->outbound_addr6,
+ &cfg->outbound_addr6.sin6_addr) == 1) {
+ cfg->enable_outbound_addr6 = true;
+ } else {
+ if (get_interface_addr(opt->outbound_addr, AF_INET6,
+ &cfg->outbound_addr6.sin6_addr) != 0) {
+ fprintf(stderr, "outbound-addr has to be valid ipv4 address or "
+ "iterface name.");
+ rc = -1;
+ goto finish;
+ }
+ cfg->enable_outbound_addr6 = true;
+ }
+#else
+ fprintf(stderr, "slirp4netns has to be compiled against libslirp 4.2.0 "
+ "or newer for --outbound-addr6 support.");
+ rc = -1;
+ goto finish;
+#endif
+ }
+
+#if SLIRP_CONFIG_VERSION_MAX >= 3
+ cfg->disable_dns = opt->disable_dns;
+#else
+ if (opt->disable_dns) {
+ fprintf(stderr, "slirp4netns has to be compiled against libslirp 4.3.0 "
+ "or newer for --disable-dns support.");
+ rc = -1;
+ goto finish;
+ }
+#endif
finish:
return rc;
}
diff --git a/slirp4netns.1.md b/slirp4netns.1.md
index 445aebb..d1e7daf 100644
--- a/slirp4netns.1.md
+++ b/slirp4netns.1.md
@@ -75,12 +75,28 @@ the capabilities except `CAP_NET_BIND_SERVICE` are dropped.
enable **seccomp(2)** to limit syscalls.
Typically used in conjunction with **--enable-sandbox**.
+**--outbound-addr=IPv4** (since v1.1.0, EXPERIMENTAL)
+specify outbound ipv4 address slirp should bind to
+
+**--outbound-addr=INTERFACE** (since v1.1.0, EXPERIMENTAL)
+specify outbound interface slirp should bind to (ipv4 traffic only)
+
+**--outbound-addr=IPv6** (since v1.1.0, EXPERIMENTAL)
+specify outbound ipv6 address slirp should bind to
+
+**--outbound-addr6=INTERFACE** (since v1.1.0, EXPERIMENTAL)
+specify outbound interface slirp should bind to (ipv6 traffic only)
+
+**--disable-dns** (since v1.1.0)
+disable built-in DNS (10.0.2.3 by default)
+
**-h**, **--help** (since v0.2.0)
show help and exit
**-v**, **--version** (since v0.2.0)
show version and exit
+
# EXAMPLE
Terminal 1: Create user/network/mount namespaces
@@ -215,6 +231,19 @@ Additionally, a **--userns-path=PATH** argument can be included to override any
$ slirp4netns --netns-type=path --userns-path=/path/to/userns /path/to/netns tap0
```
+# OUTBOUND ADDRESSES
+A user can defined preferred outbound ipv4 and ipv6 address in multi IP scenarios.
+
+```console
+$ slirp4netns --outbound-addr=10.2.2.10 --outbound-addr6=fe80::10 ...
+```
+
+Optionally you can use interface names instead of ip addresses.
+
+```console
+$ slirp4netns --outbound-addr=eth0 --outbound-addr6=eth0 ...
+```
+
# BUGS
Kernel 4.20 bumped up the default value of **/proc/sys/net/ipv4/tcp_rmem** from 87380 to 131072.
diff --git a/slirp4netns.c b/slirp4netns.c
index 4b16af7..8748808 100644
--- a/slirp4netns.c
+++ b/slirp4netns.c
@@ -275,6 +275,24 @@ Slirp *create_slirp(void *opaque, struct slirp4netns_config *s4nn)
cfg.if_mtu = s4nn->mtu;
cfg.if_mru = s4nn->mtu;
cfg.disable_host_loopback = s4nn->disable_host_loopback;
+#if SLIRP_CONFIG_VERSION_MAX >= 2
+ cfg.outbound_addr = NULL;
+ cfg.outbound_addr6 = NULL;
+ if (s4nn->enable_outbound_addr) {
+ cfg.version = 2;
+ cfg.outbound_addr = &s4nn->outbound_addr;
+ }
+ if (s4nn->enable_outbound_addr6) {
+ cfg.version = 2;
+ cfg.outbound_addr6 = &s4nn->outbound_addr6;
+ }
+#endif
+#if SLIRP_CONFIG_VERSION_MAX >= 3
+ if (s4nn->disable_dns) {
+ cfg.version = 3;
+ cfg.disable_dns = true;
+ }
+#endif
slirp = slirp_new(&cfg, &libslirp_cb, opaque);
if (slirp == NULL) {
fprintf(stderr, "slirp_new failed\n");
diff --git a/slirp4netns.h b/slirp4netns.h
index a5df49d..afe6065 100644
--- a/slirp4netns.h
+++ b/slirp4netns.h
@@ -1,21 +1,32 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef SLIRP4NETNS_H
-# define SLIRP4NETNS_H
+#define SLIRP4NETNS_H
#include <arpa/inet.h>
struct slirp4netns_config {
- unsigned int mtu;
- struct in_addr vnetwork; // 10.0.2.0
- struct in_addr vnetmask; // 255.255.255.0
- struct in_addr vhost; // 10.0.2.2
- struct in_addr vdhcp_start; // 10.0.2.15
- struct in_addr vnameserver; // 10.0.2.3
- struct in_addr recommended_vguest; // 10.0.2.100 (slirp itself is unaware of vguest)
- bool enable_ipv6;
- bool disable_host_loopback;
- bool enable_sandbox;
- bool enable_seccomp;
+ unsigned int mtu;
+ struct in_addr vnetwork; // 10.0.2.0
+ struct in_addr vnetmask; // 255.255.255.0
+ struct in_addr vhost; // 10.0.2.2
+ struct in_addr vdhcp_start; // 10.0.2.15
+ struct in_addr vnameserver; // 10.0.2.3
+ struct in_addr
+ recommended_vguest; // 10.0.2.100 (slirp itself is unaware of vguest)
+ bool enable_ipv6;
+ bool disable_host_loopback;
+ bool enable_sandbox;
+ bool enable_seccomp;
+#if SLIRP_CONFIG_VERSION_MAX >= 2
+ bool enable_outbound_addr;
+ struct sockaddr_in outbound_addr;
+ bool enable_outbound_addr6;
+ struct sockaddr_in6 outbound_addr6;
+#endif
+#if SLIRP_CONFIG_VERSION_MAX >= 3
+ bool disable_dns;
+#endif
};
-int do_slirp(int tapfd, int readyfd, int exitfd, const char *api_socket, struct slirp4netns_config *cfg);
+int do_slirp(int tapfd, int readyfd, int exitfd, const char *api_socket,
+ struct slirp4netns_config *cfg);
#endif
diff --git a/tests/common.sh b/tests/common.sh
index 5f94e89..c7f9aa7 100755
--- a/tests/common.sh
+++ b/tests/common.sh
@@ -50,6 +50,18 @@ function wait_for_ping_connectivity {
done
}
+function wait_for_connectivity {
+ COUNTER=0
+ while [ $COUNTER -lt 40 ]; do
+ if echo "wait_for_connectivity" | nsenter --preserve-credentials -U -n --target=$1 ncat -v $2 $3; then
+ break
+ else
+ sleep 0.5
+ fi
+ let COUNTER=COUNTER+1
+ done
+}
+
function wait_for_file_content {
# Wait for a file to get the specified content.
COUNTER=0
diff --git a/tests/test-slirp4netns-disable-dns.sh b/tests/test-slirp4netns-disable-dns.sh
new file mode 100755
index 0000000..c148f3d
--- /dev/null
+++ b/tests/test-slirp4netns-disable-dns.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+set -xeuo pipefail
+
+SLIRP_CONFIG_VERSION_MAX=$(slirp4netns -v | grep "SLIRP_CONFIG_VERSION_MAX: " | sed 's#SLIRP_CONFIG_VERSION_MAX: \(\)##')
+
+if [ "${SLIRP_CONFIG_VERSION_MAX:-0}" -lt 3 ]; then
+ printf "'--disable-dns' requires SLIRP_CONFIG_VERSION_MAX 3 or newer. Test skipped..."
+ exit 0
+fi
+
+. $(dirname $0)/common.sh
+
+port=53
+unshare -r -n sleep infinity &
+child=$!
+
+wait_for_network_namespace $child
+
+mtu=${MTU:=1500}
+slirp4netns -c --mtu $mtu --disable-dns $child tun11 &
+slirp_pid=$!
+
+wait_for_network_device $child tun11
+# ping to 10.0.2.2
+wait_for_ping_connectivity $child 10.0.2.2
+
+function cleanup() {
+ kill -9 $child $slirp_pid
+}
+trap cleanup EXIT
+
+set +e
+err=$(echo "should fail" | nsenter --preserve-credentials -U -n --target=$child ncat -v 10.0.2.3 $port 2>&1)
+set -e
+echo $err | grep "Connection timed out"
diff --git a/tests/test-slirp4netns-outbound-addr.sh b/tests/test-slirp4netns-outbound-addr.sh
new file mode 100755
index 0000000..4ad1947
--- /dev/null
+++ b/tests/test-slirp4netns-outbound-addr.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+set -xeuo pipefail
+
+SLIRP_CONFIG_VERSION_MAX=$(slirp4netns -v | grep "SLIRP_CONFIG_VERSION_MAX: " | sed 's#SLIRP_CONFIG_VERSION_MAX: \(\)##')
+
+if [ "${SLIRP_CONFIG_VERSION_MAX:-0}" -lt 2 ]; then
+ printf "'--disable-dns' requires SLIRP_CONFIG_VERSION_MAX 2 or newer. Test skipped..."
+ exit 0
+fi
+
+. $(dirname $0)/common.sh
+
+IPv4_1="127.0.0.1"
+IPv4_2=$(ip a | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p' | head -n 1)
+
+# For future ipv6 tests
+#IPv6_1="::1"
+#IPv6_2=$(ip a | sed -En 's/::1\/128//;s/.*inet6 (addr:)?([^ ]*)\/.*$/\2/p' | head -n 1)
+
+function cleanup() {
+ rm -rf ncat.log
+ kill -9 $child $slirp_pid || exit 0
+}
+trap cleanup EXIT
+
+port=12122
+mtu=${MTU:=1500}
+
+IPs=("$IPv4_1" "$IPv4_2")
+for ip in "${IPs[@]}"; do
+ ncat -l $port -v >ncat.log 2>&1 &
+ ncat1=$!
+
+ unshare -r -n sleep infinity &
+ child=$!
+
+ wait_for_network_namespace $child
+
+ slirp4netns -c --mtu $mtu --outbound-addr="$ip" $child tun11 &
+ slirp_pid=$!
+
+ wait_for_network_device $child tun11
+
+ wait_for_connectivity $child 10.0.2.2 $port
+
+ wait_process_exits $ncat1
+ if ! grep "$ip" ncat.log; then
+ printf "%s not found in ncat.log" "$ip"
+ exit 1
+ fi
+ cleanup
+ let port=port+1
+done