New upstream version 0.4.1
Reinhard Tartler
4 years ago
0 | vendor/libslirp/.clang-formatâ |
5 | 5 | |
6 | 6 | script: |
7 | 7 | - 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 | |
9 | 9 | - 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 | |
2 | 3 | COPY . /src |
3 | 4 | WORKDIR /src |
4 | 5 | RUN ./autogen.sh && ./configure LDFLAGS="-static" && make && cp -f slirp4netns / |
5 | 6 | |
7 | # Ubuntu | |
6 | 8 | 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 | |
8 | 10 | COPY . /src |
9 | 11 | WORKDIR /src |
10 | 12 | RUN ./autogen.sh |
15 | 17 | FROM buildtest-ubuntu1804-common AS buildtest-ubuntu1804-static |
16 | 18 | RUN ./configure LDFLAGS="-static" && make && cp -f slirp4netns / |
17 | 19 | |
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 | ||
18 | 46 | 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 | |
20 | 48 | COPY --from=buildtest-ubuntu1804-dynamic /slirp4netns /buildtest-ubuntu1804-dynamic |
21 | 49 | 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 | |
2 | 2 | COPY . /slirp4netns |
3 | 3 | WORKDIR /slirp4netns |
4 | 4 | RUN chown -R 1000:1000 /slirp4netns |
7 | 7 | |
8 | 8 | FROM build AS test |
9 | 9 | 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 | |
11 | 11 | USER 1000:1000 |
12 | 12 | CMD ["make", "ci"] |
72 | 72 | [people] |
73 | 73 | [people.akihirosuda] |
74 | 74 | Name = "Akihiro Suda" |
75 | Email = "suda.akihiro@lab.ntt.co.jp" | |
75 | Email = "akihiro.suda.cz@hco.ntt.co.jp" | |
76 | 76 | GitHub = "AkihiroSuda" |
77 | 77 | |
78 | 78 | [people.barthalion] |
0 | 0 | bin_PROGRAMS = slirp4netns |
1 | 1 | |
2 | AM_CFLAGS = @GLIB_CFLAGS@ | |
2 | AM_CFLAGS = @GLIB_CFLAGS@ @LIBCAP_CFLAGS@ @LIBSECCOMP_CFLAGS@ | |
3 | 3 | |
4 | 4 | noinst_LIBRARIES = libslirp.a libparson.a |
5 | 5 | |
13 | 13 | tests/common.sh \ |
14 | 14 | slirp4netns.h \ |
15 | 15 | api.h \ |
16 | sandbox.h \ | |
17 | seccompfilter.h \ | |
16 | 18 | vendor/libslirp/COPYRIGHT \ |
17 | 19 | vendor/libslirp/README.md \ |
18 | 20 | vendor/libslirp/src/bootp.h \ |
33 | 35 | vendor/libslirp/src/slirp.h \ |
34 | 36 | vendor/libslirp/src/socket.h \ |
35 | 37 | vendor/libslirp/src/stream.h \ |
36 | vendor/libslirp/src/tftp.h \ | |
37 | 38 | vendor/libslirp/src/tcp.h \ |
38 | 39 | vendor/libslirp/src/tcpip.h \ |
39 | 40 | vendor/libslirp/src/tcp_timer.h \ |
40 | 41 | vendor/libslirp/src/tcp_var.h \ |
42 | vendor/libslirp/src/tftp.h \ | |
41 | 43 | vendor/libslirp/src/udp.h \ |
42 | 44 | vendor/libslirp/src/util.h \ |
43 | 45 | vendor/libslirp/src/vmstate.h \ |
79 | 81 | vendor/libslirp/src/vmstate.c |
80 | 82 | |
81 | 83 | # define specific commit if git available or it was replaced during git-archive creation |
82 | COMMIT := $(shell V=5e1639783df9bd7abbc46abc084a53c1093342d8 ; \ | |
84 | COMMIT := $(shell V=4d38845e2e311b684fc8d1c775c725bfcd5ddc27 ; \ | |
83 | 85 | expr match "$$V" ormat: >/dev/null \ |
84 | 86 | && (cd "$$abs_srcdir" && [ -d .git ] && git describe --always --abbrev=0 --dirty --exclude=\* || echo unknown) \ |
85 | 87 | || echo "$$V" ) |
90 | 92 | libparson_a_CFLAGS = $(AM_CFLAGS) -I$(abs_top_builddir)/vendor/parson |
91 | 93 | libparson_a_SOURCES = vendor/parson/parson.c |
92 | 94 | |
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 | |
95 | 97 | man1_MANS = slirp4netns.1 |
96 | 98 | |
97 | 99 | generate-man: |
98 | 100 | go-md2man -in slirp4netns.1.md -out slirp4netns.1 |
99 | 101 | |
100 | 102 | CLANGTIDY = clang-tidy -warnings-as-errors='*' |
103 | ||
104 | CLANGFORMAT = clang-format | |
101 | 105 | |
102 | 106 | lint: |
103 | 107 | $(CLANGTIDY) $(slirp4netns_SOURCES) -- $(AM_CFLAGS) |
106 | 110 | $(CLANGTIDY) $(slirp4netns_SOURCES) $(libslirp_a_SOURCES) $(libparson_a_SOURCES) -- $(AM_CFLAGS) |
107 | 111 | |
108 | 112 | 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) | |
119 | 114 | |
120 | 115 | benchmark: |
121 | 116 | benchmarks/benchmark-iperf3.sh |
123 | 118 | |
124 | 119 | ci: |
125 | 120 | $(MAKE) indent |
126 | test -z "$(git diff)" | |
121 | git diff --exit-code | |
127 | 122 | # 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) | |
129 | 124 | $(MAKE) lint |
130 | 125 | $(MAKE) -j $(shell nproc) distcheck || ( find . -name test-suite.log | xargs cat; exit 1 ) |
131 | 126 | PATH=$(shell pwd):$$PATH $(MAKE) benchmark MTU=1500 |
0 | 0 | # slirp4netns: User-mode networking for unprivileged network namespaces |
1 | 1 | |
2 | 2 | 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) | |
5 | 3 | |
6 | 4 | ## Motivation |
7 | 5 | |
12 | 10 | |
13 | 11 | ## Projects using slirp4netns |
14 | 12 | |
13 | Kubernetes distributions: | |
15 | 14 | * [Usernetes](https://github.com/rootless-containers/usernetes) (via RootlessKit) |
15 | * [k3s](https://k3s.io) (via RootlessKit) | |
16 | ||
17 | Container engines: | |
16 | 18 | * [Podman](https://github.com/containers/libpod) |
17 | 19 | * [Buildah](https://github.com/containers/buildah) |
18 | 20 | * [ctnr](https://github.com/mgoltzsche/ctnr) (via slirp-cni-plugin) |
19 | 21 | * [Docker & Moby](https://get.docker.com/rootless) (optionally, via RootlessKit) |
20 | 22 | |
23 | Tools: | |
21 | 24 | * [RootlessKit](https://github.com/rootless-containers/rootlesskit) |
22 | 25 | * [become-root](https://github.com/giuseppe/become-root) |
23 | 26 | * [slirp-cni-plugin](https://github.com/mgoltzsche/slirp-cni-plugin) |
24 | 27 | |
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 | ||
25 | 46 | ## Quick start |
26 | 47 | |
27 | 48 | ### Install from source |
28 | 49 | |
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: | |
30 | 56 | |
31 | 57 | ```console |
32 | 58 | $ ./autogen.sh |
44 | 70 | |
45 | 71 | ```console |
46 | 72 | $ sudo dnf install slirp4netns |
73 | ``` | |
74 | ||
75 | #### RHEL/CentOS 7.7 | |
76 | ||
77 | ```console | |
78 | $ sudo yum install slirp4netns | |
47 | 79 | ``` |
48 | 80 | |
49 | 81 | #### [RHEL/CentOS 7.6](https://copr.fedorainfracloud.org/coprs/vbatts/shadow-utils-newxidmap/) |
170 | 202 | The latest revision of slirp4netns is regularly benchmarked (`make benchmark`) on Travis: https://travis-ci.org/rootless-containers/slirp4netns |
171 | 203 | |
172 | 204 | ## Acknowledgement |
173 | ||
174 | 205 | See [`vendor/README.md`](./vendor/README.md). |
206 | ||
207 | ## License | |
208 | [GPL-2.0-or-later](COPYING) |
0 | /* SPDX-License-Identifier: GPL-2.0-or-later */ | |
0 | 1 | #define _GNU_SOURCE |
1 | 2 | #include <stdio.h> |
2 | 3 | #include <stdlib.h> |
10 | 11 | |
11 | 12 | int api_bindlisten(const char *api_socket) |
12 | 13 | { |
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; | |
32 | 33 | } |
33 | 34 | |
34 | 35 | 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; | |
41 | 42 | }; |
42 | 43 | |
43 | 44 | 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; | |
49 | 50 | }; |
50 | 51 | |
51 | 52 | struct api_ctx *api_ctx_alloc(struct slirp4netns_config *cfg) |
52 | 53 | { |
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; | |
67 | 68 | } |
68 | 69 | |
69 | 70 | void api_ctx_free(struct api_ctx *ctx) |
70 | 71 | { |
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 | } | |
78 | 79 | } |
79 | 80 | |
80 | 81 | /* |
81 | 82 | 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(). | |
84 | 87 | */ |
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 | } | |
188 | 204 | } |
189 | 205 | |
190 | 206 | /* |
191 | 207 | Handler for list_hostfwd. |
192 | 208 | e.g. {"execute": "list_hostfwd"} |
193 | 209 | */ |
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; | |
217 | 235 | } |
218 | 236 | |
219 | 237 | /* |
220 | 238 | Handler for remove_hostfwd. |
221 | 239 | e.g. {"execute": "remove_hostfwd", "arguments": {"id": 42}} |
222 | 240 | */ |
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; | |
245 | 267 | } |
246 | 268 | |
247 | 269 | static int api_handle_req(Slirp *slirp, int fd, struct api_ctx *ctx) |
248 | 270 | { |
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; | |
285 | 311 | } |
286 | 312 | |
287 | 313 | /* |
290 | 316 | */ |
291 | 317 | int api_handler(Slirp *slirp, int listenfd, struct api_ctx *ctx) |
292 | 318 | { |
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 */ | |
0 | 1 | #ifndef SLIRP4NETNS_API_H |
1 | 2 | # define SLIRP4NETNS_API_H |
2 | 3 | int api_bindlisten(const char *api_socket); |
0 | 0 | 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]) | |
2 | 2 | AC_CONFIG_SRCDIR([main.c]) |
3 | 3 | AC_CONFIG_HEADERS([config.h]) |
4 | 4 | |
7 | 7 | |
8 | 8 | AM_INIT_AUTOMAKE([1.9 foreign subdir-objects]) |
9 | 9 | |
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]) | |
11 | 11 | |
12 | 12 | AC_CHECK_HEADER_STDBOOL |
13 | 13 | AC_C_INLINE |
24 | 24 | AC_TYPE_UINT8_T |
25 | 25 | |
26 | 26 | AC_FUNC_ALLOCA |
27 | AC_FUNC_STRTOD | |
27 | 28 | AC_FUNC_FORK |
28 | 29 | AC_FUNC_MALLOC |
29 | 30 | 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]) | |
31 | 32 | |
32 | 33 | PKG_CHECK_MODULES(GLIB, glib-2.0) |
34 | PKG_CHECK_MODULES(LIBCAP, libcap) | |
35 | PKG_CHECK_MODULES(LIBSECCOMP, libseccomp) | |
33 | 36 | |
34 | 37 | AC_CONFIG_FILES([Makefile]) |
35 | 38 | 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,} |
0 | /* SPDX-License-Identifier: GPL-2.0-or-later */ | |
0 | 1 | #define _GNU_SOURCE |
1 | 2 | #include "config.h" |
2 | 3 | #include <stdio.h> |
21 | 22 | |
22 | 23 | #define DEFAULT_MTU (1500) |
23 | 24 | #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") | |
28 | 30 | #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 . | |
30 | 33 | #define NETWORK_PREFIX_MAX (25) |
31 | 34 | |
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; | |
54 | 74 | } |
55 | 75 | |
56 | 76 | static int open_tap(const char *tapname) |
57 | 77 | { |
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; | |
73 | 93 | } |
74 | 94 | |
75 | 95 | static int sendfd(int sock, int fd) |
76 | 96 | { |
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; | |
194 | 218 | } |
195 | 219 | |
196 | 220 | static int recvfd(int sock) |
197 | 221 | { |
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; | |
259 | 301 | } |
260 | 302 | |
261 | 303 | static void usage(const char *argv0) |
262 | 304 | { |
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"); | |
275 | 335 | } |
276 | 336 | |
277 | 337 | // version output is runc-compatible and machine-parsable |
278 | 338 | static void version() |
279 | 339 | { |
280 | printf("slirp4netns version %s\n", VERSION ? VERSION : PACKAGE_VERSION); | |
340 | printf("slirp4netns version %s\n", VERSION ? VERSION : PACKAGE_VERSION); | |
281 | 341 | #ifdef COMMIT |
282 | printf("commit: %s\n", COMMIT); | |
342 | printf("commit: %s\n", COMMIT); | |
283 | 343 | #endif |
284 | 344 | } |
285 | 345 | |
286 | 346 | 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 | |
297 | 362 | }; |
298 | 363 | |
299 | 364 | static void options_init(struct options *options) |
300 | 365 | { |
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; | |
304 | 369 | } |
305 | 370 | |
306 | 371 | static void options_destroy(struct options *options) |
307 | 372 | { |
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 | } | |
321 | 397 | } |
322 | 398 | |
323 | 399 | // * caller does not need to call options_init() |
325 | 401 | // * this function calls exit() on an error. |
326 | 402 | static void parse_args(int argc, char *const argv[], struct options *options) |
327 | 403 | { |
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; | |
329 | 410 | #define CIDR -42 |
330 | 411 | #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 | } | |
409 | 543 | #undef CIDR |
410 | 544 | #undef DISABLE_HOST_LOOPBACK |
545 | #undef NETNS_TYPE | |
546 | #undef USERNS_PATH | |
411 | 547 | #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; | |
531 | 686 | } |
532 | 687 | |
533 | 688 | int main(int argc, char *const argv[]) |
534 | 689 | { |
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 |
0 | 0 | .nh |
1 | .TH SLIRP4NETNS 1 "July 2018" "Rootless Containers" "User Commands" | |
1 | .TH SLIRP4NETNS 1 "August 2019" "Rootless Containers" "User Commands" | |
2 | 2 | |
3 | 3 | .SH NAME |
4 | 4 | .PP |
7 | 7 | |
8 | 8 | .SH SYNOPSIS |
9 | 9 | .PP |
10 | slirp4netns [OPTION]... PID TAPNAME | |
10 | slirp4netns [OPTION]... PID|PATH TAPNAME | |
11 | 11 | |
12 | 12 | |
13 | 13 | .SH DESCRIPTION |
42 | 42 | .SH OPTIONS |
43 | 43 | .PP |
44 | 44 | \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. | |
46 | 47 | |
47 | 48 | .PP |
48 | 49 | \fB\-e\fP, \fB\-\-exit\-fd=FD\fP |
49 | 50 | specify the FD for terminating slirp4netns. |
51 | When the FD is specified, slirp4netns exits when a \fBpoll(2)\fP event happens on the FD. | |
50 | 52 | |
51 | 53 | .PP |
52 | 54 | \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) | |
57 | 62 | 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 | |
58 | 71 | |
59 | 72 | .PP |
60 | 73 | \fB\-\-cidr\fP (since v0.3.0) |
65 | 78 | prohibit connecting to 127.0.0.1:* on the host namespace |
66 | 79 | |
67 | 80 | .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) | |
77 | 104 | show help and exit |
78 | 105 | |
79 | 106 | .PP |
80 | \fB\-v\fP, \fB\-\-version\fP | |
107 | \fB\-v\fP, \fB\-\-version\fP (since v0.2.0) | |
81 | 108 | show version and exit |
82 | 109 | |
83 | 110 | |
267 | 294 | |
268 | 295 | .RS |
269 | 296 | .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. | |
271 | 298 | i.e. No support for keep\-alive and timeout. |
272 | 299 | .IP \(bu 2 |
273 | 300 | slirp4netns "stops the world" during processing API requests. |
274 | 301 | .IP \(bu 2 |
275 | A request must be less than 4095 bytes. | |
302 | A request must be less than 4096 bytes. | |
276 | 303 | .IP \(bu 2 |
277 | 304 | JSON responses may contain \fBerror\fP instead of \fBreturn\fP\&. |
278 | 305 | |
279 | 306 | .RE |
280 | 307 | |
281 | 308 | |
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 | ||
282 | 357 | .SH SEE ALSO |
283 | 358 | .PP |
284 | 359 | \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" | |
1 | 1 | ================================================== |
2 | 2 | |
3 | 3 | # NAME |
6 | 6 | |
7 | 7 | # SYNOPSIS |
8 | 8 | |
9 | slirp4netns [OPTION]... PID TAPNAME | |
9 | slirp4netns [OPTION]... PID|PATH TAPNAME | |
10 | 10 | |
11 | 11 | # DESCRIPTION |
12 | 12 | |
27 | 27 | # OPTIONS |
28 | 28 | |
29 | 29 | **-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. | |
31 | 32 | |
32 | 33 | **-e**, **--exit-fd=FD** |
33 | 34 | specify the FD for terminating slirp4netns. |
35 | When the FD is specified, slirp4netns exits when a **poll(2)** event happens on the FD. | |
34 | 36 | |
35 | 37 | **-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) | |
39 | 44 | 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 | |
40 | 51 | |
41 | 52 | **--cidr** (since v0.3.0) |
42 | 53 | specify CIDR, e.g. 10.0.2.0/24 |
44 | 55 | **--disable-host-loopback** (since v0.3.0) |
45 | 56 | prohibit connecting to 127.0.0.1:\* on the host namespace |
46 | 57 | |
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) | |
54 | 76 | show help and exit |
55 | 77 | |
56 | **-v**, **--version** | |
78 | **-v**, **--version** (since v0.2.0) | |
57 | 79 | show version and exit |
58 | 80 | |
59 | 81 | # EXAMPLE |
171 | 193 | |
172 | 194 | Remarks: |
173 | 195 | |
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. | |
175 | 197 | i.e. No support for keep-alive and timeout. |
176 | 198 | * 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. | |
178 | 200 | * JSON responses may contain **error** instead of **return**. |
179 | 201 | |
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 | ||
180 | 227 | # SEE ALSO |
181 | 228 | |
182 | 229 | **network_namespaces**(7), **user_namespaces**(7), **veth**(4) |
0 | /* SPDX-License-Identifier: GPL-2.0-or-later */ | |
0 | 1 | #define _GNU_SOURCE |
1 | 2 | #include <errno.h> |
2 | 3 | #include <signal.h> |
8 | 9 | |
9 | 10 | #include "vendor/libslirp/src/libslirp.h" |
10 | 11 | #include "api.h" |
12 | #include "sandbox.h" | |
13 | #include "seccompfilter.h" | |
11 | 14 | #include "slirp4netns.h" |
12 | 15 | |
13 | 16 | /* opaque for SlirpCb */ |
14 | 17 | struct libslirp_data { |
15 | int tapfd; | |
16 | GSList *timers; | |
18 | int tapfd; | |
19 | GSList *timers; | |
17 | 20 | }; |
18 | 21 | |
19 | 22 | /* 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); | |
24 | 28 | } |
25 | 29 | |
26 | 30 | /* implements SlirpCb.guest_error */ |
27 | 31 | static void libslirp_guest_error(const char *msg, void *opaque) |
28 | 32 | { |
29 | fprintf(stderr, "libslirp: %s\n", msg); | |
33 | fprintf(stderr, "libslirp: %s\n", msg); | |
30 | 34 | } |
31 | 35 | |
32 | 36 | /* implements SlirpCb.clock_get_ns */ |
33 | 37 | static int64_t libslirp_clock_get_ns(void *opaque) |
34 | 38 | { |
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; | |
38 | 42 | } |
39 | 43 | |
40 | 44 | /* timer for SlirpCb */ |
41 | 45 | 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; | |
45 | 49 | }; |
46 | 50 | |
47 | 51 | /* implements SlirpCb.timer_new */ |
48 | 52 | static void *libslirp_timer_new(SlirpTimerCb cb, void *cb_opaque, void *opaque) |
49 | 53 | { |
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; | |
57 | 61 | } |
58 | 62 | |
59 | 63 | /* implements SlirpCb.timer_free */ |
60 | 64 | static void libslirp_timer_free(void *timer, void *opaque) |
61 | 65 | { |
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); | |
65 | 69 | } |
66 | 70 | |
67 | 71 | /* 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; | |
72 | 77 | } |
73 | 78 | |
74 | 79 | /* implements SlirpCb.register_poll_fd */ |
75 | 80 | static void libslirp_register_poll_fd(int fd, void *opaque) |
76 | 81 | { |
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 | */ | |
87 | 94 | } |
88 | 95 | |
89 | 96 | /* implements SlirpCb.unregister_poll_fd */ |
90 | 97 | static void libslirp_unregister_poll_fd(int fd, void *opaque) |
91 | 98 | { |
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 | */ | |
98 | 106 | } |
99 | 107 | |
100 | 108 | /* implements SlirpCb.notify */ |
101 | 109 | static void libslirp_notify(void *opaque) |
102 | 110 | { |
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 | */ | |
113 | 123 | } |
114 | 124 | |
115 | 125 | static int libslirp_poll_to_gio(int events) |
116 | 126 | { |
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; | |
134 | 144 | } |
135 | 145 | |
136 | 146 | /* |
137 | 147 | * 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) | |
139 | 150 | */ |
140 | 151 | static int libslirp_add_poll(int fd, int events, void *opaque) |
141 | 152 | { |
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; | |
150 | 161 | } |
151 | 162 | |
152 | 163 | static int libslirp_gio_to_poll(int events) |
153 | 164 | { |
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; | |
171 | 182 | } |
172 | 183 | |
173 | 184 | /* |
174 | 185 | * implements SlirpGetREventsCB used in slirp_pollfds_poll |
175 | 186 | * originally from qemu/net/slirp.c:net_slirp_get_revents |
187 | * (4c76137484878f42a2ce1ae1b888b6a7f66b4053) | |
176 | 188 | */ |
177 | 189 | static int libslirp_get_revents(int idx, void *opaque) |
178 | 190 | { |
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); | |
181 | 193 | } |
182 | 194 | |
183 | 195 | /* |
184 | 196 | * 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 | |
186 | 199 | */ |
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 | } | |
201 | 215 | } |
202 | 216 | |
203 | 217 | /* |
204 | 218 | * 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 | |
206 | 221 | */ |
207 | 222 | static void check_ra_timeout(struct libslirp_data *data) |
208 | 223 | { |
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 | } | |
221 | 236 | } |
222 | 237 | |
223 | 238 | 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, | |
233 | 248 | }; |
234 | 249 | |
235 | 250 | Slirp *create_slirp(void *opaque, struct slirp4netns_config *s4nn) |
236 | 251 | { |
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; | |
267 | 282 | } |
268 | 283 | |
269 | 284 | #define ETH_BUF_SIZE (65536) |
270 | 285 | |
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 */ | |
0 | 1 | #ifndef SLIRP4NETNS_H |
1 | 2 | # define SLIRP4NETNS_H |
2 | 3 | #include <arpa/inet.h> |
11 | 12 | struct in_addr recommended_vguest; // 10.0.2.100 (slirp itself is unaware of vguest) |
12 | 13 | bool enable_ipv6; |
13 | 14 | bool disable_host_loopback; |
15 | bool enable_sandbox; | |
16 | bool enable_seccomp; | |
14 | 17 | }; |
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); | |
16 | 19 | |
17 | 20 | #endif |
2 | 2 | |
3 | 3 | . $(dirname $0)/common.sh |
4 | 4 | |
5 | ||
6 | # Test --netns-type=pid | |
5 | 7 | unshare -r -n sleep infinity & |
6 | 8 | child=$! |
7 | 9 | |
8 | 10 | wait_for_network_namespace $child |
9 | 11 | |
10 | slirp4netns $child tun11 & | |
12 | slirp4netns --ready-fd=3 --enable-sandbox $child tun11 3>ready.file & | |
11 | 13 | slirp_pid=$! |
12 | 14 | |
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 | |
14 | 29 | |
15 | 30 | function cleanup { |
16 | 31 | kill -9 $child $slirp_pid |
18 | 33 | trap cleanup EXIT |
19 | 34 | |
20 | 35 | 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 | |
21 | 37 | |
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 | |
22 | 52 | 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 |
0 | 0 | # DO NOT EDIT MANUALLY |
1 | 1 | |
2 | 2 | Vendored components: |
3 | * libslirp: https://gitlab.freedesktop.org/slirp/libslirp.git (`76462e2f16c6fce6856fb914cbef6207d0be4bc5`) | |
3 | * libslirp: https://gitlab.freedesktop.org/slirp/libslirp.git (`d203c81bc6c861e1671122c3194c21d1a6763641`) | |
4 | 4 | * parson: https://github.com/kgabis/parson.git (`c5bb9557fe98367aa8e041c65863909f12ee76b2`) |
5 | 5 | |
6 | 6 | Please do not edit the contents under this directory manually. |
291 | 291 | */ |
292 | 292 | while (q != (struct ipasfrag *)&fp->frag_link && |
293 | 293 | ip->ip_off + ip->ip_len > q->ipf_off) { |
294 | struct ipasfrag *prev; | |
294 | 295 | i = (ip->ip_off + ip->ip_len) - q->ipf_off; |
295 | 296 | if (i < q->ipf_len) { |
296 | 297 | q->ipf_len -= i; |
298 | 299 | m_adj(dtom(slirp, q), i); |
299 | 300 | break; |
300 | 301 | } |
302 | prev = q; | |
301 | 303 | 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)); | |
304 | 306 | } |
305 | 307 | |
306 | 308 | insert: |