Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 7dce49c

Browse files
committedSep 11, 2021
- implement forwarding - implement random CIDR Signed-off-by: fassl <[email protected]>
1 parent 631f361 commit 7dce49c

18 files changed

+977
-78
lines changed
 

‎Dockerfile.artifact

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ ARG DEBIAN_VERSION=10
33

44
FROM --platform=$TARGETPLATFORM debian:${DEBIAN_VERSION} AS build
55
ENV DEBIAN_FRONTEND=noninteractive
6-
RUN apt update && apt install -y automake autotools-dev make gcc libglib2.0-dev libcap-dev libseccomp-dev git ninja-build python3-pip
6+
RUN apt update && apt install -y automake autotools-dev make gcc libglib2.0-dev libcap-dev libseccomp-dev libssl-dev git ninja-build python3-pip
77
RUN pip3 install meson
88
RUN git clone https://gitlab.freedesktop.org/slirp/libslirp.git /libslirp
99
WORKDIR /libslirp

‎Dockerfile.buildtests

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ ARG LIBSLIRP_COMMIT=v4.6.1
22

33
# Alpine
44
FROM alpine:3 AS buildtest-alpine3-static
5-
RUN apk add --no-cache git build-base autoconf automake libtool linux-headers glib-dev glib-static libcap-static libcap-dev libseccomp-dev libseccomp-static git meson
5+
RUN apk add --no-cache git build-base autoconf automake libtool linux-headers glib-dev glib-static libcap-static libcap-dev libseccomp-dev openssl-dev openssl-libs-static libseccomp-static git meson
66
RUN git clone https://gitlab.freedesktop.org/slirp/libslirp.git /libslirp
77
WORKDIR /libslirp
88
ARG LIBSLIRP_COMMIT
@@ -14,7 +14,7 @@ RUN ./autogen.sh && ./configure LDFLAGS="-static" && make && cp -f slirp4netns /
1414
# Ubuntu
1515
FROM ubuntu:18.04 AS buildtest-ubuntu1804-common
1616
ENV DEBIAN_FRONTEND=noninteractive
17-
RUN apt update && apt install -y automake autotools-dev make gcc libglib2.0-dev libcap-dev libseccomp-dev git ninja-build python3-pip
17+
RUN apt update && apt install -y automake autotools-dev make gcc libglib2.0-dev libcap-dev libseccomp-dev libssl-dev git ninja-build python3-pip
1818
RUN pip3 install meson
1919
RUN git clone https://gitlab.freedesktop.org/slirp/libslirp.git /libslirp
2020
WORKDIR /libslirp
@@ -32,7 +32,7 @@ RUN ./configure && make && cp -f slirp4netns /
3232

3333
# openSUSE (dynamic only)
3434
FROM opensuse/leap:15 AS buildtest-opensuse15-common
35-
RUN zypper install -y --no-recommends autoconf automake gcc glib2-devel git make libcap-devel libseccomp-devel ninja python3-pip
35+
RUN zypper install -y --no-recommends autoconf automake gcc glib2-devel git make libcap-devel libseccomp-devel libopenssl-devel ninja python3-pip
3636
RUN pip3 install meson
3737
RUN git clone https://gitlab.freedesktop.org/slirp/libslirp.git /libslirp
3838
WORKDIR /libslirp

‎Dockerfile.tests

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ ARG LIBSLIRP_COMMIT=v4.6.1
22

33
FROM ubuntu:20.04 AS build
44
ENV DEBIAN_FRONTEND=noninteractive
5-
RUN apt update && apt install -y automake autotools-dev make gcc libglib2.0-dev libcap-dev libseccomp-dev git ninja-build python3-pip
5+
RUN apt update && apt install -y automake autotools-dev make gcc libglib2.0-dev libcap-dev libseccomp-dev libssl-dev git ninja-build python3-pip
66
RUN pip3 install meson
77
RUN git clone https://gitlab.freedesktop.org/slirp/libslirp.git /libslirp
88
WORKDIR /libslirp

‎Makefile.am

+6-1
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,16 @@ noinst_LIBRARIES = libparson.a
77
AM_TESTS_ENVIRONMENT = PATH="$(abs_top_builddir):$(PATH)"
88
TESTS = tests/test-slirp4netns-api-socket.sh \
99
tests/test-slirp4netns-cidr.sh \
10+
tests/test-slirp4netns-cidr6.sh \
1011
tests/test-slirp4netns-configure.sh \
1112
tests/test-slirp4netns-dhcp.sh \
1213
tests/test-slirp4netns-disable-dns.sh \
1314
tests/test-slirp4netns-disable-host-loopback.sh \
1415
tests/test-slirp4netns-exit-fd.sh \
16+
tests/test-slirp4netns-hostfwd.sh \
17+
tests/test-slirp4netns-hostfwd4.sh \
18+
tests/test-slirp4netns-hostfwd6.sh \
19+
tests/test-slirp4netns-ipv6.sh \
1520
tests/test-slirp4netns-macaddress.sh \
1621
tests/test-slirp4netns-nspath.sh \
1722
tests/test-slirp4netns-outbound-addr.sh \
@@ -47,7 +52,7 @@ libparson_a_CFLAGS = $(AM_CFLAGS) -I$(abs_top_builddir)/vendor/parson
4752
libparson_a_SOURCES = vendor/parson/parson.c
4853

4954
slirp4netns_SOURCES = main.c slirp4netns.c api.c sandbox.c seccompfilter.c
50-
slirp4netns_LDADD = libparson.a @GLIB_LIBS@ @SLIRP_LIBS@ @LIBSECCOMP_LIBS@ -lpthread
55+
slirp4netns_LDADD = libparson.a @GLIB_LIBS@ @SLIRP_LIBS@ @LIBSECCOMP_LIBS@ -lpthread -lcrypto
5156
man1_MANS = slirp4netns.1
5257

5358
generate-man:

‎README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# slirp4netns: User-mode networking for unprivileged network namespaces
1+
# slirp4netns: User-mode networking for unprivileged network namespaces
22

33
slirp4netns provides user-mode networking ("slirp") for unprivileged network namespaces.
44

@@ -73,7 +73,7 @@ Also available as a package on almost all Linux distributions:
7373
* [Arch Linux](https://www.archlinux.org/packages/community/x86_64/slirp4netns/)
7474
* [openSUSE (since Leap 15.0)](https://build.opensuse.org/package/show/openSUSE%3AFactory/slirp4netns)
7575
* [SUSE Linux Enterprise (since 15)](https://build.opensuse.org/package/show/devel%3Akubic/slirp4netns)
76-
* [Debian GNU/Linux (since 10.0)](https://packages.debian.org/buster/slirp4netns)
76+
* [Debian GNU/Linux (since 10.0)](https://packages.debian.org/buster/slirp4netns)
7777
* [Ubuntu (since 19.04)](https://packages.ubuntu.com/search?keywords=slirp4netns)
7878
* [NixOS](https://github.com/NixOS/nixpkgs/tree/master/pkgs/tools/networking/slirp4netns)
7979
* [Gentoo Linux](https://packages.gentoo.org/packages/app-emulation/slirp4netns)
@@ -126,7 +126,7 @@ starting slirp, MTU=65520
126126
link/ether c2:28:0c:0e:29:06 brd ff:ff:ff:ff:ff:ff
127127
inet 10.0.2.100/24 brd 10.0.2.255 scope global tap0
128128
valid_lft forever preferred_lft forever
129-
inet6 fe80::c028:cff:fe0e:2906/64 scope link
129+
inet6 fe80::c028:cff:fe0e:2906/64 scope link
130130
valid_lft forever preferred_lft forever
131131
(namespace)$ echo "nameserver 10.0.2.3" > /tmp/resolv.conf
132132
(namespace)$ mount --bind /tmp/resolv.conf /etc/resolv.conf
@@ -187,7 +187,7 @@ $ make
187187
$ sudo make install
188188
```
189189

190-
* [libslirp](https://gitlab.freedesktop.org/slirp/libslirp) needs to be v4.1.0 or later.
190+
* [libslirp](https://gitlab.freedesktop.org/slirp/libslirp) needs to be v4.4.0.57 or later.
191191
* To build `slirp4netns` as a static binary, run `./configure` with `LDFLAGS=-static`.
192192
* If you set `--prefix` to `$HOME`, you don't need to run `make install` with `sudo`.
193193

‎api.c

+181-46
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,13 @@ int api_bindlisten(const char *api_socket)
4141
struct api_hostfwd {
4242
int id;
4343
int is_udp;
44-
struct in_addr host_addr;
44+
int is_ipv4;
45+
int is_ipv6;
46+
struct sockaddr_in host;
47+
struct sockaddr_in guest;
48+
struct sockaddr_in6 host6;
49+
struct sockaddr_in6 guest6;
4550
int host_port;
46-
struct in_addr guest_addr;
4751
int guest_port;
4852
};
4953

@@ -107,9 +111,9 @@ static int api_handle_req_add_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx,
107111
exit(EXIT_FAILURE);
108112
}
109113
fwd->is_udp = -1; /* TODO: support SCTP */
110-
if (strcmp(proto_s, "udp") == 0) {
114+
if (strncmp(proto_s, "udp", 3) == 0) {
111115
fwd->is_udp = 1;
112-
} else if (strcmp(proto_s, "tcp") == 0) {
116+
} else if (strncmp(proto_s, "tcp", 3) == 0) {
113117
fwd->is_udp = 0;
114118
}
115119
if (fwd->is_udp == -1) {
@@ -119,16 +123,59 @@ static int api_handle_req_add_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx,
119123
free(fwd);
120124
goto finish;
121125
}
126+
int flags = (fwd->is_udp ? SLIRP_HOSTFWD_UDP : 0);
127+
122128
if (host_addr_s == NULL || host_addr_s[0] == '\0') {
123129
host_addr_s = "0.0.0.0";
124130
}
125-
if (inet_pton(AF_INET, host_addr_s, &fwd->host_addr) != 1) {
126-
const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: "
127-
"bad arguments.host_addr\"}}";
128-
wrc = write(fd, err, strlen(err));
129-
free(fwd);
130-
goto finish;
131+
if (strcmp("0.0.0.0", host_addr_s) == 0 ||
132+
strcmp("::", host_addr_s) == 0 ||
133+
strcmp("::0", host_addr_s) == 0) {
134+
fwd->is_ipv4 = 1;
135+
fwd->is_ipv6 = ctx->cfg->enable_ipv6;
136+
host_addr_s = "0.0.0.0";
137+
} else {
138+
if (strchr(host_addr_s, '.')) {
139+
fwd->is_ipv4 = 1;
140+
}
141+
else if (strchr(host_addr_s, ':')) {
142+
if (!ctx->cfg->enable_ipv6) {
143+
const char *err =
144+
"{\"error\":{\"desc\":\"bad request: add_hostfwd: "
145+
"bad arguments.host_addr\"}}";
146+
wrc = write(fd, err, strlen(err));
147+
free(fwd);
148+
goto finish;
149+
}
150+
fwd->is_ipv6 = 1;
151+
}
152+
}
153+
154+
if (strlen(proto_s) == 4) {
155+
if (proto_s[3] == '4') {
156+
fwd->is_ipv4 = 1;
157+
fwd->is_ipv6 = 0;
158+
} else if (proto_s[3] == '6') {
159+
if (!ctx->cfg->enable_ipv6) {
160+
const char *err =
161+
"{\"error\":{\"desc\":\"bad request: add_hostfwd: "
162+
"bad arguments.proto\"}}";
163+
wrc = write(fd, err, strlen(err));
164+
free(fwd);
165+
goto finish;
166+
}
167+
fwd->is_ipv4 = 0;
168+
fwd->is_ipv6 = 1;
169+
} else {
170+
const char *err =
171+
"{\"error\":{\"desc\":\"bad request: add_hostfwd: "
172+
"bad arguments.proto\"}}";
173+
wrc = write(fd, err, strlen(err));
174+
free(fwd);
175+
goto finish;
176+
}
131177
}
178+
132179
fwd->host_port = (int)json_object_dotget_number(jo, "arguments.host_port");
133180
if (fwd->host_port == 0) {
134181
const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: "
@@ -137,16 +184,6 @@ static int api_handle_req_add_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx,
137184
free(fwd);
138185
goto finish;
139186
}
140-
141-
if (guest_addr_s == NULL || guest_addr_s[0] == '\0') {
142-
fwd->guest_addr = ctx->cfg->recommended_vguest;
143-
} else if (inet_pton(AF_INET, guest_addr_s, &fwd->guest_addr) != 1) {
144-
const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: "
145-
"bad arguments.guest_addr\"}}";
146-
wrc = write(fd, err, strlen(err));
147-
free(fwd);
148-
goto finish;
149-
}
150187
fwd->guest_port =
151188
(int)json_object_dotget_number(jo, "arguments.guest_port");
152189
if (fwd->guest_port == 0) {
@@ -156,13 +193,77 @@ static int api_handle_req_add_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx,
156193
free(fwd);
157194
goto finish;
158195
}
159-
if (slirp_add_hostfwd(slirp, fwd->is_udp, fwd->host_addr, fwd->host_port,
160-
fwd->guest_addr, fwd->guest_port) < 0) {
161-
const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: "
162-
"slirp_add_hostfwd failed\"}}";
163-
wrc = write(fd, err, strlen(err));
164-
free(fwd);
165-
goto finish;
196+
197+
if (fwd->is_ipv4) {
198+
fwd->host.sin_family = AF_INET;
199+
fwd->guest.sin_family = AF_INET;
200+
fwd->host.sin_port = htons(fwd->host_port);
201+
fwd->guest.sin_port = htons(fwd->guest_port);
202+
if (guest_addr_s == NULL || guest_addr_s[0] == '\0') {
203+
fwd->guest.sin_addr = ctx->cfg->recommended_vguest;
204+
} else if (inet_pton(AF_INET, guest_addr_s, &fwd->guest.sin_addr) != 1) {
205+
const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: "
206+
"bad arguments.guest_addr\"}}";
207+
wrc = write(fd, err, strlen(err));
208+
free(fwd);
209+
goto finish;
210+
}
211+
if (inet_pton(AF_INET, host_addr_s, &fwd->host.sin_addr) != 1) {
212+
const char *err =
213+
"{\"error\":{\"desc\":\"bad request: add_hostfwd: "
214+
"bad arguments.host_addr\"}}";
215+
wrc = write(fd, err, strlen(err));
216+
free(fwd);
217+
goto finish;
218+
}
219+
if (slirp_add_hostxfwd(slirp,
220+
(const struct sockaddr*)&fwd->host, sizeof(fwd->host),
221+
(const struct sockaddr*)&fwd->guest, sizeof(fwd->guest), flags) < 0) {
222+
const char *err =
223+
"{\"error\":{\"desc\":\"bad request: add_hostfwd: "
224+
"slirp_add_hostxfwd failed\"}}";
225+
wrc = write(fd, err, strlen(err));
226+
free(fwd);
227+
goto finish;
228+
}
229+
}
230+
231+
if (fwd->is_ipv6) {
232+
fwd->host6.sin6_family = AF_INET6;
233+
fwd->guest6.sin6_family = AF_INET6;
234+
fwd->host6.sin6_port = htons(fwd->host_port);
235+
fwd->guest6.sin6_port = htons(fwd->guest_port);
236+
if (guest_addr_s == NULL || guest_addr_s[0] == '\0') {
237+
fwd->guest6.sin6_addr = ctx->cfg->recommended_vguest6;
238+
} else if (inet_pton(AF_INET6, guest_addr_s, &fwd->guest6.sin6_addr) != 1) {
239+
const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: "
240+
"bad arguments.guest_addr\"}}";
241+
wrc = write(fd, err, strlen(err));
242+
free(fwd);
243+
goto finish;
244+
}
245+
if (strcmp(host_addr_s, "0.0.0.0") == 0) {
246+
host_addr_s = "::";
247+
}
248+
if (inet_pton(AF_INET6, host_addr_s, &fwd->host6.sin6_addr) != 1) {
249+
const char *err =
250+
"{\"error\":{\"desc\":\"bad request: add_hostfwd: "
251+
"bad arguments.host_addr\"}}";
252+
wrc = write(fd, err, strlen(err));
253+
free(fwd);
254+
goto finish;
255+
}
256+
flags |= SLIRP_HOSTFWD_V6ONLY;
257+
if (slirp_add_hostxfwd(slirp,
258+
(const struct sockaddr*)&fwd->host6, sizeof(fwd->host6),
259+
(const struct sockaddr*)&fwd->guest6, sizeof(fwd->guest6), flags) < 0) {
260+
const char *err =
261+
"{\"error\":{\"desc\":\"bad request: add_hostfwd: "
262+
"slirp_add_hostxfwd failed\"}}";
263+
wrc = write(fd, err, strlen(err));
264+
free(fwd);
265+
goto finish;
266+
}
166267
}
167268
fwd->id = ctx->hostfwds_nextid;
168269
ctx->hostfwds_nextid++;
@@ -185,21 +286,46 @@ static void api_handle_req_list_hostfwd_foreach(gpointer data,
185286
JSON_Value *entry_value = json_value_init_object();
186287
JSON_Object *entry_object = json_value_get_object(entry_value);
187288
char host_addr[INET_ADDRSTRLEN], guest_addr[INET_ADDRSTRLEN];
188-
if (inet_ntop(AF_INET, &fwd->host_addr, host_addr, sizeof(host_addr)) ==
189-
NULL) {
190-
perror("fatal: inet_ntop");
191-
exit(EXIT_FAILURE);
289+
char host_addr6[INET6_ADDRSTRLEN], guest_addr6[INET6_ADDRSTRLEN];
290+
if (fwd->is_ipv4) {
291+
if (inet_ntop(AF_INET, &fwd->host.sin_addr, host_addr, sizeof(host_addr)) ==
292+
NULL) {
293+
perror("fatal: inet_ntop");
294+
exit(EXIT_FAILURE);
295+
}
296+
if (inet_ntop(AF_INET, &fwd->guest.sin_addr, guest_addr, sizeof(guest_addr)) ==
297+
NULL) {
298+
perror("fatal: inet_ntop");
299+
exit(EXIT_FAILURE);
300+
}
192301
}
193-
if (inet_ntop(AF_INET, &fwd->guest_addr, guest_addr, sizeof(guest_addr)) ==
194-
NULL) {
195-
perror("fatal: inet_ntop");
196-
exit(EXIT_FAILURE);
302+
if (fwd->is_ipv6) {
303+
if (inet_ntop(AF_INET6, &fwd->host6.sin6_addr, host_addr6, sizeof(host_addr6)) ==
304+
NULL) {
305+
perror("fatal: inet_ntop");
306+
exit(EXIT_FAILURE);
307+
}
308+
if (inet_ntop(AF_INET6, &fwd->guest6.sin6_addr, guest_addr6, sizeof(guest_addr6)) ==
309+
NULL) {
310+
perror("fatal: inet_ntop");
311+
exit(EXIT_FAILURE);
312+
}
197313
}
198314
json_object_set_number(entry_object, "id", fwd->id);
199315
json_object_set_string(entry_object, "proto", fwd->is_udp ? "udp" : "tcp");
200-
json_object_set_string(entry_object, "host_addr", host_addr);
316+
if (fwd->is_ipv4) {
317+
json_object_set_string(entry_object, "host_addr", host_addr);
318+
}
319+
if (fwd->is_ipv6) {
320+
json_object_set_string(entry_object, "host_addr6", host_addr6);
321+
}
201322
json_object_set_number(entry_object, "host_port", fwd->host_port);
202-
json_object_set_string(entry_object, "guest_addr", guest_addr);
323+
if (fwd->is_ipv4) {
324+
json_object_set_string(entry_object, "guest_addr", guest_addr);
325+
}
326+
if (fwd->is_ipv6) {
327+
json_object_set_string(entry_object, "guest_addr6", guest_addr6);
328+
}
203329
json_object_set_number(entry_object, "guest_port", fwd->guest_port);
204330
/* json_array_append_value does not copy passed value */
205331
if (json_array_append_value(entries_array, entry_value) != JSONSuccess) {
@@ -257,16 +383,25 @@ static int api_handle_req_remove_hostfwd(Slirp *slirp, int fd,
257383
} else {
258384
struct api_hostfwd *fwd = found->data;
259385
const char *api_ok = "{\"return\":{}}";
260-
if (slirp_remove_hostfwd(slirp, fwd->is_udp, fwd->host_addr,
261-
fwd->host_port) < 0) {
262-
const char *err = "{\"error\":{\"desc\":\"bad request: "
263-
"remove_hostfwd: slirp_remove_hostfwd failed\"}}";
264-
wrc = write(fd, err, strlen(err));
265-
} else {
266-
ctx->hostfwds = g_list_remove(ctx->hostfwds, fwd);
267-
g_free(fwd);
268-
wrc = write(fd, api_ok, strlen(api_ok));
386+
if (fwd->is_ipv4) {
387+
if (slirp_remove_hostxfwd(slirp,
388+
(const struct sockaddr*)&fwd->host, sizeof(fwd->host), 0) < 0) {
389+
const char *err = "{\"error\":{\"desc\":\"bad request: "
390+
"remove_hostfwd: slirp_remove_hostxfwd failed\"}}";
391+
return write(fd, err, strlen(err));
392+
}
393+
}
394+
if (fwd->is_ipv6) {
395+
if (slirp_remove_hostxfwd(slirp,
396+
(const struct sockaddr*)&fwd->host6, sizeof(fwd->host6), 0) < 0) {
397+
const char *err = "{\"error\":{\"desc\":\"bad request: "
398+
"remove_hostfwd: slirp_remove_hostxfwd failed\"}}";
399+
return write(fd, err, strlen(err));
400+
}
269401
}
402+
ctx->hostfwds = g_list_remove(ctx->hostfwds, fwd);
403+
g_free(fwd);
404+
wrc = write(fd, api_ok, strlen(api_ok));
270405
}
271406
return wrc;
272407
}

‎configure.ac

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
AC_PREREQ([2.69])
2-
AC_INIT([slirp4netns], [1.1.12+dev], [https://github.com/rootless-containers/slirp4netns/issues])
2+
AC_INIT([slirp4netns], [1.1.13+dev], [https://github.com/rootless-containers/slirp4netns/issues])
33
AC_CONFIG_SRCDIR([main.c])
44
AC_CONFIG_HEADERS([config.h])
55

@@ -36,6 +36,7 @@ PKG_CHECK_MODULES(GLIB, glib-2.0)
3636
PKG_CHECK_MODULES(SLIRP, slirp >= 4.1.0)
3737
PKG_CHECK_MODULES(LIBCAP, libcap)
3838
PKG_CHECK_MODULES(LIBSECCOMP, libseccomp)
39+
PKG_CHECK_MODULES(LIBCRYPTO, libcrypto)
3940

4041
AC_CONFIG_FILES([Makefile])
4142
AC_OUTPUT

‎main.c

+352-1
Large diffs are not rendered by default.

‎slirp4netns.1

+23-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
.nh
21
.TH SLIRP4NETNS 1 "June 2021" "Rootless Containers" "User Commands"
32

43
.SH NAME
@@ -78,6 +77,14 @@ API socket path
7877
\fB\fC\-\-cidr\fR (since v0.3.0)
7978
specify CIDR, e.g. 10.0.2.0/24
8079

80+
.PP
81+
\fB\fC\-\-cidr6\fR (since v1.1.13, EXPERIMENTAL)
82+
specify IPv6 CIDR, e.g. fd00::/64
83+
84+
.PP
85+
\fB\fC\-\-ipv6\-random\fR (since v1.1.13, EXPERIMENTAL)
86+
generate a random local IPv6 range
87+
8188
.PP
8289
\fB\fC\-\-disable\-host\-loopback\fR (since v0.3.0)
8390
prohibit connecting to 127.0.0.1:* on the host namespace
@@ -199,7 +206,7 @@ starting slirp, MTU=65520
199206
link/ether c2:28:0c:0e:29:06 brd ff:ff:ff:ff:ff:ff
200207
inet 10.0.2.100/24 brd 10.0.2.255 scope global tap0
201208
valid\_lft forever preferred\_lft forever
202-
inet6 fe80::c028:cff:fe0e:2906/64 scope link
209+
inet6 fe80::c028:cff:fe0e:2906/64 scope link
203210
valid\_lft forever preferred\_lft forever
204211
(namespace)$ echo "nameserver 10.0.2.3" > /tmp/resolv.conf
205212
(namespace)$ mount \-\-bind /tmp/resolv.conf /etc/resolv.conf
@@ -305,7 +312,7 @@ slirp4netns can provide QMP\-like API server over an UNIX socket file:
305312
.RE
306313

307314
.PP
308-
\fB\fCadd\_hostfwd\fR: Expose a port (IPv4 only)
315+
\fB\fCadd\_hostfwd\fR: Expose a port (IPv4 and IPv6 if enabled and host\_addr is "0.0.0.0" or "::")
309316

310317
.PP
311318
.RS
@@ -318,6 +325,18 @@ slirp4netns can provide QMP\-like API server over an UNIX socket file:
318325
.fi
319326
.RE
320327

328+
.PP
329+
\fB\fCadd\_hostfwd\fR: Expose a port (IPv4 only)
330+
331+
.PP
332+
specify \fB\fCtcp4\fR as \fB\fCproto\fR
333+
334+
.PP
335+
\fB\fCadd\_hostfwd\fR: Expose a port (IPv6 only)
336+
337+
.PP
338+
specify \fB\fCtcp6\fR as \fB\fCproto\fR
339+
321340
.PP
322341
If \fB\fChost\_addr\fR is not specified, then it defaults to "0.0.0.0".
323342

@@ -333,7 +352,7 @@ If \fB\fCguest\_addr\fR is not specified, then it will be set to the default add
333352
.nf
334353
(namespace)$ json='{"execute": "list\_hostfwd"}'
335354
(namespace)$ echo \-n $json | nc \-U /tmp/slirp4netns.sock
336-
{"return": {"entries": [{"id": 42, "proto": "tcp", "host\_addr": "0.0.0.0", "host\_port": 8080, "guest\_addr": "10.0.2.100", "guest\_port": 80}]}}
355+
{"return": {"entries": [{"id": 42, "proto": "tcp", "host\_addr": "0.0.0.0", "host\_addr6": "::", "host\_port": 8080, "guest\_addr": "10.0.2.100", "guest\_addr6": "fd00::100", "guest\_port": 80}]}}
337356

338357
.fi
339358
.RE

‎slirp4netns.1.md

+21-7
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ API socket path
5555
`--cidr` (since v0.3.0)
5656
specify CIDR, e.g. 10.0.2.0/24
5757

58+
`--cidr6` (since v1.1.13, EXPERIMENTAL)
59+
specify IPv6 CIDR, e.g. fd00::/64
60+
61+
`--ipv6-random` (since v1.1.13, EXPERIMENTAL)
62+
generate a random local IPv6 range
63+
5864
`--disable-host-loopback` (since v0.3.0)
5965
prohibit connecting to 127.0.0.1:\* on the host namespace
6066

@@ -139,7 +145,7 @@ starting slirp, MTU=65520
139145
link/ether c2:28:0c:0e:29:06 brd ff:ff:ff:ff:ff:ff
140146
inet 10.0.2.100/24 brd 10.0.2.255 scope global tap0
141147
valid_lft forever preferred_lft forever
142-
inet6 fe80::c028:cff:fe0e:2906/64 scope link
148+
inet6 fe80::c028:cff:fe0e:2906/64 scope link
143149
valid_lft forever preferred_lft forever
144150
(namespace)$ echo "nameserver 10.0.2.3" > /tmp/resolv.conf
145151
(namespace)$ mount --bind /tmp/resolv.conf /etc/resolv.conf
@@ -207,14 +213,22 @@ slirp4netns can provide QMP-like API server over an UNIX socket file:
207213
(host)$ slirp4netns --api-socket /tmp/slirp4netns.sock ...
208214
```
209215

210-
`add_hostfwd`: Expose a port (IPv4 only)
216+
`add_hostfwd`: Expose a port (IPv4 and IPv6 if enabled and host_addr is "0.0.0.0" or "::")
211217

212218
```console
213219
(namespace)$ json='{"execute": "add_hostfwd", "arguments": {"proto": "tcp", "host_addr": "0.0.0.0", "host_port": 8080, "guest_addr": "10.0.2.100", "guest_port": 80}}'
214220
(namespace)$ echo -n $json | nc -U /tmp/slirp4netns.sock
215221
{"return": {"id": 42}}
216222
```
217223

224+
`add_hostfwd`: Expose a port (IPv4 only)
225+
226+
specify `tcp4` as `proto`
227+
228+
`add_hostfwd`: Expose a port (IPv6 only)
229+
230+
specify `tcp6` as `proto`
231+
218232
If `host_addr` is not specified, then it defaults to "0.0.0.0".
219233

220234
If `guest_addr` is not specified, then it will be set to the default address that corresponds to `--configure`.
@@ -224,7 +238,7 @@ If `guest_addr` is not specified, then it will be set to the default address tha
224238
```console
225239
(namespace)$ json='{"execute": "list_hostfwd"}'
226240
(namespace)$ echo -n $json | nc -U /tmp/slirp4netns.sock
227-
{"return": {"entries": [{"id": 42, "proto": "tcp", "host_addr": "0.0.0.0", "host_port": 8080, "guest_addr": "10.0.2.100", "guest_port": 80}]}}
241+
{"return": {"entries": [{"id": 42, "proto": "tcp", "host_addr": "0.0.0.0", "host_addr6": "::", "host_port": 8080, "guest_addr": "10.0.2.100", "guest_addr6": "fd00::100", "guest_port": 80}]}}
228242
```
229243

230244
`remove_hostfwd`: Remove an exposed port
@@ -243,7 +257,7 @@ Remarks:
243257
* A request must be less than 4096 bytes.
244258
* JSON responses may contain `error` instead of `return`.
245259

246-
# DEFINED NAMESPACE PATHS
260+
# DEFINED NAMESPACE PATHS
247261
A user can define a network namespace path as opposed to the default process ID:
248262

249263
```console
@@ -256,14 +270,14 @@ Additionally, a `--userns-path=PATH` argument can be included to override any us
256270
(host)$ slirp4netns --netns-type=path --userns-path=/path/to/userns /path/to/netns tap0
257271
```
258272

259-
# OUTBOUND ADDRESSES
260-
A user can defined preferred outbound ipv4 and ipv6 address in multi IP scenarios.
273+
# OUTBOUND ADDRESSES
274+
A user can defined preferred outbound ipv4 and ipv6 address in multi IP scenarios.
261275

262276
```console
263277
(host)$ slirp4netns --outbound-addr=10.2.2.10 --outbound-addr6=fe80::10 ...
264278
```
265279

266-
Optionally you can use interface names instead of ip addresses.
280+
Optionally you can use interface names instead of ip addresses.
267281

268282
```console
269283
(host)$ slirp4netns --outbound-addr=eth0 --outbound-addr6=eth0 ...

‎slirp4netns.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -260,16 +260,16 @@ Slirp *create_slirp(void *opaque, struct slirp4netns_config *s4nn)
260260
cfg.vnetmask = s4nn->vnetmask;
261261
cfg.vhost = s4nn->vhost;
262262
cfg.in6_enabled = (int)(s4nn->enable_ipv6);
263-
inet_pton(AF_INET6, "fd00::", &cfg.vprefix_addr6);
263+
cfg.vprefix_addr6 = s4nn->vnetwork6;
264264
cfg.vprefix_len = 64;
265-
inet_pton(AF_INET6, "fd00::2", &cfg.vhost6);
265+
cfg.vhost6 = s4nn->vhost6;
266266
cfg.vhostname = NULL;
267267
cfg.tftp_server_name = NULL;
268268
cfg.tftp_path = NULL;
269269
cfg.bootfile = NULL;
270270
cfg.vdhcp_start = s4nn->vdhcp_start;
271271
cfg.vnameserver = s4nn->vnameserver;
272-
inet_pton(AF_INET6, "fd00::3", &cfg.vnameserver6);
272+
cfg.vnameserver6 = s4nn->vnameserver6;
273273
cfg.vdnssearch = NULL;
274274
cfg.vdomainname = NULL;
275275
cfg.if_mtu = s4nn->mtu;

‎slirp4netns.h

+7
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ struct slirp4netns_config {
1212
struct in_addr vnameserver; // 10.0.2.3
1313
struct in_addr
1414
recommended_vguest; // 10.0.2.100 (slirp itself is unaware of vguest)
15+
struct in6_addr vnetwork6; // fd00:RANDOM:0
16+
struct in6_addr vnetmask6; // /64
17+
struct in6_addr vhost6; // fd00:RANDOM:2
18+
struct in6_addr vdhcp_start6; // fd00:RANDOM:15
19+
struct in6_addr vnameserver6; // fd00:RANDOM:3
20+
struct in6_addr
21+
recommended_vguest6; // fdd00::RANDOM:100 (slirp itself is unaware of vguest)
1522
bool enable_ipv6;
1623
bool disable_host_loopback;
1724
bool enable_sandbox;

‎tests/test-slirp4netns-api-socket.sh

+7-6
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,20 @@ result=$(cat /dev/zero | ncat -U $apisocket || true)
4747
set set -e
4848
echo $result | jq .error.desc | grep "bad request: too large message"
4949

50+
set -e
5051
result=$(echo '{"execute": "add_hostfwd", "arguments":{"proto": "tcp","host_port":8080,"guest_port":80}}' | ncat -U $apisocket)
5152
[[ $(echo $result | jq .error) == null ]]
5253
id=$(echo $result | jq .return.id)
5354
[[ $id == 1 ]]
5455

5556
result=$(echo '{"execute": "list_hostfwd"}' | ncat -U $apisocket)
5657
[[ $(echo $result | jq .error) == null ]]
57-
[[ $(echo $result | jq .return.entries[0].id) == $id ]]
58-
[[ $(echo $result | jq .return.entries[0].proto) == '"tcp"' ]]
59-
[[ $(echo $result | jq .return.entries[0].host_addr) == '"0.0.0.0"' ]]
60-
[[ $(echo $result | jq .return.entries[0].host_port) == 8080 ]]
61-
[[ $(echo $result | jq .return.entries[0].guest_addr) == '"10.0.2.100"' ]]
62-
[[ $(echo $result | jq .return.entries[0].guest_port) == 80 ]]
58+
[[ $(echo $result | jq .entries[0].id) == $id ]]
59+
[[ $(echo $result | jq .entries[0].proto) == '"tcp"' ]]
60+
[[ $(echo $result | jq .entries[0].host_addr) == '"0.0.0.0"' ]]
61+
[[ $(echo $result | jq .entries[0].host_port) == 8080 ]]
62+
[[ $(echo $result | jq .entries[0].guest_addr) == '"10.0.2.100"' ]]
63+
[[ $(echo $result | jq .entries[0].guest_port) == 80 ]]
6364

6465
result=$(echo '{"execute": "remove_hostfwd", "arguments":{"id": 1}}' | ncat -U $apisocket)
6566
[[ $(echo $result | jq .error) == null ]]

‎tests/test-slirp4netns-cidr6.sh

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#!/bin/bash
2+
set -xeuo pipefail
3+
4+
. $(dirname $0)/common.sh
5+
6+
unshare -r -n sleep infinity &
7+
child=$!
8+
9+
wait_for_network_namespace $child
10+
11+
set +e
12+
result=$(slirp4netns -c --enable-ipv6 --cidr6 64 $child tun11 2>&1)
13+
set -e
14+
echo $result | grep "invalid CIDR"
15+
16+
set +e
17+
result=$(slirp4netns -c --enable-ipv6 --cidr6 foo $child tun11 2>&1)
18+
set -e
19+
echo $result | grep "invalid CIDR"
20+
21+
set +e
22+
result=$(slirp4netns -c --enable-ipv6 --cidr6 fd00::2 $child tun11 2>&1)
23+
set -e
24+
echo $result | grep "invalid CIDR"
25+
26+
set +e
27+
result=$(slirp4netns -c --enable-ipv6 --cidr6 fd00::/129 $child tun11 2>&1)
28+
set -e
29+
echo $result | grep "prefix length needs to be 8-128"
30+
31+
cidr=fd00:a1e1:1724:1a
32+
slirp4netns -c $child --enable-ipv6 --cidr6 $cidr::/64 tun11 &
33+
slirp_pid=$!
34+
35+
wait_for_network_device $child tun11
36+
37+
function cleanup {
38+
kill -9 $child $slirp_pid
39+
}
40+
trap cleanup EXIT
41+
42+
result="$(nsenter --preserve-credentials -U -n --target=$child ip a show dev tun11)"
43+
echo "$result" | grep -o '^\s*inet6 .*/..' | grep -F $cidr::100/64
44+
45+
cleanup
46+
47+
unshare -r -n sleep infinity &
48+
child=$!
49+
50+
slirp4netns -c $child --enable-ipv6 tun11 &
51+
slirp_pid=$!
52+
53+
wait_for_network_device $child tun11
54+
55+
result="$(nsenter --preserve-credentials -U -n --target=$child ip a show dev tun11)"
56+
echo "$result" | grep -o '^\s*inet6 .*/..' | grep -F fd00::100/64
57+
58+
cleanup
59+
60+
unshare -r -n sleep infinity &
61+
child=$!
62+
63+
slirp4netns -c $child --enable-ipv6 --ipv6-random tun11 &
64+
slirp_pid=$!
65+
66+
wait_for_network_device $child tun11
67+
68+
result="$(nsenter --preserve-credentials -U -n --target=$child ip a show dev tun11)"
69+
echo "$result" | grep -o '^\s*inet6 .*/..' | grep -vF fd00::100/64

‎tests/test-slirp4netns-hostfwd.sh

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/bin/bash
2+
set -xeuo pipefail
3+
4+
. $(dirname $0)/common.sh
5+
6+
host_port=8080
7+
guest_port=1080
8+
cidr=fd00:a1e1:1724:1a
9+
10+
unshare -r -n socat tcp6-listen:$guest_port,reuseaddr,fork exec:cat,nofork &
11+
child=$!
12+
13+
#nsenter -a -t $child socat tcp-listen:$guest_port,reuseaddr,fork exec:cat,nofork &
14+
15+
wait_for_network_namespace $child
16+
17+
tmpdir=$(mktemp -d /tmp/slirp4netns-bench.XXXXXXXXXX)
18+
apisocket=${tmpdir}/slirp4netns.sock
19+
20+
slirp4netns -c $child --enable-ipv6 --cidr6=$cidr::/64 --api-socket $apisocket tun11 &
21+
slirp_pid=$!
22+
23+
wait_for_network_device $child tun11
24+
25+
function cleanup() {
26+
kill -9 $child $slirp_pid
27+
rm -rf $tmpdir
28+
}
29+
trap cleanup EXIT
30+
31+
set +e
32+
result=$(cat /dev/zero | ncat -U $apisocket || true)
33+
set set -e
34+
echo $result | jq .error.desc | grep "bad request: too large message"
35+
36+
set -e
37+
result=$(echo '{"execute": "add_hostfwd", "arguments":{"proto": "tcp","host_port":'$host_port',"guest_port":'$guest_port'}}' | ncat -U $apisocket)
38+
[[ $(echo $result | jq .error) == null ]]
39+
id=$(echo $result | jq .return.id)
40+
[[ $id == 1 ]]
41+
42+
result=$(echo '{"execute": "list_hostfwd"}' | ncat -U $apisocket)
43+
[[ $(echo $result | jq .error) == null ]]
44+
[[ $(echo $result | jq .entries[0].id) == $id ]]
45+
[[ $(echo $result | jq .entries[0].proto) == '"tcp"' ]]
46+
[[ $(echo $result | jq .entries[0].host_addr) == '"0.0.0.0"' ]]
47+
[[ $(echo $result | jq .entries[0].host_addr6) == '"::"' ]]
48+
[[ $(echo $result | jq .entries[0].host_port) == $host_port ]]
49+
[[ $(echo $result | jq .entries[0].guest_addr) == '"10.0.2.100"' ]]
50+
[[ $(echo $result | jq .entries[0].guest_addr6) == '"'$cidr'::100"' ]]
51+
[[ $(echo $result | jq .entries[0].guest_port) == $guest_port ]]
52+
53+
result=$(echo works | nc -w 10 localhost6 $host_port)
54+
[[ "$result" == "works" ]]
55+
56+
result=$(echo works | nc -w 10 localhost $host_port)
57+
[[ "$result" == "works" ]]
58+
59+
result=$(echo '{"execute": "remove_hostfwd", "arguments":{"id": 1}}' | ncat -U $apisocket)
60+
[[ $(echo $result | jq .error) == null ]]
61+
62+
# see also: benchmarks/benchmark-iperf3-reverse.sh

‎tests/test-slirp4netns-hostfwd4.sh

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#!/bin/bash
2+
set -xeuo pipefail
3+
4+
. $(dirname $0)/common.sh
5+
6+
host_port=8080
7+
guest_port=1080
8+
9+
unshare -r -n socat tcp-listen:$guest_port,reuseaddr,fork exec:cat,nofork &
10+
child=$!
11+
12+
wait_for_network_namespace $child
13+
14+
tmpdir=$(mktemp -d /tmp/slirp4netns-bench.XXXXXXXXXX)
15+
apisocket=${tmpdir}/slirp4netns.sock
16+
17+
slirp4netns -c $child --api-socket $apisocket tun11 &
18+
slirp_pid=$!
19+
20+
wait_for_network_device $child tun11
21+
22+
function cleanup() {
23+
kill -9 $child $slirp_pid
24+
rm -rf $tmpdir
25+
}
26+
trap cleanup EXIT
27+
28+
set +e
29+
result=$(cat /dev/zero | ncat -U $apisocket || true)
30+
set set -e
31+
echo $result | jq .error.desc | grep "bad request: too large message"
32+
33+
set -e
34+
result=$(echo '{"execute": "add_hostfwd", "arguments":{"proto": "tcp","host_port":'$host_port',"guest_port":'$guest_port'}}' | ncat -U $apisocket)
35+
[[ $(echo $result | jq .error) == null ]]
36+
id=$(echo $result | jq .return.id)
37+
[[ $id == 1 ]]
38+
39+
result=$(echo '{"execute": "list_hostfwd"}' | ncat -U $apisocket)
40+
[[ $(echo $result | jq .error) == null ]]
41+
[[ $(echo $result | jq .entries[0].id) == $id ]]
42+
[[ $(echo $result | jq .entries[0].proto) == '"tcp"' ]]
43+
[[ $(echo $result | jq .entries[0].host_addr) == '"0.0.0.0"' ]]
44+
[[ $(echo $result | jq .entries[0].host_addr6) == null ]]
45+
[[ $(echo $result | jq .entries[0].host_port) == $host_port ]]
46+
[[ $(echo $result | jq .entries[0].guest_addr) == '"10.0.2.100"' ]]
47+
[[ $(echo $result | jq .entries[0].guest_addr6) == null ]]
48+
[[ $(echo $result | jq .entries[0].guest_port) == $guest_port ]]
49+
50+
result=$(echo works | nc -w 10 localhost $host_port)
51+
[[ "$result" == "works" ]]
52+
53+
result=$(echo works | nc -w 10 localhost6 $host_port || true)
54+
[[ "$result" != "works" ]]
55+
56+
result=$(echo '{"execute": "remove_hostfwd", "arguments":{"id": 1}}' | ncat -U $apisocket)
57+
[[ $(echo $result | jq .error) == null ]]
58+
59+
result=$(echo '{"execute": "add_hostfwd", "arguments":{"proto": "tcp","host_addr":"127.0.0.1","host_port":'$host_port',"guest_port":'$guest_port'}}' | ncat -U $apisocket)
60+
[[ $(echo $result | jq .error) == null ]]
61+
id=$(echo $result | jq .return.id)
62+
[[ $id == 2 ]]
63+
64+
result=$(echo '{"execute": "list_hostfwd"}' | ncat -U $apisocket)
65+
[[ $(echo $result | jq .error) == null ]]
66+
[[ $(echo $result | jq .entries[0].id) == $id ]]
67+
[[ $(echo $result | jq .entries[0].proto) == '"tcp"' ]]
68+
[[ $(echo $result | jq .entries[0].host_addr) == '"127.0.0.1"' ]]
69+
[[ $(echo $result | jq .entries[0].host_addr6) == null ]]
70+
[[ $(echo $result | jq .entries[0].host_port) == $host_port ]]
71+
[[ $(echo $result | jq .entries[0].guest_addr) == '"10.0.2.100"' ]]
72+
[[ $(echo $result | jq .entries[0].guest_addr6) == null ]]
73+
[[ $(echo $result | jq .entries[0].guest_port) == $guest_port ]]
74+
75+
result=$(echo works | nc -w 10 localhost $host_port)
76+
[[ "$result" == "works" ]]
77+
78+
result=$(echo works | nc -w 10 localhost6 $host_port || true)
79+
[[ "$result" != "works" ]]
80+
81+
result=$(echo '{"execute": "remove_hostfwd", "arguments":{"id": 2}}' | ncat -U $apisocket)
82+
[[ $(echo $result | jq .error) == null ]]
83+
84+
result=$(echo '{"execute": "add_hostfwd", "arguments":{"proto": "tcp6","host_port":'$host_port',"guest_port":'$guest_port'}}' | ncat -U $apisocket || true)
85+
echo $result | jq .error.desc | grep "bad arguments.proto"
86+
87+
result=$(echo '{"execute": "add_hostfwd", "arguments":{"proto": "tcp","host_addr":"::1","host_port":'$host_port',"guest_port":'$guest_port'}}' | ncat -U $apisocket || true)
88+
echo $result | jq .error.desc | grep "bad arguments.host_addr"
89+
90+
# see also: benchmarks/benchmark-iperf3-reverse.sh

‎tests/test-slirp4netns-hostfwd6.sh

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#!/bin/bash
2+
set -xeuo pipefail
3+
4+
. $(dirname $0)/common.sh
5+
6+
host_port=8080
7+
guest_port=1080
8+
cidr=fd00:a1e1:1724:1a
9+
10+
unshare -r -n socat tcp6-listen:$guest_port,reuseaddr,fork exec:cat,nofork &
11+
child=$!
12+
13+
wait_for_network_namespace $child
14+
15+
tmpdir=$(mktemp -d /tmp/slirp4netns-bench.XXXXXXXXXX)
16+
apisocket=${tmpdir}/slirp4netns.sock
17+
18+
slirp4netns -c $child --enable-ipv6 --cidr6=$cidr::/64 --api-socket $apisocket tun11 &
19+
slirp_pid=$!
20+
21+
wait_for_network_device $child tun11
22+
23+
function cleanup() {
24+
kill -9 $child $slirp_pid
25+
rm -rf $tmpdir
26+
}
27+
trap cleanup EXIT
28+
29+
set +e
30+
result=$(cat /dev/zero | ncat -U $apisocket || true)
31+
set set -e
32+
echo $result | jq .error.desc | grep "bad request: too large message"
33+
34+
set -e
35+
result=$(echo '{"execute": "add_hostfwd", "arguments":{"proto": "tcp6","host_port":'$host_port',"guest_port":'$guest_port'}}' | ncat -U $apisocket)
36+
[[ $(echo $result | jq .error) == null ]]
37+
id=$(echo $result | jq .return.id)
38+
[[ $id == 1 ]]
39+
40+
result=$(echo '{"execute": "list_hostfwd"}' | ncat -U $apisocket)
41+
[[ $(echo $result | jq .error) == null ]]
42+
[[ $(echo $result | jq .entries[0].id) == $id ]]
43+
[[ $(echo $result | jq .entries[0].proto) == '"tcp"' ]]
44+
[[ $(echo $result | jq .entries[0].host_addr) == null ]]
45+
[[ $(echo $result | jq .entries[0].host_addr6) == '"::"' ]]
46+
[[ $(echo $result | jq .entries[0].host_port) == $host_port ]]
47+
[[ $(echo $result | jq .entries[0].guest_addr) == null ]]
48+
[[ $(echo $result | jq .entries[0].guest_addr6) == '"'$cidr'::100"' ]]
49+
[[ $(echo $result | jq .entries[0].guest_port) == $guest_port ]]
50+
51+
result=$(echo works | nc -w 10 localhost6 $host_port)
52+
[[ "$result" == "works" ]]
53+
54+
result=$(echo works | nc -w 10 localhost $host_port || true)
55+
[[ "$result" != "works" ]]
56+
57+
result=$(echo '{"execute": "remove_hostfwd", "arguments":{"id": 1}}' | ncat -U $apisocket)
58+
[[ $(echo $result | jq .error) == null ]]
59+
60+
result=$(echo '{"execute": "add_hostfwd", "arguments":{"proto": "tcp6","host_addr":"::1","host_port":'$host_port',"guest_port":'$guest_port'}}' | ncat -U $apisocket)
61+
[[ $(echo $result | jq .error) == null ]]
62+
id=$(echo $result | jq .return.id)
63+
[[ $id == 2 ]]
64+
65+
result=$(echo '{"execute": "list_hostfwd"}' | ncat -U $apisocket)
66+
[[ $(echo $result | jq .error) == null ]]
67+
[[ $(echo $result | jq .entries[0].id) == $id ]]
68+
[[ $(echo $result | jq .entries[0].proto) == '"tcp"' ]]
69+
[[ $(echo $result | jq .entries[0].host_addr) == null ]]
70+
[[ $(echo $result | jq .entries[0].host_addr6) == '"::1"' ]]
71+
[[ $(echo $result | jq .entries[0].host_port) == $host_port ]]
72+
[[ $(echo $result | jq .entries[0].guest_addr) == null ]]
73+
[[ $(echo $result | jq .entries[0].guest_addr6) == '"'$cidr'::100"' ]]
74+
[[ $(echo $result | jq .entries[0].guest_port) == $guest_port ]]
75+
76+
result=$(echo works | nc -w 10 localhost6 $host_port)
77+
[[ "$result" == "works" ]]
78+
79+
result=$(echo works | nc -w 10 localhost $host_port || true)
80+
[[ "$result" != "works" ]]
81+
82+
result=$(echo '{"execute": "remove_hostfwd", "arguments":{"id": 2}}' | ncat -U $apisocket)
83+
[[ $(echo $result | jq .error) == null ]]
84+
85+
result=$(echo '{"execute": "add_hostfwd", "arguments":{"proto": "tcp4","host_addr":"::1","host_port":'$host_port',"guest_port":'$guest_port'}}' | ncat -U $apisocket || true)
86+
echo $result | jq .error.desc | grep "bad arguments.host_addr"
87+
88+
result=$(echo '{"execute": "add_hostfwd", "arguments":{"proto": "tcp6","host_addr":"127.0.0.1","host_port":'$host_port',"guest_port":'$guest_port'}}' | ncat -U $apisocket || true)
89+
echo $result | jq .error.desc | grep "bad arguments.host_addr"
90+
91+
# see also: benchmarks/benchmark-iperf3-reverse.sh

‎tests/test-slirp4netns-ipv6.sh

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/bin/bash
2+
set -xeuo pipefail
3+
4+
. $(dirname $0)/common.sh
5+
6+
host_port=8080
7+
guest_port=80
8+
cidr=fd00:a1e1:1724:1a
9+
10+
unshare -r -n sleep infinity &
11+
child=$!
12+
13+
wait_for_network_namespace $child
14+
15+
tmpdir=$(mktemp -d /tmp/slirp4netns-bench.XXXXXXXXXX)
16+
apisocket=${tmpdir}/slirp4netns.sock
17+
18+
slirp4netns -c $child --enable-ipv6 --cidr6=$cidr::/64 --api-socket $apisocket tun11 &
19+
slirp_pid=$!
20+
21+
wait_for_network_device $child tun11
22+
23+
function cleanup() {
24+
kill -9 $child $slirp_pid
25+
rm -rf $tmpdir
26+
}
27+
trap cleanup EXIT
28+
29+
set +e
30+
result=$(cat /dev/zero | ncat -U $apisocket || true)
31+
set set -e
32+
echo $result | jq .error.desc | grep "bad request: too large message"
33+
34+
set -e
35+
result=$(echo '{"execute": "add_hostfwd", "arguments":{"proto": "tcp","host_port":8080,"guest_port":80}}' | ncat -U $apisocket)
36+
[[ $(echo $result | jq .error) == null ]]
37+
id=$(echo $result | jq .return.id)
38+
[[ $id == 1 ]]
39+
40+
result=$(echo '{"execute": "list_hostfwd"}' | ncat -U $apisocket)
41+
[[ $(echo $result | jq .error) == null ]]
42+
[[ $(echo $result | jq .entries[0].id) == $id ]]
43+
[[ $(echo $result | jq .entries[0].proto) == '"tcp"' ]]
44+
[[ $(echo $result | jq .entries[0].host_addr) == '"0.0.0.0"' ]]
45+
[[ $(echo $result | jq .entries[0].host_addr6) == '"::"' ]]
46+
[[ $(echo $result | jq .entries[0].host_port) == $host_port ]]
47+
[[ $(echo $result | jq .entries[0].guest_addr) == '"10.0.2.100"' ]]
48+
[[ $(echo $result | jq .entries[0].guest_addr6) == '"'$cidr'::100"' ]]
49+
[[ $(echo $result | jq .entries[0].guest_port) == $guest_port ]]
50+
51+
result=$(echo '{"execute": "remove_hostfwd", "arguments":{"id": 1}}' | ncat -U $apisocket)
52+
[[ $(echo $result | jq .error) == null ]]
53+
54+
# see also: benchmarks/benchmark-iperf3-reverse.sh

0 commit comments

Comments
 (0)
Please sign in to comment.