Codebase list slirp4netns / upstream/0.4.1
New upstream version 0.4.1 Reinhard Tartler 4 years ago
24 changed file(s) with 1968 addition(s) and 1126 deletion(s). Raw diff Collapse all Expand all
0 vendor/libslirp/.clang-format⏎
55
66 script:
77 - docker build -t slirp4netns-tests -f Dockerfile.tests .
8 - docker run --security-opt seccomp="unconfined" -ti --rm -v /dev:/dev slirp4netns-tests
8 - docker run --security-opt seccomp="unconfined" --rm -v /dev:/dev slirp4netns-tests
99 - docker build -q -f Dockerfile.buildtests .
0 FROM alpine:3.8 AS buildtest-alpine38-static
1 RUN apk add --no-cache git build-base autoconf automake libtool linux-headers glib-dev glib-static
0 # Alpine
1 FROM alpine:3.10 AS buildtest-alpine310-static
2 RUN apk add --no-cache git build-base autoconf automake libtool linux-headers glib-dev glib-static libcap-static libcap-dev libseccomp-dev
23 COPY . /src
34 WORKDIR /src
45 RUN ./autogen.sh && ./configure LDFLAGS="-static" && make && cp -f slirp4netns /
56
7 # Ubuntu
68 FROM ubuntu:18.04 AS buildtest-ubuntu1804-common
7 RUN apt update && apt install -y automake autotools-dev make gcc libglib2.0-dev
9 RUN apt update && apt install -y automake autotools-dev make gcc libglib2.0-dev libcap-dev libseccomp-dev
810 COPY . /src
911 WORKDIR /src
1012 RUN ./autogen.sh
1517 FROM buildtest-ubuntu1804-common AS buildtest-ubuntu1804-static
1618 RUN ./configure LDFLAGS="-static" && make && cp -f slirp4netns /
1719
20 # CentOS
21 FROM centos:7.6.1810 AS buildtest-centos76-common
22 RUN yum install -y autoconf automake gcc glib2-devel git make libcap-devel libseccomp-devel
23 COPY . /src
24 WORKDIR /src
25 RUN ./autogen.sh
26
27 FROM buildtest-centos76-common AS buildtest-centos76-dynamic
28 RUN ./configure && make && cp -f slirp4netns /
29
30 FROM buildtest-centos76-common AS buildtest-centos76-static
31 RUN yum install -y glibc-static glib2-static
32 RUN yum-config-manager --add-repo=https://cbs.centos.org/repos/virt7-container-common-candidate/x86_64/os/ && \
33 yum install --nogpgcheck -y libseccomp-static
34 RUN ./configure LDFLAGS="-static" && make && cp -f slirp4netns /
35
36 # openSUSE (dynamic only)
37 FROM opensuse/leap:15.1 AS buildtest-opensuse151-common
38 RUN zypper install -y --no-recommends autoconf automake gcc glib2-devel git make libcap-devel libseccomp-devel
39 COPY . /src
40 WORKDIR /src
41 RUN ./autogen.sh
42
43 FROM buildtest-opensuse151-common AS buildtest-opensuse151-dynamic
44 RUN ./configure && make && cp -f slirp4netns /
45
1846 FROM scratch AS buildtest-final-stage
19 COPY --from=buildtest-alpine38-static /slirp4netns /buildtest-alpine38-static
47 COPY --from=buildtest-alpine310-static /slirp4netns /buildtest-alpine310-static
2048 COPY --from=buildtest-ubuntu1804-dynamic /slirp4netns /buildtest-ubuntu1804-dynamic
2149 COPY --from=buildtest-ubuntu1804-static /slirp4netns /buildtest-ubuntu1804-static
50 COPY --from=buildtest-centos76-dynamic /slirp4netns /buildtest-centos76-dynamic
51 COPY --from=buildtest-centos76-static /slirp4netns /buildtest-centos76-static
52 COPY --from=buildtest-opensuse151-dynamic /slirp4netns /buildtest-opensuse151-dynamic
0 FROM ubuntu AS build
1 RUN apt update && apt install -y automake autotools-dev make gcc libglib2.0-dev
0 FROM ubuntu:18.04 AS build
1 RUN apt update && apt install -y automake autotools-dev make gcc libglib2.0-dev libcap-dev libseccomp-dev
22 COPY . /slirp4netns
33 WORKDIR /slirp4netns
44 RUN chown -R 1000:1000 /slirp4netns
77
88 FROM build AS test
99 USER 0
10 RUN apt update && apt install -y git indent libtool iproute2 clang clang-tidy iputils-ping iperf3 nmap jq
10 RUN apt update && apt install -y git libtool iproute2 clang clang-format clang-tidy iputils-ping iperf3 nmap jq
1111 USER 1000:1000
1212 CMD ["make", "ci"]
7272 [people]
7373 [people.akihirosuda]
7474 Name = "Akihiro Suda"
75 Email = "suda.akihiro@lab.ntt.co.jp"
75 Email = "akihiro.suda.cz@hco.ntt.co.jp"
7676 GitHub = "AkihiroSuda"
7777
7878 [people.barthalion]
00 bin_PROGRAMS = slirp4netns
11
2 AM_CFLAGS = @GLIB_CFLAGS@
2 AM_CFLAGS = @GLIB_CFLAGS@ @LIBCAP_CFLAGS@ @LIBSECCOMP_CFLAGS@
33
44 noinst_LIBRARIES = libslirp.a libparson.a
55
1313 tests/common.sh \
1414 slirp4netns.h \
1515 api.h \
16 sandbox.h \
17 seccompfilter.h \
1618 vendor/libslirp/COPYRIGHT \
1719 vendor/libslirp/README.md \
1820 vendor/libslirp/src/bootp.h \
3335 vendor/libslirp/src/slirp.h \
3436 vendor/libslirp/src/socket.h \
3537 vendor/libslirp/src/stream.h \
36 vendor/libslirp/src/tftp.h \
3738 vendor/libslirp/src/tcp.h \
3839 vendor/libslirp/src/tcpip.h \
3940 vendor/libslirp/src/tcp_timer.h \
4041 vendor/libslirp/src/tcp_var.h \
42 vendor/libslirp/src/tftp.h \
4143 vendor/libslirp/src/udp.h \
4244 vendor/libslirp/src/util.h \
4345 vendor/libslirp/src/vmstate.h \
7981 vendor/libslirp/src/vmstate.c
8082
8183 # define specific commit if git available or it was replaced during git-archive creation
82 COMMIT := $(shell V=5e1639783df9bd7abbc46abc084a53c1093342d8 ; \
84 COMMIT := $(shell V=4d38845e2e311b684fc8d1c775c725bfcd5ddc27 ; \
8385 expr match "$$V" ormat: >/dev/null \
8486 && (cd "$$abs_srcdir" && [ -d .git ] && git describe --always --abbrev=0 --dirty --exclude=\* || echo unknown) \
8587 || echo "$$V" )
9092 libparson_a_CFLAGS = $(AM_CFLAGS) -I$(abs_top_builddir)/vendor/parson
9193 libparson_a_SOURCES = vendor/parson/parson.c
9294
93 slirp4netns_SOURCES = main.c slirp4netns.c api.c
94 slirp4netns_LDADD = libslirp.a libparson.a @GLIB_LIBS@ -lpthread
95 slirp4netns_SOURCES = main.c slirp4netns.c api.c sandbox.c seccompfilter.c
96 slirp4netns_LDADD = libslirp.a libparson.a @GLIB_LIBS@ @LIBSECCOMP_LIBS@ -lpthread
9597 man1_MANS = slirp4netns.1
9698
9799 generate-man:
98100 go-md2man -in slirp4netns.1.md -out slirp4netns.1
99101
100102 CLANGTIDY = clang-tidy -warnings-as-errors='*'
103
104 CLANGFORMAT = clang-format
101105
102106 lint:
103107 $(CLANGTIDY) $(slirp4netns_SOURCES) -- $(AM_CFLAGS)
106110 $(CLANGTIDY) $(slirp4netns_SOURCES) $(libslirp_a_SOURCES) $(libparson_a_SOURCES) -- $(AM_CFLAGS)
107111
108112 indent:
109 # indent(1): "You must use the ‘-T’ option to tell indent the name of all the typenames in your program that are defined by typedef."
110 indent -linux -l120 \
111 -T ssize_t -T pid_t \
112 -T GArray -T GList -T GSList -T GPollFD -T gpointer -T gconstpointer \
113 -T Slirp -T SlirpCb -T SlirpConfig -T SlirpTimerCb \
114 -T JSON_Object -T JSON_Value \
115 -T regex_t -T regmatch_t \
116 $(slirp4netns_SOURCES)
117 $(RM) *.c~
118
113 $(CLANGFORMAT) -i $(slirp4netns_SOURCES)
119114
120115 benchmark:
121116 benchmarks/benchmark-iperf3.sh
123118
124119 ci:
125120 $(MAKE) indent
126 test -z "$(git diff)"
121 git diff --exit-code
127122 # TODO: make sure ./vendor is synced with ./vendor.sh
128 # (hard to verify during `make`, because sync.sh removes ./vendor/libslirp/src/.deps)
123 # (hard to verify during `make`, because vendor.sh removes ./vendor/libslirp/src/.deps)
129124 $(MAKE) lint
130125 $(MAKE) -j $(shell nproc) distcheck || ( find . -name test-suite.log | xargs cat; exit 1 )
131126 PATH=$(shell pwd):$$PATH $(MAKE) benchmark MTU=1500
00 # slirp4netns: User-mode networking for unprivileged network namespaces
11
22 slirp4netns provides user-mode networking ("slirp") for unprivileged network namespaces.
3
4 Latest stable release: [v0.3.X](https://github.com/rootless-containers/slirp4netns/releases)
53
64 ## Motivation
75
1210
1311 ## Projects using slirp4netns
1412
13 Kubernetes distributions:
1514 * [Usernetes](https://github.com/rootless-containers/usernetes) (via RootlessKit)
15 * [k3s](https://k3s.io) (via RootlessKit)
16
17 Container engines:
1618 * [Podman](https://github.com/containers/libpod)
1719 * [Buildah](https://github.com/containers/buildah)
1820 * [ctnr](https://github.com/mgoltzsche/ctnr) (via slirp-cni-plugin)
1921 * [Docker & Moby](https://get.docker.com/rootless) (optionally, via RootlessKit)
2022
23 Tools:
2124 * [RootlessKit](https://github.com/rootless-containers/rootlesskit)
2225 * [become-root](https://github.com/giuseppe/become-root)
2326 * [slirp-cni-plugin](https://github.com/mgoltzsche/slirp-cni-plugin)
2427
28 ## Maintenance policy
29
30 Version | Status
31 -------------------------------|------------------------------------------------------------------------
32 v0.4.x | A :white_check_mark: (will be demoted to B after the release of v0.5.0)
33 v0.3.x | B :white_check_mark: (will be demoted to C after the release of v0.5.0)
34 v0.2.x | C :warning:
35 Early versions prior to v0.2.x | D :warning:
36
37 * A: Actively maintained. Patch releases for security fixes and other bug fixes are planned.
38 * B: Patch releases for security fixes are planned.
39 * C: No additional release is planned. However, anybody can still open PR for security fixes in the `release/x.y` branch.
40 * D: Not maintained. Distributors can continue to distribute this version, but they should apply security fixes by themselves.
41
42 See https://github.com/rootless-containers/slirp4netns/releases for the releases.
43
44 See https://github.com/rootless-containers/slirp4netns/security/advisories for the past security advisories.
45
2546 ## Quick start
2647
2748 ### Install from source
2849
29 Build dependency: `glib2-devel` (`libglib2.0-dev`)
50 Build dependencies:
51 * `glib2-devel` (`libglib2.0-dev`)
52 * `libcap-devel` (`libcap-dev`)
53 * `libseccomp-devel` (`libseccomp-dev`)
54
55 Install steps:
3056
3157 ```console
3258 $ ./autogen.sh
4470
4571 ```console
4672 $ sudo dnf install slirp4netns
73 ```
74
75 #### RHEL/CentOS 7.7
76
77 ```console
78 $ sudo yum install slirp4netns
4779 ```
4880
4981 #### [RHEL/CentOS 7.6](https://copr.fedorainfracloud.org/coprs/vbatts/shadow-utils-newxidmap/)
170202 The latest revision of slirp4netns is regularly benchmarked (`make benchmark`) on Travis: https://travis-ci.org/rootless-containers/slirp4netns
171203
172204 ## Acknowledgement
173
174205 See [`vendor/README.md`](./vendor/README.md).
206
207 ## License
208 [GPL-2.0-or-later](COPYING)
+297
-270
api.c less more
0 /* SPDX-License-Identifier: GPL-2.0-or-later */
01 #define _GNU_SOURCE
12 #include <stdio.h>
23 #include <stdlib.h>
1011
1112 int api_bindlisten(const char *api_socket)
1213 {
13 int fd;
14 struct sockaddr_un addr;
15 unlink(api_socket); /* avoid EADDRINUSE */
16 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
17 perror("api_bindlisten: socket");
18 return -1;
19 }
20 memset(&addr, 0, sizeof(addr));
21 addr.sun_family = AF_UNIX;
22 strncpy(addr.sun_path, api_socket, sizeof(addr.sun_path) - 1);
23 if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
24 perror("api_bindlisten: bind");
25 return -1;
26 }
27 if (listen(fd, 0) < 0) {
28 perror("api_bindlisten: listen");
29 return -1;
30 }
31 return fd;
14 int fd;
15 struct sockaddr_un addr;
16 unlink(api_socket); /* avoid EADDRINUSE */
17 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
18 perror("api_bindlisten: socket");
19 return -1;
20 }
21 memset(&addr, 0, sizeof(addr));
22 addr.sun_family = AF_UNIX;
23 strncpy(addr.sun_path, api_socket, sizeof(addr.sun_path) - 1);
24 if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
25 perror("api_bindlisten: bind");
26 return -1;
27 }
28 if (listen(fd, 0) < 0) {
29 perror("api_bindlisten: listen");
30 return -1;
31 }
32 return fd;
3233 }
3334
3435 struct api_hostfwd {
35 int id;
36 int is_udp;
37 struct in_addr host_addr;
38 int host_port;
39 struct in_addr guest_addr;
40 int guest_port;
36 int id;
37 int is_udp;
38 struct in_addr host_addr;
39 int host_port;
40 struct in_addr guest_addr;
41 int guest_port;
4142 };
4243
4344 struct api_ctx {
44 uint8_t *buf;
45 size_t buflen;
46 GList *hostfwds;
47 int hostfwds_nextid;
48 struct slirp4netns_config *cfg;
45 uint8_t *buf;
46 size_t buflen;
47 GList *hostfwds;
48 int hostfwds_nextid;
49 struct slirp4netns_config *cfg;
4950 };
5051
5152 struct api_ctx *api_ctx_alloc(struct slirp4netns_config *cfg)
5253 {
53 struct api_ctx *ctx = (struct api_ctx *)g_malloc0(sizeof(*ctx));
54 if (ctx == NULL) {
55 return NULL;
56 }
57 ctx->buflen = 4096;
58 ctx->buf = malloc(ctx->buflen); /* FIXME: realloc */
59 if (ctx->buf == NULL) {
60 free(ctx);
61 return NULL;
62 }
63 ctx->cfg = cfg;
64 ctx->hostfwds = NULL;
65 ctx->hostfwds_nextid = 1;
66 return ctx;
54 struct api_ctx *ctx = (struct api_ctx *)g_malloc0(sizeof(*ctx));
55 if (ctx == NULL) {
56 return NULL;
57 }
58 ctx->buflen = 4096;
59 ctx->buf = malloc(ctx->buflen); /* FIXME: realloc */
60 if (ctx->buf == NULL) {
61 free(ctx);
62 return NULL;
63 }
64 ctx->cfg = cfg;
65 ctx->hostfwds = NULL;
66 ctx->hostfwds_nextid = 1;
67 return ctx;
6768 }
6869
6970 void api_ctx_free(struct api_ctx *ctx)
7071 {
71 if (ctx != NULL) {
72 if (ctx->buf != NULL) {
73 free(ctx->buf);
74 }
75 g_list_free_full(ctx->hostfwds, g_free);
76 free(ctx);
77 }
72 if (ctx != NULL) {
73 if (ctx->buf != NULL) {
74 free(ctx->buf);
75 }
76 g_list_free_full(ctx->hostfwds, g_free);
77 free(ctx);
78 }
7879 }
7980
8081 /*
8182 Handler for add_hostfwd.
82 e.g. {"execute": "add_hostfwd", "arguments": {"proto": "tcp", "host_addr": "0.0.0.0", "host_port": 8080, "guest_addr": "10.0.2.100", "guest_port": 80}}
83 This function returns the return value of write(2), not the return value of slirp_add_hostfwd().
83 e.g. {"execute": "add_hostfwd", "arguments": {"proto": "tcp", "host_addr":
84 "0.0.0.0", "host_port": 8080, "guest_addr": "10.0.2.100", "guest_port": 80}}
85 This function returns the return value of write(2), not the return value of
86 slirp_add_hostfwd().
8487 */
85 static int api_handle_req_add_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx, JSON_Object *jo)
86 {
87 int wrc = 0, slirprc = 0;
88 int id = -1;
89 char idbuf[64];
90 const char *proto_s = json_object_dotget_string(jo, "arguments.proto");
91 const char *host_addr_s = json_object_dotget_string(jo, "arguments.host_addr");
92 const char *guest_addr_s = json_object_dotget_string(jo, "arguments.guest_addr");
93 struct api_hostfwd *fwd = g_malloc0(sizeof(*fwd));
94 if (fwd == NULL) {
95 perror("fatal: malloc");
96 exit(EXIT_FAILURE);
97 }
98 fwd->is_udp = -1; /* TODO: support SCTP */
99 if (strcmp(proto_s, "udp") == 0) {
100 fwd->is_udp = 1;
101 } else if (strcmp(proto_s, "tcp") == 0) {
102 fwd->is_udp = 0;
103 }
104 if (fwd->is_udp == -1) {
105 const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: bad arguments.proto\"}}";
106 wrc = write(fd, err, strlen(err));
107 free(fwd);
108 goto finish;
109 }
110 if (host_addr_s == NULL || host_addr_s[0] == '\0') {
111 host_addr_s = "0.0.0.0";
112 }
113 if (inet_pton(AF_INET, host_addr_s, &fwd->host_addr) != 1) {
114 const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: bad arguments.host_addr\"}}";
115 wrc = write(fd, err, strlen(err));
116 free(fwd);
117 goto finish;
118 }
119 fwd->host_port = (int)json_object_dotget_number(jo, "arguments.host_port");
120 if (fwd->host_port == 0) {
121 const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: bad arguments.host_port\"}}";
122 wrc = write(fd, err, strlen(err));
123 free(fwd);
124 goto finish;
125 }
126
127 if (guest_addr_s == NULL || guest_addr_s[0] == '\0') {
128 fwd->guest_addr = ctx->cfg->recommended_vguest;
129 } else if (inet_pton(AF_INET, guest_addr_s, &fwd->guest_addr) != 1) {
130 const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: bad arguments.guest_addr\"}}";
131 wrc = write(fd, err, strlen(err));
132 free(fwd);
133 goto finish;
134 }
135 fwd->guest_port = (int)json_object_dotget_number(jo, "arguments.guest_port");
136 if (fwd->guest_port == 0) {
137 const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: bad arguments.guest_port\"}}";
138 wrc = write(fd, err, strlen(err));
139 free(fwd);
140 goto finish;
141 }
142 if ((slirprc =
143 slirp_add_hostfwd(slirp, fwd->is_udp, fwd->host_addr, fwd->host_port, fwd->guest_addr,
144 fwd->guest_port)) < 0) {
145 const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: slirp_add_hostfwd failed\"}}";
146 wrc = write(fd, err, strlen(err));
147 free(fwd);
148 goto finish;
149 }
150 fwd->id = ctx->hostfwds_nextid;
151 ctx->hostfwds_nextid++;
152 ctx->hostfwds = g_list_append(ctx->hostfwds, fwd);
153 if (snprintf(idbuf, sizeof(idbuf), "{\"return\":{\"id\":%d}}", fwd->id) > sizeof(idbuf)) {
154 fprintf(stderr, "fatal: unexpected id=%d\n", fwd->id);
155 exit(EXIT_FAILURE);
156 }
157 wrc = write(fd, idbuf, strlen(idbuf));
158 finish:
159 return wrc;
160 }
161
162 static void api_handle_req_list_hostfwd_foreach(gpointer data, gpointer user_data)
163 {
164 struct api_hostfwd *fwd = data;
165 JSON_Array *entries_array = (JSON_Array *) user_data;
166 JSON_Value *entry_value = json_value_init_object();
167 JSON_Object *entry_object = json_value_get_object(entry_value);
168 char host_addr[INET_ADDRSTRLEN], guest_addr[INET_ADDRSTRLEN];
169 if (inet_ntop(AF_INET, &fwd->host_addr, host_addr, sizeof(host_addr)) == NULL) {
170 perror("fatal: inet_ntop");
171 exit(EXIT_FAILURE);
172 }
173 if (inet_ntop(AF_INET, &fwd->guest_addr, guest_addr, sizeof(guest_addr)) == NULL) {
174 perror("fatal: inet_ntop");
175 exit(EXIT_FAILURE);
176 }
177 json_object_set_number(entry_object, "id", fwd->id);
178 json_object_set_string(entry_object, "proto", fwd->is_udp ? "udp" : "tcp");
179 json_object_set_string(entry_object, "host_addr", host_addr);
180 json_object_set_number(entry_object, "host_port", fwd->host_port);
181 json_object_set_string(entry_object, "guest_addr", guest_addr);
182 json_object_set_number(entry_object, "guest_port", fwd->guest_port);
183 /* json_array_append_value does not copy passed value */
184 if (json_array_append_value(entries_array, entry_value) != JSONSuccess) {
185 fprintf(stderr, "fatal: json_array_append_value\n");
186 exit(EXIT_FAILURE);
187 }
88 static int api_handle_req_add_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx,
89 JSON_Object *jo)
90 {
91 int wrc = 0, slirprc = 0;
92 char idbuf[64];
93 const char *proto_s = json_object_dotget_string(jo, "arguments.proto");
94 const char *host_addr_s =
95 json_object_dotget_string(jo, "arguments.host_addr");
96 const char *guest_addr_s =
97 json_object_dotget_string(jo, "arguments.guest_addr");
98 struct api_hostfwd *fwd = g_malloc0(sizeof(*fwd));
99 if (fwd == NULL) {
100 perror("fatal: malloc");
101 exit(EXIT_FAILURE);
102 }
103 fwd->is_udp = -1; /* TODO: support SCTP */
104 if (strcmp(proto_s, "udp") == 0) {
105 fwd->is_udp = 1;
106 } else if (strcmp(proto_s, "tcp") == 0) {
107 fwd->is_udp = 0;
108 }
109 if (fwd->is_udp == -1) {
110 const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: "
111 "bad arguments.proto\"}}";
112 wrc = write(fd, err, strlen(err));
113 free(fwd);
114 goto finish;
115 }
116 if (host_addr_s == NULL || host_addr_s[0] == '\0') {
117 host_addr_s = "0.0.0.0";
118 }
119 if (inet_pton(AF_INET, host_addr_s, &fwd->host_addr) != 1) {
120 const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: "
121 "bad arguments.host_addr\"}}";
122 wrc = write(fd, err, strlen(err));
123 free(fwd);
124 goto finish;
125 }
126 fwd->host_port = (int)json_object_dotget_number(jo, "arguments.host_port");
127 if (fwd->host_port == 0) {
128 const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: "
129 "bad arguments.host_port\"}}";
130 wrc = write(fd, err, strlen(err));
131 free(fwd);
132 goto finish;
133 }
134
135 if (guest_addr_s == NULL || guest_addr_s[0] == '\0') {
136 fwd->guest_addr = ctx->cfg->recommended_vguest;
137 } else if (inet_pton(AF_INET, guest_addr_s, &fwd->guest_addr) != 1) {
138 const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: "
139 "bad arguments.guest_addr\"}}";
140 wrc = write(fd, err, strlen(err));
141 free(fwd);
142 goto finish;
143 }
144 fwd->guest_port =
145 (int)json_object_dotget_number(jo, "arguments.guest_port");
146 if (fwd->guest_port == 0) {
147 const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: "
148 "bad arguments.guest_port\"}}";
149 wrc = write(fd, err, strlen(err));
150 free(fwd);
151 goto finish;
152 }
153 if ((slirprc = slirp_add_hostfwd(slirp, fwd->is_udp, fwd->host_addr,
154 fwd->host_port, fwd->guest_addr,
155 fwd->guest_port)) < 0) {
156 const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: "
157 "slirp_add_hostfwd failed\"}}";
158 wrc = write(fd, err, strlen(err));
159 free(fwd);
160 goto finish;
161 }
162 fwd->id = ctx->hostfwds_nextid;
163 ctx->hostfwds_nextid++;
164 ctx->hostfwds = g_list_append(ctx->hostfwds, fwd);
165 if (snprintf(idbuf, sizeof(idbuf), "{\"return\":{\"id\":%d}}", fwd->id) >
166 sizeof(idbuf)) {
167 fprintf(stderr, "fatal: unexpected id=%d\n", fwd->id);
168 exit(EXIT_FAILURE);
169 }
170 wrc = write(fd, idbuf, strlen(idbuf));
171 finish:
172 return wrc;
173 }
174
175 static void api_handle_req_list_hostfwd_foreach(gpointer data,
176 gpointer user_data)
177 {
178 struct api_hostfwd *fwd = data;
179 JSON_Array *entries_array = (JSON_Array *)user_data;
180 JSON_Value *entry_value = json_value_init_object();
181 JSON_Object *entry_object = json_value_get_object(entry_value);
182 char host_addr[INET_ADDRSTRLEN], guest_addr[INET_ADDRSTRLEN];
183 if (inet_ntop(AF_INET, &fwd->host_addr, host_addr, sizeof(host_addr)) ==
184 NULL) {
185 perror("fatal: inet_ntop");
186 exit(EXIT_FAILURE);
187 }
188 if (inet_ntop(AF_INET, &fwd->guest_addr, guest_addr, sizeof(guest_addr)) ==
189 NULL) {
190 perror("fatal: inet_ntop");
191 exit(EXIT_FAILURE);
192 }
193 json_object_set_number(entry_object, "id", fwd->id);
194 json_object_set_string(entry_object, "proto", fwd->is_udp ? "udp" : "tcp");
195 json_object_set_string(entry_object, "host_addr", host_addr);
196 json_object_set_number(entry_object, "host_port", fwd->host_port);
197 json_object_set_string(entry_object, "guest_addr", guest_addr);
198 json_object_set_number(entry_object, "guest_port", fwd->guest_port);
199 /* json_array_append_value does not copy passed value */
200 if (json_array_append_value(entries_array, entry_value) != JSONSuccess) {
201 fprintf(stderr, "fatal: json_array_append_value\n");
202 exit(EXIT_FAILURE);
203 }
188204 }
189205
190206 /*
191207 Handler for list_hostfwd.
192208 e.g. {"execute": "list_hostfwd"}
193209 */
194 static int api_handle_req_list_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx, JSON_Object *jo)
195 {
196 int wrc = 0;
197 struct api_hostfwd *fwd = NULL;
198 JSON_Value *root_value = json_value_init_object(), *entries_value = json_value_init_array();
199 JSON_Object *root_object = json_value_get_object(root_value);
200 JSON_Array *entries_array = json_array(entries_value);
201 char *serialized_string = NULL;
202 g_list_foreach(ctx->hostfwds, api_handle_req_list_hostfwd_foreach, entries_array);
203 json_object_set_value(root_object, "entries", entries_value);
204 serialized_string = json_serialize_to_string(root_value);
205 wrc = write(fd, serialized_string, strlen(serialized_string));
206 finish:
207 json_free_serialized_string(serialized_string);
208 json_value_free(root_value);
209 return wrc;
210 }
211
212 static int api_handle_remove_list_hostfwd_find(gconstpointer gcp_fwd, gconstpointer gcp_id)
213 {
214 struct api_hostfwd *fwd = (struct api_hostfwd *)gcp_fwd;
215 int id = *(int *)gcp_id;
216 return id == fwd->id ? 0 : 1;
210 static int api_handle_req_list_hostfwd(Slirp *slirp, int fd,
211 struct api_ctx *ctx, JSON_Object *jo)
212 {
213 int wrc = 0;
214 JSON_Value *root_value = json_value_init_object(),
215 *entries_value = json_value_init_array();
216 JSON_Object *root_object = json_value_get_object(root_value);
217 JSON_Array *entries_array = json_array(entries_value);
218 char *serialized_string = NULL;
219 g_list_foreach(ctx->hostfwds, api_handle_req_list_hostfwd_foreach,
220 entries_array);
221 json_object_set_value(root_object, "entries", entries_value);
222 serialized_string = json_serialize_to_string(root_value);
223 wrc = write(fd, serialized_string, strlen(serialized_string));
224 json_free_serialized_string(serialized_string);
225 json_value_free(root_value);
226 return wrc;
227 }
228
229 static int api_handle_remove_list_hostfwd_find(gconstpointer gcp_fwd,
230 gconstpointer gcp_id)
231 {
232 struct api_hostfwd *fwd = (struct api_hostfwd *)gcp_fwd;
233 int id = *(int *)gcp_id;
234 return id == fwd->id ? 0 : 1;
217235 }
218236
219237 /*
220238 Handler for remove_hostfwd.
221239 e.g. {"execute": "remove_hostfwd", "arguments": {"id": 42}}
222240 */
223 static int api_handle_req_remove_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx, JSON_Object *jo)
224 {
225 int wrc = 0;
226 int id = (int)json_object_dotget_number(jo, "arguments.id");
227 GList *found = g_list_find_custom(ctx->hostfwds, &id, api_handle_remove_list_hostfwd_find);
228 if (found == NULL) {
229 const char *err = "{\"error\":{\"desc\":\"bad request: remove_hostfwd: bad arguments.id\"}}";
230 wrc = write(fd, err, strlen(err));
231 } else {
232 struct api_hostfwd *fwd = found->data;
233 const char *api_ok = "{\"return\":{}}";
234 if (slirp_remove_hostfwd(slirp, fwd->is_udp, fwd->host_addr, fwd->host_port) < 0) {
235 const char *err =
236 "{\"error\":{\"desc\":\"bad request: remove_hostfwd: slirp_remove_hostfwd failed\"}}";
237 wrc = write(fd, err, strlen(err));
238 } else {
239 ctx->hostfwds = g_list_remove(ctx->hostfwds, fwd);
240 g_free(fwd);
241 wrc = write(fd, api_ok, strlen(api_ok));
242 }
243 }
244 return wrc;
241 static int api_handle_req_remove_hostfwd(Slirp *slirp, int fd,
242 struct api_ctx *ctx, JSON_Object *jo)
243 {
244 int wrc = 0;
245 int id = (int)json_object_dotget_number(jo, "arguments.id");
246 GList *found = g_list_find_custom(ctx->hostfwds, &id,
247 api_handle_remove_list_hostfwd_find);
248 if (found == NULL) {
249 const char *err = "{\"error\":{\"desc\":\"bad request: remove_hostfwd: "
250 "bad arguments.id\"}}";
251 wrc = write(fd, err, strlen(err));
252 } else {
253 struct api_hostfwd *fwd = found->data;
254 const char *api_ok = "{\"return\":{}}";
255 if (slirp_remove_hostfwd(slirp, fwd->is_udp, fwd->host_addr,
256 fwd->host_port) < 0) {
257 const char *err = "{\"error\":{\"desc\":\"bad request: "
258 "remove_hostfwd: slirp_remove_hostfwd failed\"}}";
259 wrc = write(fd, err, strlen(err));
260 } else {
261 ctx->hostfwds = g_list_remove(ctx->hostfwds, fwd);
262 g_free(fwd);
263 wrc = write(fd, api_ok, strlen(api_ok));
264 }
265 }
266 return wrc;
245267 }
246268
247269 static int api_handle_req(Slirp *slirp, int fd, struct api_ctx *ctx)
248270 {
249 JSON_Value *jv = NULL;
250 JSON_Object *jo = NULL;
251 const char *execute = NULL;
252 int wrc = 0;
253 if ((jv = json_parse_string((const char *)ctx->buf)) == NULL) {
254 const char *err = "{\"error\":{\"desc\":\"bad request: cannot parse JSON\"}}";
255 wrc = write(fd, err, strlen(err));
256 goto finish;
257 }
258 if ((jo = json_object(jv)) == NULL) {
259 const char *err = "{\"error\":{\"desc\":\"bad request: json_object() failed\"}}";
260 wrc = write(fd, err, strlen(err));
261 goto finish;
262 }
263 /* TODO: json_validate */
264 if ((execute = json_object_get_string(jo, "execute")) == NULL) {
265 const char *err = "{\"error\":{\"desc\":\"bad request: no execute found\"}}";
266 wrc = write(fd, err, strlen(err));
267 goto finish;
268 }
269 if ((strcmp(execute, "add_hostfwd")) == 0) {
270 wrc = api_handle_req_add_hostfwd(slirp, fd, ctx, jo);
271 } else if ((strcmp(execute, "list_hostfwd")) == 0) {
272 wrc = api_handle_req_list_hostfwd(slirp, fd, ctx, jo);
273 } else if ((strcmp(execute, "remove_hostfwd")) == 0) {
274 wrc = api_handle_req_remove_hostfwd(slirp, fd, ctx, jo);
275 } else {
276 const char *err = "{\"error\":{\"desc\":\"bad request: unknown execute\"}}";
277 wrc = write(fd, err, strlen(err));
278 goto finish;
279 }
280 finish:
281 if (jv != NULL) {
282 json_value_free(jv);
283 }
284 return wrc;
271 JSON_Value *jv = NULL;
272 JSON_Object *jo = NULL;
273 const char *execute = NULL;
274 int wrc = 0;
275 if ((jv = json_parse_string((const char *)ctx->buf)) == NULL) {
276 const char *err =
277 "{\"error\":{\"desc\":\"bad request: cannot parse JSON\"}}";
278 wrc = write(fd, err, strlen(err));
279 goto finish;
280 }
281 if ((jo = json_object(jv)) == NULL) {
282 const char *err =
283 "{\"error\":{\"desc\":\"bad request: json_object() failed\"}}";
284 wrc = write(fd, err, strlen(err));
285 goto finish;
286 }
287 /* TODO: json_validate */
288 if ((execute = json_object_get_string(jo, "execute")) == NULL) {
289 const char *err =
290 "{\"error\":{\"desc\":\"bad request: no execute found\"}}";
291 wrc = write(fd, err, strlen(err));
292 goto finish;
293 }
294 if ((strcmp(execute, "add_hostfwd")) == 0) {
295 wrc = api_handle_req_add_hostfwd(slirp, fd, ctx, jo);
296 } else if ((strcmp(execute, "list_hostfwd")) == 0) {
297 wrc = api_handle_req_list_hostfwd(slirp, fd, ctx, jo);
298 } else if ((strcmp(execute, "remove_hostfwd")) == 0) {
299 wrc = api_handle_req_remove_hostfwd(slirp, fd, ctx, jo);
300 } else {
301 const char *err =
302 "{\"error\":{\"desc\":\"bad request: unknown execute\"}}";
303 wrc = write(fd, err, strlen(err));
304 goto finish;
305 }
306 finish:
307 if (jv != NULL) {
308 json_value_free(jv);
309 }
310 return wrc;
285311 }
286312
287313 /*
290316 */
291317 int api_handler(Slirp *slirp, int listenfd, struct api_ctx *ctx)
292318 {
293 struct sockaddr_un addr;
294 socklen_t addrlen = sizeof(struct sockaddr_un);
295 int fd;
296 int rc = 0, wrc = 0;
297 ssize_t len;
298 memset(&addr, 0, sizeof(addr));
299 if ((fd = accept(listenfd, (struct sockaddr *)&addr, &addrlen)) < 0) {
300 perror("api_handler: accept");
301 return -1;
302 }
303 if ((len = read(fd, ctx->buf, ctx->buflen)) < 0) {
304 perror("api_handler: read");
305 rc = len;
306 goto finish;
307 }
308 if (len == ctx->buflen) {
309 const char *err = "{\"error\":{\"desc\":\"bad request: too large message\"}}";
310 fprintf(stderr, "api_handler: too large message (>= %ld bytes)\n", len);
311 wrc = write(fd, err, strlen(err));
312 rc = -1;
313 goto finish;
314 }
315 ctx->buf[len] = 0;
316 fprintf(stderr, "api_handler: got request: %s\n", ctx->buf);
317 wrc = api_handle_req(slirp, fd, ctx);
318 finish:
319 shutdown(fd, SHUT_RDWR);
320 if (rc == 0 && wrc != 0) {
321 rc = wrc;
322 }
323 close(fd);
324 return rc;
325 }
319 struct sockaddr_un addr;
320 socklen_t addrlen = sizeof(struct sockaddr_un);
321 int fd;
322 int rc = 0, wrc = 0;
323 ssize_t len;
324 memset(&addr, 0, sizeof(addr));
325 if ((fd = accept(listenfd, (struct sockaddr *)&addr, &addrlen)) < 0) {
326 perror("api_handler: accept");
327 return -1;
328 }
329 if ((len = read(fd, ctx->buf, ctx->buflen)) < 0) {
330 perror("api_handler: read");
331 rc = len;
332 goto finish;
333 }
334 if (len == ctx->buflen) {
335 const char *err =
336 "{\"error\":{\"desc\":\"bad request: too large message\"}}";
337 fprintf(stderr, "api_handler: too large message (>= %ld bytes)\n", len);
338 wrc = write(fd, err, strlen(err));
339 rc = -1;
340 goto finish;
341 }
342 ctx->buf[len] = 0;
343 fprintf(stderr, "api_handler: got request: %s\n", ctx->buf);
344 wrc = api_handle_req(slirp, fd, ctx);
345 finish:
346 shutdown(fd, SHUT_RDWR);
347 if (rc == 0 && wrc != 0) {
348 rc = wrc;
349 }
350 close(fd);
351 return rc;
352 }
0 /* SPDX-License-Identifier: GPL-2.0-or-later */
01 #ifndef SLIRP4NETNS_API_H
12 # define SLIRP4NETNS_API_H
23 int api_bindlisten(const char *api_socket);
00 AC_PREREQ([2.69])
1 AC_INIT([slirp4netns], [0.3.2], [https://github.com/rootless-containers/slirp4netns/issues])
1 AC_INIT([slirp4netns], [0.4.1], [https://github.com/rootless-containers/slirp4netns/issues])
22 AC_CONFIG_SRCDIR([main.c])
33 AC_CONFIG_HEADERS([config.h])
44
77
88 AM_INIT_AUTOMAKE([1.9 foreign subdir-objects])
99
10 AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/ioctl.h sys/socket.h sys/timeb.h unistd.h getopt.h])
10 AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stddef.h stdint.h stdlib.h string.h sys/ioctl.h sys/mount.h sys/socket.h sys/timeb.h unistd.h getopt.h])
1111
1212 AC_CHECK_HEADER_STDBOOL
1313 AC_C_INLINE
2424 AC_TYPE_UINT8_T
2525
2626 AC_FUNC_ALLOCA
27 AC_FUNC_STRTOD
2728 AC_FUNC_FORK
2829 AC_FUNC_MALLOC
2930 AC_FUNC_REALLOC
30 AC_CHECK_FUNCS([atexit clock_gettime inet_ntoa memmove memset socket strchr strdup strerror strstr strtol getopt_long])
31 AC_CHECK_FUNCS([atexit clock_gettime dup2 getopt_long inet_ntoa memmove memset mkdir regcomp rmdir socket strcasecmp strchr strdup strerror strstr strtol strtoul])
3132
3233 PKG_CHECK_MODULES(GLIB, glib-2.0)
34 PKG_CHECK_MODULES(LIBCAP, libcap)
35 PKG_CHECK_MODULES(LIBSECCOMP, libseccomp)
3336
3437 AC_CONFIG_FILES([Makefile])
3538 AC_OUTPUT
0 #!/bin/bash
1 # release.sh: configurable signed-artefact release script
2 # Copyright (C) 2016-2019 SUSE LLC.
3 #
4 # This Source Code Form is subject to the terms of the Mozilla Public
5 # License, v2.0. If a copy of the MPL was not distributed with this
6 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
8 set -Eeuo pipefail
9
10 ## --->
11 # Project-specific options and functions. In *theory* you shouldn't need to
12 # touch anything else in this script in order to use this elsewhere.
13 project="slirp4netns"
14 root="$(readlink -f "$(dirname "${BASH_SOURCE}")/..")"
15
16 # Make pushd and popd silent.
17 function pushd() { command pushd "$@" &>/dev/null ; }
18 function popd() { command popd "$@" &>/dev/null ; }
19
20 # These functions allow you to configure how the defaults are computed.
21 function get_arch() { uname -m ; }
22 function get_version() { echo '@VERSION@' | "$root/config.status" --file - ; }
23
24 # Any pre-configuration steps should be done here -- for instance ./configure.
25 function setup_project() {
26 pushd "$root"
27 [ -x ./configure ] || ./autogen.sh
28 ./configure LDFLAGS="-static" --prefix=/ --bindir=/bin
29 popd
30 }
31
32 # This function takes an output path as an argument, where the built
33 # (preferably static) binary should be placed.
34 function build_project() {
35 tmprootfs="$(mktemp -d --tmpdir "$project-build.XXXXXX")"
36
37 make -C "$root" clean all install DESTDIR="$tmprootfs"
38
39 mv "$tmprootfs/bin/slirp4netns" "$1"
40 rm -rf "$tmprootfs"
41 }
42 # End of the easy-to-configure portion.
43 ## <---
44
45 # Print usage information.
46 function usage() {
47 echo "usage: release.sh [-h] [-v <version>] [-c <commit>] [-o <output-dir>]" >&2
48 echo " [-H <hashcmd>] [-S <gpg-key>]" >&2
49 }
50
51 # Log something to stderr.
52 function log() {
53 echo "[*]" "$@" >&2
54 }
55
56 # Log something to stderr and then exit with 0.
57 function quit() {
58 log "$@"
59 exit 0
60 }
61
62 # Conduct a sanity-check to make sure that GPG provided with the given
63 # arguments can sign something. Inability to sign things is not a fatal error.
64 function gpg_cansign() {
65 gpg "$@" --clear-sign </dev/null >/dev/null
66 }
67
68 # When creating releases we need to build (ideally static) binaries, an archive
69 # of the current commit, and generate detached signatures for both.
70 keyid=""
71 version=""
72 arch=""
73 commit="HEAD"
74 hashcmd="sha256sum"
75 while getopts ":h:v:c:o:S:H:" opt; do
76 case "$opt" in
77 S)
78 keyid="$OPTARG"
79 ;;
80 c)
81 commit="$OPTARG"
82 ;;
83 o)
84 outputdir="$OPTARG"
85 ;;
86 v)
87 version="$OPTARG"
88 ;;
89 H)
90 hashcmd="$OPTARG"
91 ;;
92 h)
93 usage ; exit 0
94 ;;
95 \:)
96 echo "Missing argument: -$OPTARG" >&2
97 usage ; exit 1
98 ;;
99 \?)
100 echo "Invalid option: -$OPTARG" >&2
101 usage ; exit 1
102 ;;
103 esac
104 done
105
106 # Run project setup first...
107 ( set -x ; setup_project )
108
109 # Generate the defaults for version and so on *after* argument parsing and
110 # setup_project, to avoid calling get_version() needlessly.
111 version="${version:-$(get_version)}"
112 arch="${arch:-$(get_arch)}"
113 outputdir="${outputdir:-release/$version}"
114
115 log "[[ $project ]]"
116 log "version: $version"
117 log "commit: $commit"
118 log "output_dir: $outputdir"
119 log "key: ${keyid:-(default)}"
120 log "hash_cmd: $hashcmd"
121
122 # Make explicit what we're doing.
123 set -x
124
125 # Make the release directory.
126 rm -rf "$outputdir" && mkdir -p "$outputdir"
127
128 # Build project.
129 build_project "$outputdir/$project.$arch"
130
131 # Generate new archive.
132 git archive --format=tar --prefix="$project-$version/" "$commit" | xz > "$outputdir/$project.tar.xz"
133
134 # Generate sha256 checksums for both.
135 ( cd "$outputdir" ; "$hashcmd" "$project".{"$arch",tar.xz} > "$project.$hashcmd" ; )
136
137 # Set up the gpgflags.
138 gpgflags=()
139 [[ -z "$keyid" ]] || gpgflags+=("--default-key=$keyid")
140 gpg_cansign "${gpgflags[@]}" || quit "Could not find suitable GPG key, skipping signing step."
141
142 # Sign everything.
143 gpg "${gpgflags[@]}" --detach-sign --armor "$outputdir/$project.$arch"
144 gpg "${gpgflags[@]}" --detach-sign --armor "$outputdir/$project.tar.xz"
145 gpg "${gpgflags[@]}" --clear-sign --armor \
146 --output "$outputdir/$project.$hashcmd"{.tmp,} && \
147 mv "$outputdir/$project.$hashcmd"{.tmp,}
+675
-510
main.c less more
0 /* SPDX-License-Identifier: GPL-2.0-or-later */
01 #define _GNU_SOURCE
12 #include "config.h"
23 #include <stdio.h>
2122
2223 #define DEFAULT_MTU (1500)
2324 #define DEFAULT_CIDR ("10.0.2.0/24")
24 #define DEFAULT_VHOST_OFFSET (2) // 10.0.2.2
25 #define DEFAULT_VDHCPSTART_OFFSET (15) // 10.0.2.15
26 #define DEFAULT_VNAMESERVER_OFFSET (3) // 10.0.2.3
27 #define DEFAULT_RECOMMENDED_VGUEST_OFFSET (100) // 10.0.2.100
25 #define DEFAULT_VHOST_OFFSET (2) // 10.0.2.2
26 #define DEFAULT_VDHCPSTART_OFFSET (15) // 10.0.2.15
27 #define DEFAULT_VNAMESERVER_OFFSET (3) // 10.0.2.3
28 #define DEFAULT_RECOMMENDED_VGUEST_OFFSET (100) // 10.0.2.100
29 #define DEFAULT_NETNS_TYPE ("pid")
2830 #define NETWORK_PREFIX_MIN (1)
29 // >=26 is not supported because the recommended guest IP is set to network addr + 100 .
31 // >=26 is not supported because the recommended guest IP is set to network addr
32 // + 100 .
3033 #define NETWORK_PREFIX_MAX (25)
3134
32 static int nsenter(pid_t target_pid)
33 {
34 char userns[32], netns[32];
35 int usernsfd, netnsfd;
36 snprintf(userns, sizeof(userns), "/proc/%d/ns/user", target_pid);
37 snprintf(netns, sizeof(netns), "/proc/%d/ns/net", target_pid);
38 if ((usernsfd = open(userns, O_RDONLY)) < 0) {
39 perror(userns);
40 return usernsfd;
41 }
42 if ((netnsfd = open(netns, O_RDONLY)) < 0) {
43 perror(netns);
44 return netnsfd;
45 }
46 setns(usernsfd, CLONE_NEWUSER);
47 if (setns(netnsfd, CLONE_NEWNET) < 0) {
48 perror("setns(CLONE_NEWNET)");
49 return -1;
50 }
51 close(usernsfd);
52 close(netnsfd);
53 return 0;
35 static int nsenter(pid_t target_pid, char *netns, char *userns,
36 bool only_userns)
37 {
38 int usernsfd = -1, netnsfd = -1;
39 if (!only_userns && !netns) {
40 if (asprintf(&netns, "/proc/%d/ns/net", target_pid) < 0) {
41 perror("cannot get netns path");
42 return -1;
43 }
44 }
45 if (!userns && target_pid) {
46 if (asprintf(&userns, "/proc/%d/ns/user", target_pid) < 0) {
47 perror("cannot get userns path");
48 return -1;
49 }
50 }
51 if (!only_userns && (netnsfd = open(netns, O_RDONLY)) < 0) {
52 perror(netns);
53 return netnsfd;
54 }
55 if (userns && (usernsfd = open(userns, O_RDONLY)) < 0) {
56 perror(userns);
57 return usernsfd;
58 }
59
60 if (usernsfd != -1) {
61 int r = setns(usernsfd, CLONE_NEWUSER);
62 if (only_userns && r < 0) {
63 perror("setns(CLONE_NEWUSER)");
64 return -1;
65 }
66 close(usernsfd);
67 }
68 if (netnsfd != -1 && setns(netnsfd, CLONE_NEWNET) < 0) {
69 perror("setns(CLONE_NEWNET)");
70 return -1;
71 }
72 close(netnsfd);
73 return 0;
5474 }
5575
5676 static int open_tap(const char *tapname)
5777 {
58 int fd;
59 struct ifreq ifr;
60 if ((fd = open("/dev/net/tun", O_RDWR)) < 0) {
61 perror("open(\"/dev/net/tun\")");
62 return fd;
63 }
64 memset(&ifr, 0, sizeof(ifr));
65 ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
66 strncpy(ifr.ifr_name, tapname, sizeof(ifr.ifr_name) - 1);
67 if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) {
68 perror("ioctl(TUNSETIFF)");
69 close(fd);
70 return -1;
71 }
72 return fd;
78 int fd;
79 struct ifreq ifr;
80 if ((fd = open("/dev/net/tun", O_RDWR)) < 0) {
81 perror("open(\"/dev/net/tun\")");
82 return fd;
83 }
84 memset(&ifr, 0, sizeof(ifr));
85 ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
86 strncpy(ifr.ifr_name, tapname, sizeof(ifr.ifr_name) - 1);
87 if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) {
88 perror("ioctl(TUNSETIFF)");
89 close(fd);
90 return -1;
91 }
92 return fd;
7393 }
7494
7595 static int sendfd(int sock, int fd)
7696 {
77 ssize_t rc;
78 struct msghdr msg;
79 struct cmsghdr *cmsg;
80 char cmsgbuf[CMSG_SPACE(sizeof(fd))];
81 struct iovec iov;
82 char dummy = '\0';
83 memset(&msg, 0, sizeof(msg));
84 iov.iov_base = &dummy;
85 iov.iov_len = 1;
86 msg.msg_iov = &iov;
87 msg.msg_iovlen = 1;
88 msg.msg_control = cmsgbuf;
89 msg.msg_controllen = sizeof(cmsgbuf);
90 cmsg = CMSG_FIRSTHDR(&msg);
91 cmsg->cmsg_level = SOL_SOCKET;
92 cmsg->cmsg_type = SCM_RIGHTS;
93 cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
94 memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
95 msg.msg_controllen = cmsg->cmsg_len;
96 if ((rc = sendmsg(sock, &msg, 0)) < 0) {
97 perror("sendmsg");
98 }
99 return rc;
100 }
101
102 static int configure_network(const char *tapname, struct slirp4netns_config *cfg)
103 {
104 struct rtentry route;
105 struct ifreq ifr;
106 struct sockaddr_in *sai = (struct sockaddr_in *)&ifr.ifr_addr;
107 int sockfd;
108
109 sockfd = socket(AF_INET, SOCK_DGRAM, 0);
110 if (sockfd < 0) {
111 perror("cannot create socket");
112 return -1;
113 }
114
115 memset(&ifr, 0, sizeof(ifr));
116 ifr.ifr_flags = IFF_UP | IFF_RUNNING;
117 strncpy(ifr.ifr_name, tapname, sizeof(ifr.ifr_name) - 1);
118
119 if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
120 perror("cannot set device up");
121 return -1;
122 }
123
124 ifr.ifr_mtu = (int)cfg->mtu;
125 if (ioctl(sockfd, SIOCSIFMTU, &ifr) < 0) {
126 perror("cannot set MTU");
127 return -1;
128 }
129
130 sai->sin_family = AF_INET;
131 sai->sin_port = 0;
132 sai->sin_addr = cfg->recommended_vguest;
133
134 if (ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) {
135 perror("cannot set device address");
136 return -1;
137 }
138
139 sai->sin_addr = cfg->vnetmask;
140 if (ioctl(sockfd, SIOCSIFNETMASK, &ifr) < 0) {
141 perror("cannot set device netmask");
142 return -1;
143 }
144
145 memset(&route, 0, sizeof(route));
146 sai = (struct sockaddr_in *)&route.rt_gateway;
147 sai->sin_family = AF_INET;
148 sai->sin_addr = cfg->vhost;
149 sai = (struct sockaddr_in *)&route.rt_dst;
150 sai->sin_family = AF_INET;
151 sai->sin_addr.s_addr = INADDR_ANY;
152 sai = (struct sockaddr_in *)&route.rt_genmask;
153 sai->sin_family = AF_INET;
154 sai->sin_addr.s_addr = INADDR_ANY;
155
156 route.rt_flags = RTF_UP | RTF_GATEWAY;
157 route.rt_metric = 0;
158 route.rt_dev = (char *)tapname;
159
160 if (ioctl(sockfd, SIOCADDRT, &route) < 0) {
161 perror("set route");
162 return -1;
163 }
164 return 0;
165 }
166
167 static int child(int sock, pid_t target_pid, bool do_config_network, const char *tapname, int ready_fd,
168 struct slirp4netns_config *cfg)
169 {
170 int rc, tapfd;
171 if ((rc = nsenter(target_pid)) < 0) {
172 return rc;
173 }
174 if ((tapfd = open_tap(tapname)) < 0) {
175 return tapfd;
176 }
177 if (do_config_network && configure_network(tapname, cfg) < 0) {
178 return -1;
179 }
180 if (ready_fd >= 0) {
181 do
182 rc = write(ready_fd, "1", 1);
183 while (rc < 0 && errno == EINTR);
184 close(ready_fd);
185 }
186 if (sendfd(sock, tapfd) < 0) {
187 close(tapfd);
188 close(sock);
189 return -1;
190 }
191 fprintf(stderr, "sent tapfd=%d for %s\n", tapfd, tapname);
192 close(sock);
193 return 0;
97 ssize_t rc;
98 struct msghdr msg;
99 struct cmsghdr *cmsg;
100 char cmsgbuf[CMSG_SPACE(sizeof(fd))];
101 struct iovec iov;
102 char dummy = '\0';
103 memset(&msg, 0, sizeof(msg));
104 iov.iov_base = &dummy;
105 iov.iov_len = 1;
106 msg.msg_iov = &iov;
107 msg.msg_iovlen = 1;
108 msg.msg_control = cmsgbuf;
109 msg.msg_controllen = sizeof(cmsgbuf);
110 cmsg = CMSG_FIRSTHDR(&msg);
111 cmsg->cmsg_level = SOL_SOCKET;
112 cmsg->cmsg_type = SCM_RIGHTS;
113 cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
114 memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
115 msg.msg_controllen = cmsg->cmsg_len;
116 if ((rc = sendmsg(sock, &msg, 0)) < 0) {
117 perror("sendmsg");
118 }
119 return rc;
120 }
121
122 static int configure_network(const char *tapname,
123 struct slirp4netns_config *cfg)
124 {
125 struct rtentry route;
126 struct ifreq ifr;
127 struct sockaddr_in *sai = (struct sockaddr_in *)&ifr.ifr_addr;
128 int sockfd;
129
130 sockfd = socket(AF_INET, SOCK_DGRAM, 0);
131 if (sockfd < 0) {
132 perror("cannot create socket");
133 return -1;
134 }
135
136 // set loopback device to UP
137 struct ifreq ifr_lo = { .ifr_name = "lo",
138 .ifr_flags = IFF_UP | IFF_RUNNING };
139 if (ioctl(sockfd, SIOCSIFFLAGS, &ifr_lo) < 0) {
140 perror("cannot set device up");
141 return -1;
142 }
143
144 memset(&ifr, 0, sizeof(ifr));
145 ifr.ifr_flags = IFF_UP | IFF_RUNNING;
146 strncpy(ifr.ifr_name, tapname, sizeof(ifr.ifr_name) - 1);
147
148 if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
149 perror("cannot set device up");
150 return -1;
151 }
152
153 ifr.ifr_mtu = (int)cfg->mtu;
154 if (ioctl(sockfd, SIOCSIFMTU, &ifr) < 0) {
155 perror("cannot set MTU");
156 return -1;
157 }
158
159 sai->sin_family = AF_INET;
160 sai->sin_port = 0;
161 sai->sin_addr = cfg->recommended_vguest;
162
163 if (ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) {
164 perror("cannot set device address");
165 return -1;
166 }
167
168 sai->sin_addr = cfg->vnetmask;
169 if (ioctl(sockfd, SIOCSIFNETMASK, &ifr) < 0) {
170 perror("cannot set device netmask");
171 return -1;
172 }
173
174 memset(&route, 0, sizeof(route));
175 sai = (struct sockaddr_in *)&route.rt_gateway;
176 sai->sin_family = AF_INET;
177 sai->sin_addr = cfg->vhost;
178 sai = (struct sockaddr_in *)&route.rt_dst;
179 sai->sin_family = AF_INET;
180 sai->sin_addr.s_addr = INADDR_ANY;
181 sai = (struct sockaddr_in *)&route.rt_genmask;
182 sai->sin_family = AF_INET;
183 sai->sin_addr.s_addr = INADDR_ANY;
184
185 route.rt_flags = RTF_UP | RTF_GATEWAY;
186 route.rt_metric = 0;
187 route.rt_dev = (char *)tapname;
188
189 if (ioctl(sockfd, SIOCADDRT, &route) < 0) {
190 perror("set route");
191 return -1;
192 }
193 return 0;
194 }
195
196 static int child(int sock, pid_t target_pid, bool do_config_network,
197 const char *tapname, char *netns_path, char *userns_path,
198 struct slirp4netns_config *cfg)
199 {
200 int rc, tapfd;
201 if ((rc = nsenter(target_pid, netns_path, userns_path, false)) < 0) {
202 return rc;
203 }
204 if ((tapfd = open_tap(tapname)) < 0) {
205 return tapfd;
206 }
207 if (do_config_network && configure_network(tapname, cfg) < 0) {
208 return -1;
209 }
210 if (sendfd(sock, tapfd) < 0) {
211 close(tapfd);
212 close(sock);
213 return -1;
214 }
215 fprintf(stderr, "sent tapfd=%d for %s\n", tapfd, tapname);
216 close(sock);
217 return 0;
194218 }
195219
196220 static int recvfd(int sock)
197221 {
198 int fd;
199 ssize_t rc;
200 struct msghdr msg;
201 struct cmsghdr *cmsg;
202 char cmsgbuf[CMSG_SPACE(sizeof(fd))];
203 struct iovec iov;
204 char dummy = '\0';
205 memset(&msg, 0, sizeof(msg));
206 iov.iov_base = &dummy;
207 iov.iov_len = 1;
208 msg.msg_iov = &iov;
209 msg.msg_iovlen = 1;
210 msg.msg_control = cmsgbuf;
211 msg.msg_controllen = sizeof(cmsgbuf);
212 if ((rc = recvmsg(sock, &msg, 0)) < 0) {
213 perror("recvmsg");
214 return (int)rc;
215 }
216 if (rc == 0) {
217 fprintf(stderr, "the message is empty\n");
218 return -1;
219 }
220 cmsg = CMSG_FIRSTHDR(&msg);
221 if (cmsg == NULL || cmsg->cmsg_type != SCM_RIGHTS) {
222 fprintf(stderr, "the message does not contain fd\n");
223 return -1;
224 }
225 memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd));
226 return fd;
227 }
228
229 static int parent(int sock, int exit_fd, const char *api_socket, struct slirp4netns_config *cfg)
230 {
231 int rc, tapfd;
232 if ((tapfd = recvfd(sock)) < 0) {
233 return tapfd;
234 }
235 fprintf(stderr, "received tapfd=%d\n", tapfd);
236 close(sock);
237 printf("Starting slirp\n");
238 printf("* MTU: %d\n", cfg->mtu);
239 printf("* Network: %s\n", inet_ntoa(cfg->vnetwork));
240 printf("* Netmask: %s\n", inet_ntoa(cfg->vnetmask));
241 printf("* Gateway: %s\n", inet_ntoa(cfg->vhost));
242 printf("* DNS: %s\n", inet_ntoa(cfg->vnameserver));
243 printf("* Recommended IP: %s\n", inet_ntoa(cfg->recommended_vguest));
244 if (api_socket != NULL) {
245 printf("* API Socket: %s\n", api_socket);
246 }
247 if (!cfg->disable_host_loopback) {
248 printf
249 ("WARNING: 127.0.0.1:* on the host is accessible as %s (set --disable-host-loopback to prohibit connecting to 127.0.0.1:*)\n",
250 inet_ntoa(cfg->vhost));
251 }
252 if ((rc = do_slirp(tapfd, exit_fd, api_socket, cfg)) < 0) {
253 fprintf(stderr, "do_slirp failed\n");
254 close(tapfd);
255 return rc;
256 }
257 /* NOT REACHED */
258 return 0;
222 int fd;
223 ssize_t rc;
224 struct msghdr msg;
225 struct cmsghdr *cmsg;
226 char cmsgbuf[CMSG_SPACE(sizeof(fd))];
227 struct iovec iov;
228 char dummy = '\0';
229 memset(&msg, 0, sizeof(msg));
230 iov.iov_base = &dummy;
231 iov.iov_len = 1;
232 msg.msg_iov = &iov;
233 msg.msg_iovlen = 1;
234 msg.msg_control = cmsgbuf;
235 msg.msg_controllen = sizeof(cmsgbuf);
236 if ((rc = recvmsg(sock, &msg, 0)) < 0) {
237 perror("recvmsg");
238 return (int)rc;
239 }
240 if (rc == 0) {
241 fprintf(stderr, "the message is empty\n");
242 return -1;
243 }
244 cmsg = CMSG_FIRSTHDR(&msg);
245 if (cmsg == NULL || cmsg->cmsg_type != SCM_RIGHTS) {
246 fprintf(stderr, "the message does not contain fd\n");
247 return -1;
248 }
249 memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd));
250 return fd;
251 }
252
253 static int parent(int sock, int ready_fd, int exit_fd, const char *api_socket,
254 struct slirp4netns_config *cfg, pid_t target_pid)
255 {
256 int rc, tapfd;
257 if ((tapfd = recvfd(sock)) < 0) {
258 return tapfd;
259 }
260 fprintf(stderr, "received tapfd=%d\n", tapfd);
261 close(sock);
262 printf("Starting slirp\n");
263 printf("* MTU: %d\n", cfg->mtu);
264 printf("* Network: %s\n", inet_ntoa(cfg->vnetwork));
265 printf("* Netmask: %s\n", inet_ntoa(cfg->vnetmask));
266 printf("* Gateway: %s\n", inet_ntoa(cfg->vhost));
267 printf("* DNS: %s\n", inet_ntoa(cfg->vnameserver));
268 printf("* Recommended IP: %s\n", inet_ntoa(cfg->recommended_vguest));
269 if (api_socket != NULL) {
270 printf("* API Socket: %s\n", api_socket);
271 }
272 if (!cfg->disable_host_loopback) {
273 printf(
274 "WARNING: 127.0.0.1:* on the host is accessible as %s (set "
275 "--disable-host-loopback to prohibit connecting to 127.0.0.1:*)\n",
276 inet_ntoa(cfg->vhost));
277 }
278 if (cfg->enable_sandbox && geteuid() != 0) {
279 if ((rc = nsenter(target_pid, NULL, NULL, true)) < 0) {
280 close(tapfd);
281 return rc;
282 }
283 if ((rc = setegid(0)) < 0) {
284 fprintf(stderr, "setegid(0)\n");
285 close(tapfd);
286 return rc;
287 }
288 if ((rc = seteuid(0)) < 0) {
289 fprintf(stderr, "seteuid(0)\n");
290 close(tapfd);
291 return rc;
292 }
293 }
294 if ((rc = do_slirp(tapfd, ready_fd, exit_fd, api_socket, cfg)) < 0) {
295 fprintf(stderr, "do_slirp failed\n");
296 close(tapfd);
297 return rc;
298 }
299 /* NOT REACHED */
300 return 0;
259301 }
260302
261303 static void usage(const char *argv0)
262304 {
263 printf("Usage: %s [OPTION]... PID TAPNAME\n", argv0);
264 printf("User-mode networking for unprivileged network namespaces.\n\n");
265 printf("-c, --configure bring up the interface\n");
266 printf("-e, --exit-fd=FD specify the FD for terminating slirp4netns\n");
267 printf("-r, --ready-fd=FD specify the FD to write to when the network is configured\n");
268 printf("-m, --mtu=MTU specify MTU (default=%d, max=65521)\n", DEFAULT_MTU);
269 printf("--cidr=CIDR specify network address CIDR (default=%s)\n", DEFAULT_CIDR);
270 printf("--disable-host-loopback prohibit connecting to 127.0.0.1:* on the host namespace\n");
271 printf("-a, --api-socket=PATH specify API socket path\n");
272 printf("-6, --enable-ipv6 enable IPv6 (experimental)\n");
273 printf("-h, --help show this help and exit\n");
274 printf("-v, --version show version and exit\n");
305 printf("Usage: %s [OPTION]... PID|PATH TAPNAME\n", argv0);
306 printf("User-mode networking for unprivileged network namespaces.\n\n");
307 printf("-c, --configure bring up the interface\n");
308 printf("-e, --exit-fd=FD specify the FD for terminating "
309 "slirp4netns\n");
310 printf("-r, --ready-fd=FD specify the FD to write to when the "
311 "network is configured\n");
312 /* v0.2.0 */
313 printf("-m, --mtu=MTU specify MTU (default=%d, max=65521)\n",
314 DEFAULT_MTU);
315 printf("-6, --enable-ipv6 enable IPv6 (experimental)\n");
316 /* v0.3.0 */
317 printf("-a, --api-socket=PATH specify API socket path\n");
318 printf(
319 "--cidr=CIDR specify network address CIDR (default=%s)\n",
320 DEFAULT_CIDR);
321 printf("--disable-host-loopback prohibit connecting to 127.0.0.1:* on the "
322 "host namespace\n");
323 /* v0.4.0 */
324 printf("--netns-type=TYPE specify network namespace type ([path|pid], "
325 "default=%s)\n",
326 DEFAULT_NETNS_TYPE);
327 printf("--userns-path=PATH specify user namespace path\n");
328 printf("--enable-sandbox create a new mount namespace and drop all "
329 "capabilities except CAP_NET_BIND_SERVICE (experimental)\n");
330 printf("--enable-seccomp enable seccomp to limit syscalls "
331 "(experimental)\n");
332 /* others */
333 printf("-h, --help show this help and exit\n");
334 printf("-v, --version show version and exit\n");
275335 }
276336
277337 // version output is runc-compatible and machine-parsable
278338 static void version()
279339 {
280 printf("slirp4netns version %s\n", VERSION ? VERSION : PACKAGE_VERSION);
340 printf("slirp4netns version %s\n", VERSION ? VERSION : PACKAGE_VERSION);
281341 #ifdef COMMIT
282 printf("commit: %s\n", COMMIT);
342 printf("commit: %s\n", COMMIT);
283343 #endif
284344 }
285345
286346 struct options {
287 pid_t target_pid; // argv[1]
288 char *tapname; // argv[2]
289 bool do_config_network; // -c
290 int exit_fd; // -e
291 int ready_fd; // -r
292 unsigned int mtu; // -m
293 bool disable_host_loopback; // --disable-host-loopback
294 char *cidr; // --cidr
295 bool enable_ipv6; // -6
296 char *api_socket; // -a
347 pid_t target_pid; // argv[1]
348 char *tapname; // argv[2]
349 bool do_config_network; // -c
350 int exit_fd; // -e
351 int ready_fd; // -r
352 unsigned int mtu; // -m
353 bool disable_host_loopback; // --disable-host-loopback
354 char *cidr; // --cidr
355 bool enable_ipv6; // -6
356 char *api_socket; // -a
357 char *netns_type; // argv[1]
358 char *netns_path; // --netns-path
359 char *userns_path; // --userns-path
360 bool enable_sandbox; // --enable-sandbox
361 bool enable_seccomp; // --enable-seccomp
297362 };
298363
299364 static void options_init(struct options *options)
300365 {
301 memset(options, 0, sizeof(*options));
302 options->exit_fd = options->ready_fd = -1;
303 options->mtu = DEFAULT_MTU;
366 memset(options, 0, sizeof(*options));
367 options->exit_fd = options->ready_fd = -1;
368 options->mtu = DEFAULT_MTU;
304369 }
305370
306371 static void options_destroy(struct options *options)
307372 {
308 if (options->tapname != NULL) {
309 free(options->tapname);
310 options->tapname = NULL;
311 }
312 if (options->cidr != NULL) {
313 free(options->cidr);
314 options->cidr = NULL;
315 }
316 if (options->api_socket != NULL) {
317 free(options->api_socket);
318 options->api_socket = NULL;
319 }
320 // options itself is not freed, because it can be on the stack.
373 if (options->tapname != NULL) {
374 free(options->tapname);
375 options->tapname = NULL;
376 }
377 if (options->cidr != NULL) {
378 free(options->cidr);
379 options->cidr = NULL;
380 }
381 if (options->api_socket != NULL) {
382 free(options->api_socket);
383 options->api_socket = NULL;
384 }
385 if (options->netns_type != NULL) {
386 free(options->netns_type);
387 options->netns_type = NULL;
388 }
389 if (options->netns_path != NULL) {
390 free(options->netns_path);
391 options->netns_path = NULL;
392 }
393 if (options->userns_path != NULL) {
394 free(options->userns_path);
395 options->userns_path = NULL;
396 }
321397 }
322398
323399 // * caller does not need to call options_init()
325401 // * this function calls exit() on an error.
326402 static void parse_args(int argc, char *const argv[], struct options *options)
327403 {
328 int opt;
404 int opt;
405 char *strtol_e = NULL;
406 char *optarg_cidr = NULL;
407 char *optarg_netns_type = NULL;
408 char *optarg_userns_path = NULL;
409 char *optarg_api_socket = NULL;
329410 #define CIDR -42
330411 #define DISABLE_HOST_LOOPBACK -43
331 #define _DEPRECATED_NO_HOST_LOOPBACK -10043 // deprecated in favor of disable-host-loopback
332 const struct option longopts[] = {
333 {"configure", no_argument, NULL, 'c'},
334 {"exit-fd", required_argument, NULL, 'e'},
335 {"ready-fd", required_argument, NULL, 'r'},
336 {"mtu", required_argument, NULL, 'm'},
337 {"cidr", required_argument, NULL, CIDR},
338 {"disable-host-loopback", no_argument, NULL, DISABLE_HOST_LOOPBACK},
339 {"no-host-loopback", no_argument, NULL, _DEPRECATED_NO_HOST_LOOPBACK},
340 {"api-socket", required_argument, NULL, 'a'},
341 {"enable-ipv6", no_argument, NULL, '6'},
342 {"help", no_argument, NULL, 'h'},
343 {"version", no_argument, NULL, 'v'},
344 {0, 0, 0, 0},
345 };
346 options_init(options);
347 while ((opt = getopt_long(argc, argv, "ce:r:m:a:6hv", longopts, NULL)) != -1) {
348 switch (opt) {
349 case 'c':
350 options->do_config_network = true;
351 break;
352 case 'e':
353 errno = 0;
354 options->exit_fd = strtol(optarg, NULL, 10);
355 if (errno) {
356 perror("strtol");
357 usage(argv[0]);
358 exit(EXIT_FAILURE);
359 }
360 break;
361 case 'r':
362 errno = 0;
363 options->ready_fd = strtol(optarg, NULL, 10);
364 if (errno) {
365 perror("strtol");
366 usage(argv[0]);
367 exit(EXIT_FAILURE);
368 }
369 break;
370 case 'm':
371 errno = 0;
372 options->mtu = strtol(optarg, NULL, 10);
373 if (errno) {
374 perror("strtol");
375 usage(argv[0]);
376 exit(EXIT_FAILURE);
377 }
378 break;
379 case CIDR:
380 options->cidr = strdup(optarg);
381 break;
382 case _DEPRECATED_NO_HOST_LOOPBACK:
383 // There was no tagged release with support for --no-host-loopback.
384 // So no one will be affected by removal of --no-host-loopback.
385 printf
386 ("WARNING: --no-host-loopback is deprecated and will be removed in future releases, please use --disable-host-loopback instead.\n");
387 /* FALLTHROUGH */
388 case DISABLE_HOST_LOOPBACK:
389 options->disable_host_loopback = true;
390 break;
391 case 'a':
392 options->api_socket = strdup(optarg);
393 break;
394 case '6':
395 options->enable_ipv6 = true;
396 printf("WARNING: Support for IPv6 is experimental\n");
397 break;
398 case 'h':
399 usage(argv[0]);
400 exit(EXIT_SUCCESS);
401 case 'v':
402 version();
403 exit(EXIT_SUCCESS);
404 default:
405 usage(argv[0]);
406 exit(EXIT_FAILURE);
407 }
408 }
412 #define NETNS_TYPE -44
413 #define USERNS_PATH -45
414 #define ENABLE_SANDBOX -46
415 #define ENABLE_SECCOMP -47
416 #define _DEPRECATED_NO_HOST_LOOPBACK \
417 -10043 // deprecated in favor of disable-host-loopback
418 #define _DEPRECATED_CREATE_SANDBOX \
419 -10044 // deprecated in favor of enable-sandbox
420 const struct option longopts[] = {
421 { "configure", no_argument, NULL, 'c' },
422 { "exit-fd", required_argument, NULL, 'e' },
423 { "ready-fd", required_argument, NULL, 'r' },
424 { "mtu", required_argument, NULL, 'm' },
425 { "cidr", required_argument, NULL, CIDR },
426 { "disable-host-loopback", no_argument, NULL, DISABLE_HOST_LOOPBACK },
427 { "no-host-loopback", no_argument, NULL, _DEPRECATED_NO_HOST_LOOPBACK },
428 { "netns-type", required_argument, NULL, NETNS_TYPE },
429 { "userns-path", required_argument, NULL, USERNS_PATH },
430 { "api-socket", required_argument, NULL, 'a' },
431 { "enable-ipv6", no_argument, NULL, '6' },
432 { "enable-sandbox", no_argument, NULL, ENABLE_SANDBOX },
433 { "create-sandbox", no_argument, NULL, _DEPRECATED_CREATE_SANDBOX },
434 { "enable-seccomp", no_argument, NULL, ENABLE_SECCOMP },
435 { "help", no_argument, NULL, 'h' },
436 { "version", no_argument, NULL, 'v' },
437 { 0, 0, 0, 0 },
438 };
439 options_init(options);
440 /* NOTE: clang-tidy hates strdup(optarg) in the while loop (#112) */
441 while ((opt = getopt_long(argc, argv, "ce:r:m:a:6hv", longopts, NULL)) !=
442 -1) {
443 switch (opt) {
444 case 'c':
445 options->do_config_network = true;
446 break;
447 case 'e':
448 errno = 0;
449 options->exit_fd = strtol(optarg, &strtol_e, 10);
450 if (errno || *strtol_e != '\0' || options->exit_fd < 0) {
451 fprintf(stderr, "exit-fd must be a non-negative integer\n");
452 goto error;
453 }
454 break;
455 case 'r':
456 errno = 0;
457 options->ready_fd = strtol(optarg, &strtol_e, 10);
458 if (errno || *strtol_e != '\0' || options->ready_fd < 0) {
459 fprintf(stderr, "ready-fd must be a non-negative integer\n");
460 goto error;
461 }
462 break;
463 case 'm':
464 errno = 0;
465 options->mtu = strtol(optarg, &strtol_e, 10);
466 if (errno || *strtol_e != '\0' || options->mtu <= 0 ||
467 options->mtu > 65521) {
468 fprintf(stderr, "MTU must be a positive integer (< 65522)\n");
469 goto error;
470 }
471 break;
472 case CIDR:
473 optarg_cidr = optarg;
474 break;
475 case _DEPRECATED_NO_HOST_LOOPBACK:
476 // There was no tagged release with support for --no-host-loopback.
477 // So no one will be affected by removal of --no-host-loopback.
478 printf("WARNING: --no-host-loopback is deprecated and will be "
479 "removed in future releases, please use "
480 "--disable-host-loopback instead.\n");
481 /* FALLTHROUGH */
482 case DISABLE_HOST_LOOPBACK:
483 options->disable_host_loopback = true;
484 break;
485 case _DEPRECATED_CREATE_SANDBOX:
486 // There was no tagged release with support for --create-sandbox.
487 // So no one will be affected by removal of --create-sandbox.
488 printf("WARNING: --create-sandbox is deprecated and will be "
489 "removed in future releases, please use "
490 "--enable-sandbox instead.\n");
491 /* FALLTHROUGH */
492 case ENABLE_SANDBOX:
493 printf("WARNING: Support for sandboxing is experimental\n");
494 options->enable_sandbox = true;
495 break;
496 case ENABLE_SECCOMP:
497 printf("WARNING: Support for seccomp is experimental\n");
498 options->enable_seccomp = true;
499 break;
500 case NETNS_TYPE:
501 optarg_netns_type = optarg;
502 break;
503 case USERNS_PATH:
504 optarg_userns_path = optarg;
505 if (access(optarg_userns_path, F_OK) == -1) {
506 fprintf(stderr, "userns path doesn't exist: %s\n",
507 optarg_userns_path);
508 goto error;
509 }
510 break;
511 case 'a':
512 optarg_api_socket = optarg;
513 break;
514 case '6':
515 options->enable_ipv6 = true;
516 printf("WARNING: Support for IPv6 is experimental\n");
517 break;
518 case 'h':
519 usage(argv[0]);
520 exit(EXIT_SUCCESS);
521 break;
522 case 'v':
523 version();
524 exit(EXIT_SUCCESS);
525 break;
526 default:
527 goto error;
528 break;
529 }
530 }
531 if (optarg_cidr != NULL) {
532 options->cidr = strdup(optarg_cidr);
533 }
534 if (optarg_netns_type != NULL) {
535 options->netns_type = strdup(optarg_netns_type);
536 }
537 if (optarg_userns_path != NULL) {
538 options->userns_path = strdup(optarg_userns_path);
539 }
540 if (optarg_api_socket != NULL) {
541 options->api_socket = strdup(optarg_api_socket);
542 }
409543 #undef CIDR
410544 #undef DISABLE_HOST_LOOPBACK
545 #undef NETNS_TYPE
546 #undef USERNS_PATH
411547 #undef _DEPRECATED_NO_HOST_LOOPBACK
412 if (options->ready_fd >= 0 && !options->do_config_network) {
413 fprintf(stderr, "the option -r FD requires -c\n");
414 exit(EXIT_FAILURE);
415 }
416 if (options->mtu == 0 || options->mtu > 65521) {
417 fprintf(stderr, "MTU must be a positive integer (< 65522)\n");
418 exit(EXIT_FAILURE);
419 }
420 if (argc - optind < 2) {
421 usage(argv[0]);
422 exit(EXIT_FAILURE);
423 }
424 errno = 0;
425 options->target_pid = strtol(argv[optind], NULL, 10);
426 if (errno != 0) {
427 perror("strtol");
428 usage(argv[0]);
429 exit(EXIT_FAILURE);
430 }
431 options->tapname = strdup(argv[optind + 1]);
432 }
433
434 static int from_regmatch(char *buf, size_t buf_len, regmatch_t match, const char *orig)
435 {
436 size_t len = match.rm_eo - match.rm_so;
437 if (len > buf_len - 1) {
438 return -1;
439 }
440 memset(buf, 0, buf_len);
441 strncpy(buf, &orig[match.rm_so], len);
442 return 0;
443 }
444
445 static int parse_cidr(struct in_addr *network, struct in_addr *netmask, const char *cidr)
446 {
447 int rc = 0;
448 regex_t r;
449 regmatch_t matches[4];
450 size_t nmatch = sizeof(matches) / sizeof(matches[0]);
451 const char *cidr_regex = "^(([0-9]{1,3}\\.){3}[0-9]{1,3})/([0-9]{1,2})$";
452 char snetwork[16], sprefix[16];
453 int prefix;
454 rc = regcomp(&r, cidr_regex, REG_EXTENDED);
455 if (rc != 0) {
456 fprintf(stderr, "internal regex error\n");
457 rc = -1;
458 goto finish;
459 }
460 rc = regexec(&r, cidr, nmatch, matches, 0);
461 if (rc != 0) {
462 fprintf(stderr, "invalid CIDR: %s\n", cidr);
463 rc = -1;
464 goto finish;
465 }
466 rc = from_regmatch(snetwork, sizeof(snetwork), matches[1], cidr);
467 if (rc < 0) {
468 fprintf(stderr, "invalid CIDR: %s\n", cidr);
469 goto finish;
470 }
471 rc = from_regmatch(sprefix, sizeof(sprefix), matches[3], cidr);
472 if (rc < 0) {
473 fprintf(stderr, "invalid CIDR: %s\n", cidr);
474 goto finish;
475 }
476 if (inet_pton(AF_INET, snetwork, network) != 1) {
477 fprintf(stderr, "invalid network address: %s\n", snetwork);
478 rc = -1;
479 goto finish;
480 }
481 errno = 0;
482 prefix = strtoul(sprefix, NULL, 10);
483 if (errno) {
484 fprintf(stderr, "invalid prefix length: %s\n", sprefix);
485 rc = -1;
486 goto finish;
487 }
488 if (prefix < NETWORK_PREFIX_MIN || prefix > NETWORK_PREFIX_MAX) {
489 fprintf(stderr, "prefix length needs to be %d-%d\n", NETWORK_PREFIX_MIN, NETWORK_PREFIX_MAX);
490 rc = -1;
491 goto finish;
492 }
493 netmask->s_addr = htonl(~((1 << (32 - prefix)) - 1));
494 if ((network->s_addr & netmask->s_addr) != network->s_addr) {
495 fprintf(stderr, "CIDR needs to be a network address like 10.0.2.0/24, not like 10.0.2.100/24\n");
496 rc = -1;
497 goto finish;
498 }
499 finish:
500 regfree(&r);
501 return rc;
502 }
503
504 static int slirp4netns_config_from_cidr(struct slirp4netns_config *cfg, const char *cidr)
505 {
506 int rc;
507 rc = parse_cidr(&cfg->vnetwork, &cfg->vnetmask, cidr);
508 if (rc < 0) {
509 goto finish;
510 }
511 cfg->vhost.s_addr = htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_VHOST_OFFSET);
512 cfg->vdhcp_start.s_addr = htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_VDHCPSTART_OFFSET);
513 cfg->vnameserver.s_addr = htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_VNAMESERVER_OFFSET);
514 cfg->recommended_vguest.s_addr = htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_RECOMMENDED_VGUEST_OFFSET);
515 finish:
516 return rc;
517 }
518
519 static int slirp4netns_config_from_options(struct slirp4netns_config *cfg, struct options *opt)
520 {
521 int rc = 0;
522 cfg->mtu = opt->mtu;
523 rc = slirp4netns_config_from_cidr(cfg, opt->cidr == NULL ? DEFAULT_CIDR : opt->cidr);
524 if (rc < 0) {
525 goto finish;
526 }
527 cfg->enable_ipv6 = cfg->enable_ipv6;
528 cfg->disable_host_loopback = opt->disable_host_loopback;
529 finish:
530 return rc;
548 #undef ENABLE_SANDBOX
549 #undef ENABLE_SECCOMP
550 if (argc - optind < 2) {
551 goto error;
552 }
553 if (!options->netns_type ||
554 strcmp(options->netns_type, DEFAULT_NETNS_TYPE) == 0) {
555 errno = 0;
556 options->target_pid = strtol(argv[optind], &strtol_e, 10);
557 if (errno || *strtol_e != '\0' || options->target_pid <= 0) {
558 fprintf(stderr, "PID must be a positive integer\n");
559 goto error;
560 }
561 } else {
562 options->netns_path = strdup(argv[optind]);
563 if (access(options->netns_path, F_OK) == -1) {
564 perror("existing path expected when --netns-type=path");
565 goto error;
566 }
567 }
568 options->tapname = strdup(argv[optind + 1]);
569 return;
570 error:
571 usage(argv[0]);
572 options_destroy(options);
573 exit(EXIT_FAILURE);
574 }
575
576 static int from_regmatch(char *buf, size_t buf_len, regmatch_t match,
577 const char *orig)
578 {
579 size_t len = match.rm_eo - match.rm_so;
580 if (len > buf_len - 1) {
581 return -1;
582 }
583 memset(buf, 0, buf_len);
584 strncpy(buf, &orig[match.rm_so], len);
585 return 0;
586 }
587
588 static int parse_cidr(struct in_addr *network, struct in_addr *netmask,
589 const char *cidr)
590 {
591 int rc = 0;
592 regex_t r;
593 regmatch_t matches[4];
594 size_t nmatch = sizeof(matches) / sizeof(matches[0]);
595 const char *cidr_regex = "^(([0-9]{1,3}\\.){3}[0-9]{1,3})/([0-9]{1,2})$";
596 char snetwork[16], sprefix[16];
597 int prefix;
598 rc = regcomp(&r, cidr_regex, REG_EXTENDED);
599 if (rc != 0) {
600 fprintf(stderr, "internal regex error\n");
601 rc = -1;
602 goto finish;
603 }
604 rc = regexec(&r, cidr, nmatch, matches, 0);
605 if (rc != 0) {
606 fprintf(stderr, "invalid CIDR: %s\n", cidr);
607 rc = -1;
608 goto finish;
609 }
610 rc = from_regmatch(snetwork, sizeof(snetwork), matches[1], cidr);
611 if (rc < 0) {
612 fprintf(stderr, "invalid CIDR: %s\n", cidr);
613 goto finish;
614 }
615 rc = from_regmatch(sprefix, sizeof(sprefix), matches[3], cidr);
616 if (rc < 0) {
617 fprintf(stderr, "invalid CIDR: %s\n", cidr);
618 goto finish;
619 }
620 if (inet_pton(AF_INET, snetwork, network) != 1) {
621 fprintf(stderr, "invalid network address: %s\n", snetwork);
622 rc = -1;
623 goto finish;
624 }
625 errno = 0;
626 prefix = strtoul(sprefix, NULL, 10);
627 if (errno) {
628 fprintf(stderr, "invalid prefix length: %s\n", sprefix);
629 rc = -1;
630 goto finish;
631 }
632 if (prefix < NETWORK_PREFIX_MIN || prefix > NETWORK_PREFIX_MAX) {
633 fprintf(stderr, "prefix length needs to be %d-%d\n", NETWORK_PREFIX_MIN,
634 NETWORK_PREFIX_MAX);
635 rc = -1;
636 goto finish;
637 }
638 netmask->s_addr = htonl(~((1 << (32 - prefix)) - 1));
639 if ((network->s_addr & netmask->s_addr) != network->s_addr) {
640 fprintf(stderr, "CIDR needs to be a network address like 10.0.2.0/24, "
641 "not like 10.0.2.100/24\n");
642 rc = -1;
643 goto finish;
644 }
645 finish:
646 regfree(&r);
647 return rc;
648 }
649
650 static int slirp4netns_config_from_cidr(struct slirp4netns_config *cfg,
651 const char *cidr)
652 {
653 int rc;
654 rc = parse_cidr(&cfg->vnetwork, &cfg->vnetmask, cidr);
655 if (rc < 0) {
656 goto finish;
657 }
658 cfg->vhost.s_addr =
659 htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_VHOST_OFFSET);
660 cfg->vdhcp_start.s_addr =
661 htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_VDHCPSTART_OFFSET);
662 cfg->vnameserver.s_addr =
663 htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_VNAMESERVER_OFFSET);
664 cfg->recommended_vguest.s_addr =
665 htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_RECOMMENDED_VGUEST_OFFSET);
666 finish:
667 return rc;
668 }
669
670 static int slirp4netns_config_from_options(struct slirp4netns_config *cfg,
671 struct options *opt)
672 {
673 int rc = 0;
674 cfg->mtu = opt->mtu;
675 rc = slirp4netns_config_from_cidr(cfg, opt->cidr == NULL ? DEFAULT_CIDR :
676 opt->cidr);
677 if (rc < 0) {
678 goto finish;
679 }
680 cfg->enable_ipv6 = cfg->enable_ipv6;
681 cfg->disable_host_loopback = opt->disable_host_loopback;
682 cfg->enable_sandbox = opt->enable_sandbox;
683 cfg->enable_seccomp = opt->enable_seccomp;
684 finish:
685 return rc;
531686 }
532687
533688 int main(int argc, char *const argv[])
534689 {
535 int sv[2];
536 pid_t child_pid;
537 struct options options;
538 struct slirp4netns_config slirp4netns_config;
539 int exit_status = 0;
540
541 parse_args(argc, argv, &options);
542 if (slirp4netns_config_from_options(&slirp4netns_config, &options) < 0) {
543 exit_status = EXIT_FAILURE;
544 goto finish;
545 }
546 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sv) < 0) {
547 perror("socketpair");
548 exit_status = EXIT_FAILURE;
549 goto finish;
550 }
551 if ((child_pid = fork()) < 0) {
552 perror("fork");
553 exit_status = EXIT_FAILURE;
554 goto finish;
555 }
556 if (child_pid == 0) {
557 if (child(sv[1], options.target_pid, options.do_config_network, options.tapname, options.ready_fd,
558 &slirp4netns_config) < 0) {
559 exit_status = EXIT_FAILURE;
560 goto finish;
561 }
562 } else {
563 int child_wstatus, child_status;
564 waitpid(child_pid, &child_wstatus, 0);
565 if (!WIFEXITED(child_wstatus)) {
566 fprintf(stderr, "child failed\n");
567 exit_status = EXIT_FAILURE;
568 goto finish;
569 }
570 child_status = WEXITSTATUS(child_wstatus);
571 if (child_status != 0) {
572 fprintf(stderr, "child failed(%d)\n", child_status);
573 exit_status = child_status;
574 goto finish;
575 }
576 if (parent(sv[0], options.exit_fd, options.api_socket, &slirp4netns_config) < 0) {
577 fprintf(stderr, "parent failed\n");
578 exit_status = EXIT_FAILURE;
579 goto finish;
580 }
581 }
582 finish:
583 options_destroy(&options);
584 exit(exit_status);
585 return 0;
586 }
690 int sv[2];
691 pid_t child_pid;
692 struct options options;
693 struct slirp4netns_config slirp4netns_config;
694 int exit_status = 0;
695
696 parse_args(argc, argv, &options);
697 if (slirp4netns_config_from_options(&slirp4netns_config, &options) < 0) {
698 exit_status = EXIT_FAILURE;
699 goto finish;
700 }
701 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sv) < 0) {
702 perror("socketpair");
703 exit_status = EXIT_FAILURE;
704 goto finish;
705 }
706 if ((child_pid = fork()) < 0) {
707 perror("fork");
708 exit_status = EXIT_FAILURE;
709 goto finish;
710 }
711 if (child_pid == 0) {
712 if (child(sv[1], options.target_pid, options.do_config_network,
713 options.tapname, options.netns_path, options.userns_path,
714 &slirp4netns_config) < 0) {
715 exit_status = EXIT_FAILURE;
716 goto finish;
717 }
718 } else {
719 int ret, child_wstatus, child_status;
720 do
721 ret = waitpid(child_pid, &child_wstatus, 0);
722 while (ret < 0 && errno == EINTR);
723 if (ret < 0) {
724 perror("waitpid");
725 exit_status = EXIT_FAILURE;
726 goto finish;
727 }
728 if (!WIFEXITED(child_wstatus)) {
729 fprintf(stderr, "child failed(wstatus=%d, !WIFEXITED)\n",
730 child_wstatus);
731 exit_status = EXIT_FAILURE;
732 goto finish;
733 }
734 child_status = WEXITSTATUS(child_wstatus);
735 if (child_status != 0) {
736 fprintf(stderr, "child failed(%d)\n", child_status);
737 exit_status = child_status;
738 goto finish;
739 }
740 if (parent(sv[0], options.ready_fd, options.exit_fd, options.api_socket,
741 &slirp4netns_config, options.target_pid) < 0) {
742 fprintf(stderr, "parent failed\n");
743 exit_status = EXIT_FAILURE;
744 goto finish;
745 }
746 }
747 finish:
748 options_destroy(&options);
749 exit(exit_status);
750 return 0;
751 }
0 /* SPDX-License-Identifier: GPL-2.0-or-later */
1 #define _GNU_SOURCE
2 #include <errno.h>
3 #include <sched.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <unistd.h>
8 #include <sys/prctl.h>
9 #include <sys/mount.h>
10 #include <sys/syscall.h>
11 #include <sys/capability.h>
12 #include <sys/stat.h>
13
14 static int add_mount(const char *from, const char *to)
15 {
16 int ret;
17
18 ret = mount(from, to, "",
19 MS_BIND | MS_REC | MS_SLAVE | MS_NOSUID | MS_NODEV | MS_NOEXEC,
20 NULL);
21 if (ret < 0) {
22 fprintf(stderr, "cannot bind mount %s to %s\n", from, to);
23 return ret;
24 }
25 ret = mount(from, to, "",
26 MS_REMOUNT | MS_BIND | MS_RDONLY | MS_NOSUID | MS_NODEV |
27 MS_NOEXEC,
28 NULL);
29 if (ret < 0) {
30 fprintf(stderr, "cannot remount ro %s\n", to);
31 return ret;
32 }
33 return 0;
34 }
35
36 /* lock down the process doing the following:
37 - create a new mount namespace
38 - bind mount /etc and /run from the host
39 - pivot_root in the new tmpfs.
40 - drop all capabilities.
41 */
42 int create_sandbox()
43 {
44 int ret, i;
45 struct __user_cap_header_struct hdr = { _LINUX_CAPABILITY_VERSION_3, 0 };
46 struct __user_cap_data_struct data[2] = { { 0 } };
47
48 ret = unshare(CLONE_NEWNS);
49 if (ret < 0) {
50 fprintf(stderr, "cannot unshare new mount namespace\n");
51 return ret;
52 }
53 ret = mount("", "/", "", MS_PRIVATE, NULL);
54 if (ret < 0) {
55 fprintf(stderr, "cannot remount / private\n");
56 return ret;
57 }
58
59 ret = mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC,
60 "size=1k");
61 if (ret < 0) {
62 fprintf(stderr, "cannot mount tmpfs on /tmp\n");
63 return ret;
64 }
65
66 ret = mkdir("/tmp/etc", 0755);
67 if (ret < 0) {
68 fprintf(stderr, "cannot mkdir /etc\n");
69 return ret;
70 }
71
72 ret = mkdir("/tmp/old", 0755);
73 if (ret < 0) {
74 fprintf(stderr, "cannot mkdir /old\n");
75 return ret;
76 }
77
78 ret = mkdir("/tmp/run", 0755);
79 if (ret < 0) {
80 fprintf(stderr, "cannot mkdir /run\n");
81 return ret;
82 }
83
84 ret = add_mount("/etc", "/tmp/etc");
85 if (ret < 0) {
86 return ret;
87 }
88
89 ret = add_mount("/run", "/tmp/run");
90 if (ret < 0) {
91 return ret;
92 }
93
94 ret = chdir("/tmp");
95 if (ret < 0) {
96 fprintf(stderr, "cannot chdir to /tmp\n");
97 return ret;
98 }
99
100 ret = syscall(__NR_pivot_root, ".", "old");
101 if (ret < 0) {
102 fprintf(stderr, "cannot pivot_root to /tmp\n");
103 return ret;
104 }
105
106 ret = chdir("/");
107 if (ret < 0) {
108 fprintf(stderr, "cannot chdir to /\n");
109 return ret;
110 }
111
112 ret = umount2("/old", MNT_DETACH);
113 if (ret < 0) {
114 fprintf(stderr, "cannot umount /old\n");
115 return ret;
116 }
117
118 ret = rmdir("/old");
119 if (ret < 0) {
120 fprintf(stderr, "cannot rmdir /old\n");
121 return ret;
122 }
123
124 ret = mount("tmpfs", "/", "tmpfs", MS_REMOUNT | MS_RDONLY, "size=0k");
125 if (ret < 0) {
126 fprintf(stderr, "cannot mount tmpfs on /tmp\n");
127 return ret;
128 }
129
130 ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
131 if (ret < 0) {
132 fprintf(stderr, "prctl(PR_SET_NO_NEW_PRIVS)\n");
133 return ret;
134 }
135
136 ret = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
137 if (ret < 0) {
138 fprintf(stderr, "prctl(PR_CAP_AMBIENT_CLEAR_ALL)\n");
139 return ret;
140 }
141 for (i = 0;; i++) {
142 if (i == CAP_NET_BIND_SERVICE)
143 continue;
144 ret = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
145 if (ret < 0) {
146 if (errno == EINVAL)
147 break;
148 fprintf(stderr, "prctl(PR_CAPBSET_DROP)\n");
149 return ret;
150 }
151 }
152
153 memset(&data, 0, sizeof(data));
154 data[0].effective |= 1 << CAP_NET_BIND_SERVICE;
155 data[0].permitted |= 1 << CAP_NET_BIND_SERVICE;
156 data[0].inheritable |= 1 << CAP_NET_BIND_SERVICE;
157 ret = capset(&hdr, data);
158 if (ret < 0) {
159 fprintf(stderr, "capset(0)\n");
160 return ret;
161 }
162
163 return 0;
164 }
0 /* SPDX-License-Identifier: GPL-2.0-or-later */
1 #ifndef SLIRP4NETNS_SANDBOX_H
2 # define SLIRP4NETNS_SANDBOX_H
3 int create_sandbox();
4 #endif
0 /* SPDX-License-Identifier: GPL-2.0-or-later */
1 #define _GNU_SOURCE
2 #include <stdio.h>
3 #include <seccomp.h>
4
5 int enable_seccomp()
6 {
7 int rc = -1;
8 /* Allow everything by default and block dangerous syscalls explicitly,
9 * as it is hard to find the correct set of required syscalls */
10 scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW);
11 if (ctx == NULL)
12 goto ret;
13 printf("seccomp: The following syscalls will be blocked by seccomp:");
14 #ifdef SCMP_ACT_KILL_PROCESS
15 #define BLOCK_ACTION SCMP_ACT_KILL_PROCESS
16 #else
17 #define BLOCK_ACTION SCMP_ACT_KILL
18 #endif
19 #define BLOCK(x) \
20 { \
21 rc = seccomp_rule_add(ctx, BLOCK_ACTION, SCMP_SYS(x), 0); \
22 if (rc < 0) \
23 goto ret; \
24 printf(" %s", #x); \
25 }
26 BLOCK(execve);
27 #ifdef __NR_execveat
28 BLOCK(execveat);
29 #else
30 fprintf(stderr,
31 "seccomp: can't block execevat because __NR_execveat was not "
32 "defined in the build environment\n");
33 #endif
34 /* ideally we should also block open() and openat() but required for
35 * resolv.conf */
36 BLOCK(open_by_handle_at);
37 BLOCK(ptrace);
38 BLOCK(prctl);
39 BLOCK(process_vm_readv);
40 BLOCK(process_vm_writev);
41 BLOCK(mount);
42 BLOCK(name_to_handle_at);
43 BLOCK(setns);
44 BLOCK(umount);
45 BLOCK(umount2);
46 BLOCK(unshare);
47 #undef BLOCK
48 #undef BLOCK_ACTION
49 printf(".\n");
50 rc = seccomp_load(ctx);
51 ret:
52 seccomp_release(ctx);
53 return rc;
54 }
0 /* SPDX-License-Identifier: GPL-2.0-or-later */
1 #ifndef SLIRP4NETNS_SECCOMPFILTER_H
2 # define SLIRP4NETNS_SECCOMPFILTER_H
3 int enable_seccomp();
4 #endif
00 .nh
1 .TH SLIRP4NETNS 1 "July 2018" "Rootless Containers" "User Commands"
1 .TH SLIRP4NETNS 1 "August 2019" "Rootless Containers" "User Commands"
22
33 .SH NAME
44 .PP
77
88 .SH SYNOPSIS
99 .PP
10 slirp4netns [OPTION]... PID TAPNAME
10 slirp4netns [OPTION]... PID|PATH TAPNAME
1111
1212
1313 .SH DESCRIPTION
4242 .SH OPTIONS
4343 .PP
4444 \fB\-c\fP, \fB\-\-configure\fP
45 bring up the interface. IP will be set to 10.0.2.100 (network address + 100) by default. IPv6 will be set to a random address.
45 bring up the TAP interface. IP will be set to 10.0.2.100 (network address + 100) by default. IPv6 will be set to a random address.
46 Starting with v0.4.0, the loopback interface (\fBlo\fP) is brought up as well.
4647
4748 .PP
4849 \fB\-e\fP, \fB\-\-exit\-fd=FD\fP
4950 specify the FD for terminating slirp4netns.
51 When the FD is specified, slirp4netns exits when a \fBpoll(2)\fP event happens on the FD.
5052
5153 .PP
5254 \fB\-r\fP, \fB\-\-ready\-fd=FD\fP
53 specify the FD to write to when the network is configured.
54
55 .PP
56 \fB\-m\fP, \fB\-\-mtu=MTU\fP
55 specify the FD to write to when the initialization steps are finished.
56 When the FD is specified, slirp4netns writes \fB"1"\fP to the FD and close the FD.
57 Prior to v0.4.0, the FD was written after the network configuration (\fB\-c\fP)
58 but before the API socket configuration (\fB\-a\fP).
59
60 .PP
61 \fB\-m\fP, \fB\-\-mtu=MTU\fP (since v0.2.0)
5762 specify MTU (max=65521).
63
64 .PP
65 \fB\-6\fP, \fB\-\-enable\-ipv6\fP (since v0.2.0, EXPERIMENTAL)
66 enable IPv6
67
68 .PP
69 \fB\-a\fP, \fB\-\-api\-socket\fP (since v0.3.0)
70 API socket path
5871
5972 .PP
6073 \fB\-\-cidr\fP (since v0.3.0)
6578 prohibit connecting to 127.0.0.1:* on the host namespace
6679
6780 .PP
68 \fB\-a\fP, \fB\-\-api\-socket\fP (since v0.3.0)
69 API socket path
70
71 .PP
72 \fB\-6\fP, \fB\-\-enable\-ipv6\fP
73 enable IPv6 (experimental).
74
75 .PP
76 \fB\-h\fP, \fB\-\-help\fP
81 \fB\-\-netns\-type=TYPE\fP (since v0.4.0)
82 specify network namespace type ([path|pid], default=pid)
83
84 .PP
85 \fB\-\-userns\-path=PATH\fP (since v0.4.0)
86 specify user namespace path
87
88 .PP
89 \fB\-\-enable\-sandbox\fP (since v0.4.0, EXPERIMENTAL)
90 enter the user namespace and create a new mount namespace where only /etc and
91 /run are mounted from the host.
92
93 .PP
94 When running as the root, the process does not enter the user namespace but all
95 the capabilities except \fB\fCCAP\_NET\_BIND\_SERVICE\fR are dropped.
96
97 .PP
98 \fB\-\-enable\-seccomp\fP (since v0.4.0, EXPERIMENTAL)
99 enable \fBseccomp(2)\fP to limit syscalls.
100 Typically used in conjunction with \fB\-\-enable\-sandbox\fP\&.
101
102 .PP
103 \fB\-h\fP, \fB\-\-help\fP (since v0.2.0)
77104 show help and exit
78105
79106 .PP
80 \fB\-v\fP, \fB\-\-version\fP
107 \fB\-v\fP, \fB\-\-version\fP (since v0.2.0)
81108 show version and exit
82109
83110
267294
268295 .RS
269296 .IP \(bu 2
270 Client needs to \fBshutdown\fP the socket with \fBSHUT\_WR\fP after sending every request.
297 Client needs to \fBshutdown(2)\fP the socket with \fBSHUT\_WR\fP after sending every request.
271298 i.e. No support for keep\-alive and timeout.
272299 .IP \(bu 2
273300 slirp4netns "stops the world" during processing API requests.
274301 .IP \(bu 2
275 A request must be less than 4095 bytes.
302 A request must be less than 4096 bytes.
276303 .IP \(bu 2
277304 JSON responses may contain \fBerror\fP instead of \fBreturn\fP\&.
278305
279306 .RE
280307
281308
309 .SH DEFINED NAMESPACE PATHS
310 .PP
311 A user can define a network namespace path as opposed to the default process ID:
312
313 .PP
314 .RS
315
316 .nf
317 $ slirp4netns \-\-netns\-type=path ... /path/to/netns tap0
318
319 .fi
320 .RE
321
322 .PP
323 Currently, the \fBnetns\-type=TYPE\fP argument supports \fBpath\fP or \fBpid\fP args with the default being \fBpid\fP\&.
324
325 .PP
326 Additionally, a \fB\-\-userns\-path=PATH\fP argument can be included to override any user namespace path defaults
327
328 .PP
329 .RS
330
331 .nf
332 $ slirp4netns \-\-netns\-type=path \-\-userns\-path=/path/to/userns /path/to/netns tap0
333
334 .fi
335 .RE
336
337
338 .SH BUGS
339 .PP
340 Kernel 4.20 bumped up the default value of \fB/proc/sys/net/ipv4/tcp\_rmem\fP from 87380 to 131072.
341 This is known to slow down slirp4netns port forwarding: \fBhttps://github.com/rootless\-containers/slirp4netns/issues/128\fP\&.
342
343 .PP
344 As a workaround, you can adjust the value of \fB/proc/sys/net/ipv4/tcp\_rmem\fP inside the namespace.
345 No real root privilege is needed to modify the file since kernel 4.15.
346
347 .PP
348 .RS
349
350 .nf
351 unshared$ c=$(cat /proc/sys/net/ipv4/tcp\_rmem); echo $c | sed \-e s/131072/87380/g > /proc/sys/net/ipv4/tcp\_rmem
352
353 .fi
354 .RE
355
356
282357 .SH SEE ALSO
283358 .PP
284359 \fBnetwork\_namespaces\fP(7), \fBuser\_namespaces\fP(7), \fBveth\fP(4)
0 SLIRP4NETNS 1 "July 2018" "Rootless Containers" "User Commands"
0 SLIRP4NETNS 1 "August 2019" "Rootless Containers" "User Commands"
11 ==================================================
22
33 # NAME
66
77 # SYNOPSIS
88
9 slirp4netns [OPTION]... PID TAPNAME
9 slirp4netns [OPTION]... PID|PATH TAPNAME
1010
1111 # DESCRIPTION
1212
2727 # OPTIONS
2828
2929 **-c**, **--configure**
30 bring up the interface. IP will be set to 10.0.2.100 (network address + 100) by default. IPv6 will be set to a random address.
30 bring up the TAP interface. IP will be set to 10.0.2.100 (network address + 100) by default. IPv6 will be set to a random address.
31 Starting with v0.4.0, the loopback interface (**lo**) is brought up as well.
3132
3233 **-e**, **--exit-fd=FD**
3334 specify the FD for terminating slirp4netns.
35 When the FD is specified, slirp4netns exits when a **poll(2)** event happens on the FD.
3436
3537 **-r**, **--ready-fd=FD**
36 specify the FD to write to when the network is configured.
37
38 **-m**, **--mtu=MTU**
38 specify the FD to write to when the initialization steps are finished.
39 When the FD is specified, slirp4netns writes **"1"** to the FD and close the FD.
40 Prior to v0.4.0, the FD was written after the network configuration (**-c**)
41 but before the API socket configuration (**-a**).
42
43 **-m**, **--mtu=MTU** (since v0.2.0)
3944 specify MTU (max=65521).
45
46 **-6**, **--enable-ipv6** (since v0.2.0, EXPERIMENTAL)
47 enable IPv6
48
49 **-a**, **--api-socket** (since v0.3.0)
50 API socket path
4051
4152 **--cidr** (since v0.3.0)
4253 specify CIDR, e.g. 10.0.2.0/24
4455 **--disable-host-loopback** (since v0.3.0)
4556 prohibit connecting to 127.0.0.1:\* on the host namespace
4657
47 **-a**, **--api-socket** (since v0.3.0)
48 API socket path
49
50 **-6**, **--enable-ipv6**
51 enable IPv6 (experimental).
52
53 **-h**, **--help**
58 **--netns-type=TYPE** (since v0.4.0)
59 specify network namespace type ([path|pid], default=pid)
60
61 **--userns-path=PATH** (since v0.4.0)
62 specify user namespace path
63
64 **--enable-sandbox** (since v0.4.0, EXPERIMENTAL)
65 enter the user namespace and create a new mount namespace where only /etc and
66 /run are mounted from the host.
67
68 When running as the root, the process does not enter the user namespace but all
69 the capabilities except `CAP_NET_BIND_SERVICE` are dropped.
70
71 **--enable-seccomp** (since v0.4.0, EXPERIMENTAL)
72 enable **seccomp(2)** to limit syscalls.
73 Typically used in conjunction with **--enable-sandbox**.
74
75 **-h**, **--help** (since v0.2.0)
5476 show help and exit
5577
56 **-v**, **--version**
78 **-v**, **--version** (since v0.2.0)
5779 show version and exit
5880
5981 # EXAMPLE
171193
172194 Remarks:
173195
174 * Client needs to **shutdown** the socket with **SHUT_WR** after sending every request.
196 * Client needs to **shutdown(2)** the socket with **SHUT_WR** after sending every request.
175197 i.e. No support for keep-alive and timeout.
176198 * slirp4netns "stops the world" during processing API requests.
177 * A request must be less than 4095 bytes.
199 * A request must be less than 4096 bytes.
178200 * JSON responses may contain **error** instead of **return**.
179201
202 # DEFINED NAMESPACE PATHS
203 A user can define a network namespace path as opposed to the default process ID:
204
205 ```console
206 $ slirp4netns --netns-type=path ... /path/to/netns tap0
207 ```
208 Currently, the **netns-type=TYPE** argument supports **path** or **pid** args with the default being **pid**.
209
210 Additionally, a **--userns-path=PATH** argument can be included to override any user namespace path defaults
211 ```console
212 $ slirp4netns --netns-type=path --userns-path=/path/to/userns /path/to/netns tap0
213 ```
214
215 # BUGS
216
217 Kernel 4.20 bumped up the default value of **/proc/sys/net/ipv4/tcp_rmem** from 87380 to 131072.
218 This is known to slow down slirp4netns port forwarding: **https://github.com/rootless-containers/slirp4netns/issues/128**.
219
220 As a workaround, you can adjust the value of **/proc/sys/net/ipv4/tcp_rmem** inside the namespace.
221 No real root privilege is needed to modify the file since kernel 4.15.
222
223 ```console
224 unshared$ c=$(cat /proc/sys/net/ipv4/tcp_rmem); echo $c | sed -e s/131072/87380/g > /proc/sys/net/ipv4/tcp_rmem
225 ```
226
180227 # SEE ALSO
181228
182229 **network_namespaces**(7), **user_namespaces**(7), **veth**(4)
0 /* SPDX-License-Identifier: GPL-2.0-or-later */
01 #define _GNU_SOURCE
12 #include <errno.h>
23 #include <signal.h>
89
910 #include "vendor/libslirp/src/libslirp.h"
1011 #include "api.h"
12 #include "sandbox.h"
13 #include "seccompfilter.h"
1114 #include "slirp4netns.h"
1215
1316 /* opaque for SlirpCb */
1417 struct libslirp_data {
15 int tapfd;
16 GSList *timers;
18 int tapfd;
19 GSList *timers;
1720 };
1821
1922 /* implements SlirpCb.send_packet */
20 static ssize_t libslirp_send_packet(const void *pkt, size_t pkt_len, void *opaque)
21 {
22 struct libslirp_data *data = (struct libslirp_data *)opaque;
23 return write(data->tapfd, pkt, pkt_len);
23 static ssize_t libslirp_send_packet(const void *pkt, size_t pkt_len,
24 void *opaque)
25 {
26 struct libslirp_data *data = (struct libslirp_data *)opaque;
27 return write(data->tapfd, pkt, pkt_len);
2428 }
2529
2630 /* implements SlirpCb.guest_error */
2731 static void libslirp_guest_error(const char *msg, void *opaque)
2832 {
29 fprintf(stderr, "libslirp: %s\n", msg);
33 fprintf(stderr, "libslirp: %s\n", msg);
3034 }
3135
3236 /* implements SlirpCb.clock_get_ns */
3337 static int64_t libslirp_clock_get_ns(void *opaque)
3438 {
35 struct timespec ts;
36 clock_gettime(CLOCK_MONOTONIC, &ts);
37 return ts.tv_sec * 1000000000LL + ts.tv_nsec;
39 struct timespec ts;
40 clock_gettime(CLOCK_MONOTONIC, &ts);
41 return ts.tv_sec * 1000000000LL + ts.tv_nsec;
3842 }
3943
4044 /* timer for SlirpCb */
4145 struct timer {
42 SlirpTimerCb cb;
43 void *cb_opaque;
44 int64_t expire_timer_msec;
46 SlirpTimerCb cb;
47 void *cb_opaque;
48 int64_t expire_timer_msec;
4549 };
4650
4751 /* implements SlirpCb.timer_new */
4852 static void *libslirp_timer_new(SlirpTimerCb cb, void *cb_opaque, void *opaque)
4953 {
50 struct libslirp_data *data = (struct libslirp_data *)opaque;
51 struct timer *t = g_malloc0(sizeof(*t));
52 t->cb = cb;
53 t->cb_opaque = cb_opaque;
54 t->expire_timer_msec = -1;
55 data->timers = g_slist_append(data->timers, t);
56 return t;
54 struct libslirp_data *data = (struct libslirp_data *)opaque;
55 struct timer *t = g_malloc0(sizeof(*t));
56 t->cb = cb;
57 t->cb_opaque = cb_opaque;
58 t->expire_timer_msec = -1;
59 data->timers = g_slist_append(data->timers, t);
60 return t;
5761 }
5862
5963 /* implements SlirpCb.timer_free */
6064 static void libslirp_timer_free(void *timer, void *opaque)
6165 {
62 struct libslirp_data *data = (struct libslirp_data *)opaque;
63 data->timers = g_slist_remove(data->timers, timer);
64 g_free(timer);
66 struct libslirp_data *data = (struct libslirp_data *)opaque;
67 data->timers = g_slist_remove(data->timers, timer);
68 g_free(timer);
6569 }
6670
6771 /* implements SlirpCb.timer_mod */
68 static void libslirp_timer_mod(void *timer, int64_t expire_timer_msec, void *opaque)
69 {
70 struct timer *t = (struct timer *)timer;
71 t->expire_timer_msec = expire_timer_msec;
72 static void libslirp_timer_mod(void *timer, int64_t expire_timer_msec,
73 void *opaque)
74 {
75 struct timer *t = (struct timer *)timer;
76 t->expire_timer_msec = expire_timer_msec;
7277 }
7378
7479 /* implements SlirpCb.register_poll_fd */
7580 static void libslirp_register_poll_fd(int fd, void *opaque)
7681 {
77 /*
78 * NOP
79 *
80 * This is NOP on QEMU upstream on Linux as well, see:
81 * * qemu/net/slirp.c: net_slirp_register_poll_fd (calls qemu_fd_register)
82 * * qemu/stubs/fd-register.c: qemu_fd_register (NOP on Linux)
83 *
84 * See also:
85 * * qemu/util/main-loop.c: qemu_fd_register (Win32 only)
86 */
82 /*
83 * NOP
84 *
85 * This is NOP on QEMU@4c76137484878f42a2ce1ae1b888b6a7f66b4053 on Linux as
86 * well, see:
87 * * qemu/net/slirp.c: net_slirp_register_poll_fd (calls
88 * qemu_fd_register)
89 * * qemu/stubs/fd-register.c: qemu_fd_register (NOP on Linux)
90 *
91 * See also:
92 * * qemu/util/main-loop.c: qemu_fd_register (Win32 only)
93 */
8794 }
8895
8996 /* implements SlirpCb.unregister_poll_fd */
9097 static void libslirp_unregister_poll_fd(int fd, void *opaque)
9198 {
92 /*
93 * NOP
94 *
95 * This is NOP on QEMU upstream as well, see:
96 * * qemu/net/slirp.c: net_slirp_unregister_poll_fd (NOP)
97 */
99 /*
100 * NOP
101 *
102 * This is NOP on QEMU@4c76137484878f42a2ce1ae1b888b6a7f66b4053 as well,
103 * see:
104 * * qemu/net/slirp.c: net_slirp_unregister_poll_fd (NOP)
105 */
98106 }
99107
100108 /* implements SlirpCb.notify */
101109 static void libslirp_notify(void *opaque)
102110 {
103 /*
104 * NOP
105 *
106 * This can be NOP on QEMU upstream as well, see:
107 * * qemu/net/slirp.c: net_slirp_notify (calls qemu_notify_event)
108 * * qemu/stubs/notify-event.c: qemu_notify_event (NOP)
109 *
110 * See also:
111 * * qemu/util/main-loop.c: qemu_notify_event (NOP if !qemu_aio_context)
112 */
111 /*
112 * NOP
113 *
114 * This can be NOP on QEMU@4c76137484878f42a2ce1ae1b888b6a7f66b4053 as well,
115 * see:
116 * * qemu/net/slirp.c: net_slirp_notify (calls qemu_notify_event)
117 * * qemu/stubs/notify-event.c: qemu_notify_event (NOP)
118 *
119 * See also:
120 * * qemu/util/main-loop.c: qemu_notify_event (NOP if
121 * !qemu_aio_context)
122 */
113123 }
114124
115125 static int libslirp_poll_to_gio(int events)
116126 {
117 int ret = 0;
118 if (events & SLIRP_POLL_IN) {
119 ret |= G_IO_IN;
120 }
121 if (events & SLIRP_POLL_OUT) {
122 ret |= G_IO_OUT;
123 }
124 if (events & SLIRP_POLL_PRI) {
125 ret |= G_IO_PRI;
126 }
127 if (events & SLIRP_POLL_ERR) {
128 ret |= G_IO_ERR;
129 }
130 if (events & SLIRP_POLL_HUP) {
131 ret |= G_IO_HUP;
132 }
133 return ret;
127 int ret = 0;
128 if (events & SLIRP_POLL_IN) {
129 ret |= G_IO_IN;
130 }
131 if (events & SLIRP_POLL_OUT) {
132 ret |= G_IO_OUT;
133 }
134 if (events & SLIRP_POLL_PRI) {
135 ret |= G_IO_PRI;
136 }
137 if (events & SLIRP_POLL_ERR) {
138 ret |= G_IO_ERR;
139 }
140 if (events & SLIRP_POLL_HUP) {
141 ret |= G_IO_HUP;
142 }
143 return ret;
134144 }
135145
136146 /*
137147 * implements SlirpAddPollCb used in slirp_pollfds_fill.
138 * originally from qemu/net/slirp.c:net_slirp_add_poll
148 * originally from qemu/net/slirp.c:net_slirp_add_poll
149 * (4c76137484878f42a2ce1ae1b888b6a7f66b4053)
139150 */
140151 static int libslirp_add_poll(int fd, int events, void *opaque)
141152 {
142 GArray *pollfds = opaque;
143 GPollFD pfd = {
144 .fd = fd,
145 .events = libslirp_poll_to_gio(events),
146 };
147 int idx = pollfds->len;
148 g_array_append_val(pollfds, pfd);
149 return idx;
153 GArray *pollfds = opaque;
154 GPollFD pfd = {
155 .fd = fd,
156 .events = libslirp_poll_to_gio(events),
157 };
158 int idx = pollfds->len;
159 g_array_append_val(pollfds, pfd);
160 return idx;
150161 }
151162
152163 static int libslirp_gio_to_poll(int events)
153164 {
154 int ret = 0;
155 if (events & G_IO_IN) {
156 ret |= SLIRP_POLL_IN;
157 }
158 if (events & G_IO_OUT) {
159 ret |= SLIRP_POLL_OUT;
160 }
161 if (events & G_IO_PRI) {
162 ret |= SLIRP_POLL_PRI;
163 }
164 if (events & G_IO_ERR) {
165 ret |= SLIRP_POLL_ERR;
166 }
167 if (events & G_IO_HUP) {
168 ret |= SLIRP_POLL_HUP;
169 }
170 return ret;
165 int ret = 0;
166 if (events & G_IO_IN) {
167 ret |= SLIRP_POLL_IN;
168 }
169 if (events & G_IO_OUT) {
170 ret |= SLIRP_POLL_OUT;
171 }
172 if (events & G_IO_PRI) {
173 ret |= SLIRP_POLL_PRI;
174 }
175 if (events & G_IO_ERR) {
176 ret |= SLIRP_POLL_ERR;
177 }
178 if (events & G_IO_HUP) {
179 ret |= SLIRP_POLL_HUP;
180 }
181 return ret;
171182 }
172183
173184 /*
174185 * implements SlirpGetREventsCB used in slirp_pollfds_poll
175186 * originally from qemu/net/slirp.c:net_slirp_get_revents
187 * (4c76137484878f42a2ce1ae1b888b6a7f66b4053)
176188 */
177189 static int libslirp_get_revents(int idx, void *opaque)
178190 {
179 GArray *pollfds = opaque;
180 return libslirp_gio_to_poll(g_array_index(pollfds, GPollFD, idx).revents);
191 GArray *pollfds = opaque;
192 return libslirp_gio_to_poll(g_array_index(pollfds, GPollFD, idx).revents);
181193 }
182194
183195 /*
184196 * updates timeout_msec for data->timers
185 * originally from https://github.com/rd235/libslirp/blob/d2b7032e29f3ba98e17414b32c9effffc90f2bb0/src/qemu2libslirp.c#L66
197 * originally from
198 * https://github.com/rd235/libslirp/blob/d2b7032e29f3ba98e17414b32c9effffc90f2bb0/src/qemu2libslirp.c#L66
186199 */
187 static void update_ra_timeout(uint32_t * timeout_msec, struct libslirp_data *data)
188 {
189 int64_t now_msec = libslirp_clock_get_ns(data) / 1000000;
190 GSList *f;
191 for (f = data->timers; f != NULL; f = f->next) {
192 struct timer *t = f->data;
193 if (t->expire_timer_msec != -1) {
194 int64_t diff = t->expire_timer_msec - now_msec;
195 if (diff < 0)
196 diff = 0;
197 if (diff < *timeout_msec)
198 *timeout_msec = diff;
199 }
200 }
200 static void update_ra_timeout(uint32_t *timeout_msec,
201 struct libslirp_data *data)
202 {
203 int64_t now_msec = libslirp_clock_get_ns(data) / 1000000;
204 GSList *f;
205 for (f = data->timers; f != NULL; f = f->next) {
206 struct timer *t = f->data;
207 if (t->expire_timer_msec != -1) {
208 int64_t diff = t->expire_timer_msec - now_msec;
209 if (diff < 0)
210 diff = 0;
211 if (diff < *timeout_msec)
212 *timeout_msec = diff;
213 }
214 }
201215 }
202216
203217 /*
204218 * calls SlirpTimerCb if timed out
205 * originally from https://github.com/rd235/libslirp/blob/d2b7032e29f3ba98e17414b32c9effffc90f2bb0/src/qemu2libslirp.c#L78
219 * originally from
220 * https://github.com/rd235/libslirp/blob/d2b7032e29f3ba98e17414b32c9effffc90f2bb0/src/qemu2libslirp.c#L78
206221 */
207222 static void check_ra_timeout(struct libslirp_data *data)
208223 {
209 int64_t now_msec = libslirp_clock_get_ns(data) / 1000000;
210 GSList *f;
211 for (f = data->timers; f != NULL; f = f->next) {
212 struct timer *t = f->data;
213 if (t->expire_timer_msec != -1) {
214 int64_t diff = t->expire_timer_msec - now_msec;
215 if (diff <= 0) {
216 t->expire_timer_msec = -1;
217 t->cb(t->cb_opaque);
218 }
219 }
220 }
224 int64_t now_msec = libslirp_clock_get_ns(data) / 1000000;
225 GSList *f;
226 for (f = data->timers; f != NULL; f = f->next) {
227 struct timer *t = f->data;
228 if (t->expire_timer_msec != -1) {
229 int64_t diff = t->expire_timer_msec - now_msec;
230 if (diff <= 0) {
231 t->expire_timer_msec = -1;
232 t->cb(t->cb_opaque);
233 }
234 }
235 }
221236 }
222237
223238 static const SlirpCb libslirp_cb = {
224 .send_packet = libslirp_send_packet,
225 .guest_error = libslirp_guest_error,
226 .clock_get_ns = libslirp_clock_get_ns,
227 .timer_new = libslirp_timer_new,
228 .timer_free = libslirp_timer_free,
229 .timer_mod = libslirp_timer_mod,
230 .register_poll_fd = libslirp_register_poll_fd,
231 .unregister_poll_fd = libslirp_unregister_poll_fd,
232 .notify = libslirp_notify,
239 .send_packet = libslirp_send_packet,
240 .guest_error = libslirp_guest_error,
241 .clock_get_ns = libslirp_clock_get_ns,
242 .timer_new = libslirp_timer_new,
243 .timer_free = libslirp_timer_free,
244 .timer_mod = libslirp_timer_mod,
245 .register_poll_fd = libslirp_register_poll_fd,
246 .unregister_poll_fd = libslirp_unregister_poll_fd,
247 .notify = libslirp_notify,
233248 };
234249
235250 Slirp *create_slirp(void *opaque, struct slirp4netns_config *s4nn)
236251 {
237 Slirp *slirp = NULL;
238 SlirpConfig cfg;
239 memset(&cfg, 0, sizeof(cfg));
240 cfg.version = 1;
241 cfg.restricted = 0;
242 cfg.in_enabled = 1;
243 cfg.vnetwork = s4nn->vnetwork;
244 cfg.vnetmask = s4nn->vnetmask;
245 cfg.vhost = s4nn->vhost;
246 cfg.in6_enabled = (int)(s4nn->enable_ipv6);
247 inet_pton(AF_INET6, "fd00::", &cfg.vprefix_addr6);
248 cfg.vprefix_len = 64;
249 inet_pton(AF_INET6, "fd00::2", &cfg.vhost6);
250 cfg.vhostname = NULL;
251 cfg.tftp_server_name = NULL;
252 cfg.tftp_path = NULL;
253 cfg.bootfile = NULL;
254 cfg.vdhcp_start = s4nn->vdhcp_start;
255 cfg.vnameserver = s4nn->vnameserver;
256 inet_pton(AF_INET6, "fd00::3", &cfg.vnameserver6);
257 cfg.vdnssearch = NULL;
258 cfg.vdomainname = NULL;
259 cfg.if_mtu = s4nn->mtu;
260 cfg.if_mru = s4nn->mtu;
261 cfg.disable_host_loopback = s4nn->disable_host_loopback;
262 slirp = slirp_new(&cfg, &libslirp_cb, opaque);
263 if (slirp == NULL) {
264 fprintf(stderr, "slirp_initx failed\n");
265 }
266 return slirp;
252 Slirp *slirp = NULL;
253 SlirpConfig cfg;
254 memset(&cfg, 0, sizeof(cfg));
255 cfg.version = 1;
256 cfg.restricted = 0;
257 cfg.in_enabled = 1;
258 cfg.vnetwork = s4nn->vnetwork;
259 cfg.vnetmask = s4nn->vnetmask;
260 cfg.vhost = s4nn->vhost;
261 cfg.in6_enabled = (int)(s4nn->enable_ipv6);
262 inet_pton(AF_INET6, "fd00::", &cfg.vprefix_addr6);
263 cfg.vprefix_len = 64;
264 inet_pton(AF_INET6, "fd00::2", &cfg.vhost6);
265 cfg.vhostname = NULL;
266 cfg.tftp_server_name = NULL;
267 cfg.tftp_path = NULL;
268 cfg.bootfile = NULL;
269 cfg.vdhcp_start = s4nn->vdhcp_start;
270 cfg.vnameserver = s4nn->vnameserver;
271 inet_pton(AF_INET6, "fd00::3", &cfg.vnameserver6);
272 cfg.vdnssearch = NULL;
273 cfg.vdomainname = NULL;
274 cfg.if_mtu = s4nn->mtu;
275 cfg.if_mru = s4nn->mtu;
276 cfg.disable_host_loopback = s4nn->disable_host_loopback;
277 slirp = slirp_new(&cfg, &libslirp_cb, opaque);
278 if (slirp == NULL) {
279 fprintf(stderr, "slirp_new failed\n");
280 }
281 return slirp;
267282 }
268283
269284 #define ETH_BUF_SIZE (65536)
270285
271 int do_slirp(int tapfd, int exitfd, const char *api_socket, struct slirp4netns_config *cfg)
272 {
273 int ret = -1;
274 Slirp *slirp = NULL;
275 uint8_t *buf = NULL;
276 struct libslirp_data opaque = {.tapfd = tapfd,.timers = NULL };
277 int apifd = -1;
278 struct api_ctx *apictx = NULL;
279 GArray *pollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD));
280 int pollfds_exitfd_idx = -1;
281 int pollfds_apifd_idx = -1;
282 size_t n_fds = 1;
283 GPollFD tap_pollfd = {.fd = tapfd,.events = G_IO_IN | G_IO_HUP,.revents = 0
284 };
285 GPollFD exit_pollfd = {.fd = exitfd,.events = G_IO_HUP,.revents = 0
286 };
287 GPollFD api_pollfd = {.fd = -1,.events = G_IO_IN | G_IO_HUP,.revents = 0
288 };
289
290 slirp = create_slirp((void *)&opaque, cfg);
291 if (slirp == NULL) {
292 fprintf(stderr, "create_slirp failed\n");
293 goto err;
294 }
295 buf = malloc(ETH_BUF_SIZE);
296 if (buf == NULL) {
297 goto err;
298 }
299 g_array_append_val(pollfds, tap_pollfd);
300 if (exitfd >= 0) {
301 n_fds++;
302 g_array_append_val(pollfds, exit_pollfd);
303 pollfds_exitfd_idx = n_fds - 1;
304 }
305 if (api_socket != NULL) {
306 if ((apifd = api_bindlisten(api_socket)) < 0) {
307 goto err;
308 }
309 if ((apictx = api_ctx_alloc(cfg)) == NULL) {
310 fprintf(stderr, "api_ctx_alloc failed\n");
311 goto err;
312 }
313 api_pollfd.fd = apifd;
314 n_fds++;
315 g_array_append_val(pollfds, api_pollfd);
316 pollfds_apifd_idx = n_fds - 1;
317 }
318 signal(SIGPIPE, SIG_IGN);
319 while (1) {
320 int pollout;
321 GPollFD *pollfds_data;
322 uint32_t timeout = -1; /* msec */
323 g_array_set_size(pollfds, n_fds);
324 slirp_pollfds_fill(slirp, &timeout, libslirp_add_poll, pollfds);
325 update_ra_timeout(&timeout, &opaque);
326 pollfds_data = (GPollFD *)pollfds->data;
327 do {
328 pollout = g_poll(pollfds_data, pollfds->len, timeout);
329 } while (pollout < 0 && errno == EINTR);
330 if (pollout < 0) {
331 goto err;
332 }
333
334 if (pollfds_data[0].revents) {
335 ssize_t rc = read(tapfd, buf, ETH_BUF_SIZE);
336 if (rc < 0) {
337 perror("do_slirp: read");
338 goto after_slirp_input;
339 }
340 slirp_input(slirp, buf, (int)rc);
341 after_slirp_input:
342 pollout = -1;
343 }
344
345 /* The exitfd is closed. */
346 if (pollfds_exitfd_idx >= 0 && pollfds_data[pollfds_exitfd_idx].revents) {
347 fprintf(stderr, "exitfd event\n");
348 goto success;
349 }
350
351 if (pollfds_apifd_idx >= 0 && pollfds_data[pollfds_apifd_idx].revents) {
352 int rc;
353 fprintf(stderr, "apifd event\n");
354 if ((rc = api_handler(slirp, apifd, apictx)) < 0) {
355 fprintf(stderr, "api_handler: rc=%d\n", rc);
356 }
357 }
358
359 slirp_pollfds_poll(slirp, (pollout <= 0), libslirp_get_revents, pollfds);
360 check_ra_timeout(&opaque);
361 }
362 success:
363 ret = 0;
364 err:
365 fprintf(stderr, "do_slirp is exiting\n");
366 if (buf != NULL) {
367 free(buf);
368 }
369 if (apictx != NULL) {
370 api_ctx_free(apictx);
371 unlink(api_socket);
372 }
373 g_array_free(pollfds, TRUE);
374 return ret;
375 }
286 int do_slirp(int tapfd, int readyfd, int exitfd, const char *api_socket,
287 struct slirp4netns_config *cfg)
288 {
289 int ret = -1;
290 Slirp *slirp = NULL;
291 uint8_t *buf = NULL;
292 struct libslirp_data opaque = { .tapfd = tapfd, .timers = NULL };
293 int apifd = -1;
294 struct api_ctx *apictx = NULL;
295 GArray *pollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD));
296 int pollfds_exitfd_idx = -1;
297 int pollfds_apifd_idx = -1;
298 size_t n_fds = 1;
299 GPollFD tap_pollfd = { .fd = tapfd,
300 .events = G_IO_IN | G_IO_HUP,
301 .revents = 0 };
302 GPollFD exit_pollfd = { .fd = exitfd, .events = G_IO_HUP, .revents = 0 };
303 GPollFD api_pollfd = { .fd = -1,
304 .events = G_IO_IN | G_IO_HUP,
305 .revents = 0 };
306
307 slirp = create_slirp((void *)&opaque, cfg);
308 if (slirp == NULL) {
309 fprintf(stderr, "create_slirp failed\n");
310 goto err;
311 }
312 buf = malloc(ETH_BUF_SIZE);
313 if (buf == NULL) {
314 goto err;
315 }
316 g_array_append_val(pollfds, tap_pollfd);
317 if (exitfd >= 0) {
318 n_fds++;
319 g_array_append_val(pollfds, exit_pollfd);
320 pollfds_exitfd_idx = n_fds - 1;
321 }
322 if (api_socket != NULL) {
323 if ((apifd = api_bindlisten(api_socket)) < 0) {
324 goto err;
325 }
326 if ((apictx = api_ctx_alloc(cfg)) == NULL) {
327 fprintf(stderr, "api_ctx_alloc failed\n");
328 goto err;
329 }
330 api_pollfd.fd = apifd;
331 n_fds++;
332 g_array_append_val(pollfds, api_pollfd);
333 pollfds_apifd_idx = n_fds - 1;
334 }
335 signal(SIGPIPE, SIG_IGN);
336 if (cfg->enable_sandbox && create_sandbox() < 0) {
337 fprintf(stderr, "create_sandbox failed\n");
338 goto err;
339 }
340 if (cfg->enable_seccomp && enable_seccomp() < 0) {
341 fprintf(stderr, "enable_seccomp failed\n");
342 goto err;
343 }
344 if (readyfd >= 0) {
345 int rc = -1;
346 do
347 rc = write(readyfd, "1", 1);
348 while (rc < 0 && errno == EINTR);
349 close(readyfd);
350 }
351 while (1) {
352 int pollout;
353 GPollFD *pollfds_data;
354 uint32_t timeout = -1; /* msec */
355 g_array_set_size(pollfds, n_fds);
356 slirp_pollfds_fill(slirp, &timeout, libslirp_add_poll, pollfds);
357 update_ra_timeout(&timeout, &opaque);
358 pollfds_data = (GPollFD *)pollfds->data;
359 do {
360 pollout = g_poll(pollfds_data, pollfds->len, timeout);
361 } while (pollout < 0 && errno == EINTR);
362 if (pollout < 0) {
363 goto err;
364 }
365
366 if (pollfds_data[0].revents) {
367 ssize_t rc = read(tapfd, buf, ETH_BUF_SIZE);
368 if (rc < 0) {
369 perror("do_slirp: read");
370 goto after_slirp_input;
371 }
372 slirp_input(slirp, buf, (int)rc);
373 after_slirp_input:
374 pollout = -1;
375 }
376
377 /* The exitfd is closed. */
378 if (pollfds_exitfd_idx >= 0 &&
379 pollfds_data[pollfds_exitfd_idx].revents) {
380 fprintf(stderr, "exitfd event\n");
381 goto success;
382 }
383
384 if (pollfds_apifd_idx >= 0 && pollfds_data[pollfds_apifd_idx].revents) {
385 int rc;
386 fprintf(stderr, "apifd event\n");
387 if ((rc = api_handler(slirp, apifd, apictx)) < 0) {
388 fprintf(stderr, "api_handler: rc=%d\n", rc);
389 }
390 }
391
392 slirp_pollfds_poll(slirp, (pollout <= 0), libslirp_get_revents,
393 pollfds);
394 check_ra_timeout(&opaque);
395 }
396 success:
397 ret = 0;
398 err:
399 fprintf(stderr, "do_slirp is exiting\n");
400 if (buf != NULL) {
401 free(buf);
402 }
403 if (apictx != NULL) {
404 api_ctx_free(apictx);
405 unlink(api_socket);
406 }
407 g_array_free(pollfds, TRUE);
408 return ret;
409 }
0 /* SPDX-License-Identifier: GPL-2.0-or-later */
01 #ifndef SLIRP4NETNS_H
12 # define SLIRP4NETNS_H
23 #include <arpa/inet.h>
1112 struct in_addr recommended_vguest; // 10.0.2.100 (slirp itself is unaware of vguest)
1213 bool enable_ipv6;
1314 bool disable_host_loopback;
15 bool enable_sandbox;
16 bool enable_seccomp;
1417 };
15 int do_slirp(int tapfd, int exitfd, const char *api_socket, struct slirp4netns_config *cfg);
18 int do_slirp(int tapfd, int readyfd, int exitfd, const char *api_socket, struct slirp4netns_config *cfg);
1619
1720 #endif
22
33 . $(dirname $0)/common.sh
44
5
6 # Test --netns-type=pid
57 unshare -r -n sleep infinity &
68 child=$!
79
810 wait_for_network_namespace $child
911
10 slirp4netns $child tun11 &
12 slirp4netns --ready-fd=3 --enable-sandbox $child tun11 3>ready.file &
1113 slirp_pid=$!
1214
13 wait_for_network_device $child tun11
15 # Wait that the sandbox is created
16 wait_for_file_content 1 ready.file
17 rm ready.file
18
19 # Check there are no capabilities left in slirp4netns
20 getpcaps $slirp_pid 2>&1 | tail -n1 > slirp.caps
21 grep cap_net_bind_service slirp.caps
22 grep -v cap_sys_admin slirp.caps
23 rm slirp.caps
24 test -e /proc/$slirp_pid/root/etc
25 test -e /proc/$slirp_pid/root/run
26 test \! -e /proc/$slirp_pid/root/home
27 test \! -e /proc/$slirp_pid/root/root
28 test \! -e /proc/$slirp_pid/root/var
1429
1530 function cleanup {
1631 kill -9 $child $slirp_pid
1833 trap cleanup EXIT
1934
2035 nsenter --preserve-credentials -U -n --target=$child ip -a netconf | grep tun11
36 nsenter --preserve-credentials -U -n --target=$child ip addr show tun11 | grep -v inet
2137
38 kill -9 $child $slirp_pid
39
40 # Test --userns-path= --netns-type=path
41 unshare -r -n sleep infinity &
42 child=$!
43
44 wait_for_network_namespace $child
45
46 slirp4netns --userns-path=/proc/$child/ns/user --netns-type=path /proc/$child/ns/net tun11 &
47 slirp_pid=$!
48
49 wait_for_network_device $child tun11
50
51 nsenter --preserve-credentials -U -n --target=$child ip -a netconf | grep tun11
2252 nsenter --preserve-credentials -U -n --target=$child ip addr show tun11 | grep -v inet
53
54 kill -9 $child $slirp_pid
55
56 # Test --netns-type=path
57 unshare -r -n sleep infinity &
58 child=$!
59
60 wait_for_network_namespace $child
61
62 nsenter --preserve-credentials -U --target=$child slirp4netns --netns-type=path /proc/$child/ns/net tun11 &
63 slirp_pid=$!
64
65 wait_for_network_device $child tun11
66
67 nsenter --preserve-credentials -U -n --target=$child ip -a netconf | grep tun11
68 nsenter --preserve-credentials -U -n --target=$child ip addr show tun11 | grep -v inet
00 # DO NOT EDIT MANUALLY
11
22 Vendored components:
3 * libslirp: https://gitlab.freedesktop.org/slirp/libslirp.git (`76462e2f16c6fce6856fb914cbef6207d0be4bc5`)
3 * libslirp: https://gitlab.freedesktop.org/slirp/libslirp.git (`d203c81bc6c861e1671122c3194c21d1a6763641`)
44 * parson: https://github.com/kgabis/parson.git (`c5bb9557fe98367aa8e041c65863909f12ee76b2`)
55
66 Please do not edit the contents under this directory manually.
291291 */
292292 while (q != (struct ipasfrag *)&fp->frag_link &&
293293 ip->ip_off + ip->ip_len > q->ipf_off) {
294 struct ipasfrag *prev;
294295 i = (ip->ip_off + ip->ip_len) - q->ipf_off;
295296 if (i < q->ipf_len) {
296297 q->ipf_len -= i;
298299 m_adj(dtom(slirp, q), i);
299300 break;
300301 }
302 prev = q;
301303 q = q->ipf_next;
302 m_free(dtom(slirp, q->ipf_prev));
303 ip_deq(q->ipf_prev);
304 ip_deq(prev);
305 m_free(dtom(slirp, prev));
304306 }
305307
306308 insert:
00 #!/bin/bash
11 set -eux -o pipefail
2 # Aug 2, 2019
3 LIBSLIRP_COMMIT=76462e2f16c6fce6856fb914cbef6207d0be4bc5
2 # Aug 26, 2019
3 LIBSLIRP_COMMIT=d203c81bc6c861e1671122c3194c21d1a6763641
44 LIBSLIRP_REPO=https://gitlab.freedesktop.org/slirp/libslirp.git
55
66 # Jul 12, 2019